1 use crate::{ 2 transport::{Payload, Transport}, 3 Call, Error, OffsetEncoding, Result, 4 }; 5 6 use helix_core::{find_root, ChangeSet, Rope}; 7 use jsonrpc_core as jsonrpc; 8 use lsp_types as lsp; 9 use serde_json::Value; 10 use std::future::Future; 11 use std::process::Stdio; 12 use std::sync::{ 13 atomic::{AtomicU64, Ordering}, 14 Arc, 15 }; 16 use tokio::{ 17 io::{BufReader, BufWriter}, 18 process::{Child, Command}, 19 sync::{ 20 mpsc::{channel, UnboundedReceiver, UnboundedSender}, 21 Notify, OnceCell, 22 }, 23 }; 24 25 #[derive(Debug)] 26 pub struct Client { 27 id: usize, 28 _process: Child, 29 server_tx: UnboundedSender<Payload>, 30 request_counter: AtomicU64, 31 pub(crate) capabilities: OnceCell<lsp::ServerCapabilities>, 32 offset_encoding: OffsetEncoding, 33 config: Option<Value>, 34 } 35 36 impl Client { 37 #[allow(clippy::type_complexity)] start( cmd: &str, args: &[String], config: Option<Value>, id: usize, ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc<Notify>)>38 pub fn start( 39 cmd: &str, 40 args: &[String], 41 config: Option<Value>, 42 id: usize, 43 ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc<Notify>)> { 44 let process = Command::new(cmd) 45 .args(args) 46 .stdin(Stdio::piped()) 47 .stdout(Stdio::piped()) 48 .stderr(Stdio::piped()) 49 // make sure the process is reaped on drop 50 .kill_on_drop(true) 51 .spawn(); 52 53 let mut process = process?; 54 55 // TODO: do we need bufreader/writer here? or do we use async wrappers on unblock? 56 let writer = BufWriter::new(process.stdin.take().expect("Failed to open stdin")); 57 let reader = BufReader::new(process.stdout.take().expect("Failed to open stdout")); 58 let stderr = BufReader::new(process.stderr.take().expect("Failed to open stderr")); 59 60 let (server_rx, server_tx, initialize_notify) = 61 Transport::start(reader, writer, stderr, id); 62 63 let client = Self { 64 id, 65 _process: process, 66 server_tx, 67 request_counter: AtomicU64::new(0), 68 capabilities: OnceCell::new(), 69 offset_encoding: OffsetEncoding::Utf8, 70 config, 71 }; 72 73 Ok((client, server_rx, initialize_notify)) 74 } 75 id(&self) -> usize76 pub fn id(&self) -> usize { 77 self.id 78 } 79 next_request_id(&self) -> jsonrpc::Id80 fn next_request_id(&self) -> jsonrpc::Id { 81 let id = self.request_counter.fetch_add(1, Ordering::Relaxed); 82 jsonrpc::Id::Num(id) 83 } 84 value_into_params(value: Value) -> jsonrpc::Params85 fn value_into_params(value: Value) -> jsonrpc::Params { 86 use jsonrpc::Params; 87 88 match value { 89 Value::Null => Params::None, 90 Value::Bool(_) | Value::Number(_) | Value::String(_) => Params::Array(vec![value]), 91 Value::Array(vec) => Params::Array(vec), 92 Value::Object(map) => Params::Map(map), 93 } 94 } 95 is_initialized(&self) -> bool96 pub fn is_initialized(&self) -> bool { 97 self.capabilities.get().is_some() 98 } 99 capabilities(&self) -> &lsp::ServerCapabilities100 pub fn capabilities(&self) -> &lsp::ServerCapabilities { 101 self.capabilities 102 .get() 103 .expect("language server not yet initialized!") 104 } 105 offset_encoding(&self) -> OffsetEncoding106 pub fn offset_encoding(&self) -> OffsetEncoding { 107 self.offset_encoding 108 } 109 110 /// Execute a RPC request on the language server. request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result> where R::Params: serde::Serialize, R::Result: core::fmt::Debug,111 async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result> 112 where 113 R::Params: serde::Serialize, 114 R::Result: core::fmt::Debug, // TODO: temporary 115 { 116 // a future that resolves into the response 117 let json = self.call::<R>(params).await?; 118 let response = serde_json::from_value(json)?; 119 Ok(response) 120 } 121 122 /// Execute a RPC request on the language server. call<R: lsp::request::Request>( &self, params: R::Params, ) -> impl Future<Output = Result<Value>> where R::Params: serde::Serialize,123 fn call<R: lsp::request::Request>( 124 &self, 125 params: R::Params, 126 ) -> impl Future<Output = Result<Value>> 127 where 128 R::Params: serde::Serialize, 129 { 130 let server_tx = self.server_tx.clone(); 131 let id = self.next_request_id(); 132 133 async move { 134 use std::time::Duration; 135 use tokio::time::timeout; 136 137 let params = serde_json::to_value(params)?; 138 139 let request = jsonrpc::MethodCall { 140 jsonrpc: Some(jsonrpc::Version::V2), 141 id, 142 method: R::METHOD.to_string(), 143 params: Self::value_into_params(params), 144 }; 145 146 let (tx, mut rx) = channel::<Result<Value>>(1); 147 148 server_tx 149 .send(Payload::Request { 150 chan: tx, 151 value: request, 152 }) 153 .map_err(|e| Error::Other(e.into()))?; 154 155 // TODO: specifiable timeout, delay other calls until initialize success 156 timeout(Duration::from_secs(20), rx.recv()) 157 .await 158 .map_err(|_| Error::Timeout)? // return Timeout 159 .ok_or(Error::StreamClosed)? 160 } 161 } 162 163 /// Send a RPC notification to the language server. notify<R: lsp::notification::Notification>( &self, params: R::Params, ) -> impl Future<Output = Result<()>> where R::Params: serde::Serialize,164 pub fn notify<R: lsp::notification::Notification>( 165 &self, 166 params: R::Params, 167 ) -> impl Future<Output = Result<()>> 168 where 169 R::Params: serde::Serialize, 170 { 171 let server_tx = self.server_tx.clone(); 172 173 async move { 174 let params = serde_json::to_value(params)?; 175 176 let notification = jsonrpc::Notification { 177 jsonrpc: Some(jsonrpc::Version::V2), 178 method: R::METHOD.to_string(), 179 params: Self::value_into_params(params), 180 }; 181 182 server_tx 183 .send(Payload::Notification(notification)) 184 .map_err(|e| Error::Other(e.into()))?; 185 186 Ok(()) 187 } 188 } 189 190 /// Reply to a language server RPC call. reply( &self, id: jsonrpc::Id, result: core::result::Result<Value, jsonrpc::Error>, ) -> impl Future<Output = Result<()>>191 pub fn reply( 192 &self, 193 id: jsonrpc::Id, 194 result: core::result::Result<Value, jsonrpc::Error>, 195 ) -> impl Future<Output = Result<()>> { 196 use jsonrpc::{Failure, Output, Success, Version}; 197 198 let server_tx = self.server_tx.clone(); 199 200 async move { 201 let output = match result { 202 Ok(result) => Output::Success(Success { 203 jsonrpc: Some(Version::V2), 204 id, 205 result, 206 }), 207 Err(error) => Output::Failure(Failure { 208 jsonrpc: Some(Version::V2), 209 id, 210 error, 211 }), 212 }; 213 214 server_tx 215 .send(Payload::Response(output)) 216 .map_err(|e| Error::Other(e.into()))?; 217 218 Ok(()) 219 } 220 } 221 222 // ------------------------------------------------------------------------------------------- 223 // General messages 224 // ------------------------------------------------------------------------------------------- 225 initialize(&self) -> Result<lsp::InitializeResult>226 pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> { 227 // TODO: delay any requests that are triggered prior to initialize 228 let root = find_root(None).and_then(|root| lsp::Url::from_file_path(root).ok()); 229 230 if self.config.is_some() { 231 log::info!("Using custom LSP config: {}", self.config.as_ref().unwrap()); 232 } 233 234 #[allow(deprecated)] 235 let params = lsp::InitializeParams { 236 process_id: Some(std::process::id()), 237 // root_path is obsolete, use root_uri 238 root_path: None, 239 root_uri: root, 240 initialization_options: self.config.clone(), 241 capabilities: lsp::ClientCapabilities { 242 text_document: Some(lsp::TextDocumentClientCapabilities { 243 completion: Some(lsp::CompletionClientCapabilities { 244 completion_item: Some(lsp::CompletionItemCapability { 245 snippet_support: Some(false), 246 ..Default::default() 247 }), 248 completion_item_kind: Some(lsp::CompletionItemKindCapability { 249 ..Default::default() 250 }), 251 context_support: None, // additional context information Some(true) 252 ..Default::default() 253 }), 254 hover: Some(lsp::HoverClientCapabilities { 255 // if not specified, rust-analyzer returns plaintext marked as markdown but 256 // badly formatted. 257 content_format: Some(vec![lsp::MarkupKind::Markdown]), 258 ..Default::default() 259 }), 260 code_action: Some(lsp::CodeActionClientCapabilities { 261 code_action_literal_support: Some(lsp::CodeActionLiteralSupport { 262 code_action_kind: lsp::CodeActionKindLiteralSupport { 263 value_set: [ 264 lsp::CodeActionKind::EMPTY, 265 lsp::CodeActionKind::QUICKFIX, 266 lsp::CodeActionKind::REFACTOR, 267 lsp::CodeActionKind::REFACTOR_EXTRACT, 268 lsp::CodeActionKind::REFACTOR_INLINE, 269 lsp::CodeActionKind::REFACTOR_REWRITE, 270 lsp::CodeActionKind::SOURCE, 271 lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS, 272 ] 273 .iter() 274 .map(|kind| kind.as_str().to_string()) 275 .collect(), 276 }, 277 }), 278 ..Default::default() 279 }), 280 ..Default::default() 281 }), 282 window: Some(lsp::WindowClientCapabilities { 283 work_done_progress: Some(true), 284 ..Default::default() 285 }), 286 ..Default::default() 287 }, 288 trace: None, 289 workspace_folders: None, 290 client_info: None, 291 locale: None, // TODO 292 }; 293 294 self.request::<lsp::request::Initialize>(params).await 295 } 296 shutdown(&self) -> Result<()>297 pub async fn shutdown(&self) -> Result<()> { 298 self.request::<lsp::request::Shutdown>(()).await 299 } 300 exit(&self) -> impl Future<Output = Result<()>>301 pub fn exit(&self) -> impl Future<Output = Result<()>> { 302 self.notify::<lsp::notification::Exit>(()) 303 } 304 305 /// Tries to shut down the language server but returns 306 /// early if server responds with an error. shutdown_and_exit(&self) -> Result<()>307 pub async fn shutdown_and_exit(&self) -> Result<()> { 308 self.shutdown().await?; 309 self.exit().await 310 } 311 312 /// Forcefully shuts down the language server ignoring any errors. force_shutdown(&self) -> Result<()>313 pub async fn force_shutdown(&self) -> Result<()> { 314 if let Err(e) = self.shutdown().await { 315 log::warn!("language server failed to terminate gracefully - {}", e); 316 } 317 self.exit().await 318 } 319 320 // ------------------------------------------------------------------------------------------- 321 // Text document 322 // ------------------------------------------------------------------------------------------- 323 text_document_did_open( &self, uri: lsp::Url, version: i32, doc: &Rope, language_id: String, ) -> impl Future<Output = Result<()>>324 pub fn text_document_did_open( 325 &self, 326 uri: lsp::Url, 327 version: i32, 328 doc: &Rope, 329 language_id: String, 330 ) -> impl Future<Output = Result<()>> { 331 self.notify::<lsp::notification::DidOpenTextDocument>(lsp::DidOpenTextDocumentParams { 332 text_document: lsp::TextDocumentItem { 333 uri, 334 language_id, 335 version, 336 text: String::from(doc), 337 }, 338 }) 339 } 340 changeset_to_changes( old_text: &Rope, new_text: &Rope, changeset: &ChangeSet, offset_encoding: OffsetEncoding, ) -> Vec<lsp::TextDocumentContentChangeEvent>341 pub fn changeset_to_changes( 342 old_text: &Rope, 343 new_text: &Rope, 344 changeset: &ChangeSet, 345 offset_encoding: OffsetEncoding, 346 ) -> Vec<lsp::TextDocumentContentChangeEvent> { 347 let mut iter = changeset.changes().iter().peekable(); 348 let mut old_pos = 0; 349 let mut new_pos = 0; 350 351 let mut changes = Vec::new(); 352 353 use crate::util::pos_to_lsp_pos; 354 use helix_core::Operation::*; 355 356 // this is dumb. TextEdit describes changes to the initial doc (concurrent), but 357 // TextDocumentContentChangeEvent describes a series of changes (sequential). 358 // So S -> S1 -> S2, meaning positioning depends on the previous edits. 359 // 360 // Calculation is therefore a bunch trickier. 361 362 use helix_core::RopeSlice; 363 fn traverse(pos: lsp::Position, text: RopeSlice) -> lsp::Position { 364 let lsp::Position { 365 mut line, 366 mut character, 367 } = pos; 368 369 let mut chars = text.chars().peekable(); 370 while let Some(ch) = chars.next() { 371 // LSP only considers \n, \r or \r\n as line endings 372 if ch == '\n' || ch == '\r' { 373 // consume a \r\n 374 if ch == '\r' && chars.peek() == Some(&'\n') { 375 chars.next(); 376 } 377 line += 1; 378 character = 0; 379 } else { 380 character += ch.len_utf16() as u32; 381 } 382 } 383 lsp::Position { line, character } 384 } 385 386 let old_text = old_text.slice(..); 387 388 while let Some(change) = iter.next() { 389 let len = match change { 390 Delete(i) | Retain(i) => *i, 391 Insert(_) => 0, 392 }; 393 let mut old_end = old_pos + len; 394 395 match change { 396 Retain(i) => { 397 new_pos += i; 398 } 399 Delete(_) => { 400 let start = pos_to_lsp_pos(new_text, new_pos, offset_encoding); 401 let end = traverse(start, old_text.slice(old_pos..old_end)); 402 403 // deletion 404 changes.push(lsp::TextDocumentContentChangeEvent { 405 range: Some(lsp::Range::new(start, end)), 406 text: "".to_string(), 407 range_length: None, 408 }); 409 } 410 Insert(s) => { 411 let start = pos_to_lsp_pos(new_text, new_pos, offset_encoding); 412 413 new_pos += s.chars().count(); 414 415 // a subsequent delete means a replace, consume it 416 let end = if let Some(Delete(len)) = iter.peek() { 417 old_end = old_pos + len; 418 let end = traverse(start, old_text.slice(old_pos..old_end)); 419 420 iter.next(); 421 422 // replacement 423 end 424 } else { 425 // insert 426 start 427 }; 428 429 changes.push(lsp::TextDocumentContentChangeEvent { 430 range: Some(lsp::Range::new(start, end)), 431 text: s.into(), 432 range_length: None, 433 }); 434 } 435 } 436 old_pos = old_end; 437 } 438 439 changes 440 } 441 text_document_did_change( &self, text_document: lsp::VersionedTextDocumentIdentifier, old_text: &Rope, new_text: &Rope, changes: &ChangeSet, ) -> Option<impl Future<Output = Result<()>>>442 pub fn text_document_did_change( 443 &self, 444 text_document: lsp::VersionedTextDocumentIdentifier, 445 old_text: &Rope, 446 new_text: &Rope, 447 changes: &ChangeSet, 448 ) -> Option<impl Future<Output = Result<()>>> { 449 // figure out what kind of sync the server supports 450 451 let capabilities = self.capabilities.get().unwrap(); 452 453 let sync_capabilities = match capabilities.text_document_sync { 454 Some(lsp::TextDocumentSyncCapability::Kind(kind)) 455 | Some(lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions { 456 change: Some(kind), 457 .. 458 })) => kind, 459 // None | SyncOptions { changes: None } 460 _ => return None, 461 }; 462 463 let changes = match sync_capabilities { 464 lsp::TextDocumentSyncKind::Full => { 465 vec![lsp::TextDocumentContentChangeEvent { 466 // range = None -> whole document 467 range: None, //Some(Range) 468 range_length: None, // u64 apparently deprecated 469 text: new_text.to_string(), 470 }] 471 } 472 lsp::TextDocumentSyncKind::Incremental => { 473 Self::changeset_to_changes(old_text, new_text, changes, self.offset_encoding) 474 } 475 lsp::TextDocumentSyncKind::None => return None, 476 }; 477 478 Some(self.notify::<lsp::notification::DidChangeTextDocument>( 479 lsp::DidChangeTextDocumentParams { 480 text_document, 481 content_changes: changes, 482 }, 483 )) 484 } 485 text_document_did_close( &self, text_document: lsp::TextDocumentIdentifier, ) -> impl Future<Output = Result<()>>486 pub fn text_document_did_close( 487 &self, 488 text_document: lsp::TextDocumentIdentifier, 489 ) -> impl Future<Output = Result<()>> { 490 self.notify::<lsp::notification::DidCloseTextDocument>(lsp::DidCloseTextDocumentParams { 491 text_document, 492 }) 493 } 494 495 // will_save / will_save_wait_until 496 text_document_did_save( &self, text_document: lsp::TextDocumentIdentifier, text: &Rope, ) -> Option<impl Future<Output = Result<()>>>497 pub fn text_document_did_save( 498 &self, 499 text_document: lsp::TextDocumentIdentifier, 500 text: &Rope, 501 ) -> Option<impl Future<Output = Result<()>>> { 502 let capabilities = self.capabilities.get().unwrap(); 503 504 let include_text = match &capabilities.text_document_sync { 505 Some(lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions { 506 save: Some(options), 507 .. 508 })) => match options { 509 lsp::TextDocumentSyncSaveOptions::Supported(true) => false, 510 lsp::TextDocumentSyncSaveOptions::SaveOptions(lsp_types::SaveOptions { 511 include_text, 512 }) => include_text.unwrap_or(false), 513 // Supported(false) 514 _ => return None, 515 }, 516 // unsupported 517 _ => return None, 518 }; 519 520 Some(self.notify::<lsp::notification::DidSaveTextDocument>( 521 lsp::DidSaveTextDocumentParams { 522 text_document, 523 text: include_text.then(|| text.into()), 524 }, 525 )) 526 } 527 completion( &self, text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, ) -> impl Future<Output = Result<Value>>528 pub fn completion( 529 &self, 530 text_document: lsp::TextDocumentIdentifier, 531 position: lsp::Position, 532 work_done_token: Option<lsp::ProgressToken>, 533 ) -> impl Future<Output = Result<Value>> { 534 // ) -> Result<Vec<lsp::CompletionItem>> { 535 let params = lsp::CompletionParams { 536 text_document_position: lsp::TextDocumentPositionParams { 537 text_document, 538 position, 539 }, 540 // TODO: support these tokens by async receiving and updating the choice list 541 work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token }, 542 partial_result_params: lsp::PartialResultParams { 543 partial_result_token: None, 544 }, 545 context: None, 546 // lsp::CompletionContext { trigger_kind: , trigger_character: Some(), } 547 }; 548 549 self.call::<lsp::request::Completion>(params) 550 } 551 text_document_signature_help( &self, text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, ) -> impl Future<Output = Result<Value>>552 pub fn text_document_signature_help( 553 &self, 554 text_document: lsp::TextDocumentIdentifier, 555 position: lsp::Position, 556 work_done_token: Option<lsp::ProgressToken>, 557 ) -> impl Future<Output = Result<Value>> { 558 let params = lsp::SignatureHelpParams { 559 text_document_position_params: lsp::TextDocumentPositionParams { 560 text_document, 561 position, 562 }, 563 work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token }, 564 context: None, 565 // lsp::SignatureHelpContext 566 }; 567 568 self.call::<lsp::request::SignatureHelpRequest>(params) 569 } 570 text_document_hover( &self, text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, ) -> impl Future<Output = Result<Value>>571 pub fn text_document_hover( 572 &self, 573 text_document: lsp::TextDocumentIdentifier, 574 position: lsp::Position, 575 work_done_token: Option<lsp::ProgressToken>, 576 ) -> impl Future<Output = Result<Value>> { 577 let params = lsp::HoverParams { 578 text_document_position_params: lsp::TextDocumentPositionParams { 579 text_document, 580 position, 581 }, 582 work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token }, 583 // lsp::SignatureHelpContext 584 }; 585 586 self.call::<lsp::request::HoverRequest>(params) 587 } 588 589 // formatting 590 text_document_formatting( &self, text_document: lsp::TextDocumentIdentifier, options: lsp::FormattingOptions, work_done_token: Option<lsp::ProgressToken>, ) -> Option<impl Future<Output = Result<Vec<lsp::TextEdit>>>>591 pub fn text_document_formatting( 592 &self, 593 text_document: lsp::TextDocumentIdentifier, 594 options: lsp::FormattingOptions, 595 work_done_token: Option<lsp::ProgressToken>, 596 ) -> Option<impl Future<Output = Result<Vec<lsp::TextEdit>>>> { 597 let capabilities = self.capabilities.get().unwrap(); 598 599 // check if we're able to format 600 match capabilities.document_formatting_provider { 601 Some(lsp::OneOf::Left(true)) | Some(lsp::OneOf::Right(_)) => (), 602 // None | Some(false) 603 _ => return None, 604 }; 605 // TODO: return err::unavailable so we can fall back to tree sitter formatting 606 607 let params = lsp::DocumentFormattingParams { 608 text_document, 609 options, 610 work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token }, 611 }; 612 613 let request = self.call::<lsp::request::Formatting>(params); 614 615 Some(async move { 616 let json = request.await?; 617 let response: Option<Vec<lsp::TextEdit>> = serde_json::from_value(json)?; 618 Ok(response.unwrap_or_default()) 619 }) 620 } 621 text_document_range_formatting( &self, text_document: lsp::TextDocumentIdentifier, range: lsp::Range, options: lsp::FormattingOptions, work_done_token: Option<lsp::ProgressToken>, ) -> anyhow::Result<Vec<lsp::TextEdit>>622 pub async fn text_document_range_formatting( 623 &self, 624 text_document: lsp::TextDocumentIdentifier, 625 range: lsp::Range, 626 options: lsp::FormattingOptions, 627 work_done_token: Option<lsp::ProgressToken>, 628 ) -> anyhow::Result<Vec<lsp::TextEdit>> { 629 let capabilities = self.capabilities.get().unwrap(); 630 631 // check if we're able to format 632 match capabilities.document_range_formatting_provider { 633 Some(lsp::OneOf::Left(true)) | Some(lsp::OneOf::Right(_)) => (), 634 // None | Some(false) 635 _ => return Ok(Vec::new()), 636 }; 637 // TODO: return err::unavailable so we can fall back to tree sitter formatting 638 639 let params = lsp::DocumentRangeFormattingParams { 640 text_document, 641 range, 642 options, 643 work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token }, 644 }; 645 646 let response = self 647 .request::<lsp::request::RangeFormatting>(params) 648 .await?; 649 650 Ok(response.unwrap_or_default()) 651 } 652 goto_request< T: lsp::request::Request< Params = lsp::GotoDefinitionParams, Result = Option<lsp::GotoDefinitionResponse>, >, >( &self, text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, ) -> impl Future<Output = Result<Value>>653 fn goto_request< 654 T: lsp::request::Request< 655 Params = lsp::GotoDefinitionParams, 656 Result = Option<lsp::GotoDefinitionResponse>, 657 >, 658 >( 659 &self, 660 text_document: lsp::TextDocumentIdentifier, 661 position: lsp::Position, 662 work_done_token: Option<lsp::ProgressToken>, 663 ) -> impl Future<Output = Result<Value>> { 664 let params = lsp::GotoDefinitionParams { 665 text_document_position_params: lsp::TextDocumentPositionParams { 666 text_document, 667 position, 668 }, 669 work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token }, 670 partial_result_params: lsp::PartialResultParams { 671 partial_result_token: None, 672 }, 673 }; 674 675 self.call::<T>(params) 676 } 677 goto_definition( &self, text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, ) -> impl Future<Output = Result<Value>>678 pub fn goto_definition( 679 &self, 680 text_document: lsp::TextDocumentIdentifier, 681 position: lsp::Position, 682 work_done_token: Option<lsp::ProgressToken>, 683 ) -> impl Future<Output = Result<Value>> { 684 self.goto_request::<lsp::request::GotoDefinition>(text_document, position, work_done_token) 685 } 686 goto_type_definition( &self, text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, ) -> impl Future<Output = Result<Value>>687 pub fn goto_type_definition( 688 &self, 689 text_document: lsp::TextDocumentIdentifier, 690 position: lsp::Position, 691 work_done_token: Option<lsp::ProgressToken>, 692 ) -> impl Future<Output = Result<Value>> { 693 self.goto_request::<lsp::request::GotoTypeDefinition>( 694 text_document, 695 position, 696 work_done_token, 697 ) 698 } 699 goto_implementation( &self, text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, ) -> impl Future<Output = Result<Value>>700 pub fn goto_implementation( 701 &self, 702 text_document: lsp::TextDocumentIdentifier, 703 position: lsp::Position, 704 work_done_token: Option<lsp::ProgressToken>, 705 ) -> impl Future<Output = Result<Value>> { 706 self.goto_request::<lsp::request::GotoImplementation>( 707 text_document, 708 position, 709 work_done_token, 710 ) 711 } 712 goto_reference( &self, text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option<lsp::ProgressToken>, ) -> impl Future<Output = Result<Value>>713 pub fn goto_reference( 714 &self, 715 text_document: lsp::TextDocumentIdentifier, 716 position: lsp::Position, 717 work_done_token: Option<lsp::ProgressToken>, 718 ) -> impl Future<Output = Result<Value>> { 719 let params = lsp::ReferenceParams { 720 text_document_position: lsp::TextDocumentPositionParams { 721 text_document, 722 position, 723 }, 724 context: lsp::ReferenceContext { 725 include_declaration: true, 726 }, 727 work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token }, 728 partial_result_params: lsp::PartialResultParams { 729 partial_result_token: None, 730 }, 731 }; 732 733 self.call::<lsp::request::References>(params) 734 } 735 document_symbols( &self, text_document: lsp::TextDocumentIdentifier, ) -> impl Future<Output = Result<Value>>736 pub fn document_symbols( 737 &self, 738 text_document: lsp::TextDocumentIdentifier, 739 ) -> impl Future<Output = Result<Value>> { 740 let params = lsp::DocumentSymbolParams { 741 text_document, 742 work_done_progress_params: lsp::WorkDoneProgressParams::default(), 743 partial_result_params: lsp::PartialResultParams::default(), 744 }; 745 746 self.call::<lsp::request::DocumentSymbolRequest>(params) 747 } 748 749 // empty string to get all symbols workspace_symbols(&self, query: String) -> impl Future<Output = Result<Value>>750 pub fn workspace_symbols(&self, query: String) -> impl Future<Output = Result<Value>> { 751 let params = lsp::WorkspaceSymbolParams { 752 query, 753 work_done_progress_params: lsp::WorkDoneProgressParams::default(), 754 partial_result_params: lsp::PartialResultParams::default(), 755 }; 756 757 self.call::<lsp::request::WorkspaceSymbol>(params) 758 } 759 code_actions( &self, text_document: lsp::TextDocumentIdentifier, range: lsp::Range, ) -> impl Future<Output = Result<Value>>760 pub fn code_actions( 761 &self, 762 text_document: lsp::TextDocumentIdentifier, 763 range: lsp::Range, 764 ) -> impl Future<Output = Result<Value>> { 765 let params = lsp::CodeActionParams { 766 text_document, 767 range, 768 context: lsp::CodeActionContext::default(), 769 work_done_progress_params: lsp::WorkDoneProgressParams::default(), 770 partial_result_params: lsp::PartialResultParams::default(), 771 }; 772 773 self.call::<lsp::request::CodeActionRequest>(params) 774 } 775 } 776