Merge branch 'main' into merogge/diff

This commit is contained in:
meganrogge
2023-09-21 11:05:15 -07:00
8 changed files with 284 additions and 83 deletions

View File

@@ -7,22 +7,22 @@ import * as nls from 'vs/nls';
export namespace AccessibilityHelpNLS {
export const accessibilityHelpTitle = nls.localize('accessibilityHelpTitle', "Accessibility Help");
export const openingDocs = nls.localize("openingDocs", "Now opening the Accessibility documentation page.");
export const openingDocs = nls.localize("openingDocs", "Opening the Accessibility documentation page.");
export const readonlyDiffEditor = nls.localize("readonlyDiffEditor", "You are in a read-only pane of a diff editor.");
export const editableDiffEditor = nls.localize("editableDiffEditor", "You are in a pane of a diff editor.");
export const readonlyEditor = nls.localize("readonlyEditor", "You are in a read-only code editor.");
export const editableEditor = nls.localize("editableEditor", "You are in a code editor.");
export const changeConfigToOnMac = nls.localize("changeConfigToOnMac", "To configure the application to be optimized for usage with a Screen Reader press Command+E now.");
export const changeConfigToOnWinLinux = nls.localize("changeConfigToOnWinLinux", "To configure the application to be optimized for usage with a Screen Reader press Control+E now.");
export const changeConfigToOnMac = nls.localize("changeConfigToOnMac", "Configure the application to be optimized for usage with a Screen Reader (Command+E).");
export const changeConfigToOnWinLinux = nls.localize("changeConfigToOnWinLinux", "Configure the application to be optimized for usage with a Screen Reader (Control+E).");
export const auto_on = nls.localize("auto_on", "The application is configured to be optimized for usage with a Screen Reader.");
export const auto_off = nls.localize("auto_off", "The application is configured to never be optimized for usage with a Screen Reader.");
export const screenReaderModeEnabled = nls.localize("screenReaderModeEnabled", "Screen Reader Optimized Mode enabled.");
export const screenReaderModeDisabled = nls.localize("screenReaderModeDisabled", "Screen Reader Optimized Mode disabled.");
export const tabFocusModeOnMsg = nls.localize("tabFocusModeOnMsg", "Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}.");
export const tabFocusModeOnMsg = nls.localize("tabFocusModeOnMsg", "Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior {0}.");
export const tabFocusModeOnMsgNoKb = nls.localize("tabFocusModeOnMsgNoKb", "Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding.");
export const stickScrollKb = nls.localize("stickScrollKb", "Run the command: Focus Sticky Scroll ({0}) to focus the currently nested scopes.");
export const stickScrollNoKb = nls.localize("stickScrollNoKb", "Run the command: Focus Sticky Scroll to focus the currently nested scopes. It is currently not triggerable by a keybinding.");
export const tabFocusModeOffMsg = nls.localize("tabFocusModeOffMsg", "Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}.");
export const tabFocusModeOffMsg = nls.localize("tabFocusModeOffMsg", "Pressing Tab in the current editor will insert the tab character. Toggle this behavior {0}.");
export const tabFocusModeOffMsgNoKb = nls.localize("tabFocusModeOffMsgNoKb", "Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding.");
export const showAccessibilityHelpAction = nls.localize("showAccessibilityHelpAction", "Show Accessibility Help");
}

View File

@@ -102,11 +102,14 @@ class AccessibilityHelpProvider implements IAccessibleContentProvider {
const editorContext = this._contextKeyService.getContext(this._editor.getDomNode()!);
if (editorContext.getValue<boolean>(CommentContextKeys.activeEditorHasCommentingRange.key)) {
content.push(this._descriptionForCommand(CommentCommandId.Add, CommentAccessibilityHelpNLS.addComment, CommentAccessibilityHelpNLS.addCommentNoKb));
content.push(this._descriptionForCommand(CommentCommandId.NextThread, CommentAccessibilityHelpNLS.nextCommentThreadKb, CommentAccessibilityHelpNLS.nextCommentThreadNoKb));
content.push(this._descriptionForCommand(CommentCommandId.PreviousThread, CommentAccessibilityHelpNLS.previousCommentThreadKb, CommentAccessibilityHelpNLS.previousCommentThreadNoKb));
content.push(this._descriptionForCommand(CommentCommandId.NextRange, CommentAccessibilityHelpNLS.nextRange, CommentAccessibilityHelpNLS.nextRangeNoKb));
content.push(this._descriptionForCommand(CommentCommandId.PreviousRange, CommentAccessibilityHelpNLS.previousRange, CommentAccessibilityHelpNLS.previousRangeNoKb));
const commentCommandInfo = [];
commentCommandInfo.push(CommentAccessibilityHelpNLS.intro);
commentCommandInfo.push(this._descriptionForCommand(CommentCommandId.Add, CommentAccessibilityHelpNLS.addComment, CommentAccessibilityHelpNLS.addCommentNoKb));
commentCommandInfo.push(this._descriptionForCommand(CommentCommandId.NextThread, CommentAccessibilityHelpNLS.nextCommentThreadKb, CommentAccessibilityHelpNLS.nextCommentThreadNoKb));
commentCommandInfo.push(this._descriptionForCommand(CommentCommandId.PreviousThread, CommentAccessibilityHelpNLS.previousCommentThreadKb, CommentAccessibilityHelpNLS.previousCommentThreadNoKb));
commentCommandInfo.push(this._descriptionForCommand(CommentCommandId.NextRange, CommentAccessibilityHelpNLS.nextRange, CommentAccessibilityHelpNLS.nextRangeNoKb));
commentCommandInfo.push(this._descriptionForCommand(CommentCommandId.PreviousRange, CommentAccessibilityHelpNLS.previousRange, CommentAccessibilityHelpNLS.previousRangeNoKb));
content.push(commentCommandInfo.join('\n'));
}
if (options.get(EditorOption.stickyScroll).enabled) {

View File

@@ -363,7 +363,7 @@ export class AccessibleView extends Disposable {
this._accessibleViewCurrentProviderId.set(provider.verbositySettingKey.replaceAll('accessibility.verbosity.', ''));
}
const value = this._configurationService.getValue(provider.verbositySettingKey);
const readMoreLink = provider.options.readMoreUrl ? localize("openDoc", "\n\nPress H now to open a browser window with more information related to accessibility.\n\n") : '';
const readMoreLink = provider.options.readMoreUrl ? localize("openDoc", "\n\nOpen a browser window with more information related to accessibility (H).") : '';
let disableHelpHint = '';
if (provider.options.type === AccessibleViewType.Help && !!value) {
disableHelpHint = this._getDisableVerbosityHint(provider.verbositySettingKey);
@@ -384,7 +384,8 @@ export class AccessibleView extends Disposable {
message += '\n';
}
}
this._currentContent = message + provider.provideContent() + readMoreLink + disableHelpHint;
const exitThisDialogHint = localize('exit', '\n\nExit this dialog (Escape).');
this._currentContent = message + provider.provideContent() + readMoreLink + disableHelpHint + exitThisDialogHint;
this._updateContextKeys(provider, true);
this._getTextModel(URI.from({ path: `accessible-view-${provider.verbositySettingKey}`, scheme: 'accessible-view', fragment: this._currentContent })).then((model) => {
@@ -402,7 +403,7 @@ export class AccessibleView extends Disposable {
const verbose = this._configurationService.getValue(provider.verbositySettingKey);
const hasActions = this._accessibleViewSupportsNavigation.get() || this._accessibleViewVerbosityEnabled.get() || this._accessibleViewGoToSymbolSupported.get() || this._currentProvider?.actions;
if (verbose && !showAccessibleViewHelp && hasActions) {
actionsHint = localize('ariaAccessibleViewActions', "Use Shift+Tab to explore actions such as disabling this hint.");
actionsHint = localize('ariaAccessibleViewActions', 'Explore actions such as disabling this hint (Shift+Tab).');
}
let ariaLabel = provider.options.type === AccessibleViewType.Help ? localize('accessibility-help', "Accessibility Help") : localize('accessible-view', "Accessible View");
this._title.textContent = ariaLabel;
@@ -520,7 +521,7 @@ export class AccessibleView extends Disposable {
private _getAccessibleViewHelpDialogContent(providerHasSymbols?: boolean): string {
const navigationHint = this._getNavigationHint();
const goToSymbolHint = this._getGoToSymbolHint(providerHasSymbols);
const toolbarHint = localize('toolbar', "Navigate to the toolbar (Shift+Tab))");
const toolbarHint = localize('toolbar', "Navigate to the toolbar (Shift+Tab)).");
let hint = localize('intro', "In the accessible view, you can:\n");
if (navigationHint) {
@@ -540,9 +541,9 @@ export class AccessibleView extends Disposable {
const nextKeybinding = this._keybindingService.lookupKeybinding(AccessibilityCommandId.ShowNext)?.getAriaLabel();
const previousKeybinding = this._keybindingService.lookupKeybinding(AccessibilityCommandId.ShowPrevious)?.getAriaLabel();
if (nextKeybinding && previousKeybinding) {
hint = localize('accessibleViewNextPreviousHint', "Show the next ({0}) or previous ({1}) item", nextKeybinding, previousKeybinding);
hint = localize('accessibleViewNextPreviousHint', "Show the next ({0}) or previous ({1}) item.", nextKeybinding, previousKeybinding);
} else {
hint = localize('chatAccessibleViewNextPreviousHintNoKb', "Show the next or previous item by configuring keybindings for the Show Next & Previous in Accessible View commands");
hint = localize('chatAccessibleViewNextPreviousHintNoKb', "Show the next or previous item by configuring keybindings for the Show Next & Previous in Accessible View commands.");
}
return hint;
}
@@ -553,9 +554,9 @@ export class AccessibleView extends Disposable {
let hint = '';
const disableKeybinding = this._keybindingService.lookupKeybinding(AccessibilityCommandId.DisableVerbosityHint, this._contextKeyService)?.getAriaLabel();
if (disableKeybinding) {
hint = localize('acessibleViewDisableHint', "Disable accessibility verbosity for this feature ({0}). This will disable the hint to open the accessible view for example.\n", disableKeybinding);
hint = localize('acessibleViewDisableHint', "\n\nDisable accessibility verbosity for this feature ({0}).", disableKeybinding);
} else {
hint = localize('accessibleViewDisableHintNoKb', "Add a keybinding for the command Disable Accessible View Hint, which disables accessibility verbosity for this feature.\n");
hint = localize('accessibleViewDisableHintNoKb', "\n\nAdd a keybinding for the command Disable Accessible View Hint, which disables accessibility verbosity for this feature.s");
}
return hint;
}

View File

@@ -68,19 +68,21 @@ registerSingleton(ICommentService, CommentService, InstantiationType.Delayed);
export namespace CommentAccessibilityHelpNLS {
export const escape = nls.localize('escape', "Dismiss the comment widget via Escape.");
export const nextRange = nls.localize('next', "Navigate to the next commenting range via ({0}).");
export const nextRangeNoKb = nls.localize('nextNoKb', "Run the command: Go to Next Commenting Range, which is currently not triggerable via keybinding.");
export const previousRange = nls.localize('previous', "Navigate to the previous comment range via ({0}).");
export const intro = nls.localize('intro', "The editor contains a commentable range. Some useful commands include:");
export const introWidget = nls.localize('introWidget', "Some useful comment commands include:");
export const escape = nls.localize('escape', "- Dismiss Comment (Escape)");
export const nextRange = nls.localize('next', "- Navigate to the next commenting range ({0})");
export const nextRangeNoKb = nls.localize('nextNoKb', "- Go to Next Commenting Range, which is currently not triggerable via keybinding.");
export const previousRange = nls.localize('previous', "- Navigate to the previous commenting range ({0})");
export const previousRangeNoKb = nls.localize('previousNoKb', "Run the command: Go to Previous Commenting Range, which is currently not triggerable via keybinding.");
export const nextCommentThreadKb = nls.localize('nextCommentThreadKb', "Navigate to the next comment thread via ({0}).");
export const nextCommentThreadNoKb = nls.localize('nextCommentThreadNoKb', "Run the command: Go to Next Comment Thread, which is currently not triggerable via keybinding.");
export const previousCommentThreadKb = nls.localize('previousCommentThreadKb', "Navigate to the previous comment thread via ({0}).");
export const previousCommentThreadNoKb = nls.localize('previousCommentThreadNoKb', "Run the command: Go to Previous Comment Thread, which is currently not triggerable via keybinding.");
export const addComment = nls.localize('addComment', "Add a comment via ({0}).");
export const addCommentNoKb = nls.localize('addCommentNoKb', "Add a comment via the command: Add Comment on Current Selection, which is currently not triggerable via keybinding.");
export const submitComment = nls.localize('submitComment', "Submit the comment via ({0}).");
export const submitCommentNoKb = nls.localize('submitCommentNoKb', "Submit the comment by navigating with tab to the button, as it's currently not triggerable via keybinding.");
export const nextCommentThreadKb = nls.localize('nextCommentThreadKb', "- Navigate to the next comment thread ({0})");
export const nextCommentThreadNoKb = nls.localize('nextCommentThreadNoKb', "- Run the command: Go to Next Comment Thread, which is currently not triggerable via keybinding.");
export const previousCommentThreadKb = nls.localize('previousCommentThreadKb', "- Navigate to the previous comment thread ({0})");
export const previousCommentThreadNoKb = nls.localize('previousCommentThreadNoKb', "- Run the command: Go to Previous Comment Thread, which is currently not triggerable via keybinding.");
export const addComment = nls.localize('addComment', "- Add Comment ({0})");
export const addCommentNoKb = nls.localize('addCommentNoKb', "- Add Comment on Current Selection, which is currently not triggerable via keybinding.");
export const submitComment = nls.localize('submitComment', "- Submit Comment ({0})");
export const submitCommentNoKb = nls.localize('submitCommentNoKb', "- Submit Comment, accessible via tabbing, as it's currently not triggerable with a keybinding.");
}
export class CommentsAccessibilityHelpContribution extends Disposable {
@@ -114,12 +116,13 @@ export class CommentsAccessibilityHelpProvider implements IAccessibleContentProv
provideContent(): string {
this._element = document.activeElement as HTMLElement;
const content: string[] = [];
content.push(CommentAccessibilityHelpNLS.introWidget);
content.push(CommentAccessibilityHelpNLS.escape);
content.push(this._descriptionForCommand(CommentCommandId.Add, CommentAccessibilityHelpNLS.addComment, CommentAccessibilityHelpNLS.addCommentNoKb));
content.push(this._descriptionForCommand(CommentCommandId.Submit, CommentAccessibilityHelpNLS.submitComment, CommentAccessibilityHelpNLS.submitCommentNoKb));
content.push(this._descriptionForCommand(CommentCommandId.NextRange, CommentAccessibilityHelpNLS.nextRange, CommentAccessibilityHelpNLS.nextRangeNoKb));
content.push(this._descriptionForCommand(CommentCommandId.PreviousRange, CommentAccessibilityHelpNLS.previousRange, CommentAccessibilityHelpNLS.previousRangeNoKb));
content.push(this._descriptionForCommand(CommentCommandId.Submit, CommentAccessibilityHelpNLS.submitComment, CommentAccessibilityHelpNLS.submitCommentNoKb));
return content.join('\n\n');
return content.join('\n');
}
onClose(): void {
this._element?.focus();

View File

@@ -6,7 +6,6 @@
import * as assert from 'assert';
import { Event } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { isWeb } from 'vs/base/common/platform';
import { mock } from 'vs/base/test/common/mock';
import { assertSnapshot } from 'vs/base/test/common/snapshot';
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
@@ -20,8 +19,7 @@ import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { createNotebookCellList, setupInstantiationService, withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor';
import { OutlineTarget } from 'vs/workbench/services/outline/browser/outline';
(isWeb ? suite.skip : suite)('NotebookEditorStickyScroll', () => {
suite('NotebookEditorStickyScroll', () => {
let disposables: DisposableStore;
let instantiationService: TestInstantiationService;

View File

@@ -14,23 +14,23 @@ import { ThemeIcon } from 'vs/base/common/themables';
import { URI } from 'vs/base/common/uri';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IOpenEvent, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
import { IOpenEvent, WorkbenchCompressibleAsyncDataTree } from 'vs/platform/list/browser/listService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { defaultCountBadgeStyles } from 'vs/platform/theme/browser/defaultStyles';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels';
import { API_OPEN_DIFF_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane';
import { IViewPaneOptions, ViewAction, ViewPane } from 'vs/workbench/browser/parts/views/viewPane';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { RepositoryRenderer } from 'vs/workbench/contrib/scm/browser/scmRepositoryRenderer';
import { ActionButtonRenderer } from 'vs/workbench/contrib/scm/browser/scmViewPane';
import { getActionViewItemProvider, isSCMActionButton, isSCMRepository, isSCMRepositoryArray } from 'vs/workbench/contrib/scm/browser/util';
import { ISCMActionButton, ISCMRepository, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent } from 'vs/workbench/contrib/scm/common/scm';
import { ISCMActionButton, ISCMRepository, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, SYNC_VIEW_PANE_ID } from 'vs/workbench/contrib/scm/common/scm';
import { comparePaths } from 'vs/base/common/comparers';
import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryItemGroup } from 'vs/workbench/contrib/scm/common/history';
import { localize } from 'vs/nls';
@@ -41,8 +41,18 @@ import { basename, dirname } from 'vs/base/common/resources';
import { ILabelService } from 'vs/platform/label/common/label';
import { stripIcons } from 'vs/base/common/iconLabels';
import { FileKind } from 'vs/platform/files/common/files';
import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { Codicon } from 'vs/base/common/codicons';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { Emitter } from 'vs/base/common/event';
import { ITreeCompressionDelegate } from 'vs/base/browser/ui/tree/asyncDataTree';
import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree';
import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import { IResourceNode, ResourceTree } from 'vs/base/common/resourceTree';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
type TreeElement = ISCMRepository[] | ISCMRepository | ISCMActionButton | SCMHistoryItemGroupTreeElement | SCMHistoryItemTreeElement | SCMHistoryItemChangeTreeElement;
type SCMHistoryItemChangeResourceTreeNode = IResourceNode<SCMHistoryItemChangeTreeElement, SCMHistoryItemTreeElement>;
type TreeElement = ISCMRepository[] | ISCMRepository | ISCMActionButton | SCMHistoryItemGroupTreeElement | SCMHistoryItemTreeElement | SCMHistoryItemChangeTreeElement | SCMHistoryItemChangeResourceTreeNode;
function isSCMHistoryItemGroupTreeElement(obj: any): obj is SCMHistoryItemGroupTreeElement {
return (obj as SCMHistoryItemGroupTreeElement).type === 'historyItemGroup';
@@ -86,11 +96,25 @@ function getSCMResourceId(element: TreeElement): string {
const historyItemGroup = historyItem.historyItemGroup;
const provider = historyItemGroup.repository.provider;
return `historyItemChange:${provider.id}/${historyItemGroup.id}/${historyItem.id}/${element.uri.toString()}`;
} else if (ResourceTree.isResourceNode(element)) {
const historyItem = element.context;
const historyItemGroup = historyItem.historyItemGroup;
const provider = historyItemGroup.repository.provider;
return `folder:${provider.id}/${historyItemGroup.id}/${historyItem.id}/$FOLDER/${element.uri.toString()}`;
} else {
throw new Error('Invalid tree element');
}
}
const enum ViewMode {
List = 'list',
Tree = 'tree'
}
const ContextKeys = {
ViewMode: new RawContextKey<ViewMode>('scmSyncViewMode', ViewMode.List),
};
interface SCMHistoryItemGroupTreeElement extends ISCMHistoryItemGroup {
readonly description?: string;
readonly ancestor?: string;
@@ -130,12 +154,26 @@ class ListDelegate implements IListVirtualDelegate<any> {
return HistoryItemRenderer.TEMPLATE_ID;
} else if (isSCMHistoryItemChangeTreeElement(element)) {
return HistoryItemChangeRenderer.TEMPLATE_ID;
} else if (ResourceTree.isResourceNode(element)) {
return HistoryItemChangeRenderer.TEMPLATE_ID;
} else {
throw new Error('Invalid tree element');
}
}
}
class CompressionDelegate implements ITreeCompressionDelegate<TreeElement> {
isIncompressible(element: TreeElement): boolean {
if (ResourceTree.isResourceNode(element)) {
return element.childrenCount === 0 || !element.parent || !element.parent.parent;
}
return true;
}
}
interface HistoryItemGroupTemplate {
readonly label: IconLabel;
readonly count: CountBadge;
@@ -236,12 +274,15 @@ interface HistoryItemChangeTemplate {
readonly disposables: IDisposable;
}
class HistoryItemChangeRenderer implements ITreeRenderer<SCMHistoryItemChangeTreeElement, void, HistoryItemChangeTemplate> {
class HistoryItemChangeRenderer implements ICompressibleTreeRenderer<SCMHistoryItemChangeTreeElement | SCMHistoryItemChangeResourceTreeNode, void, HistoryItemChangeTemplate> {
static readonly TEMPLATE_ID = 'historyItemChange';
get templateId(): string { return HistoryItemChangeRenderer.TEMPLATE_ID; }
constructor(private labels: ResourceLabels) { }
constructor(
private readonly viewMode: () => ViewMode,
private readonly labels: ResourceLabels,
@ILabelService private labelService: ILabelService) { }
renderTemplate(container: HTMLElement): HistoryItemChangeTemplate {
const element = append(container, $('.change'));
@@ -252,11 +293,27 @@ class HistoryItemChangeRenderer implements ITreeRenderer<SCMHistoryItemChangeTre
return { element, name, fileLabel, decorationIcon, disposables: new DisposableStore() };
}
renderElement(node: ITreeNode<SCMHistoryItemChangeTreeElement, void>, index: number, templateData: HistoryItemChangeTemplate, height: number | undefined): void {
renderElement(node: ITreeNode<SCMHistoryItemChangeTreeElement | SCMHistoryItemChangeResourceTreeNode, void>, index: number, templateData: HistoryItemChangeTemplate, height: number | undefined): void {
const historyItemChangeOrFolder = node.element;
const fileKind = ResourceTree.isResourceNode(historyItemChangeOrFolder) && historyItemChangeOrFolder.childrenCount > 0 ? FileKind.FOLDER : FileKind.FILE;
templateData.fileLabel.setFile(node.element.uri, {
fileDecorations: { colors: false, badges: true },
fileKind: FileKind.FILE,
hidePath: false,
fileKind,
hidePath: this.viewMode() === ViewMode.Tree,
});
}
renderCompressedElements(node: ITreeNode<ICompressedTreeNode<SCMHistoryItemChangeTreeElement | SCMHistoryItemChangeResourceTreeNode>, void>, index: number, templateData: HistoryItemChangeTemplate, height: number | undefined): void {
const compressed = node.element as ICompressedTreeNode<SCMHistoryItemChangeResourceTreeNode>;
const folder = compressed.elements[compressed.elements.length - 1];
const label = compressed.elements.map(e => e.name);
templateData.fileLabel.setResource({ resource: folder.uri, name: label }, {
fileDecorations: { colors: false, badges: true },
fileKind: FileKind.FOLDER,
separator: this.labelService.getSeparator(folder.uri.scheme)
});
}
@@ -292,15 +349,7 @@ class SCMSyncViewPaneAccessibilityProvider implements IListAccessibilityProvider
} else if (isSCMHistoryItemTreeElement(element)) {
return `${stripIcons(element.label).trim()}${element.description ? `, ${element.description}` : ''}`;
} else if (isSCMHistoryItemChangeTreeElement(element)) {
const result: string[] = [];
result.push(basename(element.uri));
// TODO - add decoration
// if (element.decorations.tooltip) {
// result.push(element.decorations.tooltip);
// }
const result = [basename(element.uri)];
const path = this.labelService.getUriLabel(dirname(element.uri), { relative: true, noPrefix: true });
if (path) {
@@ -375,7 +424,7 @@ export class SCMSyncViewPane extends ViewPane {
private listLabels!: ResourceLabels;
private treeContainer!: HTMLElement;
private _tree!: WorkbenchAsyncDataTree<TreeElement, TreeElement>;
private _tree!: WorkbenchCompressibleAsyncDataTree<TreeElement, TreeElement>;
private _viewModel!: SCMSyncPaneViewModel;
get viewModel(): SCMSyncPaneViewModel { return this._viewModel; }
@@ -407,24 +456,28 @@ export class SCMSyncViewPane extends ViewPane {
this._register(this.listLabels);
this._tree = this.instantiationService.createInstance(
WorkbenchAsyncDataTree,
WorkbenchCompressibleAsyncDataTree,
'SCM Sync View',
this.treeContainer,
new ListDelegate(),
new CompressionDelegate(),
[
this.instantiationService.createInstance(RepositoryRenderer, getActionViewItemProvider(this.instantiationService)),
this.instantiationService.createInstance(ActionButtonRenderer),
this.instantiationService.createInstance(HistoryItemGroupRenderer),
this.instantiationService.createInstance(HistoryItemRenderer),
this.instantiationService.createInstance(HistoryItemChangeRenderer, this.listLabels),
this.instantiationService.createInstance(HistoryItemChangeRenderer, () => this.viewModel.mode, this.listLabels),
],
this.instantiationService.createInstance(SCMSyncDataSource),
this.instantiationService.createInstance(SCMSyncDataSource, () => this.viewModel.mode),
{
compressionEnabled: true,
horizontalScrolling: false,
autoExpandSingleChildren: true,
collapseByDefault: (e) => !ResourceTree.isResourceNode(e),
accessibilityProvider: this.instantiationService.createInstance(SCMSyncViewPaneAccessibilityProvider),
identityProvider: this.instantiationService.createInstance(SCMSyncViewPaneTreeIdentityProvider),
sorter: this.instantiationService.createInstance(SCMSyncViewPaneTreeSorter),
}) as WorkbenchAsyncDataTree<TreeElement, TreeElement>;
}) as WorkbenchCompressibleAsyncDataTree<TreeElement, TreeElement>;
this._register(this._tree);
this._register(this._tree.onDidOpen(this.onDidOpen, this));
@@ -436,6 +489,7 @@ export class SCMSyncViewPane extends ViewPane {
this.updateIndentStyles(this.themeService.getFileIconTheme());
this._register(this.themeService.onDidFileIconThemeChange(this.updateIndentStyles, this));
this._register(this._viewModel.onDidChangeMode(this.onDidChangeMode, this));
}
protected override layoutBody(height: number, width: number): void {
@@ -443,18 +497,30 @@ export class SCMSyncViewPane extends ViewPane {
this._tree.layout(height, width);
}
private onDidChangeMode(): void {
this.updateIndentStyles(this.themeService.getFileIconTheme());
}
private async onDidOpen(e: IOpenEvent<TreeElement | undefined>): Promise<void> {
if (!e.element) {
return;
} else if (isSCMHistoryItemChangeTreeElement(e.element)) {
}
if (isSCMHistoryItemChangeTreeElement(e.element)) {
if (e.element.originalUri && e.element.modifiedUri) {
await this.commandService.executeCommand(API_OPEN_DIFF_EDITOR_COMMAND_ID, ...toDiffEditorArguments(e.element.uri, e.element.originalUri, e.element.modifiedUri), e);
}
} else if (ResourceTree.isResourceNode(e.element) && e.element.childrenCount === 0) {
if (e.element.element?.originalUri && e.element.element?.modifiedUri) {
await this.commandService.executeCommand(API_OPEN_DIFF_EDITOR_COMMAND_ID, ...toDiffEditorArguments(e.element.element.uri, e.element.element.originalUri, e.element.element.modifiedUri), e);
}
}
}
private updateIndentStyles(theme: any): void {
this.treeContainer.classList.toggle('align-icons-and-twisties', theme.hasFileIcons || (theme.hasFileIcons && !theme.hasFolderIcons));
this.treeContainer.classList.toggle('list-view-mode', this._viewModel.mode === ViewMode.List);
this.treeContainer.classList.toggle('tree-view-mode', this._viewModel.mode === ViewMode.Tree);
this.treeContainer.classList.toggle('align-icons-and-twisties', (this._viewModel.mode === ViewMode.List && theme.hasFileIcons) || (theme.hasFileIcons && !theme.hasFolderIcons));
}
override dispose(): void {
@@ -465,6 +531,26 @@ export class SCMSyncViewPane extends ViewPane {
class SCMSyncPaneViewModel {
private readonly _onDidChangeMode = new Emitter<ViewMode>();
readonly onDidChangeMode = this._onDidChangeMode.event;
private _mode: ViewMode;
get mode(): ViewMode { return this._mode; }
set mode(mode: ViewMode) {
if (this._mode === mode) {
return;
}
this._mode = mode;
this.refresh();
this.modeContextKey.set(mode);
this._onDidChangeMode.fire(mode);
this.storageService.store(`scm.syncViewMode`, mode, StorageScope.WORKSPACE, StorageTarget.USER);
}
private modeContextKey: IContextKey<ViewMode>;
private repositories = new Map<ISCMRepository, IDisposable>();
private historyProviders = new Map<ISCMRepository, IDisposable>();
@@ -473,19 +559,25 @@ class SCMSyncPaneViewModel {
private readonly disposables = new DisposableStore();
constructor(
private readonly tree: WorkbenchAsyncDataTree<TreeElement, TreeElement>,
private readonly tree: WorkbenchCompressibleAsyncDataTree<TreeElement, TreeElement>,
@IContextKeyService contextKeyService: IContextKeyService,
@ISCMViewService scmViewService: ISCMViewService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IStorageService private readonly storageService: IStorageService,
) {
configurationService.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables);
this.onDidChangeConfiguration();
configurationService.onDidChangeConfiguration(this._onDidChangeConfiguration, this, this.disposables);
this._onDidChangeConfiguration();
scmViewService.onDidChangeVisibleRepositories(this._onDidChangeVisibleRepositories, this, this.disposables);
this._onDidChangeVisibleRepositories({ added: scmViewService.visibleRepositories, removed: [] });
this._mode = this.getViewMode();
this.modeContextKey = ContextKeys.ViewMode.bindTo(contextKeyService);
this.modeContextKey.set(this._mode);
}
private onDidChangeConfiguration(e?: IConfigurationChangeEvent): void {
private _onDidChangeConfiguration(e?: IConfigurationChangeEvent): void {
if (!e || e.affectsConfiguration('scm.alwaysShowRepositories')) {
this.alwaysShowRepositories = this.configurationService.getValue<boolean>('scm.alwaysShowRepositories');
this.refresh();
@@ -524,6 +616,16 @@ class SCMSyncPaneViewModel {
}
}
private getViewMode(): ViewMode {
let mode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewMode.List : ViewMode.Tree;
const storageMode = this.storageService.get(`scm.syncViewMode`, StorageScope.WORKSPACE) as ViewMode;
if (typeof storageMode === 'string') {
mode = storageMode;
}
return mode;
}
private async refresh(repository?: ISCMRepository): Promise<void> {
if (this.repositories.size === 0) {
return;
@@ -548,6 +650,10 @@ class SCMSyncPaneViewModel {
class SCMSyncDataSource implements IAsyncDataSource<TreeElement, TreeElement> {
constructor(
private readonly viewMode: () => ViewMode,
@IUriIdentityService private uriIdentityService: IUriIdentityService) { }
hasChildren(element: TreeElement): boolean {
if (isSCMRepositoryArray(element)) {
return true;
@@ -561,6 +667,8 @@ class SCMSyncDataSource implements IAsyncDataSource<TreeElement, TreeElement> {
return true;
} else if (isSCMHistoryItemChangeTreeElement(element)) {
return false;
} else if (ResourceTree.isResourceNode(element)) {
return element.childrenCount > 0;
} else {
throw new Error('hasChildren not implemented.');
}
@@ -651,14 +759,35 @@ class SCMSyncDataSource implements IAsyncDataSource<TreeElement, TreeElement> {
// History Item Changes
const changes = await historyProvider.provideHistoryItemChanges(element.id) ?? [];
children.push(...changes.map(change => ({
uri: change.uri,
originalUri: change.originalUri,
modifiedUri: change.modifiedUri,
renameUri: change.renameUri,
historyItem: element,
type: 'historyItemChange'
} as SCMHistoryItemChangeTreeElement)));
if (this.viewMode() === ViewMode.List) {
// List
children.push(...changes.map(change => ({
uri: change.uri,
originalUri: change.originalUri,
modifiedUri: change.modifiedUri,
renameUri: change.renameUri,
historyItem: element,
type: 'historyItemChange'
} as SCMHistoryItemChangeTreeElement)));
} else {
// Tree
const tree = new ResourceTree<SCMHistoryItemChangeTreeElement, SCMHistoryItemTreeElement>(element, repository.provider.rootUri ?? URI.file('/'), this.uriIdentityService.extUri);
for (const change of changes) {
tree.add(change.uri, {
uri: change.uri,
originalUri: change.originalUri,
modifiedUri: change.modifiedUri,
renameUri: change.renameUri,
historyItem: element,
type: 'historyItemChange'
} as SCMHistoryItemChangeTreeElement);
}
children.push(...tree.root.children);
}
} else if (ResourceTree.isResourceNode(element)) {
children.push(...element.children);
} else {
throw new Error('getChildren Method not implemented.');
}
@@ -666,3 +795,50 @@ class SCMSyncDataSource implements IAsyncDataSource<TreeElement, TreeElement> {
return children;
}
}
class SetListViewModeAction extends ViewAction<SCMSyncViewPane> {
constructor() {
super({
id: 'workbench.scm.sync.action.setListViewMode',
title: localize('setListViewMode', "View as List"),
viewId: SYNC_VIEW_PANE_ID,
f1: false,
icon: Codicon.listTree,
toggled: ContextKeys.ViewMode.isEqualTo(ViewMode.List),
menu: {
id: MenuId.ViewTitle,
group: '1_viewmode'
}
});
}
async runInView(_: ServicesAccessor, view: SCMSyncViewPane): Promise<void> {
view.viewModel.mode = ViewMode.List;
}
}
class SetTreeViewModeAction extends ViewAction<SCMSyncViewPane> {
constructor() {
super({
id: 'workbench.scm.sync.action.setTreeViewMode',
title: localize('setTreeViewMode', "View as Tree"),
viewId: SYNC_VIEW_PANE_ID,
f1: false,
icon: Codicon.listFlat,
toggled: ContextKeys.ViewMode.isEqualTo(ViewMode.Tree),
menu: {
id: MenuId.ViewTitle,
group: '1_viewmode'
}
});
}
async runInView(_: ServicesAccessor, view: SCMSyncViewPane): Promise<void> {
view.viewModel.mode = ViewMode.Tree;
}
}
registerAction2(SetListViewModeAction);
registerAction2(SetTreeViewModeAction);

View File

@@ -132,13 +132,15 @@ async function runTestsInBrowser(testModules, browserType) {
const page = await context.newPage();
const target = url.pathToFileURL(path.join(__dirname, 'renderer.html'));
if (argv.build) {
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
target.search = `?build=true&ci=true`;
} else {
target.search = `?build=true`;
}
} else if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
target.search = `?ci=true`;
target.searchParams.set('build', 'true');
}
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
target.searchParams.set('ci', 'true');
}
// see comment on warmupExposedMethods in renderer.html for what's going on
if (browserType === 'webkit') {
target.searchParams.set('ioWarmup', __dirname);
}
const emitter = new events.EventEmitter();

View File

@@ -129,8 +129,25 @@
}
}
/**
* There is some bug in WebKit on macOS in CI only that causes the first
* invokation of the file functions to (sometimes) take an inordinately
* long period of time to run. Get around this by invoking them here.
*/
async function doIoWarmup() {
const dir = url.searchParams.get('ioWarmup');
if (!dir) {
return;
}
// these are the only two functions actually used in CI presently:
await __readFileInTests(dir + '/' + 'renderer.html');
await __readDirInTests(dir);
}
window.loadAndRun = async function loadAndRun({ modules, grep }, manual = false) {
// load
await doIoWarmup();
await loadModules(modules);
// await new Promise((resolve, reject) => {
// require(modules, resolve, err => {
@@ -152,7 +169,8 @@
});
}
const modules = new URL(window.location.href).searchParams.getAll('m');
const url = new URL(window.location.href);
const modules = url.searchParams.getAll('m');
if (Array.isArray(modules) && modules.length > 0) {
console.log('MANUALLY running tests', modules);