1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE DataKinds #-}
3 {-# LANGUAGE DefaultSignatures #-}
4 {-# LANGUAGE GADTs #-} -- for Router
5 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
6 {-# LANGUAGE InstanceSigs #-}
7 {-# LANGUAGE OverloadedStrings #-}
8 {-# LANGUAGE Rank2Types #-} -- for hoistParserPerm (which is no longer used)
9 module Symantic.CLI.Parser where
11 import Control.Applicative (Applicative(..), Alternative(..), optional, many, some)
12 import Control.Monad (Monad(..), join, sequence, forM_, void)
13 import Control.Monad.Trans.Class (MonadTrans(..))
14 import Control.Monad.Trans.State (StateT(..),evalState,get,put)
16 import Data.Char (Char)
17 import Data.Either (Either(..))
18 import Data.Eq (Eq(..))
19 import Data.Foldable (null, toList)
20 import Data.Function (($), (.), id, const)
21 import Data.Functor (Functor(..), (<$>), ($>))
22 import Data.Functor.Identity (Identity(..))
24 import Data.List.NonEmpty (NonEmpty(..))
25 import Data.Map.Strict (Map)
26 import Data.Maybe (Maybe(..), maybe, isNothing)
27 import Data.Ord (Ord(..))
28 import Data.Proxy (Proxy(..))
29 import Data.Semigroup (Semigroup(..))
30 import Data.String (String)
31 import Numeric.Natural (Natural)
32 import Prelude (Integer, Num(..), error)
33 import System.Environment (lookupEnv)
35 import Text.Read (Read, readEither)
36 import Text.Show (Show(..), ShowS, showString, showParen)
37 import Type.Reflection as Reflection
38 import qualified Data.List as List
39 import qualified Data.List.NonEmpty as NonEmpty
40 import qualified Data.Map.Merge.Strict as Map
41 import qualified Data.Map.Strict as Map
42 import qualified Data.Set as Set
43 import qualified Data.Text as Text
44 import qualified Data.Text.IO as Text
45 import qualified Data.Text.Lazy as TL
46 import qualified Data.Text.Lazy.Builder as TLB
47 import qualified Data.Text.Lazy.IO as TL
48 import qualified Symantic.Document as Doc
49 import qualified System.IO as IO
50 import qualified Text.Megaparsec as P
52 import Symantic.CLI.API
55 newtype Parser e d f k = Parser
56 { unParser :: P.ParsecT e [Arg] IO (f->k) -- Reader f k
60 P.ShowErrorComponent e =>
61 Router (Parser e d) handlers (Response (Router (Parser e d))) ->
64 parser api handlers args = do
66 (unParser $ unTrans $ router api)
69 forM_ (P.bundleErrors err) $ \e -> do
71 "Error parsing the command at argument #" <>
72 show (P.errorOffset e + 1) <> ":\n" <>
73 parseErrorTextPretty e
74 Right app -> unResponseParser $ app handlers
76 -- | Rewrite 'P.parseErrorTextPretty' to keep 'Ord' of 'Arg'.
77 parseErrorTextPretty ::
79 (P.Stream s, P.ShowErrorComponent e) =>
80 P.ParseError s e -> String
81 parseErrorTextPretty (P.TrivialError _ us ps) =
82 if isNothing us && Set.null ps
83 then "unknown parse error\n"
85 messageItemsPretty "unexpected "
86 (showErrorItem pxy <$> Set.toAscList (maybe Set.empty Set.singleton us)) <>
87 messageItemsPretty "expecting "
88 (showErrorItem pxy <$> Set.toAscList ps)
89 where pxy = Proxy :: Proxy s
90 parseErrorTextPretty err = P.parseErrorTextPretty err
92 messageItemsPretty :: String -> [String] -> String
93 messageItemsPretty prefix ts
95 | otherwise = prefix <> (orList . NonEmpty.fromList) ts <> "\n"
97 orList :: NonEmpty String -> String
99 orList (x:|[y]) = x <> " or " <> y
100 orList xs = List.intercalate ", " (NonEmpty.init xs) <> ", or " <> NonEmpty.last xs
102 showErrorItem :: P.Stream s => Proxy s -> P.ErrorItem (P.Token s) -> String
103 showErrorItem pxy = \case
104 P.Tokens ts -> P.showTokens pxy ts
105 P.Label label -> NonEmpty.toList label
106 P.EndOfInput -> "end of input"
109 instance Functor (Parser e d f) where
110 a2b`fmap`Parser x = Parser $ (a2b <$>) <$> x
111 instance Applicative (Parser e d f) where
112 pure = Parser . pure . const
113 Parser f <*> Parser x = Parser $ (<*>) <$> f <*> x
114 instance Ord e => Alternative (Parser e d f) where
116 Parser x <|> Parser y = Parser $ x <|> y
117 instance Ord e => Permutable (Parser e d) where
118 type Permutation (Parser e d) = ParserPerm e d (Parser e d)
119 runPermutation (ParserPerm ma p) = Parser $ do
120 u2p <- unParser $ optional p
123 Just perm -> runPermutation perm
126 (Parser $ P.token (const Nothing) Set.empty)
127 -- NOTE: not 'empty' so that 'P.TrivialError' has the unexpected token.
129 toPermutation (Parser x) =
131 (Parser $ (\a () -> ParserPerm (Just a) empty) <$> x)
132 toPermDefault a (Parser x) =
133 ParserPerm (Just ($ a))
134 (Parser $ (\d () -> ParserPerm (Just d) empty) <$> x)
135 instance App (Parser e d) where
136 Parser x <.> Parser y = Parser $
137 x >>= \a2b -> (. a2b) <$> y
138 instance Ord e => Alt (Parser e d) where
139 Parser x <!> Parser y = Parser $
140 (\a2k (a:!:_b) -> a2k a) <$> P.try x <|>
141 (\b2k (_a:!:b) -> b2k b) <$> y
142 Parser x `alt` Parser y = Parser $ P.try x <|> y
143 opt (Parser x) = Parser $
144 mapCont Just <$> P.try x
145 instance Ord e => AltApp (Parser e d) where
146 many0 (Parser x) = Parser $ concatCont <$> many x
147 many1 (Parser x) = Parser $ concatCont <$> some x
148 instance Pro (Parser e d) where
149 dimap a2b _b2a (Parser r) = Parser $ (\k b2k -> k (b2k . a2b)) <$> r
150 instance Ord e => CLI_Command (Parser e d) where
151 -- type CommandConstraint (Parser e d) a = ()
153 command n x = commands Map.empty (Map.singleton n x)
154 instance Ord e => CLI_Tag (Parser e d) where
155 type TagConstraint (Parser e d) a = ()
156 tagged name p = Parser $ P.try $ do
157 void $ (`P.token` exp) $ \tok ->
158 if lookupTag tok name
165 TagShort t -> Set.singleton $ P.Tokens $ pure $ ArgTagShort t
166 TagLong t -> Set.singleton $ P.Tokens $ pure $ ArgTagLong t
167 Tag s l -> Set.fromList
168 [ P.Tokens $ pure $ ArgTagShort s
169 , P.Tokens $ pure $ ArgTagLong l
171 lookupTag (ArgTagShort x) (TagShort y) = x == y
172 lookupTag (ArgTagShort x) (Tag y _) = x == y
173 lookupTag (ArgTagLong x) (TagLong y) = x == y
174 lookupTag (ArgTagLong x) (Tag _ y) = x == y
175 lookupTag _ _ = False
176 endOpts = Parser $ do
177 (`P.token` exp) $ \case
178 ArgTagLong "" -> Just id
181 exp = Set.singleton $ P.Tokens $ pure $ ArgTagLong ""
182 instance Ord e => CLI_Var (Parser e d) where
183 type VarConstraint (Parser e d) a = (IOType a, FromSegment a)
184 var' :: forall a k. VarConstraint (Parser e d) a => Name -> Parser e d (a->k) k
185 var' name = Parser $ do
186 seg <- (`P.token` expName) $ \case
187 ArgSegment seg -> Just seg
189 lift (fromSegment seg) >>= \case
190 Left err -> P.failure got expType
192 got = Just $ P.Tokens $ pure $ ArgSegment seg
193 expType = Set.singleton $ P.Label $ NonEmpty.fromList $
194 "<"<>name<>"> to be of type "<>ioType @a
196 "Prelude.read: no parse" -> ""
199 Right a -> return ($ a)
201 expName = Set.singleton $ P.Label $ NonEmpty.fromList $ "<"<>name<>">"
202 just a = Parser $ return ($ a)
203 nothing = Parser $ return id
204 instance Ord e => CLI_Env (Parser e d) where
205 type EnvConstraint (Parser e d) a = (IOType a, FromSegment a)
206 env' :: forall a k. EnvConstraint (Parser e d) a => Name -> Parser e d (a->k) k
208 lift (lookupEnv name) >>= \case
209 Nothing -> P.failure got exp
212 exp = Set.singleton $ P.Label $ NonEmpty.fromList $ "${"<>name<>"}"
214 lift (fromSegment val) >>= \case
215 Right a -> return ($ a)
216 Left err -> P.failure got exp
218 got = Just $ P.Tokens $ pure $ ArgEnv name val
219 exp = Set.singleton $ P.Label $ NonEmpty.fromList $
220 "${"<>name<>"} to be of type "<>ioType @a
222 "Prelude.read: no parse" -> ""
225 instance Ord e => CLI_Response (Parser e d) where
226 type ResponseConstraint (Parser e d) a = Outputable a
227 type ResponseArgs (Parser e d) a = ParserResponseArgs a
228 type Response (Parser e d) = ParserResponse
230 P.eof $> \({-ParserResponseArgs-} io) ->
231 ParserResponse $ io >>= output
232 instance Ord e => CLI_Help (Parser e d) where
233 type HelpConstraint (Parser e d) d' = d ~ d'
235 program n = Parser . P.label n . unParser
236 rule n = Parser . P.label n . unParser
238 concatCont :: [(a->k)->k] -> ([a]->k)->k
239 concatCont = List.foldr (consCont (:)) ($ [])
241 consCont :: (a->b->c) -> ((a->k)->k) -> ((b->k)->k) -> (c->k)->k
242 consCont ab2c a2k2k b2k2k = \c2k -> a2k2k $ \a -> b2k2k $ \b -> c2k (ab2c a b)
244 mapCont :: (a->b) -> ((a->k)->k) -> ((b->k)->k)
245 mapCont a2b a2k2k = \b2k -> a2k2k (b2k . a2b)
247 -- ** Type 'ParserResponse'
248 newtype ParserResponse = ParserResponse { unResponseParser :: IO () }
249 -- ** Type 'ParserResponseArgs'
250 type ParserResponseArgs = IO
252 -- * Class 'Outputable'
253 -- | Output of a CLI.
254 class IOType a => Outputable a where
256 default output :: Show a => a -> IO ()
259 instance Outputable () where
261 instance Outputable Bool
262 instance Outputable Int
263 instance Outputable Integer
264 instance Outputable Natural
265 instance Outputable String where
267 instance Outputable Text.Text where
269 instance Outputable TL.Text where
271 instance Outputable (Doc.AnsiText (Doc.Plain TLB.Builder)) where
277 instance Outputable (IO.Handle, Doc.AnsiText (Doc.Plain TLB.Builder)) where
285 -- | Like a MIME type but for input/output of a CLI.
288 default ioType :: Reflection.Typeable a => String
289 ioType = show (Reflection.typeRep @a)
294 instance IOType Integer
295 instance IOType Natural
296 instance IOType String
297 instance IOType Text.Text
298 instance IOType TL.Text
299 instance IOType (Doc.AnsiText (Doc.Plain TLB.Builder))
300 instance IOType (IO.Handle, Doc.AnsiText (Doc.Plain TLB.Builder))
302 -- * Class 'FromSegment'
303 class FromSegment a where
304 fromSegment :: Segment -> IO (Either String a)
305 default fromSegment :: Read a => Segment -> IO (Either String a)
306 fromSegment = return . readEither
307 instance FromSegment String where
308 fromSegment = return . Right
309 instance FromSegment Text.Text where
310 fromSegment = return . Right . Text.pack
311 instance FromSegment TL.Text where
312 fromSegment = return . Right . TL.pack
313 instance FromSegment Bool
314 instance FromSegment Int
315 instance FromSegment Integer
316 instance FromSegment Natural
318 -- ** Type 'ParserPerm'
319 data ParserPerm e d repr k a = ParserPerm
320 { permutation_result :: !(Maybe ((a->k)->k))
321 , permutation_parser :: repr () (ParserPerm e d repr k a)
324 instance (App repr, Functor (repr ())) => Functor (ParserPerm e d repr k) where
325 a2b `fmap` ParserPerm a ma = ParserPerm
326 ((\a2k2k b2k -> a2k2k $ b2k . a2b) <$> a)
327 ((a2b `fmap`) `fmap` ma)
328 instance (App repr, Functor (repr ()), Alternative (repr ())) =>
329 Applicative (ParserPerm e d repr k) where
330 pure a = ParserPerm (Just ($ a)) empty
331 lhs@(ParserPerm f ma2b) <*> rhs@(ParserPerm x ma) =
332 ParserPerm a (lhsAlt <|> rhsAlt)
335 (\a2b2k2k a2k2k -> \b2k ->
336 a2b2k2k $ \a2b -> a2k2k (b2k . a2b)
338 lhsAlt = (<*> rhs) <$> ma2b
339 rhsAlt = (lhs <*>) <$> ma
340 instance CLI_Help repr => CLI_Help (ParserPerm e d repr) where
341 type HelpConstraint (ParserPerm e d repr) d' = HelpConstraint (Parser e d) d'
347 Functor (UnTrans repr ()) =>
348 ParserPerm e d (UnTrans repr) k a -> ParserPerm e d repr k a
349 noTransParserPerm (ParserPerm a ma) = ParserPerm a (noTrans $ noTransParserPerm <$> ma)
353 Functor (UnTrans repr ()) =>
354 ParserPerm e d repr k a -> ParserPerm e d (UnTrans repr) k a
355 unTransParserPerm (ParserPerm a ma) = ParserPerm a (unTransParserPerm <$> unTrans ma)
359 (forall a b. repr a b -> repr a b) ->
360 ParserPerm e d repr k c -> ParserPerm e d repr k c
361 hoistParserPerm f (ParserPerm a ma) =
362 ParserPerm a (hoistParserPerm f <$> f ma)
364 -- ** Class 'CLI_Routing'
365 class CLI_Routing repr where
366 commands :: Map Name (repr a k) -> Map Name (repr a k) -> repr a k
367 -- taggeds :: TagConstraint repr a => Map (Either Char Name) (repr (a -> k) k) -> repr (a -> k) k
368 instance Ord e => CLI_Routing (Parser e d) where
369 commands preCmds cmds = Parser $
370 P.token check exp >>= unParser
372 exp = Set.fromList $ P.Tokens . pure . ArgSegment <$> Map.keys cmds
375 Map.lookup cmd cmds <|>
376 Map.lookup cmd preCmds
380 data Router repr a b where
381 -- | Lift any @(repr)@ into 'Router', those not useful to segregate
382 -- wrt. the 'Trans'formation performed, aka. 'noTrans'.
383 Router_Any :: repr a b -> Router repr a b
384 -- | Represent 'commands'.
386 Map Name (Router repr a k) ->
387 Map Name (Router repr a k) ->
389 -- | Represent 'tagged'.
390 Router_Tagged :: Tag -> Router repr f k -> Router repr f k
391 -- | Represent ('<.>').
392 Router_App :: Router repr a b -> Router repr b c -> Router repr a c
393 -- | Represent ('<!>').
394 Router_Alt :: Router repr a k -> Router repr b k -> Router repr (a:!:b) k
395 -- | Unify 'Router's which have different 'handlers'.
396 -- Useful to put alternative 'Router's in a 'Map' as in 'Router_Commands'.
397 Router_Union :: (b->a) -> Router repr a k -> Router repr b k
399 instance Ord e => Functor (Router (Parser e d) f) where
400 a2b`fmap`x = noTrans (a2b <$> unTrans x)
401 instance Ord e => Applicative (Router (Parser e d) f) where
402 pure = noTrans . pure
403 f <*> x = noTrans (unTrans f <*> unTrans x)
404 instance Ord e => Alternative (Router (Parser e d) f) where
405 empty = noTrans empty
406 f <|> x = noTrans (unTrans f <|> unTrans x)
407 instance Ord e => Permutable (Router (Parser e d)) where
408 type Permutation (Router (Parser e d)) = ParserPerm e d (Router (Parser e d))
409 runPermutation = noTrans . runPermutation . unTransParserPerm
410 toPermutation = noTransParserPerm . toPermutation . unTrans
411 toPermDefault a = noTransParserPerm . toPermDefault a . unTrans
412 instance (repr ~ Parser e d) => Show (Router repr a b) where
414 Router_Any{} -> showString "X"
415 Router_Commands _preCmds cmds -> showParen (p>=10) $ showString "Commands [" . go (Map.toList cmds) . showString "]"
417 go :: forall h k. [(Segment, Router repr h k)] -> ShowS
420 (showParen True $ showString (show n<>", ") . showsPrec 0 r) .
423 _ -> showString ", " . go xs
424 Router_Tagged n x -> showsPrec 10 n . showString " " . showsPrec p x
425 Router_App x y -> showParen (p>=4) $ showsPrec 4 x . showString " <.> " . showsPrec 4 y
426 Router_Alt x y -> showParen (p>=3) $ showsPrec 3 x . showString " <!> " . showsPrec 3 y
427 Router_Union _u x -> showString "Union [" . showsPrec 0 x . showString "]"
429 instance Ord e => Trans (Router (Parser e d)) where
430 type UnTrans (Router (Parser e d)) = Parser e d
432 unTrans (Router_Any x) = x
433 unTrans (Router_Alt x y) = unTrans x <!> unTrans y
434 unTrans (Router_App x y) = unTrans x <.> unTrans y
435 unTrans (Router_Commands preCmds cmds) = commands (unTrans <$> preCmds) (unTrans <$> cmds)
436 unTrans (Router_Tagged n x) = tagged n (unTrans x)
437 unTrans (Router_Union u x) = Parser $ (. u) <$> unParser (unTrans x)
439 instance Ord e => App (Router (Parser e d)) where
441 instance Ord e => Alt (Router (Parser e d)) where
443 alt x y = Router_Union (\a -> a:!:a) $ Router_Alt x y
444 instance Ord e => Pro (Router (Parser e d))
445 instance (repr ~ (Parser e d)) => CLI_Command (Router repr) where
448 let is = List.tail $ List.inits n in
449 let (preCmds, cmds) = List.splitAt (List.length is - 1) is in
451 (Map.fromAscList $ (,x) <$> preCmds)
452 (Map.fromAscList $ (,x) <$> cmds)
453 instance Ord e => CLI_Var (Router (Parser e d))
454 instance Ord e => CLI_Env (Router (Parser e d))
455 instance Ord e => CLI_Tag (Router (Parser e d)) where
456 tagged = Router_Tagged
457 instance CLI_Help (Router (Parser e d)) where
458 -- NOTE: set manually (instead of the 'Trans' default 'Router_Any')
459 -- to remove them all, since they are useless for 'Parser'
460 -- and may prevent patterns to be matched in 'router'.
464 instance Ord e => CLI_Response (Router (Parser e d))
465 instance Ord e => CLI_Routing (Router (Parser e d)) where
466 -- taggeds = Router_Taggeds
467 commands = Router_Commands
471 Router repr a b -> Router repr a b
472 router = {-debug1 "router" $-} \case
474 Router_Tagged n x -> Router_Tagged n (router x)
475 Router_Alt x y -> router x`router_Alt`router y
476 Router_Commands preCmds cmds ->
483 -- Associate to the right
484 Router_App (router x) $
485 Router_App (router y) (router z)
486 _ -> router xy `Router_App` router z
487 Router_Union u x -> Router_Union u (router x)
489 -- | Merge/reorder alternatives if possible or default to a 'Router_Alt'.
494 Router repr (a:!:b) k
495 router_Alt = {-debug2 "router_Alt"-} go
497 -- Merge alternative commands together.
498 go (Router_Commands xp xs) (Router_Commands yp ys) =
500 (xp`router_Commands`yp)
501 (xs`router_Commands`ys)
503 -- Merge left first or right first, depending on which removes 'Router_Alt'.
504 go x (y`Router_Alt`z) =
505 case x`router_Alt`y of
507 case y'`router_Alt`z of
508 yz@(Router_Alt _y z') ->
509 case x'`router_Alt`z' of
510 Router_Alt{} -> router x'`Router_Alt`yz
511 xz -> Router_Union (\(a:!:(b:!:c)) -> (a:!:c):!:b) $ xz`router_Alt`y
512 -- NOTE: prioritize the merged router 'xz' over over the non-mergeable 'y'.
513 yz -> x'`router_Alt`yz
514 xy -> Router_Union (\(a:!:(b:!:c)) -> (a:!:b):!:c) $ xy`router_Alt`z
515 go (x`Router_Alt`y) z =
516 case y`router_Alt`z of
518 case x`router_Alt`y' of
519 xy@(Router_Alt x' _y) ->
520 case x'`router_Alt`z' of
521 Router_Alt{} -> xy`Router_Alt`router z'
522 xz -> Router_Union (\((a:!:b):!:c) -> (a:!:c):!:b) $ xz`router_Alt`y
523 -- NOTE: prioritize the merged router 'xz' over the non-mergeable 'y'.
524 xy -> xy`router_Alt`z'
525 yz -> Router_Union (\((a:!:b):!:c) -> a:!:(b:!:c)) $ x`router_Alt`yz
527 -- Merge through 'Router_Union'.
528 go (Router_Union u x) y = Router_Union (\(a:!:b) -> u a:!:b) (x`router_Alt`y)
529 go x (Router_Union u y) = Router_Union (\(a:!:b) -> a:!:u b) (x`router_Alt`y)
532 go x y = x`Router_Alt`y
536 Map Segment (Router repr a k) ->
537 Map Segment (Router repr b k) ->
538 Map Segment (Router repr (a:!:b) k)
540 -- NOTE: a little bit more complex than required
541 -- in order to merge 'Router_Union's instead of nesting them,
542 -- such that 'unTrans' 'Router_Union' applies them all at once.
544 (Map.mapMissing $ const keepX)
545 (Map.mapMissing $ const keepY)
546 (Map.zipWithMatched $ const mergeFull)
549 Router_Union u r -> Router_Union (\(x:!:_y) -> u x) r
550 r -> Router_Union (\(x:!:_y) -> x) r
552 Router_Union u r -> Router_Union (\(_x:!:y) -> u y) r
553 r -> Router_Union (\(_x:!:y) -> y) r
555 Router_Union xu xr -> \case
556 Router_Union yu yr -> Router_Union (\(x:!:y) -> xu x:!:yu y) $ xr`router_Alt`yr
557 yr -> Router_Union (\(a:!:b) -> xu a:!:b) $ xr`router_Alt`yr
559 Router_Union yu yr -> Router_Union (\(a:!:b) -> a:!:yu b) $ xr`router_Alt`yr
560 yr -> xr`router_Alt`yr
567 | ArgEnv Name String -- ^ Here only for error reporting.
568 deriving (Eq,Ord,Show)
570 lexer :: [String] -> [Arg]
573 (`evalState` False) $
576 f :: String -> StateT Bool Identity [Arg]
579 if skip then return [ArgSegment s]
583 return [ArgTagLong ""]
584 '-':'-':cs -> return [ArgTagLong cs]
585 '-':cs@(_:_) -> return $ ArgTagShort <$> cs
586 seg -> return [ArgSegment seg]
588 showArg :: Arg -> String
590 ArgTagShort t -> '-':[t]
591 ArgTagLong t -> '-':'-':t
592 ArgSegment seg -> seg
593 ArgEnv name val -> name<>"="<>val
595 showArgs :: [Arg] -> String
596 showArgs args = List.intercalate " " $ showArg <$> args
598 instance P.Stream [Arg] where
599 type Token [Arg] = Arg
600 type Tokens [Arg] = [Arg]
601 tokenToChunk Proxy = pure
602 tokensToChunk Proxy = id
603 chunkToTokens Proxy = id
604 chunkLength Proxy = List.length
605 chunkEmpty Proxy = List.null
607 take1_ (t:ts) = Just (t, ts)
609 | n <= 0 = Just ([], s)
610 | List.null s = Nothing
611 | otherwise = Just (List.splitAt n s)
612 takeWhile_ = List.span
613 showTokens Proxy = showArgs . toList
614 -- NOTE: those make no sense when parsing a command line,
615 -- and should not be called since 'P.errorBundlePretty' is not used in 'parser'.
616 reachOffset = error "BUG: reachOffset must not be used on [Arg]"
617 reachOffsetNoLine = error "BUG: reachOffsetNoLine must not be used on [Arg]"