Added Required logic if not found, and added better update logic

This commit is contained in:
2026-09-19 22:07:07 +02:00
parent 45c988c989
commit dcd1bf25e7
14 changed files with 661 additions and 735 deletions
+190 -114
View File
@@ -1,25 +1,34 @@
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import * as path from 'path';
import { ScriptCompiler, ScriptCompilerOptions, CompilationError, ICompilationLogger } from 'dcs-script-compiler';
import { Logger } from './logger';
import * as luaAddonsManager from './lua-addons-manager';
import { CompilationErrorType } from '../../compiler/src/CompilationError';
const luaWorkSpaceSettingKey = "Lua.workspace";
const librarySettingsKey = "library";
const diagnosticCollection = vscode.languages.createDiagnosticCollection('lua-transpiler');
// Diagnostic codes
const DIAGNOSTIC_CODE_MISSING_GLOBAL = 'lua-missing-global-dependency';
const logger = new Logger();
const luaAddonNames: string[] = [ "dcs-types" ];
const extensionName = 'dutchies-dcs-scripting-tools';
const publisherName = 'dutchie031';
const extensionSettingsFilter = `@ext:${publisherName}.${extensionName}`;
let luaAddonsManagerInstance: luaAddonsManager.LuaAddonsManager | undefined;
// State tracking for addon updates
let extensionPath: string | undefined;
let workspaceRoot: string | undefined;
let luaAddonsTargetPath: string | undefined;
let currentExtensionVersion: string | undefined;
let installedAddonPaths: Map<string, string> = new Map();
let isUpdatingAddons = false;
//TODO:
@@ -32,9 +41,22 @@ export async function activate(context: vscode.ExtensionContext) {
extensionPath = context.extensionPath;
workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
if (workspaceRoot === undefined) {
vscode.window.showErrorMessage('Workspace root not found. Lua addons manager cannot be initialized.');
return;
}
luaAddonsTargetPath = path.join(workspaceRoot, '.vscode' , 'lua-addons');
luaAddonsManagerInstance = new luaAddonsManager.LuaAddonsManager(luaAddonsTargetPath, context.extensionPath);
if (!luaAddonsManagerInstance) {
vscode.window.showErrorMessage('Failed to initialize Lua addons manager.');
return;
}
// Initialize extension version
try {
currentExtensionVersion = await luaAddonsManager.getExtensionVersion(extensionPath);
currentExtensionVersion = await luaAddonsManagerInstance.getPackageVersion();
logger.debug(`Extension version: ${currentExtensionVersion}`);
} catch (err) {
logger.error(`Failed to determine extension version: ${err instanceof Error ? err.message : String(err)}`);
@@ -70,6 +92,23 @@ export async function activate(context: vscode.ExtensionContext) {
})
);
// Register code actions provider for quick fixes
context.subscriptions.push(
vscode.languages.registerCodeActionsProvider('lua', new LuaQuickFixProvider(), {
providedCodeActionKinds: [vscode.CodeActionKind.QuickFix]
})
);
// Register command to add global requirable
context.subscriptions.push(
vscode.commands.registerCommand(
'dutchies-dcs-scripting-tools.addGlobalRequirable',
async (dependency: string) => {
await addGlobalRequirable(dependency);
}
)
);
vscode.workspace.onDidSaveTextDocument(async(document) => {
logger.info(`Document saved: ${document.uri.fsPath} | Language: ${document.languageId}`);
if (document.languageId === 'lua') {
@@ -164,16 +203,40 @@ async function compileLuaScripts() {
logger.error('Compilation failed: ' + (err as Error).message + '\n' + (err as Error).stack);
}
const globalRequirables: string[] = config.get<string[]>('globalRequirables') || [];
// Update diagnostics
diagnosticCollection.clear();
for (const [filePath, errors] of errorsByFile) {
const uri = vscode.Uri.file(filePath);
const diagnostics = errors.map(error => {
const range = new vscode.Range(error.line, error.charStart ?? 0, error.line, error.charEnd ?? 0);
const diagnostic = new vscode.Diagnostic(range, error.message, vscode.DiagnosticSeverity.Error);
diagnostic.source = 'DCS Lua Transpiler';
return diagnostic;
});
if (error.type === CompilationErrorType.DependencyNotFound) {
const dependencyStr = error.metaData?.get("dependency") ?? undefined;
if (dependencyStr) {
if (globalRequirables.includes(dependencyStr)) {
// Do nothing, it is a globally requirable dependency
const range = new vscode.Range(error.line, error.charStart ?? 0, error.line, error.charEnd ?? 0);
const diagnostic = new vscode.Diagnostic(range, "Unchecked: Globally marked dependency", vscode.DiagnosticSeverity.Hint);
diagnostic.source = 'DCS Lua Transpiler';
return diagnostic;
} else {
const range = new vscode.Range(error.line, error.charStart ?? 0, error.line, error.charEnd ?? 0);
const diagnostic = new vscode.Diagnostic(range, error.message, vscode.DiagnosticSeverity.Error);
diagnostic.code = { value: DIAGNOSTIC_CODE_MISSING_GLOBAL, target: vscode.Uri.parse('https://example.com') };
diagnostic.source = 'DCS Lua Transpiler';
// Store the dependency name for the quick fix to access
(diagnostic as any).dependency = dependencyStr;
return diagnostic;
}
}
return undefined; //Something weird happened, let's ignore it for now.
} else {
const range = new vscode.Range(error.line, error.charStart ?? 0, error.line, error.charEnd ?? 0);
const diagnostic = new vscode.Diagnostic(range, error.message, vscode.DiagnosticSeverity.Error);
diagnostic.source = 'DCS Lua Transpiler';
return diagnostic;
}
}).filter(diagnostic => diagnostic !== undefined);
diagnosticCollection.set(uri, diagnostics);
}
}
@@ -186,6 +249,7 @@ async function enableIntellisense() {
}
await updateLuaAddons();
await addPluginPathsToSettings();
await vscode.commands.executeCommand(
"lua.startServer"
);
@@ -199,18 +263,8 @@ async function disableIntellisense() {
}
await removeVersionedPluginPathsFromSettings();
// Clean up all addon versions from workspace
if (workspaceRoot) {
try {
const discoveredAddons = await luaAddonsManager.discoverLuaAddons(extensionPath || '');
for (const addonName of discoveredAddons.keys()) {
await luaAddonsManager.deleteAllVersionsOfAddon(workspaceRoot, addonName);
logger.debug(`Deleted all versions of ${addonName} from workspace.`);
}
} catch (err) {
logger.error(`Failed to clean up addons: ${err instanceof Error ? err.message : String(err)}`);
}
if(luaAddonsManagerInstance){
await luaAddonsManagerInstance.removeAllExtensions(luaAddonNames);
}
await vscode.commands.executeCommand(
@@ -237,41 +291,28 @@ async function updateLuaAddons(): Promise<void> {
isUpdatingAddons = true;
if (luaAddonsManagerInstance === undefined) {
logger.warn('Lua Addons Manager instance is not available.');
isUpdatingAddons = false;
return;
}
try {
logger.info('Starting lua-addons update...');
// Discover bundled addons
const discoveredAddons = await luaAddonsManager.discoverLuaAddons(extensionPath);
if (discoveredAddons.size === 0) {
logger.info('No lua-addons found in extension bundle.');
isUpdatingAddons = false;
return;
for (const addonName of luaAddonNames) {
const packageVersion = await luaAddonsManagerInstance.getPackageVersion();
const addonVersion = await luaAddonsManagerInstance.getExtensionVersion(addonName);
if (packageVersion && packageVersion !== addonVersion) {
logger.warn(
`Version mismatch for addon ${addonName}: package=${packageVersion}, installed=${addonVersion}`
);
luaAddonsManagerInstance.removeExtension(addonName);
luaAddonsManagerInstance.installExtension(addonName);
}
}
logger.debug(`Discovered ${discoveredAddons.size} lua-addon(s): ${Array.from(discoveredAddons.keys()).join(', ')}`);
// Copy addons with versioned names
const copiedAddons = await luaAddonsManager.copyLuaAddons(
extensionPath,
workspaceRoot,
currentExtensionVersion,
discoveredAddons
);
// Store paths for settings management
installedAddonPaths = copiedAddons;
logger.debug(`Copied ${copiedAddons.size} addon(s) to workspace.`);
// Clean up old versions
for (const addonName of discoveredAddons.keys()) {
await luaAddonsManager.deleteOldVersions(workspaceRoot, addonName, currentExtensionVersion);
logger.debug(`Cleaned old versions of ${addonName}.`);
}
// Update Lua settings with new paths
await addVersionedPluginPathsToSettings(copiedAddons);
logger.info(`Lua-addons update completed successfully (version ${currentExtensionVersion}).`);
} catch (err) {
@@ -304,27 +345,22 @@ async function checkAndUpdateAddonsOnStartup(): Promise<void> {
logger.debug('Checking lua-addons versions on startup...');
// Get installed addons
const installedAddons = await luaAddonsManager.getInstalledAddons(workspaceRoot);
const discoveredAddons = await luaAddonsManager.discoverLuaAddons(extensionPath);
if (!luaAddonsManagerInstance) {
logger.debug('Lua Addons Manager instance is not available, skipping addon version check.');
return;
}
// Check each discovered addon
let updateNeeded = false;
for (const addonName of discoveredAddons.keys()) {
const installed = installedAddons.get(addonName);
const installedVersion = installed?.version;
if (luaAddonsManager.requiresUpdate(installedVersion, currentExtensionVersion)) {
logger.info(
`Version mismatch for ${addonName}: installed=${installedVersion || 'none'}, current=${currentExtensionVersion}`
);
for (const addonName of luaAddonNames) {
const packageVersion = await luaAddonsManagerInstance.getPackageVersion();
const addonVersion = await luaAddonsManagerInstance.getExtensionVersion(addonName);
if (packageVersion && packageVersion !== addonVersion) {
updateNeeded = true;
break;
}
}
if (updateNeeded) {
logger.info('Lua-addons update needed, prompting user...');
const userChoice = await vscode.window.showInformationMessage(
'DCS-Types are not up to date with the current extension version. Do you want to update them?',
{ modal: false },
@@ -337,17 +373,7 @@ async function checkAndUpdateAddonsOnStartup(): Promise<void> {
} else {
logger.info('User declined DCS-Types update.');
}
} else {
logger.debug('Lua-addons versions are current, no update needed.');
// Ensure paths are in settings even if no update needed
const pathsMap = new Map<string, string>();
for (const [name, info] of installedAddons.entries()) {
pathsMap.set(name, info.path);
}
installedAddonPaths = pathsMap;
await addVersionedPluginPathsToSettings(installedAddonPaths);
}
} catch (err) {
logger.error(
`Error checking lua-addons on startup: ${err instanceof Error ? err.message : String(err)}`
@@ -377,14 +403,12 @@ function convertToWorkspaceFolderPath(absolutePath: string): string {
}
/**
* Adds versioned addon paths to the Lua workspace library settings
* @param addonPaths - Map of addon name to installed path
* Adds the lua-addons directory to the Lua workspace library settings.
* Lua Language Server automatically discovers versioned addons within this directory.
*/
async function addVersionedPluginPathsToSettings(addonPaths?: Map<string, string>): Promise<void> {
const pathsToAdd = addonPaths || installedAddonPaths;
if (pathsToAdd.size === 0) {
logger.debug('No addon paths to add to settings.');
async function addPluginPathsToSettings(): Promise<void> {
if (!luaAddonsTargetPath) {
logger.warn('Lua addons target path not available.');
return;
}
@@ -392,58 +416,110 @@ async function addVersionedPluginPathsToSettings(addonPaths?: Map<string, string
const luaSettings = vscode.workspace.getConfiguration(luaWorkSpaceSettingKey);
let librarySettings = luaSettings.get<string[]>(librarySettingsKey) || [];
// Add each addon path if not already present
for (const addonPath of pathsToAdd.values()) {
const workspaceFolderPath = convertToWorkspaceFolderPath(addonPath);
if (!librarySettings.includes(workspaceFolderPath)) {
librarySettings.push(workspaceFolderPath);
logger.debug(`Added addon path to settings: ${workspaceFolderPath}`);
}
const workspaceFolderPath = convertToWorkspaceFolderPath(luaAddonsTargetPath);
if (!librarySettings.includes(workspaceFolderPath)) {
librarySettings.push(workspaceFolderPath);
logger.debug(`Added lua-addons path to Lua settings: ${workspaceFolderPath}`);
await luaSettings.update(librarySettingsKey, librarySettings, vscode.ConfigurationTarget.Workspace);
} else {
logger.debug('Lua-addons path already in settings.');
}
await luaSettings.update(librarySettingsKey, librarySettings, vscode.ConfigurationTarget.Workspace);
} catch (err) {
logger.error(`Failed to add addon paths to settings: ${err instanceof Error ? err.message : String(err)}`);
logger.error(`Failed to add lua-addons path to settings: ${err instanceof Error ? err.message : String(err)}`);
throw err;
}
}
/**
* Removes all addon paths from the Lua workspace library settings
* Removes the lua-addons directory from the Lua workspace library settings
*/
async function removeVersionedPluginPathsFromSettings(): Promise<void> {
if (!luaAddonsTargetPath) {
logger.debug('Lua addons target path not available, skipping removal.');
return;
}
try {
const luaSettings = vscode.workspace.getConfiguration(luaWorkSpaceSettingKey);
let librarySettings = luaSettings.get<string[]>(librarySettingsKey) || [];
const workspaceFolderPath = convertToWorkspaceFolderPath(luaAddonsTargetPath);
const filteredSettings = librarySettings.filter(path => path !== workspaceFolderPath);
if (filteredSettings.length !== librarySettings.length) {
await luaSettings.update(librarySettingsKey, filteredSettings, vscode.ConfigurationTarget.Workspace);
logger.debug(`Removed lua-addons path from Lua settings: ${workspaceFolderPath}`);
} else {
logger.debug('Lua-addons path not found in settings.');
}
} catch (err) {
logger.error(`Failed to remove lua-addons path from settings: ${err instanceof Error ? err.message : String(err)}`);
throw err;
}
}
if (workspaceRoot) {
// Get all installed addons to know what paths to remove
const installedAddons = await luaAddonsManager.getInstalledAddons(workspaceRoot);
/**
* Code actions provider for Lua quick fixes
*/
class LuaQuickFixProvider implements vscode.CodeActionProvider {
provideCodeActions(
document: vscode.TextDocument,
range: vscode.Range | vscode.Selection,
context: vscode.CodeActionContext,
): vscode.CodeAction[] {
const codeActions: vscode.CodeAction[] = [];
// Check for missing global dependency diagnostics
for (const diagnostic of context.diagnostics) {
const codeValue = typeof diagnostic.code === 'object' && diagnostic.code !== null
? (diagnostic.code as any).value
: diagnostic.code;
for (const addonInfo of installedAddons.values()) {
// Try both absolute and workspace-relative paths for compatibility
const absolutePath = addonInfo.path;
const relativePath = convertToWorkspaceFolderPath(absolutePath);
// Remove absolute path if present
let index = librarySettings.indexOf(absolutePath);
if (index !== -1) {
librarySettings.splice(index, 1);
logger.debug(`Removed addon path from settings: ${absolutePath}`);
}
// Remove relative path if present
index = librarySettings.indexOf(relativePath);
if (index !== -1) {
librarySettings.splice(index, 1);
logger.debug(`Removed addon path from settings: ${relativePath}`);
if (codeValue === DIAGNOSTIC_CODE_MISSING_GLOBAL) {
const dependency = (diagnostic as any).dependency;
if (dependency) {
const action = new vscode.CodeAction(
`Mark '${dependency}' as globally available`,
vscode.CodeActionKind.QuickFix
);
action.command = {
title: `Add '${dependency}' to global requireables`,
command: 'dutchies-dcs-scripting-tools.addGlobalRequirable',
arguments: [dependency]
};
action.diagnostics = [diagnostic];
codeActions.push(action);
}
}
}
await luaSettings.update(librarySettingsKey, librarySettings, vscode.ConfigurationTarget.Workspace);
return codeActions;
}
}
/**
* Adds a dependency to the global requireables list in workspace settings
* @param dependency - The dependency name to add
*/
async function addGlobalRequirable(dependency: string): Promise<void> {
try {
const config = vscode.workspace.getConfiguration(extensionName);
const globalRequirables = config.get<string[]>('globalRequirables') || [];
if (!globalRequirables.includes(dependency)) {
globalRequirables.push(dependency);
await config.update('globalRequirables', globalRequirables, vscode.ConfigurationTarget.Workspace);
vscode.window.showInformationMessage(`Added '${dependency}' to global requireables.`);
// Recompile to update diagnostics
await compileLuaScripts();
} else {
vscode.window.showInformationMessage(`'${dependency}' is already in global requireables.`);
}
} catch (err) {
logger.error(`Failed to remove addon paths from settings: ${err instanceof Error ? err.message : String(err)}`);
throw err;
const errorMsg = err instanceof Error ? err.message : String(err);
vscode.window.showErrorMessage(`Failed to add global requirable: ${errorMsg}`);
logger.error(`Error adding global requirable: ${errorMsg}`);
}
}