qingkuai-language-service
Version:
The language service for qingkuai
1,413 lines (1,387 loc) • 52.6 kB
JavaScript
import { util, PositionFlag, commonMessage as commonMessage$1 } from 'qingkuai/compiler';
import { commonMessage } from 'qingkuai';
let ts;
let fs;
let path;
let typeRefStatement;
let typeDeclarationFilePath;
let getConfig;
let getFullFileNames;
let getUserPreferences;
let getCompileInfo;
let getLineAndCharacter;
let getTsLanguageService;
let getFormattingOptions;
let getInterIndexByLineAndCharacter;
const tsFileNameToRealPath = /* @__PURE__ */ new Map();
const resolvedQingkuaiModule = /* @__PURE__ */ new Map();
const qingkuaiDiagnostics = /* @__PURE__ */ new Map();
function createLsAdapter(options) {
ts = options.ts;
fs = options.fs;
path = options.path;
getConfig = options.getConfig;
getCompileInfo = options.getCompileInfo;
getFullFileNames = options.getFullFileNames;
getUserPreferences = options.getUserPreferences;
getLineAndCharacter = options.getLineAndCharacter;
getTsLanguageService = options.getTsLanguageService;
getFormattingOptions = options.getFormattingOptions;
typeDeclarationFilePath = options.typeDeclarationFilePath;
getInterIndexByLineAndCharacter = options.getInterIndexByLineAndCharacter;
typeRefStatement = `import {__c__,wat,waT,Wat,der,stc,rea} from "${typeDeclarationFilePath}"
`;
}
const endingInvalidStrRE = /\s*;?$/;
const qkExtInImportRE = /\.qk(['"]\s*)$/;
const reactCompilerFuncRE = /^(?:rea|stc|der)$/;
const watchCompilerFuncRE = /^(?:wat|Wat|waT)$/;
const globalTypeIdentifierRE = /^(Props|Refs)$/;
const addImportTextEditRE = /^\s*import .* from/;
const validIdentifierRE = /^[a-zA-Z_$][a-zA-Z_$\d]*$/;
const existingTopScopeIdentifierRE = /^(?:rea|stc|der|wat|Wat|waT|props|refs)$/;
function debugAssert(v) {
if (!v) {
debugger;
}
}
function isNull(v) {
return v === null;
}
function isString(v) {
return typeof v === "string";
}
function isNumber(v) {
return typeof v === "number";
}
function isEmptyString(v) {
return v === "";
}
function isUndefined(v) {
return v === void 0;
}
function isQingkuaiFileName(fileName) {
return fileName.endsWith(".qk");
}
function getKindName(node) {
return ts.SyntaxKind[node.kind];
}
function getLength(nodeOrText) {
if (!isString(nodeOrText)) {
nodeOrText = nodeOrText.getText();
}
return nodeOrText.replace(endingInvalidStrRE, "").length;
}
function isInTopScope(node) {
while (!ts.isSourceFile(node)) {
node = node.parent;
if (ts.isBlock(node) || ts.isCaseBlock(node) || ts.isModuleBlock(node) || ts.isClassExpression(node) || ts.isClassDeclaration(node) || ts.isClassStaticBlockDeclaration(node)) {
return false;
}
}
return true;
}
function findAncestorUntil(node, kind) {
while (node.kind !== kind) {
node = node.parent;
if (!node) {
return void 0;
}
}
return node;
}
function isEventType(type) {
if (type.isClass()) {
return false;
}
if (type.isUnion()) {
return type.types.some(isEventType);
}
return !!(type.getCallSignatures().length || type.symbol?.name === "Function");
}
function findVariableDeclarationOfReactFunc(node) {
const { parent } = node;
if (ts.isAsExpression(parent) || ts.isSatisfiesExpression(parent) || ts.isTypeAssertionExpression(parent) || ts.isParenthesizedExpression(parent)) {
return findVariableDeclarationOfReactFunc(parent.parent);
}
return ts.isVariableDeclaration(parent) ? parent : null;
}
function isDeclarationOfGlobalType(symbol) {
return symbol?.declarations?.length === 1 && symbol.declarations[0].getSourceFile().fileName === typeDeclarationFilePath;
}
function walk$1(node, callback) {
if (!isUndefined(node)) {
callback(node), ts.forEachChild(node, (cn) => walk$1(cn, callback));
}
}
function getNodeAt(node, pos) {
const [start, len] = [node.getStart(), node.getWidth()];
if (pos >= start && pos <= start + len) {
for (const child of node.getChildren()) {
const foundInChild = getNodeAt(child, pos);
if (foundInChild) {
return foundInChild;
}
}
return node;
}
return void 0;
}
function isBuiltInGlobalDeclaration(node, typeChecker) {
const symbol = typeChecker.getSymbolAtLocation(node);
for (const declaration of symbol?.declarations || []) {
const variableDeclaration = findAncestorUntil(
declaration,
ts.SyntaxKind.VariableDeclaration
);
return variableDeclaration && isInTopScope(variableDeclaration);
}
}
function findNodeAtPosition(sourceFile, position) {
function find(node) {
if (position >= node.getStart() && position < node.getEnd()) {
return ts.forEachChild(node, find) || node;
}
return void 0;
}
return find(sourceFile);
}
var tsAst = /*#__PURE__*/Object.freeze({
__proto__: null,
findAncestorUntil: findAncestorUntil,
findNodeAtPosition: findNodeAtPosition,
findVariableDeclarationOfReactFunc: findVariableDeclarationOfReactFunc,
getKindName: getKindName,
getLength: getLength,
getNodeAt: getNodeAt,
isBuiltInGlobalDeclaration: isBuiltInGlobalDeclaration,
isDeclarationOfGlobalType: isDeclarationOfGlobalType,
isEventType: isEventType,
isInTopScope: isInTopScope,
walk: walk$1
});
function filePathToComponentName(path, filePath) {
let base = 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 isIndexesInvalid(...items) {
return items.some((item) => {
return !item || item === -1;
});
}
function getRealPath(fileName) {
return tsFileNameToRealPath.get(fileName) || fileName;
}
function isPositionFlagSetBySourceIndex(fileName, sourceIndex, key) {
return !!(getCompileInfo(fileName).positions[sourceIndex].flag & PositionFlag[key]);
}
function isPositionFlagSetByInterIndex(fileName, interIndex, key, isEnd = false) {
const sourceIndex = getSourceIndex(fileName, interIndex, isEnd);
return !isIndexesInvalid(sourceIndex) && isPositionFlagSetBySourceIndex(fileName, sourceIndex, key);
}
function isComponentIdentifier(fileName, identifier, typeChecker) {
const symbol = typeChecker.getSymbolAtLocation(identifier);
if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
for (const declaration of symbol.declarations || []) {
if (ts.isImportClause(declaration) && ts.isImportDeclaration(declaration.parent) && ts.isStringLiteral(declaration.parent.moduleSpecifier)) {
const resolvedModules = resolvedQingkuaiModule.get(fileName);
return !!resolvedModules?.has(declaration.parent.moduleSpecifier.text);
}
}
}
return isInTopScope(identifier) && identifier.getText() === filePathToComponentName(path, fileName);
}
function recordRealPath(realFileName) {
if (isQingkuaiFileName(realFileName) && !tsFileNameToRealPath.has(realFileName)) {
const normalizedPath = ts.server.toNormalizedPath(realFileName).toString();
if (normalizedPath !== realFileName) {
tsFileNameToRealPath.set(normalizedPath, realFileName);
}
}
}
function getSourceIndex(fileName, interIndex, isEnd = false) {
if (!isQingkuaiFileName(fileName)) {
return interIndex;
}
const { itos } = getCompileInfo(fileName);
const sourceIndex = itos[interIndex];
if (!isEnd || !isIndexesInvalid(sourceIndex)) {
return sourceIndex ?? -1;
}
const preSourceIndex = itos[interIndex - 1];
return isIndexesInvalid(sourceIndex) ? -1 : preSourceIndex + 1;
}
var qingkuai = /*#__PURE__*/Object.freeze({
__proto__: null,
getRealPath: getRealPath,
getSourceIndex: getSourceIndex,
isComponentIdentifier: isComponentIdentifier,
isPositionFlagSetByInterIndex: isPositionFlagSetByInterIndex,
isPositionFlagSetBySourceIndex: isPositionFlagSetBySourceIndex,
recordRealPath: recordRealPath
});
function proxyGetCompletionsAtPosition(languageService) {
const getCompletionsAtPosition = languageService.getCompletionsAtPosition;
languageService.getCompletionsAtPosition = (fileName, position, ...rest) => {
const originalResult = getCompletionsAtPosition.call(
languageService,
fileName,
position,
...rest
);
if (originalResult?.entries) {
originalResult.entries = originalResult.entries.filter((entry) => {
return !entry.source || !typeDeclarationFilePath.startsWith(
path.resolve(path.dir(fileName), entry.source)
);
});
}
return originalResult;
};
}
function proxyResolveModuleNameLiterals(languageServiceHost, couldBeImpoted) {
const resolveModuleLiterals = languageServiceHost.resolveModuleNameLiterals;
if (isUndefined(resolveModuleLiterals)) {
return;
}
languageServiceHost.resolveModuleNameLiterals = (moduleLiterals, containingFile, ...rest) => {
const containingFileRealPath = getRealPath(containingFile);
const originalRet = resolveModuleLiterals.call(
languageServiceHost,
moduleLiterals,
containingFile,
...rest
);
const qingkuaiModules = /* @__PURE__ */ new Set();
const curDir = path.dir(containingFileRealPath);
const config = getConfig(containingFileRealPath);
const fromQk = isQingkuaiFileName(containingFile);
const ret = originalRet.map((item, index) => {
const moduleText = moduleLiterals[index].text;
const isDirectory = isEmptyString(path.ext(moduleText));
const resolveAsQk = fromQk && isDirectory && config?.resolveImportExtension;
const modulePath = path.resolve(curDir, moduleText + (resolveAsQk ? ".qk" : ""));
if (!isQingkuaiFileName(modulePath)) {
return item;
}
if (!fs.exist(modulePath)) {
return {
...item,
failedLookupLocations: [modulePath]
};
}
if (!couldBeImpoted(modulePath)) {
return item;
}
qingkuaiModules.add(moduleText);
const scriptKind = languageServiceHost.getScriptKind?.(modulePath);
return {
...item,
resolvedModule: {
isExternalLibraryImport: false,
resolvedUsingTsExtension: false,
resolvedFileName: ts.server.toNormalizedPath(modulePath),
extension: scriptKind === ts.ScriptKind.TS ? ".ts" : ".js"
},
failedLookupLocations: void 0
};
});
if (qingkuaiModules.size) {
resolvedQingkuaiModule.set(containingFileRealPath, qingkuaiModules);
}
return ret;
};
}
var proxies = /*#__PURE__*/Object.freeze({
__proto__: null,
proxyGetCompletionsAtPosition: proxyGetCompletionsAtPosition,
proxyResolveModuleNameLiterals: proxyResolveModuleNameLiterals
});
const DEFAULT_POSITION = {
line: 0,
character: 0
};
const DEFAULT_RANGE = {
start: DEFAULT_POSITION,
end: DEFAULT_POSITION
};
({
...DEFAULT_RANGE});
const INTER_NAMESPACE = "__c__";
const TS_REFS_DECLARATION_LEN = 27;
const JS_REFS_DECLARATION_LEN = 32;
const TS_PROPS_DECLARATION_LEN = 28;
const JS_PROPS_DECLARATION_LEN = 33;
var GlobalTypeIdentifier = /* @__PURE__ */ ((GlobalTypeIdentifier2) => {
GlobalTypeIdentifier2["Ref"] = "Refs";
GlobalTypeIdentifier2["Prop"] = "Props";
return GlobalTypeIdentifier2;
})(GlobalTypeIdentifier || {});
const GLOBAL_TYPE_IDNTIFIERS = /* @__PURE__ */ new Set([
"Refs" /* Ref */,
"Props" /* Prop */
]);
const GLOBAL_BUILTIN_VARS = /* @__PURE__ */ new Set(["props", "refs"]);
const COMPILER_FUNCS = /* @__PURE__ */ new Set(["rea", "der", "stc", "wat", "Wat", "waT"]);
const SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".d.ts", ".ts", ".tsx", ".js", ".jsx", ".json"]);
function stringify(v) {
return JSON.stringify(v);
}
function ensureExport(languageService, fileName, content, typeDeclarationLen) {
const program = languageService.getProgram();
const typeChecker = program.getTypeChecker();
const sourceFile = program.getSourceFile(fileName);
debugAssert(!!sourceFile);
const dirPath = path.dir(fileName);
const config = getConfig(fileName);
const contentArr = content.split("");
const existingGlobalType = /* @__PURE__ */ new Set();
const diagnosticsCache = [];
const compileInfo = getCompileInfo(fileName);
const storedTypes = /* @__PURE__ */ new Map();
const importedQingkuaiFileNames = /* @__PURE__ */ new Set();
const typeRefStatementLen = typeRefStatement.length;
const slotInfoKeys = Object.keys(compileInfo.slotInfo);
const isTS = compileInfo.scriptKind === ts.ScriptKind.TS;
const qingkuaiModules = resolvedQingkuaiModule.get(fileName);
const componentName = filePathToComponentName(path, fileName);
const componentIdentifierInfos = [];
const builtInTypeDeclarationEndIndex = typeRefStatement.length + typeDeclarationLen;
slotInfoKeys.forEach((slotName) => {
compileInfo.slotInfo[slotName].properties.forEach((property) => {
if (isNumber(property[2])) {
storedTypes.set(property[2], "");
}
});
});
qingkuaiDiagnostics.set(fileName, diagnosticsCache);
const eliminateContentByLength = (start, length) => {
for (let i = 0; i < length; i++) {
contentArr[start + i] = " ";
}
};
const eliminateContentByRange = (start, end) => {
for (let i = start; i < end; i++) {
contentArr[i] = " ";
}
};
const markGlobalTypeExisting = (identifierName, startIndex) => {
if (startIndex > builtInTypeDeclarationEndIndex && globalTypeIdentifierRE.test(identifierName)) {
existingGlobalType.add(identifierName);
}
};
const recordQingkuaiDiagnostic = (start, length, category, message) => {
if (isUndefined(compileInfo.itos[start]) || compileInfo.itos[start] === -1) {
return;
}
diagnosticsCache.push({
start,
length,
category,
source: "qk",
file: void 0,
code: message.code,
messageText: message.msg
});
};
walk$1(sourceFile, (node) => {
if (ts.isBinaryExpression(node) && storedTypes.has(node.right.pos) && node.left.getText() === INTER_NAMESPACE + ".Receiver" && node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
const type = typeChecker.getTypeAtLocation(node.right);
const typeStr = typeChecker.typeToString(type);
storedTypes.set(node.right.pos, typeStr);
}
if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && isComponentIdentifier(fileName, node.expression, typeChecker) && isPositionFlagSetByInterIndex(fileName, node.getStart(), "inScript")) {
eliminateContentByRange(node.getStart(), node.expression.getStart());
eliminateContentByRange(node.expression.getEnd(), node.getEnd());
contentArr[node.expression.getStart() - 1] = ";";
contentArr[node.getEnd() - 1] = ";";
recordQingkuaiDiagnostic(
node.getStart(),
getLength(node),
ts.DiagnosticCategory.Error,
getRuntimeCommonMessage("InstantiateComponentManually")
);
}
if (!isTS && existingGlobalType.size < 2) {
const typeDefNodes = ts.getJSDocTags(node).filter((jsDocNode) => {
return ts.isJSDocTypedefTag(jsDocNode);
});
if (typeDefNodes.length > 0 && isInTopScope(node)) {
typeDefNodes.forEach((typeDefNode) => {
markGlobalTypeExisting(
ts.getNameOfJSDocTypedef(typeDefNode)?.text || "",
typeDefNode.getStart()
);
});
}
}
if (isTS && (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node))) {
isInTopScope(node) && markGlobalTypeExisting(node.name.text, node.getStart());
}
if (isTS && ts.isImportDeclaration(node) && isInTopScope(node)) {
if (!isUndefined(node.importClause?.name)) {
const identifierName = node.importClause.name.text;
if (globalTypeIdentifierRE.test(identifierName)) {
const symbol = typeChecker.getSymbolAtLocation(node.importClause.name);
const aliasedSymbol = symbol && typeChecker.getAliasedSymbol(symbol);
if (aliasedSymbol && aliasedSymbol.flags & ts.SymbolFlags.Type) {
existingGlobalType.add(identifierName);
}
}
if (ts.isStringLiteral(node.moduleSpecifier) && qingkuaiModules?.has(node.moduleSpecifier.text)) {
let componentFileName = path.resolve(dirPath, node.moduleSpecifier.text);
if (!isQingkuaiFileName(componentFileName)) {
componentFileName += ".qk";
}
componentIdentifierInfos.push({
imported: true,
name: identifierName,
relativePath: node.moduleSpecifier.text,
slotNams: getComponentSlotNames(componentFileName),
attributes: getComponentAttributes(componentFileName)
});
importedQingkuaiFileNames.add(componentFileName);
}
}
if (!isUndefined(node.importClause?.namedBindings) && ts.isNamedImports(node.importClause.namedBindings)) {
for (const element of node.importClause.namedBindings.elements) {
if (!globalTypeIdentifierRE.test(element.name.text)) {
continue;
}
const symbol = typeChecker.getSymbolAtLocation(element.name);
const aliasedSymbol = symbol && typeChecker.getAliasedSymbol(symbol);
if (aliasedSymbol && aliasedSymbol.flags & ts.SymbolFlags.Type) {
existingGlobalType.add(element.name.text);
}
}
}
}
if (ts.isIdentifier(node) && util.isBannedIdentifier(node.text)) {
recordQingkuaiDiagnostic(
node.getStart(),
getLength(node),
ts.DiagnosticCategory.Error,
getCompilerCommonMessage("IdentifierFormatIsNotAllowed", node.text)
);
}
if (ts.isExportAssignment(node) || node.modifiers?.some((m) => {
return m.kind === ts.SyntaxKind.ExportKeyword;
})) {
const [start, length] = [node.getStart(), getLength(node)];
recordQingkuaiDiagnostic(
start,
length,
ts.DiagnosticCategory.Error,
getCompilerCommonMessage("BadExportRelatedStatement")
);
if (ts.isExportAssignment(node)) {
eliminateContentByLength(start, length);
}
}
if (ts.isEnumDeclaration(node) || ts.isClassDeclaration(node) || ts.isModuleDeclaration(node) || ts.isVariableDeclaration(node) || ts.isFunctionDeclaration(node)) {
if (!isUndefined(node.name)) {
const identifierName = node.name.getText();
if (identifierName === "$arg") {
if (isInTopScope(node)) {
recordQingkuaiDiagnostic(
node.name.getStart(),
getLength(node.name),
ts.DiagnosticCategory.Warning,
getCompilerCommonMessage("IdentifierMaybeOverwritten", "$arg")
);
}
} else if (existingTopScopeIdentifierRE.test(identifierName)) {
recordQingkuaiDiagnostic(
node.name.getStart(),
getLength(node.name),
ts.DiagnosticCategory.Error,
getCompilerCommonMessage("RegisterExsitingIdentifierName", identifierName)
);
}
}
}
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && COMPILER_FUNCS.has(node.expression.text)) ;
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && COMPILER_FUNCS.has(node.expression.text)) {
const funcName = node.expression.text;
if (reactCompilerFuncRE.test(funcName)) {
const commonDiagnosticArgs = [
node.getStart(),
getLength(node),
ts.DiagnosticCategory.Error
];
if (!isInTopScope(node)) {
recordQingkuaiDiagnostic(
...commonDiagnosticArgs,
getCompilerCommonMessage("ReactCompilerFuncNotInTopScope")
);
}
const variableDeclarationNode = findVariableDeclarationOfReactFunc(node);
if (isNull(variableDeclarationNode)) {
recordQingkuaiDiagnostic(
...commonDiagnosticArgs,
getCompilerCommonMessage("ReactCompilerFuncWithoutVariableDeclaration")
);
} else {
if (
// prettier-ignore
node.arguments.length === 0 && (ts.isArrayBindingPattern(variableDeclarationNode.name) || ts.isObjectBindingPattern(variableDeclarationNode.name))
) {
recordQingkuaiDiagnostic(
node.getStart(),
getLength(node),
ts.DiagnosticCategory.Error,
getCompilerCommonMessage("DestructureReactFuncWithNoArg", funcName)
);
}
if (config?.convenientDerivedDeclaration && variableDeclarationNode.name.getText().startsWith("$")) {
if (funcName === "der") {
recordQingkuaiDiagnostic(
variableDeclarationNode.getStart(),
getLength(variableDeclarationNode),
ts.DiagnosticCategory.Warning,
getCompilerCommonMessage("MixTwoSyntaxOfDerived")
);
} else {
recordQingkuaiDiagnostic(
variableDeclarationNode.getStart(),
getLength(variableDeclarationNode),
ts.DiagnosticCategory.Error,
getCompilerCommonMessage(
"ConvenientDerivedWithOtherReactFunc",
funcName
)
);
}
}
if (!isTS && node.arguments.length > 1 && (funcName === "der" || funcName === "stc")) {
recordQingkuaiDiagnostic(
node.getStart(),
getLength(node),
ts.DiagnosticCategory.Warning,
getCompilerCommonMessage("RedundantArgsForCompilerFunc", funcName, 1)
);
}
if (!isTS && funcName === "rea" && node.arguments.length > 2) {
recordQingkuaiDiagnostic(
node.getStart(),
getLength(node),
ts.DiagnosticCategory.Warning,
getCompilerCommonMessage("RedundantArgsForCompilerFunc", funcName, 2)
);
}
}
} else if (watchCompilerFuncRE.test(funcName)) {
if (node.arguments.length < 2) {
recordQingkuaiDiagnostic(
node.getStart(),
getLength(node),
ts.DiagnosticCategory.Error,
getCompilerCommonMessage(
"WatchCompilerFuncMissingArg",
funcName,
node.arguments.length
)
);
} else if (!isTS && node.arguments.length > 2) {
recordQingkuaiDiagnostic(
node.getStart(),
getLength(node),
ts.DiagnosticCategory.Warning,
getCompilerCommonMessage("RedundantArgsForCompilerFunc", funcName, 2)
);
}
}
}
});
if (existingGlobalType.has(GlobalTypeIdentifier.Ref)) {
if (isTS) {
eliminateContentByLength(
typeRefStatementLen + TS_PROPS_DECLARATION_LEN,
TS_REFS_DECLARATION_LEN + 2
);
} else {
eliminateContentByLength(
typeRefStatementLen + JS_PROPS_DECLARATION_LEN + 1,
JS_REFS_DECLARATION_LEN
);
}
}
if (existingGlobalType.has(GlobalTypeIdentifier.Prop)) {
if (isTS) {
eliminateContentByLength(typeRefStatementLen, TS_PROPS_DECLARATION_LEN);
} else {
eliminateContentByLength(typeRefStatementLen + 3, JS_PROPS_DECLARATION_LEN);
}
}
const slotType = slotInfoKeys.reduce((ret, slotName, i) => {
const { properties } = compileInfo.slotInfo[slotName];
const key = validIdentifierRE.test(slotName) ? slotName : stringify(slotName);
const value = properties.reduce((ret2, property, i2) => {
const [name, _, iot] = property;
const key2 = validIdentifierRE.test(name) ? name : stringify(name);
const value2 = isString(iot) ? stringify(iot) : storedTypes.get(iot);
return `${ret2}${key2}:${value2}${i2 === properties.length - 1 ? "}" : ","}`;
}, "{");
return `${ret}${key}:${value}${i === slotInfoKeys.length - 1 ? "" : ","}`;
}, "");
if (isTS) {
contentArr.push(`export default class ${componentName} {
constructor(
_: ${GlobalTypeIdentifier.Prop},
__: ${GlobalTypeIdentifier.Ref},
___: {${slotType}}
){}
}`);
} else {
contentArr.push(`export default class ${componentName} {
/**
* @param {${GlobalTypeIdentifier.Prop}} _
* @param {${GlobalTypeIdentifier.Ref}} __
* @param {{${slotType}}} __
*/
constructor(_, __, ___){}
}`);
}
for (const currentFileName of getFullFileNames()) {
if (isQingkuaiFileName(currentFileName) && currentFileName !== fileName.toString() && !importedQingkuaiFileNames.has(currentFileName)) {
let relativePath = getRelativePathWithStartDot(dirPath, currentFileName);
if (config?.resolveImportExtension) {
relativePath = relativePath.slice(0, -path.ext(relativePath).length);
}
componentIdentifierInfos.push({
imported: false,
attributes: [],
relativePath,
slotNams: getComponentSlotNames(currentFileName),
name: filePathToComponentName(path, currentFileName)
});
}
}
return { content: contentArr.join(""), componentIdentifierInfos };
}
function getComponentAttributes(componentFileName) {
const program = getTsLanguageService(componentFileName)?.getProgram();
debugAssert(!!program);
const sourceFile = program.getSourceFile(componentFileName);
if (!sourceFile) {
return [];
}
const attributes = [];
const typeChecker = program.getTypeChecker();
for (const [name, symbol] of sourceFile.locals) {
if (globalTypeIdentifierRE.test(name)) {
let type;
if (!ts.isJSDocTypedefTag(symbol.declarations[0])) {
type = typeChecker.getDeclaredTypeOfSymbol(symbol);
} else {
type = typeChecker.getTypeFromTypeNode(symbol.declarations[0].typeExpression);
}
if (!type.symbol || !(type.symbol.flags & ts.SymbolFlags.Type)) {
continue;
}
type.getProperties().forEach((property) => {
const stringCandidates = [];
const propertyType = typeChecker.getTypeOfSymbolAtLocation(property, sourceFile);
if (propertyType.isUnion()) {
propertyType.types.forEach((t) => {
if (t.flags & ts.TypeFlags.StringLiteral) {
stringCandidates.push(JSON.parse(typeChecker.typeToString(t)));
}
});
} else if (propertyType.flags & ts.TypeFlags.StringLiteral) {
stringCandidates.push(JSON.parse(typeChecker.typeToString(propertyType)));
}
attributes.push({
stringCandidates,
name: property.name,
kind: name.slice(0, -1),
type: typeChecker.typeToString(propertyType),
isEvent: name === GlobalTypeIdentifier.Prop && isEventType(propertyType)
});
});
}
}
return attributes;
}
function getRelativePathWithStartDot(from, to) {
const relativePath = path.relative(from, to);
return /\.{1,2}\//.test(relativePath) ? relativePath : `./${relativePath}`;
}
function getComponentSlotNames(componentFileName) {
return Object.keys(getCompileInfo(getRealPath(componentFileName)).slotInfo);
}
function getCompilerCommonMessage(key, ...args) {
return {
// @ts-ignore
msg: commonMessage$1[key][1](...args),
code: commonMessage$1[key][0]
};
}
function getRuntimeCommonMessage(key, ...args) {
return {
// @ts-ignore
msg: commonMessage[key][1](...args),
code: commonMessage[key][0]
};
}
function mdCodeBlockGen(lang, code) {
return "```" + lang + "\n" + code + "\n```";
}
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: getRealPath(target.fileName)
})
);
return ret + `[${part.text}](command:qingkuai.openFileByFilePath?${args})`;
}
return (ret + (part.kind === "link" ? "" : part.text || "")).replace("__c__.", "");
}, "");
}
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 getAndConvertHoverTip(languageService, { fileName, pos }) {
let replacer = "";
const realPath = getRealPath(fileName);
const program = languageService.getProgram();
if (!program) {
return null;
}
const typeChecker = program.getTypeChecker();
const node = getNodeAt(program.getSourceFile(fileName), pos);
if (node && ts.isIdentifier(node)) {
const nodeRange = [node.getStart(), node.getEnd()];
if (GLOBAL_BUILTIN_VARS.has(node.text) && isBuiltInGlobalDeclaration(node, typeChecker)) {
replacer = INTER_NAMESPACE + ".";
} else if (node.text === INTER_NAMESPACE) {
return {
posRange: nodeRange,
content: "any"
};
} else if (node.parent && ts.isPropertyAccessExpression(node.parent) && node.parent.expression.getText() === INTER_NAMESPACE) {
return {
posRange: nodeRange,
content: "any"
};
} else if (isComponentIdentifier(realPath, node, typeChecker)) {
return {
posRange: [node.getStart(), node.getEnd()],
content: mdCodeBlockGen("ts", `(component) class ${node.text}`)
};
}
}
if (node && node.parent && ts.isNewExpression(node.parent) && ts.isIdentifier(node.parent.expression) && isComponentIdentifier(realPath, node.parent.expression, typeChecker)) {
return {
posRange: [node.parent.expression.getStart(), node.parent.expression.getEnd()],
content: mdCodeBlockGen("ts", `(component) class ${node.parent.expression.text}`)
};
}
const ret = languageService.getQuickInfoAtPosition(fileName, pos);
if (isUndefined(ret)) {
return null;
}
const { start, length } = ret.textSpan;
const display = convertDisplayPartsToPlainTextWithLink(ret.displayParts);
const documentation = convertDisplayPartsToPlainTextWithLink(ret.documentation);
return {
posRange: [start, start + length],
content: mdCodeBlockGen("ts", display.replace(replacer, "")) + "\n" + documentation
};
}
const lineAndCharacter = {
toSource(fileName, lineAndCharacter2, isEnd = false) {
if (!isQingkuaiFileName(fileName)) {
return lineAndCharacter2;
}
const interIndex = getInterIndexByLineAndCharacter(fileName, lineAndCharacter2);
const sourceIndex = getSourceIndex(fileName, interIndex, isEnd);
if (isIndexesInvalid(sourceIndex)) {
return void 0;
}
const position = getCompileInfo(fileName).positions[sourceIndex];
return { line: position.line - 1, character: position.column };
},
toProtocolLocation(lineAndCharacter2) {
return { line: lineAndCharacter2.line + 1, offset: lineAndCharacter2.character + 1 };
}
};
const lsRange = {
fromProtocolTextSpan(fileName, span) {
const sourceStartPosition = lineAndCharacter.toSource(
fileName,
protocolLocation.toLineAndCharacter(span.start)
);
const sourceEndPosition = lineAndCharacter.toSource(
fileName,
protocolLocation.toLineAndCharacter(span.end)
);
if (!sourceStartPosition || !sourceEndPosition) {
return void 0;
}
return { start: sourceStartPosition, end: sourceEndPosition };
},
fromTextSpan(fileName, span) {
if (!isQingkuaiFileName(fileName)) {
const startLineAndCharacter = getLineAndCharacter(fileName, span.start);
const endLineAndCharacter = getLineAndCharacter(fileName, span.start + span.length);
if (!startLineAndCharacter || !endLineAndCharacter) {
return void 0;
}
return { start: startLineAndCharacter, end: endLineAndCharacter };
}
const sourceStartIndex = getSourceIndex(fileName, span.start);
const sourceEndIndex = getSourceIndex(fileName, span.start + span.length, true);
if (isIndexesInvalid(sourceStartIndex, sourceEndIndex)) {
return void 0;
}
const positions = getCompileInfo(fileName).positions;
if (!positions[sourceStartIndex] && !positions[sourceEndIndex]) {
return void 0;
}
const endPosition = positions[sourceEndIndex];
const startPosition = positions[sourceStartIndex];
return {
start: {
line: startPosition.line - 1,
character: startPosition.column
},
end: {
line: endPosition.line - 1,
character: endPosition.column
}
};
},
fromSourceStartAndEnd(fileName, sourceStart, sourceEnd) {
const positions = getCompileInfo(fileName).positions;
return {
start: {
line: positions[sourceStart].line - 1,
character: positions[sourceStart].column
},
end: {
line: positions[sourceEnd].line - 1,
character: positions[sourceEnd].column
}
};
}
};
const protocolLocation = {
toSource(fileName, location, isEnd = false) {
if (!isQingkuaiFileName(fileName)) {
return location;
}
const sourceLineAndCharacter = lineAndCharacter.toSource(
fileName,
this.toLineAndCharacter(location),
isEnd
);
return sourceLineAndCharacter && lineAndCharacter.toProtocolLocation(sourceLineAndCharacter);
},
toLineAndCharacter(location) {
return {
line: location.line - 1,
character: location.offset - 1
};
},
toSourceIndex(fileName, location) {
return getSourceIndex(
fileName,
getInterIndexByLineAndCharacter(fileName, this.toLineAndCharacter(location))
);
}
};
async function findComponentTagRanges(fileName, componentTag, getCompileRes) {
const ranges = [];
const cr = await getCompileRes(fileName);
walk(cr.templateNodes, (node) => {
if (node.componentTag === componentTag) {
ranges.push(cr.getRange(node.range[0], node.range[0] + node.tag.length + 1));
}
});
return ranges;
}
function walk(nodes, cb) {
for (const node of nodes) {
const ret = cb(node);
if (ret) {
return ret;
}
node.children.length && walk(node.children, cb);
}
}
async function findAndConvertReferences(languageService, getCompileRes, { fileName, pos }) {
const sourceFile = languageService.getProgram()?.getSourceFile(fileName);
if (!sourceFile) {
return null;
}
const result = [];
const node = findNodeAtPosition(sourceFile, pos);
if (node?.parent && GLOBAL_TYPE_IDNTIFIERS.has(node.getText()) && (ts.isTypeAliasDeclaration(node.parent) || ts.isInterfaceDeclaration(node.parent))) {
for (const ref of languageService.getFileReferences(fileName)) {
if (!isQingkuaiFileName(fileName)) {
continue;
}
const refRealPath = getRealPath(ref.fileName);
const ranges = await findComponentTagRanges(
refRealPath,
filePathToComponentName(path, fileName),
getCompileRes
);
ranges.forEach((range) => result.push({ fileName: refRealPath, range }));
}
}
const res = languageService.findReferences(fileName, pos);
for (const item of res || []) {
for (const ref of item.references) {
if (ref.isDefinition) {
continue;
}
const range = lsRange.fromTextSpan(ref.fileName, ref.textSpan);
range && result.push({ fileName: getRealPath(ref.fileName), range });
}
}
return result;
}
function getAndConvertDiagnostics(languageService, fileName, isSemanticProject) {
const diagnostics = [];
const diagnosticMethods = ["getSyntacticDiagnostics"];
if (isSemanticProject) {
diagnosticMethods.push("getSemanticDiagnostics", "getSuggestionDiagnostics");
}
diagnosticMethods.forEach((m) => {
diagnostics.push(...languageService[m](fileName));
});
const result = [];
for (const item of diagnostics.concat(qingkuaiDiagnostics.get(getRealPath(fileName)) || [])) {
const start = item.start ?? 0;
const end = start + (item.length ?? 0);
const ss = getSourceIndex(fileName, start);
const se = getSourceIndex(fileName, end, true);
if (isIndexesInvalid(ss, se)) {
continue;
}
const relatedInformations = [];
const filteredRelatedInformations = (item.relatedInformation || []).filter((ri) => !!ri.file);
for (const ri of filteredRelatedInformations) {
let range;
const start2 = ri.start ?? 0;
const end2 = start2 + (ri.length ?? 0);
const realPath = getRealPath(ri.file.fileName);
if (!isQingkuaiFileName(realPath)) {
range = {
start: getLineAndCharacter(realPath, start2),
end: getLineAndCharacter(realPath, end2)
};
} else {
const ss2 = getSourceIndex(realPath, start2);
const se2 = getSourceIndex(realPath, end2, true);
if (isIndexesInvalid(ss2, se2)) {
continue;
}
range = lsRange.fromSourceStartAndEnd(realPath, ss2, se2);
}
relatedInformations.push({
range,
filePath: getRealPath(ri.file.fileName),
message: formatDiagnosticMessage(ri.messageText)
});
}
result.push({
relatedInformations,
code: item.code,
kind: item.category,
source: item.source || "ts",
deprecated: Boolean(item.reportsDeprecated),
unnecessary: Boolean(item.reportsUnnecessary),
message: formatDiagnosticMessage(item.messageText),
range: lsRange.fromSourceStartAndEnd(fileName, ss, se)
});
}
return result;
}
function formatDiagnosticMessage(mt, indentLevel = 0) {
const indent = " ".repeat(indentLevel * 2);
const eliminate = (s) => {
return s.replace(` Did you mean '${INTER_NAMESPACE}'?`, "");
};
if (isString(mt)) {
return eliminate(mt);
}
if (isUndefined(mt.next)) {
return eliminate(mt.messageText);
}
const nextMsg = mt.next.reduce((p, c) => {
return p + indent + formatDiagnosticMessage(c, indentLevel + 1);
}, "");
return eliminate(mt.messageText) + "\n" + nextMsg;
}
function getAndConvertSignatureHelp(languageService, lsParams) {
let reason = void 0;
const { fileName, pos, isRetrigger, triggerCharacter } = lsParams;
const sourceFile = languageService.getProgram()?.getSourceFile(fileName);
if (sourceFile) {
const node = getNodeAt(sourceFile, pos);
const callee = node && findAncestorUntil(node, ts.SyntaxKind.CallExpression);
if (callee?.getText().startsWith(INTER_NAMESPACE)) {
return null;
}
}
if (isRetrigger) {
reason = {
kind: "retrigger",
triggerCharacter
};
} else if (triggerCharacter) {
reason = {
kind: "characterTyped",
triggerCharacter
};
} else {
reason = {
kind: "invoked"
};
}
const options = { triggerReason: reason };
const getSignatureHelpRes = languageService?.getSignatureHelpItems(fileName, pos, options);
if (!getSignatureHelpRes) {
return null;
}
return {
signatures: convertSignatures(getSignatureHelpRes.items),
activeParameter: getActiveParameter(getSignatureHelpRes),
activeSignature: getSignatureHelpRes.selectedItemIndex
};
}
function getActiveParameter(info) {
const activeSignature = info.items[info.selectedItemIndex];
if (activeSignature?.isVariadic) {
return Math.min(info.argumentIndex, activeSignature.parameters.length - 1);
}
return info.argumentIndex;
}
function convertSignatures(items) {
return items.map((item) => {
const signatureLabelParts = [
convertDisplayPartsToPlainTextWithLink(item.prefixDisplayParts)
];
const separate = convertDisplayPartsToPlainTextWithLink(item.separatorDisplayParts);
const parameters = item.parameters.map((p, i) => {
const parameterLabel = convertDisplayPartsToPlainTextWithLink(p.displayParts);
const documentation2 = convertDisplayPartsToPlainTextWithLink(p.documentation);
signatureLabelParts.push(
parameterLabel,
i === item.parameters.length - 1 ? "" : separate
);
const parameter = { label: parameterLabel };
if (documentation2) {
parameter.documentation = {
kind: "markdown",
value: documentation2
};
}
return parameter;
});
signatureLabelParts.push(convertDisplayPartsToPlainTextWithLink(item.suffixDisplayParts));
const signature = { label: signatureLabelParts.join(""), parameters };
const documentation = convertDisplayPartsToPlainTextWithLink(item.documentation) + convertJsDocTagsToMarkdown(item.tags.filter((t) => t.name !== "param"));
if (documentation) {
signature.documentation = {
kind: "markdown",
value: documentation
};
}
return signature;
});
}
function findAndConvertImplementations(languageService, { fileName, pos }) {
const implementations = languageService.getImplementationAtPosition(fileName, pos);
if (!implementations) {
return null;
}
const result = [];
for (const { fileName: fileName2, textSpan } of implementations) {
const range = lsRange.fromTextSpan(fileName2, textSpan);
range && result.push({ range, fileName: fileName2 });
}
return result;
}
function renameAndConvert(languageService, { fileName, pos }) {
const locations = [];
const existringMap = /* @__PURE__ */ new Map();
const renameLocations = languageService.findRenameLocations(
fileName,
pos,
false,
false,
getUserPreferences(fileName)
);
if (!renameLocations) {
return null;
}
renameLocations.forEach((item) => {
const { start, length } = item.textSpan;
const realPath = getRealPath(item.fileName);
const locationItem = { fileName: realPath };
if (item.prefixText) {
locationItem.prefix = item.prefixText;
}
if (item.suffixText) {
locationItem.suffix = item.suffixText;
}
if (isQingkuaiFileName(item.fileName)) {
const sourceStart = getSourceIndex(item.fileName, start);
const existing = existringMap.get(realPath) || /* @__PURE__ */ new Set();
const sourceEnd = getSourceIndex(item.fileName, start + length, true);
const existringKey = `${sourceStart},${sourceEnd}`;
if (isIndexesInvalid(sourceStart, sourceEnd)) {
return;
}
existing.add(existringKey);
existringMap.set(realPath, existing);
locationItem.range = [sourceStart, sourceEnd];
} else {
const startLineAndCharacter = getLineAndCharacter(item.fileName, start);
const endLineAndCharacter = getLineAndCharacter(item.fileName, start + length);
if (!startLineAndCharacter || !endLineAndCharacter) {
return;
}
locationItem.loc = { start: startLineAndCharacter, end: endLineAndCharacter };
}
locations.push(locationItem);
});
return locations;
}
function prepareRenameAndConvert(languageService, { fileName, pos }) {
const renameInfo = languageService.getRenameInfo(fileName, pos, getUserPreferences(fileName));
if (!renameInfo || !renameInfo.canRename) {
return null;
}
return [
renameInfo.triggerSpan.start,
renameInfo.triggerSpan.start + renameInfo.triggerSpan.length
];
}
function getAndConvertDefinitions(languageService, { fileName, pos }) {
const data = languageService.getDefinitionAndBoundSpan(fileName, pos);
if (!data) {
return null;
}
const originRange = lsRange.fromTextSpan(fileName, data.textSpan) || DEFAULT_RANGE;
const dealtDefinitions = (data.definitions || []).map((item) => {
const range = lsRange.fromTextSpan(item.fileName, item.textSpan);
if (item.contextSpan) {
const contextRange = lsRange.fromTextSpan(item.fileName, item.contextSpan);
return {
fileName: item.fileName,
targetRange: contextRange,
targetSelectionRange: range
};
}
return {
fileName: item.fileName,
targetRange: range,
targetSelectionRange: range
};
});
return { range: originRange, definitions: dealtDefinitions };
}
function getAndConvertTypeDefinitions(languageService, { fileName, pos }) {
const definitions = languageService.getTypeDefinitionAtPosition(fileName, pos);
if (!definitions?.length) {
return null;
}
return definitions.map((definition) => {
const realPath = getRealPath(definition.fileName);
const range = lsRange.fromTextSpan(realPath, definition.textSpan) || DEFAULT_RANGE;
let contextRange = void 0;
if (definition.contextSpan) {
contextRange = lsRange.fromTextSpan(realPath, definition.contextSpan);
}
return {
fileName: realPath,
targetRange: range,
targetSelectionRange: contextRange || range
};
});
}
function getAndConvertCompletionInfo(languageService, params) {
const completionRes = languageService.getCompletionsAtPosition(
params.fileName,
params.pos,
getUserPreferences(params.fileName),
getFormattingOptions(params.fileName)
);
if (!completionRes) {
return null;
}
const convertedEntries = [];
for (const entry of completionRes.entries || []) {
const kindModifiers = parseKindModifier(entry.kindModifiers);
convertedEntries.push({
...entry,
isColor: kindModifiers.has("color"),
detail: getScriptKindDetails(entry),
isDeprecated: kindModifiers.has("deprecated"),
label: entry.name + (kindModifiers.has("optional") ? "?" : "")
});
}
return { ...completionRes, entries: convertedEntries };
}
function getAndConvertCompletionDetail(languageService, lsParams) {
const { fileName, pos, entryName, original, source } = lsParams;
const formattingOptions = getFormattingOptions(fileName);
const userPreferences = getUserPreferences(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 = getConfig(fileName)?.resolveImportExtension;
detail.codeActions?.forEach((action) => {
const effects = [];
const currentFileChanges = [];
if (resolveImportExtension) {
action.description = action.description.replace(qkExtInImportRE, "$1");
}
action.changes.forEach((change) => {
const realPath = getRealPath(change.fileName);
const currentEffect = {
textChanges: [],
fileName: realPath,
isNewFile: change.isNewFile
};
change.textChanges.forEach((item) => {
const sourceStartIndex = getSourceIndex(realPath, item.span.start);
if (fileName === change.fileName) {
const sourceEndIndex = getSourceIndex(
realPath,
item.span.start + item.span.length
);
const isAddImport = addImportTextEditRE.test(item.newText);
if (isAddImport && resolveImportExtension) {
item.newText = item.newText.replace(qkExtInImportRE, "$1");
}
if (isAddImport || !isIndexesInvalid(sourceStartIndex, sourceEndIndex)) {
currentFileChanges.push({
newText: item.newText,
range: [so