-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
Add pull request files line selections #36014
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
Open
lunny
wants to merge
20
commits into
go-gitea:main
Choose a base branch
from
lunny:lunny/add_pr_files_selection
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
1ec71e8
Add pull request files line selections
lunny c144b64
Fix bug
lunny 373b530
Fix unfold when loading with line selections
lunny 89989c5
Support select the blank position of the line numbers
lunny 35b8f11
Fix bug when selecting plain lines
lunny 5580cc7
Fix bug
lunny 83d18d6
Fix bug
lunny 45d2fbc
fix selection css
lunny f8567d0
Merge branch 'main' into lunny/add_pr_files_selection
lunny 41857b4
some improvements
lunny a5738ef
Merge branch 'main' into lunny/add_pr_files_selection
lunny 91fc220
Fix conflicts
lunny ac5f96b
Fix format
lunny dbfa95b
Fix format
lunny 3140aac
Remove unnecessary code
lunny ff5544a
Remove unnecessary code
lunny a9d887a
improvements
lunny cd8130e
Merge branch 'main' into lunny/add_pr_files_selection
lunny cf809d3
Fix bug
lunny 4f65ae3
Add unit test for applyDiffLineSelection
lunny File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,60 @@ | ||
| import {svg} from '../svg.ts'; | ||
|
|
||
| function parseTransitionValue(value: string): number { | ||
| let max = 0; | ||
| for (const current of value.split(',')) { | ||
| const trimmed = current.trim(); | ||
| if (!trimmed) continue; | ||
| const isMs = trimmed.endsWith('ms'); | ||
| const numericPortion = Number.parseFloat(trimmed.replace(/ms|s$/u, '')); | ||
| if (Number.isNaN(numericPortion)) continue; | ||
| const duration = numericPortion * (isMs ? 1 : 1000); | ||
| max = Math.max(max, duration); | ||
| } | ||
| return max; | ||
| } | ||
|
|
||
| function waitForTransitionEnd(element: Element): Promise<void> { | ||
| if (!(element instanceof HTMLElement)) return Promise.resolve(); | ||
| const transitionTarget = element.querySelector<HTMLElement>('.diff-file-body') ?? element; | ||
| const styles = window.getComputedStyle(transitionTarget); | ||
| const transitionDuration = parseTransitionValue(styles.transitionDuration); | ||
| const transitionDelay = parseTransitionValue(styles.transitionDelay); | ||
| const total = transitionDuration + transitionDelay; | ||
| if (total === 0) return Promise.resolve(); | ||
|
|
||
| return new Promise((resolve) => { | ||
| let resolved = false; | ||
| function cleanup() { | ||
| if (resolved) return; | ||
| resolved = true; | ||
| transitionTarget.removeEventListener('transitionend', onTransitionEnd); | ||
| resolve(); | ||
| } | ||
| function onTransitionEnd(event: TransitionEvent) { | ||
| if (event.target !== transitionTarget) return; | ||
| cleanup(); | ||
| } | ||
| transitionTarget.addEventListener('transitionend', onTransitionEnd); | ||
| window.setTimeout(cleanup, total + 50); | ||
| }); | ||
| } | ||
|
|
||
| // Hides the file if newFold is true, and shows it otherwise. The actual hiding is performed using CSS. | ||
| // | ||
| // The fold arrow is the icon displayed on the upper left of the file box, especially intended for components having the 'fold-file' class. | ||
| // The file content box is the box that should be hidden or shown, especially intended for components having the 'file-content' class. | ||
| // | ||
| export function setFileFolding(fileContentBox: Element, foldArrow: HTMLElement, newFold: boolean) { | ||
| export function setFileFolding(fileContentBox: Element, foldArrow: HTMLElement, newFold: boolean): Promise<void> { | ||
| foldArrow.innerHTML = svg(`octicon-chevron-${newFold ? 'right' : 'down'}`, 18); | ||
| fileContentBox.setAttribute('data-folded', String(newFold)); | ||
| if (newFold && fileContentBox.getBoundingClientRect().top < 0) { | ||
| fileContentBox.scrollIntoView(); | ||
| } | ||
| return waitForTransitionEnd(fileContentBox); | ||
| } | ||
|
|
||
| // Like `setFileFolding`, except that it automatically inverts the current file folding state. | ||
| export function invertFileFolding(fileContentBox:HTMLElement, foldArrow: HTMLElement) { | ||
| setFileFolding(fileContentBox, foldArrow, fileContentBox.getAttribute('data-folded') !== 'true'); | ||
| export function invertFileFolding(fileContentBox:HTMLElement, foldArrow: HTMLElement): Promise<void> { | ||
| return setFileFolding(fileContentBox, foldArrow, fileContentBox.getAttribute('data-folded') !== 'true'); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| import {applyDiffLineSelection} from './repo-diff-selection.ts'; | ||
|
|
||
| function createDiffRow(tbody: HTMLTableSectionElement, options: {id?: string, lineType?: string} = {}) { | ||
| const tr = document.createElement('tr'); | ||
| if (options.lineType) tr.setAttribute('data-line-type', options.lineType); | ||
|
|
||
| const numberCell = document.createElement('td'); | ||
| numberCell.classList.add('lines-num'); | ||
| const span = document.createElement('span'); | ||
| if (options.id) span.id = options.id; | ||
| numberCell.append(span); | ||
| tr.append(numberCell); | ||
|
|
||
| tr.append(document.createElement('td')); | ||
| tbody.append(tr); | ||
| return tr; | ||
| } | ||
|
|
||
| describe('applyDiffLineSelection', () => { | ||
| beforeEach(() => { | ||
| document.body.innerHTML = ''; | ||
| }); | ||
|
|
||
| test('selects contiguous diff rows, skips expansion rows, and clears previous selection', () => { | ||
| const fragment = 'diff-selection'; | ||
|
|
||
| const otherBox = document.createElement('div'); | ||
| const otherTable = document.createElement('table'); | ||
| otherTable.classList.add('code-diff'); | ||
| const otherTbody = document.createElement('tbody'); | ||
| const staleActiveRow = document.createElement('tr'); | ||
| staleActiveRow.classList.add('active'); | ||
| otherTbody.append(staleActiveRow); | ||
| otherTable.append(otherTbody); | ||
| otherBox.append(otherTable); | ||
|
|
||
| const container = document.createElement('div'); | ||
| container.classList.add('diff-file-box'); | ||
| const table = document.createElement('table'); | ||
| table.classList.add('code-diff'); | ||
| const tbody = document.createElement('tbody'); | ||
| table.append(tbody); | ||
| container.append(table); | ||
|
|
||
| const rows = [ | ||
| createDiffRow(tbody, {id: `${fragment}L1`}), | ||
| createDiffRow(tbody), | ||
| createDiffRow(tbody, {lineType: '4'}), | ||
| createDiffRow(tbody), | ||
| createDiffRow(tbody, {id: `${fragment}R5`}), | ||
| createDiffRow(tbody), | ||
| ]; | ||
|
|
||
| document.body.append(otherBox, container); | ||
|
|
||
| const range = {fragment, startSide: 'L' as const, startLine: 1, endSide: 'R' as const, endLine: 5}; | ||
| const applied = applyDiffLineSelection(container, range); | ||
|
|
||
| expect(applied).toBe(true); | ||
| expect(rows[0].classList.contains('active')).toBe(true); | ||
| expect(rows[1].classList.contains('active')).toBe(true); | ||
| expect(rows[2].classList.contains('active')).toBe(false); | ||
| expect(rows[3].classList.contains('active')).toBe(true); | ||
| expect(rows[4].classList.contains('active')).toBe(true); | ||
| expect(rows[5].classList.contains('active')).toBe(false); | ||
| expect(staleActiveRow.classList.contains('active')).toBe(false); | ||
| }); | ||
|
|
||
| test('returns false when either anchor is missing', () => { | ||
| const fragment = 'diff-missing'; | ||
| const container = document.createElement('div'); | ||
| container.classList.add('diff-file-box'); | ||
| const table = document.createElement('table'); | ||
| table.classList.add('code-diff'); | ||
| const tbody = document.createElement('tbody'); | ||
| table.append(tbody); | ||
| container.append(table); | ||
| document.body.append(container); | ||
|
|
||
| createDiffRow(tbody, {id: `${fragment}L1`}); | ||
|
|
||
| const applied = applyDiffLineSelection(container, {fragment, startSide: 'L' as const, startLine: 1, endSide: 'R' as const, endLine: 2}); | ||
| expect(applied).toBe(false); | ||
| expect(container.querySelectorAll('tr.active').length).toBe(0); | ||
| }); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
It is 100% garbage function .....