Skip to content

[feature] Command mode#4880

Open
ckardaris wants to merge 7 commits intophilc:masterfrom
ckardaris:vimium-4103-command-line-mode
Open

[feature] Command mode#4880
ckardaris wants to merge 7 commits intophilc:masterfrom
ckardaris:vimium-4103-command-line-mode

Conversation

@ckardaris
Copy link
Copy Markdown

Introduce command mode in the omni bar.

Features:

  • Launch with ':' (default key mapping).
  • Support prefix counts.
  • Show all commands (even mapped ones).
  • Show all commands as specified (with any options) in user defined key mappings.
  • Show keys for mapped commands, similarly to the help page.

Fix:

  • Fix collection of unit tests in subdirectories.

Other:

  • Revise 'noRepeat' attribute for all commands.

Description

Good evening, this MR implements feature request #4103.

I have tried to implement this feature as cleanly as possible.
I understand that this is quite a big patch, so I expect some changes based on reviewer suggestions,
but I think that it is working nicely.
I tried to add some tests, as well, but I am afraid I am not very familiar with shoulda and puppeteer,
so I am not sure if I have followed best practices and if I am inside the general spirit of the Vimium tests.

I am looking forward to your feedback and hope to be merging this soon.

@philc
Copy link
Copy Markdown
Owner

philc commented Mar 24, 2026

Whoa, very exciting! Thank you for the PR. I'll take this for a spin in the next few days.

ckardaris added a commit to ckardaris/vimium that referenced this pull request Mar 26, 2026
Copy link
Copy Markdown
Owner

@philc philc left a comment

Choose a reason for hiding this comment

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

Hey @ckardaris, I tried this out and it works well. I've left some initial comments on the code. For the UX:

  • We should remove "command" as a prefix in the UI. One could argue it should be removed for bookmarks and tabs too when using their specific selection UIs, but that can be debated offline. Since commands can only be searched when using the command search UI, it just adds clutter to the UI.
  • The styles of the keybindings in this dialog needs to be improved. The Vomnibar's background colors are different than the help dialog's, so it doesn't look good, at least in dark mode. I can fiddle with it post-merge.
  • Hitting enter when there are no completions should do nothing, rather than submit the query to the default search engine. This is an existing bug, and also affects the bookmark and tabs listing.

tabId;
// Whether this is a suggestion provided by a user's custom search engine.
isCustomSearch;
// Suggestion in 'command' mode.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Use double quotes here, and throughout all the comments, to match the existing style.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done.

isCustomSearch;
// Suggestion in 'command' mode.
// command = {
// // 'RegistryEntry' to execute the command in 'NormalMode.commandHandler'.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This comment shouldn't be double commented.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done.

const relevancyHtml = showRelevancy
? `<span class='relevancy'>${this.computeRelevancy()}</span>`
? `
<span class='relevancy'>${this.computeRelevancy()}</span>`
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Why is there a newline added here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I have reverted. It was an attempt to unify the generated HTML and better format it, but I agree that maybe I should not touch unrelated code and keep the scope narrower.

<span class="title">${this.highlightQueryTerms(Utils.escapeHtml(this.title))}</span>
${relevancyHtml}
</div>\
this.html = `
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This looks like an unrelated whitespace change (indentation and additional line wrapping).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Same. I have reverted to keep the scope narrower.

</div>
`;
} else if (this.command) {
// Key mappings containing key-modifiers are represented in the form of '<modifier-key>'
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

key modifiers should be two words.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done.

CommandCompleter,
MultiCompleter,
Suggestion,
} from "../../background_scripts/completion/completers.js";
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Having each import on its own line does not match the style in the rest of the codebase.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I am afraid this is automatically formatted that way.

assert.equal("constructor ", ui.input.value);
});

should("fill the suggestions list with correct HTML", async () => {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This test is too verbose, and it's too fragile -- changing any part of the HTML will break this test.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I was mostly interested about testing the key block. I have simplified the test.

make.js Outdated
// Import every test file.
const dir = path.join(projectPath, "tests/unit_tests");
const files = Array.from(Deno.readDirSync(dir)).map((f) => f.name).sort();
const files = collectFiles(dir);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Nice find. That seems like a bad error =)

But it's not related to this PR; please split out into a separate PR.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done. #4882.

(await chrome.storage.session.get("commandToOptionsToKeys")).commandToOptionsToKeys;

// Create a RegistryEntry for the default action (no options specified) of a command.
const createUnboundRegistryEntry = (command) => {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I haven't looked closely at why this is needed, but it would be nice if we didn't have to have this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is done this way because the main loop uses commandsToOptionsToKeys to gather available commands. This means that non-mapped commands are not collected even though they are available. createUnboundRegistryEntry fills this gap.

To be honest this part of the implementation seemed quite obscure to me and it also made for some weird test programming that you have already commented on in the other threads.

I am open to suggestions if I am missing an obvious and easier way to do this.

});

// Listen and handle 'command' events coming from the background and
// originally sent from the omnibar page.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This comment is wrapped too aggressively.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done.

@ckardaris ckardaris force-pushed the vimium-4103-command-line-mode branch 2 times, most recently from eef7f95 to 73e6696 Compare March 28, 2026 15:52
@ckardaris
Copy link
Copy Markdown
Author

ckardaris commented Mar 28, 2026

Hello @philc. Thanks for the review. I am in the process of going over it and fixing stuff based on your suggestions.

* We should remove "command" as a prefix in the UI. One could argue it should be removed for bookmarks and tabs too when using their specific selection UIs, but that can be debated offline. Since commands can only be searched when using the command search UI, it just adds clutter to the UI.

I also agree that it is "weird" to see the same "source" for non-omni modes. Would the proposal of #4481 be interesting? I think we could the best of both worlds considering that we would still have an indicator of the current mode of operation.

* The styles of the keybindings in this dialog needs to be improved. The Vomnibar's background colors are different than the help dialog's, so it doesn't look good, at least in dark mode. I can fiddle with it post-merge.

Thanks. Would it maybe make sense to do a style update and unify the background with the help page, so that both modes work as expected and the CSS styling can keep being shared?

* Hitting enter when there are no completions should do nothing, rather than submit the query to the default search engine. This is an existing bug, and also affects the bookmark and tabs listing.

I have pushed a one-line fix for this. Let me know if you want to keep it in this PR or you would prefer to split it, since it is kind of a bigger (conceptually) change.

Introduce command mode in the omni bar.

Features:
- Launch with ':' (default key mapping).
- Support prefix counts.
- Show all commands (even mapped ones).
- Show all commands as specified (with any options) in user defined key
  mappings.
- Show keys for mapped commands, similarly to the help page.

Other:
- Revise 'noRepeat' attribute for all commands.

Disable ctrl-enter for command mode
"Enter" on no-selection is a no-op for all completers apart from "omni".
@ckardaris ckardaris force-pushed the vimium-4103-command-line-mode branch from 0861582 to b08a12b Compare March 28, 2026 16:29
@ckardaris
Copy link
Copy Markdown
Author

Hello @philc. Thanks again for looking at this PR. I have finished addressing the first round of review. Let me know if you have any additional remarks.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants