Merge branch 'main' into tyriar/274723_platform_terminal__api

This commit is contained in:
Daniel Imms
2025-11-08 09:42:14 -08:00
committed by GitHub
31 changed files with 126 additions and 109 deletions

View File

@@ -338,23 +338,8 @@ export default tseslint.config(
'src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts',
'src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts',
'src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts',
'src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts',
'src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts',
'src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts',
'src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts',
'src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/commandLineAutoApprover.ts',
'src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/taskHelpers.ts',
'src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/toolTerminalCreator.ts',
'src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/monitoring/outputMonitor.ts',
'src/vs/workbench/contrib/terminalContrib/commandGuide/browser/terminal.commandGuide.contribution.ts',
'src/vs/workbench/contrib/terminalContrib/history/browser/terminalRunRecentQuickPick.ts',
'src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts',
'src/vs/workbench/contrib/terminalContrib/links/test/browser/linkTestUtils.ts',
'src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts',
'src/vs/workbench/contrib/terminalContrib/sendSequence/browser/terminal.sendSequence.contribution.ts',
'src/vs/workbench/contrib/terminalContrib/sendSignal/browser/terminal.sendSignal.contribution.ts',
'src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts',
'src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts',
'src/vs/workbench/contrib/testing/browser/explorerProjections/listProjection.ts',
'src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts',
'src/vs/workbench/contrib/testing/browser/testCoverageBars.ts',
@@ -493,9 +478,6 @@ export default tseslint.config(
// Platform
'src/vs/platform/accessibility/browser/accessibleView.ts',
'src/vs/platform/accessibility/common/accessibility.ts',
'src/vs/platform/action/common/action.ts',
'src/vs/platform/actions/common/actions.ts',
'src/vs/platform/assignment/common/assignment.ts',
'src/vs/platform/browserElements/electron-main/nativeBrowserElementsMainService.ts',
'src/vs/platform/commands/common/commands.ts',
'src/vs/platform/configuration/common/configuration.ts',
@@ -530,7 +512,6 @@ export default tseslint.config(
'src/vs/platform/extensions/common/extensionValidator.ts',
'src/vs/platform/extensions/common/extensions.ts',
'src/vs/platform/extensions/electron-main/extensionHostStarter.ts',
'src/vs/platform/externalTerminal/node/externalTerminalService.ts',
'src/vs/platform/instantiation/common/descriptors.ts',
'src/vs/platform/instantiation/common/extensions.ts',
'src/vs/platform/instantiation/common/instantiation.ts',
@@ -607,7 +588,6 @@ export default tseslint.config(
'src/vs/platform/userDataSync/common/userDataSyncService.ts',
'src/vs/platform/userDataSync/common/userDataSyncServiceIpc.ts',
'src/vs/platform/userDataSync/common/userDataSyncStoreService.ts',
'src/vs/platform/webContentExtractor/electron-main/cdpAccessibilityDomain.ts',
'src/vs/platform/webview/common/webviewManagerService.ts',
'src/vs/platform/configuration/test/common/testConfigurationService.ts',
'src/vs/platform/instantiation/test/common/instantiationServiceMock.ts',
@@ -722,8 +702,6 @@ export default tseslint.config(
'src/vs/workbench/contrib/bulkEdit/browser/opaqueEdits.ts',
'src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts',
'src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts',
'src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts',
'src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts',
'src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts',
'src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts',
'src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts',
@@ -799,12 +777,10 @@ export default tseslint.config(
'src/vs/workbench/contrib/extensions/common/extensions.ts',
'src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts',
'src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts',
'src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts',
'src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts',
'src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts',
'src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts',
'src/vs/workbench/contrib/issue/browser/issueReporterModel.ts',
'src/vs/workbench/contrib/list/browser/tableColumnResizeQuickPick.ts',
'src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts',
'src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts',
'src/vs/workbench/contrib/markers/browser/markers.contribution.ts',
@@ -917,7 +893,6 @@ export default tseslint.config(
'src/vs/workbench/contrib/testing/common/storedValue.ts',
'src/vs/workbench/contrib/testing/common/testItemCollection.ts',
'src/vs/workbench/contrib/testing/test/browser/testObjectTree.ts',
'src/vs/workbench/contrib/timeline/browser/timelinePane.ts',
'src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution.ts',
'src/vs/workbench/contrib/typeHierarchy/common/typeHierarchy.ts',
'src/vs/workbench/contrib/update/browser/update.ts',

View File

@@ -136,8 +136,8 @@ export class AssignmentFilterProvider implements IExperimentationFilterProvider
}
}
getFilters(): Map<string, any> {
const filters: Map<string, any> = new Map<string, any>();
getFilters(): Map<string, unknown> {
const filters: Map<string, unknown> = new Map<string, unknown>();
const filterValues = Object.values(Filters);
for (const value of filterValues) {
filters.set(value, this.getFilterValue(value));

View File

@@ -89,7 +89,7 @@ export class WindowsExternalTerminalService extends ExternalTerminalService impl
// delete environment variables that have a null value
Object.keys(env).filter(v => env[v] === null).forEach(key => delete env[key]);
const options: any = {
const options = {
cwd: dir,
env: env,
windowsVerbatimArguments: true
@@ -267,7 +267,7 @@ export class LinuxExternalTerminalService extends ExternalTerminalService implem
// delete environment variables that have a null value
Object.keys(env).filter(v => env[v] === null).forEach(key => delete env[key]);
const options: any = {
const options = {
cwd: dir,
env: env
};

View File

@@ -37,13 +37,18 @@ import { hasKey } from '../../../base/common/types.js';
type XtermTerminal = pkg.Terminal;
const { Terminal: XtermTerminal } = pkg;
interface ITraceRpcArgs {
logService: ILogService;
simulatedLatency: number;
}
export function traceRpc(_target: Object, key: string, descriptor: PropertyDescriptor) {
if (typeof descriptor.value !== 'function') {
throw new Error('not supported');
}
const fnKey = 'value';
const fn = descriptor.value;
descriptor[fnKey] = async function <TThis extends { traceRpcArgs: { logService: ILogService; simulatedLatency: number } }>(this: TThis, ...args: unknown[]) {
descriptor[fnKey] = async function <TThis extends { traceRpcArgs: ITraceRpcArgs }>(this: TThis, ...args: unknown[]) {
if (this.traceRpcArgs.logService.getLevel() === LogLevel.Trace) {
this.traceRpcArgs.logService.trace(`[RPC Request] PtyService#${fn.name}(${args.map(e => JSON.stringify(e)).join(', ')})`);
}
@@ -123,7 +128,7 @@ export class PtyService extends Disposable implements IPtyService {
}
@memoize
get traceRpcArgs(): { logService: ILogService; simulatedLatency: number } {
get traceRpcArgs(): ITraceRpcArgs {
return {
logService: this._logService,
simulatedLatency: this._simulatedLatency

View File

@@ -9,7 +9,7 @@ import { URI } from '../../../base/common/uri.js';
export interface AXValue {
type: AXValueType;
value?: any;
value?: unknown;
relatedNodes?: AXNode[];
sources?: AXValueSource[];
}

View File

@@ -65,7 +65,7 @@ class CallHierarchyController implements IEditorContribution {
this._ctxIsVisible = _ctxCallHierarchyVisible.bindTo(this._contextKeyService);
this._ctxHasProvider = _ctxHasCallHierarchyProvider.bindTo(this._contextKeyService);
this._ctxDirection = _ctxCallHierarchyDirection.bindTo(this._contextKeyService);
this._dispoables.add(Event.any<any>(_editor.onDidChangeModel, _editor.onDidChangeModelLanguage, CallHierarchyProviderRegistry.onDidChange)(() => {
this._dispoables.add(Event.any<unknown>(_editor.onDidChangeModel, _editor.onDidChangeModelLanguage, CallHierarchyProviderRegistry.onDidChange)(() => {
this._ctxHasProvider.set(_editor.hasModel() && CallHierarchyProviderRegistry.has(_editor.getModel()));
}));
this._dispoables.add(this._sessionDisposables);
@@ -125,7 +125,7 @@ class CallHierarchyController implements IEditorContribution {
this._ctxIsVisible.set(true);
this._ctxDirection.set(direction);
Event.any<any>(this._editor.onDidChangeModel, this._editor.onDidChangeModelLanguage)(this.endCallHierarchy, this, this._sessionDisposables);
Event.any<unknown>(this._editor.onDidChangeModel, this._editor.onDidChangeModelLanguage)(this.endCallHierarchy, this, this._sessionDisposables);
this._widget = this._instantiationService.createInstance(CallHierarchyTreePeekWidget, this._editor, position, direction);
this._widget.showLoading();
this._sessionDisposables.add(this._widget.onDidClose(() => {

View File

@@ -164,7 +164,7 @@ CommandsRegistry.registerCommand('_executePrepareCallHierarchy', async (accessor
}
});
function isCallHierarchyItemDto(obj: any): obj is CallHierarchyItem {
function isCallHierarchyItemDto(obj: unknown): obj is CallHierarchyItem {
return true;
}

View File

@@ -182,7 +182,7 @@ export class AgentSessionsViewModel extends Disposable implements IAgentSessions
break;
case AgentSessionProviders.Background:
providerLabel = localize('chat.session.providerLabel.background', "Background");
icon = Codicon.layers;
icon = Codicon.serverProcess;
break;
case AgentSessionProviders.Cloud:
providerLabel = localize('chat.session.providerLabel.cloud', "Cloud");

View File

@@ -45,10 +45,12 @@ interface IAgentSessionItemTemplate {
// Column 2 Row 1
readonly title: IconLabel;
// Column 2 Row 2
readonly description: HTMLElement;
readonly diffFiles: HTMLElement;
readonly diffAdded: HTMLElement;
readonly diffRemoved: HTMLElement;
// Column 2 Row 2
readonly description: HTMLElement;
readonly status: HTMLElement;
readonly elementDisposable: DisposableStore;
@@ -86,13 +88,14 @@ export class AgentSessionRenderer implements ICompressibleTreeRenderer<IAgentSes
h('div.agent-session-main-col', [
h('div.agent-session-title-row', [
h('div.agent-session-title@title'),
]),
h('div.agent-session-details-row', [
h('div.agent-session-description@description'),
h('div.agent-session-diff', [
h('span.agent-session-diff-files@diffFiles'),
h('span.agent-session-diff-added@diffAdded'),
h('span.agent-session-diff-removed@diffRemoved')
]),
]),
h('div.agent-session-details-row', [
h('div.agent-session-description@description'),
h('div.agent-session-status@status')
])
])
@@ -106,6 +109,7 @@ export class AgentSessionRenderer implements ICompressibleTreeRenderer<IAgentSes
icon: elements.icon,
title: disposables.add(new IconLabel(elements.title, { supportHighlights: true, supportIcons: true })),
description: elements.description,
diffFiles: elements.diffFiles,
diffAdded: elements.diffAdded,
diffRemoved: elements.diffRemoved,
status: elements.status,
@@ -125,8 +129,9 @@ export class AgentSessionRenderer implements ICompressibleTreeRenderer<IAgentSes
// Diff
const { statistics: diff } = session.element;
template.diffAdded.textContent = diff ? `+${diff.insertions}` : '';
template.diffRemoved.textContent = diff ? `-${diff.deletions}` : '';
template.diffFiles.textContent = diff?.files && diff.files > 0 ? `${diff.files}` : '';
template.diffAdded.textContent = diff?.insertions && diff.insertions > 0 ? `+${diff.insertions}` : '';
template.diffRemoved.textContent = diff?.deletions && diff.deletions > 0 ? `-${diff.deletions}` : '';
// Description
if (typeof session.element.description === 'string') {

View File

@@ -14,7 +14,7 @@
}
.agent-sessions-new-session-container {
padding: 5px 12px;
padding: 6px 12px;
flex: 0 0 auto !important;
}

View File

@@ -10,16 +10,22 @@
}
.monaco-list-row.selected .agent-session-details-row,
.monaco-list-row.selected span.agent-session-diff-files,
.monaco-list-row.selected span.agent-session-diff-added,
.monaco-list-row.selected span.agent-session-diff-removed {
color: unset;
.rendered-markdown {
a {
color: unset;
}
}
}
.agent-session-item {
display: flex;
flex-direction: row;
padding: 0 12px;
gap: 2px;
padding: 0 8px;
.agent-session-main-col,
.agent-session-title-row,
@@ -38,6 +44,10 @@
width: 16px;
height: 16px;
font-size: 16px;
&.codicon-terminal {
font-size: 15px; /* TODO@bpasero remove once we settle on icon */
}
}
}
@@ -64,7 +74,7 @@
align-items: center;
margin: 0;
>span.codicon {
> span.codicon {
margin-right: 2px;
}
}
@@ -77,6 +87,7 @@
.agent-session-title,
.agent-session-description {
flex: 1; /* push other items to the end */
text-overflow: ellipsis;
overflow: hidden;
}
@@ -84,12 +95,15 @@
/* #region Diff Styling */
.agent-session-diff {
flex: 1; /* push status to the end */
font-weight: 700;
font-size: 12px;
display: flex;
gap: 4px;
}
span.agent-session-diff-files {
color: var(--vscode-descriptionForeground);
}
span.agent-session-diff-added {
color: var(--vscode-chat-linesAddedForeground);
}

View File

@@ -332,24 +332,16 @@ class SetupAgent extends Disposable implements IChatAgentImplementation {
try {
const ready = await Promise.race([
timeout(this.environmentService.remoteAuthority ? 60000 /* increase for remote scenarios */ : 20000).then(() => 'timedout'),
this.whenDefaultAgentFailed(chatService).then(() => 'error'),
this.whenDefaultAgentActivated(chatService),
Promise.allSettled([whenLanguageModelReady, whenAgentReady, whenToolsModelReady])
]);
if (ready === 'error' || ready === 'timedout') {
if (ready === 'timedout') {
let warningMessage: string;
if (ready === 'timedout') {
if (this.chatEntitlementService.anonymous) {
warningMessage = localize('chatTookLongWarningAnonymous', "Chat took too long to get ready. Please ensure that the extension `{0}` is installed and enabled.", defaultChat.chatExtensionId);
} else {
warningMessage = localize('chatTookLongWarning', "Chat took too long to get ready. Please ensure you are signed in to {0} and that the extension `{1}` is installed and enabled.", defaultChat.provider.default.name, defaultChat.chatExtensionId);
}
if (this.chatEntitlementService.anonymous) {
warningMessage = localize('chatTookLongWarningAnonymous', "Chat took too long to get ready. Please ensure that the extension `{0}` is installed and enabled.", defaultChat.chatExtensionId);
} else {
if (this.chatEntitlementService.anonymous) {
warningMessage = localize('chatFailedWarningAnonymous', "Chat failed to get ready. Please ensure that the extension `{0}` is installed and enabled.", defaultChat.chatExtensionId);
} else {
warningMessage = localize('chatFailedWarning', "Chat failed to get ready. Please ensure you are signed in to {0} and that the extension `{1}` is installed and enabled.", defaultChat.provider.default.name, defaultChat.chatExtensionId);
}
warningMessage = localize('chatTookLongWarning', "Chat took too long to get ready. Please ensure you are signed in to {0} and that the extension `{1}` is installed and enabled.", defaultChat.provider.default.name, defaultChat.chatExtensionId);
}
this.logService.warn(warningMessage, {
@@ -439,10 +431,12 @@ class SetupAgent extends Disposable implements IChatAgentImplementation {
}));
}
private async whenDefaultAgentFailed(chatService: IChatService): Promise<void> {
return new Promise<void>(resolve => {
chatService.activateDefaultAgent(this.location).catch(() => resolve());
});
private async whenDefaultAgentActivated(chatService: IChatService): Promise<void> {
try {
await chatService.activateDefaultAgent(this.location);
} catch (error) {
this.logService.error(error);
}
}
private async doInvokeWithSetup(request: IChatAgentRequest, progress: (part: IChatProgress) => void, chatService: IChatService, languageModelsService: ILanguageModelsService, chatWidgetService: IChatWidgetService, chatAgentService: IChatAgentService, languageModelToolsService: ILanguageModelToolsService): Promise<IChatAgentResult> {

View File

@@ -334,7 +334,7 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction {
});
}
async run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise<void> {
async run(accessor: ServicesAccessor, editor: ICodeEditor, args: unknown): Promise<void> {
if (!editor.hasModel()) {
return;
}

View File

@@ -15,7 +15,7 @@ interface IColumnResizeQuickPickItem extends IQuickPickItem {
export class TableColumnResizeQuickPick extends Disposable {
constructor(
private readonly _table: Table<any>,
private readonly _table: Table<unknown>,
@IQuickInputService private readonly _quickInputService: IQuickInputService,
) {
super();

View File

@@ -10,6 +10,7 @@ import { IExtensionTerminalProfile, ITerminalCompletionProviderContribution, ITe
import { URI } from '../../../../base/common/uri.js';
import { Emitter, Event } from '../../../../base/common/event.js';
import { isProposedApiEnabled } from '../../../services/extensions/common/extensions.js';
import { isObject } from '../../../../base/common/types.js';
// terminal extension point
const terminalsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<ITerminalContributions>(terminalContributionsDescriptor);
@@ -63,13 +64,16 @@ export class TerminalContributionService implements ITerminalContributionService
}
function hasValidTerminalIcon(profile: ITerminalProfileContribution): boolean {
return !profile.icon ||
(
typeof profile.icon === 'string' ||
URI.isUri(profile.icon) ||
(
(<{ light: URI; dark: URI }>profile.icon).light && URI.isUri(profile.icon.light) &&
(<{ light: URI; dark: URI }>profile.icon).dark && URI.isUri(profile.icon.dark)
)
function isValidDarkLightIcon(obj: unknown): obj is { light: URI; dark: URI } {
return (
isObject(obj) &&
'light' in obj && URI.isUri(obj.light) &&
'dark' in obj && URI.isUri(obj.dark)
);
}
return !profile.icon || (
typeof profile.icon === 'string' ||
URI.isUri(profile.icon) ||
isValidDarkLightIcon(profile.icon)
);
}

View File

@@ -19,7 +19,7 @@ import { ContextKeyExpr, IContextKeyService } from '../../../../../platform/cont
import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js';
import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js';
import { ITerminalCommand, TerminalCapability } from '../../../../../platform/terminal/common/capabilities/capabilities.js';
import { ICurrentPartialCommand } from '../../../../../platform/terminal/common/capabilities/commandDetection/terminalCommand.js';
import { ICurrentPartialCommand, isFullTerminalCommand } from '../../../../../platform/terminal/common/capabilities/commandDetection/terminalCommand.js';
import { TerminalSettingId } from '../../../../../platform/terminal/common/terminal.js';
import { accessibleViewCurrentProviderId, accessibleViewIsShown } from '../../../accessibility/browser/accessibilityConfiguration.js';
import { AccessibilityHelpAction, AccessibleViewAction } from '../../../accessibility/browser/accessibleViewActions.js';
@@ -226,9 +226,9 @@ export class TerminalAccessibleViewContribution extends Disposable implements IT
return;
}
let line: number | undefined;
if ('marker' in command) {
if (isFullTerminalCommand(command)) {
line = command.marker?.line;
} else if ('commandStartMarker' in command) {
} else {
line = command.commandStartMarker?.line;
}
if (line === undefined || line < 0) {

View File

@@ -8,7 +8,7 @@ import { Disposable } from '../../../../../base/common/lifecycle.js';
import { IAccessibleViewContentProvider, AccessibleViewProviderId, IAccessibleViewOptions, AccessibleViewType, IAccessibleViewSymbol } from '../../../../../platform/accessibility/browser/accessibleView.js';
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
import { TerminalCapability, ITerminalCommand } from '../../../../../platform/terminal/common/capabilities/capabilities.js';
import { ICurrentPartialCommand } from '../../../../../platform/terminal/common/capabilities/commandDetection/terminalCommand.js';
import { ICurrentPartialCommand, isFullTerminalCommand } from '../../../../../platform/terminal/common/capabilities/commandDetection/terminalCommand.js';
import { AccessibilityVerbositySettingId } from '../../../accessibility/browser/accessibilityConfiguration.js';
import { ITerminalInstance, ITerminalService } from '../../../terminal/browser/terminal.js';
import { BufferContentTracker } from './bufferContentTracker.js';
@@ -98,9 +98,9 @@ export class TerminalAccessibleBufferProvider extends Disposable implements IAcc
}
private _getEditorLineForCommand(command: ITerminalCommand | ICurrentPartialCommand): number | undefined {
let line: number | undefined;
if ('marker' in command) {
if (isFullTerminalCommand(command)) {
line = command.marker?.line;
} else if ('commandStartMarker' in command) {
} else {
line = command.commandStartMarker?.line;
}
if (line === undefined || line < 0) {

View File

@@ -30,6 +30,7 @@ import { TerminalInstance } from '../../../terminal/browser/terminalInstance.js'
import { TerminalInitialHintSettingId } from '../common/terminalInitialHintConfiguration.js';
import './media/terminalInitialHint.css';
import { TerminalChatCommandId } from './terminalChat.js';
import { hasKey } from '../../../../../base/common/types.js';
const $ = dom.$;
@@ -107,7 +108,7 @@ export class TerminalInitialHintContribution extends Disposable implements ITerm
xtermOpen(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void {
// Don't show is the terminal was launched by an extension or a feature like debug
if ('shellLaunchConfig' in this._ctx.instance && (this._ctx.instance.shellLaunchConfig.isExtensionOwnedTerminal || this._ctx.instance.shellLaunchConfig.isFeatureTerminal)) {
if (hasKey(this._ctx.instance, { shellLaunchConfig: true }) && (this._ctx.instance.shellLaunchConfig.isExtensionOwnedTerminal || this._ctx.instance.shellLaunchConfig.isFeatureTerminal)) {
return;
}
// Don't show if disabled

View File

@@ -54,19 +54,25 @@ registerActiveXtermAction({
}
const contr = TerminalChatController.activeChatController || TerminalChatController.get(activeInstance);
if (!contr) {
return;
}
if (opts) {
function isValidOptionsObject(obj: unknown): obj is { query: string; isPartialQuery?: boolean } {
return typeof obj === 'object' && obj !== null && 'query' in obj && typeof obj.query === 'string';
}
opts = typeof opts === 'string' ? { query: opts } : opts;
if (typeof opts === 'object' && opts !== null && 'query' in opts && typeof opts.query === 'string') {
contr?.updateInput(opts.query, false);
if (!('isPartialQuery' in opts && opts.isPartialQuery)) {
contr?.terminalChatWidget?.acceptInput();
if (isValidOptionsObject(opts)) {
contr.updateInput(opts.query, false);
if (!opts.isPartialQuery) {
contr.terminalChatWidget?.acceptInput();
}
}
}
contr?.terminalChatWidget?.reveal();
contr.terminalChatWidget?.reveal();
}
});

View File

@@ -187,13 +187,13 @@ export class CommandLineAutoApprover extends Disposable {
const defaultValue = configInspectValue?.default?.value;
const isDefaultRule = !!(
isObject(defaultValue) &&
key in defaultValue &&
Object.prototype.hasOwnProperty.call(defaultValue, key) &&
structuralEquals((defaultValue as Record<string, unknown>)[key], value)
);
function checkTarget(inspectValue: Readonly<unknown> | undefined): boolean {
return (
isObject(inspectValue) &&
key in inspectValue &&
Object.prototype.hasOwnProperty.call(inspectValue, key) &&
structuralEquals((inspectValue as Record<string, unknown>)[key], value)
);
}

View File

@@ -10,7 +10,7 @@ import { CancellationError } from '../../../../../base/common/errors.js';
import { Event } from '../../../../../base/common/event.js';
import { DisposableStore, MutableDisposable } from '../../../../../base/common/lifecycle.js';
import { ThemeIcon } from '../../../../../base/common/themables.js';
import { isNumber, isObject } from '../../../../../base/common/types.js';
import { hasKey, isNumber, isObject } from '../../../../../base/common/types.js';
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
import { TerminalCapability } from '../../../../../platform/terminal/common/capabilities/capabilities.js';
import { PromptInputState } from '../../../../../platform/terminal/common/capabilities/commandDetection/promptInputModel.js';
@@ -63,7 +63,7 @@ export class ToolTerminalCreator {
instance.processReady.then(() => processReadyTimestamp = Date.now()),
Event.toPromise(instance.onExit),
]);
if (!isNumber(initResult) && isObject(initResult) && 'message' in initResult) {
if (!isNumber(initResult) && isObject(initResult) && hasKey(initResult, { message: true })) {
throw new Error(initResult.message);
}

View File

@@ -15,6 +15,7 @@ import { PANEL_BORDER } from '../../../../common/theme.js';
import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, IXtermTerminal } from '../../../terminal/browser/terminal.js';
import { registerTerminalContribution, type IDetachedCompatibleTerminalContributionContext, type ITerminalContributionContext } from '../../../terminal/browser/terminalExtensions.js';
import { terminalCommandGuideConfigSection, TerminalCommandGuideSettingId, type ITerminalCommandGuideConfiguration } from '../common/terminalCommandGuideConfiguration.js';
import { isFullTerminalCommand } from '../../../../../platform/terminal/common/capabilities/commandDetection/terminalCommand.js';
// #region Terminal Contributions
@@ -80,7 +81,7 @@ class TerminalCommandGuideContribution extends Disposable implements ITerminalCo
}
const mouseCursorY = Math.floor((e.clientY - rect.top) / (rect.height / xterm.raw.rows));
const command = this._ctx.instance.capabilities.get(TerminalCapability.CommandDetection)?.getCommandForLine(xterm.raw.buffer.active.viewportY + mouseCursorY);
if (command && 'getOutput' in command) {
if (command && isFullTerminalCommand(command)) {
xterm.markTracker.showCommandGuide(command);
} else {
xterm.markTracker.showCommandGuide(undefined);

View File

@@ -31,6 +31,7 @@ import { getCommandHistory, getDirectoryHistory, getShellFileHistory } from '../
import { ResourceSet } from '../../../../../base/common/map.js';
import { extUri, extUriIgnorePathCase } from '../../../../../base/common/resources.js';
import { IPathService } from '../../../../services/path/common/pathService.js';
import { isObject } from '../../../../../base/common/types.js';
export async function showRunRecentQuickPick(
accessor: ServicesAccessor,
@@ -325,7 +326,10 @@ export async function showRunRecentQuickPick(
if (!item) {
return;
}
if ('command' in item && item.command && item.command.marker) {
function isItem(obj: unknown): obj is Item {
return isObject(obj) && 'rawLabel' in obj;
}
if (isItem(item) && item.command && item.command.marker) {
if (!terminalScrollStateSaved) {
xterm.markTracker.saveScrollState();
terminalScrollStateSaved = true;

View File

@@ -20,6 +20,7 @@ import { ILabelService } from '../../../../../platform/label/common/label.js';
import { basenameOrAuthority, dirname } from '../../../../../base/common/resources.js';
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
import { AccessibleViewProviderId, IAccessibleViewService } from '../../../../../platform/accessibility/browser/accessibleView.js';
import { hasKey } from '../../../../../base/common/types.js';
export class TerminalLinkQuickpick extends DisposableStore {
@@ -167,7 +168,7 @@ export class TerminalLinkQuickpick extends DisposableStore {
accepted = true;
const event = new TerminalLinkQuickPickEvent(EventType.CLICK);
const activeItem = pick.activeItems?.[0];
if (activeItem && 'link' in activeItem) {
if (activeItem && hasKey(activeItem, { link: true })) {
activeItem.link.activate(event, activeItem.label);
}
disposables.dispose();
@@ -193,7 +194,7 @@ export class TerminalLinkQuickpick extends DisposableStore {
// Add a consistently formatted resolved URI label to the description if applicable
let description: string | undefined;
if ('uri' in link && link.uri) {
if (hasKey(link, { uri: true }) && link.uri) {
// For local files and folders, mimic the presentation of go to file
if (
link.type === TerminalBuiltinLinkType.LocalFile ||
@@ -234,7 +235,7 @@ export class TerminalLinkQuickpick extends DisposableStore {
}
private _previewItem(item: ITerminalLinkQuickPickItem | IQuickPickItem) {
if (!item || !('link' in item) || !item.link) {
if (!item || !hasKey(item, { link: true }) || !item.link) {
return;
}
@@ -242,7 +243,7 @@ export class TerminalLinkQuickpick extends DisposableStore {
const link = item.link;
this._previewItemInTerminal(link);
if (!('uri' in link) || !link.uri) {
if (!hasKey(link, { uri: true }) || !link.uri) {
return;
}

View File

@@ -7,6 +7,7 @@ import { deepStrictEqual } from 'assert';
import { ITerminalLinkDetector, TerminalLinkType } from '../../browser/links.js';
import { URI } from '../../../../../../base/common/uri.js';
import type { IBufferLine } from '@xterm/xterm';
import { hasKey } from '../../../../../../base/common/types.js';
export async function assertLinkHelper(
text: string,
@@ -40,7 +41,7 @@ export async function assertLinkHelper(
const expectedLinks = expected.map(e => {
return {
type: expectedType,
link: 'uri' in e ? e.uri.toString() : e.text,
link: hasKey(e, { uri: true }) ? e.uri.toString() : e.text,
bufferRange: {
start: { x: e.range[0][0], y: e.range[0][1] },
end: { x: e.range[1][0], y: e.range[1][1] },

View File

@@ -33,7 +33,7 @@ import { CodeActionKind } from '../../../../../editor/contrib/codeAction/common/
import { Codicon } from '../../../../../base/common/codicons.js';
import { ThemeIcon } from '../../../../../base/common/themables.js';
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
import type { SingleOrMany } from '../../../../../base/common/types.js';
import { hasKey, type SingleOrMany } from '../../../../../base/common/types.js';
const enum QuickFixDecorationSelector {
QuickFix = 'quick-fix'
@@ -380,7 +380,7 @@ export async function getQuickFixesForCommand(
if (quickFixes) {
for (const quickFix of asArray(quickFixes)) {
let action: ITerminalAction | undefined;
if ('type' in quickFix) {
if (hasKey(quickFix, { type: true })) {
switch (quickFix.type) {
case TerminalQuickFixType.TerminalCommand: {
const fix = quickFix as ITerminalQuickFixTerminalCommandAction;
@@ -536,7 +536,7 @@ function getQuickFixIcon(quickFix: TerminalQuickFixItem): ThemeIcon {
}
switch (quickFix.type) {
case TerminalQuickFixType.Opener:
if ('uri' in quickFix.action && quickFix.action.uri) {
if (quickFix.action.uri) {
const isUrl = (quickFix.action.uri.scheme === Schemas.http || quickFix.action.uri.scheme === Schemas.https);
return isUrl ? Codicon.linkExternal : Codicon.goToFile;
}

View File

@@ -38,7 +38,10 @@ export const terminalSendSequenceCommand = async (accessor: ServicesAccessor, ar
const instance = terminalService.activeInstance;
if (instance) {
let text = isObject(args) && 'text' in args ? toOptionalString(args.text) : undefined;
function isTextArg(obj: unknown): obj is { text: string } {
return isObject(obj) && 'text' in obj;
}
let text = isTextArg(args) ? toOptionalString(args.text) : undefined;
// If no text provided, prompt user for input and process special characters
if (!text) {

View File

@@ -45,7 +45,10 @@ registerTerminalAction({
return;
}
let signal = isObject(args) && 'signal' in args ? toOptionalString(args.signal) : undefined;
function isSignalArg(obj: unknown): obj is { signal: string } {
return isObject(obj) && 'signal' in obj;
}
let signal = isSignalArg(args) ? toOptionalString(args.signal) : undefined;
if (!signal) {
const signalOptions: QuickPickItem[] = [

View File

@@ -20,7 +20,7 @@ import { IContextKeyService } from '../../../../../platform/contextkey/common/co
import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js';
import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js';
import { ICommandDetectionCapability, ITerminalCommand } from '../../../../../platform/terminal/common/capabilities/capabilities.js';
import { ICurrentPartialCommand } from '../../../../../platform/terminal/common/capabilities/commandDetection/terminalCommand.js';
import { ICurrentPartialCommand, isFullTerminalCommand } from '../../../../../platform/terminal/common/capabilities/commandDetection/terminalCommand.js';
import { IThemeService } from '../../../../../platform/theme/common/themeService.js';
import { ITerminalConfigurationService, ITerminalInstance, IXtermColorProvider, IXtermTerminal } from '../../../terminal/browser/terminal.js';
import { openContextMenu } from '../../../terminal/browser/terminalContextMenu.js';
@@ -235,7 +235,7 @@ export class TerminalStickyScrollOverlay extends Disposable {
}
// Partial command
if (!('marker' in command)) {
if (!isFullTerminalCommand(command)) {
const partialCommand = this._commandDetection.currentCommand;
if (partialCommand?.commandStartMarker && partialCommand.commandExecutedMarker) {
this._updateContent(partialCommand, partialCommand.commandStartMarker);
@@ -279,7 +279,7 @@ export class TerminalStickyScrollOverlay extends Disposable {
// of the sticky overlay because we do not want to show any content above the bounds of the
// original terminal. This is done because it seems like scrolling flickers more when a
// partial line can be drawn on the top.
const isPartialCommand = !('getOutput' in command);
const isPartialCommand = !isFullTerminalCommand(command);
const rowOffset = !isPartialCommand && command.endMarker ? Math.max(buffer.viewportY - command.endMarker.line + 1, 0) : 0;
const maxLineCount = Math.min(this._rawMaxLineCount, Math.floor(xterm.rows * Constants.StickyScrollPercentageCap));
const stickyScrollLineCount = Math.min(promptRowCount + commandRowCount - 1, maxLineCount) - rowOffset;

View File

@@ -175,7 +175,7 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo
const providerConfig: { [key: string]: boolean } = this._configurationService.getValue(TerminalSuggestSettingId.Providers);
return providers.filter(p => {
const providerId = p.id;
return providerId && (!(providerId in providerConfig) || providerConfig[providerId] !== false);
return providerId && (!Object.prototype.hasOwnProperty.call(providerConfig, providerId) || providerConfig[providerId] !== false);
});
}

View File

@@ -693,7 +693,7 @@ export class TimelinePane extends ViewPane {
}
}
private *getItems(): Generator<ITreeElement<TreeElement>, any, any> {
private *getItems(): Generator<ITreeElement<TreeElement>, void, undefined> {
let more = false;
if (this.uri === undefined || this.timelinesBySource.size === 0) {