diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..98d4bff --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +package-metadata.json diff --git a/Default.sublime-keymap b/Default.sublime-keymap index 5247173..5930f96 100644 --- a/Default.sublime-keymap +++ b/Default.sublime-keymap @@ -14,4 +14,13 @@ ] }, + {"command": "super_elixir_params_autocomplete", + "keys": ["("], + "context": [ + {"key": "selector", "operator": "equal", "operand": "source.elixir - string - comment"}, + {"key": "selection_empty", "operator": "equal", "operand": true, "match_all": true}, + {"key": "following_text", "operator": "regex_match", "operand": "^($| )", "match_all": true} + ] + }, + ] diff --git a/super_elixir/autocomplete.py b/super_elixir/autocomplete.py index b6d685f..d378efb 100644 --- a/super_elixir/autocomplete.py +++ b/super_elixir/autocomplete.py @@ -7,12 +7,17 @@ from .sense_client import get_elixir_sense +FOLLOWING_CHARS = set(["\r", "\n", "\t", " ", ")", "]", ";", "}", "\x00"]) + + class Autocomplete(sublime_plugin.EventListener): def on_query_completions(self, view, prefix, locations): if not is_elixir(view): return + print(prefix, locations) + buffer, line, column = get_buffer_line_column(view, locations[0]) sense = get_elixir_sense(view) @@ -65,9 +70,9 @@ def on_query_completions(self, view, prefix, locations): def _sort_by_frequency_in_view(self, buffer, completions): completions.sort(key=lambda c: ( - -buffer.count(c['name']), - len(c['name']) - len(c['name'].strip('_')), - c['name'], + -buffer.count(c['name']), # how many times it is in the buffer + len(c['name']) - len(c['name'].strip('_')), # less underscores wins + c['name'], # alphabetically )) return completions @@ -94,3 +99,85 @@ def on_hover(self, view, point, hover_zone): location=point, max_width=1024, ) + + +class SuperElixirParamsAutocomplete(sublime_plugin.TextCommand): + + def run(self, edit, characters='('): + + point = self.view.sel()[0].a + f_name = self.view.substr(self.view.word(point)) + completions = Autocomplete().on_query_completions( + self.view, f_name, [point]) + + if completions: + show, snippet = completions[0] + snippet = snippet[snippet.index('('):] # cut out the function name + self.view.run_command('insert_snippet', {"contents": snippet}) + else: + self._insert_characters(edit, characters, ')') + + @property + def auto_match_enabled(self): + """ check if sublime closes parenthesis automaticly """ + return self.view.settings().get('auto_match_enabled', True) + + def _insert_characters(self, edit, open_pair, close_pair): + """ + Insert autocomplete character with closed pair + and update selection regions + + If sublime option `auto_match_enabled` turned on, next behavior have to + be: + + when none selection + + `( => ()` + `1 => ( => (1` + + when text selected + + `text => (text)` + + In other case: + + when none selection + + `( => (` + + when text selected + + `text => (` + + + :param edit: sublime.Edit + :param characters: str + """ + regions = [a for a in self.view.sel()] + self.view.sel().clear() + + for region in reversed(regions): + next_char = self.view.substr(region.begin()) + # replace null byte to prevent error + next_char = next_char.replace('\x00', '\n') + print("Next characters: {0}".format(next_char)) + + following_text = next_char not in FOLLOWING_CHARS + print("Following text: {0}".format(following_text)) + + if self.auto_match_enabled: + self.view.insert(edit, region.begin(), open_pair) + position = region.end() + 1 + + # IF selection is non-zero + # OR after cursor no any text and selection size is zero + # THEN insert closing pair + if (region.size() > 0 or + not following_text and region.size() == 0): + self.view.insert(edit, region.end() + 1, close_pair) + position += (len(open_pair) - 1) + else: + self.view.replace(edit, region, open_pair) + position = region.begin() + len(open_pair) + + self.view.sel().add(sublime.Region(position, position))