.load, .save and local scope tab completion #2063

Closed
wants to merge 3 commits into
from

Projects

None yet

4 participants

@seebees

Details below. My tests pass but I need to make sure 'make test-all' is clean. Before I do all that I wanted to make sure someone besides me thinks this is cool.

The major down side as I see it is that I'm storing every line executed in a given REPL on that REPL... But how much memory would that use anyway ;) On the up-side, local variable tab-completion and file saving!

If no one likes it, feel free to close it. You won't hurt my feelings.

REPLServer.prototype.resetContext:
Reset the line cache

REPLServer.prototype.memory (don't know if I like that name, called from finish)
pushes what cmd's have been executed against it into this.lines
pushes the "tab depth" for bufferedCommands, in this.lines.level

REPLServer.prototype.displayPrompt:
Uses "tab depth" from this.lines.level to adjust the prompt to visually denote this depth e.g.

asdf = function () {
… var inner = {
….. one:1

REPLServer.prototype.complete:
Now notices if there is a bufferedCommand and attempts determine locally scoped variables by removing any functions from this.lines and evaling these lines in a nested REPL e.g.

asdf = function () {
… var inner = { one: 1};
… inn\t
will complete to 'inner' and inner.o\t will complete to 'inner.one'
If the nested REPL still has a bufferedCommand it will falls back to the default.

ArrayStream is a helper class for the nested REPL to get commands pushed to it.
new REPLServer('', new ArrayStream());

Finally added two new REPL commands .save and .load, each takes 1 parameter, a file and attempts to save or load the file to or from the REPL respectively.

@bnoordhuis
Node.js Foundation member

@seebees: Can you explain how tab completion is different? I'm probably misunderstanding your patch but the REPL already supports property auto-completion. For example:

> inner = {one: 1}
{ one: 1 }
> inn<TAB><CR>
{ one: 1 }
> inner.<TAB>
inner.__defineGetter__      inner.__defineSetter__
inner.__lookupGetter__      inner.__lookupSetter__
inner.constructor           inner.hasOwnProperty
inner.isPrototypeOf         inner.propertyIsEnumerable
inner.toLocaleString        inner.toString
inner.valueOf               

inner.one                   

> inner.
@seebees

@bnoordhuis the REPL currently only supports property completion for global variables. Note the scope in your example vs mine :)

>var top = function() {
...var inner = {
.....one:1
....};
...inner.<TAB>
inner.__defineGetter__      inner.__defineSetter__
inner.__lookupGetter__      inner.__lookupSetter__
inner.constructor           inner.hasOwnProperty
inner.isPrototypeOf         inner.propertyIsEnumerable
inner.toLocaleString        inner.toString
inner.valueOf               

inner.one                   

...inner.
@ry
ry commented Nov 12, 2011

So - im having trouble seeing what is being added here

> function foo(a, b) {
... var hello = 'world';
... return he<TAB>llo;
... }
undefined
> foo()
'world'

hello is local to foo but is tab completed.

I do like the tests. Can you please separate the commit into two parts - one with tests which match today's behavior - and then a second test which adds to that with the new behavior? I think that will make it clear the difference.

seebees added some commits Nov 12, 2011
@seebees seebees Tab Compete test for node REPL
Currently the REPL only tab completes for globally scoped variables
df72553
@seebees seebees .load, .save and local scope tab completion
REPLServer.prototype.resetContext:
Reset the line cache

REPLServer.prototype.memory (don't know if I like that name, called from finish)
pushes what cmd's have been executed against it into this.lines
pushes the "tab depth" for bufferedCommands, in this.lines.level

REPLServer.prototype.displayPrompt:
Uses "tab depth" from this.lines.level to adjust the prompt to visually denote this depth e.g.
> asdf = function () {
… var inner = {
….. one:1

REPLServer.prototype.complete:
Now notices if there is a bufferedCommand and attempts determine locally scoped variables by removing any functions from this.lines and evaling these lines in a nested REPL e.g.
> asdf = function () {
… var inner = { one: 1};
… inn\t
will complete to 'inner' and inner.o\t will complete to 'inner.one'
If the nested REPL still has a bufferedCommand it will falls back to the default.

ArrayStream is a helper class for the nested REPL to get commands pushed to it.
new REPLServer('', new ArrayStream());

Finally added two new REPL commands .save and .load, each takes 1 parameter, a file and attempts to save or load the file to or from the REPL respectively.

Conflicts:

	test/simple/test-repl-tab-complete.js
0380f5c
@seebees seebees Need to respect tab depth
The second send "function test_fun(){"
increases the tab depth for the REPL so the new prompt is
…..

instead of
...
709b6fe
@seebees

@ry Is this what you want?

@ry
ry commented Nov 12, 2011

Yes. LGTM - we'll want to squash 0380f5c and 709b6fe when landing. I'm waiting for @indutny to review before merging.

@seebees

Ok. I'm thinking I should write tests for .load and .save. Every time I've though that something was simple enough that it did not need a test I turn out to be wrong. And I'm guessing that you will want me to update the documentation as well.

I'll add commits for that soon.

@indutny indutny commented on the diff Nov 12, 2011
lib/repl.js
@@ -304,6 +326,28 @@ var simpleExpressionRE =
// Warning: This eval's code like "foo.bar.baz", so it will run property
// getter code.
REPLServer.prototype.complete = function(line, callback) {
+ // There may be local variables to evaluate, try a nested REPL
+ if (this.bufferedCommand != undefined && this.bufferedCommand.length) {
+ // Get a new array of inputed lines
+ var tmp = this.lines.slice();
+ // Kill off all function declarations to push all local variables into
+ // global scope
+ this.lines.level.forEach(function (kill) {
+ if (kill.isFunction) {
+ tmp[kill.line] = '';
+ }
+ });
+ var flat = new ArrayStream(); // make a new "input" stream
+ var magic = new REPLServer('', flat); // make a nested REPL
@indutny
indutny Nov 12, 2011

Can we create and init only one instance of magic for REPLServer?

@indutny
Node.js Foundation member

lgtm, with one small comment ^

/cc @ry

@ry ry added a commit that closed this pull request Nov 12, 2011
@seebees seebees .load, .save and local scope tab completion
Fixes #2063.

REPLServer.prototype.resetContext:
Reset the line cache

REPLServer.prototype.memory (don't know if I like that name, called from finish)
pushes what cmd's have been executed against it into this.lines
pushes the "tab depth" for bufferedCommands, in this.lines.level

REPLServer.prototype.displayPrompt:
Uses "tab depth" from this.lines.level to adjust the prompt to visually
denote this depth e.g.
> asdf = function () {
… var inner = {
….. one:1

REPLServer.prototype.complete:
Now notices if there is a bufferedCommand and attempts determine locally
scoped variables by removing any functions from this.lines and evaling these
lines in a nested REPL e.g.
> asdf = function () {
… var inner = { one: 1};
… inn\t
will complete to 'inner' and inner.o\t will complete to 'inner.one'
If the nested REPL still has a bufferedCommand it will falls back to the
default.

ArrayStream is a helper class for the nested REPL to get commands pushed to it.
new REPLServer('', new ArrayStream());

Finally added two new REPL commands .save and .load, each takes 1 parameter,
a file and attempts to save or load the file to or from the REPL
respectively.
3421f43
@ry ry closed this in 3421f43 Nov 12, 2011
@ry
ry commented Nov 12, 2011

@seebees Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment