Skip to content

Commit 9d130c9

Browse files
committed
[info] Hook completion call, fix still some issues with range.
1 parent ba520d0 commit 9d130c9

File tree

7 files changed

+190
-18
lines changed

7 files changed

+190
-18
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
- new trim command (both in the server and in VSCode) to liberate
6464
space used in the cache (@ejgallego, #662, fixes #367 cc: #253 #236
6565
#348)
66+
- completion and notation display (@ejgallego, #41 ???)
6667

6768
# coq-lsp 0.1.8.1: Spring fix
6869
-----------------------------

controller/rq_completion.ml

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,19 +91,43 @@ let get_char_at_point ~(doc : Fleche.Doc.t) ~point =
9191
None
9292

9393
(* point is a utf char! *)
94-
let completion ~token:_ ~doc ~point =
94+
(* let completion ~token:_ ~doc ~point = *)
95+
96+
let build_doc_idents_and_nametab ~token ~doc ~point prefix =
97+
let st = Fleche.Info.LC.node ~doc ~point PrevIfEmpty in
98+
let st = Option.map Fleche.Doc.Node.state st in
99+
let st = Option.default doc.Fleche.Doc.root st in
100+
let toc = build_doc_idents ~doc in
101+
let open Coq.Protect.E.O in
102+
let+ nametab = Fleche.Info.Completion.candidates ~token ~st prefix in
103+
let nametab = Option.default [] nametab in
104+
let nametab = CList.map (fun label -> mk_completion ~label ()) nametab in
105+
toc @ nametab
106+
107+
(* point is a utf-16 charpoint! *)
108+
let completion ~token ~doc ~point =
95109
(* Instead of get_char_at_point we should have a CompletionContext.t, to be
96110
addressed in further completion PRs *)
97-
(match get_char_at_point ~doc ~point with
111+
match get_char_at_point ~doc ~point with
98112
| None ->
99113
let incomplete = true in
100114
let items = [] in
101-
mk_completion_list ~incomplete ~items
115+
mk_completion_list ~incomplete ~items |> Result.ok |> Coq.Protect.E.ok
102116
| Some c ->
103-
let incomplete, items =
104-
if c = '\\' then (false, unicode_list point)
105-
else (true, build_doc_idents ~doc)
117+
let open Coq.Protect.E.O in
118+
let+ incomplete, items =
119+
if c = '\\' then Coq.Protect.E.ok (false, unicode_list point)
120+
else
121+
(* We want to get the previous *)
122+
let point = (fst point, max (snd point - 1) 0) in
123+
let prefix = Rq_common.get_id_at_point ~contents:doc.contents ~point in
124+
let prefix = Option.default (String.make 1 c) prefix in
125+
let+ items = build_doc_idents_and_nametab ~token ~doc ~point prefix in
126+
(true, items)
106127
in
107128
let res = mk_completion_list ~incomplete ~items in
108-
res)
109-
|> Result.ok
129+
Result.ok res
130+
131+
let completion ~token ~doc ~point =
132+
let f () = completion ~token ~doc ~point in
133+
Request.R.of_execution ~name:"completion" ~f ()

etc/todo.org

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ info.
44

55
* Coq LSP Todos
66

7+
** 0.2
8+
- jump to definition
9+
- warnings suggestions / fix
10+
11+
** 0.3
712
- profit from command refinement
813
- lsp server process and coq-lsp-checker client for every document
914
- dune integration, auto-build, dap
1015
- documentation links
11-
- jump to definition
1216
- integration with tracing
13-
- warnings suggestions / fix
1417
- flags for coq process
1518
- Implement onsave => compile vo file? [see on-disk cache issues below]
1619

fleche/debug.ml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ let send = false || all || lsp
1212
let read = false || all || lsp
1313
let lsp_init = false || all || lsp
1414

15+
(* finding tokens from a position *)
16+
let find = false || all
17+
1518
(* Parsing (this is a bit expensive as it will call the printer *)
1619
let parsing = false || all
1720

@@ -31,4 +34,4 @@ let sched_wakeup = false || all
3134
let request_delay = true || all
3235

3336
(* Competion *)
34-
let completion = false || all
37+
let completion = true || all

fleche/info.ml

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ module LineCol : Point with type t = int * int = struct
6969
if line1 = line && line2 = line then col1 <= col && col < col2
7070
else (line1 = line && col1 <= col) || (line2 = line && col < col2)
7171

72+
(* Point is beyond [range] *)
7273
let gt_range ?range (line, col) =
7374
match range with
7475
| None -> false
@@ -172,6 +173,18 @@ module Goals = struct
172173
end
173174

174175
module Completion = struct
176+
let obind f x = Option.bind x f
177+
178+
(* let find ~doc ~point approx = *)
179+
(* let res = find ~doc ~point approx in *)
180+
(* match res with *)
181+
(* | Some res -> *)
182+
(* Io.Log.trace "info:find" ("found node at " ^ P.to_string point); *)
183+
(* Some res *)
184+
(* | None -> *)
185+
(* Io.Log.trace "info:find" ("failed at " ^ P.to_string point); *)
186+
(* None *)
187+
175188
(* XXX: This belongs in Coq *)
176189
let pr_extref gr =
177190
match gr with
@@ -180,13 +193,57 @@ module Completion = struct
180193

181194
(* XXX This may fail when passed "foo." for example, so more sanitizing is
182195
needed *)
183-
let to_qualid p = try Some (Libnames.qualid_of_string p) with _ -> None
196+
let remove_dot_if_last p : string =
197+
let l = String.length p in
198+
if l > 1 then if p.[l - 1] = '.' then String.sub p 0 (l - 1) else p else p
199+
200+
(* let candidates ~token ~st prefix = *)
201+
(* let ( let* ) = Option.bind in *)
202+
(* Coq.State.in_state ~token ~st prefix ~f:(fun prefix -> *)
203+
(* let* p = to_qualid prefix in *)
204+
(* Nametab.completion_canditates p *)
205+
(* |> List.map (fun x -> Pp.string_of_ppcmds (pr_extref x)) *)
206+
(* |> some) *)
207+
let to_qualid p =
208+
let p = remove_dot_if_last p in
209+
try Some (Libnames.qualid_of_string p)
210+
with _ ->
211+
Io.Log.trace "completion" ("broken qualid_of_string: " ^ p);
212+
None
213+
214+
let completion ~token ~st prefix =
215+
Coq.State.in_state ~token ~st prefix ~f:(fun prefix ->
216+
to_qualid prefix
217+
|> obind (fun p ->
218+
Nametab.completion_canditates p
219+
|> List.map (fun x -> Pp.string_of_ppcmds (pr_extref x))
220+
|> List.append
221+
(Notgram_ops.get_defined_notations () |> List.map snd)
222+
|> some))
223+
224+
let _get_id_at_node_point offset range text = Span.find ~offset ~range text
184225

226+
let debug_completion cat msg =
227+
if Debug.completion then Io.Log.trace ("completion: " ^ cat) msg
228+
229+
let pr_completion_res = function
230+
| None -> "no results"
231+
| Some res -> string_of_int (List.length res) ^ " results"
232+
233+
(* This is still buggy for the case that find doesn't work (i.e. no ast) *)
234+
(* let candidates ~st ~point:_ () = *)
185235
let candidates ~token ~st prefix =
186-
let ( let* ) = Option.bind in
187-
Coq.State.in_state ~token ~st prefix ~f:(fun prefix ->
188-
let* p = to_qualid prefix in
189-
Nametab.completion_canditates p
190-
|> List.map (fun x -> Pp.string_of_ppcmds (pr_extref x))
191-
|> some)
236+
(* we do exact matching for... *)
237+
(* let range = node.Doc.Node.range in *)
238+
(* let text = doc.Doc.contents.text in *)
239+
(* let span = Span.make ~contents:doc.Doc.contents ~loc in *)
240+
(* let offset = P.to_offset point text in *)
241+
(* debug_completion "offset" (string_of_int offset); *)
242+
(* let prefix = get_id_at_node_point offset range text in *)
243+
(* let prefix = "" in *)
244+
debug_completion "prefix" prefix;
245+
let open Coq.Protect.E.O in
246+
let+ res = completion ~token ~st prefix in
247+
debug_completion "n results" (pr_completion_res res);
248+
res
192249
end

fleche/span.ml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
(************************************************************************)
2+
(* Coq Language Server Protocol *)
3+
(* Copyright 2019 MINES ParisTech -- Dual License LGPL 2.1 / GPL3+ *)
4+
(* Copyright 2022 Inria -- Dual License LGPL 2.1 / GPL3+ *)
5+
(* Written by: Emilio J. Gallego Arias *)
6+
(************************************************************************)
7+
(* Status: Experimental *)
8+
(************************************************************************)
9+
10+
type t =
11+
{ range : Lang.Range.t
12+
; contents : String.t
13+
}
14+
15+
let make ~contents ~range = { range; contents }
16+
17+
let delim c =
18+
match c with
19+
| '\n' | ' ' -> true
20+
| _ -> false
21+
22+
let rec find_backwards_delim offset lower text =
23+
if offset = lower then offset
24+
else if delim text.[offset - 1] then offset
25+
else find_backwards_delim (offset - 1) lower text
26+
27+
let rec id_at_point acc offset upper text =
28+
if delim text.[offset] || offset + 1 = upper then acc
29+
else id_at_point (text.[offset] :: acc) (offset + 1) upper text
30+
31+
let id_at_point offset upper text =
32+
let id = id_at_point [] offset upper text |> List.rev in
33+
let len = List.length id in
34+
String.init len (List.nth id)
35+
36+
let debug_find cat msg =
37+
if Debug.find then Io.Log.trace ("Span.find: " ^ cat) msg
38+
39+
let find ~offset ~(range : Lang.Range.t) text =
40+
let lower = range.start.offset in
41+
let upper = range.end_.offset in
42+
debug_find "lower / upper" (string_of_int lower ^ "/" ^ string_of_int upper);
43+
debug_find "text length" (string_of_int (String.length text));
44+
let rtext = String.sub text lower (upper - lower) in
45+
debug_find "ranged" rtext;
46+
debug_find "char at off" (String.make 1 text.[offset]);
47+
debug_find "initial offset" (string_of_int offset);
48+
let offset = find_backwards_delim offset lower text in
49+
debug_find "span.find, base offset" (string_of_int offset);
50+
id_at_point offset upper text

fleche/span.mli

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
(************************************************************************)
2+
(* Coq Language Server Protocol *)
3+
(* Copyright 2019 MINES ParisTech -- Dual License LGPL 2.1 / GPL3+ *)
4+
(* Copyright 2022 Inria -- Dual License LGPL 2.1 / GPL3+ *)
5+
(* Written by: Emilio J. Gallego Arias *)
6+
(************************************************************************)
7+
(* Status: Experimental *)
8+
(************************************************************************)
9+
10+
type t =
11+
{ range : Lang.Range.t
12+
; contents : String.t
13+
}
14+
15+
val make : contents:String.t -> range:Lang.Range.t -> t
16+
17+
(** [find ~offset ~range text] finds an identifier at offset, offset is
18+
absolutely positioned *)
19+
val find : offset:int -> range:Lang.Range.t -> string -> string
20+
21+
(** TODO:
22+
23+
- We want some kind of tokenization for the span, two kinds of spans, with
24+
AST, and without *)
25+
26+
(** Focused text span on a range / XXX same structure than caching *)
27+
28+
(**
29+
type context =
30+
| Parsed of { span : t; node : Doc.node }
31+
(** [span] corresponding to [node] *)
32+
| Text of { span : t; prev : Doc.node }
33+
(** Independent [span], [prev] node for help *)
34+
*)

0 commit comments

Comments
 (0)