fix: fix an issue of error position

This commit is contained in:
Peng Xiao 2018-12-13 15:04:09 +08:00
parent 37dae21bbd
commit 56dda31a03
6 changed files with 120 additions and 371 deletions

View file

@ -20,10 +20,9 @@ Load with ESM is added, but not yet tested.
## Development
* `git clone https://github.com/kpdecker/monaco-yaml`
* `git clone https://github.com/pengx17/monaco-yaml`
* `cd monaco-yaml`
* `yarn`
* `yarn watch`
* open `$/monaco-yaml/test/index.html` in your favorite browser.
A running example:
@ -40,4 +39,4 @@ Manually clone dependencies list below and update the project files accordingly:
- `src/yaml-ast-parser`: https://github.com/mulesoft-labs/yaml-ast-parser/tree/master/src
## License
[MIT](https://github.com/kpdecker/monaco-yaml/blob/master/LICENSE.md)
[MIT](https://github.com/pengx17/monaco-yaml/blob/master/LICENSE.md)

View file

@ -17,10 +17,10 @@
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/kpdecker/monaco-yaml"
"url": "https://github.com/pengx17/monaco-yaml"
},
"bugs": {
"url": "https://github.com/kpdecker/monaco-yaml/issues"
"url": "https://github.com/pengx17/monaco-yaml/issues"
},
"devDependencies": {
"@types/chai": "^4.1.4",
@ -28,7 +28,7 @@
"@types/node": "^10.9.3",
"js-yaml": "^3.12.0",
"jsonc-parser": "^2.0.2",
"monaco-editor-core": "0.15.0",
"monaco-editor-core": "0.15.5",
"monaco-languages": "1.6.0",
"monaco-plugin-helpers": "^1.0.2",
"requirejs": "^2.3.5",

View file

@ -987,307 +987,3 @@ function validate(node: ASTNode, schema: JSONSchema, validationResult: Validatio
}
}
}
export function parse(textDocument: TextDocument, config?: JSONDocumentConfig): JSONDocument {
let problems: Diagnostic[] = [];
let lastProblemOffset = -1;
let text = textDocument.getText();
let scanner = Json.createScanner(text, false);
let commentRanges: Range[] = config && config.collectComments ? [] : void 0;
function _scanNext(): Json.SyntaxKind {
while (true) {
let token = scanner.scan();
_checkScanError();
switch (token) {
case Json.SyntaxKind.LineCommentTrivia:
case Json.SyntaxKind.BlockCommentTrivia:
if (Array.isArray(commentRanges)) {
commentRanges.push(Range.create(textDocument.positionAt(scanner.getTokenOffset()), textDocument.positionAt(scanner.getTokenOffset() + scanner.getTokenLength())));
}
break;
case Json.SyntaxKind.Trivia:
case Json.SyntaxKind.LineBreakTrivia:
break;
default:
return token;
}
}
}
function _accept(token: Json.SyntaxKind): boolean {
if (scanner.getToken() === token) {
_scanNext();
return true;
}
return false;
}
function _errorAtRange<T extends ASTNode>(message: string, code: ErrorCode, startOffset: number, endOffset: number, severity: DiagnosticSeverity = DiagnosticSeverity.Error): void {
if (problems.length === 0 || startOffset !== lastProblemOffset) {
let range = Range.create(textDocument.positionAt(startOffset), textDocument.positionAt(endOffset));
problems.push(Diagnostic.create(range, message, severity, code, textDocument.languageId));
lastProblemOffset = startOffset;
}
}
function _error<T extends ASTNodeImpl>(message: string, code: ErrorCode, node: T = null, skipUntilAfter: Json.SyntaxKind[] = [], skipUntil: Json.SyntaxKind[] = []): T {
let start = scanner.getTokenOffset();
let end = scanner.getTokenOffset() + scanner.getTokenLength();
if (start === end && start > 0) {
start--;
while (start > 0 && /\s/.test(text.charAt(start))) {
start--;
}
end = start + 1;
}
_errorAtRange(message, code, start, end);
if (node) {
_finalize(node, false);
}
if (skipUntilAfter.length + skipUntil.length > 0) {
let token = scanner.getToken();
while (token !== Json.SyntaxKind.EOF) {
if (skipUntilAfter.indexOf(token) !== -1) {
_scanNext();
break;
} else if (skipUntil.indexOf(token) !== -1) {
break;
}
token = _scanNext();
}
}
return node;
}
function _checkScanError(): boolean {
switch (scanner.getTokenError()) {
case Json.ScanError.InvalidUnicode:
_error(localize('InvalidUnicode', 'Invalid unicode sequence in string.'), ErrorCode.InvalidUnicode);
return true;
case Json.ScanError.InvalidEscapeCharacter:
_error(localize('InvalidEscapeCharacter', 'Invalid escape character in string.'), ErrorCode.InvalidEscapeCharacter);
return true;
case Json.ScanError.UnexpectedEndOfNumber:
_error(localize('UnexpectedEndOfNumber', 'Unexpected end of number.'), ErrorCode.UnexpectedEndOfNumber);
return true;
case Json.ScanError.UnexpectedEndOfComment:
_error(localize('UnexpectedEndOfComment', 'Unexpected end of comment.'), ErrorCode.UnexpectedEndOfComment);
return true;
case Json.ScanError.UnexpectedEndOfString:
_error(localize('UnexpectedEndOfString', 'Unexpected end of string.'), ErrorCode.UnexpectedEndOfString);
return true;
case Json.ScanError.InvalidCharacter:
_error(localize('InvalidCharacter', 'Invalid characters in string. Control characters must be escaped.'), ErrorCode.InvalidCharacter);
return true;
}
return false;
}
function _finalize<T extends ASTNodeImpl>(node: T, scanNext: boolean): T {
node.length = scanner.getTokenOffset() + scanner.getTokenLength() - node.offset;
if (scanNext) {
_scanNext();
}
return node;
}
function _parseArray(parent: ASTNode): ArrayASTNode {
if (scanner.getToken() !== Json.SyntaxKind.OpenBracketToken) {
return null;
}
let node = new ArrayASTNodeImpl(parent, scanner.getTokenOffset());
_scanNext(); // consume OpenBracketToken
let count = 0;
let needsComma = false;
while (scanner.getToken() !== Json.SyntaxKind.CloseBracketToken && scanner.getToken() !== Json.SyntaxKind.EOF) {
if (scanner.getToken() === Json.SyntaxKind.CommaToken) {
if (!needsComma) {
_error(localize('ValueExpected', 'Value expected'), ErrorCode.ValueExpected);
}
let commaOffset = scanner.getTokenOffset();
_scanNext(); // consume comma
if (scanner.getToken() === Json.SyntaxKind.CloseBracketToken) {
if (needsComma) {
_errorAtRange(localize('TrailingComma', 'Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1);
}
continue;
}
} else if (needsComma) {
_error(localize('ExpectedComma', 'Expected comma'), ErrorCode.CommaExpected);
}
let item = _parseValue(node, count++);
if (!item) {
_error(localize('PropertyExpected', 'Value expected'), ErrorCode.ValueExpected, null, [], [Json.SyntaxKind.CloseBracketToken, Json.SyntaxKind.CommaToken]);
} else {
node.items.push(item);
}
needsComma = true;
}
if (scanner.getToken() !== Json.SyntaxKind.CloseBracketToken) {
return _error(localize('ExpectedCloseBracket', 'Expected comma or closing bracket'), ErrorCode.CommaOrCloseBacketExpected, node);
}
return _finalize(node, true);
}
function _parseProperty(parent: ObjectASTNode, keysSeen: { [key: string]: (PropertyASTNode | boolean) }): PropertyASTNode {
let node = new PropertyASTNodeImpl(parent, scanner.getTokenOffset());
let key = _parseString(node);
if (!key) {
if (scanner.getToken() === Json.SyntaxKind.Unknown) {
// give a more helpful error message
_error(localize('DoubleQuotesExpected', 'Property keys must be doublequoted'), ErrorCode.Undefined);
let keyNode = new StringASTNodeImpl(node, scanner.getTokenOffset(), scanner.getTokenLength());
keyNode.value = scanner.getTokenValue();
key = keyNode;
_scanNext(); // consume Unknown
} else {
return null;
}
}
node.keyNode = key;
let seen = keysSeen[key.value];
if (seen) {
_errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), ErrorCode.DuplicateKey, node.keyNode.offset, node.keyNode.offset + node.keyNode.length, DiagnosticSeverity.Warning);
if (typeof seen === 'object') {
_errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), ErrorCode.DuplicateKey, seen.keyNode.offset, seen.keyNode.offset + seen.keyNode.length, DiagnosticSeverity.Warning);
}
keysSeen[key.value] = true; // if the same key is duplicate again, avoid duplicate error reporting
} else {
keysSeen[key.value] = node;
}
if (scanner.getToken() === Json.SyntaxKind.ColonToken) {
node.colonOffset = scanner.getTokenOffset();
_scanNext(); // consume ColonToken
} else {
_error(localize('ColonExpected', 'Colon expected'), ErrorCode.ColonExpected);
if (scanner.getToken() === Json.SyntaxKind.StringLiteral && textDocument.positionAt(key.offset + key.length).line < textDocument.positionAt(scanner.getTokenOffset()).line) {
node.length = key.length;
return node;
}
}
let value = _parseValue(node, key.value);
if (!value) {
return _error(localize('ValueExpected', 'Value expected'), ErrorCode.ValueExpected, node, [], [Json.SyntaxKind.CloseBraceToken, Json.SyntaxKind.CommaToken]);
}
node.valueNode = value;
node.length = value.offset + value.length - node.offset;
return node;
}
function _parseObject(parent: ASTNode): ObjectASTNode {
if (scanner.getToken() !== Json.SyntaxKind.OpenBraceToken) {
return null;
}
let node = new ObjectASTNodeImpl(parent, scanner.getTokenOffset());
let keysSeen: any = Object.create(null);
_scanNext(); // consume OpenBraceToken
let needsComma = false;
while (scanner.getToken() !== Json.SyntaxKind.CloseBraceToken && scanner.getToken() !== Json.SyntaxKind.EOF) {
if (scanner.getToken() === Json.SyntaxKind.CommaToken) {
if (!needsComma) {
_error(localize('PropertyExpected', 'Property expected'), ErrorCode.PropertyExpected);
}
let commaOffset = scanner.getTokenOffset();
_scanNext(); // consume comma
if (scanner.getToken() === Json.SyntaxKind.CloseBraceToken) {
if (needsComma) {
_errorAtRange(localize('TrailingComma', 'Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1);
}
continue;
}
} else if (needsComma) {
_error(localize('ExpectedComma', 'Expected comma'), ErrorCode.CommaExpected);
}
let property = _parseProperty(node, keysSeen);
if (!property) {
_error(localize('PropertyExpected', 'Property expected'), ErrorCode.PropertyExpected, null, [], [Json.SyntaxKind.CloseBraceToken, Json.SyntaxKind.CommaToken]);
} else {
node.properties.push(property);
}
needsComma = true;
}
if (scanner.getToken() !== Json.SyntaxKind.CloseBraceToken) {
return _error(localize('ExpectedCloseBrace', 'Expected comma or closing brace'), ErrorCode.CommaOrCloseBraceExpected, node);
}
return _finalize(node, true);
}
function _parseString(parent: ASTNode): StringASTNode {
if (scanner.getToken() !== Json.SyntaxKind.StringLiteral) {
return null;
}
let node = new StringASTNodeImpl(parent, scanner.getTokenOffset());
node.value = scanner.getTokenValue();
return _finalize(node, true);
}
function _parseNumber(parent: ASTNode): NumberASTNode {
if (scanner.getToken() !== Json.SyntaxKind.NumericLiteral) {
return null;
}
let node = new NumberASTNodeImpl(parent, scanner.getTokenOffset());
if (scanner.getTokenError() === Json.ScanError.None) {
let tokenValue = scanner.getTokenValue();
try {
let numberValue = JSON.parse(tokenValue);
if (!isNumber(numberValue)) {
return _error(localize('InvalidNumberFormat', 'Invalid number format.'), ErrorCode.Undefined, node);
}
node.value = numberValue;
} catch (e) {
return _error(localize('InvalidNumberFormat', 'Invalid number format.'), ErrorCode.Undefined, node);
}
node.isInteger = tokenValue.indexOf('.') === -1;
}
return _finalize(node, true);
}
function _parseLiteral(parent: ASTNode): ASTNode {
let node: ASTNodeImpl;
switch (scanner.getToken()) {
case Json.SyntaxKind.NullKeyword:
return _finalize(new NullASTNodeImpl(parent, scanner.getTokenOffset()), true);
case Json.SyntaxKind.TrueKeyword:
return _finalize(new BooleanASTNodeImpl(parent, true, scanner.getTokenOffset()), true);
case Json.SyntaxKind.FalseKeyword:
return _finalize(new BooleanASTNodeImpl(parent, false, scanner.getTokenOffset()), true);
default:
return null;
}
}
function _parseValue(parent: ASTNode, name: Json.Segment): ASTNode {
return _parseArray(parent) || _parseObject(parent) || _parseString(parent) || _parseNumber(parent) || _parseLiteral(parent);
}
let _root = null;
let token = _scanNext();
if (token !== Json.SyntaxKind.EOF) {
_root = _parseValue(null, null);
if (!_root) {
_error(localize('Invalid symbol', 'Expected a JSON object, array or literal.'), ErrorCode.Undefined);
} else if (scanner.getToken() !== Json.SyntaxKind.EOF) {
_error(localize('End of file expected', 'End of file expected.'), ErrorCode.Undefined);
}
}
return new JSONDocument(_root, problems, commentRanges);
}

View file

@ -131,7 +131,7 @@ function recursivelyBuildAst(parent: ASTNode, node: Yaml.YAMLNode): ASTNode {
}
function convertError(e: Yaml.Error) {
return { message: `${e.reason}`, location: { start: e.mark.position, end: e.mark.position + e.mark.column, code: ErrorCode.Undefined } }
return { message: `${e.reason}`, location: { start: e.mark.position - e.mark.column, end: e.mark.position, code: ErrorCode.Undefined } }
}
function createJSONDocument(yamlDoc: Yaml.YAMLNode, startPositions: number[], text: string) {
@ -169,7 +169,7 @@ export function parse(text: string, customTags = []): YAMLDocument {
const startPositions = getLineStartPositions(text)
// This is documented to return a YAMLNode even though the
// typing only returns a YAMLDocument
const yamlDocs = []
const yamlDocs: Yaml.YAMLNode[] = []
let schemaWithAdditionalTags = Schema.create(customTags.map((tag) => {
const typeInfo = tag.split(' ');

View file

@ -2,51 +2,52 @@
<html>
<head>
<meta http-equiv="X-UA-Compatible"
content="IE=edge" />
<meta http-equiv="Content-Type"
content="text/html;charset=utf-8" />
<link rel="stylesheet"
data-name="vs/editor/editor.main"
href="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.css">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<link rel="stylesheet" data-name="vs/editor/editor.main" href="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.css">
</head>
<body>
<h2>Monaco Editor YAML test page</h2>
<code id="path"></code>
<div id="container"
style="width:800px;height:600px;border:1px solid grey"></div>
<h2>Monaco Editor YAML test page</h2>
<code id="path"></code>
<div id="container" style="width:800px;height:600px;border:1px solid grey"></div>
<script>
// Loading basic-languages to get the YAML language definition
var paths = {
'vs/basic-languages': '../node_modules/monaco-languages/release/dev',
'vs/language/yaml': '../release/dev',
'vs': '../node_modules/monaco-editor-core/dev/vs'
}
if (document.location.protocol === 'http:') {
// Add support for running local http server
let testIndex = document.location.pathname.indexOf('/test/');
if (testIndex !== -1) {
let prefix = document.location.pathname.substr(0, testIndex);
paths['vs/language/yaml'] = prefix + '/release/dev';
}
}
var require = {
paths: paths
};
</script>
<script src="../node_modules/monaco-editor-core/dev/vs/loader.js"></script>
<script src="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.nls.js"></script>
<script src="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.js"></script>
<style>
.x-highlight-range {
background-color: lightblue;
}
</style>
<script>
require([
'vs/basic-languages/monaco.contribution',
'vs/language/yaml/monaco.contribution'
], function () {
const yaml = `apiVersion: apps/v1
<script>
// Loading basic-languages to get the YAML language definition
var paths = {
'vs/basic-languages': '../node_modules/monaco-languages/release/dev',
'vs/language/yaml': '../release/dev',
'vs': '../node_modules/monaco-editor-core/dev/vs'
}
if (document.location.protocol === 'http:') {
// Add support for running local http server
let testIndex = document.location.pathname.indexOf('/test/');
if (testIndex !== -1) {
let prefix = document.location.pathname.substr(0, testIndex);
paths['vs/language/yaml'] = prefix + '/release/dev';
}
}
var require = {
paths: paths
};
</script>
<script src="../node_modules/monaco-editor-core/dev/vs/loader.js"></script>
<script src="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.nls.js"></script>
<script src="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.js"></script>
<script>
require([
'vs/basic-languages/monaco.contribution',
'vs/language/yaml/monaco.contribution'
], function () {
const yaml = `apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
@ -67,23 +68,76 @@ spec:
- name: nginx
image: nginx:alpine`;
var editor = monaco.editor.create(document.getElementById('container'), {
value: yaml,
language: 'yaml'
});
var editor = monaco.editor.create(document.getElementById('container'), {
value: yaml,
language: 'yaml'
});
monaco.languages.yaml.yamlDefaults.setDiagnosticsOptions({
enableSchemaRequest: true,
validate: true,
schemas: [
{
uri: 'https://raw.githubusercontent.com/garethr/kubernetes-json-schema/master/master/deployment.json',
fileMatch: ['*'],
},
],
});
});
</script>
monaco.languages.yaml.yamlDefaults.setDiagnosticsOptions({
enableSchemaRequest: true,
validate: true,
schemas: [
{
uri: 'https://raw.githubusercontent.com/garethr/kubernetes-json-schema/master/master/deployment.json',
fileMatch: ['*'],
},
],
});
require(["vs/editor/contrib/quickOpen/quickOpen"], async quickOpen => {
const NEVER_CANCEL_TOKEN = {
isCancellationRequested: false,
onCancellationRequested: () => Event.NONE,
};
let oldDecorations = [];
async function _getSymbolForPosition(model, position) {
const symbols = await quickOpen.getDocumentSymbols(
model,
false,
NEVER_CANCEL_TOKEN,
);
function _recur(symbol) {
let target = symbol;
if (symbol && symbol.children && symbol.children.length) {
target = _recur(symbol.children.find(child => child.range.containsPosition(position))) || symbol;
}
return target;
}
return _recur({ children: symbols });
}
editor.onDidChangeCursorSelection(async ({ selection }) => {
const model = editor.getModel();
const position = selection.getPosition();
const symbol = await _getSymbolForPosition(model, position);
if (symbol) {
const decoration = {
range: symbol.range,
options: {
isWholeLine: false,
className: 'x-highlight-range',
},
};
oldDecorations = editor.deltaDecorations(
oldDecorations,
decoration ? [decoration] : [],
)
console.log(`${symbol.name}: ${symbol.range}`);
}
});
});
});
</script>
</body>

View file

@ -140,10 +140,10 @@ minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
monaco-editor-core@0.15.0:
version "0.15.0"
resolved "https://registry.yarnpkg.com/monaco-editor-core/-/monaco-editor-core-0.15.0.tgz#1ad5f130ed8efae9d9ed85d8a58a55067e14d983"
integrity sha512-s1zuo+p6Gl6IC4WJP6HBkr4pWULm+HdFfacB8vOFPQLLi2oJseO20UuSkxYuZTUJQIjvhNrQfLNAmPKLZaf7tg==
monaco-editor-core@0.15.5:
version "0.15.5"
resolved "https://registry.yarnpkg.com/monaco-editor-core/-/monaco-editor-core-0.15.5.tgz#145f1953a8e319282d92502252d68ef3486b6875"
integrity sha512-kM3KHRjj16cFdK5Z0EppKUu793JVMpsEesBSWlqdgrxcmjyDMXV6xK0oatPcAYp3eOfbbyjPhruxDXj85FKyIg==
monaco-languages@1.6.0:
version "1.6.0"