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