Showing inline errors in codemirror
Problem
Our website has hundreds of different pages, and many of them have a split-page layout:
- Left side is a code editor (we use CodeMirror for that)
- Right side is some execution result (e.g., SQL table output, Graphviz/Mermaid graph, regex/JQ/JSONPath result, etc.)
When an issue occurs on the right side, it's critical to point the user to the exact location in the code editor so they can fix it. For example, this is bad experience - we just show an error that underlying engine throws.
Solution
There are 2 possible solutions:
CodeMirror linter with matching rules to the execution engine
For example, this works well for us on the Graphviz Playground, where we have a Graphviz linter + Graphviz rendering. Independent execution usually matches errors, so it feels like when render fails, we show the correct error.
Here is how it's done in the Graphviz code editor (CodeMirror):
const syntaxLinter = linter((view) => {
let diagnostics: Diagnostic[] = [];
let graphtype: string | null = null;
...
return diagnostics;
});
let state = EditorState.create({
doc: defaultValue,
extensions: [
basicSetup,
...
syntaxLinter,
theme,
]
});
Manually show execution engine errors in CodeMirror
We just started this approach with our DDL to DBML/grpahviz conversion page.
We are using DBML-core library to parse DDL, it throws an error with line number and column number when parsing failed. We are catching this error and converting that to a codemirror diagnostics object.
Construct correct codemirror offset for diagnostics error
First we convert the error to our internal type.
try {
databaseObj = Parser.parse(source, 'mysql');
} catch (e) {
if (e instanceof CompilerError) {
const compilerError = e as CompilerError;
const diag: CompilerDiagnostic = compilerError.diags[0];
const editorError: EditorError = {
name: diag.type || 'Error',
message: diag.message.replace(/\\n/g, '\n'),
location: {
start: diag.location.start,
end: diag.location.end
}
}
setEditorError(editorError);
}
}
then we convert that into CodeMirror diagnostics object
let diagnostics = [
{
from: startPosition,
to: endPosition,
severity: "error" as "error",
message: error.message
}
];
editorRef.current.view?.dispatch(
setDiagnostics(editorRef.current.state!, diagnostics)
);
The result looks like this
Now we plan to apply the same idea to other pages like SQL playground, MermaidJS playground, Regex tester, JQ playground etc.