Skip to content

Fix action removes ticks from TemplateHaskellQuotes (#628) #3260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 9, 2022
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -839,8 +839,13 @@ suggestAddTypeAnnotationToSatisfyContraints sourceOpt Diagnostic{_range=_range,.
in [( title, edits )]


suggestReplaceIdentifier :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
suggestReplaceIdentifier contents Diagnostic{_range=_range,..}
-- | GHC strips out backticks in case of infix functions as well as single quote
-- in case of quoted name when using TemplateHaskellQuotes. Which is not desired.
--
-- For example:
-- 1.
--
-- @
-- File.hs:52:41: error:
-- * Variable not in scope:
-- suggestAcion :: Maybe T.Text -> Range -> Range
Expand All @@ -852,6 +857,27 @@ suggestReplaceIdentifier contents Diagnostic{_range=_range,..}
-- ‘T.isInfixOf’ (imported from Data.Text),
-- ‘T.isSuffixOf’ (imported from Data.Text)
-- Module ‘Data.Text’ does not export ‘isPrfixOf’.
-- @
--
-- * action: \`suggestAcion\` will be renamed to \`suggestAction\` keeping back ticks around the function
--
-- 2.
--
-- @
-- import Language.Haskell.TH (Name)
-- foo :: Name
-- foo = 'bread
--
-- File.hs:8:7: error:
-- Not in scope: ‘bread’
-- * Perhaps you meant one of these:
-- ‘break’ (imported from Prelude), ‘read’ (imported from Prelude)
-- * In the Template Haskell quotation 'bread
-- @
--
-- * action: 'bread will be renamed to 'break keeping single quote on beginning of name
suggestReplaceIdentifier :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
suggestReplaceIdentifier contents Diagnostic{_range=_range,..}
| renameSuggestions@(_:_) <- extractRenamableTerms _message
= [ ("Replace with ‘" <> name <> "’", [mkRenameEdit contents _range name]) | name <- renameSuggestions ]
| otherwise = []
Expand Down Expand Up @@ -1771,15 +1797,17 @@ extractDoesNotExportModuleName x


mkRenameEdit :: Maybe T.Text -> Range -> T.Text -> TextEdit
mkRenameEdit contents range name =
if maybeIsInfixFunction == Just True
then TextEdit range ("`" <> name <> "`")
else TextEdit range name
mkRenameEdit contents range name
| maybeIsInfixFunction == Just True = TextEdit range ("`" <> name <> "`")
| maybeIsTemplateFunction == Just True = TextEdit range ("'" <> name)
| otherwise = TextEdit range name
where
maybeIsInfixFunction = do
curr <- textInRange range <$> contents
pure $ "`" `T.isPrefixOf` curr && "`" `T.isSuffixOf` curr

maybeIsTemplateFunction = do
curr <- textInRange range <$> contents
pure $ "'" `T.isPrefixOf` curr

-- | Extract the type and surround it in parentheses except in obviously safe cases.
--
Expand Down
22 changes: 22 additions & 0 deletions plugins/hls-refactor-plugin/test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,28 @@ renameActionTests = testGroup "rename actions"
, "foo x y = x `monus` y"
]
liftIO $ expectedContentAfterAction @=? contentAfterAction
, testSession "change template function" $ do
let content = T.unlines
[ "{-# LANGUAGE TemplateHaskellQuotes #-}"
, "module Testing where"
, "import Language.Haskell.TH (Name)"
, "foo :: Name"
, "foo = 'bread"
]
doc <- createDoc "Testing.hs" "haskell" content
diags <- waitForDiagnostics
actionsOrCommands <- getCodeActions doc (Range (Position 4 6) (Position 4 12))
[fixTypo] <- pure [action | InR action@CodeAction{ _title = actionTitle } <- actionsOrCommands, "break" `T.isInfixOf` actionTitle ]
executeCodeAction fixTypo
contentAfterAction <- documentContents doc
let expectedContentAfterAction = T.unlines
[ "{-# LANGUAGE TemplateHaskellQuotes #-}"
, "module Testing where"
, "import Language.Haskell.TH (Name)"
, "foo :: Name"
, "foo = 'break"
]
liftIO $ expectedContentAfterAction @=? contentAfterAction
]

typeWildCardActionTests :: TestTree
Expand Down