diff --git a/okcli/completion_cache.py b/okcli/completion_cache.py new file mode 100644 index 0000000..a7f2e0c --- /dev/null +++ b/okcli/completion_cache.py @@ -0,0 +1,18 @@ +import hashlib +from os import path + +def completion_filename(user, host): + return hashlib.md5(("%s%s" % (user, host)).encode('utf-8')).hexdigest() + +def completion_cache_dir(config): + return path.join(path.dirname(config.filename), '.okcli_cache') + +def completion_cache_file(user, host, config): + return path.join(completion_cache_dir(config), completion_filename(user, host)) + +def existing_completion_cache_file(user, host, config): + maybe_file = completion_cache_file(user, host, config) + if path.isfile(maybe_file): + return maybe_file + else: + return None diff --git a/okcli/main.py b/okcli/main.py index 282e6a2..5d451dd 100755 --- a/okcli/main.py +++ b/okcli/main.py @@ -42,6 +42,9 @@ from .packages.special.main import NO_QUERY from .sqlcompleter import SQLCompleter from .sqlexecute import SQLExecute +from .completion_cache import (existing_completion_cache_file, + completion_cache_file, + completion_cache_dir) click.disable_unicode_literals_warning = True try: @@ -106,6 +109,7 @@ def __init__(self, sqlexecute=None, prompt=None, self.formatter = TabularOutputFormatter( format_name=c['main']['table_format']) self.syntax_style = c['main']['syntax_style'] + self.cache_completions = c['main'].as_bool('cache_completions') self.cli_style = c['colors'] self.wider_completion_menu = c['main'].as_bool('wider_completion_menu') c_ddl_warning = c['main'].as_bool('ddl_warning') @@ -640,17 +644,31 @@ def refresh_completions(self, reset=False): if reset: with self._completer_lock: self.completer.reset_completions() + status_message = 'Auto-completion refresh started in the background.' + if self.cache_completions: + maybe_cache = existing_completion_cache_file(self.sqlexecute.user, self.sqlexecute.host, self.config) + if maybe_cache is not None: + import pickle + status_message = "%s (using cache for now)" % status_message + with open(maybe_cache, 'rb') as compfile: + new_completer = pickle.load(compfile) + self._swap_completer_objects(new_completer) self.completion_refresher.refresh( self.sqlexecute, self._on_completions_refreshed, {'smart_completion': self.smart_completion, 'supported_formats': self.formatter.supported_formats}) - return [(None, None, None, - 'Auto-completion refresh started in the background.')] + return [(None, None, None, status_message)] def _on_completions_refreshed(self, new_completer): self._swap_completer_objects(new_completer) + if self.cache_completions: + import pickle + os.makedirs(completion_cache_dir(self.config), exist_ok=True) + with open(completion_cache_file(self.sqlexecute.user, self.sqlexecute.host, self.config), 'wb') as compfile: + pickle.dump(new_completer, compfile) + if self.cli: # After refreshing, redraw the CLI to clear the statusbar # "Refreshing completions..." indicator diff --git a/okcli/okclirc b/okcli/okclirc index 90972b1..401877e 100644 --- a/okcli/okclirc +++ b/okcli/okclirc @@ -5,6 +5,11 @@ # possible completions will be listed. smart_completion = True +# Caches completions until they have been refreshed. +# This makes sense when loading completions is not instantaneous +# (depends on the amount of things in the database) +cache_completions = False + # Multi-line mode allows breaking up the sql statements into multiple lines. If # this is set to True, then the end of the statements must have a semi-colon. # If this is set to False then sql statements can't be split into multiple