Skip to content

Commit

Permalink
add shared focus dependency parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
IShiraiKurokoI committed Sep 8, 2024
1 parent 7ab1c33 commit 5094e4a
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 0 deletions.
7 changes: 7 additions & 0 deletions i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ const internalTable = /* SOT Do not remove this comment */{
"scanref.done": "Scan reference done.",
"scanref.noeditor": "No opened editor.",
"scanref.unsupportedtype": "Unsupported file type to scan references.",
"sharedFocusIndex.builddone": "Building shared focus index done.",
"sharedFocusIndex.building": "Building shared focus index...",
"sharedFocusIndex.mod": "[Mod]",
"sharedFocusIndex.parseFailure": "Parsing failed! Please check if the file has issues!",
"sharedFocusIndex.vanilla": "[Vanilla]",
"sharedFocusIndex.workspace.builddone": "Building workspace Focus index done.",
"sharedFocusIndex.workspace.building": "Building workspace Focus index...",
"techtree.cantfindtechfolderin": "Can't find technology folder {0} in {1}.",
"techtree.cantfindtechitemin": "Can't find containerwindowtype \"{0}\" in {1}",
"techtree.cantfindviewin": "Can't find {0} in {1}.",
Expand Down
7 changes: 7 additions & 0 deletions i18n/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ const table: Partial<typeof __table> = {
"scanref.done": "Scan reference done.",
"scanref.noeditor": "No opened editor.",
"scanref.unsupportedtype": "Unsupported file type to scan references.",
"sharedFocusIndex.builddone": "Building shared focus index done.",
"sharedFocusIndex.building": "Building shared focus index...",
"sharedFocusIndex.mod": "[Mod]",
"sharedFocusIndex.parseFailure": "Parsing failed! Please check if the file has issues!",
"sharedFocusIndex.vanilla": "[Vanilla]",
"sharedFocusIndex.workspace.builddone": "Building workspace Focus index done.",
"sharedFocusIndex.workspace.building": "Building workspace Focus index...",
"techtree.cantfindtechfolderin": "Can't find technology folder {0} in {1}.",
"techtree.cantfindtechitemin": "Can't find containerwindowtype \"{0}\" in {1}",
"techtree.cantfindviewin": "Can't find {0} in {1}.",
Expand Down
7 changes: 7 additions & 0 deletions i18n/zh-cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ const table: Partial<typeof __table> = {
"scanref.done": "扫描引用完毕。",
"scanref.noeditor": "无打开的编辑器。",
"scanref.unsupportedtype": "无法扫描此文件的引用。",
"sharedFocusIndex.builddone": "共享国策索引构建完成。",
"sharedFocusIndex.building": "正在构建共享国策索引...",
"sharedFocusIndex.mod": "[模组]",
"sharedFocusIndex.parseFailure": "解析失败!请检查文件是否存在问题!",
"sharedFocusIndex.vanilla": "[原版]",
"sharedFocusIndex.workspace.builddone": "工作区国策索引构建完成。",
"sharedFocusIndex.workspace.building": "正在构建工作区国策索引...",
"techtree.cantfindtechfolderin": "在{1}里找不到科技树目录{0}。",
"techtree.cantfindtechitemin": "在{1}里找不到containerwindowtype \"{0}\"。",
"techtree.cantfindviewin": "在{1}里找不到控件{0}。",
Expand Down
2 changes: 2 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { loadI18n } from './util/i18n';
import { registerGfxIndex } from './util/gfxindex';
import { Logger } from "./util/logger";
import { registerLocalisationIndex } from "./util/localisationIndex";
import { registerSharedFocusIndex } from "./util/sharedFocusIndex";

export function activate(context: vscode.ExtensionContext) {
let locale = (context as any).extension?.packageJSON.locale;
Expand All @@ -37,6 +38,7 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(registerHoiFs());
context.subscriptions.push(vscode.window.registerCustomEditorProvider(ViewType.DDS, new DDSViewProvider()));
context.subscriptions.push(vscode.window.registerCustomEditorProvider(ViewType.TGA, new TGAViewProvider()));
context.subscriptions.push(registerSharedFocusIndex());
context.subscriptions.push(registerGfxIndex());
context.subscriptions.push(registerLocalisationIndex());

Expand Down
23 changes: 23 additions & 0 deletions src/util/dependency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { getEvents, HOIEvents, HOIEvent } from "../previewdef/event/schema";
import { getLanguageIdInYml, getRelativePathInWorkspace, isSameUri } from "./vsccommon";
import { flatMap, flatten } from "lodash";
import { parseYaml } from "./yaml";
import { findFileByFocusKey } from "./sharedFocusIndex";
import { sharedFocusIndex } from "./featureflags";

export type Dependency = { type: string, path: string };

Expand All @@ -30,6 +32,27 @@ export function getDependenciesFromText(text: string): Dependency[] {
match = regex.exec(text);
}

if (sharedFocusIndex){
// Regex to match "shared_focus = focus_key" with variable length spaces, where focus_key does not start with "{"
const sharedFocusRegex = /\s*shared_focus\s*=\s*(?!\{)(\S+)/g;
let sharedFocusMatch = sharedFocusRegex.exec(text);

while (sharedFocusMatch) {
const focusKey = sharedFocusMatch[1].trim(); // The focus key after '='

// Use the Index (e.g., findFileByFocusKey) to get the file path
const filePath = findFileByFocusKey(focusKey);

if (filePath) {
dependencies.push({ type: 'focus', path: filePath });
} else {
console.warn(`Focus key "${focusKey}" could not be found in the index.`);
}

sharedFocusMatch = sharedFocusRegex.exec(text);
}
}

return dependencies;
}

Expand Down
1 change: 1 addition & 0 deletions src/util/featureflags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ const featureFlags = getConfiguration().featureFlags;

export const useConditionInFocus = !featureFlags.includes('!useConditionInFocus');
export const eventTreePreview = !featureFlags.includes('!eventTreePreview');
export const sharedFocusIndex = !featureFlags.includes('!sharedFocusIndex');
export const gfxIndex = featureFlags.includes('gfxIndex');
export const localisationIndex = featureFlags.includes('localisationIndex');
190 changes: 190 additions & 0 deletions src/util/sharedFocusIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import * as vscode from 'vscode';
import * as path from 'path';
import { debounceByInput } from './common';
import { listFilesFromModOrHOI4, readFileFromModOrHOI4 } from './fileloader';
import { localize } from './i18n';
import { sendEvent } from './telemetry';
import { Logger } from "./logger";
import { getFocusTree } from "../previewdef/focustree/schema";
import { parseHoi4File } from "../hoiformat/hoiparser";
import { sharedFocusIndex } from "./featureflags";

interface FocusIndex {
[file: string]: string[]; // Filename -> array of focus keys
}

const globalFocusIndex: FocusIndex = {};
let workspaceFocusIndex: FocusIndex = {};

export function registerSharedFocusIndex(): vscode.Disposable {
const disposables: vscode.Disposable[] = [];

if (sharedFocusIndex) {
const estimatedSize: [number] = [0];

const task = Promise.all([
buildGlobalFocusIndex(estimatedSize),
buildWorkspaceFocusIndex(estimatedSize)
]);

vscode.window.setStatusBarMessage('$(loading~spin) ' + localize('sharedFocusIndex.building', 'Building Shared Focus index...'), task);
task.then(() => {
vscode.window.showInformationMessage(localize('sharedFocusIndex.builddone', 'Building Shared Focus index done.'));
sendEvent('sharedFocusIndex', { size: estimatedSize[0].toString() });
});

disposables.push(vscode.workspace.onDidChangeWorkspaceFolders(onChangeWorkspaceFolders));
disposables.push(vscode.workspace.onDidChangeTextDocument(onChangeTextDocument));
disposables.push(vscode.workspace.onDidCloseTextDocument(onCloseTextDocument));
disposables.push(vscode.workspace.onDidCreateFiles(onCreateFiles));
disposables.push(vscode.workspace.onDidDeleteFiles(onDeleteFiles));
disposables.push(vscode.workspace.onDidRenameFiles(onRenameFiles));
}

return vscode.Disposable.from(...disposables);
}

async function buildGlobalFocusIndex(estimatedSize: [number]): Promise<void> {
const options = { mod: false, hoi4: true, recursively: true };
const focusFiles = await listFilesFromModOrHOI4('common/national_focus', options);
await Promise.all(focusFiles.map(f => fillFocusItems('common/national_focus/' + f, globalFocusIndex, options, estimatedSize)));
}

async function buildWorkspaceFocusIndex(estimatedSize: [number]): Promise<void> {
const options = { mod: true, hoi4: false, recursively: true };
const focusFiles = await listFilesFromModOrHOI4('common/national_focus', options);
await Promise.all(focusFiles.map(f => fillFocusItems('common/national_focus/' + f, workspaceFocusIndex, options, estimatedSize)));
}

async function fillFocusItems(focusFile: string, focusIndex: FocusIndex, options: { mod?: boolean; hoi4?: boolean }, estimatedSize?: [number]): Promise<void> {
const [fileBuffer, uri] = await readFileFromModOrHOI4(focusFile, options);
const fileContent = fileBuffer.toString();

try {
const sharedFocusTrees: any[] = [];
const focusTrees = getFocusTree(parseHoi4File(fileContent, localize('infile', 'In file {0}:\n', focusFile)), sharedFocusTrees, focusFile);

// Only store focus trees where isSharedFocues is true
focusTrees.forEach(tree => {
if (tree.isSharedFocues) {
const focusKeys = Object.keys(tree.focuses);
focusIndex[focusFile] = focusKeys;
}
});

if (estimatedSize) {
estimatedSize[0] += fileBuffer.length;
}
} catch (e) {
const baseMessage = options.hoi4
? localize('sharedFocusIndex.vanilla', '[Vanilla]')
: localize('sharedFocusIndex.mod', '[Mod]');

const failureMessage = localize('sharedFocusIndex.parseFailure', 'Parsing failed! Please check if the file has issues!');
if (e instanceof Error) {
Logger.error(`${baseMessage} ${focusFile} ${failureMessage}\n${e.stack}`);
}
}
}

// Function to find the file name containing the specified focus key
export function findFileByFocusKey(key: string): string | undefined {
let result: string | undefined;

// Search in globalFocusIndex first
for (const file in globalFocusIndex) {
if (globalFocusIndex[file].includes(key)) {
result = file;
break;
}
}

// Always search in workspaceFocusIndex, and if found, override the result
for (const file in workspaceFocusIndex) {
if (workspaceFocusIndex[file].includes(key)) {
result = file;
break;
}
}

return result;
}

function onChangeWorkspaceFolders(_: vscode.WorkspaceFoldersChangeEvent) {
// Clear the workspace focus index
workspaceFocusIndex = {};

const estimatedSize: [number] = [0];
const task = buildWorkspaceFocusIndex(estimatedSize);
vscode.window.setStatusBarMessage('$(loading~spin) ' + localize('sharedFocusIndex.workspace.building', 'Building workspace Focus index...'), task);
task.then(() => {
vscode.window.showInformationMessage(localize('sharedFocusIndex.workspace.builddone', 'Building workspace Focus index done.'));
sendEvent('sharedFocusIndex.workspace', { size: estimatedSize[0].toString() });
});
}

function onChangeTextDocument(e: vscode.TextDocumentChangeEvent) {
const file = e.document.uri;
if (file.path.endsWith('.txt')) {
onChangeTextDocumentImpl(file);
}
}

const onChangeTextDocumentImpl = debounceByInput(
(file: vscode.Uri) => {
removeWorkspaceFocusIndex(file);
addWorkspaceFocusIndex(file);
},
file => file.toString(),
1000,
{ trailing: true }
);

function onCloseTextDocument(document: vscode.TextDocument) {
const file = document.uri;
if (file.path.endsWith('.txt')) {
removeWorkspaceFocusIndex(file);
addWorkspaceFocusIndex(file);
}
}

function onCreateFiles(e: vscode.FileCreateEvent) {
for (const file of e.files) {
if (file.path.endsWith('.txt')) {
addWorkspaceFocusIndex(file);
}
}
}

function onDeleteFiles(e: vscode.FileDeleteEvent) {
for (const file of e.files) {
if (file.path.endsWith('.txt')) {
removeWorkspaceFocusIndex(file);
}
}
}

function onRenameFiles(e: vscode.FileRenameEvent) {
onDeleteFiles({ files: e.files.map(f => f.oldUri) });
onCreateFiles({ files: e.files.map(f => f.newUri) });
}

function removeWorkspaceFocusIndex(file: vscode.Uri) {
const wsFolder = vscode.workspace.getWorkspaceFolder(file);
if (wsFolder) {
const relative = path.relative(wsFolder.uri.path, file.path).replace(/\\+/g, '/');
if (relative && relative.startsWith('common/national_focus/')) {
delete workspaceFocusIndex[relative];
}
}
}

function addWorkspaceFocusIndex(file: vscode.Uri) {
const wsFolder = vscode.workspace.getWorkspaceFolder(file);
if (wsFolder) {
const relative = path.relative(wsFolder.uri.path, file.path).replace(/\\+/g, '/');
if (relative && relative.startsWith('common/national_focus/')) {
fillFocusItems(relative, workspaceFocusIndex, { hoi4: false });
}
}
}

0 comments on commit 5094e4a

Please sign in to comment.