euporie 2.8.6__py3-none-any.whl → 2.8.8__py3-none-any.whl
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.
- euporie/console/app.py +2 -0
- euporie/console/tabs/console.py +27 -17
- euporie/core/__init__.py +2 -2
- euporie/core/app/_commands.py +4 -21
- euporie/core/app/app.py +13 -7
- euporie/core/bars/command.py +9 -6
- euporie/core/bars/search.py +43 -2
- euporie/core/border.py +7 -2
- euporie/core/comm/base.py +2 -2
- euporie/core/comm/ipywidgets.py +3 -3
- euporie/core/commands.py +44 -8
- euporie/core/completion.py +14 -6
- euporie/core/convert/datum.py +7 -7
- euporie/core/data_structures.py +20 -1
- euporie/core/filters.py +8 -0
- euporie/core/ft/html.py +47 -40
- euporie/core/graphics.py +11 -3
- euporie/core/history.py +15 -5
- euporie/core/inspection.py +16 -9
- euporie/core/io.py +1 -1
- euporie/core/kernel/__init__.py +53 -1
- euporie/core/kernel/base.py +571 -0
- euporie/core/kernel/{client.py → jupyter.py} +173 -430
- euporie/core/kernel/{manager.py → jupyter_manager.py} +4 -3
- euporie/core/kernel/local.py +694 -0
- euporie/core/key_binding/bindings/basic.py +6 -3
- euporie/core/keys.py +26 -25
- euporie/core/layout/cache.py +31 -7
- euporie/core/layout/containers.py +88 -13
- euporie/core/layout/scroll.py +45 -148
- euporie/core/log.py +1 -1
- euporie/core/style.py +2 -1
- euporie/core/suggest.py +155 -74
- euporie/core/tabs/__init__.py +10 -0
- euporie/core/tabs/_commands.py +76 -0
- euporie/core/tabs/_settings.py +16 -0
- euporie/core/tabs/base.py +22 -8
- euporie/core/tabs/kernel.py +81 -35
- euporie/core/tabs/notebook.py +14 -22
- euporie/core/utils.py +1 -1
- euporie/core/validation.py +8 -8
- euporie/core/widgets/_settings.py +19 -2
- euporie/core/widgets/cell.py +31 -31
- euporie/core/widgets/cell_outputs.py +10 -1
- euporie/core/widgets/dialog.py +30 -75
- euporie/core/widgets/forms.py +71 -59
- euporie/core/widgets/inputs.py +7 -4
- euporie/core/widgets/layout.py +281 -93
- euporie/core/widgets/menu.py +55 -15
- euporie/core/widgets/palette.py +3 -1
- euporie/core/widgets/tree.py +86 -76
- euporie/notebook/app.py +35 -16
- euporie/notebook/tabs/edit.py +4 -4
- euporie/notebook/tabs/json.py +6 -2
- euporie/notebook/tabs/notebook.py +26 -8
- euporie/preview/tabs/notebook.py +17 -13
- euporie/web/tabs/web.py +22 -3
- euporie/web/widgets/webview.py +3 -0
- {euporie-2.8.6.dist-info → euporie-2.8.8.dist-info}/METADATA +1 -1
- {euporie-2.8.6.dist-info → euporie-2.8.8.dist-info}/RECORD +65 -62
- {euporie-2.8.6.dist-info → euporie-2.8.8.dist-info}/entry_points.txt +1 -1
- {euporie-2.8.6.dist-info → euporie-2.8.8.dist-info}/licenses/LICENSE +1 -1
- {euporie-2.8.6.data → euporie-2.8.8.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.8.6.data → euporie-2.8.8.data}/data/share/applications/euporie-notebook.desktop +0 -0
- {euporie-2.8.6.dist-info → euporie-2.8.8.dist-info}/WHEEL +0 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
"""Contains commands for tabs."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import logging
|
6
|
+
|
7
|
+
from euporie.core.commands import add_cmd
|
8
|
+
from euporie.core.filters import tab_can_save, tab_has_focus
|
9
|
+
|
10
|
+
log = logging.getLogger(__name__)
|
11
|
+
|
12
|
+
|
13
|
+
@add_cmd(filter=tab_can_save, aliases=["w"])
|
14
|
+
def _save_file(path: str = "") -> None:
|
15
|
+
"""Save the current file."""
|
16
|
+
from euporie.core.app.current import get_app
|
17
|
+
|
18
|
+
if (tab := get_app().tab) is not None:
|
19
|
+
from upath import UPath
|
20
|
+
|
21
|
+
try:
|
22
|
+
tab._save(UPath(path) if path else None)
|
23
|
+
except NotImplementedError:
|
24
|
+
pass
|
25
|
+
|
26
|
+
|
27
|
+
@add_cmd(aliases=["wq", "x"])
|
28
|
+
def _save_and_quit(path: str = "") -> None:
|
29
|
+
"""Save the current tab then quits euporie."""
|
30
|
+
from euporie.core.app.current import get_app
|
31
|
+
|
32
|
+
app = get_app()
|
33
|
+
if (tab := app.tab) is not None:
|
34
|
+
from upath import UPath
|
35
|
+
|
36
|
+
try:
|
37
|
+
tab.save(UPath(path) if path else None)
|
38
|
+
except NotImplementedError:
|
39
|
+
pass
|
40
|
+
|
41
|
+
app.exit()
|
42
|
+
|
43
|
+
|
44
|
+
@add_cmd(
|
45
|
+
menu_title="Save As…",
|
46
|
+
filter=tab_can_save,
|
47
|
+
)
|
48
|
+
def _save_as(path: str = "") -> None:
|
49
|
+
"""Save the current file at a new location."""
|
50
|
+
if path:
|
51
|
+
_save_file(path)
|
52
|
+
else:
|
53
|
+
from euporie.core.app.current import get_app
|
54
|
+
|
55
|
+
app = get_app()
|
56
|
+
if dialog := app.dialogs.get("save-as"):
|
57
|
+
dialog.show(tab=app.tab)
|
58
|
+
|
59
|
+
|
60
|
+
@add_cmd(filter=tab_has_focus, title="Refresh the current tab")
|
61
|
+
def _refresh_tab() -> None:
|
62
|
+
"""Reload the tab contents and reset the tab."""
|
63
|
+
from euporie.core.app.current import get_app
|
64
|
+
|
65
|
+
if (tab := get_app().tab) is not None:
|
66
|
+
tab.reset()
|
67
|
+
|
68
|
+
|
69
|
+
# Depreciated v2.5.0
|
70
|
+
@add_cmd(filter=tab_has_focus, title="Reset the current tab")
|
71
|
+
def _reset_tab() -> None:
|
72
|
+
log.warning(
|
73
|
+
"The `reset-tab` command was been renamed to `refresh-tab` in v2.5.0,"
|
74
|
+
" and will be removed in a future version"
|
75
|
+
)
|
76
|
+
_refresh_tab()
|
euporie/core/tabs/_settings.py
CHANGED
@@ -4,6 +4,22 @@ from prompt_toolkit.filters import buffer_has_focus
|
|
4
4
|
|
5
5
|
from euporie.core.config import add_setting
|
6
6
|
|
7
|
+
add_setting(
|
8
|
+
name="backup_on_save",
|
9
|
+
group="euporie.core.tabs.base",
|
10
|
+
flags=["--backup-on-save"],
|
11
|
+
type_=bool,
|
12
|
+
help_="Create backups before saving files",
|
13
|
+
default=False,
|
14
|
+
description="""
|
15
|
+
Determines whether a backup file should be created before saving a file.
|
16
|
+
|
17
|
+
If set to ``True``, the original file will be copied to a new file with the
|
18
|
+
same name but prefixed with ``.`` and suffixed with ``.bak`` before writing the
|
19
|
+
updated contents.
|
20
|
+
""",
|
21
|
+
)
|
22
|
+
|
7
23
|
add_setting(
|
8
24
|
name="kernel_name",
|
9
25
|
group="euporie.core.tabs.kernel",
|
euporie/core/tabs/base.py
CHANGED
@@ -22,11 +22,13 @@ from euporie.core.layout.containers import Window
|
|
22
22
|
from euporie.core.path import UntitledPath, parse_path
|
23
23
|
|
24
24
|
if TYPE_CHECKING:
|
25
|
+
from collections.abc import Sequence
|
25
26
|
from pathlib import Path
|
26
27
|
from typing import Any, Callable
|
27
28
|
|
28
29
|
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
|
29
30
|
from prompt_toolkit.layout.containers import AnyContainer
|
31
|
+
from prompt_toolkit.layout.controls import BufferControl
|
30
32
|
|
31
33
|
from euporie.core.app.app import BaseApp
|
32
34
|
from euporie.core.bars.status import StatusBarFields
|
@@ -133,13 +135,14 @@ class Tab(metaclass=ABCMeta):
|
|
133
135
|
if not name.startswith("."):
|
134
136
|
name = f".{name}"
|
135
137
|
backup_path = parent / name
|
136
|
-
|
137
|
-
|
138
|
+
if self.app.config.backup_on_save:
|
139
|
+
try:
|
140
|
+
import shutil
|
138
141
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
142
|
+
shutil.copy2(path, backup_path)
|
143
|
+
except Exception as e:
|
144
|
+
log.error("Failed to create backup: %s", e)
|
145
|
+
raise
|
143
146
|
|
144
147
|
# Write new content directly to original file
|
145
148
|
try:
|
@@ -179,6 +182,17 @@ class Tab(metaclass=ABCMeta):
|
|
179
182
|
f"File saving not implement for `{self.__class__.__name__}` tab"
|
180
183
|
)
|
181
184
|
|
185
|
+
def __pt_searchables__(self) -> Sequence[BufferControl]:
|
186
|
+
"""Return a list of searchable buffer controls for this tab.
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
A list of searchable buffer controls
|
190
|
+
|
191
|
+
Raises:
|
192
|
+
NotImplementedError: If the tab does not provide searchable buffers
|
193
|
+
"""
|
194
|
+
raise NotImplementedError()
|
195
|
+
|
182
196
|
def __pt_status__(self) -> StatusBarFields | None:
|
183
197
|
"""Return a list of statusbar field values shown then this tab is active."""
|
184
198
|
return ([], [])
|
@@ -208,11 +222,11 @@ class Tab(metaclass=ABCMeta):
|
|
208
222
|
|
209
223
|
@staticmethod
|
210
224
|
@add_cmd(filter=tab_can_save, aliases=["w"])
|
211
|
-
def _save_file(event: KeyPressEvent) -> None:
|
225
|
+
def _save_file(event: KeyPressEvent, path: str = "") -> None:
|
212
226
|
"""Save the current file."""
|
213
227
|
if (tab := get_app().tab) is not None:
|
214
228
|
try:
|
215
|
-
tab._save(UPath(
|
229
|
+
tab._save(UPath(path) if path else None)
|
216
230
|
except NotImplementedError:
|
217
231
|
pass
|
218
232
|
|
euporie/core/tabs/kernel.py
CHANGED
@@ -6,11 +6,11 @@ import asyncio
|
|
6
6
|
import logging
|
7
7
|
from abc import ABCMeta
|
8
8
|
from collections import deque
|
9
|
-
from functools import partial
|
9
|
+
from functools import lru_cache, partial
|
10
10
|
from typing import TYPE_CHECKING
|
11
11
|
from weakref import WeakKeyDictionary
|
12
12
|
|
13
|
-
from prompt_toolkit.auto_suggest import DummyAutoSuggest
|
13
|
+
from prompt_toolkit.auto_suggest import DummyAutoSuggest, DynamicAutoSuggest
|
14
14
|
from prompt_toolkit.completion.base import (
|
15
15
|
DynamicCompleter,
|
16
16
|
_MergedCompleter,
|
@@ -30,8 +30,8 @@ from euporie.core.inspection import (
|
|
30
30
|
KernelInspector,
|
31
31
|
LspInspector,
|
32
32
|
)
|
33
|
-
from euporie.core.kernel
|
34
|
-
from euporie.core.
|
33
|
+
from euporie.core.kernel import list_kernels
|
34
|
+
from euporie.core.kernel.base import NoKernel
|
35
35
|
from euporie.core.tabs.base import Tab
|
36
36
|
|
37
37
|
if TYPE_CHECKING:
|
@@ -47,16 +47,33 @@ if TYPE_CHECKING:
|
|
47
47
|
from euporie.core.comm.base import Comm
|
48
48
|
from euporie.core.format import Formatter
|
49
49
|
from euporie.core.inspection import Inspector
|
50
|
+
from euporie.core.kernel.base import BaseKernel, KernelFactory, MsgCallbacks
|
50
51
|
from euporie.core.lsp import LspClient
|
51
52
|
from euporie.core.widgets.inputs import KernelInput
|
52
53
|
|
53
54
|
log = logging.getLogger(__name__)
|
54
55
|
|
55
56
|
|
57
|
+
@lru_cache
|
58
|
+
def autosuggest_factory(kind: str, history: History) -> AutoSuggest:
|
59
|
+
"""Generate autosuggesters."""
|
60
|
+
if kind == "smart":
|
61
|
+
from euporie.core.suggest import SmartHistoryAutoSuggest
|
62
|
+
|
63
|
+
return SmartHistoryAutoSuggest(history)
|
64
|
+
elif kind == "simple":
|
65
|
+
from euporie.core.suggest import SimpleHistoryAutoSuggest
|
66
|
+
|
67
|
+
return SimpleHistoryAutoSuggest(history)
|
68
|
+
else:
|
69
|
+
from prompt_toolkit.auto_suggest import DummyAutoSuggest
|
70
|
+
|
71
|
+
return DummyAutoSuggest()
|
72
|
+
|
73
|
+
|
56
74
|
class KernelTab(Tab, metaclass=ABCMeta):
|
57
75
|
"""A Tab which connects to a kernel."""
|
58
76
|
|
59
|
-
kernel: Kernel
|
60
77
|
kernel_language: str
|
61
78
|
_metadata: dict[str, Any]
|
62
79
|
bg_init = False
|
@@ -68,7 +85,7 @@ class KernelTab(Tab, metaclass=ABCMeta):
|
|
68
85
|
self,
|
69
86
|
app: BaseApp,
|
70
87
|
path: Path | None = None,
|
71
|
-
kernel:
|
88
|
+
kernel: BaseKernel | None = None,
|
72
89
|
comms: dict[str, Comm] | None = None,
|
73
90
|
use_kernel_history: bool = False,
|
74
91
|
connection_file: Path | None = None,
|
@@ -77,6 +94,7 @@ class KernelTab(Tab, metaclass=ABCMeta):
|
|
77
94
|
# Init tab
|
78
95
|
super().__init__(app, path)
|
79
96
|
|
97
|
+
self.kernel: BaseKernel = NoKernel(self)
|
80
98
|
self.lsps: list[LspClient] = []
|
81
99
|
self.history: History = DummyHistory()
|
82
100
|
self.inspectors: list[Inspector] = []
|
@@ -182,10 +200,12 @@ class KernelTab(Tab, metaclass=ABCMeta):
|
|
182
200
|
|
183
201
|
def post_init_kernel(self) -> None:
|
184
202
|
"""Run stuff after the kernel is loaded."""
|
203
|
+
if not isinstance(self.kernel, NoKernel):
|
204
|
+
self.metadata["kernelspec"] = self.kernel.spec
|
185
205
|
|
186
206
|
def init_kernel(
|
187
207
|
self,
|
188
|
-
kernel:
|
208
|
+
kernel: BaseKernel | None = None,
|
189
209
|
comms: dict[str, Comm] | None = None,
|
190
210
|
use_kernel_history: bool = False,
|
191
211
|
connection_file: Path | None = None,
|
@@ -199,20 +219,47 @@ class KernelTab(Tab, metaclass=ABCMeta):
|
|
199
219
|
self.kernel = kernel
|
200
220
|
self.kernel.default_callbacks = self.default_callbacks
|
201
221
|
else:
|
202
|
-
|
222
|
+
from euporie.core.kernel import list_kernels
|
223
|
+
|
224
|
+
kernel_infos = list_kernels()
|
225
|
+
kernel_name = self.kernel_name or self.app.config.kernel_name
|
226
|
+
for info in kernel_infos:
|
227
|
+
if info.name == kernel_name:
|
228
|
+
factory = info.factory
|
229
|
+
break
|
230
|
+
else:
|
231
|
+
msg = (
|
232
|
+
f"Kernel '{self.kernel_display_name}' not found"
|
233
|
+
if self.kernel_name
|
234
|
+
else "No kernel selected"
|
235
|
+
)
|
236
|
+
self.change_kernel(msg=msg, startup=True)
|
237
|
+
return
|
238
|
+
self.kernel = factory(
|
203
239
|
kernel_tab=self,
|
204
240
|
allow_stdin=self.allow_stdin,
|
205
241
|
default_callbacks=self.default_callbacks,
|
206
|
-
|
242
|
+
**(
|
243
|
+
{"connection_file": connection_file}
|
244
|
+
if connection_file is not None
|
245
|
+
else {}
|
246
|
+
),
|
207
247
|
)
|
248
|
+
|
208
249
|
self.comms = comms or {} # The client-side comm states
|
209
|
-
self.completers.append(KernelCompleter(self.kernel))
|
210
|
-
self.inspectors.append(KernelInspector(self.kernel))
|
250
|
+
self.completers.append(KernelCompleter(lambda: self.kernel))
|
251
|
+
self.inspectors.append(KernelInspector(lambda: self.kernel))
|
211
252
|
self.use_kernel_history = use_kernel_history
|
212
253
|
self.history = (
|
213
|
-
KernelHistory(self.kernel)
|
254
|
+
KernelHistory(lambda: self.kernel)
|
255
|
+
if use_kernel_history
|
256
|
+
else InMemoryHistory()
|
214
257
|
)
|
215
|
-
|
258
|
+
|
259
|
+
def _get_suggester() -> AutoSuggest | None:
|
260
|
+
return autosuggest_factory(self.app.config.autosuggest, self.history)
|
261
|
+
|
262
|
+
self.suggester = DynamicAutoSuggest(_get_suggester)
|
216
263
|
|
217
264
|
self.app.create_background_task(self.load_lsps())
|
218
265
|
|
@@ -220,7 +267,7 @@ class KernelTab(Tab, metaclass=ABCMeta):
|
|
220
267
|
|
221
268
|
def close(self, cb: Callable | None = None) -> None:
|
222
269
|
"""Shut down kernel when tab is closed."""
|
223
|
-
if
|
270
|
+
if self.kernel is not None:
|
224
271
|
self.kernel.shutdown()
|
225
272
|
super().close(cb)
|
226
273
|
|
@@ -232,6 +279,7 @@ class KernelTab(Tab, metaclass=ABCMeta):
|
|
232
279
|
"""Restart the current `Notebook`'s kernel."""
|
233
280
|
|
234
281
|
def _cb(result: dict[str, Any]) -> None:
|
282
|
+
self.kernel_started()
|
235
283
|
if callable(cb):
|
236
284
|
cb()
|
237
285
|
|
@@ -245,18 +293,8 @@ class KernelTab(Tab, metaclass=ABCMeta):
|
|
245
293
|
|
246
294
|
def kernel_started(self, result: dict[str, Any] | None = None) -> None:
|
247
295
|
"""Task to run when the kernel has started."""
|
248
|
-
#
|
249
|
-
if
|
250
|
-
if not self.kernel_name:
|
251
|
-
msg = "No kernel selected"
|
252
|
-
else:
|
253
|
-
msg = f"Kernel '{self.kernel_display_name}' not installed"
|
254
|
-
self.change_kernel(
|
255
|
-
msg=msg,
|
256
|
-
startup=True,
|
257
|
-
)
|
258
|
-
|
259
|
-
elif self.kernel.status == "error":
|
296
|
+
# Set kernel spec in metadata
|
297
|
+
if self.kernel.status == "error":
|
260
298
|
self.report_kernel_error(self.kernel.error)
|
261
299
|
|
262
300
|
else:
|
@@ -300,9 +338,7 @@ class KernelTab(Tab, metaclass=ABCMeta):
|
|
300
338
|
@property
|
301
339
|
def kernel_name(self) -> str:
|
302
340
|
"""Return the name of the kernel defined in the notebook JSON."""
|
303
|
-
return self.metadata.get("kernelspec", {}).get(
|
304
|
-
"name", self.app.config.kernel_name
|
305
|
-
)
|
341
|
+
return self.metadata.get("kernelspec", {}).get("name", "")
|
306
342
|
|
307
343
|
@kernel_name.setter
|
308
344
|
def kernel_name(self, value: str) -> None:
|
@@ -337,22 +373,32 @@ class KernelTab(Tab, metaclass=ABCMeta):
|
|
337
373
|
|
338
374
|
def change_kernel(self, msg: str | None = None, startup: bool = False) -> None:
|
339
375
|
"""Prompt the user to select a new kernel."""
|
340
|
-
|
376
|
+
kernel_infos = list_kernels()
|
341
377
|
|
342
378
|
# Warn the user if no kernels are installed
|
343
|
-
if not
|
379
|
+
if not kernel_infos:
|
344
380
|
if startup and "no-kernels" in self.app.dialogs:
|
345
381
|
self.app.dialogs["no-kernels"].show()
|
346
382
|
return
|
347
383
|
|
348
384
|
# Automatically select the only kernel if there is only one
|
349
|
-
if startup and len(
|
350
|
-
self.
|
385
|
+
if startup and len(kernel_infos) == 1:
|
386
|
+
self.switch_kernel(next(iter(kernel_infos)).factory)
|
351
387
|
return
|
352
388
|
|
353
|
-
|
354
|
-
|
389
|
+
# Prompt user to select a kernel
|
390
|
+
self.app.dialogs["change-kernel"].show(tab=self, message=msg)
|
391
|
+
|
392
|
+
def switch_kernel(self, factory: KernelFactory) -> None:
|
393
|
+
"""Shut down the current kernel and change to another."""
|
394
|
+
if (old_kernel := self.kernel) is not None:
|
395
|
+
old_kernel.shutdown(wait=True)
|
396
|
+
kernel = factory(
|
397
|
+
kernel_tab=self,
|
398
|
+
default_callbacks=self.default_callbacks,
|
399
|
+
allow_stdin=self.allow_stdin,
|
355
400
|
)
|
401
|
+
self.init_kernel(kernel)
|
356
402
|
|
357
403
|
def comm_open(self, content: dict, buffers: Sequence[bytes]) -> None:
|
358
404
|
"""Register a new kernel Comm object in the notebook."""
|
euporie/core/tabs/notebook.py
CHANGED
@@ -12,10 +12,8 @@ import nbformat
|
|
12
12
|
from prompt_toolkit.filters import Never
|
13
13
|
|
14
14
|
from euporie.core.comm.registry import open_comm
|
15
|
-
from euporie.core.commands import get_cmd
|
16
15
|
from euporie.core.io import edit_in_editor
|
17
|
-
from euporie.core.kernel.
|
18
|
-
from euporie.core.path import UntitledPath
|
16
|
+
from euporie.core.kernel.base import MsgCallbacks
|
19
17
|
from euporie.core.tabs.kernel import KernelTab
|
20
18
|
from euporie.core.widgets.cell import Cell, get_cell_id
|
21
19
|
|
@@ -27,15 +25,17 @@ except ModuleNotFoundError:
|
|
27
25
|
from nbformat import write as write_nb
|
28
26
|
|
29
27
|
if TYPE_CHECKING:
|
28
|
+
from collections.abc import Sequence
|
30
29
|
from pathlib import Path
|
31
30
|
from typing import Any, Callable
|
32
31
|
|
33
32
|
from prompt_toolkit.filters import Filter
|
34
33
|
from prompt_toolkit.layout.containers import AnyContainer
|
34
|
+
from prompt_toolkit.layout.controls import BufferControl
|
35
35
|
|
36
36
|
from euporie.core.app.app import BaseApp
|
37
37
|
from euporie.core.comm.base import Comm
|
38
|
-
from euporie.core.kernel.
|
38
|
+
from euporie.core.kernel.base import BaseKernel
|
39
39
|
from euporie.core.lsp import LspClient
|
40
40
|
from euporie.core.widgets.inputs import KernelInput
|
41
41
|
|
@@ -52,7 +52,7 @@ class BaseNotebook(KernelTab, metaclass=ABCMeta):
|
|
52
52
|
self,
|
53
53
|
app: BaseApp,
|
54
54
|
path: Path | None = None,
|
55
|
-
kernel:
|
55
|
+
kernel: BaseKernel | None = None,
|
56
56
|
comms: dict[str, Comm] | None = None,
|
57
57
|
use_kernel_history: bool = False,
|
58
58
|
json: dict[str, Any] | None = None,
|
@@ -92,6 +92,9 @@ class BaseNotebook(KernelTab, metaclass=ABCMeta):
|
|
92
92
|
app, path, kernel=kernel, comms=comms, use_kernel_history=use_kernel_history
|
93
93
|
)
|
94
94
|
|
95
|
+
# Load notebook file
|
96
|
+
self.container = self.load_container()
|
97
|
+
|
95
98
|
# Tab stuff
|
96
99
|
|
97
100
|
def reset(self) -> None:
|
@@ -104,24 +107,10 @@ class BaseNotebook(KernelTab, metaclass=ABCMeta):
|
|
104
107
|
|
105
108
|
# KernelTab stuff
|
106
109
|
|
107
|
-
def pre_init_kernel(self) -> None:
|
108
|
-
"""Run stuff before the kernel is loaded."""
|
109
|
-
super().pre_init_kernel()
|
110
|
-
# Load notebook file
|
111
|
-
self.load()
|
112
|
-
|
113
110
|
def post_init_kernel(self) -> None:
|
114
111
|
"""Load the notebook container after the kernel has been loaded."""
|
115
112
|
super().post_init_kernel()
|
116
113
|
|
117
|
-
# Replace the tab's container
|
118
|
-
prev = self.container
|
119
|
-
self.container = self.load_container()
|
120
|
-
self.loaded = True
|
121
|
-
# Update the focus if the old container had focus
|
122
|
-
if self.app.layout.has_focus(prev):
|
123
|
-
self.focus()
|
124
|
-
|
125
114
|
# Load widgets
|
126
115
|
self.load_widgets_from_metadata()
|
127
116
|
|
@@ -179,8 +168,7 @@ class BaseNotebook(KernelTab, metaclass=ABCMeta):
|
|
179
168
|
|
180
169
|
@abstractmethod
|
181
170
|
def load_container(self) -> AnyContainer:
|
182
|
-
"""
|
183
|
-
...
|
171
|
+
"""Absract method for loading the notebook's main container."""
|
184
172
|
|
185
173
|
@abstractproperty
|
186
174
|
def cell(self) -> Cell:
|
@@ -270,7 +258,7 @@ class BaseNotebook(KernelTab, metaclass=ABCMeta):
|
|
270
258
|
if self.dirty and (unsaved := self.app.dialogs.get("unsaved")):
|
271
259
|
unsaved.show(
|
272
260
|
tab=self,
|
273
|
-
cb=cb,
|
261
|
+
cb=partial(super().close, cb),
|
274
262
|
)
|
275
263
|
else:
|
276
264
|
super().close(cb)
|
@@ -422,3 +410,7 @@ class BaseNotebook(KernelTab, metaclass=ABCMeta):
|
|
422
410
|
def lsp_update_diagnostics(self, lsp: LspClient) -> None:
|
423
411
|
"""Process a new diagnostic report from the LSP."""
|
424
412
|
# Do nothing, these are handled by cells
|
413
|
+
|
414
|
+
def __pt_searchables__(self) -> Sequence[BufferControl]:
|
415
|
+
"""Return list of cell input buffer controls for searching."""
|
416
|
+
return [cell.input_box.control for cell in self.rendered_cells()]
|
euporie/core/utils.py
CHANGED
euporie/core/validation.py
CHANGED
@@ -9,29 +9,29 @@ from prompt_toolkit.validation import ValidationError, Validator
|
|
9
9
|
if TYPE_CHECKING:
|
10
10
|
from prompt_toolkit.document import Document
|
11
11
|
|
12
|
-
from euporie.core.kernel.
|
12
|
+
from euporie.core.kernel.base import BaseKernel
|
13
13
|
|
14
14
|
|
15
15
|
class KernelValidator(Validator):
|
16
16
|
"""Validate kernel input using a kernel code completeness call."""
|
17
17
|
|
18
|
-
def __init__(self, kernel:
|
18
|
+
def __init__(self, kernel: BaseKernel) -> None:
|
19
19
|
"""Initialize the validator."""
|
20
20
|
self.kernel = kernel
|
21
21
|
|
22
22
|
def validate(self, document: Document) -> None:
|
23
23
|
"""Validate the input synchronously."""
|
24
|
-
completeness_status = self.kernel.is_complete(
|
25
|
-
|
26
|
-
)
|
24
|
+
completeness_status = self.kernel.is_complete(source=document.text).get(
|
25
|
+
"status", "unknown"
|
26
|
+
)
|
27
27
|
if completeness_status == "incomplete":
|
28
28
|
raise ValidationError
|
29
29
|
|
30
30
|
async def validate_async(self, document: Document) -> None:
|
31
31
|
"""Return a `Future` which is set when the validation is ready."""
|
32
|
-
completeness_status = (
|
33
|
-
|
34
|
-
)
|
32
|
+
completeness_status = (
|
33
|
+
await self.kernel.is_complete_async(source=document.text)
|
34
|
+
).get("status", "unknown")
|
35
35
|
if completeness_status == "incomplete":
|
36
36
|
raise ValidationError
|
37
37
|
return
|
@@ -22,6 +22,22 @@ add_setting(
|
|
22
22
|
cmd_filter=~buffer_has_focus,
|
23
23
|
)
|
24
24
|
|
25
|
+
add_setting(
|
26
|
+
name="text_output_limit",
|
27
|
+
group="euporie.core.widgets.cell_outputs",
|
28
|
+
flags=["--text-output-limit"],
|
29
|
+
type_=int,
|
30
|
+
help_="Limit the amount of cell text output",
|
31
|
+
default=1_000_000,
|
32
|
+
schema={
|
33
|
+
"minimum": 0,
|
34
|
+
},
|
35
|
+
description="""
|
36
|
+
Limit the number of text characters in interactive cell text output to this value.
|
37
|
+
Use ``0`` to allow any amount of characters.
|
38
|
+
""",
|
39
|
+
)
|
40
|
+
|
25
41
|
# euporie,core.widgets.file_browser:FileBrowser
|
26
42
|
|
27
43
|
add_setting(
|
@@ -86,9 +102,10 @@ add_setting(
|
|
86
102
|
name="autosuggest",
|
87
103
|
group="euporie.core.widgets.inputs",
|
88
104
|
flags=["--autosuggest"],
|
89
|
-
type_=
|
105
|
+
type_=str,
|
106
|
+
choices=["smart", "simple", "none"],
|
90
107
|
help_="Provide line completion suggestions",
|
91
|
-
default=
|
108
|
+
default="smart",
|
92
109
|
description="""
|
93
110
|
Whether to automatically suggestion line content while typing in code cells.
|
94
111
|
""",
|