je-editor 0.0.215__py3-none-any.whl → 0.0.216__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- git_client/commit_graph.py +88 -0
- git_client/git.py +181 -0
- git_client/git_cli.py +66 -0
- git_client/github.py +50 -0
- {je_editor-0.0.215.dist-info → je_editor-0.0.216.dist-info}/METADATA +2 -1
- je_editor-0.0.216.dist-info/RECORD +127 -0
- je_editor-0.0.216.dist-info/top_level.txt +4 -0
- pyside_ui/git_ui/commit_table.py +27 -0
- pyside_ui/git_ui/git_branch_tree_widget.py +119 -0
- pyside_ui/git_ui/git_client_gui.py +292 -0
- pyside_ui/git_ui/graph_view.py +174 -0
- pyside_ui/main_ui/ai_widget/ai_config.py +19 -0
- pyside_ui/main_ui/ai_widget/ask_thread.py +17 -0
- pyside_ui/main_ui/ai_widget/chat_ui.py +130 -0
- pyside_ui/main_ui/ai_widget/langchain_interface.py +45 -0
- pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +81 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/dock_menu/build_dock_menu.py +2 -2
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/tab_menu/build_tab_menu.py +2 -2
- {je_editor/utils → utils}/multi_language/english.py +1 -1
- {je_editor/utils → utils}/multi_language/traditional_chinese.py +1 -1
- je_editor/__init__.py +0 -34
- je_editor/__main__.py +0 -18
- je_editor/start_editor.py +0 -17
- je_editor-0.0.215.dist-info/RECORD +0 -117
- je_editor-0.0.215.dist-info/top_level.txt +0 -1
- {je_editor/code_scan → code_scan}/__init__.py +0 -0
- {je_editor/code_scan → code_scan}/ruff_thread.py +0 -0
- {je_editor/code_scan → code_scan}/watchdog_implement.py +0 -0
- {je_editor/code_scan → code_scan}/watchdog_thread.py +0 -0
- {je_editor-0.0.215.dist-info → je_editor-0.0.216.dist-info}/WHEEL +0 -0
- {je_editor-0.0.215.dist-info → je_editor-0.0.216.dist-info}/licenses/LICENSE +0 -0
- {je_editor/pyside_ui → pyside_ui}/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/browser/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/browser/browser_download_window.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/browser/browser_serach_lineedit.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/browser/browser_view.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/browser/browser_widget.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/auto_save/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/auto_save/auto_save_manager.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/auto_save/auto_save_thread.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/code_format/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/code_format/pep8_format.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/code_process/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/code_process/code_exec.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/plaintext_code_edit/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/plaintext_code_edit/code_edit_plaintext.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/running_process_manager.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/shell_process/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/shell_process/shell_exec.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/syntax/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/syntax/python_syntax.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/syntax/syntax_setting.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/textedit_code_result/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/textedit_code_result/code_record.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/variable_inspector/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/code/variable_inspector/inspector_gui.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/dialog/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/dialog/ai_dialog/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/dialog/ai_dialog/set_ai_dialog.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/dialog/file_dialog/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/dialog/file_dialog/create_file_dialog.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/dialog/file_dialog/open_file_dialog.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/dialog/file_dialog/save_file_dialog.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/dialog/search_ui/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/dialog/search_ui/search_error_box.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/dialog/search_ui/search_text_box.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/console_widget/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/console_widget/console_gui.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/console_widget/qprocess_adapter.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/dock/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/dock/destroy_dock.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/editor/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/editor/editor_widget.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/editor/editor_widget_dock.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/editor/process_input.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/ipython_widget/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/ipython_widget/rich_jupyter.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/main_editor.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/dock_menu/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/file_menu/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/file_menu/build_file_menu.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/help_menu/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/help_menu/build_help_menu.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/language_menu/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/language_menu/build_language_server.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/python_env_menu/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/python_env_menu/build_venv_menu.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/build_run_menu.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/under_run_menu/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/under_run_menu/utils.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/set_menu_bar.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/style_menu/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/style_menu/build_style_menu.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/tab_menu/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/text_menu/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/menu/text_menu/build_text_menu.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/save_settings/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/save_settings/setting_utils.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/save_settings/user_color_setting_file.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/save_settings/user_setting_file.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/system_tray/__init__.py +0 -0
- {je_editor/pyside_ui → pyside_ui}/main_ui/system_tray/extend_system_tray.py +0 -0
- {je_editor/utils → utils}/__init__.py +0 -0
- {je_editor/utils → utils}/encodings/__init__.py +0 -0
- {je_editor/utils → utils}/encodings/python_encodings.py +0 -0
- {je_editor/utils → utils}/exception/__init__.py +0 -0
- {je_editor/utils → utils}/exception/exception_tags.py +0 -0
- {je_editor/utils → utils}/exception/exceptions.py +0 -0
- {je_editor/utils → utils}/file/__init__.py +0 -0
- {je_editor/utils → utils}/file/open/__init__.py +0 -0
- {je_editor/utils → utils}/file/open/open_file.py +0 -0
- {je_editor/utils → utils}/file/save/__init__.py +0 -0
- {je_editor/utils → utils}/file/save/save_file.py +0 -0
- {je_editor/utils → utils}/json/__init__.py +0 -0
- {je_editor/utils → utils}/json/json_file.py +0 -0
- {je_editor/utils → utils}/json_format/__init__.py +0 -0
- {je_editor/utils → utils}/json_format/json_process.py +0 -0
- {je_editor/utils → utils}/logging/__init__.py +0 -0
- {je_editor/utils → utils}/logging/loggin_instance.py +0 -0
- {je_editor/utils → utils}/multi_language/__init__.py +0 -0
- {je_editor/utils → utils}/multi_language/multi_language_wrapper.py +0 -0
- {je_editor/utils → utils}/redirect_manager/__init__.py +0 -0
- {je_editor/utils → utils}/redirect_manager/redirect_manager_class.py +0 -0
- {je_editor/utils → utils}/venv_check/__init__.py +0 -0
- {je_editor/utils → utils}/venv_check/check_venv.py +0 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
import logging
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from typing import List, Dict
|
4
|
+
|
5
|
+
log = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass
|
9
|
+
class CommitNode:
|
10
|
+
commit_sha: str
|
11
|
+
author_name: str
|
12
|
+
commit_date: str
|
13
|
+
commit_message: str
|
14
|
+
parent_shas: List[str]
|
15
|
+
lane_index: int = -1 # assigned later
|
16
|
+
|
17
|
+
@dataclass
|
18
|
+
class CommitGraph:
|
19
|
+
nodes: List[CommitNode] = field(default_factory=list)
|
20
|
+
index: Dict[str, int] = field(default_factory=dict) # sha -> row
|
21
|
+
|
22
|
+
def build(self, commits: List[Dict], refs: Dict[str, str]) -> None:
|
23
|
+
# commits are topo-ordered by git_client log --topo-order; we keep it.
|
24
|
+
self.nodes = [
|
25
|
+
CommitNode(
|
26
|
+
commit_sha=c["sha"],
|
27
|
+
author_name=c["author"],
|
28
|
+
commit_date=c["date"],
|
29
|
+
commit_message=c["message"],
|
30
|
+
parent_shas=c["parents"],
|
31
|
+
) for c in commits
|
32
|
+
]
|
33
|
+
self.index = {n.commit_sha: i for i, n in enumerate(self.nodes)}
|
34
|
+
self._assign_lanes()
|
35
|
+
|
36
|
+
def _assign_lanes(self) -> None:
|
37
|
+
"""
|
38
|
+
Simple lane assignment similar to 'git_client log --graph' lanes.
|
39
|
+
Greedy: reuse freed lanes; parents may create new lanes.
|
40
|
+
"""
|
41
|
+
active: Dict[int, str] = {} # lane -> sha
|
42
|
+
free_lanes: List[int] = []
|
43
|
+
|
44
|
+
for i, node in enumerate(self.nodes):
|
45
|
+
# If any active lane points to this commit, use that lane
|
46
|
+
lane_found = None
|
47
|
+
for lane, sha in list(active.items()):
|
48
|
+
if sha == node.commit_sha:
|
49
|
+
lane_found = lane
|
50
|
+
break
|
51
|
+
|
52
|
+
if lane_found is None:
|
53
|
+
if free_lanes:
|
54
|
+
node.lane_index = free_lanes.pop(0)
|
55
|
+
else:
|
56
|
+
node.lane_index = 0 if not active else max(active.keys()) + 1
|
57
|
+
else:
|
58
|
+
node.lane_index = lane_found
|
59
|
+
|
60
|
+
# Update active: current node consumes its lane, parents occupy lanes
|
61
|
+
# Remove the current sha from any lane that pointed to it
|
62
|
+
for lane, sha in list(active.items()):
|
63
|
+
if sha == node.commit_sha:
|
64
|
+
del active[lane]
|
65
|
+
|
66
|
+
# First parent continues in the same lane; others go to free/new lanes
|
67
|
+
if node.parent_shas:
|
68
|
+
first = node.parent_shas[0]
|
69
|
+
active[node.lane_index] = first
|
70
|
+
# Side branches
|
71
|
+
for p in node.parent_shas[1:]:
|
72
|
+
# Pick a free lane or new one
|
73
|
+
if free_lanes:
|
74
|
+
pl = free_lanes.pop(0)
|
75
|
+
else:
|
76
|
+
pl = 0 if not active else max(active.keys()) + 1
|
77
|
+
active[pl] = p
|
78
|
+
|
79
|
+
# Any lane whose target no longer appears in the future will be freed later
|
80
|
+
# We approximate by freeing lanes when a target didn't appear in the next rows;
|
81
|
+
# but for minimal viable, free when lane not reassigned by parents this row.
|
82
|
+
used_lanes = set(active.keys())
|
83
|
+
# Collect gaps below max lane as free lanes to reuse
|
84
|
+
max_lane = max(used_lanes) if used_lanes else -1
|
85
|
+
present = set(range(max_lane + 1))
|
86
|
+
missing = sorted(list(present - used_lanes))
|
87
|
+
# Merge missing into free_lanes maintaining order
|
88
|
+
free_lanes = sorted(set(free_lanes + missing))
|
git_client/git.py
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
import os
|
2
|
+
from datetime import datetime
|
3
|
+
|
4
|
+
from PySide6.QtCore import QThread, Signal
|
5
|
+
from git import Repo, GitCommandError, InvalidGitRepositoryError, NoSuchPathError
|
6
|
+
|
7
|
+
|
8
|
+
# -----------------------
|
9
|
+
# Simple audit logger
|
10
|
+
# -----------------------
|
11
|
+
def audit_log(repo_path: str, action: str, detail: str, ok: bool, err: str = ""):
|
12
|
+
"""
|
13
|
+
Append an audit log entry to 'audit.log' in the repo directory.
|
14
|
+
This is useful for compliance and traceability.
|
15
|
+
"""
|
16
|
+
try:
|
17
|
+
path = os.path.join(repo_path if repo_path else ".", "audit.log")
|
18
|
+
with open(path, "a", encoding="utf-8") as f:
|
19
|
+
ts = datetime.now().isoformat(timespec="seconds")
|
20
|
+
f.write(f"{ts}\taction={action}\tok={ok}\tdetail={detail}\terr={err}\n")
|
21
|
+
except Exception:
|
22
|
+
pass # Never let audit logging failure break the UI
|
23
|
+
|
24
|
+
|
25
|
+
# -----------------------
|
26
|
+
# Git service layer
|
27
|
+
# -----------------------
|
28
|
+
class GitService:
|
29
|
+
"""
|
30
|
+
Encapsulates Git operations using GitPython.
|
31
|
+
Keeps UI logic separate from Git logic.
|
32
|
+
"""
|
33
|
+
|
34
|
+
def __init__(self):
|
35
|
+
self.repo: Repo | None = None
|
36
|
+
self.repo_path: str | None = None
|
37
|
+
|
38
|
+
def open_repo(self, path: str):
|
39
|
+
try:
|
40
|
+
self.repo = Repo(path)
|
41
|
+
self.repo_path = path
|
42
|
+
audit_log(path, "open_repo", path, True)
|
43
|
+
except (InvalidGitRepositoryError, NoSuchPathError) as e:
|
44
|
+
audit_log(path, "open_repo", path, False, str(e))
|
45
|
+
raise
|
46
|
+
|
47
|
+
def list_branches(self):
|
48
|
+
self._ensure_repo()
|
49
|
+
branches = [head.name for head in self.repo.heads]
|
50
|
+
audit_log(self.repo_path, "list_branches", ",".join(branches), True)
|
51
|
+
return branches
|
52
|
+
|
53
|
+
def current_branch(self):
|
54
|
+
self._ensure_repo()
|
55
|
+
try:
|
56
|
+
return self.repo.active_branch.name
|
57
|
+
except TypeError:
|
58
|
+
return "(detached HEAD)"
|
59
|
+
|
60
|
+
def checkout(self, branch: str):
|
61
|
+
self._ensure_repo()
|
62
|
+
try:
|
63
|
+
self.repo.git.checkout(branch)
|
64
|
+
audit_log(self.repo_path, "checkout", branch, True)
|
65
|
+
except GitCommandError as e:
|
66
|
+
audit_log(self.repo_path, "checkout", branch, False, str(e))
|
67
|
+
raise
|
68
|
+
|
69
|
+
def list_commits(self, branch: str, max_count: int = 100):
|
70
|
+
self._ensure_repo()
|
71
|
+
commits = list(self.repo.iter_commits(branch, max_count=max_count))
|
72
|
+
data = [
|
73
|
+
{
|
74
|
+
"hexsha": c.hexsha,
|
75
|
+
"summary": c.summary,
|
76
|
+
"author": c.author.name if c.author else "",
|
77
|
+
"date": datetime.fromtimestamp(c.committed_date).isoformat(sep=" ", timespec="seconds"),
|
78
|
+
}
|
79
|
+
for c in commits
|
80
|
+
]
|
81
|
+
audit_log(self.repo_path, "list_commits", f"{branch}:{len(data)}", True)
|
82
|
+
return data
|
83
|
+
|
84
|
+
def show_diff_of_commit(self, commit_sha: str) -> str:
|
85
|
+
self._ensure_repo()
|
86
|
+
commit = self.repo.commit(commit_sha)
|
87
|
+
parent = commit.parents[0] if commit.parents else None
|
88
|
+
if parent is None:
|
89
|
+
null_tree = self.repo.tree(NULL_TREE)
|
90
|
+
diffs = commit.diff(null_tree, create_patch=True)
|
91
|
+
else:
|
92
|
+
diffs = commit.diff(parent, create_patch=True)
|
93
|
+
text = []
|
94
|
+
for d in diffs:
|
95
|
+
try:
|
96
|
+
text.append(d.diff.decode("utf-8", errors="replace"))
|
97
|
+
except Exception:
|
98
|
+
pass
|
99
|
+
out = "".join(text) if text else "(No patch content)"
|
100
|
+
audit_log(self.repo_path, "show_diff", commit_sha, True)
|
101
|
+
return out
|
102
|
+
|
103
|
+
def stage_all(self):
|
104
|
+
self._ensure_repo()
|
105
|
+
try:
|
106
|
+
self.repo.git.add(all=True)
|
107
|
+
audit_log(self.repo_path, "stage_all", "git_client add -A", True)
|
108
|
+
except GitCommandError as e:
|
109
|
+
audit_log(self.repo_path, "stage_all", "git_client add -A", False, str(e))
|
110
|
+
raise
|
111
|
+
|
112
|
+
def commit(self, message: str):
|
113
|
+
self._ensure_repo()
|
114
|
+
if not message.strip():
|
115
|
+
raise ValueError("Commit message is empty.")
|
116
|
+
try:
|
117
|
+
self.repo.index.commit(message)
|
118
|
+
audit_log(self.repo_path, "commit", message, True)
|
119
|
+
except Exception as e:
|
120
|
+
audit_log(self.repo_path, "commit", message, False, str(e))
|
121
|
+
raise
|
122
|
+
|
123
|
+
def pull(self, remote: str = "origin", branch: str | None = None):
|
124
|
+
self._ensure_repo()
|
125
|
+
if branch is None:
|
126
|
+
branch = self.current_branch()
|
127
|
+
try:
|
128
|
+
res = self.repo.git.pull(remote, branch)
|
129
|
+
audit_log(self.repo_path, "pull", f"{remote}/{branch}", True)
|
130
|
+
return res
|
131
|
+
except GitCommandError as e:
|
132
|
+
audit_log(self.repo_path, "pull", f"{remote}/{branch}", False, str(e))
|
133
|
+
raise
|
134
|
+
|
135
|
+
def push(self, remote: str = "origin", branch: str | None = None):
|
136
|
+
self._ensure_repo()
|
137
|
+
if branch is None:
|
138
|
+
branch = self.current_branch()
|
139
|
+
try:
|
140
|
+
res = self.repo.git.push(remote, branch)
|
141
|
+
audit_log(self.repo_path, "push", f"{remote}/{branch}", True)
|
142
|
+
return res
|
143
|
+
except GitCommandError as e:
|
144
|
+
audit_log(self.repo_path, "push", f"{remote}/{branch}", False, str(e))
|
145
|
+
raise
|
146
|
+
|
147
|
+
def remotes(self):
|
148
|
+
self._ensure_repo()
|
149
|
+
return [r.name for r in self.repo.remotes]
|
150
|
+
|
151
|
+
def _ensure_repo(self):
|
152
|
+
if self.repo is None:
|
153
|
+
raise RuntimeError("Repository not opened.")
|
154
|
+
|
155
|
+
|
156
|
+
# Null tree constant for initial commit diff
|
157
|
+
NULL_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
|
158
|
+
|
159
|
+
|
160
|
+
# -----------------------
|
161
|
+
# Worker thread wrapper
|
162
|
+
# -----------------------
|
163
|
+
class Worker(QThread):
|
164
|
+
"""
|
165
|
+
Runs a function in a separate thread to avoid blocking the UI.
|
166
|
+
Emits (result, error) when done.
|
167
|
+
"""
|
168
|
+
done = Signal(object, object)
|
169
|
+
|
170
|
+
def __init__(self, fn, *args, **kwargs):
|
171
|
+
super().__init__()
|
172
|
+
self.fn = fn
|
173
|
+
self.args = args
|
174
|
+
self.kwargs = kwargs
|
175
|
+
|
176
|
+
def run(self):
|
177
|
+
try:
|
178
|
+
res = self.fn(*self.args, **self.kwargs)
|
179
|
+
self.done.emit(res, None)
|
180
|
+
except Exception as e:
|
181
|
+
self.done.emit(None, e)
|
git_client/git_cli.py
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
import logging
|
2
|
+
import subprocess
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import List, Dict
|
5
|
+
|
6
|
+
log = logging.getLogger(__name__)
|
7
|
+
|
8
|
+
|
9
|
+
class GitCLI:
|
10
|
+
def __init__(self, repo_path: Path):
|
11
|
+
self.repo_path = Path(repo_path)
|
12
|
+
|
13
|
+
def is_git_repo(self) -> bool:
|
14
|
+
return (self.repo_path / ".git_client").exists()
|
15
|
+
|
16
|
+
def _run(self, args: List[str]) -> str:
|
17
|
+
log.debug("git_client %s", " ".join(args))
|
18
|
+
res = subprocess.run(
|
19
|
+
["git_client"] + args,
|
20
|
+
cwd=self.repo_path,
|
21
|
+
stdout=subprocess.PIPE,
|
22
|
+
stderr=subprocess.PIPE,
|
23
|
+
text=True,
|
24
|
+
encoding="utf-8",
|
25
|
+
)
|
26
|
+
if res.returncode != 0:
|
27
|
+
log.error("Git failed: %s", res.stderr.strip())
|
28
|
+
raise RuntimeError(res.stderr.strip())
|
29
|
+
return res.stdout
|
30
|
+
|
31
|
+
def get_all_refs(self) -> Dict[str, str]:
|
32
|
+
# returns refname -> commit hash
|
33
|
+
out = self._run(["show-ref", "--heads", "--tags"])
|
34
|
+
refs = {}
|
35
|
+
for line in out.splitlines():
|
36
|
+
if not line.strip():
|
37
|
+
continue
|
38
|
+
sha, ref = line.split(" ", 1)
|
39
|
+
refs[ref.strip()] = sha.strip()
|
40
|
+
return refs
|
41
|
+
|
42
|
+
def get_commits(self, max_count: int = 500) -> List[Dict]:
|
43
|
+
"""
|
44
|
+
Return recent commits across all refs, with parents.
|
45
|
+
"""
|
46
|
+
fmt = "%H%x01%P%x01%an%x01%ad%x01%s"
|
47
|
+
out = self._run([
|
48
|
+
"log", "--date=short", f"--format={fmt}", "--all",
|
49
|
+
f"--max-count={max_count}", "--topo-order"
|
50
|
+
])
|
51
|
+
commits = []
|
52
|
+
for line in out.splitlines():
|
53
|
+
if not line.strip():
|
54
|
+
continue
|
55
|
+
parts = line.split("\x01")
|
56
|
+
if len(parts) != 5:
|
57
|
+
continue
|
58
|
+
sha, parents, author, date, msg = parts
|
59
|
+
commits.append({
|
60
|
+
"sha": sha,
|
61
|
+
"parents": [p for p in parents.strip().split() if p],
|
62
|
+
"author": author,
|
63
|
+
"date": date,
|
64
|
+
"message": msg,
|
65
|
+
})
|
66
|
+
return commits
|
git_client/github.py
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
import datetime
|
2
|
+
import traceback
|
3
|
+
|
4
|
+
from git import Repo, GitCommandError, InvalidGitRepositoryError, NoSuchPathError
|
5
|
+
|
6
|
+
|
7
|
+
class GitCloneHandler:
|
8
|
+
"""
|
9
|
+
Handles cloning of remote Git repositories with audit logging.
|
10
|
+
Can be reused in UI or CLI contexts.
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(self, audit_log_path: str = "git_clone_audit.log"):
|
14
|
+
self.audit_log_path = audit_log_path
|
15
|
+
|
16
|
+
def clone_repo(self, remote_url: str, local_path: str) -> str:
|
17
|
+
"""
|
18
|
+
Clone a remote repository to a local path.
|
19
|
+
|
20
|
+
:param remote_url: The Git repository URL (e.g., https://github.com/user/repo.git)
|
21
|
+
:param local_path: The local directory to clone into
|
22
|
+
:return: The path to the cloned repository
|
23
|
+
:raises: Exception if cloning fails
|
24
|
+
"""
|
25
|
+
try:
|
26
|
+
self._log_audit(f"Cloning started: {remote_url} -> {local_path}")
|
27
|
+
Repo.clone_from(remote_url, local_path)
|
28
|
+
self._log_audit(f"Cloning completed: {remote_url} -> {local_path}")
|
29
|
+
return local_path
|
30
|
+
except (GitCommandError, InvalidGitRepositoryError, NoSuchPathError) as e:
|
31
|
+
self._log_audit(
|
32
|
+
f"ERROR: Git operation failed: {remote_url} -> {local_path}\n{str(e)}\nTraceback:\n{traceback.format_exc()}")
|
33
|
+
raise RuntimeError(f"Git operation failed: {str(e)}") from e
|
34
|
+
except Exception as e:
|
35
|
+
self._log_audit(
|
36
|
+
f"ERROR: Unexpected error during clone: {remote_url} -> {local_path}\n{str(e)}\nTraceback:\n{traceback.format_exc()}")
|
37
|
+
raise RuntimeError(f"Unexpected error during clone: {str(e)}") from e
|
38
|
+
|
39
|
+
def _log_audit(self, message: str):
|
40
|
+
"""
|
41
|
+
Append an audit log entry with timestamp.
|
42
|
+
"""
|
43
|
+
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
44
|
+
log_entry = f"[{timestamp}] {message}\n"
|
45
|
+
try:
|
46
|
+
with open(self.audit_log_path, "a", encoding="utf-8") as f:
|
47
|
+
f.write(log_entry)
|
48
|
+
except Exception:
|
49
|
+
# Never let audit logging failure break the flow
|
50
|
+
pass
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: je_editor
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.216
|
4
4
|
Summary: JEditor is basic but powerful editor include GPT
|
5
5
|
Author-email: JE-Chen <jechenmailman@gmail.com>
|
6
6
|
Project-URL: Homepage, https://github.com/JE-Chen/je_editor
|
@@ -28,6 +28,7 @@ Requires-Dist: langchain
|
|
28
28
|
Requires-Dist: pydantic
|
29
29
|
Requires-Dist: watchdog
|
30
30
|
Requires-Dist: ruff
|
31
|
+
Requires-Dist: gitpython
|
31
32
|
Dynamic: license-file
|
32
33
|
|
33
34
|
# je_editor
|
@@ -0,0 +1,127 @@
|
|
1
|
+
code_scan/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
code_scan/ruff_thread.py,sha256=pQjfsaQXI2ZJ6nAGq0Fpw-k3S2V_gYQ0lPgYnNC9Re0,938
|
3
|
+
code_scan/watchdog_implement.py,sha256=9N5TCvWQKi66DSrViUaNhUwP5TkYhmbz_ncS9KTGPx8,1313
|
4
|
+
code_scan/watchdog_thread.py,sha256=jHJCIsDN1kcfK4dIY5MHORafV4EJNN93x4GgNnEx1G4,833
|
5
|
+
git_client/commit_graph.py,sha256=Zg0HD-yxbyGQAOAX27QKz7enwQ9UAhNaXgXll36199o,3486
|
6
|
+
git_client/git.py,sha256=mxv-WRryIKRUONLU6V8X9ZMzIgYp76f-k3pZpqLqIbc,6253
|
7
|
+
git_client/git_cli.py,sha256=yINNHrFHQl-7zQxSk2Dhbw38EJCpOg5jbSe1ChKTVSM,2119
|
8
|
+
git_client/github.py,sha256=7ipQwkTS2ovjTe66kOyxlJth6HlEpqEZJ21PlKpKDw0,2124
|
9
|
+
je_editor-0.0.216.dist-info/licenses/LICENSE,sha256=KMhUHh6pnIUvmXDW-49L_Sz63bqkOlPDqsecaqKiitU,1091
|
10
|
+
pyside_ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
+
pyside_ui/browser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
pyside_ui/browser/browser_download_window.py,sha256=KEO8AoZYSDuES0z5os9ziD44lS47kIo3kbd5iOUM2Go,2018
|
13
|
+
pyside_ui/browser/browser_serach_lineedit.py,sha256=GYX6faCIxl-HtRXHZoc3EAuK6Ll41jgUV3ksawY5rsc,899
|
14
|
+
pyside_ui/browser/browser_view.py,sha256=vH_-Xw1DE5RqTz-wYxkKl0X6tzpcFVKXQqMqay7tJiw,1661
|
15
|
+
pyside_ui/browser/browser_widget.py,sha256=0fVnl6ye4FBz3bnxm-0SSEWW8bGlHS5DqQmMew5CKDk,3031
|
16
|
+
pyside_ui/code/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
|
+
pyside_ui/code/running_process_manager.py,sha256=VdfdprPNlPmMySOKDOjEEl5X9IsJVPSDb0a2KDKgiQg,1008
|
18
|
+
pyside_ui/code/auto_save/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
+
pyside_ui/code/auto_save/auto_save_manager.py,sha256=hQ03TVq-c-mGku51DrjpmLtsPkVcZv4G_jaozC06Ir0,1019
|
20
|
+
pyside_ui/code/auto_save/auto_save_thread.py,sha256=V_A8_4JZdHdF9e2E_rYY_paS7rnJjB8_YVPxTyHZ_QE,1662
|
21
|
+
pyside_ui/code/code_format/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
|
+
pyside_ui/code/code_format/pep8_format.py,sha256=zrCkUdz2SG4bnMSkXfccvpHAmUOxXLedSBxL_nkdaoE,3574
|
23
|
+
pyside_ui/code/code_process/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
|
+
pyside_ui/code/code_process/code_exec.py,sha256=k0EPC2ZhC7yZTJkcezUvELIuD8AkU_GoXQcM8JL2Ptc,10686
|
25
|
+
pyside_ui/code/plaintext_code_edit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
|
+
pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py,sha256=lcwmBemFenH-uDGe6fo70MS47emzPFgy9XL7cipwryY,14249
|
27
|
+
pyside_ui/code/shell_process/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
|
+
pyside_ui/code/shell_process/shell_exec.py,sha256=3x9nMRlAQdK7asoB_GD-Dx9LyrCOic8YIy0wzapmpiw,9527
|
29
|
+
pyside_ui/code/syntax/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
|
+
pyside_ui/code/syntax/python_syntax.py,sha256=9rK7_DYAANgf28PUdVl9xs42b76Re_SWwIpijEGBuzQ,3054
|
31
|
+
pyside_ui/code/syntax/syntax_setting.py,sha256=oaLRF_83Oa6bcAorf7AlZG0Mrt4FmsK7b4aA-LS_bRo,2284
|
32
|
+
pyside_ui/code/textedit_code_result/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
33
|
+
pyside_ui/code/textedit_code_result/code_record.py,sha256=pT-CmcADEaXLZ0ih6D6rpe1XywgszrEpcJxtJYgopJE,2162
|
34
|
+
pyside_ui/code/variable_inspector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
35
|
+
pyside_ui/code/variable_inspector/inspector_gui.py,sha256=QMcwN3td5Nr01bWpUHEFJzl-wb0Ifigqx6ybfvQSzJY,4747
|
36
|
+
pyside_ui/dialog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
|
+
pyside_ui/dialog/ai_dialog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
38
|
+
pyside_ui/dialog/ai_dialog/set_ai_dialog.py,sha256=w_cku2dQthtk0TDSxEeDB-tvzdHV4vmUhFVDlb2ynO8,2392
|
39
|
+
pyside_ui/dialog/file_dialog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
40
|
+
pyside_ui/dialog/file_dialog/create_file_dialog.py,sha256=3NvGVld_If2TsM_Xt0pgWDTcj51szS8MxG1raU-tP2A,1650
|
41
|
+
pyside_ui/dialog/file_dialog/open_file_dialog.py,sha256=wTI4TwMfJ-960c_C0cLy2EhWbZosewSmpyWOrcf9yLE,3806
|
42
|
+
pyside_ui/dialog/file_dialog/save_file_dialog.py,sha256=pyPfwgrDiDpMKk8T_4oBukgJLxdWnzIKSziwFt4YuUU,1781
|
43
|
+
pyside_ui/dialog/search_ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
44
|
+
pyside_ui/dialog/search_ui/search_error_box.py,sha256=jSa423cQW_xQ2WAj46CCWPsCP0Gy7g-UDe5o8LQyKQk,1190
|
45
|
+
pyside_ui/dialog/search_ui/search_text_box.py,sha256=ul-98FVByq_TNkkLZcBSF4VSwIQNBBj-4e6jYlh6YEA,1229
|
46
|
+
pyside_ui/git_ui/commit_table.py,sha256=5NkPx27omhtql1n0E51i5wuXqojnioxRD3SJPq-X0Xg,1136
|
47
|
+
pyside_ui/git_ui/git_branch_tree_widget.py,sha256=zCVz-_67Rtz_TtKVnzGxoX3ia8FLb2qgkpeXQthwQbc,4473
|
48
|
+
pyside_ui/git_ui/git_client_gui.py,sha256=U0bHZxUGMeebf320JX107-54jXS5s30FyPocwu2UFGs,11694
|
49
|
+
pyside_ui/git_ui/graph_view.py,sha256=0qLxZMzRZMpDFACiUlv0EyyVkAZiYFlU7d2jE5NuVIE,6045
|
50
|
+
pyside_ui/main_ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
51
|
+
pyside_ui/main_ui/main_editor.py,sha256=O5wiy5QW8IrxHAzBkcu7v4OmFraVxDIkoQhwWA032GU,11546
|
52
|
+
pyside_ui/main_ui/ai_widget/ai_config.py,sha256=Uc9UkrIeALqjmZCF5ukXtSgU1EB4xa5223gk3CfYXX4,459
|
53
|
+
pyside_ui/main_ui/ai_widget/ask_thread.py,sha256=ma5Cnwlr0RbU-sytmodcYabWueyGE8DKKISZ_0zf3Eo,571
|
54
|
+
pyside_ui/main_ui/ai_widget/chat_ui.py,sha256=yBoWXvGvgUCWjmPoBcOhj0mtOm0R42mYEs7FokYQmnE,6763
|
55
|
+
pyside_ui/main_ui/ai_widget/langchain_interface.py,sha256=OgBaUcUMR0Z2VQUO9QfCX2wJc_qBFw3IvoMCiEGBTug,1768
|
56
|
+
pyside_ui/main_ui/console_widget/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
|
+
pyside_ui/main_ui/console_widget/console_gui.py,sha256=3H0Ocj9oDtgAoH7mT2YjDsSeFW3FphsstJqtIesS4yA,5572
|
58
|
+
pyside_ui/main_ui/console_widget/qprocess_adapter.py,sha256=UIyIoCEyt6hMxc2oLH68ioERFyuPDnPub05_Yi_-rGs,2304
|
59
|
+
pyside_ui/main_ui/dock/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
|
+
pyside_ui/main_ui/dock/destroy_dock.py,sha256=MTN45BykNm6FA4gMW7gI4Kr9orTdcxVTm7mch3DUhaw,604
|
61
|
+
pyside_ui/main_ui/editor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
62
|
+
pyside_ui/main_ui/editor/editor_widget.py,sha256=AnrBGoToipAtxh2XRNey3wskKns6AxJ6hwtdST3fovw,10387
|
63
|
+
pyside_ui/main_ui/editor/editor_widget_dock.py,sha256=6oDf0pKx0sThlk_Qz0gduPIpasPqib6OYnMnQ4FRXFo,1930
|
64
|
+
pyside_ui/main_ui/editor/process_input.py,sha256=zj29E3He5_PD23HnIaDQ1vn3LWRECXTf8P7MWsX73k0,3401
|
65
|
+
pyside_ui/main_ui/ipython_widget/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
66
|
+
pyside_ui/main_ui/ipython_widget/rich_jupyter.py,sha256=LKbMbgpstsk_EiwbBSJ9Ljn7Ia3j2UNpQ_JAI6_LJ4Q,1629
|
67
|
+
pyside_ui/main_ui/menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
68
|
+
pyside_ui/main_ui/menu/set_menu_bar.py,sha256=I12DXLyRO4cKe17fQY-QDazKEIh9W36LNV1aKUw47MU,1823
|
69
|
+
pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py,sha256=1wD6FI21Dw3IcQONuTkHP9rUPGwY70OvCtSc-ZcTKMk,3793
|
70
|
+
pyside_ui/main_ui/menu/dock_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
71
|
+
pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py,sha256=Wf03lbITEifq5YywEvqsXqzLxes6R-CBsAE00giTwA0,8402
|
72
|
+
pyside_ui/main_ui/menu/file_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
73
|
+
pyside_ui/main_ui/menu/file_menu/build_file_menu.py,sha256=YT6kvXShRbfM4Qo51VveO_IK8uDqlNrGwHZUiM6_Tes,6881
|
74
|
+
pyside_ui/main_ui/menu/help_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
75
|
+
pyside_ui/main_ui/menu/help_menu/build_help_menu.py,sha256=iy1xDpV-u4DhZp8ZsetVKwHuJCctYgCWMpkIv12u3V0,2869
|
76
|
+
pyside_ui/main_ui/menu/language_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
77
|
+
pyside_ui/main_ui/menu/language_menu/build_language_server.py,sha256=23jKdAYxN65xJMVd5NbCwhIjwh2kfXVDszN1wmIs1A0,2333
|
78
|
+
pyside_ui/main_ui/menu/python_env_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
79
|
+
pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py,sha256=Hv_UbIwlRGlcpvDVSHjNHFslYvFawLh81raWA6zoJAY,9206
|
80
|
+
pyside_ui/main_ui/menu/run_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
81
|
+
pyside_ui/main_ui/menu/run_menu/build_run_menu.py,sha256=r1jl9FYHttRK6D09YItjVI6qsz46TA-OCV03TYOI4W8,5269
|
82
|
+
pyside_ui/main_ui/menu/run_menu/under_run_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
83
|
+
pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py,sha256=eTkByv3TBxYuyxNWYnohWNPo2I4COxglV_GEeTsgNvk,3329
|
84
|
+
pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py,sha256=jfc7l6zYpRdtYdj2f0nydP41hxEkqCXFISLcTn_AU0E,3125
|
85
|
+
pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py,sha256=KNRNjMyqhaimjOrwuEwtVfGD90xph0VYl25_-kUGllE,2896
|
86
|
+
pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py,sha256=zk5adQ7xidJgzaO81kKxc7LI34jCplcocL6vhacHEec,858
|
87
|
+
pyside_ui/main_ui/menu/style_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
88
|
+
pyside_ui/main_ui/menu/style_menu/build_style_menu.py,sha256=OjfcjQ7fA1Z1rMwIS9oGkIkC-jy-e0jdGoYo1btTqUQ,1852
|
89
|
+
pyside_ui/main_ui/menu/tab_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
90
|
+
pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py,sha256=XPyNSkNM-pZJbRfpeymYooWNokv_IT36csvJfEgXP-Q,9238
|
91
|
+
pyside_ui/main_ui/menu/text_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
92
|
+
pyside_ui/main_ui/menu/text_menu/build_text_menu.py,sha256=k3pOpK4i8ccs3D8VIrNx8g3kUDp1fZdqv9H5uEvNl5c,3558
|
93
|
+
pyside_ui/main_ui/save_settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
94
|
+
pyside_ui/main_ui/save_settings/setting_utils.py,sha256=p1gY71j_PgSdlibA0uOm1eIKvOlIH_jNIJ-At0x3iMU,600
|
95
|
+
pyside_ui/main_ui/save_settings/user_color_setting_file.py,sha256=o0BZRHPMUEtoXaf0ihfOEzt3doLwgSmU3gQx51cCWAg,3145
|
96
|
+
pyside_ui/main_ui/save_settings/user_setting_file.py,sha256=XkHC2BABD6xLIx7gFTy6NzjqYXFMImSngwqp9GxELdc,1034
|
97
|
+
pyside_ui/main_ui/system_tray/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
98
|
+
pyside_ui/main_ui/system_tray/extend_system_tray.py,sha256=xrk3TEaWdYyqc2U7po928sHV_tUj5QCdOsZEDKoU3_Y,2259
|
99
|
+
utils/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
100
|
+
utils/encodings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
101
|
+
utils/encodings/python_encodings.py,sha256=Nn_GsBZcPHcY5djoL2ki5eriKUpCI0ZgZ6XKgnEo1YI,1613
|
102
|
+
utils/exception/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
103
|
+
utils/exception/exception_tags.py,sha256=KjkbbqrtIuNn07657AOVEpX0RZlOpTUFxKPiHld3xgM,1104
|
104
|
+
utils/exception/exceptions.py,sha256=4e2ivqFWgBq4L_8QgRjd79xdOAgR_tyW_hpBlQeePpw,506
|
105
|
+
utils/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
106
|
+
utils/file/open/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
107
|
+
utils/file/open/open_file.py,sha256=zaqj0g87AbczCTNnVW9xPsQ2Da9pTsUwO75WIp08IPg,1211
|
108
|
+
utils/file/save/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
109
|
+
utils/file/save/save_file.py,sha256=j_WpSXz2M0XweuruiC332-ZXpqgPiYlZULOnEiwuUOM,998
|
110
|
+
utils/json/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
111
|
+
utils/json/json_file.py,sha256=zTzQp-VFlaXg7puxC3EgEECURuuDmfJ8I9emX0EZyfw,1699
|
112
|
+
utils/json_format/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
113
|
+
utils/json_format/json_process.py,sha256=tszo48OnVivVkuuPrl-FtGaaq-1YOJLKT2o2A3qQi3M,1250
|
114
|
+
utils/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
115
|
+
utils/logging/loggin_instance.py,sha256=5MUuLesU_WpR_Iu1uoAATi5Bj2yMYkimZrMfiXbk5pI,881
|
116
|
+
utils/multi_language/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
117
|
+
utils/multi_language/english.py,sha256=OJc8VEh4PhMQhdHz7_aWSzIdNoL2vJIK-uPQw0ZOM1g,9974
|
118
|
+
utils/multi_language/multi_language_wrapper.py,sha256=_MtYrE51poQ1p_iWvT2eg_604uj6IsiBgGpifWpYLGc,1050
|
119
|
+
utils/multi_language/traditional_chinese.py,sha256=i-OYyq0ywYo8e3YqYQNoX5lAs38j6W3xeg47qOJ7MGU,10052
|
120
|
+
utils/redirect_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
121
|
+
utils/redirect_manager/redirect_manager_class.py,sha256=1gICetKpohBvmxmVhnqeCRq7AQS2YWK4AURmrqnVYVw,2277
|
122
|
+
utils/venv_check/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
123
|
+
utils/venv_check/check_venv.py,sha256=oCrMdue4NYUUGrVifh_iHFwIgxVx9azYN4jz3Xiulgg,999
|
124
|
+
je_editor-0.0.216.dist-info/METADATA,sha256=LCghqFMAv91NyxBQ5TiFtlTXPPAHZu1Q1DLEgrts_4s,3472
|
125
|
+
je_editor-0.0.216.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
126
|
+
je_editor-0.0.216.dist-info/top_level.txt,sha256=Sj3iPnIESliaN1BbNN4PibPXeNQabxXhcqnNP-SfGTE,37
|
127
|
+
je_editor-0.0.216.dist-info/RECORD,,
|
@@ -0,0 +1,27 @@
|
|
1
|
+
from PySide6.QtGui import QStandardItemModel, QStandardItem
|
2
|
+
from PySide6.QtWidgets import QTableView
|
3
|
+
|
4
|
+
|
5
|
+
class CommitTable(QTableView):
|
6
|
+
def __init__(self, parent=None):
|
7
|
+
super().__init__(parent)
|
8
|
+
self.model_data = QStandardItemModel(0, 5, self) # 多一欄
|
9
|
+
self.model_data.setHorizontalHeaderLabels(["#", "SHA", "Message", "Author", "Date"])
|
10
|
+
self.setModel(self.model_data)
|
11
|
+
self.setSelectionBehavior(QTableView.SelectionBehavior.SelectRows)
|
12
|
+
self.setEditTriggers(QTableView.EditTrigger.NoEditTriggers)
|
13
|
+
self.horizontalHeader().setStretchLastSection(True)
|
14
|
+
|
15
|
+
def set_commits(self, commits):
|
16
|
+
self.model_data.setRowCount(0)
|
17
|
+
for idx, c in enumerate(commits, start=1):
|
18
|
+
row = [
|
19
|
+
QStandardItem(str(idx)), # 行號
|
20
|
+
QStandardItem(c["sha"][:7]),
|
21
|
+
QStandardItem(c["message"]),
|
22
|
+
QStandardItem(c["author"]),
|
23
|
+
QStandardItem(c["date"]),
|
24
|
+
]
|
25
|
+
for item in row:
|
26
|
+
item.setEditable(False)
|
27
|
+
self.model_data.appendRow(row)
|