Make `complete-prefix` smarter about field projection expressions passed as `-prefix`
ncik-roberts opened this issue · 0 comments
This is a feature request.
Request
complete-prefix -prefix "t." -position $POS -file $FILE
should auto-complete to the fields of the record value t
, even if $FILE
doesn't currently have the text "t." at position POS
.
This matches the current behavior of complete-prefix -prefix "M." -position $POS -file $FILE
, which is to autocomplete to the bindings of module M
, even if $FILE
doesn't currently have the text "M." at position $POS
.
Example
file.ml
:
module M = struct
type t = { a : int; b : int }
let y = { a = 3; b = 4 }
end
let module_ = (* module cursor *)
let field (x : M.t) = (* field cursor *)
Current behavior:
$ module_cursor=6:15
$ field_cursor=7:23
$ complete-prefix -prefix "M." -file file.ml -position $module_cursor < file.ml | jq .value.entries[].name
"y"
"t"
"a"
"b"
$ complete-prefix -prefix "x." -file file.ml -position $field_cursor < file.ml | jq .value.entries[].name
"contents"
"CamlinternalAtomic"
# ...more unrelated bindings...
Requested behavior:
$ module_cursor=6:15
$ field_cursor=7:23
$ complete-prefix -prefix "M." -file file.ml -position $module_cursor < file.ml | jq .value.entries[].name
"y"
"t"
"a"
"b"
$ complete-prefix -prefix "x." -file file.ml -position $field_cursor < file.ml | jq .value.entries[].name
"a"
"b"
# I don't care what comes next
Rationale
This can allow for better caching behavior in merlin.
If the file contents remain unchanged from merlin's view while the user is entering a field projection expression like M.foo.bar
, then merlin will not have to re-parse and re-typecheck the file after every keypress in order to continue providing completion results.
Concretely, if the user starts with this file:
module M = struct
module N = struct
type t = { a : int; b : int }
let y = { a = 3; b = 4 }
end
end
let go (x : M.N.t) = (* cursor is here *)
If the user types M.N.y.
at the point of the cursor, there are two ways you could imagine the editor making successive completion requests for M.
, M.N.
, and M.N.y.
:
- Sending updated file contents to merlin for each request (where the
(* cursor is here *)
is replaced withM.
,M.N.
, etc. successively) - Sending the same file contents to merlin for each request, and just varying the
-prefix
argument tocomplete-prefix
.
The second approach enjoys better caching from merlin. The second approach works well for M.
and M.N.
, because merlin tries to understand -prefix
as a module path and tries to look up its components in the environment, one at a time. However, it doesn't work for M.N.y.
as merlin's special handling of -prefix
doesn't currently extend to record field projection.
@catern is working on the editor-side of a change to get better caching behavior out of merlin and so may correct me if I've said something inaccurate. The editor change becomes simpler if merlin handles field projection too.
Technical detail
These two code paths are different right now:
- Generating field name completion results on a record field projection
- Generating completion results for a module's binding on a module path
Merlin only tries to generate field name completion results if the original file contents have something that type-checks as a record field projection at the requested position.
Merlin clearly does some name analysis on some -prefix
arguments: that's how it handles module paths. You could imagine extending this analysis to work for records.
If any of the components of -prefix
that appear prior to a .
are in lowercase, that means that the argument could be treated as record field projection, and merlin could look up the type of the lowercase identifiers in the appropriate environments in order to service the request for the appropriate field names. Examples:
foo.
foo.bar.
foo.Bar.
foo.bar.Baz.quux.