Skip to content

Commit 950a776

Browse files
committed
fmt-signature: fix wrapping for indented continued lines
1 parent b11541b commit 950a776

File tree

4 files changed

+76
-43
lines changed

4 files changed

+76
-43
lines changed

TODO

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@ fmt-signature
99
. Break input into lines without comments and wrap each section
1010
separately. The only wrinkle is that only the first line of the first
1111
section isn't indented.
12+
- wrap inside ()s
13+
. I want one indent per (, and to wrap at the lowest indent.
14+
. This is what Util.Format does, so maybe I should try to extract and use
15+
it.
16+
. But it has bugs. Or can I do a simpler one? Or maybe the bugs are just
17+
in Util.Format?
1218
- configure indentation
19+
- format non-toplevel declarations
20+
. I think I just need to preserve leading indent.
1321
- can I get vim to expand the selection to the whole signature?
1422

1523

@@ -33,6 +41,28 @@ string-literal
3341
\ blah"
3442
, ...
3543
)
44+
- How can I support embedded operators? E.g.
45+
. "blah blah "
46+
<> x <> "blah\
47+
\ blah"
48+
=>
49+
blah blah
50+
<> x <> blah
51+
blah
52+
53+
. "blah" <> blah
54+
<> "e..."
55+
. For stripping, I can just not strip if it's not a \. But once I strip,
56+
it becomes ambiguous.
57+
. I could try to make it non-ambiguous, by replacing quotes with some
58+
magic symbol.
59+
. Hm, I could even remember wrapped or not by using a different symbol.
60+
. It should be typeable, because I want to write these inline:
61+
. •blah blah
62+
blah blah•
63+
<> x <> •blah blah
64+
blah•
65+
3666
- Also I could ignore non-indented leading lines, e.g.
3767
variable =
3868
"blah blah"

src/FmtSignature.hs

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{-# LANGUAGE OverloadedStrings #-}
2+
{-# LANGUAGE PatternGuards #-}
3+
-- | Format type signature declarations.
24
module FmtSignature where
35
import Control.Applicative ((<|>))
46
import qualified Control.Arrow as Arrow
@@ -22,34 +24,14 @@ main = do
2224
args <- System.Environment.getArgs
2325
case args of
2426
[width] | Just width <- Read.readMaybe width ->
25-
interact $ \s -> maybe s id (fmt indent width s)
27+
interact $ \s -> maybe s id (fmt width s)
2628
_ -> IO.hPutStrLn IO.stderr usage >> System.Exit.exitFailure
2729

28-
Just t0 = parse "a -> b"
29-
Just t1 = parse "a => (bb->c)"
30+
indentSpaces :: Int
31+
indentSpaces = 4
3032

31-
indent :: String
32-
indent = " "
33-
34-
{-
35-
forall y z. X y => A -> IO z
36-
37-
(a -> b) -> c
38-
39-
leave lines with comments on them alone. What about
40-
a -> (b -- hi
41-
-> c)
42-
43-
-}
44-
45-
fmt :: String -> Int -> String -> Maybe String
46-
fmt indent width =
47-
fmap (List.intercalate "\n" . indented . wrap (width - length indent)
48-
. toWords)
49-
. parse
50-
where
51-
indented (x : xs) = x : map (indent++) xs
52-
indented [] = []
33+
fmt :: Int -> String -> Maybe String
34+
fmt width = fmap (List.intercalate "\n" . wrap width . toWords) . parse
5335

5436
toWords :: [Parsed] -> [String]
5537
toWords = map (strip . concat) . splitWith (`elem` ["->", "=>"]) . map unparse
@@ -59,13 +41,26 @@ toWords = map (strip . concat) . splitWith (`elem` ["->", "=>"]) . map unparse
5941
-- In fact, this is basically just Util.Format, so I should use that, after
6042
-- I extract it.
6143
wrap :: Int -> [String] -> [String]
62-
wrap width = map unwords . split
44+
wrap width = mapTail (indent<>) . go (width : repeat (width - indentSpaces))
45+
where
46+
indent = replicate indentSpaces ' '
47+
go (width : widths) words
48+
| null pre = []
49+
| otherwise = unwords pre : go widths post
50+
where (pre, post) = wrap1 width words
51+
go [] _ = [] -- unreachable, widths is infinite
52+
53+
mapTail :: (a -> a) -> [a] -> [a]
54+
mapTail f (x : xs) = x : map f xs
55+
mapTail _ [] = []
56+
57+
wrap1 :: Int -> [String] -> ([String], [String])
58+
wrap1 width words
59+
| null words = ([], [])
60+
| null pre = (take 1 post, drop 1 post)
61+
| otherwise = (pre, post)
6362
where
64-
split words
65-
| null words = []
66-
| null pre = take 1 post : split (drop 1 post)
67-
| otherwise = pre : split post
68-
where (pre, post) = breakAccum (>width) accum (-1) words
63+
(pre, post) = breakAccum (>width) accum (-1) words
6964
accum w word = w + 1 + length word
7065

7166
-- * parse

src/FmtSignature_test.hs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{-# LANGUAGE OverloadedStrings #-}
22
{-# LANGUAGE FlexibleContexts #-}
3-
import Data.Monoid ((<>))
43
import qualified GHC.Stack as Stack
54
import qualified Test.Tasty as Tasty
65
import qualified Test.Tasty.HUnit as HUnit
@@ -9,8 +8,8 @@ import qualified FmtSignature
98

109

1110
main :: IO ()
12-
main = Tasty.defaultMain $ Tasty.testGroup "tests"
13-
[ test_fmt
11+
main = run $ Tasty.testGroup "tests"
12+
[ test_fmt, test_wrap
1413
]
1514

1615
run :: Tasty.TestTree -> IO ()
@@ -19,16 +18,26 @@ run = Tasty.defaultMain
1918
test_fmt :: Tasty.TestTree
2019
test_fmt = Tasty.testGroup "fmt"
2120
[ "a -> b" ==> Just "a -> b"
22-
, "aaa -> bbb -> ccc" ==> Just "aaa -> bbb\n____-> ccc"
23-
, "aaa -> () -> bbb" ==> Just "aaa -> ()\n____-> bbb"
24-
, "a -> b -> c -> d" ==> Just "a -> b\n____-> c\n____-> d"
25-
-- , "a -> b -> c -> d"
21+
, "abc1234567890 -> a" ==> Just "abc1234567890\n -> a"
22+
, "aaa -> bbb -> ccc" ==> Just "aaa -> bbb\n -> ccc"
23+
, "aaa -> () -> bbb" ==> Just "aaa -> ()\n -> bbb"
24+
, "a -> b -> c -> d" ==> Just "a -> b -> c\n -> d"
25+
-- ()s don't break
26+
, "a -> (b -> c) -> d" ==> Just "a\n -> (b -> c)\n -> d"
2627
]
2728
where
28-
f = FmtSignature.fmt "____" 12
29+
f = FmtSignature.fmt 12
2930
(==>) :: Stack.HasCallStack => String -> Maybe String -> Tasty.TestTree
3031
(==>) = test f
3132

33+
test_wrap :: Tasty.TestTree
34+
test_wrap = Tasty.testGroup "wrap"
35+
[ ["aaa", "-> bbb", "-> ccc"] ==> ["aaa -> bbb", " -> ccc"]
36+
]
37+
where
38+
(==>) :: Stack.HasCallStack => [String] -> [String] -> Tasty.TestTree
39+
(==>) = test (FmtSignature.wrap 12)
40+
3241
test :: (Stack.HasCallStack, Show a, Eq b, Show b) => (a -> b) -> a -> b
3342
-> Tasty.TestTree
3443
test f x expected = HUnit.testCase (take 70 $ show x) $ f x HUnit.@?= expected

src/StringLiteral.hs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ usage =
2222
"string-literal [ --wrapped --{add,remove,toggle}-{backslash,lines} ]\n\
2323
\\n\
2424
\Convert between plain text and either backslash-continued string\n\
25-
\literals, or list of lines style strings. This is to work around\n\
26-
\haskell's lack of multi-line string literals. Bind the toggle variant\n\
25+
\literals, or list of lines style strings. Bind the toggle variant\n\
2726
\to a vim key to switch between raw text and haskell string literal.\n\
2827
\\n\
2928
\It assumes a single level of indent for the strings, and leaves the\n\
@@ -40,8 +39,8 @@ usage =
4039
\preserve your own leading spaces. If you are doing explicit formatting\n\
4140
\then don't use --wrapped.\n\
4241
\\n\
43-
\Standard CPP doesn't like Haskell string-gap syntax. You can either use\n\
44-
\cpphs via -pgmP 'cpphs --cpp', or use lines mode, which is more\n\
42+
\Standard CPP doesn't like Haskell string-gap syntax. You can either\n\
43+
\use cpphs via -pgmP 'cpphs --cpp', or use lines mode, which is more\n\
4544
\cluttered but doesn't make CPP mad. Presumably you have a unlines or\n\
4645
\Text.unlines call at the front of the list.\n"
4746

0 commit comments

Comments
 (0)