mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
First implementation of autocomplete.
This commit is contained in:
parent
3f586ff4ab
commit
9a63b3147f
4 changed files with 90 additions and 79 deletions
58
lib/repl.js
58
lib/repl.js
|
@ -1,18 +1,14 @@
|
|||
(function() {
|
||||
var CoffeeScript, autocomplete, error, helpers, readline, repl, run, stdio;
|
||||
var ACCESSOR, CoffeeScript, SIMPLEVAR, Script, autocomplete, completeAttribute, completeVariable, error, getCompletions, getPropertyNames, helpers, readline, repl, run, stdio;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
CoffeeScript = require('./coffee-script');
|
||||
helpers = require('./helpers');
|
||||
autocomplete = require('./autocomplete');
|
||||
readline = require('readline');
|
||||
Script = process.binding('evals').Script;
|
||||
stdio = process.openStdin();
|
||||
error = function(err) {
|
||||
return stdio.write((err.stack || err.toString()) + '\n\n');
|
||||
};
|
||||
helpers.extend(global, {
|
||||
quit: function() {
|
||||
return process.exit(0);
|
||||
}
|
||||
});
|
||||
run = function(buffer) {
|
||||
var val;
|
||||
try {
|
||||
|
@ -29,8 +25,54 @@
|
|||
}
|
||||
return repl.prompt();
|
||||
};
|
||||
ACCESSOR = /\s*([\w\.]+)(?:\.(\w*))$/;
|
||||
SIMPLEVAR = /\s*(\w*)$/i;
|
||||
autocomplete = function(text) {
|
||||
return completeAttribute(text) || completeVariable(text) || [[], text];
|
||||
};
|
||||
completeAttribute = function(text) {
|
||||
var all, completions, match, obj, prefix, val;
|
||||
if (match = text.match(ACCESSOR)) {
|
||||
all = match[0], obj = match[1], prefix = match[2];
|
||||
try {
|
||||
val = Script.runInThisContext(obj);
|
||||
} catch (error) {
|
||||
return [[], text];
|
||||
}
|
||||
completions = getCompletions(prefix, getPropertyNames(val));
|
||||
return [completions, prefix];
|
||||
}
|
||||
};
|
||||
completeVariable = function(text) {
|
||||
var completions, free, scope, _ref;
|
||||
if (free = (_ref = text.match(SIMPLEVAR)) != null ? _ref[1] : void 0) {
|
||||
scope = Script.runInThisContext('this');
|
||||
completions = getCompletions(free, CoffeeScript.RESERVED.concat(getPropertyNames(scope)));
|
||||
return [completions, free];
|
||||
}
|
||||
};
|
||||
getCompletions = function(prefix, candidates) {
|
||||
var el, _i, _len, _results;
|
||||
_results = [];
|
||||
for (_i = 0, _len = candidates.length; _i < _len; _i++) {
|
||||
el = candidates[_i];
|
||||
if (el.indexOf(prefix) === 0) {
|
||||
_results.push(el);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
getPropertyNames = function(obj) {
|
||||
var name, _results;
|
||||
_results = [];
|
||||
for (name in obj) {
|
||||
if (!__hasProp.call(obj, name)) continue;
|
||||
_results.push(name);
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
process.on('uncaughtException', error);
|
||||
repl = readline.createInterface(stdio, autocomplete.complete);
|
||||
repl = readline.createInterface(stdio, autocomplete);
|
||||
repl.setPrompt('coffee> ');
|
||||
stdio.on('data', function(buffer) {
|
||||
return repl.write(buffer);
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
|
||||
{RESERVED} = require './coffee-script'
|
||||
Script = process.binding('evals').Script
|
||||
|
||||
# Return elements of candidates for which `prefix` is a prefix.
|
||||
get_completions = (prefix, candidates) ->
|
||||
(el for el in candidates when el.indexOf(prefix) == 0)
|
||||
|
||||
get_property_names = (o) ->
|
||||
try
|
||||
Object.getOwnPropertyNames(o)
|
||||
catch error
|
||||
(k for k of o)
|
||||
|
||||
complete_attribute = (text) ->
|
||||
match = /\s*([\w\.]+)(?:\.(\w*))$/.exec(text)
|
||||
if match?
|
||||
[ob, prefix] = [match[1], match[2]]
|
||||
try
|
||||
val = Script.runInThisContext ob
|
||||
catch error
|
||||
return [[], text]
|
||||
completions = get_completions prefix, get_property_names val
|
||||
[completions, prefix]
|
||||
|
||||
complete_variable = (text) ->
|
||||
free = /\W*(\w*)$/i.exec(text)?[1]
|
||||
if free?
|
||||
completions = get_completions free, RESERVED.concat(get_property_names Script.runInThisContext 'this')
|
||||
[completions, free]
|
||||
|
||||
# Returns a list of completions and the completed text
|
||||
exports.complete = (text) ->
|
||||
complete_attribute(text) or complete_variable(text) or [[], text]
|
|
@ -7,8 +7,10 @@
|
|||
# Require the **coffee-script** module to get access to the compiler.
|
||||
CoffeeScript = require './coffee-script'
|
||||
helpers = require './helpers'
|
||||
autocomplete = require './autocomplete'
|
||||
readline = require 'readline'
|
||||
Script = process.binding('evals').Script
|
||||
|
||||
# REPL Setup
|
||||
|
||||
# Start by opening up **stdio**.
|
||||
stdio = process.openStdin()
|
||||
|
@ -17,9 +19,6 @@ stdio = process.openStdin()
|
|||
error = (err) ->
|
||||
stdio.write (err.stack or err.toString()) + '\n\n'
|
||||
|
||||
# Quick alias for quitting the REPL.
|
||||
helpers.extend global, quit: -> process.exit(0)
|
||||
|
||||
# The main REPL function. **run** is called every time a line of code is entered.
|
||||
# Attempt to evaluate the command. If there's an exception, print it out instead
|
||||
# of exiting.
|
||||
|
@ -31,11 +30,47 @@ run = (buffer) ->
|
|||
error err
|
||||
repl.prompt()
|
||||
|
||||
## Autocompletion
|
||||
|
||||
# Regexes to match complete-able bits of text.
|
||||
ACCESSOR = /\s*([\w\.]+)(?:\.(\w*))$/
|
||||
SIMPLEVAR = /\s*(\w*)$/i
|
||||
|
||||
# Returns a list of completions, and the completed text.
|
||||
autocomplete = (text) ->
|
||||
completeAttribute(text) or completeVariable(text) or [[], text]
|
||||
|
||||
# Attempt to autocomplete a chained dotted attribute: `one.two.three`.
|
||||
completeAttribute = (text) ->
|
||||
if match = text.match ACCESSOR
|
||||
[all, obj, prefix] = match
|
||||
try
|
||||
val = Script.runInThisContext obj
|
||||
catch error
|
||||
return [[], text]
|
||||
completions = getCompletions prefix, getPropertyNames val
|
||||
[completions, prefix]
|
||||
|
||||
# Attempt to autocomplete an in-scope free variable: `one`.
|
||||
completeVariable = (text) ->
|
||||
if free = text.match(SIMPLEVAR)?[1]
|
||||
scope = Script.runInThisContext 'this'
|
||||
completions = getCompletions free, CoffeeScript.RESERVED.concat(getPropertyNames scope)
|
||||
[completions, free]
|
||||
|
||||
# Return elements of candidates for which `prefix` is a prefix.
|
||||
getCompletions = (prefix, candidates) ->
|
||||
(el for el in candidates when el.indexOf(prefix) is 0)
|
||||
|
||||
# Return all "own" properties of an object.
|
||||
getPropertyNames = (obj) ->
|
||||
(name for own name of obj)
|
||||
|
||||
# Make sure that uncaught exceptions don't kill the REPL.
|
||||
process.on 'uncaughtException', error
|
||||
|
||||
# Create the REPL by listening to **stdin**.
|
||||
repl = readline.createInterface stdio, autocomplete.complete
|
||||
repl = readline.createInterface stdio, autocomplete
|
||||
repl.setPrompt 'coffee> '
|
||||
stdio.on 'data', (buffer) -> repl.write buffer
|
||||
repl.on 'close', -> stdio.destroy()
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
return unless require?
|
||||
|
||||
complete = require './../lib/autocomplete'
|
||||
|
||||
eq_set = (left, right) ->
|
||||
left = left.slice(0)
|
||||
right = right.slice(0)
|
||||
left.sort()
|
||||
right.sort()
|
||||
eq left.join(' '), right.join(' ')
|
||||
|
||||
# JavaScript keywords
|
||||
[completions, completed] = complete.complete "c"
|
||||
ok completions instanceof Array
|
||||
should_be = ["case", "catch", "class", "clearInterval", "clearTimeout", "console", "const", "continue"]
|
||||
eq_set should_be, completions
|
||||
|
||||
[completions, completed] = complete.complete 'E'
|
||||
eq_set completions, ['EvalError', 'Error']
|
||||
|
||||
[completions, completed] = complete.complete "Math.c"
|
||||
eq_set completions, ["cos", "ceil"]
|
||||
|
||||
# I don't know how to make this testable :(
|
||||
# a = {baba: 1, babo: 2}
|
||||
|
||||
# [completions, completed] = complete.complete "a.bab"
|
||||
# eq_set completions, ["baba", "babo"]
|
||||
|
||||
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue