数通智联化工云平台
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
10 KiB

2 years ago
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createLanguageContext = void 0;
const path_1 = require("path");
const virtualFiles_1 = require("./virtualFiles");
const types_1 = require("./types");
function createLanguageContext(host, modules, languageModules) {
for (const languageModule of languageModules.reverse()) {
if (languageModule.proxyLanguageServiceHost) {
const proxyApis = languageModule.proxyLanguageServiceHost(host);
host = new Proxy(host, {
get(target, key) {
if (key in proxyApis) {
return proxyApis[key];
}
return target[key];
},
});
}
}
let lastProjectVersion;
let tsProjectVersion = 0;
const virtualFiles = (0, virtualFiles_1.createVirtualFiles)(languageModules);
const ts = modules.typescript;
const scriptSnapshots = new Map();
const sourceTsFileVersions = new Map();
const sourceFileVersions = new Map();
const virtualFileVersions = new Map();
const _tsHost = {
fileExists: host.fileExists
? fileName => {
const ext = fileName.substring(fileName.lastIndexOf('.'));
if (ext === '.js'
|| ext === '.ts'
|| ext === '.jsx'
|| ext === '.tsx') {
/**
* If try to access a external .vue file that outside of the project,
* the file will not process by language service host,
* so virtual file will not be created.
*
* We try to create virtual file here.
*/
const sourceFileName = fileName.substring(0, fileName.lastIndexOf('.'));
if (!virtualFiles.hasSource(sourceFileName)) {
const scriptSnapshot = host.getScriptSnapshot(sourceFileName);
if (scriptSnapshot) {
virtualFiles.updateSource(sourceFileName, scriptSnapshot, host.getScriptLanguageId?.(sourceFileName));
}
}
}
if (virtualFiles.hasVirtualFile(fileName)) {
return true;
}
return !!host.fileExists?.(fileName);
}
: undefined,
getProjectVersion: () => {
return tsProjectVersion.toString();
},
getScriptFileNames,
getScriptVersion,
getScriptSnapshot,
readDirectory: (_path, extensions, exclude, include, depth) => {
const result = host.readDirectory?.(_path, extensions, exclude, include, depth) ?? [];
for (const { fileName } of virtualFiles.allSources()) {
const vuePath2 = path_1.posix.join(_path, path_1.posix.basename(fileName));
if (path_1.posix.relative(_path.toLowerCase(), fileName.toLowerCase()).startsWith('..')) {
continue;
}
if (!depth && fileName.toLowerCase() === vuePath2.toLowerCase()) {
result.push(vuePath2);
}
else if (depth) {
result.push(vuePath2); // TODO: depth num
}
}
return result;
},
getScriptKind(fileName) {
if (ts) {
if (virtualFiles.hasSource(fileName))
return ts.ScriptKind.Deferred;
switch (path_1.posix.extname(fileName)) {
case '.js': return ts.ScriptKind.JS;
case '.jsx': return ts.ScriptKind.JSX;
case '.ts': return ts.ScriptKind.TS;
case '.tsx': return ts.ScriptKind.TSX;
case '.json': return ts.ScriptKind.JSON;
default: return ts.ScriptKind.Unknown;
}
}
return 0;
},
};
return {
typescript: {
languageServiceHost: new Proxy(_tsHost, {
get: (target, property) => {
update();
return target[property] || host[property];
},
}),
},
virtualFiles: new Proxy(virtualFiles, {
get: (target, property) => {
update();
return target[property];
},
}),
};
function update() {
const newProjectVersion = host.getProjectVersion?.();
const shouldUpdate = newProjectVersion === undefined || newProjectVersion !== lastProjectVersion;
lastProjectVersion = newProjectVersion;
if (!shouldUpdate)
return;
let shouldUpdateTsProject = false;
let virtualFilesUpdatedNum = 0;
const remainRootFiles = new Set(host.getScriptFileNames());
// .vue
for (const { fileName } of virtualFiles.allSources()) {
remainRootFiles.delete(fileName);
const snapshot = host.getScriptSnapshot(fileName);
if (!snapshot) {
// delete
virtualFiles.deleteSource(fileName);
shouldUpdateTsProject = true;
virtualFilesUpdatedNum++;
continue;
}
const newVersion = host.getScriptVersion(fileName);
if (sourceFileVersions.get(fileName) !== newVersion) {
// update
sourceFileVersions.set(fileName, newVersion);
virtualFiles.updateSource(fileName, snapshot, host.getScriptLanguageId?.(fileName));
virtualFilesUpdatedNum++;
}
}
// no any vue file version change, it mean project version was update by ts file change at this time
if (!virtualFilesUpdatedNum) {
shouldUpdateTsProject = true;
}
// add
for (const fileName of [...remainRootFiles]) {
const snapshot = host.getScriptSnapshot(fileName);
if (snapshot) {
const virtualFile = virtualFiles.updateSource(fileName, snapshot, host.getScriptLanguageId?.(fileName));
if (virtualFile) {
remainRootFiles.delete(fileName);
}
}
}
// .ts / .js / .d.ts / .json ...
for (const [oldTsFileName, oldTsFileVersion] of [...sourceTsFileVersions]) {
const newVersion = host.getScriptVersion(oldTsFileName);
if (oldTsFileVersion !== newVersion) {
if (!remainRootFiles.has(oldTsFileName) && !host.getScriptSnapshot(oldTsFileName)) {
// delete
sourceTsFileVersions.delete(oldTsFileName);
}
else {
// update
sourceTsFileVersions.set(oldTsFileName, newVersion);
}
shouldUpdateTsProject = true;
}
}
for (const nowFileName of remainRootFiles) {
if (!sourceTsFileVersions.has(nowFileName)) {
// add
const newVersion = host.getScriptVersion(nowFileName);
sourceTsFileVersions.set(nowFileName, newVersion);
shouldUpdateTsProject = true;
}
}
for (const { root: rootVirtualFile } of virtualFiles.allSources()) {
if (!shouldUpdateTsProject) {
(0, virtualFiles_1.forEachEmbeddedFile)(rootVirtualFile, embedded => {
if (embedded.kind === types_1.FileKind.TypeScriptHostFile) {
if (virtualFileVersions.has(embedded.fileName) && virtualFileVersions.get(embedded.fileName)?.virtualFileSnapshot !== embedded.snapshot) {
shouldUpdateTsProject = true;
}
}
});
}
}
if (shouldUpdateTsProject) {
tsProjectVersion++;
}
}
function getScriptFileNames() {
const tsFileNames = new Set();
for (const { root: rootVirtualFile } of virtualFiles.allSources()) {
(0, virtualFiles_1.forEachEmbeddedFile)(rootVirtualFile, embedded => {
if (embedded.kind === types_1.FileKind.TypeScriptHostFile) {
tsFileNames.add(embedded.fileName); // virtual .ts
}
});
}
for (const fileName of host.getScriptFileNames()) {
if (!virtualFiles.hasSource(fileName)) {
tsFileNames.add(fileName); // .ts
}
}
return [...tsFileNames];
}
function getScriptVersion(fileName) {
let [virtualFile, source] = virtualFiles.getVirtualFile(fileName);
if (virtualFile && source) {
let version = virtualFileVersions.get(virtualFile.fileName);
if (!version) {
version = {
value: 0,
virtualFileSnapshot: virtualFile.snapshot,
sourceFileSnapshot: source.snapshot,
};
virtualFileVersions.set(virtualFile.fileName, version);
}
else if (version.virtualFileSnapshot !== virtualFile.snapshot
|| (host.isTsc && version.sourceFileSnapshot !== source.snapshot) // fix https://github.com/johnsoncodehk/volar/issues/1082
) {
version.value++;
version.virtualFileSnapshot = virtualFile.snapshot;
version.sourceFileSnapshot = source.snapshot;
}
return version.value.toString();
}
return host.getScriptVersion(fileName);
}
function getScriptSnapshot(fileName) {
const version = getScriptVersion(fileName);
const cache = scriptSnapshots.get(fileName.toLowerCase());
if (cache && cache[0] === version) {
return cache[1];
}
const [virtualFile] = virtualFiles.getVirtualFile(fileName);
if (virtualFile) {
const snapshot = virtualFile.snapshot;
scriptSnapshots.set(fileName.toLowerCase(), [version, snapshot]);
return snapshot;
}
let tsScript = host.getScriptSnapshot(fileName);
if (tsScript) {
scriptSnapshots.set(fileName.toLowerCase(), [version, tsScript]);
return tsScript;
}
}
}
exports.createLanguageContext = createLanguageContext;
//# sourceMappingURL=languageContext.js.map