Skip to content

Commit 9c8fab6

Browse files
committed
Organize decorators in dedicated module
1 parent cc79c46 commit 9c8fab6

File tree

9 files changed

+141
-86
lines changed

9 files changed

+141
-86
lines changed

latextools/biblatex_crossref_completions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sublime
44
import sublime_plugin
55

6-
from .utils.sublime_utils import async_completions
6+
from .utils.decorators import async_completions
77

88
__all__ = ["BiblatexCrossrefCompletions"]
99

latextools/biblatex_name_completions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from ..vendor.bibtex.names import Name
88
from ..vendor.bibtex.tex import tokenize_list
9-
from .utils.sublime_utils import async_completions
9+
from .utils.decorators import async_completions
1010

1111
__all__ = ["BiblatexNameCompletions"]
1212

latextools/latex_cwl_completions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
from .latex_ref_completions import OLD_STYLE_REF_REGEX
1818
from .utils import analysis
1919
from .utils import utils
20-
from .utils.parser_utils import command_to_snippet
20+
from .utils.decorators import async_completions
2121
from .utils.logging import logger
22+
from .utils.parser_utils import command_to_snippet
2223
from .utils.settings import get_setting
23-
from .utils.sublime_utils import async_completions
2424
from .utils.tex_directives import get_tex_root
2525

2626
__all__ = ["LatexCwlCompletion"]

latextools/latex_directive_completions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
from . import detect_spellcheck
88
from .latex_fill_all import FillAllHelper
9+
from .utils.decorators import async_completions
910
from .utils.settings import get_setting
10-
from .utils.sublime_utils import async_completions
1111

1212
try:
1313
installed_locales = sorted(detect_spellcheck._dictionary_mappings.keys())

latextools/latex_fill_all.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
from .deprecated_command import deprecate
99
from .latextools_plugin import _classname_to_internal_name
1010
from .latextools_plugin import get_plugins_by_type
11+
from .utils.decorators import async_completions
1112
from .utils.logging import logger
1213
from .utils.settings import get_setting
1314
from .utils.internal_types import FillAllHelper
14-
from .utils.sublime_utils import async_completions
1515

1616
__all__ = [
1717
"LatexFillAllEventListener",

latextools/preview/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import sublime_plugin
44

5-
from ..utils.debounce import debounce
5+
from ..utils.decorators import debounce
66

77
from .preview_image import ImagePreviewHoverListener
88
from .preview_image import ImagePreviewPhantomProvider

latextools/utils/debounce.py

Lines changed: 0 additions & 58 deletions
This file was deleted.

latextools/utils/decorators.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import cProfile
2+
import pstats
3+
import sys
4+
from functools import partial, wraps
5+
from timeit import default_timer
6+
7+
import sublime
8+
import sublime_plugin
9+
10+
PROFILE = False
11+
12+
13+
def async_completions(func):
14+
"""Asynchronously query completions
15+
16+
Creates and immediately returns a `sublime.CompletionList`, applying results
17+
once `on_query_completions` has finished executing in ST's worker thread.
18+
"""
19+
20+
@wraps(func)
21+
def wrapper(*args, **kwargs):
22+
completion_list = sublime.CompletionList()
23+
24+
def query():
25+
try:
26+
completions = func(*args, **kwargs)
27+
if completions:
28+
flags = sublime.AutoCompleteFlags.INHIBIT_WORD_COMPLETIONS
29+
else:
30+
flags = sublime.AutoCompleteFlags.NONE
31+
completions = []
32+
except Exception:
33+
completion_list.set_completions([])
34+
raise
35+
else:
36+
completion_list.set_completions(completions, flags)
37+
38+
sublime.set_timeout_async(query)
39+
40+
return completion_list
41+
42+
return wrapper
43+
44+
45+
def debounce(delay_in_ms, sync=False):
46+
"""Delay calls to event hooks until they weren't triggered for n ms.
47+
48+
Performs view-specific tracking and is best suited for the
49+
`on_modified` and `on_selection_modified` methods
50+
and their `_async` variants.
51+
The `view` is taken from the first argument for `EventListener`s
52+
and from the instance for `ViewEventListener`s.
53+
54+
Calls are only made when the `view` is still "valid" according to ST's API,
55+
so it's not necessary to check it in the wrapped function.
56+
"""
57+
58+
# We assume that locking is not necessary because each function will be called
59+
# from either the ui or the async thread only.
60+
set_timeout = sublime.set_timeout if sync else sublime.set_timeout_async
61+
62+
def decorator(func):
63+
to_call_times = {}
64+
65+
def _debounced_callback(view, callback):
66+
vid = view.id()
67+
threshold = to_call_times.get(vid)
68+
if not threshold:
69+
return
70+
if not view.is_valid():
71+
del to_call_times[vid]
72+
return
73+
diff = threshold - default_timer() * 1000
74+
if diff > 0:
75+
set_timeout(partial(_debounced_callback, view, callback), diff)
76+
else:
77+
del to_call_times[vid]
78+
callback()
79+
80+
@wraps(func)
81+
def wrapper(self, *args, **kwargs):
82+
view = self.view if hasattr(self, 'view') else args[0]
83+
if not view.is_valid():
84+
return
85+
vid = view.id()
86+
busy = vid in to_call_times
87+
to_call_times[vid] = default_timer() * 1000 + delay_in_ms
88+
if busy:
89+
return
90+
callback = partial(func, self, *args, **kwargs)
91+
set_timeout(partial(_debounced_callback, view, callback), delay_in_ms)
92+
93+
return wrapper
94+
95+
return decorator
96+
97+
98+
def profile(func):
99+
"""Run the profiler in the given function
100+
101+
Debugging helper decorateor
102+
"""
103+
104+
@wraps(func)
105+
def wrapper(*args, **kwargs):
106+
if not PROFILE:
107+
return func(*args, **kwargs)
108+
pr = cProfile.Profile()
109+
pr.enable()
110+
result = func(*args, **kwargs)
111+
pr.disable()
112+
ps = pstats.Stats(pr, stream=sys.stdout)
113+
ps.sort_stats('time')
114+
ps.print_stats(15)
115+
return result
116+
117+
return wrapper
118+
119+
120+
def timing(func):
121+
"""Print decorated function's execution time
122+
123+
Debugging helper decorateor
124+
"""
125+
126+
@wraps(func)
127+
def decorator(*args, **kw):
128+
ts = default_timer()
129+
result = func(*args, **kw)
130+
te = default_timer()
131+
print(f"{func.__name__}({args}, {kw}) took: {1000.0 * (te - ts):2.3f} ms")
132+
return result
133+
134+
return decorator

latextools/utils/sublime_utils.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,6 @@
1616
SUBLIME_VERSION = re.compile(r"Build (\d{4})", re.UNICODE)
1717

1818

19-
def async_completions(func):
20-
def decorator(*args, **kwargs):
21-
completion_list = sublime.CompletionList()
22-
23-
def query():
24-
completions = func(*args, **kwargs)
25-
if completions:
26-
flags = sublime.AutoCompleteFlags.INHIBIT_WORD_COMPLETIONS
27-
else:
28-
flags = sublime.AutoCompleteFlags.NONE
29-
completions = []
30-
31-
completion_list.set_completions(completions, flags)
32-
33-
sublime.set_timeout_async(query)
34-
35-
return completion_list
36-
37-
return decorator
38-
39-
4019
# returns the focus to ST
4120
# NB its probably good to call this as little as possible since focus-stealing
4221
# annoys people

0 commit comments

Comments
 (0)