internal/template: identify template files by the templateExtensions option
Make the language id (sent from the client) 'gotmpl' equivalent to 'tmpl'
Wherever a view is known, use its options to determine which files
are template files. Whenever the client sends an explicit
languageID, use that.
Partially fixes golang/vscode-go#1957
Change-Id: I04cd630d6c6c80e0a78c2fafb6ddc1166ce86829
Reviewed-on: https://go-review.googlesource.com/c/tools/+/376854
Trust: Peter Weinberger <pjw@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/internal/lsp/cache/cache.go b/internal/lsp/cache/cache.go
index be03e63..ac670b5 100644
--- a/internal/lsp/cache/cache.go
+++ b/internal/lsp/cache/cache.go
@@ -168,10 +168,6 @@
return h.uri
}
-func (h *fileHandle) Kind() source.FileKind {
- return source.DetectLanguage("", h.uri.Filename())
-}
-
func (h *fileHandle) Hash() string {
return h.hash
}
@@ -180,7 +176,6 @@
return source.FileIdentity{
URI: h.uri,
Hash: h.hash,
- Kind: h.Kind(),
}
}
diff --git a/internal/lsp/cache/load.go b/internal/lsp/cache/load.go
index c5b5a3d..84e8245 100644
--- a/internal/lsp/cache/load.go
+++ b/internal/lsp/cache/load.go
@@ -57,7 +57,7 @@
uri := span.URI(scope)
// Don't try to load a file that doesn't exist.
fh := s.FindFile(uri)
- if fh == nil || fh.Kind() != source.Go {
+ if fh == nil || s.View().FileKind(uri) != source.Go {
continue
}
query = append(query, fmt.Sprintf("file=%s", uri.Filename()))
@@ -264,7 +264,7 @@
for _, fh := range files {
// Place the diagnostics on the package or module declarations.
var rng protocol.Range
- switch fh.Kind() {
+ switch s.view.FileKind(fh.URI()) {
case source.Go:
if pgf, err := s.ParseGo(ctx, fh, source.ParseHeader); err == nil {
pkgDecl := span.NewRange(s.FileSet(), pgf.File.Package, pgf.File.Name.End())
diff --git a/internal/lsp/cache/mod.go b/internal/lsp/cache/mod.go
index a915d05..f881a02 100644
--- a/internal/lsp/cache/mod.go
+++ b/internal/lsp/cache/mod.go
@@ -164,7 +164,7 @@
}
func (s *snapshot) ModWhy(ctx context.Context, fh source.FileHandle) (map[string]string, error) {
- if fh.Kind() != source.Mod {
+ if s.View().FileKind(fh.URI()) != source.Mod {
return nil, fmt.Errorf("%s is not a go.mod file", fh.URI())
}
if handle := s.getModWhyHandle(fh.URI()); handle != nil {
diff --git a/internal/lsp/cache/parse.go b/internal/lsp/cache/parse.go
index 0de0f70..e761373 100644
--- a/internal/lsp/cache/parse.go
+++ b/internal/lsp/cache/parse.go
@@ -13,6 +13,7 @@
"go/scanner"
"go/token"
"go/types"
+ "path/filepath"
"reflect"
"strconv"
"strings"
@@ -246,7 +247,8 @@
ctx, done := event.Start(ctx, "cache.parseGo", tag.File.Of(fh.URI().Filename()))
defer done()
- if fh.Kind() != source.Go {
+ ext := filepath.Ext(fh.URI().Filename())
+ if ext != ".go" && ext != "" { // files generated by cgo have no extension
return &parseGoData{err: errors.Errorf("cannot parse non-Go file %s", fh.URI())}
}
src, err := fh.Read()
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index a65b8fe..e906ecc 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -62,7 +62,6 @@
return source.FileIdentity{
URI: o.uri,
Hash: o.hash,
- Kind: o.kind,
}
}
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index 53f97f4..1b4ef46 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -159,7 +159,8 @@
}
func (s *snapshot) Templates() map[span.URI]source.VersionedFileHandle {
- if len(s.view.Options().TemplateExtensions) == 0 {
+ opts := s.view.Options().TemplateExtensions
+ if len(opts) == 0 {
return nil
}
@@ -168,8 +169,18 @@
s.mu.Lock()
defer s.mu.Unlock()
+ isin := func(s string, a []string) bool {
+ for _, x := range a {
+ if x == s || "."+x == s {
+ return true
+ }
+ }
+ return false
+ }
+
for k, x := range s.files {
- if strings.HasSuffix(filepath.Ext(k.Filename()), "tmpl") {
+ suffix := filepath.Ext(k.Filename())
+ if isin(suffix, opts) {
ans[k] = x
}
}
@@ -516,8 +527,8 @@
if err != nil {
return nil, err
}
- if fh.Kind() != source.Go {
- return nil, fmt.Errorf("no packages for non-Go file %s", uri)
+ if kind := s.view.FileKind(fh.FileIdentity().URI); kind != source.Go {
+ return nil, fmt.Errorf("no packages for non-Go file %s (%v)", uri, kind)
}
knownIDs, err := s.getOrLoadIDsForURI(ctx, uri)
if err != nil {
@@ -780,7 +791,7 @@
return s.workspacePackages[id]
}
-const fileExtensions = "go,mod,sum,work,tmpl"
+const fileExtensions = "go,mod,sum,work"
func (s *snapshot) fileWatchingGlobPatterns(ctx context.Context) map[string]struct{} {
extensions := fileExtensions
@@ -1574,7 +1585,7 @@
var files []source.VersionedFileHandle
for uri, fh := range s.files {
// Don't try to reload metadata for go.mod files.
- if fh.Kind() != source.Go {
+ if s.view.FileKind(uri) != source.Go {
continue
}
// If the URI doesn't belong to this view, then it's not in a workspace
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index fcff02a..b4fe38e 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -241,6 +241,26 @@
return v.options
}
+func (v *View) FileKind(URI span.URI) source.FileKind {
+ got := filepath.Ext(URI.Filename())
+ switch got {
+ case ".go":
+ return source.Go
+ case ".mod":
+ return source.Mod
+ case ".sum":
+ return source.Sum
+ }
+ exts := v.Options().TemplateExtensions
+ for _, ext := range exts {
+ if got == ext || got == "."+ext {
+ return source.Tmpl
+ }
+ }
+ // and now what? This should never happen, but it does for cgo before go1.15
+ return source.Go
+}
+
func minorOptionsChange(a, b *source.Options) bool {
// Check if any of the settings that modify our understanding of files have been changed
if !reflect.DeepEqual(a.Env, b.Env) {
diff --git a/internal/lsp/cmd/cmd.go b/internal/lsp/cmd/cmd.go
index 8245e12..06a33a1 100644
--- a/internal/lsp/cmd/cmd.go
+++ b/internal/lsp/cmd/cmd.go
@@ -497,7 +497,7 @@
p := &protocol.DidOpenTextDocumentParams{
TextDocument: protocol.TextDocumentItem{
URI: protocol.URIFromSpanURI(uri),
- LanguageID: source.DetectLanguage("", file.uri.Filename()).String(),
+ LanguageID: "go",
Version: 1,
Text: string(file.mapper.Content),
},
diff --git a/internal/lsp/code_action.go b/internal/lsp/code_action.go
index 526d279..45d2d6e 100644
--- a/internal/lsp/code_action.go
+++ b/internal/lsp/code_action.go
@@ -30,9 +30,10 @@
uri := fh.URI()
// Determine the supported actions for this file kind.
- supportedCodeActions, ok := snapshot.View().Options().SupportedCodeActions[fh.Kind()]
+ kind := snapshot.View().FileKind(uri)
+ supportedCodeActions, ok := snapshot.View().Options().SupportedCodeActions[kind]
if !ok {
- return nil, fmt.Errorf("no supported code actions for %v file kind", fh.Kind())
+ return nil, fmt.Errorf("no supported code actions for %v file kind", kind)
}
// The Only field of the context specifies which code actions the client wants.
@@ -67,7 +68,7 @@
}
var codeActions []protocol.CodeAction
- switch fh.Kind() {
+ switch kind {
case source.Mod:
if diagnostics := params.Context.Diagnostics; len(diagnostics) > 0 {
diags, err := mod.DiagnosticsForMod(ctx, snapshot, fh)
diff --git a/internal/lsp/code_lens.go b/internal/lsp/code_lens.go
index 6e371fc..345a21d 100644
--- a/internal/lsp/code_lens.go
+++ b/internal/lsp/code_lens.go
@@ -23,7 +23,7 @@
return nil, err
}
var lenses map[command.Command]source.LensFunc
- switch fh.Kind() {
+ switch snapshot.View().FileKind(fh.URI()) {
case source.Mod:
lenses = mod.LensFuncs()
case source.Go:
diff --git a/internal/lsp/completion.go b/internal/lsp/completion.go
index 4523d34..4e90405 100644
--- a/internal/lsp/completion.go
+++ b/internal/lsp/completion.go
@@ -27,7 +27,7 @@
}
var candidates []completion.CompletionItem
var surrounding *completion.Selection
- switch fh.Kind() {
+ switch snapshot.View().FileKind(fh.URI()) {
case source.Go:
candidates, surrounding, err = completion.Completion(ctx, snapshot, fh, params.Position, params.Context)
case source.Mod:
diff --git a/internal/lsp/definition.go b/internal/lsp/definition.go
index ab4aaed..b55f26d 100644
--- a/internal/lsp/definition.go
+++ b/internal/lsp/definition.go
@@ -18,7 +18,7 @@
if !ok {
return nil, err
}
- if fh.Kind() == source.Tmpl {
+ if snapshot.View().FileKind(fh.URI()) == source.Tmpl {
return template.Definition(snapshot, fh, params.Position)
}
ident, err := source.Identifier(ctx, snapshot, fh, params.Position)
diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go
index f9e1a47..64379af 100644
--- a/internal/lsp/diagnostics.go
+++ b/internal/lsp/diagnostics.go
@@ -416,7 +416,7 @@
// If they cannot and the workspace is not otherwise unloaded, it also surfaces
// a warning, suggesting that the user check the file for build tags.
func (s *Server) checkForOrphanedFile(ctx context.Context, snapshot source.Snapshot, fh source.VersionedFileHandle) *source.Diagnostic {
- if fh.Kind() != source.Go {
+ if snapshot.View().FileKind(fh.URI()) != source.Go {
return nil
}
// builtin files won't have a package, but they are never orphaned.
diff --git a/internal/lsp/format.go b/internal/lsp/format.go
index 62b25d8..467bcce 100644
--- a/internal/lsp/format.go
+++ b/internal/lsp/format.go
@@ -18,7 +18,7 @@
if !ok {
return nil, err
}
- switch fh.Kind() {
+ switch snapshot.View().FileKind(fh.URI()) {
case source.Mod:
return mod.Format(ctx, snapshot, fh)
case source.Go:
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index a946c80..7f23893 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -466,7 +466,8 @@
release()
return nil, nil, false, func() {}, err
}
- if expectKind != source.UnknownKind && fh.Kind() != expectKind {
+ kind := snapshot.View().FileKind(fh.URI())
+ if expectKind != source.UnknownKind && kind != expectKind {
// Wrong kind of file. Nothing to do.
release()
return nil, nil, false, func() {}, nil
diff --git a/internal/lsp/highlight.go b/internal/lsp/highlight.go
index a350dd5..71bd7d9 100644
--- a/internal/lsp/highlight.go
+++ b/internal/lsp/highlight.go
@@ -21,7 +21,7 @@
return nil, err
}
- if fh.Kind() == source.Tmpl {
+ if snapshot.View().FileKind(fh.URI()) == source.Tmpl {
return template.Highlight(ctx, snapshot, fh, params.Position)
}
diff --git a/internal/lsp/hover.go b/internal/lsp/hover.go
index 1e118bc..9125c23 100644
--- a/internal/lsp/hover.go
+++ b/internal/lsp/hover.go
@@ -19,7 +19,7 @@
if !ok {
return nil, err
}
- switch fh.Kind() {
+ switch snapshot.View().FileKind(fh.URI()) {
case source.Mod:
return mod.Hover(ctx, snapshot, fh, params.Position)
case source.Go:
diff --git a/internal/lsp/link.go b/internal/lsp/link.go
index 87692fa..d76f1d0 100644
--- a/internal/lsp/link.go
+++ b/internal/lsp/link.go
@@ -30,7 +30,7 @@
if !ok {
return nil, err
}
- switch fh.Kind() {
+ switch snapshot.View().FileKind(fh.URI()) {
case source.Mod:
links, err = modLinks(ctx, snapshot, fh)
case source.Go:
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index 8a66730..ccfe2c9 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -71,8 +71,7 @@
var modifications []source.FileModification
for filename, content := range datum.Config.Overlay {
- kind := source.DetectLanguage("", filename)
- if kind != source.Go {
+ if filepath.Ext(filename) != ".go" {
continue
}
modifications = append(modifications, source.FileModification{
@@ -187,7 +186,7 @@
}
func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens) {
- if source.DetectLanguage("", uri.Filename()) != source.Mod {
+ if !strings.HasSuffix(uri.Filename(), "go.mod") {
return
}
got, err := r.server.codeLens(r.ctx, &protocol.CodeLensParams{
diff --git a/internal/lsp/references.go b/internal/lsp/references.go
index d8f2f1e..b5bb00f 100644
--- a/internal/lsp/references.go
+++ b/internal/lsp/references.go
@@ -18,7 +18,7 @@
if !ok {
return nil, err
}
- if fh.Kind() == source.Tmpl {
+ if snapshot.View().FileKind(fh.URI()) == source.Tmpl {
return template.References(ctx, snapshot, fh, params)
}
references, err := source.References(ctx, snapshot, fh, params.Position, params.Context.IncludeDeclaration)
diff --git a/internal/lsp/semantic.go b/internal/lsp/semantic.go
index c3ea15c..3925ce3 100644
--- a/internal/lsp/semantic.go
+++ b/internal/lsp/semantic.go
@@ -70,7 +70,8 @@
// the client won't remember the wrong answer
return nil, errors.Errorf("semantictokens are disabled")
}
- if fh.Kind() == source.Tmpl {
+ kind := snapshot.View().FileKind(fh.URI())
+ if kind == source.Tmpl {
// this is a little cumbersome to avoid both exporting 'encoded' and its methods
// and to avoid import cycles
e := &encoded{
@@ -87,7 +88,7 @@
}
return template.SemanticTokens(ctx, snapshot, fh.URI(), add, data)
}
- if fh.Kind() != source.Go {
+ if kind != source.Go {
return nil, nil
}
pkg, err := snapshot.PackageForFile(ctx, fh.URI(), source.TypecheckFull, source.WidestPackage)
diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go
index d0e6b8a..4a2c43f 100644
--- a/internal/lsp/source/source_test.go
+++ b/internal/lsp/source/source_test.go
@@ -66,8 +66,7 @@
var modifications []source.FileModification
for filename, content := range datum.Config.Overlay {
- kind := source.DetectLanguage("", filename)
- if kind != source.Go {
+ if filepath.Ext(filename) != ".go" {
continue
}
modifications = append(modifications, source.FileModification{
diff --git a/internal/lsp/source/util.go b/internal/lsp/source/util.go
index 9500eee..c9b4878 100644
--- a/internal/lsp/source/util.go
+++ b/internal/lsp/source/util.go
@@ -162,34 +162,39 @@
var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`)
func DetectLanguage(langID, filename string) FileKind {
- switch langID {
- case "go":
- return Go
- case "go.mod":
- return Mod
- case "go.sum":
- return Sum
- case "tmpl":
- return Tmpl
+ // use the langID if the client sent it
+ if langID != "" {
+ switch langID {
+ case "go":
+ return Go
+ case "go.mod":
+ return Mod
+ case "go.sum":
+ return Sum
+ case "tmpl", "gotmpl":
+ return Tmpl
+ default:
+ return UnknownKind
+ }
}
- // Fallback to detecting the language based on the file extension.
+ // Detect the language based on the file extension.
switch ext := filepath.Ext(filename); ext {
case ".mod":
return Mod
case ".sum":
return Sum
+ case ".go":
+ return Go
default:
- if strings.HasSuffix(ext, "tmpl") {
- // .tmpl, .gotmpl, etc
- return Tmpl
- }
- // It's a Go file, or we shouldn't be seeing it
+ // (for instance, before go1.15 cgo files had no extension)
return Go
}
}
func (k FileKind) String() string {
switch k {
+ case Go:
+ return "go"
case Mod:
return "go.mod"
case Sum:
@@ -197,7 +202,7 @@
case Tmpl:
return "tmpl"
default:
- return "go"
+ return fmt.Sprintf("unk%d", k)
}
}
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index 2285e5a..5064856 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -266,6 +266,9 @@
// RegisterModuleUpgrades registers that upgrades exist for the given modules.
RegisterModuleUpgrades(upgrades map[string]string)
+
+ // FileKind returns the type of a file
+ FileKind(uri span.URI) FileKind
}
// A FileSource maps uris to FileHandles. This abstraction exists both for
@@ -498,7 +501,6 @@
// FileHandle represents a handle to a specific version of a single file.
type FileHandle interface {
URI() span.URI
- Kind() FileKind
// FileIdentity returns a FileIdentity for the file, even if there was an
// error reading it.
@@ -516,17 +518,14 @@
// Identifier represents a unique identifier for the file's content.
Hash string
-
- // Kind is the file's kind.
- Kind FileKind
}
func (id FileIdentity) String() string {
- return fmt.Sprintf("%s%s%s", id.URI, id.Hash, id.Kind)
+ return fmt.Sprintf("%s%s", id.URI, id.Hash)
}
// FileKind describes the kind of the file in question.
-// It can be one of Go, mod, or sum.
+// It can be one of Go,mod, Sum, or Tmpl.
type FileKind int
const (
diff --git a/internal/lsp/symbols.go b/internal/lsp/symbols.go
index 5bde1bd..49566e0 100644
--- a/internal/lsp/symbols.go
+++ b/internal/lsp/symbols.go
@@ -24,7 +24,7 @@
return []interface{}{}, err
}
var docSymbols []protocol.DocumentSymbol
- if fh.Kind() == source.Tmpl {
+ if snapshot.View().FileKind(fh.URI()) == source.Tmpl {
docSymbols, err = template.DocumentSymbols(snapshot, fh)
} else {
docSymbols, err = source.DocumentSymbols(ctx, snapshot, fh)
diff --git a/internal/lsp/workspace.go b/internal/lsp/workspace.go
index 02feae5..c239942 100644
--- a/internal/lsp/workspace.go
+++ b/internal/lsp/workspace.go
@@ -79,8 +79,8 @@
if err != nil {
return err
}
- snapshot, release := view.Snapshot(ctx)
go func() {
+ snapshot, release := view.Snapshot(ctx)
defer release()
s.diagnoseDetached(snapshot)
}()