|
8 | 8 | lsp_types::{ |
9 | 9 | CompletionOptions, CompletionParams, CompletionResponse, DidChangeTextDocumentParams, |
10 | 10 | DidCloseTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, |
11 | | - DocumentFormattingParams, DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, |
12 | | - InitializeParams, InitializeResult, OneOf, Position, PublishDiagnosticsParams, Range, |
13 | | - SaveOptions, ServerCapabilities, ServerInfo, TextDocumentSyncCapability, |
| 11 | + DocumentFormattingParams, DocumentRangeFormattingParams, DocumentSymbol, |
| 12 | + DocumentSymbolParams, DocumentSymbolResponse, InitializeParams, InitializeResult, |
| 13 | + MessageType, OneOf, Position, PublishDiagnosticsParams, Range, SaveOptions, |
| 14 | + ServerCapabilities, ServerInfo, ShowMessageParams, TextDocumentSyncCapability, |
14 | 15 | TextDocumentSyncKind, TextDocumentSyncOptions, TextDocumentSyncSaveOptions, TextEdit, |
15 | 16 | WorkspaceSymbolParams, WorkspaceSymbolResponse, |
16 | 17 | }, |
17 | 18 | tree, |
18 | 19 | }, |
19 | 20 | async_lsp::{ |
20 | 21 | ClientSocket, ErrorCode, LanguageClient, LanguageServer, ResponseError, |
21 | | - client_monitor::ClientProcessMonitorLayer, |
22 | | - concurrency::ConcurrencyLayer, |
23 | | - lsp_types::{MessageType, ShowMessageParams}, |
24 | | - panic::CatchUnwindLayer, |
25 | | - router::Router, |
26 | | - server::LifecycleLayer, |
27 | | - tracing::TracingLayer, |
| 22 | + client_monitor::ClientProcessMonitorLayer, concurrency::ConcurrencyLayer, |
| 23 | + panic::CatchUnwindLayer, router::Router, server::LifecycleLayer, tracing::TracingLayer, |
28 | 24 | }, |
29 | 25 | futures::future::BoxFuture, |
30 | 26 | ropey::Rope, |
@@ -134,6 +130,8 @@ impl LanguageServer for ServerState { |
134 | 130 | trigger_characters: Some(vec!["$".into(), "@".into()]), |
135 | 131 | ..Default::default() |
136 | 132 | }), |
| 133 | + |
| 134 | + document_range_formatting_provider: Some(OneOf::Left(true)), |
137 | 135 | document_formatting_provider: Some(OneOf::Left(true)), |
138 | 136 | document_symbol_provider: Some(OneOf::Left(true)), |
139 | 137 | text_document_sync: Some(TextDocumentSyncCapability::Options( |
@@ -387,27 +385,75 @@ impl LanguageServer for ServerState { |
387 | 385 | }; |
388 | 386 |
|
389 | 387 | let (rope, tree) = (&document.rope, &document.tree); |
390 | | - let new = match format::format(tree.root_node(), rope, format::Config { |
| 388 | + let new_text = match format::format(tree.root_node(), rope, format::Config { |
391 | 389 | indent: &" ".repeat(self.config.spaces), |
392 | 390 | line_ending: LineEnding::Auto, |
393 | 391 | }) { |
394 | | - Ok(new) => new, |
| 392 | + Ok(text) => text, |
395 | 393 | Err(error) => { |
396 | 394 | tracing::error!(?error, "failed to format"); |
397 | 395 | return Box::pin(async { Ok(None) }); |
398 | 396 | } |
399 | 397 | }; |
400 | 398 |
|
401 | | - // TODO: only format if necessary and send text edits... |
402 | 399 | let edits = vec![TextEdit { |
| 400 | + new_text, |
403 | 401 | range: Range::new( |
404 | 402 | Position::new(0, 0), |
405 | 403 | Position::new( |
406 | 404 | (rope.len_lines() - 1) as u32, |
407 | 405 | (rope.len_chars() - rope.line_to_char(rope.len_lines() - 1)) as u32, |
408 | 406 | ), |
409 | 407 | ), |
410 | | - new_text: new, |
| 408 | + }]; |
| 409 | + |
| 410 | + Box::pin(async move { Ok(Some(edits)) }) |
| 411 | + } |
| 412 | + |
| 413 | + fn range_formatting( |
| 414 | + &mut self, |
| 415 | + params: DocumentRangeFormattingParams, |
| 416 | + ) -> BoxFuture<'static, Result<Option<Vec<TextEdit>>, ResponseError>> { |
| 417 | + let uri = params.text_document.uri; |
| 418 | + let range = params.range; |
| 419 | + let path = uri.to_file_path().unwrap(); |
| 420 | + |
| 421 | + tracing::debug!(?uri, "format"); |
| 422 | + |
| 423 | + let Some(document) = self.document_map.get(&path) else { |
| 424 | + tracing::info!(?uri, "document not found"); |
| 425 | + return Box::pin(async move { Err(ResponseError::new(ErrorCode::INTERNAL_ERROR, "")) }); |
| 426 | + }; |
| 427 | + |
| 428 | + let (rope, tree) = (&document.rope, &document.tree); |
| 429 | + let Some(node) = tree.root_node().descendant_for_point_range( |
| 430 | + Point::new(range.start.line as usize, range.start.character as usize), |
| 431 | + Point::new(range.end.line as usize, range.end.character as usize), |
| 432 | + ) else { |
| 433 | + tracing::info!(?range, "no node for range"); |
| 434 | + return Box::pin(async { Ok(None) }); |
| 435 | + }; |
| 436 | + |
| 437 | + let new_text = match format::format(node, rope, format::Config { |
| 438 | + indent: &" ".repeat(self.config.spaces), |
| 439 | + line_ending: LineEnding::Auto, |
| 440 | + }) { |
| 441 | + Ok(text) => text, |
| 442 | + Err(error) => { |
| 443 | + tracing::error!(?error, "failed to format"); |
| 444 | + return Box::pin(async { Ok(None) }); |
| 445 | + } |
| 446 | + }; |
| 447 | + |
| 448 | + let start = node.start_position(); |
| 449 | + let end = node.end_position(); |
| 450 | + |
| 451 | + let edits = vec![TextEdit { |
| 452 | + new_text, |
| 453 | + range: Range::new( |
| 454 | + Position::new(start.row as u32, start.column as u32), |
| 455 | + Position::new(end.row as u32, end.column as u32), |
| 456 | + ), |
411 | 457 | }]; |
412 | 458 |
|
413 | 459 | Box::pin(async move { Ok(Some(edits)) }) |
|
0 commit comments