274 lines
12 KiB
JavaScript
274 lines
12 KiB
JavaScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Red Hat, Inc. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
(function (factory) {
|
|
if (typeof module === "object" && typeof module.exports === "object") {
|
|
var v = factory(require, exports);
|
|
if (v !== undefined) module.exports = v;
|
|
}
|
|
else if (typeof define === "function" && define.amd) {
|
|
define(["require", "exports", "./jsonParser07", "yaml", "./yamlParser07", "vscode-json-languageservice", "./ast-converter", "../utils/arrUtils", "../utils/astUtils", "../utils/strings"], factory);
|
|
}
|
|
})(function (require, exports) {
|
|
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.yamlDocumentsCache = exports.YamlDocuments = exports.YAMLDocument = exports.SingleYAMLDocument = void 0;
|
|
const jsonParser07_1 = require("./jsonParser07");
|
|
const yaml_1 = require("yaml");
|
|
const yamlParser07_1 = require("./yamlParser07");
|
|
const vscode_json_languageservice_1 = require("vscode-json-languageservice");
|
|
const ast_converter_1 = require("./ast-converter");
|
|
const arrUtils_1 = require("../utils/arrUtils");
|
|
const astUtils_1 = require("../utils/astUtils");
|
|
const strings_1 = require("../utils/strings");
|
|
/**
|
|
* These documents are collected into a final YAMLDocument
|
|
* and passed to the `parseYAML` caller.
|
|
*/
|
|
class SingleYAMLDocument extends jsonParser07_1.JSONDocument {
|
|
constructor(lineCounter) {
|
|
super(null, []);
|
|
this.lineCounter = lineCounter;
|
|
}
|
|
/**
|
|
* Create a deep copy of this document
|
|
*/
|
|
clone() {
|
|
const copy = new SingleYAMLDocument(this.lineCounter);
|
|
copy.isKubernetes = this.isKubernetes;
|
|
copy.disableAdditionalProperties = this.disableAdditionalProperties;
|
|
copy.uri = this.uri;
|
|
copy.currentDocIndex = this.currentDocIndex;
|
|
copy._lineComments = this.lineComments.slice();
|
|
// this will re-create root node
|
|
copy.internalDocument = this._internalDocument.clone();
|
|
return copy;
|
|
}
|
|
collectLineComments() {
|
|
this._lineComments = [];
|
|
if (this._internalDocument.commentBefore) {
|
|
const comments = this._internalDocument.commentBefore.split('\n');
|
|
comments.forEach((comment) => this._lineComments.push(`#${comment}`));
|
|
}
|
|
(0, yaml_1.visit)(this.internalDocument, (_key, node) => {
|
|
if (node?.commentBefore) {
|
|
const comments = node?.commentBefore.split('\n');
|
|
comments.forEach((comment) => this._lineComments.push(`#${comment}`));
|
|
}
|
|
if (node?.comment) {
|
|
this._lineComments.push(`#${node.comment}`);
|
|
}
|
|
});
|
|
if (this._internalDocument.comment) {
|
|
this._lineComments.push(`#${this._internalDocument.comment}`);
|
|
}
|
|
}
|
|
/**
|
|
* Updates the internal AST tree of the object
|
|
* from the internal node. This is call whenever the
|
|
* internalDocument is set but also can be called to
|
|
* reflect any changes on the underlying document
|
|
* without setting the internalDocument explicitly.
|
|
*/
|
|
updateFromInternalDocument() {
|
|
this.root = (0, ast_converter_1.convertAST)(null, this._internalDocument.contents, this._internalDocument, this.lineCounter);
|
|
}
|
|
set internalDocument(document) {
|
|
this._internalDocument = document;
|
|
this.updateFromInternalDocument();
|
|
}
|
|
get internalDocument() {
|
|
return this._internalDocument;
|
|
}
|
|
get lineComments() {
|
|
if (!this._lineComments) {
|
|
this.collectLineComments();
|
|
}
|
|
return this._lineComments;
|
|
}
|
|
set lineComments(val) {
|
|
this._lineComments = val;
|
|
}
|
|
get errors() {
|
|
return this.internalDocument.errors.map(YAMLErrorToYamlDocDiagnostics);
|
|
}
|
|
get warnings() {
|
|
return this.internalDocument.warnings.map(YAMLErrorToYamlDocDiagnostics);
|
|
}
|
|
getNodeFromPosition(positionOffset, textBuffer, configuredIndentation) {
|
|
const position = textBuffer.getPosition(positionOffset);
|
|
const lineContent = textBuffer.getLineContent(position.line);
|
|
if (lineContent.trim().length === 0) {
|
|
return [this.findClosestNode(positionOffset, textBuffer, configuredIndentation), true];
|
|
}
|
|
const textAfterPosition = lineContent.substring(position.character);
|
|
const spacesAfterPositionMatch = textAfterPosition.match(/^([ ]+)\n?$/);
|
|
const areOnlySpacesAfterPosition = !!spacesAfterPositionMatch;
|
|
const countOfSpacesAfterPosition = spacesAfterPositionMatch?.[1].length;
|
|
let closestNode;
|
|
(0, yaml_1.visit)(this.internalDocument, (key, node) => {
|
|
if (!node) {
|
|
return;
|
|
}
|
|
const range = node.range;
|
|
if (!range) {
|
|
return;
|
|
}
|
|
const isNullNodeOnTheLine = () => areOnlySpacesAfterPosition &&
|
|
positionOffset + countOfSpacesAfterPosition === range[2] &&
|
|
(0, yaml_1.isScalar)(node) &&
|
|
node.value === null;
|
|
if ((range[0] <= positionOffset && range[1] >= positionOffset) || isNullNodeOnTheLine()) {
|
|
closestNode = node;
|
|
}
|
|
else {
|
|
return yaml_1.visit.SKIP;
|
|
}
|
|
});
|
|
return [closestNode, false];
|
|
}
|
|
findClosestNode(offset, textBuffer, configuredIndentation) {
|
|
let offsetDiff = this.internalDocument.range[2];
|
|
let maxOffset = this.internalDocument.range[0];
|
|
let closestNode;
|
|
(0, yaml_1.visit)(this.internalDocument, (key, node) => {
|
|
if (!node) {
|
|
return;
|
|
}
|
|
const range = node.range;
|
|
if (!range) {
|
|
return;
|
|
}
|
|
const diff = range[1] - offset;
|
|
if (maxOffset <= range[0] && diff <= 0 && Math.abs(diff) <= offsetDiff) {
|
|
offsetDiff = Math.abs(diff);
|
|
maxOffset = range[0];
|
|
closestNode = node;
|
|
}
|
|
});
|
|
const position = textBuffer.getPosition(offset);
|
|
const lineContent = textBuffer.getLineContent(position.line);
|
|
const indentation = (0, strings_1.getIndentation)(lineContent, position.character);
|
|
if ((0, yaml_1.isScalar)(closestNode) && closestNode.value === null) {
|
|
return closestNode;
|
|
}
|
|
if (indentation === position.character) {
|
|
closestNode = this.getProperParentByIndentation(indentation, closestNode, textBuffer, '', configuredIndentation);
|
|
}
|
|
return closestNode;
|
|
}
|
|
getProperParentByIndentation(indentation, node, textBuffer, currentLine, configuredIndentation, rootParent) {
|
|
if (!node) {
|
|
return this.internalDocument.contents;
|
|
}
|
|
configuredIndentation = !configuredIndentation ? 2 : configuredIndentation;
|
|
if ((0, yaml_1.isNode)(node) && node.range) {
|
|
const position = textBuffer.getPosition(node.range[0]);
|
|
const lineContent = textBuffer.getLineContent(position.line);
|
|
currentLine = currentLine === '' ? lineContent.trim() : currentLine;
|
|
if (currentLine.startsWith('-') && indentation === configuredIndentation && currentLine === lineContent.trim()) {
|
|
position.character += indentation;
|
|
}
|
|
if (position.character > indentation && position.character > 0) {
|
|
const parent = this.getParent(node);
|
|
if (parent) {
|
|
return this.getProperParentByIndentation(indentation, parent, textBuffer, currentLine, configuredIndentation, rootParent);
|
|
}
|
|
}
|
|
else if (position.character < indentation) {
|
|
const parent = this.getParent(node);
|
|
if ((0, yaml_1.isPair)(parent) && (0, yaml_1.isNode)(parent.value)) {
|
|
return parent.value;
|
|
}
|
|
else if ((0, yaml_1.isPair)(rootParent) && (0, yaml_1.isNode)(rootParent.value)) {
|
|
return rootParent.value;
|
|
}
|
|
}
|
|
else {
|
|
return node;
|
|
}
|
|
}
|
|
else if ((0, yaml_1.isPair)(node)) {
|
|
rootParent = node;
|
|
const parent = this.getParent(node);
|
|
return this.getProperParentByIndentation(indentation, parent, textBuffer, currentLine, configuredIndentation, rootParent);
|
|
}
|
|
return node;
|
|
}
|
|
getParent(node) {
|
|
return (0, astUtils_1.getParent)(this.internalDocument, node);
|
|
}
|
|
}
|
|
exports.SingleYAMLDocument = SingleYAMLDocument;
|
|
/**
|
|
* Contains the SingleYAMLDocuments, to be passed
|
|
* to the `parseYAML` caller.
|
|
*/
|
|
class YAMLDocument {
|
|
constructor(documents, tokens) {
|
|
this.documents = documents;
|
|
this.tokens = tokens;
|
|
this.errors = [];
|
|
this.warnings = [];
|
|
}
|
|
}
|
|
exports.YAMLDocument = YAMLDocument;
|
|
class YamlDocuments {
|
|
constructor() {
|
|
// a mapping of URIs to cached documents
|
|
this.cache = new Map();
|
|
}
|
|
/**
|
|
* Get cached YAMLDocument
|
|
* @param document TextDocument to parse
|
|
* @param parserOptions YAML parserOptions
|
|
* @param addRootObject if true and document is empty add empty object {} to force schema usage
|
|
* @returns the YAMLDocument
|
|
*/
|
|
getYamlDocument(document, parserOptions, addRootObject = false) {
|
|
this.ensureCache(document, parserOptions ?? yamlParser07_1.defaultOptions, addRootObject);
|
|
return this.cache.get(document.uri).document;
|
|
}
|
|
/**
|
|
* For test purpose only!
|
|
*/
|
|
clear() {
|
|
this.cache.clear();
|
|
}
|
|
ensureCache(document, parserOptions, addRootObject) {
|
|
const key = document.uri;
|
|
if (!this.cache.has(key)) {
|
|
this.cache.set(key, { version: -1, document: new YAMLDocument([], []), parserOptions: yamlParser07_1.defaultOptions });
|
|
}
|
|
const cacheEntry = this.cache.get(key);
|
|
if (cacheEntry.version !== document.version ||
|
|
(parserOptions.customTags && !(0, arrUtils_1.isArrayEqual)(cacheEntry.parserOptions.customTags, parserOptions.customTags))) {
|
|
let text = document.getText();
|
|
// if text is contains only whitespace wrap all text in object to force schema selection
|
|
if (addRootObject && !/\S/.test(text)) {
|
|
text = `{${text}}`;
|
|
}
|
|
const doc = (0, yamlParser07_1.parse)(text, parserOptions, document);
|
|
cacheEntry.document = doc;
|
|
cacheEntry.version = document.version;
|
|
cacheEntry.parserOptions = parserOptions;
|
|
}
|
|
}
|
|
}
|
|
exports.YamlDocuments = YamlDocuments;
|
|
exports.yamlDocumentsCache = new YamlDocuments();
|
|
function YAMLErrorToYamlDocDiagnostics(error) {
|
|
return {
|
|
message: error.message,
|
|
location: {
|
|
start: error.pos[0],
|
|
end: error.pos[1],
|
|
toLineEnd: true,
|
|
},
|
|
severity: 1,
|
|
code: vscode_json_languageservice_1.ErrorCode.Undefined,
|
|
};
|
|
}
|
|
});
|
|
//# sourceMappingURL=yaml-documents.js.map
|