Skip to content

Commit 17c4b29

Browse files
committed
refactor: implement GitHelper for streamlined Git operations
1 parent b94ed5c commit 17c4b29

File tree

2 files changed

+209
-162
lines changed

2 files changed

+209
-162
lines changed

plugin/commands.py

Lines changed: 34 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
import json
1717
import os
18-
import subprocess
1918
import uuid
2019
from abc import ABC
2120
from collections.abc import Callable
@@ -70,6 +69,7 @@
7069
prepare_conversation_turn_request,
7170
preprocess_chat_message,
7271
preprocess_message_for_html,
72+
GitHelper,
7373
)
7474
from .log import log_info
7575
from .types import (
@@ -609,8 +609,34 @@ def run(self, plugin: CopilotPlugin, session: Session, _: sublime.Edit) -> None:
609609
session.send_request(Request(REQ_CONVERSATION_AGENTS, {}), self._on_result_conversation_agents)
610610

611611
def _on_result_conversation_agents(self, payload: list[CopilotRequestConversationAgent]) -> None:
612-
for agent in payload:
613-
print(agent["slug"], agent["name"], agent["description"])
612+
if not payload:
613+
status_message("No conversation agents available", icon="❌")
614+
return
615+
616+
window = self.view.window() or sublime.active_window()
617+
window.show_quick_panel(
618+
[
619+
sublime.QuickPanelItem(
620+
trigger=agent["slug"],
621+
details=agent["name"],
622+
annotation=agent["description"],
623+
)
624+
for agent in payload
625+
],
626+
lambda index: self._on_agent_selected(index, payload),
627+
)
628+
629+
def _on_agent_selected(self, index: int, agents: list[CopilotRequestConversationAgent]) -> None:
630+
if index == -1:
631+
return
632+
633+
# For now, just show detailed info about the selected agent
634+
agent = agents[index]
635+
message_dialog(
636+
f"Agent: {agent['name']}\n"
637+
f"Slug: {agent['slug']}\n"
638+
f"Description: {agent['description']}"
639+
)
614640

615641

616642
class CopilotRegisterConversationToolsCommand(CopilotTextCommand):
@@ -792,175 +818,21 @@ def _on_result_code_review(self, payload: dict, window: sublime.Window) -> None:
792818
class CopilotGitCommitGenerateCommand(CopilotTextCommand):
793819
"""Command to generate Git commit messages using GitHub Copilot."""
794820

795-
def _run_git_command(self, cmd: list[str], cwd: str | None = None) -> str | None:
796-
"""Run a git command and return the output, or None if it fails."""
797-
try:
798-
result = subprocess.run(
799-
cmd,
800-
cwd=cwd,
801-
capture_output=True,
802-
text=True,
803-
timeout=10,
804-
check=True
805-
)
806-
return result.stdout.strip()
807-
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
808-
return None
809-
810-
def _get_git_repo_root(self, start_path: str) -> str | None:
811-
"""Find the Git repository root starting from the given path."""
812-
try:
813-
result = subprocess.run(
814-
["git", "rev-parse", "--show-toplevel"],
815-
cwd=start_path,
816-
capture_output=True,
817-
text=True,
818-
timeout=5,
819-
check=True
820-
)
821-
return result.stdout.strip()
822-
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
823-
return None
824-
825-
def _get_git_changes(self, repo_root: str) -> list[str]:
826-
"""Get actual diff content for staged and unstaged changes."""
827-
changes = []
828-
829-
# Get staged changes (actual diff content)
830-
staged_output = self._run_git_command(["git", "diff", "--cached"], repo_root)
831-
if staged_output:
832-
changes.append(f"=== STAGED CHANGES ===\n{staged_output}")
833-
834-
# Get unstaged changes (actual diff content)
835-
unstaged_output = self._run_git_command(["git", "diff"], repo_root)
836-
if unstaged_output:
837-
changes.append(f"=== UNSTAGED CHANGES ===\n{unstaged_output}")
838-
839-
# Get untracked files content (for small files)
840-
untracked_output = self._run_git_command(["git", "ls-files", "--others", "--exclude-standard"], repo_root)
841-
if untracked_output:
842-
untracked_files = [f.strip() for f in untracked_output.split('\n') if f.strip()]
843-
untracked_content = []
844-
845-
for file_path in untracked_files[:5]: # Limit to first 5 untracked files
846-
full_path = Path(repo_root) / file_path
847-
try:
848-
if full_path.is_file() and full_path.stat().st_size < 10000: # Only read files < 10KB
849-
content = full_path.read_text(encoding='utf-8', errors='ignore')
850-
untracked_content.append(f"=== NEW FILE: {file_path} ===\n{content}")
851-
except (OSError, UnicodeDecodeError):
852-
untracked_content.append(f"=== NEW FILE: {file_path} ===\n[Binary or unreadable file]")
853-
854-
if untracked_content:
855-
changes.extend(untracked_content)
856-
857-
return changes
858-
859-
def _get_current_user_email(self, repo_root: str) -> str | None:
860-
"""Get the current Git user email."""
861-
return self._run_git_command(["git", "config", "user.email"], repo_root)
862-
863-
def _get_user_commits(self, repo_root: str, user_email: str | None, limit: int = 10) -> list[str]:
864-
"""Get recent commits by the current user."""
865-
if not user_email:
866-
return []
867-
868-
cmd = [
869-
"git", "log",
870-
f"--author={user_email}",
871-
f"--max-count={limit}",
872-
"--oneline",
873-
"--no-merges"
874-
]
875-
876-
output = self._run_git_command(cmd, repo_root)
877-
if output:
878-
return [line.strip() for line in output.split('\n') if line.strip()]
879-
return []
880-
881-
def _get_recent_commits(self, repo_root: str, limit: int = 20) -> list[str]:
882-
"""Get recent commits from all contributors."""
883-
cmd = [
884-
"git", "log",
885-
f"--max-count={limit}",
886-
"--oneline",
887-
"--no-merges"
888-
]
889-
890-
output = self._run_git_command(cmd, repo_root)
891-
if output:
892-
return [line.strip() for line in output.split('\n') if line.strip()]
893-
return []
894-
895-
def _get_workspace_folder(self) -> str | None:
896-
"""Get the workspace folder path."""
897-
if not (window := self.view.window()):
898-
return None
899-
900-
# Try to get the project path
901-
if folders := window.folders():
902-
return folders[0]
903-
904-
# Fallback to file directory
905-
if file_name := self.view.file_name():
906-
return str(Path(file_name).parent)
907-
908-
return None
909-
910-
def _get_user_language(self) -> str | None:
911-
"""Get user's language preference from Sublime Text settings."""
912-
# Try to get language from various Sublime Text settings
913-
settings = sublime.load_settings("Preferences.sublime-settings")
914-
915-
# Check for explicit language setting
916-
if lang := settings.get("language"):
917-
return lang
918-
919-
# Fallback to system locale
920-
try:
921-
import locale
922-
return locale.getdefaultlocale()[0]
923-
except:
924-
return "en-US"
925-
926821
@_provide_plugin_session()
927822
def run(self, plugin: CopilotPlugin, session: Session, _: sublime.Edit) -> None:
928823
if not (window := self.view.window()):
929824
return
930825

931-
# Get workspace folder
932-
workspace_folder = self._get_workspace_folder()
933-
if not workspace_folder:
934-
status_message("No workspace folder found", icon="❌")
935-
return
936-
937-
# Find Git repository root
938-
repo_root = self._get_git_repo_root(workspace_folder)
939-
if not repo_root:
940-
status_message("Not in a Git repository", icon="❌")
826+
# Gather all Git data using GitHelper
827+
git_data = GitHelper.gather_git_commit_data(self.view)
828+
if not git_data:
829+
status_message("Not in a Git repository or no workspace folder found", icon="❌")
941830
return
942-
943-
# Gather Git information
944-
changes = self._get_git_changes(repo_root)
945-
user_email = self._get_current_user_email(repo_root)
946-
user_commits = self._get_user_commits(repo_root, user_email)
947-
recent_commits = self._get_recent_commits(repo_root)
948-
user_language = self._get_user_language()
949-
950-
# Prepare payload according to expected structure
951-
payload = {
952-
"changes": changes,
953-
"userCommits": user_commits,
954-
"recentCommits": recent_commits,
955-
"workspaceFolder": workspace_folder,
956-
"userLanguage": user_language,
957-
}
958-
959831
status_message("Generating commit message...", icon="⏳")
960832

961833
# Send request to generate commit message
962834
session.send_request(
963-
Request(REQ_GIT_COMMIT_GENERATE, payload),
835+
Request(REQ_GIT_COMMIT_GENERATE, git_data),
964836
lambda response: self._on_result_git_commit_generate(response, window)
965837
)
966838

0 commit comments

Comments
 (0)