je-editor 0.0.220__tar.gz → 0.0.222__tar.gz

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 (151) hide show
  1. {je_editor-0.0.220 → je_editor-0.0.222}/PKG-INFO +4 -4
  2. je_editor-0.0.222/je_editor/code_scan/ruff_thread.py +58 -0
  3. je_editor-0.0.222/je_editor/code_scan/watchdog_implement.py +56 -0
  4. je_editor-0.0.222/je_editor/code_scan/watchdog_thread.py +78 -0
  5. je_editor-0.0.222/je_editor/git_client/commit_graph.py +77 -0
  6. je_editor-0.0.220/je_editor/git_client/git.py → je_editor-0.0.222/je_editor/git_client/git_action.py +1 -1
  7. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/git_client/git_cli.py +4 -4
  8. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/git_client/github.py +36 -5
  9. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/browser/browser_download_window.py +41 -5
  10. je_editor-0.0.222/je_editor/pyside_ui/browser/browser_serach_lineedit.py +51 -0
  11. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/browser/browser_view.py +42 -1
  12. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/browser/browser_widget.py +43 -14
  13. je_editor-0.0.222/je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +90 -0
  14. je_editor-0.0.222/je_editor/pyside_ui/git_ui/code_diff_compare/line_number_code_viewer.py +141 -0
  15. je_editor-0.0.222/je_editor/pyside_ui/git_ui/code_diff_compare/multi_file_diff_viewer.py +88 -0
  16. je_editor-0.0.222/je_editor/pyside_ui/git_ui/code_diff_compare/side_by_side_diff_widget.py +271 -0
  17. {je_editor-0.0.220/je_editor/pyside_ui/git_ui → je_editor-0.0.222/je_editor/pyside_ui/git_ui/git_client}/git_branch_tree_widget.py +17 -14
  18. je_editor-0.0.222/je_editor/pyside_ui/git_ui/git_client/git_client_gui.py +734 -0
  19. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +1 -1
  20. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/main_editor.py +0 -3
  21. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +14 -3
  22. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +19 -4
  23. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/multi_language/english.py +2 -1
  24. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/multi_language/traditional_chinese.py +2 -2
  25. je_editor-0.0.222/je_editor/utils/redirect_manager/__init__.py +0 -0
  26. je_editor-0.0.222/je_editor/utils/venv_check/__init__.py +0 -0
  27. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor.egg-info/PKG-INFO +4 -4
  28. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor.egg-info/SOURCES.txt +11 -5
  29. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor.egg-info/requires.txt +3 -3
  30. {je_editor-0.0.220 → je_editor-0.0.222}/pyproject.toml +3 -3
  31. je_editor-0.0.220/je_editor/code_scan/ruff_thread.py +0 -31
  32. je_editor-0.0.220/je_editor/code_scan/watchdog_implement.py +0 -34
  33. je_editor-0.0.220/je_editor/code_scan/watchdog_thread.py +0 -33
  34. je_editor-0.0.220/je_editor/git_client/commit_graph.py +0 -88
  35. je_editor-0.0.220/je_editor/pyside_ui/browser/browser_serach_lineedit.py +0 -27
  36. je_editor-0.0.220/je_editor/pyside_ui/git_ui/git_client_gui.py +0 -291
  37. {je_editor-0.0.220 → je_editor-0.0.222}/LICENSE +0 -0
  38. {je_editor-0.0.220 → je_editor-0.0.222}/README.md +0 -0
  39. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/__init__.py +0 -0
  40. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/__main__.py +0 -0
  41. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/code_scan/__init__.py +0 -0
  42. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/git_client/__init__.py +0 -0
  43. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/__init__.py +0 -0
  44. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/browser/__init__.py +0 -0
  45. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/__init__.py +0 -0
  46. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/auto_save/__init__.py +0 -0
  47. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/auto_save/auto_save_manager.py +0 -0
  48. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/auto_save/auto_save_thread.py +0 -0
  49. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/code_format/__init__.py +0 -0
  50. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/code_format/pep8_format.py +0 -0
  51. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/code_process/__init__.py +0 -0
  52. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/code_process/code_exec.py +0 -0
  53. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/plaintext_code_edit/__init__.py +0 -0
  54. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py +0 -0
  55. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/running_process_manager.py +0 -0
  56. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/shell_process/__init__.py +0 -0
  57. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/shell_process/shell_exec.py +0 -0
  58. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/syntax/__init__.py +0 -0
  59. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/syntax/python_syntax.py +0 -0
  60. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/syntax/syntax_setting.py +0 -0
  61. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/textedit_code_result/__init__.py +0 -0
  62. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/textedit_code_result/code_record.py +0 -0
  63. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/variable_inspector/__init__.py +0 -0
  64. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/variable_inspector/inspector_gui.py +0 -0
  65. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/__init__.py +0 -0
  66. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/ai_dialog/__init__.py +0 -0
  67. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/ai_dialog/set_ai_dialog.py +0 -0
  68. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/file_dialog/__init__.py +0 -0
  69. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/file_dialog/create_file_dialog.py +0 -0
  70. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/file_dialog/open_file_dialog.py +0 -0
  71. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/file_dialog/save_file_dialog.py +0 -0
  72. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/search_ui/__init__.py +0 -0
  73. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/search_ui/search_error_box.py +0 -0
  74. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/search_ui/search_text_box.py +0 -0
  75. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/git_ui/__init__.py +0 -0
  76. {je_editor-0.0.220/je_editor/pyside_ui/main_ui → je_editor-0.0.222/je_editor/pyside_ui/git_ui/code_diff_compare}/__init__.py +0 -0
  77. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/ai_widget → je_editor-0.0.222/je_editor/pyside_ui/git_ui/git_client}/__init__.py +0 -0
  78. {je_editor-0.0.220/je_editor/pyside_ui/git_ui → je_editor-0.0.222/je_editor/pyside_ui/git_ui/git_client}/commit_table.py +0 -0
  79. {je_editor-0.0.220/je_editor/pyside_ui/git_ui → je_editor-0.0.222/je_editor/pyside_ui/git_ui/git_client}/graph_view.py +0 -0
  80. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/console_widget → je_editor-0.0.222/je_editor/pyside_ui/main_ui}/__init__.py +0 -0
  81. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/dock → je_editor-0.0.222/je_editor/pyside_ui/main_ui/ai_widget}/__init__.py +0 -0
  82. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/ai_widget/ai_config.py +0 -0
  83. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/ai_widget/ask_thread.py +0 -0
  84. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/ai_widget/chat_ui.py +0 -0
  85. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/editor → je_editor-0.0.222/je_editor/pyside_ui/main_ui/console_widget}/__init__.py +0 -0
  86. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/console_widget/console_gui.py +0 -0
  87. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/console_widget/qprocess_adapter.py +0 -0
  88. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/ipython_widget → je_editor-0.0.222/je_editor/pyside_ui/main_ui/dock}/__init__.py +0 -0
  89. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/dock/destroy_dock.py +0 -0
  90. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/editor}/__init__.py +0 -0
  91. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/editor/editor_widget.py +0 -0
  92. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/editor/editor_widget_dock.py +0 -0
  93. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/editor/process_input.py +0 -0
  94. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/check_style_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/ipython_widget}/__init__.py +0 -0
  95. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/ipython_widget/rich_jupyter.py +0 -0
  96. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/dock_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu}/__init__.py +0 -0
  97. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/file_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/check_style_menu}/__init__.py +0 -0
  98. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +0 -0
  99. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/help_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/dock_menu}/__init__.py +0 -0
  100. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/language_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/file_menu}/__init__.py +0 -0
  101. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/file_menu/build_file_menu.py +0 -0
  102. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/python_env_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/help_menu}/__init__.py +0 -0
  103. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +0 -0
  104. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/run_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/language_menu}/__init__.py +0 -0
  105. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/language_menu/build_language_server.py +0 -0
  106. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/python_env_menu}/__init__.py +0 -0
  107. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py +0 -0
  108. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/style_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/run_menu}/__init__.py +0 -0
  109. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/run_menu/build_run_menu.py +0 -0
  110. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/tab_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu}/__init__.py +0 -0
  111. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +0 -0
  112. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +0 -0
  113. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +0 -0
  114. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py +0 -0
  115. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/set_menu_bar.py +0 -0
  116. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/text_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/style_menu}/__init__.py +0 -0
  117. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/style_menu/build_style_menu.py +0 -0
  118. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/save_settings → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/tab_menu}/__init__.py +0 -0
  119. {je_editor-0.0.220/je_editor/pyside_ui/main_ui/system_tray → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/text_menu}/__init__.py +0 -0
  120. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/text_menu/build_text_menu.py +0 -0
  121. {je_editor-0.0.220/je_editor/utils/encodings → je_editor-0.0.222/je_editor/pyside_ui/main_ui/save_settings}/__init__.py +0 -0
  122. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/save_settings/setting_utils.py +0 -0
  123. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/save_settings/user_color_setting_file.py +0 -0
  124. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/save_settings/user_setting_file.py +0 -0
  125. {je_editor-0.0.220/je_editor/utils/file → je_editor-0.0.222/je_editor/pyside_ui/main_ui/system_tray}/__init__.py +0 -0
  126. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/system_tray/extend_system_tray.py +0 -0
  127. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/start_editor.py +0 -0
  128. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/__init__.py +0 -0
  129. {je_editor-0.0.220/je_editor/utils/json → je_editor-0.0.222/je_editor/utils/encodings}/__init__.py +0 -0
  130. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/encodings/python_encodings.py +0 -0
  131. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/exception/__init__.py +0 -0
  132. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/exception/exception_tags.py +0 -0
  133. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/exception/exceptions.py +0 -0
  134. {je_editor-0.0.220/je_editor/utils/logging → je_editor-0.0.222/je_editor/utils/file}/__init__.py +0 -0
  135. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/file/open/__init__.py +0 -0
  136. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/file/open/open_file.py +0 -0
  137. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/file/save/__init__.py +0 -0
  138. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/file/save/save_file.py +0 -0
  139. {je_editor-0.0.220/je_editor/utils/multi_language → je_editor-0.0.222/je_editor/utils/json}/__init__.py +0 -0
  140. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/json/json_file.py +0 -0
  141. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/json_format/__init__.py +0 -0
  142. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/json_format/json_process.py +0 -0
  143. {je_editor-0.0.220/je_editor/utils/redirect_manager → je_editor-0.0.222/je_editor/utils/logging}/__init__.py +0 -0
  144. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/logging/loggin_instance.py +0 -0
  145. {je_editor-0.0.220/je_editor/utils/venv_check → je_editor-0.0.222/je_editor/utils/multi_language}/__init__.py +0 -0
  146. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/multi_language/multi_language_wrapper.py +0 -0
  147. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/redirect_manager/redirect_manager_class.py +0 -0
  148. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/venv_check/check_venv.py +0 -0
  149. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor.egg-info/dependency_links.txt +0 -0
  150. {je_editor-0.0.220 → je_editor-0.0.222}/je_editor.egg-info/top_level.txt +0 -0
  151. {je_editor-0.0.220 → je_editor-0.0.222}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: je_editor
3
- Version: 0.0.220
3
+ Version: 0.0.222
4
4
  Summary: JEditor is basic but powerful editor include GPT
5
5
  Author-email: JE-Chen <jechenmailman@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/JE-Chen/je_editor
@@ -16,15 +16,15 @@ Classifier: Operating System :: OS Independent
16
16
  Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
- Requires-Dist: PySide6==6.9.3
19
+ Requires-Dist: PySide6==6.10.0
20
20
  Requires-Dist: qt-material
21
21
  Requires-Dist: yapf
22
22
  Requires-Dist: frontengine
23
23
  Requires-Dist: pycodestyle
24
24
  Requires-Dist: jedi
25
25
  Requires-Dist: qtconsole
26
- Requires-Dist: langchain_openai
27
- Requires-Dist: langchain
26
+ Requires-Dist: langchain_openai==1.0.0
27
+ Requires-Dist: langchain==1.0.0
28
28
  Requires-Dist: pydantic
29
29
  Requires-Dist: watchdog
30
30
  Requires-Dist: ruff
@@ -0,0 +1,58 @@
1
+ import subprocess
2
+ import threading
3
+ import time
4
+ from queue import Queue
5
+
6
+
7
+ class RuffThread(threading.Thread):
8
+ """
9
+ A thread class to run Ruff (a Python linter/formatter) as a subprocess.
10
+ 使用子執行緒執行 Ruff (Python 程式碼檢查/格式化工具)。
11
+ """
12
+
13
+ def __init__(self, ruff_commands: list, std_queue: Queue, stderr_queue: Queue):
14
+ """
15
+ Initialize the RuffThread.
16
+ 初始化 RuffThread。
17
+
18
+ :param ruff_commands: list of commands to run Ruff, e.g. ["ruff", "check"]
19
+ 要執行的 Ruff 指令,例如 ["ruff", "check"]
20
+ :param std_queue: queue to store standard output
21
+ 用來存放標準輸出的佇列
22
+ :param stderr_queue: queue to store error output
23
+ 用來存放錯誤輸出的佇列
24
+ """
25
+ super().__init__()
26
+ if ruff_commands is None:
27
+ self.ruff_commands = ["ruff", "check"]
28
+ else:
29
+ self.ruff_commands = ruff_commands
30
+
31
+ self.ruff_process = None
32
+ self.std_queue = std_queue
33
+ self.stderr_queue = stderr_queue
34
+
35
+ def run(self):
36
+ """
37
+ Run the Ruff process in a separate thread.
38
+ 在子執行緒中執行 Ruff 程式。
39
+ """
40
+ # 啟動子程序,捕捉 stdout 與 stderr
41
+ self.ruff_process = subprocess.Popen(
42
+ self.ruff_commands,
43
+ stdout=subprocess.PIPE,
44
+ stderr=subprocess.PIPE,
45
+ text=True,
46
+ bufsize=1
47
+ )
48
+
49
+ # 等待子程序結束
50
+ while self.ruff_process.poll() is None:
51
+ time.sleep(1)
52
+ else:
53
+ # 子程序結束後,讀取 stdout 與 stderr
54
+ for line in self.ruff_process.stdout:
55
+ self.std_queue.put(line.strip())
56
+
57
+ for line in self.ruff_process.stderr:
58
+ self.stderr_queue.put(line.strip())
@@ -0,0 +1,56 @@
1
+ import time
2
+ from queue import Queue
3
+ from typing import Dict
4
+
5
+ from watchdog.events import FileSystemEventHandler
6
+
7
+ from je_editor.code_scan.ruff_thread import RuffThread
8
+
9
+
10
+ class RuffPythonFileChangeHandler(FileSystemEventHandler):
11
+ """
12
+ File system event handler that runs Ruff when Python files are modified.
13
+ 當 Python 檔案被修改時,自動觸發 Ruff 檢查。
14
+ """
15
+
16
+ def __init__(self, ruff_commands: list = None, debounce_interval: float = 1.0):
17
+ """
18
+ :param ruff_commands: Ruff command list, e.g. ["ruff", "check"]
19
+ :param debounce_interval: Minimum interval (seconds) between re-runs for the same file
20
+ 同一檔案觸發 Ruff 的最小間隔秒數
21
+ """
22
+ super().__init__()
23
+ self.ruff_commands = ruff_commands or ["ruff", "check"]
24
+ self.stdout_queue: Queue = Queue()
25
+ self.stderr_queue: Queue = Queue()
26
+ self.ruff_threads_dict: Dict[str, RuffThread] = {}
27
+ self.last_run_time: Dict[str, float] = {}
28
+ self.debounce_interval = debounce_interval
29
+
30
+ def _start_new_thread(self, file_path: str):
31
+ """Helper to start a new Ruff thread for a given file."""
32
+ ruff_thread = RuffThread(self.ruff_commands, self.stdout_queue, self.stderr_queue)
33
+ self.ruff_threads_dict[file_path] = ruff_thread
34
+ self.last_run_time[file_path] = time.time()
35
+ ruff_thread.start()
36
+
37
+ def on_modified(self, event):
38
+ """Triggered when a file is modified."""
39
+ if event.is_directory:
40
+ return
41
+
42
+ if not event.src_path.endswith(".py"):
43
+ return
44
+
45
+ now = time.time()
46
+ last_time = self.last_run_time.get(event.src_path, 0)
47
+
48
+ # Debounce: skip if last run was too recent
49
+ if now - last_time < self.debounce_interval:
50
+ return
51
+
52
+ ruff_thread = self.ruff_threads_dict.get(event.src_path)
53
+
54
+ if ruff_thread is None or not ruff_thread.is_alive():
55
+ self._start_new_thread(event.src_path)
56
+ # else: thread still running, skip
@@ -0,0 +1,78 @@
1
+ import threading
2
+ import time
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ from watchdog.observers import Observer
7
+
8
+ from je_editor.code_scan.watchdog_implement import RuffPythonFileChangeHandler
9
+
10
+
11
+ class WatchdogThread(threading.Thread):
12
+ """
13
+ A thread that runs a watchdog observer to monitor file changes.
14
+ 使用 watchdog 監控檔案變化的執行緒。
15
+ """
16
+
17
+ def __init__(self, check_path: str):
18
+ """
19
+ :param check_path: Path to monitor (directory or file)
20
+ 要監控的路徑(資料夾或檔案)
21
+ """
22
+ super().__init__(daemon=True) # 設為 daemon,主程式結束時自動退出
23
+ self.check_path = Path(check_path).resolve()
24
+ self.ruff_handler = RuffPythonFileChangeHandler()
25
+ self.running = True
26
+ self.observer = Observer()
27
+
28
+ def run(self):
29
+ """Start the watchdog observer loop."""
30
+ if not self.check_path.exists():
31
+ print(f"[Error] Path does not exist: {self.check_path}", file=sys.stderr)
32
+ return
33
+
34
+ # 設定監控
35
+ self.observer.schedule(self.ruff_handler, str(self.check_path), recursive=True)
36
+ self.observer.start()
37
+ print(f"[Watchdog] Monitoring started on {self.check_path}")
38
+
39
+ try:
40
+ while self.running:
41
+ time.sleep(1)
42
+ # 這裡可以加上 queue 輸出處理
43
+ self._process_ruff_output()
44
+ except KeyboardInterrupt:
45
+ print("[Watchdog] Interrupted by user")
46
+ finally:
47
+ self.observer.stop()
48
+ self.observer.join()
49
+ print("[Watchdog] Monitoring stopped")
50
+
51
+ def stop(self):
52
+ """Stop the watchdog thread safely."""
53
+ self.running = False
54
+
55
+ def _process_ruff_output(self):
56
+ """Process stdout/stderr queues from Ruff threads."""
57
+ while not self.ruff_handler.stdout_queue.empty():
58
+ line = self.ruff_handler.stdout_queue.get()
59
+ print(f"[Ruff STDOUT] {line}")
60
+
61
+ while not self.ruff_handler.stderr_queue.empty():
62
+ line = self.ruff_handler.stderr_queue.get()
63
+ print(f"[Ruff STDERR] {line}", file=sys.stderr)
64
+
65
+
66
+ if __name__ == '__main__':
67
+ # 預設監控當前目錄
68
+ path_to_watch = "."
69
+ watchdog_thread = WatchdogThread(path_to_watch)
70
+ watchdog_thread.start()
71
+
72
+ try:
73
+ while True:
74
+ time.sleep(1)
75
+ except KeyboardInterrupt:
76
+ print("[Main] Stopping watchdog...")
77
+ watchdog_thread.stop()
78
+ watchdog_thread.join()
@@ -0,0 +1,77 @@
1
+ import logging
2
+ from dataclasses import dataclass, field
3
+ from typing import List, Dict, Any
4
+
5
+ log = logging.getLogger(__name__)
6
+
7
+
8
+ @dataclass
9
+ class CommitNode:
10
+ commit_sha: str
11
+ author_name: str
12
+ commit_date: str
13
+ commit_message: str
14
+ parent_shas: List[str]
15
+ lane_index: int = -1 # assigned later
16
+
17
+
18
+ @dataclass
19
+ class CommitGraph:
20
+ nodes: List[CommitNode] = field(default_factory=list)
21
+ index: Dict[str, int] = field(default_factory=dict) # sha -> row
22
+
23
+ def build(self, commits: List[Dict[str, Any]], refs: Dict[str, str] | None = None) -> None:
24
+ """
25
+ Build commit graph from topo-ordered commits.
26
+ 從 topo-order 的 commits 建立 commit graph。
27
+ """
28
+ self.nodes = [
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"],
35
+ )
36
+ for c in commits
37
+ ]
38
+ self.index = {n.commit_sha: i for i, n in enumerate(self.nodes)}
39
+ self._assign_lanes()
40
+
41
+ def _assign_lanes(self) -> None:
42
+ """
43
+ Assign lanes to commits, similar to `git log --graph`.
44
+ 分配 lanes,模擬 `git log --graph` 的效果。
45
+ """
46
+ active: Dict[int, str] = {} # lane -> sha
47
+ free_lanes: List[int] = []
48
+
49
+ for node in self.nodes:
50
+ # Step 1: 找到 lane
51
+ lane_found = next((lane for lane, sha in active.items() if sha == node.commit_sha), None)
52
+
53
+ if lane_found is not None:
54
+ node.lane_index = lane_found
55
+ elif free_lanes:
56
+ node.lane_index = free_lanes.pop(0)
57
+ else:
58
+ node.lane_index = 0 if not active else max(active.keys()) + 1
59
+
60
+ # Step 2: 更新 active
61
+ # 移除舊的 sha
62
+ active = {lane: sha for lane, sha in active.items() if sha != node.commit_sha}
63
+
64
+ # 父節點分配 lane
65
+ if node.parent_shas:
66
+ first_parent = node.parent_shas[0]
67
+ active[node.lane_index] = first_parent
68
+ for p in node.parent_shas[1:]:
69
+ pl = free_lanes.pop(0) if free_lanes else (max(active.keys()) + 1)
70
+ active[pl] = p
71
+
72
+ # Step 3: 更新 free_lanes
73
+ if active:
74
+ max_lane = max(active.keys())
75
+ used = set(active.keys())
76
+ all_lanes = set(range(max_lane + 1))
77
+ free_lanes = sorted(set(free_lanes).union(all_lanes - used))
@@ -160,7 +160,7 @@ NULL_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
160
160
  # -----------------------
161
161
  # Worker thread wrapper
162
162
  # -----------------------
163
- class Worker(QThread):
163
+ class GitWorker(QThread):
164
164
  """
165
165
  Runs a function in a separate thread to avoid blocking the UI.
166
166
  Emits (result, error) when done.
@@ -11,12 +11,12 @@ class GitCLI:
11
11
  self.repo_path = Path(repo_path)
12
12
 
13
13
  def is_git_repo(self) -> bool:
14
- return (self.repo_path / ".git_client").exists()
14
+ return (self.repo_path / ".git").exists()
15
15
 
16
16
  def _run(self, args: List[str]) -> str:
17
- log.debug("git_client %s", " ".join(args))
17
+ log.debug("git %s", " ".join(args))
18
18
  res = subprocess.run(
19
- ["git_client"] + args,
19
+ ["git"] + args,
20
20
  cwd=self.repo_path,
21
21
  stdout=subprocess.PIPE,
22
22
  stderr=subprocess.PIPE,
@@ -63,4 +63,4 @@ class GitCLI:
63
63
  "date": date,
64
64
  "message": msg,
65
65
  })
66
- return commits
66
+ return commits
@@ -7,38 +7,68 @@ from git import Repo, GitCommandError, InvalidGitRepositoryError, NoSuchPathErro
7
7
  class GitCloneHandler:
8
8
  """
9
9
  Handles cloning of remote Git repositories with audit logging.
10
- Can be reused in UI or CLI contexts.
10
+ 負責複製遠端 Git 儲存庫,並記錄稽核日誌。
11
+ 可在 UI 或 CLI 環境中重複使用。
11
12
  """
12
13
 
13
14
  def __init__(self, audit_log_path: str = "git_clone_audit.log"):
15
+ """
16
+ Initialize the handler with an audit log file path.
17
+ 初始化處理器,指定稽核日誌檔案路徑。
18
+
19
+ :param audit_log_path: Path to the audit log file
20
+ 稽核日誌檔案的路徑
21
+ """
14
22
  self.audit_log_path = audit_log_path
15
23
 
16
24
  def clone_repo(self, remote_url: str, local_path: str) -> str:
17
25
  """
18
26
  Clone a remote repository to a local path.
27
+ 將遠端 Git 儲存庫複製到本地路徑。
19
28
 
20
29
  :param remote_url: The Git repository URL (e.g., https://github.com/user/repo.git)
30
+ Git 儲存庫的 URL
21
31
  :param local_path: The local directory to clone into
32
+ 要複製到的本地目錄
22
33
  :return: The path to the cloned repository
23
- :raises: Exception if cloning fails
34
+ 複製完成後的本地路徑
35
+ :raises: RuntimeError if cloning fails
36
+ 若複製失敗則拋出 RuntimeError
24
37
  """
25
38
  try:
39
+ # 記錄開始複製 / Log start of cloning
26
40
  self._log_audit(f"Cloning started: {remote_url} -> {local_path}")
41
+
42
+ # 執行 Git 複製 / Perform Git clone
27
43
  Repo.clone_from(remote_url, local_path)
44
+
45
+ # 記錄完成複製 / Log completion
28
46
  self._log_audit(f"Cloning completed: {remote_url} -> {local_path}")
29
47
  return local_path
48
+
30
49
  except (GitCommandError, InvalidGitRepositoryError, NoSuchPathError) as e:
50
+ # 捕捉 Git 相關錯誤 / Catch Git-related errors
31
51
  self._log_audit(
32
- f"ERROR: Git operation failed: {remote_url} -> {local_path}\n{str(e)}\nTraceback:\n{traceback.format_exc()}")
52
+ f"ERROR: Git operation failed: {remote_url} -> {local_path}\n"
53
+ f"{str(e)}\nTraceback:\n{traceback.format_exc()}"
54
+ )
33
55
  raise RuntimeError(f"Git operation failed: {str(e)}") from e
56
+
34
57
  except Exception as e:
58
+ # 捕捉其他未預期錯誤 / Catch unexpected errors
35
59
  self._log_audit(
36
- f"ERROR: Unexpected error during clone: {remote_url} -> {local_path}\n{str(e)}\nTraceback:\n{traceback.format_exc()}")
60
+ f"ERROR: Unexpected error during clone: {remote_url} -> {local_path}\n"
61
+ f"{str(e)}\nTraceback:\n{traceback.format_exc()}"
62
+ )
37
63
  raise RuntimeError(f"Unexpected error during clone: {str(e)}") from e
38
64
 
39
65
  def _log_audit(self, message: str):
40
66
  """
41
67
  Append an audit log entry with timestamp.
68
+ 在稽核日誌中加入帶有時間戳的紀錄。
69
+
70
+ :param message: The message to log
71
+ 要記錄的訊息
42
72
  """
43
73
  timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
44
74
  log_entry = f"[{timestamp}] {message}\n"
@@ -46,5 +76,6 @@ class GitCloneHandler:
46
76
  with open(self.audit_log_path, "a", encoding="utf-8") as f:
47
77
  f.write(log_entry)
48
78
  except Exception:
79
+ # 確保日誌寫入失敗不會影響主要流程
49
80
  # Never let audit logging failure break the flow
50
- pass
81
+ pass
@@ -7,33 +7,69 @@ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapp
7
7
 
8
8
 
9
9
  class BrowserDownloadWindow(QWidget):
10
+ """
11
+ A window widget to display details of a browser download.
12
+ 瀏覽器下載視窗,用來顯示下載的詳細資訊。
13
+ """
10
14
 
11
15
  def __init__(self, download_instance: QWebEngineDownloadRequest):
16
+ """
17
+ Initialize the download window with a given QWebEngineDownloadRequest.
18
+ 使用指定的 QWebEngineDownloadRequest 初始化下載視窗。
19
+ """
12
20
  super().__init__()
21
+ # 記錄初始化訊息到 logger
13
22
  jeditor_logger.info("Init BrowserDownloadWindow "
14
23
  f"download_instance: {download_instance}")
24
+
25
+ # 設定視窗屬性:當視窗關閉時自動刪除
15
26
  self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
27
+
28
+ # 建立垂直方向的 BoxLayout
16
29
  self.box_layout = QBoxLayout(QBoxLayout.Direction.TopToBottom)
30
+
31
+ # 建立文字框來顯示下載細節,並設為唯讀
17
32
  self.show_download_detail_plaintext = QPlainTextEdit()
18
33
  self.show_download_detail_plaintext.setReadOnly(True)
34
+
35
+ # 設定視窗標題,支援多語言
19
36
  self.setWindowTitle(language_wrapper.language_word_dict.get("browser_download_detail"))
37
+
38
+ # 儲存下載實例
20
39
  self.download_instance = download_instance
21
- self.download_instance.isFinishedChanged.connect(self.print_finish)
22
- self.download_instance.interruptReasonChanged.connect(self.print_interrupt)
23
- self.download_instance.stateChanged.connect(self.print_state)
40
+
41
+ # 綁定下載事件到對應的處理函式
42
+ self.download_instance.isFinishedChanged.connect(self.print_finish) # 當下載完成時
43
+ self.download_instance.interruptReasonChanged.connect(self.print_interrupt) # 當下載被中斷時
44
+ self.download_instance.stateChanged.connect(self.print_state) # 當下載狀態改變時
45
+
46
+ # 接受下載請求,開始下載
24
47
  self.download_instance.accept()
48
+
49
+ # 將文字框加入版面配置
25
50
  self.box_layout.addWidget(self.show_download_detail_plaintext)
26
51
  self.setLayout(self.box_layout)
27
52
 
28
-
29
53
  def print_finish(self):
54
+ """
55
+ Slot function triggered when download finishes.
56
+ 當下載完成時觸發,將完成狀態輸出到 logger 與文字框。
57
+ """
30
58
  jeditor_logger.info("BrowserDownloadWindow Print Download is Finished")
31
59
  self.show_download_detail_plaintext.appendPlainText(str(self.download_instance.isFinished()))
32
60
 
33
61
  def print_interrupt(self):
62
+ """
63
+ Slot function triggered when download is interrupted.
64
+ 當下載被中斷時觸發,將中斷原因輸出到 logger 與文字框。
65
+ """
34
66
  jeditor_logger.info("BrowserDownloadWindow Print interruptReason")
35
67
  self.show_download_detail_plaintext.appendPlainText(str(self.download_instance.interruptReason()))
36
68
 
37
69
  def print_state(self):
70
+ """
71
+ Slot function triggered when download state changes.
72
+ 當下載狀態改變時觸發,將狀態輸出到 logger 與文字框。
73
+ """
38
74
  jeditor_logger.info("BrowserDownloadWindow Print State")
39
- self.show_download_detail_plaintext.appendPlainText(str(self.download_instance.state()))
75
+ self.show_download_detail_plaintext.appendPlainText(str(self.download_instance.state()))
@@ -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)
@@ -8,28 +8,69 @@ from je_editor.utils.logging.loggin_instance import jeditor_logger
8
8
 
9
9
 
10
10
  class BrowserView(QWebEngineView):
11
+ """
12
+ A custom QWebEngineView that supports file downloads and manages download windows.
13
+ 自訂的 QWebEngineView,支援檔案下載並管理下載視窗。
14
+ """
11
15
 
12
16
  def __init__(self, start_url: str = "https://www.google.com/"):
17
+ """
18
+ Initialize the browser view with a start URL.
19
+ 使用指定的起始網址初始化瀏覽器視圖。
20
+ """
13
21
  super().__init__()
22
+ # 記錄初始化訊息
14
23
  jeditor_logger.info("Init BrowserView "
15
24
  f"start_url: {start_url}")
25
+
26
+ # 設定初始網址
16
27
  self.setUrl(start_url)
28
+
29
+ # 儲存下載請求的清單
17
30
  self.download_list: List[QWebEngineDownloadRequest] = list()
31
+
32
+ # 儲存下載視窗的清單
18
33
  self.download_window_list: List[BrowserDownloadWindow] = list()
34
+
35
+ # 綁定下載事件:當有下載請求時觸發 download_file
19
36
  self.page().profile().downloadRequested.connect(self.download_file)
20
37
 
21
38
  def download_file(self, download_instance: QWebEngineDownloadRequest):
39
+ """
40
+ Handle a new download request.
41
+ 當有新的下載請求時觸發:
42
+ - 將下載請求加入清單
43
+ - 建立並顯示下載細節視窗
44
+ """
22
45
  jeditor_logger.info("Download File "
23
46
  f"download_instance: {download_instance}")
47
+
48
+ # 加入下載請求到清單
24
49
  self.download_list.append(download_instance)
50
+
51
+ # 建立下載細節視窗
25
52
  download_detail_window = BrowserDownloadWindow(download_instance)
53
+
54
+ # 加入視窗到清單並顯示
26
55
  self.download_window_list.append(download_detail_window)
27
56
  download_detail_window.show()
28
57
 
29
58
  def closeEvent(self, event) -> None:
59
+ """
60
+ Handle the close event of the browser view.
61
+ 當瀏覽器視窗關閉時:
62
+ - 取消所有進行中的下載
63
+ - 關閉所有下載細節視窗
64
+ """
30
65
  jeditor_logger.info(f"BrowserView closeEvent event: {event}")
66
+
67
+ # 取消所有下載
31
68
  for download_instance in self.download_list:
32
69
  download_instance.cancel()
70
+
71
+ # 關閉所有下載視窗
33
72
  for download_window in self.download_window_list:
34
73
  download_window.close()
35
- super().closeEvent(event)
74
+
75
+ # 呼叫父類別的 closeEvent,確保正常關閉
76
+ super().closeEvent(event)