Skip to content

Commit

Permalink
Implement ,complete repl command.
Browse files Browse the repository at this point in the history
This will make it much easier to add completion from external embedded repls.
  • Loading branch information
technomancy committed Jun 6, 2021
1 parent 9caa3de commit 7f21cc9
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 38 deletions.
2 changes: 1 addition & 1 deletion api.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ used to print normal values, and one which is used to print errors.

```fennel
(local fennel (require :fennel)
(fn locals [env _read on-values on-error]
(fn locals [env read on-values on-error scope]
"Print all locals in repl session scope."
(on-values [(fennel.view env.___replLocals___)]))
Expand Down
82 changes: 45 additions & 37 deletions src/fennel/repl.fnl
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,40 @@
(table.insert spliced-source (length spliced-source) save-source))
(table.concat spliced-source "\n")))

(fn completer [env scope text]
(let [matches []
input-fragment (text:gsub ".*[%s)(]+" "")]
(var stop-looking? false)

(fn add-partials [input tbl prefix] ; add partial key matches in tbl
(each [k (utils.allpairs tbl)]
(let [k (if (or (= tbl env) (= tbl env.___replLocals___))
(. scope.unmanglings k)
k)]
(when (and (< (length matches) 2000)
; stop explosion on too many items
(= (type k) :string) (= input (k:sub 0 (length input))))
(table.insert matches (.. prefix k))))))

(fn add-matches [input tbl prefix] ; add matches, descending into tbl fields
(let [prefix (if prefix (.. prefix ".") "")]
(if (not (input:find "%.")) ; no more dots, so add matches
(add-partials input tbl prefix)
(let [(head tail) (input:match "^([^.]+)%.(.*)")
raw-head (if (or (= tbl env) (= tbl env.___replLocals___))
(. scope.manglings head)
head)]
(when (= (type (. tbl raw-head)) :table)
(set stop-looking? true)
(add-matches tail (. tbl raw-head) (.. prefix head)))))))

(each [_ source (ipairs [scope.specials scope.macros
(or env.___replLocals___ []) env env._G])]
(add-matches input-fragment source)
;; bootstrap compiler doesn't yet know how to :until
(when stop-looking? (lua :break)))
matches))

(local commands {})

(fn command? [input]
Expand Down Expand Up @@ -121,6 +155,14 @@ For more information about the language, see https://fennel-lang.org/reference")
(compiler.metadata:set commands.reset :fnl/docstring
"Erase all repl-local scope.")

(fn commands.complete [env read on-values on-error scope]
(match (read)
(true input) (on-values (completer env scope (tostring input)))
(_ ?msg) (on-error :Parse (or ?msg "Couldn't parse completion input."))))

(compiler.metadata:set commands.complete :fnl/docstring
"Print all possible completions for a given input.")

(fn load-plugin-commands []
(when (and utils.root utils.root.options utils.root.options.plugins)
(each [_ plugin (ipairs utils.root.options.plugins)]
Expand All @@ -129,50 +171,16 @@ For more information about the language, see https://fennel-lang.org/reference")
(match (name:match "^repl%-command%-(.*)")
cmd-name (tset commands cmd-name (or (. commands cmd-name) f)))))))

(fn run-command [input read loop env on-values on-error]
(fn run-command [input read loop env on-values on-error scope]
(load-plugin-commands)
(let [command-name (input:match ",([^%s/]+)")]
(match (. commands command-name)
command (command env read on-values on-error)
command (command env read on-values on-error scope)
_ (when (not= :exit command-name)
(on-values ["Unknown command" command-name])))
(when (not= :exit command-name)
(loop))))

(fn completer [env scope text]
(let [matches []
input-fragment (text:gsub ".*[%s)(]+" "")]
(var stop-looking? false)

(fn add-partials [input tbl prefix] ; add partial key matches in tbl
(each [k (utils.allpairs tbl)]
(let [k (if (or (= tbl env) (= tbl env.___replLocals___))
(. scope.unmanglings k)
k)]
(when (and (< (length matches) 2000)
; stop explosion on too many items
(= (type k) :string) (= input (k:sub 0 (length input))))
(table.insert matches (.. prefix k))))))

(fn add-matches [input tbl prefix] ; add matches, descending into tbl fields
(let [prefix (if prefix (.. prefix ".") "")]
(if (not (input:find "%.")) ; no more dots, so add matches
(add-partials input tbl prefix)
(let [(head tail) (input:match "^([^.]+)%.(.*)")
raw-head (if (or (= tbl env) (= tbl env.___replLocals___))
(. scope.manglings head)
head)]
(when (= (type (. tbl raw-head)) :table)
(set stop-looking? true)
(add-matches tail (. tbl raw-head) (.. prefix head)))))))

(each [_ source (ipairs [scope.specials scope.macros
(or env.___replLocals___ []) env])]
(add-matches input-fragment source)
;; bootstrap compiler doesn't yet know how to :until
(when stop-looking? (lua :break)))
matches))

(fn repl [options]
(let [old-root-options utils.root.options
env (if options.env
Expand Down Expand Up @@ -223,7 +231,7 @@ For more information about the language, see https://fennel-lang.org/reference")
(reset)
(loop))
(command? src-string)
(run-command src-string read loop env on-values on-error)
(run-command src-string read loop env on-values on-error scope)
(when parse-ok? ; if this is false, we got eof
(match (pcall compiler.compile x
{:correlate opts.correlate
Expand Down

0 comments on commit 7f21cc9

Please sign in to comment.