Skip to content

Commit 19e5ae6

Browse files
sergvhololeap
authored andcommitted
Omit more parens for wildcard type signature (haskell#2929)
* Omit more parens for wildcard type signature (haskell#2929) This is a followup to haskell#2764. * Review suggestions
1 parent 5aa21b2 commit 19e5ae6

File tree

2 files changed

+90
-11
lines changed

2 files changed

+90
-11
lines changed

Diff for: ghcide/src/Development/IDE/Plugin/CodeAction.hs

+50-8
Original file line numberDiff line numberDiff line change
@@ -1561,15 +1561,57 @@ mkRenameEdit contents range name =
15611561
-- require understanding both the precedence of the context of the hole and of
15621562
-- the signature itself. Inserting them (almost) unconditionally is ugly but safe.
15631563
extractWildCardTypeSignature :: T.Text -> T.Text
1564-
extractWildCardTypeSignature msg = (if enclosed || not application then id else bracket) signature
1564+
extractWildCardTypeSignature msg
1565+
| enclosed || not isApp || isToplevelSig = sig
1566+
| otherwise = "(" <> sig <> ")"
15651567
where
1566-
msgSigPart = snd $ T.breakOnEnd "standing for " msg
1567-
signature = T.takeWhile (/='') . T.dropWhile (=='') . T.dropWhile (/='') $ msgSigPart
1568-
-- parenthesize type applications, e.g. (Maybe Char)
1569-
application = any isSpace . T.unpack $ signature
1570-
-- do not add extra parentheses to lists, tuples and already parenthesized types
1571-
enclosed = not (T.null signature) && (T.head signature, T.last signature) `elem` [('(',')'), ('[',']')]
1572-
bracket = ("(" `T.append`) . (`T.append` ")")
1568+
msgSigPart = snd $ T.breakOnEnd "standing for " msg
1569+
(sig, rest) = T.span (/='') . T.dropWhile (=='') . T.dropWhile (/='') $ msgSigPart
1570+
-- If we're completing something like ‘foo :: _’ parens can be safely omitted.
1571+
isToplevelSig = errorMessageRefersToToplevelHole rest
1572+
-- Parenthesize type applications, e.g. (Maybe Char).
1573+
isApp = T.any isSpace sig
1574+
-- Do not add extra parentheses to lists, tuples and already parenthesized types.
1575+
enclosed = not (T.null sig) && (T.head sig, T.last sig) `elem` [('(', ')'), ('[', ']')]
1576+
1577+
-- | Detect whether user wrote something like @foo :: _@ or @foo :: (_, Int)@.
1578+
-- The former is considered toplevel case for which the function returns 'True',
1579+
-- the latter is not toplevel and the returned value is 'False'.
1580+
--
1581+
-- When type hole is at toplevel then there’s a line starting with
1582+
-- "• In the type signature" which ends with " :: _" like in the
1583+
-- following snippet:
1584+
--
1585+
-- source/library/Language/Haskell/Brittany/Internal.hs:131:13: error:
1586+
-- • Found type wildcard ‘_’ standing for ‘HsDecl GhcPs’
1587+
-- To use the inferred type, enable PartialTypeSignatures
1588+
-- • In the type signature: decl :: _
1589+
-- In an equation for ‘splitAnnots’:
1590+
-- splitAnnots m@HsModule {hsmodAnn, hsmodDecls}
1591+
-- = undefined
1592+
-- where
1593+
-- ann :: SrcSpanAnnA
1594+
-- decl :: _
1595+
-- L ann decl = head hsmodDecls
1596+
-- • Relevant bindings include
1597+
-- [REDACTED]
1598+
--
1599+
-- When type hole is not at toplevel there’s a stack of where
1600+
-- the hole was located ending with "In the type signature":
1601+
--
1602+
-- source/library/Language/Haskell/Brittany/Internal.hs:130:20: error:
1603+
-- • Found type wildcard ‘_’ standing for ‘GhcPs’
1604+
-- To use the inferred type, enable PartialTypeSignatures
1605+
-- • In the first argument of ‘HsDecl’, namely ‘_’
1606+
-- In the type ‘HsDecl _’
1607+
-- In the type signature: decl :: HsDecl _
1608+
-- • Relevant bindings include
1609+
-- [REDACTED]
1610+
errorMessageRefersToToplevelHole :: T.Text -> Bool
1611+
errorMessageRefersToToplevelHole msg =
1612+
not (T.null prefix) && " :: _" `T.isSuffixOf` T.takeWhile (/= '\n') rest
1613+
where
1614+
(prefix, rest) = T.breakOn "• In the type signature:" msg
15731615

15741616
extractRenamableTerms :: T.Text -> [T.Text]
15751617
extractRenamableTerms msg

Diff for: ghcide/test/exe/Main.hs

+40-3
Original file line numberDiff line numberDiff line change
@@ -1196,7 +1196,7 @@ typeWildCardActionTests = testGroup "type wildcard actions"
11961196
[ "func :: _"
11971197
, "func x = x"
11981198
]
1199-
[ "func :: (p -> p)"
1199+
[ "func :: p -> p"
12001200
, "func x = x"
12011201
]
12021202
, testUseTypeSignature "local signature"
@@ -1212,11 +1212,11 @@ typeWildCardActionTests = testGroup "type wildcard actions"
12121212
, " y = x * 2"
12131213
, " in y"
12141214
]
1215-
, testUseTypeSignature "multi-line message"
1215+
, testUseTypeSignature "multi-line message 1"
12161216
[ "func :: _"
12171217
, "func x y = x + y"
12181218
]
1219-
[ "func :: (Integer -> Integer -> Integer)"
1219+
[ "func :: Integer -> Integer -> Integer"
12201220
, "func x y = x + y"
12211221
]
12221222
, testUseTypeSignature "type in parentheses"
@@ -1240,6 +1240,43 @@ typeWildCardActionTests = testGroup "type wildcard actions"
12401240
[ "func :: IO ()"
12411241
, "func = putChar 'H'"
12421242
]
1243+
, testUseTypeSignature "no spaces around '::'"
1244+
[ "func::_"
1245+
, "func x y = x + y"
1246+
]
1247+
[ "func::Integer -> Integer -> Integer"
1248+
, "func x y = x + y"
1249+
]
1250+
, testGroup "add parens if hole is part of bigger type"
1251+
[ testUseTypeSignature "subtype 1"
1252+
[ "func :: _ -> Integer -> Integer"
1253+
, "func x y = x + y"
1254+
]
1255+
[ "func :: Integer -> Integer -> Integer"
1256+
, "func x y = x + y"
1257+
]
1258+
, testUseTypeSignature "subtype 2"
1259+
[ "func :: Integer -> _ -> Integer"
1260+
, "func x y = x + y"
1261+
]
1262+
[ "func :: Integer -> Integer -> Integer"
1263+
, "func x y = x + y"
1264+
]
1265+
, testUseTypeSignature "subtype 3"
1266+
[ "func :: Integer -> Integer -> _"
1267+
, "func x y = x + y"
1268+
]
1269+
[ "func :: Integer -> Integer -> Integer"
1270+
, "func x y = x + y"
1271+
]
1272+
, testUseTypeSignature "subtype 4"
1273+
[ "func :: Integer -> _"
1274+
, "func x y = x + y"
1275+
]
1276+
[ "func :: Integer -> (Integer -> Integer)"
1277+
, "func x y = x + y"
1278+
]
1279+
]
12431280
]
12441281
where
12451282
-- | Test session of given name, checking action "Use type signature..."

0 commit comments

Comments
 (0)