Add documentation for using Milkdown with various frameworks
- Created a new document for using components in Milkdown. - Added a guide for using plugins in Milkdown, including toggling plugins programmatically and listing official plugins. - Introduced a recipe for integrating Milkdown with Angular, including installation steps and component creation. - Added a recipe for using Milkdown with Next.js, detailing installation and component setup. - Created a guide for integrating Milkdown with NuxtJS, including installation and component creation. - Added a comprehensive guide for using Milkdown with React, covering both Crepe and core Milkdown usage. - Introduced a recipe for SolidJS integration with Milkdown, including installation and component creation. - Added a guide for using Milkdown with Svelte, detailing installation and component setup. - Created a comprehensive guide for integrating Milkdown with Vue, covering both Crepe and core Milkdown usage. - Added a recipe for using Milkdown with Vue2, including installation and component creation.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { InlineEditRequestLogContext } from '../../../platform/inlineEdits/common/inlineEditLogContext';
|
||||
import { basename } from '../../../util/vs/base/common/path';
|
||||
|
||||
export class GhostTextContext extends InlineEditRequestLogContext {
|
||||
override getDebugName(): string {
|
||||
return `Ghost | ${basename(this.filePath)} (v${this.version})`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITelemetryService, TelemetryEventMeasurements, TelemetryEventProperties } from '../../../../../platform/telemetry/common/telemetry';
|
||||
import { wrapEventNameForPrefixRemoval } from '../../../../../platform/telemetry/node/azureInsightsReporter';
|
||||
import { createServiceIdentifier } from '../../../../../util/common/services';
|
||||
import { TelemetryMeasurements, TelemetryProperties, TelemetryStore } from '../../lib/src/telemetry';
|
||||
import type { TelemetrySpy } from '../../lib/src/test/telemetrySpy';
|
||||
|
||||
export const ICompletionsTelemetryService = createServiceIdentifier<ICompletionsTelemetryService>('completionsTelemetryService');
|
||||
export interface ICompletionsTelemetryService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
sendGHTelemetryEvent(eventName: string, properties?: TelemetryEventProperties, measurements?: TelemetryEventMeasurements, store?: TelemetryStore): void;
|
||||
sendEnhancedGHTelemetryEvent(eventName: string, properties?: TelemetryEventProperties, measurements?: TelemetryEventMeasurements, store?: TelemetryStore): void;
|
||||
sendGHTelemetryErrorEvent(eventName: string, properties?: TelemetryEventProperties, measurements?: TelemetryEventMeasurements, store?: TelemetryStore): void;
|
||||
sendGHTelemetryException(maybeError: unknown, origin: string, store?: TelemetryStore): void;
|
||||
setSpyReporters(reporter: TelemetrySpy, enhancedReporter: TelemetrySpy): void;
|
||||
clearSpyReporters(): void;
|
||||
}
|
||||
|
||||
export class CompletionsTelemetryServiceBridge implements ICompletionsTelemetryService {
|
||||
declare _serviceBrand: undefined;
|
||||
|
||||
private reporter: TelemetrySpy | undefined;
|
||||
private enhancedReporter: TelemetrySpy | undefined;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService
|
||||
) {
|
||||
this.reporter = undefined;
|
||||
this.enhancedReporter = undefined;
|
||||
}
|
||||
|
||||
sendGHTelemetryEvent(eventName: string, properties?: TelemetryEventProperties, measurements?: TelemetryEventMeasurements, store?: TelemetryStore): void {
|
||||
this.telemetryService.sendGHTelemetryEvent(wrapEventNameForPrefixRemoval(`copilot/${eventName}`), properties, measurements);
|
||||
this.getSpyReporters(store ?? TelemetryStore.Standard)?.sendTelemetryEvent(eventName, properties as TelemetryProperties, measurements as TelemetryMeasurements);
|
||||
}
|
||||
|
||||
sendEnhancedGHTelemetryEvent(eventName: string, properties?: TelemetryEventProperties, measurements?: TelemetryEventMeasurements, store?: TelemetryStore): void {
|
||||
this.telemetryService.sendEnhancedGHTelemetryEvent(wrapEventNameForPrefixRemoval(`copilot/${eventName}`), properties, measurements);
|
||||
this.getSpyReporters(store ?? TelemetryStore.Enhanced)?.sendTelemetryEvent(eventName, properties as TelemetryProperties, measurements as TelemetryMeasurements);
|
||||
}
|
||||
|
||||
sendGHTelemetryErrorEvent(eventName: string, properties?: TelemetryEventProperties, measurements?: TelemetryEventMeasurements, store?: TelemetryStore): void {
|
||||
this.telemetryService.sendGHTelemetryErrorEvent(wrapEventNameForPrefixRemoval(`copilot/${eventName}`), properties, measurements);
|
||||
this.getSpyReporters(store ?? TelemetryStore.Enhanced)?.sendTelemetryErrorEvent(eventName, properties as TelemetryProperties, measurements as TelemetryMeasurements);
|
||||
}
|
||||
|
||||
sendGHTelemetryException(maybeError: unknown, origin: string, store?: TelemetryStore): void {
|
||||
this.telemetryService.sendGHTelemetryException(maybeError, origin);
|
||||
if (maybeError instanceof Error) {
|
||||
this.getSpyReporters(store ?? TelemetryStore.Enhanced)?.sendTelemetryException(maybeError as Error, undefined, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
setSpyReporters(reporter: TelemetrySpy, enhancedReporter: TelemetrySpy) {
|
||||
this.reporter = reporter;
|
||||
this.enhancedReporter = enhancedReporter;
|
||||
}
|
||||
|
||||
clearSpyReporters() {
|
||||
this.reporter = undefined;
|
||||
this.enhancedReporter = undefined;
|
||||
}
|
||||
|
||||
private getSpyReporters(store: TelemetryStore): TelemetrySpy | undefined {
|
||||
if (TelemetryStore.isEnhanced(store)) {
|
||||
return this.enhancedReporter;
|
||||
} else {
|
||||
return this.reporter;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { commands, env } from 'vscode';
|
||||
import { ILogService } from '../../../platform/log/common/logService';
|
||||
import { outputChannel } from '../../../platform/log/vscode/outputChannelLogTarget';
|
||||
import { DisposableStore, IDisposable } from '../../../util/vs/base/common/lifecycle';
|
||||
import { URI } from '../../../util/vs/base/common/uri';
|
||||
import { SyncDescriptor } from '../../../util/vs/platform/instantiation/common/descriptors';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from '../../../util/vs/platform/instantiation/common/serviceCollection';
|
||||
import { CompletionsTelemetryServiceBridge, ICompletionsTelemetryService } from './bridge/src/completionsTelemetryServiceBridge';
|
||||
import { LoggingCitationManager } from './extension/src/codeReferencing/citationManager';
|
||||
import { CompletionsObservableWorkspace } from './extension/src/completionsObservableWorkspace';
|
||||
import { disableCompletions, enableCompletions, toggleCompletions, VSCodeConfigProvider, VSCodeEditorInfo } from './extension/src/config';
|
||||
import { CMDDisableCompletionsChat, CMDDisableCompletionsClient, CMDEnableCompletionsChat, CMDEnableCompletionsClient, CMDOpenDocumentationClient, CMDOpenLogsClient, CMDOpenModelPickerChat, CMDOpenModelPickerClient, CMDToggleCompletionsChat, CMDToggleCompletionsClient, CMDToggleStatusMenuChat, CMDToggleStatusMenuClient } from './extension/src/constants';
|
||||
import { contextProviderMatch } from './extension/src/contextProviderMatch';
|
||||
import { registerPanelSupport } from './extension/src/copilotPanel/common';
|
||||
import { CopilotExtensionStatus, ICompletionsExtensionStatus } from './extension/src/extensionStatus';
|
||||
import { extensionFileSystem } from './extension/src/fileSystem';
|
||||
import { ModelPickerManager } from './extension/src/modelPicker';
|
||||
import { CopilotStatusBar } from './extension/src/statusBar';
|
||||
import { CopilotStatusBarPickMenu } from './extension/src/statusBarPicker';
|
||||
import { ExtensionTextDocumentManager } from './extension/src/textDocumentManager';
|
||||
import { exception } from './extension/src/vscodeInlineCompletionItemProvider';
|
||||
import { CopilotTokenManagerImpl, ICompletionsCopilotTokenManager } from './lib/src/auth/copilotTokenManager';
|
||||
import { ICompletionsCitationManager } from './lib/src/citationManager';
|
||||
import { CompletionNotifier, ICompletionsNotifierService } from './lib/src/completionNotifier';
|
||||
import { ICompletionsObservableWorkspace } from './lib/src/completionsObservableWorkspace';
|
||||
import { ICompletionsConfigProvider, ICompletionsEditorAndPluginInfo } from './lib/src/config';
|
||||
import { registerDocumentTracker } from './lib/src/documentTracker';
|
||||
import { ICompletionsUserErrorNotifierService, UserErrorNotifier } from './lib/src/error/userErrorNotifier';
|
||||
import { setupCompletionsExperimentationService } from './lib/src/experiments/defaultExpFilters';
|
||||
import { Features } from './lib/src/experiments/features';
|
||||
import { ICompletionsFeaturesService } from './lib/src/experiments/featuresService';
|
||||
import { FileReader, ICompletionsFileReaderService } from './lib/src/fileReader';
|
||||
import { ICompletionsFileSystemService } from './lib/src/fileSystem';
|
||||
import { AsyncCompletionManager, ICompletionsAsyncManagerService } from './lib/src/ghostText/asyncCompletions';
|
||||
import { CompletionsCache, ICompletionsCacheService } from './lib/src/ghostText/completionsCache';
|
||||
import { ConfigBlockModeConfig, ICompletionsBlockModeConfig } from './lib/src/ghostText/configBlockMode';
|
||||
import { CurrentGhostText, ICompletionsCurrentGhostText } from './lib/src/ghostText/current';
|
||||
import { ICompletionsLastGhostText, LastGhostText } from './lib/src/ghostText/last';
|
||||
import { ICompletionsSpeculativeRequestCache, SpeculativeRequestCache } from './lib/src/ghostText/speculativeRequestCache';
|
||||
import { ICompletionsLogTargetService, LogLevel } from './lib/src/logger';
|
||||
import { formatLogMessage } from './lib/src/logging/util';
|
||||
import { CompletionsFetcher, ICompletionsFetcherService } from './lib/src/networking';
|
||||
import { ExtensionNotificationSender, ICompletionsNotificationSender } from './lib/src/notificationSender';
|
||||
import { ICompletionsOpenAIFetcherService, LiveOpenAIFetcher } from './lib/src/openai/fetch';
|
||||
import { AvailableModelsManager, ICompletionsModelManagerService } from './lib/src/openai/model';
|
||||
import { ICompletionsStatusReporter } from './lib/src/progress';
|
||||
import {
|
||||
CompletionsPromptFactory, ICompletionsPromptFactoryService
|
||||
} from './lib/src/prompt/completionsPromptFactory/completionsPromptFactory';
|
||||
import { ContextProviderBridge, ICompletionsContextProviderBridgeService } from './lib/src/prompt/components/contextProviderBridge';
|
||||
import {
|
||||
CachedContextProviderRegistry,
|
||||
CoreContextProviderRegistry,
|
||||
DefaultContextProvidersContainer, ICompletionsContextProviderRegistryService,
|
||||
ICompletionsDefaultContextProviders
|
||||
} from './lib/src/prompt/contextProviderRegistry';
|
||||
import { ContextProviderStatistics, ICompletionsContextProviderService } from './lib/src/prompt/contextProviderStatistics';
|
||||
import { FullRecentEditsProvider, ICompletionsRecentEditsProviderService } from './lib/src/prompt/recentEdits/recentEditsProvider';
|
||||
import { CompositeRelatedFilesProvider } from './lib/src/prompt/similarFiles/compositeRelatedFilesProvider';
|
||||
import { ICompletionsRelatedFilesProviderService } from './lib/src/prompt/similarFiles/relatedFiles';
|
||||
import { ICompletionsTelemetryUserConfigService, TelemetryUserConfig } from './lib/src/telemetry/userConfig';
|
||||
import { ICompletionsTextDocumentManagerService } from './lib/src/textDocumentManager';
|
||||
import { ICompletionsPromiseQueueService, PromiseQueue } from './lib/src/util/promiseQueue';
|
||||
import { ICompletionsRuntimeModeService, RuntimeMode } from './lib/src/util/runtimeMode';
|
||||
|
||||
/** @public */
|
||||
export function createContext(serviceAccessor: ServicesAccessor, store: DisposableStore): IInstantiationService {
|
||||
const logService = serviceAccessor.get(ILogService);
|
||||
|
||||
const serviceCollection = new ServiceCollection();
|
||||
|
||||
serviceCollection.set(ICompletionsLogTargetService, new class implements ICompletionsLogTargetService {
|
||||
declare _serviceBrand: undefined;
|
||||
logIt(level: LogLevel, category: string, ...extra: unknown[]): void {
|
||||
const msg = formatLogMessage(category, ...extra);
|
||||
switch (level) {
|
||||
case LogLevel.DEBUG: return logService.debug(msg);
|
||||
case LogLevel.INFO: return logService.info(msg);
|
||||
case LogLevel.WARN: return logService.warn(msg);
|
||||
case LogLevel.ERROR: return logService.error(msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
serviceCollection.set(ICompletionsRuntimeModeService, RuntimeMode.fromEnvironment(false));
|
||||
serviceCollection.set(ICompletionsCacheService, new CompletionsCache());
|
||||
serviceCollection.set(ICompletionsConfigProvider, new VSCodeConfigProvider());
|
||||
serviceCollection.set(ICompletionsLastGhostText, new LastGhostText());
|
||||
serviceCollection.set(ICompletionsCurrentGhostText, new CurrentGhostText());
|
||||
serviceCollection.set(ICompletionsSpeculativeRequestCache, new SpeculativeRequestCache());
|
||||
serviceCollection.set(ICompletionsNotificationSender, new SyncDescriptor(ExtensionNotificationSender));
|
||||
serviceCollection.set(ICompletionsEditorAndPluginInfo, new VSCodeEditorInfo());
|
||||
serviceCollection.set(ICompletionsExtensionStatus, new CopilotExtensionStatus());
|
||||
serviceCollection.set(ICompletionsFeaturesService, new SyncDescriptor(Features));
|
||||
serviceCollection.set(ICompletionsObservableWorkspace, new SyncDescriptor(CompletionsObservableWorkspace));
|
||||
serviceCollection.set(ICompletionsStatusReporter, new SyncDescriptor(CopilotStatusBar, ['github.copilot.languageStatus']));
|
||||
serviceCollection.set(ICompletionsCopilotTokenManager, new SyncDescriptor(CopilotTokenManagerImpl, [false]));
|
||||
serviceCollection.set(ICompletionsTextDocumentManagerService, new SyncDescriptor(ExtensionTextDocumentManager));
|
||||
serviceCollection.set(ICompletionsFileReaderService, new SyncDescriptor(FileReader));
|
||||
serviceCollection.set(ICompletionsBlockModeConfig, new SyncDescriptor(ConfigBlockModeConfig));
|
||||
serviceCollection.set(ICompletionsTelemetryService, new SyncDescriptor(CompletionsTelemetryServiceBridge));
|
||||
serviceCollection.set(ICompletionsTelemetryUserConfigService, new SyncDescriptor(TelemetryUserConfig));
|
||||
serviceCollection.set(ICompletionsRecentEditsProviderService, new SyncDescriptor(FullRecentEditsProvider, [undefined]));
|
||||
serviceCollection.set(ICompletionsNotifierService, new SyncDescriptor(CompletionNotifier));
|
||||
serviceCollection.set(ICompletionsOpenAIFetcherService, new SyncDescriptor(LiveOpenAIFetcher));
|
||||
serviceCollection.set(ICompletionsModelManagerService, new SyncDescriptor(AvailableModelsManager, [true]));
|
||||
serviceCollection.set(ICompletionsAsyncManagerService, new SyncDescriptor(AsyncCompletionManager));
|
||||
serviceCollection.set(ICompletionsContextProviderBridgeService, new SyncDescriptor(ContextProviderBridge));
|
||||
serviceCollection.set(ICompletionsUserErrorNotifierService, new SyncDescriptor(UserErrorNotifier));
|
||||
serviceCollection.set(ICompletionsRelatedFilesProviderService, new SyncDescriptor(CompositeRelatedFilesProvider));
|
||||
serviceCollection.set(ICompletionsFileSystemService, extensionFileSystem);
|
||||
serviceCollection.set(ICompletionsContextProviderRegistryService, new SyncDescriptor(CachedContextProviderRegistry, [CoreContextProviderRegistry, contextProviderMatch]));
|
||||
serviceCollection.set(ICompletionsPromiseQueueService, new PromiseQueue());
|
||||
serviceCollection.set(ICompletionsCitationManager, new SyncDescriptor(LoggingCitationManager));
|
||||
serviceCollection.set(ICompletionsContextProviderService, new ContextProviderStatistics());
|
||||
serviceCollection.set(ICompletionsPromptFactoryService, new SyncDescriptor(CompletionsPromptFactory));
|
||||
serviceCollection.set(ICompletionsFetcherService, new SyncDescriptor(CompletionsFetcher));
|
||||
serviceCollection.set(ICompletionsDefaultContextProviders, new DefaultContextProvidersContainer());
|
||||
|
||||
return serviceAccessor.get(IInstantiationService).createChild(serviceCollection, store);
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function setup(serviceAccessor: ServicesAccessor, disposables: DisposableStore) {
|
||||
// This must be registered before activation!
|
||||
// CodeQuote needs to listen for the initial token notification event.
|
||||
disposables.add(serviceAccessor.get(ICompletionsCitationManager).register());
|
||||
|
||||
// Register to listen for changes to the active document to keep track
|
||||
// of last access time
|
||||
disposables.add(registerDocumentTracker(serviceAccessor));
|
||||
|
||||
// Register the context providers enabled by default.
|
||||
const defaultContextProviders = serviceAccessor.get(ICompletionsDefaultContextProviders);
|
||||
defaultContextProviders.add('ms-vscode.cpptools');
|
||||
defaultContextProviders.add('promptfile-ai-context-provider');
|
||||
|
||||
disposables.add(setupCompletionsExperimentationService(serviceAccessor));
|
||||
}
|
||||
|
||||
export function registerUnificationCommands(accessor: ServicesAccessor): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
disposables.add(registerEnablementCommands(accessor));
|
||||
disposables.add(registerStatusBar(accessor));
|
||||
disposables.add(registerDiagnosticCommands(accessor));
|
||||
disposables.add(registerPanelSupport(accessor));
|
||||
disposables.add(registerModelPickerCommands(accessor));
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
function registerEnablementCommands(accessor: ServicesAccessor): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
|
||||
// Enable/Disable/Toggle completions commands [with Command Palette support]
|
||||
function enable(id: string): IDisposable {
|
||||
return registerCommandWrapper(accessor, id, async () => {
|
||||
await instantiationService.invokeFunction(enableCompletions);
|
||||
});
|
||||
}
|
||||
function disable(id: string): IDisposable {
|
||||
return registerCommandWrapper(accessor, id, async () => {
|
||||
await instantiationService.invokeFunction(disableCompletions);
|
||||
});
|
||||
}
|
||||
function toggle(id: string): IDisposable {
|
||||
return registerCommandWrapper(accessor, id, async () => {
|
||||
await instantiationService.invokeFunction(toggleCompletions);
|
||||
});
|
||||
}
|
||||
|
||||
// To support command palette
|
||||
disposables.add(enable(CMDEnableCompletionsChat));
|
||||
disposables.add(disable(CMDDisableCompletionsChat));
|
||||
disposables.add(toggle(CMDToggleCompletionsChat));
|
||||
|
||||
// To support keybindings/main functionality
|
||||
disposables.add(enable(CMDEnableCompletionsClient));
|
||||
disposables.add(disable(CMDDisableCompletionsClient));
|
||||
disposables.add(toggle(CMDToggleCompletionsClient));
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
function registerModelPickerCommands(accessor: ServicesAccessor): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
|
||||
const modelsPicker = instantiationService.createInstance(ModelPickerManager);
|
||||
|
||||
function registerModelPicker(commandId: string): IDisposable {
|
||||
return registerCommandWrapper(accessor, commandId, async () => {
|
||||
await modelsPicker.showModelPicker();
|
||||
});
|
||||
}
|
||||
|
||||
// Model picker command [with Command Palette support]
|
||||
disposables.add(registerModelPicker(CMDOpenModelPickerClient));
|
||||
disposables.add(registerModelPicker(CMDOpenModelPickerChat));
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
function registerStatusBar(accessor: ServicesAccessor): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const copilotTokenManagerService = accessor.get(ICompletionsCopilotTokenManager);
|
||||
const extensionStatusService = accessor.get(ICompletionsExtensionStatus);
|
||||
|
||||
// Status menu command [with Command Palette support]
|
||||
function registerStatusMenu(menuId: string): IDisposable {
|
||||
return registerCommandWrapper(accessor, menuId, async () => {
|
||||
if (extensionStatusService.kind === 'Error') {
|
||||
// Try for a fresh token to clear up the error, but don't block the UI for too long.
|
||||
await Promise.race([
|
||||
copilotTokenManagerService.primeToken(),
|
||||
new Promise(resolve => setTimeout(resolve, 100)),
|
||||
]);
|
||||
}
|
||||
instantiationService.createInstance(CopilotStatusBarPickMenu).showStatusMenu();
|
||||
});
|
||||
}
|
||||
disposables.add(registerStatusMenu(CMDToggleStatusMenuClient));
|
||||
disposables.add(registerStatusMenu(CMDToggleStatusMenuChat));
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
function registerDiagnosticCommands(accessor: ServicesAccessor): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
disposables.add(registerCommandWrapper(accessor, CMDOpenDocumentationClient, () => {
|
||||
return env.openExternal(
|
||||
URI.parse('https://docs.github.com/en/copilot/getting-started-with-github-copilot?tool=vscode')
|
||||
);
|
||||
}));
|
||||
disposables.add(registerCommandWrapper(accessor, CMDOpenLogsClient, () => {
|
||||
outputChannel.show();
|
||||
}));
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
export function registerCommandWrapper(accessor: ServicesAccessor, command: string, fn: (...args: unknown[]) => unknown): IDisposable {
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
return commands.registerCommand(command, async (...args: unknown[]) => {
|
||||
try {
|
||||
await fn(...args);
|
||||
} catch (error) {
|
||||
instantiationService.invokeFunction(exception, error, command);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { commands } from 'vscode';
|
||||
import { CodeReference } from '.';
|
||||
import { IAuthenticationService } from '../../../../../../platform/authentication/common/authentication';
|
||||
import { Disposable } from '../../../../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { onCopilotToken } from '../../../lib/src/auth/copilotTokenNotifier';
|
||||
import { ICompletionsCitationManager, IPDocumentCitation } from '../../../lib/src/citationManager';
|
||||
import { OutputPaneShowCommand } from '../../../lib/src/snippy/constants';
|
||||
import { copilotOutputLogTelemetry } from '../../../lib/src/snippy/telemetryHandlers';
|
||||
import { notify } from './matchNotifier';
|
||||
import { GitHubCopilotLogger } from './outputChannel';
|
||||
|
||||
/**
|
||||
* Citation manager that logs citations to the VS Code log. On the first citation encountered,
|
||||
* the user gets a notification.
|
||||
*/
|
||||
export class LoggingCitationManager extends Disposable implements ICompletionsCitationManager {
|
||||
declare _serviceBrand: undefined;
|
||||
|
||||
private logger?: GitHubCopilotLogger;
|
||||
private readonly codeReference: CodeReference;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IAuthenticationService authenticationService: IAuthenticationService,
|
||||
) {
|
||||
super();
|
||||
this.codeReference = this._register(this.instantiationService.createInstance(CodeReference));
|
||||
const disposable = onCopilotToken(authenticationService, _ => {
|
||||
if (this.logger) {
|
||||
return;
|
||||
}
|
||||
this.logger = instantiationService.createInstance(GitHubCopilotLogger);
|
||||
const initialNotificationCommand = commands.registerCommand(OutputPaneShowCommand, () =>
|
||||
this.logger?.forceShow()
|
||||
);
|
||||
this.codeReference.addDisposable(initialNotificationCommand);
|
||||
});
|
||||
this.codeReference.addDisposable(disposable);
|
||||
}
|
||||
|
||||
register() {
|
||||
return this.codeReference.register();
|
||||
}
|
||||
|
||||
async handleIPCodeCitation(citation: IPDocumentCitation): Promise<void> {
|
||||
if (!this.codeReference.enabled || !this.logger || citation.details.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const start = citation.location?.start;
|
||||
const matchLocation = start ? `[Ln ${start.line + 1}, Col ${start.character + 1}]` : 'Location not available';
|
||||
const shortenedMatchText = `${citation.matchingText
|
||||
?.slice(0, 100)
|
||||
.replace(/[\r\n\t]+|^[ \t]+/gm, ' ')
|
||||
.trim()}...`;
|
||||
|
||||
this.logger.info(citation.inDocumentUri, `Similar code at `, matchLocation, shortenedMatchText);
|
||||
for (const detail of citation.details) {
|
||||
const { license, url } = detail;
|
||||
this.logger.info(`License: ${license.replace('NOASSERTION', 'unknown')}, URL: ${url}`);
|
||||
}
|
||||
copilotOutputLogTelemetry.handleWrite({ instantiationService: this.instantiationService });
|
||||
await this.instantiationService.invokeFunction(notify);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TextEditor, window } from 'vscode';
|
||||
import { Disposable } from '../../../../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { copilotOutputLogTelemetry } from '../../../lib/src/snippy/telemetryHandlers';
|
||||
import { citationsChannelName } from './outputChannel';
|
||||
|
||||
export class CodeRefEngagementTracker extends Disposable {
|
||||
private activeLog = false;
|
||||
|
||||
constructor(@IInstantiationService private instantiationService: IInstantiationService) {
|
||||
super();
|
||||
this._register(window.onDidChangeActiveTextEditor((e) => this.onActiveEditorChange(e)));
|
||||
this._register(window.onDidChangeVisibleTextEditors((e) => this.onVisibleEditorsChange(e)));
|
||||
}
|
||||
|
||||
onActiveEditorChange = (editor: TextEditor | undefined) => {
|
||||
if (this.isOutputLog(editor)) {
|
||||
copilotOutputLogTelemetry.handleFocus({ instantiationService: this.instantiationService });
|
||||
}
|
||||
};
|
||||
|
||||
onVisibleEditorsChange = (currEditors: readonly TextEditor[]) => {
|
||||
const copilotLog = currEditors.find(e => this.isOutputLog(e));
|
||||
|
||||
if (this.activeLog) {
|
||||
if (!copilotLog) {
|
||||
this.activeLog = false;
|
||||
}
|
||||
} else if (copilotLog) {
|
||||
this.activeLog = true;
|
||||
copilotOutputLogTelemetry.handleOpen({ instantiationService: this.instantiationService });
|
||||
}
|
||||
};
|
||||
|
||||
get logVisible() {
|
||||
return this.activeLog;
|
||||
}
|
||||
|
||||
private isOutputLog = (editor: TextEditor | undefined) => {
|
||||
return (
|
||||
editor && editor.document.uri.scheme === 'output' && editor.document.uri.path.includes(citationsChannelName)
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vscode';
|
||||
import { IAuthenticationService } from '../../../../../../platform/authentication/common/authentication';
|
||||
import { IDisposable } from '../../../../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { CopilotToken } from '../../../lib/src/auth/copilotTokenManager';
|
||||
import { onCopilotToken } from '../../../lib/src/auth/copilotTokenNotifier';
|
||||
import { ICompletionsLogTargetService } from '../../../lib/src/logger';
|
||||
import { codeReferenceLogger } from '../../../lib/src/snippy/logger';
|
||||
import { ICompletionsRuntimeModeService } from '../../../lib/src/util/runtimeMode';
|
||||
import { CodeRefEngagementTracker } from './codeReferenceEngagementTracker';
|
||||
|
||||
export class CodeReference implements IDisposable {
|
||||
subscriptions: Disposable | undefined;
|
||||
event?: Disposable;
|
||||
enabled: boolean = false;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ICompletionsRuntimeModeService readonly _runtimeMode: ICompletionsRuntimeModeService,
|
||||
@ICompletionsLogTargetService private readonly _logTarget: ICompletionsLogTargetService,
|
||||
@IAuthenticationService private readonly _authenticationService: IAuthenticationService,
|
||||
) { }
|
||||
|
||||
dispose() {
|
||||
this.subscriptions?.dispose();
|
||||
this.event?.dispose();
|
||||
}
|
||||
|
||||
register() {
|
||||
if (!this._runtimeMode.isRunningInTest()) {
|
||||
this.event = onCopilotToken(this._authenticationService, (t) => this.onCopilotToken(t));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
addDisposable(disposable: Disposable) {
|
||||
if (!this.subscriptions) {
|
||||
this.subscriptions = Disposable.from(disposable);
|
||||
} else {
|
||||
this.subscriptions = Disposable.from(this.subscriptions, disposable);
|
||||
}
|
||||
}
|
||||
|
||||
onCopilotToken = (token: Omit<CopilotToken, 'token'>) => {
|
||||
this.enabled = token.codeQuoteEnabled || false;
|
||||
if (!token.codeQuoteEnabled) {
|
||||
this.subscriptions?.dispose();
|
||||
this.subscriptions = undefined;
|
||||
codeReferenceLogger.debug(this._logTarget, 'Public code references are disabled.');
|
||||
return;
|
||||
}
|
||||
|
||||
codeReferenceLogger.info(this._logTarget, 'Public code references are enabled.');
|
||||
this.addDisposable(this._instantiationService.createInstance(CodeRefEngagementTracker));
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { commands, env, Uri } from 'vscode';
|
||||
import { IVSCodeExtensionContext } from '../../../../../../platform/extContext/common/extensionContext';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { ICompletionsNotificationSender } from '../../../lib/src/notificationSender';
|
||||
import { OutputPaneShowCommand } from '../../../lib/src/snippy/constants';
|
||||
import { matchNotificationTelemetry, TelemetryActor } from '../../../lib/src/snippy/telemetryHandlers';
|
||||
|
||||
const matchCodeMessage =
|
||||
'We found a reference to public code in a recent suggestion. To learn more about public code references, review the [documentation](https://aka.ms/github-copilot-match-public-code).';
|
||||
const MatchAction = 'View reference';
|
||||
const SettingAction = 'Change setting';
|
||||
const CodeReferenceKey = 'codeReference.notified';
|
||||
|
||||
/**
|
||||
* Displays a toast notification when the first code reference is found.
|
||||
* The user will only ever see a single notification of this behavior.
|
||||
* Displays the output panel on notification ack.
|
||||
*/
|
||||
export function notify(accessor: ServicesAccessor) {
|
||||
const extension = accessor.get(IVSCodeExtensionContext);
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const didNotify = extension.globalState.get<boolean>(CodeReferenceKey);
|
||||
|
||||
if (didNotify) {
|
||||
return;
|
||||
}
|
||||
|
||||
const notificationSender = accessor.get(ICompletionsNotificationSender);
|
||||
|
||||
const messageItems = [{ title: MatchAction }, { title: SettingAction }];
|
||||
|
||||
void notificationSender.showWarningMessage(matchCodeMessage, ...messageItems).then(async action => {
|
||||
const event = { instantiationService, actor: 'user' as TelemetryActor };
|
||||
|
||||
switch (action?.title) {
|
||||
case MatchAction: {
|
||||
matchNotificationTelemetry.handleDoAction(event);
|
||||
await commands.executeCommand(OutputPaneShowCommand);
|
||||
break;
|
||||
}
|
||||
case SettingAction: {
|
||||
await env.openExternal(Uri.parse('https://aka.ms/github-copilot-settings'));
|
||||
break;
|
||||
}
|
||||
case undefined: {
|
||||
matchNotificationTelemetry.handleDismiss(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return extension.globalState.update(CodeReferenceKey, true);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { window, type OutputChannel } from 'vscode';
|
||||
import { IAuthenticationService } from '../../../../../../platform/authentication/common/authentication';
|
||||
import { Disposable, IDisposable, MutableDisposable } from '../../../../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { CopilotToken } from '../../../lib/src/auth/copilotTokenManager';
|
||||
import { onCopilotToken } from '../../../lib/src/auth/copilotTokenNotifier';
|
||||
|
||||
interface GitHubLogger extends Disposable {
|
||||
info(...messages: string[]): void;
|
||||
forceShow(): void;
|
||||
}
|
||||
|
||||
export const citationsChannelName = 'GitHub Copilot Log (Code References)';
|
||||
|
||||
// Literally taken from VS Code
|
||||
function getCurrentTimestamp() {
|
||||
const toTwoDigits = (v: number) => (v < 10 ? `0${v}` : v);
|
||||
const toThreeDigits = (v: number) => (v < 10 ? `00${v}` : v < 100 ? `0${v}` : v);
|
||||
const currentTime = new Date();
|
||||
return `${currentTime.getFullYear()}-${toTwoDigits(currentTime.getMonth() + 1)}-${toTwoDigits(
|
||||
currentTime.getDate()
|
||||
)} ${toTwoDigits(currentTime.getHours())}:${toTwoDigits(currentTime.getMinutes())}:${toTwoDigits(
|
||||
currentTime.getSeconds()
|
||||
)}.${toThreeDigits(currentTime.getMilliseconds())}`;
|
||||
}
|
||||
|
||||
class CodeReferenceOutputChannel implements IDisposable {
|
||||
constructor(private output: OutputChannel) { }
|
||||
|
||||
info(...messages: string[]) {
|
||||
this.output.appendLine(`${getCurrentTimestamp()} [info] ${messages.join(' ')}`);
|
||||
}
|
||||
|
||||
show(preserveFocus: boolean) {
|
||||
this.output.show(preserveFocus);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.output.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class GitHubCopilotLogger extends Disposable implements GitHubLogger {
|
||||
|
||||
private output = this._register(new MutableDisposable<CodeReferenceOutputChannel>());
|
||||
|
||||
constructor(
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IAuthenticationService authenticationService: IAuthenticationService
|
||||
) {
|
||||
super();
|
||||
this._register(onCopilotToken(authenticationService, t => this.checkCopilotToken(t)));
|
||||
|
||||
this.createChannel();
|
||||
}
|
||||
|
||||
private checkCopilotToken = (token: Omit<CopilotToken, 'token'>) => {
|
||||
if (token.codeQuoteEnabled) {
|
||||
this.createChannel();
|
||||
} else {
|
||||
this.removeChannel();
|
||||
}
|
||||
};
|
||||
|
||||
private log(type: 'info', ...messages: string[]) {
|
||||
const output = this.createChannel();
|
||||
|
||||
const [base, ...rest] = messages;
|
||||
output[type](base, ...rest);
|
||||
}
|
||||
|
||||
info(...messages: string[]) {
|
||||
this.log('info', ...messages);
|
||||
}
|
||||
|
||||
forceShow() {
|
||||
// Preserve focus in the editor
|
||||
this.getChannel()?.show(true);
|
||||
}
|
||||
|
||||
private createChannel(): CodeReferenceOutputChannel {
|
||||
if (this.output.value) {
|
||||
return this.output.value;
|
||||
}
|
||||
|
||||
this.output.value = new CodeReferenceOutputChannel(window.createOutputChannel(citationsChannelName, 'code-referencing'));
|
||||
return this.output.value;
|
||||
}
|
||||
|
||||
private getChannel(): CodeReferenceOutputChannel | undefined {
|
||||
return this.output.value;
|
||||
}
|
||||
|
||||
private removeChannel() {
|
||||
this.output.value = undefined;
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { TextEditor } from 'vscode';
|
||||
import { DisposableStore } from '../../../../../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { withInMemoryTelemetry } from '../../../../lib/src/test/telemetry';
|
||||
import { createExtensionTestingContext } from '../../test/context';
|
||||
import { CodeRefEngagementTracker } from '../codeReferenceEngagementTracker';
|
||||
import { citationsChannelName } from '../outputChannel';
|
||||
|
||||
suite('CodeReferenceEngagementTracker', function () {
|
||||
let engagementTracker: CodeRefEngagementTracker;
|
||||
let accessor: ServicesAccessor;
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
setup(function () {
|
||||
accessor = createExtensionTestingContext().createTestingAccessor();
|
||||
engagementTracker = disposables.add(accessor.get(IInstantiationService).createInstance(CodeRefEngagementTracker));
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
disposables.clear();
|
||||
});
|
||||
|
||||
test('sends a telemetry event when the output channel is focused', async function () {
|
||||
const telemetry = await withInMemoryTelemetry(accessor, () => {
|
||||
engagementTracker.onActiveEditorChange({
|
||||
document: { uri: { scheme: 'output', path: citationsChannelName } },
|
||||
} as TextEditor);
|
||||
});
|
||||
|
||||
assert.ok(telemetry.reporter.events.length === 1);
|
||||
assert.strictEqual(telemetry.reporter.events[0].name, 'code_referencing.github_copilot_log.focus.count');
|
||||
});
|
||||
|
||||
test('sends a telemetry event when the output channel is focused2', async function () {
|
||||
const telemetry = await withInMemoryTelemetry(accessor, () => {
|
||||
engagementTracker.onActiveEditorChange({
|
||||
document: { uri: { scheme: 'output', path: citationsChannelName } },
|
||||
} as TextEditor);
|
||||
});
|
||||
|
||||
assert.ok(telemetry.reporter.events.length === 1);
|
||||
assert.strictEqual(telemetry.reporter.events[0].name, 'code_referencing.github_copilot_log.focus.count');
|
||||
});
|
||||
|
||||
|
||||
test('sends a telemetry event when the output channel is opened', async function () {
|
||||
const telemetry = await withInMemoryTelemetry(accessor, () => {
|
||||
engagementTracker.onVisibleEditorsChange([
|
||||
{
|
||||
document: { uri: { scheme: 'output', path: citationsChannelName } },
|
||||
},
|
||||
] as TextEditor[]);
|
||||
});
|
||||
|
||||
assert.ok(telemetry.reporter.events.length === 1);
|
||||
assert.strictEqual(telemetry.reporter.events[0].name, 'code_referencing.github_copilot_log.open.count');
|
||||
});
|
||||
|
||||
test('does not send a telemetry event when the output channel is already opened', async function () {
|
||||
const telemetry = await withInMemoryTelemetry(accessor, () => {
|
||||
engagementTracker.onVisibleEditorsChange([
|
||||
{
|
||||
document: { uri: { scheme: 'output', path: citationsChannelName } },
|
||||
},
|
||||
] as TextEditor[]);
|
||||
engagementTracker.onVisibleEditorsChange([
|
||||
{
|
||||
document: { uri: { scheme: 'output', path: citationsChannelName } },
|
||||
},
|
||||
{
|
||||
document: { uri: { scheme: 'file', path: 'some-other-file.js' } },
|
||||
},
|
||||
] as TextEditor[]);
|
||||
});
|
||||
|
||||
assert.ok(telemetry.reporter.events.length === 1);
|
||||
});
|
||||
|
||||
test('tracks when the log closes internally', async function () {
|
||||
const telemetry = await withInMemoryTelemetry(accessor, () => {
|
||||
engagementTracker.onVisibleEditorsChange([
|
||||
{
|
||||
document: { uri: { scheme: 'output', path: citationsChannelName } },
|
||||
},
|
||||
] as TextEditor[]);
|
||||
engagementTracker.onVisibleEditorsChange([
|
||||
{
|
||||
document: { uri: { scheme: 'file', path: 'some-other-file.js' } },
|
||||
},
|
||||
] as TextEditor[]);
|
||||
});
|
||||
|
||||
assert.ok(telemetry.reporter.events.length === 1);
|
||||
assert.ok(engagementTracker.logVisible === false);
|
||||
});
|
||||
});
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import * as Sinon from 'sinon';
|
||||
import { Disposable, ExtensionContext } from 'vscode';
|
||||
import { CodeReference } from '..';
|
||||
import { CopilotToken, createTestExtendedTokenInfo } from '../../../../../../../platform/authentication/common/copilotToken';
|
||||
import { generateUuid } from '../../../../../../../util/vs/base/common/uuid';
|
||||
import { IInstantiationService } from '../../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { ConnectionState } from '../../../../lib/src/snippy/connectionState';
|
||||
import { createExtensionTestingContext } from '../../test/context';
|
||||
|
||||
function testExtensionContext() {
|
||||
return {
|
||||
subscriptions: [],
|
||||
};
|
||||
}
|
||||
|
||||
suite('CodeReference', function () {
|
||||
let extensionContext: ExtensionContext;
|
||||
let instantiationService: IInstantiationService;
|
||||
let sub: Disposable | undefined;
|
||||
|
||||
setup(function () {
|
||||
const accessor = createExtensionTestingContext().createTestingAccessor();
|
||||
instantiationService = accessor.get(IInstantiationService);
|
||||
extensionContext = testExtensionContext() as unknown as ExtensionContext;
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
extensionContext.subscriptions.forEach(sub => {
|
||||
sub.dispose();
|
||||
});
|
||||
sub?.dispose();
|
||||
ConnectionState.setDisabled();
|
||||
});
|
||||
|
||||
suite('subscriptions', function () {
|
||||
test('should be undefined by default', function () {
|
||||
const result = instantiationService.createInstance(CodeReference);
|
||||
sub = result.subscriptions;
|
||||
assert.ok(!sub);
|
||||
});
|
||||
|
||||
test('should be updated correctly when token change events received', function () {
|
||||
const codeQuote = instantiationService.createInstance(CodeReference);
|
||||
const enabledToken = new CopilotToken(createTestExtendedTokenInfo({ token: `test token ${generateUuid()}`, username: 'fixedTokenManager', copilot_plan: 'unknown', code_quote_enabled: true }));
|
||||
const disabledToken = new CopilotToken(createTestExtendedTokenInfo({ token: `test token ${generateUuid()}`, username: 'fixedTokenManager', copilot_plan: 'unknown', code_quote_enabled: false }));
|
||||
|
||||
codeQuote.onCopilotToken(enabledToken);
|
||||
|
||||
assert.ok(codeQuote.enabled);
|
||||
assert.ok(codeQuote.subscriptions);
|
||||
assert.ok(codeQuote.subscriptions instanceof Disposable);
|
||||
|
||||
const subSpy = Sinon.spy(codeQuote.subscriptions, 'dispose');
|
||||
codeQuote.onCopilotToken(disabledToken);
|
||||
|
||||
assert.ok(!codeQuote.enabled);
|
||||
assert.strictEqual(codeQuote.subscriptions, undefined);
|
||||
assert.strictEqual(subSpy.calledOnce, true);
|
||||
|
||||
codeQuote.onCopilotToken(enabledToken);
|
||||
assert.ok(codeQuote.enabled);
|
||||
assert.notStrictEqual(codeQuote.subscriptions, undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import sinon from 'sinon';
|
||||
import { commands, env } from 'vscode';
|
||||
import { IVSCodeExtensionContext } from '../../../../../../../platform/extContext/common/extensionContext';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { ICompletionsNotificationSender } from '../../../../lib/src/notificationSender';
|
||||
import { OutputPaneShowCommand } from '../../../../lib/src/snippy/constants';
|
||||
import { withInMemoryTelemetry } from '../../../../lib/src/test/telemetry';
|
||||
import { TestNotificationSender } from '../../../../lib/src/test/testHelpers';
|
||||
import { createExtensionTestingContext } from '../../test/context';
|
||||
import { notify } from '../matchNotifier';
|
||||
|
||||
suite('.match', function () {
|
||||
let accessor: ServicesAccessor;
|
||||
|
||||
setup(function () {
|
||||
accessor = createExtensionTestingContext().createTestingAccessor();
|
||||
});
|
||||
|
||||
test('populates the globalState object', async function () {
|
||||
const extensionContext = accessor.get(IVSCodeExtensionContext);
|
||||
const globalState = extensionContext.globalState;
|
||||
|
||||
await notify(accessor);
|
||||
|
||||
assert.ok(globalState.get('codeReference.notified'));
|
||||
});
|
||||
|
||||
test('notifies the user', async function () {
|
||||
const testNotificationSender = accessor.get(ICompletionsNotificationSender) as TestNotificationSender;
|
||||
testNotificationSender.performAction('View reference');
|
||||
|
||||
await notify(accessor);
|
||||
|
||||
assert.strictEqual(testNotificationSender.sentMessages.length, 1);
|
||||
});
|
||||
|
||||
test('sends a telemetry event on view reference action', async function () {
|
||||
const testNotificationSender = accessor.get(ICompletionsNotificationSender) as TestNotificationSender;
|
||||
testNotificationSender.performAction('View reference');
|
||||
|
||||
const telemetry = await withInMemoryTelemetry(accessor, async accessor => {
|
||||
await notify(accessor);
|
||||
});
|
||||
|
||||
assert.strictEqual(telemetry.reporter.events.length, 1);
|
||||
assert.strictEqual(telemetry.reporter.events[0].name, 'code_referencing.match_notification.acknowledge.count');
|
||||
});
|
||||
|
||||
test('executes the output panel display command on view reference action', async function () {
|
||||
const spy = sinon.spy(commands, 'executeCommand');
|
||||
const testNotificationSender = accessor.get(ICompletionsNotificationSender) as TestNotificationSender;
|
||||
testNotificationSender.performAction('View reference');
|
||||
|
||||
await notify(accessor);
|
||||
|
||||
await testNotificationSender.waitForMessages();
|
||||
|
||||
assert.ok(spy.calledOnce);
|
||||
assert.ok(spy.calledWith(OutputPaneShowCommand));
|
||||
|
||||
spy.restore();
|
||||
});
|
||||
|
||||
test('opens the settings page on change setting action', async function () {
|
||||
const stub = sinon.stub(env, 'openExternal');
|
||||
const testNotificationSender = accessor.get(ICompletionsNotificationSender) as TestNotificationSender;
|
||||
testNotificationSender.performAction('Change setting');
|
||||
|
||||
await notify(accessor);
|
||||
|
||||
await testNotificationSender.waitForMessages();
|
||||
|
||||
assert.ok(stub.calledOnce);
|
||||
assert.ok(
|
||||
stub.calledWith(
|
||||
sinon.match({
|
||||
scheme: 'https',
|
||||
authority: 'aka.ms',
|
||||
path: '/github-copilot-settings',
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
test('sends a telemetry event on notification dismissal', async function () {
|
||||
const testNotificationSender = accessor.get(ICompletionsNotificationSender) as TestNotificationSender;
|
||||
testNotificationSender.performDismiss();
|
||||
|
||||
const telemetry = await withInMemoryTelemetry(accessor, async accessor => {
|
||||
await notify(accessor);
|
||||
});
|
||||
|
||||
await testNotificationSender.waitForMessages();
|
||||
|
||||
assert.strictEqual(telemetry.reporter.events.length, 1);
|
||||
assert.strictEqual(telemetry.reporter.events[0].name, 'code_referencing.match_notification.ignore.count');
|
||||
});
|
||||
|
||||
test('does not notify if already notified', async function () {
|
||||
const extensionContext = accessor.get(IVSCodeExtensionContext);
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const globalState = extensionContext.globalState;
|
||||
const testNotificationSender = accessor.get(ICompletionsNotificationSender) as TestNotificationSender;
|
||||
testNotificationSender.performAction('View reference');
|
||||
|
||||
await globalState.update('codeReference.notified', true);
|
||||
|
||||
await instantiationService.invokeFunction(notify);
|
||||
|
||||
await testNotificationSender.waitForMessages();
|
||||
|
||||
assert.strictEqual(testNotificationSender.sentMessages.length, 0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { VSCodeWorkspace } from '../../../../inlineEdits/vscode-node/parts/vscodeWorkspace';
|
||||
import { ICompletionsObservableWorkspace } from '../../lib/src/completionsObservableWorkspace';
|
||||
|
||||
export class CompletionsObservableWorkspace extends VSCodeWorkspace implements ICompletionsObservableWorkspace {
|
||||
declare _serviceBrand: undefined;
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { WorkspaceConfiguration } from 'vscode';
|
||||
import * as vscode from 'vscode';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import {
|
||||
ConfigKey,
|
||||
ConfigKeyType,
|
||||
ConfigProvider, getConfigDefaultForKey,
|
||||
getConfigKeyRecursively,
|
||||
getOptionalConfigDefaultForKey,
|
||||
ICompletionsConfigProvider,
|
||||
ICompletionsEditorAndPluginInfo,
|
||||
packageJson
|
||||
} from '../../lib/src/config';
|
||||
import { CopilotConfigPrefix } from '../../lib/src/constants';
|
||||
import { Logger } from '../../lib/src/logger';
|
||||
import { transformEvent } from '../../lib/src/util/event';
|
||||
|
||||
const logger = new Logger('extensionConfig');
|
||||
|
||||
export class VSCodeConfigProvider extends ConfigProvider {
|
||||
private config: WorkspaceConfiguration;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.config = vscode.workspace.getConfiguration(CopilotConfigPrefix);
|
||||
|
||||
// Reload cached config if a workspace config change effects Copilot namespace
|
||||
vscode.workspace.onDidChangeConfiguration(changeEvent => {
|
||||
if (changeEvent.affectsConfiguration(CopilotConfigPrefix)) {
|
||||
this.config = vscode.workspace.getConfiguration(CopilotConfigPrefix);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
override getConfig<T>(key: ConfigKeyType): T {
|
||||
return getConfigKeyRecursively<T>(this.config, key) ?? getConfigDefaultForKey(key);
|
||||
}
|
||||
|
||||
override getOptionalConfig<T>(key: ConfigKeyType): T | undefined {
|
||||
return getConfigKeyRecursively<T>(this.config, key) ?? getOptionalConfigDefaultForKey(key);
|
||||
}
|
||||
|
||||
// Dumps config settings defined in the extension json
|
||||
override dumpForTelemetry(): { [key: string]: string } {
|
||||
return {};
|
||||
}
|
||||
|
||||
override onDidChangeCopilotSettings: ConfigProvider['onDidChangeCopilotSettings'] = transformEvent(
|
||||
vscode.workspace.onDidChangeConfiguration,
|
||||
event => {
|
||||
if (event.affectsConfiguration('github.copilot')) {
|
||||
return this;
|
||||
}
|
||||
if (event.affectsConfiguration('github.copilot-chat')) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// From vscode's src/vs/platform/telemetry/common/telemetryUtils.ts
|
||||
const telemetryAllowedAuthorities = new Set([
|
||||
'ssh-remote',
|
||||
'dev-container',
|
||||
'attached-container',
|
||||
'wsl',
|
||||
'tunnel',
|
||||
'codespaces',
|
||||
'amlext',
|
||||
]);
|
||||
|
||||
export class VSCodeEditorInfo implements ICompletionsEditorAndPluginInfo {
|
||||
declare _serviceBrand: undefined;
|
||||
getEditorInfo() {
|
||||
let devName = vscode.env.uriScheme;
|
||||
if (vscode.version.endsWith('-insider')) {
|
||||
devName = devName.replace(/-insiders$/, '');
|
||||
}
|
||||
const remoteName = vscode.env.remoteName;
|
||||
if (remoteName) {
|
||||
devName += `@${telemetryAllowedAuthorities.has(remoteName) ? remoteName : 'other'}`;
|
||||
}
|
||||
return {
|
||||
name: 'vscode',
|
||||
readableName: vscode.env.appName.replace(/ - Insiders$/, ''),
|
||||
devName: devName,
|
||||
version: vscode.version,
|
||||
root: vscode.env.appRoot,
|
||||
};
|
||||
}
|
||||
getEditorPluginInfo() {
|
||||
return { name: 'copilot-chat', readableName: 'GitHub Copilot for Visual Studio Code', version: packageJson.version };
|
||||
}
|
||||
getRelatedPluginInfo() {
|
||||
// Any additions to this list should also be added as a known filter in
|
||||
// lib/src/experiments/filters.ts
|
||||
return [
|
||||
'ms-vscode.cpptools',
|
||||
'ms-vscode.cmake-tools',
|
||||
'ms-vscode.makefile-tools',
|
||||
'ms-dotnettools.csdevkit',
|
||||
'ms-python.python',
|
||||
'ms-python.vscode-pylance',
|
||||
'vscjava.vscode-java-pack',
|
||||
'vscjava.vscode-java-dependency',
|
||||
'vscode.typescript-language-features',
|
||||
'ms-vscode.vscode-typescript-next',
|
||||
'ms-dotnettools.csharp',
|
||||
'github.copilot-chat',
|
||||
]
|
||||
.map(name => {
|
||||
const extpj = vscode.extensions.getExtension(name)?.packageJSON as unknown;
|
||||
if (extpj && typeof extpj === 'object' && 'version' in extpj && typeof extpj.version === 'string') {
|
||||
return { name, version: extpj.version };
|
||||
}
|
||||
})
|
||||
.filter(plugin => plugin !== undefined);
|
||||
}
|
||||
}
|
||||
|
||||
type EnabledConfigKeyType = { [key: string]: boolean };
|
||||
|
||||
function getEnabledConfigObject(accessor: ServicesAccessor): EnabledConfigKeyType {
|
||||
const configProvider = accessor.get(ICompletionsConfigProvider);
|
||||
return { '*': true, ...(configProvider.getConfig<EnabledConfigKeyType>(ConfigKey.Enable) ?? {}) };
|
||||
}
|
||||
|
||||
function getEnabledConfig(accessor: ServicesAccessor, languageId: string): boolean {
|
||||
const obj = getEnabledConfigObject(accessor);
|
||||
return obj[languageId] ?? obj['*'] ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if automatic completions are enabled for the current document by all Copilot completion settings.
|
||||
* Excludes the `editor.inlineSuggest.enabled` setting.
|
||||
* Return undefined if there is no current document.
|
||||
*/
|
||||
export function isCompletionEnabled(accessor: ServicesAccessor): boolean | undefined {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return undefined;
|
||||
}
|
||||
return isCompletionEnabledForDocument(accessor, editor.document);
|
||||
}
|
||||
|
||||
export function isCompletionEnabledForDocument(accessor: ServicesAccessor, document: vscode.TextDocument): boolean {
|
||||
return getEnabledConfig(accessor, document.languageId);
|
||||
}
|
||||
|
||||
export function isInlineSuggestEnabled(): boolean | undefined {
|
||||
return vscode.workspace.getConfiguration('editor.inlineSuggest').get<boolean>('enabled');
|
||||
}
|
||||
|
||||
type ConfigurationInspect = Exclude<ReturnType<vscode.WorkspaceConfiguration['inspect']>, undefined>;
|
||||
const inspectKinds: [keyof ConfigurationInspect, vscode.ConfigurationTarget, boolean][] = [
|
||||
['workspaceFolderLanguageValue', vscode.ConfigurationTarget.WorkspaceFolder, true],
|
||||
['workspaceFolderValue', vscode.ConfigurationTarget.WorkspaceFolder, false],
|
||||
['workspaceLanguageValue', vscode.ConfigurationTarget.Workspace, true],
|
||||
['workspaceValue', vscode.ConfigurationTarget.Workspace, false],
|
||||
['globalLanguageValue', vscode.ConfigurationTarget.Global, true],
|
||||
['globalValue', vscode.ConfigurationTarget.Global, false],
|
||||
];
|
||||
|
||||
function getConfigurationTargetForEnabledConfig(): vscode.ConfigurationTarget {
|
||||
const inspect = vscode.workspace.getConfiguration(CopilotConfigPrefix).inspect(ConfigKey.Enable);
|
||||
if (inspect?.workspaceFolderValue !== undefined) {
|
||||
return vscode.ConfigurationTarget.WorkspaceFolder;
|
||||
} else if (inspect?.workspaceValue !== undefined) {
|
||||
return vscode.ConfigurationTarget.Workspace;
|
||||
} else {
|
||||
return vscode.ConfigurationTarget.Global;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable completions by every means possible.
|
||||
*/
|
||||
export async function enableCompletions(accessor: ServicesAccessor) {
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const scope = vscode.window.activeTextEditor?.document;
|
||||
// Make sure both of these settings are enabled, because that's a precondition for the user seeing inline completions.
|
||||
for (const [section, option] of [['', 'editor.inlineSuggest.enabled']]) {
|
||||
const config = vscode.workspace.getConfiguration(section, scope);
|
||||
const inspect = config.inspect(option);
|
||||
// Start from the most specific setting and work our way up to the global default.
|
||||
for (const [key, target, overrideInLanguage] of inspectKinds) {
|
||||
// Exit condition: if VS Code thinks the setting is enabled, we're done.
|
||||
// This might be true from the start, or a call to .update() might flip it.
|
||||
if (vscode.workspace.getConfiguration(section, scope).get(option)) {
|
||||
break;
|
||||
}
|
||||
if (inspect?.[key] === false) {
|
||||
await config.update(option, true, target, overrideInLanguage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The rest of this function is the inverse of disableCompletions(), updating the github.copilot.enable setting.
|
||||
const languageId = vscode.window.activeTextEditor?.document.languageId;
|
||||
if (!languageId) { return; }
|
||||
const config = vscode.workspace.getConfiguration(CopilotConfigPrefix);
|
||||
const enabledConfig = { ...instantiationService.invokeFunction(getEnabledConfigObject) };
|
||||
if (!(languageId in enabledConfig)) {
|
||||
enabledConfig['*'] = true;
|
||||
} else {
|
||||
enabledConfig[languageId] = true;
|
||||
}
|
||||
await config.update(ConfigKey.Enable, enabledConfig, getConfigurationTargetForEnabledConfig());
|
||||
if (!instantiationService.invokeFunction(isCompletionEnabled)) {
|
||||
const inspect = vscode.workspace.getConfiguration(CopilotConfigPrefix).inspect(ConfigKey.Enable);
|
||||
const error = new Error(`Failed to enable completions for ${languageId}: ${JSON.stringify(inspect)}`);
|
||||
instantiationService.invokeFunction(acc => logger.exception(acc, error, '.enable'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable completions using the github.copilot.enable setting.
|
||||
*/
|
||||
export async function disableCompletions(accessor: ServicesAccessor) {
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const languageId = vscode.window.activeTextEditor?.document.languageId;
|
||||
if (!languageId) { return; }
|
||||
const config = vscode.workspace.getConfiguration(CopilotConfigPrefix);
|
||||
const enabledConfig = { ...instantiationService.invokeFunction(getEnabledConfigObject) };
|
||||
if (!(languageId in enabledConfig)) {
|
||||
enabledConfig['*'] = false;
|
||||
} else if (enabledConfig[languageId]) {
|
||||
enabledConfig[languageId] = false;
|
||||
}
|
||||
await config.update(ConfigKey.Enable, enabledConfig, getConfigurationTargetForEnabledConfig());
|
||||
if (instantiationService.invokeFunction(isCompletionEnabled)) {
|
||||
const inspect = vscode.workspace.getConfiguration(CopilotConfigPrefix).inspect(ConfigKey.Enable);
|
||||
const error = new Error(`Failed to disable completions for ${languageId}: ${JSON.stringify(inspect)}`);
|
||||
instantiationService.invokeFunction(acc => logger.exception(acc, error, '.disable'));
|
||||
}
|
||||
}
|
||||
|
||||
export async function toggleCompletions(accessor: ServicesAccessor) {
|
||||
if (isCompletionEnabled(accessor) && isInlineSuggestEnabled()) {
|
||||
await disableCompletions(accessor);
|
||||
} else {
|
||||
await enableCompletions(accessor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// Commands ending with "Client" refer to the command ID used in the legacy Copilot extension.
|
||||
// - These IDs should not appear in the package.json file
|
||||
// - These IDs should be registered to support all functionality (except if this command needs to be supported when both extensions are loaded/active).
|
||||
// Commands ending with "Chat" refer to the command ID used in the Copilot Chat extension.
|
||||
// - These IDs should be used in package.json
|
||||
// - These IDs should only be registered if they appear in the package.json (meaning the command palette) or if the command needs to be supported when both extensions are loaded/active.
|
||||
|
||||
export const CMDOpenPanelClient = 'github.copilot.generate';
|
||||
export const CMDOpenPanelChat = 'github.copilot.chat.openSuggestionsPanel'; // "github.copilot.chat.generate" is already being used
|
||||
|
||||
export const CMDAcceptCursorPanelSolutionClient = 'github.copilot.acceptCursorPanelSolution';
|
||||
export const CMDNavigatePreviousPanelSolutionClient = 'github.copilot.previousPanelSolution';
|
||||
export const CMDNavigateNextPanelSolutionClient = 'github.copilot.nextPanelSolution';
|
||||
|
||||
export const CMDToggleStatusMenuClient = 'github.copilot.toggleStatusMenu';
|
||||
export const CMDToggleStatusMenuChat = 'github.copilot.chat.toggleStatusMenu';
|
||||
|
||||
// Needs to be supported in both extensions when they are loaded/active. Requires a different ID.
|
||||
export const CMDSendCompletionsFeedbackChat = 'github.copilot.chat.sendCompletionFeedback';
|
||||
|
||||
export const CMDEnableCompletionsChat = 'github.copilot.chat.completions.enable';
|
||||
export const CMDDisableCompletionsChat = 'github.copilot.chat.completions.disable';
|
||||
export const CMDToggleCompletionsChat = 'github.copilot.chat.completions.toggle';
|
||||
export const CMDEnableCompletionsClient = 'github.copilot.completions.enable';
|
||||
export const CMDDisableCompletionsClient = 'github.copilot.completions.disable';
|
||||
export const CMDToggleCompletionsClient = 'github.copilot.completions.toggle';
|
||||
|
||||
export const CMDOpenLogsClient = 'github.copilot.openLogs';
|
||||
export const CMDOpenDocumentationClient = 'github.copilot.openDocs';
|
||||
|
||||
// Existing chat command reused for diagnostics
|
||||
export const CMDCollectDiagnosticsChat = 'github.copilot.debug.collectDiagnostics';
|
||||
|
||||
// Context variable that enable/disable panel-specific commands
|
||||
export const CopilotPanelVisible = 'github.copilot.panelVisible';
|
||||
export const ComparisonPanelVisible = 'github.copilot.comparisonPanelVisible';
|
||||
|
||||
export const CMDOpenModelPickerClient = 'github.copilot.openModelPicker';
|
||||
export const CMDOpenModelPickerChat = 'github.copilot.chat.openModelPicker';
|
||||
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { languages, workspace } from 'vscode';
|
||||
import { DocumentSelector } from 'vscode-languageserver-protocol';
|
||||
import { IInstantiationService } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { isDocumentValid } from '../../lib/src/util/documentEvaluation';
|
||||
import { DocumentContext } from '../../types/src';
|
||||
|
||||
export async function contextProviderMatch(
|
||||
instantiationService: IInstantiationService,
|
||||
documentSelector: DocumentSelector,
|
||||
documentContext: DocumentContext
|
||||
): Promise<number> {
|
||||
const vscDoc = workspace.textDocuments.find(td => td.uri.toString() === documentContext.uri);
|
||||
if (!vscDoc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const result = await instantiationService.invokeFunction(isDocumentValid, documentContext);
|
||||
if (result.status !== 'valid') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return languages.match(documentSelector, vscDoc);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Command, commands, InlineCompletionItem, Uri } from 'vscode';
|
||||
import { Disposable } from '../../../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { collectCompletionDiagnostics, formatDiagnosticsAsMarkdown } from '../../lib/src/diagnostics';
|
||||
import { telemetry, TelemetryData } from '../../lib/src/telemetry';
|
||||
import { CMDSendCompletionsFeedbackChat } from './constants';
|
||||
|
||||
export const sendCompletionFeedbackCommand: Command = {
|
||||
command: CMDSendCompletionsFeedbackChat,
|
||||
title: 'Send Copilot Completion Feedback',
|
||||
tooltip: 'Send feedback about the last shown Copilot completion item',
|
||||
};
|
||||
|
||||
export class CopilotCompletionFeedbackTracker extends Disposable {
|
||||
private lastShownCopilotCompletionItem: InlineCompletionItem | undefined;
|
||||
|
||||
constructor(@IInstantiationService private readonly instantiationService: IInstantiationService) {
|
||||
super();
|
||||
this._register(commands.registerCommand(sendCompletionFeedbackCommand.command, async () => {
|
||||
const commandArg: unknown = this.lastShownCopilotCompletionItem?.command?.arguments?.[0];
|
||||
let telemetryArg: TelemetryData | undefined;
|
||||
if (commandArg && typeof commandArg === 'object' && 'telemetry' in commandArg) {
|
||||
if (commandArg.telemetry instanceof TelemetryData) {
|
||||
telemetryArg = commandArg.telemetry;
|
||||
}
|
||||
}
|
||||
this.instantiationService.invokeFunction(telemetry, 'ghostText.sentFeedback', telemetryArg);
|
||||
|
||||
await this.instantiationService.invokeFunction(openGitHubIssue, this.lastShownCopilotCompletionItem, telemetryArg);
|
||||
}));
|
||||
}
|
||||
|
||||
trackItem(item: InlineCompletionItem) {
|
||||
this.lastShownCopilotCompletionItem = item;
|
||||
}
|
||||
}
|
||||
|
||||
async function openGitHubIssue(
|
||||
accessor: ServicesAccessor,
|
||||
item: InlineCompletionItem | undefined,
|
||||
telemetry: TelemetryData | undefined
|
||||
) {
|
||||
const body = generateGitHubIssueBody(accessor, item, telemetry);
|
||||
await commands.executeCommand('workbench.action.openIssueReporter', {
|
||||
extensionId: 'github.copilot',
|
||||
uri: Uri.parse('https://github.com/microsoft/vscode'),
|
||||
data: body,
|
||||
});
|
||||
}
|
||||
|
||||
function generateGitHubIssueBody(
|
||||
accessor: ServicesAccessor,
|
||||
item: InlineCompletionItem | undefined,
|
||||
telemetry: TelemetryData | undefined
|
||||
) {
|
||||
const diagnostics = collectCompletionDiagnostics(accessor, telemetry);
|
||||
const formattedDiagnostics = formatDiagnosticsAsMarkdown(diagnostics);
|
||||
if (typeof item?.insertText !== 'string') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `## Copilot Completion Feedback
|
||||
### Describe the issue, feedback, or steps to reproduce it:
|
||||
|
||||
|
||||
### Completion text:
|
||||
\`\`\`
|
||||
${item.insertText}
|
||||
\`\`\`
|
||||
|
||||
<details>
|
||||
<summary>Diagnostics</summary>
|
||||
|
||||
${formattedDiagnostics}
|
||||
|
||||
</details>
|
||||
`;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Range, commands, window, type Disposable } from 'vscode';
|
||||
import { CopilotNamedAnnotationList } from '../../../../../../platform/completions-core/common/openai/copilotAnnotations';
|
||||
import { DisposableStore, IDisposable } from '../../../../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService, type ServicesAccessor } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import * as constants from '../constants';
|
||||
import { registerCommand } from '../telemetry';
|
||||
import { wrapDoc } from '../textDocumentManager';
|
||||
import { CopilotSuggestionsPanelManager } from './copilotSuggestionsPanelManager';
|
||||
|
||||
// Exported for testing
|
||||
export enum PanelNavigationType {
|
||||
Previous = 'previous',
|
||||
Next = 'next',
|
||||
}
|
||||
|
||||
/**
|
||||
* This interface contains data associated to a completion displayed in the panel.
|
||||
*/
|
||||
export interface PanelCompletion {
|
||||
insertText: string;
|
||||
range: Range;
|
||||
copilotAnnotations?: CopilotNamedAnnotationList;
|
||||
postInsertionCallback: () => PromiseLike<void> | void;
|
||||
}
|
||||
|
||||
export function registerPanelSupport(accessor: ServicesAccessor): Disposable {
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const suggestionsPanelManager = instantiationService.createInstance(CopilotSuggestionsPanelManager);
|
||||
|
||||
const disposableStore = new DisposableStore();
|
||||
|
||||
function registerOpenPanelCommand(id: string): IDisposable {
|
||||
return registerCommand(accessor, id, async () => {
|
||||
// hide ghost text while opening the generation ui
|
||||
await commands.executeCommand('editor.action.inlineSuggest.hide');
|
||||
await instantiationService.invokeFunction(commandOpenPanel, suggestionsPanelManager);
|
||||
});
|
||||
}
|
||||
|
||||
// Register both commands to also support command palette
|
||||
disposableStore.add(registerOpenPanelCommand(constants.CMDOpenPanelChat));
|
||||
disposableStore.add(registerOpenPanelCommand(constants.CMDOpenPanelClient));
|
||||
|
||||
// No command palette support needed for these commands
|
||||
disposableStore.add(suggestionsPanelManager.registerCommands());
|
||||
|
||||
return disposableStore;
|
||||
}
|
||||
|
||||
function commandOpenPanel(accessor: ServicesAccessor, suggestionsPanelManager: CopilotSuggestionsPanelManager) {
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) { return; }
|
||||
const wrapped = wrapDoc(editor.document);
|
||||
if (!wrapped) { return; }
|
||||
|
||||
const { line, character } = editor.selection.active;
|
||||
|
||||
suggestionsPanelManager.renderPanel(editor.document, { line, character }, wrapped);
|
||||
return commands.executeCommand('setContext', constants.CopilotPanelVisible, true);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IInstantiationService } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { IPosition, ITextDocument } from '../../../lib/src/textDocument';
|
||||
import { solutionCountTarget } from '../lib/copilotPanel/common';
|
||||
import { runSolutions } from '../lib/copilotPanel/panel';
|
||||
import { UnformattedSolution } from '../lib/panelShared/panelTypes';
|
||||
import { BaseListDocument } from '../panelShared/baseListDocument';
|
||||
import { BasePanelCompletion, ISuggestionsPanel } from '../panelShared/basePanelTypes';
|
||||
import { PanelCompletion } from './common';
|
||||
|
||||
/**
|
||||
* Class representing a Open Copilot list using a ITextDocument as a way of displaying results.
|
||||
* Currently only used in the VSCode extension.
|
||||
*/
|
||||
export class CopilotListDocument extends BaseListDocument<PanelCompletion> {
|
||||
constructor(
|
||||
textDocument: ITextDocument,
|
||||
position: IPosition,
|
||||
panel: ISuggestionsPanel,
|
||||
countTarget = solutionCountTarget,
|
||||
@IInstantiationService instantiationService: IInstantiationService
|
||||
) {
|
||||
super(textDocument, position, panel, countTarget, instantiationService);
|
||||
}
|
||||
|
||||
protected createPanelCompletion(
|
||||
unformatted: UnformattedSolution,
|
||||
baseCompletion: BasePanelCompletion
|
||||
): PanelCompletion {
|
||||
return {
|
||||
insertText: baseCompletion.insertText,
|
||||
range: baseCompletion.range,
|
||||
copilotAnnotations: baseCompletion.copilotAnnotations,
|
||||
postInsertionCallback: baseCompletion.postInsertionCallback,
|
||||
};
|
||||
}
|
||||
|
||||
protected shouldAddSolution(newItem: PanelCompletion): boolean {
|
||||
return !this.findDuplicateSolution(newItem);
|
||||
}
|
||||
|
||||
protected runSolutionsImpl(): Promise<void> {
|
||||
return this.instantiationService.invokeFunction(runSolutions, this, this);
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TextDocument, WebviewPanel } from 'vscode';
|
||||
import { IVSCodeExtensionContext } from '../../../../../../platform/extContext/common/extensionContext';
|
||||
import { BaseSuggestionsPanel, SolutionContent, WebviewMessage } from '../panelShared/baseSuggestionsPanel';
|
||||
import { PanelCompletion } from './common';
|
||||
import { CopilotSuggestionsPanelManager } from './copilotSuggestionsPanelManager';
|
||||
import { copilotPanelConfig } from './panelConfig';
|
||||
|
||||
export interface CopilotSolutionsMessage {
|
||||
command: 'solutionsUpdated';
|
||||
solutions: SolutionContent[];
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
export class CopilotSuggestionsPanel extends BaseSuggestionsPanel<PanelCompletion> {
|
||||
constructor(
|
||||
webviewPanel: WebviewPanel,
|
||||
document: TextDocument,
|
||||
suggestionsPanelManager: CopilotSuggestionsPanelManager,
|
||||
@IVSCodeExtensionContext contextService: IVSCodeExtensionContext,
|
||||
) {
|
||||
super(webviewPanel, document, suggestionsPanelManager, copilotPanelConfig, contextService);
|
||||
}
|
||||
|
||||
protected renderSolutionContent(item: PanelCompletion, baseContent: SolutionContent): SolutionContent {
|
||||
// Copilot panel just returns the base content without modifications
|
||||
return baseContent;
|
||||
}
|
||||
|
||||
protected createSolutionsMessage(content: SolutionContent[], percentage: number): CopilotSolutionsMessage {
|
||||
return {
|
||||
command: 'solutionsUpdated',
|
||||
solutions: content,
|
||||
percentage,
|
||||
};
|
||||
}
|
||||
|
||||
protected override async handleCustomMessage(message: WebviewMessage): Promise<boolean> {
|
||||
switch (message.command) {
|
||||
case 'acceptSolution': {
|
||||
const solution = this.items()[message.solutionIndex];
|
||||
await this.acceptSolution(solution, true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
default:
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TextDocument, WebviewPanel } from 'vscode';
|
||||
import { IVSCodeExtensionContext } from '../../../../../../platform/extContext/common/extensionContext';
|
||||
import { IInstantiationService } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { IPosition, ITextDocument } from '../../../lib/src/textDocument';
|
||||
import { solutionCountTarget } from '../lib/copilotPanel/common';
|
||||
import { BaseSuggestionsPanelManager, ListDocumentInterface } from '../panelShared/baseSuggestionsPanelManager';
|
||||
import { PanelCompletion } from './common';
|
||||
import { CopilotListDocument } from './copilotListDocument';
|
||||
import { CopilotSuggestionsPanel } from './copilotSuggestionsPanel';
|
||||
import { copilotPanelConfig } from './panelConfig';
|
||||
|
||||
export class CopilotSuggestionsPanelManager extends BaseSuggestionsPanelManager<PanelCompletion> {
|
||||
constructor(
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IVSCodeExtensionContext extensionContext: IVSCodeExtensionContext,
|
||||
) {
|
||||
super(copilotPanelConfig, instantiationService, extensionContext);
|
||||
}
|
||||
|
||||
protected createListDocument(
|
||||
wrapped: ITextDocument,
|
||||
position: IPosition,
|
||||
panel: CopilotSuggestionsPanel
|
||||
): ListDocumentInterface {
|
||||
return this._instantiationService.createInstance(CopilotListDocument, wrapped, position, panel, solutionCountTarget);
|
||||
}
|
||||
|
||||
protected createSuggestionsPanel(
|
||||
panel: WebviewPanel,
|
||||
document: TextDocument,
|
||||
manager: this
|
||||
): CopilotSuggestionsPanel {
|
||||
return this._instantiationService.createInstance(CopilotSuggestionsPanel, panel, document, manager);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as constants from '../constants';
|
||||
import { CopilotPanelVisible } from '../constants';
|
||||
import { PanelConfig } from '../panelShared/basePanelTypes';
|
||||
|
||||
// Configuration for the GitHub Copilot Suggestions Panel
|
||||
export const copilotPanelConfig: PanelConfig = {
|
||||
panelTitle: 'GitHub Copilot Suggestions',
|
||||
webviewId: 'GitHub Copilot Suggestions',
|
||||
webviewScriptName: 'suggestionsPanelWebview.js',
|
||||
contextVariable: CopilotPanelVisible,
|
||||
commands: {
|
||||
accept: constants.CMDAcceptCursorPanelSolutionClient,
|
||||
navigatePrevious: constants.CMDNavigatePreviousPanelSolutionClient,
|
||||
navigateNext: constants.CMDNavigateNextPanelSolutionClient,
|
||||
},
|
||||
renderingMode: 'streaming',
|
||||
shuffleSolutions: false,
|
||||
};
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { provideVSCodeDesignSystem, vsCodeButton } from '@vscode/webview-ui-toolkit';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
const solutionsContainer = document.getElementById('solutionsContainer');
|
||||
const vscode = acquireVsCodeApi();
|
||||
let currentFocusIndex: number = 0;
|
||||
let solutionEventHandlersInitialized = false;
|
||||
|
||||
provideVSCodeDesignSystem().register(vsCodeButton());
|
||||
|
||||
type Message = {
|
||||
command: string;
|
||||
solutions: {
|
||||
htmlSnippet: string;
|
||||
citation?: {
|
||||
message: string;
|
||||
url: string;
|
||||
};
|
||||
}[];
|
||||
percentage: number;
|
||||
};
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
// Notify the extension that the webview is ready
|
||||
vscode.postMessage({ command: 'webviewReady' });
|
||||
initializeSolutionEventHandlers();
|
||||
});
|
||||
|
||||
window.addEventListener('message', (event) => {
|
||||
const message = event.data as Message; // The JSON data our extension sent
|
||||
|
||||
switch (message.command) {
|
||||
case 'solutionsUpdated':
|
||||
handleSolutionUpdate(message);
|
||||
break;
|
||||
case 'navigatePreviousSolution':
|
||||
navigatePreviousSolution();
|
||||
break;
|
||||
case 'navigateNextSolution':
|
||||
navigateNextSolution();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
function handleSolutionUpdate(message: Message) {
|
||||
updateLoadingContainer(message);
|
||||
|
||||
if (solutionsContainer) {
|
||||
solutionsContainer.innerHTML = message.solutions
|
||||
.map((solution, index) => {
|
||||
const renderedCitation = solution.citation
|
||||
? `<p>
|
||||
<span style="vertical-align: text-bottom" aria-hidden="true">Warning</span>
|
||||
${DOMPurify.sanitize(solution.citation.message)}
|
||||
<a href="${DOMPurify.sanitize(solution.citation.url)}" target="_blank">Inspect source code</a>
|
||||
</p>`
|
||||
: '';
|
||||
const sanitizedSnippet = DOMPurify.sanitize(solution.htmlSnippet);
|
||||
|
||||
return `<h3 class='solutionHeading' id="solution-${index + 1}-heading">Suggestion ${index + 1}</h3>
|
||||
<div class='snippetContainer' aria-labelledby="solution-${index + 1}-heading" role="group" data-solution-index="${index}">${sanitizedSnippet
|
||||
}</div>
|
||||
${DOMPurify.sanitize(renderedCitation)}
|
||||
<vscode-button role="button" class="acceptButton" id="acceptButton${index}" appearance="secondary" data-solution-index="${index}">Accept suggestion ${index + 1
|
||||
}</vscode-button>`;
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
}
|
||||
|
||||
function navigatePreviousSolution() {
|
||||
const snippets = document.querySelectorAll<HTMLElement>('.snippetContainer pre');
|
||||
const prevIndex = currentFocusIndex - 1;
|
||||
|
||||
snippets[prevIndex]?.focus();
|
||||
}
|
||||
|
||||
function navigateNextSolution() {
|
||||
const snippets = document.querySelectorAll<HTMLElement>('.snippetContainer pre');
|
||||
const nextIndex = (currentFocusIndex ?? -1) + 1;
|
||||
|
||||
if (snippets[nextIndex]) {
|
||||
snippets[nextIndex].focus();
|
||||
} else if (snippets[0]) {
|
||||
snippets[0].focus();
|
||||
}
|
||||
}
|
||||
|
||||
function updateLoadingContainer(message: Message) {
|
||||
const progressBar = document.getElementById('progress-bar') as HTMLProgressElement;
|
||||
const loadingContainer = document.getElementById('loadingContainer') as HTMLDivElement;
|
||||
if (!progressBar || !loadingContainer) {
|
||||
return;
|
||||
}
|
||||
if (message.percentage >= 100) {
|
||||
loadingContainer.innerHTML = `${message.solutions.length} Suggestions`;
|
||||
} else {
|
||||
const loadingLabelElement = loadingContainer.querySelector('label') as HTMLLabelElement;
|
||||
if (loadingLabelElement.textContent !== 'Loading suggestions:\u00A0') {
|
||||
loadingLabelElement.textContent = 'Loading suggestions:\u00A0';
|
||||
}
|
||||
progressBar.value = message.percentage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function initializeSolutionEventHandlers(): void {
|
||||
if (solutionEventHandlersInitialized || solutionsContainer === null) {
|
||||
return;
|
||||
}
|
||||
solutionsContainer.addEventListener('focusin', (event) => {
|
||||
const target = event.target as HTMLElement | null;
|
||||
const index = extractSolutionIndex(target);
|
||||
if (index === undefined) {
|
||||
return;
|
||||
}
|
||||
handleFocus(index);
|
||||
});
|
||||
solutionsContainer.addEventListener('click', (event) => {
|
||||
const target = event.target as HTMLElement | null;
|
||||
const button = target?.closest('vscode-button[data-solution-index]');
|
||||
if (!(button instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
const index = extractSolutionIndex(button);
|
||||
if (index === undefined) {
|
||||
return;
|
||||
}
|
||||
handleClick(index);
|
||||
});
|
||||
solutionEventHandlersInitialized = true;
|
||||
}
|
||||
|
||||
function extractSolutionIndex(element: HTMLElement | null): number | undefined {
|
||||
const solutionElement = element?.closest('[data-solution-index]');
|
||||
if (!(solutionElement instanceof HTMLElement)) {
|
||||
return undefined;
|
||||
}
|
||||
const attributeValue = solutionElement.getAttribute('data-solution-index');
|
||||
if (attributeValue === null) {
|
||||
return undefined;
|
||||
}
|
||||
const index = Number.parseInt(attributeValue, 10);
|
||||
return Number.isNaN(index) ? undefined : index;
|
||||
}
|
||||
|
||||
function handleFocus(index: number) {
|
||||
currentFocusIndex = index;
|
||||
vscode.postMessage({
|
||||
command: 'focusSolution',
|
||||
solutionIndex: index,
|
||||
});
|
||||
}
|
||||
|
||||
function handleClick(index: number) {
|
||||
vscode.postMessage({
|
||||
command: 'acceptSolution',
|
||||
solutionIndex: index,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2022",
|
||||
"skipLibCheck": true, // https://github.com/DataDog/datadog-ci/issues/1059
|
||||
"sourceMap": true,
|
||||
"rootDir": ".",
|
||||
"lib": ["ES2021", "dom"],
|
||||
// Reset values set in the parent tsconfig
|
||||
"strict": true, /* enable all strict type-checking options */
|
||||
/* Additional Checks */
|
||||
"noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
"noImplicitOverride": true, /* Force use of `override` keyword. */
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"useDefineForClassFields": false,
|
||||
"resolveJsonModule": true,
|
||||
"experimentalDecorators": true,
|
||||
"isolatedModules": false,
|
||||
},
|
||||
"exclude": [],
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createServiceIdentifier } from '../../../../../util/common/services';
|
||||
import { Command, StatusKind } from '../../types/src';
|
||||
|
||||
export const ICompletionsExtensionStatus = createServiceIdentifier<ICompletionsExtensionStatus>('ICompletionsExtensionStatus');
|
||||
export interface ICompletionsExtensionStatus {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
kind: StatusKind;
|
||||
message?: string;
|
||||
busy: boolean;
|
||||
command?: Command;
|
||||
}
|
||||
|
||||
export class CopilotExtensionStatus implements ICompletionsExtensionStatus {
|
||||
declare _serviceBrand: undefined;
|
||||
constructor(
|
||||
public kind: StatusKind = 'Normal',
|
||||
public message?: string,
|
||||
public busy = false,
|
||||
public command?: Command
|
||||
) { }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { FileType, Uri, workspace } from 'vscode';
|
||||
import { FileIdentifier, FileStat, ICompletionsFileSystemService } from '../../lib/src/fileSystem';
|
||||
|
||||
class ExtensionFileSystem implements ICompletionsFileSystemService {
|
||||
declare _serviceBrand: undefined;
|
||||
|
||||
async readFileString(uri: FileIdentifier): Promise<string> {
|
||||
if (typeof uri !== 'string') {
|
||||
uri = uri.uri;
|
||||
}
|
||||
return new TextDecoder().decode(await workspace.fs.readFile(Uri.parse(uri, true)));
|
||||
}
|
||||
async stat(uri: FileIdentifier): Promise<FileStat> {
|
||||
if (typeof uri !== 'string') {
|
||||
uri = uri.uri;
|
||||
}
|
||||
return await workspace.fs.stat(Uri.parse(uri, true));
|
||||
}
|
||||
async readDirectory(uri: FileIdentifier): Promise<[string, FileType][]> {
|
||||
if (typeof uri !== 'string') {
|
||||
uri = uri.uri;
|
||||
}
|
||||
return await workspace.fs.readDirectory(Uri.parse(uri, true));
|
||||
}
|
||||
}
|
||||
|
||||
export const extensionFileSystem = new ExtensionFileSystem();
|
||||
@@ -0,0 +1,127 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import {
|
||||
CancellationToken,
|
||||
InlineCompletionContext,
|
||||
InlineCompletionEndOfLifeReason,
|
||||
InlineCompletionEndOfLifeReasonKind,
|
||||
InlineCompletionItem,
|
||||
InlineCompletionList,
|
||||
InlineCompletionTriggerKind,
|
||||
PartialAcceptInfo,
|
||||
Position,
|
||||
Range,
|
||||
TextDocument,
|
||||
window
|
||||
} from 'vscode';
|
||||
import { ISurveyService } from '../../../../../../platform/survey/common/surveyService';
|
||||
import { assertNever } from '../../../../../../util/vs/base/common/assert';
|
||||
import { IInstantiationService } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { createCorrelationId } from '../../../../../inlineEdits/common/correlationId';
|
||||
import { CopilotCompletion } from '../../../lib/src/ghostText/copilotCompletion';
|
||||
import { handleGhostTextPostInsert, handleGhostTextShown, handlePartialGhostTextPostInsert } from '../../../lib/src/ghostText/last';
|
||||
import { GhostText } from '../../../lib/src/inlineCompletion';
|
||||
import { telemetry } from '../../../lib/src/telemetry';
|
||||
import { wrapDoc } from '../textDocumentManager';
|
||||
|
||||
export interface GhostTextCompletionList extends InlineCompletionList {
|
||||
items: GhostTextCompletionItem[];
|
||||
}
|
||||
|
||||
export interface GhostTextCompletionItem extends InlineCompletionItem {
|
||||
copilotCompletion: CopilotCompletion;
|
||||
}
|
||||
|
||||
export class GhostTextProvider {
|
||||
|
||||
private readonly ghostText: GhostText;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ISurveyService private readonly _surveyService: ISurveyService,
|
||||
) {
|
||||
this.ghostText = this.instantiationService.createInstance(GhostText);
|
||||
}
|
||||
|
||||
async provideInlineCompletionItems(
|
||||
vscodeDoc: TextDocument,
|
||||
position: Position,
|
||||
context: InlineCompletionContext,
|
||||
token: CancellationToken
|
||||
): Promise<GhostTextCompletionList | undefined> {
|
||||
const textDocument = wrapDoc(vscodeDoc);
|
||||
if (!textDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Opportunity ID is a unique ID generated by the client relating to a single "opportunity"
|
||||
// to provide some kind of suggestion to the user. Multiple requests might be made for a single
|
||||
// opportunity, for example requesting a completion as well as an edit suggestion. The single ID
|
||||
// allows us to correlate the different requests.
|
||||
const opportunityId = context.requestUuid;
|
||||
|
||||
const formattingOptions = window.visibleTextEditors.find(e => e.document.uri === vscodeDoc.uri)?.options;
|
||||
|
||||
const rawCompletions = await this.ghostText.getInlineCompletions(textDocument, position, token, {
|
||||
isCycling: context.triggerKind === InlineCompletionTriggerKind.Invoke,
|
||||
selectedCompletionInfo: context.selectedCompletionInfo,
|
||||
formattingOptions,
|
||||
opportunityId,
|
||||
});
|
||||
|
||||
if (!rawCompletions) {
|
||||
return;
|
||||
}
|
||||
|
||||
const items: GhostTextCompletionItem[] = rawCompletions.map(completion => {
|
||||
const { start, end } = completion.range;
|
||||
const newRange = new Range(start.line, start.character, end.line, end.character);
|
||||
return {
|
||||
insertText: completion.insertText,
|
||||
range: newRange,
|
||||
copilotCompletion: completion,
|
||||
correlationId: createCorrelationId('completions', {}),
|
||||
} satisfies GhostTextCompletionItem;
|
||||
});
|
||||
|
||||
return { items };
|
||||
}
|
||||
|
||||
handleDidShowCompletionItem(item: GhostTextCompletionItem) {
|
||||
this.instantiationService.invokeFunction(handleGhostTextShown, item.copilotCompletion);
|
||||
}
|
||||
|
||||
handleDidPartiallyAcceptCompletionItem(item: GhostTextCompletionItem, info: number | PartialAcceptInfo) {
|
||||
if (typeof info === 'number') {
|
||||
return; // deprecated API
|
||||
}
|
||||
this.instantiationService.invokeFunction(handlePartialGhostTextPostInsert, item.copilotCompletion, info.acceptedLength);
|
||||
}
|
||||
|
||||
async handleEndOfLifetime(completionItem: GhostTextCompletionItem, reason: InlineCompletionEndOfLifeReason) {
|
||||
const copilotCompletion = completionItem.copilotCompletion;
|
||||
switch (reason.kind) {
|
||||
case InlineCompletionEndOfLifeReasonKind.Accepted: {
|
||||
this.instantiationService.invokeFunction(handleGhostTextPostInsert, copilotCompletion);
|
||||
this._surveyService.signalUsage('completions').catch(() => {
|
||||
// Ignore errors from the survey command execution
|
||||
});
|
||||
return;
|
||||
}
|
||||
case InlineCompletionEndOfLifeReasonKind.Rejected: {
|
||||
this.instantiationService.invokeFunction(telemetry, 'ghostText.dismissed', copilotCompletion.telemetry);
|
||||
return;
|
||||
}
|
||||
case InlineCompletionEndOfLifeReasonKind.Ignored: {
|
||||
// @ulugbekna: no-op ?
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
assertNever(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export enum Icon {
|
||||
Logo = '$(copilot)',
|
||||
Warning = '$(copilot-warning)',
|
||||
NotConnected = '$(copilot-not-connected)',
|
||||
Blocked = '$(copilot-blocked)',
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export const solutionCountTarget = 10;
|
||||
@@ -0,0 +1,137 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IInstantiationService, type ServicesAccessor } from '../../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { asyncIterableMapFilter } from '../../../../lib/src/helpers/iterableHelpers';
|
||||
import { ICompletionsLogTargetService, Logger } from '../../../../lib/src/logger';
|
||||
import { CopilotUiKind, ICompletionsOpenAIFetcherService } from '../../../../lib/src/openai/fetch';
|
||||
import { APIChoice } from '../../../../lib/src/openai/openai';
|
||||
import { ICompletionsStatusReporter } from '../../../../lib/src/progress';
|
||||
import { getNodeStartUtil } from '../../../../lib/src/prompt/parseBlock';
|
||||
import { trimLastLine } from '../../../../lib/src/prompt/prompt';
|
||||
import { postProcessChoiceInContext } from '../../../../lib/src/suggestions/suggestions';
|
||||
import { LocationFactory } from '../../../../lib/src/textDocument';
|
||||
import {
|
||||
generateSolutionsStream,
|
||||
reportSolutions,
|
||||
setupCompletionParams,
|
||||
setupPromptAndTelemetry,
|
||||
SolutionManager,
|
||||
trimChoices,
|
||||
} from '../panelShared/common';
|
||||
import { ISolutionHandler, SolutionsStream, UnformattedSolution } from '../panelShared/panelTypes';
|
||||
|
||||
const solutionsLogger = new Logger('solutions');
|
||||
|
||||
/**
|
||||
* Given an `ISolutionManager` with the context of a specific "Open Copilot" request,
|
||||
* initiate the generation of a stream of solutions for that request.
|
||||
*/
|
||||
export async function launchSolutions(accessor: ServicesAccessor, solutionManager: SolutionManager): Promise<SolutionsStream> {
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const fetcherService = accessor.get(ICompletionsOpenAIFetcherService);
|
||||
const logTarget = accessor.get(ICompletionsLogTargetService);
|
||||
const position = solutionManager.targetPosition;
|
||||
const document = solutionManager.textDocument;
|
||||
|
||||
// Setup prompt and telemetry using shared function
|
||||
const promptSetup = await setupPromptAndTelemetry(accessor, solutionManager, 'open copilot', solutionsLogger);
|
||||
if ('status' in promptSetup) {
|
||||
// This is a SolutionsStream indicating an error occurred
|
||||
return promptSetup;
|
||||
}
|
||||
|
||||
const { prompt, trailingWs, telemetryData, repoInfo, ourRequestId } = promptSetup;
|
||||
|
||||
// Setup completion parameters using shared function
|
||||
const { extra, postOptions, finishedCb, engineInfo } = instantiationService.invokeFunction(setupCompletionParams,
|
||||
document,
|
||||
position,
|
||||
prompt,
|
||||
solutionManager,
|
||||
telemetryData
|
||||
);
|
||||
|
||||
const cancellationToken = solutionManager.cancellationToken;
|
||||
|
||||
const completionParams = {
|
||||
prompt,
|
||||
languageId: document.detectedLanguageId,
|
||||
repoInfo,
|
||||
ourRequestId,
|
||||
engineModelId: engineInfo.modelId,
|
||||
count: solutionManager.solutionCountTarget,
|
||||
uiKind: CopilotUiKind.Panel,
|
||||
postOptions,
|
||||
headers: engineInfo.headers,
|
||||
extra,
|
||||
};
|
||||
|
||||
const res = await fetcherService.fetchAndStreamCompletions(completionParams, telemetryData.extendedBy(), finishedCb, cancellationToken);
|
||||
|
||||
if (res.type === 'failed' || res.type === 'canceled') {
|
||||
return { status: 'FinishedWithError', error: `${res.type}: ${res.reason}` };
|
||||
}
|
||||
|
||||
let choices: AsyncIterable<APIChoice> = res.choices;
|
||||
choices = trimChoices(choices);
|
||||
choices = asyncIterableMapFilter(choices, choice => instantiationService.invokeFunction(postProcessChoiceInContext, document, position, choice, false, solutionsLogger));
|
||||
|
||||
const solutions = asyncIterableMapFilter(choices, async (apiChoice: APIChoice) => {
|
||||
let display = apiChoice.completionText;
|
||||
solutionsLogger.info(logTarget, `Open Copilot completion: [${apiChoice.completionText}]`);
|
||||
|
||||
// For completions that can happen in any location in the middle of the code we try to find the existing code
|
||||
// that should be displayed in the OpenCopilot panel so the code is nicely formatted/highlighted.
|
||||
// This is not needed for implement unknown function quick fix, as it will be
|
||||
// always "complete" standalone function in the location suggested by TS' extension.
|
||||
const displayStartPos =
|
||||
(await getNodeStartUtil(document, position, apiChoice.completionText)) ??
|
||||
LocationFactory.position(position.line, 0);
|
||||
const [displayBefore] = trimLastLine(document.getText(LocationFactory.range(displayStartPos, position)));
|
||||
|
||||
display = displayBefore + display;
|
||||
let completionText = apiChoice.completionText;
|
||||
|
||||
if (trailingWs.length > 0 && completionText.startsWith(trailingWs)) {
|
||||
completionText = completionText.substring(trailingWs.length);
|
||||
}
|
||||
|
||||
const meanLogProb = apiChoice.meanLogProb;
|
||||
const meanProb: number = meanLogProb !== undefined ? Math.exp(meanLogProb) : 0;
|
||||
|
||||
const solutionTelemetryData = telemetryData.extendedBy({
|
||||
choiceIndex: apiChoice.choiceIndex.toString(),
|
||||
});
|
||||
const solution: UnformattedSolution = {
|
||||
completionText,
|
||||
insertText: display,
|
||||
range: LocationFactory.range(displayStartPos, position),
|
||||
meanProb: meanProb,
|
||||
meanLogProb: meanLogProb || 0,
|
||||
requestId: apiChoice.requestId,
|
||||
choiceIndex: apiChoice.choiceIndex,
|
||||
telemetryData: solutionTelemetryData,
|
||||
copilotAnnotations: apiChoice.copilotAnnotations,
|
||||
};
|
||||
return solution;
|
||||
});
|
||||
// deliberately not awaiting so that we can return quickly
|
||||
const solutionsStream = generateSolutionsStream(cancellationToken, solutions[Symbol.asyncIterator]());
|
||||
return solutionsStream;
|
||||
}
|
||||
|
||||
export async function runSolutions(
|
||||
accessor: ServicesAccessor,
|
||||
solutionManager: SolutionManager,
|
||||
solutionHandler: ISolutionHandler
|
||||
): Promise<void> {
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const statusReporter = accessor.get(ICompletionsStatusReporter);
|
||||
return statusReporter.withProgress(async () => {
|
||||
const nextSolution = instantiationService.invokeFunction(launchSolutions, solutionManager);
|
||||
return await reportSolutions(nextSolution, solutionHandler);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken } from 'vscode';
|
||||
import { generateUuid } from '../../../../../../../util/vs/base/common/uuid';
|
||||
import { IInstantiationService, type ServicesAccessor } from '../../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { createCompletionState } from '../../../../lib/src/completionState';
|
||||
import { BlockMode } from '../../../../lib/src/config';
|
||||
import { ICompletionsFeaturesService } from '../../../../lib/src/experiments/featuresService';
|
||||
import { ICompletionsBlockModeConfig } from '../../../../lib/src/ghostText/configBlockMode';
|
||||
import { ICompletionsLogTargetService, type Logger } from '../../../../lib/src/logger';
|
||||
import { getEngineRequestInfo } from '../../../../lib/src/openai/config';
|
||||
import { CompletionHeaders, CompletionRequestExtra, PostOptions } from '../../../../lib/src/openai/fetch';
|
||||
import { APIChoice, FinishedCallback } from '../../../../lib/src/openai/openai';
|
||||
import { contextIndentation, parsingBlockFinished } from '../../../../lib/src/prompt/parseBlock';
|
||||
import { extractPrompt, Prompt } from '../../../../lib/src/prompt/prompt';
|
||||
import { extractRepoInfoInBackground, MaybeRepoInfo } from '../../../../lib/src/prompt/repository';
|
||||
import { telemetrizePromptLength, telemetry, TelemetryData, TelemetryWithExp } from '../../../../lib/src/telemetry';
|
||||
import { IPosition, ITextDocument, LocationFactory, TextDocumentContents } from '../../../../lib/src/textDocument';
|
||||
import { isSupportedLanguageId } from '../../../../prompt/src/parse';
|
||||
import { Position } from '../../../../types/src';
|
||||
import { ISolutionHandler, SolutionsStream, UnformattedSolution } from './panelTypes';
|
||||
|
||||
export const solutionCountTarget = 10;
|
||||
|
||||
export function panelPositionForDocument(document: TextDocumentContents, position: Position): IPosition {
|
||||
let returnPosition = position;
|
||||
const line = document.lineAt(position.line);
|
||||
if (!line.isEmptyOrWhitespace) {
|
||||
returnPosition = line.range.end;
|
||||
}
|
||||
return returnPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim trailing whitespace.
|
||||
*/
|
||||
export async function* trimChoices(choices: AsyncIterable<APIChoice>): AsyncIterable<APIChoice> {
|
||||
for await (const choice of choices) {
|
||||
const choiceCopy = { ...choice };
|
||||
choiceCopy.completionText = choiceCopy.completionText.trimEnd();
|
||||
yield choiceCopy;
|
||||
}
|
||||
}
|
||||
|
||||
export class SolutionManager {
|
||||
private _savedTelemetryData?: TelemetryWithExp | undefined;
|
||||
readonly targetPosition = panelPositionForDocument(this.textDocument, this.startPosition);
|
||||
|
||||
constructor(
|
||||
readonly textDocument: ITextDocument,
|
||||
public startPosition: IPosition,
|
||||
readonly cancellationToken: CancellationToken,
|
||||
readonly solutionCountTarget: number
|
||||
) { }
|
||||
|
||||
get savedTelemetryData(): TelemetryWithExp | undefined {
|
||||
return this._savedTelemetryData;
|
||||
}
|
||||
|
||||
set savedTelemetryData(data: TelemetryWithExp | undefined) {
|
||||
this._savedTelemetryData = data;
|
||||
}
|
||||
}
|
||||
|
||||
export async function reportSolutions(
|
||||
nextSolutionPromise: Promise<SolutionsStream>,
|
||||
solutionHandler: ISolutionHandler
|
||||
): Promise<void> {
|
||||
const nextSolution = await nextSolutionPromise;
|
||||
switch (nextSolution.status) {
|
||||
case 'Solution':
|
||||
await solutionHandler.onSolution(nextSolution.solution);
|
||||
await reportSolutions(nextSolution.next, solutionHandler);
|
||||
break;
|
||||
case 'FinishedNormally':
|
||||
await solutionHandler.onFinishedNormally();
|
||||
break;
|
||||
case 'FinishedWithError':
|
||||
await solutionHandler.onFinishedWithError(nextSolution.error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateSolutionsStream(
|
||||
cancellationToken: CancellationToken,
|
||||
solutions: AsyncIterator<UnformattedSolution>
|
||||
): Promise<SolutionsStream> {
|
||||
if (cancellationToken.isCancellationRequested) {
|
||||
return { status: 'FinishedWithError', error: 'Cancelled' };
|
||||
}
|
||||
const nextResult = await solutions.next();
|
||||
if (nextResult.done === true) {
|
||||
return { status: 'FinishedNormally' };
|
||||
}
|
||||
return {
|
||||
status: 'Solution',
|
||||
solution: nextResult.value,
|
||||
next: generateSolutionsStream(cancellationToken, solutions),
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeCompletionText(text: string): string {
|
||||
return text.replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of prompt processing setup
|
||||
*/
|
||||
export interface PromptSetupResult {
|
||||
prompt: Prompt;
|
||||
trailingWs: string;
|
||||
telemetryData: TelemetryWithExp;
|
||||
repoInfo: MaybeRepoInfo;
|
||||
ourRequestId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up prompt extraction, telemetry, and handles common error cases.
|
||||
* Returns null if an error occurred that should terminate processing.
|
||||
*/
|
||||
export async function setupPromptAndTelemetry(
|
||||
accessor: ServicesAccessor,
|
||||
solutionManager: SolutionManager,
|
||||
source: 'open copilot' | 'open comparison',
|
||||
solutionsLogger: Logger,
|
||||
engineName?: string,
|
||||
comparisonRequestId?: string
|
||||
): Promise<PromptSetupResult | SolutionsStream> {
|
||||
const position = solutionManager.targetPosition;
|
||||
const document = solutionManager.textDocument;
|
||||
|
||||
const repoInfo = extractRepoInfoInBackground(accessor, document.uri);
|
||||
|
||||
// Telemetry setup
|
||||
const ourRequestId = generateUuid();
|
||||
const tempTelemetry = TelemetryData.createAndMarkAsIssued(
|
||||
{
|
||||
headerRequestId: ourRequestId,
|
||||
languageId: document.detectedLanguageId,
|
||||
source,
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const featuresService = accessor.get(ICompletionsFeaturesService);
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const logTarget = accessor.get(ICompletionsLogTargetService);
|
||||
// Update telemetry with experiment values
|
||||
solutionManager.savedTelemetryData = await featuresService
|
||||
.fetchTokenAndUpdateExPValuesAndAssignments(
|
||||
{ uri: document.uri, languageId: document.detectedLanguageId },
|
||||
tempTelemetry
|
||||
);
|
||||
|
||||
// Add in comparison panel specific info
|
||||
if (engineName) {
|
||||
solutionManager.savedTelemetryData = solutionManager.savedTelemetryData!.extendedBy({
|
||||
engineName,
|
||||
});
|
||||
}
|
||||
if (comparisonRequestId) {
|
||||
solutionManager.savedTelemetryData = solutionManager.savedTelemetryData!.extendedBy({
|
||||
comparisonRequestId,
|
||||
});
|
||||
}
|
||||
|
||||
// Extract prompt
|
||||
const promptResponse = await instantiationService.invokeFunction(extractPrompt,
|
||||
ourRequestId,
|
||||
createCompletionState(document, position),
|
||||
solutionManager.savedTelemetryData!
|
||||
);
|
||||
|
||||
// Handle prompt extraction errors
|
||||
if (promptResponse.type === 'copilotContentExclusion') {
|
||||
return { status: 'FinishedNormally' };
|
||||
}
|
||||
if (promptResponse.type === 'contextTooShort') {
|
||||
return { status: 'FinishedWithError', error: 'Context too short' };
|
||||
}
|
||||
if (promptResponse.type === 'promptCancelled') {
|
||||
return { status: 'FinishedWithError', error: 'Prompt cancelled' };
|
||||
}
|
||||
if (promptResponse.type === 'promptTimeout') {
|
||||
return { status: 'FinishedWithError', error: 'Prompt timeout' };
|
||||
}
|
||||
if (promptResponse.type === 'promptError') {
|
||||
return { status: 'FinishedWithError', error: 'Prompt error' };
|
||||
}
|
||||
|
||||
const prompt = promptResponse.prompt;
|
||||
const trailingWs = promptResponse.trailingWs;
|
||||
|
||||
// Handle trailing whitespace adjustment
|
||||
if (trailingWs.length > 0) {
|
||||
solutionManager.startPosition = LocationFactory.position(
|
||||
solutionManager.startPosition.line,
|
||||
solutionManager.startPosition.character - trailingWs.length
|
||||
);
|
||||
}
|
||||
|
||||
// Update telemetry with prompt information
|
||||
solutionManager.savedTelemetryData = solutionManager.savedTelemetryData!.extendedBy(
|
||||
{},
|
||||
{
|
||||
...telemetrizePromptLength(prompt),
|
||||
solutionCount: solutionManager.solutionCountTarget,
|
||||
promptEndPos: document.offsetAt(position),
|
||||
}
|
||||
);
|
||||
|
||||
solutionsLogger.debug(logTarget, 'prompt:', prompt);
|
||||
instantiationService.invokeFunction(telemetry, 'solution.requested', solutionManager.savedTelemetryData);
|
||||
|
||||
return {
|
||||
prompt,
|
||||
trailingWs,
|
||||
telemetryData: solutionManager.savedTelemetryData,
|
||||
repoInfo,
|
||||
ourRequestId,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of completion parameters setup
|
||||
*/
|
||||
export interface CompletionSetupResult {
|
||||
extra: CompletionRequestExtra;
|
||||
postOptions: PostOptions;
|
||||
finishedCb: FinishedCallback;
|
||||
engineInfo: { modelId: string; headers: CompletionHeaders };
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up block mode, completion parameters, and finished callback.
|
||||
*/
|
||||
export function setupCompletionParams(
|
||||
accessor: ServicesAccessor,
|
||||
document: ITextDocument,
|
||||
position: IPosition,
|
||||
prompt: Prompt,
|
||||
solutionManager: SolutionManager,
|
||||
telemetryData: TelemetryWithExp
|
||||
): CompletionSetupResult {
|
||||
// Compute block mode
|
||||
const blockMode = accessor.get(ICompletionsBlockModeConfig).forLanguage(document.detectedLanguageId, telemetryData);
|
||||
const isSupportedLanguage = isSupportedLanguageId(document.detectedLanguageId);
|
||||
|
||||
const contextIndent = contextIndentation(document, position);
|
||||
const extra: CompletionRequestExtra = {
|
||||
language: document.detectedLanguageId,
|
||||
next_indent: contextIndent.next ?? 0,
|
||||
prompt_tokens: prompt.prefixTokens ?? 0,
|
||||
suffix_tokens: prompt.suffixTokens ?? 0,
|
||||
};
|
||||
|
||||
const postOptions: PostOptions = {};
|
||||
if (blockMode === BlockMode.Parsing && !isSupportedLanguage) {
|
||||
postOptions['stop'] = ['\n\n', '\r\n\r\n'];
|
||||
}
|
||||
|
||||
const engineInfo = getEngineRequestInfo(accessor, telemetryData);
|
||||
|
||||
let finishedCb: FinishedCallback;
|
||||
|
||||
switch (blockMode) {
|
||||
case BlockMode.Server:
|
||||
// Client knows the block is done when the completion is.
|
||||
finishedCb = () => undefined;
|
||||
// If requested at the top-level, don't trim at all.
|
||||
extra.force_indent = contextIndent.prev ?? -1;
|
||||
extra.trim_by_indentation = true;
|
||||
break;
|
||||
case BlockMode.ParsingAndServer:
|
||||
finishedCb = isSupportedLanguage
|
||||
? parsingBlockFinished(document, solutionManager.startPosition)
|
||||
: () => undefined;
|
||||
// If requested at the top-level, don't trim at all.
|
||||
extra.force_indent = contextIndent.prev ?? -1;
|
||||
extra.trim_by_indentation = true;
|
||||
break;
|
||||
case BlockMode.Parsing:
|
||||
default:
|
||||
finishedCb = isSupportedLanguage
|
||||
? parsingBlockFinished(document, solutionManager.startPosition)
|
||||
: () => undefined;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
extra,
|
||||
postOptions,
|
||||
finishedCb,
|
||||
engineInfo,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CopilotNamedAnnotationList } from '../../../../../../../platform/completions-core/common/openai/copilotAnnotations';
|
||||
import { RequestId } from '../../../../../../../platform/networking/common/fetch';
|
||||
import { TelemetryWithExp } from '../../../../lib/src/telemetry';
|
||||
import { IRange } from '../../../../lib/src/textDocument';
|
||||
|
||||
export interface UnformattedSolution {
|
||||
/** Raw text returned by model */
|
||||
completionText: string;
|
||||
/** Text that should be inserted into the document, replacing the text at .range */
|
||||
insertText: string;
|
||||
range: IRange;
|
||||
meanProb: number;
|
||||
meanLogProb: number;
|
||||
requestId: RequestId;
|
||||
choiceIndex: number;
|
||||
telemetryData: TelemetryWithExp;
|
||||
copilotAnnotations?: CopilotNamedAnnotationList;
|
||||
/** Optional Model ID when fetching from multiple models */
|
||||
modelId?: string;
|
||||
}
|
||||
|
||||
export interface ISolutionHandler {
|
||||
onSolution(solution: UnformattedSolution): Promise<void> | void;
|
||||
onFinishedNormally(): Promise<void> | void;
|
||||
onFinishedWithError(error: string): Promise<void> | void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A stream of solutions, ending either with 'FinishedNormally' or 'FinishedWithError'.
|
||||
* This structure allows for errors to occur part way through the stream, as well as
|
||||
* at the beginning.
|
||||
*
|
||||
* The stream is similar to an async generator, but with more information when the stream
|
||||
* ends: instead of just `done` we can have `FinishedNormally` or `FinishedWithError`.
|
||||
*/
|
||||
export type SolutionsStream =
|
||||
| { status: 'FinishedNormally' }
|
||||
| { status: 'FinishedWithError'; error: string }
|
||||
| { status: 'Solution'; solution: UnformattedSolution; next: Promise<SolutionsStream> };
|
||||
@@ -0,0 +1,148 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { env, QuickPick, QuickPickItem, QuickPickItemKind, Uri, window, workspace } from 'vscode';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { ConfigKey, getConfig } from '../../lib/src/config';
|
||||
import { CopilotConfigPrefix } from '../../lib/src/constants';
|
||||
import { AsyncCompletionManager, ICompletionsAsyncManagerService } from '../../lib/src/ghostText/asyncCompletions';
|
||||
import { CompletionsCache, ICompletionsCacheService } from '../../lib/src/ghostText/completionsCache';
|
||||
import { ICompletionsLogTargetService, Logger } from '../../lib/src/logger';
|
||||
import { AvailableModelsManager, ICompletionsModelManagerService, ModelItem } from '../../lib/src/openai/model';
|
||||
import { telemetry, TelemetryData } from '../../lib/src/telemetry';
|
||||
const logger = new Logger('modelPicker');
|
||||
|
||||
interface ModelPickerItem extends Omit<ModelItem, 'preview' | 'tokenizer'>, QuickPickItem {
|
||||
// Distinguish between items in the quick pick
|
||||
type: 'model' | 'separator' | 'learn-more';
|
||||
}
|
||||
|
||||
// Separator and learn-more links are always shown in the quick pick
|
||||
const defaultModelPickerItems: ModelPickerItem[] = [
|
||||
// Add separator after the models
|
||||
{
|
||||
label: '',
|
||||
kind: QuickPickItemKind.Separator,
|
||||
modelId: 'separator',
|
||||
type: 'separator' as const,
|
||||
alwaysShow: true,
|
||||
},
|
||||
// Add "Learn more" item at the end
|
||||
{
|
||||
modelId: 'learn-more',
|
||||
label: 'Learn more $(link-external)',
|
||||
description: '',
|
||||
alwaysShow: true,
|
||||
type: 'learn-more' as const,
|
||||
},
|
||||
];
|
||||
|
||||
export class ModelPickerManager {
|
||||
// URL for information about Copilot models
|
||||
private readonly MODELS_INFO_URL = 'https://aka.ms/CopilotCompletionsModelPickerLearnMore';
|
||||
|
||||
get models(): ModelItem[] {
|
||||
return this._modelManager.getGenericCompletionModels();
|
||||
}
|
||||
|
||||
private getDefaultModelId(): string {
|
||||
return this._modelManager.getDefaultModelId();
|
||||
}
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ICompletionsAsyncManagerService private readonly _asyncCompletionManager: AsyncCompletionManager,
|
||||
@ICompletionsModelManagerService private readonly _modelManager: AvailableModelsManager,
|
||||
@ICompletionsLogTargetService private readonly _logTarget: ICompletionsLogTargetService,
|
||||
@ICompletionsCacheService private readonly _completionsCache: CompletionsCache
|
||||
) { }
|
||||
|
||||
async setUserSelectedCompletionModel(modelId: string | null) {
|
||||
return workspace
|
||||
.getConfiguration(CopilotConfigPrefix)
|
||||
.update(ConfigKey.UserSelectedCompletionModel, modelId ?? '', true);
|
||||
}
|
||||
|
||||
async handleModelSelection(quickpickList: QuickPick<ModelPickerItem>) {
|
||||
const model = quickpickList.activeItems[0];
|
||||
if (model === undefined) {
|
||||
return;
|
||||
}
|
||||
quickpickList.hide();
|
||||
|
||||
// Open up the link
|
||||
if (model.type === 'learn-more') {
|
||||
await env.openExternal(Uri.parse(this.MODELS_INFO_URL));
|
||||
this._instantiationService.invokeFunction(telemetry, 'modelPicker.learnMoreClicked');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.selectModel(model);
|
||||
}
|
||||
|
||||
async selectModel(model: ModelPickerItem) {
|
||||
const currentModel = this._instantiationService.invokeFunction(getUserSelectedModelConfiguration);
|
||||
|
||||
if (currentModel !== model.modelId) {
|
||||
this._completionsCache.clear();
|
||||
this._asyncCompletionManager.clear();
|
||||
}
|
||||
|
||||
const modelSelection = model.modelId === this.getDefaultModelId() ? null : model.modelId;
|
||||
await this.setUserSelectedCompletionModel(modelSelection);
|
||||
if (modelSelection === null) {
|
||||
logger.info(this._logTarget, `User selected default model; setting null`);
|
||||
} else {
|
||||
logger.info(this._logTarget, `Selected model: ${model.modelId}`);
|
||||
}
|
||||
|
||||
this._instantiationService.invokeFunction(
|
||||
telemetry,
|
||||
'modelPicker.modelSelected',
|
||||
TelemetryData.createAndMarkAsIssued({
|
||||
engineName: modelSelection ?? 'default',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private modelsForModelPicker(): [string | null, ModelPickerItem[]] {
|
||||
const currentModelSelection = this._instantiationService.invokeFunction(getUserSelectedModelConfiguration);
|
||||
const items: ModelPickerItem[] = this.models.map(model => {
|
||||
return {
|
||||
modelId: model.modelId,
|
||||
label: `${model.label}${model.preview ? ' (Preview)' : ''}`,
|
||||
description: `(${model.modelId})`,
|
||||
alwaysShow: model.modelId === this.getDefaultModelId(),
|
||||
type: 'model' as const,
|
||||
};
|
||||
});
|
||||
|
||||
return [currentModelSelection, items];
|
||||
}
|
||||
|
||||
showModelPicker(): QuickPick<ModelPickerItem> {
|
||||
const [currentModelSelection, items] = this.modelsForModelPicker();
|
||||
|
||||
const quickPick = window.createQuickPick<ModelPickerItem>();
|
||||
quickPick.title = 'Change Completions Model';
|
||||
quickPick.items = [...items, ...defaultModelPickerItems];
|
||||
quickPick.onDidAccept(() => this.handleModelSelection(quickPick));
|
||||
|
||||
const currentModelOrDefault = currentModelSelection ?? this.getDefaultModelId();
|
||||
|
||||
// set the currently selected model as active
|
||||
const selectedItem = quickPick.items.find(item => item.modelId === currentModelOrDefault);
|
||||
if (selectedItem) {
|
||||
quickPick.activeItems = [selectedItem];
|
||||
}
|
||||
|
||||
quickPick.show();
|
||||
return quickPick;
|
||||
}
|
||||
}
|
||||
|
||||
function getUserSelectedModelConfiguration(accessor: ServicesAccessor): string | null {
|
||||
const value = getConfig<string | null>(accessor, ConfigKey.UserSelectedCompletionModel);
|
||||
return typeof value === 'string' && value.length > 0 ? value : null;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Position, Range } from 'vscode';
|
||||
import { IInstantiationService } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { postInsertionTasks } from '../../../lib/src/postInsertion';
|
||||
import { countLines } from '../../../lib/src/suggestions/partialSuggestions';
|
||||
import { IPosition, ITextDocument } from '../../../lib/src/textDocument';
|
||||
import { normalizeCompletionText, solutionCountTarget, SolutionManager } from '../lib/panelShared/common';
|
||||
import { UnformattedSolution } from '../lib/panelShared/panelTypes';
|
||||
import { BasePanelCompletion, ISuggestionsPanel } from './basePanelTypes';
|
||||
|
||||
// BaseListDocument to be shared with both the copilot and comparison completion panels.
|
||||
export abstract class BaseListDocument<TPanelCompletion extends BasePanelCompletion> extends SolutionManager {
|
||||
private _solutionCount = 0;
|
||||
protected readonly _solutions: TPanelCompletion[] = [];
|
||||
|
||||
constructor(
|
||||
textDocument: ITextDocument,
|
||||
position: IPosition,
|
||||
readonly panel: ISuggestionsPanel,
|
||||
countTarget = solutionCountTarget,
|
||||
@IInstantiationService protected readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
super(textDocument, position, panel.cancellationToken, countTarget);
|
||||
}
|
||||
|
||||
protected abstract createPanelCompletion(
|
||||
unformatted: UnformattedSolution,
|
||||
baseCompletion: BasePanelCompletion
|
||||
): TPanelCompletion;
|
||||
protected abstract shouldAddSolution(newItem: TPanelCompletion): boolean;
|
||||
protected abstract runSolutionsImpl(): Promise<void>;
|
||||
|
||||
// Find if two solutions are duplicates by comparing their normalized text content.
|
||||
protected areSolutionsDuplicates(solutionA: TPanelCompletion, solutionB: TPanelCompletion): boolean {
|
||||
const stripA = normalizeCompletionText(solutionA.insertText);
|
||||
const stripB = normalizeCompletionText(solutionB.insertText);
|
||||
return stripA === stripB;
|
||||
}
|
||||
|
||||
protected findDuplicateSolution(newItem: TPanelCompletion): TPanelCompletion | undefined {
|
||||
return this._solutions.find(item => this.areSolutionsDuplicates(item, newItem));
|
||||
}
|
||||
|
||||
onSolution(unformatted: UnformattedSolution) {
|
||||
const offset = this.textDocument.offsetAt(this.targetPosition);
|
||||
const rank = this._solutions.length;
|
||||
|
||||
const postInsertionCallback = () => {
|
||||
const telemetryData = this.savedTelemetryData!.extendedBy(
|
||||
{
|
||||
choiceIndex: unformatted.choiceIndex.toString(),
|
||||
engineName: unformatted.modelId || '',
|
||||
},
|
||||
{
|
||||
compCharLen: unformatted.insertText.length,
|
||||
meanProb: unformatted.meanProb,
|
||||
rank,
|
||||
}
|
||||
);
|
||||
return this.instantiationService.invokeFunction(postInsertionTasks,
|
||||
'solution',
|
||||
unformatted.insertText,
|
||||
offset,
|
||||
this.textDocument.uri,
|
||||
telemetryData,
|
||||
{
|
||||
compType: 'full',
|
||||
acceptedLength: unformatted.insertText.length,
|
||||
acceptedLines: countLines(unformatted.insertText),
|
||||
},
|
||||
unformatted.copilotAnnotations
|
||||
);
|
||||
};
|
||||
|
||||
const baseCompletion: BasePanelCompletion = {
|
||||
insertText: unformatted.insertText,
|
||||
range: new Range(
|
||||
new Position(unformatted.range.start.line, unformatted.range.start.character),
|
||||
new Position(unformatted.range.end.line, unformatted.range.end.character)
|
||||
),
|
||||
copilotAnnotations: unformatted.copilotAnnotations,
|
||||
postInsertionCallback,
|
||||
};
|
||||
|
||||
const newItem = this.createPanelCompletion(unformatted, baseCompletion);
|
||||
|
||||
if (this.shouldAddSolution(newItem)) {
|
||||
this.panel.onItem(newItem);
|
||||
this._solutions.push(newItem);
|
||||
}
|
||||
this._solutionCount++;
|
||||
this.panel.onWorkDone({ percentage: (100 * this._solutionCount) / this.solutionCountTarget });
|
||||
}
|
||||
|
||||
onFinishedNormally() {
|
||||
return this.panel.onFinished();
|
||||
}
|
||||
|
||||
onFinishedWithError(_: string) {
|
||||
return this.onFinishedNormally();
|
||||
}
|
||||
|
||||
runQuery() {
|
||||
return this.runSolutionsImpl();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken, Range } from 'vscode';
|
||||
import { CopilotNamedAnnotationList } from '../../../../../../platform/completions-core/common/openai/copilotAnnotations';
|
||||
|
||||
// Base interface for a completion displayed in the panel.
|
||||
export interface BasePanelCompletion {
|
||||
insertText: string;
|
||||
range: Range;
|
||||
copilotAnnotations?: CopilotNamedAnnotationList;
|
||||
postInsertionCallback: () => PromiseLike<void> | void;
|
||||
}
|
||||
|
||||
// Interface for the suggestions panel, which handles work done notifications and item selections.
|
||||
export interface ISuggestionsPanel {
|
||||
cancellationToken: CancellationToken;
|
||||
onWorkDone(_: { percentage: number }): void;
|
||||
onItem(_: BasePanelCompletion): void;
|
||||
onFinished(): void;
|
||||
}
|
||||
|
||||
// Configuration for webview panels for completions.
|
||||
export interface PanelConfig {
|
||||
panelTitle: string;
|
||||
webviewId: string;
|
||||
webviewScriptName: string;
|
||||
contextVariable: string;
|
||||
commands: {
|
||||
accept: string;
|
||||
navigatePrevious: string;
|
||||
navigateNext: string;
|
||||
};
|
||||
renderingMode: 'streaming' | 'batch';
|
||||
shuffleSolutions: boolean;
|
||||
}
|
||||
|
||||
// Configuration for webview panels, used to pass settings to the webview.
|
||||
export interface WebviewConfig {
|
||||
renderingMode: 'batch' | 'streaming';
|
||||
shuffleSolutions: boolean;
|
||||
}
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import {
|
||||
CancellationTokenSource,
|
||||
Disposable,
|
||||
Event,
|
||||
EventEmitter,
|
||||
TextDocument,
|
||||
Uri,
|
||||
WebviewPanel,
|
||||
WorkspaceEdit,
|
||||
commands,
|
||||
workspace,
|
||||
} from 'vscode';
|
||||
import { IVSCodeExtensionContext } from '../../../../../../platform/extContext/common/extensionContext';
|
||||
import { debounce } from '../../../../../../util/common/debounce';
|
||||
import { BasePanelCompletion, ISuggestionsPanel, PanelConfig } from './basePanelTypes';
|
||||
import { Highlighter } from './highlighter';
|
||||
import { getNonce, pluralize } from './utils';
|
||||
|
||||
//import { IPCitationDetail } from '#lib/citationManager';
|
||||
interface IPCitationDetail {
|
||||
license: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface SuggestionsPanelManagerInterface {
|
||||
activeWebviewPanel: BaseSuggestionsPanel<BasePanelCompletion> | undefined;
|
||||
decrementPanelCount(): void;
|
||||
}
|
||||
|
||||
export interface SolutionContent {
|
||||
htmlSnippet: string;
|
||||
citation?: { message: string; url: string };
|
||||
[key: string]: unknown; // Allow additional properties for panel-specific content
|
||||
}
|
||||
|
||||
export interface BaseWebviewMessage {
|
||||
command: string;
|
||||
}
|
||||
|
||||
interface AcceptSolutionMessage extends BaseWebviewMessage {
|
||||
command: 'acceptSolution';
|
||||
solutionIndex: number;
|
||||
}
|
||||
|
||||
interface FocusSolutionMessage extends BaseWebviewMessage {
|
||||
command: 'focusSolution';
|
||||
solutionIndex: number;
|
||||
}
|
||||
|
||||
interface SubmitFeedbackMessage extends BaseWebviewMessage {
|
||||
command: 'submitFeedback';
|
||||
solutionIndex: number;
|
||||
feedback: string;
|
||||
}
|
||||
|
||||
interface RefreshMessage extends BaseWebviewMessage {
|
||||
command: 'refresh';
|
||||
}
|
||||
|
||||
interface WebviewReadyMessage extends BaseWebviewMessage {
|
||||
command: 'webviewReady';
|
||||
}
|
||||
|
||||
export type WebviewMessage =
|
||||
| AcceptSolutionMessage
|
||||
| FocusSolutionMessage
|
||||
| SubmitFeedbackMessage
|
||||
| RefreshMessage
|
||||
| WebviewReadyMessage;
|
||||
|
||||
export abstract class BaseSuggestionsPanel<TPanelCompletion extends BasePanelCompletion> implements ISuggestionsPanel {
|
||||
private _disposables: Disposable[] = [];
|
||||
#items: TPanelCompletion[] = [];
|
||||
#batchItems: TPanelCompletion[] = [];
|
||||
#percentage = 0;
|
||||
#highlighter: Thenable<Highlighter>;
|
||||
private _focusedSolution: TPanelCompletion | undefined;
|
||||
private _isDisposed: boolean = false;
|
||||
#documentUri: Uri;
|
||||
#cts = new CancellationTokenSource();
|
||||
|
||||
private _onDidDispose = new EventEmitter<void>();
|
||||
readonly onDidDispose: Event<void> = this._onDidDispose.event;
|
||||
|
||||
get cancellationToken() {
|
||||
return this.#cts.token;
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly webviewPanel: WebviewPanel,
|
||||
document: TextDocument,
|
||||
protected suggestionsPanelManager: SuggestionsPanelManagerInterface,
|
||||
protected readonly config: PanelConfig,
|
||||
@IVSCodeExtensionContext protected readonly contextService: IVSCodeExtensionContext,
|
||||
) {
|
||||
webviewPanel.onDidDispose(() => this._dispose(), null, this._disposables);
|
||||
webviewPanel.webview.html = this._getWebviewContent();
|
||||
this.#documentUri = document.uri;
|
||||
|
||||
this.#highlighter = Highlighter.create(document.languageId);
|
||||
|
||||
workspace.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('workbench.colorTheme')) {
|
||||
return this.render();
|
||||
}
|
||||
});
|
||||
|
||||
webviewPanel.webview.onDidReceiveMessage(async (message: WebviewMessage) => {
|
||||
// First lest the subclass handle custom messages
|
||||
if ((await this.handleCustomMessage(message)) === true) {
|
||||
return;
|
||||
}
|
||||
switch (message.command) {
|
||||
case 'focusSolution':
|
||||
this._focusedSolution = this.#items[message.solutionIndex];
|
||||
return;
|
||||
case 'webviewReady':
|
||||
// Send the config to the webview
|
||||
void this.postMessage({
|
||||
command: 'updateConfig',
|
||||
config: {
|
||||
renderingMode: this.config.renderingMode,
|
||||
shuffleSolutions: this.config.shuffleSolutions,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
}, undefined);
|
||||
|
||||
webviewPanel.onDidChangeViewState(e => {
|
||||
if (e.webviewPanel?.visible) {
|
||||
this.suggestionsPanelManager.activeWebviewPanel = this;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected async handleCustomMessage(message: BaseWebviewMessage): Promise<boolean> {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
protected abstract renderSolutionContent(item: TPanelCompletion, baseContent: SolutionContent): SolutionContent;
|
||||
|
||||
private _buildExtensionUri(...path: string[]): Uri {
|
||||
const extensionPath = Uri.joinPath(this.contextService.extensionUri, ...path);
|
||||
return this.webviewPanel.webview.asWebviewUri(extensionPath);
|
||||
}
|
||||
|
||||
private _getWebviewContent() {
|
||||
const nonce = getNonce();
|
||||
const scriptUri = this._buildExtensionUri('dist', this.config.webviewScriptName);
|
||||
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'none'; font-src ${this.webviewPanel.webview.cspSource}; style-src 'unsafe-inline' ${this.webviewPanel.webview.cspSource}; script-src 'nonce-${nonce}';"
|
||||
/>
|
||||
<title>${this.config.panelTitle}</title>
|
||||
<style>
|
||||
.solutionHeading {
|
||||
margin-top: 40px;
|
||||
}
|
||||
pre:focus-visible {
|
||||
border: 1px solid var(--vscode-focusBorder);
|
||||
outline: none;
|
||||
}
|
||||
pre {
|
||||
margin-bottom: 6px;
|
||||
display: block;
|
||||
padding: 9.5px;
|
||||
line-height: 1.42857143;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--vscode-notebook-cellBorderColor);
|
||||
white-space: pre-wrap;
|
||||
font-size: var(--vscode-editor-font-size);
|
||||
}
|
||||
pre.shiki {
|
||||
padding: 0.5em 0.7em;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
code {
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>${this.config.panelTitle}</h2>
|
||||
<div id="loadingContainer" aria-live="assertive" aria-atomic="true">
|
||||
<label for="progress-bar">Loading suggestions:</label>
|
||||
<progress id="progress-bar" max="100" value="0"></progress>
|
||||
</div>
|
||||
<div id="solutionsContainer" aria-busy="true" aria-describedby="progress-bar"></div>
|
||||
<script nonce="${nonce}" type="module" src="${scriptUri.toString()}"></script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
|
||||
onWorkDone({ percentage }: { percentage: number }) {
|
||||
this.#percentage = percentage;
|
||||
void this.render();
|
||||
}
|
||||
|
||||
onItem(item: TPanelCompletion) {
|
||||
// If rendering mode is 'batch', we collect items and render them later
|
||||
// Otherwise, we render immediately
|
||||
if (this.config.renderingMode === 'batch') {
|
||||
this.#batchItems.push(item);
|
||||
} else {
|
||||
this.#items.push(item);
|
||||
void this.render();
|
||||
}
|
||||
}
|
||||
|
||||
clearSolutions() {
|
||||
// Cancel any ongoing operations
|
||||
this.#cts.cancel();
|
||||
// Create a new cancellation token source for the next operation
|
||||
this.#cts = new CancellationTokenSource();
|
||||
|
||||
// Clear all solutions and reset state
|
||||
this.#items = [];
|
||||
this.#batchItems = [];
|
||||
this._focusedSolution = undefined;
|
||||
this.#percentage = 0;
|
||||
void this.render();
|
||||
}
|
||||
|
||||
onFinished() {
|
||||
this.#percentage = 100;
|
||||
|
||||
// If we have batch items, add them to the main items list, shuffle if needed, and render
|
||||
if (this.#batchItems.length > 0) {
|
||||
this.#items.push(...this.#batchItems);
|
||||
|
||||
if (this.config.shuffleSolutions) {
|
||||
this.#items = this.#items.sort(() => Math.random() - 0.5);
|
||||
}
|
||||
|
||||
this.#batchItems = [];
|
||||
}
|
||||
|
||||
void this.render();
|
||||
}
|
||||
|
||||
protected async acceptSolution(solution: TPanelCompletion, closePanel: boolean = true) {
|
||||
if (this._isDisposed === false && solution?.range) {
|
||||
const edit = new WorkspaceEdit();
|
||||
edit.replace(this.#documentUri, solution.range, solution.insertText);
|
||||
await workspace.applyEdit(edit);
|
||||
this.#cts.cancel();
|
||||
if (closePanel) {
|
||||
await commands.executeCommand('workbench.action.closeActiveEditor');
|
||||
}
|
||||
await solution.postInsertionCallback();
|
||||
}
|
||||
}
|
||||
|
||||
protected items(): TPanelCompletion[] {
|
||||
return this.#items;
|
||||
}
|
||||
|
||||
async acceptFocusedSolution() {
|
||||
const solution = this._focusedSolution;
|
||||
if (solution) {
|
||||
return this.acceptSolution(solution);
|
||||
}
|
||||
}
|
||||
|
||||
protected async renderSolutions() {
|
||||
const highlighter = await this.#highlighter;
|
||||
const content = this.#items.map(item => {
|
||||
const firstCitation = item.copilotAnnotations?.ip_code_citations?.[0];
|
||||
const details = firstCitation?.details.citations as IPCitationDetail[] | undefined;
|
||||
let renderedCitatation: { message: string; url: string } | undefined;
|
||||
if (details && details.length > 0) {
|
||||
const licensesSet = new Set(details.map(d => d.license));
|
||||
if (licensesSet.has('NOASSERTION')) {
|
||||
licensesSet.delete('NOASSERTION');
|
||||
licensesSet.add('unknown');
|
||||
}
|
||||
const allLicenses = Array.from(licensesSet).sort();
|
||||
const licenseString = allLicenses.length === 1 ? allLicenses[0] : `[${allLicenses.join(', ')}]`;
|
||||
renderedCitatation = {
|
||||
message: `Similar code with ${pluralize(allLicenses.length, 'license type')} ${licenseString} detected.`,
|
||||
url: details[0].url,
|
||||
};
|
||||
}
|
||||
|
||||
const baseContent = {
|
||||
htmlSnippet: highlighter.createSnippet(item.insertText.trim()),
|
||||
citation: renderedCitatation,
|
||||
};
|
||||
|
||||
return this.renderSolutionContent(item, baseContent);
|
||||
});
|
||||
|
||||
const message = this.createSolutionsMessage(content, this.#percentage);
|
||||
await this.postMessage(message);
|
||||
}
|
||||
|
||||
// Subclasses must implement this to create their specific message format
|
||||
protected abstract createSolutionsMessage(content: SolutionContent[], percentage: number): unknown;
|
||||
|
||||
render = debounce(10, () => this.renderSolutions());
|
||||
|
||||
postMessage(message: unknown) {
|
||||
if (this._isDisposed === false) {
|
||||
return this.webviewPanel.webview.postMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
private _dispose() {
|
||||
this._isDisposed = true;
|
||||
this._onDidDispose.fire();
|
||||
this.suggestionsPanelManager.decrementPanelCount();
|
||||
while (this._disposables.length) {
|
||||
const disposable = this._disposables.pop();
|
||||
if (disposable) {
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
||||
this._onDidDispose.dispose();
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TextDocument, Uri, ViewColumn, WebviewPanel, commands, window } from 'vscode';
|
||||
import { IVSCodeExtensionContext } from '../../../../../../platform/extContext/common/extensionContext';
|
||||
import { DisposableStore, IDisposable } from '../../../../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { IPosition, ITextDocument } from '../../../lib/src/textDocument';
|
||||
import { basename } from '../../../lib/src/util/uri';
|
||||
import { registerCommandWrapper } from '../telemetry';
|
||||
import { BasePanelCompletion, PanelConfig } from './basePanelTypes';
|
||||
import { BaseSuggestionsPanel, SuggestionsPanelManagerInterface } from './baseSuggestionsPanel';
|
||||
|
||||
export interface ListDocumentInterface {
|
||||
runQuery(): Promise<void>;
|
||||
}
|
||||
|
||||
export abstract class BaseSuggestionsPanelManager<TPanelCompletion extends BasePanelCompletion>
|
||||
implements SuggestionsPanelManagerInterface {
|
||||
activeWebviewPanel: BaseSuggestionsPanel<TPanelCompletion> | undefined;
|
||||
private _panelCount: number = 0;
|
||||
|
||||
constructor(
|
||||
protected readonly config: PanelConfig,
|
||||
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
|
||||
@IVSCodeExtensionContext protected readonly _extensionContext: IVSCodeExtensionContext,
|
||||
) { }
|
||||
|
||||
protected abstract createListDocument(
|
||||
wrapped: ITextDocument,
|
||||
position: IPosition,
|
||||
panel: BaseSuggestionsPanel<TPanelCompletion>
|
||||
): ListDocumentInterface;
|
||||
|
||||
protected abstract createSuggestionsPanel(
|
||||
panel: WebviewPanel,
|
||||
document: TextDocument,
|
||||
manager: this
|
||||
): BaseSuggestionsPanel<TPanelCompletion>;
|
||||
|
||||
renderPanel(
|
||||
document: TextDocument,
|
||||
position: IPosition,
|
||||
wrapped: ITextDocument
|
||||
): BaseSuggestionsPanel<TPanelCompletion> {
|
||||
const title = `${this.config.panelTitle} for ${basename(document.uri.toString()) || document.uri.toString()}`;
|
||||
const panel = window.createWebviewPanel(this.config.webviewId, title, ViewColumn.Two, {
|
||||
enableScripts: true,
|
||||
localResourceRoots: [Uri.joinPath(this._extensionContext.extensionUri, 'dist')],
|
||||
retainContextWhenHidden: true,
|
||||
});
|
||||
|
||||
const suggestionPanel = this.createSuggestionsPanel(panel, document, this);
|
||||
// Listen for the panel disposal event to clear our reference
|
||||
suggestionPanel.onDidDispose(() => {
|
||||
if (this.activeWebviewPanel === suggestionPanel) {
|
||||
this.activeWebviewPanel = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
void this.createListDocument(wrapped, position, suggestionPanel).runQuery();
|
||||
|
||||
this.activeWebviewPanel = suggestionPanel;
|
||||
this._panelCount = this._panelCount + 1;
|
||||
return suggestionPanel;
|
||||
}
|
||||
|
||||
registerCommands(): IDisposable {
|
||||
const disposableStore = new DisposableStore();
|
||||
|
||||
disposableStore.add(this._instantiationService.invokeFunction(registerCommandWrapper, this.config.commands.accept, () => {
|
||||
return this.activeWebviewPanel?.acceptFocusedSolution();
|
||||
}));
|
||||
|
||||
disposableStore.add(this._instantiationService.invokeFunction(registerCommandWrapper, this.config.commands.navigatePrevious, () => {
|
||||
return this.activeWebviewPanel?.postMessage({
|
||||
command: 'navigatePreviousSolution',
|
||||
});
|
||||
}));
|
||||
|
||||
disposableStore.add(this._instantiationService.invokeFunction(registerCommandWrapper, this.config.commands.navigateNext, () => {
|
||||
return this.activeWebviewPanel?.postMessage({
|
||||
command: 'navigateNextSolution',
|
||||
});
|
||||
}));
|
||||
|
||||
return disposableStore;
|
||||
}
|
||||
|
||||
decrementPanelCount() {
|
||||
this._panelCount = this._panelCount - 1;
|
||||
if (this._panelCount === 0) {
|
||||
void commands.executeCommand('setContext', this.config.contextVariable, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { getSingletonHighlighterCore, HighlighterCore, ThemeRegistration, ThemeRegistrationAny } from 'shiki/core';
|
||||
import * as langs from 'shiki/langs';
|
||||
import { BundledLanguage } from 'shiki/langs';
|
||||
import getWasmInlined from 'shiki/wasm';
|
||||
import { ColorThemeKind, window, workspace } from 'vscode';
|
||||
import * as languages from './languages';
|
||||
import * as themes from './themes';
|
||||
|
||||
export class Highlighter {
|
||||
private constructor(
|
||||
private languageId: string | undefined,
|
||||
private highlighter: HighlighterCore | undefined
|
||||
) { }
|
||||
|
||||
static async create(languageId = window.activeTextEditor?.document.languageId): Promise<Highlighter> {
|
||||
if (!languageId) {
|
||||
return new Highlighter(undefined, undefined);
|
||||
}
|
||||
|
||||
const highlighter = await getSingletonHighlighterCore({
|
||||
langs: Object.values(langs.bundledLanguages),
|
||||
loadWasm: getWasmInlined,
|
||||
});
|
||||
|
||||
// Load additional language if not out of the box for shiki
|
||||
if (!langs.bundledLanguages[languageId as BundledLanguage]) {
|
||||
const additionalLang = vscLanguageMap[languageId as keyof typeof vscLanguageMap];
|
||||
if (additionalLang) {
|
||||
await highlighter.loadLanguage(additionalLang);
|
||||
}
|
||||
}
|
||||
|
||||
return new Highlighter(languageId, highlighter);
|
||||
}
|
||||
|
||||
createSnippet(text: string): string {
|
||||
if (!this.highlighter || !this.languageId || !this.languageSupported()) {
|
||||
return `<pre>${text}</pre>`;
|
||||
}
|
||||
|
||||
return this.highlighter.codeToHtml(text, { lang: this.languageId, theme: getCurrentTheme() });
|
||||
}
|
||||
|
||||
private languageSupported() {
|
||||
if (!this.languageId) { return false; }
|
||||
|
||||
if (this.highlighter?.getLoadedLanguages().includes(this.languageId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentTheme(): ThemeRegistration {
|
||||
const workbenchConfig = workspace.getConfiguration('workbench');
|
||||
if (workbenchConfig) {
|
||||
const vsCodeTheme = workbenchConfig.get<string>('colorTheme');
|
||||
if (vsCodeTheme && isSupportedTheme(vsCodeTheme)) {
|
||||
return vscThemeMap[vsCodeTheme];
|
||||
}
|
||||
const themeType = window.activeColorTheme;
|
||||
const defaultTheme = vscDefaultMap[themeType.kind]; // fall back to default themes if we don't have a match
|
||||
|
||||
return defaultTheme;
|
||||
} else {
|
||||
return vscThemeMap['Default Dark Modern'];
|
||||
}
|
||||
}
|
||||
|
||||
const vscDefaultMap: { [key in ColorThemeKind]: ThemeRegistrationAny } = {
|
||||
[ColorThemeKind.Dark]: themes.darkModern,
|
||||
[ColorThemeKind.Light]: themes.lightModern,
|
||||
[ColorThemeKind.HighContrast]: themes.darkHC,
|
||||
[ColorThemeKind.HighContrastLight]: themes.lightHC,
|
||||
};
|
||||
|
||||
// These are vs code themes that aren't out of the box in shiki but come standard with vs code
|
||||
const vscThemeMap: { [key: string]: ThemeRegistrationAny } = {
|
||||
Abyss: themes.abyss,
|
||||
'Dark High Contrast': themes.darkHC,
|
||||
'Light High Constrast': themes.lightHC,
|
||||
'Default Dark Modern': themes.darkModern,
|
||||
'Kimbie Dark': themes.kimbieDark,
|
||||
'Default Light Modern': themes.lightModern,
|
||||
'Monokai Dimmed': themes.monokaiDim,
|
||||
'Quiet Light': themes.quietLight,
|
||||
Red: themes.red,
|
||||
'Tomorrow Night Blue': themes.tomorrowNightBlue,
|
||||
'Visual Studio Dark': themes.vsDark,
|
||||
'Visual Studio Light': themes.vsLight,
|
||||
'Default Dark+': themes.darkPlus,
|
||||
'Default Light+': themes.lightPlus,
|
||||
Monokai: themes.monokai,
|
||||
'Solarized Dark': themes.solarizedDark,
|
||||
'Solarized Light': themes.solarizedLight,
|
||||
} as const;
|
||||
|
||||
function isSupportedTheme(theme: keyof typeof vscThemeMap): theme is keyof typeof vscThemeMap {
|
||||
return theme in vscThemeMap;
|
||||
}
|
||||
|
||||
// These are vs code themes that aren't out of the box in shiki but come standard with vs code
|
||||
const vscLanguageMap = {
|
||||
'cuda-cpp': languages.cudaCpp,
|
||||
javascriptreact: languages.javascriptreact,
|
||||
markdown_latex_combined: languages.markdownLatexCombined,
|
||||
'markdown-math': languages.markdownMath,
|
||||
restructuredtext: languages.restructuredtext,
|
||||
'search-result': languages.searchResult,
|
||||
typescriptreact: languages.typescriptreact,
|
||||
} as const;
|
||||
+19825
File diff suppressed because one or more lines are too long
@@ -0,0 +1,11 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
export { cudaCpp } from './cuda-cpp.tmLanguage';
|
||||
export { javascriptreact } from './javaScriptReact.tmLanguage';
|
||||
export { markdownLatexCombined } from './markdown-latex-combined.tmLanguage';
|
||||
export { markdownMath } from './md-math.tmLanguage';
|
||||
export { restructuredtext } from './rst.tmLanguage';
|
||||
export { searchResult } from './searchResult.tmLanguage';
|
||||
export { typescriptreact } from './typeScriptReact.tmLanguage';
|
||||
+5928
File diff suppressed because one or more lines are too long
+3011
File diff suppressed because it is too large
Load Diff
+113
@@ -0,0 +1,113 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { LanguageInput } from 'shiki/core';
|
||||
|
||||
// This file includes some grammar rules copied from https://github.com/James-Yu/LaTeX-Workshop/blob/master/syntax/TeX.tmLanguage.json',
|
||||
export const markdownMath: LanguageInput = {
|
||||
name: 'markdown-math',
|
||||
scopeName: 'text.html.markdown.math',
|
||||
patterns: [
|
||||
{
|
||||
include: '#math',
|
||||
},
|
||||
],
|
||||
repository: {
|
||||
$self: {},
|
||||
$base: {},
|
||||
math: {
|
||||
patterns: [
|
||||
{
|
||||
name: 'comment.line.math.tex',
|
||||
match: '((?<!\\\\)%)(.+)$',
|
||||
captures: {
|
||||
'1': {
|
||||
name: 'punctuation.definition.comment.math.tex',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'line.separator.math.tex',
|
||||
match: '(\\\\\\\\)$',
|
||||
captures: {
|
||||
'1': {
|
||||
name: 'punctuation.line.separator.math.tex',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'meta.function.math.tex',
|
||||
begin: '((\\\\)([a-zA-Z_]+))\\s*(\\{)',
|
||||
beginCaptures: {
|
||||
'1': {
|
||||
name: 'storage.type.function.math.tex',
|
||||
},
|
||||
'2': {
|
||||
name: 'punctuation.definition.function.math.tex',
|
||||
},
|
||||
'3': {
|
||||
name: 'entity.name.function.math.tex',
|
||||
},
|
||||
'4': {
|
||||
name: 'punctuation.definition.arguments.begin.math.tex',
|
||||
},
|
||||
},
|
||||
end: '\\}',
|
||||
endCaptures: {
|
||||
'0': {
|
||||
name: 'punctuation.definition.arguments.end.math.tex',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '$self',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
captures: {
|
||||
'1': {
|
||||
name: 'punctuation.definition.constant.math.tex',
|
||||
},
|
||||
},
|
||||
match: '(\\\\)([a-zA-Z_]+)\\b',
|
||||
name: 'constant.character.math.tex',
|
||||
},
|
||||
{
|
||||
captures: {
|
||||
'1': {
|
||||
name: 'punctuation.definition.constant.math.tex',
|
||||
},
|
||||
},
|
||||
match: '(\\\\)(?!begin\\*\\{|verb)([A-Za-z]+)',
|
||||
name: 'constant.other.general.math.tex',
|
||||
},
|
||||
{
|
||||
match: '(?<!\\\\)\\{',
|
||||
name: 'punctuation.math.begin.bracket.curly',
|
||||
},
|
||||
{
|
||||
match: '(?<!\\\\)\\}',
|
||||
name: 'punctuation.math.end.bracket.curly',
|
||||
},
|
||||
{
|
||||
match: '\\(',
|
||||
name: 'punctuation.math.begin.bracket.round',
|
||||
},
|
||||
{
|
||||
match: '\\)',
|
||||
name: 'punctuation.math.end.bracket.round',
|
||||
},
|
||||
{
|
||||
match: '(([0-9]*[\\.][0-9]+)|[0-9]+)',
|
||||
name: 'constant.numeric.math.tex',
|
||||
},
|
||||
{
|
||||
match: '[\\+\\*/_\\^-]',
|
||||
name: 'punctuation.math.operator.latex',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
+740
@@ -0,0 +1,740 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/* eslint-disable local/no-unexternalized-strings */
|
||||
import { LanguageInput } from 'shiki/core';
|
||||
|
||||
// This file has been converted from https://github.com/trond-snekvik/vscode-rst/blob/master/syntaxes/rst.tmLanguage.json
|
||||
// If you want to provide a fix or improvement, please create a pull request against the original repository.
|
||||
// Once accepted there, we are happy to receive an update request.
|
||||
// version: https://github.com/trond-snekvik/vscode-rst/commit/f0fe19ffde6509be52ad9267a57e1b3df665f072
|
||||
export const restructuredtext: LanguageInput = {
|
||||
scopeName: 'source.rst',
|
||||
name: 'restructuredtext',
|
||||
patterns: [
|
||||
{
|
||||
include: '#body',
|
||||
},
|
||||
],
|
||||
repository: {
|
||||
$self: {},
|
||||
$base: {},
|
||||
body: {
|
||||
patterns: [
|
||||
{
|
||||
include: '#title',
|
||||
},
|
||||
{
|
||||
include: '#inline-markup',
|
||||
},
|
||||
{
|
||||
include: '#anchor',
|
||||
},
|
||||
{
|
||||
include: '#line-block',
|
||||
},
|
||||
{
|
||||
include: '#replace-include',
|
||||
},
|
||||
{
|
||||
include: '#footnote',
|
||||
},
|
||||
{
|
||||
include: '#substitution',
|
||||
},
|
||||
{
|
||||
include: '#blocks',
|
||||
},
|
||||
{
|
||||
include: '#table',
|
||||
},
|
||||
{
|
||||
include: '#simple-table',
|
||||
},
|
||||
{
|
||||
include: '#options-list',
|
||||
},
|
||||
],
|
||||
},
|
||||
title: {
|
||||
match: '^(\\*{3,}|#{3,}|\\={3,}|~{3,}|\\+{3,}|-{3,}|`{3,}|\\^{3,}|:{3,}|"{3,}|_{3,}|\'{3,})$',
|
||||
name: 'markup.heading',
|
||||
},
|
||||
'inline-markup': {
|
||||
patterns: [
|
||||
{
|
||||
include: '#escaped',
|
||||
},
|
||||
{
|
||||
include: '#ignore',
|
||||
},
|
||||
{
|
||||
include: '#ref',
|
||||
},
|
||||
{
|
||||
include: '#literal',
|
||||
},
|
||||
{
|
||||
include: '#monospaced',
|
||||
},
|
||||
{
|
||||
include: '#citation',
|
||||
},
|
||||
{
|
||||
include: '#bold',
|
||||
},
|
||||
{
|
||||
include: '#italic',
|
||||
},
|
||||
{
|
||||
include: '#list',
|
||||
},
|
||||
{
|
||||
include: '#macro',
|
||||
},
|
||||
{
|
||||
include: '#reference',
|
||||
},
|
||||
{
|
||||
include: '#footnote-ref',
|
||||
},
|
||||
],
|
||||
},
|
||||
ignore: {
|
||||
patterns: [
|
||||
{
|
||||
match: "'[`*]+'",
|
||||
},
|
||||
{
|
||||
match: '<[`*]+>',
|
||||
},
|
||||
{
|
||||
match: '{[`*]+}',
|
||||
},
|
||||
{
|
||||
match: '\\([`*]+\\)',
|
||||
},
|
||||
{
|
||||
match: '\\[[`*]+\\]',
|
||||
},
|
||||
{
|
||||
match: '"[`*]+"',
|
||||
},
|
||||
],
|
||||
},
|
||||
table: {
|
||||
begin: '^\\s*\\+[=+-]+\\+\\s*$',
|
||||
end: '^(?![+|])',
|
||||
beginCaptures: {
|
||||
'0': {
|
||||
name: 'keyword.control.table',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
match: '[=+|-]',
|
||||
name: 'keyword.control.table',
|
||||
},
|
||||
],
|
||||
},
|
||||
'simple-table': {
|
||||
match: '^[=\\s]+$',
|
||||
name: 'keyword.control.table',
|
||||
},
|
||||
ref: {
|
||||
begin: '(:ref:)`',
|
||||
end: '`|^\\s*$',
|
||||
name: 'entity.name.tag',
|
||||
beginCaptures: {
|
||||
'1': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
match: '<.*?>',
|
||||
name: 'markup.underline.link',
|
||||
},
|
||||
],
|
||||
},
|
||||
reference: {
|
||||
match: '[\\w-]*[a-zA-Z\\d-]__?\\b',
|
||||
name: 'entity.name.tag',
|
||||
},
|
||||
macro: {
|
||||
match: '\\|[^\\|]+\\|',
|
||||
name: 'entity.name.tag',
|
||||
},
|
||||
literal: {
|
||||
match: '(:\\S+:)(`.*?`\\\\?)',
|
||||
captures: {
|
||||
'1': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'2': {
|
||||
name: 'entity.name.tag',
|
||||
},
|
||||
},
|
||||
},
|
||||
monospaced: {
|
||||
begin: '(?<=[\\s"\'(\\[{<]|^)``[^\\s`]',
|
||||
end: '``|^\\s*$',
|
||||
name: 'string.interpolated',
|
||||
},
|
||||
citation: {
|
||||
begin: '(?<=[\\s"\'(\\[{<]|^)`[^\\s`]',
|
||||
end: '`_{,2}|^\\s*$',
|
||||
name: 'entity.name.tag',
|
||||
applyEndPatternLast: false,
|
||||
},
|
||||
bold: {
|
||||
begin: '(?<=[\\s"\'(\\[{<]|^)\\*{2}[^\\s*]',
|
||||
end: '\\*{2}|^\\s*$',
|
||||
name: 'markup.bold',
|
||||
},
|
||||
italic: {
|
||||
begin: '(?<=[\\s"\'(\\[{<]|^)\\*[^\\s*]',
|
||||
end: '\\*|^\\s*$',
|
||||
name: 'markup.italic',
|
||||
},
|
||||
escaped: {
|
||||
match: '\\\\.',
|
||||
name: 'constant.character.escape',
|
||||
},
|
||||
list: {
|
||||
match: '^\\s*(\\d+\\.|\\* -|[a-zA-Z#]\\.|[iIvVxXmMcC]+\\.|\\(\\d+\\)|\\d+\\)|[*+-])\\s+',
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'line-block': {
|
||||
match: '^\\|\\s+',
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'raw-html': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+raw\\s*::)\\s+(html)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'3': {
|
||||
name: 'variable.parameter.html',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: 'text.html.derivative',
|
||||
},
|
||||
],
|
||||
},
|
||||
anchor: {
|
||||
match: '^\\.{2}\\s+(_[^:]+:)\\s*',
|
||||
name: 'entity.name.tag.anchor',
|
||||
},
|
||||
'replace-include': {
|
||||
match: '^\\s*(\\.{2})\\s+(\\|[^\\|]+\\|)\\s+(replace::)',
|
||||
captures: {
|
||||
'1': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'2': {
|
||||
name: 'entity.name.tag',
|
||||
},
|
||||
'3': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
},
|
||||
},
|
||||
footnote: {
|
||||
match: '^\\s*\\.{2}\\s+\\[(?:[\\w\\.-]+|[#*]|#\\w+)\\]\\s+',
|
||||
name: 'entity.name.tag',
|
||||
},
|
||||
'footnote-ref': {
|
||||
match: '\\[(?:[\\w\\.-]+|[#*])\\]_',
|
||||
name: 'entity.name.tag',
|
||||
},
|
||||
substitution: {
|
||||
match: '^\\.{2}\\s*\\|([^|]+)\\|',
|
||||
name: 'entity.name.tag',
|
||||
},
|
||||
'options-list': {
|
||||
match: '^((?:-\\w|--[\\w-]+|/\\w+)(?:,? ?[\\w-]+)*)(?: |\\t|$)',
|
||||
name: 'variable.parameter',
|
||||
},
|
||||
blocks: {
|
||||
patterns: [
|
||||
{
|
||||
include: '#domains',
|
||||
},
|
||||
{
|
||||
include: '#doctest',
|
||||
},
|
||||
{
|
||||
include: '#code-block-cpp',
|
||||
},
|
||||
{
|
||||
include: '#code-block-py',
|
||||
},
|
||||
{
|
||||
include: '#code-block-console',
|
||||
},
|
||||
{
|
||||
include: '#code-block-javascript',
|
||||
},
|
||||
{
|
||||
include: '#code-block-yaml',
|
||||
},
|
||||
{
|
||||
include: '#code-block-cmake',
|
||||
},
|
||||
{
|
||||
include: '#code-block-kconfig',
|
||||
},
|
||||
{
|
||||
include: '#code-block-ruby',
|
||||
},
|
||||
{
|
||||
include: '#code-block-dts',
|
||||
},
|
||||
{
|
||||
include: '#code-block',
|
||||
},
|
||||
{
|
||||
include: '#doctest-block',
|
||||
},
|
||||
{
|
||||
include: '#raw-html',
|
||||
},
|
||||
{
|
||||
include: '#block',
|
||||
},
|
||||
{
|
||||
include: '#literal-block',
|
||||
},
|
||||
{
|
||||
include: '#block-comment',
|
||||
},
|
||||
],
|
||||
},
|
||||
'block-comment': {
|
||||
begin: '^(\\s*)\\.{2}',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
name: 'comment.block',
|
||||
},
|
||||
'literal-block': {
|
||||
begin: '^(\\s*)(.*)(::)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
patterns: [
|
||||
{
|
||||
include: '#inline-markup',
|
||||
},
|
||||
],
|
||||
},
|
||||
'3': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
},
|
||||
},
|
||||
block: {
|
||||
begin: '^(\\s*)(\\.{2}\\s+\\S+::)(.*)',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'3': {
|
||||
name: 'variable',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: '#body',
|
||||
},
|
||||
],
|
||||
},
|
||||
'block-param': {
|
||||
patterns: [
|
||||
{
|
||||
match: '(:param\\s+(.+?):)(?:\\s|$)',
|
||||
captures: {
|
||||
'1': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'2': {
|
||||
name: 'variable.parameter',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
match: '(:.+?:)(?:$|\\s+(.*))',
|
||||
captures: {
|
||||
'1': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'2': {
|
||||
patterns: [
|
||||
{
|
||||
match: '\\b(0x[a-fA-F\\d]+|\\d+)\\b',
|
||||
name: 'constant.numeric',
|
||||
},
|
||||
{
|
||||
include: '#inline-markup',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
domains: {
|
||||
patterns: [
|
||||
{
|
||||
include: '#domain-cpp',
|
||||
},
|
||||
{
|
||||
include: '#domain-py',
|
||||
},
|
||||
{
|
||||
include: '#domain-auto',
|
||||
},
|
||||
{
|
||||
include: '#domain-js',
|
||||
},
|
||||
],
|
||||
},
|
||||
'domain-cpp': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+(?:cpp|c):(?:class|struct|function|member|var|type|enum|enum-struct|enum-class|enumerator|union|concept)::)\\s*(?:(@\\w+)|(.*))',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'3': {
|
||||
name: 'entity.name.tag',
|
||||
},
|
||||
'4': {
|
||||
patterns: [
|
||||
{
|
||||
include: 'source.cpp',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: '#body',
|
||||
},
|
||||
],
|
||||
},
|
||||
'domain-py': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+py:(?:module|function|data|exception|class|attribute|property|method|staticmethod|classmethod|decorator|decoratormethod)::)\\s*(.*)',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'3': {
|
||||
patterns: [
|
||||
{
|
||||
include: 'source.python',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: '#body',
|
||||
},
|
||||
],
|
||||
},
|
||||
'domain-auto': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+auto(?:class|module|exception|function|decorator|data|method|attribute|property)::)\\s*(.*)',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control.py',
|
||||
},
|
||||
'3': {
|
||||
patterns: [
|
||||
{
|
||||
include: 'source.python',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: '#body',
|
||||
},
|
||||
],
|
||||
},
|
||||
'domain-js': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+js:\\w+::)\\s*(.*)',
|
||||
end: '^(?!\\1[ \\t]|$)',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'3': {
|
||||
patterns: [
|
||||
{
|
||||
include: 'source.js',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: '#body',
|
||||
},
|
||||
],
|
||||
},
|
||||
doctest: {
|
||||
begin: '^(>>>)\\s*(.*)',
|
||||
end: '^\\s*$',
|
||||
beginCaptures: {
|
||||
'1': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'2': {
|
||||
patterns: [
|
||||
{
|
||||
include: 'source.python',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
'code-block-cpp': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+(code|code-block)::)\\s*(c|c\\+\\+|cpp|C|C\\+\\+|CPP|Cpp)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'4': {
|
||||
name: 'variable.parameter.codeblock.cpp',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: 'source.cpp',
|
||||
},
|
||||
],
|
||||
},
|
||||
'code-block-console': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+(code|code-block)::)\\s*(console|shell|bash)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'4': {
|
||||
name: 'variable.parameter.codeblock.console',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: 'source.shell',
|
||||
},
|
||||
],
|
||||
},
|
||||
'code-block-py': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+(code|code-block)::)\\s*(python)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'4': {
|
||||
name: 'variable.parameter.codeblock.py',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: 'source.python',
|
||||
},
|
||||
],
|
||||
},
|
||||
'code-block-javascript': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+(code|code-block)::)\\s*(javascript)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'4': {
|
||||
name: 'variable.parameter.codeblock.js',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: 'source.js',
|
||||
},
|
||||
],
|
||||
},
|
||||
'code-block-yaml': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+(code|code-block)::)\\s*(ya?ml)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'4': {
|
||||
name: 'variable.parameter.codeblock.yaml',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: 'source.yaml',
|
||||
},
|
||||
],
|
||||
},
|
||||
'code-block-cmake': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+(code|code-block)::)\\s*(cmake)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'4': {
|
||||
name: 'variable.parameter.codeblock.cmake',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: 'source.cmake',
|
||||
},
|
||||
],
|
||||
},
|
||||
'code-block-kconfig': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+(code|code-block)::)\\s*([kK]config)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'4': {
|
||||
name: 'variable.parameter.codeblock.kconfig',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: 'source.kconfig',
|
||||
},
|
||||
],
|
||||
},
|
||||
'code-block-ruby': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+(code|code-block)::)\\s*(ruby)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'4': {
|
||||
name: 'variable.parameter.codeblock.ruby',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: 'source.ruby',
|
||||
},
|
||||
],
|
||||
},
|
||||
'code-block-dts': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+(code|code-block)::)\\s*(dts|DTS|devicetree)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
'4': {
|
||||
name: 'variable.parameter.codeblock.dts',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: 'source.dts',
|
||||
},
|
||||
],
|
||||
},
|
||||
'code-block': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+(code|code-block)::)',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
],
|
||||
},
|
||||
'doctest-block': {
|
||||
begin: '^(\\s*)(\\.{2}\\s+doctest::)\\s*$',
|
||||
while: '^\\1(?=\\s)|^\\s*$',
|
||||
beginCaptures: {
|
||||
'2': {
|
||||
name: 'keyword.control',
|
||||
},
|
||||
},
|
||||
patterns: [
|
||||
{
|
||||
include: '#block-param',
|
||||
},
|
||||
{
|
||||
include: 'source.python',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
+4671
File diff suppressed because it is too large
Load Diff
+5927
File diff suppressed because one or more lines are too long
@@ -0,0 +1,343 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const abyss: ThemeRegistrationAny = {
|
||||
type: 'dark',
|
||||
colors: {
|
||||
'activityBar.background': '#051336',
|
||||
'badge.background': '#0063a5',
|
||||
'button.background': '#2b3c5d',
|
||||
'debugExceptionWidget.background': '#051336',
|
||||
'debugExceptionWidget.border': '#ab395b',
|
||||
'debugToolBar.background': '#051336',
|
||||
'diffEditor.insertedTextBackground': '#31958a55',
|
||||
'diffEditor.removedTextBackground': '#892f4688',
|
||||
'dropdown.background': '#181f2f',
|
||||
'editor.background': '#000c18',
|
||||
'editor.findMatchHighlightBackground': '#eeeeee44',
|
||||
'editor.foreground': '#6688cc',
|
||||
'editor.lineHighlightBackground': '#082050',
|
||||
'editor.selectionBackground': '#770811',
|
||||
'editorCursor.foreground': '#ddbb88',
|
||||
'editorGroup.border': '#2b2b4a',
|
||||
'editorGroup.dropBackground': '#25375daa',
|
||||
'editorGroupHeader.tabsBackground': '#1c1c2a',
|
||||
'editorHoverWidget.background': '#000c38',
|
||||
'editorHoverWidget.border': '#004c18',
|
||||
'editorIndentGuide.activeBackground': '#204972',
|
||||
'editorIndentGuide.background': '#002952',
|
||||
'editorLineNumber.activeForeground': '#80a2c2',
|
||||
'editorLineNumber.foreground': '#406385',
|
||||
'editorLink.activeForeground': '#0063a5',
|
||||
'editorMarkerNavigation.background': '#060621',
|
||||
'editorMarkerNavigationError.background': '#ab395b',
|
||||
'editorMarkerNavigationWarning.background': '#5b7e7a',
|
||||
'editorWhitespace.foreground': '#103050',
|
||||
'editorWidget.background': '#262641',
|
||||
'extensionButton.prominentBackground': '#5f8b3b',
|
||||
'extensionButton.prominentHoverBackground': '#5f8b3bbb',
|
||||
focusBorder: '#596f99',
|
||||
'input.background': '#181f2f',
|
||||
'inputOption.activeBorder': '#1d4a87',
|
||||
'inputValidation.errorBackground': '#a22d44',
|
||||
'inputValidation.errorBorder': '#ab395b',
|
||||
'inputValidation.infoBackground': '#051336',
|
||||
'inputValidation.infoBorder': '#384078',
|
||||
'inputValidation.warningBackground': '#5b7e7a',
|
||||
'inputValidation.warningBorder': '#5b7e7a',
|
||||
'list.activeSelectionBackground': '#08286b',
|
||||
'list.dropBackground': '#041d52',
|
||||
'list.highlightForeground': '#0063a5',
|
||||
'list.hoverBackground': '#061940',
|
||||
'list.inactiveSelectionBackground': '#152037',
|
||||
'minimap.selectionHighlight': '#750000',
|
||||
'panel.border': '#2b2b4a',
|
||||
'peekView.border': '#2b2b4a',
|
||||
'peekViewEditor.background': '#10192c',
|
||||
'peekViewEditor.matchHighlightBackground': '#eeeeee33',
|
||||
'peekViewResult.background': '#060621',
|
||||
'peekViewResult.matchHighlightBackground': '#eeeeee44',
|
||||
'peekViewTitle.background': '#10192c',
|
||||
'pickerGroup.border': '#596f99',
|
||||
'pickerGroup.foreground': '#596f99',
|
||||
'ports.iconRunningProcessForeground': '#80a2c2',
|
||||
'progressBar.background': '#0063a5',
|
||||
'quickInputList.focusBackground': '#08286b',
|
||||
'scrollbar.shadow': '#515e91aa',
|
||||
'scrollbarSlider.activeBackground': '#3b3f5188',
|
||||
'scrollbarSlider.background': '#1f2230aa',
|
||||
'scrollbarSlider.hoverBackground': '#3b3f5188',
|
||||
'sideBar.background': '#060621',
|
||||
'sideBarSectionHeader.background': '#10192c',
|
||||
'statusBar.background': '#10192c',
|
||||
'statusBar.debuggingBackground': '#10192c',
|
||||
'statusBar.noFolderBackground': '#10192c',
|
||||
'statusBarItem.prominentBackground': '#0063a5',
|
||||
'statusBarItem.prominentHoverBackground': '#0063a5dd',
|
||||
'statusBarItem.remoteBackground': '#0063a5',
|
||||
'tab.border': '#2b2b4a',
|
||||
'tab.inactiveBackground': '#10192c',
|
||||
'tab.lastPinnedBorder': '#2b3c5d',
|
||||
'terminal.ansiBlack': '#111111',
|
||||
'terminal.ansiBlue': '#bbdaff',
|
||||
'terminal.ansiBrightBlack': '#333333',
|
||||
'terminal.ansiBrightBlue': '#80baff',
|
||||
'terminal.ansiBrightCyan': '#78ffff',
|
||||
'terminal.ansiBrightGreen': '#b8f171',
|
||||
'terminal.ansiBrightMagenta': '#d778ff',
|
||||
'terminal.ansiBrightRed': '#ff7882',
|
||||
'terminal.ansiBrightWhite': '#ffffff',
|
||||
'terminal.ansiBrightYellow': '#ffe580',
|
||||
'terminal.ansiCyan': '#99ffff',
|
||||
'terminal.ansiGreen': '#d1f1a9',
|
||||
'terminal.ansiMagenta': '#ebbbff',
|
||||
'terminal.ansiRed': '#ff9da4',
|
||||
'terminal.ansiWhite': '#cccccc',
|
||||
'terminal.ansiYellow': '#ffeead',
|
||||
'titleBar.activeBackground': '#10192c',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: ['meta.embedded', 'source.groovy.embedded', 'string meta.image.inline.markdown'],
|
||||
settings: {
|
||||
foreground: '#6688CC',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'comment',
|
||||
settings: {
|
||||
foreground: '#384887',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string',
|
||||
settings: {
|
||||
foreground: '#22AA44',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.numeric',
|
||||
settings: {
|
||||
foreground: '#F280D0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.language',
|
||||
settings: {
|
||||
foreground: '#F280D0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['constant.character', 'constant.other'],
|
||||
settings: {
|
||||
foreground: '#F280D0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable',
|
||||
settings: {
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword',
|
||||
settings: {
|
||||
foreground: '#225588',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage',
|
||||
settings: {
|
||||
foreground: '#225588',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage.type',
|
||||
settings: {
|
||||
foreground: '#9966B8',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['entity.name.class', 'entity.name.type', 'entity.name.namespace', 'entity.name.scope-resolution'],
|
||||
settings: {
|
||||
foreground: '#FFEEBB',
|
||||
fontStyle: 'underline',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.inherited-class',
|
||||
settings: {
|
||||
foreground: '#DDBB88',
|
||||
fontStyle: 'italic underline',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.function',
|
||||
settings: {
|
||||
foreground: '#DDBB88',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.parameter',
|
||||
settings: {
|
||||
foreground: '#2277FF',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#225588',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.attribute-name',
|
||||
settings: {
|
||||
foreground: '#DDBB88',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.function',
|
||||
settings: {
|
||||
foreground: '#9966B8',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.constant',
|
||||
settings: {
|
||||
foreground: '#9966B8',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['support.type', 'support.class'],
|
||||
settings: {
|
||||
foreground: '#9966B8',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.other.variable',
|
||||
settings: {
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#A22D44',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid.deprecated',
|
||||
settings: {
|
||||
foreground: '#A22D44',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.diff', 'meta.diff.header'],
|
||||
settings: {
|
||||
foreground: '#E0EDDD',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted',
|
||||
settings: {
|
||||
foreground: '#DC322F',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed',
|
||||
settings: {
|
||||
foreground: '#CB4B16',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted',
|
||||
settings: {
|
||||
foreground: '#219186',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.quote',
|
||||
settings: {
|
||||
foreground: '#22AA44',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['markup.bold', 'markup.italic'],
|
||||
settings: {
|
||||
foreground: '#22AA44',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.italic',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inline.raw',
|
||||
settings: {
|
||||
foreground: '#9966B8',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['markup.heading', 'markup.heading.setext'],
|
||||
settings: {
|
||||
foreground: '#6688CC',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#6796E6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#F44747',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#B267E6',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,462 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const darkHC: ThemeRegistrationAny = {
|
||||
$schema: 'vscode://schemas/color-theme',
|
||||
type: 'dark',
|
||||
colors: {
|
||||
'actionBar.toggledBackground': '#383a49',
|
||||
'editor.background': '#000000',
|
||||
'editor.foreground': '#ffffff',
|
||||
'editor.selectionBackground': '#ffffff',
|
||||
'editorIndentGuide.activeBackground1': '#ffffff',
|
||||
'editorIndentGuide.background1': '#ffffff',
|
||||
'editorWhitespace.foreground': '#7c7c7c',
|
||||
'ports.iconRunningProcessForeground': '#ffffff',
|
||||
'selection.background': '#008000',
|
||||
'sideBarTitle.foreground': '#ffffff',
|
||||
'statusBarItem.remoteBackground': '#00000000',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: [
|
||||
'meta.embedded',
|
||||
'source.groovy.embedded',
|
||||
'string meta.image.inline.markdown',
|
||||
'variable.legacy.builtin.python',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'emphasis',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'strong',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.diff.header',
|
||||
settings: {
|
||||
foreground: '#000080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'comment',
|
||||
settings: {
|
||||
foreground: '#7CA668',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.language',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'constant.numeric',
|
||||
'constant.other.color.rgb-value',
|
||||
'constant.other.rgb-value',
|
||||
'support.constant.color',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.regexp',
|
||||
settings: {
|
||||
foreground: '#B46695',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.character',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag.css',
|
||||
settings: {
|
||||
foreground: '#D7BA7D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.attribute-name',
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'entity.other.attribute-name.class.css',
|
||||
'entity.other.attribute-name.class.mixin.css',
|
||||
'entity.other.attribute-name.id.css',
|
||||
'entity.other.attribute-name.parent-selector.css',
|
||||
'entity.other.attribute-name.pseudo-class.css',
|
||||
'entity.other.attribute-name.pseudo-element.css',
|
||||
'source.css.less entity.other.attribute-name.id',
|
||||
'entity.other.attribute-name.scss',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#D7BA7D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#F44747',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.underline',
|
||||
settings: {
|
||||
fontStyle: 'underline',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.heading',
|
||||
settings: {
|
||||
foreground: '#6796E6',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.italic',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.definition.tag'],
|
||||
settings: {
|
||||
foreground: '#808080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.string',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.numeric',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.structure.dictionary.key.python',
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage.type',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage.modifier',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.tag',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.value',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.regexp',
|
||||
settings: {
|
||||
foreground: '#D16969',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.template-expression.begin',
|
||||
'punctuation.definition.template-expression.end',
|
||||
'punctuation.section.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.template.expression'],
|
||||
settings: {
|
||||
foreground: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.type.vendored.property-name',
|
||||
'support.type.property-name',
|
||||
'variable.css',
|
||||
'variable.scss',
|
||||
'variable.other.less',
|
||||
'source.coffee.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.control',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator',
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword.operator.new',
|
||||
'keyword.operator.expression',
|
||||
'keyword.operator.cast',
|
||||
'keyword.operator.sizeof',
|
||||
'keyword.operator.logical.python',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.other.unit',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.function.git-rebase',
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.sha.git-rebase',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.modifier.import.java', 'variable.language.wildcard.java', 'storage.modifier.package.java'],
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.language.this',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'entity.name.function',
|
||||
'support.function',
|
||||
'support.constant.handlebars',
|
||||
'source.powershell variable.other.member',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#DCDCAA',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.class',
|
||||
'support.type',
|
||||
'entity.name.type',
|
||||
'entity.name.namespace',
|
||||
'entity.name.scope-resolution',
|
||||
'entity.name.class',
|
||||
'storage.type.cs',
|
||||
'storage.type.generic.cs',
|
||||
'storage.type.modifier.cs',
|
||||
'storage.type.variable.cs',
|
||||
'storage.type.annotation.java',
|
||||
'storage.type.generic.java',
|
||||
'storage.type.java',
|
||||
'storage.type.object.array.java',
|
||||
'storage.type.primitive.array.java',
|
||||
'storage.type.primitive.java',
|
||||
'storage.type.token.java',
|
||||
'storage.type.groovy',
|
||||
'storage.type.annotation.groovy',
|
||||
'storage.type.parameters.groovy',
|
||||
'storage.type.generic.groovy',
|
||||
'storage.type.object.array.groovy',
|
||||
'storage.type.primitive.array.groovy',
|
||||
'storage.type.primitive.groovy',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#4EC9B0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'meta.type.cast.expr',
|
||||
'meta.type.new.expr',
|
||||
'support.constant.math',
|
||||
'support.constant.dom',
|
||||
'support.constant.json',
|
||||
'entity.other.inherited-class',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#4EC9B0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword.control',
|
||||
'source.cpp keyword.operator.new',
|
||||
'source.cpp keyword.operator.delete',
|
||||
'keyword.other.using',
|
||||
'keyword.other.directive.using',
|
||||
'keyword.other.operator',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#C586C0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['variable', 'meta.definition.variable.name', 'support.variable'],
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.object-literal.key'],
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.constant.property-value',
|
||||
'support.constant.font-name',
|
||||
'support.constant.media-type',
|
||||
'support.constant.media',
|
||||
'constant.other.color.rgb-value',
|
||||
'constant.other.rgb-value',
|
||||
'support.constant.color',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.resultLinePrefix.contextLinePrefix.search',
|
||||
settings: {
|
||||
foreground: '#CBEDCB',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#6796E6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#008000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#FF0000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#B267E6',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,692 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const darkModern: ThemeRegistrationAny = {
|
||||
$schema: 'vscode://schemas/color-theme',
|
||||
type: 'dark',
|
||||
colors: {
|
||||
'actionBar.toggledBackground': '#383a49',
|
||||
'activityBar.activeBorder': '#0078d4',
|
||||
'activityBar.background': '#181818',
|
||||
'activityBar.border': '#2b2b2b',
|
||||
'activityBar.foreground': '#d7d7d7',
|
||||
'activityBar.inactiveForeground': '#868686',
|
||||
'activityBarBadge.background': '#0078d4',
|
||||
'activityBarBadge.foreground': '#ffffff',
|
||||
'badge.background': '#616161',
|
||||
'badge.foreground': '#f8f8f8',
|
||||
'button.background': '#0078d4',
|
||||
'button.border': '#ffffff12',
|
||||
'button.foreground': '#ffffff',
|
||||
'button.hoverBackground': '#026ec1',
|
||||
'button.secondaryBackground': '#313131',
|
||||
'button.secondaryForeground': '#cccccc',
|
||||
'button.secondaryHoverBackground': '#3c3c3c',
|
||||
'chat.slashCommandBackground': '#34414b',
|
||||
'chat.slashCommandForeground': '#40a6ff',
|
||||
'checkbox.background': '#313131',
|
||||
'checkbox.border': '#3c3c3c',
|
||||
'debugToolBar.background': '#181818',
|
||||
descriptionForeground: '#9d9d9d',
|
||||
'dropdown.background': '#313131',
|
||||
'dropdown.border': '#3c3c3c',
|
||||
'dropdown.foreground': '#cccccc',
|
||||
'dropdown.listBackground': '#1f1f1f',
|
||||
'editor.background': '#1f1f1f',
|
||||
'editor.findMatchBackground': '#9e6a03',
|
||||
'editor.foreground': '#cccccc',
|
||||
'editor.inactiveSelectionBackground': '#3a3d41',
|
||||
'editor.selectionHighlightBackground': '#add6ff26',
|
||||
'editorGroup.border': '#ffffff17',
|
||||
'editorGroupHeader.tabsBackground': '#181818',
|
||||
'editorGroupHeader.tabsBorder': '#2b2b2b',
|
||||
'editorGutter.addedBackground': '#2ea043',
|
||||
'editorGutter.deletedBackground': '#f85149',
|
||||
'editorGutter.modifiedBackground': '#0078d4',
|
||||
'editorIndentGuide.activeBackground1': '#707070',
|
||||
'editorIndentGuide.background1': '#404040',
|
||||
'editorLineNumber.activeForeground': '#cccccc',
|
||||
'editorLineNumber.foreground': '#6e7681',
|
||||
'editorOverviewRuler.border': '#010409',
|
||||
'editorWidget.background': '#202020',
|
||||
errorForeground: '#f85149',
|
||||
focusBorder: '#0078d4',
|
||||
foreground: '#cccccc',
|
||||
'icon.foreground': '#cccccc',
|
||||
'input.background': '#313131',
|
||||
'input.border': '#3c3c3c',
|
||||
'input.foreground': '#cccccc',
|
||||
'input.placeholderForeground': '#818181',
|
||||
'inputOption.activeBackground': '#2489db82',
|
||||
'inputOption.activeBorder': '#2488db',
|
||||
'keybindingLabel.foreground': '#cccccc',
|
||||
'list.activeSelectionIconForeground': '#ffffff',
|
||||
'list.dropBackground': '#383b3d',
|
||||
'menu.background': '#1f1f1f',
|
||||
'menu.border': '#454545',
|
||||
'menu.foreground': '#cccccc',
|
||||
'menu.separatorBackground': '#454545',
|
||||
'notificationCenterHeader.background': '#1f1f1f',
|
||||
'notificationCenterHeader.foreground': '#cccccc',
|
||||
'notifications.background': '#1f1f1f',
|
||||
'notifications.border': '#2b2b2b',
|
||||
'notifications.foreground': '#cccccc',
|
||||
'panel.background': '#181818',
|
||||
'panel.border': '#2b2b2b',
|
||||
'panelInput.border': '#2b2b2b',
|
||||
'panelTitle.activeBorder': '#0078d4',
|
||||
'panelTitle.activeForeground': '#cccccc',
|
||||
'panelTitle.inactiveForeground': '#9d9d9d',
|
||||
'peekViewEditor.background': '#1f1f1f',
|
||||
'peekViewEditor.matchHighlightBackground': '#bb800966',
|
||||
'peekViewResult.background': '#1f1f1f',
|
||||
'peekViewResult.matchHighlightBackground': '#bb800966',
|
||||
'pickerGroup.border': '#3c3c3c',
|
||||
'ports.iconRunningProcessForeground': '#369432',
|
||||
'progressBar.background': '#0078d4',
|
||||
'quickInput.background': '#222222',
|
||||
'quickInput.foreground': '#cccccc',
|
||||
'settings.dropdownBackground': '#313131',
|
||||
'settings.dropdownBorder': '#3c3c3c',
|
||||
'settings.headerForeground': '#ffffff',
|
||||
'settings.modifiedItemIndicator': '#bb800966',
|
||||
'sideBar.background': '#181818',
|
||||
'sideBar.border': '#2b2b2b',
|
||||
'sideBar.foreground': '#cccccc',
|
||||
'sideBarSectionHeader.background': '#181818',
|
||||
'sideBarSectionHeader.border': '#2b2b2b',
|
||||
'sideBarSectionHeader.foreground': '#cccccc',
|
||||
'sideBarTitle.foreground': '#cccccc',
|
||||
'statusBar.background': '#181818',
|
||||
'statusBar.border': '#2b2b2b',
|
||||
'statusBar.debuggingBackground': '#0078d4',
|
||||
'statusBar.debuggingForeground': '#ffffff',
|
||||
'statusBar.focusBorder': '#0078d4',
|
||||
'statusBar.foreground': '#cccccc',
|
||||
'statusBar.noFolderBackground': '#1f1f1f',
|
||||
'statusBarItem.focusBorder': '#0078d4',
|
||||
'statusBarItem.prominentBackground': '#6e768166',
|
||||
'statusBarItem.remoteBackground': '#0078d4',
|
||||
'statusBarItem.remoteForeground': '#ffffff',
|
||||
'tab.activeBackground': '#1f1f1f',
|
||||
'tab.activeBorder': '#1f1f1f',
|
||||
'tab.activeBorderTop': '#0078d4',
|
||||
'tab.activeForeground': '#ffffff',
|
||||
'tab.border': '#2b2b2b',
|
||||
'tab.hoverBackground': '#1f1f1f',
|
||||
'tab.inactiveBackground': '#181818',
|
||||
'tab.inactiveForeground': '#9d9d9d',
|
||||
'tab.lastPinnedBorder': '#cccccc33',
|
||||
'tab.unfocusedActiveBorder': '#1f1f1f',
|
||||
'tab.unfocusedActiveBorderTop': '#2b2b2b',
|
||||
'tab.unfocusedHoverBackground': '#1f1f1f',
|
||||
'terminal.foreground': '#cccccc',
|
||||
'terminal.inactiveSelectionBackground': '#3a3d41',
|
||||
'terminal.tab.activeBorder': '#0078d4',
|
||||
'textBlockQuote.background': '#2b2b2b',
|
||||
'textBlockQuote.border': '#616161',
|
||||
'textCodeBlock.background': '#2b2b2b',
|
||||
'textLink.activeForeground': '#4daafc',
|
||||
'textLink.foreground': '#4daafc',
|
||||
'textPreformat.background': '#3c3c3c',
|
||||
'textPreformat.foreground': '#d0d0d0',
|
||||
'textSeparator.foreground': '#21262d',
|
||||
'titleBar.activeBackground': '#181818',
|
||||
'titleBar.activeForeground': '#cccccc',
|
||||
'titleBar.border': '#2b2b2b',
|
||||
'titleBar.inactiveBackground': '#1f1f1f',
|
||||
'titleBar.inactiveForeground': '#9d9d9d',
|
||||
'welcomePage.progress.foreground': '#0078d4',
|
||||
'welcomePage.tileBackground': '#2b2b2b',
|
||||
'widget.border': '#313131',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: [
|
||||
'meta.embedded',
|
||||
'source.groovy.embedded',
|
||||
'string meta.image.inline.markdown',
|
||||
'variable.legacy.builtin.python',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'emphasis',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'strong',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'header',
|
||||
settings: {
|
||||
foreground: '#000080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'comment',
|
||||
settings: {
|
||||
foreground: '#6A9955',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.language',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'constant.numeric',
|
||||
'variable.other.enummember',
|
||||
'keyword.operator.plus.exponent',
|
||||
'keyword.operator.minus.exponent',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.regexp',
|
||||
settings: {
|
||||
foreground: '#646695',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag.css',
|
||||
settings: {
|
||||
foreground: '#D7BA7D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.attribute-name',
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'entity.other.attribute-name.class.css',
|
||||
'entity.other.attribute-name.class.mixin.css',
|
||||
'entity.other.attribute-name.id.css',
|
||||
'entity.other.attribute-name.parent-selector.css',
|
||||
'entity.other.attribute-name.pseudo-class.css',
|
||||
'entity.other.attribute-name.pseudo-element.css',
|
||||
'source.css.less entity.other.attribute-name.id',
|
||||
'entity.other.attribute-name.scss',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#D7BA7D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#F44747',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.underline',
|
||||
settings: {
|
||||
fontStyle: 'underline',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.heading',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.italic',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.definition.quote.begin.markdown',
|
||||
settings: {
|
||||
foreground: '#6A9955',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.definition.list.begin.markdown',
|
||||
settings: {
|
||||
foreground: '#6796E6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inline.raw',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.definition.tag',
|
||||
settings: {
|
||||
foreground: '#808080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.preprocessor', 'entity.name.function.preprocessor'],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.string',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.numeric',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.structure.dictionary.key.python',
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.diff.header',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage.type',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.modifier', 'keyword.operator.noexcept'],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['string', 'meta.embedded.assembly'],
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.tag',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.value',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.regexp',
|
||||
settings: {
|
||||
foreground: '#D16969',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.template-expression.begin',
|
||||
'punctuation.definition.template-expression.end',
|
||||
'punctuation.section.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.template.expression'],
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.type.vendored.property-name',
|
||||
'support.type.property-name',
|
||||
'variable.css',
|
||||
'variable.scss',
|
||||
'variable.other.less',
|
||||
'source.coffee.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.control',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator',
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword.operator.new',
|
||||
'keyword.operator.expression',
|
||||
'keyword.operator.cast',
|
||||
'keyword.operator.sizeof',
|
||||
'keyword.operator.alignof',
|
||||
'keyword.operator.typeid',
|
||||
'keyword.operator.alignas',
|
||||
'keyword.operator.instanceof',
|
||||
'keyword.operator.logical.python',
|
||||
'keyword.operator.wordlike',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.other.unit',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.section.embedded.begin.php', 'punctuation.section.embedded.end.php'],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.function.git-rebase',
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.sha.git-rebase',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.modifier.import.java', 'variable.language.wildcard.java', 'storage.modifier.package.java'],
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.language',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'entity.name.function',
|
||||
'support.function',
|
||||
'support.constant.handlebars',
|
||||
'source.powershell variable.other.member',
|
||||
'entity.name.operator.custom-literal',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#DCDCAA',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.class',
|
||||
'support.type',
|
||||
'entity.name.type',
|
||||
'entity.name.namespace',
|
||||
'entity.other.attribute',
|
||||
'entity.name.scope-resolution',
|
||||
'entity.name.class',
|
||||
'storage.type.numeric.go',
|
||||
'storage.type.byte.go',
|
||||
'storage.type.boolean.go',
|
||||
'storage.type.string.go',
|
||||
'storage.type.uintptr.go',
|
||||
'storage.type.error.go',
|
||||
'storage.type.rune.go',
|
||||
'storage.type.cs',
|
||||
'storage.type.generic.cs',
|
||||
'storage.type.modifier.cs',
|
||||
'storage.type.variable.cs',
|
||||
'storage.type.annotation.java',
|
||||
'storage.type.generic.java',
|
||||
'storage.type.java',
|
||||
'storage.type.object.array.java',
|
||||
'storage.type.primitive.array.java',
|
||||
'storage.type.primitive.java',
|
||||
'storage.type.token.java',
|
||||
'storage.type.groovy',
|
||||
'storage.type.annotation.groovy',
|
||||
'storage.type.parameters.groovy',
|
||||
'storage.type.generic.groovy',
|
||||
'storage.type.object.array.groovy',
|
||||
'storage.type.primitive.array.groovy',
|
||||
'storage.type.primitive.groovy',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#4EC9B0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'meta.type.cast.expr',
|
||||
'meta.type.new.expr',
|
||||
'support.constant.math',
|
||||
'support.constant.dom',
|
||||
'support.constant.json',
|
||||
'entity.other.inherited-class',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#4EC9B0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword.control',
|
||||
'source.cpp keyword.operator.new',
|
||||
'keyword.operator.delete',
|
||||
'keyword.other.using',
|
||||
'keyword.other.directive.using',
|
||||
'keyword.other.operator',
|
||||
'entity.name.operator',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#C586C0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'variable',
|
||||
'meta.definition.variable.name',
|
||||
'support.variable',
|
||||
'entity.name.variable',
|
||||
'constant.other.placeholder',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['variable.other.constant', 'variable.other.enummember'],
|
||||
settings: {
|
||||
foreground: '#4FC1FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.object-literal.key'],
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.constant.property-value',
|
||||
'support.constant.font-name',
|
||||
'support.constant.media-type',
|
||||
'support.constant.media',
|
||||
'constant.other.color.rgb-value',
|
||||
'constant.other.rgb-value',
|
||||
'support.constant.color',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.group.regexp',
|
||||
'punctuation.definition.group.assertion.regexp',
|
||||
'punctuation.definition.character-class.regexp',
|
||||
'punctuation.character.set.begin.regexp',
|
||||
'punctuation.character.set.end.regexp',
|
||||
'keyword.operator.negation.regexp',
|
||||
'support.other.parenthesis.regexp',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'constant.character.character-class.regexp',
|
||||
'constant.other.character-class.set.regexp',
|
||||
'constant.other.character-class.regexp',
|
||||
'constant.character.set.regexp',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#D16969',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['keyword.operator.or.regexp', 'keyword.control.anchor.regexp'],
|
||||
settings: {
|
||||
foreground: '#DCDCAA',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator.quantifier.regexp',
|
||||
settings: {
|
||||
foreground: '#D7BA7D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['constant.character', 'constant.other.option'],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.character.escape',
|
||||
settings: {
|
||||
foreground: '#D7BA7D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.label',
|
||||
settings: {
|
||||
foreground: '#C8C8C8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'ref.matchtext',
|
||||
settings: {
|
||||
foreground: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#6796E6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#F44747',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#B267E6',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
export { default as darkPlus } from 'shiki/themes/dark-plus.mjs';
|
||||
export { default as lightPlus } from 'shiki/themes/light-plus.mjs';
|
||||
export { default as monokai } from 'shiki/themes/monokai.mjs';
|
||||
export { default as solarizedDark } from 'shiki/themes/solarized-dark.mjs';
|
||||
export { default as solarizedLight } from 'shiki/themes/solarized-light.mjs';
|
||||
export { abyss } from './abyss';
|
||||
export { darkHC } from './dark-hc';
|
||||
export { darkModern } from './dark-modern';
|
||||
export { kimbieDark } from './kimbie-dark';
|
||||
export { lightHC } from './light-hc';
|
||||
export { lightModern } from './light-modern';
|
||||
export { monokaiDim } from './monokai-dim';
|
||||
export { quietLight } from './quiet-light';
|
||||
export { red } from './red';
|
||||
export { tomorrowNightBlue } from './tomorrow-night-blue';
|
||||
export { vsDark } from './vs-dark';
|
||||
export { vsLight } from './vs-light';
|
||||
@@ -0,0 +1,374 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const kimbieDark: ThemeRegistrationAny = {
|
||||
$schema: 'vscode://schemas/color-theme',
|
||||
type: 'dark',
|
||||
colors: {
|
||||
'activityBar.background': '#221a0f',
|
||||
'activityBar.foreground': '#d3af86',
|
||||
'badge.background': '#7f5d38',
|
||||
'button.background': '#6e583b',
|
||||
'dropdown.background': '#51412c',
|
||||
'editor.background': '#221a0f',
|
||||
'editor.foreground': '#d3af86',
|
||||
'editor.lineHighlightBackground': '#5e452b',
|
||||
'editor.selectionBackground': '#84613daa',
|
||||
'editorCursor.foreground': '#d3af86',
|
||||
'editorGroupHeader.tabsBackground': '#131510',
|
||||
'editorHoverWidget.background': '#221a14',
|
||||
'editorLineNumber.activeForeground': '#adadad',
|
||||
'editorWhitespace.foreground': '#a57a4c',
|
||||
'editorWidget.background': '#131510',
|
||||
focusBorder: '#a57a4c',
|
||||
'input.background': '#51412c',
|
||||
'inputOption.activeBorder': '#a57a4c',
|
||||
'inputValidation.errorBackground': '#5f0d0d',
|
||||
'inputValidation.errorBorder': '#9d2f23',
|
||||
'inputValidation.infoBackground': '#2b2a42',
|
||||
'inputValidation.infoBorder': '#1b60a5',
|
||||
'inputValidation.warningBackground': '#51412c',
|
||||
'list.activeSelectionBackground': '#7c5021',
|
||||
'list.highlightForeground': '#e3b583',
|
||||
'list.hoverBackground': '#7c502166',
|
||||
'list.inactiveSelectionBackground': '#645342',
|
||||
'menu.background': '#362712',
|
||||
'menu.foreground': '#cccccc',
|
||||
'minimap.selectionHighlight': '#84613daa',
|
||||
'peekView.border': '#5e452b',
|
||||
'peekViewEditor.background': '#221a14',
|
||||
'peekViewEditor.matchHighlightBackground': '#84613daa',
|
||||
'peekViewResult.background': '#362712',
|
||||
'peekViewTitle.background': '#362712',
|
||||
'pickerGroup.border': '#e3b583',
|
||||
'pickerGroup.foreground': '#e3b583',
|
||||
'ports.iconRunningProcessForeground': '#369432',
|
||||
'progressBar.background': '#7f5d38',
|
||||
'quickInputList.focusBackground': '#7c5021aa',
|
||||
'selection.background': '#84613daa',
|
||||
'sideBar.background': '#362712',
|
||||
'statusBar.background': '#423523',
|
||||
'statusBar.debuggingBackground': '#423523',
|
||||
'statusBar.noFolderBackground': '#423523',
|
||||
'statusBarItem.remoteBackground': '#6e583b',
|
||||
'tab.inactiveBackground': '#131510',
|
||||
'tab.lastPinnedBorder': '#51412c',
|
||||
'titleBar.activeBackground': '#423523',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: [
|
||||
'meta.embedded',
|
||||
'source.groovy.embedded',
|
||||
'string meta.image.inline.markdown',
|
||||
'variable.legacy.builtin.python',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#D3AF86',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.parameter.function',
|
||||
settings: {
|
||||
foreground: '#D3AF86',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['comment', 'punctuation.definition.comment'],
|
||||
settings: {
|
||||
foreground: '#A57A4C',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.string',
|
||||
'punctuation.definition.variable',
|
||||
'punctuation.definition.string',
|
||||
'punctuation.definition.parameters',
|
||||
'punctuation.definition.string',
|
||||
'punctuation.definition.array',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#D3AF86',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'none',
|
||||
settings: {
|
||||
foreground: '#D3AF86',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator',
|
||||
settings: {
|
||||
foreground: '#D3AF86',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword',
|
||||
'keyword.control',
|
||||
'keyword.operator.new.cpp',
|
||||
'keyword.operator.delete.cpp',
|
||||
'keyword.other.using',
|
||||
'keyword.other.directive.using',
|
||||
'keyword.other.operator',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#98676A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable',
|
||||
settings: {
|
||||
foreground: '#DC3958',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['entity.name.function', 'meta.require', 'support.function.any-method'],
|
||||
settings: {
|
||||
foreground: '#8AB1B0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.class',
|
||||
'entity.name.class',
|
||||
'entity.name.type',
|
||||
'entity.name.namespace',
|
||||
'entity.name.scope-resolution',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#F06431',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.other.special-method',
|
||||
settings: {
|
||||
foreground: '#8AB1B0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage',
|
||||
settings: {
|
||||
foreground: '#98676A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.function',
|
||||
settings: {
|
||||
foreground: '#7E602C',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['string', 'constant.other.symbol', 'entity.other.inherited-class'],
|
||||
settings: {
|
||||
foreground: '#889B4A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.numeric',
|
||||
settings: {
|
||||
foreground: '#F79A32',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'none',
|
||||
settings: {
|
||||
foreground: '#F79A32',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'none',
|
||||
settings: {
|
||||
foreground: '#F79A32',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant',
|
||||
settings: {
|
||||
foreground: '#F79A32',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#DC3958',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.attribute-name',
|
||||
settings: {
|
||||
foreground: '#F79A32',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['entity.other.attribute-name.id', 'punctuation.definition.entity'],
|
||||
settings: {
|
||||
foreground: '#8AB1B0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.selector',
|
||||
settings: {
|
||||
foreground: '#98676A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'none',
|
||||
settings: {
|
||||
foreground: '#F79A32',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['markup.heading', 'markup.heading.setext', 'punctuation.definition.heading', 'entity.name.section'],
|
||||
settings: {
|
||||
foreground: '#8AB1B0',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.other.unit',
|
||||
settings: {
|
||||
foreground: '#F79A32',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['markup.bold', 'punctuation.definition.bold'],
|
||||
settings: {
|
||||
foreground: '#F06431',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['markup.italic', 'punctuation.definition.italic'],
|
||||
settings: {
|
||||
foreground: '#98676A',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inline.raw',
|
||||
settings: {
|
||||
foreground: '#889B4A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.other.link',
|
||||
settings: {
|
||||
foreground: '#DC3958',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.link',
|
||||
settings: {
|
||||
foreground: '#F79A32',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.list',
|
||||
settings: {
|
||||
foreground: '#DC3958',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.quote',
|
||||
settings: {
|
||||
foreground: '#F79A32',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.separator',
|
||||
settings: {
|
||||
foreground: '#D3AF86',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted',
|
||||
settings: {
|
||||
foreground: '#889B4A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted',
|
||||
settings: {
|
||||
foreground: '#DC3958',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed',
|
||||
settings: {
|
||||
foreground: '#98676A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.other.color',
|
||||
settings: {
|
||||
foreground: '#7E602C',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.regexp',
|
||||
settings: {
|
||||
foreground: '#7E602C',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.character.escape',
|
||||
settings: {
|
||||
foreground: '#7E602C',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.section.embedded', 'variable.interpolation'],
|
||||
settings: {
|
||||
foreground: '#088649',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#DC3958',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'ref.matchtext',
|
||||
settings: {
|
||||
foreground: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#6796E6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#F44747',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#B267E6',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,572 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const lightHC: ThemeRegistrationAny = {
|
||||
$schema: 'vscode://schemas/color-theme',
|
||||
type: 'light',
|
||||
colors: {
|
||||
'actionBar.toggledBackground': '#dddddd',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: ['meta.embedded', 'source.groovy.embedded', 'variable.legacy.builtin.python'],
|
||||
settings: {
|
||||
foreground: '#292929',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'emphasis',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'strong',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.diff.header',
|
||||
settings: {
|
||||
foreground: '#062F4A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'comment',
|
||||
settings: {
|
||||
foreground: '#515151',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.language',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'constant.numeric',
|
||||
'variable.other.enummember',
|
||||
'keyword.operator.plus.exponent',
|
||||
'keyword.operator.minus.exponent',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#096D48',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.regexp',
|
||||
settings: {
|
||||
foreground: '#811F3F',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.selector',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.attribute-name',
|
||||
settings: {
|
||||
foreground: '#264F78',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'entity.other.attribute-name.class.css',
|
||||
'entity.other.attribute-name.class.mixin.css',
|
||||
'entity.other.attribute-name.id.css',
|
||||
'entity.other.attribute-name.parent-selector.css',
|
||||
'entity.other.attribute-name.pseudo-class.css',
|
||||
'entity.other.attribute-name.pseudo-element.css',
|
||||
'source.css.less entity.other.attribute-name.id',
|
||||
'entity.other.attribute-name.scss',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#B5200D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.underline',
|
||||
settings: {
|
||||
fontStyle: 'underline',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold',
|
||||
settings: {
|
||||
foreground: '#000080',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.heading',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.italic',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted',
|
||||
settings: {
|
||||
foreground: '#096D48',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted',
|
||||
settings: {
|
||||
foreground: '#5A5A5A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed',
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.definition.quote.begin.markdown', 'punctuation.definition.list.begin.markdown'],
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inline.raw',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.definition.tag',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.preprocessor', 'entity.name.function.preprocessor'],
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.string',
|
||||
settings: {
|
||||
foreground: '#B5200D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.numeric',
|
||||
settings: {
|
||||
foreground: '#096D48',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.structure.dictionary.key.python',
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage.type',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.modifier', 'keyword.operator.noexcept'],
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['string', 'meta.embedded.assembly'],
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'string.comment.buffered.block.pug',
|
||||
'string.quoted.pug',
|
||||
'string.interpolated.pug',
|
||||
'string.unquoted.plain.in.yaml',
|
||||
'string.unquoted.plain.out.yaml',
|
||||
'string.unquoted.block.yaml',
|
||||
'string.quoted.single.yaml',
|
||||
'string.quoted.double.xml',
|
||||
'string.quoted.single.xml',
|
||||
'string.unquoted.cdata.xml',
|
||||
'string.quoted.double.html',
|
||||
'string.quoted.single.html',
|
||||
'string.unquoted.html',
|
||||
'string.quoted.single.handlebars',
|
||||
'string.quoted.double.handlebars',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.regexp',
|
||||
settings: {
|
||||
foreground: '#811F3F',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.template-expression.begin',
|
||||
'punctuation.definition.template-expression.end',
|
||||
'punctuation.section.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.template.expression'],
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.constant.property-value',
|
||||
'support.constant.font-name',
|
||||
'support.constant.media-type',
|
||||
'support.constant.media',
|
||||
'constant.other.color.rgb-value',
|
||||
'constant.other.rgb-value',
|
||||
'support.constant.color',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.type.vendored.property-name',
|
||||
'support.type.property-name',
|
||||
'variable.css',
|
||||
'variable.scss',
|
||||
'variable.other.less',
|
||||
'source.coffee.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#264F78',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['support.type.property-name.json'],
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.control',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword.operator.new',
|
||||
'keyword.operator.expression',
|
||||
'keyword.operator.cast',
|
||||
'keyword.operator.sizeof',
|
||||
'keyword.operator.alignof',
|
||||
'keyword.operator.typeid',
|
||||
'keyword.operator.alignas',
|
||||
'keyword.operator.instanceof',
|
||||
'keyword.operator.logical.python',
|
||||
'keyword.operator.wordlike',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.other.unit',
|
||||
settings: {
|
||||
foreground: '#096D48',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.section.embedded.begin.php', 'punctuation.section.embedded.end.php'],
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.function.git-rebase',
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.sha.git-rebase',
|
||||
settings: {
|
||||
foreground: '#096D48',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.modifier.import.java', 'variable.language.wildcard.java', 'storage.modifier.package.java'],
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.language',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'entity.name.function',
|
||||
'support.function',
|
||||
'support.constant.handlebars',
|
||||
'source.powershell variable.other.member',
|
||||
'entity.name.operator.custom-literal',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#5E2CBC',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.class',
|
||||
'support.type',
|
||||
'entity.name.type',
|
||||
'entity.name.namespace',
|
||||
'entity.other.attribute',
|
||||
'entity.name.scope-resolution',
|
||||
'entity.name.class',
|
||||
'storage.type.numeric.go',
|
||||
'storage.type.byte.go',
|
||||
'storage.type.boolean.go',
|
||||
'storage.type.string.go',
|
||||
'storage.type.uintptr.go',
|
||||
'storage.type.error.go',
|
||||
'storage.type.rune.go',
|
||||
'storage.type.cs',
|
||||
'storage.type.generic.cs',
|
||||
'storage.type.modifier.cs',
|
||||
'storage.type.variable.cs',
|
||||
'storage.type.annotation.java',
|
||||
'storage.type.generic.java',
|
||||
'storage.type.java',
|
||||
'storage.type.object.array.java',
|
||||
'storage.type.primitive.array.java',
|
||||
'storage.type.primitive.java',
|
||||
'storage.type.token.java',
|
||||
'storage.type.groovy',
|
||||
'storage.type.annotation.groovy',
|
||||
'storage.type.parameters.groovy',
|
||||
'storage.type.generic.groovy',
|
||||
'storage.type.object.array.groovy',
|
||||
'storage.type.primitive.array.groovy',
|
||||
'storage.type.primitive.groovy',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#185E73',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'meta.type.cast.expr',
|
||||
'meta.type.new.expr',
|
||||
'support.constant.math',
|
||||
'support.constant.dom',
|
||||
'support.constant.json',
|
||||
'entity.other.inherited-class',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#185E73',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword.control',
|
||||
'source.cpp keyword.operator.new',
|
||||
'source.cpp keyword.operator.delete',
|
||||
'keyword.other.using',
|
||||
'keyword.other.directive.using',
|
||||
'keyword.other.operator',
|
||||
'entity.name.operator',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#B5200D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'variable',
|
||||
'meta.definition.variable.name',
|
||||
'support.variable',
|
||||
'entity.name.variable',
|
||||
'constant.other.placeholder',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#001080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['variable.other.constant', 'variable.other.enummember'],
|
||||
settings: {
|
||||
foreground: '#02715D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.object-literal.key'],
|
||||
settings: {
|
||||
foreground: '#001080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.constant.property-value',
|
||||
'support.constant.font-name',
|
||||
'support.constant.media-type',
|
||||
'support.constant.media',
|
||||
'constant.other.color.rgb-value',
|
||||
'constant.other.rgb-value',
|
||||
'support.constant.color',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.group.regexp',
|
||||
'punctuation.definition.group.assertion.regexp',
|
||||
'punctuation.definition.character-class.regexp',
|
||||
'punctuation.character.set.begin.regexp',
|
||||
'punctuation.character.set.end.regexp',
|
||||
'keyword.operator.negation.regexp',
|
||||
'support.other.parenthesis.regexp',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#D16969',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'constant.character.character-class.regexp',
|
||||
'constant.other.character-class.set.regexp',
|
||||
'constant.other.character-class.regexp',
|
||||
'constant.character.set.regexp',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#811F3F',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator.quantifier.regexp',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['keyword.operator.or.regexp', 'keyword.control.anchor.regexp'],
|
||||
settings: {
|
||||
foreground: '#EE0000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.character',
|
||||
settings: {
|
||||
foreground: '#0F4A85',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.character.escape',
|
||||
settings: {
|
||||
foreground: '#EE0000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.label',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#316BCD',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#CD3131',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#800080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'ref.matchtext',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,716 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const lightModern: ThemeRegistrationAny = {
|
||||
$schema: 'vscode://schemas/color-theme',
|
||||
type: 'light',
|
||||
colors: {
|
||||
'actionBar.toggledBackground': '#dddddd',
|
||||
'activityBar.activeBorder': '#005fb8',
|
||||
'activityBar.background': '#f8f8f8',
|
||||
'activityBar.border': '#e5e5e5',
|
||||
'activityBar.foreground': '#1f1f1f',
|
||||
'activityBar.inactiveForeground': '#616161',
|
||||
'activityBarBadge.background': '#005fb8',
|
||||
'activityBarBadge.foreground': '#ffffff',
|
||||
'badge.background': '#cccccc',
|
||||
'badge.foreground': '#3b3b3b',
|
||||
'button.background': '#005fb8',
|
||||
'button.border': '#0000001a',
|
||||
'button.foreground': '#ffffff',
|
||||
'button.hoverBackground': '#0258a8',
|
||||
'button.secondaryBackground': '#e5e5e5',
|
||||
'button.secondaryForeground': '#3b3b3b',
|
||||
'button.secondaryHoverBackground': '#cccccc',
|
||||
'chat.slashCommandBackground': '#d2ecff',
|
||||
'chat.slashCommandForeground': '#306ca2',
|
||||
'checkbox.background': '#f8f8f8',
|
||||
'checkbox.border': '#cecece',
|
||||
descriptionForeground: '#3b3b3b',
|
||||
'dropdown.background': '#ffffff',
|
||||
'dropdown.border': '#cecece',
|
||||
'dropdown.foreground': '#3b3b3b',
|
||||
'dropdown.listBackground': '#ffffff',
|
||||
'editor.background': '#ffffff',
|
||||
'editor.foreground': '#3b3b3b',
|
||||
'editor.inactiveSelectionBackground': '#e5ebf1',
|
||||
'editor.selectionHighlightBackground': '#add6ff80',
|
||||
'editorGroup.border': '#e5e5e5',
|
||||
'editorGroupHeader.tabsBackground': '#f8f8f8',
|
||||
'editorGroupHeader.tabsBorder': '#e5e5e5',
|
||||
'editorGutter.addedBackground': '#2ea043',
|
||||
'editorGutter.deletedBackground': '#f85149',
|
||||
'editorGutter.modifiedBackground': '#005fb8',
|
||||
'editorIndentGuide.activeBackground1': '#939393',
|
||||
'editorIndentGuide.background1': '#d3d3d3',
|
||||
'editorLineNumber.activeForeground': '#171184',
|
||||
'editorLineNumber.foreground': '#6e7681',
|
||||
'editorOverviewRuler.border': '#e5e5e5',
|
||||
'editorSuggestWidget.background': '#f8f8f8',
|
||||
'editorWidget.background': '#f8f8f8',
|
||||
errorForeground: '#f85149',
|
||||
focusBorder: '#005fb8',
|
||||
foreground: '#3b3b3b',
|
||||
'icon.foreground': '#3b3b3b',
|
||||
'input.background': '#ffffff',
|
||||
'input.border': '#cecece',
|
||||
'input.foreground': '#3b3b3b',
|
||||
'input.placeholderForeground': '#868686',
|
||||
'inputOption.activeBackground': '#bed6ed',
|
||||
'inputOption.activeBorder': '#005fb8',
|
||||
'inputOption.activeForeground': '#000000',
|
||||
'keybindingLabel.foreground': '#3b3b3b',
|
||||
'list.activeSelectionBackground': '#e8e8e8',
|
||||
'list.activeSelectionForeground': '#000000',
|
||||
'list.activeSelectionIconForeground': '#000000',
|
||||
'list.focusAndSelectionOutline': '#005fb8',
|
||||
'list.hoverBackground': '#f2f2f2',
|
||||
'menu.border': '#cecece',
|
||||
'notebook.cellBorderColor': '#e5e5e5',
|
||||
'notebook.selectedCellBackground': '#c8ddf150',
|
||||
'notificationCenterHeader.background': '#ffffff',
|
||||
'notificationCenterHeader.foreground': '#3b3b3b',
|
||||
'notifications.background': '#ffffff',
|
||||
'notifications.border': '#e5e5e5',
|
||||
'notifications.foreground': '#3b3b3b',
|
||||
'panel.background': '#f8f8f8',
|
||||
'panel.border': '#e5e5e5',
|
||||
'panelInput.border': '#e5e5e5',
|
||||
'panelTitle.activeBorder': '#005fb8',
|
||||
'panelTitle.activeForeground': '#3b3b3b',
|
||||
'panelTitle.inactiveForeground': '#3b3b3b',
|
||||
'peekViewEditor.matchHighlightBackground': '#bb800966',
|
||||
'peekViewResult.background': '#ffffff',
|
||||
'peekViewResult.matchHighlightBackground': '#bb800966',
|
||||
'pickerGroup.border': '#e5e5e5',
|
||||
'pickerGroup.foreground': '#8b949e',
|
||||
'ports.iconRunningProcessForeground': '#369432',
|
||||
'progressBar.background': '#005fb8',
|
||||
'quickInput.background': '#f8f8f8',
|
||||
'quickInput.foreground': '#3b3b3b',
|
||||
'searchEditor.textInputBorder': '#cecece',
|
||||
'settings.dropdownBackground': '#ffffff',
|
||||
'settings.dropdownBorder': '#cecece',
|
||||
'settings.headerForeground': '#1f1f1f',
|
||||
'settings.modifiedItemIndicator': '#bb800966',
|
||||
'settings.numberInputBorder': '#cecece',
|
||||
'settings.textInputBorder': '#cecece',
|
||||
'sideBar.background': '#f8f8f8',
|
||||
'sideBar.border': '#e5e5e5',
|
||||
'sideBar.foreground': '#3b3b3b',
|
||||
'sideBarSectionHeader.background': '#f8f8f8',
|
||||
'sideBarSectionHeader.border': '#e5e5e5',
|
||||
'sideBarSectionHeader.foreground': '#3b3b3b',
|
||||
'sideBarTitle.foreground': '#3b3b3b',
|
||||
'statusBar.background': '#f8f8f8',
|
||||
'statusBar.border': '#e5e5e5',
|
||||
'statusBar.debuggingBackground': '#fd716c',
|
||||
'statusBar.debuggingForeground': '#000000',
|
||||
'statusBar.focusBorder': '#005fb8',
|
||||
'statusBar.foreground': '#3b3b3b',
|
||||
'statusBar.noFolderBackground': '#f8f8f8',
|
||||
'statusBarItem.errorBackground': '#c72e0f',
|
||||
'statusBarItem.focusBorder': '#005fb8',
|
||||
'statusBarItem.prominentBackground': '#6e768166',
|
||||
'statusBarItem.remoteBackground': '#005fb8',
|
||||
'statusBarItem.remoteForeground': '#ffffff',
|
||||
'tab.activeBackground': '#ffffff',
|
||||
'tab.activeBorder': '#f8f8f8',
|
||||
'tab.activeBorderTop': '#005fb8',
|
||||
'tab.activeForeground': '#3b3b3b',
|
||||
'tab.border': '#e5e5e5',
|
||||
'tab.hoverBackground': '#ffffff',
|
||||
'tab.inactiveBackground': '#f8f8f8',
|
||||
'tab.inactiveForeground': '#868686',
|
||||
'tab.lastPinnedBorder': '#d4d4d4',
|
||||
'tab.unfocusedActiveBorder': '#f8f8f8',
|
||||
'tab.unfocusedActiveBorderTop': '#e5e5e5',
|
||||
'tab.unfocusedHoverBackground': '#f8f8f8',
|
||||
'terminal.foreground': '#3b3b3b',
|
||||
'terminal.inactiveSelectionBackground': '#e5ebf1',
|
||||
'terminal.tab.activeBorder': '#005fb8',
|
||||
'terminalCursor.foreground': '#005fb8',
|
||||
'textBlockQuote.background': '#f8f8f8',
|
||||
'textBlockQuote.border': '#e5e5e5',
|
||||
'textCodeBlock.background': '#f8f8f8',
|
||||
'textLink.activeForeground': '#005fb8',
|
||||
'textLink.foreground': '#005fb8',
|
||||
'textPreformat.background': '#0000001f',
|
||||
'textPreformat.foreground': '#3b3b3b',
|
||||
'textSeparator.foreground': '#21262d',
|
||||
'titleBar.activeBackground': '#f8f8f8',
|
||||
'titleBar.activeForeground': '#1e1e1e',
|
||||
'titleBar.border': '#e5e5e5',
|
||||
'titleBar.inactiveBackground': '#f8f8f8',
|
||||
'titleBar.inactiveForeground': '#8b949e',
|
||||
'welcomePage.tileBackground': '#f3f3f3',
|
||||
'widget.border': '#e5e5e5',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: [
|
||||
'meta.embedded',
|
||||
'source.groovy.embedded',
|
||||
'string meta.image.inline.markdown',
|
||||
'variable.legacy.builtin.python',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'emphasis',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'strong',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.diff.header',
|
||||
settings: {
|
||||
foreground: '#000080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'comment',
|
||||
settings: {
|
||||
foreground: '#008000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.language',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'constant.numeric',
|
||||
'variable.other.enummember',
|
||||
'keyword.operator.plus.exponent',
|
||||
'keyword.operator.minus.exponent',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#098658',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.regexp',
|
||||
settings: {
|
||||
foreground: '#811F3F',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.selector',
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.attribute-name',
|
||||
settings: {
|
||||
foreground: '#E50000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'entity.other.attribute-name.class.css',
|
||||
'entity.other.attribute-name.class.mixin.css',
|
||||
'entity.other.attribute-name.id.css',
|
||||
'entity.other.attribute-name.parent-selector.css',
|
||||
'entity.other.attribute-name.pseudo-class.css',
|
||||
'entity.other.attribute-name.pseudo-element.css',
|
||||
'source.css.less entity.other.attribute-name.id',
|
||||
'entity.other.attribute-name.scss',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#CD3131',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.underline',
|
||||
settings: {
|
||||
fontStyle: 'underline',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold',
|
||||
settings: {
|
||||
foreground: '#000080',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.heading',
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.italic',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted',
|
||||
settings: {
|
||||
foreground: '#098658',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted',
|
||||
settings: {
|
||||
foreground: '#A31515',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed',
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.definition.quote.begin.markdown', 'punctuation.definition.list.begin.markdown'],
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inline.raw',
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.definition.tag',
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.preprocessor', 'entity.name.function.preprocessor'],
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.string',
|
||||
settings: {
|
||||
foreground: '#A31515',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.numeric',
|
||||
settings: {
|
||||
foreground: '#098658',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.structure.dictionary.key.python',
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage.type',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.modifier', 'keyword.operator.noexcept'],
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['string', 'meta.embedded.assembly'],
|
||||
settings: {
|
||||
foreground: '#A31515',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'string.comment.buffered.block.pug',
|
||||
'string.quoted.pug',
|
||||
'string.interpolated.pug',
|
||||
'string.unquoted.plain.in.yaml',
|
||||
'string.unquoted.plain.out.yaml',
|
||||
'string.unquoted.block.yaml',
|
||||
'string.quoted.single.yaml',
|
||||
'string.quoted.double.xml',
|
||||
'string.quoted.single.xml',
|
||||
'string.unquoted.cdata.xml',
|
||||
'string.quoted.double.html',
|
||||
'string.quoted.single.html',
|
||||
'string.unquoted.html',
|
||||
'string.quoted.single.handlebars',
|
||||
'string.quoted.double.handlebars',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.regexp',
|
||||
settings: {
|
||||
foreground: '#811F3F',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.template-expression.begin',
|
||||
'punctuation.definition.template-expression.end',
|
||||
'punctuation.section.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.template.expression'],
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.constant.property-value',
|
||||
'support.constant.font-name',
|
||||
'support.constant.media-type',
|
||||
'support.constant.media',
|
||||
'constant.other.color.rgb-value',
|
||||
'constant.other.rgb-value',
|
||||
'support.constant.color',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.type.vendored.property-name',
|
||||
'support.type.property-name',
|
||||
'variable.css',
|
||||
'variable.scss',
|
||||
'variable.other.less',
|
||||
'source.coffee.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#E50000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['support.type.property-name.json'],
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.control',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword.operator.new',
|
||||
'keyword.operator.expression',
|
||||
'keyword.operator.cast',
|
||||
'keyword.operator.sizeof',
|
||||
'keyword.operator.alignof',
|
||||
'keyword.operator.typeid',
|
||||
'keyword.operator.alignas',
|
||||
'keyword.operator.instanceof',
|
||||
'keyword.operator.logical.python',
|
||||
'keyword.operator.wordlike',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.other.unit',
|
||||
settings: {
|
||||
foreground: '#098658',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.section.embedded.begin.php', 'punctuation.section.embedded.end.php'],
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.function.git-rebase',
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.sha.git-rebase',
|
||||
settings: {
|
||||
foreground: '#098658',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.modifier.import.java', 'variable.language.wildcard.java', 'storage.modifier.package.java'],
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.language',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'entity.name.function',
|
||||
'support.function',
|
||||
'support.constant.handlebars',
|
||||
'source.powershell variable.other.member',
|
||||
'entity.name.operator.custom-literal',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#795E26',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.class',
|
||||
'support.type',
|
||||
'entity.name.type',
|
||||
'entity.name.namespace',
|
||||
'entity.other.attribute',
|
||||
'entity.name.scope-resolution',
|
||||
'entity.name.class',
|
||||
'storage.type.numeric.go',
|
||||
'storage.type.byte.go',
|
||||
'storage.type.boolean.go',
|
||||
'storage.type.string.go',
|
||||
'storage.type.uintptr.go',
|
||||
'storage.type.error.go',
|
||||
'storage.type.rune.go',
|
||||
'storage.type.cs',
|
||||
'storage.type.generic.cs',
|
||||
'storage.type.modifier.cs',
|
||||
'storage.type.variable.cs',
|
||||
'storage.type.annotation.java',
|
||||
'storage.type.generic.java',
|
||||
'storage.type.java',
|
||||
'storage.type.object.array.java',
|
||||
'storage.type.primitive.array.java',
|
||||
'storage.type.primitive.java',
|
||||
'storage.type.token.java',
|
||||
'storage.type.groovy',
|
||||
'storage.type.annotation.groovy',
|
||||
'storage.type.parameters.groovy',
|
||||
'storage.type.generic.groovy',
|
||||
'storage.type.object.array.groovy',
|
||||
'storage.type.primitive.array.groovy',
|
||||
'storage.type.primitive.groovy',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#267F99',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'meta.type.cast.expr',
|
||||
'meta.type.new.expr',
|
||||
'support.constant.math',
|
||||
'support.constant.dom',
|
||||
'support.constant.json',
|
||||
'entity.other.inherited-class',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#267F99',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword.control',
|
||||
'source.cpp keyword.operator.new',
|
||||
'source.cpp keyword.operator.delete',
|
||||
'keyword.other.using',
|
||||
'keyword.other.directive.using',
|
||||
'keyword.other.operator',
|
||||
'entity.name.operator',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#AF00DB',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'variable',
|
||||
'meta.definition.variable.name',
|
||||
'support.variable',
|
||||
'entity.name.variable',
|
||||
'constant.other.placeholder',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#001080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['variable.other.constant', 'variable.other.enummember'],
|
||||
settings: {
|
||||
foreground: '#0070C1',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.object-literal.key'],
|
||||
settings: {
|
||||
foreground: '#001080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.constant.property-value',
|
||||
'support.constant.font-name',
|
||||
'support.constant.media-type',
|
||||
'support.constant.media',
|
||||
'constant.other.color.rgb-value',
|
||||
'constant.other.rgb-value',
|
||||
'support.constant.color',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.group.regexp',
|
||||
'punctuation.definition.group.assertion.regexp',
|
||||
'punctuation.definition.character-class.regexp',
|
||||
'punctuation.character.set.begin.regexp',
|
||||
'punctuation.character.set.end.regexp',
|
||||
'keyword.operator.negation.regexp',
|
||||
'support.other.parenthesis.regexp',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#D16969',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'constant.character.character-class.regexp',
|
||||
'constant.other.character-class.set.regexp',
|
||||
'constant.other.character-class.regexp',
|
||||
'constant.character.set.regexp',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#811F3F',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator.quantifier.regexp',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['keyword.operator.or.regexp', 'keyword.control.anchor.regexp'],
|
||||
settings: {
|
||||
foreground: '#EE0000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['constant.character', 'constant.other.option'],
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.character.escape',
|
||||
settings: {
|
||||
foreground: '#EE0000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.label',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'ref.matchtext',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#316BCD',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#CD3131',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#800080',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,573 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const monokaiDim: ThemeRegistrationAny = {
|
||||
$schema: 'vscode://schemas/color-theme',
|
||||
type: 'dark',
|
||||
colors: {
|
||||
'activityBar.background': '#353535',
|
||||
'activityBar.foreground': '#ffffff',
|
||||
'activityBarBadge.background': '#3655b5',
|
||||
'button.background': '#565656',
|
||||
'dropdown.background': '#525252',
|
||||
'editor.background': '#1e1e1e',
|
||||
'editor.foreground': '#c5c8c6',
|
||||
'editor.lineHighlightBackground': '#303030',
|
||||
'editor.selectionBackground': '#676b7180',
|
||||
'editor.selectionHighlightBackground': '#575b6180',
|
||||
'editor.wordHighlightBackground': '#4747a180',
|
||||
'editor.wordHighlightStrongBackground': '#6767ce80',
|
||||
'editorCursor.foreground': '#c07020',
|
||||
'editorGroupHeader.tabsBackground': '#282828',
|
||||
'editorIndentGuide.activeBackground': '#707057',
|
||||
'editorIndentGuide.background': '#505037',
|
||||
'editorLineNumber.activeForeground': '#949494',
|
||||
'editorWhitespace.foreground': '#505037',
|
||||
focusBorder: '#3655b5',
|
||||
'inputOption.activeBorder': '#3655b5',
|
||||
'list.activeSelectionBackground': '#707070',
|
||||
'list.highlightForeground': '#e58520',
|
||||
'list.hoverBackground': '#444444',
|
||||
'list.inactiveSelectionBackground': '#4e4e4e',
|
||||
'menu.background': '#272727',
|
||||
'menu.foreground': '#cccccc',
|
||||
'minimap.selectionHighlight': '#676b7180',
|
||||
'panelTitle.activeForeground': '#ffffff',
|
||||
'peekView.border': '#3655b5',
|
||||
'pickerGroup.foreground': '#b0b0b0',
|
||||
'ports.iconRunningProcessForeground': '#cccccc',
|
||||
'quickInputList.focusBackground': '#707070',
|
||||
'sideBar.background': '#272727',
|
||||
'sideBarSectionHeader.background': '#505050',
|
||||
'statusBar.background': '#505050',
|
||||
'statusBar.debuggingBackground': '#505050',
|
||||
'statusBar.noFolderBackground': '#505050',
|
||||
'statusBarItem.remoteBackground': '#3655b5',
|
||||
'tab.border': '#303030',
|
||||
'tab.inactiveBackground': '#404040',
|
||||
'tab.inactiveForeground': '#d8d8d8',
|
||||
'tab.lastPinnedBorder': '#505050',
|
||||
'terminal.ansiBlack': '#1e1e1e',
|
||||
'terminal.ansiBlue': '#6a7ec8',
|
||||
'terminal.ansiBrightBlack': '#666666',
|
||||
'terminal.ansiBrightBlue': '#819aff',
|
||||
'terminal.ansiBrightCyan': '#66d9ef',
|
||||
'terminal.ansiBrightGreen': '#a6e22e',
|
||||
'terminal.ansiBrightMagenta': '#ae81ff',
|
||||
'terminal.ansiBrightRed': '#f92672',
|
||||
'terminal.ansiBrightWhite': '#f8f8f2',
|
||||
'terminal.ansiBrightYellow': '#e2e22e',
|
||||
'terminal.ansiCyan': '#56adbc',
|
||||
'terminal.ansiGreen': '#86b42b',
|
||||
'terminal.ansiMagenta': '#8c6bc8',
|
||||
'terminal.ansiRed': '#c4265e',
|
||||
'terminal.ansiWhite': '#e3e3dd',
|
||||
'terminal.ansiYellow': '#b3b42b',
|
||||
'terminal.inactiveSelectionBackground': '#676b7140',
|
||||
'titleBar.activeBackground': '#505050',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: ['meta.embedded', 'source.groovy.embedded', 'variable.legacy.builtin.python'],
|
||||
settings: {
|
||||
foreground: '#C5C8C6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'comment',
|
||||
settings: {
|
||||
foreground: '#9A9B99',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string',
|
||||
settings: {
|
||||
foreground: '#9AA83A',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string source',
|
||||
settings: {
|
||||
foreground: '#D08442',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.numeric',
|
||||
settings: {
|
||||
foreground: '#6089B4',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.language',
|
||||
settings: {
|
||||
foreground: '#408080',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.character, constant.other',
|
||||
settings: {
|
||||
foreground: '#8080FF',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword',
|
||||
settings: {
|
||||
foreground: '#6089B4',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support',
|
||||
settings: {
|
||||
foreground: '#C7444A',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage',
|
||||
settings: {
|
||||
foreground: '#9872A2',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.class, entity.name.type, entity.name.namespace, entity.name.scope-resolution',
|
||||
settings: {
|
||||
foreground: '#9B0000',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.inherited-class',
|
||||
settings: {
|
||||
foreground: '#C7444A',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.function',
|
||||
settings: {
|
||||
foreground: '#CE6700',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.parameter',
|
||||
settings: {
|
||||
foreground: '#6089B4',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#9872A2',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.attribute-name',
|
||||
settings: {
|
||||
foreground: '#9872A2',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.function',
|
||||
settings: {
|
||||
foreground: '#9872A2',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword',
|
||||
settings: {
|
||||
foreground: '#676867',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.other, variable.js, punctuation.separator.variable',
|
||||
settings: {
|
||||
foreground: '#6089B4',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.section.embedded -(source string source punctuation.section.embedded), meta.brace.erb.html',
|
||||
settings: {
|
||||
foreground: '#008200',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#FF0B00',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.other.php, variable.other.normal',
|
||||
settings: {
|
||||
foreground: '#6089B4',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.function-call.object',
|
||||
settings: {
|
||||
foreground: '#9872A2',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.other.property',
|
||||
settings: {
|
||||
foreground: '#9872A2',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword.control',
|
||||
'keyword.operator.new.cpp',
|
||||
'keyword.operator.delete.cpp',
|
||||
'keyword.other.using',
|
||||
'keyword.other.directive.using',
|
||||
'keyword.other.operator',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#9872A2',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.tag',
|
||||
settings: {
|
||||
foreground: '#D0B344',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#6089B4',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.doctype, meta.tag.sgml-declaration.doctype, meta.tag.sgml.doctype',
|
||||
settings: {
|
||||
foreground: '#9AA83A',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.tag.inline source, text.html.php.source',
|
||||
settings: {
|
||||
foreground: '#9AA83A',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.tag.other, entity.name.tag.style, entity.name.tag.script, meta.tag.block.script, source.js.embedded punctuation.definition.tag.html, source.css.embedded punctuation.definition.tag.html',
|
||||
settings: {
|
||||
foreground: '#9872A2',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.attribute-name, meta.tag punctuation.definition.string',
|
||||
settings: {
|
||||
foreground: '#D0B344',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.tag string -source -punctuation, text source text meta.tag string -punctuation',
|
||||
settings: {
|
||||
foreground: '#6089B4',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.section.embedded -(source string source punctuation.section.embedded), meta.brace.erb.html',
|
||||
settings: {
|
||||
foreground: '#D0B344',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.toc-list.id',
|
||||
settings: {
|
||||
foreground: '#9AA83A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.quoted.double.html, punctuation.definition.string.begin.html, punctuation.definition.string.end.html, punctuation.definition.string.end.html source, string.quoted.double.html source',
|
||||
settings: {
|
||||
foreground: '#9AA83A',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.definition.tag.html, punctuation.definition.tag.begin, punctuation.definition.tag.end',
|
||||
settings: {
|
||||
foreground: '#6089B4',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.selector.css entity.other.attribute-name.id',
|
||||
settings: {
|
||||
foreground: '#9872A2',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.type.property-name.css',
|
||||
settings: {
|
||||
foreground: '#676867',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.property-group support.constant.property-value.css, meta.property-value support.constant.property-value.css',
|
||||
settings: {
|
||||
foreground: '#C7444A',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.language.js',
|
||||
settings: {
|
||||
foreground: '#CC555A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.definition.template-expression', 'punctuation.section.embedded.coffee'],
|
||||
settings: {
|
||||
foreground: '#D08442',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.template.expression'],
|
||||
settings: {
|
||||
foreground: '#C5C8C6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.function-call.object.php',
|
||||
settings: {
|
||||
foreground: '#D0B344',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.definition.string.end.php, punctuation.definition.string.begin.php',
|
||||
settings: {
|
||||
foreground: '#9AA83A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'source.php.embedded.line.html',
|
||||
settings: {
|
||||
foreground: '#676867',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.section.embedded.begin.php, punctuation.section.embedded.end.php',
|
||||
settings: {
|
||||
foreground: '#D08442',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.other.symbol.ruby',
|
||||
settings: {
|
||||
foreground: '#9AA83A',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.language.ruby',
|
||||
settings: {
|
||||
foreground: '#D0B344',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.other.special-method.ruby',
|
||||
settings: {
|
||||
foreground: '#D9B700',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.section.embedded.begin.ruby', 'punctuation.section.embedded.end.ruby'],
|
||||
settings: {
|
||||
foreground: '#D08442',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.other.DML.sql',
|
||||
settings: {
|
||||
foreground: '#D0B344',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.diff, meta.diff.header',
|
||||
settings: {
|
||||
foreground: '#E0EDDD',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted',
|
||||
settings: {
|
||||
foreground: '#DC322F',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed',
|
||||
settings: {
|
||||
foreground: '#CB4B16',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted',
|
||||
settings: {
|
||||
foreground: '#219186',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.quote',
|
||||
settings: {
|
||||
foreground: '#9872A2',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.list',
|
||||
settings: {
|
||||
foreground: '#9AA83A',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold, markup.italic',
|
||||
settings: {
|
||||
foreground: '#6089B4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inline.raw',
|
||||
settings: {
|
||||
foreground: '#FF0080',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.heading',
|
||||
settings: {
|
||||
foreground: '#D0B344',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.heading.setext',
|
||||
settings: {
|
||||
foreground: '#D0B344',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.heading.markdown',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.quote.markdown',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold.markdown',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.other.link.title.markdown,string.other.link.description.markdown',
|
||||
settings: {
|
||||
foreground: '#AE81FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.underline.link.markdown,markup.underline.link.image.markdown',
|
||||
settings: {},
|
||||
},
|
||||
{
|
||||
scope: 'markup.italic.markdown',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.list.unnumbered.markdown, markup.list.numbered.markdown',
|
||||
settings: {},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.definition.list.begin.markdown'],
|
||||
settings: {},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#6796E6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#F44747',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#B267E6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.language',
|
||||
settings: {
|
||||
foreground: '#C7444A',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,465 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const quietLight: ThemeRegistrationAny = {
|
||||
$schema: 'vscode://schemas/color-theme',
|
||||
type: 'light',
|
||||
colors: {
|
||||
'activityBar.background': '#ededf5',
|
||||
'activityBar.foreground': '#705697',
|
||||
'activityBarBadge.background': '#705697',
|
||||
'badge.background': '#705697aa',
|
||||
'button.background': '#705697',
|
||||
'dropdown.background': '#f5f5f5',
|
||||
'editor.background': '#f5f5f5',
|
||||
'editor.findMatchBackground': '#bf9cac',
|
||||
'editor.findMatchHighlightBackground': '#edc9d899',
|
||||
'editor.lineHighlightBackground': '#e4f6d4',
|
||||
'editor.selectionBackground': '#c9d0d9',
|
||||
'editorCursor.foreground': '#54494b',
|
||||
'editorGroup.dropBackground': '#c9d0d988',
|
||||
'editorIndentGuide.activeBackground': '#777777b0',
|
||||
'editorIndentGuide.background': '#aaaaaa60',
|
||||
'editorLineNumber.activeForeground': '#9769dc',
|
||||
'editorLineNumber.foreground': '#6d705b',
|
||||
'editorWhitespace.foreground': '#aaaaaa',
|
||||
errorForeground: '#f1897f',
|
||||
focusBorder: '#9769dc',
|
||||
'inputOption.activeBorder': '#adafb7',
|
||||
'inputValidation.errorBackground': '#ffeaea',
|
||||
'inputValidation.errorBorder': '#f1897f',
|
||||
'inputValidation.infoBackground': '#f2fcff',
|
||||
'inputValidation.infoBorder': '#4ec1e5',
|
||||
'inputValidation.warningBackground': '#fffee2',
|
||||
'inputValidation.warningBorder': '#ffe055',
|
||||
'list.activeSelectionBackground': '#c4d9b1',
|
||||
'list.activeSelectionForeground': '#6c6c6c',
|
||||
'list.highlightForeground': '#9769dc',
|
||||
'list.hoverBackground': '#e0e0e0',
|
||||
'list.inactiveSelectionBackground': '#d3dbcd',
|
||||
'minimap.selectionHighlight': '#c9d0d9',
|
||||
'panel.background': '#f5f5f5',
|
||||
'peekView.border': '#705697',
|
||||
'peekViewEditor.background': '#f2f8fc',
|
||||
'peekViewEditor.matchHighlightBackground': '#c2dfe3',
|
||||
'peekViewResult.background': '#f2f8fc',
|
||||
'peekViewResult.matchHighlightBackground': '#93c6d6',
|
||||
'peekViewTitle.background': '#f2f8fc',
|
||||
'pickerGroup.border': '#749351',
|
||||
'pickerGroup.foreground': '#a6b39b',
|
||||
'ports.iconRunningProcessForeground': '#749351',
|
||||
'progressBar.background': '#705697',
|
||||
'quickInputList.focusBackground': '#cadeb9',
|
||||
'selection.background': '#c9d0d9',
|
||||
'sideBar.background': '#f2f2f2',
|
||||
'sideBarSectionHeader.background': '#ede8ef',
|
||||
'statusBar.background': '#705697',
|
||||
'statusBar.debuggingBackground': '#705697',
|
||||
'statusBar.noFolderBackground': '#705697',
|
||||
'statusBarItem.remoteBackground': '#4e3c69',
|
||||
'tab.lastPinnedBorder': '#c9d0d9',
|
||||
'titleBar.activeBackground': '#c4b7d7',
|
||||
'walkThrough.embeddedEditorBackground': '#00000014',
|
||||
'welcomePage.tileBackground': '#f0f0f7',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: [
|
||||
'meta.embedded',
|
||||
'source.groovy.embedded',
|
||||
'string meta.image.inline.markdown',
|
||||
'variable.legacy.builtin.python',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#333333',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['comment', 'punctuation.definition.comment'],
|
||||
settings: {
|
||||
foreground: '#AAAAAA',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'comment.block.preprocessor',
|
||||
settings: {
|
||||
foreground: '#AAAAAA',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'comment.documentation',
|
||||
'comment.block.documentation',
|
||||
'comment.block.documentation punctuation.definition.comment ',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#448C27',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#CD3131',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid.illegal',
|
||||
settings: {
|
||||
foreground: '#660000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator',
|
||||
settings: {
|
||||
foreground: '#777777',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['keyword', 'storage'],
|
||||
settings: {
|
||||
foreground: '#4B69C6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.type', 'support.type'],
|
||||
settings: {
|
||||
foreground: '#7A3E9D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['constant.language', 'support.constant', 'variable.language'],
|
||||
settings: {
|
||||
foreground: '#9C5D27',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['variable', 'support.variable'],
|
||||
settings: {
|
||||
foreground: '#7A3E9D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['entity.name.function', 'support.function'],
|
||||
settings: {
|
||||
foreground: '#AA3731',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'entity.name.type',
|
||||
'entity.name.namespace',
|
||||
'entity.name.scope-resolution',
|
||||
'entity.other.inherited-class',
|
||||
'support.class',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#7A3E9D',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.exception',
|
||||
settings: {
|
||||
foreground: '#660000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.section',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['constant.numeric', 'constant.character', 'constant'],
|
||||
settings: {
|
||||
foreground: '#9C5D27',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string',
|
||||
settings: {
|
||||
foreground: '#448C27',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.character.escape',
|
||||
settings: {
|
||||
foreground: '#777777',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.regexp',
|
||||
settings: {
|
||||
foreground: '#4B69C6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.other.symbol',
|
||||
settings: {
|
||||
foreground: '#9C5D27',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation',
|
||||
settings: {
|
||||
foreground: '#777777',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'meta.tag.sgml.doctype',
|
||||
'meta.tag.sgml.doctype string',
|
||||
'meta.tag.sgml.doctype entity.name.tag',
|
||||
'meta.tag.sgml punctuation.definition.tag.html',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#AAAAAA',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'meta.tag',
|
||||
'punctuation.definition.tag.html',
|
||||
'punctuation.definition.tag.begin.html',
|
||||
'punctuation.definition.tag.end.html',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#91B3E0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#4B69C6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.tag entity.other.attribute-name', 'entity.other.attribute-name.html'],
|
||||
settings: {
|
||||
foreground: '#8190A0',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['constant.character.entity', 'punctuation.definition.entity'],
|
||||
settings: {
|
||||
foreground: '#9C5D27',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.selector', 'meta.selector entity', 'meta.selector entity punctuation', 'entity.name.tag.css'],
|
||||
settings: {
|
||||
foreground: '#7A3E9D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.property-name', 'support.type.property-name'],
|
||||
settings: {
|
||||
foreground: '#9C5D27',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.property-value', 'meta.property-value constant.other', 'support.constant.property-value'],
|
||||
settings: {
|
||||
foreground: '#448C27',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.other.important',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.italic',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.error',
|
||||
settings: {
|
||||
foreground: '#660000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.link',
|
||||
settings: {
|
||||
foreground: '#4B69C6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['markup.output', 'markup.raw'],
|
||||
settings: {
|
||||
foreground: '#777777',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.prompt',
|
||||
settings: {
|
||||
foreground: '#777777',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.heading',
|
||||
settings: {
|
||||
foreground: '#AA3731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.traceback',
|
||||
settings: {
|
||||
foreground: '#660000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.underline',
|
||||
settings: {
|
||||
fontStyle: 'underline',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.quote',
|
||||
settings: {
|
||||
foreground: '#7A3E9D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.list',
|
||||
settings: {
|
||||
foreground: '#4B69C6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['markup.bold', 'markup.italic'],
|
||||
settings: {
|
||||
foreground: '#448C27',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inline.raw',
|
||||
settings: {
|
||||
foreground: '#9C5D27',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.diff.range', 'meta.diff.index', 'meta.separator'],
|
||||
settings: {
|
||||
foreground: '#434343',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.diff.header.from-file', 'punctuation.definition.from-file.diff'],
|
||||
settings: {
|
||||
foreground: '#4B69C6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.diff.header.to-file', 'punctuation.definition.to-file.diff'],
|
||||
settings: {
|
||||
foreground: '#4B69C6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted.diff',
|
||||
settings: {
|
||||
foreground: '#C73D20',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed.diff',
|
||||
settings: {
|
||||
foreground: '#9C5D27',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted.diff',
|
||||
settings: {
|
||||
foreground: '#448C27',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.tag.js',
|
||||
'punctuation.definition.tag.begin.js',
|
||||
'punctuation.definition.tag.end.js',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#91B3E0',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.jsx.children.js',
|
||||
settings: {
|
||||
foreground: '#333333',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'ref.matchtext',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#316BCD',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#CD3131',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#800080',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,376 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const red: ThemeRegistrationAny = {
|
||||
$schema: 'vscode://schemas/color-theme',
|
||||
type: 'dark',
|
||||
colors: {
|
||||
'activityBar.background': '#580000',
|
||||
'badge.background': '#cc3333',
|
||||
'button.background': '#883333',
|
||||
'debugToolBar.background': '#660000',
|
||||
'dropdown.background': '#580000',
|
||||
'editor.background': '#390000',
|
||||
'editor.foreground': '#f8f8f8',
|
||||
'editor.hoverHighlightBackground': '#ff000044',
|
||||
'editor.lineHighlightBackground': '#ff000033',
|
||||
'editor.selectionBackground': '#750000',
|
||||
'editor.selectionHighlightBackground': '#f5500039',
|
||||
'editorCursor.foreground': '#970000',
|
||||
'editorGroup.border': '#ff666633',
|
||||
'editorGroupHeader.tabsBackground': '#330000',
|
||||
'editorHoverWidget.background': '#300000',
|
||||
'editorLineNumber.activeForeground': '#ffbbbb88',
|
||||
'editorLineNumber.foreground': '#ff777788',
|
||||
'editorLink.activeForeground': '#ffd0aa',
|
||||
'editorSuggestWidget.background': '#300000',
|
||||
'editorSuggestWidget.border': '#220000',
|
||||
'editorWhitespace.foreground': '#c10000',
|
||||
'editorWidget.background': '#300000',
|
||||
errorForeground: '#ffeaea',
|
||||
'extensionButton.prominentBackground': '#cc3333',
|
||||
'extensionButton.prominentHoverBackground': '#cc333388',
|
||||
focusBorder: '#ff6666aa',
|
||||
'input.background': '#580000',
|
||||
'inputOption.activeBorder': '#cc0000',
|
||||
'inputValidation.infoBackground': '#550000',
|
||||
'inputValidation.infoBorder': '#db7e58',
|
||||
'list.activeSelectionBackground': '#880000',
|
||||
'list.dropBackground': '#662222',
|
||||
'list.highlightForeground': '#ff4444',
|
||||
'list.hoverBackground': '#800000',
|
||||
'list.inactiveSelectionBackground': '#770000',
|
||||
'minimap.selectionHighlight': '#750000',
|
||||
'peekView.border': '#ff000044',
|
||||
'peekViewEditor.background': '#300000',
|
||||
'peekViewResult.background': '#400000',
|
||||
'peekViewTitle.background': '#550000',
|
||||
'pickerGroup.border': '#ff000033',
|
||||
'pickerGroup.foreground': '#cc9999',
|
||||
'ports.iconRunningProcessForeground': '#db7e58',
|
||||
'progressBar.background': '#cc3333',
|
||||
'quickInputList.focusBackground': '#660000',
|
||||
'selection.background': '#ff777788',
|
||||
'sideBar.background': '#330000',
|
||||
'statusBar.background': '#700000',
|
||||
'statusBar.noFolderBackground': '#700000',
|
||||
'statusBarItem.remoteBackground': '#cc3333',
|
||||
'tab.activeBackground': '#490000',
|
||||
'tab.inactiveBackground': '#300a0a',
|
||||
'tab.lastPinnedBorder': '#ff000044',
|
||||
'titleBar.activeBackground': '#770000',
|
||||
'titleBar.inactiveBackground': '#772222',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: [
|
||||
'meta.embedded',
|
||||
'source.groovy.embedded',
|
||||
'string meta.image.inline.markdown',
|
||||
'variable.legacy.builtin.python',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#F8F8F8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'comment',
|
||||
settings: {
|
||||
foreground: '#E7C0C0',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant',
|
||||
settings: {
|
||||
foreground: '#994646',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword',
|
||||
settings: {
|
||||
foreground: '#F12727',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity',
|
||||
settings: {
|
||||
foreground: '#FEC758',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage',
|
||||
settings: {
|
||||
foreground: '#FF6262',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string',
|
||||
settings: {
|
||||
foreground: '#CD8D8D',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support',
|
||||
settings: {
|
||||
foreground: '#9DF39F',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable',
|
||||
settings: {
|
||||
foreground: '#FB9A4B',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.inherited-class',
|
||||
settings: {
|
||||
foreground: '#AA5507',
|
||||
fontStyle: 'underline',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.character',
|
||||
settings: {
|
||||
foreground: '#EC0D1E',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['string constant', 'constant.character.escape'],
|
||||
settings: {
|
||||
foreground: '#FFE862',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.regexp',
|
||||
settings: {
|
||||
foreground: '#FFB454',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string variable',
|
||||
settings: {
|
||||
foreground: '#EDEF7D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.function',
|
||||
settings: {
|
||||
foreground: '#FFB454',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['support.constant', 'support.variable'],
|
||||
settings: {
|
||||
foreground: '#EB939A',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'declaration.sgml.html declaration.doctype',
|
||||
'declaration.sgml.html declaration.doctype entity',
|
||||
'declaration.sgml.html declaration.doctype string',
|
||||
'declaration.xml-processing',
|
||||
'declaration.xml-processing entity',
|
||||
'declaration.xml-processing string',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#73817D',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['declaration.tag', 'declaration.tag entity', 'meta.tag', 'meta.tag entity'],
|
||||
settings: {
|
||||
foreground: '#EC0D1E',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.selector.css entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#AA5507',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.selector.css entity.other.attribute-name.id',
|
||||
settings: {
|
||||
foreground: '#FEC758',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.selector.css entity.other.attribute-name.class',
|
||||
settings: {
|
||||
foreground: '#41A83E',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.type.property-name.css',
|
||||
settings: {
|
||||
foreground: '#96DD3B',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'meta.property-group support.constant.property-value.css',
|
||||
'meta.property-value support.constant.property-value.css',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#FFE862',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.property-value support.constant.named-color.css', 'meta.property-value constant'],
|
||||
settings: {
|
||||
foreground: '#FFE862',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.at-rule keyword.control.at-rule',
|
||||
settings: {
|
||||
foreground: '#FD6209',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.constructor.argument.css',
|
||||
settings: {
|
||||
foreground: '#EC9799',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.diff', 'meta.diff.header'],
|
||||
settings: {
|
||||
foreground: '#F8F8F8',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted',
|
||||
settings: {
|
||||
foreground: '#EC9799',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed',
|
||||
settings: {
|
||||
foreground: '#F8F8F8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted',
|
||||
settings: {
|
||||
foreground: '#41A83E',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.quote',
|
||||
settings: {
|
||||
foreground: '#F12727',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.list',
|
||||
settings: {
|
||||
foreground: '#FF6262',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['markup.bold', 'markup.italic'],
|
||||
settings: {
|
||||
foreground: '#FB9A4B',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.italic',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inline.raw',
|
||||
settings: {
|
||||
foreground: '#CD8D8D',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['markup.heading', 'markup.heading.setext', 'punctuation.definition.heading', 'entity.name.section'],
|
||||
settings: {
|
||||
foreground: '#FEC758',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.template-expression.begin',
|
||||
'punctuation.definition.template-expression.end',
|
||||
'punctuation.section.embedded',
|
||||
'.format.placeholder',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#EC0D1E',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#6796E6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#F44747',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#B267E6',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const tomorrowNightBlue: ThemeRegistrationAny = {
|
||||
$schema: 'vscode://schemas/color-theme',
|
||||
type: 'dark',
|
||||
colors: {
|
||||
'activityBar.background': '#001733',
|
||||
'badge.background': '#bbdaffcc',
|
||||
'badge.foreground': '#001733',
|
||||
'debugToolBar.background': '#001c40',
|
||||
'dropdown.background': '#001733',
|
||||
'editor.background': '#002451',
|
||||
'editor.foreground': '#ffffff',
|
||||
'editor.lineHighlightBackground': '#00346e',
|
||||
'editor.selectionBackground': '#003f8e',
|
||||
'editorCursor.foreground': '#ffffff',
|
||||
'editorGroup.border': '#404f7d',
|
||||
'editorGroup.dropBackground': '#25375daa',
|
||||
'editorGroupHeader.tabsBackground': '#001733',
|
||||
'editorHoverWidget.background': '#001c40',
|
||||
'editorHoverWidget.border': '#ffffff44',
|
||||
'editorLineNumber.activeForeground': '#949494',
|
||||
'editorWhitespace.foreground': '#404f7d',
|
||||
'editorWidget.background': '#001c40',
|
||||
errorForeground: '#a92049',
|
||||
focusBorder: '#bbdaff',
|
||||
'input.background': '#001733',
|
||||
'list.activeSelectionBackground': '#ffffff60',
|
||||
'list.highlightForeground': '#bbdaff',
|
||||
'list.hoverBackground': '#ffffff30',
|
||||
'list.inactiveSelectionBackground': '#ffffff40',
|
||||
'minimap.selectionHighlight': '#003f8e',
|
||||
'peekViewResult.background': '#001c40',
|
||||
'pickerGroup.foreground': '#bbdaff',
|
||||
'ports.iconRunningProcessForeground': '#bbdaff',
|
||||
'progressBar.background': '#bbdaffcc',
|
||||
'quickInputList.focusBackground': '#ffffff60',
|
||||
'sideBar.background': '#001c40',
|
||||
'statusBar.background': '#001126',
|
||||
'statusBar.debuggingBackground': '#001126',
|
||||
'statusBar.noFolderBackground': '#001126',
|
||||
'statusBarItem.remoteBackground': '#0e639c',
|
||||
'tab.inactiveBackground': '#001c40',
|
||||
'tab.lastPinnedBorder': '#007acc80',
|
||||
'terminal.ansiBlack': '#111111',
|
||||
'terminal.ansiBlue': '#bbdaff',
|
||||
'terminal.ansiBrightBlack': '#333333',
|
||||
'terminal.ansiBrightBlue': '#80baff',
|
||||
'terminal.ansiBrightCyan': '#78ffff',
|
||||
'terminal.ansiBrightGreen': '#b8f171',
|
||||
'terminal.ansiBrightMagenta': '#d778ff',
|
||||
'terminal.ansiBrightRed': '#ff7882',
|
||||
'terminal.ansiBrightWhite': '#ffffff',
|
||||
'terminal.ansiBrightYellow': '#ffe580',
|
||||
'terminal.ansiCyan': '#99ffff',
|
||||
'terminal.ansiGreen': '#d1f1a9',
|
||||
'terminal.ansiMagenta': '#ebbbff',
|
||||
'terminal.ansiRed': '#ff9da4',
|
||||
'terminal.ansiWhite': '#cccccc',
|
||||
'terminal.ansiYellow': '#ffeead',
|
||||
'titleBar.activeBackground': '#001126',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: [
|
||||
'meta.embedded',
|
||||
'source.groovy.embedded',
|
||||
'meta.jsx.children',
|
||||
'string meta.image.inline.markdown',
|
||||
'variable.legacy.builtin.python',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'comment',
|
||||
settings: {
|
||||
foreground: '#7285B7',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator.class, keyword.operator, constant.other, source.php.embedded.line',
|
||||
settings: {
|
||||
foreground: '#FFFFFF',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable, support.other.variable, string.other.link, string.regexp, entity.name.tag, entity.other.attribute-name, meta.tag, declaration.tag, markup.deleted.git_gutter',
|
||||
settings: {
|
||||
foreground: '#FF9DA4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.numeric, constant.language, support.constant, constant.character, variable.parameter, punctuation.section.embedded, keyword.other.unit',
|
||||
settings: {
|
||||
foreground: '#FFC58F',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.class, entity.name.type, entity.name.namespace, entity.name.scope-resolution, support.type, support.class',
|
||||
settings: {
|
||||
foreground: '#FFEEAD',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string, constant.other.symbol, entity.other.inherited-class, markup.heading, markup.inserted.git_gutter',
|
||||
settings: {
|
||||
foreground: '#D1F1A9',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator, constant.other.color',
|
||||
settings: {
|
||||
foreground: '#99FFFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.function, meta.function-call, support.function, keyword.other.special-method, meta.block-level, markup.changed.git_gutter',
|
||||
settings: {
|
||||
foreground: '#BBDAFF',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword, storage, storage.type, entity.name.tag.css',
|
||||
settings: {
|
||||
foreground: '#EBBBFF',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#A92049',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.separator',
|
||||
settings: {
|
||||
foreground: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid.deprecated',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted.diff, markup.deleted.diff, meta.diff.header.to-file, meta.diff.header.from-file',
|
||||
settings: {
|
||||
foreground: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted.diff, meta.diff.header.to-file',
|
||||
settings: {
|
||||
foreground: '#718C00',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted.diff, meta.diff.header.from-file',
|
||||
settings: {
|
||||
foreground: '#C82829',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.diff.header.from-file, meta.diff.header.to-file',
|
||||
settings: {
|
||||
foreground: '#4271AE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.diff.range',
|
||||
settings: {
|
||||
foreground: '#3E999F',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.quote',
|
||||
settings: {
|
||||
foreground: '#FFC58F',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.list',
|
||||
settings: {
|
||||
foreground: '#BBDAFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold, markup.italic',
|
||||
settings: {
|
||||
foreground: '#FFC58F',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.italic',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inline.raw',
|
||||
settings: {
|
||||
foreground: '#FF9DA4',
|
||||
fontStyle: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.heading',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#6796E6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#F44747',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#B267E6',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,412 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const vsDark: ThemeRegistrationAny = {
|
||||
$schema: 'vscode://schemas/color-theme',
|
||||
type: 'dark',
|
||||
colors: {
|
||||
'actionBar.toggledBackground': '#383a49',
|
||||
'activityBarBadge.background': '#007acc',
|
||||
'checkbox.border': '#6b6b6b',
|
||||
'editor.background': '#1e1e1e',
|
||||
'editor.foreground': '#d4d4d4',
|
||||
'editor.inactiveSelectionBackground': '#3a3d41',
|
||||
'editor.selectionHighlightBackground': '#add6ff26',
|
||||
'editorIndentGuide.activeBackground1': '#707070',
|
||||
'editorIndentGuide.background1': '#404040',
|
||||
'input.placeholderForeground': '#a6a6a6',
|
||||
'list.activeSelectionIconForeground': '#ffffff',
|
||||
'list.dropBackground': '#383b3d',
|
||||
'menu.background': '#252526',
|
||||
'menu.border': '#454545',
|
||||
'menu.foreground': '#cccccc',
|
||||
'menu.separatorBackground': '#454545',
|
||||
'ports.iconRunningProcessForeground': '#369432',
|
||||
'sideBarSectionHeader.background': '#00000000',
|
||||
'sideBarSectionHeader.border': '#cccccc33',
|
||||
'sideBarTitle.foreground': '#bbbbbb',
|
||||
'statusBarItem.remoteBackground': '#16825d',
|
||||
'statusBarItem.remoteForeground': '#ffffff',
|
||||
'tab.lastPinnedBorder': '#cccccc33',
|
||||
'terminal.inactiveSelectionBackground': '#3a3d41',
|
||||
'widget.border': '#303031',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: [
|
||||
'meta.embedded',
|
||||
'source.groovy.embedded',
|
||||
'string meta.image.inline.markdown',
|
||||
'variable.legacy.builtin.python',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'emphasis',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'strong',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'header',
|
||||
settings: {
|
||||
foreground: '#000080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'comment',
|
||||
settings: {
|
||||
foreground: '#6A9955',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.language',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'constant.numeric',
|
||||
'variable.other.enummember',
|
||||
'keyword.operator.plus.exponent',
|
||||
'keyword.operator.minus.exponent',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.regexp',
|
||||
settings: {
|
||||
foreground: '#646695',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag.css',
|
||||
settings: {
|
||||
foreground: '#D7BA7D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.attribute-name',
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'entity.other.attribute-name.class.css',
|
||||
'entity.other.attribute-name.class.mixin.css',
|
||||
'entity.other.attribute-name.id.css',
|
||||
'entity.other.attribute-name.parent-selector.css',
|
||||
'entity.other.attribute-name.pseudo-class.css',
|
||||
'entity.other.attribute-name.pseudo-element.css',
|
||||
'source.css.less entity.other.attribute-name.id',
|
||||
'entity.other.attribute-name.scss',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#D7BA7D',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#F44747',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.underline',
|
||||
settings: {
|
||||
fontStyle: 'underline',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.heading',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.italic',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.definition.quote.begin.markdown',
|
||||
settings: {
|
||||
foreground: '#6A9955',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.definition.list.begin.markdown',
|
||||
settings: {
|
||||
foreground: '#6796E6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inline.raw',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.definition.tag',
|
||||
settings: {
|
||||
foreground: '#808080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.preprocessor', 'entity.name.function.preprocessor'],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.string',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.numeric',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.structure.dictionary.key.python',
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.diff.header',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage.type',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.modifier', 'keyword.operator.noexcept'],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['string', 'meta.embedded.assembly'],
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.tag',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.value',
|
||||
settings: {
|
||||
foreground: '#CE9178',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.regexp',
|
||||
settings: {
|
||||
foreground: '#D16969',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.template-expression.begin',
|
||||
'punctuation.definition.template-expression.end',
|
||||
'punctuation.section.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.template.expression'],
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.type.vendored.property-name',
|
||||
'support.type.property-name',
|
||||
'variable.css',
|
||||
'variable.scss',
|
||||
'variable.other.less',
|
||||
'source.coffee.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.control',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator',
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword.operator.new',
|
||||
'keyword.operator.expression',
|
||||
'keyword.operator.cast',
|
||||
'keyword.operator.sizeof',
|
||||
'keyword.operator.alignof',
|
||||
'keyword.operator.typeid',
|
||||
'keyword.operator.alignas',
|
||||
'keyword.operator.instanceof',
|
||||
'keyword.operator.logical.python',
|
||||
'keyword.operator.wordlike',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.other.unit',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.section.embedded.begin.php', 'punctuation.section.embedded.end.php'],
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.function.git-rebase',
|
||||
settings: {
|
||||
foreground: '#9CDCFE',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.sha.git-rebase',
|
||||
settings: {
|
||||
foreground: '#B5CEA8',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.modifier.import.java', 'variable.language.wildcard.java', 'storage.modifier.package.java'],
|
||||
settings: {
|
||||
foreground: '#D4D4D4',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.language',
|
||||
settings: {
|
||||
foreground: '#569CD6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'ref.matchtext',
|
||||
settings: {
|
||||
foreground: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#6796E6',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#F44747',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#B267E6',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,435 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ThemeRegistrationAny } from 'shiki';
|
||||
|
||||
export const vsLight: ThemeRegistrationAny = {
|
||||
$schema: 'vscode://schemas/color-theme',
|
||||
type: 'light',
|
||||
colors: {
|
||||
'actionBar.toggledBackground': '#dddddd',
|
||||
'activityBarBadge.background': '#007acc',
|
||||
'checkbox.border': '#919191',
|
||||
'editor.background': '#ffffff',
|
||||
'editor.foreground': '#000000',
|
||||
'editor.inactiveSelectionBackground': '#e5ebf1',
|
||||
'editor.selectionHighlightBackground': '#add6ff80',
|
||||
'editorIndentGuide.activeBackground1': '#939393',
|
||||
'editorIndentGuide.background1': '#d3d3d3',
|
||||
'editorSuggestWidget.background': '#f3f3f3',
|
||||
'input.placeholderForeground': '#767676',
|
||||
'list.activeSelectionIconForeground': '#ffffff',
|
||||
'list.focusAndSelectionOutline': '#90c2f9',
|
||||
'list.hoverBackground': '#e8e8e8',
|
||||
'menu.border': '#d4d4d4',
|
||||
'notebook.cellBorderColor': '#e8e8e8',
|
||||
'notebook.selectedCellBackground': '#c8ddf150',
|
||||
'ports.iconRunningProcessForeground': '#369432',
|
||||
'searchEditor.textInputBorder': '#cecece',
|
||||
'settings.numberInputBorder': '#cecece',
|
||||
'settings.textInputBorder': '#cecece',
|
||||
'sideBarSectionHeader.background': '#00000000',
|
||||
'sideBarSectionHeader.border': '#61616130',
|
||||
'sideBarTitle.foreground': '#6f6f6f',
|
||||
'statusBarItem.errorBackground': '#c72e0f',
|
||||
'statusBarItem.remoteBackground': '#16825d',
|
||||
'statusBarItem.remoteForeground': '#ffffff',
|
||||
'tab.lastPinnedBorder': '#61616130',
|
||||
'terminal.inactiveSelectionBackground': '#e5ebf1',
|
||||
'widget.border': '#d4d4d4',
|
||||
},
|
||||
tokenColors: [
|
||||
{
|
||||
scope: [
|
||||
'meta.embedded',
|
||||
'source.groovy.embedded',
|
||||
'string meta.image.inline.markdown',
|
||||
'variable.legacy.builtin.python',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'emphasis',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'strong',
|
||||
settings: {
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.diff.header',
|
||||
settings: {
|
||||
foreground: '#000080',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'comment',
|
||||
settings: {
|
||||
foreground: '#008000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.language',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'constant.numeric',
|
||||
'variable.other.enummember',
|
||||
'keyword.operator.plus.exponent',
|
||||
'keyword.operator.minus.exponent',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#098658',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.regexp',
|
||||
settings: {
|
||||
foreground: '#811F3F',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.tag',
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.name.selector',
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'entity.other.attribute-name',
|
||||
settings: {
|
||||
foreground: '#E50000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'entity.other.attribute-name.class.css',
|
||||
'entity.other.attribute-name.class.mixin.css',
|
||||
'entity.other.attribute-name.id.css',
|
||||
'entity.other.attribute-name.parent-selector.css',
|
||||
'entity.other.attribute-name.pseudo-class.css',
|
||||
'entity.other.attribute-name.pseudo-element.css',
|
||||
'source.css.less entity.other.attribute-name.id',
|
||||
'entity.other.attribute-name.scss',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'invalid',
|
||||
settings: {
|
||||
foreground: '#CD3131',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.underline',
|
||||
settings: {
|
||||
fontStyle: 'underline',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.bold',
|
||||
settings: {
|
||||
foreground: '#000080',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.heading',
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
fontStyle: 'bold',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.italic',
|
||||
settings: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.strikethrough',
|
||||
settings: {
|
||||
fontStyle: 'strikethrough',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inserted',
|
||||
settings: {
|
||||
foreground: '#098658',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.deleted',
|
||||
settings: {
|
||||
foreground: '#A31515',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.changed',
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.definition.quote.begin.markdown', 'punctuation.definition.list.begin.markdown'],
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'markup.inline.raw',
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'punctuation.definition.tag',
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.preprocessor', 'entity.name.function.preprocessor'],
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.string',
|
||||
settings: {
|
||||
foreground: '#A31515',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.preprocessor.numeric',
|
||||
settings: {
|
||||
foreground: '#098658',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'meta.structure.dictionary.key.python',
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'storage.type',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.modifier', 'keyword.operator.noexcept'],
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['string', 'meta.embedded.assembly'],
|
||||
settings: {
|
||||
foreground: '#A31515',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'string.comment.buffered.block.pug',
|
||||
'string.quoted.pug',
|
||||
'string.interpolated.pug',
|
||||
'string.unquoted.plain.in.yaml',
|
||||
'string.unquoted.plain.out.yaml',
|
||||
'string.unquoted.block.yaml',
|
||||
'string.quoted.single.yaml',
|
||||
'string.quoted.double.xml',
|
||||
'string.quoted.single.xml',
|
||||
'string.unquoted.cdata.xml',
|
||||
'string.quoted.double.html',
|
||||
'string.quoted.single.html',
|
||||
'string.unquoted.html',
|
||||
'string.quoted.single.handlebars',
|
||||
'string.quoted.double.handlebars',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'string.regexp',
|
||||
settings: {
|
||||
foreground: '#811F3F',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'punctuation.definition.template-expression.begin',
|
||||
'punctuation.definition.template-expression.end',
|
||||
'punctuation.section.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['meta.template.expression'],
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.constant.property-value',
|
||||
'support.constant.font-name',
|
||||
'support.constant.media-type',
|
||||
'support.constant.media',
|
||||
'constant.other.color.rgb-value',
|
||||
'constant.other.rgb-value',
|
||||
'support.constant.color',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'support.type.vendored.property-name',
|
||||
'support.type.property-name',
|
||||
'variable.css',
|
||||
'variable.scss',
|
||||
'variable.other.less',
|
||||
'source.coffee.embedded',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#E50000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['support.type.property-name.json'],
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.control',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.operator',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
'keyword.operator.new',
|
||||
'keyword.operator.expression',
|
||||
'keyword.operator.cast',
|
||||
'keyword.operator.sizeof',
|
||||
'keyword.operator.alignof',
|
||||
'keyword.operator.typeid',
|
||||
'keyword.operator.alignas',
|
||||
'keyword.operator.instanceof',
|
||||
'keyword.operator.logical.python',
|
||||
'keyword.operator.wordlike',
|
||||
],
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'keyword.other.unit',
|
||||
settings: {
|
||||
foreground: '#098658',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['punctuation.section.embedded.begin.php', 'punctuation.section.embedded.end.php'],
|
||||
settings: {
|
||||
foreground: '#800000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'support.function.git-rebase',
|
||||
settings: {
|
||||
foreground: '#0451A5',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'constant.sha.git-rebase',
|
||||
settings: {
|
||||
foreground: '#098658',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: ['storage.modifier.import.java', 'variable.language.wildcard.java', 'storage.modifier.package.java'],
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'variable.language',
|
||||
settings: {
|
||||
foreground: '#0000FF',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'ref.matchtext',
|
||||
settings: {
|
||||
foreground: '#000000',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.info-token',
|
||||
settings: {
|
||||
foreground: '#316BCD',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.warn-token',
|
||||
settings: {
|
||||
foreground: '#CD9731',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.error-token',
|
||||
settings: {
|
||||
foreground: '#CD3131',
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: 'token.debug-token',
|
||||
settings: {
|
||||
foreground: '#800080',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export function getNonce() {
|
||||
let text = '';
|
||||
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
for (let i = 0; i < 32; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
export function pluralize(count: number, noun: string, suffix = 's') {
|
||||
return `${count} ${noun}${count !== 1 ? suffix : ''}`;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { commands, Disposable, languages, LanguageStatusItem, LanguageStatusSeverity, window, workspace } from 'vscode';
|
||||
import { IDisposable } from '../../../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { CopilotConfigPrefix } from '../../lib/src/constants';
|
||||
import { CMDQuotaExceeded } from '../../lib/src/openai/fetch';
|
||||
import { StatusChangedEvent, StatusReporter } from '../../lib/src/progress';
|
||||
import { isCompletionEnabled, isInlineSuggestEnabled } from './config';
|
||||
import { CMDToggleStatusMenuChat } from './constants';
|
||||
import { ICompletionsExtensionStatus } from './extensionStatus';
|
||||
import { Icon } from './icon';
|
||||
|
||||
export class CopilotStatusBar extends StatusReporter implements IDisposable {
|
||||
readonly item!: LanguageStatusItem;
|
||||
showingMessage = false;
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@ICompletionsExtensionStatus readonly extensionStatusService: ICompletionsExtensionStatus,
|
||||
@IInstantiationService readonly instantiationService: IInstantiationService,
|
||||
|
||||
) {
|
||||
super();
|
||||
|
||||
this.item = languages.createLanguageStatusItem(id, '*');
|
||||
this.disposables.push(this.item);
|
||||
|
||||
this.updateStatusBarIndicator();
|
||||
|
||||
this.disposables.push(
|
||||
window.onDidChangeActiveTextEditor(() => {
|
||||
this.updateStatusBarIndicator();
|
||||
})
|
||||
);
|
||||
|
||||
this.disposables.push(
|
||||
workspace.onDidCloseTextDocument(() => {
|
||||
this.updateStatusBarIndicator();
|
||||
})
|
||||
);
|
||||
|
||||
this.disposables.push(
|
||||
workspace.onDidOpenTextDocument(() => {
|
||||
this.updateStatusBarIndicator();
|
||||
})
|
||||
);
|
||||
|
||||
this.disposables.push(
|
||||
workspace.onDidChangeConfiguration(e => {
|
||||
if (!e.affectsConfiguration(CopilotConfigPrefix)) { return; }
|
||||
this.updateStatusBarIndicator();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override didChange(event: StatusChangedEvent): void {
|
||||
this.extensionStatusService.kind = event.kind;
|
||||
this.extensionStatusService.message = event.message;
|
||||
this.extensionStatusService.command = event.command;
|
||||
this.updateStatusBarIndicator();
|
||||
}
|
||||
|
||||
private checkEnabledForLanguage(): boolean {
|
||||
return this.instantiationService.invokeFunction(isCompletionEnabled) ?? true;
|
||||
}
|
||||
|
||||
protected updateStatusBarIndicator() {
|
||||
if (this.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
void commands.executeCommand(
|
||||
'setContext',
|
||||
'github.copilot.completions.quotaExceeded',
|
||||
this.extensionStatusService.command?.command === CMDQuotaExceeded
|
||||
);
|
||||
const enabled = this.checkEnabledForLanguage();
|
||||
void commands.executeCommand('setContext', 'github.copilot.completions.enabled', enabled);
|
||||
this.item.command = { command: CMDToggleStatusMenuChat, title: 'View Details' };
|
||||
switch (this.extensionStatusService.kind) {
|
||||
case 'Error':
|
||||
this.item.severity = LanguageStatusSeverity.Error;
|
||||
this.item.text = `${Icon.Warning} Completions`;
|
||||
this.item.detail = 'Error';
|
||||
break;
|
||||
case 'Warning':
|
||||
this.item.severity = LanguageStatusSeverity.Warning;
|
||||
this.item.text = `${Icon.Warning} Completions`;
|
||||
this.item.detail = 'Temporary issues';
|
||||
break;
|
||||
case 'Inactive':
|
||||
this.item.severity = LanguageStatusSeverity.Information;
|
||||
this.item.text = `${Icon.Blocked} Completions`;
|
||||
this.item.detail = 'Inactive';
|
||||
break;
|
||||
case 'Normal':
|
||||
this.item.severity = LanguageStatusSeverity.Information;
|
||||
if (!isInlineSuggestEnabled()) {
|
||||
this.item.text = `${Icon.NotConnected} Completions`;
|
||||
this.item.detail = 'VS Code inline suggestions disabled';
|
||||
} else if (!enabled) {
|
||||
this.item.text = `${Icon.NotConnected} Completions`;
|
||||
this.item.detail = 'Disabled';
|
||||
} else {
|
||||
this.item.text = `${Icon.Logo} Completions`;
|
||||
this.item.detail = '';
|
||||
}
|
||||
this.item.command.title = 'Open Menu';
|
||||
break;
|
||||
}
|
||||
this.item.accessibilityInformation = {
|
||||
label: 'Inline Suggestions',
|
||||
};
|
||||
if (this.extensionStatusService.command) {
|
||||
this.item.command = this.extensionStatusService.command;
|
||||
this.item.detail = this.extensionStatusService.message;
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
for (const d of this.disposables) {
|
||||
d.dispose();
|
||||
}
|
||||
this.disposables = [];
|
||||
}
|
||||
|
||||
private isDisposed() {
|
||||
return this.disposables.length === 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { QuickPick, QuickPickItem, QuickPickItemKind, commands, l10n, window } from 'vscode';
|
||||
import { isWeb } from '../../../../../util/vs/base/common/platform';
|
||||
import { IInstantiationService } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { isCompletionEnabled, isInlineSuggestEnabled } from './config';
|
||||
import { CMDCollectDiagnosticsChat, CMDDisableCompletionsChat, CMDEnableCompletionsChat, CMDOpenDocumentationClient, CMDOpenLogsClient, CMDOpenModelPickerClient, CMDOpenPanelClient } from './constants';
|
||||
import { ICompletionsExtensionStatus } from './extensionStatus';
|
||||
import { Icon } from './icon';
|
||||
|
||||
export class CopilotStatusBarPickMenu {
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ICompletionsExtensionStatus private readonly extensionStatusService: ICompletionsExtensionStatus,
|
||||
) { }
|
||||
|
||||
showStatusMenu() {
|
||||
const quickpickList = window.createQuickPick();
|
||||
quickpickList.placeholder = l10n.t('Select an option');
|
||||
quickpickList.title = l10n.t('Configure Inline Suggestions');
|
||||
quickpickList.items = this.collectQuickPickItems();
|
||||
quickpickList.onDidAccept(() => this.handleItemSelection(quickpickList));
|
||||
quickpickList.show();
|
||||
return quickpickList;
|
||||
}
|
||||
|
||||
async handleItemSelection(quickpickList: QuickPick<QuickPickItem>): Promise<void> {
|
||||
const selection = quickpickList.selectedItems[0];
|
||||
if (selection === undefined) { return; }
|
||||
|
||||
if ('command' in selection) {
|
||||
const commandSelection = selection as CommandQuickItem;
|
||||
await commands.executeCommand(commandSelection.command, ...commandSelection.commandArgs);
|
||||
quickpickList.hide();
|
||||
} else {
|
||||
throw new Error('Unexpected Copilot quick picker selection');
|
||||
}
|
||||
}
|
||||
|
||||
private collectQuickPickItems() {
|
||||
return [
|
||||
this.newStatusItem(),
|
||||
this.newSeparator(),
|
||||
...this.collectLanguageSpecificItems(),
|
||||
this.newKeyboardItem(),
|
||||
this.newSettingsItem(),
|
||||
...this.collectDiagnosticsItems(),
|
||||
this.newOpenLogsItem(),
|
||||
this.newSeparator(),
|
||||
this.newDocsItem(),
|
||||
//this.newForumItem(),
|
||||
];
|
||||
}
|
||||
|
||||
private collectLanguageSpecificItems() {
|
||||
const items: QuickPickItem[] = [];
|
||||
if (!this.hasActiveStatus()) { return items; }
|
||||
|
||||
const editor = window.activeTextEditor;
|
||||
if (!isWeb && editor) { items.push(this.newPanelItem()); }
|
||||
// Always show the model picker even if only one model is available
|
||||
if (!isWeb) { items.push(this.newChangeModelItem()); }
|
||||
if (editor) { items.push(...this.newEnableLanguageItem()); }
|
||||
if (items.length) { items.push(this.newSeparator()); }
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private hasActiveStatus() {
|
||||
return ['Normal'].includes(this.extensionStatusService.kind);
|
||||
}
|
||||
|
||||
private isCompletionEnabled() {
|
||||
return isInlineSuggestEnabled() && this.instantiationService.invokeFunction(isCompletionEnabled);
|
||||
}
|
||||
|
||||
private newEnableLanguageItem() {
|
||||
const isEnabled = this.isCompletionEnabled();
|
||||
if (isEnabled) {
|
||||
return [this.newCommandItem(l10n.t('Disable Inline Suggestions'), CMDDisableCompletionsChat)];
|
||||
} else if (isEnabled === false) {
|
||||
return [this.newCommandItem(l10n.t('Enable Inline Suggestions'), CMDEnableCompletionsChat)];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private newStatusItem() {
|
||||
let statusText;
|
||||
let statusIcon = Icon.Logo;
|
||||
switch (this.extensionStatusService.kind) {
|
||||
case 'Normal':
|
||||
statusText = l10n.t('Ready');
|
||||
if (isInlineSuggestEnabled() === false) {
|
||||
statusText += ` (${l10n.t('VS Code inline suggestions disabled')})`;
|
||||
} else if (this.instantiationService.invokeFunction(isCompletionEnabled) === false) {
|
||||
statusText += ` (${l10n.t('Disabled')})`;
|
||||
}
|
||||
break;
|
||||
case 'Inactive':
|
||||
statusText = this.extensionStatusService.message || l10n.t('Copilot is currently inactive');
|
||||
statusIcon = Icon.Blocked;
|
||||
break;
|
||||
default:
|
||||
statusText = this.extensionStatusService.message || l10n.t('Copilot has encountered an error');
|
||||
statusIcon = Icon.NotConnected;
|
||||
break;
|
||||
}
|
||||
return this.newCommandItem(`${statusIcon} ${l10n.t('Status')}: ${statusText}`, CMDOpenLogsClient);
|
||||
}
|
||||
|
||||
private newOpenLogsItem() {
|
||||
return this.newCommandItem(l10n.t('Open Logs...'), CMDOpenLogsClient);
|
||||
}
|
||||
|
||||
private collectDiagnosticsItems() {
|
||||
if (isWeb) { return []; }
|
||||
return [this.newCommandItem(l10n.t('Show Diagnostics...'), CMDCollectDiagnosticsChat)];
|
||||
}
|
||||
|
||||
private newKeyboardItem() {
|
||||
return this.newCommandItem(l10n.t('$(keyboard) Edit Keyboard Shortcuts...'), 'workbench.action.openGlobalKeybindings', [
|
||||
'copilot',
|
||||
]);
|
||||
}
|
||||
|
||||
private newSettingsItem() {
|
||||
return this.newCommandItem(l10n.t('$(settings-gear) Edit Settings...'), 'workbench.action.openSettings', [
|
||||
'GitHub Copilot',
|
||||
]);
|
||||
}
|
||||
|
||||
private newPanelItem() {
|
||||
return this.newCommandItem(l10n.t('Open Completions Panel...'), CMDOpenPanelClient);
|
||||
}
|
||||
|
||||
private newChangeModelItem() {
|
||||
return this.newCommandItem(l10n.t('Change Completions Model...'), CMDOpenModelPickerClient);
|
||||
}
|
||||
|
||||
private newDocsItem() {
|
||||
return this.newCommandItem(
|
||||
l10n.t('$(remote-explorer-documentation) View Copilot Documentation...'),
|
||||
CMDOpenDocumentationClient
|
||||
);
|
||||
}
|
||||
|
||||
private newCommandItem(label: string, command: string, commandArgs?: string[]): CommandQuickItem {
|
||||
return new CommandQuickItem(label, command, commandArgs || []);
|
||||
}
|
||||
|
||||
private newSeparator(): QuickPickItem {
|
||||
return {
|
||||
label: '',
|
||||
kind: QuickPickItemKind.Separator,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class CommandQuickItem implements QuickPickItem {
|
||||
constructor(
|
||||
readonly label: string,
|
||||
readonly command: string,
|
||||
readonly commandArgs: string[]
|
||||
) { }
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { commands, Disposable } from 'vscode';
|
||||
import { IDisposable } from '../../../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService, type ServicesAccessor } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { handleException } from '../../lib/src/defaultHandlers';
|
||||
import { Logger } from '../../lib/src/logger';
|
||||
|
||||
function exception(accessor: ServicesAccessor, error: unknown, origin: string, logger?: Logger) {
|
||||
if (error instanceof Error && error.name === 'Canceled') {
|
||||
// these are VS Code cancellations
|
||||
return;
|
||||
}
|
||||
if (error instanceof Error && error.name === 'CodeExpectedError') {
|
||||
// expected errors from VS Code
|
||||
return;
|
||||
}
|
||||
handleException(accessor, error, origin, logger);
|
||||
}
|
||||
|
||||
export function registerCommand(accessor: ServicesAccessor, command: string, fn: (...args: unknown[]) => unknown): Disposable {
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
try {
|
||||
const disposable = commands.registerCommand(command, async (...args: unknown[]) => {
|
||||
try {
|
||||
await fn(...args);
|
||||
} catch (error) {
|
||||
// Pass in the command string as the origin
|
||||
instantiationService.invokeFunction(exception, error, command);
|
||||
}
|
||||
});
|
||||
return disposable;
|
||||
} catch (error) {
|
||||
console.error(`Error registering command ${command}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper that handles errors and cleans up the command on extension deactivation
|
||||
export function registerCommandWrapper(accessor: ServicesAccessor, command: string, fn: (...args: unknown[]) => unknown): IDisposable {
|
||||
return registerCommand(accessor, command, fn);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ConfigKey, ConfigKeyType, DefaultsOnlyConfigProvider, InMemoryConfigProvider } from '../../../lib/src/config';
|
||||
import { VSCodeConfigProvider } from '../config';
|
||||
|
||||
/**
|
||||
* Provides the default configurations, except lets through the configured value
|
||||
* of test-only settings like the proxy override URL.
|
||||
*/
|
||||
export class ExtensionTestConfigProvider extends InMemoryConfigProvider {
|
||||
private readonly vscConfigProvider = new VSCodeConfigProvider();
|
||||
|
||||
constructor() {
|
||||
super(new DefaultsOnlyConfigProvider());
|
||||
}
|
||||
|
||||
override getConfig<T>(key: ConfigKeyType): T {
|
||||
if (key === ConfigKey.DebugTestOverrideProxyUrl) {
|
||||
return this.vscConfigProvider.getConfig<T>(key);
|
||||
}
|
||||
return super.getConfig(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { SyncDescriptor } from '../../../../../../util/vs/platform/instantiation/common/descriptors';
|
||||
import { createExtensionTestingServices } from '../../../../../test/vscode-node/services';
|
||||
import { ICompletionsEditorAndPluginInfo } from '../../../lib/src/config';
|
||||
import { ICompletionsFileSystemService } from '../../../lib/src/fileSystem';
|
||||
import { ICompletionsFetcherService } from '../../../lib/src/networking';
|
||||
import { _createBaselineContext } from '../../../lib/src/test/context';
|
||||
import { StaticFetcher } from '../../../lib/src/test/fetcher';
|
||||
import { ICompletionsTextDocumentManagerService } from '../../../lib/src/textDocumentManager';
|
||||
import { VSCodeEditorInfo } from '../config';
|
||||
import { CopilotExtensionStatus, ICompletionsExtensionStatus } from '../extensionStatus';
|
||||
import { extensionFileSystem } from '../fileSystem';
|
||||
import { ExtensionTextDocumentManager } from '../textDocumentManager';
|
||||
import { ExtensionTestConfigProvider } from './config';
|
||||
|
||||
/**
|
||||
* A default context for VSCode extension testing, building on general one in `lib`.
|
||||
* Only includes items that are needed for almost all extension tests.
|
||||
*/
|
||||
export function createExtensionTestingContext() {
|
||||
let serviceCollection = createExtensionTestingServices();
|
||||
serviceCollection = _createBaselineContext(serviceCollection, new ExtensionTestConfigProvider());
|
||||
|
||||
serviceCollection.define(ICompletionsFetcherService, new StaticFetcher());
|
||||
serviceCollection.define(ICompletionsEditorAndPluginInfo, new VSCodeEditorInfo());
|
||||
serviceCollection.define(ICompletionsTextDocumentManagerService, new SyncDescriptor(ExtensionTextDocumentManager));
|
||||
serviceCollection.define(ICompletionsFileSystemService, extensionFileSystem);
|
||||
serviceCollection.define(ICompletionsExtensionStatus, new CopilotExtensionStatus());
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import assert from 'assert';
|
||||
import sinon from 'sinon';
|
||||
import { commands, env } from 'vscode';
|
||||
import { SyncDescriptor } from '../../../../../../util/vs/platform/instantiation/common/descriptors';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { AvailableModelsManager, ICompletionsModelManagerService } from '../../../lib/src/openai/model';
|
||||
import { ModelPickerManager } from './../modelPicker';
|
||||
import { createExtensionTestingContext } from './context';
|
||||
|
||||
suite('ModelPickerManager unit tests', function () {
|
||||
let accessor: ServicesAccessor;
|
||||
let modelPicker: ModelPickerManager;
|
||||
let availableModelsManager: ICompletionsModelManagerService;
|
||||
let sandbox: sinon.SinonSandbox;
|
||||
|
||||
// Couple of fake models to use in our tests.
|
||||
const fakeModels = [
|
||||
{
|
||||
modelId: 'model-a',
|
||||
label: 'Model A',
|
||||
type: 'model',
|
||||
alwaysShow: true,
|
||||
preview: false,
|
||||
tokenizer: 'o200k_base',
|
||||
},
|
||||
{
|
||||
modelId: 'model-b',
|
||||
label: 'Model B',
|
||||
type: 'model',
|
||||
alwaysShow: true,
|
||||
preview: false,
|
||||
tokenizer: 'cl100k_base',
|
||||
},
|
||||
];
|
||||
|
||||
setup(function () {
|
||||
sandbox = sinon.createSandbox();
|
||||
// Create our test context, and stub the AvailableModelsManager to return our fake models.
|
||||
const serviceCollection = createExtensionTestingContext();
|
||||
serviceCollection.define(ICompletionsModelManagerService, new SyncDescriptor(AvailableModelsManager, [true]));
|
||||
accessor = serviceCollection.createTestingAccessor();
|
||||
|
||||
availableModelsManager = accessor.get(ICompletionsModelManagerService);
|
||||
sandbox.stub(availableModelsManager, 'getGenericCompletionModels').returns(fakeModels);
|
||||
modelPicker = accessor.get(IInstantiationService).createInstance(ModelPickerManager);
|
||||
});
|
||||
|
||||
teardown(async function () {
|
||||
// Make sure to close any open quick pick dialogs after each test.
|
||||
await commands.executeCommand('workbench.action.closeQuickOpen');
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
test('showModelPicker returns correct items', function () {
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
|
||||
modelPicker = instantiationService.createInstance(ModelPickerManager);
|
||||
|
||||
const quickPick = modelPicker.showModelPicker();
|
||||
|
||||
// Check that we have the correct number of items
|
||||
// The items should include the two fake models, a separator, and a learn more item.
|
||||
assert(quickPick.items.length === 4, quickPick.items.length.toString());
|
||||
assert.strictEqual(quickPick.items[0].modelId, 'model-a');
|
||||
assert.strictEqual(quickPick.items[1].modelId, 'model-b');
|
||||
assert.strictEqual(quickPick.items[2].type, 'separator');
|
||||
assert.strictEqual(quickPick.items[3].type, 'learn-more');
|
||||
});
|
||||
|
||||
test('selecting a model updates user selection', async function () {
|
||||
// Stub out setting model
|
||||
const setModelStub = sandbox.stub(modelPicker, 'setUserSelectedCompletionModel').resolves();
|
||||
|
||||
const quickPick = modelPicker.showModelPicker();
|
||||
|
||||
const secondItem = quickPick.items[1];
|
||||
assert(secondItem !== undefined, 'model picker should have a model-b second item.');
|
||||
|
||||
// Fake selecting the second item
|
||||
quickPick.activeItems = [secondItem];
|
||||
await modelPicker.handleModelSelection(quickPick);
|
||||
|
||||
// Test that we updated the user configuration with the selected model
|
||||
assert(setModelStub.calledOnce, 'setUserSelectedCompletionModel should be called once');
|
||||
assert.strictEqual(setModelStub.firstCall.args[0], secondItem.modelId);
|
||||
});
|
||||
|
||||
test('selecting the learn more link tries to open the learn more url', async function () {
|
||||
// Stub openExternal
|
||||
const openUrlStub = sandbox.stub(env, 'openExternal').resolves();
|
||||
|
||||
const quickPick = modelPicker.showModelPicker();
|
||||
|
||||
const learnMoreItem = quickPick.items[3];
|
||||
assert(learnMoreItem !== undefined, 'model picker should have a learn more item.');
|
||||
|
||||
// Fake selecting the learn more item
|
||||
quickPick.activeItems = [learnMoreItem];
|
||||
await modelPicker.handleModelSelection(quickPick);
|
||||
|
||||
// Test that we opened the learn more URL
|
||||
assert(openUrlStub.calledOnce, 'openUrl should be called once');
|
||||
assert.strictEqual(
|
||||
openUrlStub.firstCall.args[0].toString(),
|
||||
'https://aka.ms/CopilotCompletionsModelPickerLearnMore'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,123 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { window, workspace } from 'vscode';
|
||||
import { detectLanguage } from '../../lib/src/language/languageDetection';
|
||||
import { CopilotTextDocument, INotebookCell, INotebookDocument, ITextDocument } from '../../lib/src/textDocument';
|
||||
import { TextDocumentManager, WorkspaceFoldersChangeEvent } from '../../lib/src/textDocumentManager';
|
||||
import { transformEvent } from '../../lib/src/util/event';
|
||||
import { normalizeUri } from '../../lib/src/util/uri';
|
||||
|
||||
// List of document URI schemes that avoid ghost text suggestions
|
||||
const ignoreUriSchemes = new Set([
|
||||
'output', // vscode output pane (important: avoids infinite log loop)
|
||||
'search-editor', // search results virtual document
|
||||
'comment', // very little context available and suggestions are often bad
|
||||
'git', // virtual file tracked by git
|
||||
'chat-editing-snapshot-text-model', // VS Code Chat temporary editing snapshot
|
||||
]);
|
||||
|
||||
export function wrapDoc(doc: vscode.TextDocument): ITextDocument | undefined {
|
||||
if (ignoreUriSchemes.has(doc.uri.scheme)) {
|
||||
return;
|
||||
}
|
||||
let text: string;
|
||||
try {
|
||||
text = doc.getText();
|
||||
} catch (e) {
|
||||
// "Invalid string length", it's too big to fit in a string
|
||||
if (e instanceof RangeError) {
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
const languageId = detectLanguage({ uri: doc.uri.toString(), languageId: doc.languageId });
|
||||
return CopilotTextDocument.create(doc.uri.toString(), doc.languageId, doc.version, text, languageId);
|
||||
}
|
||||
|
||||
export class ExtensionTextDocumentManager extends TextDocumentManager {
|
||||
override onDidFocusTextDocument = transformEvent(window.onDidChangeActiveTextEditor, event => {
|
||||
return { document: event && { uri: event.document.uri.toString() } };
|
||||
});
|
||||
|
||||
override onDidChangeTextDocument = transformEvent(workspace.onDidChangeTextDocument, e => {
|
||||
const document = wrapDoc(e.document);
|
||||
return document && { document, contentChanges: e.contentChanges };
|
||||
});
|
||||
|
||||
override onDidOpenTextDocument = transformEvent(workspace.onDidOpenTextDocument, e => {
|
||||
// use wrapDoc() to handle the "Invalid string length" case
|
||||
const text = wrapDoc(e)?.getText();
|
||||
if (text === undefined) {
|
||||
return;
|
||||
}
|
||||
return { document: { uri: e.uri.toString(), languageId: e.languageId, version: e.version, text } };
|
||||
});
|
||||
|
||||
override onDidCloseTextDocument = transformEvent(workspace.onDidCloseTextDocument, e => {
|
||||
return { document: { uri: normalizeUri(e.uri.toString()) } };
|
||||
});
|
||||
|
||||
override onDidChangeWorkspaceFolders = transformEvent(
|
||||
workspace.onDidChangeWorkspaceFolders,
|
||||
(e): WorkspaceFoldersChangeEvent => {
|
||||
return {
|
||||
workspaceFolders: this.getWorkspaceFolders(),
|
||||
added: e.added.map(f => ({ uri: f.uri.toString(), name: f.name })),
|
||||
removed: e.removed.map(f => ({ uri: f.uri.toString(), name: f.name })),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
getTextDocumentsUnsafe(): ITextDocument[] {
|
||||
const docs: ITextDocument[] = [];
|
||||
for (const vscodeDoc of workspace.textDocuments) {
|
||||
const doc = wrapDoc(vscodeDoc);
|
||||
if (doc) {
|
||||
docs.push(doc);
|
||||
}
|
||||
}
|
||||
return docs;
|
||||
}
|
||||
|
||||
findNotebook(doc: { uri: string }): INotebookDocument | undefined {
|
||||
for (const notebook of workspace.notebookDocuments) {
|
||||
if (notebook.getCells().some(cell => cell.document.uri.toString() === doc.uri.toString())) {
|
||||
return {
|
||||
getCells: () => notebook.getCells().map(cell => this.wrapCell(cell)),
|
||||
getCellFor: ({ uri }: { uri: string }) => {
|
||||
const cell = notebook.getCells().find(cell => cell.document.uri.toString() === uri.toString());
|
||||
return cell ? this.wrapCell(cell) : undefined;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wrapCell(cell: vscode.NotebookCell): INotebookCell {
|
||||
return {
|
||||
...cell,
|
||||
get document(): ITextDocument {
|
||||
return CopilotTextDocument.create(
|
||||
cell.document.uri.toString(),
|
||||
cell.document.languageId,
|
||||
cell.document.version,
|
||||
cell.document.getText(),
|
||||
// use the original language id as cells have no metadata to leverage for language detection
|
||||
cell.document.languageId
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getWorkspaceFolders() {
|
||||
return (
|
||||
workspace.workspaceFolders?.map(f => {
|
||||
return { uri: f.uri.toString(), name: f.name };
|
||||
}) ?? []
|
||||
);
|
||||
}
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import {
|
||||
CancellationToken,
|
||||
InlineCompletionContext,
|
||||
InlineCompletionEndOfLifeReason,
|
||||
InlineCompletionItemProvider,
|
||||
InlineCompletionList,
|
||||
InlineCompletionTriggerKind,
|
||||
PartialAcceptInfo,
|
||||
Position,
|
||||
TextDocument,
|
||||
workspace
|
||||
} from 'vscode';
|
||||
import { Disposable } from '../../../../../util/vs/base/common/lifecycle';
|
||||
import { LineEdit } from '../../../../../util/vs/editor/common/core/edits/lineEdit';
|
||||
import { TextEdit, TextReplacement } from '../../../../../util/vs/editor/common/core/edits/textEdit';
|
||||
import { Range } from '../../../../../util/vs/editor/common/core/range';
|
||||
import { LineBasedText } from '../../../../../util/vs/editor/common/core/text/abstractText';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { InlineEditLogger } from '../../../../inlineEdits/vscode-node/parts/inlineEditLogger';
|
||||
import { GhostTextContext } from '../../../common/ghostTextContext';
|
||||
import { ICompletionsTelemetryService } from '../../bridge/src/completionsTelemetryServiceBridge';
|
||||
import { BuildInfo } from '../../lib/src/config';
|
||||
import { CopilotConfigPrefix } from '../../lib/src/constants';
|
||||
import { handleException } from '../../lib/src/defaultHandlers';
|
||||
import { Logger } from '../../lib/src/logger';
|
||||
import { isCompletionEnabledForDocument } from './config';
|
||||
import { CopilotCompletionFeedbackTracker, sendCompletionFeedbackCommand } from './copilotCompletionFeedbackTracker';
|
||||
import { ICompletionsExtensionStatus } from './extensionStatus';
|
||||
import { GhostTextCompletionItem, GhostTextCompletionList, GhostTextProvider } from './ghostText/ghostTextProvider';
|
||||
|
||||
const logger = new Logger('inlineCompletionItemProvider');
|
||||
|
||||
function quickSuggestionsDisabled() {
|
||||
const qs = workspace.getConfiguration('editor.quickSuggestions');
|
||||
return qs.get('other') !== 'on' && qs.get('comments') !== 'on' && qs.get('strings') !== 'on';
|
||||
}
|
||||
|
||||
export function exception(accessor: ServicesAccessor, error: unknown, origin: string, logger?: Logger) {
|
||||
if (error instanceof Error && error.name === 'Canceled') {
|
||||
// these are VS Code cancellations
|
||||
return;
|
||||
}
|
||||
if (error instanceof Error && error.name === 'CodeExpectedError') {
|
||||
// expected errors from VS Code
|
||||
return;
|
||||
}
|
||||
const telemetryService = accessor.get(ICompletionsTelemetryService);
|
||||
telemetryService.sendGHTelemetryException(error, 'codeUnification.completions.exception');
|
||||
handleException(accessor, error, origin, logger);
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export class CopilotInlineCompletionItemProvider extends Disposable implements InlineCompletionItemProvider {
|
||||
private readonly copilotCompletionFeedbackTracker: CopilotCompletionFeedbackTracker;
|
||||
private readonly ghostTextProvider: GhostTextProvider;
|
||||
private readonly inlineEditLogger: InlineEditLogger;
|
||||
|
||||
public onDidChange = undefined;
|
||||
public handleListEndOfLifetime: InlineCompletionItemProvider['handleListEndOfLifetime'] = undefined;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ICompletionsTelemetryService private readonly telemetryService: ICompletionsTelemetryService,
|
||||
@ICompletionsExtensionStatus private readonly extensionStatusService: ICompletionsExtensionStatus,
|
||||
) {
|
||||
super();
|
||||
this.copilotCompletionFeedbackTracker = this._register(this.instantiationService.createInstance(CopilotCompletionFeedbackTracker));
|
||||
this.ghostTextProvider = this.instantiationService.createInstance(GhostTextProvider);
|
||||
this.inlineEditLogger = this.instantiationService.createInstance(InlineEditLogger);
|
||||
}
|
||||
|
||||
async provideInlineCompletionItems(
|
||||
doc: TextDocument,
|
||||
position: Position,
|
||||
context: InlineCompletionContext,
|
||||
token: CancellationToken
|
||||
): Promise<GhostTextCompletionList | undefined> {
|
||||
const logContext = new GhostTextContext(doc.uri.toString(), doc.version, context);
|
||||
try {
|
||||
return await this._provideInlineCompletionItems(doc, position, context, logContext, token);
|
||||
} catch (e) {
|
||||
logContext.setError(e);
|
||||
this.telemetryService.sendGHTelemetryException(e, 'codeUnification.completions.exception');
|
||||
} finally {
|
||||
this.inlineEditLogger.add(logContext);
|
||||
}
|
||||
}
|
||||
|
||||
private async _provideInlineCompletionItems(
|
||||
doc: TextDocument,
|
||||
position: Position,
|
||||
context: InlineCompletionContext,
|
||||
logContext: GhostTextContext,
|
||||
token: CancellationToken
|
||||
): Promise<GhostTextCompletionList | undefined> {
|
||||
if (context.triggerKind === InlineCompletionTriggerKind.Automatic) {
|
||||
if (!this.instantiationService.invokeFunction(isCompletionEnabledForDocument, doc)) {
|
||||
return;
|
||||
}
|
||||
if (this.extensionStatusService.kind === 'Error') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const copilotConfig = workspace.getConfiguration(CopilotConfigPrefix);
|
||||
// Constraining the generated inline completion to match selectedCompletionInfo sandbags Copilot pretty hard, as
|
||||
// typically it's just the first entry in the list alphabetically. But if we generate a result that doesn't
|
||||
// match it, VS Code won't show it to the user unless the completion dropdown is dismissed. Historically we've
|
||||
// chosen to favor completion quality, but this option allows opting into or out of generating a completion that
|
||||
// VS Code will actually show.
|
||||
if (!copilotConfig.get('respectSelectedCompletionInfo', quickSuggestionsDisabled() || BuildInfo.isPreRelease())) {
|
||||
context = { ...context, selectedCompletionInfo: undefined };
|
||||
}
|
||||
|
||||
try {
|
||||
let items = await this.ghostTextProvider.provideInlineCompletionItems(doc, position, context, token);
|
||||
|
||||
if (!items) {
|
||||
if (token.isCancellationRequested) {
|
||||
logContext.setIsSkipped();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If the language client provides a list of items, we want to add the send feedback command to it.
|
||||
if (Array.isArray(items)) {
|
||||
items = { items };
|
||||
}
|
||||
|
||||
this.logSuggestion(logContext, doc, items);
|
||||
|
||||
return {
|
||||
...items,
|
||||
commands: [sendCompletionFeedbackCommand],
|
||||
};
|
||||
} catch (e) {
|
||||
this.instantiationService.invokeFunction(exception, e, '._provideInlineCompletionItems', logger);
|
||||
logContext.setError(e);
|
||||
}
|
||||
}
|
||||
|
||||
handleDidShowCompletionItem(item: GhostTextCompletionItem) {
|
||||
try {
|
||||
this.copilotCompletionFeedbackTracker.trackItem(item);
|
||||
return this.ghostTextProvider.handleDidShowCompletionItem(item);
|
||||
} catch (e) {
|
||||
this.instantiationService.invokeFunction(exception, e, '.handleDidShowCompletionItem', logger);
|
||||
}
|
||||
}
|
||||
|
||||
handleDidPartiallyAcceptCompletionItem(
|
||||
item: GhostTextCompletionItem,
|
||||
acceptedLengthOrInfo: number | PartialAcceptInfo
|
||||
) {
|
||||
try {
|
||||
return this.ghostTextProvider.handleDidPartiallyAcceptCompletionItem(item, acceptedLengthOrInfo);
|
||||
} catch (e) {
|
||||
this.instantiationService.invokeFunction(exception, e, '.handleDidPartiallyAcceptCompletionItem', logger);
|
||||
}
|
||||
}
|
||||
|
||||
handleEndOfLifetime(completionItem: GhostTextCompletionItem, reason: InlineCompletionEndOfLifeReason) {
|
||||
try {
|
||||
return this.ghostTextProvider.handleEndOfLifetime(completionItem, reason);
|
||||
} catch (e) {
|
||||
this.instantiationService.invokeFunction(exception, e, '.handleEndOfLifetime', logger);
|
||||
}
|
||||
}
|
||||
|
||||
private logSuggestion(
|
||||
logContext: GhostTextContext,
|
||||
doc: TextDocument,
|
||||
items: InlineCompletionList
|
||||
) {
|
||||
if (items.items.length === 0) {
|
||||
logContext.markAsNoSuggestions();
|
||||
logContext.addLog('No inline completion items provided');
|
||||
return;
|
||||
}
|
||||
const firstItem = items.items[0];
|
||||
if (!firstItem.range) {
|
||||
logContext.addLog('Inline completion item has no range');
|
||||
return;
|
||||
}
|
||||
if (typeof firstItem.insertText !== 'string') {
|
||||
logContext.addLog('Inline completion item has non-string insertText');
|
||||
return;
|
||||
}
|
||||
|
||||
const text = new LineBasedText(lineNumber => doc.lineAt(lineNumber - 1).text, doc.lineCount);
|
||||
|
||||
const lineEdit = LineEdit.fromTextEdit(
|
||||
new TextEdit(
|
||||
[new TextReplacement(
|
||||
new Range(firstItem.range.start.line + 1, firstItem.range.start.character + 1, firstItem.range.end.line + 1, firstItem.range.end.character + 1),
|
||||
firstItem.insertText,
|
||||
)],
|
||||
),
|
||||
text
|
||||
);
|
||||
|
||||
const patch = lineEdit.humanReadablePatch(text.getLines());
|
||||
|
||||
logContext.setResult(patch);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
require('tsx/cjs');
|
||||
|
||||
const { globSync } = require('glob');
|
||||
const Mocha = require('mocha');
|
||||
const path = require('path');
|
||||
const dotenv = require('dotenv');
|
||||
const envfile = path.join(__dirname, '../../../../../../.env');
|
||||
dotenv.config({ path: envfile });
|
||||
|
||||
function run() {
|
||||
const projectRoot = path.resolve(__dirname, '../..');
|
||||
const mochaOptions = {
|
||||
ui: 'tdd',
|
||||
color: !process.env.NO_COLOR && process.env.TERM !== 'dumb',
|
||||
reporter: 'mocha-multi-reporters',
|
||||
reporterOptions: {
|
||||
reporterEnabled: 'spec',
|
||||
},
|
||||
};
|
||||
if (process.env.MOCHA_GREP) {
|
||||
mochaOptions.grep = process.env.MOCHA_GREP;
|
||||
}
|
||||
if (process.env.CI) {
|
||||
mochaOptions.forbidOnly = true;
|
||||
mochaOptions.retries = 2;
|
||||
mochaOptions.reporterOptions.reporterEnabled += ', mocha-junit-reporter';
|
||||
mochaOptions.reporterOptions.mochaJunitReporterReporterOptions = {
|
||||
testCaseSwitchClassnameAndName: true,
|
||||
testsuitesTitle: 'Copilot VS Code Extension Tests',
|
||||
mochaFile: path.resolve(projectRoot, 'test-results-Extension.xml'),
|
||||
};
|
||||
}
|
||||
if (process.env.GITHUB_EVENT_NAME === 'merge_group') {
|
||||
mochaOptions.retries = 3;
|
||||
}
|
||||
|
||||
// Create the mocha test
|
||||
const mocha = new Mocha(mochaOptions);
|
||||
|
||||
let fileCount = 0;
|
||||
(process.env.MOCHA_FILES || [
|
||||
path.resolve(projectRoot, 'lib/src/**/*.test.{ts,tsx}'),
|
||||
path.resolve(projectRoot, 'extension/src/**/*.test.{ts,tsx}')
|
||||
].join('\n')).split('\n').forEach(f => {
|
||||
globSync(f, { windowsPathsNoEscape: true }).forEach(f => {
|
||||
fileCount++;
|
||||
mocha.addFile(f);
|
||||
});
|
||||
});
|
||||
if (!fileCount) {
|
||||
throw new Error('No tests to run');
|
||||
}
|
||||
|
||||
return new Promise((c, e) => {
|
||||
try {
|
||||
// Run the mocha test
|
||||
mocha.run(failures => {
|
||||
if (failures > 0) {
|
||||
e(new Error(`${failures} tests failed.`));
|
||||
} else {
|
||||
c();
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
e(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { run };
|
||||
@@ -0,0 +1,79 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { promises as fs } from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
|
||||
import { runTests } from '@vscode/test-electron';
|
||||
|
||||
async function main() {
|
||||
const tempdir = await fs.mkdtemp(os.tmpdir() + '/copilot-extension-test-');
|
||||
|
||||
let exitCode;
|
||||
try {
|
||||
// The folder containing the Extension Manifest package.json
|
||||
// Passed to `--extensionDevelopmentPath`
|
||||
const extensionDevelopmentPath = path.resolve(__dirname, '../..');
|
||||
|
||||
// The path to the extension test script (must be javascript)
|
||||
// Passed to --extensionTestsPath
|
||||
const extensionTestsPath = path.resolve(__dirname, './run');
|
||||
|
||||
const launchArgs = [];
|
||||
// Disable other extensions while testing,
|
||||
launchArgs.push('--disable-extensions');
|
||||
|
||||
// use a temporary folder so we can run multiple instances of the same VS Code together
|
||||
// see https://github.com/microsoft/vscode/issues/137678
|
||||
launchArgs.push('--user-data-dir', tempdir);
|
||||
|
||||
const argv = await yargs(hideBin(process.argv))
|
||||
.options({
|
||||
stable: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
grep: {
|
||||
alias: 'g',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
.parse();
|
||||
const version = argv.stable ? 'stable' : 'insiders';
|
||||
|
||||
const extensionTestsEnv: typeof process.env = {};
|
||||
// Pass arguments to mocha by environment variables
|
||||
if (argv.grep) { extensionTestsEnv.MOCHA_GREP = argv.grep; }
|
||||
if (argv._.length > 0) { extensionTestsEnv.MOCHA_FILES = argv._.join('\n'); }
|
||||
if (!process.stdout.isTTY) { extensionTestsEnv.NO_COLOR = 'true'; }
|
||||
const workspaceFolder = await fs.mkdtemp(path.join(os.tmpdir(), 'copilot-extension-test-'));
|
||||
launchArgs.push(workspaceFolder);
|
||||
|
||||
extensionTestsEnv.CORETEST = 'true';
|
||||
//@dbaeumer This can be removed as soon as we have the cache handle CORETEST
|
||||
extensionTestsEnv.VITEST = 'true';
|
||||
|
||||
// Download VS Code, unzip it and run the integration test
|
||||
exitCode = await runTests({
|
||||
version,
|
||||
extensionDevelopmentPath,
|
||||
extensionTestsPath,
|
||||
launchArgs,
|
||||
extensionTestsEnv,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Failed to run tests', err);
|
||||
exitCode = 1;
|
||||
} finally {
|
||||
await fs.rm(tempdir, { recursive: true });
|
||||
}
|
||||
process.exit(exitCode);
|
||||
}
|
||||
|
||||
void main();
|
||||
@@ -0,0 +1,72 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAuthenticationService } from '../../../../../../platform/authentication/common/authentication';
|
||||
import { CopilotToken } from '../../../../../../platform/authentication/common/copilotToken';
|
||||
import { createServiceIdentifier } from '../../../../../../util/common/services';
|
||||
import { ThrottledDelayer } from '../../../../../../util/vs/base/common/async';
|
||||
import { Disposable } from '../../../../../../util/vs/base/common/lifecycle';
|
||||
export { CopilotToken } from '../../../../../../platform/authentication/common/copilotToken';
|
||||
|
||||
export const ICompletionsCopilotTokenManager = createServiceIdentifier<ICompletionsCopilotTokenManager>('ICompletionsCopilotTokenManager');
|
||||
export interface ICompletionsCopilotTokenManager {
|
||||
readonly _serviceBrand: undefined;
|
||||
get token(): CopilotToken | undefined;
|
||||
primeToken(): Promise<boolean>;
|
||||
getToken(): Promise<CopilotToken>;
|
||||
resetToken(httpError?: number): void;
|
||||
getLastToken(): Omit<CopilotToken, 'token'> | undefined;
|
||||
}
|
||||
|
||||
export class CopilotTokenManagerImpl extends Disposable implements ICompletionsCopilotTokenManager {
|
||||
declare _serviceBrand: undefined;
|
||||
private tokenRefetcher = new ThrottledDelayer(5_000);
|
||||
private _token: CopilotToken | undefined;
|
||||
get token() {
|
||||
void this.tokenRefetcher.trigger(() => this.updateCachedToken());
|
||||
return this._token;
|
||||
}
|
||||
|
||||
constructor(
|
||||
protected primed = false,
|
||||
@IAuthenticationService private readonly authenticationService: IAuthenticationService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.updateCachedToken();
|
||||
this._register(this.authenticationService.onDidAuthenticationChange(() => this.updateCachedToken()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we have a token and that the `StatusReporter` is up to date.
|
||||
*/
|
||||
primeToken(): Promise<boolean> {
|
||||
try {
|
||||
return this.getToken().then(
|
||||
() => true,
|
||||
() => false
|
||||
);
|
||||
} catch (e) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
async getToken(): Promise<CopilotToken> {
|
||||
return this.updateCachedToken();
|
||||
}
|
||||
|
||||
private async updateCachedToken(): Promise<CopilotToken> {
|
||||
this._token = await this.authenticationService.getCopilotToken();
|
||||
return this._token;
|
||||
}
|
||||
|
||||
resetToken(httpError?: number): void {
|
||||
this.authenticationService.resetCopilotToken();
|
||||
}
|
||||
|
||||
getLastToken(): Omit<CopilotToken, 'token'> | undefined {
|
||||
return this.authenticationService.copilotToken;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAuthenticationService } from '../../../../../../platform/authentication/common/authentication';
|
||||
import { CopilotToken } from '../../../../../../platform/authentication/common/copilotToken';
|
||||
|
||||
export function onCopilotToken(authService: IAuthenticationService, listener: (token: Omit<CopilotToken, 'token'>) => unknown) {
|
||||
return authService.onDidAuthenticationChange(() => {
|
||||
const copilotToken = authService.copilotToken;
|
||||
if (copilotToken) {
|
||||
listener(copilotToken);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CopilotToken } from './copilotTokenManager';
|
||||
|
||||
/**
|
||||
* A function used to determine if the org list contains an known organization
|
||||
* @param orgs The list of organizations the user is a member of
|
||||
* @returns The first known organization or undefined if none are known.
|
||||
*/
|
||||
function findKnownOrg(orgs: string[]): string | undefined {
|
||||
// Do not add org mapping
|
||||
const known_orgs = [
|
||||
'a5db0bcaae94032fe715fb34a5e4bce2',
|
||||
'7184f66dfcee98cb5f08a1cb936d5225',
|
||||
'faef89d9169d5eacf1d8c8dde3412e37',
|
||||
'4535c7beffc844b46bb1ed4aa04d759a',
|
||||
];
|
||||
return known_orgs.find(o => orgs.includes(o));
|
||||
}
|
||||
|
||||
export function getUserKind(token: Omit<CopilotToken, 'token'>): string {
|
||||
const orgs = token.organizationList ?? [];
|
||||
return findKnownOrg(orgs) ?? '';
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { Disposable } from '../../types/src';
|
||||
import { ICompletionsTextDocumentManagerService } from './textDocumentManager';
|
||||
|
||||
/**
|
||||
* A tracker which can take an arbitrary number of actions to run after a given timeout
|
||||
* When all pushed timeouts have been resolved, the tracker disposes of itself.
|
||||
*/
|
||||
export class ChangeTracker {
|
||||
private _offset: number;
|
||||
get offset(): number {
|
||||
return this._offset;
|
||||
}
|
||||
private _referenceCount = 0;
|
||||
private _tracker: Disposable;
|
||||
private _isDisposed = false;
|
||||
|
||||
constructor(
|
||||
fileURI: string,
|
||||
insertionOffset: number,
|
||||
@ICompletionsTextDocumentManagerService documentManager: ICompletionsTextDocumentManagerService
|
||||
) {
|
||||
this._offset = insertionOffset;
|
||||
|
||||
this._tracker = documentManager.onDidChangeTextDocument(e => {
|
||||
if (e.document.uri === fileURI) {
|
||||
for (const cc of e.contentChanges) {
|
||||
if (cc.rangeOffset + cc.rangeLength <= this.offset) {
|
||||
const delta = cc.text.length - cc.rangeLength;
|
||||
this._offset = this._offset + delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
push(action: () => void, timeout: number): void {
|
||||
if (this._isDisposed) {
|
||||
throw new Error('Unable to push new actions to a disposed ChangeTracker');
|
||||
}
|
||||
this._referenceCount++;
|
||||
setTimeout(() => {
|
||||
action();
|
||||
this._referenceCount--;
|
||||
if (this._referenceCount === 0) {
|
||||
this._tracker.dispose();
|
||||
this._isDisposed = true;
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { createServiceIdentifier } from '../../../../../util/common/services';
|
||||
import { Disposable, IDisposable } from '../../../../../util/vs/base/common/lifecycle';
|
||||
import { IRange } from './textDocument';
|
||||
|
||||
export interface IPCitationDetail {
|
||||
license: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface IPDocumentCitation {
|
||||
inDocumentUri: string;
|
||||
offsetStart: number;
|
||||
offsetEnd: number;
|
||||
version?: number;
|
||||
location?: IRange;
|
||||
matchingText?: string;
|
||||
details: IPCitationDetail[];
|
||||
}
|
||||
|
||||
export const ICompletionsCitationManager = createServiceIdentifier<ICompletionsCitationManager>('ICompletionsCitationManager');
|
||||
export interface ICompletionsCitationManager {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
register(): IDisposable;
|
||||
handleIPCodeCitation(citation: IPDocumentCitation): Promise<void>;
|
||||
}
|
||||
|
||||
export class NoOpCitationManager implements ICompletionsCitationManager {
|
||||
declare _serviceBrand: undefined;
|
||||
|
||||
register() { return Disposable.None; }
|
||||
|
||||
async handleIPCodeCitation(citation: IPDocumentCitation): Promise<void> {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import EventEmitter from 'events';
|
||||
import { createServiceIdentifier } from '../../../../../util/common/services';
|
||||
import { ICompletionsTelemetryService } from '../../bridge/src/completionsTelemetryServiceBridge';
|
||||
import { CancellationToken, Disposable } from '../../types/src';
|
||||
import { CompletionState } from './completionState';
|
||||
import { GetGhostTextOptions } from './ghostText/ghostText';
|
||||
import { telemetryCatch, TelemetryWithExp } from './telemetry';
|
||||
import { ICompletionsPromiseQueueService } from './util/promiseQueue';
|
||||
|
||||
export type CompletionRequestedEvent = {
|
||||
completionId: string;
|
||||
completionState: CompletionState;
|
||||
telemetryData: TelemetryWithExp;
|
||||
cancellationToken?: CancellationToken;
|
||||
options?: Partial<GetGhostTextOptions>;
|
||||
};
|
||||
|
||||
const requestEventName = 'CompletionRequested';
|
||||
|
||||
export const ICompletionsNotifierService = createServiceIdentifier<ICompletionsNotifierService>('ICompletionsNotifierService');
|
||||
export interface ICompletionsNotifierService {
|
||||
readonly _serviceBrand: undefined;
|
||||
notifyRequest(
|
||||
completionState: CompletionState,
|
||||
completionId: string,
|
||||
telemetryData: TelemetryWithExp,
|
||||
cancellationToken?: CancellationToken,
|
||||
options?: Partial<GetGhostTextOptions>
|
||||
): void;
|
||||
|
||||
onRequest(listener: (event: CompletionRequestedEvent) => void): Disposable;
|
||||
}
|
||||
|
||||
export class CompletionNotifier implements ICompletionsNotifierService {
|
||||
declare _serviceBrand: undefined;
|
||||
#emitter = new EventEmitter();
|
||||
constructor(
|
||||
@ICompletionsPromiseQueueService protected completionsPromiseQueue: ICompletionsPromiseQueueService,
|
||||
@ICompletionsTelemetryService protected completionsTelemetryService: ICompletionsTelemetryService,
|
||||
) { }
|
||||
|
||||
notifyRequest(
|
||||
completionState: CompletionState,
|
||||
completionId: string,
|
||||
telemetryData: TelemetryWithExp,
|
||||
cancellationToken?: CancellationToken,
|
||||
options?: Partial<GetGhostTextOptions>
|
||||
) {
|
||||
return this.#emitter.emit(requestEventName, {
|
||||
completionId,
|
||||
completionState,
|
||||
telemetryData,
|
||||
cancellationToken,
|
||||
options,
|
||||
});
|
||||
}
|
||||
|
||||
onRequest(listener: (event: CompletionRequestedEvent) => void): Disposable {
|
||||
const wrapper = telemetryCatch(this.completionsTelemetryService, this.completionsPromiseQueue, listener, `event.${requestEventName}`);
|
||||
this.#emitter.on(requestEventName, wrapper);
|
||||
return Disposable.create(() => this.#emitter.off(requestEventName, wrapper));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { Position, ProposedTextEdit, TextEdit } from '../../types/src';
|
||||
import { IntelliSenseInsertion, ITextDocument, type TextDocumentContents } from './textDocument';
|
||||
|
||||
export class CompletionState {
|
||||
readonly originalPosition: Position;
|
||||
readonly originalVersion: number;
|
||||
readonly originalOffset: number;
|
||||
private readonly _editsWithPosition: ReadonlyArray<ProposedTextEdit>;
|
||||
|
||||
constructor(
|
||||
private readonly _textDocument: ITextDocument,
|
||||
private readonly _position: Position,
|
||||
edits: ProposedTextEdit[] = [],
|
||||
originalPosition?: Position,
|
||||
originalVersion?: number,
|
||||
originalOffset?: number
|
||||
) {
|
||||
this.originalPosition = originalPosition ?? Position.create(_position.line, _position.character);
|
||||
this.originalVersion = originalVersion ?? _textDocument.version;
|
||||
this.originalOffset = originalOffset ?? _textDocument.offsetAt(this.originalPosition);
|
||||
this._editsWithPosition = [...edits];
|
||||
}
|
||||
|
||||
get textDocument(): TextDocumentContents {
|
||||
return this._textDocument;
|
||||
}
|
||||
|
||||
get position(): Position {
|
||||
return this._position;
|
||||
}
|
||||
|
||||
get editsWithPosition(): ProposedTextEdit[] {
|
||||
return [...this._editsWithPosition];
|
||||
}
|
||||
|
||||
private updateState(textDocument: ITextDocument, position: Position, edits?: ProposedTextEdit[]): CompletionState {
|
||||
return new CompletionState(
|
||||
textDocument,
|
||||
position,
|
||||
edits ?? this.editsWithPosition,
|
||||
this.originalPosition,
|
||||
this.originalVersion,
|
||||
this.originalOffset
|
||||
);
|
||||
}
|
||||
|
||||
updatePosition(position: Position): CompletionState {
|
||||
return this.updateState(this._textDocument, position);
|
||||
}
|
||||
|
||||
addSelectedCompletionInfo(selectedCompletionInfo: IntelliSenseInsertion): CompletionState {
|
||||
if (this.editsWithPosition.find(edit => edit.source === 'selectedCompletionInfo')) {
|
||||
throw new Error('Selected completion info already applied');
|
||||
}
|
||||
|
||||
const edit: TextEdit = {
|
||||
range: selectedCompletionInfo.range,
|
||||
newText: selectedCompletionInfo.text,
|
||||
};
|
||||
return this.applyEdits([edit], true);
|
||||
}
|
||||
|
||||
applyEdits(edits: TextEdit[], isSelectedCompletionInfo = false): CompletionState {
|
||||
if (isSelectedCompletionInfo && edits.length > 1) {
|
||||
throw new Error('Selected completion info should be a single edit');
|
||||
}
|
||||
|
||||
let textDocument = this._textDocument;
|
||||
let position = this._position;
|
||||
let offset: number = textDocument.offsetAt(position);
|
||||
const newEdits = this.editsWithPosition;
|
||||
|
||||
for (const { range, newText } of edits) {
|
||||
const oldText = textDocument.getText(range);
|
||||
const oldEndOffset = textDocument.offsetAt(range.end);
|
||||
textDocument = textDocument.applyEdits([{ range, newText }]);
|
||||
// We err on the side of updating the position if it's exactly aligned with the start of the range. This is
|
||||
// what we want in the context of applying a completion, but it does make some operations impossible, like
|
||||
// preserving a position at the start of the document (line 0 column 0).
|
||||
if (offset < textDocument.offsetAt(range.start)) {
|
||||
const edit: ProposedTextEdit = {
|
||||
range,
|
||||
newText,
|
||||
positionAfterEdit: Position.create(position.line, position.character),
|
||||
};
|
||||
if (isSelectedCompletionInfo) {
|
||||
edit.source = 'selectedCompletionInfo';
|
||||
}
|
||||
newEdits.push(edit);
|
||||
continue;
|
||||
}
|
||||
if (offset < oldEndOffset) {
|
||||
offset = oldEndOffset;
|
||||
}
|
||||
offset += newText.length - oldText.length;
|
||||
position = textDocument.positionAt(offset);
|
||||
const edit: ProposedTextEdit = {
|
||||
range,
|
||||
newText,
|
||||
positionAfterEdit: Position.create(position.line, position.character),
|
||||
};
|
||||
if (isSelectedCompletionInfo) {
|
||||
edit.source = 'selectedCompletionInfo';
|
||||
}
|
||||
newEdits.push(edit);
|
||||
}
|
||||
|
||||
return this.updateState(textDocument, position, newEdits);
|
||||
}
|
||||
}
|
||||
|
||||
export function createCompletionState(textDocument: ITextDocument, position: Position): CompletionState {
|
||||
return new CompletionState(textDocument, position);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { DocumentId } from '../../../../../platform/inlineEdits/common/dataTypes/documentId';
|
||||
import { IObservableDocument } from '../../../../../platform/inlineEdits/common/observableWorkspace';
|
||||
import { IObservableWithChange } from '../../../../../util/vs/base/common/observableInternal';
|
||||
import { URI } from '../../../../../util/vs/base/common/uri';
|
||||
import { createDecorator as createServiceIdentifier } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const ICompletionsObservableWorkspace = createServiceIdentifier<ICompletionsObservableWorkspace>('ICompletionsObservableWorkspace');
|
||||
export interface ICompletionsObservableWorkspace {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
get openDocuments(): IObservableWithChange<readonly IObservableDocument[], { added: readonly IObservableDocument[]; removed: readonly IObservableDocument[] }>;
|
||||
|
||||
getWorkspaceRoot(documentId: DocumentId): URI | undefined;
|
||||
|
||||
getFirstOpenDocument(): IObservableDocument | undefined;
|
||||
|
||||
getDocument(documentId: DocumentId): IObservableDocument | undefined;
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { packageJson } from '../../../../../platform/env/common/packagejson';
|
||||
import { createServiceIdentifier } from '../../../../../util/common/services';
|
||||
import { ServicesAccessor } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { CopilotConfigPrefix } from './constants';
|
||||
import { Filter } from './experiments/filters';
|
||||
import { Emitter, Event } from './util/event';
|
||||
|
||||
export { packageJson };
|
||||
|
||||
export const ConfigKey = {
|
||||
Enable: 'enable',
|
||||
UserSelectedCompletionModel: 'selectedCompletionModel',
|
||||
|
||||
ShowEditorCompletions: 'editor.showEditorCompletions',
|
||||
EnableAutoCompletions: 'editor.enableAutoCompletions',
|
||||
DelayCompletions: 'editor.delayCompletions',
|
||||
FilterCompletions: 'editor.filterCompletions',
|
||||
CompletionsDelay: 'completionsDelay',
|
||||
CompletionsDebounce: 'completionsDebounce',
|
||||
|
||||
// Advanced config (don't add new config here)
|
||||
RelatedFilesVSCodeCSharp: 'advanced.relatedFilesVSCodeCSharp',
|
||||
RelatedFilesVSCodeTypeScript: 'advanced.relatedFilesVSCodeTypeScript',
|
||||
RelatedFilesVSCode: 'advanced.relatedFilesVSCode',
|
||||
ContextProviders: 'advanced.contextProviders',
|
||||
DebugFilterLogCategories: 'advanced.debug.filterLogCategories',
|
||||
DebugSnippyOverrideUrl: 'advanced.debug.codeRefOverrideUrl',
|
||||
UseSubsetMatching: 'advanced.useSubsetMatching',
|
||||
ContextProviderTimeBudget: 'advanced.contextProviderTimeBudget',
|
||||
|
||||
// Internal config
|
||||
DebugOverrideCapiUrl: 'internal.capiUrl',
|
||||
DebugOverrideCapiUrlLegacy: 'advanced.debug.overrideCapiUrl',
|
||||
DebugTestOverrideCapiUrl: 'internal.capiTestUrl',
|
||||
DebugTestOverrideCapiUrlLegacy: 'advanced.debug.testOverrideCapiUrl',
|
||||
DebugOverrideProxyUrl: 'internal.completionsUrl',
|
||||
DebugOverrideProxyUrlLegacy: 'advanced.debug.overrideProxyUrl',
|
||||
DebugTestOverrideProxyUrl: 'internal.completionsTestUrl',
|
||||
DebugTestOverrideProxyUrlLegacy: 'advanced.debug.testOverrideProxyUrl',
|
||||
DebugOverrideEngine: 'internal.completionModel',
|
||||
DebugOverrideEngineLegacy: 'advanced.debug.overrideEngine',
|
||||
/**
|
||||
* Internal experiment for always requesting multiline completions.
|
||||
* This might not result always in a multiline suggestion, but most often will.
|
||||
*/
|
||||
AlwaysRequestMultiline: 'internal.alwaysRequestMultiline',
|
||||
/**
|
||||
* Let the model terminate single line completions when AlwaysRequestMultiline is enabled.
|
||||
*/
|
||||
ModelAlwaysTerminatesSingleline: 'internal.modelAlwaysTerminatesSingleline',
|
||||
|
||||
/**
|
||||
* Overrides whether to use the Workspace Context Coordinator to coordinate workspace context.
|
||||
* This setting takes precedence over the value from ExP.
|
||||
*/
|
||||
UseWorkspaceContextCoordinator: 'internal.useWorkspaceContextCoordinator',
|
||||
|
||||
/**
|
||||
* Overrides whether to include neighboring files in the prompt
|
||||
* alongside context providers.
|
||||
* This setting takes precedence over the value from ExP.
|
||||
*/
|
||||
IncludeNeighboringFiles: 'internal.includeNeighboringFiles',
|
||||
ExcludeRelatedFiles: 'internal.excludeRelatedFiles',
|
||||
DebugOverrideCppHeadersEnableSwitch: 'internal.cppHeadersEnableSwitch',
|
||||
|
||||
/**
|
||||
* Internal config for using the completions prompt with split context.
|
||||
* https://github.com/github/copilot/issues/19286
|
||||
*/
|
||||
UseSplitContextPrompt: 'internal.useSplitContextPrompt',
|
||||
};
|
||||
|
||||
export type ConfigKeyType = string;
|
||||
|
||||
// How to determine where to terminate the completion to the current block.
|
||||
export enum BlockMode {
|
||||
/**
|
||||
* Parse the context + completion on the client using treesitter to
|
||||
* determine blocks.
|
||||
*/
|
||||
Parsing = 'parsing',
|
||||
/**
|
||||
* Let the server parse out blocks and assume that the completion terminates
|
||||
* at the end of a block.
|
||||
*/
|
||||
Server = 'server',
|
||||
/**
|
||||
* Runs both the treesitter parsing on the client plus indentation-based
|
||||
* truncation on the proxy.
|
||||
*/
|
||||
ParsingAndServer = 'parsingandserver',
|
||||
/**
|
||||
* Client-based heuristic to display more multiline completions.
|
||||
* It almost always requests a multiline completion from the server and tries to break it up to something useful on the client.
|
||||
*
|
||||
* This should not be rolled out at the moment (latency impact is high, UX needs further fine-tuning),
|
||||
* but can be used for internal experimentation.
|
||||
*/
|
||||
MoreMultiline = 'moremultiline',
|
||||
}
|
||||
|
||||
export function shouldDoServerTrimming(blockMode: BlockMode): boolean {
|
||||
return [BlockMode.Server, BlockMode.ParsingAndServer].includes(blockMode);
|
||||
}
|
||||
|
||||
// TODO rework this enum so that the normal/nightly and prod/dev distinctions are orthogonal. (dev builds should behave like nightly?)
|
||||
export enum BuildType {
|
||||
DEV = 'dev',
|
||||
PROD = 'prod',
|
||||
NIGHTLY = 'nightly',
|
||||
}
|
||||
|
||||
export const ICompletionsConfigProvider = createServiceIdentifier<ICompletionsConfigProvider>('ICompletionsConfigProvider');
|
||||
export interface ICompletionsConfigProvider {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
getConfig<T>(key: ConfigKeyType): T;
|
||||
getOptionalConfig<T>(key: ConfigKeyType): T | undefined;
|
||||
dumpForTelemetry(): { [key: string]: string };
|
||||
onDidChangeCopilotSettings: Event<ConfigProvider>;
|
||||
}
|
||||
|
||||
export abstract class ConfigProvider implements ICompletionsConfigProvider {
|
||||
declare _serviceBrand: undefined;
|
||||
abstract getConfig<T>(key: ConfigKeyType): T;
|
||||
abstract getOptionalConfig<T>(key: ConfigKeyType): T | undefined;
|
||||
abstract dumpForTelemetry(): { [key: string]: string };
|
||||
abstract onDidChangeCopilotSettings: Event<ConfigProvider>;
|
||||
|
||||
// The language server receives workspace configuration *after* it is fully initialized, which creates a race
|
||||
// condition where an incoming request immediately after initialization might have the default values. Awaiting
|
||||
// this promise allows consumers to ensure that the configuration is ready before using it.
|
||||
requireReady(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides only the default values, ignoring the user's settings.
|
||||
* @public KEEPING FOR TESTS
|
||||
*/
|
||||
export class DefaultsOnlyConfigProvider extends ConfigProvider {
|
||||
override getConfig<T>(key: ConfigKeyType): T {
|
||||
// hardcode default values for the agent, for now
|
||||
return getConfigDefaultForKey<T>(key);
|
||||
}
|
||||
|
||||
override getOptionalConfig<T>(key: ConfigKeyType): T | undefined {
|
||||
return getOptionalConfigDefaultForKey<T>(key);
|
||||
}
|
||||
|
||||
override dumpForTelemetry(): { [key: string]: string } {
|
||||
return {};
|
||||
}
|
||||
|
||||
override onDidChangeCopilotSettings = () => {
|
||||
// no-op, since this provider does not support changing settings
|
||||
return {
|
||||
dispose: () => { },
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A ConfigProvider that allows overriding of config values.
|
||||
* @public KEEPING FOR TESTS
|
||||
*/
|
||||
export class InMemoryConfigProvider extends ConfigProvider {
|
||||
protected readonly copilotEmitter = new Emitter<this>();
|
||||
readonly onDidChangeCopilotSettings = this.copilotEmitter.event;
|
||||
private overrides: Map<ConfigKeyType, unknown> = new Map();
|
||||
|
||||
constructor(
|
||||
private readonly baseConfigProvider: ConfigProvider,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
setOverrides(overrides: Map<ConfigKeyType, unknown>): void {
|
||||
this.overrides = overrides;
|
||||
}
|
||||
|
||||
clearOverrides(): void {
|
||||
this.overrides.clear();
|
||||
}
|
||||
|
||||
protected getOptionalOverride<T>(key: ConfigKeyType): T | undefined {
|
||||
return this.overrides.get(key) as T | undefined;
|
||||
}
|
||||
|
||||
override getConfig<T>(key: ConfigKeyType): T {
|
||||
return this.getOptionalOverride(key) ?? this.baseConfigProvider.getConfig(key);
|
||||
}
|
||||
|
||||
override getOptionalConfig<T>(key: ConfigKeyType): T | undefined {
|
||||
return this.getOptionalOverride(key) ?? this.baseConfigProvider.getOptionalConfig(key);
|
||||
}
|
||||
|
||||
setConfig(key: ConfigKeyType, value: unknown): void {
|
||||
this.setCopilotSettings({ [key]: value });
|
||||
}
|
||||
|
||||
setCopilotSettings(settings: Record<ConfigKeyType, unknown>): void {
|
||||
for (const [key, value] of Object.entries(settings)) {
|
||||
if (value !== undefined) {
|
||||
this.overrides.set(key, value);
|
||||
} else {
|
||||
this.overrides.delete(key);
|
||||
}
|
||||
}
|
||||
this.copilotEmitter.fire(this);
|
||||
}
|
||||
|
||||
override dumpForTelemetry(): { [key: string]: string } {
|
||||
const config = this.baseConfigProvider.dumpForTelemetry();
|
||||
// reflects what's mapped in Hydro
|
||||
for (const key of [
|
||||
ConfigKey.ShowEditorCompletions,
|
||||
ConfigKey.EnableAutoCompletions,
|
||||
ConfigKey.DelayCompletions,
|
||||
ConfigKey.FilterCompletions,
|
||||
]) {
|
||||
const value = this.overrides.get(key);
|
||||
if (value !== undefined) {
|
||||
config[key] = JSON.stringify(value);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export function getConfigKeyRecursively<T>(config: Record<string, unknown>, key: string): T | undefined {
|
||||
let value: unknown = config;
|
||||
const prefix: string[] = [];
|
||||
for (const segment of key.split('.')) {
|
||||
const child = [...prefix, segment].join('.');
|
||||
if (value && typeof value === 'object' && child in value) {
|
||||
value = (value as { [key: string]: unknown })[child];
|
||||
prefix.length = 0;
|
||||
} else {
|
||||
prefix.push(segment);
|
||||
}
|
||||
}
|
||||
if (value === undefined || prefix.length > 0) { return; }
|
||||
return value as T;
|
||||
}
|
||||
|
||||
export function getConfigDefaultForKey<T>(key: string): T {
|
||||
if (configDefaults.has(key)) {
|
||||
return configDefaults.get(key) as T;
|
||||
}
|
||||
throw new Error(`Missing config default value: ${CopilotConfigPrefix}.${key}`);
|
||||
}
|
||||
|
||||
export function getOptionalConfigDefaultForKey<T>(key: string): T | undefined {
|
||||
return <T>configDefaults.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults for "hidden" config keys. These are supplemented by the defaults in package.json.
|
||||
*/
|
||||
const configDefaults = new Map<ConfigKeyType, unknown>([
|
||||
[ConfigKey.DebugOverrideCppHeadersEnableSwitch, false],
|
||||
[ConfigKey.RelatedFilesVSCodeCSharp, false],
|
||||
[ConfigKey.RelatedFilesVSCodeTypeScript, false],
|
||||
[ConfigKey.RelatedFilesVSCode, false],
|
||||
[ConfigKey.IncludeNeighboringFiles, false],
|
||||
[ConfigKey.ExcludeRelatedFiles, false],
|
||||
[ConfigKey.ContextProviders, []],
|
||||
[ConfigKey.DebugSnippyOverrideUrl, ''],
|
||||
[ConfigKey.UseSubsetMatching, null],
|
||||
[ConfigKey.ContextProviderTimeBudget, undefined],
|
||||
[ConfigKey.DebugOverrideCapiUrl, ''],
|
||||
[ConfigKey.DebugTestOverrideCapiUrl, ''],
|
||||
[ConfigKey.DebugOverrideProxyUrl, ''],
|
||||
[ConfigKey.DebugTestOverrideProxyUrl, ''],
|
||||
[ConfigKey.DebugOverrideEngine, ''],
|
||||
[ConfigKey.AlwaysRequestMultiline, undefined],
|
||||
[ConfigKey.CompletionsDebounce, undefined],
|
||||
[ConfigKey.CompletionsDelay, undefined],
|
||||
[ConfigKey.ModelAlwaysTerminatesSingleline, undefined],
|
||||
[ConfigKey.UseWorkspaceContextCoordinator, undefined],
|
||||
|
||||
|
||||
// These are only used for telemetry from LSP based editors and do not affect any behavior.
|
||||
[ConfigKey.ShowEditorCompletions, undefined],
|
||||
[ConfigKey.EnableAutoCompletions, undefined],
|
||||
[ConfigKey.DelayCompletions, undefined],
|
||||
[ConfigKey.FilterCompletions, undefined],
|
||||
[ConfigKey.UseSplitContextPrompt, true],
|
||||
|
||||
// These are defaults from package.json
|
||||
[ConfigKey.Enable, { '*': true, 'plaintext': false, 'markdown': false, 'scminput': false }],
|
||||
[ConfigKey.UserSelectedCompletionModel, ''],
|
||||
|
||||
// These are advanced defaults from package.json
|
||||
[ConfigKey.DebugOverrideEngineLegacy, ''],
|
||||
[ConfigKey.DebugOverrideProxyUrlLegacy, ''],
|
||||
[ConfigKey.DebugTestOverrideProxyUrlLegacy, ''],
|
||||
[ConfigKey.DebugOverrideCapiUrlLegacy, ''],
|
||||
[ConfigKey.DebugTestOverrideCapiUrlLegacy, ''],
|
||||
[ConfigKey.DebugFilterLogCategories, []],
|
||||
]);
|
||||
|
||||
export function getConfig<T>(accessor: ServicesAccessor, key: ConfigKeyType): T {
|
||||
return accessor.get(ICompletionsConfigProvider).getConfig(key);
|
||||
}
|
||||
|
||||
export function dumpForTelemetry(accessor: ServicesAccessor) {
|
||||
try {
|
||||
return accessor.get(ICompletionsConfigProvider).dumpForTelemetry();
|
||||
} catch (e) {
|
||||
console.error(`Error dumping config for telemetry: ${e}`);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export class BuildInfo {
|
||||
|
||||
static isPreRelease(): boolean {
|
||||
return this.getBuildType() === BuildType.NIGHTLY;
|
||||
}
|
||||
|
||||
static isProduction(): boolean {
|
||||
return this.getBuildType() !== BuildType.DEV;
|
||||
}
|
||||
|
||||
static getBuildType(): BuildType {
|
||||
const buildType = <'dev' | 'prod'>packageJson.buildType;
|
||||
if (buildType === 'prod') {
|
||||
return BuildInfo.getVersion().length === 15 ? BuildType.NIGHTLY : BuildType.PROD;
|
||||
}
|
||||
return BuildType.DEV;
|
||||
}
|
||||
|
||||
static getVersion(): string {
|
||||
return packageJson.version;
|
||||
}
|
||||
|
||||
static getBuild(): string {
|
||||
return packageJson.build;
|
||||
}
|
||||
}
|
||||
|
||||
type NameAndVersion = {
|
||||
name: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
export type EditorInfo = NameAndVersion & {
|
||||
// The root directory of the installation, currently only used to simplify stack traces.
|
||||
root?: string;
|
||||
// A programmatic name, used for error reporting.
|
||||
devName?: string;
|
||||
};
|
||||
|
||||
export type EditorPluginInfo = NameAndVersion;
|
||||
|
||||
export type EditorPluginFilter = { filter: Filter; value: string; isVersion?: boolean };
|
||||
|
||||
export function formatNameAndVersion({ name, version }: NameAndVersion): string {
|
||||
return `${name}/${version}`;
|
||||
}
|
||||
|
||||
export const ICompletionsEditorAndPluginInfo = createServiceIdentifier<ICompletionsEditorAndPluginInfo>('ICompletionsEditorAndPluginInfo');
|
||||
export interface ICompletionsEditorAndPluginInfo {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
getEditorInfo(): EditorInfo;
|
||||
getEditorPluginInfo(): EditorPluginInfo;
|
||||
getRelatedPluginInfo(): EditorPluginInfo[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this in new code. Every endpoint has its own unique versioning.
|
||||
* Centralizing in a single constant was a mistake.
|
||||
* @deprecated
|
||||
*/
|
||||
export const apiVersion = '2025-05-01';
|
||||
|
||||
export function editorVersionHeaders(accessor: ServicesAccessor): { [key: string]: string } {
|
||||
const info = accessor.get(ICompletionsEditorAndPluginInfo);
|
||||
return {
|
||||
'Editor-Version': formatNameAndVersion(info.getEditorInfo()),
|
||||
'Editor-Plugin-Version': formatNameAndVersion(info.getEditorPluginInfo()),
|
||||
'Copilot-Language-Server-Version': BuildInfo.getVersion(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
export const CopilotConfigPrefix = 'github.copilot';
|
||||
@@ -0,0 +1,41 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ServicesAccessor } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { Logger, logger } from './logger';
|
||||
import { isAbortError } from './networking';
|
||||
import { ICompletionsStatusReporter } from './progress';
|
||||
|
||||
const oomCodes = new Set(['ERR_WORKER_OUT_OF_MEMORY', 'ENOMEM']);
|
||||
|
||||
function isOomError(error: NodeJS.ErrnoException) {
|
||||
return (
|
||||
oomCodes.has(error.code ?? '') ||
|
||||
// happens in loadWasmLanguage
|
||||
(error.name === 'RangeError' && error.message === 'WebAssembly.Memory(): could not allocate memory')
|
||||
);
|
||||
}
|
||||
|
||||
export function handleException(accessor: ServicesAccessor, err: unknown, origin: string, _logger: Logger = logger): void {
|
||||
if (isAbortError(err)) {
|
||||
// ignore cancelled fetch requests
|
||||
return;
|
||||
}
|
||||
const statusReporter = accessor.get(ICompletionsStatusReporter);
|
||||
if (err instanceof Error) {
|
||||
const error = err as NodeJS.ErrnoException;
|
||||
if (isOomError(error)) {
|
||||
statusReporter.setWarning('Out of memory');
|
||||
} else if (error.code === 'EMFILE' || error.code === 'ENFILE') {
|
||||
statusReporter.setWarning('Too many open files');
|
||||
} else if (error.code === 'CopilotPromptLoadFailure') {
|
||||
statusReporter.setWarning('Corrupted Copilot installation');
|
||||
} else if (`${error.code}`.startsWith('CopilotPromptWorkerExit')) {
|
||||
statusReporter.setWarning('Worker unexpectedly exited');
|
||||
} else if (error.syscall === 'uv_cwd' && error.code === 'ENOENT') {
|
||||
statusReporter.setWarning('Current working directory does not exist');
|
||||
}
|
||||
}
|
||||
_logger.exception(accessor, err, origin);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ServicesAccessor } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { BuildInfo, ICompletionsEditorAndPluginInfo } from './config';
|
||||
import { TelemetryData } from './telemetry';
|
||||
|
||||
const os = {
|
||||
EOL: '\n',
|
||||
};
|
||||
|
||||
/** Diagnostics report made available in extension or agent. */
|
||||
interface Report {
|
||||
sections: Section[];
|
||||
}
|
||||
|
||||
type SectionItems = { [key: string]: boolean | string | number | undefined };
|
||||
|
||||
/** Section of a diagnostics report. */
|
||||
interface Section {
|
||||
name: string;
|
||||
items: SectionItems;
|
||||
}
|
||||
|
||||
export function collectCompletionDiagnostics(accessor: ServicesAccessor, telemetry: TelemetryData | undefined): Report {
|
||||
const telemetryItems: SectionItems = {};
|
||||
if (telemetry !== undefined) {
|
||||
if (telemetry.properties.headerRequestId) {
|
||||
telemetryItems['Header Request ID'] = telemetry.properties.headerRequestId;
|
||||
}
|
||||
if (telemetry.properties.choiceIndex) {
|
||||
telemetryItems['Choice Index'] = telemetry.properties.choiceIndex;
|
||||
}
|
||||
if (telemetry.properties.opportunityId) {
|
||||
telemetryItems['Opportunity ID'] = telemetry.properties.opportunityId;
|
||||
}
|
||||
if (telemetry.properties.clientCompletionId) {
|
||||
telemetryItems['Client Completion ID'] = telemetry.properties.clientCompletionId;
|
||||
}
|
||||
if (telemetry.properties.engineName) {
|
||||
telemetryItems['Model ID'] = telemetry.properties.engineName;
|
||||
}
|
||||
}
|
||||
return {
|
||||
sections: [
|
||||
{
|
||||
name: 'Copilot Extension',
|
||||
items: {
|
||||
Version: BuildInfo.getVersion(),
|
||||
Editor: getEditorDisplayVersion(accessor),
|
||||
...telemetryItems,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function formatDiagnosticsAsMarkdown(data: Report): string {
|
||||
const s = data.sections.map(formatSectionAsMarkdown);
|
||||
return s.join(os.EOL + os.EOL) + os.EOL;
|
||||
}
|
||||
|
||||
function formatSectionAsMarkdown(s: Section) {
|
||||
return (
|
||||
`## ${s.name}` +
|
||||
os.EOL +
|
||||
os.EOL +
|
||||
Object.keys(s.items)
|
||||
.filter(k => k !== 'name')
|
||||
.map(k => `- ${k}: ${s.items[k] ?? 'N/A'}`)
|
||||
.join(os.EOL)
|
||||
);
|
||||
}
|
||||
|
||||
function getEditorDisplayVersion(accessor: ServicesAccessor): string {
|
||||
const info = accessor.get(ICompletionsEditorAndPluginInfo).getEditorInfo();
|
||||
return `${info.name} ${info.version}`;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ServicesAccessor } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { LRUCacheMap } from './helpers/cache';
|
||||
import { TextDocumentIdentifier } from './textDocument';
|
||||
import { ICompletionsTextDocumentManagerService } from './textDocumentManager';
|
||||
|
||||
/**
|
||||
* A map from the string representation of a document URI to its last access time in ms since the
|
||||
* epoch.
|
||||
*/
|
||||
export const accessTimes = new LRUCacheMap<string, number>();
|
||||
|
||||
/**
|
||||
* Returns a copy of `docs` sorted by access time, from most to least recent.
|
||||
*/
|
||||
export function sortByAccessTimes<T extends TextDocumentIdentifier>(docs: readonly T[]): T[] {
|
||||
return [...docs].sort((a, b) => {
|
||||
const aAccessTime = accessTimes.get(a.uri) ?? 0;
|
||||
const bAccessTime = accessTimes.get(b.uri) ?? 0;
|
||||
return bAccessTime - aAccessTime;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener on the `window.onDidChangeActiveTextEditor` event that records/updates the
|
||||
* access time of the document.
|
||||
*/
|
||||
export const registerDocumentTracker = (accessor: ServicesAccessor) =>
|
||||
accessor.get(ICompletionsTextDocumentManagerService).onDidFocusTextDocument(e => {
|
||||
if (e.document) {
|
||||
accessTimes.set(e.document.uri.toString(), Date.now());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IEnvService } from '../../../../../../platform/env/common/envService';
|
||||
import { createServiceIdentifier } from '../../../../../../util/common/services';
|
||||
import { URI } from '../../../../../../util/vs/base/common/uri';
|
||||
import { ICompletionsLogTargetService, Logger } from '../logger';
|
||||
import { ICompletionsNotificationSender } from '../notificationSender';
|
||||
|
||||
const CERTIFICATE_ERRORS = ['UNABLE_TO_VERIFY_LEAF_SIGNATURE', 'CERT_SIGNATURE_FAILURE'];
|
||||
const errorMsg =
|
||||
'Your proxy connection requires a trusted certificate. Please make sure the proxy certificate and any issuers are configured correctly and trusted by your operating system.';
|
||||
const learnMoreLink = 'https://gh.io/copilot-network-errors';
|
||||
|
||||
export const ICompletionsUserErrorNotifierService = createServiceIdentifier<ICompletionsUserErrorNotifierService>('ICompletionsUserErrorNotifierService');
|
||||
export interface ICompletionsUserErrorNotifierService {
|
||||
readonly _serviceBrand: undefined;
|
||||
notifyUser(e: unknown): void;
|
||||
}
|
||||
|
||||
export class UserErrorNotifier implements ICompletionsUserErrorNotifierService {
|
||||
declare _serviceBrand: undefined;
|
||||
private readonly notifiedErrorCodes: string[] = [];
|
||||
|
||||
constructor(
|
||||
@ICompletionsLogTargetService private readonly _logTarget: ICompletionsLogTargetService,
|
||||
@ICompletionsNotificationSender private readonly _notificationSender: ICompletionsNotificationSender,
|
||||
@IEnvService private readonly _env: IEnvService
|
||||
) { }
|
||||
|
||||
notifyUser(e: unknown) {
|
||||
if (!(e instanceof Error)) { return; }
|
||||
const error: NodeJS.ErrnoException = e;
|
||||
if (error.code && CERTIFICATE_ERRORS.includes(error.code) && !this.didNotifyBefore(error.code)) {
|
||||
this.notifiedErrorCodes.push(error.code);
|
||||
void this.displayCertificateErrorNotification(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async displayCertificateErrorNotification(err: NodeJS.ErrnoException) {
|
||||
new Logger('certificates').error(
|
||||
this._logTarget,
|
||||
`${errorMsg} Please visit ${learnMoreLink} to learn more. Original cause:`,
|
||||
err
|
||||
);
|
||||
const learnMoreAction = { title: 'Learn more' };
|
||||
return this._notificationSender
|
||||
.showWarningMessage(errorMsg, learnMoreAction)
|
||||
.then(userResponse => {
|
||||
if (userResponse?.title === learnMoreAction.title) {
|
||||
return this._env.openExternal(URI.parse(learnMoreLink));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private didNotifyBefore(code: string) {
|
||||
return this.notifiedErrorCodes.indexOf(code) !== -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAuthenticationService } from '../../../../../../platform/authentication/common/authentication';
|
||||
import { IExperimentationService } from '../../../../../../platform/telemetry/common/nullExperimentationService';
|
||||
import { IDisposable } from '../../../../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { CopilotToken } from '../auth/copilotTokenManager';
|
||||
import { getUserKind } from '../auth/orgs';
|
||||
import {
|
||||
BuildInfo,
|
||||
BuildType,
|
||||
ConfigKey,
|
||||
getConfig
|
||||
} from '../config';
|
||||
import { getEngineRequestInfo } from '../openai/config';
|
||||
import { Filter, Release } from './filters';
|
||||
|
||||
export function setupCompletionsExperimentationService(accessor: ServicesAccessor): IDisposable {
|
||||
const authService = accessor.get(IAuthenticationService);
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
|
||||
const disposable = authService.onDidAccessTokenChange(() => {
|
||||
authService.getCopilotToken()
|
||||
.then(t => instantiationService.invokeFunction(updateCompletionsFilters, t))
|
||||
.catch(err => { });
|
||||
});
|
||||
|
||||
updateCompletionsFilters(accessor, authService.copilotToken);
|
||||
|
||||
return disposable;
|
||||
}
|
||||
|
||||
function getPluginRelease(accessor: ServicesAccessor): Release {
|
||||
if (BuildInfo.getBuildType() === BuildType.NIGHTLY) {
|
||||
return Release.Nightly;
|
||||
}
|
||||
return Release.Stable;
|
||||
}
|
||||
|
||||
function updateCompletionsFilters(accessor: ServicesAccessor, token: Omit<CopilotToken, 'token'> | undefined) {
|
||||
const exp = accessor.get(IExperimentationService);
|
||||
|
||||
const filters = createCompletionsFilters(accessor, token);
|
||||
|
||||
exp.setCompletionsFilters(filters);
|
||||
}
|
||||
|
||||
export function createCompletionsFilters(accessor: ServicesAccessor, token: Omit<CopilotToken, 'token'> | undefined) {
|
||||
const filters = new Map<Filter, string>();
|
||||
|
||||
filters.set(Filter.ExtensionRelease, getPluginRelease(accessor));
|
||||
filters.set(Filter.CopilotOverrideEngine, getConfig(accessor, ConfigKey.DebugOverrideEngine) || getConfig(accessor, ConfigKey.DebugOverrideEngineLegacy));
|
||||
filters.set(Filter.CopilotClientVersion, BuildInfo.isProduction() ? BuildInfo.getVersion() : '1.999.0');
|
||||
|
||||
if (token) {
|
||||
const userKind = getUserKind(token);
|
||||
const customModel = token.getTokenValue('ft') ?? '';
|
||||
const orgs = token.getTokenValue('ol') ?? '';
|
||||
const customModelNames = token.getTokenValue('cml') ?? '';
|
||||
const copilotTrackingId = token.getTokenValue('tid') ?? '';
|
||||
|
||||
filters.set(Filter.CopilotUserKind, userKind);
|
||||
filters.set(Filter.CopilotCustomModel, customModel);
|
||||
filters.set(Filter.CopilotOrgs, orgs);
|
||||
filters.set(Filter.CopilotCustomModelNames, customModelNames);
|
||||
filters.set(Filter.CopilotTrackingId, copilotTrackingId);
|
||||
filters.set(Filter.CopilotUserKind, getUserKind(token));
|
||||
}
|
||||
|
||||
const model = getEngineRequestInfo(accessor).modelId;
|
||||
filters.set(Filter.CopilotEngine, model);
|
||||
return filters;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ServicesAccessor } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { TelemetryData, telemetryExpProblem } from '../telemetry';
|
||||
import { ExpServiceTelemetryNames } from './telemetryNames';
|
||||
|
||||
// All variables we pull from Exp and might want to use
|
||||
export enum ExpTreatmentVariables {
|
||||
// the engine we want to request, used in actual experiment(s)
|
||||
CustomEngine = 'copilotcustomengine',
|
||||
// if set, any custom engine (see previous) will only apply when the current engine matches the value of this variable
|
||||
CustomEngineTargetEngine = 'copilotcustomenginetargetengine',
|
||||
|
||||
OverrideBlockMode = 'copilotoverrideblockmode',
|
||||
SuffixPercent = 'CopilotSuffixPercent', // the percentage of the prompt tokens to allocate to the suffix
|
||||
CppHeadersEnableSwitch = 'copilotcppheadersenableswitch', // whether to enable the inclusion of C++ headers as neighbors in the prompt
|
||||
UseSubsetMatching = 'copilotsubsetmatching', // whether to use subset matching instead of jaccard similarity experiment
|
||||
|
||||
// granularity specification
|
||||
SuffixMatchThreshold = 'copilotsuffixmatchthreshold', // the threshold that new suffix should match with old suffix
|
||||
|
||||
MaxPromptCompletionTokens = 'maxpromptcompletionTokens', // the maximum tokens of the prompt and completion
|
||||
|
||||
/**
|
||||
* Enable the use of the Workspace Context Coordinator to coordinate context from providers of workspace snippets.
|
||||
*/
|
||||
StableContextPercent = 'copilotstablecontextpercent', // the percentage of the prompt tokens to allocate to the stable context
|
||||
VolatileContextPercent = 'copilotvolatilecontextpercent', // the percentage of the prompt tokens to allocate to the volatile context
|
||||
|
||||
/**
|
||||
* Flags that control the enablement of the related files extensibility for various languages in VSCode.
|
||||
*/
|
||||
RelatedFilesVSCodeCSharp = 'copilotrelatedfilesvscodecsharp', // whether to include related files as neighbors in the prompt for C#, this takes precedence over RelatedFilesVSCode
|
||||
RelatedFilesVSCodeTypeScript = 'copilotrelatedfilesvscodetypescript', // whether to include related files as neighbors in the prompt for TS/JS, this takes precedence over RelatedFilesVSCode
|
||||
RelatedFilesVSCode = 'copilotrelatedfilesvscode', // whether to include related files as neighbors in the prompt, vscode experiment
|
||||
|
||||
/**
|
||||
* Flags that control the inclusion of open tab files as neighboring files for various languages.
|
||||
*/
|
||||
ContextProviders = 'copilotcontextproviders', // comma-separated list of context providers IDs (case sensitive) to enable
|
||||
IncludeNeighboringFiles = 'copilotincludeneighboringfiles', // Always include neighboring files alongside context providers
|
||||
ExcludeRelatedFiles = 'copilotexcluderelatedfiles', // Exclude related files even if neighboring files are enabled
|
||||
ContextProviderTimeBudget = 'copilotcontextprovidertimebudget', // time budget for context providers in milliseconds
|
||||
|
||||
/**
|
||||
* Values to control the ContextProvider API's CodeSnippets provided by the C++ Language Service.
|
||||
*/
|
||||
CppContextProviderParams = 'copilotcppContextProviderParams',
|
||||
|
||||
/**
|
||||
* Values to control the ContextProvider API's CodeSnippets provided by the C# Language Service.
|
||||
*/
|
||||
CSharpContextProviderParams = 'copilotcsharpcontextproviderparams',
|
||||
|
||||
/**
|
||||
* Values to control the ContextProvider API's CodeSnippets provided by the Java Language Service.
|
||||
*/
|
||||
JavaContextProviderParams = 'copilotjavacontextproviderparams',
|
||||
|
||||
/**
|
||||
* Values to control the MultiLanguageContextProvider parameters.
|
||||
*/
|
||||
MultiLanguageContextProviderParams = 'copilotmultilanguagecontextproviderparams',
|
||||
|
||||
/**
|
||||
* Values to control the TsContextProvider parameters.
|
||||
*/
|
||||
TsContextProviderParams = 'copilottscontextproviderparams',
|
||||
|
||||
/**
|
||||
* Controls the delay to apply to debouncing of completion requests.
|
||||
*/
|
||||
CompletionsDebounce = 'copilotcompletionsdebounce',
|
||||
|
||||
/**
|
||||
* Enable the electron networking in VS Code.
|
||||
*/
|
||||
ElectronFetcher = 'copilotelectronfetcher',
|
||||
FetchFetcher = 'copilotfetchfetcher',
|
||||
|
||||
/**
|
||||
* Sets the timeout for waiting for async completions in flight before
|
||||
* issuing a new network request. Set to -1 to disable the timeout entirely.
|
||||
*/
|
||||
AsyncCompletionsTimeout = 'copilotasynccompletionstimeout',
|
||||
|
||||
/**
|
||||
* Controls whether the prompt context for code completions needs to be split from the document prefix.
|
||||
*/
|
||||
EnablePromptContextProxyField = 'copilotenablepromptcontextproxyfield',
|
||||
|
||||
/**
|
||||
* Controls progressive reveal of completions.
|
||||
*/
|
||||
ProgressiveReveal = 'copilotprogressivereveal',
|
||||
// part of progressive reveal, controls whether the model or client terminates single-line completions
|
||||
ModelAlwaysTerminatesSingleline = 'copilotmodelterminatesingleline',
|
||||
// long look-ahead window size (in lines) for progressive reveal
|
||||
ProgressiveRevealLongLookaheadSize = 'copilotprogressivereveallonglookaheadsize',
|
||||
// short look-ahead window size (in lines) for progressive reveal
|
||||
ProgressiveRevealShortLookaheadSize = 'copilotprogressiverevealshortlookaheadsize',
|
||||
// maximum token count when requesting multi-line completions
|
||||
MaxMultilineTokens = 'copilotmaxmultilinetokens',
|
||||
|
||||
/**
|
||||
* Controls number of lines to trim to after accepting a completion.
|
||||
*/
|
||||
MultilineAfterAcceptLines = 'copilotmultilineafteracceptlines',
|
||||
|
||||
/**
|
||||
* Add a delay before rendering completions.
|
||||
*/
|
||||
CompletionsDelay = 'copilotcompletionsdelay',
|
||||
|
||||
/**
|
||||
* Request single line completions unless the previous completion was just accepted.
|
||||
*/
|
||||
SingleLineUnlessAccepted = 'copilotsinglelineunlessaccepted',
|
||||
}
|
||||
|
||||
export type ExpTreatmentVariableValue = boolean | string | number;
|
||||
|
||||
export class ExpConfig {
|
||||
variables: Partial<Record<ExpTreatmentVariables, ExpTreatmentVariableValue>>; // for the 'vscode' config
|
||||
features: string; // semicolon-separated feature IDs
|
||||
|
||||
constructor(
|
||||
variables: Partial<Record<ExpTreatmentVariables, ExpTreatmentVariableValue>>,
|
||||
features: string
|
||||
) {
|
||||
this.variables = variables;
|
||||
this.features = features;
|
||||
}
|
||||
|
||||
static createFallbackConfig(accessor: ServicesAccessor, reason: string): ExpConfig {
|
||||
telemetryExpProblem(accessor, { reason });
|
||||
return this.createEmptyConfig();
|
||||
}
|
||||
|
||||
static createEmptyConfig() {
|
||||
return new ExpConfig({}, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds (or overwrites) the given experiment config to the telemetry data.
|
||||
* @param telemetryData telemetryData object. If previous ExpConfigs are already present, they will be overwritten.
|
||||
*/
|
||||
addToTelemetry(telemetryData: TelemetryData): void {
|
||||
telemetryData.properties[ExpServiceTelemetryNames.featuresTelemetryPropertyName] = this.features;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ILogService } from '../../../../../../platform/log/common/logService';
|
||||
import { IExperimentationService } from '../../../../../../platform/telemetry/common/nullExperimentationService';
|
||||
import { IInstantiationService } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import {
|
||||
DEFAULT_MAX_COMPLETION_LENGTH,
|
||||
DEFAULT_MAX_PROMPT_LENGTH,
|
||||
DEFAULT_PROMPT_ALLOCATION_PERCENT,
|
||||
DEFAULT_SUFFIX_MATCH_THRESHOLD
|
||||
} from '../../../prompt/src/prompt';
|
||||
import { CopilotToken, ICompletionsCopilotTokenManager } from '../auth/copilotTokenManager';
|
||||
import { BlockMode } from '../config';
|
||||
import { TelemetryData, TelemetryWithExp } from '../telemetry';
|
||||
import { createCompletionsFilters } from './defaultExpFilters';
|
||||
import { ExpConfig, ExpTreatmentVariables, ExpTreatmentVariableValue } from './expConfig';
|
||||
import { CompletionsFiltersInfo, ContextProviderExpSettings, ICompletionsFeaturesService } from './featuresService';
|
||||
import { Filter, FilterSettings } from './filters';
|
||||
|
||||
type InternalContextProviderExpSettings = {
|
||||
id?: string;
|
||||
ids?: string[];
|
||||
includeNeighboringFiles?: boolean;
|
||||
excludeRelatedFiles?: boolean;
|
||||
timeBudget?: number;
|
||||
params?: Record<string, string | boolean | number>;
|
||||
};
|
||||
|
||||
/** General-purpose API for accessing ExP variable values. */
|
||||
export class Features implements ICompletionsFeaturesService {
|
||||
declare _serviceBrand: undefined;
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IExperimentationService private readonly experimentationService: IExperimentationService,
|
||||
@ICompletionsCopilotTokenManager private readonly copilotTokenManager: ICompletionsCopilotTokenManager,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Central logic for obtaining the assignments of treatment groups
|
||||
* for a given set of filters (i.e. descriptors of who is getting the treatment).
|
||||
* Also gets the values of variables controlled by experiment.
|
||||
*
|
||||
* This function should be called **exactly once** at the start of every
|
||||
* 'completion request' in the client (e.g. ghostText, panel request or chat conversation).
|
||||
*
|
||||
* It is called with an initial set of filters, (FeaturesFilterArgs)
|
||||
* but it adds many of its own.
|
||||
* At first the general background filters like extension version.
|
||||
* Then it will check ExP assignments for the first time, to find out
|
||||
* whether there are any assignments of a special granularity
|
||||
* (i.e. the concept that we want to redraw assignments based on
|
||||
* time bucket, or checksum of time, etc).
|
||||
*
|
||||
* On most calls to this function, the assignment fetches will be the
|
||||
* assignments from previously used filters, so they will be cached and return fast.
|
||||
*
|
||||
* @param telemetryData The base telemetry object to which the experimental filters, ExP
|
||||
* variable values, and experimental assignments will be added. All properties and measurements
|
||||
* of the input telemetryData will be present in the output TelemetryWithExp object.
|
||||
* Every telemetry data used to generate ExP scorecards (e.g. ghostText events) must
|
||||
* include the correct experiment assignments in order to properly create those
|
||||
* scorecards.
|
||||
*/
|
||||
async updateExPValuesAndAssignments(
|
||||
filtersInfo?: CompletionsFiltersInfo,
|
||||
telemetryData: TelemetryData = TelemetryData.createAndMarkAsIssued()
|
||||
): Promise<TelemetryWithExp> {
|
||||
// We should not allow accidentally overwriting existing ExP vals/assignments.
|
||||
// This doesn't stop all misuse cases, but should prevent some trivial ones.
|
||||
if (telemetryData instanceof TelemetryWithExp) {
|
||||
throw new Error('updateExPValuesAndAssignments should not be called with TelemetryWithExp');
|
||||
}
|
||||
|
||||
const token = this.copilotTokenManager.token ?? await this.copilotTokenManager.getToken();
|
||||
const { filters, exp } = this.createExpConfigAndFilters(token);
|
||||
|
||||
return new TelemetryWithExp(telemetryData.properties, telemetryData.measurements, telemetryData.issuedTime, {
|
||||
filters,
|
||||
exp: exp,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a Copilot token and use that token to call updateExPValuesAndAssignments. Do NOT call this at startup.
|
||||
* Instead, register a onCopilotToken handler and use that token with updateExPValuesAndAssignments directly.
|
||||
*/
|
||||
async fetchTokenAndUpdateExPValuesAndAssignments(
|
||||
filtersInfo?: CompletionsFiltersInfo,
|
||||
telemetryData?: TelemetryData
|
||||
) {
|
||||
return await this.updateExPValuesAndAssignments(filtersInfo, telemetryData);
|
||||
}
|
||||
|
||||
private createExpConfigAndFilters(token: CopilotToken) {
|
||||
|
||||
const exp2: Partial<Record<ExpTreatmentVariables, ExpTreatmentVariableValue>> = {};
|
||||
for (const varName of Object.values<ExpTreatmentVariables>(ExpTreatmentVariables)) {
|
||||
const value = this.experimentationService.getTreatmentVariable(varName);
|
||||
if (value !== undefined) {
|
||||
exp2[varName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
const features = Object.entries(exp2).map(([name, value]) => {
|
||||
// Based on what tas-client does in https://github.com/microsoft/tas-client/blob/2bd24c976273b671892aad99139af2c7c7dc3b26/tas-client/src/tas-client/FeatureProvider/TasApiFeatureProvider.ts#L59
|
||||
return name + (value ? '' : 'cf');
|
||||
});
|
||||
const exp = new ExpConfig(exp2, features.join(';'));
|
||||
const filterMap = this.instantiationService.invokeFunction(createCompletionsFilters, token);
|
||||
const filterRecord: Partial<Record<Filter, string>> = {};
|
||||
for (const [key, value] of filterMap.entries()) {
|
||||
filterRecord[key] = value;
|
||||
}
|
||||
|
||||
const filters = new FilterSettings(filterRecord);
|
||||
return { filters, exp };
|
||||
}
|
||||
|
||||
/** Get the entries from this.assignments corresponding to given settings. */
|
||||
async getFallbackExpAndFilters(): Promise<{ filters: FilterSettings; exp: ExpConfig }> {
|
||||
const token = this.copilotTokenManager.token ?? await this.copilotTokenManager.getToken();
|
||||
return this.createExpConfigAndFilters(token);
|
||||
}
|
||||
|
||||
/** Override for BlockMode to send in the request. */
|
||||
overrideBlockMode(telemetryWithExp: TelemetryWithExp): BlockMode | undefined {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.OverrideBlockMode] as BlockMode) ||
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
||||
/** Functions with arguments, passed via object destructuring */
|
||||
|
||||
/** @returns the string for copilotcustomengine, or "" if none is set. */
|
||||
customEngine(telemetryWithExp: TelemetryWithExp): string {
|
||||
return (telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.CustomEngine] as string) ?? '';
|
||||
}
|
||||
|
||||
/** @returns the string for copilotcustomenginetargetengine, or undefined if none is set. */
|
||||
customEngineTargetEngine(telemetryWithExp: TelemetryWithExp): string | undefined {
|
||||
return telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.CustomEngineTargetEngine] as string;
|
||||
}
|
||||
|
||||
/** @returns the percent of prompt tokens to be allocated to the suffix */
|
||||
suffixPercent(telemetryWithExp: TelemetryWithExp): number {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.SuffixPercent] as number) ??
|
||||
DEFAULT_PROMPT_ALLOCATION_PERCENT.suffix
|
||||
);
|
||||
}
|
||||
|
||||
/** @returns the percentage match threshold for using the cached suffix */
|
||||
suffixMatchThreshold(telemetryWithExp: TelemetryWithExp): number {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.SuffixMatchThreshold] as number) ??
|
||||
DEFAULT_SUFFIX_MATCH_THRESHOLD
|
||||
);
|
||||
}
|
||||
|
||||
/** @returns whether to enable the inclusion of C++ headers as neighbor files. */
|
||||
cppHeadersEnableSwitch(telemetryWithExp: TelemetryWithExp): boolean {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.CppHeadersEnableSwitch] as boolean) ??
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/** @returns whether to use included related files as neighbor files for C# (vscode experiment). */
|
||||
relatedFilesVSCodeCSharp(telemetryWithExp: TelemetryWithExp): boolean {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.RelatedFilesVSCodeCSharp] as boolean) ??
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/** @returns whether to use included related files as neighbor files for TS/JS (vscode experiment). */
|
||||
relatedFilesVSCodeTypeScript(telemetryWithExp: TelemetryWithExp): boolean {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[
|
||||
ExpTreatmentVariables.RelatedFilesVSCodeTypeScript
|
||||
] as boolean) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
/** @returns whether to use included related files as neighbor files (vscode experiment). */
|
||||
relatedFilesVSCode(telemetryWithExp: TelemetryWithExp): boolean {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.RelatedFilesVSCode] as boolean) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
/** @returns the list of context providers IDs to enable. The special value `*` enables all context providers. */
|
||||
contextProviders(telemetryWithExp: TelemetryWithExp): string[] {
|
||||
const providers = (telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.ContextProviders] ??
|
||||
'') as string;
|
||||
if (!providers) {
|
||||
return [];
|
||||
}
|
||||
return providers.split(',').map(provider => provider.trim());
|
||||
}
|
||||
|
||||
contextProviderTimeBudget(languageId: string, telemetryWithExp: TelemetryWithExp): number {
|
||||
const client = (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.ContextProviderTimeBudget] as number) ??
|
||||
150
|
||||
);
|
||||
if (client) {
|
||||
return client;
|
||||
}
|
||||
const chat = this.getContextProviderExpSettings(languageId);
|
||||
return chat?.timeBudget ?? 150;
|
||||
}
|
||||
|
||||
includeNeighboringFiles(languageId: string, telemetryWithExp: TelemetryWithExp): boolean {
|
||||
const client = (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.IncludeNeighboringFiles] as boolean) ??
|
||||
false
|
||||
);
|
||||
if (client) {
|
||||
return true;
|
||||
}
|
||||
const chat = this.getContextProviderExpSettings(languageId);
|
||||
return chat?.includeNeighboringFiles ?? false;
|
||||
}
|
||||
|
||||
excludeRelatedFiles(languageId: string, telemetryWithExp: TelemetryWithExp): boolean {
|
||||
const client = (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.ExcludeRelatedFiles] as boolean) ??
|
||||
false
|
||||
);
|
||||
if (client) {
|
||||
return true;
|
||||
}
|
||||
const chat = this.getContextProviderExpSettings(languageId);
|
||||
return chat?.excludeRelatedFiles ?? false;
|
||||
}
|
||||
|
||||
getContextProviderExpSettings(languageId: string): ContextProviderExpSettings | undefined {
|
||||
const value = this.experimentationService.getTreatmentVariable<string>(`config.github.copilot.chat.contextprovider.${languageId}`);
|
||||
if (typeof value === 'string') {
|
||||
try {
|
||||
const parsed: Partial<InternalContextProviderExpSettings> = JSON.parse(value);
|
||||
const ids = this.getProviderIDs(parsed);
|
||||
delete parsed.id;
|
||||
delete parsed.ids;
|
||||
return Object.assign({ ids }, { includeNeighboringFiles: false, excludeRelatedFiles: false, timeBudget: 150 }, parsed as Omit<InternalContextProviderExpSettings, 'id' | 'ids'>);
|
||||
} catch (err) {
|
||||
this.instantiationService.invokeFunction((accessor) => {
|
||||
const logService = accessor.get(ILogService);
|
||||
logService.error(`Failed to parse context provider exp settings for language ${languageId}`);
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private getProviderIDs(json: InternalContextProviderExpSettings): string[] {
|
||||
const result: string[] = [];
|
||||
if (typeof json.id === 'string' && json.id.length > 0) {
|
||||
result.push(json.id);
|
||||
}
|
||||
if (Array.isArray(json.ids)) {
|
||||
for (const id of json.ids) {
|
||||
if (typeof id === 'string' && id.length > 0) {
|
||||
result.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @returns the maximal number of tokens of prompt AND completion */
|
||||
maxPromptCompletionTokens(telemetryWithExp: TelemetryWithExp): number {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.MaxPromptCompletionTokens] as number) ??
|
||||
DEFAULT_MAX_PROMPT_LENGTH + DEFAULT_MAX_COMPLETION_LENGTH
|
||||
);
|
||||
}
|
||||
|
||||
stableContextPercent(telemetryWithExp: TelemetryWithExp): number {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.StableContextPercent] as number) ??
|
||||
DEFAULT_PROMPT_ALLOCATION_PERCENT.stableContext
|
||||
);
|
||||
}
|
||||
|
||||
volatileContextPercent(telemetryWithExp: TelemetryWithExp): number {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.VolatileContextPercent] as number) ??
|
||||
DEFAULT_PROMPT_ALLOCATION_PERCENT.volatileContext
|
||||
);
|
||||
}
|
||||
|
||||
/** Custom parameters for language specific Context Providers. */
|
||||
cppContextProviderParams(telemetryWithExp: TelemetryWithExp): string | undefined {
|
||||
const cppContextProviderParams = telemetryWithExp.filtersAndExp.exp.variables[
|
||||
ExpTreatmentVariables.CppContextProviderParams
|
||||
] as string;
|
||||
return cppContextProviderParams;
|
||||
}
|
||||
|
||||
csharpContextProviderParams(telemetryWithExp: TelemetryWithExp): string | undefined {
|
||||
const csharpContextProviderParams = telemetryWithExp.filtersAndExp.exp.variables[
|
||||
ExpTreatmentVariables.CSharpContextProviderParams
|
||||
] as string;
|
||||
return csharpContextProviderParams;
|
||||
}
|
||||
|
||||
javaContextProviderParams(telemetryWithExp: TelemetryWithExp): string | undefined {
|
||||
const javaContextProviderParams = telemetryWithExp.filtersAndExp.exp.variables[
|
||||
ExpTreatmentVariables.JavaContextProviderParams
|
||||
] as string;
|
||||
return javaContextProviderParams;
|
||||
}
|
||||
|
||||
multiLanguageContextProviderParams(telemetryWithExp: TelemetryWithExp): string | undefined {
|
||||
const multiLanguageContextProviderParams = telemetryWithExp.filtersAndExp.exp.variables[
|
||||
ExpTreatmentVariables.MultiLanguageContextProviderParams
|
||||
] as string;
|
||||
return multiLanguageContextProviderParams;
|
||||
}
|
||||
|
||||
tsContextProviderParams(telemetryWithExp: TelemetryWithExp): string | undefined {
|
||||
const tsContextProviderParams = telemetryWithExp.filtersAndExp.exp.variables[
|
||||
ExpTreatmentVariables.TsContextProviderParams
|
||||
] as string;
|
||||
return tsContextProviderParams;
|
||||
}
|
||||
|
||||
completionsDebounce(telemetryWithExp: TelemetryWithExp): number | undefined {
|
||||
return telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.CompletionsDebounce] as
|
||||
| number
|
||||
| undefined;
|
||||
}
|
||||
|
||||
enableElectronFetcher(telemetryWithExp: TelemetryWithExp): boolean {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.ElectronFetcher] as boolean) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
enableFetchFetcher(telemetryWithExp: TelemetryWithExp): boolean {
|
||||
return (telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.FetchFetcher] as boolean) ?? false;
|
||||
}
|
||||
|
||||
asyncCompletionsTimeout(telemetryWithExp: TelemetryWithExp): number {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.AsyncCompletionsTimeout] as number) ??
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
enableProgressiveReveal(telemetryWithExp: TelemetryWithExp): boolean {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.ProgressiveReveal] as boolean) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
modelAlwaysTerminatesSingleline(telemetryWithExp: TelemetryWithExp): boolean {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[
|
||||
ExpTreatmentVariables.ModelAlwaysTerminatesSingleline
|
||||
] as boolean) ?? true
|
||||
);
|
||||
}
|
||||
|
||||
longLookaheadSize(telemetryWithExp: TelemetryWithExp): number {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[
|
||||
ExpTreatmentVariables.ProgressiveRevealLongLookaheadSize
|
||||
] as number) ?? 9
|
||||
);
|
||||
}
|
||||
|
||||
shortLookaheadSize(telemetryWithExp: TelemetryWithExp): number {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[
|
||||
ExpTreatmentVariables.ProgressiveRevealShortLookaheadSize
|
||||
] as number) ?? 3
|
||||
);
|
||||
}
|
||||
|
||||
maxMultilineTokens(telemetryWithExp: TelemetryWithExp): number {
|
||||
// p50 line length is 19 characters (p95 is 73)
|
||||
// average token length is around 4 characters
|
||||
// the below value has quite a bit of buffer while bringing the limit in significantly from 500
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.MaxMultilineTokens] as number) ?? 200
|
||||
);
|
||||
}
|
||||
|
||||
multilineAfterAcceptLines(telemetryWithExp: TelemetryWithExp): number {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.MultilineAfterAcceptLines] as number) ??
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
completionsDelay(telemetryWithExp: TelemetryWithExp): number {
|
||||
return (telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.CompletionsDelay] as number) ?? 200;
|
||||
}
|
||||
|
||||
singleLineUnlessAccepted(telemetryWithExp: TelemetryWithExp): boolean {
|
||||
return (
|
||||
(telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.SingleLineUnlessAccepted] as boolean) ??
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createServiceIdentifier } from '../../../../../../util/common/services';
|
||||
import { BlockMode } from '../config';
|
||||
import { TelemetryData, TelemetryWithExp } from '../telemetry';
|
||||
import { ExpConfig } from './expConfig';
|
||||
import { FilterSettings } from './filters';
|
||||
|
||||
export type CompletionsFiltersInfo = { uri: string; languageId: string };
|
||||
|
||||
export type ContextProviderExpSettings = {
|
||||
ids: string[];
|
||||
includeNeighboringFiles: boolean;
|
||||
excludeRelatedFiles: boolean;
|
||||
timeBudget: number;
|
||||
params?: Record<string, string | boolean | number>;
|
||||
}
|
||||
|
||||
export const ICompletionsFeaturesService = createServiceIdentifier<ICompletionsFeaturesService>('ICompletionsFeaturesService');
|
||||
export interface ICompletionsFeaturesService {
|
||||
readonly _serviceBrand: undefined;
|
||||
updateExPValuesAndAssignments(
|
||||
filtersInfo?: CompletionsFiltersInfo,
|
||||
telemetryData?: TelemetryData
|
||||
): Promise<TelemetryWithExp>;
|
||||
fetchTokenAndUpdateExPValuesAndAssignments(
|
||||
filtersInfo?: CompletionsFiltersInfo,
|
||||
telemetryData?: TelemetryData
|
||||
): Promise<TelemetryWithExp>;
|
||||
getFallbackExpAndFilters(): Promise<{ filters: FilterSettings; exp: ExpConfig }>;
|
||||
overrideBlockMode(telemetryWithExp: TelemetryWithExp): BlockMode | undefined;
|
||||
customEngine(telemetryWithExp: TelemetryWithExp): string;
|
||||
customEngineTargetEngine(telemetryWithExp: TelemetryWithExp): string | undefined;
|
||||
suffixPercent(telemetryWithExp: TelemetryWithExp): number;
|
||||
suffixMatchThreshold(telemetryWithExp: TelemetryWithExp): number;
|
||||
cppHeadersEnableSwitch(telemetryWithExp: TelemetryWithExp): boolean;
|
||||
relatedFilesVSCodeCSharp(telemetryWithExp: TelemetryWithExp): boolean;
|
||||
relatedFilesVSCodeTypeScript(telemetryWithExp: TelemetryWithExp): boolean;
|
||||
relatedFilesVSCode(telemetryWithExp: TelemetryWithExp): boolean;
|
||||
contextProviders(telemetryWithExp: TelemetryWithExp): string[];
|
||||
contextProviderTimeBudget(languageId: string, telemetryWithExp: TelemetryWithExp): number;
|
||||
includeNeighboringFiles(languageId: string, telemetryWithExp: TelemetryWithExp): boolean;
|
||||
excludeRelatedFiles(languageId: string, telemetryWithExp: TelemetryWithExp): boolean;
|
||||
getContextProviderExpSettings(languageId: string): ContextProviderExpSettings | undefined;
|
||||
maxPromptCompletionTokens(telemetryWithExp: TelemetryWithExp): number;
|
||||
stableContextPercent(telemetryWithExp: TelemetryWithExp): number;
|
||||
volatileContextPercent(telemetryWithExp: TelemetryWithExp): number;
|
||||
cppContextProviderParams(telemetryWithExp: TelemetryWithExp): string | undefined;
|
||||
csharpContextProviderParams(telemetryWithExp: TelemetryWithExp): string | undefined;
|
||||
javaContextProviderParams(telemetryWithExp: TelemetryWithExp): string | undefined;
|
||||
multiLanguageContextProviderParams(telemetryWithExp: TelemetryWithExp): string | undefined;
|
||||
tsContextProviderParams(telemetryWithExp: TelemetryWithExp): string | undefined;
|
||||
completionsDebounce(telemetryWithExp: TelemetryWithExp): number | undefined;
|
||||
enableElectronFetcher(telemetryWithExp: TelemetryWithExp): boolean;
|
||||
enableFetchFetcher(telemetryWithExp: TelemetryWithExp): boolean;
|
||||
asyncCompletionsTimeout(telemetryWithExp: TelemetryWithExp): number;
|
||||
enableProgressiveReveal(telemetryWithExp: TelemetryWithExp): boolean;
|
||||
modelAlwaysTerminatesSingleline(telemetryWithExp: TelemetryWithExp): boolean;
|
||||
longLookaheadSize(telemetryWithExp: TelemetryWithExp): number;
|
||||
shortLookaheadSize(telemetryWithExp: TelemetryWithExp): number;
|
||||
maxMultilineTokens(telemetryWithExp: TelemetryWithExp): number;
|
||||
multilineAfterAcceptLines(telemetryWithExp: TelemetryWithExp): number;
|
||||
completionsDelay(telemetryWithExp: TelemetryWithExp): number;
|
||||
singleLineUnlessAccepted(telemetryWithExp: TelemetryWithExp): boolean;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TelemetryData } from '../telemetry';
|
||||
|
||||
/** The prefix used for related plugin version headers. */
|
||||
const CopilotRelatedPluginVersionPrefix = 'X-Copilot-RelatedPluginVersion-';
|
||||
|
||||
/** The filter headers that ExP knows about. */
|
||||
export enum Filter {
|
||||
// Default VSCode filters
|
||||
|
||||
ExtensionRelease = 'X-VSCode-ExtensionRelease',
|
||||
|
||||
// Copilot-specific filters
|
||||
|
||||
/** The machine ID concatenated with a 1-hour bucket. */
|
||||
CopilotClientTimeBucket = 'X-Copilot-ClientTimeBucket',
|
||||
/** The model currently in use. Not included in fallback filters */
|
||||
CopilotEngine = 'X-Copilot-Engine',
|
||||
/** The engine override value from settings, if present. */
|
||||
CopilotOverrideEngine = 'X-Copilot-OverrideEngine',
|
||||
/** Git repo info. Not included in fallback filters */
|
||||
CopilotRepository = 'X-Copilot-Repository',
|
||||
/** Language of the file on which a given request is being made. Not included in fallback filters */
|
||||
CopilotFileType = 'X-Copilot-FileType', // Wired to languageId
|
||||
/** The organization the user belongs to. Not included in fallback filters */
|
||||
CopilotUserKind = 'X-Copilot-UserKind',
|
||||
/** Declare experiment dogfood program if any. Not included in fallback filters */
|
||||
CopilotDogfood = 'X-Copilot-Dogfood',
|
||||
/** For custom Model Alpha. Not included in fallback filters */
|
||||
CopilotCustomModel = 'X-Copilot-CustomModel',
|
||||
/** Organizations. */
|
||||
CopilotOrgs = 'X-Copilot-Orgs',
|
||||
/** Identifiers for Custom Model(s) */
|
||||
CopilotCustomModelNames = 'X-Copilot-CustomModelNames',
|
||||
/** Copilot Tracking ID */
|
||||
CopilotTrackingId = 'X-Copilot-CopilotTrackingId',
|
||||
/** The Copilot Client Version */
|
||||
CopilotClientVersion = 'X-Copilot-ClientVersion',
|
||||
|
||||
CopilotRelatedPluginVersionCppTools = CopilotRelatedPluginVersionPrefix + 'msvscodecpptools',
|
||||
CopilotRelatedPluginVersionCMakeTools = CopilotRelatedPluginVersionPrefix + 'msvscodecmaketools',
|
||||
CopilotRelatedPluginVersionMakefileTools = CopilotRelatedPluginVersionPrefix + 'msvscodemakefiletools',
|
||||
CopilotRelatedPluginVersionCSharpDevKit = CopilotRelatedPluginVersionPrefix + 'msdotnettoolscsdevkit',
|
||||
CopilotRelatedPluginVersionPython = CopilotRelatedPluginVersionPrefix + 'mspythonpython',
|
||||
CopilotRelatedPluginVersionPylance = CopilotRelatedPluginVersionPrefix + 'mspythonvscodepylance',
|
||||
CopilotRelatedPluginVersionJavaPack = CopilotRelatedPluginVersionPrefix + 'vscjavavscodejavapack',
|
||||
CopilotRelatedPluginVersionJavaManager = CopilotRelatedPluginVersionPrefix + 'vscjavavscodejavadependency',
|
||||
CopilotRelatedPluginVersionTypescript = CopilotRelatedPluginVersionPrefix + 'vscodetypescriptlanguagefeatures',
|
||||
CopilotRelatedPluginVersionTypescriptNext = CopilotRelatedPluginVersionPrefix + 'msvscodevscodetypescriptnext',
|
||||
CopilotRelatedPluginVersionCSharp = CopilotRelatedPluginVersionPrefix + 'msdotnettoolscsharp',
|
||||
CopilotRelatedPluginVersionGithubCopilotChat = CopilotRelatedPluginVersionPrefix + 'githubcopilotchat',
|
||||
CopilotRelatedPluginVersionGithubCopilot = CopilotRelatedPluginVersionPrefix + 'githubcopilot',
|
||||
}
|
||||
|
||||
export enum Release {
|
||||
Stable = 'stable',
|
||||
Nightly = 'nightly',
|
||||
}
|
||||
|
||||
const telmetryNames: Partial<Record<Filter, string>> = {
|
||||
[Filter.CopilotClientTimeBucket]: 'timeBucket',
|
||||
[Filter.CopilotOverrideEngine]: 'engine',
|
||||
[Filter.CopilotRepository]: 'repo',
|
||||
[Filter.CopilotFileType]: 'fileType',
|
||||
[Filter.CopilotUserKind]: 'userKind',
|
||||
};
|
||||
|
||||
/**
|
||||
* The class FilterSettings holds the variables that were used to filter
|
||||
* experiment groups.
|
||||
*/
|
||||
export class FilterSettings {
|
||||
constructor(private readonly filters: Partial<Record<Filter, string>>) {
|
||||
// empyt string is equivalent to absent, so remove it
|
||||
for (const [filter, value] of Object.entries(this.filters)) {
|
||||
if (value === '') {
|
||||
delete this.filters[filter as Filter];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the telemetry Data with the current filter variables.
|
||||
* @param telemetryData Extended in place.
|
||||
*/
|
||||
addToTelemetry(telemetryData: TelemetryData) {
|
||||
// add all values:
|
||||
for (const [filter, value] of Object.entries(this.filters)) {
|
||||
const telemetryName = telmetryNames[filter as Filter];
|
||||
if (telemetryName === undefined) {
|
||||
continue;
|
||||
}
|
||||
telemetryData.properties[telemetryName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a copy of the filters. */
|
||||
toHeaders(): Partial<Record<Filter, string>> {
|
||||
return { ...this.filters };
|
||||
}
|
||||
}
|
||||
@@ -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 { ServicesAccessor } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { DEFAULT_NUM_SNIPPETS } from '../../../prompt/src/prompt';
|
||||
import { defaultSimilarFilesOptions, SimilarFilesOptions } from '../../../prompt/src/snippetInclusion/similarFiles';
|
||||
import { ConfigKey, getConfig } from '../config';
|
||||
import { TelemetryWithExp } from '../telemetry';
|
||||
import { ExpTreatmentVariables } from './expConfig';
|
||||
import { getCppNumberOfSnippets, getCppSimilarFilesOptions } from './similarFileOptionsProviderCpp';
|
||||
|
||||
type SimilarFilesOptionsProvider = (accessor: ServicesAccessor, exp: TelemetryWithExp) => SimilarFilesOptions;
|
||||
// Add here for more options for other language ids.
|
||||
const languageSimilarFilesOptions: ReadonlyMap<string, SimilarFilesOptionsProvider> = new Map<
|
||||
string,
|
||||
SimilarFilesOptionsProvider
|
||||
>([['cpp', getCppSimilarFilesOptions]]);
|
||||
|
||||
export function getSimilarFilesOptions(accessor: ServicesAccessor, exp: TelemetryWithExp, langId: string): SimilarFilesOptions {
|
||||
const optionsProvider: SimilarFilesOptionsProvider | undefined = languageSimilarFilesOptions.get(langId);
|
||||
if (optionsProvider) {
|
||||
return optionsProvider(accessor, exp);
|
||||
} else {
|
||||
return {
|
||||
...defaultSimilarFilesOptions,
|
||||
useSubsetMatching: useSubsetMatching(accessor, exp),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
type NumberOfSnippetsProvider = (exp: TelemetryWithExp) => number;
|
||||
// Add here for more values for other language ids.
|
||||
const numberOfSnippets: ReadonlyMap<string, NumberOfSnippetsProvider> = new Map<string, NumberOfSnippetsProvider>([
|
||||
['cpp', getCppNumberOfSnippets],
|
||||
]);
|
||||
|
||||
export function getNumberOfSnippets(exp: TelemetryWithExp, langId: string): number {
|
||||
const provider: NumberOfSnippetsProvider | undefined = numberOfSnippets.get(langId);
|
||||
return provider ? provider(exp) : DEFAULT_NUM_SNIPPETS;
|
||||
}
|
||||
|
||||
export function useSubsetMatching(accessor: ServicesAccessor, telemetryWithExp: TelemetryWithExp): boolean {
|
||||
return (
|
||||
((telemetryWithExp.filtersAndExp.exp.variables[ExpTreatmentVariables.UseSubsetMatching] as boolean) ||
|
||||
getConfig(accessor, ConfigKey.UseSubsetMatching)) ??
|
||||
false
|
||||
);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ServicesAccessor } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { defaultCppSimilarFilesOptions, SimilarFilesOptions } from '../../../prompt/src/snippetInclusion/similarFiles';
|
||||
import { TelemetryWithExp } from '../telemetry';
|
||||
import { useSubsetMatching } from './similarFileOptionsProvider';
|
||||
|
||||
export function getCppSimilarFilesOptions(accessor: ServicesAccessor, telemetryWithExp: TelemetryWithExp): SimilarFilesOptions {
|
||||
return {
|
||||
...defaultCppSimilarFilesOptions,
|
||||
useSubsetMatching: useSubsetMatching(accessor, telemetryWithExp),
|
||||
};
|
||||
}
|
||||
|
||||
export function getCppNumberOfSnippets(telemetryWithExp: TelemetryWithExp): number {
|
||||
return defaultCppSimilarFilesOptions.maxTopSnippets;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export enum ExpServiceTelemetryNames {
|
||||
// these are defined (but not exported) in the code for the tas client, currently here:
|
||||
// https://github.com/microsoft/tas-client/blob/75f8895b15ef5696653cbee134ccae24477b0b94/vscode-tas-client/src/vscode-tas-client/VSCodeTasClient.ts#L67
|
||||
featuresTelemetryPropertyName = 'VSCode.ABExp.Features',
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ServicesAccessor } from '../../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { extractRepoInfoInBackground } from '../../prompt/repository';
|
||||
import { TelemetryData } from '../../telemetry';
|
||||
import { createLibTestingContext } from '../../test/context';
|
||||
import { makeFsUri } from '../../util/uri';
|
||||
import { ICompletionsFeaturesService } from '../featuresService';
|
||||
|
||||
suite('updateExPValuesAndAssignments', function () {
|
||||
let accessor: ServicesAccessor;
|
||||
|
||||
const filenameUri = makeFsUri(__filename);
|
||||
|
||||
setup(async function () {
|
||||
accessor = createLibTestingContext().createTestingAccessor();
|
||||
// Trigger extractRepoInfoInBackground early + add a sleep to force repo info to be available
|
||||
extractRepoInfoInBackground(accessor, filenameUri);
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
});
|
||||
|
||||
test('If no options are provided, repo filters should be empty and there should be no telemetry properties or measurements', async function () {
|
||||
const featuresService = accessor.get(ICompletionsFeaturesService);
|
||||
const telemetry = await featuresService.updateExPValuesAndAssignments();
|
||||
|
||||
assert.deepStrictEqual(telemetry.properties, {});
|
||||
assert.deepStrictEqual(telemetry.measurements, {});
|
||||
|
||||
const filters = telemetry.filtersAndExp.filters.toHeaders();
|
||||
assert.deepStrictEqual(filters['X-Copilot-Repository'], undefined);
|
||||
assert.deepStrictEqual(filters['X-Copilot-FileType'], undefined);
|
||||
});
|
||||
|
||||
test('If telemetry data is passed as a parameter, it should be used in the resulting telemetry object', async function () {
|
||||
const telemetryData = TelemetryData.createAndMarkAsIssued({ foo: 'bar' }, { baz: 42 });
|
||||
|
||||
const featuresService = accessor.get(ICompletionsFeaturesService);
|
||||
const telemetry = await featuresService.updateExPValuesAndAssignments(undefined, telemetryData);
|
||||
|
||||
assert.deepStrictEqual(telemetry.properties, { foo: 'bar' });
|
||||
assert.deepStrictEqual(telemetry.measurements, { baz: 42 });
|
||||
|
||||
const filters = telemetry.filtersAndExp.filters.toHeaders();
|
||||
assert.deepStrictEqual(filters['X-Copilot-Repository'], undefined);
|
||||
assert.deepStrictEqual(filters['X-Copilot-FileType'], undefined);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { createServiceIdentifier } from '../../../../../util/common/services';
|
||||
import { IInstantiationService } from '../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { ICompletionsFileSystemService } from './fileSystem';
|
||||
import { CopilotTextDocument, ITextDocument, TextDocumentIdentifier, TextDocumentResult } from './textDocument';
|
||||
import { ICompletionsTextDocumentManagerService } from './textDocumentManager';
|
||||
import { isDocumentValid } from './util/documentEvaluation';
|
||||
import { basename } from './util/uri';
|
||||
|
||||
export const ICompletionsFileReaderService = createServiceIdentifier<ICompletionsFileReaderService>('ICompletionsFileReaderService');
|
||||
export interface ICompletionsFileReaderService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
getRelativePath(doc: TextDocumentIdentifier): string | undefined;
|
||||
|
||||
getOrReadTextDocument(doc: TextDocumentIdentifier): Promise<TextDocumentResult>;
|
||||
|
||||
getOrReadTextDocumentWithFakeClientProperties(
|
||||
doc: TextDocumentIdentifier
|
||||
): Promise<TextDocumentResult<ITextDocument>>;
|
||||
}
|
||||
|
||||
export class FileReader implements ICompletionsFileReaderService {
|
||||
declare _serviceBrand: undefined;
|
||||
constructor(
|
||||
@ICompletionsTextDocumentManagerService private readonly documentManagerService: ICompletionsTextDocumentManagerService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ICompletionsFileSystemService private readonly fileSystemService: ICompletionsFileSystemService,
|
||||
) { }
|
||||
|
||||
getRelativePath(doc: TextDocumentIdentifier) {
|
||||
return this.documentManagerService.getRelativePath(doc) ?? basename(doc.uri);
|
||||
}
|
||||
|
||||
getOrReadTextDocument(doc: TextDocumentIdentifier): Promise<TextDocumentResult> {
|
||||
return this.readFile(doc.uri);
|
||||
}
|
||||
|
||||
getOrReadTextDocumentWithFakeClientProperties(
|
||||
doc: TextDocumentIdentifier
|
||||
): Promise<TextDocumentResult<ITextDocument>> {
|
||||
return this.readFile(doc.uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `getOrReadTextDocument` instead
|
||||
*/
|
||||
protected async readFile(uri: string): Promise<TextDocumentResult<ITextDocument>> {
|
||||
const documentResult = await this.documentManagerService.getTextDocumentWithValidation({ uri });
|
||||
if (documentResult.status !== 'notfound') {
|
||||
return documentResult;
|
||||
}
|
||||
try {
|
||||
const fileSizeMB = await this.getFileSizeMB(uri);
|
||||
// Note: the real production behavior actually blocks files larger than 5MB
|
||||
if (fileSizeMB > 1) {
|
||||
// Using notfound instead of invalid because of the mapping in statusFromTextDocumentResult
|
||||
return { status: 'notfound' as const, message: 'File too large' };
|
||||
}
|
||||
const text = await this.doReadFile(uri);
|
||||
|
||||
// Note, that we check for blocked files even for empty files!
|
||||
const rcmResult = await this.instantiationService.invokeFunction(isDocumentValid, { uri });
|
||||
if (rcmResult.status === 'valid') {
|
||||
const doc = CopilotTextDocument.create(uri, 'UNKNOWN', -1, text);
|
||||
return { status: 'valid' as const, document: doc };
|
||||
}
|
||||
|
||||
return rcmResult;
|
||||
} catch (e) {
|
||||
return { status: 'notfound' as const, message: 'File not found' };
|
||||
}
|
||||
}
|
||||
|
||||
private async doReadFile(uri: string) {
|
||||
return await this.fileSystemService.readFileString(uri);
|
||||
}
|
||||
|
||||
private async getFileSizeMB(uri: string) {
|
||||
const stat = await this.fileSystemService.stat(uri);
|
||||
return stat.size / 1024 / 1024;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createServiceIdentifier } from '../../../../../util/common/services';
|
||||
|
||||
/**
|
||||
* `FileType` identifies the type of a file. `SymbolicLink` may be combined
|
||||
* with other types, e.g. `FileType.Directory | FileType.SymbolicLink`.
|
||||
*/
|
||||
export enum FileType {
|
||||
/** The file type is not known. */
|
||||
Unknown = 0,
|
||||
/** The file is a regular file. */
|
||||
File = 1,
|
||||
/** The file is a directory. */
|
||||
Directory = 2,
|
||||
/** The file is a symbolic link. */
|
||||
SymbolicLink = 64,
|
||||
}
|
||||
|
||||
/**
|
||||
* The `FileStat`-type represents metadata about a file
|
||||
*/
|
||||
export interface FileStat {
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* *Note:* If the file changed, it is important to provide an updated `mtime` that advanced
|
||||
* from the previous value. Otherwise there may be optimizations in place that will not show
|
||||
* the updated file contents in an editor for example.
|
||||
*/
|
||||
|
||||
mtime: number;
|
||||
/**
|
||||
* The size in bytes.
|
||||
*
|
||||
* *Note:* If the file changed, it is important to provide an updated `size`. Otherwise there
|
||||
* may be optimizations in place that will not show the updated file contents in an editor for
|
||||
* example.
|
||||
*/
|
||||
size: number;
|
||||
/**
|
||||
* The type of file.
|
||||
*
|
||||
* *Note:* This is a bit field. Multiple flags may be set on it, e.g.
|
||||
* `FileType.File | FileType.SymbolicLink`.
|
||||
*/
|
||||
type: FileType;
|
||||
}
|
||||
|
||||
export type FileIdentifier = string | { readonly uri: string };
|
||||
|
||||
export const ICompletionsFileSystemService = createServiceIdentifier<ICompletionsFileSystemService>('ICompletionsFileSystemService');
|
||||
export interface ICompletionsFileSystemService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
readFileString(uri: FileIdentifier): Promise<string>;
|
||||
stat(uri: FileIdentifier): Promise<FileStat>;
|
||||
readDirectory(uri: FileIdentifier): Promise<[string, FileType][]>;
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { createServiceIdentifier } from '../../../../../../util/common/services';
|
||||
import { CancellationTokenSource } from '../../../types/src';
|
||||
import { ICompletionsFeaturesService } from '../experiments/featuresService';
|
||||
import { LRUCacheMap } from '../helpers/cache';
|
||||
import { ICompletionsLogTargetService, Logger } from '../logger';
|
||||
import { APIChoice } from '../openai/openai';
|
||||
import { Prompt } from '../prompt/prompt';
|
||||
import { TelemetryWithExp } from '../telemetry';
|
||||
import { Deferred } from '../util/async';
|
||||
import { ReplaySubject } from '../util/subject';
|
||||
import { GetNetworkCompletionsType } from './ghostText';
|
||||
|
||||
enum AsyncCompletionRequestState {
|
||||
Completed,
|
||||
Error,
|
||||
Pending,
|
||||
}
|
||||
|
||||
interface BaseAsyncCompletionRequest {
|
||||
cancellationTokenSource: CancellationTokenSource;
|
||||
headerRequestId: string;
|
||||
partialCompletionText?: string;
|
||||
prefix: string;
|
||||
prompt: Prompt;
|
||||
subject: ReplaySubject<AsyncCompletionRequest>;
|
||||
}
|
||||
|
||||
interface PendingAsyncCompletionRequest extends BaseAsyncCompletionRequest {
|
||||
state: AsyncCompletionRequestState.Pending;
|
||||
}
|
||||
|
||||
interface CompletedAsyncCompletionRequest extends BaseAsyncCompletionRequest {
|
||||
state: AsyncCompletionRequestState.Completed;
|
||||
choice: APIChoice;
|
||||
result: GetNetworkCompletionsType;
|
||||
allChoicesPromise: Promise<void>;
|
||||
}
|
||||
|
||||
type AsyncCompletionRequest = PendingAsyncCompletionRequest | CompletedAsyncCompletionRequest;
|
||||
|
||||
export const ICompletionsAsyncManagerService = createServiceIdentifier<ICompletionsAsyncManagerService>('ICompletionsAsyncManagerService');
|
||||
export interface ICompletionsAsyncManagerService {
|
||||
readonly _serviceBrand: undefined;
|
||||
clear(): void;
|
||||
shouldWaitForAsyncCompletions(prefix: string, prompt: Prompt): boolean;
|
||||
updateCompletion(headerRequestId: string, text: string): void;
|
||||
queueCompletionRequest(
|
||||
headerRequestId: string,
|
||||
prefix: string,
|
||||
prompt: Prompt,
|
||||
cancellationTokenSource: CancellationTokenSource,
|
||||
resultPromise: Promise<GetNetworkCompletionsType>
|
||||
): Promise<void>;
|
||||
getFirstMatchingRequestWithTimeout(
|
||||
headerRequestId: string,
|
||||
prefix: string,
|
||||
prompt: Prompt,
|
||||
isSpeculative: boolean,
|
||||
telemetryWithExp: TelemetryWithExp
|
||||
): Promise<[APIChoice, Promise<void>] | undefined>;
|
||||
getFirstMatchingRequest(
|
||||
headerRequestId: string,
|
||||
prefix: string,
|
||||
prompt: Prompt,
|
||||
isSpeculative: boolean
|
||||
): Promise<[APIChoice, Promise<void>] | undefined>;
|
||||
}
|
||||
|
||||
export class AsyncCompletionManager implements ICompletionsAsyncManagerService {
|
||||
declare _serviceBrand: undefined;
|
||||
|
||||
#logger = new Logger('AsyncCompletionManager');
|
||||
|
||||
/** Mapping of headerRequestId to completion request */
|
||||
private readonly requests = new LRUCacheMap<string, AsyncCompletionRequest>(100);
|
||||
|
||||
/** The most recently requested (either via getFirstMatchingRequest or
|
||||
* getFirstMatchingRequestWithTimeout) header request ID. Serves as a lock
|
||||
* for cancellation. Since we only want to cancel requests that don't match
|
||||
* the most recent request prefix. */
|
||||
private mostRecentRequestId = '';
|
||||
|
||||
constructor(
|
||||
@ICompletionsFeaturesService private readonly featuresService: ICompletionsFeaturesService,
|
||||
@ICompletionsLogTargetService private readonly logTarget: ICompletionsLogTargetService,
|
||||
) { }
|
||||
|
||||
clear() {
|
||||
this.requests.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are any candidate completions for the current position.
|
||||
* We need to strike the right balance between queuing completions as the
|
||||
* user types, without queuing one per keystroke. This method should return
|
||||
* true if we don't have any completions that match the current position.
|
||||
* This method should return false if we have reasonable candidates that
|
||||
* match the current position.
|
||||
*/
|
||||
shouldWaitForAsyncCompletions(prefix: string, prompt: Prompt): boolean {
|
||||
// TODO: Consider adding a minimum threshold for candidate completions,
|
||||
// where we will queue more if the user's typing seems to be diverging
|
||||
// from current speculation.
|
||||
for (const [_, request] of this.requests) {
|
||||
if (isCandidate(prefix, prompt, request)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from a FinishedCallback to report partial results as a completion
|
||||
* is streamed back from the server.
|
||||
*/
|
||||
updateCompletion(headerRequestId: string, text: string) {
|
||||
const request = this.requests.get(headerRequestId);
|
||||
if (request === undefined) { return; }
|
||||
request.partialCompletionText = text;
|
||||
request.subject.next(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an in-flight completion request to the requests map for tracking.
|
||||
* Once the request is completed it is removed from the requests map.
|
||||
*/
|
||||
queueCompletionRequest(
|
||||
headerRequestId: string,
|
||||
prefix: string,
|
||||
prompt: Prompt,
|
||||
cancellationTokenSource: CancellationTokenSource,
|
||||
resultPromise: Promise<GetNetworkCompletionsType>
|
||||
) {
|
||||
this.#logger.debug(this.logTarget,
|
||||
`[${headerRequestId}] Queueing async completion request:`,
|
||||
prefix.substring(prefix.lastIndexOf('\n') + 1)
|
||||
);
|
||||
const subject = new ReplaySubject<AsyncCompletionRequest>();
|
||||
this.requests.set(headerRequestId, {
|
||||
state: AsyncCompletionRequestState.Pending,
|
||||
cancellationTokenSource,
|
||||
headerRequestId,
|
||||
prefix,
|
||||
prompt,
|
||||
subject,
|
||||
});
|
||||
return resultPromise
|
||||
.then(result => {
|
||||
this.requests.delete(headerRequestId);
|
||||
if (result.type !== 'success') {
|
||||
this.#logger.debug(this.logTarget, `[${headerRequestId}] Request failed with`, result.reason);
|
||||
subject.error(result.reason);
|
||||
return;
|
||||
}
|
||||
const completed: CompletedAsyncCompletionRequest = {
|
||||
cancellationTokenSource,
|
||||
headerRequestId,
|
||||
prefix,
|
||||
prompt,
|
||||
subject,
|
||||
choice: result.value[0],
|
||||
result,
|
||||
state: AsyncCompletionRequestState.Completed,
|
||||
allChoicesPromise: result.value[1],
|
||||
};
|
||||
this.requests.set(headerRequestId, completed);
|
||||
subject.next(completed);
|
||||
subject.complete();
|
||||
})
|
||||
.catch((e: unknown) => {
|
||||
this.#logger.error(this.logTarget, `[${headerRequestId}] Request errored with`, e);
|
||||
this.requests.delete(headerRequestId);
|
||||
subject.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns the first matching completion or times out. */
|
||||
getFirstMatchingRequestWithTimeout(
|
||||
headerRequestId: string,
|
||||
prefix: string,
|
||||
prompt: Prompt,
|
||||
isSpeculative: boolean,
|
||||
telemetryWithExp: TelemetryWithExp
|
||||
): Promise<[APIChoice, Promise<void>] | undefined> {
|
||||
const timeout = this.featuresService.asyncCompletionsTimeout(telemetryWithExp);
|
||||
if (timeout < 0) {
|
||||
this.#logger.debug(this.logTarget, `[${headerRequestId}] Waiting for completions without timeout`);
|
||||
return this.getFirstMatchingRequest(headerRequestId, prefix, prompt, isSpeculative);
|
||||
}
|
||||
this.#logger.debug(this.logTarget, `[${headerRequestId}] Waiting for completions with timeout of ${timeout}ms`);
|
||||
return Promise.race([
|
||||
this.getFirstMatchingRequest(headerRequestId, prefix, prompt, isSpeculative),
|
||||
new Promise<null>(r => setTimeout(() => r(null), timeout)),
|
||||
]).then(result => {
|
||||
if (result === null) {
|
||||
this.#logger.debug(this.logTarget, `[${headerRequestId}] Timed out waiting for completion`);
|
||||
return undefined;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first resolved matching completion request. Modifies the
|
||||
* returned APIChoice to match the current prompt.
|
||||
*/
|
||||
async getFirstMatchingRequest(
|
||||
headerRequestId: string,
|
||||
prefix: string,
|
||||
prompt: Prompt,
|
||||
isSpeculative: boolean
|
||||
): Promise<[APIChoice, Promise<void>] | undefined> {
|
||||
if (!isSpeculative) { this.mostRecentRequestId = headerRequestId; }
|
||||
let resolved = false;
|
||||
const deferred = new Deferred<[APIChoice, Promise<void>] | undefined>();
|
||||
const subscriptions = new Map<string, () => void>();
|
||||
const finishRequest = (id: string) => () => {
|
||||
const subscription = subscriptions.get(id);
|
||||
if (subscription === undefined) { return; }
|
||||
subscription();
|
||||
subscriptions.delete(id);
|
||||
if (!resolved && subscriptions.size === 0) {
|
||||
// TODO: Check for new candidates before resolving.
|
||||
resolved = true;
|
||||
this.#logger.debug(this.logTarget, `[${headerRequestId}] No matching completions found`);
|
||||
deferred.resolve(undefined);
|
||||
}
|
||||
};
|
||||
const next = (request: AsyncCompletionRequest) => {
|
||||
if (isCandidate(prefix, prompt, request)) {
|
||||
if (request.state === AsyncCompletionRequestState.Completed) {
|
||||
const remainingPrefix = prefix.substring(request.prefix.length);
|
||||
let { completionText } = request.choice;
|
||||
if (
|
||||
!completionText.startsWith(remainingPrefix) ||
|
||||
completionText.length <= remainingPrefix.length
|
||||
) {
|
||||
finishRequest(request.headerRequestId)();
|
||||
return;
|
||||
}
|
||||
completionText = completionText.substring(remainingPrefix.length);
|
||||
request.choice.telemetryData.measurements.foundOffset = remainingPrefix.length;
|
||||
this.#logger.debug(this.logTarget,
|
||||
`[${headerRequestId}] Found completion at offset ${remainingPrefix.length}: ${JSON.stringify(completionText)}`
|
||||
);
|
||||
deferred.resolve([{ ...request.choice, completionText }, request.allChoicesPromise]);
|
||||
resolved = true;
|
||||
}
|
||||
} else {
|
||||
this.cancelRequest(headerRequestId, request);
|
||||
finishRequest(request.headerRequestId)();
|
||||
}
|
||||
};
|
||||
for (const [id, request] of this.requests) {
|
||||
if (isCandidate(prefix, prompt, request)) {
|
||||
subscriptions.set(
|
||||
id,
|
||||
request.subject.subscribe({
|
||||
next,
|
||||
error: finishRequest(id),
|
||||
complete: finishRequest(id),
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this.cancelRequest(headerRequestId, request);
|
||||
}
|
||||
}
|
||||
return deferred.promise.finally(() => {
|
||||
for (const dispose of subscriptions.values()) {
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to cancel a request if it is still pending and the request
|
||||
* attempting the cancellation (that it no longer matches) is the most
|
||||
* recent request.
|
||||
*
|
||||
* @param headerRequestId The request id for the call to
|
||||
* getFirstMatchingRequest that the `request` no longer matches.
|
||||
* @param request The request to cancel
|
||||
*/
|
||||
private cancelRequest(headerRequestId: string, request: AsyncCompletionRequest) {
|
||||
if (headerRequestId !== this.mostRecentRequestId) { return; }
|
||||
if (request.state === AsyncCompletionRequestState.Completed) { return; }
|
||||
this.#logger.debug(this.logTarget, `[${headerRequestId}] Cancelling request: ${request.headerRequestId}`);
|
||||
request.cancellationTokenSource.cancel();
|
||||
this.requests.delete(request.headerRequestId);
|
||||
}
|
||||
}
|
||||
|
||||
function isCandidate(prefix: string, prompt: Prompt, request: AsyncCompletionRequest): boolean {
|
||||
if (request.prompt.suffix !== prompt.suffix) { return false; }
|
||||
if (!prefix.startsWith(request.prefix)) { return false; }
|
||||
const remainingPrefix = prefix.substring(request.prefix.length);
|
||||
if (request.state === AsyncCompletionRequestState.Completed) {
|
||||
return (
|
||||
request.choice.completionText.startsWith(remainingPrefix) &&
|
||||
request.choice.completionText.trimEnd().length > remainingPrefix.length
|
||||
);
|
||||
}
|
||||
if (request.partialCompletionText === undefined) { return true; }
|
||||
return request.partialCompletionText.startsWith(remainingPrefix);
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { StatementNode, StatementTree } from './statementTree';
|
||||
import { IPosition, TextDocumentContents } from '../textDocument';
|
||||
|
||||
/**
|
||||
* BlockTrimmer base class.
|
||||
*/
|
||||
export abstract class BlockTrimmer {
|
||||
static isSupported(languageId: string): boolean {
|
||||
return StatementTree.isSupported(languageId);
|
||||
}
|
||||
|
||||
/** Tests for the subset of supported languages that are trimmed by default */
|
||||
static isTrimmedByDefault(languageId: string): boolean {
|
||||
return StatementTree.isTrimmedByDefault(languageId);
|
||||
}
|
||||
|
||||
constructor(
|
||||
protected readonly languageId: string,
|
||||
protected readonly prefix: string,
|
||||
protected readonly completion: string
|
||||
) { }
|
||||
|
||||
abstract getCompletionTrimOffset(): Promise<number | undefined>;
|
||||
|
||||
protected async withParsedStatementTree<T>(fn: (tree: StatementTree) => Promise<T> | T): Promise<T> {
|
||||
const tree = StatementTree.create(
|
||||
this.languageId,
|
||||
this.prefix + this.completion,
|
||||
this.prefix.length,
|
||||
this.prefix.length + this.completion.length
|
||||
);
|
||||
await tree.build();
|
||||
|
||||
try {
|
||||
return await fn(tree);
|
||||
} finally {
|
||||
tree[Symbol.dispose]();
|
||||
}
|
||||
}
|
||||
|
||||
protected trimmedCompletion(offset: number | undefined): string {
|
||||
return offset === undefined ? this.completion : this.completion.substring(0, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the statement at the cursor position.
|
||||
* If the cursor is not within a statement (e.g. it's on an error node),
|
||||
* returns the first statement from the tree (if any).
|
||||
*/
|
||||
protected getStatementAtCursor(tree: StatementTree): StatementNode | undefined {
|
||||
return tree.statementAt(Math.max(this.prefix.length - 1, 0)) ?? tree.statements[0];
|
||||
}
|
||||
|
||||
protected getContainingBlockOffset(stmt: StatementNode | undefined): number | undefined {
|
||||
let trimTo: StatementNode | undefined;
|
||||
if (stmt && this.isCompoundStatement(stmt)) {
|
||||
// for compound statement types, trim to the current statement
|
||||
trimTo = stmt;
|
||||
} else if (stmt) {
|
||||
// for non-compound statement types, trim to the closest compound ancestor
|
||||
let parent = stmt.parent;
|
||||
while (parent && !this.isCompoundStatement(parent)) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
trimTo = parent;
|
||||
}
|
||||
|
||||
if (trimTo) {
|
||||
const newOffset = this.asCompletionOffset(trimTo.node.endIndex);
|
||||
|
||||
// don't trim trailing whitespace as that will terminate the completion prematurely
|
||||
if (newOffset && this.completion.substring(newOffset).trim() !== '') { return newOffset; }
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected hasNonStatementContentAfter(stmt: StatementNode | undefined): boolean {
|
||||
if (!stmt || !stmt.nextSibling) { return false; }
|
||||
const spanStart = this.asCompletionOffset(stmt.node.endIndex);
|
||||
const spanEnd = this.asCompletionOffset(stmt.nextSibling.node.startIndex);
|
||||
const content = this.completion.substring(Math.max(0, spanStart ?? 0), Math.max(0, spanEnd ?? 0));
|
||||
return content.trim() !== '';
|
||||
}
|
||||
|
||||
protected asCompletionOffset(offset: number | undefined): number | undefined {
|
||||
return offset === undefined ? undefined : offset - this.prefix.length;
|
||||
}
|
||||
|
||||
protected isCompoundStatement(stmt: StatementNode): boolean {
|
||||
return stmt.isCompoundStatementType || stmt.children.length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A block trimmer that tries to obtain the longest reasonable completion
|
||||
* within its line limit. This results in a more verbose completion.
|
||||
*
|
||||
* Don't delete it is used in tests.
|
||||
*/
|
||||
export class VerboseBlockTrimmer extends BlockTrimmer {
|
||||
private readonly offsetLimit: number | undefined;
|
||||
|
||||
constructor(
|
||||
languageId: string,
|
||||
prefix: string,
|
||||
completion: string,
|
||||
private readonly lineLimit: number = 10
|
||||
) {
|
||||
super(languageId, prefix, completion);
|
||||
// determine the end of the lineLimit line as an offset into the completion
|
||||
const completionLineEnds = [...this.completion.matchAll(/\n/g)];
|
||||
if (completionLineEnds.length >= this.lineLimit && this.lineLimit > 0) {
|
||||
this.offsetLimit = completionLineEnds[this.lineLimit - 1].index;
|
||||
} else {
|
||||
this.offsetLimit = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async getCompletionTrimOffset(): Promise<number | undefined> {
|
||||
return await this.withParsedStatementTree(tree => {
|
||||
const stmt = this.getStatementAtCursor(tree);
|
||||
|
||||
// do not go past the containing block
|
||||
let offset = this.getContainingBlockOffset(stmt);
|
||||
|
||||
// first try trimming at a blank line
|
||||
if (!this.isWithinLimit(offset)) {
|
||||
offset = this.trimToBlankLine(offset);
|
||||
}
|
||||
|
||||
// then try trimming at a statement
|
||||
if (!this.isWithinLimit(offset)) {
|
||||
offset = this.trimToStatement(stmt, offset);
|
||||
}
|
||||
|
||||
return offset;
|
||||
});
|
||||
}
|
||||
|
||||
private isWithinLimit(offset: number | undefined): boolean {
|
||||
return this.offsetLimit === undefined || (offset !== undefined && offset <= this.offsetLimit);
|
||||
}
|
||||
|
||||
private trimToBlankLine(offset: number | undefined): number | undefined {
|
||||
const blankLines = [...this.trimmedCompletion(offset).matchAll(/\r?\n\s*\r?\n/g)].reverse();
|
||||
while (blankLines.length > 0 && !this.isWithinLimit(offset)) {
|
||||
const match = blankLines.pop()!;
|
||||
offset = match.index;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private trimToStatement(stmt: StatementNode | undefined, offset: number | undefined): number | undefined {
|
||||
const min = this.prefix.length;
|
||||
const max = this.prefix.length + (this.offsetLimit ?? this.completion.length);
|
||||
let s = stmt;
|
||||
let next = stmt?.nextSibling;
|
||||
while (next && next.node.endIndex <= max && !this.hasNonStatementContentAfter(s)) {
|
||||
s = next;
|
||||
next = next.nextSibling;
|
||||
}
|
||||
if (s && s === stmt && s.node.endIndex <= min) {
|
||||
s = next;
|
||||
}
|
||||
if (s && s.node.endIndex > max) {
|
||||
// break at an internal statement if possible
|
||||
return this.trimToStatement(s.children[0], this.asCompletionOffset(s.node.endIndex));
|
||||
}
|
||||
return this.asCompletionOffset(s?.node?.endIndex) ?? offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A block trimmer that stops when it's likely the end of a logical section has
|
||||
* been reached, such as the start of a new compound statement. This results in
|
||||
* a more terse completion.
|
||||
*/
|
||||
export class TerseBlockTrimmer extends BlockTrimmer {
|
||||
private readonly limitOffset: number | undefined;
|
||||
private readonly lookAheadOffset: number | undefined;
|
||||
|
||||
constructor(
|
||||
languageId: string,
|
||||
prefix: string,
|
||||
completion: string,
|
||||
private readonly lineLimit: number = 3,
|
||||
private readonly lookAhead: number = 7
|
||||
) {
|
||||
super(languageId, prefix, completion);
|
||||
// determine the end of the lineLimit line as an offset into the completion
|
||||
const completionLineEnds = [...this.completion.matchAll(/\n/g)];
|
||||
const limitAndLookAhead = this.lineLimit + this.lookAhead;
|
||||
if (completionLineEnds.length >= this.lineLimit && this.lineLimit > 0) {
|
||||
this.limitOffset = completionLineEnds[this.lineLimit - 1].index;
|
||||
}
|
||||
if (completionLineEnds.length >= limitAndLookAhead && limitAndLookAhead > 0) {
|
||||
this.lookAheadOffset = completionLineEnds[limitAndLookAhead - 1].index;
|
||||
}
|
||||
}
|
||||
|
||||
async getCompletionTrimOffset(): Promise<number | undefined> {
|
||||
return await this.withParsedStatementTree(tree => {
|
||||
const stmt = tree.statementAt(this.stmtStartPos());
|
||||
|
||||
// do not go past the containing block
|
||||
let offset = this.getContainingBlockOffset(stmt);
|
||||
|
||||
// trim at any blank lines
|
||||
offset = this.trimAtFirstBlankLine(offset);
|
||||
|
||||
// trim at new blocks starts or areas of comments
|
||||
if (stmt) {
|
||||
offset = this.trimAtStatementChange(stmt, offset);
|
||||
}
|
||||
|
||||
// hard trim at the line limit if we have enough context
|
||||
if (this.limitOffset && this.lookAheadOffset && (offset === undefined || offset > this.lookAheadOffset)) {
|
||||
return this.limitOffset;
|
||||
}
|
||||
|
||||
return offset;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the position of the first non-whitespace character to the right
|
||||
* of the cursor, or the start of the completion if it is blank.
|
||||
*/
|
||||
private stmtStartPos(): number {
|
||||
const match = this.completion.match(/\S/);
|
||||
if (match && match.index !== undefined) {
|
||||
return this.prefix.length + match.index;
|
||||
}
|
||||
return Math.max(this.prefix.length - 1, 0);
|
||||
}
|
||||
|
||||
private trimAtFirstBlankLine(offset: number | undefined): number | undefined {
|
||||
const blankLines = [...this.trimmedCompletion(offset).matchAll(/\r?\n\s*\r?\n/g)];
|
||||
|
||||
while (blankLines.length > 0 && (offset === undefined || offset > blankLines[0].index)) {
|
||||
const match = blankLines.shift()!;
|
||||
if (this.completion.substring(0, match.index).trim() !== '') {
|
||||
return match.index;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private trimAtStatementChange(stmt: StatementNode, offset: number | undefined): number | undefined {
|
||||
const min = this.prefix.length;
|
||||
const max = this.prefix.length + (offset ?? this.completion.length);
|
||||
|
||||
// if the first statement is a compound statement, trim to the first statement
|
||||
if (stmt.node.endIndex > min && this.isCompoundStatement(stmt)) {
|
||||
// if we have a next sibling, the statement is likely finished
|
||||
if (stmt.nextSibling && stmt.node.endIndex < max) {
|
||||
return this.asCompletionOffset(stmt.node.endIndex);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
// otherwise, stop at the first compound statement or non-statement content
|
||||
let s = stmt;
|
||||
let next = stmt.nextSibling;
|
||||
while (
|
||||
next &&
|
||||
next.node.endIndex <= max &&
|
||||
!this.hasNonStatementContentAfter(s) &&
|
||||
!this.isCompoundStatement(next)
|
||||
) {
|
||||
s = next;
|
||||
next = next.nextSibling;
|
||||
}
|
||||
if (next && s.node.endIndex > min && s.node.endIndex < max) {
|
||||
return this.asCompletionOffset(s.node.endIndex);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
export enum BlockPositionType {
|
||||
NonBlock = 'non-block',
|
||||
EmptyBlock = 'empty-block',
|
||||
BlockEnd = 'block-end',
|
||||
MidBlock = 'mid-block',
|
||||
}
|
||||
|
||||
export async function getBlockPositionType(
|
||||
document: TextDocumentContents,
|
||||
position: IPosition
|
||||
): Promise<BlockPositionType> {
|
||||
const text = document.getText();
|
||||
const offset = document.offsetAt(position);
|
||||
const tree = StatementTree.create(document.detectedLanguageId, text, 0, text.length);
|
||||
try {
|
||||
await tree.build();
|
||||
|
||||
const stmt = tree.statementAt(offset);
|
||||
|
||||
if (!stmt) { return BlockPositionType.NonBlock; }
|
||||
|
||||
if (!stmt.isCompoundStatementType && stmt.children.length === 0) {
|
||||
if (stmt.parent && !stmt.nextSibling && stmt.node.endPosition.row <= position.line) {
|
||||
return BlockPositionType.BlockEnd;
|
||||
} else if (stmt.parent) {
|
||||
return BlockPositionType.MidBlock;
|
||||
}
|
||||
return BlockPositionType.NonBlock;
|
||||
}
|
||||
|
||||
if (stmt.children.length === 0) {
|
||||
return BlockPositionType.EmptyBlock;
|
||||
}
|
||||
|
||||
const lastChild = stmt.children[stmt.children.length - 1];
|
||||
if (offset < lastChild.node.startIndex) {
|
||||
return BlockPositionType.MidBlock;
|
||||
}
|
||||
|
||||
return BlockPositionType.BlockEnd;
|
||||
} finally {
|
||||
tree[Symbol.dispose]();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { createServiceIdentifier } from '../../../../../../util/common/services';
|
||||
import { LRURadixTrie } from '../helpers/radix';
|
||||
import { APIChoice } from '../openai/openai';
|
||||
|
||||
interface CompletionsCacheContents {
|
||||
content: {
|
||||
suffix: string;
|
||||
choice: APIChoice;
|
||||
}[];
|
||||
}
|
||||
|
||||
export const ICompletionsCacheService = createServiceIdentifier<ICompletionsCacheService>('ICompletionsCacheService');
|
||||
export interface ICompletionsCacheService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
/** Given a document prefix and suffix, return all of the completions that match. */
|
||||
findAll(prefix: string, suffix: string): APIChoice[];
|
||||
|
||||
/** Add cached completions for a given prefix. */
|
||||
append(prefix: string, suffix: string, choice: APIChoice): void;
|
||||
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
/** Caches recent completions by document prefix. */
|
||||
export class CompletionsCache implements ICompletionsCacheService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
private cache = new LRURadixTrie<CompletionsCacheContents>(100);
|
||||
|
||||
/** Given a document prefix and suffix, return all of the completions that match. */
|
||||
findAll(prefix: string, suffix: string): APIChoice[] {
|
||||
return this.cache.findAll(prefix).flatMap(({ remainingKey, value }) =>
|
||||
value.content
|
||||
.filter(
|
||||
c =>
|
||||
c.suffix === suffix &&
|
||||
c.choice.completionText.startsWith(remainingKey) &&
|
||||
c.choice.completionText.length > remainingKey.length
|
||||
)
|
||||
.map(c => ({
|
||||
...c.choice,
|
||||
completionText: c.choice.completionText.slice(remainingKey.length),
|
||||
telemetryData: c.choice.telemetryData.extendedBy({}, { foundOffset: remainingKey.length }),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
/** Add cached completions for a given prefix. */
|
||||
append(prefix: string, suffix: string, choice: APIChoice) {
|
||||
const existing = this.cache.findAll(prefix);
|
||||
// Append to an existing array if there is an exact match.
|
||||
if (existing.length > 0 && existing[0].remainingKey === '') {
|
||||
const content = existing[0].value.content;
|
||||
this.cache.set(prefix, { content: [...content, { suffix, choice }] });
|
||||
} else {
|
||||
// Otherwise, add a new value.
|
||||
this.cache.set(prefix, { content: [{ suffix, choice }] });
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.cache = new LRURadixTrie<CompletionsCacheContents>(100);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// The following code was moved from config.ts into here to break the cyclic dependencies
|
||||
|
||||
import { createServiceIdentifier } from '../../../../../../util/common/services';
|
||||
import { IInstantiationService } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { BlockMode } from '../../../../../completions/common/config';
|
||||
import { isSupportedLanguageId } from '../../../prompt/src/parse';
|
||||
import { ConfigKey, getConfig } from '../config';
|
||||
import { ICompletionsFeaturesService } from '../experiments/featuresService';
|
||||
import { TelemetryWithExp } from '../telemetry';
|
||||
import { BlockTrimmer } from './blockTrimmer';
|
||||
import { StatementTree } from './statementTree';
|
||||
|
||||
export const ICompletionsBlockModeConfig = createServiceIdentifier<ICompletionsBlockModeConfig>('ICompletionsBlockModeConfig');
|
||||
export interface ICompletionsBlockModeConfig {
|
||||
readonly _serviceBrand: undefined;
|
||||
forLanguage(languageId: string, telemetryData: TelemetryWithExp): BlockMode;
|
||||
}
|
||||
|
||||
export class ConfigBlockModeConfig implements ICompletionsBlockModeConfig {
|
||||
declare _serviceBrand: undefined;
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ICompletionsFeaturesService private readonly featuresService: ICompletionsFeaturesService,
|
||||
) { }
|
||||
|
||||
forLanguage(languageId: string, telemetryData: TelemetryWithExp): BlockMode {
|
||||
const overrideBlockMode = this.featuresService.overrideBlockMode(telemetryData);
|
||||
if (overrideBlockMode) {
|
||||
return toApplicableBlockMode(overrideBlockMode, languageId);
|
||||
}
|
||||
const progressiveReveal = this.featuresService.enableProgressiveReveal(telemetryData);
|
||||
const config = this.instantiationService.invokeFunction(getConfig, ConfigKey.AlwaysRequestMultiline);
|
||||
if (config ?? progressiveReveal) {
|
||||
return toApplicableBlockMode(BlockMode.MoreMultiline, languageId);
|
||||
}
|
||||
|
||||
if (BlockTrimmer.isTrimmedByDefault(languageId)) {
|
||||
return toApplicableBlockMode(BlockMode.MoreMultiline, languageId);
|
||||
}
|
||||
// special casing once cancellations based on tree-sitter propagate to
|
||||
// the proxy.
|
||||
if (languageId === 'ruby') {
|
||||
return BlockMode.Parsing;
|
||||
}
|
||||
// For existing multiline languages use standard tree-sitter based parsing
|
||||
// plus proxy-side trimming
|
||||
if (isSupportedLanguageId(languageId)) {
|
||||
return BlockMode.ParsingAndServer;
|
||||
}
|
||||
return BlockMode.Server;
|
||||
}
|
||||
}
|
||||
|
||||
function blockModeRequiresTreeSitter(blockMode: BlockMode): boolean {
|
||||
return [BlockMode.Parsing, BlockMode.ParsingAndServer, BlockMode.MoreMultiline].includes(blockMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents tree-sitter parsing from being applied to languages we don't include
|
||||
* parsers for.
|
||||
*/
|
||||
function toApplicableBlockMode(blockMode: BlockMode, languageId: string): BlockMode {
|
||||
if (blockMode === BlockMode.MoreMultiline && StatementTree.isSupported(languageId)) {
|
||||
return blockMode;
|
||||
}
|
||||
if (blockModeRequiresTreeSitter(blockMode) && !isSupportedLanguageId(languageId)) {
|
||||
return BlockMode.Server;
|
||||
}
|
||||
return blockMode;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
export const contextualFilterCharacterMap: { [key: string]: number } = {
|
||||
' ': 1,
|
||||
'!': 2,
|
||||
'"': 3,
|
||||
'#': 4,
|
||||
$: 5,
|
||||
'%': 6,
|
||||
'&': 7,
|
||||
'\'': 8,
|
||||
'(': 9,
|
||||
')': 10,
|
||||
'*': 11,
|
||||
'+': 12,
|
||||
',': 13,
|
||||
'-': 14,
|
||||
'.': 15,
|
||||
'/': 16,
|
||||
'0': 17,
|
||||
'1': 18,
|
||||
'2': 19,
|
||||
'3': 20,
|
||||
'4': 21,
|
||||
'5': 22,
|
||||
'6': 23,
|
||||
'7': 24,
|
||||
'8': 25,
|
||||
'9': 26,
|
||||
':': 27,
|
||||
';': 28,
|
||||
'<': 29,
|
||||
'=': 30,
|
||||
'>': 31,
|
||||
'?': 32,
|
||||
'@': 33,
|
||||
A: 34,
|
||||
B: 35,
|
||||
C: 36,
|
||||
D: 37,
|
||||
E: 38,
|
||||
F: 39,
|
||||
G: 40,
|
||||
H: 41,
|
||||
I: 42,
|
||||
J: 43,
|
||||
K: 44,
|
||||
L: 45,
|
||||
M: 46,
|
||||
N: 47,
|
||||
O: 48,
|
||||
P: 49,
|
||||
Q: 50,
|
||||
R: 51,
|
||||
S: 52,
|
||||
T: 53,
|
||||
U: 54,
|
||||
V: 55,
|
||||
W: 56,
|
||||
X: 57,
|
||||
Y: 58,
|
||||
Z: 59,
|
||||
'[': 60,
|
||||
'\\': 61,
|
||||
']': 62,
|
||||
'^': 63,
|
||||
_: 64,
|
||||
'`': 65,
|
||||
a: 66,
|
||||
b: 67,
|
||||
c: 68,
|
||||
d: 69,
|
||||
e: 70,
|
||||
f: 71,
|
||||
g: 72,
|
||||
h: 73,
|
||||
i: 74,
|
||||
j: 75,
|
||||
k: 76,
|
||||
l: 77,
|
||||
m: 78,
|
||||
n: 79,
|
||||
o: 80,
|
||||
p: 81,
|
||||
q: 82,
|
||||
r: 83,
|
||||
s: 84,
|
||||
t: 85,
|
||||
u: 86,
|
||||
v: 87,
|
||||
w: 88,
|
||||
x: 89,
|
||||
y: 90,
|
||||
z: 91,
|
||||
'{': 92,
|
||||
'|': 93,
|
||||
'}': 94,
|
||||
'~': 95,
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { CopilotNamedAnnotationList } from '../../../../../../platform/completions-core/common/openai/copilotAnnotations';
|
||||
import { generateUuid } from '../../../../../../util/vs/base/common/uuid';
|
||||
import { TelemetryWithExp } from '../telemetry';
|
||||
import { IPosition, IRange, LocationFactory, TextDocumentContents } from '../textDocument';
|
||||
import { CompletionResult, ResultType } from './ghostText';
|
||||
import { ITextEditorOptions, normalizeIndentCharacter } from './normalizeIndent';
|
||||
|
||||
export interface CopilotCompletion {
|
||||
uuid: string;
|
||||
insertText: string;
|
||||
range: IRange;
|
||||
uri: string;
|
||||
telemetry: TelemetryWithExp;
|
||||
displayText: string;
|
||||
position: IPosition;
|
||||
offset: number;
|
||||
index: number;
|
||||
resultType: ResultType;
|
||||
copilotAnnotations?: CopilotNamedAnnotationList;
|
||||
clientCompletionId: string;
|
||||
}
|
||||
|
||||
export function completionsFromGhostTextResults(
|
||||
completionResults: CompletionResult[],
|
||||
resultType: ResultType,
|
||||
document: TextDocumentContents,
|
||||
position: IPosition,
|
||||
textEditorOptions?: ITextEditorOptions,
|
||||
lastShownCompletionIndex?: number
|
||||
): CopilotCompletion[] {
|
||||
const currentLine = document.lineAt(position);
|
||||
let completions = completionResults.map(result => {
|
||||
const range = LocationFactory.range(
|
||||
LocationFactory.position(position.line, 0),
|
||||
LocationFactory.position(position.line, position.character + result.suffixCoverage)
|
||||
);
|
||||
let insertText = '';
|
||||
if (textEditorOptions) {
|
||||
result.completion = normalizeIndentCharacter(
|
||||
textEditorOptions,
|
||||
result.completion,
|
||||
currentLine.isEmptyOrWhitespace
|
||||
);
|
||||
}
|
||||
if (
|
||||
currentLine.isEmptyOrWhitespace &&
|
||||
(result.completion.displayNeedsWsOffset || // Deindenting case
|
||||
// This enables stable behavior for deleting whitespace on blank lines
|
||||
result.completion.completionText.startsWith(currentLine.text))
|
||||
) {
|
||||
insertText = result.completion.completionText;
|
||||
} else {
|
||||
const rangeFromStart = LocationFactory.range(range.start, position);
|
||||
insertText = document.getText(rangeFromStart) + result.completion.displayText;
|
||||
}
|
||||
|
||||
const completion: CopilotCompletion = {
|
||||
uuid: generateUuid(),
|
||||
insertText,
|
||||
range,
|
||||
uri: document.uri,
|
||||
index: result.completion.completionIndex,
|
||||
telemetry: result.telemetry,
|
||||
displayText: result.completion.displayText,
|
||||
position,
|
||||
offset: document.offsetAt(position),
|
||||
resultType,
|
||||
copilotAnnotations: result.copilotAnnotations,
|
||||
clientCompletionId: result.clientCompletionId,
|
||||
};
|
||||
return completion;
|
||||
});
|
||||
//If we are in typing as suggested flow, we want to put the last displayed completion at the top of the list to keep it selected
|
||||
if (resultType === ResultType.TypingAsSuggested && lastShownCompletionIndex !== undefined) {
|
||||
const lastShownCompletion = completions.find(predicate => predicate.index === lastShownCompletionIndex);
|
||||
if (lastShownCompletion) {
|
||||
const restCompletions = completions.filter(predicate => predicate.index !== lastShownCompletionIndex);
|
||||
completions = [lastShownCompletion, ...restCompletions];
|
||||
}
|
||||
}
|
||||
return completions;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user