diff --git a/spx-gui/src/components/editor/code-editor/ui/markdown/CodeView.vue b/spx-gui/src/components/editor/code-editor/ui/markdown/CodeView.vue index 20148fbdc..975e69cb7 100644 --- a/spx-gui/src/components/editor/code-editor/ui/markdown/CodeView.vue +++ b/spx-gui/src/components/editor/code-editor/ui/markdown/CodeView.vue @@ -99,9 +99,11 @@ const codeHtml = computed(() => { .addition, .deletion { - :deep(pre) { + :deep(> pre) { position: relative; padding-left: 24px; + // Disable default background and show background from parent (addition/deletion) + background-color: transparent !important; } :deep(.line::before) { position: absolute; diff --git a/tools/spxls/internal/server/compile.go b/tools/spxls/internal/server/compile.go index a28251c52..d72dfcc74 100644 --- a/tools/spxls/internal/server/compile.go +++ b/tools/spxls/internal/server/compile.go @@ -98,6 +98,9 @@ type compileResult struct { // computedCache is the cache for computed results. computedCache compileResultComputedCache + + // documentURIs maps each spx file path to its document URI. + documentURIs map[string]DocumentURI } // compileResultComputedCache represents the computed cache for [compileResult]. @@ -129,7 +132,7 @@ func (r *compileResult) isInFset(pos goptoken.Pos) bool { // innermostScopeAt returns the innermost scope that contains the given // position. It returns nil if not found. func (r *compileResult) innermostScopeAt(pos goptoken.Pos) *types.Scope { - fileScope := r.typeInfo.Scopes[r.mainASTPkg.Files[r.fset.Position(pos).Filename]] + fileScope := r.typeInfo.Scopes[r.posASTFile(pos)] if fileScope == nil { return nil } @@ -231,8 +234,8 @@ func (r *compileResult) refIdentsFor(obj types.Object) []*gopast.Ident { // selectorTypeNameForIdent returns the selector type name for the given // identifier. It returns empty string if no selector can be inferred. func (r *compileResult) selectorTypeNameForIdent(ident *gopast.Ident) string { - astFile, ok := r.mainASTPkg.Files[r.fset.Position(ident.Pos()).Filename] - if !ok { + astFile := r.nodeASTFile(ident) + if astFile == nil { return "" } @@ -272,7 +275,7 @@ func (r *compileResult) selectorTypeNameForIdent(ident *gopast.Ident) string { astFileScope := r.typeInfo.Scopes[astFile] innermostScope := r.innermostScopeAt(ident.Pos()) if innermostScope == astFileScope { - spxFile := r.fset.Position(ident.Pos()).Filename + spxFile := r.nodeFilename(ident) if spxFile == r.mainSpxFile { return "Game" } @@ -340,8 +343,8 @@ func (r *compileResult) isDefinedInFirstVarBlock(obj types.Object) bool { if defIdent == nil { return false } - astFile, ok := r.mainASTPkg.Files[r.fset.Position(defIdent.Pos()).Filename] - if !ok { + astFile := r.nodeASTFile(defIdent) + if astFile == nil { return false } firstVarBlock := r.firstVarBlocks[astFile] @@ -426,7 +429,7 @@ func (r *compileResult) spxDefinitionsForNamedStruct(named *types.Named) (defs [ // spxResourceRefAtASTFilePosition returns the spx resource reference at the // given position in the given AST file. func (r *compileResult) spxResourceRefAtASTFilePosition(astFile *gopast.File, position Position) *SpxResourceRef { - spxFile := r.fset.Position(astFile.Pos()).Filename + spxFile := r.nodeFilename(astFile) line := int(position.Line) + 1 column := int(position.Character) + 1 @@ -493,6 +496,71 @@ func (r *compileResult) addDiagnostics(documentURI DocumentURI, diags ...Diagnos } } +// addDiagnosticsForSpxFile adds diagnostics to the compile result for the given +// spx file. +func (r *compileResult) addDiagnosticsForSpxFile(spxFile string, diags ...Diagnostic) { + r.addDiagnostics(r.documentURIs[spxFile], diags...) +} + +// posFilename returns the filename for the given position. +func (r *compileResult) posFilename(pos goptoken.Pos) string { + return r.fset.Position(pos).Filename +} + +// nodeFilename returns the filename for the given node. +func (r *compileResult) nodeFilename(node gopast.Node) string { + return r.posFilename(node.Pos()) +} + +// posASTFile returns the AST file for the given position. +func (r *compileResult) posASTFile(pos goptoken.Pos) *gopast.File { + return r.mainASTPkg.Files[r.posFilename(pos)] +} + +// nodeASTFile returns the AST file for the given node. +func (r *compileResult) nodeASTFile(node gopast.Node) *gopast.File { + return r.posASTFile(node.Pos()) +} + +// posDocumentURI returns the [DocumentURI] for the given position. +func (r *compileResult) posDocumentURI(pos goptoken.Pos) DocumentURI { + return r.documentURIs[r.posFilename(pos)] +} + +// nodeDocumentURI returns the [DocumentURI] for the given node. +func (r *compileResult) nodeDocumentURI(node gopast.Node) DocumentURI { + return r.posDocumentURI(node.Pos()) +} + +// rangeForPos returns the [Range] for the given position. +func (r *compileResult) rangeForPos(pos goptoken.Pos) Range { + return RangeForGopTokenPosition(r.fset.Position(pos)) +} + +// rangeForNode returns the [Range] for the given node. +func (r *compileResult) rangeForNode(node gopast.Node) Range { + return Range{ + Start: FromGopTokenPosition(r.fset.Position(node.Pos())), + End: FromGopTokenPosition(r.fset.Position(node.End())), + } +} + +// locationForPos returns the [Location] for the given position. +func (r *compileResult) locationForPos(pos goptoken.Pos) Location { + return Location{ + URI: r.documentURIs[r.posFilename(pos)], + Range: r.rangeForPos(pos), + } +} + +// locationForNode returns the [Location] for the given node. +func (r *compileResult) locationForNode(node gopast.Node) Location { + return Location{ + URI: r.documentURIs[r.nodeFilename(node)], + Range: r.rangeForNode(node), + } +} + // compileCache represents a cache for compilation results. type compileCache struct { result *compileResult @@ -580,6 +648,7 @@ func (s *Server) compileUncached(snapshot *vfs.MapFS, spxFiles []string) (*compi spxSoundResourceAutoBindings: make(map[types.Object]struct{}), spxSpriteResourceAutoBindings: make(map[types.Object]struct{}), diagnostics: make(map[DocumentURI][]Diagnostic, len(spxFiles)), + documentURIs: make(map[string]DocumentURI, len(spxFiles)), } var ( @@ -589,6 +658,7 @@ func (s *Server) compileUncached(snapshot *vfs.MapFS, spxFiles []string) (*compi for _, spxFile := range spxFiles { documentURI := s.toDocumentURI(spxFile) result.diagnostics[documentURI] = []Diagnostic{} + result.documentURIs[spxFile] = documentURI astFile, err := gopparser.ParseFSEntry(result.fset, gpfs, spxFile, nil, gopparser.Config{ Mode: gopparser.ParseComments | gopparser.AllErrors | gopparser.ParseGoPlusClass, @@ -603,33 +673,23 @@ func (s *Server) compileUncached(snapshot *vfs.MapFS, spxFiles []string) (*compi for _, e := range parseErr { result.addDiagnostics(documentURI, Diagnostic{ Severity: SeverityError, - Range: Range{ - Start: FromGopTokenPosition(e.Pos), - End: FromGopTokenPosition(e.Pos), - }, - Message: e.Msg, + Range: RangeForGopTokenPosition(e.Pos), + Message: e.Msg, }) } } else if errors.As(err, &codeErr) { // Handle code generation errors. - position := codeErr.Fset.Position(codeErr.Pos) result.addDiagnostics(documentURI, Diagnostic{ Severity: SeverityError, - Range: Range{ - Start: FromGopTokenPosition(position), - End: FromGopTokenPosition(position), - }, - Message: codeErr.Error(), + Range: result.rangeForPos(codeErr.Pos), + Message: codeErr.Error(), }) } else { // Handle unknown errors. result.addDiagnostics(documentURI, Diagnostic{ Severity: SeverityError, - Range: Range{ - Start: Position{Line: 0, Character: 0}, - End: Position{Line: 0, Character: 0}, - }, - Message: fmt.Sprintf("failed to parse spx file: %v", err), + Range: RangeForGopTokenPosition(goptoken.Position{}), + Message: fmt.Sprintf("failed to parse spx file: %v", err), }) } } @@ -675,14 +735,10 @@ func (s *Server) compileUncached(snapshot *vfs.MapFS, spxFiles []string) (*compi Error: func(err error) { if typeErr, ok := err.(types.Error); ok { position := typeErr.Fset.Position(typeErr.Pos) - documentURI := s.toDocumentURI(position.Filename) - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnosticsForSpxFile(position.Filename, Diagnostic{ Severity: SeverityError, - Range: Range{ - Start: FromGopTokenPosition(position), - End: FromGopTokenPosition(position), - }, - Message: typeErr.Msg, + Range: RangeForGopTokenPosition(position), + Message: typeErr.Msg, }) } }, @@ -760,14 +816,10 @@ func (s *Server) inspectForSpxResourceSet(snapshot *vfs.MapFS, result *compileRe if types.AssignableTo(firstArgTV.Type, types.Typ[types.String]) { spxResourceRootDir, _ = getStringLitOrConstValue(firstArg, firstArgTV) } else { - documentURI := s.toDocumentURI(result.mainSpxFile) - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnosticsForSpxFile(result.mainSpxFile, Diagnostic{ Severity: SeverityError, - Range: Range{ - Start: FromGopTokenPosition(result.fset.Position(firstArg.Pos())), - End: FromGopTokenPosition(result.fset.Position(firstArg.End())), - }, - Message: "first argument of run must be a string literal or constant", + Range: result.rangeForNode(firstArg), + Message: "first argument of run must be a string literal or constant", }) } return false @@ -779,13 +831,10 @@ func (s *Server) inspectForSpxResourceSet(snapshot *vfs.MapFS, result *compileRe spxResourceSet, err := NewSpxResourceSet(spxResourceRootFS) if err != nil { - result.addDiagnostics(s.toDocumentURI(result.mainSpxFile), Diagnostic{ + result.addDiagnosticsForSpxFile(result.mainSpxFile, Diagnostic{ Severity: SeverityError, - Range: Range{ - Start: Position{Line: 0, Character: 0}, - End: Position{Line: 0, Character: 0}, - }, - Message: fmt.Sprintf("failed to create spx resource set: %v", err), + Range: RangeForGopTokenPosition(goptoken.Position{}), + Message: fmt.Sprintf("failed to create spx resource set: %v", err), }) return } @@ -825,7 +874,7 @@ func (s *Server) inspectForSpxResourceRefs(result *compileResult) { continue } - spxFile := result.fset.Position(ident.Pos()).Filename + spxFile := result.nodeFilename(ident) if spxFile != result.mainSpxFile || result.innermostScopeAt(ident.Pos()) != mainSpxFileScope { continue } @@ -847,14 +896,10 @@ func (s *Server) inspectForSpxResourceRefs(result *compileResult) { } if !result.isDefinedInFirstVarBlock(obj) { - documentURI := s.toDocumentURI(spxFile) - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnosticsForSpxFile(spxFile, Diagnostic{ Severity: SeverityWarning, - Range: Range{ - Start: FromGopTokenPosition(result.fset.Position(ident.Pos())), - End: FromGopTokenPosition(result.fset.Position(ident.End())), - }, - Message: "resources must be defined in the first var block for auto-binding", + Range: result.rangeForNode(ident), + Message: "resources must be defined in the first var block for auto-binding", }) continue } @@ -950,8 +995,8 @@ func (s *Server) inspectForSpxResourceRefs(result *compileResult) { GetSpxSpriteNameType(), GetSpxSoundNameType(), GetSpxWidgetNameType(): - astFile, ok := result.mainASTPkg.Files[result.fset.Position(ident.Pos()).Filename] - if !ok { + astFile := result.nodeASTFile(ident) + if astFile == nil { continue } @@ -1029,11 +1074,8 @@ func (s *Server) inspectSpxResourceRefForTypeAtExpr(result *compileResult, expr // reference at an expression. It returns the spx backdrop resource if it was // successfully retrieved. func (s *Server) inspectSpxBackdropResourceRefAtExpr(result *compileResult, expr gopast.Expr, declaredType types.Type) *SpxBackdropResource { - documentURI := s.toDocumentURI(result.fset.Position(expr.Pos()).Filename) - exprRange := Range{ - Start: FromGopTokenPosition(result.fset.Position(expr.Pos())), - End: FromGopTokenPosition(result.fset.Position(expr.End())), - } + exprDocumentURI := result.nodeDocumentURI(expr) + exprRange := result.rangeForNode(expr) exprTV := result.typeInfo.Types[expr] spxBackdropName, ok := getStringLitOrConstValue(expr, exprTV) @@ -1041,7 +1083,7 @@ func (s *Server) inspectSpxBackdropResourceRefAtExpr(result *compileResult, expr return nil } if spxBackdropName == "" { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: "backdrop resource name cannot be empty", @@ -1060,7 +1102,7 @@ func (s *Server) inspectSpxBackdropResourceRefAtExpr(result *compileResult, expr spxBackdropResource := result.spxResourceSet.Backdrop(spxBackdropName) if spxBackdropResource == nil { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: fmt.Sprintf("backdrop resource %q not found", spxBackdropName), @@ -1074,24 +1116,18 @@ func (s *Server) inspectSpxBackdropResourceRefAtExpr(result *compileResult, expr // at an expression. It returns the spx sprite resource if it was successfully // retrieved. func (s *Server) inspectSpxSpriteResourceRefAtExpr(result *compileResult, expr gopast.Expr, declaredType types.Type) *SpxSpriteResource { - documentURI := s.toDocumentURI(result.fset.Position(expr.Pos()).Filename) - exprRange := Range{ - Start: FromGopTokenPosition(result.fset.Position(expr.Pos())), - End: FromGopTokenPosition(result.fset.Position(expr.End())), - } + exprDocumentURI := result.nodeDocumentURI(expr) + exprRange := result.rangeForNode(expr) exprTV := result.typeInfo.Types[expr] var spxSpriteName string if callExpr, ok := expr.(*gopast.CallExpr); ok { switch fun := callExpr.Fun.(type) { case *gopast.Ident: - spxSpriteName = strings.TrimSuffix(path.Base(result.fset.Position(callExpr.Pos()).Filename), ".spx") + spxSpriteName = strings.TrimSuffix(path.Base(result.nodeFilename(callExpr)), ".spx") case *gopast.SelectorExpr: if ident, ok := fun.X.(*gopast.Ident); ok { - exprRange = Range{ - Start: FromGopTokenPosition(result.fset.Position(ident.Pos())), - End: FromGopTokenPosition(result.fset.Position(ident.End())), - } + exprRange = result.rangeForNode(ident) obj := result.typeInfo.ObjectOf(ident) if obj != nil { @@ -1108,7 +1144,7 @@ func (s *Server) inspectSpxSpriteResourceRefAtExpr(result *compileResult, expr g } } if spxSpriteName == "" { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: "sprite resource name cannot be empty", @@ -1153,7 +1189,7 @@ func (s *Server) inspectSpxSpriteResourceRefAtExpr(result *compileResult, expr g return nil } if spxSpriteName == "" { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: "sprite resource name cannot be empty", @@ -1169,7 +1205,7 @@ func (s *Server) inspectSpxSpriteResourceRefAtExpr(result *compileResult, expr g spxSpriteResource := result.spxResourceSet.Sprite(spxSpriteName) if spxSpriteResource == nil { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: fmt.Sprintf("sprite resource %q not found", spxSpriteName), @@ -1183,11 +1219,8 @@ func (s *Server) inspectSpxSpriteResourceRefAtExpr(result *compileResult, expr g // resource reference at an expression. It returns the spx sprite costume // resource if it was successfully retrieved. func (s *Server) inspectSpxSpriteCostumeResourceRefAtExpr(result *compileResult, spxSpriteResource *SpxSpriteResource, expr gopast.Expr, declaredType types.Type) *SpxSpriteCostumeResource { - documentURI := s.toDocumentURI(result.fset.Position(expr.Pos()).Filename) - exprRange := Range{ - Start: FromGopTokenPosition(result.fset.Position(expr.Pos())), - End: FromGopTokenPosition(result.fset.Position(expr.End())), - } + exprDocumentURI := result.nodeDocumentURI(expr) + exprRange := result.rangeForNode(expr) exprTV := result.typeInfo.Types[expr] var typ types.Type @@ -1216,7 +1249,7 @@ func (s *Server) inspectSpxSpriteCostumeResourceRefAtExpr(result *compileResult, return nil } if spxSpriteCostumeName == "" { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: "sprite costume resource name cannot be empty", @@ -1231,7 +1264,7 @@ func (s *Server) inspectSpxSpriteCostumeResourceRefAtExpr(result *compileResult, spxSpriteCostumeResource := spxSpriteResource.Costume(spxSpriteCostumeName) if spxSpriteCostumeResource == nil { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: fmt.Sprintf("costume resource %q not found in sprite %q", spxSpriteCostumeName, spxSpriteResource.Name), @@ -1245,11 +1278,8 @@ func (s *Server) inspectSpxSpriteCostumeResourceRefAtExpr(result *compileResult, // resource reference at an expression. It returns the spx sprite animation // resource if it was successfully retrieved. func (s *Server) inspectSpxSpriteAnimationResourceRefAtExpr(result *compileResult, spxSpriteResource *SpxSpriteResource, expr gopast.Expr, declaredType types.Type) *SpxSpriteAnimationResource { - documentURI := s.toDocumentURI(result.fset.Position(expr.Pos()).Filename) - exprRange := Range{ - Start: FromGopTokenPosition(result.fset.Position(expr.Pos())), - End: FromGopTokenPosition(result.fset.Position(expr.End())), - } + exprDocumentURI := result.nodeDocumentURI(expr) + exprRange := result.rangeForNode(expr) exprTV := result.typeInfo.Types[expr] var typ types.Type @@ -1278,7 +1308,7 @@ func (s *Server) inspectSpxSpriteAnimationResourceRefAtExpr(result *compileResul return nil } if spxSpriteAnimationName == "" { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: "sprite animation resource name cannot be empty", @@ -1293,7 +1323,7 @@ func (s *Server) inspectSpxSpriteAnimationResourceRefAtExpr(result *compileResul spxSpriteAnimationResource := spxSpriteResource.Animation(spxSpriteAnimationName) if spxSpriteAnimationResource == nil { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: fmt.Sprintf("animation resource %q not found in sprite %q", spxSpriteAnimationName, spxSpriteResource.Name), @@ -1307,11 +1337,8 @@ func (s *Server) inspectSpxSpriteAnimationResourceRefAtExpr(result *compileResul // an expression. It returns the spx sound resource if it was successfully // retrieved. func (s *Server) inspectSpxSoundResourceRefAtExpr(result *compileResult, expr gopast.Expr, declaredType types.Type) *SpxSoundResource { - documentURI := s.toDocumentURI(result.fset.Position(expr.Pos()).Filename) - exprRange := Range{ - Start: FromGopTokenPosition(result.fset.Position(expr.Pos())), - End: FromGopTokenPosition(result.fset.Position(expr.End())), - } + exprDocumentURI := result.nodeDocumentURI(expr) + exprRange := result.rangeForNode(expr) exprTV := result.typeInfo.Types[expr] var typ types.Type @@ -1354,7 +1381,7 @@ func (s *Server) inspectSpxSoundResourceRefAtExpr(result *compileResult, expr go return nil } if spxSoundName == "" { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: "sound resource name cannot be empty", @@ -1369,7 +1396,7 @@ func (s *Server) inspectSpxSoundResourceRefAtExpr(result *compileResult, expr go spxSoundResource := result.spxResourceSet.Sound(spxSoundName) if spxSoundResource == nil { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: fmt.Sprintf("sound resource %q not found", spxSoundName), @@ -1383,11 +1410,8 @@ func (s *Server) inspectSpxSoundResourceRefAtExpr(result *compileResult, expr go // at an expression. It returns the spx widget resource if it was successfully // retrieved. func (s *Server) inspectSpxWidgetResourceRefAtExpr(result *compileResult, expr gopast.Expr, declaredType types.Type) *SpxWidgetResource { - documentURI := s.toDocumentURI(result.fset.Position(expr.Pos()).Filename) - exprRange := Range{ - Start: FromGopTokenPosition(result.fset.Position(expr.Pos())), - End: FromGopTokenPosition(result.fset.Position(expr.End())), - } + exprDocumentURI := result.nodeDocumentURI(expr) + exprRange := result.rangeForNode(expr) exprTV := result.typeInfo.Types[expr] var typ types.Type @@ -1416,7 +1440,7 @@ func (s *Server) inspectSpxWidgetResourceRefAtExpr(result *compileResult, expr g return nil } if spxWidgetName == "" { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: "widget resource name cannot be empty", @@ -1431,7 +1455,7 @@ func (s *Server) inspectSpxWidgetResourceRefAtExpr(result *compileResult, expr g spxWidgetResource := result.spxResourceSet.Widget(spxWidgetName) if spxWidgetResource == nil { - result.addDiagnostics(documentURI, Diagnostic{ + result.addDiagnostics(exprDocumentURI, Diagnostic{ Severity: SeverityError, Range: exprRange, Message: fmt.Sprintf("widget resource %q not found", spxWidgetName), diff --git a/tools/spxls/internal/server/definition.go b/tools/spxls/internal/server/definition.go index 0e56a8554..9eff619bf 100644 --- a/tools/spxls/internal/server/definition.go +++ b/tools/spxls/internal/server/definition.go @@ -32,11 +32,11 @@ func (s *Server) textDocumentDefinition(params *DefinitionParams) (any, error) { if !result.isInFset(objPos) { return nil, nil } - return s.createLocationFromPos(result.fset, objPos), nil + return result.locationForPos(objPos), nil } else if !result.isInFset(defIdent.Pos()) { return nil, nil } - return s.createLocationFromIdent(result.fset, defIdent), nil + return result.locationForNode(defIdent), nil } // See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_typeDefinition @@ -64,5 +64,5 @@ func (s *Server) textDocumentTypeDefinition(params *TypeDefinitionParams) (any, if !result.isInFset(objPos) { return nil, nil } - return s.createLocationFromPos(result.fset, objPos), nil + return result.locationForPos(objPos), nil } diff --git a/tools/spxls/internal/server/document.go b/tools/spxls/internal/server/document.go index 7cb15e10c..2dbc8716f 100644 --- a/tools/spxls/internal/server/document.go +++ b/tools/spxls/internal/server/document.go @@ -28,16 +28,12 @@ func (s *Server) textDocumentDocumentLink(params *DocumentLinkParams) (links []D // Add links for spx resource references. links = slices.Grow(links, len(result.spxResourceRefs)) for _, spxResourceRef := range result.spxResourceRefs { - nodePos := result.fset.Position(spxResourceRef.Node.Pos()) - if nodePos.Filename != spxFile { + if result.nodeFilename(spxResourceRef.Node) != spxFile { continue } target := URI(spxResourceRef.ID.URI()) links = append(links, DocumentLink{ - Range: Range{ - Start: FromGopTokenPosition(nodePos), - End: FromGopTokenPosition(result.fset.Position(spxResourceRef.Node.End())), - }, + Range: result.rangeForNode(spxResourceRef.Node), Target: &target, Data: SpxResourceRefDocumentLinkData{ Kind: spxResourceRef.Kind, @@ -48,15 +44,11 @@ func (s *Server) textDocumentDocumentLink(params *DocumentLinkParams) (links []D // Add links for spx definitions. links = slices.Grow(links, len(result.typeInfo.Defs)+len(result.typeInfo.Uses)) addLinksForIdent := func(ident *gopast.Ident) { - identPos := result.fset.Position(ident.Pos()) - if identPos.Filename != spxFile { + if result.nodeFilename(ident) != spxFile { return } if spxDefs := result.spxDefinitionsForIdent(ident); spxDefs != nil { - identRange := Range{ - Start: FromGopTokenPosition(identPos), - End: FromGopTokenPosition(result.fset.Position(ident.End())), - } + identRange := result.rangeForNode(ident) for _, spxDef := range spxDefs { target := URI(spxDef.ID.String()) links = append(links, DocumentLink{ diff --git a/tools/spxls/internal/server/highlight.go b/tools/spxls/internal/server/highlight.go index 3ebb19522..f31ebb4be 100644 --- a/tools/spxls/internal/server/highlight.go +++ b/tools/spxls/internal/server/highlight.go @@ -144,11 +144,8 @@ func (s *Server) textDocumentDocumentHighlight(params *DocumentHighlightParams) } highlights = append(highlights, DocumentHighlight{ - Range: Range{ - Start: FromGopTokenPosition(result.fset.Position(ident.Pos())), - End: FromGopTokenPosition(result.fset.Position(ident.End())), - }, - Kind: kind, + Range: result.rangeForNode(ident), + Kind: kind, }) return true }) diff --git a/tools/spxls/internal/server/hover.go b/tools/spxls/internal/server/hover.go index daaf06034..b1afed428 100644 --- a/tools/spxls/internal/server/hover.go +++ b/tools/spxls/internal/server/hover.go @@ -18,10 +18,7 @@ func (s *Server) textDocumentHover(params *HoverParams) (*Hover, error) { Kind: Markdown, Value: spxResourceRef.ID.URI().HTML(), }, - Range: Range{ - Start: FromGopTokenPosition(result.fset.Position(spxResourceRef.Node.Pos())), - End: FromGopTokenPosition(result.fset.Position(spxResourceRef.Node.End())), - }, + Range: result.rangeForNode(spxResourceRef.Node), }, nil } @@ -44,9 +41,6 @@ func (s *Server) textDocumentHover(params *HoverParams) (*Hover, error) { Kind: Markdown, Value: hoverContent.String(), }, - Range: Range{ - Start: FromGopTokenPosition(result.fset.Position(ident.Pos())), - End: FromGopTokenPosition(result.fset.Position(ident.End())), - }, + Range: result.rangeForNode(ident), }, nil } diff --git a/tools/spxls/internal/server/implementation.go b/tools/spxls/internal/server/implementation.go index 296635e2c..958822518 100644 --- a/tools/spxls/internal/server/implementation.go +++ b/tools/spxls/internal/server/implementation.go @@ -24,7 +24,7 @@ func (s *Server) textDocumentImplementation(params *ImplementationParams) (any, } } - return s.createLocationFromPos(result.fset, obj.Pos()), nil + return result.locationForPos(obj.Pos()), nil } // findImplementingMethodDefinitions finds the definition locations of all @@ -49,7 +49,7 @@ func (s *Server) findImplementingMethodDefinitions(result *compileResult, iface continue } - implementations = append(implementations, s.createLocationFromPos(result.fset, method.Pos())) + implementations = append(implementations, result.locationForPos(method.Pos())) } } return implementations diff --git a/tools/spxls/internal/server/protocol.go b/tools/spxls/internal/server/protocol.go index 0b0fff66a..d1eefb1dd 100644 --- a/tools/spxls/internal/server/protocol.go +++ b/tools/spxls/internal/server/protocol.go @@ -26,6 +26,14 @@ func FromGopTokenPosition(p goptoken.Position) Position { } } +// RangeForGopTokenPosition returns a [Range] for the given [goptoken.Position]. +func RangeForGopTokenPosition(pos goptoken.Position) Range { + return Range{ + Start: FromGopTokenPosition(pos), + End: FromGopTokenPosition(pos), + } +} + // toURI converts a string to a [URI]. func toURI(s string) *URI { u := URI(s) diff --git a/tools/spxls/internal/server/reference.go b/tools/spxls/internal/server/reference.go index 2e475c06f..075add38a 100644 --- a/tools/spxls/internal/server/reference.go +++ b/tools/spxls/internal/server/reference.go @@ -35,10 +35,10 @@ func (s *Server) textDocumentReferences(params *ReferenceParams) ([]Location, er if defIdent == nil { objPos := obj.Pos() if result.isInFset(objPos) { - locations = append(locations, s.createLocationFromPos(result.fset, objPos)) + locations = append(locations, result.locationForPos(objPos)) } } else if result.isInFset(defIdent.Pos()) { - locations = append(locations, s.createLocationFromIdent(result.fset, defIdent)) + locations = append(locations, result.locationForNode(defIdent)) } } @@ -53,7 +53,7 @@ func (s *Server) findReferenceLocations(result *compileResult, obj types.Object) } locations := make([]Location, 0, len(refIdents)) for _, refIdent := range refIdents { - locations = append(locations, s.createLocationFromIdent(result.fset, refIdent)) + locations = append(locations, result.locationForNode(refIdent)) } return locations } diff --git a/tools/spxls/internal/server/rename.go b/tools/spxls/internal/server/rename.go index d0b86f9f1..55f6ff65f 100644 --- a/tools/spxls/internal/server/rename.go +++ b/tools/spxls/internal/server/rename.go @@ -5,6 +5,7 @@ import ( "go/types" "slices" + "github.com/goplus/builder/tools/spxls/internal/util" gopast "github.com/goplus/gop/ast" ) @@ -31,10 +32,7 @@ func (s *Server) textDocumentPrepareRename(params *PrepareRenameParams) (*Range, return nil, nil } - return &Range{ - Start: FromGopTokenPosition(result.fset.Position(ident.Pos())), - End: FromGopTokenPosition(result.fset.Position(ident.End())), - }, nil + return util.ToPtr(result.rangeForNode(ident)), nil } // See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_rename @@ -65,16 +63,13 @@ func (s *Server) textDocumentRename(params *RenameParams) (*WorkspaceEdit, error return nil, fmt.Errorf("failed to find definition of object %q", obj.Name()) } - defLoc := s.createLocationFromIdent(result.fset, defIdent) + defLoc := result.locationForNode(defIdent) workspaceEdit := WorkspaceEdit{ Changes: map[DocumentURI][]TextEdit{ defLoc.URI: { { - Range: Range{ - Start: FromGopTokenPosition(result.fset.Position(defIdent.Pos())), - End: FromGopTokenPosition(result.fset.Position(defIdent.End())), - }, + Range: result.rangeForNode(defIdent), NewText: params.NewName, }, }, @@ -123,7 +118,7 @@ func (s *Server) spxRenameResourceAtRefs(result *compileResult, id SpxResourceID nodeEnd.Column-- } - documentURI := s.toDocumentURI(nodePos.Filename) + documentURI := result.documentURIs[nodePos.Filename] textEdit := TextEdit{ Range: Range{ Start: FromGopTokenPosition(nodePos), @@ -173,12 +168,9 @@ func (s *Server) spxRenameSpriteResource(result *compileResult, id SpxSpriteReso continue } if tv.Type.String() == "main."+id.SpriteName { - documentURI := s.toDocumentURI(result.fset.Position(expr.Pos()).Filename) + documentURI := result.nodeDocumentURI(expr) textEdit := TextEdit{ - Range: Range{ - Start: FromGopTokenPosition(result.fset.Position(expr.Pos())), - End: FromGopTokenPosition(result.fset.Position(expr.End())), - }, + Range: result.rangeForNode(expr), NewText: newName, } diff --git a/tools/spxls/internal/server/server.go b/tools/spxls/internal/server/server.go index 1d4d56387..b0b2dc887 100644 --- a/tools/spxls/internal/server/server.go +++ b/tools/spxls/internal/server/server.go @@ -8,8 +8,6 @@ import ( "github.com/goplus/builder/tools/spxls/internal/jsonrpc2" "github.com/goplus/builder/tools/spxls/internal/vfs" - gopast "github.com/goplus/gop/ast" - goptoken "github.com/goplus/gop/token" ) // MessageReplier is an interface for sending messages back to the client. @@ -313,27 +311,3 @@ func (s *Server) fromDocumentURI(documentURI DocumentURI) (string, error) { func (s *Server) toDocumentURI(path string) DocumentURI { return DocumentURI(string(s.workspaceRootURI) + path) } - -// createLocationFromPos creates a Location from a position. -func (s *Server) createLocationFromPos(fset *goptoken.FileSet, pos goptoken.Pos) Location { - position := fset.Position(pos) - return Location{ - URI: s.toDocumentURI(position.Filename), - Range: Range{ - Start: FromGopTokenPosition(position), - End: FromGopTokenPosition(position), - }, - } -} - -// createLocationFromIdent creates a Location from an ident position. -func (s *Server) createLocationFromIdent(fset *goptoken.FileSet, ident *gopast.Ident) Location { - identPos := fset.Position(ident.Pos()) - return Location{ - URI: s.toDocumentURI(identPos.Filename), - Range: Range{ - Start: FromGopTokenPosition(identPos), - End: FromGopTokenPosition(fset.Position(ident.End())), - }, - } -}