You've already forked objdiff-web
mirror of
https://github.com/encounter/objdiff-web.git
synced 2026-07-10 12:18:37 -07:00
Refactor based on objdiff v3.0.0-alpha.1
This commit is contained in:
Vendored
+2
-1
@@ -27,7 +27,8 @@
|
|||||||
"editor.defaultFormatter": "biomejs.biome"
|
"editor.defaultFormatter": "biomejs.biome"
|
||||||
},
|
},
|
||||||
"[json]": {
|
"[json]": {
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
"editor.defaultFormatter": "biomejs.biome",
|
||||||
|
"editor.formatOnSave": false
|
||||||
},
|
},
|
||||||
"[jsonc]": {
|
"[jsonc]": {
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
"editor.defaultFormatter": "biomejs.biome"
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ Open the project in Visual Studio Code.
|
|||||||
- Install dependencies:
|
- Install dependencies:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
pnpm install
|
||||||
```
|
```
|
||||||
|
|
||||||
- Start dev server using `Ctrl+Shift+B` or by running:
|
- Start dev server using `Ctrl+Shift+B` or by running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run watch
|
pnpm watch
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run the extension in debug mode using `F5`.
|
- Run the extension in debug mode using `F5`.
|
||||||
|
|||||||
@@ -27,6 +27,12 @@
|
|||||||
"recommended": true,
|
"recommended": true,
|
||||||
"a11y": {
|
"a11y": {
|
||||||
"all": false
|
"all": false
|
||||||
|
},
|
||||||
|
"suspicious": {
|
||||||
|
"noExplicitAny": "off"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"noNonNullAssertion": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
-3916
File diff suppressed because it is too large
Load Diff
+39
-18
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "objdiff",
|
"name": "objdiff",
|
||||||
"displayName": "objdiff",
|
"displayName": "objdiff",
|
||||||
"description": "objdiff",
|
"description": "A local diffing tool for decompilation projects",
|
||||||
"publisher": "decomp-dev",
|
"publisher": "decomp-dev",
|
||||||
"version": "0.0.2",
|
"version": "0.1.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/encounter/objdiff-code"
|
"url": "https://github.com/encounter/objdiff-code"
|
||||||
@@ -20,13 +20,15 @@
|
|||||||
"@protobuf-ts/runtime": "^2.9.4",
|
"@protobuf-ts/runtime": "^2.9.4",
|
||||||
"@vscode/codicons": "^0.0.36",
|
"@vscode/codicons": "^0.0.36",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"core-js": "^3.39.0",
|
|
||||||
"memoize-one": "^6.0.0",
|
"memoize-one": "^6.0.0",
|
||||||
|
"objdiff-wasm": "^3.0.0-alpha.1",
|
||||||
"picomatch": "^4.0.2",
|
"picomatch": "^4.0.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
"react-tooltip": "^5.28.0",
|
||||||
"react-virtualized-auto-sizer": "^1.0.24",
|
"react-virtualized-auto-sizer": "^1.0.24",
|
||||||
"react-window": "^1.8.10",
|
"react-window": "^1.8.10",
|
||||||
|
"shescape": "^2.1.1",
|
||||||
"zustand": "^5.0.2"
|
"zustand": "^5.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -35,7 +37,6 @@
|
|||||||
"@rsbuild/plugin-react": "^1.0.7",
|
"@rsbuild/plugin-react": "^1.0.7",
|
||||||
"@rsbuild/plugin-type-check": "^1.1.0",
|
"@rsbuild/plugin-type-check": "^1.1.0",
|
||||||
"@rsbuild/plugin-typed-css-modules": "^1.0.2",
|
"@rsbuild/plugin-typed-css-modules": "^1.0.2",
|
||||||
"@types/core-js": "^2.5.8",
|
|
||||||
"@types/node": "^22.10.2",
|
"@types/node": "^22.10.2",
|
||||||
"@types/picomatch": "^3.0.1",
|
"@types/picomatch": "^3.0.1",
|
||||||
"@types/react": "^18.3.1",
|
"@types/react": "^18.3.1",
|
||||||
@@ -95,23 +96,31 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"configuration": [
|
"configuration": [
|
||||||
{
|
|
||||||
"title": "Extension",
|
|
||||||
"properties": {
|
|
||||||
"objdiff.binaryPath": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Path to the objdiff-cli binary",
|
|
||||||
"scope": "machine"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "General",
|
"title": "General",
|
||||||
"properties": {
|
"properties": {
|
||||||
"objdiff.relaxRelocDiffs": {
|
"objdiff.functionRelocDiffs": {
|
||||||
"type": "boolean",
|
"type": "string",
|
||||||
"description": "Ignores differences in relocation targets. (Address, name, etc)",
|
"description": "How relocation targets will be diffed in the function view.",
|
||||||
"default": false
|
"default": "name_address",
|
||||||
|
"enum": [
|
||||||
|
"none",
|
||||||
|
"name_address",
|
||||||
|
"data_value",
|
||||||
|
"all"
|
||||||
|
],
|
||||||
|
"enumItemLabels": [
|
||||||
|
"None",
|
||||||
|
"Name or address",
|
||||||
|
"Data value",
|
||||||
|
"Name or address, data value"
|
||||||
|
],
|
||||||
|
"enumDescriptions": [
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"objdiff.spaceBetweenArgs": {
|
"objdiff.spaceBetweenArgs": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -122,6 +131,11 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Combines data sections with equal names.",
|
"description": "Combines data sections with equal names.",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"objdiff.combineTextSections": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Combines all text sections into one.",
|
||||||
|
"default": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -324,5 +338,12 @@
|
|||||||
"contents": "Loading..."
|
"contents": "Loading..."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"onlyBuiltDependencies": [
|
||||||
|
"@biomejs/biome",
|
||||||
|
"core-js",
|
||||||
|
"esbuild"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+2388
File diff suppressed because it is too large
Load Diff
+18
-6
@@ -1,5 +1,6 @@
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import type { ServerResponse } from 'node:http';
|
import type { ServerResponse } from 'node:http';
|
||||||
|
import path from 'node:path';
|
||||||
import { type RequestHandler, defineConfig } from '@rsbuild/core';
|
import { type RequestHandler, defineConfig } from '@rsbuild/core';
|
||||||
import { pluginReact } from '@rsbuild/plugin-react';
|
import { pluginReact } from '@rsbuild/plugin-react';
|
||||||
import { pluginTypeCheck } from '@rsbuild/plugin-type-check';
|
import { pluginTypeCheck } from '@rsbuild/plugin-type-check';
|
||||||
@@ -92,15 +93,27 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const PROJECT_ROOT = '../prime';
|
||||||
|
|
||||||
// Mock API middleware for development.
|
// Mock API middleware for development.
|
||||||
const apiMiddleware: RequestHandler = (req, res, next) => {
|
const apiMiddleware: RequestHandler = (req, res, next) => {
|
||||||
if (req.method === 'GET' && req.url === '/api/project') {
|
if (!req.url || !req.headers.host || req.method !== 'GET') {
|
||||||
return sendFile(res, '../prime/objdiff.json', 'application/json');
|
return next();
|
||||||
}
|
}
|
||||||
if (req.method === 'GET' && req.url === '/api/diff') {
|
const url = new URL(req.url, `http://${req.headers.host}`);
|
||||||
return sendFile(res, '../prime/diff.binpb', 'application/octet-stream');
|
if (!url) {
|
||||||
|
return next();
|
||||||
}
|
}
|
||||||
next();
|
if (url.pathname === '/api/get') {
|
||||||
|
const file = url.searchParams.get('path');
|
||||||
|
if (file) {
|
||||||
|
const filepath = path.join(PROJECT_ROOT, file);
|
||||||
|
if (filepath.startsWith(PROJECT_ROOT)) {
|
||||||
|
return sendFile(res, filepath, 'application/octet-stream');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return next();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send a file as a response.
|
// Send a file as a response.
|
||||||
@@ -115,7 +128,6 @@ function sendFile(
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
let statusCode = 500;
|
let statusCode = 500;
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: Node error
|
|
||||||
if ((err as any).code === 'ENOENT') {
|
if ((err as any).code === 'ENOENT') {
|
||||||
statusCode = 404;
|
statusCode = 404;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,230 +0,0 @@
|
|||||||
{
|
|
||||||
"properties": [
|
|
||||||
{
|
|
||||||
"id": "relaxRelocDiffs",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Relax relocation diffs",
|
|
||||||
"description": "Ignores differences in relocation targets. (Address, name, etc)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "spaceBetweenArgs",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true,
|
|
||||||
"name": "Space between args",
|
|
||||||
"description": "Adds a space between arguments in the diff output."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "combineDataSections",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Combine data sections",
|
|
||||||
"description": "Combines data sections with equal names."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.archVersion",
|
|
||||||
"type": "choice",
|
|
||||||
"default": "auto",
|
|
||||||
"name": "Architecture version",
|
|
||||||
"description": "ARM architecture version to use for disassembly.",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"value": "auto",
|
|
||||||
"name": "Auto"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "v4t",
|
|
||||||
"name": "ARMv4T (GBA)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "v5te",
|
|
||||||
"name": "ARMv5TE (DS)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "v6k",
|
|
||||||
"name": "ARMv6K (3DS)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.unifiedSyntax",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Unified syntax",
|
|
||||||
"description": "Disassemble as unified assembly language (UAL)."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.avRegisters",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Use A/V registers",
|
|
||||||
"description": "Display R0-R3 as A1-A4 and R4-R11 as V1-V8."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.r9Usage",
|
|
||||||
"type": "choice",
|
|
||||||
"default": "generalPurpose",
|
|
||||||
"name": "Display R9 as",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"value": "generalPurpose",
|
|
||||||
"name": "R9 or V6",
|
|
||||||
"description": "Use R9 as a general-purpose register."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "sb",
|
|
||||||
"name": "SB (static base)",
|
|
||||||
"description": "Used for position-independent data (PID)."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "tr",
|
|
||||||
"name": "TR (TLS register)",
|
|
||||||
"description": "Used for thread-local storage."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.slUsage",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Display R10 as SL",
|
|
||||||
"description": "Used for explicit stack limits."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.fpUsage",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Display R11 as FP",
|
|
||||||
"description": "Used for frame pointers."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm.ipUsage",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"name": "Display R12 as IP",
|
|
||||||
"description": "Used for interworking and long branches."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mips.abi",
|
|
||||||
"type": "choice",
|
|
||||||
"default": "auto",
|
|
||||||
"name": "ABI",
|
|
||||||
"description": "MIPS ABI to use for disassembly.",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"value": "auto",
|
|
||||||
"name": "Auto"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "o32",
|
|
||||||
"name": "O32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "n32",
|
|
||||||
"name": "N32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "n64",
|
|
||||||
"name": "N64"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mips.instrCategory",
|
|
||||||
"type": "choice",
|
|
||||||
"default": "auto",
|
|
||||||
"name": "Instruction category",
|
|
||||||
"description": "MIPS instruction category to use for disassembly.",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"value": "auto",
|
|
||||||
"name": "Auto"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "cpu",
|
|
||||||
"name": "CPU"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "rsp",
|
|
||||||
"name": "RSP (N64)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "r3000gte",
|
|
||||||
"name": "R3000 GTE (PS1)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "r4000allegrex",
|
|
||||||
"name": "R4000 ALLEGREX (PSP)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "r5900",
|
|
||||||
"name": "R5900 EE (PS2)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "x86.formatter",
|
|
||||||
"type": "choice",
|
|
||||||
"default": "intel",
|
|
||||||
"name": "Format",
|
|
||||||
"description": "x86 disassembly syntax.",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"value": "intel",
|
|
||||||
"name": "Intel"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "gas",
|
|
||||||
"name": "AT&T"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "nasm",
|
|
||||||
"name": "NASM"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "masm",
|
|
||||||
"name": "MASM"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"groups": [
|
|
||||||
{
|
|
||||||
"id": "general",
|
|
||||||
"name": "General",
|
|
||||||
"properties": [
|
|
||||||
"relaxRelocDiffs",
|
|
||||||
"spaceBetweenArgs",
|
|
||||||
"combineDataSections"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arm",
|
|
||||||
"name": "ARM",
|
|
||||||
"properties": [
|
|
||||||
"arm.archVersion",
|
|
||||||
"arm.unifiedSyntax",
|
|
||||||
"arm.avRegisters",
|
|
||||||
"arm.r9Usage",
|
|
||||||
"arm.slUsage",
|
|
||||||
"arm.fpUsage",
|
|
||||||
"arm.ipUsage"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mips",
|
|
||||||
"name": "MIPS",
|
|
||||||
"properties": [
|
|
||||||
"mips.abi",
|
|
||||||
"mips.instrCategory"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "x86",
|
|
||||||
"name": "x86",
|
|
||||||
"properties": [
|
|
||||||
"x86.formatter"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
+1
-1
@@ -257,7 +257,7 @@ export type ConfigSchema = {
|
|||||||
groups: ConfigGroup[];
|
groups: ConfigGroup[];
|
||||||
};
|
};
|
||||||
|
|
||||||
import configSchema from './config-schema.json';
|
import configSchema from 'objdiff-wasm/dist/config-schema.json';
|
||||||
export const CONFIG_SCHEMA = configSchema as ConfigSchema;
|
export const CONFIG_SCHEMA = configSchema as ConfigSchema;
|
||||||
|
|
||||||
export function getPropertyValue(
|
export function getPropertyValue(
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+16
-1
@@ -5,12 +5,27 @@ import type {
|
|||||||
Unit,
|
Unit,
|
||||||
} from './config';
|
} from './config';
|
||||||
|
|
||||||
|
export type WebviewProps = {
|
||||||
|
extensionVersion: string;
|
||||||
|
resourceRoot: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BuildStatus = {
|
||||||
|
success: boolean;
|
||||||
|
cmdline: string;
|
||||||
|
stdout: string;
|
||||||
|
stderr: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type StateMessage = {
|
export type StateMessage = {
|
||||||
type: 'state';
|
type: 'state';
|
||||||
buildRunning?: boolean;
|
buildRunning?: boolean;
|
||||||
configProperties?: ConfigProperties;
|
configProperties?: ConfigProperties;
|
||||||
currentUnit?: Unit | null;
|
currentUnit?: Unit | null;
|
||||||
data?: ArrayBuffer | null;
|
leftStatus?: BuildStatus | null;
|
||||||
|
rightStatus?: BuildStatus | null;
|
||||||
|
leftObject?: ArrayBuffer | null;
|
||||||
|
rightObject?: ArrayBuffer | null;
|
||||||
projectConfig?: ProjectConfig | null;
|
projectConfig?: ProjectConfig | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+23
-7
@@ -1,6 +1,10 @@
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import type { Unit } from '../shared/config';
|
import type { Unit } from '../shared/config';
|
||||||
import type { InboundMessage, OutboundMessage } from '../shared/messages';
|
import type {
|
||||||
|
InboundMessage,
|
||||||
|
OutboundMessage,
|
||||||
|
WebviewProps,
|
||||||
|
} from '../shared/messages';
|
||||||
import { Workspace } from './workspace';
|
import { Workspace } from './workspace';
|
||||||
|
|
||||||
export function activate(context: vscode.ExtensionContext) {
|
export function activate(context: vscode.ExtensionContext) {
|
||||||
@@ -42,7 +46,6 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
workspace = new Workspace(
|
workspace = new Workspace(
|
||||||
chan,
|
chan,
|
||||||
vscode.workspace.workspaceFolders[0],
|
vscode.workspace.workspaceFolders[0],
|
||||||
storageUri,
|
|
||||||
deferredCurrentUnit,
|
deferredCurrentUnit,
|
||||||
);
|
);
|
||||||
workspace.onDidChangeProjectConfig(
|
workspace.onDidChangeProjectConfig(
|
||||||
@@ -61,7 +64,8 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
if (!unit) {
|
if (!unit) {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: 'state',
|
type: 'state',
|
||||||
data: null,
|
leftObject: null,
|
||||||
|
rightObject: null,
|
||||||
currentUnit: null,
|
currentUnit: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -73,7 +77,8 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
(data) => {
|
(data) => {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
type: 'state',
|
type: 'state',
|
||||||
data: data?.buffer || null,
|
leftObject: data?.leftObject?.buffer || null,
|
||||||
|
rightObject: data?.rightObject?.buffer || null,
|
||||||
currentUnit: workspace?.currentUnit || null,
|
currentUnit: workspace?.currentUnit || null,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -244,8 +249,17 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
// For development, allow static assets (production will be inlined)
|
// For development, allow static assets (production will be inlined)
|
||||||
html = html.replaceAll(/"\/static\/(.*?)"/g, (_, p) => {
|
html = html.replaceAll(/"\/static\/(.*?)"/g, (_, p) => {
|
||||||
const assetUri = vscode.Uri.joinPath(webviewRoot, 'static', p);
|
const assetUri = vscode.Uri.joinPath(webviewRoot, 'static', p);
|
||||||
return `"${view.webview.asWebviewUri(assetUri)}"`;
|
return JSON.stringify(view.webview.asWebviewUri(assetUri).toString());
|
||||||
});
|
});
|
||||||
|
// Inject extension information into the webview
|
||||||
|
const props: WebviewProps = {
|
||||||
|
extensionVersion: context.extension.packageJSON.version,
|
||||||
|
resourceRoot: `${view.webview.asWebviewUri(webviewRoot).toString()}/`,
|
||||||
|
};
|
||||||
|
html = html.replace(
|
||||||
|
'<head>',
|
||||||
|
`<head><script>window.webviewProps=${JSON.stringify(props)}</script>`,
|
||||||
|
);
|
||||||
view.webview.options = {
|
view.webview.options = {
|
||||||
localResourceRoots: [context.extensionUri],
|
localResourceRoots: [context.extensionUri],
|
||||||
enableScripts: true,
|
enableScripts: true,
|
||||||
@@ -257,7 +271,8 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
buildRunning: workspace?.buildRunning || false,
|
buildRunning: workspace?.buildRunning || false,
|
||||||
configProperties: workspace?.configProperties || {},
|
configProperties: workspace?.configProperties || {},
|
||||||
currentUnit: workspace?.currentUnit || null,
|
currentUnit: workspace?.currentUnit || null,
|
||||||
data: workspace?.cachedData?.buffer || null,
|
leftObject: workspace?.cachedData?.leftObject?.buffer || null,
|
||||||
|
rightObject: workspace?.cachedData?.rightObject?.buffer || null,
|
||||||
projectConfig: workspace?.projectConfig || null,
|
projectConfig: workspace?.projectConfig || null,
|
||||||
} as InboundMessage);
|
} as InboundMessage);
|
||||||
view.webview.onDidReceiveMessage(
|
view.webview.onDidReceiveMessage(
|
||||||
@@ -269,7 +284,8 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
buildRunning: workspace?.buildRunning || false,
|
buildRunning: workspace?.buildRunning || false,
|
||||||
configProperties: workspace?.configProperties || {},
|
configProperties: workspace?.configProperties || {},
|
||||||
currentUnit: workspace?.currentUnit || null,
|
currentUnit: workspace?.currentUnit || null,
|
||||||
data: workspace?.cachedData?.buffer || null,
|
leftObject: workspace?.cachedData?.leftObject?.buffer || null,
|
||||||
|
rightObject: workspace?.cachedData?.rightObject?.buffer || null,
|
||||||
projectConfig: workspace?.projectConfig || null,
|
projectConfig: workspace?.projectConfig || null,
|
||||||
} as InboundMessage);
|
} as InboundMessage);
|
||||||
chan.info('Webview ready');
|
chan.info('Webview ready');
|
||||||
|
|||||||
+129
@@ -0,0 +1,129 @@
|
|||||||
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
|
export type Task = {
|
||||||
|
type: string;
|
||||||
|
command: string;
|
||||||
|
args: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface TaskExecutor {
|
||||||
|
run(task: Task): Promise<TaskResult>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TaskResult {
|
||||||
|
code: number;
|
||||||
|
stdout?: string;
|
||||||
|
stderr?: string;
|
||||||
|
startTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RunningTask {
|
||||||
|
execution: vscode.TaskExecution;
|
||||||
|
resolve: (result: TaskResult) => void;
|
||||||
|
reject: (reason?: any) => void;
|
||||||
|
startTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VscodeTaskExecutor implements TaskExecutor, vscode.Disposable {
|
||||||
|
private runningTasks: RunningTask[] = [];
|
||||||
|
private disposables: vscode.Disposable[] = [];
|
||||||
|
private taskId = 0;
|
||||||
|
|
||||||
|
constructor(private workspaceFolder: vscode.WorkspaceFolder) {
|
||||||
|
vscode.tasks.onDidEndTaskProcess(
|
||||||
|
(e) => {
|
||||||
|
if (e.execution.task.definition.type !== 'objdiff') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const index = this.runningTasks.findIndex(
|
||||||
|
(task) =>
|
||||||
|
task.execution.task.definition.id ===
|
||||||
|
e.execution.task.definition.id,
|
||||||
|
);
|
||||||
|
if (index !== -1) {
|
||||||
|
const task = this.runningTasks[index];
|
||||||
|
this.runningTasks.splice(index, 1);
|
||||||
|
task.resolve({
|
||||||
|
code: e.exitCode ?? -1,
|
||||||
|
startTime: task.startTime,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
this.disposables,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(task: Task): Promise<TaskResult> {
|
||||||
|
const { type, command, args } = task;
|
||||||
|
const startTime = performance.now();
|
||||||
|
const vscodeTask = new vscode.Task(
|
||||||
|
{
|
||||||
|
type: 'objdiff',
|
||||||
|
taskType: type,
|
||||||
|
id: this.taskId++,
|
||||||
|
},
|
||||||
|
this.workspaceFolder,
|
||||||
|
'objdiff',
|
||||||
|
'objdiff',
|
||||||
|
new vscode.ShellExecution(command, args),
|
||||||
|
);
|
||||||
|
vscodeTask.presentationOptions.reveal = vscode.TaskRevealKind.Silent;
|
||||||
|
const execution = await vscode.tasks.executeTask(vscodeTask);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.runningTasks.push({
|
||||||
|
execution,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
startTime,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
for (const disposable of this.disposables) {
|
||||||
|
disposable.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DirectTaskExecutor implements TaskExecutor {
|
||||||
|
constructor(private workspaceFolder: vscode.WorkspaceFolder) {}
|
||||||
|
|
||||||
|
async run(task: Task): Promise<TaskResult> {
|
||||||
|
const { command, args } = task;
|
||||||
|
const startTime = performance.now();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const { execFile } = require('node:child_process');
|
||||||
|
execFile(
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
{
|
||||||
|
cwd: this.workspaceFolder.uri.fsPath,
|
||||||
|
encoding: 'utf8',
|
||||||
|
},
|
||||||
|
(error: any, stdout: string, stderr: string) => {
|
||||||
|
if (error) {
|
||||||
|
if (stdout || stderr) {
|
||||||
|
resolve({
|
||||||
|
code: error.code ?? -1,
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
startTime,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolve({
|
||||||
|
code: 0,
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
startTime,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+101
-159
@@ -1,4 +1,5 @@
|
|||||||
import * as picomatch from 'picomatch';
|
import * as picomatch from 'picomatch';
|
||||||
|
import { Shescape } from 'shescape';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import {
|
import {
|
||||||
CONFIG_FILENAME,
|
CONFIG_FILENAME,
|
||||||
@@ -6,13 +7,23 @@ import {
|
|||||||
type ConfigPropertyValue,
|
type ConfigPropertyValue,
|
||||||
type ProjectConfig,
|
type ProjectConfig,
|
||||||
type Unit,
|
type Unit,
|
||||||
getModifiedConfigProperties,
|
|
||||||
resolveProjectConfig,
|
resolveProjectConfig,
|
||||||
} from '../shared/config';
|
} from '../shared/config';
|
||||||
|
import {
|
||||||
|
DirectTaskExecutor,
|
||||||
|
type Task,
|
||||||
|
type TaskResult,
|
||||||
|
VscodeTaskExecutor,
|
||||||
|
} from './util';
|
||||||
|
|
||||||
|
export type BuildData = {
|
||||||
|
leftObject: Uint8Array | null;
|
||||||
|
rightObject: Uint8Array | null;
|
||||||
|
};
|
||||||
|
|
||||||
export class Workspace extends vscode.Disposable {
|
export class Workspace extends vscode.Disposable {
|
||||||
public buildRunning = false;
|
public buildRunning = false;
|
||||||
public cachedData: Uint8Array | null = null;
|
public cachedData: BuildData | null = null;
|
||||||
public configProperties: ConfigProperties = {};
|
public configProperties: ConfigProperties = {};
|
||||||
public currentUnit?: Unit;
|
public currentUnit?: Unit;
|
||||||
public projectConfig?: ProjectConfig;
|
public projectConfig?: ProjectConfig;
|
||||||
@@ -22,19 +33,22 @@ export class Workspace extends vscode.Disposable {
|
|||||||
public onDidChangeConfigProperties: vscode.Event<ConfigProperties>;
|
public onDidChangeConfigProperties: vscode.Event<ConfigProperties>;
|
||||||
public onDidChangeCurrentUnit: vscode.Event<Unit | undefined>;
|
public onDidChangeCurrentUnit: vscode.Event<Unit | undefined>;
|
||||||
public onDidChangeBuildRunning: vscode.Event<boolean>;
|
public onDidChangeBuildRunning: vscode.Event<boolean>;
|
||||||
public onDidChangeData: vscode.Event<Uint8Array | null>;
|
public onDidChangeData: vscode.Event<BuildData | null>;
|
||||||
|
|
||||||
private subscriptions: vscode.Disposable[] = [];
|
private subscriptions: vscode.Disposable[] = [];
|
||||||
private wwSubscriptions: vscode.Disposable[] = [];
|
private wwSubscriptions: vscode.Disposable[] = [];
|
||||||
private pathMatcher?: picomatch.Matcher;
|
private pathMatcher?: picomatch.Matcher;
|
||||||
|
|
||||||
|
private buildTaskExecutor: VscodeTaskExecutor;
|
||||||
|
private directTaskExecutor: DirectTaskExecutor;
|
||||||
|
|
||||||
private didChangeBuildRunningEmitter = new vscode.EventEmitter<boolean>();
|
private didChangeBuildRunningEmitter = new vscode.EventEmitter<boolean>();
|
||||||
private didChangeConfigPropertiesEmitter =
|
private didChangeConfigPropertiesEmitter =
|
||||||
new vscode.EventEmitter<ConfigProperties>();
|
new vscode.EventEmitter<ConfigProperties>();
|
||||||
private didChangeCurrentUnitEmitter = new vscode.EventEmitter<
|
private didChangeCurrentUnitEmitter = new vscode.EventEmitter<
|
||||||
Unit | undefined
|
Unit | undefined
|
||||||
>();
|
>();
|
||||||
private didChangeDataEmitter = new vscode.EventEmitter<Uint8Array | null>();
|
private didChangeDataEmitter = new vscode.EventEmitter<BuildData | null>();
|
||||||
private didChangeProjectConfigEmitter = new vscode.EventEmitter<
|
private didChangeProjectConfigEmitter = new vscode.EventEmitter<
|
||||||
ProjectConfig | undefined
|
ProjectConfig | undefined
|
||||||
>();
|
>();
|
||||||
@@ -42,7 +56,6 @@ export class Workspace extends vscode.Disposable {
|
|||||||
constructor(
|
constructor(
|
||||||
public readonly chan: vscode.LogOutputChannel,
|
public readonly chan: vscode.LogOutputChannel,
|
||||||
public readonly workspaceFolder: vscode.WorkspaceFolder,
|
public readonly workspaceFolder: vscode.WorkspaceFolder,
|
||||||
private readonly storageUri: vscode.Uri,
|
|
||||||
public deferredCurrentUnit?: string,
|
public deferredCurrentUnit?: string,
|
||||||
) {
|
) {
|
||||||
super(() => {
|
super(() => {
|
||||||
@@ -55,11 +68,9 @@ export class Workspace extends vscode.Disposable {
|
|||||||
this.onDidChangeData = this.didChangeDataEmitter.event;
|
this.onDidChangeData = this.didChangeDataEmitter.event;
|
||||||
this.onDidChangeProjectConfig = this.didChangeProjectConfigEmitter.event;
|
this.onDidChangeProjectConfig = this.didChangeProjectConfigEmitter.event;
|
||||||
|
|
||||||
vscode.tasks.onDidEndTaskProcess(
|
this.buildTaskExecutor = new VscodeTaskExecutor(workspaceFolder);
|
||||||
this.onDidEndTaskProcess,
|
this.directTaskExecutor = new DirectTaskExecutor(workspaceFolder);
|
||||||
this,
|
|
||||||
this.subscriptions,
|
|
||||||
);
|
|
||||||
vscode.workspace.onDidChangeConfiguration(
|
vscode.workspace.onDidChangeConfiguration(
|
||||||
this.onDidChangeConfiguration,
|
this.onDidChangeConfiguration,
|
||||||
this,
|
this,
|
||||||
@@ -90,7 +101,6 @@ export class Workspace extends vscode.Disposable {
|
|||||||
chan.info(`Initialized workspace: ${this.workspaceFolder.uri.toString()}`);
|
chan.info(`Initialized workspace: ${this.workspaceFolder.uri.toString()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: pass through message args
|
|
||||||
showError(message: string, ...args: any[]) {
|
showError(message: string, ...args: any[]) {
|
||||||
this.chan.error(message, ...args);
|
this.chan.error(message, ...args);
|
||||||
vscode.window
|
vscode.window
|
||||||
@@ -104,7 +114,6 @@ export class Workspace extends vscode.Disposable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: pass through message args
|
|
||||||
showWarning(message: string, ...args: any[]) {
|
showWarning(message: string, ...args: any[]) {
|
||||||
this.chan.warn(message, ...args);
|
this.chan.warn(message, ...args);
|
||||||
vscode.window.showWarningMessage(`objdiff: ${message}`);
|
vscode.window.showWarningMessage(`objdiff: ${message}`);
|
||||||
@@ -262,7 +271,7 @@ export class Workspace extends vscode.Disposable {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
tryBuild() {
|
async tryBuild() {
|
||||||
if (this.buildRunning) {
|
if (this.buildRunning) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -283,145 +292,101 @@ export class Workspace extends vscode.Disposable {
|
|||||||
const basePath =
|
const basePath =
|
||||||
this.currentUnit.base_path &&
|
this.currentUnit.base_path &&
|
||||||
vscode.Uri.joinPath(this.workspaceFolder.uri, this.currentUnit.base_path);
|
vscode.Uri.joinPath(this.workspaceFolder.uri, this.currentUnit.base_path);
|
||||||
this.chan.info('Diffing', targetPath?.toString(), basePath?.toString());
|
|
||||||
if (!targetPath && !basePath) {
|
if (!targetPath && !basePath) {
|
||||||
this.showWarning('No target or base path');
|
this.showWarning('No target or base path');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const buildCmd = this.projectConfig.custom_make || 'make';
|
|
||||||
const buildArgs = this.projectConfig.custom_args || [];
|
|
||||||
const hash = cyrb53(this.workspaceFolder.uri.toString());
|
|
||||||
const outputUri = vscode.Uri.joinPath(
|
|
||||||
this.storageUri,
|
|
||||||
`diff_${hash}.binpb`,
|
|
||||||
);
|
|
||||||
const args = [];
|
|
||||||
if (this.currentUnit.target_path && this.projectConfig.build_target) {
|
|
||||||
if (args.length) {
|
|
||||||
args.push('&&');
|
|
||||||
}
|
|
||||||
args.push(buildCmd, ...buildArgs, this.currentUnit.target_path);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
this.currentUnit.base_path &&
|
|
||||||
(this.projectConfig.build_base ||
|
|
||||||
this.projectConfig.build_base === undefined)
|
|
||||||
) {
|
|
||||||
if (args.length) {
|
|
||||||
args.push('&&');
|
|
||||||
}
|
|
||||||
args.push(buildCmd, ...buildArgs, this.currentUnit.base_path);
|
|
||||||
}
|
|
||||||
if (args.length) {
|
|
||||||
args.push('&&');
|
|
||||||
}
|
|
||||||
const binaryPath = this.configProperties.binaryPath as string | undefined;
|
|
||||||
if (!binaryPath) {
|
|
||||||
vscode.window
|
|
||||||
.showWarningMessage('objdiff.binaryPath not set', {
|
|
||||||
title: 'Open settings',
|
|
||||||
})
|
|
||||||
.then((item) => {
|
|
||||||
if (item) {
|
|
||||||
vscode.commands.executeCommand(
|
|
||||||
'workbench.action.openSettings',
|
|
||||||
'objdiff.binaryPath',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
args.push(binaryPath, 'diff');
|
|
||||||
if (targetPath) {
|
|
||||||
args.push('-1', targetPath.fsPath);
|
|
||||||
}
|
|
||||||
if (basePath) {
|
|
||||||
args.push('-2', basePath.fsPath);
|
|
||||||
}
|
|
||||||
args.push('--format', 'proto', '-o', outputUri.fsPath);
|
|
||||||
const configProperties = getModifiedConfigProperties(this.configProperties);
|
|
||||||
for (const key in configProperties) {
|
|
||||||
args.push('-c', `${key}=${configProperties[key]}`);
|
|
||||||
}
|
|
||||||
const startTime = performance.now();
|
|
||||||
const task = new vscode.Task(
|
|
||||||
{
|
|
||||||
type: 'objdiff',
|
|
||||||
taskType: 'build',
|
|
||||||
startTime,
|
|
||||||
},
|
|
||||||
this.workspaceFolder,
|
|
||||||
'objdiff',
|
|
||||||
'objdiff',
|
|
||||||
new vscode.ShellExecution(args[0], args.slice(1)),
|
|
||||||
);
|
|
||||||
task.presentationOptions.reveal = vscode.TaskRevealKind.Silent;
|
|
||||||
this.buildRunning = true;
|
this.buildRunning = true;
|
||||||
this.didChangeBuildRunningEmitter.fire(true);
|
this.didChangeBuildRunningEmitter.fire(true);
|
||||||
vscode.tasks.executeTask(task).then(
|
|
||||||
({ task, terminate: _ }) => {
|
|
||||||
const curTime = performance.now();
|
|
||||||
this.chan.info(
|
|
||||||
'Diff task started in',
|
|
||||||
curTime - task.definition.startTime,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(reason) => {
|
|
||||||
this.showError('Failed to start diff task', reason);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async onDidEndTaskProcess(e: vscode.TaskProcessEndEvent) {
|
|
||||||
if (e.execution.task.definition.type !== 'objdiff') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const endTime = performance.now();
|
const buildCmd = this.projectConfig.custom_make || 'make';
|
||||||
this.chan.info(
|
const buildArgs = this.projectConfig.custom_args || [];
|
||||||
'Task ended',
|
|
||||||
e.exitCode,
|
// Build target object
|
||||||
endTime - e.execution.task.definition.startTime,
|
if (this.currentUnit.target_path && this.projectConfig.build_target) {
|
||||||
);
|
const result = await this.runTask({
|
||||||
const proc = e.execution.task.execution as vscode.ProcessExecution;
|
type: 'objdiff',
|
||||||
const outputFile = proc.args[proc.args.indexOf('-o') + 1];
|
command: buildCmd,
|
||||||
const outputUri = vscode.Uri.file(outputFile);
|
args: [...buildArgs, this.currentUnit.target_path],
|
||||||
if (e.exitCode === 0) {
|
});
|
||||||
const data = await vscode.workspace.fs.readFile(outputUri);
|
if (result.code !== 0) {
|
||||||
this.chan.info(
|
this.showError(`Target build failed with code ${result.code}`);
|
||||||
'Read output file',
|
return;
|
||||||
outputFile,
|
|
||||||
'with size',
|
|
||||||
data.byteLength,
|
|
||||||
);
|
|
||||||
this.cachedData = data;
|
|
||||||
this.didChangeDataEmitter.fire(data);
|
|
||||||
} else {
|
|
||||||
this.showError(`Build failed with code ${e.exitCode}`);
|
|
||||||
}
|
|
||||||
let exists = false;
|
|
||||||
try {
|
|
||||||
const stat = await vscode.workspace.fs.stat(outputUri);
|
|
||||||
exists = stat.type === vscode.FileType.File;
|
|
||||||
} catch (reason) {
|
|
||||||
if (
|
|
||||||
reason instanceof vscode.FileSystemError &&
|
|
||||||
reason.code !== 'FileNotFound'
|
|
||||||
) {
|
|
||||||
throw reason;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (exists) {
|
|
||||||
await vscode.workspace.fs.delete(outputUri);
|
// Build base object
|
||||||
|
if (
|
||||||
|
this.currentUnit.base_path &&
|
||||||
|
(this.projectConfig.build_base ||
|
||||||
|
this.projectConfig.build_base === undefined)
|
||||||
|
) {
|
||||||
|
const result = await this.runTask({
|
||||||
|
type: 'objdiff',
|
||||||
|
command: buildCmd,
|
||||||
|
args: [...buildArgs, this.currentUnit.base_path],
|
||||||
|
});
|
||||||
|
if (result.code !== 0) {
|
||||||
|
this.showError(`Base build failed with code ${result.code}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read target object
|
||||||
|
let targetData: Uint8Array | null = null;
|
||||||
|
if (targetPath) {
|
||||||
|
targetData = await vscode.workspace.fs.readFile(targetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read base object
|
||||||
|
let baseData: Uint8Array | null = null;
|
||||||
|
if (basePath) {
|
||||||
|
baseData = await vscode.workspace.fs.readFile(basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cachedData = { leftObject: targetData, rightObject: baseData };
|
||||||
|
this.didChangeDataEmitter.fire(this.cachedData);
|
||||||
} catch (reason) {
|
} catch (reason) {
|
||||||
this.showError('Failed to process build result', reason);
|
this.showError('Failed to execute build', reason);
|
||||||
} finally {
|
} finally {
|
||||||
this.buildRunning = false;
|
this.buildRunning = false;
|
||||||
this.didChangeBuildRunningEmitter.fire(false);
|
this.didChangeBuildRunningEmitter.fire(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async runTask(task: Task): Promise<TaskResult> {
|
||||||
|
this.logTask(task);
|
||||||
|
const result = await this.directTaskExecutor.run(task);
|
||||||
|
this.logTaskResult(task, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private logTask(task: Task) {
|
||||||
|
const command = new Shescape({ flagProtection: false, shell: true })
|
||||||
|
.quoteAll([task.command, ...task.args])
|
||||||
|
.join(' ');
|
||||||
|
this.chan.info('Executing', command);
|
||||||
|
}
|
||||||
|
|
||||||
|
private logTaskResult(task: Task, result: TaskResult) {
|
||||||
|
if (result.code === 0) {
|
||||||
|
const endTime = performance.now();
|
||||||
|
const elapsed = (endTime - result.startTime).toFixed(0);
|
||||||
|
this.chan.info(`Command ${task.command} succeeded in ${elapsed}ms`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.chan.error(
|
||||||
|
[
|
||||||
|
`Command ${task.command} failed with code ${result.code}`,
|
||||||
|
result.stdout,
|
||||||
|
result.stderr,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('\n'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private async onDidChangeConfiguration(e: vscode.ConfigurationChangeEvent) {
|
private async onDidChangeConfiguration(e: vscode.ConfigurationChangeEvent) {
|
||||||
if (!e.affectsConfiguration('objdiff')) {
|
if (!e.affectsConfiguration('objdiff')) {
|
||||||
return;
|
return;
|
||||||
@@ -455,6 +420,7 @@ export class Workspace extends vscode.Disposable {
|
|||||||
|
|
||||||
private disposeImpl() {
|
private disposeImpl() {
|
||||||
this.chan.info('Disposing workspace');
|
this.chan.info('Disposing workspace');
|
||||||
|
this.buildTaskExecutor.dispose();
|
||||||
this.projectConfigWatcher.dispose();
|
this.projectConfigWatcher.dispose();
|
||||||
for (const sub of this.wwSubscriptions) {
|
for (const sub of this.wwSubscriptions) {
|
||||||
sub.dispose();
|
sub.dispose();
|
||||||
@@ -468,27 +434,3 @@ export class Workspace extends vscode.Disposable {
|
|||||||
this.subscriptions = [];
|
this.subscriptions = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* cyrb53 (c) 2018 bryc (github.com/bryc)
|
|
||||||
* License: Public domain (or MIT if needed). Attribution appreciated.
|
|
||||||
* A fast and simple 53-bit string hash function with decent collision resistance.
|
|
||||||
* Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity.
|
|
||||||
*/
|
|
||||||
function cyrb53(str: string, seed = 0) {
|
|
||||||
let h1 = 0xdeadbeef ^ seed;
|
|
||||||
let h2 = 0x41c6ce57 ^ seed;
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
|
||||||
const ch = str.charCodeAt(i);
|
|
||||||
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
||||||
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
||||||
}
|
|
||||||
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
|
||||||
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
||||||
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
|
|
||||||
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
||||||
return (
|
|
||||||
(h2 >>> 0).toString(16).padStart(8, '0') +
|
|
||||||
(h1 >>> 0).toString(16).padStart(8, '0')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
+4
-4
@@ -3,12 +3,13 @@ import { CONFIG_SCHEMA } from './shared/config';
|
|||||||
|
|
||||||
const packageJson = JSON.parse(readFileSync('./package.json', 'utf-8'));
|
const packageJson = JSON.parse(readFileSync('./package.json', 'utf-8'));
|
||||||
const extensionConfig = packageJson.contributes.configuration.find(
|
const extensionConfig = packageJson.contributes.configuration.find(
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: ignore
|
|
||||||
(config: any) => config.title === 'Extension',
|
(config: any) => config.title === 'Extension',
|
||||||
);
|
);
|
||||||
const categories = [extensionConfig];
|
const categories: any[] = [];
|
||||||
|
if (extensionConfig) {
|
||||||
|
categories.push(extensionConfig);
|
||||||
|
}
|
||||||
for (const group of CONFIG_SCHEMA.groups) {
|
for (const group of CONFIG_SCHEMA.groups) {
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: ignore
|
|
||||||
const category: any = {
|
const category: any = {
|
||||||
title: group.name,
|
title: group.name,
|
||||||
properties: {},
|
properties: {},
|
||||||
@@ -18,7 +19,6 @@ for (const group of CONFIG_SCHEMA.groups) {
|
|||||||
if (!property) {
|
if (!property) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: ignore
|
|
||||||
const config: any = {
|
const config: any = {
|
||||||
type: property.type === 'boolean' ? 'boolean' : 'string',
|
type: property.type === 'boolean' ? 'boolean' : 'string',
|
||||||
description: property.description,
|
description: property.description,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
--color-red: #f85149;
|
--color-red: #f85149;
|
||||||
--color-blue: #add8e6;
|
--color-blue: #add8e6;
|
||||||
--color-muted: var(--vscode-disabledForeground, rgba(204, 204, 204, 0.5));
|
--color-muted: var(--vscode-disabledForeground, rgba(204, 204, 204, 0.5));
|
||||||
|
--color-bright: light-dark(black, white);
|
||||||
|
|
||||||
--panel-background: var(--vscode-panel-background, #181818);
|
--panel-background: var(--vscode-panel-background, #181818);
|
||||||
--panel-separator: var(--vscode-menu-separatorBackground, #454545);
|
--panel-separator: var(--vscode-menu-separatorBackground, #454545);
|
||||||
@@ -71,6 +72,12 @@
|
|||||||
--checkbox-border-color: var(--vscode-settings-checkboxBorder, #3c3c3c);
|
--checkbox-border-color: var(--vscode-settings-checkboxBorder, #3c3c3c);
|
||||||
|
|
||||||
color-scheme: light dark;
|
color-scheme: light dark;
|
||||||
|
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
+24
-28
@@ -1,12 +1,7 @@
|
|||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
|
import type { diff, display } from 'objdiff-wasm';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
import { SectionKind } from '../shared/gen/diff_pb';
|
|
||||||
import type {
|
|
||||||
Symbol as DiffSymbol,
|
|
||||||
ObjectDiff,
|
|
||||||
SymbolDiff,
|
|
||||||
} from '../shared/gen/diff_pb';
|
|
||||||
import FunctionView from './FunctionView';
|
import FunctionView from './FunctionView';
|
||||||
import SettingsView from './SettingsView';
|
import SettingsView from './SettingsView';
|
||||||
import SymbolsView from './SymbolsView';
|
import SymbolsView from './SymbolsView';
|
||||||
@@ -15,39 +10,38 @@ import { useAppStore, useExtensionStore } from './state';
|
|||||||
import type { SymbolRefByName } from './state';
|
import type { SymbolRefByName } from './state';
|
||||||
|
|
||||||
const findSymbol = (
|
const findSymbol = (
|
||||||
obj: ObjectDiff | undefined,
|
obj: diff.ObjectDiff | undefined,
|
||||||
symbolRef: SymbolRefByName | null,
|
symbolRef: SymbolRefByName | null,
|
||||||
): SymbolDiff | null => {
|
): display.SectionDisplaySymbol | null => {
|
||||||
if (!obj || !symbolRef) {
|
if (!obj || !symbolRef) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (const section of obj.sections) {
|
const idx = obj.findSymbol(
|
||||||
if (section.name === symbolRef.section_name) {
|
symbolRef.symbolName,
|
||||||
if (section.kind === SectionKind.SECTION_TEXT) {
|
symbolRef.sectionName ?? undefined,
|
||||||
for (const diff of section.symbols) {
|
);
|
||||||
const symbol = diff.symbol as DiffSymbol;
|
if (idx !== undefined) {
|
||||||
if (symbol.name === symbolRef.symbol_name) {
|
return {
|
||||||
return diff;
|
symbol: idx,
|
||||||
}
|
isMappingSymbol: false,
|
||||||
}
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const { buildRunning, diff, config, ready } = useExtensionStore(
|
const { buildRunning, result, config, ready } = useExtensionStore(
|
||||||
useShallow((state) => ({
|
useShallow((state) => ({
|
||||||
buildRunning: state.buildRunning,
|
buildRunning: state.buildRunning,
|
||||||
diff: state.diff,
|
result: state.result,
|
||||||
config: state.projectConfig,
|
config: state.projectConfig,
|
||||||
ready: state.ready,
|
ready: state.ready,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
const { selectedSymbolRef, currentView } = useAppStore(
|
const { leftSymbolRef, rightSymbolRef, currentView } = useAppStore(
|
||||||
useShallow((state) => ({
|
useShallow((state) => ({
|
||||||
selectedSymbolRef: state.selectedSymbol,
|
leftSymbolRef: state.leftSymbol,
|
||||||
|
rightSymbolRef: state.rightSymbol,
|
||||||
currentView: state.currentView,
|
currentView: state.currentView,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
@@ -57,13 +51,15 @@ const App = () => {
|
|||||||
return <div className="loading-root" />;
|
return <div className="loading-root" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (diff) {
|
if (result) {
|
||||||
const leftSymbol = findSymbol(diff.left, selectedSymbolRef);
|
const leftSymbol = findSymbol(result.left, leftSymbolRef);
|
||||||
const rightSymbol = findSymbol(diff.right, selectedSymbolRef);
|
const rightSymbol = findSymbol(result.right, rightSymbolRef);
|
||||||
if (leftSymbol || rightSymbol) {
|
if (leftSymbol || rightSymbol) {
|
||||||
return <FunctionView left={leftSymbol} right={rightSymbol} />;
|
return (
|
||||||
|
<FunctionView diff={result} left={leftSymbol} right={rightSymbol} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return <SymbolsView diff={diff} />;
|
return <SymbolsView diff={result} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (currentView) {
|
switch (currentView) {
|
||||||
|
|||||||
@@ -27,23 +27,25 @@
|
|||||||
color: white;
|
color: white;
|
||||||
background-color: #aa8b00;
|
background-color: #aa8b00;
|
||||||
}
|
}
|
||||||
.line-number {
|
|
||||||
color: var(--line-number-foreground);
|
.diff-any {
|
||||||
}
|
|
||||||
.diff_any {
|
|
||||||
background-color: rgba(255, 255, 255, 0.02);
|
background-color: rgba(255, 255, 255, 0.02);
|
||||||
}
|
}
|
||||||
.diff_change {
|
|
||||||
|
.segment-dim {
|
||||||
|
color: var(--line-number-foreground);
|
||||||
|
}
|
||||||
|
.segment-bright {
|
||||||
|
color: var(--color-bright);
|
||||||
|
}
|
||||||
|
.segment-replace {
|
||||||
color: var(--color-blue);
|
color: var(--color-blue);
|
||||||
}
|
}
|
||||||
.diff_add {
|
.segment-delete {
|
||||||
color: var(--color-green);
|
|
||||||
}
|
|
||||||
.diff_remove {
|
|
||||||
color: var(--color-red);
|
color: var(--color-red);
|
||||||
}
|
}
|
||||||
.symbol {
|
.segment-insert {
|
||||||
color: light-dark(black, white);
|
color: var(--color-green);
|
||||||
}
|
}
|
||||||
|
|
||||||
.rotation0 {
|
.rotation0 {
|
||||||
|
|||||||
+194
-108
@@ -3,25 +3,25 @@ import headerStyles from './Header.module.css';
|
|||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import memoizeOne from 'memoize-one';
|
import memoizeOne from 'memoize-one';
|
||||||
|
import { type diff, display } from 'objdiff-wasm';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
import { FixedSizeList, areEqual } from 'react-window';
|
import { FixedSizeList, areEqual } from 'react-window';
|
||||||
import type { ListChildComponentProps } from 'react-window';
|
import type { ListChildComponentProps } from 'react-window';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
import { DiffKind } from '../shared/gen/diff_pb';
|
import TooltipShared from './TooltipShared';
|
||||||
import type {
|
|
||||||
Symbol as DiffSymbol,
|
|
||||||
InstructionDiff,
|
|
||||||
SymbolDiff,
|
|
||||||
} from '../shared/gen/diff_pb';
|
|
||||||
import { displayDiff } from './diff';
|
|
||||||
import {
|
import {
|
||||||
type HighlightState,
|
type HighlightState,
|
||||||
highlightColumn,
|
highlightColumn,
|
||||||
highlightMatches,
|
highlightMatches,
|
||||||
updateHighlight,
|
updateHighlight,
|
||||||
} from './highlight';
|
} from './highlight';
|
||||||
import { runBuild, useAppStore, useExtensionStore } from './state';
|
import {
|
||||||
|
buildDiffConfig,
|
||||||
|
runBuild,
|
||||||
|
useAppStore,
|
||||||
|
useExtensionStore,
|
||||||
|
} from './state';
|
||||||
import { percentClass, useFontSize } from './util';
|
import { percentClass, useFontSize } from './util';
|
||||||
|
|
||||||
const ROTATION_CLASSES = [
|
const ROTATION_CLASSES = [
|
||||||
@@ -37,100 +37,118 @@ const ROTATION_CLASSES = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const AsmCell = ({
|
const AsmCell = ({
|
||||||
insDiff,
|
obj,
|
||||||
|
config,
|
||||||
symbol,
|
symbol,
|
||||||
|
row,
|
||||||
column,
|
column,
|
||||||
highlight: highlightState,
|
highlight: highlightState,
|
||||||
setHighlight,
|
setHighlight,
|
||||||
}: {
|
}: {
|
||||||
insDiff: InstructionDiff | undefined;
|
obj: diff.ObjectDiff | undefined;
|
||||||
symbol: DiffSymbol | undefined;
|
config: diff.DiffConfig;
|
||||||
|
symbol: display.SectionDisplaySymbol | null;
|
||||||
|
row: number;
|
||||||
column: number;
|
column: number;
|
||||||
highlight: HighlightState;
|
highlight: HighlightState;
|
||||||
setHighlight: (highlight: HighlightState) => void;
|
setHighlight: (highlight: HighlightState) => void;
|
||||||
}) => {
|
}) => {
|
||||||
if (!insDiff || !symbol) {
|
if (!obj || !symbol) {
|
||||||
return <div className={styles.instructionCell} />;
|
return <div className={styles.instructionCell} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const highlight = highlightColumn(highlightState, column);
|
const highlight = highlightColumn(highlightState, column);
|
||||||
const out: React.ReactNode[] = [];
|
const out: React.ReactNode[] = [];
|
||||||
|
|
||||||
|
const insRow = display.displayInstructionRow(obj, symbol, row, config);
|
||||||
let index = 0;
|
let index = 0;
|
||||||
displayDiff(insDiff, symbol.address, (t) => {
|
for (const segment of insRow.segments) {
|
||||||
let className: string | undefined;
|
let className: string | undefined;
|
||||||
if (t.diff_index != null) {
|
switch (segment.color.tag) {
|
||||||
className = ROTATION_CLASSES[t.diff_index % ROTATION_CLASSES.length];
|
case 'normal':
|
||||||
|
break;
|
||||||
|
case 'dim':
|
||||||
|
className = styles.segmentDim;
|
||||||
|
break;
|
||||||
|
case 'bright':
|
||||||
|
className = styles.segmentBright;
|
||||||
|
break;
|
||||||
|
case 'replace':
|
||||||
|
className = styles.segmentReplace;
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
className = styles.segmentDelete;
|
||||||
|
break;
|
||||||
|
case 'insert':
|
||||||
|
className = styles.segmentInsert;
|
||||||
|
break;
|
||||||
|
case 'rotating':
|
||||||
|
className =
|
||||||
|
ROTATION_CLASSES[segment.color.val % ROTATION_CLASSES.length];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn('Unknown color type', segment.color);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
const t = segment.text;
|
||||||
let text = '';
|
let text = '';
|
||||||
let postText = ''; // unhighlightable text after the token
|
let postText = ''; // unhighlightable text after the token
|
||||||
let padTo = 0;
|
|
||||||
let isToken = false;
|
let isToken = false;
|
||||||
switch (t.type) {
|
switch (t.tag) {
|
||||||
case 'basic':
|
case 'basic':
|
||||||
text = t.text;
|
text = t.val;
|
||||||
break;
|
|
||||||
case 'basic_color':
|
|
||||||
text = t.text;
|
|
||||||
className = ROTATION_CLASSES[t.index % ROTATION_CLASSES.length];
|
|
||||||
break;
|
break;
|
||||||
case 'line':
|
case 'line':
|
||||||
text = (t.line_number || 0).toString(10);
|
text = t.val.toString(10);
|
||||||
className = styles.lineNumber;
|
|
||||||
padTo = 5;
|
|
||||||
break;
|
break;
|
||||||
case 'address':
|
case 'address':
|
||||||
text = (t.address || 0).toString(16);
|
text = t.val.toString(16);
|
||||||
postText = ':';
|
postText = ':';
|
||||||
padTo = 5;
|
|
||||||
isToken = true;
|
isToken = true;
|
||||||
break;
|
break;
|
||||||
case 'opcode':
|
case 'opcode':
|
||||||
text = t.mnemonic;
|
text = t.val.mnemonic;
|
||||||
padTo = 8;
|
|
||||||
isToken = true;
|
isToken = true;
|
||||||
if (insDiff.diff_kind === DiffKind.DIFF_OP_MISMATCH) {
|
|
||||||
className = styles.diff_change;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'argument': {
|
case 'signed':
|
||||||
const value = t.value.value;
|
if (t.val < 0) {
|
||||||
switch (value.oneofKind) {
|
text = `-0x${(-t.val).toString(16)}`;
|
||||||
case 'signed':
|
} else {
|
||||||
if (value.signed < 0) {
|
text = `0x${t.val.toString(16)}`;
|
||||||
text = `-0x${(-value.signed).toString(16)}`;
|
|
||||||
} else {
|
|
||||||
text = `0x${value.signed.toString(16)}`;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'unsigned':
|
|
||||||
text = `0x${value.unsigned.toString(16)}`;
|
|
||||||
break;
|
|
||||||
case 'opaque':
|
|
||||||
text = value.opaque;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
isToken = true;
|
isToken = true;
|
||||||
break;
|
break;
|
||||||
}
|
case 'unsigned':
|
||||||
case 'branch_dest':
|
text = `0x${t.val.toString(16)}`;
|
||||||
text = (t.address || 0).toString(16);
|
|
||||||
isToken = true;
|
isToken = true;
|
||||||
break;
|
break;
|
||||||
case 'symbol': {
|
case 'opaque':
|
||||||
const symbol = t.target.symbol as DiffSymbol;
|
text = t.val;
|
||||||
text = symbol.demangled_name || symbol.name;
|
isToken = true;
|
||||||
if (t.diff_index == null) {
|
break;
|
||||||
className = styles.symbol;
|
case 'branch-dest':
|
||||||
|
text = t.val.toString(16);
|
||||||
|
isToken = true;
|
||||||
|
break;
|
||||||
|
case 'symbol':
|
||||||
|
text = t.val.demangledName || t.val.name;
|
||||||
|
isToken = true;
|
||||||
|
break;
|
||||||
|
case 'addend':
|
||||||
|
if (t.val < 0) {
|
||||||
|
text = `-0x${(-t.val).toString(16)}`;
|
||||||
|
} else {
|
||||||
|
text = `+0x${t.val.toString(16)}`;
|
||||||
}
|
}
|
||||||
isToken = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case 'spacing':
|
case 'spacing':
|
||||||
text = ' '.repeat(t.count);
|
text = ' '.repeat(t.val);
|
||||||
break;
|
break;
|
||||||
|
case 'eol':
|
||||||
|
continue;
|
||||||
default:
|
default:
|
||||||
console.warn('Unknown text type', t);
|
console.warn('Unknown text type', t);
|
||||||
return null;
|
break;
|
||||||
}
|
}
|
||||||
out.push(
|
out.push(
|
||||||
<span
|
<span
|
||||||
@@ -151,39 +169,55 @@ const AsmCell = ({
|
|||||||
);
|
);
|
||||||
index++;
|
index++;
|
||||||
if (postText) {
|
if (postText) {
|
||||||
out.push(<span key={index}>{postText}</span>);
|
out.push(
|
||||||
|
<span key={index} className={className}>
|
||||||
|
{postText}
|
||||||
|
</span>,
|
||||||
|
);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
if (padTo > text.length + postText.length) {
|
if (segment.padTo > text.length + postText.length) {
|
||||||
const spacing = ' '.repeat(padTo - text.length - postText.length);
|
const spacing = ' '.repeat(segment.padTo - text.length - postText.length);
|
||||||
out.push(<span key={index}>{spacing}</span>);
|
out.push(<span key={index}>{spacing}</span>);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
const classes = [styles.instructionCell];
|
const classes = [styles.instructionCell];
|
||||||
if (insDiff.diff_kind) {
|
if (insRow.diffKind !== 'none') {
|
||||||
classes.push(styles.diff_any);
|
classes.push(styles.diffAny);
|
||||||
}
|
}
|
||||||
switch (insDiff.diff_kind) {
|
if (!out.length) {
|
||||||
case DiffKind.DIFF_DELETE:
|
return <div className={clsx(classes)} />;
|
||||||
classes.push(styles.diff_remove);
|
|
||||||
break;
|
|
||||||
case DiffKind.DIFF_INSERT:
|
|
||||||
classes.push(styles.diff_add);
|
|
||||||
break;
|
|
||||||
case DiffKind.DIFF_REPLACE:
|
|
||||||
classes.push(styles.diff_change);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={clsx(classes)}>{out}</div>;
|
const tooltipContent: InstructionTooltipContent = { column, row };
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx(classes)}
|
||||||
|
data-tooltip-id="instruction-tooltip"
|
||||||
|
data-tooltip-content={JSON.stringify(tooltipContent)}
|
||||||
|
>
|
||||||
|
{out}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type InstructionTooltipContent = {
|
||||||
|
column: number;
|
||||||
|
row: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ItemData = {
|
type ItemData = {
|
||||||
itemCount: number;
|
itemCount: number;
|
||||||
left: SymbolDiff | null;
|
symbolName: string;
|
||||||
right: SymbolDiff | null;
|
result: diff.DiffResult;
|
||||||
|
config: diff.DiffConfig;
|
||||||
|
matchPercent?: number;
|
||||||
|
left: display.SectionDisplaySymbol | null;
|
||||||
|
leftSymbol: display.SymbolDisplay | null;
|
||||||
|
right: display.SectionDisplaySymbol | null;
|
||||||
|
rightSymbol: display.SymbolDisplay | null;
|
||||||
highlight: HighlightState;
|
highlight: HighlightState;
|
||||||
setHighlight: (highlight: HighlightState) => void;
|
setHighlight: (highlight: HighlightState) => void;
|
||||||
};
|
};
|
||||||
@@ -192,10 +226,8 @@ const AsmRow = memo(
|
|||||||
({
|
({
|
||||||
index,
|
index,
|
||||||
style,
|
style,
|
||||||
data: { left, right, highlight, setHighlight },
|
data: { result, config, left, right, highlight, setHighlight },
|
||||||
}: ListChildComponentProps<ItemData>) => {
|
}: ListChildComponentProps<ItemData>) => {
|
||||||
const leftIns = left?.instructions[index];
|
|
||||||
const rightIns = right?.instructions[index];
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={styles.instructionRow}
|
className={styles.instructionRow}
|
||||||
@@ -212,15 +244,19 @@ const AsmRow = memo(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AsmCell
|
<AsmCell
|
||||||
insDiff={leftIns}
|
obj={result.left}
|
||||||
symbol={left?.symbol}
|
config={config}
|
||||||
|
symbol={left}
|
||||||
|
row={index}
|
||||||
column={0}
|
column={0}
|
||||||
highlight={highlight}
|
highlight={highlight}
|
||||||
setHighlight={setHighlight}
|
setHighlight={setHighlight}
|
||||||
/>
|
/>
|
||||||
<AsmCell
|
<AsmCell
|
||||||
insDiff={rightIns}
|
obj={result.right}
|
||||||
symbol={right?.symbol}
|
config={config}
|
||||||
|
symbol={right}
|
||||||
|
row={index}
|
||||||
column={1}
|
column={1}
|
||||||
highlight={highlight}
|
highlight={highlight}
|
||||||
setHighlight={setHighlight}
|
setHighlight={setHighlight}
|
||||||
@@ -233,23 +269,41 @@ const AsmRow = memo(
|
|||||||
|
|
||||||
const createItemData = memoizeOne(
|
const createItemData = memoizeOne(
|
||||||
(
|
(
|
||||||
left: SymbolDiff | null,
|
result: diff.DiffResult,
|
||||||
right: SymbolDiff | null,
|
left: display.SectionDisplaySymbol | null,
|
||||||
|
right: display.SectionDisplaySymbol | null,
|
||||||
highlight: HighlightState,
|
highlight: HighlightState,
|
||||||
setHighlight: (highlight: HighlightState) => void,
|
setHighlight: (highlight: HighlightState) => void,
|
||||||
): ItemData => {
|
): ItemData => {
|
||||||
|
const leftSymbol = left ? display.displaySymbol(result.left!, left) : null;
|
||||||
|
const rightSymbol = right
|
||||||
|
? display.displaySymbol(result.right!, right)
|
||||||
|
: null;
|
||||||
const itemCount = Math.max(
|
const itemCount = Math.max(
|
||||||
left?.instructions.length || 0,
|
leftSymbol?.rowCount || 0,
|
||||||
right?.instructions.length || 0,
|
rightSymbol?.rowCount || 0,
|
||||||
);
|
);
|
||||||
return { itemCount, left, right, highlight, setHighlight };
|
const symbolName = leftSymbol?.name || rightSymbol?.name || '';
|
||||||
|
const config = buildDiffConfig(null);
|
||||||
|
return {
|
||||||
|
itemCount,
|
||||||
|
symbolName,
|
||||||
|
result,
|
||||||
|
config,
|
||||||
|
left,
|
||||||
|
leftSymbol,
|
||||||
|
right,
|
||||||
|
rightSymbol,
|
||||||
|
highlight,
|
||||||
|
setHighlight,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const SymbolLabel = ({
|
const SymbolLabel = ({
|
||||||
symbol,
|
symbol,
|
||||||
}: {
|
}: {
|
||||||
symbol: SymbolDiff | null;
|
symbol: display.SymbolDisplay | null;
|
||||||
}) => {
|
}) => {
|
||||||
if (!symbol) {
|
if (!symbol) {
|
||||||
return (
|
return (
|
||||||
@@ -258,21 +312,26 @@ const SymbolLabel = ({
|
|||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const demangledName = symbol.symbol?.demangled_name || symbol.symbol?.name;
|
const displayName = symbol.demangledName || symbol.name;
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={clsx(headerStyles.label, headerStyles.emphasized)}
|
className={clsx(headerStyles.label, headerStyles.emphasized)}
|
||||||
title={demangledName}
|
title={displayName}
|
||||||
>
|
>
|
||||||
{demangledName}
|
{displayName}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const FunctionView = ({
|
const FunctionView = ({
|
||||||
|
diff,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
}: { left: SymbolDiff | null; right: SymbolDiff | null }) => {
|
}: {
|
||||||
|
diff: diff.DiffResult;
|
||||||
|
left: display.SectionDisplaySymbol | null;
|
||||||
|
right: display.SectionDisplaySymbol | null;
|
||||||
|
}) => {
|
||||||
const { buildRunning, currentUnit, lastBuilt } = useExtensionStore(
|
const { buildRunning, currentUnit, lastBuilt } = useExtensionStore(
|
||||||
useShallow((state) => ({
|
useShallow((state) => ({
|
||||||
buildRunning: state.buildRunning,
|
buildRunning: state.buildRunning,
|
||||||
@@ -291,29 +350,27 @@ const FunctionView = ({
|
|||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
const symbolName = left?.symbol?.name || right?.symbol?.name || '';
|
const itemData = createItemData(diff, left, right, highlight, setHighlight);
|
||||||
const initialScrollOffset = useMemo(
|
const initialScrollOffset = useMemo(
|
||||||
() =>
|
() =>
|
||||||
useAppStore.getState().getUnitState(currentUnitName).symbolScrollOffsets[
|
useAppStore.getState().getUnitState(currentUnitName).symbolScrollOffsets[
|
||||||
symbolName
|
itemData.symbolName
|
||||||
] || 0,
|
] || 0,
|
||||||
[currentUnitName, symbolName],
|
[currentUnitName, itemData.symbolName],
|
||||||
);
|
);
|
||||||
|
|
||||||
const itemSize = useFontSize() * 1.33;
|
const itemSize = useFontSize() * 1.33;
|
||||||
const itemData = createItemData(left, right, highlight, setHighlight);
|
|
||||||
const matchPercent = right?.match_percent;
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={headerStyles.header}>
|
<div className={headerStyles.header}>
|
||||||
<div className={headerStyles.column}>
|
<div className={headerStyles.column}>
|
||||||
<div className={headerStyles.row}>
|
<div className={headerStyles.row}>
|
||||||
<button title="Back" onClick={() => setSelectedSymbol(null)}>
|
<button title="Back" onClick={() => setSelectedSymbol(null, null)}>
|
||||||
<span className="codicon codicon-chevron-left" />
|
<span className="codicon codicon-chevron-left" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className={headerStyles.row}>
|
<div className={headerStyles.row}>
|
||||||
<SymbolLabel symbol={left} />
|
<SymbolLabel symbol={itemData.leftSymbol} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={headerStyles.column}>
|
<div className={headerStyles.column}>
|
||||||
@@ -332,20 +389,20 @@ const FunctionView = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={headerStyles.row}>
|
<div className={headerStyles.row}>
|
||||||
{matchPercent !== undefined && (
|
{itemData.matchPercent !== undefined && (
|
||||||
<>
|
<>
|
||||||
<span
|
<span
|
||||||
className={clsx(
|
className={clsx(
|
||||||
headerStyles.label,
|
headerStyles.label,
|
||||||
percentClass(matchPercent),
|
percentClass(itemData.matchPercent),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{Math.floor(matchPercent).toFixed(0)}%
|
{Math.floor(itemData.matchPercent).toFixed(0)}%
|
||||||
</span>
|
</span>
|
||||||
{' | '}
|
{' | '}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<SymbolLabel symbol={right} />
|
<SymbolLabel symbol={itemData.rightSymbol} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -362,7 +419,7 @@ const FunctionView = ({
|
|||||||
onScroll={(e) => {
|
onScroll={(e) => {
|
||||||
setSymbolScrollOffset(
|
setSymbolScrollOffset(
|
||||||
currentUnitName,
|
currentUnitName,
|
||||||
symbolName,
|
itemData.symbolName,
|
||||||
e.scrollOffset,
|
e.scrollOffset,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
@@ -373,6 +430,35 @@ const FunctionView = ({
|
|||||||
)}
|
)}
|
||||||
</AutoSizer>
|
</AutoSizer>
|
||||||
</div>
|
</div>
|
||||||
|
<TooltipShared
|
||||||
|
id="instruction-tooltip"
|
||||||
|
callback={(content) => {
|
||||||
|
const data: InstructionTooltipContent = JSON.parse(content);
|
||||||
|
let obj: diff.ObjectDiff | undefined;
|
||||||
|
let symbol: display.SectionDisplaySymbol | undefined;
|
||||||
|
switch (data.column) {
|
||||||
|
case 0:
|
||||||
|
obj = diff.left;
|
||||||
|
symbol = itemData.left ?? undefined;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
obj = diff.right;
|
||||||
|
symbol = itemData.right ?? undefined;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!obj || !symbol) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return display.instructionHover(
|
||||||
|
obj,
|
||||||
|
symbol,
|
||||||
|
data.row,
|
||||||
|
itemData.config,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.emphasized {
|
.emphasized {
|
||||||
color: light-dark(black, white);
|
color: var(--color-bright);
|
||||||
}
|
}
|
||||||
|
|
||||||
.missing {
|
.missing {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user