package forester

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file Semantic_tokens.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
(*
 * SPDX-FileCopyrightText: 2024 The Forester Project Contributors AND The RedPRL Development Team
 *
 * SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0 WITH LLVM-exception
 *
 *)

open Forester_prelude
open Forester_core
open Forester_compiler

open struct module L = Lsp.Types end

let print_array =
  Format.(
    pp_print_array
      ~pp_sep: (fun out () -> fprintf out "; ")
      pp_print_int
  )

module Token_type = struct
  type t = L.SemanticTokenTypes.t
  let legend : L.SemanticTokenTypes.t list = [
    Namespace;
    Type;
    Class;
    Enum;
    Interface;
    Struct;
    TypeParameter;
    Parameter;
    Variable;
    Property;
    EnumMember;
    Event;
    Function;
    Method;
    Macro;
    Keyword;
    Modifier;
    Comment;
    String;
    Number;
    Regexp;
    Operator;
    Decorator
  ]
  let of_builtin t = t

  let token_types =
    List.map
      (fun s ->
        match L.SemanticTokenTypes.yojson_of_t s with
        | `String s -> s
        | _ -> assert false
      )
      legend

  let to_int =
    let module Table = MoreLabels.Hashtbl in
    let table =
      lazy(
        let t = Table.create (List.length legend) in
        List.iteri (fun data key -> Table.add t ~key ~data) legend;
        t
      )
    in
    fun t -> Table.find (Lazy.force table) t

  let to_legend t =
    match L.SemanticTokenTypes.yojson_of_t t with
    | `String s -> s
    | _ -> assert false
end

module Token_modifiers_set = struct
  let list = [
    "declaration";
    "definition";
    "readonly";
    "static";
    "deprecated";
    "abstract";
    "async";
    "modification";
    "documentation";
    "defaultLibrary"
  ]
  ;;
end

let legend =
  L.SemanticTokensLegend.create
    ~tokenTypes: Token_type.token_types
    ~tokenModifiers: Token_modifiers_set.list

type token = {
  (* node: string; *)
  line: int;
  start_char: int;
  length: int;
  token_type: int;
  token_modifiers: int;
}
[@@deriving show]

type delta_token = {
  delta_line: int;
  delta_start_char: int;
  length: int;
  token_type: int;
  token_modifiers: int;
}
[@@deriving show]

let encode : token -> int list = function
  | {line; start_char; length; token_type; token_modifiers; _} ->
    [line; start_char; length; token_type; token_modifiers]

let encode_deltas : delta_token -> int list = function
  | {delta_line; delta_start_char; length; token_type; token_modifiers} ->
    [delta_line; delta_start_char; length; token_type; token_modifiers]

let group f l =
  let rec grouping acc = function
    | [] -> acc
    | hd :: tl ->
      let l1, l2 = List.partition (f hd) tl in
      grouping ((hd :: l1) :: acc) l2
  in
  grouping [] l

(* TODO? *)
let node_to_tokens (_ : Code.node Range.located) _ _list = []

let tokenize_path ~(start : L.Position.t) (path : string list) : token list =
  let offset = ref (start.character) in
  Eio.traceln "path has %i segments" (List.length path);
  let@ segment = List.concat_map @~ path in
  let length = String.length segment in
  let start_char = !offset in
  offset := !offset + length + 1;
  let token_type = 1 in
  let line = start.line in
  [
    {
      line;
      start_char;
      length;
      token_type;
      token_modifiers = 0;
      (* node = segment *)
    }
  ]

let shift offset =
  List.map @@ fun token ->
  {token with start_char = token.start_char + offset}

let builtin ~(start : L.Position.t) str tks =
  let offset = String.length str in
  {
    (* node = str; *)
    line = start.line;
    start_char = start.character;
    length = offset;
    token_type = 5;
    token_modifiers = 0
  } :: shift offset tks

let tokens (nodes : Code.t) : token list =
  let@ Range.{loc; value} = List.concat_map @~ nodes in
  let L.Range.{start; end_} = Lsp_shims.Loc.lsp_range_of_range loc in
  (* Multiline tokens not supported*)
  if start.line <> end_.line then
    []
  else
    match value with
    | Code.Ident path ->
      tokenize_path ~start path
    | Code.Text _ -> []
    | Code.Put (_path, _t) -> []
    (* -> *)
    (*     builtin *)
    (*       ~start *)
    (*       "put" @@ *)
    (*     tokenize_path ~start path @ tokens t *)
    | Code.Math (_, _)
    | Code.Verbatim _
    | Code.Import (_, _)
    | Code.Let (_, _, _)
    | Code.Def (_, _, _)
    | Code.Group (_, _)
    | Code.Hash_ident _
    | Code.Xml_ident (_, _)
    | Code.Subtree (_, _)
    | Code.Open _
    | Code.Scope _
    | Code.Default (_, _)
    | Code.Get _
    | Code.Fun (_, _)
    | Code.Object _
    | Code.Patch _
    | Code.Call (_, _)
    | Code.Decl_xmlns (_, _)
    | Code.Alloc _
    | Code.Dx_sequent (_, _)
    | Code.Dx_query (_, _, _)
    | Code.Dx_prop (_, _)
    | Code.Dx_var _
    | Code.Dx_const_content _
    | Code.Dx_const_uri _
    | Code.Error _
    | Code.Comment _
    | Code.Namespace (_, _) ->
      []

let process_line_delta (index_of_last_line : int option) (tokens : token list) : int * delta_token list =
  let line = (List.hd tokens).line in
  let deltas =
    List.fold_left
      (fun
          (last_token, acc)
          ({start_char;
            length;
            token_type;
            token_modifiers;
            line;
            _;
          } as current_token)
        ->
        match last_token with
        | None ->
          let delta_line = match index_of_last_line with Some i -> i - line | None -> line in
          let delta_start_char = start_char in
          let t = {delta_line; delta_start_char; length; token_type; token_modifiers} in
          (Some current_token, t :: acc)
        | Some last_token ->
          (*If there is a previous token, we know we are still on the same line*)
          let delta_line = current_token.line - last_token.line in
          let delta_start_char = if delta_line > 0 then current_token.start_char else current_token.start_char - last_token.start_char in
          let delta = {delta_line; delta_start_char; length = current_token.length; token_type = current_token.token_type; token_modifiers;} in
          (Some current_token, delta :: acc)
      )
      (None, [])
      tokens
  in
  line, snd deltas |> List.rev

let delta_tokens (tokens : token list list) : int array =
  tokens
  |> List.fold_left
      (fun (last_line, acc) tokens_on_line ->
        let line, delta_tokens = process_line_delta last_line tokens_on_line in
        Some line, delta_tokens :: acc
      )
      (None, [])
  |> snd
  |> List.rev
  |> List.concat
  |> List.concat_map encode_deltas
  |> List.rev
  |> Array.of_list

let semantic_tokens_delta (_code : Code.node Range.located list) : L.SemanticTokensDelta.t = {
  L.SemanticTokensDelta.resultId = None;
  edits = [];
}

let tokenize_document (identifier : L.TextDocumentIdentifier.t) : L.SemanticTokens.t option =
  let Lsp_state.{forest; _} = Lsp_state.get () in
  let uri = URI_scheme.lsp_uri_to_uri ~base: forest.config.url identifier.uri in
  let@ {nodes; _} = Option.map @~ Imports.resolve_uri_to_code forest uri in
  let tokens = tokens nodes in
  Format.(
    Eio.traceln
      "%a"
      (
        pp_print_list
          ~pp_sep: (fun out () -> fprintf out "; ")
          pp_token
      )
      tokens
  );
  let encoded = List.concat_map encode tokens in
  let data = Array.of_list @@ encoded in
  L.SemanticTokens.{data; resultId = None}

let tokenize_document_delta (textDocument : L.TextDocumentIdentifier.t) : L.SemanticTokensDelta.t option =
  let Lsp_state.{forest; _} = Lsp_state.get () in
  let uri = URI_scheme.lsp_uri_to_uri ~base: forest.config.url textDocument.uri in
  let@ tree = Option.map @~ Imports.resolve_uri_to_code forest uri in
  semantic_tokens_delta tree.nodes

let on_full_request (params : L.SemanticTokensParams.t) : L.SemanticTokens.t option =
  tokenize_document params.textDocument

let on_delta_request (params : L.SemanticTokensDeltaParams.t) : [`SemanticTokens of L.SemanticTokens.t | `SemanticTokensDelta of L.SemanticTokensDelta.t] option =
  let@ tokens = Option.map @~ tokenize_document_delta params.textDocument in
  `SemanticTokensDelta tokens
OCaml

Innovation. Community. Security.