euporie 2.8.0__py3-none-any.whl → 2.8.5__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 +267 -147
- euporie/core/__init__.py +1 -9
- euporie/core/__main__.py +31 -5
- euporie/core/_settings.py +104 -0
- euporie/core/app/__init__.py +3 -0
- euporie/core/app/_commands.py +70 -0
- euporie/core/app/_settings.py +427 -0
- euporie/core/{app.py → app/app.py} +214 -572
- 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 +182 -0
- euporie/core/bars/menu.py +258 -0
- euporie/core/{widgets → bars}/search.py +154 -57
- euporie/core/{widgets → bars}/status.py +9 -26
- euporie/core/clipboard.py +19 -80
- euporie/core/comm/base.py +8 -6
- euporie/core/comm/ipywidgets.py +21 -12
- euporie/core/comm/registry.py +2 -1
- euporie/core/commands.py +11 -5
- euporie/core/completion.py +3 -2
- euporie/core/config.py +368 -341
- euporie/core/convert/__init__.py +0 -30
- euporie/core/convert/datum.py +131 -60
- euporie/core/convert/formats/__init__.py +31 -0
- euporie/core/convert/formats/ansi.py +46 -30
- 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 +11 -8
- euporie/core/convert/utils.py +50 -23
- euporie/core/diagnostics.py +2 -2
- euporie/core/filters.py +72 -82
- euporie/core/format.py +13 -2
- euporie/core/ft/ansi.py +1 -1
- euporie/core/ft/html.py +36 -36
- euporie/core/ft/table.py +1 -3
- euporie/core/ft/utils.py +4 -1
- euporie/core/graphics.py +216 -124
- 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} +100 -139
- euporie/core/kernel/manager.py +114 -0
- euporie/core/key_binding/bindings/__init__.py +2 -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 +5 -7
- euporie/core/key_binding/bindings/mouse.py +26 -24
- euporie/core/key_binding/bindings/terminal.py +193 -0
- euporie/core/key_binding/bindings/vi.py +46 -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 -92
- euporie/core/layout/cache.py +35 -25
- euporie/core/layout/containers.py +280 -74
- euporie/core/layout/decor.py +5 -5
- euporie/core/layout/mouse.py +1 -1
- euporie/core/layout/print.py +16 -3
- euporie/core/layout/scroll.py +26 -28
- euporie/core/log.py +75 -60
- euporie/core/lsp.py +118 -24
- euporie/core/margins.py +60 -31
- euporie/core/path.py +2 -1
- euporie/core/renderer.py +58 -17
- euporie/core/style.py +60 -40
- euporie/core/suggest.py +103 -85
- euporie/core/tabs/__init__.py +34 -0
- euporie/core/tabs/_settings.py +113 -0
- euporie/core/tabs/base.py +11 -435
- euporie/core/tabs/kernel.py +420 -0
- euporie/core/tabs/notebook.py +20 -54
- euporie/core/utils.py +98 -6
- euporie/core/validation.py +1 -1
- euporie/core/widgets/_settings.py +188 -0
- euporie/core/widgets/cell.py +90 -158
- euporie/core/widgets/cell_outputs.py +26 -37
- euporie/core/widgets/decor.py +11 -41
- euporie/core/widgets/dialog.py +55 -44
- euporie/core/widgets/display.py +27 -24
- euporie/core/widgets/file_browser.py +5 -26
- euporie/core/widgets/forms.py +16 -12
- euporie/core/widgets/inputs.py +37 -81
- euporie/core/widgets/layout.py +7 -6
- euporie/core/widgets/logo.py +49 -0
- euporie/core/widgets/menu.py +13 -11
- euporie/core/widgets/pager.py +9 -11
- euporie/core/widgets/palette.py +6 -6
- euporie/hub/app.py +52 -31
- euporie/notebook/_commands.py +24 -0
- euporie/notebook/_settings.py +107 -0
- euporie/notebook/app.py +109 -210
- 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 +2 -2
- euporie/notebook/tabs/edit.py +12 -7
- euporie/notebook/tabs/json.py +3 -3
- euporie/notebook/tabs/log.py +1 -18
- euporie/notebook/tabs/notebook.py +21 -674
- 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 +8 -30
- euporie/preview/tabs/notebook.py +15 -86
- euporie/web/tabs/web.py +4 -6
- euporie/web/widgets/webview.py +5 -12
- {euporie-2.8.0.dist-info → euporie-2.8.5.dist-info}/METADATA +11 -15
- euporie-2.8.5.dist-info/RECORD +172 -0
- {euporie-2.8.0.dist-info → euporie-2.8.5.dist-info}/WHEEL +1 -1
- {euporie-2.8.0.dist-info → euporie-2.8.5.dist-info}/entry_points.txt +2 -2
- {euporie-2.8.0.dist-info → euporie-2.8.5.dist-info}/licenses/LICENSE +1 -1
- euporie/core/launch.py +0 -59
- euporie/core/terminal.py +0 -527
- euporie-2.8.0.dist-info/RECORD +0 -146
- {euporie-2.8.0.data → euporie-2.8.5.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.8.0.data → euporie-2.8.5.data}/data/share/applications/euporie-notebook.desktop +0 -0
@@ -72,7 +72,7 @@ def crop(data: PilImage, bbox: DiInt) -> PilImage:
|
|
72
72
|
rows=full_height,
|
73
73
|
)
|
74
74
|
if image is not None:
|
75
|
-
cell_size_x, cell_size_y = self.app.
|
75
|
+
cell_size_x, cell_size_y = self.app.cell_size_px
|
76
76
|
# Downscale image to fit target region for precise cropping
|
77
77
|
image.thumbnail((full_width * cell_size_x, full_height * cell_size_y))
|
78
78
|
image = image.crop(
|
@@ -13,7 +13,6 @@ from euporie.core.filters import command_exists, have_modules
|
|
13
13
|
if TYPE_CHECKING:
|
14
14
|
from euporie.core.convert.datum import Datum
|
15
15
|
|
16
|
-
|
17
16
|
register(
|
18
17
|
from_="base64-png",
|
19
18
|
to="png",
|
@@ -126,9 +125,8 @@ async def latex_to_png_py_mpl(
|
|
126
125
|
from matplotlib.backends import backend_agg
|
127
126
|
|
128
127
|
# mpl mathtext doesn't support display math, force inline
|
129
|
-
data = datum.data.
|
130
|
-
|
131
|
-
data = f"${data}$"
|
128
|
+
data = datum.data.replace("$", "").strip()
|
129
|
+
data = f"${data}$"
|
132
130
|
buffer = BytesIO()
|
133
131
|
prop = font_manager.FontProperties(size=12)
|
134
132
|
parser = mathtext.MathTextParser("path")
|
@@ -143,7 +141,7 @@ async def latex_to_png_py_mpl(
|
|
143
141
|
register(
|
144
142
|
from_=("svg", "jpeg", "pdf", "gif"),
|
145
143
|
to="png",
|
146
|
-
filter_=command_exists("
|
144
|
+
filter_=command_exists("magick"),
|
147
145
|
)(partial(imagemagick_convert, "PNG"))
|
148
146
|
|
149
147
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
5
5
|
from functools import partial
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
|
+
from euporie.core.app.current import get_app
|
8
9
|
from euporie.core.convert.formats.common import (
|
9
10
|
chafa_convert_cmd,
|
10
11
|
chafa_convert_py,
|
@@ -12,7 +13,6 @@ from euporie.core.convert.formats.common import (
|
|
12
13
|
)
|
13
14
|
from euporie.core.convert.registry import register
|
14
15
|
from euporie.core.convert.utils import call_subproc
|
15
|
-
from euporie.core.current import get_app
|
16
16
|
from euporie.core.filters import command_exists, have_modules
|
17
17
|
|
18
18
|
if TYPE_CHECKING:
|
@@ -53,7 +53,7 @@ async def png_to_sixel_img2sixel(
|
|
53
53
|
if bg:
|
54
54
|
cmd += [f"--bgcolor={bg}"]
|
55
55
|
if cols is not None:
|
56
|
-
px, _ = get_app().
|
56
|
+
px, _ = get_app().cell_size_px
|
57
57
|
cmd += [f"--width={int(cols * px)}"]
|
58
58
|
return (await call_subproc(datum.data, cmd)).decode()
|
59
59
|
|
@@ -61,7 +61,7 @@ async def png_to_sixel_img2sixel(
|
|
61
61
|
register(
|
62
62
|
from_=("png", "jpeg", "svg", "pdf"),
|
63
63
|
to="sixel",
|
64
|
-
filter_=command_exists("
|
64
|
+
filter_=command_exists("magick"),
|
65
65
|
)(partial(imagemagick_convert, "sixel"))
|
66
66
|
|
67
67
|
|
euporie/core/convert/registry.py
CHANGED
@@ -8,19 +8,15 @@ from typing import TYPE_CHECKING, NamedTuple
|
|
8
8
|
from prompt_toolkit.cache import FastDictCache, SimpleCache
|
9
9
|
from prompt_toolkit.filters import to_filter
|
10
10
|
|
11
|
-
# from euporie.core.cache import cache
|
12
|
-
|
13
11
|
if TYPE_CHECKING:
|
14
|
-
from
|
12
|
+
from collections.abc import Iterable
|
13
|
+
from typing import Callable
|
15
14
|
|
16
15
|
from prompt_toolkit.filters import Filter, FilterOrBool
|
17
16
|
|
18
17
|
log = logging.getLogger(__name__)
|
19
18
|
|
20
19
|
|
21
|
-
BASE64_FORMATS = {"png", "jpeg", "pdf", "gif"}
|
22
|
-
|
23
|
-
|
24
20
|
class Converter(NamedTuple):
|
25
21
|
"""Hold a conversion function and its weight."""
|
26
22
|
|
@@ -58,8 +54,10 @@ def register(
|
|
58
54
|
return decorator
|
59
55
|
|
60
56
|
|
61
|
-
def
|
57
|
+
def _find_route(from_: str, to: str) -> list | None:
|
62
58
|
"""Find the shortest conversion path between two formats."""
|
59
|
+
from euporie.core.convert import formats # noqa: F401
|
60
|
+
|
63
61
|
if from_ == to:
|
64
62
|
return [from_]
|
65
63
|
|
@@ -100,5 +98,10 @@ def find_route(from_: str, to: str) -> list | None:
|
|
100
98
|
|
101
99
|
|
102
100
|
_CONVERTOR_ROUTE_CACHE: FastDictCache[tuple[str, str], list | None] = FastDictCache(
|
103
|
-
|
101
|
+
_find_route
|
104
102
|
)
|
103
|
+
|
104
|
+
|
105
|
+
def find_route(from_: str, to: str) -> list | None:
|
106
|
+
"""Find and cache conversion routes."""
|
107
|
+
return _CONVERTOR_ROUTE_CACHE[from_, to]
|
euporie/core/convert/utils.py
CHANGED
@@ -6,15 +6,49 @@ import asyncio
|
|
6
6
|
import logging
|
7
7
|
import subprocess # S404 - Security implications have been considered
|
8
8
|
import tempfile
|
9
|
+
from math import ceil
|
9
10
|
from pathlib import Path
|
10
11
|
from typing import TYPE_CHECKING
|
11
12
|
|
13
|
+
from euporie.core.app.current import get_app
|
14
|
+
|
12
15
|
if TYPE_CHECKING:
|
13
16
|
from typing import Any
|
14
17
|
|
18
|
+
from euporie.core.convert.datum import Datum
|
19
|
+
|
15
20
|
log = logging.getLogger(__name__)
|
16
21
|
|
17
22
|
|
23
|
+
async def scale_to_fit(
|
24
|
+
datum: Datum, cols: int | None, rows: int | None
|
25
|
+
) -> tuple[int, int]:
|
26
|
+
"""Calculate image size based on aspect ratio, and scale to fit."""
|
27
|
+
data = datum.data
|
28
|
+
px, py = get_app().cell_size_px
|
29
|
+
|
30
|
+
# Calculate rows based on image aspect ratio
|
31
|
+
w, h = data.size
|
32
|
+
if rows is None and cols is not None:
|
33
|
+
rows = ceil(cols / w * h)
|
34
|
+
elif cols is None and rows is not None:
|
35
|
+
cols = ceil(rows / h * w)
|
36
|
+
elif rows is None and cols is None:
|
37
|
+
cols = ceil(w / px)
|
38
|
+
rows = ceil(h / py)
|
39
|
+
assert rows is not None
|
40
|
+
assert cols is not None
|
41
|
+
|
42
|
+
# Scale to fit while maintaining aspect ratio
|
43
|
+
_width, aspect = await datum.cell_size_async()
|
44
|
+
if cols * aspect < rows:
|
45
|
+
rows = ceil(cols * aspect)
|
46
|
+
else:
|
47
|
+
cols = ceil(rows / aspect)
|
48
|
+
|
49
|
+
return cols, rows
|
50
|
+
|
51
|
+
|
18
52
|
async def call_subproc(
|
19
53
|
data: str | bytes,
|
20
54
|
cmd: list[Any],
|
@@ -43,15 +77,18 @@ async def call_subproc(
|
|
43
77
|
if use_tempfile:
|
44
78
|
# If the command cannot read from stdin, create a temporary file to pass to
|
45
79
|
# the command
|
46
|
-
|
47
|
-
|
48
|
-
tfile.close()
|
80
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tfile:
|
81
|
+
tfile.write(data)
|
49
82
|
cmd.append(tfile.name)
|
50
83
|
stdinput = None
|
51
84
|
else:
|
52
85
|
stdinput = data
|
53
86
|
|
54
|
-
log.
|
87
|
+
if log.level <= 0:
|
88
|
+
import shlex
|
89
|
+
|
90
|
+
log.debug("Running external command `%s`", shlex.join(cmd))
|
91
|
+
|
55
92
|
error: Exception | None = None
|
56
93
|
try:
|
57
94
|
proc = await asyncio.create_subprocess_exec(
|
@@ -61,28 +98,18 @@ async def call_subproc(
|
|
61
98
|
stderr=asyncio.subprocess.DEVNULL,
|
62
99
|
)
|
63
100
|
output_bytes, _ = await proc.communicate(stdinput)
|
64
|
-
except FileNotFoundError as
|
101
|
+
except FileNotFoundError as error:
|
65
102
|
log.error("Could not run external command `%s`", cmd)
|
66
|
-
error
|
67
|
-
except subprocess.CalledProcessError as
|
103
|
+
raise error
|
104
|
+
except subprocess.CalledProcessError as error:
|
68
105
|
log.error("There was an error while running external command `%s`", cmd)
|
69
|
-
error
|
106
|
+
raise error
|
107
|
+
else:
|
108
|
+
if (proc.returncode or 0) > 0 or error:
|
109
|
+
# Raise an exception if the process failed so we can continue on the the
|
110
|
+
# next conversion method
|
111
|
+
raise subprocess.CalledProcessError(proc.returncode or 0, cmd)
|
70
112
|
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
113
|
# Clean up any temporary file
|
87
114
|
if use_tempfile:
|
88
115
|
tfile.close()
|
euporie/core/diagnostics.py
CHANGED
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
4
4
|
|
5
5
|
import logging
|
6
6
|
from abc import ABCMeta
|
7
|
-
from typing import TYPE_CHECKING,
|
7
|
+
from typing import TYPE_CHECKING, NamedTuple
|
8
8
|
|
9
9
|
if TYPE_CHECKING:
|
10
10
|
from typing import Literal
|
@@ -23,7 +23,7 @@ class Diagnostic(NamedTuple):
|
|
23
23
|
chars: slice
|
24
24
|
|
25
25
|
|
26
|
-
class Report(
|
26
|
+
class Report(list[Diagnostic], metaclass=ABCMeta):
|
27
27
|
"""Class for storing a diagnostic report."""
|
28
28
|
|
29
29
|
@classmethod
|
euporie/core/filters.py
CHANGED
@@ -3,16 +3,17 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
import os
|
6
|
-
from functools import
|
7
|
-
from importlib import
|
6
|
+
from functools import cache, partial, reduce
|
7
|
+
from importlib.util import find_spec
|
8
8
|
from shutil import which
|
9
9
|
from typing import TYPE_CHECKING
|
10
10
|
|
11
|
-
from prompt_toolkit.enums import EditingMode
|
11
|
+
# from prompt_toolkit.enums import EditingMode
|
12
12
|
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,
|
@@ -26,6 +27,7 @@ if TYPE_CHECKING:
|
|
26
27
|
from prompt_toolkit.layout.containers import Window
|
27
28
|
|
28
29
|
|
30
|
+
@cache
|
29
31
|
def command_exists(*cmds: str) -> Filter:
|
30
32
|
"""Verify a list of external commands exist on the system."""
|
31
33
|
filters = [
|
@@ -35,72 +37,18 @@ def command_exists(*cmds: str) -> Filter:
|
|
35
37
|
return reduce(lambda a, b: a & b, filters, to_filter(True))
|
36
38
|
|
37
39
|
|
40
|
+
@cache
|
38
41
|
def have_modules(*modules: str) -> Filter:
|
39
42
|
"""Verify a list of python modules are importable."""
|
40
43
|
|
41
44
|
def try_import(module: str) -> bool:
|
42
|
-
|
43
|
-
|
44
|
-
except ModuleNotFoundError:
|
45
|
-
return False
|
46
|
-
else:
|
47
|
-
return True
|
45
|
+
loader = find_spec(module)
|
46
|
+
return loader is not None
|
48
47
|
|
49
48
|
filters = [Condition(partial(try_import, module)) for module in modules]
|
50
49
|
return reduce(lambda a, b: a & b, filters, to_filter(True))
|
51
50
|
|
52
51
|
|
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
52
|
# Determine if euporie is running inside a multiplexer.
|
105
53
|
in_screen = to_filter(os.environ.get("TERM", "").startswith("screen"))
|
106
54
|
in_tmux = to_filter(os.environ.get("TMUX") is not None)
|
@@ -129,12 +77,20 @@ def has_suggestion() -> bool:
|
|
129
77
|
)
|
130
78
|
|
131
79
|
|
80
|
+
@Condition
|
81
|
+
def has_tabs() -> bool:
|
82
|
+
"""Filter to show if any tabs are open in an app."""
|
83
|
+
from euporie.core.app.current import get_app
|
84
|
+
|
85
|
+
return bool(get_app().tabs)
|
86
|
+
|
87
|
+
|
132
88
|
@Condition
|
133
89
|
def has_dialog() -> bool:
|
134
90
|
"""Determine if a dialog is being displayed."""
|
135
91
|
from prompt_toolkit.layout.containers import ConditionalContainer
|
136
92
|
|
137
|
-
from euporie.core.current import get_app
|
93
|
+
from euporie.core.app.current import get_app
|
138
94
|
|
139
95
|
app = get_app()
|
140
96
|
for dialog in app.dialogs.values():
|
@@ -157,10 +113,22 @@ def has_menus() -> bool:
|
|
157
113
|
return False
|
158
114
|
|
159
115
|
|
116
|
+
has_float = has_dialog | has_menus | has_completions
|
117
|
+
|
118
|
+
|
119
|
+
@Condition
|
120
|
+
def has_toolbar() -> bool:
|
121
|
+
"""Is there an active toolbar?"""
|
122
|
+
from euporie.core.app.current import get_app
|
123
|
+
from euporie.core.bars import BAR_BUFFERS
|
124
|
+
|
125
|
+
return get_app().current_buffer.name in BAR_BUFFERS
|
126
|
+
|
127
|
+
|
160
128
|
@Condition
|
161
129
|
def tab_has_focus() -> bool:
|
162
130
|
"""Determine if there is a currently focused tab."""
|
163
|
-
from euporie.core.current import get_app
|
131
|
+
from euporie.core.app.current import get_app
|
164
132
|
|
165
133
|
return get_app().tab is not None
|
166
134
|
|
@@ -168,7 +136,7 @@ def tab_has_focus() -> bool:
|
|
168
136
|
@Condition
|
169
137
|
def pager_has_focus() -> bool:
|
170
138
|
"""Determine if there is a currently focused notebook."""
|
171
|
-
from euporie.core.current import get_app
|
139
|
+
from euporie.core.app.current import get_app
|
172
140
|
|
173
141
|
app = get_app()
|
174
142
|
pager = app.pager
|
@@ -180,7 +148,7 @@ def pager_has_focus() -> bool:
|
|
180
148
|
@Condition
|
181
149
|
def display_has_focus() -> bool:
|
182
150
|
"""Determine if there is a currently focused cell."""
|
183
|
-
from euporie.core.current import get_app
|
151
|
+
from euporie.core.app.current import get_app
|
184
152
|
from euporie.core.widgets.display import DisplayControl
|
185
153
|
|
186
154
|
return isinstance(get_app().layout.current_control, DisplayControl)
|
@@ -189,7 +157,7 @@ def display_has_focus() -> bool:
|
|
189
157
|
@Condition
|
190
158
|
def buffer_is_empty() -> bool:
|
191
159
|
"""Determine if the current buffer contains nothing."""
|
192
|
-
from euporie.core.current import get_app
|
160
|
+
from euporie.core.app.current import get_app
|
193
161
|
|
194
162
|
return not get_app().current_buffer.text
|
195
163
|
|
@@ -197,7 +165,7 @@ def buffer_is_empty() -> bool:
|
|
197
165
|
@Condition
|
198
166
|
def buffer_is_code() -> bool:
|
199
167
|
"""Determine if the current buffer contains code."""
|
200
|
-
from euporie.core.current import get_app
|
168
|
+
from euporie.core.app.current import get_app
|
201
169
|
|
202
170
|
return get_app().current_buffer.name == "code"
|
203
171
|
|
@@ -205,7 +173,7 @@ def buffer_is_code() -> bool:
|
|
205
173
|
@Condition
|
206
174
|
def buffer_is_markdown() -> bool:
|
207
175
|
"""Determine if the current buffer contains markdown."""
|
208
|
-
from euporie.core.current import get_app
|
176
|
+
from euporie.core.app.current import get_app
|
209
177
|
|
210
178
|
return get_app().current_buffer.name == "markdown"
|
211
179
|
|
@@ -213,15 +181,16 @@ def buffer_is_markdown() -> bool:
|
|
213
181
|
@Condition
|
214
182
|
def micro_mode() -> bool:
|
215
183
|
"""When the micro key-bindings are active."""
|
216
|
-
from euporie.core.
|
184
|
+
from euporie.core.app.app import ExtraEditingMode
|
185
|
+
from euporie.core.app.current import get_app
|
217
186
|
|
218
|
-
return get_app().editing_mode ==
|
187
|
+
return get_app().editing_mode == ExtraEditingMode.MICRO
|
219
188
|
|
220
189
|
|
221
190
|
@Condition
|
222
191
|
def micro_replace_mode() -> bool:
|
223
192
|
"""Determine if the editor is in overwrite mode."""
|
224
|
-
from euporie.core.current import get_app
|
193
|
+
from euporie.core.app.current import get_app
|
225
194
|
|
226
195
|
app = get_app()
|
227
196
|
return app.micro_state.input_mode == MicroInputMode.REPLACE
|
@@ -230,7 +199,7 @@ def micro_replace_mode() -> bool:
|
|
230
199
|
@Condition
|
231
200
|
def micro_insert_mode() -> bool:
|
232
201
|
"""Determine if the editor is in insert mode."""
|
233
|
-
from euporie.core.current import get_app
|
202
|
+
from euporie.core.app.current import get_app
|
234
203
|
|
235
204
|
app = get_app()
|
236
205
|
return app.micro_state.input_mode == MicroInputMode.INSERT
|
@@ -239,7 +208,7 @@ def micro_insert_mode() -> bool:
|
|
239
208
|
@Condition
|
240
209
|
def micro_recording_macro() -> bool:
|
241
210
|
"""Determine if a micro macro is being recorded."""
|
242
|
-
from euporie.core.current import get_app
|
211
|
+
from euporie.core.app.current import get_app
|
243
212
|
|
244
213
|
return get_app().micro_state.current_recording is not None
|
245
214
|
|
@@ -247,7 +216,7 @@ def micro_recording_macro() -> bool:
|
|
247
216
|
@Condition
|
248
217
|
def is_returnable() -> bool:
|
249
218
|
"""Determine if the current buffer has an accept handler."""
|
250
|
-
from euporie.core.current import get_app
|
219
|
+
from euporie.core.app.current import get_app
|
251
220
|
|
252
221
|
return get_app().current_buffer.is_returnable
|
253
222
|
|
@@ -255,7 +224,7 @@ def is_returnable() -> bool:
|
|
255
224
|
@Condition
|
256
225
|
def cursor_at_start_of_line() -> bool:
|
257
226
|
"""Determine if the cursor is at the start of a line."""
|
258
|
-
from euporie.core.current import get_app
|
227
|
+
from euporie.core.app.current import get_app
|
259
228
|
|
260
229
|
return get_app().current_buffer.document.cursor_position_col == 0
|
261
230
|
|
@@ -263,7 +232,7 @@ def cursor_at_start_of_line() -> bool:
|
|
263
232
|
@Condition
|
264
233
|
def cursor_on_first_line() -> bool:
|
265
234
|
"""Determine if the cursor is on the first line of a buffer."""
|
266
|
-
from euporie.core.current import get_app
|
235
|
+
from euporie.core.app.current import get_app
|
267
236
|
|
268
237
|
return get_app().current_buffer.document.on_first_line
|
269
238
|
|
@@ -271,11 +240,32 @@ def cursor_on_first_line() -> bool:
|
|
271
240
|
@Condition
|
272
241
|
def cursor_on_last_line() -> bool:
|
273
242
|
"""Determine if the cursor is on the last line of a buffer."""
|
274
|
-
from euporie.core.current import get_app
|
243
|
+
from euporie.core.app.current import get_app
|
275
244
|
|
276
245
|
return get_app().current_buffer.document.on_last_line
|
277
246
|
|
278
247
|
|
248
|
+
@cache
|
249
|
+
def char_after_cursor(char: str) -> Condition:
|
250
|
+
"""Generate a condition to check for a character after the cursor."""
|
251
|
+
from euporie.core.app.current import get_app
|
252
|
+
|
253
|
+
return Condition(
|
254
|
+
lambda: bool(
|
255
|
+
(post := get_app().current_buffer.document.text_after_cursor)
|
256
|
+
and post[0] == char
|
257
|
+
)
|
258
|
+
)
|
259
|
+
|
260
|
+
|
261
|
+
@Condition
|
262
|
+
def has_matching_bracket() -> bool:
|
263
|
+
"""Determine if the bracket at the cursor has a matching pair."""
|
264
|
+
from euporie.core.app.current import get_app
|
265
|
+
|
266
|
+
return bool(get_app().current_buffer.document.find_matching_bracket_position())
|
267
|
+
|
268
|
+
|
279
269
|
"""Determine if any binding style is in insert mode."""
|
280
270
|
insert_mode = (
|
281
271
|
(vi_mode & vi_insert_mode)
|
@@ -290,7 +280,7 @@ replace_mode = micro_replace_mode | vi_replace_mode
|
|
290
280
|
@Condition
|
291
281
|
def is_searching() -> bool:
|
292
282
|
"""Determine if the app is in search mode."""
|
293
|
-
from euporie.core.current import get_app
|
283
|
+
from euporie.core.app.current import get_app
|
294
284
|
|
295
285
|
app = get_app()
|
296
286
|
return (
|
@@ -310,8 +300,8 @@ def at_end_of_buffer() -> bool:
|
|
310
300
|
@Condition
|
311
301
|
def kernel_is_python() -> bool:
|
312
302
|
"""Determine if the current notebook has a python kernel."""
|
313
|
-
from euporie.core.current import get_app
|
314
|
-
from euporie.core.tabs.
|
303
|
+
from euporie.core.app.current import get_app
|
304
|
+
from euporie.core.tabs.kernel import KernelTab
|
315
305
|
|
316
306
|
kernel_tab = get_app().tab
|
317
307
|
if isinstance(kernel_tab, KernelTab):
|
@@ -322,7 +312,7 @@ def kernel_is_python() -> bool:
|
|
322
312
|
@Condition
|
323
313
|
def multiple_cells_selected() -> bool:
|
324
314
|
"""Determine if there is more than one selected cell."""
|
325
|
-
from euporie.core.current import get_app
|
315
|
+
from euporie.core.app.current import get_app
|
326
316
|
from euporie.core.tabs.notebook import BaseNotebook
|
327
317
|
|
328
318
|
nb = get_app().tab
|
@@ -334,8 +324,8 @@ def multiple_cells_selected() -> bool:
|
|
334
324
|
@Condition
|
335
325
|
def kernel_tab_has_focus() -> bool:
|
336
326
|
"""Determine if there is a focused kernel tab."""
|
337
|
-
from euporie.core.current import get_app
|
338
|
-
from euporie.core.tabs.
|
327
|
+
from euporie.core.app.current import get_app
|
328
|
+
from euporie.core.tabs.kernel import KernelTab
|
339
329
|
|
340
330
|
return isinstance(get_app().tab, KernelTab)
|
341
331
|
|
euporie/core/format.py
CHANGED
@@ -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()"
|
euporie/core/ft/ansi.py
CHANGED