Created
September 30, 2015 22:14
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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