Skip to content

Commit 57e132a

Browse files
committed
Compute more granular textedits for formatting requests
1 parent 89467a7 commit 57e132a

File tree

1 file changed

+87
-12
lines changed

1 file changed

+87
-12
lines changed

src/cli/lsp.rs

Lines changed: 87 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,87 @@ use lsp_types::{
88
Range, ServerCapabilities, ServerInfo, TextDocumentSyncCapability, TextDocumentSyncKind,
99
TextEdit, Uri,
1010
};
11+
use similar::{DiffOp, TextDiff};
1112
use stylua_lib::{format_code, OutputVerification};
1213

1314
use crate::{config::ConfigResolver, opt};
1415

16+
fn diffop_to_textedit(op: DiffOp, formatted_contents: &str) -> Option<TextEdit> {
17+
match op {
18+
DiffOp::Equal {
19+
old_index: _,
20+
new_index: _,
21+
len: _,
22+
} => None,
23+
DiffOp::Delete {
24+
old_index,
25+
old_len,
26+
new_index: _,
27+
} => Some(TextEdit {
28+
range: Range {
29+
start: Position {
30+
line: old_index.try_into().expect("usize fits into u32"),
31+
character: 0,
32+
},
33+
end: Position {
34+
line: (old_index + old_len)
35+
.try_into()
36+
.expect("usize fits into u32"),
37+
character: 0,
38+
},
39+
},
40+
new_text: String::new(),
41+
}),
42+
DiffOp::Insert {
43+
old_index,
44+
new_index,
45+
new_len,
46+
} => {
47+
let insert_position = Position {
48+
line: old_index.try_into().expect("usize fits into u32"),
49+
character: 0,
50+
};
51+
Some(TextEdit {
52+
range: Range {
53+
start: insert_position,
54+
end: insert_position,
55+
},
56+
new_text: formatted_contents
57+
.lines()
58+
.skip(new_index)
59+
.take(new_len)
60+
.collect::<Vec<_>>()
61+
.join("\n"),
62+
})
63+
}
64+
DiffOp::Replace {
65+
old_index,
66+
old_len,
67+
new_index,
68+
new_len,
69+
} => Some(TextEdit {
70+
range: Range {
71+
start: Position {
72+
line: old_index.try_into().expect("usize fits into u32"),
73+
character: 0,
74+
},
75+
end: Position {
76+
line: (old_index + old_len)
77+
.try_into()
78+
.expect("usize fits into u32"),
79+
character: 0,
80+
},
81+
},
82+
new_text: formatted_contents
83+
.lines()
84+
.skip(new_index)
85+
.take(new_len)
86+
.collect::<Vec<_>>()
87+
.join("\n"),
88+
}),
89+
}
90+
}
91+
1592
fn handle_formatting(
1693
uri: &Uri,
1794
document: &FullTextDocument,
@@ -30,18 +107,16 @@ fn handle_formatting(
30107

31108
let formatted_contents = format_code(contents, config, range, OutputVerification::None).ok()?;
32109

33-
let last_line_idx = document.line_count().saturating_sub(1);
34-
let last_line_offset = document.offset_at(Position::new(last_line_idx, 0));
35-
let last_col = document.content_len() - last_line_offset;
36-
37-
// TODO: We can be smarter about this in the future, and update only the parts that changed (using output_diff)
38-
Some(vec![TextEdit {
39-
range: Range {
40-
start: Position::new(0, 0),
41-
end: Position::new(last_line_idx, last_col),
42-
},
43-
new_text: formatted_contents,
44-
}])
110+
let operations = TextDiff::from_lines(contents, &formatted_contents).grouped_ops(0);
111+
let edits = operations
112+
.into_iter()
113+
.flat_map(|operations| {
114+
operations
115+
.into_iter()
116+
.filter_map(|op| diffop_to_textedit(op, &formatted_contents))
117+
})
118+
.collect();
119+
Some(edits)
45120
}
46121

47122
fn handle_request(

0 commit comments

Comments
 (0)