kjelsrud.dev/node_modules/@astrojs/language-server/dist/plugins/html/HTMLPlugin.js
2023-07-19 21:31:30 +02:00

198 lines
8.8 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HTMLPlugin = void 0;
const emmet_helper_1 = require("@vscode/emmet-helper");
const vscode_html_languageservice_1 = require("vscode-html-languageservice");
const vscode_languageserver_1 = require("vscode-languageserver");
const utils_1 = require("../../core/documents/utils");
const astro_attributes_1 = require("./features/astro-attributes");
const utils_2 = require("./utils");
class HTMLPlugin {
constructor(configManager) {
this.__name = 'html';
this.lang = (0, vscode_html_languageservice_1.getLanguageService)({
customDataProviders: [astro_attributes_1.astroAttributes, astro_attributes_1.astroElements, astro_attributes_1.classListAttribute],
});
this.attributeOnlyLang = (0, vscode_html_languageservice_1.getLanguageService)({
customDataProviders: [astro_attributes_1.astroAttributes],
useDefaultDataProvider: false,
});
this.componentLang = (0, vscode_html_languageservice_1.getLanguageService)({
customDataProviders: [astro_attributes_1.astroAttributes, astro_attributes_1.astroDirectives],
useDefaultDataProvider: false,
});
this.styleScriptTemplate = new Set(['style']);
this.configManager = configManager;
}
async doHover(document, position) {
if (!(await this.featureEnabled(document, 'hover'))) {
return null;
}
const html = document.html;
if (!html) {
return null;
}
const node = html.findNodeAt(document.offsetAt(position));
if (!node) {
return null;
}
// If the node we're hovering on is a component, instead only provide astro-specific hover info
if ((0, utils_1.isPossibleComponent)(node) && node.tag !== 'Fragment') {
return this.componentLang.doHover(document, position, html);
}
return this.lang.doHover(document, position, html);
}
/**
* Get HTML completions
*/
async getCompletions(document, position) {
if (!(await this.featureEnabled(document, 'completions'))) {
return null;
}
const html = document.html;
const offset = document.offsetAt(position);
if (!html ||
(0, utils_1.isInsideFrontmatter)(document.getText(), offset) ||
(0, utils_1.isInsideExpression)(document.getText(), html.findNodeAt(offset).start, offset)) {
return null;
}
// Get Emmet completions
let emmetResults = {
isIncomplete: true,
items: [],
};
const emmetConfig = await this.configManager.getEmmetConfig(document);
const extensionConfig = (await this.configManager.getConfig('astro', document.uri)) ?? {};
if (extensionConfig?.html?.completions?.emmet ?? true) {
this.lang.setCompletionParticipants([
{
onHtmlContent: () => (emmetResults = (0, emmet_helper_1.doComplete)(document, position, 'html', emmetConfig) || emmetResults),
},
]);
}
// If we're in a component starting tag, we do not want HTML language completions
// as HTML attributes are not valid for components
const inComponentTag = (0, utils_1.isInComponentStartTag)(html, offset);
const inTagName = (0, utils_1.isInTagName)(html, offset);
const results = inComponentTag && !inTagName
? (0, utils_2.removeDataAttrCompletion)(this.attributeOnlyLang.doComplete(document, position, html).items)
: this.lang.doComplete(document, position, html).items.filter(isNoAddedTagWithNoDocumentation);
const langCompletions = inComponentTag ? [] : this.getLangCompletions(results);
return vscode_languageserver_1.CompletionList.create([...results, ...langCompletions, ...emmetResults.items],
// Emmet completions change on every keystroke, so they are never complete
emmetResults.items.length > 0);
// Filter script and style completions with no documentation to prevent duplicates
// due to our added definitions for those tags
function isNoAddedTagWithNoDocumentation(item) {
return !(['script', 'style'].includes(item.label) && item.documentation === undefined);
}
}
getFoldingRanges(document) {
const html = document.html;
if (!html) {
return null;
}
return this.lang.getFoldingRanges(document);
}
getLinkedEditingRanges(document, position) {
const html = document.html;
if (!html) {
return null;
}
const ranges = this.lang.findLinkedEditingRanges(document, position, html);
if (!ranges) {
return null;
}
return { ranges };
}
async doTagComplete(document, position) {
if (!(await this.featureEnabled(document, 'tagComplete'))) {
return null;
}
const html = document.html;
const offset = document.offsetAt(position);
if (!html ||
(0, utils_1.isInsideFrontmatter)(document.getText(), offset) ||
(0, utils_1.isInsideExpression)(document.getText(), html.findNodeAt(offset).start, offset)) {
return null;
}
return this.lang.doTagComplete(document, position, html);
}
prepareRename(document, position) {
const html = document.html;
const offset = document.offsetAt(position);
const node = html.findNodeAt(offset);
if (!node || (0, utils_1.isPossibleComponent)(node) || !node.tag || !this.isRenameAtTag(node, offset)) {
return null;
}
const tagNameStart = node.start + '<'.length;
return vscode_languageserver_1.Range.create(document.positionAt(tagNameStart), document.positionAt(tagNameStart + node.tag.length));
}
rename(document, position, newName) {
const html = document.html;
const offset = document.offsetAt(position);
if (!html || (0, utils_1.isInsideFrontmatter)(document.getText(), offset)) {
return null;
}
const node = html.findNodeAt(offset);
// The TypeScript plugin handles renaming for components
if (!node || (0, utils_1.isPossibleComponent)(node) || !this.isRenameAtTag(node, offset)) {
return null;
}
return this.lang.doRename(document, position, newName, html);
}
async getDocumentSymbols(document) {
if (!(await this.featureEnabled(document, 'documentSymbols'))) {
return [];
}
const html = document.html;
if (!html) {
return [];
}
return this.lang.findDocumentSymbols(document, html);
}
/**
* Get lang completions for style tags (ex: `<style lang="scss">`)
*/
getLangCompletions(completions) {
const styleScriptTemplateCompletions = completions.filter((completion) => completion.kind === vscode_languageserver_1.CompletionItemKind.Property && this.styleScriptTemplate.has(completion.label));
const langCompletions = [];
addLangCompletion('style', ['scss', 'sass', 'less', 'styl', 'stylus']);
return langCompletions;
/** Add language completions */
function addLangCompletion(tag, languages) {
const existingCompletion = styleScriptTemplateCompletions.find((completion) => completion.label === tag);
if (!existingCompletion) {
return;
}
languages.forEach((lang) => langCompletions.push({
...existingCompletion,
label: `${tag} (lang="${lang}")`,
insertText: existingCompletion.insertText && `${existingCompletion.insertText} lang="${lang}"`,
textEdit: existingCompletion.textEdit && vscode_languageserver_1.TextEdit.is(existingCompletion.textEdit)
? {
range: existingCompletion.textEdit.range,
newText: `${existingCompletion.textEdit.newText} lang="${lang}"`,
}
: undefined,
}));
}
}
/**
* Returns true if rename happens at the tag name, not anywhere inbetween.
*/
isRenameAtTag(node, offset) {
if (!node.tag) {
return false;
}
const startTagNameEnd = node.start + `<${node.tag}`.length;
const isAtStartTag = offset > node.start && offset <= startTagNameEnd;
const isAtEndTag = node.endTagStart !== undefined && offset >= node.endTagStart && offset < node.end;
return isAtStartTag || isAtEndTag;
}
async featureEnabled(document, feature) {
return ((await this.configManager.isEnabled(document, 'html')) &&
(await this.configManager.isEnabled(document, 'html', feature)));
}
}
exports.HTMLPlugin = HTMLPlugin;