javascript-typescript-langserver
Version:
Implementation of the Language Server Protocol for JavaScript and TypeScript
207 lines • 7.33 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const fast_json_patch_1 = require("fast-json-patch");
const rxjs_1 = require("rxjs");
const string_similarity_1 = require("string-similarity");
const ts = require("typescript");
const url = require("url");
/**
* Converts an Iterable to an Observable.
* Workaround for https://github.com/ReactiveX/rxjs/issues/2306
*/
function observableFromIterable(iterable) {
return rxjs_1.Observable.from(iterable);
}
exports.observableFromIterable = observableFromIterable;
/**
* Template string tag to escape JSON Pointer components as per https://tools.ietf.org/html/rfc6901#section-3
*/
function JSONPTR(strings, ...toEscape) {
return strings.reduce((left, right, i) => left + fast_json_patch_1.escapePathComponent(toEscape[i - 1]) + right);
}
exports.JSONPTR = JSONPTR;
/**
* Makes documentation string from symbol display part array returned by TS
*/
function docstring(parts) {
return ts.displayPartsToString(parts);
}
exports.docstring = docstring;
/**
* Normalizes path to match POSIX standard (slashes)
* This conversion should only be necessary to convert windows paths when calling TS APIs.
*/
function toUnixPath(filePath) {
return filePath.replace(/\\/g, '/');
}
exports.toUnixPath = toUnixPath;
/**
* Normalizes URI encoding by encoding _all_ special characters in the pathname
*/
function normalizeUri(uri) {
const parts = url.parse(uri);
if (!parts.pathname) {
return uri;
}
const pathParts = parts.pathname.split('/').map(segment => encodeURIComponent(decodeURIComponent(segment)));
// Decode Windows drive letter colon
if (/^[a-z]%3A$/i.test(pathParts[1])) {
pathParts[1] = decodeURIComponent(pathParts[1]);
}
parts.pathname = pathParts.join('/');
return url.format(parts);
}
exports.normalizeUri = normalizeUri;
/**
* Converts an abolute path to a file:// uri
*
* @param path an absolute path
*/
function path2uri(path) {
// Require a leading slash, on windows prefixed with drive letter
if (!/^(?:[a-z]:)?[\\\/]/i.test(path)) {
throw new Error(`${path} is not an absolute path`);
}
const parts = path.split(/[\\\/]/);
// If the first segment is a Windows drive letter, prefix with a slash and skip encoding
let head = parts.shift();
if (head !== '') {
head = '/' + head;
}
else {
head = encodeURIComponent(head);
}
return `file://${head}/${parts.map(encodeURIComponent).join('/')}`;
}
exports.path2uri = path2uri;
/**
* Converts a uri to an absolute path.
* The OS style is determined by the URI. E.g. `file:///c:/foo` always results in `c:\foo`
*
* @param uri a file:// uri
*/
function uri2path(uri) {
const parts = url.parse(uri);
if (parts.protocol !== 'file:') {
throw new Error('Cannot resolve non-file uri to path: ' + uri);
}
let filePath = parts.pathname || '';
// If the path starts with a drive letter, return a Windows path
if (/^\/[a-z]:\//i.test(filePath)) {
filePath = filePath.substr(1).replace(/\//g, '\\');
}
return decodeURIComponent(filePath);
}
exports.uri2path = uri2path;
const jstsPattern = /\.[tj]sx?$/;
function isJSTSFile(filename) {
return jstsPattern.test(filename);
}
exports.isJSTSFile = isJSTSFile;
const jstsConfigPattern = /(^|\/)[tj]sconfig\.json$/;
function isConfigFile(filename) {
return jstsConfigPattern.test(filename);
}
exports.isConfigFile = isConfigFile;
const packageJsonPattern = /(^|\/)package\.json$/;
function isPackageJsonFile(filename) {
return packageJsonPattern.test(filename);
}
exports.isPackageJsonFile = isPackageJsonFile;
const globalTSPatterns = [
/(^|\/)globals?\.d\.ts$/,
/node_modules\/(?:\@|%40)types\/(node|jasmine|jest|mocha)\/.*\.d\.ts$/,
/(^|\/)typings\/.*\.d\.ts$/,
/(^|\/)tsd\.d\.ts($|\/)/,
/(^|\/)tslib\.d\.ts$/,
];
// isGlobalTSFile returns whether or not the filename contains global
// variables based on a best practices heuristic
// (https://basarat.gitbooks.io/typescript/content/docs/project/modules.html). In
// reality, a file has global scope if it does not begin with an
// import statement, but to check this, we'd have to read each
// TypeScript file.
function isGlobalTSFile(filename) {
for (const globalTSPattern of globalTSPatterns) {
if (globalTSPattern.test(filename)) {
return true;
}
}
return false;
}
exports.isGlobalTSFile = isGlobalTSFile;
function isDependencyFile(filename) {
return filename.startsWith('node_modules/') || filename.indexOf('/node_modules/') !== -1;
}
exports.isDependencyFile = isDependencyFile;
function isDeclarationFile(filename) {
return filename.endsWith('.d.ts');
}
exports.isDeclarationFile = isDeclarationFile;
/**
* Compares two values and returns a numeric score between 0 and 1 defining of how well they match.
* E.g. if 2 of 4 properties in the query match, will return 2
*/
function getMatchingPropertyCount(query, value) {
// Compare strings by similarity
// This allows to match a path like "lib/foo/bar.d.ts" with "src/foo/bar.ts"
// Last check is a workaround for https://github.com/aceakash/string-similarity/issues/6
if (typeof query === 'string' && typeof value === 'string' && !(query.length <= 1 && value.length <= 1)) {
return string_similarity_1.compareTwoStrings(query, value);
}
// If query is a scalar value, compare by identity and return 0 or 1
if (typeof query !== 'object' || query === null) {
return +(query === value);
}
// If value is scalar, return no match
if (typeof value !== 'object' && value !== null) {
return 0;
}
// Both values are objects, compare each property and sum the scores
return Object.keys(query).reduce((score, key) => score + getMatchingPropertyCount(query[key], value[key]), 0);
}
exports.getMatchingPropertyCount = getMatchingPropertyCount;
/**
* Returns the maximum score that could be achieved with the given query (the amount of "leaf" properties)
* E.g. for `{ name, kind, package: { name }}` will return 3
*/
function getPropertyCount(query) {
if (typeof query === 'object' && query !== null) {
return Object.keys(query).reduce((score, key) => score + getPropertyCount(query[key]), 0);
}
return 1;
}
exports.getPropertyCount = getPropertyCount;
/**
* Returns true if the passed SymbolDescriptor has at least the same properties as the passed partial SymbolDescriptor
*/
function isSymbolDescriptorMatch(query, symbol) {
for (const key of Object.keys(query)) {
if (!query[key]) {
continue;
}
if (key === 'package') {
if (!symbol.package || !isPackageDescriptorMatch(query.package, symbol.package)) {
return false;
}
continue;
}
if (query[key] !== symbol[key]) {
return false;
}
}
return true;
}
exports.isSymbolDescriptorMatch = isSymbolDescriptorMatch;
function isPackageDescriptorMatch(query, pkg) {
for (const key of Object.keys(query)) {
if (query[key] === undefined) {
continue;
}
if (query[key] !== pkg[key]) {
return false;
}
}
return true;
}
//# sourceMappingURL=util.js.map