Skip to content

Commit 9e5823c

Browse files
committed
Automatically inject state values into the render functions
1 parent 260325c commit 9e5823c

File tree

5 files changed

+173
-72
lines changed

5 files changed

+173
-72
lines changed

common/ui.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from contextlib import contextmanager
22
from copy import deepcopy
33
from functools import wraps
4+
import inspect
45
import re
56
from textwrap import dedent
67
import threading
@@ -40,11 +41,12 @@
4041
T = TypeVar("T")
4142
T_state = TypeVar("T_state", bound=MutableMapping)
4243
SectionRegions = Dict[str, sublime.Region]
44+
RenderFnReturnType = Union[str, Tuple[str, List["SectionFn"]]]
4345

4446
class SectionFn(Protocol):
4547
key = '' # type: str
4648

47-
def __call__(self) -> 'Union[str, Tuple[str, List[SectionFn]]]':
49+
def __call__(self) -> RenderFnReturnType:
4850
pass
4951

5052

@@ -382,13 +384,45 @@ def _pick_subscribed_topics_from_store(self, state):
382384

383385

384386
def section(key):
385-
# type: (str) -> Callable[[Callable[..., Union[str, Tuple[str, List[SectionFn]]]]], SectionFn]
387+
# type: (str) -> Callable[[Callable[..., RenderFnReturnType]], SectionFn]
386388
def decorator(fn):
387389
fn.key = key
388-
return fn
390+
return inject_state()(fn)
389391
return decorator
390392

391393

394+
def inject_state():
395+
def decorator(fn):
396+
sig = inspect.signature(fn)
397+
keys = ordered_positional_args(sig)
398+
if "self" not in keys:
399+
return fn
400+
401+
@wraps(fn)
402+
def decorated(self, *args, **kwargs):
403+
b = sig.bind_partial(self, *args, **kwargs)
404+
given_args = b.arguments.keys()
405+
try:
406+
values = {key: self.state[key] for key in keys if key not in given_args}
407+
except KeyError:
408+
return ""
409+
else:
410+
kwargs.update(b.arguments)
411+
kwargs.update(values)
412+
return fn(**kwargs)
413+
return decorated
414+
return decorator
415+
416+
417+
def ordered_positional_args(sig):
418+
# type: (inspect.Signature) -> List[str]
419+
return [
420+
name
421+
for name, parameter in sig.parameters.items()
422+
if parameter.default is inspect.Parameter.empty
423+
]
424+
425+
392426
def indent_by_2(text):
393427
return "\n".join(line[2:] for line in text.split("\n"))
394428

core/interfaces/branch.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -167,16 +167,15 @@ def cursor_is_on_active_branch():
167167
self.view.run_command("gs_branches_navigate_to_active_branch")
168168

169169
@ui.section("branch_status")
170-
def render_branch_status(self):
171-
return self.state['long_status']
170+
def render_branch_status(self, long_status):
171+
return long_status
172172

173173
@ui.section("git_root")
174-
def render_git_root(self):
175-
return self.state['git_root']
174+
def render_git_root(self, git_root):
175+
return git_root
176176

177177
@ui.section("head")
178-
def render_head(self):
179-
recent_commits = self.state['recent_commits']
178+
def render_head(self, recent_commits):
180179
if recent_commits is NullRecentCommits:
181180
return ""
182181
if not recent_commits:
@@ -185,22 +184,22 @@ def render_head(self):
185184
return "{0.hash} {0.message}".format(recent_commits[0])
186185

187186
@ui.section("branch_list")
188-
def render_branch_list(self):
189-
# type: () -> str
190-
branches = [branch for branch in self.state["branches"] if not branch.is_remote]
191-
if self.state["sort_by_recent"]:
192-
branches = sorted(branches, key=lambda branch: -branch.committerdate)
193-
return self._render_branch_list(None, branches)
194-
195-
def _render_branch_list(self, remote_name, branches):
196-
# type: (Optional[str], List[Branch]) -> str
197-
remote_name_l = len(remote_name + "/") if remote_name else 0
187+
def render_branch_list(self, branches, sort_by_recent, descriptions):
188+
# type: (List[Branch], bool, Dict[str, str]) -> str
189+
local_branches = [branch for branch in branches if not branch.is_remote]
190+
if sort_by_recent:
191+
local_branches = sorted(local_branches, key=lambda branch: -branch.committerdate)
192+
return self._render_branch_list(None, local_branches, descriptions)
193+
194+
def _render_branch_list(self, remote_name, branches, descriptions):
195+
# type: (Optional[str], List[Branch], Dict[str, str]) -> str
196+
remote_name_length = len(remote_name + "/") if remote_name else 0
198197
return "\n".join(
199198
" {indicator} {hash:.7} {name}{tracking}{description}".format(
200199
indicator="▸" if branch.active else " ",
201200
hash=branch.commit_hash,
202-
name=branch.canonical_name[remote_name_l:],
203-
description=(" " + self.state["descriptions"].get(branch.canonical_name, "")).rstrip(),
201+
name=branch.canonical_name[remote_name_length:],
202+
description=(" " + descriptions.get(branch.canonical_name, "")).rstrip(),
204203
tracking=(" ({branch}{status})".format(
205204
branch=branch.upstream.canonical_name,
206205
status=", " + branch.upstream.status if branch.upstream.status else ""
@@ -209,29 +208,29 @@ def _render_branch_list(self, remote_name, branches):
209208
)
210209

211210
@ui.section("remotes")
212-
def render_remotes(self):
211+
def render_remotes(self, show_remotes):
213212
return (self.render_remotes_on()
214-
if self.state["show_remotes"] else
213+
if show_remotes else
215214
self.render_remotes_off())
216215

217216
@ui.section("help")
218-
def render_help(self):
219-
show_help = self.state['show_help']
217+
def render_help(self, show_help):
220218
if not show_help:
221219
return ""
222220
return self.template_help
223221

224222
def render_remotes_off(self):
225223
return "\n\n ** Press [e] to toggle display of remote branches. **\n"
226224

227-
def render_remotes_on(self):
225+
@ui.inject_state()
226+
def render_remotes_on(self, branches, sort_by_recent, remotes):
228227
output_tmpl = "\n"
229228
render_fns = []
230-
remote_branches = [b for b in self.state["branches"] if b.is_remote]
231-
if self.state["sort_by_recent"]:
229+
remote_branches = [b for b in branches if b.is_remote]
230+
if sort_by_recent:
232231
remote_branches = sorted(remote_branches, key=lambda branch: -branch.committerdate)
233232

234-
for remote_name in self.state["remotes"]:
233+
for remote_name in remotes:
235234
key = "branch_list_" + remote_name
236235
output_tmpl += "{" + key + "}\n"
237236
branches = [b for b in remote_branches if b.canonical_name.startswith(remote_name + "/")]
@@ -240,7 +239,7 @@ def render_remotes_on(self):
240239
def render(remote_name=remote_name, branches=branches):
241240
return self.template_remote.format(
242241
remote_name=remote_name,
243-
remote_branch_list=self._render_branch_list(remote_name, branches)
242+
remote_branch_list=self._render_branch_list(remote_name, branches, {})
244243
)
245244

246245
render_fns.append(render)

core/interfaces/status.py

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -249,31 +249,30 @@ def after_view_creation(self, view):
249249
view.settings().set("result_base_dir", self.repo_path)
250250

251251
@ui.section("branch_status")
252-
def render_branch_status(self):
253-
return self.state['long_status']
252+
def render_branch_status(self, long_status):
253+
return long_status
254254

255255
@ui.section("git_root")
256-
def render_git_root(self):
257-
return self.state['git_root']
256+
def render_git_root(self, git_root):
257+
return git_root
258258

259259
@ui.section("head")
260-
def render_head(self):
261-
# type: () -> str
262-
recent_commits = self.state['recent_commits']
260+
def render_head(self, recent_commits, head, branches):
261+
# type: (List[Commit], HeadState, List[Branch]) -> str
263262
if recent_commits is NullRecentCommits:
264263
return ""
265264
if not recent_commits:
266265
return "No commits yet."
267266

268-
current_upstream = self.state['head'].remote
269-
branches = self.state['branches']
267+
current_upstream = head.remote
268+
branches = branches
270269
return "\n ".join(
271270
format_and_limit(recent_commits, ITEMS_IN_THE_RECENT_LIST, current_upstream, branches)
272271
)
273272

274273
@ui.section("staged_files")
275-
def render_staged_files(self):
276-
staged_files = self.state['status'].staged_files
274+
def render_staged_files(self, status):
275+
staged_files = status.staged_files
277276
if not staged_files:
278277
return ""
279278

@@ -289,8 +288,8 @@ def get_path(file_status):
289288
))
290289

291290
@ui.section("unstaged_files")
292-
def render_unstaged_files(self):
293-
unstaged_files = self.state['status'].unstaged_files
291+
def render_unstaged_files(self, status):
292+
unstaged_files = status.unstaged_files
294293
if not unstaged_files:
295294
return ""
296295

@@ -300,47 +299,44 @@ def render_unstaged_files(self):
300299
))
301300

302301
@ui.section("untracked_files")
303-
def render_untracked_files(self):
304-
untracked_files = self.state['status'].untracked_files
302+
def render_untracked_files(self, status):
303+
untracked_files = status.untracked_files
305304
if not untracked_files:
306305
return ""
307306

308307
return self.template_untracked.format(
309308
"\n".join(" " + f.path for f in untracked_files))
310309

311310
@ui.section("merge_conflicts")
312-
def render_merge_conflicts(self):
313-
merge_conflicts = self.state['status'].merge_conflicts
311+
def render_merge_conflicts(self, status):
312+
merge_conflicts = status.merge_conflicts
314313
if not merge_conflicts:
315314
return ""
316315
return self.template_merge_conflicts.format(
317316
"\n".join(" " + f.path for f in merge_conflicts))
318317

319318
@ui.section("conflicts_bindings")
320-
def render_conflicts_bindings(self):
321-
return self.conflicts_keybindings if self.state['status'].merge_conflicts else ""
319+
def render_conflicts_bindings(self, status):
320+
return self.conflicts_keybindings if status.merge_conflicts else ""
322321

323322
@ui.section("no_status_message")
324-
def render_no_status_message(self):
325-
status = self.state['status']
323+
def render_no_status_message(self, status):
326324
return (
327325
"\n Your working directory is clean.\n"
328326
if status is not NullWorkingDirState and status.clean
329327
else ""
330328
)
331329

332330
@ui.section("stashes")
333-
def render_stashes(self):
334-
stash_list = self.state['stashes']
335-
if not stash_list:
331+
def render_stashes(self, stashes):
332+
if not stashes:
336333
return ""
337334

338335
return self.template_stashes.format("\n".join(
339-
" ({}) {}".format(stash.id, stash.description) for stash in stash_list))
336+
" ({}) {}".format(stash.id, stash.description) for stash in stashes))
340337

341338
@ui.section("help")
342-
def render_help(self):
343-
show_help = self.state['show_help']
339+
def render_help(self, show_help):
344340
if not show_help:
345341
return ""
346342

core/interfaces/tags.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,15 @@ def keep_cursor_on_something(self):
157157
self.view.run_command("gs_tags_navigate_tag")
158158

159159
@ui.section("branch_status")
160-
def render_branch_status(self):
161-
return self.state['long_status']
160+
def render_branch_status(self, long_status):
161+
return long_status
162162

163163
@ui.section("git_root")
164-
def render_git_root(self):
165-
return self.state['git_root']
164+
def render_git_root(self, git_root):
165+
return git_root
166166

167167
@ui.section("head")
168-
def render_head(self):
169-
recent_commits = self.state['recent_commits']
168+
def render_head(self, recent_commits):
170169
if recent_commits is NullRecentCommits:
171170
return ""
172171
if not recent_commits:
@@ -175,15 +174,13 @@ def render_head(self):
175174
return "{0.hash} {0.message}".format(recent_commits[0])
176175

177176
@ui.section("local_tags")
178-
def render_local_tags(self):
179-
local_tags = self.state["local_tags"]
177+
def render_local_tags(self, local_tags, max_items):
180178
if local_tags is NullTagList:
181179
return ""
182180
if not any(local_tags.all):
183181
return NO_LOCAL_TAGS_MESSAGE
184182

185183
regular_tags, versions = local_tags
186-
max_items = self.state["max_items"]
187184
return "\n{}\n".format(" " * 60).join( # need some spaces on the separator line otherwise
188185
# the syntax expects the remote section begins
189186
filter_((
@@ -207,17 +204,17 @@ def render_local_tags(self):
207204
)
208205

209206
@ui.section("remote_tags")
210-
def render_remote_tags(self):
211-
if not self.state["remotes"]:
207+
def render_remote_tags(self, remotes, show_remotes):
208+
if not remotes:
212209
return "\n"
213210

214-
if not self.state["show_remotes"]:
211+
if not show_remotes:
215212
return self.render_remote_tags_off()
216213

217214
output_tmpl = "\n"
218215
render_fns = []
219216

220-
for remote_name in self.state["remotes"]:
217+
for remote_name in remotes:
221218
tmpl_key = "remote_tags_list_" + remote_name
222219
output_tmpl += "{" + tmpl_key + "}\n"
223220

@@ -230,8 +227,7 @@ def render_remote(remote_name=remote_name) -> str:
230227
return output_tmpl, render_fns
231228

232229
@ui.section("help")
233-
def render_help(self):
234-
show_help = self.state['show_help']
230+
def render_help(self, show_help):
235231
if not show_help:
236232
return ""
237233
return self.template_help

0 commit comments

Comments
 (0)