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,51 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from PySide6.QtCore import Qt
6
+ from PySide6.QtWidgets import QLineEdit
7
+
8
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
9
+
10
+ if TYPE_CHECKING:
11
+ # Forward declaration to avoid circular import at runtime
12
+ # 僅在型別檢查時匯入,避免執行時循環匯入問題
13
+ from je_editor.pyside_ui.browser.browser_widget import BrowserWidget
14
+
15
+
16
+ class BrowserLineSearch(QLineEdit):
17
+ """
18
+ A custom QLineEdit widget for browser search input.
19
+ 自訂的 QLineEdit,用於瀏覽器搜尋輸入。
20
+ """
21
+
22
+ def __init__(self, browser_widget: BrowserWidget):
23
+ """
24
+ Initialize the search line with a reference to the browser widget.
25
+ 初始化搜尋輸入框,並保存瀏覽器元件的參考。
26
+ """
27
+ super().__init__()
28
+ # 記錄初始化訊息到 logger
29
+ jeditor_logger.info("Init BrowserLineSearch "
30
+ f"browser_widget: {browser_widget}")
31
+
32
+ # 設定屬性:當視窗關閉時自動刪除
33
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
34
+
35
+ # 保存瀏覽器元件的參考,用於觸發搜尋
36
+ self.browser = browser_widget
37
+
38
+ def keyPressEvent(self, event) -> None:
39
+ """
40
+ Handle key press events.
41
+ 當使用者按下按鍵時觸發:
42
+ - 如果是 Enter 或 Return,則呼叫瀏覽器的 search() 方法。
43
+ - 其他情況則交由父類別處理。
44
+ """
45
+ if event.key() in [Qt.Key.Key_Enter, Qt.Key.Key_Return]:
46
+ jeditor_logger.info("Browser Search")
47
+ # 呼叫瀏覽器元件的搜尋方法
48
+ self.browser.search()
49
+
50
+ # 呼叫父類別的 keyPressEvent,確保其他按鍵行為正常
51
+ super().keyPressEvent(event)
@@ -0,0 +1,87 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import List, TYPE_CHECKING
4
+
5
+ from PySide6.QtCore import Signal
6
+
7
+ if TYPE_CHECKING:
8
+ from je_editor.pyside_ui.browser.browser_widget import BrowserWidget
9
+
10
+ from PySide6.QtWebEngineCore import QWebEngineDownloadRequest
11
+ from PySide6.QtWebEngineWidgets import QWebEngineView
12
+
13
+ from je_editor.pyside_ui.browser.browser_download_window import BrowserDownloadWindow
14
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
15
+
16
+
17
+ class BrowserView(QWebEngineView):
18
+ new_tab_requested = Signal(QWebEngineView)
19
+ """
20
+ A custom QWebEngineView that supports file downloads and manages download windows.
21
+ 自訂的 QWebEngineView,支援檔案下載並管理下載視窗。
22
+ """
23
+
24
+ def __init__(self, start_url: str = "https://www.google.com/",
25
+ main_widget: BrowserWidget = None, parent=None):
26
+ """
27
+ Initialize the browser view with a start URL.
28
+ 使用指定的起始網址初始化瀏覽器視圖。
29
+ """
30
+ super().__init__(parent)
31
+ # 記錄初始化訊息
32
+ jeditor_logger.info("Init BrowserView "
33
+ f"start_url: {start_url}")
34
+
35
+ # 設定初始網址
36
+ self.setUrl(start_url)
37
+
38
+ # 儲存下載請求的清單
39
+ self.download_list: List[QWebEngineDownloadRequest] = list()
40
+
41
+ # 儲存下載視窗的清單
42
+ self.download_window_list: List[BrowserDownloadWindow] = list()
43
+
44
+ # 綁定下載事件:當有下載請求時觸發 download_file
45
+ self.page().profile().downloadRequested.connect(self.download_file)
46
+
47
+ self.main_widget = main_widget
48
+
49
+ def download_file(self, download_instance: QWebEngineDownloadRequest):
50
+ """
51
+ Handle a new download request.
52
+ 當有新的下載請求時觸發:
53
+ - 將下載請求加入清單
54
+ - 建立並顯示下載細節視窗
55
+ """
56
+ jeditor_logger.info("Download File "
57
+ f"download_instance: {download_instance}")
58
+
59
+ # 加入下載請求到清單
60
+ self.download_list.append(download_instance)
61
+
62
+ # 建立下載細節視窗
63
+ download_detail_window = BrowserDownloadWindow(download_instance)
64
+
65
+ # 加入視窗到清單並顯示
66
+ self.download_window_list.append(download_detail_window)
67
+ download_detail_window.show()
68
+
69
+ def closeEvent(self, event) -> None:
70
+ """
71
+ Handle the close event of the browser view.
72
+ 當瀏覽器視窗關閉時:
73
+ - 取消所有進行中的下載
74
+ - 關閉所有下載細節視窗
75
+ """
76
+ jeditor_logger.info(f"BrowserView closeEvent event: {event}")
77
+
78
+ # 取消所有下載
79
+ for download_instance in self.download_list:
80
+ download_instance.cancel()
81
+
82
+ # 關閉所有下載視窗
83
+ for download_window in self.download_window_list:
84
+ download_window.close()
85
+
86
+ # 呼叫父類別的 closeEvent,確保正常關閉
87
+ super().closeEvent(event)
@@ -0,0 +1,103 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from je_editor.pyside_ui.browser.main_browser_widget import MainBrowserWidget
7
+
8
+ from PySide6.QtCore import Qt
9
+ from PySide6.QtGui import QAction
10
+ from PySide6.QtWidgets import QWidget, QPushButton, QGridLayout, QInputDialog
11
+
12
+ from je_editor.pyside_ui.browser.browser_serach_lineedit import BrowserLineSearch
13
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
14
+ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
15
+
16
+ from je_editor.pyside_ui.browser.browser_view import BrowserView
17
+
18
+
19
+ class BrowserWidget(QWidget):
20
+ def __init__(self, start_url: str = "https://www.google.com/",
21
+ search_prefix: str = "https://www.google.com.tw/search?q=",
22
+ main_widget: MainBrowserWidget = None, browser_view: BrowserView = None):
23
+ # --- Browser setting / 瀏覽器設定 ---
24
+ super().__init__()
25
+ self.main_widget = main_widget
26
+ if browser_view:
27
+ self.browser = browser_view
28
+ else:
29
+ # 建立內嵌的瀏覽器視圖
30
+ # Create embedded browser view
31
+ self.browser = BrowserView(start_url, main_widget=main_widget)
32
+ self.search_prefix = search_prefix # 搜尋引擎前綴字串 / Search engine prefix
33
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose) # 視窗關閉時釋放資源 / Free resources on close
34
+
35
+ # --- Top bar buttons / 上方工具列按鈕 ---
36
+ # 返回上一頁 / Back button
37
+ self.back_button = QPushButton(language_wrapper.language_word_dict.get("browser_back_button"))
38
+ self.back_button.clicked.connect(self.browser.back)
39
+
40
+ # 前往下一頁 / Forward button
41
+ self.forward_button = QPushButton(language_wrapper.language_word_dict.get("browser_forward_button"))
42
+ self.forward_button.clicked.connect(self.browser.forward)
43
+
44
+ # 重新整理 / Reload button
45
+ self.reload_button = QPushButton(language_wrapper.language_word_dict.get("browser_reload_button"))
46
+ self.reload_button.clicked.connect(self.browser.reload)
47
+
48
+ # 搜尋按鈕 / Search button
49
+ self.search_button = QPushButton(language_wrapper.language_word_dict.get("browser_search_button"))
50
+ self.search_button.clicked.connect(self.search)
51
+
52
+ # URL / Search input line (自訂義 QLineEdit)
53
+ # Custom QLineEdit for URL or search input
54
+ self.url_input = BrowserLineSearch(self)
55
+
56
+ # --- Action: Ctrl+F to find text / 快捷鍵 Ctrl+F 搜尋文字 ---
57
+ self.find_action = QAction()
58
+ self.find_action.setShortcut("Ctrl+f") # 設定快捷鍵 / Set shortcut
59
+ self.find_action.triggered.connect(self.find_text) # 綁定搜尋文字功能 / Connect to find_text
60
+ self.addAction(self.find_action)
61
+
62
+ # --- Layout / 版面配置 ---
63
+ self.grid_layout = QGridLayout()
64
+ self.grid_layout.addWidget(self.back_button, 0, 0)
65
+ self.grid_layout.addWidget(self.forward_button, 0, 1)
66
+ self.grid_layout.addWidget(self.reload_button, 0, 2)
67
+ self.grid_layout.addWidget(self.url_input, 0, 3)
68
+ self.grid_layout.addWidget(self.search_button, 0, 4)
69
+ # 瀏覽器視圖佔滿下方所有空間
70
+ # Browser view fills the lower area
71
+ self.grid_layout.addWidget(self.browser, 1, 0, -1, -1)
72
+ self.setLayout(self.grid_layout)
73
+
74
+ def search(self):
75
+ """
76
+ Perform a search using the text in the input line.
77
+ 使用輸入框的文字進行搜尋,將字串附加到 search_prefix 後送出。
78
+ """
79
+ jeditor_logger.info("BrowserWidget Search")
80
+ # 將輸入框文字拼接到搜尋前綴,並設定為瀏覽器 URL
81
+ # Append input text to search prefix and set as browser URL
82
+ self.browser.setUrl(f"{self.search_prefix}{self.url_input.text()}")
83
+
84
+ def find_text(self):
85
+ """
86
+ Open a dialog to find text in the current page.
87
+ 開啟輸入對話框,在當前頁面中搜尋文字。
88
+ - 如果按下 OK,搜尋輸入的文字
89
+ - 如果取消,清除搜尋
90
+ """
91
+ jeditor_logger.info("BrowserWidget Find Text")
92
+ search_box = QInputDialog(self)
93
+ search_text, press_ok = search_box.getText(
94
+ self,
95
+ language_wrapper.language_word_dict.get("browser_find_text"),
96
+ language_wrapper.language_word_dict.get("browser_find_text_input")
97
+ )
98
+ if press_ok:
99
+ # 在頁面中搜尋輸入文字 / Search entered text in page
100
+ self.browser.findText(search_text)
101
+ else:
102
+ # 清除搜尋結果 / Clear search
103
+ self.browser.findText("")
@@ -0,0 +1,85 @@
1
+ from PySide6.QtWidgets import QWidget, QGridLayout, QTabWidget, QTabBar
2
+
3
+ from je_editor.pyside_ui.browser.browser_widget import BrowserWidget
4
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
5
+
6
+
7
+ class MainBrowserWidget(QWidget):
8
+ """
9
+ 瀏覽器元件:包含分頁,並在最右方固定一個「+」分頁。
10
+ Browser component: includes tabs, with a fixed "+" tab at the far right.
11
+ """
12
+
13
+ def __init__(self, start_url: str = "https://www.google.com/",
14
+ search_prefix: str = "https://www.google.com.tw/search?q="):
15
+ super().__init__()
16
+ # 初始化時記錄訊息 (方便除錯)
17
+ # Log initialization info (for debugging)
18
+ jeditor_logger.info("Init BrowserWidget "
19
+ f"start_url: {start_url} "
20
+ f"search_prefix: {search_prefix}")
21
+
22
+ grid_layout = QGridLayout() # 建立網格佈局 / Create grid layout
23
+
24
+ self.browser_tab = QTabWidget() # 建立分頁容器 / Create tab widget
25
+ self.browser_tab.setTabsClosable(True) # 分頁可關閉 / Tabs can be closed
26
+ self.browser_tab.tabCloseRequested.connect(self.close_tab) # 綁定關閉事件 / Connect close event
27
+
28
+ self.search_prefix = search_prefix # 搜尋前綴字串 / Search prefix string
29
+
30
+ # 預設第一個分頁 (Google)
31
+ # Default first tab (Google)
32
+ self.add_browser_tab(
33
+ BrowserWidget(start_url=start_url, search_prefix=search_prefix), "Google"
34
+ )
35
+
36
+ # 固定一個「+」分頁
37
+ # Add a fixed "+" tab
38
+ self.add_plus_tab()
39
+
40
+ grid_layout.addWidget(self.browser_tab) # 把分頁加入佈局 / Add tab widget to layout
41
+ self.setLayout(grid_layout) # 設定主視窗佈局 / Set main layout
42
+
43
+ def add_browser_tab(self, browser_widget: BrowserWidget, title: str = "New Tab"):
44
+ # 在「+」分頁前插入新分頁
45
+ # Insert new tab before the "+" tab
46
+ plus_index = self.browser_tab.count() - 1
47
+ index = self.browser_tab.insertTab(plus_index, browser_widget, title)
48
+ self.browser_tab.setCurrentIndex(index) # 自動切換到新分頁 / Switch to new tab
49
+
50
+ # 更新分頁標題 (當網頁標題改變時)
51
+ # Update tab title when browser title changes
52
+ browser_widget.browser.titleChanged.connect(
53
+ lambda t, i=index: self.browser_tab.setTabText(i, t)
54
+ )
55
+
56
+ def add_plus_tab(self):
57
+ """新增一個固定的「+」分頁 / Add a fixed "+" tab"""
58
+ add_new_page_widget = QWidget()
59
+ self.browser_tab.addTab(add_new_page_widget, "+")
60
+ # 禁止關閉「+」分頁 / Disable closing of "+" tab
61
+ self.browser_tab.tabBar().setTabButton(self.browser_tab.count() - 1,
62
+ QTabBar.ButtonPosition.RightSide, None)
63
+ # 點擊分頁時觸發事件 / Connect tab click event
64
+ self.browser_tab.tabBar().tabBarClicked.connect(self.handle_tab_changed)
65
+
66
+ def handle_tab_changed(self, index: int):
67
+ """如果點到「+」分頁,就開新分頁
68
+ If "+" tab is clicked, open a new tab
69
+ """
70
+ if self.browser_tab.tabText(index) == "+": # 檢查是否為「+」分頁 / Check if "+" tab
71
+ # 建立新分頁(會自動切換到新分頁)
72
+ # Create a new tab (auto switch to it)
73
+ self.add_browser_tab(
74
+ BrowserWidget(start_url="https://www.google.com/",
75
+ search_prefix=self.search_prefix),
76
+ "Google"
77
+ )
78
+
79
+ def close_tab(self, index: int):
80
+ """關閉指定分頁,但保留「+」
81
+ Close the specified tab, but keep the "+"
82
+ """
83
+ widget = self.browser_tab.widget(index) # 取得要關閉的分頁元件 / Get tab widget
84
+ self.browser_tab.removeTab(index) # 移除分頁 / Remove tab
85
+ widget.deleteLater() # 釋放資源 / Free memory
@@ -0,0 +1,60 @@
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
+ # Import only for type checking, prevents circular imports
10
+ from je_editor.pyside_ui.main_ui.main_editor import EditorWidget
11
+
12
+ from je_editor.pyside_ui.code.auto_save.auto_save_thread import CodeEditSaveThread
13
+
14
+ # --- 全域管理字典 / Global manager dictionaries ---
15
+
16
+ # 管理每個檔案對應的自動儲存執行緒
17
+ # Manage auto-save threads for each file
18
+ auto_save_manager_dict: dict = dict()
19
+
20
+ # 管理檔案是否已經開啟
21
+ # Track whether a file is currently open
22
+ file_is_open_manager_dict: dict = dict()
23
+
24
+
25
+ def init_new_auto_save_thread(file_path: str, widget: EditorWidget):
26
+ """
27
+ 初始化新的自動儲存執行緒
28
+ Initialize a new auto-save thread for the given file and editor widget.
29
+
30
+ :param file_path: 檔案路徑 / Path of the file to be auto-saved
31
+ :param widget: 編輯器元件 / Editor widget instance
32
+ """
33
+ # 記錄初始化訊息 (方便除錯)
34
+ # Log initialization info (for debugging)
35
+ jeditor_logger.info(f"auto_save_manager.py init_new_auto_save_thread "
36
+ f"file_path: {file_path} "
37
+ f"widget: {widget}")
38
+
39
+ # 將目前編輯器綁定的檔案設為 file_path
40
+ # Set the current file of the editor widget
41
+ widget.current_file = file_path
42
+
43
+ # 如果該檔案尚未有自動儲存執行緒
44
+ # If no auto-save thread exists for this file
45
+ if auto_save_manager_dict.get(file_path, None) is None:
46
+ # 建立新的自動儲存執行緒,綁定到編輯器
47
+ # Create a new auto-save thread for this file
48
+ widget.code_save_thread = CodeEditSaveThread(
49
+ file_to_save=widget.current_file, editor=widget.code_edit
50
+ )
51
+
52
+ # 更新管理字典,記錄該檔案對應的執行緒
53
+ # Update manager dict with the new thread
54
+ auto_save_manager_dict.update({
55
+ file_path: widget.code_save_thread
56
+ })
57
+
58
+ # 啟動自動儲存執行緒
59
+ # Start the auto-save thread
60
+ widget.code_save_thread.start()
@@ -0,0 +1,59 @@
1
+ import time
2
+ from pathlib import Path
3
+ from threading import Thread
4
+ from typing import Union
5
+
6
+ from je_editor.pyside_ui.code.plaintext_code_edit.code_edit_plaintext import CodeEditor
7
+ from je_editor.utils.file.save.save_file import write_file
8
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
9
+
10
+
11
+ class CodeEditSaveThread(Thread):
12
+
13
+ def __init__(
14
+ self, file_to_save: Union[str, None] = None, editor: Union[None, CodeEditor] = None):
15
+ """
16
+ This thread is used to auto save current file.
17
+ 這個執行緒用來自動儲存當前檔案。
18
+
19
+ :param file_to_save: file we want to auto save
20
+ 要自動儲存的檔案路徑
21
+ :param editor: code editor to auto save
22
+ 要自動儲存內容的編輯器元件
23
+ """
24
+ jeditor_logger.info(f"Init CodeEditSaveThread "
25
+ f"file_to_save: {file_to_save} "
26
+ f"editor: {editor}")
27
+ super().__init__()
28
+ self.file: str = file_to_save
29
+ self.editor: Union[None, CodeEditor] = editor
30
+ self.still_run: bool = True # 控制執行緒是否繼續運行 / Flag to control thread loop
31
+ self.daemon = True # 設定為守護執行緒,主程式結束時自動結束
32
+ # Set as daemon thread, ends with main program
33
+ self.skip_this_round: bool = False # 是否跳過本次儲存 / Skip this save cycle
34
+
35
+ def run(self) -> None:
36
+ """
37
+ loop and save current edit file
38
+ 持續迴圈,每隔一段時間自動儲存當前編輯檔案
39
+ """
40
+ jeditor_logger.info("CodeEditSaveThread run")
41
+ if self.file is not None:
42
+ path = Path(self.file)
43
+ # 當檔案存在且編輯器不為 None 時持續運行
44
+ # Keep running while file exists and editor is valid
45
+ while path.is_file() and self.editor is not None:
46
+ time.sleep(5) # 每 5 秒檢查一次 / Check every 5 seconds
47
+ if self.still_run:
48
+ if self.skip_this_round:
49
+ # 如果設定跳過本輪,什麼都不做
50
+ # Skip this round if flag is set
51
+ pass
52
+ else:
53
+ # 將編輯器內容寫入檔案
54
+ # Write editor content to file
55
+ write_file(self.file, self.editor.toPlainText())
56
+ else:
57
+ # 如果 still_run 為 False,結束迴圈
58
+ # Exit loop if still_run is False
59
+ break
@@ -0,0 +1,130 @@
1
+ import tokenize
2
+
3
+ import pycodestyle
4
+
5
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
6
+
7
+
8
+ class PEP8FormatChecker(pycodestyle.Checker):
9
+ def __init__(self, filename: str, **kwargs):
10
+ """
11
+ 自訂的 PEP8 格式檢查器,繼承自 pycodestyle.Checker。
12
+ Custom PEP8 format checker, inherits from pycodestyle.Checker.
13
+ """
14
+ jeditor_logger.info(f"Init PEP8FormatChecker "
15
+ f"filename: {filename} "
16
+ f"kwargs: {kwargs}")
17
+ super().__init__(filename, **kwargs)
18
+
19
+ # 初始化變數 / Initialize variables
20
+ self.physical_line = None
21
+ self.blank_before = None
22
+ self.blank_lines = None
23
+ self.tokens = None
24
+ self.previous_unindented_logical_line = None
25
+ self.previous_logical = None
26
+ self.indent_level = None
27
+ self.previous_indent_level = None
28
+ self.line_number = None
29
+ self.indent_char = None
30
+ self.total_lines = None
31
+
32
+ # 定義換行符號集合 / Define newline token set
33
+ self.new_line = frozenset([tokenize.NL, tokenize.NEWLINE])
34
+
35
+ # 將 report_error 替換為自訂方法 / Override report_error with custom method
36
+ self.report_error = self.replace_report_error
37
+
38
+ # 當前檔案名稱 / Current file name
39
+ self.current_file: str = filename
40
+
41
+ # 儲存錯誤訊息的清單 / List to store error messages
42
+ self.error_list: list = list()
43
+
44
+ def replace_report_error(self, line_number, offset, text, check):
45
+ """
46
+ 自訂錯誤回報方法,過濾掉特定錯誤 (例如 W191)。
47
+ Custom error reporting method, filters out specific errors (e.g., W191).
48
+ """
49
+ jeditor_logger.info(f"PEP8FormatChecker replace_report_error "
50
+ f"line_number: {line_number} "
51
+ f"offset: {offset} "
52
+ f"text: {text}")
53
+ # 忽略 W191 (縮排使用 Tab 的警告)
54
+ # Ignore W191 (indentation contains tabs)
55
+ if not text.startswith("W191"):
56
+ self.error_list.append(f"{text} on line: {line_number}, offset: {offset}")
57
+
58
+ def check_all_format(self, expected=None, line_offset=0) -> int:
59
+ """
60
+ 執行所有格式檢查。
61
+ Run all checks on the input file.
62
+ """
63
+ jeditor_logger.info(f"PEP8FormatChecker check_all_format "
64
+ f"expected: {expected} "
65
+ f"line_offset: {line_offset}")
66
+
67
+ # 初始化檔案檢查 / Initialize file check
68
+ self.report.init_file(self.filename, self.lines, expected, line_offset)
69
+ self.total_lines = len(self.lines)
70
+
71
+ # 如果有 AST 檢查,先執行 / Run AST checks if available
72
+ if self._ast_checks:
73
+ self.check_ast()
74
+
75
+ # 重設狀態變數 / Reset state variables
76
+ self.line_number = 0
77
+ self.indent_char = None
78
+ self.indent_level = self.previous_indent_level = 0
79
+ self.previous_logical = ''
80
+ self.previous_unindented_logical_line = ''
81
+ self.tokens = []
82
+ self.blank_lines = self.blank_before = 0
83
+ parens = 0 # 括號層級計數器 / Parentheses nesting counter
84
+
85
+ # 逐一處理 Token / Process tokens one by one
86
+ for token in self.generate_tokens():
87
+ self.tokens.append(token)
88
+ token_type, text = token[0:2]
89
+
90
+ # 如果 verbose >= 3,輸出詳細 Token 資訊
91
+ # If verbose >= 3, log detailed token info
92
+ if self.verbose >= 3:
93
+ if token[2][0] == token[3][0]:
94
+ pos = '[{}:{}]'.format(token[2][1] or '', token[3][1])
95
+ else:
96
+ pos = 'l.%s' % token[3][0]
97
+ self.replace_report_error(token[2][0], pos, tokenize.tok_name[token[0]], text)
98
+
99
+ # 檢查括號層級 / Track parentheses nesting
100
+ if token_type == tokenize.OP:
101
+ if text in '([{':
102
+ parens += 1
103
+ elif text in '}])':
104
+ parens -= 1
105
+ # 當不在括號內時,檢查換行 / When not inside parentheses, check newlines
106
+ elif not parens:
107
+ if token_type in self.new_line:
108
+ if token_type == tokenize.NEWLINE:
109
+ # 完整邏輯行結束,檢查邏輯行
110
+ # End of logical line, check it
111
+ self.check_logical()
112
+ self.blank_before = 0
113
+ elif len(self.tokens) == 1:
114
+ # 只有換行符號,代表空行
115
+ # Line contains only newline, count as blank line
116
+ self.blank_lines += 1
117
+ del self.tokens[0]
118
+ else:
119
+ # 其他情況也檢查邏輯行
120
+ # Otherwise, check logical line
121
+ self.check_logical()
122
+
123
+ # 如果還有剩餘 Token,檢查最後一行
124
+ # If tokens remain, check the last line
125
+ if self.tokens:
126
+ self.check_physical(self.lines[-1]) # 檢查物理行 / Check physical line
127
+ self.check_logical() # 檢查邏輯行 / Check logical line
128
+
129
+ # 回傳檔案檢查結果 / Return file check results
130
+ return self.report.get_file_results()