From 367275a4e9335e603b730d97f0156a25c0e5b28d Mon Sep 17 00:00:00 2001 From: Davin Shearer Date: Thu, 19 Oct 2023 12:14:23 -0400 Subject: [PATCH] Implemented Indexable ByteValue Indications - Added indexable indication array for ByteValues within the viewport displays. - Created categorical byte indiciations values & CSS selectors. Closes #784 --- .../CustomByteDisplay/DataLineFeed.svelte | 155 ++++++++------ .../CustomByteDisplay/DataValue.svelte | 81 ++------ .../CustomByteDisplay/SelectedByteEdit.svelte | 6 +- .../DataDisplays/Header/DisplayHeader.svelte | 6 +- .../Header/fieldsets/FileMetrics.svelte | 2 +- .../Header/fieldsets/SearchReplace.svelte | 33 ++- .../Header/fieldsets/SearchReplace.ts | 50 +++-- src/svelte/src/components/dataEditor.svelte | 5 +- src/svelte/src/components/globalStyles.css | 2 + src/svelte/src/stores/index.ts | 11 +- src/svelte/src/utilities/display.ts | 1 - src/svelte/src/utilities/highlights.ts | 193 +++++++++++++----- 12 files changed, 320 insertions(+), 225 deletions(-) diff --git a/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte b/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte index 644ec8eae..8955d8733 100644 --- a/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte +++ b/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataLineFeed.svelte @@ -31,6 +31,8 @@ limitations under the License. seekOffsetInput, visableViewports, dataDislayLineAmount, + replaceQuery, + searchResultsUpdated, } from '../../../stores' import { EditByteModes, @@ -62,10 +64,12 @@ limitations under the License. type CSSThemeClass, } from '../../../utilities/colorScheme' import { - selectionHighlights, - searchResultsHighlights, - updateSearchResultsHighlights, - searchResultsUpdated, + CATEGORY_ONE_MASK, + CATEGORY_TWO_MASK, + viewportByteIndicators, + type ByteValueIndications, + IndicatorCategoryShift, + byteCategoryValue, } from '../../../utilities/highlights' import { bytesPerRow } from '../../../stores' export let awaitViewportSeek: boolean @@ -169,7 +173,7 @@ limitations under the License. bytes: Array highlight: 'even' | 'odd' } - + enum ViewportScrollDirection { DECREMENT = -1, NONE = 0, @@ -181,10 +185,13 @@ limitations under the License. let viewportDataContainer: HTMLDivElement let selectedByteElement: HTMLDivElement let themeClass: CSSThemeClass - let activeSelection: Uint8Array let lineTopFileOffset: number - let searchResults: Uint8Array + let makingSelection = false + $: { + makingSelection = + $selectionDataStore.startOffset >= 0 && $selectionDataStore.active === false + } onMount(() => { viewportDataContainer = document.getElementById( CONTAINER_ID @@ -215,13 +222,10 @@ limitations under the License. } $: { - activeSelection = $selectionHighlights - searchResults = $searchResultsHighlights if ( - (viewportData.fileOffset >= 0 && - !awaitViewportSeek && - $dataFeedLineTop >= 0) || - $searchResultsUpdated + viewportData.fileOffset >= 0 && + !awaitViewportSeek && + $dataFeedLineTop >= 0 ) { if ( viewportLines.length !== 0 && @@ -243,6 +247,11 @@ limitations under the License. } } $: byteElementWidth = byteDivWidthFromRadix(dataRadix) + $: { + viewportByteIndicators.updateSelectionIndications($selectionDataStore.startOffset, $selectionDataStore.endOffset) + viewportByteIndicators.updateSearchIndications($searchQuery.searchResults, $searchQuery.byteLength) + viewportByteIndicators.updateReplaceIndications($replaceQuery.results, viewportData.fileOffset) + } function generate_line_data( startIndex: number, @@ -359,21 +368,21 @@ limitations under the License. : atViewportHead && !atFileHead } - function mousedown(event: CustomEvent) { + function mousedown(event: ByteSelectionEvent) { selectionDataStore.update((selections) => { selections.active = false - selections.startOffset = event.detail.targetByte.offset + selections.startOffset = event.targetByte.offset selections.endOffset = -1 selections.originalEndOffset = -1 return selections }) } - function mouseup(event: CustomEvent) { + function mouseup(event: ByteSelectionEvent) { selectionDataStore.update((selections) => { selections.active = true - selections.endOffset = event.detail.targetByte.offset - selections.originalEndOffset = event.detail.targetByte.offset + selections.endOffset = event.targetByte.offset + selections.originalEndOffset = event.targetByte.offset adjust_event_offsets() return selections }) @@ -383,7 +392,7 @@ limitations under the License. return } - set_byte_selection(event.detail) + set_byte_selection(event) } function adjust_event_offsets() { @@ -471,6 +480,48 @@ limitations under the License. } } + function mouseover_handler(e: Event) { + if(!makingSelection) return + + const target = e.target as HTMLDivElement + let targetViewportIndex = parseInt(target.getAttribute('offset')!) + + selectionDataStore.update((selections) => { + selections.endOffset = targetViewportIndex + adjust_event_offsets() + return selections + }) + } + + function mouseclick_handler(e: Event) { + const type = e.type + const targetElement = e.target as HTMLDivElement + let targetViewportIndex = parseInt(targetElement.getAttribute('offset')!) + let byteText: string | undefined = targetElement.innerHTML + let byteValue: number = byteText === undefined ? -1 : parseInt(byteText) + + if (targetElement.id.includes('logical')) byteText = String.fromCharCode(byteValue) + let targetByte: ByteValue = { + offset: targetViewportIndex, + text: byteText, + value: byteValue + } + const byteSelectionEvent: ByteSelectionEvent = + { + targetElement: targetElement, + targetByte: targetByte, + fromViewport: targetElement.id.includes('logical') ? 'logical' : 'physical', + } + + switch(type) { + case 'mousedown': + mousedown(byteSelectionEvent) + break + case 'mouseup': + mouseup(byteSelectionEvent) + } + } + window.addEventListener('keydown', navigation_keydown_event) window.addEventListener('message', (msg) => { switch (msg.data.command) { @@ -485,30 +536,31 @@ limitations under the License. selectedByteElement = document.getElementById( $selectedByte.offset.toString() ) as HTMLDivElement - - updateSearchResultsHighlights( - $searchQuery.searchResults, - viewportData.fileOffset, - $searchQuery.byteLength - ) } break } }) + -{#if $selectionDataStore.active && $editMode == EditByteModes.Single} - {#key $selectedByte || selectedByteElement || dataRadix || $editorActionsAllowed == EditActionRestrictions.None} - - {/key} -{/if} -
+ + + +
+ {#if $selectionDataStore.active && $editMode == EditByteModes.Single} + {#key $selectedByte || selectedByteElement || dataRadix || $editorActionsAllowed == EditActionRestrictions.None} + + {/key} + {/if} {#each viewportLines as viewportLine, i}
@@ -523,17 +575,12 @@ limitations under the License. {#each viewportLine.bytes as byte} > - activeSelection[byte.offset]} id={'physical'} - radix={dataRadix} + indicators={{ + cat1: byteCategoryValue($viewportByteIndicators[byte.offset] & CATEGORY_ONE_MASK, IndicatorCategoryShift.CategoryOne), + cat2: byteCategoryValue($viewportByteIndicators[byte.offset] & CATEGORY_TWO_MASK, IndicatorCategoryShift.CategoryTwo) }} width={byteElementWidth} disabled={byte.value === -1} - bind:selectionData={$selectionDataStore} - on:mouseup={mouseup} - on:mousedown={mousedown} /> {/each}
@@ -546,17 +593,12 @@ limitations under the License. {#each viewportLine.bytes as byte} > - activeSelection[byte.offset]} + indicators={{ + cat1: byteCategoryValue($viewportByteIndicators[byte.offset] & CATEGORY_ONE_MASK, IndicatorCategoryShift.CategoryOne), + cat2: byteCategoryValue($viewportByteIndicators[byte.offset] & CATEGORY_TWO_MASK, IndicatorCategoryShift.CategoryTwo) }} id={'logical'} - radix={dataRadix} width={byteElementWidth} disabled={byte.value === -1} - bind:selectionData={$selectionDataStore} - on:mouseup={mouseup} - on:mousedown={mousedown} /> {/each}
@@ -697,15 +739,6 @@ limitations under the License. flex-direction: column; margin: 0 5px; } - span.submit-bpr-input { - font-size: 14px; - cursor: pointer; - margin: 0 5px; - } - span.submit-bpr-input:hover { - font-weight: bold; - cursor: pointer; - } div.container { display: flex; flex-direction: column; diff --git a/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataValue.svelte b/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataValue.svelte index ab12df431..f8a94298a 100644 --- a/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataValue.svelte +++ b/src/svelte/src/components/DataDisplays/CustomByteDisplay/DataValue.svelte @@ -15,61 +15,23 @@ See the License for the specific language governing permissions and limitations under the License. --> @@ -78,33 +40,22 @@ limitations under the License. {:else if id === 'physical'}
{byte.text}
{:else}
{latin1Undefined(byte.value) ? '' : String.fromCharCode(byte.value)}
@@ -118,7 +69,6 @@ limitations under the License. align-items: center; flex-direction: row; font-family: var(--monospace-font); - /* border-radius: 5px; */ border-style: solid; border-width: 2px; border-color: transparent; @@ -126,21 +76,20 @@ limitations under the License. text-align: center; transition: all 0.25s; } - div.byte.isSelected, + div.byte.selected, div.byte.isSearchResult, - div.byte.possibleSelection { + div.byte.isReplaceResult { border-radius: 5px; } - div.byte.isSelected { + div.byte.selected { background-color: var(--color-secondary-light); color: var(--color-secondary-darkest); } div.byte.isSearchResult { - background-color: var(--color-tertiary-light); - color: var(--color-secondary-darkest); + border-color: var(--color-search-result); } - div.byte.possibleSelection { - border-color: var(--color-secondary-light); + div.byte.isReplaceResult { + border-color: var(--color-replace-result); } div.byte:hover { border-color: var(--color-secondary-mid); diff --git a/src/svelte/src/components/DataDisplays/CustomByteDisplay/SelectedByteEdit.svelte b/src/svelte/src/components/DataDisplays/CustomByteDisplay/SelectedByteEdit.svelte index 08cb5008b..d85e0b13e 100644 --- a/src/svelte/src/components/DataDisplays/CustomByteDisplay/SelectedByteEdit.svelte +++ b/src/svelte/src/components/DataDisplays/CustomByteDisplay/SelectedByteEdit.svelte @@ -343,7 +343,7 @@ limitations under the License. class="delete {themeClass}" id={actionElements['delete'].id} style:width={elementDivWidth} - on:click={send_delete} + on:mousedown|stopPropagation={send_delete} > ✖ @@ -358,7 +358,7 @@ limitations under the License. class="insert-before {themeClass}" id={actionElements['insert-before'].id} style:width={elementDivWidth} - on:click={send_insert} + on:mousedown|stopPropagation={send_insert} > ⇤ @@ -372,7 +372,7 @@ limitations under the License. class="insert-after {themeClass}" id={actionElements['insert-after'].id} style:width={elementDivWidth} - on:click={send_insert} + on:mousedown|stopPropagation={send_insert} > ⇥ diff --git a/src/svelte/src/components/DataDisplays/Header/DisplayHeader.svelte b/src/svelte/src/components/DataDisplays/Header/DisplayHeader.svelte index 4628e7858..86552922b 100644 --- a/src/svelte/src/components/DataDisplays/Header/DisplayHeader.svelte +++ b/src/svelte/src/components/DataDisplays/Header/DisplayHeader.svelte @@ -127,14 +127,14 @@ limitations under the License. {#if $displayRadix === RADIX_OPTIONS.Binary} {#each offsetLine as offset}
-
{offset}
-
{bitIndexStr}
+
{offset}
+
{bitIndexStr}
{/each} {:else} {#each offsetLine as offset}
- {offset} + {offset}
{/each} {/if} diff --git a/src/svelte/src/components/Header/fieldsets/FileMetrics.svelte b/src/svelte/src/components/Header/fieldsets/FileMetrics.svelte index 21dc3b1cc..62ff4c54c 100644 --- a/src/svelte/src/components/Header/fieldsets/FileMetrics.svelte +++ b/src/svelte/src/components/Header/fieldsets/FileMetrics.svelte @@ -19,7 +19,7 @@ limitations under the License. import FlexContainer from '../../layouts/FlexContainer.svelte' import { MessageCommand } from '../../../utilities/message' import { vscode } from '../../../utilities/vscode' - import { saveable, fileMetrics } from '../../../stores' + import { saveable, fileMetrics, replaceQuery } from '../../../stores' import { createEventDispatcher } from 'svelte' import SidePanel from '../../layouts/SidePanel.svelte' import ByteFrequencyGraph from '../../DataMetrics/DataMetrics.svelte' diff --git a/src/svelte/src/components/Header/fieldsets/SearchReplace.svelte b/src/svelte/src/components/Header/fieldsets/SearchReplace.svelte index 12c8c90b4..d73623ea5 100644 --- a/src/svelte/src/components/Header/fieldsets/SearchReplace.svelte +++ b/src/svelte/src/components/Header/fieldsets/SearchReplace.svelte @@ -42,13 +42,8 @@ limitations under the License. import { createEventDispatcher } from 'svelte' import { UIThemeCSSClass } from '../../../utilities/colorScheme' import ToggleableButton from '../../Inputs/Buttons/ToggleableButton.svelte' - import { - clearSearchResultsHighlights, - updateSearchResultsHighlights, - } from '../../../utilities/highlights' - import { viewport } from '../../../stores' import { EditActionRestrictions } from '../../../stores/configuration' - import { OffsetSearchType } from './SearchReplace' + import { OffsetSearchType, clear_queryable_results } from './SearchReplace' import Tooltip from '../../layouts/Tooltip.svelte' const eventDispatcher = createEventDispatcher() @@ -201,7 +196,8 @@ limitations under the License. searchStarted = false replaceStarted = false matchOffset = -1 - clearSearchResultsHighlights() + clear_queryable_results() + eventDispatcher('clearDataDisplays') } @@ -210,25 +206,24 @@ limitations under the License. // handle search results case MessageCommand.searchResults: if (msg.data.data.searchResults.length > 0) { - $searchQuery.searchResults = msg.data.data.searchResults - $searchQuery.byteLength = msg.data.data.searchDataBytesLength + searchQuery.updateSearchResults(msg.data.data) switch (direction) { case 'Home': - hasNext = msg.data.data.overflow + hasNext = $searchQuery.overflow hasPrev = false break case 'End': hasNext = false - hasPrev = msg.data.data.overflow + hasPrev = $searchQuery.overflow break case 'Forward': - hasNext = msg.data.data.overflow + hasNext = $searchQuery.overflow hasPrev = justReplaced ? preReplaceHasPrev : true justReplaced = false break case 'Backward': hasNext = true - hasPrev = msg.data.data.overflow + hasPrev = $searchQuery.overflow break } matchOffset = $searchQuery.searchResults[0] @@ -240,17 +235,12 @@ limitations under the License. showReplaceOptions = true showSearchOptions = false } - $searchQuery.overflow = msg.data.data.overflow } else { matchOffset = -1 $searchQuery.overflow = showSearchOptions = showReplaceOptions = false + searchQuery.clear() } searchStarted = replaceStarted = false - updateSearchResultsHighlights( - $searchQuery.searchResults, - $viewport.fileOffset, - $searchQuery.byteLength - ) $searchQuery.processing = false break @@ -259,8 +249,11 @@ limitations under the License. searchStarted = replaceStarted = false if (msg.data.data.replacementsCount > 0) { // subtract 1 from the next offset because search next will add 1 - clearSearchResultsHighlights() matchOffset = msg.data.data.nextOffset - 1 + replaceQuery.add_result({ + byteLength: msg.data.data.replaceDataBytesLength, + offset: msg.data.data.nextOffset - msg.data.data.replaceDataBytesLength + }) preReplaceHasPrev = hasPrev justReplaced = true searchNext() diff --git a/src/svelte/src/components/Header/fieldsets/SearchReplace.ts b/src/svelte/src/components/Header/fieldsets/SearchReplace.ts index e1f739b94..9ead5dbb0 100644 --- a/src/svelte/src/components/Header/fieldsets/SearchReplace.ts +++ b/src/svelte/src/components/Header/fieldsets/SearchReplace.ts @@ -16,8 +16,7 @@ */ import { SimpleWritable } from '../../../stores/localStore' -import { addressRadix, seekOffsetInput } from '../../../stores' -import { get } from 'svelte/store' +import { replaceQuery, searchQuery } from '../../../stores' export enum OffsetSearchType { ABSOLUTE, @@ -52,34 +51,51 @@ export class SearchQuery extends SimpleWritable { return query }) } - public updateSearchResults(offset?: number) { + public updateSearchResults(msgData: any) { this.update((query) => { - query.searchIndex = !offset - ? Math.abs( - (query.searchResults.length + query.searchIndex) % - query.searchResults.length - ) - : Math.abs( - (query.searchResults.length + offset) % query.searchResults.length - ) - - seekOffsetInput.update((_) => { - return query.searchResults[query.searchIndex].toString( - get(addressRadix) - ) - }) + query.searchResults = msgData.searchResults + query.byteLength = msgData.searchDataBytesLength + query.overflow = msgData.overflow return query }) } } +/** +Object that defines describes an instance of a replacement that occured during a Search & Replace query. +@param offset **File** offset of where the replacement occured. +@param byteLength Byte length of the replacement data. +*/ +export type DataReplacement = { + offset: number + byteLength: number +} + class ReplaceData implements QueryableData { input: string = '' processing: boolean = false isValid: boolean = false + results: Array = [] } export class ReplaceQuery extends SimpleWritable { protected init(): ReplaceData { return new ReplaceData() } + public add_result(result: DataReplacement) { + this.update((data) => { + data.results.push(result) + return data + }) + } + public clear() { + this.update((data) => { + data.results = [] + return data + }) + } +} + +export function clear_queryable_results() { + searchQuery.clear() + replaceQuery.clear() } diff --git a/src/svelte/src/components/dataEditor.svelte b/src/svelte/src/components/dataEditor.svelte index 32b52854b..33d89a12a 100644 --- a/src/svelte/src/components/dataEditor.svelte +++ b/src/svelte/src/components/dataEditor.svelte @@ -61,7 +61,6 @@ limitations under the License. ViewportData_t, } from './DataDisplays/CustomByteDisplay/BinaryData' import { byte_count_divisible_offset } from '../utilities/display' - import { clearSearchResultsHighlights } from '../utilities/highlights' import Help from './layouts/Help.svelte' $: $UIThemeCSSClass = $darkUITheme ? CSSThemeClass.Dark : CSSThemeClass.Light @@ -248,13 +247,13 @@ limitations under the License. function clearQueryableData() { searchQuery.clear() - clearSearchResultsHighlights() } function handleKeyBind(event: Event) { const kbdEvent = event as KeyboardEvent if (key_is_mappable(kbdEvent.key)) { - elementKeypressEventMap.run(document.activeElement.id, kbdEvent) + if(document.activeElement) // document.activeElement is possibly undefined / null + elementKeypressEventMap.run(document.activeElement.id, kbdEvent) return } if ($editMode === EditByteModes.Multiple) return diff --git a/src/svelte/src/components/globalStyles.css b/src/svelte/src/components/globalStyles.css index 916f9b3e3..765897f77 100644 --- a/src/svelte/src/components/globalStyles.css +++ b/src/svelte/src/components/globalStyles.css @@ -60,6 +60,8 @@ html { --color-tertiary-dark: #5f816b; --color-tertiary-darkest: #232f27; --color-alternate-grey: #bbb5bd; + --color-search-result: #69a0a7; + --color-replace-result: #5f816b; } /* Global LEGEND Styles */ diff --git a/src/svelte/src/stores/index.ts b/src/svelte/src/stores/index.ts index 66b3c8ea0..a0e8c14ad 100644 --- a/src/svelte/src/stores/index.ts +++ b/src/svelte/src/stores/index.ts @@ -43,7 +43,6 @@ import { type RadixValues, type BytesPerRow, EditActionRestrictions, - VIEWPORT_CAPACITY_MAX, } from './configuration' import type { AvailableHelpSections } from '../components/layouts/Help' @@ -76,6 +75,10 @@ export enum EditModeRestrictions { OverwriteOnly, } +/**************************************************************************/ +/* Writable Stores */ +/**************************************************************************/ + // noinspection JSUnusedGlobalSymbols // theme to use for the UI @@ -155,6 +158,12 @@ export const dataDislayLineAmount = writable(20) export type VisibleViewports = 'physical' | 'logical' | 'all' export const visableViewports = writable('all' as VisibleViewports) + +export const searchResultsUpdated = writable(false) + +/**************************************************************************/ +/* Derived Stores */ +/**************************************************************************/ // Can the user's selection derive both edit modes? export const regularSizedFile = derived(fileMetrics, ($fileMetrics) => { return $fileMetrics.computedSize >= 2 diff --git a/src/svelte/src/utilities/display.ts b/src/svelte/src/utilities/display.ts index 6b0f522a3..481d5390b 100644 --- a/src/svelte/src/utilities/display.ts +++ b/src/svelte/src/utilities/display.ts @@ -12,7 +12,6 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - import { EditByteModes, type BytesPerRow, diff --git a/src/svelte/src/utilities/highlights.ts b/src/svelte/src/utilities/highlights.ts index bb00644db..7940d6a89 100644 --- a/src/svelte/src/utilities/highlights.ts +++ b/src/svelte/src/utilities/highlights.ts @@ -15,62 +15,157 @@ * limitations under the License. */ -import { derived, readable, writable } from 'svelte/store' -import { selectionDataStore } from '../stores' +import { get } from 'svelte/store' +import { searchResultsUpdated, selectionDataStore, viewport } from '../stores' +import { VIEWPORT_CAPACITY_MAX } from '../stores/configuration' +import { SimpleWritable } from '../stores/localStore' +import type { DataReplacement } from '../components/Header/fieldsets/SearchReplace' -let selectionHighlightLUT = new Uint8Array(1024) -export let selectionHighlightMask = writable(0) +/** `0b0000 1111` */ +export const CATEGORY_ONE_MASK = 0x0f +/** `0b1111 0000` */ +export const CATEGORY_TWO_MASK = 0xf0 -let searchResultsHighlightLUT = new Uint8Array(1024).fill(0) +export type ByteValueIndications = { + cat1: number + cat2: number +} + +export enum IndicatorCategoryShift { + CategoryOne = 0, + CategoryTwo = 4, +} + +/** +`Uint8Array` byte index's map for bits[0-3]: `bbbb 0000`. Category 1 attributes change +the background color of a `ByteValue`. + +- `bbbb 0000`: (0) Viewport byte index has no category 1 attribute. +- `bbbb 0001`: (1) Viewport byte index is a byte within the user's selection range. +*/ +enum CategoryOneIndications { + None = 0, + Selected = 1, +} +export const CategoryOneIndicators = ['', 'selected'] + +/** +`Uint8Array` byte index's map for bits[4-7]: `0000 bbbb`. Category 2 attributes change +the border color of a `ByteValue`. + +- `0000 bbbb`: (0) Viewport byte index has no category 2 attribute. +- `0001 bbbb`: (16) Viewport byte index is a search result. +- `0010 bbbb`: (17) Viewport byte index is a replace result. -export enum HightlightCategoryMasks { +> **NOTE:** bits[4-7] get shifted by 4 bits when querying categories. +*/ +enum CategoryTwoIndications { None = 0, - ActiveSelection = 1, - ConsideredForSelection = 2, - SearchResult = 4, + IsSearchResult = 1, + IsReplaceResult = 2, +} +export const CategoryTwoIndicators = ['', 'isSearchResult', 'isReplaceResult'] + +export function byteCategoryValue( + indicationValue: number, + category: IndicatorCategoryShift +): number { + return indicationValue >> category +} + +type IndexCriteria = { + start: number + end: number + data: any[] } -export const selectionHighlights = derived( - [selectionDataStore, selectionHighlightMask], - ([$selectionData, $selectionHighlightMask]) => { - let start = $selectionData.startOffset - let end = - $selectionHighlightMask === 0 - ? $selectionData.originalEndOffset - : $selectionData.endOffset - if (start > end && end > -1) [start, end] = [end, start] - - for (let i = 0; i < 1024; i++) { - selectionHighlightLUT[i] = - i >= start && i <= end ? 1 << $selectionHighlightMask : 0 +class ViewportByteIndications extends SimpleWritable { + protected init(): Uint8Array { + return new Uint8Array(VIEWPORT_CAPACITY_MAX).fill(0) + } + private obtainIterationCriteria( + data: any[], + viewportFileOffset: number + ): IndexCriteria { + const start = data.findIndex((x) => x >= viewportFileOffset) + const end = data.findIndex( + (x) => x >= viewportFileOffset + VIEWPORT_CAPACITY_MAX + ) + let ret: IndexCriteria = { + start: start, + end: end, + data: data.slice(start, end >= 0 ? end : data.length), } + return ret + } + public clearSearchIndications() { + this.store.update((indications) => { + return indications.map((byte) => { + byte &= ~(byte & CATEGORY_TWO_MASK) + return byte + }) + }) + } + public updateSearchIndications(data: number[], byteWidth: number) { + this.store.update((indications) => { + const viewportFileOffset = get(viewport).fileOffset + const searchCriteria = this.obtainIterationCriteria( + data, + viewportFileOffset + ) + + this.clearSearchIndications() - return selectionHighlightLUT + searchCriteria.data.forEach((offset) => { + for (let i = 0; i < byteWidth; i++) + indications[offset - viewportFileOffset + i] |= + CategoryTwoIndications.IsSearchResult << + IndicatorCategoryShift.CategoryTwo + }) + searchResultsUpdated.set(true) + return indications + }) + } + public updateReplaceIndications( + data: DataReplacement[], + viewportFileOffset: number + ) { + this.update((indications) => { + const criteriaStart = data.findIndex( + (replacementData) => replacementData.offset >= viewportFileOffset + ) + const criteriaEnd = data.findIndex( + (replacementData) => + replacementData.offset >= viewportFileOffset + VIEWPORT_CAPACITY_MAX + ) + const matchingReplacements = data.slice( + criteriaStart, + criteriaEnd >= 0 ? criteriaEnd : data.length + ) + + matchingReplacements.forEach((replacementData) => { + for (let i = 0; i < replacementData.byteLength; i++) + indications[replacementData.offset - viewportFileOffset + i] |= + CategoryTwoIndications.IsReplaceResult << + IndicatorCategoryShift.CategoryTwo + }) + return indications + }) + } + public updateSelectionIndications(start: number, end: number) { + this.store.update((indications) => { + if (start > end && end > -1) [start, end] = [end, start] + + for (let i = 0; i < VIEWPORT_CAPACITY_MAX; i++) { + indications[i] = + i >= start && i <= end + ? CategoryOneIndications.Selected & CATEGORY_ONE_MASK + : CategoryOneIndications.None & CATEGORY_ONE_MASK + } + + return indications + }) } -) - -export const searchResultsHighlights = readable(searchResultsHighlightLUT) -export const searchResultsUpdated = writable(false) -export function updateSearchResultsHighlights( - data: number[], - viewportFileOffset: number, - byteWidth: number -) { - const criteriaStart = data.findIndex((x) => x >= viewportFileOffset) - const criteriaEnd = data.findIndex((x) => x >= viewportFileOffset + 1024) - const searchCriteria = data.slice( - criteriaStart, - criteriaEnd >= 0 ? criteriaEnd : data.length - ) - - searchResultsHighlightLUT.fill(0) - - searchCriteria.forEach((offset) => { - for (let i = 0; i < byteWidth; i++) - searchResultsHighlightLUT[offset - viewportFileOffset + i] = 1 - }) - searchResultsUpdated.set(true) -} -export function clearSearchResultsHighlights() { - searchResultsHighlightLUT.fill(0) } + +export const viewportByteIndicators = new ViewportByteIndications()