Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 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
21 changes: 17 additions & 4 deletions internal/ls/findallreferences.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ 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/ls/lsconv"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/scanner"
Expand Down Expand Up @@ -582,8 +583,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 +666,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{}, fmt.Errorf("%s", diagnostics.You_cannot_rename_this_element)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot you need to localize this diagnostic, not just print it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d8a16fb - now using diagnostics.You_cannot_rename_this_element.Localize(locale.FromContext(ctx)) to properly localize the error message.

}

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