atom-languageclient
Version:
Integrate Language Servers with Atom
119 lines • 15.7 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getExePath = exports.exeExtentionDefault = exports.rootPathDefault = exports.promiseWithTimeout = exports.assertUnreachable = exports.doWithCancellationToken = exports.cancelAndRefreshCancellationToken = exports.escapeRegExp = exports.getWordAtPosition = void 0;
const path_1 = require("path");
const fs_1 = require("fs");
const atom_1 = require("atom");
const vscode_jsonrpc_1 = require("vscode-jsonrpc");
/** Obtain the range of the word at the given editor position. Uses the non-word characters from the position's grammar scope. */
function getWordAtPosition(editor, position) {
const nonWordCharacters = escapeRegExp(editor.getNonWordCharacters(position));
const range = _getRegexpRangeAtPosition(editor.getBuffer(), position, new RegExp(`^[\t ]*$|[^\\s${nonWordCharacters}]+`, "g"));
if (range == null) {
return new atom_1.Range(position, position);
}
return range;
}
exports.getWordAtPosition = getWordAtPosition;
function escapeRegExp(string) {
// From atom/underscore-plus.
return string.replace(/[$()*+./?[\\\]^{|}-]/g, "\\$&");
}
exports.escapeRegExp = escapeRegExp;
function _getRegexpRangeAtPosition(buffer, position, wordRegex) {
const { row, column } = position;
const rowRange = buffer.rangeForRow(row, false);
let matchData;
// Extract the expression from the row text.
buffer.scanInRange(wordRegex, rowRange, (data) => {
const { range } = data;
if (position.isGreaterThanOrEqual(range.start) &&
// Range endpoints are exclusive.
position.isLessThan(range.end)) {
matchData = data;
data.stop();
return;
}
// Stop the scan if the scanner has passed our position.
if (range.end.column > column) {
data.stop();
}
});
return matchData == null ? null : matchData.range;
}
/**
* For the given connection and cancellationTokens map, cancel the existing CancellationToken for that connection then
* create and store a new CancellationToken to be used for the current request.
*/
function cancelAndRefreshCancellationToken(key, cancellationTokens) {
let cancellationToken = cancellationTokens.get(key);
if (cancellationToken !== undefined && !cancellationToken.token.isCancellationRequested) {
cancellationToken.cancel();
}
cancellationToken = new vscode_jsonrpc_1.CancellationTokenSource();
cancellationTokens.set(key, cancellationToken);
return cancellationToken.token;
}
exports.cancelAndRefreshCancellationToken = cancelAndRefreshCancellationToken;
function doWithCancellationToken(key, cancellationTokens, work) {
return __awaiter(this, void 0, void 0, function* () {
const token = cancelAndRefreshCancellationToken(key, cancellationTokens);
const result = yield work(token);
cancellationTokens.delete(key);
return result;
});
}
exports.doWithCancellationToken = doWithCancellationToken;
function assertUnreachable(_) {
return _;
}
exports.assertUnreachable = assertUnreachable;
function promiseWithTimeout(ms, promise) {
return new Promise((resolve, reject) => {
// create a timeout to reject promise if not resolved
const timer = setTimeout(() => {
reject(new Error(`Timeout after ${ms}ms`));
}, ms);
promise
.then((res) => {
clearTimeout(timer);
resolve(res);
})
.catch((err) => {
clearTimeout(timer);
reject(err);
});
});
}
exports.promiseWithTimeout = promiseWithTimeout;
exports.rootPathDefault = path_1.join("bin", `${process.platform}-${process.arch}`);
exports.exeExtentionDefault = process.platform === "win32" ? ".exe" : "";
/**
* Finds an exe file in the package assuming it is placed under `rootPath/platform-arch/exe`. If the exe file did not
* exist, the given name is returned. For example on Windows x64, if the `exeName` is `serve-d`, it returns the absolute
* path to `./bin/win32-x64/exeName.exe`, and if the file did not exist, `serve-d` is returned.
*
* @param exeName Name of the exe file
* @param rootPath The path of the folder of the exe file. Defaults to 'join("bin", `${process.platform}-${process.arch}`)'
* @param exeExtention The extention of the exe file. Defaults to `process.platform === "win32" ? ".exe" : ""`
*/
function getExePath(exeName, rootPath = exports.rootPathDefault, exeExtention = exports.exeExtentionDefault) {
const exePath = path_1.resolve(path_1.join(rootPath, `${exeName}${exeExtention}`));
if (fs_1.existsSync(exePath)) {
return exePath;
}
else {
return exeName;
}
}
exports.getExePath = getExePath;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9saWIvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBQUEsK0JBQW9DO0FBQ3BDLDJCQUErQjtBQUMvQiwrQkFBNkU7QUFDN0UsbURBQTJFO0FBSTNFLGlJQUFpSTtBQUNqSSxTQUFnQixpQkFBaUIsQ0FBQyxNQUFrQixFQUFFLFFBQWU7SUFDbkUsTUFBTSxpQkFBaUIsR0FBRyxZQUFZLENBQUMsTUFBTSxDQUFDLG9CQUFvQixDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUE7SUFDN0UsTUFBTSxLQUFLLEdBQUcseUJBQXlCLENBQ3JDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsRUFDbEIsUUFBUSxFQUNSLElBQUksTUFBTSxDQUFDLGlCQUFpQixpQkFBaUIsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUN4RCxDQUFBO0lBQ0QsSUFBSSxLQUFLLElBQUksSUFBSSxFQUFFO1FBQ2pCLE9BQU8sSUFBSSxZQUFLLENBQUMsUUFBUSxFQUFFLFFBQVEsQ0FBQyxDQUFBO0tBQ3JDO0lBQ0QsT0FBTyxLQUFLLENBQUE7QUFDZCxDQUFDO0FBWEQsOENBV0M7QUFFRCxTQUFnQixZQUFZLENBQUMsTUFBYztJQUN6Qyw2QkFBNkI7SUFDN0IsT0FBTyxNQUFNLENBQUMsT0FBTyxDQUFDLHVCQUF1QixFQUFFLE1BQU0sQ0FBQyxDQUFBO0FBQ3hELENBQUM7QUFIRCxvQ0FHQztBQUVELFNBQVMseUJBQXlCLENBQUMsTUFBa0IsRUFBRSxRQUFlLEVBQUUsU0FBaUI7SUFDdkYsTUFBTSxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsR0FBRyxRQUFRLENBQUE7SUFDaEMsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLFdBQVcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUE7SUFDL0MsSUFBSSxTQUE4QyxDQUFBO0lBQ2xELDRDQUE0QztJQUM1QyxNQUFNLENBQUMsV0FBVyxDQUFDLFNB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UUFBQSxtQkFBbUIsR0FBRyxPQUFPLENBQUMsUUFBUSxLQUFLLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUE7QUFFN0U7Ozs7Ozs7O0dBUUc7QUFDSCxTQUFnQixVQUFVLENBQUMsT0FBZSxFQUFFLFFBQVEsR0FBRyx1QkFBZSxFQUFFLFlBQVksR0FBRywyQkFBbUI7SUFDeEcsTUFBTSxPQUFPLEdBQUcsY0FBTyxDQUFDLFdBQUksQ0FBQyxRQUFRLEVBQUUsR0FBRyxPQUFPLEdBQUcsWUFBWSxFQUFFLENBQUMsQ0FBQyxDQUFBO0lBQ3BFLElBQUksZUFBVSxDQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQ3ZCLE9BQU8sT0FBTyxDQUFBO0tBQ2Y7U0FBTTtRQUNMLE9BQU8sT0FBTyxDQUFBO0tBQ2Y7QUFDSCxDQUFDO0FBUEQsZ0NBT0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBqb2luLCByZXNvbHZlIH0gZnJvbSBcInBhdGhcIlxuaW1wb3J0IHsgZXhpc3RzU3luYyB9IGZyb20gXCJmc1wiXG5pbXBvcnQgeyBQb2ludCwgVGV4dEJ1ZmZlciwgVGV4dEVkaXRvciwgUmFuZ2UsIEJ1ZmZlclNjYW5SZXN1bHQgfSBmcm9tIFwiYXRvbVwiXG5pbXBvcnQgeyBDYW5jZWxsYXRpb25Ub2tlbiwgQ2FuY2VsbGF0aW9uVG9rZW5Tb3VyY2UgfSBmcm9tIFwidnNjb2RlLWpzb25ycGNcIlxuXG5leHBvcnQgdHlwZSBSZXBvcnRCdXN5V2hpbGUgPSA8VD4odGl0bGU6IHN0cmluZywgZjogKCkgPT4gUHJvbWlzZTxUPikgPT4gUHJvbWlzZTxUPlxuXG4vKiogT2J0YWluIHRoZSByYW5nZSBvZiB0aGUgd29yZCBhdCB0aGUgZ2l2ZW4gZWRpdG9yIHBvc2l0aW9uLiBVc2VzIHRoZSBub24td29yZCBjaGFyYWN0ZXJzIGZyb20gdGhlIHBvc2l0aW9uJ3MgZ3JhbW1hciBzY29wZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRXb3JkQXRQb3NpdGlvbihlZGl0b3I6IFRleHRFZGl0b3IsIHBvc2l0aW9uOiBQb2ludCk6IFJhbmdlIHtcbiAgY29uc3Qgbm9uV29yZENoYXJhY3RlcnMgPSBlc2NhcGVSZWdFeHAoZWRpdG9yLmdldE5vbldvcmRDaGFyYWN0ZXJzKHBvc2l0aW9uKSlcbiAgY29uc3QgcmFuZ2UgPSBfZ2V0UmVnZXhwUmFuZ2VBdFBvc2l0aW9uKFxuICAgIGVkaXRvci5nZXRCdWZmZXIoKSxcbiAgICBwb3NpdGlvbixcbiAgICBuZXcgUmVnRXhwKGBeW1xcdCBdKiR8W15cXFxccyR7bm9uV29yZENoYXJhY3RlcnN9XStgLCBcImdcIilcbiAgKVxuICBpZiAocmFuZ2UgPT0gbnVsbCkge1xuICAgIHJldHVybiBuZXcgUmFuZ2UocG9zaXRpb24sIHBvc2l0aW9uKVxuICB9XG4gIHJldHVybiByYW5nZVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZXNjYXBlUmVnRXhwKHN0cmluZzogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gRnJvbSBhdG9tL3VuZGVyc2NvcmUtcGx1cy5cbiAgcmV0dXJuIHN0cmluZy5yZXBsYWNlKC9bJCgpKisuLz9bXFxcXFxcXV57fH0tXS9nLCBcIlxcXFwkJlwiKVxufVxuXG5mdW5jdGlvbiBfZ2V0UmVnZXhwUmFuZ2VBdFBvc2l0aW9uKGJ1ZmZlcjogVGV4dEJ1ZmZlciwgcG9zaXRpb246IFBvaW50LCB3b3JkUmVnZXg6IFJlZ0V4cCk6IFJhbmdlIHwgbnVsbCB7XG4gIGNvbnN0IHsgcm93LCBjb2x1bW4gfSA9IHBvc2l0aW9uXG4gIGNvbnN0IHJvd1JhbmdlID0gYnVmZmVyLnJhbmdlRm9yUm93KHJvdywgZmFsc2UpXG4gIGxldCBtYXRjaERhdGE6IEJ1ZmZlclNjYW5SZXN1bHQgfCB1bmRlZmluZWQgfCBudWxsXG4gIC8vIEV4dHJhY3QgdGhlIGV4cHJlc3Npb24gZnJvbSB0aGUgcm93IHRleHQuXG4gIGJ1ZmZlci5zY2FuSW5SYW5nZSh3b3JkUmVnZXgsIHJvd1JhbmdlLCAoZGF0YSkgPT4ge1xuICAgIGNvbnN0IHsgcmFuZ2UgfSA9IGRhdGFcbiAgICBpZiAoXG4gICAgICBwb3NpdGlvbi5pc0dyZWF0ZXJUaGFuT3JFcXVhbChyYW5nZS5zdGFydCkgJiZcbiAgICAgIC8vIFJhbmdlIGVuZHBvaW50cyBhcmUgZXhjbHVzaXZlLlxuICAgICAgcG9zaXRpb24uaXNMZXNzVGhhbihyYW5nZS5lbmQpXG4gICAgKSB7XG4gICAgICBtYXRjaERhdGEgPSBkYXRhXG4gICAgICBkYXRhLnN0b3AoKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIC8vIFN0b3AgdGhlIHNjYW4gaWYgdGhlIHNjYW5uZXIgaGFzIHBhc3NlZCBvdXIgcG9zaXRpb24uXG4gICAgaWYgKHJhbmdlLmVuZC5jb2x1bW4gPiBjb2x1bW4pIHtcbiAgICAgIGRhdGEuc3RvcCgpXG4gICAgfVxuICB9KVxuICByZXR1cm4gbWF0Y2hEYXRhID09IG51bGwgPyBudWxsIDogbWF0Y2hEYXRhLnJhbmdlXG59XG5cbi8qKlxuICogRm9yIHRoZSBnaXZlbiBjb25uZWN0aW9uIGFuZCBjYW5jZWxsYXRpb25Ub2tlbnMgbWFwLCBjYW5jZWwgdGhlIGV4aXN0aW5nIENhbmNlbGxhdGlvblRva2VuIGZvciB0aGF0IGNvbm5lY3Rpb24gdGhlblxuICogY3JlYXRlIGFuZCBzdG9yZSBhIG5ldyBDYW5jZWxsYXRpb25Ub2tlbiB0byBiZSB1c2VkIGZvciB0aGUgY3VycmVudCByZXF1ZXN0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY2FuY2VsQW5kUmVmcmVzaENhbmNlbGxhdGlvblRva2VuPFQgZXh0ZW5kcyBvYmplY3Q+KFxuICBrZXk6IFQsXG4gIGNhbmNlbGxhdGlvblRva2VuczogV2Vha01hcDxULCBDYW5jZWxsYXRpb25Ub2tlblNvdXJjZT5cbik6IENhbmNlbGxhdGlvblRva2VuIHtcbiAgbGV0IGNhbmNlbGxhdGlvblRva2VuID0gY2FuY2VsbGF0aW9uVG9rZW5zLmdldChrZXkpXG4gIGlmIChjYW5jZWxsYXRpb25Ub2tlbiAhPT0gdW5kZWZpbmVkICYmICFjYW5jZWxsYXRpb25Ub2tlbi50b2tlbi5pc0NhbmNlbGxhdGlvblJlcXVlc3RlZCkge1xuICAgIGNhbmNlbGxhdGlvblRva2VuLmNhbmNlbCgpXG4gIH1cblxuICBjYW5jZWxsYXRpb25Ub2tlbiA9IG5ldyBDYW5jZWxsYXRpb25Ub2tlblNvdXJjZSgpXG4gIGNhbmNlbGxhdGlvblRva2Vucy5zZXQoa2V5LCBjYW5jZWxsYXRpb25Ub2tlbilcbiAgcmV0dXJuIGNhbmNlbGxhdGlvblRva2VuLnRva2VuXG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBkb1dpdGhDYW5jZWxsYXRpb25Ub2tlbjxUMSBleHRlbmRzIG9iamVjdCwgVDI+KFxuICBrZXk6IFQxLFxuICBjYW5jZWxsYXRpb25Ub2tlbnM6IFdlYWtNYXA8VDEsIENhbmNlbGxhdGlvblRva2VuU291cmNlPixcbiAgd29yazogKHRva2VuOiBDYW5jZWxsYXRpb25Ub2tlbikgPT4gUHJvbWlzZTxUMj5cbik6IFByb21pc2U8VDI+IHtcbiAgY29uc3QgdG9rZW4gPSBjYW5jZWxBbmRSZWZyZXNoQ2FuY2VsbGF0aW9uVG9rZW4oa2V5LCBjYW5jZWxsYXRpb25Ub2tlbnMpXG4gIGNvbnN0IHJlc3VsdDogVDIgPSBhd2FpdCB3b3JrKHRva2VuKVxuICBjYW5jZWxsYXRpb25Ub2tlbnMuZGVsZXRlKGtleSlcbiAgcmV0dXJuIHJlc3VsdFxufVxuXG5leHBvcnQgZnVuY3Rpb24gYXNzZXJ0VW5yZWFjaGFibGUoXzogbmV2ZXIpOiBuZXZlciB7XG4gIHJldHVybiBfXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwcm9taXNlV2l0aFRpbWVvdXQ8VD4obXM6IG51bWJlciwgcHJvbWlzZTogUHJvbWlzZTxUPik6IFByb21pc2U8VD4ge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIC8vIGNyZWF0ZSBhIHRpbWVvdXQgdG8gcmVqZWN0IHByb21pc2UgaWYgbm90IHJlc29sdmVkXG4gICAgY29uc3QgdGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIHJlamVjdChuZXcgRXJyb3IoYFRpbWVvdXQgYWZ0ZXIgJHttc31tc2ApKVxuICAgIH0sIG1zKVxuXG4gICAgcHJvbWlzZVxuICAgICAgLnRoZW4oKHJlcykgPT4ge1xuICAgICAgICBjbGVhclRpbWVvdXQodGltZXIpXG4gICAgICAgIHJlc29sdmUocmVzKVxuICAgICAgfSlcbiAgICAgIC5jYXRjaCgoZXJyKSA9PiB7XG4gICAgICAgIGNsZWFyVGltZW91dCh0aW1lcilcbiAgICAgICAgcmVqZWN0KGVycilcbiAgICAgIH0pXG4gIH0pXG59XG5cbmV4cG9ydCBjb25zdCByb290UGF0aERlZmF1bHQgPSBqb2luKFwiYmluXCIsIGAke3Byb2Nlc3MucGxhdGZvcm19LSR7cHJvY2Vzcy5hcmNofWApXG5leHBvcnQgY29uc3QgZXhlRXh0ZW50aW9uRGVmYXVsdCA9IHByb2Nlc3MucGxhdGZvcm0gPT09IFwid2luMzJcIiA/IFwiLmV4ZVwiIDogXCJcIlxuXG4vKipcbiAqIEZpbmRzIGFuIGV4ZSBmaWxlIGluIHRoZSBwYWNrYWdlIGFzc3VtaW5nIGl0IGlzIHBsYWNlZCB1bmRlciBgcm9vdFBhdGgvcGxhdGZvcm0tYXJjaC9leGVgLiBJZiB0aGUgZXhlIGZpbGUgZGlkIG5vdFxuICogZXhpc3QsIHRoZSBnaXZlbiBuYW1lIGlzIHJldHVybmVkLiBGb3IgZXhhbXBsZSBvbiBXaW5kb3dzIHg2NCwgaWYgdGhlIGBleGVOYW1lYCBpcyBgc2VydmUtZGAsIGl0IHJldHVybnMgdGhlIGFic29sdXRlXG4gKiBwYXRoIHRvIGAuL2Jpbi93aW4zMi14NjQvZXhlTmFtZS5leGVgLCBhbmQgaWYgdGhlIGZpbGUgZGlkIG5vdCBleGlzdCwgYHNlcnZlLWRgIGlzIHJldHVybmVkLlxuICpcbiAqIEBwYXJhbSBleGVOYW1lIE5hbWUgb2YgdGhlIGV4ZSBmaWxlXG4gKiBAcGFyYW0gcm9vdFBhdGggVGhlIHBhdGggb2YgdGhlIGZvbGRlciBvZiB0aGUgZXhlIGZpbGUuIERlZmF1bHRzIHRvICdqb2luKFwiYmluXCIsIGAke3Byb2Nlc3MucGxhdGZvcm19LSR7cHJvY2Vzcy5hcmNofWApJ1xuICogQHBhcmFtIGV4ZUV4dGVudGlvbiBUaGUgZXh0ZW50aW9uIG9mIHRoZSBleGUgZmlsZS4gRGVmYXVsdHMgdG8gYHByb2Nlc3MucGxhdGZvcm0gPT09IFwid2luMzJcIiA/IFwiLmV4ZVwiIDogXCJcImBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEV4ZVBhdGgoZXhlTmFtZTogc3RyaW5nLCByb290UGF0aCA9IHJvb3RQYXRoRGVmYXVsdCwgZXhlRXh0ZW50aW9uID0gZXhlRXh0ZW50aW9uRGVmYXVsdCk6IHN0cmluZyB7XG4gIGNvbnN0IGV4ZVBhdGggPSByZXNvbHZlKGpvaW4ocm9vdFBhdGgsIGAke2V4ZU5hbWV9JHtleGVFeHRlbnRpb259YCkpXG4gIGlmIChleGlzdHNTeW5jKGV4ZVBhdGgpKSB7XG4gICAgcmV0dXJuIGV4ZVBhdGhcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gZXhlTmFtZVxuICB9XG59XG4iXX0=
;