I am attempting to make a language server for a small domain-specific language, and I was thinking of using tree-sitter to generate a parse tree to use for auto-completions based on looking at type annotations in tree nodes. The language is strongly typed and almost every node in the tree should contain type annotations. Therefore I am hoping that I do not have to dive deep into type theory/Hindly-Milner type stuff yet to make a reasonable auto-completer, as I'm still a newbie to this.
I have been pondering on how to efficiently approach this problem, but I got stuck when thinking about harder cases. For example for the following code:
const x: Int = 20
const y: String = x.show()
// after user presses `.` -> get the word before the `.` with a tree query -> `x`
-> lookup type of `x` in the tree -> suggest the hard-coded completion method `show()` (Int -> String)
Inferring auto-completions for x
should be no problem, as I could hard-code the completion methods for these (primitive) builtin types (eg Bool
, Int
, String
).
Where I get stuck is in a case like this, where the user defines its own types, in this case a struct type, and chains multiple nested member (and/or call) expressions:
struct Datum {
deadline: Int
}
const d: Datum = Datum { 5 }
const deadline: String = d.deadline.show()
// after user presses `.` -> lookup word before `.` -> `d.deadline` -> how to infer completions for d.deadline?
--
<Node type=struct_statement, start_point=(2, 0), end_point=(4, 1)>,
<Node type=const_statement, start_point=(6, 0), end_point=(6, 28)>,
<Node type="const", start_point=(7, 0), end_point=(7, 5)>,
<Node type=identifier, start_point=(7, 6), end_point=(7, 14)>,
<Node type=":", start_point=(7, 14), end_point=(7, 15)>,
<Node type=type, start_point=(7, 16), end_point=(7, 22)>,
<Node type="=", start_point=(7, 23), end_point=(7, 24)>,
<Node type=member_expression, start_point=(7, 25), end_point=(7, 35)>,
<Node type=".", start_point=(7, 35), end_point=(7, 36)>]
How could I efficiently infer type and auto-completions for a more complicated case like this? Is it as simple as evaluating from left to right? A pseudo-algorithm I came up with which may or may not work was the following:
start with d ->
infer its type (Datum in this case) ->
get the Datum methods + fields (only 1 field in this case) ->
check if the word following the . character (deadline in this case) is contained herein ->
if not return (there's an error), else check its type ->
finished if builtin type, else if user defined type repeat the process
Perhaps this approach is very inefficient, and there is a much better way I haven't explored with tree queries?
Appreciate all help and input!