Skip to content

Migrate nwnsc to nwn_script_comp #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,7 @@ I think we can consider the extension stable and out of beta. A big thank you to
- New provider: [Document Symbols](https://code.visualstudio.com/api/language-extensions/programmatic-language-features#show-all-symbol-definitions-within-a-document).
- Fixed a small issue with the `CompletionItemsProvider`.
- Goto will now _really_ work with definitions from `nwscript.nss` if the file is in your project.

## [2.2.0]

- The compilation engine has been migrated from nwnsc to nwn_script_comp.
7 changes: 1 addition & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,11 @@ Notes:

Notes:

- Diagnostics are provided by compiling the file with the [nwnsc](https://github.com/nwneetools/nwnsc) executable.
- Diagnostics are provided by compiling the file with the [nwn_script_comp](https://github.com/niv/neverwinter.nim/blob/master/nwn_script_comp.nim) compiled executable.
- The compiler executable is provided for Windows, Darwin and Linux operating systems.
- Diagnostics are currently published when opening or saving a file.
- By default, the compiler will try to detect automatically your Neverwinter Nights home and installation folders if they are not specified. If it fails to do so, you can provide the paths in the extension settings like shown above - input paths are wrapped into quotes automatically.
- You can set the `verbose` setting to `true` if you wish to see detailed logs of the compilation process.
- Big files with a lot of includes can take between half a second to a second to compile on older machines - it will not affect the client performances as the processing is done on the server.

### Syntax highligthing

Expand All @@ -112,10 +111,6 @@ The language symbols or tokens are not generated using an AST like language serv

Implementing a language parser to build its AST is a lot of work, and none was available at the time I implemented this project. Now that NWScript compiler has been made [public](https://github.com/niv/neverwinter.nim), it would be much easier to create a utility responsible for parsing a file of code and generating its AST. Implementing this utility and refactoring the whole tokenization engine of the Language Server is, however, a non-negligible amount of work. Considering the fact that the current solution works well for common use, I do not intend to do it.

## Known issues

The nwnsc process doesn't terminate on linux. This is caused by the [compiler](https://github.com/nwneetools/nwnsc) itself, not the extension.

## Issues

Please report any issues on the github [repository](https://github.com/PhilippeChab/nwscript-ee-language-server/issues).
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"url": "https://github.com/PhilippeChab/nwscript-ee-language-server"
},
"license": "MIT",
"version": "2.1.0",
"version": "2.2.0",
"author": {
"name": "Philippe Chabot"
},
Expand Down
Binary file added server/resources/compiler/linux/nwn_script_comp
Binary file not shown.
Binary file removed server/resources/compiler/linux/nwnsc
Binary file not shown.
Binary file added server/resources/compiler/mac/nwn_script_comp
Binary file not shown.
Binary file removed server/resources/compiler/mac/nwnsc
Binary file not shown.
3,228 changes: 3,228 additions & 0 deletions server/resources/compiler/windows/cacert.pem

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file added server/resources/compiler/windows/libcurl.dll
Binary file not shown.
Binary file not shown.
Binary file added server/resources/compiler/windows/libssl-1_1.dll
Binary file not shown.
Binary file not shown.
Binary file removed server/resources/compiler/windows/nwnsc.exe
Binary file not shown.
Binary file added server/resources/compiler/windows/pcre.dll
Binary file not shown.
Binary file added server/resources/compiler/windows/pcre32.dll
Binary file not shown.
Binary file added server/resources/compiler/windows/pcre64.dll
Binary file not shown.
Binary file added server/resources/compiler/windows/pdcurses.dll
Binary file not shown.
Binary file added server/resources/compiler/windows/pdcurses32.dll
Binary file not shown.
Binary file added server/resources/compiler/windows/pdcurses64.dll
Binary file not shown.
Binary file added server/resources/compiler/windows/sqlite3_32.dll
Binary file not shown.
Binary file added server/resources/compiler/windows/sqlite3_64.dll
Binary file not shown.
65 changes: 26 additions & 39 deletions server/src/Providers/DiagnosticsProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { ServerManager } from "../ServerManager";
import Provider from "./Provider";

const lineNumber = /\(([^)]+)\)/;
const lineMessage = /(Error|Warning):(.*)/;
const lineFilename = /^[^(]+/;
const lineMessage = /(ERROR|WARNING):(.*)/;
const lineFilename = /(:\s)([^(]+)/;

enum OS {
linux = "Linux",
Expand All @@ -25,7 +25,7 @@ export default class DiagnoticsProvider extends Provider {

private generateDiagnostics(uris: string[], files: FilesDiagnostics, severity: DiagnosticSeverity) {
return (line: string) => {
const uri = uris.find((uri) => basename(fileURLToPath(uri)) === lineFilename.exec(line)![0]);
const uri = uris.find((uri) => basename(fileURLToPath(uri)) === lineFilename.exec(line)![2]);

if (uri) {
const linePosition = Number(lineNumber.exec(line)![1]) - 1;
Expand All @@ -52,11 +52,11 @@ export default class DiagnoticsProvider extends Provider {

switch (specifiedOs) {
case OS.linux:
return "../resources/compiler/linux/nwnsc";
return "../resources/compiler/linux/nwn_script_comp";
case OS.mac:
return "../resources/compiler/mac/nwnsc";
return "../resources/compiler/mac/nwn_script_comp";
case OS.windows:
return "../resources/compiler/windows/nwnsc.exe";
return "../resources/compiler/windows/nwn_script_comp.exe";
default:
return "";
}
Expand All @@ -79,7 +79,7 @@ export default class DiagnoticsProvider extends Provider {

if (!this.server.configLoaded || !document) {
if (!this.server.documentsWaitingForPublish.includes(uri)) {
this.server.documentsWaitingForPublish?.push(uri);
this.server.documentsWaitingForPublish.push(uri);
}
return resolve(true);
}
Expand All @@ -88,7 +88,7 @@ export default class DiagnoticsProvider extends Provider {
const files: FilesDiagnostics = { [document.uri]: [] };
const uris: string[] = [];
children.forEach((child) => {
const fileUri = this.server.documentsCollection?.get(child)?.uri;
const fileUri = this.server.documentsCollection.get(child)?.uri;
if (fileUri) {
files[fileUri] = [];
uris.push(fileUri);
Expand All @@ -100,29 +100,25 @@ export default class DiagnoticsProvider extends Provider {
}
// The compiler command:
// - y; continue on error
// - c; compile includes
// - l; try to load resources if paths are not supplied
// - r; don't generate the compiled file
// - h; game home path
// - n; game installation path
// - i; includes directories
const args = ["-y", "-c", "-l", "-r", "SKIP_OUTPUT"];
// - s; dry run
const args = ["-y", "-s"];
if (Boolean(nwnHome)) {
args.push("-h");
args.push("--userdirectory");
args.push(`"${nwnHome}"`);
} else if (verbose) {
this.server.logger.info("Trying to resolve Neverwinter Nights home directory automatically.");
}
if (Boolean(nwnInstallation)) {
args.push("-n");
args.push("--root");
args.push(`"${nwnInstallation}"`);
} else if (verbose) {
this.server.logger.info("Trying to resolve Neverwinter Nights installation directory automatically.");
}
if (children.length > 0) {
args.push("-i");
args.push(`"${[...new Set(uris.map((uri) => dirname(fileURLToPath(uri))))].join(";")}"`);
args.push("--dirs");
args.push(`"${[...new Set(uris.map((uri) => dirname(fileURLToPath(uri))))].join(",")}"`);
}
args.push("-c");
args.push(`"${fileURLToPath(uri)}"`);

let stdout = "";
Expand All @@ -144,46 +140,37 @@ export default class DiagnoticsProvider extends Provider {
});

child.on("close", (_) => {
const lines = stdout
const lines = stderr
.toString()
.split("\n")
.filter((line) => line !== "\r" && line !== "\n" && Boolean(line));
const errors: string[] = [];
const warnings: string[] = [];

lines.forEach((line) => {
if (verbose && !line.includes("Compiling:")) {
if (verbose) {
this.server.logger.info(line);
}

// Diagnostics
if (line.includes("Error:")) {
if (line.includes("ERROR:")) {
errors.push(line);
}
if (reportWarnings && line.includes("Warning:")) {

if (reportWarnings && line.includes("WARNING:")) {
warnings.push(line);
}

// Actual errors
if (line.includes("NOTFOUND")) {
return this.server.logger.error(
"Unable to resolve nwscript.nss. Are your Neverwinter Nights home and/or installation directories valid?",
);
}
if (line.includes("Failed to open .key archive")) {
return this.server.logger.error(
"Unable to open nwn_base.key Is your Neverwinter Nights installation directory valid?",
);
if (line.includes("unhandled exception")) {
this.server.logger.error(line);
}
if (line.includes("Unable to read input file")) {

if (line.includes("Could not locate")) {
if (Boolean(nwnHome) || Boolean(nwnInstallation)) {
return this.server.logger.error(
"Unable to resolve provided Neverwinter Nights home and/or installation directories. Ensure the paths are valid in the extension settings.",
);
return this.server.logger.error("Unable to resolve provided Neverwinter Nights home and/or installation directories. Ensure the paths are valid in the extension settings.");
} else {
return this.server.logger.error(
"Unable to automatically resolve Neverwinter Nights home and/or installation directories.",
);
return this.server.logger.error("Unable to automatically resolve Neverwinter Nights home and/or installation directories.");
}
}
});
Expand Down