Skip to content

Commit 4c4dc22

Browse files
committed
Merge branch 'develop'
2 parents b72a6b9 + 94e4057 commit 4c4dc22

5 files changed

Lines changed: 125 additions & 45 deletions

File tree

core/aliases.py

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -61,47 +61,48 @@
6161
).lstrip()
6262

6363

64-
def check(desired_state):
64+
def check(desired_state, on_demand=False):
6565
if desired_state:
66-
enable()
66+
enable(on_demand)
6767
else:
6868
disable()
6969

7070

7171
def disable():
7272
log("Disabling aliases")
7373

74+
real_syntaxes = get_real_syntaxes()
75+
7476
def delete_alias_files(syntaxes):
75-
base_syntaxes = None
7677
for syntax in syntaxes:
77-
if HAS_FIND_SYNTAX:
78-
base_syntaxes = sublime.find_syntax_by_scope(
79-
syntax.get("base", "text.plain")
80-
)
81-
if base_syntaxes:
82-
delete_alias_file(syntax, base_syntaxes[0].path)
83-
else:
84-
delete_alias_file(syntax, "Plain text.tmLanguage")
78+
delete_alias_file(
79+
syntax,
80+
real_syntaxes.get(
81+
syntax.get("base", "text.plain"),
82+
"Packages/Text/Plain text.tmLanguage",
83+
),
84+
)
8585

8686
for file_type in icons_json_content().values():
8787
delete_alias_files(file_type.get("aliases", []))
8888
delete_alias_files(file_type.get("syntaxes", []))
8989

90-
shutil.rmtree(path.overlay_aliases_path(), ignore_errors=True)
91-
shutil.rmtree(path.overlay_cache_path(), ignore_errors=True)
90+
def remove():
91+
shutil.rmtree(path.overlay_aliases_path(), ignore_errors=True)
92+
shutil.rmtree(path.overlay_cache_path(), ignore_errors=True)
9293

94+
sublime.set_timeout_async(remove)
9395

94-
def enable():
95-
if HAS_FIND_SYNTAX:
96-
# Built a dict of { scope: syntax } from visible/real syntaxes.
97-
# Note: Existing aliases in the overlay are hidden and thus excluded
98-
# by default. Also ignore possible aliases or special purpose
99-
# syntaxes from 3rd-party packages.
100-
real_syntaxes = {
101-
s.scope: s.path for s in sublime.list_syntaxes() if not s.hidden
102-
}
103-
else:
104-
real_syntaxes = {}
96+
97+
def enable(on_demand=False):
98+
real_syntaxes = get_real_syntaxes()
99+
syntax_names = real_syntaxes.keys()
100+
try:
101+
if on_demand and enable.syntax_names == syntax_names:
102+
return
103+
except Exception:
104+
pass
105+
enable.syntax_names = syntax_names
105106

106107
def real_syntax_for(selector):
107108
for scope in selector.split(","):
@@ -145,7 +146,7 @@ def create_alias_file(alias):
145146
else:
146147
out.write(EMPTY_TEMPLATE.format(name, scope, exts, base))
147148
except FileExistsError:
148-
pass
149+
dump("■ {}.sublime-syntax".format(name))
149150
except Exception as error:
150151
dump("+ {}.sublime-syntax | {}".format(name, error))
151152
else:
@@ -156,6 +157,7 @@ def delete_alias_file(alias, real_syntax):
156157
alias_name = alias["name"] + ".sublime-syntax"
157158
alias_path = path.overlay_aliases_path(alias_name)
158159
if not os.path.exists(alias_path):
160+
dump("□ " + alias_name)
159161
return
160162

161163
# reassign real syntax to any open view, which uses the alias
@@ -166,10 +168,25 @@ def delete_alias_file(alias, real_syntax):
166168
if syntax and syntax == alias_resource:
167169
view.assign_syntax(real_syntax)
168170

169-
# actually delete the alias syntax
170-
try:
171-
os.remove(alias_path)
172-
except Exception as error:
173-
dump("- {} | {}".format(alias_name, error))
174-
else:
175-
dump("- {}".format(alias_name))
171+
# delete the alias syntax asynchronously, after ST applied real syntax
172+
def remove():
173+
try:
174+
os.remove(alias_path)
175+
except Exception as error:
176+
dump("- {} | {}".format(alias_name, error))
177+
else:
178+
dump("- {}".format(alias_name))
179+
180+
sublime.set_timeout_async(remove)
181+
182+
183+
def get_real_syntaxes():
184+
# Built a dict of { scope: syntax } from visible/real syntaxes.
185+
# Note: Existing aliases in the overlay are hidden and thus excluded
186+
# by default. Also ignore possible aliases or special purpose
187+
# syntaxes from 3rd-party packages.
188+
return (
189+
{s.scope: s.path for s in sublime.list_syntaxes() if not s.hidden}
190+
if HAS_FIND_SYNTAX
191+
else {}
192+
)

core/settings.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,43 @@
55
from . import themes
66

77
from .overlay import with_ignored_overlay
8+
from .utils.decorators import debounce
89
from .utils.logging import log
910
from .utils.path import PACKAGE_NAME
1011

1112
PACKAGE_SETTINGS = "A File Icon.sublime-settings"
13+
USER_SETTINGS = "Preferences.sublime-settings"
1214

15+
_cached_packages = []
1316
_cached_settings = {}
1417
_uuid = "9ebcce78-4cac-4089-8bd7-d551c634b052"
1518

1619

1720
def add_listener():
1821
log("Initializing settings")
1922
path = "Packages/{0}/{1}".format(PACKAGE_NAME, PACKAGE_SETTINGS)
20-
settings = sublime.load_settings(PACKAGE_SETTINGS)
23+
package_settings = sublime.load_settings(PACKAGE_SETTINGS)
2124
for key in sublime.decode_value(sublime.load_resource(path)).keys():
2225
if key not in ("dev_mode", "dev_trace"):
23-
_cached_settings[key] = settings.get(key)
26+
_cached_settings[key] = package_settings.get(key)
27+
28+
user_settings = sublime.load_settings(USER_SETTINGS)
29+
_cached_packages = user_settings.get("ignored_packages")
2430

2531
icons.init()
2632
themes.patch(_cached_settings)
2733
aliases.check(_cached_settings["aliases"])
28-
sublime.load_settings(PACKAGE_SETTINGS).add_on_change(_uuid, _on_change)
34+
35+
package_settings.add_on_change(_uuid, _on_change_package)
36+
user_settings.add_on_change(_uuid, _on_change_user)
2937

3038

3139
def clear_listener():
3240
sublime.load_settings(PACKAGE_SETTINGS).clear_on_change(_uuid)
41+
sublime.load_settings(USER_SETTINGS).clear_on_change(_uuid)
3342

3443

35-
def _on_change():
44+
def _on_change_package():
3645
is_aliases_changed = False
3746
is_icons_changed = False
3847
is_force_mode_changed = False
@@ -55,12 +64,18 @@ def _on_change():
5564
aliases.check(_cached_settings["aliases"])
5665
if is_icons_changed:
5766
log("Icons settings changed")
58-
_patch_theme(True)
67+
themes.patch(_cached_settings, overwrite=True)
5968
elif is_force_mode_changed:
6069
log("Force mode settings changed")
61-
_patch_theme(False)
62-
63-
64-
@with_ignored_overlay
65-
def _patch_theme(overwrite):
66-
themes.patch(_cached_settings, overwrite)
70+
themes.patch(_cached_settings)
71+
72+
73+
@debounce(2000)
74+
def _on_change_user():
75+
global _cached_packages
76+
settings = sublime.load_settings(USER_SETTINGS)
77+
packages = settings.get("ignored_packages")
78+
if packages != _cached_packages:
79+
_cached_packages = packages
80+
themes.patch(_cached_settings, on_demand=True)
81+
aliases.check(_cached_settings["aliases"], on_demand=True)

core/themes.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,25 @@
44

55
import sublime
66

7+
from itertools import chain
8+
79
from .utils import path
810
from .utils.colors import convert_color_value
911
from .utils.logging import log, dump
1012

1113
from . import icons
1214

1315

14-
def patch(settings, overwrite=False):
16+
def patch(settings, overwrite=False, on_demand=False):
1517
theme_packages = _installed_themes()
18+
themes = set((theme for theme in chain(*theme_packages.values())))
19+
try:
20+
if on_demand and patch.themes == themes:
21+
return
22+
except Exception:
23+
pass
24+
patch.themes = themes
25+
1626
supported = [] if settings.get("force_mode") else _customizable_themes()
1727

1828
general_patch = _create_general_patch(settings)

core/utils/decorators.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from functools import partial, wraps
2+
from timeit import default_timer
3+
4+
import sublime
5+
6+
7+
def debounce(delay_in_ms, sync=False):
8+
"""Delay calls to functions until they weren't triggered for n ms."""
9+
10+
# We assume that locking is not necessary because each function will be called
11+
# from either the ui or the async thread only.
12+
set_timeout = sublime.set_timeout if sync else sublime.set_timeout_async
13+
14+
def decorator(func):
15+
call_at = 0
16+
17+
def _debounced_callback(callback):
18+
nonlocal call_at
19+
diff = call_at - int(default_timer() * 1000)
20+
if diff > 0:
21+
set_timeout(partial(_debounced_callback, callback), diff)
22+
else:
23+
call_at = 0
24+
callback()
25+
26+
@wraps(func)
27+
def wrapper(*args, **kwargs):
28+
nonlocal call_at
29+
pending = call_at > 0
30+
call_at = int(default_timer() * 1000) + delay_in_ms
31+
if pending:
32+
return
33+
callback = partial(func, *args, **kwargs)
34+
set_timeout(partial(_debounced_callback, callback), delay_in_ms)
35+
36+
return wrapper
37+
38+
return decorator

icons/icons.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@
850850
".Dockerfile"
851851
],
852852
"name": "Plain Text (Dockerfile)",
853-
"scope": "source.dockerfile"
853+
"scope": "source.containerfile, source.dockerfile"
854854
}
855855
]
856856
},

0 commit comments

Comments
 (0)