Skip to content

Commit

Permalink
Select between offsets (#470)
Browse files Browse the repository at this point in the history
* added select between 2 offsets command

* refactoring select between offsets command

* saving a reference to current HexDocument so we can access it later

* setting default value of the 'from offset' to the selected offset

* added helper functions to create inputbox listeners

* acquiring active hex document from registry instead of saving a static reference

* refactoring multi step input for select between offsets feature
  • Loading branch information
IngilizAdam authored Jan 9, 2024
1 parent 3bad0a5 commit 35dab02
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 1 deletion.
9 changes: 9 additions & 0 deletions media/editor/dataDisplayContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,15 @@ export class DisplayContext {
this.setSelectionRanges([Range.single(msg.offset)]);
});

registerHandler(MessageType.SetFocusedByteRange, msg => {
if (!document.hasFocus()) {
window.focus();
}

this.focusedElement = new FocusedElement(false, msg.startingOffset);
this.setSelectionRanges([Range.inclusive(msg.startingOffset, msg.endingOffset)]);
});

this.selectionChangeEmitter.addListener(() => this.publishSelections());
}

Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@
{
"command": "hexEditor.goToOffset",
"title": "Hex Editor: Go To Offset"
},
{
"command": "hexEditor.selectBetweenOffsets",
"title": "Hex Editor: Select Between Offsets"
}
],
"viewsContainers": {
Expand Down
9 changes: 9 additions & 0 deletions shared/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const enum MessageType {
StashDisplayedOffset,
GoToOffset,
SetFocusedByte,
SetFocusedByteRange,
SetSelectedCount,
PopDisplayedOffset,
//#endregion
Expand Down Expand Up @@ -126,6 +127,13 @@ export interface SetFocusedByteMessage {
offset: number;
}

/** Focuses a byte range in the editor. */
export interface SetFocusedByteRangeMessage {
type: MessageType.SetFocusedByteRange;
startingOffset: number;
endingOffset: number;
}

/** sets the count of selected bytes. */
export interface SetSelectedCountMessage {
type: MessageType.SetSelectedCount;
Expand All @@ -152,6 +160,7 @@ export type ToWebviewMessage =
| GoToOffsetMessage
| SetEditsMessage
| SetFocusedByteMessage
| SetFocusedByteRangeMessage
| PopDisplayedOffsetMessage
| StashDisplayedOffsetMessage;

Expand Down
8 changes: 8 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { DataInspectorView } from "./dataInspectorView";
import { showGoToOffset } from "./goToOffset";
import { HexEditorProvider } from "./hexEditorProvider";
import { HexEditorRegistry } from "./hexEditorRegistry";
import { showSelectBetweenOffsets } from "./selectBetweenOffsets";
import StatusSelectionCount from "./statusSelectionCount";

function readConfigFromPackageJson(extension: vscode.Extension<any>): { extId: string; version: string; aiKey: string } {
Expand Down Expand Up @@ -45,8 +46,15 @@ export function activate(context: vscode.ExtensionContext): void {
showGoToOffset(first.value);
}
});
const selectBetweenOffsetsCommand = vscode.commands.registerCommand("hexEditor.selectBetweenOffsets", () => {
const first = registry.activeMessaging[Symbol.iterator]().next();
if (first.value) {
showSelectBetweenOffsets(first.value, registry);
}
});
context.subscriptions.push(new StatusSelectionCount(registry));
context.subscriptions.push(goToOffsetCommand);
context.subscriptions.push(selectBetweenOffsetsCommand);
context.subscriptions.push(openWithCommand);
context.subscriptions.push(telemetryReporter);
context.subscriptions.push(HexEditorProvider.register(context, telemetryReporter, dataInspectorProvider, registry));
Expand Down
2 changes: 1 addition & 1 deletion src/hexDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,4 +302,4 @@ export class HexDocument extends Disposable implements vscode.CustomDocument {
str = str.toLowerCase();
return str.startsWith("0x") ? parseInt(str.substring(2), 16) : parseInt(str, 10);
}
}
}
89 changes: 89 additions & 0 deletions src/selectBetweenOffsets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as vscode from "vscode";
import { ExtensionHostMessageHandler, MessageType } from "../shared/protocol";
import { ISelectionState } from "./hexDocument";
import { HexEditorRegistry } from "./hexEditorRegistry";

const addressRe = /^0x[a-f0-9]+$/i;
const decimalRe = /^[0-9]+$/i;

export const showSelectBetweenOffsets = async (messaging: ExtensionHostMessageHandler, registry: HexEditorRegistry): Promise<void> => {
messaging.sendEvent({ type: MessageType.StashDisplayedOffset });

let focusedOffset: string | undefined = undefined;

// acquire selection state from active HexDocument
const selectionState: ISelectionState | undefined = registry.activeDocument?.selectionState;

// if there is a selection, use the focused offset as the starting offset
if (selectionState !== undefined && selectionState.selected > 0 && selectionState.focused !== undefined) {
// converting to hex to increase readability
focusedOffset = `0x${selectionState.focused.toString(16)}`;
}

const offset1 = await getOffset("Enter offset to select from", focusedOffset);
if (offset1 !== undefined) {
const offset2 = await getOffset("Enter offset to select until");
if (offset2 !== undefined) {
messaging.sendEvent({ type: MessageType.SetFocusedByteRange, startingOffset: offset1, endingOffset: offset2 });
}
}

async function getOffset(inputBoxTitle: string, value?: string): Promise<number | undefined> {
const disposables: vscode.Disposable[] = [];
try {
return await new Promise<number | undefined>((resolve, _reject) => {
const input = vscode.window.createInputBox();
input.title = inputBoxTitle;
input.value = value || "";
input.prompt = inputBoxTitle;
input.ignoreFocusOut = true;
input.placeholder = inputBoxTitle;
disposables.push(
input.onDidAccept(() => {
const value = input.value;
input.enabled = false;
input.busy = true;
const offset = validate(value);
if (offset !== undefined) {
resolve(offset);
}
input.enabled = true;
input.busy = false;
}),
input.onDidChangeValue(text => {
const offset = validate(text);

if (offset === undefined) {
input.validationMessage = "Offset must be provided as a decimal (12345) or hex (0x12345) address";
}
else {
input.validationMessage = "";
messaging.sendEvent({ type: MessageType.GoToOffset, offset: offset });
}
}),
input.onDidHide(() => {
messaging.sendEvent({ type: MessageType.PopDisplayedOffset });
resolve(undefined);
}),
input
);
input.show();
});
} finally {
disposables.forEach(d => d.dispose());
}

function validate(text: string): number | undefined {
let validatedOffset: number | undefined = undefined;
if (!text) {
validatedOffset = undefined;
} else if (addressRe.test(text)) {
validatedOffset = parseInt(text.slice(2), 16);
} else if (decimalRe.test(text)) {
validatedOffset = parseInt(text, 10);
}

return validatedOffset;
}
}
};

0 comments on commit 35dab02

Please sign in to comment.