euporie 2.8.6__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/app/_commands.py +4 -21
- euporie/core/app/app.py +13 -7
- euporie/core/bars/command.py +9 -6
- 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 -8
- euporie/core/completion.py +14 -6
- euporie/core/convert/datum.py +7 -7
- euporie/core/data_structures.py +20 -1
- euporie/core/filters.py +8 -0
- euporie/core/ft/html.py +47 -40
- euporie/core/graphics.py +11 -3
- 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 +45 -148
- euporie/core/log.py +1 -1
- euporie/core/style.py +2 -1
- euporie/core/suggest.py +155 -74
- euporie/core/tabs/__init__.py +10 -0
- euporie/core/tabs/_commands.py +76 -0
- euporie/core/tabs/_settings.py +16 -0
- euporie/core/tabs/base.py +22 -8
- euporie/core/tabs/kernel.py +81 -35
- euporie/core/tabs/notebook.py +14 -22
- euporie/core/utils.py +1 -1
- euporie/core/validation.py +8 -8
- euporie/core/widgets/_settings.py +19 -2
- euporie/core/widgets/cell.py +31 -31
- euporie/core/widgets/cell_outputs.py +10 -1
- euporie/core/widgets/dialog.py +30 -75
- 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 +55 -15
- euporie/core/widgets/palette.py +3 -1
- euporie/core/widgets/tree.py +86 -76
- euporie/notebook/app.py +35 -16
- euporie/notebook/tabs/edit.py +4 -4
- euporie/notebook/tabs/json.py +6 -2
- euporie/notebook/tabs/notebook.py +26 -8
- euporie/preview/tabs/notebook.py +17 -13
- euporie/web/tabs/web.py +22 -3
- euporie/web/widgets/webview.py +3 -0
- {euporie-2.8.6.dist-info → euporie-2.8.7.dist-info}/METADATA +1 -1
- {euporie-2.8.6.dist-info → euporie-2.8.7.dist-info}/RECORD +64 -61
- {euporie-2.8.6.dist-info → euporie-2.8.7.dist-info}/entry_points.txt +1 -1
- {euporie-2.8.6.dist-info → euporie-2.8.7.dist-info}/licenses/LICENSE +1 -1
- {euporie-2.8.6.data → euporie-2.8.7.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.8.6.data → euporie-2.8.7.data}/data/share/applications/euporie-notebook.desktop +0 -0
- {euporie-2.8.6.dist-info → euporie-2.8.7.dist-info}/WHEEL +0 -0
euporie/core/widgets/cell.py
CHANGED
@@ -6,7 +6,7 @@ import asyncio
|
|
6
6
|
import logging
|
7
7
|
import os
|
8
8
|
import weakref
|
9
|
-
from functools import partial
|
9
|
+
from functools import lru_cache, partial
|
10
10
|
from pathlib import Path
|
11
11
|
from typing import TYPE_CHECKING, cast
|
12
12
|
from weakref import WeakKeyDictionary
|
@@ -50,6 +50,7 @@ if TYPE_CHECKING:
|
|
50
50
|
from prompt_toolkit.completion.base import Completer
|
51
51
|
from prompt_toolkit.formatted_text.base import StyleAndTextTuples
|
52
52
|
|
53
|
+
from euporie.core.border import GridStyle
|
53
54
|
from euporie.core.format import Formatter
|
54
55
|
from euporie.core.inspection import Inspector
|
55
56
|
from euporie.core.lsp import LspClient
|
@@ -79,6 +80,18 @@ def get_cell_id(cell_json: dict) -> str:
|
|
79
80
|
return cell_id
|
80
81
|
|
81
82
|
|
83
|
+
@lru_cache(maxsize=32)
|
84
|
+
def _get_border_style(
|
85
|
+
selected: bool, focused: bool, show_borders: bool, multi_selected: bool
|
86
|
+
) -> GridStyle:
|
87
|
+
"""Get the border style grid based on cell state."""
|
88
|
+
if not (show_borders or selected):
|
89
|
+
return NoLine.grid
|
90
|
+
if focused and multi_selected:
|
91
|
+
return ThickLine.outer
|
92
|
+
return ThinLine.outer
|
93
|
+
|
94
|
+
|
82
95
|
class Cell:
|
83
96
|
"""A kernel_tab cell element.
|
84
97
|
|
@@ -86,8 +99,6 @@ class Cell:
|
|
86
99
|
focused.
|
87
100
|
"""
|
88
101
|
|
89
|
-
input_box: KernelInput
|
90
|
-
|
91
102
|
def __init__(
|
92
103
|
self, index: int, json: dict, kernel_tab: BaseNotebook, is_new: bool = False
|
93
104
|
) -> None:
|
@@ -158,6 +169,14 @@ class Cell:
|
|
158
169
|
"""Update cell json when the input buffer has been edited."""
|
159
170
|
weak_self._set_input(buf.text)
|
160
171
|
weak_self.kernel_tab.dirty = True
|
172
|
+
weak_self.on_change()
|
173
|
+
# Re-render markdown cells when edited outside of edit mode
|
174
|
+
if (
|
175
|
+
weak_self.cell_type == "markdown"
|
176
|
+
and not weak_self.kernel_tab.in_edit_mode()
|
177
|
+
):
|
178
|
+
weak_self.output_area.json = weak_self.output_json
|
179
|
+
weak_self.refresh()
|
161
180
|
|
162
181
|
def on_cursor_position_changed(buf: Buffer) -> None:
|
163
182
|
"""Respond to cursor movements."""
|
@@ -199,42 +218,22 @@ class Cell:
|
|
199
218
|
)
|
200
219
|
self.input_box.buffer.name = self.cell_type
|
201
220
|
|
202
|
-
self.input_box.buffer.on_text_changed += lambda buf: weak_self.on_change()
|
203
|
-
|
204
221
|
def border_char(name: str) -> Callable[..., str]:
|
205
222
|
"""Return a function which returns the cell border character to display."""
|
206
223
|
|
207
224
|
def _inner() -> str:
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
grid = ThinLine.outer
|
225
|
+
if not weak_self:
|
226
|
+
return " "
|
227
|
+
grid = _get_border_style(
|
228
|
+
weak_self.selected,
|
229
|
+
weak_self.focused,
|
230
|
+
weak_self.kernel_tab.app.config.show_cell_borders,
|
231
|
+
multiple_cells_selected(),
|
232
|
+
)
|
217
233
|
return getattr(grid, name.upper())
|
218
234
|
|
219
235
|
return _inner
|
220
236
|
|
221
|
-
# @lru_cache(maxsize=None)
|
222
|
-
# def _cell_border_char(
|
223
|
-
# name: str,
|
224
|
-
# show_cell_borders: bool,
|
225
|
-
# focused: bool,
|
226
|
-
# selected: bool,
|
227
|
-
# multiple_cells_selected: bool,
|
228
|
-
# ) -> str:
|
229
|
-
# if show_cell_borders or selected:
|
230
|
-
# if focused and multiple_cells_selected:
|
231
|
-
# grid = ThickLine.outer
|
232
|
-
# else:
|
233
|
-
# grid = ThinLine.outer
|
234
|
-
# else:
|
235
|
-
# grid = NoLine.grid
|
236
|
-
# return getattr(grid, name.upper())
|
237
|
-
|
238
237
|
self.control = Window(
|
239
238
|
FormattedTextControl(
|
240
239
|
border_char("TOP_LEFT"),
|
@@ -764,6 +763,7 @@ class Cell:
|
|
764
763
|
def set_execution_count(self, n: int) -> None:
|
765
764
|
"""Set the execution count of the cell."""
|
766
765
|
self.json["execution_count"] = n
|
766
|
+
self.refresh()
|
767
767
|
|
768
768
|
def add_output(self, output_json: dict[str, Any], own: bool) -> None:
|
769
769
|
"""Add a new output to the cell."""
|
@@ -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_can_save
|
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_can_save,
|
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,
|
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
|
|