From 6909b420c45a2d3e747c11f265e043c38e257ebe Mon Sep 17 00:00:00 2001 From: lorsanta Date: Sun, 24 Mar 2024 15:57:35 +0100 Subject: [PATCH 1/5] add copy selection as different formats and paste hexstring support --- media/editor/copyPaste.tsx | 6 +- media/editor/dataDisplayContext.tsx | 9 +++ package.json | 19 ++++- package.nls.json | 1 + shared/protocol.ts | 21 ++++- src/copyAs.ts | 118 ++++++++++++++++++++++++++++ src/extension.ts | 9 +++ src/hexEditorProvider.ts | 50 ++++++++++-- 8 files changed, 225 insertions(+), 8 deletions(-) create mode 100644 src/copyAs.ts diff --git a/media/editor/copyPaste.tsx b/media/editor/copyPaste.tsx index b2a7883..a676287 100644 --- a/media/editor/copyPaste.tsx +++ b/media/editor/copyPaste.tsx @@ -16,23 +16,27 @@ const style = throwOnUndefinedAccessInDev(_style); const enum Encoding { Base64 = "base64", Utf8 = "utf-8", + Hex = "hex", } -const encodings = [Encoding.Utf8, Encoding.Base64]; +const encodings = [Encoding.Utf8, Encoding.Base64, Encoding.Hex]; const encodingLabel: { [key in Encoding]: string } = { [Encoding.Base64]: "Base64", [Encoding.Utf8]: "UTF-8", + [Encoding.Hex]: "Hex", }; const isData: { [key in Encoding]: (data: string) => boolean } = { [Encoding.Base64]: d => base64.isValid(d), [Encoding.Utf8]: () => true, + [Encoding.Hex]: () => true, }; const decode: { [key in Encoding]: (data: string) => Uint8Array } = { [Encoding.Base64]: d => base64.toUint8Array(d), [Encoding.Utf8]: d => new TextEncoder().encode(d), + [Encoding.Hex]: d => Uint8Array.from(d.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16))), }; const EncodingOption: React.FC<{ diff --git a/media/editor/dataDisplayContext.tsx b/media/editor/dataDisplayContext.tsx index 9ea7488..46444a0 100644 --- a/media/editor/dataDisplayContext.tsx +++ b/media/editor/dataDisplayContext.tsx @@ -243,6 +243,15 @@ export class DisplayContext { this.setSelectionRanges([Range.inclusive(msg.startingOffset, msg.endingOffset)]); }); + registerHandler(MessageType.TriggerCopyAs, msg => { + messageHandler.sendEvent({ + type: MessageType.DoCopy, + selections: this.selection.map(r => [r.start, r.end]), + asText: false, + format: msg.format, + }); + }); + this.selectionChangeEmitter.addListener(() => this.publishSelections()); } diff --git a/package.json b/package.json index d041d6e..e5a85a1 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,11 @@ "command": "hexEditor.selectBetweenOffsets", "category": "%name%", "title": "%hexEditor.selectBetweenOffsets%" + }, + { + "command": "hexEditor.copyAs", + "category": "%name%", + "title": "%hexEditor.copyAs%" } ], "viewsContainers": { @@ -147,6 +152,13 @@ "command": "hexEditor.openFile", "group": "navigation@1" } + ], + "webview/context": [ + { + "when": "webviewId == 'hexEditor.hexedit'", + "command": "hexEditor.copyAs", + "group": "9_cutcopypaste" + } ] }, "keybindings": [ @@ -154,6 +166,11 @@ "command": "hexEditor.goToOffset", "key": "ctrl+g", "when": "activeCustomEditorId == hexEditor.hexedit" + }, + { + "command": "hexEditor.copyAs", + "key": "alt+ctrl+c", + "when": "activeCustomEditorId == hexEditor.hexedit" } ] }, @@ -200,4 +217,4 @@ "tabWidth": 2, "arrowParens": "avoid" } -} \ No newline at end of file +} diff --git a/package.nls.json b/package.nls.json index ccdd5cd..8ed11ef 100644 --- a/package.nls.json +++ b/package.nls.json @@ -12,5 +12,6 @@ "hexEditor.openFile": "Open Active File in Hex Editor", "hexEditor.goToOffset": "Go To Offset", "hexEditor.selectBetweenOffsets": "Select Between Offsets", + "hexEditor.copyAs": "Copy As", "dataInspectorView": "Data Inspector" } diff --git a/shared/protocol.ts b/shared/protocol.ts index d1320c2..6d1b457 100644 --- a/shared/protocol.ts +++ b/shared/protocol.ts @@ -19,6 +19,7 @@ export const enum MessageType { SetSelectedCount, PopDisplayedOffset, DeleteAccepted, + TriggerCopyAs, //#endregion //#region from webview ReadyRequest, @@ -160,6 +161,22 @@ export interface DeleteAcceptedMessage { type: MessageType.DeleteAccepted; } +export const enum CopyFormat { + Hex = "Hex", + Literal = "Literal", + Text = "Text", + C = "C", + Golang = "Golang", + Java = "Java", + JSON = "JSON", + Base64 = "Base64", +} + +export interface TriggerCopyAsMessage { + type: MessageType.TriggerCopyAs; + format: CopyFormat; +} + export type ToWebviewMessage = | ReadyResponseMessage | ReadRangeResponseMessage @@ -172,7 +189,8 @@ export type ToWebviewMessage = | SetFocusedByteRangeMessage | PopDisplayedOffsetMessage | StashDisplayedOffsetMessage - | DeleteAcceptedMessage; + | DeleteAcceptedMessage + | TriggerCopyAsMessage; export interface OpenDocumentMessage { type: MessageType.OpenDocument; @@ -238,6 +256,7 @@ export interface CopyMessage { type: MessageType.DoCopy; selections: [from: number, to: number][]; asText: boolean; + format?: CopyFormat; } export interface RequestDeletesMessage { diff --git a/src/copyAs.ts b/src/copyAs.ts new file mode 100644 index 0000000..72bcba0 --- /dev/null +++ b/src/copyAs.ts @@ -0,0 +1,118 @@ +import * as base64 from "js-base64"; +import * as vscode from "vscode"; +import { CopyFormat, ExtensionHostMessageHandler, MessageType } from "../shared/protocol"; + +interface QuickPickCopyFormat extends vscode.QuickPickItem { + label: CopyFormat; +} + +export const copyAs = async (messaging: ExtensionHostMessageHandler): Promise => { + const formats: QuickPickCopyFormat[] = [ + { label: CopyFormat.Hex }, + { label: CopyFormat.Literal }, + { label: CopyFormat.Text }, + { label: CopyFormat.C }, + { label: CopyFormat.Golang }, + { label: CopyFormat.Java }, + { label: CopyFormat.JSON }, + { label: CopyFormat.Base64 }, + ]; + + vscode.window.showQuickPick(formats, { ignoreFocusOut: true }).then(format => { + if (format) { + messaging.sendEvent({ type: MessageType.TriggerCopyAs, format: format["label"] }); + } + }); +}; + +export function copyAsText(buffer: Uint8Array) { + vscode.env.clipboard.writeText(new TextDecoder().decode(buffer)); +} + +export function copyAsHex(buffer: Uint8Array) { + vscode.env.clipboard.writeText(Buffer.from(buffer).toString("hex")); +} + +export function copyAsLiteral(buffer: Uint8Array) { + let encoded: string = ""; + const digits = Buffer.from(buffer) + .toString("hex") + .match(/.{1,2}/g); + if (digits) { + encoded = "\\x" + digits.join("\\x"); + } + + vscode.env.clipboard.writeText(encoded); +} + +export function copyAsC(buffer: Uint8Array) { + const len = buffer.length; + let content: string = "unsigned char rawData[" + len + "] =\n{"; + + for (let i = 0; i < len; ++i) { + if (i % 8 == 0) { + content += "\n\t"; + } + const byte = buffer[i].toString(16); + content += (byte.length < 2 ? "0x0" : "0x") + byte + ", "; + } + + content += "\n};\n"; + + if (/^win/.test(process.platform)) { + content = content.replace(/\n/g, "\r\n"); + } + + vscode.env.clipboard.writeText(content); +} + +export function copyAsGolang(buffer: Uint8Array) { + const len = buffer.length; + let content: string = "// RawData (" + len + " bytes)\n"; + content += "var RawData = []byte{"; + + for (let i = 0; i < len; ++i) { + if (i % 8 == 0) { + content += "\n\t"; + } + const byte = buffer[i].toString(16); + content += (byte.length < 2 ? "0x0" : "0x") + byte + ", "; + } + + content += "\n}\n"; + + if (/^win/.test(process.platform)) { + content = content.replace(/\n/g, "\r\n"); + } + + vscode.env.clipboard.writeText(content); +} + +export function copyAsJava(buffer: Uint8Array) { + const len = buffer.length; + let content: string = "byte rawData[] =\n{"; + + for (let i = 0; i < len; ++i) { + if (i % 8 == 0) { + content += "\n\t"; + } + const byte = buffer[i].toString(16); + content += (byte.length < 2 ? "0x0" : "0x") + byte + ", "; + } + + content += "\n};\n"; + + if (/^win/.test(process.platform)) { + content = content.replace(/\n/g, "\r\n"); + } + + vscode.env.clipboard.writeText(content); +} + +export function copyAsJSON(buffer: Uint8Array) { + vscode.env.clipboard.writeText(JSON.stringify(buffer)); +} + +export function copyAsBase64(buffer: Uint8Array) { + vscode.env.clipboard.writeText(base64.fromUint8Array(buffer)); +} diff --git a/src/extension.ts b/src/extension.ts index 563e7c6..2c44933 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,6 +3,7 @@ import TelemetryReporter from "@vscode/extension-telemetry"; import * as vscode from "vscode"; +import { copyAs } from "./copyAs"; import { DataInspectorView } from "./dataInspectorView"; import { showGoToOffset } from "./goToOffset"; import { HexEditorProvider } from "./hexEditorProvider"; @@ -69,6 +70,14 @@ export function activate(context: vscode.ExtensionContext): void { } }, ); + + context.subscriptions.push(vscode.commands.registerCommand("hexEditor.copyAs", () => { + const first = registry.activeMessaging[Symbol.iterator]().next(); + if (first.value) { + copyAs(first.value); + } + })); + context.subscriptions.push(new StatusSelectionCount(registry)); context.subscriptions.push(goToOffsetCommand); context.subscriptions.push(selectBetweenOffsetsCommand); diff --git a/src/hexEditorProvider.ts b/src/hexEditorProvider.ts index 13ad13b..87983c2 100644 --- a/src/hexEditorProvider.ts +++ b/src/hexEditorProvider.ts @@ -2,7 +2,6 @@ // Licensed under the MIT license. import TelemetryReporter from "@vscode/extension-telemetry"; -import * as base64 from "js-base64"; import * as vscode from "vscode"; import { HexDocumentEdit, @@ -10,6 +9,7 @@ import { HexDocumentEditReference, } from "../shared/hexDocumentModel"; import { + CopyFormat, Endianness, ExtensionHostMessageHandler, FromWebviewMessage, @@ -23,6 +23,16 @@ import { } from "../shared/protocol"; import { deserializeEdits, serializeEdits } from "../shared/serialization"; import { ILocalizedStrings, placeholder1 } from "../shared/strings"; +import { + copyAsBase64, + copyAsC, + copyAsGolang, + copyAsHex, + copyAsJSON, + copyAsJava, + copyAsLiteral, + copyAsText, +} from "./copyAs"; import { DataInspectorView } from "./dataInspectorView"; import { disposeAll } from "./dispose"; import { HexDocument } from "./hexDocument"; @@ -352,10 +362,40 @@ export class HexEditorProvider implements vscode.CustomEditorProvider document.readBuffer(s[0], s[1] - s[0])), ); const flatParts = flattenBuffers(parts); - const encoded = message.asText - ? new TextDecoder().decode(flatParts) - : base64.fromUint8Array(flatParts); - vscode.env.clipboard.writeText(encoded); + if (message.format !== undefined) { + switch (message.format) { + case CopyFormat.Hex: + copyAsHex(flatParts); + break; + case CopyFormat.Literal: + copyAsLiteral(flatParts); + break; + case CopyFormat.Text: + copyAsText(flatParts); + break; + case CopyFormat.C: + copyAsC(flatParts); + break; + case CopyFormat.Golang: + copyAsGolang(flatParts); + break; + case CopyFormat.Java: + copyAsJava(flatParts); + break; + case CopyFormat.JSON: + copyAsJSON(flatParts); + break; + case CopyFormat.Base64: + copyAsBase64(flatParts); + break; + } + } else { + if (message.asText) { + copyAsText(flatParts); + } else { + copyAsBase64(flatParts); + } + } return; } case MessageType.RequestDeletes: { From d8d5f20f9bdee4206cfdf88e1a988c9d9b1e1e2f Mon Sep 17 00:00:00 2001 From: lorsanta Date: Fri, 3 May 2024 18:19:54 +0200 Subject: [PATCH 2/5] fix formatting issues --- package.json | 1 - src/extension.ts | 39 +++++++++++++++++---------------------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index f6f160e..3b81d39 100644 --- a/package.json +++ b/package.json @@ -186,7 +186,6 @@ "key": "Insert", "when": "hexEditor:isActive" } - } ] }, "scripts": { diff --git a/src/extension.ts b/src/extension.ts index 7207cbf..98b9c20 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,8 +3,8 @@ import TelemetryReporter from "@vscode/extension-telemetry"; import * as vscode from "vscode"; -import { copyAs } from "./copyAs"; import { HexDocumentEditOp } from "../shared/hexDocumentModel"; +import { copyAs } from "./copyAs"; import { DataInspectorView } from "./dataInspectorView"; import { showGoToOffset } from "./goToOffset"; import { HexEditorProvider } from "./hexEditorProvider"; @@ -73,36 +73,31 @@ export function activate(context: vscode.ExtensionContext): void { } }, ); - const copyAsCommand = vscode.commands.registerCommand( - "hexEditor.copyAs", - () => { - const first = registry.activeMessaging[Symbol.iterator]().next(); - if (first.value) { - copyAs(first.value); - } - }); - const switchEditModeCommand = vscode.commands.registerCommand( - "hexEditor.switchEditMode", - () => { - if (registry.activeDocument) { - registry.activeDocument.editMode = - registry.activeDocument.editMode === HexDocumentEditOp.Insert - ? HexDocumentEditOp.Replace - : HexDocumentEditOp.Insert; - } - }, - ); + const copyAsCommand = vscode.commands.registerCommand("hexEditor.copyAs", () => { + const first = registry.activeMessaging[Symbol.iterator]().next(); + if (first.value) { + copyAs(first.value); + } + }); + const switchEditModeCommand = vscode.commands.registerCommand("hexEditor.switchEditMode", () => { + if (registry.activeDocument) { + registry.activeDocument.editMode = + registry.activeDocument.editMode === HexDocumentEditOp.Insert + ? HexDocumentEditOp.Replace + : HexDocumentEditOp.Insert; + } + }); context.subscriptions.push(new StatusEditMode(registry)); context.subscriptions.push(new StatusFocus(registry)); context.subscriptions.push(new StatusHoverAndSelection(registry)); context.subscriptions.push(goToOffsetCommand); context.subscriptions.push(selectBetweenOffsetsCommand); - context.subscriptions.push(copyAsCommand); + context.subscriptions.push(copyAsCommand); context.subscriptions.push(switchEditModeCommand); context.subscriptions.push(openWithCommand); context.subscriptions.push(telemetryReporter); - context.subscriptions.push( + context.subscriptions.push( HexEditorProvider.register(context, telemetryReporter, dataInspectorProvider, registry), ); } From 618f6f87eb4c6024fd3b309124940422b8cf388b Mon Sep 17 00:00:00 2001 From: lorsanta Date: Tue, 7 May 2024 23:00:36 +0200 Subject: [PATCH 3/5] add requested changes from review --- media/editor/dataDisplay.tsx | 9 ++++- media/editor/dataDisplayContext.tsx | 1 - shared/protocol.ts | 7 ++-- src/copyAs.ts | 38 +++++++++---------- src/hexEditorProvider.ts | 57 +++++++++++------------------ 5 files changed, 51 insertions(+), 61 deletions(-) diff --git a/media/editor/dataDisplay.tsx b/media/editor/dataDisplay.tsx index 38238e8..93054bf 100644 --- a/media/editor/dataDisplay.tsx +++ b/media/editor/dataDisplay.tsx @@ -4,7 +4,12 @@ import React, { Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useRecoilValue, useSetRecoilState } from "recoil"; import { EditRangeOp, HexDocumentEditOp } from "../../shared/hexDocumentModel"; -import { DeleteAcceptedMessage, InspectorLocation, MessageType } from "../../shared/protocol"; +import { + CopyFormat, + DeleteAcceptedMessage, + InspectorLocation, + MessageType, +} from "../../shared/protocol"; import { Range } from "../../shared/util/range"; import { PastePopup } from "./copyPaste"; import _style from "./dataDisplay.css"; @@ -288,7 +293,7 @@ export const DataDisplay: React.FC = () => { select.messageHandler.sendEvent({ type: MessageType.DoCopy, selections: ctx.selection.map(r => [r.start, r.end]), - asText: ctx.focusedElement.char, + format: ctx.focusedElement.char ? CopyFormat.Utf8 : CopyFormat.Base64, }); } }); diff --git a/media/editor/dataDisplayContext.tsx b/media/editor/dataDisplayContext.tsx index 1c7ee37..be5414e 100644 --- a/media/editor/dataDisplayContext.tsx +++ b/media/editor/dataDisplayContext.tsx @@ -247,7 +247,6 @@ export class DisplayContext { messageHandler.sendEvent({ type: MessageType.DoCopy, selections: this.selection.map(r => [r.start, r.end]), - asText: false, format: msg.format, }); }); diff --git a/shared/protocol.ts b/shared/protocol.ts index bb3fdf4..3be1d03 100644 --- a/shared/protocol.ts +++ b/shared/protocol.ts @@ -179,9 +179,9 @@ export interface DeleteAcceptedMessage { export const enum CopyFormat { Hex = "Hex", Literal = "Literal", - Text = "Text", + Utf8 = "UTF-8", C = "C", - Golang = "Golang", + Go = "Go", Java = "Java", JSON = "JSON", Base64 = "Base64", @@ -271,8 +271,7 @@ export interface PasteMessage { export interface CopyMessage { type: MessageType.DoCopy; selections: [from: number, to: number][]; - asText: boolean; - format?: CopyFormat; + format: CopyFormat; } export interface RequestDeletesMessage { diff --git a/src/copyAs.ts b/src/copyAs.ts index 72bcba0..4a0c74c 100644 --- a/src/copyAs.ts +++ b/src/copyAs.ts @@ -10,15 +10,15 @@ export const copyAs = async (messaging: ExtensionHostMessageHandler): Promise { + vscode.window.showQuickPick(formats).then(format => { if (format) { messaging.sendEvent({ type: MessageType.TriggerCopyAs, format: format["label"] }); } @@ -45,64 +45,64 @@ export function copyAsLiteral(buffer: Uint8Array) { vscode.env.clipboard.writeText(encoded); } -export function copyAsC(buffer: Uint8Array) { +export function copyAsC(buffer: Uint8Array, filename: string) { const len = buffer.length; - let content: string = "unsigned char rawData[" + len + "] =\n{"; + let content: string = `unsigned char ${filename}[${len}] =\n{`; for (let i = 0; i < len; ++i) { if (i % 8 == 0) { content += "\n\t"; } - const byte = buffer[i].toString(16); - content += (byte.length < 2 ? "0x0" : "0x") + byte + ", "; + const byte = buffer[i].toString(16).padStart(2, "0"); + content += `0x${byte}, `; } content += "\n};\n"; - if (/^win/.test(process.platform)) { + if (process.platform === "win32") { content = content.replace(/\n/g, "\r\n"); } vscode.env.clipboard.writeText(content); } -export function copyAsGolang(buffer: Uint8Array) { +export function copyAsGo(buffer: Uint8Array, filename: string) { const len = buffer.length; - let content: string = "// RawData (" + len + " bytes)\n"; - content += "var RawData = []byte{"; + let content: string = `// ${filename} (${len} bytes)\n`; + content += `var ${filename} = []byte{`; for (let i = 0; i < len; ++i) { if (i % 8 == 0) { content += "\n\t"; } - const byte = buffer[i].toString(16); - content += (byte.length < 2 ? "0x0" : "0x") + byte + ", "; + const byte = buffer[i].toString(16).padStart(2, "0"); + content += `0x${byte}, `; } content += "\n}\n"; - if (/^win/.test(process.platform)) { + if (process.platform === "win32") { content = content.replace(/\n/g, "\r\n"); } vscode.env.clipboard.writeText(content); } -export function copyAsJava(buffer: Uint8Array) { +export function copyAsJava(buffer: Uint8Array, filename: string) { const len = buffer.length; - let content: string = "byte rawData[] =\n{"; + let content: string = `byte ${filename}[] =\n{`; for (let i = 0; i < len; ++i) { if (i % 8 == 0) { content += "\n\t"; } - const byte = buffer[i].toString(16); - content += (byte.length < 2 ? "0x0" : "0x") + byte + ", "; + const byte = buffer[i].toString(16).padStart(2, "0"); + content += `0x${byte}, `; } content += "\n};\n"; - if (/^win/.test(process.platform)) { + if (process.platform === "win32") { content = content.replace(/\n/g, "\r\n"); } diff --git a/src/hexEditorProvider.ts b/src/hexEditorProvider.ts index 2e45d6a..5e7a13b 100644 --- a/src/hexEditorProvider.ts +++ b/src/hexEditorProvider.ts @@ -26,7 +26,7 @@ import { ILocalizedStrings, placeholder1 } from "../shared/strings"; import { copyAsBase64, copyAsC, - copyAsGolang, + copyAsGo, copyAsHex, copyAsJSON, copyAsJava, @@ -374,40 +374,27 @@ export class HexEditorProvider implements vscode.CustomEditorProvider document.readBuffer(s[0], s[1] - s[0])), ); const flatParts = flattenBuffers(parts); - if (message.format !== undefined) { - switch (message.format) { - case CopyFormat.Hex: - copyAsHex(flatParts); - break; - case CopyFormat.Literal: - copyAsLiteral(flatParts); - break; - case CopyFormat.Text: - copyAsText(flatParts); - break; - case CopyFormat.C: - copyAsC(flatParts); - break; - case CopyFormat.Golang: - copyAsGolang(flatParts); - break; - case CopyFormat.Java: - copyAsJava(flatParts); - break; - case CopyFormat.JSON: - copyAsJSON(flatParts); - break; - case CopyFormat.Base64: - copyAsBase64(flatParts); - break; - } - } else { - if (message.asText) { - copyAsText(flatParts); - } else { - copyAsBase64(flatParts); - } - } + const filenameWoutExt = ((path: string): string => { + const filename = path.split("/").pop()!; + return filename.substring(0, filename.lastIndexOf(".")) || filename; + })(document.uri.path); + const copyAsFormats: { [K in CopyFormat]: (buffer: Uint8Array) => void } = { + [CopyFormat.Hex]: copyAsHex, + [CopyFormat.Literal]: copyAsLiteral, + [CopyFormat.Utf8]: copyAsText, + [CopyFormat.C]: (buffer: Uint8Array) => { + copyAsC(buffer, filenameWoutExt); + }, + [CopyFormat.Go]: (buffer: Uint8Array) => { + copyAsGo(buffer, filenameWoutExt); + }, + [CopyFormat.Java]: (buffer: Uint8Array) => { + copyAsJava(buffer, filenameWoutExt); + }, + [CopyFormat.JSON]: copyAsJSON, + [CopyFormat.Base64]: copyAsBase64, + }; + copyAsFormats[message.format](flatParts); return; } case MessageType.RequestDeletes: { From c4cd91e1de060af13563ef070c670b78e811a171 Mon Sep 17 00:00:00 2001 From: lorsanta Date: Wed, 8 May 2024 22:07:41 +0200 Subject: [PATCH 4/5] fix bugs for the web version --- src/copyAs.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/copyAs.ts b/src/copyAs.ts index 4a0c74c..2a40a3e 100644 --- a/src/copyAs.ts +++ b/src/copyAs.ts @@ -30,14 +30,14 @@ export function copyAsText(buffer: Uint8Array) { } export function copyAsHex(buffer: Uint8Array) { - vscode.env.clipboard.writeText(Buffer.from(buffer).toString("hex")); + const hexString = Array.from(buffer, b => b.toString(16).padStart(2, "0")).join(""); + vscode.env.clipboard.writeText(hexString); } export function copyAsLiteral(buffer: Uint8Array) { let encoded: string = ""; - const digits = Buffer.from(buffer) - .toString("hex") - .match(/.{1,2}/g); + const hexString = Array.from(buffer, b => b.toString(16).padStart(2, "0")).join(""); + const digits = hexString.match(/.{1,2}/g); if (digits) { encoded = "\\x" + digits.join("\\x"); } @@ -59,7 +59,7 @@ export function copyAsC(buffer: Uint8Array, filename: string) { content += "\n};\n"; - if (process.platform === "win32") { + if (typeof process !== "undefined" && process.platform === "win32") { content = content.replace(/\n/g, "\r\n"); } @@ -81,7 +81,7 @@ export function copyAsGo(buffer: Uint8Array, filename: string) { content += "\n}\n"; - if (process.platform === "win32") { + if (typeof process !== "undefined" && process.platform === "win32") { content = content.replace(/\n/g, "\r\n"); } @@ -102,7 +102,7 @@ export function copyAsJava(buffer: Uint8Array, filename: string) { content += "\n};\n"; - if (process.platform === "win32") { + if (typeof process !== "undefined" && process.platform === "win32") { content = content.replace(/\n/g, "\r\n"); } From b2399ec6eb899e1afe57c9c9477a810fb1c7e6d8 Mon Sep 17 00:00:00 2001 From: lorsanta Date: Wed, 8 May 2024 22:10:26 +0200 Subject: [PATCH 5/5] add other requested changes --- package.nls.json | 2 +- src/copyAs.ts | 12 ++++++++++++ src/hexEditorProvider.ts | 40 +++++++--------------------------------- src/util.ts | 6 ++++++ 4 files changed, 26 insertions(+), 34 deletions(-) diff --git a/package.nls.json b/package.nls.json index 3360f12..49cdc2a 100644 --- a/package.nls.json +++ b/package.nls.json @@ -12,7 +12,7 @@ "hexEditor.openFile": "Open Active File in Hex Editor", "hexEditor.goToOffset": "Go To Offset", "hexEditor.selectBetweenOffsets": "Select Between Offsets", - "hexEditor.copyAs": "Copy As", + "hexEditor.copyAs": "Copy As...", "hexEditor.switchEditMode": "Switch Edit Mode", "dataInspectorView": "Data Inspector" } diff --git a/src/copyAs.ts b/src/copyAs.ts index 2a40a3e..e3da281 100644 --- a/src/copyAs.ts +++ b/src/copyAs.ts @@ -6,6 +6,18 @@ interface QuickPickCopyFormat extends vscode.QuickPickItem { label: CopyFormat; } +export const copyAsFormats: { [K in CopyFormat]: (buffer: Uint8Array, filename: string) => void } = + { + [CopyFormat.Hex]: copyAsHex, + [CopyFormat.Literal]: copyAsLiteral, + [CopyFormat.Utf8]: copyAsText, + [CopyFormat.C]: copyAsC, + [CopyFormat.Go]: copyAsGo, + [CopyFormat.Java]: copyAsJava, + [CopyFormat.JSON]: copyAsJSON, + [CopyFormat.Base64]: copyAsBase64, + }; + export const copyAs = async (messaging: ExtensionHostMessageHandler): Promise => { const formats: QuickPickCopyFormat[] = [ { label: CopyFormat.Hex }, diff --git a/src/hexEditorProvider.ts b/src/hexEditorProvider.ts index 5e7a13b..8cae05e 100644 --- a/src/hexEditorProvider.ts +++ b/src/hexEditorProvider.ts @@ -9,7 +9,6 @@ import { HexDocumentEditReference, } from "../shared/hexDocumentModel"; import { - CopyFormat, Endianness, ExtensionHostMessageHandler, FromWebviewMessage, @@ -23,22 +22,13 @@ import { } from "../shared/protocol"; import { deserializeEdits, serializeEdits } from "../shared/serialization"; import { ILocalizedStrings, placeholder1 } from "../shared/strings"; -import { - copyAsBase64, - copyAsC, - copyAsGo, - copyAsHex, - copyAsJSON, - copyAsJava, - copyAsLiteral, - copyAsText, -} from "./copyAs"; +import { copyAsFormats } from "./copyAs"; import { DataInspectorView } from "./dataInspectorView"; import { disposeAll } from "./dispose"; import { HexDocument } from "./hexDocument"; import { HexEditorRegistry } from "./hexEditorRegistry"; import { ISearchRequest, LiteralSearchRequest, RegexSearchRequest } from "./searchRequest"; -import { flattenBuffers, getCorrectArrayBuffer, randomString } from "./util"; +import { flattenBuffers, getBaseName, getCorrectArrayBuffer, randomString } from "./util"; const defaultEditorSettings: Readonly = { columnWidth: 16, @@ -374,27 +364,11 @@ export class HexEditorProvider implements vscode.CustomEditorProvider document.readBuffer(s[0], s[1] - s[0])), ); const flatParts = flattenBuffers(parts); - const filenameWoutExt = ((path: string): string => { - const filename = path.split("/").pop()!; - return filename.substring(0, filename.lastIndexOf(".")) || filename; - })(document.uri.path); - const copyAsFormats: { [K in CopyFormat]: (buffer: Uint8Array) => void } = { - [CopyFormat.Hex]: copyAsHex, - [CopyFormat.Literal]: copyAsLiteral, - [CopyFormat.Utf8]: copyAsText, - [CopyFormat.C]: (buffer: Uint8Array) => { - copyAsC(buffer, filenameWoutExt); - }, - [CopyFormat.Go]: (buffer: Uint8Array) => { - copyAsGo(buffer, filenameWoutExt); - }, - [CopyFormat.Java]: (buffer: Uint8Array) => { - copyAsJava(buffer, filenameWoutExt); - }, - [CopyFormat.JSON]: copyAsJSON, - [CopyFormat.Base64]: copyAsBase64, - }; - copyAsFormats[message.format](flatParts); + + const filenameWoutExt = getBaseName(document.uri.path); + + copyAsFormats[message.format](flatParts, filenameWoutExt); + return; } case MessageType.RequestDeletes: { diff --git a/src/util.ts b/src/util.ts index a1a5ecd..64c2cfd 100644 --- a/src/util.ts +++ b/src/util.ts @@ -58,3 +58,9 @@ export const flattenBuffers = (buffers: readonly Uint8Array[]): Uint8Array => { return target; }; + +export const getBaseName = (path: string): string => { + let filename = path.split("/").pop()!; + filename = filename.substring(0, filename.lastIndexOf(".")) || filename; + return filename.replace(/[^a-z0-9]/gi, ""); +};