Compilation aided syntax highlighting

This commit is contained in:
Mia
2026-03-09 11:07:24 +01:00
parent 63890e46d9
commit dc94090976
11 changed files with 798 additions and 280 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
use crate::parsing::semantic_tokens;
use crate::syntax_tokens::semantic_tokens;
use crate::utils::UriUtils;
use crate::workspace::{Workspace, start_workspace_thread};
use std::sync::Arc;
@@ -8,7 +8,7 @@ use tower_lsp_server::ls_types::request::{GotoDeclarationParams, GotoDeclaration
use tower_lsp_server::{Client, LspService, Server};
use tower_lsp_server::{LanguageServer, ls_types::*};
mod parsing;
mod syntax_tokens;
mod utils;
mod workspace;
+128 -61
View File
@@ -1,7 +1,11 @@
use crate::utils::{correct_semantic_token_deltas, make_diagnostics};
use leaf_assembly::{
functions::ir::{Instruction, InstructionVariant},
values::{AnyConst, AnyValue},
};
use leaf_compiler::metadata::CodePosition;
use leaf_parser::{SourceCode, Text, ast::*};
use std::{sync::Arc, time::Instant};
use std::{collections::HashMap, sync::Arc, time::Instant, u32};
use tower_lsp_server::ls_types::{Diagnostic, SemanticToken, SemanticTokens, SemanticTokensResult};
pub mod semantic_tokens {
@@ -27,17 +31,29 @@ pub mod semantic_tokens {
pub const OPERATOR: u32 = 7;
}
pub struct DocumentParsingResult {
pub struct DocumentSyntax {
pub tokens: SemanticTokensResult,
pub diagnostics: Vec<Diagnostic>,
}
pub fn parse_document(code: &Arc<SourceCode>) -> DocumentParsingResult {
impl Default for DocumentSyntax {
fn default() -> Self {
Self {
tokens: SemanticTokensResult::Tokens(SemanticTokens::default()),
diagnostics: Default::default(),
}
}
}
pub fn generate_syntax_tokens(
code: &Arc<SourceCode>,
symbols: &HashMap<Arc<[u32]>, [u8; size_of::<AnyValue>()]>,
) -> DocumentSyntax {
let now = Instant::now();
let ast = match code.ast() {
Ok(d) => d,
Err(err) => {
return DocumentParsingResult {
return DocumentSyntax {
tokens: SemanticTokensResult::Tokens(SemanticTokens::default()),
diagnostics: make_diagnostics(
&leaf_compiler::diagnostics::Diagnostic::parsing_err(code.clone(), err.clone()),
@@ -48,7 +64,7 @@ pub fn parse_document(code: &Arc<SourceCode>) -> DocumentParsingResult {
let mut tokens = SemanticTokens::default();
for decl in &ast.decls {
decl.push_semantic_tokens(code, &mut tokens, None);
decl.push_semantic_tokens(code, &mut tokens, None, symbols);
}
correct_semantic_token_deltas(&mut tokens);
@@ -64,7 +80,7 @@ pub fn parse_document(code: &Arc<SourceCode>) -> DocumentParsingResult {
size as f32 / 1000.0,
now.elapsed()
);
DocumentParsingResult {
DocumentSyntax {
tokens: SemanticTokensResult::Tokens(tokens),
diagnostics: vec![],
}
@@ -90,6 +106,7 @@ trait PushSemanticTokens {
file: &Arc<SourceCode>,
tokens: &mut SemanticTokens,
hint: Option<u32>,
symbols: &HashMap<Arc<[u32]>, [u8; size_of::<AnyValue>()]>,
);
}
@@ -99,24 +116,44 @@ impl PushSemanticTokens for AstNode<ConstDecl> {
file: &Arc<SourceCode>,
tokens: &mut SemanticTokens,
hint: Option<u32>,
symbols: &HashMap<Arc<[u32]>, [u8; size_of::<AnyValue>()]>,
) {
match self.names.as_slice() {
[ident] => {
push_token!(
file,
tokens,
ident,
match &self.value.node {
if let [ident] = self.names.as_slice() {
push_token!(
file,
tokens,
ident.range,
match symbols.get(&ident.path) {
Some(symbol) => get_semantic_token(*symbol),
None => match &self.value.node {
Expr::Func(_) => semantic_tokens::FUNCTION,
Expr::Ptr { .. } => semantic_tokens::TYPE,
Expr::Struct { .. } => semantic_tokens::TYPE,
_ => semantic_tokens::VARIABLE,
}
);
}
_ => {}
},
}
)
}
self.value.push_semantic_tokens(file, tokens, hint);
self.value.push_semantic_tokens(file, tokens, hint, symbols);
}
}
impl PushSemanticTokens for AstNode<()> {
fn push_semantic_tokens(
&self,
file: &Arc<SourceCode>,
tokens: &mut SemanticTokens,
hint: Option<u32>,
symbols: &HashMap<Arc<[u32]>, [u8; size_of::<AnyValue>()]>,
) {
push_token!(
file,
tokens,
self.range,
match symbols.get(&self.path) {
Some(symbol) => get_semantic_token(*symbol),
None => hint.unwrap_or(semantic_tokens::VARIABLE),
}
)
}
}
@@ -126,21 +163,25 @@ impl PushSemanticTokens for AstNode<Expr> {
file: &Arc<SourceCode>,
tokens: &mut SemanticTokens,
hint: Option<u32>,
symbols: &HashMap<Arc<[u32]>, [u8; size_of::<AnyValue>()]>,
) {
match &self.node {
Expr::Ident(range) => {
Expr::Ident => {
push_token!(
file,
tokens,
range,
hint.unwrap_or(semantic_tokens::VARIABLE)
self.range,
match symbols.get(&self.path) {
Some(v) => get_semantic_token(*v),
None => hint.unwrap_or(semantic_tokens::VARIABLE),
}
);
}
Expr::Number(_) => {
push_token!(file, tokens, self.range, semantic_tokens::NUMBER);
}
Expr::If(expr) => expr.push_semantic_tokens(file, tokens, None),
Expr::While(expr) => expr.push_semantic_tokens(file, tokens, None),
Expr::If(expr) => expr.push_semantic_tokens(file, tokens, None, symbols),
Expr::While(expr) => expr.push_semantic_tokens(file, tokens, None, symbols),
Expr::Func(func) => {
let Function {
fn_tok,
@@ -155,14 +196,14 @@ impl PushSemanticTokens for AstNode<Expr> {
..
} in args
{
push_token!(file, tokens, name, semantic_tokens::PARAMETER);
value.push_semantic_tokens(file, tokens, Some(semantic_tokens::TYPE));
push_token!(file, tokens, name.range, semantic_tokens::PARAMETER);
value.push_semantic_tokens(file, tokens, Some(semantic_tokens::TYPE), symbols);
}
if let Some(ret) = ret {
push_token!(file, tokens, ret.range, semantic_tokens::TYPE);
ret.push_semantic_tokens(file, tokens, Some(semantic_tokens::TYPE), symbols);
}
if let Some(block) = block {
block.push_semantic_tokens(file, tokens, hint);
block.push_semantic_tokens(file, tokens, None, symbols);
}
}
Expr::VarDecl(expr) => {
@@ -170,34 +211,34 @@ impl PushSemanticTokens for AstNode<Expr> {
names,
r#type,
value,
} = &**expr;
for range in names.as_slice() {
push_token!(file, tokens, range, semantic_tokens::VARIABLE);
} = &expr.node;
for node in names.as_slice() {
push_token!(file, tokens, node.range, semantic_tokens::VARIABLE);
}
if let Some(ty) = r#type {
ty.push_semantic_tokens(file, tokens, Some(semantic_tokens::TYPE));
ty.push_semantic_tokens(file, tokens, Some(semantic_tokens::TYPE), symbols);
}
value.push_semantic_tokens(file, tokens, hint);
value.push_semantic_tokens(file, tokens, None, symbols);
}
Expr::ConstDecl(expr) => {
let ConstDecl {
names,
r#type,
value,
} = &**expr;
for range in names.as_slice() {
push_token!(file, tokens, range, semantic_tokens::VARIABLE);
} = &expr.node;
for node in names.as_slice() {
push_token!(file, tokens, node.range, semantic_tokens::VARIABLE);
}
if let Some(ty) = r#type {
ty.push_semantic_tokens(file, tokens, Some(semantic_tokens::TYPE));
ty.push_semantic_tokens(file, tokens, Some(semantic_tokens::TYPE), symbols);
}
value.push_semantic_tokens(file, tokens, hint);
value.push_semantic_tokens(file, tokens, None, symbols);
}
Expr::Ptr { mutable, base, .. } => {
if let Some(mutable) = mutable {
push_token!(file, tokens, mutable, semantic_tokens::KEYWORD);
}
base.push_semantic_tokens(file, tokens, hint);
base.push_semantic_tokens(file, tokens, hint, symbols);
}
Expr::Struct { fields, struct_tok } => {
push_token!(file, tokens, struct_tok, semantic_tokens::KEYWORD);
@@ -217,8 +258,8 @@ impl PushSemanticTokens for AstNode<Expr> {
if let Some(mutable) = mutable {
push_token!(file, tokens, mutable, semantic_tokens::KEYWORD);
}
push_token!(file, tokens, name, semantic_tokens::PROPERTY);
ty.push_semantic_tokens(file, tokens, Some(semantic_tokens::TYPE));
push_token!(file, tokens, name.range, semantic_tokens::PROPERTY);
ty.push_semantic_tokens(file, tokens, Some(semantic_tokens::TYPE), symbols);
}
}
Expr::StructCtor { ty, values } => {
@@ -226,11 +267,11 @@ impl PushSemanticTokens for AstNode<Expr> {
push_token!(file, tokens, ty.range, semantic_tokens::TYPE);
}
for value in values {
value.push_semantic_tokens(file, tokens, None);
value.push_semantic_tokens(file, tokens, None, symbols);
}
}
Expr::Deref { value, operator } => {
value.push_semantic_tokens(file, tokens, None);
value.push_semantic_tokens(file, tokens, None, symbols);
push_token!(file, tokens, operator, semantic_tokens::OPERATOR);
}
Expr::Binary {
@@ -238,31 +279,31 @@ impl PushSemanticTokens for AstNode<Expr> {
operator: AstNode {
node: BinaryOperator::Cast,
range,
..
},
rhs,
} => {
lhs.push_semantic_tokens(file, tokens, None);
lhs.push_semantic_tokens(file, tokens, None, symbols);
push_token!(file, tokens, range, semantic_tokens::KEYWORD);
rhs.push_semantic_tokens(file, tokens, Some(semantic_tokens::TYPE));
rhs.push_semantic_tokens(file, tokens, Some(semantic_tokens::TYPE), symbols);
}
Expr::Binary {
lhs,
rhs,
operator: AstNode { range, .. },
} => {
lhs.push_semantic_tokens(file, tokens, None);
rhs.push_semantic_tokens(file, tokens, None);
lhs.push_semantic_tokens(file, tokens, None, symbols);
rhs.push_semantic_tokens(file, tokens, None, symbols);
push_token!(file, tokens, range, semantic_tokens::OPERATOR);
}
Expr::Access { value, field, .. } => {
push_token!(file, tokens, value.range, semantic_tokens::VARIABLE);
push_token!(file, tokens, field, semantic_tokens::PROPERTY);
value.push_semantic_tokens(file, tokens, None);
value.push_semantic_tokens(file, tokens, Some(semantic_tokens::VARIABLE), symbols);
field.push_semantic_tokens(file, tokens, Some(semantic_tokens::PROPERTY), symbols);
}
Expr::Call { func, args } => {
push_token!(file, tokens, func.range, semantic_tokens::FUNCTION);
func.push_semantic_tokens(file, tokens, Some(semantic_tokens::FUNCTION), symbols);
for arg in args.iter() {
arg.push_semantic_tokens(file, tokens, None);
arg.push_semantic_tokens(file, tokens, None, symbols);
}
}
_ => {}
@@ -276,9 +317,10 @@ impl PushSemanticTokens for Block {
file: &Arc<SourceCode>,
tokens: &mut SemanticTokens,
_: Option<u32>,
symbols: &HashMap<Arc<[u32]>, [u8; size_of::<AnyValue>()]>,
) {
for expr in &self.0 {
expr.push_semantic_tokens(file, tokens, None);
expr.push_semantic_tokens(file, tokens, None, symbols);
}
}
}
@@ -289,19 +331,20 @@ impl PushSemanticTokens for If {
file: &Arc<SourceCode>,
tokens: &mut SemanticTokens,
_: Option<u32>,
symbols: &HashMap<Arc<[u32]>, [u8; size_of::<AnyValue>()]>,
) {
push_token!(file, tokens, self.if_tok, semantic_tokens::KEYWORD);
self.cond.push_semantic_tokens(file, tokens, None);
self.block.push_semantic_tokens(file, tokens, None);
self.cond.push_semantic_tokens(file, tokens, None, symbols);
self.block.push_semantic_tokens(file, tokens, None, symbols);
if let Some(expr) = &self.else_ {
match &**expr {
Else::If { else_tok, expr } => {
push_token!(file, tokens, else_tok, semantic_tokens::KEYWORD);
expr.push_semantic_tokens(file, tokens, None);
expr.push_semantic_tokens(file, tokens, None, symbols);
}
Else::Block { else_tok, expr } => {
push_token!(file, tokens, else_tok, semantic_tokens::KEYWORD);
expr.push_semantic_tokens(file, tokens, None);
expr.push_semantic_tokens(file, tokens, None, symbols);
}
}
}
@@ -314,10 +357,11 @@ impl PushSemanticTokens for While {
file: &Arc<SourceCode>,
tokens: &mut SemanticTokens,
_: Option<u32>,
symbols: &HashMap<Arc<[u32]>, [u8; size_of::<AnyValue>()]>,
) {
push_token!(file, tokens, self.while_tok, semantic_tokens::KEYWORD);
self.cond.push_semantic_tokens(file, tokens, None);
self.block.push_semantic_tokens(file, tokens, None);
self.cond.push_semantic_tokens(file, tokens, None, symbols);
self.block.push_semantic_tokens(file, tokens, None, symbols);
}
}
@@ -327,8 +371,31 @@ impl PushSemanticTokens for NameValuePair {
file: &Arc<SourceCode>,
tokens: &mut SemanticTokens,
_: Option<u32>,
symbols: &HashMap<Arc<[u32]>, [u8; size_of::<AnyValue>()]>,
) {
push_token!(file, tokens, self.name, semantic_tokens::PROPERTY);
self.value.push_semantic_tokens(file, tokens, None);
push_token!(file, tokens, self.name.range, semantic_tokens::PROPERTY);
self.value.push_semantic_tokens(file, tokens, None, symbols);
}
}
fn get_semantic_token(value: [u8; size_of::<AnyValue>()]) -> u32 {
let value: AnyValue = unsafe { std::mem::transmute(value) };
match value {
AnyValue::Parameter(_, _) => semantic_tokens::PARAMETER,
AnyValue::Constant(AnyConst::Type(_)) => semantic_tokens::TYPE,
AnyValue::Constant(AnyConst::Function(_)) => semantic_tokens::FUNCTION,
AnyValue::Instruction(Instruction {
variant:
InstructionVariant::GetElementPtr(_, _) | InstructionVariant::GetElementVal(_, _),
..
}) => semantic_tokens::PROPERTY,
AnyValue::Instruction(Instruction {
variant: InstructionVariant::StackAlloc(_) | InstructionVariant::GCAlloc(_),
..
}) => semantic_tokens::VARIABLE,
_ => {
eprintln!("unknown value case {value:?}");
u32::MAX
}
}
}
+174 -103
View File
@@ -1,40 +1,33 @@
use leaf_allocators::SyncArenaAllocator;
use leaf_assembly::{
assembly::{AssemblyIdentifier, Version},
types::{Type, derivations::PtrT},
values::{AnyConst, AnyValue, Value},
};
use leaf_compiler::{CompilationContext, events::Event, metadata::CodePosition};
use leaf_assembly::assembly::{AssemblyIdentifier, Version};
use leaf_compiler::{CompilationContext, events::Event};
use leaf_parser::{ArcStr, SourceCode, Text};
use rangemap::RangeMap;
use ropey::Rope;
use rust_search::SearchBuilder;
use std::{
borrow::Cow,
cell::RefCell,
collections::HashMap,
fmt::Write,
ops::Range,
path::PathBuf,
str::FromStr,
sync::{Arc, OnceLock},
sync::{Arc, Mutex, OnceLock},
time::Instant,
};
use tokio::sync::{
Mutex, Notify, RwLock,
Notify,
mpsc::{Sender, channel},
};
use tower_lsp_server::{
Client,
ls_types::{
Diagnostic, DidChangeTextDocumentParams, Hover, HoverContents, HoverParams, Location,
MarkedString, Position, SemanticToken, SemanticTokens, SemanticTokensParams,
SemanticTokensResult, TextDocumentPositionParams, Uri,
DidChangeTextDocumentParams, Hover, HoverParams, Position, SemanticTokensParams,
SemanticTokensResult, Uri,
request::{GotoDeclarationParams, GotoDeclarationResponse},
},
};
use crate::{
parsing::{DocumentParsingResult, parse_document},
utils::{CodePositionUtils, UriUtils, correct_semantic_token_deltas, make_diagnostics},
syntax_tokens::{DocumentSyntax, generate_syntax_tokens},
utils::{UriUtils, make_diagnostics},
};
pub struct Workspace {
@@ -116,115 +109,193 @@ enum Request {
),
}
#[derive(Default)]
struct State {
file_text: Mutex<HashMap<Uri, (Rope, i32)>>,
file_code: Mutex<HashMap<Uri, (Arc<SourceCode>, i32)>>,
file_docs: Mutex<HashMap<Uri, Arc<DocumentSyntax>>>,
compilation_state: Arc<Mutex<CompilationState>>,
}
impl State {
pub fn with_file_text<T>(&self, uri: Uri, action: impl FnOnce(&mut (Rope, i32)) -> T) -> T {
let mut file_text = self.file_text.lock().unwrap();
action(file_text.entry(uri).or_insert_with_key(|key| {
let path = key.strip_header();
(Rope::from_str(&std::fs::read_to_string(path).unwrap()), 0)
}))
}
pub fn with_file_code<T>(
&self,
uri: Uri,
action: impl FnOnce(&mut (Arc<SourceCode>, i32)) -> T,
) -> T {
self.with_file_text(uri.clone(), |(text, version)| {
let mut file_code = self.file_code.lock().unwrap();
let entry = match file_code.get_mut(&uri) {
None => file_code.entry(uri).or_insert_with_key(|uri| {
(
SourceCode {
file: uri.strip_header().into(),
text: Text::ArcStr(ArcStr::from(text.to_string()), OnceLock::new()),
ast: OnceLock::new(),
}
.into(),
*version,
)
}),
Some(entry) => {
if entry.1 != *version {
entry.0 = SourceCode {
file: uri.strip_header().into(),
text: Text::ArcStr(ArcStr::from(text.to_string()), OnceLock::new()),
ast: OnceLock::new(),
}
.into();
entry.1 = *version;
self.file_docs.lock().unwrap().remove(&uri);
}
entry
}
};
action(entry)
})
}
}
#[derive(Default)]
enum CompilationState {
#[default]
None,
Compiling,
Compiled(Instant),
}
pub fn start_workspace_thread(url: Uri, client: Client) -> Result<Arc<Workspace>, String> {
let base = url.strip_header().to_string();
let path = PathBuf::from(&base);
let (sender, mut receiver) = channel(1);
let _handle = tokio::task::spawn(async move {
let mut alloc = SyncArenaAllocator::default();
let mut file_text = HashMap::<Uri, (Rope, i32)>::new();
let mut files = HashMap::<Uri, (Arc<SourceCode>, i32)>::new();
let mut parsed = HashMap::<Uri, DocumentParsingResult>::new();
macro_rules! get_file_text {
($uri:expr, $version:expr) => {
file_text.entry($uri).or_insert_with_key(|key| {
let path = key.strip_header();
(
Rope::from_str(&std::fs::read_to_string(path).unwrap()),
$version,
)
})
};
}
let state = Arc::new(State::default());
'global: loop {
alloc.reset();
let symbols = Arc::new(Mutex::new(HashMap::new()));
let mut files = vec![];
for file in SearchBuilder::default().location(&path).ext("leaf").build() {
state.with_file_code(Uri::from_file_path(file).unwrap(), |(code, _)| {
files.push(code.clone());
});
}
let now = Instant::now();
let mut context = CompilationContext::new(&alloc);
{
let state = state.clone();
let symbols = symbols.clone();
context.add_event_callback(move |e| unsafe {
match e {
Event::Symbol { value, position } => {
let mut symbols = symbols.lock().unwrap();
symbols.insert(position.path.clone(), std::mem::transmute(*value));
}
Event::Definition { value, position } => {
let mut symbols = symbols.lock().unwrap();
symbols.insert(position.path.clone(), std::mem::transmute(*value));
}
Event::Diagnostic(diag) => {
let uri = Uri::from_file_path(&diag.position.file.file).unwrap();
let mut file_docs = state.file_docs.lock().unwrap();
if let Some(docs) = file_docs.get_mut(&uri) {
let docs = Arc::get_mut(docs).unwrap();
docs.diagnostics.extend(make_diagnostics(diag));
}
}
}
});
}
if let Err(err) = context.extend(
AssemblyIdentifier {
version: Version::default(),
name: Cow::Borrowed("Leaf lsp tmp"),
},
&files,
) {
let uri = Uri::from_file_path(&err.position.file.file).unwrap();
let diagnostics = {
let mut file_docs = state.file_docs.lock().unwrap();
if let Some(docs) = file_docs.get_mut(&uri) {
let docs = Arc::get_mut(docs).unwrap();
docs.diagnostics.extend(make_diagnostics(&err));
docs.diagnostics.clone()
} else {
vec![]
}
};
client.publish_diagnostics(uri, diagnostics, None).await;
eprintln!("Compilation failed in {:?}", now.elapsed());
} else {
client.semantic_tokens_refresh().await.unwrap();
eprintln!("Compilation succeeded in {:?}", now.elapsed());
}
while let Some(event) = receiver.recv().await {
match event {
Request::Reload => continue 'global,
Request::Hover(params, result) => {
Request::Reload => {
continue 'global;
}
Request::Hover(_params, result) => {
result.1.notify_one();
}
Request::GoToDeclaration(params, result) => {
Request::GoToDeclaration(_params, result) => {
result.1.notify_one();
}
Request::FileChanged(params) => {
let (file, version) = get_file_text!(
state.with_file_text(
params.text_document.uri.clone(),
params.text_document.version
|(file, version)| {
eprintln!("File {:?} changed", params.text_document.uri.as_str());
for change in params.content_changes {
let Some(range) = change.range else {
*file = Rope::from_str(&change.text);
continue;
};
fn pos_to_char_index(
rope: &ropey::Rope,
pos: Position,
) -> usize {
let line_start = rope.line_to_char(pos.line as usize);
line_start + pos.character as usize
}
let start = pos_to_char_index(file, range.start);
let end = pos_to_char_index(file, range.end);
file.remove(start..end);
file.insert(start, &change.text);
}
*version = params.text_document.version;
},
);
eprintln!("File {:?} changed", params.text_document.uri.as_str());
for change in params.content_changes {
let Some(range) = change.range else {
*file = Rope::from_str(&change.text);
continue;
};
fn pos_to_char_index(rope: &ropey::Rope, pos: Position) -> usize {
let line_start = rope.line_to_char(pos.line as usize);
line_start + pos.character as usize
}
let start = pos_to_char_index(file, range.start);
let end = pos_to_char_index(file, range.end);
file.remove(start..end);
file.insert(start, &change.text);
}
*version = params.text_document.version;
client.semantic_tokens_refresh().await.unwrap();
}
Request::SemanticTokens(params, result) => {
eprintln!(
"Sending semantic tokens for file {:?}",
params.text_document.uri.as_str()
);
let (file, version) = get_file_text!(params.text_document.uri.clone(), 0);
let source = match files.get_mut(&params.text_document.uri) {
None => {
&files
let parsed =
state.with_file_code(params.text_document.uri.clone(), |(code, _)| {
let mut file_docs = state.file_docs.lock().unwrap();
file_docs
.entry(params.text_document.uri.clone())
.or_insert_with_key(|uri| {
(
SourceCode {
file: uri.strip_header().into(),
text: Text::ArcStr(
ArcStr::from(file.to_string()),
OnceLock::new(),
),
ast: OnceLock::new(),
}
.into(),
*version,
)
.or_insert_with(|| {
let symbols = symbols.lock().unwrap();
Arc::new(generate_syntax_tokens(code, &symbols))
})
.0
}
Some((source, v)) => {
if *v != *version {
eprintln!(
"Updating file {:?}",
params.text_document.uri.as_str()
);
*source = SourceCode {
file: params.text_document.uri.strip_header().into(),
text: Text::ArcStr(
ArcStr::from(file.to_string()),
OnceLock::new(),
),
ast: OnceLock::new(),
}
.into();
*v = *version;
parsed.remove(&params.text_document.uri);
}
source
}
};
let parsed = parsed
.entry(params.text_document.uri.clone())
.or_insert_with(|| parse_document(source));
.clone()
});
let _ = client
.publish_diagnostics(