Vendored
+5
-1
@@ -9,5 +9,9 @@
|
|||||||
"dist": true // set this to false to include "dist" folder in search results
|
"dist": true // set this to false to include "dist" folder in search results
|
||||||
},
|
},
|
||||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||||
"typescript.tsc.autoDetect": "off"
|
"typescript.tsc.autoDetect": "off",
|
||||||
|
"cSpell.words": [
|
||||||
|
"dutchie",
|
||||||
|
"dutchies"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
+161
-37
@@ -4,6 +4,8 @@ import * as path from 'path';
|
|||||||
export interface CompilationError {
|
export interface CompilationError {
|
||||||
filePath: string;
|
filePath: string;
|
||||||
line: number;
|
line: number;
|
||||||
|
charStart?: number;
|
||||||
|
charEnd?: number;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,49 +17,93 @@ export interface ScriptCompilerOptions {
|
|||||||
onError?: (error: CompilationError) => void
|
onError?: (error: CompilationError) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ICompilationLogger {
|
||||||
|
info(message: string): void;
|
||||||
|
error(message: string): void;
|
||||||
|
writeLine(message: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Metrics {
|
||||||
|
public totalLinesRead : number = 0;
|
||||||
|
public totalLinesWritten: number = 0;
|
||||||
|
public readTimeMs : number = 0;
|
||||||
|
public writeTimeMs : number = 0;
|
||||||
|
public totalTimeMs : number = 0;
|
||||||
|
public filesRead : number = 0;
|
||||||
|
public filesWritten : number = 0;
|
||||||
|
|
||||||
|
log(logger: ICompilationLogger){
|
||||||
|
logger.writeLine("Compilation Metrics: ")
|
||||||
|
logger.writeLine(`=====================`)
|
||||||
|
logger.writeLine(`Lines Read: ${this.totalLinesRead}`)
|
||||||
|
logger.writeLine(`Lines Written: ${this.totalLinesWritten}`)
|
||||||
|
logger.writeLine(`Files Processed: ${this.filesRead} | ${this.filesWritten}`)
|
||||||
|
logger.writeLine(`=====================`)
|
||||||
|
logger.writeLine(`Read Time (ms): ${this.readTimeMs} ms`)
|
||||||
|
logger.writeLine(`Write Time (ms): ${this.writeTimeMs} ms`)
|
||||||
|
logger.writeLine(`Total Time (ms): ${this.totalTimeMs} ms`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const LUA_SCRIPT_GLOBAL_KEYWORD = 'ScriptGlobals';
|
const LUA_SCRIPT_GLOBAL_KEYWORD = 'ScriptGlobals';
|
||||||
|
|
||||||
export class ScriptCompiler {
|
export class ScriptCompiler {
|
||||||
|
|
||||||
constructor(private options: ScriptCompilerOptions) {
|
constructor(private options: ScriptCompilerOptions, private logger: ICompilationLogger) {
|
||||||
if (options.outputFileName === undefined) {
|
if (options.outputFileName === undefined) {
|
||||||
this.options.outputFileName = 'compiled.lua';
|
this.options.outputFileName = 'compiled.lua';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async compile(): Promise<void> {
|
public async compile(includeDevScript: boolean): Promise<void> {
|
||||||
|
const start = Date.now();
|
||||||
|
const metricsMeter = new Metrics();
|
||||||
const entries = fs.readdirSync(this.options.sourcePath, { recursive: true, withFileTypes: true });
|
const entries = fs.readdirSync(this.options.sourcePath, { recursive: true, withFileTypes: true });
|
||||||
const parsedFiles: Map<string, ParsedFile> = new Map();
|
const parsedFiles: Map<string, ParsedFile> = new Map();
|
||||||
|
|
||||||
|
const readStart = Date.now();
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
if (entry.isFile() && entry.name.endsWith('.lua')) {
|
if (entry.isFile() && entry.name.endsWith('.lua')) {
|
||||||
const fullPath = path.join(entry.parentPath ?? entry.path, entry.name);
|
const fullPath = path.join(entry.parentPath, entry.name);
|
||||||
const relativePath = path.relative(this.options.sourcePath, fullPath);
|
const relativePath = path.relative(this.options.sourcePath, fullPath);
|
||||||
const content = fs.readFileSync(fullPath, 'utf-8');
|
const content = fs.readFileSync(fullPath, 'utf-8');
|
||||||
|
|
||||||
const parsedFile = this.parseFile(relativePath, content, fullPath);
|
const parsedFile = this.parseFile(relativePath, content, fullPath, metricsMeter);
|
||||||
parsedFiles.set(parsedFile.fileKey, parsedFile);
|
parsedFiles.set(parsedFile.fileKey, parsedFile);
|
||||||
|
metricsMeter.filesRead++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const readEnd = Date.now();
|
||||||
|
metricsMeter.readTimeMs = readEnd - readStart;
|
||||||
|
|
||||||
const writer = new Writer(
|
const writer = new Writer(
|
||||||
path.join(this.options.outputPath, this.options.outputFileName!),
|
path.join(this.options.outputPath, this.options.outputFileName!),
|
||||||
parsedFiles,
|
parsedFiles,
|
||||||
|
this.logger,
|
||||||
this.options.onError
|
this.options.onError
|
||||||
);
|
);
|
||||||
|
|
||||||
writer.write();
|
const writeStart = Date.now();
|
||||||
console.log(`Compilation complete. Output written to ${path.join(this.options.outputPath, this.options.outputFileName!)}`);
|
writer.write(includeDevScript, metricsMeter);
|
||||||
|
const writeEnd = Date.now();
|
||||||
|
metricsMeter.writeTimeMs = (writeEnd - writeStart);
|
||||||
|
|
||||||
|
// writer.logDependencyTree();
|
||||||
|
this.logger.info(`Compilation complete. Output written to ${path.join(this.options.outputPath, this.options.outputFileName!)}`);
|
||||||
|
const end = Date.now();
|
||||||
|
|
||||||
|
metricsMeter.totalTimeMs = (end-start);
|
||||||
|
metricsMeter.log(this.logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
private reportError(filePath: string, line: number, message: string): void {
|
private reportError(filePath: string, line: number, charStart: number | undefined, charEnd: number | undefined, message: string): void {
|
||||||
if (this.options.onError) {
|
if (this.options.onError) {
|
||||||
this.options.onError({ filePath, line, message });
|
this.options.onError({ filePath, line, charStart, charEnd, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseFile(filePath: string, content: string, fullPath: string): ParsedFile {
|
private parseFile(filePath: string, content: string, fullPath: string, metricsMeter: Metrics): ParsedFile {
|
||||||
const dependencies: string[] = [];
|
const dependencies: Dependency[] = [];
|
||||||
const newLines: string[] = [`do --${filePath}`];
|
const newLines: string[] = [`do --${filePath}`];
|
||||||
|
|
||||||
content = stripLuaMultilineComments(content);
|
content = stripLuaMultilineComments(content);
|
||||||
@@ -83,6 +129,7 @@ export class ScriptCompiler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
metricsMeter.totalLinesRead++;
|
||||||
let line = lines[i];
|
let line = lines[i];
|
||||||
const trimmedLine = line.trim();
|
const trimmedLine = line.trim();
|
||||||
|
|
||||||
@@ -103,7 +150,7 @@ export class ScriptCompiler {
|
|||||||
}
|
}
|
||||||
newLines.push(line);
|
newLines.push(line);
|
||||||
} else {
|
} else {
|
||||||
this.reportError(fullPath, i, `Code found after module-level return: ${trimmedLine}`);
|
this.reportError(fullPath, i, undefined, undefined, `Code found after module-level return: ${trimmedLine}`);
|
||||||
newLines.push(line); // Continue processing despite error
|
newLines.push(line); // Continue processing despite error
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -112,8 +159,10 @@ export class ScriptCompiler {
|
|||||||
// Handle require statements
|
// Handle require statements
|
||||||
const requireMatch = line.match(/require\(['"](.+?)['"]\)/);
|
const requireMatch = line.match(/require\(['"](.+?)['"]\)/);
|
||||||
if (requireMatch) {
|
if (requireMatch) {
|
||||||
const requiredModule = fileReferenceToLuaVariable(requireMatch[1]);
|
const textMatch = requireMatch[1];
|
||||||
dependencies.push(requiredModule);
|
const requiredModule = fileReferenceToLuaVariable(textMatch);
|
||||||
|
|
||||||
|
dependencies.push(new Dependency(textMatch, i, requireMatch.index ?? 0, (requireMatch.index ?? 0) + requireMatch[0].length));
|
||||||
line = line.replace(requireMatch[0], requiredModule);
|
line = line.replace(requireMatch[0], requiredModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +213,7 @@ export class ScriptCompiler {
|
|||||||
const afterReturn = trimmedLine.substring(afterReturnPos).trim();
|
const afterReturn = trimmedLine.substring(afterReturnPos).trim();
|
||||||
|
|
||||||
if (afterReturn === '') {
|
if (afterReturn === '') {
|
||||||
this.reportError(fullPath, i, 'Empty return statement at module level');
|
this.reportError(fullPath, i, afterReturnPos, afterReturnPos, 'Empty return statement at module level');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +256,7 @@ export class ScriptCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasMultipleValues) {
|
if (hasMultipleValues) {
|
||||||
this.reportError(fullPath, i, `Multiple return values not supported: ${trimmedLine}`);
|
this.reportError(fullPath, i, afterReturnPos, afterReturnPos + afterReturn.length, `Multiple return values not supported: ${trimmedLine}`);
|
||||||
} else {
|
} else {
|
||||||
// Replace return with assignment
|
// Replace return with assignment
|
||||||
const moduleVariable = fileReferenceToLuaVariable(filePath);
|
const moduleVariable = fileReferenceToLuaVariable(filePath);
|
||||||
@@ -241,6 +290,20 @@ function stripLuaMultilineComments(content: string): string {
|
|||||||
return content.replace(/--\[(=*)\[[\s\S]*?\]\1\]/g, '');
|
return content.replace(/--\[(=*)\[[\s\S]*?\]\1\]/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Dependency {
|
||||||
|
public readonly fileKey: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly requiredModule: string,
|
||||||
|
public readonly requiredAtLine: number,
|
||||||
|
public readonly charStart: number,
|
||||||
|
public readonly charEnd: number
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.fileKey = fileReferenceToLuaVariable(requiredModule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ParsedFile {
|
class ParsedFile {
|
||||||
public readonly fileKey: string;
|
public readonly fileKey: string;
|
||||||
|
|
||||||
@@ -248,7 +311,7 @@ class ParsedFile {
|
|||||||
public readonly filePath: string,
|
public readonly filePath: string,
|
||||||
public readonly fullPath: string,
|
public readonly fullPath: string,
|
||||||
public readonly lines: string[],
|
public readonly lines: string[],
|
||||||
public readonly dependencies: string[]
|
public readonly dependencies: Dependency[]
|
||||||
) {
|
) {
|
||||||
this.fileKey = fileReferenceToLuaVariable(filePath);
|
this.fileKey = fileReferenceToLuaVariable(filePath);
|
||||||
}
|
}
|
||||||
@@ -289,37 +352,55 @@ class Writer {
|
|||||||
constructor(
|
constructor(
|
||||||
public location: string,
|
public location: string,
|
||||||
public files: Map<string, ParsedFile>,
|
public files: Map<string, ParsedFile>,
|
||||||
|
private readonly logger: ICompilationLogger,
|
||||||
public onError?: (error: CompilationError) => void
|
public onError?: (error: CompilationError) => void
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private getStartLines(): string[] {
|
private getStartLines(): string[] {
|
||||||
return [`local ${LUA_SCRIPT_GLOBAL_KEYWORD} = {}`];
|
return [
|
||||||
|
`-- Transpiled at (UTC): ${new Date().toISOString()}`,
|
||||||
|
`local ${LUA_SCRIPT_GLOBAL_KEYWORD} = {}`
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
write(): void {
|
logDependencyTree(): void {
|
||||||
const writtenFiles: Set<string> = new Set();
|
const visited: Set<string> = new Set();
|
||||||
const outputLines: string[] = [];
|
const depth : number = 1;
|
||||||
|
this.logger.writeLine('Dependency Tree <root>:');
|
||||||
outputLines.push(...this.getStartLines());
|
const logFileRecursive = (parsedFile: ParsedFile, currentDepth: number) => {
|
||||||
|
if(currentDepth === 0 && visited.has(parsedFile.fileKey)) {
|
||||||
const writeFileRecursive = (fileKey: string) => {
|
|
||||||
if (writtenFiles.has(fileKey)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = this.files.get(fileKey);
|
visited.add(parsedFile.fileKey);
|
||||||
if (!file) {
|
let padding = ' '.repeat(currentDepth * 2);
|
||||||
throw new Error(`File not found in compiler: ${fileKey}`);
|
if (currentDepth > 0) {
|
||||||
|
padding += '└─>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write dependencies first
|
const printable = parsedFile.fileKey.replace(LUA_SCRIPT_GLOBAL_KEYWORD + '.', '');
|
||||||
for (const dep of file.dependencies) {
|
const lastPart = printable.split('.').pop();
|
||||||
writeFileRecursive(dep);
|
this.logger.writeLine(`${padding} ${lastPart} ${' '.repeat(Math.max(0, 64 - padding.length - (lastPart ? lastPart.length : 0)))} ${printable}`);
|
||||||
|
for (const dep of parsedFile.dependencies) {
|
||||||
|
const depFile = this.files.get(dep.fileKey);
|
||||||
|
if (depFile) {
|
||||||
|
logFileRecursive(depFile, currentDepth + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
outputLines.push(...file.lines);
|
for (const parsedFile of this.files.values()) {
|
||||||
writtenFiles.add(fileKey);
|
logFileRecursive(parsedFile, depth);
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
write(includeDevScript: boolean, metrics: Metrics): void {
|
||||||
|
const writtenFiles: Set<string> = new Set();
|
||||||
|
const outputLines: string[] = [];
|
||||||
|
|
||||||
|
const startLines = this.getStartLines();
|
||||||
|
metrics.totalLinesWritten+=startLines.length;
|
||||||
|
outputLines.push(...startLines);
|
||||||
|
|
||||||
//Check for circular dependencies before writing
|
//Check for circular dependencies before writing
|
||||||
const visited: Set<string> = new Set();
|
const visited: Set<string> = new Set();
|
||||||
@@ -347,7 +428,7 @@ class Writer {
|
|||||||
const file = this.files.get(key);
|
const file = this.files.get(key);
|
||||||
if (file) {
|
if (file) {
|
||||||
for (const dep of file.dependencies) {
|
for (const dep of file.dependencies) {
|
||||||
if (checkCircular(dep)) {
|
if (checkCircular(dep.fileKey)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,12 +442,55 @@ class Writer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const writeFileRecursive = (parsedFile: ParsedFile) => {
|
||||||
|
if (writtenFiles.has(parsedFile.fileKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write dependencies first
|
||||||
|
for (const dep of parsedFile.dependencies) {
|
||||||
|
const depFile = this.files.get(dep.fileKey);
|
||||||
|
if (depFile) {
|
||||||
|
writeFileRecursive(depFile);
|
||||||
|
} else {
|
||||||
|
this.onError?.({
|
||||||
|
filePath: parsedFile.fullPath,
|
||||||
|
line: dep.requiredAtLine,
|
||||||
|
charStart: dep.charStart,
|
||||||
|
charEnd: dep.charEnd,
|
||||||
|
message: `Missing dependency: ${dep.fileKey}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metrics.totalLinesWritten += parsedFile.lines.length;
|
||||||
|
outputLines.push(...parsedFile.lines);
|
||||||
|
writtenFiles.add(parsedFile.fileKey);
|
||||||
|
metrics.filesWritten++;
|
||||||
|
};
|
||||||
|
|
||||||
// Write all files in dependency order
|
// Write all files in dependency order
|
||||||
for (const fileKey of this.files.keys()) {
|
for (const parsedFile of this.files.values()) {
|
||||||
writeFileRecursive(fileKey);
|
writeFileRecursive(parsedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.mkdirSync(path.dirname(this.location), { recursive: true });
|
fs.mkdirSync(path.dirname(this.location), { recursive: true });
|
||||||
fs.writeFileSync(this.location, outputLines.join('\n'), 'utf-8');
|
fs.writeFileSync(this.location, outputLines.join('\n'), 'utf-8');
|
||||||
|
|
||||||
|
if(includeDevScript) {
|
||||||
|
const devFileLocation = this.location.replace('.lua', '.dev.lua');
|
||||||
|
this.writeDevScript(devFileLocation, this.location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private writeDevScript(devFileLocation: string, actualFileLocation: string){
|
||||||
|
const devLines = [
|
||||||
|
`-- DEV SCRIPT - NOT FOR PRODUCTION USE`,
|
||||||
|
`-- This script can be referenced in DCS. Compiled script will then be loaded dynamically.`,
|
||||||
|
`-- This way you can test the compiled output without having to re-import the script into the mission every time.`,
|
||||||
|
`-- This file will only have to be re-imported when the name or location of the compiled script file changes.`,
|
||||||
|
`assert(loadfile("${actualFileLocation}"))()`
|
||||||
|
];
|
||||||
|
fs.writeFileSync(devFileLocation, devLines.join('\n'), 'utf-8');
|
||||||
|
this.logger.info(`Development script written to ${devFileLocation}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "Node16",
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2022"],
|
||||||
|
"strict": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"types": ["node"],
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
||||||
@@ -65,6 +65,12 @@ return utils
|
|||||||
|
|
||||||
## Release Notes
|
## Release Notes
|
||||||
|
|
||||||
|
### 0.0.2
|
||||||
|
|
||||||
|
- Improved error handling and reporting in the script compiler.
|
||||||
|
- Added Output channel for compilation logs and errors.
|
||||||
|
- Improved Settings for better user experience.
|
||||||
|
|
||||||
### 0.0.1
|
### 0.0.1
|
||||||
|
|
||||||
Initial.
|
Initial.
|
||||||
|
|||||||
@@ -24,9 +24,49 @@
|
|||||||
"categories": [
|
"categories": [
|
||||||
"Other"
|
"Other"
|
||||||
],
|
],
|
||||||
"activationEvents": [],
|
"activationEvents": [
|
||||||
|
"onLanguage:lua",
|
||||||
|
"workspaceContains:**/*.lua"
|
||||||
|
],
|
||||||
"main": "./dist/extension.js",
|
"main": "./dist/extension.js",
|
||||||
"contributes": {
|
"contributes": {
|
||||||
|
"configuration": {
|
||||||
|
"title": "Dutchies DCS Tools",
|
||||||
|
"type" : "object",
|
||||||
|
"properties": {
|
||||||
|
"dutchies-dcs-scripting-tools.dcsTypes": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Enable or disable DCS Types (adds types for DCS functions and objects)"
|
||||||
|
},
|
||||||
|
"dutchies-dcs-scripting-tools.spearheadTypes": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "(Coming Soon) Enable or disable Spearhead Types (requires DCS Types to be enabled)"
|
||||||
|
},
|
||||||
|
"dutchies-dcs-scripting-tools.compileAt" : {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["onSave", "onCompileCommand"],
|
||||||
|
"default": "onCompileCommand",
|
||||||
|
"description": "When to compile the Lua script"
|
||||||
|
},
|
||||||
|
"dutchies-dcs-scripting-tools.includeDevelopmentScript": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Adds an additional script. This can be referenced from DCS through doScriptFile. This will then execute the compiles script. This way you don't have to reinsert the script on each change, but can just hit 'restart'."
|
||||||
|
},
|
||||||
|
"dutchies-dcs-scripting-tools.luaSrcDirectory" : {
|
||||||
|
"type":"string",
|
||||||
|
"default": "${workspaceFolder}/src",
|
||||||
|
"description": "Where the source files start. default: ${workspaceFolder}/src"
|
||||||
|
},
|
||||||
|
"dutchies-dcs-scripting-tools.luaOutputPath" : {
|
||||||
|
"type":"string",
|
||||||
|
"default": "${workspaceFolder}/dist",
|
||||||
|
"description": "Where the compiled Lua files will be output. default: ${workspaceFolder}/dist"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"grammars": [
|
"grammars": [
|
||||||
{
|
{
|
||||||
"path": "./syntaxes/lua-slocal.json",
|
"path": "./syntaxes/lua-slocal.json",
|
||||||
@@ -35,6 +75,11 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"commands": [
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": "dutchies-dcs-scripting-tools.openSettings",
|
||||||
|
"title": "Open Settings",
|
||||||
|
"category": "Dutchies DCS Tools"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "dutchies-dcs-scripting-tools.enable",
|
"command": "dutchies-dcs-scripting-tools.enable",
|
||||||
"title": "Enable",
|
"title": "Enable",
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// The module 'vscode' contains the VS Code extensibility API
|
// The module 'vscode' contains the VS Code extensibility API
|
||||||
// Import the module and reference it with the alias vscode in your code below
|
// Import the module and reference it with the alias vscode in your code below
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { ScriptCompiler, ScriptCompilerOptions, CompilationError } from 'dcs-script-compiler';
|
import { ScriptCompiler, ScriptCompilerOptions, CompilationError, ICompilationLogger } from 'dcs-script-compiler';
|
||||||
|
import { Logger } from './logger';
|
||||||
|
|
||||||
const luaWorkSpaceSettingKey = "Lua.workspace";
|
const luaWorkSpaceSettingKey = "Lua.workspace";
|
||||||
const librarySettingsKey = "library";
|
const librarySettingsKey = "library";
|
||||||
@@ -9,53 +10,107 @@ const diagnosticCollection = vscode.languages.createDiagnosticCollection('lua-tr
|
|||||||
|
|
||||||
let pluginPath : string | undefined;
|
let pluginPath : string | undefined;
|
||||||
|
|
||||||
|
const logger = new Logger();
|
||||||
|
|
||||||
|
const extensionName = 'dutchies-dcs-scripting-tools';
|
||||||
|
const publisherName = 'dutchie031';
|
||||||
|
const extensionSettingsFilter = `@ext:${publisherName}.${extensionName}`;
|
||||||
|
|
||||||
|
//TODO:
|
||||||
|
// - ENABLE/DISABLE with settings instead of commands (or both)
|
||||||
|
|
||||||
// This method is called when your extension is activated
|
// This method is called when your extension is activated
|
||||||
// Your extension is activated the very first time the command is executed
|
// Your extension is activated the very first time the command is executed
|
||||||
export function activate(context: vscode.ExtensionContext) {
|
export function activate(context: vscode.ExtensionContext) {
|
||||||
|
|
||||||
pluginPath = context.asAbsolutePath('lua-addons');
|
pluginPath = context.asAbsolutePath('lua-addons');
|
||||||
|
|
||||||
vscode.commands.registerCommand('dutchies-dcs-scripting-tools.enable', () => {
|
context.subscriptions.push(
|
||||||
addPluginPathToSettings();
|
vscode.commands.registerCommand('dutchies-dcs-scripting-tools.enable', async () => {
|
||||||
vscode.commands.executeCommand(
|
enableIntellisense();
|
||||||
"lua.startServer"
|
})
|
||||||
);
|
);
|
||||||
vscode.window.showInformationMessage('Dutchies DCS Scripting Tools enabled.');
|
|
||||||
});
|
|
||||||
|
|
||||||
vscode.commands.registerCommand('dutchies-dcs-scripting-tools.disable', () => {
|
context.subscriptions.push(
|
||||||
removePluginPathFromSettings();
|
vscode.commands.registerCommand('dutchies-dcs-scripting-tools.disable', async () => {
|
||||||
vscode.commands.executeCommand(
|
disableIntellisense();
|
||||||
"lua.startServer"
|
})
|
||||||
);
|
);
|
||||||
vscode.window.showInformationMessage('Dutchies DCS Scripting Tools disabled.');
|
|
||||||
});
|
|
||||||
|
|
||||||
vscode.commands.registerCommand('dutchies-dcs-scripting-tools.compileLuaScripts', async () => {
|
context.subscriptions.push(
|
||||||
try{
|
vscode.commands.registerCommand('dutchies-dcs-scripting-tools.openSettings', async () => {
|
||||||
const start = Date.now();
|
await vscode.commands.executeCommand("workbench.action.openSettings", `@ext:${extensionSettingsFilter}`);
|
||||||
await compileLuaScripts();
|
}));
|
||||||
const end = Date.now();
|
|
||||||
console.log(`Lua scripts compiled in ${(end - start) / 1000} seconds.`);
|
context.subscriptions.push(
|
||||||
vscode.window.showInformationMessage(`Lua scripts compiled successfully in ${(end - start) / 1000} seconds.`);
|
vscode.commands.registerCommand('dutchies-dcs-scripting-tools.compileLuaScripts', async () => {
|
||||||
}catch(err){
|
try{
|
||||||
vscode.window.showErrorMessage('Error compiling Lua scripts: ' + (err as Error).message);
|
const start = Date.now();
|
||||||
|
await compileLuaScripts();
|
||||||
|
const end = Date.now();
|
||||||
|
vscode.window.showInformationMessage(`Lua scripts compiled successfully in ${(end - start) / 1000} seconds.`);
|
||||||
|
}catch(err){
|
||||||
|
vscode.window.showErrorMessage('Error compiling Lua scripts: ' + (err as Error).message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
vscode.workspace.onDidSaveTextDocument(async(document) => {
|
||||||
|
logger.info(`Document saved: ${document.uri.fsPath} | Language: ${document.languageId}`);
|
||||||
|
if (document.languageId === 'lua') {
|
||||||
|
const config = vscode.workspace.getConfiguration(extensionName);
|
||||||
|
const compileAt = config.get<string>('compileAt') || "undefined";
|
||||||
|
if (compileAt === 'onSave') {
|
||||||
|
await compileLuaScripts();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
vscode.workspace.onDidChangeConfiguration(async(event) => {
|
||||||
|
if (event.affectsConfiguration(`${extensionName}.dcsTypes`)) {
|
||||||
|
const config = vscode.workspace.getConfiguration(extensionName);
|
||||||
|
const dcsTypesEnabled = config.get<boolean>('dcsTypes') || false;
|
||||||
|
if (dcsTypesEnabled) {
|
||||||
|
addPluginPathToSettings();
|
||||||
|
} else {
|
||||||
|
removePluginPathFromSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info('Dutchies DCS Scripting Tools extension activated');
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is called when your extension is deactivated
|
// This method is called when your extension is deactivated
|
||||||
export function deactivate()
|
export function deactivate()
|
||||||
{
|
{
|
||||||
removePluginPathFromSettings();
|
removePluginPathFromSettings();
|
||||||
|
logger.info('Dutchies DCS Scripting Tools extension deactivated');
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompilationLogger implements ICompilationLogger {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly logger: Logger) {
|
||||||
|
}
|
||||||
|
info(message: string): void {
|
||||||
|
this.logger.info(message);
|
||||||
|
}
|
||||||
|
error(message: string): void {
|
||||||
|
this.logger.error(message);
|
||||||
|
}
|
||||||
|
writeLine(message: string): void {
|
||||||
|
this.logger.log(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function compileLuaScripts() {
|
async function compileLuaScripts() {
|
||||||
|
logger.clear();
|
||||||
|
logger.info("Compiling...");
|
||||||
|
|
||||||
const config = vscode.workspace.getConfiguration('dcsScriptingTools');
|
const config = vscode.workspace.getConfiguration(extensionName);
|
||||||
const sourcePath = config.get<string>('luaSourcePath') || '${workspaceFolder}/src';
|
const sourcePath = config.get<string>('luaSrcDirectory') || '${workspaceFolder}/src';
|
||||||
const outputPath = config.get<string>('luaOutputPath') || '${workspaceFolder}/dist';
|
const outputPath = config.get<string>('luaOutputPath') || '${workspaceFolder}/dist';
|
||||||
const minify = config.get<boolean>('minifyLuaScripts') || false;
|
|
||||||
|
|
||||||
const resolvedSourcePath = sourcePath.replace('${workspaceFolder}', vscode.workspace.workspaceFolders?.[0].uri.fsPath || '');
|
const resolvedSourcePath = sourcePath.replace('${workspaceFolder}', vscode.workspace.workspaceFolders?.[0].uri.fsPath || '');
|
||||||
const resolvedOutputPath = outputPath.replace('${workspaceFolder}', vscode.workspace.workspaceFolders?.[0].uri.fsPath || '');
|
const resolvedOutputPath = outputPath.replace('${workspaceFolder}', vscode.workspace.workspaceFolders?.[0].uri.fsPath || '');
|
||||||
@@ -66,7 +121,7 @@ async function compileLuaScripts() {
|
|||||||
const options: ScriptCompilerOptions = {
|
const options: ScriptCompilerOptions = {
|
||||||
sourcePath: resolvedSourcePath,
|
sourcePath: resolvedSourcePath,
|
||||||
outputPath: resolvedOutputPath,
|
outputPath: resolvedOutputPath,
|
||||||
minify: minify,
|
minify: false,
|
||||||
onError: (error: CompilationError) => {
|
onError: (error: CompilationError) => {
|
||||||
const errors = errorsByFile.get(error.filePath) || [];
|
const errors = errorsByFile.get(error.filePath) || [];
|
||||||
errors.push(error);
|
errors.push(error);
|
||||||
@@ -74,9 +129,13 @@ async function compileLuaScripts() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const compiler = new ScriptCompiler(options);
|
const compilationLogger = new CompilationLogger(logger);
|
||||||
|
const compiler = new ScriptCompiler(options, compilationLogger);
|
||||||
|
|
||||||
|
const includeDevScript = config.get<boolean>('includeDevelopmentScript') || false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await compiler.compile();
|
await compiler.compile(includeDevScript);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
vscode.window.showErrorMessage('Compilation failed: ' + (err as Error).message);
|
vscode.window.showErrorMessage('Compilation failed: ' + (err as Error).message);
|
||||||
}
|
}
|
||||||
@@ -86,7 +145,7 @@ async function compileLuaScripts() {
|
|||||||
for (const [filePath, errors] of errorsByFile) {
|
for (const [filePath, errors] of errorsByFile) {
|
||||||
const uri = vscode.Uri.file(filePath);
|
const uri = vscode.Uri.file(filePath);
|
||||||
const diagnostics = errors.map(error => {
|
const diagnostics = errors.map(error => {
|
||||||
const range = new vscode.Range(error.line, 0, error.line, 0);
|
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);
|
const diagnostic = new vscode.Diagnostic(range, error.message, vscode.DiagnosticSeverity.Error);
|
||||||
diagnostic.source = 'DCS Lua Transpiler';
|
diagnostic.source = 'DCS Lua Transpiler';
|
||||||
return diagnostic;
|
return diagnostic;
|
||||||
@@ -95,6 +154,33 @@ async function compileLuaScripts() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function enableIntellisense() {
|
||||||
|
|
||||||
|
const config = vscode.workspace.getConfiguration(extensionName);
|
||||||
|
if (config.get<boolean>("dcsTypes") === false) {
|
||||||
|
config.update("dcsTypes", true, vscode.ConfigurationTarget.Workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
addPluginPathToSettings();
|
||||||
|
await vscode.commands.executeCommand(
|
||||||
|
"lua.startServer"
|
||||||
|
);
|
||||||
|
vscode.window.showInformationMessage('Dutchies DCS Scripting Tools enabled.');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function disableIntellisense() {
|
||||||
|
const config = vscode.workspace.getConfiguration(extensionName);
|
||||||
|
if (config.get<boolean>("dcsTypes") === true) {
|
||||||
|
config.update("dcsTypes", false, vscode.ConfigurationTarget.Workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
removePluginPathFromSettings();
|
||||||
|
await vscode.commands.executeCommand(
|
||||||
|
"lua.startServer"
|
||||||
|
);
|
||||||
|
vscode.window.showInformationMessage('Dutchies DCS Scripting Tools disabled.');
|
||||||
|
}
|
||||||
|
|
||||||
function addPluginPathToSettings() {
|
function addPluginPathToSettings() {
|
||||||
if (pluginPath) {
|
if (pluginPath) {
|
||||||
const luaSettings = vscode.workspace.getConfiguration(luaWorkSpaceSettingKey);
|
const luaSettings = vscode.workspace.getConfiguration(luaWorkSpaceSettingKey);
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
|
export class Logger {
|
||||||
|
|
||||||
|
private outputChannel: vscode.OutputChannel;
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
this.outputChannel = vscode.window.createOutputChannel('DCS Scripting Tools');
|
||||||
|
}
|
||||||
|
|
||||||
|
log(message: string) {
|
||||||
|
this.outputChannel.appendLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
info(message: string) {
|
||||||
|
this.outputChannel.appendLine(`[${new Date().toISOString()}][INFO] ${message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(message: string) {
|
||||||
|
this.outputChannel.appendLine(`[${new Date().toISOString()}][ERROR] ${message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.outputChannel.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.outputChannel.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,10 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"dcs-script-compiler": ["../compiler/src/ScriptCompiler.ts"]
|
"dcs-script-compiler": ["../compiler/src/ScriptCompiler.ts"]
|
||||||
},
|
},
|
||||||
|
"types": [
|
||||||
|
"node",
|
||||||
|
"mocha"
|
||||||
|
],
|
||||||
/* Additional Checks */
|
/* Additional Checks */
|
||||||
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
|||||||
Generated
+26
-5
@@ -8,7 +8,11 @@
|
|||||||
"workspaces": [
|
"workspaces": [
|
||||||
"compiler",
|
"compiler",
|
||||||
"dutchies-dcs-scripting-tools"
|
"dutchies-dcs-scripting-tools"
|
||||||
]
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/mocha": "^10.0.10",
|
||||||
|
"@types/node": "^25.5.2"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"compiler": {
|
"compiler": {
|
||||||
"name": "dcs-script-compiler",
|
"name": "dcs-script-compiler",
|
||||||
@@ -38,6 +42,16 @@
|
|||||||
"vscode": "^1.108.1"
|
"vscode": "^1.108.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dutchies-dcs-scripting-tools/node_modules/@types/node": {
|
||||||
|
"version": "22.19.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
|
||||||
|
"integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@bcoe/v8-coverage": {
|
"node_modules/@bcoe/v8-coverage": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz",
|
||||||
@@ -830,15 +844,22 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.19.7",
|
"version": "25.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz",
|
||||||
"integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==",
|
"integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~7.18.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node/node_modules/undici-types": {
|
||||||
|
"version": "7.18.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||||
|
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/vscode": {
|
"node_modules/@types/vscode": {
|
||||||
"version": "1.108.1",
|
"version": "1.108.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.108.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.108.1.tgz",
|
||||||
|
|||||||
+13
-1
@@ -4,5 +4,17 @@
|
|||||||
"workspaces": [
|
"workspaces": [
|
||||||
"compiler",
|
"compiler",
|
||||||
"dutchies-dcs-scripting-tools"
|
"dutchies-dcs-scripting-tools"
|
||||||
]
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/vscode": "^1.108.1",
|
||||||
|
"@types/mocha": "^10.0.10",
|
||||||
|
"@types/node": "22.x",
|
||||||
|
"typescript-eslint": "^8.52.0",
|
||||||
|
"eslint": "^9.39.2",
|
||||||
|
"esbuild": "^0.27.2",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
|
"@vscode/test-cli": "^0.0.12",
|
||||||
|
"@vscode/test-electron": "^2.5.2"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user