chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:38:42 +08:00
commit 2d485ba600
8326 changed files with 2641263 additions and 0 deletions
@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext, Uri, l10n } from 'vscode';
import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient';
import { startClient, LanguageClientConstructor } from '../cssClient';
import { LanguageClient } from 'vscode-languageclient/browser';
import { registerDropOrPasteResourceSupport } from '../dropOrPaste/dropOrPasteResource';
let client: BaseLanguageClient | undefined;
// this method is called when vs code is activated
export async function activate(context: ExtensionContext) {
const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/cssServerMain.js');
try {
const worker = new Worker(serverMain.toString());
worker.postMessage({ i10lLocation: l10n.uri?.toString(false) ?? '' });
const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
return new LanguageClient(id, name, worker, clientOptions);
};
client = await startClient(context, newLanguageClient, { TextDecoder });
context.subscriptions.push(registerDropOrPasteResourceSupport({ language: 'css', scheme: '*' }));
} catch (e) {
console.log(e);
}
}
export async function deactivate(): Promise<void> {
if (client) {
await client.stop();
client = undefined;
}
}
@@ -0,0 +1,211 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList, FormattingOptions, workspace, l10n } from 'vscode';
import { Disposable, LanguageClientOptions, ProvideCompletionItemsSignature, NotificationType, BaseLanguageClient, DocumentRangeFormattingParams, DocumentRangeFormattingRequest } from 'vscode-languageclient';
import { getCustomDataSource } from './customData';
import { RequestService, serveFileSystemRequests } from './requests';
namespace CustomDataChangedNotification {
export const type: NotificationType<string[]> = new NotificationType('css/customDataChanged');
}
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient;
export interface Runtime {
TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string } };
fs?: RequestService;
}
interface FormatterRegistration {
readonly languageId: string;
readonly settingId: string;
provider: Disposable | undefined;
}
interface CSSFormatSettings {
newlineBetweenSelectors?: boolean;
newlineBetweenRules?: boolean;
spaceAroundSelectorSeparator?: boolean;
braceStyle?: 'collapse' | 'expand';
preserveNewLines?: boolean;
maxPreserveNewLines?: number | null;
}
const cssFormatSettingKeys: (keyof CSSFormatSettings)[] = ['newlineBetweenSelectors', 'newlineBetweenRules', 'spaceAroundSelectorSeparator', 'braceStyle', 'preserveNewLines', 'maxPreserveNewLines'];
export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise<BaseLanguageClient> {
const customDataSource = getCustomDataSource(context.subscriptions);
const documentSelector = ['css', 'scss', 'less'];
const formatterRegistrations: FormatterRegistration[] = documentSelector.map(languageId => ({
languageId, settingId: `${languageId}.format.enable`, provider: undefined
}));
// Options to control the language client
const clientOptions: LanguageClientOptions = {
documentSelector,
synchronize: {
configurationSection: ['css', 'scss', 'less']
},
initializationOptions: {
handledSchemas: ['file'],
provideFormatter: false, // tell the server to not provide formatting capability
customCapabilities: { rangeFormatting: { editLimit: 10000 } }
},
middleware: {
provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult<CompletionItem[] | CompletionList> {
// testing the replace / insert mode
function updateRanges(item: CompletionItem) {
const range = item.range;
if (range instanceof Range && range.end.isAfter(position) && range.start.isBeforeOrEqual(position)) {
item.range = { inserting: new Range(range.start, position), replacing: range };
}
}
function updateLabel(item: CompletionItem) {
if (item.kind === CompletionItemKind.Color) {
item.label = {
label: item.label as string,
description: (item.documentation as string)
};
}
}
// testing the new completion
function updateProposals(r: CompletionItem[] | CompletionList | null | undefined): CompletionItem[] | CompletionList | null | undefined {
if (r) {
(Array.isArray(r) ? r : r.items).forEach(updateRanges);
(Array.isArray(r) ? r : r.items).forEach(updateLabel);
}
return r;
}
const isThenable = <T>(obj: ProviderResult<T>): obj is Thenable<T> => obj && (<any>obj)['then'];
const r = next(document, position, context, token);
if (isThenable<CompletionItem[] | CompletionList | null | undefined>(r)) {
return r.then(updateProposals);
}
return updateProposals(r);
}
}
};
// Create the language client and start the client.
const client = newLanguageClient('css', l10n.t('CSS Language Server'), clientOptions);
client.registerProposedFeatures();
await client.start();
client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
customDataSource.onDidChange(() => {
client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
});
// manually register / deregister format provider based on the `css/less/scss.format.enable` setting avoiding issues with late registration. See #71652.
for (const registration of formatterRegistrations) {
updateFormatterRegistration(registration);
context.subscriptions.push({ dispose: () => registration.provider?.dispose() });
context.subscriptions.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(registration.settingId) && updateFormatterRegistration(registration)));
}
serveFileSystemRequests(client, runtime);
context.subscriptions.push(initCompletionProvider());
function initCompletionProvider(): Disposable {
const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/;
return languages.registerCompletionItemProvider(documentSelector, {
provideCompletionItems(doc: TextDocument, pos: Position) {
const lineUntilPos = doc.getText(new Range(new Position(pos.line, 0), pos));
const match = lineUntilPos.match(regionCompletionRegExpr);
if (match) {
const range = new Range(new Position(pos.line, match[1].length), pos);
const beginProposal = new CompletionItem('#region', CompletionItemKind.Snippet);
beginProposal.range = range; TextEdit.replace(range, '/* #region */');
beginProposal.insertText = new SnippetString('/* #region $1*/');
beginProposal.documentation = l10n.t('Folding Region Start');
beginProposal.filterText = match[2];
beginProposal.sortText = 'za';
const endProposal = new CompletionItem('#endregion', CompletionItemKind.Snippet);
endProposal.range = range;
endProposal.insertText = '/* #endregion */';
endProposal.documentation = l10n.t('Folding Region End');
endProposal.sortText = 'zb';
endProposal.filterText = match[2];
return [beginProposal, endProposal];
}
return null;
}
});
}
commands.registerCommand('_css.applyCodeAction', applyCodeAction);
function applyCodeAction(uri: string, documentVersion: number, edits: TextEdit[]) {
const textEditor = window.activeTextEditor;
if (textEditor && textEditor.document.uri.toString() === uri) {
if (textEditor.document.version !== documentVersion) {
window.showInformationMessage(l10n.t('CSS fix is outdated and can\'t be applied to the document.'));
}
textEditor.edit(mutator => {
for (const edit of edits) {
mutator.replace(client.protocol2CodeConverter.asRange(edit.range), edit.newText);
}
}).then(success => {
if (!success) {
window.showErrorMessage(l10n.t('Failed to apply CSS fix to the document. Please consider opening an issue with steps to reproduce.'));
}
});
}
}
function updateFormatterRegistration(registration: FormatterRegistration) {
const formatEnabled = workspace.getConfiguration().get(registration.settingId);
if (!formatEnabled && registration.provider) {
registration.provider.dispose();
registration.provider = undefined;
} else if (formatEnabled && !registration.provider) {
registration.provider = languages.registerDocumentRangeFormattingEditProvider(registration.languageId, {
provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult<TextEdit[]> {
const filesConfig = workspace.getConfiguration('files', document);
const fileFormattingOptions = {
trimTrailingWhitespace: filesConfig.get<boolean>('trimTrailingWhitespace'),
trimFinalNewlines: filesConfig.get<boolean>('trimFinalNewlines'),
insertFinalNewline: filesConfig.get<boolean>('insertFinalNewline'),
};
const params: DocumentRangeFormattingParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
range: client.code2ProtocolConverter.asRange(range),
options: client.code2ProtocolConverter.asFormattingOptions(options, fileFormattingOptions)
};
// add the css formatter options from the settings
const formatterSettings = workspace.getConfiguration(registration.languageId, document).get<CSSFormatSettings>('format');
if (formatterSettings) {
for (const key of cssFormatSettingKeys) {
const val = formatterSettings[key];
if (val !== undefined && val !== null) {
params.options[key] = val;
}
}
}
return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then(
client.protocol2CodeConverter.asTextEdits,
(error) => {
client.handleFailedRequest(DocumentRangeFormattingRequest.type, undefined, error, []);
return Promise.resolve([]);
}
);
}
});
}
}
return client;
}
@@ -0,0 +1,88 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { workspace, extensions, Uri, EventEmitter, Disposable } from 'vscode';
import { Utils } from 'vscode-uri';
export function getCustomDataSource(toDispose: Disposable[]) {
let pathsInWorkspace = getCustomDataPathsInAllWorkspaces();
let pathsInExtensions = getCustomDataPathsFromAllExtensions();
const onChange = new EventEmitter<void>();
toDispose.push(extensions.onDidChange(_ => {
const newPathsInExtensions = getCustomDataPathsFromAllExtensions();
if (newPathsInExtensions.length !== pathsInExtensions.length || !newPathsInExtensions.every((val, idx) => val === pathsInExtensions[idx])) {
pathsInExtensions = newPathsInExtensions;
onChange.fire();
}
}));
toDispose.push(workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('css.customData')) {
pathsInWorkspace = getCustomDataPathsInAllWorkspaces();
onChange.fire();
}
}));
return {
get uris() {
return pathsInWorkspace.concat(pathsInExtensions);
},
get onDidChange() {
return onChange.event;
}
};
}
function getCustomDataPathsInAllWorkspaces(): string[] {
const workspaceFolders = workspace.workspaceFolders;
const dataPaths: string[] = [];
if (!workspaceFolders) {
return dataPaths;
}
const collect = (paths: string[] | undefined, rootFolder: Uri) => {
if (Array.isArray(paths)) {
for (const path of paths) {
if (typeof path === 'string') {
dataPaths.push(Utils.resolvePath(rootFolder, path).toString());
}
}
}
};
for (let i = 0; i < workspaceFolders.length; i++) {
const folderUri = workspaceFolders[i].uri;
const allCssConfig = workspace.getConfiguration('css', folderUri);
const customDataInspect = allCssConfig.inspect<string[]>('customData');
if (customDataInspect) {
collect(customDataInspect.workspaceFolderValue, folderUri);
if (i === 0) {
if (workspace.workspaceFile) {
collect(customDataInspect.workspaceValue, workspace.workspaceFile);
}
collect(customDataInspect.globalValue, folderUri);
}
}
}
return dataPaths;
}
function getCustomDataPathsFromAllExtensions(): string[] {
const dataPaths: string[] = [];
for (const extension of extensions.all) {
const customData = extension.packageJSON?.contributes?.css?.customData;
if (Array.isArray(customData)) {
for (const rp of customData) {
dataPaths.push(Utils.joinPath(extension.extensionUri, rp).toString());
}
}
}
return dataPaths;
}
@@ -0,0 +1,153 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import * as vscode from 'vscode';
import { getDocumentDir, Mimes, Schemes } from './shared';
import { UriList } from './uriList';
class DropOrPasteResourceProvider implements vscode.DocumentDropEditProvider, vscode.DocumentPasteEditProvider {
readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('css', 'link', 'url');
async provideDocumentDropEdits(
document: vscode.TextDocument,
position: vscode.Position,
dataTransfer: vscode.DataTransfer,
token: vscode.CancellationToken,
): Promise<vscode.DocumentDropEdit | undefined> {
const uriList = await this.getUriList(dataTransfer);
if (!uriList.entries.length || token.isCancellationRequested) {
return;
}
const snippet = await this.createUriListSnippet(document.uri, uriList);
if (!snippet || token.isCancellationRequested) {
return;
}
return {
kind: this.kind,
title: snippet.label,
insertText: snippet.snippet.value,
yieldTo: this.pasteAsCssUrlByDefault(document, position) ? [] : [vscode.DocumentDropOrPasteEditKind.Empty.append('uri')]
};
}
async provideDocumentPasteEdits(
document: vscode.TextDocument,
ranges: readonly vscode.Range[],
dataTransfer: vscode.DataTransfer,
_context: vscode.DocumentPasteEditContext,
token: vscode.CancellationToken
): Promise<vscode.DocumentPasteEdit[] | undefined> {
const uriList = await this.getUriList(dataTransfer);
if (!uriList.entries.length || token.isCancellationRequested) {
return;
}
const snippet = await this.createUriListSnippet(document.uri, uriList);
if (!snippet || token.isCancellationRequested) {
return;
}
return [{
kind: this.kind,
title: snippet.label,
insertText: snippet.snippet.value,
yieldTo: this.pasteAsCssUrlByDefault(document, ranges[0].start) ? [] : [vscode.DocumentDropOrPasteEditKind.Empty.append('uri')]
}];
}
private async getUriList(dataTransfer: vscode.DataTransfer): Promise<UriList> {
const urlList = await dataTransfer.get(Mimes.uriList)?.asString();
if (urlList) {
return UriList.from(urlList);
}
// Find file entries
const uris: vscode.Uri[] = [];
for (const [_, entry] of dataTransfer) {
const file = entry.asFile();
if (file?.uri) {
uris.push(file.uri);
}
}
return new UriList(uris.map(uri => ({ uri, str: uri.toString(true) })));
}
private async createUriListSnippet(docUri: vscode.Uri, uriList: UriList): Promise<{ readonly snippet: vscode.SnippetString; readonly label: string } | undefined> {
if (!uriList.entries.length) {
return;
}
const snippet = new vscode.SnippetString();
for (let i = 0; i < uriList.entries.length; i++) {
const uri = uriList.entries[i];
const relativePath = getRelativePath(getDocumentDir(docUri), uri.uri);
const urlText = relativePath ?? uri.str;
snippet.appendText(`url(${urlText})`);
if (i !== uriList.entries.length - 1) {
snippet.appendText(' ');
}
}
return {
snippet,
label: uriList.entries.length > 1
? vscode.l10n.t('Insert url() Functions')
: vscode.l10n.t('Insert url() Function')
};
}
private pasteAsCssUrlByDefault(document: vscode.TextDocument, position: vscode.Position): boolean {
const regex = /url\(.+?\)/gi;
for (const match of Array.from(document.lineAt(position.line).text.matchAll(regex))) {
if (position.character > match.index && position.character < match.index + match[0].length) {
return false;
}
}
return true;
}
}
function getRelativePath(fromFile: vscode.Uri | undefined, toFile: vscode.Uri): string | undefined {
if (fromFile && fromFile.scheme === toFile.scheme && fromFile.authority === toFile.authority) {
if (toFile.scheme === Schemes.file) {
// On windows, we must use the native `path.relative` to generate the relative path
// so that drive-letters are resolved cast insensitively. However we then want to
// convert back to a posix path to insert in to the document
const relativePath = path.relative(fromFile.fsPath, toFile.fsPath);
return path.posix.normalize(relativePath.split(path.sep).join(path.posix.sep));
}
return path.posix.relative(fromFile.path, toFile.path);
}
return undefined;
}
export function registerDropOrPasteResourceSupport(selector: vscode.DocumentSelector): vscode.Disposable {
const provider = new DropOrPasteResourceProvider();
return vscode.Disposable.from(
vscode.languages.registerDocumentDropEditProvider(selector, provider, {
providedDropEditKinds: [provider.kind],
dropMimeTypes: [
Mimes.uriList,
'files'
]
}),
vscode.languages.registerDocumentPasteEditProvider(selector, provider, {
providedPasteEditKinds: [provider.kind],
pasteMimeTypes: [
Mimes.uriList,
'files'
]
})
);
}
@@ -0,0 +1,42 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { Utils } from 'vscode-uri';
export const Schemes = Object.freeze({
file: 'file',
notebookCell: 'vscode-notebook-cell',
untitled: 'untitled',
});
export const Mimes = Object.freeze({
plain: 'text/plain',
uriList: 'text/uri-list',
});
export function getDocumentDir(uri: vscode.Uri): vscode.Uri | undefined {
const docUri = getParentDocumentUri(uri);
if (docUri.scheme === Schemes.untitled) {
return vscode.workspace.workspaceFolders?.[0]?.uri;
}
return Utils.dirname(docUri);
}
function getParentDocumentUri(uri: vscode.Uri): vscode.Uri {
if (uri.scheme === Schemes.notebookCell) {
// is notebook documents necessary?
for (const notebook of vscode.workspace.notebookDocuments) {
for (const cell of notebook.getCells()) {
if (cell.document.uri.toString() === uri.toString()) {
return notebook.uri;
}
}
}
}
return uri;
}
@@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
function splitUriList(str: string): string[] {
return str.split('\r\n');
}
function parseUriList(str: string): string[] {
return splitUriList(str)
.filter(value => !value.startsWith('#')) // Remove comments
.map(value => value.trim());
}
export class UriList {
static from(str: string): UriList {
return new UriList(coalesce(parseUriList(str).map(line => {
try {
return { uri: vscode.Uri.parse(line), str: line };
} catch {
// Uri parse failure
return undefined;
}
})));
}
constructor(
public readonly entries: ReadonlyArray<{ readonly uri: vscode.Uri; readonly str: string }>
) { }
}
function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
return <T[]>array.filter(e => !!e);
}
@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TextDecoder } from 'util';
import { ExtensionContext, extensions, l10n } from 'vscode';
import { BaseLanguageClient, LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node';
import { LanguageClientConstructor, startClient } from '../cssClient';
import { getNodeFSRequestService } from './nodeFs';
import { registerDropOrPasteResourceSupport } from '../dropOrPaste/dropOrPasteResource';
let client: BaseLanguageClient | undefined;
// this method is called when vs code is activated
export async function activate(context: ExtensionContext) {
const clientMain = extensions.getExtension('vscode.css-language-features')?.packageJSON?.main || '';
const serverMain = `./server/${clientMain.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/cssServerMain`;
const serverModule = context.asAbsolutePath(serverMain);
// The debug options for the server
const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (7000 + Math.round(Math.random() * 999))] };
// If the extension is launch in debug mode the debug server options are use
// Otherwise the run options are used
const serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
};
const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
return new LanguageClient(id, name, serverOptions, clientOptions);
};
// pass the location of the localization bundle to the server
process.env['VSCODE_L10N_BUNDLE_LOCATION'] = l10n.uri?.toString() ?? '';
client = await startClient(context, newLanguageClient, { fs: getNodeFSRequestService(), TextDecoder });
context.subscriptions.push(registerDropOrPasteResourceSupport({ language: 'css', scheme: '*' }));
}
export async function deactivate(): Promise<void> {
if (client) {
await client.stop();
client = undefined;
}
}
@@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import { Uri } from 'vscode';
import { RequestService, FileType } from '../requests';
export function getNodeFSRequestService(): RequestService {
function ensureFileUri(location: string) {
if (!location.startsWith('file://')) {
throw new Error('fileRequestService can only handle file URLs');
}
}
return {
getContent(location: string, encoding?: BufferEncoding) {
ensureFileUri(location);
return new Promise((c, e) => {
const uri = Uri.parse(location);
fs.readFile(uri.fsPath, encoding, (err, buf) => {
if (err) {
return e(err);
}
c(buf.toString());
});
});
},
stat(location: string) {
ensureFileUri(location);
return new Promise((c, e) => {
const uri = Uri.parse(location);
fs.stat(uri.fsPath, (err, stats) => {
if (err) {
if (err.code === 'ENOENT') {
return c({ type: FileType.Unknown, ctime: -1, mtime: -1, size: -1 });
} else {
return e(err);
}
}
let type = FileType.Unknown;
if (stats.isFile()) {
type = FileType.File;
} else if (stats.isDirectory()) {
type = FileType.Directory;
} else if (stats.isSymbolicLink()) {
type = FileType.SymbolicLink;
}
c({
type,
ctime: stats.ctime.getTime(),
mtime: stats.mtime.getTime(),
size: stats.size
});
});
});
},
readDirectory(location: string) {
ensureFileUri(location);
return new Promise((c, e) => {
const path = Uri.parse(location).fsPath;
fs.readdir(path, { withFileTypes: true }, (err, children) => {
if (err) {
return e(err);
}
c(children.map(stat => {
if (stat.isSymbolicLink()) {
return [stat.name, FileType.SymbolicLink];
} else if (stat.isDirectory()) {
return [stat.name, FileType.Directory];
} else if (stat.isFile()) {
return [stat.name, FileType.File];
} else {
return [stat.name, FileType.Unknown];
}
}));
});
});
}
};
}
@@ -0,0 +1,90 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Uri, workspace } from 'vscode';
import { RequestType, BaseLanguageClient } from 'vscode-languageclient';
import { Runtime } from './cssClient';
export namespace FsContentRequest {
export const type: RequestType<{ uri: string; encoding?: string }, string, any> = new RequestType('fs/content');
}
export namespace FsStatRequest {
export const type: RequestType<string, FileStat, any> = new RequestType('fs/stat');
}
export namespace FsReadDirRequest {
export const type: RequestType<string, [string, FileType][], any> = new RequestType('fs/readDir');
}
export function serveFileSystemRequests(client: BaseLanguageClient, runtime: Runtime) {
client.onRequest(FsContentRequest.type, (param: { uri: string; encoding?: string }) => {
const uri = Uri.parse(param.uri);
if (uri.scheme === 'file' && runtime.fs) {
return runtime.fs.getContent(param.uri);
}
return workspace.fs.readFile(uri).then(buffer => {
return new runtime.TextDecoder(param.encoding).decode(buffer);
});
});
client.onRequest(FsReadDirRequest.type, (uriString: string) => {
const uri = Uri.parse(uriString);
if (uri.scheme === 'file' && runtime.fs) {
return runtime.fs.readDirectory(uriString);
}
return workspace.fs.readDirectory(uri);
});
client.onRequest(FsStatRequest.type, (uriString: string) => {
const uri = Uri.parse(uriString);
if (uri.scheme === 'file' && runtime.fs) {
return runtime.fs.stat(uriString);
}
return workspace.fs.stat(uri);
});
}
export enum FileType {
/**
* The file type is unknown.
*/
Unknown = 0,
/**
* A regular file.
*/
File = 1,
/**
* A directory.
*/
Directory = 2,
/**
* A symbolic link to a file.
*/
SymbolicLink = 64
}
export interface FileStat {
/**
* The type of the file, e.g. is a regular file, a directory, or symbolic link
* to a file.
*/
type: FileType;
/**
* The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC.
*/
ctime: number;
/**
* The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC.
*/
mtime: number;
/**
* The size in bytes.
*/
size: number;
}
export interface RequestService {
getContent(uri: string, encoding?: string): Promise<string>;
stat(uri: string): Promise<FileStat>;
readDirectory(uri: string): Promise<[string, FileType][]>;
}