qingkuai-language-service
Version:
The language service for qingkuai
1,360 lines (1,346 loc) • 69.8 kB
JavaScript
import { a as isUndefined, g as traverseObject, L as LSU_AND_DOT, G as GLOBAL_TYPE_IDS, v as GlobalTypeIsNonObjectJs, w as recoverNumberArray, x as recoverPositions, y as compressNumberArray, z as isQingkuaiFileName, I as INVALID_COMPLETION_TEXT_LABELS, A as INVLALID_COMPLETION_PACKAGES, d as debugAssert, B as qkExtInImportRE, C as CompletionImportTextEditRE, c as isIndexesInvalid, D as SCRIPT_EXTENSIONS, m as mdCodeBlockGen, P as PROXIED_MARK, i as isEmptyString, E as SOURCE_SPAN_MARK, b as isString } from './chunks/shared.js';
import { constants, util, PositionFlag } from 'qingkuai/compiler';
let ts;
function setState(options) {
({ ts } = options);
}
function isInTopScope(node) {
switch (node.parent.kind) {
case void 0:
case ts.SyntaxKind.SourceFile: {
return true;
}
case ts.SyntaxKind.Block:
case ts.SyntaxKind.CaseBlock:
case ts.SyntaxKind.ModuleBlock:
case ts.SyntaxKind.ForStatement:
case ts.SyntaxKind.ForInStatement:
case ts.SyntaxKind.ForOfStatement:
case ts.SyntaxKind.ClassStaticBlockDeclaration: {
return false;
}
default: {
return !ts.isParameter(node) && isInTopScope(node.parent);
}
}
}
function isInComponentFunctionTopScope(node) {
const blockNode = ts.findAncestor(node, (ancestor) => {
return ts.isBlock(ancestor);
});
return !!(blockNode?.parent && ts.isFunctionDeclaration(blockNode.parent) && blockNode.parent.name?.text === constants.LSC.COMPONENT);
}
function walkTsNode(node, callback) {
if (!isUndefined(node)) {
callback(node);
ts.forEachChild(node, (cn) => {
walkTsNode(cn, callback);
});
}
}
function getNodeAtPositionAndWithin(node, pos) {
const [start, len] = [node.getStart(), node.getWidth()];
if (pos >= start && pos <= start + len) {
for (const child of node.getChildren()) {
const foundInChild = getNodeAtPositionAndWithin(child, pos);
if (foundInChild) {
return foundInChild;
}
}
return node;
}
return void 0;
}
const ExternalGlobalTypeWithGenerics = () => {
return [
3001,
`The external type with generics cannot be used as the global type.`,
"https://qingkuai.dev/misc/typescript.html#generics"
];
};
const GlobalTypeIsNonObjectTs = (name) => {
return [
3002,
`The global type "${name}" must satisfy the constraint of being an object type.`,
"https://qingkuai.dev/misc/typescript.html#component-attribute-types"
];
};
var __defProp$3 = Object.defineProperty;
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
function confirmTypesForCompileResult(adapter, fileInfo) {
let sourceFile;
let typeChecker;
let componentFuncNode;
let componentReturnsNode;
const updateSourceFile = () => {
const program = adapter.getDefaultProgram(fileInfo.path);
sourceFile = program?.getSourceFile(fileInfo.path);
typeChecker = program?.getTypeChecker();
return sourceFile;
};
if (fileInfo.typesConfirmed || !updateSourceFile()) {
return;
}
fileInfo.typesConfirmed = true;
const componentGenerics = [];
const slotNames = [];
const edit = new FileEdit(fileInfo, updateSourceFile);
const globalTypes = {};
const extractedSlotContexts = [];
const anyValueStr = constants.LSC.UTIL + ".anyValue";
const getTypeDelayIndexesSet = new Set(fileInfo.getTypeDelayIndexes);
walkTsNode(sourceFile, (node) => {
var _a;
if (ts.isFunctionDeclaration(node) && node.name?.text === constants.LSC.COMPONENT && isInTopScope(node)) {
componentFuncNode = node;
}
if (!fileInfo.isTS) {
const jsDocs = node.jsDoc;
jsDocs?.forEach((jsDoc) => {
for (const jsDocTag of jsDoc.tags ?? []) {
if (ts.isJSDocTypedefTag(jsDocTag) && jsDocTag.name?.text && GLOBAL_TYPE_IDS.has(jsDocTag.name.text) && !globalTypes[jsDocTag.name.text] && isInComponentFunctionTopScope(jsDocTag)) {
const constraints = [];
const genericNames = [];
const globalType = typeChecker.getTypeAtLocation(jsDocTag);
const templateTags = jsDocTag.parent.tags?.filter(ts.isJSDocTemplateTag);
if (!(globalType.flags & ts.TypeFlags.Object)) {
fileInfo.pushDiagnostic(
jsDocTag.getStart(),
jsDocTag.getEnd(),
GlobalTypeIsNonObjectJs(jsDocTag.name.text)
);
}
templateTags?.forEach((tag) => {
tag.typeParameters.forEach((param, index) => {
const genericName = param.name.getText() + jsDocTag.name.text.slice(0, 1);
const constraint = param.constraint ?? (index === 0 ? tag.constraint : void 0);
const constraintText = constraint?.getText() ?? "";
if (constraintText) {
constraints.push(constraintText);
}
genericNames.push(genericName);
componentGenerics.push(`@template${constraintText} ${genericName}`);
});
});
globalTypes[jsDocTag.name.text] = {
constraints,
genericNames,
type: globalType,
isExternal: false
};
}
}
});
} else if ((ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node)) && GLOBAL_TYPE_IDS.has(node.name.text) && !globalTypes[node.name.text] && isInComponentFunctionTopScope(node)) {
const constraints = [];
const genericNames = [];
const globalType = typeChecker.getTypeAtLocation(node);
if (!(globalType.flags & ts.TypeFlags.Object)) {
fileInfo.pushDiagnostic(
node.name.getStart(),
node.name.getEnd(),
GlobalTypeIsNonObjectTs(node.name.text)
);
}
node.typeParameters?.forEach((param) => {
const genericName = param.name.getText() + node.name.text.slice(0, 1);
if (param.constraint) {
constraints.push(param.constraint.getText());
}
componentGenerics.push(
`${genericName}${param.constraint ? ` extends ${param.constraint.getText()}` : ""}`
);
genericNames.push(genericName);
});
globalTypes[node.name.text] = {
genericNames,
constraints,
isExternal: false,
type: typeChecker.getTypeAtLocation(node)
};
}
if (ts.isImportDeclaration(node)) {
if (isInTopScope(node)) {
const identifiers = [];
if (ts.isImportDeclaration(node) && node.importClause) {
if (node.importClause.name) {
identifiers.push(node.importClause.name);
}
if (node.importClause.namedBindings && !ts.isNamespaceImport(node.importClause.namedBindings)) {
for (const spec of node.importClause.namedBindings.elements) {
identifiers.push(spec.name);
}
}
}
for (const id of identifiers) {
if (!globalTypes[id.text] && GLOBAL_TYPE_IDS.has(id.text)) {
const symbol = typeChecker.getSymbolAtLocation(id);
const aliasedSymbol = symbol && typeChecker.getAliasedSymbol(symbol);
if (aliasedSymbol && aliasedSymbol.flags & ts.SymbolFlags.Type) {
const globalType = typeChecker.getTypeAtLocation(
aliasedSymbol.declarations[0]
);
if (!(globalType.flags & ts.TypeFlags.Object)) {
fileInfo.pushDiagnostic(
id.getStart(),
id.getEnd(),
GlobalTypeIsNonObjectTs(id.text)
);
} else if (globalType.objectFlags & ts.ObjectFlags.Reference && typeChecker.getTypeArguments(globalType).length) {
fileInfo.pushDiagnostic(
id.getStart(),
id.getEnd(),
ExternalGlobalTypeWithGenerics()
);
}
globalTypes[id.text] = {
isExternal: true,
constraints: [],
genericNames: [],
type: globalType
};
}
}
}
}
}
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && ts.isIdentifier(node.expression.expression) && getTypeDelayIndexesSet.has(node.getStart()) && node.expression.getText() === constants.LSC.GET_TYPE_DELAY_MARKING) {
const slotNameNode = node.arguments[0];
const contextPropertyNode = node.arguments[1];
if (slotNameNode.text !== slotNames[slotNames.length - 1]?.name) {
slotNames.push({
name: slotNameNode.text,
sourceRange: [
fileInfo.getSourceIndex(slotNameNode.getStart()),
fileInfo.getSourceIndex(slotNameNode.getEnd())
]
});
fileInfo.slotNames.push(slotNameNode.text);
}
(extractedSlotContexts[_a = slotNames.length - 1] ?? (extractedSlotContexts[_a] = [])).push({
property: {
name: contextPropertyNode.text,
sourceRange: [
fileInfo.getSourceIndex(contextPropertyNode.getStart()),
fileInfo.getSourceIndex(contextPropertyNode.getEnd())
]
},
valueType: typeChecker.typeToString(
typeChecker.getTypeAtLocation(node.arguments[2])
)
});
}
if (ts.isReturnStatement(node) && node.getEnd() === componentFuncNode.getEnd() - 2) {
componentReturnsNode = node;
}
});
traverseObject(globalTypes, (kind, globalType) => {
if (globalType && (kind === "Props" || kind === "Refs") && globalType.type.flags & ts.TypeFlags.Object) {
for (const property of typeChecker.getPropertiesOfType(globalType.type)) {
const propertyType = typeChecker.getTypeOfSymbolAtLocation(property, sourceFile);
const attributeItem = {
kind,
name: property.name,
stringCandidates: [],
type: typeChecker.typeToString(propertyType),
optional: !!(property.flags & ts.SymbolFlags.Optional),
mayBeEvent: kind === "Props" && isMayBeEventType(propertyType),
couldBeString: !!(propertyType.flags & ts.TypeFlags.StringLike)
};
if (propertyType.isUnion()) {
propertyType.types.forEach((t) => {
if (t.flags & ts.TypeFlags.StringLiteral) {
attributeItem.stringCandidates.push(
JSON.parse(typeChecker.typeToString(t))
);
}
attributeItem.couldBeString || (attributeItem.couldBeString = !!(t.flags & ts.TypeFlags.StringLike));
});
} else if (propertyType.flags & ts.TypeFlags.StringLiteral) {
attributeItem.stringCandidates.push(
JSON.parse(typeChecker.typeToString(propertyType))
);
}
if (isEnumerablePropertyOfGlobalTypes(property)) {
fileInfo.attributes.push(attributeItem);
}
}
}
});
["Props", "Refs"].forEach((kind) => {
const globalType = globalTypes[kind];
if (!globalType) {
edit.setEditIndex(componentFuncNode.getStart());
if (fileInfo.isTS) {
edit.push(`type ${kind} = ${constants.LSC.UTIL}.EmptyObject;
`);
} else {
edit.push(`/** @typedef {${constants.LSC.UTIL}.EmptyObject} ${kind} */
`);
}
}
});
if (fileInfo.isTS && !edit.isEmpty) {
edit.flush();
}
let contextRefsType = "Refs";
let declareRefsType = "Refs";
let contextPropsType = "Props";
let declarePropsType = "Props";
if (globalTypes.Refs?.constraints.length) {
declareRefsType = `Refs<${globalTypes.Refs.constraints.join(", ")}>`;
}
if (globalTypes.Refs?.genericNames.length) {
contextRefsType = `Refs<${globalTypes.Refs.genericNames.join(", ")}>`;
}
if (globalTypes.Props?.constraints.length) {
declarePropsType = `Props<${globalTypes.Props.constraints.join(", ")}>`;
}
if (globalTypes.Props?.genericNames.length) {
contextPropsType = `Props<${globalTypes.Props.genericNames.join(", ")}>`;
}
if (componentReturnsNode) {
if (fileInfo.isTS) {
edit.setEditIndex(componentFuncNode.body.getStart() + 2);
edit.push(` const props: Readonly<${declarePropsType}> = ${anyValueStr};
`);
edit.push(` const refs: ${declareRefsType} = ${anyValueStr};
`);
edit.flush();
if (componentGenerics.length) {
edit.setEditIndex(componentReturnsNode.getStart() + 7);
edit.push(`<${componentGenerics.join(", ")}>`);
edit.flush();
}
edit.setEditIndex(componentReturnsNode.getStart() + 9);
edit.push(`: { props: ${contextPropsType}; refs: ${contextRefsType}; slots: `);
} else {
edit.setEditIndex(componentFuncNode.body.getStart() + 2);
edit.push(
` /** @type {Readonly<${declarePropsType}>} */ const props = ${anyValueStr};
`
);
edit.push(` /** @type {${declareRefsType}} */ const refs = ${anyValueStr};
`);
edit.flush();
edit.push("/**\n");
for (const generic of componentGenerics) {
edit.push(` * ${generic}
`);
}
edit.setEditIndex(componentReturnsNode.getStart());
edit.push(
` * @param {Object} _
* @param {${contextPropsType}} _.props
`
);
edit.push(` * @param {${contextRefsType}} _.refs
* @param {`);
}
}
if (!slotNames.length) {
edit.push(`${LSU_AND_DOT}EmptyObject`);
} else {
edit.push("{ ");
for (let i = 0; i < slotNames.length; i++) {
edit.push(util.toPropertyKey(slotNames[i].name), slotNames[i].sourceRange);
edit.push(": (context: { ");
for (const { property, valueType } of extractedSlotContexts[i]) {
edit.push(util.toPropertyKey(property.name), property.sourceRange);
edit.push(`: ${valueType};`);
}
edit.push("}) => void;");
}
edit.push(" }");
}
if (fileInfo.isTS) {
edit.push("}");
} else {
edit.push("} _.slots\n */\n ");
}
edit.flush();
updateSourceFile();
const sourceFileSymbol = typeChecker.getSymbolAtLocation(sourceFile);
const defaultExportSymbol = sourceFileSymbol.exports?.get(ts.InternalSymbolName.Default);
if (defaultExportSymbol) {
const defaultExportType = typeChecker.getTypeOfSymbolAtLocation(
defaultExportSymbol,
sourceFile
);
const defaultExportTypeStr = typeChecker.typeToString(defaultExportType).replaceAll("{", "{\n");
fileInfo.defaultExportTypeStr = defaultExportTypeStr;
}
}
class FileEdit {
constructor(fileInfo, updateSourceFile) {
this.fileInfo = fileInfo;
this.updateSourceFile = updateSourceFile;
__publicField$3(this, "index", -1);
__publicField$3(this, "insertInfo", {});
__publicField$3(this, "items", []);
}
get isEmpty() {
return this.items.length === 0;
}
get editStartIndex() {
return this.getEditedIndex(this.index);
}
setEditIndex(index) {
this.index = index;
}
getEditedIndex(index) {
let ret = index;
traverseObject(this.insertInfo, (key, value) => {
if (key < index) {
ret += value;
}
});
return ret;
}
push(content, sourceRange) {
this.items.push({ content, sourceRange });
}
flush() {
var _a, _b;
let newContent;
const startIndex = this.editStartIndex;
const originalContent = this.fileInfo.code;
newContent = originalContent.slice(0, startIndex);
for (const item of this.items) {
newContent += item.content;
}
newContent += this.fileInfo.code.slice(startIndex);
this.fileInfo.updateContent(newContent);
this.fileInfo.adjustIndexMap(this);
for (const item of this.items) {
(_a = this.insertInfo)[_b = this.index] ?? (_a[_b] = 0);
this.insertInfo[this.index] += item.content.length;
}
this.index = -1;
this.items = [];
}
}
function isMayBeEventType(type) {
if (type.isClass()) {
return false;
}
if (type.isUnion()) {
return type.types.some((item) => isMayBeEventType(item));
}
return !!(type.getCallSignatures().length || type.symbol?.name === "Function");
}
function isEnumerablePropertyOfGlobalTypes(symbol) {
if (symbol.declarations?.length !== 1) {
return false;
}
const declaration = symbol.declarations[0];
if (!("name" in declaration)) {
return false;
}
switch (declaration.name.kind) {
case ts.SyntaxKind.Identifier:
case ts.SyntaxKind.StringLiteral:
case ts.SyntaxKind.NumericLiteral: {
return true;
}
default: {
return false;
}
}
}
var __defProp$2 = Object.defineProperty;
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
class QingkuaiFileInfo {
constructor(code, isTS, version, path, getTypeDelayIndexes, idStatusInfo, adapter, itos, stoi, positions) {
this.code = code;
this.isTS = isTS;
this.version = version;
this.path = path;
this.getTypeDelayIndexes = getTypeDelayIndexes;
this.idStatusInfo = idStatusInfo;
this.adapter = adapter;
this.itos = itos;
this.stoi = stoi;
this.positions = positions;
__publicField$2(this, "nextAdjustSourceIndex", -1);
__publicField$2(this, "isOpen", false);
__publicField$2(this, "componentName");
__publicField$2(this, "typesConfirmed", false);
__publicField$2(this, "slotNames", []);
__publicField$2(this, "defaultExportTypeStr", "");
__publicField$2(this, "lsDiagnostics", []);
__publicField$2(this, "attributes", []);
this.componentName = filePathToComponentName(adapter, path);
}
getSourceIndex(interIndex) {
return this.itos[interIndex];
}
getInterIndex(sourceIndex) {
return this.stoi[sourceIndex];
}
getPositionByIndex(index) {
return this.positions[index];
}
updateContent(newContent) {
this.adapter.updateContent(this, newContent);
}
confirmTypes() {
confirmTypesForCompileResult(this.adapter, this);
}
isPositionFlagSetAtIndex(key, index) {
return !!(this.positions[index].flag & PositionFlag[key]);
}
adjustIndexMap(edit) {
const newItos = [];
const editStartIndex = edit.editStartIndex;
newItos.push(...this.itos.slice(0, editStartIndex));
for (let i = 0, j = editStartIndex; i < edit.items.length; i++) {
const item = edit.items[i];
const contentLength = item.content.length;
const [interStart, interEnd] = [j, j += contentLength];
for (let i2 = 0; i2 < this.stoi.length; i2++) {
if (this.stoi[i2] > interStart) {
this.stoi[i2] += contentLength;
}
}
if (!item.sourceRange) {
newItos.push(...Array(contentLength).fill(-1));
if (this.nextAdjustSourceIndex !== -1) {
newItos[interStart] = this.nextAdjustSourceIndex;
this.stoi[this.nextAdjustSourceIndex] = interStart;
this.nextAdjustSourceIndex = -1;
}
continue;
}
const [sourceStart, sourceEnd] = item.sourceRange;
this.stoi[sourceEnd] = interEnd;
this.nextAdjustSourceIndex = sourceEnd;
for (let i2 = 0; i2 < contentLength; i2++) {
newItos.push(Math.min(sourceStart + i2, sourceEnd - 1));
}
for (let i2 = 0; i2 < sourceEnd - sourceStart; i2++) {
this.stoi[sourceStart + i2] = Math.min(interStart + i2, interEnd - 1);
}
}
const left = this.itos.slice(editStartIndex);
this.itos.length = 0;
this.itos.push(...newItos, ...left);
}
pushDiagnostic(start, end, [code, message, link]) {
const category = code >= 3e3 && code < 4e3 ? this.adapter.ts.DiagnosticCategory.Error : this.adapter.ts.DiagnosticCategory.Warning;
this.lsDiagnostics.push({
code,
category,
url: link,
start,
source: "qk",
length: end - start,
messageText: message,
file: this.adapter.getDefaultSourceFile(this.path)
});
}
}
function updateQingkuaiFile(adapter, params) {
const itos = recoverNumberArray(params.itos);
const stoi = recoverNumberArray(params.stoi);
const path = adapter.getNormalizedPath(params.fileName);
const existing = adapter.qingkuaiFileInfos.get(path);
const positions = recoverPositions(params.positions);
const flags = recoverNumberArray(params.positionFlags);
for (let i = 0; i < positions.length; i++) {
positions[i].flag = flags[i];
}
const newFileInfo = new QingkuaiFileInfo(
params.content,
params.isTS,
existing?.version ?? 0,
path,
params.getTypeDelayIndexes,
params.identifierStatusInfo,
adapter,
itos,
stoi,
positions
);
newFileInfo.isOpen = !!existing?.isOpen;
newFileInfo.updateContent(params.content);
adapter.qingkuaiFileInfos.set(path, newFileInfo);
newFileInfo.confirmTypes();
return {
aitos: compressNumberArray(itos),
astoi: compressNumberArray(stoi)
};
}
function ensureGetQingkuaiFileInfo(adapter, path) {
const existing = adapter.qingkuaiFileInfos.get(path);
if (existing) {
return existing;
}
const newFileInfo = compileQingkuaiFile(adapter, path);
return newFileInfo.confirmTypes(), newFileInfo;
}
function filePathToComponentName(adapter, filePath) {
let base = adapter.path.base(filePath);
base = base.replace(/[^a-zA-Z]*/, "");
base = base.replace(/[^a-zA-Z\d]/g, "");
if (!base) {
return "Anonymous";
}
return util.kebab2Camel(base, true);
}
function compileQingkuaiFile(adapter, path) {
const compileRes = adapter.compile(path);
const existing = adapter.qingkuaiFileInfos.get(path);
const newVersion = existing ? existing.version + 1 : 0;
const fileInfo = new QingkuaiFileInfo(
compileRes.code,
compileRes.scriptDescriptor.isTS,
newVersion,
path,
compileRes.getTypeDelayInterIndexes,
compileRes.identifierStatusInfo,
adapter,
compileRes.indexMap.itos,
compileRes.indexMap.stoi,
compileRes.positions
);
return adapter.qingkuaiFileInfos.set(path, fileInfo), fileInfo;
}
function convertJsDocTagsToMarkdown(tags) {
const labels = tags.map((tag) => {
switch (tag.name) {
case "augments":
case "extends":
case "param":
case "template": {
const body = getTagBody(tag);
if (body?.length === 3) {
const param = body[1];
const doc = body[2];
const label2 = `*@${tag.name}* \`${param}\``;
if (!doc) {
return label2;
}
return label2 + (doc.match(/\r\n|\n/g) ? " \n" + doc : ` \u2014 ${doc}`);
}
break;
}
case "return":
case "returns": {
if (!tag.text?.length) {
return void 0;
}
break;
}
}
const label = `*@${tag.name}*`;
const text = getTagBodyText(tag);
if (!text) {
return label;
}
return label + (text.match(/\r\n|\n/g) ? " \n" + text : ` \u2014 ${text}`);
});
return labels.filter((label) => !!label).join(" \n\n");
}
function convertDisplayPartsToPlainTextWithLink(parts) {
if (isUndefined(parts)) {
return "";
}
return parts.reduce((ret, part) => {
if (part.kind === "linkText") {
let spaceIndex = part.text.indexOf(" ");
if (spaceIndex === -1) {
spaceIndex = part.text.length;
}
return ret + `[${part.text.slice(spaceIndex)}](${part.text.slice(0, spaceIndex)})`;
} else if (part.kind === "linkName") {
const target = part.target;
const args = encodeURIComponent(
JSON.stringify({
end: target.textSpan.start,
start: target.textSpan.start,
path: ts.server.toNormalizedPath(target.fileName)
})
);
return ret + `[${part.text}](command:qingkuai.openFileByFilePath?${args})`;
}
return (ret + (part.kind === "link" ? "" : part.text || "")).replace(LSU_AND_DOT, "");
}, "");
}
function getTagBodyText(tag) {
if (!tag.text) {
return void 0;
}
function makeCodeblock(text2) {
if (/^\s*[~`]{3}/m.test(text2)) {
return text2;
}
return "```\n" + text2 + "\n```";
}
let text = convertDisplayPartsToPlainTextWithLink(tag.text);
switch (tag.name) {
case "example": {
const captionTagMatches = text.match(/<caption>(.*?)<\/caption>\s*(\r\n|\n)/);
if (captionTagMatches && captionTagMatches.index === 0) {
return captionTagMatches[1] + "\n" + makeCodeblock(text.slice(captionTagMatches[0].length));
} else {
return makeCodeblock(text);
}
}
case "author": {
const emailMatch = text.match(/(.+)\s<([-.\w]+@[-.\w]+)>/);
if (emailMatch === null) {
return text;
} else {
return `${emailMatch[1]} ${emailMatch[2]}`;
}
}
case "default": {
return makeCodeblock(text);
}
default: {
return text;
}
}
}
function getTagBody(tag) {
if (tag.name === "template") {
const parts = tag.text;
if (parts && typeof parts !== "string") {
const docs = parts.filter((p) => p.kind === "text");
const docsText = docs.map((p) => p.text.replace(/^\s*-?\s*/, "")).join(" ");
const params = parts.filter((p) => p.kind === "typeParameterName");
const paramsText = params.map((p) => p.text).join(", ");
return params ? ["", paramsText, docsText] : void 0;
}
}
return convertDisplayPartsToPlainTextWithLink(tag.text).split(/^(\S+)\s*-?\s*/);
}
function getAndConvertCompletionInfo(adapter, params) {
const filePath = adapter.getNormalizedPath(params.fileName);
const languageService = adapter.getDefaultLanguageService(filePath);
debugAssert(!!languageService);
const completionRes = languageService.getCompletionsAtPosition(
filePath,
params.pos,
{
...adapter.getUserPreferences(filePath),
triggerKind: params.triggerKind,
includeInsertTextCompletions: true,
triggerCharacter: params.triggerCharacter
},
adapter.getFormattingOptions(filePath)
);
if (!completionRes) {
return null;
}
const convertedEntries = [];
for (const entry of completionRes.entries || []) {
const kindModifiers = parseKindModifier(entry.kindModifiers);
convertedEntries.push({
...entry,
name: entry.name,
isColor: kindModifiers.has("color"),
detail: getScriptKindDetails(entry),
isDeprecated: kindModifiers.has("deprecated"),
label: entry.name + (kindModifiers.has("optional") ? "?" : "")
});
}
return { ...completionRes, entries: convertedEntries };
}
function getAndConvertCompletionDetail(adapter, lsParams) {
const { fileName, pos, entryName, original, source } = lsParams;
const filePath = adapter.getNormalizedPath(fileName);
const languageService = adapter.getDefaultLanguageService(filePath);
debugAssert(!!languageService);
const userPreferences = adapter.getUserPreferences(fileName);
const formattingOptions = adapter.getFormattingOptions(fileName);
const detail = languageService.getCompletionEntryDetails(
fileName,
pos,
entryName,
formattingOptions,
source,
userPreferences,
original
);
if (!detail) {
return null;
}
const converted = {
codeActions: [],
name: detail.name,
kind: detail.kind,
kindModifiers: detail.kindModifiers,
detail: ts.displayPartsToString(detail.displayParts)
};
if (detail.tags?.length) {
converted.tags = detail.tags;
}
if (detail.codeActions?.length) {
const resolveImportExtension = adapter.getQingkuaiConfig(fileName)?.resolveImportExtension;
detail.codeActions?.forEach((action) => {
const effects = [];
const currentFileChanges = [];
if (resolveImportExtension) {
action.description = action.description.replace(qkExtInImportRE, "$1");
}
action.changes.forEach((change) => {
const changeFilePath = adapter.getNormalizedPath(change.fileName);
const changeFileInfo = adapter.service.ensureGetQingkuaiFileInfo(changeFilePath);
const currentEffect = {
textChanges: [],
fileName: changeFilePath,
isNewFile: change.isNewFile
};
change.textChanges.forEach((item) => {
const sourceStartIndex = changeFileInfo.getSourceIndex(item.span.start);
if (fileName === change.fileName) {
const sourceEndIndex = changeFileInfo.getSourceIndex(
item.span.start + item.span.length
);
const willEditImport = CompletionImportTextEditRE.test(item.newText);
if (willEditImport && resolveImportExtension) {
item.newText = item.newText.replace(qkExtInImportRE, "$1");
}
if (willEditImport || !isIndexesInvalid(sourceStartIndex, sourceEndIndex)) {
currentFileChanges.push({
newText: item.newText,
range: [sourceStartIndex, sourceEndIndex]
});
}
} else if (!isIndexesInvalid(sourceStartIndex)) {
currentEffect.textChanges.push({
newText: item.newText,
span: {
start: sourceStartIndex,
length: item.span.length
}
});
}
});
if (currentEffect.textChanges.length) {
effects.push(currentEffect);
}
});
converted.codeActions.push({
effects,
currentFileChanges,
commands: action.commands,
description: action.description
});
});
}
if (detail.documentation) {
converted.documentation = convertDisplayPartsToPlainTextWithLink(detail.documentation);
}
return converted;
}
function proxyGetCompletionsAtPositionToConvert(adapter, project) {
const languageService = project.getLanguageService();
const getCompletionsAtPosition = languageService.getCompletionsAtPosition;
languageService.getCompletionsAtPosition = (fileName, position, preference, formattingOptions) => {
const originalResult = getCompletionsAtPosition.call(
languageService,
fileName,
position,
preference,
formattingOptions
);
if (originalResult?.entries) {
originalResult.entries = originalResult.entries.filter((entry) => {
const sourceFileName = entry.data?.fileName;
if (sourceFileName && isQingkuaiFileName(sourceFileName)) {
entry.name = adapter.service.ensureGetQingkuaiFileInfo(sourceFileName).componentName;
}
if (entry.name.startsWith(constants.PRESERVED_IDPREFIX)) {
return false;
}
if (INVALID_COMPLETION_TEXT_LABELS.has(entry.name)) {
const entryDetail = languageService.getCompletionEntryDetails(
fileName,
position,
entry.name,
formattingOptions,
entry.source,
preference,
entry.data
);
return !entryDetail?.displayParts.some((part) => {
return part.text === constants.LSC.UTIL;
});
}
return !entry.source || !INVLALID_COMPLETION_PACKAGES.has(entry.source);
});
}
return originalResult;
};
}
function proxyGetCompletionEntryDetailsToConvert(_, project) {
const languageService = project.getLanguageService();
const getCompletionEntryDetails = languageService.getCompletionEntryDetails;
languageService.getCompletionEntryDetails = (...args) => {
const originalRet = getCompletionEntryDetails.apply(languageService, args);
if (originalRet && args[6]?.fileName && isQingkuaiFileName(args[6].fileName)) {
originalRet.tags?.forEach((tag) => {
tag.text?.forEach((item) => {
item.text = item.text.replaceAll(LSU_AND_DOT, "");
});
});
for (let i = 0; i < originalRet.displayParts.length; i++) {
if (originalRet.displayParts[i].text !== constants.LSC.UTIL) {
continue;
}
if (originalRet.displayParts[i + 1].text === ".") {
originalRet.displayParts[i + 1].text = "";
}
originalRet.displayParts[i].text = "";
}
}
return originalRet;
};
}
function parseKindModifier(kindModifiers) {
if (isUndefined(kindModifiers)) {
kindModifiers = "";
}
return new Set(kindModifiers.split(/,|\s+/g));
}
function getScriptKindDetails(entry) {
if (!entry.kindModifiers || entry.kind !== ts.ScriptElementKind.scriptElement) {
return void 0;
}
const kindModifiers = parseKindModifier(entry.kindModifiers);
for (const extModifier of SCRIPT_EXTENSIONS) {
if (kindModifiers.has(extModifier)) {
if (entry.name.toLowerCase().endsWith(extModifier)) {
return entry.name;
} else {
return entry.name + extModifier;
}
}
}
return void 0;
}
function getAndConvertDefinitions(adapter, params) {
const filePath = adapter.getNormalizedPath(params.fileName);
const languageService = adapter.getDefaultLanguageService(filePath);
if (!debugAssert(languageService)) {
return null;
}
const data = languageService.getDefinitionAndBoundSpan(filePath, params.pos);
if (!data?.definitions) {
return null;
}
const locationConvertor = adapter.service.createLocationConvertor(filePath);
const dealtDefinitions = data.definitions.map((definition) => {
const definitionLocationConvertor = adapter.service.createLocationConvertor(
definition.fileName
);
const range = definitionLocationConvertor.languageServerRange.fromSourceTextSpan(
definition.textSpan
);
if (definition.contextSpan) {
const contextRange = definition.contextSpan && definitionLocationConvertor.languageServerRange.fromSourceTextSpan(
definition.contextSpan
);
return {
fileName: definition.fileName,
targetSelectionRange: range,
targetRange: contextRange ?? range
};
}
return {
fileName: definition.fileName,
targetSelectionRange: range,
targetRange: range
};
});
return {
definitions: dealtDefinitions,
range: locationConvertor.languageServerRange.fromTextSpan(data.textSpan)
};
}
function getAndConvertTypeDefinitions(adapter, params) {
const filePath = adapter.getNormalizedPath(params.fileName);
const languageService = adapter.getDefaultLanguageService(filePath);
if (!debugAssert(languageService)) {
return null;
}
const definitions = languageService.getTypeDefinitionAtPosition(filePath, params.pos);
if (!definitions?.length) {
return null;
}
return definitions.map((definition) => {
let contextRange = void 0;
const definitionFilePath = adapter.getNormalizedPath(definition.fileName);
const definitionLocationConvertor = adapter.service.createLocationConvertor(definitionFilePath);
const range = definitionLocationConvertor.languageServerRange.fromTextSpan(
definition.textSpan
);
if (definition.contextSpan) {
contextRange = definitionLocationConvertor.languageServerRange.fromTextSpan(
definition.contextSpan
);
}
return {
fileName: filePath,
targetRange: range,
targetSelectionRange: contextRange ?? range
};
});
}
function proxyGetDefinitionAndBoundSpanToConvert(adapter, project) {
const languageService = project.getLanguageService();
const getDefinitionAndBoundSpan = languageService.getDefinitionAndBoundSpan;
languageService.getDefinitionAndBoundSpan = (fileName, position) => {
const originalRet = getDefinitionAndBoundSpan.call(languageService, fileName, position);
if (!originalRet?.definitions) {
return;
}
originalRet.definitions.forEach((definition) => {
if (isQingkuaiFileName(definition.fileName)) {
const definitionLocationConvertor = adapter.service.createLocationConvertor(
definition.fileName
);
definition.textSpan = definitionLocationConvertor.textSpan.toSourceTextSpan(
definition.textSpan
);
definition.contextSpan = definition.contextSpan && definitionLocationConvertor.textSpan.toSourceTextSpan(definition.contextSpan);
}
});
return originalRet;
};
}
function proxyGetTypeDefinitionAtPositionToConvert(adapter, project) {
const languageService = project.getLanguageService();
const getTypeDefinitionAtPosition = languageService.getTypeDefinitionAtPosition;
languageService.getTypeDefinitionAtPosition = (fileName, position) => {
const originalRet = getTypeDefinitionAtPosition.call(languageService, fileName, position);
originalRet?.forEach((definition) => {
if (isQingkuaiFileName(definition.fileName)) {
const definitionLocationConvertor = adapter.service.createLocationConvertor(
definition.fileName
);
definition.textSpan = definitionLocationConvertor.textSpan.toSourceTextSpan(
definition.textSpan
);
definition.contextSpan = definition.contextSpan && definitionLocationConvertor.textSpan.toSourceTextSpan(definition.contextSpan);
}
});
return originalRet;
};
}
function getAndConvertReferences(adapter, params) {
const result = [];
const filePath = adapter.getNormalizedPath(params.fileName);
const languageService = adapter.getDefaultLanguageService(filePath);
if (!debugAssert(languageService)) {
return null;
}
const data = languageService.findReferences(filePath, params.pos);
data?.forEach((item) => {
item.references.forEach((reference) => {
if (reference.isDefinition) {
return;
}
const referenceLocationConvertor = adapter.service.createLocationConvertor(
reference.fileName
);
result.push({
range: referenceLocationConvertor.languageServerRange.fromSourceTextSpan(
reference.textSpan
),
fileName: adapter.getNormalizedPath(reference.fileName)
});
});
});
return result;
}
function proxyFindReferencesToConvert(adapter, project) {
const languageService = project.getLanguageService();
const findReferences = languageService.findReferences;
languageService.findReferences = (fileName, position) => {
const originalRet = findReferences.call(languageService, fileName, position);
originalRet?.forEach((item) => {
item.references = item.references.filter((reference) => {
if (!isQingkuaiFileName(reference.fileName)) {
return true;
}
reference.textSpan.start;
const referenceLocationConvertor = adapter.service.createLocationConvertor(
reference.fileName
);
reference.textSpan = referenceLocationConvertor.textSpan.toSourceTextSpan(
reference.textSpan
);
reference.contextSpan = reference.contextSpan && referenceLocationConvertor.textSpan.toSourceTextSpan(reference.contextSpan);
return reference.textSpan !== referenceLocationConvertor.textSpan.defaultValue;
});
});
return originalRet;
};
}
function findAndConvertImplementations(adapter, params) {
const filePath = adapter.getNormalizedPath(params.fileName);
const languageService = adapter.getDefaultLanguageService(filePath);
if (!debugAssert(languageService)) {
return null;
}
const implementations = languageService.getImplementationAtPosition(filePath, params.pos);
if (!implementations) {
return null;
}
return implementations.map((implementation) => {
const implementationLocationConvertor = adapter.service.createLocationConvertor(
implementation.fileName
);
return {
range: implementationLocationConvertor.languageServerRange.fromSourceTextSpan(
implementation.textSpan
),
fileName: adapter.getNormalizedPath(implementation.fileName)
};
});
}
function proxyGetImplementationAtPositionToConvert(adapter, project) {
const languageService = project.getLanguageService();
const getImplementationsAtPosition = languageService.getImplementationAtPosition;
languageService.getImplementationAtPosition = (fileName, position) => {
const originalRet = getImplementationsAtPosition.call(languageService, fileName, position);
return originalRet?.filter((implementation) => {
if (!isQingkuaiFileName(implementation.fileName)) {
return true;
}
implementation.textSpan.start;
const implementationLocationConvertor = adapter.service.createLocationConvertor(
implementation.fileName
);
implementation.contextSpan = implementation.contextSpan && implementationLocationConvertor.textSpan.toSourceTextSpan(
implementation.contextSpan
);
implementation.textSpan = implementationLocationConvertor.textSpan.toSourceTextSpan(
implementation.textSpan
);
return implementation.textSpan !== implementationLocationConvertor.textSpan.defaultValue;
});
};
}
function getAndConvertHoverTip(adapter, { fileName, pos }) {
const fileInfo = adapter.service.ensureGetQingkuaiFileInfo(fileName);
const program = adapter.getDefaultProgram(fileInfo.path);
if (!debugAssert(program)) {
return null;
}
let idStatusDisplay = "";
const typeChecker = program.getTypeChecker();
const config = adapter.getQingkuaiConfig(fileInfo.path);
const languageService = adapter.getDefaultLanguageService(fileInfo.path);
const node = getNodeAtPositionAndWithin(program.getSourceFile(fileName), pos);
if (node && ts.isIdentifier(node)) {
const nodeRange = [node.getStart(), node.getEnd()];
if (config?.hoverTipReactiveStatus && fileInfo.idStatusInfo[node.text]) {
const symbol = typeChecker.getSymbolAtLocation(node);
if (symbol && symbol.declarations && !(symbol.flags & ts.SymbolFlags.Alias) && symbol.declarations.some((decl) => isInTopScope(decl))) {
const statusInfo = fileInfo.idStatusInfo[node.text];
idStatusDisplay = ` // - ${statusInfo} -`;
}
}
if (node.text.startsWith(constants.PRESERVED_IDPREFIX)) {
return {
content: "any",
range: nodeRange
};
}
if (node.parent && ts.isPropertyAccessExpression(node.parent) && node.parent.expression.getText() === constants.LSC.UTIL) {
return {
content: "any",
range: nodeRange
};
}
}
const ret = languageService.getQuickInfoAtPosition(fileName, pos);
if (isUndefined(ret)) {
return null;
}
const { start, length } = ret.textSpan;
const documentation = convertDisplayPartsToPlainTextWithLink(ret.documentation);
const display = convertDisplayPartsToPlainTextWithLink(ret.displayParts).replace(
LSU_AND_DOT,
""
);
return {
range: [start, start + length],
content: mdCodeBlockGen("ts", display + idStatusDisplay) + "\n" + documentation
};
}
function proxyGetQuickInfoAtPosition(_, project) {
const languageService = project.getLanguageService();
const getQuickInfoAtPosition = languageService.getQuickInfoAtPosition;
languageService.getQuickInfoAtPosition = (fileName, position) => {
const originalRet = getQuickInfoAtPosition.call(languageService, fileName, position);
if (originalRet?.displayParts) {
originalRet.displayParts = filterDisplayParts(originalRet.displayParts);
}
if (originalRet?.documentation) {
originalRet.documentation = filterDisplayParts(originalRet.documentation);
}
return originalRet;
};
}
function filterDisplayParts(target) {
return target?.filter((item, index) => {
switch (item.text) {
case constants.LSC.UTIL:
case constants.LSC.COMPONENT:
case constants.PRESERVED_IDPREFIX:
case constants.LSC.GET_TYPE_DELAY_MARKING: {
return false;
}
case ".": {
return target[index - 1].text !== constants.LSC.UTIL;
}
default: {
return true;
}
}
});
}
function proxyProject(adapter, project) {
const projectAny = project;
if (!projectAny[PROXIED_MARK]) {
projectAny[PROXIED_MARK] = true;
for (const proxyFn of [
proxyGetScriptVersion,
proxyGetScriptKind,
proxyGetScriptSnapshot,
proxyGetQuickInfoAtPosition,
proxyFindReferencesToConvert,
proxyResolveModuleNameLiterals,
proxyGetCompletionsAtPositionToConvert,
proxyGetCompletionEntryDetailsToConvert,
proxyGetDefinitionAndBoundSpanToConvert,
proxyGetTypeDefinitionAtPositionToConvert,
proxyGetImplementationAtPositionToConvert
]) {
proxyFn(adapter, project);
}
}
}
function proxyGetScriptSnapshot(adapter, languageServiceHost) {
const getScriptSnapshot = languageServiceHost.getScriptSnapshot;
languageServiceHost.getScriptSnapshot = (fileName) => {
const originalRet = getScriptSnapshot?.call(languageServiceHost, fileName);
if (!isQingkuaiFileName(fileName)) {
return originalRet;
}
return adapter.ts.ScriptSnapshot.fromString(
adapter.service.ensureGetQingkuaiFileInfo(fileName).code
);
};
}
function proxyGetScriptVersion(adapter, languageServiceHost) {
const getScriptVersion = languageServiceHost.getScriptVersion;
languageServiceHost.getScriptVersion = (fileName) => {
if (!isQingkuaiFileName(fileName)) {
return getScriptVersion?.call(languageServiceHost, fileName) ?? "";
}
return adapter.service.ensureGetQingkuaiFileInfo(fileName).version.toString();
};
}
function proxyGetScriptKind(adapter, languageServiceHost) {
const getScriptKind = languageServiceHost.getScriptKind;
if (getScriptKind) {
languageServiceHost.getScriptKind = (fileName) => {
if (!isQingkuaiFileName(fileName)) {
return getScriptKind.call(languageServiceHost, fileName);
}
return adapter.service.ensureGetQingkuaiFileInfo(fileName).isTS ? adapter.ts.ScriptKind.TS : adapter.ts.ScriptKind.JS;
};
}
}
function proxyResolveModuleNameLiterals(adapter, languageServiceHost) {
const resolveModuleLiterals = languageServiceHost.resolveModuleNameLiterals;
if (isUndefined(resolveModuleLiterals)) {
return;
}
languageServiceHost.resolveModuleNameLiterals = (moduleLiterals, containingFile, ...rest) => {
const originalRet = resolveModuleLiterals.call(
languageServiceHost,
moduleLiterals,
containingFile,
...rest
);
const containingFileInfo = isQingkuaiFileName(containingFile) ? adapter.service.ensureGetQingkuaiFileInfo(containingFile) : void 0;
const importByQingkuaiFile = isQingkuaiFileName(containingFile);
const containingFilePath = adapter.getNormalizedPath(containingFile);
const qingkuaiConfig = adapter.getQingkuaiConfig(containingFilePath);
const dirPath = adapter.getNormalizedPath(adapter.path.dir(containingFilePath));
const importedQingkuaiModuleTexts = /* @__PURE__ */ new Set();
adapter.resolvedQingkuaiModules.set(containingFilePath, importedQingkuaiModuleTexts);
const ret = originalRet.map((item, index) => {
const moduleText = moduleLiterals[index].text;
const isDirectory = isEmptyString(adapter.path.ext(moduleText));
const inferredAsQingkuaiFile = importByQingkuaiFile && isDirectory && qingkuaiConfig?.resolveImportExtension;
const modulePath = adapter.getNormalizedPath(
adapter.path.resolve(dirPath, moduleText + (inferredAsQingkuaiFile ? ".qk" : ""))
);
const moduleFileInfo = isQingkuaiFileName(modulePath) && adapter.fs.exist(modulePath) ? adapter.service.ensureGetQingkuaiFileInfo(modulePath) : void 0;
if (!moduleFileInfo || modulePath === containingFileInfo?.path) {
if (inferredAsQingkuaiFile && !item.resolvedModule) {
(item.failedLookupLocations ?? (item.failedLookupLocations = [])).push(modulePath);
}
return item;
}
importedQingkuaiModuleTexts.add(moduleText);
return {
...item,
resolvedModule: {
isExternalLibraryImport: false,
resolvedUsingTsExtension: false,
extension: moduleFileInfo.isTS ? ".ts" : ".js",
resolvedFileName: adapter.getNormalizedPath(modulePath)
},
failedLookupLocations: void 0
};
});
return ret;
};
}
function getNavigationTree(adapter, fileName) {
const filePath = adapter.getNormalizedPath(fileName);
const languageService = adapter.getDefaultLanguageService(filePath);
if (!languageService) {
return null;
}
return languageService.getNavigationTree(filePath);
}
function getComponentInfos(adapter, filePath) {
const sourceFile = adapter?.getDefaultSourceFile(filePath);
if (!debugAssert(sourceFile)) {
return [];
}
const dirPath = adapter.path.dir(filePath);
const config = adapter.getQingkuaiConfig(filePath);
const importedQingkuaiFileNames = /* @__PURE__ */ new Set();
const componentInfos = [];
const qingkuaiModules = adapter.resolvedQingkuaiModules.get(filePath);
walkTsNode(sourceFile, (node) => {
if (adapter.ts.isImportDeclaration(node) && isInTopScope(node)) {
if (!isUndefined(node.importClause?.name)) {
const identifierName = node.importClause.name.text;
if (adapter.ts.isStringLiteral(node.moduleSpecifier) && qingkuaiModules?.has(node.moduleSpecifier.text)) {
let relative = adapter.getNormalizedPath(node.moduleSpecifier.text);
let absolute = adapter.path.resolve(dirPath, node.moduleSpecifier.text);
const extension = !isQingkuaiFileName(relative) ? ".qk" : "";
const targetFileInfo = adapter.service.ensureGetQingkuaiFileInfo(
absolute + extension
);
componentInfos.push({
imported: true,
name: identifierName,
absolutePath: targetFileInfo.path,
relativePath: relative + extension,
slotNames: targetFileInfo.slotNames