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
|
@@ -23,7 +23,6 @@ from je_editor.utils.venv_check.check_venv import check_and_choose_venv
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class ShellManager(object):
|
|
26
|
-
|
|
27
26
|
def __init__(
|
|
28
27
|
self,
|
|
29
28
|
main_window: Union[EditorWidget, None] = None,
|
|
@@ -31,36 +30,41 @@ class ShellManager(object):
|
|
|
31
30
|
program_buffer: int = 1024,
|
|
32
31
|
after_done_function: Union[None, Callable] = None
|
|
33
32
|
):
|
|
33
|
+
# 初始化 ShellManager,記錄基本設定
|
|
34
|
+
# Initialize ShellManager, log basic settings
|
|
34
35
|
jeditor_logger.info(f"Init ShellManager "
|
|
35
36
|
f"main_window: {main_window} "
|
|
36
37
|
f"shell_encoding: {shell_encoding} "
|
|
37
38
|
f"program_buffer: {program_buffer} "
|
|
38
39
|
f"after_done_function: {after_done_function}")
|
|
39
40
|
"""
|
|
40
|
-
:param main_window: Pyside main window
|
|
41
|
-
:param shell_encoding: shell command
|
|
42
|
-
:param program_buffer: buffer size
|
|
41
|
+
:param main_window: Pyside 主視窗 / Pyside main window
|
|
42
|
+
:param shell_encoding: shell 輸出編碼 / shell command output encoding
|
|
43
|
+
:param program_buffer: 緩衝區大小 / buffer size
|
|
43
44
|
"""
|
|
44
|
-
self.read_program_error_output_from_thread = None
|
|
45
|
-
self.read_program_output_from_thread = None
|
|
46
|
-
self.main_window: EditorWidget = main_window
|
|
47
|
-
self.compiler_path = None
|
|
48
|
-
self.code_result: Union[QTextEdit, None] = None
|
|
49
|
-
self.timer: Union[QTimer, None] = None
|
|
50
|
-
self.still_run_shell: bool = True
|
|
51
|
-
self.process = None
|
|
52
|
-
self.run_output_queue: queue.Queue = queue.Queue()
|
|
53
|
-
self.run_error_queue: queue.Queue = queue.Queue()
|
|
54
|
-
self.program_encoding: str = shell_encoding
|
|
55
|
-
self.program_buffer: int = program_buffer
|
|
56
|
-
self.after_done_function = after_done_function
|
|
57
|
-
self.renew_path()
|
|
58
|
-
run_instance_manager.instance_list.append(self)
|
|
45
|
+
self.read_program_error_output_from_thread = None # 錯誤輸出讀取執行緒 / thread for reading stderr
|
|
46
|
+
self.read_program_output_from_thread = None # 標準輸出讀取執行緒 / thread for reading stdout
|
|
47
|
+
self.main_window: EditorWidget = main_window # 主視窗 / main window
|
|
48
|
+
self.compiler_path = None # Python 編譯器路徑 / Python compiler path
|
|
49
|
+
self.code_result: Union[QTextEdit, None] = None # 顯示輸出結果的文字框 / QTextEdit for displaying results
|
|
50
|
+
self.timer: Union[QTimer, None] = None # 定時器 / QTimer for periodic updates
|
|
51
|
+
self.still_run_shell: bool = True # 是否仍在執行 / flag for running state
|
|
52
|
+
self.process = None # 子程序物件 / subprocess object
|
|
53
|
+
self.run_output_queue: queue.Queue = queue.Queue() # 標準輸出佇列 / stdout queue
|
|
54
|
+
self.run_error_queue: queue.Queue = queue.Queue() # 錯誤輸出佇列 / stderr queue
|
|
55
|
+
self.program_encoding: str = shell_encoding # 編碼設定 / encoding setting
|
|
56
|
+
self.program_buffer: int = program_buffer # 緩衝區大小 / buffer size
|
|
57
|
+
self.after_done_function = after_done_function # 完成後的回呼函數 / callback after done
|
|
58
|
+
self.renew_path() # 更新 Python 執行路徑 / renew Python path
|
|
59
|
+
run_instance_manager.instance_list.append(self) # 註冊到執行管理器 / register instance
|
|
59
60
|
|
|
60
61
|
def renew_path(self) -> None:
|
|
62
|
+
# 更新 Python 編譯器路徑
|
|
63
|
+
# Renew Python compiler path
|
|
61
64
|
jeditor_logger.info("ShellManager renew_path")
|
|
62
65
|
if self.main_window.python_compiler is None:
|
|
63
|
-
#
|
|
66
|
+
# 如果主視窗沒有指定 Python,則使用 venv
|
|
67
|
+
# If no compiler specified, use venv
|
|
64
68
|
if sys.platform in ["win32", "cygwin", "msys"]:
|
|
65
69
|
venv_path = Path(os.getcwd() + "/venv/Scripts")
|
|
66
70
|
else:
|
|
@@ -70,6 +74,8 @@ class ShellManager(object):
|
|
|
70
74
|
self.compiler_path = self.main_window.python_compiler
|
|
71
75
|
|
|
72
76
|
def later_init(self) -> None:
|
|
77
|
+
# 延遲初始化,綁定輸出視窗
|
|
78
|
+
# Late initialization, bind output QTextEdit
|
|
73
79
|
jeditor_logger.info("ShellManager later_init")
|
|
74
80
|
if self.main_window is not None:
|
|
75
81
|
self.code_result: QTextEdit = self.main_window.code_result
|
|
@@ -78,22 +84,25 @@ class ShellManager(object):
|
|
|
78
84
|
|
|
79
85
|
def exec_shell(self, shell_command: Union[str, list]) -> None:
|
|
80
86
|
"""
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
執行 shell 指令
|
|
88
|
+
Execute shell command
|
|
89
|
+
:param shell_command: 要執行的指令 / command to run
|
|
90
|
+
:return: 若錯誤則回傳錯誤訊息 / return error message if failed
|
|
83
91
|
"""
|
|
84
92
|
jeditor_logger.info(f"ShellManager exec_shell, shell_command: {shell_command}")
|
|
85
93
|
try:
|
|
86
|
-
self.exit_program()
|
|
87
|
-
self.code_result.setPlainText("")
|
|
94
|
+
self.exit_program() # 結束舊的程序 / terminate previous process
|
|
95
|
+
self.code_result.setPlainText("") # 清空輸出視窗 / clear output window
|
|
88
96
|
if sys.platform in ["win32", "cygwin", "msys"]:
|
|
89
97
|
args = shell_command
|
|
90
98
|
else:
|
|
91
|
-
args = shlex.split(shell_command)
|
|
99
|
+
args = shlex.split(shell_command) # 非 Windows 系統需分割指令 / split command for Unix-like
|
|
92
100
|
text_cursor = self.code_result.textCursor()
|
|
93
101
|
text_format = QTextCharFormat()
|
|
94
102
|
text_format.setForeground(actually_color_dict.get("normal_output_color"))
|
|
95
|
-
text_cursor.insertText(str(args), text_format)
|
|
103
|
+
text_cursor.insertText(str(args), text_format) # 顯示執行的指令 / show executed command
|
|
96
104
|
text_cursor.insertBlock()
|
|
105
|
+
# 建立子程序 / create subprocess
|
|
97
106
|
self.process = subprocess.Popen(
|
|
98
107
|
args=args,
|
|
99
108
|
stdout=subprocess.PIPE,
|
|
@@ -102,24 +111,26 @@ class ShellManager(object):
|
|
|
102
111
|
shell=True,
|
|
103
112
|
)
|
|
104
113
|
self.still_run_shell = True
|
|
105
|
-
#
|
|
114
|
+
# 建立讀取標準輸出的執行緒 / thread for stdout
|
|
106
115
|
self.read_program_output_from_thread = Thread(
|
|
107
116
|
target=self.read_program_output_from_process,
|
|
108
117
|
daemon=True
|
|
109
118
|
)
|
|
110
119
|
self.read_program_output_from_thread.start()
|
|
111
|
-
#
|
|
120
|
+
# 建立讀取錯誤輸出的執行緒 / thread for stderr
|
|
112
121
|
self.read_program_error_output_from_thread = Thread(
|
|
113
122
|
target=self.read_program_error_output_from_process,
|
|
114
123
|
daemon=True
|
|
115
124
|
)
|
|
116
125
|
self.read_program_error_output_from_thread.start()
|
|
117
|
-
# start
|
|
126
|
+
# 啟動定時器,每 10ms 更新一次輸出 / start QTimer for updating output
|
|
118
127
|
self.timer = QTimer(self.main_window)
|
|
119
128
|
self.timer.setInterval(10)
|
|
120
129
|
self.timer.timeout.connect(self.pull_text)
|
|
121
130
|
self.timer.start()
|
|
122
131
|
except Exception as error:
|
|
132
|
+
# 若發生錯誤,顯示錯誤訊息並結束程序
|
|
133
|
+
# If error occurs, show error message and terminate process
|
|
123
134
|
text_cursor = self.code_result.textCursor()
|
|
124
135
|
text_format = QTextCharFormat()
|
|
125
136
|
text_format.setForeground(actually_color_dict.get("error_output_color"))
|
|
@@ -130,6 +141,8 @@ class ShellManager(object):
|
|
|
130
141
|
|
|
131
142
|
# tkinter_ui update method
|
|
132
143
|
def pull_text(self) -> None:
|
|
144
|
+
# 從佇列中拉取輸出與錯誤訊息,並更新到 UI
|
|
145
|
+
# Pull stdout/stderr messages from queue and update UI
|
|
133
146
|
jeditor_logger.info("ShellManager pull_text")
|
|
134
147
|
try:
|
|
135
148
|
if not self.run_output_queue.empty():
|
|
@@ -152,61 +165,71 @@ class ShellManager(object):
|
|
|
152
165
|
text_cursor.insertBlock()
|
|
153
166
|
except queue.Empty:
|
|
154
167
|
pass
|
|
168
|
+
# 檢查子程序是否結束 / check if process finished
|
|
155
169
|
if self.process.returncode == 0:
|
|
156
170
|
self.process_run_over()
|
|
157
171
|
elif self.process.returncode is not None:
|
|
158
172
|
self.process_run_over()
|
|
159
173
|
if self.still_run_shell:
|
|
160
|
-
#
|
|
174
|
+
# 持續檢查程序狀態 / keep polling process
|
|
161
175
|
self.process.poll()
|
|
162
|
-
|
|
163
176
|
def process_run_over(self):
|
|
177
|
+
# 當子程序結束時呼叫,停止計時器並清理資源
|
|
178
|
+
# Called when subprocess finishes, stop timer and clean up resources
|
|
164
179
|
jeditor_logger.info("ShellManager process_run_over")
|
|
165
|
-
self.timer.stop()
|
|
166
|
-
self.exit_program()
|
|
167
|
-
self.main_window.exec_shell = None
|
|
180
|
+
self.timer.stop() # 停止定時器 / stop QTimer
|
|
181
|
+
self.exit_program() # 結束程序並清理 / terminate process and cleanup
|
|
182
|
+
self.main_window.exec_shell = None # 重置 main_window 的 exec_shell / reset exec_shell reference
|
|
168
183
|
if self.after_done_function is not None:
|
|
169
|
-
self.after_done_function()
|
|
184
|
+
self.after_done_function() # 執行結束後的回呼函數 / run callback if provided
|
|
170
185
|
|
|
171
|
-
# exit program
|
|
186
|
+
# exit program: 將執行旗標設為 False,清理執行緒、佇列與子程序
|
|
187
|
+
# exit program: set running flag to False, clean threads, queues, and subprocess
|
|
172
188
|
def exit_program(self) -> None:
|
|
173
189
|
jeditor_logger.info("ShellManager exit_program")
|
|
174
|
-
self.still_run_shell = False
|
|
190
|
+
self.still_run_shell = False # 停止讀取迴圈 / stop reading loop
|
|
175
191
|
if self.read_program_output_from_thread is not None:
|
|
176
|
-
self.read_program_output_from_thread = None
|
|
192
|
+
self.read_program_output_from_thread = None # 清理 stdout 執行緒 / clear stdout thread
|
|
177
193
|
if self.read_program_error_output_from_thread is not None:
|
|
178
|
-
self.read_program_error_output_from_thread = None
|
|
179
|
-
self.print_and_clear_queue()
|
|
194
|
+
self.read_program_error_output_from_thread = None # 清理 stderr 執行緒 / clear stderr thread
|
|
195
|
+
self.print_and_clear_queue() # 清空輸出佇列 / clear output queues
|
|
180
196
|
if self.process is not None:
|
|
181
|
-
self.process.terminate()
|
|
197
|
+
self.process.terminate() # 終止子程序 / terminate subprocess
|
|
182
198
|
text_cursor = self.code_result.textCursor()
|
|
183
199
|
text_format = QTextCharFormat()
|
|
184
200
|
text_format.setForeground(actually_color_dict.get("normal_output_color"))
|
|
201
|
+
# 顯示退出代碼 / show exit code
|
|
185
202
|
text_cursor.insertText(f"Shell command exit with code {self.process.returncode}", text_format)
|
|
186
203
|
text_cursor.insertBlock()
|
|
187
|
-
self.process = None
|
|
204
|
+
self.process = None # 清空 process 物件 / reset process object
|
|
188
205
|
|
|
189
206
|
def print_and_clear_queue(self) -> None:
|
|
207
|
+
# 清空 stdout 與 stderr 佇列
|
|
208
|
+
# Reset stdout and stderr queues
|
|
190
209
|
jeditor_logger.info("ShellManager print_and_clear_queue")
|
|
191
210
|
self.run_output_queue = queue.Queue()
|
|
192
211
|
self.run_error_queue = queue.Queue()
|
|
193
212
|
|
|
194
213
|
def read_program_output_from_process(self) -> None:
|
|
214
|
+
# 從子程序讀取標準輸出並放入佇列
|
|
215
|
+
# Continuously read stdout from subprocess and put into queue
|
|
195
216
|
jeditor_logger.info("ShellManager read_program_output_from_process")
|
|
196
217
|
while self.still_run_shell:
|
|
197
218
|
program_output_data = self.process.stdout.readline(
|
|
198
219
|
self.program_buffer) \
|
|
199
|
-
.decode(self.program_encoding, "replace")
|
|
220
|
+
.decode(self.program_encoding, "replace") # 解碼輸出 / decode output
|
|
200
221
|
if self.process:
|
|
201
|
-
self.process.stdout.flush()
|
|
202
|
-
self.run_output_queue.put_nowait(program_output_data)
|
|
222
|
+
self.process.stdout.flush() # 清空緩衝區 / flush buffer
|
|
223
|
+
self.run_output_queue.put_nowait(program_output_data) # 放入輸出佇列 / enqueue stdout
|
|
203
224
|
|
|
204
225
|
def read_program_error_output_from_process(self) -> None:
|
|
226
|
+
# 從子程序讀取錯誤輸出並放入佇列
|
|
227
|
+
# Continuously read stderr from subprocess and put into queue
|
|
205
228
|
jeditor_logger.info("ShellManager read_program_error_output_from_process")
|
|
206
229
|
while self.still_run_shell:
|
|
207
230
|
program_error_output_data = self.process.stderr.readline(
|
|
208
231
|
self.program_buffer) \
|
|
209
|
-
.decode(self.program_encoding, "replace")
|
|
232
|
+
.decode(self.program_encoding, "replace") # 解碼錯誤輸出 / decode stderr
|
|
210
233
|
if self.process:
|
|
211
|
-
self.process.stderr.flush()
|
|
212
|
-
self.run_error_queue.put_nowait(program_error_output_data)
|
|
234
|
+
self.process.stderr.flush() # 清空緩衝區 / flush buffer
|
|
235
|
+
self.run_error_queue.put_nowait(program_error_output_data) # 放入錯誤佇列 / enqueue stderr
|
|
@@ -4,46 +4,74 @@ from pathlib import Path
|
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
6
|
if TYPE_CHECKING:
|
|
7
|
+
# 僅在型別檢查時匯入,避免循環依賴
|
|
8
|
+
# Only imported during type checking to avoid circular imports
|
|
7
9
|
from je_editor.pyside_ui.code.plaintext_code_edit.code_edit_plaintext import CodeEditor
|
|
8
10
|
|
|
9
11
|
from PySide6.QtCore import QRegularExpression
|
|
10
12
|
from PySide6.QtGui import QSyntaxHighlighter
|
|
11
13
|
from PySide6.QtGui import QTextCharFormat
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
# 匯入語法設定,包括關鍵字、規則、擴展設定
|
|
16
|
+
# Import syntax settings: keywords, rules, and extended settings
|
|
17
|
+
from je_editor.pyside_ui.code.syntax.syntax_setting import (
|
|
18
|
+
syntax_word_setting_dict,
|
|
19
|
+
syntax_rule_setting_dict,
|
|
14
20
|
syntax_extend_setting_dict
|
|
21
|
+
)
|
|
15
22
|
from je_editor.utils.logging.loggin_instance import jeditor_logger
|
|
16
23
|
|
|
17
24
|
|
|
18
25
|
class PythonHighlighter(QSyntaxHighlighter):
|
|
26
|
+
"""
|
|
27
|
+
Python 語法高亮類別,繼承自 QSyntaxHighlighter
|
|
28
|
+
Python syntax highlighter class, inherits from QSyntaxHighlighter
|
|
29
|
+
"""
|
|
30
|
+
|
|
19
31
|
def __init__(self, parent=None, main_window: CodeEditor = None):
|
|
20
32
|
jeditor_logger.info(f"Init PythonHighlighter parent: {parent}")
|
|
21
33
|
super().__init__(parent)
|
|
22
34
|
|
|
23
|
-
self.highlight_rules = []
|
|
35
|
+
self.highlight_rules = [] # 儲存所有高亮規則 / store all highlight rules
|
|
36
|
+
|
|
37
|
+
# 判斷目前檔案副檔名,若無則預設為 .py
|
|
38
|
+
# Determine current file suffix, default to .py
|
|
24
39
|
if main_window.current_file is not None:
|
|
25
40
|
current_file_suffix = Path(main_window.current_file).suffix
|
|
26
41
|
else:
|
|
27
42
|
current_file_suffix = ".py"
|
|
28
|
-
|
|
43
|
+
|
|
44
|
+
# -------------------------
|
|
45
|
+
# 基本語法規則 (通用)
|
|
46
|
+
# Basic highlight rules (common)
|
|
47
|
+
# -------------------------
|
|
29
48
|
for rule_variable_dict in syntax_rule_setting_dict.values():
|
|
30
|
-
color = rule_variable_dict.get("color")
|
|
49
|
+
color = rule_variable_dict.get("color") # 規則顏色 / rule color
|
|
31
50
|
text_char_format = QTextCharFormat()
|
|
32
51
|
text_char_format.setForeground(color)
|
|
33
|
-
for rule in rule_variable_dict.get("rules"):
|
|
52
|
+
for rule in rule_variable_dict.get("rules"): # 正則規則 / regex rules
|
|
34
53
|
pattern = QRegularExpression(rule)
|
|
35
54
|
self.highlight_rules.append((pattern, text_char_format))
|
|
55
|
+
|
|
56
|
+
# -------------------------
|
|
57
|
+
# Python 語法高亮
|
|
58
|
+
# Python-specific highlight
|
|
59
|
+
# -------------------------
|
|
36
60
|
if current_file_suffix == ".py":
|
|
37
|
-
# Python Highlight
|
|
38
61
|
for rule_variable_dict in syntax_word_setting_dict.values():
|
|
39
62
|
color = rule_variable_dict.get("color")
|
|
40
63
|
text_char_format = QTextCharFormat()
|
|
41
64
|
text_char_format.setForeground(color)
|
|
42
|
-
for word in rule_variable_dict.get("words"):
|
|
65
|
+
for word in rule_variable_dict.get("words"): # 關鍵字清單 / keyword list
|
|
66
|
+
# 使用 \b 確保完整單字匹配 / use \b for whole word match
|
|
43
67
|
pattern = QRegularExpression(rf"\b{word}\b")
|
|
44
68
|
self.highlight_rules.append((pattern, text_char_format))
|
|
69
|
+
|
|
70
|
+
# -------------------------
|
|
71
|
+
# 其他語言的擴展高亮
|
|
72
|
+
# Extended highlight for other languages
|
|
73
|
+
# -------------------------
|
|
45
74
|
else:
|
|
46
|
-
# Another Highlight
|
|
47
75
|
if syntax_extend_setting_dict.get(current_file_suffix):
|
|
48
76
|
for rule_variable_dict in syntax_extend_setting_dict.get(current_file_suffix).values():
|
|
49
77
|
color = rule_variable_dict.get("color")
|
|
@@ -53,12 +81,19 @@ class PythonHighlighter(QSyntaxHighlighter):
|
|
|
53
81
|
pattern = QRegularExpression(rf"\b{word}\b")
|
|
54
82
|
self.highlight_rules.append((pattern, text_char_format))
|
|
55
83
|
else:
|
|
84
|
+
# 若無對應規則則略過
|
|
85
|
+
# Skip if no rules found
|
|
56
86
|
pass
|
|
57
87
|
|
|
58
88
|
def highlightBlock(self, text) -> None:
|
|
89
|
+
"""
|
|
90
|
+
對每一行文字進行語法高亮
|
|
91
|
+
Apply syntax highlighting to each block of text
|
|
92
|
+
"""
|
|
59
93
|
jeditor_logger.info(f"PythonHighlighter highlightBlock text: {text}")
|
|
60
94
|
for pattern, pattern_format in self.highlight_rules:
|
|
61
|
-
match_iterator = pattern.globalMatch(text)
|
|
95
|
+
match_iterator = pattern.globalMatch(text) # 全域比對 / global regex match
|
|
62
96
|
while match_iterator.hasNext():
|
|
63
97
|
match = match_iterator.next()
|
|
64
|
-
|
|
98
|
+
# 設定比對到的文字格式 / apply format to matched text
|
|
99
|
+
self.setFormat(match.capturedStart(), match.capturedLength(), pattern_format)
|
|
@@ -1,27 +1,45 @@
|
|
|
1
1
|
from PySide6.QtGui import QColor
|
|
2
2
|
|
|
3
|
+
# -----------------------------
|
|
4
|
+
# 基本語法規則設定 (數字、註解、字串)
|
|
5
|
+
# Basic syntax rule settings (numbers, comments, strings)
|
|
6
|
+
# -----------------------------
|
|
3
7
|
syntax_rule_setting_dict: dict = {
|
|
4
8
|
"number_rule": {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
# 數字規則 (整數、十六進位、小數/科學記號)
|
|
10
|
+
# Number rules (integer, hex, float/scientific notation)
|
|
11
|
+
"rules": (
|
|
12
|
+
r"\b[+-]?[0-9]+[lL]?\b", # 整數 integer
|
|
13
|
+
r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", # 十六進位 hex
|
|
14
|
+
r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b" # 浮點數/科學記號 float/scientific
|
|
15
|
+
),
|
|
16
|
+
"color": QColor(0, 128, 255) # 藍色 Blue
|
|
9
17
|
},
|
|
10
18
|
"comment_rule": {
|
|
19
|
+
# 註解規則 (以 # 開頭直到換行)
|
|
20
|
+
# Comment rule (starts with # until newline)
|
|
11
21
|
"rules": (r"#[^\n]*",),
|
|
12
|
-
"color": QColor(0, 230, 0)
|
|
22
|
+
"color": QColor(0, 230, 0) # 綠色 Green
|
|
13
23
|
},
|
|
14
24
|
"string_rule": {
|
|
25
|
+
# 字串規則 (單引號與雙引號)
|
|
26
|
+
# String rules (single and double quotes)
|
|
15
27
|
"rules": (
|
|
16
|
-
r"'[^'\\]*(\\.[^'\\]*)*'", #
|
|
17
|
-
r'"[^"\\]*(\\.[^"\\]*)*"', #
|
|
28
|
+
r"'[^'\\]*(\\.[^'\\]*)*'", # 單引號字串 single-quoted string
|
|
29
|
+
r'"[^"\\]*(\\.[^"\\]*)*"', # 雙引號字串 double-quoted string
|
|
18
30
|
),
|
|
19
|
-
"color": QColor(0, 153, 0)
|
|
31
|
+
"color": QColor(0, 153, 0) # 深綠色 Dark green
|
|
20
32
|
}
|
|
21
33
|
}
|
|
22
34
|
|
|
35
|
+
# -----------------------------
|
|
36
|
+
# 關鍵字與內建函式設定
|
|
37
|
+
# Keywords and built-in functions
|
|
38
|
+
# -----------------------------
|
|
23
39
|
syntax_word_setting_dict: dict = {
|
|
24
40
|
"keywords": {
|
|
41
|
+
# Python 保留字 (語法關鍵字)
|
|
42
|
+
# Python reserved keywords
|
|
25
43
|
"words": (
|
|
26
44
|
"False", "None", "True", "and", "as", "assert", "async",
|
|
27
45
|
"await", "break", "class", "continue", "def", "del",
|
|
@@ -29,9 +47,11 @@ syntax_word_setting_dict: dict = {
|
|
|
29
47
|
"global", "if", "import", "in", "is", "lambda", "nonlocal",
|
|
30
48
|
"not", "or", "pass", "raise", "return", "try", "while", "with", "yield"
|
|
31
49
|
),
|
|
32
|
-
"color": QColor(255, 212, 102)
|
|
50
|
+
"color": QColor(255, 212, 102) # 黃色 Yellow
|
|
33
51
|
},
|
|
34
52
|
"builtins_keyword": {
|
|
53
|
+
# Python 內建函式與型別
|
|
54
|
+
# Python built-in functions and types
|
|
35
55
|
"words": (
|
|
36
56
|
"abs", "aiter", "all", "any", "anext", "ascii",
|
|
37
57
|
"bin", "bool", "breakpoint", "bytearray", "bytes",
|
|
@@ -55,13 +75,21 @@ syntax_word_setting_dict: dict = {
|
|
|
55
75
|
"zip",
|
|
56
76
|
"__import__"
|
|
57
77
|
),
|
|
58
|
-
"color": QColor(0, 255, 255)
|
|
78
|
+
"color": QColor(0, 255, 255) # 青色 Cyan
|
|
59
79
|
},
|
|
60
80
|
"self": {
|
|
81
|
+
# Python 類別中的 self 關鍵字
|
|
82
|
+
# "self" keyword in Python classes
|
|
61
83
|
"words": ("self",),
|
|
62
|
-
"color": QColor(204, 0, 204)
|
|
84
|
+
"color": QColor(204, 0, 204) # 紫色 Purple
|
|
63
85
|
}
|
|
64
86
|
}
|
|
65
87
|
|
|
88
|
+
# -----------------------------
|
|
89
|
+
# 擴展語法設定 (其他語言可在此加入)
|
|
90
|
+
# Extended syntax settings (for other languages)
|
|
91
|
+
# -----------------------------
|
|
66
92
|
syntax_extend_setting_dict: dict = {
|
|
67
|
-
|
|
93
|
+
# 目前為空,可依副檔名新增規則
|
|
94
|
+
# Currently empty, can add rules for other file suffixes
|
|
95
|
+
}
|
|
@@ -7,47 +7,69 @@ from je_editor.utils.logging.loggin_instance import jeditor_logger
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class CodeRecord(QTextEdit):
|
|
10
|
-
#
|
|
10
|
+
# 繼承自 QTextEdit,作為程式碼輸出紀錄區
|
|
11
|
+
# Extend QTextEdit, used as a code output record area
|
|
11
12
|
def __init__(self):
|
|
12
13
|
jeditor_logger.info("Init CodeRecord")
|
|
13
14
|
super().__init__()
|
|
14
|
-
self.setLineWrapMode(self.LineWrapMode.NoWrap)
|
|
15
|
-
self.setReadOnly(True)
|
|
16
|
-
|
|
15
|
+
self.setLineWrapMode(self.LineWrapMode.NoWrap) # 禁止自動換行 / disable line wrapping
|
|
16
|
+
self.setReadOnly(True) # 設為唯讀 / set as read-only
|
|
17
|
+
|
|
18
|
+
# 建立搜尋錯誤的快捷動作
|
|
19
|
+
# Create search error action with shortcut
|
|
17
20
|
self.search_result_action = QAction("Search Error")
|
|
18
21
|
self.search_result_action.setShortcut("Ctrl+e")
|
|
19
22
|
self.search_result_action.triggered.connect(
|
|
20
|
-
self.start_search_result_dialog
|
|
23
|
+
self.start_search_result_dialog # 綁定觸發事件 / bind trigger event
|
|
21
24
|
)
|
|
22
25
|
self.addAction(self.search_result_action)
|
|
23
26
|
|
|
24
27
|
def append(self, text: str) -> None:
|
|
28
|
+
"""
|
|
29
|
+
新增文字到輸出區,若超過最大行數則清空
|
|
30
|
+
Append text to output area, clear if exceeding max lines
|
|
31
|
+
"""
|
|
25
32
|
jeditor_logger.info("CodeRecord append")
|
|
26
33
|
max_line: int = user_setting_dict.get("max_line_of_output", 200000)
|
|
27
34
|
if self.document().lineCount() >= max_line > 0:
|
|
35
|
+
# 若行數超過設定,清空內容
|
|
36
|
+
# Clear content if line count exceeds limit
|
|
28
37
|
self.setPlainText("")
|
|
29
38
|
super().append(text)
|
|
30
39
|
|
|
31
40
|
def start_search_result_dialog(self) -> None:
|
|
41
|
+
"""
|
|
42
|
+
開啟搜尋對話框,並綁定搜尋按鈕事件
|
|
43
|
+
Open search dialog and bind search button events
|
|
44
|
+
"""
|
|
32
45
|
jeditor_logger.info("CodeRecord start_search_result_dialog")
|
|
33
|
-
#
|
|
34
|
-
self.search_result_box = SearchResultBox()
|
|
46
|
+
self.search_result_box = SearchResultBox() # 建立搜尋框 / create search box
|
|
35
47
|
self.search_result_box.search_back_button.clicked.connect(
|
|
36
|
-
self.find_back_text
|
|
48
|
+
self.find_back_text # 綁定「上一個」搜尋 / bind "find previous"
|
|
37
49
|
)
|
|
38
50
|
self.search_result_box.search_next_button.clicked.connect(
|
|
39
|
-
self.find_next_text
|
|
51
|
+
self.find_next_text # 綁定「下一個」搜尋 / bind "find next"
|
|
40
52
|
)
|
|
41
|
-
self.search_result_box.show()
|
|
53
|
+
self.search_result_box.show() # 顯示搜尋框 / show search box
|
|
42
54
|
|
|
43
55
|
def find_next_text(self) -> None:
|
|
56
|
+
"""
|
|
57
|
+
搜尋下一個符合的文字
|
|
58
|
+
Find next matching text
|
|
59
|
+
"""
|
|
44
60
|
jeditor_logger.info("CodeRecord find_next_text")
|
|
45
61
|
if self.search_result_box.isVisible():
|
|
46
62
|
text = self.search_result_box.search_input.text()
|
|
47
|
-
self.find(text)
|
|
63
|
+
self.find(text) # 使用 QTextEdit 內建 find 方法 / use QTextEdit built-in find
|
|
48
64
|
|
|
49
65
|
def find_back_text(self) -> None:
|
|
66
|
+
"""
|
|
67
|
+
搜尋上一個符合的文字
|
|
68
|
+
Find previous matching text
|
|
69
|
+
"""
|
|
50
70
|
jeditor_logger.info("CodeRecord find_back_text")
|
|
51
71
|
if self.search_result_box.isVisible():
|
|
52
72
|
text = self.search_result_box.search_input.text()
|
|
53
|
-
|
|
73
|
+
# 使用 FindBackward 旗標往回搜尋
|
|
74
|
+
# Use FindBackward flag to search backwards
|
|
75
|
+
self.find(text, QTextDocument.FindFlag.FindBackward)
|