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
@@ -5,22 +5,26 @@ from __future__ import annotations
|
|
5
5
|
import logging
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
|
+
from prompt_toolkit.buffer import Buffer
|
9
|
+
from prompt_toolkit.document import Document
|
8
10
|
from prompt_toolkit.filters.app import is_searching
|
11
|
+
from prompt_toolkit.filters.base import Condition
|
12
|
+
from prompt_toolkit.formatted_text.base import to_formatted_text
|
9
13
|
from prompt_toolkit.key_binding.vi_state import InputMode
|
10
14
|
from prompt_toolkit.layout.controls import BufferControl, SearchBufferControl
|
11
15
|
from prompt_toolkit.search import SearchDirection
|
12
16
|
from prompt_toolkit.selection import SelectionState
|
13
17
|
from prompt_toolkit.widgets import SearchToolbar as PtkSearchToolbar
|
14
18
|
|
19
|
+
from euporie.core.app.current import get_app
|
20
|
+
from euporie.core.bars import SEARCH_BAR_BUFFER
|
15
21
|
from euporie.core.commands import add_cmd
|
16
|
-
from euporie.core.current import get_app
|
17
22
|
from euporie.core.key_binding.registry import (
|
18
23
|
load_registered_bindings,
|
19
24
|
register_bindings,
|
20
25
|
)
|
21
26
|
|
22
27
|
if TYPE_CHECKING:
|
23
|
-
from prompt_toolkit.buffer import Buffer
|
24
28
|
from prompt_toolkit.filters import FilterOrBool
|
25
29
|
from prompt_toolkit.formatted_text.base import AnyFormattedText
|
26
30
|
|
@@ -38,34 +42,41 @@ class SearchBar(PtkSearchToolbar):
|
|
38
42
|
search_buffer: Buffer | None = None,
|
39
43
|
vi_mode: bool = False,
|
40
44
|
text_if_not_searching: AnyFormattedText = "",
|
41
|
-
forward_search_prompt: AnyFormattedText = "
|
42
|
-
backward_search_prompt: AnyFormattedText = "
|
45
|
+
forward_search_prompt: AnyFormattedText = " Find: ",
|
46
|
+
backward_search_prompt: AnyFormattedText = " Find (up): ",
|
43
47
|
ignore_case: FilterOrBool = False,
|
44
48
|
) -> None:
|
45
49
|
"""Create a new search bar instance."""
|
50
|
+
if search_buffer is None:
|
51
|
+
search_buffer = Buffer(name=SEARCH_BAR_BUFFER)
|
46
52
|
super().__init__(
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
search_buffer=search_buffer,
|
54
|
+
vi_mode=vi_mode,
|
55
|
+
text_if_not_searching=text_if_not_searching,
|
56
|
+
forward_search_prompt=to_formatted_text(
|
57
|
+
forward_search_prompt, "class:status-field"
|
58
|
+
),
|
59
|
+
backward_search_prompt=to_formatted_text(
|
60
|
+
backward_search_prompt, "class:status-field"
|
61
|
+
),
|
56
62
|
)
|
57
63
|
self.control.key_bindings = load_registered_bindings(
|
58
|
-
"euporie.core.
|
64
|
+
"euporie.core.bars.search.SearchBar",
|
65
|
+
config=get_app().config,
|
66
|
+
)
|
67
|
+
search_state = self.control.searcher_search_state
|
68
|
+
search_state.ignore_case = Condition(
|
69
|
+
lambda: self.search_buffer.text.islower() or search_state.text.islower()
|
59
70
|
)
|
60
71
|
|
61
72
|
register_bindings(
|
62
73
|
{
|
63
|
-
"euporie.core.app.BaseApp": {
|
74
|
+
"euporie.core.app.app:BaseApp": {
|
64
75
|
"find": ["c-f", "f3", "f7"],
|
65
76
|
"find-next": "c-g",
|
66
77
|
"find-previous": "c-p",
|
67
78
|
},
|
68
|
-
"euporie.core.
|
79
|
+
"euporie.core.bars.search.SearchBar": {
|
69
80
|
"accept-search": "enter",
|
70
81
|
"stop-search": "escape",
|
71
82
|
},
|
@@ -73,28 +84,44 @@ class SearchBar(PtkSearchToolbar):
|
|
73
84
|
)
|
74
85
|
|
75
86
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
87
|
+
def find_search_control() -> tuple[SearchBufferControl | None, BufferControl | None]:
|
88
|
+
"""Find the current search buffer and buffer control."""
|
89
|
+
current_buffer_control: BufferControl | None = None
|
90
|
+
search_buffer_control: SearchBufferControl | None = None
|
91
|
+
|
81
92
|
app = get_app()
|
82
93
|
layout = app.layout
|
83
|
-
current_control = layout.current_control
|
84
|
-
|
85
|
-
if
|
94
|
+
current_control = app.layout.current_control
|
95
|
+
|
96
|
+
if isinstance(current_control, SearchBufferControl):
|
97
|
+
search_buffer_control = current_control
|
98
|
+
|
99
|
+
if search_buffer_control is None and app.search_bar is not None:
|
86
100
|
search_buffer_control = app.search_bar.control
|
87
|
-
|
88
|
-
|
89
|
-
|
101
|
+
|
102
|
+
if search_buffer_control is not None and current_buffer_control is None:
|
103
|
+
current_buffer_control = layout.search_links.get(search_buffer_control)
|
104
|
+
|
105
|
+
if current_buffer_control is None and isinstance(current_control, BufferControl):
|
106
|
+
current_buffer_control = current_control
|
107
|
+
|
108
|
+
if (
|
109
|
+
search_buffer_control is None
|
110
|
+
and current_buffer_control is not None
|
111
|
+
and current_buffer_control.search_buffer_control is not None
|
90
112
|
):
|
91
|
-
search_buffer_control =
|
92
|
-
|
93
|
-
|
94
|
-
|
113
|
+
search_buffer_control = current_buffer_control.search_buffer_control
|
114
|
+
|
115
|
+
return search_buffer_control, current_buffer_control
|
116
|
+
|
117
|
+
|
118
|
+
def find_searchable_controls(
|
119
|
+
search_buffer_control: SearchBufferControl, current_control: BufferControl | None
|
120
|
+
) -> list[BufferControl]:
|
121
|
+
"""Find list of searchable controls and the index of the next control."""
|
95
122
|
searchable_controls: list[BufferControl] = []
|
96
123
|
next_control_index = 0
|
97
|
-
for control in layout.find_all_controls():
|
124
|
+
for control in get_app().layout.find_all_controls():
|
98
125
|
# Find the index of the next searchable control so we can link the search
|
99
126
|
# control to it if the currently focused control is not searchable. This is so
|
100
127
|
# that the next searchable control can be focused when search is completed.
|
@@ -105,24 +132,40 @@ def start_global_search(
|
|
105
132
|
isinstance(control, BufferControl)
|
106
133
|
and control.search_buffer_control == search_buffer_control
|
107
134
|
):
|
108
|
-
# Set its search direction
|
109
|
-
control.search_state.direction = direction
|
110
135
|
# Add it to our list
|
111
136
|
searchable_controls.append(control)
|
137
|
+
searchable_controls = (
|
138
|
+
searchable_controls[next_control_index:]
|
139
|
+
+ searchable_controls[:next_control_index]
|
140
|
+
)
|
141
|
+
return searchable_controls
|
142
|
+
|
143
|
+
|
144
|
+
def start_global_search(
|
145
|
+
buffer_control: BufferControl | None = None,
|
146
|
+
direction: SearchDirection = SearchDirection.FORWARD,
|
147
|
+
) -> None:
|
148
|
+
"""Start a search through all searchable `buffer_controls` in the layout."""
|
149
|
+
search_buffer_control, current_control = find_search_control()
|
150
|
+
if search_buffer_control is None:
|
151
|
+
return
|
152
|
+
searchable_controls = find_searchable_controls(
|
153
|
+
search_buffer_control, current_control
|
154
|
+
)
|
112
155
|
|
113
156
|
# Stop the search if we did not find any searchable controls
|
114
157
|
if not searchable_controls:
|
115
158
|
return
|
116
159
|
|
117
160
|
# If the current control is searchable, link it
|
161
|
+
app = get_app()
|
162
|
+
layout = app.layout
|
118
163
|
if current_control in searchable_controls:
|
119
164
|
assert isinstance(current_control, BufferControl)
|
120
165
|
layout.search_links[search_buffer_control] = current_control
|
121
166
|
else:
|
122
167
|
# otherwise use the next after the currently selected control
|
123
|
-
layout.search_links[search_buffer_control] = searchable_controls[
|
124
|
-
next_control_index % len(searchable_controls)
|
125
|
-
]
|
168
|
+
layout.search_links[search_buffer_control] = searchable_controls[0]
|
126
169
|
# Make sure to focus the search BufferControl
|
127
170
|
layout.focus(search_buffer_control)
|
128
171
|
# If we're in Vi mode, make sure to go into insert mode.
|
@@ -137,31 +180,85 @@ def find() -> None:
|
|
137
180
|
|
138
181
|
def find_prev_next(direction: SearchDirection) -> None:
|
139
182
|
"""Find the previous or next search match."""
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
search_buffer_control
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
183
|
+
if is_searching():
|
184
|
+
accept_search()
|
185
|
+
|
186
|
+
search_buffer_control, current_control = find_search_control()
|
187
|
+
if search_buffer_control is None:
|
188
|
+
return
|
189
|
+
searchable_controls = find_searchable_controls(
|
190
|
+
search_buffer_control, current_control
|
191
|
+
)
|
192
|
+
|
193
|
+
if direction == SearchDirection.BACKWARD:
|
194
|
+
searchable_controls = searchable_controls[:1] + searchable_controls[1:][::-1]
|
195
|
+
|
196
|
+
# Search over all searchable buffers
|
197
|
+
for i, control in enumerate(searchable_controls):
|
154
198
|
# Update search_state.
|
155
199
|
search_state = control.search_state
|
156
200
|
search_state.direction = direction
|
157
201
|
# Apply search to buffer
|
158
202
|
buffer = control.buffer
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
)
|
164
|
-
|
203
|
+
|
204
|
+
search_result: tuple[int, int] | None = None
|
205
|
+
|
206
|
+
# If we are searching history, use the PTK buffer search implementation
|
207
|
+
if buffer.enable_history_search():
|
208
|
+
search_result = buffer._search(search_state)
|
209
|
+
|
210
|
+
# Otherwise, only search the buffer's current "working line"
|
211
|
+
else:
|
212
|
+
document = buffer.document
|
213
|
+
# If have move to the next buffer, set the cursor position for the start of
|
214
|
+
# the search to the start or the end of the text, depending on if we are
|
215
|
+
# searching forwards or backwards
|
216
|
+
if i > 0:
|
217
|
+
if direction == SearchDirection.FORWARD:
|
218
|
+
document = Document(document.text, 0)
|
219
|
+
else:
|
220
|
+
document = Document(document.text, len(document.text))
|
221
|
+
|
222
|
+
text = search_state.text
|
223
|
+
ignore_case = search_state.ignore_case()
|
224
|
+
|
225
|
+
if direction == SearchDirection.FORWARD:
|
226
|
+
# Try find at the current input.
|
227
|
+
new_index = document.find(
|
228
|
+
text,
|
229
|
+
# If we have moved to the next buffer, include the current position
|
230
|
+
# which will be the start of the document text
|
231
|
+
include_current_position=i > 0,
|
232
|
+
ignore_case=ignore_case,
|
233
|
+
)
|
234
|
+
if new_index is not None:
|
235
|
+
search_result = (
|
236
|
+
buffer.working_index,
|
237
|
+
document.cursor_position + new_index,
|
238
|
+
)
|
239
|
+
else:
|
240
|
+
# Try find at the current input.
|
241
|
+
new_index = document.find_backwards(text, ignore_case=ignore_case)
|
242
|
+
if new_index is not None:
|
243
|
+
search_result = (
|
244
|
+
buffer.working_index,
|
245
|
+
document.cursor_position + new_index,
|
246
|
+
)
|
247
|
+
|
248
|
+
if search_result is not None:
|
249
|
+
working_index, cursor_position = search_result
|
250
|
+
buffer.working_index = working_index
|
251
|
+
buffer.cursor_position = cursor_position
|
252
|
+
# Set SelectionState
|
253
|
+
buffer.selection_state = SelectionState(
|
254
|
+
buffer.cursor_position + len(search_state.text)
|
255
|
+
)
|
256
|
+
buffer.selection_state.enter_shift_mode()
|
257
|
+
|
258
|
+
# Trigger a cursor position changed event on this buffer
|
259
|
+
buffer._cursor_position_changed()
|
260
|
+
|
261
|
+
break
|
165
262
|
|
166
263
|
|
167
264
|
@add_cmd()
|
@@ -16,13 +16,13 @@ from prompt_toolkit.layout.containers import (
|
|
16
16
|
)
|
17
17
|
from prompt_toolkit.layout.controls import FormattedTextControl
|
18
18
|
|
19
|
-
from euporie.core.
|
20
|
-
from euporie.core.
|
21
|
-
from euporie.core.filters import is_searching
|
19
|
+
from euporie.core.app.current import get_app
|
20
|
+
from euporie.core.filters import has_toolbar
|
22
21
|
from euporie.core.layout.containers import VSplit, Window
|
23
22
|
|
24
23
|
if TYPE_CHECKING:
|
25
|
-
from
|
24
|
+
from collections.abc import Sequence
|
25
|
+
from typing import Callable
|
26
26
|
|
27
27
|
from prompt_toolkit.filters.base import FilterOrBool
|
28
28
|
from prompt_toolkit.formatted_text import StyleAndTextTuples
|
@@ -77,9 +77,9 @@ class StatusBar:
|
|
77
77
|
) -> None:
|
78
78
|
"""Create a new status bar instance."""
|
79
79
|
self.default: StatusBarFields = default or ([], [])
|
80
|
-
self._status_cache: FastDictCache[
|
81
|
-
|
82
|
-
|
80
|
+
self._status_cache: FastDictCache[tuple[int], list[StyleAndTextTuples]] = (
|
81
|
+
FastDictCache(self._status, size=1)
|
82
|
+
)
|
83
83
|
self.container = ConditionalContainer(
|
84
84
|
content=VSplit(
|
85
85
|
[
|
@@ -99,8 +99,8 @@ class StatusBar:
|
|
99
99
|
],
|
100
100
|
height=1,
|
101
101
|
),
|
102
|
-
filter=get_app().config.
|
103
|
-
& ~
|
102
|
+
filter=get_app().config.filters.show_status_bar
|
103
|
+
& ~has_toolbar
|
104
104
|
& to_filter(extra_filter),
|
105
105
|
)
|
106
106
|
|
@@ -144,20 +144,3 @@ class StatusBar:
|
|
144
144
|
def __pt_container__(self) -> AnyContainer:
|
145
145
|
"""Return the widget's container."""
|
146
146
|
return self.container
|
147
|
-
|
148
|
-
# ################################### Settings ####################################
|
149
|
-
|
150
|
-
add_setting(
|
151
|
-
name="show_status_bar",
|
152
|
-
flags=["--show-status-bar"],
|
153
|
-
type_=bool,
|
154
|
-
title="status bar",
|
155
|
-
help_="Show the status bar",
|
156
|
-
default=True,
|
157
|
-
schema={
|
158
|
-
"type": "boolean",
|
159
|
-
},
|
160
|
-
description="""
|
161
|
-
Whether the status bar should be shown at the bottom of the screen.
|
162
|
-
""",
|
163
|
-
)
|
euporie/core/clipboard.py
CHANGED
@@ -3,21 +3,15 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
import logging
|
6
|
-
from typing import TYPE_CHECKING
|
7
6
|
|
8
7
|
from prompt_toolkit.clipboard.base import Clipboard, ClipboardData
|
9
8
|
from prompt_toolkit.clipboard.in_memory import InMemoryClipboard
|
10
9
|
from prompt_toolkit.clipboard.pyperclip import PyperclipClipboard
|
11
10
|
from prompt_toolkit.selection import SelectionType
|
12
|
-
from pyperclip import determine_clipboard
|
13
11
|
|
14
|
-
from euporie.core.
|
12
|
+
from euporie.core.app.current import get_app
|
15
13
|
from euporie.core.io import Vt100_Output
|
16
|
-
|
17
|
-
if TYPE_CHECKING:
|
18
|
-
from euporie.core.app import BaseApp
|
19
|
-
from euporie.core.config import Setting
|
20
|
-
from euporie.core.terminal import TerminalQuery
|
14
|
+
from euporie.core.key_binding.key_processor import KeyProcessor
|
21
15
|
|
22
16
|
log = logging.getLogger(__name__)
|
23
17
|
|
@@ -25,16 +19,13 @@ log = logging.getLogger(__name__)
|
|
25
19
|
class Osc52Clipboard(Clipboard):
|
26
20
|
"""Clipboard that syncs with the system clipboard using OSC52 escape codes."""
|
27
21
|
|
28
|
-
def __init__(self
|
22
|
+
def __init__(self) -> None:
|
29
23
|
"""Create a new instance of the clipboard."""
|
30
|
-
self.
|
31
|
-
term_clipboard_data = app.term_info.clipboard_data
|
32
|
-
term_clipboard_data.event += self._update_clipboard
|
33
|
-
self._data = ClipboardData(text=term_clipboard_data.value)
|
24
|
+
self._data = ClipboardData()
|
34
25
|
|
35
26
|
def set_data(self, data: ClipboardData) -> None:
|
36
27
|
"""Set clipboard data."""
|
37
|
-
output =
|
28
|
+
output = get_app().output
|
38
29
|
if isinstance(output, Vt100_Output):
|
39
30
|
output.set_clipboard(data.text)
|
40
31
|
output.flush()
|
@@ -43,16 +34,21 @@ class Osc52Clipboard(Clipboard):
|
|
43
34
|
def get_data(self) -> ClipboardData:
|
44
35
|
"""Retrieve clipboard data."""
|
45
36
|
# Send clipboard query
|
46
|
-
|
37
|
+
app = get_app()
|
38
|
+
output = app.output
|
39
|
+
# Request clipboard contents from terminal
|
47
40
|
if isinstance(output, Vt100_Output):
|
41
|
+
from euporie.core.keys import MoreKeys
|
42
|
+
|
48
43
|
output.get_clipboard()
|
49
44
|
output.flush()
|
50
|
-
|
45
|
+
# Wait for terminal response
|
46
|
+
if isinstance(app.key_processor, KeyProcessor):
|
47
|
+
app.key_processor.await_key(MoreKeys.ClipboardDataResponse)
|
51
48
|
return self._data
|
52
49
|
|
53
|
-
def
|
50
|
+
def sync(self, text: str) -> None:
|
54
51
|
"""Update the last known clipboard data."""
|
55
|
-
text = query.value
|
56
52
|
if text != self._data.text:
|
57
53
|
self._data = ClipboardData(
|
58
54
|
text=text,
|
@@ -60,65 +56,8 @@ class Osc52Clipboard(Clipboard):
|
|
60
56
|
)
|
61
57
|
|
62
58
|
|
63
|
-
|
64
|
-
""
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
def __init__(self, app: BaseApp) -> None:
|
69
|
-
"""Create a new clipboard instance."""
|
70
|
-
self.app = app
|
71
|
-
clipboard_config = app.config.settings["clipboard"]
|
72
|
-
self.get_clipboard(clipboard_config)
|
73
|
-
clipboard_config.event += self.get_clipboard
|
74
|
-
|
75
|
-
def get_clipboard(self, setting: Setting) -> None:
|
76
|
-
"""Determine which clipboard to use."""
|
77
|
-
clipboard: Clipboard | None = None
|
78
|
-
if setting.value == "external" and determine_clipboard()[0]:
|
79
|
-
log.debug("Using pyperclip clipboard")
|
80
|
-
clipboard = PyperclipClipboard()
|
81
|
-
if not clipboard:
|
82
|
-
if setting.value == "terminal":
|
83
|
-
log.debug("Using terminal clipboard")
|
84
|
-
clipboard = Osc52Clipboard(self.app)
|
85
|
-
else:
|
86
|
-
log.debug("Using in-memory clipboard")
|
87
|
-
clipboard = InMemoryClipboard()
|
88
|
-
self._clipboard = clipboard
|
89
|
-
|
90
|
-
def set_data(self, data: ClipboardData) -> None:
|
91
|
-
"""Set data to the clipboard."""
|
92
|
-
self._clipboard.set_data(data)
|
93
|
-
|
94
|
-
def set_text(self, text: str) -> None:
|
95
|
-
"""Shortcut for setting plain text on clipboard."""
|
96
|
-
self._clipboard.set_text(text)
|
97
|
-
|
98
|
-
def rotate(self) -> None:
|
99
|
-
"""For Emacs mode, rotate the kill ring."""
|
100
|
-
self._clipboard.rotate()
|
101
|
-
|
102
|
-
def get_data(self) -> ClipboardData:
|
103
|
-
"""Return clipboard data."""
|
104
|
-
return self._clipboard.get_data()
|
105
|
-
|
106
|
-
# ################################### Settings ####################################w
|
107
|
-
|
108
|
-
add_setting(
|
109
|
-
name="clipboard",
|
110
|
-
flags=["--clipboard"],
|
111
|
-
choices=["external", "internal", "terminal"],
|
112
|
-
type_=str,
|
113
|
-
default="external",
|
114
|
-
help_="The preferred clipboard access method",
|
115
|
-
description="""
|
116
|
-
The clipboard access method to use.
|
117
|
-
- ``external``: Data is saved to the system clipboard using OS native tooling.
|
118
|
-
- ``internal``: Clipboard data is only stored and usable inside euporie - it is
|
119
|
-
not saved to the system clipboard.
|
120
|
-
- ``terminal``: uses OSC52 escape sequences to retrieve and set the clipboard
|
121
|
-
contents. Requires your terminal emulator to support OSC52. Works over SSH.
|
122
|
-
|
123
|
-
""",
|
124
|
-
)
|
59
|
+
CONFIGURED_CLIPBOARDS = {
|
60
|
+
"internal": InMemoryClipboard,
|
61
|
+
"external": PyperclipClipboard,
|
62
|
+
"terminal": Osc52Clipboard,
|
63
|
+
}
|
euporie/core/comm/base.py
CHANGED
@@ -7,17 +7,16 @@ from abc import ABCMeta, abstractmethod
|
|
7
7
|
from typing import TYPE_CHECKING
|
8
8
|
from weakref import WeakKeyDictionary
|
9
9
|
|
10
|
-
from euporie.core.
|
11
|
-
from euporie.core.current import get_app
|
12
|
-
from euporie.core.widgets.display import Display
|
10
|
+
from euporie.core.app.current import get_app
|
13
11
|
|
14
12
|
if TYPE_CHECKING:
|
15
|
-
from
|
13
|
+
from collections.abc import Mapping, Sequence
|
14
|
+
from typing import Any, Callable
|
16
15
|
|
17
16
|
from prompt_toolkit.layout.containers import AnyContainer
|
18
17
|
|
19
|
-
from euporie.core.kernel import Kernel
|
20
|
-
from euporie.core.tabs.
|
18
|
+
from euporie.core.kernel.client import Kernel
|
19
|
+
from euporie.core.tabs.kernel import KernelTab
|
21
20
|
from euporie.core.widgets.cell_outputs import OutputParent
|
22
21
|
|
23
22
|
|
@@ -97,6 +96,9 @@ class Comm(metaclass=ABCMeta):
|
|
97
96
|
|
98
97
|
def create_view(self, parent: OutputParent) -> CommView:
|
99
98
|
"""Create a new :class:`CommView` for this Comm."""
|
99
|
+
from euporie.core.convert.datum import Datum
|
100
|
+
from euporie.core.widgets.display import Display
|
101
|
+
|
100
102
|
return CommView(Display(Datum("[Object cannot be rendered]", format="ansi")))
|
101
103
|
|
102
104
|
def new_view(self, parent: OutputParent) -> CommView:
|
euporie/core/comm/ipywidgets.py
CHANGED
@@ -18,12 +18,9 @@ from prompt_toolkit.layout.containers import HSplit, VSplit
|
|
18
18
|
from prompt_toolkit.layout.processors import BeforeInput
|
19
19
|
|
20
20
|
from euporie.core.comm.base import Comm, CommView
|
21
|
-
from euporie.core.convert.datum import Datum
|
22
21
|
from euporie.core.data_structures import DiBool
|
23
|
-
from euporie.core.kernel import MsgCallbacks
|
22
|
+
from euporie.core.kernel.client import MsgCallbacks
|
24
23
|
from euporie.core.layout.decor import FocusedStyle
|
25
|
-
from euporie.core.widgets.cell_outputs import CellOutputArea
|
26
|
-
from euporie.core.widgets.display import Display
|
27
24
|
from euporie.core.widgets.forms import (
|
28
25
|
Button,
|
29
26
|
Checkbox,
|
@@ -46,13 +43,14 @@ from euporie.core.widgets.layout import (
|
|
46
43
|
)
|
47
44
|
|
48
45
|
if TYPE_CHECKING:
|
49
|
-
from
|
46
|
+
from collections.abc import Iterable, MutableSequence, Sequence
|
47
|
+
from typing import Any
|
50
48
|
|
51
49
|
from prompt_toolkit.buffer import Buffer
|
52
50
|
from prompt_toolkit.formatted_text.base import AnyFormattedText
|
53
51
|
from prompt_toolkit.layout.containers import AnyContainer, _Split
|
54
52
|
|
55
|
-
from euporie.core.
|
53
|
+
from euporie.core.tabs.kernel import KernelTab
|
56
54
|
from euporie.core.widgets.cell_outputs import OutputParent
|
57
55
|
from euporie.core.widgets.forms import SelectableWidget, ToggleableWidget
|
58
56
|
from euporie.core.widgets.layout import StackedSplit
|
@@ -122,7 +120,7 @@ def _separate_buffers(
|
|
122
120
|
cloned_substrate = dict(substate) # clone list/tuple
|
123
121
|
cloned_substrate[k] = vnew
|
124
122
|
else:
|
125
|
-
raise ValueError("
|
123
|
+
raise ValueError(f"Expected state to be a list or dict, not {substate!r}")
|
126
124
|
return cloned_substrate if cloned_substrate is not None else substate
|
127
125
|
|
128
126
|
|
@@ -213,6 +211,9 @@ class UnimplementedModel(IpyWidgetComm):
|
|
213
211
|
|
214
212
|
def create_view(self, parent: OutputParent) -> CommView:
|
215
213
|
"""Create a new view."""
|
214
|
+
from euporie.core.convert.datum import Datum
|
215
|
+
from euporie.core.widgets.display import Display
|
216
|
+
|
216
217
|
return CommView(Display(Datum("[Widget not implemented]", format="ansi")))
|
217
218
|
|
218
219
|
|
@@ -242,6 +243,8 @@ class OutputModel(IpyWidgetComm):
|
|
242
243
|
|
243
244
|
def create_view(self, parent: OutputParent) -> CommView:
|
244
245
|
"""Create a new view of this output ipywidget."""
|
246
|
+
from euporie.core.widgets.cell_outputs import CellOutputArea
|
247
|
+
|
245
248
|
container = CellOutputArea(
|
246
249
|
self.data.get("state", {}).get("outputs", []), parent
|
247
250
|
)
|
@@ -250,7 +253,7 @@ class OutputModel(IpyWidgetComm):
|
|
250
253
|
{"outputs": partial(setattr, container, "json")},
|
251
254
|
)
|
252
255
|
|
253
|
-
def add_output(self, json: dict[str, Any]) -> None:
|
256
|
+
def add_output(self, json: dict[str, Any], own: bool) -> None:
|
254
257
|
"""Add a new output to this widget."""
|
255
258
|
if self.clear_output_wait:
|
256
259
|
self.set_state("outputs", [json])
|
@@ -285,9 +288,9 @@ class OutputModel(IpyWidgetComm):
|
|
285
288
|
else:
|
286
289
|
# Restore the message's callbacks
|
287
290
|
if self.original_callbacks:
|
288
|
-
self.comm_container.kernel.msg_id_callbacks[
|
289
|
-
self.
|
290
|
-
|
291
|
+
self.comm_container.kernel.msg_id_callbacks[self.prev_msg_id] = (
|
292
|
+
self.original_callbacks
|
293
|
+
)
|
291
294
|
self.original_callbacks = MsgCallbacks()
|
292
295
|
else:
|
293
296
|
del self.comm_container.kernel.msg_id_callbacks[self.prev_msg_id]
|
@@ -1258,6 +1261,9 @@ class HTMLModel(IpyWidgetComm):
|
|
1258
1261
|
|
1259
1262
|
def create_view(self, parent: OutputParent) -> CommView:
|
1260
1263
|
"""Create a new view of the HTML widget."""
|
1264
|
+
from euporie.core.convert.datum import Datum
|
1265
|
+
from euporie.core.widgets.display import Display
|
1266
|
+
|
1261
1267
|
html = Display(
|
1262
1268
|
Datum(data=self.data["state"].get("value", ""), format="html"),
|
1263
1269
|
dont_extend_width=True,
|
@@ -1285,6 +1291,9 @@ class ImageModel(IpyWidgetComm):
|
|
1285
1291
|
|
1286
1292
|
def create_view(self, parent: OutputParent) -> CommView:
|
1287
1293
|
"""Create a new view of the image widget."""
|
1294
|
+
from euporie.core.convert.datum import Datum
|
1295
|
+
from euporie.core.widgets.display import Display
|
1296
|
+
|
1288
1297
|
display = Display(
|
1289
1298
|
Datum(
|
1290
1299
|
data=self.data["state"].get("value", b""),
|
@@ -1408,7 +1417,7 @@ class ColorPickerModel(TextBoxIpyWidgetComm):
|
|
1408
1417
|
if value in NAMED_COLORS:
|
1409
1418
|
value = NAMED_COLORS[value]
|
1410
1419
|
elif 4 <= len(value) < 7:
|
1411
|
-
value = f"#{value[1]*2}{value[2]*2}{value[3]*2}"
|
1420
|
+
value = f"#{value[1] * 2}{value[2] * 2}{value[3] * 2}"
|
1412
1421
|
else:
|
1413
1422
|
value = value[:7]
|
1414
1423
|
return value
|
euporie/core/comm/registry.py
CHANGED
@@ -8,7 +8,8 @@ from euporie.core.comm.base import UnimplementedComm
|
|
8
8
|
from euporie.core.comm.ipywidgets import open_comm_ipywidgets
|
9
9
|
|
10
10
|
if TYPE_CHECKING:
|
11
|
-
from
|
11
|
+
from collections.abc import Sequence
|
12
|
+
from typing import Any, Callable
|
12
13
|
|
13
14
|
from euporie.core.comm.base import Comm, KernelTab
|
14
15
|
|