diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.ts index 86d6bdf09c6..7bb290a8211 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/code-panel/code-panel.component.ts @@ -38,7 +38,7 @@ export class CodePanelComponent implements OnChanges{ @Input() showLineNumbers: boolean = true; @Input() loadFailed : boolean = false; @Input() codeLineSearchText: string | undefined; - @Input() codeLineNavigationDirection: number | undefined; + @Input() codeLineSearchInfo: CodeLineSearchInfo | undefined = undefined; @Output() hasActiveConversationEmitter : EventEmitter = new EventEmitter(); @Output() codeLineSearchInfoEmitter : EventEmitter = new EventEmitter(); @@ -56,8 +56,6 @@ export class CodePanelComponent implements OnChanges{ searchMatchedRowInfo: Map = new Map(); codeLineSearchMatchInfo : DoublyLinkedList | undefined = undefined; - currentCodeLineSearchMatch: DoublyLinkedListNode | undefined = undefined; - codeLineSearchInfo: CodeLineSearchInfo | undefined = undefined; destroy$ = new Subject(); @@ -103,11 +101,8 @@ export class CodePanelComponent implements OnChanges{ await this.searchCodePanelRowData(this.codeLineSearchText!); } - if (changes['codeLineNavigationDirection']) { - this.navigateToCodeLineWithSearchMatch( - changes['codeLineNavigationDirection'].previousValue, - changes['codeLineNavigationDirection'].currentValue - ); + if (changes['codeLineSearchInfo'] && changes['codeLineSearchInfo'].currentValue != changes['codeLineSearchInfo'].previousValue) { + this.navigateToCodeLineWithSearchMatch(); } } @@ -702,7 +697,6 @@ export class CodePanelComponent implements OnChanges{ if (!searchText || searchText.length === 0) { this.clearSearchMatchHighlights(); this.codeLineSearchMatchInfo = undefined; - this.currentCodeLineSearchMatch = undefined; this.codeLineSearchInfo = undefined; this.codeLineSearchInfoEmitter.emit(this.codeLineSearchInfo); return; @@ -726,34 +720,24 @@ export class CodePanelComponent implements OnChanges{ } } }); - - let currentMatch = 0; - let totalMatchCount = 0; if (hasMatch) { - this.currentCodeLineSearchMatch = this.codeLineSearchMatchInfo.head; + this.codeLineSearchInfo = new CodeLineSearchInfo(this.codeLineSearchMatchInfo.head, this.codeLineSearchMatchInfo.length); - if (this.currentCodeLineSearchMatch?.value.rowIndex! < this.codePanelRowSource?.adapter?.firstVisible.$index! || - this.currentCodeLineSearchMatch?.value.rowIndex! > this.codePanelRowSource?.adapter?.lastVisible.$index!) { + if (this.codeLineSearchInfo.currentMatch?.value.rowIndex! < this.codePanelRowSource?.adapter?.firstVisible.$index! || + this.codeLineSearchInfo.currentMatch?.value.rowIndex! > this.codePanelRowSource?.adapter?.lastVisible.$index!) { // Scroll first match into view - await this.scrollToNode(this.currentCodeLineSearchMatch!.value.nodeIdHashed, undefined, false, false); + await this.scrollToNode(this.codeLineSearchInfo.currentMatch!.value.nodeIdHashed, undefined, false, false); await this.codePanelRowSource?.adapter?.relax(); } - currentMatch = this.currentCodeLineSearchMatch!.index + 1; - totalMatchCount = this.codeLineSearchMatchInfo.length; this.highlightSearchMatches(); this.highlightActiveSearchMatch(); } else { this.clearSearchMatchHighlights(); this.codeLineSearchMatchInfo = undefined; - this.currentCodeLineSearchMatch = undefined; + this.codeLineSearchInfo = undefined; } - - this.codeLineSearchInfo = { - currentMatch: currentMatch, - totalMatchCount: totalMatchCount - }; this.codeLineSearchInfoEmitter.emit(this.codeLineSearchInfo); } @@ -833,9 +817,9 @@ export class CodePanelComponent implements OnChanges{ } private highlightActiveSearchMatch(scrollIntoView: boolean = true) { - if (this.currentCodeLineSearchMatch) { - const nodeIdHashed = this.currentCodeLineSearchMatch.value.nodeIdHashed; - const matchId = this.currentCodeLineSearchMatch.value.matchId; + if (this.codeLineSearchInfo?.currentMatch) { + const nodeIdHashed = this.codeLineSearchInfo?.currentMatch.value.nodeIdHashed; + const matchId = this.codeLineSearchInfo?.currentMatch.value.matchId; const activeMatch = this.elementRef.nativeElement.querySelector('.codeline-search-match-highlight.active'); if (activeMatch) { @@ -860,31 +844,16 @@ export class CodePanelComponent implements OnChanges{ /** * Navigates to the next or previous code line that contains a search match but is outside the viewport */ - private navigateToCodeLineWithSearchMatch(previousPosition: number, newPosition: number) { - if (this.currentCodeLineSearchMatch) { + private navigateToCodeLineWithSearchMatch() { + if (this.codeLineSearchInfo?.currentMatch) { const firstVisibleIndex = this.codePanelRowSource?.adapter?.firstVisible.$index!; const lastVisibleIndex = this.codePanelRowSource?.adapter?.lastVisible.$index!; - let currentMatch = this.codeLineSearchInfo?.currentMatch!; - - if (!previousPosition || newPosition > previousPosition) { - this.currentCodeLineSearchMatch = this.currentCodeLineSearchMatch?.next!; - currentMatch++; - } else if (newPosition < previousPosition) { - this.currentCodeLineSearchMatch = this.currentCodeLineSearchMatch?.prev!; - currentMatch--; - } - if (this.currentCodeLineSearchMatch && (this.currentCodeLineSearchMatch.value.rowIndex < firstVisibleIndex || this.currentCodeLineSearchMatch.value.rowIndex > lastVisibleIndex)) { - this.scrollToNode(this.currentCodeLineSearchMatch.value.nodeIdHashed, undefined, false, false); + if (this.codeLineSearchInfo?.currentMatch && (this.codeLineSearchInfo?.currentMatch.value.rowIndex < firstVisibleIndex || this.codeLineSearchInfo?.currentMatch.value.rowIndex > lastVisibleIndex)) { + this.scrollToNode(this.codeLineSearchInfo?.currentMatch.value.nodeIdHashed, undefined, false, false); this.codePanelRowSource?.adapter?.relax(); } - this.highlightActiveSearchMatch(); - this.codeLineSearchInfo = { - currentMatch: currentMatch, - totalMatchCount: this.codeLineSearchInfo?.totalMatchCount - }; - this.codeLineSearchInfoEmitter.emit(this.codeLineSearchInfo); } } @@ -963,7 +932,7 @@ export class CodePanelComponent implements OnChanges{ const viewport = this.elementRef.nativeElement.ownerDocument.getElementById('viewport'); if (viewport) { viewport.addEventListener('scroll', (event) => { - if (this.currentCodeLineSearchMatch) { + if (this.codeLineSearchInfo?.currentMatch) { this.highlightSearchMatches(); this.highlightActiveSearchMatch(false); } diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.html b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.html index a94db26d439..000d68a2441 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.html +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.html @@ -75,7 +75,7 @@
- {{ codeLineSearchInfo.currentMatch }} of {{ codeLineSearchInfo.totalMatchCount }} + {{ (codeLineSearchInfo.currentMatch?.index ?? 0) + 1 }} of {{ codeLineSearchInfo.totalMatchCount }} diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.ts index d23297bdc8f..6eac738afd0 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page-options/review-page-options.component.ts @@ -50,7 +50,7 @@ export class ReviewPageOptionsComponent implements OnInit, OnChanges { @Output() diffNavaigationEmitter : EventEmitter = new EventEmitter(); @Output() copyReviewTextEmitter : EventEmitter = new EventEmitter(); @Output() codeLineSearchTextEmitter : EventEmitter = new EventEmitter(); - @Output() codeLineSearchNaviationEmmiter : EventEmitter = new EventEmitter(); + @Output() codeLineSearchInfoEmitter : EventEmitter = new EventEmitter(); private destroy$ = new Subject(); @@ -374,9 +374,17 @@ export class ReviewPageOptionsComponent implements OnInit, OnChanges { * @param number */ navigateSearch(number: 1 | -1) { - const navigationPosition = this.codeLineSearchInfo?.currentMatch! + number; - if (navigationPosition >= 1 && navigationPosition <= this.codeLineSearchInfo?.totalMatchCount!) { - this.codeLineSearchNaviationEmmiter.emit(navigationPosition!); + if (number == 1) { + if (!this.codeLineSearchInfo?.currentMatch?.isTail()) { + this.codeLineSearchInfo!.currentMatch = this.codeLineSearchInfo?.currentMatch?.next; + this.codeLineSearchInfoEmitter.emit(this.codeLineSearchInfo!); + } + } + else { + if (!this.codeLineSearchInfo?.currentMatch?.isHead()) { + this.codeLineSearchInfo!.currentMatch = this.codeLineSearchInfo?.currentMatch?.prev; + this.codeLineSearchInfoEmitter.emit(this.codeLineSearchInfo!); + } } } diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.html b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.html index 2edb967ffe8..c4e32d900ea 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.html +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.html @@ -30,7 +30,7 @@ [showLineNumbers]="showLineNumbers" [scrollToNodeIdHashed]="scrollToNodeIdHashed" [scrollToNodeId]="scrollToNodeId" [codeLineSearchText]="codeLineSearchText" - [codeLineNavigationDirection]="codeLineNavigationDirection" + [codeLineSearchInfo]="codeLineSearchInfo" (hasActiveConversationEmitter)="handleHasActiveConversationEmitter($event)" (codeLineSearchInfoEmitter)="handleCodeLineSearchInfoEmitter($event)">
@@ -66,7 +66,7 @@ (diffNavaigationEmitter)="handleDiffNavaigationEmitter($event)" (copyReviewTextEmitter)="handleCopyReviewTextEmitter($event)" (codeLineSearchTextEmitter)="handleCodeLineSearchTextEmitter($event)" - (codeLineSearchNaviationEmmiter)="handleCodeLineSearchNavigationEmitter($event)"> + (codeLineSearchInfoEmitter)="handleCodeLineSearchInfoEmitter($event)"> diff --git a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts index 37ae132a06e..4934d1ea7df 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_components/review-page/review-page.component.ts @@ -53,7 +53,7 @@ export class ReviewPageComponent implements OnInit { preferredApprovers : string[] = []; hasFatalDiagnostics : boolean = false; hasActiveConversation : boolean = false; - codeLineSearchInfo : CodeLineSearchInfo | undefined = new CodeLineSearchInfo(); + codeLineSearchInfo : CodeLineSearchInfo | undefined; numberOfActiveConversation : number = 0; hasHiddenAPIs : boolean = false; hasHiddenAPIThatIsDiff : boolean = false; @@ -72,7 +72,6 @@ export class ReviewPageComponent implements OnInit { lastNodeIdUnhashedDiscarded = ''; codeLineSearchText: string | undefined = undefined; - codeLineNavigationDirection: number | undefined = undefined; private destroy$ = new Subject(); private destroyLoadAPIRevision$ : Subject | null = null; @@ -491,16 +490,14 @@ export class ReviewPageComponent implements OnInit { this.codeLineSearchText = searchText; } - handleCodeLineSearchNavigationEmitter(direction: number) { - this.codeLineNavigationDirection = direction; - } - handleHasActiveConversationEmitter(value: boolean) { this.hasActiveConversation = value; } handleCodeLineSearchInfoEmitter(value: CodeLineSearchInfo) { - this.codeLineSearchInfo = value; + setTimeout(() => { + this.codeLineSearchInfo = (value) ? new CodeLineSearchInfo(value.currentMatch, value.totalMatchCount) : undefined; + }, 0); } handleNumberOfActiveThreadsEmitter(value: number) { diff --git a/src/dotnet/APIView/ClientSPA/src/app/_helpers/doubly-linkedlist.ts b/src/dotnet/APIView/ClientSPA/src/app/_helpers/doubly-linkedlist.ts index 72935dafad5..fe251434512 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_helpers/doubly-linkedlist.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_helpers/doubly-linkedlist.ts @@ -29,4 +29,12 @@ export class DoublyLinkedListNode { this.value = value; this.index = index; } + + isHead(): boolean { + return this.prev === undefined; + } + + isTail(): boolean { + return this.next === undefined; + } } \ No newline at end of file diff --git a/src/dotnet/APIView/ClientSPA/src/app/_models/codeLineSearchInfo.ts b/src/dotnet/APIView/ClientSPA/src/app/_models/codeLineSearchInfo.ts index 55bb7405af4..321330c80c7 100644 --- a/src/dotnet/APIView/ClientSPA/src/app/_models/codeLineSearchInfo.ts +++ b/src/dotnet/APIView/ClientSPA/src/app/_models/codeLineSearchInfo.ts @@ -1,6 +1,13 @@ +import { DoublyLinkedListNode } from "../_helpers/doubly-linkedlist"; + export class CodeLineSearchInfo { - currentMatch?: number; - totalMatchCount?: number + currentMatch?: DoublyLinkedListNode | undefined; + totalMatchCount?: number; + + constructor(currentMatch: DoublyLinkedListNode | undefined, totalMatchCount: number | undefined) { + this.currentMatch = currentMatch; + this.totalMatchCount = totalMatchCount; + } } export class CodeLineSearchMatch {