Node 实现 REPL 自动补全功能

之前没看过 Node 的 repl 模块, 尝试了一下
http://nodejs.org/api/repl.html
repl 模块主要是封装了一个执行 Node 的 context 从命令行传入数据访问
我看中的是自动补全的功能, 好比 CoffeeScript 命令行工具借此实现了 Tab 补全html

按文档的代码例子跑了之后, 发现补全功能其实只是 Node 默认的 JS 变量补全
若是是要自定义补全的内容, 就不能经过这个 API
网上给出的访方案是经过重写 .complete 方法来达到自定义的目的
http://stackoverflow.com/a/7096913/883571node

因而我写了一个例子, 测试在每次 Tab 时是否能本身打印环境当中的数据git

repl = require 'repl'
util = require 'util'

shell = repl.start('cirru> ')

y = 0

shell.complete = (x, callback) ->
  y += 1
  console.log y
  util.print 'cirru> '
  callback([])

shell.context.shell = shell

网上再找了之后, 我意识到 readline 模块能够直接完成这样的事情
因而有了下面的例子, 主要是 completer 方法的写法:github

util = require 'util'
readline = require 'readline'

candidates = ['acd', 'abc', 'abd']

completer = (line) ->
  matchLastWord = line.match(/[\w-:\/]+$/)
  return [candidates, ''] unless matchLastWord?
  lastWord = matchLastWord[0]
  wordsBefore = line[...(-lastWord.length)]
  hits = candidates
  .filter (word) ->
    (word.indexOf lastWord) is 0
  if hits.length > 0
    [hits, lastWord]
  else
    [[], null]

shell = readline.createInterface
  input: process.stdin
  output: process.stdout
  completer: completer

do repl = ->
  shell.question 'cirru> ', (anwser) ->
    repl()

完整的代码我用在了 Cirru-Shell 的实现当中:
https://github.com/Cirru/cirru-shell/blob/master/coffee/shell.coffeeshell

completer 的返回值的格式是 [[substr1, substr2, ...], originalsubstring]
举一个例子, 命令行里内容是 c a b d, 光标前面的内容是 c a
好比 completer 的返回内容是 [['abc', 'acd'], a], 命令行里是
那么就会根据 a, 在按 <tab> 时提示这两个选项.
若是光标前面是 c ab, 那么返回的内容对应 [[abc], 'ab'],
那么这时候 Node 就会对原来的内容进行替换, 变成 c abc
而光标之后的内容保持不变.api

至于其中查找匹配的选项的逻辑, 彻底能够本身去定制,
能够参考我写在 Cirru-Shell 里的例子less


看了一下 readline 模块的源码, 中间有一部分红能够看懂的
https://github.com/joyent/node/blob/master/lib/readline.js
好比 self.cursor self._moveCursor self.line 能猜到意思
以此是, 光标位置, 光标移动距离, 当前行的内容
另外 self.question 方法的劣迹也清晰多了, 仅仅是 self.emit 'line'工具

因而稍微 hack 了一些, 在 <tab> 时我实现了对括号自动补全
补全括号的光标移动顺序我目前没法改动 completer, 经过事件解决:测试

completer = (line) ->
  if line[line.length - 1] is '('
    # console.log shell.line, shell.cursor
    if shell.line[shell.cursor] isnt ')'
      setTimeout ->
        shell._moveCursor -1
      , 0
      return [['()'], '(']
    else
      return [[], null]

这是我看到的包含了私有的 API 的一份列表:
https://gist.github.com/viatropos/3843973
参考这里, 相似还有 history 之类的接口, 对照源码对历史进行存储ui

相关文章
相关标签/搜索