je-editor 0.0.202__py3-none-any.whl → 0.0.228__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.
Files changed (96) hide show
  1. je_editor/__init__.py +2 -2
  2. je_editor/code_scan/__init__.py +0 -0
  3. je_editor/code_scan/ruff_thread.py +58 -0
  4. je_editor/code_scan/watchdog_implement.py +56 -0
  5. je_editor/code_scan/watchdog_thread.py +78 -0
  6. je_editor/git_client/__init__.py +0 -0
  7. je_editor/git_client/commit_graph.py +77 -0
  8. je_editor/git_client/git_action.py +175 -0
  9. je_editor/git_client/git_cli.py +66 -0
  10. je_editor/pyside_ui/browser/browser_download_window.py +40 -4
  11. je_editor/pyside_ui/browser/browser_serach_lineedit.py +24 -0
  12. je_editor/pyside_ui/browser/browser_view.py +55 -6
  13. je_editor/pyside_ui/browser/browser_widget.py +62 -19
  14. je_editor/pyside_ui/browser/main_browser_widget.py +85 -0
  15. je_editor/pyside_ui/code/auto_save/auto_save_manager.py +33 -1
  16. je_editor/pyside_ui/code/auto_save/auto_save_thread.py +18 -5
  17. je_editor/pyside_ui/code/code_format/pep8_format.py +52 -7
  18. je_editor/pyside_ui/code/code_process/code_exec.py +118 -64
  19. je_editor/pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py +115 -53
  20. je_editor/pyside_ui/code/running_process_manager.py +18 -0
  21. je_editor/pyside_ui/code/shell_process/shell_exec.py +99 -60
  22. je_editor/pyside_ui/code/syntax/python_syntax.py +44 -9
  23. je_editor/pyside_ui/code/syntax/syntax_setting.py +39 -11
  24. je_editor/pyside_ui/code/textedit_code_result/code_record.py +33 -11
  25. je_editor/pyside_ui/code/variable_inspector/__init__.py +0 -0
  26. je_editor/pyside_ui/code/variable_inspector/inspector_gui.py +172 -0
  27. je_editor/pyside_ui/dialog/ai_dialog/__init__.py +0 -0
  28. je_editor/pyside_ui/dialog/ai_dialog/set_ai_dialog.py +71 -0
  29. je_editor/pyside_ui/dialog/file_dialog/create_file_dialog.py +38 -4
  30. je_editor/pyside_ui/dialog/file_dialog/open_file_dialog.py +32 -4
  31. je_editor/pyside_ui/dialog/file_dialog/save_file_dialog.py +25 -2
  32. je_editor/pyside_ui/dialog/search_ui/search_error_box.py +27 -2
  33. je_editor/pyside_ui/dialog/search_ui/search_text_box.py +28 -3
  34. je_editor/pyside_ui/git_ui/__init__.py +0 -0
  35. je_editor/pyside_ui/git_ui/code_diff_compare/__init__.py +0 -0
  36. je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +90 -0
  37. je_editor/pyside_ui/git_ui/code_diff_compare/line_number_code_viewer.py +141 -0
  38. je_editor/pyside_ui/git_ui/code_diff_compare/multi_file_diff_viewer.py +88 -0
  39. je_editor/pyside_ui/git_ui/code_diff_compare/side_by_side_diff_widget.py +284 -0
  40. je_editor/pyside_ui/git_ui/git_client/__init__.py +0 -0
  41. je_editor/pyside_ui/git_ui/git_client/commit_table.py +65 -0
  42. je_editor/pyside_ui/git_ui/git_client/git_branch_tree_widget.py +156 -0
  43. je_editor/pyside_ui/git_ui/git_client/git_client_gui.py +799 -0
  44. je_editor/pyside_ui/git_ui/git_client/graph_view.py +218 -0
  45. je_editor/pyside_ui/main_ui/ai_widget/__init__.py +0 -0
  46. je_editor/pyside_ui/main_ui/ai_widget/ai_config.py +34 -0
  47. je_editor/pyside_ui/main_ui/ai_widget/ask_thread.py +36 -0
  48. je_editor/pyside_ui/main_ui/ai_widget/chat_ui.py +147 -0
  49. je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +84 -0
  50. je_editor/pyside_ui/main_ui/console_widget/__init__.py +0 -0
  51. je_editor/pyside_ui/main_ui/console_widget/console_gui.py +162 -0
  52. je_editor/pyside_ui/main_ui/console_widget/qprocess_adapter.py +84 -0
  53. je_editor/pyside_ui/main_ui/dock/destroy_dock.py +32 -1
  54. je_editor/pyside_ui/main_ui/editor/editor_widget.py +113 -23
  55. je_editor/pyside_ui/main_ui/editor/editor_widget_dock.py +33 -6
  56. je_editor/pyside_ui/main_ui/editor/process_input.py +42 -10
  57. je_editor/pyside_ui/main_ui/ipython_widget/rich_jupyter.py +45 -10
  58. je_editor/pyside_ui/main_ui/main_editor.py +189 -49
  59. je_editor/pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +50 -27
  60. je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +141 -30
  61. je_editor/pyside_ui/main_ui/menu/file_menu/build_file_menu.py +67 -17
  62. je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +33 -3
  63. je_editor/pyside_ui/main_ui/menu/language_menu/build_language_server.py +39 -0
  64. je_editor/pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py +102 -40
  65. je_editor/pyside_ui/main_ui/menu/run_menu/build_run_menu.py +53 -6
  66. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +47 -3
  67. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +45 -5
  68. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +41 -3
  69. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py +21 -0
  70. je_editor/pyside_ui/main_ui/menu/set_menu_bar.py +37 -11
  71. je_editor/pyside_ui/main_ui/menu/style_menu/build_style_menu.py +42 -6
  72. je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +202 -27
  73. je_editor/pyside_ui/main_ui/menu/text_menu/build_text_menu.py +66 -0
  74. je_editor/pyside_ui/main_ui/save_settings/setting_utils.py +18 -1
  75. je_editor/pyside_ui/main_ui/save_settings/user_color_setting_file.py +32 -2
  76. je_editor/pyside_ui/main_ui/save_settings/user_setting_file.py +37 -10
  77. je_editor/pyside_ui/main_ui/system_tray/extend_system_tray.py +39 -0
  78. je_editor/start_editor.py +25 -0
  79. je_editor/utils/encodings/python_encodings.py +100 -97
  80. je_editor/utils/exception/exception_tags.py +11 -11
  81. je_editor/utils/file/open/open_file.py +35 -19
  82. je_editor/utils/file/save/save_file.py +34 -13
  83. je_editor/utils/json/json_file.py +29 -14
  84. je_editor/utils/json_format/json_process.py +32 -1
  85. je_editor/utils/logging/loggin_instance.py +37 -7
  86. je_editor/utils/multi_language/english.py +102 -6
  87. je_editor/utils/multi_language/multi_language_wrapper.py +28 -3
  88. je_editor/utils/multi_language/traditional_chinese.py +101 -10
  89. je_editor/utils/redirect_manager/redirect_manager_class.py +49 -11
  90. je_editor/utils/venv_check/check_venv.py +37 -14
  91. {je_editor-0.0.202.dist-info → je_editor-0.0.228.dist-info}/METADATA +12 -6
  92. je_editor-0.0.228.dist-info/RECORD +140 -0
  93. {je_editor-0.0.202.dist-info → je_editor-0.0.228.dist-info}/WHEEL +1 -1
  94. je_editor-0.0.202.dist-info/RECORD +0 -108
  95. {je_editor-0.0.202.dist-info → je_editor-0.0.228.dist-info/licenses}/LICENSE +0 -0
  96. {je_editor-0.0.202.dist-info → je_editor-0.0.228.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,7 @@ from threading import Thread
10
10
  from typing import Union, Callable
11
11
 
12
12
  from PySide6.QtCore import QTimer
13
+ from PySide6.QtGui import QTextCharFormat
13
14
  from PySide6.QtWidgets import QTextEdit
14
15
 
15
16
  from je_editor.pyside_ui.code.running_process_manager import run_instance_manager
@@ -22,7 +23,6 @@ from je_editor.utils.venv_check.check_venv import check_and_choose_venv
22
23
 
23
24
 
24
25
  class ShellManager(object):
25
-
26
26
  def __init__(
27
27
  self,
28
28
  main_window: Union[EditorWidget, None] = None,
@@ -30,36 +30,41 @@ class ShellManager(object):
30
30
  program_buffer: int = 1024,
31
31
  after_done_function: Union[None, Callable] = None
32
32
  ):
33
+ # 初始化 ShellManager,記錄基本設定
34
+ # Initialize ShellManager, log basic settings
33
35
  jeditor_logger.info(f"Init ShellManager "
34
36
  f"main_window: {main_window} "
35
37
  f"shell_encoding: {shell_encoding} "
36
38
  f"program_buffer: {program_buffer} "
37
39
  f"after_done_function: {after_done_function}")
38
40
  """
39
- :param main_window: Pyside main window
40
- :param shell_encoding: shell command read output encoding
41
- :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
42
44
  """
43
- self.read_program_error_output_from_thread = None
44
- self.read_program_output_from_thread = None
45
- self.main_window: EditorWidget = main_window
46
- self.compiler_path = None
47
- self.code_result: [QTextEdit, None] = None
48
- self.timer: [QTimer, None] = None
49
- self.still_run_shell: bool = True
50
- self.process = None
51
- self.run_output_queue: queue = queue.Queue()
52
- self.run_error_queue: queue = queue.Queue()
53
- self.program_encoding: str = shell_encoding
54
- self.program_buffer: int = program_buffer
55
- self.after_done_function = after_done_function
56
- self.renew_path()
57
- 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
58
60
 
59
61
  def renew_path(self) -> None:
62
+ # 更新 Python 編譯器路徑
63
+ # Renew Python compiler path
60
64
  jeditor_logger.info("ShellManager renew_path")
61
65
  if self.main_window.python_compiler is None:
62
- # Renew compiler path
66
+ # 如果主視窗沒有指定 Python,則使用 venv
67
+ # If no compiler specified, use venv
63
68
  if sys.platform in ["win32", "cygwin", "msys"]:
64
69
  venv_path = Path(os.getcwd() + "/venv/Scripts")
65
70
  else:
@@ -69,27 +74,35 @@ class ShellManager(object):
69
74
  self.compiler_path = self.main_window.python_compiler
70
75
 
71
76
  def later_init(self) -> None:
77
+ # 延遲初始化,綁定輸出視窗
78
+ # Late initialization, bind output QTextEdit
72
79
  jeditor_logger.info("ShellManager later_init")
73
80
  if self.main_window is not None:
74
81
  self.code_result: QTextEdit = self.main_window.code_result
75
82
  else:
76
83
  raise JEditorException(je_editor_init_error)
77
84
 
78
- def exec_shell(self, shell_command: [str, list]) -> None:
85
+ def exec_shell(self, shell_command: Union[str, list]) -> None:
79
86
  """
80
- :param shell_command: shell command will run
81
- :return: if error return result and True else return result and False
87
+ 執行 shell 指令
88
+ Execute shell command
89
+ :param shell_command: 要執行的指令 / command to run
90
+ :return: 若錯誤則回傳錯誤訊息 / return error message if failed
82
91
  """
83
92
  jeditor_logger.info(f"ShellManager exec_shell, shell_command: {shell_command}")
84
93
  try:
85
- self.exit_program()
86
- self.code_result.setTextColor(actually_color_dict.get("normal_output_color"))
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)
92
- self.code_result.append(str(args))
99
+ args = shlex.split(shell_command) # 非 Windows 系統需分割指令 / split command for Unix-like
100
+ text_cursor = self.code_result.textCursor()
101
+ text_format = QTextCharFormat()
102
+ text_format.setForeground(actually_color_dict.get("normal_output_color"))
103
+ text_cursor.insertText(str(args), text_format) # 顯示執行的指令 / show executed command
104
+ text_cursor.insertBlock()
105
+ # 建立子程序 / create subprocess
93
106
  self.process = subprocess.Popen(
94
107
  args=args,
95
108
  stdout=subprocess.PIPE,
@@ -98,100 +111,126 @@ class ShellManager(object):
98
111
  shell=True,
99
112
  )
100
113
  self.still_run_shell = True
101
- # program output message queue thread
114
+ # 建立讀取標準輸出的執行緒 / thread for stdout
102
115
  self.read_program_output_from_thread = Thread(
103
116
  target=self.read_program_output_from_process,
104
117
  daemon=True
105
118
  )
106
119
  self.read_program_output_from_thread.start()
107
- # program error message queue thread
120
+ # 建立讀取錯誤輸出的執行緒 / thread for stderr
108
121
  self.read_program_error_output_from_thread = Thread(
109
122
  target=self.read_program_error_output_from_process,
110
123
  daemon=True
111
124
  )
112
125
  self.read_program_error_output_from_thread.start()
113
- # start timer
126
+ # 啟動定時器,每 10ms 更新一次輸出 / start QTimer for updating output
114
127
  self.timer = QTimer(self.main_window)
115
128
  self.timer.setInterval(10)
116
129
  self.timer.timeout.connect(self.pull_text)
117
130
  self.timer.start()
118
131
  except Exception as error:
119
- self.code_result.setTextColor(actually_color_dict.get("error_output_color"))
120
- self.code_result.append(str(error))
121
- self.code_result.setTextColor(actually_color_dict.get("normal_output_color"))
132
+ # 若發生錯誤,顯示錯誤訊息並結束程序
133
+ # If error occurs, show error message and terminate process
134
+ text_cursor = self.code_result.textCursor()
135
+ text_format = QTextCharFormat()
136
+ text_format.setForeground(actually_color_dict.get("error_output_color"))
137
+ text_cursor.insertText(str(error), text_format)
138
+ text_cursor.insertBlock()
122
139
  if self.process is not None:
123
140
  self.process.terminate()
124
141
 
125
142
  # tkinter_ui update method
126
143
  def pull_text(self) -> None:
144
+ # 從佇列中拉取輸出與錯誤訊息,並更新到 UI
145
+ # Pull stdout/stderr messages from queue and update UI
127
146
  jeditor_logger.info("ShellManager pull_text")
128
147
  try:
129
- self.code_result.setTextColor(actually_color_dict.get("normal_output_color"))
130
148
  if not self.run_output_queue.empty():
131
149
  output_message = self.run_output_queue.get_nowait()
132
150
  output_message = str(output_message).strip()
133
151
  if output_message:
134
- self.code_result.append(output_message)
135
- self.code_result.setTextColor(actually_color_dict.get("error_output_color"))
152
+ text_cursor = self.code_result.textCursor()
153
+ text_format = QTextCharFormat()
154
+ text_format.setForeground(actually_color_dict.get("normal_output_color"))
155
+ text_cursor.insertText(output_message, text_format)
156
+ text_cursor.insertBlock()
136
157
  if not self.run_error_queue.empty():
137
158
  error_message = self.run_error_queue.get_nowait()
138
159
  error_message = str(error_message).strip()
139
160
  if error_message:
140
- self.code_result.append(error_message)
141
- self.code_result.setTextColor(actually_color_dict.get("normal_output_color"))
161
+ text_cursor = self.code_result.textCursor()
162
+ text_format = QTextCharFormat()
163
+ text_format.setForeground(actually_color_dict.get("error_output_color"))
164
+ text_cursor.insertText(error_message, text_format)
165
+ text_cursor.insertBlock()
142
166
  except queue.Empty:
143
167
  pass
168
+ # 檢查子程序是否結束 / check if process finished
144
169
  if self.process.returncode == 0:
145
170
  self.process_run_over()
146
171
  elif self.process.returncode is not None:
147
172
  self.process_run_over()
148
173
  if self.still_run_shell:
149
- # poll return code
174
+ # 持續檢查程序狀態 / keep polling process
150
175
  self.process.poll()
151
176
 
152
177
  def process_run_over(self):
178
+ # 當子程序結束時呼叫,停止計時器並清理資源
179
+ # Called when subprocess finishes, stop timer and clean up resources
153
180
  jeditor_logger.info("ShellManager process_run_over")
154
- self.timer.stop()
155
- self.exit_program()
156
- self.main_window.exec_shell = None
181
+ self.timer.stop() # 停止定時器 / stop QTimer
182
+ self.exit_program() # 結束程序並清理 / terminate process and cleanup
183
+ self.main_window.exec_shell = None # 重置 main_window 的 exec_shell / reset exec_shell reference
157
184
  if self.after_done_function is not None:
158
- self.after_done_function()
185
+ self.after_done_function() # 執行結束後的回呼函數 / run callback if provided
159
186
 
160
- # exit program change run flag to false and clean read thread and queue and process
187
+ # exit program: 將執行旗標設為 False,清理執行緒、佇列與子程序
188
+ # exit program: set running flag to False, clean threads, queues, and subprocess
161
189
  def exit_program(self) -> None:
162
190
  jeditor_logger.info("ShellManager exit_program")
163
- self.still_run_shell = False
191
+ self.still_run_shell = False # 停止讀取迴圈 / stop reading loop
164
192
  if self.read_program_output_from_thread is not None:
165
- self.read_program_output_from_thread = None
193
+ self.read_program_output_from_thread = None # 清理 stdout 執行緒 / clear stdout thread
166
194
  if self.read_program_error_output_from_thread is not None:
167
- self.read_program_error_output_from_thread = None
168
- self.print_and_clear_queue()
195
+ self.read_program_error_output_from_thread = None # 清理 stderr 執行緒 / clear stderr thread
196
+ self.print_and_clear_queue() # 清空輸出佇列 / clear output queues
169
197
  if self.process is not None:
170
- self.process.terminate()
171
- self.code_result.append(f"Shell command exit with code {self.process.returncode}")
172
- self.process = None
198
+ self.process.terminate() # 終止子程序 / terminate subprocess
199
+ text_cursor = self.code_result.textCursor()
200
+ text_format = QTextCharFormat()
201
+ text_format.setForeground(actually_color_dict.get("normal_output_color"))
202
+ # 顯示退出代碼 / show exit code
203
+ text_cursor.insertText(f"Shell command exit with code {self.process.returncode}", text_format)
204
+ text_cursor.insertBlock()
205
+ self.process = None # 清空 process 物件 / reset process object
173
206
 
174
207
  def print_and_clear_queue(self) -> None:
208
+ # 清空 stdout 與 stderr 佇列
209
+ # Reset stdout and stderr queues
175
210
  jeditor_logger.info("ShellManager print_and_clear_queue")
176
211
  self.run_output_queue = queue.Queue()
177
212
  self.run_error_queue = queue.Queue()
178
213
 
179
214
  def read_program_output_from_process(self) -> None:
215
+ # 從子程序讀取標準輸出並放入佇列
216
+ # Continuously read stdout from subprocess and put into queue
180
217
  jeditor_logger.info("ShellManager read_program_output_from_process")
181
218
  while self.still_run_shell:
182
- program_output_data = self.process.stdout.read(
219
+ program_output_data = self.process.stdout.readline(
183
220
  self.program_buffer) \
184
- .decode(self.program_encoding, "replace")
221
+ .decode(self.program_encoding, "replace") # 解碼輸出 / decode output
185
222
  if self.process:
186
- self.process.stdout.flush()
187
- self.run_output_queue.put_nowait(program_output_data)
223
+ self.process.stdout.flush() # 清空緩衝區 / flush buffer
224
+ self.run_output_queue.put_nowait(program_output_data) # 放入輸出佇列 / enqueue stdout
188
225
 
189
226
  def read_program_error_output_from_process(self) -> None:
227
+ # 從子程序讀取錯誤輸出並放入佇列
228
+ # Continuously read stderr from subprocess and put into queue
190
229
  jeditor_logger.info("ShellManager read_program_error_output_from_process")
191
230
  while self.still_run_shell:
192
- program_error_output_data = self.process.stderr.read(
231
+ program_error_output_data = self.process.stderr.readline(
193
232
  self.program_buffer) \
194
- .decode(self.program_encoding, "replace")
233
+ .decode(self.program_encoding, "replace") # 解碼錯誤輸出 / decode stderr
195
234
  if self.process:
196
- self.process.stderr.flush()
197
- self.run_error_queue.put_nowait(program_error_output_data)
235
+ self.process.stderr.flush() # 清空緩衝區 / flush buffer
236
+ 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
- from je_editor.pyside_ui.code.syntax.syntax_setting import syntax_word_setting_dict, syntax_rule_setting_dict, \
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
- # Basic Highlight
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()
98
+ # 設定比對到的文字格式 / apply format to matched text
64
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
- "rules": (r"\b[+-]?[0-9]+[lL]?\b",
6
- r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b",
7
- r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"),
8
- "color": QColor(0, 128, 255)
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"'[^'\\]*(\\.[^'\\]*)*'", # Single
17
- r'"[^"\\]*(\\.[^"\\]*)*"', # Double
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 = {
93
+ # 目前為空,可依副檔名新增規則
94
+ # Currently empty, can add rules for other file suffixes
67
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
- # Extend QTextEdit
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
- # Search Result
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
- # Binding search box
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()
73
+ # 使用 FindBackward 旗標往回搜尋
74
+ # Use FindBackward flag to search backwards
53
75
  self.find(text, QTextDocument.FindFlag.FindBackward)