Skip to content

Commit 42ad67c

Browse files
author
izuzu
committed
Allow renaming when cursor is at end of alias
1 parent 4c4b25e commit 42ad67c

3 files changed

Lines changed: 38 additions & 12 deletions

File tree

plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ prepareRenameProvider state _pluginId (PrepareRenameParams (TextDocumentIdentifi
110110
HAR{hieAst} <- handleGetHieAst state nfp
111111
let spansWithNamesUnderCursor =
112112
[ srcSpan
113-
| (names, srcSpan) <- getNamesSpansAtPoint' hieAst pos
113+
| (names, srcSpan) <- getNamesSpansAtPoint' hieAst lspPos
114114
, not (null names)]
115115
-- When this handler says that rename is invalid, VSCode shows "The element can't be renamed"
116116
-- and doesn't even allow you to create full rename request.
@@ -304,10 +304,10 @@ getCodePointPosition state nfp pos = do
304304
"The cursor position is inside a Unicode surrogate pair."
305305
Just codePointPosition -> pure codePointPosition
306306

307-
-- TODO: 'getNamesAtPos' passes the LSP 'Position' directly to 'pointCommand',
307+
-- FIXME: 'getNamesAtPos' passes the LSP 'Position' directly to 'pointCommand',
308308
-- which treats '_character' as a code-point column. This is incorrect for
309309
-- files with supplementary-plane Unicode characters before the cursor.
310-
-- Fixing it requires changes in ghcide, not here.
310+
-- Fixing it requires changes to 'pointCommand' in ghcide, not here.
311311
getNamesAtPos :: MonadIO m => IdeState -> NormalizedFilePath -> Position -> ExceptT PluginError m [Name]
312312
getNamesAtPos state nfp pos = do
313313
HAR{hieAst} <- handleGetHieAst state nfp
@@ -352,11 +352,13 @@ collectWith :: (Hashable a, Eq b) => (a -> b) -> HashSet a -> [(b, HashSet a)]
352352
collectWith f = map (\(a :| as) -> (f a, HS.fromList (a:as))) . groupWith f . HS.toList
353353

354354
-- | A variant 'getNamesAtPoint' that does not expect a 'PositionMapping'
355+
-- FIXME: The use of 'pointCommand' is problematic. See 'getNamesAtPos' above.
355356
getNamesAtPoint' :: HieASTs a -> Position -> [Name]
356357
getNamesAtPoint' hf pos =
357358
concat $ pointCommand hf pos (rights . M.keys . getNodeIds)
358359

359360
-- | A variant of `getNamesAtPoint'` that also returns source spans.
361+
-- FIXME: The use of 'pointCommand' is problematic. See 'getNamesAtPos' above.
360362
getNamesSpansAtPoint' :: HieASTs a -> Position -> [([Name], RealSrcSpan)]
361363
getNamesSpansAtPoint' hf pos =
362364
pointCommand hf pos $

plugins/hls-rename-plugin/src/Ide/Plugin/Rename/ImportAlias.hs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ findAliasDeclAtPos pos imports = listToMaybe $ do
174174
Just locatedAlias <- [ideclAs importDecl]
175175
RealSrcSpan aliasDeclSpan _ <- [getLoc locatedAlias]
176176
let aliasDeclRange = realSrcSpanToCodePointRange aliasDeclSpan
177-
guard (rangeContainsPosition aliasDeclRange pos)
177+
guard (rangeContainsPositionInclusive aliasDeclRange pos)
178178
let aliasModuleName = unLoc (ideclName importDecl)
179179
aliasName = unLoc locatedAlias
180180
aliasIsShared = length (filter (== aliasName) allAliases) > 1
@@ -194,12 +194,12 @@ findAliasUseAtPos pos imports hsDecls =
194194
Qual qualifier _ <- [unLoc locatedRdrName]
195195
RealSrcSpan qualifiedNameSpan _ <- [getLoc locatedRdrName]
196196
let qualifiedNameRange = realSrcSpanToCodePointRange qualifiedNameSpan
197-
guard (rangeContainsPosition qualifiedNameRange pos)
197+
guard (rangeContainsPositionInclusive qualifiedNameRange pos)
198198
let qualifierLength = fromIntegral (moduleNameLength qualifier)
199199
qualifierStart = qualifiedNameRange ^. VFS.start
200200
qualifierRange = qualifiedNameRange
201201
& VFS.end .~ (qualifierStart & VFS.character +~ qualifierLength)
202-
guard (rangeContainsPosition qualifierRange pos)
202+
guard (rangeContainsPositionInclusive qualifierRange pos)
203203
[(qualifierRange, qualifier)]
204204
in case qualifiersAtPos of
205205
[] -> Nothing
@@ -295,16 +295,18 @@ ambiguousAliasErrorMessage _ = ""
295295
---------------------------------------------------------------------------------------------------
296296
-- Utility functions
297297

298-
-- | Check whether a 'CodePointRange' contains a 'CodePointPosition'
299-
-- (inclusive start, exclusive end).
300-
rangeContainsPosition :: VFS.CodePointRange -> VFS.CodePointPosition -> Bool
301-
rangeContainsPosition
298+
-- | Check whether a 'CodePointRange' contains a 'CodePointPosition' (inclusive
299+
-- start, inclusive end).
300+
-- NOTE: The use of inclusive end allows the user to place the cursor at the end
301+
-- of an import alias and rename it.
302+
rangeContainsPositionInclusive :: VFS.CodePointRange -> VFS.CodePointPosition -> Bool
303+
rangeContainsPositionInclusive
302304
(VFS.CodePointRange
303305
(VFS.CodePointPosition startLine startColumn)
304306
(VFS.CodePointPosition endLine endColumn))
305307
(VFS.CodePointPosition posLine posColumn)
306308
= (posLine > startLine || (posLine == startLine && posColumn >= startColumn))
307-
&& (posLine < endLine || (posLine == endLine && posColumn < endColumn))
309+
&& (posLine < endLine || (posLine == endLine && posColumn <= endColumn))
308310

309311
-- | Build a 'TextEdit' from a 'VFS.CodePointRange' and replacement text.
310312
-- Returns @Nothing@ if the range is out of bounds in the VFS.

plugins/hls-rename-plugin/test/Main.hs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,28 @@ prepareRenameTests = testGroup "PrepareRename"
3737
result <- prepareRename doc (Position 0 9)
3838
liftIO $ result @?= InR Null
3939

40+
, testCase "Import alias in declaration" $ runRenameSession "" $ do
41+
doc <- openDoc "PrepareRename.hs" "haskell"
42+
void waitForBuildQueue
43+
let expected = InL (PrepareRenameResult (InL (Range (Position 2 24) (Position 2 25))))
44+
resultAtStart <- prepareRename doc (Position 2 24)
45+
liftIO $ resultAtStart @?= expected
46+
resultAtEnd <- prepareRename doc (Position 2 25)
47+
liftIO $ resultAtEnd @?= expected
48+
resultOutside <- prepareRename doc (Position 2 26)
49+
liftIO $ resultOutside /= expected @? "Cursor is outside alias"
50+
51+
, testCase "Import alias at use site" $ runRenameSession "" $ do
52+
doc <- openDoc "PrepareRename.hs" "haskell"
53+
void waitForBuildQueue
54+
let expected = InL (PrepareRenameResult (InL (Range (Position 10 14) (Position 10 15))))
55+
resultAtStart <- prepareRename doc (Position 10 14)
56+
liftIO $ resultAtStart @?= expected
57+
resultAtEnd <- prepareRename doc (Position 10 15)
58+
liftIO $ resultAtEnd @?= expected
59+
resultOutside <- prepareRename doc (Position 10 16)
60+
liftIO $ resultOutside /= expected @? "Cursor is outside qualifier"
61+
4062
, testCase "Function name" $ runRenameSession "" $ do
4163
doc <- openDoc "PrepareRename.hs" "haskell"
4264
void waitForBuildQueue
@@ -96,7 +118,7 @@ renameTests = testGroup "Identifier"
96118
, goldenWithRename "Import alias declaration" "ImportAlias" $ \doc ->
97119
rename doc (Position 1 14) "G"
98120
, goldenWithRename "Import alias at use site" "ImportAlias" $ \doc ->
99-
rename doc (Position 5 6) "G"
121+
rename doc (Position 5 10) "G"
100122
, goldenWithRename "Import alias declaration (shared by unrelated imports)" "ImportAliasShared" $ \doc ->
101123
rename doc (Position 1 31) "Maybe"
102124
, goldenWithRename "Import alias at use site (shared by unrelated imports)" "ImportAliasShared" $ \doc ->

0 commit comments

Comments
 (0)