pyqt-code-editor 0.0.54__tar.gz → 0.0.55__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.
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/PKG-INFO +2 -2
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/__init__.py +1 -1
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/_settings.py +1 -0
- pyqt_code_editor-0.0.55/pyqt_code_editor/components/sigmund/__init__.py +1 -0
- pyqt_code_editor-0.0.55/pyqt_code_editor/components/sigmund/confirm_run_code_dialog.py +59 -0
- pyqt_code_editor-0.0.54/pyqt_code_editor/components/sigmund.py → pyqt_code_editor-0.0.55/pyqt_code_editor/components/sigmund/editor_workspace.py +5 -26
- pyqt_code_editor-0.0.55/pyqt_code_editor/components/sigmund/sigmund.py +28 -0
- pyqt_code_editor-0.0.55/pyqt_code_editor/components/sigmund/sigmund_analyst_chat_widget.py +35 -0
- pyqt_code_editor-0.0.55/pyqt_code_editor/components/sigmund/sigmund_analyst_widget.py +191 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/merge_undo_actions.py +1 -6
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/readme.md +1 -1
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/COPYING +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyproject.toml +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/app.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/code_editors/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/code_editors/code_editors.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/code_editors/languages/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/code_editors/languages/generic.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/code_editors/languages/javascript.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/code_editors/languages/markdown.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/code_editors/languages/python.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/code_editors/languages/text.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/editor_panel.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/find_in_files.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/jupyter_console.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/project_explorer.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/settings_panel.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/workspace_explorer.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/environment_manager.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/auto_indent.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/auto_pair.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/base.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/check.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/comment.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/complete.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/execute.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/file_link.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/highlight_matching_brackets.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/line_number.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/python_auto_indent.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/search_replace.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/shortcuts.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/smart_backspace_delete.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/symbols.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/theme.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/widgets/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/zoom.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/sigmund-analyst.png +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/signal_router.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/syntax_highlighters/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/syntax_highlighters/languages/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/syntax_highlighters/languages/generic.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/syntax_highlighters/languages/python.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/syntax_highlighters/syntax_highlighter.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/themes.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/utils/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/utils/languages/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/utils/languages/python/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/utils/languages/python/_auto_indent.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/utils/languages/python/_extract_cells.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/utils/languages/python/_mask_str_in_code.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/watchdog.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/calltip_widget.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/completion_popup.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/dock.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/quick_open_dialog.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/quick_open_file_dialog.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/quick_symbol_dialog.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/search_replace_frame.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/tab_splitter.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/tabbed_editor.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/languages/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/languages/generic.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/languages/python.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/manager.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/process.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/__init__.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/codestral.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/jedi.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/ruff.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/sigmund.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/symbol.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/tree_sitter.py +0 -0
- {pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/settings.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyqt_code_editor
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.55
|
|
4
4
|
Summary: Fully featured code-editor widgets for PyQt
|
|
5
5
|
Keywords: code editor,pyqt,ide
|
|
6
6
|
Author-email: Sebastiaan Mathôt <s.mathot@cogsci.nl>
|
|
@@ -32,7 +32,7 @@ Project-URL: Source, https://github.com/open-cogsci/sigmund-analyst
|
|
|
32
32
|
|
|
33
33
|
# PyQt Code Editor / Sigmund Analyst
|
|
34
34
|
|
|
35
|
-
Copyright 2025 Sebastiaan Mathôt
|
|
35
|
+
Copyright 2025-2026 Sebastiaan Mathôt
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
## About
|
|
@@ -120,6 +120,7 @@ class Settings(QObject):
|
|
|
120
120
|
check_interval_delay = SettingProperty(5000, "Checking")
|
|
121
121
|
|
|
122
122
|
# Sigmund provider
|
|
123
|
+
sigmund_review_actions = SettingProperty(True, "Sigmund")
|
|
123
124
|
sigmund_max_context = SettingProperty(2000, "Sigmund")
|
|
124
125
|
sigmund_fim_endpoint = SettingProperty('http://localhost:5000/code_completion/fim', "Sigmund")
|
|
125
126
|
sigmund_token = SettingProperty(None, "Sigmund")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .sigmund import Sigmund
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from qtpy.QtWidgets import QVBoxLayout, QDialogButtonBox, QLabel, \
|
|
2
|
+
QSizePolicy, QDialog, QSplitter
|
|
3
|
+
from qtpy.QtCore import Qt
|
|
4
|
+
import logging
|
|
5
|
+
from sigmund_qtwidget.chat_browser import ChatBrowser
|
|
6
|
+
from pyqt_code_editor.code_editors import create_editor
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
MAX_MESSAGE_HEIGHT = 200
|
|
11
|
+
|
|
12
|
+
class ConfirmRunCodeDialog(QDialog):
|
|
13
|
+
"""
|
|
14
|
+
A modal dialog that displays code to be executed and asks the user to confirm or cancel.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, parent, code: str, language: str):
|
|
18
|
+
super().__init__(parent)
|
|
19
|
+
|
|
20
|
+
self.setWindowTitle(f"Confirm {language} code execution")
|
|
21
|
+
|
|
22
|
+
layout = QVBoxLayout()
|
|
23
|
+
self.setLayout(layout)
|
|
24
|
+
|
|
25
|
+
# Use ChatBrowser to display the AI message
|
|
26
|
+
self.message_browser = ChatBrowser(self)
|
|
27
|
+
self.message_browser.append_message(
|
|
28
|
+
'ai', 'Sigmund wants to run the following code:')
|
|
29
|
+
self.message_browser.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
30
|
+
self.message_browser.setMaximumHeight(MAX_MESSAGE_HEIGHT)
|
|
31
|
+
|
|
32
|
+
# Create code editor for the code to be executed
|
|
33
|
+
self.code_view = create_editor(language=language)
|
|
34
|
+
self.code_view.setPlainText(code)
|
|
35
|
+
self.code_view.setReadOnly(True)
|
|
36
|
+
|
|
37
|
+
# Create a vertical splitter to hold the message browser and the code
|
|
38
|
+
splitter = QSplitter(Qt.Vertical)
|
|
39
|
+
splitter.addWidget(self.message_browser)
|
|
40
|
+
splitter.addWidget(self.code_view)
|
|
41
|
+
layout.addWidget(splitter)
|
|
42
|
+
|
|
43
|
+
# The disclaimer label
|
|
44
|
+
disclaimer_label = QLabel(
|
|
45
|
+
"Carefully review the code before execution. Executing code can have unintended consequences.",
|
|
46
|
+
self
|
|
47
|
+
)
|
|
48
|
+
disclaimer_label.setWordWrap(True)
|
|
49
|
+
disclaimer_label.setObjectName('control-warning')
|
|
50
|
+
disclaimer_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
51
|
+
layout.addWidget(disclaimer_label)
|
|
52
|
+
|
|
53
|
+
# Dialog buttons
|
|
54
|
+
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
|
55
|
+
self.button_box.accepted.connect(self.accept)
|
|
56
|
+
self.button_box.rejected.connect(self.reject)
|
|
57
|
+
layout.addWidget(self.button_box)
|
|
58
|
+
|
|
59
|
+
self.resize(800, 600)
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
from sigmund_qtwidget.sigmund_widget import SigmundWidget
|
|
2
|
-
from ..widgets import Dock
|
|
3
|
-
from .. import watchdog, settings
|
|
4
1
|
import textwrap
|
|
5
2
|
import logging
|
|
6
3
|
logger = logging.getLogger(__name__)
|
|
@@ -76,11 +73,14 @@ class EditorWorkspace:
|
|
|
76
73
|
|
|
77
74
|
def set(self, content, language):
|
|
78
75
|
text_cursor = self._editor.textCursor()
|
|
76
|
+
text_cursor.beginEditBlock()
|
|
79
77
|
if text_cursor.hasSelection():
|
|
80
78
|
text_cursor.insertText(content)
|
|
81
|
-
self._editor.setTextCursor(text_cursor)
|
|
82
79
|
else:
|
|
83
|
-
|
|
80
|
+
text_cursor.select(text_cursor.Document)
|
|
81
|
+
text_cursor.insertText(content)
|
|
82
|
+
text_cursor.endEditBlock()
|
|
83
|
+
self._editor.setTextCursor(text_cursor)
|
|
84
84
|
self._editor.set_modified(True)
|
|
85
85
|
|
|
86
86
|
def has_changed(self, content, language):
|
|
@@ -98,24 +98,3 @@ class EditorWorkspace:
|
|
|
98
98
|
if content is None:
|
|
99
99
|
return ''
|
|
100
100
|
return content
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
class Sigmund(Dock):
|
|
104
|
-
def __init__(self, parent, editor_panel):
|
|
105
|
-
super().__init__('Sigmund', parent)
|
|
106
|
-
self.setObjectName("sigmund")
|
|
107
|
-
workspace = EditorWorkspace(editor_panel)
|
|
108
|
-
self.sigmund_widget = SigmundWidget(self, application='Sigmund Analyst')
|
|
109
|
-
self.sigmund_widget.setStyleSheet(f'font-size: {settings.font_size}pt')
|
|
110
|
-
self.sigmund_widget.set_workspace_manager(workspace)
|
|
111
|
-
self.setWidget(self.sigmund_widget)
|
|
112
|
-
|
|
113
|
-
def setVisible(self, visible):
|
|
114
|
-
if visible:
|
|
115
|
-
logger.info('starting sigmund connector')
|
|
116
|
-
self.sigmund_widget.start_server()
|
|
117
|
-
watchdog.register_subprocess(self.sigmund_widget.server_pid)
|
|
118
|
-
else:
|
|
119
|
-
logger.info('stopping sigmund connector')
|
|
120
|
-
self.sigmund_widget.stop_server()
|
|
121
|
-
super().setVisible(visible)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from ...widgets import Dock
|
|
2
|
+
from ... import watchdog, settings
|
|
3
|
+
from .editor_workspace import EditorWorkspace
|
|
4
|
+
from .sigmund_analyst_widget import SigmundAnalystWidget
|
|
5
|
+
import logging
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Sigmund(Dock):
|
|
10
|
+
def __init__(self, parent, editor_panel):
|
|
11
|
+
super().__init__('Sigmund', parent)
|
|
12
|
+
self.setObjectName("sigmund")
|
|
13
|
+
workspace = EditorWorkspace(editor_panel)
|
|
14
|
+
self.sigmund_widget = SigmundAnalystWidget(
|
|
15
|
+
self, editor_panel)
|
|
16
|
+
self.sigmund_widget.setStyleSheet(f'font-size: {settings.font_size}pt')
|
|
17
|
+
self.sigmund_widget.set_workspace_manager(workspace)
|
|
18
|
+
self.setWidget(self.sigmund_widget)
|
|
19
|
+
|
|
20
|
+
def setVisible(self, visible):
|
|
21
|
+
if visible:
|
|
22
|
+
logger.info('starting sigmund connector')
|
|
23
|
+
self.sigmund_widget.start_server()
|
|
24
|
+
watchdog.register_subprocess(self.sigmund_widget.server_pid)
|
|
25
|
+
else:
|
|
26
|
+
logger.info('stopping sigmund connector')
|
|
27
|
+
self.sigmund_widget.stop_server()
|
|
28
|
+
super().setVisible(visible)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from qtpy.QtWidgets import QCheckBox, QWidget, QVBoxLayout
|
|
2
|
+
from sigmund_qtwidget.chat_widget import ChatWidget
|
|
3
|
+
from ... import settings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SigmundAnalystChatWidget(ChatWidget):
|
|
7
|
+
"""Extended chat widget with Sigmund Analyst-specific options"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, parent=None):
|
|
10
|
+
super().__init__(parent)
|
|
11
|
+
# Create a container for the checkboxes
|
|
12
|
+
options_container = QWidget()
|
|
13
|
+
options_layout = QVBoxLayout(options_container)
|
|
14
|
+
options_layout.setContentsMargins(5, 5, 5, 5)
|
|
15
|
+
options_layout.setSpacing(2)
|
|
16
|
+
# "Review proposed changes" checkbox
|
|
17
|
+
self._review_actions_checkbox = QCheckBox(
|
|
18
|
+
"Review Sigmund's actions (recommended)")
|
|
19
|
+
self._review_actions_checkbox.setChecked(
|
|
20
|
+
settings.sigmund_review_actions)
|
|
21
|
+
self._review_actions_checkbox.stateChanged.connect(
|
|
22
|
+
self._on_review_actions_changed)
|
|
23
|
+
options_layout.addWidget(self._review_actions_checkbox)
|
|
24
|
+
# Insert the options container before the input container
|
|
25
|
+
main_layout = self.layout()
|
|
26
|
+
main_layout.insertWidget(main_layout.count(), options_container)
|
|
27
|
+
|
|
28
|
+
def _on_review_actions_changed(self, state):
|
|
29
|
+
"""Store the review changes setting."""
|
|
30
|
+
settings.sigmund_review_actions = bool(state)
|
|
31
|
+
|
|
32
|
+
def append_message(self, msg_type, text, scroll=True):
|
|
33
|
+
if msg_type == 'ai' and '(Suggesting IDE action)' in text:
|
|
34
|
+
text = '⚙️ Sigmund is working …'
|
|
35
|
+
self._chat_browser.append_message(msg_type, text, scroll)
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
from sigmund_qtwidget.sigmund_widget import SigmundWidget
|
|
4
|
+
from qtpy.QtCore import QEventLoop, QTimer
|
|
5
|
+
from qtpy.QtWidgets import QMessageBox, QDialog
|
|
6
|
+
from .sigmund_analyst_chat_widget import SigmundAnalystChatWidget
|
|
7
|
+
from .confirm_run_code_dialog import ConfirmRunCodeDialog
|
|
8
|
+
from ... import settings
|
|
9
|
+
import logging
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
ACTION_CANCELLED = 'I do not approve this action.'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SigmundAnalystWidget(SigmundWidget):
|
|
16
|
+
"""Extends the default Sigmund widget with Sigmund Analyst-specific
|
|
17
|
+
functionality.
|
|
18
|
+
"""
|
|
19
|
+
chat_widget_cls = SigmundAnalystChatWidget
|
|
20
|
+
|
|
21
|
+
def __init__(self, parent, editor_panel):
|
|
22
|
+
super().__init__(parent, application='Sigmund Analyst')
|
|
23
|
+
try:
|
|
24
|
+
self._app = parent.parent()
|
|
25
|
+
self._jupyter_console = self._app._jupyter_console
|
|
26
|
+
# Connect the code_executed signal to a slot
|
|
27
|
+
except AttributeError:
|
|
28
|
+
logger.warning('No Jupyter console found.')
|
|
29
|
+
self._jupyter_console = None
|
|
30
|
+
else:
|
|
31
|
+
self._jupyter_console.execution_complete.connect(
|
|
32
|
+
self._handle_execution_result)
|
|
33
|
+
self._editor_panel = editor_panel
|
|
34
|
+
self._transient_settings = {
|
|
35
|
+
'tool_ide_open_file': 'true',
|
|
36
|
+
'tool_ide_execute_code': 'true'
|
|
37
|
+
}
|
|
38
|
+
# Store execution results
|
|
39
|
+
self._execution_result = None
|
|
40
|
+
self._execution_loop = None
|
|
41
|
+
|
|
42
|
+
def _confirm_action(self, action_type, details):
|
|
43
|
+
"""Show a confirmation dialog for the proposed action.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
action_type (str): Type of action (e.g., "open file", "execute code")
|
|
47
|
+
details (str): Detailed information about the action
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
bool: True if action is approved, False otherwise
|
|
51
|
+
"""
|
|
52
|
+
if not settings.sigmund_review_actions:
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
if action_type == "open file":
|
|
56
|
+
return QMessageBox.question(
|
|
57
|
+
self,
|
|
58
|
+
"Open file",
|
|
59
|
+
f"Sigmund wants to open: \n\n{details}",
|
|
60
|
+
QMessageBox.Ok | QMessageBox.Cancel,
|
|
61
|
+
QMessageBox.Ok
|
|
62
|
+
) == QMessageBox.Ok
|
|
63
|
+
dialog = ConfirmRunCodeDialog(
|
|
64
|
+
self,
|
|
65
|
+
action_type,
|
|
66
|
+
details
|
|
67
|
+
)
|
|
68
|
+
return dialog.exec_() == QDialog.Accepted
|
|
69
|
+
|
|
70
|
+
def _handle_execution_result(self, output, result):
|
|
71
|
+
"""Slot that handles the code_executed signal."""
|
|
72
|
+
self._execution_result = (output, result)
|
|
73
|
+
if self._execution_loop and self._execution_loop.isRunning():
|
|
74
|
+
time.sleep(0.5) # Wait for errors to appear
|
|
75
|
+
self._execution_loop.quit()
|
|
76
|
+
|
|
77
|
+
def run_command_open_file(self, path):
|
|
78
|
+
if settings.sigmund_review_actions:
|
|
79
|
+
if not QMessageBox.question(self,
|
|
80
|
+
"Confirm open file",
|
|
81
|
+
f"Sigmund wants to open: \n\n{path}",
|
|
82
|
+
QMessageBox.Ok | QMessageBox.Cancel,
|
|
83
|
+
QMessageBox.Ok) == QMessageBox.Ok:
|
|
84
|
+
return ACTION_CANCELLED
|
|
85
|
+
try:
|
|
86
|
+
self._editor_panel.open_file(path)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
return f'Failed to open file: {e}'
|
|
89
|
+
return f'Opened file: {path}'
|
|
90
|
+
|
|
91
|
+
def run_command_execute_code(self, code, language):
|
|
92
|
+
if settings.sigmund_review_actions:
|
|
93
|
+
if not ConfirmRunCodeDialog(self, code, language).exec() \
|
|
94
|
+
== ConfirmRunCodeDialog.Accepted:
|
|
95
|
+
return ACTION_CANCELLED
|
|
96
|
+
if self._jupyter_console is None:
|
|
97
|
+
return 'Code execution not supported.'
|
|
98
|
+
self._app._toggle_dock_widget(self._jupyter_console, show=True)
|
|
99
|
+
self._execution_result = None
|
|
100
|
+
self._execution_loop = QEventLoop()
|
|
101
|
+
QTimer.singleShot(30000, self._execution_loop.quit) # 30 second timeout
|
|
102
|
+
self._jupyter_console.execute_code(code)
|
|
103
|
+
self._execution_loop.exec_()
|
|
104
|
+
self._execution_loop = None
|
|
105
|
+
if self._execution_result is None:
|
|
106
|
+
return 'Code execution timed out or failed to return a result.'
|
|
107
|
+
try:
|
|
108
|
+
console_content = self._jupyter_console.get_current_console().jupyter_widget._control.toPlainText()
|
|
109
|
+
except Exception as e:
|
|
110
|
+
return f'Failed to get results from console content: {e}'
|
|
111
|
+
console_content = ''
|
|
112
|
+
console_content = '\n'.join(console_content.splitlines()[-100:])
|
|
113
|
+
return f'# Console output (may be truncated):\n\n{console_content}'
|
|
114
|
+
@property
|
|
115
|
+
def _editor(self):
|
|
116
|
+
return self._editor_panel.active_editor()
|
|
117
|
+
|
|
118
|
+
def send_user_message(self, text, *args, **kwargs):
|
|
119
|
+
current_path = self._editor.code_editor_file_path
|
|
120
|
+
# If the editor is not linked to a file, simply use the working
|
|
121
|
+
# directory.
|
|
122
|
+
if current_path is None:
|
|
123
|
+
current_path = '[unsaved file]'
|
|
124
|
+
working_directory = os.getcwd()
|
|
125
|
+
else:
|
|
126
|
+
working_directory = os.path.dirname(current_path)
|
|
127
|
+
|
|
128
|
+
# Initialize with default value
|
|
129
|
+
working_directory_contents = "(No directory contents available)"
|
|
130
|
+
|
|
131
|
+
# Get directory contents with priority to top-level items
|
|
132
|
+
top_level = []
|
|
133
|
+
all_items = []
|
|
134
|
+
|
|
135
|
+
# Only wrap file system operations in try-except
|
|
136
|
+
try:
|
|
137
|
+
# Get top-level items (depth=1)
|
|
138
|
+
top_level = [f for f in os.listdir(working_directory)
|
|
139
|
+
if os.path.isfile(os.path.join(working_directory, f))
|
|
140
|
+
or os.path.isdir(os.path.join(working_directory, f))]
|
|
141
|
+
|
|
142
|
+
# Then get deeper items if we haven't reached our limit
|
|
143
|
+
remaining_slots = max(0, 20 - len(top_level))
|
|
144
|
+
if remaining_slots > 0:
|
|
145
|
+
for root, dirs, files in os.walk(working_directory):
|
|
146
|
+
# Skip the top level since we already have it
|
|
147
|
+
if root == working_directory:
|
|
148
|
+
continue
|
|
149
|
+
# Add files first
|
|
150
|
+
for file in files:
|
|
151
|
+
if remaining_slots <= 0:
|
|
152
|
+
break
|
|
153
|
+
rel_path = os.path.relpath(os.path.join(root, file), working_directory)
|
|
154
|
+
all_items.append(f".\\{rel_path}")
|
|
155
|
+
remaining_slots -= 1
|
|
156
|
+
# Then add directories if we still have space
|
|
157
|
+
for dir in dirs:
|
|
158
|
+
if remaining_slots <= 0:
|
|
159
|
+
break
|
|
160
|
+
rel_path = os.path.relpath(os.path.join(root, dir), working_directory)
|
|
161
|
+
all_items.append(f".\\{rel_path}\\")
|
|
162
|
+
remaining_slots -= 1
|
|
163
|
+
|
|
164
|
+
# Combine top level and deeper items
|
|
165
|
+
working_directory_contents = "\n".join(
|
|
166
|
+
sorted([f".\\{item}" if os.path.isfile(os.path.join(working_directory, item))
|
|
167
|
+
else f".\\{item}\\" for item in top_level] +
|
|
168
|
+
all_items)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
if len(top_level) + len(all_items) > 20:
|
|
172
|
+
working_directory_contents += "\n... (additional files truncated)"
|
|
173
|
+
|
|
174
|
+
except PermissionError:
|
|
175
|
+
working_directory_contents = "(Could not read some directory contents - permission denied)"
|
|
176
|
+
except OSError as e:
|
|
177
|
+
working_directory_contents = f"(Could not read directory contents: {str(e)})"
|
|
178
|
+
|
|
179
|
+
system_prompt = f'''## Working directory
|
|
180
|
+
|
|
181
|
+
The workspace corresponds to the following file: {current_path}
|
|
182
|
+
The working directory is: {working_directory}
|
|
183
|
+
|
|
184
|
+
Overview of working directory:
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
{working_directory_contents}
|
|
188
|
+
```
|
|
189
|
+
'''
|
|
190
|
+
self._transient_system_prompt = system_prompt
|
|
191
|
+
super().send_user_message(text, *args, **kwargs)
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/merge_undo_actions.py
RENAMED
|
@@ -20,11 +20,6 @@ class MergeUndoActions:
|
|
|
20
20
|
key = event.key()
|
|
21
21
|
modifiers = event.modifiers()
|
|
22
22
|
|
|
23
|
-
is_navigation = (
|
|
24
|
-
key in navigation_keys
|
|
25
|
-
and not (modifiers & Qt.KeyboardModifier.ShiftModifier)
|
|
26
|
-
)
|
|
27
|
-
|
|
28
23
|
# Treat platform "command" modifier as well (Control on Windows/Linux, Meta on macOS)
|
|
29
24
|
ctrl_or_meta = Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.MetaModifier
|
|
30
25
|
has_ctrl_or_meta = bool(modifiers & ctrl_or_meta)
|
|
@@ -39,7 +34,7 @@ class MergeUndoActions:
|
|
|
39
34
|
key == Qt.Key.Key_Y
|
|
40
35
|
)
|
|
41
36
|
|
|
42
|
-
if
|
|
37
|
+
if key in navigation_keys or is_undo or is_redo:
|
|
43
38
|
# Do not wrap these in an edit block
|
|
44
39
|
super().keyPressEvent(event)
|
|
45
40
|
return
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/code_editors/__init__.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/code_editors/code_editors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/code_editors/languages/text.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/editor_panel.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/find_in_files.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/jupyter_console.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/project_explorer.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/components/settings_panel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/python_auto_indent.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/search_replace.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/mixins/widgets/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/syntax_highlighters/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/utils/languages/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/calltip_widget.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/completion_popup.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/quick_open_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/quick_symbol_dialog.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/search_replace_frame.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/tab_splitter.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/widgets/tabbed_editor.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/languages/__init__.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/languages/generic.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/languages/python.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/__init__.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/codestral.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/jedi.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/ruff.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/sigmund.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/symbol.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.54 → pyqt_code_editor-0.0.55}/pyqt_code_editor/worker/providers/tree_sitter.py
RENAMED
|
File without changes
|
|
File without changes
|