pyqt-code-editor 0.0.60__tar.gz → 0.0.62__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.
Files changed (91) hide show
  1. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/PKG-INFO +1 -1
  2. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/__init__.py +1 -1
  3. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/sigmund/sigmund_analyst_widget.py +57 -3
  4. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/search_replace.py +21 -9
  5. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/COPYING +0 -0
  6. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyproject.toml +0 -0
  7. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/_settings.py +0 -0
  8. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/app.py +0 -0
  9. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/code_editors/__init__.py +0 -0
  10. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/code_editors/code_editors.py +0 -0
  11. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/code_editors/languages/__init__.py +0 -0
  12. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/code_editors/languages/generic.py +0 -0
  13. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/code_editors/languages/javascript.py +0 -0
  14. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/code_editors/languages/markdown.py +0 -0
  15. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/code_editors/languages/python.py +0 -0
  16. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/code_editors/languages/text.py +0 -0
  17. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/__init__.py +0 -0
  18. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/editor_panel.py +0 -0
  19. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/find_in_files.py +0 -0
  20. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/jupyter_console/__init__.py +0 -0
  21. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/jupyter_console/jupyter_console.py +0 -0
  22. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/jupyter_console/jupyter_console_tab.py +0 -0
  23. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/jupyter_console/kernel_spec_manager.py +0 -0
  24. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/project_explorer.py +0 -0
  25. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/settings_panel.py +0 -0
  26. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/sigmund/__init__.py +0 -0
  27. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/sigmund/confirm_run_code_dialog.py +0 -0
  28. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/sigmund/editor_workspace.py +0 -0
  29. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/sigmund/sigmund.py +0 -0
  30. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/sigmund/sigmund_analyst_chat_widget.py +0 -0
  31. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/components/workspace_explorer.py +0 -0
  32. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/environment_manager.py +0 -0
  33. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/__init__.py +0 -0
  34. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/auto_indent.py +0 -0
  35. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/auto_pair.py +0 -0
  36. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/base.py +0 -0
  37. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/check.py +0 -0
  38. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/comment.py +0 -0
  39. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/complete.py +0 -0
  40. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/execute.py +0 -0
  41. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/file_link.py +0 -0
  42. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/highlight_matching_brackets.py +0 -0
  43. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/line_number.py +0 -0
  44. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/merge_undo_actions.py +0 -0
  45. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/python_auto_indent.py +0 -0
  46. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/shortcuts.py +0 -0
  47. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/smart_backspace_delete.py +0 -0
  48. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/symbols.py +0 -0
  49. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/theme.py +0 -0
  50. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/widgets/__init__.py +0 -0
  51. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/mixins/zoom.py +0 -0
  52. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/sigmund-analyst.png +0 -0
  53. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/signal_router.py +0 -0
  54. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/syntax_highlighters/__init__.py +0 -0
  55. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/syntax_highlighters/languages/__init__.py +0 -0
  56. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/syntax_highlighters/languages/generic.py +0 -0
  57. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/syntax_highlighters/languages/python.py +0 -0
  58. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/syntax_highlighters/syntax_highlighter.py +0 -0
  59. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/themes.py +0 -0
  60. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/utils/__init__.py +0 -0
  61. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/utils/languages/__init__.py +0 -0
  62. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/utils/languages/python/__init__.py +0 -0
  63. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/utils/languages/python/_auto_indent.py +0 -0
  64. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/utils/languages/python/_extract_cells.py +0 -0
  65. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/utils/languages/python/_mask_str_in_code.py +0 -0
  66. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/watchdog.py +0 -0
  67. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/widgets/__init__.py +0 -0
  68. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/widgets/calltip_widget.py +0 -0
  69. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/widgets/completion_popup.py +0 -0
  70. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/widgets/dock.py +0 -0
  71. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/widgets/quick_open_dialog.py +0 -0
  72. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/widgets/quick_open_file_dialog.py +0 -0
  73. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/widgets/quick_symbol_dialog.py +0 -0
  74. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/widgets/search_replace_frame.py +0 -0
  75. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/widgets/tab_splitter.py +0 -0
  76. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/widgets/tabbed_editor.py +0 -0
  77. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/__init__.py +0 -0
  78. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/languages/__init__.py +0 -0
  79. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/languages/generic.py +0 -0
  80. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/languages/python.py +0 -0
  81. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/manager.py +0 -0
  82. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/process.py +0 -0
  83. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/providers/__init__.py +0 -0
  84. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/providers/codestral.py +0 -0
  85. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/providers/jedi.py +0 -0
  86. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/providers/ruff.py +0 -0
  87. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/providers/sigmund.py +0 -0
  88. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/providers/symbol.py +0 -0
  89. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/providers/tree_sitter.py +0 -0
  90. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/pyqt_code_editor/worker/settings.py +0 -0
  91. {pyqt_code_editor-0.0.60 → pyqt_code_editor-0.0.62}/readme.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqt_code_editor
3
- Version: 0.0.60
3
+ Version: 0.0.62
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>
@@ -2,4 +2,4 @@
2
2
 
3
3
  from ._settings import settings
4
4
 
5
- __version__ = '0.0.60'
5
+ __version__ = '0.0.62'
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import subprocess
2
3
  import time
3
4
  from sigmund_qtwidget.sigmund_widget import SigmundWidget
4
5
  from qtpy.QtCore import QEventLoop, QTimer
@@ -35,7 +36,9 @@ class SigmundAnalystWidget(SigmundWidget):
35
36
  'tool_ide_open_file': 'true',
36
37
  'tool_ide_execute_code': 'true',
37
38
  'tool_ide_inspect_files': 'true',
38
- 'tool_ide_list_files': 'true'
39
+ 'tool_ide_list_files': 'true',
40
+ 'tool_ide_write_file': 'true',
41
+ 'tool_ide_execute_shell_command': 'true'
39
42
  }
40
43
  # Store execution results
41
44
  self._execution_result = None
@@ -205,6 +208,57 @@ class SigmundAnalystWidget(SigmundWidget):
205
208
  <output>
206
209
  {self._execution_result[0]}
207
210
  </output>
211
+ '''
212
+
213
+ def run_command_write_file(self, path, content, encoding='utf-8'):
214
+ path = self._resolve_path(path)
215
+ language = os.path.splitext(path)[1].lstrip('.') or 'text'
216
+ if settings.sigmund_review_actions:
217
+ if not ConfirmRunCodeDialog(self, content, language).exec() \
218
+ == ConfirmRunCodeDialog.Accepted:
219
+ return ACTION_CANCELLED
220
+ try:
221
+ dir_name = os.path.dirname(path)
222
+ if dir_name:
223
+ os.makedirs(dir_name, exist_ok=True)
224
+ with open(path, 'w', encoding=encoding) as f:
225
+ f.write(content)
226
+ except Exception as e:
227
+ return f'Failed to write file: {e}'
228
+ return f'Wrote file: {path}'
229
+
230
+ def run_command_execute_shell_command(self, shell_command, timeout=30,
231
+ working_directory=None):
232
+ if settings.sigmund_review_actions:
233
+ if not ConfirmRunCodeDialog(self, shell_command, 'shell').exec() \
234
+ == ConfirmRunCodeDialog.Accepted:
235
+ return ACTION_CANCELLED
236
+ try:
237
+ result = subprocess.run(
238
+ shell_command,
239
+ shell=True,
240
+ capture_output=True,
241
+ text=True,
242
+ timeout=timeout,
243
+ cwd=working_directory if working_directory else self._working_directory
244
+ )
245
+ output = result.stdout
246
+ if result.stderr:
247
+ output += f'\nSTDERR:\n{result.stderr}'
248
+ if not output.strip():
249
+ output = '(no output)'
250
+ except subprocess.TimeoutExpired:
251
+ return f'Shell command timed out after {timeout} seconds.'
252
+ except Exception as e:
253
+ return f'Failed to execute shell command: {e}'
254
+ return f'''The following shell command was executed:
255
+ <executed_command>
256
+ {shell_command}
257
+ </executed_command>
258
+
259
+ <output>
260
+ {output}
261
+ </output>
208
262
  '''
209
263
 
210
264
  @property
@@ -273,7 +327,7 @@ class SigmundAnalystWidget(SigmundWidget):
273
327
  working_directory_contents = f"(Could not read directory contents: {str(e)})"
274
328
 
275
329
  system_prompt = f'''## Working directory
276
-
330
+
277
331
  IMPORTANT: Do NOT call inspect_files() with {current_path}, because it is already in the workspace.
278
332
 
279
333
  The workspace corresponds to the following file: {current_path}
@@ -288,4 +342,4 @@ Overview of working directory:
288
342
  self._transient_system_prompt = system_prompt
289
343
  self._working_directory = working_directory
290
344
  super().send_user_message(text, *args, **kwargs)
291
- self._attachments = None
345
+ self._attachments = None
@@ -65,27 +65,39 @@ class SearchReplaceHighlighter(QSyntaxHighlighter):
65
65
  # Find all matches in the entire document
66
66
  self._matches = []
67
67
  full_text = self.document().toPlainText()
68
+
69
+ # Qt uses UTF-16 internally, while Python's re works on Unicode code
70
+ # points. Characters outside the Basic Multilingual Plane (e.g. most
71
+ # emojis, ord > 0xFFFF) are a single Python code point but take up
72
+ # TWO UTF-16 code units (a surrogate pair). This mismatch causes
73
+ # highlight offsets to be wrong whenever such a character appears
74
+ # before a match. We therefore precompute a lookup table that maps
75
+ # each Python string index to its corresponding UTF-16 offset.
76
+ utf16_pos = [0] * (len(full_text) + 1)
77
+ for i, ch in enumerate(full_text):
78
+ utf16_pos[i + 1] = utf16_pos[i] + (2 if ord(ch) > 0xFFFF else 1)
79
+
68
80
  for match in regex.finditer(full_text):
69
- start = match.start()
70
- end = match.end()
81
+ utf16_start = utf16_pos[match.start()]
82
+ utf16_end = utf16_pos[match.end()]
71
83
 
72
- # Convert character positions to block/position
73
- block = self.document().findBlock(start)
84
+ # Convert UTF-16 positions to block/position
85
+ block = self.document().findBlock(utf16_start)
74
86
  block_number = block.blockNumber()
75
87
  block_start = block.position()
76
88
  block_end = block_start + block.length() - 1
77
89
 
78
90
  # Handle matches that span multiple blocks
79
- while start <= block_end and end > block_start:
80
- match_start_in_block = max(start - block_start, 0)
81
- match_end_in_block = min(end - block_start, block.length() - 1)
91
+ while utf16_start <= block_end and utf16_end > block_start:
92
+ match_start_in_block = max(utf16_start - block_start, 0)
93
+ match_end_in_block = min(utf16_end - block_start, block.length() - 1)
82
94
  match_length = match_end_in_block - match_start_in_block
83
95
 
84
96
  if match_length > 0:
85
97
  self._matches.append((block_number, match_start_in_block, match_length))
86
98
 
87
99
  # Move to next block if match spans multiple blocks
88
- if end > block_end:
100
+ if utf16_end > block_end:
89
101
  block = block.next()
90
102
  if not block.isValid():
91
103
  break
@@ -104,7 +116,6 @@ class SearchReplaceHighlighter(QSyntaxHighlighter):
104
116
  if match_block == block_number:
105
117
  self.setFormat(start, length, self.highlight_format)
106
118
 
107
-
108
119
  class SearchReplace:
109
120
  """
110
121
  A mixin for QPlainTextEdit that floats a search/replace widget
@@ -195,6 +206,7 @@ class SearchReplace:
195
206
  self._searchFrame.showSearchReplace()
196
207
  self._searchFrame.setVisible(True)
197
208
  self._searchFrame.findEdit.setFocus()
209
+ self._searchFrame.findEdit.selectAll()
198
210
  self.updateSearchPosition()
199
211
 
200
212
  # Swap out original highlighter for the search highlighter