esolang/ui/code-editor/monaco-utils.ts
2022-02-16 21:20:44 +05:30

102 lines
3.6 KiB
TypeScript

import monaco from "monaco-editor";
import { DocumentEdit, DocumentRange } from "../../languages/types";
import { WorkerParseError } from "../../languages/worker-errors";
/** Type alias for an instance of Monaco editor */
export type EditorInstance = monaco.editor.IStandaloneCodeEditor;
/** Type alias for the Monaco global */
export type MonacoInstance = typeof monaco;
/** Type alias for Monaco mouse events */
export type MonacoMouseEvent = monaco.editor.IEditorMouseEvent;
/** Type alias for Monaco mouse-leave event */
export type MonacoMouseLeaveEvent = monaco.editor.IPartialEditorMouseEvent;
/** Type alias for Monaco decoration object */
export type MonacoDecoration = monaco.editor.IModelDeltaDecoration;
/** Create Monaco decoration range object for text highlighting */
export const createHighlightRange = (
monacoInstance: MonacoInstance,
highlights: DocumentRange
): MonacoDecoration => {
const location = get1IndexedLocation(highlights);
let { startLine, endLine, startCol, endCol } = location;
const range = new monacoInstance.Range(
startLine,
startCol == null ? 1 : startCol,
endLine == null ? startLine : endLine,
endCol == null ? Infinity : endCol
);
// const isWholeLine = startCol == null && endCol == null;
return { range, options: { inlineClassName: "code-highlight" } };
};
/** Create Monaco decoration range object from highlights */
export const createBreakpointRange = (
monacoInstance: MonacoInstance,
lineNum: number,
hint?: boolean
): MonacoDecoration => {
const range = new monacoInstance.Range(lineNum, 0, lineNum, 1000);
const className = "breakpoint-glyph " + (hint ? "hint" : "solid");
return { range, options: { glyphMarginClassName: className } };
};
/** Create Monaco syntax-error marker from message and document range */
export const createValidationMarker = (
monacoInstance: MonacoInstance,
error: WorkerParseError,
range: DocumentRange
): monaco.editor.IMarkerData => {
const location = get1IndexedLocation(range);
const { startLine, endLine, startCol, endCol } = location;
return {
startLineNumber: startLine,
endLineNumber: endLine == null ? startLine : endLine,
startColumn: startCol == null ? 1 : startCol,
endColumn: endCol == null ? Infinity : endCol,
severity: monacoInstance.MarkerSeverity.Error,
message: error.message,
source: error.name,
};
};
/**
* Convert a DocumentEdit instance to Monaco edit object format.
* @param edit DocumentEdit to convert to Monaco format
* @returns Instance of Monaco's edit object
*/
export const createMonacoDocEdit = (
edit: DocumentEdit
): monaco.editor.IIdentifiedSingleEditOperation => {
const location = get1IndexedLocation(edit.range);
const { startLine, endLine, startCol, endCol } = location;
return {
text: edit.text,
range: {
startLineNumber: startLine,
endLineNumber: endLine == null ? startLine : endLine,
startColumn: startCol == null ? 1 : startCol,
endColumn: endCol == null ? Infinity : endCol,
},
};
};
/**
* Convert a DocumentRange to use 1-indexed values. Used since language engines
* use 0-indexed ranges but Monaco requires 1-indexed ranges.
* @param range DocumentRange to convert to 1-indexed
* @returns DocumentRange that uses 1-indexed values
*/
const get1IndexedLocation = (range: DocumentRange): DocumentRange => {
return {
startLine: range.startLine + 1,
startCol: range.startCol == null ? 1 : range.startCol + 1,
endLine: range.endLine == null ? range.startLine + 1 : range.endLine + 1,
endCol: range.endCol == null ? Infinity : range.endCol + 1,
};
};