1{-# LANGUAGE PatternGuards #-}
3module Idris.Interactive(caseSplitAt, addClauseFrom, addProofClauseFrom,
4 addMissing, makeWith, makeCase, doProofSearch,
7{- Bits and pieces for editing source files interactively, called from
11import Idris.Core.Evaluate
16import Idris.Delaborate
18import Idris.IdeMode hiding (IdeModeCommand(..))
19import Idris.Elab.Value
26import System.Directory
29import Data.Maybe (fromMaybe)
30import Data.List (isSuffixOf)
34caseSplitAt :: FilePath -> Bool -> Int -> Name -> Idris ()
35caseSplitAt fn updatefile l n
36 = do src <- runIO $ readSource fn
37 res <- splitOnLine l n fn
38 logLvl 1 (showSep "\n" (map show res))
39 let (before, (ap : later)) = splitAt (l-1) (lines src)
40 res' <- replaceSplits ap res
43 then do let fb = fn ++ "~" -- make a backup!
44 runIO $ writeSource fb (unlines before ++ new ++ unlines later)
45 runIO $ copyFile fb fn
46 else -- do iputStrLn (show res)
49addClauseFrom :: FilePath -> Bool -> Int -> Name -> Idris ()
50addClauseFrom fn updatefile l n
51 = do src <- runIO $ readSource fn
52 let (before, tyline : later) = splitAt (l-1) (lines src)
53 let indent = getIndent 0 (show n) tyline
54 cl <- getClause l n fn
55 -- add clause before first blank line in 'later'
56 let (nonblank, rest) = span (not . all isSpace) (tyline:later)
58 then do let fb = fn ++ "~"
59 runIO $ writeSource fb (unlines (before ++ nonblank) ++
60 replicate indent ' ' ++
63 runIO $ copyFile fb fn
67 getIndent i n xs | take 9 xs == "instance " = i
68 getIndent i n xs | take (length n) xs == n = i
69 getIndent i n (x : xs) = getIndent (i + 1) n xs
71addProofClauseFrom :: FilePath -> Bool -> Int -> Name -> Idris ()
72addProofClauseFrom fn updatefile l n
73 = do src <- runIO $ readSource fn
74 let (before, tyline : later) = splitAt (l-1) (lines src)
75 let indent = getIndent 0 (show n) tyline
76 cl <- getProofClause l n fn
77 -- add clause before first blank line in 'later'
78 let (nonblank, rest) = span (not . all isSpace) (tyline:later)
80 then do let fb = fn ++ "~"
81 runIO $ writeSource fb (unlines (before ++ nonblank) ++
82 replicate indent ' ' ++
85 runIO $ copyFile fb fn
89 getIndent i n xs | take (length n) xs == n = i
90 getIndent i n (x : xs) = getIndent (i + 1) n xs
92addMissing :: FilePath -> Bool -> Int -> Name -> Idris ()
93addMissing fn updatefile l n
94 = do src <- runIO $ readSource fn
95 let (before, tyline : later) = splitAt (l-1) (lines src)
96 let indent = getIndent 0 (show n) tyline
98 cl <- getInternalApp fn l
99 let n' = getAppName cl
101 extras <- case lookupCtxt n' (idris_patdefs i) of
103 [(_, tms)] -> do tms' <- nameMissing tms
104 showNew (show n ++ "_rhs") 1 indent tms'
105 other -> return "" -- happens if called on a metavar, or with no clauses
106 let (nonblank, rest) = span (not . all isSpace) (tyline:later)
108 then do let fb = fn ++ "~"
109 runIO $ writeSource fb (unlines (before ++ nonblank)
110 ++ extras ++ unlines rest)
111 runIO $ copyFile fb fn
112 else iPrintResult extras
113 where showPat = show . stripNS
114 stripNS tm = mapPT dens tm where
115 dens (PRef fc hls n) = PRef fc hls (nsroot n)
118 nsroot (NS n _) = nsroot n
119 nsroot (SN (WhereN _ _ n)) = nsroot n
122 getAppName (PApp _ r _) = getAppName r
123 getAppName (PRef _ _ r) = r
126 makeIndent ind | ".lidr" `isSuffixOf` fn = '>' : ' ' : replicate (ind-2) ' '
127 | otherwise = replicate ind ' '
129 showNew nm i ind (tm : tms)
130 = do (nm', i') <- getUniq nm i
131 rest <- showNew nm i' ind tms
132 return (makeIndent ind ++
133 showPat tm ++ " = ?" ++ nm' ++
135 showNew nm i _ [] = return ""
138 getIndent i n xs | take (length n) xs == n = i
139 getIndent i n (x : xs) = getIndent (i + 1) n xs
142makeWith :: FilePath -> Bool -> Int -> Name -> Idris ()
143makeWith fn updatefile l n
144 = do src <- runIO $ readSource fn
145 let (before, tyline : later) = splitAt (l-1) (lines src)
146 let ind = getIndent tyline
147 let with = mkWith tyline n
148 -- add clause before first blank line in 'later',
149 -- or (TODO) before first line with same indentation as tyline
150 let (nonblank, rest) = span (\x -> not (all isSpace x) &&
151 not (ind == getIndent x)) later
153 do let fb = fn ++ "~"
154 runIO $ writeSource fb (unlines (before ++ nonblank)
157 runIO $ copyFile fb fn
158 else iPrintResult with
159 where getIndent s = length (takeWhile isSpace s)
161-- Replace the given metavariable on the given line with a 'case'
162-- block, using a _ for the scrutinee
163makeCase :: FilePath -> Bool -> Int -> Name -> Idris ()
164makeCase fn updatefile l n
165 = do src <- runIO $ readSource fn
166 let (before, tyline : later) = splitAt (l-1) (lines src)
167 let newcase = addCaseSkel (show n) tyline
170 do let fb = fn ++ "~"
171 runIO $ writeSource fb (unlines (before ++ newcase ++ later))
172 runIO $ copyFile fb fn
173 else iPrintResult (showSep "\n" newcase)
174 where addCaseSkel n line =
175 let b = brackets False line in
176 case findSubstr ('?':n) line of
177 Just (before, pos, after) ->
178 [before ++ (if b then "(" else "") ++ "case _ of",
179 take (pos + (if b then 6 else 5)) (repeat ' ') ++
180 "case_val => ?" ++ n ++ if b then ")" else "",
182 Nothing -> fail "No such metavariable"
184 -- Assume case needs to be bracketed unless the metavariable is
185 -- on its own after an =
186 brackets eq line | line == '?' : show n = not eq
187 brackets eq ('=':ls) = brackets True ls
188 brackets eq (' ':ls) = brackets eq ls
189 brackets eq (l : ls) = brackets False ls
190 brackets eq [] = True
192 findSubstr n xs = findSubstr' [] 0 n xs
194 findSubstr' acc i n xs | take (length n) xs == n
195 = Just (reverse acc, i, drop (length n) xs)
196 findSubstr' acc i n [] = Nothing
197 findSubstr' acc i n (x : xs) = findSubstr' (x : acc) (i + 1) n xs
203doProofSearch :: FilePath -> Bool -> Bool ->
204 Int -> Name -> [Name] -> Maybe Int -> Idris ()
205doProofSearch fn updatefile rec l n hints Nothing
206 = doProofSearch fn updatefile rec l n hints (Just 20)
207doProofSearch fn updatefile rec l n hints (Just depth)
208 = do src <- runIO $ readSource fn
209 let (before, tyline : later) = splitAt (l-1) (lines src)
211 mn <- case lookupNames n ctxt of
214 ns -> ierror (CantResolveAlts ns)
216 let (top, envlen, psnames, _)
217 = case lookup mn (idris_metavars i) of
218 Just (t, e, ns, False) -> (t, e, ns, False)
219 _ -> (Nothing, 0, [], True)
221 let body t = PProof [Try (TSeq Intros (ProofSearch rec False depth t psnames hints))
222 (ProofSearch rec False depth t psnames hints)]
223 let def = PClause fc mn (PRef fc [] mn) [] (body top) []
225 (do elabDecl' EAll recinfo (PClauses fc [] mn [def])
226 (tm, ty) <- elabVal recinfo ERHS (PRef fc [] mn)
229 return . flip displayS "" . renderPretty 1.0 80 $
230 pprintPTerm defaultPPOption [] [] (idris_infixes i)
233 (delab i (fst (specialise ctxt [] [(mn, 1)] tm))))))
234 (\e -> return ("?" ++ show n))
236 do let fb = fn ++ "~"
237 runIO $ writeSource fb (unlines before ++
238 updateMeta False tyline (show n) newmv ++ "\n"
240 runIO $ copyFile fb fn
241 else iPrintResult newmv
242 where dropCtxt 0 sc = sc
243 dropCtxt i (PPi _ _ _ _ sc) = dropCtxt (i - 1) sc
244 dropCtxt i (PLet _ _ _ _ _ sc) = dropCtxt (i - 1) sc
245 dropCtxt i (PLam _ _ _ _ sc) = dropCtxt (i - 1) sc
248 stripNS tm = mapPT dens tm where
249 dens (PRef fc hls n) = PRef fc hls (nsroot n)
252 nsroot (NS n _) = nsroot n
253 nsroot (SN (WhereN _ _ n)) = nsroot n
256updateMeta brack ('?':cs) n new
257 | length cs >= length n
258 = case splitAt (length n) cs of
260 if ((isSpace c || c == ')' || c == '}') && mv == n)
261 then addBracket brack new ++ (c : cs)
262 else '?' : mv ++ c : updateMeta True cs n new
263 (mv, []) -> if (mv == n) then addBracket brack new else '?' : mv
264updateMeta brack ('=':cs) n new = '=':updateMeta False cs n new
265updateMeta brack (c:cs) n new
266 = c : updateMeta (brack || not (isSpace c)) cs n new
267updateMeta brack [] n new = ""
269checkProv line n = isProv' False line n
271 isProv' p cs n | take (length n) cs == n = p
272 isProv' _ ('?':cs) n = isProv' False cs n
273 isProv' _ ('{':cs) n = isProv' True cs n
274 isProv' _ ('}':cs) n = isProv' True cs n
275 isProv' p (_:cs) n = isProv' p cs n
276 isProv' _ [] n = False
278addBracket False new = new
279addBracket True new@('(':xs) | last xs == ')' = new
280addBracket True new | any isSpace new = '(' : new ++ ")"
284makeLemma :: FilePath -> Bool -> Int -> Name -> Idris ()
285makeLemma fn updatefile l n
286 = do src <- runIO $ readSource fn
287 let (before, tyline : later) = splitAt (l-1) (lines src)
289 -- if the name is in braces, rather than preceded by a ?, treat it
290 -- as a lemma in a provisional definition
292 let isProv = checkProv tyline (show n)
295 (fname, mty) <- case lookupTyName n ctxt of
297 [] -> ierror (NoSuchVariable n)
298 ns -> ierror (CantResolveAlts (map fst ns))
300 margs <- case lookup fname (idris_metavars i) of
301 Just (_, arity, _, _) -> return arity
304 if (not isProv) then do
305 let skip = guessImps i (tt_ctxt i) mty
306 let classes = guessClasses i (tt_ctxt i) mty
308 let lem = show n ++ " : " ++
309 constraints i classes mty ++
310 showTmOpts (defaultPPOption { ppopt_pinames = True })
311 (stripMNBind skip margs (delab i mty))
312 let lem_app = show n ++ appArgs skip margs mty
315 do let fb = fn ++ "~"
316 runIO $ writeSource fb (addLem before tyline lem lem_app later)
317 runIO $ copyFile fb fn
318 else case idris_outputmode i of
319 RawOutput _ -> iPrintResult $ lem ++ "\n" ++ lem_app
321 let good = SexpList [SymbolAtom "ok",
322 SexpList [SymbolAtom "metavariable-lemma",
323 SexpList [SymbolAtom "replace-metavariable",
325 SexpList [SymbolAtom "definition-type",
327 in runIO . hPutStrLn h $ convSExp "return" good n
329 else do -- provisional definition
330 let lem_app = show n ++ appArgs [] (-1) mty ++
331 " = ?" ++ show n ++ "_rhs"
333 do let fb = fn ++ "~"
334 runIO $ writeSource fb (addProv before tyline lem_app later)
335 runIO $ copyFile fb fn
336 else case idris_outputmode i of
337 RawOutput _ -> iPrintResult $ lem_app
339 let good = SexpList [SymbolAtom "ok",
340 SexpList [SymbolAtom "provisional-definition-lemma",
341 SexpList [SymbolAtom "definition-clause",
342 StringAtom lem_app]]]
343 in runIO . hPutStrLn h $ convSExp "return" good n
345 where getIndent s = length (takeWhile isSpace s)
347 appArgs skip 0 _ = ""
348 appArgs skip i (Bind n@(UN c) (Pi _ _ _) sc)
349 | (thead c /= '_' && n `notElem` skip)
350 = " " ++ show n ++ appArgs skip (i - 1) sc
351 appArgs skip i (Bind _ (Pi _ _ _) sc) = appArgs skip (i - 1) sc
352 appArgs skip i _ = ""
354 stripMNBind _ args t | args <= 0 = t
355 stripMNBind skip args (PPi b n@(UN c) _ ty sc)
356 | n `notElem` skip ||
357 take 4 (str c) == "__pi" -- keep in type, but not in app
358 = PPi b n NoFC ty (stripMNBind skip (args - 1) sc)
359 stripMNBind skip args (PPi b _ _ ty sc) = stripMNBind skip (args - 1) sc
360 stripMNBind skip args t = t
362 constraints :: IState -> [Name] -> Type -> String
363 constraints i [] ty = ""
364 constraints i [n] ty = showSep ", " (showConstraints i [n] ty) ++ " => "
365 constraints i ns ty = "(" ++ showSep ", " (showConstraints i ns ty) ++ ") => "
367 showConstraints i ns (Bind n (Pi _ ty _) sc)
368 | n `elem` ns = show (delab i ty) :
369 showConstraints i ns (substV (P Bound n Erased) sc)
370 | otherwise = showConstraints i ns (substV (P Bound n Erased) sc)
371 showConstraints _ _ _ = []
373 -- Guess which binders should be implicits in the generated lemma.
374 -- Make them implicit if they appear guarded by a top level constructor,
375 -- or at the top level themselves.
376 -- Also, make type class instances implicit
377 guessImps :: IState -> Context -> Term -> [Name]
378 -- machine names aren't lifted
379 guessImps ist ctxt (Bind n@(MN _ _) (Pi _ ty _) sc)
380 = n : guessImps ist ctxt sc
381 guessImps ist ctxt (Bind n (Pi _ ty _) sc)
382 | guarded ctxt n (substV (P Bound n Erased) sc)
383 = n : guessImps ist ctxt sc
385 = n : guessImps ist ctxt sc
386 | TType _ <- ty = n : guessImps ist ctxt sc
387 | ignoreName n = n : guessImps ist ctxt sc
388 | otherwise = guessImps ist ctxt sc
389 guessImps ist ctxt _ = []
391 -- TMP HACK unusable name so don't lift
392 ignoreName n = case show n of
396 guessClasses :: IState -> Context -> Term -> [Name]
397 guessClasses ist ctxt (Bind n (Pi _ ty _) sc)
398 | isParamClass ist ty
399 = n : guessClasses ist ctxt sc
400 | otherwise = guessClasses ist ctxt sc
401 guessClasses ist ctxt _ = []
404 | (P _ n _, args) <- unApply t
405 = case lookupCtxtExact n (idris_classes ist) of
411 | (P _ n _, args) <- unApply t
412 = case lookupCtxtExact n (idris_classes ist) of
413 Just _ -> any isV args
416 where isV (V _) = True
419 guarded ctxt n (P _ n' _) | n == n' = True
420 guarded ctxt n ap@(App _ _ _)
421 | (P _ f _, args) <- unApply ap,
422 isConName f ctxt = any (guarded ctxt n) args
423-- guarded ctxt n (Bind (UN cn) (Pi t) sc) -- ignore shadows
424-- | thead cn /= '_' = guarded ctxt n t || guarded ctxt n sc
425 guarded ctxt n (Bind _ (Pi _ t _) sc)
426 = guarded ctxt n t || guarded ctxt n sc
427 guarded ctxt n _ = False
431 addLem before tyline lem lem_app later
432 = let (bef_end, blankline : bef_start)
433 = case span (not . blank) (reverse before) of
434 (bef, []) -> (bef, "" : [])
436 mvline = updateMeta False tyline (show n) lem_app in
437 unlines $ reverse bef_start ++
438 [blankline, lem, blankline] ++
439 reverse bef_end ++ mvline : later
441 addProv before tyline lem_app later
442 = let (later_bef, blankline : later_end)
443 = case span (not . blank) later of
444 (bef, []) -> (bef, "" : [])
446 unlines $ before ++ tyline :
447 (later_bef ++ [blankline, lem_app, blankline] ++
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list