Skip to content

Instantly share code, notes, and snippets.

@Levertion
Created November 4, 2017 12:54
Show Gist options
  • Save Levertion/8180150d01eea7ba505fe896c0cb706b to your computer and use it in GitHub Desktop.
Save Levertion/8180150d01eea7ba505fe896c0cb706b to your computer and use it in GitHub Desktop.
An attempted refactor of https://github.com/Levertion/vscode-mcfunction, until I realised it was going to end up being the exact same code
/* --------------------------------------------------------------------------------------------
* Licensed under the MIT License. See LICENSE and ThirdPartyNotices.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
'use strict';
import {
IPCMessageReader, IPCMessageWriter, createConnection, IConnection, InitializeResult,
TextDocuments, Diagnostic, TextDocument
} from 'vscode-languageserver';
import { readFileSync, lstatSync } from 'fs';
import * as path from 'path';
// Create a connection for the server. The connection uses Node's IPC as a transport
let connection: IConnection = createConnection(new IPCMessageReader(process), new IPCMessageWriter(process));
var documents = new TextDocuments();
documents.listen(connection)
interface singleLineRange {
start: number
end: number
}
interface functionInformation {
commands: FunctionLine[]
}
interface interpretedCommand {
parts: part[]
equivalent?: part
}
var availableCommands: interpretedCommand[] = [];
var computedDocuments: { [uri: string]: functionInformation } = {};
interface FunctionLine {
diagnostics: Diagnostic[]
text: string
command: commandSection[]
}
interface commandSection {
part: part
subSections: commandSection[]
range: singleLineRange
valid: boolean
}
type partType = "literal" | "entities" | "entity" | "players" | "string" | "id" | "x y z" | "x y" | "x z" | "nbt" | "item" | "int" | "bool" | "block" | "x y" | "float" | "json" | "player";
interface part {
type: partType
value: string //The name or, in the case of "literal", the literal value. This name is only for hover support if provided
additionalInfo?: {
noFollowSpace?: boolean //Whether a space after this argument is to not be assumed.
final?: boolean
}
}
interface functionDiagnostic {
}
interface subValidatorReturn {
subSections: commandSection[]
diagnostics: functionDiagnostic[]
}
let workspaceRoot: string;
connection.onInitialize((params): InitializeResult => {
workspaceRoot = params.rootUri; //Replaced as rootPath is deprecated
return {
capabilities: {
// Tell the client that the server works in incremental text document sync mode
textDocumentSync: documents.syncKind,
}
}
});
// hold the maxNumberOfProblems setting
let maxNumberOfProblems: number;
//Hold the available commands (in the format as at https://gist.github.com/Dinnerbone/943fbcd763c19be188ed6b72a12d7e65/a7ecc4cfb1d12b66aeb6d4e7f643bec227f0d4f7)
let commands: string[];
connection.onDidChangeConfiguration((change) => {
function pathValid(uri: string): boolean {
try {
return !lstatSync(uri).isDirectory();
} catch (error) {
return false;
}
}
interface mcfunctionSettings {
maxNumberOfProblems?: number,
commandsFilePath?: string
}
interface Settings {
mcfunction: mcfunctionSettings;
}
let settings = <Settings>change.settings;
maxNumberOfProblems = settings.mcfunction.maxNumberOfProblems || 100;
let fallbackURI = path.join(__dirname, "..", "commands", "minecraft_commands.txt");
let commandsURI: string;
if (settings.mcfunction.commandsFilePath) { //If the setting is set
let normalizedPath = path.normalize(settings.mcfunction.commandsFilePath);
if (pathValid(normalizedPath)) { //If it is a resolving filepath
commandsURI = normalizedPath; //URI is the value of the setting
} else {
if (workspaceRoot !== null) { //If a folder is open
let joinedPath = path.join(workspaceRoot, settings.mcfunction.commandsFilePath);
if (pathValid(joinedPath)) { //If it is a relative URI from the workspaceRoot and not a directory
commandsURI = joinedPath; //Set the URI to this value
} else {
commandsURI = fallbackURI; //In all other cases, use the fallback URI
}
} else {
commandsURI = fallbackURI;
}
}
} else {
commandsURI = fallbackURI;
}
try {
commands = readFileSync(commandsURI).toString().split(/\r?\n/g);
}
catch (e) {
throw "No command set could be found. This is most likely to be caused by your extension folder not containing a command set. If you see this error, please report it at https://github.com/Levertion/vscode-mcfunction/issues. This could also have occured if you moved your command set during the ";
}
for (var s = 0; s < commands.length; s++) {
availableCommands[s] = interpret(commands[s]);
}
// Revalidate any open text documents
documents.all().forEach(calculateFunction);
});
function isValidPartType(type: string): partType {
if (["literal", "entities", "entity", "players", "string", "id", "x y z", "x y", "x z", "nbt", "item", "int", "bool", "block", "x y", "float", "json", "player"].includes(type)) {
return <partType>type;
}
else {
throw `|${type}| is not available as a type`;
}
};
function interpret(command: string): interpretedCommand {
let intParts: part[] = [];
let equiv: RegExpExecArray = /-> (.+)/.exec(command);
if (equiv) { //Work out the equivalent sections
command = command.substring(0, command.length - equiv[0].length);
var equivalent: part = { type: "literal", value: equiv[1] };
}
while (command.length > 0) {
let lenChange: number;
let sections: RegExpExecArray, section: string;
switch (/[ <]|$/.exec(command)[0]) {
case "<": //Argument calculation
sections = /(.+?)>(?: |(.)|$)/.exec(command);
section = sections[1];
lenChange = sections[0].length;
let split = section.split(/: ?/);
try {
let type: partType = isValidPartType(split[1]);
intParts.push({ value: split[0].substring(1), type: type, additionalInfo: { noFollowSpace: sections.length == 3 } });
} catch (e) {
connection.console.error(e);
}
break;
default: //Anything else (space or command end)
sections = /(.+?)(?: |$)/.exec(command);
intParts.push({ type: "literal", value: (sections[1]) });
lenChange = sections[0].length;
break;
}
command = command.substring(lenChange);
}
return { equivalent, parts: intParts };
}
function validateCommand(command: string): subValidatorReturn { return {} }
function calculateFunction(document: TextDocument): void {
let lines = document.getText().split(/\r?\n/);
for (var i = 0; i < lines.length; i++) {
let line = lines[i];
let lineReturn = validateCommand(line);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment