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,84 @@
1
+ import os
2
+
3
+ from PySide6.QtCore import QObject, QProcess, Signal, QTimer
4
+
5
+
6
+ class ConsoleProcessAdapter(QObject):
7
+ """
8
+ ConsoleProcessAdapter 負責管理 QProcess,提供互動式 shell 的啟動、指令傳送與輸出監聽。
9
+ ConsoleProcessAdapter manages QProcess, providing interactive shell start, command sending, and output handling.
10
+ """
11
+
12
+ # 定義訊號 / Define signals
13
+ started = Signal() # 當子程序啟動時發射 / Emitted when process starts
14
+ finished = Signal(int, QProcess.ExitStatus) # 當子程序結束時發射 / Emitted when process finishes
15
+ stdout = Signal(str) # 標準輸出訊號 / Standard output signal
16
+ stderr = Signal(str) # 錯誤輸出訊號 / Standard error signal
17
+ system = Signal(str) # 系統訊息訊號 / System message signal
18
+
19
+ def __init__(self, parent=None):
20
+ super().__init__(parent)
21
+ # 建立 QProcess 物件 / Create QProcess object
22
+ self.proc = QProcess(self)
23
+ # 設定輸出通道分離 (stdout / stderr) / Separate stdout and stderr
24
+ self.proc.setProcessChannelMode(QProcess.ProcessChannelMode.SeparateChannels)
25
+
26
+ # 綁定事件處理函式 / Connect signals to handlers
27
+ self.proc.readyReadStandardOutput.connect(self._on_stdout)
28
+ self.proc.readyReadStandardError.connect(self._on_stderr)
29
+ self.proc.started.connect(self.started)
30
+ self.proc.finished.connect(self.finished)
31
+
32
+ # 設定工作目錄 / Set working directory
33
+ def set_cwd(self, path: str):
34
+ self.proc.setWorkingDirectory(path)
35
+
36
+ # 啟動 shell / Start shell
37
+ def start_shell(self, shell: str = "auto"):
38
+ if self.is_running():
39
+ self.system.emit("Shell already running") # 如果已經在執行,發送提示 / Emit message if already running
40
+ return
41
+ program, args = self._build_shell_command(shell) # 建立 shell 指令 / Build shell command
42
+ self.proc.start(program, args) # 啟動子程序 / Start process
43
+
44
+ # Windows 特殊處理:設定 UTF-8 編碼 / Windows-specific: set UTF-8 encoding
45
+ if os.name == "nt":
46
+ QTimer.singleShot(500, lambda: self.send_command("chcp 65001"))
47
+
48
+ # 傳送指令到 shell / Send command to shell
49
+ def send_command(self, cmd: str):
50
+ if not self.is_running():
51
+ self.system.emit("Shell not running") # 如果 shell 未啟動,發送提示 / Emit message if not running
52
+ return
53
+ self.proc.write((cmd + "\n").encode("utf-8")) # 傳送指令並換行 / Send command with newline
54
+
55
+ # 停止 shell / Stop shell
56
+ def stop(self):
57
+ if not self.is_running():
58
+ return
59
+ self.proc.terminate() # 嘗試正常結束 / Try graceful termination
60
+ # 如果 1 秒後仍在執行,強制 kill / Force kill if still running after 1s
61
+ QTimer.singleShot(1000, lambda: self.is_running() and self.proc.kill())
62
+
63
+ # 判斷是否正在執行 / Check if process is running
64
+ def is_running(self):
65
+ return self.proc.state() != QProcess.ProcessState.NotRunning
66
+
67
+ # 處理標準輸出 / Handle standard output
68
+ def _on_stdout(self):
69
+ self.stdout.emit(bytes(self.proc.readAllStandardOutput()).decode("utf-8", errors="replace"))
70
+
71
+ # 處理錯誤輸出 / Handle standard error
72
+ def _on_stderr(self):
73
+ self.stderr.emit(bytes(self.proc.readAllStandardError()).decode("utf-8", errors="replace"))
74
+
75
+ # 建立 shell 指令 / Build shell command
76
+ def _build_shell_command(self, shell: str):
77
+ if shell == "auto":
78
+ shell = "cmd" if os.name == "nt" else "bash" # Windows 預設 cmd,Linux/macOS 預設 bash
79
+ if os.name == "nt":
80
+ if shell == "powershell":
81
+ return "powershell.exe", ["-NoLogo", "-NoProfile", "-ExecutionPolicy", "Bypass"]
82
+ return "cmd.exe", []
83
+ # Linux/macOS 預設 bash,否則使用 sh / Default bash, fallback to sh
84
+ return ("/bin/bash" if shell == "bash" else "/bin/sh"), []
File without changes
@@ -0,0 +1,50 @@
1
+ from PySide6.QtCore import Qt
2
+ from PySide6.QtWidgets import QDockWidget
3
+
4
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
5
+
6
+
7
+ class DestroyDock(QDockWidget):
8
+ """
9
+ DestroyDock 繼承自 QDockWidget,主要用於建立一個可停駐的視窗,
10
+ 並在關閉時自動釋放資源與記錄日誌。
11
+
12
+ DestroyDock inherits from QDockWidget, mainly used to create a dockable window,
13
+ and automatically release resources and log events when closed.
14
+ """
15
+
16
+ def __init__(self):
17
+ # 初始化時記錄日誌 / Log initialization
18
+ jeditor_logger.info("Init DestroyDock")
19
+ super().__init__()
20
+
21
+ # 設定允許停駐的區域 (可停駐在所有邊)
22
+ # Allow docking in all areas (top, bottom, left, right)
23
+ self.setAllowedAreas(Qt.DockWidgetArea.AllDockWidgetAreas)
24
+
25
+ # 設定屬性:當視窗關閉時,自動刪除物件以釋放記憶體
26
+ # Set attribute: delete object on close to free memory
27
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
28
+
29
+ def closeEvent(self, event) -> None:
30
+ """
31
+ 覆寫 closeEvent,在關閉時額外處理:
32
+ - 記錄關閉事件
33
+ - 關閉內部 widget
34
+ - 呼叫父類別的 close()
35
+
36
+ Override closeEvent to:
37
+ - Log the close event
38
+ - Close the internal widget
39
+ - Call parent close()
40
+ """
41
+ # 記錄關閉事件 / Log close event
42
+ jeditor_logger.info(f"DestroyDock closeEvent event: {event}")
43
+
44
+ # 關閉 dock 內部的 widget,確保資源釋放
45
+ # Close the internal widget to ensure resource release
46
+ self.widget().close()
47
+
48
+ # 呼叫父類別的 close(),完成 Qt 預設的關閉流程
49
+ # Call parent close() to complete default Qt closing process
50
+ super().close()
File without changes
@@ -0,0 +1,301 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from PySide6.QtGui import QTextCharFormat
6
+
7
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
8
+ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
9
+
10
+ if TYPE_CHECKING:
11
+ from je_editor.pyside_ui.main_ui.main_editor import EditorMain
12
+ from je_editor.pyside_ui.code.shell_process.shell_exec import ShellManager
13
+ from je_editor.pyside_ui.code.code_process.code_exec import ExecManager
14
+
15
+ import pathlib
16
+ from pathlib import Path
17
+ from typing import Union
18
+
19
+ from PySide6.QtCore import Qt, QFileInfo, QDir
20
+ from PySide6.QtWidgets import QWidget, QGridLayout, QSplitter, QScrollArea, QFileSystemModel, QTreeView, QTabWidget, \
21
+ QMessageBox
22
+
23
+ from je_editor.pyside_ui.code.auto_save.auto_save_manager import auto_save_manager_dict, init_new_auto_save_thread, \
24
+ file_is_open_manager_dict
25
+ from je_editor.pyside_ui.code.auto_save.auto_save_thread import CodeEditSaveThread
26
+ from je_editor.pyside_ui.code.code_format.pep8_format import PEP8FormatChecker
27
+ from je_editor.pyside_ui.code.plaintext_code_edit.code_edit_plaintext import CodeEditor
28
+ from je_editor.pyside_ui.code.textedit_code_result.code_record import CodeRecord
29
+ from je_editor.pyside_ui.main_ui.save_settings.user_color_setting_file import actually_color_dict
30
+ from je_editor.pyside_ui.main_ui.save_settings.user_setting_file import user_setting_dict
31
+ from je_editor.utils.file.open.open_file import read_file
32
+
33
+
34
+ class EditorWidget(QWidget):
35
+ """
36
+ EditorWidget 是主要的程式碼編輯器元件,包含:
37
+ - 專案檔案樹狀檢視
38
+ - 程式碼編輯區
39
+ - 執行結果、格式檢查、除錯輸出
40
+ - 自動儲存與檔案管理
41
+
42
+ EditorWidget is the main code editor widget, including:
43
+ - Project file tree view
44
+ - Code editing area
45
+ - Execution result, format check, debugger output
46
+ - Auto-save and file management
47
+ """
48
+
49
+ def __init__(self, main_window: EditorMain):
50
+ jeditor_logger.info(f"Init EditorWidget main_window: {main_window}")
51
+ super().__init__()
52
+ # ---------------- Init variables 初始化變數 ----------------
53
+ self.checker: Union[PEP8FormatChecker, None] = None
54
+ self.current_file = None
55
+ self.tree_view_scroll_area = None
56
+ self.project_treeview: Union[QTreeView, None] = None
57
+ self.project_treeview_model = None
58
+ self.python_compiler = None
59
+ self.main_window = main_window
60
+ self.tab_manager = self.main_window.tab_widget
61
+
62
+ # 執行相關物件 / Execution related objects
63
+ self.exec_program: Union[None, ExecManager] = None
64
+ self.exec_shell: Union[None, ShellManager] = None
65
+ self.exec_python_debugger: Union[None, ExecManager] = None
66
+
67
+ # 自動儲存執行緒 / Auto-save thread
68
+ self.code_save_thread: Union[CodeEditSaveThread, None] = None
69
+
70
+ # ---------------- UI 初始化 ----------------
71
+ self.grid_layout = QGridLayout(self)
72
+ self.setWindowTitle(language_wrapper.language_word_dict.get("application_name"))
73
+
74
+ # 建立專案檔案樹狀檢視 / Setup project tree view
75
+ self.set_project_treeview()
76
+
77
+ # 主分割器 (左:檔案樹,右:編輯器) / Main splitter (left: tree, right: editor)
78
+ self.full_splitter = QSplitter()
79
+ self.full_splitter.setOrientation(Qt.Orientation.Horizontal)
80
+
81
+ # 編輯器分割器 (上:編輯器,下:輸出) / Editor splitter (top: editor, bottom: output)
82
+ self.edit_splitter = QSplitter(self.full_splitter)
83
+ self.edit_splitter.setOrientation(Qt.Orientation.Vertical)
84
+
85
+ # 程式碼編輯器與輸出區 / Code editor and result area
86
+ self.code_edit = CodeEditor(self)
87
+ self.code_result = CodeRecord()
88
+ self.code_result_cursor = self.code_result.textCursor()
89
+
90
+ # 捲動區包裝編輯器與輸出 / Scroll areas for editor and result
91
+ self.code_edit_scroll_area = QScrollArea()
92
+ self.code_edit_scroll_area.setWidgetResizable(True)
93
+ self.code_edit_scroll_area.setViewportMargins(0, 0, 0, 0)
94
+ self.code_edit_scroll_area.setWidget(self.code_edit)
95
+
96
+ self.code_result_scroll_area = QScrollArea()
97
+ self.code_result_scroll_area.setWidgetResizable(True)
98
+ self.code_result_scroll_area.setViewportMargins(0, 0, 0, 0)
99
+ self.code_result_scroll_area.setWidget(self.code_result)
100
+
101
+ # 格式檢查與除錯輸出 / Format check and debugger output
102
+ self.format_check_result = CodeRecord()
103
+ self.debugger_result = CodeRecord()
104
+
105
+ # 輸出分頁 (執行結果 / 格式檢查 / 除錯) / Output tabs
106
+ self.code_difference_result = QTabWidget()
107
+ self.code_difference_result.addTab(
108
+ self.code_result_scroll_area, language_wrapper.language_word_dict.get("editor_code_result"))
109
+ self.code_difference_result.addTab(
110
+ self.format_check_result, language_wrapper.language_word_dict.get("editor_format_check"))
111
+ self.code_difference_result.addTab(
112
+ self.debugger_result, language_wrapper.language_word_dict.get("editor_debugger_input_title_label"))
113
+
114
+ # 加入分割器 / Add widgets to splitters
115
+ self.edit_splitter.addWidget(self.code_edit_scroll_area)
116
+ self.edit_splitter.addWidget(self.code_difference_result)
117
+ self.edit_splitter.setStretchFactor(0, 3)
118
+ self.edit_splitter.setStretchFactor(1, 1)
119
+ self.edit_splitter.setSizes([300, 100])
120
+
121
+ self.full_splitter.addWidget(self.project_treeview)
122
+ self.full_splitter.addWidget(self.edit_splitter)
123
+ self.full_splitter.setStretchFactor(0, 1)
124
+ self.full_splitter.setStretchFactor(1, 3)
125
+ self.full_splitter.setSizes([100, 300])
126
+
127
+ # 設定字體樣式 / Set font style
128
+ self.code_edit.setStyleSheet(
129
+ f"font-size: {user_setting_dict.get('font_size', 12)}pt;"
130
+ f"font-family: {user_setting_dict.get('font', 'Lato')};"
131
+ )
132
+ self.code_result.setStyleSheet(
133
+ f"font-size: {user_setting_dict.get('font_size', 12)}pt;"
134
+ f"font-family: {user_setting_dict.get('font', 'Lato')};"
135
+ )
136
+
137
+ # 加入主版面配置 / Add to main layout
138
+ self.grid_layout.addWidget(self.full_splitter)
139
+
140
+ # ---------------- Project Treeview ----------------
141
+ def set_project_treeview(self) -> None:
142
+ """
143
+ 建立並設定專案檔案樹狀檢視
144
+ Setup and configure project file tree view
145
+ """
146
+ jeditor_logger.info("EditorWidget set_project_treeview")
147
+ self.project_treeview_model = QFileSystemModel()
148
+ self.project_treeview_model.setRootPath(QDir.currentPath())
149
+ self.project_treeview = QTreeView()
150
+ self.project_treeview.setModel(self.project_treeview_model)
151
+
152
+ # 設定根目錄 (工作目錄或當前路徑) / Set root directory (working dir or current path)
153
+ if self.main_window.working_dir is None:
154
+ self.project_treeview.setRootIndex(
155
+ self.project_treeview_model.index(str(Path.cwd()))
156
+ )
157
+ else:
158
+ self.project_treeview.setRootIndex(
159
+ self.project_treeview_model.index(self.main_window.working_dir)
160
+ )
161
+
162
+ # 包裝成可捲動區域 / Wrap in scroll area
163
+ self.tree_view_scroll_area = QScrollArea()
164
+ self.tree_view_scroll_area.setWidgetResizable(True)
165
+ self.tree_view_scroll_area.setViewportMargins(0, 0, 0, 0)
166
+ self.tree_view_scroll_area.setWidget(self.project_treeview)
167
+ self.grid_layout.addWidget(self.tree_view_scroll_area, 0, 0, 0, 1)
168
+
169
+ # 點擊檔案時觸發 / Connect click event
170
+ self.project_treeview.clicked.connect(self.treeview_click)
171
+
172
+ def check_is_open(self, path: Path):
173
+ """
174
+ 檢查檔案是否已經開啟,如果已開啟則切換到該分頁。
175
+ Check if the file is already open, if yes then switch to that tab.
176
+ """
177
+ jeditor_logger.info(f"EditorWidget check_is_open path: {path}")
178
+ if file_is_open_manager_dict.get(str(path), None) is not None:
179
+ # 嘗試在分頁中找到對應的 EditorWidget
180
+ # Try to find the corresponding EditorWidget in tab manager
181
+ widget: QWidget = self.tab_manager.findChild(EditorWidget, str(path))
182
+ if widget is None:
183
+ # 如果找不到,代表之前的紀錄失效,移除紀錄
184
+ # If not found, remove stale record
185
+ file_is_open_manager_dict.pop(str(path), None)
186
+ else:
187
+ # 如果找到,直接切換到該分頁
188
+ # If found, switch to that tab
189
+ self.tab_manager.setCurrentWidget(widget)
190
+ return False
191
+ else:
192
+ # 如果檔案未開啟,加入紀錄
193
+ # If file not open, add to open manager dict
194
+ file_is_open_manager_dict.update({str(path): str(path)})
195
+ return True
196
+
197
+ def open_an_file(self, path: Path) -> bool:
198
+ """
199
+ 開啟檔案並載入到編輯器。
200
+ Open a file and load it into the editor.
201
+
202
+ :param path: 檔案路徑 / File path
203
+ :return: 如果檔案已經開啟則回傳 False / Return False if file tab already exists
204
+ """
205
+ jeditor_logger.info(f"EditorWidget open_an_file path: {path}")
206
+ if not self.check_is_open(path):
207
+ return False
208
+
209
+ # 如果有自動儲存執行緒,暫時跳過這一輪
210
+ # If auto-save thread exists, skip this round
211
+ if self.code_save_thread:
212
+ self.code_save_thread.skip_this_round = True
213
+
214
+ # 讀取檔案內容 / Read file content
215
+ file, file_content = read_file(str(path))
216
+ self.code_edit.setPlainText(file_content)
217
+
218
+ # 更新目前檔案資訊 / Update current file info
219
+ self.current_file = file
220
+ self.code_edit.current_file = file
221
+ self.code_edit.reset_highlighter()
222
+
223
+ # 更新使用者設定中的最後開啟檔案 / Update last opened file in user settings
224
+ user_setting_dict.update({"last_file": str(self.current_file)})
225
+
226
+ # 啟動或更新自動儲存執行緒 / Start or update auto-save thread
227
+ if self.current_file is not None and self.code_save_thread is None:
228
+ init_new_auto_save_thread(self.current_file, self)
229
+ else:
230
+ self.code_save_thread.file = self.current_file
231
+ self.code_save_thread.skip_this_round = False
232
+
233
+ # 更新分頁標籤名稱 / Update tab title
234
+ self.rename_self_tab()
235
+ return True
236
+
237
+ def treeview_click(self) -> None:
238
+ """
239
+ 當使用者點擊檔案樹中的項目時觸發。
240
+ Triggered when user clicks an item in the project tree view.
241
+ """
242
+ jeditor_logger.info("EditorWidget treeview_click")
243
+ clicked_item: QFileSystemModel = self.project_treeview.selectedIndexes()[0]
244
+ file_info: QFileInfo = self.project_treeview.model().fileInfo(clicked_item)
245
+ path = pathlib.Path(file_info.absoluteFilePath())
246
+ if path.is_file():
247
+ self.open_an_file(path)
248
+
249
+ def rename_self_tab(self):
250
+ """
251
+ 將目前分頁的標籤名稱改為目前檔案名稱。
252
+ Rename the current tab to the current file name.
253
+ """
254
+ jeditor_logger.info("EditorWidget rename_self_tab")
255
+ if self.tab_manager.currentWidget() is self:
256
+ self.tab_manager.setTabText(
257
+ self.tab_manager.currentIndex(), str(Path(self.current_file)))
258
+ self.setObjectName(str(Path(self.current_file)))
259
+
260
+ def check_file_format(self):
261
+ """
262
+ 檢查目前檔案的程式碼格式 (僅支援 Python)。
263
+ Check the code format of the current file (only supports Python).
264
+ """
265
+ if self.current_file:
266
+ jeditor_logger.info("EditorWidget check_file_format")
267
+ suffix_checker = Path(self.current_file).suffix
268
+ if suffix_checker == ".py":
269
+ # 使用 PEP8 格式檢查器 / Use PEP8 format checker
270
+ self.checker = PEP8FormatChecker(self.current_file)
271
+ self.checker.check_all_format()
272
+ self.format_check_result.setPlainText("")
273
+
274
+ # 顯示錯誤訊息並套用顏色 / Display errors with color formatting
275
+ for error in self.checker.error_list:
276
+ text_cursor = self.format_check_result.textCursor()
277
+ text_format = QTextCharFormat()
278
+ text_format.setForeground(actually_color_dict.get("error_output_color"))
279
+ text_cursor.insertText(error, text_format)
280
+ text_cursor.insertBlock()
281
+ self.checker.error_list.clear()
282
+ else:
283
+ # 非 Python 檔案,顯示提示訊息 / Show message if not Python file
284
+ message_box = QMessageBox()
285
+ message_box.setText(
286
+ language_wrapper.language_word_dict.get("python_format_checker_only_support_python_message"))
287
+ message_box.exec_()
288
+
289
+ def close(self) -> bool:
290
+ """
291
+ 關閉編輯器,釋放資源並移除自動儲存紀錄。
292
+ Close the editor, release resources, and remove auto-save records.
293
+ """
294
+ jeditor_logger.info("EditorWidget close")
295
+ if self.code_save_thread is not None:
296
+ self.code_save_thread.still_run = False
297
+ self.code_save_thread = None
298
+ if self.current_file:
299
+ file_is_open_manager_dict.pop(str(Path(self.current_file)), None)
300
+ auto_save_manager_dict.pop(self.current_file, None)
301
+ return super().close()
@@ -0,0 +1,70 @@
1
+ from pathlib import Path
2
+
3
+ from PySide6.QtCore import Qt
4
+ from PySide6.QtWidgets import QWidget, QGridLayout, QScrollArea
5
+ from frontengine.utils.multi_language.language_wrapper import language_wrapper
6
+
7
+ from je_editor.pyside_ui.code.plaintext_code_edit.code_edit_plaintext import CodeEditor
8
+ from je_editor.pyside_ui.main_ui.save_settings.user_setting_file import user_setting_dict
9
+ from je_editor.utils.file.save.save_file import write_file
10
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
11
+
12
+
13
+ class FullEditorWidget(QWidget):
14
+ """
15
+ FullEditorWidget 提供一個完整的單檔編輯器介面,
16
+ 包含程式碼編輯區、捲動支援,以及關閉時自動儲存功能。
17
+
18
+ FullEditorWidget provides a full single-file editor interface,
19
+ including code editing area, scroll support, and auto-save on close.
20
+ """
21
+
22
+ def __init__(self, current_file: str):
23
+ # 初始化時記錄日誌 / Log initialization
24
+ jeditor_logger.info(f"Init FullEditorWidget current_file: {current_file}")
25
+ super().__init__()
26
+
27
+ # ---------------- Init variable 初始化變數 ----------------
28
+ self.current_file = current_file # 目前編輯的檔案路徑 / Current editing file path
29
+
30
+ # ---------------- Attributes 屬性設定 ----------------
31
+ # 設定關閉時自動刪除物件,釋放記憶體
32
+ # Delete object on close to free memory
33
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
34
+
35
+ # ---------------- UI 初始化 ----------------
36
+ self.grid_layout = QGridLayout(self)
37
+ self.setWindowTitle(language_wrapper.language_word_dict.get("application_name"))
38
+
39
+ # 建立程式碼編輯器並放入捲動區域
40
+ # Create code editor and put inside scroll area
41
+ self.code_edit = CodeEditor(self)
42
+ self.code_edit_scroll_area = QScrollArea()
43
+ self.code_edit_scroll_area.setWidgetResizable(True)
44
+ self.code_edit_scroll_area.setViewportMargins(0, 0, 0, 0)
45
+ self.code_edit_scroll_area.setWidget(self.code_edit)
46
+
47
+ # 將編輯器加入版面配置
48
+ # Add editor to layout
49
+ self.grid_layout.addWidget(self.code_edit_scroll_area, 0, 0)
50
+
51
+ # 設定字體樣式 (從使用者設定檔讀取)
52
+ # Set font style (from user settings)
53
+ self.code_edit.setStyleSheet(
54
+ f"font-size: {user_setting_dict.get('font_size', 12)}pt;"
55
+ f"font-family: {user_setting_dict.get('font', 'Lato')};"
56
+ )
57
+
58
+ def closeEvent(self, event) -> None:
59
+ """
60
+ 覆寫 closeEvent,在關閉視窗時自動儲存檔案內容。
61
+ Override closeEvent to auto-save file content when closing window.
62
+ """
63
+ jeditor_logger.info(f"FullEditorWidget closeEvent event: {event}")
64
+ path = Path(self.current_file)
65
+ if path.exists() and path.is_file():
66
+ # 將編輯器內容寫回檔案 / Write editor content back to file
67
+ write_file(self.current_file, self.code_edit.toPlainText())
68
+ # 呼叫父類別的 closeEvent,完成 Qt 預設流程
69
+ # Call parent closeEvent to complete Qt default process
70
+ super().closeEvent(event)
@@ -0,0 +1,101 @@
1
+ from __future__ import annotations # 允許未來版本的型別註解功能 / Enable postponed evaluation of type annotations
2
+
3
+ from typing import \
4
+ TYPE_CHECKING # 用於避免循環匯入,僅在型別檢查時載入 / Used to avoid circular imports, only loaded during type checking
5
+
6
+ from PySide6.QtCore import Qt # Qt 核心模組 / Qt core module
7
+ from PySide6.QtWidgets import QWidget, QLineEdit, QBoxLayout, QPushButton, QHBoxLayout
8
+
9
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
10
+
11
+ # 匯入 PySide6 的 GUI 元件 / Import GUI widgets from PySide6
12
+
13
+ # 專案內的日誌紀錄器 / Project's logger instance
14
+
15
+ if TYPE_CHECKING:
16
+ from je_editor.pyside_ui.main_ui.main_editor import EditorWidget
17
+ # 僅在型別檢查時匯入 EditorWidget,避免循環依賴 / Import only for type checking to avoid circular dependency
18
+
19
+ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
20
+
21
+
22
+ # 多語系支援工具 / Multi-language wrapper for UI text
23
+
24
+
25
+ class ProcessInput(QWidget):
26
+ """
27
+ ProcessInput 是一個輸入視窗,允許使用者輸入指令並傳送到不同的子程序 (program/shell/debugger)。
28
+ ProcessInput is an input widget that allows users to send commands to different subprocesses.
29
+ """
30
+
31
+ def __init__(self, main_window: EditorWidget, process_type: str = "debugger"):
32
+ # 初始化時記錄日誌 / Log initialization
33
+ jeditor_logger.info("Init ProcessInput "
34
+ f"main_window: {main_window} "
35
+ f"process_type: {process_type}")
36
+ super().__init__()
37
+
38
+ # 設定當視窗關閉時自動刪除資源
39
+ # Set attribute to delete widget on close
40
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
41
+
42
+ # === UI 設定 / UI Setup ===
43
+ self.main_window = main_window # 儲存主視窗參考 / Store reference to main window
44
+ self.box_layout = QBoxLayout(QBoxLayout.Direction.TopToBottom) # 垂直佈局 / Vertical layout
45
+ self.command_input = QLineEdit() # 輸入框 / Input field
46
+ self.send_command_button = QPushButton() # 傳送按鈕 / Send button
47
+
48
+ # 設定按鈕文字 (多語系) / Set button text (multi-language)
49
+ self.send_command_button.setText(language_wrapper.language_word_dict.get("process_input_send_command"))
50
+
51
+ # 水平佈局,放置按鈕 / Horizontal layout for button
52
+ self.box_h_layout = QHBoxLayout()
53
+ self.box_h_layout.addWidget(self.send_command_button)
54
+
55
+ # 將元件加入主佈局 / Add widgets to main layout
56
+ self.box_layout.addWidget(self.command_input)
57
+ self.box_layout.addLayout(self.box_h_layout)
58
+
59
+ # 根據 process_type 設定不同的標題與功能 / Configure behavior based on process_type
60
+ if process_type == "program":
61
+ self.setWindowTitle(language_wrapper.language_word_dict.get("editor_program_input_title_label"))
62
+ self.send_command_button.clicked.connect(self.program_send_command)
63
+ elif process_type == "shell":
64
+ self.setWindowTitle(language_wrapper.language_word_dict.get("editor_shell_input_title_label"))
65
+ self.send_command_button.clicked.connect(self.shell_send_command)
66
+ else: # 預設為 debugger / Default: debugger
67
+ self.setWindowTitle(language_wrapper.language_word_dict.get("editor_debugger_input_title_label"))
68
+ self.send_command_button.clicked.connect(self.debugger_send_command)
69
+ # 切換主視窗的顯示頁面到 debugger 結果 / Switch main window tab to debugger result
70
+ self.main_window.code_difference_result.setCurrentWidget(self.main_window.debugger_result)
71
+
72
+ # 設定主佈局 / Apply layout
73
+ self.setLayout(self.box_layout)
74
+
75
+ # === Debugger 指令傳送 / Send command to debugger ===
76
+ def debugger_send_command(self):
77
+ jeditor_logger.info("EditorWidget debugger_send_command")
78
+ if self.main_window.exec_python_debugger is not None:
79
+ process_stdin = self.main_window.exec_python_debugger.process.stdin
80
+ if process_stdin is not None:
81
+ # 將輸入框文字編碼後寫入子程序 stdin / Write encoded input to subprocess stdin
82
+ process_stdin.write(self.command_input.text().encode() + b"\n")
83
+ process_stdin.flush()
84
+
85
+ # === Shell 指令傳送 / Send command to shell ===
86
+ def shell_send_command(self):
87
+ jeditor_logger.info("EditorWidget shell_send_command")
88
+ if self.main_window.exec_shell is not None:
89
+ process_stdin = self.main_window.exec_shell.process.stdin
90
+ if process_stdin is not None:
91
+ process_stdin.write(self.command_input.text().encode() + b"\n")
92
+ process_stdin.flush()
93
+
94
+ # === Program 指令傳送 / Send command to program ===
95
+ def program_send_command(self):
96
+ jeditor_logger.info("EditorWidget program_send_command")
97
+ if self.main_window.exec_program is not None:
98
+ process_stdin = self.main_window.exec_program.process.stdin
99
+ if process_stdin is not None:
100
+ process_stdin.write(self.command_input.text().encode() + b"\n")
101
+ process_stdin.flush()
File without changes