1{-# LANGUAGE PatternGuards #-}
3module Idris.Interactive(caseSplitAt, addClauseFrom, addProofClauseFrom,
4 addMissing, makeWith, doProofSearch,
7{- Bits and pieces for editing source files interactively, called from
11import Idris.Core.Evaluate
17import Idris.Delaborate
19import Idris.IdeSlave hiding (IdeSlaveCommand(..))
21import Idris.Elab.Value
27import System.Directory
30import Data.Maybe (fromMaybe)
31import Data.List (isSuffixOf)
35caseSplitAt :: FilePath -> Bool -> Int -> Name -> Idris ()
36caseSplitAt fn updatefile l n
37 = do src <- runIO $ readFile fn
38 res <- splitOnLine l n fn
39 iLOG (showSep "\n" (map show res))
40 let (before, (ap : later)) = splitAt (l-1) (lines src)
41 res' <- replaceSplits ap res
44 then do let fb = fn ++ "~" -- make a backup!
45 runIO $ writeFile fb (unlines before ++ new ++ unlines later)
46 runIO $ copyFile fb fn
47 else -- do iputStrLn (show res)
50addClauseFrom :: FilePath -> Bool -> Int -> Name -> Idris ()
51addClauseFrom fn updatefile l n
52 = do src <- runIO $ readFile fn
53 let (before, tyline : later) = splitAt (l-1) (lines src)
54 let indent = getIndent 0 (show n) tyline
55 cl <- getClause l n fn
56 -- add clause before first blank line in 'later'
57 let (nonblank, rest) = span (not . all isSpace) (tyline:later)
59 then do let fb = fn ++ "~"
60 runIO $ writeFile fb (unlines (before ++ nonblank) ++
61 replicate indent ' ' ++
64 runIO $ copyFile fb fn
68 getIndent i n xs | take 9 xs == "instance " = i
69 getIndent i n xs | take (length n) xs == n = i
70 getIndent i n (x : xs) = getIndent (i + 1) n xs
72addProofClauseFrom :: FilePath -> Bool -> Int -> Name -> Idris ()
73addProofClauseFrom fn updatefile l n
74 = do src <- runIO $ readFile fn
75 let (before, tyline : later) = splitAt (l-1) (lines src)
76 let indent = getIndent 0 (show n) tyline
77 cl <- getProofClause l n fn
78 -- add clause before first blank line in 'later'
79 let (nonblank, rest) = span (not . all isSpace) (tyline:later)
81 then do let fb = fn ++ "~"
82 runIO $ writeFile fb (unlines (before ++ nonblank) ++
83 replicate indent ' ' ++
86 runIO $ copyFile fb fn
90 getIndent i n xs | take (length n) xs == n = i
91 getIndent i n (x : xs) = getIndent (i + 1) n xs
93addMissing :: FilePath -> Bool -> Int -> Name -> Idris ()
94addMissing fn updatefile l n
95 = do src <- runIO $ readFile fn
96 let (before, tyline : later) = splitAt (l-1) (lines src)
97 let indent = getIndent 0 (show n) tyline
99 cl <- getInternalApp fn l
100 let n' = getAppName cl
102 extras <- case lookupCtxt n' (idris_patdefs i) of
104 [(_, tms)] -> do tms' <- nameMissing tms
105 showNew (show n ++ "_rhs") 1 indent tms'
106 let (nonblank, rest) = span (not . all isSpace) (tyline:later)
108 then do let fb = fn ++ "~"
109 runIO $ writeFile 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 n) = PRef fc (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 $ readFile 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 $ writeFile fb (unlines (before ++ nonblank)
157 runIO $ copyFile fb fn
158 else iPrintResult with
159 where getIndent s = length (takeWhile isSpace s)
162doProofSearch :: FilePath -> Bool -> Bool ->
163 Int -> Name -> [Name] -> Maybe Int -> Idris ()
164doProofSearch fn updatefile rec l n hints Nothing
165 = doProofSearch fn updatefile rec l n hints (Just 10)
166doProofSearch fn updatefile rec l n hints (Just depth)
167 = do src <- runIO $ readFile fn
168 let (before, tyline : later) = splitAt (l-1) (lines src)
170 mn <- case lookupNames n ctxt of
173 ns -> ierror (CantResolveAlts ns)
175 let (top, envlen, _) = case lookup mn (idris_metavars i) of
176 Just (t, e, False) -> (t, e, False)
177 _ -> (Nothing, 0, True)
179 let body t = PProof [Try (TSeq Intros (ProofSearch rec False depth t hints))
180 (ProofSearch rec False depth t hints)]
181 let def = PClause fc mn (PRef fc mn) [] (body top) []
183 (do elabDecl' EAll recinfo (PClauses fc [] mn [def])
184 (tm, ty) <- elabVal recinfo ERHS (PRef fc mn)
187 return . flip displayS "" . renderPretty 1.0 80 $
188 pprintPTerm defaultPPOption [] [] (idris_infixes i)
191 (delab i (specialise ctxt [] [(mn, 1)] tm)))))
192 (\e -> return ("?" ++ show n))
194 do let fb = fn ++ "~"
195 runIO $ writeFile fb (unlines before ++
196 updateMeta False tyline (show n) newmv ++ "\n"
198 runIO $ copyFile fb fn
199 else iPrintResult newmv
200 where dropCtxt 0 sc = sc
201 dropCtxt i (PPi _ _ _ sc) = dropCtxt (i - 1) sc
202 dropCtxt i (PLet _ _ _ sc) = dropCtxt (i - 1) sc
203 dropCtxt i (PLam _ _ sc) = dropCtxt (i - 1) sc
206 stripNS tm = mapPT dens tm where
207 dens (PRef fc n) = PRef fc (nsroot n)
210 nsroot (NS n _) = nsroot n
211 nsroot (SN (WhereN _ _ n)) = nsroot n
214updateMeta brack ('?':cs) n new
215 | length cs >= length n
216 = case splitAt (length n) cs of
218 if ((isSpace c || c == ')' || c == '}') && mv == n)
219 then addBracket brack new ++ (c : cs)
220 else '?' : mv ++ c : updateMeta True cs n new
221 (mv, []) -> if (mv == n) then addBracket brack new else '?' : mv
222updateMeta brack ('=':cs) n new = '=':updateMeta False cs n new
223updateMeta brack (c:cs) n new
224 = c : updateMeta (brack || not (isSpace c)) cs n new
225updateMeta brack [] n new = ""
227checkProv line n = isProv' False line n
229 isProv' p cs n | take (length n) cs == n = p
230 isProv' _ ('?':cs) n = isProv' False cs n
231 isProv' _ ('{':cs) n = isProv' True cs n
232 isProv' _ ('}':cs) n = isProv' True cs n
233 isProv' p (_:cs) n = isProv' p cs n
234 isProv' _ [] n = False
236addBracket False new = new
237addBracket True new@('(':xs) | last xs == ')' = new
238addBracket True new | any isSpace new = '(' : new ++ ")"
242makeLemma :: FilePath -> Bool -> Int -> Name -> Idris ()
243makeLemma fn updatefile l n
244 = do src <- runIO $ readFile fn
245 let (before, tyline : later) = splitAt (l-1) (lines src)
247 -- if the name is in braces, rather than preceded by a ?, treat it
248 -- as a lemma in a provisional definition
250 let isProv = checkProv tyline (show n)
253 mty <- case lookupTyName n ctxt of
255 [] -> ierror (NoSuchVariable n)
256 ns -> ierror (CantResolveAlts (map fst ns))
258 margs <- case lookup n (idris_metavars i) of
259 Just (_, arity, _) -> return arity
262 if (not isProv) then do
263 let skip = guessImps (tt_ctxt i) mty
265 let lem = show n ++ " : " ++ show (stripMNBind skip (delab i mty))
266 let lem_app = show n ++ appArgs skip margs mty
269 do let fb = fn ++ "~"
270 runIO $ writeFile fb (addLem before tyline lem lem_app later)
271 runIO $ copyFile fb fn
272 else case idris_outputmode i of
273 RawOutput _ -> iPrintResult $ lem ++ "\n" ++ lem_app
275 let good = SexpList [SymbolAtom "ok",
276 SexpList [SymbolAtom "metavariable-lemma",
277 SexpList [SymbolAtom "replace-metavariable",
279 SexpList [SymbolAtom "definition-type",
281 in runIO . hPutStrLn h $ convSExp "return" good n
283 else do -- provisional definition
284 let lem_app = show n ++ appArgs [] (-1) mty ++
285 " = ?" ++ show n ++ "_rhs"
287 do let fb = fn ++ "~"
288 runIO $ writeFile fb (addProv before tyline lem_app later)
289 runIO $ copyFile fb fn
290 else case idris_outputmode i of
291 RawOutput _ -> iPrintResult $ lem_app
293 let good = SexpList [SymbolAtom "ok",
294 SexpList [SymbolAtom "provisional-definition-lemma",
295 SexpList [SymbolAtom "definition-clause",
296 StringAtom lem_app]]]
297 in runIO . hPutStrLn h $ convSExp "return" good n
299 where getIndent s = length (takeWhile isSpace s)
301 appArgs skip 0 _ = ""
302 appArgs skip i (Bind n@(UN c) (Pi _) sc)
303 | (thead c /= '_' && n `notElem` skip)
304 = " " ++ show n ++ appArgs skip (i - 1) sc
305 appArgs skip i (Bind _ (Pi _) sc) = appArgs skip (i - 1) sc
306 appArgs skip i _ = ""
308 stripMNBind skip (PPi b n@(UN c) ty sc)
309 | (thead c /= '_' && n `notElem` skip) ||
310 take 4 (str c) == "__pi" -- keep in type, but not in app
311 = PPi b n ty (stripMNBind skip sc)
312 stripMNBind skip (PPi b _ ty sc) = stripMNBind skip sc
313 stripMNBind skip t = t
315 -- Guess which binders should be implicits in the generated lemma.
316 -- Make them implicit if they appear guarded by a top level constructor,
317 -- or at the top level themselves.
318 guessImps :: Context -> Term -> [Name]
319 guessImps ctxt (Bind n (Pi _) sc)
320 | guarded ctxt n (substV (P Bound n Erased) sc)
321 = n : guessImps ctxt sc
322 | otherwise = guessImps ctxt sc
323 guessImps ctxt _ = []
325 guarded ctxt n (P _ n' _) | n == n' = True
326 guarded ctxt n ap@(App _ _)
327 | (P _ f _, args) <- unApply ap,
328 isConName f ctxt = any (guarded ctxt n) args
329-- guarded ctxt n (Bind (UN cn) (Pi t) sc) -- ignore shadows
330-- | thead cn /= '_' = guarded ctxt n t || guarded ctxt n sc
331 guarded ctxt n (Bind _ (Pi t) sc)
332 = guarded ctxt n t || guarded ctxt n sc
333 guarded ctxt n _ = False
337 addLem before tyline lem lem_app later
338 = let (bef_end, blankline : bef_start)
339 = case span (not . blank) (reverse before) of
340 (bef, []) -> (bef, "" : [])
342 mvline = updateMeta False tyline (show n) lem_app in
343 unlines $ reverse bef_start ++
344 [blankline, lem, blankline] ++
345 reverse bef_end ++ mvline : later
347 addProv before tyline lem_app later
348 = let (later_bef, blankline : later_end)
349 = case span (not . blank) later of
350 (bef, []) -> (bef, "" : [])
352 unlines $ before ++ tyline :
353 (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