je-editor 0.0.223__py3-none-any.whl → 0.0.224__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.

Potentially problematic release.


This version of je-editor might be problematic. Click here for more details.

Files changed (71) hide show
  1. je_editor/git_client/commit_graph.py +7 -7
  2. je_editor/git_client/git_action.py +0 -7
  3. je_editor/pyside_ui/browser/browser_widget.py +24 -11
  4. je_editor/pyside_ui/browser/main_browser_widget.py +40 -27
  5. je_editor/pyside_ui/code/auto_save/auto_save_manager.py +34 -2
  6. je_editor/pyside_ui/code/auto_save/auto_save_thread.py +19 -6
  7. je_editor/pyside_ui/code/code_format/pep8_format.py +53 -9
  8. je_editor/pyside_ui/code/code_process/code_exec.py +88 -52
  9. je_editor/pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py +116 -55
  10. je_editor/pyside_ui/code/running_process_manager.py +19 -1
  11. je_editor/pyside_ui/code/shell_process/shell_exec.py +71 -48
  12. je_editor/pyside_ui/code/syntax/python_syntax.py +45 -10
  13. je_editor/pyside_ui/code/syntax/syntax_setting.py +40 -12
  14. je_editor/pyside_ui/code/textedit_code_result/code_record.py +34 -12
  15. je_editor/pyside_ui/code/variable_inspector/inspector_gui.py +53 -6
  16. je_editor/pyside_ui/dialog/ai_dialog/set_ai_dialog.py +30 -3
  17. je_editor/pyside_ui/dialog/file_dialog/create_file_dialog.py +35 -2
  18. je_editor/pyside_ui/dialog/file_dialog/open_file_dialog.py +33 -5
  19. je_editor/pyside_ui/dialog/file_dialog/save_file_dialog.py +25 -3
  20. je_editor/pyside_ui/dialog/search_ui/search_error_box.py +26 -1
  21. je_editor/pyside_ui/dialog/search_ui/search_text_box.py +26 -1
  22. je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +11 -11
  23. je_editor/pyside_ui/git_ui/git_client/commit_table.py +46 -8
  24. je_editor/pyside_ui/git_ui/git_client/git_branch_tree_widget.py +49 -15
  25. je_editor/pyside_ui/git_ui/git_client/graph_view.py +64 -20
  26. je_editor/pyside_ui/main_ui/ai_widget/ai_config.py +20 -5
  27. je_editor/pyside_ui/main_ui/ai_widget/ask_thread.py +20 -1
  28. je_editor/pyside_ui/main_ui/ai_widget/chat_ui.py +56 -41
  29. je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +45 -6
  30. je_editor/pyside_ui/main_ui/console_widget/console_gui.py +44 -12
  31. je_editor/pyside_ui/main_ui/console_widget/qprocess_adapter.py +34 -13
  32. je_editor/pyside_ui/main_ui/dock/destroy_dock.py +33 -2
  33. je_editor/pyside_ui/main_ui/editor/editor_widget.py +104 -20
  34. je_editor/pyside_ui/main_ui/editor/editor_widget_dock.py +34 -7
  35. je_editor/pyside_ui/main_ui/editor/process_input.py +38 -11
  36. je_editor/pyside_ui/main_ui/ipython_widget/rich_jupyter.py +46 -11
  37. je_editor/pyside_ui/main_ui/main_editor.py +175 -37
  38. je_editor/pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +51 -28
  39. je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +80 -22
  40. je_editor/pyside_ui/main_ui/menu/file_menu/build_file_menu.py +70 -17
  41. je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +34 -3
  42. je_editor/pyside_ui/main_ui/menu/language_menu/build_language_server.py +41 -1
  43. je_editor/pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py +100 -42
  44. je_editor/pyside_ui/main_ui/menu/run_menu/build_run_menu.py +57 -7
  45. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +50 -4
  46. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +52 -6
  47. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +44 -4
  48. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py +23 -1
  49. je_editor/pyside_ui/main_ui/menu/set_menu_bar.py +37 -12
  50. je_editor/pyside_ui/main_ui/menu/style_menu/build_style_menu.py +44 -7
  51. je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +126 -28
  52. je_editor/pyside_ui/main_ui/menu/text_menu/build_text_menu.py +65 -1
  53. je_editor/pyside_ui/main_ui/save_settings/setting_utils.py +18 -1
  54. je_editor/pyside_ui/main_ui/save_settings/user_color_setting_file.py +33 -3
  55. je_editor/pyside_ui/main_ui/save_settings/user_setting_file.py +38 -11
  56. je_editor/pyside_ui/main_ui/system_tray/extend_system_tray.py +39 -2
  57. je_editor/start_editor.py +26 -1
  58. je_editor/utils/encodings/python_encodings.py +101 -98
  59. je_editor/utils/file/open/open_file.py +36 -19
  60. je_editor/utils/file/save/save_file.py +35 -14
  61. je_editor/utils/json/json_file.py +29 -14
  62. je_editor/utils/json_format/json_process.py +33 -2
  63. je_editor/utils/logging/loggin_instance.py +38 -8
  64. je_editor/utils/multi_language/multi_language_wrapper.py +29 -4
  65. je_editor/utils/redirect_manager/redirect_manager_class.py +49 -11
  66. je_editor/utils/venv_check/check_venv.py +45 -15
  67. {je_editor-0.0.223.dist-info → je_editor-0.0.224.dist-info}/METADATA +1 -1
  68. {je_editor-0.0.223.dist-info → je_editor-0.0.224.dist-info}/RECORD +71 -71
  69. {je_editor-0.0.223.dist-info → je_editor-0.0.224.dist-info}/WHEEL +0 -0
  70. {je_editor-0.0.223.dist-info → je_editor-0.0.224.dist-info}/licenses/LICENSE +0 -0
  71. {je_editor-0.0.223.dist-info → je_editor-0.0.224.dist-info}/top_level.txt +0 -0
@@ -27,15 +27,15 @@ class CommitGraph:
27
27
  """
28
28
  self.nodes = [
29
29
  CommitNode(
30
- commit_sha=c["sha"],
31
- author_name=c["author"],
32
- commit_date=c["date"],
33
- commit_message=c["message"],
34
- parent_shas=c["parents"],
30
+ commit_sha=commit["sha"],
31
+ author_name=commit["author"],
32
+ commit_date=commit["date"],
33
+ commit_message=commit["message"],
34
+ parent_shas=commit["parents"],
35
35
  )
36
- for c in commits
36
+ for commit in commits
37
37
  ]
38
- self.index = {n.commit_sha: i for i, n in enumerate(self.nodes)}
38
+ self.index = {node.commit_sha: index for index, node in enumerate(self.nodes)}
39
39
  self._assign_lanes()
40
40
 
41
41
  def _assign_lanes(self) -> None:
@@ -5,9 +5,7 @@ from PySide6.QtCore import QThread, Signal
5
5
  from git import Repo, GitCommandError, InvalidGitRepositoryError, NoSuchPathError
6
6
 
7
7
 
8
- # -----------------------
9
8
  # Simple audit logger
10
- # -----------------------
11
9
  def audit_log(repo_path: str, action: str, detail: str, ok: bool, err: str = ""):
12
10
  """
13
11
  Append an audit log entry to 'audit.log' in the repo directory.
@@ -22,9 +20,7 @@ def audit_log(repo_path: str, action: str, detail: str, ok: bool, err: str = "")
22
20
  pass # Never let audit logging failure break the UI
23
21
 
24
22
 
25
- # -----------------------
26
23
  # Git service layer
27
- # -----------------------
28
24
  class GitService:
29
25
  """
30
26
  Encapsulates Git operations using GitPython.
@@ -156,10 +152,7 @@ class GitService:
156
152
  # Null tree constant for initial commit diff
157
153
  NULL_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
158
154
 
159
-
160
- # -----------------------
161
155
  # Worker thread wrapper
162
- # -----------------------
163
156
  class GitWorker(QThread):
164
157
  """
165
158
  Runs a function in a separate thread to avoid blocking the UI.
@@ -26,30 +26,37 @@ class BrowserWidget(QWidget):
26
26
  if browser_view:
27
27
  self.browser = browser_view
28
28
  else:
29
- self.browser = BrowserView(start_url, main_widget=main_widget) # 內嵌的瀏覽器視圖
30
- self.search_prefix = search_prefix # 搜尋引擎前綴字串
31
- self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
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
32
34
 
33
35
  # --- Top bar buttons / 上方工具列按鈕 ---
36
+ # 返回上一頁 / Back button
34
37
  self.back_button = QPushButton(language_wrapper.language_word_dict.get("browser_back_button"))
35
- self.back_button.clicked.connect(self.browser.back) # 返回上一頁
38
+ self.back_button.clicked.connect(self.browser.back)
36
39
 
40
+ # 前往下一頁 / Forward button
37
41
  self.forward_button = QPushButton(language_wrapper.language_word_dict.get("browser_forward_button"))
38
- self.forward_button.clicked.connect(self.browser.forward) # 前往下一頁
42
+ self.forward_button.clicked.connect(self.browser.forward)
39
43
 
44
+ # 重新整理 / Reload button
40
45
  self.reload_button = QPushButton(language_wrapper.language_word_dict.get("browser_reload_button"))
41
- self.reload_button.clicked.connect(self.browser.reload) # 重新整理
46
+ self.reload_button.clicked.connect(self.browser.reload)
42
47
 
48
+ # 搜尋按鈕 / Search button
43
49
  self.search_button = QPushButton(language_wrapper.language_word_dict.get("browser_search_button"))
44
- self.search_button.clicked.connect(self.search) # 搜尋按鈕
50
+ self.search_button.clicked.connect(self.search)
45
51
 
46
- # URL / Search input line (custom QLineEdit)
52
+ # URL / Search input line (自訂義 QLineEdit)
53
+ # Custom QLineEdit for URL or search input
47
54
  self.url_input = BrowserLineSearch(self)
48
55
 
49
56
  # --- Action: Ctrl+F to find text / 快捷鍵 Ctrl+F 搜尋文字 ---
50
57
  self.find_action = QAction()
51
- self.find_action.setShortcut("Ctrl+f")
52
- 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
53
60
  self.addAction(self.find_action)
54
61
 
55
62
  # --- Layout / 版面配置 ---
@@ -59,7 +66,9 @@ class BrowserWidget(QWidget):
59
66
  self.grid_layout.addWidget(self.reload_button, 0, 2)
60
67
  self.grid_layout.addWidget(self.url_input, 0, 3)
61
68
  self.grid_layout.addWidget(self.search_button, 0, 4)
62
- self.grid_layout.addWidget(self.browser, 1, 0, -1, -1) # 瀏覽器視圖佔滿下方
69
+ # 瀏覽器視圖佔滿下方所有空間
70
+ # Browser view fills the lower area
71
+ self.grid_layout.addWidget(self.browser, 1, 0, -1, -1)
63
72
  self.setLayout(self.grid_layout)
64
73
 
65
74
  def search(self):
@@ -68,6 +77,8 @@ class BrowserWidget(QWidget):
68
77
  使用輸入框的文字進行搜尋,將字串附加到 search_prefix 後送出。
69
78
  """
70
79
  jeditor_logger.info("BrowserWidget Search")
80
+ # 將輸入框文字拼接到搜尋前綴,並設定為瀏覽器 URL
81
+ # Append input text to search prefix and set as browser URL
71
82
  self.browser.setUrl(f"{self.search_prefix}{self.url_input.text()}")
72
83
 
73
84
  def find_text(self):
@@ -85,6 +96,8 @@ class BrowserWidget(QWidget):
85
96
  language_wrapper.language_word_dict.get("browser_find_text_input")
86
97
  )
87
98
  if press_ok:
99
+ # 在頁面中搜尋輸入文字 / Search entered text in page
88
100
  self.browser.findText(search_text)
89
101
  else:
102
+ # 清除搜尋結果 / Clear search
90
103
  self.browser.findText("")
@@ -7,66 +7,79 @@ from je_editor.utils.logging.loggin_instance import jeditor_logger
7
7
  class MainBrowserWidget(QWidget):
8
8
  """
9
9
  瀏覽器元件:包含分頁,並在最右方固定一個「+」分頁。
10
+ Browser component: includes tabs, with a fixed "+" tab at the far right.
10
11
  """
11
12
 
12
13
  def __init__(self, start_url: str = "https://www.google.com/",
13
14
  search_prefix: str = "https://www.google.com.tw/search?q="):
14
15
  super().__init__()
16
+ # 初始化時記錄訊息 (方便除錯)
17
+ # Log initialization info (for debugging)
15
18
  jeditor_logger.info("Init BrowserWidget "
16
19
  f"start_url: {start_url} "
17
20
  f"search_prefix: {search_prefix}")
18
21
 
19
- grid_layout = QGridLayout()
22
+ grid_layout = QGridLayout() # 建立網格佈局 / Create grid layout
20
23
 
21
- self.browser_tab = QTabWidget()
22
- self.browser_tab.setTabsClosable(True)
23
- self.browser_tab.tabCloseRequested.connect(self.close_tab)
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
24
27
 
25
- self.search_prefix = search_prefix
28
+ self.search_prefix = search_prefix # 搜尋前綴字串 / Search prefix string
26
29
 
27
- # 預設第一個分頁
28
- self.add_browser_tab(BrowserWidget(start_url=start_url,
29
- search_prefix=search_prefix),
30
- "Google")
30
+ # 預設第一個分頁 (Google)
31
+ # Default first tab (Google)
32
+ self.add_browser_tab(
33
+ BrowserWidget(start_url=start_url, search_prefix=search_prefix),"Google"
34
+ )
31
35
 
32
36
  # 固定一個「+」分頁
37
+ # Add a fixed "+" tab
33
38
  self.add_plus_tab()
34
39
 
35
- grid_layout.addWidget(self.browser_tab)
36
- self.setLayout(grid_layout)
40
+ grid_layout.addWidget(self.browser_tab) # 把分頁加入佈局 / Add tab widget to layout
41
+ self.setLayout(grid_layout) # 設定主視窗佈局 / Set main layout
37
42
 
38
43
  def add_browser_tab(self, browser_widget: BrowserWidget, title: str = "New Tab"):
39
44
  # 在「+」分頁前插入新分頁
45
+ # Insert new tab before the "+" tab
40
46
  plus_index = self.browser_tab.count() - 1
41
47
  index = self.browser_tab.insertTab(plus_index, browser_widget, title)
42
- self.browser_tab.setCurrentIndex(index)
48
+ self.browser_tab.setCurrentIndex(index) # 自動切換到新分頁 / Switch to new tab
43
49
 
44
- # 更新分頁標題
50
+ # 更新分頁標題 (當網頁標題改變時)
51
+ # Update tab title when browser title changes
45
52
  browser_widget.browser.titleChanged.connect(
46
53
  lambda t, i=index: self.browser_tab.setTabText(i, t)
47
54
  )
48
55
 
49
56
  def add_plus_tab(self):
50
- """新增一個固定的「+」分頁"""
57
+ """新增一個固定的「+」分頁 / Add a fixed "+" tab"""
51
58
  add_new_page_widget = QWidget()
52
59
  self.browser_tab.addTab(add_new_page_widget, "+")
53
- # 禁止關閉「+」分頁
60
+ # 禁止關閉「+」分頁 / Disable closing of "+" tab
54
61
  self.browser_tab.tabBar().setTabButton(self.browser_tab.count() - 1,
55
62
  QTabBar.ButtonPosition.RightSide, None)
63
+ # 點擊分頁時觸發事件 / Connect tab click event
56
64
  self.browser_tab.tabBar().tabBarClicked.connect(self.handle_tab_changed)
57
65
 
58
66
  def handle_tab_changed(self, index: int):
59
- """如果點到「+」分頁,就開新分頁"""
60
- if self.browser_tab.tabText(index) == "+": # 最後一個是「+」
61
- # 建立新分頁(會自動切換到新分頁)
62
- self.add_browser_tab(
63
- BrowserWidget(start_url="https://www.google.com/",
64
- search_prefix=self.search_prefix),
65
- "Google"
66
- )
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
+ )
67
78
 
68
79
  def close_tab(self, index: int):
69
- """關閉指定分頁,但保留「+」"""
70
- widget = self.browser_tab.widget(index)
71
- self.browser_tab.removeTab(index)
72
- widget.deleteLater()
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
  })
28
- widget.code_save_thread.start()
57
+
58
+ # 啟動自動儲存執行緒
59
+ # Start the auto-save thread
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:
46
- break
57
+ # 如果 still_run 為 False,結束迴圈
58
+ # Exit loop if still_run is False
59
+ break
@@ -1,18 +1,21 @@
1
1
  import tokenize
2
-
3
2
  import pycodestyle
4
3
 
5
4
  from je_editor.utils.logging.loggin_instance import jeditor_logger
6
5
 
7
6
 
8
7
  class PEP8FormatChecker(pycodestyle.Checker):
9
-
10
8
  def __init__(self, filename: str, **kwargs):
9
+ """
10
+ 自訂的 PEP8 格式檢查器,繼承自 pycodestyle.Checker。
11
+ Custom PEP8 format checker, inherits from pycodestyle.Checker.
12
+ """
11
13
  jeditor_logger.info(f"Init PEP8FormatChecker "
12
14
  f"filename: {filename} "
13
15
  f"kwargs: {kwargs}")
14
16
  super().__init__(filename, **kwargs)
15
- # Init variable
17
+
18
+ # 初始化變數 / Initialize variables
16
19
  self.physical_line = None
17
20
  self.blank_before = None
18
21
  self.blank_lines = None
@@ -24,28 +27,51 @@ class PEP8FormatChecker(pycodestyle.Checker):
24
27
  self.line_number = None
25
28
  self.indent_char = None
26
29
  self.total_lines = None
30
+
31
+ # 定義換行符號集合 / Define newline token set
27
32
  self.new_line = frozenset([tokenize.NL, tokenize.NEWLINE])
33
+
34
+ # 將 report_error 替換為自訂方法 / Override report_error with custom method
28
35
  self.report_error = self.replace_report_error
36
+
37
+ # 當前檔案名稱 / Current file name
29
38
  self.current_file: str = filename
39
+
40
+ # 儲存錯誤訊息的清單 / List to store error messages
30
41
  self.error_list: list = list()
31
42
 
32
43
  def replace_report_error(self, line_number, offset, text, check):
44
+ """
45
+ 自訂錯誤回報方法,過濾掉特定錯誤 (例如 W191)。
46
+ Custom error reporting method, filters out specific errors (e.g., W191).
47
+ """
33
48
  jeditor_logger.info(f"PEP8FormatChecker replace_report_error "
34
49
  f"line_number: {line_number} "
35
50
  f"offset: {offset} "
36
51
  f"text: {text}")
52
+ # 忽略 W191 (縮排使用 Tab 的警告)
53
+ # Ignore W191 (indentation contains tabs)
37
54
  if not text.startswith("W191"):
38
55
  self.error_list.append(f"{text} on line: {line_number}, offset: {offset}")
39
56
 
40
57
  def check_all_format(self, expected=None, line_offset=0) -> int:
41
- """Run all checks on the input file."""
58
+ """
59
+ 執行所有格式檢查。
60
+ Run all checks on the input file.
61
+ """
42
62
  jeditor_logger.info(f"PEP8FormatChecker check_all_format "
43
63
  f"expected: {expected} "
44
64
  f"line_offset: {line_offset}")
65
+
66
+ # 初始化檔案檢查 / Initialize file check
45
67
  self.report.init_file(self.filename, self.lines, expected, line_offset)
46
68
  self.total_lines = len(self.lines)
69
+
70
+ # 如果有 AST 檢查,先執行 / Run AST checks if available
47
71
  if self._ast_checks:
48
72
  self.check_ast()
73
+
74
+ # 重設狀態變數 / Reset state variables
49
75
  self.line_number = 0
50
76
  self.indent_char = None
51
77
  self.indent_level = self.previous_indent_level = 0
@@ -53,33 +79,51 @@ class PEP8FormatChecker(pycodestyle.Checker):
53
79
  self.previous_unindented_logical_line = ''
54
80
  self.tokens = []
55
81
  self.blank_lines = self.blank_before = 0
56
- parens = 0
82
+ parens = 0 # 括號層級計數器 / Parentheses nesting counter
83
+
84
+ # 逐一處理 Token / Process tokens one by one
57
85
  for token in self.generate_tokens():
58
86
  self.tokens.append(token)
59
87
  token_type, text = token[0:2]
88
+
89
+ # 如果 verbose >= 3,輸出詳細 Token 資訊
90
+ # If verbose >= 3, log detailed token info
60
91
  if self.verbose >= 3:
61
92
  if token[2][0] == token[3][0]:
62
93
  pos = '[{}:{}]'.format(token[2][1] or '', token[3][1])
63
94
  else:
64
95
  pos = 'l.%s' % token[3][0]
65
96
  self.replace_report_error(token[2][0], pos, tokenize.tok_name[token[0]], text)
97
+
98
+ # 檢查括號層級 / Track parentheses nesting
66
99
  if token_type == tokenize.OP:
67
100
  if text in '([{':
68
101
  parens += 1
69
102
  elif text in '}])':
70
103
  parens -= 1
104
+ # 當不在括號內時,檢查換行 / When not inside parentheses, check newlines
71
105
  elif not parens:
72
106
  if token_type in self.new_line:
73
107
  if token_type == tokenize.NEWLINE:
108
+ # 完整邏輯行結束,檢查邏輯行
109
+ # End of logical line, check it
74
110
  self.check_logical()
75
111
  self.blank_before = 0
76
112
  elif len(self.tokens) == 1:
77
- # The physical line contains only this token.
113
+ # 只有換行符號,代表空行
114
+ # Line contains only newline, count as blank line
78
115
  self.blank_lines += 1
79
116
  del self.tokens[0]
80
117
  else:
118
+ # 其他情況也檢查邏輯行
119
+ # Otherwise, check logical line
81
120
  self.check_logical()
121
+
122
+ # 如果還有剩餘 Token,檢查最後一行
123
+ # If tokens remain, check the last line
82
124
  if self.tokens:
83
- self.check_physical(self.lines[-1])
84
- self.check_logical()
85
- return self.report.get_file_results()
125
+ self.check_physical(self.lines[-1]) # 檢查物理行 / Check physical line
126
+ self.check_logical() # 檢查邏輯行 / Check logical line
127
+
128
+ # 回傳檔案檢查結果 / Return file check results
129
+ return self.report.get_file_results()