on master help sign in
Improve auto-completion a bit more

It turns out it is really complicated to do reasonable things with the `complete` command. For example, it is extremely difficult to suggest files yourself because it will always add a space after directories. If you disable the adding of spaces, now it does not add spaces after files. This change makes it possible to get autocompletion set up by putting the following instruction in your .bash_profile or whatever: complete -o default -C elm elm Elm just knows how to complete itself! So there is no script that has to go with this. I also stopped suggesting files, because if you use the -o default option then it will still suggest files for you. It’s not exactly what I wanted, but it should work well in any setting where COMP_LINE and COMP_POINT are set by `complete`

commit2ec15df276faedc0b4a2d77a46cff64e7610841d authorEvan Czaplicki <evancz@users.noreply.github.com> dateMon 9 Apr 2018, 21:59 (9 years ago) parent07c7666
5 files changed+85−47
builder/src/Generate/Output.hs+2−2 ui/terminal/src/Make.hs+2−2 ui/terminal/src/Terminal/Args.hs+78−40 ui/terminal/src/Terminal/Args/Chomp.hs+1−1 ui/terminal/src/Terminal/Args/Helpers.hs+2−2
builder/src/Generate/Output.hs+2−2
@@ -38,7 +38,7 @@ import qualified Reporting.Exit as Exit
3838import qualified Reporting.Exit.Make as E
3939import qualified Reporting.Task as Task
4040import qualified Stuff.Paths as Paths
41−import Terminal.Args (Parser(..), suggestFiles)
41+import Terminal.Args (Parser(..))
4242
4343
4444
@@ -206,7 +206,7 @@ output =
206206 { _singular = "output file"
207207 , _plural = "output files"
208208 , _parser = parseOutput
209− , _suggest = suggestFiles ["js","html"]
209+ , _suggest = \_ -> return []
210210 , _examples = exampleOutput
211211 }
212212
ui/terminal/src/Make.hs+2−2
@@ -20,7 +20,7 @@ import qualified Reporting.Task as Task
2020import qualified Reporting.Progress as Progress
2121import qualified Reporting.Progress.Json as Json
2222import qualified Reporting.Progress.Terminal as Terminal
23−import Terminal.Args (Parser(..), suggestFiles)
23+import Terminal.Args (Parser(..))
2424
2525
2626
@@ -92,6 +92,6 @@ docsFile =
9292 { _singular = "json file"
9393 , _plural = "json files"
9494 , _parser = \string -> if FP.takeExtension string == ".json" then Just string else Nothing
95− , _suggest = suggestFiles ["json"]
95+ , _suggest = \_ -> return []
9696 , _examples = \_ -> return ["docs.json","documentation.json"]
9797 }
ui/terminal/src/Terminal/Args.hs+78−40
@@ -5,7 +5,7 @@ module Terminal.Args
55 , complex
66 , Flags, noFlags, flags, (|--)
77 , Flag, flag, onOff
8− , Parser(..), suggestFiles
8+ , Parser(..)
99 , Args, noArgs, required, optional, zeroOrMore, oneOrMore, oneOf
1010 , require0, require1, require2, require3, require4, require5
1111 , RequiredArgs, args, exactly, (!), (?), (...)
@@ -13,7 +13,7 @@ module Terminal.Args
1313 where
1414
1515
16−import Control.Monad (filterM)
16+import Control.Monad (filterM, when)
1717import qualified Data.Char as Char
1818import qualified Data.List as List
1919import qualified Data.Maybe as Maybe
@@ -22,8 +22,9 @@ import qualified System.Environment as Env
2222import qualified System.Exit as Exit
2323import qualified System.FilePath as FP
2424import System.FilePath ((</>))
25−import System.IO (hPutStrLn, stdout)
25+import System.IO (hPutStr, hPutStrLn, stdout)
2626import qualified Text.PrettyPrint.ANSI.Leijen as P
27+import qualified Text.Read as Read
2728
2829import qualified Elm.Compiler as Compiler
2930import qualified Elm.Package as Pkg
@@ -40,10 +41,6 @@ simple :: String -> P.Doc -> Args args -> Flags flags -> (args -> flags -> IO ()
4041simple details example args_ flags_ callback =
4142 do argStrings <- Env.getArgs
4243 case argStrings of
43− "autocomplete" : number : chunks ->
44− attemptAutoComplete number $ \index ->
45− fst $ Chomp.chomp (Just index) chunks args_ flags_
46−
4744 ["--version"] ->
4845 do hPutStrLn stdout (Pkg.versionToString Compiler.version)
4946 Exit.exitSuccess
@@ -53,12 +50,15 @@ simple details example args_ flags_ callback =
5350 Error.exitWithHelp Nothing details example args_ flags_
5451
5552 else
56− case snd $ Chomp.chomp Nothing chunks args_ flags_ of
57− Right (argsValue, flagValue) ->
58− callback argsValue flagValue
53+ do maybeAutoComplete chunks $ \index cs ->
54+ fst $ Chomp.chomp (Just index) cs args_ flags_
55+
56+ case snd $ Chomp.chomp Nothing chunks args_ flags_ of
57+ Right (argsValue, flagValue) ->
58+ callback argsValue flagValue
5959
60− Left err ->
61− Error.exitWithError err
60+ Left err ->
61+ Error.exitWithError err
6262
6363
6464complex :: P.Doc -> P.Doc -> [Interface] -> IO ()
@@ -75,44 +75,79 @@ complex intro outro interfaces =
7575 do hPutStrLn stdout (Pkg.versionToString Compiler.version)
7676 Exit.exitSuccess
7777
78− "autocomplete" : number : chunks ->
79− attemptAutoComplete number $ \index ->
80− complexSuggest index chunks interfaces
81−
8278 command : chunks ->
83− case List.find (\iface -> toName iface == command) interfaces of
84− Nothing ->
85− Error.exitWithUnknown command (map toName interfaces)
79+ do maybeAutoComplete argStrings (complexSuggest interfaces)
80+ case List.find (\iface -> toName iface == command) interfaces of
81+ Nothing ->
82+ Error.exitWithUnknown command (map toName interfaces)
8683
87− Just (Interface _ _ details example args_ flags_ callback) ->
88− if elem "--help" chunks then
89− Error.exitWithHelp (Just command) details example args_ flags_
84+ Just (Interface _ _ details example args_ flags_ callback) ->
85+ if elem "--help" chunks then
86+ Error.exitWithHelp (Just command) details example args_ flags_
9087
91− else
92− case snd $ Chomp.chomp Nothing chunks args_ flags_ of
93− Right (argsValue, flagValue) ->
94− callback argsValue flagValue
88+ else
89+ case snd $ Chomp.chomp Nothing chunks args_ flags_ of
90+ Right (argsValue, flagValue) ->
91+ callback argsValue flagValue
9592
96− Left err ->
97− Error.exitWithError err
93+ Left err ->
94+ Error.exitWithError err
9895
9996
10097
10198-- AUTO-COMPLETE
10299
103100
104−attemptAutoComplete :: String -> (Int -> IO [String]) -> IO a
105−attemptAutoComplete number getSuggestions =
106− if all Char.isDigit number then
107− do suggestions <- getSuggestions (read number)
108− hPutStrLn stdout (List.intercalate " " suggestions)
109− Exit.exitSuccess
101+maybeAutoComplete :: [String] -> (Int -> [String] -> IO [String]) -> IO ()
102+maybeAutoComplete args getSuggestions =
103+ if length args /= 3 then
104+ return ()
110105 else
111− Exit.exitFailure
106+ do maybeLine <- Env.lookupEnv "COMP_LINE"
107+ case maybeLine of
108+ Nothing ->
109+ return ()
110+
111+ Just line ->
112+ do (index, chunks) <- getCompIndex line
113+ suggestions <- getSuggestions index chunks
114+ hPutStr stdout (unlines suggestions)
115+ Exit.exitFailure
116+
117+
118+getCompIndex :: String -> IO (Int, [String])
119+getCompIndex line =
120+ do maybePoint <- Env.lookupEnv "COMP_POINT"
121+ case Read.readMaybe =<< maybePoint :: Maybe Int of
122+ Nothing ->
123+ do let chunks = words line
124+ return (length chunks, chunks)
125+
126+ Just point ->
127+ let
128+ isPoint (_,i) = i == point
129+ groups = List.groupBy grouper (zip line [0..])
130+ rawChunks = drop 1 (filter (all (not . isSpace . fst)) groups)
131+ maybeIndex = List.findIndex (any isPoint) rawChunks
132+ in
133+ return
134+ ( maybe (length rawChunks) id maybeIndex
135+ , map (map fst) rawChunks
136+ )
112137
113138
114−complexSuggest :: Int -> [String] -> [Interface] -> IO [String]
115−complexSuggest index strings interfaces =
139+grouper :: (Char, Int) -> (Char, Int) -> Bool
140+grouper (c1, _) (c2, _) =
141+ isSpace c1 == isSpace c2
142+
143+
144+isSpace :: Char -> Bool
145+isSpace char =
146+ char == ' ' || char == '\t' || char == '\n'
147+
148+
149+complexSuggest :: [Interface] -> Int -> [String] -> IO [String]
150+complexSuggest interfaces index strings =
116151 case strings of
117152 [] ->
118153 return (map toName interfaces)
@@ -120,7 +155,10 @@ complexSuggest index strings interfaces =
120155 command : chunks ->
121156 case List.find (\iface -> toName iface == command) interfaces of
122157 Nothing ->
123− return (map toName interfaces)
158+ if index == 1 then
159+ return (filter (List.isPrefixOf command) (map toName interfaces))
160+ else
161+ return []
124162
125163 Just (Interface _ _ _ _ args_ flags_ _) ->
126164 fst $ Chomp.chomp (Just (index-1)) chunks args_ flags_
@@ -289,8 +327,8 @@ Notice that you can limit the suggestion by the file extension! If you need
289327something more elaborate, you can implement a function like this yourself that
290328does whatever you need!
291329-}
292−suggestFiles :: [String] -> String -> IO [String]
293−suggestFiles extensions string =
330+_suggestFiles :: [String] -> String -> IO [String]
331+_suggestFiles extensions string =
294332 let
295333 (dir, start) =
296334 FP.splitFileName string
ui/terminal/src/Terminal/Args/Chomp.hs+1−1
@@ -464,7 +464,7 @@ getFlagName :: Flag a -> String
464464getFlagName flag =
465465 case flag of
466466 Flag name _ _ ->
467− "--" ++ name ++ "="
467+ "--" ++ name
468468
469469 OnOff name _ ->
470470 "--" ++ name
ui/terminal/src/Terminal/Args/Helpers.hs+2−2
@@ -11,7 +11,7 @@ import qualified Data.Char as Char
1111import qualified Data.Text as Text
1212import qualified System.FilePath as FP
1313
14−import Terminal.Args (Parser(..), suggestFiles)
14+import Terminal.Args (Parser(..))
1515import qualified Elm.Package as Pkg
1616
1717
@@ -67,7 +67,7 @@ elmFile =
6767 { _singular = "elm file"
6868 , _plural = "elm files"
6969 , _parser = parseElmFile
70− , _suggest = suggestFiles ["elm"]
70+ , _suggest = \_ -> return []
7171 , _examples = exampleElmFiles
7272 }
7373