UNPKG

atom-nuclide

Version:

A unified developer experience for web and mobile development, built as a suite of features on top of Atom to provide hackability and the support of an active community.

350 lines (300 loc) 12.4 kB
Object.defineProperty(exports, '__esModule', { value: true }); /* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. */ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); exports.getCachedHackLanguageForUri = getCachedHackLanguageForUri; var getHackLanguageForUri = _asyncToGenerator(function* (uri) { if (uri == null || uri.length === 0) { return null; } var key = getKeyOfUri(uri); if (key == null) { return null; } return yield createHackLanguageIfNotExisting(key, uri); }); exports.getHackLanguageForUri = getHackLanguageForUri; var createHackLanguageIfNotExisting = _asyncToGenerator(function* (key, fileUri) { var hackLanguage = uriToHackLanguage.get(key); if (hackLanguage == null) { var hackEnvironment = yield (0, (_utils2 || _utils()).getHackEnvironmentDetails)(fileUri); // If multiple calls were done asynchronously, then return the single-created HackLanguage. hackLanguage = uriToHackLanguage.get(key); if (hackLanguage == null) { hackLanguage = createHackLanguage(hackEnvironment.hackService, hackEnvironment.isAvailable, hackEnvironment.hackRoot); uriToHackLanguage.set(key, hackLanguage); } } return hackLanguage; } // Must clear the cache when servers go away. // TODO: Could be more precise about this and only clear those entries // for the closed connection. ); exports.clearHackLanguageCache = clearHackLanguageCache; function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { var callNext = step.bind(null, 'next'); var callThrow = step.bind(null, 'throw'); function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(callNext, callThrow); } } callNext(); }); }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _nuclideRemoteConnection2; function _nuclideRemoteConnection() { return _nuclideRemoteConnection2 = require('../../nuclide-remote-connection'); } var _commonsNodeNuclideUri2; function _commonsNodeNuclideUri() { return _commonsNodeNuclideUri2 = _interopRequireDefault(require('../../commons-node/nuclideUri')); } var _utils2; function _utils() { return _utils2 = require('./utils'); } var _atom2; function _atom() { return _atom2 = require('atom'); } var _nuclideLogging2; function _nuclideLogging() { return _nuclideLogging2 = require('../../nuclide-logging'); } var _TypedRegions2; function _TypedRegions() { return _TypedRegions2 = require('./TypedRegions'); } var _assert2; function _assert() { return _assert2 = _interopRequireDefault(require('assert')); } /** * Serves language requests from HackService. * Note that all line/column values are 1 based. */ var HackLanguage = (function () { /** * `basePath` should be the directory where the .hhconfig file is located. */ function HackLanguage(hackService, hhAvailable, basePath) { _classCallCheck(this, HackLanguage); this._hackService = hackService; this._hhAvailable = hhAvailable; this._basePath = basePath; } _createClass(HackLanguage, [{ key: 'dispose', value: function dispose() {} }, { key: 'getCompletions', value: _asyncToGenerator(function* (filePath, contents, offset, line, column) { var completions = yield this._hackService.getCompletions(filePath, contents, offset, line, column); if (completions == null) { return []; } return processCompletions(completions, contents, offset); }) }, { key: 'formatSource', value: _asyncToGenerator(function* (contents, startPosition, endPosition) { var path = this._basePath; if (path == null) { throw new Error('No Hack provider for this file.'); } var response = yield this._hackService.formatSource(path, contents, startPosition, endPosition); if (response == null) { throw new Error('Error formatting hack source.'); } else if (response.error_message !== '') { throw new Error('Error formatting hack source: ' + response.error_message); } return response.result; }) }, { key: 'highlightSource', value: _asyncToGenerator(function* (filePath, contents, line, col) { var response = yield this._hackService.getSourceHighlights(filePath, contents, line, col); if (response == null) { return []; } return response.map(hackRangeToAtomRange); }) }, { key: 'getDiagnostics', value: _asyncToGenerator(function* (filePath, contents) { try { var result = yield this._hackService.getDiagnostics(filePath, contents); if (result == null) { (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)().error('hh_client could not be reached'); return []; } return result; } catch (err) { (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)().error(err); return []; } }) }, { key: 'getTypeCoverage', value: _asyncToGenerator(function* (filePath) { var regions = yield this._hackService.getTypedRegions(filePath); return (0, (_TypedRegions2 || _TypedRegions()).convertTypedRegionsToCoverageResult)(regions); }) }, { key: 'getIdeOutline', value: function getIdeOutline(filePath, contents) { return this._hackService.getIdeOutline(filePath, contents); } }, { key: 'getIdeDefinition', value: _asyncToGenerator(function* (filePath, contents, lineNumber, column) { var definitions = yield this._hackService.getDefinition(filePath, contents, lineNumber, column); if (definitions == null) { return []; } function convertDefinition(def) { (0, (_assert2 || _assert()).default)(def.definition_pos != null); return { name: def.name, path: def.definition_pos.filename, line: def.definition_pos.line, column: def.definition_pos.char_start, queryRange: hackRangeToAtomRange(def.pos) }; } return definitions.filter(function (definition) { return definition.definition_pos != null; }).map(convertDefinition); }) }, { key: 'getType', value: _asyncToGenerator(function* (filePath, contents, expression, lineNumber, column) { if (!expression.startsWith('$')) { return null; } var result = yield this._hackService.getTypeAtPos(filePath, contents, lineNumber, column); return result == null ? null : result.type; }) }, { key: 'findReferences', value: _asyncToGenerator(function* (filePath, contents, line, column) { var references = yield this._hackService.findReferences(filePath, contents, line, column); if (references == null || references.length === 0) { return null; } (0, (_assert2 || _assert()).default)(this._basePath != null); return { baseUri: this._basePath, symbolName: references[0].name, references: references }; }) }, { key: 'getBasePath', value: function getBasePath() { return this._basePath; } }, { key: 'isHackAvailable', value: function isHackAvailable() { return this._hhAvailable; } }]); return HackLanguage; })(); exports.HackLanguage = HackLanguage; function hackRangeToAtomRange(position) { return new (_atom2 || _atom()).Range([position.line - 1, position.char_start - 1], [position.line - 1, position.char_end]); } function matchTypeOfType(type) { // strip parens if present if (type[0] === '(' && type[type.length - 1] === ')') { return type.substring(1, type.length - 1); } return type; } function escapeName(name) { return name.replace(/\\/g, '\\\\'); } function paramSignature(params) { var paramStrings = params.map(function (param) { return param.type + ' ' + param.name; }); return '(' + paramStrings.join(', ') + ')'; } function matchSnippet(name, params) { var escapedName = escapeName(name); if (params != null) { // Construct the snippet: e.g. myFunction(${1:$arg1}, ${2:$arg2}); var paramsString = params.map(function (param, index) { return '${' + (index + 1) + ':' + param.name + '}'; }).join(', '); return escapedName + '(' + paramsString + ')'; } else { return escapedName; } } // Returns the length of the largest match between a suffix of contents // and a prefix of match. function matchLength(contents, match) { for (var i = match.length; i > 0; i--) { var toMatch = match.substring(0, i); if (contents.endsWith(toMatch)) { return i; } } return 0; } function processCompletions(completionsResponse, contents, offset) { var contentsLine = contents.substring(contents.lastIndexOf('\n', offset - 1) + 1, offset).toLowerCase(); return completionsResponse.map(function (completion) { var name = completion.name; var type = completion.type; var func_details = completion.func_details; var commonResult = { displayText: name, replacementPrefix: contents.substring(offset - matchLength(contentsLine, name.toLowerCase()), offset), description: matchTypeOfType(type) }; if (func_details != null) { return _extends({}, commonResult, { snippet: matchSnippet(name, func_details.params), leftLabel: func_details.return_type, rightLabel: paramSignature(func_details.params), type: 'function' }); } else { return _extends({}, commonResult, { snippet: matchSnippet(name), rightLabel: matchTypeOfType(type) }); } }); } /** * This is responsible for managing (creating/disposing) multiple HackLanguage instances, * creating the designated HackService instances with the NuclideClient it needs per remote project. * Also, it deelegates the language feature request to the correct HackLanguage instance. */ var uriToHackLanguage = new Map(); // dummy key into uriToHackLanguage for local projects. // Any non-remote NuclideUri will do. // TODO: I suspect we should key the local service off of the presence of a .hhconfig file // rather than having a single HackLanguage for all local requests. Regardless, we haven't tested // local hack services so save that for another day. var LOCAL_URI_KEY = 'local-hack-key'; function createHackLanguage(hackService, hhAvailable, basePath) { return new HackLanguage(hackService, hhAvailable, basePath); } // Returns null if we can't get the key at this time because the RemoteConnection is initializing. // This can happen on startup when reloading remote files. function getKeyOfUri(uri) { var remoteConnection = (_nuclideRemoteConnection2 || _nuclideRemoteConnection()).RemoteConnection.getForUri(uri); return remoteConnection == null ? (_commonsNodeNuclideUri2 || _commonsNodeNuclideUri()).default.isRemote(uri) ? null : LOCAL_URI_KEY : remoteConnection.getUriForInitialWorkingDirectory(); } function getCachedHackLanguageForUri(uri) { var key = getKeyOfUri(uri); return key == null ? null : uriToHackLanguage.get(uri); } function clearHackLanguageCache() { uriToHackLanguage.clear(); } // Range in the input where the symbol reference occurs.