euporie 2.3.2__py3-none-any.whl → 2.4.1__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/__main__.py +3 -1
- euporie/console/app.py +6 -4
- euporie/console/tabs/console.py +34 -9
- euporie/core/__init__.py +6 -1
- euporie/core/__main__.py +1 -1
- euporie/core/app.py +79 -109
- euporie/core/border.py +44 -14
- euporie/core/comm/base.py +5 -4
- euporie/core/comm/ipywidgets.py +11 -11
- euporie/core/comm/registry.py +12 -6
- euporie/core/commands.py +30 -23
- euporie/core/completion.py +1 -4
- euporie/core/config.py +15 -5
- euporie/core/convert/{base.py → core.py} +117 -53
- euporie/core/convert/formats/ansi.py +46 -25
- euporie/core/convert/formats/base64.py +3 -3
- euporie/core/convert/formats/common.py +38 -13
- euporie/core/convert/formats/formatted_text.py +54 -12
- euporie/core/convert/formats/html.py +5 -5
- euporie/core/convert/formats/jpeg.py +1 -1
- euporie/core/convert/formats/markdown.py +4 -4
- euporie/core/convert/formats/pdf.py +1 -1
- euporie/core/convert/formats/pil.py +5 -3
- euporie/core/convert/formats/png.py +7 -6
- euporie/core/convert/formats/rich.py +4 -3
- euporie/core/convert/formats/sixel.py +5 -5
- euporie/core/convert/utils.py +1 -1
- euporie/core/current.py +11 -5
- euporie/core/formatted_text/ansi.py +4 -8
- euporie/core/formatted_text/html.py +1630 -856
- euporie/core/formatted_text/markdown.py +177 -166
- euporie/core/formatted_text/table.py +20 -14
- euporie/core/formatted_text/utils.py +21 -10
- euporie/core/io.py +14 -14
- euporie/core/kernel.py +48 -37
- euporie/core/key_binding/bindings/micro.py +5 -1
- euporie/core/key_binding/bindings/mouse.py +2 -2
- euporie/core/keys.py +3 -0
- euporie/core/launch.py +5 -2
- euporie/core/lexers.py +13 -2
- euporie/core/log.py +135 -139
- euporie/core/margins.py +32 -14
- euporie/core/path.py +273 -0
- euporie/core/processors.py +35 -0
- euporie/core/renderer.py +21 -5
- euporie/core/style.py +34 -19
- euporie/core/tabs/base.py +101 -17
- euporie/core/tabs/notebook.py +72 -30
- euporie/core/terminal.py +56 -48
- euporie/core/utils.py +12 -16
- euporie/core/widgets/cell.py +6 -5
- euporie/core/widgets/cell_outputs.py +2 -2
- euporie/core/widgets/decor.py +74 -82
- euporie/core/widgets/dialog.py +132 -28
- euporie/core/widgets/display.py +76 -24
- euporie/core/widgets/file_browser.py +87 -31
- euporie/core/widgets/formatted_text_area.py +1 -3
- euporie/core/widgets/forms.py +79 -40
- euporie/core/widgets/inputs.py +23 -13
- euporie/core/widgets/layout.py +4 -3
- euporie/core/widgets/menu.py +368 -216
- euporie/core/widgets/page.py +99 -58
- euporie/core/widgets/pager.py +1 -1
- euporie/core/widgets/palette.py +30 -27
- euporie/core/widgets/search_bar.py +38 -25
- euporie/core/widgets/status_bar.py +103 -5
- euporie/data/desktop/euporie-console.desktop +7 -0
- euporie/data/desktop/euporie-notebook.desktop +7 -0
- euporie/hub/__main__.py +3 -1
- euporie/hub/app.py +9 -7
- euporie/notebook/__main__.py +3 -1
- euporie/notebook/app.py +7 -30
- euporie/notebook/tabs/__init__.py +7 -3
- euporie/notebook/tabs/display.py +18 -9
- euporie/notebook/tabs/edit.py +106 -23
- euporie/notebook/tabs/json.py +73 -0
- euporie/notebook/tabs/log.py +18 -8
- euporie/notebook/tabs/notebook.py +60 -41
- euporie/preview/__main__.py +3 -1
- euporie/preview/app.py +2 -1
- euporie/preview/tabs/notebook.py +23 -10
- euporie/web/tabs/web.py +149 -0
- euporie/web/widgets/webview.py +563 -0
- euporie-2.4.1.data/data/share/applications/euporie-console.desktop +7 -0
- euporie-2.4.1.data/data/share/applications/euporie-notebook.desktop +7 -0
- {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/METADATA +6 -5
- euporie-2.4.1.dist-info/RECORD +129 -0
- {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/WHEEL +1 -1
- euporie/core/url.py +0 -64
- euporie-2.3.2.dist-info/RECORD +0 -122
- {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/entry_points.txt +0 -0
- {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/licenses/LICENSE +0 -0
euporie/console/__main__.py
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
|
4
4
|
def main() -> "None":
|
5
5
|
"""Call the main entrypoint to the application."""
|
6
|
+
import sys
|
7
|
+
|
6
8
|
from euporie.core import __main__
|
7
9
|
|
8
|
-
__main__.main(__name__.
|
10
|
+
__main__.main(str(sys.modules[__name__].__package__).rpartition(".")[2])
|
9
11
|
|
10
12
|
|
11
13
|
if __name__ == "__main__":
|
euporie/console/app.py
CHANGED
@@ -22,7 +22,7 @@ from prompt_toolkit.layout.containers import (
|
|
22
22
|
)
|
23
23
|
from prompt_toolkit.layout.dimension import Dimension
|
24
24
|
|
25
|
-
from euporie.console.tabs.console import
|
25
|
+
from euporie.console.tabs.console import Console
|
26
26
|
from euporie.core import __logo__
|
27
27
|
from euporie.core.app import BaseApp
|
28
28
|
from euporie.core.commands import add_cmd
|
@@ -82,7 +82,7 @@ class ConsoleApp(BaseApp):
|
|
82
82
|
self.search_bar = SearchBar()
|
83
83
|
self.bindings_to_load += ["euporie.console.app.ConsoleApp"]
|
84
84
|
|
85
|
-
self.tabs = [
|
85
|
+
self.tabs = []
|
86
86
|
self.pager = Pager()
|
87
87
|
|
88
88
|
self.config.get_item("mouse_support").event += lambda x: setattr(
|
@@ -99,6 +99,8 @@ class ConsoleApp(BaseApp):
|
|
99
99
|
|
100
100
|
def load_container(self) -> FloatContainer:
|
101
101
|
"""Return a container with all opened tabs."""
|
102
|
+
self.tabs = [Console(self)]
|
103
|
+
|
102
104
|
assert self.pager is not None
|
103
105
|
assert self.search_bar is not None
|
104
106
|
assert self.tab is not None
|
@@ -166,7 +168,7 @@ class ConsoleApp(BaseApp):
|
|
166
168
|
app = get_app()
|
167
169
|
nb_app = NotebookApp()
|
168
170
|
for tab in app.tabs:
|
169
|
-
if isinstance(tab,
|
171
|
+
if isinstance(tab, Console):
|
170
172
|
nb = Notebook(
|
171
173
|
app=nb_app,
|
172
174
|
path=tab.path,
|
@@ -195,7 +197,7 @@ class ConsoleApp(BaseApp):
|
|
195
197
|
app = get_app()
|
196
198
|
tab = app.tab
|
197
199
|
app.renderer.clear()
|
198
|
-
if isinstance(tab,
|
200
|
+
if isinstance(tab, Console):
|
199
201
|
tab.reset()
|
200
202
|
app.layout.focus(tab.input_box)
|
201
203
|
|
euporie/console/tabs/console.py
CHANGED
@@ -28,6 +28,7 @@ from prompt_toolkit.layout.containers import (
|
|
28
28
|
)
|
29
29
|
from prompt_toolkit.layout.controls import FormattedTextControl
|
30
30
|
from prompt_toolkit.layout.layout import Layout
|
31
|
+
from upath import UPath
|
31
32
|
|
32
33
|
from euporie.core.commands import add_cmd, get_cmd
|
33
34
|
from euporie.core.config import add_setting
|
@@ -52,6 +53,7 @@ from euporie.core.widgets.page import PrintingContainer
|
|
52
53
|
from euporie.core.widgets.pager import PagerState
|
53
54
|
|
54
55
|
if TYPE_CHECKING:
|
56
|
+
from pathlib import Path
|
55
57
|
from typing import Any, Callable, Sequence
|
56
58
|
|
57
59
|
from prompt_toolkit.application.application import Application
|
@@ -59,32 +61,35 @@ if TYPE_CHECKING:
|
|
59
61
|
from prompt_toolkit.key_binding.key_bindings import NotImplementedOrNone
|
60
62
|
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
|
61
63
|
from prompt_toolkit.layout.containers import Float
|
62
|
-
from upath import UPath
|
63
64
|
|
64
65
|
from euporie.core.app import BaseApp
|
65
66
|
|
66
67
|
log = logging.getLogger(__name__)
|
67
68
|
|
68
69
|
|
69
|
-
class
|
70
|
+
class Console(KernelTab):
|
70
71
|
"""Interactive console.
|
71
72
|
|
72
73
|
An interactive console which connects to a Jupyter kernel.
|
73
74
|
|
74
75
|
"""
|
75
76
|
|
77
|
+
bg_init = False
|
78
|
+
|
76
79
|
def __init__(
|
77
80
|
self,
|
78
81
|
app: BaseApp,
|
79
|
-
path:
|
82
|
+
path: Path | None = None,
|
80
83
|
use_kernel_history: bool = True,
|
84
|
+
connection_file: str = "",
|
81
85
|
) -> None:
|
82
|
-
"""Create a new :py:class:`
|
86
|
+
"""Create a new :py:class:`Console` tab instance.
|
83
87
|
|
84
88
|
Args:
|
85
89
|
app: The euporie application the console tab belongs to
|
86
90
|
path: A file path to open (not used currently)
|
87
91
|
use_kernel_history: If :const:`True`, history will be loaded from the kernel
|
92
|
+
connection_file: The connection file of an existing kernel
|
88
93
|
"""
|
89
94
|
# Kernel setup
|
90
95
|
self._metadata = {}
|
@@ -105,7 +110,12 @@ class ConsoleTab(KernelTab):
|
|
105
110
|
)
|
106
111
|
self.kernel_tab = self
|
107
112
|
|
108
|
-
super().__init__(
|
113
|
+
super().__init__(
|
114
|
+
app=app,
|
115
|
+
path=path,
|
116
|
+
use_kernel_history=use_kernel_history,
|
117
|
+
connection_file=app.config.connection_file,
|
118
|
+
)
|
109
119
|
|
110
120
|
self.lang_info: dict[str, Any] = {}
|
111
121
|
self.execution_count = 0
|
@@ -477,7 +487,7 @@ class ConsoleTab(KernelTab):
|
|
477
487
|
"""Request the output is refreshed (does nothing)."""
|
478
488
|
pass
|
479
489
|
|
480
|
-
def
|
490
|
+
def __pt_status__(
|
481
491
|
self,
|
482
492
|
) -> tuple[Sequence[AnyFormattedText], Sequence[AnyFormattedText]]:
|
483
493
|
"""Generate the formatted text for the statusbar."""
|
@@ -534,7 +544,7 @@ class ConsoleTab(KernelTab):
|
|
534
544
|
callback=_cb,
|
535
545
|
)
|
536
546
|
|
537
|
-
def save(self, path:
|
547
|
+
def save(self, path: Path | None = None, cb: Callable | None = None) -> None:
|
538
548
|
"""Save the console as a notebook."""
|
539
549
|
from euporie.core.tabs.notebook import BaseNotebook
|
540
550
|
|
@@ -573,7 +583,7 @@ class ConsoleTab(KernelTab):
|
|
573
583
|
from euporie.console.app import get_app
|
574
584
|
|
575
585
|
console = get_app().tab
|
576
|
-
assert isinstance(console,
|
586
|
+
assert isinstance(console, Console)
|
577
587
|
console.run()
|
578
588
|
|
579
589
|
@staticmethod
|
@@ -585,7 +595,7 @@ class ConsoleTab(KernelTab):
|
|
585
595
|
from euporie.console.app import get_app
|
586
596
|
|
587
597
|
console = get_app().tab
|
588
|
-
assert isinstance(console,
|
598
|
+
assert isinstance(console, Console)
|
589
599
|
console.inspect()
|
590
600
|
|
591
601
|
@staticmethod
|
@@ -644,6 +654,21 @@ class ConsoleTab(KernelTab):
|
|
644
654
|
""",
|
645
655
|
)
|
646
656
|
|
657
|
+
add_setting(
|
658
|
+
name="connection_file",
|
659
|
+
flags=["--connection-file", "--kernel-connection-file"],
|
660
|
+
type_=UPath,
|
661
|
+
help_="Attempt to connect to an existing kernel using a JSON connection info file",
|
662
|
+
default=None,
|
663
|
+
description="""
|
664
|
+
If the file does not exist, kernel connection information will be written
|
665
|
+
to the file path provided.
|
666
|
+
|
667
|
+
If the file exists, kernel connection info will be read from the file,
|
668
|
+
allowing euporie to connect to existing kernels.
|
669
|
+
""",
|
670
|
+
)
|
671
|
+
|
647
672
|
# ################################# Key Bindings ##################################
|
648
673
|
|
649
674
|
register_bindings(
|
euporie/core/__init__.py
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
"""This package defines the euporie application and its components."""
|
2
2
|
|
3
3
|
__app_name__ = "euporie"
|
4
|
-
__version__ = "2.
|
4
|
+
__version__ = "2.4.1"
|
5
5
|
__logo__ = "⚈"
|
6
6
|
__strapline__ = "Jupyter in the terminal"
|
7
7
|
__author__ = "Josiah Outram Halstead"
|
8
8
|
__email__ = "josiah@halstead.email"
|
9
9
|
__copyright__ = f"© 2022, {__author__}"
|
10
10
|
__license__ = "MIT"
|
11
|
+
|
12
|
+
|
13
|
+
# Register extensions to external packages
|
14
|
+
from euporie.core import path # noqa F401
|
15
|
+
from euporie.core import pygments # noqa F401
|
euporie/core/__main__.py
CHANGED
@@ -5,7 +5,7 @@ def main(name: "str" = "launch") -> "None":
|
|
5
5
|
"""Load and launches the application."""
|
6
6
|
from importlib.metadata import entry_points
|
7
7
|
|
8
|
-
for entry in entry_points()
|
8
|
+
for entry in entry_points().get("euporie.apps", []):
|
9
9
|
if entry.name == name:
|
10
10
|
return entry.load().launch()
|
11
11
|
else:
|
euporie/core/app.py
CHANGED
@@ -5,7 +5,10 @@ from __future__ import annotations
|
|
5
5
|
import asyncio
|
6
6
|
import json
|
7
7
|
import logging
|
8
|
+
import signal
|
9
|
+
import sys
|
8
10
|
from functools import partial
|
11
|
+
from pathlib import PurePath
|
9
12
|
from typing import TYPE_CHECKING, cast
|
10
13
|
from weakref import WeakSet
|
11
14
|
|
@@ -17,7 +20,6 @@ from prompt_toolkit.cursor_shapes import CursorShape, CursorShapeConfig
|
|
17
20
|
from prompt_toolkit.data_structures import Point
|
18
21
|
from prompt_toolkit.enums import EditingMode
|
19
22
|
from prompt_toolkit.filters import Condition, buffer_has_focus, to_filter
|
20
|
-
from prompt_toolkit.formatted_text import to_formatted_text
|
21
23
|
from prompt_toolkit.input.defaults import create_input
|
22
24
|
from prompt_toolkit.key_binding.bindings.basic import load_basic_bindings
|
23
25
|
from prompt_toolkit.key_binding.bindings.cpr import load_cpr_bindings
|
@@ -39,7 +41,6 @@ from prompt_toolkit.key_binding.key_bindings import (
|
|
39
41
|
)
|
40
42
|
from prompt_toolkit.layout.containers import Float, FloatContainer, Window, to_container
|
41
43
|
from prompt_toolkit.layout.layout import Layout
|
42
|
-
from prompt_toolkit.layout.menus import CompletionsMenu
|
43
44
|
from prompt_toolkit.output import ColorDepth
|
44
45
|
from prompt_toolkit.output.defaults import create_output
|
45
46
|
from prompt_toolkit.output.vt100 import Vt100_Output as PtkVt100_Output
|
@@ -61,6 +62,7 @@ from upath import UPath
|
|
61
62
|
|
62
63
|
from euporie.core.commands import add_cmd
|
63
64
|
from euporie.core.config import Config, add_setting
|
65
|
+
from euporie.core.convert.core import get_mime
|
64
66
|
from euporie.core.current import get_app
|
65
67
|
from euporie.core.filters import in_tmux, insert_mode, replace_mode, tab_has_focus
|
66
68
|
from euporie.core.io import Vt100_Output, Vt100Parser
|
@@ -71,7 +73,8 @@ from euporie.core.key_binding.registry import (
|
|
71
73
|
register_bindings,
|
72
74
|
)
|
73
75
|
from euporie.core.key_binding.vi_state import ViState
|
74
|
-
from euporie.core.log import
|
76
|
+
from euporie.core.log import setup_logs
|
77
|
+
from euporie.core.path import parse_path
|
75
78
|
from euporie.core.renderer import Renderer
|
76
79
|
from euporie.core.style import (
|
77
80
|
DEFAULT_COLORS,
|
@@ -83,19 +86,20 @@ from euporie.core.style import (
|
|
83
86
|
build_style,
|
84
87
|
)
|
85
88
|
from euporie.core.terminal import TerminalInfo
|
86
|
-
from euporie.core.utils import ChainedList
|
89
|
+
from euporie.core.utils import ChainedList
|
87
90
|
from euporie.core.widgets.decor import Shadow
|
91
|
+
from euporie.core.widgets.menu import CompletionsMenu
|
88
92
|
|
89
93
|
if TYPE_CHECKING:
|
90
94
|
from asyncio import AbstractEventLoop
|
91
|
-
from
|
95
|
+
from pathlib import Path
|
96
|
+
from types import FrameType
|
97
|
+
from typing import Any, Callable
|
92
98
|
|
93
99
|
from prompt_toolkit.clipboard import Clipboard
|
94
100
|
from prompt_toolkit.contrib.ssh import PromptToolkitSSHSession
|
95
101
|
from prompt_toolkit.filters import Filter, FilterOrBool
|
96
|
-
from prompt_toolkit.formatted_text import AnyFormattedText, StyleAndTextTuples
|
97
102
|
from prompt_toolkit.input import Input
|
98
|
-
from prompt_toolkit.layout.containers import AnyContainer
|
99
103
|
from prompt_toolkit.layout.layout import FocusableElement
|
100
104
|
from prompt_toolkit.layout.screen import WritePosition
|
101
105
|
from prompt_toolkit.output import Output
|
@@ -107,12 +111,6 @@ if TYPE_CHECKING:
|
|
107
111
|
from euporie.core.widgets.pager import Pager
|
108
112
|
from euporie.core.widgets.search_bar import SearchBar
|
109
113
|
|
110
|
-
StatusBarFields = tuple[Sequence[AnyFormattedText], Sequence[AnyFormattedText]]
|
111
|
-
ContainerStatusDict = dict[
|
112
|
-
AnyContainer,
|
113
|
-
Callable[[], StatusBarFields],
|
114
|
-
]
|
115
|
-
|
116
114
|
log = logging.getLogger(__name__)
|
117
115
|
|
118
116
|
|
@@ -182,7 +180,6 @@ class BaseApp(Application):
|
|
182
180
|
mouse_position: Point
|
183
181
|
|
184
182
|
config = Config()
|
185
|
-
status_default: StatusBarFields = ([], [])
|
186
183
|
need_mouse_support: bool = False
|
187
184
|
log_stdout_level: str = "CRITICAL"
|
188
185
|
|
@@ -255,8 +252,6 @@ class BaseApp(Application):
|
|
255
252
|
self.dialogs.values(),
|
256
253
|
self.menus.values(),
|
257
254
|
)
|
258
|
-
# Mapping of Containers to status field generating functions
|
259
|
-
self.container_statuses: ContainerStatusDict = {}
|
260
255
|
# Continue loading when the application has been launched
|
261
256
|
# and an event loop has been creeated
|
262
257
|
self.pre_run_callables = [self.pre_run]
|
@@ -345,16 +340,12 @@ class BaseApp(Application):
|
|
345
340
|
# Set the application's style, and update it when the terminal responds
|
346
341
|
self.update_style()
|
347
342
|
self.term_info.colors.event += self.update_style
|
343
|
+
# Pause rendering while we load the layout
|
348
344
|
self.pause_rendering()
|
349
345
|
# Load completions menu. This must be done after the app is initialized, because
|
350
346
|
# :py:func:`get_app` is needed to access the config
|
351
347
|
self.menus["completions"] = Float(
|
352
|
-
content=Shadow(
|
353
|
-
CompletionsMenu(
|
354
|
-
max_height=16,
|
355
|
-
scroll_offset=1,
|
356
|
-
)
|
357
|
-
),
|
348
|
+
content=Shadow(CompletionsMenu()),
|
358
349
|
xcursor=True,
|
359
350
|
ycursor=True,
|
360
351
|
)
|
@@ -389,10 +380,7 @@ class BaseApp(Application):
|
|
389
380
|
async def await_terminal_feedback() -> None:
|
390
381
|
try:
|
391
382
|
# Send queries to the terminal if supported
|
392
|
-
if self.input.__class__.__name__
|
393
|
-
"Vt100Input",
|
394
|
-
"PosixPipeInput",
|
395
|
-
):
|
383
|
+
if self.input.__class__.__name__ == "Vt100Input":
|
396
384
|
self.term_info.send_all()
|
397
385
|
# Give the terminal a chance to respond
|
398
386
|
await asyncio.sleep(0.1)
|
@@ -421,6 +409,7 @@ class BaseApp(Application):
|
|
421
409
|
|
422
410
|
"""
|
423
411
|
input_ = create_input(always_prefer_tty=True)
|
412
|
+
|
424
413
|
if stdin := getattr(input_, "stdin", None):
|
425
414
|
if not stdin.isatty():
|
426
415
|
from euporie.core.io import IgnoredInput
|
@@ -515,7 +504,7 @@ class BaseApp(Application):
|
|
515
504
|
def launch(cls) -> None:
|
516
505
|
"""Launch the app."""
|
517
506
|
# Load default logging
|
518
|
-
|
507
|
+
setup_logs()
|
519
508
|
# Load the app's configuration
|
520
509
|
cls.config.load(cls)
|
521
510
|
# Configure the logs
|
@@ -525,7 +514,40 @@ class BaseApp(Application):
|
|
525
514
|
# Run the application
|
526
515
|
with create_app_session(input=cls.load_input(), output=cls.load_output()):
|
527
516
|
# Create an instance of the app and run it
|
528
|
-
|
517
|
+
|
518
|
+
original_sigterm = signal.getsignal(signal.SIGTERM)
|
519
|
+
original_sigint = signal.getsignal(signal.SIGINT)
|
520
|
+
|
521
|
+
app = cls()
|
522
|
+
|
523
|
+
signal.signal(signal.SIGTERM, app.cleanup)
|
524
|
+
signal.signal(signal.SIGINT, app.cleanup)
|
525
|
+
|
526
|
+
result = app.run()
|
527
|
+
|
528
|
+
signal.signal(signal.SIGTERM, original_sigterm)
|
529
|
+
signal.signal(signal.SIGINT, original_sigint)
|
530
|
+
|
531
|
+
return result
|
532
|
+
|
533
|
+
def cleanup(self, signum: int, frame: FrameType | None) -> None:
|
534
|
+
"""Restore the state of the terminal on unexpected exit."""
|
535
|
+
log.critical("Unexpected exit signal, restoring terminal")
|
536
|
+
output = self.output
|
537
|
+
self.exit()
|
538
|
+
# Reset terminal state
|
539
|
+
output.quit_alternate_screen()
|
540
|
+
output.disable_mouse_support()
|
541
|
+
output.reset_cursor_key_mode()
|
542
|
+
output.enable_autowrap()
|
543
|
+
output.disable_bracketed_paste()
|
544
|
+
output.clear_title()
|
545
|
+
output.reset_cursor_shape()
|
546
|
+
output.show_cursor()
|
547
|
+
output.reset_attributes()
|
548
|
+
output.flush()
|
549
|
+
# Exit the main thread
|
550
|
+
sys.exit(1)
|
529
551
|
|
530
552
|
@classmethod
|
531
553
|
async def interact(cls, ssh_session: PromptToolkitSSHSession) -> None:
|
@@ -544,27 +566,52 @@ class BaseApp(Application):
|
|
544
566
|
floats=cast("list[Float]", self.floats),
|
545
567
|
)
|
546
568
|
|
547
|
-
def
|
569
|
+
def get_file_tabs(self, path: Path) -> list[type[Tab]]:
|
570
|
+
"""Return the tab to use for a file path."""
|
571
|
+
from euporie.core.tabs.base import Tab
|
572
|
+
|
573
|
+
path_mime = get_mime(path) or "text/plain"
|
574
|
+
log.debug("File %s has mime type: %s", path, path_mime)
|
575
|
+
|
576
|
+
tab_options = set()
|
577
|
+
for tab_cls in Tab._registry:
|
578
|
+
for mime_type in tab_cls.mime_types:
|
579
|
+
if PurePath(path_mime).match(mime_type):
|
580
|
+
tab_options.add(tab_cls)
|
581
|
+
if path.suffix in tab_cls.file_extensions:
|
582
|
+
tab_options.add(tab_cls)
|
583
|
+
|
584
|
+
return sorted(tab_options, key=lambda x: x.weight, reverse=True)
|
585
|
+
|
586
|
+
def get_file_tab(self, path: Path) -> type[Tab] | None:
|
548
587
|
"""Return the tab to use for a file path."""
|
588
|
+
if tabs := self.get_file_tabs(path):
|
589
|
+
return tabs[0]
|
549
590
|
return None
|
550
591
|
|
551
|
-
def open_file(
|
592
|
+
def open_file(
|
593
|
+
self, path: Path, read_only: bool = False, tab_class: type[Tab] | None = None
|
594
|
+
) -> None:
|
552
595
|
"""Create a tab for a file.
|
553
596
|
|
554
597
|
Args:
|
555
598
|
path: The file path of the notebook file to open
|
556
599
|
read_only: If true, the file should be opened read_only
|
600
|
+
tab_class: The tab type to use to open the file
|
557
601
|
|
558
602
|
"""
|
559
603
|
ppath = parse_path(path)
|
560
604
|
log.info(f"Opening file {path}")
|
561
605
|
for tab in self.tabs:
|
562
|
-
if ppath == getattr(tab, "path", "")
|
606
|
+
if ppath == getattr(tab, "path", "") and (
|
607
|
+
tab_class is None or isinstance(tab, tab_class)
|
608
|
+
):
|
563
609
|
log.info(f"File {path} already open, activating")
|
564
610
|
self.layout.focus(tab)
|
565
611
|
break
|
566
612
|
else:
|
567
|
-
tab_class
|
613
|
+
if tab_class is None:
|
614
|
+
tab_class = self.get_file_tab(path)
|
568
615
|
if tab_class is None:
|
569
616
|
log.error("Unable to display file %s", path)
|
570
617
|
else:
|
@@ -757,44 +804,6 @@ class BaseApp(Application):
|
|
757
804
|
"""Block default style loading."""
|
758
805
|
return DummyStyle()
|
759
806
|
|
760
|
-
def format_status(self, part: Literal["left", "right"]) -> StyleAndTextTuples:
|
761
|
-
"""Format the fields in the statusbar generated by the current tab.
|
762
|
-
|
763
|
-
Args:
|
764
|
-
part: ``'left'`` to return the fields on the left side of the statusbar,
|
765
|
-
and ``'right'`` to return the fields on the right
|
766
|
-
|
767
|
-
Returns:
|
768
|
-
A list of style and text tuples for display in the statusbar
|
769
|
-
|
770
|
-
"""
|
771
|
-
entries: StatusBarFields = ([], [])
|
772
|
-
for container, status_func in self.container_statuses.items():
|
773
|
-
if self.layout.has_focus(container):
|
774
|
-
entries = status_func()
|
775
|
-
break
|
776
|
-
else:
|
777
|
-
if not self.tabs:
|
778
|
-
entries = self.status_default
|
779
|
-
|
780
|
-
output: StyleAndTextTuples = []
|
781
|
-
# Show the tab's status fields
|
782
|
-
for field in entries[0 if part == "left" else 1]:
|
783
|
-
if field:
|
784
|
-
if isinstance(field, tuple):
|
785
|
-
ft = [field]
|
786
|
-
else:
|
787
|
-
ft = to_formatted_text(field, style="class:status.field")
|
788
|
-
output += [
|
789
|
-
("class:status.field", " "),
|
790
|
-
*ft,
|
791
|
-
("class:status.field", " "),
|
792
|
-
("class:status", " "),
|
793
|
-
]
|
794
|
-
if output:
|
795
|
-
output.pop()
|
796
|
-
return output
|
797
|
-
|
798
807
|
def draw(self, render_as_done: bool = True) -> None:
|
799
808
|
"""Draw the app without focus, leaving the cursor below the drawn output."""
|
800
809
|
# Hide ephemeral containers
|
@@ -908,45 +917,6 @@ class BaseApp(Application):
|
|
908
917
|
""",
|
909
918
|
)
|
910
919
|
|
911
|
-
add_setting(
|
912
|
-
name="log_file",
|
913
|
-
flags=["--log-file"],
|
914
|
-
nargs="?",
|
915
|
-
default="",
|
916
|
-
type_=str,
|
917
|
-
title="the log file path",
|
918
|
-
help_="File path for logs",
|
919
|
-
description="""
|
920
|
-
When set to a file path, the log output will be written to the given path.
|
921
|
-
If no value is given output will be sent to the standard output.
|
922
|
-
""",
|
923
|
-
)
|
924
|
-
|
925
|
-
add_setting(
|
926
|
-
name="log_level",
|
927
|
-
flags=["--log-level"],
|
928
|
-
type_=str,
|
929
|
-
default="",
|
930
|
-
title="the log level",
|
931
|
-
help_="Set the log level",
|
932
|
-
choices=["debug", "info", "warning", "error", "critical"],
|
933
|
-
description="""
|
934
|
-
When set, logging events at the given level are emitted.
|
935
|
-
""",
|
936
|
-
)
|
937
|
-
|
938
|
-
add_setting(
|
939
|
-
name="log_config",
|
940
|
-
flags=["--log-config"],
|
941
|
-
type_=str,
|
942
|
-
default=None,
|
943
|
-
title="additional logging configuration",
|
944
|
-
help_="Additional logging configuration",
|
945
|
-
description="""
|
946
|
-
A JSON string specifying additional logging configuration.
|
947
|
-
""",
|
948
|
-
)
|
949
|
-
|
950
920
|
add_setting(
|
951
921
|
name="edit_mode",
|
952
922
|
flags=["--edit-mode"],
|
euporie/core/border.py
CHANGED
@@ -275,22 +275,21 @@ ThickQuadrupleDashedLine = LineStyle("QuadDashed", (3, 1), parent=ThickLine)
|
|
275
275
|
ThickTripleDashedLine = LineStyle("TripleDashed", (3, 2), parent=ThickLine)
|
276
276
|
ThickDoubleDashedLine = LineStyle("DoubleDashed", (3, 3), parent=ThickLine)
|
277
277
|
|
278
|
-
|
279
|
-
|
280
|
-
# 🮂🮂🮂🮂🮂🮂🮂🮂🮂🮂🮂🮂
|
278
|
+
UpperRightQuarterLine = LineStyle("UpperRightQuarterLine", (4, 2), parent=ThickLine)
|
279
|
+
LowerLeftQuarterLine = LineStyle("LowerLeftQuarterLine", (4, 2), parent=ThickLine)
|
281
280
|
|
282
|
-
UpperRightHalfLine = LineStyle("UpperRightHalfLine", (
|
283
|
-
LowerLeftHalfLine = LineStyle("LowerLeftHalfLine", (
|
281
|
+
UpperRightHalfLine = LineStyle("UpperRightHalfLine", (5, 2), parent=ThickLine)
|
282
|
+
LowerLeftHalfLine = LineStyle("LowerLeftHalfLine", (5, 2), parent=ThickLine)
|
284
283
|
UpperRightHalfDottedLine = LineStyle(
|
285
|
-
"UpperRightHalfDottedLine", (
|
284
|
+
"UpperRightHalfDottedLine", (5, 1), parent=UpperRightHalfLine
|
286
285
|
)
|
287
286
|
LowerLeftHalfDottedLine = LineStyle(
|
288
|
-
"LowerLeftHalfDottedLine", (
|
287
|
+
"LowerLeftHalfDottedLine", (5, 2), parent=LowerLeftHalfLine
|
289
288
|
)
|
290
289
|
|
291
290
|
|
292
|
-
FullLine = LineStyle("FullLine", (
|
293
|
-
FullDottedLine = LineStyle("FullDottedLine", (
|
291
|
+
FullLine = LineStyle("FullLine", (6, 2), parent=ThickLine)
|
292
|
+
FullDottedLine = LineStyle("FullDottedLine", (6, 1), parent=ThickLine)
|
294
293
|
|
295
294
|
|
296
295
|
class GridChar(NamedTuple):
|
@@ -502,6 +501,37 @@ _GRID_CHARS = {
|
|
502
501
|
GridChar(ThinLine, UpperRightEighthLine, NoLine, UpperRightEighthLine): "▔",
|
503
502
|
GridChar(NoLine, NoLine, UpperRightEighthLine, LowerLeftEighthLine ): "▁",
|
504
503
|
GridChar(LowerLeftEighthLine, UpperRightEighthLine, NoLine, NoLine ): "▔",
|
504
|
+
|
505
|
+
# UpperRightQuarterLine
|
506
|
+
GridChar(UpperRightQuarterLine, NoLine, UpperRightQuarterLine, NoLine): "🮇",
|
507
|
+
GridChar(NoLine, UpperRightQuarterLine, NoLine, UpperRightQuarterLine): "🮂",
|
508
|
+
GridChar(NoLine, UpperRightQuarterLine, UpperRightQuarterLine, NoLine): "🮇",
|
509
|
+
GridChar(UpperRightQuarterLine, NoLine, NoLine, UpperRightQuarterLine): "🮂",
|
510
|
+
GridChar(UpperRightQuarterLine, UpperRightQuarterLine, UpperRightQuarterLine, NoLine): "🮇",
|
511
|
+
GridChar(UpperRightQuarterLine, UpperRightQuarterLine, NoLine, UpperRightQuarterLine): "🮂",
|
512
|
+
# Corners
|
513
|
+
GridChar(NoLine, LowerLeftEighthLine, UpperRightQuarterLine, NoLine): " ",
|
514
|
+
GridChar(NoLine, NoLine, LowerLeftQuarterLine, LowerLeftEighthLine): " ",
|
515
|
+
GridChar(LowerLeftQuarterLine, NoLine, NoLine, UpperRightEighthLine): " ",
|
516
|
+
GridChar(UpperRightQuarterLine, UpperRightEighthLine, NoLine, NoLine): " ",
|
517
|
+
|
518
|
+
# LowerLeftQuarterLine
|
519
|
+
GridChar(LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine, NoLine): "▎",
|
520
|
+
GridChar(NoLine, LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine): "▂",
|
521
|
+
GridChar(NoLine, LowerLeftQuarterLine, LowerLeftQuarterLine, NoLine): "▂",
|
522
|
+
GridChar(LowerLeftQuarterLine, NoLine, NoLine, LowerLeftQuarterLine): "▎",
|
523
|
+
GridChar(NoLine, LowerLeftQuarterLine, LowerLeftQuarterLine, LowerLeftQuarterLine): "▂",
|
524
|
+
GridChar(LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine, LowerLeftQuarterLine): "▎",
|
525
|
+
# Half/ThinLine combos
|
526
|
+
GridChar(LowerLeftQuarterLine, ThinLine, LowerLeftQuarterLine, NoLine): "▎",
|
527
|
+
GridChar(NoLine, LowerLeftQuarterLine, ThinLine, LowerLeftQuarterLine): "▂",
|
528
|
+
GridChar(LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine, ThinLine): "▎",
|
529
|
+
GridChar(ThinLine, LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine): "▂",
|
530
|
+
GridChar(UpperRightQuarterLine, ThinLine, UpperRightQuarterLine, NoLine): "🮇",
|
531
|
+
GridChar(NoLine, UpperRightQuarterLine, ThinLine, UpperRightQuarterLine): "🮂",
|
532
|
+
GridChar(UpperRightQuarterLine, NoLine, UpperRightQuarterLine, ThinLine): "🮇",
|
533
|
+
GridChar(ThinLine, UpperRightQuarterLine, NoLine, UpperRightQuarterLine): "🮂",
|
534
|
+
|
505
535
|
# UpperRightHalfLine
|
506
536
|
GridChar(UpperRightHalfLine, NoLine, UpperRightHalfLine, NoLine): "▐",
|
507
537
|
GridChar(NoLine, UpperRightHalfLine, NoLine, UpperRightHalfLine): "▀",
|
@@ -817,15 +847,15 @@ class GridStyle:
|
|
817
847
|
|
818
848
|
ThinGrid = ThinLine.grid
|
819
849
|
|
820
|
-
|
821
|
-
|
822
|
-
+
|
823
|
-
+ UpperRightEighthLine.left_edge
|
850
|
+
InsetGrid = (
|
851
|
+
UpperRightQuarterLine.left_edge
|
852
|
+
+ LowerLeftQuarterLine.right_edge
|
824
853
|
+ UpperRightEighthLine.bottom_edge
|
854
|
+
+ LowerLeftEighthLine.top_edge
|
825
855
|
+ ThinLine.inner
|
826
856
|
)
|
827
857
|
|
828
|
-
|
858
|
+
OutsetGrid = (
|
829
859
|
LowerLeftEighthLine.top_edge
|
830
860
|
+ UpperRightEighthLine.right_edge
|
831
861
|
+ UpperRightEighthLine.bottom_edge
|
euporie/core/comm/base.py
CHANGED
@@ -11,7 +11,7 @@ from euporie.core.current import get_app
|
|
11
11
|
from euporie.core.widgets.display import Display
|
12
12
|
|
13
13
|
if TYPE_CHECKING:
|
14
|
-
from typing import Any, Callable, Sequence
|
14
|
+
from typing import Any, Callable, Mapping, Sequence
|
15
15
|
|
16
16
|
from prompt_toolkit.layout.containers import AnyContainer
|
17
17
|
|
@@ -29,7 +29,7 @@ class CommView:
|
|
29
29
|
def __init__(
|
30
30
|
self,
|
31
31
|
container: AnyContainer,
|
32
|
-
setters:
|
32
|
+
setters: Mapping[str, Callable[..., None]] | None = None,
|
33
33
|
) -> None:
|
34
34
|
"""Create a new instance of the Comm vieew.
|
35
35
|
|
@@ -39,7 +39,7 @@ class CommView:
|
|
39
39
|
be called when the named state value changed.
|
40
40
|
"""
|
41
41
|
self.container = container
|
42
|
-
self.setters = setters or {}
|
42
|
+
self.setters: dict[str, Callable[..., None]] = dict(setters or {})
|
43
43
|
self.kernel: Kernel | None = None
|
44
44
|
|
45
45
|
def update(self, changes: dict[str, Any]) -> None:
|
@@ -117,4 +117,5 @@ class UnimplementedComm(Comm):
|
|
117
117
|
|
118
118
|
def process_data(self, data: dict, buffers: Sequence[bytes]) -> None:
|
119
119
|
"""Doe nothing when data is received."""
|
120
|
-
|
120
|
+
self.data = data
|
121
|
+
self.buffers = buffers
|