Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed May 21, 2024
1 parent 71a58c8 commit 69fbebb
Show file tree
Hide file tree
Showing 79 changed files with 1,136 additions and 1,038 deletions.
4 changes: 2 additions & 2 deletions packages/eslint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TextDocument } from 'vscode-languageserver-textdocument';
const windowsPath = /\\/g;

export function createProcessor(
languagePlugins: LanguagePlugin[],
languagePlugins: LanguagePlugin<string>[],
caseSensitive: boolean,
extensionsMap: Record<string, string> = {
'javascript': '.js',
Expand All @@ -27,7 +27,7 @@ export function createProcessor(
},
supportsAutofix = true,
): Linter.Processor {
const language = createLanguage(languagePlugins, caseSensitive, () => { });
const language = createLanguage<string>(languagePlugins, new FileMap(caseSensitive), () => { });
const documents = new FileMap<{
sourceDocument: TextDocument;
embeddedDocuments: TextDocument[];
Expand Down
105 changes: 71 additions & 34 deletions packages/kit/lib/createChecker.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { CodeActionTriggerKind, Diagnostic, DiagnosticSeverity, DidChangeWatchedFilesParams, FileChangeType, LanguagePlugin, NotificationHandler, LanguageServicePlugin, ServiceEnvironment, createLanguageService, mergeWorkspaceEdits, TypeScriptProjectHost } from '@volar/language-service';
import { CodeActionTriggerKind, Diagnostic, DiagnosticSeverity, DidChangeWatchedFilesParams, FileChangeType, LanguagePlugin, NotificationHandler, LanguageServicePlugin, LanguageServiceEnvironment, createLanguageService, mergeWorkspaceEdits, createLanguage, createUriMap } from '@volar/language-service';
import * as path from 'typesafe-path/posix';
import * as ts from 'typescript';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { createServiceEnvironment } from './createServiceEnvironment';
import { asPosix, defaultCompilerOptions, fileNameToUri, uriToFileName } from './utils';
import { createTypeScriptLanguage } from '@volar/typescript';
import { URI } from 'vscode-uri';
import { TypeScriptProjectHost, createLanguageServiceHost, resolveFileLanguageId } from '@volar/typescript';

export function createTypeScriptChecker(
languagePlugins: LanguagePlugin[],
languagePlugins: LanguagePlugin<URI>[],
languageServicePlugins: LanguageServicePlugin[],
tsconfig: string,
) {
const tsconfigPath = asPosix(tsconfig);
return createTypeScriptCheckerWorker(languagePlugins, languageServicePlugins, env => {
return createTypeScriptCheckerWorker(languagePlugins, languageServicePlugins, tsconfigPath, env => {
return createTypeScriptProjectHost(
tsconfigPath,
env,
() => {
const parsed = ts.parseJsonSourceFileConfigFileContent(
Expand All @@ -34,14 +34,13 @@ export function createTypeScriptChecker(
}

export function createTypeScriptInferredChecker(
languagePlugins: LanguagePlugin[],
languagePlugins: LanguagePlugin<URI>[],
languageServicePlugins: LanguageServicePlugin[],
getScriptFileNames: () => string[],
compilerOptions = defaultCompilerOptions
) {
return createTypeScriptCheckerWorker(languagePlugins, languageServicePlugins, env => {
return createTypeScriptCheckerWorker(languagePlugins, languageServicePlugins, undefined, env => {
return createTypeScriptProjectHost(
undefined,
env,
() => ({
options: compilerOptions,
Expand All @@ -51,10 +50,13 @@ export function createTypeScriptInferredChecker(
});
}

const fsFileSnapshots = createUriMap<[number | undefined, ts.IScriptSnapshot | undefined]>();

function createTypeScriptCheckerWorker(
languagePlugins: LanguagePlugin[],
languagePlugins: LanguagePlugin<URI>[],
languageServicePlugins: LanguageServicePlugin[],
getProjectHost: (env: ServiceEnvironment) => TypeScriptProjectHost
configFileName: string | undefined,
getProjectHost: (env: LanguageServiceEnvironment) => TypeScriptProjectHost,
) {

let settings = {};
Expand All @@ -71,11 +73,52 @@ function createTypeScriptCheckerWorker(
};
};

const language = createTypeScriptLanguage(
ts,
languagePlugins,
getProjectHost(env),
const language = createLanguage(
[
...languagePlugins,
{
getLanguageId(uri) {
return resolveFileLanguageId(uri.fsPath);
},
},
],
createUriMap(ts.sys.useCaseSensitiveFileNames),
uri => {
// fs files
const cache = fsFileSnapshots.get(uri);
const fileName = uriToFileName(uri);
const modifiedTime = ts.sys.getModifiedTime?.(fileName)?.valueOf();
if (!cache || cache[0] !== modifiedTime) {
if (ts.sys.fileExists(fileName)) {
const text = ts.sys.readFile(fileName);
const snapshot = text !== undefined ? ts.ScriptSnapshot.fromString(text) : undefined;
fsFileSnapshots.set(uri, [modifiedTime, snapshot]);
}
else {
fsFileSnapshots.set(uri, [modifiedTime, undefined]);
}
}
const snapshot = fsFileSnapshots.get(uri)?.[1];
if (snapshot) {
language.scripts.set(uri, snapshot);
}
else {
language.scripts.delete(uri);
}
},
);
language.typescript = {
configFileName,
asFileName: uriToFileName,
asScriptId: fileNameToUri,
...createLanguageServiceHost(
ts,
ts.sys,
language,
fileNameToUri,
getProjectHost(env),
),
};
const service = createLanguageService(
language,
languageServicePlugins,
Expand Down Expand Up @@ -112,13 +155,13 @@ function createTypeScriptCheckerWorker(
function fileEvent(fileName: string, type: FileChangeType) {
fileName = asPosix(fileName);
for (const cb of didChangeWatchedFilesCallbacks) {
cb({ changes: [{ uri: fileNameToUri(fileName), type }] });
cb({ changes: [{ uri: fileNameToUri(fileName).toString(), type }] });
}
}

function check(fileName: string) {
fileName = asPosix(fileName);
const uri = fileNameToUri(fileName);
const uri = fileNameToUri(fileName).toString();
return service.doValidation(uri);
}

Expand All @@ -129,7 +172,7 @@ function createTypeScriptCheckerWorker(
if (sourceScript) {
const document = service.context.documents.get(uri, sourceScript.languageId, sourceScript.snapshot);
const range = { start: document.positionAt(0), end: document.positionAt(document.getText().length) };
const codeActions = await service.doCodeActions(uri, range, { diagnostics, only, triggerKind: 1 satisfies typeof CodeActionTriggerKind.Invoked });
const codeActions = await service.doCodeActions(uri.toString(), range, { diagnostics, only, triggerKind: 1 satisfies typeof CodeActionTriggerKind.Invoked });
if (codeActions) {
for (let i = 0; i < codeActions.length; i++) {
codeActions[i] = await service.doCodeActionResolve(codeActions[i]);
Expand All @@ -141,21 +184,23 @@ function createTypeScriptCheckerWorker(
for (const uri in rootEdit.changes ?? {}) {
const edits = rootEdit.changes![uri];
if (edits.length) {
const editFile = service.context.language.scripts.get(uri);
const parsedUri = URI.parse(uri);
const editFile = service.context.language.scripts.get(parsedUri);
if (editFile) {
const editDocument = service.context.documents.get(uri, editFile.languageId, editFile.snapshot);
const editDocument = service.context.documents.get(parsedUri, editFile.languageId, editFile.snapshot);
const newString = TextDocument.applyEdits(editDocument, edits);
await writeFile(uriToFileName(uri), newString);
await writeFile(uriToFileName(parsedUri), newString);
}
}
}
for (const change of rootEdit.documentChanges ?? []) {
if ('textDocument' in change) {
const editFile = service.context.language.scripts.get(change.textDocument.uri);
const changeUri = URI.parse(change.textDocument.uri);
const editFile = service.context.language.scripts.get(changeUri);
if (editFile) {
const editDocument = service.context.documents.get(change.textDocument.uri, editFile.languageId, editFile.snapshot);
const editDocument = service.context.documents.get(changeUri, editFile.languageId, editFile.snapshot);
const newString = TextDocument.applyEdits(editDocument, change.edits);
await writeFile(uriToFileName(change.textDocument.uri), newString);
await writeFile(uriToFileName(changeUri), newString);
}
}
// TODO: CreateFile | RenameFile | DeleteFile
Expand Down Expand Up @@ -196,22 +241,15 @@ function createTypeScriptCheckerWorker(
}

function createTypeScriptProjectHost(
configFileName: string | undefined,
env: ServiceEnvironment,
env: LanguageServiceEnvironment,
createParsedCommandLine: () => Pick<ts.ParsedCommandLine, 'options' | 'fileNames'>,
) {

let scriptSnapshotsCache: Map<string, ts.IScriptSnapshot | undefined> = new Map();
let parsedCommandLine = createParsedCommandLine();
let projectVersion = 0;
let shouldCheckRootFiles = false;

const host: TypeScriptProjectHost = {
...ts.sys,
configFileName,
getCurrentDirectory: () => {
return uriToFileName(env.workspaceFolder);
},
getCompilationSettings: () => {
return parsedCommandLine.options;
},
Expand All @@ -235,13 +273,12 @@ function createTypeScriptProjectHost(
}
return scriptSnapshotsCache.get(fileName);
},
fileNameToScriptId: env.typescript!.fileNameToUri,
scriptIdToFileName: env.typescript!.uriToFileName,
};

env.onDidChangeWatchedFiles?.(({ changes }) => {
for (const change of changes) {
const fileName = uriToFileName(change.uri);
const changeUri = URI.parse(change.uri);
const fileName = uriToFileName(changeUri);
if (change.type === 2 satisfies typeof FileChangeType.Changed) {
if (scriptSnapshotsCache.has(fileName)) {
projectVersion++;
Expand Down
12 changes: 6 additions & 6 deletions packages/kit/lib/createFormatter.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { FormattingOptions, LanguagePlugin, LanguageServicePlugin, createLanguage, createLanguageService } from '@volar/language-service';
import { FormattingOptions, LanguagePlugin, LanguageServicePlugin, createLanguage, createLanguageService, createUriMap } from '@volar/language-service';
import * as ts from 'typescript';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { URI } from 'vscode-uri';
import { createServiceEnvironment } from './createServiceEnvironment';

export function createFormatter(
languages: LanguagePlugin[],
languages: LanguagePlugin<URI>[],
services: LanguageServicePlugin[]
) {

let fakeUri = 'file:///dummy.txt';
let settings = {};

const fakeUri = URI.parse('file:///dummy.txt');
const env = createServiceEnvironment(() => settings);
const language = createLanguage(languages, false, () => { });
const language = createLanguage(languages, createUriMap(false), () => { });
const service = createLanguageService(
language,
services,
Expand All @@ -36,7 +36,7 @@ export function createFormatter(
language.scripts.set(fakeUri, snapshot, languageId);

const document = service.context.documents.get(fakeUri, languageId, snapshot);
const edits = await service.format(fakeUri, options, undefined, undefined);
const edits = await service.format(fakeUri.toString(), options, undefined, undefined);
if (edits?.length) {
const newString = TextDocument.applyEdits(document, edits);
return newString;
Expand Down
25 changes: 9 additions & 16 deletions packages/kit/lib/createServiceEnvironment.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { FileSystem, FileType, ServiceEnvironment } from '@volar/language-service';
import { FileSystem, FileType, LanguageServiceEnvironment } from '@volar/language-service';
import * as fs from 'fs';
import { URI } from 'vscode-uri';
import { fileNameToUri, uriToFileName } from './utils';

export function createServiceEnvironment(getSettings: () => any): ServiceEnvironment {

export function createServiceEnvironment(getSettings: () => any): LanguageServiceEnvironment {
return {
workspaceFolder: URI.file(process.cwd()).toString(),
workspaceFolder: URI.file(process.cwd()),
getConfiguration(section: string) {
const settings = getSettings();
if (section in settings) {
Expand All @@ -33,18 +31,14 @@ export function createServiceEnvironment(getSettings: () => any): ServiceEnviron
},
fs: nodeFs,
console,
typescript: {
fileNameToUri: fileNameToUri,
uriToFileName: uriToFileName,
},
};
}

const nodeFs: FileSystem = {
stat(uri) {
if (uri.startsWith('file://')) {
if (uri.scheme === 'file') {
try {
const stats = fs.statSync(uriToFileName(uri), { throwIfNoEntry: false });
const stats = fs.statSync(uri.fsPath, { throwIfNoEntry: false });
if (stats) {
return {
type: stats.isFile() ? FileType.File
Expand All @@ -63,20 +57,19 @@ const nodeFs: FileSystem = {
}
},
readFile(uri, encoding) {
if (uri.startsWith('file://')) {
if (uri.scheme === 'file') {
try {
return fs.readFileSync(uriToFileName(uri), { encoding: encoding as 'utf-8' ?? 'utf-8' });
return fs.readFileSync(uri.fsPath, { encoding: encoding as 'utf-8' ?? 'utf-8' });
}
catch {
return undefined;
}
}
},
readDirectory(uri) {
if (uri.startsWith('file://')) {
if (uri.scheme === 'file') {
try {
const dirName = uriToFileName(uri);
const files = fs.readdirSync(dirName, { withFileTypes: true });
const files = fs.readdirSync(uri.fsPath, { withFileTypes: true });
return files.map<[string, FileType]>(file => {
return [file.name, file.isFile() ? FileType.File
: file.isDirectory() ? FileType.Directory
Expand Down
4 changes: 2 additions & 2 deletions packages/kit/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ export function asPosix(path: string) {
return path.replace(/\\/g, '/') as path.PosixPath;
}

export const uriToFileName = (uri: string) => URI.parse(uri).fsPath.replace(/\\/g, '/');
export const uriToFileName = (uri: URI) => uri.fsPath.replace(/\\/g, '/');

export const fileNameToUri = (fileName: string) => URI.file(fileName).toString();
export const fileNameToUri = (fileName: string) => URI.file(fileName);

0 comments on commit 69fbebb

Please sign in to comment.