3Description : Idris' IDE Mode
6Maintainer : The Idris Community.
8{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
10{-# LANGUAGE FlexibleInstances, IncoherentInstances, PatternGuards #-}
12module Idris.IdeMode(parseMessage, convSExp, WhatDocs(..), IdeModeCommand(..), sexpToCommand, toSExp, SExp(..), SExpable, Opt(..), ideModeEpoch, getLen, getNChar, sExpToString) where
14import Idris.Core.Binary
17import Control.Applicative hiding (Const)
18import qualified Data.Binary as Binary
19import qualified Data.ByteString.Base64 as Base64
20import qualified Data.ByteString.Lazy as Lazy
21import qualified Data.ByteString.UTF8 as UTF8
23import Data.Maybe (isJust)
24import qualified Data.Text as T
28import Text.Trifecta hiding (Err)
29import Text.Trifecta.Delta
31getNChar :: Handle -> Int -> String -> IO (String)
32getNChar _ 0 s = return (reverse s)
33getNChar h n s = do c <- hGetChar h
34 getNChar h (n - 1) (c : s)
36getLen :: Handle -> IO (Either Err Int)
37getLen h = do s <- getNChar h 6 ""
39 ((num, ""):_) -> return $ Right num
40 _ -> return $ Left . Msg $ "Couldn't read length " ++ s
42data SExp = SexpList [SExp]
49sExpToString :: SExp -> String
50sExpToString (StringAtom s) = "\"" ++ escape s ++ "\""
51sExpToString (BoolAtom True) = ":True"
52sExpToString (BoolAtom False) = ":False"
53sExpToString (IntegerAtom i) = printf "%d" i
54sExpToString (SymbolAtom s) = ":" ++ s
55sExpToString (SexpList l) = "(" ++ intercalate " " (map sExpToString l) ++ ")"
60instance SExpable SExp where
63instance SExpable Bool where
64 toSExp True = BoolAtom True
65 toSExp False = BoolAtom False
67instance SExpable String where
68 toSExp s = StringAtom s
70instance SExpable Integer where
71 toSExp n = IntegerAtom n
73instance SExpable Int where
74 toSExp n = IntegerAtom (toInteger n)
77instance SExpable Name where
78 toSExp s = StringAtom (show s)
81instance (SExpable a) => SExpable (Maybe a) where
82 toSExp Nothing = SexpList [SymbolAtom "Nothing"]
83 toSExp (Just a) = SexpList [SymbolAtom "Just", toSExp a]
85instance (SExpable a) => SExpable [a] where
86 toSExp l = SexpList (map toSExp l)
88instance (SExpable a, SExpable b) => SExpable (a, b) where
89 toSExp (l, r) = SexpList [toSExp l, toSExp r]
91instance (SExpable a, SExpable b, SExpable c) => SExpable (a, b, c) where
92 toSExp (l, m, n) = SexpList [toSExp l, toSExp m, toSExp n]
94instance (SExpable a, SExpable b, SExpable c, SExpable d) => SExpable (a, b, c, d) where
95 toSExp (l, m, n, o) = SexpList [toSExp l, toSExp m, toSExp n, toSExp o]
97instance (SExpable a, SExpable b, SExpable c, SExpable d, SExpable e) =>
98 SExpable (a, b, c, d, e) where
99 toSExp (l, m, n, o, p) = SexpList [toSExp l, toSExp m, toSExp n, toSExp o, toSExp p]
101instance SExpable NameOutput where
102 toSExp TypeOutput = SymbolAtom "type"
103 toSExp FunOutput = SymbolAtom "function"
104 toSExp DataOutput = SymbolAtom "data"
105 toSExp MetavarOutput = SymbolAtom "metavar"
106 toSExp PostulateOutput = SymbolAtom "postulate"
108maybeProps :: SExpable a => [(String, Maybe a)] -> [(SExp, SExp)]
110maybeProps ((n, Just p):ps) = (SymbolAtom n, toSExp p) : maybeProps ps
111maybeProps ((n, Nothing):ps) = maybeProps ps
113constTy :: Const -> String
115constTy (BI _) = "Integer"
116constTy (Fl _) = "Double"
117constTy (Ch _) = "Char"
118constTy (Str _) = "String"
119constTy (B8 _) = "Bits8"
120constTy (B16 _) = "Bits16"
121constTy (B32 _) = "Bits32"
122constTy (B64 _) = "Bits64"
125namespaceOf :: Name -> Maybe String
126namespaceOf (NS _ ns) = Just (intercalate "." $ reverse (map T.unpack ns))
127namespaceOf _ = Nothing
129instance SExpable OutputAnnotation where
130 toSExp (AnnName n ty d t) = toSExp $ [(SymbolAtom "name", StringAtom (show n)),
131 (SymbolAtom "implicit", BoolAtom False),
132 (SymbolAtom "key", StringAtom (encodeName n))] ++
133 maybeProps [("decor", ty)] ++
134 maybeProps [("doc-overview", d), ("type", t)] ++
135 maybeProps [("namespace", namespaceOf n)]
136 toSExp (AnnBoundName n imp) = toSExp [(SymbolAtom "name", StringAtom (show n)),
137 (SymbolAtom "decor", SymbolAtom "bound"),
138 (SymbolAtom "implicit", BoolAtom imp)]
139 toSExp (AnnConst c) = toSExp [(SymbolAtom "decor",
140 SymbolAtom (if constIsType c then "type" else "data")),
141 (SymbolAtom "type", StringAtom (constTy c)),
142 (SymbolAtom "doc-overview", StringAtom (constDocs c)),
143 (SymbolAtom "name", StringAtom (show c))]
144 toSExp (AnnData ty doc) = toSExp [(SymbolAtom "decor", SymbolAtom "data"),
145 (SymbolAtom "type", StringAtom ty),
146 (SymbolAtom "doc-overview", StringAtom doc)]
147 toSExp (AnnType name doc) = toSExp $ [(SymbolAtom "decor", SymbolAtom "type"),
148 (SymbolAtom "type", StringAtom "Type"),
149 (SymbolAtom "doc-overview", StringAtom doc)] ++
150 if not (null name) then [(SymbolAtom "name", StringAtom name)] else []
151 toSExp AnnKeyword = toSExp [(SymbolAtom "decor", SymbolAtom "keyword")]
152 toSExp (AnnFC fc) = toSExp [(SymbolAtom "source-loc", toSExp fc)]
153 toSExp (AnnTextFmt fmt) = toSExp [(SymbolAtom "text-formatting", SymbolAtom format)]
154 where format = case fmt of
156 ItalicText -> "italic"
157 UnderlineText -> "underline"
158 toSExp (AnnLink url) = toSExp [(SymbolAtom "link-href", StringAtom url)]
159 toSExp (AnnTerm bnd tm)
160 | termSmallerThan 1000 tm = toSExp [(SymbolAtom "tt-term", StringAtom (encodeTerm bnd tm))]
161 | otherwise = SexpList []
162 toSExp (AnnSearchResult ordr) = toSExp [(SymbolAtom "doc-overview",
163 StringAtom ("Result type is " ++ descr))]
164 where descr = case ordr of
166 LT -> "more general than searched type"
167 GT -> "more specific than searched type"
168 toSExp (AnnErr e) = toSExp [(SymbolAtom "error", StringAtom (encodeErr e))]
169 toSExp (AnnNamespace ns file) =
170 toSExp $ [(SymbolAtom "namespace", StringAtom (intercalate "." (map T.unpack ns)))] ++
171 [(SymbolAtom "decor", SymbolAtom $ if isJust file then "module" else "namespace")] ++
172 maybeProps [("source-file", file)]
173 toSExp AnnQuasiquote = toSExp [(SymbolAtom "quasiquotation", True)]
174 toSExp AnnAntiquote = toSExp [(SymbolAtom "antiquotation", True)]
176encodeName :: Name -> String
177encodeName n = UTF8.toString . Base64.encode . Lazy.toStrict . Binary.encode $ n
179decodeName :: String -> Name
180decodeName = Binary.decode . Lazy.fromStrict . Base64.decodeLenient . UTF8.fromString
182encodeTerm :: [(Name, Bool)] -> Term -> String
183encodeTerm bnd tm = UTF8.toString . Base64.encode . Lazy.toStrict . Binary.encode $
186decodeTerm :: String -> ([(Name, Bool)], Term)
187decodeTerm = Binary.decode . Lazy.fromStrict . Base64.decodeLenient . UTF8.fromString
189encodeErr :: Err -> String
190encodeErr e = UTF8.toString . Base64.encode . Lazy.toStrict . Binary.encode $ e
192decodeErr :: String -> Err
193decodeErr = Binary.decode . Lazy.fromStrict . Base64.decodeLenient . UTF8.fromString
195instance SExpable FC where
196 toSExp (FC f (sl, sc) (el, ec)) =
197 toSExp ((SymbolAtom "filename", StringAtom f),
198 (SymbolAtom "start", IntegerAtom (toInteger sl), IntegerAtom (toInteger sc)),
199 (SymbolAtom "end", IntegerAtom (toInteger el), IntegerAtom (toInteger ec)))
200 toSExp NoFC = toSExp ([] :: [String])
201 toSExp (FileFC f) = toSExp [(SymbolAtom "filename", StringAtom f)]
203escape :: String -> String
204escape = concatMap escapeChar
206 escapeChar '\\' = "\\\\"
207 escapeChar '"' = "\\\""
210pSExp = do xs <- between (char '(') (char ')') (pSExp `sepBy` (char ' '))
214atom = do string "nil"; return (SexpList [])
215 <|> do char ':'; x <- atomC; return x
216 <|> do char '"'; xs <- many quotedChar; char '"'; return (StringAtom xs)
217 <|> do ints <- some digit
219 ((num, ""):_) -> return (IntegerAtom (toInteger num))
220 _ -> return (StringAtom ints)
222atomC = do string "True"; return (BoolAtom True)
223 <|> do string "False"; return (BoolAtom False)
224 <|> do xs <- many (noneOf " \n\t\r\"()"); return (SymbolAtom xs)
226quotedChar = try (string "\\\\" >> return '\\')
227 <|> try (string "\\\"" >> return '"')
230parseSExp :: String -> Result SExp
231parseSExp = parseString pSExp (Directed (UTF8.fromString "(unknown)") 0 0 0 0)
233data Opt = ShowImpl | ErrContext deriving Show
235data WhatDocs = Overview | Full
237data IdeModeCommand = REPLCompletions String
240 | CaseSplit Int String
241 | AddClause Int String
242 | AddProofClause Int String
243 | AddMissing Int String
244 | MakeWithBlock Int String
245 | MakeCaseBlock Int String
246 | ProofSearch Bool Int String [String] (Maybe Int) -- ^^ Recursive?, line, name, hints, depth
247 | MakeLemma Int String
248 | LoadFile String (Maybe Int)
249 | DocsFor String WhatDocs
253 | Metavariables Int -- ^^ the Int is the column count for pretty-printing
257 | TermNormalise [(Name, Bool)] Term
258 | TermShowImplicits [(Name, Bool)] Term
259 | TermNoImplicits [(Name, Bool)] Term
260 | TermElab [(Name, Bool)] Term
266sexpToCommand :: SExp -> Maybe IdeModeCommand
267sexpToCommand (SexpList ([x])) = sexpToCommand x
268sexpToCommand (SexpList [SymbolAtom "interpret", StringAtom cmd]) = Just (Interpret cmd)
269sexpToCommand (SexpList [SymbolAtom "repl-completions", StringAtom prefix]) = Just (REPLCompletions prefix)
270sexpToCommand (SexpList [SymbolAtom "load-file", StringAtom filename, IntegerAtom line]) = Just (LoadFile filename (Just (fromInteger line)))
271sexpToCommand (SexpList [SymbolAtom "load-file", StringAtom filename]) = Just (LoadFile filename Nothing)
272sexpToCommand (SexpList [SymbolAtom "type-of", StringAtom name]) = Just (TypeOf name)
273sexpToCommand (SexpList [SymbolAtom "case-split", IntegerAtom line, StringAtom name]) = Just (CaseSplit (fromInteger line) name)
274sexpToCommand (SexpList [SymbolAtom "add-clause", IntegerAtom line, StringAtom name]) = Just (AddClause (fromInteger line) name)
275sexpToCommand (SexpList [SymbolAtom "add-proof-clause", IntegerAtom line, StringAtom name]) = Just (AddProofClause (fromInteger line) name)
276sexpToCommand (SexpList [SymbolAtom "add-missing", IntegerAtom line, StringAtom name]) = Just (AddMissing (fromInteger line) name)
277sexpToCommand (SexpList [SymbolAtom "make-with", IntegerAtom line, StringAtom name]) = Just (MakeWithBlock (fromInteger line) name)
278sexpToCommand (SexpList [SymbolAtom "make-case", IntegerAtom line, StringAtom name]) = Just (MakeCaseBlock (fromInteger line) name)
279-- The Boolean in ProofSearch means "search recursively"
280-- If it's False, that means "refine", i.e. apply the name and fill in any
281-- arguments which can be done by unification.
282sexpToCommand (SexpList (SymbolAtom "proof-search" : IntegerAtom line : StringAtom name : rest))
283 | [] <- rest = Just (ProofSearch True (fromInteger line) name [] Nothing)
284 | [SexpList hintexp] <- rest
285 , Just hints <- getHints hintexp = Just (ProofSearch True (fromInteger line) name hints Nothing)
286 | [SexpList hintexp, IntegerAtom depth] <- rest
287 , Just hints <- getHints hintexp = Just (ProofSearch True (fromInteger line) name hints (Just (fromInteger depth)))
288 where getHints = mapM (\h -> case h of
289 StringAtom s -> Just s
291sexpToCommand (SexpList [SymbolAtom "make-lemma", IntegerAtom line, StringAtom name]) = Just (MakeLemma (fromInteger line) name)
292sexpToCommand (SexpList [SymbolAtom "refine", IntegerAtom line, StringAtom name, StringAtom hint]) = Just (ProofSearch False (fromInteger line) name [hint] Nothing)
293sexpToCommand (SexpList [SymbolAtom "docs-for", StringAtom name]) = Just (DocsFor name Full)
294sexpToCommand (SexpList [SymbolAtom "docs-for", StringAtom name, SymbolAtom s])
295 | Just w <- lookup s opts = Just (DocsFor name w)
296 where opts = [("overview", Overview), ("full", Full)]
297sexpToCommand (SexpList [SymbolAtom "apropos", StringAtom search]) = Just (Apropos search)
298sexpToCommand (SymbolAtom "get-options") = Just GetOpts
299sexpToCommand (SexpList [SymbolAtom "set-option", SymbolAtom s, BoolAtom b])
300 | Just opt <- lookup s opts = Just (SetOpt opt b)
301 where opts = [("show-implicits", ShowImpl), ("error-context", ErrContext)] --TODO support more options. Issue #1611 in the Issue tracker. https://github.com/idris-lang/Idris-dev/issues/1611
302sexpToCommand (SexpList [SymbolAtom "metavariables", IntegerAtom cols]) = Just (Metavariables (fromIntegral cols))
303sexpToCommand (SexpList [SymbolAtom "who-calls", StringAtom name]) = Just (WhoCalls name)
304sexpToCommand (SexpList [SymbolAtom "calls-who", StringAtom name]) = Just (CallsWho name)
305sexpToCommand (SexpList [SymbolAtom "browse-namespace", StringAtom ns]) = Just (BrowseNS ns)
306sexpToCommand (SexpList [SymbolAtom "normalise-term", StringAtom encoded]) = let (bnd, tm) = decodeTerm encoded in
307 Just (TermNormalise bnd tm)
308sexpToCommand (SexpList [SymbolAtom "show-term-implicits", StringAtom encoded]) = let (bnd, tm) = decodeTerm encoded in
309 Just (TermShowImplicits bnd tm)
310sexpToCommand (SexpList [SymbolAtom "hide-term-implicits", StringAtom encoded]) = let (bnd, tm) = decodeTerm encoded in
311 Just (TermNoImplicits bnd tm)
312sexpToCommand (SexpList [SymbolAtom "elaborate-term", StringAtom encoded]) = let (bnd, tm) = decodeTerm encoded in
313 Just (TermElab bnd tm)
314sexpToCommand (SexpList [SymbolAtom "print-definition", StringAtom name]) = Just (PrintDef name)
315sexpToCommand (SexpList [SymbolAtom "error-string", StringAtom encoded]) = Just . ErrString . decodeErr $ encoded
316sexpToCommand (SexpList [SymbolAtom "error-pprint", StringAtom encoded]) = Just . ErrPPrint . decodeErr $ encoded
317sexpToCommand (SymbolAtom "version") = Just GetIdrisVersion
318sexpToCommand _ = Nothing
320parseMessage :: String -> Either Err (SExp, Integer)
321parseMessage x = case receiveString x of
322 Right (SexpList [cmd, (IntegerAtom id)]) -> Right (cmd, id)
323 Right x -> Left . Msg $ "Invalid message " ++ show x
326receiveString :: String -> Either Err SExp
329 Failure _ -> Left . Msg $ "parse failure"
332convSExp :: SExpable a => String -> a -> Integer -> String
334 let sex = SexpList [SymbolAtom pre, toSExp s, IntegerAtom id] in
335 let str = sExpToString sex in
336 (getHexLength str) ++ str
338getHexLength :: String -> String
339getHexLength s = printf "%06x" (1 + (length s))
341-- | The version of the IDE mode command set. Increment this when you
342-- change it so clients can adapt.
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