fix(tui): normalize tabs in editor setText and input paths (#2027)
This commit is contained in:
@@ -272,7 +272,7 @@ export class Editor implements Component, Focusable {
|
|||||||
|
|
||||||
/** Internal setText that doesn't reset history state - used by navigateHistory */
|
/** Internal setText that doesn't reset history state - used by navigateHistory */
|
||||||
private setTextInternal(text: string): void {
|
private setTextInternal(text: string): void {
|
||||||
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
const lines = text.split("\n");
|
||||||
this.state.lines = lines.length === 0 ? [""] : lines;
|
this.state.lines = lines.length === 0 ? [""] : lines;
|
||||||
this.state.cursorLine = this.state.lines.length - 1;
|
this.state.cursorLine = this.state.lines.length - 1;
|
||||||
this.setCursorCol(this.state.lines[this.state.cursorLine]?.length || 0);
|
this.setCursorCol(this.state.lines[this.state.cursorLine]?.length || 0);
|
||||||
@@ -819,11 +819,12 @@ export class Editor implements Component, Focusable {
|
|||||||
setText(text: string): void {
|
setText(text: string): void {
|
||||||
this.lastAction = null;
|
this.lastAction = null;
|
||||||
this.historyIndex = -1; // Exit history browsing mode
|
this.historyIndex = -1; // Exit history browsing mode
|
||||||
|
const normalized = this.normalizeText(text);
|
||||||
// Push undo snapshot if content differs (makes programmatic changes undoable)
|
// Push undo snapshot if content differs (makes programmatic changes undoable)
|
||||||
if (this.getText() !== text) {
|
if (this.getText() !== normalized) {
|
||||||
this.pushUndoSnapshot();
|
this.pushUndoSnapshot();
|
||||||
}
|
}
|
||||||
this.setTextInternal(text);
|
this.setTextInternal(normalized);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -839,6 +840,15 @@ export class Editor implements Component, Focusable {
|
|||||||
this.insertTextAtCursorInternal(text);
|
this.insertTextAtCursorInternal(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize text for editor storage:
|
||||||
|
* - Normalize line endings (\r\n and \r -> \n)
|
||||||
|
* - Expand tabs to 4 spaces
|
||||||
|
*/
|
||||||
|
private normalizeText(text: string): string {
|
||||||
|
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\t/g, " ");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal text insertion at cursor. Handles single and multi-line text.
|
* Internal text insertion at cursor. Handles single and multi-line text.
|
||||||
* Does not push undo snapshots or trigger autocomplete - caller is responsible.
|
* Does not push undo snapshots or trigger autocomplete - caller is responsible.
|
||||||
@@ -847,8 +857,8 @@ export class Editor implements Component, Focusable {
|
|||||||
private insertTextAtCursorInternal(text: string): void {
|
private insertTextAtCursorInternal(text: string): void {
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
|
|
||||||
// Normalize line endings
|
// Normalize line endings and tabs
|
||||||
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
const normalized = this.normalizeText(text);
|
||||||
const insertedLines = normalized.split("\n");
|
const insertedLines = normalized.split("\n");
|
||||||
|
|
||||||
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
||||||
@@ -955,14 +965,11 @@ export class Editor implements Component, Focusable {
|
|||||||
|
|
||||||
this.pushUndoSnapshot();
|
this.pushUndoSnapshot();
|
||||||
|
|
||||||
// Clean the pasted text
|
// Clean the pasted text: normalize line endings, expand tabs
|
||||||
const cleanText = pastedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
const cleanText = this.normalizeText(pastedText);
|
||||||
|
|
||||||
// Convert tabs to spaces (4 spaces per tab)
|
|
||||||
const tabExpandedText = cleanText.replace(/\t/g, " ");
|
|
||||||
|
|
||||||
// Filter out non-printable characters except newlines
|
// Filter out non-printable characters except newlines
|
||||||
let filteredText = tabExpandedText
|
let filteredText = cleanText
|
||||||
.split("")
|
.split("")
|
||||||
.filter((char) => char === "\n" || char.charCodeAt(0) >= 32)
|
.filter((char) => char === "\n" || char.charCodeAt(0) >= 32)
|
||||||
.join("");
|
.join("");
|
||||||
|
|||||||
Reference in New Issue
Block a user