mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-28 06:31:58 +00:00
Merge pull request #285039 from microsoft/tyriar/285032__285031
Fix performance problems with terminal tabs
This commit is contained in:
commit
6979e72216
@ -90,7 +90,6 @@ import type { IMenu } from '../../../../platform/actions/common/actions.js';
|
||||
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
|
||||
import { TerminalContribCommandId } from '../terminalContribExports.js';
|
||||
import type { IProgressState } from '@xterm/addon-progress';
|
||||
import { refreshShellIntegrationInfoStatus } from './terminalTooltip.js';
|
||||
import { generateUuid } from '../../../../base/common/uuid.js';
|
||||
import { PromptInputState } from '../../../../platform/terminal/common/capabilities/commandDetection/promptInputModel.js';
|
||||
import { hasKey, isNumber, isString } from '../../../../base/common/types.js';
|
||||
@ -465,7 +464,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
capabilityListeners.get(e.id)?.dispose();
|
||||
const refreshInfo = () => {
|
||||
this._labelComputer?.refreshLabel(this);
|
||||
refreshShellIntegrationInfoStatus(this);
|
||||
this._refreshShellIntegrationInfoStatus(this);
|
||||
};
|
||||
switch (e.id) {
|
||||
case TerminalCapability.CwdDetection: {
|
||||
@ -499,7 +498,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
}
|
||||
}
|
||||
}));
|
||||
this._register(this.onDidChangeShellType(() => refreshShellIntegrationInfoStatus(this)));
|
||||
this._register(this.onDidChangeShellType(() => this._refreshShellIntegrationInfoStatus(this)));
|
||||
this._register(this.capabilities.onDidRemoveCapability(e => {
|
||||
capabilityListeners.get(e.id)?.dispose();
|
||||
}));
|
||||
@ -888,7 +887,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
// Register and update the terminal's shell integration status
|
||||
this._register(Event.runAndSubscribe(xterm.shellIntegration.onDidChangeSeenSequences, () => {
|
||||
if (xterm.shellIntegration.seenSequences.size > 0) {
|
||||
refreshShellIntegrationInfoStatus(this);
|
||||
this._refreshShellIntegrationInfoStatus(this);
|
||||
}
|
||||
}));
|
||||
|
||||
@ -922,6 +921,54 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
return xterm;
|
||||
}
|
||||
|
||||
// Debounce this to avoid impacting input latency while typing into the prompt
|
||||
@debounce(500)
|
||||
private _refreshShellIntegrationInfoStatus(instance: ITerminalInstance) {
|
||||
if (!instance.xterm) {
|
||||
return;
|
||||
}
|
||||
const cmdDetectionType = (
|
||||
instance.capabilities.get(TerminalCapability.CommandDetection)?.hasRichCommandDetection
|
||||
? nls.localize('shellIntegration.rich', 'Rich')
|
||||
: instance.capabilities.has(TerminalCapability.CommandDetection)
|
||||
? nls.localize('shellIntegration.basic', 'Basic')
|
||||
: instance.usedShellIntegrationInjection
|
||||
? nls.localize('shellIntegration.injectionFailed', "Injection failed to activate")
|
||||
: nls.localize('shellIntegration.no', 'No')
|
||||
);
|
||||
|
||||
const detailedAdditions: string[] = [];
|
||||
if (instance.shellType) {
|
||||
detailedAdditions.push(`Shell type: \`${instance.shellType}\``);
|
||||
}
|
||||
const cwd = instance.cwd;
|
||||
if (cwd) {
|
||||
detailedAdditions.push(`Current working directory: \`${cwd}\``);
|
||||
}
|
||||
const seenSequences = Array.from(instance.xterm.shellIntegration.seenSequences);
|
||||
if (seenSequences.length > 0) {
|
||||
detailedAdditions.push(`Seen sequences: ${seenSequences.map(e => `\`${e}\``).join(', ')}`);
|
||||
}
|
||||
const promptType = instance.capabilities.get(TerminalCapability.PromptTypeDetection)?.promptType;
|
||||
if (promptType) {
|
||||
detailedAdditions.push(`Prompt type: \`${promptType}\``);
|
||||
}
|
||||
const combinedString = instance.capabilities.get(TerminalCapability.CommandDetection)?.promptInputModel.getCombinedString();
|
||||
if (combinedString !== undefined) {
|
||||
detailedAdditions.push(`Prompt input: \`\`\`${combinedString}\`\`\``);
|
||||
}
|
||||
const detailedAdditionsString = detailedAdditions.length > 0
|
||||
? '\n\n' + detailedAdditions.map(e => `- ${e}`).join('\n')
|
||||
: '';
|
||||
|
||||
instance.statusList.add({
|
||||
id: TerminalStatus.ShellIntegrationInfo,
|
||||
severity: Severity.Info,
|
||||
tooltip: `${nls.localize('shellIntegration', "Shell integration")}: ${cmdDetectionType}`,
|
||||
detailedTooltip: `${nls.localize('shellIntegration', "Shell integration")}: ${cmdDetectionType}${detailedAdditionsString}`
|
||||
});
|
||||
}
|
||||
|
||||
async runCommand(commandLine: string, shouldExecute: boolean, commandId?: string): Promise<void> {
|
||||
let commandDetection = this.capabilities.get(TerminalCapability.CommandDetection);
|
||||
const siInjectionEnabled = this._configurationService.getValue(TerminalSettingId.ShellIntegrationEnabled) === true;
|
||||
|
||||
@ -74,6 +74,12 @@ export class TerminalTabList extends WorkbenchList<ITerminalInstance> {
|
||||
private _terminalTabsSingleSelectedContextKey: IContextKey<boolean>;
|
||||
private _isSplitContextKey: IContextKey<boolean>;
|
||||
|
||||
private _hasText: boolean = true;
|
||||
get hasText(): boolean { return this._hasText; }
|
||||
|
||||
private _hasActionBar: boolean = true;
|
||||
get hasActionBar(): boolean { return this._hasActionBar; }
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@ -94,7 +100,7 @@ export class TerminalTabList extends WorkbenchList<ITerminalInstance> {
|
||||
getHeight: () => TerminalTabsListSizes.TabHeight,
|
||||
getTemplateId: () => 'terminal.tabs'
|
||||
},
|
||||
[instantiationService.createInstance(TerminalTabsRenderer, container, instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER), () => this.getSelectedElements())],
|
||||
[instantiationService.createInstance(TerminalTabsRenderer, container, instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER), () => this.getSelectedElements(), () => this.hasText, () => this.hasActionBar)],
|
||||
{
|
||||
horizontalScrolling: false,
|
||||
supportDynamicHeights: false,
|
||||
@ -248,15 +254,29 @@ export class TerminalTabList extends WorkbenchList<ITerminalInstance> {
|
||||
const instance = this.getFocusedElements();
|
||||
this._isSplitContextKey.set(instance.length > 0 && this._terminalGroupService.instanceIsSplit(instance[0]));
|
||||
}
|
||||
|
||||
override layout(height?: number, width?: number): void {
|
||||
super.layout(height, width);
|
||||
const actualWidth = width ?? this.getHTMLElement().clientWidth;
|
||||
const newHasText = actualWidth >= TerminalTabsListSizes.MidpointViewWidth;
|
||||
const newHasActionBar = actualWidth > TerminalTabsListSizes.ActionbarMinimumWidth;
|
||||
if (this._hasText !== newHasText || this._hasActionBar !== newHasActionBar) {
|
||||
this._hasText = newHasText;
|
||||
this._hasActionBar = newHasActionBar;
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TerminalTabsRenderer implements IListRenderer<ITerminalInstance, ITerminalTabEntryTemplate> {
|
||||
templateId = 'terminal.tabs';
|
||||
|
||||
constructor(
|
||||
private readonly _container: HTMLElement,
|
||||
_container: HTMLElement,
|
||||
private readonly _labels: ResourceLabels,
|
||||
private readonly _getSelection: () => ITerminalInstance[],
|
||||
private readonly _getHasText: () => boolean,
|
||||
private readonly _getHasActionBar: () => boolean,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ITerminalConfigurationService private readonly _terminalConfigurationService: ITerminalConfigurationService,
|
||||
@ITerminalService private readonly _terminalService: ITerminalService,
|
||||
@ -321,25 +341,9 @@ class TerminalTabsRenderer implements IListRenderer<ITerminalInstance, ITerminal
|
||||
};
|
||||
}
|
||||
|
||||
shouldHideText(): boolean {
|
||||
return this._container ? this.getContainerWidthCachedForTask() < TerminalTabsListSizes.MidpointViewWidth : false;
|
||||
}
|
||||
|
||||
shouldHideActionBar(): boolean {
|
||||
return this._container ? this.getContainerWidthCachedForTask() <= TerminalTabsListSizes.ActionbarMinimumWidth : false;
|
||||
}
|
||||
|
||||
private _cachedContainerWidth = -1;
|
||||
getContainerWidthCachedForTask(): number {
|
||||
if (this._cachedContainerWidth === -1) {
|
||||
this._cachedContainerWidth = this._container.clientWidth;
|
||||
queueMicrotask(() => this._cachedContainerWidth = -1);
|
||||
}
|
||||
return this._cachedContainerWidth;
|
||||
}
|
||||
|
||||
renderElement(instance: ITerminalInstance, index: number, template: ITerminalTabEntryTemplate): void {
|
||||
const hasText = !this.shouldHideText();
|
||||
const hasText = this._getHasText();
|
||||
const hasActionBar = this._getHasActionBar();
|
||||
|
||||
const group = this._terminalGroupService.getGroupForInstance(instance);
|
||||
if (!group) {
|
||||
@ -365,7 +369,6 @@ class TerminalTabsRenderer implements IListRenderer<ITerminalInstance, ITerminal
|
||||
template.context.hoverActions = hoverInfo.actions;
|
||||
|
||||
const iconId = this._instantiationService.invokeFunction(getIconId, instance);
|
||||
const hasActionbar = !this.shouldHideActionBar();
|
||||
let label: string = '';
|
||||
if (!hasText) {
|
||||
const primaryStatus = instance.statusList.primary;
|
||||
@ -385,7 +388,7 @@ class TerminalTabsRenderer implements IListRenderer<ITerminalInstance, ITerminal
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasActionbar) {
|
||||
if (!hasActionBar) {
|
||||
template.actionBar.clear();
|
||||
}
|
||||
|
||||
|
||||
@ -3,18 +3,15 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from '../../../../nls.js';
|
||||
import { ITerminalInstance } from './terminal.js';
|
||||
import type { IHoverAction } from '../../../../base/browser/ui/hover/hover.js';
|
||||
import { asArray } from '../../../../base/common/arrays.js';
|
||||
import { MarkdownString } from '../../../../base/common/htmlContent.js';
|
||||
import type { IHoverAction } from '../../../../base/browser/ui/hover/hover.js';
|
||||
import { TerminalCapability } from '../../../../platform/terminal/common/capabilities/capabilities.js';
|
||||
import { TerminalStatus } from './terminalStatusList.js';
|
||||
import Severity from '../../../../base/common/severity.js';
|
||||
import { StorageScope, StorageTarget, type IStorageService } from '../../../../platform/storage/common/storage.js';
|
||||
import { TerminalStorageKeys } from '../common/terminalStorageKeys.js';
|
||||
import type { ITerminalStatusHoverAction } from '../common/terminal.js';
|
||||
import { basename } from '../../../../base/common/path.js';
|
||||
import { localize } from '../../../../nls.js';
|
||||
import { StorageScope, StorageTarget, type IStorageService } from '../../../../platform/storage/common/storage.js';
|
||||
import type { ITerminalStatusHoverAction } from '../common/terminal.js';
|
||||
import { TerminalStorageKeys } from '../common/terminalStorageKeys.js';
|
||||
import { ITerminalInstance } from './terminal.js';
|
||||
|
||||
export function getInstanceHoverInfo(instance: ITerminalInstance, storageService: IStorageService): { content: MarkdownString; actions: IHoverAction[] } {
|
||||
const showDetailed = parseInt(storageService.get(TerminalStorageKeys.TabsShowDetailed, StorageScope.APPLICATION) ?? '0');
|
||||
@ -77,48 +74,3 @@ export function getShellProcessTooltip(instance: ITerminalInstance, showDetailed
|
||||
return lines.length ? `\n\n---\n\n${lines.join('\n')}` : '';
|
||||
}
|
||||
|
||||
export function refreshShellIntegrationInfoStatus(instance: ITerminalInstance) {
|
||||
if (!instance.xterm) {
|
||||
return;
|
||||
}
|
||||
const cmdDetectionType = (
|
||||
instance.capabilities.get(TerminalCapability.CommandDetection)?.hasRichCommandDetection
|
||||
? localize('shellIntegration.rich', 'Rich')
|
||||
: instance.capabilities.has(TerminalCapability.CommandDetection)
|
||||
? localize('shellIntegration.basic', 'Basic')
|
||||
: instance.usedShellIntegrationInjection
|
||||
? localize('shellIntegration.injectionFailed', "Injection failed to activate")
|
||||
: localize('shellIntegration.no', 'No')
|
||||
);
|
||||
|
||||
const detailedAdditions: string[] = [];
|
||||
if (instance.shellType) {
|
||||
detailedAdditions.push(`Shell type: \`${instance.shellType}\``);
|
||||
}
|
||||
const cwd = instance.cwd;
|
||||
if (cwd) {
|
||||
detailedAdditions.push(`Current working directory: \`${cwd}\``);
|
||||
}
|
||||
const seenSequences = Array.from(instance.xterm.shellIntegration.seenSequences);
|
||||
if (seenSequences.length > 0) {
|
||||
detailedAdditions.push(`Seen sequences: ${seenSequences.map(e => `\`${e}\``).join(', ')}`);
|
||||
}
|
||||
const promptType = instance.capabilities.get(TerminalCapability.PromptTypeDetection)?.promptType;
|
||||
if (promptType) {
|
||||
detailedAdditions.push(`Prompt type: \`${promptType}\``);
|
||||
}
|
||||
const combinedString = instance.capabilities.get(TerminalCapability.CommandDetection)?.promptInputModel.getCombinedString();
|
||||
if (combinedString !== undefined) {
|
||||
detailedAdditions.push(`Prompt input: \`\`\`${combinedString}\`\`\``);
|
||||
}
|
||||
const detailedAdditionsString = detailedAdditions.length > 0
|
||||
? '\n\n' + detailedAdditions.map(e => `- ${e}`).join('\n')
|
||||
: '';
|
||||
|
||||
instance.statusList.add({
|
||||
id: TerminalStatus.ShellIntegrationInfo,
|
||||
severity: Severity.Info,
|
||||
tooltip: `${localize('shellIntegration', "Shell integration")}: ${cmdDetectionType}`,
|
||||
detailedTooltip: `${localize('shellIntegration', "Shell integration")}: ${cmdDetectionType}${detailedAdditionsString}`
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user