feat(tui): make select list column sizing configurable (#2154)

* feat(tui): make select list layout configurable

* feat(tui): tune select list column sizing

* test(tui): fix select list alignment assertions
This commit is contained in:
Markus Ylisiurunen
2026-03-14 16:50:00 +02:00
committed by GitHub
parent e5b5738255
commit c9a3d14aa5
8 changed files with 253 additions and 76 deletions

View File

@@ -5,7 +5,7 @@ import { KillRing } from "../kill-ring.js";
import { type Component, CURSOR_MARKER, type Focusable, type TUI } from "../tui.js";
import { UndoStack } from "../undo-stack.js";
import { getSegmenter, isPunctuationChar, isWhitespaceChar, truncateToWidth, visibleWidth } from "../utils.js";
import { SelectList, type SelectListTheme } from "./select-list.js";
import { SelectList, type SelectListLayoutOptions, type SelectListTheme } from "./select-list.js";
const baseSegmenter = getSegmenter();
@@ -207,6 +207,11 @@ export interface EditorOptions {
autocompleteMaxVisible?: number;
}
const SLASH_COMMAND_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {
minPrimaryColumnWidth: 12,
maxPrimaryColumnWidth: 32,
};
export class Editor implements Component, Focusable {
private state: EditorState = {
lines: [""],
@@ -2031,6 +2036,14 @@ export class Editor implements Component, Focusable {
return firstPrefixIndex;
}
private createAutocompleteList(
prefix: string,
items: Array<{ value: string; label: string; description?: string }>,
): SelectList {
const layout = prefix.startsWith("/") ? SLASH_COMMAND_SELECT_LIST_LAYOUT : undefined;
return new SelectList(items, this.autocompleteMaxVisible, this.theme.selectList, layout);
}
private tryTriggerAutocomplete(explicitTab: boolean = false): void {
if (!this.autocompleteProvider) return;
@@ -2053,7 +2066,7 @@ export class Editor implements Component, Focusable {
if (suggestions && suggestions.items.length > 0) {
this.autocompletePrefix = suggestions.prefix;
this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList);
this.autocompleteList = this.createAutocompleteList(suggestions.prefix, suggestions.items);
// If typed prefix exactly matches one of the suggestions, select that item
const bestMatchIndex = this.getBestAutocompleteMatchIndex(suggestions.items, suggestions.prefix);
@@ -2129,7 +2142,7 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/
}
this.autocompletePrefix = suggestions.prefix;
this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList);
this.autocompleteList = this.createAutocompleteList(suggestions.prefix, suggestions.items);
// If typed prefix exactly matches one of the suggestions, select that item
const bestMatchIndex = this.getBestAutocompleteMatchIndex(suggestions.items, suggestions.prefix);
@@ -2169,7 +2182,7 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/
if (suggestions && suggestions.items.length > 0) {
this.autocompletePrefix = suggestions.prefix;
// Always create new SelectList to ensure update
this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList);
this.autocompleteList = this.createAutocompleteList(suggestions.prefix, suggestions.items);
// If typed prefix exactly matches one of the suggestions, select that item
const bestMatchIndex = this.getBestAutocompleteMatchIndex(suggestions.items, suggestions.prefix);