Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli: implement autocompletion for cli commands #132

Closed
wants to merge 7 commits into from

Conversation

lundibundi
Copy link
Member

@lundibundi lundibundi commented May 5, 2017

Implements 'Autocompletion for CLI commands' and allows to implement 'Autocompletion for method names' (#125)

The behavior is as follows:

  • on empty string (or trailing space) just show what commands are available for current level
  • on partially entered commands show appropriate completions
  • on fully entered command with no trailing space or when no completions available show help
  • allow to use partially entered command without completing them it there are no collisions

Now it splits 'str' till limit is bound or no more separators
left in 'str' if leaveEmpty is true then multiple separators
in sequence are written in the resulting array as one empty
string (''), else they are skipped and doesn't get counted to limit
@lundibundi lundibundi added the cli label May 5, 2017
@lundibundi lundibundi self-assigned this May 5, 2017
@lundibundi lundibundi requested review from belochub and aqrln May 5, 2017 18:33
@aqrln aqrln mentioned this pull request May 5, 2017
2 tasks
@aqrln
Copy link
Member

aqrln commented May 5, 2017

@lundibundi can you please add the Refs: https://github.com/metarhia/JSTP/issues/125 metadata field to the description of your second commit? Thanks!

tools/cli.js Outdated

const result = [];
let start = 0;

// eslint-disable-next-line no-unmodified-loop-condition
for (let i = 0; !limit || i < limit; i++) {
while (!limit || limit - result.length > 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, rewriting second condition as

result.length < limit

will make it more readable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@lundibundi
Copy link
Member Author

@aqrln done

tools/cli.js Outdated
const split = str.indexOf(separator, start);
if (split === -1) break;
if (!shouldTrim(start, split)) {
if (start !== split || leaveEmpty && !isLastEmpty(result)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This complex condition really needs parentheses.

tools/cli.js Outdated
@@ -24,14 +24,64 @@ const log = (msg) => {
if (userInput) rl.write(userInput);
};

function completeByFields(input, object) {
return Object.keys(object)
.filter(c => !c.startsWith('_') && (!input || c.startsWith(input)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What type can input have? If it's always a string, then the !input check is superfluous, c.startsWith('') will always return true.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aqrln the problem is it can be undefined when you press tab with no input, not sure how to handle this better

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, well then. If it can be undefined, the check is completely valid.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aqrln, but isn't it redundant? As far as I know, String.prototype.startsWith will return false when first argument is undefined.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aqrln, oh, I am sorry, I see why this check is OK here.

// splits 'str' till limit is bound or no more separators left in 'str'
// if leaveEmpty is true then multiple separators in sequence are written in
// resulting array as one empty string (''), else they are skipped
// and doesn't get counted to limit
function _split(str, separator, limit, leaveEmpty) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this PR, but why does this function start with an underscore? It isn't exported so it's private anyway.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aqrln we originally wrote it like this, as it really is private, i guess i will fix this during decomposition of cli.js in #127

tools/cli.js Outdated
}
start = split + separator.length;
}
if (!shouldTrim(start, str.length)) {
if (start !== str.length || leaveEmpty && !isLastEmpty(result)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is almost the same condition as on line 192, you should probably replace it with function, like it was before with shouldTrim.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@belochub yeah, but i'm not sure if it's okay to use shouldTrim (name) as it will also add '' when needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lundibundi, I didn't say that you should leave shouldTrim the way it was before. I meant that you you should replace repetitive conditions with function, like it was done before with shouldTrim function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@belochub I know, I meant that I couldn't come up with a good name for it.

tools/cli.js Outdated
}

function tryComplete(input, completer) {
const completions = completer._complete([input], 0)[0];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understand the completer interface, _complete and _help are its public methods. Why do they start with underscores?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. It's a hack to make objects which serve as sources for completions completers themselves.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aqrln hm, at first I thought to always use fields for completion, but now I see that it'd to be better to state the completions manually, then i can get rid of the underscore.

tools/cli.js Outdated
}
return [completions, help];
}
const [newCompletions, newDepth] = completer._complete(inputs, depth);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment above this function states:

// completer - object that has '_complete(inputs, depth)' function or ._help()
// or neither (no completions or help available)

But if the completer object has no _complete method, this line will fail with a TypeError.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, forgot about first entrance, thanks for catching that.

tools/cli.js Outdated
return helper(newDepth, depth, nextCompleter, newCompletions);
}
if (inputs[oldDepth] === completions[0]) {
completions.shift();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This statement modifies the completions array. Won't it have any unwanted side-effects?

Copy link
Member Author

@lundibundi lundibundi May 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's needed to remove completion if user has already entered full command, what sort of side-effects? Actually it is possible to use it's own return statement there as completions will always by empty at that time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, I was thinking of some global array of completions that unexpectedly gets deteriorated as user keeps hitting Tab. That's not the case here.

Copy link
Member

@aqrln aqrln May 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, it's not related to the current PR if that's how it is actually implemented in the CLI. It's a matter of a separate issue and PR.

Sorry, wrong thread.

tools/cli.js Outdated
commandProcessor.event = (interfaceName, eventName, args, callback) => {
if (!state.client) return callback(new Error('Not connected'));
state.connection.emitRemoteEvent(interfaceName, eventName, args);
callback();
};

commandProcessor.event._help = () => (
'event <interfaceName> <eventName> [ <arg> [ , ... ] ]'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Events in JSTP don't have a concept of arguments, like methods do. They only have a single argument that can be of any type. Is it just the help that is incorrect here or that's how it is actually implemented so that it's a bug in CLI?

Copy link
Member Author

@lundibundi lundibundi May 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aqrln not sure what you meant, the '[ <arg> [ , ... ] ]' will be passed as an array as a third argument to Connection.emitRemoteEvent

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but it's not supposed to be an array. It can be an arbitrary JavaScript value. At least, in the current version of the protocol.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, it's not related to the current PR if that's how it is actually implemented in the CLI. It's a matter of a separate issue and PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it is implemented like that, so we should open an issue for that.

If anyone passed 'completer' that has no _complete method
it'd have failed with TypeError
tools/cli.js Outdated
function _split(str, separator, limit, leaveEmpty) {
const shouldTrim = (start, split) => !leaveEmpty && start === split;
const isLastEmpty = arr => !arr[arr.length - 1];

const result = [];
let start = 0;

// eslint-disable-next-line no-unmodified-loop-condition
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the linter still fail without this directive with the new loop condition?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aqrln yeah(

@aqrln aqrln changed the title Autocompletion cli cli: implement autocompletion for cli commands May 5, 2017
@aqrln aqrln added this to the 1.0.0 milestone May 5, 2017
Get rid of underscore methods and state available completions
manually in complete method
* don't use shift, just return empty array
* extract complex condition to separate arrow function
tools/cli.js Outdated
// limit - resulting length of output array - 1 (last one is what's left),
// if !limit === true => means no limit and split till no more
// separators found
// leaveEmpty - if true multiple separators in sequence will be added as empty
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Word 'empty' is repeated twice here, probably a typo.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah

tools/cli.js Outdated
@@ -109,17 +109,18 @@ const state = {
connection: null
};

commandProcessor._complete = (inputs, depth) => {
commandProcessor.complete = (inputs, depth) => {
const completions = ['call', 'event', 'connect', 'disconnect', 'exit'];
Copy link
Member

@aqrln aqrln May 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer these to be sorted alphabetically, though I don't feel strongly for it.

tools/cli.js Outdated
const state = {
client: null,
connection: null
};

commandProcessor.complete = (inputs, depth) => {
const completions = ['call', 'event', 'connect', 'disconnect', 'exit'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer these to be sorted alphabetically, though I don't feel strongly for it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok with me

* sort available completions in commandProcessor
* fix a typo in iterativeCompletion doc
It was working incorrectly with multiple leading separators
Copy link
Member

@belochub belochub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@aqrln aqrln left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

aqrln pushed a commit that referenced this pull request May 5, 2017
Now it splits 'str' till limit is bound or no more separators
left in 'str' if leaveEmpty is true then multiple separators
in sequence are written in the resulting array as one empty
string (''), else they are skipped and doesn't get counted to limit

PR-URL: #132
Reviewed-By: Mykola Bilochub <nbelochub@gmail.com>
Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com>
aqrln pushed a commit that referenced this pull request May 5, 2017
PR-URL: #132
Refs: #125
Reviewed-By: Mykola Bilochub <nbelochub@gmail.com>
Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com>
@aqrln
Copy link
Member

aqrln commented May 5, 2017

Landed in f11130f and 8a6bc52 🎉

@aqrln aqrln closed this May 5, 2017
@aqrln aqrln deleted the autocompletion-cli branch May 5, 2017 22:36
belochub pushed a commit that referenced this pull request Jan 22, 2018
Now it splits 'str' till limit is bound or no more separators
left in 'str' if leaveEmpty is true then multiple separators
in sequence are written in the resulting array as one empty
string (''), else they are skipped and doesn't get counted to limit

PR-URL: #132
Reviewed-By: Mykola Bilochub <nbelochub@gmail.com>
Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com>
belochub pushed a commit that referenced this pull request Jan 22, 2018
PR-URL: #132
Refs: #125
Reviewed-By: Mykola Bilochub <nbelochub@gmail.com>
Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com>
belochub pushed a commit that referenced this pull request Jan 22, 2018
Now it splits 'str' till limit is bound or no more separators
left in 'str' if leaveEmpty is true then multiple separators
in sequence are written in the resulting array as one empty
string (''), else they are skipped and doesn't get counted to limit

PR-URL: #132
Reviewed-By: Mykola Bilochub <nbelochub@gmail.com>
Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com>
belochub pushed a commit that referenced this pull request Jan 22, 2018
PR-URL: #132
Refs: #125
Reviewed-By: Mykola Bilochub <nbelochub@gmail.com>
Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com>
@belochub belochub mentioned this pull request Jan 22, 2018
belochub pushed a commit to metarhia/mdsf that referenced this pull request Jul 19, 2018
Now it splits 'str' till limit is bound or no more separators
left in 'str' if leaveEmpty is true then multiple separators
in sequence are written in the resulting array as one empty
string (''), else they are skipped and doesn't get counted to limit

PR-URL: metarhia/jstp#132
Reviewed-By: Mykola Bilochub <nbelochub@gmail.com>
Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com>
belochub pushed a commit to metarhia/mdsf that referenced this pull request Jul 19, 2018
PR-URL: metarhia/jstp#132
Refs: metarhia/jstp#125
Reviewed-By: Mykola Bilochub <nbelochub@gmail.com>
Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com>
belochub pushed a commit to metarhia/mdsf that referenced this pull request Jul 19, 2018
Now it splits 'str' till limit is bound or no more separators
left in 'str' if leaveEmpty is true then multiple separators
in sequence are written in the resulting array as one empty
string (''), else they are skipped and doesn't get counted to limit

PR-URL: metarhia/jstp#132
Reviewed-By: Mykola Bilochub <nbelochub@gmail.com>
Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com>
belochub pushed a commit to metarhia/mdsf that referenced this pull request Jul 19, 2018
PR-URL: metarhia/jstp#132
Refs: metarhia/jstp#125
Reviewed-By: Mykola Bilochub <nbelochub@gmail.com>
Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants