Skip to content

Commit 94dd444

Browse files
authored
[#2512] Add global search panel (#2526)
The file search feature only supports searching within a single repository under one author. Students in SWE courses find it useful to search across all repositories to see how others implement certain files (e.g., testing files, **/ug.md) by glob. Let's add global search panel with mode toggle button in the summary header. The implementation preserves the existing local search functionality while introducing a new global search mode. A mode toggle button replaces the text field to switch between local and global search. The global search panel displays results across all authors and repositories.
1 parent 4ced2e8 commit 94dd444

22 files changed

+1888
-83
lines changed

docs/dg/report.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ The authorship module retrieves the relevant information from the corresponding
127127
### Showing relevant information by authors
128128
The files will be filtered, picking only files the selected author has written in. The lines are then split into chunks of "touched" and "untouched" code segments displayed in the tab view which will be popped up on the right side of the screen.
129129

130+
<!-- ==================================================================================================== -->
131+
## Global file browser view ([c-global-file-browser.vue](https://github.com/reposense/RepoSense/blob/master/frontend/src/views/c-global-file-browser.vue))
132+
The `c-global-file-browser` module is responsible for file filtering across repositories. It allows users to find files by glob and displays the results by path or by repository.
133+
130134
<!-- ==================================================================================================== -->
131135

132136
## Zoom view ([c-zoom.vue](https://github.com/reposense/RepoSense/blob/master/frontend/src/views/c-zoom.vue))

docs/ug/usingReports.md

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ Here is an example of how a typical report looks like:
3636

3737
![report](../images/report-features.png)
3838

39-
The report is divided into two sections: [_**Chart panel**_](#chart-panel) and the [_**Code panel**_](#code-panel). In some situations, the [_**Commits panel**_](#commits-panel) will appear in place of the _code panel_. All three are explained in the sections below.
40-
39+
The report is divided into two sections: [_**Chart panel**_](#chart-panel) and the [_**Code panel**_](#code-panel). In some situations, the [_**Commits panel**_](#commits-panel) or the [_**Global file browser**_](#global-file-browser) will appear in place of the Code panel.
4140
<box type="info" seamless>
4241

4342
Links to external webpages (repository link, author's profile, blame view, etc.) is only properly supported when RepoSense analyzes a remote repository belonging to GitHub, GitLab or BitBucket.
@@ -189,3 +188,81 @@ The `Commits panel` allows users to see the commits attributed to a specific aut
189188
* To promote and encourage the 50/72 rule for commit messages, a dotted vertical line will be shown for:
190189
* Commit message subject that exceeds 50 characters.
191190
* Commit message body after the 72nd character mark.
191+
192+
## Global file browser panel
193+
194+
The `Global file browser` provides a way to search and browse files across all repositories in the report. Unlike the [Code panel](#code-panel), which shows code attributed to a single author within one repository, the global file browser lets you explore files from every repository at once.
195+
196+
#### Accessing the global file browser
197+
198+
1. Locate the **filter mode** dropdown at the top of the tool bar.
199+
2. Select **Global** from the dropdown.
200+
3. The global file browser will appear on the right side of the report, replacing the Code panel.
201+
202+
To return to the normal view, select **Local** from the dropdown. You can also click any author's %%:fas-code:%% or %%:fas-list-ul:%% icon in the Chart panel, which automatically switches the filter mode back to Local and opens the corresponding Code or Commits panel.
203+
204+
<box type="info" seamless>
205+
206+
When you switch to global mode, any previously highlighted author in the Chart panel is deselected. Likewise, switching back to Local mode (or clicking an author icon) closes the global file browser.
207+
</box>
208+
209+
#### Filtering files with glob patterns
210+
211+
The search input at the top of the panel filters files using [glob patterns](https://docs.oracle.com/javase/tutorial/essential/io/fileOps.html#glob). The file count updates in real-time as you type.
212+
213+
<box type="tip" seamless>
214+
215+
Glob matching uses the `matchBase` option, so a pattern like `*.java` will match `src/main/App.java` even though the file is nested in subdirectories. You do not need to prefix every pattern with `**/`.
216+
</box>
217+
218+
#### View modes
219+
220+
The global file browser offers two view modes, toggled by the buttons below the search input:
221+
222+
##### Sort By Path (default)
223+
224+
Files from all repositories are displayed in a single flat list, sorted alphabetically by file path.
225+
226+
##### Group By Repo
227+
228+
Files are organized under collapsible repository headers, sorted alphabetically by repository name.
229+
230+
Each repository group shows:
231+
* The **repository name** and **total file count** in the group header.
232+
* A **badge** that reads _Click to expand_ or _Click to collapse_, indicating the current state.
233+
* A **preview** of the top 3 matching files when the group is collapsed.
234+
* An **"and N more file(s)..."** link if the repository has more than 3 matching files. Clicking this link lists all remaining files (without expanding their contents).
235+
236+
**Expanding all files in a repository group:** Click the **repository group header** to expand (or collapse) _all_ files in that group at once. This opens every file's content simultaneously, which is useful for quickly scanning an entire repository. Each repository group can be expanded and collapsed independently.
237+
238+
<box type="info" seamless>
239+
240+
Switching between _Sort By Path_ and _Group By Repo_ collapses all currently expanded files.
241+
</box>
242+
243+
#### Viewing file contents
244+
245+
Click on any non-binary, non-ignored file header to expand it and view its contents. Click the same header again to collapse it.
246+
247+
The file content is displayed as **author-colored segments**:
248+
* Each contiguous block of lines written by the same author is shown as a segment with a **colored left border** identifying the author.
249+
* Hover over a segment to see a **tooltip** showing the author's name and whether they are the primary author or a co-author.
250+
251+
<box type="info" seamless>
252+
253+
Binary files (such as images, executables, and archives) and ignored files cannot be expanded. These files are indicated with yellow **binary** or **ignored** badges.
254+
</box>
255+
256+
#### Use cases
257+
258+
The global file browser is particularly useful for:
259+
260+
* **Cross-repository code search**: Find files matching a specific pattern (e.g., `*.yml` for CI configs) across all repositories.
261+
* **Code review**: Quickly browse all files of a specific type and see who wrote each line.
262+
* **Comparing implementations**: Filter for files with similar names or extensions across different repositories to compare how they are implemented.
263+
* **Identifying authorship**: Expand files to see author-colored segments, making it easy to identify who contributed which parts of a file.
264+
265+
<box type="tip" seamless>
266+
267+
Combine glob patterns with the Group By Repo view to see how a specific file type is distributed across repositories. For example, filter by `**/*Test*.java` and switch to Group By Repo to see which repositories have Java test files and how many.
268+
</box>

frontend/cypress/tests/chartView/chartView_optimiseTimeline.cy.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ describe('optimise timeline', () => {
122122
it('subzoom panel range should work correctly when timeline is optimised', () => {
123123
const zoomKey = Cypress.platform === 'darwin' ? '{meta}' : '{ctrl}';
124124

125+
// Set granularity to commit to show individual commit slices
126+
cy.get('div.mui-select.granularity > select:visible')
127+
.select('Commit');
128+
125129
cy.get('#summary label.optimise-timeline > input:visible')
126130
.should('be.visible')
127131
.check()

frontend/cypress/tests/chartView/chartView_reload.cy.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ describe('reload page', () => {
55
.should('be.visible')
66
.type('eugene');
77

8-
// filter file
9-
cy.get('.tooltip .mui-textfield.filter_file > input:visible')
10-
.should('be.visible');
11-
128
// group by
139
cy.get('div.mui-select.grouping > select:visible')
1410
.select('groupByAuthors');

frontend/cypress/tests/chartView/chartView_toolBar_searchBox.cy.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
describe('search bar', () => {
22
it('non-existent author shows no result', () => {
33
cy.get('#app #tab-resize .tab-close').click();
4-
// First one is the global file search, second one is the search for repos
4+
// Get the search box (now the only text input after filter mode became a dropdown)
55
cy.get('#summary-wrapper input[type=text]')
6-
.eq(1)
6+
.eq(0)
77
.type('abcdef')
88
.type('{enter}');
99

@@ -20,7 +20,7 @@ describe('search bar', () => {
2020
it('unique author shows one result', () => {
2121
cy.get('#app #tab-resize .tab-close').click();
2222
cy.get('#summary-wrapper input[type=text]')
23-
.eq(1)
23+
.eq(0)
2424
.type('Metta')
2525
.type('{enter}');
2626

@@ -36,7 +36,7 @@ describe('search bar', () => {
3636
it('searching by non-existent tag shows no results', () => {
3737
cy.get('#app #tab-resize .tab-close').click();
3838
cy.get('#summary-wrapper input[type=text]')
39-
.eq(1)
39+
.eq(0)
4040
.type('tag: asdfghjkl')
4141
.type('{enter}');
4242

@@ -50,7 +50,7 @@ describe('search bar', () => {
5050
it("searching tag that only exists in one author's commits shows one result", () => {
5151
cy.get('#app #tab-resize .tab-close').click();
5252
cy.get('#summary-wrapper input[type=text]')
53-
.eq(1)
53+
.eq(0)
5454
.type('tag: v1.8')
5555
.type('{enter}');
5656

@@ -73,7 +73,7 @@ describe('search bar', () => {
7373
it("searching tag that only exists in two authors' commits shows two results", () => {
7474
cy.get('#app #tab-resize .tab-close').click();
7575
cy.get('#summary-wrapper input[type=text]')
76-
.eq(1)
76+
.eq(0)
7777
.type('tag: v1.10')
7878
.type('{enter}');
7979

@@ -97,7 +97,7 @@ describe('search bar', () => {
9797
it("search field doesn't start with 'tag:' prefix but still contains it shows no results", () => {
9898
cy.get('#app #tab-resize .tab-close').click();
9999
cy.get('#summary-wrapper input[type=text]')
100-
.eq(1)
100+
.eq(0)
101101
.type('v1.10 tag: v1.10')
102102
.type('{enter}');
103103

@@ -111,7 +111,7 @@ describe('search bar', () => {
111111
it("search field doesn't contain 'tag:' at all shows no results", () => {
112112
cy.get('#app #tab-resize .tab-close').click();
113113
cy.get('#summary-wrapper input[type=text]')
114-
.eq(1)
114+
.eq(0)
115115
.type('v1.10')
116116
.type('{enter}');
117117

@@ -125,7 +125,7 @@ describe('search bar', () => {
125125
it('searching for multiple tags shows results containing all the tags searched', () => {
126126
cy.get('#app #tab-resize .tab-close').click();
127127
cy.get('#summary-wrapper input[type=text]')
128-
.eq(1)
128+
.eq(0)
129129
.type('tag: bb v1.10')
130130
.type('{enter}');
131131

frontend/cypress/tests/codeView/codeView_renderFilterHash.cy.js

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,6 @@ Cypress.on('uncaught:exception', (err) => {
99
});
1010

1111
describe('render filter hash', () => {
12-
it('filter files: url params should persist after change and reload', () => {
13-
cy.get('.tooltip .mui-textfield.filter_file > input:visible')
14-
.should('be.visible')
15-
.invoke('val')
16-
.should('eq', '');
17-
18-
cy.get('.mui-textfield.filter_file > input:visible')
19-
.should('be.visible')
20-
.type('**java**{enter}');
21-
22-
cy.url()
23-
.should('contain', 'filteredFileName=**java**');
24-
25-
cy.reload();
26-
27-
cy.url()
28-
.should('contain', 'filteredFileName=**java**');
29-
})
3012

3113
it('search: url params should persist after change and reload', () => {
3214
/* Check initial state */

0 commit comments

Comments
 (0)