Merge branch 'main' into tyriar/141599

This commit is contained in:
Daniel Imms 2022-02-15 12:08:52 -08:00 committed by GitHub
commit f5fbf3b920
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 420 additions and 158 deletions

View file

@ -209,13 +209,6 @@ function packageTask(sourceFolderName, destinationFolderName) {
gulp.src('resources/server/code-512.png', { base: 'resources/server' })
);
// TODO@bpasero remove me in Feb
const legacyMain = es.merge(
gulp.src(sourceFolderName + '/vs/workbench/workbench.web.main.js').pipe(rename('out/vs/workbench/workbench.web.api.js')).pipe(replace('workbench.web.main', 'workbench.web.api')),
gulp.src(sourceFolderName + '/vs/workbench/workbench.web.main.css').pipe(rename('out/vs/workbench/workbench.web.api.css')),
gulp.src(sourceFolderName + '/vs/workbench/workbench.web.main.nls.js').pipe(rename('out/vs/workbench/workbench.web.api.nls.js')).pipe(replace('workbench.web.main', 'workbench.web.api'))
);
let all = es.merge(
packageJsonStream,
license,
@ -223,8 +216,7 @@ function packageTask(sourceFolderName, destinationFolderName) {
deps,
favicon,
manifest,
pwaicons,
legacyMain
pwaicons
);
let result = all

View file

@ -531,6 +531,11 @@
"title": "%command.rebaseAbort%",
"category": "Git"
},
{
"command": "git.closeAllDiffEditors",
"title": "%command.closeAllDiffEditors%",
"category": "Git"
},
{
"command": "git.api.getRepositories",
"title": "%command.api.getRepositories%",
@ -933,6 +938,10 @@
"command": "git.timeline.compareWithSelected",
"when": "false"
},
{
"command": "git.closeAllDiffEditors",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.api.getRepositories",
"when": "false"
@ -2231,7 +2240,9 @@
"default",
"download"
],
"tags": ["experimental"],
"tags": [
"experimental"
],
"scope": "machine",
"description": "%config.experimental.installGuide%",
"default": "default"

View file

@ -29,6 +29,7 @@
"command.cleanAll": "Discard All Changes",
"command.cleanAllTracked": "Discard All Tracked Changes",
"command.cleanAllUntracked": "Discard All Untracked Changes",
"command.closeAllDiffEditors": "Close All Diff Editors",
"command.commit": "Commit",
"command.commitStaged": "Commit Staged",
"command.commitEmpty": "Commit Empty",

View file

@ -2753,6 +2753,17 @@ export class CommandCenter {
}
}
@command('git.closeAllDiffEditors', { repository: true })
closeDiffEditors(repository: Repository): void {
const resources = [
...repository.indexGroup.resourceStates.map(r => r.resourceUri.fsPath),
...repository.workingTreeGroup.resourceStates.map(r => r.resourceUri.fsPath),
...repository.untrackedGroup.resourceStates.map(r => r.resourceUri.fsPath)
];
repository.closeDiffEditors(resources, resources, true);
}
private createCommand(id: string, key: string, method: Function, options: ScmCommandOptions): (...args: any[]) => any {
const result = (...args: any[]) => {
let result: Promise<any>;

View file

@ -2043,7 +2043,6 @@ export class Repository {
if (branchName.startsWith('refs/heads/')) {
branchName = branchName.substring(11);
const index = upstream.indexOf('/');
let ahead;
let behind;
@ -2056,8 +2055,8 @@ export class Repository {
type: RefType.Head,
name: branchName,
upstream: upstream ? {
name: upstream.substring(index + 1),
remote: upstream.substring(0, index)
name: upstream.substring(upstream.length - branchName.length),
remote: upstream.substring(0, upstream.length - branchName.length - 1)
} : undefined,
commit: ref || undefined,
ahead: Number(ahead) || 0,

View file

@ -1266,9 +1266,9 @@ export class Repository implements Disposable {
});
}
private closeDiffEditors(indexResources: string[], workingTreeResources: string[]): void {
closeDiffEditors(indexResources: string[], workingTreeResources: string[], ignoreSetting: boolean = false): void {
const config = workspace.getConfiguration('git', Uri.file(this.root));
if (!config.get<boolean>('closeDiffOnOperation', false)) { return; }
if (!config.get<boolean>('closeDiffOnOperation', false) && !ignoreSetting) { return; }
const diffEditorTabsToClose: Tab[] = [];

View file

@ -75,7 +75,7 @@
"keytar": "7.6.0",
"minimist": "^1.2.5",
"native-is-elevated": "0.4.3",
"native-keymap": "3.1.0",
"native-keymap": "3.2.1",
"native-watchdog": "1.3.0",
"node-pty": "0.11.0-beta11",
"spdlog": "^0.13.0",

View file

@ -27,7 +27,7 @@
"licenseFileName": "LICENSE.txt",
"reportIssueUrl": "https://github.com/microsoft/vscode/issues/new",
"urlProtocol": "code-oss",
"webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-webview.net/insider/d372f9187401bd145a0a6e15ba369e2d82d02005/out/vs/workbench/contrib/webview/browser/pre/",
"webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-webview.net/insider/50089c3f92c17584a4aca179f51f220b56c22020/out/vs/workbench/contrib/webview/browser/pre/",
"extensionAllowedProposedApi": [
"ms-vscode.vscode-js-profile-flame",
"ms-vscode.vscode-js-profile-table",

View file

@ -51,13 +51,45 @@ class WindowManager {
}
}
/**
* See https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#monitoring_screen_resolution_or_zoom_level_changes
*/
class DevicePixelRatioMonitor extends Disposable {
private readonly _onDidChange = this._register(new Emitter<void>());
public readonly onDidChange = this._onDidChange.event;
private readonly _listener: () => void;
private _mediaQueryList: MediaQueryList | null;
constructor() {
super();
this._listener = () => this._handleChange(true);
this._mediaQueryList = null;
this._handleChange(false);
}
private _handleChange(fireEvent: boolean): void {
if (this._mediaQueryList) {
this._mediaQueryList.removeEventListener('change', this._listener);
}
this._mediaQueryList = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
this._mediaQueryList.addEventListener('change', this._listener);
if (fireEvent) {
this._onDidChange.fire();
}
}
}
class PixelRatioImpl extends Disposable {
private readonly _onDidChange = this._register(new Emitter<number>());
public readonly onDidChange = this._onDidChange.event;
private _value: number;
private _removeListener: () => void;
public get value(): number {
return this._value;
@ -67,28 +99,12 @@ class PixelRatioImpl extends Disposable {
super();
this._value = this._getPixelRatio();
this._removeListener = this._installResolutionListener();
}
public override dispose() {
this._removeListener();
super.dispose();
}
private _installResolutionListener(): () => void {
// See https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#monitoring_screen_resolution_or_zoom_level_changes
const mediaQueryList = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
const listener = () => this._updateValue();
mediaQueryList.addEventListener('change', listener);
return () => {
mediaQueryList.removeEventListener('change', listener);
};
}
private _updateValue(): void {
this._value = this._getPixelRatio();
this._onDidChange.fire(this._value);
this._removeListener = this._installResolutionListener();
const dprMonitor = this._register(new DevicePixelRatioMonitor());
this._register(dprMonitor.onDidChange(() => {
this._value = this._getPixelRatio();
this._onDidChange.fire(this._value);
}));
}
private _getPixelRatio(): number {

View file

@ -9,7 +9,7 @@ import { DistributeSizing, ISplitViewStyles, IView as ISplitView, LayoutPriority
import { equals as arrayEquals, tail2 as tail } from 'vs/base/common/arrays';
import { Color } from 'vs/base/common/color';
import { Emitter, Event, Relay } from 'vs/base/common/event';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { rot } from 'vs/base/common/numbers';
import { isUndefined } from 'vs/base/common/types';
import 'vs/css!./gridview';
@ -775,6 +775,8 @@ class LeafNode implements ISplitView<ILayoutContext>, IDisposable {
private _onDidViewChange: Event<number | undefined>;
readonly onDidChange: Event<number | undefined>;
private disposables = new DisposableStore();
constructor(
readonly view: IView,
readonly orientation: Orientation,
@ -786,7 +788,7 @@ class LeafNode implements ISplitView<ILayoutContext>, IDisposable {
this._size = size;
const onDidChange = createLatchedOnDidChangeViewEvent(view);
this._onDidViewChange = Event.map(onDidChange, e => e && (this.orientation === Orientation.VERTICAL ? e.width : e.height));
this._onDidViewChange = Event.map(onDidChange, e => e && (this.orientation === Orientation.VERTICAL ? e.width : e.height), this.disposables);
this.onDidChange = Event.any(this._onDidViewChange, this._onDidSetLinkedNode.event, this._onDidLinkedWidthNodeChange.event, this._onDidLinkedHeightNodeChange.event);
}
@ -900,7 +902,9 @@ class LeafNode implements ISplitView<ILayoutContext>, IDisposable {
}
}
dispose(): void { }
dispose(): void {
this.disposables.dispose();
}
}
type Node = BranchNode | LeafNode;

View file

@ -434,6 +434,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
const emptyDisposedTextBuffer = new PieceTreeTextBuffer([], '', '\n', false, false, true, true);
emptyDisposedTextBuffer.dispose();
this._buffer = emptyDisposedTextBuffer;
this._bufferDisposable = Disposable.None;
}
private _assertNotDisposed(): void {

View file

@ -66,7 +66,6 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa
setResourceEnablement(resource: SyncResource, enabled: boolean): void {
if (this.isResourceEnabled(resource) !== enabled) {
const resourceEnablementKey = getEnablementKey(resource);
this.telemetryService.publicLog2<{ enabled: boolean }, SyncEnablementClassification>(resourceEnablementKey, { enabled });
this.storeResourceEnablement(resourceEnablementKey, enabled);
}
}

View file

@ -5,7 +5,7 @@
import 'vs/workbench/browser/style';
import { localize } from 'vs/nls';
import { Event, Emitter, setGlobalLeakWarningThreshold } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { RunOnceScheduler, runWhenIdle, timeout } from 'vs/base/common/async';
import { isFirefox, isSafari, isChrome, PixelRatio } from 'vs/base/browser/browser';
import { mark } from 'vs/base/common/performance';
@ -133,7 +133,9 @@ export class Workbench extends Layout {
try {
// Configure emitter leak warning threshold
setGlobalLeakWarningThreshold(175);
// TODO@jrieken: This is temporarily commented out
// due to https://github.com/microsoft/vscode/issues/143111
// setGlobalLeakWarningThreshold(175);
// Services
const instantiationService = this.initServices(this.serviceCollection);

View file

@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { IDebugService, State } from 'vs/workbench/contrib/debug/common/debug';
export class AudioCueLineDebuggerContribution
extends Disposable
implements IWorkbenchContribution {
constructor(
@IDebugService debugService: IDebugService,
@IAudioCueService audioCueService: IAudioCueService,
) {
super();
this._register(debugService.onDidChangeState(e => {
if (e === State.Stopped) {
audioCueService.playAudioCue(AudioCue.executionStopped);
}
}));
}
}

View file

@ -16,8 +16,9 @@ import { autorun, autorunDelta, constObservable, debouncedObservable, fromEvent,
import { ITextModel } from 'vs/editor/common/model';
import { GhostTextController } from 'vs/editor/contrib/inlineCompletions/browser/ghostTextController';
import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { CursorChangeReason } from 'vs/editor/common/cursorEvents';
export class AudioCueContribution
export class AudioCueLineFeatureContribution
extends Disposable
implements IWorkbenchContribution {
private readonly store = this._register(new DisposableStore());
@ -88,7 +89,13 @@ export class AudioCueContribution
const curLineNumber = fromEvent(
editor.onDidChangeCursorPosition,
() => editor.getPosition()?.lineNumber
(args) => {
if (args && args.reason !== CursorChangeReason.Explicit) {
// Ignore cursor changes caused by navigation (e.g. which happens when execution is paused).
return undefined;
}
return editor.getPosition()?.lineNumber;
}
);
const debouncedLineNumber = debouncedObservable(curLineNumber, 100, store);

View file

@ -148,6 +148,12 @@ export class AudioCue {
settingsKey: 'audioCues.lineHasInlineSuggestion',
});
public static readonly executionStopped = AudioCue.register({
name: 'Debugger Execution Paused',
sound: Sound.break,
settingsKey: 'audioCues.debuggerExecutionPaused',
});
private constructor(
public readonly sound: Sound,
public readonly name: string,

View file

@ -9,14 +9,16 @@ import { Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IC
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { AudioCueContribution } from 'vs/workbench/contrib/audioCues/browser/audioCueContribution';
import { AudioCueLineDebuggerContribution } from 'vs/workbench/contrib/audioCues/browser/audioCueDebuggerContribution';
import { AudioCueLineFeatureContribution } from 'vs/workbench/contrib/audioCues/browser/audioCueLineFeatureContribution';
import { AudioCueService, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { ShowAudioCueHelp } from 'vs/workbench/contrib/audioCues/browser/commands';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
registerSingleton(IAudioCueService, AudioCueService);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AudioCueContribution, LifecyclePhase.Restored);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AudioCueLineFeatureContribution, LifecyclePhase.Restored);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(AudioCueLineDebuggerContribution, LifecyclePhase.Restored);
const audioCueFeatureBase: IConfigurationPropertySchema = {
'type': 'string',
@ -55,6 +57,10 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).regis
...audioCueFeatureBase,
default: 'off',
},
'audioCues.debuggerExecutionPaused': {
'description': localize('audioCues.debuggerExecutionPaused', "Plays an audio cue when the debugger paused."),
...audioCueFeatureBase,
},
}
});

View file

@ -18,16 +18,19 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { WorkbenchAsyncDataTree, IListService, IWorkbenchAsyncDataTreeOptions } from 'vs/platform/list/browser/listService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IColorMapping } from 'vs/platform/theme/common/styler';
import { TimestampWidget } from 'vs/workbench/contrib/comments/browser/timestamp';
import { Codicon } from 'vs/base/common/codicons';
import { IMarkdownString } from 'vs/base/common/htmlContent';
export const COMMENTS_VIEW_ID = 'workbench.panel.comments';
export const COMMENTS_VIEW_TITLE = 'Comments';
export class CommentsAsyncDataSource implements IAsyncDataSource<any, any> {
hasChildren(element: any): boolean {
return element instanceof CommentsModel || element instanceof ResourceWithCommentThreads || (element instanceof CommentNode && !!element.replies.length);
return (element instanceof CommentsModel || element instanceof ResourceWithCommentThreads) && !(element instanceof CommentNode);
}
getChildren(element: any): any[] | Promise<any[]> {
@ -37,9 +40,6 @@ export class CommentsAsyncDataSource implements IAsyncDataSource<any, any> {
if (element instanceof ResourceWithCommentThreads) {
return Promise.resolve(element.commentThreads);
}
if (element instanceof CommentNode) {
return Promise.resolve(element.replies);
}
return Promise.resolve([]);
}
}
@ -49,9 +49,21 @@ interface IResourceTemplateData {
}
interface ICommentThreadTemplateData {
icon: HTMLImageElement;
userName: HTMLSpanElement;
commentText: HTMLElement;
threadMetadata: {
icon?: HTMLElement;
userNames: HTMLSpanElement;
timestamp: TimestampWidget;
separator: HTMLElement;
commentPreview: HTMLSpanElement;
};
repliesMetadata: {
container: HTMLElement;
icon: HTMLElement;
count: HTMLSpanElement;
lastReplyDetail: HTMLSpanElement;
separator: HTMLElement;
timestamp: TimestampWidget;
};
disposables: IDisposable[];
}
@ -61,6 +73,9 @@ export class CommentsModelVirualDelegate implements IListVirtualDelegate<any> {
getHeight(element: any): number {
if ((element instanceof CommentNode) && element.hasReply()) {
return 44;
}
return 22;
}
@ -105,50 +120,97 @@ export class CommentNodeRenderer implements IListRenderer<ITreeNode<CommentNode>
templateId: string = 'comment-node';
constructor(
@IOpenerService private readonly openerService: IOpenerService
@IOpenerService private readonly openerService: IOpenerService,
@IConfigurationService private readonly configurationService: IConfigurationService
) { }
renderTemplate(container: HTMLElement) {
const data = <ICommentThreadTemplateData>Object.create(null);
const labelContainer = dom.append(container, dom.$('.comment-container'));
data.userName = dom.append(labelContainer, dom.$('.user'));
data.commentText = dom.append(labelContainer, dom.$('.text'));
data.disposables = [];
const threadContainer = dom.append(container, dom.$('.comment-thread-container'));
const metadataContainer = dom.append(threadContainer, dom.$('.comment-metadata-container'));
data.threadMetadata = {
icon: dom.append(metadataContainer, dom.$('.icon')),
userNames: dom.append(metadataContainer, dom.$('.user')),
timestamp: new TimestampWidget(this.configurationService, dom.append(metadataContainer, dom.$('.timestamp-container'))),
separator: dom.append(metadataContainer, dom.$('.separator')),
commentPreview: dom.append(metadataContainer, dom.$('.text'))
};
data.threadMetadata.separator.innerText = '\u00b7';
const snippetContainer = dom.append(threadContainer, dom.$('.comment-snippet-container'));
data.repliesMetadata = {
container: snippetContainer,
icon: dom.append(snippetContainer, dom.$('.icon')),
count: dom.append(snippetContainer, dom.$('.count')),
lastReplyDetail: dom.append(snippetContainer, dom.$('.reply-detail')),
separator: dom.append(snippetContainer, dom.$('.separator')),
timestamp: new TimestampWidget(this.configurationService, dom.append(snippetContainer, dom.$('.timestamp-container'))),
};
data.repliesMetadata.separator.innerText = '\u00b7';
data.repliesMetadata.icon.classList.add(...ThemeIcon.asClassNameArray(Codicon.indent));
data.disposables = [data.threadMetadata.timestamp, data.repliesMetadata.timestamp];
return data;
}
renderElement(node: ITreeNode<CommentNode>, index: number, templateData: ICommentThreadTemplateData, height: number | undefined): void {
templateData.userName.textContent = node.element.comment.userName;
templateData.commentText.innerText = '';
if (typeof node.element.comment.body === 'string') {
templateData.commentText.innerText = node.element.comment.body;
private getCountString(commentCount: number): string {
if (commentCount > 1) {
return nls.localize('commentsCount', "{0} comments", commentCount);
} else {
return nls.localize('commentCount', "1 comment");
}
}
private getRenderedComment(commentBody: IMarkdownString, disposables: DisposableStore) {
const renderedComment = renderMarkdown(commentBody, {
inline: true,
actionHandler: {
callback: (content) => {
this.openerService.open(content, { allowCommands: commentBody.isTrusted }).catch(onUnexpectedError);
},
disposables: disposables
}
});
const images = renderedComment.element.getElementsByTagName('img');
for (let i = 0; i < images.length; i++) {
const image = images[i];
const textDescription = dom.$('');
textDescription.textContent = image.alt ? nls.localize('imageWithLabel', "Image: {0}", image.alt) : nls.localize('image', "Image");
image.parentNode!.replaceChild(textDescription, image);
}
return renderedComment;
}
renderElement(node: ITreeNode<CommentNode>, index: number, templateData: ICommentThreadTemplateData, height: number | undefined): void {
const commentCount = node.element.replies.length + 1;
templateData.threadMetadata.icon?.classList.add(...ThemeIcon.asClassNameArray((commentCount === 1) ? Codicon.comment : Codicon.commentDiscussion));
templateData.threadMetadata.userNames.textContent = node.element.comment.userName;
templateData.threadMetadata.timestamp.setTimestamp(node.element.comment.timestamp ? new Date(node.element.comment.timestamp) : undefined);
const originalComment = node.element;
templateData.threadMetadata.commentPreview.innerText = '';
if (typeof originalComment.comment.body === 'string') {
templateData.threadMetadata.commentPreview.innerText = originalComment.comment.body;
} else {
const commentBody = node.element.comment.body;
const disposables = new DisposableStore();
templateData.disposables.push(disposables);
const renderedComment = renderMarkdown(commentBody, {
inline: true,
actionHandler: {
callback: (content) => {
this.openerService.open(content, { allowCommands: commentBody.isTrusted }).catch(onUnexpectedError);
},
disposables: disposables
}
});
const renderedComment = this.getRenderedComment(originalComment.comment.body, disposables);
templateData.disposables.push(renderedComment);
const images = renderedComment.element.getElementsByTagName('img');
for (let i = 0; i < images.length; i++) {
const image = images[i];
const textDescription = dom.$('');
textDescription.textContent = image.alt ? nls.localize('imageWithLabel', "Image: {0}", image.alt) : nls.localize('image', "Image");
image.parentNode!.replaceChild(textDescription, image);
}
templateData.commentText.appendChild(renderedComment.element);
templateData.commentText.title = renderedComment.element.textContent ?? '';
templateData.threadMetadata.commentPreview.appendChild(renderedComment.element);
templateData.threadMetadata.commentPreview.title = renderedComment.element.textContent ?? '';
}
if (!node.element.hasReply()) {
templateData.repliesMetadata.container.style.display = 'none';
return;
}
templateData.repliesMetadata.container.style.display = '';
templateData.repliesMetadata.count.textContent = this.getCountString(commentCount);
templateData.repliesMetadata.lastReplyDetail.textContent = nls.localize('lastReplyFrom', "Last reply from {0}", node.element.replies[node.element.replies.length - 1].comment.userName);
templateData.repliesMetadata.timestamp.setTimestamp(originalComment.comment.timestamp ? new Date(originalComment.comment.timestamp) : undefined);
}
disposeTemplate(templateData: ICommentThreadTemplateData): void {

View file

@ -68,6 +68,7 @@ export class CommentsPanel extends ViewPane {
let domContainer = dom.append(container, dom.$('.comments-panel-container'));
this.treeContainer = dom.append(domContainer, dom.$('.tree-container'));
this.treeContainer.classList.add('file-icon-themable-tree', 'show-file-icons');
this.commentsModel = new CommentsModel();
this.createTree();

View file

@ -20,31 +20,57 @@
visibility: hidden;
}
.comments-panel .comments-panel-container .tree-container .comment-thread-container {
display: block;
}
.comments-panel .comments-panel-container .tree-container .resource-container,
.comments-panel .comments-panel-container .tree-container .comment-container {
.comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-metadata-container,
.comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-snippet-container {
display: flex;
}
.comments-panel .count,
.comments-panel .user {
padding-right: 5px;
opacity: 0.5;
}
.comments-panel .comments-panel-container .tree-container .comment-container .text {
.comments-panel .comments-panel-container .tree-container .comment-thread-container .icon {
padding-top: 4px;
padding-right: 5px;
}
.comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-metadata-container .count,
.comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-snippet-container .text {
display: flex;
flex: 1;
min-width: 0;
}
.comments-panel .comments-panel-container .tree-container .comment-container .text * {
.comments-panel .comments-panel-container .tree-container .comment-thread-container .reply-detail,
.comments-panel .comments-panel-container .tree-container .comment-thread-container .timestamp {
display:flex;
font-size: 0.9em;
padding-right: 5px;
opacity: 0.8;
}
.comments-panel .comments-panel-container .tree-container .comment-thread-container .text * {
margin: 0;
text-overflow: ellipsis;
max-width: 500px;
overflow: hidden;
}
.comments-panel .comments-panel-container .tree-container .comment-container .text code {
.comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-snippet-container .text code {
font-family: var(--monaco-monospace-font);
}
.comments-panel .comments-panel-container .tree-container .comment-thread-container .separator {
padding-right: 5px;
opacity: 0.8;
}
.comments-panel .comments-panel-container .message-box-container {
line-height: 22px;
padding-left: 20px;
@ -55,7 +81,12 @@
margin-left: 10px;
}
.comments-panel .comments-panel-container .tree-container .comment-container {
.comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-metadata-container,
.comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-snippet-container {
line-height: 22px;
margin-right: 5px;
}
.comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-snippet-container {
padding-left: 16px;
}

View file

@ -18,6 +18,7 @@ export class TimestampWidget extends Disposable {
constructor(private configurationService: IConfigurationService, container: HTMLElement, timeStamp?: Date) {
super();
this._date = dom.append(container, dom.$('span.timestamp'));
this._date.style.display = 'none';
this._useRelativeTime = this.useRelativeTimeSetting;
this.setTimestamp(timeStamp);
}
@ -37,9 +38,10 @@ export class TimestampWidget extends Disposable {
private updateDate(timestamp?: Date) {
if (!timestamp) {
this._date.textContent = '';
this._date.style.display = 'none';
} else if ((timestamp !== this._timestamp)
|| (this.useRelativeTimeSetting !== this._useRelativeTime)) {
this._date.style.display = '';
let textContent: string;
let tooltip: string | undefined;
if (this.useRelativeTimeSetting) {

View file

@ -280,19 +280,28 @@ export const VIEW_CONTAINER: ViewContainer = viewContainerRegistry.registerViewC
},
}, ViewContainerLocation.Sidebar, { isDefault: true });
const openFolder = localize('openFolder', "Open Folder");
const addAFolder = localize('addAFolder', "add a folder");
const addRootFolderButton = `[${openFolder}](command:${AddRootFolderAction.ID})`;
const addAFolderButton = `[${addAFolder}](command:${AddRootFolderAction.ID})`;
const commandId = (isMacintosh && !isWeb) ? OpenFileFolderAction.ID : OpenFolderAction.ID;
const openFolderButton = `[${openFolder}](command:${commandId})`;
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
content: localize({ key: 'noWorkspaceHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
"You have not yet added a folder to the workspace.\n[Open Folder](command:{0})", AddRootFolderAction.ID),
"You have not yet added a folder to the workspace.\n{0}", addRootFolderButton),
when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), IsIOSContext.toNegated()),
group: ViewContentGroups.Open,
order: 1
});
const commandId = (isMacintosh && !isWeb) ? OpenFileFolderAction.ID : OpenFolderAction.ID;
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
content: localize({ key: 'remoteNoFolderHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
"Connected to remote.\n[Open Folder](command:{0})", commandId),
"Connected to remote.\n{0}", openFolderButton),
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.notEqualsTo(''), IsWebContext.toNegated()),
group: ViewContentGroups.Open,
order: 1
@ -300,7 +309,7 @@ viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
content: localize({ key: 'noFolderButEditorsHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
"You have not yet opened a folder.\n[Open Folder](command:{0})\nOpening a folder will close all currently open editors. To keep them open, [add a folder](command:{1}) instead.", commandId, AddRootFolderAction.ID),
"You have not yet opened a folder.\n{0}\nOpening a folder will close all currently open editors. To keep them open, {0} instead.", openFolderButton, addAFolderButton),
when: ContextKeyExpr.and(ContextKeyExpr.has('editorIsOpen'), ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext))),
group: ViewContentGroups.Open,
order: 1
@ -308,7 +317,7 @@ viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
content: localize({ key: 'noFolderHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
"You have not yet opened a folder.\n[Open Folder](command:{0})", commandId),
"You have not yet opened a folder.\n{0}", openFolderButton),
when: ContextKeyExpr.and(ContextKeyExpr.has('editorIsOpen')?.negate(), ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext))),
group: ViewContentGroups.Open,
order: 1

View file

@ -42,7 +42,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { isCancellationError } from 'vs/base/common/errors';
import { toAction } from 'vs/base/common/actions';
import { EditorResolution } from 'vs/platform/editor/common/editor';
import { EditorOpenContext, EditorResolution } from 'vs/platform/editor/common/editor';
import { hash } from 'vs/base/common/hash';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
@ -365,7 +365,7 @@ CommandsRegistry.registerCommand({
const uri = getResourceForCommand(resource, accessor.get(IListService), accessor.get(IEditorService));
if (uri) {
return editorService.openEditor({ resource: uri, options: { override: EditorResolution.PICK } });
return editorService.openEditor({ resource: uri, options: { override: EditorResolution.PICK, context: EditorOpenContext.USER } });
}
return undefined;

View file

@ -259,7 +259,7 @@ export class SettingsEditor2 extends EditorPane {
this.modelDisposables = this._register(new DisposableStore());
}
override get minimumWidth(): number { return 375; }
override get minimumWidth(): number { return SettingsEditor2.EDITOR_MIN_WIDTH; }
override get maximumWidth(): number { return Number.POSITIVE_INFINITY; }
// these setters need to exist because this extends from EditorPane

View file

@ -34,7 +34,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
private _cwd: string | undefined;
private _currentCommand: ICurrentPartialCommand = {};
private _isWindowsPty: boolean = false;
private _onDataListener?: IDisposable;
private _onCursorMoveListener?: IDisposable;
private _commandMarkers: IMarker[] = [];
get commands(): readonly ITerminalCommand[] { return this._commands; }
@ -72,7 +72,8 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
this._currentCommand.commandStartMarker = this._terminal.registerMarker(0);
// On Windows track all cursor movements after the command start sequence
if (this._isWindowsPty) {
this._onDataListener = this._terminal.onCursorMove(() => {
this._commandMarkers.length = 0;
this._onCursorMoveListener = this._terminal.onCursorMove(() => {
if (this._commandMarkers.length === 0 || this._commandMarkers[this._commandMarkers.length - 1].line !== this._terminal.buffer.active.cursorY) {
const marker = this._terminal.registerMarker(0);
if (marker) {
@ -87,13 +88,9 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
handleCommandExecuted(): void {
// On Windows, use the gathered cursor move markers to correct the command start and
// executed markers
if (this._isWindowsPty && this._commandMarkers.length > 0) {
this._commandMarkers = this._commandMarkers.sort((a, b) => a.line - b.line);
this._currentCommand.commandStartMarker = this._commandMarkers[0];
this._currentCommand.commandExecutedMarker = this._commandMarkers[this._commandMarkers.length - 1];
this._onDataListener?.dispose();
this._onDataListener = undefined;
this._commandMarkers.length = 0;
if (this._isWindowsPty) {
this._onCursorMoveListener?.dispose();
this._onCursorMoveListener = undefined;
}
this._currentCommand.commandExecutedMarker = this._terminal.registerMarker(0);
@ -126,6 +123,15 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
}
handleCommandFinished(exitCode: number | undefined): void {
// On Windows, use the gathered cursor move markers to correct the command start and
// executed markers. This is done on command finished just in case command executed never
// happens (for example PSReadLine tab completion)
if (this._isWindowsPty) {
this._commandMarkers = this._commandMarkers.sort((a, b) => a.line - b.line);
this._currentCommand.commandStartMarker = this._commandMarkers[0];
this._currentCommand.commandExecutedMarker = this._commandMarkers[this._commandMarkers.length - 1];
}
this._currentCommand.commandFinishedMarker = this._terminal.registerMarker(0);
const command = this._currentCommand.command;
this._logService.debug('CommandDetectionCapability#handleCommandFinished', this._terminal.buffer.active.cursorX, this._currentCommand.commandFinishedMarker?.line, this._currentCommand.command, this._currentCommand);
@ -145,7 +151,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
timestamp,
cwd: this._cwd,
exitCode: this._exitCode,
hasOutput: (this._currentCommand.commandExecutedMarker!.line < this._currentCommand.commandFinishedMarker!.line),
hasOutput: !!(this._currentCommand.commandExecutedMarker && this._currentCommand.commandFinishedMarker && this._currentCommand.commandExecutedMarker?.line < this._currentCommand.commandFinishedMarker!.line),
getOutput: () => getOutputForCommand(clonedPartialCommand, buffer)
};
this._commands.push(newCommand);

View file

@ -15,6 +15,8 @@ else
fi
IN_COMMAND_EXECUTION="1"
LAST_HISTORY_ID=$(history | tail -n1 | awk '{print $1;}')
prompt_start() {
printf "\033]633;A\007"
}
@ -32,7 +34,14 @@ command_output_start() {
}
command_complete() {
printf "\033]633;D;%s\007" "$STATUS"
local HISTORY_ID=$(history | tail -n1 | awk '{print $1;}')
if [[ "$HISTORY_ID" == "$LAST_HISTORY_ID" ]]; then
printf "\033]633;D\007"
else
printf "\033]633;D;%s\007" "$STATUS"
LAST_HISTORY_ID=$HISTORY_ID
fi
update_cwd
}

View file

@ -21,6 +21,7 @@ function Global:__VSCode-Get-LastExitCode {
}
function Global:Prompt() {
$LastExitCode = $(__VSCode-Get-LastExitCode);
$LastHistoryEntry = $(Get-History -Count 1)
if ($LastHistoryEntry.Id -eq $Global:__LastHistoryId) {
# Don't provide a command line or exit code if there was no history entry (eg. ctrl+c, enter on no command)
@ -38,7 +39,7 @@ function Global:Prompt() {
$Result += "`a"
# Command finished exit code
# OSC 633 ; D [; <ExitCode>] ST
$Result += "`e]633;D;$(__VSCode-Get-LastExitCode)`a"
$Result += "`e]633;D;$LastExitCode`a"
}
# Prompt started
# OSC 633 ; A ST

View file

@ -413,6 +413,9 @@
top: 50%;
}
.terminal-command-decoration:hover {
.terminal-command-decoration:not(.skipped):hover {
cursor: pointer;
}
.terminal-command-decoration.skipped {
pointer-events: none;
}

View file

@ -1638,16 +1638,23 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
@debounce(2000)
private async _updateProcessCwd(): Promise<string> {
private async _updateProcessCwd(): Promise<void> {
if (this._isDisposed) {
return this.cwd || this._initialCwd || '';
return;
}
// reset cwd if it has changed, so file based url paths can be resolved
const cwd = await this.refreshProperty(ProcessPropertyType.Cwd);
if (typeof cwd !== 'string') {
throw new Error('cwd is not a string');
try {
const cwd = await this.refreshProperty(ProcessPropertyType.Cwd);
if (typeof cwd !== 'string') {
throw new Error('cwd is not a string');
}
} catch (e: unknown) {
// Swallow this as it means the process has been killed
if (e instanceof Error && e.message === 'Cannot refresh property when process is not set') {
return;
}
throw e;
}
return cwd;
}
updateConfig(): void {

View file

@ -597,7 +597,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
async refreshProperty<T extends ProcessPropertyType>(type: T): Promise<IProcessPropertyMap[T]> {
if (!this._process) {
throw new Error('Cannot refresh property when process is undefined');
throw new Error('Cannot refresh property when process is not set');
}
return this._process.refreshProperty(type);
}

View file

@ -10,7 +10,7 @@ import * as dom from 'vs/base/browser/dom';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ITerminalCapabilityStore, TerminalCapability } from 'vs/workbench/contrib/terminal/common/capabilities/capabilities';
import { IColorTheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { TERMINAL_COMMAND_DECORATION_DEFAULT_BACKGROUND_COLOR, TERMINAL_COMMAND_DECORATION_ERROR_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { TERMINAL_COMMAND_DECORATION_DEFAULT_BACKGROUND_COLOR, TERMINAL_COMMAND_DECORATION_ERROR_BACKGROUND_COLOR, TERMINAL_COMMAND_DECORATION_SKIPPED_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IHoverService } from 'vs/workbench/services/hover/browser/hover';
import { IAction } from 'vs/base/common/actions';
@ -26,12 +26,13 @@ import { TerminalSettingId } from 'vs/platform/terminal/common/terminal';
const enum DecorationSelector {
CommandDecoration = 'terminal-command-decoration',
ErrorColor = 'error',
SkippedColor = 'skipped',
Codicon = 'codicon',
}
const enum DecorationStyles { ButtonMargin = 4 }
interface IDisposableDecoration { decoration: IDecoration; diposables: IDisposable[] }
interface IDisposableDecoration { decoration: IDecoration; disposables: IDisposable[] }
export class DecorationAddon extends Disposable implements ITerminalAddon {
protected _terminal: Terminal | undefined;
@ -105,7 +106,7 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
if (!command.marker) {
throw new Error(`cannot add a decoration for a command ${JSON.stringify(command)} with no marker`);
}
if (!this._terminal || command.command.trim().length === 0) {
if (!this._terminal) {
return undefined;
}
@ -116,14 +117,19 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
decoration.onRender(target => {
if (decoration.element && !this._decorations.get(decoration.marker.id)) {
this._decorations.set(decoration.marker.id, { decoration, diposables: [this._createContextMenu(decoration.element, command), ...this._createHover(decoration.element, command)] });
const disposables = command.exitCode === undefined ? [] : [this._createContextMenu(decoration.element, command), ...this._createHover(decoration.element, command)];
this._decorations.set(decoration.marker.id, { decoration, disposables });
}
if (decoration.element?.clientWidth! > 0) {
const marginWidth = ((decoration.element?.parentElement?.parentElement?.previousElementSibling?.clientWidth || 0) - (decoration.element?.parentElement?.parentElement?.clientWidth || 0)) * .5;
target.style.marginLeft = `${((marginWidth - (decoration.element!.clientWidth + DecorationStyles.ButtonMargin)) * .5) - marginWidth}px`;
target.classList.add(DecorationSelector.CommandDecoration);
target.classList.add(DecorationSelector.Codicon);
if (command.exitCode) {
if (command.exitCode === undefined) {
target.classList.add(DecorationSelector.SkippedColor);
// TODO: Use outline icon?
target.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.CommandIcon)}`);
} else if (command.exitCode) {
target.classList.add(DecorationSelector.ErrorColor);
target.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.CommandIconError)}`);
} else {
@ -147,18 +153,26 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
private _createHover(target: HTMLElement, command: ITerminalCommand): IDisposable[] {
return [
dom.addDisposableListener(target, dom.EventType.MOUSE_ENTER, async () => {
dom.addDisposableListener(target, dom.EventType.MOUSE_ENTER, () => {
if (this._contextMenuVisible) {
return;
}
let hoverContent = `${localize('terminal-prompt-context-menu', "Show Actions")}` + ` ...${fromNow(command.timestamp, true)}`;
if (command.exitCode) {
hoverContent += `\n\n\n\nExit Code: ${command.exitCode} `;
}
await this._hoverDelayer.trigger(() => { this._hoverService.showHover({ content: new MarkdownString(hoverContent), target }); });
this._hoverDelayer.trigger(() => {
let hoverContent = `${localize('terminal-prompt-context-menu', "Show Actions")}...\n\n---\n\n- ${localize('terminal-prompt-command-executed-time', 'Executed: {0}', fromNow(command.timestamp, true))}`;
if (command.exitCode) {
hoverContent += `\n- ${command.exitCode === -1 ? localize('terminal-prompt-command-failed', 'Failed') : localize('terminal-prompt-command-exit-code', 'Exit code: {0}', command.exitCode)}`;
}
this._hoverService.showHover({ content: new MarkdownString(hoverContent), target });
});
}),
dom.addDisposableListener(target, dom.EventType.MOUSE_LEAVE, () => this._hoverService.hideHover()),
dom.addDisposableListener(target, dom.EventType.MOUSE_OUT, () => this._hoverService.hideHover())];
dom.addDisposableListener(target, dom.EventType.MOUSE_LEAVE, () => this._hideHover()),
dom.addDisposableListener(target, dom.EventType.MOUSE_OUT, () => this._hideHover())
];
}
private _hideHover() {
this._hoverDelayer.cancel();
this._hoverService.hideHover();
}
private async _getCommandActions(command: ITerminalCommand): Promise<IAction[]> {
@ -182,6 +196,8 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
collector.addRule(`.${DecorationSelector.CommandDecoration} { color: ${commandDecorationDefaultColor ? commandDecorationDefaultColor.toString() : ''}; } `);
const commandDecorationErrorColor = theme.getColor(TERMINAL_COMMAND_DECORATION_ERROR_BACKGROUND_COLOR);
collector.addRule(`.${DecorationSelector.CommandDecoration}.${DecorationSelector.ErrorColor} { color: ${commandDecorationErrorColor ? commandDecorationErrorColor.toString() : ''}; } `);
const commandDecorationSkippedColor = theme.getColor(TERMINAL_COMMAND_DECORATION_SKIPPED_BACKGROUND_COLOR);
collector.addRule(`.${DecorationSelector.CommandDecoration}.${DecorationSelector.SkippedColor} { color: ${commandDecorationSkippedColor ? commandDecorationSkippedColor.toString() : ''}; } `);
const toolbarHoverBackgroundColor = theme.getColor(toolbarHoverBackground);
collector.addRule(`.${DecorationSelector.CommandDecoration}:hover { background-color: ${toolbarHoverBackgroundColor ? toolbarHoverBackgroundColor.toString() : ''}; }`);
collector.addRule(`.${DecorationSelector.CommandDecoration}:not(.${DecorationSelector.SkippedColor}):hover { background-color: ${toolbarHoverBackgroundColor ? toolbarHoverBackgroundColor.toString() : ''}; }`);
});

View file

@ -31,12 +31,17 @@ export const TERMINAL_COMMAND_DECORATION_DEFAULT_BACKGROUND_COLOR = registerColo
light: '#66afe0',
dark: '#399ee6',
hc: '#399ee6'
}, nls.localize('terminalCommandDecoration.defaultBackground', 'The default terminal command decoration background color.'));
}, nls.localize('terminalCommandDecoration.defaultBackground', 'The default terminal command decoration background color for successful commands (zero exit code).'));
export const TERMINAL_COMMAND_DECORATION_ERROR_BACKGROUND_COLOR = registerColor('terminalCommandDecoration.errorBackground', {
light: '#a1260d',
dark: '#be1100',
hc: '#be1100'
}, nls.localize('terminalCommandDecoration.errorBackground', 'The terminal command decoration background color when there is an exit code.'));
}, nls.localize('terminalCommandDecoration.errorBackground', 'The terminal command decoration background color when the command fails (non-zero exit code).'));
export const TERMINAL_COMMAND_DECORATION_SKIPPED_BACKGROUND_COLOR = registerColor('terminalCommandDecoration.skippedBackground', {
light: '#00000040',
dark: '#ffffff40',
hc: '#ffffff80'
}, nls.localize('terminalCommandDecoration.skippedBackground', 'The terminal command decoration background color when the command was skipped (undefined exit code).'));
export const TERMINAL_BORDER_COLOR = registerColor('terminal.border', {
dark: PANEL_BORDER,
light: PANEL_BORDER,

View file

@ -14,7 +14,7 @@ import { IComputedStateAccessor, refreshComputedState } from 'vs/workbench/contr
import { IObservableValue, MutableObservableValue, staticObservableValue } from 'vs/workbench/contrib/testing/common/observableValue';
import { IRichLocation, ISerializedTestResults, ITestItem, ITestMessage, ITestOutputMessage, ITestRunTask, ITestTaskState, ResolvedTestRunRequest, TestItemExpandState, TestMessageType, TestResultItem, TestResultState } from 'vs/workbench/contrib/testing/common/testCollection';
import { TestCoverage } from 'vs/workbench/contrib/testing/common/testCoverage';
import { maxPriority, statesInOrder } from 'vs/workbench/contrib/testing/common/testingStates';
import { maxPriority, statesInOrder, terminalStatePriorities } from 'vs/workbench/contrib/testing/common/testingStates';
export interface ITestRunTaskResults extends ITestRunTask {
/**
@ -379,6 +379,17 @@ export class LiveTestResult implements ITestResult {
}
const index = this.mustGetTaskIndex(taskId);
const oldTerminalStatePrio = terminalStatePriorities[entry.tasks[index].state];
const newTerminalStatePrio = terminalStatePriorities[state];
// Ignore requests to set the state from one terminal state back to a
// "lower" one, e.g. from failed back to passed:
if (oldTerminalStatePrio !== undefined &&
(newTerminalStatePrio === undefined || newTerminalStatePrio < oldTerminalStatePrio)) {
return;
}
this.fireUpdateAndRefresh(entry, index, state, duration);
}

View file

@ -59,3 +59,15 @@ export const maxPriority = (...states: TestResultState[]) => {
export const statesInOrder = Object.keys(statePriority).map(s => Number(s) as TestResultState).sort(cmpPriority);
export const isRunningState = (s: TestResultState) => s === TestResultState.Queued || s === TestResultState.Running;
/**
* Some states are considered terminal; once these are set for a given test run, they
* are not reset back to a non-terminal state, or to a terminal state with lower
* priority.
*/
export const terminalStatePriorities: { [key in TestResultState]?: number } = {
[TestResultState.Passed]: 0,
[TestResultState.Skipped]: 1,
[TestResultState.Failed]: 2,
[TestResultState.Errored]: 3,
};

View file

@ -144,14 +144,15 @@ suite('Workbench - Test Results Service', () => {
test('updateState', () => {
changed.clear();
r.updateState(new TestId(['ctrlId', 'id-a', 'id-aa']).toString(), 't', TestResultState.Running);
const testId = new TestId(['ctrlId', 'id-a', 'id-aa']).toString();
r.updateState(testId, 't', TestResultState.Running);
assert.deepStrictEqual(r.counts, {
...makeEmptyCounts(),
[TestResultState.Unset]: 2,
[TestResultState.Running]: 1,
[TestResultState.Queued]: 1,
});
assert.deepStrictEqual(r.getStateById(new TestId(['ctrlId', 'id-a', 'id-aa']).toString())?.ownComputedState, TestResultState.Running);
assert.deepStrictEqual(r.getStateById(testId)?.ownComputedState, TestResultState.Running);
// update computed state:
assert.deepStrictEqual(r.getStateById(tests.root.id)?.computedState, TestResultState.Running);
assert.deepStrictEqual(getChangeSummary(), [
@ -159,6 +160,15 @@ suite('Workbench - Test Results Service', () => {
{ label: 'aa', reason: TestResultItemChangeReason.OwnStateChange },
{ label: 'root', reason: TestResultItemChangeReason.ComputedStateChange },
]);
r.updateState(testId, 't', TestResultState.Passed);
assert.deepStrictEqual(r.getStateById(testId)?.ownComputedState, TestResultState.Passed);
r.updateState(testId, 't', TestResultState.Errored);
assert.deepStrictEqual(r.getStateById(testId)?.ownComputedState, TestResultState.Errored);
r.updateState(testId, 't', TestResultState.Passed);
assert.deepStrictEqual(r.getStateById(testId)?.ownComputedState, TestResultState.Errored);
});
test('retire', () => {

View file

@ -869,7 +869,7 @@ onDomReady(() => {
}
if (!options.allowScripts && isSafari) {
// On Safari for iframes with scripts disabled, the `DOMContentLoaded` never seems to be fired.
// On Safari for iframes with scripts disabled, the `DOMContentLoaded` never seems to be fired: https://bugs.webkit.org/show_bug.cgi?id=33604
// Use polling instead.
const interval = setInterval(() => {
// If the frame is no longer mounted, loading has stopped
@ -881,7 +881,8 @@ onDomReady(() => {
const contentDocument = assertIsDefined(newFrame.contentDocument);
if (contentDocument.readyState !== 'loading') {
clearInterval(interval);
onFrameLoaded(contentDocument);
// Workaround for https://bugs.webkit.org/show_bug.cgi?id=236624
setTimeout(() => onFrameLoaded(contentDocument), 50);
}
}, 10);
} else {

View file

@ -182,7 +182,7 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi
const webviewExternalEndpointCommit = this.payload?.get('webviewExternalEndpointCommit');
return endpoint
.replace('{{commit}}', webviewExternalEndpointCommit ?? this.productService.commit ?? 'd372f9187401bd145a0a6e15ba369e2d82d02005')
.replace('{{commit}}', webviewExternalEndpointCommit ?? this.productService.commit ?? '50089c3f92c17584a4aca179f51f220b56c22020')
.replace('{{quality}}', (webviewExternalEndpointCommit ? 'insider' : this.productService.quality) ?? 'insider');
}

View file

@ -208,11 +208,6 @@ export class LocalProcessExtensionHost implements IExtensionHost {
this._extensionHostProcess = new ExtensionHostProcess(extensionHostCreationResult.id, this._extensionHostStarter);
let lang = processEnv['LANG'];
if (platform.isMacintosh && lang === undefined) {
lang = Intl.DateTimeFormat().resolvedOptions().locale;
}
const env = objects.mixin(processEnv, {
VSCODE_AMD_ENTRYPOINT: 'vs/workbench/api/node/extensionHostProcess',
VSCODE_PIPE_LOGGING: 'true',
@ -220,8 +215,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
VSCODE_LOG_NATIVE: this._isExtensionDevHost,
VSCODE_IPC_HOOK_EXTHOST: pipeName,
VSCODE_HANDLES_UNCAUGHT_ERRORS: true,
VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || this._productService.quality !== 'stable' || this._environmentService.verbose),
'LANG': lang
VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || this._productService.quality !== 'stable' || this._environmentService.verbose)
});
if (this._environmentService.debugExtensionHost.env) {

View file

@ -282,7 +282,7 @@ async function launchBrowser(options: LaunchOptions, endpoint: string) {
}
});
const payloadParam = `[["enableProposedApi",""],["webviewExternalEndpointCommit","d372f9187401bd145a0a6e15ba369e2d82d02005"],["skipWelcome","true"]]`;
const payloadParam = `[["enableProposedApi",""],["webviewExternalEndpointCommit","50089c3f92c17584a4aca179f51f220b56c22020"],["skipWelcome","true"]]`;
await measureAndLog(page.goto(`${endpoint}&folder=${URI.file(workspacePath!).path}&payload=${payloadParam}`), 'page.goto()', logger);
return { browser, context, page };

View file

@ -65,7 +65,7 @@ async function runTestsInBrowser(browserType: BrowserType, endpoint: url.UrlWith
const testExtensionUri = url.format({ pathname: URI.file(path.resolve(optimist.argv.extensionDevelopmentPath)).path, protocol, host, slashes: true });
const testFilesUri = url.format({ pathname: URI.file(path.resolve(optimist.argv.extensionTestsPath)).path, protocol, host, slashes: true });
const payloadParam = `[["extensionDevelopmentPath","${testExtensionUri}"],["extensionTestsPath","${testFilesUri}"],["enableProposedApi",""],["webviewExternalEndpointCommit","d372f9187401bd145a0a6e15ba369e2d82d02005"],["skipWelcome","true"]]`;
const payloadParam = `[["extensionDevelopmentPath","${testExtensionUri}"],["extensionTestsPath","${testFilesUri}"],["enableProposedApi",""],["webviewExternalEndpointCommit","50089c3f92c17584a4aca179f51f220b56c22020"],["skipWelcome","true"]]`;
if (path.extname(testWorkspacePath) === '.code-workspace') {
await page.goto(`${endpoint.href}&workspace=${testWorkspacePath}&payload=${payloadParam}`);

View file

@ -8193,10 +8193,10 @@ native-is-elevated@0.4.3:
resolved "https://registry.yarnpkg.com/native-is-elevated/-/native-is-elevated-0.4.3.tgz#f1071c4a821acc71d43f36ff8051d3816d832e1c"
integrity sha512-bHS3sCoh+raqFGIxmL/plER3eBQ+IEBy4RH/4uahhToZneTvqNKQrL0PgOTtnpL55XjBd3dy0pNtZMkCk0J48g==
native-keymap@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-3.1.0.tgz#4ad8350f9253d0ae13c77f37d4a34ab4773e305f"
integrity sha512-h67B9n/L0DRGAftZqO7ZTlgGy7UY8r7N/GOAnnDibmRqFoenjbd9IdkN9uCf1Xs+vGW4BDJn9ZtO6xwJAbDmEA==
native-keymap@3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-3.2.1.tgz#2b34ee7e08722f107baba9beae7e61ea43eceae7"
integrity sha512-kR8r1Ody16qNE52fenuCMQBHtFpIV7HNVjQA7dm/pXpFcQmqCl8jWQuJYdZvNH7fQmyAv3lOgPfR3FDHPRYiiA==
native-watchdog@1.3.0:
version "1.3.0"