From 1aef8974947a2638978d28537afbfcbfeedbfc7b Mon Sep 17 00:00:00 2001 From: Ian Cottrell Date: Sat, 17 Nov 2018 19:01:23 -0500 Subject: [PATCH] internal/lsp: fix for limitations of export data It is impossible to reconstruct a line and column correctly from export data, so we have to attempt to find open and process the file in order to guess what the original pos was when we have one that originated in export data. This occurs in any time in go to definition when the target is not in the same pacakge as the source. Change-Id: Ib2ee404d4f1c39d8bd7f1fbc2096d8d6cbeed6f8 Reviewed-on: https://go-review.googlesource.com/c/150044 Reviewed-by: Rebecca Stambler --- internal/lsp/protocol/printers.go | 6 +++--- internal/lsp/source/definition.go | 34 +++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/internal/lsp/protocol/printers.go b/internal/lsp/protocol/printers.go index f4f80db567..e4995b4736 100644 --- a/internal/lsp/protocol/printers.go +++ b/internal/lsp/protocol/printers.go @@ -17,9 +17,9 @@ import ( ) func (p Position) Format(f fmt.State, c rune) { - fmt.Fprintf(f, "%d", int(p.Line)) + fmt.Fprintf(f, "%d", int(p.Line)+1) if p.Character >= 0 { - fmt.Fprintf(f, ":%d", int(p.Character)) + fmt.Fprintf(f, ":%d", int(p.Character)+1) } } @@ -28,7 +28,7 @@ func (r Range) Format(f fmt.State, c rune) { case r.Start == r.End || r.End.Line < 0: fmt.Fprintf(f, "%v", r.Start) case r.End.Line == r.Start.Line: - fmt.Fprintf(f, "%v¦%d", r.Start, int(r.End.Character)) + fmt.Fprintf(f, "%v¦%d", r.Start, int(r.End.Character)+1) default: fmt.Fprintf(f, "%v¦%v", r.Start, r.End) } diff --git a/internal/lsp/source/definition.go b/internal/lsp/source/definition.go index 0df2bd139e..4354638166 100644 --- a/internal/lsp/source/definition.go +++ b/internal/lsp/source/definition.go @@ -5,11 +5,13 @@ package source import ( + "bytes" "context" "fmt" "go/ast" "go/token" "go/types" + "io/ioutil" "golang.org/x/tools/go/ast/astutil" ) @@ -43,10 +45,7 @@ func Definition(ctx context.Context, f *File, pos token.Pos) (Range, error) { } } } - return Range{ - Start: obj.Pos(), - End: obj.Pos() + token.Pos(len([]byte(obj.Name()))), // TODO: use real range of obj - }, nil + return objToRange(f.view.Config.Fset, obj), nil } // ident returns the ident plus any extra information needed @@ -93,3 +92,30 @@ func checkIdentifier(f *ast.File, pos token.Pos) (ident, error) { } return result, nil } + +func objToRange(fSet *token.FileSet, obj types.Object) Range { + p := obj.Pos() + f := fSet.File(p) + pos := f.Position(p) + if pos.Column == 1 { + // Column is 1, so we probably do not have full position information + // Currently exportdata does not store the column. + // For now we attempt to read the original source and find the identifier + // within the line. If we find it we patch the column to match its offset. + // TODO: we have probably already added the full data for the file to the + // fileset, we ought to track it rather than adding it over and over again + // TODO: if we parse from source, we will never need this hack + if src, err := ioutil.ReadFile(pos.Filename); err == nil { + newF := fSet.AddFile(pos.Filename, -1, len(src)) + newF.SetLinesForContent(src) + lineStart := lineStart(newF, pos.Line) + offset := newF.Offset(lineStart) + col := bytes.Index(src[offset:], []byte(obj.Name())) + p = newF.Pos(offset + col) + } + } + return Range{ + Start: p, + End: p + token.Pos(len([]byte(obj.Name()))), // TODO: use real range of obj + } +}