pyqt-code-editor 0.0.28__tar.gz → 0.0.30__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 (80) hide show
  1. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/PKG-INFO +2 -1
  2. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyproject.toml +2 -1
  3. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/__init__.py +1 -1
  4. pyqt_code_editor-0.0.30/pyqt_code_editor/environment_manager.py +37 -0
  5. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/check.py +5 -5
  6. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/complete.py +4 -2
  7. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/languages/generic.py +2 -2
  8. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/languages/python.py +4 -3
  9. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/process.py +109 -28
  10. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/providers/codestral.py +6 -1
  11. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/providers/jedi.py +12 -5
  12. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/providers/ruff.py +9 -2
  13. pyqt_code_editor-0.0.28/pyqt_code_editor/environment_manager.py +0 -37
  14. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/COPYING +0 -0
  15. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/_settings.py +0 -0
  16. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/app.py +0 -0
  17. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/code_editors/__init__.py +0 -0
  18. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/code_editors/code_editors.py +0 -0
  19. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/code_editors/languages/__init__.py +0 -0
  20. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/code_editors/languages/generic.py +0 -0
  21. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/code_editors/languages/markdown.py +0 -0
  22. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/code_editors/languages/python.py +0 -0
  23. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/code_editors/languages/text.py +0 -0
  24. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/components/__init__.py +0 -0
  25. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/components/editor_panel.py +0 -0
  26. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/components/find_in_files.py +0 -0
  27. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/components/jupyter_console.py +0 -0
  28. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/components/project_explorer.py +0 -0
  29. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/components/settings_panel.py +0 -0
  30. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/components/sigmund.py +0 -0
  31. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/components/workspace_explorer.py +0 -0
  32. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/__init__.py +0 -0
  33. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/auto_indent.py +0 -0
  34. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/auto_pair.py +0 -0
  35. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/base.py +0 -0
  36. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/comment.py +0 -0
  37. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/execute.py +0 -0
  38. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/file_link.py +0 -0
  39. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/line_number.py +0 -0
  40. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/python_auto_indent.py +0 -0
  41. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/search_replace.py +0 -0
  42. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/shortcuts.py +0 -0
  43. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/smart_backspace_delete.py +0 -0
  44. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/symbols.py +0 -0
  45. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/theme.py +0 -0
  46. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/widgets/__init__.py +0 -0
  47. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/mixins/zoom.py +0 -0
  48. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/sigmund-analyst.png +0 -0
  49. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/signal_router.py +0 -0
  50. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/syntax_highlighters/__init__.py +0 -0
  51. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/syntax_highlighters/languages/__init__.py +0 -0
  52. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/syntax_highlighters/languages/generic.py +0 -0
  53. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/syntax_highlighters/languages/python.py +0 -0
  54. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/syntax_highlighters/syntax_highlighter.py +0 -0
  55. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/themes.py +0 -0
  56. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/utils/__init__.py +0 -0
  57. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/utils/languages/__init__.py +0 -0
  58. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/utils/languages/python/__init__.py +0 -0
  59. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/utils/languages/python/_auto_indent.py +0 -0
  60. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/utils/languages/python/_extract_cells.py +0 -0
  61. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/utils/languages/python/_mask_str_in_code.py +0 -0
  62. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/watchdog.py +0 -0
  63. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/widgets/__init__.py +0 -0
  64. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/widgets/calltip_widget.py +0 -0
  65. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/widgets/completion_popup.py +0 -0
  66. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/widgets/dock.py +0 -0
  67. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/widgets/quick_open_dialog.py +0 -0
  68. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/widgets/quick_open_file_dialog.py +0 -0
  69. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/widgets/quick_symbol_dialog.py +0 -0
  70. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/widgets/search_replace_frame.py +0 -0
  71. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/widgets/tab_splitter.py +0 -0
  72. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/widgets/tabbed_editor.py +0 -0
  73. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/__init__.py +0 -0
  74. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/languages/__init__.py +0 -0
  75. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/manager.py +0 -0
  76. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/providers/__init__.py +0 -0
  77. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/providers/sigmund.py +0 -0
  78. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/providers/symbol.py +0 -0
  79. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/pyqt_code_editor/worker/settings.py +0 -0
  80. {pyqt_code_editor-0.0.28 → pyqt_code_editor-0.0.30}/readme.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqt_code_editor
3
- Version: 0.0.28
3
+ Version: 0.0.30
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>
@@ -10,6 +10,7 @@ License-File: COPYING
10
10
  Requires-Dist: chardet
11
11
  Requires-Dist: detect-indent
12
12
  Requires-Dist: jedi
13
+ Requires-Dist: httpx<0.29.0
13
14
  Requires-Dist: mistralai
14
15
  Requires-Dist: pathspec
15
16
  Requires-Dist: psutil
@@ -15,6 +15,7 @@ dependencies = [
15
15
  "chardet",
16
16
  "detect-indent",
17
17
  "jedi",
18
+ "httpx<0.29.0",
18
19
  "mistralai",
19
20
  "pathspec",
20
21
  "psutil",
@@ -36,4 +37,4 @@ Source = "https://github.com/open-cogsci/sigmund-analyst"
36
37
 
37
38
  [project.scripts]
38
39
  sigmund-analyst = "pyqt_code_editor.app:launch_app"
39
- pyqt-code-editor = "pyqt_code_editor.app:launch_app"
40
+ pyqt-code-editor = "pyqt_code_editor.app:launch_app"
@@ -2,4 +2,4 @@
2
2
 
3
3
  from ._settings import settings
4
4
 
5
- __version__ = '0.0.28'
5
+ __version__ = '0.0.30'
@@ -0,0 +1,37 @@
1
+ import os
2
+ from qtpy.QtCore import Signal, QObject
3
+ import logging
4
+ logger = logging.getLogger(__name__)
5
+
6
+ class EnvironmentManager(QObject):
7
+ """Singleton class to share the current environment and language between all
8
+ components of the app.
9
+ """
10
+ environment_changed = Signal(str, str, str, str)
11
+
12
+ def __init__(self):
13
+ super().__init__()
14
+ self.name = None
15
+ self.path = None
16
+ self.language = None
17
+ self.prefix = None
18
+
19
+ @property
20
+ def current_environment(self):
21
+ return self.name, self.path, self.language
22
+
23
+ def set_environment(self, name, path, language, prefix=None):
24
+ if path != self.path:
25
+ # The path may not exist if the environment is simply python or
26
+ # python3, in which case we do not need to specify it at all.
27
+ if not os.path.exists(path):
28
+ path = None
29
+ self.name = name
30
+ self.path = path
31
+ self.language = language
32
+ self.prefix = prefix
33
+ self.environment_changed.emit(name, path, language, prefix)
34
+
35
+
36
+ # Singleton instance
37
+ environment_manager = EnvironmentManager()
@@ -1,7 +1,7 @@
1
- from qtpy.QtWidgets import QWidget, QPlainTextEdit
2
- from qtpy.QtCore import Qt, QRect, QPoint, QSize, QTimer
3
- from qtpy.QtGui import QPainter, QColor, QBrush
4
1
  import logging
2
+ from qtpy.QtCore import QTimer
3
+ from qtpy.QtGui import QColor
4
+ from ..environment_manager import environment_manager
5
5
  logger = logging.getLogger(__name__)
6
6
 
7
7
 
@@ -50,8 +50,8 @@ class Check:
50
50
  self.send_worker_request(
51
51
  action='check',
52
52
  code=code,
53
- language=getattr(self, "code_editor_language", "python")
54
- )
53
+ language=getattr(self, "code_editor_language", "python"),
54
+ prefix=environment_manager.prefix)
55
55
 
56
56
  def handle_worker_result(self, action: str, result):
57
57
  """
@@ -276,7 +276,8 @@ class Complete:
276
276
  multiline=multiline,
277
277
  path=self.code_editor_file_path,
278
278
  language=self.code_editor_language,
279
- env_path=environment_manager.path)
279
+ env_path=environment_manager.path,
280
+ prefix=environment_manager.prefix)
280
281
 
281
282
  def _cm_request_calltip(self):
282
283
  """Send a calltip request."""
@@ -288,7 +289,8 @@ class Complete:
288
289
  cursor_pos=cursor_pos,
289
290
  path=self.code_editor_file_path,
290
291
  language=self.code_editor_language,
291
- env_path=environment_manager.path)
292
+ env_path=environment_manager.path,
293
+ prefix=environment_manager.prefix)
292
294
 
293
295
  def _cm_hide_calltip(self):
294
296
  """Hide the calltip widget."""
@@ -1,9 +1,9 @@
1
1
  from ..providers import symbol, codestral
2
2
 
3
- def complete(code, cursor_pos, path, multiline, full, env_path):
3
+ def complete(code, cursor_pos, path, multiline, full, env_path, prefix):
4
4
  if full or multiline:
5
5
  completions = codestral.codestral_complete(
6
- code, cursor_pos, multiline=multiline)
6
+ code, cursor_pos, multiline=multiline, prefix=prefix)
7
7
  else:
8
8
  completions = []
9
9
  if not multiline:
@@ -1,15 +1,16 @@
1
1
  from ..providers import jedi, codestral, ruff
2
2
 
3
3
 
4
- def complete(code, cursor_pos, path, multiline, full, env_path):
4
+ def complete(code, cursor_pos, path, multiline, full, env_path, prefix):
5
5
  if full or multiline:
6
6
  codestral_completions = codestral.codestral_complete(
7
- code, cursor_pos, multiline=multiline)
7
+ code, cursor_pos, multiline=multiline, prefix=prefix)
8
8
  else:
9
9
  codestral_completions = []
10
10
  if not multiline:
11
11
  jedi_completions = jedi.jedi_complete(code, cursor_pos, path=path,
12
- env_path=env_path)
12
+ env_path=env_path,
13
+ prefix=prefix)
13
14
  else:
14
15
  jedi_completions = []
15
16
  # If there is at least one jedi completion, it is always insert at first.
@@ -8,13 +8,98 @@ worker_functions_cache = {}
8
8
 
9
9
  def main_worker_process_function(request_queue, result_queue):
10
10
  """
11
- Runs in a separate process, handling requests in dict form.
12
- Supported actions include:
13
- - 'complete': triggers code completion
14
- - 'calltip': fetches calltip/signature info
15
- - 'check': fetches code check/ linting info
16
- - 'setting': updates settings in the 'settings' module
17
- - 'quit': shuts down the worker
11
+ Main worker process for handling editor backend requests.
12
+
13
+ Runs in a separate process, continuously processing requests from a queue
14
+ and sending results back through another queue. Supports code completion,
15
+ calltips, symbol extraction, code checking, and settings management.
16
+
17
+ Parameters
18
+ ----------
19
+ request_queue : multiprocessing.Queue
20
+ Queue containing request dictionaries to process
21
+ result_queue : multiprocessing.Queue
22
+ Queue where results are sent back to the main process
23
+
24
+ Request Format
25
+ --------------
26
+ Each request must be a dictionary with the following structure:
27
+
28
+ Required fields:
29
+ action : str
30
+ The action to perform. One of: 'complete', 'calltip', 'symbols',
31
+ 'check', 'set_settings', 'quit'
32
+
33
+ Optional fields (depending on action):
34
+ language : str, default 'text'
35
+ Programming language for syntax-aware processing
36
+ code : str, default ''
37
+ Source code content to analyze
38
+ cursor_pos : int, default 0
39
+ Character position of cursor in the code
40
+ path : str, optional
41
+ File path for context-aware processing
42
+ multiline : bool, default False
43
+ Whether to enable multiline completion mode
44
+ full : bool, default False
45
+ Whether to return full completion details
46
+ env_path : str, optional
47
+ Path to Python environment for context
48
+ prefix : str, optional
49
+ Prefix string for filtering results
50
+ settings : dict, optional
51
+ Settings to update (only for 'set_settings' action)
52
+
53
+ Response Format
54
+ ---------------
55
+ Results are dictionaries placed on result_queue with action-specific content:
56
+
57
+ For 'complete' action:
58
+ {
59
+ 'action': 'complete',
60
+ 'completions': list or None,
61
+ 'cursor_pos': int,
62
+ 'multiline': bool,
63
+ 'full': bool
64
+ }
65
+
66
+ For 'calltip' action:
67
+ {
68
+ 'action': 'calltip',
69
+ 'signatures': list or None,
70
+ 'cursor_pos': int
71
+ }
72
+
73
+ For 'symbols' action:
74
+ {
75
+ 'action': 'symbols',
76
+ 'symbols': list
77
+ }
78
+
79
+ For 'check' action:
80
+ {
81
+ 'action': 'check',
82
+ 'messages': dict
83
+ }
84
+
85
+ Behavior
86
+ --------
87
+ - Runs indefinitely until receiving a 'quit' action
88
+ - Dynamically loads language-specific worker modules on first use
89
+ - Caches imported worker modules for efficiency
90
+ - Falls back to generic worker functions if language-specific module unavailable
91
+ - Skips invalid requests (None, non-dict, or missing 'action' field)
92
+ - Logs all major operations and errors
93
+ - For 'set_settings' action, updates the settings module attributes directly
94
+ - Worker modules must provide functions: complete, calltip, symbols, check
95
+ (functions can be None if not supported for that language)
96
+
97
+ Notes
98
+ -----
99
+ This function is designed to run in a separate process to avoid blocking
100
+ the main editor thread during potentially slow operations like code analysis.
101
+ Language-specific worker modules are expected to be in the 'languages'
102
+ subpackage.
18
103
  """
19
104
  logger.info("Started completion worker.")
20
105
  while True:
@@ -25,13 +110,21 @@ def main_worker_process_function(request_queue, result_queue):
25
110
  if request is None:
26
111
  logger.info("Received None request (possibly legacy or invalid). Skipping.")
27
112
  continue
28
-
29
113
  # Expect a dict with at least an 'action' field
30
114
  if not isinstance(request, dict):
31
115
  logger.info(f"Invalid request type: {type(request)}. Skipping.")
32
- continue
33
-
116
+ continue
117
+ # Most of the parameters apply to multiple actions, so we extract them
118
+ # here.
34
119
  action = request.get('action', None)
120
+ language = request.get('language', 'text')
121
+ code = request.get('code', '')
122
+ cursor_pos = request.get('cursor_pos', 0)
123
+ path = request.get('path', None)
124
+ multiline = request.get('multiline', False)
125
+ full = request.get('full', False)
126
+ env_path = request.get('env_path', None)
127
+ prefix = request.get('prefix', None)
35
128
  if action is None:
36
129
  logger.info("Request is missing 'action' field. Skipping.")
37
130
  continue
@@ -46,8 +139,7 @@ def main_worker_process_function(request_queue, result_queue):
46
139
  break
47
140
 
48
141
  # Load the worker functions depending on the language. We store the
49
- # imported module in a cache for efficiency
50
- language = request.get('language', 'text')
142
+ # imported module in a cache for efficiency
51
143
  if language not in worker_functions_cache:
52
144
  try:
53
145
  worker_functions = importlib.import_module(
@@ -66,16 +158,10 @@ def main_worker_process_function(request_queue, result_queue):
66
158
  if worker_functions.complete is None:
67
159
  completions = None
68
160
  else:
69
- code = request.get('code', '')
70
- cursor_pos = request.get('cursor_pos', 0)
71
- path = request.get('path', None)
72
- multiline = request.get('multiline', False)
73
- full = request.get('full', False)
74
- env_path = request.get('env_path', None)
75
161
  logger.info(f"Performing code completion: language='{language}', multiline={multiline}, path={path}, env_path={env_path}")
76
162
  completions = worker_functions.complete(
77
163
  code, cursor_pos, path=path, multiline=multiline, full=full,
78
- env_path=env_path)
164
+ env_path=env_path, prefix=prefix)
79
165
  if not completions:
80
166
  logger.info("No completions")
81
167
  else:
@@ -89,16 +175,13 @@ def main_worker_process_function(request_queue, result_queue):
89
175
  })
90
176
 
91
177
  elif action == 'calltip':
92
- cursor_pos = request.get('cursor_pos', 0)
93
178
  if worker_functions.calltip is None:
94
179
  signatures = None
95
180
  else:
96
- code = request.get('code', '')
97
- path = request.get('path', None)
98
- env_path = request.get('env_path', None)
99
181
  logger.info(f"Performing calltip: language='{language}', path={path}, env_path={env_path}")
100
182
  signatures = worker_functions.calltip(
101
- code, cursor_pos, path=path, env_path=env_path)
183
+ code, cursor_pos, path=path, env_path=env_path,
184
+ prefix=prefix)
102
185
  if signatures is None:
103
186
  logger.info("No calltip signatures. Sending result back.")
104
187
  else:
@@ -113,7 +196,6 @@ def main_worker_process_function(request_queue, result_queue):
113
196
  if worker_functions.symbols is None:
114
197
  symbols_results = []
115
198
  else:
116
- code = request.get('code', '')
117
199
  symbols_results = worker_functions.symbols(code)
118
200
  result_queue.put({
119
201
  'action': 'symbols',
@@ -124,11 +206,10 @@ def main_worker_process_function(request_queue, result_queue):
124
206
  if worker_functions.check is None:
125
207
  check_results = {}
126
208
  else:
127
- code = request.get('code', '')
128
- check_results = worker_functions.check(code)
209
+ check_results = worker_functions.check(code, prefix=prefix)
129
210
  result_queue.put({
130
211
  'action': 'check',
131
212
  'messages': check_results
132
213
  })
133
214
 
134
- logger.info("Completion worker has shut down.")
215
+ logger.info("Completion worker has shut down.")
@@ -6,7 +6,8 @@ client = None
6
6
 
7
7
 
8
8
  def codestral_complete(code: str, cursor_pos: int,
9
- multiline: bool = False) -> list[str]:
9
+ multiline: bool = False,
10
+ prefix: str | None = None) -> list[str]:
10
11
  global client
11
12
 
12
13
  if not settings.codestral_api_key:
@@ -22,6 +23,10 @@ def codestral_complete(code: str, cursor_pos: int,
22
23
  start = max(0, cursor_pos - settings.codestral_max_context)
23
24
  end = cursor_pos + settings.codestral_max_context
24
25
  prompt = code[start: cursor_pos]
26
+ if prefix:
27
+ if not prefix.endswith('\n'):
28
+ prefix += '\n'
29
+ prompt = prefix + prompt
25
30
  suffix = code[cursor_pos: end]
26
31
 
27
32
  request = dict(
@@ -21,11 +21,16 @@ def _signature_to_html(signature) -> str:
21
21
 
22
22
 
23
23
  def _prepare_jedi_script(code: str, cursor_position: int, path: str | None,
24
- env_path: str | None):
24
+ env_path: str | None, prefix: str | None = None):
25
25
  """
26
26
  Prepare a Jedi Script object and calculate line_no/column_no from the
27
27
  given code and cursor_position. Returns (script, line_no, column_no).
28
28
  """
29
+ if prefix:
30
+ if not prefix.endswith('\n'):
31
+ prefix += '\n'
32
+ code = prefix + code
33
+ cursor_position += len(prefix)
29
34
  # Convert the flat cursor_position into line & column (1-based indexing for Jedi)
30
35
  line_no = code[:cursor_position].count('\n') + 1
31
36
  last_newline_idx = code.rfind('\n', 0, cursor_position)
@@ -43,7 +48,8 @@ def _prepare_jedi_script(code: str, cursor_position: int, path: str | None,
43
48
 
44
49
 
45
50
  def jedi_complete(code: str, cursor_position: int, path: str | None,
46
- env_path: str | None = None) -> list[str]:
51
+ env_path: str | None = None,
52
+ prefix: str | None = None) -> list[str]:
47
53
  """
48
54
  Perform Python-specific completion using Jedi. Returns a list of possible completions
49
55
  for the text at the given cursor position, or None if no completion is found.
@@ -61,7 +67,7 @@ def jedi_complete(code: str, cursor_position: int, path: str | None,
61
67
  return []
62
68
  # Go Jedi!
63
69
  script, line_no, column_no = _prepare_jedi_script(code, cursor_position,
64
- path, env_path)
70
+ path, env_path, prefix)
65
71
  completions = script.complete(line=line_no, column=column_no)
66
72
  if not completions:
67
73
  return []
@@ -75,7 +81,8 @@ def jedi_complete(code: str, cursor_position: int, path: str | None,
75
81
  def jedi_signatures(code: str, cursor_position: int, path: str | None,
76
82
  multiline: bool = False, max_width: int = 40,
77
83
  max_lines: int = 10,
78
- env_path: str | None = None):
84
+ env_path: str | None = None,
85
+ prefix: str | None = None) -> list[str]:
79
86
  """
80
87
  Retrieve function signatures (calltips) from Jedi given the current cursor position.
81
88
  Returns a list of strings describing each signature, or None if none.
@@ -90,7 +97,7 @@ def jedi_signatures(code: str, cursor_position: int, path: str | None,
90
97
 
91
98
  logger.info("Starting Jedi calltip request (multiline=%r).", multiline)
92
99
  script, line_no, column_no = _prepare_jedi_script(code, cursor_position,
93
- path, env_path)
100
+ path, env_path, prefix)
94
101
 
95
102
  signatures = script.get_signatures(line=line_no, column=column_no)
96
103
  if not signatures:
@@ -6,13 +6,20 @@ import logging
6
6
  logger = logging.getLogger(__name__)
7
7
 
8
8
 
9
- def ruff_check(code: str) -> list[dict]:
9
+ def ruff_check(code: str, prefix: str | None = None) -> list[dict]:
10
10
  """
11
11
  Lints Python source code using Ruff.
12
12
 
13
13
  Args:
14
14
  code (str): The Python source code to lint.
15
15
  """
16
+ if prefix:
17
+ if not prefix.endswith('\n'):
18
+ prefix += '\n'
19
+ code = prefix + code
20
+ prefix_line_offset = prefix.count('\n')
21
+ else:
22
+ prefix_line_offset = 0
16
23
  with tempfile.NamedTemporaryFile(delete=False, suffix=".py", mode="w") as tmp_file:
17
24
  tmp_file.write(code)
18
25
  tmp_file_path = tmp_file.name
@@ -35,7 +42,7 @@ def ruff_check(code: str) -> list[dict]:
35
42
  return {}
36
43
  formatted_result = {}
37
44
  for message in result:
38
- row = message['location']['row']
45
+ row = message['location']['row'] - prefix_line_offset
39
46
  if row not in formatted_result:
40
47
  formatted_result[row] = []
41
48
  formatted_result[row].append({
@@ -1,37 +0,0 @@
1
- import os
2
- from qtpy.QtCore import Signal, QObject
3
- import logging
4
- logger = logging.getLogger(__name__)
5
-
6
- class EnvironmentManager(QObject):
7
- """Singleton class to share the current environment and language between all
8
- components of the app.
9
- """
10
- environment_changed = Signal(str, str, str)
11
-
12
- def __init__(self):
13
- super().__init__()
14
- self.name = None
15
- self.path = None
16
- self.language = None
17
-
18
- @property
19
- def current_environment(self):
20
- return self.name, self.path, self.language
21
-
22
- def set_environment(self, name, path, language):
23
- path = os.path.abspath(path)
24
- if path == self.path:
25
- return
26
- if not os.path.exists(path):
27
- logger.warning(f'no such environment: {path}')
28
- return
29
- logger.info(f'environment changed: {path}')
30
- self.name = name
31
- self.path = path
32
- self.language = language
33
- self.environment_changed.emit(name, path, language)
34
-
35
-
36
- # Singleton instance
37
- environment_manager = EnvironmentManager()