Skip to content

Commit

Permalink
refactor(typescript): handle virtual files by lsHost instead of sys
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Jun 18, 2023
1 parent b2dbe05 commit c81a193
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 36 deletions.
2 changes: 1 addition & 1 deletion packages/language-server/src/common/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ async function createParsedCommandLine(
): Promise<ts.ParsedCommandLine> {
const extraFileExtensions = plugins.map(plugin => plugin.extraFileExtensions ?? []).flat();
if (ts) {
const sys = createSys(undefined, ts, {
const sys = createSys(ts, {
...env,
onDidChangeWatchedFiles: undefined,
});
Expand Down
90 changes: 89 additions & 1 deletion packages/typescript/src/languageServiceHost.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { FileKind, VirtualFile, LanguageContext } from '@volar/language-service';
import type * as ts from 'typescript/lib/tsserverlibrary';
import { posix as path } from 'path';
import { matchFiles } from './typescript/utilities';

export function createLanguageServiceHost(
ctx: LanguageContext,
Expand Down Expand Up @@ -38,9 +39,12 @@ export function createLanguageServiceHost(
return snapshot.getText(0, snapshot.getLength());
}
},
readDirectory,
getDirectories,
directoryExists,
fileExists,
getProjectVersion: () => {
return tsProjectVersion.toString() + ':' + sys.version;
return tsProjectVersion + ':' + sys.version;
},
getTypeRootsVersion: () => {
return sys.version ?? -1; // TODO: only update for /node_modules changes?
Expand Down Expand Up @@ -119,6 +123,83 @@ export function createLanguageServiceHost(
oldOtherVirtualFileSnapshots = newOtherVirtualFileSnapshots;
}

function readDirectory(
dirName: string,
extensions?: readonly string[],
excludes?: readonly string[],
includes?: readonly string[],
depth?: number,
): string[] {
let matches = matchFiles(
dirName,
extensions,
excludes,
includes,
sys?.useCaseSensitiveFileNames ?? false,
ctx.host.getCurrentDirectory(),
depth,
(dirPath) => {

const files: string[] = [];

for (const fileName of getScriptFileNames()) {
if (fileName.toLowerCase().startsWith(dirPath.toLowerCase())) {
const baseName = fileName.substring(dirPath.length);
if (baseName.indexOf('/') === -1) {
files.push(baseName);
}
}
}

return {
files,
directories: getVirtualFileDirectories(dirPath),
};
},
sys?.realpath ? (path => sys.realpath!(path)) : (path => path),
);
if (ctx) {
matches = matches.map(match => {
const [_, source] = ctx.virtualFiles.getVirtualFile(match);
if (source) {
return source.fileName;
}
return match;
});
}
return [...new Set([
...matches,
...sys.readDirectory(dirName, extensions, excludes, includes, depth),
])];
}

function getDirectories(dirName: string): string[] {
return [...new Set([
...getVirtualFileDirectories(dirName),
...sys.getDirectories(dirName),
])];
}

function getVirtualFileDirectories(dirName: string): string[] {

const names = new Set<string>();

for (const fileName of getScriptFileNames()) {
if (fileName.toLowerCase().startsWith(dirName.toLowerCase())) {
const path = fileName.substring(dirName.length);
if (path.indexOf('/') >= 0) {
names.add(path.split('/')[0]);
}
}
}

for (const name of sys.getDirectories(dirName)) {
names.add(name);
}

return [...names];
}

function getScriptFileNames() {

const tsFileNames = new Set<string>();
Expand Down Expand Up @@ -186,6 +267,13 @@ export function createLanguageServiceHost(
return sys.getModifiedTime?.(fileName)?.valueOf().toString() ?? '';
}

function directoryExists(dirName: string): boolean {
if (getScriptFileNames().some(fileName => fileName.toLowerCase().startsWith(dirName.toLowerCase()))) {
return true;
}
return sys.directoryExists(dirName);
}

function fileExists(fileName: string) {

// fill external virtual files
Expand Down
39 changes: 5 additions & 34 deletions packages/typescript/src/sys.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { FileChangeType, FileType, ServiceEnvironment, Disposable, LanguageContext } from '@volar/language-service';
import { FileKind, forEachEmbeddedFile } from '@volar/language-core';
import type { FileChangeType, FileType, ServiceEnvironment, Disposable } from '@volar/language-service';
import type * as ts from 'typescript/lib/tsserverlibrary';
import { posix as path } from 'path';
import { matchFiles } from './typescript/utilities';
Expand All @@ -22,7 +21,6 @@ interface Dir {
let currentCwd = '';

export function createSys(
ctx: LanguageContext | undefined,
ts: typeof import('typescript/lib/tsserverlibrary'),
env: ServiceEnvironment,
dtsHost?: IDtsHost,
Expand Down Expand Up @@ -190,7 +188,7 @@ export function createSys(
file.exists = result?.type === 1 satisfies FileType.File || result?.type === 64 satisfies FileType.SymbolicLink;
if (file.exists) {
const time = Date.now();
file.modifiedTime = time !== file.modifiedTime ? time : time + 1;
file.modifiedTime = time !== file.modifiedTime ? time : file.modifiedTime + 1;
version++;
}
});
Expand Down Expand Up @@ -218,7 +216,7 @@ export function createSys(
depth?: number,
) {
dirName = resolvePath(dirName);
let matches = matchFiles(
const matches = matchFiles(
dirName,
extensions,
excludes,
Expand All @@ -231,40 +229,14 @@ export function createSys(
dirPath = resolvePath(dirPath);
readDirectoryWorker(dirPath);
const dir = getDir(dirPath);
const virtualFiles: string[] = [];

if (ctx) {
for (const { root } of ctx.virtualFiles.allSources()) {
forEachEmbeddedFile(root, file => {
if (file.kind === FileKind.TypeScriptHostFile) {
const fileDirName = path.dirname(file.fileName);
if (fileDirName.toLowerCase() === dirPath.toLowerCase()) {
virtualFiles.push(path.basename(file.fileName));
}
}
});
}
}

return {
files: [
...[...Object.entries(dir.files)].filter(([_, file]) => file.exists).map(([name]) => name),
...virtualFiles,
],
files: [...Object.entries(dir.files)].filter(([_, file]) => file.exists).map(([name]) => name),
directories: [...Object.entries(dir.dirs)].filter(([_, dir]) => dir.exists).map(([name]) => name),
};
},
sys?.realpath ? (path => sys.realpath!(path)) : (path => path),
);
if (ctx) {
matches = matches.map(match => {
const [_, source] = ctx.virtualFiles.getVirtualFile(match);
if (source) {
return source.fileName;
}
return match;
});
}
return [...new Set(matches)];
}

Expand Down Expand Up @@ -320,10 +292,9 @@ export function createSys(
}
dir.requested = true;

const uri = env.fileNameToUri(dirName);
const result = dirName.startsWith('/node_modules/') && dtsHost
? dtsHost.readDirectory(dirName)
: env.fs?.readDirectory(uri);
: env.fs?.readDirectory(env.fileNameToUri(dirName || '.'));

if (typeof result === 'object' && 'then' in result) {
const promise = result;
Expand Down

0 comments on commit c81a193

Please sign in to comment.