Skip to content

Commit b108bee

Browse files
committed
feat(lsp): completions for imports and fields #35
1 parent 444010f commit b108bee

File tree

7 files changed

+99
-10
lines changed

7 files changed

+99
-10
lines changed

.devcontainer/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.202.5/containers/go/.devcontainer/base.Dockerfile
22

33
# [Choice] Go version (use -bullseye variants on local arm64/Apple Silicon): 1, 1.16, 1.17, 1-bullseye, 1.16-bullseye, 1.17-bullseye, 1-buster, 1.16-buster, 1.17-buster
4-
ARG VARIANT="1.17-bullseye"
4+
ARG VARIANT="1.19-bullseye"
55
FROM mcr.microsoft.com/vscode/devcontainers/go:0-${VARIANT}
66

77
# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
- stdlib: new function `lists.prepend` for prepending lists
88
- fix: improved type switch error message
99
- fix: improved stdlib not found error message
10+
- lsp: complete importable modules #35
11+
- lsp: field completions #35
1012
- chore: bump dependencies
1113

1214
## v0.0.17

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.19
55
require (
66
github.com/smacker/go-tree-sitter v0.0.0-20221023091341-2009a4db91e4
77
github.com/spf13/cobra v1.6.1
8-
github.com/tliron/glsp v0.1.1
8+
github.com/tliron/glsp v0.1.2-0.20220804144236-0fe570f215a5
99
github.com/tliron/kutil v0.1.62
1010
github.com/vknabel/tree-sitter-lithia v0.2.2-0.20221001133044-2d70ec255b9f
1111
)

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
4747
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
4848
github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM=
4949
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
50-
github.com/tliron/glsp v0.1.1 h1:GNNgUX9p1Q9MoPQooJoZ0+WaLL03EkhcKZUYJAtiNqs=
51-
github.com/tliron/glsp v0.1.1/go.mod h1:RVyVKeY3U+Nlc3DRklUiaegNsQyjzNTEool6YWh1v7g=
50+
github.com/tliron/glsp v0.1.2-0.20220804144236-0fe570f215a5 h1:Qqqhhlk6YA+UsQCdWKStjHzK6UqMjblw5jZ0xJY2DLY=
51+
github.com/tliron/glsp v0.1.2-0.20220804144236-0fe570f215a5/go.mod h1:Hqz7gzsJnxJidRXTUsvqdmk1xeh4zdlB8B2Jv3244RU=
5252
github.com/tliron/kutil v0.1.62 h1:Nj4avenQO9t9QNaD6qXf1DsdyOjQrORIJjoc0FCnsBg=
5353
github.com/tliron/kutil v0.1.62/go.mod h1:Mo1pAtg/9yG3ClnUv32Hrl+t0BFFCg49RpCjHG3sY7c=
5454
github.com/vknabel/tree-sitter-lithia v0.2.2-0.20221001133044-2d70ec255b9f h1:6OyIrB3iRHe1zZq+VEBmxIk2WVTuNWdJrpoG91cThYU=

langsrv/handler-completion.go

+56
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,68 @@ import (
77
"github.com/tliron/glsp"
88
protocol "github.com/tliron/glsp/protocol_3_16"
99
"github.com/vknabel/lithia/ast"
10+
"github.com/vknabel/lithia/parser"
1011
)
1112

1213
func textDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (interface{}, error) {
1314
rc := NewReqContextAtPosition(&params.TextDocumentPositionParams)
1415

16+
targetNode, err := rc.findNode()
17+
if err != nil {
18+
return nil, err
19+
}
20+
1521
completionItems := []protocol.CompletionItem{}
22+
23+
switch targetNode.Type() {
24+
case parser.TYPE_NODE_IMPORT_DECLARATION:
25+
kind := protocol.CompletionItemKindModule
26+
mods, err := ls.resolver.ImportableModules(rc.module.Package())
27+
if err != nil {
28+
return nil, err
29+
}
30+
for _, mod := range mods {
31+
completionItems = append(completionItems, protocol.CompletionItem{
32+
Label: string(mod.RelativeName),
33+
Kind: &kind,
34+
Documentation: &protocol.MarkupContent{
35+
Kind: protocol.MarkupKindMarkdown,
36+
Value: fmt.Sprintf("`import %s`", mod.RelativeName),
37+
},
38+
})
39+
}
40+
return completionItems, nil
41+
case parser.TYPE_NODE_MEMBER_ACCESS, ".":
42+
decls := rc.accessibleDeclarations(context)
43+
44+
for _, imported := range decls {
45+
switch decl := imported.decl.(type) {
46+
case ast.DeclData:
47+
for _, field := range decl.Fields {
48+
insertText := string(field.DeclName())
49+
var detail string
50+
if imported.decl.Meta().ModuleName != "" {
51+
detail = string(imported.decl.Meta().ModuleName) +
52+
"." +
53+
string(imported.decl.DeclName()) +
54+
"." +
55+
string(field.DeclName())
56+
}
57+
58+
completionItems = append(completionItems, protocol.CompletionItem{
59+
Label: string(field.DeclName()),
60+
Kind: completionItemKindForDecl(imported.decl),
61+
InsertText: &insertText,
62+
Detail: &detail,
63+
CommitCharacters: []string{"."},
64+
Documentation: documentationMarkupContentForDecl(imported.decl),
65+
})
66+
}
67+
}
68+
}
69+
return completionItems, nil
70+
}
71+
1672
for _, imported := range rc.accessibleDeclarations(context) {
1773
insertText := insertTextForImportedDecl(imported)
1874
var detail string

langsrv/lang-server.go

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ func init() {
3838
Shutdown: shutdown,
3939
SetTrace: setTrace,
4040

41+
CancelRequest: func(context *glsp.Context, params *protocol.CancelParams) error { return nil },
42+
4143
TextDocumentDidOpen: textDocumentDidOpen,
4244
TextDocumentDidChange: textDocumentDidChange,
4345
TextDocumentDidClose: func(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error { return nil },

resolution/module-resolver.go

+35-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package resolution
22

33
import (
4+
"os"
45
"path"
56
"path/filepath"
67
"strings"
@@ -41,7 +42,7 @@ type ResolvedModule struct {
4142
packageRef *ResolvedPackage
4243
// all modules of this package are relative to this path
4344
// might contain the package manifest file
44-
relativeName ast.ModuleName
45+
RelativeName ast.ModuleName
4546
Path string
4647
Files []string
4748
}
@@ -83,8 +84,12 @@ func (mr *ModuleResolver) ResolvePackageForReferenceFile(referenceFile string) R
8384
manifestPath := filepath.Join(path.Dir(referenceFile), candidates, mr.manifestName)
8485
if _, err := world.Current.FS.Stat(manifestPath); err == nil {
8586
packagePath := path.Dir(manifestPath)
87+
packageName := path.Base(packagePath)
88+
if packageName == "/" || packageName == "." {
89+
packageName = mr.defaultPackageName
90+
}
8691
return ResolvedPackage{
87-
Name: mr.defaultPackageName,
92+
Name: packageName,
8893
Path: packagePath,
8994
Manifest: &PackageManifest{
9095
Path: manifestPath,
@@ -130,7 +135,7 @@ func (mr *ModuleResolver) CreateSingleFileModule(pkg ResolvedPackage, file strin
130135
uniform := strings.ReplaceAll(trimmed, ".", "_")
131136
return ResolvedModule{
132137
packageRef: &pkg,
133-
relativeName: ast.ModuleName(uniform),
138+
RelativeName: ast.ModuleName(uniform),
134139
Path: file,
135140
Files: []string{file},
136141
}
@@ -192,7 +197,7 @@ func (mr *ModuleResolver) resolveModuleWithinPackage(pkg ResolvedPackage, module
192197
files, err := world.Current.FS.Glob(path.Join(modulePath, mr.lithiaSourceGlob))
193198
return ResolvedModule{
194199
packageRef: &pkg,
195-
relativeName: ast.ModuleName(strings.Join(moduleParts, ".")),
200+
RelativeName: ast.ModuleName(strings.Join(moduleParts, ".")),
196201
Path: modulePath,
197202
Files: files,
198203
}, err
@@ -203,13 +208,37 @@ func (mod ResolvedModule) Package() ResolvedPackage {
203208
}
204209

205210
func (mod ResolvedModule) AbsoluteModuleName() ast.ModuleName {
206-
if mod.relativeName == "" {
211+
if mod.RelativeName == "" {
207212
return ast.ModuleName(mod.Package().Name)
208213
} else {
209-
return ast.ModuleName(mod.packageRef.Name) + "." + mod.relativeName
214+
return ast.ModuleName(mod.packageRef.Name) + "." + mod.RelativeName
210215
}
211216
}
212217

213218
func removingFilePrefix(str string) string {
214219
return strings.TrimPrefix(str, "file://")
215220
}
221+
222+
func (mr *ModuleResolver) ImportableModules(pkg ResolvedPackage) ([]ResolvedModule, error) {
223+
var modules []ResolvedModule
224+
225+
possibleModuleNames := make([]string, 0)
226+
for _, root := range mr.externalImportRoots {
227+
dirs, err := os.ReadDir(root)
228+
if err != nil {
229+
return nil, err
230+
}
231+
for _, dir := range dirs {
232+
if dir.IsDir() {
233+
possibleModuleNames = append(possibleModuleNames, dir.Name())
234+
}
235+
}
236+
}
237+
238+
for _, modName := range possibleModuleNames {
239+
mod := mr.ResolvePackageAndModuleForReferenceFile(modName)
240+
modules = append(modules, mod)
241+
}
242+
243+
return modules, nil
244+
}

0 commit comments

Comments
 (0)