mirror of
https://github.com/open-goal/opengoal-vscode.git
synced 2024-10-19 20:47:37 -04:00
decomp: add a feature that compares two function bodies and if they are the same, copies the name changes (#333)
This commit is contained in:
parent
b3ce6e7ef4
commit
4cc9e61c33
|
@ -139,6 +139,10 @@
|
||||||
"command": "opengoal.decomp.misc.batchRenameUnnamedVars",
|
"command": "opengoal.decomp.misc.batchRenameUnnamedVars",
|
||||||
"title": "OpenGOAL - Misc - Batch Rename Unnamed Vars"
|
"title": "OpenGOAL - Misc - Batch Rename Unnamed Vars"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "opengoal.decomp.misc.compareFuncWithJak2",
|
||||||
|
"title": "OpenGOAL - Misc - Compare Func with Jak 2"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "opengoal.decomp.typeSearcher.open",
|
"command": "opengoal.decomp.typeSearcher.open",
|
||||||
"title": "OpenGOAL - Misc - Type Searcher"
|
"title": "OpenGOAL - Misc - Type Searcher"
|
||||||
|
|
|
@ -15,6 +15,11 @@ import {
|
||||||
import { activateDecompTypeSearcher } from "./type-searcher/type-searcher";
|
import { activateDecompTypeSearcher } from "./type-searcher/type-searcher";
|
||||||
import { updateTypeCastSuggestions } from "./type-caster";
|
import { updateTypeCastSuggestions } from "./type-caster";
|
||||||
import { glob } from "fast-glob";
|
import { glob } from "fast-glob";
|
||||||
|
import {
|
||||||
|
getFuncBodyFromPosition,
|
||||||
|
getFuncNameFromPosition,
|
||||||
|
} from "../languages/ir2/ir2-utils";
|
||||||
|
import { copyVarCastsFromOneGameToAnother } from "./utils";
|
||||||
|
|
||||||
const execFileAsync = util.promisify(execFile);
|
const execFileAsync = util.promisify(execFile);
|
||||||
const execAsync = util.promisify(exec);
|
const execAsync = util.promisify(exec);
|
||||||
|
@ -155,10 +160,17 @@ async function checkDecompilerPath(): Promise<string | undefined> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function decompFiles(
|
async function decompFiles(
|
||||||
decompConfig: string,
|
|
||||||
gameName: GameName,
|
gameName: GameName,
|
||||||
fileNames: string[],
|
fileNames: string[],
|
||||||
|
omitVariableCasts: boolean = false,
|
||||||
) {
|
) {
|
||||||
|
const decompConfig = getDecompilerConfig(gameName);
|
||||||
|
if (decompConfig === undefined) {
|
||||||
|
await vscode.window.showErrorMessage(
|
||||||
|
`OpenGOAL - Can't decompile no ${gameName.toString} config selected`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (fileNames.length == 0) {
|
if (fileNames.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -180,8 +192,16 @@ async function decompFiles(
|
||||||
"--version",
|
"--version",
|
||||||
getDecompilerConfigVersion(gameName),
|
getDecompilerConfigVersion(gameName),
|
||||||
"--config-override",
|
"--config-override",
|
||||||
`{"decompile_code": true, "print_cfgs": true, "levels_extract": false, "allowed_objects": [${allowed_objects}]}`,
|
|
||||||
];
|
];
|
||||||
|
if (omitVariableCasts) {
|
||||||
|
args.push(
|
||||||
|
`{"decompile_code": true, "print_cfgs": true, "levels_extract": false, "ignore_var_name_casts": true,"allowed_objects": [${allowed_objects}]}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
args.push(
|
||||||
|
`{"decompile_code": true, "print_cfgs": true, "levels_extract": false, "allowed_objects": [${allowed_objects}]}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
const { stdout, stderr } = await execFileAsync(decompilerPath, args, {
|
const { stdout, stderr } = await execFileAsync(decompilerPath, args, {
|
||||||
encoding: "utf8",
|
encoding: "utf8",
|
||||||
cwd: getProjectRoot()?.fsPath,
|
cwd: getProjectRoot()?.fsPath,
|
||||||
|
@ -218,10 +238,8 @@ async function getValidObjectNames(gameName: string) {
|
||||||
for (const obj of objs) {
|
for (const obj of objs) {
|
||||||
const is_tpage = obj[0].includes("tpage");
|
const is_tpage = obj[0].includes("tpage");
|
||||||
const is_art_file = obj[0].endsWith("-ag");
|
const is_art_file = obj[0].endsWith("-ag");
|
||||||
if (obj[2] == 4 || obj[2] == 5) {
|
if (!is_tpage && !is_art_file) {
|
||||||
if (!is_tpage && !is_art_file) {
|
names.push(obj[0]);
|
||||||
names.push(obj[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return names;
|
return names;
|
||||||
|
@ -268,16 +286,7 @@ async function decompSpecificFile() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine what decomp config to use
|
await decompFiles(gameName, [fileName]);
|
||||||
const decompConfig = getDecompilerConfig(gameName);
|
|
||||||
if (decompConfig === undefined) {
|
|
||||||
await vscode.window.showErrorMessage(
|
|
||||||
`OpenGOAL - Can't decompile no ${gameName.toString} config selected`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await decompFiles(decompConfig, gameName, [fileName]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function decompCurrentFile() {
|
async function decompCurrentFile() {
|
||||||
|
@ -307,15 +316,8 @@ async function decompCurrentFile() {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const decompConfig = getDecompilerConfig(gameName);
|
|
||||||
if (decompConfig === undefined) {
|
|
||||||
await vscode.window.showErrorMessage(
|
|
||||||
`OpenGOAL - Can't decompile no ${gameName.toString} config selected`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await decompFiles(decompConfig, gameName, [fileName]);
|
await decompFiles(gameName, [fileName]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function decompAllActiveFiles() {
|
async function decompAllActiveFiles() {
|
||||||
|
@ -356,35 +358,13 @@ async function decompAllActiveFiles() {
|
||||||
jak3ObjectNames = [...new Set(jak3ObjectNames)];
|
jak3ObjectNames = [...new Set(jak3ObjectNames)];
|
||||||
|
|
||||||
if (jak1ObjectNames.length > 0) {
|
if (jak1ObjectNames.length > 0) {
|
||||||
const jak1Config = getDecompilerConfig(GameName.Jak1);
|
await decompFiles(GameName.Jak1, jak1ObjectNames);
|
||||||
if (jak1Config === undefined) {
|
|
||||||
await vscode.window.showErrorMessage(
|
|
||||||
"OpenGOAL - Can't decompile, no Jak 1 config selected",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await decompFiles(jak1Config, GameName.Jak1, jak1ObjectNames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jak2ObjectNames.length > 0) {
|
if (jak2ObjectNames.length > 0) {
|
||||||
const jak2Config = getDecompilerConfig(GameName.Jak2);
|
await decompFiles(GameName.Jak2, jak2ObjectNames);
|
||||||
if (jak2Config === undefined) {
|
|
||||||
await vscode.window.showErrorMessage(
|
|
||||||
"OpenGOAL - Can't decompile, no Jak 2 config selected",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await decompFiles(jak2Config, GameName.Jak2, jak2ObjectNames);
|
|
||||||
}
|
}
|
||||||
if (jak3ObjectNames.length > 0) {
|
if (jak3ObjectNames.length > 0) {
|
||||||
const jak3Config = getDecompilerConfig(GameName.Jak3);
|
await decompFiles(GameName.Jak3, jak3ObjectNames);
|
||||||
if (jak3Config === undefined) {
|
|
||||||
await vscode.window.showErrorMessage(
|
|
||||||
"OpenGOAL - Can't decompile, no Jak 3 config selected",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await decompFiles(jak3Config, GameName.Jak3, jak3ObjectNames);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,6 +514,111 @@ async function updateReferenceTest() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function compareFunctionWithJak2() {
|
||||||
|
const editor = vscode.window.activeTextEditor;
|
||||||
|
if (!editor || !editor.document === undefined) {
|
||||||
|
await vscode.window.showErrorMessage(
|
||||||
|
"No active file open, can't compare decompiler output!",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let fileName = path.basename(editor.document.fileName);
|
||||||
|
if (!fileName.match(/.*_ir2\.asm/)) {
|
||||||
|
await vscode.window.showErrorMessage(
|
||||||
|
"Current file is not a valid IR2 file, can't compare!",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
fileName = fileName.split("_ir2.asm")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0. Determine the current function we are interested in comparing
|
||||||
|
const funcName = getFuncNameFromPosition(
|
||||||
|
editor.document,
|
||||||
|
editor.selection.start,
|
||||||
|
);
|
||||||
|
if (funcName === undefined) {
|
||||||
|
await vscode.window.showErrorMessage(
|
||||||
|
"Couldn't determine function name to compare with jak 2!",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 1. Run the decompiler on the same file in jak 2 without variable names
|
||||||
|
await decompFiles(GameName.Jak2, [fileName], true);
|
||||||
|
// 2. Go grab that file's contents, find the same function and grab it, cut out the docstring if it's there (and save it)
|
||||||
|
const decompiledOutput = (
|
||||||
|
await fs.readFile(
|
||||||
|
path.join(
|
||||||
|
getProjectRoot()?.fsPath,
|
||||||
|
"decompiler_out",
|
||||||
|
"jak2",
|
||||||
|
`${fileName}_ir2.asm`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.toString()
|
||||||
|
.split("\n");
|
||||||
|
let foundFunc = false;
|
||||||
|
let foundFuncBody = false;
|
||||||
|
const funcBody = [];
|
||||||
|
const docstring = [];
|
||||||
|
for (const line of decompiledOutput) {
|
||||||
|
if (line.includes(`; .function ${funcName}`)) {
|
||||||
|
foundFunc = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (foundFunc && line.includes(";;-*-OpenGOAL-Start-*-")) {
|
||||||
|
foundFuncBody = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (foundFuncBody) {
|
||||||
|
if (line.includes(";;-*-OpenGOAL-End-*-")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line.trim() === ``) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// NOTE - this will fail with functions with multi-line signatures
|
||||||
|
if (
|
||||||
|
funcBody.length === 1 &&
|
||||||
|
(line.trim().startsWith('"') || !line.trim().startsWith("("))
|
||||||
|
) {
|
||||||
|
docstring.push(line.trimEnd());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
funcBody.push(line.trimEnd());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3. Compare the two, if they match, then copy over any var-name changes and put the docstring in the clipboard
|
||||||
|
const jak3FuncBody = getFuncBodyFromPosition(
|
||||||
|
editor.document,
|
||||||
|
editor.selection.start,
|
||||||
|
);
|
||||||
|
if (jak3FuncBody === undefined) {
|
||||||
|
await vscode.window.showErrorMessage(
|
||||||
|
"Couldn't determine function body in jak 3!",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (funcBody.join("\n") === jak3FuncBody.join("\n")) {
|
||||||
|
// Update var casts
|
||||||
|
await copyVarCastsFromOneGameToAnother(
|
||||||
|
editor.document,
|
||||||
|
GameName.Jak2,
|
||||||
|
GameName.Jak3,
|
||||||
|
funcName,
|
||||||
|
);
|
||||||
|
await vscode.window.showInformationMessage(
|
||||||
|
"Function bodies match! Docstring copied to clipboard if it was found.",
|
||||||
|
);
|
||||||
|
if (docstring.length > 0) {
|
||||||
|
await vscode.env.clipboard.writeText(docstring.join("\n"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await vscode.window.showWarningMessage("Function bodies don't match!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function activateDecompTools() {
|
export async function activateDecompTools() {
|
||||||
// no color support :( - https://github.com/microsoft/vscode/issues/571
|
// no color support :( - https://github.com/microsoft/vscode/issues/571
|
||||||
channel = vscode.window.createOutputChannel(
|
channel = vscode.window.createOutputChannel(
|
||||||
|
@ -580,6 +665,12 @@ export async function activateDecompTools() {
|
||||||
updateReferenceTest,
|
updateReferenceTest,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
getExtensionContext().subscriptions.push(
|
||||||
|
vscode.commands.registerCommand(
|
||||||
|
"opengoal.decomp.misc.compareFuncWithJak2",
|
||||||
|
compareFunctionWithJak2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
activateDecompTypeSearcher();
|
activateDecompTypeSearcher();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,30 @@ import { ArgumentMeta } from "../languages/opengoal/opengoal-tools";
|
||||||
import { determineGameFromPath, GameName } from "../utils/file-utils";
|
import { determineGameFromPath, GameName } from "../utils/file-utils";
|
||||||
import { getWorkspaceFolderByName } from "../utils/workspace";
|
import { getWorkspaceFolderByName } from "../utils/workspace";
|
||||||
|
|
||||||
|
export function getCastFilePathForGame(
|
||||||
|
projectRoot: vscode.Uri,
|
||||||
|
gameName: GameName,
|
||||||
|
fileName: string,
|
||||||
|
): string {
|
||||||
|
const config = getConfig();
|
||||||
|
if (gameName == GameName.Jak1) {
|
||||||
|
return vscode.Uri.joinPath(
|
||||||
|
projectRoot,
|
||||||
|
`decompiler/config/jak1/${config.jak1DecompConfigVersion}/${fileName}`,
|
||||||
|
).fsPath;
|
||||||
|
} else if (gameName == GameName.Jak2) {
|
||||||
|
return vscode.Uri.joinPath(
|
||||||
|
projectRoot,
|
||||||
|
`decompiler/config/jak2/${config.jak2DecompConfigVersion}/${fileName}`,
|
||||||
|
).fsPath;
|
||||||
|
} else {
|
||||||
|
return vscode.Uri.joinPath(
|
||||||
|
projectRoot,
|
||||||
|
`decompiler/config/jak3/${config.jak3DecompConfigVersion}/${fileName}`,
|
||||||
|
).fsPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getCastFileData(
|
export function getCastFileData(
|
||||||
projectRoot: vscode.Uri,
|
projectRoot: vscode.Uri,
|
||||||
document: vscode.TextDocument,
|
document: vscode.TextDocument,
|
||||||
|
@ -16,24 +40,20 @@ export function getCastFileData(
|
||||||
if (gameName === undefined) {
|
if (gameName === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const config = getConfig();
|
const castFilePath = getCastFilePathForGame(projectRoot, gameName, fileName);
|
||||||
let castFilePath = "";
|
if (!existsSync(castFilePath)) {
|
||||||
if (gameName == GameName.Jak1) {
|
return undefined;
|
||||||
castFilePath = vscode.Uri.joinPath(
|
|
||||||
projectRoot,
|
|
||||||
`decompiler/config/jak1/${config.jak1DecompConfigVersion}/${fileName}`,
|
|
||||||
).fsPath;
|
|
||||||
} else if (gameName == GameName.Jak2) {
|
|
||||||
castFilePath = vscode.Uri.joinPath(
|
|
||||||
projectRoot,
|
|
||||||
`decompiler/config/jak2/${config.jak2DecompConfigVersion}/${fileName}`,
|
|
||||||
).fsPath;
|
|
||||||
} else if (gameName == GameName.Jak3) {
|
|
||||||
castFilePath = vscode.Uri.joinPath(
|
|
||||||
projectRoot,
|
|
||||||
`decompiler/config/jak3/${config.jak3DecompConfigVersion}/${fileName}`,
|
|
||||||
).fsPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return parse(readFileSync(castFilePath).toString(), undefined, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCastFileDataForGame(
|
||||||
|
projectRoot: vscode.Uri,
|
||||||
|
gameName: GameName,
|
||||||
|
fileName: string,
|
||||||
|
): any | undefined {
|
||||||
|
const castFilePath = getCastFilePathForGame(projectRoot, gameName, fileName);
|
||||||
if (!existsSync(castFilePath)) {
|
if (!existsSync(castFilePath)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -268,7 +288,7 @@ export async function bulkUpdateVarCasts(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write out cast file change
|
// Write out cast file change
|
||||||
const configDir = await getDecompilerConfigDirectory(document.uri);
|
const configDir = getDecompilerConfigDirectory(document.uri);
|
||||||
if (configDir === undefined) {
|
if (configDir === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -276,3 +296,48 @@ export async function bulkUpdateVarCasts(
|
||||||
|
|
||||||
writeFileSync(filePath, stringify(varNameData, null, 2));
|
writeFileSync(filePath, stringify(varNameData, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function copyVarCastsFromOneGameToAnother(
|
||||||
|
document: vscode.TextDocument,
|
||||||
|
oldGame: GameName,
|
||||||
|
newGame: GameName,
|
||||||
|
funcName: string,
|
||||||
|
) {
|
||||||
|
const projectRoot = getWorkspaceFolderByName("jak-project");
|
||||||
|
if (projectRoot === undefined) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
"OpenGOAL - Unable to locate 'jak-project' workspace folder",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldVarNameData = getCastFileDataForGame(
|
||||||
|
projectRoot,
|
||||||
|
oldGame,
|
||||||
|
"var_names.jsonc",
|
||||||
|
);
|
||||||
|
if (oldVarNameData === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newVarNameData = getCastFileDataForGame(
|
||||||
|
projectRoot,
|
||||||
|
newGame,
|
||||||
|
"var_names.jsonc",
|
||||||
|
);
|
||||||
|
if (newVarNameData === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(funcName in oldVarNameData)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newVarNameData[funcName] = oldVarNameData[funcName];
|
||||||
|
// Write out cast file change
|
||||||
|
const configDir = getDecompilerConfigDirectory(document.uri);
|
||||||
|
if (configDir === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const filePath = join(configDir, "var_names.jsonc");
|
||||||
|
|
||||||
|
writeFileSync(filePath, stringify(newVarNameData, null, 2));
|
||||||
|
}
|
||||||
|
|
|
@ -21,10 +21,10 @@ export function insideGoalCodeInIR(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFuncNameFromPosition(
|
export function getFuncNameFromPosition(
|
||||||
document: vscode.TextDocument,
|
document: vscode.TextDocument,
|
||||||
position: vscode.Position,
|
position: vscode.Position,
|
||||||
): Promise<string | undefined> {
|
): string | undefined {
|
||||||
const funcNameRegex = /; \.function (.*).*/g;
|
const funcNameRegex = /; \.function (.*).*/g;
|
||||||
for (let i = position.line; i >= 0; i--) {
|
for (let i = position.line; i >= 0; i--) {
|
||||||
const line = document.lineAt(i).text;
|
const line = document.lineAt(i).text;
|
||||||
|
@ -33,9 +33,7 @@ export async function getFuncNameFromPosition(
|
||||||
return matches[0][1].toString();
|
return matches[0][1].toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await vscode.window.showErrorMessage(
|
vscode.window.showErrorMessage("Couldn't determine function or method name");
|
||||||
"Couldn't determine function or method name",
|
|
||||||
);
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,3 +43,52 @@ export async function getFuncNameFromSelection(
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
return await getFuncNameFromPosition(document, selection.start);
|
return await getFuncNameFromPosition(document, selection.start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFuncBodyFromPosition(
|
||||||
|
document: vscode.TextDocument,
|
||||||
|
position: vscode.Position,
|
||||||
|
): string[] | undefined {
|
||||||
|
let funcName = undefined;
|
||||||
|
let funcNamePosition = 0;
|
||||||
|
const funcNameRegex = /; \.function (.*).*/g;
|
||||||
|
for (let i = position.line; i >= 0; i--) {
|
||||||
|
const line = document.lineAt(i).text;
|
||||||
|
const matches = [...line.matchAll(funcNameRegex)];
|
||||||
|
if (matches.length == 1) {
|
||||||
|
funcName = matches[0][1].toString();
|
||||||
|
funcNamePosition = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (funcName === undefined) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
"Couldn't determine function or method name",
|
||||||
|
);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// Find the function body
|
||||||
|
let foundFunc = false;
|
||||||
|
let foundFuncBody = false;
|
||||||
|
const funcBody = [];
|
||||||
|
for (let i = funcNamePosition; i <= document.lineCount; i++) {
|
||||||
|
const line = document.lineAt(i).text;
|
||||||
|
if (line.includes(`; .function ${funcName}`)) {
|
||||||
|
foundFunc = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (foundFunc && line.includes(";;-*-OpenGOAL-Start-*-")) {
|
||||||
|
foundFuncBody = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (foundFuncBody) {
|
||||||
|
if (line.includes(";;-*-OpenGOAL-End-*-")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line.trim() === ``) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
funcBody.push(line.trimEnd());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return funcBody;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue