1
0
Fork 0
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:
Jeremy Ashkenas 2011-01-15 14:53:07 -05:00
parent 3f586ff4ab
commit 9a63b3147f
4 changed files with 90 additions and 79 deletions

View file

@ -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);

View file

@ -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]

View file

@ -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()

View file

@ -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"]