internal/lsp: extract view to its own package
This allows us to write the lsp verbs in terms of a stable underlying source
management layer.
This should make it easier to refactor the underlying layer to add more powerful
caching and incremental modes as we go.
Change-Id: Iab97b061d80394a6fa6748a93a4c68f2deb46129
Reviewed-on: https://go-review.googlesource.com/c/147201
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go
index c4dbb88..94e8ff1 100644
--- a/internal/lsp/diagnostics.go
+++ b/internal/lsp/diagnostics.go
@@ -11,10 +11,11 @@
"golang.org/x/tools/go/packages"
"golang.org/x/tools/internal/lsp/protocol"
+ "golang.org/x/tools/internal/lsp/source"
)
-func (v *view) diagnostics(uri protocol.DocumentURI) (map[string][]protocol.Diagnostic, error) {
- pkg, err := v.typeCheck(uri)
+func diagnostics(v *source.View, uri protocol.DocumentURI) (map[string][]protocol.Diagnostic, error) {
+ pkg, err := v.TypeCheck(uri)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/diagnostics_test.go b/internal/lsp/diagnostics_test.go
index e547c6b..093bfc3 100644
--- a/internal/lsp/diagnostics_test.go
+++ b/internal/lsp/diagnostics_test.go
@@ -15,6 +15,7 @@
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/packages/packagestest"
"golang.org/x/tools/internal/lsp/protocol"
+ "golang.org/x/tools/internal/lsp/source"
)
func TestDiagnostics(t *testing.T) {
@@ -84,12 +85,12 @@
if err != nil {
t.Fatal(err)
}
- v := newView()
- v.config = exported.Config
- v.config.Mode = packages.LoadSyntax
+ v := source.NewView()
+ v.Config = exported.Config
+ v.Config.Mode = packages.LoadSyntax
for _, pkg := range pkgs {
for _, filename := range pkg.GoFiles {
- diagnostics, err := v.diagnostics(filenameToURI(filename))
+ diagnostics, err := diagnostics(v, source.ToURI(filename))
if err != nil {
t.Fatal(err)
}
diff --git a/internal/lsp/format.go b/internal/lsp/format.go
index c78cf36..9bfdb65 100644
--- a/internal/lsp/format.go
+++ b/internal/lsp/format.go
@@ -10,11 +10,12 @@
"go/format"
"golang.org/x/tools/internal/lsp/protocol"
+ "golang.org/x/tools/internal/lsp/source"
)
-// format formats a document with a given range.
-func (s *server) format(uri protocol.DocumentURI, rng *protocol.Range) ([]protocol.TextEdit, error) {
- data, err := s.readActiveFile(uri)
+// formatRange formats a document with a given range.
+func formatRange(v *source.View, uri protocol.DocumentURI, rng *protocol.Range) ([]protocol.TextEdit, error) {
+ data, err := v.ReadActiveFile(uri)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index 58cecde..63c403e 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -11,6 +11,7 @@
"golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp/protocol"
+ "golang.org/x/tools/internal/lsp/source"
)
// RunServer starts an LSP server on the supplied stream, and waits until the
@@ -28,7 +29,7 @@
initializedMu sync.Mutex
initialized bool // set once the server has received "initialize" request
- *view
+ view *source.View
}
func (s *server) Initialize(ctx context.Context, params *protocol.InitializeParams) (*protocol.InitializeResult, error) {
@@ -37,7 +38,7 @@
if s.initialized {
return nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidRequest, "server already initialized")
}
- s.view = newView()
+ s.view = source.NewView()
s.initialized = true
return &protocol.InitializeResult{
Capabilities: protocol.ServerCapabilities{
@@ -110,15 +111,13 @@
}
func (s *server) cacheAndDiagnoseFile(ctx context.Context, uri protocol.DocumentURI, text string) {
- s.view.activeFilesMu.Lock()
- s.view.activeFiles[uri] = []byte(text)
- s.view.activeFilesMu.Unlock()
+ s.view.SetActiveFileContent(uri, []byte(text))
go func() {
- reports, err := s.diagnostics(uri)
+ reports, err := diagnostics(s.view, uri)
if err == nil {
for filename, diagnostics := range reports {
s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
- URI: filenameToURI(filename),
+ URI: source.ToURI(filename),
Diagnostics: diagnostics,
})
}
@@ -140,7 +139,7 @@
}
func (s *server) DidClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error {
- s.clearActiveFile(params.TextDocument.URI)
+ s.view.ClearActiveFile(params.TextDocument.URI)
return nil
}
@@ -213,11 +212,11 @@
}
func (s *server) Formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
- return s.format(params.TextDocument.URI, nil)
+ return formatRange(s.view, params.TextDocument.URI, nil)
}
func (s *server) RangeFormatting(ctx context.Context, params *protocol.DocumentRangeFormattingParams) ([]protocol.TextEdit, error) {
- return s.format(params.TextDocument.URI, ¶ms.Range)
+ return formatRange(s.view, params.TextDocument.URI, ¶ms.Range)
}
func (s *server) OnTypeFormatting(context.Context, *protocol.DocumentOnTypeFormattingParams) ([]protocol.TextEdit, error) {
diff --git a/internal/lsp/source/uri.go b/internal/lsp/source/uri.go
new file mode 100644
index 0000000..c6b9d8f
--- /dev/null
+++ b/internal/lsp/source/uri.go
@@ -0,0 +1,32 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package source
+
+import (
+ "fmt"
+ "path/filepath"
+ "strings"
+
+ "golang.org/x/tools/internal/lsp/protocol"
+)
+
+const fileSchemePrefix = "file://"
+
+// FromURI gets the file path for a given URI.
+// It will return an error if the uri is not valid, or if the URI was not
+// a file URI
+func FromURI(uri protocol.DocumentURI) (string, error) {
+ s := string(uri)
+ if !strings.HasPrefix(s, fileSchemePrefix) {
+ return "", fmt.Errorf("only file URI's are supported, got %v", uri)
+ }
+ return filepath.FromSlash(s[len(fileSchemePrefix):]), nil
+}
+
+// ToURI returns a protocol URI for the supplied path.
+// It will always have the file scheme.
+func ToURI(path string) protocol.DocumentURI {
+ return protocol.DocumentURI(fileSchemePrefix + filepath.ToSlash(path))
+}
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
new file mode 100644
index 0000000..27fb61b
--- /dev/null
+++ b/internal/lsp/source/view.go
@@ -0,0 +1,88 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package source
+
+import (
+ "fmt"
+ "go/token"
+ "sync"
+
+ "golang.org/x/tools/go/packages"
+ "golang.org/x/tools/internal/lsp/protocol"
+)
+
+type View struct {
+ Config *packages.Config
+
+ activeFilesMu sync.Mutex
+ activeFiles map[protocol.DocumentURI][]byte
+
+ fset *token.FileSet
+}
+
+func NewView() *View {
+ fset := token.NewFileSet()
+ return &View{
+ Config: &packages.Config{
+ Mode: packages.LoadSyntax,
+ Fset: fset,
+ Tests: true,
+ },
+ activeFiles: make(map[protocol.DocumentURI][]byte),
+ fset: fset,
+ }
+}
+
+func (v *View) overlay() map[string][]byte {
+ over := make(map[string][]byte)
+
+ v.activeFilesMu.Lock()
+ defer v.activeFilesMu.Unlock()
+
+ for uri, content := range v.activeFiles {
+ filename, err := FromURI(uri)
+ if err == nil {
+ over[filename] = content
+ }
+ }
+ return over
+}
+
+func (v *View) SetActiveFileContent(uri protocol.DocumentURI, content []byte) {
+ v.activeFilesMu.Lock()
+ v.activeFiles[uri] = content
+ v.activeFilesMu.Unlock()
+}
+
+func (v *View) ReadActiveFile(uri protocol.DocumentURI) ([]byte, error) {
+ v.activeFilesMu.Lock()
+ content, ok := v.activeFiles[uri]
+ v.activeFilesMu.Unlock()
+ if !ok {
+ return nil, fmt.Errorf("uri not found: %s", uri)
+ }
+ return content, nil
+}
+
+func (v *View) ClearActiveFile(uri protocol.DocumentURI) {
+ v.activeFilesMu.Lock()
+ delete(v.activeFiles, uri)
+ v.activeFilesMu.Unlock()
+}
+
+// TypeCheck type-checks the package for the given package path.
+func (v *View) TypeCheck(uri protocol.DocumentURI) (*packages.Package, error) {
+ v.Config.Overlay = v.overlay()
+ path, err := FromURI(uri)
+ if err != nil {
+ return nil, err
+ }
+ pkgs, err := packages.Load(v.Config, fmt.Sprintf("file=%s", path))
+ if len(pkgs) == 0 {
+ return nil, err
+ }
+ pkg := pkgs[0]
+ return pkg, nil
+}
diff --git a/internal/lsp/view.go b/internal/lsp/view.go
deleted file mode 100644
index 77524d6..0000000
--- a/internal/lsp/view.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package lsp
-
-import (
- "fmt"
- "go/token"
- "strings"
- "sync"
-
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/internal/lsp/protocol"
-)
-
-type view struct {
- activeFilesMu sync.Mutex
- activeFiles map[protocol.DocumentURI][]byte
- config *packages.Config
-
- fset *token.FileSet
-}
-
-func newView() *view {
- fset := token.NewFileSet()
- return &view{
- config: &packages.Config{
- Mode: packages.LoadSyntax,
- Fset: fset,
- Tests: true,
- },
- activeFiles: make(map[protocol.DocumentURI][]byte),
- fset: fset,
- }
-}
-
-func (v *view) overlay() map[string][]byte {
- over := make(map[string][]byte)
-
- v.activeFilesMu.Lock()
- defer v.activeFilesMu.Unlock()
-
- for uri, content := range v.activeFiles {
- over[uriToFilename(uri)] = content
- }
- return over
-}
-
-func (v *view) readActiveFile(uri protocol.DocumentURI) ([]byte, error) {
- v.activeFilesMu.Lock()
- defer v.activeFilesMu.Unlock()
-
- content, ok := v.activeFiles[uri]
- if !ok {
- return nil, fmt.Errorf("file not found: %s", uri)
- }
- return content, nil
-}
-
-func (v *view) clearActiveFile(uri protocol.DocumentURI) {
- v.activeFilesMu.Lock()
- delete(v.activeFiles, uri)
- v.activeFilesMu.Unlock()
-}
-
-// typeCheck type-checks the package for the given package path.
-func (v *view) typeCheck(uri protocol.DocumentURI) (*packages.Package, error) {
- v.config.Overlay = v.overlay()
- pkgs, err := packages.Load(v.config, fmt.Sprintf("file=%s", uriToFilename(uri)))
- if len(pkgs) == 0 {
- if err == nil {
- err = fmt.Errorf("no packages found for %s", uri)
- }
- return nil, err
- }
- pkg := pkgs[0]
- return pkg, nil
-}
-
-func uriToFilename(uri protocol.DocumentURI) string {
- return strings.TrimPrefix(string(uri), "file://")
-}
-
-func filenameToURI(filename string) protocol.DocumentURI {
- return protocol.DocumentURI("file://" + filename)
-}