Skip to content

Commit

Permalink
Fix the width of the structure highlighting.
Browse files Browse the repository at this point in the history
Closes #253
  • Loading branch information
microbit-matt-hillsdon committed May 26, 2022
1 parent 0239287 commit f2dd315
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 19 deletions.
4 changes: 3 additions & 1 deletion src/editor/codemirror/structure-highlighting/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ export const baseTheme = EditorView.baseTheme({
position: "absolute",
top: 0,
height: "100%",
width: "100%",
// Width is set in code.
zIndex: -1,
// Prevents horizontal scrollbar flicker when narrowing the editor.
overflow: "hidden",
},
".cm-cs--block, .cm-cs--indent": {
display: "block",
Expand Down
60 changes: 45 additions & 15 deletions src/editor/codemirror/structure-highlighting/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,35 @@ const grammarInfo = {
]),
};

interface Measure {
blocks: VisualBlock[];
class Measure {
constructor(
readonly width: number,
readonly left: number,
readonly blocks: VisualBlock[]
) {}
eq(other: Measure) {
if (this.width !== other.width) {
return false;
}
if (this.left !== other.left) {
return false;
}
const blocksChanged =
other.blocks.length !== this.blocks.length ||
other.blocks.some((b, i) => !b.eq(this.blocks[i]));
if (blocksChanged) {
return false;
}
return true;
}
}

export const codeStructureView = (option: "full" | "simple") =>
ViewPlugin.fromClass(
class {
measureReq: { read: () => Measure; write: (value: Measure) => void };
overlayLayer: HTMLElement;
blocks: VisualBlock[] = [];
lastMeasure: Measure = new Measure(0, 0, []);

constructor(readonly view: EditorView) {
this.measureReq = {
Expand All @@ -59,6 +78,18 @@ export const codeStructureView = (option: "full" | "simple") =>
const view = this.view;
const { state } = view;

const contentDOMRect = view.contentDOM.getBoundingClientRect();
const scrollDOMRect = view.scrollDOM.getBoundingClientRect();
// The gutter is awkward as it's position fixed in the scroller.
const gutterWidth =
view.scrollDOM.firstElementChild!.getBoundingClientRect().width;
const width = Math.max(
contentDOMRect.width,
// When the content is narrower than the scrollable area.
scrollDOMRect.width - gutterWidth
);
const left = gutterWidth;

let cursorFound = false;

const positionsForNode = (
Expand All @@ -67,11 +98,8 @@ export const codeStructureView = (option: "full" | "simple") =>
end: number,
depth: number,
body: boolean
) => {
): Positions | undefined => {
const diagnostics = state.field(lintState, false)?.diagnostics;
const leftEdge =
view.contentDOM.getBoundingClientRect().left -
view.scrollDOM.getBoundingClientRect().left;
const indentWidth =
state.facet(indentUnit).length * view.defaultCharacterWidth;

Expand Down Expand Up @@ -106,7 +134,7 @@ export const codeStructureView = (option: "full" | "simple") =>
const bottom = bottomLine.bottom;
const height = bottom - top;
const leftIndent = depth * indentWidth;
const left = leftEdge + leftIndent;
const left = leftIndent;
const mainCursor = state.selection.main.head;
const cursorActive =
!cursorFound &&
Expand Down Expand Up @@ -170,6 +198,7 @@ export const codeStructureView = (option: "full" | "simple") =>
blocks.push(
new VisualBlock(
bodyPullBack,
width,
parentPositions,
bodyPositions
)
Expand All @@ -191,15 +220,16 @@ export const codeStructureView = (option: "full" | "simple") =>
},
});
}
return { blocks: blocks.reverse() };
return new Measure(width, left, blocks.reverse());
}

drawBlocks({ blocks }: Measure) {
const blocksChanged =
blocks.length !== this.blocks.length ||
blocks.some((b, i) => !b.eq(this.blocks[i]));
if (blocksChanged) {
this.blocks = blocks;
drawBlocks(measure: Measure) {
if (!measure.eq(this.lastMeasure)) {
const { blocks, left, width } = measure;
this.lastMeasure = measure;

this.overlayLayer.style.width = width + "px";
this.overlayLayer.style.left = left + "px";

// Should be able to adjust old elements here if it's a performance win.
this.overlayLayer.textContent = "";
Expand Down
11 changes: 8 additions & 3 deletions src/editor/codemirror/structure-highlighting/visual-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class Positions {
export class VisualBlock {
constructor(
readonly bodyPullBack: boolean,
readonly width: number,
readonly parent?: Positions,
readonly body?: Positions
) {}
Expand Down Expand Up @@ -73,7 +74,7 @@ export class VisualBlock {
parent.style.left = this.parent.left + "px";
parent.style.top = this.parent.top + "px";
parent.style.height = this.parent.height + "px";
parent.style.width = `calc(100% - ${this.parent.left}px)`;
parent.style.width = this.width - this.parent.left + "px";
}

// Optionally allows nested compound statements some breathing space
Expand All @@ -82,7 +83,7 @@ export class VisualBlock {
body.style.left = this.body.left - bodyPullBack + "px";
body.style.top = this.body.top + "px";
body.style.height = this.body.height + "px";
body.style.width = `calc(100% - ${this.body.left - bodyPullBack}px)`;
body.style.width = this.width - this.body.left + bodyPullBack + "px";
}

if (this.parent && parent && this.body && body && indent) {
Expand All @@ -95,7 +96,11 @@ export class VisualBlock {
}

eq(other: VisualBlock) {
return equals(this.body, other.body) && equals(this.parent, other.parent);
return (
equals(this.body, other.body) &&
equals(this.parent, other.parent) &&
this.width === other.width
);
}
}

Expand Down

0 comments on commit f2dd315

Please sign in to comment.