Skip to content

Commit a42bdb9

Browse files
committed
lsp: improved autocompletion within a module
1 parent 7e36af6 commit a42bdb9

File tree

6 files changed

+173
-69
lines changed

6 files changed

+173
-69
lines changed

ast/context-file.go

+10
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,13 @@ func (sf *SourceFile) AddExpr(expr Expr) {
3838
}
3939
sf.Statements = append(sf.Statements, expr)
4040
}
41+
42+
func (sf *SourceFile) ExportedDeclarations() []Decl {
43+
decls := make([]Decl, 0)
44+
for _, decl := range sf.Declarations {
45+
if decl.IsExportedDecl() {
46+
decls = append(decls, decl)
47+
}
48+
}
49+
return decls
50+
}

langsrv/handler-completion.go

+108-47
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package langsrv
22

33
import (
44
"fmt"
5+
"strings"
56

67
"github.com/tliron/glsp"
78
protocol "github.com/tliron/glsp/protocol_3_16"
@@ -14,63 +15,123 @@ func textDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
1415
if sourceFile == nil {
1516
return nil, nil
1617
}
17-
18-
completionItems := []protocol.CompletionItem{}
19-
for _, decl := range sourceFile.Declarations {
20-
var docs string
21-
if documented, ok := decl.(ast.Documented); ok {
22-
docs = documented.ProvidedDocs().Content + "\n\n"
18+
globalDeclarations := sourceFile.Declarations
19+
for _, sameModuleFile := range rc.textDocumentEntry.module.Files {
20+
fileUrl := "file://" + sameModuleFile
21+
if rc.item.URI == fileUrl {
22+
continue
2323
}
24-
var overview string
25-
if overviewable, ok := decl.(ast.Overviewable); ok {
26-
overview = "```lithia\n" + overviewable.DeclOverview() + "\n```\n\n"
24+
docEntry := langserver.documentCache.documents[fileUrl]
25+
if docEntry == nil || docEntry.sourceFile == nil {
26+
continue
2727
}
28+
29+
globalDeclarations = append(globalDeclarations, docEntry.sourceFile.ExportedDeclarations()...)
30+
}
31+
32+
completionItems := []protocol.CompletionItem{}
33+
for _, decl := range globalDeclarations {
34+
insertText := insertTextForDecl(decl)
2835
var detail string
2936
if decl.Meta().ModuleName != "" {
3037
detail = string(decl.Meta().ModuleName) +
3138
"." +
3239
string(decl.DeclName())
3340
}
34-
var kind protocol.CompletionItemKind
35-
switch decl.(type) {
36-
case ast.DeclEnum:
37-
kind = protocol.CompletionItemKindEnum
38-
case ast.DeclEnumCase:
39-
kind = protocol.CompletionItemKindEnumMember
40-
case ast.DeclConstant:
41-
kind = protocol.CompletionItemKindConstant
42-
case ast.DeclData:
43-
kind = protocol.CompletionItemKindStruct
44-
case ast.DeclExternFunc:
45-
kind = protocol.CompletionItemKindFunction
46-
case ast.DeclExternType:
47-
kind = protocol.CompletionItemKindClass
48-
case ast.DeclField:
49-
kind = protocol.CompletionItemKindField
50-
case ast.DeclFunc:
51-
kind = protocol.CompletionItemKindFunction
52-
case ast.DeclImport:
53-
kind = protocol.CompletionItemKindModule
54-
case ast.DeclImportMember:
55-
kind = protocol.CompletionItemKindValue
56-
case ast.DeclModule:
57-
kind = protocol.CompletionItemKindModule
58-
case ast.DeclParameter:
59-
kind = protocol.CompletionItemKindVariable
60-
default:
61-
kind = protocol.CompletionItemKindText
62-
}
63-
insertText := string(decl.DeclName())
6441
completionItems = append(completionItems, protocol.CompletionItem{
65-
Label: string(decl.DeclName()),
66-
Kind: &kind,
67-
InsertText: &insertText,
68-
Detail: &detail,
69-
Documentation: &protocol.MarkupContent{
70-
Kind: protocol.MarkupKindMarkdown,
71-
Value: overview + fmt.Sprintf("_module %s_\n\n", decl.Meta().ModuleName) + docs,
72-
},
42+
Label: string(decl.DeclName()),
43+
Kind: completionItemKindForDecl(decl),
44+
InsertText: &insertText,
45+
Detail: &detail,
46+
Documentation: documentationMarkupContentForDecl(decl),
7347
})
7448
}
7549
return &completionItems, nil
7650
}
51+
52+
func insertTextForDecl(decl ast.Decl) string {
53+
switch decl := decl.(type) {
54+
case ast.DeclFunc:
55+
return insertTextForCallableDeclParams(decl, decl.Impl.Parameters)
56+
case ast.DeclExternFunc:
57+
return insertTextForCallableDeclParams(decl, decl.Parameters)
58+
case ast.DeclData:
59+
return insertTextForCallableDeclFields(decl, decl.Fields)
60+
default:
61+
return string(decl.DeclName())
62+
}
63+
}
64+
65+
func insertTextForCallableDeclParams(decl ast.Decl, parameters []ast.DeclParameter) string {
66+
paramNames := make([]string, len(parameters))
67+
for i, param := range parameters {
68+
paramNames[i] = string(param.DeclName())
69+
}
70+
return insertTextForCallableDecl(decl, paramNames)
71+
}
72+
73+
func insertTextForCallableDeclFields(decl ast.Decl, fields []ast.DeclField) string {
74+
fieldNames := make([]string, len(fields))
75+
for i, param := range fields {
76+
fieldNames[i] = string(param.DeclName())
77+
}
78+
return insertTextForCallableDecl(decl, fieldNames)
79+
}
80+
81+
func insertTextForCallableDecl(decl ast.Decl, parameters []string) string {
82+
if len(parameters) == 0 {
83+
return string(decl.DeclName())
84+
}
85+
if len(parameters) == 1 {
86+
return fmt.Sprintf("%s %s", decl.DeclName(), parameters[0])
87+
}
88+
return fmt.Sprintf("(%s %s)", decl.DeclName(), strings.Join(parameters, ", "))
89+
}
90+
91+
func documentationMarkupContentForDecl(decl ast.Decl) *protocol.MarkupContent {
92+
var docs string
93+
if documented, ok := decl.(ast.Documented); ok {
94+
docs = documented.ProvidedDocs().Content + "\n\n"
95+
}
96+
var overview string
97+
if overviewable, ok := decl.(ast.Overviewable); ok {
98+
overview = "```lithia\n" + overviewable.DeclOverview() + "\n```\n\n"
99+
}
100+
return &protocol.MarkupContent{
101+
Kind: protocol.MarkupKindMarkdown,
102+
Value: overview + fmt.Sprintf("_module %s_\n\n", decl.Meta().ModuleName) + docs,
103+
}
104+
}
105+
106+
func completionItemKindForDecl(decl ast.Decl) *protocol.CompletionItemKind {
107+
var kind protocol.CompletionItemKind
108+
switch decl.(type) {
109+
case ast.DeclEnum:
110+
kind = protocol.CompletionItemKindEnum
111+
case ast.DeclEnumCase:
112+
kind = protocol.CompletionItemKindEnumMember
113+
case ast.DeclConstant:
114+
kind = protocol.CompletionItemKindConstant
115+
case ast.DeclData:
116+
kind = protocol.CompletionItemKindStruct
117+
case ast.DeclExternFunc:
118+
kind = protocol.CompletionItemKindFunction
119+
case ast.DeclExternType:
120+
kind = protocol.CompletionItemKindClass
121+
case ast.DeclField:
122+
kind = protocol.CompletionItemKindField
123+
case ast.DeclFunc:
124+
kind = protocol.CompletionItemKindFunction
125+
case ast.DeclImport:
126+
kind = protocol.CompletionItemKindModule
127+
case ast.DeclImportMember:
128+
kind = protocol.CompletionItemKindValue
129+
case ast.DeclModule:
130+
kind = protocol.CompletionItemKindModule
131+
case ast.DeclParameter:
132+
kind = protocol.CompletionItemKindVariable
133+
default:
134+
kind = protocol.CompletionItemKindText
135+
}
136+
return &kind
137+
}

langsrv/handler-declaration.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,22 @@ func textDocumentDeclaration(context *glsp.Context, params *protocol.Declaration
1515
if err != nil {
1616
return nil, nil
1717
}
18-
for _, decl := range sourceFile.Declarations {
18+
19+
globalDeclarations := sourceFile.Declarations
20+
for _, sameModuleFile := range rc.textDocumentEntry.module.Files {
21+
fileUrl := "file://" + sameModuleFile
22+
if rc.item.URI == fileUrl {
23+
continue
24+
}
25+
docEntry := langserver.documentCache.documents[fileUrl]
26+
if docEntry == nil || docEntry.sourceFile == nil {
27+
continue
28+
}
29+
30+
globalDeclarations = append(globalDeclarations, docEntry.sourceFile.ExportedDeclarations()...)
31+
}
32+
33+
for _, decl := range globalDeclarations {
1934
if string(decl.DeclName()) != token || decl.Meta().Source == nil {
2035
continue
2136
}

langsrv/handler-definition.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,22 @@ func textDocumentDefinition(context *glsp.Context, params *protocol.DefinitionPa
1515
if err != nil && token == "" {
1616
return nil, nil
1717
}
18-
for _, decl := range sourceFile.Declarations {
18+
19+
globalDeclarations := sourceFile.Declarations
20+
for _, sameModuleFile := range rc.textDocumentEntry.module.Files {
21+
fileUrl := "file://" + sameModuleFile
22+
if rc.item.URI == fileUrl {
23+
continue
24+
}
25+
docEntry := langserver.documentCache.documents[fileUrl]
26+
if docEntry == nil || docEntry.sourceFile == nil {
27+
continue
28+
}
29+
30+
globalDeclarations = append(globalDeclarations, docEntry.sourceFile.ExportedDeclarations()...)
31+
}
32+
33+
for _, decl := range globalDeclarations {
1934
if string(decl.DeclName()) != token || decl.Meta().Source == nil {
2035
continue
2136
}

langsrv/handler-hover.go

+17-16
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package langsrv
22

33
import (
4-
"fmt"
5-
64
"github.com/tliron/glsp"
75
protocol "github.com/tliron/glsp/protocol_3_16"
8-
"github.com/vknabel/lithia/ast"
96
)
107

118
func textDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
@@ -18,24 +15,28 @@ func textDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*pr
1815
if err != nil && tokenRange == nil {
1916
return nil, nil
2017
}
21-
for _, decl := range sourceFile.Declarations {
22-
if string(decl.DeclName()) != name {
18+
19+
globalDeclarations := sourceFile.Declarations
20+
for _, sameModuleFile := range rc.textDocumentEntry.module.Files {
21+
fileUrl := "file://" + sameModuleFile
22+
if rc.item.URI == fileUrl {
2323
continue
2424
}
25-
var docs string
26-
if documented, ok := decl.(ast.Documented); ok {
27-
docs = documented.ProvidedDocs().Content + "\n\n"
25+
docEntry := langserver.documentCache.documents[fileUrl]
26+
if docEntry == nil || docEntry.sourceFile == nil {
27+
continue
2828
}
29-
var overview string
30-
if overviewable, ok := decl.(ast.Overviewable); ok {
31-
overview = "```lithia\n" + overviewable.DeclOverview() + "\n```\n\n"
29+
30+
globalDeclarations = append(globalDeclarations, docEntry.sourceFile.ExportedDeclarations()...)
31+
}
32+
33+
for _, decl := range globalDeclarations {
34+
if string(decl.DeclName()) != name {
35+
continue
3236
}
3337
return &protocol.Hover{
34-
Contents: protocol.MarkupContent{
35-
Kind: protocol.MarkupKindMarkdown,
36-
Value: overview + fmt.Sprintf("_module %s_\n\n", decl.Meta().ModuleName) + docs,
37-
},
38-
Range: tokenRange,
38+
Contents: documentationMarkupContentForDecl(decl),
39+
Range: tokenRange,
3940
}, nil
4041
}
4142
return nil, nil

resolution/module-resolver.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type ModuleResolver struct {
1717
manifestName string
1818
manifestSearchPaths []string
1919
defaultSrcDir string
20+
lithiaSourceGlob string
2021
}
2122

2223
func DefaultModuleResolver() ModuleResolver {
@@ -26,6 +27,7 @@ func DefaultModuleResolver() ModuleResolver {
2627
manifestName: "Potfile",
2728
manifestSearchPaths: []string{".", "..", "../..", "../../..", "../../../.."},
2829
defaultSrcDir: "src",
30+
lithiaSourceGlob: "*.lithia",
2931
}
3032
}
3133

@@ -105,7 +107,7 @@ func (mr *ModuleResolver) ResolvePackageAndModuleForReferenceFile(referenceFile
105107
return mr.CreateSingleFileModule(pkg, referenceFile)
106108
}
107109
moduleFilepath := filepath.Dir(relativeFile)
108-
moduleParts := filepath.SplitList(moduleFilepath)
110+
moduleParts := strings.Split(moduleFilepath, string(filepath.Separator))
109111
resolvedModule, err := mr.resolveModuleWithinPackage(pkg, moduleParts)
110112
if err != nil {
111113
return mr.CreateSingleFileModule(pkg, referenceFile)
@@ -163,15 +165,15 @@ func (mr *ModuleResolver) ResolveModuleFromPackage(pkg ResolvedPackage, moduleNa
163165

164166
func (mr *ModuleResolver) resolveModuleWithinPackage(pkg ResolvedPackage, moduleParts []string) (ResolvedModule, error) {
165167
if len(moduleParts) == 0 {
166-
files, err := filepath.Glob(path.Join(pkg.Path, mr.defaultSrcDir, "*.lithia"))
168+
files, err := filepath.Glob(path.Join(pkg.Path, mr.defaultSrcDir, mr.lithiaSourceGlob))
167169
if len(files) > 0 {
168170
return ResolvedModule{
169171
packageRef: &pkg,
170172
Path: path.Join(pkg.Path, mr.defaultSrcDir),
171173
Files: files,
172174
}, err
173175
}
174-
files, err = filepath.Glob(path.Join(pkg.Path, "*.lithia"))
176+
files, err = filepath.Glob(path.Join(pkg.Path, mr.lithiaSourceGlob))
175177
return ResolvedModule{
176178
packageRef: &pkg,
177179
Path: pkg.Path,
@@ -180,7 +182,7 @@ func (mr *ModuleResolver) resolveModuleWithinPackage(pkg ResolvedPackage, module
180182
}
181183
pathElems := append([]string{pkg.Path}, moduleParts...)
182184
modulePath := path.Join(pathElems...)
183-
files, err := filepath.Glob(path.Join(modulePath, "*.lithia"))
185+
files, err := filepath.Glob(path.Join(modulePath, mr.lithiaSourceGlob))
184186
return ResolvedModule{
185187
packageRef: &pkg,
186188
relativeName: ast.ModuleName(strings.Join(moduleParts, ".")),

0 commit comments

Comments
 (0)