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.
- je_editor/__init__.py +26 -21
- je_editor/__main__.py +1 -1
- je_editor/code_scan/ruff_thread.py +58 -0
- je_editor/code_scan/watchdog_implement.py +56 -0
- je_editor/code_scan/watchdog_thread.py +78 -0
- je_editor/git_client/commit_graph.py +77 -0
- je_editor/git_client/git_action.py +175 -0
- je_editor/git_client/git_cli.py +66 -0
- je_editor/pyside_ui/browser/browser_download_window.py +75 -0
- je_editor/pyside_ui/browser/browser_serach_lineedit.py +51 -0
- je_editor/pyside_ui/browser/browser_view.py +87 -0
- je_editor/pyside_ui/browser/browser_widget.py +103 -0
- je_editor/pyside_ui/browser/main_browser_widget.py +85 -0
- je_editor/pyside_ui/code/auto_save/auto_save_manager.py +60 -0
- je_editor/pyside_ui/code/auto_save/auto_save_thread.py +59 -0
- je_editor/pyside_ui/code/code_format/pep8_format.py +130 -0
- je_editor/pyside_ui/code/code_process/code_exec.py +267 -0
- je_editor/pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py +412 -0
- je_editor/pyside_ui/code/running_process_manager.py +48 -0
- je_editor/pyside_ui/code/shell_process/shell_exec.py +236 -0
- je_editor/pyside_ui/code/syntax/python_syntax.py +99 -0
- je_editor/pyside_ui/code/syntax/syntax_setting.py +95 -0
- je_editor/pyside_ui/code/textedit_code_result/code_record.py +75 -0
- je_editor/pyside_ui/code/variable_inspector/inspector_gui.py +172 -0
- je_editor/pyside_ui/dialog/ai_dialog/set_ai_dialog.py +71 -0
- je_editor/pyside_ui/dialog/file_dialog/create_file_dialog.py +68 -0
- je_editor/pyside_ui/dialog/file_dialog/open_file_dialog.py +111 -0
- je_editor/pyside_ui/dialog/file_dialog/save_file_dialog.py +67 -0
- je_editor/pyside_ui/dialog/search_ui/search_error_box.py +49 -0
- je_editor/pyside_ui/dialog/search_ui/search_text_box.py +49 -0
- je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +90 -0
- je_editor/pyside_ui/git_ui/code_diff_compare/line_number_code_viewer.py +141 -0
- je_editor/pyside_ui/git_ui/code_diff_compare/multi_file_diff_viewer.py +88 -0
- je_editor/pyside_ui/git_ui/code_diff_compare/side_by_side_diff_widget.py +284 -0
- je_editor/pyside_ui/git_ui/git_client/commit_table.py +65 -0
- je_editor/pyside_ui/git_ui/git_client/git_branch_tree_widget.py +156 -0
- je_editor/pyside_ui/git_ui/git_client/git_client_gui.py +799 -0
- je_editor/pyside_ui/git_ui/git_client/graph_view.py +218 -0
- je_editor/pyside_ui/main_ui/ai_widget/ai_config.py +34 -0
- je_editor/pyside_ui/main_ui/ai_widget/ask_thread.py +36 -0
- je_editor/pyside_ui/main_ui/ai_widget/chat_ui.py +147 -0
- je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +84 -0
- je_editor/pyside_ui/main_ui/console_widget/console_gui.py +162 -0
- je_editor/pyside_ui/main_ui/console_widget/qprocess_adapter.py +84 -0
- je_editor/pyside_ui/main_ui/dock/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/dock/destroy_dock.py +50 -0
- je_editor/pyside_ui/main_ui/editor/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/editor/editor_widget.py +301 -0
- je_editor/pyside_ui/main_ui/editor/editor_widget_dock.py +70 -0
- je_editor/pyside_ui/main_ui/editor/process_input.py +101 -0
- je_editor/pyside_ui/main_ui/ipython_widget/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/ipython_widget/rich_jupyter.py +78 -0
- je_editor/pyside_ui/main_ui/main_editor.py +369 -0
- je_editor/pyside_ui/main_ui/menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/check_style_menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +104 -0
- je_editor/pyside_ui/main_ui/menu/dock_menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +208 -0
- je_editor/pyside_ui/main_ui/menu/file_menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/file_menu/build_file_menu.py +186 -0
- je_editor/pyside_ui/main_ui/menu/help_menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +100 -0
- je_editor/pyside_ui/main_ui/menu/language_menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/language_menu/build_language_server.py +89 -0
- je_editor/pyside_ui/main_ui/menu/python_env_menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py +238 -0
- je_editor/pyside_ui/main_ui/menu/run_menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/run_menu/build_run_menu.py +160 -0
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +109 -0
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +101 -0
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +98 -0
- je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py +41 -0
- je_editor/pyside_ui/main_ui/menu/set_menu_bar.py +63 -0
- je_editor/pyside_ui/main_ui/menu/style_menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/style_menu/build_style_menu.py +73 -0
- je_editor/pyside_ui/main_ui/menu/tab_menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +275 -0
- je_editor/pyside_ui/main_ui/menu/text_menu/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/menu/text_menu/build_text_menu.py +135 -0
- je_editor/pyside_ui/main_ui/save_settings/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/save_settings/setting_utils.py +33 -0
- je_editor/pyside_ui/main_ui/save_settings/user_color_setting_file.py +103 -0
- je_editor/pyside_ui/main_ui/save_settings/user_setting_file.py +58 -0
- je_editor/pyside_ui/main_ui/system_tray/__init__.py +0 -0
- je_editor/pyside_ui/main_ui/system_tray/extend_system_tray.py +90 -0
- je_editor/start_editor.py +32 -8
- je_editor/utils/encodings/python_encodings.py +100 -97
- je_editor/utils/exception/exception_tags.py +11 -11
- je_editor/utils/file/open/open_file.py +38 -22
- je_editor/utils/file/save/save_file.py +40 -16
- je_editor/utils/json/json_file.py +36 -15
- je_editor/utils/json_format/json_process.py +38 -2
- je_editor/utils/logging/__init__.py +0 -0
- je_editor/utils/logging/loggin_instance.py +57 -0
- je_editor/utils/multi_language/__init__.py +0 -0
- je_editor/utils/multi_language/english.py +221 -0
- je_editor/utils/multi_language/multi_language_wrapper.py +54 -0
- je_editor/utils/multi_language/traditional_chinese.py +214 -0
- je_editor/utils/redirect_manager/redirect_manager_class.py +67 -25
- je_editor/utils/venv_check/__init__.py +0 -0
- je_editor/utils/venv_check/check_venv.py +51 -0
- je_editor-0.0.228.dist-info/METADATA +99 -0
- je_editor-0.0.228.dist-info/RECORD +140 -0
- {je_editor-0.0.104.dist-info → je_editor-0.0.228.dist-info}/WHEEL +1 -1
- {je_editor-0.0.104.dist-info → je_editor-0.0.228.dist-info/licenses}/LICENSE +1 -1
- je_editor/pyside_ui/auto_save/auto_save_thread.py +0 -34
- je_editor/pyside_ui/code_editor/code_edit_plaintext.py +0 -143
- je_editor/pyside_ui/code_process/code_exec.py +0 -190
- je_editor/pyside_ui/code_result/code_record.py +0 -39
- je_editor/pyside_ui/colors/global_color.py +0 -4
- je_editor/pyside_ui/file_dialog/open_file_dialog.py +0 -27
- je_editor/pyside_ui/file_dialog/save_file_dialog.py +0 -24
- je_editor/pyside_ui/main_ui/editor_main_ui/main_editor.py +0 -183
- je_editor/pyside_ui/main_ui_setting/ui_setting.py +0 -36
- je_editor/pyside_ui/menu/menu_bar/check_style_menu/build_check_style_menu.py +0 -44
- je_editor/pyside_ui/menu/menu_bar/file_menu/build_file_menu.py +0 -30
- je_editor/pyside_ui/menu/menu_bar/help_menu/build_help_menu.py +0 -39
- je_editor/pyside_ui/menu/menu_bar/run_menu/build_run_menu.py +0 -102
- je_editor/pyside_ui/menu/menu_bar/set_menu_bar.py +0 -24
- je_editor/pyside_ui/menu/menu_bar/venv_menu/build_venv_menu.py +0 -74
- je_editor/pyside_ui/search_ui/search_error_box.py +0 -20
- je_editor/pyside_ui/search_ui/search_text_box.py +0 -20
- je_editor/pyside_ui/shell_process/shell_exec.py +0 -157
- je_editor/pyside_ui/syntax/python_syntax.py +0 -99
- je_editor/pyside_ui/treeview/project_treeview/set_project_treeview.py +0 -47
- je_editor/pyside_ui/user_setting/user_setting_file.py +0 -23
- je_editor-0.0.104.dist-info/METADATA +0 -84
- je_editor-0.0.104.dist-info/RECORD +0 -69
- /je_editor/{pyside_ui/auto_save → code_scan}/__init__.py +0 -0
- /je_editor/{pyside_ui/code_editor → git_client}/__init__.py +0 -0
- /je_editor/pyside_ui/{code_process → browser}/__init__.py +0 -0
- /je_editor/pyside_ui/{code_result → code}/__init__.py +0 -0
- /je_editor/pyside_ui/{colors → code/auto_save}/__init__.py +0 -0
- /je_editor/pyside_ui/{file_dialog → code/code_format}/__init__.py +0 -0
- /je_editor/pyside_ui/{main_ui/editor_main_ui → code/code_process}/__init__.py +0 -0
- /je_editor/pyside_ui/{main_ui_setting → code/plaintext_code_edit}/__init__.py +0 -0
- /je_editor/pyside_ui/{menu → code/shell_process}/__init__.py +0 -0
- /je_editor/pyside_ui/{menu/menu_bar → code/syntax}/__init__.py +0 -0
- /je_editor/pyside_ui/{menu/menu_bar/check_style_menu → code/textedit_code_result}/__init__.py +0 -0
- /je_editor/pyside_ui/{menu/menu_bar/file_menu → code/variable_inspector}/__init__.py +0 -0
- /je_editor/pyside_ui/{menu/menu_bar/help_menu → dialog}/__init__.py +0 -0
- /je_editor/pyside_ui/{menu/menu_bar/run_menu → dialog/ai_dialog}/__init__.py +0 -0
- /je_editor/pyside_ui/{menu/menu_bar/venv_menu → dialog/file_dialog}/__init__.py +0 -0
- /je_editor/pyside_ui/{search_ui → dialog/search_ui}/__init__.py +0 -0
- /je_editor/pyside_ui/{shell_process → git_ui}/__init__.py +0 -0
- /je_editor/pyside_ui/{syntax → git_ui/code_diff_compare}/__init__.py +0 -0
- /je_editor/pyside_ui/{treeview → git_ui/git_client}/__init__.py +0 -0
- /je_editor/pyside_ui/{treeview/project_treeview → main_ui/ai_widget}/__init__.py +0 -0
- /je_editor/pyside_ui/{user_setting → main_ui/console_widget}/__init__.py +0 -0
- {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()
|