@@ -5,15 +5,26 @@ use lsp_textdocument::{FullTextDocument, TextDocuments};
55use lsp_types:: {
66 request:: { Formatting , RangeFormatting , Request } ,
77 DocumentFormattingParams , DocumentRangeFormattingParams , FormattingOptions , InitializeResult ,
8- OneOf , Position , Range , ServerCapabilities , ServerInfo , TextDocumentSyncCapability ,
9- TextDocumentSyncKind , TextEdit , Uri ,
8+ OneOf , Range , ServerCapabilities , ServerInfo , TextDocumentSyncCapability , TextDocumentSyncKind ,
9+ TextEdit , Uri ,
1010} ;
1111use similar:: { DiffOp , TextDiff } ;
1212use stylua_lib:: { format_code, IndentType , 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}
@@ -118,13 +88,14 @@ fn handle_formatting(
11888
11989 let formatted_contents = format_code ( contents, config, range, OutputVerification :: None ) . ok ( ) ?;
12090
121- let operations = TextDiff :: from_lines ( contents, & formatted_contents) . grouped_ops ( 0 ) ;
91+ let operations =
92+ TextDiff :: from_chars ( contents. as_bytes ( ) , formatted_contents. as_bytes ( ) ) . grouped_ops ( 0 ) ;
12293 let edits = operations
12394 . into_iter ( )
12495 . flat_map ( |operations| {
12596 operations
12697 . into_iter ( )
127- . filter_map ( |op| diffop_to_textedit ( op, & formatted_contents) )
98+ . filter_map ( |op| diffop_to_textedit ( op, document , & formatted_contents) )
12899 } )
129100 . collect ( ) ;
130101 Some ( edits)
@@ -267,6 +238,8 @@ pub fn run(opt: opt::Opt) -> anyhow::Result<()> {
267238
268239#[ cfg( test) ]
269240mod tests {
241+ use std:: cmp:: Ordering ;
242+ use std:: convert:: TryInto ;
270243 use std:: str:: FromStr ;
271244
272245 use clap:: Parser ;
@@ -386,6 +359,35 @@ mod tests {
386359 assert ! ( client. receiver. is_empty( ) ) ;
387360 }
388361
362+ fn with_edits ( text : & str , mut edits : Vec < TextEdit > ) -> String {
363+ edits. sort_by ( |a, b| match a. range . start . line . cmp ( & b. range . start . line ) {
364+ Ordering :: Equal => a
365+ . range
366+ . start
367+ . character
368+ . cmp ( & b. range . start . character )
369+ . reverse ( ) ,
370+ order => order. reverse ( ) ,
371+ } ) ;
372+ let mut text = text. to_string ( ) ;
373+ for edit in edits {
374+ let start = text
375+ . lines ( )
376+ . take ( edit. range . start . line . try_into ( ) . unwrap ( ) )
377+ . map ( |line| line. len ( ) + '\n' . len_utf8 ( ) )
378+ . sum :: < usize > ( )
379+ + <u32 as TryInto < usize > >:: try_into ( edit. range . start . character ) . unwrap ( ) ;
380+ let end = text
381+ . lines ( )
382+ . take ( edit. range . end . line . try_into ( ) . unwrap ( ) )
383+ . map ( |line| line. len ( ) + '\n' . len_utf8 ( ) )
384+ . sum :: < usize > ( )
385+ + <u32 as TryInto < usize > >:: try_into ( edit. range . end . character ) . unwrap ( ) ;
386+ text. replace_range ( start..end, & edit. new_text ) ;
387+ }
388+ text
389+ }
390+
389391 #[ test]
390392 fn test_lsp_document_formatting ( ) {
391393 let uri = Uri :: from_str ( "file:///home/documents/file.luau" ) . unwrap ( ) ;
@@ -433,17 +435,8 @@ mod tests {
433435 expect_server_initialized ( & client. receiver , 1 ) ;
434436
435437 let edits: Vec < TextEdit > = expect_response ( & client. receiver , 2 ) ;
436- assert_eq ! ( edits. len( ) , 1 ) ;
437- assert_eq ! (
438- edits[ 0 ] ,
439- TextEdit {
440- range: Range {
441- start: Position :: new( 0 , 0 ) ,
442- end: Position :: new( 0 , 14 ) ,
443- } ,
444- new_text: "local x = 1\n " . to_string( ) ,
445- }
446- ) ;
438+ let formatted = with_edits ( contents, edits) ;
439+ assert_eq ! ( formatted, "local x = 1\n " ) ;
447440
448441 expect_server_shutdown ( & client. receiver , 3 ) ;
449442 assert ! ( client. receiver. is_empty( ) ) ;
@@ -497,17 +490,8 @@ mod tests {
497490 expect_server_initialized ( & client. receiver , 1 ) ;
498491
499492 let edits: Vec < TextEdit > = expect_response ( & client. receiver , 2 ) ;
500- assert_eq ! ( edits. len( ) , 1 ) ;
501- assert_eq ! (
502- edits[ 0 ] ,
503- TextEdit {
504- range: Range {
505- start: Position :: new( 0 , 0 ) ,
506- end: Position :: new( 1 , 18 ) ,
507- } ,
508- new_text: "local x = 1\n local y = 2\n " . to_string( ) ,
509- }
510- ) ;
493+ let formatted = with_edits ( contents, edits) ;
494+ assert_eq ! ( formatted, "local x = 1\n local y = 2\n " ) ;
511495
512496 expect_server_shutdown ( & client. receiver , 3 ) ;
513497 assert ! ( client. receiver. is_empty( ) ) ;
0 commit comments