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.
302 lines (259 loc) • 11.6 kB
JavaScript
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 _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; }; })();
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
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'); } }
var _atom2;
function _atom() {
return _atom2 = require('atom');
}
var _commonsNodeCollection2;
function _commonsNodeCollection() {
return _commonsNodeCollection2 = require('../../commons-node/collection');
}
var _nuclideAnalytics2;
function _nuclideAnalytics() {
return _nuclideAnalytics2 = require('../../nuclide-analytics');
}
var _nuclideClangRpc2;
function _nuclideClangRpc() {
return _nuclideClangRpc2 = require('../../nuclide-clang-rpc');
}
var _libclang2;
function _libclang() {
return _libclang2 = require('./libclang');
}
var MAX_LINE_LENGTH = 120;
var TAB_LENGTH = 2;
var VALID_EMPTY_SUFFIX = /(->|\.|::|\()$/;
var ClangCursorToAutocompletionTypes = Object.freeze({
STRUCT_DECL: 'class',
UNION_DECL: 'class',
CLASS_DECL: 'class',
ENUM_DECL: 'class',
FIELD_DECL: 'property',
ENUM_CONSTANT_DECL: 'constant',
FUNCTION_DECL: 'function',
VAR_DECL: 'variable',
PARM_DECL: 'variable',
OBJC_INTERFACE_DECL: 'class',
OBJC_CATEGORY_DECL: 'class',
OBJC_PROTOCOL_DECL: 'class',
OBJC_PROPERTY_DECL: 'property',
OBJC_IVAR_DECL: 'variable',
OBJC_INSTANCE_METHOD_DECL: 'method',
OBJC_CLASS_METHOD_DECL: 'method',
OBJC_IMPLEMENTATION_DECL: 'class',
OBJC_CATEGORY_IMPL_DECL: 'class',
TYPEDEF_DECL: 'type',
CXX_METHOD: 'method',
CONSTRUCTOR: 'method',
DESTRUCTOR: 'method',
FUNCTION_TEMPLATE: 'function',
CLASS_TEMPLATE: 'class',
OVERLOAD_CANDIDATE: 'function'
});
function getCompletionBody(completion, columnOffset, indentation) {
var inlineBody = getCompletionBodyInline(completion);
var multiLineBody = getCompletionBodyMultiLine(completion, columnOffset, indentation);
if (columnOffset + inlineBody.length > MAX_LINE_LENGTH && multiLineBody) {
return multiLineBody;
}
return inlineBody;
}
function getCompletionBodyMultiLine(completion, columnOffset, indentation) {
// Filter out whitespace chunks.
var chunks = completion.chunks.filter(function (chunk) {
return chunk.spelling.trim();
});
// We only handle completions in which non-placeholder and placeholder
// chunks alternate, starting with non-placeholder chunk.
if (chunks.length % 2) {
return null;
}
// Group non-placeholders and placeholders into groups of two.
// One of each.
var args = [];
for (var i = 0, n = chunks.length / 2; i < n; ++i) {
var firstChunk = chunks[i * 2];
var secondChunk = chunks[i * 2 + 1];
if (firstChunk.isPlaceHolder || !secondChunk.isPlaceHolder) {
return null;
}
// If firstChunk ends with colon remove it because we add it manually later.
var _text = firstChunk.spelling;
var _placeholder = secondChunk.spelling;
if (_text.endsWith(':')) {
_text = _text.substring(0, _text.length - 1);
}
// All rows but the first one should be indented at least 2 extra levels.
// To get that we add dummy leading spaces to those rows.
if (i > 0) {
_text = ' '.repeat(2 * TAB_LENGTH) + _text;
}
args.push({
text: _text,
placeholder: _placeholder,
offset: i === 0 ? columnOffset : indentation * TAB_LENGTH
});
}
return _convertArgsToMultiLineSnippet(args);
}
function _convertArgsToMultiLineSnippet(args) {
// We have two types of multine line method calls.
//
// 1. Here first argument is the longest, so everything can be
// aligned nicely:
// [self ArgumentOne:arg1
// arg2:arg2
// Argument3:arg3]
//
// 2. Here first argument is not the longest, but we still don't move it.
// Only rule here is that colons in remaining rows are aligned:
// [self Arg1:arg1
// arg2:arg2
// Argument3:arg3]
//
var colonPosition = Math.max.apply(null, args.map(function (arg) {
return arg.offset + arg.text.length;
}));
return args.reduce(function (body, arg, index) {
var spacesCnt = index === 0 ? 0 : colonPosition - arg.offset - arg.text.length;
if (spacesCnt < 0) {
throw Error('This is a bug! Spaces count is negative.');
}
var line = '' + ' '.repeat(spacesCnt) + arg.text + ':${' + (index + 1) + ':' + arg.placeholder + '}\n';
if (index > 0 && line[colonPosition - arg.offset] !== ':') {
throw Error('This is a bug! Colons are not aligned!');
}
return body + line;
}, '');
}
function getCompletionBodyInline(completion) {
// Make a copy to avoid mutating the original.
var chunks = [].concat(_toConsumableArray(completion.chunks));
// Merge everything between the last non-optional placeholder
// and the last optional placeholder into one big optional.
var lastOptional = (0, (_commonsNodeCollection2 || _commonsNodeCollection()).arrayFindLastIndex)(chunks, function (chunk) {
return Boolean(chunk.isOptional && chunk.isPlaceHolder);
});
if (lastOptional !== -1) {
var lastNonOptional = (0, (_commonsNodeCollection2 || _commonsNodeCollection()).arrayFindLastIndex)(chunks, function (chunk) {
return Boolean(!chunk.isOptional && chunk.isPlaceHolder);
});
if (lastNonOptional !== -1 && lastNonOptional < lastOptional) {
var mergedSpelling = '';
for (var i = lastNonOptional + 1; i <= lastOptional; i++) {
mergedSpelling += chunks[i].spelling;
}
chunks.splice(lastNonOptional + 1, lastOptional - lastNonOptional, {
spelling: mergedSpelling,
isPlaceHolder: true,
isOptional: true
});
}
}
var body = '';
var placeHolderCnt = 0;
chunks.forEach(function (chunk) {
if (chunk.isPlaceHolder) {
placeHolderCnt++;
var spelling = chunk.spelling;
if (chunk.isOptional) {
spelling = '[' + spelling + ']';
}
body += '${' + placeHolderCnt + ':' + spelling + '}';
} else {
body += chunk.spelling;
}
});
return body;
}
function getCompletionPrefix(editor) {
var cursor = editor.getLastCursor();
var range = cursor.getCurrentWordBufferRange({
wordRegex: cursor.wordRegExp({ includeNonWordCharacters: false })
});
// Current word might go beyond the cursor, so we cut it.
range.end = new (_atom2 || _atom()).Point(cursor.getBufferRow(), cursor.getBufferColumn());
return editor.getTextInBufferRange(range).trim();
}
var AutocompleteHelpers = (function () {
function AutocompleteHelpers() {
_classCallCheck(this, AutocompleteHelpers);
}
_createDecoratedClass(AutocompleteHelpers, null, [{
key: 'getAutocompleteSuggestions',
decorators: [(0, (_nuclideAnalytics2 || _nuclideAnalytics()).trackTiming)('nuclide-clang-atom.autocomplete')],
value: _asyncToGenerator(function* (request) {
var editor = request.editor;
var _request$bufferPosition = request.bufferPosition;
var row = _request$bufferPosition.row;
var column = _request$bufferPosition.column;
var activatedManually = request.activatedManually;
var prefix = getCompletionPrefix(editor);
// Only autocomplete empty strings when it's a method (a.?, a->?) or qualifier (a::?),
// or function call (f(...)).
if (!activatedManually && prefix === '') {
var wordPrefix = editor.getLastCursor().getCurrentWordPrefix();
if (!VALID_EMPTY_SUFFIX.test(wordPrefix)) {
return [];
}
}
var indentation = editor.indentationForBufferRow(row);
var data = yield (0, (_libclang2 || _libclang()).getCompletions)(editor, prefix);
if (data == null) {
return [];
}
return data.map(function (completion) {
var snippet = undefined;
var displayText = undefined;
// For function argument completions, strip out everything before the current parameter.
// Ideally we'd use the replacement prefix, but this is a hard problem in C++:
// e.g. min<decltype(x)>(x, y) is a perfectly valid function call.
if (completion.cursor_kind === 'OVERLOAD_CANDIDATE') {
var curParamIndex = completion.chunks.findIndex(function (x) {
return x.kind === 'CurrentParameter';
});
if (curParamIndex !== -1) {
completion.chunks.splice(0, curParamIndex);
snippet = getCompletionBody(completion, column, indentation);
} else {
// Function had no arguments.
snippet = ')';
}
displayText = completion.spelling;
} else {
snippet = getCompletionBody(completion, column, indentation);
}
var rightLabel = completion.cursor_kind ? (_nuclideClangRpc2 || _nuclideClangRpc()).ClangCursorToDeclarationTypes[completion.cursor_kind] : null;
var type = completion.cursor_kind ? ClangCursorToAutocompletionTypes[completion.cursor_kind] : null;
return {
snippet: snippet,
displayText: displayText,
replacementPrefix: prefix,
type: type,
leftLabel: completion.result_type,
rightLabel: rightLabel,
description: completion.brief_comment || completion.result_type
};
});
})
}]);
return AutocompleteHelpers;
})();
exports.default = AutocompleteHelpers;
var __test__ = {
getCompletionBodyMultiLine: getCompletionBodyMultiLine,
getCompletionBodyInline: getCompletionBodyInline
};
exports.__test__ = __test__;