Skip to content

Commit d58ad27

Browse files
authored
Merge pull request #158 from ceifa/main
feat: add xml content type custom validation
2 parents 45bdf0a + 9a83c1e commit d58ad27

File tree

4 files changed

+106
-0
lines changed

4 files changed

+106
-0
lines changed

package-lock.json

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
"deserialize-error": "0.0.3",
9494
"dompurify": "^2.5.6",
9595
"fast-json-patch": "^3.1.1",
96+
"fast-xml-parser": "^5.2.3",
9697
"graphql": "^15.8.0",
9798
"har-validator": "^5.1.3",
9899
"http-encoding": "^2.0.1",

src/components/editor/monaco.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { defineMonacoThemes } from '../../styles';
66
import { delay } from '../../util/promise';
77
import { asError } from '../../util/error';
88
import { observable, runInAction } from 'mobx';
9+
import { setupXMLValidation } from './xml-validation';
910

1011
export type {
1112
MonacoTypes,
@@ -74,6 +75,8 @@ async function loadMonacoEditor(retries = 5): Promise<void> {
7475
},
7576
});
7677

78+
setupXMLValidation(monaco);
79+
7780
MonacoEditor = rmeModule.default;
7881
} catch (err) {
7982
console.log('Monaco load failed', asError(err).message);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import type * as MonacoTypes from 'monaco-editor'
2+
import { XMLValidator } from 'fast-xml-parser'
3+
4+
export function setupXMLValidation(monaco: typeof MonacoTypes) {
5+
const markerId = 'xml-validation'
6+
7+
function validate(model: MonacoTypes.editor.ITextModel) {
8+
const markers: MonacoTypes.editor.IMarkerData[] = []
9+
const text = model.getValue()
10+
11+
if (text.trim()) {
12+
const validationResult = XMLValidator.validate(text, {
13+
allowBooleanAttributes: true,
14+
})
15+
16+
if (validationResult !== true) {
17+
markers.push({
18+
severity: monaco.MarkerSeverity.Error,
19+
startLineNumber: validationResult.err.line,
20+
startColumn: validationResult.err.col,
21+
endLineNumber: validationResult.err.line,
22+
endColumn: model.getLineContent(validationResult.err.line).length + 1,
23+
message: validationResult.err.msg,
24+
})
25+
}
26+
}
27+
28+
monaco.editor.setModelMarkers(model, markerId, markers)
29+
}
30+
31+
const contentChangeListeners = new Map<MonacoTypes.editor.ITextModel, MonacoTypes.IDisposable>()
32+
function manageContentChangeListener(model: MonacoTypes.editor.ITextModel) {
33+
const isXml = model.getModeId() === 'xml'
34+
const listener = contentChangeListeners.get(model)
35+
36+
if (isXml && !listener) {
37+
contentChangeListeners.set(
38+
model,
39+
model.onDidChangeContent(() => validate(model))
40+
)
41+
validate(model)
42+
} else if (!isXml && listener) {
43+
listener.dispose()
44+
contentChangeListeners.delete(model)
45+
monaco.editor.setModelMarkers(model, markerId, [])
46+
}
47+
}
48+
49+
monaco.editor.onWillDisposeModel(model => {
50+
contentChangeListeners.delete(model)
51+
})
52+
monaco.editor.onDidChangeModelLanguage(({ model }) => {
53+
manageContentChangeListener(model)
54+
})
55+
monaco.editor.onDidCreateModel(model => {
56+
manageContentChangeListener(model)
57+
})
58+
}

0 commit comments

Comments
 (0)