summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorw0rp <devw0rp@gmail.com>2019-05-21 13:53:09 +0100
committerw0rp <devw0rp@gmail.com>2019-05-21 13:53:09 +0100
commit3e3801e81ef70d3b35b7c527fbd1b4f62d50bae5 (patch)
tree79ce1ca62632864002e002bef38e957fe46025bb
parent89db85121c001fc60787647f012978a2328816a5 (diff)
downloadale-3e3801e81ef70d3b35b7c527fbd1b4f62d50bae5.zip
Revert "Fix #2492 - Remove all Deoplete support for now"
This reverts commit 975cc7af8fbabe234a220c84e56b7ff719d8d959.
-rw-r--r--README.md16
-rw-r--r--doc/ale.txt12
-rw-r--r--rplugin/python3/deoplete/sources/ale.py54
-rw-r--r--test/python/test_deoplete_source.py147
-rwxr-xr-xtest/script/custom-checks10
5 files changed, 235 insertions, 4 deletions
diff --git a/README.md b/README.md
index 84439b71..76e9212e 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ features, including:
* Diagnostics (via Language Server Protocol linters)
* Go To Definition (`:ALEGoToDefinition`)
-* Completion (Built in completion support)
+* Completion (Built in completion support, or with Deoplete)
* Finding references (`:ALEFindReferences`)
* Hover information (`:ALEHover`)
* Symbol search (`:ALESymbolSearch`)
@@ -159,12 +159,24 @@ ALE offers some support for completion via hijacking of omnicompletion while you
type. All of ALE's completion information must come from Language Server
Protocol linters, or from `tsserver` for TypeScript.
-ALE offers its own automatic completion support, which does not require any
+ALE integrates with [Deoplete](https://github.com/Shougo/deoplete.nvim) as a
+completion source, named `'ale'`. You can configure Deoplete to only use ALE as
+the source of completion information, or mix it with other sources.
+
+```vim
+" Use ALE and also some plugin 'foobar' as completion sources for all code.
+let g:deoplete#sources = {'_': ['ale', 'foobar']}
+```
+
+ALE also offers its own automatic completion support, which does not require any
other plugins, and can be enabled by changing a setting before ALE is loaded.
```vim
" Enable completion where available.
" This setting must be set before ALE is loaded.
+"
+" You should not turn this setting on if you wish to use ALE as a completion
+" source for other completion plugins, like Deoplete.
let g:ale_completion_enabled = 1
```
diff --git a/doc/ale.txt b/doc/ale.txt
index ac3661fc..4bb34947 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -334,7 +334,14 @@ ALE offers support for automatic completion of code while you type.
Completion is only supported while at least one LSP linter is enabled. ALE
will only suggest symbols provided by the LSP servers.
-ALE offers its own completion implementation, which does not require any
+ *ale-deoplete-integration*
+
+ALE integrates with Deoplete for offering automatic completion data. ALE's
+completion source for Deoplete is named `'ale'`, and should enabled
+automatically if Deoplete is enabled and configured correctly. Deoplete
+integration should not be combined with ALE's own implementation.
+
+ALE also offers its own completion implementation, which does not require any
other plugins. Suggestions will be made while you type after completion is
enabled. ALE's own completion implementation can be enabled by setting
|g:ale_completion_enabled| to `1`. This setting must be set to `1` before ALE
@@ -355,7 +362,8 @@ If you don't like some of the suggestions you see, you can filter them out
with |g:ale_completion_excluded_words| or |b:ale_completion_excluded_words|.
The |ALEComplete| command can be used to show completion suggestions manually,
-even when |g:ale_completion_enabled| is set to `0`.
+even when |g:ale_completion_enabled| is set to `0`. For manually requesting
+completion information with Deoplete, consult Deoplete's documentation.
*ale-completion-completeopt-bug*
diff --git a/rplugin/python3/deoplete/sources/ale.py b/rplugin/python3/deoplete/sources/ale.py
new file mode 100644
index 00000000..7ed2f6c0
--- /dev/null
+++ b/rplugin/python3/deoplete/sources/ale.py
@@ -0,0 +1,54 @@
+"""
+A Deoplete source for ALE completion via tsserver and LSP.
+"""
+__author__ = 'Joao Paulo, w0rp'
+
+try:
+ from deoplete.source.base import Base
+except ImportError:
+ # Mock the Base class if deoplete isn't available, as mock isn't available
+ # in the Docker image.
+ class Base(object):
+ def __init__(self, vim):
+ pass
+
+
+# Make sure this code is valid in Python 2, used for running unit tests.
+class Source(Base):
+
+ def __init__(self, vim):
+ super(Source, self).__init__(vim)
+
+ self.name = 'ale'
+ self.mark = '[L]'
+ self.rank = 100
+ self.is_bytepos = True
+ self.min_pattern_length = 1
+
+ # Returns an integer for the start position, as with omnifunc.
+ def get_completion_position(self):
+ return self.vim.call('ale#completion#GetCompletionPosition')
+
+ def gather_candidates(self, context):
+ # Stop early if ALE can't provide completion data for this buffer.
+ if not self.vim.call('ale#completion#CanProvideCompletions'):
+ return None
+
+ if context.get('is_refresh'):
+ context['is_async'] = False
+
+ if context['is_async']:
+ # Result is the same as for omnifunc, or None.
+ result = self.vim.call('ale#completion#GetCompletionResult')
+
+ if result is not None:
+ context['is_async'] = False
+
+ return result
+ else:
+ context['is_async'] = True
+
+ # Request some completion results.
+ self.vim.call('ale#completion#GetCompletions', 'deoplete')
+
+ return []
diff --git a/test/python/test_deoplete_source.py b/test/python/test_deoplete_source.py
new file mode 100644
index 00000000..28eec5cd
--- /dev/null
+++ b/test/python/test_deoplete_source.py
@@ -0,0 +1,147 @@
+import unittest
+import imp
+
+ale_module = imp.load_source(
+ 'deoplete.sources.ale',
+ '/testplugin/rplugin/python3/deoplete/sources/ale.py',
+)
+
+
+class VimMock(object):
+ def __init__(self, call_list, call_results):
+ self.__call_list = call_list
+ self.__call_results = call_results
+
+ def call(self, function, *args):
+ self.__call_list.append((function, args))
+
+ return self.__call_results.get(function, 0)
+
+
+class DeopleteSourceTest(unittest.TestCase):
+ def setUp(self):
+ super(DeopleteSourceTest, self).setUp()
+
+ self.call_list = []
+ self.call_results = {'ale#completion#CanProvideCompletions': 1}
+ self.source = ale_module.Source('vim')
+ self.source.vim = VimMock(self.call_list, self.call_results)
+
+ def test_attributes(self):
+ """
+ Check all of the attributes we set.
+ """
+ attributes = dict(
+ (key, getattr(self.source, key))
+ for key in
+ dir(self.source)
+ if not key.startswith('__')
+ and key != 'vim'
+ and not hasattr(getattr(self.source, key), '__self__')
+ )
+
+ self.assertEqual(attributes, {
+ 'is_bytepos': True,
+ 'mark': '[L]',
+ 'min_pattern_length': 1,
+ 'name': 'ale',
+ 'rank': 100,
+ })
+
+ def test_completion_position(self):
+ self.call_results['ale#completion#GetCompletionPosition'] = 2
+
+ self.assertEqual(self.source.get_completion_position(), 2)
+ self.assertEqual(self.call_list, [
+ ('ale#completion#GetCompletionPosition', ()),
+ ])
+
+ def test_request_completion_results(self):
+ context = {'is_async': False}
+
+ self.assertEqual(self.source.gather_candidates(context), [])
+ self.assertEqual(context, {'is_async': True})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#CanProvideCompletions', ()),
+ ('ale#completion#GetCompletions', ('deoplete',)),
+ ])
+
+ def test_request_completion_results_from_buffer_without_providers(self):
+ self.call_results['ale#completion#CanProvideCompletions'] = 0
+ context = {'is_async': False}
+
+ self.assertIsNone(self.source.gather_candidates(context), [])
+ self.assertEqual(context, {'is_async': False})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#CanProvideCompletions', ()),
+ ])
+
+ def test_refresh_completion_results(self):
+ context = {'is_async': False}
+
+ self.assertEqual(self.source.gather_candidates(context), [])
+ self.assertEqual(context, {'is_async': True})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#CanProvideCompletions', ()),
+ ('ale#completion#GetCompletions', ('deoplete',)),
+ ])
+
+ context = {'is_async': True, 'is_refresh': True}
+
+ self.assertEqual(self.source.gather_candidates(context), [])
+ self.assertEqual(context, {'is_async': True, 'is_refresh': True})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#CanProvideCompletions', ()),
+ ('ale#completion#GetCompletions', ('deoplete',)),
+ ('ale#completion#CanProvideCompletions', ()),
+ ('ale#completion#GetCompletions', ('deoplete',)),
+ ])
+
+ def test_poll_no_result(self):
+ context = {'is_async': True}
+ self.call_results['ale#completion#GetCompletionResult'] = None
+
+ self.assertEqual(self.source.gather_candidates(context), [])
+ self.assertEqual(context, {'is_async': True})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#CanProvideCompletions', ()),
+ ('ale#completion#GetCompletionResult', ()),
+ ])
+
+ def test_poll_empty_result_ready(self):
+ context = {'is_async': True}
+ self.call_results['ale#completion#GetCompletionResult'] = []
+
+ self.assertEqual(self.source.gather_candidates(context), [])
+ self.assertEqual(context, {'is_async': False})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#CanProvideCompletions', ()),
+ ('ale#completion#GetCompletionResult', ()),
+ ])
+
+ def test_poll_non_empty_result_ready(self):
+ context = {'is_async': True}
+ self.call_results['ale#completion#GetCompletionResult'] = [
+ {
+ 'word': 'foobar',
+ 'kind': 'v',
+ 'icase': 1,
+ 'menu': '',
+ 'info': '',
+ },
+ ]
+
+ self.assertEqual(self.source.gather_candidates(context), [
+ {
+ 'word': 'foobar',
+ 'kind': 'v',
+ 'icase': 1,
+ 'menu': '',
+ 'info': '',
+ },
+ ])
+ self.assertEqual(context, {'is_async': False})
+ self.assertEqual(self.call_list, [
+ ('ale#completion#CanProvideCompletions', ()),
+ ('ale#completion#GetCompletionResult', ()),
+ ])
diff --git a/test/script/custom-checks b/test/script/custom-checks
index d4027fec..20dbfb80 100755
--- a/test/script/custom-checks
+++ b/test/script/custom-checks
@@ -67,4 +67,14 @@ echo
test/script/check-toc || exit_code=$?
+echo '========================================'
+echo 'Check Python code'
+echo '========================================'
+echo
+
+docker run --rm -v "$PWD:/testplugin" "$DOCKER_RUN_IMAGE" \
+ python -W ignore -m unittest discover /testplugin/test/python \
+ || exit_code=$?
+echo
+
exit $exit_code