euporie 2.8.4__py3-none-any.whl → 2.8.6__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/_commands.py +143 -0
- euporie/console/_settings.py +58 -0
- euporie/console/app.py +25 -71
- euporie/console/tabs/console.py +58 -62
- euporie/core/__init__.py +1 -1
- euporie/core/__main__.py +28 -11
- euporie/core/_settings.py +109 -0
- euporie/core/app/__init__.py +3 -0
- euporie/core/app/_commands.py +95 -0
- euporie/core/app/_settings.py +457 -0
- euporie/core/{app.py → app/app.py} +212 -576
- euporie/core/app/base.py +51 -0
- euporie/core/{current.py → app/current.py} +13 -4
- euporie/core/app/cursor.py +35 -0
- euporie/core/app/dummy.py +12 -0
- euporie/core/app/launch.py +28 -0
- euporie/core/bars/__init__.py +11 -0
- euporie/core/bars/command.py +205 -0
- euporie/core/bars/menu.py +258 -0
- euporie/core/{widgets → bars}/search.py +20 -16
- euporie/core/{widgets → bars}/status.py +6 -23
- euporie/core/clipboard.py +19 -80
- euporie/core/comm/base.py +8 -6
- euporie/core/comm/ipywidgets.py +16 -7
- euporie/core/comm/registry.py +2 -1
- euporie/core/commands.py +10 -20
- euporie/core/completion.py +3 -2
- euporie/core/config.py +368 -341
- euporie/core/convert/__init__.py +0 -30
- euporie/core/convert/datum.py +116 -53
- euporie/core/convert/formats/__init__.py +31 -0
- euporie/core/convert/formats/ansi.py +9 -23
- euporie/core/convert/formats/common.py +11 -23
- euporie/core/convert/formats/html.py +45 -40
- euporie/core/convert/formats/pil.py +1 -1
- euporie/core/convert/formats/png.py +3 -5
- euporie/core/convert/formats/sixel.py +3 -3
- euporie/core/convert/registry.py +4 -6
- euporie/core/convert/utils.py +41 -4
- euporie/core/diagnostics.py +2 -2
- euporie/core/filters.py +98 -40
- euporie/core/format.py +2 -3
- euporie/core/ft/ansi.py +1 -1
- euporie/core/ft/html.py +12 -21
- euporie/core/ft/table.py +1 -3
- euporie/core/ft/utils.py +4 -1
- euporie/core/graphics.py +386 -133
- euporie/core/history.py +2 -2
- euporie/core/inspection.py +3 -2
- euporie/core/io.py +207 -28
- euporie/core/kernel/__init__.py +1 -0
- euporie/core/{kernel.py → kernel/client.py} +45 -108
- euporie/core/kernel/manager.py +114 -0
- euporie/core/key_binding/bindings/__init__.py +1 -8
- euporie/core/key_binding/bindings/basic.py +47 -7
- euporie/core/key_binding/bindings/completion.py +3 -8
- euporie/core/key_binding/bindings/micro.py +1 -6
- euporie/core/key_binding/bindings/mouse.py +2 -2
- euporie/core/key_binding/bindings/terminal.py +193 -0
- euporie/core/key_binding/key_processor.py +43 -2
- euporie/core/key_binding/registry.py +2 -0
- euporie/core/key_binding/utils.py +22 -2
- euporie/core/keys.py +7156 -93
- euporie/core/layout/cache.py +3 -3
- euporie/core/layout/containers.py +48 -4
- euporie/core/layout/decor.py +2 -2
- euporie/core/layout/mouse.py +1 -1
- euporie/core/layout/print.py +2 -1
- euporie/core/layout/scroll.py +39 -34
- euporie/core/log.py +76 -64
- euporie/core/lsp.py +118 -24
- euporie/core/margins.py +1 -1
- euporie/core/path.py +62 -13
- euporie/core/renderer.py +58 -17
- euporie/core/style.py +57 -39
- euporie/core/suggest.py +103 -85
- euporie/core/tabs/__init__.py +32 -0
- euporie/core/tabs/_settings.py +113 -0
- euporie/core/tabs/base.py +80 -470
- euporie/core/tabs/kernel.py +419 -0
- euporie/core/tabs/notebook.py +24 -101
- euporie/core/utils.py +92 -15
- euporie/core/validation.py +1 -1
- euporie/core/widgets/_settings.py +188 -0
- euporie/core/widgets/cell.py +19 -50
- euporie/core/widgets/cell_outputs.py +25 -36
- euporie/core/widgets/decor.py +11 -41
- euporie/core/widgets/dialog.py +62 -27
- euporie/core/widgets/display.py +12 -15
- euporie/core/widgets/file_browser.py +2 -23
- euporie/core/widgets/forms.py +8 -5
- euporie/core/widgets/inputs.py +13 -70
- euporie/core/widgets/layout.py +2 -1
- euporie/core/widgets/logo.py +49 -0
- euporie/core/widgets/menu.py +10 -8
- euporie/core/widgets/pager.py +6 -10
- euporie/core/widgets/palette.py +6 -6
- euporie/hub/app.py +52 -35
- euporie/notebook/_commands.py +24 -0
- euporie/notebook/_settings.py +107 -0
- euporie/notebook/app.py +49 -171
- euporie/notebook/filters.py +1 -1
- euporie/notebook/tabs/__init__.py +46 -7
- euporie/notebook/tabs/_commands.py +714 -0
- euporie/notebook/tabs/_settings.py +32 -0
- euporie/notebook/tabs/display.py +4 -4
- euporie/notebook/tabs/edit.py +11 -44
- euporie/notebook/tabs/json.py +5 -5
- euporie/notebook/tabs/log.py +1 -18
- euporie/notebook/tabs/notebook.py +11 -660
- euporie/notebook/widgets/_commands.py +11 -0
- euporie/notebook/widgets/_settings.py +19 -0
- euporie/notebook/widgets/side_bar.py +14 -34
- euporie/preview/_settings.py +104 -0
- euporie/preview/app.py +6 -31
- euporie/preview/tabs/notebook.py +6 -72
- euporie/web/__init__.py +1 -0
- euporie/web/tabs/__init__.py +14 -0
- euporie/web/tabs/web.py +11 -6
- euporie/web/widgets/__init__.py +1 -0
- euporie/web/widgets/webview.py +5 -15
- {euporie-2.8.4.dist-info → euporie-2.8.6.dist-info}/METADATA +10 -8
- euporie-2.8.6.dist-info/RECORD +175 -0
- {euporie-2.8.4.dist-info → euporie-2.8.6.dist-info}/WHEEL +1 -1
- {euporie-2.8.4.dist-info → euporie-2.8.6.dist-info}/entry_points.txt +2 -2
- {euporie-2.8.4.dist-info → euporie-2.8.6.dist-info}/licenses/LICENSE +1 -1
- euporie/core/launch.py +0 -64
- euporie/core/terminal.py +0 -522
- euporie-2.8.4.dist-info/RECORD +0 -147
- {euporie-2.8.4.data → euporie-2.8.6.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.8.4.data → euporie-2.8.6.data}/data/share/applications/euporie-notebook.desktop +0 -0
@@ -0,0 +1,143 @@
|
|
1
|
+
"""Contains commands for the console."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import TYPE_CHECKING
|
6
|
+
|
7
|
+
from prompt_toolkit.filters.app import (
|
8
|
+
buffer_has_focus,
|
9
|
+
has_selection,
|
10
|
+
)
|
11
|
+
|
12
|
+
from euporie.core.commands import add_cmd
|
13
|
+
from euporie.core.filters import (
|
14
|
+
buffer_is_code,
|
15
|
+
buffer_is_empty,
|
16
|
+
kernel_tab_has_focus,
|
17
|
+
)
|
18
|
+
from euporie.core.tabs.kernel import KernelTab
|
19
|
+
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
|
22
|
+
|
23
|
+
|
24
|
+
@add_cmd()
|
25
|
+
async def _convert_to_notebook() -> None:
|
26
|
+
"""Convert the current console session to a notebook."""
|
27
|
+
from prompt_toolkit.application.run_in_terminal import in_terminal
|
28
|
+
|
29
|
+
from euporie.console.app import get_app
|
30
|
+
from euporie.console.tabs.console import Console
|
31
|
+
from euporie.notebook.app import NotebookApp
|
32
|
+
|
33
|
+
app = get_app()
|
34
|
+
NotebookApp.config = app.config
|
35
|
+
NotebookApp.load_settings()
|
36
|
+
NotebookApp.config.__init__(app="notebook") # type: ignore [misc]
|
37
|
+
nb_app = NotebookApp()
|
38
|
+
# Use same event loop
|
39
|
+
nb_app.loop = app.loop
|
40
|
+
for tab in app.tabs:
|
41
|
+
if isinstance(tab, Console):
|
42
|
+
from euporie.notebook.tabs.notebook import Notebook
|
43
|
+
|
44
|
+
nb = Notebook(
|
45
|
+
app=nb_app,
|
46
|
+
path=tab.path,
|
47
|
+
kernel=tab.kernel,
|
48
|
+
comms=tab.comms,
|
49
|
+
json=tab.json,
|
50
|
+
)
|
51
|
+
# Set the history to the console's history
|
52
|
+
nb.history = tab.history
|
53
|
+
# Add the current input
|
54
|
+
nb.add(len(nb.json["cells"]) + 1, source=tab.input_box.buffer.text)
|
55
|
+
# Add the new notebook to the notebook app
|
56
|
+
nb_app.tabs.append(nb)
|
57
|
+
# Tell notebook that the kernel has already started
|
58
|
+
nb.kernel_started()
|
59
|
+
|
60
|
+
async with in_terminal():
|
61
|
+
await nb_app.run_async()
|
62
|
+
|
63
|
+
app.exit()
|
64
|
+
|
65
|
+
|
66
|
+
@add_cmd()
|
67
|
+
def _accept_input() -> None:
|
68
|
+
"""Accept the current console input."""
|
69
|
+
from euporie.console.app import get_app
|
70
|
+
|
71
|
+
buffer = get_app().current_buffer
|
72
|
+
if buffer:
|
73
|
+
buffer.validate_and_handle()
|
74
|
+
|
75
|
+
|
76
|
+
@add_cmd(
|
77
|
+
filter=buffer_is_code & buffer_has_focus & ~has_selection & ~buffer_is_empty,
|
78
|
+
)
|
79
|
+
def _clear_input() -> None:
|
80
|
+
"""Clear the console input."""
|
81
|
+
from euporie.console.app import get_app
|
82
|
+
|
83
|
+
buffer = get_app().current_buffer
|
84
|
+
buffer.reset()
|
85
|
+
|
86
|
+
|
87
|
+
@add_cmd(
|
88
|
+
filter=buffer_is_code & buffer_has_focus,
|
89
|
+
)
|
90
|
+
def _run_input() -> None:
|
91
|
+
"""Run the console input."""
|
92
|
+
from euporie.console.app import get_app
|
93
|
+
from euporie.console.tabs.console import Console
|
94
|
+
|
95
|
+
if isinstance(console := get_app().tab, Console):
|
96
|
+
console.run()
|
97
|
+
|
98
|
+
|
99
|
+
@add_cmd(
|
100
|
+
name="cc-interrupt-kernel",
|
101
|
+
hidden=True,
|
102
|
+
filter=buffer_is_code & buffer_is_empty,
|
103
|
+
)
|
104
|
+
@add_cmd(filter=kernel_tab_has_focus)
|
105
|
+
def _interrupt_kernel() -> None:
|
106
|
+
"""Interrupt the notebook's kernel."""
|
107
|
+
from euporie.console.app import get_app
|
108
|
+
|
109
|
+
if isinstance(kt := get_app().tab, KernelTab):
|
110
|
+
kt.interrupt_kernel()
|
111
|
+
|
112
|
+
|
113
|
+
@add_cmd(filter=kernel_tab_has_focus)
|
114
|
+
def _restart_kernel() -> None:
|
115
|
+
"""Restart the notebook's kernel."""
|
116
|
+
from euporie.console.app import get_app
|
117
|
+
|
118
|
+
if isinstance(kt := get_app().tab, KernelTab):
|
119
|
+
kt.restart_kernel()
|
120
|
+
|
121
|
+
|
122
|
+
@add_cmd(
|
123
|
+
filter=buffer_is_code & buffer_is_empty,
|
124
|
+
hidden=True,
|
125
|
+
description="Signals the end of the input, causing the console to exit.",
|
126
|
+
)
|
127
|
+
def _end_of_file(event: KeyPressEvent) -> None:
|
128
|
+
"""Exit when Control-D has been pressed."""
|
129
|
+
event.app.exit(exception=EOFError)
|
130
|
+
|
131
|
+
|
132
|
+
@add_cmd()
|
133
|
+
def _clear_screen() -> None:
|
134
|
+
"""Clear the screen and the previous output."""
|
135
|
+
from euporie.console.app import get_app
|
136
|
+
from euporie.console.tabs.console import Console
|
137
|
+
|
138
|
+
app = get_app()
|
139
|
+
app.renderer.clear()
|
140
|
+
|
141
|
+
if isinstance(console := app.tab, Console):
|
142
|
+
console.reset()
|
143
|
+
app.layout.focus(console.input_box)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
"""Settings for the console app."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import TYPE_CHECKING
|
6
|
+
|
7
|
+
from upath import UPath
|
8
|
+
|
9
|
+
from euporie.core.config import add_setting
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from typing import TypeVar
|
13
|
+
|
14
|
+
_AppResult = TypeVar("_AppResult")
|
15
|
+
|
16
|
+
add_setting(
|
17
|
+
name="mouse_support",
|
18
|
+
group="euporie.console.app",
|
19
|
+
flags=["--mouse-support"],
|
20
|
+
type_=bool,
|
21
|
+
help_="Enable or disable mouse support",
|
22
|
+
default=None,
|
23
|
+
description="""
|
24
|
+
When set to True, mouse support is enabled. When set to False, mouse
|
25
|
+
support is disabled.
|
26
|
+
""",
|
27
|
+
)
|
28
|
+
add_setting(
|
29
|
+
name="max_stored_outputs",
|
30
|
+
group="euporie.console.app",
|
31
|
+
flags=["--max-stored-outputs"],
|
32
|
+
type_=int,
|
33
|
+
help_="The number of inputs / outputs to store in an in-memory notebook",
|
34
|
+
default=100,
|
35
|
+
schema={
|
36
|
+
"minimum": 0,
|
37
|
+
},
|
38
|
+
description="""
|
39
|
+
Defines the maximum number of executed "cells" to store in case the console
|
40
|
+
session is saved to a file or converted into a notebook.
|
41
|
+
""",
|
42
|
+
)
|
43
|
+
|
44
|
+
add_setting(
|
45
|
+
name="connection_file",
|
46
|
+
group="euporie.console.app",
|
47
|
+
flags=["--connection-file", "--kernel-connection-file"],
|
48
|
+
type_=UPath,
|
49
|
+
help_="Attempt to connect to an existing kernel using a JSON connection info file",
|
50
|
+
default=None,
|
51
|
+
description="""
|
52
|
+
If the file does not exist, kernel connection information will be written
|
53
|
+
to the file path provided.
|
54
|
+
|
55
|
+
If the file exists, kernel connection info will be read from the file,
|
56
|
+
allowing euporie to connect to existing kernels.
|
57
|
+
""",
|
58
|
+
)
|
euporie/console/app.py
CHANGED
@@ -6,7 +6,6 @@ import logging
|
|
6
6
|
from typing import TYPE_CHECKING, cast
|
7
7
|
|
8
8
|
from prompt_toolkit.application.current import get_app as ptk_get_app
|
9
|
-
from prompt_toolkit.application.run_in_terminal import in_terminal
|
10
9
|
from prompt_toolkit.filters.app import (
|
11
10
|
has_completions,
|
12
11
|
is_done,
|
@@ -15,6 +14,7 @@ from prompt_toolkit.filters.app import (
|
|
15
14
|
)
|
16
15
|
from prompt_toolkit.layout.containers import (
|
17
16
|
ConditionalContainer,
|
17
|
+
Float,
|
18
18
|
FloatContainer,
|
19
19
|
HSplit,
|
20
20
|
VSplit,
|
@@ -23,10 +23,11 @@ from prompt_toolkit.layout.containers import (
|
|
23
23
|
from prompt_toolkit.layout.dimension import Dimension
|
24
24
|
|
25
25
|
from euporie.console.tabs.console import Console
|
26
|
-
from euporie.core import
|
27
|
-
from euporie.core.
|
28
|
-
from euporie.core.
|
29
|
-
from euporie.core.
|
26
|
+
from euporie.core.app.app import BaseApp
|
27
|
+
from euporie.core.bars.command import CommandBar
|
28
|
+
from euporie.core.bars.menu import ToolbarCompletionsMenu
|
29
|
+
from euporie.core.bars.search import SearchBar
|
30
|
+
from euporie.core.bars.status import StatusBar
|
30
31
|
from euporie.core.filters import has_dialog
|
31
32
|
from euporie.core.layout.mouse import DisableMouseOnScroll
|
32
33
|
from euporie.core.widgets.dialog import (
|
@@ -36,14 +37,15 @@ from euporie.core.widgets.dialog import (
|
|
36
37
|
SelectKernelDialog,
|
37
38
|
ShortcutsDialog,
|
38
39
|
)
|
40
|
+
from euporie.core.widgets.logo import Logo
|
39
41
|
from euporie.core.widgets.pager import Pager
|
40
42
|
from euporie.core.widgets.palette import CommandPalette
|
41
|
-
from euporie.core.widgets.search import SearchBar
|
42
|
-
from euporie.core.widgets.status import StatusBar
|
43
43
|
|
44
44
|
if TYPE_CHECKING:
|
45
45
|
from typing import Any, TypeVar
|
46
46
|
|
47
|
+
from prompt_toolkit.application.application import Application
|
48
|
+
|
47
49
|
_AppResult = TypeVar("_AppResult")
|
48
50
|
|
49
51
|
log = logging.getLogger(__name__)
|
@@ -63,7 +65,6 @@ class ConsoleApp(BaseApp):
|
|
63
65
|
"""
|
64
66
|
|
65
67
|
name = "console"
|
66
|
-
log_stdout_level = "ERROR"
|
67
68
|
|
68
69
|
def __init__(self, **kwargs: Any) -> None:
|
69
70
|
"""Create a new euporie text user interface application instance."""
|
@@ -72,16 +73,22 @@ class ConsoleApp(BaseApp):
|
|
72
73
|
kwargs.setdefault("title", "euporie-console")
|
73
74
|
kwargs.setdefault("full_screen", False)
|
74
75
|
kwargs.setdefault("leave_graphics", True)
|
75
|
-
kwargs.setdefault("mouse_support", self.config.
|
76
|
+
kwargs.setdefault("mouse_support", self.config.filters.mouse_support)
|
76
77
|
|
77
78
|
# Initialize the application
|
78
79
|
super().__init__(**kwargs)
|
79
80
|
|
80
|
-
self.search_bar = SearchBar()
|
81
81
|
self.bindings_to_load += ["euporie.console.app.ConsoleApp"]
|
82
82
|
|
83
83
|
self.tabs = []
|
84
|
-
|
84
|
+
|
85
|
+
def pre_run(self, app: Application | None = None) -> None:
|
86
|
+
"""Continue loading the app."""
|
87
|
+
super().pre_run(app)
|
88
|
+
# Add a toolbar completion menu
|
89
|
+
self.menus["toolbar_completions"] = Float(
|
90
|
+
content=ToolbarCompletionsMenu(), ycursor=True, transparent=True
|
91
|
+
)
|
85
92
|
|
86
93
|
def _get_reserved_height(self) -> Dimension:
|
87
94
|
if has_dialog():
|
@@ -95,9 +102,9 @@ class ConsoleApp(BaseApp):
|
|
95
102
|
"""Return a container with all opened tabs."""
|
96
103
|
self.tabs = [Console(self)]
|
97
104
|
|
98
|
-
|
99
|
-
|
100
|
-
|
105
|
+
self.command_bar = CommandBar()
|
106
|
+
self.search_bar = SearchBar()
|
107
|
+
self.pager = Pager()
|
101
108
|
|
102
109
|
self.dialogs["command-palette"] = CommandPalette(self)
|
103
110
|
self.dialogs["about"] = AboutDialog(self)
|
@@ -110,7 +117,7 @@ class ConsoleApp(BaseApp):
|
|
110
117
|
DisableMouseOnScroll(
|
111
118
|
HSplit(
|
112
119
|
[
|
113
|
-
self.
|
120
|
+
self.tabs[0],
|
114
121
|
ConditionalContainer(
|
115
122
|
HSplit(
|
116
123
|
[
|
@@ -120,17 +127,12 @@ class ConsoleApp(BaseApp):
|
|
120
127
|
style="class:default",
|
121
128
|
),
|
122
129
|
self.pager,
|
123
|
-
self.search_bar,
|
124
130
|
ConditionalContainer(
|
125
131
|
VSplit(
|
126
132
|
[
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
width=3,
|
131
|
-
style="class:menu,logo",
|
132
|
-
dont_extend_width=True,
|
133
|
-
),
|
133
|
+
Logo(),
|
134
|
+
self.command_bar,
|
135
|
+
self.search_bar,
|
134
136
|
StatusBar(),
|
135
137
|
]
|
136
138
|
),
|
@@ -165,54 +167,6 @@ class ConsoleApp(BaseApp):
|
|
165
167
|
else:
|
166
168
|
super().exit()
|
167
169
|
|
168
|
-
# ################################### Commands ####################################
|
169
|
-
|
170
|
-
@staticmethod
|
171
|
-
@add_cmd()
|
172
|
-
async def _convert_to_notebook() -> None:
|
173
|
-
"""Convert the current console session to a notebook."""
|
174
|
-
from euporie.notebook.app import NotebookApp
|
175
|
-
from euporie.notebook.tabs.notebook import Notebook
|
176
|
-
|
177
|
-
app = get_app()
|
178
|
-
nb_app = NotebookApp()
|
179
|
-
for tab in app.tabs:
|
180
|
-
if isinstance(tab, Console):
|
181
|
-
nb = Notebook(
|
182
|
-
app=nb_app,
|
183
|
-
path=tab.path,
|
184
|
-
kernel=tab.kernel,
|
185
|
-
comms=tab.comms,
|
186
|
-
json=tab.json,
|
187
|
-
)
|
188
|
-
# Set the history to the console's history
|
189
|
-
nb.history = tab.history
|
190
|
-
# Add the current input
|
191
|
-
nb.add(len(nb.json["cells"]) + 1, source=tab.input_box.buffer.text)
|
192
|
-
# Add the new notebook to the notebook app
|
193
|
-
nb_app.tabs.append(nb)
|
194
|
-
# Tell notebook that the kernel has already started
|
195
|
-
nb.kernel_started()
|
196
|
-
|
197
|
-
async with in_terminal():
|
198
|
-
await nb_app.run_async()
|
199
|
-
|
200
|
-
app.exit()
|
201
|
-
|
202
|
-
# ################################### Settings ####################################
|
203
|
-
|
204
|
-
add_setting(
|
205
|
-
name="mouse_support",
|
206
|
-
flags=["--mouse-support"],
|
207
|
-
type_=bool,
|
208
|
-
help_="Enable or disable mouse support",
|
209
|
-
default=None,
|
210
|
-
description="""
|
211
|
-
When set to True, mouse support is enabled. When set to False, mouse
|
212
|
-
support is disabled.
|
213
|
-
""",
|
214
|
-
)
|
215
|
-
|
216
170
|
# ################################# Key Bindings ##################################
|
217
171
|
|
218
172
|
# register_bindings({"euporie.console.app.ConsoleApp": {}})
|
euporie/console/tabs/console.py
CHANGED
@@ -31,7 +31,6 @@ from prompt_toolkit.utils import Event
|
|
31
31
|
from upath import UPath
|
32
32
|
|
33
33
|
from euporie.core.commands import add_cmd, get_cmd
|
34
|
-
from euporie.core.config import add_setting
|
35
34
|
from euporie.core.diagnostics import Report
|
36
35
|
from euporie.core.filters import (
|
37
36
|
at_end_of_buffer,
|
@@ -40,7 +39,8 @@ from euporie.core.filters import (
|
|
40
39
|
kernel_tab_has_focus,
|
41
40
|
)
|
42
41
|
from euporie.core.format import LspFormatter
|
43
|
-
from euporie.core.
|
42
|
+
from euporie.core.io import edit_in_editor
|
43
|
+
from euporie.core.kernel.client import MsgCallbacks
|
44
44
|
from euporie.core.key_binding.registry import (
|
45
45
|
load_registered_bindings,
|
46
46
|
register_bindings,
|
@@ -48,23 +48,24 @@ from euporie.core.key_binding.registry import (
|
|
48
48
|
from euporie.core.layout.print import PrintingContainer
|
49
49
|
from euporie.core.lsp import LspCell
|
50
50
|
from euporie.core.style import KERNEL_STATUS_REPR
|
51
|
-
from euporie.core.tabs.
|
52
|
-
from euporie.core.terminal import edit_in_editor
|
51
|
+
from euporie.core.tabs.kernel import KernelTab
|
53
52
|
from euporie.core.validation import KernelValidator
|
54
53
|
from euporie.core.widgets.cell_outputs import CellOutputArea
|
55
54
|
from euporie.core.widgets.inputs import KernelInput, StdInput
|
56
55
|
|
57
56
|
if TYPE_CHECKING:
|
57
|
+
from collections.abc import Sequence
|
58
58
|
from pathlib import Path
|
59
|
-
from typing import Any, Callable
|
59
|
+
from typing import Any, Callable
|
60
60
|
|
61
|
+
from nbformat.notebooknode import NotebookNode
|
61
62
|
from prompt_toolkit.application.application import Application
|
62
63
|
from prompt_toolkit.formatted_text import AnyFormattedText, StyleAndTextTuples
|
63
64
|
from prompt_toolkit.key_binding.key_bindings import NotImplementedOrNone
|
64
65
|
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
|
65
66
|
from prompt_toolkit.layout.containers import Container, Float
|
66
67
|
|
67
|
-
from euporie.core.app import BaseApp
|
68
|
+
from euporie.core.app.app import BaseApp
|
68
69
|
from euporie.core.lsp import LspClient
|
69
70
|
|
70
71
|
log = logging.getLogger(__name__)
|
@@ -77,8 +78,6 @@ class Console(KernelTab):
|
|
77
78
|
|
78
79
|
"""
|
79
80
|
|
80
|
-
bg_init = False
|
81
|
-
|
82
81
|
def __init__(
|
83
82
|
self,
|
84
83
|
app: BaseApp,
|
@@ -134,6 +133,7 @@ class Console(KernelTab):
|
|
134
133
|
self.json = nbformat.v4.new_notebook()
|
135
134
|
self.json["metadata"] = self._metadata
|
136
135
|
self.render_queue: list[dict[str, Any]] = []
|
136
|
+
self.last_rendered: NotebookNode | None = None
|
137
137
|
|
138
138
|
self.container = self.load_container()
|
139
139
|
|
@@ -197,8 +197,7 @@ class Console(KernelTab):
|
|
197
197
|
return not (
|
198
198
|
not code.strip()
|
199
199
|
or completeness_status == "incomplete"
|
200
|
-
or completeness_status == "unknown"
|
201
|
-
and code[-2:] != "\n\n"
|
200
|
+
or (completeness_status == "unknown" and code[-2:] != "\n\n")
|
202
201
|
)
|
203
202
|
|
204
203
|
def run(self, buffer: Buffer | None = None) -> None:
|
@@ -292,10 +291,30 @@ class Console(KernelTab):
|
|
292
291
|
self.render_queue.append(cell)
|
293
292
|
|
294
293
|
# Add widgets to the live output
|
295
|
-
if
|
296
|
-
|
294
|
+
if output_json.get("output_type") == "stream":
|
295
|
+
# Use live output to enable emulation of carriage returns
|
296
|
+
text = output_json.get("text", "")
|
297
|
+
tail = ""
|
298
|
+
_text, _, _tail = text.rpartition("\n")
|
299
|
+
if "\r" in _tail: # or "\x1b[" in _tail:
|
300
|
+
text, tail = _text, _tail
|
301
|
+
if text:
|
302
|
+
# Partially Flush live output streams
|
303
|
+
cell["outputs"].extend(self.live_output.json)
|
304
|
+
self.live_output.reset()
|
305
|
+
output_json["text"] = text
|
306
|
+
cell["outputs"].append(output_json)
|
307
|
+
if tail:
|
308
|
+
self.live_output.add_output(
|
309
|
+
nbformat.v4.new_output(**{**output_json, "text": tail})
|
310
|
+
)
|
297
311
|
else:
|
298
|
-
|
312
|
+
if "application/vnd.jupyter.widget-view+json" in output_json.get(
|
313
|
+
"data", {}
|
314
|
+
):
|
315
|
+
self.live_output.add_output(output_json)
|
316
|
+
else:
|
317
|
+
cell["outputs"].append(output_json)
|
299
318
|
|
300
319
|
# Invalidate the app so the output get printed
|
301
320
|
self.app.invalidate()
|
@@ -331,7 +350,7 @@ class Console(KernelTab):
|
|
331
350
|
|
332
351
|
def reset(self) -> None:
|
333
352
|
"""Reset the state of the tab."""
|
334
|
-
from euporie.core.
|
353
|
+
from euporie.core.bars.search import stop_search
|
335
354
|
|
336
355
|
self.live_output.reset()
|
337
356
|
stop_search()
|
@@ -349,7 +368,7 @@ class Console(KernelTab):
|
|
349
368
|
) -> StyleAndTextTuples:
|
350
369
|
"""Determine what should be displayed in the prompt of the cell."""
|
351
370
|
if count is None:
|
352
|
-
|
371
|
+
return [("", " " * (len(text) + 4 + len(str(self.execution_count))))]
|
353
372
|
prompt = str(count + offset)
|
354
373
|
if show_busy and self.kernel.status in ("busy", "queued"):
|
355
374
|
prompt = "*".center(len(prompt))
|
@@ -420,12 +439,18 @@ class Console(KernelTab):
|
|
420
439
|
|
421
440
|
# Outputs
|
422
441
|
if outputs := cell.outputs:
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
442
|
+
# Add space before an output if last rendered cell did not have outputs
|
443
|
+
# or we are rendering a new output
|
444
|
+
if self.last_rendered is not None and (
|
445
|
+
not self.last_rendered.outputs
|
446
|
+
or cell.execution_count != self.last_rendered.execution_count
|
447
|
+
):
|
448
|
+
children.append(
|
449
|
+
Window(
|
450
|
+
height=1,
|
451
|
+
dont_extend_height=True,
|
452
|
+
)
|
427
453
|
)
|
428
|
-
)
|
429
454
|
|
430
455
|
def _flush(
|
431
456
|
buffer: list[dict[str, Any]], prompt: AnyFormattedText
|
@@ -450,21 +475,20 @@ class Console(KernelTab):
|
|
450
475
|
buffer.clear()
|
451
476
|
|
452
477
|
buffer: list[dict[str, Any]] = []
|
453
|
-
ec = cell.execution_count
|
478
|
+
# ec = cell.execution_count
|
454
479
|
prompt: AnyFormattedText = ""
|
455
480
|
next_prompt: AnyFormattedText
|
456
481
|
for output in outputs:
|
457
|
-
|
458
|
-
|
459
|
-
else:
|
460
|
-
next_prompt = self.prompt("Out", count=next_ec, show_busy=False)
|
461
|
-
ec = next_ec
|
482
|
+
next_ec = output.get("execution_count")
|
483
|
+
next_prompt = self.prompt("Out", count=next_ec, show_busy=False)
|
462
484
|
if next_prompt != prompt:
|
463
485
|
_flush(buffer, prompt)
|
464
486
|
prompt = next_prompt
|
465
487
|
buffer.append(output)
|
466
488
|
_flush(buffer, prompt)
|
467
489
|
|
490
|
+
self.last_rendered = cell
|
491
|
+
|
468
492
|
return Layout(
|
469
493
|
FloatContainer(
|
470
494
|
PrintingContainer(children),
|
@@ -488,9 +512,9 @@ class Console(KernelTab):
|
|
488
512
|
FormattedTextControl(
|
489
513
|
lambda: self.prompt(
|
490
514
|
"Out",
|
491
|
-
count=self.live_output.json[0]
|
492
|
-
"execution_count"
|
493
|
-
|
515
|
+
count=self.live_output.json[0].get(
|
516
|
+
"execution_count",
|
517
|
+
),
|
494
518
|
)
|
495
519
|
),
|
496
520
|
dont_extend_width=True,
|
@@ -602,7 +626,11 @@ class Console(KernelTab):
|
|
602
626
|
VSplit(
|
603
627
|
[
|
604
628
|
Window(
|
605
|
-
FormattedTextControl(
|
629
|
+
FormattedTextControl(
|
630
|
+
lambda: self.prompt(
|
631
|
+
"In ", self.execution_count, offset=1
|
632
|
+
)
|
633
|
+
),
|
606
634
|
dont_extend_width=True,
|
607
635
|
style="class:cell,input,prompt",
|
608
636
|
height=1,
|
@@ -838,38 +866,6 @@ class Console(KernelTab):
|
|
838
866
|
tab.reset()
|
839
867
|
app.layout.focus(tab.input_box)
|
840
868
|
|
841
|
-
# ################################### Settings ####################################
|
842
|
-
|
843
|
-
add_setting(
|
844
|
-
name="max_stored_outputs",
|
845
|
-
flags=["--max-stored-outputs"],
|
846
|
-
type_=int,
|
847
|
-
help_="The number of inputs / outputs to store in an in-memory notebook",
|
848
|
-
default=100,
|
849
|
-
schema={
|
850
|
-
"minimum": 0,
|
851
|
-
},
|
852
|
-
description="""
|
853
|
-
Defines the maximum number of executed "cells" to store in case the console
|
854
|
-
session is saved to a file or converted into a notebook.
|
855
|
-
""",
|
856
|
-
)
|
857
|
-
|
858
|
-
add_setting(
|
859
|
-
name="connection_file",
|
860
|
-
flags=["--connection-file", "--kernel-connection-file"],
|
861
|
-
type_=UPath,
|
862
|
-
help_="Attempt to connect to an existing kernel using a JSON connection info file",
|
863
|
-
default=None,
|
864
|
-
description="""
|
865
|
-
If the file does not exist, kernel connection information will be written
|
866
|
-
to the file path provided.
|
867
|
-
|
868
|
-
If the file exists, kernel connection info will be read from the file,
|
869
|
-
allowing euporie to connect to existing kernels.
|
870
|
-
""",
|
871
|
-
)
|
872
|
-
|
873
869
|
# ################################# Key Bindings ##################################
|
874
870
|
|
875
871
|
register_bindings(
|
euporie/core/__init__.py
CHANGED
euporie/core/__main__.py
CHANGED
@@ -1,10 +1,33 @@
|
|
1
1
|
"""Main entry point into euporie.core."""
|
2
2
|
|
3
|
+
from __future__ import annotations
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
from functools import cache
|
6
|
+
from importlib.metadata import entry_points
|
7
|
+
from typing import TYPE_CHECKING
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from importlib.metadata import EntryPoint, EntryPoints
|
11
|
+
|
12
|
+
|
13
|
+
@cache
|
14
|
+
def available_apps() -> dict[str, EntryPoint]:
|
15
|
+
"""Return a list of loadable euporie apps."""
|
16
|
+
eps: dict | EntryPoints
|
17
|
+
try:
|
18
|
+
eps = entry_points(group="euporie.apps")
|
19
|
+
except TypeError:
|
20
|
+
eps = entry_points()
|
21
|
+
if isinstance(eps, dict):
|
22
|
+
points = eps.get("euporie.apps")
|
23
|
+
else:
|
24
|
+
points = eps.select(group="euporie.apps")
|
25
|
+
apps = {x.name: x for x in points} if points else {}
|
26
|
+
return apps
|
7
27
|
|
28
|
+
|
29
|
+
def main(name: str = "launch") -> None:
|
30
|
+
"""Load and launches the application."""
|
8
31
|
# Register extensions to external packages
|
9
32
|
from euporie.core import (
|
10
33
|
path, # noqa F401
|
@@ -14,17 +37,11 @@ def main(name: "str" = "launch") -> "None":
|
|
14
37
|
# Monkey-patch prompt_toolkit
|
15
38
|
from euporie.core.layout import containers # noqa: F401
|
16
39
|
|
17
|
-
|
18
|
-
if isinstance(eps, dict):
|
19
|
-
points = eps.get("euporie.apps")
|
20
|
-
else:
|
21
|
-
points = eps.select(group="euporie.apps")
|
22
|
-
apps = {x.name: x for x in points} if points else {}
|
23
|
-
|
40
|
+
apps = available_apps()
|
24
41
|
if entry := apps.get(name):
|
25
42
|
return entry.load().launch()
|
26
43
|
else:
|
27
|
-
raise
|
44
|
+
raise ModuleNotFoundError(f"Euporie app `{name}` not installed")
|
28
45
|
|
29
46
|
|
30
47
|
if __name__ == "__main__":
|