Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions internal/ls/findallreferences.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ls
import (
"cmp"
"context"
"errors"
"fmt"
"slices"
"strings"
Expand All @@ -16,6 +17,8 @@ import (
"github.com/microsoft/typescript-go/internal/compiler"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/debug"
"github.com/microsoft/typescript-go/internal/diagnostics"
"github.com/microsoft/typescript-go/internal/locale"
"github.com/microsoft/typescript-go/internal/ls/lsconv"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/scanner"
Expand Down Expand Up @@ -582,8 +585,18 @@ func (l *LanguageService) ProvideSymbolsAndEntries(ctx context.Context, uri lspr
position := int(l.converters.LineAndCharacterToPosition(sourceFile, documentPosition))

node := astnav.GetTouchingPropertyName(sourceFile, position)
if isRename && node.Kind != ast.KindIdentifier {
return node, nil, false

// For rename, check if node could potentially be eligible after adjustment
// This is a preliminary check; full validation happens later
if isRename {
// Apply location adjustment to handle keywords shifting to identifiers
adjustedNode := getAdjustedLocation(node, true /*forRename*/, sourceFile)
// Only reject nodes that are definitely not renameable (like syntax tokens)
// Allow nodes that might be renameable (identifiers, literals, this, etc.)
// The detailed symbol validation will happen later in the flow
if !nodeIsEligibleForRename(adjustedNode) {
return adjustedNode, nil, false
}
}

var options refOptions
Expand Down Expand Up @@ -655,8 +668,10 @@ func (l *LanguageService) getImplementationReferenceEntries(ctx context.Context,
}

func (l *LanguageService) ProvideRenameFromSymbolAndEntries(ctx context.Context, params *lsproto.RenameParams, originalNode *ast.Node, symbolsAndEntries []*SymbolAndEntries) (lsproto.WorkspaceEditOrNull, error) {
if originalNode.Kind != ast.KindIdentifier {
return lsproto.WorkspaceEditOrNull{}, nil
// Check if ProvideSymbolsAndEntries returned ok=false or no symbols found
// Return an error to inform the user that rename is not available
if symbolsAndEntries == nil || len(symbolsAndEntries) == 0 {
return lsproto.WorkspaceEditOrNull{}, errors.New(diagnostics.You_cannot_rename_this_element.Localize(locale.FromContext(ctx)))
}

program := l.GetProgram()
Expand Down
42 changes: 42 additions & 0 deletions internal/ls/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,48 @@ func getAdjustedLocationForExportDeclaration(node *ast.ExportDeclaration, forRen
return nil
}

// nodeIsEligibleForRename checks if a node can be renamed.
// This matches the TypeScript implementation in services/rename.ts
func nodeIsEligibleForRename(node *ast.Node) bool {
switch node.Kind {
case ast.KindIdentifier,
ast.KindPrivateIdentifier,
ast.KindStringLiteral,
ast.KindNoSubstitutionTemplateLiteral,
ast.KindThisKeyword:
return true
case ast.KindNumericLiteral:
// For numeric literals, check if they're used as property names
parent := node.Parent
if parent == nil {
return false
}
switch parent.Kind {
case ast.KindPropertyDeclaration,
ast.KindPropertySignature,
ast.KindPropertyAssignment,
ast.KindEnumMember,
ast.KindMethodDeclaration,
ast.KindMethodSignature,
ast.KindGetAccessor,
ast.KindSetAccessor,
ast.KindModuleDeclaration:
return parent.Name() == node
case ast.KindElementAccessExpression:
return parent.AsElementAccessExpression().ArgumentExpression == node
case ast.KindComputedPropertyName:
return true
case ast.KindLiteralType:
grandParent := parent.Parent
return grandParent != nil && grandParent.Kind == ast.KindIndexedAccessType
default:
return false
}
default:
return false
}
}

func symbolFlagsHaveMeaning(flags ast.SymbolFlags, meaning ast.SemanticMeaning) bool {
if meaning == ast.SemanticMeaningAll {
return true
Expand Down