diff --git a/core/Cargo.toml b/core/Cargo.toml index 322231bd..de22d04d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -26,7 +26,7 @@ xi-rope = { path = "../../xi-editor/rust/rope/" } xi-rpc = { path = "../../xi-editor/rust/rpc/" } fzyr = "0.1.2" uuid = { version = "0.7.4", features = ["v4"] } -lsp-types = "0.82.0" +lsp-types = { version = "0.82.0", features = ["proposed"] } druid = { path = "../../druid/druid", features = ["svg"] } [build-dependencies] diff --git a/core/src/buffer.rs b/core/src/buffer.rs index 7797f752..170c2043 100644 --- a/core/src/buffer.rs +++ b/core/src/buffer.rs @@ -5,10 +5,14 @@ }; use druid::{Env, PaintCtx}; use language::{new_highlight_config, new_parser, LapceLanguage}; +use lsp_types::SemanticTokens; +use lsp_types::SemanticTokensLegend; +use lsp_types::SemanticTokensServerCapabilities; use lsp_types::{ CodeActionResponse, Position, Range, TextDocumentContentChangeEvent, }; use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::Value; use std::sync::mpsc::{channel, Receiver, Sender}; use std::{ borrow::Cow, @@ -77,6 +81,7 @@ pub struct Buffer { pub rope: Rope, highlight_config: Arc, highlight_names: Vec, + pub semantic_tokens: Option>, pub highlights: Vec<(usize, usize, Highlight)>, pub line_highlights: HashMap>, undos: Vec>, @@ -118,6 +123,7 @@ pub fn new( id: buffer_id.clone(), rope, highlight_config: Arc::new(highlight_config), + semantic_tokens: None, highlight_names, highlights: Vec::new(), line_highlights: HashMap::new(), @@ -200,27 +206,97 @@ pub fn len(&self) -> usize { self.rope.len() } - pub fn highlights_apply_delta( - &mut self, - delta: &RopeDelta, - ) -> Vec<(usize, usize, Highlight)> { + pub fn highlights_apply_delta(&mut self, delta: &RopeDelta) { let mut transformer = Transformer::new(delta); - self.highlights - .iter() - .map(|h| { - ( - transformer.transform(h.0, true), - transformer.transform(h.1, true), - h.2.clone(), - ) - }) - .collect() + if let Some(semantic_tokens) = self.semantic_tokens.as_mut() { + self.semantic_tokens = Some( + semantic_tokens + .iter() + .map(|h| { + ( + transformer.transform(h.0, true), + transformer.transform(h.1, true), + h.2.clone(), + ) + }) + .collect(), + ) + } else { + self.highlights = self + .highlights + .iter() + .map(|h| { + ( + transformer.transform(h.0, true), + transformer.transform(h.1, true), + h.2.clone(), + ) + }) + .collect() + } + } + + fn format_semantic_tokens( + &self, + semantic_tokens_provider: Option, + value: Value, + ) -> Option> { + let semantic_tokens: SemanticTokens = serde_json::from_value(value).ok()?; + let semantic_tokens_provider = semantic_tokens_provider.as_ref()?; + let semantic_lengends = semantic_tokens_lengend(semantic_tokens_provider); + + let mut highlights = Vec::new(); + let mut line = 0; + let mut start = 0; + for semantic_token in &semantic_tokens.data { + if semantic_token.delta_line > 0 { + line += semantic_token.delta_line as usize; + start = self.offset_of_line(line); + } + start += semantic_token.delta_start as usize; + let end = start + semantic_token.length as usize; + let kind = semantic_lengends.token_types + [semantic_token.token_type as usize] + .as_str() + .to_string(); + highlights.push((start, end, kind)); + } + + Some(highlights) + } + + pub fn set_semantic_tokens( + &mut self, + semantic_tokens_provider: Option, + value: Value, + ) -> Option<()> { + let semantic_tokens = + self.format_semantic_tokens(semantic_tokens_provider, value)?; + self.semantic_tokens = Some(semantic_tokens); + self.line_highlights = HashMap::new(); + let window_id = self.window_id; + let tab_id = self.tab_id; + let buffer_id = self.id; + thread::spawn(move || { + let state = LAPCE_APP_STATE.get_tab_state(&window_id, &tab_id); + for (view_id, editor) in state.editor_split.lock().editors.iter() { + if editor.buffer_id.as_ref() == Some(&buffer_id) { + LAPCE_APP_STATE.submit_ui_command( + LapceUICommand::FillTextLayouts, + view_id.clone(), + ); + } + } + }); + None } pub fn update_highlights(&mut self) { self.line_highlights = HashMap::new(); self.sender .send((self.window_id, self.tab_id, self.id, self.rev)); + let state = LAPCE_APP_STATE.get_tab_state(&self.window_id, &self.tab_id); + state.lsp.lock().get_semantic_tokens(&self); } pub fn get_line_highligh( @@ -229,23 +305,41 @@ pub fn get_line_highligh( ) -> &Vec<(usize, usize, String)> { if self.line_highlights.get(&line).is_none() { let mut line_highlight = Vec::new(); - let start_offset = self.offset_of_line(line); - let end_offset = self.offset_of_line(line + 1) - 1; - for (start, end, hl) in &self.highlights { - if *start > end_offset { - break; + if let Some(semantic_tokens) = self.semantic_tokens.as_ref() { + let start_offset = self.offset_of_line(line); + let end_offset = self.offset_of_line(line + 1) - 1; + for (start, end, hl) in semantic_tokens { + if *start > end_offset { + break; + } + if *start >= start_offset && *start <= end_offset { + let end = if *end > end_offset { + end_offset - start_offset + } else { + end - start_offset + }; + line_highlight.push((start - start_offset, end, hl.clone())); + } } - if *start >= start_offset && *start <= end_offset { - let end = if *end > end_offset { - end_offset - start_offset - } else { - end - start_offset - }; - line_highlight.push(( - start - start_offset, - end, - self.highlight_names[hl.0].to_string(), - )); + } else { + let start_offset = self.offset_of_line(line); + let end_offset = self.offset_of_line(line + 1) - 1; + for (start, end, hl) in &self.highlights { + if *start > end_offset { + break; + } + if *start >= start_offset && *start <= end_offset { + let end = if *end > end_offset { + end_offset - start_offset + } else { + end - start_offset + }; + line_highlight.push(( + start - start_offset, + end, + self.highlight_names[hl.0].to_string(), + )); + } } } self.line_highlights.insert(line, line_highlight); @@ -438,7 +532,7 @@ fn apply_delta( new_count: new_hard_count, }; self.code_actions = HashMap::new(); - self.highlights = self.highlights_apply_delta(delta); + self.highlights_apply_delta(delta); self.update_size(ui_state, &inval_lines); ui_state.update_text_layouts(&inval_lines); } @@ -1139,6 +1233,8 @@ pub fn get_text_layout( start..end, TextAttribute::TextColor(color.clone()), ); + } else { + println!("no color for {} {}", hl, start); } } let layout = layout_builder.build().unwrap(); @@ -1384,3 +1480,16 @@ pub fn get_document_content_changes( None } + +fn semantic_tokens_lengend( + semantic_tokens_provider: &SemanticTokensServerCapabilities, +) -> SemanticTokensLegend { + match semantic_tokens_provider { + SemanticTokensServerCapabilities::SemanticTokensOptions(options) => { + options.legend.clone() + } + SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions( + options, + ) => options.semantic_tokens_options.legend.clone(), + } +} diff --git a/core/src/editor.rs b/core/src/editor.rs index 40abb515..83c7bc50 100644 --- a/core/src/editor.rs +++ b/core/src/editor.rs @@ -111,7 +111,7 @@ pub fn new( editor_id: WidgetId::next(), view_id: WidgetId::next(), split_id, - tab_id: tab_id, + tab_id, buffer_id, char_width: 7.6171875, width: 0.0, @@ -143,6 +143,7 @@ pub fn update( let old_editor = old_data.get_editor(&self.view_id); let line_height = env.get(LapceTheme::EDITOR_LINE_HEIGHT); + if buffer.max_len != old_buffer.max_len || buffer.text_layouts.len() != old_buffer.text_layouts.len() { @@ -1447,11 +1448,12 @@ pub fn show_completion( let buffer = self.buffers.get(&buffer_id)?; let offset = editor.selection.get_cursor_offset(); let prev_offset = buffer.prev_code_boundary(offset); + let next_offset = buffer.next_code_boundary(offset); if request_id != prev_offset { return None; } - let input = buffer.slice_to_cow(prev_offset..offset).to_string(); + let input = buffer.slice_to_cow(prev_offset..next_offset).to_string(); self.completion.update(input, items); LAPCE_APP_STATE.submit_ui_command( LapceUICommand::RequestLayout, diff --git a/core/src/lsp.rs b/core/src/lsp.rs index caeadb58..0a3f5e14 100644 --- a/core/src/lsp.rs +++ b/core/src/lsp.rs @@ -7,6 +7,8 @@ use anyhow::{anyhow, Result}; use druid::{WidgetId, WindowId}; use jsonrpc_lite::{Id, JsonRpc, Params}; +use lsp_types::SemanticHighlightingClientCapability; +use lsp_types::SemanticTokensClientCapabilities; use parking_lot::Mutex; use std::{ collections::HashMap, @@ -31,7 +33,7 @@ DidSaveTextDocumentParams, DocumentFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, FormattingOptions, GotoDefinitionParams, InitializeParams, InitializeResult, PartialResultParams, Position, - PublishDiagnosticsParams, Range, ServerCapabilities, + PublishDiagnosticsParams, Range, SemanticTokensParams, ServerCapabilities, TextDocumentClientCapabilities, TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, TextDocumentSyncCapability, TextDocumentSyncKind, TextEdit, TraceOption, Url, @@ -58,7 +60,7 @@ pub enum LspProcess { pub struct LspCatalog { window_id: WindowId, tab_id: WidgetId, - clients: HashMap>>, + clients: HashMap>, } impl Drop for LspCatalog { @@ -78,7 +80,7 @@ pub fn new(window_id: WindowId, tab_id: WidgetId) -> LspCatalog { pub fn stop(&self) { for (_, client) in self.clients.iter() { - match &mut client.lock().process { + match &mut client.state.lock().process { LspProcess::Child(child) => { child.kill(); } @@ -103,14 +105,20 @@ pub fn start_server( .clone(); match workspace_type { LapceWorkspaceType::Local => { - let client = - LspClient::new(self.window_id, self.tab_id, exec_path, options); + let client = LspClient::new( + self.window_id, + self.tab_id, + language_id.to_string(), + exec_path, + options, + ); self.clients.insert(language_id.to_string(), client); } LapceWorkspaceType::RemoteSSH(host) => { if let Ok(client) = LspClient::new_ssh( self.window_id, self.tab_id, + language_id.to_string(), exec_path, options, &host, @@ -123,7 +131,7 @@ pub fn start_server( pub fn save_buffer(&self, buffer: &Buffer) { if let Some(client) = self.clients.get(&buffer.language_id) { - client.lock().send_did_save(buffer); + client.send_did_save(buffer); } } @@ -136,7 +144,7 @@ pub fn new_buffer( ) { let document_uri = Url::from_file_path(path).unwrap(); if let Some(client) = self.clients.get(language_id) { - client.lock().send_did_open( + client.send_did_open( buffer_id, document_uri.clone(), language_id, @@ -152,7 +160,7 @@ pub fn update( rev: u64, ) { if let Some(client) = self.clients.get(&buffer.language_id) { - client.lock().update(buffer, content_change, rev); + client.update(buffer, content_change, rev); } } @@ -163,7 +171,13 @@ pub fn get_completion( position: Position, ) { if let Some(client) = self.clients.get(&buffer.language_id) { - client.lock().get_completion(request_id, buffer, position); + client.get_completion(request_id, buffer, position); + } + } + + pub fn get_semantic_tokens(&self, buffer: &Buffer) { + if let Some(client) = self.clients.get(&buffer.language_id) { + client.get_semantic_tokens(buffer); } } @@ -175,9 +189,7 @@ pub fn get_code_actions(&self, offset: usize, buffer: &Buffer) { end: buffer.offset_to_position(offset), }; if let Some(client) = self.clients.get(&buffer.language_id) { - client - .lock() - .get_code_actions(buffer, offset, range.clone()); + client.get_code_actions(buffer, offset, range.clone()); } } @@ -186,7 +198,7 @@ pub fn get_document_symbols( buffer: &Buffer, ) -> Option { if let Some(client) = self.clients.get(&buffer.language_id) { - let receiver = { client.lock().get_document_symbols(buffer) }; + let receiver = { client.get_document_symbols(buffer) }; let result = receiver.recv_timeout(Duration::from_millis(100)); let result = result.ok()?; let value = result.ok()?; @@ -203,13 +215,13 @@ pub fn go_to_definition( position: Position, ) { if let Some(client) = self.clients.get(&buffer.language_id) { - client.lock().go_to_definition(request_id, buffer, position); + client.go_to_definition(request_id, buffer, position); } } pub fn get_document_formatting(&self, buffer: &Buffer) -> Option> { if let Some(client) = self.clients.get(&buffer.language_id) { - let receiver = { client.lock().get_document_formatting(buffer) }; + let receiver = { client.get_document_formatting(buffer) }; let result = receiver.recv_timeout(Duration::from_millis(100)); let result = result.ok()?; let value = result.ok()?; @@ -221,41 +233,47 @@ pub fn get_document_formatting(&self, buffer: &Buffer) -> Option> pub fn request_document_formatting(&self, buffer: &Buffer) { if let Some(client) = self.clients.get(&buffer.language_id) { - client.lock().document_formatting(buffer); + client.document_formatting(buffer); } } } pub trait Callable: Send { - fn call(self: Box, client: &mut LspClient, result: Result); + fn call(self: Box, client: &LspClient, result: Result); } -impl)> Callable for F { - fn call(self: Box, client: &mut LspClient, result: Result) { +impl)> Callable for F { + fn call(self: Box, client: &LspClient, result: Result) { (*self)(client, result) } } +pub struct LspState { + next_id: u64, + writer: Box, + process: LspProcess, + pending: HashMap, + pub server_capabilities: Option, + pub opened_documents: HashMap, + pub is_initialized: bool, +} + pub struct LspClient { window_id: WindowId, tab_id: WidgetId, - writer: Box, - next_id: u64, - pending: HashMap, + language_id: String, options: Option, - process: LspProcess, - pub server_capabilities: Option, - pub opened_documents: HashMap, - pub is_initialized: bool, + state: Arc>, } impl LspClient { pub fn new( window_id: WindowId, tab_id: WidgetId, + language_id: String, exec_path: &str, options: Option, - ) -> Arc> { + ) -> Arc { let mut process = Command::new(exec_path) .stdin(Stdio::piped()) .stdout(Stdio::piped()) @@ -265,18 +283,21 @@ pub fn new( let writer = Box::new(BufWriter::new(process.stdin.take().unwrap())); let stdout = process.stdout.take().unwrap(); - let lsp_client = Arc::new(Mutex::new(LspClient { + let lsp_client = Arc::new(LspClient { window_id, tab_id, - writer, - next_id: 0, - process: LspProcess::Child(process), - pending: HashMap::new(), - server_capabilities: None, - opened_documents: HashMap::new(), - is_initialized: false, + language_id, options, - })); + state: Arc::new(Mutex::new(LspState { + next_id: 0, + writer, + process: LspProcess::Child(process), + pending: HashMap::new(), + server_capabilities: None, + opened_documents: HashMap::new(), + is_initialized: false, + })), + }); let local_lsp_client = lsp_client.clone(); thread::spawn(move || { @@ -284,7 +305,7 @@ pub fn new( loop { match read_message(&mut reader) { Ok(message_str) => { - local_lsp_client.lock().handle_message(message_str.as_ref()); + local_lsp_client.handle_message(message_str.as_ref()); } Err(err) => { eprintln!("lsp read Error occurred {:?}", err); @@ -300,28 +321,32 @@ pub fn new( pub fn new_ssh( window_id: WindowId, tab_id: WidgetId, + language_id: String, exec_path: &str, options: Option, host: &str, - ) -> Result>> { + ) -> Result> { let mut ssh_session = SshSession::new(host)?; let mut channel = ssh_session.get_channel()?; ssh_session.channel_exec(&mut channel, exec_path)?; println!("lsp {}", exec_path); let writer = Box::new(ssh_session.get_stream(&channel)); let reader = ssh_session.get_stream(&channel); - let lsp_client = Arc::new(Mutex::new(LspClient { + let lsp_client = Arc::new(LspClient { window_id, tab_id, - writer, - next_id: 0, - pending: HashMap::new(), - server_capabilities: None, - process: LspProcess::SshSession(ssh_session), - opened_documents: HashMap::new(), - is_initialized: false, + language_id, + state: Arc::new(Mutex::new(LspState { + next_id: 0, + writer, + process: LspProcess::SshSession(ssh_session), + pending: HashMap::new(), + server_capabilities: None, + opened_documents: HashMap::new(), + is_initialized: false, + })), options, - })); + }); let local_lsp_client = lsp_client.clone(); // let reader = ssh_session.get_async_stream(channel.stream(0))?; @@ -330,7 +355,7 @@ pub fn new_ssh( loop { match read_message(&mut reader) { Ok(message_str) => { - local_lsp_client.lock().handle_message(message_str.as_ref()); + local_lsp_client.handle_message(message_str.as_ref()); } Err(err) => { //println!("Error occurred {:?}", err); @@ -343,7 +368,7 @@ pub fn new_ssh( } pub fn update( - &mut self, + &self, buffer: &Buffer, content_change: &TextDocumentContentChangeEvent, rev: u64, @@ -355,8 +380,12 @@ pub fn update( } } - pub fn get_uri(&mut self, buffer: &Buffer) -> Url { - if !self.opened_documents.contains_key(&buffer.id) { + pub fn get_uri(&self, buffer: &Buffer) -> Url { + let exits = { + let state = self.state.lock(); + state.opened_documents.contains_key(&buffer.id) + }; + if !exits { let document_uri = Url::from_file_path(&buffer.path).unwrap(); self.send_did_open( &buffer.id, @@ -365,11 +394,16 @@ pub fn get_uri(&mut self, buffer: &Buffer) -> Url { buffer.get_document(), ); } - self.opened_documents.get(&buffer.id).unwrap().clone() + self.state + .lock() + .opened_documents + .get(&buffer.id) + .unwrap() + .clone() } pub fn get_completion( - &mut self, + &self, request_id: usize, buffer: &Buffer, position: Position, @@ -390,12 +424,33 @@ pub fn get_completion( }) } - pub fn get_code_actions( - &mut self, - buffer: &Buffer, - offset: usize, - range: Range, - ) { + pub fn get_semantic_tokens(&self, buffer: &Buffer) { + let uri = self.get_uri(buffer); + let window_id = self.window_id; + let tab_id = self.tab_id; + let rev = buffer.rev; + let buffer_id = buffer.id; + self.request_semantic_tokens(uri, move |lsp_client, result| { + if let Ok(res) = result { + let semantic_tokens_provider = lsp_client + .state + .lock() + .server_capabilities + .as_ref() + .unwrap() + .semantic_tokens_provider + .clone(); + thread::spawn(move || { + let state = LAPCE_APP_STATE.get_tab_state(&window_id, &tab_id); + let mut editor_split = state.editor_split.lock(); + let buffer = editor_split.buffers.get_mut(&buffer_id).unwrap(); + buffer.set_semantic_tokens(semantic_tokens_provider, res); + }); + } + }) + } + + pub fn get_code_actions(&self, buffer: &Buffer, offset: usize, range: Range) { let uri = self.get_uri(buffer); let window_id = self.window_id; let tab_id = self.tab_id; @@ -414,7 +469,7 @@ pub fn get_code_actions( }) } - pub fn handle_message(&mut self, message: &str) { + pub fn handle_message(&self, message: &str) { match JsonRpc::parse(message) { Ok(JsonRpc::Request(obj)) => { // trace!("client received unexpected request: {:?}", obj) @@ -441,7 +496,7 @@ pub fn handle_message(&mut self, message: &str) { } } - pub fn handle_notification(&mut self, method: &str, params: Params) { + pub fn handle_notification(&self, method: &str, params: Params) { match method { "textDocument/publishDiagnostics" => { let window_id = self.window_id; @@ -483,45 +538,43 @@ pub fn handle_notification(&mut self, method: &str, params: Params) { } }); } - _ => (), + _ => println!("{} {:?}", method, params), } } - pub fn handle_response(&mut self, id: u64, result: Result) { + pub fn handle_response(&self, id: u64, result: Result) { let callback = self + .state + .lock() .pending .remove(&id) .unwrap_or_else(|| panic!("id {} missing from request table", id)); callback.call(self, result); } - pub fn write(&mut self, msg: &str) { - self.writer + pub fn write(&self, msg: &str) { + let mut state = self.state.lock(); + state + .writer .write_all(msg.as_bytes()) .expect("error writing to stdin"); - - self.writer.flush().expect("error flushing child stdin"); + state.writer.flush().expect("error flushing child stdin"); } - pub fn send_request( - &mut self, - method: &str, - params: Params, - completion: Callback, - ) { - let request = JsonRpc::request_with_params( - Id::Num(self.next_id as i64), - method, - params, - ); + pub fn send_request(&self, method: &str, params: Params, completion: Callback) { + let request = { + let mut state = self.state.lock(); + let next_id = state.next_id; + state.pending.insert(next_id, completion); + state.next_id += 1; - self.pending.insert(self.next_id, completion); - self.next_id += 1; + JsonRpc::request_with_params(Id::Num(next_id as i64), method, params) + }; self.send_rpc(&to_value(&request).unwrap()); } - fn send_rpc(&mut self, value: &Value) { + fn send_rpc(&self, value: &Value) { let rpc = match prepare_lsp_json(value) { Ok(r) => r, Err(err) => panic!("Encoding Error {:?}", err), @@ -530,19 +583,19 @@ fn send_rpc(&mut self, value: &Value) { self.write(rpc.as_ref()); } - pub fn send_notification(&mut self, method: &str, params: Params) { + pub fn send_notification(&self, method: &str, params: Params) { let notification = JsonRpc::notification_with_params(method, params); let res = to_value(¬ification).unwrap(); self.send_rpc(&res); } - pub fn send_initialized(&mut self) { + pub fn send_initialized(&self) { self.send_notification("initialized", Params::from(json!({}))); } - pub fn send_initialize(&mut self, root_uri: Option, on_init: CB) + pub fn send_initialize(&self, root_uri: Option, on_init: CB) where - CB: 'static + Send + FnOnce(&mut LspClient, Result), + CB: 'static + Send + FnOnce(&LspClient, Result), { let client_capabilities = ClientCapabilities { text_document: Some(TextDocumentClientCapabilities { @@ -576,6 +629,11 @@ pub fn send_initialize(&mut self, root_uri: Option, on_init: CB) }), ..Default::default() }), + semantic_highlighting_capabilities: Some( + SemanticHighlightingClientCapability { + semantic_highlighting: true, + }, + ), ..Default::default() }), ..Default::default() @@ -596,10 +654,7 @@ pub fn send_initialize(&mut self, root_uri: Option, on_init: CB) self.send_request("initialize", params, Box::new(on_init)); } - pub fn get_document_symbols( - &mut self, - buffer: &Buffer, - ) -> Receiver> { + pub fn get_document_symbols(&self, buffer: &Buffer) -> Receiver> { let uri = self.get_uri(buffer); let (sender, receiver) = channel(); self.request_document_symbols(uri, move |lsp_client, result| { @@ -608,9 +663,9 @@ pub fn get_document_symbols( return receiver; } - pub fn request_document_symbols(&mut self, document_uri: Url, cb: CB) + pub fn request_document_symbols(&self, document_uri: Url, cb: CB) where - CB: 'static + Send + FnOnce(&mut LspClient, Result), + CB: 'static + Send + FnOnce(&LspClient, Result), { let params = DocumentSymbolParams { text_document: TextDocumentIdentifier { uri: document_uri }, @@ -622,7 +677,7 @@ pub fn request_document_symbols(&mut self, document_uri: Url, cb: CB) } pub fn get_document_formatting( - &mut self, + &self, buffer: &Buffer, ) -> Receiver> { let uri = self.get_uri(buffer); @@ -633,7 +688,7 @@ pub fn get_document_formatting( return receiver; } - pub fn document_formatting(&mut self, buffer: &Buffer) { + pub fn document_formatting(&self, buffer: &Buffer) { let uri = self.get_uri(buffer); let rev = buffer.rev; let window_id = self.window_id; @@ -658,9 +713,9 @@ pub fn document_formatting(&mut self, buffer: &Buffer) { }) } - pub fn request_document_formatting(&mut self, document_uri: Url, cb: CB) + pub fn request_document_formatting(&self, document_uri: Url, cb: CB) where - CB: 'static + Send + FnOnce(&mut LspClient, Result), + CB: 'static + Send + FnOnce(&LspClient, Result), { let params = DocumentFormattingParams { text_document: TextDocumentIdentifier { uri: document_uri }, @@ -676,7 +731,7 @@ pub fn request_document_formatting(&mut self, document_uri: Url, cb: CB) } pub fn go_to_definition( - &mut self, + &self, request_id: usize, buffer: &Buffer, position: Position, @@ -698,12 +753,12 @@ pub fn go_to_definition( } pub fn request_definition( - &mut self, + &self, document_uri: Url, position: Position, on_definition: CB, ) where - CB: 'static + Send + FnOnce(&mut LspClient, Result), + CB: 'static + Send + FnOnce(&LspClient, Result), { let params = GotoDefinitionParams { text_document_position_params: TextDocumentPositionParams { @@ -721,13 +776,9 @@ pub fn request_definition( ); } - pub fn request_code_actions( - &mut self, - document_uri: Url, - range: Range, - cb: CB, - ) where - CB: 'static + Send + FnOnce(&mut LspClient, Result), + pub fn request_code_actions(&self, document_uri: Url, range: Range, cb: CB) + where + CB: 'static + Send + FnOnce(&LspClient, Result), { let params = CodeActionParams { text_document: TextDocumentIdentifier { uri: document_uri }, @@ -740,13 +791,26 @@ pub fn request_code_actions( self.send_request("textDocument/codeAction", params, Box::new(cb)); } + pub fn request_semantic_tokens(&self, document_uri: Url, cb: CB) + where + CB: 'static + Send + FnOnce(&LspClient, Result), + { + let params = SemanticTokensParams { + text_document: TextDocumentIdentifier { uri: document_uri }, + work_done_progress_params: WorkDoneProgressParams::default(), + partial_result_params: PartialResultParams::default(), + }; + let params = Params::from(serde_json::to_value(params).unwrap()); + self.send_request("textDocument/semanticTokens/full", params, Box::new(cb)); + } + pub fn request_completion( - &mut self, + &self, document_uri: Url, position: Position, on_completion: CB, ) where - CB: 'static + Send + FnOnce(&mut LspClient, Result), + CB: 'static + Send + FnOnce(&LspClient, Result), { let completion_params = CompletionParams { text_document_position: TextDocumentPositionParams { @@ -766,14 +830,44 @@ pub fn request_completion( } pub fn send_did_open( - &mut self, + &self, buffer_id: &BufferId, document_uri: Url, language_id: &str, document_text: String, ) { - self.opened_documents - .insert(buffer_id.clone(), document_uri.clone()); + let is_initialized = { + let mut state = self.state.lock(); + state + .opened_documents + .insert(buffer_id.clone(), document_uri.clone()); + state.is_initialized + }; + + if !is_initialized { + let workspace_path = LAPCE_APP_STATE + .get_tab_state(&self.window_id, &self.tab_id) + .workspace + .lock() + .path + .clone(); + let root_url = Url::from_directory_path(workspace_path).unwrap(); + let (sender, receiver) = channel(); + self.send_initialize(Some(root_url), move |lsp_client, result| { + if let Ok(result) = result { + { + let init_result: InitializeResult = + serde_json::from_value(result).unwrap(); + let mut state = lsp_client.state.lock(); + state.server_capabilities = Some(init_result.capabilities); + state.is_initialized = true; + } + lsp_client.send_initialized(); + } + sender.send(true); + }); + receiver.recv_timeout(Duration::from_millis(1000)); + } let text_document_did_open_params = DidOpenTextDocumentParams { text_document: TextDocumentItem { @@ -783,35 +877,13 @@ pub fn send_did_open( text: document_text, }, }; - let params = Params::from( serde_json::to_value(text_document_did_open_params).unwrap(), ); - let workspace_path = LAPCE_APP_STATE - .get_tab_state(&self.window_id, &self.tab_id) - .workspace - .lock() - .path - .clone(); - let root_url = Url::from_directory_path(workspace_path).unwrap(); - if !self.is_initialized { - self.send_initialize(Some(root_url), move |lsp_client, result| { - if let Ok(result) = result { - let init_result: InitializeResult = - serde_json::from_value(result).unwrap(); - - lsp_client.server_capabilities = Some(init_result.capabilities); - lsp_client.is_initialized = true; - lsp_client.send_initialized(); - lsp_client.send_notification("textDocument/didOpen", params); - } - }); - } else { - self.send_notification("textDocument/didOpen", params); - } + self.send_notification("textDocument/didOpen", params); } - pub fn send_did_save(&mut self, buffer: &Buffer) { + pub fn send_did_save(&self, buffer: &Buffer) { let uri = self.get_uri(buffer); let params = DidSaveTextDocumentParams { text_document: TextDocumentIdentifier { uri }, @@ -822,7 +894,7 @@ pub fn send_did_save(&mut self, buffer: &Buffer) { } pub fn send_did_change( - &mut self, + &self, buffer: &Buffer, changes: Vec, version: u64, @@ -843,7 +915,8 @@ pub fn send_did_change( } pub fn get_sync_kind(&self) -> Option { - let text_document_sync = self + let state = self.state.lock(); + let text_document_sync = state .server_capabilities .as_ref() .and_then(|c| c.text_document_sync.as_ref())?;