Skip to content

Commit

Permalink
Prevent infinite loop
Browse files Browse the repository at this point in the history
  • Loading branch information
mgmeyers committed Feb 19, 2024
1 parent 5d4fc7e commit c677dde
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 45 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"devDependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@codemirror/language": "https://github.com/lishid/cm-language",
"@trivago/prettier-plugin-sort-imports": "4.2.0",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "^5.2.0",
Expand Down
107 changes: 62 additions & 45 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { RangeSetBuilder, StateEffect, StateField } from '@codemirror/state';
import { ensureSyntaxTree, tokenClassNodeProp } from '@codemirror/language';
import {
EditorState,
RangeSetBuilder,
StateEffect,
StateField,
} from '@codemirror/state';
import {
Decoration,
DecorationSet,
Expand Down Expand Up @@ -76,55 +82,65 @@ export const calloutsConfigField = StateField.define<CalloutConfig[]>({
},
});

export function buildCalloutDecos(view: EditorView) {
const config = view.state.field(calloutsConfigField);
export function buildCalloutDecos(view: EditorView, state: EditorState) {
const config = state.field(calloutsConfigField);

if (!config.length) return Decoration.none;
if (!config.length || !view.visibleRanges.length) return Decoration.none;

const builder = new RangeSetBuilder<Decoration>();
const seen: Set<number> = new Set();

const lastRange = view.visibleRanges[view.visibleRanges.length - 1];
const tree = ensureSyntaxTree(state, lastRange.to, 50);
const { doc } = state;

for (const { from, to } of view.visibleRanges) {
for (let pos = from; pos <= to; ) {
const line = view.state.doc.lineAt(pos);

for (const callout of config) {
const match = line.text.match(callout.re);

if (match) {
if (seen.has(line.from)) break;
seen.add(line.from);

const labelPos = line.from + match[1].length;

// Set the line class and callout color
builder.add(line.from, line.from, calloutDecoration(callout.color));

// Decorate the callout marker
builder.add(
labelPos,
labelPos + callout.char.length,
Decoration.replace({
widget: new CalloutMarker(callout.char, callout.icon),
})
);

// Add the callout background element
builder.add(
line.to,
line.to,
Decoration.widget({
widget: new CalloutBackground(),
side: 1,
})
);

break;
tree.iterate({
from,
to,
enter({ type, from, to }): false | void {
const prop = type.prop(tokenClassNodeProp);
if (prop && /formatting-list/.test(prop)) {
const { from: lineFrom, text, number } = doc.lineAt(from);

if (seen.has(number)) return;

for (const callout of config) {
const match = text.match(callout.re);
if (match) {
seen.add(number);

const labelPos = lineFrom + match[1].length;

// Set the line class and callout color
builder.add(
lineFrom,
lineFrom,
calloutDecoration(callout.color)
);

// Add the callout background element
builder.add(
lineFrom,
lineFrom,
Decoration.widget({ widget: new CalloutBackground(), side: -1 })
);

// Decorate the callout marker
builder.add(
labelPos,
labelPos + callout.char.length,
Decoration.replace({
widget: new CalloutMarker(callout.char, callout.icon),
})
);

break;
}
}
}
}

pos = line.to + 1;
}
},
});
}

return builder.finish();
Expand All @@ -135,7 +151,7 @@ export const calloutExtension = ViewPlugin.fromClass(
decorations: DecorationSet;

constructor(view: EditorView) {
this.decorations = buildCalloutDecos(view);
this.decorations = buildCalloutDecos(view, view.state);
}

update(update: ViewUpdate) {
Expand All @@ -146,7 +162,8 @@ export const calloutExtension = ViewPlugin.fromClass(
tr.effects.some((e) => e.is(setConfig))
)
) {
this.decorations = buildCalloutDecos(update.view);
console.log('build decos');
this.decorations = buildCalloutDecos(update.view, update.state);
}
}
},
Expand Down
30 changes: 30 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"

"@codemirror/language@https://github.com/lishid/cm-language":
version "6.9.2"
resolved "https://github.com/lishid/cm-language#cc6a2cc30288db6be3f879ddf0e3ef64f14ed6ab"
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@lezer/common" "^1.1.0"
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
style-mod "^4.0.0"

"@codemirror/state@^6.0.0":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.0.1.tgz#a1994f14c49e2f77cb9e26aef35f63a8b3707c6c"
Expand Down Expand Up @@ -197,6 +208,25 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"

"@lezer/common@^1.0.0", "@lezer/common@^1.1.0":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.2.1.tgz#198b278b7869668e1bebbe687586e12a42731049"
integrity sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==

"@lezer/highlight@^1.0.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@lezer/highlight/-/highlight-1.2.0.tgz#e5898c3644208b4b589084089dceeea2966f7780"
integrity sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==
dependencies:
"@lezer/common" "^1.0.0"

"@lezer/lr@^1.0.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-1.4.0.tgz#ed52a75dbbfbb0d1eb63710ea84c35ee647cb67e"
integrity sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==
dependencies:
"@lezer/common" "^1.0.0"

"@nodelib/[email protected]":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
Expand Down

0 comments on commit c677dde

Please sign in to comment.