extHostTerminalService.ts ×109

Frontier kind: Code frontier

unlabeled · c_9f8e9cb6c4e0

42 tests · 75317 LOC · 255 files · introduces 0 tests · 894 LOC · 5 files

Introduces — evidence that enters the hierarchy at this concept

Code
163 ranges894 lines · 5 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
4860 ranges75317 lines · 255 files · Browse complete extent
All tests (intent)
42 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

5 files ranked by introduced lines: 894 introduced LOC across 163 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/api/common/extHostTerminalService.ts 423 introduced LOC · 109 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostTerminalService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type * as vscode from 'vscode';
7 > import { Event, Emitter } from '../../../base/common/event.js';
8 > import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, ITerminalDimensionsDto, ITerminalLinkDto, ExtHostTerminalIdentifier, ICommandDto, ITerminalQuickFixOpenerDto, ITerminalQuickFixTerminalCommandDto, TerminalCommandMatchResultDto, ITerminalCommandDto, ITerminalCompletionContextDto, TerminalCompletionListDto } from './extHost.protocol.js';
9 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
10 > import { URI } from '../../../base/common/uri.js';
11 > import { IExtHostRpcService } from './extHostRpcService.js';
12 > import { IDisposable, DisposableStore, Disposable, MutableDisposable } from '../../../base/common/lifecycle.js';
13 > import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType, TerminalExitReason, TerminalCompletionItem } from './extHostTypes.js';
14 > import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
15 > import { localize } from '../../../nls.js';
16 > import { NotSupportedError } from '../../../base/common/errors.js';
17 > import { serializeEnvironmentDescriptionMap, serializeEnvironmentVariableCollection } from '../../../platform/terminal/common/environmentVariableShared.js';
18 > import { CancellationTokenSource } from '../../../base/common/cancellation.js';
19 > import { generateUuid } from '../../../base/common/uuid.js';
20 > import { IEnvironmentVariableCollectionDescription, IEnvironmentVariableMutator, ISerializableEnvironmentVariableCollection } from '../../../platform/terminal/common/environmentVariable.js';
21 > import { ICreateContributedTerminalProfileOptions, IProcessReadyEvent, IShellLaunchConfigDto, ITerminalChildProcess, ITerminalLaunchError, ITerminalProfile, TerminalIcon, TerminalLocation, IProcessProperty, ProcessPropertyType, IProcessPropertyMap, TerminalShellType, WindowsShellType } from '../../../platform/terminal/common/terminal.js';
22 > import { TerminalDataBufferer } from '../../../platform/terminal/common/terminalDataBuffering.js';
23 > import { ThemeColor } from '../../../base/common/themables.js';
24 > import { Promises } from '../../../base/common/async.js';
25 > import { EditorGroupColumn } from '../../services/editor/common/editorGroupColumn.js';
26 > import { TerminalCompletionList, TerminalQuickFix, ViewColumn } from './extHostTypeConverters.js';
27 > import { IExtHostCommands } from './extHostCommands.js';
28 > import { IExtHostInitDataService } from './extHostInitDataService.js';
29 > import { MarshalledId } from '../../../base/common/marshallingIds.js';
30 > import { ISerializedTerminalInstanceContext } from '../../contrib/terminal/common/terminal.js';
31 > import { isWindows } from '../../../base/common/platform.js';
32 > import { hasKey } from '../../../base/common/types.js';
33 > import { isProposedApiEnabled } from '../../services/extensions/common/extensions.js';
34 >
35 > export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable {
36 >
37 > readonly _serviceBrand: undefined;
38 >
39 > activeTerminal: vscode.Terminal | undefined;
40 > terminals: vscode.Terminal[];
41 >
42 > readonly onDidCloseTerminal: Event<vscode.Terminal>;
43 > readonly onDidOpenTerminal: Event<vscode.Terminal>;
44 > readonly onDidChangeActiveTerminal: Event<vscode.Terminal | undefined>;
45 > readonly onDidChangeTerminalDimensions: Event<vscode.TerminalDimensionsChangeEvent>;
46 > readonly onDidChangeTerminalState: Event<vscode.Terminal>;
47 > readonly onDidWriteTerminalData: Event<vscode.TerminalDataWriteEvent>;
48 > readonly onDidExecuteTerminalCommand: Event<vscode.TerminalExecutedCommand>;
49 > readonly onDidChangeShell: Event<string>;
50 >
51 > createTerminal(name?: string, shellPath?: string, shellArgs?: readonly string[] | string): vscode.Terminal;
52 > createTerminalFromOptions(options: vscode.TerminalOptions, internalOptions?: ITerminalInternalOptions): vscode.Terminal;
53 > createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal;
54 > attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void;
55 > getDefaultShell(useAutomationShell: boolean): string;
56 > getDefaultShellArgs(useAutomationShell: boolean): string[] | string;
57 > registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable;
58 > registerProfileProvider(extension: IExtensionDescription, id: string, provider: vscode.TerminalProfileProvider): vscode.Disposable;
59 > registerTerminalQuickFixProvider(id: string, extensionId: string, provider: vscode.TerminalQuickFixProvider): vscode.Disposable;
60 > getEnvironmentVariableCollection(extension: IExtensionDescription): IEnvironmentVariableCollection;
61 > getTerminalById(id: number): ExtHostTerminal | null;
62 > getTerminalIdByApiObject(apiTerminal: vscode.Terminal): number | null;
63 > registerTerminalCompletionProvider(extension: IExtensionDescription, provider: vscode.TerminalCompletionProvider<vscode.TerminalCompletionItem>, ...triggerCharacters: string[]): vscode.Disposable;
64 > }
65 >
66 > interface IEnvironmentVariableCollection extends vscode.EnvironmentVariableCollection {
67 > getScoped(scope: vscode.EnvironmentVariableScope): vscode.EnvironmentVariableCollection;
68 > }
69 >
70 > export interface ITerminalInternalOptions {
71 > cwd?: string | URI;
72 > isFeatureTerminal?: boolean;
73 > forceShellIntegration?: boolean;
74 > useShellEnvironment?: boolean;
75 > resolvedExtHostIdentifier?: ExtHostTerminalIdentifier;
76 > /**
77 > * This location is different from the API location because it can include splitActiveTerminal,
78 > * a property we resolve internally
79 > */
80 > location?: TerminalLocation | { viewColumn: number; preserveState?: boolean } | { splitActiveTerminal: boolean };
81 > }
82 >
83 > export const IExtHostTerminalService = createDecorator<IExtHostTerminalService>('IExtHostTerminalService');
84 >
85 > export class ExtHostTerminal extends Disposable {
86 > private _disposed: boolean = false;
87 > private _pidPromise: Promise<number | undefined>;
88 > private _cols: number | undefined;
89 > private _pidPromiseComplete: ((value: number | undefined) => unknown) | undefined;
90 > private _rows: number | undefined;
91 > private _exitStatus: vscode.TerminalExitStatus | undefined;
92 > private _state: vscode.TerminalState = { isInteractedWith: false, shell: undefined };
93 > private _selection: string | undefined;
94 >
95 > shellIntegration: vscode.TerminalShellIntegration | undefined;
96 >
97 > public isOpen: boolean = false;
98 >
99 > readonly value: vscode.Terminal;
100 >
101 > protected readonly _onWillDispose = this._register(new Emitter<void>());
102 > readonly onWillDispose = this._onWillDispose.event;
103 >
104 > constructor(
105 private _proxy: MainThreadTerminalServiceShape,
106 public _id: ExtHostTerminalIdentifier,
165 };
166 }
168 > override dispose(): void {
169 this._onWillDispose.fire();
170 super.dispose();
171 }
173 > public async create(
174 options: vscode.TerminalOptions,
175 internalOptions?: ITerminalInternalOptions,
199 });
200 }
202 >
203 > public async createExtensionTerminal(location?: TerminalLocation | vscode.TerminalEditorLocationOptions | vscode.TerminalSplitLocationOptions, internalOptions?: ITerminalInternalOptions, parentTerminal?: ExtHostTerminalIdentifier, iconPath?: TerminalIcon, color?: ThemeColor, shellIntegrationNonce?: string, titleTemplate?: string): Promise<number> {
204 if (typeof this._id !== 'string') {
205 throw new Error('Terminal has already been created');
221 return this._id;
222 }
224 > private _serializeParentTerminal(location?: TerminalLocation | vscode.TerminalEditorLocationOptions | vscode.TerminalSplitLocationOptions, parentTerminal?: ExtHostTerminalIdentifier): TerminalLocation | { viewColumn: EditorGroupColumn; preserveFocus?: boolean } | { parentTerminal: ExtHostTerminalIdentifier } | undefined {
225 if (typeof location === 'object') {
226 if (hasKey(location, { parentTerminal: true }) && location.parentTerminal && parentTerminal) {
237 return location;
238 }
240 > private _checkDisposed() {
241 if (this._disposed) {
242 throw new Error('Terminal has already been disposed');
243 }
244 }
246 > public set name(name: string) {
247 this._name = name;
248 }
250 > public setExitStatus(code: number | undefined, reason: TerminalExitReason) {
251 this._exitStatus = Object.freeze({ code, reason });
252 }
254 > public setDimensions(cols: number, rows: number): boolean {
255 if (cols === this._cols && rows === this._rows) {
256 // Nothing changed
264 return true;
265 }
267 > public setInteractedWith(): boolean {
268 if (!this._state.isInteractedWith) {
269 this._state = {
275 return false;
276 }
278 > public setShellType(shellType: TerminalShellType | undefined): boolean {
279
280 if (this._state.shell !== shellType) {
287 return false;
288 }
290 > public setSelection(selection: string | undefined): void {
291 this._selection = selection;
292 }
294 > public _setProcessId(processId: number | undefined): void {
295 // The event may fire 2 times when the panel is restored
296 if (this._pidPromiseComplete) {
306 }
307 }
309 >
310 > class ExtHostPseudoterminal implements ITerminalChildProcess {
311 > readonly id = 0;
312 > readonly shouldPersist = false;
313 >
314 > private readonly _onProcessData = new Emitter<string>();
315 > public readonly onProcessData: Event<string> = this._onProcessData.event;
316 > private readonly _onProcessReady = new Emitter<IProcessReadyEvent>();
317 > public get onProcessReady(): Event<IProcessReadyEvent> { return this._onProcessReady.event; }
318 > private readonly _onDidChangeProperty = new Emitter<IProcessProperty>();
319 > public readonly onDidChangeProperty = this._onDidChangeProperty.event;
320 > private readonly _onProcessExit = new Emitter<number | undefined>();
321 > public readonly onProcessExit: Event<number | undefined> = this._onProcessExit.event;
322 >
323 > constructor(private readonly _pty: vscode.Pseudoterminal) { }
324 >
325 > refreshProperty<T extends ProcessPropertyType>(property: ProcessPropertyType): Promise<IProcessPropertyMap[T]> {
326 throw new Error(`refreshProperty is not suppported in extension owned terminals. property: ${property}`);
327 }
329 > updateProperty<T extends ProcessPropertyType>(property: ProcessPropertyType, value: IProcessPropertyMap[T]): Promise<void> {
330 throw new Error(`updateProperty is not suppported in extension owned terminals. property: ${property}, value: ${value}`);
331 }
333 > async start(): Promise<undefined> {
334 return undefined;
335 }
337 > shutdown(): void {
338 this._pty.close();
339 }
341 > input(data: string): void {
342 this._pty.handleInput?.(data);
343 }
345 > sendSignal(signal: string): void {
346 // Extension owned terminals don't support sending signals directly to processes
347 // This could be extended in the future if the pseudoterminal API is enhanced
348 }
350 > resize(cols: number, rows: number): void {
351 this._pty.setDimensions?.({ columns: cols, rows });
352 }
354 > clearBuffer(): void {
355 // no-op
356 }
358 > async processBinary(data: string): Promise<void> {
359 // No-op, processBinary is not supported in extension owned terminals.
360 }
362 > acknowledgeDataEvent(charCount: number): void {
363 // No-op, flow control is not supported in extension owned terminals. If this is ever
364 // implemented it will need new pause and resume VS Code APIs.
365 }
367 > async setUnicodeVersion(version: '6' | '11'): Promise<void> {
368 // No-op, xterm-headless isn't used for extension owned terminals.
369 }
371 > getInitialCwd(): Promise<string> {
372 return Promise.resolve('');
373 }
375 > getCwd(): Promise<string> {
376 return Promise.resolve('');
377 }
379 > startSendingEvents(initialDimensions: ITerminalDimensionsDto | undefined): void {
380 // Attach the listeners
381 this._pty.onDidWrite(e => this._onProcessData.fire(e));
400 this._onProcessReady.fire({ pid: -1, cwd: '', windowsPty: undefined });
401 }
403 >
404 > let nextLinkId = 1;
405 >
406 > interface ICachedLinkEntry {
407 > provider: vscode.TerminalLinkProvider;
408 > link: vscode.TerminalLink;
409 > }
410 >
411 > export abstract class BaseExtHostTerminalService extends Disposable implements IExtHostTerminalService, ExtHostTerminalServiceShape {
412 >
413 > readonly _serviceBrand: undefined;
414 >
415 > protected _proxy: MainThreadTerminalServiceShape;
416 > protected _activeTerminal: ExtHostTerminal | undefined;
417 > protected _terminals: ExtHostTerminal[] = [];
418 > protected _terminalProcesses: Map<number, ITerminalChildProcess> = new Map();
419 > protected _terminalProcessDisposables: { [id: number]: IDisposable } = {};
420 > protected _extensionTerminalAwaitingStart: { [id: number]: { initialDimensions: ITerminalDimensionsDto | undefined } | undefined } = {};
421 > protected _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal | undefined> } = {};
422 > protected _environmentVariableCollections: Map<string, UnifiedEnvironmentVariableCollection> = new Map();
423 > private _defaultProfile: ITerminalProfile | undefined;
424 > private _defaultAutomationProfile: ITerminalProfile | undefined;
425 > private readonly _lastQuickFixCommands: MutableDisposable<IDisposable> = this._register(new MutableDisposable());
426 >
427 > private readonly _bufferer: TerminalDataBufferer;
428 > private readonly _linkProviders: Set<vscode.TerminalLinkProvider> = new Set();
429 > private readonly _completionProviders: Map<string, vscode.TerminalCompletionProvider<vscode.TerminalCompletionItem>> = new Map();
430 > private readonly _profileProviders: Map<string, { provider: vscode.TerminalProfileProvider; extension: IExtensionDescription }> = new Map();
431 > private readonly _quickFixProviders: Map<string, vscode.TerminalQuickFixProvider> = new Map();
432 > private readonly _terminalLinkCache: Map<number, Map<number, ICachedLinkEntry>> = new Map();
433 > private readonly _terminalLinkCancellationSource: Map<number, CancellationTokenSource> = new Map();
434 >
435 > public get activeTerminal(): vscode.Terminal | undefined { return this._activeTerminal?.value; }
436 > public get terminals(): vscode.Terminal[] { return this._terminals.map(term => term.value); }
437 >
438 > protected readonly _onDidCloseTerminal = new Emitter<vscode.Terminal>();
439 > readonly onDidCloseTerminal = this._onDidCloseTerminal.event;
440 > protected readonly _onDidOpenTerminal = new Emitter<vscode.Terminal>();
441 > readonly onDidOpenTerminal = this._onDidOpenTerminal.event;
442 > protected readonly _onDidChangeActiveTerminal = new Emitter<vscode.Terminal | undefined>();
443 > readonly onDidChangeActiveTerminal = this._onDidChangeActiveTerminal.event;
444 > protected readonly _onDidChangeTerminalDimensions = new Emitter<vscode.TerminalDimensionsChangeEvent>();
445 > readonly onDidChangeTerminalDimensions = this._onDidChangeTerminalDimensions.event;
446 > protected readonly _onDidChangeTerminalState = new Emitter<vscode.Terminal>();
447 > readonly onDidChangeTerminalState = this._onDidChangeTerminalState.event;
448 > protected readonly _onDidChangeShell = new Emitter<string>();
449 > readonly onDidChangeShell = this._onDidChangeShell.event;
450 >
451 > protected readonly _onDidWriteTerminalData = new Emitter<vscode.TerminalDataWriteEvent>({
452 > onWillAddFirstListener: () => this._proxy.$startSendingDataEvents(),
453 > onDidRemoveLastListener: () => this._proxy.$stopSendingDataEvents()
454 > });
455 > readonly onDidWriteTerminalData = this._onDidWriteTerminalData.event;
456 > protected readonly _onDidExecuteCommand = new Emitter<vscode.TerminalExecutedCommand>({
457 > onWillAddFirstListener: () => this._proxy.$startSendingCommandEvents(),
458 > onDidRemoveLastListener: () => this._proxy.$stopSendingCommandEvents()
459 > });
460 > readonly onDidExecuteTerminalCommand = this._onDidExecuteCommand.event;
461 >
462 > constructor(
463 supportsProcesses: boolean,
464 @IExtHostCommands private readonly _extHostCommands: IExtHostCommands,
501 });
502 }
504 > public abstract createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal;
505 > public abstract createTerminalFromOptions(options: vscode.TerminalOptions, internalOptions?: ITerminalInternalOptions): vscode.Terminal;
506 >
507 > public getDefaultShell(useAutomationShell: boolean): string {
508 const profile = useAutomationShell ? this._defaultAutomationProfile : this._defaultProfile;
509 return profile?.path || '';
510 }
512 > public getDefaultShellArgs(useAutomationShell: boolean): string[] | string {
513 const profile = useAutomationShell ? this._defaultAutomationProfile : this._defaultProfile;
514 return profile?.args || [];
515 }
517 > public createExtensionTerminal(options: vscode.ExtensionTerminalOptions, internalOptions?: ITerminalInternalOptions): vscode.Terminal {
518 const terminal = new ExtHostTerminal(this._proxy, generateUuid(), options, options.name);
519 const p = new ExtHostPseudoterminal(options.pty);
525 return terminal.value;
526 }
528 > protected _serializeParentTerminal(options: vscode.TerminalOptions, internalOptions?: ITerminalInternalOptions): ITerminalInternalOptions {
529 internalOptions = internalOptions ? internalOptions : {};
530 if (options.location && typeof options.location === 'object' && hasKey(options.location, { parentTerminal: true })) {
543 return internalOptions;
544 }
546 > public attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void {
547 const terminal = this.getTerminalById(id);
548 if (!terminal) {
553 this._terminalProcessDisposables[id] = disposable;
554 }
556 > public async $acceptActiveTerminalChanged(id: number | null): Promise<void> {
557 const original = this._activeTerminal;
558 if (id === null) {
571 }
572 }
574 > public async $acceptTerminalProcessData(id: number, data: string): Promise<void> {
575 const terminal = this.getTerminalById(id);
576 if (terminal) {
578 }
579 }
581 > public async $acceptTerminalDimensions(id: number, cols: number, rows: number): Promise<void> {
582 const terminal = this.getTerminalById(id);
583 if (terminal) {
590 }
591 }
593 > public async $acceptDidExecuteCommand(id: number, command: ITerminalCommandDto): Promise<void> {
594 const terminal = this.getTerminalById(id);
595 if (terminal) {
597 }
598 }
600 > public async $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): Promise<void> {
601 // Extension pty terminal only - when virtual process resize fires it means that the
602 // terminal's maximum dimensions changed
603 this._terminalProcesses.get(id)?.resize(cols, rows);
604 }
606 > public async $acceptTerminalTitleChange(id: number, name: string): Promise<void> {
607 const terminal = this.getTerminalById(id);
608 if (terminal) {
610 }
611 }
613 > public async $acceptTerminalClosed(id: number, exitCode: number | undefined, exitReason: TerminalExitReason): Promise<void> {
614 // Release any cached terminal links and cancel in-flight link providers for this terminal
615 this._terminalLinkCache.delete(id);
627 }
628 }
630 > public $acceptTerminalOpened(id: number, extHostTerminalId: string | undefined, name: string, shellLaunchConfigDto: IShellLaunchConfigDto): void {
631 if (extHostTerminalId) {
632 // Resolve with the renderer generated id
655 terminal.isOpen = true;
656 }
658 > public async $acceptTerminalProcessId(id: number, processId: number): Promise<void> {
659 const terminal = this.getTerminalById(id);
660 terminal?._setProcessId(processId);
661 }
663 > public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<ITerminalLaunchError | undefined> {
664 // Make sure the ExtHostTerminal exists so onDidOpenTerminal has fired before we call
665 // Pseudoterminal.start
692 return undefined;
693 }
695 > protected _setupExtHostProcessListeners(id: number, p: ITerminalChildProcess): IDisposable {
696 const disposables = new DisposableStore();
697 disposables.add(p.onProcessReady(e => this._proxy.$sendProcessReady(id, e.pid, e.cwd, e.windowsPty)));
711 return disposables;
712 }
714 > public $acceptProcessAckDataEvent(id: number, charCount: number): void {
715 this._terminalProcesses.get(id)?.acknowledgeDataEvent(charCount);
716 }
718 > public $acceptProcessInput(id: number, data: string): void {
719 this._terminalProcesses.get(id)?.input(data);
720 }
722 > public $acceptTerminalInteraction(id: number): void {
723 const terminal = this.getTerminalById(id);
724 if (terminal?.setInteractedWith()) {
726 }
727 }
729 > public $acceptTerminalSelection(id: number, selection: string | undefined): void {
730 this.getTerminalById(id)?.setSelection(selection);
731 }
733 > public $acceptProcessResize(id: number, cols: number, rows: number): void {
734 try {
735 this._terminalProcesses.get(id)?.resize(cols, rows);
741 }
742 }
744 > public $acceptProcessShutdown(id: number, immediate: boolean): void {
745 this._terminalProcesses.get(id)?.shutdown(immediate);
746 }
748 > public $acceptProcessRequestInitialCwd(id: number): void {
749 this._terminalProcesses.get(id)?.getInitialCwd().then(initialCwd => this._proxy.$sendProcessProperty(id, { type: ProcessPropertyType.InitialCwd, value: initialCwd }));
750 }
752 > public $acceptProcessRequestCwd(id: number): void {
753 this._terminalProcesses.get(id)?.getCwd().then(cwd => this._proxy.$sendProcessProperty(id, { type: ProcessPropertyType.Cwd, value: cwd }));
754 }
756 > public $acceptProcessRequestLatency(id: number): Promise<number> {
757 return Promise.resolve(id);
758 }
760 >
761 > public registerProfileProvider(extension: IExtensionDescription, id: string, provider: vscode.TerminalProfileProvider): vscode.Disposable {
762 if (this._profileProviders.has(id)) {
763 throw new Error(`Terminal profile provider "${id}" already registered`);
770 });
771 }
773 > public registerTerminalCompletionProvider(extension: IExtensionDescription, provider: vscode.TerminalCompletionProvider<TerminalCompletionItem>, ...triggerCharacters: string[]): vscode.Disposable {
774 if (this._completionProviders.has(extension.identifier.value)) {
775 throw new Error(`Terminal completion provider "${extension.identifier.value}" already registered`);
782 });
783 }
785 > public async $provideTerminalCompletions(id: string, options: ITerminalCompletionContextDto): Promise<TerminalCompletionListDto | undefined> {
786 const token = new CancellationTokenSource().token;
787 if (token.isCancellationRequested || !this.activeTerminal) {
801 return TerminalCompletionList.from(completions, pathSeparator);
802 }
804 > public $acceptTerminalShellType(id: number, shellType: TerminalShellType | undefined): void {
805 const terminal = this.getTerminalById(id);
806 if (terminal?.setShellType(shellType)) {
808 }
809 }
811 > public registerTerminalQuickFixProvider(id: string, extensionId: string, provider: vscode.TerminalQuickFixProvider): vscode.Disposable {
812 if (this._quickFixProviders.has(id)) {
813 throw new Error(`Terminal quick fix provider "${id}" is already registered`);
820 });
821 }
823 > public async $provideTerminalQuickFixes(id: string, matchResult: TerminalCommandMatchResultDto): Promise<(ITerminalQuickFixTerminalCommandDto | ITerminalQuickFixOpenerDto | ICommandDto)[] | ITerminalQuickFixTerminalCommandDto | ITerminalQuickFixOpenerDto | ICommandDto | undefined> {
824 const token = new CancellationTokenSource().token;
825 if (token.isCancellationRequested) {
853 return result;
854 }
856 > public async $createContributedProfileTerminal(id: string, options: ICreateContributedTerminalProfileOptions): Promise<void> {
857 const token = new CancellationTokenSource().token;
858 const profileProviderData = this._profileProviders.get(id);
893 this.createTerminalFromOptions(profileOptions, options);
894 }
896 > public registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable {
897 this._linkProviders.add(provider);
898 if (this._linkProviders.size === 1) {
906 });
907 }
909 > public async $provideLinks(terminalId: number, line: string): Promise<ITerminalLinkDto[]> {
910 const terminal = this.getTerminalById(terminalId);
911 if (!terminal) {
969 return result;
970 }
972 > $activateLink(terminalId: number, linkId: number): void {
973 const cachedLink = this._terminalLinkCache.get(terminalId)?.get(linkId);
974 if (!cachedLink) {
977 cachedLink.provider.handleTerminalLink(cachedLink.link);
978 }
980 > private _onProcessExit(id: number, exitCode: number | undefined): void {
981 this._bufferer.stopBuffering(id);
982
994 this._proxy.$sendProcessExit(id, exitCode);
995 }
997 > public getTerminalById(id: number): ExtHostTerminal | null {
998 return this._getTerminalObjectById(this._terminals, id);
999 }
1001 > public getTerminalIdByApiObject(terminal: vscode.Terminal): number | null {
1002 const index = this._terminals.findIndex(item => {
1003 return item.value === terminal;
1005 return index >= 0 ? index : null;
1006 }
1008 > private _getTerminalObjectById<T extends ExtHostTerminal>(array: T[], id: number): T | null {
1009 const index = this._getTerminalObjectIndexById(array, id);
1010 return index !== null ? array[index] : null;
1011 }
1013 > private _getTerminalObjectIndexById<T extends ExtHostTerminal>(array: T[], id: ExtHostTerminalIdentifier): number | null {
1014 const index = array.findIndex(item => {
1015 return item._id === id;
1017 return index >= 0 ? index : null;
1018 }
1020 > public getEnvironmentVariableCollection(extension: IExtensionDescription): IEnvironmentVariableCollection {
1021 let collection = this._environmentVariableCollections.get(extension.identifier.value);
1022 if (!collection) {
1026 return collection.getScopedEnvironmentVariableCollection(undefined);
1027 }
1029 > private _syncEnvironmentVariableCollection(extensionIdentifier: string, collection: UnifiedEnvironmentVariableCollection): void {
1030 const serialized = serializeEnvironmentVariableCollection(collection.map);
1031 const serializedDescription = serializeEnvironmentDescriptionMap(collection.descriptionMap);
1032 this._proxy.$setEnvironmentVariableCollection(extensionIdentifier, collection.persistent, serialized.length === 0 ? undefined : serialized, serializedDescription);
1033 }
1035 > public $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void {
1036 collections.forEach(entry => {
1037 const extensionIdentifier = entry[0];
1040 });
1041 }
1043 > public $acceptDefaultProfile(profile: ITerminalProfile, automationProfile: ITerminalProfile): void {
1044 const oldProfile = this._defaultProfile;
1045 this._defaultProfile = profile;
1049 }
1050 }
1052 > private _setEnvironmentVariableCollection(extensionIdentifier: string, collection: UnifiedEnvironmentVariableCollection): void {
1053 this._environmentVariableCollections.set(extensionIdentifier, collection);
1054 this._register(collection.onDidChangeCollection(() => {
1060 }));
1061 }
1063 >
1064 > /**
1065 > * Unified environment variable collection carrying information for all scopes, for a specific extension.
1066 > */
1067 > class UnifiedEnvironmentVariableCollection extends Disposable {
1068 > readonly map: Map<string, IEnvironmentVariableMutator> = new Map();
1069 > private readonly scopedCollections: Map<string, ScopedEnvironmentVariableCollection> = new Map();
1070 > readonly descriptionMap: Map<string, IEnvironmentVariableCollectionDescription> = new Map();
1071 > private _persistent: boolean = true;
1072 >
1073 > public get persistent(): boolean { return this._persistent; }
1074 > public set persistent(value: boolean) {
1075 this._persistent = value;
1076 this._onDidChangeCollection.fire();
1077 }
1079 > protected readonly _onDidChangeCollection: Emitter<void> = this._register(new Emitter<void>());
1080 > get onDidChangeCollection(): Event<void> { return this._onDidChangeCollection && this._onDidChangeCollection.event; }
1081 >
1082 > constructor(
1083 serialized?: ISerializableEnvironmentVariableCollection
1084 ) {
1086 this.map = new Map(serialized);
1087 }
1089 > getScopedEnvironmentVariableCollection(scope: vscode.EnvironmentVariableScope | undefined): IEnvironmentVariableCollection {
1090 const scopedCollectionKey = this.getScopeKey(scope);
1091 let scopedCollection = this.scopedCollections.get(scopedCollectionKey);
1097 return scopedCollection;
1098 }
1100 > replace(variable: string, value: string, options: vscode.EnvironmentVariableMutatorOptions | undefined, scope: vscode.EnvironmentVariableScope | undefined): void {
1101 this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace, options: options ?? { applyAtProcessCreation: true }, scope });
1102 }
1104 > append(variable: string, value: string, options: vscode.EnvironmentVariableMutatorOptions | undefined, scope: vscode.EnvironmentVariableScope | undefined): void {
1105 this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append, options: options ?? { applyAtProcessCreation: true }, scope });
1106 }
1108 > prepend(variable: string, value: string, options: vscode.EnvironmentVariableMutatorOptions | undefined, scope: vscode.EnvironmentVariableScope | undefined): void {
1109 this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend, options: options ?? { applyAtProcessCreation: true }, scope });
1110 }
1112 > private _setIfDiffers(variable: string, mutator: vscode.EnvironmentVariableMutator & { scope: vscode.EnvironmentVariableScope | undefined }): void {
1113 if (mutator.options && mutator.options.applyAtProcessCreation === false && !mutator.options.applyAtShellIntegration) {
1114 throw new Error('EnvironmentVariableMutatorOptions must apply at either process creation or shell integration');
1140 }
1141 }
1143 > get(variable: string, scope: vscode.EnvironmentVariableScope | undefined): vscode.EnvironmentVariableMutator | undefined {
1144 const key = this.getKey(variable, scope);
1145 const value = this.map.get(key);
1147 return value ? convertMutator(value) : undefined;
1148 }
1150 > private getKey(variable: string, scope: vscode.EnvironmentVariableScope | undefined) {
1151 const scopeKey = this.getScopeKey(scope);
1152 return scopeKey.length ? `${variable}:::${scopeKey}` : variable;
1153 }
1155 > private getScopeKey(scope: vscode.EnvironmentVariableScope | undefined): string {
1156 return this.getWorkspaceKey(scope?.workspaceFolder) ?? '';
1157 }
1159 > private getWorkspaceKey(workspaceFolder: vscode.WorkspaceFolder | undefined): string | undefined {
1160 return workspaceFolder ? workspaceFolder.uri.toString() : undefined;
1161 }
1163 > public getVariableMap(scope: vscode.EnvironmentVariableScope | undefined): Map<string, vscode.EnvironmentVariableMutator> {
1164 const map = new Map<string, vscode.EnvironmentVariableMutator>();
1165 for (const [_, value] of this.map) {
1170 return map;
1171 }
1173 > delete(variable: string, scope: vscode.EnvironmentVariableScope | undefined): void {
1174 const key = this.getKey(variable, scope);
1175 this.map.delete(key);
1176 this._onDidChangeCollection.fire();
1177 }
1179 > clear(scope: vscode.EnvironmentVariableScope | undefined): void {
1180 if (scope?.workspaceFolder) {
1181 for (const [key, mutator] of this.map) {
1191 this._onDidChangeCollection.fire();
1192 }
1194 > setDescription(description: string | vscode.MarkdownString | undefined, scope: vscode.EnvironmentVariableScope | undefined): void {
1195 const key = this.getScopeKey(scope);
1196 const current = this.descriptionMap.get(key);
1208 }
1209 }
1211 > public getDescription(scope: vscode.EnvironmentVariableScope | undefined): string | vscode.MarkdownString | undefined {
1212 const key = this.getScopeKey(scope);
1213 return this.descriptionMap.get(key)?.description;
1214 }
1216 > private clearDescription(scope: vscode.EnvironmentVariableScope | undefined): void {
1217 const key = this.getScopeKey(scope);
1218 this.descriptionMap.delete(key);
1219 }
1221 >
1222 > class ScopedEnvironmentVariableCollection implements IEnvironmentVariableCollection {
1223 > public get persistent(): boolean { return this.collection.persistent; }
1224 > public set persistent(value: boolean) {
1225 this.collection.persistent = value;
1226 }
1228 > protected readonly _onDidChangeCollection = new Emitter<void>();
1229 > get onDidChangeCollection(): Event<void> { return this._onDidChangeCollection && this._onDidChangeCollection.event; }
1230 >
1231 > constructor(
1232 private readonly collection: UnifiedEnvironmentVariableCollection,
1233 private readonly scope: vscode.EnvironmentVariableScope | undefined
1234 ) {
1235 }
1237 > getScoped(scope: vscode.EnvironmentVariableScope | undefined) {
1238 return this.collection.getScopedEnvironmentVariableCollection(scope);
1239 }
1241 > replace(variable: string, value: string, options?: vscode.EnvironmentVariableMutatorOptions | undefined): void {
1242 this.collection.replace(variable, value, options, this.scope);
1243 }
1245 > append(variable: string, value: string, options?: vscode.EnvironmentVariableMutatorOptions | undefined): void {
1246 this.collection.append(variable, value, options, this.scope);
1247 }
1249 > prepend(variable: string, value: string, options?: vscode.EnvironmentVariableMutatorOptions | undefined): void {
1250 this.collection.prepend(variable, value, options, this.scope);
1251 }
1253 > get(variable: string): vscode.EnvironmentVariableMutator | undefined {
1254 return this.collection.get(variable, this.scope);
1255 }
1257 > forEach(callback: (variable: string, mutator: vscode.EnvironmentVariableMutator, collection: vscode.EnvironmentVariableCollection) => unknown, thisArg?: unknown): void {
1258 this.collection.getVariableMap(this.scope).forEach((value, variable) => callback.call(thisArg, variable, value, this), this.scope);
1259 }
1261 > [Symbol.iterator](): IterableIterator<[variable: string, mutator: vscode.EnvironmentVariableMutator]> {
1262 return this.collection.getVariableMap(this.scope).entries();
1263 }
1265 > delete(variable: string): void {
1266 this.collection.delete(variable, this.scope);
1267 this._onDidChangeCollection.fire(undefined);
1268 }
1270 > clear(): void {
1271 this.collection.clear(this.scope);
1272 }
1274 > set description(description: string | vscode.MarkdownString | undefined) {
1275 this.collection.setDescription(description, this.scope);
1276 }
1278 > get description(): string | vscode.MarkdownString | undefined {
1279 return this.collection.getDescription(this.scope);
1280 }
1282 >
1283 > export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
1284 >
1285 > private readonly _hasRemoteAuthority: boolean;
1286 >
1287 > constructor(
1288 @IExtHostCommands extHostCommands: IExtHostCommands,
1289 @IExtHostRpcService extHostRpc: IExtHostRpcService,
1293 this._hasRemoteAuthority = !!initData.remote.authority;
1294 }
1296 > public createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
1297 if (!this._hasRemoteAuthority) {
1298 throw new NotSupportedError();
1300 return this.createTerminalFromOptions({ name, shellPath, shellArgs });
1301 }
1303 > public createTerminalFromOptions(options: vscode.TerminalOptions, internalOptions?: ITerminalInternalOptions): vscode.Terminal {
1304 if (!this._hasRemoteAuthority) {
1305 throw new NotSupportedError();
1310 return terminal.value;
1311 }
1313 >
1314 function asTerminalIcon(iconPath?: vscode.Uri | { light: vscode.Uri; dark: vscode.Uri } | vscode.ThemeIcon): TerminalIcon | undefined {
1315 if (!iconPath || typeof iconPath === 'string') {
1326 };
1327 }
1329 function asTerminalColor(color?: vscode.ThemeColor): ThemeColor | undefined {
1330 return ThemeColor.isThemeColor(color) ? color as ThemeColor : undefined;
1331 }
1333 function convertMutator(mutator: IEnvironmentVariableMutator): vscode.EnvironmentVariableMutator {
1334 const newMutator = { ...mutator };
src/vs/workbench/api/common/extHostCommands.ts 152 introduced LOC · 21 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostCommands.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { validateConstraint } from '../../../base/common/types.js';
7 > import { ICommandMetadata } from '../../../platform/commands/common/commands.js';
8 > import * as extHostTypes from './extHostTypes.js';
9 > import * as extHostTypeConverter from './extHostTypeConverters.js';
10 > import { cloneAndChange } from '../../../base/common/objects.js';
11 > import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ICommandDto, ICommandMetadataDto, MainThreadTelemetryShape } from './extHost.protocol.js';
12 > import { isNonEmptyArray } from '../../../base/common/arrays.js';
13 > import * as languages from '../../../editor/common/languages.js';
14 > import type * as vscode from 'vscode';
15 > import { ILogService } from '../../../platform/log/common/log.js';
16 > import { revive } from '../../../base/common/marshalling.js';
17 > import { IRange, Range } from '../../../editor/common/core/range.js';
18 > import { IPosition, Position } from '../../../editor/common/core/position.js';
19 > import { URI } from '../../../base/common/uri.js';
20 > import { DisposableStore, toDisposable } from '../../../base/common/lifecycle.js';
21 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
22 > import { IExtHostRpcService } from './extHostRpcService.js';
23 > import { ISelection } from '../../../editor/common/core/selection.js';
24 > import { TestItemImpl } from './extHostTestItem.js';
25 > import { VSBuffer } from '../../../base/common/buffer.js';
26 > import { SerializableObjectWithBuffers } from '../../services/extensions/common/proxyIdentifier.js';
27 > import { toErrorMessage } from '../../../base/common/errorMessage.js';
28 > import { StopWatch } from '../../../base/common/stopwatch.js';
29 > import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
30 > import { TelemetryTrustedValue } from '../../../platform/telemetry/common/telemetryUtils.js';
31 > import { IExtHostTelemetry } from './extHostTelemetry.js';
32 > import { generateUuid } from '../../../base/common/uuid.js';
33 > import { isCancellationError } from '../../../base/common/errors.js';
34 >
35 > interface CommandHandler {
36 > callback: Function;
37 > thisArg: any;
38 > metadata?: ICommandMetadata;
39 > extension?: IExtensionDescription;
40 > }
41 >
42 > export interface ArgumentProcessor {
43 > processArgument(arg: any, extension: IExtensionDescription | undefined): any;
44 > }
45 >
46 > export class ExtHostCommands implements ExtHostCommandsShape {
47 >
48 > readonly _serviceBrand: undefined;
49 >
50 > #proxy: MainThreadCommandsShape;
51 >
52 > private readonly _commands = new Map<string, CommandHandler>();
53 > private readonly _apiCommands = new Map<string, ApiCommand>();
54 > #telemetry: MainThreadTelemetryShape;
55 >
56 > private readonly _logService: ILogService;
57 > readonly #extHostTelemetry: IExtHostTelemetry;
58 > private readonly _argumentProcessors: ArgumentProcessor[];
59 >
60 > readonly converter: CommandsConverter;
61 >
62 > constructor(
63 @IExtHostRpcService extHostRpc: IExtHostRpcService,
64 @ILogService logService: ILogService,
112 ];
113 }
115 > registerArgumentProcessor(processor: ArgumentProcessor): void {
116 this._argumentProcessors.push(processor);
117 }
119 > registerApiCommand(apiCommand: ApiCommand): extHostTypes.Disposable {
120
121
144 });
145 }
147 > registerCommand(global: boolean, id: string, callback: <T>(...args: any[]) => T | Thenable<T>, thisArg?: any, metadata?: ICommandMetadata, extension?: IExtensionDescription): extHostTypes.Disposable {
148 this._logService.trace('ExtHostCommands#registerCommand', id);
149
169 });
170 }
172 > executeCommand<T>(id: string, ...args: unknown[]): Promise<T> {
173 this._logService.trace('ExtHostCommands#executeCommand', id);
174 return this._doExecuteCommand(id, args, true);
175 }
177 > private async _doExecuteCommand<T>(id: string, args: unknown[], retry: boolean): Promise<T> {
178
179 if (this._commands.has(id)) {
227 }
228 }
230 > private async _executeContributedCommand<T = unknown>(id: string, args: unknown[], annotateError: boolean): Promise<T> {
231 const command = this._commands.get(id);
232 if (!command) {
281 }
282 }
284 > private _reportTelemetry(command: CommandHandler, id: string, duration: number) {
285 if (!command.extension) {
286 return;
308 });
309 }
311 > $executeContributedCommand(id: string, ...args: unknown[]): Promise<unknown> {
312 this._logService.trace('ExtHostCommands#$executeContributedCommand', id);
313
320 }
321 }
323 > getCommands(filterUnderscoreCommands: boolean = false): Promise<string[]> {
324 this._logService.trace('ExtHostCommands#getCommands', filterUnderscoreCommands);
325
331 });
332 }
334 > $getContributedCommandMetadata(): Promise<{ [id: string]: string | ICommandMetadataDto }> {
335 const result: { [id: string]: string | ICommandMetadata } = Object.create(null);
336 for (const [id, command] of this._commands) {
342 return Promise.resolve(result);
343 }
345 >
346 > export interface IExtHostCommands extends ExtHostCommands { }
347 > export const IExtHostCommands = createDecorator<IExtHostCommands>('IExtHostCommands');
348 >
349 > export class CommandsConverter implements extHostTypeConverter.Command.ICommandsConverter {
350 >
351 > readonly delegatingCommandId: string = `__vsc${generateUuid()}`;
352 > private readonly _cache = new Map<string, vscode.Command>();
353 > private _cachIdPool = 0;
354 >
355 > // --- conversion between internal and api commands
356 > constructor(
357 private readonly _commands: ExtHostCommands,
358 private readonly _lookupApiCommand: (id: string) => ApiCommand | undefined,
361 this._commands.registerCommand(true, this.delegatingCommandId, this._executeConvertedCommand, this);
362 }
364 > toInternal(command: vscode.Command, disposables: DisposableStore): ICommandDto;
365 > toInternal(command: vscode.Command | undefined, disposables: DisposableStore): ICommandDto | undefined;
366 > toInternal(command: vscode.Command | undefined, disposables: DisposableStore): ICommandDto | undefined {
367
368 if (!command) {
410 return result;
411 }
413 > fromInternal(command: ICommandDto): vscode.Command | undefined {
414
415 if (typeof command.$ident === 'string') {
424 }
425 }
427 >
428 > getActualCommand(...args: unknown[]): vscode.Command | undefined {
429 return this._cache.get(args[0] as string);
430 }
432 > private _executeConvertedCommand<R>(...args: unknown[]): Promise<R> {
433 const actualCmd = this.getActualCommand(...args);
434 this._logService.trace('CommandsConverter#EXECUTE', args[0], actualCmd ? actualCmd.command : 'MISSING');
439 return this._commands.executeCommand(actualCmd.command, ...(actualCmd.arguments || []));
440 }
442 > }
443 >
444 >
445 > export class ApiCommandArgument<V, O = V> {
446 >
447 > static readonly Uri = new ApiCommandArgument<URI>('uri', 'Uri of a text document', v => URI.isUri(v), v => v);
448 > static readonly Position = new ApiCommandArgument<extHostTypes.Position, IPosition>('position', 'A position in a text document', v => extHostTypes.Position.isPosition(v), extHostTypeConverter.Position.from);
449 > static readonly Range = new ApiCommandArgument<extHostTypes.Range, IRange>('range', 'A range in a text document', v => extHostTypes.Range.isRange(v), extHostTypeConverter.Range.from);
450 > static readonly Selection = new ApiCommandArgument<extHostTypes.Selection, ISelection>('selection', 'A selection in a text document', v => extHostTypes.Selection.isSelection(v), extHostTypeConverter.Selection.from);
451 > static readonly Number = new ApiCommandArgument<number>('number', '', v => typeof v === 'number', v => v);
452 > static readonly String = new ApiCommandArgument<string>('string', '', v => typeof v === 'string', v => v);
453 >
454 > static Arr<T, K = T>(element: ApiCommandArgument<T, K>) {
455 return new ApiCommandArgument(
456 `${element.name}_array`,
460 );
461 }
463 > static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof extHostTypes.CallHierarchyItem, extHostTypeConverter.CallHierarchyItem.from);
464 > static readonly TypeHierarchyItem = new ApiCommandArgument('item', 'A type hierarchy item', v => v instanceof extHostTypes.TypeHierarchyItem, extHostTypeConverter.TypeHierarchyItem.from);
465 > static readonly TestItem = new ApiCommandArgument('testItem', 'A VS Code TestItem', v => v instanceof TestItemImpl, extHostTypeConverter.TestItem.from);
466 > static readonly TestProfile = new ApiCommandArgument('testProfile', 'A VS Code test profile', v => v instanceof extHostTypes.TestRunProfileBase, extHostTypeConverter.TestRunProfile.from);
467 >
468 > constructor(
469 > readonly name: string,
470 > readonly description: string,
471 > readonly validate: (v: V) => boolean,
472 > readonly convert: (v: V) => O
473 > ) { }
474 >
475 > optional(): ApiCommandArgument<V | undefined | null, O | undefined | null> {
476 return new ApiCommandArgument(
477 this.name, `(optional) ${this.description}`,
480 );
481 }
483 > with(name: string | undefined, description: string | undefined): ApiCommandArgument<V, O> {
484 return new ApiCommandArgument(name ?? this.name, description ?? this.description, this.validate, this.convert);
485 }
487 >
488 > export class ApiCommandResult<V, O = V> {
489 >
490 > static readonly Void = new ApiCommandResult<void, void>('no result', v => v);
491 >
492 > constructor(
493 > readonly description: string,
494 > readonly convert: (v: V, apiArgs: any[], cmdConverter: CommandsConverter) => O
495 > ) { }
496 > }
497 >
498 > export class ApiCommand {
499 >
500 > constructor(
501 readonly id: string,
502 readonly internalId: string,
src/vs/workbench/services/extensions/common/extensionHostProtocol.ts 142 introduced LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extensionHostProtocol.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { VSBuffer } from '../../../../base/common/buffer.js';
7 > import { URI, UriComponents, UriDto } from '../../../../base/common/uri.js';
8 > import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
9 > import { ILoggerResource, LogLevel } from '../../../../platform/log/common/log.js';
10 > import { IRemoteConnectionData } from '../../../../platform/remote/common/remoteAuthorityResolver.js';
11 >
12 > export interface IExtensionDescriptionSnapshot {
13 > readonly versionId: number;
14 > readonly allExtensions: IExtensionDescription[];
15 > readonly activationEvents: { [extensionId: string]: string[] };
16 > readonly myExtensions: ExtensionIdentifier[];
17 > }
18 >
19 > export interface IExtensionDescriptionDelta {
20 > readonly versionId: number;
21 > readonly toRemove: ExtensionIdentifier[];
22 > readonly toAdd: IExtensionDescription[];
23 > readonly addActivationEvents: { [extensionId: string]: string[] };
24 > readonly myToRemove: ExtensionIdentifier[];
25 > readonly myToAdd: ExtensionIdentifier[];
26 > }
27 >
28 > export interface IExtensionHostInitData {
29 > version: string;
30 > quality: string | undefined;
31 > commit?: string;
32 > date?: string;
33 > /**
34 > * When set to `0`, no polling for the parent process still running will happen.
35 > */
36 > parentPid: number | 0;
37 > environment: IEnvironment;
38 > workspace?: IStaticWorkspaceData | null;
39 > extensions: IExtensionDescriptionSnapshot;
40 > nlsBaseUrl?: URI;
41 > telemetryInfo: {
42 > readonly sessionId: string;
43 > readonly machineId: string;
44 > readonly sqmId: string;
45 > readonly devDeviceId: string;
46 > readonly firstSessionDate: string;
47 > readonly msftInternal?: boolean;
48 > };
49 > remoteExtensionTips?: { readonly [remoteName: string]: unknown };
50 > virtualWorkspaceExtensionTips?: { readonly [remoteName: string]: unknown };
51 > logLevel: LogLevel;
52 > loggers: UriDto<ILoggerResource>[];
53 > logsLocation: URI;
54 > autoStart: boolean;
55 > remote: { isRemote: boolean; authority: string | undefined; connectionData: IRemoteConnectionData | null };
56 > consoleForward: { includeStack: boolean; logNative: boolean };
57 > uiKind: UIKind;
58 > messagePorts?: ReadonlyMap<string, MessagePortLike>;
59 > handle?: string;
60 > /**
61 > * The value of the `extensionEnabledApiProposalsFallback`-experiment: a comma-separated list of
62 > * `publisher.extension:proposalName` entries that are granted proposed API access even when the
63 > * extension has not declared the proposal. Only set on `stable` builds.
64 > */
65 > enabledApiProposalsFallback?: string;
66 > }
67 >
68 > export interface IEnvironment {
69 > isExtensionDevelopmentDebug: boolean;
70 > appName: string;
71 > appHost: string;
72 > appRoot?: URI;
73 > appLanguage: string;
74 > isExtensionTelemetryLoggingOnly: boolean;
75 > appUriScheme: string;
76 > isPortable?: boolean;
77 > extensionDevelopmentLocationURI?: URI[];
78 > extensionTestsLocationURI?: URI;
79 > globalStorageHome: URI;
80 > workspaceStorageHome: URI;
81 > useHostProxy?: boolean;
82 > skipWorkspaceStorageLock?: boolean;
83 > extensionLogLevel?: [string, LogLevel][];
84 > isSessionsWindow?: boolean;
85 > }
86 >
87 > export interface IStaticWorkspaceData {
88 > id: string;
89 > name: string;
90 > transient?: boolean;
91 > configuration?: UriComponents | null;
92 > isUntitled?: boolean | null;
93 > }
94 >
95 > export interface MessagePortLike {
96 > postMessage(message: unknown, transfer?: Transferable[]): void;
97 > addEventListener(type: 'message', listener: (e: MessageEvent<unknown>) => unknown): void;
98 > removeEventListener(type: 'message', listener: (e: MessageEvent<unknown>) => unknown): void;
99 > start(): void;
100 > }
101 >
102 > export enum UIKind {
103 > Desktop = 1,
104 > Web = 2
105 > }
106 >
107 > export const enum ExtensionHostExitCode {
108 > // nodejs uses codes 1-13 and exit codes >128 are signal exits
109 > VersionMismatch = 55,
110 > UnexpectedError = 81,
111 > }
112 >
113 > export interface IExtHostReadyMessage {
114 > type: 'VSCODE_EXTHOST_IPC_READY';
115 > }
116 >
117 > export interface IExtHostSocketMessage {
118 > type: 'VSCODE_EXTHOST_IPC_SOCKET';
119 > initialDataChunk: string;
120 > skipWebSocketFrames: boolean;
121 > permessageDeflate: boolean;
122 > inflateBytes: string;
123 > }
124 >
125 > export interface IExtHostReduceGraceTimeMessage {
126 > type: 'VSCODE_EXTHOST_IPC_REDUCE_GRACE_TIME';
127 > }
128 >
129 > export const enum MessageType {
130 > Initialized,
131 > Ready,
132 > Terminate
133 > }
134 >
135 > export function createMessageOfType(type: MessageType): VSBuffer {
136 const result = VSBuffer.alloc(1);
137
144 return result;
145 }
147 > export function isMessageOfType(message: VSBuffer, type: MessageType): boolean {
148 if (message.byteLength !== 1) {
149 return false;
157 }
158 }
160 > export const enum NativeLogMarkers {
161 > Start = 'START_NATIVE_LOG',
162 > End = 'END_NATIVE_LOG',
163 > }
src/vs/workbench/api/common/extHostTelemetry.ts 107 introduced LOC · 19 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostTelemetry.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type * as vscode from 'vscode';
7 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
8 > import { Event, Emitter } from '../../../base/common/event.js';
9 > import { ExtHostTelemetryShape } from './extHost.protocol.js';
10 > import { ICommonProperties, TelemetryLevel } from '../../../platform/telemetry/common/telemetry.js';
11 > import { ILogger, ILoggerService } from '../../../platform/log/common/log.js';
12 > import { IExtHostInitDataService } from './extHostInitDataService.js';
13 > import { ExtensionIdentifier, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
14 > import { UIKind } from '../../services/extensions/common/extensionHostProtocol.js';
15 > import { cleanData, cleanRemoteAuthority, TelemetryLogGroup } from '../../../platform/telemetry/common/telemetryUtils.js';
16 > import { mixin } from '../../../base/common/objects.js';
17 > import { Disposable } from '../../../base/common/lifecycle.js';
18 > import { localize } from '../../../nls.js';
19 >
20 > type ExtHostTelemetryEventData = Record<string, any> & {
21 > properties?: Record<string, any>;
22 > measurements?: Record<string, number>;
23 > };
24 >
25 > export class ExtHostTelemetry extends Disposable implements ExtHostTelemetryShape {
26 >
27 > readonly _serviceBrand: undefined;
28 >
29 > private readonly _onDidChangeTelemetryEnabled = this._register(new Emitter<boolean>());
30 > readonly onDidChangeTelemetryEnabled: Event<boolean> = this._onDidChangeTelemetryEnabled.event;
31 >
32 > private readonly _onDidChangeTelemetryConfiguration = this._register(new Emitter<vscode.TelemetryConfiguration>());
33 > readonly onDidChangeTelemetryConfiguration: Event<vscode.TelemetryConfiguration> = this._onDidChangeTelemetryConfiguration.event;
34 >
35 > private _productConfig: { usage: boolean; error: boolean } = { usage: true, error: true };
36 > private _level: TelemetryLevel = TelemetryLevel.NONE;
37 > private _oldTelemetryEnablement: boolean | undefined;
38 > private readonly _inLoggingOnlyMode: boolean = false;
39 > private readonly _outputLogger: ILogger;
40 > private readonly _telemetryLoggers = new Map<string, ExtHostTelemetryLogger[]>();
41 >
42 > constructor(
43 isWorker: boolean,
44 @IExtHostInitDataService private readonly initData: IExtHostInitDataService,
55 }));
56 }
58 > getTelemetryConfiguration(): boolean {
59 return this._level === TelemetryLevel.USAGE;
60 }
62 > getTelemetryDetails(): vscode.TelemetryConfiguration {
63 return {
64 isCrashEnabled: this._level >= TelemetryLevel.CRASH,
67 };
68 }
70 > instantiateLogger(extension: IExtensionDescription, sender: vscode.TelemetrySender, options?: vscode.TelemetryLoggerOptions) {
71 const telemetryDetails = this.getTelemetryDetails();
72 const logger = new ExtHostTelemetryLogger(
83 return logger.apiTelemetryLogger;
84 }
86 > $initializeTelemetryLevel(level: TelemetryLevel, supportsTelemetry: boolean, productConfig?: { usage: boolean; error: boolean }): void {
87 this._level = level;
88 this._productConfig = productConfig ?? { usage: true, error: true };
89 }
91 > getBuiltInCommonProperties(extension: IExtensionDescription): ICommonProperties {
92 const commonProperties: ICommonProperties = Object.create(null);
93 // TODO @lramos15, does os info like node arch, platform version, etc exist here.
125 return commonProperties;
126 }
128 > $onDidChangeTelemetryLevel(level: TelemetryLevel): void {
129 this._oldTelemetryEnablement = this.getTelemetryConfiguration();
130 this._level = level;
151 this._onDidChangeTelemetryConfiguration.fire(this.getTelemetryDetails());
152 }
154 > onExtensionError(extension: ExtensionIdentifier, error: Error): boolean {
155 const loggers = this._telemetryLoggers.get(extension.value);
156 const nonDisposedLoggers = loggers?.filter(l => !l.isDisposed);
169 return errorEmitted;
170 }
172 >
173 > export class ExtHostTelemetryLogger {
174 >
175 > static validateSender(sender: vscode.TelemetrySender): void {
176 > if (typeof sender !== 'object') {
177 > throw new TypeError('TelemetrySender argument is invalid');
178 > }
179 > if (typeof sender.sendEventData !== 'function') {
180 > throw new TypeError('TelemetrySender.sendEventData must be a function');
181 > }
182 > if (typeof sender.sendErrorData !== 'function') {
183 > throw new TypeError('TelemetrySender.sendErrorData must be a function');
184 > }
185 > if (typeof sender.flush !== 'undefined' && typeof sender.flush !== 'function') {
186 > throw new TypeError('TelemetrySender.flush must be a function or undefined');
187 > }
188 > }
189 >
190 > private readonly _onDidChangeEnableStates = new Emitter<vscode.TelemetryLogger>();
191 > private readonly _ignoreBuiltinCommonProperties: boolean;
192 > private readonly _additionalCommonProperties: Record<string, any> | undefined;
193 > public readonly ignoreUnhandledExtHostErrors: boolean;
194 >
195 > private _telemetryEnablements: { isUsageEnabled: boolean; isErrorsEnabled: boolean };
196 > private _apiObject: vscode.TelemetryLogger | undefined;
197 > private _sender: vscode.TelemetrySender | undefined;
198 >
199 > constructor(
200 sender: vscode.TelemetrySender,
201 options: vscode.TelemetryLoggerOptions | undefined,
212 this._telemetryEnablements = { isUsageEnabled: telemetryEnablements.isUsageEnabled, isErrorsEnabled: telemetryEnablements.isErrorsEnabled };
213 }
215 > updateTelemetryEnablements(isUsageEnabled: boolean, isErrorsEnabled: boolean): void {
216 if (this._apiObject) {
217 this._telemetryEnablements = { isUsageEnabled, isErrorsEnabled };
219 }
220 }
222 > mixInCommonPropsAndCleanData(data: ExtHostTelemetryEventData): Record<string, any> {
223 // Some telemetry modules prefer to break properties and measurmements up
224 // We mix common properties into the properties tab.
244 return data;
245 }
247 > private logEvent(eventName: string, data?: Record<string, any>): void {
248 // No sender means likely disposed of, we should no-op
249 if (!this._sender) {
262 this._logger.trace(eventName, data);
263 }
265 > logUsage(eventName: string, data?: Record<string, any>): void {
266 if (!this._telemetryEnablements.isUsageEnabled) {
267 return;
269 this.logEvent(eventName, data);
270 }
272 > logError(eventNameOrException: Error | string, data?: Record<string, any>): void {
273 if (!this._telemetryEnablements.isErrorsEnabled || !this._sender) {
274 return;
297 }
298 }
300 > get apiTelemetryLogger(): vscode.TelemetryLogger {
301 if (!this._apiObject) {
302 const that = this;
317 return this._apiObject;
318 }
320 > get isDisposed(): boolean {
321 return !this._sender;
322 }
324 > dispose(): void {
325 if (this._sender?.flush) {
326 let tempSender: vscode.TelemetrySender | undefined = this._sender;
333 this._onDidChangeEnableStates.dispose();
334 }
336 >
337 > export function isNewAppInstall(firstSessionDate: string): boolean {
338 const installAge = Date.now() - new Date(firstSessionDate).getTime();
339 return isNaN(installAge) ? false : installAge < 1000 * 60 * 60 * 24; // install age is less than a day
340 }
342 > export const IExtHostTelemetry = createDecorator<IExtHostTelemetry>('IExtHostTelemetry');
343 > export interface IExtHostTelemetry extends ExtHostTelemetry, ExtHostTelemetryShape { }
src/vs/workbench/api/common/extHostTestItem.ts 70 introduced LOC · 11 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostTestItem.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type * as vscode from 'vscode';
7 > import { URI } from '../../../base/common/uri.js';
8 > import * as editorRange from '../../../editor/common/core/range.js';
9 > import { TestId, TestIdPathParts } from '../../contrib/testing/common/testId.js';
10 > import { createTestItemChildren, ExtHostTestItemEvent, ITestChildrenLike, ITestItemApi, ITestItemChildren, TestItemCollection, TestItemEventOp } from '../../contrib/testing/common/testItemCollection.js';
11 > import { denamespaceTestTag, ITestItem, ITestItemContext } from '../../contrib/testing/common/testTypes.js';
12 > import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors.js';
13 > import { createPrivateApiFor, getPrivateApiFor, IExtHostTestItemApi } from './extHostTestingPrivateApi.js';
14 > import * as Convert from './extHostTypeConverters.js';
15 >
16 > const testItemPropAccessor = <K extends keyof vscode.TestItem>(
17 api: IExtHostTestItemApi,
18 defaultValue: vscode.TestItem[K],
36 };
37 };
39 > type WritableProps = Pick<vscode.TestItem, 'range' | 'label' | 'description' | 'sortText' | 'canResolveChildren' | 'busy' | 'error' | 'tags'>;
40 >
41 > const strictEqualComparator = <T>(a: T, b: T) => a === b;
42 >
43 > const propComparators: { [K in keyof Required<WritableProps>]: (a: vscode.TestItem[K], b: vscode.TestItem[K]) => boolean } = {
44 > range: (a, b) => {
45 if (a === b) { return true; }
46 if (!a || !b) { return false; }
47 return a.isEqual(b);
48 },
49 > label: strictEqualComparator, extHostTestItem.ts
50 > description: strictEqualComparator,
51 > sortText: strictEqualComparator,
52 > busy: strictEqualComparator,
53 > error: strictEqualComparator,
54 > canResolveChildren: strictEqualComparator,
55 > tags: (a, b) => {
56 if (a.length !== b.length) {
57 return false;
64 return true;
65 },
67 >
68 > const evSetProps = <T>(fn: (newValue: T) => Partial<ITestItem>): (newValue: T) => ExtHostTestItemEvent =>
69 v => ({ op: TestItemEventOp.SetProp, update: fn(v) });
71 > const makePropDescriptors = (api: IExtHostTestItemApi, label: string): { [K in keyof Required<WritableProps>]: PropertyDescriptor } => ({
72 range: (() => {
73 let value: vscode.Range | undefined;
103 })),
104 });
106 > const toItemFromPlain = (item: ITestItem.Serialized): TestItemImpl => {
107 const testId = TestId.fromString(item.extId);
108 const testItem = new TestItemImpl(testId.controllerId, testId.localId, item.label, URI.revive(item.uri) || undefined);
113 return testItem;
114 };
116 > export const toItemFromContext = (context: ITestItemContext): TestItemImpl => {
117 let node: TestItemImpl | undefined;
118 for (const test of context.tests) {
124 return node!;
125 };
127 > export class TestItemImpl implements vscode.TestItem {
128 > public readonly id!: string;
129 > public readonly uri!: vscode.Uri | undefined;
130 > public readonly children!: ITestItemChildren<vscode.TestItem>;
131 > public readonly parent!: TestItemImpl | undefined;
132 >
133 > public range!: vscode.Range | undefined;
134 > public description!: string | undefined;
135 > public sortText!: string | undefined;
136 > public label!: string;
137 > public error!: string | vscode.MarkdownString;
138 > public busy!: boolean;
139 > public canResolveChildren!: boolean;
140 > public tags!: readonly vscode.TestTag[];
141 >
142 > /**
143 > * Note that data is deprecated and here for back-compat only
144 > */
145 > constructor(controllerId: string, id: string, label: string, uri: vscode.Uri | undefined) {
146 if (id.includes(TestIdPathParts.Delimiter)) {
147 throw new Error(`Test IDs may not include the ${JSON.stringify(id)} symbol`);
174 });
175 }
177 >
178 > export class TestItemRootImpl extends TestItemImpl {
179 > public readonly _isRoot = true;
180 >
181 > constructor(controllerId: string, label: string) {
182 super(controllerId, controllerId, label, undefined);
183 }
185 >
186 > export class ExtHostTestItemCollection extends TestItemCollection<TestItemImpl> {
187 > constructor(controllerId: string, controllerLabel: string, editors: ExtHostDocumentsAndEditors) {
188 super({
189 controllerId,