@@ -4,16 +4,27 @@ use lsp_server::{Connection, ErrorCode, Message, Response};
44use lsp_textdocument:: { FullTextDocument , TextDocuments } ;
55use lsp_types:: {
66 request:: { Formatting , RangeFormatting , Request } ,
7- DocumentFormattingParams , DocumentRangeFormattingParams , InitializeResult , OneOf , Position ,
8- Range , ServerCapabilities , ServerInfo , TextDocumentSyncCapability , TextDocumentSyncKind ,
9- TextEdit , Uri ,
7+ DocumentFormattingParams , DocumentRangeFormattingParams , InitializeResult , OneOf , Range ,
8+ ServerCapabilities , ServerInfo , TextDocumentSyncCapability , TextDocumentSyncKind , TextEdit ,
9+ Uri ,
1010} ;
1111use similar:: { DiffOp , TextDiff } ;
1212use stylua_lib:: { format_code, OutputVerification } ;
1313
1414use crate :: { config:: ConfigResolver , opt} ;
1515
16- fn diffop_to_textedit ( op : DiffOp , formatted_contents : & str ) -> Option < TextEdit > {
16+ fn diffop_to_textedit (
17+ op : DiffOp ,
18+ document : & FullTextDocument ,
19+ formatted_contents : & str ,
20+ ) -> Option < TextEdit > {
21+ let range = |start : usize , len : usize | Range {
22+ start : document. position_at ( start. try_into ( ) . expect ( "usize fits into u32" ) ) ,
23+ end : document. position_at ( ( start + len) . try_into ( ) . expect ( "usize fits into u32" ) ) ,
24+ } ;
25+
26+ let lookup = |start : usize , len : usize | formatted_contents[ start..start + len] . to_string ( ) ;
27+
1728 match op {
1829 DiffOp :: Equal {
1930 old_index : _,
@@ -25,66 +36,25 @@ fn diffop_to_textedit(op: DiffOp, formatted_contents: &str) -> Option<TextEdit>
2536 old_len,
2637 new_index : _,
2738 } => 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- } ,
39+ range : range ( old_index, old_len) ,
4040 new_text : String :: new ( ) ,
4141 } ) ,
4242 DiffOp :: Insert {
4343 old_index,
4444 new_index,
4545 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- }
46+ } => Some ( TextEdit {
47+ range : range ( old_index, 0 ) ,
48+ new_text : lookup ( new_index, new_len) ,
49+ } ) ,
6450 DiffOp :: Replace {
6551 old_index,
6652 old_len,
6753 new_index,
6854 new_len,
6955 } => 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 " ) ,
56+ range : range ( old_index, old_len) ,
57+ new_text : lookup ( new_index, new_len) ,
8858 } ) ,
8959 }
9060}
@@ -107,13 +77,14 @@ fn handle_formatting(
10777
10878 let formatted_contents = format_code ( contents, config, range, OutputVerification :: None ) . ok ( ) ?;
10979
110- let operations = TextDiff :: from_lines ( contents, & formatted_contents) . grouped_ops ( 0 ) ;
80+ let operations =
81+ TextDiff :: from_chars ( contents. as_bytes ( ) , formatted_contents. as_bytes ( ) ) . grouped_ops ( 0 ) ;
11182 let edits = operations
11283 . into_iter ( )
11384 . flat_map ( |operations| {
11485 operations
11586 . into_iter ( )
116- . filter_map ( |op| diffop_to_textedit ( op, & formatted_contents) )
87+ . filter_map ( |op| diffop_to_textedit ( op, document , & formatted_contents) )
11788 } )
11889 . collect ( ) ;
11990 Some ( edits)
@@ -254,6 +225,8 @@ pub fn run(opt: opt::Opt) -> anyhow::Result<()> {
254225
255226#[ cfg( test) ]
256227mod tests {
228+ use std:: cmp:: Ordering ;
229+ use std:: convert:: TryInto ;
257230 use std:: str:: FromStr ;
258231
259232 use clap:: Parser ;
@@ -373,6 +346,35 @@ mod tests {
373346 assert ! ( client. receiver. is_empty( ) ) ;
374347 }
375348
349+ fn with_edits ( text : & str , mut edits : Vec < TextEdit > ) -> String {
350+ edits. sort_by ( |a, b| match a. range . start . line . cmp ( & b. range . start . line ) {
351+ Ordering :: Equal => a
352+ . range
353+ . start
354+ . character
355+ . cmp ( & b. range . start . character )
356+ . reverse ( ) ,
357+ order => order. reverse ( ) ,
358+ } ) ;
359+ let mut text = text. to_string ( ) ;
360+ for edit in edits {
361+ let start = text
362+ . lines ( )
363+ . take ( edit. range . start . line . try_into ( ) . unwrap ( ) )
364+ . map ( |line| line. len ( ) + '\n' . len_utf8 ( ) )
365+ . sum :: < usize > ( )
366+ + <u32 as TryInto < usize > >:: try_into ( edit. range . start . character ) . unwrap ( ) ;
367+ let end = text
368+ . lines ( )
369+ . take ( edit. range . end . line . try_into ( ) . unwrap ( ) )
370+ . map ( |line| line. len ( ) + '\n' . len_utf8 ( ) )
371+ . sum :: < usize > ( )
372+ + <u32 as TryInto < usize > >:: try_into ( edit. range . end . character ) . unwrap ( ) ;
373+ text. replace_range ( start..end, & edit. new_text ) ;
374+ }
375+ text
376+ }
377+
376378 #[ test]
377379 fn test_lsp_document_formatting ( ) {
378380 let uri = Uri :: from_str ( "file:///home/documents/file.luau" ) . unwrap ( ) ;
@@ -420,17 +422,8 @@ mod tests {
420422 expect_server_initialized ( & client. receiver , 1 ) ;
421423
422424 let edits: Vec < TextEdit > = expect_response ( & client. receiver , 2 ) ;
423- assert_eq ! ( edits. len( ) , 1 ) ;
424- assert_eq ! (
425- edits[ 0 ] ,
426- TextEdit {
427- range: Range {
428- start: Position :: new( 0 , 0 ) ,
429- end: Position :: new( 0 , 14 ) ,
430- } ,
431- new_text: "local x = 1\n " . to_string( ) ,
432- }
433- ) ;
425+ let formatted = with_edits ( contents, edits) ;
426+ assert_eq ! ( formatted, "local x = 1\n " ) ;
434427
435428 expect_server_shutdown ( & client. receiver , 3 ) ;
436429 assert ! ( client. receiver. is_empty( ) ) ;
@@ -484,17 +477,8 @@ mod tests {
484477 expect_server_initialized ( & client. receiver , 1 ) ;
485478
486479 let edits: Vec < TextEdit > = expect_response ( & client. receiver , 2 ) ;
487- assert_eq ! ( edits. len( ) , 1 ) ;
488- assert_eq ! (
489- edits[ 0 ] ,
490- TextEdit {
491- range: Range {
492- start: Position :: new( 0 , 0 ) ,
493- end: Position :: new( 1 , 18 ) ,
494- } ,
495- new_text: "local x = 1\n local y = 2\n " . to_string( ) ,
496- }
497- ) ;
480+ let formatted = with_edits ( contents, edits) ;
481+ assert_eq ! ( formatted, "local x = 1\n local y = 2\n " ) ;
498482
499483 expect_server_shutdown ( & client. receiver , 3 ) ;
500484 assert ! ( client. receiver. is_empty( ) ) ;
0 commit comments