Skip to content

Filter by file extension#37068

Open
McMichalK wants to merge 14 commits intogo-gitea:mainfrom
McMichalK:filter-by-file-extension
Open

Filter by file extension#37068
McMichalK wants to merge 14 commits intogo-gitea:mainfrom
McMichalK:filter-by-file-extension

Conversation

@McMichalK
Copy link
Copy Markdown

Add file extension filter for pull request diffs

Introduces a new dropdown filter in the PR diff toolbar that allows reviewers to show/hide files based on their extensions. This helps focus on specific file types during code review (e.g., viewing only .ts files when reviewing TypeScript changes, or hiding .test.js files when reviewing implementation).

Closes: Feature of file filter in PR page #27256

Usage

  1. Open a pull request
  2. Click the filter icon (🔽) in the diff toolbar, next to the commit filter
  3. Select/deselect file extensions to show/hide (files without extensions appear as (no extension))
  4. Use the "Select all" / "Deselect all" buttons for quick toggling
  5. Click "Apply" to update the diff view

How it works

  • The filter scans all files in the PR diff and extracts their extensions
  • Files are grouped by extension with a count displayed
  • Clicking "Apply" adds the .tw-hidden CSS class to non-matching files, instantly hiding them
  • The button shows a subtle outline indicator when a filter is active
  • Filter state is ephemeral (resets on page reload) — appropriate for per-PR review sessions

Keyboard Navigation

For users who prefer keyboard navigation:

  • Arrow Down/Up: Navigate through checkboxes and buttons
  • Escape: Close the dropdown menu
  • Space: Toggle checkboxes (native HTML behavior)
  • Enter: Activate buttons (native HTML behavior)

Technical Details

  • Vue 3 component (DiffFileExtensionFilter.vue) following the same pattern as the existing commit filter
  • Event listeners use capture-phase for dropdown compatibility with other elements
  • All UI uses Tailwind CSS and Fomantic-UI classes consistent with Gitea's design system
  • Fully accessible with ARIA attributes and semantic HTML
  • Code passes all linting and formatting checks

Testing

  1. Navigate to any pull request with multiple file types
  2. Click the filter button and verify the extension list appears
  3. Test checkbox toggling and the "Select all"/"Deselect all" buttons
  4. Click "Apply" and verify files are hidden/shown correctly
  5. Verify the button shows a visible outline when filtering is active
  6. Close the dropdown and reopen—checkboxes should reflect current view state
  7. Test keyboard navigation: use arrow keys to navigate, Escape to close

Screenshots

Filter by extension dropdown:
image

State when files are filtered out:
image

Note: This implementation uses AI assistance (GitHub Copilot). All code has been reviewed and tested against Gitea's contribution guidelines and passes all linting checks.

There is new button in pull request diff template, which opens dropbox containing list of all unique extensions that the diff contains.
List is created with checkmarks that can be checked/unchecked to filter out files with specific extension. There is also "select all" and "deselect all" buttons.
To apply the change and filter out the files user have to click on "Apply" button. If any extensions were filtered out, then button which opens the dropbox is outlined to signal some files were filtered out.
@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Apr 1, 2026
@github-actions github-actions bot added modifies/templates This PR modifies the template files modifies/frontend labels Apr 1, 2026
@wxiaoguang
Copy link
Copy Markdown
Contributor

  1. Rewrite it with modern <script setup>.
  2. There is a "loading more" button if the file list exceeds the limit, you need to sync the state after more files are loaded.

@wxiaoguang wxiaoguang marked this pull request as draft April 1, 2026 09:14
@bircni
Copy link
Copy Markdown
Member

bircni commented Apr 1, 2026

Can we also move the filter to the left, above the files and also add the search bar?

@bircni
Copy link
Copy Markdown
Member

bircni commented Apr 1, 2026

Bildschirmfoto 2026-04-01 um 12 03 57

I think the right side is already very full

@McMichalK
Copy link
Copy Markdown
Author

Can we also move the filter to the left, above the files and also add the search bar?

My thinking was: there is already one feature filtering the diff - filtering by commits, so I've added similar feature nearby to let user have those side by side.

Bildschirmfoto 2026-04-01 um 12 03 57 I think the right side is already very full

Also, as a daily gitea user, which is doing couple PR reviews per day, I do not agree that right side is very crowded right now, because everything is well placed.

I'm open to discussion tho ;)

@McMichalK
Copy link
Copy Markdown
Author

In recent changes I've fixed the code for those issues:

  1. Rewrite it with modern <script setup>.
  2. There is a "loading more" button if the file list exceeds the limit, you need to sync the state after more files are loaded.

And I also added the search box, as suggested. I did not changed the position of the button, waiting for more voices for this change.

@McMichalK McMichalK marked this pull request as ready for review April 1, 2026 16:25
@bircni
Copy link
Copy Markdown
Member

bircni commented Apr 1, 2026

And I also added the search box, as suggested. I did not changed the position of the button, waiting for more voices for this change.

@silverwind whats your opinion on that?
I would really like to have file sorting above the files not at the right side

Copy link
Copy Markdown
Member

@bircni bircni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do a proper pass over the AI-generated output before requesting review.

*/
function getExtension(filename: string): string {
const lastDot = filename.lastIndexOf('.');
if (lastDot === -1 || lastDot === 0) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Files like .gitignore, .eslintrc, .env all map to '(no extension)', mixing them with files that truly have no extension (e.g., Makefile, Dockerfile). A common convention is to treat the full name as the "extension" for dotfiles, or at minimum use a separate label.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow Golang's approach: always include the dot in ext name.

'.zip', '.html', '.env'

/**
* Filter extensions based on search query
* Matches against extension name (e.g., ".ts", ".go")
*/
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why so many unnecessary docs?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I documented all the functions by default, cause there were no clues about documenting the functions in the guidelines I belive. Is it a problem?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a function is designed clear enough (clear name, clear arguments, clear logic), it doesn't need to duplicate the comment.

Comments are used to explain more than function name itself and provide more useful information.

But TBH, for this function, even after read the comment, I don't know how it works.

I think you can show some examples: "if searchQuery=.., then return ..., otherwise, return ..."

{{template "repo/diff/whitespace_dropdown" .}}
{{template "repo/diff/options_dropdown" .}}
{{if .PageIsPullFiles}}
<div id="diff-extension-filter" data-filter_by_file_extension="{{ctx.Locale.Tr "repo.pulls.filter_by_file_extension"}}" data-select_all="{{ctx.Locale.Tr "repo.pulls.select_all_file_extensions"}}" data-deselect_all="{{ctx.Locale.Tr "repo.pulls.deselect_all_file_extensions"}}" data-apply="{{ctx.Locale.Tr "repo.pulls.apply_file_extension_filter"}}">
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data-filter_by_file_extension
mix of _ and -

select_all: el.getAttribute('data-select_all') ?? 'Select all',
deselect_all: el.getAttribute('data-deselect_all') ?? 'Deselect all',
apply: el.getAttribute('data-apply') ?? 'Apply',
search: el.getAttribute('data-search') ?? 'Search extensions...',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why ??

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just defensive approach to not cause potential TypeScript error, but I do noticed that the other vue component was leaving locale records without defaults.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to be too defensive.

If programming error happens, make it exposed as early as possible, no need to hide faults.

function getExtension(filename: string): string {
const lastDot = filename.lastIndexOf('.');
if (lastDot === -1 || lastDot === 0) {
return '(no extension)';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hard coded ?

@GiteaBot GiteaBot added lgtm/blocked A maintainer has reservations with the PR and thus it cannot be merged and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Apr 1, 2026
v-model="ext.checked"
>
<label :for="`ext-filter-${ext.ext}`" class="tw-cursor-pointer">
<span class="tw-font-mono">{{ ext.ext }}</span>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No monospace font in non-code please.

</template>
</div>
<div v-if="filteredExtensions.length === 0" class="tw-py-4 tw-text-center tw-text-text-light-2">
{{ locale.no_results ?? 'No extensions found' }}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to translate.

}

onMounted(() => {
document.body.addEventListener('click', onBodyClick, true);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Body listener is too broad. Register listener at a closer parent inside the component.

@McMichalK
Copy link
Copy Markdown
Author

@silverwind @wxiaoguang @bircni I've made edits based on your comments :)

@bircni
Copy link
Copy Markdown
Member

bircni commented Apr 5, 2026

please press on resolve on resolved comments

@Excellencedev
Copy link
Copy Markdown
Contributor

please press on resolve on resolved comments

I think its better to let the maintainer resolve it themselves in case they may not see something as actually fixed

@lunny
Copy link
Copy Markdown
Member

lunny commented Apr 5, 2026

image stats is not right.

@McMichalK
Copy link
Copy Markdown
Author

McMichalK commented Apr 6, 2026

image stats is not right.

@lunny Can you share more details, I see you have 7 files in pull request and extension filter is showing two, so I would like to know which other files were not calculated. Or you have something else in mind?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm/blocked A maintainer has reservations with the PR and thus it cannot be merged modifies/frontend modifies/templates This PR modifies the template files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature of file filter in PR page

7 participants