@mariozechner/pi-tui 0.62.0 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/autocomplete.d.ts +12 -12
- package/dist/autocomplete.d.ts.map +1 -1
- package/dist/autocomplete.js +88 -100
- package/dist/autocomplete.js.map +1 -1
- package/dist/components/editor.d.ts +13 -2
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +115 -97
- package/dist/components/editor.js.map +1 -1
- package/dist/components/markdown.d.ts.map +1 -1
- package/dist/components/markdown.js +1 -1
- package/dist/components/markdown.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +17 -1
- package/dist/terminal.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +19 -15
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
|
@@ -155,6 +155,7 @@ const SLASH_COMMAND_SELECT_LIST_LAYOUT = {
|
|
|
155
155
|
minPrimaryColumnWidth: 12,
|
|
156
156
|
maxPrimaryColumnWidth: 32,
|
|
157
157
|
};
|
|
158
|
+
const ATTACHMENT_AUTOCOMPLETE_DEBOUNCE_MS = 20;
|
|
158
159
|
export class Editor {
|
|
159
160
|
state = {
|
|
160
161
|
lines: [""],
|
|
@@ -178,6 +179,11 @@ export class Editor {
|
|
|
178
179
|
autocompleteState = null;
|
|
179
180
|
autocompletePrefix = "";
|
|
180
181
|
autocompleteMaxVisible = 5;
|
|
182
|
+
autocompleteAbort;
|
|
183
|
+
autocompleteDebounceTimer;
|
|
184
|
+
autocompleteRequestTask = Promise.resolve();
|
|
185
|
+
autocompleteStartToken = 0;
|
|
186
|
+
autocompleteRequestId = 0;
|
|
181
187
|
// Paste tracking for large pastes
|
|
182
188
|
pastes = new Map();
|
|
183
189
|
pasteCounter = 0;
|
|
@@ -237,6 +243,7 @@ export class Editor {
|
|
|
237
243
|
}
|
|
238
244
|
}
|
|
239
245
|
setAutocompleteProvider(provider) {
|
|
246
|
+
this.cancelAutocomplete();
|
|
240
247
|
this.autocompleteProvider = provider;
|
|
241
248
|
}
|
|
242
249
|
/**
|
|
@@ -478,7 +485,6 @@ export class Editor {
|
|
|
478
485
|
if (kb.matches(data, "tui.input.tab")) {
|
|
479
486
|
const selected = this.autocompleteList.getSelectedItem();
|
|
480
487
|
if (selected && this.autocompleteProvider) {
|
|
481
|
-
const shouldChainSlashArgumentAutocomplete = this.shouldChainSlashArgumentAutocompleteOnTabSelection();
|
|
482
488
|
this.pushUndoSnapshot();
|
|
483
489
|
this.lastAction = null;
|
|
484
490
|
const result = this.autocompleteProvider.applyCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol, selected, this.autocompletePrefix);
|
|
@@ -488,9 +494,6 @@ export class Editor {
|
|
|
488
494
|
this.cancelAutocomplete();
|
|
489
495
|
if (this.onChange)
|
|
490
496
|
this.onChange(this.getText());
|
|
491
|
-
if (shouldChainSlashArgumentAutocomplete && this.isBareCompletedSlashCommandAtCursor()) {
|
|
492
|
-
this.tryTriggerAutocomplete();
|
|
493
|
-
}
|
|
494
497
|
}
|
|
495
498
|
return;
|
|
496
499
|
}
|
|
@@ -781,6 +784,7 @@ export class Editor {
|
|
|
781
784
|
return { line: this.state.cursorLine, col: this.state.cursorCol };
|
|
782
785
|
}
|
|
783
786
|
setText(text) {
|
|
787
|
+
this.cancelAutocomplete();
|
|
784
788
|
this.lastAction = null;
|
|
785
789
|
this.historyIndex = -1; // Exit history browsing mode
|
|
786
790
|
const normalized = this.normalizeText(text);
|
|
@@ -798,6 +802,7 @@ export class Editor {
|
|
|
798
802
|
insertTextAtCursor(text) {
|
|
799
803
|
if (!text)
|
|
800
804
|
return;
|
|
805
|
+
this.cancelAutocomplete();
|
|
801
806
|
this.pushUndoSnapshot();
|
|
802
807
|
this.lastAction = null;
|
|
803
808
|
this.historyIndex = -1;
|
|
@@ -908,6 +913,7 @@ export class Editor {
|
|
|
908
913
|
}
|
|
909
914
|
}
|
|
910
915
|
handlePaste(pastedText) {
|
|
916
|
+
this.cancelAutocomplete();
|
|
911
917
|
this.historyIndex = -1; // Exit history browsing mode
|
|
912
918
|
this.lastAction = null;
|
|
913
919
|
this.pushUndoSnapshot();
|
|
@@ -952,6 +958,7 @@ export class Editor {
|
|
|
952
958
|
this.insertTextAtCursorInternal(filteredText);
|
|
953
959
|
}
|
|
954
960
|
addNewLine() {
|
|
961
|
+
this.cancelAutocomplete();
|
|
955
962
|
this.historyIndex = -1; // Exit history browsing mode
|
|
956
963
|
this.lastAction = null;
|
|
957
964
|
this.pushUndoSnapshot();
|
|
@@ -981,6 +988,7 @@ export class Editor {
|
|
|
981
988
|
return this.state.cursorCol > 0 && currentLine[this.state.cursorCol - 1] === "\\";
|
|
982
989
|
}
|
|
983
990
|
submitValue() {
|
|
991
|
+
this.cancelAutocomplete();
|
|
984
992
|
const result = this.expandPasteMarkers(this.state.lines.join("\n")).trim();
|
|
985
993
|
this.state = { lines: [""], cursorLine: 0, cursorCol: 0 };
|
|
986
994
|
this.pastes.clear();
|
|
@@ -1677,22 +1685,6 @@ export class Editor {
|
|
|
1677
1685
|
isInSlashCommandContext(textBeforeCursor) {
|
|
1678
1686
|
return this.isSlashMenuAllowed() && textBeforeCursor.trimStart().startsWith("/");
|
|
1679
1687
|
}
|
|
1680
|
-
shouldChainSlashArgumentAutocompleteOnTabSelection() {
|
|
1681
|
-
if (this.autocompleteState !== "regular") {
|
|
1682
|
-
return false;
|
|
1683
|
-
}
|
|
1684
|
-
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1685
|
-
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
1686
|
-
return this.isInSlashCommandContext(textBeforeCursor) && !textBeforeCursor.trimStart().includes(" ");
|
|
1687
|
-
}
|
|
1688
|
-
isBareCompletedSlashCommandAtCursor() {
|
|
1689
|
-
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1690
|
-
if (this.state.cursorCol !== currentLine.length) {
|
|
1691
|
-
return false;
|
|
1692
|
-
}
|
|
1693
|
-
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol).trimStart();
|
|
1694
|
-
return /^\/\S+ $/.test(textBeforeCursor);
|
|
1695
|
-
}
|
|
1696
1688
|
// Autocomplete methods
|
|
1697
1689
|
/**
|
|
1698
1690
|
* Find the best autocomplete item index for the given prefix.
|
|
@@ -1725,38 +1717,13 @@ export class Editor {
|
|
|
1725
1717
|
return new SelectList(items, this.autocompleteMaxVisible, this.theme.selectList, layout);
|
|
1726
1718
|
}
|
|
1727
1719
|
tryTriggerAutocomplete(explicitTab = false) {
|
|
1728
|
-
|
|
1729
|
-
return;
|
|
1730
|
-
// Check if we should trigger file completion on Tab
|
|
1731
|
-
if (explicitTab) {
|
|
1732
|
-
const provider = this.autocompleteProvider;
|
|
1733
|
-
const shouldTrigger = !provider.shouldTriggerFileCompletion ||
|
|
1734
|
-
provider.shouldTriggerFileCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol);
|
|
1735
|
-
if (!shouldTrigger) {
|
|
1736
|
-
return;
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
const suggestions = this.autocompleteProvider.getSuggestions(this.state.lines, this.state.cursorLine, this.state.cursorCol);
|
|
1740
|
-
if (suggestions && suggestions.items.length > 0) {
|
|
1741
|
-
this.autocompletePrefix = suggestions.prefix;
|
|
1742
|
-
this.autocompleteList = this.createAutocompleteList(suggestions.prefix, suggestions.items);
|
|
1743
|
-
// If typed prefix exactly matches one of the suggestions, select that item
|
|
1744
|
-
const bestMatchIndex = this.getBestAutocompleteMatchIndex(suggestions.items, suggestions.prefix);
|
|
1745
|
-
if (bestMatchIndex >= 0) {
|
|
1746
|
-
this.autocompleteList.setSelectedIndex(bestMatchIndex);
|
|
1747
|
-
}
|
|
1748
|
-
this.autocompleteState = "regular";
|
|
1749
|
-
}
|
|
1750
|
-
else {
|
|
1751
|
-
this.cancelAutocomplete();
|
|
1752
|
-
}
|
|
1720
|
+
this.requestAutocomplete({ force: false, explicitTab });
|
|
1753
1721
|
}
|
|
1754
1722
|
handleTabCompletion() {
|
|
1755
1723
|
if (!this.autocompleteProvider)
|
|
1756
1724
|
return;
|
|
1757
1725
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1758
1726
|
const beforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
1759
|
-
// Check if we're in a slash command context
|
|
1760
1727
|
if (this.isInSlashCommandContext(beforeCursor) && !beforeCursor.trimStart().includes(" ")) {
|
|
1761
1728
|
this.handleSlashCommandCompletion();
|
|
1762
1729
|
}
|
|
@@ -1765,79 +1732,130 @@ export class Editor {
|
|
|
1765
1732
|
}
|
|
1766
1733
|
}
|
|
1767
1734
|
handleSlashCommandCompletion() {
|
|
1768
|
-
this.
|
|
1735
|
+
this.requestAutocomplete({ force: false, explicitTab: true });
|
|
1769
1736
|
}
|
|
1770
|
-
/*
|
|
1771
|
-
https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/559322883
|
|
1772
|
-
17 this job fails with https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19
|
|
1773
|
-
536643416/job/55932288317 havea look at .gi
|
|
1774
|
-
*/
|
|
1775
1737
|
forceFileAutocomplete(explicitTab = false) {
|
|
1738
|
+
this.requestAutocomplete({ force: true, explicitTab });
|
|
1739
|
+
}
|
|
1740
|
+
requestAutocomplete(options) {
|
|
1776
1741
|
if (!this.autocompleteProvider)
|
|
1777
1742
|
return;
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1743
|
+
if (options.force) {
|
|
1744
|
+
const provider = this.autocompleteProvider;
|
|
1745
|
+
const shouldTrigger = !provider.shouldTriggerFileCompletion ||
|
|
1746
|
+
provider.shouldTriggerFileCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol);
|
|
1747
|
+
if (!shouldTrigger) {
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
this.cancelAutocompleteRequest();
|
|
1752
|
+
const startToken = ++this.autocompleteStartToken;
|
|
1753
|
+
const debounceMs = this.getAutocompleteDebounceMs(options);
|
|
1754
|
+
if (debounceMs > 0) {
|
|
1755
|
+
this.autocompleteDebounceTimer = setTimeout(() => {
|
|
1756
|
+
this.autocompleteDebounceTimer = undefined;
|
|
1757
|
+
void this.startAutocompleteRequest(startToken, options);
|
|
1758
|
+
}, debounceMs);
|
|
1782
1759
|
return;
|
|
1783
1760
|
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
const result = this.autocompleteProvider.applyCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol, item, suggestions.prefix);
|
|
1792
|
-
this.state.lines = result.lines;
|
|
1793
|
-
this.state.cursorLine = result.cursorLine;
|
|
1794
|
-
this.setCursorCol(result.cursorCol);
|
|
1795
|
-
if (this.onChange)
|
|
1796
|
-
this.onChange(this.getText());
|
|
1761
|
+
void this.startAutocompleteRequest(startToken, options);
|
|
1762
|
+
}
|
|
1763
|
+
async startAutocompleteRequest(startToken, options) {
|
|
1764
|
+
const previousTask = this.autocompleteRequestTask;
|
|
1765
|
+
this.autocompleteRequestTask = (async () => {
|
|
1766
|
+
await previousTask;
|
|
1767
|
+
if (startToken !== this.autocompleteStartToken || !this.autocompleteProvider) {
|
|
1797
1768
|
return;
|
|
1798
1769
|
}
|
|
1799
|
-
|
|
1800
|
-
this.
|
|
1801
|
-
|
|
1802
|
-
const
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1770
|
+
const controller = new AbortController();
|
|
1771
|
+
this.autocompleteAbort = controller;
|
|
1772
|
+
const requestId = ++this.autocompleteRequestId;
|
|
1773
|
+
const snapshotText = this.getText();
|
|
1774
|
+
const snapshotLine = this.state.cursorLine;
|
|
1775
|
+
const snapshotCol = this.state.cursorCol;
|
|
1776
|
+
await this.runAutocompleteRequest(requestId, controller, snapshotText, snapshotLine, snapshotCol, options);
|
|
1777
|
+
})();
|
|
1778
|
+
await this.autocompleteRequestTask;
|
|
1779
|
+
}
|
|
1780
|
+
getAutocompleteDebounceMs(options) {
|
|
1781
|
+
if (options.explicitTab || options.force) {
|
|
1782
|
+
return 0;
|
|
1807
1783
|
}
|
|
1808
|
-
|
|
1784
|
+
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
1785
|
+
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
1786
|
+
const isAttachmentContext = /(?:^|[ \t])@(?:"[^"]*|[^\s]*)$/.test(textBeforeCursor);
|
|
1787
|
+
return isAttachmentContext ? ATTACHMENT_AUTOCOMPLETE_DEBOUNCE_MS : 0;
|
|
1788
|
+
}
|
|
1789
|
+
async runAutocompleteRequest(requestId, controller, snapshotText, snapshotLine, snapshotCol, options) {
|
|
1790
|
+
if (!this.autocompleteProvider)
|
|
1791
|
+
return;
|
|
1792
|
+
const suggestions = await this.autocompleteProvider.getSuggestions(this.state.lines, this.state.cursorLine, this.state.cursorCol, { signal: controller.signal, force: options.force });
|
|
1793
|
+
if (!this.isAutocompleteRequestCurrent(requestId, controller, snapshotText, snapshotLine, snapshotCol)) {
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
this.autocompleteAbort = undefined;
|
|
1797
|
+
if (!suggestions || suggestions.items.length === 0) {
|
|
1809
1798
|
this.cancelAutocomplete();
|
|
1799
|
+
this.tui.requestRender();
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
if (options.force && options.explicitTab && suggestions.items.length === 1) {
|
|
1803
|
+
const item = suggestions.items[0];
|
|
1804
|
+
this.pushUndoSnapshot();
|
|
1805
|
+
this.lastAction = null;
|
|
1806
|
+
const result = this.autocompleteProvider.applyCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol, item, suggestions.prefix);
|
|
1807
|
+
this.state.lines = result.lines;
|
|
1808
|
+
this.state.cursorLine = result.cursorLine;
|
|
1809
|
+
this.setCursorCol(result.cursorCol);
|
|
1810
|
+
if (this.onChange)
|
|
1811
|
+
this.onChange(this.getText());
|
|
1812
|
+
this.tui.requestRender();
|
|
1813
|
+
return;
|
|
1810
1814
|
}
|
|
1815
|
+
this.applyAutocompleteSuggestions(suggestions, options.force ? "force" : "regular");
|
|
1816
|
+
this.tui.requestRender();
|
|
1811
1817
|
}
|
|
1812
|
-
|
|
1818
|
+
isAutocompleteRequestCurrent(requestId, controller, snapshotText, snapshotLine, snapshotCol) {
|
|
1819
|
+
return (!controller.signal.aborted &&
|
|
1820
|
+
requestId === this.autocompleteRequestId &&
|
|
1821
|
+
this.getText() === snapshotText &&
|
|
1822
|
+
this.state.cursorLine === snapshotLine &&
|
|
1823
|
+
this.state.cursorCol === snapshotCol);
|
|
1824
|
+
}
|
|
1825
|
+
applyAutocompleteSuggestions(suggestions, state) {
|
|
1826
|
+
this.autocompletePrefix = suggestions.prefix;
|
|
1827
|
+
this.autocompleteList = this.createAutocompleteList(suggestions.prefix, suggestions.items);
|
|
1828
|
+
const bestMatchIndex = this.getBestAutocompleteMatchIndex(suggestions.items, suggestions.prefix);
|
|
1829
|
+
if (bestMatchIndex >= 0) {
|
|
1830
|
+
this.autocompleteList.setSelectedIndex(bestMatchIndex);
|
|
1831
|
+
}
|
|
1832
|
+
this.autocompleteState = state;
|
|
1833
|
+
}
|
|
1834
|
+
cancelAutocompleteRequest() {
|
|
1835
|
+
this.autocompleteStartToken += 1;
|
|
1836
|
+
if (this.autocompleteDebounceTimer) {
|
|
1837
|
+
clearTimeout(this.autocompleteDebounceTimer);
|
|
1838
|
+
this.autocompleteDebounceTimer = undefined;
|
|
1839
|
+
}
|
|
1840
|
+
this.autocompleteAbort?.abort();
|
|
1841
|
+
this.autocompleteAbort = undefined;
|
|
1842
|
+
}
|
|
1843
|
+
clearAutocompleteUi() {
|
|
1813
1844
|
this.autocompleteState = null;
|
|
1814
1845
|
this.autocompleteList = undefined;
|
|
1815
1846
|
this.autocompletePrefix = "";
|
|
1816
1847
|
}
|
|
1848
|
+
cancelAutocomplete() {
|
|
1849
|
+
this.cancelAutocompleteRequest();
|
|
1850
|
+
this.clearAutocompleteUi();
|
|
1851
|
+
}
|
|
1817
1852
|
isShowingAutocomplete() {
|
|
1818
1853
|
return this.autocompleteState !== null;
|
|
1819
1854
|
}
|
|
1820
1855
|
updateAutocomplete() {
|
|
1821
1856
|
if (!this.autocompleteState || !this.autocompleteProvider)
|
|
1822
1857
|
return;
|
|
1823
|
-
|
|
1824
|
-
this.forceFileAutocomplete();
|
|
1825
|
-
return;
|
|
1826
|
-
}
|
|
1827
|
-
const suggestions = this.autocompleteProvider.getSuggestions(this.state.lines, this.state.cursorLine, this.state.cursorCol);
|
|
1828
|
-
if (suggestions && suggestions.items.length > 0) {
|
|
1829
|
-
this.autocompletePrefix = suggestions.prefix;
|
|
1830
|
-
// Always create new SelectList to ensure update
|
|
1831
|
-
this.autocompleteList = this.createAutocompleteList(suggestions.prefix, suggestions.items);
|
|
1832
|
-
// If typed prefix exactly matches one of the suggestions, select that item
|
|
1833
|
-
const bestMatchIndex = this.getBestAutocompleteMatchIndex(suggestions.items, suggestions.prefix);
|
|
1834
|
-
if (bestMatchIndex >= 0) {
|
|
1835
|
-
this.autocompleteList.setSelectedIndex(bestMatchIndex);
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
else {
|
|
1839
|
-
this.cancelAutocomplete();
|
|
1840
|
-
}
|
|
1858
|
+
this.requestAutocomplete({ force: this.autocompleteState === "force", explicitTab: false });
|
|
1841
1859
|
}
|
|
1842
1860
|
}
|
|
1843
1861
|
//# sourceMappingURL=editor.js.map
|