1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2026-03-10 21:25:31 +00:00

More sensible tab/shift-tab handling

This commit is contained in:
Fred Sauer
2026-02-28 19:23:59 -08:00
parent 44f8b97ca5
commit 4df68a920a
2 changed files with 75 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
import { closeBrackets, deleteBracketPair } from "@codemirror/autocomplete";
import { defaultKeymap, history, historyKeymap, indentWithTab, undo } from "@codemirror/commands";
import { defaultKeymap, history, historyKeymap, undo } from "@codemirror/commands";
import { cpp } from "@codemirror/lang-cpp";
import { markdown } from "@codemirror/lang-markdown";
import { bracketMatching, foldGutter, indentOnInput, indentUnit } from "@codemirror/language";
@@ -26,6 +26,7 @@ import { isMobileDevice, ProjectView } from "./baseviews";
import { debugHighlightTagsTooltip } from "./debug";
import { createTextTransformFilterEffect, textTransformFilterCompartment } from "./filters";
import { breakpointMarkers, bytes, clock, currentPcMarker, errorMarkers, offset, statusMarkers } from "./gutter";
import { tabKeymap } from "./tabs";
import { currentPc, errorMessages, highlightLines, showValue } from "./visuals";
// TODO: make this an easily toggleable debug setting.
@@ -193,7 +194,7 @@ export class SourceEditor implements ProjectView {
debugHighlightTags ? debugHighlightTagsTooltip : [],
EditorState.tabSize.of(8),
indentUnit.of(" "),
keymap.of([indentWithTab]),
keymap.of(tabKeymap),
lineWrap ? EditorView.lineWrapping : [],
currentPc.field,

72
src/ide/views/tabs.ts Normal file
View File

@@ -0,0 +1,72 @@
import { EditorSelection } from "@codemirror/state";
import { EditorView, KeyBinding } from "@codemirror/view";
function tab(view: EditorView): boolean {
const tabSize = view.state.tabSize;
view.dispatch(view.state.changeByRange(sel => {
const startLine = view.state.doc.lineAt(sel.from);
const endLine = view.state.doc.lineAt(sel.to);
if (sel.empty) {
// Cursor only: insert spaces to next tab stop
const col = sel.head - startLine.from;
const spaces = tabSize - (col % tabSize);
const insert = " ".repeat(spaces);
return {
changes: { from: sel.from, insert },
range: EditorSelection.cursor(sel.from + spaces),
};
} else if (startLine.number === endLine.number) {
// Single-line selection: replace with spaces, cursor at end of whitespace
const col = sel.from - startLine.from;
const spaces = tabSize - (col % tabSize);
const insert = " ".repeat(spaces);
return {
changes: { from: sel.from, to: sel.to, insert },
range: EditorSelection.cursor(sel.from + spaces),
};
} else {
// Multi-line selection: move first non-ws char to next tab stop on each line
const changes = [];
for (let i = startLine.number; i <= endLine.number; i++) {
const line = view.state.doc.line(i);
const firstNonWs = line.text.search(/\S/);
if (firstNonWs < 0) continue;
const newCol = (Math.floor(firstNonWs / tabSize) + 1) * tabSize;
const spaces = newCol - firstNonWs;
changes.push({ from: line.from + firstNonWs, insert: " ".repeat(spaces) });
}
const changeSet = view.state.changes(changes);
return {
changes: changeSet,
range: EditorSelection.range(changeSet.mapPos(sel.from), changeSet.mapPos(sel.to)),
};
}
}));
return true;
}
function shiftTab(view: EditorView): boolean {
const tabSize = view.state.tabSize;
const changes = [];
const seen = new Set<number>();
for (const sel of view.state.selection.ranges) {
const startLine = view.state.doc.lineAt(sel.from);
const endLine = view.state.doc.lineAt(sel.to);
for (let i = startLine.number; i <= endLine.number; i++) {
if (seen.has(i)) continue;
seen.add(i);
const line = view.state.doc.line(i);
const firstNonWs = line.text.search(/\S/);
if (firstNonWs <= 0) continue;
const newCol = Math.floor((firstNonWs - 1) / tabSize) * tabSize;
changes.push({ from: line.from + newCol, to: line.from + firstNonWs });
}
}
if (changes.length) view.dispatch({ changes });
return true;
}
export const tabKeymap: KeyBinding[] = [
{ key: "Tab", run: tab },
{ key: "Shift-Tab", run: shiftTab },
];