@@ -17,14 +17,33 @@ use crate::{config::ConfigResolver, opt, stylua_ignore};
1717fn diffop_to_textedit (
1818 op : DiffOp ,
1919 document : & FullTextDocument ,
20+ original_contents : & str ,
2021 formatted_contents : & str ,
2122) -> Option < TextEdit > {
22- let range = |start : usize , len : usize | Range {
23- start : document. position_at ( start. try_into ( ) . expect ( "usize fits into u32" ) ) ,
24- end : document. position_at ( ( start + len) . try_into ( ) . expect ( "usize fits into u32" ) ) ,
23+ let range = |start : usize , len : usize | {
24+ let byte_start = original_contents
25+ . char_indices ( )
26+ . nth ( start)
27+ . map ( |( i, _) | i)
28+ . unwrap_or ( original_contents. len ( ) ) ;
29+ let byte_end = original_contents
30+ . char_indices ( )
31+ . nth ( start + len)
32+ . map ( |( i, _) | i)
33+ . unwrap_or ( original_contents. len ( ) ) ;
34+ Range {
35+ start : document. position_at ( byte_start. try_into ( ) . expect ( "usize fits into u32" ) ) ,
36+ end : document. position_at ( byte_end. try_into ( ) . expect ( "usize fits into u32" ) ) ,
37+ }
2538 } ;
2639
27- let lookup = |start : usize , len : usize | formatted_contents[ start..start + len] . to_string ( ) ;
40+ let lookup = |start : usize , len : usize | {
41+ formatted_contents
42+ . chars ( )
43+ . skip ( start)
44+ . take ( len)
45+ . collect :: < String > ( )
46+ } ;
2847
2948 match op {
3049 DiffOp :: Equal {
@@ -181,14 +200,14 @@ impl LanguageServer<'_> {
181200 return Err ( FormattingError :: StyLuaError ) ;
182201 } ;
183202
184- let operations =
185- TextDiff :: from_chars ( contents . as_bytes ( ) , formatted_contents . as_bytes ( ) ) . grouped_ops ( 0 ) ;
203+ let operations = TextDiff :: from_chars ( contents , & formatted_contents ) . grouped_ops ( 0 ) ;
204+
186205 let edits = operations
187206 . into_iter ( )
188207 . flat_map ( |operations| {
189- operations
190- . into_iter ( )
191- . filter_map ( |op| diffop_to_textedit ( op , document , & formatted_contents ) )
208+ operations. into_iter ( ) . filter_map ( |op| {
209+ diffop_to_textedit ( op , document , contents , & formatted_contents )
210+ } )
192211 } )
193212 . collect ( ) ;
194213 Ok ( edits)
@@ -657,6 +676,153 @@ mod tests {
657676 assert ! ( client. receiver. is_empty( ) ) ;
658677 }
659678
679+ #[ test]
680+ fn test_lsp_document_formatting_with_unicode ( ) {
681+ let uri = Uri :: from_str ( "file:///home/documents/file.lua" ) . unwrap ( ) ;
682+ let contents = "local x = 1 -- 测试\n local y =2" ;
683+
684+ let opt = Opt :: parse_from ( vec ! [ "BINARY_NAME" ] ) ;
685+ let mut config_resolver = ConfigResolver :: new ( & opt) . unwrap ( ) ;
686+
687+ let ( server, client) = Connection :: memory ( ) ;
688+ client. sender . send ( initialize ( 1 , None ) ) . unwrap ( ) ;
689+ client. sender . send ( initialized ( ) ) . unwrap ( ) ;
690+ client
691+ . sender
692+ . send ( open_text_document ( uri. clone ( ) , contents. to_string ( ) ) )
693+ . unwrap ( ) ;
694+ client
695+ . sender
696+ . send ( format_document (
697+ 2 ,
698+ uri. clone ( ) ,
699+ FormattingOptions :: default ( ) ,
700+ ) )
701+ . unwrap ( ) ;
702+ client. sender . send ( shutdown ( 3 ) ) . unwrap ( ) ;
703+ client. sender . send ( exit ( ) ) . unwrap ( ) ;
704+
705+ main_loop ( server, false , & mut config_resolver) . unwrap ( ) ;
706+
707+ expect_server_initialized ( & client. receiver , 1 ) ;
708+
709+ let edits: Vec < TextEdit > = expect_response ( & client. receiver , 2 ) ;
710+ assert_eq ! (
711+ edits,
712+ [
713+ TextEdit {
714+ range: Range {
715+ start: Position {
716+ line: 0 ,
717+ character: 6
718+ } ,
719+ end: Position {
720+ line: 0 ,
721+ character: 7
722+ }
723+ } ,
724+ new_text: "" . to_string( )
725+ } ,
726+ TextEdit {
727+ range: Range {
728+ start: Position {
729+ line: 0 ,
730+ character: 8
731+ } ,
732+ end: Position {
733+ line: 0 ,
734+ character: 9
735+ }
736+ } ,
737+ new_text: "" . to_string( )
738+ } ,
739+ TextEdit {
740+ range: Range {
741+ start: Position {
742+ line: 0 ,
743+ character: 11
744+ } ,
745+ end: Position {
746+ line: 0 ,
747+ character: 12
748+ }
749+ } ,
750+ new_text: "" . to_string( )
751+ } ,
752+ TextEdit {
753+ range: Range {
754+ start: Position {
755+ line: 1 ,
756+ character: 5
757+ } ,
758+ end: Position {
759+ line: 1 ,
760+ character: 7
761+ }
762+ } ,
763+ new_text: "" . to_string( )
764+ } ,
765+ TextEdit {
766+ range: Range {
767+ start: Position {
768+ line: 1 ,
769+ character: 8
770+ } ,
771+ end: Position {
772+ line: 1 ,
773+ character: 9
774+ }
775+ } ,
776+ new_text: "" . to_string( )
777+ } ,
778+ TextEdit {
779+ range: Range {
780+ start: Position {
781+ line: 1 ,
782+ character: 10
783+ } ,
784+ end: Position {
785+ line: 1 ,
786+ character: 12
787+ }
788+ } ,
789+ new_text: "" . to_string( )
790+ } ,
791+ TextEdit {
792+ range: Range {
793+ start: Position {
794+ line: 1 ,
795+ character: 14
796+ } ,
797+ end: Position {
798+ line: 1 ,
799+ character: 14
800+ }
801+ } ,
802+ new_text: " " . to_string( )
803+ } ,
804+ TextEdit {
805+ range: Range {
806+ start: Position {
807+ line: 1 ,
808+ character: 15
809+ } ,
810+ end: Position {
811+ line: 1 ,
812+ character: 15
813+ }
814+ } ,
815+ new_text: "\n " . to_string( )
816+ }
817+ ]
818+ ) ;
819+ let formatted = apply_text_edits_to ( contents, edits) ;
820+ assert_eq ! ( formatted, "local x = 1 -- 测试\n local y = 2\n " ) ;
821+
822+ expect_server_shutdown ( & client. receiver , 3 ) ;
823+ assert ! ( client. receiver. is_empty( ) ) ;
824+ }
825+
660826 #[ test]
661827 fn test_lsp_range_formatting ( ) {
662828 let uri = Uri :: from_str ( "file:///home/documents/file.luau" ) . unwrap ( ) ;
0 commit comments