diff --git a/Context.sublime-menu b/Context.sublime-menu new file mode 100644 index 000000000..299235f20 --- /dev/null +++ b/Context.sublime-menu @@ -0,0 +1,38 @@ +[ + { + "caption": "GitSavvy: Line History", + "command": "gs_ctx_line_history" + }, + { + "caption": "GitSavvy: Pick-axe", + "command": "gs_ctx_pick_axe" + }, + { + "caption": "GitSavvy: Stage selected hunk", + "command": "gs_ctx_stage_hunk" + } + // { + // "caption": " ...", + // "children": [ + // { + // "caption": "Repo History", + // "command": "gs_graph", + // "args": { "all": true } + // }, + // { + // "caption": "Path History", + // "command": "gs_graph_current_path" + // }, + // { + // "caption": "File History", + // "command": "gs_graph_current_file", + // "args": { "all": false } + // }, + // { "caption": "-" }, + // { + // "caption": "Show current file revision at HEAD", + // "command": "gs_show_file_at_commit" + // }, + // ] + // }, +] diff --git a/GitSavvy.sublime-settings b/GitSavvy.sublime-settings index ce4ff79a4..ebf4b6a24 100755 --- a/GitSavvy.sublime-settings +++ b/GitSavvy.sublime-settings @@ -369,5 +369,11 @@ When set to `true`, no views will receive the prompt asking to initialize Git in the current view's directory when not found. */ - "disable_git_init_prompt": false + "disable_git_init_prompt": false, + + /* + Disable adding any entries to the context menu available when right-clicking + in any view. + */ + "disable_context_menus": false } diff --git a/core/commands/__init__.py b/core/commands/__init__.py index d355bdff9..46610b2c9 100644 --- a/core/commands/__init__.py +++ b/core/commands/__init__.py @@ -7,6 +7,7 @@ from .cherry_pick import * from .commit import * from .commit_compare import * +from .context_menu import * from .custom import * from .diff import * from .fetch import * diff --git a/core/commands/context_menu.py b/core/commands/context_menu.py new file mode 100644 index 000000000..04fdeaf53 --- /dev/null +++ b/core/commands/context_menu.py @@ -0,0 +1,111 @@ +from functools import lru_cache + +import sublime + +from GitSavvy.core.base_commands import GsTextCommand + +from typing import Callable, List, Optional, TypeVar +T = TypeVar("T") + +__all__ = ( + "gs_ctx_line_history", + "gs_ctx_pick_axe", + "gs_ctx_stage_hunk", +) + + +# Provide a `CommandContext` as the global `Context` which +# is valid for this exact "runtime-task". This is to speed-up +# the preconditions in `is_enabled` and `is_visible`. + +def cached_property(fn: Callable[..., T]) -> T: + return property(lru_cache(1)(fn)) # type: ignore[return-value] + + +class CommandContext: + def __init__(self, cmd: GsTextCommand): + self._cmd = cmd + + @cached_property + def enabled(self) -> bool: + return not self._cmd.savvy_settings.get("disable_context_menus") + + @cached_property + def sel(self) -> List[sublime.Region]: + return list(self._cmd.view.sel()) + + @cached_property + def repo_path(self) -> Optional[str]: + return self._cmd.find_repo_path() + + @cached_property + def file_path(self) -> Optional[str]: + return self._cmd.file_path + + +Context = None + + +def get_context(self) -> CommandContext: + global Context + if not Context: + Context = CommandContext(self) + sublime.set_timeout(reset_context) + + return Context + + +def reset_context(): + global Context + Context = None + + +class gs_ctx_line_history(GsTextCommand): + def is_enabled(self) -> bool: + ctx = get_context(self) + return bool( + ctx.sel + and ctx.repo_path + ) + + def is_visible(self) -> bool: + ctx = get_context(self) + return ctx.enabled and bool(ctx.repo_path) + + def run(self, edit) -> None: + self.view.run_command("gs_line_history") + + +class gs_ctx_stage_hunk(GsTextCommand): + def is_enabled(self) -> bool: + ctx = get_context(self) + return bool( + ctx.sel + and ctx.repo_path + and self.view.file_name() + and not self.view.is_dirty() + ) + + def is_visible(self) -> bool: + ctx = get_context(self) + return ctx.enabled and bool(ctx.repo_path) + + def run(self, edit) -> None: + self.view.run_command("gs_stage_hunk") + + +class gs_ctx_pick_axe(GsTextCommand): + def is_enabled(self) -> bool: + ctx = get_context(self) + return bool( + ctx.sel + and ctx.repo_path + and all(self.view.substr(r).strip() for r in ctx.sel) + ) + + def is_visible(self) -> bool: + ctx = get_context(self) + return ctx.enabled and bool(ctx.repo_path) + + def run(self, edit) -> None: + self.view.run_command("gs_graph_pickaxe")