0 | module Idris.IDEMode.CaseSplit
6 | import Parser.Lexer.Source
9 | import TTImp.Interactive.CaseSplit
11 | import TTImp.TTImp.Functor
14 | import Idris.IDEMode.TokenLine
15 | import Idris.REPL.Opts
16 | import Idris.Resugar
19 | import Data.List.Views
20 | import Libraries.Data.List.Extra
22 | import Libraries.Data.String as L
32 | Updates = List (RawName, String)
35 | toStrUpdate : {auto c : Ref Ctxt Defs} ->
36 | {auto s : Ref Syn SyntaxInfo} ->
37 | (Name, RawImp) -> Core Updates
38 | toStrUpdate (UN (Basic n), term)
39 | = do clause <- pterm (map defaultKindedName term)
40 | pure [(n, show (bracket clause))]
42 | bracket : PTerm' nm -> PTerm' nm
43 | bracket tm@(PRef {}) = tm
44 | bracket tm@(PList {}) = tm
45 | bracket tm@(PSnocList {}) = tm
46 | bracket tm@(PPair {}) = tm
47 | bracket tm@(PUnit {}) = tm
48 | bracket tm@(PComprehension {}) = tm
49 | bracket tm@(PPrimVal {}) = tm
50 | bracket tm = PBracketed emptyFC tm
51 | toStrUpdate _ = pure []
53 | data UPD : Type where
58 | isWhitespace : SourcePart -> Bool
59 | isWhitespace (Whitespace _) = True
60 | isWhitespace _ = False
62 | splitSpaceAndComments : List SourcePart -> (List SourcePart, List SourcePart)
63 | splitSpaceAndComments = skipSpace Lin
65 | skipSpace : SnocList SourcePart -> List SourcePart -> (List SourcePart, List SourcePart)
66 | skipComment : SnocList SourcePart -> List SourcePart -> (List SourcePart, List SourcePart)
68 | skipComment left [] = (left <>> [], [])
69 | skipComment left (Other "-" :: RBrace :: toks) = skipSpace (left :< Other "-" :< RBrace) toks
70 | skipComment left (tok :: toks) = skipComment (left :< tok) toks
72 | skipSpace left [] = (left <>> [], [])
73 | skipSpace left (LBrace :: Other "-" :: toks) = skipComment (left :< LBrace :< Other "-") toks
74 | skipSpace left (Whitespace ws :: toks) = skipSpace (left :< Whitespace ws) toks
75 | skipSpace left toks = (left <>> [], toks)
77 | data Wrap = Brace | Auto | Paren | Bracket
83 | doUpdates : {auto s : Ref Syn SyntaxInfo} ->
84 | {auto u : Ref UPD (List String)} ->
85 | Defs -> Updates -> List Wrap -> List SourcePart ->
86 | Core (List SourcePart)
87 | doUpdates defs ups stk [] = pure []
89 | doUpdates defs ups stk (Name n :: AsPattern :: xs)
90 | = pure $
Name n :: AsPattern :: !(doUpdates defs ups stk xs)
94 | doUpdates defs ups stk (AsPattern :: LBrace :: xs)
95 | = pure $
AsPattern :: LBrace :: !(doUpdates defs ups (Auto :: stk) xs)
96 | doUpdates defs ups stk toks@(LBrace :: Other "-" :: _) =
97 | let (ws, nws) = splitSpaceAndComments toks in
98 | pure (ws ++ !(doUpdates defs ups stk nws))
100 | doUpdates defs ups stk (Other "(" :: xs) = pure (Other "(" :: !(doUpdates defs ups (Paren :: stk) xs))
101 | doUpdates defs ups stk (Other ")" :: xs) = case stk of
102 | (Paren :: stk) => pure (Other ")" :: !(doUpdates defs ups stk xs))
103 | _ => pure (Other ")" :: !(doUpdates defs ups stk xs))
104 | doUpdates defs ups stk (Other "[" :: xs) = pure (Other "[" :: !(doUpdates defs ups (Bracket :: stk) xs))
105 | doUpdates defs ups stk (Other "]" :: xs) = case stk of
106 | Bracket :: stk => pure (Other "]" :: !(doUpdates defs ups stk xs))
107 | stk => pure (Other "]" :: !(doUpdates defs ups stk xs))
108 | doUpdates defs ups stk (Other "-" :: Other "-" :: xs) = pure (Other "--" :: xs)
110 | doUpdates defs ups stk (LBrace :: xs) =
111 | let (ws, nws) = splitSpaceAndComments xs in
113 | Name n :: rest => case lookup n ups of
114 | Just up => pure (LBrace :: ws ++ Name n :: Whitespace " " :: Equal :: Whitespace " " :: Other up :: !(doUpdates defs ups stk rest))
115 | Nothing => pure (LBrace :: ws ++ !(doUpdates defs ups (Brace :: stk) nws))
116 | _ => pure (LBrace :: ws ++ !(doUpdates defs ups (Brace :: stk) nws))
118 | doUpdates defs ups stk (Other "," :: xs) =
119 | let (ws, nws) = splitSpaceAndComments xs in
121 | Name n :: rest => case lookup n ups of
122 | Nothing => pure (Other "," :: ws ++ !(doUpdates defs ups stk nws))
123 | Just up => case stk of
124 | Brace :: _ => pure (Other "," :: ws ++ Name n :: Whitespace " " :: Equal :: Whitespace " " :: Other up :: !(doUpdates defs ups stk rest))
125 | _ => pure (Other "," :: ws ++ Other up :: !(doUpdates defs ups stk rest))
126 | _ => pure (Other "," :: ws ++ !(doUpdates defs ups stk nws))
128 | doUpdates defs ups stk (RBrace :: xs) = case stk of
129 | Brace :: stk => pure (RBrace :: !(doUpdates defs ups stk xs))
130 | Auto :: stk => pure (RBrace :: !(doUpdates defs ups stk xs))
131 | stk => pure (RBrace :: !(doUpdates defs ups stk xs))
135 | doUpdates defs ups stk (Name n :: xs)
136 | = case lookup n ups of
137 | Nothing => pure (Name n :: !(doUpdates defs ups stk xs))
138 | Just up => pure (Other up :: !(doUpdates defs ups stk xs))
141 | doUpdates defs ups stk (HoleName n :: xs)
142 | = do used <- get UPD
143 | n' <- uniqueHoleName defs used n
144 | put UPD (n' :: used)
145 | pure $
HoleName n' :: !(doUpdates defs ups stk xs)
147 | doUpdates defs ups stk (x :: xs)
148 | = pure $
x :: !(doUpdates defs ups stk xs)
153 | updateAll : {auto s : Ref Syn SyntaxInfo} ->
154 | {auto u : Ref UPD (List String)} ->
155 | Defs -> List SourcePart -> List Updates ->
157 | updateAll defs l [] = pure []
158 | updateAll defs l (rs :: rss)
159 | = do l' <- doUpdates defs rs [] l
160 | rss' <- updateAll defs l rss
161 | pure (concatMap toString l' :: rss')
165 | getReplaces : {auto c : Ref Ctxt Defs} ->
166 | {auto s : Ref Syn SyntaxInfo} ->
167 | List (Name, RawImp) -> Core Updates
168 | getReplaces updates
169 | = do strups <- traverse toStrUpdate updates
170 | pure (concat strups)
172 | showImpossible : {auto c : Ref Ctxt Defs} ->
173 | {auto s : Ref Syn SyntaxInfo} ->
174 | (indent : Nat) -> RawImp -> Core String
175 | showImpossible indent lhs
176 | = do clause <- pterm (map defaultKindedName lhs)
177 | pure (fastPack (replicate indent ' ') ++ show clause ++ " impossible")
190 | data CaseStmtType = Oneline Nat
196 | getCaseStmtType : (toks : List SourcePart) -> Maybe CaseStmtType
197 | getCaseStmtType toks
198 | = let nws = dropComment $
filter (not . isWhitespace) toks in
201 | case Lin <>< nws of
204 | start :< HoleName _ :< Other ")" =>
210 | case findIndex isNameOf (Lin <>< toks) of
211 | Nothing => Just HoleNameParen
212 | (Just skotOfIndex) =>
215 | let ofIndex = minus (length toks) (finToNat skotOfIndex) in
216 | Just $
OnelineParen (calcIndent ofIndex toks)
218 | start :< HoleName _ =>
219 | case findIndex isNameOf (Lin <>< toks) of
221 | (Just skotOfIndex) =>
222 | let ofIndex = minus (length toks) (finToNat skotOfIndex) in
223 | Just $
Oneline (calcIndent ofIndex toks)
227 | dropComment : List SourcePart -> List SourcePart
228 | dropComment [] = []
229 | dropComment (Other "-" :: Other "-" :: xs) = []
230 | dropComment (x :: xs) = x :: dropComment xs
232 | isNameOf : SourcePart -> Bool
233 | isNameOf (Name "of") = True
236 | calcIndent : Nat -> List SourcePart -> Nat
237 | calcIndent ofIndex toks
238 | = let (preOf, _) = splitAt ofIndex toks in
239 | foldr (\e,a => a + (length . toString) e) 0 preOf
244 | dropLast : (updChars : List Char) -> List Char
245 | dropLast updChars with (snocList updChars)
246 | dropLast [] | Empty = []
247 | dropLast (xs ++ [x]) | (Snoc x xs rec) = xs
250 | dropLastParen : SnocList Char -> SnocList Char
251 | dropLastParen Lin = Lin
252 | dropLastParen (cs :< ')') = cs
253 | dropLastParen (cs :< c) = dropLastParen cs :< c
256 | parenTrim : String -> String
257 | parenTrim = L.rtrim . fastPack . cast . dropLastParen . cast . fastUnpack
261 | onelineIndent : Nat -> String -> String
262 | onelineIndent indentation
263 | = (indent indentation) . fastPack . (drop indentation) . fastUnpack
267 | handleOneline : (indentation : Nat) -> (upds : List String) -> List String
268 | handleOneline indentation [] = []
269 | handleOneline indentation (u :: us) = u :: ((onelineIndent indentation) <$> us)
276 | handleOnelineParen : (indentation : Nat) -> (upds : List String) -> List String
277 | handleOnelineParen indentation upds with (snocList upds)
278 | handleOnelineParen indentation [] | Empty = []
279 | handleOnelineParen indentation (xs ++ [x]) | (Snoc x xs rec)
280 | = handleMiddle xs ++ [onelineIndent indentation x]
282 | handleMiddle : (us : List String) -> List String
283 | handleMiddle [] = []
284 | handleMiddle (u :: us)
285 | = (parenTrim $
onelineIndent indentation u) :: handleMiddle us
289 | handleHoleNameParen : (upds : List String) -> List String
290 | handleHoleNameParen upds with (snocList upds)
291 | handleHoleNameParen [] | Empty = []
292 | handleHoleNameParen (xs ++ [x]) | (Snoc x xs rec) = map parenTrim xs ++ [x]
297 | handleCaseStmtType : (upds : List String) ->
298 | (cst : CaseStmtType) ->
300 | handleCaseStmtType [] _ = []
301 | handleCaseStmtType (u :: us) (Oneline indentation) = handleOneline indentation (u :: us)
302 | handleCaseStmtType (u :: us) (OnelineParen indentation)
303 | = (parenTrim u) :: handleOnelineParen indentation us
304 | handleCaseStmtType upds HoleNameParen = handleHoleNameParen upds
309 | updateCase : {auto c : Ref Ctxt Defs} ->
310 | {auto s : Ref Syn SyntaxInfo} ->
311 | {auto o : Ref ROpts REPLOpts} ->
312 | List ClauseUpdate -> Int -> Int ->
314 | updateCase splits line col
315 | = do opts <- get ROpts
316 | case mainfile opts of
317 | Nothing => throw (InternalError "No file loaded")
319 | do Right file <- coreLift $
readFile f
320 | | Left err => throw (FileErr f err)
321 | let thisline = elemAt (lines file) (integerToNat (cast line))
323 | Nothing => throw (InternalError "File too short!")
325 | do let valid = mapMaybe getValid splits
326 | let bad = mapMaybe getBad splits
327 | log "interaction.casesplit" 3 $
"Valid: " ++ show valid
328 | log "interaction.casesplit" 3 $
"Bad: " ++ show bad
330 | then do let indent = getIndent 0 $
fastUnpack l
331 | traverse (showImpossible indent) bad
332 | else do rs <- traverse getReplaces valid
333 | let stok = tokens l
336 | upds <- updateAll defs stok rs
337 | pure $
case getCaseStmtType stok of
340 | handleCaseStmtType upds cst
342 | getValid : ClauseUpdate -> Maybe (List (Name, RawImp))
343 | getValid (Valid _ ups) = Just ups
344 | getValid _ = Nothing
346 | getBad : ClauseUpdate -> Maybe RawImp
347 | getBad (Impossible lhs) = Just lhs
350 | getIndent : (acc : Nat) -> List Char -> Nat
351 | getIndent acc [] = acc
352 | getIndent acc (' ' :: xs) = getIndent (acc + 1) xs
353 | getIndent acc _ = acc
355 | fnName : Bool -> Name -> String
356 | fnName lhs (UN (Basic n))
357 | = if isIdentNormal n then n
358 | else if lhs then "(" ++ n ++ ")"
360 | fnName lhs (NS _ n) = fnName lhs n
361 | fnName lhs (DN s _) = s
362 | fnName lhs n = nameRoot n
366 | getClause : {auto c : Ref Ctxt Defs} ->
367 | {auto m : Ref MD Metadata} ->
368 | {auto o : Ref ROpts REPLOpts} ->
369 | Int -> Name -> Core (Maybe String)
371 | = do defs <- get Ctxt
372 | Just (loc, nidx, envlen, ty) <- findTyDeclAt (\p, n => onLine (l-1) p)
373 | | Nothing => pure Nothing
374 | n <- getFullName nidx
375 | argns <- getEnvArgNames defs envlen !(nf defs Env.empty ty)
376 | Just srcLine <- getSourceLine l
377 | | Nothing => pure Nothing
378 | let (mark, src) = isLitLine srcLine
379 | pure (Just (indent mark loc ++ fnName True n ++ concat (map (" " ++) argns) ++
380 | " = ?" ++ fnName False n ++ "_rhs"))
382 | indent : Maybe String -> NonEmptyFC -> String
383 | indent (Just mark) fc
384 | = relit (Just mark) $
pack (replicate (integerToNat (cast (max 0 (snd (startPos fc) - 1)))) ' ')
385 | indent Nothing fc = pack (replicate (integerToNat (cast (snd (startPos fc)))) ' ')