je-editor 0.0.222__py3-none-any.whl → 0.0.224__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/git_client/commit_graph.py +7 -7
- je_editor/git_client/git_action.py +0 -7
- je_editor/pyside_ui/browser/browser_view.py +16 -4
- je_editor/pyside_ui/browser/browser_widget.py +43 -29
- je_editor/pyside_ui/browser/main_browser_widget.py +85 -0
- je_editor/pyside_ui/code/auto_save/auto_save_manager.py +34 -2
- je_editor/pyside_ui/code/auto_save/auto_save_thread.py +19 -6
- je_editor/pyside_ui/code/code_format/pep8_format.py +53 -9
- je_editor/pyside_ui/code/code_process/code_exec.py +88 -52
- je_editor/pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py +116 -55
- je_editor/pyside_ui/code/running_process_manager.py +19 -1
- je_editor/pyside_ui/code/shell_process/shell_exec.py +71 -48
- je_editor/pyside_ui/code/syntax/python_syntax.py +45 -10
- je_editor/pyside_ui/code/syntax/syntax_setting.py +40 -12
- je_editor/pyside_ui/code/textedit_code_result/code_record.py +34 -12
- je_editor/pyside_ui/code/variable_inspector/inspector_gui.py +53 -6
- je_editor/pyside_ui/dialog/ai_dialog/set_ai_dialog.py +30 -3
- je_editor/pyside_ui/dialog/file_dialog/create_file_dialog.py +35 -2
- je_editor/pyside_ui/dialog/file_dialog/open_file_dialog.py +33 -5
- je_editor/pyside_ui/dialog/file_dialog/save_file_dialog.py +25 -3
- je_editor/pyside_ui/dialog/search_ui/search_error_box.py +26 -1
- je_editor/pyside_ui/dialog/search_ui/search_text_box.py +26 -1
- je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +11 -11
- je_editor/pyside_ui/git_ui/git_client/commit_table.py +46 -8
- je_editor/pyside_ui/git_ui/git_client/git_branch_tree_widget.py +49 -15
- je_editor/pyside_ui/git_ui/git_client/git_client_gui.py +81 -16
- je_editor/pyside_ui/git_ui/git_client/graph_view.py +64 -20
- je_editor/pyside_ui/main_ui/ai_widget/ai_config.py +20 -5
- je_editor/pyside_ui/main_ui/ai_widget/ask_thread.py +20 -1
- je_editor/pyside_ui/main_ui/ai_widget/chat_ui.py +56 -41
- je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +45 -6
- je_editor/pyside_ui/main_ui/console_widget/console_gui.py +44 -12
- je_editor/pyside_ui/main_ui/console_widget/qprocess_adapter.py +34 -13
- je_editor/pyside_ui/main_ui/dock/destroy_dock.py +33 -2
- je_editor/pyside_ui/main_ui/editor/editor_widget.py +104 -20
- je_editor/pyside_ui/main_ui/editor/editor_widget_dock.py +34 -7
- je_editor/pyside_ui/main_ui/editor/process_input.py +38 -11
- je_editor/pyside_ui/main_ui/ipython_widget/rich_jupyter.py +46 -11
- je_editor/pyside_ui/main_ui/main_editor.py +180 -42
- je_editor/pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +51 -28
- je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +83 -36
- je_editor/pyside_ui/main_ui/menu/file_menu/build_file_menu.py +70 -17
- je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +35 -4
- je_editor/pyside_ui/main_ui/menu/language_menu/build_language_server.py +41 -1
- je_editor/pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py +100 -42
- je_editor/pyside_ui/main_ui/menu/run_menu/build_run_menu.py +57 -7
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +50 -4
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +52 -6
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +44 -4
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py +23 -1
- je_editor/pyside_ui/main_ui/menu/set_menu_bar.py +37 -12
- je_editor/pyside_ui/main_ui/menu/style_menu/build_style_menu.py +44 -7
- je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +127 -44
- je_editor/pyside_ui/main_ui/menu/text_menu/build_text_menu.py +65 -1
- je_editor/pyside_ui/main_ui/save_settings/setting_utils.py +18 -1
- je_editor/pyside_ui/main_ui/save_settings/user_color_setting_file.py +33 -3
- je_editor/pyside_ui/main_ui/save_settings/user_setting_file.py +38 -11
- je_editor/pyside_ui/main_ui/system_tray/extend_system_tray.py +39 -2
- je_editor/start_editor.py +26 -1
- je_editor/utils/encodings/python_encodings.py +101 -98
- je_editor/utils/file/open/open_file.py +36 -19
- je_editor/utils/file/save/save_file.py +35 -14
- je_editor/utils/json/json_file.py +29 -14
- je_editor/utils/json_format/json_process.py +33 -2
- je_editor/utils/logging/loggin_instance.py +38 -8
- je_editor/utils/multi_language/multi_language_wrapper.py +29 -4
- je_editor/utils/redirect_manager/redirect_manager_class.py +49 -11
- je_editor/utils/venv_check/check_venv.py +45 -15
- {je_editor-0.0.222.dist-info → je_editor-0.0.224.dist-info}/METADATA +1 -1
- {je_editor-0.0.222.dist-info → je_editor-0.0.224.dist-info}/RECORD +74 -74
- je_editor/git_client/github.py +0 -81
- {je_editor-0.0.222.dist-info → je_editor-0.0.224.dist-info}/WHEEL +0 -0
- {je_editor-0.0.222.dist-info → je_editor-0.0.224.dist-info}/licenses/LICENSE +0 -0
- {je_editor-0.0.222.dist-info → je_editor-0.0.224.dist-info}/top_level.txt +0 -0
|
@@ -5,17 +5,21 @@ from typing import TYPE_CHECKING
|
|
|
5
5
|
|
|
6
6
|
from je_editor.utils.logging.loggin_instance import jeditor_logger
|
|
7
7
|
|
|
8
|
+
# 僅在型別檢查時匯入,避免循環引用
|
|
9
|
+
# Only imported for type checking, avoids circular imports
|
|
8
10
|
if TYPE_CHECKING:
|
|
9
11
|
from je_editor.pyside_ui.main_ui.editor.editor_widget import EditorWidget
|
|
10
12
|
from je_editor.pyside_ui.main_ui.editor.editor_widget_dock import FullEditorWidget
|
|
11
13
|
|
|
12
14
|
from typing import Union, List
|
|
13
15
|
|
|
14
|
-
import jedi
|
|
16
|
+
import jedi # Python 自動補全與靜態分析工具
|
|
15
17
|
from PySide6 import QtGui
|
|
16
18
|
from PySide6.QtCore import Qt, QRect
|
|
17
|
-
from PySide6.QtGui import
|
|
18
|
-
|
|
19
|
+
from PySide6.QtGui import (
|
|
20
|
+
QPainter, QTextCharFormat, QTextFormat, QKeyEvent, QAction,
|
|
21
|
+
QTextDocument, QTextCursor, QTextOption
|
|
22
|
+
)
|
|
19
23
|
from PySide6.QtWidgets import QPlainTextEdit, QWidget, QTextEdit, QCompleter
|
|
20
24
|
from jedi.api.classes import Completion
|
|
21
25
|
|
|
@@ -25,6 +29,7 @@ from je_editor.pyside_ui.main_ui.save_settings.user_color_setting_file import ac
|
|
|
25
29
|
|
|
26
30
|
|
|
27
31
|
def venv_check():
|
|
32
|
+
"""檢查當前工作目錄下是否有 venv 資料夾 / Check if venv exists in current working directory"""
|
|
28
33
|
jeditor_logger.info("code_edit_plaintext.py venv check")
|
|
29
34
|
venv_path = Path(str(Path.cwd()) + "/venv")
|
|
30
35
|
return venv_path
|
|
@@ -32,24 +37,36 @@ def venv_check():
|
|
|
32
37
|
|
|
33
38
|
class CodeEditor(QPlainTextEdit):
|
|
34
39
|
"""
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
自訂的程式碼編輯器,繼承 QPlainTextEdit
|
|
41
|
+
Custom code editor extending QPlainTextEdit
|
|
42
|
+
|
|
43
|
+
功能:
|
|
44
|
+
- 行號顯示 (Line number area)
|
|
45
|
+
- Tab 縮排距離設定
|
|
46
|
+
- Python 語法高亮 (Syntax highlighting)
|
|
47
|
+
- 搜尋功能 (Search box)
|
|
48
|
+
- 自動補全 (Autocomplete with Jedi)
|
|
37
49
|
"""
|
|
38
50
|
|
|
39
51
|
def __init__(self, main_window: Union[EditorWidget, FullEditorWidget]):
|
|
40
52
|
jeditor_logger.info(f"Init CodeEditor main_window: {main_window}")
|
|
41
53
|
super().__init__()
|
|
42
|
-
|
|
54
|
+
|
|
55
|
+
# Jedi 環境,用於 Python 自動補全
|
|
43
56
|
self.env = None
|
|
44
57
|
self.check_env()
|
|
45
|
-
|
|
58
|
+
|
|
59
|
+
# 主視窗 (父元件)
|
|
46
60
|
self.main_window = main_window
|
|
47
61
|
self.current_file = main_window.current_file
|
|
48
62
|
|
|
63
|
+
# 定義哪些按鍵不會觸發補全視窗
|
|
49
64
|
self.skip_popup_behavior_list = [
|
|
50
65
|
Qt.Key.Key_Enter, Qt.Key.Key_Return, Qt.Key.Key_Up, Qt.Key.Key_Down,
|
|
51
66
|
Qt.Key.Key_Tab, Qt.Key.Key_Backtab, Qt.Key.Key_Space, Qt.Key.Key_Backspace
|
|
52
67
|
]
|
|
68
|
+
|
|
69
|
+
# 定義哪些按鍵會觸發補全 (A-Z)
|
|
53
70
|
self.need_complete_list = [
|
|
54
71
|
Qt.Key.Key_A, Qt.Key.Key_B, Qt.Key.Key_C, Qt.Key.Key_D, Qt.Key.Key_E, Qt.Key.Key_F,
|
|
55
72
|
Qt.Key.Key_G, Qt.Key.Key_H, Qt.Key.Key_I, Qt.Key.Key_J, Qt.Key.Key_K, Qt.Key.Key_L,
|
|
@@ -57,37 +74,50 @@ class CodeEditor(QPlainTextEdit):
|
|
|
57
74
|
Qt.Key.Key_S, Qt.Key.Key_T, Qt.Key.Key_U, Qt.Key.Key_V, Qt.Key.Key_W, Qt.Key.Key_X,
|
|
58
75
|
Qt.Key.Key_Y, Qt.Key.Key_Z
|
|
59
76
|
]
|
|
77
|
+
|
|
78
|
+
# 搜尋框 (延遲建立)
|
|
60
79
|
self.search_box = None
|
|
80
|
+
|
|
81
|
+
# 行號區域 (LineNumber 是另一個自訂類別)
|
|
61
82
|
self.line_number: LineNumber = LineNumber(self)
|
|
62
83
|
self.blockCountChanged.connect(self.update_line_number_area_width)
|
|
63
84
|
self.updateRequest.connect(self.update_line_number_area)
|
|
64
85
|
self.update_line_number_area_width(0)
|
|
86
|
+
|
|
87
|
+
# 當文字改變時,重新高亮當前行
|
|
65
88
|
self.textChanged.connect(self.highlight_current_line)
|
|
89
|
+
|
|
90
|
+
# 設定 Tab 寬度 (以字元寬度計算)
|
|
66
91
|
self.setTabStopDistance(
|
|
67
92
|
QtGui.QFontMetricsF(self.font()).horizontalAdvance(" ")
|
|
68
93
|
)
|
|
94
|
+
|
|
95
|
+
# Python 語法高亮
|
|
69
96
|
self.highlighter = PythonHighlighter(self.document(), main_window=self)
|
|
70
97
|
self.highlight_current_line()
|
|
98
|
+
|
|
99
|
+
# 關閉自動換行,改為單行顯示
|
|
71
100
|
self.setLineWrapMode(self.LineWrapMode.NoWrap)
|
|
72
101
|
self.setWordWrapMode(QTextOption.WrapMode.WrapAnywhere)
|
|
73
|
-
|
|
102
|
+
|
|
103
|
+
# 搜尋功能 (Ctrl+F)
|
|
74
104
|
self.search_action = QAction("Search")
|
|
75
105
|
self.search_action.setShortcut("Ctrl+f")
|
|
76
|
-
self.search_action.triggered.connect(
|
|
77
|
-
self.start_search_dialog
|
|
78
|
-
)
|
|
79
|
-
# Add actions
|
|
106
|
+
self.search_action.triggered.connect(self.start_search_dialog)
|
|
80
107
|
self.addAction(self.search_action)
|
|
81
|
-
|
|
108
|
+
|
|
109
|
+
# 自動補全初始化
|
|
82
110
|
self.completer: Union[None, QCompleter] = None
|
|
83
111
|
self.set_complete([])
|
|
84
112
|
|
|
85
113
|
def reset_highlighter(self):
|
|
114
|
+
"""重設語法高亮 / Reset syntax highlighter"""
|
|
86
115
|
jeditor_logger.info("CodeEditor reset_highlighter")
|
|
87
116
|
self.highlighter = PythonHighlighter(self.document(), main_window=self)
|
|
88
117
|
self.highlight_current_line()
|
|
89
118
|
|
|
90
119
|
def check_env(self):
|
|
120
|
+
"""檢查虛擬環境並建立 Jedi 環境 / Check venv and create Jedi environment"""
|
|
91
121
|
jeditor_logger.info("CodeEditor check_env")
|
|
92
122
|
path = venv_check()
|
|
93
123
|
if path.exists():
|
|
@@ -95,9 +125,8 @@ class CodeEditor(QPlainTextEdit):
|
|
|
95
125
|
|
|
96
126
|
def set_complete(self, list_to_complete: list) -> None:
|
|
97
127
|
"""
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
:return: None
|
|
128
|
+
設定自動補全清單
|
|
129
|
+
Set completion list
|
|
101
130
|
"""
|
|
102
131
|
jeditor_logger.info(f"CodeEditor set_complete list_to_complete: {list_to_complete}")
|
|
103
132
|
completer = QCompleter(list_to_complete)
|
|
@@ -110,9 +139,8 @@ class CodeEditor(QPlainTextEdit):
|
|
|
110
139
|
|
|
111
140
|
def insert_completion(self, completion) -> None:
|
|
112
141
|
"""
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
:return: None
|
|
142
|
+
插入補全文字
|
|
143
|
+
Insert completion text into editor
|
|
116
144
|
"""
|
|
117
145
|
jeditor_logger.info(f"CodeEditor insert_completion completion: {completion}")
|
|
118
146
|
if self.completer.widget() != self:
|
|
@@ -126,13 +154,14 @@ class CodeEditor(QPlainTextEdit):
|
|
|
126
154
|
|
|
127
155
|
@property
|
|
128
156
|
def text_under_cursor(self):
|
|
157
|
+
"""取得游標下的文字 / Get text under cursor"""
|
|
129
158
|
jeditor_logger.info("CodeEditor text_under_cursor")
|
|
130
|
-
# Find text under cursor
|
|
131
159
|
text_cursor = self.textCursor()
|
|
132
160
|
text_cursor.select(QTextCursor.SelectionType.WordUnderCursor)
|
|
133
161
|
return text_cursor.selectedText()
|
|
134
162
|
|
|
135
163
|
def focusInEvent(self, e) -> None:
|
|
164
|
+
"""當編輯器獲得焦點時,確保 completer 綁定正確"""
|
|
136
165
|
jeditor_logger.info(f"CodeEditor focusInEvent event: {e}")
|
|
137
166
|
if self.completer:
|
|
138
167
|
self.completer.setWidget(self)
|
|
@@ -140,8 +169,8 @@ class CodeEditor(QPlainTextEdit):
|
|
|
140
169
|
|
|
141
170
|
def complete(self) -> None:
|
|
142
171
|
"""
|
|
143
|
-
|
|
144
|
-
|
|
172
|
+
使用 Jedi 進行自動補全
|
|
173
|
+
Keyword autocomplete with Jedi
|
|
145
174
|
"""
|
|
146
175
|
jeditor_logger.info("CodeEditor complete")
|
|
147
176
|
prefix = self.text_under_cursor
|
|
@@ -149,15 +178,18 @@ class CodeEditor(QPlainTextEdit):
|
|
|
149
178
|
script = jedi.Script(code=self.toPlainText(), environment=self.env)
|
|
150
179
|
else:
|
|
151
180
|
script = jedi.Script(code=self.toPlainText())
|
|
181
|
+
|
|
182
|
+
# 取得補全清單
|
|
152
183
|
jedi_complete_list: List[Completion] = script.complete(
|
|
153
184
|
self.textCursor().blockNumber() + 1,
|
|
154
185
|
len(self.textCursor().document().findBlockByLineNumber(self.textCursor().blockNumber()).text())
|
|
155
186
|
)
|
|
187
|
+
|
|
156
188
|
if len(jedi_complete_list) > 0:
|
|
157
|
-
new_complete_list =
|
|
158
|
-
for complete_text in jedi_complete_list:
|
|
159
|
-
new_complete_list.append(complete_text.name)
|
|
189
|
+
new_complete_list = [complete_text.name for complete_text in jedi_complete_list]
|
|
160
190
|
self.set_complete(new_complete_list)
|
|
191
|
+
|
|
192
|
+
# 顯示補全視窗
|
|
161
193
|
self.completer.setCompletionPrefix(prefix)
|
|
162
194
|
popup = self.completer.popup()
|
|
163
195
|
cursor_rect = self.cursorRect()
|
|
@@ -166,16 +198,10 @@ class CodeEditor(QPlainTextEdit):
|
|
|
166
198
|
self.completer.complete(cursor_rect)
|
|
167
199
|
|
|
168
200
|
def start_search_dialog(self) -> None:
|
|
169
|
-
"""
|
|
170
|
-
Show search box ui and bind.
|
|
171
|
-
:return: None
|
|
172
|
-
"""
|
|
201
|
+
"""顯示搜尋框 / Show search box"""
|
|
173
202
|
jeditor_logger.info("CodeEditor start_search_dialog")
|
|
174
|
-
# Search box connect to function
|
|
175
203
|
self.search_box = SearchBox()
|
|
176
|
-
self.search_box.search_back_button.clicked.connect(
|
|
177
|
-
self.find_back_text
|
|
178
|
-
)
|
|
204
|
+
self.search_box.search_back_button.clicked.connect(self.find_back_text)
|
|
179
205
|
self.search_box.search_next_button.clicked.connect(
|
|
180
206
|
self.find_next_text
|
|
181
207
|
)
|
|
@@ -183,8 +209,8 @@ class CodeEditor(QPlainTextEdit):
|
|
|
183
209
|
|
|
184
210
|
def find_next_text(self) -> None:
|
|
185
211
|
"""
|
|
186
|
-
|
|
187
|
-
|
|
212
|
+
找到下一個符合的文字
|
|
213
|
+
Find next match text
|
|
188
214
|
"""
|
|
189
215
|
jeditor_logger.info("CodeEditor find_next_text")
|
|
190
216
|
if self.search_box.isVisible():
|
|
@@ -193,8 +219,8 @@ class CodeEditor(QPlainTextEdit):
|
|
|
193
219
|
|
|
194
220
|
def find_back_text(self) -> None:
|
|
195
221
|
"""
|
|
196
|
-
|
|
197
|
-
|
|
222
|
+
找到上一個符合的文字
|
|
223
|
+
Find previous match text
|
|
198
224
|
"""
|
|
199
225
|
jeditor_logger.info("CodeEditor find_back_text")
|
|
200
226
|
if self.search_box.isVisible():
|
|
@@ -202,13 +228,22 @@ class CodeEditor(QPlainTextEdit):
|
|
|
202
228
|
self.find(text, QTextDocument.FindFlag.FindBackward)
|
|
203
229
|
|
|
204
230
|
def line_number_paint(self, event) -> None:
|
|
231
|
+
"""
|
|
232
|
+
繪製行號區域
|
|
233
|
+
Paint line number area
|
|
234
|
+
"""
|
|
205
235
|
jeditor_logger.info(f"CodeEditor line_number_paint event: {event}")
|
|
206
236
|
painter = QPainter(self.line_number)
|
|
237
|
+
# 填滿背景色
|
|
207
238
|
painter.fillRect(event.rect(), actually_color_dict.get("line_number_background_color"))
|
|
239
|
+
|
|
240
|
+
# 從第一個可見區塊開始
|
|
208
241
|
block = self.firstVisibleBlock()
|
|
209
242
|
block_number = block.blockNumber()
|
|
210
243
|
top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top()
|
|
211
244
|
bottom = top + self.blockBoundingRect(block).height()
|
|
245
|
+
|
|
246
|
+
# 逐行繪製行號
|
|
212
247
|
while block.isValid() and top <= event.rect().bottom():
|
|
213
248
|
if block.isVisible() and bottom >= event.rect().top():
|
|
214
249
|
number = str(block_number + 1)
|
|
@@ -227,20 +262,27 @@ class CodeEditor(QPlainTextEdit):
|
|
|
227
262
|
block_number += 1
|
|
228
263
|
|
|
229
264
|
def line_number_width(self) -> int:
|
|
265
|
+
"""
|
|
266
|
+
計算行號區域寬度
|
|
267
|
+
Calculate line number area width
|
|
268
|
+
"""
|
|
230
269
|
jeditor_logger.info("CodeEditor line_number_width")
|
|
231
|
-
digits = len(str(self.blockCount()))
|
|
270
|
+
digits = len(str(self.blockCount())) # 根據總行數決定位數
|
|
232
271
|
space = 12 * digits
|
|
233
272
|
return space
|
|
234
273
|
|
|
235
274
|
def update_line_number_area_width(self, value) -> None:
|
|
275
|
+
"""
|
|
276
|
+
更新行號區域寬度
|
|
277
|
+
Update line number area width
|
|
278
|
+
"""
|
|
236
279
|
jeditor_logger.info(f"CodeEditor update_line_number_area_width value: {value}")
|
|
237
280
|
self.setViewportMargins(self.line_number_width(), 0, 0, 0)
|
|
238
281
|
|
|
239
282
|
def resizeEvent(self, event) -> None:
|
|
240
283
|
"""
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
:return: None
|
|
284
|
+
視窗大小改變時,調整行號區域
|
|
285
|
+
Resize line number paint area
|
|
244
286
|
"""
|
|
245
287
|
jeditor_logger.info(f"CodeEditor resizeEvent event:{event}")
|
|
246
288
|
QPlainTextEdit.resizeEvent(self, event)
|
|
@@ -251,10 +293,8 @@ class CodeEditor(QPlainTextEdit):
|
|
|
251
293
|
|
|
252
294
|
def update_line_number_area(self, rect, dy) -> None:
|
|
253
295
|
"""
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
:param dy: update or not.
|
|
257
|
-
:return: None
|
|
296
|
+
更新行號顯示
|
|
297
|
+
Update line number area
|
|
258
298
|
"""
|
|
259
299
|
jeditor_logger.info(f"CodeEditor update_line_number_area rect: {rect}, dy: {dy}")
|
|
260
300
|
if dy:
|
|
@@ -271,8 +311,8 @@ class CodeEditor(QPlainTextEdit):
|
|
|
271
311
|
|
|
272
312
|
def highlight_current_line(self) -> None:
|
|
273
313
|
"""
|
|
274
|
-
|
|
275
|
-
|
|
314
|
+
高亮目前所在行
|
|
315
|
+
Highlight current line
|
|
276
316
|
"""
|
|
277
317
|
jeditor_logger.info("CodeEditor highlight_current_line")
|
|
278
318
|
selections = []
|
|
@@ -290,13 +330,16 @@ class CodeEditor(QPlainTextEdit):
|
|
|
290
330
|
|
|
291
331
|
def keyPressEvent(self, event: QKeyEvent) -> None:
|
|
292
332
|
"""
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
:
|
|
333
|
+
鍵盤事件處理
|
|
334
|
+
Handle key press events
|
|
335
|
+
- Ctrl+B: 使用 Jedi 跳轉定義
|
|
336
|
+
- Shift+Enter: 忽略軟換行
|
|
337
|
+
- 其他情況觸發自動補全
|
|
296
338
|
"""
|
|
297
|
-
# Catch soft wrap shift + return (line nuber not working on soft warp)
|
|
298
339
|
key = event.key()
|
|
299
340
|
jeditor_logger.info(f"CodeEditor keyPressEvent event: {event} key: {key}")
|
|
341
|
+
|
|
342
|
+
# Ctrl + B → 跳轉到定義
|
|
300
343
|
if event.modifiers() and Qt.Modifier.CTRL:
|
|
301
344
|
if key == Qt.Key.Key_B:
|
|
302
345
|
if self.env is not None:
|
|
@@ -313,31 +356,45 @@ class CodeEditor(QPlainTextEdit):
|
|
|
313
356
|
else:
|
|
314
357
|
self.textCursor().setPosition(goto_list[0].line - 1)
|
|
315
358
|
return
|
|
359
|
+
|
|
360
|
+
# 如果補全視窗開啟,且按下不該觸發的按鍵 → 關閉補全
|
|
316
361
|
if self.completer.popup().isVisible() and key in self.skip_popup_behavior_list:
|
|
317
362
|
self.completer.popup().close()
|
|
318
363
|
event.ignore()
|
|
319
364
|
return
|
|
365
|
+
|
|
366
|
+
# Shift+Enter → 忽略 (避免軟換行影響行號)
|
|
320
367
|
if event.modifiers() and Qt.Modifier.SHIFT:
|
|
321
368
|
if key == Qt.Key.Key_Enter or key == Qt.Key.Key_Return:
|
|
322
369
|
event.ignore()
|
|
323
370
|
return
|
|
371
|
+
|
|
372
|
+
# 呼叫父類別處理其他按鍵
|
|
324
373
|
super().keyPressEvent(event)
|
|
374
|
+
|
|
375
|
+
# 更新目前行高亮
|
|
325
376
|
self.highlight_current_line()
|
|
377
|
+
|
|
378
|
+
# 如果輸入英文字母,觸發自動補全
|
|
326
379
|
if key in self.need_complete_list and self.completer is not None:
|
|
327
380
|
if self.completer.popup().isVisible():
|
|
328
381
|
self.completer.popup().close()
|
|
329
382
|
self.complete()
|
|
330
383
|
|
|
331
384
|
def mousePressEvent(self, event) -> None:
|
|
385
|
+
"""
|
|
386
|
+
滑鼠點擊事件
|
|
387
|
+
Mouse press event
|
|
388
|
+
- 點擊後高亮所在行
|
|
389
|
+
"""
|
|
332
390
|
jeditor_logger.info(f"CodeEditor mousePressEvent event: {event}")
|
|
333
|
-
# Highlight mouse click line
|
|
334
391
|
super().mousePressEvent(event)
|
|
335
392
|
self.highlight_current_line()
|
|
336
393
|
|
|
337
|
-
|
|
338
394
|
class LineNumber(QWidget):
|
|
339
395
|
"""
|
|
340
|
-
|
|
396
|
+
行號區域元件
|
|
397
|
+
Widget used to paint line numbers
|
|
341
398
|
"""
|
|
342
399
|
|
|
343
400
|
def __init__(self, editor):
|
|
@@ -346,5 +403,9 @@ class LineNumber(QWidget):
|
|
|
346
403
|
self.editor = editor
|
|
347
404
|
|
|
348
405
|
def paintEvent(self, event) -> None:
|
|
406
|
+
"""
|
|
407
|
+
呼叫編輯器的 line_number_paint 來繪製行號
|
|
408
|
+
Delegate painting to CodeEditor.line_number_paint
|
|
409
|
+
"""
|
|
349
410
|
jeditor_logger.info(f"LineNumber paintEvent event: {event}")
|
|
350
|
-
self.editor.line_number_paint(event)
|
|
411
|
+
self.editor.line_number_paint(event)
|
|
@@ -5,6 +5,8 @@ from typing import TYPE_CHECKING
|
|
|
5
5
|
from je_editor.utils.logging.loggin_instance import jeditor_logger
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
|
+
# 僅在型別檢查時匯入,避免循環依賴
|
|
9
|
+
# Only imported during type checking to avoid circular imports
|
|
8
10
|
from je_editor.pyside_ui.code.code_process.code_exec import ExecManager
|
|
9
11
|
from je_editor.pyside_ui.code.shell_process.shell_exec import ShellManager
|
|
10
12
|
|
|
@@ -12,19 +14,35 @@ from typing import List, Union
|
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
class RunInstanceManager(object):
|
|
17
|
+
"""
|
|
18
|
+
管理程式執行與 Shell 執行的實例
|
|
19
|
+
Manager for ExecManager and ShellManager instances
|
|
20
|
+
"""
|
|
15
21
|
|
|
16
22
|
def __init__(self):
|
|
23
|
+
# 初始化,建立一個空的實例清單
|
|
24
|
+
# Initialize with an empty instance list
|
|
17
25
|
jeditor_logger.info("Init RunInstanceManager")
|
|
18
26
|
self.instance_list: List[Union[ExecManager, ShellManager]] = list()
|
|
19
27
|
|
|
20
28
|
def close_all_instance(self):
|
|
29
|
+
"""
|
|
30
|
+
關閉所有執行中的實例,並清理 main_window 的執行狀態
|
|
31
|
+
Close all running instances and reset main_window execution states
|
|
32
|
+
"""
|
|
21
33
|
jeditor_logger.info("RunInstanceManager close_all_instance")
|
|
22
34
|
for manager in self.instance_list:
|
|
35
|
+
# 若子程序仍存在,則終止
|
|
36
|
+
# Terminate process if still running
|
|
23
37
|
if manager.process is not None:
|
|
24
38
|
manager.process.terminate()
|
|
39
|
+
# 清理 main_window 的執行狀態
|
|
40
|
+
# Reset execution states in main_window
|
|
25
41
|
manager.main_window.exec_program = None
|
|
26
42
|
manager.main_window.exec_shell = None
|
|
27
43
|
manager.main_window.exec_python_debugger = None
|
|
28
44
|
|
|
29
45
|
|
|
30
|
-
|
|
46
|
+
# 建立全域唯一的 RunInstanceManager 實例
|
|
47
|
+
# Create a global singleton instance of RunInstanceManager
|
|
48
|
+
run_instance_manager = RunInstanceManager()
|