euporie 2.8.5__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/core/__init__.py +1 -1
- euporie/core/__main__.py +2 -2
- euporie/core/_settings.py +7 -2
- euporie/core/app/_commands.py +26 -1
- euporie/core/app/_settings.py +34 -4
- euporie/core/app/app.py +18 -11
- euporie/core/bars/command.py +44 -21
- euporie/core/commands.py +0 -16
- euporie/core/filters.py +32 -9
- euporie/core/format.py +2 -3
- euporie/core/graphics.py +191 -31
- euporie/core/layout/scroll.py +35 -33
- euporie/core/log.py +1 -4
- euporie/core/path.py +61 -13
- euporie/core/tabs/__init__.py +2 -4
- euporie/core/tabs/base.py +73 -7
- euporie/core/tabs/kernel.py +2 -3
- euporie/core/tabs/notebook.py +14 -54
- euporie/core/utils.py +1 -18
- euporie/core/widgets/cell.py +1 -1
- euporie/core/widgets/dialog.py +32 -3
- euporie/core/widgets/display.py +2 -2
- euporie/core/widgets/menu.py +1 -1
- euporie/notebook/tabs/display.py +2 -2
- euporie/notebook/tabs/edit.py +8 -43
- euporie/notebook/tabs/json.py +2 -2
- euporie/web/__init__.py +1 -0
- euporie/web/tabs/__init__.py +14 -0
- euporie/web/tabs/web.py +10 -4
- euporie/web/widgets/__init__.py +1 -0
- euporie/web/widgets/webview.py +2 -4
- {euporie-2.8.5.dist-info → euporie-2.8.6.dist-info}/METADATA +4 -2
- {euporie-2.8.5.dist-info → euporie-2.8.6.dist-info}/RECORD +38 -35
- {euporie-2.8.5.data → euporie-2.8.6.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.8.5.data → euporie-2.8.6.data}/data/share/applications/euporie-notebook.desktop +0 -0
- {euporie-2.8.5.dist-info → euporie-2.8.6.dist-info}/WHEEL +0 -0
- {euporie-2.8.5.dist-info → euporie-2.8.6.dist-info}/entry_points.txt +0 -0
- {euporie-2.8.5.dist-info → euporie-2.8.6.dist-info}/licenses/LICENSE +0 -0
euporie/core/__init__.py
CHANGED
euporie/core/__main__.py
CHANGED
@@ -7,13 +7,13 @@ from importlib.metadata import entry_points
|
|
7
7
|
from typing import TYPE_CHECKING
|
8
8
|
|
9
9
|
if TYPE_CHECKING:
|
10
|
-
from importlib.metadata import EntryPoint, EntryPoints
|
10
|
+
from importlib.metadata import EntryPoint, EntryPoints
|
11
11
|
|
12
12
|
|
13
13
|
@cache
|
14
14
|
def available_apps() -> dict[str, EntryPoint]:
|
15
15
|
"""Return a list of loadable euporie apps."""
|
16
|
-
eps: dict |
|
16
|
+
eps: dict | EntryPoints
|
17
17
|
try:
|
18
18
|
eps = entry_points(group="euporie.apps")
|
19
19
|
except TypeError:
|
euporie/core/_settings.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
"""Defines core settings."""
|
2
2
|
|
3
|
+
import json
|
4
|
+
|
3
5
|
from euporie.core import __version__
|
4
6
|
from euporie.core.config import add_setting
|
5
7
|
|
@@ -73,8 +75,11 @@ add_setting(
|
|
73
75
|
name="log_config",
|
74
76
|
group="euporie.core.log",
|
75
77
|
flags=["--log-config"],
|
76
|
-
type_=
|
77
|
-
default=
|
78
|
+
type_=json.loads,
|
79
|
+
default={},
|
80
|
+
schema={
|
81
|
+
"type": "object",
|
82
|
+
},
|
78
83
|
title="additional logging configuration",
|
79
84
|
help_="Additional logging configuration",
|
80
85
|
description="""
|
euporie/core/app/_commands.py
CHANGED
@@ -8,7 +8,7 @@ from prompt_toolkit.filters import buffer_has_focus
|
|
8
8
|
|
9
9
|
from euporie.core.app.current import get_app
|
10
10
|
from euporie.core.commands import add_cmd
|
11
|
-
from euporie.core.filters import tab_has_focus
|
11
|
+
from euporie.core.filters import tab_has_focus, tab_type_has_focus
|
12
12
|
|
13
13
|
if TYPE_CHECKING:
|
14
14
|
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
|
@@ -20,6 +20,14 @@ def _quit() -> None:
|
|
20
20
|
get_app().exit()
|
21
21
|
|
22
22
|
|
23
|
+
@add_cmd(aliases=["q!"])
|
24
|
+
def _force_quit() -> None:
|
25
|
+
"""Quit euporie without saving any changes."""
|
26
|
+
from prompt_toolkit.application.application import Application
|
27
|
+
|
28
|
+
Application.exit(get_app())
|
29
|
+
|
30
|
+
|
23
31
|
@add_cmd(aliases=["wq", "x"])
|
24
32
|
def _save_and_quit(event: KeyPressEvent) -> None:
|
25
33
|
"""Save the current tab then quits euporie."""
|
@@ -68,3 +76,20 @@ def _focus_previous() -> None:
|
|
68
76
|
def _clear_screen() -> None:
|
69
77
|
"""Clear the screen."""
|
70
78
|
get_app().renderer.clear()
|
79
|
+
|
80
|
+
|
81
|
+
@add_cmd(hidden=True, aliases=[""])
|
82
|
+
def _go_to(event: KeyPressEvent) -> None:
|
83
|
+
"""Go to a line or cell by number."""
|
84
|
+
try:
|
85
|
+
idx = int(event._arg or "") - 1
|
86
|
+
except (ValueError, TypeError):
|
87
|
+
return
|
88
|
+
if buffer_has_focus():
|
89
|
+
buffer = get_app().current_buffer
|
90
|
+
buffer.cursor_position = len("".join(buffer.text.splitlines(True)[:idx]))
|
91
|
+
elif tab_type_has_focus("euporie.notebook.tabs.notebook:Notebook")():
|
92
|
+
from euporie.notebook.tabs.notebook import Notebook
|
93
|
+
|
94
|
+
if isinstance(nb := get_app().tab, Notebook):
|
95
|
+
nb.select(idx)
|
euporie/core/app/_settings.py
CHANGED
@@ -140,6 +140,8 @@ add_setting(
|
|
140
140
|
|
141
141
|
e.g.
|
142
142
|
|
143
|
+
.. code-block:: json
|
144
|
+
|
143
145
|
[
|
144
146
|
{"command": ["ruff", "format", "-"], "languages": ["python"]},
|
145
147
|
{"command": ["black", "-"], "languages": ["python"]},
|
@@ -281,6 +283,30 @@ add_setting(
|
|
281
283
|
""",
|
282
284
|
)
|
283
285
|
|
286
|
+
add_setting(
|
287
|
+
name="custom_styles",
|
288
|
+
group="euporie.core.style",
|
289
|
+
flags=["--custom-styles"],
|
290
|
+
type_=json.loads,
|
291
|
+
default={},
|
292
|
+
schema={
|
293
|
+
"type": "object",
|
294
|
+
},
|
295
|
+
help_="Additional style settings",
|
296
|
+
description="""
|
297
|
+
A JSON object mapping style names to prompt-toolkit style values.
|
298
|
+
|
299
|
+
The style keys used in euporie can be found in :py:func:`euporie.core.style.build_style`.
|
300
|
+
|
301
|
+
e.g.:
|
302
|
+
|
303
|
+
.. code-block:: json
|
304
|
+
|
305
|
+
{ "cell input prompt":"fg:purple", "cell output prompt": "fg:green" }
|
306
|
+
|
307
|
+
""",
|
308
|
+
)
|
309
|
+
|
284
310
|
add_setting(
|
285
311
|
name="key_bindings",
|
286
312
|
group="euporie.core.app.app",
|
@@ -288,19 +314,19 @@ add_setting(
|
|
288
314
|
type_=json.loads,
|
289
315
|
help_="Additional key binding definitions",
|
290
316
|
default={},
|
291
|
-
description="""
|
292
|
-
A mapping of component names to mappings of command name to key-binding lists.
|
293
|
-
""",
|
294
317
|
schema={
|
295
318
|
"type": "object",
|
296
319
|
},
|
320
|
+
description="""
|
321
|
+
A mapping of component names to mappings of command name to key-binding lists.
|
322
|
+
""",
|
297
323
|
)
|
298
324
|
|
299
325
|
add_setting(
|
300
326
|
name="graphics",
|
301
327
|
group="euporie.core.app.app",
|
302
328
|
flags=["--graphics"],
|
303
|
-
choices=["none", "sixel", "kitty", "iterm"],
|
329
|
+
choices=["none", "sixel", "kitty", "kitty-unicode", "iterm"],
|
304
330
|
type_=str,
|
305
331
|
default=None,
|
306
332
|
help_="The preferred graphics protocol",
|
@@ -375,6 +401,8 @@ add_setting(
|
|
375
401
|
description="""
|
376
402
|
Additional language servers can be defined here, e.g.:
|
377
403
|
|
404
|
+
.. code-block:: json
|
405
|
+
|
378
406
|
{
|
379
407
|
"ruff": {"command": ["ruff-lsp"], "languages": ["python"]},
|
380
408
|
"pylsp": {"command": ["pylsp"], "languages": ["python"]},
|
@@ -392,6 +420,8 @@ add_setting(
|
|
392
420
|
empty dictionary. For example, the following would disable the awk language
|
393
421
|
server:
|
394
422
|
|
423
|
+
.. code-block:: json
|
424
|
+
|
395
425
|
{
|
396
426
|
"awk-language-server": {},
|
397
427
|
}
|
euporie/core/app/app.py
CHANGED
@@ -791,6 +791,13 @@ class BaseApp(ConfigurableApp, Application, ABC):
|
|
791
791
|
syntax_theme = "tango" if self.color_palette.bg.is_light else "euporie"
|
792
792
|
return syntax_theme
|
793
793
|
|
794
|
+
base_styles = (
|
795
|
+
Style(MIME_STYLE),
|
796
|
+
Style(HTML_STYLE),
|
797
|
+
Style(LOG_STYLE),
|
798
|
+
Style(IPYWIDGET_STYLE),
|
799
|
+
)
|
800
|
+
|
794
801
|
def create_merged_style(self) -> BaseStyle:
|
795
802
|
"""Generate a new merged style for the application.
|
796
803
|
|
@@ -801,6 +808,11 @@ class BaseApp(ConfigurableApp, Application, ABC):
|
|
801
808
|
Return a combined style to use for the application
|
802
809
|
|
803
810
|
"""
|
811
|
+
styles: list[BaseStyle] = [
|
812
|
+
style_from_pygments_cls(get_style_by_name(self.syntax_theme)),
|
813
|
+
*self.base_styles,
|
814
|
+
]
|
815
|
+
|
804
816
|
# Get foreground and background colors based on the configured colour scheme
|
805
817
|
theme_colors: dict[str, dict[str, str]] = {
|
806
818
|
"default": {},
|
@@ -844,7 +856,7 @@ class BaseApp(ConfigurableApp, Application, ABC):
|
|
844
856
|
)
|
845
857
|
|
846
858
|
# Build app style
|
847
|
-
|
859
|
+
styles.append(build_style(cp))
|
848
860
|
|
849
861
|
# Apply style transformations based on the configured color scheme
|
850
862
|
self.style_transformation = merge_style_transformations(
|
@@ -862,16 +874,11 @@ class BaseApp(ConfigurableApp, Application, ABC):
|
|
862
874
|
]
|
863
875
|
)
|
864
876
|
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
Style(LOG_STYLE),
|
871
|
-
Style(IPYWIDGET_STYLE),
|
872
|
-
app_style,
|
873
|
-
]
|
874
|
-
)
|
877
|
+
# Add user style customizations
|
878
|
+
if custom_style_dict := self.config.custom_styles:
|
879
|
+
styles.append(Style.from_dict(custom_style_dict))
|
880
|
+
|
881
|
+
return merge_styles(styles)
|
875
882
|
|
876
883
|
def update_style(self, query: Setting | None = None) -> None:
|
877
884
|
"""Update the application's style when the syntax theme is changed."""
|
euporie/core/bars/command.py
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
import logging
|
6
|
+
import re
|
7
|
+
from functools import lru_cache
|
6
8
|
from typing import TYPE_CHECKING
|
7
9
|
|
8
10
|
from prompt_toolkit.buffer import Buffer
|
9
11
|
from prompt_toolkit.completion.base import Completer, Completion
|
10
|
-
from prompt_toolkit.filters import
|
11
|
-
|
12
|
-
has_focus,
|
13
|
-
)
|
12
|
+
from prompt_toolkit.filters import buffer_has_focus, has_focus, vi_navigation_mode
|
13
|
+
from prompt_toolkit.key_binding.vi_state import InputMode
|
14
14
|
from prompt_toolkit.layout.containers import ConditionalContainer, Container, Window
|
15
15
|
from prompt_toolkit.layout.controls import (
|
16
16
|
BufferControl,
|
@@ -39,6 +39,23 @@ if TYPE_CHECKING:
|
|
39
39
|
log = logging.getLogger(__name__)
|
40
40
|
|
41
41
|
|
42
|
+
@lru_cache
|
43
|
+
def _parse_cmd(text: str) -> tuple[Command | None, str]:
|
44
|
+
"""Parse a command line to command and arguments.
|
45
|
+
|
46
|
+
Command names cannot start with digits, so lines staring with digits have an empty
|
47
|
+
command string (this is used for the go-to-cell/go-to-line shortcuts).
|
48
|
+
"""
|
49
|
+
if match := re.fullmatch(r"^(?P<cmd>[^\d][^\s]*|)\s*(?P<args>.*)$", text):
|
50
|
+
cmd, args = match.groups()
|
51
|
+
else:
|
52
|
+
cmd, args = "", ""
|
53
|
+
try:
|
54
|
+
return get_cmd(cmd), args
|
55
|
+
except KeyError:
|
56
|
+
return None, args
|
57
|
+
|
58
|
+
|
42
59
|
class CommandCompleter(Completer):
|
43
60
|
"""Completer of commands."""
|
44
61
|
|
@@ -49,7 +66,11 @@ class CommandCompleter(Completer):
|
|
49
66
|
prefix = document.text
|
50
67
|
found_so_far: set[Command] = set()
|
51
68
|
for alias, command in commands.items():
|
52
|
-
if
|
69
|
+
if (
|
70
|
+
alias.startswith(prefix)
|
71
|
+
and command not in found_so_far
|
72
|
+
and not command.hidden()
|
73
|
+
):
|
53
74
|
yield Completion(
|
54
75
|
command.name,
|
55
76
|
start_position=-len(prefix),
|
@@ -105,21 +126,18 @@ class CommandBar:
|
|
105
126
|
|
106
127
|
def _validate(self, text: str) -> bool:
|
107
128
|
"""Verify that a valid command has been entered."""
|
108
|
-
cmd,
|
109
|
-
|
110
|
-
get_cmd(cmd)
|
111
|
-
except KeyError:
|
112
|
-
return False
|
113
|
-
else:
|
114
|
-
return True
|
129
|
+
cmd, _args = _parse_cmd(text)
|
130
|
+
return bool(cmd)
|
115
131
|
|
116
132
|
def _accept(self, buffer: Buffer) -> bool:
|
117
133
|
"""Return value determines if the text is kept."""
|
118
|
-
|
119
|
-
|
134
|
+
app = get_app()
|
135
|
+
app.vi_state.input_mode = InputMode.NAVIGATION
|
136
|
+
app.layout.focus_last()
|
120
137
|
text = buffer.text.strip()
|
121
|
-
cmd,
|
122
|
-
|
138
|
+
cmd, args = _parse_cmd(text)
|
139
|
+
if cmd:
|
140
|
+
cmd.run(args)
|
123
141
|
return False
|
124
142
|
|
125
143
|
def __pt_container__(self) -> Container:
|
@@ -135,25 +153,28 @@ class CommandBar:
|
|
135
153
|
"activate-command-bar-shell-alt": "A-!",
|
136
154
|
},
|
137
155
|
"euporie.core.bars.command:CommandBar": {
|
138
|
-
"deactivate-command-bar": "escape",
|
156
|
+
"deactivate-command-bar": ["escape", "c-c"],
|
139
157
|
},
|
140
158
|
}
|
141
159
|
)
|
142
160
|
|
143
161
|
@staticmethod
|
144
162
|
@add_cmd(name="activate-command-bar-alt", hidden=True)
|
145
|
-
@add_cmd(filter=~buffer_has_focus)
|
163
|
+
@add_cmd(filter=~buffer_has_focus | vi_navigation_mode)
|
146
164
|
def _activate_command_bar(event: KeyPressEvent) -> None:
|
147
165
|
"""Enter command mode."""
|
148
166
|
event.app.layout.focus(COMMAND_BAR_BUFFER)
|
167
|
+
event.app.vi_state.input_mode = InputMode.INSERT
|
149
168
|
|
150
169
|
@staticmethod
|
151
170
|
@add_cmd(filter=~buffer_has_focus)
|
152
171
|
@add_cmd(name="activate-command-bar-shell-alt", hidden=True)
|
153
172
|
def _activate_command_bar_shell(event: KeyPressEvent) -> None:
|
154
173
|
"""Enter command mode."""
|
155
|
-
|
174
|
+
app = event.app
|
175
|
+
layout = app.layout
|
156
176
|
layout.focus(COMMAND_BAR_BUFFER)
|
177
|
+
app.vi_state.input_mode = InputMode.INSERT
|
157
178
|
if isinstance(control := layout.current_control, BufferControl):
|
158
179
|
buffer = control.buffer
|
159
180
|
buffer.text = "shell "
|
@@ -163,12 +184,14 @@ class CommandBar:
|
|
163
184
|
@add_cmd(hidden=True)
|
164
185
|
def _deactivate_command_bar(event: KeyPressEvent) -> None:
|
165
186
|
"""Exit command mode."""
|
166
|
-
|
187
|
+
app = event.app
|
188
|
+
layout = app.layout
|
167
189
|
layout.focus(COMMAND_BAR_BUFFER)
|
168
190
|
if isinstance(control := layout.current_control, BufferControl):
|
191
|
+
app.vi_state.input_mode = InputMode.NAVIGATION
|
169
192
|
buffer = control.buffer
|
170
193
|
buffer.reset()
|
171
|
-
|
194
|
+
app.layout.focus_previous()
|
172
195
|
|
173
196
|
@staticmethod
|
174
197
|
@add_cmd(aliases=["shell"])
|
euporie/core/commands.py
CHANGED
@@ -191,22 +191,6 @@ class Command:
|
|
191
191
|
return format_keys([self.keys[0]])[0]
|
192
192
|
return ""
|
193
193
|
|
194
|
-
@property
|
195
|
-
def menu_handler(self) -> Callable[[], None]:
|
196
|
-
"""Return a menu handler for the command."""
|
197
|
-
handler = self.handler
|
198
|
-
if isawaitable(handler):
|
199
|
-
|
200
|
-
def _menu_handler() -> None:
|
201
|
-
task = cast("CommandHandlerNoArgs", handler)()
|
202
|
-
task = cast("Coroutine[Any, Any, None]", task)
|
203
|
-
if task is not None:
|
204
|
-
get_app().create_background_task(task)
|
205
|
-
|
206
|
-
return _menu_handler
|
207
|
-
else:
|
208
|
-
return cast("Callable[[], None]", handler)
|
209
|
-
|
210
194
|
@property
|
211
195
|
def menu(self) -> MenuItem:
|
212
196
|
"""Return a menu item for the command."""
|
euporie/core/filters.py
CHANGED
@@ -133,6 +133,38 @@ def tab_has_focus() -> bool:
|
|
133
133
|
return get_app().tab is not None
|
134
134
|
|
135
135
|
|
136
|
+
@Condition
|
137
|
+
def kernel_tab_has_focus() -> bool:
|
138
|
+
"""Determine if there is a focused kernel tab."""
|
139
|
+
from euporie.core.app.current import get_app
|
140
|
+
from euporie.core.tabs.kernel import KernelTab
|
141
|
+
|
142
|
+
return isinstance(get_app().tab, KernelTab)
|
143
|
+
|
144
|
+
|
145
|
+
@cache
|
146
|
+
def tab_type_has_focus(tab_class_path: str) -> Condition:
|
147
|
+
"""Determine if the focused tab is of a particular type."""
|
148
|
+
from pkgutil import resolve_name
|
149
|
+
|
150
|
+
from euporie.core.app.current import get_app
|
151
|
+
|
152
|
+
tab_class = cache(resolve_name)
|
153
|
+
|
154
|
+
return Condition(lambda: isinstance(get_app().tab, tab_class(tab_class_path)))
|
155
|
+
|
156
|
+
|
157
|
+
@Condition
|
158
|
+
def tab_can_save() -> bool:
|
159
|
+
"""Determine if the current tab can save it's contents."""
|
160
|
+
from euporie.core.app.current import get_app
|
161
|
+
from euporie.core.tabs.base import Tab
|
162
|
+
|
163
|
+
return (
|
164
|
+
tab := get_app().tab
|
165
|
+
) is not None and tab.__class__.write_file != Tab.write_file
|
166
|
+
|
167
|
+
|
136
168
|
@Condition
|
137
169
|
def pager_has_focus() -> bool:
|
138
170
|
"""Determine if there is a currently focused notebook."""
|
@@ -321,15 +353,6 @@ def multiple_cells_selected() -> bool:
|
|
321
353
|
return False
|
322
354
|
|
323
355
|
|
324
|
-
@Condition
|
325
|
-
def kernel_tab_has_focus() -> bool:
|
326
|
-
"""Determine if there is a focused kernel tab."""
|
327
|
-
from euporie.core.app.current import get_app
|
328
|
-
from euporie.core.tabs.kernel import KernelTab
|
329
|
-
|
330
|
-
return isinstance(get_app().tab, KernelTab)
|
331
|
-
|
332
|
-
|
333
356
|
def scrollable(window: Window) -> Filter:
|
334
357
|
"""Return a filter which indicates if a window is scrollable."""
|
335
358
|
return Condition(
|
euporie/core/format.py
CHANGED
@@ -110,11 +110,10 @@ class LspFormatter(Formatter):
|
|
110
110
|
range_ = change.get("range", {})
|
111
111
|
start = range_.get("start", {})
|
112
112
|
start_line = start.get("line", 0)
|
113
|
-
start_char = start.get("
|
113
|
+
start_char = start.get("character", 0)
|
114
114
|
end = range_.get("end", {})
|
115
115
|
end_line = end.get("line", 0)
|
116
|
-
end_char = end.get("
|
117
|
-
|
116
|
+
end_char = end.get("character", 0)
|
118
117
|
segment = range_to_slice(
|
119
118
|
start_line, start_char, end_line, end_char, text
|
120
119
|
)
|