UNPKG

svelte-language-server

Version:
247 lines 10.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FoldingRangeProviderImpl = void 0; const typescript_1 = __importDefault(require("typescript")); const vscode_languageserver_1 = require("vscode-languageserver"); const documents_1 = require("../../../lib/documents"); const utils_1 = require("../../../utils"); const utils_2 = require("../utils"); const utils_3 = require("./utils"); const indentFolding_1 = require("../../../lib/foldingRange/indentFolding"); const svelte_ast_utils_1 = require("../svelte-ast-utils"); class FoldingRangeProviderImpl { constructor(lsAndTsDocResolver, configManager) { this.lsAndTsDocResolver = lsAndTsDocResolver; this.configManager = configManager; this.foldEndPairCharacters = ['}', ']', ')', '`', '>']; } async getFoldingRanges(document) { // don't use ls.getProgram unless it's necessary // this feature is pure syntactic and doesn't need type information const { lang, tsDoc } = await this.lsAndTsDocResolver.getLsForSyntheticOperations(document); const foldingRanges = tsDoc.parserError && !document.moduleScriptInfo && !document.scriptInfo ? [] : lang.getOutliningSpans(tsDoc.filePath); const lineFoldingOnly = !!this.configManager.getClientCapabilities()?.textDocument?.foldingRange ?.lineFoldingOnly; const result = foldingRanges .filter((span) => !(0, utils_3.isTextSpanInGeneratedCode)(tsDoc.getFullText(), span.textSpan)) .map((span) => ({ originalRange: this.mapToOriginalRange(tsDoc, span.textSpan, document), span })) .map(({ originalRange, span }) => this.convertOutliningSpan(span, document, originalRange, lineFoldingOnly)) .filter(utils_1.isNotNullOrUndefined) .concat(this.collectSvelteBlockFolding(document, tsDoc, lineFoldingOnly)) .concat(this.getSvelteTagFoldingIfParserError(document, tsDoc)) .filter((r) => (lineFoldingOnly ? r.startLine < r.endLine : r.startLine <= r.endLine)); return result; } mapToOriginalRange(tsDoc, textSpan, document) { const range = (0, documents_1.mapRangeToOriginal)(tsDoc, (0, utils_2.convertRange)(tsDoc, textSpan)); const startOffset = document.offsetAt(range.start); if (range.start.line < 0 || range.end.line < 0 || range.start.line > range.end.line) { return; } if ((0, documents_1.isInTag)(range.start, document.scriptInfo) || (0, documents_1.isInTag)(range.start, document.moduleScriptInfo)) { return range; } const endOffset = document.offsetAt(range.end); const originalText = document.getText().slice(startOffset, endOffset); if (originalText.length === 0) { return; } const generatedText = tsDoc.getText(textSpan.start, textSpan.start + textSpan.length); const oneToOne = originalText.trim() === generatedText.trim(); if (oneToOne) { return range; } } /** * Doing this here with the svelte2tsx's svelte ast is slightly * less prone to error and faster than * using the svelte ast in the svelte plugins. */ collectSvelteBlockFolding(document, tsDoc, lineFoldingOnly) { if (tsDoc.parserError) { return []; } const ranges = []; const provider = this; const enter = function (node, parent, key) { if (key === 'attributes') { this.skip(); } // use sub-block for await block if (!node.type.endsWith('Block') || node.type === 'AwaitBlock') { return; } if (node.type === 'IfBlock') { provider.getIfBlockFolding(node, document, ranges); return; } if ((0, svelte_ast_utils_1.isElseBlockWithElseIf)(node)) { return; } if ((node.type === 'CatchBlock' || node.type === 'ThenBlock') && (0, svelte_ast_utils_1.isAwaitBlock)(parent)) { const expressionEnd = (node.type === 'CatchBlock' ? parent.error?.end : parent.value?.end) ?? document.getText().indexOf('}', node.start); const beforeBlockStartTagEnd = document.getText().indexOf('}', expressionEnd); if (beforeBlockStartTagEnd == -1) { return; } ranges.push(provider.createFoldingRange(document, beforeBlockStartTagEnd + 1, node.end)); return; } if ((0, svelte_ast_utils_1.isEachBlock)(node)) { const start = document.getText().indexOf('}', (node.key ?? node.expression).end); const elseStart = node.else ? (0, svelte_ast_utils_1.findElseBlockTagStart)(document.getText(), node.else) : -1; ranges.push(provider.createFoldingRange(document, start, elseStart === -1 ? node.end : elseStart)); return; } if ('expression' in node && node.expression && typeof node.expression === 'object') { const start = provider.getStartForNodeWithExpression(node, document); const end = node.end; ranges.push(provider.createFoldingRange(document, start, end)); return; } if (node.start != null && node.end != null) { const start = node.start; const end = node.end; ranges.push(provider.createFoldingRange(document, start, end)); } }; tsDoc.walkSvelteAst({ enter }); if (lineFoldingOnly) { return ranges.map((r) => ({ startLine: r.startLine, endLine: this.previousLineOfEndLine(r.startLine, r.endLine) })); } return ranges; } getIfBlockFolding(node, document, ranges) { const typed = node; const documentText = document.getText(); const start = this.getStartForNodeWithExpression(typed, document); const end = (0, svelte_ast_utils_1.hasElseBlock)(typed) ? (0, svelte_ast_utils_1.findElseBlockTagStart)(documentText, typed.else) : (0, svelte_ast_utils_1.findIfBlockEndTagStart)(documentText, typed); ranges.push(this.createFoldingRange(document, start, end)); } getStartForNodeWithExpression(node, document) { return document.getText().indexOf('}', node.expression.end) + 1; } createFoldingRange(document, start, end) { const range = (0, documents_1.toRange)(document, start, end); return { startLine: range.start.line, startCharacter: range.start.character, endLine: range.end.line, endCharacter: range.end.character }; } convertOutliningSpan(span, document, originalRange, lineFoldingOnly) { if (!originalRange) { return null; } const end = lineFoldingOnly ? this.adjustFoldingEndToNotHideEnd(originalRange, document) : originalRange.end; const result = { startLine: originalRange.start.line, endLine: end.line, kind: this.getFoldingRangeKind(span), startCharacter: lineFoldingOnly ? undefined : originalRange.start.character, endCharacter: lineFoldingOnly ? undefined : end.character }; return result; } getFoldingRangeKind(span) { switch (span.kind) { case typescript_1.default.OutliningSpanKind.Comment: return vscode_languageserver_1.FoldingRangeKind.Comment; case typescript_1.default.OutliningSpanKind.Region: return vscode_languageserver_1.FoldingRangeKind.Region; case typescript_1.default.OutliningSpanKind.Imports: return vscode_languageserver_1.FoldingRangeKind.Imports; case typescript_1.default.OutliningSpanKind.Code: default: return undefined; } } adjustFoldingEndToNotHideEnd(range, document) { // don't fold end bracket, brace... if (range.end.character > 0) { const text = document.getText(); const offsetBeforeEnd = document.offsetAt({ line: range.end.line, character: range.end.character - 1 }); const foldEndCharacter = text[offsetBeforeEnd]; if (this.foldEndPairCharacters.includes(foldEndCharacter)) { return { line: this.previousLineOfEndLine(range.start.line, range.end.line) }; } } return range.end; } getSvelteTagFoldingIfParserError(document, tsDoc) { if (!tsDoc.parserError) { return []; } const htmlTemplateRanges = this.getHtmlTemplateRangesForChecking(document); return (0, indentFolding_1.indentBasedFoldingRange)({ document, skipFold: (_, lineContent) => { return !/{\s*(#|\/|:)/.test(lineContent); }, ranges: htmlTemplateRanges }); } getHtmlTemplateRangesForChecking(document) { const ranges = []; const excludeTags = [ document.templateInfo, document.moduleScriptInfo, document.scriptInfo, document.styleInfo ] .filter(utils_1.isNotNullOrUndefined) .map((info) => ({ startLine: document.positionAt(info.container.start).line, endLine: document.positionAt(info.container.end).line })) .sort((a, b) => a.startLine - b.startLine); if (excludeTags.length === 0) { return [{ startLine: 0, endLine: document.lineCount - 1 }]; } if (excludeTags[0].startLine > 0) { ranges.push({ startLine: 0, endLine: excludeTags[0].startLine - 1 }); } for (let index = 0; index < excludeTags.length; index++) { const element = excludeTags[index]; const next = excludeTags[index + 1]; ranges.push({ startLine: element.endLine + 1, endLine: next ? next.startLine - 1 : document.lineCount - 1 }); } return ranges; } previousLineOfEndLine(startLine, endLine) { return Math.max(endLine - 1, startLine); } } exports.FoldingRangeProviderImpl = FoldingRangeProviderImpl; //# sourceMappingURL=FoldingRangeProvider.js.map