@@ -927,8 +927,10 @@ extension SwiftLanguageService {
927
927
return categorizedRanges. compactMap { SyntacticRenameName ( $0, in: snapshot, keys: keys, values: values) }
928
928
}
929
929
930
- /// If `position` is on an argument label or a parameter name, find the position of the function's base name.
931
- private func findFunctionBaseNamePosition( of position: Position , in snapshot: DocumentSnapshot ) async -> Position ? {
930
+ /// If `position` is on an argument label or a parameter name, find the range from the function's base name to the
931
+ /// token that terminates the arguments or parameters of the function. Typically, this is the closing ')' but it can
932
+ /// also be a closing ']' for subscripts or the end of a trailing closure.
933
+ private func findFunctionLikeRange( of position: Position , in snapshot: DocumentSnapshot ) async -> Range < Position > ? {
932
934
let tree = await self . syntaxTreeManager. syntaxTree ( for: snapshot)
933
935
guard let absolutePosition = snapshot. absolutePosition ( of: position) else {
934
936
return nil
@@ -939,22 +941,28 @@ extension SwiftLanguageService {
939
941
940
942
// The node that contains the function's base name. This might be an expression like `self.doStuff`.
941
943
// The start position of the last token in this node will be used as the base name position.
942
- var baseNode : Syntax ? = nil
944
+ var startToken : TokenSyntax ? = nil
945
+ var endToken : TokenSyntax ? = nil
943
946
944
947
switch token. keyPathInParent {
945
948
case \LabeledExprSyntax . label:
946
949
let callLike = token. parent ( as: LabeledExprSyntax . self) ? . parent ( as: LabeledExprListSyntax . self) ? . parent
947
950
switch callLike? . as ( SyntaxEnum . self) {
948
951
case . attribute( let attribute) :
949
- baseNode = Syntax ( attribute. attributeName)
952
+ startToken = attribute. attributeName. lastToken ( viewMode: . sourceAccurate)
953
+ endToken = attribute. lastToken ( viewMode: . sourceAccurate)
950
954
case . functionCallExpr( let functionCall) :
951
- baseNode = Syntax ( functionCall. calledExpression)
955
+ startToken = functionCall. calledExpression. lastToken ( viewMode: . sourceAccurate)
956
+ endToken = functionCall. lastToken ( viewMode: . sourceAccurate)
952
957
case . macroExpansionDecl( let macroExpansionDecl) :
953
- baseNode = Syntax ( macroExpansionDecl. macroName)
958
+ startToken = macroExpansionDecl. macroName
959
+ endToken = macroExpansionDecl. lastToken ( viewMode: . sourceAccurate)
954
960
case . macroExpansionExpr( let macroExpansionExpr) :
955
- baseNode = Syntax ( macroExpansionExpr. macroName)
961
+ startToken = macroExpansionExpr. macroName
962
+ endToken = macroExpansionExpr. lastToken ( viewMode: . sourceAccurate)
956
963
case . subscriptCallExpr( let subscriptCall) :
957
- baseNode = Syntax ( subscriptCall. leftSquare)
964
+ startToken = subscriptCall. leftSquare
965
+ endToken = subscriptCall. lastToken ( viewMode: . sourceAccurate)
958
966
default :
959
967
break
960
968
}
@@ -967,16 +975,20 @@ extension SwiftLanguageService {
967
975
if let functionSignature = parameterClause? . parent ( as: FunctionSignatureSyntax . self) {
968
976
switch functionSignature. parent? . as ( SyntaxEnum . self) {
969
977
case . functionDecl( let functionDecl) :
970
- baseNode = Syntax ( functionDecl. name)
978
+ startToken = functionDecl. name
979
+ endToken = functionSignature. parameterClause. rightParen
971
980
case . initializerDecl( let initializerDecl) :
972
- baseNode = Syntax ( initializerDecl. initKeyword)
981
+ startToken = initializerDecl. initKeyword
982
+ endToken = functionSignature. parameterClause. rightParen
973
983
case . macroDecl( let macroDecl) :
974
- baseNode = Syntax ( macroDecl. name)
984
+ startToken = macroDecl. name
985
+ endToken = functionSignature. parameterClause. rightParen
975
986
default :
976
987
break
977
988
}
978
989
} else if let subscriptDecl = parameterClause? . parent ( as: SubscriptDeclSyntax . self) {
979
- baseNode = Syntax ( subscriptDecl. subscriptKeyword)
990
+ startToken = subscriptDecl. subscriptKeyword
991
+ endToken = subscriptDecl. parameterClause. rightParen
980
992
}
981
993
case \DeclNameArgumentSyntax . name:
982
994
let declReference =
@@ -985,21 +997,24 @@ extension SwiftLanguageService {
985
997
. parent ( as: DeclNameArgumentListSyntax . self) ?
986
998
. parent ( as: DeclNameArgumentsSyntax . self) ?
987
999
. parent ( as: DeclReferenceExprSyntax . self)
988
- baseNode = Syntax ( declReference? . baseName)
1000
+ startToken = declReference? . baseName
1001
+ endToken = declReference? . argumentNames? . rightParen
989
1002
default :
990
1003
break
991
1004
}
992
1005
993
- if let lastToken = baseNode? . lastToken ( viewMode: . sourceAccurate) ,
994
- let position = snapshot. position ( of: lastToken. positionAfterSkippingLeadingTrivia)
1006
+ if let startToken, let endToken,
1007
+ let startPosition = snapshot. position ( of: startToken. positionAfterSkippingLeadingTrivia) ,
1008
+ let endPosition = snapshot. position ( of: endToken. endPositionBeforeTrailingTrivia)
995
1009
{
996
- return position
1010
+ return startPosition ..< endPosition
997
1011
}
998
1012
return nil
999
1013
}
1000
1014
1001
1015
/// When the user requested a rename at `position` in `snapshot`, determine the position at which the rename should be
1002
- /// performed internally and USR of the symbol to rename.
1016
+ /// performed internally, the USR of the symbol to rename and the range to rename that should be returned to the
1017
+ /// editor.
1003
1018
///
1004
1019
/// This is necessary to adjust the rename position when renaming function parameters. For example when invoking
1005
1020
/// rename on `x` in `foo(x:)`, we need to perform a rename of `foo` in sourcekitd so that we can rename the function
@@ -1010,41 +1025,49 @@ extension SwiftLanguageService {
1010
1025
/// file. In this case, there is no base name that refers to the initializer of `MyStruct`. When `position` is `nil`
1011
1026
/// a pure index-based rename from the usr USR or `symbolDetails` needs to be performed and no `relatedIdentifiers`
1012
1027
/// request can be used to rename symbols in the current file.
1028
+ ///
1029
+ /// `position` might be at a different location in the source file than where the user initiated the rename.
1030
+ /// For example, `position` could point to the definition of a function within the file when rename was initiated on
1031
+ /// a call.
1032
+ ///
1033
+ /// If a `range` is returned, this is an expanded range that contains both the symbol to rename as well as the
1034
+ /// position at which the rename was requested. For example, when rename was initiated from the argument label of a
1035
+ /// function call, the `range` will contain the entire function call from the base name to the closing `)`.
1013
1036
func symbolToRename(
1014
1037
at position: Position ,
1015
1038
in snapshot: DocumentSnapshot
1016
- ) async -> ( position: Position ? , usr: String ? ) {
1039
+ ) async -> ( position: Position ? , usr: String ? , functionLikeRange : Range < Position > ? ) {
1017
1040
let symbolInfo = try ? await self . symbolInfo (
1018
1041
SymbolInfoRequest ( textDocument: TextDocumentIdentifier ( snapshot. uri) , position: position)
1019
1042
)
1020
1043
1021
- guard let baseNamePosition = await findFunctionBaseNamePosition ( of: position, in: snapshot) else {
1022
- return ( position, symbolInfo? . only? . usr)
1044
+ guard let functionLikeRange = await findFunctionLikeRange ( of: position, in: snapshot) else {
1045
+ return ( position, symbolInfo? . only? . usr, nil )
1023
1046
}
1024
1047
if let onlySymbol = symbolInfo? . only, onlySymbol. kind == . constructor {
1025
1048
// We have a rename like `MyStruct(x: 1)`, invoked from `x`.
1026
1049
if let bestLocalDeclaration = onlySymbol. bestLocalDeclaration, bestLocalDeclaration. uri == snapshot. uri {
1027
1050
// If the initializer is declared within the same file, we can perform rename in the current file based on
1028
1051
// the declaration's location.
1029
- return ( bestLocalDeclaration. range. lowerBound, onlySymbol. usr)
1052
+ return ( bestLocalDeclaration. range. lowerBound, onlySymbol. usr, functionLikeRange )
1030
1053
}
1031
1054
// Otherwise, we don't have a reference to the base name of the initializer and we can't use related
1032
1055
// identifiers to perform the rename.
1033
1056
// Return `nil` for the position to perform a pure index-based rename.
1034
- return ( nil , onlySymbol. usr)
1057
+ return ( nil , onlySymbol. usr, functionLikeRange )
1035
1058
}
1036
1059
// Adjust the symbol info to the symbol info of the base name.
1037
1060
// This ensures that we get the symbol info of the function's base instead of the parameter.
1038
1061
let baseNameSymbolInfo = try ? await self . symbolInfo (
1039
- SymbolInfoRequest ( textDocument: TextDocumentIdentifier ( snapshot. uri) , position: baseNamePosition )
1062
+ SymbolInfoRequest ( textDocument: TextDocumentIdentifier ( snapshot. uri) , position: functionLikeRange . lowerBound )
1040
1063
)
1041
- return ( baseNamePosition , baseNameSymbolInfo? . only? . usr)
1064
+ return ( functionLikeRange . lowerBound , baseNameSymbolInfo? . only? . usr, functionLikeRange )
1042
1065
}
1043
1066
1044
1067
public func rename( _ request: RenameRequest ) async throws -> ( edits: WorkspaceEdit , usr: String ? ) {
1045
1068
let snapshot = try self . documentManager. latestSnapshot ( request. textDocument. uri)
1046
1069
1047
- let ( renamePosition, usr) = await symbolToRename ( at: request. position, in: snapshot)
1070
+ let ( renamePosition, usr, _ ) = await symbolToRename ( at: request. position, in: snapshot)
1048
1071
guard let renamePosition else {
1049
1072
return ( edits: WorkspaceEdit ( ) , usr: usr)
1050
1073
}
@@ -1342,7 +1365,7 @@ extension SwiftLanguageService {
1342
1365
) async throws -> ( prepareRename: PrepareRenameResponse , usr: String ? ) ? {
1343
1366
let snapshot = try self . documentManager. latestSnapshot ( request. textDocument. uri)
1344
1367
1345
- let ( renamePosition, usr) = await symbolToRename ( at: request. position, in: snapshot)
1368
+ let ( renamePosition, usr, functionLikeRange ) = await symbolToRename ( at: request. position, in: snapshot)
1346
1369
guard let renamePosition else {
1347
1370
return nil
1348
1371
}
@@ -1358,11 +1381,11 @@ extension SwiftLanguageService {
1358
1381
if name. hasSuffix ( " () " ) {
1359
1382
name = String ( name. dropLast ( 2 ) )
1360
1383
}
1361
- guard let range = response. relatedIdentifiers. first ( where: { $0. range. contains ( renamePosition) } ) ? . range
1384
+ guard let relatedIdentRange = response. relatedIdentifiers. first ( where: { $0. range. contains ( renamePosition) } ) ? . range
1362
1385
else {
1363
1386
return nil
1364
1387
}
1365
- return ( PrepareRenameResponse ( range: range , placeholder: name) , usr)
1388
+ return ( PrepareRenameResponse ( range: functionLikeRange ?? relatedIdentRange , placeholder: name) , usr)
1366
1389
}
1367
1390
}
1368
1391
0 commit comments