Skip to content

Commit 8829867

Browse files
committed
feat: Implement range formatting
1 parent 45af8c3 commit 8829867

File tree

1 file changed

+60
-14
lines changed

1 file changed

+60
-14
lines changed

src/lsp_async_lsp.rs

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,19 @@ use {
88
lsp_types::{
99
CompletionOptions, CompletionParams, CompletionResponse, DidChangeTextDocumentParams,
1010
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,
1415
TextDocumentSyncKind, TextDocumentSyncOptions, TextDocumentSyncSaveOptions, TextEdit,
1516
WorkspaceSymbolParams, WorkspaceSymbolResponse,
1617
},
1718
tree,
1819
},
1920
async_lsp::{
2021
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,
2824
},
2925
futures::future::BoxFuture,
3026
ropey::Rope,
@@ -134,6 +130,8 @@ impl LanguageServer for ServerState {
134130
trigger_characters: Some(vec!["$".into(), "@".into()]),
135131
..Default::default()
136132
}),
133+
134+
document_range_formatting_provider: Some(OneOf::Left(true)),
137135
document_formatting_provider: Some(OneOf::Left(true)),
138136
document_symbol_provider: Some(OneOf::Left(true)),
139137
text_document_sync: Some(TextDocumentSyncCapability::Options(
@@ -387,27 +385,75 @@ impl LanguageServer for ServerState {
387385
};
388386

389387
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 {
391389
indent: &" ".repeat(self.config.spaces),
392390
line_ending: LineEnding::Auto,
393391
}) {
394-
Ok(new) => new,
392+
Ok(text) => text,
395393
Err(error) => {
396394
tracing::error!(?error, "failed to format");
397395
return Box::pin(async { Ok(None) });
398396
}
399397
};
400398

401-
// TODO: only format if necessary and send text edits...
402399
let edits = vec![TextEdit {
400+
new_text,
403401
range: Range::new(
404402
Position::new(0, 0),
405403
Position::new(
406404
(rope.len_lines() - 1) as u32,
407405
(rope.len_chars() - rope.line_to_char(rope.len_lines() - 1)) as u32,
408406
),
409407
),
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+
),
411457
}];
412458

413459
Box::pin(async move { Ok(Some(edits)) })

0 commit comments

Comments
 (0)