Skip to content

Commit

Permalink
feat(svc): support service extends
Browse files Browse the repository at this point in the history
  • Loading branch information
joyme123 committed Nov 7, 2023
1 parent cc49746 commit 163d9d1
Show file tree
Hide file tree
Showing 11 changed files with 1,517 additions and 1,306 deletions.
29 changes: 19 additions & 10 deletions format/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,25 @@ import (
)

const (
serviceOneLineTpl = `{{.Comments}}{{.Service}} {{.Identifier}} {{.LCUR}}{{.RCUR}}{{.Annotations}}{{.EndLineComments}}`
serviceOneLineTpl = `{{.Comments}}{{.Service}} {{.Identifier}}{{.Extends}}{{.ExtendServiceName}} {{.LCUR}}{{.RCUR}}{{.Annotations}}{{.EndLineComments}}`

serviceMultiLineTpl = `{{.Comments}}{{.Service}} {{.Identifier}} {{.LCUR}}
serviceMultiLineTpl = `{{.Comments}}{{.Service}} {{.Identifier}}{{.Extends}}{{.ExtendServiceName}} {{.LCUR}}
{{.Functions}}
{{.RCUR}}{{.Annotations}}{{.EndLineComments}}
`
)

type ServiceFormatter struct {
Comments string
Service string
Identifier string
LCUR string
Functions string
RCUR string
Annotations string
EndLineComments string
Comments string
Service string
Identifier string
LCUR string
Functions string
RCUR string
Annotations string
EndLineComments string
Extends string
ExtendServiceName string
}

func MustFormatService(svc *parser.Service) string {
Expand All @@ -41,6 +43,13 @@ func MustFormatService(svc *parser.Service) string {
EndLineComments: MustFormatEndLineComments(svc.EndLineComments, ""),
}

if svc.ExtendsKeyword != nil {
f.Extends = " " + MustFormatKeyword(svc.ExtendsKeyword.Keyword)
}
if svc.Extends != nil {
f.ExtendServiceName = " " + MustFormatIdentifier(svc.Extends)
}

if len(svc.Functions) > 0 {
return MustFormat(serviceMultiLineTpl, f)
}
Expand Down
4 changes: 2 additions & 2 deletions lsp/cache/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ func (v *View) ContainsFile(uri uri.URI) bool {
return false
}

folder = strings.TrimRight(folder, "/")
file = strings.TrimLeft(file, folder)
folder = strings.TrimSuffix(folder, "/")
file = strings.TrimPrefix(file, folder)

if strings.HasPrefix(file, "/") {
return true
Expand Down
50 changes: 50 additions & 0 deletions lsp/codejump/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,61 @@ func Definition(ctx context.Context, ss *cache.Snapshot, file uri.URI, pos proto
return typeNameDefinition(ctx, ss, file, pf.AST(), targetNode)
case "ConstValue":
return constValueTypeDefinition(ctx, ss, file, pf.AST(), targetNode)
case "IdentifierName": // service extends
return serviceDefinition(ctx, ss, file, pf.AST(), targetNode)
}

return
}

func serviceDefinition(ctx context.Context, ss *cache.Snapshot, file uri.URI, ast *parser.Document, targetNode parser.Node) ([]protocol.Location, error) {
res := make([]protocol.Location, 0)
astFile, id, _, err := ServiceDefinitionIdentifier(ctx, ss, file, ast, targetNode)
if err != nil {
return res, err
}
if id != nil {
res = append(res, jump(astFile, id.Name))
}

return res, nil
}

func ServiceDefinitionIdentifier(ctx context.Context, ss *cache.Snapshot, file uri.URI, ast *parser.Document, targetNode parser.Node) (uri.URI, *parser.Identifier, string, error) {
identifierName := targetNode.(*parser.IdentifierName)

include, identifier, found := strings.Cut(identifierName.Text, ".")
var astFile uri.URI
if !found {
identifier = include
include = ""
astFile = file
} else {
path := lsputils.GetIncludePath(ast, include)
if path == "" { // doesn't match any include path
return "", nil, "", nil
}
astFile = lsputils.IncludeURI(file, path)
}

// now we can find destinate definition in `dstAst` by `identifier`
dstAst, err := ss.Parse(ctx, astFile)
if err != nil {
return astFile, nil, "", err
}

if len(dstAst.Errors()) > 0 {
log.Errorf("parse error: %v", dstAst.Errors())
}

dstService := GetServiceNode(dstAst.AST(), identifier)
if dstService != nil {
return astFile, dstService.Name, "Service", nil
}

return astFile, nil, "", nil
}

func typeNameDefinition(ctx context.Context, ss *cache.Snapshot, file uri.URI, ast *parser.Document, targetNode parser.Node) ([]protocol.Location, error) {
res := make([]protocol.Location, 0)
astFile, id, _, err := TypeNameDefinitionIdentifier(ctx, ss, file, ast, targetNode)
Expand Down
43 changes: 41 additions & 2 deletions lsp/codejump/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,56 @@ func Hover(ctx context.Context, ss *cache.Snapshot, file uri.URI, pos protocol.P
nodePath := parser.SearchNodePathByPosition(pf.AST(), astPos)
targetNode := nodePath[len(nodePath)-1]

log.Info("node type:", targetNode.Type())

switch targetNode.Type() {
case "TypeName":
return hoverDefinition(ctx, ss, file, pf.AST(), nodePath, targetNode)
return hoverDefinition(ctx, ss, file, pf.AST(), targetNode)
case "ConstValue":
return hoverConstValue(ctx, ss, file, pf.AST(), targetNode)
case "IdentifierName": // service extends
return hoverService(ctx, ss, file, pf.AST(), targetNode)
}

return
}

func hoverDefinition(ctx context.Context, ss *cache.Snapshot, file uri.URI, ast *parser.Document, nodePath []parser.Node, targetNode parser.Node) (string, error) {
func hoverService(ctx context.Context, ss *cache.Snapshot, file uri.URI, ast *parser.Document, targetNode parser.Node) (string, error) {
identifierName := targetNode.(*parser.IdentifierName)
name := identifierName.Text
include, identifier, found := strings.Cut(name, ".")
var astFile uri.URI
if !found {
identifier = include
include = ""
astFile = file
} else {
path := lsputils.GetIncludePath(ast, include)
if path == "" { // doesn't match any include path
return "", nil
}
astFile = lsputils.IncludeURI(file, path)
}

// now we can find destinate definition in `dstAst` by `identifier`
dstAst, err := ss.Parse(ctx, astFile)
if err != nil {
return "", err
}

if len(dstAst.Errors()) > 0 {
log.Errorf("parse error: %v", dstAst.Errors())
}

dstService := GetServiceNode(dstAst.AST(), identifier)
if dstService != nil {
return format.MustFormatService(dstService), nil
}

return "", nil
}

func hoverDefinition(ctx context.Context, ss *cache.Snapshot, file uri.URI, ast *parser.Document, targetNode parser.Node) (string, error) {
typeName := targetNode.(*parser.TypeName)
typeV := typeName.Name
if IsBasicType(typeV) {
Expand Down
75 changes: 73 additions & 2 deletions lsp/codejump/reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ func Reference(ctx context.Context, ss *cache.Snapshot, file uri.URI, pos protoc
}
// search in const value
return searchConstValueIdentifierReferences(ctx, ss, file, typeName)
} else if definitionType == "Service" {
svcName := targetNode.(*parser.IdentifierName).Text
if !strings.Contains(svcName, ".") {
svcName = fmt.Sprintf("%s.%s", lsputils.GetIncludeName(file), svcName)
} else {
include, _, _ := strings.Cut(svcName, ".")
path := lsputils.GetIncludePath(pf.AST(), include)
if path != "" { // doesn't match any include path
file = lsputils.IncludeURI(file, path)
}
}
return searchServiceReferences(ctx, ss, file, svcName)
}

if _, ok := validReferenceDefinitionType[definitionType]; !ok {
Expand Down Expand Up @@ -114,13 +126,71 @@ func searchTypeNameReferences(ctx context.Context, ss *cache.Snapshot, file uri.
return
}

func searchServiceReferences(ctx context.Context, ss *cache.Snapshot, file uri.URI, svcName string) (res []protocol.Location, err error) {
log.Debugln("searchServiceReferences for file:", file, "svcName:", svcName)
var errs []error

// search in it self
locations, err := searchServiceDefinitionReferences(ctx, ss, file, strings.TrimPrefix(svcName, fmt.Sprintf("%s.", lsputils.GetIncludeName(file))))
if err != nil {
errs = append(errs, err)
}
res = append(res, locations...)

// search service references in other file
includeNode := ss.Graph().Get(file)
if includeNode != nil {
if len(includeNode.InDegree()) == 0 && len(includeNode.OutDegree()) == 0 {
ss.Graph().Debug()
}
referenceFiles := includeNode.InDegree()
for _, referenceFile := range referenceFiles {
log.Debugln("reference file: ", referenceFile)
locations, err := searchServiceDefinitionReferences(ctx, ss, referenceFile, svcName)
if err != nil {
errs = append(errs, err)
}
res = append(res, locations...)
}
}

if len(errs) > 0 {
err = utilerrors.NewAggregate(errs)
}

return
}

func searchServiceDefinitionReferences(ctx context.Context, ss *cache.Snapshot, file uri.URI, svcName string) (res []protocol.Location, err error) {
ast, err := ss.Parse(ctx, file)
if err != nil {
return
}

if ast.AST() == nil {
return
}

for _, svc := range ast.AST().Services {
if svc.BadNode || svc.ChildrenBadNode() || svc.Extends == nil || svc.Extends.Name == nil {
continue
}

if svcName == svc.Extends.Name.Text {
res = append(res, jump(file, svc.Extends.Name))
}
}

return
}

func searchIdentifierReferences(ctx context.Context, ss *cache.Snapshot, file uri.URI, typeName string, definitionType string) (res []protocol.Location, err error) {
log.Debugln("searchIdentifierReferences for file:", file, "typeName:", typeName)
var errs []error

// search in it self
locations, err := searchDefinitionIdentifierReferences(ctx, ss, file,
strings.TrimLeft(typeName, fmt.Sprintf("%s.", lsputils.GetIncludeName(file))), definitionType)
strings.TrimPrefix(typeName, fmt.Sprintf("%s.", lsputils.GetIncludeName(file))), definitionType)
if err != nil {
errs = append(errs, err)
}
Expand Down Expand Up @@ -193,6 +263,7 @@ func searchDefinitionIdentifierReferences(ctx context.Context, ss *cache.Snapsho
}

for i := range fn.Arguments {
log.Debugln("search function args", "definitionType", definitionType, "typeName", typeName)
jumpField(fn.Arguments[i])
}

Expand Down Expand Up @@ -287,7 +358,7 @@ func searchConstValueReferences(ctx context.Context, ss *cache.Snapshot, file ur
func searchConstValueIdentifierReferences(ctx context.Context, ss *cache.Snapshot, file uri.URI, valueName string) (res []protocol.Location, err error) {
var errs []error
// search in it self
locations, err := searchConstValueIdentifierReference(ctx, ss, file, strings.TrimLeft(valueName, fmt.Sprintf("%s.", lsputils.GetIncludeName(file))))
locations, err := searchConstValueIdentifierReference(ctx, ss, file, strings.TrimPrefix(valueName, fmt.Sprintf("%s.", lsputils.GetIncludeName(file))))
if err != nil {
errs = append(errs, err)
}
Expand Down
23 changes: 23 additions & 0 deletions lsp/codejump/rename.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"strings"

"github.com/joyme123/thrift-ls/lsp/cache"
"github.com/joyme123/thrift-ls/lsp/lsputils"
Expand Down Expand Up @@ -88,6 +89,28 @@ func Rename(ctx context.Context, ss *cache.Snapshot, file uri.URI, pos protocol.
Range: self,
})

return convertLocationToWorkspaceEdit(locations, file, newName), nil
} else if definitionType == "Service" {
svcName := targetNode.(*parser.IdentifierName).Text
if !strings.Contains(svcName, ".") {
svcName = fmt.Sprintf("%s.%s", lsputils.GetIncludeName(file), svcName)
} else {
include, _, _ := strings.Cut(svcName, ".")
path := lsputils.GetIncludePath(pf.AST(), include)
if path != "" { // doesn't match any include path
file = lsputils.IncludeURI(file, path)
}
}
locations, err := searchServiceReferences(ctx, ss, file, svcName)
if err != nil {
return nil, err
}

locations = append(locations, protocol.Location{
URI: file,
Range: self,
})

return convertLocationToWorkspaceEdit(locations, file, newName), nil
}

Expand Down
15 changes: 15 additions & 0 deletions lsp/codejump/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,21 @@ func GetTypedefNode(ast *parser.Document, name string) *parser.Typedef {
return nil
}

func GetServiceNode(ast *parser.Document, name string) *parser.Service {
if ast == nil {
return nil
}

for _, svc := range ast.Services {
if svc.BadNode || svc.Name == nil || svc.Name.Name == nil || svc.Name.Name.Text != name {
continue
}
return svc
}

return nil
}

func jump(file uri.URI, node parser.Node) protocol.Location {
rng := lsputils.ASTNodeToRange(node)
return protocol.Location{
Expand Down
6 changes: 3 additions & 3 deletions lsp/completion/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ func ListDirAndFiles(dir, prefix string) (res []Candidate, err error) {

pathItems = pathItems[0 : len(pathItems)-up]

dir, filePrefix := filepath.Split(strings.TrimLeft(prefixClean, "../"))
filePrefix = strings.TrimLeft(filePrefix, "./")
dir, filePrefix := filepath.Split(strings.TrimPrefix(prefixClean, "../"))
filePrefix = strings.TrimPrefix(filePrefix, "./")
baseDir := strings.Join(pathItems, "/") + "/" + dir
prefix = strings.TrimRight(prefix, filePrefix)
prefix = strings.TrimSuffix(prefix, filePrefix)

log.Debugf("include completion: walk dir %s with prefix %s, filePrefix %s", baseDir, prefix, filePrefix)
filepath.WalkDir(baseDir, func(path string, d fs.DirEntry, err error) error {
Expand Down
4 changes: 4 additions & 0 deletions lsp/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ func (s *Server) hover(ctx context.Context, params *protocol.HoverParams) (*prot
return nil, err
}

if content == "" {
return nil, nil
}

return &protocol.Hover{
Contents: protocol.MarkupContent{
Kind: protocol.Markdown,
Expand Down
4 changes: 2 additions & 2 deletions parser/thrift.peg
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ IntConstant = comments:ReservedComments v:(HexIntConstant / OctIntConstant / No
}

HexIntConstant = "0x" ([0-9] / [A-Z] / [a-z])+ {
v, err := strconv.ParseInt(strings.TrimLeft(string(c.text), "0x"), 16, 64)
v, err := strconv.ParseInt(strings.TrimPrefix(string(c.text), "0x"), 16, 64)
if err != nil {
return nil, err
}
Expand All @@ -513,7 +513,7 @@ HexIntConstant = "0x" ([0-9] / [A-Z] / [a-z])+ {
}

OctIntConstant = "0o" Digit+ {
v, err := strconv.ParseInt(strings.TrimLeft(string(c.text), "0o"), 8, 64)
v, err := strconv.ParseInt(strings.TrimPrefix(string(c.text), "0o"), 8, 64)
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 163d9d1

Please sign in to comment.