prime31
9/30/2015 - 10:14 PM

Atom.io Haxe package autocomplete modifications. Just replace the provider.js file from the package with this file. All modifications have t

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