mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-20 02:08:47 +00:00
Merge branch 'main' into merogge/diff
This commit is contained in:
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,6 +759,9 @@ class SCMSyncDataSource implements IAsyncDataSource<TreeElement, TreeElement> {
|
||||
|
||||
// History Item Changes
|
||||
const changes = await historyProvider.provideHistoryItemChanges(element.id) ?? [];
|
||||
|
||||
if (this.viewMode() === ViewMode.List) {
|
||||
// List
|
||||
children.push(...changes.map(change => ({
|
||||
uri: change.uri,
|
||||
originalUri: change.originalUri,
|
||||
@@ -659,6 +770,24 @@ class SCMSyncDataSource implements IAsyncDataSource<TreeElement, TreeElement> {
|
||||
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);
|
||||
|
||||
@@ -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`;
|
||||
target.searchParams.set('build', 'true');
|
||||
}
|
||||
} else if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
|
||||
target.search = `?ci=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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user