Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Editor highlighting experiment #3365

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ export function getMarkers(
return markers;
}

// TODO: This seems to be broken now. Only finding a small subset of the lines that should be highlighted.
export function focusGutterExtension(): Extension {
const markersFacet = Facet.define<
RangeSet<GutterMarker>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {

import { showGutterFacet } from "../fields.js";
import { extensionFromFacets } from "../utils.js";
import { focusGutterExtension } from "./focusGutterExtension.js";

export function gutterExtension(initialShowGutter: boolean) {
return [
Expand All @@ -19,7 +18,6 @@ export function gutterExtension(initialShowGutter: boolean) {
? [
highlightActiveLine(),
highlightActiveLineGutter(),
focusGutterExtension(),
lineNumbers(),
foldGutter(),
]
Expand Down
53 changes: 50 additions & 3 deletions packages/components/src/components/CodeEditor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { StateEffect, StateField } from "@codemirror/state";
import { Decoration, DecorationSet, EditorView } from "@codemirror/view";
import { forwardRef, ReactNode, useImperativeHandle } from "react";

import { SqLocation, SqProject, SqValuePath } from "@quri/squiggle-lang";
Expand All @@ -6,6 +8,38 @@
import { formatSquiggle } from "./formatSquiggleExtension.js";
import { useSquiggleEditorView } from "./useSquiggleEditorView.js";

const flashHighlight = StateEffect.define<{
from: number;
to: number;
} | null>();

const flashHighlightField = StateField.define<DecorationSet>({

Check failure on line 16 in packages/components/src/components/CodeEditor/index.tsx

View workflow job for this annotation

GitHub Actions / Build, test, lint

'flashHighlightField' is assigned a value but never used
create() {
return Decoration.none;
},
update(highlights, tr) {
highlights = highlights.map(tr.changes);
for (let e of tr.effects) {

Check failure on line 22 in packages/components/src/components/CodeEditor/index.tsx

View workflow job for this annotation

GitHub Actions / Build, test, lint

'e' is never reassigned. Use 'const' instead
if (e.is(flashHighlight)) {
if (e.value) {
highlights = highlights.update({
add: [
Decoration.mark({ class: "cm-flash-highlight" }).range(
e.value.from,
e.value.to
),
],
});
} else {
highlights = Decoration.none;
}
}
}
return highlights;
},
provide: (f) => EditorView.decorations.from(f),
});

export type CodeEditorProps = {
defaultValue: string;
onChange: (value: string) => void;
Expand Down Expand Up @@ -37,13 +71,18 @@
function CodeEditor(props, ref) {
const { view, ref: editorRef } = useSquiggleEditorView(props);

const scrollTo = (location: SqLocation, focus: boolean) => {
const scrollTo = (location: SqLocation, flash: boolean) => {

Check failure on line 74 in packages/components/src/components/CodeEditor/index.tsx

View workflow job for this annotation

GitHub Actions / Build, test, lint

'flash' is defined but never used. Allowed unused args must match /^_/u
if (!view) return;

view.dispatch({
selection: { anchor: location.start.offset, head: location.end.offset },
scrollIntoView: true,
effects: true

Check failure on line 79 in packages/components/src/components/CodeEditor/index.tsx

View workflow job for this annotation

GitHub Actions / Build, test, lint

Unexpected constant condition
? flashHighlight.of({
from: location.start.offset,
to: location.end.offset,
})
: undefined,
});
focus && view.focus();
};

useImperativeHandle(ref, () => ({
Expand All @@ -57,3 +96,11 @@
);
}
);

const style = document.createElement("style");
style.textContent = `
.cm-flash-highlight {
background-color: #ff9999;
}
`;
document.head.appendChild(style);
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
syntaxHighlighting,
} from "@codemirror/language";
import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
import { EditorState } from "@codemirror/state";
import { EditorState, StateField } from "@codemirror/state";
import {
Decoration,
DecorationSet,
drawSelection,
dropCursor,
EditorView,
Expand All @@ -32,7 +34,7 @@
useConfigureCodemirrorView,
} from "./codemirrorHooks.js";
import { errorsExtension } from "./errorsExtension.js";
import { showGutterFacet, useReactPropsField } from "./fields.js";
import { useReactPropsField } from "./fields.js";
import { formatSquiggleExtension } from "./formatSquiggleExtension.js";
import { gutterExtension } from "./gutter/gutterExtension.js";
import { CodeEditorProps } from "./index.js";
Expand All @@ -46,14 +48,34 @@
import { tooltipsExtension } from "./tooltips/index.js";
import { viewNodeExtension } from "./viewNodeExtension.js";

// eslint-disable-next-line @typescript-eslint/no-unused-vars
function debugExtension() {
// Print state or specific fields on changes.
return EditorView.updateListener.of(({ state }) => {
// eslint-disable-next-line no-console
console.log(state.facet(showGutterFacet));
});
}
const flashHighlightField = StateField.define<DecorationSet>({
create() {
return Decoration.none;
},
update(highlights, tr) {
console.log("Update flash highlight field", highlights, tr.effects);

Check warning on line 56 in packages/components/src/components/CodeEditor/useSquiggleEditorView.ts

View workflow job for this annotation

GitHub Actions / Build, test, lint

Unexpected console statement
highlights = highlights.map(tr.changes);
let newHighlight: DecorationSet | null = null;
for (let e of tr.effects) {

Check failure on line 59 in packages/components/src/components/CodeEditor/useSquiggleEditorView.ts

View workflow job for this annotation

GitHub Actions / Build, test, lint

'e' is never reassigned. Use 'const' instead
if (e.value && "from" in e.value && "to" in e.value) {
// Create a new highlight, replacing any existing one
newHighlight = Decoration.set([
Decoration.mark({ class: "cm-flash-highlight" }).range(
e.value.from,
e.value.to
),
]);
} else if (e.value === null) {
// Remove all highlights when e.value is null
newHighlight = Decoration.none;
}
}
// If a new highlight was created or all highlights were removed, use it
// Otherwise, keep the existing highlights
return newHighlight !== null ? newHighlight : highlights;
},
provide: (f) => EditorView.decorations.from(f),
});

export function useSquiggleEditorExtensions(
view: EditorView | undefined,
Expand Down Expand Up @@ -87,6 +109,7 @@
// uncomment for local debugging:
// debugExtension(),
highlightSpecialChars(),
flashHighlightField,
history(),
drawSelection(),
dropCursor(),
Expand Down Expand Up @@ -128,6 +151,7 @@
height: params.height,
}),
tooltipsExtension(),
flashHighlightField,
];

return [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export const ValueWithContextViewer: FC<Props> = ({
? "mb-2 px-0.5 py-1 focus:bg-indigo-50"
: "focus:bg-indigo-100"
)}
onFocus={(_) => {
onMouseEnter={(_) => {
scrollEditorToPath();
}}
onKeyDown={(event) => {
Expand Down
Loading