Skip to content

Commit

Permalink
Merge pull request #35 from xushiwei/q
Browse files Browse the repository at this point in the history
lsp/definition
  • Loading branch information
xushiwei authored Oct 21, 2023
2 parents f1ee17a + 29dbc75 commit e975742
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 5 deletions.
2 changes: 1 addition & 1 deletion gopls/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.18

require (
github.com/google/go-cmp v0.5.9
github.com/goplus/gop v1.1.4-0.20231021132916-568cdda00461
github.com/goplus/gop v1.1.4-0.20231021204738-dc26c66d30f6
github.com/goplus/mod v0.11.8-0.20231019172744-da5848421263
github.com/jba/printsrc v0.2.2
github.com/jba/templatecheck v0.6.0
Expand Down
4 changes: 2 additions & 2 deletions gopls/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8=
github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
github.com/goplus/gop v1.1.4-0.20231021132916-568cdda00461 h1:CvURAOe1m28sY9L/rQKexc49oAoKfqBX6aJH/h7znm4=
github.com/goplus/gop v1.1.4-0.20231021132916-568cdda00461/go.mod h1:jfqgaSg3PBpKcatrfL9eJTnj/wAqS1UadGAd6WZPhow=
github.com/goplus/gop v1.1.4-0.20231021204738-dc26c66d30f6 h1:RH70+Sr7USKyJn04f5R3PkvcatCvFXjpUcFDtVnXJlE=
github.com/goplus/gop v1.1.4-0.20231021204738-dc26c66d30f6/go.mod h1:jfqgaSg3PBpKcatrfL9eJTnj/wAqS1UadGAd6WZPhow=
github.com/goplus/gox v1.12.2-0.20231020202641-5f657ff4e754 h1:uuXDqFfg4RhmHD7slw15hzwVQtJ51CsdcZn6gQtve/E=
github.com/goplus/gox v1.12.2-0.20231020202641-5f657ff4e754/go.mod h1:Ek1YIy3wRaZ1i0DD2XG29i3r5AFdhcOradK0/GGs1YQ=
github.com/goplus/mod v0.11.8-0.20231019172744-da5848421263 h1:PE0HveOss5mai9pa52L4/ZvVqZtltogJ9rIUIsdlG/I=
Expand Down
2 changes: 2 additions & 0 deletions gopls/internal/lsp/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ func (s *Server) definition(ctx context.Context, params *protocol.DefinitionPara
switch kind := snapshot.View().FileKind(fh); kind {
case source.Tmpl:
return template.Definition(snapshot, fh, params.Position)
case source.Gop: // goxls: Go+
return source.GopDefinition(ctx, snapshot, fh, params.Position)
case source.Go:
// Partial support for jumping from linkname directive (position at 2nd argument).
locations, err := source.LinknameDefinition(ctx, snapshot, fh, params.Position)
Expand Down
5 changes: 5 additions & 0 deletions gopls/internal/lsp/definition_gox.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright 2023 The GoPlus Authors (goplus.org). 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
2 changes: 0 additions & 2 deletions gopls/internal/lsp/source/completion/definition_gox.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

package completion

/*
import (
"go/types"
"strings"
Expand Down Expand Up @@ -98,4 +97,3 @@ func gopDefinition(path []ast.Node, obj types.Object, pgf *source.ParsedGopFile)
}
return ans, sel
}
*/
197 changes: 197 additions & 0 deletions gopls/internal/lsp/source/definition_gox.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,133 @@
package source

import (
"context"
"fmt"
"go/types"
"log"

"github.com/goplus/gop/ast"
"github.com/goplus/gop/token"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/goxls/parserutil"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/internal/event"
)

// GopDefinition handles the textDocument/definition request for Go files.
func GopDefinition(ctx context.Context, snapshot Snapshot, fh FileHandle, position protocol.Position) ([]protocol.Location, error) {
ctx, done := event.Start(ctx, "source.GopDefinition")
defer done()

pkg, pgf, err := NarrowestPackageForGopFile(ctx, snapshot, fh.URI())
if err != nil {
return nil, err
}
pos, err := pgf.PositionPos(position)
if err != nil {
return nil, err
}

// Handle the case where the cursor is in an import.
importLocations, err := gopImportDefinition(ctx, snapshot, pkg, pgf, pos)
if err != nil {
return nil, err
}
if len(importLocations) > 0 {
return importLocations, nil
}

// Handle the case where the cursor is in the package name.
// We use "<= End" to accept a query immediately after the package name.
// goxls: a Go+ file maybe no `package xxx`
// if pgf.File != nil && pgf.File.Name.Pos() <= pos && pos <= pgf.File.Name.End() {
if pgf.File != nil && pgf.HasPkgDecl() && pgf.File.Name.Pos() <= pos && pos <= pgf.File.Name.End() {
for _, pgf := range pkg.CompiledGoFiles() {
if pgf.File.Name != nil && pgf.File.Doc != nil {
loc, err := pgf.NodeLocation(pgf.File.Name)
if err != nil {
return nil, err
}
return []protocol.Location{loc}, nil
}
}
// If there's no package documentation, just use current file.
declFile := pgf
for _, pgf := range pkg.CompiledGopFiles() {
if pgf.HasPkgDecl() && pgf.File.Doc != nil {
declFile = pgf
break
}
}
loc, err := declFile.NodeLocation(declFile.File.Name)
if err != nil {
return nil, err
}
return []protocol.Location{loc}, nil
}

// The general case: the cursor is on an identifier.
_, obj, _ := gopReferencedObject(pkg, pgf, pos)
if obj == nil {
return nil, nil
}

// Handle objects with no position: builtin, unsafe.
if !obj.Pos().IsValid() {
var pgf *ParsedGoFile
if obj.Parent() == types.Universe {
// pseudo-package "builtin"
builtinPGF, err := snapshot.BuiltinFile(ctx)
if err != nil {
return nil, err
}
pgf = builtinPGF

} else if obj.Pkg() == types.Unsafe {
// package "unsafe"
unsafe := snapshot.Metadata("unsafe")
if unsafe == nil {
return nil, fmt.Errorf("no metadata for package 'unsafe'")
}
uri := unsafe.GoFiles[0]
fh, err := snapshot.ReadFile(ctx, uri)
if err != nil {
return nil, err
}
pgf, err = snapshot.ParseGo(ctx, fh, ParseFull&^SkipObjectResolution)
if err != nil {
return nil, err
}
} else {
return nil, bug.Errorf("internal error: no position for %v", obj.Name())
}
// Inv: pgf ∈ {builtin,unsafe}.go

// Use legacy (go/ast) object resolution.
astObj := pgf.File.Scope.Lookup(obj.Name())
if astObj == nil {
// Every built-in should have documentation syntax.
return nil, bug.Errorf("internal error: no object for %s", obj.Name())
}
decl, ok := astObj.Decl.(ast.Node)
if !ok {
return nil, bug.Errorf("internal error: no declaration for %s", obj.Name())
}
loc, err := pgf.PosLocation(decl.Pos(), decl.Pos()+token.Pos(len(obj.Name())))
if err != nil {
return nil, err
}
return []protocol.Location{loc}, nil
}

// Finally, map the object position.
loc, err := mapPosition(ctx, pkg.FileSet(), snapshot, obj.Pos(), adjustedObjEnd(obj))
if err != nil {
return nil, err
}
return []protocol.Location{loc}, nil
}

// gopReferencedObject returns the identifier and object referenced at the
// specified position, which must be within the file pgf, for the purposes of
// definition/hover/call hierarchy operations. It returns a nil object if no
Expand Down Expand Up @@ -68,3 +188,80 @@ func gopReferencedObject(pkg Package, pgf *ParsedGopFile, pos token.Pos) (*ast.I
}
return nil, nil, nil
}

// gopImportDefinition returns locations defining a package referenced by the
// import spec containing pos.
//
// If pos is not inside an import spec, it returns nil, nil.
func gopImportDefinition(ctx context.Context, s Snapshot, pkg Package, pgf *ParsedGopFile, pos token.Pos) ([]protocol.Location, error) {
var imp *ast.ImportSpec
for _, spec := range pgf.File.Imports {
// We use "<= End" to accept a query immediately after an ImportSpec.
if spec.Path.Pos() <= pos && pos <= spec.Path.End() {
imp = spec
}
}
if imp == nil {
return nil, nil
}

importPath := GopUnquoteImportPath(imp)
impID := pkg.Metadata().DepsByImpPath[importPath]
if impID == "" {
return nil, fmt.Errorf("failed to resolve import %q", importPath)
}
impMetadata := s.Metadata(impID)
if impMetadata == nil {
return nil, fmt.Errorf("missing information for package %q", impID)
}

var locs []protocol.Location
for _, f := range impMetadata.CompiledGoFiles {
fh, err := s.ReadFile(ctx, f)
if err != nil {
if ctx.Err() != nil {
return nil, ctx.Err()
}
continue
}
pgf, err := s.ParseGo(ctx, fh, ParseHeader)
if err != nil {
if ctx.Err() != nil {
return nil, ctx.Err()
}
continue
}
loc, err := pgf.NodeLocation(pgf.File)
if err != nil {
return nil, err
}
locs = append(locs, loc)
}
for _, f := range impMetadata.CompiledGopFiles {
fh, err := s.ReadFile(ctx, f)
if err != nil {
if ctx.Err() != nil {
return nil, ctx.Err()
}
continue
}
pgf, err := s.ParseGop(ctx, fh, parserutil.ParseHeader)
if err != nil {
if ctx.Err() != nil {
return nil, ctx.Err()
}
continue
}
loc, err := pgf.NodeLocation(pgf.File)
if err != nil {
return nil, err
}
locs = append(locs, loc)
}

if len(locs) == 0 {
return nil, fmt.Errorf("package %q has no readable files", impID) // incl. unsafe
}

return locs, nil
}
10 changes: 10 additions & 0 deletions gopls/internal/lsp/source/view_gox.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ type ParsedGopFile struct {
ParseErr scanner.ErrorList
}

// HasPkgDecl checks if `package xxx` exists or not.
func (pgf *ParsedGopFile) HasPkgDecl() bool {
return pgf.File.Package != token.NoPos
}

// PositionPos returns the token.Pos of protocol position p within the file.
func (pgf *ParsedGopFile) PositionPos(p protocol.Position) (token.Pos, error) {
offset, err := pgf.Mapper.PositionOffset(p)
Expand All @@ -64,6 +69,11 @@ func (pgf *ParsedGopFile) NodeRange(node ast.Node) (protocol.Range, error) {
return pgf.Mapper.NodeRange(pgf.Tok, node)
}

// NodeLocation returns a protocol Location for the ast.Node interval in this file.
func (pgf *ParsedGopFile) NodeLocation(node ast.Node) (protocol.Location, error) {
return pgf.Mapper.PosLocation(pgf.Tok, node.Pos(), node.End())
}

// RangePos parses a protocol Range back into the go/token domain.
func (pgf *ParsedGopFile) RangePos(r protocol.Range) (token.Pos, token.Pos, error) {
start, end, err := pgf.Mapper.RangeOffsets(r)
Expand Down

0 comments on commit e975742

Please sign in to comment.