Skip to content

Commit b31f07f

Browse files
committed
Add visual tab stop ruler
- Visual ruler at the top of the editor shows tab stops. - When 'Tab key inserts spaces' is set, the ruler shows the {opcodes, operands, comments} virutal tab stops. - In all other cases, fixed tab positions are shown based on the currently configured tab size. - Clicking the visual ruler at the top of the editor is a shortcut to opening the settings dialog.
1 parent bad3897 commit b31f07f

4 files changed

Lines changed: 76 additions & 1 deletion

File tree

src/ide/views/editors.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { createAssetHeaderPlugin } from "./assetdecorations";
2626
import { ProjectView } from "./baseviews";
2727
import { createTextTransformFilterEffect, textTransformFilterCompartment } from "./filters";
2828
import { breakpointMarkers, bytes, clock, currentPcMarker, errorMarkers, gutterLineInfo, offset, statusMarkers } from "./gutter";
29+
import { tabStopRuler } from "./tabstopruler";
2930
import { currentPc, errorMessages, errorSpans, highlightLines, showValue } from "./visuals";
3031

3132
// look ahead this many bytes when finding source lines for a PC
@@ -193,6 +194,7 @@ export class SourceEditor implements ProjectView {
193194
parser || [],
194195
theme,
195196
editorTheme,
197+
tabStopRuler,
196198
lineWrap ? EditorView.lineWrapping : [],
197199

198200
currentPc.field,

src/ide/views/tabstopruler.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { indentUnit } from "@codemirror/language";
2+
import { EditorState } from "@codemirror/state";
3+
import { EditorView, Panel, showPanel } from "@codemirror/view";
4+
import { AsmTabStops, tabStopsEquals } from "../../common/tabdetect";
5+
import { openSettings, tabStopsFacet } from "../settings";
6+
import { MAX_COLS, tabStopsToColumns } from "../views/tabs";
7+
8+
function buildContent(asmTabStops: AsmTabStops, tabSize: number, useTabs: boolean): string {
9+
const chars = new Array(MAX_COLS);
10+
chars.fill(useTabs ? " " : "<span class='cm-highlightSpace'> </span>");
11+
for (const col of tabStopsToColumns(asmTabStops, tabSize, useTabs)) {
12+
chars[col] = "▾";
13+
}
14+
return chars.join("");
15+
}
16+
17+
function rulerPanel(view: EditorView): Panel {
18+
const dom = document.createElement("div");
19+
dom.className = "tab-stop-ruler";
20+
dom.setAttribute("aria-hidden", "true");
21+
let currentAsmTabStops: AsmTabStops = {};
22+
let currentTabSize = 0;
23+
let currentIndent = "";
24+
25+
function rebuild() {
26+
const asmTabStops = view.state.facet(tabStopsFacet);
27+
const tabSize = view.state.facet(EditorState.tabSize);
28+
const indent = view.state.facet(indentUnit);
29+
if (tabStopsEquals(asmTabStops, currentAsmTabStops) && tabSize === currentTabSize && indent === currentIndent) return;
30+
currentAsmTabStops = asmTabStops;
31+
currentTabSize = tabSize;
32+
currentIndent = indent;
33+
dom.innerHTML = buildContent(asmTabStops, tabSize, indent === '\t');
34+
}
35+
36+
function sync() {
37+
const contentStyle = getComputedStyle(view.contentDOM);
38+
dom.style.font = contentStyle.font;
39+
const left = view.contentDOM.getBoundingClientRect().left - view.dom.getBoundingClientRect().left;
40+
dom.style.marginLeft = left + "px";
41+
const line = view.contentDOM.querySelector(".cm-line");
42+
if (line) dom.style.paddingLeft = getComputedStyle(line).paddingLeft;
43+
}
44+
45+
rebuild();
46+
dom.addEventListener("click", openSettings);
47+
sync();
48+
49+
return {
50+
dom,
51+
top: true,
52+
update() {
53+
rebuild();
54+
sync();
55+
}
56+
};
57+
}
58+
59+
export const tabStopRuler = showPanel.of(rulerPanel);

src/themes/editorTheme.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,10 @@ export const editorTheme = EditorView.theme({
3838
"& .cm-lineNumbers .cm-gutterElement": {
3939
color: "#99cc99",
4040
},
41-
});
41+
".tab-stop-ruler": {
42+
whiteSpace: "pre",
43+
overflow: "hidden",
44+
userSelect: "none",
45+
cursor: "pointer",
46+
}
47+
});

src/themes/mbo.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ export const mboTheme = EditorView.theme({
4343
backgroundImage: `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="10" height="20"><path stroke="%23aaaaaa45" stroke-width="1" fill="none" d="M0 10H10L7 6M10 10L7 14"/></svg>')`,
4444
backgroundPosition: "left 50%"
4545
},
46+
".cm-panels": {
47+
backgroundColor: "#4e4e4e" /* color left of tab stop ruler */,
48+
},
49+
".tab-stop-ruler": {
50+
color: "rgba(255,255,255,0.25)",
51+
backgroundColor: "#202020",
52+
borderBottom: "2px solid #4e4e4e",
53+
},
4654
}, { dark: true });
4755

4856
export const mboHighlightStyle = HighlightStyle.define([

0 commit comments

Comments
 (0)