je-editor 0.0.202__py3-none-any.whl → 0.0.228__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. je_editor/__init__.py +2 -2
  2. je_editor/code_scan/__init__.py +0 -0
  3. je_editor/code_scan/ruff_thread.py +58 -0
  4. je_editor/code_scan/watchdog_implement.py +56 -0
  5. je_editor/code_scan/watchdog_thread.py +78 -0
  6. je_editor/git_client/__init__.py +0 -0
  7. je_editor/git_client/commit_graph.py +77 -0
  8. je_editor/git_client/git_action.py +175 -0
  9. je_editor/git_client/git_cli.py +66 -0
  10. je_editor/pyside_ui/browser/browser_download_window.py +40 -4
  11. je_editor/pyside_ui/browser/browser_serach_lineedit.py +24 -0
  12. je_editor/pyside_ui/browser/browser_view.py +55 -6
  13. je_editor/pyside_ui/browser/browser_widget.py +62 -19
  14. je_editor/pyside_ui/browser/main_browser_widget.py +85 -0
  15. je_editor/pyside_ui/code/auto_save/auto_save_manager.py +33 -1
  16. je_editor/pyside_ui/code/auto_save/auto_save_thread.py +18 -5
  17. je_editor/pyside_ui/code/code_format/pep8_format.py +52 -7
  18. je_editor/pyside_ui/code/code_process/code_exec.py +118 -64
  19. je_editor/pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py +115 -53
  20. je_editor/pyside_ui/code/running_process_manager.py +18 -0
  21. je_editor/pyside_ui/code/shell_process/shell_exec.py +99 -60
  22. je_editor/pyside_ui/code/syntax/python_syntax.py +44 -9
  23. je_editor/pyside_ui/code/syntax/syntax_setting.py +39 -11
  24. je_editor/pyside_ui/code/textedit_code_result/code_record.py +33 -11
  25. je_editor/pyside_ui/code/variable_inspector/__init__.py +0 -0
  26. je_editor/pyside_ui/code/variable_inspector/inspector_gui.py +172 -0
  27. je_editor/pyside_ui/dialog/ai_dialog/__init__.py +0 -0
  28. je_editor/pyside_ui/dialog/ai_dialog/set_ai_dialog.py +71 -0
  29. je_editor/pyside_ui/dialog/file_dialog/create_file_dialog.py +38 -4
  30. je_editor/pyside_ui/dialog/file_dialog/open_file_dialog.py +32 -4
  31. je_editor/pyside_ui/dialog/file_dialog/save_file_dialog.py +25 -2
  32. je_editor/pyside_ui/dialog/search_ui/search_error_box.py +27 -2
  33. je_editor/pyside_ui/dialog/search_ui/search_text_box.py +28 -3
  34. je_editor/pyside_ui/git_ui/__init__.py +0 -0
  35. je_editor/pyside_ui/git_ui/code_diff_compare/__init__.py +0 -0
  36. je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +90 -0
  37. je_editor/pyside_ui/git_ui/code_diff_compare/line_number_code_viewer.py +141 -0
  38. je_editor/pyside_ui/git_ui/code_diff_compare/multi_file_diff_viewer.py +88 -0
  39. je_editor/pyside_ui/git_ui/code_diff_compare/side_by_side_diff_widget.py +284 -0
  40. je_editor/pyside_ui/git_ui/git_client/__init__.py +0 -0
  41. je_editor/pyside_ui/git_ui/git_client/commit_table.py +65 -0
  42. je_editor/pyside_ui/git_ui/git_client/git_branch_tree_widget.py +156 -0
  43. je_editor/pyside_ui/git_ui/git_client/git_client_gui.py +799 -0
  44. je_editor/pyside_ui/git_ui/git_client/graph_view.py +218 -0
  45. je_editor/pyside_ui/main_ui/ai_widget/__init__.py +0 -0
  46. je_editor/pyside_ui/main_ui/ai_widget/ai_config.py +34 -0
  47. je_editor/pyside_ui/main_ui/ai_widget/ask_thread.py +36 -0
  48. je_editor/pyside_ui/main_ui/ai_widget/chat_ui.py +147 -0
  49. je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +84 -0
  50. je_editor/pyside_ui/main_ui/console_widget/__init__.py +0 -0
  51. je_editor/pyside_ui/main_ui/console_widget/console_gui.py +162 -0
  52. je_editor/pyside_ui/main_ui/console_widget/qprocess_adapter.py +84 -0
  53. je_editor/pyside_ui/main_ui/dock/destroy_dock.py +32 -1
  54. je_editor/pyside_ui/main_ui/editor/editor_widget.py +113 -23
  55. je_editor/pyside_ui/main_ui/editor/editor_widget_dock.py +33 -6
  56. je_editor/pyside_ui/main_ui/editor/process_input.py +42 -10
  57. je_editor/pyside_ui/main_ui/ipython_widget/rich_jupyter.py +45 -10
  58. je_editor/pyside_ui/main_ui/main_editor.py +189 -49
  59. je_editor/pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +50 -27
  60. je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +141 -30
  61. je_editor/pyside_ui/main_ui/menu/file_menu/build_file_menu.py +67 -17
  62. je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +33 -3
  63. je_editor/pyside_ui/main_ui/menu/language_menu/build_language_server.py +39 -0
  64. je_editor/pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py +102 -40
  65. je_editor/pyside_ui/main_ui/menu/run_menu/build_run_menu.py +53 -6
  66. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +47 -3
  67. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +45 -5
  68. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +41 -3
  69. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py +21 -0
  70. je_editor/pyside_ui/main_ui/menu/set_menu_bar.py +37 -11
  71. je_editor/pyside_ui/main_ui/menu/style_menu/build_style_menu.py +42 -6
  72. je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +202 -27
  73. je_editor/pyside_ui/main_ui/menu/text_menu/build_text_menu.py +66 -0
  74. je_editor/pyside_ui/main_ui/save_settings/setting_utils.py +18 -1
  75. je_editor/pyside_ui/main_ui/save_settings/user_color_setting_file.py +32 -2
  76. je_editor/pyside_ui/main_ui/save_settings/user_setting_file.py +37 -10
  77. je_editor/pyside_ui/main_ui/system_tray/extend_system_tray.py +39 -0
  78. je_editor/start_editor.py +25 -0
  79. je_editor/utils/encodings/python_encodings.py +100 -97
  80. je_editor/utils/exception/exception_tags.py +11 -11
  81. je_editor/utils/file/open/open_file.py +35 -19
  82. je_editor/utils/file/save/save_file.py +34 -13
  83. je_editor/utils/json/json_file.py +29 -14
  84. je_editor/utils/json_format/json_process.py +32 -1
  85. je_editor/utils/logging/loggin_instance.py +37 -7
  86. je_editor/utils/multi_language/english.py +102 -6
  87. je_editor/utils/multi_language/multi_language_wrapper.py +28 -3
  88. je_editor/utils/multi_language/traditional_chinese.py +101 -10
  89. je_editor/utils/redirect_manager/redirect_manager_class.py +49 -11
  90. je_editor/utils/venv_check/check_venv.py +37 -14
  91. {je_editor-0.0.202.dist-info → je_editor-0.0.228.dist-info}/METADATA +12 -6
  92. je_editor-0.0.228.dist-info/RECORD +140 -0
  93. {je_editor-0.0.202.dist-info → je_editor-0.0.228.dist-info}/WHEEL +1 -1
  94. je_editor-0.0.202.dist-info/RECORD +0 -108
  95. {je_editor-0.0.202.dist-info → je_editor-0.0.228.dist-info/licenses}/LICENSE +0 -0
  96. {je_editor-0.0.202.dist-info → je_editor-0.0.228.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,12 @@
1
- import pathlib
2
- from typing import List
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
3
9
 
4
- from PySide6.QtCore import Qt
5
- from PySide6.QtNetwork import QNetworkCookie
6
10
  from PySide6.QtWebEngineCore import QWebEngineDownloadRequest
7
11
  from PySide6.QtWebEngineWidgets import QWebEngineView
8
12
 
@@ -11,28 +15,73 @@ from je_editor.utils.logging.loggin_instance import jeditor_logger
11
15
 
12
16
 
13
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
+ """
14
23
 
15
- def __init__(self, start_url: str = "https://www.google.com/"):
16
- super().__init__()
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
+ # 記錄初始化訊息
17
32
  jeditor_logger.info("Init BrowserView "
18
33
  f"start_url: {start_url}")
34
+
35
+ # 設定初始網址
19
36
  self.setUrl(start_url)
37
+
38
+ # 儲存下載請求的清單
20
39
  self.download_list: List[QWebEngineDownloadRequest] = list()
40
+
41
+ # 儲存下載視窗的清單
21
42
  self.download_window_list: List[BrowserDownloadWindow] = list()
43
+
44
+ # 綁定下載事件:當有下載請求時觸發 download_file
22
45
  self.page().profile().downloadRequested.connect(self.download_file)
23
46
 
47
+ self.main_widget = main_widget
48
+
24
49
  def download_file(self, download_instance: QWebEngineDownloadRequest):
50
+ """
51
+ Handle a new download request.
52
+ 當有新的下載請求時觸發:
53
+ - 將下載請求加入清單
54
+ - 建立並顯示下載細節視窗
55
+ """
25
56
  jeditor_logger.info("Download File "
26
57
  f"download_instance: {download_instance}")
58
+
59
+ # 加入下載請求到清單
27
60
  self.download_list.append(download_instance)
61
+
62
+ # 建立下載細節視窗
28
63
  download_detail_window = BrowserDownloadWindow(download_instance)
64
+
65
+ # 加入視窗到清單並顯示
29
66
  self.download_window_list.append(download_detail_window)
30
67
  download_detail_window.show()
31
68
 
32
69
  def closeEvent(self, event) -> None:
70
+ """
71
+ Handle the close event of the browser view.
72
+ 當瀏覽器視窗關閉時:
73
+ - 取消所有進行中的下載
74
+ - 關閉所有下載細節視窗
75
+ """
33
76
  jeditor_logger.info(f"BrowserView closeEvent event: {event}")
77
+
78
+ # 取消所有下載
34
79
  for download_instance in self.download_list:
35
80
  download_instance.cancel()
81
+
82
+ # 關閉所有下載視窗
36
83
  for download_window in self.download_window_list:
37
84
  download_window.close()
85
+
86
+ # 呼叫父類別的 closeEvent,確保正常關閉
38
87
  super().closeEvent(event)
@@ -1,60 +1,103 @@
1
- from PySide6.QtGui import QAction, Qt
2
- from PySide6.QtWidgets import QWidget, QGridLayout, QPushButton, QInputDialog
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
3
11
 
4
12
  from je_editor.pyside_ui.browser.browser_serach_lineedit import BrowserLineSearch
5
- from je_editor.pyside_ui.browser.browser_view import BrowserView
6
13
  from je_editor.utils.logging.loggin_instance import jeditor_logger
7
14
  from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
8
15
 
16
+ from je_editor.pyside_ui.browser.browser_view import BrowserView
9
17
 
10
- class BrowserWidget(QWidget):
11
18
 
19
+ class BrowserWidget(QWidget):
12
20
  def __init__(self, start_url: str = "https://www.google.com/",
13
- search_prefix: str = "https://www.google.com.tw/search?q="):
21
+ search_prefix: str = "https://www.google.com.tw/search?q=",
22
+ main_widget: MainBrowserWidget = None, browser_view: BrowserView = None):
23
+ # --- Browser setting / 瀏覽器設定 ---
14
24
  super().__init__()
15
- jeditor_logger.info("Init BrowserWidget "
16
- f"start_url: {start_url} "
17
- f"search_prefix: {search_prefix}")
18
- # Browser setting
19
- self.browser = BrowserView(start_url)
20
- self.search_prefix = search_prefix
21
- self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
22
- # Top bar
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
23
37
  self.back_button = QPushButton(language_wrapper.language_word_dict.get("browser_back_button"))
24
38
  self.back_button.clicked.connect(self.browser.back)
39
+
40
+ # 前往下一頁 / Forward button
25
41
  self.forward_button = QPushButton(language_wrapper.language_word_dict.get("browser_forward_button"))
26
42
  self.forward_button.clicked.connect(self.browser.forward)
43
+
44
+ # 重新整理 / Reload button
27
45
  self.reload_button = QPushButton(language_wrapper.language_word_dict.get("browser_reload_button"))
28
46
  self.reload_button.clicked.connect(self.browser.reload)
47
+
48
+ # 搜尋按鈕 / Search button
29
49
  self.search_button = QPushButton(language_wrapper.language_word_dict.get("browser_search_button"))
30
50
  self.search_button.clicked.connect(self.search)
51
+
52
+ # URL / Search input line (自訂義 QLineEdit)
53
+ # Custom QLineEdit for URL or search input
31
54
  self.url_input = BrowserLineSearch(self)
32
- # Action
55
+
56
+ # --- Action: Ctrl+F to find text / 快捷鍵 Ctrl+F 搜尋文字 ---
33
57
  self.find_action = QAction()
34
- self.find_action.setShortcut("Ctrl+f")
35
- self.find_action.triggered.connect(self.find_text)
58
+ self.find_action.setShortcut("Ctrl+f") # 設定快捷鍵 / Set shortcut
59
+ self.find_action.triggered.connect(self.find_text) # 綁定搜尋文字功能 / Connect to find_text
36
60
  self.addAction(self.find_action)
37
- # Layout
61
+
62
+ # --- Layout / 版面配置 ---
38
63
  self.grid_layout = QGridLayout()
39
64
  self.grid_layout.addWidget(self.back_button, 0, 0)
40
65
  self.grid_layout.addWidget(self.forward_button, 0, 1)
41
66
  self.grid_layout.addWidget(self.reload_button, 0, 2)
42
67
  self.grid_layout.addWidget(self.url_input, 0, 3)
43
68
  self.grid_layout.addWidget(self.search_button, 0, 4)
69
+ # 瀏覽器視圖佔滿下方所有空間
70
+ # Browser view fills the lower area
44
71
  self.grid_layout.addWidget(self.browser, 1, 0, -1, -1)
45
72
  self.setLayout(self.grid_layout)
46
73
 
47
74
  def search(self):
75
+ """
76
+ Perform a search using the text in the input line.
77
+ 使用輸入框的文字進行搜尋,將字串附加到 search_prefix 後送出。
78
+ """
48
79
  jeditor_logger.info("BrowserWidget Search")
80
+ # 將輸入框文字拼接到搜尋前綴,並設定為瀏覽器 URL
81
+ # Append input text to search prefix and set as browser URL
49
82
  self.browser.setUrl(f"{self.search_prefix}{self.url_input.text()}")
50
83
 
51
84
  def find_text(self):
85
+ """
86
+ Open a dialog to find text in the current page.
87
+ 開啟輸入對話框,在當前頁面中搜尋文字。
88
+ - 如果按下 OK,搜尋輸入的文字
89
+ - 如果取消,清除搜尋
90
+ """
52
91
  jeditor_logger.info("BrowserWidget Find Text")
53
92
  search_box = QInputDialog(self)
54
93
  search_text, press_ok = search_box.getText(
55
- self, language_wrapper.language_word_dict.get("browser_find_text"),
56
- language_wrapper.language_word_dict.get("browser_find_text_input"))
94
+ self,
95
+ language_wrapper.language_word_dict.get("browser_find_text"),
96
+ language_wrapper.language_word_dict.get("browser_find_text_input")
97
+ )
57
98
  if press_ok:
99
+ # 在頁面中搜尋輸入文字 / Search entered text in page
58
100
  self.browser.findText(search_text)
59
101
  else:
102
+ # 清除搜尋結果 / Clear search
60
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
@@ -5,24 +5,56 @@ from typing import TYPE_CHECKING
5
5
  from je_editor.utils.logging.loggin_instance import jeditor_logger
6
6
 
7
7
  if TYPE_CHECKING:
8
+ # 僅在型別檢查時匯入,避免循環匯入問題
9
+ # Import only for type checking, prevents circular imports
8
10
  from je_editor.pyside_ui.main_ui.main_editor import EditorWidget
9
11
 
10
12
  from je_editor.pyside_ui.code.auto_save.auto_save_thread import CodeEditSaveThread
11
13
 
14
+ # --- 全域管理字典 / Global manager dictionaries ---
15
+
16
+ # 管理每個檔案對應的自動儲存執行緒
17
+ # Manage auto-save threads for each file
12
18
  auto_save_manager_dict: dict = dict()
13
19
 
20
+ # 管理檔案是否已經開啟
21
+ # Track whether a file is currently open
14
22
  file_is_open_manager_dict: dict = dict()
15
23
 
16
24
 
17
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)
18
35
  jeditor_logger.info(f"auto_save_manager.py init_new_auto_save_thread "
19
36
  f"file_path: {file_path} "
20
37
  f"widget: {widget}")
38
+
39
+ # 將目前編輯器綁定的檔案設為 file_path
40
+ # Set the current file of the editor widget
21
41
  widget.current_file = file_path
42
+
43
+ # 如果該檔案尚未有自動儲存執行緒
44
+ # If no auto-save thread exists for this file
22
45
  if auto_save_manager_dict.get(file_path, None) is None:
46
+ # 建立新的自動儲存執行緒,綁定到編輯器
47
+ # Create a new auto-save thread for this file
23
48
  widget.code_save_thread = CodeEditSaveThread(
24
- file_to_save=widget.current_file, editor=widget.code_edit)
49
+ file_to_save=widget.current_file, editor=widget.code_edit
50
+ )
51
+
52
+ # 更新管理字典,記錄該檔案對應的執行緒
53
+ # Update manager dict with the new thread
25
54
  auto_save_manager_dict.update({
26
55
  file_path: widget.code_save_thread
27
56
  })
57
+
58
+ # 啟動自動儲存執行緒
59
+ # Start the auto-save thread
28
60
  widget.code_save_thread.start()
@@ -14,8 +14,12 @@ class CodeEditSaveThread(Thread):
14
14
  self, file_to_save: Union[str, None] = None, editor: Union[None, CodeEditor] = None):
15
15
  """
16
16
  This thread is used to auto save current file.
17
+ 這個執行緒用來自動儲存當前檔案。
18
+
17
19
  :param file_to_save: file we want to auto save
20
+ 要自動儲存的檔案路徑
18
21
  :param editor: code editor to auto save
22
+ 要自動儲存內容的編輯器元件
19
23
  """
20
24
  jeditor_logger.info(f"Init CodeEditSaveThread "
21
25
  f"file_to_save: {file_to_save} "
@@ -23,24 +27,33 @@ class CodeEditSaveThread(Thread):
23
27
  super().__init__()
24
28
  self.file: str = file_to_save
25
29
  self.editor: Union[None, CodeEditor] = editor
26
- self.still_run: bool = True
27
- # set daemon
28
- self.daemon = True
29
- self.skip_this_round: bool = False
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
30
34
 
31
35
  def run(self) -> None:
32
36
  """
33
37
  loop and save current edit file
38
+ 持續迴圈,每隔一段時間自動儲存當前編輯檔案
34
39
  """
35
40
  jeditor_logger.info("CodeEditSaveThread run")
36
41
  if self.file is not None:
37
42
  path = Path(self.file)
43
+ # 當檔案存在且編輯器不為 None 時持續運行
44
+ # Keep running while file exists and editor is valid
38
45
  while path.is_file() and self.editor is not None:
39
- time.sleep(5)
46
+ time.sleep(5) # 每 5 秒檢查一次 / Check every 5 seconds
40
47
  if self.still_run:
41
48
  if self.skip_this_round:
49
+ # 如果設定跳過本輪,什麼都不做
50
+ # Skip this round if flag is set
42
51
  pass
43
52
  else:
53
+ # 將編輯器內容寫入檔案
54
+ # Write editor content to file
44
55
  write_file(self.file, self.editor.toPlainText())
45
56
  else:
57
+ # 如果 still_run 為 False,結束迴圈
58
+ # Exit loop if still_run is False
46
59
  break
@@ -6,13 +6,17 @@ from je_editor.utils.logging.loggin_instance import jeditor_logger
6
6
 
7
7
 
8
8
  class PEP8FormatChecker(pycodestyle.Checker):
9
-
10
9
  def __init__(self, filename: str, **kwargs):
10
+ """
11
+ 自訂的 PEP8 格式檢查器,繼承自 pycodestyle.Checker。
12
+ Custom PEP8 format checker, inherits from pycodestyle.Checker.
13
+ """
11
14
  jeditor_logger.info(f"Init PEP8FormatChecker "
12
15
  f"filename: {filename} "
13
16
  f"kwargs: {kwargs}")
14
17
  super().__init__(filename, **kwargs)
15
- # Init variable
18
+
19
+ # 初始化變數 / Initialize variables
16
20
  self.physical_line = None
17
21
  self.blank_before = None
18
22
  self.blank_lines = None
@@ -24,28 +28,51 @@ class PEP8FormatChecker(pycodestyle.Checker):
24
28
  self.line_number = None
25
29
  self.indent_char = None
26
30
  self.total_lines = None
31
+
32
+ # 定義換行符號集合 / Define newline token set
27
33
  self.new_line = frozenset([tokenize.NL, tokenize.NEWLINE])
34
+
35
+ # 將 report_error 替換為自訂方法 / Override report_error with custom method
28
36
  self.report_error = self.replace_report_error
37
+
38
+ # 當前檔案名稱 / Current file name
29
39
  self.current_file: str = filename
40
+
41
+ # 儲存錯誤訊息的清單 / List to store error messages
30
42
  self.error_list: list = list()
31
43
 
32
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
+ """
33
49
  jeditor_logger.info(f"PEP8FormatChecker replace_report_error "
34
50
  f"line_number: {line_number} "
35
51
  f"offset: {offset} "
36
52
  f"text: {text}")
53
+ # 忽略 W191 (縮排使用 Tab 的警告)
54
+ # Ignore W191 (indentation contains tabs)
37
55
  if not text.startswith("W191"):
38
56
  self.error_list.append(f"{text} on line: {line_number}, offset: {offset}")
39
57
 
40
58
  def check_all_format(self, expected=None, line_offset=0) -> int:
41
- """Run all checks on the input file."""
59
+ """
60
+ 執行所有格式檢查。
61
+ Run all checks on the input file.
62
+ """
42
63
  jeditor_logger.info(f"PEP8FormatChecker check_all_format "
43
64
  f"expected: {expected} "
44
65
  f"line_offset: {line_offset}")
66
+
67
+ # 初始化檔案檢查 / Initialize file check
45
68
  self.report.init_file(self.filename, self.lines, expected, line_offset)
46
69
  self.total_lines = len(self.lines)
70
+
71
+ # 如果有 AST 檢查,先執行 / Run AST checks if available
47
72
  if self._ast_checks:
48
73
  self.check_ast()
74
+
75
+ # 重設狀態變數 / Reset state variables
49
76
  self.line_number = 0
50
77
  self.indent_char = None
51
78
  self.indent_level = self.previous_indent_level = 0
@@ -53,33 +80,51 @@ class PEP8FormatChecker(pycodestyle.Checker):
53
80
  self.previous_unindented_logical_line = ''
54
81
  self.tokens = []
55
82
  self.blank_lines = self.blank_before = 0
56
- parens = 0
83
+ parens = 0 # 括號層級計數器 / Parentheses nesting counter
84
+
85
+ # 逐一處理 Token / Process tokens one by one
57
86
  for token in self.generate_tokens():
58
87
  self.tokens.append(token)
59
88
  token_type, text = token[0:2]
89
+
90
+ # 如果 verbose >= 3,輸出詳細 Token 資訊
91
+ # If verbose >= 3, log detailed token info
60
92
  if self.verbose >= 3:
61
93
  if token[2][0] == token[3][0]:
62
94
  pos = '[{}:{}]'.format(token[2][1] or '', token[3][1])
63
95
  else:
64
96
  pos = 'l.%s' % token[3][0]
65
97
  self.replace_report_error(token[2][0], pos, tokenize.tok_name[token[0]], text)
98
+
99
+ # 檢查括號層級 / Track parentheses nesting
66
100
  if token_type == tokenize.OP:
67
101
  if text in '([{':
68
102
  parens += 1
69
103
  elif text in '}])':
70
104
  parens -= 1
105
+ # 當不在括號內時,檢查換行 / When not inside parentheses, check newlines
71
106
  elif not parens:
72
107
  if token_type in self.new_line:
73
108
  if token_type == tokenize.NEWLINE:
109
+ # 完整邏輯行結束,檢查邏輯行
110
+ # End of logical line, check it
74
111
  self.check_logical()
75
112
  self.blank_before = 0
76
113
  elif len(self.tokens) == 1:
77
- # The physical line contains only this token.
114
+ # 只有換行符號,代表空行
115
+ # Line contains only newline, count as blank line
78
116
  self.blank_lines += 1
79
117
  del self.tokens[0]
80
118
  else:
119
+ # 其他情況也檢查邏輯行
120
+ # Otherwise, check logical line
81
121
  self.check_logical()
122
+
123
+ # 如果還有剩餘 Token,檢查最後一行
124
+ # If tokens remain, check the last line
82
125
  if self.tokens:
83
- self.check_physical(self.lines[-1])
84
- self.check_logical()
126
+ self.check_physical(self.lines[-1]) # 檢查物理行 / Check physical line
127
+ self.check_logical() # 檢查邏輯行 / Check logical line
128
+
129
+ # 回傳檔案檢查結果 / Return file check results
85
130
  return self.report.get_file_results()