euporie 2.8.5__py3-none-any.whl → 2.8.7__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/app.py +2 -0
- euporie/console/tabs/console.py +27 -17
- euporie/core/__init__.py +2 -2
- euporie/core/__main__.py +2 -2
- euporie/core/_settings.py +7 -2
- euporie/core/app/_commands.py +20 -12
- euporie/core/app/_settings.py +34 -4
- euporie/core/app/app.py +31 -18
- euporie/core/bars/command.py +53 -27
- euporie/core/bars/search.py +43 -2
- euporie/core/border.py +7 -2
- euporie/core/comm/base.py +2 -2
- euporie/core/comm/ipywidgets.py +3 -3
- euporie/core/commands.py +44 -24
- euporie/core/completion.py +14 -6
- euporie/core/convert/datum.py +7 -7
- euporie/core/data_structures.py +20 -1
- euporie/core/filters.py +40 -9
- euporie/core/format.py +2 -3
- euporie/core/ft/html.py +47 -40
- euporie/core/graphics.py +199 -31
- euporie/core/history.py +15 -5
- euporie/core/inspection.py +16 -9
- euporie/core/kernel/__init__.py +53 -1
- euporie/core/kernel/base.py +571 -0
- euporie/core/kernel/{client.py → jupyter.py} +173 -430
- euporie/core/kernel/{manager.py → jupyter_manager.py} +4 -3
- euporie/core/kernel/local.py +694 -0
- euporie/core/key_binding/bindings/basic.py +6 -3
- euporie/core/keys.py +26 -25
- euporie/core/layout/cache.py +31 -7
- euporie/core/layout/containers.py +88 -13
- euporie/core/layout/scroll.py +69 -170
- euporie/core/log.py +2 -5
- euporie/core/path.py +61 -13
- euporie/core/style.py +2 -1
- euporie/core/suggest.py +155 -74
- euporie/core/tabs/__init__.py +12 -4
- euporie/core/tabs/_commands.py +76 -0
- euporie/core/tabs/_settings.py +16 -0
- euporie/core/tabs/base.py +89 -9
- euporie/core/tabs/kernel.py +83 -38
- euporie/core/tabs/notebook.py +28 -76
- euporie/core/utils.py +2 -19
- euporie/core/validation.py +8 -8
- euporie/core/widgets/_settings.py +19 -2
- euporie/core/widgets/cell.py +32 -32
- euporie/core/widgets/cell_outputs.py +10 -1
- euporie/core/widgets/dialog.py +60 -76
- euporie/core/widgets/display.py +2 -2
- euporie/core/widgets/forms.py +71 -59
- euporie/core/widgets/inputs.py +7 -4
- euporie/core/widgets/layout.py +281 -93
- euporie/core/widgets/menu.py +56 -16
- euporie/core/widgets/palette.py +3 -1
- euporie/core/widgets/tree.py +86 -76
- euporie/notebook/app.py +35 -16
- euporie/notebook/tabs/display.py +2 -2
- euporie/notebook/tabs/edit.py +11 -46
- euporie/notebook/tabs/json.py +8 -4
- euporie/notebook/tabs/notebook.py +26 -8
- euporie/preview/tabs/notebook.py +17 -13
- euporie/web/__init__.py +1 -0
- euporie/web/tabs/__init__.py +14 -0
- euporie/web/tabs/web.py +30 -5
- euporie/web/widgets/__init__.py +1 -0
- euporie/web/widgets/webview.py +5 -4
- {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/METADATA +4 -2
- {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/RECORD +74 -68
- {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/entry_points.txt +1 -1
- {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/licenses/LICENSE +1 -1
- {euporie-2.8.5.data → euporie-2.8.7.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.8.5.data → euporie-2.8.7.data}/data/share/applications/euporie-notebook.desktop +0 -0
- {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/WHEEL +0 -0
@@ -116,6 +116,16 @@ class CellOutputDataElement(CellOutputElement):
|
|
116
116
|
format_ = data_format
|
117
117
|
break
|
118
118
|
|
119
|
+
config = get_app().config
|
120
|
+
|
121
|
+
# Limit size of text only outputs
|
122
|
+
if (
|
123
|
+
format_ == "ansi"
|
124
|
+
and (limit := config.text_output_limit) > 0
|
125
|
+
and len(data) > limit
|
126
|
+
):
|
127
|
+
data = data[:limit] + "\n… (Output truncated)"
|
128
|
+
|
119
129
|
self._datum = Datum(
|
120
130
|
data,
|
121
131
|
format_,
|
@@ -123,7 +133,6 @@ class CellOutputDataElement(CellOutputElement):
|
|
123
133
|
py=metadata.get("height"),
|
124
134
|
bg=bg_color,
|
125
135
|
)
|
126
|
-
config = get_app().config
|
127
136
|
|
128
137
|
self.container = Display(
|
129
138
|
self._datum,
|
euporie/core/widgets/dialog.py
CHANGED
@@ -16,6 +16,7 @@ from prompt_toolkit.filters import (
|
|
16
16
|
buffer_has_focus,
|
17
17
|
has_completions,
|
18
18
|
has_focus,
|
19
|
+
vi_insert_mode,
|
19
20
|
)
|
20
21
|
from prompt_toolkit.formatted_text import AnyFormattedText, to_formatted_text
|
21
22
|
from prompt_toolkit.formatted_text.utils import split_lines
|
@@ -40,7 +41,6 @@ from euporie.core.border import (
|
|
40
41
|
UpperRightHalfLine,
|
41
42
|
)
|
42
43
|
from euporie.core.commands import add_cmd
|
43
|
-
from euporie.core.filters import tab_has_focus
|
44
44
|
from euporie.core.ft.utils import FormattedTextAlign, align, lex
|
45
45
|
from euporie.core.key_binding.registry import register_bindings
|
46
46
|
from euporie.core.layout.containers import HSplit, VSplit, Window
|
@@ -64,6 +64,7 @@ if TYPE_CHECKING:
|
|
64
64
|
from prompt_toolkit.mouse_events import MouseEvent
|
65
65
|
|
66
66
|
from euporie.core.app.app import BaseApp
|
67
|
+
from euporie.core.kernel.base import KernelInfo
|
67
68
|
from euporie.core.tabs.base import Tab
|
68
69
|
from euporie.core.tabs.kernel import KernelTab
|
69
70
|
|
@@ -197,7 +198,7 @@ class Dialog(Float, metaclass=ABCMeta):
|
|
197
198
|
|
198
199
|
# Create key-bindings
|
199
200
|
kb = KeyBindings()
|
200
|
-
kb.add("escape"
|
201
|
+
kb.add("escape", filter=~(buffer_has_focus & vi_insert_mode))(self.hide)
|
201
202
|
kb.add("tab", filter=~has_completions)(focus_next)
|
202
203
|
kb.add("s-tab", filter=~has_completions)(focus_previous)
|
203
204
|
|
@@ -597,21 +598,6 @@ class SaveAsDialog(FileDialog):
|
|
597
598
|
if callable(cb):
|
598
599
|
cb()
|
599
600
|
|
600
|
-
# ################################### Commands ####################################
|
601
|
-
|
602
|
-
@staticmethod
|
603
|
-
@add_cmd(
|
604
|
-
menu_title="Save As…",
|
605
|
-
filter=tab_has_focus,
|
606
|
-
)
|
607
|
-
def _save_as() -> None:
|
608
|
-
"""Save the current file at a new location."""
|
609
|
-
from euporie.core.app.current import get_app
|
610
|
-
|
611
|
-
app = get_app()
|
612
|
-
if dialog := app.dialogs.get("save-as"):
|
613
|
-
dialog.show(tab=app.tab)
|
614
|
-
|
615
601
|
# ################################# Key Bindings ##################################
|
616
602
|
|
617
603
|
register_bindings(
|
@@ -655,62 +641,37 @@ class SelectKernelDialog(Dialog):
|
|
655
641
|
|
656
642
|
title = "Select Kernel"
|
657
643
|
|
658
|
-
def load(
|
659
|
-
self,
|
660
|
-
kernel_specs: dict[str, Any] | None = None,
|
661
|
-
runtime_dirs: dict[str, Path] | None = None,
|
662
|
-
tab: KernelTab | None = None,
|
663
|
-
message: str = "",
|
664
|
-
) -> None:
|
644
|
+
def load(self, tab: KernelTab | None = None, message: str = "") -> None:
|
665
645
|
"""Load dialog body & buttons."""
|
666
|
-
from
|
667
|
-
|
646
|
+
from euporie.core.kernel import list_kernels
|
668
647
|
from euporie.core.widgets.layout import TabbedSplit
|
669
648
|
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
connection_files = {
|
689
|
-
path.name: path
|
690
|
-
for path in Path(jupyter_runtime_dir()).glob("kernel-*.json")
|
691
|
-
}
|
692
|
-
options_files = Select(
|
693
|
-
options=list(connection_files.values()),
|
694
|
-
labels=list(connection_files.keys()),
|
695
|
-
style="class:input,radio-buttons",
|
696
|
-
prefix=("○", "◉"),
|
697
|
-
multiple=False,
|
698
|
-
border=None,
|
699
|
-
rows=5,
|
700
|
-
dont_extend_width=False,
|
701
|
-
)
|
702
|
-
|
703
|
-
msg_ft = (f"{message}\n\n" if message else "") + "Please select a kernel:"
|
649
|
+
infos = list_kernels()
|
650
|
+
infos_by_kind: dict[str, list[KernelInfo]] = {}
|
651
|
+
for info in infos:
|
652
|
+
infos_by_kind.setdefault(info.kind, []).append(info)
|
653
|
+
|
654
|
+
selects = {}
|
655
|
+
for kind, infos in infos_by_kind.items():
|
656
|
+
selects[kind] = Select(
|
657
|
+
options=infos,
|
658
|
+
labels=[info.display_name for info in infos],
|
659
|
+
style="class:input,radio-buttons",
|
660
|
+
prefix=("○", "◉"),
|
661
|
+
multiple=False,
|
662
|
+
border=None,
|
663
|
+
rows=5,
|
664
|
+
dont_extend_width=False,
|
665
|
+
)
|
704
666
|
|
705
667
|
self.body = HSplit(
|
706
668
|
[
|
707
|
-
Label(
|
669
|
+
Label(
|
670
|
+
(f"{message}\n\n" if message else "") + "Please select a kernel:"
|
671
|
+
),
|
708
672
|
tabs := TabbedSplit(
|
709
|
-
[
|
710
|
-
|
711
|
-
FocusedStyle(options_files),
|
712
|
-
],
|
713
|
-
titles=["New", "Existing"],
|
673
|
+
[FocusedStyle(select) for select in selects.values()],
|
674
|
+
titles=[kind.title() for kind in selects],
|
714
675
|
width=Dimension(min=30),
|
715
676
|
),
|
716
677
|
]
|
@@ -719,15 +680,9 @@ class SelectKernelDialog(Dialog):
|
|
719
680
|
def _change_kernel() -> None:
|
720
681
|
self.hide()
|
721
682
|
assert tab is not None
|
722
|
-
if tabs.active
|
723
|
-
|
724
|
-
|
725
|
-
else:
|
726
|
-
name = None
|
727
|
-
connection_file = options_files.value
|
728
|
-
tab.kernel.change(
|
729
|
-
name=name, connection_file=connection_file, cb=tab.kernel_started
|
730
|
-
)
|
683
|
+
if (index := tabs.active) is not None:
|
684
|
+
info = list(selects.values())[index].value
|
685
|
+
tab.switch_kernel(info.factory)
|
731
686
|
|
732
687
|
self.buttons = {
|
733
688
|
"Select": _change_kernel,
|
@@ -834,7 +789,36 @@ class ErrorDialog(Dialog):
|
|
834
789
|
def _copy_traceback() -> None:
|
835
790
|
self.app.clipboard.set_data(ClipboardData(tb_text))
|
836
791
|
|
837
|
-
|
792
|
+
def _report() -> None:
|
793
|
+
import webbrowser
|
794
|
+
from importlib.metadata import metadata
|
795
|
+
from urllib.parse import urlencode, urlparse, urlunparse
|
796
|
+
|
797
|
+
data = metadata("euporie")
|
798
|
+
if issue_url := dict(
|
799
|
+
x.split(", ", 1) for x in data.json["project_url"]
|
800
|
+
).get("Issues"):
|
801
|
+
parsed_url = urlparse(issue_url)
|
802
|
+
url = urlunparse(
|
803
|
+
parsed_url._replace(
|
804
|
+
path=f"{parsed_url.path.rstrip('/')}/new"
|
805
|
+
)._replace(
|
806
|
+
query=urlencode(
|
807
|
+
{
|
808
|
+
"title": f"Error: {exception!r}",
|
809
|
+
"body": "(Please describe what you did)\n\n"
|
810
|
+
f"## Traceback\n\n```python\n{tb_text}\n```\n",
|
811
|
+
}
|
812
|
+
)
|
813
|
+
)
|
814
|
+
)
|
815
|
+
webbrowser.open(url, new=2, autoraise=True)
|
816
|
+
|
817
|
+
self.buttons = {
|
818
|
+
"Report on GitHub": _report,
|
819
|
+
"Copy Traceback": _copy_traceback,
|
820
|
+
"Close": None,
|
821
|
+
}
|
838
822
|
|
839
823
|
|
840
824
|
class UnsavedDialog(Dialog):
|
euporie/core/widgets/display.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
+
import asyncio
|
5
6
|
import logging
|
6
7
|
from functools import partial
|
7
8
|
from math import ceil
|
@@ -28,7 +29,6 @@ from euporie.core.key_binding.registry import (
|
|
28
29
|
)
|
29
30
|
from euporie.core.layout.containers import VSplit, Window
|
30
31
|
from euporie.core.margins import MarginContainer, ScrollbarMargin
|
31
|
-
from euporie.core.utils import run_in_thread_with_context
|
32
32
|
|
33
33
|
if TYPE_CHECKING:
|
34
34
|
from collections.abc import Iterable
|
@@ -217,7 +217,7 @@ class DisplayControl(UIControl):
|
|
217
217
|
if not self.rendering:
|
218
218
|
self.rendering = True
|
219
219
|
if self.threaded:
|
220
|
-
|
220
|
+
get_app().create_background_task(asyncio.to_thread(_render))
|
221
221
|
else:
|
222
222
|
_render()
|
223
223
|
|
euporie/core/widgets/forms.py
CHANGED
@@ -11,17 +11,19 @@ from math import ceil, floor
|
|
11
11
|
from typing import TYPE_CHECKING, cast
|
12
12
|
from weakref import finalize
|
13
13
|
|
14
|
-
from prompt_toolkit.
|
14
|
+
from prompt_toolkit.auto_suggest import DynamicAutoSuggest
|
15
|
+
from prompt_toolkit.buffer import Buffer, ValidationState
|
15
16
|
from prompt_toolkit.cache import SimpleCache
|
16
|
-
from prompt_toolkit.completion
|
17
|
-
from prompt_toolkit.completion.word_completer import WordCompleter
|
17
|
+
from prompt_toolkit.completion import Completer, ConditionalCompleter, WordCompleter
|
18
18
|
from prompt_toolkit.data_structures import Point
|
19
|
+
from prompt_toolkit.document import Document
|
19
20
|
from prompt_toolkit.filters import (
|
20
21
|
Always,
|
21
22
|
Condition,
|
22
23
|
Filter,
|
23
24
|
FilterOrBool,
|
24
25
|
has_focus,
|
26
|
+
is_true,
|
25
27
|
to_filter,
|
26
28
|
)
|
27
29
|
from prompt_toolkit.formatted_text.base import to_formatted_text
|
@@ -39,13 +41,19 @@ from prompt_toolkit.layout.controls import (
|
|
39
41
|
UIControl,
|
40
42
|
)
|
41
43
|
from prompt_toolkit.layout.dimension import Dimension
|
42
|
-
from prompt_toolkit.layout.processors import
|
44
|
+
from prompt_toolkit.layout.processors import (
|
45
|
+
AfterInput,
|
46
|
+
BeforeInput,
|
47
|
+
ConditionalProcessor,
|
48
|
+
PasswordProcessor,
|
49
|
+
Processor,
|
50
|
+
)
|
43
51
|
from prompt_toolkit.layout.screen import WritePosition
|
44
52
|
from prompt_toolkit.layout.utils import explode_text_fragments
|
53
|
+
from prompt_toolkit.lexers import DynamicLexer, Lexer
|
45
54
|
from prompt_toolkit.mouse_events import MouseButton, MouseEvent, MouseEventType
|
46
55
|
from prompt_toolkit.utils import Event
|
47
56
|
from prompt_toolkit.validation import Validator
|
48
|
-
from prompt_toolkit.widgets.base import TextArea
|
49
57
|
|
50
58
|
from euporie.core.app.current import get_app
|
51
59
|
from euporie.core.border import InsetGrid
|
@@ -59,7 +67,8 @@ if TYPE_CHECKING:
|
|
59
67
|
from collections.abc import Sequence
|
60
68
|
from typing import Any, Callable
|
61
69
|
|
62
|
-
from prompt_toolkit.
|
70
|
+
from prompt_toolkit.auto_suggest import AutoSuggest
|
71
|
+
from prompt_toolkit.buffer import BufferAcceptHandler
|
63
72
|
from prompt_toolkit.completion.base import Completer
|
64
73
|
from prompt_toolkit.formatted_text.base import (
|
65
74
|
AnyFormattedText,
|
@@ -498,6 +507,7 @@ class ExpandingBufferControl(BufferControl):
|
|
498
507
|
focus_on_click: FilterOrBool = False,
|
499
508
|
key_bindings: KeyBindingsBase | None = None,
|
500
509
|
expand: FilterOrBool = True,
|
510
|
+
on_focus: Callable[[], None] | None = None,
|
501
511
|
) -> None:
|
502
512
|
"""Add an ``expand`` parameter to the buffer control."""
|
503
513
|
super().__init__(
|
@@ -513,6 +523,7 @@ class ExpandingBufferControl(BufferControl):
|
|
513
523
|
key_bindings=key_bindings,
|
514
524
|
)
|
515
525
|
self.expand = to_filter(expand)
|
526
|
+
self.on_focus = on_focus
|
516
527
|
|
517
528
|
def preferred_width(self, max_available_width: int) -> int | None:
|
518
529
|
"""Enure text box expands to available width.
|
@@ -528,6 +539,16 @@ class ExpandingBufferControl(BufferControl):
|
|
528
539
|
else:
|
529
540
|
return None
|
530
541
|
|
542
|
+
def mouse_handler(self, mouse_event: MouseEvent) -> NotImplementedOrNone:
|
543
|
+
"""Optionally call focus handler when focused."""
|
544
|
+
layout = get_app().layout
|
545
|
+
was_focused = layout.current_control == self
|
546
|
+
result = super().mouse_handler(mouse_event)
|
547
|
+
is_focused = layout.current_control == self
|
548
|
+
if not was_focused and is_focused and callable(self.on_focus):
|
549
|
+
self.on_focus()
|
550
|
+
return result
|
551
|
+
|
531
552
|
|
532
553
|
class Text:
|
533
554
|
"""A text input widget."""
|
@@ -554,6 +575,9 @@ class Text:
|
|
554
575
|
password: FilterOrBool = False,
|
555
576
|
wrap_lines: FilterOrBool = False,
|
556
577
|
prompt: AnyFormattedText | None = None,
|
578
|
+
on_focus: Callable[[], None] | None = None,
|
579
|
+
complete_while_typing: FilterOrBool = True,
|
580
|
+
auto_suggest: AutoSuggest | None = None,
|
557
581
|
) -> None:
|
558
582
|
"""Create a new text widget instance.
|
559
583
|
|
@@ -580,33 +604,44 @@ class Text:
|
|
580
604
|
disabled: A filter which when evaluated to :py:const:`True` causes the
|
581
605
|
widget to be disabled
|
582
606
|
password: A filter to determine if the text input is a password field
|
583
|
-
prompt: Text to display before the input
|
584
607
|
wrap_lines: Whether to wrap lines wider than the text area
|
608
|
+
prompt: Text to display before the input
|
609
|
+
on_focus: Function to call when the buffer gains focus
|
610
|
+
complete_while_typing: Whether to show completions while typing
|
611
|
+
auto_suggest: Auto-suggestion behavior for the text input
|
585
612
|
"""
|
586
613
|
self.style = style
|
587
614
|
self.options = options or []
|
588
615
|
self.disabled = to_filter(disabled)
|
589
|
-
|
590
616
|
self.placeholder = placeholder
|
617
|
+
self.wrap_lines = wrap_lines
|
618
|
+
self.complete_while_typing = complete_while_typing
|
619
|
+
self.auto_suggest = auto_suggest
|
620
|
+
self.lexer = lexer
|
591
621
|
|
592
|
-
self.
|
593
|
-
str(text),
|
622
|
+
self.buffer = Buffer(
|
623
|
+
document=Document(str(text), 0),
|
594
624
|
multiline=multiline,
|
595
|
-
height=Dimension(min=min_height, max=height, preferred=height),
|
596
|
-
width=width,
|
597
|
-
focusable=~self.disabled,
|
598
|
-
focus_on_click=~self.disabled,
|
599
625
|
read_only=self.disabled,
|
600
|
-
style=f"{style} class:text,text-area",
|
601
|
-
validator=Validator.from_callable(validation) if validation else None,
|
602
|
-
accept_handler=accept_handler,
|
603
626
|
completer=completer
|
604
627
|
or ConditionalCompleter(
|
605
628
|
WordCompleter(self.options),
|
606
629
|
filter=Condition(lambda: bool(self.options)),
|
607
630
|
),
|
608
|
-
|
631
|
+
complete_while_typing=Condition(
|
632
|
+
lambda: is_true(self.complete_while_typing)
|
633
|
+
),
|
634
|
+
validator=Validator.from_callable(validation) if validation else None,
|
635
|
+
validate_while_typing=validation is not None,
|
636
|
+
auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest),
|
637
|
+
accept_handler=accept_handler,
|
638
|
+
)
|
639
|
+
|
640
|
+
self.control = ExpandingBufferControl(
|
641
|
+
buffer=self.buffer,
|
642
|
+
lexer=DynamicLexer(lambda: self.lexer),
|
609
643
|
input_processors=[
|
644
|
+
BeforeInput(prompt, style="class:text-area.prompt"),
|
610
645
|
ConditionalProcessor(
|
611
646
|
AfterInput(
|
612
647
|
lambda: self.placeholder, style="class:text,placeholder"
|
@@ -615,58 +650,35 @@ class Text:
|
|
615
650
|
lambda: self.placeholder is not None and self.buffer.text == ""
|
616
651
|
),
|
617
652
|
),
|
653
|
+
ConditionalProcessor(
|
654
|
+
processor=PasswordProcessor(), filter=to_filter(password)
|
655
|
+
),
|
618
656
|
*(input_processors or []),
|
619
657
|
],
|
620
|
-
|
621
|
-
|
622
|
-
)
|
623
|
-
self.buffer = self.text_area.buffer
|
624
|
-
|
625
|
-
# Patch text area control's to expand to fill available width
|
626
|
-
# Do this without monkey-pathing by sub-classing :class:`BufferControl` and
|
627
|
-
# re-assigning the text-area's control
|
628
|
-
self.text_area.control = ExpandingBufferControl(
|
629
|
-
buffer=self.text_area.control.buffer,
|
630
|
-
input_processors=self.text_area.control.input_processors,
|
631
|
-
include_default_input_processors=(
|
632
|
-
self.text_area.control.include_default_input_processors
|
633
|
-
),
|
634
|
-
lexer=self.text_area.control.lexer,
|
635
|
-
preview_search=self.text_area.control.preview_search,
|
636
|
-
focusable=self.text_area.control.focusable,
|
637
|
-
search_buffer_control=self.text_area.control._search_buffer_control,
|
638
|
-
menu_position=self.text_area.control.menu_position,
|
639
|
-
focus_on_click=self.text_area.control.focus_on_click,
|
640
|
-
key_bindings=self.text_area.control.key_bindings,
|
658
|
+
focusable=~self.disabled,
|
659
|
+
focus_on_click=~self.disabled,
|
641
660
|
expand=expand,
|
661
|
+
on_focus=on_focus,
|
642
662
|
)
|
643
|
-
|
644
|
-
self.
|
645
|
-
height=
|
646
|
-
width=
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
style=window.style,
|
651
|
-
wrap_lines=window.wrap_lines,
|
652
|
-
left_margins=window.left_margins,
|
653
|
-
right_margins=window.right_margins,
|
654
|
-
get_line_prefix=window.get_line_prefix,
|
663
|
+
|
664
|
+
self.window = Window(
|
665
|
+
height=Dimension(min=min_height, max=height, preferred=height),
|
666
|
+
width=width,
|
667
|
+
content=self.control,
|
668
|
+
wrap_lines=Condition(lambda: is_true(self.wrap_lines)),
|
669
|
+
style=f"{style} class:text,text-area",
|
655
670
|
)
|
656
|
-
self.text_area.window.content = self.text_area.control
|
657
671
|
|
658
672
|
if on_text_changed:
|
659
|
-
self.
|
673
|
+
self.buffer.on_text_changed += on_text_changed
|
660
674
|
if validation:
|
661
|
-
self.
|
675
|
+
self.buffer.validate_while_typing = Always()
|
662
676
|
self.container = Border(
|
663
677
|
VSplit(
|
664
678
|
[
|
665
|
-
self.
|
679
|
+
self.window,
|
666
680
|
ConditionalContainer(
|
667
|
-
MarginContainer(
|
668
|
-
ScrollbarMargin(), target=self.text_area.window
|
669
|
-
),
|
681
|
+
MarginContainer(ScrollbarMargin(), target=self.window),
|
670
682
|
filter=to_filter(multiline),
|
671
683
|
),
|
672
684
|
]
|
@@ -678,7 +690,7 @@ class Text:
|
|
678
690
|
|
679
691
|
def border_style(self) -> str:
|
680
692
|
"""Calculate the style to apply to the widget's border."""
|
681
|
-
if self.
|
693
|
+
if self.buffer.validation_state == ValidationState.INVALID:
|
682
694
|
return f"{self.style} class:text,border,invalid"
|
683
695
|
else:
|
684
696
|
return f"{self.style} class:text,border"
|
euporie/core/widgets/inputs.py
CHANGED
@@ -250,7 +250,10 @@ class KernelInput(TextArea):
|
|
250
250
|
buffer=self.buffer,
|
251
251
|
lexer=DynamicLexer(
|
252
252
|
lambda: _get_lexer(
|
253
|
-
|
253
|
+
# Only lex buffers with text
|
254
|
+
self.buffer.text and app.config.syntax_highlighting,
|
255
|
+
self.lexer,
|
256
|
+
self.language,
|
254
257
|
)
|
255
258
|
),
|
256
259
|
input_processors=[
|
@@ -506,7 +509,7 @@ class StdInput:
|
|
506
509
|
password=Condition(lambda: self.password),
|
507
510
|
style="class:input",
|
508
511
|
)
|
509
|
-
self.window = text.
|
512
|
+
self.window = text.window
|
510
513
|
self.container = ConditionalContainer(
|
511
514
|
LabelledWidget(
|
512
515
|
body=text,
|
@@ -517,8 +520,8 @@ class StdInput:
|
|
517
520
|
|
518
521
|
def accept(self, buffer: Buffer) -> bool:
|
519
522
|
"""Send the input to the kernel and hide the input box."""
|
520
|
-
if self.kernel_tab.kernel
|
521
|
-
self.kernel_tab.kernel.
|
523
|
+
if self.kernel_tab.kernel is not None:
|
524
|
+
self.kernel_tab.kernel.input(buffer.text)
|
522
525
|
# Cleanup
|
523
526
|
self.active = False
|
524
527
|
|