-
Notifications
You must be signed in to change notification settings - Fork 36k
Added support for navigating across files in multi-file diff editor and info for narrator #274466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,7 @@ import { Button } from '../../../../base/browser/ui/button/button.js'; | |||||||||||||||||
| import { Codicon } from '../../../../base/common/codicons.js'; | ||||||||||||||||||
| import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; | ||||||||||||||||||
| import { autorun, derived, globalTransaction, observableValue } from '../../../../base/common/observable.js'; | ||||||||||||||||||
| import { localize } from '../../../../nls.js'; | ||||||||||||||||||
| import { createActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; | ||||||||||||||||||
| import { MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; | ||||||||||||||||||
| import { MenuId } from '../../../../platform/actions/common/actions.js'; | ||||||||||||||||||
|
|
@@ -244,21 +245,43 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< | |||||||||||||||||
| let isDeleted = false; | ||||||||||||||||||
| let isAdded = false; | ||||||||||||||||||
| let flag = ''; | ||||||||||||||||||
| let status = 'Modified'; | ||||||||||||||||||
| if (data.viewModel.modifiedUri && data.viewModel.originalUri && data.viewModel.modifiedUri.path !== data.viewModel.originalUri.path) { | ||||||||||||||||||
| flag = 'R'; | ||||||||||||||||||
| isRenamed = true; | ||||||||||||||||||
| status = 'Renamed'; | ||||||||||||||||||
| } else if (!data.viewModel.modifiedUri) { | ||||||||||||||||||
| flag = 'D'; | ||||||||||||||||||
| isDeleted = true; | ||||||||||||||||||
| status = 'Deleted'; | ||||||||||||||||||
| } else if (!data.viewModel.originalUri) { | ||||||||||||||||||
| flag = 'A'; | ||||||||||||||||||
| isAdded = true; | ||||||||||||||||||
| status = 'Added'; | ||||||||||||||||||
| } | ||||||||||||||||||
| this._elements.status.classList.toggle('renamed', isRenamed); | ||||||||||||||||||
| this._elements.status.classList.toggle('deleted', isDeleted); | ||||||||||||||||||
| this._elements.status.classList.toggle('added', isAdded); | ||||||||||||||||||
| this._elements.status.innerText = flag; | ||||||||||||||||||
|
|
||||||||||||||||||
| // Set aria-label for the individual file container | ||||||||||||||||||
| const originalFileName = data.viewModel.originalUri?.path.split('/').pop(); | ||||||||||||||||||
| const modifiedFileName = data.viewModel.modifiedUri?.path.split('/').pop(); | ||||||||||||||||||
|
|
||||||||||||||||||
| // Set aria-labels for the headers | ||||||||||||||||||
| let fileAriaLabel = ''; | ||||||||||||||||||
| if (isRenamed) { | ||||||||||||||||||
| fileAriaLabel = localize('renamedFileHeader', 'Renamed file from {0} to {1}', originalFileName, modifiedFileName); | ||||||||||||||||||
| } else if (isAdded) { | ||||||||||||||||||
| fileAriaLabel = localize('modifiedFileHeader', '{0} file {1}', status, modifiedFileName); | ||||||||||||||||||
| } else { | ||||||||||||||||||
| fileAriaLabel = localize('modifiedFileHeader', '{0} file {1}', status, originalFileName); | ||||||||||||||||||
|
Comment on lines
+276
to
+278
|
||||||||||||||||||
| fileAriaLabel = localize('modifiedFileHeader', '{0} file {1}', status, modifiedFileName); | |
| } else { | |
| fileAriaLabel = localize('modifiedFileHeader', '{0} file {1}', status, originalFileName); | |
| fileAriaLabel = localize('addedFileHeader', 'Added file {0}', modifiedFileName); | |
| } else if (isDeleted) { | |
| fileAriaLabel = localize('deletedFileHeader', 'Deleted file {0}', originalFileName); | |
| } else { | |
| fileAriaLabel = localize('modifiedFileHeader', 'Modified file {0}', originalFileName); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -2,7 +2,6 @@ | |||||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||||||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||||||
| *--------------------------------------------------------------------------------------------*/ | ||||||
|
|
||||||
| import { Dimension, getWindow, h, scheduleAtNextAnimationFrame } from '../../../../base/browser/dom.js'; | ||||||
| import { SmoothScrollableElement } from '../../../../base/browser/ui/scrollbar/scrollableElement.js'; | ||||||
| import { compareBy, numberComparator } from '../../../../base/common/arrays.js'; | ||||||
|
|
@@ -88,7 +87,10 @@ export class MultiDiffEditorWidgetImpl extends Disposable { | |||||
| horizontal: ScrollbarVisibility.Auto, | ||||||
| useShadows: false, | ||||||
| }, this._scrollable)); | ||||||
| this._elements = h('div.monaco-component.multiDiffEditor', {}, [ | ||||||
| this._elements = h('div.monaco-component.multiDiffEditor', { | ||||||
| 'aria-label': localize('multiDiffEditor.ariaLabel', 'Multi File Diff Editor'), | ||||||
| 'role': 'region' | ||||||
| }, [ | ||||||
| h('div', {}, [this._scrollableElement.getDomNode()]), | ||||||
| h('div.placeholder@placeholder', {}, [h('div')]), | ||||||
| ]); | ||||||
|
|
@@ -346,6 +348,75 @@ export class MultiDiffEditorWidgetImpl extends Disposable { | |||||
|
|
||||||
| this._scrollableElements.content.style.transform = `translateY(${-(scrollTop + contentScrollOffsetToScrollOffset)}px)`; | ||||||
| } | ||||||
|
|
||||||
| public goToNextFile(): void { | ||||||
| const viewModel = this._viewModel.get(); | ||||||
| if (!viewModel) { | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| const items = viewModel.items.get(); | ||||||
| const activeItem = viewModel.activeDiffItem.get(); | ||||||
| const currentIndex = activeItem ? items.indexOf(activeItem) : -1; | ||||||
| const nextIndex = Math.min(currentIndex + 1, items.length - 1); | ||||||
|
|
||||||
| if (nextIndex !== currentIndex && nextIndex >= 0) { | ||||||
|
||||||
| if (nextIndex !== currentIndex && nextIndex >= 0) { | |
| if (nextIndex !== currentIndex) { |
Copilot
AI
Nov 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check prevIndex >= 0 on line 382 is redundant. Since prevIndex = Math.max(currentIndex - 1, 0), it's guaranteed to be at least 0. The condition prevIndex !== currentIndex is sufficient to ensure we're not already at the first item.
| if (prevIndex !== currentIndex && prevIndex >= 0) { | |
| if (prevIndex !== currentIndex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason we don't want to use MultiDiffEditorWidgetImpl#reveal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can use
basename