complete word completion in the shell. works for commands, flags, options, abstract syntax identifiers and NL strings

This commit is contained in:
kr.angelov
2008-06-05 11:29:08 +00:00
parent f5fd3aa603
commit 11f24097b4
5 changed files with 120 additions and 22 deletions

View File

@@ -26,3 +26,23 @@ data Argument
= AExp Exp
| ANoArg
deriving (Eq,Ord,Show)
valIdOpts :: String -> String -> [Option] -> String
valIdOpts flag def opts = case valOpts flag (VId def) opts of
VId v -> v
_ -> def
valIntOpts :: String -> Integer -> [Option] -> Int
valIntOpts flag def opts = fromInteger $ case valOpts flag (VInt def) opts of
VInt v -> v
_ -> def
valOpts :: String -> Value -> [Option] -> Value
valOpts flag def opts = case lookup flag flags of
Just v -> v
_ -> def
where
flags = [(f,v) | OFlag f v <- opts]
isOpt :: String -> [Option] -> Bool
isOpt o opts = elem o [x | OOpt x <- opts]

View File

@@ -64,26 +64,6 @@ commandHelp full (co,info) = unlines $ [
"flags: " ++ unwords (flags info)
] else []
valIdOpts :: String -> String -> [Option] -> String
valIdOpts flag def opts = case valOpts flag (VId def) opts of
VId v -> v
_ -> def
valIntOpts :: String -> Integer -> [Option] -> Int
valIntOpts flag def opts = fromInteger $ case valOpts flag (VInt def) opts of
VInt v -> v
_ -> def
valOpts :: String -> Value -> [Option] -> Value
valOpts flag def opts = case lookup flag flags of
Just v -> v
_ -> def
where
flags = [(f,v) | OFlag f v <- opts]
isOpt :: String -> [Option] -> Bool
isOpt o opts = elem o [x | OOpt x <- opts]
-- this list must be kept sorted by the command name!
allCommands :: PGF -> Map.Map String CommandInfo
allCommands pgf = Map.fromAscList [

View File

@@ -1,4 +1,4 @@
module GF.Command.Parse(readCommandLine) where
module GF.Command.Parse(readCommandLine, pCommand) where
import PGF.ExprSyntax
import GF.Command.Abstract

View File

@@ -18,6 +18,8 @@ import System.Console.Readline
fetchCommand :: String -> IO (String)
fetchCommand s = do
setCompletionAppendCharacter Nothing
setBasicQuoteCharacters ""
res <- readline s
case res of
Nothing -> return "q"

View File

@@ -3,15 +3,24 @@ module GFI (mainGFI) where
import GF.Command.Interpreter
import GF.Command.Importing
import GF.Command.Commands
import GF.Command.Abstract
import GF.Command.Parse
import GF.Data.ErrM
import GF.Grammar.API -- for cc command
import GF.Infra.UseIO
import GF.Infra.Option
import GF.System.Readline (fetchCommand)
import GF.System.Readline
import PGF
import PGF.Data
import PGF.Macros
import Data.Char
import Data.List(isPrefixOf)
import qualified Data.Map as Map
import qualified Text.ParserCombinators.ReadP as RP
import System.CPUTime
import Control.Exception
import Data.Version
import Paths_gf
@@ -28,6 +37,7 @@ loop :: Options -> GFEnv -> IO GFEnv
loop opts gfenv0 = do
let env = commandenv gfenv0
let sgr = sourcegrammar gfenv0
setCompletionFunction (Just (wordCompletion (commandenv gfenv0)))
s <- fetchCommand (prompt env)
let gfenv = gfenv0 {history = s : history gfenv0}
let loopNewCPU gfenv' = do cpu' <- getCPUTime
@@ -102,3 +112,89 @@ data GFEnv = GFEnv {
emptyGFEnv :: GFEnv
emptyGFEnv = GFEnv emptyGrammar (mkCommandEnv emptyPGF) [] 0
wordCompletion cmdEnv line prefix p = do
case wc_type (take p line) of
CmplCmd pref
-> ret ' ' [name | name <- Map.keys (commands cmdEnv), isPrefixOf pref name]
CmplStr (Just (Command _ opts _)) s
-> do mb_state0 <- try (evaluate (initState pgf (optLang opts) (optCat opts)))
case mb_state0 of
Right state0 -> let ws = words (take (length s - length prefix) s)
state = foldl nextState state0 ws
compls = getCompletions state prefix
in ret ' ' (Map.keys compls)
Left _ -> ret ' ' []
CmplOpt (Just (Command n _ _)) pref
-> case Map.lookup n (commands cmdEnv) of
Just inf -> do let flg_compls = ['-':flg | flg <- flags inf, isPrefixOf pref flg]
opt_compls = ['-':opt | opt <- options inf, isPrefixOf pref opt]
ret (if null flg_compls then ' ' else '=')
(flg_compls++opt_compls)
Nothing -> ret ' ' []
CmplIdent _ pref
-> ret ' ' [name | cid <- Map.keys (funs (abstract pgf)), let name = prCId cid, isPrefixOf pref name]
_ -> ret ' ' []
where
pgf = multigrammar cmdEnv
optLang opts = valIdOpts "lang" (head (languages pgf)) opts
optCat opts = valIdOpts "cat" (lookStartCat pgf) opts
ret c [x] = return [x++[c]]
ret _ xs = return xs
data CompletionType
= CmplCmd Ident
| CmplStr (Maybe Command) String
| CmplOpt (Maybe Command) Ident
| CmplIdent (Maybe Command) Ident
deriving Show
wc_type :: String -> CompletionType
wc_type = cmd_name
where
cmd_name cs =
let cs1 = dropWhile isSpace cs
in go cs1 cs1
where
go x [] = CmplCmd x
go x (c:cs)
| isIdent c = go x cs
| otherwise = cmd x cs
cmd x [] = ret CmplIdent x "" 0
cmd _ ('|':cs) = cmd_name cs
cmd _ (';':cs) = cmd_name cs
cmd x ('"':cs) = str x cs cs
cmd x ('-':cs) = option x cs cs
cmd x (c :cs)
| isIdent c = ident x (c:cs) cs
| otherwise = cmd x cs
option x y [] = ret CmplOpt x y 1
option x y (c:cs)
| isIdent c = option x y cs
| otherwise = cmd x cs
ident x y [] = ret CmplIdent x y 0
ident x y (c:cs)
| isIdent c = ident x y cs
| otherwise = cmd x cs
str x y [] = ret CmplStr x y 1
str x y ('\"':cs) = cmd x cs
str x y ('\\':c:cs) = str x y cs
str x y (c:cs) = str x y cs
ret f x y d = f cmd y
where
x1 = take (length x - length y - d) x
x2 = takeWhile (\c -> isIdent c || isSpace c || c == '-' || c == '=') x1
cmd = case [x | (x,cs) <- RP.readP_to_S pCommand x2, all isSpace cs] of
[x] -> Just x
_ -> Nothing
isIdent c = c == '_' || c == '\'' || isAlphaNum c