pyqt-code-editor 0.0.34__tar.gz → 0.0.35__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 (79) hide show
  1. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/PKG-INFO +1 -1
  2. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/__init__.py +1 -1
  3. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/code_editors/languages/python.py +1 -1
  4. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/auto_pair.py +34 -27
  5. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/providers/jedi.py +68 -10
  6. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/COPYING +0 -0
  7. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyproject.toml +0 -0
  8. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/_settings.py +0 -0
  9. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/app.py +0 -0
  10. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/code_editors/__init__.py +0 -0
  11. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/code_editors/code_editors.py +0 -0
  12. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/code_editors/languages/__init__.py +0 -0
  13. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/code_editors/languages/generic.py +0 -0
  14. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/code_editors/languages/markdown.py +0 -0
  15. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/code_editors/languages/text.py +0 -0
  16. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/components/__init__.py +0 -0
  17. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/components/editor_panel.py +0 -0
  18. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/components/find_in_files.py +0 -0
  19. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/components/jupyter_console.py +0 -0
  20. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/components/project_explorer.py +0 -0
  21. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/components/settings_panel.py +0 -0
  22. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/components/sigmund.py +0 -0
  23. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/components/workspace_explorer.py +0 -0
  24. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/environment_manager.py +0 -0
  25. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/__init__.py +0 -0
  26. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/auto_indent.py +0 -0
  27. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/base.py +0 -0
  28. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/check.py +0 -0
  29. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/comment.py +0 -0
  30. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/complete.py +0 -0
  31. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/execute.py +0 -0
  32. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/file_link.py +0 -0
  33. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/line_number.py +0 -0
  34. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/python_auto_indent.py +0 -0
  35. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/search_replace.py +0 -0
  36. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/shortcuts.py +0 -0
  37. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/smart_backspace_delete.py +0 -0
  38. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/symbols.py +0 -0
  39. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/theme.py +0 -0
  40. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/widgets/__init__.py +0 -0
  41. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/mixins/zoom.py +0 -0
  42. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/sigmund-analyst.png +0 -0
  43. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/signal_router.py +0 -0
  44. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/syntax_highlighters/__init__.py +0 -0
  45. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/syntax_highlighters/languages/__init__.py +0 -0
  46. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/syntax_highlighters/languages/generic.py +0 -0
  47. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/syntax_highlighters/languages/python.py +0 -0
  48. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/syntax_highlighters/syntax_highlighter.py +0 -0
  49. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/themes.py +0 -0
  50. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/utils/__init__.py +0 -0
  51. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/utils/languages/__init__.py +0 -0
  52. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/utils/languages/python/__init__.py +0 -0
  53. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/utils/languages/python/_auto_indent.py +0 -0
  54. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/utils/languages/python/_extract_cells.py +0 -0
  55. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/utils/languages/python/_mask_str_in_code.py +0 -0
  56. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/watchdog.py +0 -0
  57. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/widgets/__init__.py +0 -0
  58. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/widgets/calltip_widget.py +0 -0
  59. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/widgets/completion_popup.py +0 -0
  60. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/widgets/dock.py +0 -0
  61. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/widgets/quick_open_dialog.py +0 -0
  62. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/widgets/quick_open_file_dialog.py +0 -0
  63. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/widgets/quick_symbol_dialog.py +0 -0
  64. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/widgets/search_replace_frame.py +0 -0
  65. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/widgets/tab_splitter.py +0 -0
  66. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/widgets/tabbed_editor.py +0 -0
  67. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/__init__.py +0 -0
  68. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/languages/__init__.py +0 -0
  69. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/languages/generic.py +0 -0
  70. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/languages/python.py +0 -0
  71. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/manager.py +0 -0
  72. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/process.py +0 -0
  73. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/providers/__init__.py +0 -0
  74. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/providers/codestral.py +0 -0
  75. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/providers/ruff.py +0 -0
  76. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/providers/sigmund.py +0 -0
  77. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/providers/symbol.py +0 -0
  78. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/pyqt_code_editor/worker/settings.py +0 -0
  79. {pyqt_code_editor-0.0.34 → pyqt_code_editor-0.0.35}/readme.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqt_code_editor
3
- Version: 0.0.34
3
+ Version: 0.0.35
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.34'
5
+ __version__ = '0.0.35'
@@ -7,7 +7,7 @@ from pyqt_code_editor.mixins import Complete, PythonAutoIndent, \
7
7
 
8
8
  class Editor(LineNumber, Zoom, SmartBackspaceDelete, PythonAutoPair, Complete,
9
9
  Comment, PythonAutoIndent, SearchReplace, FileLink, Execute,
10
- Theme, Check, Shortcuts, Symbols, Base,
10
+ Theme, Shortcuts, Symbols, Base, Check,
11
11
  QPlainTextEdit):
12
12
 
13
13
  code_editor_language = 'python'
@@ -42,6 +42,7 @@ class AutoPair:
42
42
  # 1) Handle backspace for removing empty pairs like (|)
43
43
  if event.key() == Qt.Key_Backspace:
44
44
  if self._handle_auto_pair_backspace():
45
+ super().keyPressEvent(event)
45
46
  return
46
47
 
47
48
  typed_char = event.text()
@@ -93,39 +94,45 @@ class AutoPair:
93
94
  def _handle_auto_pair_backspace(self) -> bool:
94
95
  """
95
96
  If the cursor is between an exact open_seq and close_seq pair (e.g., '(|)'),
96
- remove the full pair. Returns True if handled, False if not.
97
+ remove the following close_seq. The preceding open_seq will be removed
98
+ by the standard backspace. Only works for pairs of single characters.
99
+ Returns True if handled, False if not.
97
100
  """
98
101
  cursor = self.textCursor()
99
102
  pos = cursor.position()
100
- text = self.toPlainText()
101
-
103
+
104
+ # We need at least one character before and after cursor
105
+ if pos < 1 or cursor.atEnd():
106
+ return False
107
+
108
+ # Get the character before the cursor
109
+ cursor.setPosition(pos - 1)
110
+ cursor.setPosition(pos, cursor.KeepAnchor)
111
+ char_before = cursor.selectedText()
112
+
113
+ # Get the character after the cursor
114
+ cursor.setPosition(pos)
115
+ cursor.setPosition(pos + 1, cursor.KeepAnchor)
116
+ char_after = cursor.selectedText()
117
+
118
+ # Check if they form a pair
102
119
  for pair in self.PAIRS:
103
120
  open_seq = pair["open_seq"]
104
121
  close_seq = pair["close_seq"]
105
- if open_seq and close_seq:
106
- l_open = len(open_seq)
107
- l_close = len(close_seq)
108
-
109
- # Make sure there's enough room before and after the cursor
110
- if pos >= l_open and pos + l_close <= len(text):
111
- behind = text[pos - l_open : pos]
112
- ahead = text[pos : pos + l_close]
113
-
114
- # If the cursor is right between open_seq and close_seq
115
- # with nothing typed in between (e.g. (|))
116
- if behind == open_seq and ahead == close_seq:
117
- c = self.textCursor()
118
- c.beginEditBlock()
119
- # Remove from open_seq start to close_seq end
120
- c.setPosition(pos - l_open)
121
- for _ in range(l_open + l_close):
122
- c.deleteChar()
123
- c.endEditBlock()
124
-
125
- # Move cursor to the old open_seq start
126
- c.setPosition(pos - l_open)
127
- self.setTextCursor(c)
128
- return True
122
+
123
+ # Only handle single-character pairs
124
+ if len(open_seq) != 1 or len(close_seq) != 1:
125
+ continue
126
+
127
+ if char_before == open_seq and char_after == close_seq:
128
+ logger.info("deleting closing bracket")
129
+ # Delete only the closing character
130
+ cursor.setPosition(pos)
131
+ cursor.deleteChar()
132
+
133
+ # The standard backspace will handle the opening character
134
+ return True
135
+
129
136
  return False
130
137
 
131
138
 
@@ -7,18 +7,77 @@ logger = logging.getLogger(__name__)
7
7
  env_cache = {}
8
8
 
9
9
 
10
- def _signature_to_html(signature) -> str:
10
+ def _estimate_width(text):
11
+ """Helper function to estimate display width of signature
12
+ (rough approximation)
13
+ """
14
+ # Remove HTML tags for width calculation
15
+ clean_text = re.sub(r'<[^>]+>', '', text)
16
+ return len(clean_text)
17
+
18
+
19
+ def _wrap_params(params, width_limit):
20
+ """Helper function to wrap signature parameters"""
21
+ lines = []
22
+ current_line = []
23
+ current_width = 1 # Starting with "("
24
+
25
+ for i, param in enumerate(params):
26
+ param_width = _estimate_width(param)
27
+ # Add comma and space width for non-first parameters
28
+ if current_line:
29
+ param_width += 2
30
+
31
+ # Check if adding this param would exceed width
32
+ if current_line and current_width + param_width > width_limit:
33
+ # Start a new line
34
+ lines.append(", ".join(current_line))
35
+ current_line = [param]
36
+ current_width = 6 + param_width # Indent width + param
37
+ else:
38
+ current_line.append(param)
39
+ current_width += param_width
40
+
41
+ if current_line:
42
+ lines.append(", ".join(current_line))
43
+
44
+ return lines
45
+
46
+
47
+ def _signature_to_html(signature, max_width: int, max_lines: int) -> str:
11
48
  """Convert jedi.Script.get_signatures() output to nicely formatted HTML."""
12
49
  param_strs = []
13
50
  for param in signature.params:
14
51
  param_strs.append(param.to_string())
15
- # Build the signature line
16
- sig_line = ",<br />&nbsp;".join(param_strs)
52
+
53
+ # Get return annotation if available
17
54
  return_hint = ""
18
- # If there's a known return annotation, append it
19
55
  if hasattr(signature, "annotation_string") and signature.annotation_string:
20
- return_hint = f"-> {signature.annotation_string}"
21
- return f"({sig_line}){return_hint}"
56
+ return_hint = f" -> {signature.annotation_string}"
57
+
58
+ # Wrap parameters based on max_width
59
+ wrapped_lines = _wrap_params(param_strs,
60
+ max_width - _estimate_width(return_hint) - 2)
61
+
62
+ # Limit number of lines
63
+ if len(wrapped_lines) > max_lines:
64
+ # Keep first max_lines-1 lines and add ellipsis
65
+ wrapped_lines = wrapped_lines[:max_lines-1]
66
+ if wrapped_lines:
67
+ wrapped_lines[-1] += ", ..."
68
+
69
+ # Build the final HTML
70
+ if len(wrapped_lines) == 0:
71
+ return f"(){return_hint}"
72
+ elif len(wrapped_lines) == 1:
73
+ return f"({wrapped_lines[0]}){return_hint}"
74
+ else:
75
+ # Multi-line format with indentation
76
+ html_lines = [f"({wrapped_lines[0]},"]
77
+ for line in wrapped_lines[1:-1]:
78
+ html_lines.append(f"<br />&nbsp;{line},")
79
+ html_lines.append(f"<br />&nbsp;{wrapped_lines[-1]}){return_hint}")
80
+ return "".join(html_lines)
22
81
 
23
82
 
24
83
  def _prepare_jedi_script(code: str, cursor_position: int, path: str | None,
@@ -84,8 +143,7 @@ def jedi_complete(code: str, cursor_position: int, path: str | None,
84
143
 
85
144
 
86
145
  def jedi_signatures(code: str, cursor_position: int, path: str | None,
87
- multiline: bool = False, max_width: int = 40,
88
- max_lines: int = 10,
146
+ max_width: int = 40, max_lines: int = 10,
89
147
  env_path: str | None = None,
90
148
  prefix: str | None = None) -> list[str]:
91
149
  """
@@ -100,7 +158,7 @@ def jedi_signatures(code: str, cursor_position: int, path: str | None,
100
158
  logger.info("No code or cursor_position=0; cannot fetch calltip.")
101
159
  return None
102
160
 
103
- logger.info("Starting Jedi calltip request (multiline=%r).", multiline)
161
+ logger.info("Starting Jedi calltip request")
104
162
  script, line_no, column_no = _prepare_jedi_script(code, cursor_position,
105
163
  path, env_path, prefix)
106
164
 
@@ -111,7 +169,7 @@ def jedi_signatures(code: str, cursor_position: int, path: str | None,
111
169
 
112
170
  results = []
113
171
  for sig in signatures:
114
- results.append(_signature_to_html(sig))
172
+ results.append(_signature_to_html(sig, max_width, max_lines))
115
173
 
116
174
  logger.info("Got %d signature(s) from Jedi.", len(results))
117
175
  return results or None