feat: add xml linter to code mirror (#306)

This commit is contained in:
Kristin Aoki
2023-04-14 11:58:49 -04:00
committed by GitHub
parent 4b7b1c91ec
commit 1d2a4c212d
6 changed files with 93 additions and 12 deletions

View File

@@ -115,7 +115,7 @@ exports[`EditorFooter render snapshot: save failed. Show error message 1`] = `
show={true}
>
<FormattedMessage
defaultMessage="Error: Content save failed. Try again later."
defaultMessage="Error: Content save failed. Please check recent changes and try again later."
description="Error message displayed when content fails to save."
id="authoring.editorfooter.save.error"
/>

View File

@@ -4,7 +4,7 @@ const messages = defineMessages({
contentSaveFailed: {
id: 'authoring.editorfooter.save.error',
defaultMessage: 'Error: Content save failed. Try again later.',
defaultMessage: 'Error: Content save failed. Please check recent changes and try again later.',
description: 'Error message displayed when content fails to save.',
},
cancelButtonAriaLabel: {

View File

@@ -1,11 +1,12 @@
import React, { useEffect } from 'react';
import xmlChecker from 'xmlchecker';
import { basicSetup } from 'codemirror';
import { EditorState } from '@codemirror/state';
import { EditorView } from '@codemirror/view';
import { html } from '@codemirror/lang-html';
import { xml } from '@codemirror/lang-xml';
import { linter } from '@codemirror/lint';
import alphanumericMap from './constants';
import './index.scss';
@@ -27,6 +28,32 @@ export const cleanHTML = ({ initialText }) => {
return initialText.replace(translateRegex, translator);
};
export const syntaxChecker = ({ textArr, lang }) => {
const diagnostics = [];
if (lang === 'xml') {
const docString = textArr.join('\n');
const xmlDoc = `<?xml version="1.0" encoding="UTF-8"?> ${docString}`;
try {
xmlChecker.check(xmlDoc);
} catch (error) {
let errorStart = 0;
for (let i = 0; i < error.line - 1; i++) {
errorStart += textArr[i].length;
}
const errorLine = error.line;
const errorEnd = errorStart + textArr[errorLine - 1].length;
diagnostics.push({
from: errorStart,
to: errorEnd,
severity: 'error',
message: `${error.name}: ${error.message}`,
});
}
}
return diagnostics;
};
export const createCodeMirrorDomNode = ({
ref,
initialText,
@@ -38,7 +65,15 @@ export const createCodeMirrorDomNode = ({
const cleanText = cleanHTML({ initialText });
const newState = EditorState.create({
doc: cleanText,
extensions: [basicSetup, languageExtension, EditorView.lineWrapping],
extensions: [
basicSetup,
languageExtension,
EditorView.lineWrapping,
linter((view) => {
const textArr = view.docView.view.viewState.state.doc.text;
return syntaxChecker({ textArr, lang });
}),
],
});
const view = new EditorView({ state: newState, parent: ref.current });
// eslint-disable-next-line no-param-reassign

View File

@@ -114,6 +114,35 @@ describe('CodeEditor', () => {
});
});
});
describe('xmlSyntaxChecker', () => {
describe('lang equals html', () => {
it('returns empty array', () => {
const textArr = ['<problem>', '<p>', 'this is some text', '</p>', '</problem>'];
const diagnostics = hooks.syntaxChecker({ textArr, lang: 'html' });
expect(diagnostics).toEqual([]);
});
});
describe('lang equals xml', () => {
it('returns empty array', () => {
const textArr = ['<problem>', '<p>', 'this is some text', '</p>', '</problem>'];
const diagnostics = hooks.syntaxChecker({ textArr, lang: 'xml' });
expect(diagnostics).toEqual([]);
});
it('returns an array with error object', () => {
const textArr = ['<problem>', '<p>', '<p>', 'this is some text', '</p>', '</problem>'];
const expectedDiagnostics = hooks.syntaxChecker({ textArr, lang: 'xml' });
const diagnostics = [{
from: 9,
to: 12,
severity: 'error',
message: 'SyntaxError: Expected that start and end tag must be identical but "<" found.',
}];
expect(expectedDiagnostics).toEqual(diagnostics);
});
});
});
describe('Component', () => {
describe('Snapshots', () => {
const mockHideBtn = jest.fn().mockName('mockHidebtn');