euporie 2.8.0__tar.gz → 2.8.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {euporie-2.8.0 → euporie-2.8.2}/PKG-INFO +3 -7
- {euporie-2.8.0 → euporie-2.8.2}/euporie/console/tabs/console.py +2 -1
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/__init__.py +1 -9
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/__main__.py +9 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/datum.py +14 -9
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/ansi.py +37 -7
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/utils.py +9 -19
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/filters.py +5 -52
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/format.py +13 -2
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/ft/html.py +22 -11
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/graphics.py +4 -5
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/bindings/micro.py +4 -1
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/keys.py +1 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/launch.py +7 -2
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/layout/scroll.py +7 -6
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/cell.py +8 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/cell_outputs.py +1 -1
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/display.py +18 -12
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/inputs.py +3 -2
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/menu.py +4 -4
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/pager.py +3 -1
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/search.py +135 -42
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/tabs/edit.py +4 -1
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/tabs/notebook.py +6 -12
- {euporie-2.8.0 → euporie-2.8.2}/euporie/web/widgets/webview.py +2 -1
- {euporie-2.8.0 → euporie-2.8.2}/pyproject.toml +6 -9
- {euporie-2.8.0 → euporie-2.8.2}/.gitignore +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/LICENSE +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/README.rst +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/console/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/console/__main__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/console/app.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/console/py.typed +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/console/tabs/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/app.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/border.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/clipboard.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/comm/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/comm/base.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/comm/ipywidgets.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/comm/registry.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/commands.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/completion.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/config.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/base64.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/common.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/ft.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/html.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/jpeg.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/markdown.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/pdf.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/pil.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/png.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/rich.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/sixel.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/formats/svg.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/mime.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/convert/registry.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/current.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/data_structures.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/diagnostics.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/ft/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/ft/ansi.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/ft/table.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/ft/utils.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/history.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/inspection.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/io.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/kernel.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/bindings/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/bindings/basic.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/bindings/completion.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/bindings/mouse.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/bindings/page_navigation.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/key_processor.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/micro_state.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/registry.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/utils.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/key_binding/vi_state.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/layout/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/layout/cache.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/layout/containers.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/layout/controls.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/layout/decor.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/layout/mouse.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/layout/print.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/layout/screen.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/lexers.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/log.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/lsp.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/margins.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/path.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/processors.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/py.typed +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/pygments.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/reference.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/renderer.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/style.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/suggest.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/tabs/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/tabs/base.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/tabs/notebook.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/terminal.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/utils.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/validation.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/decor.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/dialog.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/file_browser.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/formatted_text_area.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/forms.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/layout.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/palette.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/status.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/core/widgets/tree.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/data/desktop/euporie-console.desktop +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/data/desktop/euporie-notebook.desktop +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/hub/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/hub/__main__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/hub/app.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/hub/py.typed +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/__main__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/app.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/current.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/enums.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/filters.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/py.typed +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/tabs/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/tabs/display.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/tabs/json.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/tabs/log.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/widgets/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/notebook/widgets/side_bar.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/preview/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/preview/__main__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/preview/app.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/preview/py.typed +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/preview/tabs/__init__.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/preview/tabs/notebook.py +0 -0
- {euporie-2.8.0 → euporie-2.8.2}/euporie/web/tabs/web.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.3
|
2
2
|
Name: euporie
|
3
|
-
Version: 2.8.
|
3
|
+
Version: 2.8.2
|
4
4
|
Summary: Euporie is a suite of terminal applications for interacting with Jupyter kernels
|
5
5
|
Project-URL: Documentation, https://euporie.readthedocs.io/en/latest
|
6
6
|
Project-URL: Issues, https://github.com/joouha/euporie/issues
|
@@ -32,7 +32,7 @@ Requires-Dist: linkify-it-py~=1.0
|
|
32
32
|
Requires-Dist: markdown-it-py~=2.1.0
|
33
33
|
Requires-Dist: mdit-py-plugins~=0.3.0
|
34
34
|
Requires-Dist: nbformat~=5.0
|
35
|
-
Requires-Dist: pillow
|
35
|
+
Requires-Dist: pillow>=9.0
|
36
36
|
Requires-Dist: platformdirs~=3.5
|
37
37
|
Requires-Dist: prompt-toolkit~=3.0.36
|
38
38
|
Requires-Dist: pygments~=2.11
|
@@ -41,10 +41,6 @@ Requires-Dist: sixelcrop~=0.1.6
|
|
41
41
|
Requires-Dist: timg~=1.1.6
|
42
42
|
Requires-Dist: typing-extensions~=4.5
|
43
43
|
Requires-Dist: universal-pathlib~=0.2.1
|
44
|
-
Provides-Extra: format
|
45
|
-
Requires-Dist: black>=19.3.b0; extra == 'format'
|
46
|
-
Requires-Dist: isort~=5.10.1; extra == 'format'
|
47
|
-
Requires-Dist: ruff~=0.1.0; extra == 'format'
|
48
44
|
Provides-Extra: hub
|
49
45
|
Requires-Dist: asyncssh~=2.10.1; extra == 'hub'
|
50
46
|
Description-Content-Type: text/x-rst
|
@@ -1,18 +1,10 @@
|
|
1
1
|
"""This package defines the euporie application and its components."""
|
2
2
|
|
3
3
|
__app_name__ = "euporie"
|
4
|
-
__version__ = "2.8.
|
4
|
+
__version__ = "2.8.2"
|
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"© 2024, {__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
|
16
|
-
|
17
|
-
# Monkey-patch prompt_toolkit
|
18
|
-
from euporie.core.layout import containers # noqa: F401
|
@@ -5,6 +5,15 @@ def main(name: "str" = "launch") -> "None":
|
|
5
5
|
"""Load and launches the application."""
|
6
6
|
from importlib.metadata import entry_points
|
7
7
|
|
8
|
+
# Register extensions to external packages
|
9
|
+
from euporie.core import (
|
10
|
+
path, # noqa F401
|
11
|
+
pygments, # noqa F401
|
12
|
+
)
|
13
|
+
|
14
|
+
# Monkey-patch prompt_toolkit
|
15
|
+
from euporie.core.layout import containers # noqa: F401
|
16
|
+
|
8
17
|
eps = entry_points() # group="euporie.apps")
|
9
18
|
if isinstance(eps, dict):
|
10
19
|
points = eps.get("euporie.apps")
|
@@ -230,9 +230,7 @@ class Datum(Generic[T], metaclass=_MetaDatum):
|
|
230
230
|
return self._conversions[key]
|
231
231
|
|
232
232
|
routes = _CONVERTOR_ROUTE_CACHE[(self.format, to)]
|
233
|
-
# log.debug(
|
234
|
-
# "Converting from '%s' to '%s' using route: %s", self, to, routes
|
235
|
-
# )
|
233
|
+
# log.debug("Converting from '%s' to '%s' using route: %s", self, to, routes)
|
236
234
|
output: T | None = None
|
237
235
|
if routes:
|
238
236
|
datum = self
|
@@ -244,18 +242,25 @@ class Datum(Generic[T], metaclass=_MetaDatum):
|
|
244
242
|
output = self._conversions[key]
|
245
243
|
else:
|
246
244
|
# Find converter with lowest weight
|
247
|
-
|
245
|
+
for converter in sorted(
|
248
246
|
[
|
249
247
|
conv
|
250
248
|
for conv in converters[stage_b][stage_a]
|
251
249
|
if _FILTER_CACHE.get((conv,), conv.filter_)
|
252
250
|
],
|
253
251
|
key=lambda x: x.weight,
|
254
|
-
)
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
252
|
+
):
|
253
|
+
try:
|
254
|
+
output = await converter.func(
|
255
|
+
datum, cols, rows, fg, bg, extend
|
256
|
+
)
|
257
|
+
self._conversions[key] = output
|
258
|
+
except Exception:
|
259
|
+
log.debug("Conversion step %s failed", converter)
|
260
|
+
continue
|
261
|
+
else:
|
262
|
+
break
|
263
|
+
else:
|
259
264
|
log.exception("An error occurred during format conversion")
|
260
265
|
output = None
|
261
266
|
if output is None:
|
@@ -169,9 +169,10 @@ async def html_to_ansi_py_htmlparser(
|
|
169
169
|
@register(
|
170
170
|
from_="latex",
|
171
171
|
to="ansi",
|
172
|
-
filter_=
|
172
|
+
filter_=command_exists("utftex"),
|
173
|
+
weight=0,
|
173
174
|
)
|
174
|
-
async def
|
175
|
+
async def latex_to_ansi_utftex(
|
175
176
|
datum: Datum,
|
176
177
|
cols: int | None = None,
|
177
178
|
rows: int | None = None,
|
@@ -179,16 +180,15 @@ async def latex_to_ansi_py_flatlatex(
|
|
179
180
|
bg: str | None = None,
|
180
181
|
extend: bool = True,
|
181
182
|
) -> str:
|
182
|
-
"""
|
183
|
-
|
184
|
-
|
185
|
-
return flatlatex.converter().convert(datum.data.strip().strip("$").strip())
|
183
|
+
"""Render LaTeX maths as unicode."""
|
184
|
+
return (await call_subproc(datum.data, ["utftex"])).decode()
|
186
185
|
|
187
186
|
|
188
187
|
@register(
|
189
188
|
from_="latex",
|
190
189
|
to="ansi",
|
191
190
|
filter_=have_modules("pylatexenc"),
|
191
|
+
weight=0,
|
192
192
|
)
|
193
193
|
async def latex_to_ansi_py_pylatexenc(
|
194
194
|
datum: Datum,
|
@@ -204,6 +204,36 @@ async def latex_to_ansi_py_pylatexenc(
|
|
204
204
|
return LatexNodes2Text().latex_to_text(datum.data.strip().strip("$").strip())
|
205
205
|
|
206
206
|
|
207
|
+
@register(
|
208
|
+
from_="latex",
|
209
|
+
to="ansi",
|
210
|
+
filter_=have_modules("flatlatex.latexfuntypes"),
|
211
|
+
weight=0,
|
212
|
+
)
|
213
|
+
async def latex_to_ansi_py_flatlatex(
|
214
|
+
datum: Datum,
|
215
|
+
cols: int | None = None,
|
216
|
+
rows: int | None = None,
|
217
|
+
fg: str | None = None,
|
218
|
+
bg: str | None = None,
|
219
|
+
extend: bool = True,
|
220
|
+
) -> str:
|
221
|
+
"""Convert LaTeX to ANSI using :py:mod:`flatlatex`."""
|
222
|
+
import flatlatex
|
223
|
+
from flatlatex.latexfuntypes import latexfun
|
224
|
+
|
225
|
+
converter = flatlatex.converter()
|
226
|
+
for style in (
|
227
|
+
r"\textstyle",
|
228
|
+
r"\displaystyle",
|
229
|
+
r"\scriptstyle",
|
230
|
+
r"\scriptscriptstyle",
|
231
|
+
):
|
232
|
+
converter._converter__cmds[style] = latexfun(lambda x: "", 0)
|
233
|
+
|
234
|
+
return converter.convert(datum.data.strip().strip("$").strip())
|
235
|
+
|
236
|
+
|
207
237
|
@register(
|
208
238
|
from_="latex",
|
209
239
|
to="ansi",
|
@@ -261,7 +291,7 @@ async def pil_to_ansi_py_timg(
|
|
261
291
|
assert rows is not None
|
262
292
|
assert cols is not None
|
263
293
|
|
264
|
-
# `timg` assumes a 2x1 terminal cell aspect ratio, so we correct for while
|
294
|
+
# `timg` assumes a 2x1 terminal cell aspect ratio, so we correct for this while
|
265
295
|
# resizing the image
|
266
296
|
data = data.resize((cols, ceil(rows * 2 * (px / py) / 0.5)))
|
267
297
|
|
@@ -61,28 +61,18 @@ async def call_subproc(
|
|
61
61
|
stderr=asyncio.subprocess.DEVNULL,
|
62
62
|
)
|
63
63
|
output_bytes, _ = await proc.communicate(stdinput)
|
64
|
-
except FileNotFoundError as
|
64
|
+
except FileNotFoundError as error:
|
65
65
|
log.error("Could not run external command `%s`", cmd)
|
66
|
-
error
|
67
|
-
except subprocess.CalledProcessError as
|
66
|
+
raise error
|
67
|
+
except subprocess.CalledProcessError as error:
|
68
68
|
log.error("There was an error while running external command `%s`", cmd)
|
69
|
-
error
|
69
|
+
raise error
|
70
|
+
else:
|
71
|
+
if (proc.returncode or 0) > 0 or error:
|
72
|
+
# Raise an exception if the process failed so we can continue on the the
|
73
|
+
# next conversion method
|
74
|
+
raise subprocess.CalledProcessError(proc.returncode or 0, cmd)
|
70
75
|
finally:
|
71
|
-
if error is not None:
|
72
|
-
# Generate an output stating there was an error
|
73
|
-
output_bytes = (
|
74
|
-
b"\x1b[33m" # Set fg to yellow
|
75
|
-
b"\xee\x82\xb6" # Draw left pill side
|
76
|
-
b"\x1b[43m\x1b[30m" # Set fg to black, bg to yellow
|
77
|
-
b"\xe2\x9a\xa0" # Draw warning symbol
|
78
|
-
b" Rendering Error"
|
79
|
-
b"\x1b[33m\x1b[49m" # Set fg to yellow, reset bg
|
80
|
-
b"\xee\x82\xb4" # Draw right pill side
|
81
|
-
b"\x1b[n" # Reset style
|
82
|
-
)
|
83
|
-
|
84
|
-
# TODO Log any stderr
|
85
|
-
|
86
76
|
# Clean up any temporary file
|
87
77
|
if use_tempfile:
|
88
78
|
tfile.close()
|
@@ -3,7 +3,7 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
import os
|
6
|
-
from functools import
|
6
|
+
from functools import partial, reduce
|
7
7
|
from importlib import import_module
|
8
8
|
from shutil import which
|
9
9
|
from typing import TYPE_CHECKING
|
@@ -13,6 +13,7 @@ from prompt_toolkit.filters import (
|
|
13
13
|
Condition,
|
14
14
|
emacs_insert_mode,
|
15
15
|
emacs_mode,
|
16
|
+
has_completions,
|
16
17
|
to_filter,
|
17
18
|
vi_insert_mode,
|
18
19
|
vi_mode,
|
@@ -50,57 +51,6 @@ def have_modules(*modules: str) -> Filter:
|
|
50
51
|
return reduce(lambda a, b: a & b, filters, to_filter(True))
|
51
52
|
|
52
53
|
|
53
|
-
@Condition
|
54
|
-
@lru_cache
|
55
|
-
def have_ruff() -> bool:
|
56
|
-
"""Determine if ruff is available."""
|
57
|
-
try:
|
58
|
-
import ruff # noqa F401
|
59
|
-
except ModuleNotFoundError:
|
60
|
-
return False
|
61
|
-
else:
|
62
|
-
return True
|
63
|
-
|
64
|
-
|
65
|
-
@Condition
|
66
|
-
@lru_cache
|
67
|
-
def have_black() -> bool:
|
68
|
-
"""Determine if black is available."""
|
69
|
-
try:
|
70
|
-
import black.const # noqa F401
|
71
|
-
except ModuleNotFoundError:
|
72
|
-
return False
|
73
|
-
else:
|
74
|
-
return True
|
75
|
-
|
76
|
-
|
77
|
-
@Condition
|
78
|
-
@lru_cache
|
79
|
-
def have_isort() -> bool:
|
80
|
-
"""Determine if isort is available."""
|
81
|
-
try:
|
82
|
-
import isort # noqa F401
|
83
|
-
except ModuleNotFoundError:
|
84
|
-
return False
|
85
|
-
else:
|
86
|
-
return True
|
87
|
-
|
88
|
-
|
89
|
-
@Condition
|
90
|
-
@lru_cache
|
91
|
-
def have_ssort() -> bool:
|
92
|
-
"""Determine if ssort is available."""
|
93
|
-
try:
|
94
|
-
import ssort # noqa F401
|
95
|
-
except ModuleNotFoundError:
|
96
|
-
return False
|
97
|
-
else:
|
98
|
-
return True
|
99
|
-
|
100
|
-
|
101
|
-
# Determine if we have at least one formatter
|
102
|
-
have_formatter = have_black | have_isort | have_ssort | have_ruff
|
103
|
-
|
104
54
|
# Determine if euporie is running inside a multiplexer.
|
105
55
|
in_screen = to_filter(os.environ.get("TERM", "").startswith("screen"))
|
106
56
|
in_tmux = to_filter(os.environ.get("TMUX") is not None)
|
@@ -157,6 +107,9 @@ def has_menus() -> bool:
|
|
157
107
|
return False
|
158
108
|
|
159
109
|
|
110
|
+
has_float = has_dialog | has_menus | has_completions
|
111
|
+
|
112
|
+
|
160
113
|
@Condition
|
161
114
|
def tab_has_focus() -> bool:
|
162
115
|
"""Determine if there is a currently focused tab."""
|
@@ -67,11 +67,18 @@ class CliFormatter(Formatter):
|
|
67
67
|
except Exception:
|
68
68
|
return text
|
69
69
|
try:
|
70
|
-
output,
|
70
|
+
output, error = proc.communicate(text)
|
71
71
|
except Exception:
|
72
72
|
return text
|
73
73
|
else:
|
74
|
-
|
74
|
+
if output and not error:
|
75
|
+
return output.rstrip("\r\n")
|
76
|
+
else:
|
77
|
+
return text
|
78
|
+
|
79
|
+
def __repr__(self) -> str:
|
80
|
+
"""Return representation of the formatter as a string."""
|
81
|
+
return f"{self.command[0].title()}Formatter()"
|
75
82
|
|
76
83
|
|
77
84
|
class LspFormatter(Formatter):
|
@@ -115,3 +122,7 @@ class LspFormatter(Formatter):
|
|
115
122
|
text = text.rstrip()
|
116
123
|
|
117
124
|
return text
|
125
|
+
|
126
|
+
def __repr__(self) -> str:
|
127
|
+
"""Return representation of the formatter as a string."""
|
128
|
+
return f"{self.lsp.name.title()}Formatter()"
|
@@ -15,7 +15,6 @@ from math import ceil
|
|
15
15
|
from operator import eq, ge, gt, le, lt
|
16
16
|
from typing import TYPE_CHECKING, NamedTuple, cast, overload
|
17
17
|
|
18
|
-
from flatlatex.data import subscript, superscript
|
19
18
|
from fsspec.core import url_to_fs
|
20
19
|
from prompt_toolkit.application.current import get_app_session
|
21
20
|
from prompt_toolkit.data_structures import Size
|
@@ -74,6 +73,16 @@ from euporie.core.ft.utils import (
|
|
74
73
|
valign,
|
75
74
|
)
|
76
75
|
|
76
|
+
sub_trans = str.maketrans(
|
77
|
+
"0123456789+-=()aeijoruvxβγρφχ",
|
78
|
+
"₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑᵢⱼₒᵣᵤᵥₓᵦᵧᵨᵩᵪ",
|
79
|
+
)
|
80
|
+
|
81
|
+
sup_trans = str.maketrans(
|
82
|
+
"0123456789+-=()abcdefghijklmnoprstuvwxyzABDEGHIJKLMNOPRTUVWαβγδ∊θιΦφχ",
|
83
|
+
"⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖʳˢᵗᵘᵛʷˣʸᶻᴬᴮᴰᴱᴳᴴᴵᴶᴷᴸᴹᴺᴼᴾᴿᵀᵁⱽᵂᵅᵝᵞᵟᵋᶿᶥᶲᵠᵡ",
|
84
|
+
)
|
85
|
+
|
77
86
|
|
78
87
|
class CssSelector(NamedTuple):
|
79
88
|
"""A named tuple to hold CSS selector data."""
|
@@ -343,6 +352,7 @@ _HERITABLE_PROPS = {
|
|
343
352
|
"text_transform",
|
344
353
|
"text_decoration",
|
345
354
|
"text_align",
|
355
|
+
"vertical_align",
|
346
356
|
"visibility",
|
347
357
|
"white_space",
|
348
358
|
"list_style_type",
|
@@ -1761,18 +1771,19 @@ class Theme(Mapping):
|
|
1761
1771
|
|
1762
1772
|
async def text_transform(self, value: str) -> str:
|
1763
1773
|
"""Return a function which transforms text."""
|
1764
|
-
|
1774
|
+
transform = self.theme["text_transform"]
|
1775
|
+
if "uppercase" in transform:
|
1765
1776
|
return value.upper()
|
1766
|
-
elif "lowercase" in
|
1777
|
+
elif "lowercase" in transform:
|
1767
1778
|
return value.lower()
|
1768
|
-
elif "capitalize" in
|
1779
|
+
elif "capitalize" in transform:
|
1769
1780
|
return value.capitalize()
|
1770
|
-
elif "
|
1771
|
-
return "".join(subscript.get(c, c) for c in value)
|
1772
|
-
elif "super" in self.theme["vertical_align"]:
|
1773
|
-
return "".join(superscript.get(c, c) for c in value)
|
1774
|
-
elif "latex" in self.theme["text_transform"]:
|
1781
|
+
elif "latex" in transform:
|
1775
1782
|
return await Datum(value, "latex").convert_async("ansi")
|
1783
|
+
if "sub" in self.theme["vertical_align"]:
|
1784
|
+
return value.translate(sub_trans)
|
1785
|
+
elif "super" in self.theme["vertical_align"]:
|
1786
|
+
return value.translate(sup_trans)
|
1776
1787
|
return value
|
1777
1788
|
|
1778
1789
|
@cached_property
|
@@ -2771,7 +2782,7 @@ _BROWSER_CSS: CssSelectors = {
|
|
2771
2782
|
"text_transform": "latex",
|
2772
2783
|
"display": "inline-block",
|
2773
2784
|
"vertical_align": "top",
|
2774
|
-
|
2785
|
+
"white_space": "pre",
|
2775
2786
|
},
|
2776
2787
|
(
|
2777
2788
|
(
|
@@ -4275,7 +4286,7 @@ class HTML:
|
|
4275
4286
|
|
4276
4287
|
# Render text representation
|
4277
4288
|
ft: StyleAndTextTuples = await self.render_node_content(
|
4278
|
-
element, left, fill, align_content
|
4289
|
+
element, left=0, fill=False, align_content=False
|
4279
4290
|
)
|
4280
4291
|
|
4281
4292
|
# Render graphic representation
|
@@ -10,7 +10,6 @@ from typing import TYPE_CHECKING
|
|
10
10
|
|
11
11
|
from prompt_toolkit.cache import FastDictCache, SimpleCache
|
12
12
|
from prompt_toolkit.data_structures import Point
|
13
|
-
from prompt_toolkit.filters.app import has_completions
|
14
13
|
from prompt_toolkit.filters.base import Condition
|
15
14
|
from prompt_toolkit.filters.utils import to_filter
|
16
15
|
from prompt_toolkit.formatted_text.base import to_formatted_text
|
@@ -25,7 +24,7 @@ from euporie.core.convert.datum import Datum
|
|
25
24
|
from euporie.core.convert.registry import find_route
|
26
25
|
from euporie.core.current import get_app
|
27
26
|
from euporie.core.data_structures import DiInt
|
28
|
-
from euporie.core.filters import
|
27
|
+
from euporie.core.filters import has_float, in_mplex
|
29
28
|
from euporie.core.ft.utils import _ZERO_WIDTH_FRAGMENTS
|
30
29
|
from euporie.core.layout.scroll import BoundedWritePosition
|
31
30
|
from euporie.core.terminal import passthrough
|
@@ -192,7 +191,7 @@ class SixelGraphicControl(GraphicControl):
|
|
192
191
|
def render_lines() -> list[StyleAndTextTuples]:
|
193
192
|
"""Render the lines to display in the control."""
|
194
193
|
ft: list[StyleAndTextTuples] = []
|
195
|
-
if height:
|
194
|
+
if height >= 0:
|
196
195
|
cmd = self.convert_data(
|
197
196
|
BoundedWritePosition(0, 0, width, height, self.bbox)
|
198
197
|
)
|
@@ -282,7 +281,7 @@ class ItermGraphicControl(GraphicControl):
|
|
282
281
|
def render_lines() -> list[StyleAndTextTuples]:
|
283
282
|
"""Render the lines to display in the control."""
|
284
283
|
ft: list[StyleAndTextTuples] = []
|
285
|
-
if height:
|
284
|
+
if height > 0 and width > 0:
|
286
285
|
b64data = self.convert_data(
|
287
286
|
BoundedWritePosition(0, 0, width, height, self.bbox)
|
288
287
|
)
|
@@ -574,7 +573,7 @@ class GraphicWindow(Window):
|
|
574
573
|
super().__init__(*args, **kwargs)
|
575
574
|
self.content = content
|
576
575
|
self.get_position = get_position
|
577
|
-
self.filter = ~
|
576
|
+
self.filter = ~has_float & to_filter(filter)
|
578
577
|
self._pre_rendered = False
|
579
578
|
|
580
579
|
def write_to_screen(
|
@@ -680,6 +680,9 @@ def dent_buffer(event: KeyPressEvent, indenting: bool = True) -> None:
|
|
680
680
|
)
|
681
681
|
* sign
|
682
682
|
)
|
683
|
+
selection_state.original_cursor_position = max(
|
684
|
+
min(selection_state.original_cursor_position, len(buffer.text)), 0
|
685
|
+
)
|
683
686
|
# Maintain the selection state before indentation
|
684
687
|
buffer.selection_state = selection_state
|
685
688
|
|
@@ -722,7 +725,7 @@ def indent_lines(event: KeyPressEvent) -> None:
|
|
722
725
|
@add_cmd(
|
723
726
|
filter=buffer_has_focus
|
724
727
|
& (cursor_in_leading_ws | has_selection)
|
725
|
-
& ~cursor_at_start_of_line,
|
728
|
+
& (~cursor_at_start_of_line | cursor_at_start_of_line),
|
726
729
|
)
|
727
730
|
def unindent_lines(event: KeyPressEvent) -> None:
|
728
731
|
"""Unindent the current or selected lines."""
|
@@ -11,6 +11,12 @@ APP_ALIASES = {
|
|
11
11
|
"edit": "notebook",
|
12
12
|
}
|
13
13
|
|
14
|
+
_EPS = entry_points()
|
15
|
+
if isinstance(_EPS, dict):
|
16
|
+
APP_ENTRY_POINTS = _EPS.get("euporie.apps")
|
17
|
+
else:
|
18
|
+
APP_ENTRY_POINTS = _EPS.select(group="euporie.apps")
|
19
|
+
|
14
20
|
|
15
21
|
class CoreApp:
|
16
22
|
"""Launch a euporie application."""
|
@@ -50,8 +56,7 @@ class CoreApp:
|
|
50
56
|
required=False,
|
51
57
|
help_="The application to launch",
|
52
58
|
choices=sorted(
|
53
|
-
{entry.name for entry in
|
54
|
-
| APP_ALIASES.keys()
|
59
|
+
{entry.name for entry in APP_ENTRY_POINTS} - {"launch"} | APP_ALIASES.keys()
|
55
60
|
),
|
56
61
|
description="""
|
57
62
|
The name of the application to launch.
|
@@ -600,9 +600,10 @@ class ScrollingContainer(Container):
|
|
600
600
|
|
601
601
|
def all_children(self) -> Sequence[Container]:
|
602
602
|
"""Return the list of all children of this container."""
|
603
|
-
|
604
|
-
|
605
|
-
|
603
|
+
_children = self._children
|
604
|
+
if self.refresh_children or not self._children:
|
605
|
+
self.refresh_children = False
|
606
|
+
new_children = []
|
606
607
|
new_child_hashes = set()
|
607
608
|
for child in self.children_func():
|
608
609
|
if not (
|
@@ -611,21 +612,21 @@ class ScrollingContainer(Container):
|
|
611
612
|
wrapped_child = self._child_cache[child_hash] = CachedContainer(
|
612
613
|
child, mouse_handler_wrapper=self._mouse_handler_wrapper
|
613
614
|
)
|
614
|
-
|
615
|
+
new_children.append(wrapped_child)
|
615
616
|
new_child_hashes.add(child_hash)
|
617
|
+
_children[:] = new_children
|
616
618
|
|
617
619
|
# Clean up metacache
|
618
620
|
for child_hash in set(self._child_cache) - new_child_hashes:
|
619
621
|
del self._child_cache[child_hash]
|
620
622
|
|
621
|
-
self.refresh_children = False
|
622
623
|
# Clean up positions
|
623
624
|
self.index_positions = {
|
624
625
|
i: pos
|
625
626
|
for i, pos in self.index_positions.items()
|
626
627
|
if i < len(self._children)
|
627
628
|
}
|
628
|
-
return
|
629
|
+
return _children
|
629
630
|
|
630
631
|
def get_children(self) -> list[Container]:
|
631
632
|
"""Return the list of currently visible children to include in the layout."""
|
@@ -17,6 +17,7 @@ from prompt_toolkit.completion.base import (
|
|
17
17
|
_MergedCompleter,
|
18
18
|
)
|
19
19
|
from prompt_toolkit.document import Document
|
20
|
+
from prompt_toolkit.filters.app import is_searching
|
20
21
|
from prompt_toolkit.filters.base import Condition
|
21
22
|
from prompt_toolkit.layout.containers import (
|
22
23
|
ConditionalContainer,
|
@@ -163,6 +164,11 @@ class Cell:
|
|
163
164
|
"""Respond to cursor movements."""
|
164
165
|
# Tell the scrolling container to scroll the cursor into view on the next render
|
165
166
|
weak_self.kernel_tab.page.scroll_to_cursor = True
|
167
|
+
if not is_searching():
|
168
|
+
if not self.selected:
|
169
|
+
weak_self.kernel_tab.select(self.index, scroll=True)
|
170
|
+
if not weak_self.kernel_tab.in_edit_mode():
|
171
|
+
weak_self.kernel_tab.enter_edit_mode()
|
166
172
|
|
167
173
|
# Now we generate the main container used to represent a kernel_tab cell
|
168
174
|
|
@@ -720,6 +726,8 @@ class Cell:
|
|
720
726
|
self.input_box.control._fragment_cache.clear()
|
721
727
|
# Trigger callbacks
|
722
728
|
self.on_change()
|
729
|
+
# Flag notebook as modified
|
730
|
+
self.kernel_tab.dirty = True
|
723
731
|
|
724
732
|
@property
|
725
733
|
def path(self) -> Path:
|
@@ -358,7 +358,7 @@ class CellOutput:
|
|
358
358
|
metadata=self.json.get("metadata", {}).get(mime, {}),
|
359
359
|
parent=self.parent,
|
360
360
|
)
|
361
|
-
except NotImplementedError:
|
361
|
+
except (NotImplementedError, KeyError):
|
362
362
|
self.selected_mime = mime = list(data.keys())[-1]
|
363
363
|
continue
|
364
364
|
else:
|