Skip to content

Commit b4ac14b

Browse files
committed
feat: lsp range fmt. Support range format in lsp
1 parent 59539d8 commit b4ac14b

File tree

5 files changed

+80
-17
lines changed

5 files changed

+80
-17
lines changed

kclvm/tools/src/LSP/src/capabilities.rs

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
3737
}),
3838
),
3939
document_formatting_provider: Some(OneOf::Left(true)),
40+
document_range_formatting_provider: Some(OneOf::Left(true)),
4041
..Default::default()
4142
}
4243
}

kclvm/tools/src/LSP/src/formatting.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use kclvm_tools::format::{format_source, FormatOptions};
22
use lsp_types::{Position, Range, TextEdit};
33

4-
pub(crate) fn format_single_file(
4+
pub(crate) fn format(
55
file: String,
66
src: String,
7+
range: Option<Range>,
78
) -> anyhow::Result<Option<Vec<TextEdit>>> {
89
let (source, is_formatted) = format_source(
910
&file,
@@ -13,10 +14,13 @@ pub(crate) fn format_single_file(
1314
..Default::default()
1415
},
1516
)
16-
.map_err(|err| anyhow::anyhow!("Formmatting failed: {}", err))?;
17+
.map_err(|err| anyhow::anyhow!("Formatting failed: {}", err))?;
1718
if is_formatted {
1819
Ok(Some(vec![TextEdit {
19-
range: Range::new(Position::new(0, 0), Position::new(u32::MAX, u32::MAX)),
20+
range: range.unwrap_or(Range::new(
21+
Position::new(0, 0),
22+
Position::new(u32::MAX, u32::MAX),
23+
)),
2024
new_text: source,
2125
}]))
2226
} else {

kclvm/tools/src/LSP/src/request.rs

+29-6
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ use crossbeam_channel::Sender;
22

33
use lsp_types::{CodeAction, CodeActionKind, CodeActionOrCommand, Position, Range, TextEdit};
44
use ra_ap_vfs::VfsPath;
5-
use std::{collections::HashMap, time::Instant};
5+
use std::{collections::HashMap, ops::Index, time::Instant};
66

77
use crate::{
88
completion::completion,
99
db::AnalysisDatabase,
1010
dispatcher::RequestDispatcher,
1111
document_symbol::document_symbol,
12-
formatting::format_single_file,
12+
formatting::format,
1313
from_lsp::{self, file_path_from_url, kcl_pos},
1414
goto_def::goto_definition,
1515
hover, quick_fix,
@@ -48,6 +48,7 @@ impl LanguageServerState {
4848
.on::<lsp_types::request::DocumentSymbolRequest>(handle_document_symbol)?
4949
.on::<lsp_types::request::CodeActionRequest>(handle_code_action)?
5050
.on::<lsp_types::request::Formatting>(handle_formatting)?
51+
.on::<lsp_types::request::RangeFormatting>(handle_range_formatting)?
5152
.finish();
5253

5354
Ok(())
@@ -69,18 +70,40 @@ impl LanguageServerSnapshot {
6970
}
7071

7172
pub(crate) fn handle_formatting(
72-
_snap: LanguageServerSnapshot,
73+
_snapshot: LanguageServerSnapshot,
7374
params: lsp_types::DocumentFormattingParams,
74-
sender: Sender<Task>,
75+
_sender: Sender<Task>,
7576
) -> anyhow::Result<Option<Vec<TextEdit>>> {
7677
let file = file_path_from_url(&params.text_document.uri)?;
7778
let src = std::fs::read_to_string(file.clone())?;
78-
format_single_file(file, src)
79+
format(file, src, None)
80+
}
81+
82+
pub(crate) fn handle_range_formatting(
83+
snapshot: LanguageServerSnapshot,
84+
params: lsp_types::DocumentRangeFormattingParams,
85+
_sender: Sender<Task>,
86+
) -> anyhow::Result<Option<Vec<TextEdit>>> {
87+
let file = file_path_from_url(&params.text_document.uri)?;
88+
let path = from_lsp::abs_path(&params.text_document.uri)?;
89+
let vfs = &*snapshot.vfs.read();
90+
91+
let file_id = vfs
92+
.file_id(&path.clone().into())
93+
.ok_or(anyhow::anyhow!("Already checked that the file_id exists!"))?;
94+
95+
let text = String::from_utf8(vfs.file_contents(file_id).to_vec())?;
96+
let range = from_lsp::text_range(&text, params.range);
97+
if let Some(src) = text.get(range) {
98+
format(file, src.to_owned(), Some(params.range))
99+
} else {
100+
Ok(None)
101+
}
79102
}
80103

81104
/// Called when a `GotoDefinition` request was received.
82105
pub(crate) fn handle_code_action(
83-
_snap: LanguageServerSnapshot,
106+
_snapshot: LanguageServerSnapshot,
84107
params: lsp_types::CodeActionParams,
85108
_sender: Sender<Task>,
86109
) -> anyhow::Result<Option<lsp_types::CodeActionResponse>> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
a=1
3+
4+
5+
b= 2
6+
7+
8+
c =3
9+
10+
11+
12+
d = 4

kclvm/tools/src/LSP/src/tests.rs

+31-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::env;
2+
use std::ops::Index;
23
use std::path::PathBuf;
34
use std::process::Command;
45
use std::sync::Arc;
@@ -29,11 +30,13 @@ use lsp_types::Url;
2930
use lsp_types::WorkspaceEdit;
3031
use lsp_types::{Position, Range, TextDocumentContentChangeEvent};
3132
use parking_lot::RwLock;
33+
use ra_ap_vfs::AbsPathBuf;
34+
use ra_ap_vfs::Vfs;
3235

3336
use crate::completion::KCLCompletionItem;
3437
use crate::document_symbol::document_symbol;
35-
use crate::formatting::format_single_file;
36-
use crate::from_lsp::file_path_from_url;
38+
use crate::formatting::format;
39+
use crate::from_lsp::{abs_path, file_path_from_url, text_range};
3740
use crate::hover::hover;
3841
use crate::quick_fix::quick_fix;
3942
use crate::to_lsp::kcl_diag_to_lsp_diags;
@@ -149,7 +152,7 @@ fn diagnostics_test() {
149152
Param {
150153
file: file.to_string(),
151154
},
152-
None,
155+
Some(Arc::new(RwLock::new(Default::default()))),
153156
)
154157
.unwrap();
155158

@@ -237,7 +240,7 @@ fn quick_fix_test() {
237240
Param {
238241
file: file.to_string(),
239242
},
240-
None,
243+
Some(Arc::new(RwLock::new(Default::default()))),
241244
)
242245
.unwrap();
243246

@@ -1226,7 +1229,7 @@ fn goto_import_external_file_test() {
12261229
Param {
12271230
file: path.to_string(),
12281231
},
1229-
None,
1232+
Some(Arc::new(RwLock::new(Default::default()))),
12301233
)
12311234
.unwrap();
12321235

@@ -1243,7 +1246,7 @@ fn goto_import_external_file_test() {
12431246
}
12441247

12451248
#[test]
1246-
fn formmat_signle_file_test() {
1249+
fn format_signle_file_test() {
12471250
const FILE_INPUT_SUFFIX: &str = ".input";
12481251
const FILE_OUTPUT_SUFFIX: &str = ".golden";
12491252
const TEST_CASES: &[&str; 17] = &[
@@ -1282,7 +1285,7 @@ fn formmat_signle_file_test() {
12821285
.unwrap()
12831286
.to_string();
12841287
let test_src = std::fs::read_to_string(&test_file).unwrap();
1285-
let got = format_single_file(test_file, test_src).unwrap().unwrap();
1288+
let got = format(test_file, test_src, None).unwrap().unwrap();
12861289
let data_output = std::fs::read_to_string(
12871290
&test_dir
12881291
.join(format!("{}{}", case, FILE_OUTPUT_SUFFIX))
@@ -1310,6 +1313,26 @@ fn formmat_signle_file_test() {
13101313
.unwrap()
13111314
.to_string();
13121315
let test_src = std::fs::read_to_string(&test_file).unwrap();
1313-
let got = format_single_file(test_file, test_src).unwrap();
1316+
let got = format(test_file, test_src, None).unwrap();
13141317
assert_eq!(got, None)
13151318
}
1319+
1320+
#[test]
1321+
fn format_range_test() {
1322+
let (file, program, prog_scope, _) = compile_test_file("src/test_data/format/format_range.k");
1323+
let lsp_range = Range::new(Position::new(0, 0), Position::new(11, 0));
1324+
let text = std::fs::read_to_string(file.clone()).unwrap();
1325+
1326+
let range = text_range(&text, lsp_range);
1327+
let src = text.index(range);
1328+
1329+
let got = format(file, src.to_owned(), Some(lsp_range))
1330+
.unwrap()
1331+
.unwrap();
1332+
1333+
let expected = vec![TextEdit {
1334+
range: lsp_range,
1335+
new_text: "a = 1\nb = 2\nc = 3\n".to_string(),
1336+
}];
1337+
assert_eq!(got, expected)
1338+
}

0 commit comments

Comments
 (0)