Skip to content

Commit df7139c

Browse files
author
Santiago Weight
committed
refact: extract FillTypeWildcard
1 parent a8035c7 commit df7139c

File tree

2 files changed

+77
-69
lines changed

2 files changed

+77
-69
lines changed

plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs

+1-69
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ import GHC (AddEpAnn (Ad
103103
LEpaComment,
104104
LocatedA)
105105
import Development.IDE.Plugin.Plugins.ImportUtils
106+
import Development.IDE.Plugin.Plugins.FillTypeWildcard (suggestFillTypeWildcard)
106107
#else
107108
import Language.Haskell.GHC.ExactPrint.Types (Annotation (annsDP),
108109
DeltaPos,
@@ -916,17 +917,6 @@ newDefinitionAction IdeOptions {..} parsedModule Range {_start} name typ
916917
ParsedModule {pm_parsed_source = L _ HsModule {hsmodDecls}} = parsedModule
917918

918919

919-
suggestFillTypeWildcard :: Diagnostic -> [(T.Text, TextEdit)]
920-
suggestFillTypeWildcard Diagnostic{_range=_range,..}
921-
-- Foo.hs:3:8: error:
922-
-- * Found type wildcard `_' standing for `p -> p1 -> p'
923-
924-
| "Found type wildcard" `T.isInfixOf` _message
925-
, " standing for " `T.isInfixOf` _message
926-
, typeSignature <- extractWildCardTypeSignature _message
927-
= [("Use type signature: ‘" <> typeSignature <> "", TextEdit _range typeSignature)]
928-
| otherwise = []
929-
930920
{- Handles two variants with different formatting
931921
932922
1. Could not find module ‘Data.Cha’
@@ -1846,64 +1836,6 @@ mkRenameEdit contents range name
18461836
curr <- textInRange range <$> contents
18471837
pure $ "'" `T.isPrefixOf` curr
18481838

1849-
-- | Extract the type and surround it in parentheses except in obviously safe cases.
1850-
--
1851-
-- Inferring when parentheses are actually needed around the type signature would
1852-
-- require understanding both the precedence of the context of the hole and of
1853-
-- the signature itself. Inserting them (almost) unconditionally is ugly but safe.
1854-
extractWildCardTypeSignature :: T.Text -> T.Text
1855-
extractWildCardTypeSignature msg
1856-
| enclosed || not isApp || isToplevelSig = sig
1857-
| otherwise = "(" <> sig <> ")"
1858-
where
1859-
msgSigPart = snd $ T.breakOnEnd "standing for " msg
1860-
(sig, rest) = T.span (/='') . T.dropWhile (=='') . T.dropWhile (/='') $ msgSigPart
1861-
-- If we're completing something like ‘foo :: _’ parens can be safely omitted.
1862-
isToplevelSig = errorMessageRefersToToplevelHole rest
1863-
-- Parenthesize type applications, e.g. (Maybe Char).
1864-
isApp = T.any isSpace sig
1865-
-- Do not add extra parentheses to lists, tuples and already parenthesized types.
1866-
enclosed = not (T.null sig) && (T.head sig, T.last sig) `elem` [('(', ')'), ('[', ']')]
1867-
1868-
-- | Detect whether user wrote something like @foo :: _@ or @foo :: (_, Int)@.
1869-
-- The former is considered toplevel case for which the function returns 'True',
1870-
-- the latter is not toplevel and the returned value is 'False'.
1871-
--
1872-
-- When type hole is at toplevel then there’s a line starting with
1873-
-- "• In the type signature" which ends with " :: _" like in the
1874-
-- following snippet:
1875-
--
1876-
-- source/library/Language/Haskell/Brittany/Internal.hs:131:13: error:
1877-
-- • Found type wildcard ‘_’ standing for ‘HsDecl GhcPs’
1878-
-- To use the inferred type, enable PartialTypeSignatures
1879-
-- • In the type signature: decl :: _
1880-
-- In an equation for ‘splitAnnots’:
1881-
-- splitAnnots m@HsModule {hsmodAnn, hsmodDecls}
1882-
-- = undefined
1883-
-- where
1884-
-- ann :: SrcSpanAnnA
1885-
-- decl :: _
1886-
-- L ann decl = head hsmodDecls
1887-
-- • Relevant bindings include
1888-
-- [REDACTED]
1889-
--
1890-
-- When type hole is not at toplevel there’s a stack of where
1891-
-- the hole was located ending with "In the type signature":
1892-
--
1893-
-- source/library/Language/Haskell/Brittany/Internal.hs:130:20: error:
1894-
-- • Found type wildcard ‘_’ standing for ‘GhcPs’
1895-
-- To use the inferred type, enable PartialTypeSignatures
1896-
-- • In the first argument of ‘HsDecl’, namely ‘_’
1897-
-- In the type ‘HsDecl _’
1898-
-- In the type signature: decl :: HsDecl _
1899-
-- • Relevant bindings include
1900-
-- [REDACTED]
1901-
errorMessageRefersToToplevelHole :: T.Text -> Bool
1902-
errorMessageRefersToToplevelHole msg =
1903-
not (T.null prefix) && " :: _" `T.isSuffixOf` T.takeWhile (/= '\n') rest
1904-
where
1905-
(prefix, rest) = T.breakOn "• In the type signature:" msg
1906-
19071839
extractRenamableTerms :: T.Text -> [T.Text]
19081840
extractRenamableTerms msg
19091841
-- Account for both "Variable not in scope" and "Not in scope"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
module Development.IDE.Plugin.Plugins.FillTypeWildcard
2+
( suggestFillTypeWildcard
3+
) where
4+
5+
import Data.Char
6+
import qualified Data.Text as T
7+
import Language.LSP.Types (Diagnostic (..),
8+
TextEdit (TextEdit))
9+
10+
suggestFillTypeWildcard :: Diagnostic -> [(T.Text, TextEdit)]
11+
suggestFillTypeWildcard Diagnostic{_range=_range,..}
12+
-- Foo.hs:3:8: error:
13+
-- * Found type wildcard `_' standing for `p -> p1 -> p'
14+
| "Found type wildcard" `T.isInfixOf` _message
15+
, " standing for " `T.isInfixOf` _message
16+
, typeSignature <- extractWildCardTypeSignature _message
17+
= [("Use type signature: ‘" <> typeSignature <> "", TextEdit _range typeSignature)]
18+
| otherwise = []
19+
20+
-- | Extract the type and surround it in parentheses except in obviously safe cases.
21+
--
22+
-- Inferring when parentheses are actually needed around the type signature would
23+
-- require understanding both the precedence of the context of the hole and of
24+
-- the signature itself. Inserting them (almost) unconditionally is ugly but safe.
25+
extractWildCardTypeSignature :: T.Text -> T.Text
26+
extractWildCardTypeSignature msg
27+
| enclosed || not isApp || isToplevelSig = sig
28+
| otherwise = "(" <> sig <> ")"
29+
where
30+
msgSigPart = snd $ T.breakOnEnd "standing for " msg
31+
(sig, rest) = T.span (/='') . T.dropWhile (=='') . T.dropWhile (/='') $ msgSigPart
32+
-- If we're completing something like ‘foo :: _’ parens can be safely omitted.
33+
isToplevelSig = errorMessageRefersToToplevelHole rest
34+
-- Parenthesize type applications, e.g. (Maybe Char).
35+
isApp = T.any isSpace sig
36+
-- Do not add extra parentheses to lists, tuples and already parenthesized types.
37+
enclosed = not (T.null sig) && (T.head sig, T.last sig) `elem` [('(', ')'), ('[', ']')]
38+
39+
-- | Detect whether user wrote something like @foo :: _@ or @foo :: (_, Int)@.
40+
-- The former is considered toplevel case for which the function returns 'True',
41+
-- the latter is not toplevel and the returned value is 'False'.
42+
--
43+
-- When type hole is at toplevel then there’s a line starting with
44+
-- "• In the type signature" which ends with " :: _" like in the
45+
-- following snippet:
46+
--
47+
-- source/library/Language/Haskell/Brittany/Internal.hs:131:13: error:
48+
-- • Found type wildcard ‘_’ standing for ‘HsDecl GhcPs’
49+
-- To use the inferred type, enable PartialTypeSignatures
50+
-- • In the type signature: decl :: _
51+
-- In an equation for ‘splitAnnots’:
52+
-- splitAnnots m@HsModule {hsmodAnn, hsmodDecls}
53+
-- = undefined
54+
-- where
55+
-- ann :: SrcSpanAnnA
56+
-- decl :: _
57+
-- L ann decl = head hsmodDecls
58+
-- • Relevant bindings include
59+
-- [REDACTED]
60+
--
61+
-- When type hole is not at toplevel there’s a stack of where
62+
-- the hole was located ending with "In the type signature":
63+
--
64+
-- source/library/Language/Haskell/Brittany/Internal.hs:130:20: error:
65+
-- • Found type wildcard ‘_’ standing for ‘GhcPs’
66+
-- To use the inferred type, enable PartialTypeSignatures
67+
-- • In the first argument of ‘HsDecl’, namely ‘_’
68+
-- In the type ‘HsDecl _’
69+
-- In the type signature: decl :: HsDecl _
70+
-- • Relevant bindings include
71+
-- [REDACTED]
72+
errorMessageRefersToToplevelHole :: T.Text -> Bool
73+
errorMessageRefersToToplevelHole msg =
74+
not (T.null prefix) && " :: _" `T.isSuffixOf` T.takeWhile (/= '\n') rest
75+
where
76+
(prefix, rest) = T.breakOn "• In the type signature:" msg

0 commit comments

Comments
 (0)