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.

197 lines (162 loc) 8.92 kB
Object.defineProperty(exports, '__esModule', { value: true }); 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 _createDecoratedClass = (function () { function defineProperties(target, descriptors, initializers) { for (var i = 0; i < descriptors.length; i++) { var descriptor = descriptors[i]; var decorators = descriptor.decorators; var key = descriptor.key; delete descriptor.key; delete descriptor.decorators; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor || descriptor.initializer) descriptor.writable = true; if (decorators) { for (var f = 0; f < decorators.length; f++) { var decorator = decorators[f]; if (typeof decorator === 'function') { descriptor = decorator(target, key, descriptor) || descriptor; } else { throw new TypeError('The decorator for method ' + descriptor.key + ' is of the invalid type ' + typeof decorator); } } if (descriptor.initializer !== undefined) { initializers[key] = descriptor; continue; } } Object.defineProperty(target, key, descriptor); } } return function (Constructor, protoProps, staticProps, protoInitializers, staticInitializers) { if (protoProps) defineProperties(Constructor.prototype, protoProps, protoInitializers); if (staticProps) defineProperties(Constructor, staticProps, staticInitializers); return Constructor; }; })(); var fetchCompletionsForEditor = _asyncToGenerator(function* (editor, prefix) { var hackLanguage = yield (0, (_HackLanguage2 || _HackLanguage()).getHackLanguageForUri)(editor.getPath()); var filePath = editor.getPath(); if (!hackLanguage || !filePath) { return []; } (0, (_assert2 || _assert()).default)(filePath); var contents = editor.getText(); var cursor = editor.getLastCursor(); var position = cursor.getBufferPosition(); var offset = editor.getBuffer().characterIndexForPosition(position); // The returned completions may have unrelated results, even though the offset is set on the end // of the prefix. var completions = yield hackLanguage.getCompletions(filePath, contents, offset, position.row + 1, position.column + 1); // Filter out the completions that do not contain the prefix as a token in the match text case // insentively. var tokenLowerCase = prefix.toLowerCase(); var hackCompletionsComparator = compareHackCompletions(prefix); return completions.filter(function (completion) { (0, (_assert2 || _assert()).default)(completion.displayText != null); return completion.displayText.toLowerCase().indexOf(tokenLowerCase) >= 0; }) // Sort the auto-completions based on a scoring function considering: // case sensitivity, position in the completion, private functions and alphabetical order. .sort(function (completion1, completion2) { return hackCompletionsComparator(completion1, completion2); }); }); exports.compareHackCompletions = compareHackCompletions; 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 }; } /* * 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 _atom2; function _atom() { return _atom2 = require('atom'); } var _nuclideAnalytics2; function _nuclideAnalytics() { return _nuclideAnalytics2 = require('../../nuclide-analytics'); } var _HackLanguage2; function _HackLanguage() { return _HackLanguage2 = require('./HackLanguage'); } var _assert2; function _assert() { return _assert2 = _interopRequireDefault(require('assert')); } var FIELD_ACCESSORS = ['->', '::']; var PREFIX_LOOKBACK = Math.max.apply(null, FIELD_ACCESSORS.map(function (prefix) { return prefix.length; })); var AutocompleteProvider = (function () { function AutocompleteProvider() { _classCallCheck(this, AutocompleteProvider); } /** * Returns true if `bufferPosition` is prefixed with any of the passed `checkPrefixes`. */ _createDecoratedClass(AutocompleteProvider, [{ key: 'getAutocompleteSuggestions', decorators: [(0, (_nuclideAnalytics2 || _nuclideAnalytics()).trackTiming)('hack.getAutocompleteSuggestions')], value: _asyncToGenerator(function* (request) { var editor = request.editor; var bufferPosition = request.bufferPosition; var replacementPrefix = findHackPrefix(editor); if (!replacementPrefix && !hasPrefix(editor, bufferPosition, FIELD_ACCESSORS, PREFIX_LOOKBACK)) { return []; } var completions = yield fetchCompletionsForEditor(editor, replacementPrefix); return completions.map(function (completion) { return _extends({}, completion, { replacementPrefix: completion.replacementPrefix === '' ? replacementPrefix : completion.replacementPrefix }); }); }) }]); return AutocompleteProvider; })(); exports.default = AutocompleteProvider; function hasPrefix(editor, bufferPosition, checkPrefixes, prefixLookback) { var priorChars = editor.getTextInBufferRange(new (_atom2 || _atom()).Range(new (_atom2 || _atom()).Point(bufferPosition.row, bufferPosition.column - prefixLookback), bufferPosition)); return checkPrefixes.some(function (prefix) { return priorChars.endsWith(prefix); }); } function findHackPrefix(editor) { var cursor = editor.getLastCursor(); // We use custom wordRegex to adopt php variables starting with $. var currentRange = cursor.getCurrentWordBufferRange({ wordRegex: /(\$\w*)|\w+/ }); // Current word might go beyond the cursor, so we cut it. var range = new (_atom2 || _atom()).Range(currentRange.start, new (_atom2 || _atom()).Point(cursor.getBufferRow(), cursor.getBufferColumn())); var prefix = editor.getTextInBufferRange(range).trim(); // Prefix could just be $ or ends with string literal. if (prefix === '$' || !/[\W]$/.test(prefix)) { return prefix; } else { return ''; } } var MATCH_PREFIX_CASE_SENSITIVE_SCORE = 6; var MATCH_PREFIX_CASE_INSENSITIVE_SCORE = 4; var MATCH_TOKEN_CASE_SENSITIVE_SCORE = 2; var MATCH_TOKEN_CASE_INSENSITIVE_SCORE = 0; var MATCH_PRIVATE_FUNCTION_PENALTY = -4; var MATCH_APLHABETICAL_SCORE = 1; function compareHackCompletions(token) { var tokenLowerCase = token.toLowerCase(); return function (completion1, completion2) { // Prefer completions with larger prefixes. (0, (_assert2 || _assert()).default)(completion1.replacementPrefix != null); (0, (_assert2 || _assert()).default)(completion2.replacementPrefix != null); var prefixComparison = completion2.replacementPrefix.length - completion1.replacementPrefix.length; if (prefixComparison !== 0) { return prefixComparison; } (0, (_assert2 || _assert()).default)(completion1.displayText != null); (0, (_assert2 || _assert()).default)(completion2.displayText != null); var texts = [completion1.displayText, completion2.displayText]; var scores = texts.map(function (text, i) { if (text.startsWith(token)) { // Matches starting with the prefix gets the highest score. return MATCH_PREFIX_CASE_SENSITIVE_SCORE; } else if (text.toLowerCase().startsWith(tokenLowerCase)) { // Ignore case score matches gets a good score. return MATCH_PREFIX_CASE_INSENSITIVE_SCORE; } var score = undefined; if (text.indexOf(token) !== -1) { // Small score for a match that contains the token case-sensitive. score = MATCH_TOKEN_CASE_SENSITIVE_SCORE; } else { // Zero score for a match that contains the token without case-sensitive matching. score = MATCH_TOKEN_CASE_INSENSITIVE_SCORE; } // Private functions gets negative score. if (text.startsWith('_')) { score += MATCH_PRIVATE_FUNCTION_PENALTY; } return score; }); // Finally, consider the alphabetical order, but not higher than any other score. if (texts[0] < texts[1]) { scores[0] += MATCH_APLHABETICAL_SCORE; } else { scores[1] += MATCH_APLHABETICAL_SCORE; } return scores[1] - scores[0]; }; }