mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-24 10:38:59 +01:00
Merge branch 'master' into pty_bug
This commit is contained in:
@@ -21,6 +21,7 @@ import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import * as callh from 'vs/workbench/contrib/callHierarchy/browser/callHierarchy';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { decodeSemanticTokensDto, ISemanticTokensDto } from 'vs/workbench/api/common/shared/semanticTokens';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
|
||||
export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape {
|
||||
@@ -324,6 +325,12 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
}));
|
||||
}
|
||||
|
||||
// --- semantic coloring
|
||||
|
||||
$registerSemanticColoringProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticColoringLegend): void {
|
||||
this._registrations.set(handle, modes.SemanticColoringProviderRegistry.register(selector, new MainThreadSemanticColoringProvider(this._proxy, handle, legend)));
|
||||
}
|
||||
|
||||
// --- suggest
|
||||
|
||||
private static _inflateSuggestDto(defaultRange: IRange | { insert: IRange, replace: IRange }, data: ISuggestDataDto): modes.CompletionItem {
|
||||
@@ -594,3 +601,76 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MainThreadSemanticColoringCacheEntry implements modes.SemanticColoring {
|
||||
|
||||
constructor(
|
||||
private readonly _parent: MainThreadSemanticColoringProvider,
|
||||
public readonly uri: URI,
|
||||
public readonly id: number,
|
||||
public readonly areas: modes.SemanticColoringArea[],
|
||||
) {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._parent.release(this);
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadSemanticColoringProvider implements modes.SemanticColoringProvider {
|
||||
|
||||
private readonly _cache = new Map<string, MainThreadSemanticColoringCacheEntry>();
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: ExtHostLanguageFeaturesShape,
|
||||
private readonly _handle: number,
|
||||
private readonly _legend: modes.SemanticColoringLegend,
|
||||
) {
|
||||
}
|
||||
|
||||
release(entry: MainThreadSemanticColoringCacheEntry): void {
|
||||
const currentCacheEntry = this._cache.get(entry.uri.toString()) || null;
|
||||
if (currentCacheEntry && currentCacheEntry.id === entry.id) {
|
||||
this._cache.delete(entry.uri.toString());
|
||||
}
|
||||
this._proxy.$releaseSemanticColoring(this._handle, entry.id);
|
||||
}
|
||||
|
||||
getLegend(): modes.SemanticColoringLegend {
|
||||
return this._legend;
|
||||
}
|
||||
|
||||
async provideSemanticColoring(model: ITextModel, token: CancellationToken): Promise<modes.SemanticColoring | null> {
|
||||
const lastResult = this._cache.get(model.uri.toString()) || null;
|
||||
const encodedDto = await this._proxy.$provideSemanticColoring(this._handle, model.uri, lastResult ? lastResult.id : 0, token);
|
||||
if (!encodedDto) {
|
||||
return null;
|
||||
}
|
||||
if (token.isCancellationRequested) {
|
||||
return null;
|
||||
}
|
||||
const dto = decodeSemanticTokensDto(encodedDto);
|
||||
const res = this._resolveDeltas(model, lastResult, dto);
|
||||
this._cache.set(model.uri.toString(), res);
|
||||
return res;
|
||||
}
|
||||
|
||||
private _resolveDeltas(model: ITextModel, lastResult: MainThreadSemanticColoringCacheEntry | null, dto: ISemanticTokensDto): MainThreadSemanticColoringCacheEntry {
|
||||
let areas: modes.SemanticColoringArea[] = [];
|
||||
for (let i = 0, len = dto.areas.length; i < len; i++) {
|
||||
const areaDto = dto.areas[i];
|
||||
if (areaDto.type === 'full') {
|
||||
areas[i] = {
|
||||
line: areaDto.line,
|
||||
data: areaDto.data
|
||||
};
|
||||
} else {
|
||||
areas[i] = {
|
||||
line: areaDto.line,
|
||||
data: lastResult!.areas[areaDto.oldIndex].data
|
||||
};
|
||||
}
|
||||
}
|
||||
return new MainThreadSemanticColoringCacheEntry(this, model.uri, dto.id, areas);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/
|
||||
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { ISaveParticipant, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol';
|
||||
|
||||
export interface ICodeActionsOnSaveOptions {
|
||||
|
||||
@@ -37,7 +37,6 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { createCSSRule, asCSSUrl } from 'vs/base/browser/dom';
|
||||
|
||||
export interface IUserFriendlyViewsContainerDescriptor {
|
||||
id: string;
|
||||
@@ -254,10 +253,9 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
|
||||
private registerTestViewContainer(): void {
|
||||
const title = localize('test', "Test");
|
||||
const cssClass = `extensionViewlet-test`;
|
||||
const icon = URI.parse(require.toUrl('./media/test.svg'));
|
||||
|
||||
this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, cssClass, undefined);
|
||||
this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, undefined);
|
||||
}
|
||||
|
||||
private isValidViewsContainer(viewsContainersDescriptors: IUserFriendlyViewsContainerDescriptor[], collector: ExtensionMessageCollector): boolean {
|
||||
@@ -290,10 +288,9 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
|
||||
private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[]): number {
|
||||
containers.forEach(descriptor => {
|
||||
const cssClass = `extensionViewlet-${descriptor.id}`;
|
||||
const icon = resources.joinPath(extension.extensionLocation, descriptor.icon);
|
||||
const id = `workbench.view.extension.${descriptor.id}`;
|
||||
const viewContainer = this.registerCustomViewContainer(id, descriptor.title, icon, order++, cssClass, extension.identifier);
|
||||
const viewContainer = this.registerCustomViewContainer(id, descriptor.title, icon, order++, extension.identifier);
|
||||
|
||||
// Move those views that belongs to this container
|
||||
if (existingViewContainers.length) {
|
||||
@@ -311,7 +308,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
return order;
|
||||
}
|
||||
|
||||
private registerCustomViewContainer(id: string, title: string, icon: URI, order: number, cssClass: string, extensionId: ExtensionIdentifier | undefined): ViewContainer {
|
||||
private registerCustomViewContainer(id: string, title: string, icon: URI, order: number, extensionId: ExtensionIdentifier | undefined): ViewContainer {
|
||||
let viewContainer = this.viewContainersRegistry.get(id);
|
||||
|
||||
if (!viewContainer) {
|
||||
@@ -339,7 +336,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
CustomViewlet,
|
||||
id,
|
||||
title,
|
||||
cssClass,
|
||||
undefined,
|
||||
order,
|
||||
icon
|
||||
);
|
||||
@@ -363,15 +360,6 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
`View: Show ${title}`,
|
||||
localize('view', "View")
|
||||
);
|
||||
|
||||
// Generate CSS to show the icon in the activity bar
|
||||
const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${cssClass}`;
|
||||
createCSSRule(iconClass, `
|
||||
mask: ${asCSSUrl(icon)} no-repeat 50% 50%;
|
||||
mask-size: 24px;
|
||||
-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%;
|
||||
-webkit-mask-size: 24px;`
|
||||
);
|
||||
}
|
||||
|
||||
return viewContainer;
|
||||
|
||||
@@ -351,6 +351,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacters: string[]): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(extension, checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters));
|
||||
},
|
||||
registerSemanticColoringProvider(selector: vscode.DocumentSelector, provider: vscode.SemanticColoringProvider, legend: vscode.SemanticColoringLegend): vscode.Disposable {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostLanguageFeatures.registerSemanticColoringProvider(extension, checkSelector(selector), provider, legend);
|
||||
},
|
||||
registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, firstItem?: string | vscode.SignatureHelpProviderMetadata, ...remaining: string[]): vscode.Disposable {
|
||||
if (typeof firstItem === 'object') {
|
||||
return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, firstItem);
|
||||
@@ -889,6 +893,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
RelativePattern: extHostTypes.RelativePattern,
|
||||
ResolvedAuthority: extHostTypes.ResolvedAuthority,
|
||||
RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError,
|
||||
SemanticColoring: extHostTypes.SemanticColoring,
|
||||
SemanticColoringArea: extHostTypes.SemanticColoringArea,
|
||||
SemanticColoringLegend: extHostTypes.SemanticColoringLegend,
|
||||
Selection: extHostTypes.Selection,
|
||||
SelectionRange: extHostTypes.SelectionRange,
|
||||
ShellExecution: extHostTypes.ShellExecution,
|
||||
|
||||
@@ -45,7 +45,7 @@ import { ITerminalDimensions, IShellLaunchConfig } from 'vs/workbench/contrib/te
|
||||
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import * as search from 'vs/workbench/services/search/common/search';
|
||||
import { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
|
||||
export interface IEnvironment {
|
||||
@@ -354,6 +354,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
|
||||
$registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void;
|
||||
$registerNavigateTypeSupport(handle: number): void;
|
||||
$registerRenameSupport(handle: number, selector: IDocumentFilterDto[], supportsResolveInitialValues: boolean): void;
|
||||
$registerSemanticColoringProvider(handle: number, selector: IDocumentFilterDto[], legend: modes.SemanticColoringLegend): void;
|
||||
$registerSuggestSupport(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, extensionId: ExtensionIdentifier): void;
|
||||
$registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void;
|
||||
$registerDocumentLinkProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean): void;
|
||||
@@ -1158,6 +1159,8 @@ export interface ExtHostLanguageFeaturesShape {
|
||||
$releaseWorkspaceSymbols(handle: number, id: number): void;
|
||||
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<IWorkspaceEditDto | undefined>;
|
||||
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation | undefined>;
|
||||
$provideSemanticColoring(handle: number, resource: UriComponents, previousSemanticColoringResultId: number, token: CancellationToken): Promise<VSBuffer | null>;
|
||||
$releaseSemanticColoring(handle: number, semanticColoringResultId: number): void;
|
||||
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<ISuggestResultDto | undefined>;
|
||||
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: ChainedCacheId, token: CancellationToken): Promise<ISuggestDataDto | undefined>;
|
||||
$releaseCompletionItems(handle: number, id: number): void;
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, IResou
|
||||
import { TextEdit } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Range, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import * as vscode from 'vscode';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { AsyncEmitter, Emitter, Event, IWaitUntil } from 'vs/base/common/event';
|
||||
import { IRelativePattern, parse } from 'vs/base/common/glob';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import * as vscode from 'vscode';
|
||||
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, IResourceFileEditDto, IResourceTextEditDto, MainThreadTextEditorsShape } from './extHost.protocol';
|
||||
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, MainThreadTextEditorsShape, IResourceFileEditDto, IResourceTextEditDto } from './extHost.protocol';
|
||||
import * as typeConverter from './extHostTypeConverters';
|
||||
import { Disposable, WorkspaceEdit } from './extHostTypes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { FileOperation } from 'vs/platform/files/common/files';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
|
||||
class FileSystemWatcher implements vscode.FileSystemWatcher {
|
||||
|
||||
@@ -142,13 +142,13 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
|
||||
$onDidRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined): void {
|
||||
switch (operation) {
|
||||
case FileOperation.MOVE:
|
||||
this._onDidRenameFile.fire(Object.freeze({ renamed: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }] }));
|
||||
this._onDidRenameFile.fire(Object.freeze({ files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }] }));
|
||||
break;
|
||||
case FileOperation.DELETE:
|
||||
this._onDidDeleteFile.fire(Object.freeze({ deleted: [URI.revive(target)] }));
|
||||
this._onDidDeleteFile.fire(Object.freeze({ files: [URI.revive(target)] }));
|
||||
break;
|
||||
case FileOperation.CREATE:
|
||||
this._onDidCreateFile.fire(Object.freeze({ created: [URI.revive(target)] }));
|
||||
this._onDidCreateFile.fire(Object.freeze({ files: [URI.revive(target)] }));
|
||||
break;
|
||||
default:
|
||||
//ignore, dont send
|
||||
@@ -179,38 +179,39 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
|
||||
async $onWillRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined): Promise<any> {
|
||||
switch (operation) {
|
||||
case FileOperation.MOVE:
|
||||
await this._fireWillRename(URI.revive(source!), URI.revive(target));
|
||||
await this._fireWillEvent(this._onWillRenameFile, { files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }], });
|
||||
break;
|
||||
case FileOperation.DELETE:
|
||||
this._onWillDeleteFile.fireAsync(thenables => (<vscode.FileWillDeleteEvent>{ deleting: [URI.revive(target)], waitUntil: p => thenables.push(Promise.resolve(p)) }));
|
||||
await this._fireWillEvent(this._onWillDeleteFile, { files: [URI.revive(target)] });
|
||||
break;
|
||||
case FileOperation.CREATE:
|
||||
this._onWillCreateFile.fireAsync(thenables => (<vscode.FileWillCreateEvent>{ creating: [URI.revive(target)], waitUntil: p => thenables.push(Promise.resolve(p)) }));
|
||||
await this._fireWillEvent(this._onWillCreateFile, { files: [URI.revive(target)] });
|
||||
break;
|
||||
default:
|
||||
//ignore, dont send
|
||||
}
|
||||
}
|
||||
|
||||
private async _fireWillRename(oldUri: URI, newUri: URI): Promise<any> {
|
||||
private async _fireWillEvent<E extends IWaitUntil>(emitter: AsyncEmitter<E>, data: Omit<E, 'waitUntil'>): Promise<any> {
|
||||
|
||||
const edits: WorkspaceEdit[] = [];
|
||||
await Promise.resolve(this._onWillRenameFile.fireAsync(bucket => {
|
||||
return {
|
||||
renaming: [{ oldUri, newUri }],
|
||||
waitUntil: (thenable: Promise<vscode.WorkspaceEdit>): void => {
|
||||
if (Object.isFrozen(bucket)) {
|
||||
throw new TypeError('waitUntil cannot be called async');
|
||||
}
|
||||
const index = bucket.length;
|
||||
const wrappedThenable = Promise.resolve(thenable).then(result => {
|
||||
// ignore all results except for WorkspaceEdits. Those
|
||||
// are stored in a spare array
|
||||
if (result instanceof WorkspaceEdit) {
|
||||
edits[index] = result;
|
||||
await Promise.resolve(emitter.fireAsync(bucket => {
|
||||
return <E>{
|
||||
...data,
|
||||
...{
|
||||
waitUntil: (thenable: Promise<vscode.WorkspaceEdit>): void => {
|
||||
if (Object.isFrozen(bucket)) {
|
||||
throw new TypeError('waitUntil cannot be called async');
|
||||
}
|
||||
});
|
||||
bucket.push(wrappedThenable);
|
||||
const promise = Promise.resolve(thenable).then(result => {
|
||||
// ignore all results except for WorkspaceEdits. Those
|
||||
// are stored in a spare array
|
||||
if (result instanceof WorkspaceEdit) {
|
||||
edits.push(result);
|
||||
}
|
||||
});
|
||||
bucket.push(promise);
|
||||
}
|
||||
}
|
||||
};
|
||||
}));
|
||||
@@ -223,13 +224,9 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
|
||||
// and apply them in one go.
|
||||
const allEdits = new Array<Array<IResourceFileEditDto | IResourceTextEditDto>>();
|
||||
for (let edit of edits) {
|
||||
if (edit) { // sparse array
|
||||
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
|
||||
allEdits.push(edits);
|
||||
}
|
||||
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
|
||||
allEdits.push(edits);
|
||||
}
|
||||
return this._mainThreadTextEditors.$tryApplyWorkspaceEdit({ edits: flatten(allEdits) });
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import * as vscode from 'vscode';
|
||||
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticColoringArea } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/model';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
@@ -26,6 +26,8 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { IURITransformer } from 'vs/base/common/uriIpc';
|
||||
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { encodeSemanticTokensDto, ISemanticTokensDto, ISemanticTokensAreaDto } from 'vs/workbench/api/common/shared/semanticTokens';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
|
||||
// --- adapter
|
||||
@@ -613,6 +615,364 @@ class RenameAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
export const enum SemanticColoringConstants {
|
||||
/**
|
||||
* Let's aim at having 8KB buffers if possible...
|
||||
* So that would be 8192 / (5 * 4) = 409.6 tokens per area
|
||||
*/
|
||||
DesiredTokensPerArea = 400,
|
||||
|
||||
/**
|
||||
* Try to keep the total number of areas under 1024 if possible,
|
||||
* simply compensate by having more tokens per area...
|
||||
*/
|
||||
DesiredMaxAreas = 1024,
|
||||
|
||||
/**
|
||||
* Threshold for merging multiple delta areas and sending a full area.
|
||||
*/
|
||||
MinTokensPerArea = 50
|
||||
}
|
||||
|
||||
interface ISemanticColoringAreaPair {
|
||||
data: Uint32Array;
|
||||
dto: ISemanticTokensAreaDto;
|
||||
}
|
||||
|
||||
export class SemanticColoringAdapter {
|
||||
|
||||
private readonly _previousResults: Map<number, Uint32Array[]>;
|
||||
private readonly _splitSingleAreaTokenCountThreshold: number;
|
||||
private _nextResultId = 1;
|
||||
|
||||
constructor(
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
private readonly _provider: vscode.SemanticColoringProvider,
|
||||
private readonly _desiredTokensPerArea = SemanticColoringConstants.DesiredTokensPerArea,
|
||||
private readonly _desiredMaxAreas = SemanticColoringConstants.DesiredMaxAreas,
|
||||
private readonly _minTokensPerArea = SemanticColoringConstants.MinTokensPerArea
|
||||
) {
|
||||
this._previousResults = new Map<number, Uint32Array[]>();
|
||||
this._splitSingleAreaTokenCountThreshold = Math.round(1.5 * this._desiredTokensPerArea);
|
||||
}
|
||||
|
||||
provideSemanticColoring(resource: URI, previousSemanticColoringResultId: number, token: CancellationToken): Promise<VSBuffer | null> {
|
||||
const doc = this._documents.getDocument(resource);
|
||||
|
||||
return asPromise(() => this._provider.provideSemanticColoring(doc, token)).then(value => {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const oldAreas = (previousSemanticColoringResultId !== 0 ? this._previousResults.get(previousSemanticColoringResultId) : null);
|
||||
if (oldAreas) {
|
||||
this._previousResults.delete(previousSemanticColoringResultId);
|
||||
return this._deltaEncodeAreas(oldAreas, value.areas);
|
||||
}
|
||||
|
||||
return this._fullEncodeAreas(value.areas);
|
||||
});
|
||||
}
|
||||
|
||||
async releaseSemanticColoring(semanticColoringResultId: number): Promise<void> {
|
||||
this._previousResults.delete(semanticColoringResultId);
|
||||
}
|
||||
|
||||
private _deltaEncodeAreas(oldAreas: Uint32Array[], newAreas: SemanticColoringArea[]): VSBuffer {
|
||||
if (newAreas.length > 1) {
|
||||
// this is a fancy provider which is smart enough to break things into good areas
|
||||
// we therefore try to match old areas only by object identity
|
||||
const oldAreasIndexMap = new Map<Uint32Array, number>();
|
||||
for (let i = 0, len = oldAreas.length; i < len; i++) {
|
||||
oldAreasIndexMap.set(oldAreas[i], i);
|
||||
}
|
||||
|
||||
let result: ISemanticColoringAreaPair[] = [];
|
||||
for (let i = 0, len = newAreas.length; i < len; i++) {
|
||||
const newArea = newAreas[i];
|
||||
if (oldAreasIndexMap.has(newArea.data)) {
|
||||
// great! we can reuse this area
|
||||
const oldIndex = oldAreasIndexMap.get(newArea.data)!;
|
||||
result.push({
|
||||
data: newArea.data,
|
||||
dto: {
|
||||
type: 'delta',
|
||||
line: newArea.line,
|
||||
oldIndex: oldIndex
|
||||
}
|
||||
});
|
||||
} else {
|
||||
result.push({
|
||||
data: newArea.data,
|
||||
dto: {
|
||||
type: 'full',
|
||||
line: newArea.line,
|
||||
data: newArea.data
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this._saveResultAndEncode(result);
|
||||
}
|
||||
|
||||
return this._deltaEncodeArea(oldAreas, newAreas[0]);
|
||||
}
|
||||
|
||||
private static _oldAreaAppearsInNewArea(oldAreaData: Uint32Array, oldAreaTokenCount: number, newAreaData: Uint32Array, newAreaOffset: number): boolean {
|
||||
const newTokenStartDeltaLine = newAreaData[5 * newAreaOffset];
|
||||
|
||||
// check that each and every value from `oldArea` is equal to `area`
|
||||
for (let j = 0; j < oldAreaTokenCount; j++) {
|
||||
const oldOffset = 5 * j;
|
||||
const newOffset = 5 * (j + newAreaOffset);
|
||||
|
||||
if (
|
||||
(oldAreaData[oldOffset] !== newAreaData[newOffset] - newTokenStartDeltaLine)
|
||||
|| (oldAreaData[oldOffset + 1] !== newAreaData[newOffset + 1])
|
||||
|| (oldAreaData[oldOffset + 2] !== newAreaData[newOffset + 2])
|
||||
|| (oldAreaData[oldOffset + 3] !== newAreaData[newOffset + 3])
|
||||
|| (oldAreaData[oldOffset + 4] !== newAreaData[newOffset + 4])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private _deltaEncodeArea(oldAreas: Uint32Array[], newArea: SemanticColoringArea): VSBuffer {
|
||||
const newAreaData = newArea.data;
|
||||
const prependAreas: ISemanticColoringAreaPair[] = [];
|
||||
const appendAreas: ISemanticColoringAreaPair[] = [];
|
||||
|
||||
// Try to find appearences of `oldAreas` inside `area`.
|
||||
let newTokenStartIndex = 0;
|
||||
let newTokenEndIndex = (newAreaData.length / 5) | 0;
|
||||
let oldAreaUsedIndex = -1;
|
||||
for (let i = 0, len = oldAreas.length; i < len; i++) {
|
||||
const oldAreaData = oldAreas[i];
|
||||
const oldAreaTokenCount = (oldAreaData.length / 5) | 0;
|
||||
if (oldAreaTokenCount === 0) {
|
||||
// skip old empty areas
|
||||
continue;
|
||||
}
|
||||
if (newTokenEndIndex - newTokenStartIndex < oldAreaTokenCount) {
|
||||
// there are too many old tokens, this cannot work
|
||||
break;
|
||||
}
|
||||
|
||||
const newAreaOffset = newTokenStartIndex;
|
||||
const newTokenStartDeltaLine = newAreaData[5 * newAreaOffset];
|
||||
const isEqual = SemanticColoringAdapter._oldAreaAppearsInNewArea(oldAreaData, oldAreaTokenCount, newAreaData, newAreaOffset);
|
||||
if (!isEqual) {
|
||||
break;
|
||||
}
|
||||
newTokenStartIndex += oldAreaTokenCount;
|
||||
|
||||
oldAreaUsedIndex = i;
|
||||
prependAreas.push({
|
||||
data: oldAreaData,
|
||||
dto: {
|
||||
type: 'delta',
|
||||
line: newArea.line + newTokenStartDeltaLine,
|
||||
oldIndex: i
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = oldAreas.length - 1; i > oldAreaUsedIndex; i--) {
|
||||
const oldAreaData = oldAreas[i];
|
||||
const oldAreaTokenCount = (oldAreaData.length / 5) | 0;
|
||||
if (oldAreaTokenCount === 0) {
|
||||
// skip old empty areas
|
||||
continue;
|
||||
}
|
||||
if (newTokenEndIndex - newTokenStartIndex < oldAreaTokenCount) {
|
||||
// there are too many old tokens, this cannot work
|
||||
break;
|
||||
}
|
||||
|
||||
const newAreaOffset = (newTokenEndIndex - oldAreaTokenCount);
|
||||
const newTokenStartDeltaLine = newAreaData[5 * newAreaOffset];
|
||||
const isEqual = SemanticColoringAdapter._oldAreaAppearsInNewArea(oldAreaData, oldAreaTokenCount, newAreaData, newAreaOffset);
|
||||
if (!isEqual) {
|
||||
break;
|
||||
}
|
||||
newTokenEndIndex -= oldAreaTokenCount;
|
||||
|
||||
appendAreas.unshift({
|
||||
data: oldAreaData,
|
||||
dto: {
|
||||
type: 'delta',
|
||||
line: newArea.line + newTokenStartDeltaLine,
|
||||
oldIndex: i
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (prependAreas.length === 0 && appendAreas.length === 0) {
|
||||
// There is no reuse possibility!
|
||||
return this._fullEncodeAreas([newArea]);
|
||||
}
|
||||
|
||||
if (newTokenStartIndex === newTokenEndIndex) {
|
||||
// 100% reuse!
|
||||
return this._saveResultAndEncode(prependAreas.concat(appendAreas));
|
||||
}
|
||||
|
||||
// It is clear at this point that there will be at least one full area.
|
||||
// Expand the mid area if the areas next to it are too small
|
||||
while (prependAreas.length > 0) {
|
||||
const tokenCount = (prependAreas[prependAreas.length - 1].data.length / 5);
|
||||
if (tokenCount < this._minTokensPerArea) {
|
||||
newTokenStartIndex -= tokenCount;
|
||||
prependAreas.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (appendAreas.length > 0) {
|
||||
const tokenCount = (appendAreas[0].data.length / 5);
|
||||
if (tokenCount < this._minTokensPerArea) {
|
||||
newTokenEndIndex += tokenCount;
|
||||
appendAreas.shift();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the mid area
|
||||
const newTokenStartDeltaLine = newAreaData[5 * newTokenStartIndex];
|
||||
const newMidAreaData = new Uint32Array(5 * (newTokenEndIndex - newTokenStartIndex));
|
||||
for (let tokenIndex = newTokenStartIndex; tokenIndex < newTokenEndIndex; tokenIndex++) {
|
||||
const srcOffset = 5 * tokenIndex;
|
||||
const deltaLine = newAreaData[srcOffset];
|
||||
const startCharacter = newAreaData[srcOffset + 1];
|
||||
const endCharacter = newAreaData[srcOffset + 2];
|
||||
const tokenType = newAreaData[srcOffset + 3];
|
||||
const tokenModifiers = newAreaData[srcOffset + 4];
|
||||
|
||||
const destOffset = 5 * (tokenIndex - newTokenStartIndex);
|
||||
newMidAreaData[destOffset] = deltaLine - newTokenStartDeltaLine;
|
||||
newMidAreaData[destOffset + 1] = startCharacter;
|
||||
newMidAreaData[destOffset + 2] = endCharacter;
|
||||
newMidAreaData[destOffset + 3] = tokenType;
|
||||
newMidAreaData[destOffset + 4] = tokenModifiers;
|
||||
}
|
||||
|
||||
const newMidArea = new SemanticColoringArea(newArea.line + newTokenStartDeltaLine, newMidAreaData);
|
||||
const newMidAreas = this._splitAreaIntoMultipleAreasIfNecessary(newMidArea);
|
||||
const newMidAreasPairs: ISemanticColoringAreaPair[] = newMidAreas.map(a => {
|
||||
return {
|
||||
data: a.data,
|
||||
dto: {
|
||||
type: 'full',
|
||||
line: a.line,
|
||||
data: a.data,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return this._saveResultAndEncode(prependAreas.concat(newMidAreasPairs).concat(appendAreas));
|
||||
}
|
||||
|
||||
private _fullEncodeAreas(areas: SemanticColoringArea[]): VSBuffer {
|
||||
if (areas.length === 1) {
|
||||
areas = this._splitAreaIntoMultipleAreasIfNecessary(areas[0]);
|
||||
}
|
||||
|
||||
return this._saveResultAndEncode(areas.map(a => {
|
||||
return {
|
||||
data: a.data,
|
||||
dto: {
|
||||
type: 'full',
|
||||
line: a.line,
|
||||
data: a.data
|
||||
}
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
private _saveResultAndEncode(areas: ISemanticColoringAreaPair[]): VSBuffer {
|
||||
const myId = this._nextResultId++;
|
||||
this._previousResults.set(myId, areas.map(a => a.data));
|
||||
console.log(`_saveResultAndEncode: ${myId} --> ${areas.map(a => `${a.dto.line}-${a.dto.type}(${a.data.length / 5})`).join(', ')}`);
|
||||
const dto: ISemanticTokensDto = {
|
||||
id: myId,
|
||||
areas: areas.map(a => a.dto)
|
||||
};
|
||||
return encodeSemanticTokensDto(dto);
|
||||
}
|
||||
|
||||
private _splitAreaIntoMultipleAreasIfNecessary(area: vscode.SemanticColoringArea): SemanticColoringArea[] {
|
||||
const srcAreaLine = area.line;
|
||||
const srcAreaData = area.data;
|
||||
const tokenCount = (srcAreaData.length / 5) | 0;
|
||||
if (tokenCount <= this._splitSingleAreaTokenCountThreshold) {
|
||||
return [area];
|
||||
}
|
||||
|
||||
const tokensPerArea = Math.max(Math.ceil(tokenCount / this._desiredMaxAreas), this._desiredTokensPerArea);
|
||||
|
||||
let result: SemanticColoringArea[] = [];
|
||||
let tokenIndex = 0;
|
||||
while (tokenIndex < tokenCount) {
|
||||
const tokenStartIndex = tokenIndex;
|
||||
let tokenEndIndex = Math.min(tokenStartIndex + tokensPerArea, tokenCount);
|
||||
|
||||
// Keep tokens on the same line in the same area...
|
||||
if (tokenEndIndex < tokenCount) {
|
||||
let smallAvoidDeltaLine = srcAreaData[5 * tokenEndIndex];
|
||||
let smallTokenEndIndex = tokenEndIndex;
|
||||
while (smallTokenEndIndex - 1 > tokenStartIndex && srcAreaData[5 * (smallTokenEndIndex - 1)] === smallAvoidDeltaLine) {
|
||||
smallTokenEndIndex--;
|
||||
}
|
||||
|
||||
if (smallTokenEndIndex - 1 === tokenStartIndex) {
|
||||
// there are so many tokens on this line that our area would be empty, we must now go right
|
||||
let bigAvoidDeltaLine = srcAreaData[5 * (tokenEndIndex - 1)];
|
||||
let bigTokenEndIndex = tokenEndIndex;
|
||||
while (bigTokenEndIndex + 1 < tokenCount && srcAreaData[5 * (bigTokenEndIndex + 1)] === bigAvoidDeltaLine) {
|
||||
bigTokenEndIndex++;
|
||||
}
|
||||
tokenEndIndex = bigTokenEndIndex;
|
||||
} else {
|
||||
tokenEndIndex = smallTokenEndIndex;
|
||||
}
|
||||
}
|
||||
|
||||
let destAreaLine = 0;
|
||||
const destAreaData = new Uint32Array((tokenEndIndex - tokenStartIndex) * 5);
|
||||
while (tokenIndex < tokenEndIndex) {
|
||||
const srcOffset = 5 * tokenIndex;
|
||||
const line = srcAreaLine + srcAreaData[srcOffset];
|
||||
const startCharacter = srcAreaData[srcOffset + 1];
|
||||
const endCharacter = srcAreaData[srcOffset + 2];
|
||||
const tokenType = srcAreaData[srcOffset + 3];
|
||||
const tokenModifiers = srcAreaData[srcOffset + 4];
|
||||
|
||||
if (tokenIndex === tokenStartIndex) {
|
||||
destAreaLine = line;
|
||||
}
|
||||
|
||||
const destOffset = 5 * (tokenIndex - tokenStartIndex);
|
||||
destAreaData[destOffset] = line - destAreaLine;
|
||||
destAreaData[destOffset + 1] = startCharacter;
|
||||
destAreaData[destOffset + 2] = endCharacter;
|
||||
destAreaData[destOffset + 3] = tokenType;
|
||||
destAreaData[destOffset + 4] = tokenModifiers;
|
||||
|
||||
tokenIndex++;
|
||||
}
|
||||
|
||||
result.push(new SemanticColoringArea(destAreaLine, destAreaData));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class SuggestAdapter {
|
||||
|
||||
static supportsResolving(provider: vscode.CompletionItemProvider): boolean {
|
||||
@@ -1120,8 +1480,9 @@ class CallHierarchyAdapter {
|
||||
type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
|
||||
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
|
||||
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
|
||||
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter
|
||||
| ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter;
|
||||
| SemanticColoringAdapter | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter
|
||||
| ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter
|
||||
| DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter;
|
||||
|
||||
class AdapterData {
|
||||
constructor(
|
||||
@@ -1445,6 +1806,24 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
||||
return this._withAdapter(handle, RenameAdapter, adapter => adapter.resolveRenameLocation(URI.revive(resource), position, token), undefined);
|
||||
}
|
||||
|
||||
//#region semantic coloring
|
||||
|
||||
registerSemanticColoringProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SemanticColoringProvider, legend: vscode.SemanticColoringLegend): vscode.Disposable {
|
||||
const handle = this._addNewAdapter(new SemanticColoringAdapter(this._documents, provider), extension);
|
||||
this._proxy.$registerSemanticColoringProvider(handle, this._transformDocumentSelector(selector), legend);
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
||||
$provideSemanticColoring(handle: number, resource: UriComponents, previousSemanticColoringResultId: number, token: CancellationToken): Promise<VSBuffer | null> {
|
||||
return this._withAdapter(handle, SemanticColoringAdapter, adapter => adapter.provideSemanticColoring(URI.revive(resource), previousSemanticColoringResultId, token), null);
|
||||
}
|
||||
|
||||
$releaseSemanticColoring(handle: number, semanticColoringResultId: number): void {
|
||||
this._withAdapter(handle, SemanticColoringAdapter, adapter => adapter.releaseSemanticColoring(semanticColoringResultId), undefined);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
// --- suggestion
|
||||
|
||||
registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/mode
|
||||
import * as vscode from 'vscode';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import * as editorRange from 'vs/editor/common/core/range';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
|
||||
@@ -2368,6 +2368,38 @@ export enum CommentMode {
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Semantic Coloring
|
||||
|
||||
export class SemanticColoringLegend {
|
||||
public readonly tokenTypes: string[];
|
||||
public readonly tokenModifiers: string[];
|
||||
|
||||
constructor(tokenTypes: string[], tokenModifiers: string[]) {
|
||||
this.tokenTypes = tokenTypes;
|
||||
this.tokenModifiers = tokenModifiers;
|
||||
}
|
||||
}
|
||||
|
||||
export class SemanticColoringArea {
|
||||
public readonly line: number;
|
||||
public readonly data: Uint32Array;
|
||||
|
||||
constructor(line: number, data: Uint32Array) {
|
||||
this.line = line;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
export class SemanticColoring {
|
||||
public readonly areas: SemanticColoringArea[];
|
||||
|
||||
constructor(areas: SemanticColoringArea[]) {
|
||||
this.areas = areas;
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region debug
|
||||
export enum DebugConsoleMode {
|
||||
/**
|
||||
|
||||
136
src/vs/workbench/api/common/shared/semanticTokens.ts
Normal file
136
src/vs/workbench/api/common/shared/semanticTokens.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
|
||||
export interface ISemanticTokensFullAreaDto {
|
||||
type: 'full';
|
||||
line: number;
|
||||
data: Uint32Array;
|
||||
}
|
||||
|
||||
export interface ISemanticTokensDeltaAreaDto {
|
||||
type: 'delta';
|
||||
line: number;
|
||||
oldIndex: number;
|
||||
}
|
||||
|
||||
export type ISemanticTokensAreaDto = ISemanticTokensFullAreaDto | ISemanticTokensDeltaAreaDto;
|
||||
|
||||
export interface ISemanticTokensDto {
|
||||
id: number;
|
||||
areas: ISemanticTokensAreaDto[];
|
||||
}
|
||||
|
||||
const enum EncodedSemanticTokensAreaType {
|
||||
Full = 1,
|
||||
Delta = 2
|
||||
}
|
||||
|
||||
export function encodeSemanticTokensDto(semanticTokens: ISemanticTokensDto): VSBuffer {
|
||||
const buff = VSBuffer.alloc(encodedSize(semanticTokens));
|
||||
let offset = 0;
|
||||
buff.writeUInt32BE(semanticTokens.id, offset); offset += 4;
|
||||
buff.writeUInt32BE(semanticTokens.areas.length, offset); offset += 4;
|
||||
for (let i = 0; i < semanticTokens.areas.length; i++) {
|
||||
offset = encodeArea(semanticTokens.areas[i], buff, offset);
|
||||
}
|
||||
return buff;
|
||||
}
|
||||
|
||||
function encodedSize(semanticTokens: ISemanticTokensDto): number {
|
||||
let result = 0;
|
||||
result += 4; // etag
|
||||
result += 4; // area count
|
||||
for (let i = 0; i < semanticTokens.areas.length; i++) {
|
||||
result += encodedAreaSize(semanticTokens.areas[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function decodeSemanticTokensDto(buff: VSBuffer): ISemanticTokensDto {
|
||||
let offset = 0;
|
||||
const id = buff.readUInt32BE(offset); offset += 4;
|
||||
const areasCount = buff.readUInt32BE(offset); offset += 4;
|
||||
let areas: ISemanticTokensAreaDto[] = [];
|
||||
for (let i = 0; i < areasCount; i++) {
|
||||
offset = decodeArea(buff, offset, areas);
|
||||
}
|
||||
return {
|
||||
id: id,
|
||||
areas: areas
|
||||
};
|
||||
}
|
||||
|
||||
function encodeArea(area: ISemanticTokensAreaDto, buff: VSBuffer, offset: number): number {
|
||||
buff.writeUInt8(area.type === 'full' ? EncodedSemanticTokensAreaType.Full : EncodedSemanticTokensAreaType.Delta, offset); offset += 1;
|
||||
buff.writeUInt32BE(area.line + 1, offset); offset += 4;
|
||||
if (area.type === 'full') {
|
||||
const tokens = area.data;
|
||||
const tokenCount = (tokens.length / 5) | 0;
|
||||
buff.writeUInt32BE(tokenCount, offset); offset += 4;
|
||||
// here we are explicitly iterating an writing the ints again to ensure writing the desired endianness.
|
||||
for (let i = 0; i < tokenCount; i++) {
|
||||
const tokenOffset = 5 * i;
|
||||
buff.writeUInt32BE(tokens[tokenOffset], offset); offset += 4;
|
||||
buff.writeUInt32BE(tokens[tokenOffset + 1], offset); offset += 4;
|
||||
buff.writeUInt32BE(tokens[tokenOffset + 2], offset); offset += 4;
|
||||
buff.writeUInt32BE(tokens[tokenOffset + 3], offset); offset += 4;
|
||||
buff.writeUInt32BE(tokens[tokenOffset + 4], offset); offset += 4;
|
||||
}
|
||||
// buff.set(VSBuffer.wrap(uint8), offset); offset += area.data.byteLength;
|
||||
} else {
|
||||
buff.writeUInt32BE(area.oldIndex, offset); offset += 4;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
function encodedAreaSize(area: ISemanticTokensAreaDto): number {
|
||||
let result = 0;
|
||||
result += 1; // type
|
||||
result += 4; // line
|
||||
if (area.type === 'full') {
|
||||
const tokens = area.data;
|
||||
const tokenCount = (tokens.length / 5) | 0;
|
||||
result += 4; // token count
|
||||
result += tokenCount * 5 * 4;
|
||||
return result;
|
||||
} else {
|
||||
result += 4; // old index
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function decodeArea(buff: VSBuffer, offset: number, areas: ISemanticTokensAreaDto[]): number {
|
||||
const type: EncodedSemanticTokensAreaType = buff.readUInt8(offset); offset += 1;
|
||||
const line = buff.readUInt32BE(offset); offset += 4;
|
||||
if (type === EncodedSemanticTokensAreaType.Full) {
|
||||
// here we are explicitly iterating and reading the ints again to ensure reading the desired endianness.
|
||||
const tokenCount = buff.readUInt32BE(offset); offset += 4;
|
||||
const data = new Uint32Array(5 * tokenCount);
|
||||
for (let i = 0; i < tokenCount; i++) {
|
||||
const destOffset = 5 * i;
|
||||
data[destOffset] = buff.readUInt32BE(offset); offset += 4;
|
||||
data[destOffset + 1] = buff.readUInt32BE(offset); offset += 4;
|
||||
data[destOffset + 2] = buff.readUInt32BE(offset); offset += 4;
|
||||
data[destOffset + 3] = buff.readUInt32BE(offset); offset += 4;
|
||||
data[destOffset + 4] = buff.readUInt32BE(offset); offset += 4;
|
||||
}
|
||||
areas.push({
|
||||
type: 'full',
|
||||
line: line,
|
||||
data: data
|
||||
});
|
||||
return offset;
|
||||
} else {
|
||||
const oldIndex = buff.readUInt32BE(offset); offset += 4;
|
||||
areas.push({
|
||||
type: 'delta',
|
||||
line: line,
|
||||
oldIndex: oldIndex
|
||||
});
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user