yaml-language-server
Version:
219 lines • 8.18 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Location, Range, SymbolKind } from 'vscode-languageserver-types';
import { yamlDocumentsCache } from '../parser/yaml-documents';
import { isMap, isSeq } from 'yaml';
export class YAMLDocumentSymbols {
constructor(telemetry) {
this.telemetry = telemetry;
}
findDocumentSymbols(document, context = { resultLimit: Number.MAX_VALUE }) {
let results = [];
try {
const doc = yamlDocumentsCache.getYamlDocument(document);
if (!doc || doc.documents.length === 0) {
return null;
}
for (const yamlDoc of doc.documents) {
if (yamlDoc.root) {
results = results.concat(this.findFlatDocumentSymbols(document, yamlDoc.root, context));
}
}
}
catch (err) {
this.telemetry?.sendError('yaml.documentSymbols.error', err);
}
return results;
}
findHierarchicalDocumentSymbols(document, context = { resultLimit: Number.MAX_VALUE }) {
let results = [];
try {
const doc = yamlDocumentsCache.getYamlDocument(document);
if (!doc || doc.documents.length === 0) {
return null;
}
for (const yamlDoc of doc.documents) {
if (yamlDoc.root) {
results = results.concat(this.findHierarchicalSymbols(document, yamlDoc.root, context));
}
}
}
catch (err) {
this.telemetry?.sendError('yaml.hierarchicalDocumentSymbols.error', err);
}
return results;
}
findFlatDocumentSymbols(document, root, context) {
let limit = context.resultLimit || Number.MAX_VALUE;
const toVisit = [{ node: root, containerName: '' }];
let nextToVisit = 0;
let limitExceeded = false;
const result = [];
const collectOutlineEntries = (node, containerName) => {
if (node.type === 'array') {
node.items.forEach((item) => {
if (item) {
toVisit.push({ node: item, containerName });
}
});
}
else if (node.type === 'object') {
node.properties.forEach((property) => {
const valueNode = property.valueNode;
if (!valueNode) {
return;
}
if (limit > 0) {
limit--;
const location = Location.create(document.uri, getRange(document, property));
const childContainerName = containerName ? containerName + '.' + property.keyNode.value : property.keyNode.value;
result.push({
name: getKeyLabel(property),
kind: getSymbolKind(valueNode.type),
location,
containerName,
});
toVisit.push({ node: valueNode, containerName: childContainerName });
}
else {
limitExceeded = true;
}
});
}
};
while (nextToVisit < toVisit.length) {
const next = toVisit[nextToVisit++];
collectOutlineEntries(next.node, next.containerName);
}
if (limitExceeded) {
context.onResultLimitExceeded?.(document.uri);
}
return result;
}
findHierarchicalSymbols(document, root, context) {
let limit = context.resultLimit || Number.MAX_VALUE;
const result = [];
const toVisit = [{ node: root, result }];
let nextToVisit = 0;
let limitExceeded = false;
const collectOutlineEntries = (node, result) => {
if (node.type === 'array') {
node.items.forEach((item, index) => {
if (!item) {
return;
}
if (limit > 0) {
limit--;
const range = getRange(document, item);
const symbol = {
name: String(index),
kind: getSymbolKind(item.type),
range,
selectionRange: range,
children: [],
};
result.push(symbol);
toVisit.push({ result: symbol.children, node: item });
}
else {
limitExceeded = true;
}
});
}
else if (node.type === 'object') {
node.properties.forEach((property) => {
const valueNode = property.valueNode;
if (!valueNode) {
return;
}
if (limit > 0) {
limit--;
const children = [];
const symbol = {
name: getKeyLabel(property),
kind: getSymbolKind(valueNode.type),
range: getRange(document, property),
selectionRange: getRange(document, property.keyNode),
children,
detail: getDetail(valueNode),
};
result.push(symbol);
toVisit.push({ result: children, node: valueNode });
}
else {
limitExceeded = true;
}
});
}
};
while (nextToVisit < toVisit.length) {
const next = toVisit[nextToVisit++];
collectOutlineEntries(next.node, next.result);
}
if (limitExceeded) {
context.onResultLimitExceeded?.(document.uri);
}
return result;
}
}
function getSymbolKind(nodeType) {
switch (nodeType) {
case 'object':
return SymbolKind.Module;
case 'string':
return SymbolKind.String;
case 'number':
return SymbolKind.Number;
case 'array':
return SymbolKind.Array;
case 'boolean':
return SymbolKind.Boolean;
default:
return SymbolKind.Variable;
}
}
function getKeyLabel(property) {
const keyNode = property.keyNode.internalNode;
let name;
if (isMap(keyNode)) {
name = '{}';
}
else if (isSeq(keyNode)) {
name = '[]';
}
else if ('source' in keyNode && typeof keyNode.source === 'string') {
name = keyNode.source;
}
else {
name = property.keyNode.value;
}
if (name) {
name = name.replace(/[\n]/g, '↵');
}
if (name && name.trim()) {
return name;
}
return `"${name}"`;
}
function getDetail(node) {
if (!node) {
return undefined;
}
if (node.type === 'boolean' || node.type === 'number' || node.type === 'null' || node.type === 'string') {
return String(node.value);
}
if (node.type === 'array') {
return node.children.length ? undefined : '[]';
}
if (node.type === 'object') {
return node.children.length ? undefined : '{}';
}
return undefined;
}
function getRange(document, node) {
return Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
}
//# sourceMappingURL=documentSymbols.js.map