From bdea15d213361bed8c4030e36257bb7d8d9b7450 Mon Sep 17 00:00:00 2001
From: klymp <14829626+kylmp@users.noreply.github.com>
Date: Sun, 8 Feb 2026 22:12:26 +0000
Subject: [PATCH] feat: use runescript-lsp
---
.gitignore | 1 +
.vscode/launch.json | 38 ++++
client/cache/activeCursorCache.js | 24 ---
client/cache/activeFileCache.js | 120 -----------
client/cache/cacheManager.js | 197 ------------------
client/cache/class/LineReferenceCache.js | 57 -----
client/cache/class/Trie.js | 89 --------
client/cache/completionCache.js | 55 -----
client/cache/identifierCache.js | 136 ------------
client/cache/returnBlockLinesCache.js | 11 -
client/cache/switchStmtLinesCache.js | 10 -
client/enum/hoverConfigOptions.js | 9 -
client/enum/hoverDisplayItems.js | 14 --
client/enum/regex.js | 22 --
client/info/configKeyInfo.js | 47 -----
client/info/triggerInfo.js | 18 --
client/matching/matchType.js | 196 -----------------
client/matching/matchWord.js | 131 ------------
client/matching/matchers/commandMatcher.js | 22 --
client/matching/matchers/configMatcher.js | 147 -------------
client/matching/matchers/localVarMatcher.js | 23 --
client/matching/matchers/packMatcher.js | 25 ---
client/matching/matchers/parametersMatcher.js | 155 --------------
client/matching/matchers/prevCharMatcher.js | 17 --
client/matching/matchers/regexWordMatcher.js | 21 --
client/matching/matchers/switchCaseMatcher.js | 17 --
client/matching/matchers/triggerMatcher.js | 27 ---
client/provider/color24Provider.js | 41 ----
client/provider/completionProvider.js | 132 ------------
client/provider/configHelpProvider.js | 56 -----
client/provider/gotoDefinition.js | 39 ----
client/provider/hoverProvider.js | 80 -------
client/provider/recolorProvider.js | 41 ----
client/provider/referenceProvider.js | 46 ----
client/provider/renameProvider.js | 111 ----------
client/provider/signatureHelpProvider.js | 134 ------------
client/provider/vscodeCommands.js | 17 --
client/resource/configKeys.js | 44 ----
client/resource/dataTypeToMatchId.js | 15 --
client/resource/hoverConfigResolver.js | 22 --
client/resource/identifierFactory.js | 135 ------------
client/resource/postProcessors.js | 80 -------
client/resource/triggers.js | 61 ------
client/runescript-language.js | 61 ------
client/utils/cacheUtils.js | 29 ---
client/utils/markdownUtils.js | 66 ------
client/utils/matchUtils.js | 54 -----
client/utils/stringUtils.js | 49 -----
package-lock.json | 108 +++++++++-
package.json | 31 ++-
src/clientState.js | 11 +
src/commands.js | 21 ++
src/devModeHighlights.js | 39 ++++
src/events.js | 28 +++
src/runescript-extension.js | 75 +++++++
55 files changed, 347 insertions(+), 2908 deletions(-)
create mode 100644 .vscode/launch.json
delete mode 100644 client/cache/activeCursorCache.js
delete mode 100644 client/cache/activeFileCache.js
delete mode 100644 client/cache/cacheManager.js
delete mode 100644 client/cache/class/LineReferenceCache.js
delete mode 100644 client/cache/class/Trie.js
delete mode 100644 client/cache/completionCache.js
delete mode 100644 client/cache/identifierCache.js
delete mode 100644 client/cache/returnBlockLinesCache.js
delete mode 100644 client/cache/switchStmtLinesCache.js
delete mode 100644 client/enum/hoverConfigOptions.js
delete mode 100644 client/enum/hoverDisplayItems.js
delete mode 100644 client/enum/regex.js
delete mode 100644 client/info/configKeyInfo.js
delete mode 100644 client/info/triggerInfo.js
delete mode 100644 client/matching/matchType.js
delete mode 100644 client/matching/matchWord.js
delete mode 100644 client/matching/matchers/commandMatcher.js
delete mode 100644 client/matching/matchers/configMatcher.js
delete mode 100644 client/matching/matchers/localVarMatcher.js
delete mode 100644 client/matching/matchers/packMatcher.js
delete mode 100644 client/matching/matchers/parametersMatcher.js
delete mode 100644 client/matching/matchers/prevCharMatcher.js
delete mode 100644 client/matching/matchers/regexWordMatcher.js
delete mode 100644 client/matching/matchers/switchCaseMatcher.js
delete mode 100644 client/matching/matchers/triggerMatcher.js
delete mode 100644 client/provider/color24Provider.js
delete mode 100644 client/provider/completionProvider.js
delete mode 100644 client/provider/configHelpProvider.js
delete mode 100644 client/provider/gotoDefinition.js
delete mode 100644 client/provider/hoverProvider.js
delete mode 100644 client/provider/recolorProvider.js
delete mode 100644 client/provider/referenceProvider.js
delete mode 100644 client/provider/renameProvider.js
delete mode 100644 client/provider/signatureHelpProvider.js
delete mode 100644 client/provider/vscodeCommands.js
delete mode 100644 client/resource/configKeys.js
delete mode 100644 client/resource/dataTypeToMatchId.js
delete mode 100644 client/resource/hoverConfigResolver.js
delete mode 100644 client/resource/identifierFactory.js
delete mode 100644 client/resource/postProcessors.js
delete mode 100644 client/resource/triggers.js
delete mode 100644 client/runescript-language.js
delete mode 100644 client/utils/cacheUtils.js
delete mode 100644 client/utils/markdownUtils.js
delete mode 100644 client/utils/matchUtils.js
delete mode 100644 client/utils/stringUtils.js
create mode 100644 src/clientState.js
create mode 100644 src/commands.js
create mode 100644 src/devModeHighlights.js
create mode 100644 src/events.js
create mode 100644 src/runescript-extension.js
diff --git a/.gitignore b/.gitignore
index 49fa97c..39f601f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
*.vsix
*.DS_store
+node_modules/
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..28eb403
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,38 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Run RuneScriptLanguage Extension",
+ "type": "extensionHost",
+ "request": "launch",
+ "runtimeExecutable": "${execPath}",
+ "args": ["--extensionDevelopmentPath=${workspaceFolder}"],
+ "outFiles": [],
+ "sourceMaps": false,
+ "resolveSourceMapLocations": [
+ "${workspaceFolder}/**",
+ "!**/node_modules/**"
+ ]
+ },
+ {
+ "name": "Attach to RuneScript LSP",
+ "type": "node",
+ "request": "attach",
+ "port": 6009,
+ "restart": true,
+ "sourceMaps": true,
+ "outFiles": [
+ "${workspaceFolder}/node_modules/runescript-lsp/dist/**/*.js"
+ ],
+ "sourceMapPathOverrides": {
+ "../../src/*": "${workspaceFolder}/../runescript-lsp/src/*",
+ "../src/*": "${workspaceFolder}/../runescript-lsp/src/*"
+ },
+ "resolveSourceMapLocations": [
+ "${workspaceFolder}/node_modules/runescript-lsp/dist/**/*.js",
+ "${workspaceFolder}/node_modules/runescript-lsp/src/**/*.ts",
+ "!**/node_modules/**"
+ ]
+ }
+ ]
+}
diff --git a/client/cache/activeCursorCache.js b/client/cache/activeCursorCache.js
deleted file mode 100644
index aa0b697..0000000
--- a/client/cache/activeCursorCache.js
+++ /dev/null
@@ -1,24 +0,0 @@
-let activeCursorMatchTypeId;
-let line;
-let index;
-let path;
-
-function get(document, position) {
- if (document.uri.fsPath === path && position.line === line && getIndex(document, position) === index) {
- return activeCursorMatchTypeId;
- }
- return null;
-}
-
-function set(value, document, position) {
- path = document.uri.fsPath;
- index = getIndex(document, position);
- line = position.line;
- activeCursorMatchTypeId = value;
-}
-
-function getIndex(document, position) {
- return document.lineAt(position.line).text.substring(0, position.character).split(',').length;
-}
-
-module.exports = { get, set };
diff --git a/client/cache/activeFileCache.js b/client/cache/activeFileCache.js
deleted file mode 100644
index 7610735..0000000
--- a/client/cache/activeFileCache.js
+++ /dev/null
@@ -1,120 +0,0 @@
-const vscode = require('vscode');
-const { TRIGGER_LINE, TRIGGER_DEFINITION, LOCAL_VAR_WORD_PATTERN } = require('../enum/regex');
-const { getWords } = require('../utils/matchUtils');
-const dataTypeToMatchId = require('../resource/dataTypeToMatchId');
-const { getLines } = require('../utils/stringUtils');
-
-/**
- * A cache which keeps track of script blocks in the active / viewing file
- * Only applies to rs2 files
- * Allows a quick look up of script data by passing in a line number
- * Script data object:
-{
- name: string
- start: number (line number that the script starts on)
- trigger: string
- returns: string[] (matchTypeId)
- variables: { $varName1: {type: string, matchTypeId: string, parameter: boolean, declaration: range, references: range[]}, ... }
-}
- */
-var scriptData;
-var lineNumToScript;
-var curData;
-
-function getScriptData(lineNum) {
- let data;
- for (const script of scriptData) {
- if (lineNum >= script.start) data = script;
- }
- return data;
-}
-
-function rebuild() {
- scriptData = [];
- lineNumToScript = {};
- curData = null;
- const activeEditor = vscode.window.activeTextEditor;
- if (activeEditor && activeEditor.document.uri.path.endsWith('.rs2')) {
- parseFile(getLines(activeEditor.document.getText()), activeEditor.document.uri);
- }
-}
-
-function parseFile(lines, uri) {
- for (let i = 0; i < lines.length; i++) {
- let line = lines[i];
- let indexOffset = 0;
- if (TRIGGER_LINE.test(line)) {
- const definitionLength = TRIGGER_DEFINITION.exec(line);
- if (definitionLength) {
- // Split the line into definition part and code part, for scripts with same line code
- indexOffset = definitionLength[0].length;
- parseTriggerLine(line.substring(0, indexOffset), i, uri);
- line = line.substring(indexOffset); // update line to only the code portion of the line (if any)
- }
- }
- parseLine(line, i, uri, indexOffset);
- }
- if (curData) scriptData.push(curData);
-}
-
-function parseTriggerLine(line, lineNum, uri) {
- // Save previously parsed script data and init a new one for this block
- if (curData) scriptData.push(curData);
- curData = {start: lineNum, variables: {}, returns: []};
-
- // Parse for script name and trigger
- const nameAndTrigger = line.substring(1, line.indexOf(']')).split(',');
- curData.trigger = nameAndTrigger[0];
- curData.name = nameAndTrigger[1];
-
- // Parse script params and save as variables
- let openingIndex = line.indexOf('(');
- let closingIndex = line.indexOf(')');
- if (openingIndex >= 0 && closingIndex >= 0 && ++openingIndex !== closingIndex) {
- line.substring(openingIndex, closingIndex).split(',').forEach(param => {
- const split = param.trim().split(' ');
- const position = new vscode.Position(lineNum, line.indexOf(split[1]));
- const location = new vscode.Location(uri, new vscode.Range(position, position.translate(0, split[1].length)));
- addVariable(split[0], split[1], location, true);
- });
- }
-
- // Parse return type into an array of matchTypeId (string)
- line = line.substring(closingIndex + 1);
- openingIndex = line.indexOf('(');
- closingIndex = line.indexOf(')');
- if (openingIndex >= 0 && closingIndex >= 0 && ++openingIndex !== closingIndex) {
- curData.returns = line.substring(openingIndex, closingIndex).split(',').map(item => dataTypeToMatchId(item.trim()));
- }
-}
-
-function parseLine(line, lineNum, uri, indexOffset=0) {
- const words = getWords(line.split('//')[0], LOCAL_VAR_WORD_PATTERN);
- for (let i = 0; i < words.length; i++) {
- if (words[i].value.charAt(0) === '$') {
- const name = words[i].value;
- const position = new vscode.Position(lineNum, words[i].start + indexOffset);
- const location = new vscode.Location(uri, new vscode.Range(position, position.translate(0, name.length)));
- (i > 0 && words[i-1].value.startsWith('def_')) ? addVariable(words[i-1].value.substring(4), name, location) : addVariableReference(name, location);
- }
- }
-}
-
-function addVariable(type, name, location, isParam=false) {
- curData.variables[name] = {
- type: type,
- matchTypeId: dataTypeToMatchId(type),
- parameter: isParam,
- declaration: location,
- references: []
- };
- addVariableReference(name, location);
-}
-
-function addVariableReference(name, location) {
- if (curData.variables[name]) {
- curData.variables[name].references.push(location);
- }
-}
-
-module.exports = { rebuild, getScriptData };
diff --git a/client/cache/cacheManager.js b/client/cache/cacheManager.js
deleted file mode 100644
index f1647e7..0000000
--- a/client/cache/cacheManager.js
+++ /dev/null
@@ -1,197 +0,0 @@
-const fs = require('fs').promises;
-const vscode = require('vscode');
-const matchType = require("../matching/matchType");
-const identifierCache = require('./identifierCache');
-const activeFileCache = require('./activeFileCache');
-const stringUtils = require('../utils/stringUtils');
-const { matchWords } = require('../matching/matchWord');
-const identifierFactory = require('../resource/identifierFactory');
-const { INFO_MATCHER, TRIGGER_LINE } = require('../enum/regex');
-const cacheUtils = require('../utils/cacheUtils');
-const returnBlockLinesCache = require('./returnBlockLinesCache');
-const switchStmtLinesCache = require('./switchStmtLinesCache');
-const dataTypeToMatchId = require('../resource/dataTypeToMatchId');
-
-/**
- * Builds the set of monitored file types, any file events with other file types will be ignored
- * Monitored file types are determined by checking all file types defined in the matchType object
- */
-const monitoredFileTypes = new Set();
-function determineFileTypes() {
- monitoredFileTypes.add('pack');
- Object.keys(matchType).filter(mt => !mt.referenceOnly).forEach(matchTypeId => {
- const fileTypes = matchType[matchTypeId].fileTypes || [];
- for (const fileType of fileTypes) {
- monitoredFileTypes.add(fileType);
- }
- });
-}
-
-/**
- * Rebuilds the entire identifier cache for all relevant workspace files
- * Need to do 2 passes on the files to for ensuring things like engine command
- * parameters get matched correctly. On the first pass, the commands don't yet exist in the cache
- * so the matching service cannot accurately build everything until 2 passes are made
- */
-async function rebuildAll() {
- if (monitoredFileTypes.size === 0) determineFileTypes();
- clearAll();
- const fileUris = await getFiles();
- await Promise.all(fileUris.map(uri => parseFileAndCacheIdentifiers(uri)));
- await Promise.all(fileUris.map(uri => parseFileAndCacheIdentifiers(uri)));
- return rebuildActiveFile();
-}
-
-/**
- * Rebuilds the activeFileCache, parses the active text editor file and stores relevant script data
- * such as script variables, script return types, switch statement types, etc...
- */
-var debounceTimer;
-function rebuildActiveFile() {
- clearTimeout(debounceTimer);
- debounceTimer = setTimeout(() => {
- activeFileCache.rebuild();
- }, 400);
-}
-
-/**
- * Rebuilds the identifier cache for identifiers in the provided file uri
- */
-async function rebuildFile(uri) {
- if (isValidFile(uri)) {
- clearFile(uri);
- parseFileAndCacheIdentifiers(uri);
- rebuildActiveFile();
- }
-}
-
-/**
- * Clears the identifier cache for identifiers in the provided list of file uris
- */
-async function clearFiles(uris) {
- for (const uri of uris) {
- if (isValidFile(uri)) {
- clearFile(uri);
- }
- }
-}
-
-/**
- * Clears the identifier cache for identifiers in the provided list of old file uris
- * and then recaches the files using the new file names
- */
-async function renameFiles(uriPairs) {
- for (const uriPair of uriPairs) {
- if (isValidFile(uriPair.oldUri) && isValidFile(uriPair.newUri)) {
- clearFile(uriPair.oldUri);
- parseFileAndCacheIdentifiers(uriPair.newUri);
- }
- }
-}
-
-/**
- * Adds to cache for new files
- */
-async function createFiles(uris) {
- for (const uri of uris) {
- if (isValidFile(uri)) {
- parseFileAndCacheIdentifiers(uri);
- }
- }
-}
-
-/**
- * Get a list of all relevant files in the workspace which might contain identifiers
- */
-async function getFiles() {
- const fileTypesToScan = [];
- monitoredFileTypes.forEach(fileType => fileTypesToScan.push(`**/*.${fileType}`));
- return vscode.workspace.findFiles(`{${[...fileTypesToScan].join(',')}}`);
-}
-
-/**
- * Parses the input file for identifiers, and caches them when found
- */
-async function parseFileAndCacheIdentifiers(uri) {
- const isRs2 = uri.fsPath.endsWith('.rs2');
- const fileText = await fs.readFile(uri.fsPath, "utf8");
- const lines = stringUtils.getLines(fileText);
- for (let line = 0; line < lines.length; line++) {
- cacheSwitchStatementBlock(line, uri);
- const matches = (matchWords(lines[line], line, uri) || []).filter(match => match && match.match.cache);
- if (matches.length > 0) {
- const text = {lines: null, start: 0};
- matches.forEach(match => {
- if (match.match.declaration) {
- text.lines = (text.lines) ? text.lines : lines.slice(line);
- const location = new vscode.Location(uri, new vscode.Position(line, match.context.word.start));
- const identifier = identifierFactory.build(match.word, match.match, location, getInfo(lines, line), text);
- identifierCache.put(match.word, match.match, identifier);
- identifierCache.putReference(match.word, match.match, uri, line, match.context.word.start);
- cacheReturnBlock(identifier, line, match);
- } else {
- const id = match.context.cert ? undefined : match.context.packId;
- let index = match.context.word.start;
- if (!match.context.modifiedWord && match.word.indexOf(':') > 0) {
- index += match.word.indexOf(':') + 1;
- }
- identifierCache.putReference(match.word, match.match, uri, line, index, id);
- }
- });
- }
- }
-
- function cacheReturnBlock(identifier, line, match) {
- if (isRs2 && identifier.signature.returns.length > 0 && TRIGGER_LINE.test(lines[line])) {
- returnBlockLinesCache.put(line + 1, cacheUtils.resolveKey(match.word, match.match), uri);
- }
- }
-
- function cacheSwitchStatementBlock(line, uri) {
- if (isRs2) {
- const switchSplit = lines[line].split("switch_");
- if (switchSplit.length > 1) {
- const switchMatchType = dataTypeToMatchId(switchSplit[1].split(/[ (]/)[0]);
- if (switchMatchType !== matchType.UNKNOWN.id) {
- switchStmtLinesCache.put(line + 1, switchMatchType, uri);
- }
- }
- }
- }
-}
-
-/**
- * Checks the previous line before an identifier for an "info" tag, if so it is added to the identifier
- */
-function getInfo(lines, line) {
- if (line < 1) return null;
- const infoMatch = INFO_MATCHER.exec(lines[line - 1]);
- return (infoMatch && infoMatch[2]) ? infoMatch[2].trim() : null;
-}
-
-/**
- * Checks if the file extension of the uri is in the list of monitored file types
- */
-function isValidFile(uri) {
- return monitoredFileTypes.has(uri.fsPath.split(/[#?]/)[0].split('.').pop().trim());
-}
-
-/**
- * Empty the caches entirely
- */
-function clearAll() {
- identifierCache.clear();
- returnBlockLinesCache.clear();
- switchStmtLinesCache.clear();
-}
-
-/**
- * Empty the caches for a single file
- */
-function clearFile(uri) {
- identifierCache.clearFile(uri);
- returnBlockLinesCache.clearFile(uri);
- switchStmtLinesCache.clearFile(uri);
-}
-
-module.exports = { rebuildAll, rebuildFile, rebuildActiveFile, clearFiles, renameFiles, createFiles, clearAll }
diff --git a/client/cache/class/LineReferenceCache.js b/client/cache/class/LineReferenceCache.js
deleted file mode 100644
index e4c35bd..0000000
--- a/client/cache/class/LineReferenceCache.js
+++ /dev/null
@@ -1,57 +0,0 @@
-const { resolveFileKey } = require("../../utils/cacheUtils");
-
-function encodeLineValue(startLine, identifierKey) {
- return `${startLine}|${identifierKey}`;
-}
-
-function decodeLineValue(encodedValue) {
- const split = encodedValue.split('|');
- return (split.length !== 2) ? null : { line: Number(split[0]), value: split[1] };
-}
-
-class LineReferenceCache {
- constructor() {
- this.cache = {};
- }
-
- put(startLine, value, uri) {
- const fileKey = resolveFileKey(uri);
- if (value && fileKey) {
- const fileLineReferences = this.cache[fileKey] || new Set();
- fileLineReferences.add(encodeLineValue(startLine, value));
- this.cache[fileKey] = fileLineReferences;
- }
- }
-
- get(lineNum, uri) {
- const fileKey = resolveFileKey(uri);
- const fileLineReferences = this.cache[fileKey] || new Set();
- let curKey;
- let curLine = 0;
- fileLineReferences.forEach(ref => {
- const { line, value } = decodeLineValue(ref);
- if (lineNum >= line && curLine < line) {
- curKey = value;
- curLine = line;
- }
- });
- return curKey;
- }
-
- getAll() {
- return this.cache;
- }
-
- clearFile(uri) {
- const fileKey = resolveFileKey(uri);
- if (fileKey) {
- delete this.cache[fileKey];
- }
- }
-
- clear() {
- this.cache = {};
- }
-}
-
-module.exports = LineReferenceCache;
diff --git a/client/cache/class/Trie.js b/client/cache/class/Trie.js
deleted file mode 100644
index 8466921..0000000
--- a/client/cache/class/Trie.js
+++ /dev/null
@@ -1,89 +0,0 @@
-class Trie {
- constructor() {
- this.root = new TrieNode();
- }
-
- insert(word) {
- if (!word) return false;
- let currNode = this.root;
- for (const letter of word) {
- if (!currNode.children.has(letter)) {
- currNode.children.set(letter, new TrieNode(letter));
- }
- currNode = currNode.children.get(letter);
- }
- currNode.endOfWord = true;
- return currNode;
- }
-
- getLastNode(letters, start = this.root) {
- let currNode = start;
- for (const letter of letters) {
- if (!currNode.children.has(letter)) return false;
- currNode = currNode.children.get(letter);
- }
- return currNode;
- }
-
- hasWord(word, start = this.root) {
- let node = this.getLastNode(word, start);
- return node && node !== this.root ? node.endOfWord : false;
- }
-
- findAllWithPrefix(prefix, start = this.root) {
- let words = [];
- let currNode = this.getLastNode(prefix, start);
- if (currNode) {
- if (currNode.endOfWord) words.push(prefix);
- currNode.children.forEach((child) =>
- this.getWordsFrom(child, prefix, words)
- );
- }
- return words;
- }
-
- getWordsFrom(node = this.root, string = '', array = []) {
- if (!node) return;
- string += node.value;
- if (node.endOfWord) array.push(string);
- node.children.forEach((child) => {
- this.getWordsFrom(child, string, array);
- });
- return array;
- }
-
- removeWord(word) {
- if (!word) return false;
- let currNode = this.root;
- let stack = [];
- for (const letter of word) {
- if (!currNode.children.has(letter)) return false;
- currNode = currNode.children.get(letter);
- if (word[word.length - 1] !== currNode.value) stack.push(currNode);
- }
- currNode.endOfWord = false;
- while (stack.length > 0 && !currNode.endOfWord) {
- let prevNode = currNode;
- currNode = stack.pop();
- if (prevNode.children.size > 0) {
- break;
- }
- currNode.children.delete(prevNode.value);
- }
- return true;
- }
-
- clear() {
- this.root.children.clear();
- }
-}
-
-class TrieNode {
- constructor(value = "") {
- this.children = new Map();
- this.value = value;
- this.endOfWord = false;
- }
-}
-
-module.exports = Trie;
diff --git a/client/cache/completionCache.js b/client/cache/completionCache.js
deleted file mode 100644
index b9a96cc..0000000
--- a/client/cache/completionCache.js
+++ /dev/null
@@ -1,55 +0,0 @@
-const Trie = require('./class/Trie');
-
-/**
- * One trie per matchType, stores the names of all identifiers of a matchtype in a trie datastructure
- * This is used for quicker code completion lookups
- */
-var completionCache = {};
-
-function put(name, matchTypeId) {
- if (!completionCache[matchTypeId]) {
- completionCache[matchTypeId] = new Trie();
- }
- completionCache[matchTypeId].insert(name);
- const colonIndex = name.indexOf(':');
- if (colonIndex >= 0) {
- completionCache[matchTypeId].insert(name.substring(colonIndex + 1));
- }
-}
-
-function getAllWithPrefix(prefix, matchTypeId) {
- const matchTrie = completionCache[matchTypeId];
- if (matchTrie) {
- return matchTrie.findAllWithPrefix(prefix);
- }
- return null;
-}
-
-function contains(name, matchTypeId) {
- const matchTrie = completionCache[matchTypeId];
- if (matchTrie) {
- return matchTrie.hasWord(name);
- }
- return false;
-}
-
-function remove(name, matchTypeId) {
- const matchTrie = completionCache[matchTypeId];
- if (matchTrie) {
- matchTrie.removeWord(name);
- }
-}
-
-function clear(matchTypeId) {
- if (matchTypeId) {
- delete completionCache[matchTypeId];
- } else {
- completionCache = {};
- }
-}
-
-function getTypes() {
- return Object.keys(completionCache);
-}
-
-module.exports = { put, getAllWithPrefix, getTypes, contains, remove, clear };
\ No newline at end of file
diff --git a/client/cache/identifierCache.js b/client/cache/identifierCache.js
deleted file mode 100644
index 2f9fdb6..0000000
--- a/client/cache/identifierCache.js
+++ /dev/null
@@ -1,136 +0,0 @@
-const { buildRef } = require('../resource/identifierFactory');
-const cacheUtils = require('../utils/cacheUtils');
-const completionCache = require('./completionCache');
-
-/**
- * The identifierCache stores all matched identifiers in the workspace
- * identifierCache = {key [name+matchTypeId]: identifier}
- * See identifierFactory.js for the object structure
- */
-var identifierCache = {};
-
-/**
- * The fileToIdentiferMap keeps track of all identifiers and references in a file
- * This is used for updating the cache as necessary when a file is modified
- * fileToIdentiferMap = {filePath: {declarations: Set(): identifierKey, references: Set(): identifierKey}}
- */
-var fileToIdentifierMap = {};
-
-function contains(name, match) {
- return identifierCache[cacheUtils.resolveKey(name, match)] !== undefined;
-}
-
-function get(name, match) {
- return identifierCache[cacheUtils.resolveKey(name, match)];
-}
-
-function getByKey(key) {
- return identifierCache[key];
-}
-
-/**
- * Given a file URI, a line number, this will return the closest declaration identifier
- * to the given line number which is above the line number provided.
- */
-function getParentDeclaration(uri, lineNum, requiredMatchTypeId=undefined) {
- const fileIdentifiers = fileToIdentifierMap[cacheUtils.resolveFileKey(uri)];
- if (!fileIdentifiers) {
- return null;
- }
- let lineRef = -1;
- let declaration;
- fileIdentifiers.declarations.forEach(dec => {
- const iden = identifierCache[dec];
- if (iden.declaration && iden.declaration.range.start.line < lineNum && iden.declaration.range.start.line > lineRef) {
- if (!requiredMatchTypeId || requiredMatchTypeId === iden.matchId) {
- lineRef = iden.declaration.range.start.line;
- declaration = iden;
- }
- }
- });
- return declaration;
-}
-
-function put(name, match, identifier) {
- const key = cacheUtils.resolveKey(name, match);
- const fileKey = cacheUtils.resolveFileKey(identifier.declaration.uri);
- if (!key || !fileKey) {
- return null;
- }
- let curIdentifier = identifierCache[key];
- if (curIdentifier && curIdentifier.declaration) {
- return null; // declaration already exists, don't overwrite, if it needs to be updated it should be deleted first
- }
- if (curIdentifier) {
- if (curIdentifier.id) identifier.id = curIdentifier.id;
- if (!curIdentifier.declaration) identifier.references = curIdentifier.references;
- }
- addToFileMap(fileKey, key);
- identifierCache[key] = identifier;
- completionCache.put(name, match.id);
-}
-
-function putReference(name, match, uri, lineNum, index, packId) {
- const key = cacheUtils.resolveKey(name, match)
- const fileKey = cacheUtils.resolveFileKey(uri);
- if (!key || !fileKey) {
- return null;
- }
- if (!identifierCache[key]) {
- identifierCache[key] = buildRef(name, match);
- }
- const fileReferences = identifierCache[key].references[fileKey] || new Set();
- fileReferences.add(cacheUtils.encodeReference(lineNum, index));
- addToFileMap(fileKey, key, false);
- identifierCache[key].references[fileKey] = fileReferences;
- if (packId) identifierCache[key].id = packId;
- if (match.referenceOnly) completionCache.put(name, match.id);
-}
-
-function clear() {
- identifierCache = {};
- fileToIdentifierMap = {};
- completionCache.clear();
-}
-
-function clearFile(uri) {
- const fileKey = cacheUtils.resolveFileKey(uri);
- const identifiersInFile = fileToIdentifierMap[fileKey] || { declarations: new Set(), references: new Set() };
- identifiersInFile.references.forEach(key => {
- if (identifierCache[key]) {
- // Delete references to the cleared file from every identifier which referenced the file
- if (identifierCache[key].references[fileKey]) {
- delete identifierCache[key].references[fileKey];
- }
- // Cleanup/Delete identifiers without a declaration who no longer have any references
- if (Object.keys(identifierCache[key].references).length === 0 && !identifierCache[key].declaration) {
- const iden = identifierCache[key];
- completionCache.remove(iden.name, iden.matchId);
- delete identifierCache[key];
- }
- }
- })
- identifiersInFile.declarations.forEach(key => {
- if (identifierCache[key]) {
- // If the identifier has orphaned references, then we only delete the declaration and keep the identifier w/references
- // Otherwise, we delete the entire identifier (no declaration and no references => no longer exists in any capacity)
- const iden = identifierCache[key];
- completionCache.remove(iden.name, iden.matchId);
- const hasOrphanedRefs = Object.keys(identifierCache[key].references).length > 0;
- if (hasOrphanedRefs) {
- delete identifierCache[key].declaration;
- } else {
- delete identifierCache[key];
- }
- }
- });
- delete fileToIdentifierMap[fileKey];
-}
-
-function addToFileMap(fileKey, identifierKey, declaration=true) {
- const identifiersInFile = fileToIdentifierMap[fileKey] || { declarations: new Set(), references: new Set() };
- (declaration) ? identifiersInFile.declarations.add(identifierKey) : identifiersInFile.references.add(identifierKey);
- fileToIdentifierMap[fileKey] = identifiersInFile;
-}
-
-module.exports = { contains, get, getParentDeclaration, getByKey, put, putReference, clear, clearFile };
diff --git a/client/cache/returnBlockLinesCache.js b/client/cache/returnBlockLinesCache.js
deleted file mode 100644
index 345b9c6..0000000
--- a/client/cache/returnBlockLinesCache.js
+++ /dev/null
@@ -1,11 +0,0 @@
-const LineReferenceCache = require("./class/LineReferenceCache");
-
-/**
- * A cache which enables a quick lookup of the identifier for the block the line is in
- * Given a line number, it will return the name of the block that line number is a part of (if any)
- * A block referring to the code block of a proc, label, queue, etc...
- * This cache is used to quickly determine the return type for a given line
- */
-const returnBlockLinesCache = new LineReferenceCache();
-
-module.exports = returnBlockLinesCache;
diff --git a/client/cache/switchStmtLinesCache.js b/client/cache/switchStmtLinesCache.js
deleted file mode 100644
index b2ffbc4..0000000
--- a/client/cache/switchStmtLinesCache.js
+++ /dev/null
@@ -1,10 +0,0 @@
-const LineReferenceCache = require("./class/LineReferenceCache");
-
-/**
- * A cache which enables a quick lookup of the matchType of a switch statement
- * Given a line number, this cache will return the type (if any) for the switch statement
- * that line number is a part of
- */
-const switchStmtLinesCache = new LineReferenceCache();
-
-module.exports = switchStmtLinesCache;
diff --git a/client/enum/hoverConfigOptions.js b/client/enum/hoverConfigOptions.js
deleted file mode 100644
index 2ffe6e7..0000000
--- a/client/enum/hoverConfigOptions.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const option = {
- DECLARATION_HOVER_ITEMS: 'DECLARATION_HOVER_ITEMS', // display items that show on hover for identifier declarations
- REFERENCE_HOVER_ITEMS: 'REFERENCE_HOVER_ITEMS', // display items that show on hover for identifier references
- LANGUAGE: 'LANGUAGE', // the code language that this matchType should use in hover codeblock text
- BLOCK_SKIP_LINES: 'BLOCK_SKIP_LINES', // the number of lines to skip in code block displays (default value is 1 -> skip first line for most blocks which is the '[identifierName]' line)
- CONFIG_INCLUSIONS: 'CONFIG_INCLUSIONS' // the config tags you want to be shown (ex: obj displays name, desc, and category only), if null (default) then all fields are displayed
-}
-
-module.exports = option;
diff --git a/client/enum/hoverDisplayItems.js b/client/enum/hoverDisplayItems.js
deleted file mode 100644
index ae4be8d..0000000
--- a/client/enum/hoverDisplayItems.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// In order for a display item to be shown in hover texts, the matchType to which the identifier belongs to
-// must define a declaration or reference config which includes the desired hoverDisplay item in its displayItems array
-// Note: in order to get identifier.value to display you must define a custom postProcessor for the matchType which
-// populates identifier.value, there is no default value parsing like there is with the others
-
-const hoverDisplay = {
- TITLE: 'title', // hover text title display :
fileType.png matchType.id identifier.name
- INFO: 'info', // hover text info display : identifier.info (in italics)
- VALUE: 'value', // hover text value display : identifier.value (plain text)
- SIGNATURE: 'signature', // signature display : identifier.params
identifier.returns (in code syntax)
- CODEBLOCK: 'codeblock' // block display : identifier.block (in code syntax)
-};
-
-module.exports = hoverDisplay;
diff --git a/client/enum/regex.js b/client/enum/regex.js
deleted file mode 100644
index 707ca58..0000000
--- a/client/enum/regex.js
+++ /dev/null
@@ -1,22 +0,0 @@
-const regex = {
- COORD: /(\d+_){4}\d+/,
- COLOR: /\d{6}/,
- RECOLOR: /(recol[1-6][sd])=(\d+)/g,
- NUMBER: /^\d+.?\d+$/,
- END_OF_BLOCK: /(\r\n|\r|\n)(\[.+|val=.+|\^.+|\d+=.+)(?:$|(\r\n|\r|\n))/,
- END_OF_BLOCK_LINE: /^(\[|\^|\d+=)/,
- START_OF_LINE: /(?<=[\n])(?!.*[\n]).*/,
- END_OF_LINE: /\r\n|\r|\n/,
- WORD_PATTERN: /(\.\w+)|(\w+:\w+)|([^\`\~\!\@\#\%\^\&\*\(\)\-\$\=\+\[\{\]\}\\\|\;\:\'\\"\,\.\<\>\/\?\s]+)/g,
- LOCAL_VAR_WORD_PATTERN: /(\$\w+)|(\.\w+)|(\w+:\w+)|([^\`\~\!\@\#\%\^\&\*\(\)\-\$\=\+\[\{\]\}\\\|\;\:\'\\"\,\.\<\>\/\?\s]+)/g,
- CONFIG_LINE: /^\w+=.+$/,
- CONFIG_DECLARATION: /\[\w+\]/,
- TRIGGER_LINE: /\[\w+,(\.)?\w+(:\w+)?\]/,
- TRIGGER_DEFINITION: /\[.+,.+\](\([\w, :\.$]*\))?(\([\w, :\.$]*\))?/,
- INFO_MATCHER: /\/\/[ ]{0,1}(desc|info):(.+)/,
- SWITCH_CASE: /\s*case.+/,
- COLOR24: /(colour|mapcolour|activecolour|overcolour|activeovercolour)=(\w+)/g,
- LOC_MODEL: /^(?!model_[a-z0-9]$)\w+_[a-z0-9]\b/
-}
-
-module.exports = regex;
diff --git a/client/info/configKeyInfo.js b/client/info/configKeyInfo.js
deleted file mode 100644
index ec589ac..0000000
--- a/client/info/configKeyInfo.js
+++ /dev/null
@@ -1,47 +0,0 @@
-const { expandCsvKeyObject } = require("../utils/matchUtils");
-
-/**
- * Defines any config keys with info that will be displayed when the user hovers over that config key
- * Format: { key: { 'any': 'info for any fileType', 'obj': 'obj specific info', 'loc, npc': 'loc and npc specific info' } }
- * You can define different info for specific file types, or use 'any' to apply to all file types (unless already defined)
- * Tip: you can use the same value for multiple file types using a key as a CSV (i.e. use 'obj, loc, npc' as a key)
- * Tip: config key will apply to all tags which end in numbers (for example stock will apply to stock1, stock2, stock100, etc...)
- * Tip: you can use $TYPE which will be replaced by the file type of the config (loc, obj, etc...)
- */
-const configKeyInfo = expandInfo({
- type: { 'varp': 'The data type of this player variable' },
- param: { 'any': 'A param value in the format "paramName,value"' },
- inputtype: { 'enum': 'The input data type for the enum' },
- outputtype: { 'enum': 'The output data type for the enum' },
- val: { 'enum': 'A data value for the enum in the format "inputData,outputData"' },
- scope: { 'varp': 'The lifetime of a player variable\n\nBy default it is temporary and reset on logout/login. You can make it persist by setting scope=perm' },
- protect: { 'varp': 'If the player variable should require protected access\n\nDefault value true (acceptable values: true/yes, false/no)\n\nProtected means a script can not write to it without sole access, but a varp can always be read regardless of the protection.' },
- clientcode: { 'varp, if': 'Ties this to specific client-side code logic\n\nAcceptable value defined in client source, if you actually need this you should already know what to put.' },
- transmit: { 'varp': 'If a player variable should be transmitted to the client\n\nDefault value false (acceptable values: true/yes, false/no)\n\nThe main use for this property is in conjunction with interfaces.' },
- stock: { 'inv': 'Stock of an item in a shop, format "object,stock,restock_ticks"'},
- count: { 'obj': 'Object to use when the based on the stack size of an item' },
- respawnrate: { 'obj, npc': 'Respawn rate of this $TYPE, in game ticks' },
- category: { 'any': 'The category this $TYPE belongs to, multiple categories are possible\n\nCan be used with category engine commands such as inv_totalcat\n\nAlso can be used in triggers by preceeding the category with an underscore (_)\n\nEx: [oplocu,_watersource] is a script which applies to all items with the category \'watersource\'' },
- basevar: { 'varbit': 'The base varp backing this varbit.' },
- startbit: { 'varbit': 'The starting bit range on the basevar to limit this view to.' },
- endbit: { 'varbit': 'The ending bit range on the basevar to limit this view to.' },
-});
-function expandInfo(obj) {
- Object.keys(obj).forEach(key => obj[key] = expandCsvKeyObject(obj[key]));
- return obj;
-}
-
-// Find info for a given config key. If no fileType, will match config keys for 'any' type. Else, return null.
-function matchConfigKeyInfo(key, fileType) {
- const endingNums = key.match(/\d+$/);
- if (endingNums) {
- key = key.substring(0, key.indexOf(endingNums));
- }
- const info = configKeyInfo[key];
- if (info[fileType]) {
- return info[fileType];
- }
- return info.any;
-}
-
-module.exports = matchConfigKeyInfo;
diff --git a/client/info/triggerInfo.js b/client/info/triggerInfo.js
deleted file mode 100644
index 3215321..0000000
--- a/client/info/triggerInfo.js
+++ /dev/null
@@ -1,18 +0,0 @@
-const { expandCsvKeyObject } = require("../utils/matchUtils");
-
-/**
- * Defines trigger information which will be displayed on hover if a user hovers over a trigger keyword
- * Tip: You can use CSV keys such as 'oploc1, oploc2, oploc3' to apply the same info message for all of those triggers
- * Tip: The string 'NAME' will be replaced with the actual triggers defined name [trigger,triggerName]
- */
-const triggerInfo = expandCsvKeyObject({
- logout: 'The script that executes when the user logs out',
- debugproc: 'Proc that only runs for users with cheats enabled, run with ::NAME'
-});
-
-
-function matchTriggerInfo(key, triggerName) {
- return (triggerInfo[key] || '').replace('NAME', triggerName);
-}
-
-module.exports = matchTriggerInfo;
diff --git a/client/matching/matchType.js b/client/matching/matchType.js
deleted file mode 100644
index 4ec30f2..0000000
--- a/client/matching/matchType.js
+++ /dev/null
@@ -1,196 +0,0 @@
-const { dataTypePostProcessor, enumPostProcessor, columnPostProcessor, rowPostProcessor, componentPostProcessor,
- fileNamePostProcessor, coordPostProcessor, configKeyPostProcessor, triggerPostProcessor, categoryPostProcessor } = require('../resource/postProcessors');
-const { VALUE, SIGNATURE, CODEBLOCK, TITLE, INFO } = require("../enum/hoverDisplayItems");
-const { DECLARATION_HOVER_ITEMS, REFERENCE_HOVER_ITEMS, LANGUAGE, BLOCK_SKIP_LINES, CONFIG_INCLUSIONS } = require('../enum/hoverConfigOptions');
-
-/*
-Match types define the possible types of identifiers that can be found. The config for a match type tells the extension
-all the necessary data it needs for finding declarations, building hover texts, and finding references.
-{
- id: String - the unique id for the matchType,
- types: String[] - the type keywords which map to this matchType, for example: [namedobj, obj] for OBJ
- fileTypes: String[] - the possible file types this matchType can be declared in
- cache: boolean - whether or not identifiers with this matchType should be cached
- hoverConfig: Object - Config options to modify the hover display for this matchType, options in hoverConfig.js
- postProcessor: Function(identifier) - An optional post processing function to apply for this matchType, see postjs
- allowRename: Whether or not to allow rename symbol (F2) on this type
- referenceOnly: If true, then declaration is not saved/doesn't exist and only references exist. Default ctrl+click will be goto references rather than goto definition.
- hoverOnly: boolean - if true, this match type is only used for hover displays
- noop: boolean - if true, nothing is done with this match type (but still useful for terminating word searching early)
-}
-*/
-const matchType = {
- LOCAL_VAR: {
- id: 'LOCAL_VAR', types: [], fileTypes: ['rs2'], cache: false, allowRename: true,
- },
- GLOBAL_VAR: {
- id: 'GLOBAL_VAR', types: ['var'], fileTypes: ['varp', 'varbit', 'vars', 'varn'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'varpconfig'},
- postProcessor: dataTypePostProcessor
- },
- CONSTANT: {
- id: 'CONSTANT', types: [], fileTypes: ['constant'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'constants', [BLOCK_SKIP_LINES]: 0},
- },
- LABEL: {
- id: 'LABEL', types: ['label'], fileTypes: ['rs2'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE]},
- },
- PROC: {
- id: 'PROC', types: ['proc'], fileTypes: ['rs2'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE]},
- },
- TIMER: {
- id: 'TIMER', types: ['timer'], fileTypes: ['rs2'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE]},
- },
- SOFTTIMER: {
- id: 'SOFTTIMER', types: ['softtimer'], fileTypes: ['rs2'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE]},
- },
- QUEUE: {
- id: 'QUEUE', types: ['queue'], fileTypes: ['rs2'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE]},
- },
- SEQ: {
- id: 'SEQ', types: ['seq'], fileTypes: ['seq'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO], [LANGUAGE]: 'seqconfig'},
- },
- SPOTANIM: {
- id: 'SPOTANIM', types: ['spotanim'], fileTypes: ['spotanim'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO], [LANGUAGE]: 'spotanimconfig'},
- },
- HUNT: {
- id: 'HUNT', types: ['hunt'], fileTypes: ['hunt'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'huntconfig', [CONFIG_INCLUSIONS]: ['type']},
- },
- LOC: {
- id: 'LOC', types: ['loc'], fileTypes: ['loc'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'locconfig', [CONFIG_INCLUSIONS]: ['name', 'desc', 'category']},
- },
- NPC: {
- id: 'NPC', types: ['npc'], fileTypes: ['npc'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'npcconfig', [CONFIG_INCLUSIONS]: ['name', 'desc', 'category']},
- },
- OBJ: {
- id: 'OBJ', types: ['namedobj', 'obj'], fileTypes: ['obj'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'objconfig', [CONFIG_INCLUSIONS]: ['name', 'desc', 'category']},
- },
- INV: {
- id: 'INV', types: ['inv'], fileTypes: ['inv'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'invconfig', [CONFIG_INCLUSIONS]: ['scope', 'size']},
- },
- ENUM: {
- id: 'ENUM', types: ['enum'], fileTypes: ['enum'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'enumconfig', [CONFIG_INCLUSIONS]: ['inputtype', 'outputtype']},
- postProcessor: enumPostProcessor
- },
- DBCOLUMN: {
- id: 'DBCOLUMN', types: ['dbcolumn'], fileTypes: ['dbtable'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'runescript', [BLOCK_SKIP_LINES]: 0},
- postProcessor: columnPostProcessor
- },
- DBROW: {
- id: 'DBROW', types: ['dbrow'], fileTypes: ['dbrow'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'dbrowconfig', [CONFIG_INCLUSIONS]: ['table']},
- postProcessor: rowPostProcessor
- },
- DBTABLE: {
- id: 'DBTABLE', types: ['dbtable'], fileTypes: ['dbtable'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'dbtableconfig'},
- },
- INTERFACE: {
- id: 'INTERFACE', types: ['interface'], fileTypes: ['if'], cache: true, allowRename: false, referenceOnly: true,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE, INFO], [LANGUAGE]: 'interface'},
- postProcessor: fileNamePostProcessor
- },
- COMPONENT: {
- id: 'COMPONENT', types: ['component'], fileTypes: ['if'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO], [LANGUAGE]: 'interface'},
- postProcessor: componentPostProcessor
- },
- PARAM: {
- id: 'PARAM', types: ['param'], fileTypes: ['param'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'paramconfig'},
- postProcessor: dataTypePostProcessor
- },
- COMMAND: {
- id: 'COMMAND', types: [], fileTypes: ['rs2'], cache: true, allowRename: false,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE]},
- },
- SYNTH: {
- id: 'SYNTH', types: ['synth'], fileTypes: ['synth'], cache: true, allowRename: true, referenceOnly: true, renameFile: true,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE, INFO]},
- postProcessor: fileNamePostProcessor
- },
- MODEL: {
- id: 'MODEL', types: ['ob2', 'model'], fileTypes: ['ob2'], cache: true, allowRename: true, referenceOnly: true, renameFile: true,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE, INFO]},
- },
- WALKTRIGGER: {
- id: 'WALKTRIGGER', types: ['walktrigger'], fileTypes: ['rs2'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, SIGNATURE]},
- },
- IDK: {
- id: 'IDK', types: ['idk', 'idkit'], fileTypes: ['idk'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO, CODEBLOCK], [LANGUAGE]: 'idkconfig'},
- },
- MESANIM: {
- id: 'MESANIM', types: ['mesanim'], fileTypes: ['mesanim'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO], [LANGUAGE]: 'mesanimconfig'},
- },
- STRUCT: {
- id: 'STRUCT', types: ['struct'], fileTypes: ['struct'], cache: true, allowRename: true,
- hoverConfig: {[DECLARATION_HOVER_ITEMS]: [TITLE, INFO], [REFERENCE_HOVER_ITEMS]: [TITLE, INFO], [LANGUAGE]: 'structconfig'},
- },
- // Hover only match types that are only used for displaying hover displays (no finding references/declarations)
- // Useful for terminating word searches early when detected. Postprocessing can be done on these.
- // Specify referenceConfig to select which displayItems should be shown on hover.
- COORDINATES: {
- id: 'COORDINATES', types: [], hoverOnly: true, cache: false, allowRename: false,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE, VALUE]},
- postProcessor: coordPostProcessor
- },
- CONFIG_KEY: {
- id: 'CONFIG_KEY', types: [], hoverOnly: true, cache: false, allowRename: false,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE, INFO]},
- postProcessor: configKeyPostProcessor
- },
- TRIGGER: {
- id: 'TRIGGER', types: [], hoverOnly: true, cache: false, allowRename: false,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE, INFO]},
- postProcessor: triggerPostProcessor
- },
- STAT: {
- id: 'STAT', types: ['stat'], hoverOnly: true, cache: false, allowRename: false,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE]},
- },
- NPC_STAT: {
- id: 'NPC_STAT', types: ['npc_stat'], hoverOnly: true, cache: false, allowRename: false,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE]},
- },
- NPC_MODE: {
- id: 'NPC_MODE', types: ['npc_mode'], hoverOnly: true, cache: false, allowRename: false,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE]},
- },
- LOCSHAPE: {
- id: 'LOCSHAPE', types: ['locshape'], hoverOnly: true, cache: false, allowRename: false,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE]},
- },
- FONTMETRICS: {
- id: 'FONTMETRICS', types: ['fontmetrics'], hoverOnly: true, cache: false, allowRename: false,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE]},
- },
- CATEGORY: {
- id: 'CATEGORY', types: ['category'], hoverOnly: true, cache: true, allowRename: true, referenceOnly: true,
- hoverConfig: {[REFERENCE_HOVER_ITEMS]: [TITLE, VALUE]},
- postProcessor: categoryPostProcessor
- },
- // NOOP Match types that might get detected, but nothing is done with them (no hover display, no finding references/declarations)
- // Useful for terminating word searching early when detected, and possibly doing something with them at a later date
- UNKNOWN: { id: 'UNKNOWN', noop: true, cache: false }, // default to map to when a value is matched but no specific matchType for it
- COLOR: { id: 'COLOR', noop: true, cache: false },
- NUMBER: { id: 'NUMBER', noop: true, cache: false }
-};
-
-module.exports = matchType;
diff --git a/client/matching/matchWord.js b/client/matching/matchWord.js
deleted file mode 100644
index ff70384..0000000
--- a/client/matching/matchWord.js
+++ /dev/null
@@ -1,131 +0,0 @@
-const matchType = require('./matchType');
-const { getWordAtIndex, getBaseContext } = require('../utils/matchUtils');
-const { getParentDeclaration } = require('../cache/identifierCache');
-const { LOC_MODEL } = require('../enum/regex');
-
-// Do not reorder the matchers unless there is a reason to
-// quicker potential matches are processed earlier in order to short circuit faster
-const matchers = [
- require('./matchers/packMatcher'),
- require('./matchers/regexWordMatcher'),
- require('./matchers/commandMatcher'),
- require('./matchers/localVarMatcher'),
- require('./matchers/prevCharMatcher'),
- require('./matchers/triggerMatcher'),
- require('./matchers/configMatcher').configMatcher,
- require('./matchers/switchCaseMatcher'),
- require('./matchers/parametersMatcher').parametersMatcher
-];
-
-/**
- * Match with one word given a vscode document and a vscode position
- */
-function matchWordFromDocument(document, position) {
- return matchWord(document.lineAt(position.line).text, position.line, document.uri, position.character);
-}
-
-/**
- * Match with one word given a line of text and an index position
- */
-function matchWord(lineText, lineNum, uri, index) {
- if (!lineText || !uri || !index) {
- return undefined;
- }
- const context = getBaseContext(lineText, lineNum, uri);
- const word = getWordAtIndex(context.words, index);
- const wordContext = {
- ...context,
- word: word,
- lineIndex: index,
- prevWord: (word.index === 0) ? undefined : context.words[word.index - 1],
- prevChar: lineText.charAt(word.start - 1),
- nextChar: lineText.charAt(word.end + 1),
- }
- return match(wordContext);
-}
-
-/**
- * Match with all words given a line of text
- */
-function matchWords(lineText, lineNum, uri) {
- if (!lineText || !uri) {
- return undefined;
- }
- const context = getBaseContext(lineText, lineNum, uri);
- const matches = [];
- for (let i = 0; i < context.words.length; i++) {
- const wordContext = {
- ...context,
- word: context.words[i],
- lineIndex: context.words[i].start,
- prevWord: (i === 0) ? undefined : context.words[i-1],
- prevChar: lineText.charAt(context.words[i].start - 1),
- nextChar: lineText.charAt(context.words[i].end + 1),
- }
- matches.push(match(wordContext));
- }
- return matches;
-}
-
-/**
- * Iterates thru all matchers to try to find a match, short circuits early if a match is made
- */
-function match(context) {
- if (!context.word || context.word.value === 'null') { // Also ignore null
- return response();
- }
-
- for (const matcher of matchers) {
- let match = matcher(context);
- if (match) {
- return response(match, context);
- }
- }
- return response();
-}
-
-/**
- * Build the response object for a match response
- */
-function response(match, context) {
- if (!match || !context) {
- return undefined;
- }
- if (match.id === matchType.COMPONENT.id && !context.word.value.includes(':')) {
- context.word.value = `${context.file.name}:${context.word.value}`;
- context.modifiedWord = true;
- }
- if (match.id === matchType.DBCOLUMN.id && !context.word.value.includes(':')) {
- const requiredType = context.file.type === 'dbtable' ? matchType.DBTABLE.id : matchType.DBROW.id;
- const iden = getParentDeclaration(context.uri, context.line.number, requiredType);
- if (!iden) {
- return undefined;
- }
- const tableName = (context.file.type === 'dbrow') ? iden.extraData.table : iden.name;
- context.word.value = `${tableName}:${context.word.value}`;
- context.modifiedWord = true;
- }
- if (match.id === matchType.OBJ.id && context.word.value.startsWith('cert_')) {
- context.word.value = context.word.value.substring(5);
- context.word.start = context.word.start + 5;
- context.originalPrefix = 'cert_';
- context.cert = true;
- context.modifiedWord = true;
- }
- if (match.id === matchType.CATEGORY.id && context.word.value.startsWith('_')) {
- context.word.value = context.word.value.substring(1);
- context.word.start = context.word.start + 1;
- context.originalPrefix = '_';
- context.modifiedWord = true;
- }
- // If model match type, determine if it is a loc model and if so remove the suffix part (_0 or _q, etc...)
- if (match.id === matchType.MODEL.id && LOC_MODEL.test(context.word.value)) {
- const lastUnderscore = context.word.value.lastIndexOf("_");
- context.originalSuffix = context.word.value.slice(lastUnderscore);
- context.word.value = context.word.value.slice(0, lastUnderscore);
- context.modifiedWord = true;
- }
- return { match: match, word: context.word.value, context: context };
-}
-
-module.exports = { matchWord, matchWords, matchWordFromDocument };
diff --git a/client/matching/matchers/commandMatcher.js b/client/matching/matchers/commandMatcher.js
deleted file mode 100644
index d8f20d8..0000000
--- a/client/matching/matchers/commandMatcher.js
+++ /dev/null
@@ -1,22 +0,0 @@
-const identifierCache = require("../../cache/identifierCache");
-const matchType = require("../matchType");
-const { reference, declaration } = require("../../utils/matchUtils");
-const { TRIGGER_LINE } = require("../../enum/regex");
-
-/**
- * Looks for matches of known engine commands
- */
-function commandMatcher(context) {
- const command = identifierCache.get(context.word.value, matchType.COMMAND);
- if (command) {
- if (context.uri.fsPath.includes("engine.rs2") && TRIGGER_LINE.test(context.line.text) && context.word.index === 1) {
- return declaration(matchType.COMMAND);
- }
- if (command.signature.params.length > 0 && context.nextChar !== '('){
- return null;
- }
- return reference(matchType.COMMAND);
- }
-}
-
-module.exports = commandMatcher;
diff --git a/client/matching/matchers/configMatcher.js b/client/matching/matchers/configMatcher.js
deleted file mode 100644
index e1b0d96..0000000
--- a/client/matching/matchers/configMatcher.js
+++ /dev/null
@@ -1,147 +0,0 @@
-const { CONFIG_DECLARATION, CONFIG_LINE } = require("../../enum/regex");
-const matchType = require("../matchType");
-const { declaration, reference } = require("../../utils/matchUtils");
-const dataTypeToMatchId = require("../../resource/dataTypeToMatchId");
-const { regexConfigKeys, configKeys, specialCaseKeys } = require("../../resource/configKeys");
-const identifierCache = require('../../cache/identifierCache');
-
-/**
- * Looks for matches on config files, both config declarations and config line items
- */
-function configMatcher(context) {
- // Check for config file declarations (i.e. declarations with [NAME])
- if (CONFIG_DECLARATION.test(context.line.text)) {
- return declarationMatcher(context);
- }
-
- // Check if the line we are matching is a config line
- const configMatch = getConfigLineMatch(context);
- return configMatch ? configMatch.match : undefined;
-}
-
-function declarationMatcher(context) {
- switch (context.file.type) {
- case "varp": case "varbit": case "varn": case "vars": return declaration(matchType.GLOBAL_VAR);
- case "obj": return declaration(matchType.OBJ);
- case "loc": return declaration(matchType.LOC);
- case "npc": return declaration(matchType.NPC);
- case "param": return declaration(matchType.PARAM);
- case "seq": return declaration(matchType.SEQ);
- case "struct": return declaration(matchType.STRUCT);
- case "dbrow": return declaration(matchType.DBROW);
- case "dbtable": return declaration(matchType.DBTABLE);
- case "enum": return declaration(matchType.ENUM);
- case "hunt": return declaration(matchType.HUNT);
- case "inv": return declaration(matchType.INV);
- case "spotanim": return declaration(matchType.SPOTANIM);
- case "idk": return declaration(matchType.IDK);
- case "mesanim": return declaration(matchType.MESANIM);
- case "if": return declaration(matchType.COMPONENT)
- }
-}
-
-function getConfigLineMatch(context) {
- if (!CONFIG_LINE.test(context.line.text)) return null;
- const configKey = context.words[0].value;
- let response = {key: configKey};
- // The config key itsself is selected, so check if it is a known config key or not (config key with info)
- if (context.word.index === 0) {
- return {...response, match: reference(matchType.CONFIG_KEY)};
- }
- // Check for special cases that need to be manually handled
- if (specialCaseKeys.includes(configKey)) {
- return handleSpecialCases(response, configKey, context);
- }
- // Otherwise, if the second word is the selected word (word after '=') then handle remaining known keys/regex keys
- if (context.word.index >= 1) {
- const configMatch = configKeys[configKey] || getRegexKey(configKey, context);
- if (configMatch) {
- const paramIndex = getParamIndex(context);
- const param = configMatch.params[paramIndex];
- if (param) {
- const match = (param.declaration) ? declaration(matchType[dataTypeToMatchId(param.typeId)]) : reference(matchType[dataTypeToMatchId(param.typeId)]);
- return {...response, match: match, params: configMatch.params.map(p => p.typeId), index: paramIndex};
- }
- }
- }
- return null;
-}
-
-function getRegexKey(configKey, context) {
- const fileTypeRegexMatchers = regexConfigKeys.get(context.file.type) || [];
- for (let regexKey of fileTypeRegexMatchers) {
- if (regexKey.regex.test(configKey)) {
- return regexKey;
- }
- }
- return null;
-}
-
-function getParamIndex(context) {
- let line = context.line.text;
- let index = 0;
- const split = line.substring(index).split(',');
- for (i = 0; i < split.length; i++) {
- index += split[i].length + 1;
- if (context.lineIndex < index) {
- return i;
- }
- }
- return undefined;
-}
-
-function handleSpecialCases(response, key, context) {
- switch (key) {
- case 'param': return paramSpecialCase(response, context);
- case 'val': return valSpecialCase(response, context);
- case 'data': return dataSpecialCase(response, context);
- }
-}
-
-function paramSpecialCase(response, context) {
- if (context.word.index === 1) {
- return {...response, match: reference(matchType.PARAM), params: ['param','value'], index: 0};
- }
- if (context.word.index === 2) {
- const paramIdentifier = identifierCache.get(context.words[1].value, matchType.PARAM);
- if (paramIdentifier && paramIdentifier.extraData) {
- const match = reference(matchType[dataTypeToMatchId(paramIdentifier.extraData.dataType)]);
- return {...response, match: match, params: [paramIdentifier.name, paramIdentifier.extraData.dataType], index: 1};
- }
- }
- return {...response, match: matchType.UNKNOWN};
-}
-
-function valSpecialCase(response, context) {
- const enumIdentifier = identifierCache.getParentDeclaration(context.uri, context.line.number);
- if (enumIdentifier) {
- response.params = [enumIdentifier.extraData.inputType, enumIdentifier.extraData.outputType];
- response.index = getParamIndex(context);
- response.match = reference(matchType[dataTypeToMatchId(response.params[response.index])]);
- return response;
- }
- return {...response, match: matchType.UNKNOWN};
-}
-
-function dataSpecialCase(response, context) {
- if (context.word.index === 1) {
- return {...response, match: reference(matchType.DBCOLUMN), params: ['dbcolumn', 'fields...'], index: 0};
- }
- if (context.word.index > 1) {
- let colName = context.words[1].value;
- if (context.words[1].value.indexOf(':') < 0) {
- const row = identifierCache.getParentDeclaration(context.uri, context.line.number);
- colName = `${row.extraData.table}:${context.words[1].value}`
- }
- const col = identifierCache.get(colName, matchType.DBCOLUMN);
- if (col && col.extraData) {
- response.params = [col.name, ...col.extraData.dataTypes];
- response.index = getParamIndex(context);
- response.match = reference(matchType[dataTypeToMatchId(response.params[response.index])]);
- return response;
- }
- }
- return {...response, match: matchType.UNKNOWN};
-}
-
-module.exports = { configMatcher, getConfigLineMatch };
diff --git a/client/matching/matchers/localVarMatcher.js b/client/matching/matchers/localVarMatcher.js
deleted file mode 100644
index 6235f83..0000000
--- a/client/matching/matchers/localVarMatcher.js
+++ /dev/null
@@ -1,23 +0,0 @@
-const matchType = require("../matchType");
-const { reference, declaration } = require("../../utils/matchUtils");
-
-/**
- * Looks for matches of local variables
- */
-function matchLocalVar(context) {
- if (context.prevChar === '$') {
- let prevWord = context.prevWord;
- if (!prevWord) {
- return reference(matchType.LOCAL_VAR);
- }
- prevWord = prevWord.value;
- if (prevWord.startsWith("def_")) {
- prevWord = prevWord.substr(4);
- }
- const defKeyword = "\\b(int|string|boolean|seq|locshape|component|idk|midi|npc_mode|namedobj|synth|stat|npc_stat|fontmetrics|enum|loc|model|npc|obj|player_uid|spotanim|npc_uid|inv|category|struct|dbrow|interface|dbtable|coord|mesanim|param|queue|weakqueue|timer|softtimer|char|dbcolumn|proc|label)\\b";
- const match = prevWord.match(new RegExp(defKeyword));
- return !match ? reference(matchType.LOCAL_VAR) : declaration(matchType.LOCAL_VAR);
- }
-}
-
-module.exports = matchLocalVar;
diff --git a/client/matching/matchers/packMatcher.js b/client/matching/matchers/packMatcher.js
deleted file mode 100644
index 5642a78..0000000
--- a/client/matching/matchers/packMatcher.js
+++ /dev/null
@@ -1,25 +0,0 @@
-const { reference } = require("../../utils/matchUtils");
-const dataTypeToMatchId = require("../../resource/dataTypeToMatchId");
-const matchType = require("../matchType");
-
-/**
- * Looks for matches in pack files
- */
-function packMatcher(context) {
- if (context.file.type === 'pack' && context.word.index === 1) {
- let match;
- if (matchType.GLOBAL_VAR.fileTypes.includes(context.file.name)) {
- match = matchType.GLOBAL_VAR;
- } else if(context.file.name === 'interface' && context.word.value.includes(':')) {
- match = matchType.COMPONENT;
- } else {
- match = matchType[dataTypeToMatchId(context.file.name)];
- }
- if (match.id !== matchType.UNKNOWN.id) {
- context.packId = context.words[0].value;
- }
- return reference(match);
- }
-}
-
-module.exports = packMatcher;
diff --git a/client/matching/matchers/parametersMatcher.js b/client/matching/matchers/parametersMatcher.js
deleted file mode 100644
index 348be36..0000000
--- a/client/matching/matchers/parametersMatcher.js
+++ /dev/null
@@ -1,155 +0,0 @@
-const matchType = require('../matchType');
-const identifierCache = require('../../cache/identifierCache');
-const returnBlockLinesCache = require('../../cache/returnBlockLinesCache');
-const { getWordAtIndex, reference } = require('../../utils/matchUtils');
-
-/**
- * Looks for matches of values inside of parenthesis
- * This includes return statement params, engine command parameters, proc parameters, label parameters, and queue parameters
- */
-function parametersMatcher(context) {
- if (context.file.type !== 'rs2') {
- return null;
- }
- const paramsIdentifier = getParamsMatch(context);
- return (paramsIdentifier) ? paramsIdentifier.match : null;
-}
-
-// Checks if the index location of a line of code is within the parenthesis of an identifier
-// If it is, it returns which param index the cursor is at, the match type of that param, and the parent identifier itself
-function getParamsMatch(context) {
- const { identifierName, paramIndex } = parseForIdentifierNameAndParamIndex(context.line.text, context.lineIndex, context.words);
- if (!identifierName) {
- return null;
- }
- const name = identifierName.value;
- const prev = context.line.text.charAt(identifierName.start - 1);
-
- if (name === 'return') {
- const blockIdentifierKey = returnBlockLinesCache.get(context.line.number, context.uri);
- if (blockIdentifierKey) {
- const iden = identifierCache.getByKey(blockIdentifierKey);
- if (iden && iden.signature && iden.signature.returns.length > paramIndex) {
- return {identifier: iden, index: paramIndex, match: reference(matchType[iden.signature.returns[paramIndex]]), isReturns: true};
- }
- }
- return null;
- }
-
- let iden;
- let indexOffset = 0;
- let dynamicCommand;
- if (name === 'queue') {
- indexOffset = 2;
- if (paramIndex < indexOffset) {
- iden = identifierCache.get(name, matchType.COMMAND);
- indexOffset = 0;
- } else {
- const queueName = getWordAtIndex(context.words, identifierName.end + 2);
- iden = (queueName) ? identifierCache.get(queueName.value, matchType.QUEUE) : null;
- dynamicCommand = name;
- }
- } else if (name === 'longqueue') {
- indexOffset = 3;
- if (paramIndex < indexOffset) {
- iden = identifierCache.get(name, matchType.COMMAND);
- indexOffset = 0;
- } else {
- const queueName = getWordAtIndex(context.words, identifierName.end + 2);
- iden = (queueName) ? identifierCache.get(queueName.value, matchType.QUEUE) : null;
- dynamicCommand = name;
- }
- } else if (prev === '@') {
- iden = identifierCache.get(name, matchType.LABEL);
- } else if (prev === '~') {
- iden = identifierCache.get(name, matchType.PROC);
- } else {
- iden = identifierCache.get(name, matchType.COMMAND);
- }
- if (!iden) {
- return null;
- }
- const response = {identifier: iden, index: paramIndex, isReturns: false, dynamicCommand: dynamicCommand};
- if (iden.signature && iden.signature.params.length > (paramIndex - indexOffset)) {
- response.match = reference(matchType[iden.signature.params[(paramIndex - indexOffset)].matchTypeId]);
- }
- return response;
-}
-
-// Determines if we are inside of an identifiers parenthesis, and returns which param index it is if we are
-// Scans the characters from the cursor index to the begining of the code
-function parseForIdentifierNameAndParamIndex(lineText, index, words) {
- const init = initializeString(lineText, index);
- lineText = init.interpolatedText || lineText;
- let isInString = init.isInString;
- let isInInterpolated = 0;
- let isInParams = 0;
- let paramIndex = 0;
- for (let i = index; i >= 0; i--) {
- const char = lineText.charAt(i);
-
- // Handle interpolated code inside of strings, and nested interpolated code
- if (char === '>') isInInterpolated++;
- if (isInInterpolated > 0) {
- if (char === '<') isInInterpolated--;
- continue;
- }
-
- // Handle strings and escaped quotes within strings
- if (isInString) {
- if (char === '"' && i > 0 && lineText.charAt(i - 1) !== '\\') isInString = false;
- continue;
- }
- else if (char === '"' && i > 0 && lineText.charAt(i - 1) !== '\\') {
- isInString = true;
- continue;
- }
-
- // Handle nested parenthesis
- if (char === ')') isInParams++;
- if (isInParams > 0) {
- if (char === '(') isInParams--;
- continue;
- }
-
- // === Code below is only reached when not inside a string, interpolated code, or nested params ===
-
- // Increase param index when a comma is found
- if (char === ',') {
- paramIndex++;
- }
- // Reached the end of interpolated code without finding a match, exit early with null response
- if (char === '<') {
- return {identifierName: null, paramIndex: null};
- }
- // Found an opening parenthesis which marks the end of our search, return the previous word (the identifier name)
- if (char === '(') {
- return {identifierName: getWordAtIndex(words, i - 2), paramIndex: paramIndex};
- }
- }
- return {identifierName: null, paramIndex: null};
-}
-
-// Determines if we are currently inside of a string, and if we are inside of interpolated code
-// Scans the characters from the cursor index to the end of the line of code
-function initializeString(lineText, index) {
- let quoteCount = 0;
- let interpolatedCount = 0;
- for (let i = index; i < lineText.length; i++) {
- if (lineText.charAt(i) === '"' && i > 0 && lineText.charAt(i - 1) !== '\\') {
- quoteCount++;
- }
- if (lineText.charAt(i) === '>') {
- if (interpolatedCount === 0) {
- return { interpolatedText: lineText.substring(0, i - 1), isInString: quoteCount % 2 === 1 };
- }
- interpolatedCount--;
- }
- if (lineText.charAt(i) === '<') {
- interpolatedCount++;
- }
- }
- return { isInString: quoteCount % 2 === 1 };
-}
-
-module.exports = { parametersMatcher, getParamsMatch };
diff --git a/client/matching/matchers/prevCharMatcher.js b/client/matching/matchers/prevCharMatcher.js
deleted file mode 100644
index 5f36424..0000000
--- a/client/matching/matchers/prevCharMatcher.js
+++ /dev/null
@@ -1,17 +0,0 @@
-const matchType = require("../matchType");
-const { reference, declaration } = require("../../utils/matchUtils");
-
-/**
- * Looks for matches based on the previous character, such as ~WORD indicates a proc reference
- */
-function prevCharMatcher(context) {
- switch (context.prevChar) {
- case '^': return (context.file.type === "constant") ? declaration(matchType.CONSTANT) : reference(matchType.CONSTANT);
- case '%': return reference(matchType.GLOBAL_VAR);
- case '@': return (context.nextChar === '@') ? null : reference(matchType.LABEL);
- case '~': return reference(matchType.PROC);
- case ',': return (context.prevWord.value === "p") ? reference(matchType.MESANIM) : null;
- }
-}
-
-module.exports = prevCharMatcher;
diff --git a/client/matching/matchers/regexWordMatcher.js b/client/matching/matchers/regexWordMatcher.js
deleted file mode 100644
index 6a65534..0000000
--- a/client/matching/matchers/regexWordMatcher.js
+++ /dev/null
@@ -1,21 +0,0 @@
-const { COLOR, COORD, NUMBER } = require("../../enum/regex");
-const matchType = require("../matchType");
-const { reference } = require("../../utils/matchUtils");
-
-/**
- * Looks for matches with direct word regex checks, such as for coordinates
- */
-function regexWordMatcher(context) {
- const word = context.word.value;
- if (COORD.test(word)) {
- return reference(matchType.COORDINATES);
- }
- if (COLOR.test(word)) {
- return reference(matchType.COLOR);
- }
- if (NUMBER.test(word)) {
- return reference(matchType.NUMBER);
- }
-}
-
-module.exports = regexWordMatcher;
diff --git a/client/matching/matchers/switchCaseMatcher.js b/client/matching/matchers/switchCaseMatcher.js
deleted file mode 100644
index 2b456c3..0000000
--- a/client/matching/matchers/switchCaseMatcher.js
+++ /dev/null
@@ -1,17 +0,0 @@
-const { SWITCH_CASE } = require("../../enum/regex");
-const matchType = require("../matchType");
-const { reference } = require("../../utils/matchUtils");
-const switchStmtLinesCache = require("../../cache/switchStmtLinesCache");
-
-/**
- * Looks for matches in case statements
- */
-function switchCaseMatcher(context) {
- if (context.file.type === 'rs2' && context.word.index > 0 && context.word.value !== 'default' &&
- SWITCH_CASE.test(context.line.text) && context.lineIndex < context.line.text.indexOf(' :')) {
- const matchTypeId = switchStmtLinesCache.get(context.line.number, context.uri);
- return matchTypeId ? reference(matchType[matchTypeId]) : matchType.UNKNOWN;
- }
-}
-
-module.exports = switchCaseMatcher;
diff --git a/client/matching/matchers/triggerMatcher.js b/client/matching/matchers/triggerMatcher.js
deleted file mode 100644
index 769338d..0000000
--- a/client/matching/matchers/triggerMatcher.js
+++ /dev/null
@@ -1,27 +0,0 @@
-const { TRIGGER_LINE } = require("../../enum/regex");
-const matchType = require("../matchType");
-const triggers = require("../../resource/triggers");
-const { reference, declaration } = require("../../utils/matchUtils");
-
-/**
- * Looks for matches with known runescript triggers, see triggers.js
- */
-function triggerMatcher(context) {
- if (context.file.type !== 'rs2') {
- return null;
- }
- if (TRIGGER_LINE.test(context.line.text) && context.word.index <= 1) {
- const trigger = triggers[context.words[0].value.toLowerCase()];
- if (trigger) {
- if (context.word.index === 0) {
- return reference(matchType.TRIGGER, {triggerName: context.words[1].value});
- }
- if (context.word.value.charAt(0) === '_') {
- return reference(matchType.CATEGORY, {matchId: trigger.match.id, categoryName: context.word.value.substring(1)});
- }
- return trigger.declaration ? declaration(trigger.match) : reference(trigger.match);
- }
- }
-}
-
-module.exports = triggerMatcher;
diff --git a/client/provider/color24Provider.js b/client/provider/color24Provider.js
deleted file mode 100644
index 8b80bbc..0000000
--- a/client/provider/color24Provider.js
+++ /dev/null
@@ -1,41 +0,0 @@
-const vscode = require('vscode');
-const { COLOR24 } = require('../enum/regex');
-
-const color24Provider = {
- provideColorPresentations(color, context, token) {
- const r = Math.round(color.red * 255);
- const g = Math.round(color.green * 255);
- const b = Math.round(color.blue * 255);
- const rgb = (r << 16) | (g << 8) | b;
-
- return [
- {
- label: 'Color Picker',
- textEdit: new vscode.TextEdit(context.range, '0x' + rgb.toString(16).toUpperCase().padStart(6, '0'))
- }
- ];
- },
-
- provideDocumentColors(document) {
- const text = document.getText();
- let match;
-
- const matches = [];
- while (match = COLOR24.exec(text)) {
- const rgb = parseInt(match[2], 16);
-
- const r = (rgb >> 16) & 0xFF;
- const g = (rgb >> 8) & 0xFF;
- const b = rgb & 0xFF;
-
- matches.push({
- color: new vscode.Color(r / 255, g / 255, b / 255, 1),
- range: new vscode.Range(document.positionAt(match.index + match[1].length + 1), document.positionAt(match.index + match[1].length + match[2].length + 1))
- });
- }
-
- return matches;
- }
-};
-
-module.exports = color24Provider;
diff --git a/client/provider/completionProvider.js b/client/provider/completionProvider.js
deleted file mode 100644
index 439b0d2..0000000
--- a/client/provider/completionProvider.js
+++ /dev/null
@@ -1,132 +0,0 @@
-const vscode = require('vscode');
-const activeFileCache = require('../cache/activeFileCache');
-const completionCache = require('../cache/completionCache');
-const matchType = require('../matching/matchType');
-const { matchWord } = require('../matching/matchWord');
-const activeCursorCache = require('../cache/activeCursorCache');
-const runescriptTrigger = require('../resource/triggers');
-
-const triggers = ['$', '^', '%', '~', '@', '`', '>'];
-const autoTriggeredTypeIds = [
- matchType.CONSTANT.id,
- matchType.GLOBAL_VAR.id,
- matchType.LOCAL_VAR.id,
- matchType.PROC.id,
- matchType.LABEL.id
-];
-
-const provider = {
- provideCompletionItems(document, position, cancellationToken, context) {
- if (context.triggerKind === 1) {
- if (context.triggerCharacter === '`' && position.character > 1 &&
- document.lineAt(position.line).text.charAt(position.character - 2) === '`') {
- return searchForMatchType(document, position, true);
- }
- return invoke(document, position, position.character - 1, '');
- }
- const wordRange = document.getWordRangeAtPosition(position);
- const word = (!wordRange) ? '' : document.getText(wordRange);
- const triggerIndex = (!wordRange) ? position.character - 1 : wordRange.start.character - 1;
- return invoke(document, position, triggerIndex, word);
- }
-}
-
-function invoke(document, position, triggerIndex, word) {
- switch (document.lineAt(position.line).text.charAt(triggerIndex)) {
- case '$': return completeLocalVar(position);
- case '`': return completionTypeSelector(position);
- case '>': return completionByType(document, position, triggerIndex, word);
- case '^': return completionByTrigger(word, matchType.CONSTANT.id);
- case '%': return completionByTrigger(word, matchType.GLOBAL_VAR.id);
- case '~': return completionByTrigger(word, matchType.PROC.id);
- case '@': return completionByTrigger(word, matchType.LABEL.id);
- default: return searchForMatchType(document, position);
- }
-}
-
-function completeLocalVar(position) {
- const completionItems = [];
- const completionKind = getCompletionItemKind(matchType.LOCAL_VAR.id);
- const scriptData = activeFileCache.getScriptData(position.line);
- if (scriptData) {
- Object.keys(scriptData.variables).forEach(varName => {
- const localVar = scriptData.variables[varName];
- const range = localVar.declaration.range;
- if (position.line > range.start.line || (position.line === range.start.line && position.character > range.end.character)) {
- const item = new vscode.CompletionItem(varName, completionKind);
- item.range = new vscode.Range(position.translate(0, -1), position);
- item.detail = localVar.parameter ? `${localVar.type} (param)` : localVar.type;
- completionItems.push(item);
- }
- });
- }
- return completionItems;
-}
-
-function completionByTrigger(prefix, matchTypeId, additionalTextEdits) {
- let identifierNames;
- if (matchTypeId === matchType.TRIGGER.id) {
- identifierNames = Object.keys(runescriptTrigger);
- } else {
- identifierNames = completionCache.getAllWithPrefix(prefix, matchTypeId);
- }
- if (!identifierNames) {
- return null;
- }
- const completionKind = getCompletionItemKind(matchTypeId);
- const completionItems = [];
- identifierNames.forEach(identifierName => {
- const item = new vscode.CompletionItem(identifierName, completionKind);
- item.detail = matchTypeId.toLowerCase();
- if (additionalTextEdits) item.additionalTextEdits = additionalTextEdits;
- completionItems.push(item);
- });
- return completionItems;
-}
-
-function completionTypeSelector(position) {
- const completionItems = completionCache.getTypes().filter(type => !autoTriggeredTypeIds.includes(type)).map(type => {
- const item = new vscode.CompletionItem(`${type}>`, vscode.CompletionItemKind.Enum);
- item.additionalTextEdits = [vscode.TextEdit.delete(new vscode.Range(position.translate(0, -1), position))];
- item.command = { command: 'editor.action.triggerSuggest' };
- return item;
- });
- return completionItems;
-}
-
-function completionByType(document, position, triggerIndex, word) {
- prevWordRange = document.getWordRangeAtPosition(new vscode.Position(position.line, triggerIndex));
- if (!prevWordRange) {
- return null;
- }
- const matchTypeId = document.getText(prevWordRange);
- const additionalTextEdits = [vscode.TextEdit.delete(new vscode.Range(prevWordRange.start, prevWordRange.end.translate(0, 1)))];
- return completionByTrigger(word, matchTypeId, additionalTextEdits);
-}
-
-function searchForMatchType(document, position, fromTrigger = false) {
- const triggerOffset = fromTrigger ? 2 : 0;
- let matchTypeId = fromTrigger ? false : activeCursorCache.get(document, position);
- if (!matchTypeId) {
- let str = document.lineAt(position.line).text;
- str = str.substring(0, position.character - triggerOffset) + 'temp' + str.substring(position.character);
- const match = matchWord(str, position.line, document.uri, position.character);
- matchTypeId = (match) ? match.match.id : matchType.COMMAND.id;
- }
- const additionalTextEdits = [vscode.TextEdit.delete(new vscode.Range(position.translate(0, -triggerOffset), position))];
- return completionByTrigger('', matchTypeId, additionalTextEdits);
-}
-
-function getCompletionItemKind(matchTypeId) {
- switch (matchTypeId) {
- case matchType.CONSTANT.id: return vscode.CompletionItemKind.Constant;
- case matchType.LOCAL_VAR.id:
- case matchType.GLOBAL_VAR.id: return vscode.CompletionItemKind.Variable;
- case matchType.COMMAND.id:
- case matchType.PROC.id:
- case matchType.LABEL.id: return vscode.CompletionItemKind.Function;
- default: return vscode.CompletionItemKind.Text;
- }
-}
-
-module.exports = {triggers, provider};
diff --git a/client/provider/configHelpProvider.js b/client/provider/configHelpProvider.js
deleted file mode 100644
index 0c1a5c9..0000000
--- a/client/provider/configHelpProvider.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const vscode = require('vscode');
-const { getBaseContext, getWordAtIndex } = require('../utils/matchUtils');
-const { getConfigLineMatch } = require('../matching/matchers/configMatcher');
-const dataTypeToMatchId = require('../resource/dataTypeToMatchId');
-const { contains } = require('../cache/completionCache');
-const matchType = require('../matching/matchType');
-const activeCursorCache = require('../cache/activeCursorCache');
-
-const metadata = {
- triggerCharacters: ['=', ','],
- retriggerCharacters: [',']
-}
-
-const provider = {
- provideSignatureHelp(document, position) {
- let str = document.lineAt(position.line).text;
- str = str.substring(0, position.character) + 'temp' + str.substring(position.character);
- const matchContext = getBaseContext(str, position.line, document.uri);
- matchContext.lineIndex = position.character + 1;
- matchContext.word = getWordAtIndex(matchContext.words, matchContext.lineIndex);
- const config = getConfigLineMatch(matchContext);
- if (!config) {
- return null;
- }
-
- // Build the signature info
- const signatureInfo = new vscode.SignatureInformation(`${config.key}=${config.params.join(',')}`);
- let index = config.key.length + 1; // Starting index of params
- config.params.forEach(param => {
- // use range instead of param name due to possible duplicates
- signatureInfo.parameters.push(new vscode.ParameterInformation([index, index + param.length]));
- index += param.length + 1;
- });
- signatureInfo.activeParameter = config.index;
-
- // Build the signature help
- const signatureHelp = new vscode.SignatureHelp();
- signatureHelp.signatures.push(signatureInfo);
- signatureHelp.activeSignature = 0;
- invokeCompletionItems(dataTypeToMatchId(config.params[config.index]), document, position);
- return signatureHelp;
- }
-}
-
-function invokeCompletionItems(matchTypeId, document, position) {
- activeCursorCache.set(matchTypeId, document, position);
- if (matchTypeId !== matchType.UNKNOWN.id) {
- const word = document.getText(document.getWordRangeAtPosition(position));
- if (contains(word, matchTypeId)) {
- return;
- }
- vscode.commands.executeCommand('editor.action.triggerSuggest');
- }
-}
-
-module.exports = { provider, metadata };
diff --git a/client/provider/gotoDefinition.js b/client/provider/gotoDefinition.js
deleted file mode 100644
index 4b243ae..0000000
--- a/client/provider/gotoDefinition.js
+++ /dev/null
@@ -1,39 +0,0 @@
-const vscode = require('vscode');
-const identifierCache = require("../cache/identifierCache");
-const matchType = require('../matching/matchType');
-const { matchWordFromDocument } = require('../matching/matchWord');
-const activeFileCache = require('../cache/activeFileCache');
-
-const gotoDefinitionProvider = {
- async provideDefinition(document, position) {
- // Get a match for the current word, and ignore noop or hover only tagged matches
- const { match, word } = matchWordFromDocument(document, position)
- if (!match || match.noop || match.isHoverOnly) {
- return null;
- }
-
- // If we are already on a declaration, there is nowhere to goto. Returning current location
- // indicates to vscode that we instead want to try doing "find references"
- if (match.declaration || match.referenceOnly) {
- return new vscode.Location(document.uri, position);
- }
-
- // Search for the identifier and its declaration location, and goto it if found
- if (match.id === matchType.LOCAL_VAR.id) {
- return gotoLocalVar(position, word);
- }
- return gotoDefinition(word, match);
- }
-}
-
-const gotoLocalVar = (position, word) => {
- const scriptData = activeFileCache.getScriptData(position.line);
- return (scriptData) ? (scriptData.variables[`$${word}`] || {declaration: null}).declaration : null;
-}
-
-const gotoDefinition = async (word, match) => {
- const definition = identifierCache.get(word, match);
- return (definition) ? definition.declaration : null;
-}
-
-module.exports = gotoDefinitionProvider;
diff --git a/client/provider/hoverProvider.js b/client/provider/hoverProvider.js
deleted file mode 100644
index eeaf2a7..0000000
--- a/client/provider/hoverProvider.js
+++ /dev/null
@@ -1,80 +0,0 @@
-const vscode = require('vscode');
-const matchType = require('../matching/matchType');
-const identifierCache = require('../cache/identifierCache');
-const activeFileCache = require('../cache/activeFileCache');
-const identifierFactory = require('../resource/identifierFactory');
-const { matchWordFromDocument } = require('../matching/matchWord');
-const { resolve } = require('../resource/hoverConfigResolver');
-const { DECLARATION_HOVER_ITEMS, REFERENCE_HOVER_ITEMS } = require('../enum/hoverConfigOptions');
-const { markdownBase, appendTitle, appendInfo, appendValue, appendSignature,
- appendCodeBlock, expectedIdentifierMessage } = require('../utils/markdownUtils');
-
-const hoverProvider = function(context) {
- return {
- async provideHover(document, position) {
- // Find a match for the word user is hovering over, and ignore noop tagged matches
- const { word, match, context: matchContext } = matchWordFromDocument(document, position);
- if (!match || match.noop) {
- return null;
- }
-
- // Setup the hover text markdown content object
- const markdown = markdownBase(context);
-
- // Local vars are handled differently than the rest
- if (match.id === matchType.LOCAL_VAR.id) {
- appendLocalVarHoverText(position, word, match, markdown);
- return new vscode.Hover(markdown);
- }
-
- // If no config found, or no items to display then exit early
- const hoverDisplayItems = (match.declaration) ? resolve(DECLARATION_HOVER_ITEMS, match) : resolve(REFERENCE_HOVER_ITEMS, match);
- if (hoverDisplayItems.length === 0) {
- return null;
- }
-
- // Get/Build identifier object for the match found
- const identifier = getIdentifier(word, match, document, position);
-
- // No identifier or hideDisplay property is set, then there is nothing to display
- if (!identifier || identifier.hideDisplay) {
- return null;
- }
-
- // Match type is a reference, but it has no declaration => display a warning message "expected identifier"
- if (!match.declaration && !match.referenceOnly && !identifier.declaration) {
- expectedIdentifierMessage(word, match, markdown);
- return new vscode.Hover(markdown);
- }
-
- // Append the registered hoverDisplayItems defined in the matchType for the identifier
- appendTitle(identifier.name, identifier.fileType, identifier.matchId, markdown, identifier.id, matchContext.cert);
- appendInfo(identifier, hoverDisplayItems, markdown);
- appendValue(identifier, hoverDisplayItems, markdown);
- appendSignature(identifier, hoverDisplayItems, markdown);
- appendCodeBlock(identifier, hoverDisplayItems, markdown);
- return new vscode.Hover(markdown);
- }
- };
-}
-
-function appendLocalVarHoverText(position, word, match, markdown) {
- const scriptData = activeFileCache.getScriptData(position.line);
- if (scriptData) {
- const variable = scriptData.variables[`$${word}`];
- if (variable) {
- appendTitle(word, 'rs2', match.id, markdown);
- markdown.appendCodeblock(variable.parameter ? `${variable.type} $${word} (script parameter)` : `${variable.type} $${word}`, 'runescript');
- } else {
- expectedIdentifierMessage(word, match, markdown);
- }
- }
-}
-
-function getIdentifier(word, match, document, position) {
- return (match.hoverOnly) ?
- identifierFactory.build(word, match, new vscode.Location(document.uri, position)) :
- identifierCache.get(word, match, match.declaration ? document.uri : null);
-}
-
-module.exports = hoverProvider;
diff --git a/client/provider/recolorProvider.js b/client/provider/recolorProvider.js
deleted file mode 100644
index 916e543..0000000
--- a/client/provider/recolorProvider.js
+++ /dev/null
@@ -1,41 +0,0 @@
-const vscode = require('vscode');
-const { RECOLOR } = require('../enum/regex');
-
-const recolProvider = {
- provideColorPresentations(color, context, token) {
- const r = Math.round(color.red * 31);
- const g = Math.round(color.green * 31);
- const b = Math.round(color.blue * 31);
- const rgb = (r << 10) | (g << 5) | b;
-
- return [
- {
- label: 'Model Recolor',
- textEdit: new vscode.TextEdit(context.range, rgb.toString())
- }
- ];
- },
-
- provideDocumentColors(document) {
- const text = document.getText();
- let match;
-
- const matches = [];
- while (match = RECOLOR.exec(text)) {
- const rgb = parseInt(match[2]);
-
- const r = (rgb >> 10) & 0x1f;
- const g = (rgb >> 5) & 0x1f;
- const b = rgb & 0x1f;
-
- matches.push({
- color: new vscode.Color(r / 31, g / 31, b / 31, 1),
- range: new vscode.Range(document.positionAt(match.index + match[1].length + 1), document.positionAt(match.index + match[1].length + match[2].length + 1))
- });
- }
-
- return matches;
- }
-};
-
-module.exports = recolProvider;
diff --git a/client/provider/referenceProvider.js b/client/provider/referenceProvider.js
deleted file mode 100644
index 0a4eea0..0000000
--- a/client/provider/referenceProvider.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const vscode = require('vscode');
-const { matchWordFromDocument } = require('../matching/matchWord');
-const identifierCache = require('../cache/identifierCache');
-const cacheUtils = require('../utils/cacheUtils');
-const matchType = require('../matching/matchType');
-const activeFileCache = require('../cache/activeFileCache');
-
-const referenceProvider = {
- async provideReferences(document, position) {
- // Find a match for the current word, and ignore noop or hoverOnly tagged matches
- const { match, word } = matchWordFromDocument(document, position)
- if (!match || match.noop || match.isHoverOnly) {
- return null;
- }
-
- // Use activeFileCache to get references of variables for active script block
- if (match.id === matchType.LOCAL_VAR.id) {
- const scriptData = activeFileCache.getScriptData(position.line);
- if (scriptData) {
- return (scriptData.variables[`$${word}`] || {references: []}).references;
- }
- return null;
- }
-
- // Get the identifier from the cache
- const identifier = identifierCache.get(word, match);
- if (!identifier || !identifier.references) {
- return null;
- }
-
- // Decode all the references for the identifier into an array of vscode Location objects
- const referenceLocations = [];
- Object.keys(identifier.references).forEach(fileKey => {
- const uri = vscode.Uri.file(fileKey);
- identifier.references[fileKey].forEach(encodedReference =>
- referenceLocations.push(cacheUtils.decodeReferenceToLocation(uri, encodedReference)));
- });
- // If there is only one reference and its the declaration, return null as theres no other references to show
- if (match.declaration && referenceLocations.length === 1) {
- return null;
- }
- return referenceLocations;
- }
-}
-
-module.exports = referenceProvider;
diff --git a/client/provider/renameProvider.js b/client/provider/renameProvider.js
deleted file mode 100644
index bb82877..0000000
--- a/client/provider/renameProvider.js
+++ /dev/null
@@ -1,111 +0,0 @@
-const vscode = require('vscode');
-const identifierCache = require('../cache/identifierCache');
-const { matchWordFromDocument } = require('../matching/matchWord');
-const cacheUtils = require('../utils/cacheUtils');
-const matchType = require('../matching/matchType');
-const activeFileCache = require('../cache/activeFileCache');
-const { LOC_MODEL } = require('../enum/regex');
-
-const renameProvider = {
- prepareRename(document, position) {
- const matchedWord = matchWordFromDocument(document, position);
- if (!matchedWord) {
- throw new Error("Cannot rename");
- }
- const { match, word } = matchedWord;
- if (!match.allowRename || match.noop) {
- throw new Error(`${match.id} renaming not supported`);
- }
- if (match.id !== matchType.LOCAL_VAR.id) {
- const identifier = identifierCache.get(word, match);
- if (!identifier) {
- return new Error('Cannot find any references to rename');
- }
- }
- },
-
- provideRenameEdits(document, position, newName) {
- const { word, match, context } = matchWordFromDocument(document, position);
-
- if (match.id === matchType.LOCAL_VAR.id) {
- return renameLocalVariableReferences(position, word, newName);
- }
-
- const validationError = validate(match, newName);
- if (validationError) {
- vscode.window.showErrorMessage('Rename failed: ' + validationError);
- return new vscode.WorkspaceEdit();
- }
-
- const adjustedNewName = adjustNewName(context, newName);
- const identifier = identifierCache.get(word, match);
- renameFiles(match, word, adjustedNewName);
- return renameReferences(identifier, word, adjustedNewName);
- }
-}
-
-// Use activeFileCache to get references of variables for active script block
-function renameLocalVariableReferences(position, word, newName) {
- const renameWorkspaceEdits = new vscode.WorkspaceEdit();
- const scriptData = activeFileCache.getScriptData(position.line);
- if (scriptData) {
- (scriptData.variables[`$${word}`] || { references: [] }).references.forEach(location => {
- renameWorkspaceEdits.replace(location.uri, location.range, `$${newName}`);
- });
- }
- return renameWorkspaceEdits;
-}
-
-function validate(match, newName) {
- if (match.id === matchType.MODEL.id && LOC_MODEL.test(newName)) {
- return `Do not include final _X suffix in model renames`;
- }
-}
-
-// Decode all the references for the identifier into an array of vscode ranges,
-// then use that to rename all of the references to the newName
-function renameReferences(identifier, oldName, newName) {
- const renameWorkspaceEdits = new vscode.WorkspaceEdit();
- if (identifier.references) {
- const wordLength = oldName.length - oldName.indexOf(':') - 1;
- Object.keys(identifier.references).forEach(fileKey => {
- const uri = vscode.Uri.file(fileKey);
- identifier.references[fileKey].forEach(encodedReference => {
- const range = cacheUtils.decodeReferenceToRange(wordLength, encodedReference);
- renameWorkspaceEdits.replace(uri, range, newName);
- });
- });
- }
- return renameWorkspaceEdits;
-}
-
-function adjustNewName(context, newName) {
- // Strip the cert_ and the _ prefix on objs or categories
- if (context.originalPrefix && newName.startsWith(context.originalPrefix)) {
- newName = newName.substring(context.originalPrefix.length);
- }
- // Strip the _0 (or others) on loc models
- if (context.originalSuffix && newName.endsWith(context.originalSuffix)) {
- newName = newName.slice(0, -2);
- }
- // Strip the left side of identifier names with colons in them
- if (newName.indexOf(':') > -1) {
- newName = newName.substring(newName.indexOf(':') + 1);
- }
- return newName;
-}
-
-async function renameFiles(match, oldName, newName) {
- if (match.renameFile && Array.isArray(match.fileTypes) && match.fileTypes.length > 0) {
- const fileSearch = match.id === matchType.MODEL.id ? `**/${oldName}_*.${match.fileTypes[0]}` : `**/${oldName}.${match.fileTypes[0]}`;
- const files = await vscode.workspace.findFiles(fileSearch) || [];
- for (const oldUri of files) {
- const suffix = match.id === matchType.MODEL.id ? oldUri.path.slice(-6, -4) : '';
- const newFileName = suffix ? `${newName}${suffix}.${match.fileTypes[0]}` : `${newName}.${match.fileTypes[0]}`;
- const newUri = vscode.Uri.joinPath(oldUri.with({ path: oldUri.path.replace(/\/[^/]+$/, '') }), newFileName);
- vscode.workspace.fs.rename(oldUri, newUri);
- }
- }
-}
-
-module.exports = renameProvider;
diff --git a/client/provider/signatureHelpProvider.js b/client/provider/signatureHelpProvider.js
deleted file mode 100644
index 4667323..0000000
--- a/client/provider/signatureHelpProvider.js
+++ /dev/null
@@ -1,134 +0,0 @@
-const vscode = require('vscode');
-const { getBaseContext } = require('../utils/matchUtils');
-const matchType = require('../matching/matchType');
-const { get } = require('../cache/identifierCache');
-const activeCursorCache = require('../cache/activeCursorCache');
-const dataTypeToMatchId = require('../resource/dataTypeToMatchId');
-const runescriptTrigger = require('../resource/triggers');
-const { contains } = require('../cache/completionCache');
-const { getParamsMatch } = require('../matching/matchers/parametersMatcher');
-
-const metadata = {
- triggerCharacters: ['(', ',', '['],
- retriggerCharacters: [',']
-}
-
-const provider = {
- provideSignatureHelp(document, position) {
- const signatureHelp = getScriptTriggerHelp(document, position);
- if (signatureHelp) {
- return signatureHelp;
- }
- return getParametersHelp(document, position);
- }
-}
-
-function getScriptTriggerHelp(document, position) {
- let matchTypeId = matchType.UNKNOWN.id;
- let signatureInfo;
- const str = document.lineAt(position.line).text;
- if (str.charAt(0) === '[') {
- if (position.character > str.indexOf(']')) {
- return null;
- }
- const split = str.split(',');
- if (split.length > 1) {
- const triggerName = split[0].substring(1);
- const trigger = runescriptTrigger[triggerName];
- if (trigger) {
- matchTypeId = trigger.declaration ? matchType.UNKNOWN.id : trigger.match.id;
- const matchLabel = matchTypeId === matchType.UNKNOWN.id ? `script_name` : matchTypeId.toLowerCase();
- signatureInfo = new vscode.SignatureInformation(`script [${triggerName},${matchLabel}]`);
- signatureInfo.parameters.push(new vscode.ParameterInformation(triggerName));
- signatureInfo.parameters.push(new vscode.ParameterInformation(matchLabel));
- signatureInfo.activeParameter = 1;
- }
- } else {
- matchTypeId = matchType.TRIGGER.id;
- signatureInfo = new vscode.SignatureInformation('script [trigger,value]');
- signatureInfo.parameters.push(new vscode.ParameterInformation('trigger'));
- signatureInfo.parameters.push(new vscode.ParameterInformation('value'));
- signatureInfo.activeParameter = 0;
- }
- }
- if (signatureInfo) {
- const signatureHelp = new vscode.SignatureHelp();
- signatureHelp.signatures.push(signatureInfo);
- signatureHelp.activeSignature = 0;
- invokeCompletionItems(matchTypeId, document, position);
- return signatureHelp;
- }
- return null;
-}
-
-function getParametersHelp(document, position) {
- let str = document.lineAt(position.line).text;
- str = str.substring(0, position.character) + 'temp' + str.substring(position.character);
- const matchContext = getBaseContext(str, position.line, document.uri);
- matchContext.lineIndex = position.character + 1;
- var paramIden = getParamsMatch(matchContext);
- if (!paramIden) {
- return null;
- }
- if (!paramIden.isReturns && paramIden.identifier.signature.paramsText.length === 0) {
- return displayMessage(`${paramIden.identifier.matchId} ${paramIden.identifier.name} has no parameters, remove the parenthesis`);
- }
-
- // For things like queues, manually handled - todo try to find better way
- paramIden = handleDynamicParams(paramIden);
-
- // Build the signature info
- const signature = paramIden.identifier.signature;
- const params = (paramIden.isReturns) ? signature.returnsText : signature.paramsText;
- const label = (paramIden.isReturns) ? `return (${params})` : `${paramIden.identifier.name}(${params})${signature.returnsText.length > 0 ? `: ${signature.returnsText}` : ''}`;
- const signatureInfo = new vscode.SignatureInformation(label);
- params.split(',').forEach(param => signatureInfo.parameters.push(new vscode.ParameterInformation(param.trim())));
- signatureInfo.activeParameter = paramIden.index;
-
- // Build the signature help
- const signatureHelp = new vscode.SignatureHelp();
- signatureHelp.signatures.push(signatureInfo);
- signatureHelp.activeSignature = 0;
-
- // Trigger autocomplete suggestions
- invokeCompletionItems(dataTypeToMatchId(signatureInfo.parameters[paramIden.index].label.split(' ')[0]), document, position);
-
- return signatureHelp;
-}
-
-function handleDynamicParams(paramIdentifier) {
- if (paramIdentifier.dynamicCommand && paramIdentifier.identifier.signature.paramsText.length > 0) {
- const name = paramIdentifier.identifier.name;
- const command = get(paramIdentifier.dynamicCommand, matchType.COMMAND);
- if (command && command.signature.paramsText.length > 0) {
- let paramsText = `${command.signature.paramsText}, ${paramIdentifier.identifier.signature.paramsText}`;
- paramsText = `${name}${paramsText.substring(paramsText.indexOf(','))}`;
- return {index: paramIdentifier.index, identifier: {name: paramIdentifier.dynamicCommand, signature: {paramsText: paramsText, returnsText: ''}}};
- }
- }
- return paramIdentifier;
-}
-
-function displayMessage(message) {
- const signatureInfo = new vscode.SignatureInformation(message);
- const signatureHelp = new vscode.SignatureHelp();
- signatureHelp.signatures.push(signatureInfo);
- signatureHelp.activeSignature = 0;
- return signatureHelp;
-}
-
-function invokeCompletionItems(matchTypeId, document, position) {
- activeCursorCache.set(matchTypeId, document, position);
- if (matchTypeId !== matchType.UNKNOWN.id) {
- const word = document.getText(document.getWordRangeAtPosition(position));
- if (matchType.TRIGGER.id === matchTypeId && runescriptTrigger[word]) {
- return;
- }
- if (contains(word, matchTypeId)) {
- return;
- }
- vscode.commands.executeCommand('editor.action.triggerSuggest');
- }
-}
-
-module.exports = { provider, metadata };
diff --git a/client/provider/vscodeCommands.js b/client/provider/vscodeCommands.js
deleted file mode 100644
index ec39cb1..0000000
--- a/client/provider/vscodeCommands.js
+++ /dev/null
@@ -1,17 +0,0 @@
-const vscode = require('vscode');
-const cacheManager = require('../cache/cacheManager');
-
-const commands = {
- rebuildCache: {
- id: 'RuneScriptLanguage.rebuildCache',
- command: () => {
- vscode.window.withProgress({
- location: vscode.ProgressLocation.Notification,
- title: "Runescript Extension: Building cache / Indexing files...",
- cancellable: false
- }, cacheManager.rebuildAll);
- }
- }
-};
-
-module.exports = commands;
diff --git a/client/resource/configKeys.js b/client/resource/configKeys.js
deleted file mode 100644
index 1b7200d..0000000
--- a/client/resource/configKeys.js
+++ /dev/null
@@ -1,44 +0,0 @@
-// === STATIC CONFIG KEY MATCHES ===
-const configKeys = {
- walkanim: { params: [param('seq'), param('seq'), param('seq'), param('seq')] },
- multivar: { params: [param('var')] },
- multiloc: { params: [param('int'), param('loc')] },
- multinpc: { params: [param('int'), param('npc')] },
- basevar: { params: [param('var')] },
-
- category: { params: [param('category')] },
- huntmode: { params: [param('hunt')] },
- table: { params: [param('dbtable')] },
- column: { params: [param('dbcolumn', true)] },
-}
-
-// === REGEX CONFIG KEY MATCHES ===
-const regexConfigKeys = groupByFileType([
- { regex: /stock\d+/, params: [param('obj'), param('int'), param('int')], fileTypes: ["inv"] },
- { regex: /count\d+/, params: [param('obj'), param('int')], fileTypes: ["obj"] },
- { regex: /(model|head|womanwear|manwear|womanhead|manhead|activemodel)\d*/, params: [param('ob2')], fileTypes:['npc', 'loc', 'obj', 'spotanim', 'if', 'idk'] },
- { regex: /\w*anim\w*/, params: [param('seq')], fileTypes: ["loc", "npc", "if", "spotanim"] },
- { regex: /replaceheldleft|replaceheldright/, params: [param('obj')], fileTypes: ["seq"] },
-]);
-
-// === CONFIG KEYS THAT ARE HANDLED MANUALLY IN CONFIG_MATCHER ===
-const specialCaseKeys = ['val', 'param', 'data'];
-
-function param(type, declaration = false) {
- return {typeId: type, declaration: declaration};
-}
-
-function groupByFileType(config) {
- const result = new Map();
- for (const { regex, params, fileTypes } of config) {
- for (const fileType of fileTypes) {
- if (!result.has(fileType)) {
- result.set(fileType, []);
- }
- result.get(fileType).push({ regex, params });
- }
- }
- return result;
-}
-
-module.exports = { configKeys, regexConfigKeys, specialCaseKeys };
diff --git a/client/resource/dataTypeToMatchId.js b/client/resource/dataTypeToMatchId.js
deleted file mode 100644
index ecc8427..0000000
--- a/client/resource/dataTypeToMatchId.js
+++ /dev/null
@@ -1,15 +0,0 @@
-const matchType = require("../matching/matchType");
-
-const keywordToId = {};
-
-Object.keys(matchType).forEach(matchTypeId => {
- for (let keyword of (matchType[matchTypeId].types || [])) {
- keywordToId[keyword] = matchTypeId;
- }
-});
-
-function dataTypeToMatchId(keyword) {
- return keywordToId[keyword] || matchType.UNKNOWN.id;
-}
-
-module.exports = dataTypeToMatchId;
diff --git a/client/resource/hoverConfigResolver.js b/client/resource/hoverConfigResolver.js
deleted file mode 100644
index 02f9291..0000000
--- a/client/resource/hoverConfigResolver.js
+++ /dev/null
@@ -1,22 +0,0 @@
-const { DECLARATION_HOVER_ITEMS, REFERENCE_HOVER_ITEMS, LANGUAGE, BLOCK_SKIP_LINES, CONFIG_INCLUSIONS } = require("../enum/hoverConfigOptions");
-
-const resolve = function(opt, match) {
- const config = (!match.hoverConfig) ? {} : match.hoverConfig;
- switch(opt) {
- case DECLARATION_HOVER_ITEMS: return config[opt] || [];
- case REFERENCE_HOVER_ITEMS: return config[opt] || [];
- case LANGUAGE: return config[opt] || 'runescript';
- case BLOCK_SKIP_LINES: return (config[opt] !== undefined) ? match.hoverConfig[opt] : 1;
- case CONFIG_INCLUSIONS: return config[opt] || null;
- }
-}
-
-const resolveAllHoverItems = function(match) {
- const config = (!match.hoverConfig) ? {} : match.hoverConfig;
- const displayItems = new Set();
- (config[DECLARATION_HOVER_ITEMS] || []).forEach(item => displayItems.add(item));
- (config[REFERENCE_HOVER_ITEMS] || []).forEach(item => displayItems.add(item));
- return displayItems;
-}
-
-module.exports = { resolve, resolveAllHoverItems };
diff --git a/client/resource/identifierFactory.js b/client/resource/identifierFactory.js
deleted file mode 100644
index 929a3ad..0000000
--- a/client/resource/identifierFactory.js
+++ /dev/null
@@ -1,135 +0,0 @@
-const dataTypeToMatchId = require('./dataTypeToMatchId');
-const hoverConfigResolver = require('./hoverConfigResolver');
-const { SIGNATURE, CODEBLOCK } = require('../enum/hoverDisplayItems');
-const { END_OF_BLOCK_LINE } = require('../enum/regex');
-const { LANGUAGE, BLOCK_SKIP_LINES, CONFIG_INCLUSIONS } = require('../enum/hoverConfigOptions');
-const matchType = require('../matching/matchType');
-
-/**
- * Builds an identifier object
- * identifier = {
- * name: String,
- * matchId: matchTypeId,
- * declaration: vscode.Location
- * references: {filePath1: String[], filePath2: String[], ...} (String is encoded location value)
- * fileType: String,
- * language: String,
- * info?: String,
- * signature?: {params: {type: String, name: String, matchTypeId: String}[], returns: String, paramsText: String},
- * block?: String
- * }
- */
-function build(name, match, location, info = null, text = {lines: [], start: 0}) {
- const identifier = {
- name: name,
- match: match,
- declaration: location,
- references: {},
- fileType: location ? location.uri.fsPath.split(/[#?]/)[0].split('.').pop().trim() : 'rs2',
- language: hoverConfigResolver.resolve(LANGUAGE, match),
- text: text
- }
- if (info) identifier.info = info;
- addExtraData(identifier, match.extraData);
- process(identifier);
- cleanup(identifier);
- return identifier;
-}
-
-function buildRef(name, match) {
- const identifier = {
- name: name,
- match: match,
- references: {},
- fileType: (match.fileTypes || [])[0] || 'rs2',
- language: hoverConfigResolver.resolve(LANGUAGE, match),
- }
- if (match.referenceOnly) {
- addExtraData(identifier, match.extraData);
- process(identifier);
- }
- cleanup(identifier);
- return identifier;
-}
-
-function process(identifier) {
- // Process specififed display items
- if (identifier.text) {
- const hoverDisplayItems = hoverConfigResolver.resolveAllHoverItems(identifier.match);
- for (const hoverDisplayItem of hoverDisplayItems) {
- switch(hoverDisplayItem) {
- case SIGNATURE: processSignature(identifier); break;
- case CODEBLOCK: processCodeBlock(identifier); break;
- }
- }
- }
-
- // Execute custom post processing for the identifier's matchType (if defined)
- if (identifier.match.postProcessor) {
- identifier.match.postProcessor(identifier);
- }
-}
-
-function cleanup(identifier) {
- identifier.matchId = identifier.match.id;
- delete identifier.match;
- delete identifier.text;
-}
-
-function processSignature(identifier) {
- // Get first line of text, which should contain the data for parsing the signature
- let line = identifier.text.lines[identifier.text.start];
-
- // Parse input params
- const params = [];
- let openingIndex = line.indexOf('(');
- let closingIndex = line.indexOf(')');
- if (openingIndex >= 0 && closingIndex >= 0 && ++openingIndex !== closingIndex) {
- line.substring(openingIndex, closingIndex).split(',').forEach(param => {
- if (param.startsWith(' ')) param = param.substring(1);
- const split = param.split(' ');
- if (split.length === 2) {
- params.push({type: split[0], name: split[1], matchTypeId: dataTypeToMatchId(split[0])});
- }
- });
- }
-
- // Parse response type
- let returns = [];
- let returnsText = '';
- line = line.substring(closingIndex + 1);
- openingIndex = line.indexOf('(');
- closingIndex = line.indexOf(')');
- if (openingIndex >= 0 && closingIndex >= 0 && ++openingIndex !== closingIndex) {
- returnsText = line.substring(openingIndex, closingIndex);
- returns = line.substring(openingIndex, closingIndex).split(',').map(item => dataTypeToMatchId(item.trim()));
- }
-
- // Add signature to identifier
- const paramsText = (params.length > 0) ? params.map(param => `${param.type} ${param.name}`).join(', ') : '';
- identifier.signature = {params: params, returns: returns, paramsText: paramsText, returnsText: returnsText};
-}
-
-function processCodeBlock(identifier) {
- const lines = identifier.text.lines;
- const startIndex = identifier.text.start + hoverConfigResolver.resolve(BLOCK_SKIP_LINES, identifier.match);
- const configInclusionTags = hoverConfigResolver.resolve(CONFIG_INCLUSIONS, identifier.match);
- let blockInclusionLines = [];
- if (identifier.match.id === matchType.CONSTANT.id) blockInclusionLines.push(lines[startIndex]);
- for (let i = startIndex; i < lines.length; i++) {
- let currentLine = lines[i];
- if (END_OF_BLOCK_LINE.test(currentLine)) break;
- if (currentLine.startsWith('//')) continue;
- if (configInclusionTags && !configInclusionTags.some(inclusionTag => currentLine.startsWith(inclusionTag))) continue;
- blockInclusionLines.push(currentLine);
- }
- identifier.block = blockInclusionLines.join('\n');
-}
-
-function addExtraData(identifier, extraData) {
- if (!extraData) return;
- if (!identifier.extraData) identifier.extraData = {};
- Object.keys(extraData).forEach(key => identifier.extraData[key] = extraData[key]);
-}
-
-module.exports = { build, buildRef };
diff --git a/client/resource/postProcessors.js b/client/resource/postProcessors.js
deleted file mode 100644
index 195434e..0000000
--- a/client/resource/postProcessors.js
+++ /dev/null
@@ -1,80 +0,0 @@
-const { END_OF_LINE } = require('../enum/regex');
-const matchConfigKeyInfo = require('../info/configKeyInfo');
-const matchTriggerInfo = require('../info/triggerInfo');
-const { getLineText } = require('../utils/stringUtils');
-
-// Post processors are used for any additional post modification needed for a matchType, after an identifier has been built
-// postProcessors must be a function which takes indentifier as an input, and directly modifies that identifier as necessary
-
-const coordPostProcessor = function(identifier) {
- const coordinates = identifier.name.split('_');
- const xCoord = Number(coordinates[1] << 6) + Number(coordinates[3]);
- const zCoord = Number(coordinates[2] << 6) + Number(coordinates[4]);
- identifier.value = `Absolute coordinates: (${xCoord}, ${zCoord})`;
-}
-
-const enumPostProcessor = function(identifier) {
- const inputtypeLine = getLineText(identifier.block.substring(identifier.block.indexOf("inputtype=")));
- const outputtypeLine = getLineText(identifier.block.substring(identifier.block.indexOf("outputtype=")));
- identifier.extraData = {inputType: inputtypeLine.substring(10), outputType: outputtypeLine.substring(11)};
-}
-
-const dataTypePostProcessor = function(identifier) {
- const index = identifier.block.indexOf("type=");
- const dataType = (index < 0) ? 'int' : getLineText(identifier.block.substring(index)).substring(5);
- identifier.extraData = {dataType: dataType};
-}
-
-const configKeyPostProcessor = function(identifier) {
- const info = matchConfigKeyInfo(identifier.name, identifier.fileType);
- info ? identifier.info = info.replaceAll('$TYPE', identifier.fileType) : identifier.hideDisplay = true;
-}
-
-const triggerPostProcessor = function(identifier) {
- const info = matchTriggerInfo(identifier.name, identifier.extraData.triggerName);
- if (info) identifier.info = info;
-}
-
-const categoryPostProcessor = function(identifier) {
- const extraData = identifier.extraData;
- if (extraData && extraData.matchId && extraData.categoryName) {
- identifier.value = `This script applies to all ${identifier.extraData.matchId} with \`category=${identifier.extraData.categoryName}\``;
- }
-}
-
-const componentPostProcessor = function(identifier) {
- const split = identifier.name.split(':');
- identifier.info = `A component of the ${split[0]} interface`;
- identifier.name = split[1];
-}
-
-const rowPostProcessor = function(identifier) {
- if (identifier.block) {
- const tableName = (identifier.block.split('=') || ['', ''])[1];
- identifier.info = `A row in the ${tableName} table`;
- delete identifier.block;
- identifier.extraData = {table: tableName};
- }
-}
-
-const columnPostProcessor = function(identifier) {
- const split = identifier.name.split(':');
- identifier.info = `A column of the ${split[0]} table`;
- identifier.name = split[1];
-
- const exec = END_OF_LINE.exec(identifier.block);
- if (!exec) return;
- const types = identifier.block.substring(8 + identifier.name.length, exec.index).split(',');
- identifier.extraData = {dataTypes: types};
- identifier.block = `Field types: ${types.join(', ')}`;
-}
-
-const fileNamePostProcessor = function(identifier) {
- identifier.info = `Refers to the file ${identifier.name}.${identifier.fileType}`;
-}
-
-module.exports = {
- coordPostProcessor, enumPostProcessor, dataTypePostProcessor, configKeyPostProcessor,
- triggerPostProcessor, categoryPostProcessor, componentPostProcessor, columnPostProcessor,
- fileNamePostProcessor, rowPostProcessor
-};
diff --git a/client/resource/triggers.js b/client/resource/triggers.js
deleted file mode 100644
index 1d72ad2..0000000
--- a/client/resource/triggers.js
+++ /dev/null
@@ -1,61 +0,0 @@
-const matchType = require("../matching/matchType");
-
-const runescriptTrigger = {
- proc: build(matchType.PROC, true),
- label: build(matchType.LABEL, true),
- queue: build(matchType.QUEUE, true),
- softtimer: build(matchType.SOFTTIMER, true),
- timer: build(matchType.TIMER, true),
- ai_timer: build(matchType.NPC, false),
- if_button: build(matchType.COMPONENT, false),
- if_close: build(matchType.COMPONENT, false),
- walktrigger: build(matchType.WALKTRIGGER, true),
- ai_walktrigger: build(matchType.NPC, false),
- debugproc: build(matchType.UNKNOWN, true),
- login: build(matchType.UNKNOWN, true),
- logout: build(matchType.UNKNOWN, true),
- tutorial: build(matchType.UNKNOWN, true),
- advancestat: build(matchType.STAT, false),
- mapzone: build(matchType.UNKNOWN, true),
- mapzoneexit: build(matchType.UNKNOWN, true),
- zone: build(matchType.UNKNOWN, true),
- zoneexit: build(matchType.UNKNOWN, true),
- command: build(matchType.COMMAND, true)
-}
-
-const configDuplicates = [
- {startsWith: 'opnpc', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.NPC},
- {startsWith: 'apnpc', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.NPC},
- {startsWith: 'ai_apnpc', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.NPC},
- {startsWith: 'ai_opnpc', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.NPC},
- {startsWith: 'opobj', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.OBJ},
- {startsWith: 'apobj', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.OBJ},
- {startsWith: 'ai_apobj', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.OBJ},
- {startsWith: 'ai_opobj', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.OBJ},
- {startsWith: 'oploc', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.LOC},
- {startsWith: 'aploc', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.LOC},
- {startsWith: 'ai_aploc', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.LOC},
- {startsWith: 'ai_oploc', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.LOC},
- {startsWith: 'opplayer', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.UNKNOWN},
- {startsWith: 'applayer', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.UNKNOWN},
- {startsWith: 'ai_applayer', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.NPC},
- {startsWith: 'ai_opplayer', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.NPC},
- {startsWith: 'ai_queue', upToNum: 20, includeU: false, includeT: false, includeD: false, defaultMatch: matchType.NPC},
- {startsWith: 'opheld', upToNum: 5, includeU: true, includeT: true, includeD: false, defaultMatch: matchType.OBJ},
- {startsWith: 'inv_button', upToNum: 5, includeU: false, includeT: false, includeD: true, defaultMatch: matchType.COMPONENT},
-];
-
-configDuplicates.forEach(dupeDef => {
- for (let i = 1; i <= dupeDef.upToNum; i++) {
- runescriptTrigger[`${dupeDef.startsWith}${i}`] = build(dupeDef.defaultMatch, false);
- }
- if (dupeDef.includeU) runescriptTrigger[`${dupeDef.startsWith}u`] = build(dupeDef.defaultMatch, false);
- if (dupeDef.includeT) runescriptTrigger[`${dupeDef.startsWith}t`] = build(matchType.COMPONENT, false);
- if (dupeDef.includeD) runescriptTrigger[`${dupeDef.startsWith}d`] = build(dupeDef.defaultMatch, false);
-});
-
-function build(match, declaration) {
- return {match: match, declaration: declaration};
-}
-
-module.exports = runescriptTrigger;
diff --git a/client/runescript-language.js b/client/runescript-language.js
deleted file mode 100644
index 85d9034..0000000
--- a/client/runescript-language.js
+++ /dev/null
@@ -1,61 +0,0 @@
-const vscode = require('vscode');
-const hoverProvider = require('./provider/hoverProvider');
-const recolorProvider = require('./provider/recolorProvider');
-const definitionProvider = require('./provider/gotoDefinition');
-const referenceProvider = require('./provider/referenceProvider');
-const renameProvider = require('./provider/renameProvider');
-const cacheManager = require('./cache/cacheManager');
-const commands = require('./provider/vscodeCommands');
-const signatureHelp = require('./provider/signatureHelpProvider');
-const configHelp = require('./provider/configHelpProvider');
-const completionProvider = require('./provider/completionProvider');
-const color24Provider = require('./provider/color24Provider.js');
-
-const languages = ['runescript','locconfig','objconfig','npcconfig','dbtableconfig','dbrowconfig','paramconfig','structconfig','enumconfig','varpconfig','varbitconfig','varnconfig','varsconfig','invconfig','seqconfig','spotanimconfig','mesanimconfig','idkconfig','huntconfig','constants','interface','pack','floconfig'];
-
-function activate(context) {
- // Register commands created by this extension
- Object.keys(commands).forEach(key =>
- context.subscriptions.push(vscode.commands.registerCommand(commands[key].id, commands[key].command)));
-
- // Populate cache on extension activation
- vscode.commands.executeCommand(commands.rebuildCache.id);
-
- // Cache processing event handlers for git branch changes, updating files, create/rename/delete files
- vscode.workspace.createFileSystemWatcher('**/.git/HEAD').onDidCreate(() => vscode.commands.executeCommand(commands.rebuildCache.id));
- vscode.workspace.onDidSaveTextDocument(saveDocumentEvent => cacheManager.rebuildFile(saveDocumentEvent.uri));
- vscode.workspace.onDidChangeTextDocument(() => cacheManager.rebuildActiveFile());
- vscode.window.onDidChangeActiveTextEditor(() => cacheManager.rebuildActiveFile());
- vscode.workspace.onDidDeleteFiles(filesDeletedEvent => cacheManager.clearFiles(filesDeletedEvent.files));
- vscode.workspace.onDidRenameFiles(filesRenamedEvent => cacheManager.renameFiles(filesRenamedEvent.files));
- vscode.workspace.onDidCreateFiles(filesCreatedEvent => cacheManager.createFiles(filesCreatedEvent.files));
-
- // Register providers (hover, rename, recolor, definition, reference)
- for (const language of languages) {
- vscode.languages.registerHoverProvider(language, hoverProvider(context));
- vscode.languages.registerRenameProvider(language, renameProvider);
- vscode.languages.registerCompletionItemProvider(language, completionProvider.provider, ...completionProvider.triggers);
- context.subscriptions.push(vscode.languages.registerDefinitionProvider(language, definitionProvider));
- context.subscriptions.push(vscode.languages.registerReferenceProvider(language, referenceProvider));
-
- if (language === 'floconfig' || language === 'interface') {
- vscode.languages.registerColorProvider(language, color24Provider);
- } else if (language.endsWith('config')) {
- vscode.languages.registerColorProvider(language, recolorProvider);
- }
-
- if (language.endsWith('config') || language === 'interface') {
- vscode.languages.registerSignatureHelpProvider(language, configHelp.provider, configHelp.metadata);
- }
- }
- vscode.languages.registerSignatureHelpProvider('runescript', signatureHelp.provider, signatureHelp.metadata);
-}
-
-function deactivate() {
- cacheManager.clearAll();
- }
-
-module.exports = {
- activate,
- deactivate
-};
diff --git a/client/utils/cacheUtils.js b/client/utils/cacheUtils.js
deleted file mode 100644
index 026c1f9..0000000
--- a/client/utils/cacheUtils.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const vscode = require('vscode');
-
-function resolveKey(name, match) {
- return (!name || !match) ? null : name + match.id;
-}
-
-function resolveFileKey(uri) {
- return (uri) ? uri.fsPath : null;
-}
-
-function encodeReference(line, index) {
- return `${line}|${index}`;
-}
-
-function decodeReferenceToLocation(uri, encodedValue) {
- const split = encodedValue.split('|');
- return (split.length !== 2) ? null : new vscode.Location(uri, new vscode.Position(Number(split[0]), Number(split[1])));
-}
-
-function decodeReferenceToRange(wordLength, encodedValue) {
- const split = encodedValue.split('|');
- if (split.length !== 2) {
- return null;
- }
- const startPosition = new vscode.Position(Number(split[0]), Number(split[1]));
- return new vscode.Range(startPosition, startPosition.translate(0, wordLength));
-}
-
-module.exports = { resolveKey, resolveFileKey, encodeReference, decodeReferenceToLocation, decodeReferenceToRange };
diff --git a/client/utils/markdownUtils.js b/client/utils/markdownUtils.js
deleted file mode 100644
index 97478e5..0000000
--- a/client/utils/markdownUtils.js
+++ /dev/null
@@ -1,66 +0,0 @@
-const vscode = require('vscode');
-const path = require('path');
-const { INFO, VALUE, SIGNATURE, CODEBLOCK } = require('../enum/hoverDisplayItems');
-const { GLOBAL_VAR } = require('../matching/matchType');
-
-function markdownBase(extensionContext) {
- const markdown = new vscode.MarkdownString();
- markdown.supportHtml = true;
- markdown.isTrusted = true;
- markdown.supportThemeIcons = true;
- markdown.baseUri = vscode.Uri.file(path.join(extensionContext.extensionPath, 'icons', path.sep));
- return markdown;
-}
-
-function expectedIdentifierMessage(word, match, markdown) {
- markdown.appendMarkdown(`
${match.id} ${word} not found`);
-}
-
-function appendTitle(name, type, matchId, markdown, id, isCert) {
- if (isCert && id) {
- name = `${name} (cert) [${Number(id) + 1}]`;
- } else if (id) {
- name = `${name} [${id}]`;
- }
- //
- if (matchId === GLOBAL_VAR.id) {
- markdown.appendMarkdown(`${type.toUpperCase()} ${name}`);
- } else {
- markdown.appendMarkdown(`${matchId} ${name}`);
- }
-}
-
-function appendInfo(identifier, displayItems, markdown) {
- if (displayItems.includes(INFO) && identifier.info) {
- appendBody(`${identifier.info}`, markdown);
- }
-}
-
-function appendValue(identifier, displayItems, markdown) {
- if (displayItems.includes(VALUE) && identifier.value) {
- appendBody(`${identifier.value}`, markdown);
- }
-}
-
-function appendSignature(identifier, displayItems, markdown) {
- if (displayItems.includes(SIGNATURE) && identifier.signature) {
- if (identifier.signature.paramsText.length > 0) markdown.appendCodeblock(`params: ${identifier.signature.paramsText}`, identifier.language);
- if (identifier.signature.returnsText.length > 0) markdown.appendCodeblock(`returns: ${identifier.signature.returnsText}`, identifier.language);
- }
-}
-
-function appendCodeBlock(identifier, displayItems, markdown) {
- if (displayItems.includes(CODEBLOCK) && identifier.block) {
- markdown.appendCodeblock(identifier.block, identifier.language);
- }
-}
-
-function appendBody(text, markdown) {
- if (!markdown.value.includes('---')) {
- markdown.appendMarkdown('\n\n---');
- }
- markdown.appendMarkdown(`\n\n${text}`);
-}
-
-module.exports = { markdownBase, expectedIdentifierMessage, appendTitle, appendInfo, appendValue,
- appendSignature, appendCodeBlock, appendBody };
diff --git a/client/utils/matchUtils.js b/client/utils/matchUtils.js
deleted file mode 100644
index 7834dc7..0000000
--- a/client/utils/matchUtils.js
+++ /dev/null
@@ -1,54 +0,0 @@
-const { WORD_PATTERN } = require("../enum/regex");
-
-function getWords(lineText, wordPattern=WORD_PATTERN) {
- return [ ...lineText.matchAll(wordPattern) ].map((wordMatch, index) => {
- return { value: wordMatch[0], start: wordMatch.index, end: wordMatch.index + wordMatch[0].length - 1, index: index}
- });
-}
-
-function getWordAtIndex(words, index) {
- if (words.length < 1) return null;
- let prev;
- for (let i = words.length - 1; i >= 0; i--) {
- if (index <= words[i].end) prev = words[i];
- else break;
- }
- return (prev && prev.start <= index && prev.end >= index) ? prev : null
-}
-
-function expandCsvKeyObject(obj) {
- let keys = Object.keys(obj);
- for (let i = 0; i < keys.length; ++i) {
- let key = keys[i];
- let subkeys = key.split(/,\s?/);
- let target = obj[key];
- delete obj[key];
- subkeys.forEach(k => obj[k] = target);
- }
- return obj;
-}
-
-/**
- * Context items shared by both matchWord and matchWords
- */
-function getBaseContext(lineText, lineNum, uri) {
- lineText = lineText.split('//')[0]; // Ignore anything after a comment
- const words = getWords(lineText);
- const fileSplit = uri.fsPath.split('\\').pop().split('/').pop().split('.');
- return {
- words: words,
- uri: uri,
- line: {text: lineText, number: lineNum},
- file: {name: fileSplit[0], type: fileSplit[1]},
- }
-}
-
-function reference(type, extraData) {
- return (extraData) ? { ...type, extraData: extraData, declaration: false } : { ...type, declaration: false };
-}
-
-function declaration(type, extraData) {
- return (extraData) ? { ...type, extraData: extraData, declaration: true } : { ...type, declaration: true };
-}
-
-module.exports = { getWords, getWordAtIndex, getBaseContext, expandCsvKeyObject, reference, declaration };
diff --git a/client/utils/stringUtils.js b/client/utils/stringUtils.js
deleted file mode 100644
index ea5d577..0000000
--- a/client/utils/stringUtils.js
+++ /dev/null
@@ -1,49 +0,0 @@
-const vscode = require('vscode');
-const { END_OF_LINE, END_OF_BLOCK } = require('../enum/regex');
-
-const getLineText = function(input) {
- const endOfLine = END_OF_LINE.exec(input);
- return !endOfLine ? input : input.substring(0, endOfLine.index);
-}
-
-const getLines = function(input) {
- return input.split(END_OF_LINE);
-}
-
-const skipFirstLine = function(input) {
- const endOfLine = END_OF_LINE.exec(input);
- return !endOfLine ? input : input.substring(endOfLine.index + 1);
-}
-
-const getBlockText = function(input) {
- const endOfBlock = END_OF_BLOCK.exec(input);
- return !endOfBlock ? input : input.substring(0, endOfBlock.index);
-}
-
-const nthIndexOf = function(input, pattern, n) {
- let i = -1;
- while (n-- > 0 && i++ < input.length) {
- i = input.indexOf(pattern, i);
- if (i < 0) break;
- }
- return i;
-}
-
-const truncateMatchingParenthesis = function(str) {
- let truncateIndex = 0;
- let count = 0;
- for (let i = 0; i < str.length; i++) {
- if (str.charAt(i) === '(') count++;
- if (str.charAt(i) === ')' && --count === 0) truncateIndex = i;
- }
- return (truncateIndex > 0) ? str.substring(truncateIndex + 1) : str;
-}
-
-function createSearchableString(linkableText, query, filesToInclude, isRegex=false) {
- const searchOptions = JSON.stringify({ query: query, filesToInclude: filesToInclude, isRegex: isRegex});
- return `[${linkableText}](${vscode.Uri.parse(`command:workbench.action.findInFiles?${encodeURIComponent(searchOptions)}`)})`;
-}
-
-module.exports = {
- getLineText, getLines, skipFirstLine, getBlockText, nthIndexOf, truncateMatchingParenthesis, createSearchableString
-};
diff --git a/package-lock.json b/package-lock.json
index 6242117..4b57000 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,15 +1,119 @@
{
"name": "runescriptlanguage",
- "version": "0.1.0",
+ "version": "0.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "runescriptlanguage",
- "version": "0.1.0",
+ "version": "0.3.0",
+ "dependencies": {
+ "runescript-lsp": "file:../runescript-lsp",
+ "vscode-languageclient": "^9.0.1"
+ },
"engines": {
"vscode": "^1.75.0"
}
+ },
+ "../runescript-lsp": {
+ "version": "0.1.0",
+ "dependencies": {
+ "runescript-parser": "^0.1.4",
+ "vscode-languageserver": "^9.0.1",
+ "vscode-languageserver-textdocument": "^1.0.11",
+ "vscode-uri": "^3.0.8"
+ },
+ "devDependencies": {
+ "@types/node": "^22.13.5",
+ "eslint": "^9.19.0",
+ "rimraf": "^6.0.1",
+ "typescript": "^5.7.3"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/runescript-lsp": {
+ "resolved": "../runescript-lsp",
+ "link": true
+ },
+ "node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/vscode-jsonrpc": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
+ "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/vscode-languageclient": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz",
+ "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==",
+ "license": "MIT",
+ "dependencies": {
+ "minimatch": "^5.1.0",
+ "semver": "^7.3.7",
+ "vscode-languageserver-protocol": "3.17.5"
+ },
+ "engines": {
+ "vscode": "^1.82.0"
+ }
+ },
+ "node_modules/vscode-languageserver-protocol": {
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
+ "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
+ "license": "MIT",
+ "dependencies": {
+ "vscode-jsonrpc": "8.2.0",
+ "vscode-languageserver-types": "3.17.5"
+ }
+ },
+ "node_modules/vscode-languageserver-types": {
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
+ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
+ "license": "MIT"
}
}
}
diff --git a/package.json b/package.json
index 0637b21..d2762ee 100644
--- a/package.json
+++ b/package.json
@@ -4,8 +4,9 @@
"publisher": "2004scape",
"repository": "https://github.com/LostCityRS/RuneScriptLanguage",
"description": "Syntax highlighting for RuneScript.",
- "version": "0.2.4",
+ "version": "0.3.0",
"icon": "icons/icon-min.png",
+ "type": "commonjs",
"engines": {
"vscode": "^1.75.0"
},
@@ -13,10 +14,30 @@
"Programming Languages",
"Snippets"
],
- "main": "./client/runescript-language.js",
+ "main": "./src/runescript-extension.js",
"activationEvents": [
],
"contributes": {
+ "configuration": {
+ "title": "RuneScript",
+ "properties": {
+ "runescript.enableHover": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable/disable RuneScript hover information."
+ },
+ "runescript.enableDiagnostics": {
+ "type": "boolean",
+ "default": true,
+ "description": "Enable/disable RuneScript diagnostics."
+ },
+ "runescript.enableDevMode": {
+ "type": "boolean",
+ "default": false,
+ "description": "Enable/disable RuneScript extension development mode."
+ }
+ }
+ },
"languages": [
{
"id": "runescript",
@@ -542,8 +563,12 @@
"commands": [
{
"command": "RuneScriptLanguage.rebuildCache",
- "title": "Runescript: Rebuild Workspace Cache"
+ "title": "Runescript: Rescan Workspace"
}
]
+ },
+ "dependencies": {
+ "runescript-lsp": "file:../runescript-lsp",
+ "vscode-languageclient": "^9.0.1"
}
}
diff --git a/src/clientState.js b/src/clientState.js
new file mode 100644
index 0000000..f21aa5e
--- /dev/null
+++ b/src/clientState.js
@@ -0,0 +1,11 @@
+let client;
+
+function setClient(nextClient) {
+ client = nextClient;
+}
+
+function getClient() {
+ return client;
+}
+
+module.exports = { setClient, getClient };
diff --git a/src/commands.js b/src/commands.js
new file mode 100644
index 0000000..7ad52d0
--- /dev/null
+++ b/src/commands.js
@@ -0,0 +1,21 @@
+const { getClient } = require("./clientState");
+
+const vscode = require('vscode');
+
+function registerCommands(context) {
+ context.subscriptions.push(
+ vscode.commands.registerCommand('RuneScriptLanguage.rebuildCache', executeWorkspaceRescan)
+ );
+}
+
+async function executeWorkspaceRescan() {
+ const editor = vscode.window.activeTextEditor;
+ const folder = editor ? vscode.workspace.getWorkspaceFolder(editor.document.uri) : undefined;
+ const workspaceFolder = folder?.uri.toString();
+ await getClient().sendRequest("workspace/executeCommand", {
+ command: "runescript.rescanWorkspace",
+ arguments: workspaceFolder ? [workspaceFolder] : []
+ });
+}
+
+module.exports = { registerCommands };
diff --git a/src/devModeHighlights.js b/src/devModeHighlights.js
new file mode 100644
index 0000000..d061f28
--- /dev/null
+++ b/src/devModeHighlights.js
@@ -0,0 +1,39 @@
+const { getClient } = require("./clientState");
+const vscode = require('vscode');
+
+const decorationType = vscode.window.createTextEditorDecorationType({
+ backgroundColor: 'rgba(80, 200, 120, 0.20)'
+});
+
+function registerDevModeHighlights() {
+ getClient().onNotification("runescript/decorations", recieveDecorations);
+}
+
+function recieveDecorations({ uri, ranges }) {
+ const editor = vscode.window.visibleTextEditors.find((e) => e.document.uri.toString() === uri);
+ if (!editor) return;
+
+ const vscodeRanges = ranges.map(r =>
+ new vscode.Range(
+ new vscode.Position(r.start.line, r.start.character),
+ new vscode.Position(r.end.line, r.end.character)
+ )
+ );
+
+ editor.setDecorations(decorationType, vscodeRanges);
+}
+
+async function requestDecorations(editor) {
+ const uri = editor.document.uri.toString();
+ const res = await getClient().sendRequest("runescript/getDecorations", { uri });
+ const ranges = res?.ranges ?? [];
+
+ editor.setDecorations(decorationType, ranges.map(r =>
+ new vscode.Range(
+ new vscode.Position(r.start.line, r.start.character),
+ new vscode.Position(r.end.line, r.end.character)
+ )
+ ));
+}
+
+module.exports = { requestDecorations, registerDevModeHighlights };
diff --git a/src/events.js b/src/events.js
new file mode 100644
index 0000000..dcc68a8
--- /dev/null
+++ b/src/events.js
@@ -0,0 +1,28 @@
+const { requestDecorations } = require('./devModeHighlights');
+const { getClient } = require("./clientState");
+
+const vscode = require('vscode');
+
+function registerEventHandlers(context) {
+ const gitBranchWatcher = vscode.workspace.createFileSystemWatcher('**/.git/HEAD');
+ gitBranchWatcher.onDidCreate(onGitBranchChange);
+
+ context.subscriptions.push(
+ gitBranchWatcher,
+ vscode.window.onDidChangeActiveTextEditor(changeActiveTextEditorHandler)
+ );
+}
+
+function onGitBranchChange(uri) {
+ const folder = vscode.workspace.getWorkspaceFolder(uri);
+ getClient().sendNotification("runescript/gitBranchChanged", {
+ workspaceUri: folder?.uri.toString()
+ });
+}
+
+async function changeActiveTextEditorHandler(editor) {
+ if (!editor) return;
+ requestDecorations(editor);
+}
+
+module.exports = { registerEventHandlers };
diff --git a/src/runescript-extension.js b/src/runescript-extension.js
new file mode 100644
index 0000000..26144b5
--- /dev/null
+++ b/src/runescript-extension.js
@@ -0,0 +1,75 @@
+const path = require('path');
+const vscode = require('vscode');
+const { LanguageClient, TransportKind } = require('vscode-languageclient/node');
+const { registerCommands } = require('./commands');
+const { registerDevModeHighlights } = require('./devModeHighlights');
+const { registerEventHandlers } = require('./events');
+const { setClient } = require('./clientState');
+
+const outputChannelName = 'RuneScript LSP';
+const languages = ['runescript','locconfig','objconfig','npcconfig','dbtableconfig','dbrowconfig','paramconfig',
+ 'structconfig','enumconfig','varpconfig','varbitconfig','varnconfig','varsconfig','invconfig','seqconfig',
+ 'spotanimconfig','mesanimconfig','idkconfig','huntconfig','constants','interface','pack','floconfig'];
+
+let outputChannel;
+let client;
+
+function activate(context) {
+ const devMode = context.extensionMode === vscode.ExtensionMode.Development;
+ outputChannel = vscode.window.createOutputChannel(outputChannelName);
+ context.subscriptions.push(outputChannel);
+
+ const serverModule = devMode
+ ? process.env.RUNESCRIPT_LSP_PATH ?? path.resolve(context.extensionPath, '..', 'runescript-lsp', 'dist', 'server.js')
+ : context.asAbsolutePath(path.join('node_modules', 'runescript-lsp', 'dist', 'server.js'));
+
+ const serverOptions = {
+ run: { module: serverModule, transport: TransportKind.ipc },
+ debug: {
+ module: serverModule,
+ transport: TransportKind.ipc,
+ options: { execArgv: ['--nolazy', '--inspect=6009'] }
+ }
+ };
+
+ const clientOptions = {
+ documentSelector: languages.map((language) => ({ scheme: 'file', language })),
+ outputChannel: outputChannel,
+ synchronize: {
+ configurationSection: 'runescript',
+ fileEvents: vscode.workspace.createFileSystemWatcher('**/*.{rs2,cs2,obj,loc,npc,dbtable,dbrow,param,struct,enum,varp,varbit,varn,vars,inv,seq,spotanim,mesanim,idk,hunt,constant,if,flo,pack,order,opt,jm2}')
+ }
+ };
+
+ if (devMode) {
+ outputChannel.appendLine(`Server module: ${serverModule}`)
+ clientOptions.traceOutputChannel = outputChannel;
+ }
+
+ client = new LanguageClient(
+ 'runescriptLanguageServer',
+ 'RuneScript Language Server',
+ serverOptions,
+ clientOptions
+ );
+ setClient(client);
+
+ client.onDidChangeState((event) => {
+ if (devMode) outputChannel.appendLine(`Client state: ${event.newState}`);
+ });
+
+ registerCommands(context);
+ registerDevModeHighlights();
+ registerEventHandlers(context);
+
+ context.subscriptions.push(client.start());
+}
+
+function deactivate() {
+ if (!client) {
+ return undefined;
+ }
+ return client.stop();
+}
+
+module.exports = { activate, deactivate };