Source file text_document.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
open Import
include struct
open Types
module DidOpenTextDocumentParams = DidOpenTextDocumentParams
module DocumentUri = DocumentUri
module Range = Range
module TextDocumentItem = TextDocumentItem
module TextDocumentContentChangeEvent = TextDocumentContentChangeEvent
module TextEdit = TextEdit
end
type invalid_utf = String_zipper.invalid_utf =
| Malformed of string
| Insufficient_input
exception Invalid_utf = String_zipper.Invalid_utf
type t =
{ languageId : string
;
mutable text : string option
; uri : DocumentUri.t
; version : int
; mutable zipper : String_zipper.t
; position_encoding : [ `UTF8 | `UTF16 ]
}
let position_encoding t = t.position_encoding
let make
~position_encoding
{ DidOpenTextDocumentParams.textDocument =
{ TextDocumentItem.languageId; text; uri; version }
}
=
let zipper = String_zipper.of_string text in
{ text = Some text; position_encoding; zipper; uri; version; languageId }
;;
let documentUri (t : t) = t.uri
let version (t : t) = t.version
let languageId (t : t) = t.languageId
let apply_change encoding sz (change : TextDocumentContentChangeEvent.t) =
match change.range with
| None -> String_zipper.of_string change.text
| Some range -> String_zipper.apply_change sz range encoding ~replacement:change.text
;;
let apply_content_changes ?version t changes =
let zipper =
List.fold_left ~f:(apply_change t.position_encoding) ~init:t.zipper changes
in
let version =
match version with
| None -> t.version
| Some version -> version
in
{ t with zipper; text = None; version }
;;
let text t =
match t.text with
| Some text -> text
| None ->
let zipper, text = String_zipper.squash t.zipper in
t.text <- Some text;
t.zipper <- zipper;
text
;;
module Edit_map = Map.Make (Position)
let add_edit map (change : TextEdit.t) =
Edit_map.update map ~key:change.range.start ~f:(function
| None -> Some [ change ]
| Some changes -> Some (change :: changes))
;;
let apply_changes zipper encoding changes =
let simplified = List.fold_left changes ~init:Edit_map.empty ~f:add_edit in
let b = Buffer.create 0 in
let pos = ref Position.zero in
let zipper = String_zipper.goto_position zipper !pos encoding in
let zipper = ref zipper in
Edit_map.iter simplified ~f:(fun ~key:start ~data ->
assert (Position.compare start !pos >= 0);
zipper := String_zipper.goto_position !zipper !pos encoding;
let zipper' = String_zipper.goto_position !zipper start encoding in
String_zipper.add_buffer_between b !zipper zipper';
zipper := zipper';
List.rev data
|> List.iter ~f:(fun { TextEdit.newText; range } ->
assert (Position.compare range.end_ !pos >= 0);
pos := range.end_;
Buffer.add_string b newText));
let zipper = String_zipper.goto_position !zipper !pos encoding in
let zipper' = String_zipper.goto_end zipper in
String_zipper.add_buffer_between b zipper zipper';
Buffer.contents b
;;
let set_version t ~version = { t with version }
let apply_text_document_edits t (edits : TextEdit.t list) =
let text = apply_changes t.zipper t.position_encoding edits in
let zipper = String_zipper.of_string text in
{ t with text = Some text; zipper }
;;
let absolute_position t pos =
String_zipper.goto_position t.zipper pos t.position_encoding |> String_zipper.offset
;;
let absolute_range t (range : Range.t) =
let zipper = String_zipper.goto_position t.zipper range.start t.position_encoding in
let start = String_zipper.offset zipper in
let zipper = String_zipper.goto_position zipper range.end_ t.position_encoding in
let stop = String_zipper.offset zipper in
start, stop
;;