Skip to content

Commit

Permalink
Fix clang completions hiding default completions (issue niosus#55)
Browse files Browse the repository at this point in the history
When clang completions returned from the binary clang were empty, the code
still hid default completions popup. Fix by not hiding autocompletion popup
when parsed suggestions end up being empty.

libclang variant returned early if no completions were found. That meant that
it didn't have the same problem but that was still bad for two reasons:
 - Non-empty completions might still have ended up being empty if none were
   parsed properly and then the same problem as with binary clang would trigger.
 - Returning early meant that no error regions were created. That was
   inconsistent with binary clang variant which always added error regions.

Also added tests for testing whether default completions are hidden by empty
clang/libclang completions and for testing that error markers are still added
when there are no completions.
  • Loading branch information
rcopera committed Sep 20, 2016
1 parent c374611 commit fa4ac26
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 8 deletions.
6 changes: 5 additions & 1 deletion plugin/completion/bin_complete.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,12 @@ def complete(self, view, cursor_pos, show_errors):
log.debug(" code complete done in %s seconds", end - start)

self.completions = Completer._parse_completions(raw_complete)
log.debug(' completions: %s' % self.completions)
self.async_completions_ready = True
Completer._reload_completions(view)
if len(self.completions) > 0:
Completer._reload_completions(view)
else:
log.debug(" no completions")

if show_errors:
self.show_errors(view, output_text)
Expand Down
16 changes: 10 additions & 6 deletions plugin/completion/lib_complete.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,15 +228,19 @@ def complete(self, view, cursor_pos, show_errors):
row, col,
unsaved_files=files)
end = time.time()
if complete_obj is None or len(complete_obj.results) == 0:
log.debug(" no completions")
return None
log.debug(" code complete done in %s seconds", end - start)

self.completions = Completer._parse_completions(complete_obj)
log.debug(self.completions)
if complete_obj is None or len(complete_obj.results) == 0:
self.completions = []
else:
self.completions = Completer._parse_completions(complete_obj)
log.debug(' completions: %s' % self.completions)
self.async_completions_ready = True
Completer._reload_completions(view)
if len(self.completions) > 0:
Completer._reload_completions(view)
else:
log.debug(" no completions")

if show_errors:
self.show_errors(
view, self.translation_units[view.buffer_id()].diagnostics)
Expand Down
2 changes: 1 addition & 1 deletion plugin/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ def get_position_status(point, view, settings):
log.debug(" wrong trigger '%s%s'.", prev_char, curr_char)
wrong_trigger_found = True
if wrong_trigger_found:
# no correct trigger found, vut a wrong one fired instead
# no correct trigger found, but a wrong one fired instead
log.debug(" wrong trigger fired")
return PosStatus.WRONG_TRIGGER

Expand Down
86 changes: 86 additions & 0 deletions tests/test_complete.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,92 @@ def test_unsaved_views(self):
# Verify that the completer ignores the scratch view.
self.assertFalse(completer.exists_for_view(self.view.buffer_id()))

def test_cooperation_with_default_completions(self):
"""
Test that empty clang completions do not hide default completions.
"""
self.setUpView("test_errors.cpp")

completer = self.setUpCompleter()
self.assertTrue(completer.exists_for_view(self.view.buffer_id()))

# Undefined foo object has no completions.
self.assertEqual(self.getRow(1), " foo.")
pos = self.view.text_point(1, 6)
current_word = self.view.substr(self.view.word(pos))
self.assertEqual(current_word, ".\n")

# Trigger default completions popup.
self.view.run_command('auto_complete')
self.assertTrue(self.view.is_auto_complete_visible())

# Load the completions.
settings = Settings()
completer.complete(self.view, pos, settings.errors_on_save)

# Wait 2 seconds for them to load.
counter = 0
while not completer.async_completions_ready:
time.sleep(0.1)
counter += 1
if counter > 20:
self.fail("Completions not ready after %d tries" % counter)

# Verify that we got the expected completions back.
self.assertEqual([], completer.completions)
# And that popup with default completions is still open.
self.assertTrue(self.view.is_auto_complete_visible())

def test_no_completions_with_error_markers(self):
""" Test that empty completions still produce error marks. """
self.setUpView("test_errors.cpp")

completer = self.setUpCompleter()
self.assertTrue(completer.exists_for_view(self.view.buffer_id()))

# Undefined foo object has no completions.
self.assertEqual(self.getRow(1), " foo.")
pos = self.view.text_point(1, 6)
current_word = self.view.substr(self.view.word(pos))
self.assertEqual(current_word, ".\n")

# Load the completions.
settings = Settings()
completer.complete(self.view, pos, settings.errors_on_save)

# Wait 2 seconds for them to load.
counter = 0
while not completer.async_completions_ready:
time.sleep(0.1)
counter += 1
if counter > 20:
self.fail("Completions not ready after %d tries" % counter)

# Verify that we got the expected completions back.
self.assertEqual([], completer.completions)

# Verify that error regions were added.
error_vis = completer.error_vis
self.assertIsNotNone(error_vis)
regions_dict = error_vis.err_regions[self.view.buffer_id()]
self.assertIsNotNone(regions_dict)
# Verify the number of rows with error regions.
expected_rows_count = 1
self.assertEqual(expected_rows_count, len(regions_dict.keys()))
# Verify the region row.
expected_row = 2
self.assertIn(expected_row, regions_dict)
# Verify that correct amount of regions were created.
expected_regions_count = 1
self.assertEqual(expected_regions_count,
len(regions_dict[expected_row]))
# Verify the column, row and region extent.
error_region = regions_dict[expected_row][0]
self.assertEqual('3', error_region['col'])
self.assertEqual('2', error_region['row'])
self.assertEqual(43, error_region['region'].a)
self.assertEqual(46, error_region['region'].b)

def test_cmake_generate(self):
"""
We search for cmakelists and generate .clang_complete file.
Expand Down
4 changes: 4 additions & 0 deletions tests/test_errors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
int main(int argc, char const *argv[]) {
foo.
return 0;
}

0 comments on commit fa4ac26

Please sign in to comment.