git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Merge pull request #2532 from kRITZCREEK/psc-ide-pretty-unicode-types
c09ceb7Christoph Hegemann
psc-ide-client/Main.hs+2
@@ -35,6 +35,8 @@ main = do
35
35
36client :: PortID -> IO ()
36client :: PortID -> IO ()
37client port = do
37client port = do
38+ hSetEncoding stdin utf8
39+ hSetEncoding stdout utf8
38 h <-
40 h <-
39 connectTo "127.0.0.1" port `catch`
41 connectTo "127.0.0.1" port `catch`
40 (\(SomeException e) ->
42 (\(SomeException e) ->
purescript.cabal−1
@@ -280,7 +280,6 @@ library
280 Language.PureScript.Ide.CaseSplit
280 Language.PureScript.Ide.CaseSplit
281 Language.PureScript.Ide.Command
281 Language.PureScript.Ide.Command
282 Language.PureScript.Ide.Completion
282 Language.PureScript.Ide.Completion
283− Language.PureScript.Ide.Conversions
284 Language.PureScript.Ide.Externs
283 Language.PureScript.Ide.Externs
285 Language.PureScript.Ide.Error
284 Language.PureScript.Ide.Error
286 Language.PureScript.Ide.Filter
285 Language.PureScript.Ide.Filter
src/Language/PureScript/Ide/Conversions.hsdeleted−29
@@ -1,29 +0,0 @@
1−-----------------------------------------------------------------------------
2−--
3−-- Module : Language.PureScript.Ide.Conversions
4−-- Description : Conversions to Text for PureScript types
5−-- Copyright : Christoph Hegemann 2016
6−-- License : MIT (http://opensource.org/licenses/MIT)
7−--
8−-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9−-- Stability : experimental
10−--
11−-- |
12−-- Conversions to Text for PureScript types
13−-----------------------------------------------------------------------------
14−
15−module Language.PureScript.Ide.Conversions where
16−
17−import Control.Lens.Iso
18−import Data.Text (lines, strip, unwords, pack)
19−import qualified Language.PureScript as P
20−import Protolude
21−
22−properNameT :: Iso' (P.ProperName a) Text
23−properNameT = iso P.runProperName P.ProperName
24−
25−identT :: Iso' P.Ident Text
26−identT = iso P.runIdent P.Ident
27−
28−prettyTypeT :: P.Type -> Text
29−prettyTypeT = unwords . map strip . lines . pack . P.prettyPrintType
src/Language/PureScript/Ide/Util.hs+18−3
@@ -24,19 +24,20 @@ module Language.PureScript.Ide.Util
24 , withEmptyAnn
24 , withEmptyAnn
25 , valueOperatorAliasT
25 , valueOperatorAliasT
26 , typeOperatorAliasT
26 , typeOperatorAliasT
27− , module Language.PureScript.Ide.Conversions
27+ , prettyTypeT
28+ , properNameT
29+ , identT
28 , module Language.PureScript.Ide.Logging
30 , module Language.PureScript.Ide.Logging
29 ) where
31 ) where
30
32
31import Protolude hiding (decodeUtf8,
33import Protolude hiding (decodeUtf8,
32 encodeUtf8)
34 encodeUtf8)
33
35
34−import Control.Lens ((^.))
36+import Control.Lens ((^.), Iso', iso)
35import Data.Aeson
37import Data.Aeson
36import qualified Data.Text as T
38import qualified Data.Text as T
37import Data.Text.Lazy.Encoding (decodeUtf8, encodeUtf8)
39import Data.Text.Lazy.Encoding (decodeUtf8, encodeUtf8)
38import qualified Language.PureScript as P
40import qualified Language.PureScript as P
39−import Language.PureScript.Ide.Conversions
40import Language.PureScript.Ide.Logging
41import Language.PureScript.Ide.Logging
41import Language.PureScript.Ide.Types
42import Language.PureScript.Ide.Types
42
43
@@ -114,3 +115,17 @@ unwrapPositioned x = x
114unwrapPositionedRef :: P.DeclarationRef -> P.DeclarationRef
115unwrapPositionedRef :: P.DeclarationRef -> P.DeclarationRef
115unwrapPositionedRef (P.PositionedDeclarationRef _ _ x) = unwrapPositionedRef x
116unwrapPositionedRef (P.PositionedDeclarationRef _ _ x) = unwrapPositionedRef x
116unwrapPositionedRef x = x
117unwrapPositionedRef x = x
118+
119+properNameT :: Iso' (P.ProperName a) Text
120+properNameT = iso P.runProperName P.ProperName
121+
122+identT :: Iso' P.Ident Text
123+identT = iso P.runIdent P.Ident
124+
125+prettyTypeT :: P.Type -> Text
126+prettyTypeT =
127+ T.unwords
128+ . map T.strip
129+ . T.lines
130+ . T.pack
131+ . P.prettyPrintTypeWithUnicode
src/Language/PureScript/Pretty/Types.hs+58−28
@@ -5,10 +5,10 @@ module Language.PureScript.Pretty.Types
5 ( typeAsBox
5 ( typeAsBox
6 , suggestedTypeAsBox
6 , suggestedTypeAsBox
7 , prettyPrintType
7 , prettyPrintType
8+ , prettyPrintTypeWithUnicode
8 , prettyPrintSuggestedType
9 , prettyPrintSuggestedType
9 , typeAtomAsBox
10 , typeAtomAsBox
10 , prettyPrintTypeAtom
11 , prettyPrintTypeAtom
11− , prettyPrintRowWith
12 , prettyPrintRow
12 , prettyPrintRow
13 , prettyPrintLabel
13 , prettyPrintLabel
14 , prettyPrintObjectKey
14 , prettyPrintObjectKey
@@ -37,9 +37,12 @@ import Text.PrettyPrint.Boxes hiding ((<+>))
37
37
38-- TODO(Christoph): get rid of T.unpack s
38-- TODO(Christoph): get rid of T.unpack s
39
39
40−constraintsAsBox :: [Constraint] -> Box -> Box
40+constraintsAsBox :: TypeRenderOptions -> [Constraint] -> Box -> Box
41+constraintsAsBox tro constraints ty = case constraints of
41−constraintsAsBox [con] ty = text "(" <> constraintAsBox con `before` (text ") => " <> ty)
42+ [con] -> text "(" <> constraintAsBox con `before` (") " <> text doubleRightArrow <> " " <> ty)
42−constraintsAsBox xs ty = vcat left (zipWith (\i con -> text (if i == 0 then "( " else ", ") <> constraintAsBox con) [0 :: Int ..] xs) `before` (text ") => " <> ty)
43+ xs -> vcat left (zipWith (\i con -> text (if i == 0 then "( " else ", ") <> constraintAsBox con) [0 :: Int ..] xs) `before` (") " <> text doubleRightArrow <> " " <> ty)
44+ where
45+ doubleRightArrow = if troUnicode tro then "⇒" else "=>"
43
46
44constraintAsBox :: Constraint -> Box
47constraintAsBox :: Constraint -> Box
45constraintAsBox (Constraint pn tys _) = typeAsBox (foldl TypeApp (TypeConstructor (fmap coerceProperName pn)) tys)
48constraintAsBox (Constraint pn tys _) = typeAsBox (foldl TypeApp (TypeConstructor (fmap coerceProperName pn)) tys)
@@ -47,11 +50,13 @@ constraintAsBox (Constraint pn tys _) = typeAsBox (foldl TypeApp (TypeConstructo
47-- |
50-- |
48-- Generate a pretty-printed string representing a Row
51-- Generate a pretty-printed string representing a Row
49--
52--
50−prettyPrintRowWith :: Char -> Char -> Type -> Box
53+prettyPrintRowWith :: TypeRenderOptions -> Char -> Char -> Type -> Box
51−prettyPrintRowWith open close = uncurry listToBox . toList []
54+prettyPrintRowWith tro open close = uncurry listToBox . toList []
52 where
55 where
53 nameAndTypeToPs :: Char -> Label -> Type -> Box
56 nameAndTypeToPs :: Char -> Label -> Type -> Box
54− nameAndTypeToPs start name ty = text (start : ' ' : T.unpack (prettyPrintLabel name) ++ " :: ") <> typeAsBox ty
57+ nameAndTypeToPs start name ty = text (start : ' ' : T.unpack (prettyPrintLabel name) ++ " " ++ doubleColon ++ " ") <> typeAsBox ty
58+
59+ doubleColon = if troUnicode tro then "∷" else "::"
55
60
56 tailToPs :: Type -> Box
61 tailToPs :: Type -> Box
57 tailToPs REmpty = nullBox
62 tailToPs REmpty = nullBox
@@ -63,13 +68,12 @@ prettyPrintRowWith open close = uncurry listToBox . toList []
63 listToBox ts rest = vcat left $
68 listToBox ts rest = vcat left $
64 zipWith (\(nm, ty) i -> nameAndTypeToPs (if i == 0 then open else ',') nm ty) ts [0 :: Int ..] ++
69 zipWith (\(nm, ty) i -> nameAndTypeToPs (if i == 0 then open else ',') nm ty) ts [0 :: Int ..] ++
65 [ tailToPs rest, text [close] ]
70 [ tailToPs rest, text [close] ]
66−
67 toList :: [(Label, Type)] -> Type -> ([(Label, Type)], Type)
71 toList :: [(Label, Type)] -> Type -> ([(Label, Type)], Type)
68 toList tys (RCons name ty row) = toList ((name, ty):tys) row
72 toList tys (RCons name ty row) = toList ((name, ty):tys) row
69 toList tys r = (reverse tys, r)
73 toList tys r = (reverse tys, r)
70
74
71prettyPrintRow :: Type -> String
75prettyPrintRow :: Type -> String
72−prettyPrintRow = render . prettyPrintRowWith '(' ')'
76+prettyPrintRow = render . prettyPrintRowWith defaultOptions '(' ')'
73
77
74typeApp :: Pattern () Type (Type, Type)
78typeApp :: Pattern () Type (Type, Type)
75typeApp = mkPattern match
79typeApp = mkPattern match
@@ -113,16 +117,16 @@ explicitParens = mkPattern match
113 match (ParensInType ty) = Just ((), ty)
117 match (ParensInType ty) = Just ((), ty)
114 match _ = Nothing
118 match _ = Nothing
115
119
116−matchTypeAtom :: Bool -> Pattern () Type Box
120+matchTypeAtom :: TypeRenderOptions -> Pattern () Type Box
117−matchTypeAtom suggesting =
121+matchTypeAtom tro@TypeRenderOptions{troSuggesting = suggesting} =
118− typeLiterals <+> fmap ((`before` (text ")")) . (text "(" <>)) (matchType suggesting)
122+ typeLiterals <+> fmap ((`before` (text ")")) . (text "(" <>)) (matchType tro)
119 where
123 where
120 typeLiterals :: Pattern () Type Box
124 typeLiterals :: Pattern () Type Box
121 typeLiterals = mkPattern match where
125 typeLiterals = mkPattern match where
122 match TypeWildcard{} = Just $ text "_"
126 match TypeWildcard{} = Just $ text "_"
123 match (TypeVar var) = Just $ text $ T.unpack var
127 match (TypeVar var) = Just $ text $ T.unpack var
124 match (TypeLevelString s) = Just $ text $ T.unpack $ prettyPrintString s
128 match (TypeLevelString s) = Just $ text $ T.unpack $ prettyPrintString s
125− match (PrettyPrintObject row) = Just $ prettyPrintRowWith '{' '}' row
129+ match (PrettyPrintObject row) = Just $ prettyPrintRowWith tro '{' '}' row
126 match (TypeConstructor ctor) = Just $ text $ T.unpack $ runProperName $ disqualify ctor
130 match (TypeConstructor ctor) = Just $ text $ T.unpack $ runProperName $ disqualify ctor
127 match (TUnknown u)
131 match (TUnknown u)
128 | suggesting = Just $ text "_"
132 | suggesting = Just $ text "_"
@@ -131,24 +135,28 @@ matchTypeAtom suggesting =
131 | suggesting = Just $ text $ T.unpack name
135 | suggesting = Just $ text $ T.unpack name
132 | otherwise = Just $ text $ T.unpack name ++ show s
136 | otherwise = Just $ text $ T.unpack name ++ show s
133 match REmpty = Just $ text "()"
137 match REmpty = Just $ text "()"
134− match row@RCons{} = Just $ prettyPrintRowWith '(' ')' row
138+ match row@RCons{} = Just $ prettyPrintRowWith tro '(' ')' row
135 match (BinaryNoParensType op l r) =
139 match (BinaryNoParensType op l r) =
136 Just $ typeAsBox l <> text " " <> typeAsBox op <> text " " <> typeAsBox r
140 Just $ typeAsBox l <> text " " <> typeAsBox op <> text " " <> typeAsBox r
137 match (TypeOp op) = Just $ text $ T.unpack $ showQualified runOpName op
141 match (TypeOp op) = Just $ text $ T.unpack $ showQualified runOpName op
138 match _ = Nothing
142 match _ = Nothing
139
143
140−matchType :: Bool -> Pattern () Type Box
144+matchType :: TypeRenderOptions -> Pattern () Type Box
141−matchType = buildPrettyPrinter operators . matchTypeAtom where
145+matchType tro = buildPrettyPrinter operators (matchTypeAtom tro) where
142 operators :: OperatorTable () Type Box
146 operators :: OperatorTable () Type Box
143 operators =
147 operators =
144 OperatorTable [ [ AssocL typeApp $ \f x -> keepSingleLinesOr (moveRight 2) f x ]
148 OperatorTable [ [ AssocL typeApp $ \f x -> keepSingleLinesOr (moveRight 2) f x ]
145− , [ AssocR appliedFunction $ \arg ret -> keepSingleLinesOr id arg (text "-> " <> ret) ]
149+ , [ AssocR appliedFunction $ \arg ret -> keepSingleLinesOr id arg (text rightArrow <> " " <> ret) ]
146− , [ Wrap constrained $ \deps ty -> constraintsAsBox deps ty ]
150+ , [ Wrap constrained $ \deps ty -> constraintsAsBox tro deps ty ]
147− , [ Wrap forall_ $ \idents ty -> keepSingleLinesOr (moveRight 2) (text ("forall " ++ unwords idents ++ ".")) ty ]
151+ , [ Wrap forall_ $ \idents ty -> keepSingleLinesOr (moveRight 2) (text (forall' ++ " " ++ unwords idents ++ ".")) ty ]
148− , [ Wrap kinded $ \k ty -> keepSingleLinesOr (moveRight 2) ty (text (":: " ++ T.unpack (prettyPrintKind k))) ]
152+ , [ Wrap kinded $ \k ty -> keepSingleLinesOr (moveRight 2) ty (text (doubleColon ++ " " ++ T.unpack (prettyPrintKind k))) ]
149 , [ Wrap explicitParens $ \_ ty -> ty ]
153 , [ Wrap explicitParens $ \_ ty -> ty ]
150 ]
154 ]
151
155
156+ rightArrow = if troUnicode tro then "→" else "->"
157+ forall' = if troUnicode tro then "∀" else "forall"
158+ doubleColon = if troUnicode tro then "∷" else "::"
159+
152 -- If both boxes span a single line, keep them on the same line, or else
160 -- If both boxes span a single line, keep them on the same line, or else
153 -- use the specified function to modify the second box, then combine vertically.
161 -- use the specified function to modify the second box, then combine vertically.
154 keepSingleLinesOr :: (Box -> Box) -> Box -> Box -> Box
162 keepSingleLinesOr :: (Box -> Box) -> Box -> Box -> Box
@@ -165,7 +173,7 @@ forall_ = mkPattern match
165typeAtomAsBox :: Type -> Box
173typeAtomAsBox :: Type -> Box
166typeAtomAsBox
174typeAtomAsBox
167 = fromMaybe (internalError "Incomplete pattern")
175 = fromMaybe (internalError "Incomplete pattern")
168− . PA.pattern (matchTypeAtom False) ()
176+ . PA.pattern (matchTypeAtom defaultOptions) ()
169 . insertPlaceholders
177 . insertPlaceholders
170
178
171-- | Generate a pretty-printed string representing a Type, as it should appear inside parentheses
179-- | Generate a pretty-printed string representing a Type, as it should appear inside parentheses
@@ -173,24 +181,46 @@ prettyPrintTypeAtom :: Type -> String
173prettyPrintTypeAtom = render . typeAtomAsBox
181prettyPrintTypeAtom = render . typeAtomAsBox
174
182
175typeAsBox :: Type -> Box
183typeAsBox :: Type -> Box
176−typeAsBox = typeAsBoxImpl False
184+typeAsBox = typeAsBoxImpl defaultOptions
177
185
178suggestedTypeAsBox :: Type -> Box
186suggestedTypeAsBox :: Type -> Box
179−suggestedTypeAsBox = typeAsBoxImpl True
187+suggestedTypeAsBox = typeAsBoxImpl suggestingOptions
188+
189+data TypeRenderOptions = TypeRenderOptions
190+ { troSuggesting :: Bool
191+ , troUnicode :: Bool
192+ }
180
193
194+suggestingOptions :: TypeRenderOptions
195+suggestingOptions = TypeRenderOptions True False
196+
197+defaultOptions :: TypeRenderOptions
198+defaultOptions = TypeRenderOptions False False
199+
200+unicodeOptions :: TypeRenderOptions
201+unicodeOptions = TypeRenderOptions False True
202+
181−typeAsBoxImpl :: Bool -> Type -> Box
203+typeAsBoxImpl :: TypeRenderOptions -> Type -> Box
182−typeAsBoxImpl suggesting
204+typeAsBoxImpl tro
183 = fromMaybe (internalError "Incomplete pattern")
205 = fromMaybe (internalError "Incomplete pattern")
184− . PA.pattern (matchType suggesting) ()
206+ . PA.pattern (matchType tro) ()
185 . insertPlaceholders
207 . insertPlaceholders
186
208
187-- | Generate a pretty-printed string representing a 'Type'
209-- | Generate a pretty-printed string representing a 'Type'
188prettyPrintType :: Type -> String
210prettyPrintType :: Type -> String
189−prettyPrintType = render . typeAsBoxImpl False
211+prettyPrintType = prettyPrintType' defaultOptions
212+
213+-- | Generate a pretty-printed string representing a 'Type' using unicode
214+-- symbols where applicable
215+prettyPrintTypeWithUnicode :: Type -> String
216+prettyPrintTypeWithUnicode = prettyPrintType' unicodeOptions
190
217
191-- | Generate a pretty-printed string representing a suggested 'Type'
218-- | Generate a pretty-printed string representing a suggested 'Type'
192prettyPrintSuggestedType :: Type -> String
219prettyPrintSuggestedType :: Type -> String
193−prettyPrintSuggestedType = render . typeAsBoxImpl True
220+prettyPrintSuggestedType = prettyPrintType' suggestingOptions
221+
222+prettyPrintType' :: TypeRenderOptions -> Type -> String
223+prettyPrintType' tro = render . typeAsBoxImpl tro
194
224
195prettyPrintLabel :: Label -> Text
225prettyPrintLabel :: Label -> Text
196prettyPrintLabel (Label s) =
226prettyPrintLabel (Label s) =
tests/Language/PureScript/Ide/Integration.hs+14−7
@@ -50,12 +50,14 @@ import Data.Maybe (fromJust)
50
50
51import Data.Aeson
51import Data.Aeson
52import Data.Aeson.Types
52import Data.Aeson.Types
53−import qualified Data.Text as T
53+import qualified Data.Text.Encoding as T
54+import qualified Data.ByteString.Char8 as BS8
55+import qualified Data.ByteString.Lazy.Char8 as BSL8
54import qualified Data.Vector as V
56import qualified Data.Vector as V
55−import Language.PureScript.Ide.Util
56import qualified Language.PureScript as P
57import qualified Language.PureScript as P
57import System.Directory
58import System.Directory
58import System.FilePath
59import System.FilePath
60+import System.IO
59import System.IO.Error (mkIOError, userErrorType)
61import System.IO.Error (mkIOError, userErrorType)
60import System.Process
62import System.Process
61
63
@@ -124,11 +126,16 @@ fileGlob = "\"src/**/*.purs\""
124-- Integration Testing API
126-- Integration Testing API
125
127
126sendCommand :: Value -> IO Text
128sendCommand :: Value -> IO Text
127−sendCommand v = toS <$> readCreateProcess
129+sendCommand v = do
130+ (Just hin, Just hout, _, _) <-
128− ((shell "psc-ide-client") { std_out=CreatePipe
131+ createProcess ((proc "psc-ide-client" []) {std_in=CreatePipe, std_out=CreatePipe})
129− , std_err=CreatePipe
132+
130− })
133+ hSetEncoding hin utf8
131− (T.unpack (encodeT v))
134+ hSetEncoding hout utf8
135+
136+ BS8.hPutStrLn hin (BSL8.toStrict (encode v))
137+ hFlush hin
138+ T.decodeUtf8 <$> BS8.hGetLine hout
132
139
133quitServer :: IO ()
140quitServer :: IO ()
134quitServer = do
141quitServer = do
tests/Language/PureScript/Ide/MatcherSpec.hs+1−1
@@ -46,4 +46,4 @@ spec = do
46 it "matches on equality" $ do
46 it "matches on equality" $ do
47 -- ignore any position information
47 -- ignore any position information
48 (m, i, t, _) : _ <- getFlexCompletions "const"
48 (m, i, t, _) : _ <- getFlexCompletions "const"
49− (m, i, t) `shouldBe` ("MatcherSpec", "const", "forall a b. a -> b -> a")
49+ (m, i, t) `shouldBe` ("MatcherSpec", "const", "∀ a b. a → b → a")