Skip to content

Instantly share code, notes, and snippets.

@prime31
Created September 30, 2015 22:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save prime31/1e251e204d2e57507999 to your computer and use it in GitHub Desktop.
Save prime31/1e251e204d2e57507999 to your computer and use it in GitHub Desktop.
Atom.io Haxe package autocomplete modifications. Just replace the provider.js file from the package with this file. All modifications have the comment MIKEWASHERE for easy identification
// node built in
var path = require('path')
, crypto = require('crypto')
// lib code
, query = require('./query')
, debug = require('./debug')
, file = require('./file')
, state = require('../haxe-state')
, signatures = require('../parsing/signatures')
, escape = require('../utils/escape-html')
, code = require('../utils/haxe-code')
, compiler = require('../parsing/compiler')
// dep code
, xml2js = require('xml2js')
, fs = require('fs-extra')
, filter = require('fuzzaldrin').filter
var REGEX_ENDS_WITH_DOT_IDENTIFIER = /\.([a-zA-Z_0-9]*)$/;
var REGEX_ENDS_WITH_DOT_NUMBER = /[^a-zA-Z0-9_\]\)]([\.0-9]+)$/;
var REGEX_ENDS_WITH_PARTIAL_PACKAGE_DECL = /[^a-zA-Z0-9_]package\s+([a-zA-Z_0-9]+(\.[a-zA-Z_0-9]+)*)\.([a-zA-Z_0-9]*)$/;
var REGEX_BEGINS_WITH_KEY = /^([a-zA-Z0-9_]+)\s*\:/;
var REGEX_ENDS_WITH_ALPHANUMERIC = /([A-Za-z0-9_]+)$/;
module.exports = {
selector: '.source.haxe',
disableForSelector: '.source.haxe .comment',
inclusionPriority: 2,
excludeLowerPriority: true,
prefixes:['.','('],
last_completion_index: null,
last_key_path: null,
last_has_partial_key: false,
last_suggestions: null,
last_suggestions_kind: null,
last_pretext: null,
last_mode: null,
// Set to true to use extended completion
// on anonymous structures as argument
use_extended_completion: true,
getSuggestions : function(opt) {
if(!state.hxml_cwd) return [];
if(!state.hxml_content && !state.hxml_file) return [];
var _buffer_file = opt.editor.buffer.file;
if(!_buffer_file) return [];
var _file = _buffer_file.path;
if(!_file) return [];
return new Promise( function(resolve, reject) {
var buffer_pos = opt.bufferPosition.toArray();
var pretext = opt.editor.getTextInBufferRange( [ [0,0], buffer_pos] );
var text = opt.editor.getText();
var index = pretext.length;
var mode = null;
var position_info = this._position_info_from_text_and_index(pretext, index);
// If a new index is provided, use it
if (position_info != null) {
index = position_info.index;
if (position_info.mode != null) {
mode = position_info.mode;
}
//if (position_info.call != null) {
// console.log(position_info.call);
//}
} else {
// Nothing to query from the current index
return resolve([]);
}
// Get current key_path and partial_key
var key_path = null, has_partial_key = false;
if (position_info != null && position_info.call != null) {
key_path = position_info.call.key_path;
has_partial_key = (position_info.call.partial_key != null);
}
// Check if we should run haxe autocomplete again
if (this.last_completion_index != null) {
if (this.last_completion_index === index && this.last_pretext.slice(0, index) === pretext.slice(0, index) && this.last_mode === mode) {
var should_recompute_completion = true;
// Compare last key path/partial key with the current one
if (((key_path != null && this.last_key_path != null && key_path.join(',') === this.last_key_path.join(','))
|| (key_path == null && this.last_key_path == null)) && this.last_has_partial_key == has_partial_key) {
// Key path is the same as before
should_recompute_completion = false;
}
if (!should_recompute_completion) {
// No need to recompute haxe completion
// Just filter and resolve
var filtered = this._filter_suggestions(this.last_suggestions, this.last_suggestions_kind, opt, position_info);
return resolve(filtered);
}
}
}
// Keep useful values for later completion
this.last_pretext = pretext;
this.last_completion_index = index;
this.last_key_path = key_path;
this.last_has_partial_key = has_partial_key;
this.last_mode = mode;
// We need to perform a new haxe query
// Save file first
var fetch, save_info;
var use_external_file = atom.config.get('haxe.completion_avoid_saving_original_file');
if (use_external_file) {
// Completion using an external file
save_info = file.save_tmp_file_for_completion_of_original_file(opt.editor.buffer.file.path, text);
fetch = query.get({
file: save_info.file_path,
byte: Buffer.byteLength(save_info.contents.slice(0, index + save_info.contents.length - text.length), 'utf8'),
mode: mode,
add_args: ['-cp', save_info.cp_path]
});
}
else {
// `classic` way of saving the file
save_info = this._save_for_completion(opt.editor, _file);
fetch = query.get({
file: save_info.file,
byte: Buffer.byteLength(pretext.slice(0, index), 'utf8'),
mode: mode,
add_args:[]
});
}
fetch.then(function(data) {
var parse = this._parse_suggestions(opt, data, position_info);
parse.then(function(result) {
resolve(result);
}).catch(function(e) {
reject(e);
});
}.bind(this)).catch(reject).then(function() {
if (use_external_file) {
file.remove_tmp_file(save_info);
}
else {
this._restore_post_completion(save_info);
}
}.bind(this)); //then
}.bind(this)); //promise
}, //getSuggestions
onDidInsertSuggestion: function(options) {
// Get editor state
var editor = options.editor;
var position = options.triggerPosition;
var buffer_pos = editor.getLastCursor().getBufferPosition().toArray();
var suggestion = options.suggestion;
if (this.use_extended_completion && (this.last_mode !== 'toplevel' && this.last_key_path != null || this.last_has_partial_key) && suggestion.className != 'haxe-autocomplete-suggestion-type-hint') {
// When inserting a structure key, add a colon, unless it already exists
var following_text = editor.getTextInBufferRange([ [position.row,position.column], editor.getBuffer().getEndPosition().toArray() ]);
REGEX_BEGINS_WITH_KEY.lastIndex = -1;
if (!REGEX_BEGINS_WITH_KEY.test(following_text)) {
editor.insertText(': ');
}
} else {
// When inserting a signature as snippet, remove the contents of the signature
// because it is usually annoying, especially with optional arguments.
// The type hinting should be enough to know what to type next
// And in case the developer really wants to restore the full snippet,
// ctrl/cmd + z shortcut will do this job
var range = [ [position.row,position.column], buffer_pos ];
var inserted_text = editor.getTextInBufferRange(range);
var sig_start = inserted_text.indexOf('(');
if (sig_start > -1) {
var following_text = editor.getTextInBufferRange([ [position.row,position.column], editor.getBuffer().getEndPosition().toArray() ])
var sig_end = following_text.indexOf(')')
if (sig_end != -1) {
inserted_text = following_text.slice(sig_start + 1, sig_end);
editor.setTextInBufferRange([ [position.row,position.column+sig_start+1], [position.row,position.column+sig_start+inserted_text.length+1] ], '');
// Ensure the cursor is inside the parenthesis
editor.setCursorBufferPosition([position.row,position.column+sig_start+1]);
}
}
}
}, //onDidInsertSuggestion
// Compute and return the position info from the current text and index
// such as the current replacement prefix
_position_info_from_text_and_index: function(text, index) {
REGEX_ENDS_WITH_DOT_IDENTIFIER.lastIndex = -1;
var m, call_info;
// We only care about the text before index
text = text.slice(0, index);
// Don't provide suggestions if inside a string or comment
text = code.code_with_empty_comments_and_strings(text);
// Look for a dot
if (m = text.match(REGEX_ENDS_WITH_DOT_IDENTIFIER)) {
// Don't query haxe when writing a number containing dots
REGEX_ENDS_WITH_DOT_NUMBER.lastIndex = -1;
if ((' '+text).match(REGEX_ENDS_WITH_DOT_NUMBER)) {
return null;
}
// Don't query haxe when writing a package declaration
REGEX_ENDS_WITH_PARTIAL_PACKAGE_DECL.lastIndex = -1;
if ((' '+text).match(REGEX_ENDS_WITH_PARTIAL_PACKAGE_DECL)) {
return null;
}
return {
index: (index - m[1].length),
prefix: m[1]
};
}
// Look for parens open
else if (call_info = code.parse_partial_signature(text, index)) {
var prefix = '';
if (call_info.partial_key != null) {
prefix = call_info.partial_key;
// Don't provide completion right after comma in this case
// because it looks confusing
if (call_info.partial_key.length == 0) {
var c = text.charAt(index - 1);
if (c == ',') {
return null;
}
}
}
else {
if (call_info.key_path != null && call_info.key_path.length > 0) {
REGEX_ENDS_WITH_ALPHANUMERIC.lastIndex = -1;
if (m = text.match(REGEX_ENDS_WITH_ALPHANUMERIC)) {
prefix = m[1];
} else {
return null;
}
return {
index: (call_info.signature_start + 1),
prefix: prefix,
mode: 'toplevel'
};
}
else if (call_info.key_path == null && call_info.partial_arg != null) {
REGEX_ENDS_WITH_ALPHANUMERIC.lastIndex = -1;
if (m = text.match(REGEX_ENDS_WITH_ALPHANUMERIC)) {
prefix = m[1];
} else {
return null;
}
return {
index: index - prefix.length,
prefix: prefix,
mode: 'toplevel',
call: call_info
};
}
}
return {
index: (call_info.signature_start + 1),
prefix: prefix,
call: call_info
};
}
else {
REGEX_ENDS_WITH_ALPHANUMERIC.lastIndex = -1;
if (m = text.match(REGEX_ENDS_WITH_ALPHANUMERIC)) {
prefix = m[1];
} else {
return null;
}
return {
index: index - prefix.length,
prefix: prefix,
mode: 'toplevel'
};
}
}, //_position_info_from_text_and_index
_save_for_completion: function(_editor, _file) {
var filepath = path.dirname(_file);
var filename = path.basename(_file);
var tmpname = '.' + filename;
var tempfile = path.join(filepath, tmpname);
fs.copySync(_file, tempfile);
var _code = _editor.getText();
var b = new Buffer(_code, 'utf8');
var freal = fs.openSync(_file, 'w');
fs.writeSync(freal, b, 0, b.length, 0);
fs.closeSync(freal);
freal = null;
return {
tempfile: tempfile,
file: _file
}
}, //_save_for_completion
_restore_post_completion: function(save_info) {
debug.query('remove ' + save_info.tempfile);
if(fs.existsSync(save_info.tempfile)) {
fs.deleteSync(save_info.tempfile);
}
}, //_restore_post_completion
_parse_suggestions: function(opt, content, position_info) {
return new Promise(function(resolve,reject) {
var has_list = content.indexOf('<list>') != -1;
var has_type = content.indexOf('<type>') != -1;
var has_toplevel_list = content.indexOf('<il>') != -1;
if(!has_list && !has_type && !has_toplevel_list) {
// Apparently no completion from this point
var suggestions = [];
if (position_info == null || position_info.mode !== 'toplevel') {
// Try to extract haxe errors from output
var haxe_errors = compiler.parse_output(content);
if (haxe_errors.length > 0) {
var message = haxe_errors[0].message;
var message_words = message.split(' ');
if (message_words.length != 2 || message_words[0] != 'Unexpected') {
// Don't display errors like `Unexpected (` because they are happening only
// because we are in the middle of typing an expression.
// However we want to keep the other errors as they can tell the user
// why he doesn't get autocomplete suggestion.
suggestions.push({
rightLabelHTML: escape(message),
text: '', // No text as we will perform no replacemennt and only label will be visible
replacementPrefix: '',
className: 'haxe-autocomplete-suggestion-error'
});
}
}
}
resolve(suggestions);
debug.query('autocomplete response: ' + content);
// because the error could come from the server not being ready
// or other temporary state, reset completion info to be clean during next call
this.last_completion_index = null;
this.last_suggestions = null;
this.last_suggestions_kind = null;
} else {
xml2js.parseString(content, function (err, json) {
// TODO care about err
var info = this._parse_haxe_completion_json(json);
if (this.use_extended_completion && position_info != null && position_info.call != null && position_info.call.key_path != null) {
// Extended completion with key path
if (info.type != null && info.type.args != null) {
// Extract the type we want to use to get key path completion
var structure_type = info.type.args[position_info.call.number_of_args - 1];
if (structure_type == null) {
return resolve([]);
}
if (typeof(structure_type) == 'object' && structure_type.type != null) {
structure_type = structure_type.type;
}
structure_type = code.string_from_parsed_type(structure_type);
// When having no partial key, we just want
// to hint the current type
var used_key_path = [].concat(position_info.call.key_path);
if (position_info.call.partial_key == null) {
used_key_path.pop();
}
// Fetch instance completion list
var fetch_instance_list = function(type, key_path, imports) {
// Create a temporary file to get completion list
var save_info = file.save_tmp_file_for_completion_list_of_instance_type(type, key_path, imports);
// Query completion server
var fetch = query.get({
file: save_info.file_path,
byte: Buffer.byteLength(save_info.contents, 'utf8'),
add_args: ['-cp', save_info.cp_path]
});
fetch.then(function(content) {
// Remove temporary file
file.remove_tmp_file(save_info);
var has_list = content.indexOf('<list>') != -1;
if (!has_list) {
return resolve([]);
}
xml2js.parseString(content, function(err, json) {
// TODO care about err
var info = this._parse_haxe_completion_json(json);
if (info.list != null) {
var suggestions = [];
// Fill suggestions
this._add_suggestions_with_list(info.list, suggestions, {ignore_methods: true});
this.last_suggestions_kind = 'list';
// Keep computed suggestions for later use
this.last_suggestions = suggestions;
var filtered = this._filter_suggestions(this.last_suggestions, this.last_suggestions_kind, opt, position_info);
resolve(filtered);
} else {
resolve([]);
}
}.bind(this));
}.bind(this)).catch(reject);
}.bind(this);
var last_dot_index = structure_type.lastIndexOf('.');
if (last_dot_index != -1 && structure_type.trim().charAt(0) !== '{') {
// Because haxe server gives the package, not the module as response
// We need to import sub-packages and then query completion on the type
// This wouldn't be required if the completion response was giving us the full type path
// At the moment, better have something that works with one more query than something not working.
var package_name = structure_type.slice(0, last_dot_index);
var base_type_name = structure_type.slice(last_dot_index + 1);
// Create a temporary file to get completion list
var save_info = file.save_tmp_file_for_completion_list_of_package(package_name);
// Query completion server
var fetch = query.get({
file: save_info.file_path,
byte: Buffer.byteLength(save_info.contents, 'utf8'),
add_args: ['-cp', save_info.cp_path]
});
fetch.then(function(content) {
// Remove temporary file
file.remove_tmp_file(save_info);
var has_list = content.indexOf('<list>') != -1;
if (!has_list) {
return resolve([]);
}
xml2js.parseString(content, function(err, json) {
// TODO care about err
var info = this._parse_haxe_completion_json(json);
if (info.list != null) {
// Compute imports list
var imports_list = [];
for (var i = 0; i < info.list.length; i++) {
imports_list.push(package_name + '.' + info.list[i].name);
}
// We've got the list of imports, query instance type
fetch_instance_list(base_type_name, used_key_path, imports_list);
} else {
resolve([]);
}
}.bind(this));
}.bind(this));
}
else {
// No need to fetch package's sub-packages
// Fetch instance's list directly
fetch_instance_list(structure_type, used_key_path);
}
}
else {
return resolve([]);
}
} else {
// Regular completion
var suggestions = [];
// If the info is a list
if (info.list != null) {
this._add_suggestions_with_list(info.list, suggestions);
this.last_suggestions_kind = 'list';
}
else if (info.type != null) {
this._add_suggestion_with_type(info.type, suggestions);
this.last_suggestions_kind = 'type';
}
// Keep computed suggestions for later use
this.last_suggestions = suggestions;
// Filter and resolve
var filtered = this._filter_suggestions(this.last_suggestions, this.last_suggestions_kind, opt, position_info);
resolve(filtered);
}
}.bind(this)); //parseString
} //contains <list>
}.bind(this)); //promise
}, //_parse_suggestions
_parse_haxe_completion_json: function(json) {
var result;
if (json.list) {
result = {list: []};
for (var i = 0; i < json.list.i.length; ++i) {
var node = json.list.i[i];
var name = node.$.n;
var kind = node.$.k;
var type = node.t.length ? node.t[0] : null;
var description = node.d; // MIKEWASHERE
var entry = {
name: name,
type: type
};
if (kind) {
entry.kind = kind;
}
// MIKEWASHERE
if (description) {
entry.description = String(description).trim();
}
result.list.push(entry);
}
return result;
} else if (json.type) {
var argtypes = String(json.type).trim();
var info = code.parse_composed_type(argtypes);
return {
type: info
};
}
else if (json.il) {
result = {list: []};
for (var i = 0; i < json.il.i.length; i++) {
var raw_entry = json.il.i[i];
var name = raw_entry._;
var type = (raw_entry.$.t || raw_entry.$.p);
if (raw_entry.$.k === 'package') {
type = 'package';
}
if (type != 'atom_tempfile__' && name != 'atom_tempfile__') {
result.list.push({
name: name,
type: type,
kind: raw_entry.$.k
});
}
}
return result;
}
return result;
}, //parse_haxe_completion_json
_add_suggestions_with_list: function(list, suggestions, options) {
for (var i = 0; i < list.length; ++i) {
var item = list[i];
var name = item.name;
var type = code.parse_composed_type(item.type);
var right = null, text = null, snippet = null;
var suggestionType = null, description = null, left = null; // MIKEWASHERE
// If the type is a method type
if (type.args != null) {
// Ignore methods?
if (options != null && options.ignore_methods) {
continue;
}
// Format method arguments
var dumped_args = [];
var number_of_chars = name.length;
for (var j = 0; j < type.args.length; j++) {
var arg = type.args[j];
var arg_str = '';
if (arg != null) {
if (arg.name != null) {
arg_str += arg.name;
}
else {
arg_str += 'arg' + (j+1)
}
if (arg.optional) {
arg_str = '?' + arg_str;
}
number_of_chars += arg_str.length + 2;
if (number_of_chars > 40) {
arg_str = arg_str.slice(0, arg_str.length - number_of_chars + 42);
if (arg_str.length > 2) {
// Strip the argument completely when only 2 characters ar left
// Otherwise, it would look too ugly
arg_str += '\u2026';
}
else {
arg_str = '\u2026';
}
}
arg_str = '${' + (j + 1) + ':' + arg_str + '}';
}
dumped_args.push(arg_str);
// If there are too many arguments in list, don't display all of them
if (number_of_chars > 40) {
break;
}
}
// When suggesting a function, use a snippet in order to get nicer output
// and to have the cursor put inside the parenthesis when confirming
right = code.string_from_parsed_type(type.type);
if (dumped_args.length > 0) {
snippet = name + '(' + dumped_args.join(', ') + ')';
} else {
text = name + '()';
}
}
else {
// Otherwise just format the type
right = code.string_from_parsed_type(type);
text = name;
}
// MIKEWASHERE
// figure out the proper suggestionType. available types: variable, constant, property, value, method (function), class(type),
// keyword (builtin), tag, snippet, import, require
switch( item.kind )
{
case 'package':
suggestionType = 'import';
break;
case 'local':
suggestionType = 'variable';
break;
case 'var':
case 'member':
suggestionType = 'property';
break;
case 'static':
suggestionType = 'constant'; // wrong! but it will do for differentiation.
break;
case 'type':
case 'enum':
suggestionType = 'type';
break;
case 'method':
suggestionType = 'method';
break;
default:
console.log( 'found unhandled item.kind' );
console.log( item );
suggestionType = '';
break;
}
if( item.description != null && item.description.length > 0 )
description = item.description;
// END-MIKEWASHERE
// Don't display Unknown types
if (right === 'Unknown' || right.slice(0, 8) === 'Unknown<') {
right = '';
} else {
// MIKEWASHERE
// if we have a method, leave the type hint on the right else move it to the left
if( suggestionType != 'method' )
{
left = right;
right = null;
}
// END-MIKEWASHERE
}
// if (item.kind != null && item.kind !== right) {
// if (right.length > 0) {
// right = '(' + item.kind + ') ' + right;
// } else {
// right = item.kind;
// }
// }
// Create final suggestion
var suggestion = {
replacementPrefix: '',
rightLabel: right,
className: 'haxe-autocomplete-suggestion-list-item',
leftLabel: left, // MIKEWASHERE
type: suggestionType, // MIKEWASHERE
description: description // MIKEWASHERE
};
if (snippet != null) {
suggestion.snippet = snippet;
}
else {
suggestion.text = text;
}
suggestions.push(suggestion);
} //each item*/
}, //_add_suggestions_with_list
_add_suggestion_with_type: function(type, suggestions) {
// Compute list of arguments in signature
var displayed_type;
if (type.args != null && type.args.length > 0) {
var call_args = [];
for (var i = 0; i < type.args.length; i++) {
var arg = type.args[i];
var arg_str = arg.name;
if (arg_str == null) {
arg_str = 'arg' + (i + 1)
}
if (arg.optional) {
arg_str = '?' + arg_str;
}
if (arg.type != null) {
var type_str = code.string_from_parsed_type(arg.type);
// Don't display Unknown types
if (type_str !== 'Unknown' && type_str.slice(0, 8) !== 'Unknown<') {
arg_str = arg_str + ': ' + type_str;
}
}
call_args.push(arg_str);
}
displayed_type = call_args.join(', ');
}
else {
displayed_type = 'no parameters';
}
suggestions.push({
_call_args: call_args, // for later argument highlighting when filtering
rightLabelHTML: escape(displayed_type),
text: '_', // No text as we will perform no replacemennt and only label will be visible
snippet: '',
replacementPrefix: '',
className: 'haxe-autocomplete-suggestion-type-hint'
});
},
_filter_suggestions: function(prev_suggestions, prev_suggestions_kind, options, position_info) {
var prev_suggestion, suggestion, i;
// No sugggestions to filter? return empty array
if (prev_suggestions == null) {
return [];
}
// Return key path type hint if needed
if (position_info != null
&& position_info.call != null
&& position_info.call.partial_key == null
&& position_info.call.key_path != null
&& position_info.call.key_path.length > 0) {
var key = position_info.call.key_path[position_info.call.key_path.length - 1];
// Look for a completion element with the same key
for (i = 0; i < prev_suggestions.length; i++) {
prev_suggestion = prev_suggestions[i];
if (prev_suggestion.text == key) {
if (prev_suggestion.rightLabel != null) {
// Found it. Create a type hint element from it and return only this one
return [{
rightLabelHTML: '<span class="current-argument">' + escape(prev_suggestion.text) + ': ' + escape(prev_suggestion.rightLabel) + '</span>',
text: '', // No text as we will perform no replacemennt and only label will be visible
replacementPrefix: '',
className: 'haxe-autocomplete-suggestion-type-hint'
}];
}
return [];
}
}
}
// Update prefix if needed
var prefix = options.prefix;
if (position_info != null && position_info.prefix != null) {
prefix = position_info.prefix;
}
else if (this.prefixes.indexOf(prefix) != -1) {
prefix = '';
}
// Create filterable suggestions
var suggestions = []
for (i = 0; i < prev_suggestions.length; i++) {
prev_suggestion = prev_suggestions[i];
suggestion = {};
// MIKEWASHERE
// type
if (prev_suggestion.type != null) {
suggestion.type = prev_suggestion.type;
}
// description
if (prev_suggestion.description != null) {
suggestion.description = prev_suggestion.description;
}
// leftLabel
if (prev_suggestion.leftLabel != null) {
suggestion.leftLabel = prev_suggestion.leftLabel;
}
// END-MIKEWASHERE
// text
if (prev_suggestion.text != null) {
suggestion.text = prev_suggestion.text;
}
// snippet
if (prev_suggestion.snippet != null) {
suggestion.snippet = prev_suggestion.snippet;
}
// label
if (prev_suggestion.rightLabel != null) {
suggestion.rightLabel = prev_suggestion.rightLabel;
}
// label (html)
if (prev_suggestion.rightLabelHTML != null) {
suggestion.rightLabelHTML = prev_suggestion.rightLabelHTML;
}
// class name
if (prev_suggestion.className != null) {
suggestion.className = prev_suggestion.className;
}
// replacement prefix
if (prev_suggestions_kind === 'list') {
suggestion.replacementPrefix = prefix;
} else {
// When doing type hinting, no replacement will be done
suggestion.replacementPrefix = '';
}
// call args
if (prev_suggestion._call_args != null) {
suggestion._call_args = prev_suggestion._call_args;
}
// filter key for fuzzaldrin
if (suggestion.snippet != null) {
suggestion._filter = suggestion.snippet;
} else if (suggestion.text != null) {
suggestion._filter = suggestion.text;
}
suggestions.push(suggestion);
}
// Filter suggestions if needed
if (prev_suggestions_kind === 'list' && prefix != null && prefix.length > 0) {
suggestions = filter(suggestions, prefix, {key: '_filter'});
}
// When making suggestions for anonymous structures,
// remove suggestions that match the already used keys
if (prev_suggestions_kind === 'list'
&& position_info != null
&& position_info.call != null
&& position_info.call.used_keys != null
&& position_info.call.used_keys.length > 0) {
var filtered_suggestions = [];
var forbidden_keys = {};
for (i = 0; i < position_info.call.used_keys.length; i++) {
forbidden_keys[position_info.call.used_keys[i]] = true;
}
for (i = 0; i < suggestions.length; i++) {
suggestion = suggestions[i];
if (!forbidden_keys[suggestion.text]) {
filtered_suggestions.push(suggestion);
}
}
suggestions = filtered_suggestions;
}
// Highlight argument if doing type hinting
if (prev_suggestions_kind === 'type' && position_info != null && position_info.call != null && suggestions.length === 1) {
suggestion = suggestions[0];
if (suggestion._call_args != null && suggestion._call_args.length > 0) {
var formatted_args = [];
var current_arg_index = position_info.call.number_of_args - 1;
for (i = 0; i < suggestion._call_args.length; i++) {
var arg = suggestion._call_args[i];
if (i === current_arg_index) {
formatted_args.push('<span class="current-argument">' + escape(arg) + '</span>');
} else {
formatted_args.push(escape(arg));
}
}
suggestion.rightLabelHTML = formatted_args.join(', ');
}
}
return suggestions;
} //_filter_suggestions
} //module.exports
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment