je-editor 0.0.221__py3-none-any.whl → 0.0.223__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.
Potentially problematic release.
This version of je-editor might be problematic. Click here for more details.
- je_editor/__init__.py +2 -2
- je_editor/code_scan/ruff_thread.py +33 -6
- je_editor/code_scan/watchdog_implement.py +42 -20
- je_editor/code_scan/watchdog_thread.py +54 -9
- je_editor/git_client/commit_graph.py +32 -43
- je_editor/git_client/{git.py → git_action.py} +1 -1
- je_editor/git_client/git_cli.py +4 -4
- je_editor/pyside_ui/browser/browser_download_window.py +41 -5
- je_editor/pyside_ui/browser/browser_serach_lineedit.py +25 -1
- je_editor/pyside_ui/browser/browser_view.py +56 -3
- je_editor/pyside_ui/browser/browser_widget.py +52 -22
- je_editor/pyside_ui/browser/main_browser_widget.py +72 -0
- je_editor/pyside_ui/git_ui/code_diff_compare/__init__.py +0 -0
- je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +90 -0
- je_editor/pyside_ui/git_ui/code_diff_compare/line_number_code_viewer.py +141 -0
- je_editor/pyside_ui/git_ui/code_diff_compare/multi_file_diff_viewer.py +88 -0
- je_editor/pyside_ui/git_ui/code_diff_compare/side_by_side_diff_widget.py +271 -0
- je_editor/pyside_ui/git_ui/git_client/__init__.py +0 -0
- je_editor/pyside_ui/git_ui/{git_branch_tree_widget.py → git_client/git_branch_tree_widget.py} +17 -14
- je_editor/pyside_ui/git_ui/git_client/git_client_gui.py +799 -0
- je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +1 -1
- je_editor/pyside_ui/main_ui/main_editor.py +5 -8
- je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +17 -17
- je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +2 -2
- je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +21 -21
- je_editor/utils/multi_language/english.py +2 -1
- je_editor/utils/multi_language/traditional_chinese.py +2 -2
- {je_editor-0.0.221.dist-info → je_editor-0.0.223.dist-info}/METADATA +3 -3
- {je_editor-0.0.221.dist-info → je_editor-0.0.223.dist-info}/RECORD +34 -28
- je_editor/git_client/github.py +0 -50
- je_editor/pyside_ui/git_ui/git_client_gui.py +0 -291
- /je_editor/pyside_ui/git_ui/{commit_table.py → git_client/commit_table.py} +0 -0
- /je_editor/pyside_ui/git_ui/{graph_view.py → git_client/graph_view.py} +0 -0
- {je_editor-0.0.221.dist-info → je_editor-0.0.223.dist-info}/WHEEL +0 -0
- {je_editor-0.0.221.dist-info → je_editor-0.0.223.dist-info}/licenses/LICENSE +0 -0
- {je_editor-0.0.221.dist-info → je_editor-0.0.223.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
from PySide6.QtGui import QColor, QTextCursor, QTextCharFormat, QFont
|
|
2
|
+
from PySide6.QtWidgets import QPlainTextEdit, QTextEdit, QWidget, QHBoxLayout, QVBoxLayout, QLabel
|
|
3
|
+
|
|
4
|
+
from je_editor.pyside_ui.git_ui.code_diff_compare.line_number_code_viewer import LineNumberedCodeViewer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SideBySideDiffWidget(QWidget):
|
|
8
|
+
"""
|
|
9
|
+
Side-by-side diff viewer widget.
|
|
10
|
+
左右對照的差異檢視元件。
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, parent=None):
|
|
14
|
+
super().__init__(parent)
|
|
15
|
+
|
|
16
|
+
# === 顏色設定 / Color configuration ===
|
|
17
|
+
self.is_dark = True
|
|
18
|
+
# 刪除行背景 / Deleted line background
|
|
19
|
+
self.color_del = QColor("#ffcccc") # 淡紅色,深淺背景都清楚
|
|
20
|
+
# 新增行背景 / Added line background
|
|
21
|
+
self.color_add = QColor("#ccffcc") # 淡綠色,對比度佳
|
|
22
|
+
# Hunk header 背景
|
|
23
|
+
self.color_hunk = QColor("#cce5ff") # 淡藍色,醒目但不刺眼
|
|
24
|
+
# Diff header 背景
|
|
25
|
+
self.color_header = QColor("#e0e0e0") # 淺灰,適合標題區塊
|
|
26
|
+
|
|
27
|
+
# === 左右檔名標籤 / File name labels ===
|
|
28
|
+
self.leftLabel = QLabel("Left: (old)")
|
|
29
|
+
self.rightLabel = QLabel("Right: (new)")
|
|
30
|
+
font = QFont()
|
|
31
|
+
font.setBold(True)
|
|
32
|
+
self.leftLabel.setFont(font)
|
|
33
|
+
self.rightLabel.setFont(font)
|
|
34
|
+
|
|
35
|
+
# === 左右文字編輯器 / Left and right code editors ===
|
|
36
|
+
self.leftEdit = LineNumberedCodeViewer()
|
|
37
|
+
self.rightEdit = LineNumberedCodeViewer()
|
|
38
|
+
for edit in (self.leftEdit, self.rightEdit):
|
|
39
|
+
edit.setReadOnly(True) # 設為唯讀 / Read-only
|
|
40
|
+
edit.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap) # 不自動換行 / Disable line wrap
|
|
41
|
+
mono = QFont("Consolas") # 使用等寬字型 / Monospaced font
|
|
42
|
+
mono.setStyleHint(QFont.StyleHint.Monospace)
|
|
43
|
+
edit.setFont(mono)
|
|
44
|
+
|
|
45
|
+
# === 版面配置 / Layout ===
|
|
46
|
+
leftBox = QVBoxLayout()
|
|
47
|
+
leftBox.addWidget(self.leftLabel)
|
|
48
|
+
leftBox.addWidget(self.leftEdit)
|
|
49
|
+
|
|
50
|
+
rightBox = QVBoxLayout()
|
|
51
|
+
rightBox.addWidget(self.rightLabel)
|
|
52
|
+
rightBox.addWidget(self.rightEdit)
|
|
53
|
+
|
|
54
|
+
main = QHBoxLayout(self)
|
|
55
|
+
leftContainer = QWidget()
|
|
56
|
+
leftContainer.setLayout(leftBox)
|
|
57
|
+
rightContainer = QWidget()
|
|
58
|
+
rightContainer.setLayout(rightBox)
|
|
59
|
+
main.addWidget(leftContainer)
|
|
60
|
+
main.addWidget(rightContainer)
|
|
61
|
+
|
|
62
|
+
# 同步左右捲軸 / Sync scrollbars
|
|
63
|
+
self._sync_scrollbars()
|
|
64
|
+
|
|
65
|
+
# 預設深色模式 / Default to dark theme
|
|
66
|
+
self.set_dark_theme()
|
|
67
|
+
|
|
68
|
+
def _sync_scrollbars(self):
|
|
69
|
+
"""
|
|
70
|
+
Synchronize scrollbars between left and right editors.
|
|
71
|
+
同步左右編輯器的捲軸。
|
|
72
|
+
"""
|
|
73
|
+
self.leftEdit.verticalScrollBar().valueChanged.connect(
|
|
74
|
+
self.rightEdit.verticalScrollBar().setValue
|
|
75
|
+
)
|
|
76
|
+
self.rightEdit.verticalScrollBar().valueChanged.connect(
|
|
77
|
+
self.leftEdit.verticalScrollBar().setValue
|
|
78
|
+
)
|
|
79
|
+
self.leftEdit.horizontalScrollBar().valueChanged.connect(
|
|
80
|
+
self.rightEdit.horizontalScrollBar().setValue
|
|
81
|
+
)
|
|
82
|
+
self.rightEdit.horizontalScrollBar().valueChanged.connect(
|
|
83
|
+
self.leftEdit.horizontalScrollBar().setValue
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def set_diff_text(self, diff_text: str):
|
|
87
|
+
"""
|
|
88
|
+
Parse unified diff text and display it in side-by-side editors.
|
|
89
|
+
解析 unified diff 文字並顯示在左右編輯器。
|
|
90
|
+
"""
|
|
91
|
+
left_lines, right_lines, left_marks, right_marks, left_name, right_name = \
|
|
92
|
+
self._parse_unified_diff(diff_text)
|
|
93
|
+
|
|
94
|
+
self.leftLabel.setText(f"Left: {left_name or '(old)'}")
|
|
95
|
+
self.rightLabel.setText(f"Right: {right_name or '(new)'}")
|
|
96
|
+
|
|
97
|
+
self._set_text_with_highlights(self.leftEdit, left_lines, left_marks)
|
|
98
|
+
self._set_text_with_highlights(self.rightEdit, right_lines, right_marks)
|
|
99
|
+
|
|
100
|
+
# 游標移到開頭 / Move cursor to start
|
|
101
|
+
self.leftEdit.moveCursor(QTextCursor.MoveOperation.Start)
|
|
102
|
+
self.rightEdit.moveCursor(QTextCursor.MoveOperation.Start)
|
|
103
|
+
|
|
104
|
+
def _set_text_with_highlights(self, edit: QPlainTextEdit, lines, marks):
|
|
105
|
+
"""
|
|
106
|
+
Set text and apply syntax highlighting based on diff marks.
|
|
107
|
+
設定文字並依 diff 標記加上背景色。
|
|
108
|
+
"""
|
|
109
|
+
edit.setPlainText("\n".join(lines))
|
|
110
|
+
|
|
111
|
+
diff_extras = []
|
|
112
|
+
for i, mark in enumerate(marks):
|
|
113
|
+
fmt = QTextCharFormat()
|
|
114
|
+
# Always set foreground so it won't fall back
|
|
115
|
+
# 永遠設定前景色,避免 fallback
|
|
116
|
+
fmt.setForeground(QColor("#d4d4d4") if self.is_dark else QColor("black"))
|
|
117
|
+
|
|
118
|
+
if mark == "DEL":
|
|
119
|
+
fmt.setBackground(self.color_del)
|
|
120
|
+
elif mark == "ADD":
|
|
121
|
+
fmt.setBackground(self.color_add)
|
|
122
|
+
elif mark == "HUNK":
|
|
123
|
+
fmt.setBackground(self.color_hunk)
|
|
124
|
+
elif mark == "HDR":
|
|
125
|
+
fmt.setBackground(self.color_header)
|
|
126
|
+
else:
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
sel = self._line_selection(edit, i, fmt)
|
|
130
|
+
diff_extras.append(sel)
|
|
131
|
+
|
|
132
|
+
# 保留 diff selections,方便主題切換時重用
|
|
133
|
+
setattr(edit, "_diff_extras", diff_extras)
|
|
134
|
+
|
|
135
|
+
# 嘗試合併其他高亮(例如 LineNumberedCodeViewer 的當前行高亮)
|
|
136
|
+
if hasattr(edit, "_current_line_extras"):
|
|
137
|
+
merged = diff_extras + edit._current_line_extras
|
|
138
|
+
else:
|
|
139
|
+
merged = diff_extras
|
|
140
|
+
|
|
141
|
+
edit.setExtraSelections(merged)
|
|
142
|
+
|
|
143
|
+
def _line_selection(self, edit: QPlainTextEdit, line_index: int, fmt: QTextCharFormat):
|
|
144
|
+
"""
|
|
145
|
+
Create a selection for a specific line with given format.
|
|
146
|
+
建立某一行的選取區並套用格式。
|
|
147
|
+
"""
|
|
148
|
+
sel = QTextEdit.ExtraSelection()
|
|
149
|
+
sel.format = fmt
|
|
150
|
+
cursor = edit.textCursor()
|
|
151
|
+
cursor.movePosition(QTextCursor.MoveOperation.Start)
|
|
152
|
+
for _ in range(line_index):
|
|
153
|
+
cursor.movePosition(QTextCursor.MoveOperation.Down)
|
|
154
|
+
cursor.select(QTextCursor.SelectionType.LineUnderCursor)
|
|
155
|
+
sel.cursor = cursor
|
|
156
|
+
return sel
|
|
157
|
+
|
|
158
|
+
def _parse_unified_diff(self, diff_text: str):
|
|
159
|
+
"""
|
|
160
|
+
Parse unified diff into left/right lines and marks.
|
|
161
|
+
將 unified diff 解析成左右行與標記。
|
|
162
|
+
"""
|
|
163
|
+
left_lines, right_lines, left_marks, right_marks = [], [], [], []
|
|
164
|
+
left_name, right_name = None, None
|
|
165
|
+
|
|
166
|
+
def add_left(line, mark=None):
|
|
167
|
+
left_lines.append(line)
|
|
168
|
+
left_marks.append(mark or "CTX")
|
|
169
|
+
|
|
170
|
+
def add_right(line, mark=None):
|
|
171
|
+
right_lines.append(line)
|
|
172
|
+
right_marks.append(mark or "CTX")
|
|
173
|
+
|
|
174
|
+
def align():
|
|
175
|
+
# 對齊左右行數 / Align left and right line counts
|
|
176
|
+
if len(left_lines) < len(right_lines):
|
|
177
|
+
for _ in range(len(right_lines) - len(left_lines)):
|
|
178
|
+
add_left("")
|
|
179
|
+
elif len(right_lines) < len(left_lines):
|
|
180
|
+
for _ in range(len(left_lines) - len(right_lines)):
|
|
181
|
+
add_right("")
|
|
182
|
+
|
|
183
|
+
for raw in diff_text.splitlines():
|
|
184
|
+
if raw.startswith("diff "):
|
|
185
|
+
add_left(raw, "HDR"); add_right(raw, "HDR"); align()
|
|
186
|
+
elif raw.startswith("--- "):
|
|
187
|
+
left_name = raw[4:].strip()
|
|
188
|
+
add_left(raw, "HDR"); add_right("", "HDR"); align()
|
|
189
|
+
elif raw.startswith("+++ "):
|
|
190
|
+
right_name = raw[4:].strip()
|
|
191
|
+
add_left("", "HDR"); add_right(raw, "HDR"); align()
|
|
192
|
+
elif raw.startswith("@@"):
|
|
193
|
+
add_left(raw, "HUNK"); add_right(raw, "HUNK"); align()
|
|
194
|
+
elif raw.startswith("-"):
|
|
195
|
+
add_left(raw, "DEL"); add_right("", None); align()
|
|
196
|
+
elif raw.startswith("+"):
|
|
197
|
+
add_left("", None); add_right(raw, "ADD"); align()
|
|
198
|
+
else:
|
|
199
|
+
add_left(raw, None); add_right(raw, None); align()
|
|
200
|
+
|
|
201
|
+
return left_lines, right_lines, left_marks, right_marks, left_name, right_name
|
|
202
|
+
|
|
203
|
+
def _reapply_highlights_for_theme(self):
|
|
204
|
+
"""
|
|
205
|
+
Reapply highlights when theme changes.
|
|
206
|
+
主題切換時重新套用高亮。
|
|
207
|
+
"""
|
|
208
|
+
for edit in (self.leftEdit, self.rightEdit):
|
|
209
|
+
if hasattr(edit, "_diff_extras"):
|
|
210
|
+
updated = []
|
|
211
|
+
for sel in edit._diff_extras:
|
|
212
|
+
fmt: QTextCharFormat = QTextCharFormat(sel.format)
|
|
213
|
+
|
|
214
|
+
# 前景色依主題切換
|
|
215
|
+
fmt.setForeground(QColor("#d4d4d4") if self.is_dark else QColor("black"))
|
|
216
|
+
|
|
217
|
+
# 依照 mark 更新背景色
|
|
218
|
+
cursor = sel.cursor
|
|
219
|
+
cursor.select(QTextCursor.SelectionType.LineUnderCursor)
|
|
220
|
+
text = cursor.selectedText()
|
|
221
|
+
|
|
222
|
+
if text.startswith("-"):
|
|
223
|
+
fmt.setBackground(self.color_del)
|
|
224
|
+
elif text.startswith("+"):
|
|
225
|
+
fmt.setBackground(self.color_add)
|
|
226
|
+
elif text.startswith("@@"):
|
|
227
|
+
fmt.setBackground(self.color_hunk)
|
|
228
|
+
elif text.startswith("diff") or text.startswith("---") or text.startswith("+++"):
|
|
229
|
+
fmt.setBackground(self.color_header)
|
|
230
|
+
|
|
231
|
+
sel.format = fmt
|
|
232
|
+
updated.append(sel)
|
|
233
|
+
|
|
234
|
+
edit._diff_extras = updated
|
|
235
|
+
|
|
236
|
+
if hasattr(edit, "_current_line_extras"):
|
|
237
|
+
merged = updated + edit._current_line_extras
|
|
238
|
+
else:
|
|
239
|
+
merged = updated
|
|
240
|
+
edit.setExtraSelections(merged)
|
|
241
|
+
|
|
242
|
+
def set_dark_theme(self):
|
|
243
|
+
"""
|
|
244
|
+
Apply dark theme colors.
|
|
245
|
+
套用深色主題配色。
|
|
246
|
+
"""
|
|
247
|
+
self.is_dark = True
|
|
248
|
+
self.color_del = QColor(60, 20, 20)
|
|
249
|
+
self.color_add = QColor(20, 60, 20)
|
|
250
|
+
self.color_hunk = QColor(25, 25, 60)
|
|
251
|
+
self.color_header = QColor(50, 50, 50)
|
|
252
|
+
self.setStyleSheet("""QWidget { background-color: #1e1e1e; color: #d4d4d4; }""")
|
|
253
|
+
self._reapply_highlights_for_theme()
|
|
254
|
+
self.leftEdit.apply_theme_to_editor(dark=self.is_dark)
|
|
255
|
+
self.rightEdit.apply_theme_to_editor(dark=self.is_dark)
|
|
256
|
+
|
|
257
|
+
def set_light_theme(self):
|
|
258
|
+
"""
|
|
259
|
+
Apply light theme colors.
|
|
260
|
+
套用淺色主題配色。
|
|
261
|
+
"""
|
|
262
|
+
self.is_dark = False
|
|
263
|
+
self.color_del = QColor(255, 230, 230)
|
|
264
|
+
self.color_add = QColor(230, 255, 230)
|
|
265
|
+
self.color_hunk = QColor(230, 230, 255)
|
|
266
|
+
self.color_header = QColor(240, 240, 240)
|
|
267
|
+
self.setStyleSheet("""QWidget { background-color: white; color: black; }""")
|
|
268
|
+
self._reapply_highlights_for_theme()
|
|
269
|
+
self.leftEdit.apply_theme_to_editor(dark=self.is_dark)
|
|
270
|
+
self.rightEdit.apply_theme_to_editor(dark=self.is_dark)
|
|
271
|
+
|
|
File without changes
|
je_editor/pyside_ui/git_ui/{git_branch_tree_widget.py → git_client/git_branch_tree_widget.py}
RENAMED
|
@@ -10,8 +10,8 @@ from PySide6.QtWidgets import (
|
|
|
10
10
|
|
|
11
11
|
from je_editor.git_client.commit_graph import CommitGraph
|
|
12
12
|
from je_editor.git_client.git_cli import GitCLI
|
|
13
|
-
from je_editor.pyside_ui.git_ui.commit_table import CommitTable
|
|
14
|
-
from je_editor.pyside_ui.git_ui.graph_view import CommitGraphView
|
|
13
|
+
from je_editor.pyside_ui.git_ui.git_client.commit_table import CommitTable
|
|
14
|
+
from je_editor.pyside_ui.git_ui.git_client.graph_view import CommitGraphView
|
|
15
15
|
from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
|
|
16
16
|
|
|
17
17
|
logging.basicConfig(level=logging.INFO)
|
|
@@ -24,20 +24,20 @@ class GitTreeViewGUI(QWidget):
|
|
|
24
24
|
self.language_wrapper_get = language_wrapper.language_word_dict.get
|
|
25
25
|
self.setWindowTitle(self.language_wrapper_get("git_graph_title"))
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
git_treeview_main_layout = QVBoxLayout(self)
|
|
28
|
+
git_treeview_main_layout.setContentsMargins(0, 0, 0, 0)
|
|
29
|
+
git_treeview_main_layout.setSpacing(0)
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
git_treeview_toolbar = QToolBar()
|
|
32
|
+
open_repo_action = QAction(self.language_wrapper_get("git_graph_toolbar_open"), self)
|
|
33
|
+
open_repo_action.triggered.connect(self.open_repo)
|
|
34
|
+
git_treeview_toolbar.addAction(open_repo_action)
|
|
35
35
|
|
|
36
36
|
act_refresh = QAction(self.language_wrapper_get("git_graph_toolbar_refresh"), self)
|
|
37
37
|
act_refresh.triggered.connect(self.refresh_graph)
|
|
38
|
-
|
|
38
|
+
git_treeview_toolbar.addAction(act_refresh)
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
git_treeview_main_layout.addWidget(git_treeview_toolbar)
|
|
41
41
|
|
|
42
42
|
splitter = QSplitter(Qt.Orientation.Horizontal)
|
|
43
43
|
self.graph_view = CommitGraphView()
|
|
@@ -45,10 +45,10 @@ class GitTreeViewGUI(QWidget):
|
|
|
45
45
|
splitter.addWidget(self.graph_view)
|
|
46
46
|
splitter.addWidget(self.commit_table)
|
|
47
47
|
splitter.setSizes([600, 400])
|
|
48
|
-
|
|
48
|
+
git_treeview_main_layout.addWidget(splitter, 1) # 1 表示可伸縮
|
|
49
49
|
|
|
50
50
|
self.status = QStatusBar()
|
|
51
|
-
|
|
51
|
+
git_treeview_main_layout.addWidget(self.status)
|
|
52
52
|
|
|
53
53
|
self.repo_path = None
|
|
54
54
|
self.git = None
|
|
@@ -70,6 +70,7 @@ class GitTreeViewGUI(QWidget):
|
|
|
70
70
|
self.graph_view.focus_row(row)
|
|
71
71
|
|
|
72
72
|
def open_repo(self):
|
|
73
|
+
# Open repo and set up the graph.
|
|
73
74
|
path = QFileDialog.getExistingDirectory(self, self.language_wrapper_get("git_graph_menu_open_repo"))
|
|
74
75
|
if not path:
|
|
75
76
|
return
|
|
@@ -100,10 +101,12 @@ class GitTreeViewGUI(QWidget):
|
|
|
100
101
|
if refs_dir.exists():
|
|
101
102
|
self.watcher.addPath(str(refs_dir))
|
|
102
103
|
|
|
103
|
-
def _on_git_changed(self
|
|
104
|
+
def _on_git_changed(self):
|
|
105
|
+
# Refresh git tree timer
|
|
104
106
|
self.refresh_timer.start(500)
|
|
105
107
|
|
|
106
108
|
def refresh_graph(self):
|
|
109
|
+
# Refresh git tree
|
|
107
110
|
if not self.git:
|
|
108
111
|
return
|
|
109
112
|
try:
|