UNPKG

qingkuai-language-service

Version:

The language service for qingkuai

1,412 lines (1,384 loc) 52.8 kB
'use strict'; var compiler = require('qingkuai/compiler'); var qingkuai$1 = require('qingkuai'); let ts; let fs; let path; exports.typeRefStatement = void 0; exports.typeDeclarationFilePath = void 0; let getConfig; let getFullFileNames; let getUserPreferences; exports.getCompileInfo = void 0; 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; exports.getCompileInfo = options.getCompileInfo; getFullFileNames = options.getFullFileNames; getUserPreferences = options.getUserPreferences; getLineAndCharacter = options.getLineAndCharacter; getTsLanguageService = options.getTsLanguageService; getFormattingOptions = options.getFormattingOptions; exports.typeDeclarationFilePath = options.typeDeclarationFilePath; getInterIndexByLineAndCharacter = options.getInterIndexByLineAndCharacter; exports.typeRefStatement = `import {__c__,wat,waT,Wat,der,stc,rea} from "${exports.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 === exports.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 compiler.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 !!(exports.getCompileInfo(fileName).positions[sourceIndex].flag & compiler.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 } = exports.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 || !exports.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 = exports.getCompileInfo(fileName); const storedTypes = /* @__PURE__ */ new Map(); const importedQingkuaiFileNames = /* @__PURE__ */ new Set(); const typeRefStatementLen = exports.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 = exports.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) && compiler.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(exports.getCompileInfo(getRealPath(componentFileName)).slotInfo); } function getCompilerCommonMessage(key, ...args) { return { // @ts-ignore msg: compiler.commonMessage[key][1](...args), code: compiler.commonMessage[key][0] }; } function getRuntimeCommonMessage(key, ...args) { return { // @ts-ignore msg: qingkuai$1.commonMessage[key][1](...args), code: qingkuai$1.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 = exports.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 = exports.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 = exports.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"); }