0

I am creating a language server and currently have a list of static/preloaded completion items. This language server will activate on any .script file. In the same directory there will exist a .map file (arbitrary extension, I am not referring to JS source map files). The map file is just a plain text document that contains a list of classnames and names (for each 'entity'). A typical entry might look like this:

// entity 237
{
"classname" "func_static"
"name" "func_static_237"
"origin" "200 179.5 -181"
"rotation" "0 -1 0 1 0 0 0 0 1"
}

I'm trying to add each name/classname pair to the completion list (name being the completion item label, and the classname being the detail). Here is what I have done so far:

As there are unfortunately single cases of 'classname' on their own, I opted to use a regex that matches across both lines. Then I used two capture groups (one for name and one for classname) to push both into an array. I then iterate across each pair in the array and add it to the completion list.

The idea is that I am preloading variables from a map file into the corresponding script file (they'll both have the same filename and be in the same directory).

The code looks like this:

'use strict';

import * as path from 'path';
import * as vscode from 'vscode';
import * as fs from 'fs';

export function activate(context: vscode.ExtensionContext) {

    ...

    // each pair will go into this
    var objArray = new Array;

    function readTextFile() {
        // load the map file
        var text = fs.readFileSync(context.asAbsolutePath(path.join('donkey.map')));
        // match classname and name
        var regex = /\"classname\"\s\"(\w+)\"(?:\r\n|[\r\n])\"name\"\s\"(\w+)\"/g
        getMatches(text.toString(), regex);
    }

    function getMatches(string: string, regex: RegExp) {
        var match;
        while (match = regex.exec(string)){
            // capture group 1 = classname
            // capture group 2 = name
            // pushing name first because it's more important
            objArray.push([match[2], match[1]]);
        }
    }

    var loop1 = function (i: number) {
        // create a new completion item
        vscode.languages.registerCompletionItemProvider('quadscript', {
            provideCompletionItems: function () {
                var compItem = new vscode.CompletionItem(objArray[i][0], vscode.CompletionItemKind.Variable);
                compItem.insertText = ("$" + objArray[i][0]);
                compItem.detail = (objArray[i][1]);
                return [
                    compItem
                ];
            }
        });
    }

    for (var i = 0; i < objArray.length; i++) {
        loop1(i);
    }

    readTextFile();
}

When I debug the extension in the Extension Development Host, the debug console shows this:

C:\Program Files\Microsoft VS Code\Code.exe --debugBrkPluginHost=37535 --debugId=cfbf2c6d-c313-46fa-8c44-4e64f44eb97f --extensionDevelopmentPath=c:\Users\thefyrewire\Documents\VSCode\quadscript

And then when I type func they show up as expected showing they were sucessfully read from the file (which exists at c:\Users\thefyrewire\Documents\VSCode\quadscript\donkey.map, corresponding to the extensionDevelopmentPath as seen in the debug console).

completion items successfully read from another file

I next went to c:\Users\thefyrewire\Documents\test and moved the donkey.script and donkey.map files here, then packaged and installed the extension. When I went into the script file and brought up the completion list, the func_static items were not there. I think this is because it is trying to read from where the extension is installed in .vscode\extensions\<extension>\donkey.map which now doesn't exist as I moved the file.

So I tried to change the path from where I was reading the map file and used an absolute path as a test.

var text = fs.readFileSync("file:///C:/Users/thefyrewire/Documents/test/donkey.map");

When debugging I get returned this:

ENOENT: no such file or directory, open 'C:\Program Files\Microsoft VS Code\file:\C:\Users\thefyrewire\Documents\test\donkey.map'. Close Warn

Using a relative path like so:

var text = fs.readFileSync("./donkey.map");

Returns:

ENOENT: no such file or directory, open 'C:\Program Files\Microsoft VS Code\donkey.map'. Close Warn

I tried multiple other fs methods and it appears to only start reading from C:\Program Files\Microsoft VS Code.

So finally my question: what do I need to do in order to allow my extension to dynamically read from the same directory as whatever script file is loaded? Any advice would be greatly appreciated, thank you!

Edit: I realise this is unclear. By 'script file' I don't mean a JS script file, it's again part of the custom language (hence the language server).

thefyrewire
  • 85
  • 1
  • 7
  • 1
    It is weird to see you have LSP server code in `activate`, as most existing language server implementations use a separate process. When do you need to read the .map file? If it should only be read before preparing completion items, you should use the `TextDocumentPositionParams.textDocument` to find the JS file path first, and then calculate the .map file path. Anyway, divide the problem to smaller steps and conquer them one by one. – Lex Li Dec 26 '17 at 16:59

1 Answers1

0

You can use __dirname

var fs = require('fs')
var path = require('path')

var text = fs.readFileSync(path.join(__dirname, donkey.map));
Vipin Kumar
  • 6,441
  • 1
  • 19
  • 25
  • Thank you, but when I tried this it only tried to open the file in the same directory as where the js file was running from (inside the extension folder) like the first time I tried. I am trying to find a way to detect the current directory of the script file (which a user can open, and therefore will change between different files) – thefyrewire Dec 14 '17 at 15:26