Add breakpoints to editor UI
This commit is contained in:
115
ui/code-editor/use-editor-breakpoints.ts
Normal file
115
ui/code-editor/use-editor-breakpoints.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import React from "react";
|
||||
import {
|
||||
createBreakpointRange,
|
||||
EditorInstance,
|
||||
MonacoInstance,
|
||||
MonacoMouseEvent,
|
||||
MonacoMouseLeaveEvent,
|
||||
} from "./monaco-utils";
|
||||
|
||||
type BreakpointsMap = { [k: number]: string[] };
|
||||
type HoverBreakpoint = {
|
||||
lineNum: number;
|
||||
decorRanges: string[];
|
||||
};
|
||||
|
||||
type Args = {
|
||||
editor: EditorInstance | null;
|
||||
monaco: MonacoInstance | null;
|
||||
onUpdateBreakpoints: (newBreakpoints: number[]) => void;
|
||||
};
|
||||
|
||||
export const useEditorBreakpoints = ({
|
||||
editor,
|
||||
monaco,
|
||||
onUpdateBreakpoints,
|
||||
}: Args) => {
|
||||
const breakpoints = React.useRef<BreakpointsMap>({});
|
||||
const hoverBreakpoint = React.useRef<HoverBreakpoint | null>(null);
|
||||
|
||||
// Mouse clicks -> add or remove breakpoint
|
||||
React.useEffect(() => {
|
||||
if (!editor || !monaco) return;
|
||||
const disposer = editor.onMouseDown((e: MonacoMouseEvent) => {
|
||||
// Check if click is in glyph display channel
|
||||
const glyphMarginType = monaco.editor.MouseTargetType.GUTTER_GLYPH_MARGIN;
|
||||
const isGlyphMargin = e.target.type === glyphMarginType;
|
||||
if (!isGlyphMargin) return;
|
||||
|
||||
const lineNum = e.target.position!.lineNumber;
|
||||
const existingRange = breakpoints.current[lineNum];
|
||||
if (existingRange) {
|
||||
// Already has breakpoint - remove it
|
||||
editor.deltaDecorations(existingRange, []);
|
||||
delete breakpoints.current[lineNum];
|
||||
} else {
|
||||
// Add breakpoint to this line
|
||||
const range = createBreakpointRange(monaco, lineNum);
|
||||
const newRangeStr = editor.deltaDecorations([], [range]);
|
||||
breakpoints.current[lineNum] = newRangeStr;
|
||||
}
|
||||
|
||||
// Update breakpoints to parent
|
||||
const bpLineNumStrs = Object.keys(breakpoints.current);
|
||||
const bpLineNums = bpLineNumStrs.map((numStr) => parseInt(numStr, 10));
|
||||
onUpdateBreakpoints(bpLineNums);
|
||||
});
|
||||
return () => disposer.dispose();
|
||||
}, [editor, monaco]);
|
||||
|
||||
// Mouse enter -> show semi-transparent breakpoint icon
|
||||
React.useEffect(() => {
|
||||
if (!editor || !monaco) return;
|
||||
const disposer = editor.onMouseMove((e: MonacoMouseEvent) => {
|
||||
// Check if click is in glyph display channel
|
||||
const glyphMarginType = monaco.editor.MouseTargetType.GUTTER_GLYPH_MARGIN;
|
||||
const isGlyphMargin = e.target.type === glyphMarginType;
|
||||
|
||||
// If mouse goes out of glyph channel...
|
||||
if (!isGlyphMargin) {
|
||||
if (hoverBreakpoint.current) {
|
||||
editor.deltaDecorations(hoverBreakpoint.current.decorRanges, []);
|
||||
hoverBreakpoint.current = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if hover is in already hinted line
|
||||
const hoverLineNum = e.target.position!.lineNumber;
|
||||
if (hoverLineNum === hoverBreakpoint.current?.lineNum) return;
|
||||
|
||||
// Add hover decoration to newly hovered line
|
||||
const range = createBreakpointRange(monaco, hoverLineNum, true);
|
||||
const newHoverRangeStr = editor.deltaDecorations([], [range]);
|
||||
|
||||
// Remove existing breakpoint hover
|
||||
if (hoverBreakpoint.current)
|
||||
editor.deltaDecorations(hoverBreakpoint.current.decorRanges, []);
|
||||
|
||||
// Set hover breakpoint state to new one
|
||||
hoverBreakpoint.current = {
|
||||
lineNum: hoverLineNum,
|
||||
decorRanges: newHoverRangeStr,
|
||||
};
|
||||
|
||||
// If breakpoint already on line, ignore
|
||||
const lineNum = e.target.position!.lineNumber;
|
||||
const existingRange = breakpoints.current[lineNum];
|
||||
if (existingRange) return;
|
||||
});
|
||||
return () => disposer.dispose();
|
||||
}, [editor, monaco]);
|
||||
|
||||
// Mouse leaves editor -> remove hover breakpoint hint
|
||||
React.useEffect(() => {
|
||||
if (!editor) return;
|
||||
const disposer = editor.onMouseLeave((e: MonacoMouseLeaveEvent) => {
|
||||
if (!hoverBreakpoint.current) return;
|
||||
editor.deltaDecorations(hoverBreakpoint.current.decorRanges, []);
|
||||
hoverBreakpoint.current = null;
|
||||
});
|
||||
return () => disposer.dispose();
|
||||
}, [editor]);
|
||||
|
||||
return breakpoints;
|
||||
};
|
||||
Reference in New Issue
Block a user