Before: Save g:ale_completion_autoimport Save g:ale_completion_max_suggestions let g:ale_completion_max_suggestions = 50 After: Restore unlet! b:ale_completion_info Execute(Should handle Rust completion results correctly): AssertEqual \ [ \ {'word': 'new', 'menu': 'pub fn new() -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'with_capacity', 'menu': 'pub fn with_capacity(capacity: usize) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_utf8', 'menu': 'pub fn from_utf8(vec: Vec) -> Result', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_utf8_lossy', 'menu': 'pub fn from_utf8_lossy<''a>(v: &''a [u8]) -> Cow<''a, str>', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_utf16', 'menu': 'pub fn from_utf16(v: &[u16]) -> Result', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_utf16_lossy', 'menu': 'pub fn from_utf16_lossy(v: &[u16]) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_raw_parts', 'menu': 'pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_utf8_unchecked', 'menu': 'pub unsafe fn from_utf8_unchecked(bytes: Vec) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_iter', 'menu': 'fn from_iter>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_iter', 'menu': 'fn from_iter>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_iter', 'menu': 'fn from_iter>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_iter', 'menu': 'fn from_iter>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from_iter', 'menu': 'fn from_iter>>(iter: I) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'Searcher', 'menu': 'type Searcher = <&''b str as Pattern<''a>>::Searcher;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'default', 'menu': 'fn default() -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = String;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Output', 'menu': 'type Output = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Target', 'menu': 'type Target = str;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'Err', 'menu': 'type Err = ParseError;', 'info': '', 'kind': 't', 'icase': 1}, \ {'word': 'from_str', 'menu': 'fn from_str(s: &str) -> Result', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from', 'menu': 'fn from(s: &''a str) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from', 'menu': 'fn from(s: Box) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \ {'word': 'from', 'menu': 'fn from(s: Cow<''a, str>) -> String', 'info': '', 'kind': 'f', 'icase': 1}, \], \ ale#completion#ParseLSPCompletions({ \ "jsonrpc":"2.0", \ "id":65, \ "result":[ \ { \ "label":"new", \ "kind":3, \ "detail":"pub fn new() -> String" \ }, \ { \ "label":"with_capacity", \ "kind":3, \ "detail":"pub fn with_capacity(capacity: usize) -> String" \ }, \ { \ "label":"from_utf8", \ "kind":3, \ "detail":"pub fn from_utf8(vec: Vec) -> Result" \ }, \ { \ "label":"from_utf8_lossy", \ "kind":3, \ "detail":"pub fn from_utf8_lossy<'a>(v: &'a [u8]) -> Cow<'a, str>" \ }, \ { \ "label":"from_utf16", \ "kind":3, \ "detail":"pub fn from_utf16(v: &[u16]) -> Result" \ }, \ { \ "label":"from_utf16_lossy", \ "kind":3, \ "detail":"pub fn from_utf16_lossy(v: &[u16]) -> String" \ }, \ { \ "label":"from_raw_parts", \ "kind":3, \ "detail":"pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String" \ }, \ { \ "label":"from_utf8_unchecked", \ "kind":3, \ "detail":"pub unsafe fn from_utf8_unchecked(bytes: Vec) -> String" \ }, \ { \ "label":"from_iter", \ "kind":3, \ "detail":"fn from_iter>(iter: I) -> String" \ }, \ { \ "label":"from_iter", \ "kind":3, \ "detail":"fn from_iter>(iter: I) -> String" \ }, \ { \ "label":"from_iter", \ "kind":3, \ "detail":"fn from_iter>(iter: I) -> String" \ }, \ { \ "label":"from_iter", \ "kind":3, \ "detail":"fn from_iter>(iter: I) -> String" \ }, \ { \ "label":"from_iter", \ "kind":3, \ "detail":"fn from_iter>>(iter: I) -> String" \ }, \ { \ "label":"Searcher", \ "kind":8, \ "detail":"type Searcher = <&'b str as Pattern<'a>>::Searcher;" \ }, \ { \ "label":"default", \ "kind":3, \ "detail":"fn default() -> String" \ }, \ { \ "label":"Output", \ "kind":8, \ "detail":"type Output = String;" \ }, \ { \ "label":"Output", \ "kind":8, \ "detail":"type Output = str;" \ }, \ { \ "label":"Output", \ "kind":8, \ "detail":"type Output = str;" \ }, \ { \ "label":"Output", \ "kind":8, \ "detail":"type Output = str;" \ }, \ { \ "label":"Output", \ "kind":8, \ "detail":"type Output = str;" \ }, \ { \ "label":"Output", \ "kind":8, \ "detail":"type Output = str;" \ }, \ { \ "label":"Output", \ "kind":8, \ "detail":"type Output = str;" \ }, \ { \ "label":"Target", \ "kind":8, \ "detail":"type Target = str;" \ }, \ { \ "label":"Err", \ "kind":8, \ "detail":"type Err = ParseError;" \ }, \ { \ "label":"from_str", \ "kind":3, \ "detail":"fn from_str(s: &str) -> Result" \ }, \ { \ "label":"from", \ "kind":3, \ "detail":"fn from(s: &'a str) -> String" \ }, \ { \ "label":"from", \ "kind":3, \ "detail":"fn from(s: Box) -> String" \ }, \ { \ "label":"from", \ "kind":3, \ "detail":"fn from(s: Cow<'a, str>) -> String" \ } \ ] \ }) Execute(Should handle Python completion results correctly): let b:ale_completion_info = { \ 'completion_filter': 'ale#completion#python#CompletionItemFilter', \} AssertEqual \ [ \ {'word': 'what', 'menu': 'example-python-project.bar.Bar', 'info': "what()\n\n", 'kind': 'f', 'icase': 1}, \ ], \ ale#completion#ParseLSPCompletions({ \ "jsonrpc":"2.0", \ "id":6, \ "result":{ \ "isIncomplete":v:false, \ "items":[ \ { \ "label":"what()", \ "kind":3, \ "detail":"example-python-project.bar.Bar", \ "documentation":"what()\n\n", \ "sortText":"awhat", \ "insertText":"what" \ }, \ { \ "label":"__class__", \ "kind":7, \ "detail":"object", \ "documentation":"type(object_or_name, bases, dict)\ntype(object) -> the object's type\ntype(name, bases, dict) -> a new type", \ "sortText":"z__class__", \ "insertText":"__class__" \ }, \ { \ "label":"__delattr__(name)", \ "kind":3, \ "detail":"object", \ "documentation":"Implement delattr(self, name).", \ "sortText":"z__delattr__", \ "insertText":"__delattr__" \ }, \ { \ "label":"__dir__()", \ "kind":3, \ "detail":"object", \ "documentation":"__dir__() -> list\ndefault dir() implementation", \ "sortText":"z__dir__", \ "insertText":"__dir__" \ }, \ { \ "label":"__doc__", \ "kind":18, \ "detail":"object", \ "documentation":"str(object='') -> str\nstr(bytes_or_buffer[, encoding[, errors]]) -> str\n\nCreate a new string object from the given object. If encoding or\nerrors is specified, then the object must expose a data buffer\nthat will be decoded using the given encoding and error handler.\nOtherwise, returns the result of object.__str__() (if defined)\nor repr(object).\nencoding defaults to sys.getdefaultencoding().\nerrors defaults to 'strict'.", \ "sortText":"z__doc__", \ "insertText":"__doc__" \ }, \ { \ "label":"__eq__(value)", \ "kind":3, \ "detail":"object", \ "documentation":"Return self==value.", \ "sortText":"z__eq__", \ "insertText":"__eq__" \ }, \ { \ "label":"__format__()", \ "kind":3, \ "detail":"object", \ "documentation":"default object formatter", \ "sortText":"z__format__", \ "insertText":"__format__" \ }, \ { \ "label":"__ge__(value)", \ "kind":3, \ "detail":"object", \ "documentation":"Return self>=value.", \ "sortText":"z__ge__", \ "insertText":"__ge__" \ }, \ { \ "label":"__getattribute__(name)", \ "kind":3, \ "detail":"object", \ "documentation":"Return getattr(self, name).", \ "sortText":"z__getattribute__", \ "insertText":"__getattribute__" \ }, \ { \ "label":"__gt__(value)", \ "kind":3, \ "detail":"object", \ "documentation":"Return self>value.", \ "sortText":"z__gt__", \ "insertText":"__gt__" \ }, \ { \ "label":"__hash__()", \ "kind":3, \ "detail":"object", \ "documentation":"Return hash(self).", \ "sortText":"z__hash__", \ "insertText":"__hash__" \ }, \ { \ "label":"__init__(args, kwargs)", \ "kind":3, \ "detail":"object", \ "documentation":"Initialize self.\u00a0\u00a0See help(type(self)) for accurate signature.", \ "sortText":"z__init__", \ "insertText":"__init__" \ }, \ { \ "label":"__init_subclass__()", \ "kind":3, \ "detail":"object", \ "documentation":"This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", \ "sortText":"z__init_subclass__", \ "insertText":"__init_subclass__" \ }, \ { \ "label":"__le__(value)", \ "kind":3, \ "detail":"object", \ "documentation":"Return self<=value.", \ "sortText":"z__le__", \ "insertText":"__le__" \ }, \ { \ "label":"__lt__(value)", \ "kind":3, \ "detail":"object", \ "documentation":"Return self int\nsize of object in memory, in bytes", \ "sortText":"z__sizeof__", \ "insertText":"__sizeof__" \ }, \ { \ "label":"__str__()", \ "kind":3, \ "detail":"object", \ "documentation":"Return str(self).", \ "sortText":"z__str__", \ "insertText":"__str__" \ }, \ { \ "label":"__subclasshook__()", \ "kind":3, \ "detail":"object", \ "documentation":"Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented.\u00a0\u00a0If it returns\nNotImplemented, the normal algorithm is used.\u00a0\u00a0Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", \ "sortText":"z__subclasshook__", \ "insertText":"__subclasshook__" \ } \ ] \ } \ }) Execute(Should handle Python completion results correctly): let b:ale_completion_info = { \ 'completion_filter': 'ale#completion#python#CompletionItemFilter', \ 'prefix': 'mig', \} AssertEqual \ [ \ {'word': 'migrations', 'menu': 'xxx', 'info': 'migrations', 'kind': 'f', 'icase': 1}, \ {'word': 'MigEngine', 'menu': 'xxx', 'info': 'mig engine', 'kind': 'f', 'icase': 1}, \ ], \ ale#completion#ParseLSPCompletions({ \ 'jsonrpc': '2.0', \ 'id': 6, \ 'result': { \ 'isIncomplete': v:false, \ 'items': [ \ { \ 'label': 'migrations', \ 'kind': 3, \ 'detail': 'xxx', \ 'documentation': 'migrations', \ }, \ { \ 'label': 'MigEngine', \ 'kind': 3, \ 'detail': 'xxx', \ 'documentation': 'mig engine', \ }, \ { \ 'label': 'ignore me', \ 'kind': 3, \ 'detail': 'nope', \ 'documentation': 'nope', \ }, \ ] \ } \ }) Execute(Should handle missing keys): AssertEqual \ [ \ {'word': 'x', 'menu': '', 'info': '', 'kind': 'v', 'icase': 1}, \ ], \ ale#completion#ParseLSPCompletions({ \ 'jsonrpc': '2.0', \ 'id': 6, \ 'result': { \ 'isIncomplete': v:false, \ 'items': [ \ { \ 'label': 'x', \ }, \ ] \ } \ }) Execute(Should handle documentation in the markdown format): AssertEqual \ [ \ {'word': 'migrations', 'menu': 'xxx', 'info': 'Markdown documentation', 'kind': 'f', 'icase': 1}, \ ], \ ale#completion#ParseLSPCompletions({ \ 'jsonrpc': '2.0', \ 'id': 6, \ 'result': { \ 'isIncomplete': v:false, \ 'items': [ \ { \ 'label': 'migrations', \ 'kind': 3, \ 'detail': 'xxx', \ 'documentation': { \ 'kind': 'markdown', \ 'value': 'Markdown documentation', \ }, \ }, \ ], \ }, \ }) Execute(Should handle completion messages with textEdit objects): AssertEqual \ [ \ {'word': 'next_callback', 'menu': 'PlayTimeCallback', 'info': '', 'kind': 'v', 'icase': 1}, \ ], \ ale#completion#ParseLSPCompletions({ \ 'id': 226, \ 'jsonrpc': '2.0', \ 'result': { \ 'isIncomplete': v:false, \ 'items': [ \ { \ 'detail': 'PlayTimeCallback', \ 'filterText': 'next_callback', \ 'insertText': 'ignoreme', \ 'insertTextFormat': 1, \ 'kind': 6, \ 'label': ' next_callback', \ 'sortText': '3ee19999next_callback', \ 'textEdit': { \ 'newText': 'next_callback', \ 'range': { \ 'end': {'character': 13, 'line': 12}, \ 'start': {'character': 4, 'line': 12}, \ }, \ }, \ }, \ ], \ }, \ }) Execute(Should handle completion messages with the deprecated insertText attribute): AssertEqual \ [ \ {'word': 'next_callback', 'menu': 'PlayTimeCallback', 'info': '', 'kind': 'v', 'icase': 1}, \ ], \ ale#completion#ParseLSPCompletions({ \ 'id': 226, \ 'jsonrpc': '2.0', \ 'result': { \ 'isIncomplete': v:false, \ 'items': [ \ { \ 'detail': 'PlayTimeCallback', \ 'filterText': 'next_callback', \ 'insertText': 'next_callback', \ 'insertTextFormat': 1, \ 'kind': 6, \ 'label': ' next_callback', \ 'sortText': '3ee19999next_callback', \ }, \ ], \ }, \ }) Execute(Should handle completion messages with additionalTextEdits when ale_completion_autoimport is turned on): let g:ale_completion_autoimport = 1 let b:ale_completion_info = {'line': 30} AssertEqual \ [ \ { \ 'word': 'next_callback', \ 'menu': 'PlayTimeCallback', \ 'info': '', \ 'kind': 'v', \ 'icase': 1, \ 'user_data': json_encode({ \ 'codeActions': [ \ { \ 'description': 'completion', \ 'changes': [ \ { \ 'fileName': expand('#' . bufnr('') . ':p'), \ 'textChanges': [ \ { \ 'start': { \ 'line': 11, \ 'offset': 2, \ }, \ 'end': { \ 'line': 13, \ 'offset': 4, \ }, \ 'newText': 'from "module" import next_callback', \ }, \ ], \ }, \ ], \ }, \ ], \ }), \ }, \ ], \ ale#completion#ParseLSPCompletions({ \ 'id': 226, \ 'jsonrpc': '2.0', \ 'result': { \ 'isIncomplete': v:false, \ 'items': [ \ { \ 'detail': 'PlayTimeCallback', \ 'filterText': 'next_callback', \ 'insertText': 'next_callback', \ 'insertTextFormat': 1, \ 'kind': 6, \ 'label': ' next_callback', \ 'sortText': '3ee19999next_callback', \ 'additionalTextEdits': [ \ { \ 'range': { \ 'start': { \ 'line': 29, \ 'character': 10, \ }, \ 'end': { \ 'line': 29, \ 'character': 10, \ }, \ }, \ 'newText': 'next_callback', \ }, \ { \ 'range': { \ 'start': { \ 'line': 10, \ 'character': 1, \ }, \ 'end': { \ 'line': 12, \ 'character': 3, \ }, \ }, \ 'newText': 'from "module" import next_callback', \ }, \ ], \ }, \ ], \ }, \ }) Execute(Should not handle completion messages with additionalTextEdits when ale_completion_autoimport is turned off): let g:ale_completion_autoimport = 0 let b:ale_completion_info = {'line': 30} AssertEqual \ [], \ ale#completion#ParseLSPCompletions({ \ 'id': 226, \ 'jsonrpc': '2.0', \ 'result': { \ 'isIncomplete': v:false, \ 'items': [ \ { \ 'detail': 'PlayTimeCallback', \ 'filterText': 'next_callback', \ 'insertText': 'next_callback', \ 'insertTextFormat': 1, \ 'kind': 6, \ 'label': ' next_callback', \ 'sortText': '3ee19999next_callback', \ 'additionalTextEdits': [ \ { \ 'range': { \ 'start': { \ 'line': 29, \ 'character': 10, \ }, \ 'end': { \ 'line': 29, \ 'character': 10, \ }, \ }, \ 'newText': 'next_callback', \ }, \ { \ 'range': { \ 'start': { \ 'line': 10, \ 'character': 1, \ }, \ 'end': { \ 'line': 12, \ 'character': 3, \ }, \ }, \ 'newText': 'from "module" import next_callback', \ }, \ ], \ }, \ ], \ }, \ }) Execute(Should still handle completion messages with additionalTextEdits with ale_completion_autoimport turned off, if edits all start on the line): let g:ale_completion_autoimport = 0 let b:ale_completion_info = {'line': 30} AssertEqual \ [ \ { \ 'word': 'next_callback', \ 'menu': 'PlayTimeCallback', \ 'info': '', \ 'kind': 'v', \ 'icase': 1, \ } \ ], \ ale#completion#ParseLSPCompletions({ \ 'id': 226, \ 'jsonrpc': '2.0', \ 'result': { \ 'isIncomplete': v:false, \ 'items': [ \ { \ 'detail': 'PlayTimeCallback', \ 'filterText': 'next_callback', \ 'insertText': 'next_callback', \ 'insertTextFormat': 1, \ 'kind': 6, \ 'label': ' next_callback', \ 'sortText': '3ee19999next_callback', \ 'additionalTextEdits': [ \ { \ 'range': { \ 'start': { \ 'line': 29, \ 'character': 10, \ }, \ 'end': { \ 'line': 29, \ 'character': 10, \ }, \ }, \ 'newText': 'next_callback', \ }, \ ], \ }, \ ], \ }, \ })