pyqt-code-editor 0.0.20__tar.gz → 0.0.22__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.20 → pyqt_code_editor-0.0.22}/PKG-INFO +1 -1
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/__init__.py +1 -1
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/_settings.py +2 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/app.py +6 -6
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/code_editors/languages/python.py +1 -1
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/jupyter_console.py +18 -12
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/project_explorer.py +10 -2
- pyqt_code_editor-0.0.22/pyqt_code_editor/environment_manager.py +29 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/comment.py +21 -2
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/complete.py +10 -2
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/file_link.py +51 -5
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/languages/python.py +3 -2
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/process.py +7 -4
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/jedi.py +14 -8
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/COPYING +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyproject.toml +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/code_editors/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/code_editors/code_editors.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/code_editors/languages/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/code_editors/languages/generic.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/code_editors/languages/markdown.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/code_editors/languages/text.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/editor_panel.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/find_in_files.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/settings_panel.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/sigmund.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/workspace_explorer.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/auto_indent.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/auto_pair.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/base.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/check.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/execute.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/line_number.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/python_auto_indent.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/search_replace.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/shortcuts.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/smart_backspace_delete.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/symbols.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/theme.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/widgets/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/zoom.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/signal_router.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/syntax_highlighters/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/syntax_highlighters/languages/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/syntax_highlighters/languages/generic.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/syntax_highlighters/languages/python.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/syntax_highlighters/syntax_highlighter.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/themes.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/utils/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/utils/languages/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/utils/languages/python/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/utils/languages/python/_auto_indent.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/utils/languages/python/_extract_cells.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/utils/languages/python/_mask_str_in_code.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/watchdog.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/calltip_widget.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/completion_popup.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/dock.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/quick_open_dialog.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/quick_open_file_dialog.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/quick_symbol_dialog.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/search_replace_frame.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/tab_splitter.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/tabbed_editor.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/languages/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/languages/generic.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/manager.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/__init__.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/codestral.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/ruff.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/sigmund.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/symbol.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/settings.py +0 -0
- {pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/readme.md +0 -0
|
@@ -80,9 +80,11 @@ class Settings(QObject):
|
|
|
80
80
|
|
|
81
81
|
# Files and Folders
|
|
82
82
|
current_folder = SettingProperty(os.path.expanduser('~'), "Files")
|
|
83
|
+
project_folders = SettingProperty(os.path.expanduser('~'), "Files")
|
|
83
84
|
default_filename = SettingProperty('untitled.txt', "Files")
|
|
84
85
|
default_language = SettingProperty('python', "Files")
|
|
85
86
|
default_encoding = SettingProperty('utf-8', "Files")
|
|
87
|
+
max_file_size = SettingProperty(5 * 1024 ** 2, "Files") # 5 MB)
|
|
86
88
|
|
|
87
89
|
# Keyboard shortcuts
|
|
88
90
|
shortcut_move_line_up = SettingProperty('Alt+Up', "Shortcuts")
|
|
@@ -26,9 +26,8 @@ class MainWindow(QMainWindow):
|
|
|
26
26
|
super().__init__()
|
|
27
27
|
logger.info("MainWindow initialized")
|
|
28
28
|
# If no path is provided, fall back to current dir
|
|
29
|
-
if
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
if root_path:
|
|
30
|
+
settings.project_folders = settings.current_folder = root_path
|
|
32
31
|
self.setWindowTitle(f"Sigmund Analyst {__version__}")
|
|
33
32
|
# The editor panel provides splittable editor tabs
|
|
34
33
|
self._editor_panel = EditorPanel()
|
|
@@ -38,8 +37,9 @@ class MainWindow(QMainWindow):
|
|
|
38
37
|
# Track if project explorers are hidden as a group
|
|
39
38
|
self._project_explorers_hidden = False
|
|
40
39
|
# Open initial project explorer
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
for project_folder in str(settings.project_folders).split(':'):
|
|
41
|
+
if project_folder:
|
|
42
|
+
self._open_project_explorer(project_folder)
|
|
43
43
|
# Set up the workspace explorer
|
|
44
44
|
self._workspace_explorer = WorkspaceExplorer(parent=self)
|
|
45
45
|
self.addDockWidget(Qt.RightDockWidgetArea, self._workspace_explorer)
|
|
@@ -442,7 +442,7 @@ class MainWindow(QMainWindow):
|
|
|
442
442
|
open_file_callback=self._editor_panel.open_file,
|
|
443
443
|
)
|
|
444
444
|
dlg.exec_()
|
|
445
|
-
|
|
445
|
+
|
|
446
446
|
|
|
447
447
|
def launch_app():
|
|
448
448
|
app = QApplication(sys.argv)
|
|
@@ -6,7 +6,7 @@ from pyqt_code_editor.mixins import Complete, PythonAutoIndent, \
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Editor(LineNumber, Zoom, SmartBackspaceDelete, PythonAutoPair, Complete,
|
|
9
|
-
|
|
9
|
+
Comment, PythonAutoIndent, SearchReplace, FileLink, Execute,
|
|
10
10
|
Theme, Check, Shortcuts, Symbols, Base,
|
|
11
11
|
QPlainTextEdit):
|
|
12
12
|
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/jupyter_console.py
RENAMED
|
@@ -5,12 +5,14 @@ from qtconsole.rich_jupyter_widget import RichJupyterWidget
|
|
|
5
5
|
from qtconsole.manager import QtKernelManager
|
|
6
6
|
from jupyter_client.kernelspec import KernelSpecManager
|
|
7
7
|
import qtawesome as qta
|
|
8
|
+
import sys
|
|
8
9
|
import os
|
|
9
10
|
import logging
|
|
10
11
|
import uuid
|
|
11
12
|
import json
|
|
12
13
|
from concurrent.futures import Future
|
|
13
14
|
from .. import settings
|
|
15
|
+
from ..environment_manager import environment_manager
|
|
14
16
|
from ..themes import THEMES, OUTER_CONTENT_MARGINS, HORIZONTAL_SPACING
|
|
15
17
|
from ..widgets import Dock
|
|
16
18
|
from .. import watchdog
|
|
@@ -359,8 +361,8 @@ print(json.dumps({result_var}))
|
|
|
359
361
|
class JupyterConsole(Dock):
|
|
360
362
|
"""Dockable widget containing tabbed Jupyter consoles"""
|
|
361
363
|
|
|
362
|
-
execution_complete = Signal(str, object)
|
|
363
|
-
workspace_updated = Signal(dict)
|
|
364
|
+
execution_complete = Signal(str, object)
|
|
365
|
+
workspace_updated = Signal(dict)
|
|
364
366
|
|
|
365
367
|
def __init__(self, parent=None, default_kernel='python3'):
|
|
366
368
|
super().__init__("Jupyter Console", parent)
|
|
@@ -370,7 +372,7 @@ class JupyterConsole(Dock):
|
|
|
370
372
|
# ---------------------------------------------------------------------
|
|
371
373
|
# Kernel cache
|
|
372
374
|
# ---------------------------------------------------------------------
|
|
373
|
-
self.available_kernels =
|
|
375
|
+
self.available_kernels = {} # will be filled by refresh_kernel_menu()
|
|
374
376
|
|
|
375
377
|
# ---------------------------------------------------------------------
|
|
376
378
|
# UI setup
|
|
@@ -429,6 +431,14 @@ class JupyterConsole(Dock):
|
|
|
429
431
|
console_tab = self.tab_widget.widget(index)
|
|
430
432
|
if console_tab:
|
|
431
433
|
console_tab.update_workspace()
|
|
434
|
+
spec = self.available_kernels.get(console_tab.kernel_name, None)
|
|
435
|
+
if spec:
|
|
436
|
+
python_executable = spec['spec']['argv'][0]
|
|
437
|
+
else:
|
|
438
|
+
python_executable = sys.executable
|
|
439
|
+
environment_manager.set_environment(console_tab.kernel_name,
|
|
440
|
+
python_executable,
|
|
441
|
+
'python')
|
|
432
442
|
|
|
433
443
|
# -------------------------------------------------------------------------
|
|
434
444
|
# Kernel handling
|
|
@@ -439,12 +449,9 @@ class JupyterConsole(Dock):
|
|
|
439
449
|
|
|
440
450
|
# Get available kernelspecs
|
|
441
451
|
kernel_spec_manager = HomeAwareKernelSpecManager()
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
self.available_kernels = list(specs.keys())
|
|
446
|
-
|
|
447
|
-
for spec_name, spec in specs.items():
|
|
452
|
+
self.available_kernels = kernel_spec_manager.get_all_specs()
|
|
453
|
+
self.fallback_kernel = list(self.available_kernels.keys())[0]
|
|
454
|
+
for spec_name, spec in self.available_kernels.items():
|
|
448
455
|
display_name = spec['spec']['display_name']
|
|
449
456
|
action = QAction(display_name, self)
|
|
450
457
|
action.setData(spec_name)
|
|
@@ -462,12 +469,11 @@ class JupyterConsole(Dock):
|
|
|
462
469
|
"""
|
|
463
470
|
if kernel_name not in self.available_kernels:
|
|
464
471
|
if self.available_kernels:
|
|
465
|
-
fallback = self.available_kernels[0]
|
|
466
472
|
logger.warning(
|
|
467
473
|
"Requested kernel '%s' not found. "
|
|
468
|
-
"Falling back to '%s'.", kernel_name,
|
|
474
|
+
"Falling back to '%s'.", kernel_name, self.fallback_kernel
|
|
469
475
|
)
|
|
470
|
-
kernel_name =
|
|
476
|
+
kernel_name = self.fallback_kernel
|
|
471
477
|
else:
|
|
472
478
|
logger.error("No available kernels found. Cannot create console tab.")
|
|
473
479
|
return None
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/project_explorer.py
RENAMED
|
@@ -262,8 +262,12 @@ class ProjectExplorer(QDockWidget):
|
|
|
262
262
|
if not selected_dir:
|
|
263
263
|
return None
|
|
264
264
|
settings.current_folder = selected_dir
|
|
265
|
+
# Add selected folder to project folders but make sure we don't
|
|
266
|
+
# duplicate
|
|
267
|
+
settings.project_folders = ':'.join(
|
|
268
|
+
set(str(settings.project_folders).split(':')) | {selected_dir})
|
|
265
269
|
explorer = cls(editor_panel, root_path=selected_dir, parent=parent)
|
|
266
|
-
return explorer
|
|
270
|
+
return explorer
|
|
267
271
|
|
|
268
272
|
def _toggle_gitignore(self, enabled):
|
|
269
273
|
"""Toggles the .gitignore filter on or off and refreshes the model.
|
|
@@ -514,6 +518,10 @@ class ProjectExplorer(QDockWidget):
|
|
|
514
518
|
self._clipboard_operation = None
|
|
515
519
|
self._clipboard_source_path = None
|
|
516
520
|
|
|
517
|
-
def closeEvent(self, event):
|
|
521
|
+
def closeEvent(self, event):
|
|
522
|
+
# Add selected folder to project folders but make sure we don't
|
|
523
|
+
# duplicate
|
|
524
|
+
settings.project_folders = ':'.join(
|
|
525
|
+
set(str(settings.project_folders).split(':')) - {self._display_root})
|
|
518
526
|
super().closeEvent(event)
|
|
519
527
|
self.closed.emit(self)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from qtpy.QtCore import Signal, QObject
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class EnvironmentManager(QObject):
|
|
5
|
+
"""Singleton class to share the current environment and language between all
|
|
6
|
+
components of the app.
|
|
7
|
+
"""
|
|
8
|
+
environment_changed = Signal(str, str, str)
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super().__init__()
|
|
12
|
+
self.name = None
|
|
13
|
+
self.path = None
|
|
14
|
+
self.language = None
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def current_environment(self):
|
|
18
|
+
return self.name, self.path, self.language
|
|
19
|
+
|
|
20
|
+
def set_environment(self, name, path, language):
|
|
21
|
+
if path != self.path:
|
|
22
|
+
self.name = name
|
|
23
|
+
self.path = path
|
|
24
|
+
self.language = language
|
|
25
|
+
self.environment_changed.emit(name, path, language)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Singleton instance
|
|
29
|
+
environment_manager = EnvironmentManager()
|
|
@@ -17,12 +17,31 @@ class Comment:
|
|
|
17
17
|
(default "# " for Python).
|
|
18
18
|
"""
|
|
19
19
|
super().__init__(*args, **kwargs)
|
|
20
|
-
# The text to prepend for a commented line
|
|
21
|
-
self.code_editor_comment_string
|
|
22
20
|
self._comment_shortcut = QShortcut(
|
|
23
21
|
QKeySequence(settings.shortcut_comment), self)
|
|
24
22
|
self._comment_shortcut.setContext(Qt.WidgetWithChildrenShortcut)
|
|
25
23
|
self._comment_shortcut.activated.connect(self._toggle_comment)
|
|
24
|
+
|
|
25
|
+
def keyPressEvent(self, event):
|
|
26
|
+
# When a return is pressed on a commented line, the comment should
|
|
27
|
+
# continue on the next line
|
|
28
|
+
if event.key() in (Qt.Key_Enter, Qt.Key_Return):
|
|
29
|
+
cursor = self.textCursor()
|
|
30
|
+
block = cursor.block()
|
|
31
|
+
line_text = block.text()
|
|
32
|
+
# Check if the current line is a comment
|
|
33
|
+
stripped = line_text.lstrip()
|
|
34
|
+
if stripped.startswith(self.code_editor_comment_string[0]):
|
|
35
|
+
# Extract the indentation from the current line
|
|
36
|
+
indentation = line_text[:len(line_text) - len(stripped)]
|
|
37
|
+
# Insert newline with same indentation plus comment marker
|
|
38
|
+
cursor.beginEditBlock()
|
|
39
|
+
cursor.insertText(
|
|
40
|
+
'\n' + indentation + self.code_editor_comment_string)
|
|
41
|
+
cursor.endEditBlock()
|
|
42
|
+
event.accept()
|
|
43
|
+
return
|
|
44
|
+
super().keyPressEvent(event)
|
|
26
45
|
|
|
27
46
|
def _toggle_comment(self):
|
|
28
47
|
"""Toggle comment on the current selection or current line."""
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
1
3
|
import logging
|
|
2
4
|
from qtpy.QtCore import QTimer, Qt
|
|
3
5
|
from qtpy.QtGui import QTextCursor
|
|
4
6
|
from qtpy.QtWidgets import QApplication
|
|
5
7
|
from ..widgets.completion_popup import CompletionPopup
|
|
6
8
|
from ..widgets.calltip_widget import CalltipWidget
|
|
9
|
+
from ..environment_manager import environment_manager
|
|
7
10
|
from .. import settings
|
|
8
11
|
logging.basicConfig(level=logging.INFO, force=True)
|
|
9
12
|
logger = logging.getLogger(__name__)
|
|
@@ -273,7 +276,8 @@ class Complete:
|
|
|
273
276
|
cursor_pos=cursor_pos, full=full,
|
|
274
277
|
multiline=multiline,
|
|
275
278
|
path=self.code_editor_file_path,
|
|
276
|
-
language=self.code_editor_language
|
|
279
|
+
language=self.code_editor_language,
|
|
280
|
+
env_path=environment_manager.path)
|
|
277
281
|
|
|
278
282
|
def _cm_request_calltip(self):
|
|
279
283
|
"""Send a calltip request."""
|
|
@@ -284,7 +288,8 @@ class Complete:
|
|
|
284
288
|
self.send_worker_request(action='calltip', code=code,
|
|
285
289
|
cursor_pos=cursor_pos,
|
|
286
290
|
path=self.code_editor_file_path,
|
|
287
|
-
language=self.code_editor_language
|
|
291
|
+
language=self.code_editor_language,
|
|
292
|
+
env_path=environment_manager.path)
|
|
288
293
|
|
|
289
294
|
def _cm_hide_calltip(self):
|
|
290
295
|
"""Hide the calltip widget."""
|
|
@@ -533,3 +538,6 @@ class Complete:
|
|
|
533
538
|
def update_theme(self):
|
|
534
539
|
super().update_theme()
|
|
535
540
|
self._cm_calltip_widget.apply_stylesheet()
|
|
541
|
+
|
|
542
|
+
def _current_environment(self):
|
|
543
|
+
return os.environ.get('SIGMUND_PYENV', sys.executable)
|
|
@@ -40,7 +40,12 @@ class FileLink:
|
|
|
40
40
|
if not path.is_file():
|
|
41
41
|
raise FileNotFoundError(f"No such file: {path}")
|
|
42
42
|
|
|
43
|
+
# Check file size
|
|
44
|
+
file_size = path.stat().st_size
|
|
45
|
+
is_large = file_size > settings.max_file_size
|
|
46
|
+
|
|
43
47
|
raw_data = path.read_bytes()
|
|
48
|
+
encoding_unknown = False
|
|
44
49
|
|
|
45
50
|
if encoding is None:
|
|
46
51
|
# First see if everything is within ASCII range
|
|
@@ -51,14 +56,55 @@ class FileLink:
|
|
|
51
56
|
except UnicodeDecodeError:
|
|
52
57
|
# Not pure ASCII; let chardet pick
|
|
53
58
|
detect_result = chardet.detect(raw_data)
|
|
54
|
-
# If detection fails or returns None,
|
|
55
|
-
|
|
59
|
+
# If detection fails or returns None, or confidence is low
|
|
60
|
+
if (detect_result is None or
|
|
61
|
+
detect_result.get("encoding") is None or
|
|
62
|
+
detect_result.get("confidence", 0) < 0.7):
|
|
63
|
+
used_encoding = "utf-8"
|
|
64
|
+
encoding_unknown = True
|
|
65
|
+
else:
|
|
66
|
+
used_encoding = detect_result["encoding"]
|
|
56
67
|
else:
|
|
57
68
|
used_encoding = encoding
|
|
69
|
+
|
|
70
|
+
# Show warning if file is large or encoding is uncertain
|
|
71
|
+
if is_large or encoding_unknown:
|
|
72
|
+
warnings = []
|
|
73
|
+
if is_large:
|
|
74
|
+
size_mb = file_size / (1024 * 1024)
|
|
75
|
+
warnings.append(f"This file is {size_mb:.1f} MB, which exceeds the recommended maximum of {settings.max_file_size / (1024 * 1024):.1f} MB.")
|
|
76
|
+
if encoding_unknown:
|
|
77
|
+
warnings.append("The file encoding could not be reliably detected. It may not display correctly.")
|
|
78
|
+
|
|
79
|
+
warning_message = "\n\n".join(warnings)
|
|
80
|
+
warning_message += "\n\nDo you want to continue opening this file?"
|
|
81
|
+
|
|
82
|
+
reply = QMessageBox.warning(
|
|
83
|
+
self,
|
|
84
|
+
"File Warning",
|
|
85
|
+
warning_message,
|
|
86
|
+
QMessageBox.Yes | QMessageBox.No,
|
|
87
|
+
QMessageBox.No
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if reply != QMessageBox.Yes:
|
|
91
|
+
logger.info(f"User declined to open file: {path}")
|
|
92
|
+
return
|
|
93
|
+
|
|
58
94
|
logger.info(f'opening file as {used_encoding}')
|
|
59
95
|
# Try reading with the chosen encoding
|
|
60
|
-
|
|
61
|
-
|
|
96
|
+
try:
|
|
97
|
+
with path.open("r", encoding=used_encoding) as f:
|
|
98
|
+
content = f.read()
|
|
99
|
+
except UnicodeDecodeError as e:
|
|
100
|
+
# If decoding fails, offer to try as binary or cancel
|
|
101
|
+
reply = QMessageBox.critical(
|
|
102
|
+
self,
|
|
103
|
+
"Encoding Error",
|
|
104
|
+
f"Failed to decode file with {used_encoding} encoding.\n{str(e)}\n\nThis may not be a text file.",
|
|
105
|
+
QMessageBox.Cancel
|
|
106
|
+
)
|
|
107
|
+
return
|
|
62
108
|
|
|
63
109
|
# Store the content in the editor
|
|
64
110
|
self.setPlainText(content)
|
|
@@ -181,4 +227,4 @@ class FileLink:
|
|
|
181
227
|
# Re-add the file to watcher so we keep listening for future changes
|
|
182
228
|
if self._file_watcher is not None:
|
|
183
229
|
self._file_watcher.addPath(changed_path)
|
|
184
|
-
self._watch_file_changes = True
|
|
230
|
+
self._watch_file_changes = True
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/languages/python.py
RENAMED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
from ..providers import jedi, codestral, ruff
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def complete(code, cursor_pos, path, multiline, full):
|
|
4
|
+
def complete(code, cursor_pos, path, multiline, full, env_path):
|
|
5
5
|
if full or multiline:
|
|
6
6
|
codestral_completions = codestral.codestral_complete(
|
|
7
7
|
code, cursor_pos, path=path, multiline=multiline)
|
|
8
8
|
else:
|
|
9
9
|
codestral_completions = []
|
|
10
10
|
if not multiline:
|
|
11
|
-
jedi_completions = jedi.jedi_complete(code, cursor_pos, path=path
|
|
11
|
+
jedi_completions = jedi.jedi_complete(code, cursor_pos, path=path,
|
|
12
|
+
env_path=env_path)
|
|
12
13
|
else:
|
|
13
14
|
jedi_completions = []
|
|
14
15
|
# If there is at least one jedi completion, it is always insert at first.
|
|
@@ -68,9 +68,11 @@ def main_worker_process_function(request_queue, result_queue):
|
|
|
68
68
|
path = request.get('path', None)
|
|
69
69
|
multiline = request.get('multiline', False)
|
|
70
70
|
full = request.get('full', False)
|
|
71
|
-
|
|
71
|
+
env_path = request.get('env_path', None)
|
|
72
|
+
logger.info(f"Performing code completion: language='{language}', multiline={multiline}, path={path}, env_path={env_path}")
|
|
72
73
|
completions = worker_functions.complete(
|
|
73
|
-
code, cursor_pos, path=path, multiline=multiline, full=full
|
|
74
|
+
code, cursor_pos, path=path, multiline=multiline, full=full,
|
|
75
|
+
env_path=env_path)
|
|
74
76
|
if not completions:
|
|
75
77
|
logger.info("No completions")
|
|
76
78
|
else:
|
|
@@ -90,9 +92,10 @@ def main_worker_process_function(request_queue, result_queue):
|
|
|
90
92
|
else:
|
|
91
93
|
code = request.get('code', '')
|
|
92
94
|
path = request.get('path', None)
|
|
93
|
-
|
|
95
|
+
env_path = request.get('env_path', None)
|
|
96
|
+
logger.info(f"Performing calltip: language='{language}', path={path}, env_path={env_path}")
|
|
94
97
|
signatures = worker_functions.calltip(
|
|
95
|
-
code, cursor_pos, path=path)
|
|
98
|
+
code, cursor_pos, path=path, env_path=env_path)
|
|
96
99
|
if signatures is None:
|
|
97
100
|
logger.info("No calltip signatures. Sending result back.")
|
|
98
101
|
else:
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/jedi.py
RENAMED
|
@@ -20,7 +20,8 @@ def _signature_to_html(signature) -> str:
|
|
|
20
20
|
return f"({sig_line}){return_hint}"
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def _prepare_jedi_script(code: str, cursor_position: int, path: str | None
|
|
23
|
+
def _prepare_jedi_script(code: str, cursor_position: int, path: str | None,
|
|
24
|
+
env_path: str | None):
|
|
24
25
|
"""
|
|
25
26
|
Prepare a Jedi Script object and calculate line_no/column_no from the
|
|
26
27
|
given code and cursor_position. Returns (script, line_no, column_no).
|
|
@@ -32,15 +33,17 @@ def _prepare_jedi_script(code: str, cursor_position: int, path: str | None):
|
|
|
32
33
|
column_no = cursor_position
|
|
33
34
|
else:
|
|
34
35
|
column_no = cursor_position - (last_newline_idx + 1)
|
|
35
|
-
|
|
36
36
|
logger.info("Creating Jedi Script for path=%r at line=%d, column=%d",
|
|
37
37
|
path, line_no, column_no)
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
# We explicitly indicate that the environment is safe, because we know that
|
|
39
|
+
# they come from the app itself
|
|
40
|
+
env = jedi.create_environment(env_path, safe=False) if env_path else None
|
|
41
|
+
script = jedi.Script(code, path=path, environment=env)
|
|
40
42
|
return script, line_no, column_no
|
|
41
43
|
|
|
42
44
|
|
|
43
|
-
def jedi_complete(code: str, cursor_position: int, path: str | None
|
|
45
|
+
def jedi_complete(code: str, cursor_position: int, path: str | None,
|
|
46
|
+
env_path: str | None = None) -> list[str]:
|
|
44
47
|
"""
|
|
45
48
|
Perform Python-specific completion using Jedi. Returns a list of possible completions
|
|
46
49
|
for the text at the given cursor position, or None if no completion is found.
|
|
@@ -57,7 +60,8 @@ def jedi_complete(code: str, cursor_position: int, path: str | None) -> list[str
|
|
|
57
60
|
if code_up_to_cursor.rfind('#') > code_up_to_cursor.rfind('\n'):
|
|
58
61
|
return []
|
|
59
62
|
# Go Jedi!
|
|
60
|
-
script, line_no, column_no = _prepare_jedi_script(code, cursor_position,
|
|
63
|
+
script, line_no, column_no = _prepare_jedi_script(code, cursor_position,
|
|
64
|
+
path, env_path)
|
|
61
65
|
completions = script.complete(line=line_no, column=column_no)
|
|
62
66
|
if not completions:
|
|
63
67
|
return []
|
|
@@ -70,7 +74,8 @@ def jedi_complete(code: str, cursor_position: int, path: str | None) -> list[str
|
|
|
70
74
|
|
|
71
75
|
def jedi_signatures(code: str, cursor_position: int, path: str | None,
|
|
72
76
|
multiline: bool = False, max_width: int = 40,
|
|
73
|
-
max_lines: int = 10
|
|
77
|
+
max_lines: int = 10,
|
|
78
|
+
env_path: str | None = None):
|
|
74
79
|
"""
|
|
75
80
|
Retrieve function signatures (calltips) from Jedi given the current cursor position.
|
|
76
81
|
Returns a list of strings describing each signature, or None if none.
|
|
@@ -84,7 +89,8 @@ def jedi_signatures(code: str, cursor_position: int, path: str | None,
|
|
|
84
89
|
return None
|
|
85
90
|
|
|
86
91
|
logger.info("Starting Jedi calltip request (multiline=%r).", multiline)
|
|
87
|
-
script, line_no, column_no = _prepare_jedi_script(code, cursor_position,
|
|
92
|
+
script, line_no, column_no = _prepare_jedi_script(code, cursor_position,
|
|
93
|
+
path, env_path)
|
|
88
94
|
|
|
89
95
|
signatures = script.get_signatures(line=line_no, column=column_no)
|
|
90
96
|
if not signatures:
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/code_editors/__init__.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/code_editors/code_editors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/code_editors/languages/text.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/editor_panel.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/components/find_in_files.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/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
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/python_auto_indent.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/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.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/mixins/widgets/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/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.20 → pyqt_code_editor-0.0.22}/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.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/calltip_widget.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/completion_popup.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/quick_open_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/quick_symbol_dialog.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/search_replace_frame.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/tab_splitter.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/widgets/tabbed_editor.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/languages/__init__.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/languages/generic.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/__init__.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/codestral.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/ruff.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/sigmund.py
RENAMED
|
File without changes
|
{pyqt_code_editor-0.0.20 → pyqt_code_editor-0.0.22}/pyqt_code_editor/worker/providers/symbol.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|