Skip to content

Commit e7d71a1

Browse files
committed
lsp: local autocompletion #30
1 parent 6eebdb5 commit e7d71a1

39 files changed

+225
-11
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- compiler: `import root` imports the current package (imports `src` if folder exists)
99
- lsp: improved autocompletion and hover information
1010
- lsp: autocompletion and hover information across module and file boundaries
11+
- lsp: local autocompletion #30
1112

1213
## v0.0.15
1314

README.md

+20-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Currently Lithia is an early proof of concept. Basic language features exist, bu
2626
- [x] Creating a custom language server
2727
- [x] ... with diagnostics
2828
- [x] ... with syntax highlighting
29-
- [ ] ... with auto completion _proof of concept_
29+
- [x] ... with auto completion _basic_
3030
- [ ] ... with highlights
3131
- [ ] ... with refactorings
3232
- [ ] ... with formatter
@@ -264,9 +264,26 @@ As shown, a common use case is to pass the module itself instead of multiple wit
264264

265265
### Module resolution
266266

267-
Lithia will search for a folder containing source files at the following locations:
267+
To create your own package of modules, you can create a `Potfile`. Every module defined by the package is a directory next to the Potfile, with `.lithia` files in it.
268+
269+
Though there are a few special cases:
270+
271+
- the `cmd`-folder is typically used for individual files rather than a module. You execute them with `$ lithia cmd/<file>`.
272+
- the `src`-folder represents the `root` of the package.
273+
- if the `src`-folder is missing, the package `root` is next to the Potfile.
274+
275+
```
276+
.
277+
├── Potfile
278+
├── cmd
279+
│ ├── main.lithia
280+
│ └── test.lithia
281+
└── src
282+
└── greet.lithia
283+
```
284+
285+
If there aren't any matching local modules, Lithia will search for a package containing source files at the following locations:
268286

269-
- when executing a file, relative to it
270287
- when in REPL, inside the current working directory
271288
- at `$LITHIA_LOCALS` if set
272289
- at `$LITHIA_PACKAGES` if set

ast/context-file.go

+9
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,12 @@ func (sf *SourceFile) ExportedDeclarations() []Decl {
4848
}
4949
return decls
5050
}
51+
52+
func (sf SourceFile) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
53+
for _, decl := range sf.Declarations {
54+
decl.EnumerateNestedDecls(enumerate)
55+
}
56+
for _, stmt := range sf.Statements {
57+
stmt.EnumerateNestedDecls(enumerate)
58+
}
59+
}

ast/decl-constant.go

+4
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,7 @@ func MakeDeclConstant(name Identifier, value Expr, source *Source) *DeclConstant
4040
func (e DeclConstant) ProvidedDocs() *Docs {
4141
return e.Docs
4242
}
43+
44+
func (e DeclConstant) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
45+
e.Value.EnumerateNestedDecls(enumerate)
46+
}

ast/decl-data.go

+4
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,7 @@ func (e *DeclData) AddField(field DeclField) {
5757
func (decl DeclData) ProvidedDocs() *Docs {
5858
return decl.Docs
5959
}
60+
61+
func (DeclData) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
62+
// no nested decls
63+
}

ast/decl-enum-case.go

+4
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,7 @@ func MakeDeclEnumCase(name Identifier) *DeclEnumCase {
3131
func (decl DeclEnumCase) ProvidedDocs() *Docs {
3232
return decl.Docs
3333
}
34+
35+
func (DeclEnumCase) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
36+
// no nested decls
37+
}

ast/decl-enum.go

+4
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,7 @@ func (e DeclEnum) String() string {
6969
func (decl DeclEnum) ProvidedDocs() *Docs {
7070
return decl.Docs
7171
}
72+
73+
func (decl DeclEnum) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
74+
// no nested decls - will be handled by the parser
75+
}

ast/decl-extern-func.go

+4
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,7 @@ func MakeDeclExternFunc(name Identifier, params []DeclParameter, source *Source)
5050
func (decl DeclExternFunc) ProvidedDocs() *Docs {
5151
return decl.Docs
5252
}
53+
54+
func (DeclExternFunc) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
55+
// no nested decls
56+
}

ast/decl-extern-type.go

+4
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,7 @@ func (e *DeclExternType) AddField(decl DeclField) {
5555
func (decl DeclExternType) ProvidedDocs() *Docs {
5656
return decl.Docs
5757
}
58+
59+
func (DeclExternType) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
60+
// no nested decls
61+
}

ast/decl-field.go

+4
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,7 @@ func MakeDeclField(name Identifier, params []DeclParameter, source *Source) *Dec
5353
func (decl DeclField) ProvidedDocs() *Docs {
5454
return decl.Docs
5555
}
56+
57+
func (DeclField) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
58+
// no nested decls
59+
}

ast/decl-func.go

+4
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,7 @@ func MakeDeclFunc(name Identifier, impl *ExprFunc, source *Source) *DeclFunc {
5252
func (decl DeclFunc) ProvidedDocs() *Docs {
5353
return decl.Docs
5454
}
55+
56+
func (decl DeclFunc) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
57+
decl.Impl.EnumerateNestedDecls(enumerate)
58+
}

ast/decl-import-member.go

+4
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ func MakeDeclImportMember(moduleName ModuleName, name Identifier, source *Source
3535
MetaInfo: &MetaDecl{source},
3636
}
3737
}
38+
39+
func (DeclImportMember) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
40+
// no nested decls
41+
}

ast/decl-import.go

+4
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@ func MakeDeclImport(name ModuleName, source *Source) *DeclImport {
4343
MetaInfo: &MetaDecl{source},
4444
}
4545
}
46+
47+
func (DeclImport) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
48+
// no nested decls
49+
}

ast/decl-module.go

+4
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ func MakeDeclModule(internalName Identifier, source *Source) *DeclModule {
3535
func (decl DeclModule) ProvidedDocs() *Docs {
3636
return decl.Docs
3737
}
38+
39+
func (DeclModule) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
40+
// no nested decls
41+
}

ast/decl-parameter.go

+4
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ func MakeDeclParameter(name Identifier, source *Source) *DeclParameter {
3333
func (decl DeclParameter) ProvidedDocs() *Docs {
3434
return decl.Docs
3535
}
36+
37+
func (DeclParameter) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
38+
// no nested decls
39+
}

ast/decl.go

+2
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ type Decl interface {
44
DeclName() Identifier
55
IsExportedDecl() bool
66
Meta() *MetaDecl
7+
8+
EnumerateNestedDecls(enumerate func(interface{}, []Decl))
79
}

ast/expr-array.go

+6
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,9 @@ func MakeExprArray(elements []Expr, source *Source) *ExprArray {
2020
},
2121
}
2222
}
23+
24+
func (e ExprArray) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
25+
for _, el := range e.Elements {
26+
el.EnumerateNestedDecls(enumerate)
27+
}
28+
}

ast/expr-float.go

+4
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ type ExprFloat struct {
1111
func (e ExprFloat) Meta() *MetaExpr {
1212
return e.MetaInfo
1313
}
14+
15+
func (e ExprFloat) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
16+
// no nested decls
17+
}

ast/expr-func.go

+16
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,19 @@ func (e *ExprFunc) AddDecl(decl Decl) {
3232
func (e *ExprFunc) AddExpr(expr Expr) {
3333
e.Expressions = append(e.Expressions, expr)
3434
}
35+
36+
func (e ExprFunc) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
37+
enumeratedDecls := make([]Decl, len(e.Parameters)+len(e.Declarations))
38+
for i, param := range e.Parameters {
39+
enumeratedDecls[i] = param
40+
}
41+
for i, decl := range e.Declarations {
42+
decl.EnumerateNestedDecls(enumerate)
43+
enumeratedDecls[len(e.Parameters)+i] = decl
44+
}
45+
enumerate(e, enumeratedDecls)
46+
47+
for _, expr := range e.Expressions {
48+
expr.EnumerateNestedDecls(enumerate)
49+
}
50+
}

ast/expr-group.go

+4
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ func MakeExprGroup(expr Expr, source *Source) *ExprGroup {
1818
MetaInfo: &MetaExpr{Source: source},
1919
}
2020
}
21+
22+
func (e ExprGroup) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
23+
e.Expr.EnumerateNestedDecls(enumerate)
24+
}

ast/expr-identifier.go

+4
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ func MakeExprIdentifier(name Identifier, source *Source) *ExprIdentifier {
2020
},
2121
}
2222
}
23+
24+
func (e ExprIdentifier) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
25+
// no nested decls
26+
}

ast/expr-int.go

+4
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ func MakeExprInt(literal int64, source *Source) *ExprInt {
2020
},
2121
}
2222
}
23+
24+
func (e ExprInt) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
25+
// no nested decls
26+
}

ast/expr-invocation.go

+8
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,11 @@ func MakeExprInvocation(function Expr, source *Source) *ExprInvocation {
2323
func (e *ExprInvocation) AddArgument(argument Expr) {
2424
e.Arguments = append(e.Arguments, &argument)
2525
}
26+
27+
func (e ExprInvocation) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
28+
e.Function.EnumerateNestedDecls(enumerate)
29+
30+
for _, arg := range e.Arguments {
31+
(*arg).EnumerateNestedDecls(enumerate)
32+
}
33+
}

ast/expr-member-access.go

+4
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@ func MakeExprMemberAccess(target Expr, accessPath []Identifier, source *Source)
2222
},
2323
}
2424
}
25+
26+
func (e ExprMemberAccess) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
27+
e.Target.EnumerateNestedDecls(enumerate)
28+
}

ast/expr-operator-binary.go

+5
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,8 @@ func MakeExprOperatorBinary(operator OperatorBinary, left, right Expr, source *S
2424
},
2525
}
2626
}
27+
28+
func (e ExprOperatorBinary) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
29+
e.Left.EnumerateNestedDecls(enumerate)
30+
e.Right.EnumerateNestedDecls(enumerate)
31+
}

ast/expr-operator-unary.go

+4
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,7 @@ type ExprOperatorUnary struct {
1212
func (e ExprOperatorUnary) Meta() *MetaExpr {
1313
return e.MetaInfo
1414
}
15+
16+
func (e ExprOperatorUnary) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
17+
(*e.Expr).EnumerateNestedDecls(enumerate)
18+
}

ast/expr-string.go

+4
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ func MakeExprString(literal string, source *Source) *ExprString {
2020
},
2121
}
2222
}
23+
24+
func (e ExprString) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
25+
// no nested decls
26+
}

ast/expr-type-switch.go

+7
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,10 @@ func (e *ExprTypeSwitch) AddCase(key Identifier, value Expr) {
2929
e.CaseOrder = append(e.CaseOrder, key)
3030
e.Cases[key] = value
3131
}
32+
33+
func (e ExprTypeSwitch) EnumerateNestedDecls(enumerate func(interface{}, []Decl)) {
34+
e.Type.EnumerateNestedDecls(enumerate)
35+
for _, ident := range e.CaseOrder {
36+
e.Cases[ident].EnumerateNestedDecls(enumerate)
37+
}
38+
}

ast/expr.go

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ package ast
22

33
type Expr interface {
44
Meta() *MetaExpr
5+
EnumerateNestedDecls(enumerate func(interface{}, []Decl))
56
}

ast/meta-decl.go

+4
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ package ast
33
type MetaDecl struct {
44
*Source
55
}
6+
7+
func (m MetaDecl) SourceLocation() *Source {
8+
return m.Source
9+
}

ast/meta-expr.go

+4
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ package ast
33
type MetaExpr struct {
44
Source *Source
55
}
6+
7+
func (m MetaExpr) SourceLocation() *Source {
8+
return m.Source
9+
}

ast/meta.go

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package ast
2+
3+
type Meta interface {
4+
SourceLocation() *Source
5+
}

examples/greeter/cmd/main.lithia

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import package
1+
import root
22

3-
package.greet "World"
3+
root.greet "World"

langsrv/handler-completion.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,20 @@ func textDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
1313
rc := NewReqContextAtPosition(&params.TextDocumentPositionParams)
1414

1515
completionItems := []protocol.CompletionItem{}
16-
for _, imported := range rc.globalDeclarations(context) {
16+
for _, imported := range rc.accessibleDeclarations(context) {
1717
insertText := insertTextForImportedDecl(imported)
1818
var detail string
1919
if imported.decl.Meta().ModuleName != "" {
2020
detail = string(imported.decl.Meta().ModuleName) +
2121
"." +
2222
string(imported.decl.DeclName())
2323
}
24+
var importPrefix string
25+
if imported.importDecl != nil {
26+
importPrefix = fmt.Sprintf("%s.", imported.importDecl.DeclName())
27+
}
2428
completionItems = append(completionItems, protocol.CompletionItem{
25-
Label: string(imported.decl.DeclName()),
29+
Label: importPrefix + string(imported.decl.DeclName()),
2630
Kind: completionItemKindForDecl(imported.decl),
2731
InsertText: &insertText,
2832
Detail: &detail,

langsrv/handler-declaration.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func textDocumentDeclaration(context *glsp.Context, params *protocol.Declaration
1313
return nil, nil
1414
}
1515

16-
for _, imported := range rc.globalDeclarations(context) {
16+
for _, imported := range rc.accessibleDeclarations(context) {
1717
if string(imported.decl.DeclName()) != token || imported.decl.Meta().Source == nil {
1818
continue
1919
}

langsrv/handler-definition.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func textDocumentDefinition(context *glsp.Context, params *protocol.DefinitionPa
1313
return nil, nil
1414
}
1515

16-
for _, imported := range rc.globalDeclarations(context) {
16+
for _, imported := range rc.accessibleDeclarations(context) {
1717
decl := imported.decl
1818
if string(decl.DeclName()) != token || decl.Meta().Source == nil {
1919
continue

langsrv/handler-hover.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func textDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*pr
1313
return nil, nil
1414
}
1515

16-
for _, imported := range rc.globalDeclarations(context) {
16+
for _, imported := range rc.accessibleDeclarations(context) {
1717
decl := imported.decl
1818
if string(decl.DeclName()) != name {
1919
continue

langsrv/node-position-helpers.go

+22
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package langsrv
33
import (
44
sitter "github.com/smacker/go-tree-sitter"
55
protocol "github.com/tliron/glsp/protocol_3_16"
6+
"github.com/vknabel/lithia/ast"
67
)
78

89
func NodeAtPosition(node *sitter.Node, position protocol.Position) *sitter.Node {
@@ -62,3 +63,24 @@ func includesNodePosition(node *sitter.Node, position protocol.Position) bool {
6263
}
6364
return false
6465
}
66+
67+
func includesAstSourcePosition(source *ast.Source, position protocol.Position) bool {
68+
if source == nil {
69+
return false
70+
}
71+
startRow := uint32(source.Start.Line)
72+
startCol := uint32(source.Start.Column)
73+
endRow := uint32(source.End.Line)
74+
endCol := uint32(source.End.Column)
75+
76+
if startRow <= position.Line && endRow >= position.Line {
77+
if startRow == position.Line && startCol > position.Character {
78+
return false
79+
}
80+
if endRow == position.Line && endCol < position.Character {
81+
return false
82+
}
83+
return true
84+
}
85+
return false
86+
}

0 commit comments

Comments
 (0)