linol/lsp/test/text_document_tests.ml
Simon Cruanes 7fbc187548 Squashed 'thirdparty/lsp/' content from commit aae69863
git-subtree-dir: thirdparty/lsp
git-subtree-split: aae6986391a8519de3da6a7a341f2bd3376e0d2f
2025-04-10 15:44:25 -04:00

316 lines
7.8 KiB
OCaml

open Lsp
open Lsp.Types
module List = ListLabels
module String = StringLabels
let printf = Printf.printf
let tuple_range start end_ =
{ Range.start =
(let line, character = start in
{ Position.line; character })
; end_ =
(let line, character = end_ in
{ Position.line; character })
}
;;
let make_document ?(position_encoding = `UTF8) uri ~text =
let td =
let version = 1 in
let languageId = "fake language" in
let textDocument = { TextDocumentItem.uri; version; languageId; text } in
Text_document.make ~position_encoding { DidOpenTextDocumentParams.textDocument }
in
Text_document.apply_content_changes
td
[ TextDocumentContentChangeEvent.create ~text () ]
;;
let test_general text changes =
let test position_encoding =
let td =
let uri = DocumentUri.of_path "" in
let version = 1 in
let languageId = "fake language" in
let textDocument = { TextDocumentItem.uri; version; languageId; text } in
Text_document.make ~position_encoding { DidOpenTextDocumentParams.textDocument }
in
let td =
Text_document.apply_content_changes
td
(ListLabels.map changes ~f:(fun (range, text) ->
TextDocumentContentChangeEvent.create ?range ~text ()))
in
Text_document.text td
in
let utf8 = test `UTF8 in
let utf16 = test `UTF16 in
if String.equal utf16 utf8
then printf "result: %s\n" (String.escaped utf8)
else (
print_endline "[FAILURE] utf16 and utf8 disagree";
printf "utf16: %s\n" (String.escaped utf16);
printf "utf8: %s\n" (String.escaped utf8))
;;
let test text range ~change = test_general text [ Some range, change ]
let test_multiple text changes =
test_general text (List.map changes ~f:(fun (range, text) -> Some range, text))
;;
let%expect_test "first line insert" =
let range = tuple_range (0, 1) (0, 3) in
test "foo bar baz" range ~change:"XXXX";
[%expect
{|
result: fXXXX bar baz |}]
;;
let%expect_test "null edit" =
test "foo bar" (tuple_range (0, 2) (0, 2)) ~change:"";
[%expect
{|
result: foo bar |}]
;;
let%expect_test "no range" =
test_general "foo bar baz" [ None, "XXXX" ];
[%expect
{|
result: XXXX |}]
;;
let%expect_test "char by char" =
test_multiple
""
[ tuple_range (0, 0) (0, 0), "f"
; tuple_range (0, 1) (0, 1), "o"
; tuple_range (0, 2) (0, 2), "o"
];
[%expect
{|
result: foo |}]
;;
let%expect_test "char by char - 2" =
test_multiple
"char by char - 2\n"
[ tuple_range (1, 10) (1, 10), "b"
; tuple_range (1, 10) (1, 10), "a"
; tuple_range (1, 10) (1, 10), "r"
; tuple_range (1, 1) (1, 2), ""
];
[%expect
{|
result: char by char - 2\nbr |}]
;;
let%expect_test "char by char - 3" =
test_multiple
"first line skip\nchar by char - 2\n"
[ tuple_range (1, 4) (1, 5), ""
; tuple_range (1, 3) (1, 4), ""
; tuple_range (1, 3) (1, 3), "x"
];
[%expect
{|
result: first line skip\nchaxby char - 2\n |}]
;;
let%expect_test "insert last" =
test "x" (tuple_range (0, 1) (0, 1)) ~change:"y";
[%expect
{|
result: xy |}];
test "x\ny" (tuple_range (1, 1) (1, 1)) ~change:"z";
[%expect
{|
result: x\nyz |}];
test "x\ny" (tuple_range (1, 10) (1, 10)) ~change:"z";
[%expect
{|
result: x\nyz |}]
;;
let%expect_test "replace second line" =
let range = tuple_range (1, 0) (2, 0) in
test "foo\nbar\nbaz\n" range ~change:"XXXX\n";
[%expect
{|
result: foo\nXXXX\nbaz\n |}]
;;
let%expect_test "edit in second line" =
let range = tuple_range (1, 1) (1, 2) in
test "foo\nbar\nbaz\n" range ~change:"-XXX-";
[%expect
{|
result: foo\nb-XXX-r\nbaz\n |}]
;;
let%expect_test "insert at the end" =
let range = tuple_range (3, 0) (3, 0) in
test "foo\nbar\nbaz\n" range ~change:"XXX";
[%expect
{|
result: foo\nbar\nbaz\nXXX |}];
let range = tuple_range (3, 0) (4, 0) in
test "foo\nbar\nbaz\n" range ~change:"XXX";
[%expect
{|
result: foo\nbar\nbaz\nXXX |}]
;;
let%expect_test "insert at the beginning" =
let range = tuple_range (0, 0) (0, 0) in
test "foo\n\bar\nbaz\n" range ~change:"XXX\n";
[%expect
{|
result: XXX\nfoo\n\bar\nbaz\n |}]
;;
let%expect_test "insert in the middle" =
test "ab" (tuple_range (0, 1) (0, 1)) ~change:"---";
[%expect
{|
result: a---b |}]
;;
let%expect_test "replace first line" =
let range = tuple_range (0, 0) (1, 0) in
test "foo\nbar\n" range ~change:"baz\n";
[%expect
{|
result: baz\nbar\n |}]
;;
let%expect_test "beyond max char" =
let range = tuple_range (0, 0) (0, 100) in
test "foo\nbar\n" range ~change:"baz";
[%expect
{|
result: baz\nbar\n |}]
;;
let%expect_test "entire line without newline" =
test "xxx\n" (tuple_range (0, 0) (0, 3)) ~change:"baz";
[%expect
{|
result: baz\n |}];
test "xxx\n" (tuple_range (0, 0) (0, 4)) ~change:"baz";
[%expect
{|
result: baz\n |}];
test "xxx\n" (tuple_range (0, 0) (1, 0)) ~change:"baz";
[%expect
{|
result: baz |}]
;;
let%expect_test "replace two lines" =
test "a\nb\nc\n" (tuple_range (0, 0) (2, 0)) ~change:"XXX\n";
[%expect
{|
result: XXX\nc\n |}]
;;
let%expect_test "join lines" =
test "a\nb" (tuple_range (0, 1) (1, 0)) ~change:"";
[%expect
{|
result: ab |}]
;;
let%expect_test "remove text" =
test "a---b" (tuple_range (0, 1) (0, 4)) ~change:"";
[%expect
{|
result: ab |}]
;;
let%expect_test "remove newline - 1" =
test "\n" (tuple_range (0, 0) (0, 1)) ~change:"";
[%expect
{|
result: \n |}]
;;
let%expect_test "remove newlines - 2" =
test_multiple "\nXXX\n" [ tuple_range (0, 0) (0, 1), "" ];
[%expect
{|
result: \nXXX\n |}]
;;
let%expect_test "remove newlines - 3" =
test_multiple
"\nXXX\n\n"
[ tuple_range (0, 0) (0, 1), ""; tuple_range (0, 1) (0, 2), "" ];
[%expect
{|
result: \nXXX\n\n |}]
;;
let%expect_test "update when inserting a line at the end of the doc" =
test "let x = 1;\n\nlet y = 2;" (tuple_range (2, 10) (2, 10)) ~change:"\n-ZZZ";
[%expect
{|
result: let x = 1;\n\nlet y = 2;\n-ZZZ |}]
;;
let%expect_test "update when inserting a line at the end of the doc" =
test_multiple
"1\n2\n3\n"
[ tuple_range (1, 9) (1, 9), "l"; tuple_range (1, 9) (1, 10), "" ];
[%expect
{|
result: 1\n2l\n3\n |}]
;;
let%expect_test "absolute_position" =
let text = "foo|bar\nbaz.x" in
let td = make_document (Uri.of_path "foo.ml") ~text in
let test (line, character) =
let offset = Text_document.absolute_position td (Position.create ~line ~character) in
printf "position: %d/%d\n" offset (String.length text)
in
test (0, 0);
[%expect {| position: 0/13 |}];
test (3, 0);
[%expect {| position: 13/13 |}];
test (1, 0);
[%expect {| position: 8/13 |}];
test (1, 100);
[%expect {| position: 13/13 |}];
test (0, 100);
[%expect {| position: 7/13 |}];
test (100, 0);
[%expect {| position: 13/13 |}]
;;
let%expect_test "replace second line first line is \\n" =
let range = tuple_range (1, 2) (1, 2) in
let doc = make_document (Uri.of_path "foo.ml") ~text:"\nfoo\nbar\nbaz\n" in
let edit = TextEdit.create ~newText:"change" ~range in
let new_doc = Text_document.apply_text_document_edits doc [ edit ] in
new_doc |> Text_document.text |> String.escaped |> print_endline;
[%expect
{|
\nfochangeo\nbar\nbaz\n |}]
;;
let%expect_test "get position after change" =
let range = tuple_range (1, 2) (1, 2) in
let doc = make_document (Uri.of_path "foo.ml") ~text:"\nfoo\nbar\nbaz\n" in
let edit = TextDocumentContentChangeEvent.create ~text:"change" ~range () in
let new_doc = Text_document.apply_content_changes doc [ edit ] in
let pos = Text_document.absolute_position new_doc range.start in
new_doc |> Text_document.text |> String.escaped |> print_endline;
printf "pos: %d\n" pos;
[%expect
{|
\nfochangeo\nbar\nbaz\n
pos: 22 |}]
;;