Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ dist/
compile_commands.json
.envrc.local
/src/glide.ts
.vscode/
17 changes: 15 additions & 2 deletions src/glide/browser/base/content/browser-commandline.mts
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,21 @@ export class TabsCompletionSource implements GlideCompletionSource<TabCompletion
],
}),

async accept() {
gBrowser.selectedTab = tab;
async accept(ctx) {
const input = ctx.input.trim();
const parts = input.split(" ");
const arg = parts[1];

// User typed "tab " with no arg - select the focused tab from completion list
if (arg === undefined || arg === "") {
gBrowser.selectedTab = tab;
GlideBrowser.remove_notification("glide-excmd-error");
return;
}

// User typed "tab <something>" - execute through GlideExcmds
// This handles both valid indices and invalid input (which will show an error)
await GlideExcmds.execute(input as any);
},
async delete() {
gBrowser.removeTab(this.tab);
Expand Down
6 changes: 6 additions & 0 deletions src/glide/browser/base/content/browser-excmds.mts
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,17 @@ class GlideExcmdsClass {
command: glide.ExcmdValue,
props?: SetOptional<ExecuteProps, "args">,
): Promise<void> {
const command_name = typeof command === "string" ? extract_command_name(command) : command.name;

try {
await this.#execute(command, {
...props,
args: { ...this.#parse_args(command), tab_id: GlideBrowser.active_tab_id, ...props?.args },
});

if (!command_name.includes("commandline_")) {
GlideBrowser.remove_notification("glide-excmd-error");
}
} catch (err) {
GlideBrowser._log.error(err);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -725,3 +725,237 @@ add_task(async function test_commandline_close() {
is(result, true, "close() should return true when the commandline was open");
await wait_for_mode("normal");
});

add_task(async function test_error_notification_tab_missing_args() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":tab<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

const notification = gNotificationBox.getNotificationWithValue("glide-excmd-error")!;
ok(
notification.shadowRoot.querySelector(".message")?.textContent?.includes("tab_index"),
"error message should mention missing 'tab_index' argument",
);
gNotificationBox.removeNotification(notification);
});
});

add_task(async function test_error_notification_tab_invalid_index() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":tab 999<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

const notification = gNotificationBox.getNotificationWithValue("glide-excmd-error")!;
ok(
notification.shadowRoot.querySelector(".message")?.textContent?.includes("could not find a tab"),
"error message should indicate tab not found",
);
gNotificationBox.removeNotification(notification);
});
});

add_task(async function test_error_notification_set_missing_args() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":set<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

gNotificationBox.removeNotification(gNotificationBox.getNotificationWithValue("glide-excmd-error")!);
});
});

add_task(async function test_error_notification_set_invalid_option() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":set invalid_option_name 123<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

const notification = gNotificationBox.getNotificationWithValue("glide-excmd-error")!;
ok(
notification.shadowRoot.querySelector(".message")?.textContent?.includes("not a valid option"),
"error message should indicate invalid option",
);
gNotificationBox.removeNotification(notification);
});
});

add_task(async function test_error_notification_mode_change_missing_args() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":mode_change<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

gNotificationBox.removeNotification(gNotificationBox.getNotificationWithValue("glide-excmd-error")!);
});
});

add_task(async function test_error_notification_keys_missing_args() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":keys<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

const notification = gNotificationBox.getNotificationWithValue("glide-excmd-error")!;
ok(
notification.shadowRoot.querySelector(".message")?.textContent?.includes("keyseq"),
"error message should mention missing 'keyseq' argument",
);
gNotificationBox.removeNotification(notification);
});
});

add_task(async function test_error_notification_unmap_missing_args() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":unmap<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

const notification = gNotificationBox.getNotificationWithValue("glide-excmd-error")!;
ok(
notification.shadowRoot.querySelector(".message")?.textContent?.includes("lhs"),
"error message should mention missing 'lhs' argument",
);
gNotificationBox.removeNotification(notification);
});
});

add_task(async function test_error_notification_caret_move_missing_args() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":caret_move<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

const notification = gNotificationBox.getNotificationWithValue("glide-excmd-error")!;
ok(
notification.shadowRoot.querySelector(".message")?.textContent?.includes("direction"),
"error message should mention missing 'direction' argument",
);
gNotificationBox.removeNotification(notification);
});
});

add_task(async function test_error_notification_cleared_tab_succeeds() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":tab<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

await keys(":tab 0<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error") === null).ok();

is(
gNotificationBox.getNotificationWithValue("glide-excmd-error"),
null,
"error notification should be cleared after successful tab command",
);
});
});

add_task(async function test_error_notification_cleared_mode_change_succeeds() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":mode_change<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

await keys(":mode_change normal<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error") === null).ok();

is(
gNotificationBox.getNotificationWithValue("glide-excmd-error"),
null,
"error notification should be cleared after successful mode_change command",
);
});
});

add_task(async function test_error_notification_cleared_by_different_command() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":tab<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

await keys(":reload<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error") === null).ok();

is(
gNotificationBox.getNotificationWithValue("glide-excmd-error"),
null,
"error notification should be cleared after a different successful command",
);
});
});

add_task(async function test_error_notification_cleared_by_different_command_with_args() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":set<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

await keys(":mode_change normal<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error") === null).ok();

is(
gNotificationBox.getNotificationWithValue("glide-excmd-error"),
null,
"error notification should be cleared after a different successful command with args",
);
});
});

add_task(async function test_error_notification_cleared_via_tab_completion() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":tab<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

await GlideTestUtils.commandline.open();
await keys("tab ");
await waiter(() => GlideTestUtils.commandline.current_source_header()).is("tabs");
await waiter(() => GlideTestUtils.commandline.visible_rows().length > 0).ok();
await keys("<CR>");

await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error") === null).ok();

is(
gNotificationBox.getNotificationWithValue("glide-excmd-error"),
null,
"error notification should be cleared after selecting tab via completion",
);
});
});

add_task(async function test_multiple_errors_cleared_by_one_success() {
await reload_config(function _() {});

await BrowserTestUtils.withNewTab(FILE, async () => {
await keys(":tab<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

await keys(":set<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error")).ok();

await keys(":tab 0<CR>");
await waiter(() => gNotificationBox.getNotificationWithValue("glide-excmd-error") === null).ok();

is(
gNotificationBox.getNotificationWithValue("glide-excmd-error"),
null,
"error notification should be cleared after any successful command",
);
});
});