je-editor 0.0.104__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 (151) hide show
  1. je_editor/__init__.py +26 -21
  2. je_editor/__main__.py +1 -1
  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/commit_graph.py +77 -0
  7. je_editor/git_client/git_action.py +175 -0
  8. je_editor/git_client/git_cli.py +66 -0
  9. je_editor/pyside_ui/browser/browser_download_window.py +75 -0
  10. je_editor/pyside_ui/browser/browser_serach_lineedit.py +51 -0
  11. je_editor/pyside_ui/browser/browser_view.py +87 -0
  12. je_editor/pyside_ui/browser/browser_widget.py +103 -0
  13. je_editor/pyside_ui/browser/main_browser_widget.py +85 -0
  14. je_editor/pyside_ui/code/auto_save/auto_save_manager.py +60 -0
  15. je_editor/pyside_ui/code/auto_save/auto_save_thread.py +59 -0
  16. je_editor/pyside_ui/code/code_format/pep8_format.py +130 -0
  17. je_editor/pyside_ui/code/code_process/code_exec.py +267 -0
  18. je_editor/pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py +412 -0
  19. je_editor/pyside_ui/code/running_process_manager.py +48 -0
  20. je_editor/pyside_ui/code/shell_process/shell_exec.py +236 -0
  21. je_editor/pyside_ui/code/syntax/python_syntax.py +99 -0
  22. je_editor/pyside_ui/code/syntax/syntax_setting.py +95 -0
  23. je_editor/pyside_ui/code/textedit_code_result/code_record.py +75 -0
  24. je_editor/pyside_ui/code/variable_inspector/inspector_gui.py +172 -0
  25. je_editor/pyside_ui/dialog/ai_dialog/set_ai_dialog.py +71 -0
  26. je_editor/pyside_ui/dialog/file_dialog/create_file_dialog.py +68 -0
  27. je_editor/pyside_ui/dialog/file_dialog/open_file_dialog.py +111 -0
  28. je_editor/pyside_ui/dialog/file_dialog/save_file_dialog.py +67 -0
  29. je_editor/pyside_ui/dialog/search_ui/search_error_box.py +49 -0
  30. je_editor/pyside_ui/dialog/search_ui/search_text_box.py +49 -0
  31. je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +90 -0
  32. je_editor/pyside_ui/git_ui/code_diff_compare/line_number_code_viewer.py +141 -0
  33. je_editor/pyside_ui/git_ui/code_diff_compare/multi_file_diff_viewer.py +88 -0
  34. je_editor/pyside_ui/git_ui/code_diff_compare/side_by_side_diff_widget.py +284 -0
  35. je_editor/pyside_ui/git_ui/git_client/commit_table.py +65 -0
  36. je_editor/pyside_ui/git_ui/git_client/git_branch_tree_widget.py +156 -0
  37. je_editor/pyside_ui/git_ui/git_client/git_client_gui.py +799 -0
  38. je_editor/pyside_ui/git_ui/git_client/graph_view.py +218 -0
  39. je_editor/pyside_ui/main_ui/ai_widget/ai_config.py +34 -0
  40. je_editor/pyside_ui/main_ui/ai_widget/ask_thread.py +36 -0
  41. je_editor/pyside_ui/main_ui/ai_widget/chat_ui.py +147 -0
  42. je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +84 -0
  43. je_editor/pyside_ui/main_ui/console_widget/console_gui.py +162 -0
  44. je_editor/pyside_ui/main_ui/console_widget/qprocess_adapter.py +84 -0
  45. je_editor/pyside_ui/main_ui/dock/__init__.py +0 -0
  46. je_editor/pyside_ui/main_ui/dock/destroy_dock.py +50 -0
  47. je_editor/pyside_ui/main_ui/editor/__init__.py +0 -0
  48. je_editor/pyside_ui/main_ui/editor/editor_widget.py +301 -0
  49. je_editor/pyside_ui/main_ui/editor/editor_widget_dock.py +70 -0
  50. je_editor/pyside_ui/main_ui/editor/process_input.py +101 -0
  51. je_editor/pyside_ui/main_ui/ipython_widget/__init__.py +0 -0
  52. je_editor/pyside_ui/main_ui/ipython_widget/rich_jupyter.py +78 -0
  53. je_editor/pyside_ui/main_ui/main_editor.py +369 -0
  54. je_editor/pyside_ui/main_ui/menu/__init__.py +0 -0
  55. je_editor/pyside_ui/main_ui/menu/check_style_menu/__init__.py +0 -0
  56. je_editor/pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +104 -0
  57. je_editor/pyside_ui/main_ui/menu/dock_menu/__init__.py +0 -0
  58. je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +208 -0
  59. je_editor/pyside_ui/main_ui/menu/file_menu/__init__.py +0 -0
  60. je_editor/pyside_ui/main_ui/menu/file_menu/build_file_menu.py +186 -0
  61. je_editor/pyside_ui/main_ui/menu/help_menu/__init__.py +0 -0
  62. je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +100 -0
  63. je_editor/pyside_ui/main_ui/menu/language_menu/__init__.py +0 -0
  64. je_editor/pyside_ui/main_ui/menu/language_menu/build_language_server.py +89 -0
  65. je_editor/pyside_ui/main_ui/menu/python_env_menu/__init__.py +0 -0
  66. je_editor/pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py +238 -0
  67. je_editor/pyside_ui/main_ui/menu/run_menu/__init__.py +0 -0
  68. je_editor/pyside_ui/main_ui/menu/run_menu/build_run_menu.py +160 -0
  69. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/__init__.py +0 -0
  70. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +109 -0
  71. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +101 -0
  72. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +98 -0
  73. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py +41 -0
  74. je_editor/pyside_ui/main_ui/menu/set_menu_bar.py +63 -0
  75. je_editor/pyside_ui/main_ui/menu/style_menu/__init__.py +0 -0
  76. je_editor/pyside_ui/main_ui/menu/style_menu/build_style_menu.py +73 -0
  77. je_editor/pyside_ui/main_ui/menu/tab_menu/__init__.py +0 -0
  78. je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +275 -0
  79. je_editor/pyside_ui/main_ui/menu/text_menu/__init__.py +0 -0
  80. je_editor/pyside_ui/main_ui/menu/text_menu/build_text_menu.py +135 -0
  81. je_editor/pyside_ui/main_ui/save_settings/__init__.py +0 -0
  82. je_editor/pyside_ui/main_ui/save_settings/setting_utils.py +33 -0
  83. je_editor/pyside_ui/main_ui/save_settings/user_color_setting_file.py +103 -0
  84. je_editor/pyside_ui/main_ui/save_settings/user_setting_file.py +58 -0
  85. je_editor/pyside_ui/main_ui/system_tray/__init__.py +0 -0
  86. je_editor/pyside_ui/main_ui/system_tray/extend_system_tray.py +90 -0
  87. je_editor/start_editor.py +32 -8
  88. je_editor/utils/encodings/python_encodings.py +100 -97
  89. je_editor/utils/exception/exception_tags.py +11 -11
  90. je_editor/utils/file/open/open_file.py +38 -22
  91. je_editor/utils/file/save/save_file.py +40 -16
  92. je_editor/utils/json/json_file.py +36 -15
  93. je_editor/utils/json_format/json_process.py +38 -2
  94. je_editor/utils/logging/__init__.py +0 -0
  95. je_editor/utils/logging/loggin_instance.py +57 -0
  96. je_editor/utils/multi_language/__init__.py +0 -0
  97. je_editor/utils/multi_language/english.py +221 -0
  98. je_editor/utils/multi_language/multi_language_wrapper.py +54 -0
  99. je_editor/utils/multi_language/traditional_chinese.py +214 -0
  100. je_editor/utils/redirect_manager/redirect_manager_class.py +67 -25
  101. je_editor/utils/venv_check/__init__.py +0 -0
  102. je_editor/utils/venv_check/check_venv.py +51 -0
  103. je_editor-0.0.228.dist-info/METADATA +99 -0
  104. je_editor-0.0.228.dist-info/RECORD +140 -0
  105. {je_editor-0.0.104.dist-info → je_editor-0.0.228.dist-info}/WHEEL +1 -1
  106. {je_editor-0.0.104.dist-info → je_editor-0.0.228.dist-info/licenses}/LICENSE +1 -1
  107. je_editor/pyside_ui/auto_save/auto_save_thread.py +0 -34
  108. je_editor/pyside_ui/code_editor/code_edit_plaintext.py +0 -143
  109. je_editor/pyside_ui/code_process/code_exec.py +0 -190
  110. je_editor/pyside_ui/code_result/code_record.py +0 -39
  111. je_editor/pyside_ui/colors/global_color.py +0 -4
  112. je_editor/pyside_ui/file_dialog/open_file_dialog.py +0 -27
  113. je_editor/pyside_ui/file_dialog/save_file_dialog.py +0 -24
  114. je_editor/pyside_ui/main_ui/editor_main_ui/main_editor.py +0 -183
  115. je_editor/pyside_ui/main_ui_setting/ui_setting.py +0 -36
  116. je_editor/pyside_ui/menu/menu_bar/check_style_menu/build_check_style_menu.py +0 -44
  117. je_editor/pyside_ui/menu/menu_bar/file_menu/build_file_menu.py +0 -30
  118. je_editor/pyside_ui/menu/menu_bar/help_menu/build_help_menu.py +0 -39
  119. je_editor/pyside_ui/menu/menu_bar/run_menu/build_run_menu.py +0 -102
  120. je_editor/pyside_ui/menu/menu_bar/set_menu_bar.py +0 -24
  121. je_editor/pyside_ui/menu/menu_bar/venv_menu/build_venv_menu.py +0 -74
  122. je_editor/pyside_ui/search_ui/search_error_box.py +0 -20
  123. je_editor/pyside_ui/search_ui/search_text_box.py +0 -20
  124. je_editor/pyside_ui/shell_process/shell_exec.py +0 -157
  125. je_editor/pyside_ui/syntax/python_syntax.py +0 -99
  126. je_editor/pyside_ui/treeview/project_treeview/set_project_treeview.py +0 -47
  127. je_editor/pyside_ui/user_setting/user_setting_file.py +0 -23
  128. je_editor-0.0.104.dist-info/METADATA +0 -84
  129. je_editor-0.0.104.dist-info/RECORD +0 -69
  130. /je_editor/{pyside_ui/auto_save → code_scan}/__init__.py +0 -0
  131. /je_editor/{pyside_ui/code_editor → git_client}/__init__.py +0 -0
  132. /je_editor/pyside_ui/{code_process → browser}/__init__.py +0 -0
  133. /je_editor/pyside_ui/{code_result → code}/__init__.py +0 -0
  134. /je_editor/pyside_ui/{colors → code/auto_save}/__init__.py +0 -0
  135. /je_editor/pyside_ui/{file_dialog → code/code_format}/__init__.py +0 -0
  136. /je_editor/pyside_ui/{main_ui/editor_main_ui → code/code_process}/__init__.py +0 -0
  137. /je_editor/pyside_ui/{main_ui_setting → code/plaintext_code_edit}/__init__.py +0 -0
  138. /je_editor/pyside_ui/{menu → code/shell_process}/__init__.py +0 -0
  139. /je_editor/pyside_ui/{menu/menu_bar → code/syntax}/__init__.py +0 -0
  140. /je_editor/pyside_ui/{menu/menu_bar/check_style_menu → code/textedit_code_result}/__init__.py +0 -0
  141. /je_editor/pyside_ui/{menu/menu_bar/file_menu → code/variable_inspector}/__init__.py +0 -0
  142. /je_editor/pyside_ui/{menu/menu_bar/help_menu → dialog}/__init__.py +0 -0
  143. /je_editor/pyside_ui/{menu/menu_bar/run_menu → dialog/ai_dialog}/__init__.py +0 -0
  144. /je_editor/pyside_ui/{menu/menu_bar/venv_menu → dialog/file_dialog}/__init__.py +0 -0
  145. /je_editor/pyside_ui/{search_ui → dialog/search_ui}/__init__.py +0 -0
  146. /je_editor/pyside_ui/{shell_process → git_ui}/__init__.py +0 -0
  147. /je_editor/pyside_ui/{syntax → git_ui/code_diff_compare}/__init__.py +0 -0
  148. /je_editor/pyside_ui/{treeview → git_ui/git_client}/__init__.py +0 -0
  149. /je_editor/pyside_ui/{treeview/project_treeview → main_ui/ai_widget}/__init__.py +0 -0
  150. /je_editor/pyside_ui/{user_setting → main_ui/console_widget}/__init__.py +0 -0
  151. {je_editor-0.0.104.dist-info → je_editor-0.0.228.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
6
+
7
+ if TYPE_CHECKING:
8
+ # 僅在型別檢查時匯入,避免循環依賴
9
+ # Only imported during type checking to avoid circular imports
10
+ from je_editor.pyside_ui.code.code_process.code_exec import ExecManager
11
+ from je_editor.pyside_ui.code.shell_process.shell_exec import ShellManager
12
+
13
+ from typing import List, Union
14
+
15
+
16
+ class RunInstanceManager(object):
17
+ """
18
+ 管理程式執行與 Shell 執行的實例
19
+ Manager for ExecManager and ShellManager instances
20
+ """
21
+
22
+ def __init__(self):
23
+ # 初始化,建立一個空的實例清單
24
+ # Initialize with an empty instance list
25
+ jeditor_logger.info("Init RunInstanceManager")
26
+ self.instance_list: List[Union[ExecManager, ShellManager]] = list()
27
+
28
+ def close_all_instance(self):
29
+ """
30
+ 關閉所有執行中的實例,並清理 main_window 的執行狀態
31
+ Close all running instances and reset main_window execution states
32
+ """
33
+ jeditor_logger.info("RunInstanceManager close_all_instance")
34
+ for manager in self.instance_list:
35
+ # 若子程序仍存在,則終止
36
+ # Terminate process if still running
37
+ if manager.process is not None:
38
+ manager.process.terminate()
39
+ # 清理 main_window 的執行狀態
40
+ # Reset execution states in main_window
41
+ manager.main_window.exec_program = None
42
+ manager.main_window.exec_shell = None
43
+ manager.main_window.exec_python_debugger = None
44
+
45
+
46
+ # 建立全域唯一的 RunInstanceManager 實例
47
+ # Create a global singleton instance of RunInstanceManager
48
+ run_instance_manager = RunInstanceManager()
@@ -0,0 +1,236 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import queue
5
+ import shlex
6
+ import subprocess
7
+ import sys
8
+ from pathlib import Path
9
+ from threading import Thread
10
+ from typing import Union, Callable
11
+
12
+ from PySide6.QtCore import QTimer
13
+ from PySide6.QtGui import QTextCharFormat
14
+ from PySide6.QtWidgets import QTextEdit
15
+
16
+ from je_editor.pyside_ui.code.running_process_manager import run_instance_manager
17
+ from je_editor.pyside_ui.main_ui.editor.editor_widget import EditorWidget
18
+ from je_editor.pyside_ui.main_ui.save_settings.user_color_setting_file import actually_color_dict
19
+ from je_editor.utils.exception.exception_tags import je_editor_init_error
20
+ from je_editor.utils.exception.exceptions import JEditorException
21
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
22
+ from je_editor.utils.venv_check.check_venv import check_and_choose_venv
23
+
24
+
25
+ class ShellManager(object):
26
+ def __init__(
27
+ self,
28
+ main_window: Union[EditorWidget, None] = None,
29
+ shell_encoding: str = "utf-8",
30
+ program_buffer: int = 1024,
31
+ after_done_function: Union[None, Callable] = None
32
+ ):
33
+ # 初始化 ShellManager,記錄基本設定
34
+ # Initialize ShellManager, log basic settings
35
+ jeditor_logger.info(f"Init ShellManager "
36
+ f"main_window: {main_window} "
37
+ f"shell_encoding: {shell_encoding} "
38
+ f"program_buffer: {program_buffer} "
39
+ f"after_done_function: {after_done_function}")
40
+ """
41
+ :param main_window: Pyside 主視窗 / Pyside main window
42
+ :param shell_encoding: shell 輸出編碼 / shell command output encoding
43
+ :param program_buffer: 緩衝區大小 / buffer size
44
+ """
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
60
+
61
+ def renew_path(self) -> None:
62
+ # 更新 Python 編譯器路徑
63
+ # Renew Python compiler path
64
+ jeditor_logger.info("ShellManager renew_path")
65
+ if self.main_window.python_compiler is None:
66
+ # 如果主視窗沒有指定 Python,則使用 venv
67
+ # If no compiler specified, use venv
68
+ if sys.platform in ["win32", "cygwin", "msys"]:
69
+ venv_path = Path(os.getcwd() + "/venv/Scripts")
70
+ else:
71
+ venv_path = Path(os.getcwd() + "/venv/bin")
72
+ self.compiler_path = check_and_choose_venv(venv_path)
73
+ else:
74
+ self.compiler_path = self.main_window.python_compiler
75
+
76
+ def later_init(self) -> None:
77
+ # 延遲初始化,綁定輸出視窗
78
+ # Late initialization, bind output QTextEdit
79
+ jeditor_logger.info("ShellManager later_init")
80
+ if self.main_window is not None:
81
+ self.code_result: QTextEdit = self.main_window.code_result
82
+ else:
83
+ raise JEditorException(je_editor_init_error)
84
+
85
+ def exec_shell(self, shell_command: Union[str, list]) -> None:
86
+ """
87
+ 執行 shell 指令
88
+ Execute shell command
89
+ :param shell_command: 要執行的指令 / command to run
90
+ :return: 若錯誤則回傳錯誤訊息 / return error message if failed
91
+ """
92
+ jeditor_logger.info(f"ShellManager exec_shell, shell_command: {shell_command}")
93
+ try:
94
+ self.exit_program() # 結束舊的程序 / terminate previous process
95
+ self.code_result.setPlainText("") # 清空輸出視窗 / clear output window
96
+ if sys.platform in ["win32", "cygwin", "msys"]:
97
+ args = shell_command
98
+ else:
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
106
+ self.process = subprocess.Popen(
107
+ args=args,
108
+ stdout=subprocess.PIPE,
109
+ stderr=subprocess.PIPE,
110
+ stdin=subprocess.PIPE,
111
+ shell=True,
112
+ )
113
+ self.still_run_shell = True
114
+ # 建立讀取標準輸出的執行緒 / thread for stdout
115
+ self.read_program_output_from_thread = Thread(
116
+ target=self.read_program_output_from_process,
117
+ daemon=True
118
+ )
119
+ self.read_program_output_from_thread.start()
120
+ # 建立讀取錯誤輸出的執行緒 / thread for stderr
121
+ self.read_program_error_output_from_thread = Thread(
122
+ target=self.read_program_error_output_from_process,
123
+ daemon=True
124
+ )
125
+ self.read_program_error_output_from_thread.start()
126
+ # 啟動定時器,每 10ms 更新一次輸出 / start QTimer for updating output
127
+ self.timer = QTimer(self.main_window)
128
+ self.timer.setInterval(10)
129
+ self.timer.timeout.connect(self.pull_text)
130
+ self.timer.start()
131
+ except Exception as error:
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()
139
+ if self.process is not None:
140
+ self.process.terminate()
141
+
142
+ # tkinter_ui update method
143
+ def pull_text(self) -> None:
144
+ # 從佇列中拉取輸出與錯誤訊息,並更新到 UI
145
+ # Pull stdout/stderr messages from queue and update UI
146
+ jeditor_logger.info("ShellManager pull_text")
147
+ try:
148
+ if not self.run_output_queue.empty():
149
+ output_message = self.run_output_queue.get_nowait()
150
+ output_message = str(output_message).strip()
151
+ if output_message:
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()
157
+ if not self.run_error_queue.empty():
158
+ error_message = self.run_error_queue.get_nowait()
159
+ error_message = str(error_message).strip()
160
+ if error_message:
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()
166
+ except queue.Empty:
167
+ pass
168
+ # 檢查子程序是否結束 / check if process finished
169
+ if self.process.returncode == 0:
170
+ self.process_run_over()
171
+ elif self.process.returncode is not None:
172
+ self.process_run_over()
173
+ if self.still_run_shell:
174
+ # 持續檢查程序狀態 / keep polling process
175
+ self.process.poll()
176
+
177
+ def process_run_over(self):
178
+ # 當子程序結束時呼叫,停止計時器並清理資源
179
+ # Called when subprocess finishes, stop timer and clean up resources
180
+ jeditor_logger.info("ShellManager process_run_over")
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
184
+ if self.after_done_function is not None:
185
+ self.after_done_function() # 執行結束後的回呼函數 / run callback if provided
186
+
187
+ # exit program: 將執行旗標設為 False,清理執行緒、佇列與子程序
188
+ # exit program: set running flag to False, clean threads, queues, and subprocess
189
+ def exit_program(self) -> None:
190
+ jeditor_logger.info("ShellManager exit_program")
191
+ self.still_run_shell = False # 停止讀取迴圈 / stop reading loop
192
+ if self.read_program_output_from_thread is not None:
193
+ self.read_program_output_from_thread = None # 清理 stdout 執行緒 / clear stdout thread
194
+ if self.read_program_error_output_from_thread is not None:
195
+ self.read_program_error_output_from_thread = None # 清理 stderr 執行緒 / clear stderr thread
196
+ self.print_and_clear_queue() # 清空輸出佇列 / clear output queues
197
+ if self.process is not 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
206
+
207
+ def print_and_clear_queue(self) -> None:
208
+ # 清空 stdout 與 stderr 佇列
209
+ # Reset stdout and stderr queues
210
+ jeditor_logger.info("ShellManager print_and_clear_queue")
211
+ self.run_output_queue = queue.Queue()
212
+ self.run_error_queue = queue.Queue()
213
+
214
+ def read_program_output_from_process(self) -> None:
215
+ # 從子程序讀取標準輸出並放入佇列
216
+ # Continuously read stdout from subprocess and put into queue
217
+ jeditor_logger.info("ShellManager read_program_output_from_process")
218
+ while self.still_run_shell:
219
+ program_output_data = self.process.stdout.readline(
220
+ self.program_buffer) \
221
+ .decode(self.program_encoding, "replace") # 解碼輸出 / decode output
222
+ if self.process:
223
+ self.process.stdout.flush() # 清空緩衝區 / flush buffer
224
+ self.run_output_queue.put_nowait(program_output_data) # 放入輸出佇列 / enqueue stdout
225
+
226
+ def read_program_error_output_from_process(self) -> None:
227
+ # 從子程序讀取錯誤輸出並放入佇列
228
+ # Continuously read stderr from subprocess and put into queue
229
+ jeditor_logger.info("ShellManager read_program_error_output_from_process")
230
+ while self.still_run_shell:
231
+ program_error_output_data = self.process.stderr.readline(
232
+ self.program_buffer) \
233
+ .decode(self.program_encoding, "replace") # 解碼錯誤輸出 / decode stderr
234
+ if self.process:
235
+ self.process.stderr.flush() # 清空緩衝區 / flush buffer
236
+ self.run_error_queue.put_nowait(program_error_output_data) # 放入錯誤佇列 / enqueue stderr
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ # 僅在型別檢查時匯入,避免循環依賴
8
+ # Only imported during type checking to avoid circular imports
9
+ from je_editor.pyside_ui.code.plaintext_code_edit.code_edit_plaintext import CodeEditor
10
+
11
+ from PySide6.QtCore import QRegularExpression
12
+ from PySide6.QtGui import QSyntaxHighlighter
13
+ from PySide6.QtGui import QTextCharFormat
14
+
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,
20
+ syntax_extend_setting_dict
21
+ )
22
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
23
+
24
+
25
+ class PythonHighlighter(QSyntaxHighlighter):
26
+ """
27
+ Python 語法高亮類別,繼承自 QSyntaxHighlighter
28
+ Python syntax highlighter class, inherits from QSyntaxHighlighter
29
+ """
30
+
31
+ def __init__(self, parent=None, main_window: CodeEditor = None):
32
+ jeditor_logger.info(f"Init PythonHighlighter parent: {parent}")
33
+ super().__init__(parent)
34
+
35
+ self.highlight_rules = [] # 儲存所有高亮規則 / store all highlight rules
36
+
37
+ # 判斷目前檔案副檔名,若無則預設為 .py
38
+ # Determine current file suffix, default to .py
39
+ if main_window.current_file is not None:
40
+ current_file_suffix = Path(main_window.current_file).suffix
41
+ else:
42
+ current_file_suffix = ".py"
43
+
44
+ # -------------------------
45
+ # 基本語法規則 (通用)
46
+ # Basic highlight rules (common)
47
+ # -------------------------
48
+ for rule_variable_dict in syntax_rule_setting_dict.values():
49
+ color = rule_variable_dict.get("color") # 規則顏色 / rule color
50
+ text_char_format = QTextCharFormat()
51
+ text_char_format.setForeground(color)
52
+ for rule in rule_variable_dict.get("rules"): # 正則規則 / regex rules
53
+ pattern = QRegularExpression(rule)
54
+ self.highlight_rules.append((pattern, text_char_format))
55
+
56
+ # -------------------------
57
+ # Python 語法高亮
58
+ # Python-specific highlight
59
+ # -------------------------
60
+ if current_file_suffix == ".py":
61
+ for rule_variable_dict in syntax_word_setting_dict.values():
62
+ color = rule_variable_dict.get("color")
63
+ text_char_format = QTextCharFormat()
64
+ text_char_format.setForeground(color)
65
+ for word in rule_variable_dict.get("words"): # 關鍵字清單 / keyword list
66
+ # 使用 \b 確保完整單字匹配 / use \b for whole word match
67
+ pattern = QRegularExpression(rf"\b{word}\b")
68
+ self.highlight_rules.append((pattern, text_char_format))
69
+
70
+ # -------------------------
71
+ # 其他語言的擴展高亮
72
+ # Extended highlight for other languages
73
+ # -------------------------
74
+ else:
75
+ if syntax_extend_setting_dict.get(current_file_suffix):
76
+ for rule_variable_dict in syntax_extend_setting_dict.get(current_file_suffix).values():
77
+ color = rule_variable_dict.get("color")
78
+ text_char_format = QTextCharFormat()
79
+ text_char_format.setForeground(color)
80
+ for word in rule_variable_dict.get("words"):
81
+ pattern = QRegularExpression(rf"\b{word}\b")
82
+ self.highlight_rules.append((pattern, text_char_format))
83
+ else:
84
+ # 若無對應規則則略過
85
+ # Skip if no rules found
86
+ pass
87
+
88
+ def highlightBlock(self, text) -> None:
89
+ """
90
+ 對每一行文字進行語法高亮
91
+ Apply syntax highlighting to each block of text
92
+ """
93
+ jeditor_logger.info(f"PythonHighlighter highlightBlock text: {text}")
94
+ for pattern, pattern_format in self.highlight_rules:
95
+ match_iterator = pattern.globalMatch(text) # 全域比對 / global regex match
96
+ while match_iterator.hasNext():
97
+ match = match_iterator.next()
98
+ # 設定比對到的文字格式 / apply format to matched text
99
+ self.setFormat(match.capturedStart(), match.capturedLength(), pattern_format)
@@ -0,0 +1,95 @@
1
+ from PySide6.QtGui import QColor
2
+
3
+ # -----------------------------
4
+ # 基本語法規則設定 (數字、註解、字串)
5
+ # Basic syntax rule settings (numbers, comments, strings)
6
+ # -----------------------------
7
+ syntax_rule_setting_dict: dict = {
8
+ "number_rule": {
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
17
+ },
18
+ "comment_rule": {
19
+ # 註解規則 (以 # 開頭直到換行)
20
+ # Comment rule (starts with # until newline)
21
+ "rules": (r"#[^\n]*",),
22
+ "color": QColor(0, 230, 0) # 綠色 Green
23
+ },
24
+ "string_rule": {
25
+ # 字串規則 (單引號與雙引號)
26
+ # String rules (single and double quotes)
27
+ "rules": (
28
+ r"'[^'\\]*(\\.[^'\\]*)*'", # 單引號字串 single-quoted string
29
+ r'"[^"\\]*(\\.[^"\\]*)*"', # 雙引號字串 double-quoted string
30
+ ),
31
+ "color": QColor(0, 153, 0) # 深綠色 Dark green
32
+ }
33
+ }
34
+
35
+ # -----------------------------
36
+ # 關鍵字與內建函式設定
37
+ # Keywords and built-in functions
38
+ # -----------------------------
39
+ syntax_word_setting_dict: dict = {
40
+ "keywords": {
41
+ # Python 保留字 (語法關鍵字)
42
+ # Python reserved keywords
43
+ "words": (
44
+ "False", "None", "True", "and", "as", "assert", "async",
45
+ "await", "break", "class", "continue", "def", "del",
46
+ "elif", "else", "except", "finally", "for", "from",
47
+ "global", "if", "import", "in", "is", "lambda", "nonlocal",
48
+ "not", "or", "pass", "raise", "return", "try", "while", "with", "yield"
49
+ ),
50
+ "color": QColor(255, 212, 102) # 黃色 Yellow
51
+ },
52
+ "builtins_keyword": {
53
+ # Python 內建函式與型別
54
+ # Python built-in functions and types
55
+ "words": (
56
+ "abs", "aiter", "all", "any", "anext", "ascii",
57
+ "bin", "bool", "breakpoint", "bytearray", "bytes",
58
+ "callable", "chr", "classmethod", "compile", "complex",
59
+ "delattr", "dict", "dir", "divmod",
60
+ "enumerate", "eval", "exec",
61
+ "filter", "float", "format", "frozenset",
62
+ "getattr", "globals",
63
+ "hasattr", "hash", "help", "hex",
64
+ "id", "input", "int", "isinstance", "issubclass", "iter",
65
+ "len", "list", "locals",
66
+ "map", "max", "memoryview", "min",
67
+ "next",
68
+ "object", "oct", "open", "ord",
69
+ "pow", "print", "property",
70
+ "range", "repr", "reversed", "round",
71
+ "set", "setattr", "slice", "sorted", "staticmethod",
72
+ "str", "sum", "super",
73
+ "tuple", "type",
74
+ "vars",
75
+ "zip",
76
+ "__import__"
77
+ ),
78
+ "color": QColor(0, 255, 255) # 青色 Cyan
79
+ },
80
+ "self": {
81
+ # Python 類別中的 self 關鍵字
82
+ # "self" keyword in Python classes
83
+ "words": ("self",),
84
+ "color": QColor(204, 0, 204) # 紫色 Purple
85
+ }
86
+ }
87
+
88
+ # -----------------------------
89
+ # 擴展語法設定 (其他語言可在此加入)
90
+ # Extended syntax settings (for other languages)
91
+ # -----------------------------
92
+ syntax_extend_setting_dict: dict = {
93
+ # 目前為空,可依副檔名新增規則
94
+ # Currently empty, can add rules for other file suffixes
95
+ }
@@ -0,0 +1,75 @@
1
+ from PySide6.QtGui import QTextDocument, QAction
2
+ from PySide6.QtWidgets import QTextEdit
3
+
4
+ from je_editor.pyside_ui.dialog.search_ui.search_error_box import SearchResultBox
5
+ from je_editor.pyside_ui.main_ui.save_settings.user_setting_file import user_setting_dict
6
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
7
+
8
+
9
+ class CodeRecord(QTextEdit):
10
+ # 繼承自 QTextEdit,作為程式碼輸出紀錄區
11
+ # Extend QTextEdit, used as a code output record area
12
+ def __init__(self):
13
+ jeditor_logger.info("Init CodeRecord")
14
+ super().__init__()
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
20
+ self.search_result_action = QAction("Search Error")
21
+ self.search_result_action.setShortcut("Ctrl+e")
22
+ self.search_result_action.triggered.connect(
23
+ self.start_search_result_dialog # 綁定觸發事件 / bind trigger event
24
+ )
25
+ self.addAction(self.search_result_action)
26
+
27
+ def append(self, text: str) -> None:
28
+ """
29
+ 新增文字到輸出區,若超過最大行數則清空
30
+ Append text to output area, clear if exceeding max lines
31
+ """
32
+ jeditor_logger.info("CodeRecord append")
33
+ max_line: int = user_setting_dict.get("max_line_of_output", 200000)
34
+ if self.document().lineCount() >= max_line > 0:
35
+ # 若行數超過設定,清空內容
36
+ # Clear content if line count exceeds limit
37
+ self.setPlainText("")
38
+ super().append(text)
39
+
40
+ def start_search_result_dialog(self) -> None:
41
+ """
42
+ 開啟搜尋對話框,並綁定搜尋按鈕事件
43
+ Open search dialog and bind search button events
44
+ """
45
+ jeditor_logger.info("CodeRecord start_search_result_dialog")
46
+ self.search_result_box = SearchResultBox() # 建立搜尋框 / create search box
47
+ self.search_result_box.search_back_button.clicked.connect(
48
+ self.find_back_text # 綁定「上一個」搜尋 / bind "find previous"
49
+ )
50
+ self.search_result_box.search_next_button.clicked.connect(
51
+ self.find_next_text # 綁定「下一個」搜尋 / bind "find next"
52
+ )
53
+ self.search_result_box.show() # 顯示搜尋框 / show search box
54
+
55
+ def find_next_text(self) -> None:
56
+ """
57
+ 搜尋下一個符合的文字
58
+ Find next matching text
59
+ """
60
+ jeditor_logger.info("CodeRecord find_next_text")
61
+ if self.search_result_box.isVisible():
62
+ text = self.search_result_box.search_input.text()
63
+ self.find(text) # 使用 QTextEdit 內建 find 方法 / use QTextEdit built-in find
64
+
65
+ def find_back_text(self) -> None:
66
+ """
67
+ 搜尋上一個符合的文字
68
+ Find previous matching text
69
+ """
70
+ jeditor_logger.info("CodeRecord find_back_text")
71
+ if self.search_result_box.isVisible():
72
+ text = self.search_result_box.search_input.text()
73
+ # 使用 FindBackward 旗標往回搜尋
74
+ # Use FindBackward flag to search backwards
75
+ self.find(text, QTextDocument.FindFlag.FindBackward)