euporie 2.8.11__py3-none-any.whl → 2.8.12__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.
Files changed (39) hide show
  1. euporie/console/app.py +2 -2
  2. euporie/console/tabs/console.py +3 -3
  3. euporie/core/__init__.py +1 -1
  4. euporie/core/app/app.py +10 -8
  5. euporie/core/bars/search.py +2 -2
  6. euporie/core/completion.py +3 -3
  7. euporie/core/convert/formats/common.py +6 -1
  8. euporie/core/ft/ansi.py +8 -0
  9. euporie/core/ft/html.py +3 -3
  10. euporie/core/ft/table.py +2 -2
  11. euporie/core/history.py +1 -1
  12. euporie/core/io.py +14 -5
  13. euporie/core/key_binding/bindings/basic.py +4 -2
  14. euporie/core/key_binding/bindings/micro.py +2 -2
  15. euporie/core/key_binding/bindings/page_navigation.py +2 -2
  16. euporie/core/key_binding/bindings/terminal.py +22 -7
  17. euporie/core/key_binding/key_processor.py +1 -1
  18. euporie/core/keys.py +310 -1
  19. euporie/core/layout/mouse.py +1 -1
  20. euporie/core/lsp.py +16 -16
  21. euporie/core/renderer.py +21 -8
  22. euporie/core/tabs/base.py +2 -2
  23. euporie/core/widgets/_settings.py +2 -2
  24. euporie/core/widgets/display.py +2 -2
  25. euporie/core/widgets/inputs.py +2 -2
  26. euporie/core/widgets/pager.py +2 -2
  27. euporie/notebook/app.py +2 -2
  28. euporie/notebook/tabs/edit.py +1 -1
  29. euporie/notebook/tabs/notebook.py +3 -3
  30. euporie/notebook/widgets/side_bar.py +1 -1
  31. euporie/preview/app.py +2 -2
  32. euporie/web/widgets/webview.py +2 -2
  33. {euporie-2.8.11.dist-info → euporie-2.8.12.dist-info}/METADATA +2 -2
  34. {euporie-2.8.11.dist-info → euporie-2.8.12.dist-info}/RECORD +39 -39
  35. {euporie-2.8.11.data → euporie-2.8.12.data}/data/share/applications/euporie-console.desktop +0 -0
  36. {euporie-2.8.11.data → euporie-2.8.12.data}/data/share/applications/euporie-notebook.desktop +0 -0
  37. {euporie-2.8.11.dist-info → euporie-2.8.12.dist-info}/WHEEL +0 -0
  38. {euporie-2.8.11.dist-info → euporie-2.8.12.dist-info}/entry_points.txt +0 -0
  39. {euporie-2.8.11.dist-info → euporie-2.8.12.dist-info}/licenses/LICENSE +0 -0
euporie/console/app.py CHANGED
@@ -79,7 +79,7 @@ class ConsoleApp(BaseApp):
79
79
  # Initialize the application
80
80
  super().__init__(**kwargs)
81
81
 
82
- self.bindings_to_load += ["euporie.console.app.ConsoleApp"]
82
+ self.bindings_to_load += ["euporie.console.app:ConsoleApp"]
83
83
 
84
84
  self.tabs = []
85
85
 
@@ -171,4 +171,4 @@ class ConsoleApp(BaseApp):
171
171
 
172
172
  # ################################# Key Bindings ##################################
173
173
 
174
- # register_bindings({"euporie.console.app.ConsoleApp": {}})
174
+ # register_bindings({"euporie.console.app:ConsoleApp": {}})
@@ -667,7 +667,7 @@ class Console(KernelTab):
667
667
  *input_row,
668
668
  ],
669
669
  key_bindings=load_registered_bindings(
670
- "euporie.console.tabs.console.Console",
670
+ "euporie.console.tabs.console:Console",
671
671
  config=self.app.config,
672
672
  ),
673
673
  )
@@ -883,13 +883,13 @@ class Console(KernelTab):
883
883
 
884
884
  register_bindings(
885
885
  {
886
- "euporie.console.tabs.console.Console": {
886
+ "euporie.console.tabs.console:Console": {
887
887
  "clear-input": ["c-c", "<sigint>"],
888
888
  "cc-interrupt-kernel": ["c-c", "<sigint>"],
889
889
  "run-input": ["c-enter", "c-e"],
890
890
  "end-of-file": "c-d",
891
891
  "clear-screen": "c-l",
892
892
  },
893
- "euporie.console.app.ConsoleApp": {},
893
+ "euporie.console.app:ConsoleApp": {},
894
894
  }
895
895
  )
euporie/core/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """This package defines the euporie application and its components."""
2
2
 
3
3
  __app_name__ = "euporie"
4
- __version__ = "2.8.11"
4
+ __version__ = "2.8.12"
5
5
  __logo__ = "⚈"
6
6
  __strapline__ = "Jupyter in the terminal"
7
7
  __author__ = "Josiah Outram Halstead"
euporie/core/app/app.py CHANGED
@@ -206,6 +206,7 @@ class BaseApp(ConfigurableApp, Application, ABC):
206
206
  self.term_graphics_iterm = False
207
207
  self.term_graphics_kitty = False
208
208
  self.term_sgr_pixel = False
209
+ self.term_osc52_clipboard = False
209
210
  self._term_size_px: tuple[int, int]
210
211
  # Floats at the app level
211
212
  self.leave_graphics = to_filter(leave_graphics)
@@ -232,7 +233,7 @@ class BaseApp(ConfigurableApp, Application, ABC):
232
233
  # List of key-bindings groups to load
233
234
  self.bindings_to_load = [
234
235
  "euporie.core.app.app:BaseApp",
235
- "euporie.core.io.TerminalInfo",
236
+ "euporie.core.key_binding.bindings.terminal:TerminalQueries",
236
237
  ]
237
238
 
238
239
  if enable_page_navigation_bindings:
@@ -387,7 +388,7 @@ class BaseApp(ConfigurableApp, Application, ABC):
387
388
  self.output.get_colors()
388
389
  self.output.get_pixel_size()
389
390
  self.output.get_kitty_graphics_status()
390
- self.output.get_sixel_graphics_status()
391
+ self.output.get_device_attributes()
391
392
  self.output.get_iterm_graphics_status()
392
393
  self.output.get_sgr_pixel_status()
393
394
  self.output.get_csiu_status()
@@ -814,11 +815,6 @@ class BaseApp(ConfigurableApp, Application, ABC):
814
815
  Return a combined style to use for the application
815
816
 
816
817
  """
817
- styles: list[BaseStyle] = [
818
- style_from_pygments_cls(get_style_by_name(self.syntax_theme)),
819
- *self.base_styles,
820
- ]
821
-
822
818
  # Get foreground and background colors based on the configured colour scheme
823
819
  theme_colors: dict[str, dict[str, str]] = {
824
820
  "default": {},
@@ -862,7 +858,11 @@ class BaseApp(ConfigurableApp, Application, ABC):
862
858
  )
863
859
 
864
860
  # Build app style
865
- styles.append(build_style(cp))
861
+ styles: list[BaseStyle] = [
862
+ style_from_pygments_cls(get_style_by_name(self.syntax_theme)),
863
+ *self.base_styles,
864
+ build_style(cp),
865
+ ]
866
866
 
867
867
  # Apply style transformations based on the configured color scheme
868
868
  self.style_transformation = merge_style_transformations(
@@ -889,6 +889,8 @@ class BaseApp(ConfigurableApp, Application, ABC):
889
889
  def update_style(self, query: Setting | None = None) -> None:
890
890
  """Update the application's style when the syntax theme is changed."""
891
891
  self.renderer.style = self.create_merged_style()
892
+ # self.invalidate()
893
+ # self.renderer.reset()
892
894
 
893
895
  def refresh(self) -> None:
894
896
  """Reset all tabs."""
@@ -63,7 +63,7 @@ class SearchBar(PtkSearchToolbar):
63
63
  ),
64
64
  )
65
65
  self.control.key_bindings = load_registered_bindings(
66
- "euporie.core.bars.search.SearchBar",
66
+ "euporie.core.bars.search:SearchBar",
67
67
  config=get_app().config,
68
68
  )
69
69
  search_state = self.control.searcher_search_state
@@ -78,7 +78,7 @@ class SearchBar(PtkSearchToolbar):
78
78
  "find-next": "c-g",
79
79
  "find-previous": "c-p",
80
80
  },
81
- "euporie.core.bars.search.SearchBar": {
81
+ "euporie.core.bars.search:SearchBar": {
82
82
  "accept-search": "enter",
83
83
  "stop-search": "escape",
84
84
  },
@@ -48,7 +48,7 @@ class KernelCompleter(Completer):
48
48
 
49
49
  async def get_completions_async(
50
50
  self, document: Document, complete_event: CompleteEvent
51
- ) -> AsyncGenerator[Completion, None]:
51
+ ) -> AsyncGenerator[Completion]:
52
52
  """Retrieve completions from a :class:`Kernel`."""
53
53
  for kwargs in await self.kernel.complete_async(
54
54
  source=document.text,
@@ -84,7 +84,7 @@ class LspCompleter(Completer):
84
84
 
85
85
  async def get_completions_async(
86
86
  self, document: Document, complete_event: CompleteEvent
87
- ) -> AsyncGenerator[Completion, None]:
87
+ ) -> AsyncGenerator[Completion]:
88
88
  """Retrieve completions from an LSP server."""
89
89
  # Get completions
90
90
  text_preceding = document.current_line_before_cursor.lower()
@@ -138,7 +138,7 @@ class DeduplicateCompleter(Completer):
138
138
 
139
139
  async def get_completions_async(
140
140
  self, document: Document, complete_event: CompleteEvent
141
- ) -> AsyncGenerator[Completion, None]:
141
+ ) -> AsyncGenerator[Completion]:
142
142
  """Get completions from wrapped completer."""
143
143
  # Keep track of the document strings we'd get after applying any completion.
144
144
  found_so_far: set[str] = set()
@@ -67,7 +67,12 @@ async def chafa_convert_cmd(
67
67
  extend: bool = True,
68
68
  ) -> str | bytes:
69
69
  """Convert image data to ANSI text using :command:`chafa`."""
70
- cmd: list[Any] = ["chafa", f"--format={output_format}"]
70
+ cmd: list[Any] = [
71
+ "chafa",
72
+ f"--format={output_format}",
73
+ "--passthrough=none",
74
+ "--polite=on",
75
+ ]
71
76
  if cols is not None or rows is not None:
72
77
  size = "--size="
73
78
  if cols is not None:
euporie/core/ft/ansi.py CHANGED
@@ -38,6 +38,13 @@ class ANSI(PTANSI):
38
38
  value = re.sub(r".*\x1b\[2K", "", value, count=0)
39
39
  # Remove hide & show cursor commands
40
40
  value = re.sub(r"\x1b\[\?25[hl]", "", value, count=0)
41
+ # Collapse cursor up movements
42
+ while (match := re.search(r"\x1b\[(?P<count>\d+)A", value)) is not None:
43
+ lines = int(match["count"])
44
+ before = value[: match.start()]
45
+ after = value[match.end() :]
46
+ before = "".join(before.splitlines(keepends=True)[:-lines])
47
+ value = before + after
41
48
 
42
49
  super().__init__(value)
43
50
 
@@ -153,3 +160,4 @@ class ANSI(PTANSI):
153
160
  continue
154
161
 
155
162
  formatted_text.append((style, sequence))
163
+ # log.debug(repr(sequence))
euporie/core/ft/html.py CHANGED
@@ -3036,14 +3036,14 @@ class Node:
3036
3036
  return contents
3037
3037
 
3038
3038
  @property
3039
- def descendents(self) -> Generator[Node, None, None]:
3039
+ def descendents(self) -> Generator[Node]:
3040
3040
  """Yield all descendent elements."""
3041
3041
  for child in self.contents:
3042
3042
  yield child
3043
3043
  yield from child.descendents
3044
3044
 
3045
3045
  @property
3046
- def renderable_descendents(self) -> Generator[Node, None, None]:
3046
+ def renderable_descendents(self) -> Generator[Node]:
3047
3047
  """Yield descendents, including pseudo and skipping inline elements."""
3048
3048
  for child in self.renderable_contents:
3049
3049
  if (
@@ -3084,7 +3084,7 @@ class Node:
3084
3084
  return False
3085
3085
 
3086
3086
  @property
3087
- def child_elements(self) -> Generator[Node, None, None]:
3087
+ def child_elements(self) -> Generator[Node]:
3088
3088
  """Yield all of the child element nodes."""
3089
3089
  for child in self.contents:
3090
3090
  # Ignore text and comment nodes
euporie/core/ft/table.py CHANGED
@@ -1503,8 +1503,8 @@ class Table:
1503
1503
 
1504
1504
  # Calculate border visibility
1505
1505
  render_count = self.render_count
1506
- row_edge_visibilities: dict[int, bool] = defaultdict(lambda: False)
1507
- col_edge_visibilities: dict[int, bool] = defaultdict(lambda: False)
1506
+ row_edge_visibilities: dict[int, bool] = defaultdict(bool)
1507
+ col_edge_visibilities: dict[int, bool] = defaultdict(bool)
1508
1508
  for y, row in enumerate(self.rows):
1509
1509
  for x, cell in enumerate(row.cells):
1510
1510
  bv = compute_border_visibility(cell, render_count)
euporie/core/history.py CHANGED
@@ -37,7 +37,7 @@ class KernelHistory(History):
37
37
  return self._kernel()
38
38
  return self._kernel
39
39
 
40
- async def load(self) -> AsyncGenerator[str, None]:
40
+ async def load(self) -> AsyncGenerator[str]:
41
41
  """Load the history and yield all entries, most recent history first.
42
42
 
43
43
  This method can be called multiple times from the `Buffer` to
euporie/core/io.py CHANGED
@@ -138,12 +138,13 @@ class Vt100Parser(vt100_parser.Vt100Parser):
138
138
  # Allow BEL or ST as terminator
139
139
  r"(?:\x1b\\|\x9c|\x07)"
140
140
  ),
141
+ MoreKeys.PaletteDsrResponse: re.compile(r"^\x1b\[\?997;(?P<mode>\d)n"),
141
142
  MoreKeys.PixelSizeResponse: re.compile(r"^\x1b\[4;(?P<y>\d+);(?P<x>\d+)t"),
142
143
  MoreKeys.KittyGraphicsStatusResponse: re.compile(
143
144
  r"^\x1b_Gi=(4294967295|0);(?P<status>OK)\x1b\\"
144
145
  ),
145
- MoreKeys.SixelGraphicsStatusResponse: re.compile(
146
- r"^\x1b\[\?(?:\d+;)*(?P<sixel>4)(?:;\d+)*c"
146
+ MoreKeys.DeviceAttributesResponse: re.compile(
147
+ r"^\x1b\[\?(?P<attrs>[\d;]*)c"
147
148
  ),
148
149
  MoreKeys.ItermGraphicsStatusResponse: re.compile(
149
150
  r"^\x1bP>\|(?P<term>[^\x1b]+)\x1b\\"
@@ -184,6 +185,14 @@ class Vt100_Output(PtkVt100_Output):
184
185
  """Disable SGR-pixel mouse positioning."""
185
186
  self.write_raw("\x1b[?1016l")
186
187
 
188
+ def enable_palette_dsr(self) -> None:
189
+ """Enable device status reports for color palette updates."""
190
+ self.write_raw("\x1b[?2031h")
191
+
192
+ def disable_palette_dsr(self) -> None:
193
+ """Disable device status reports for color palette updates."""
194
+ self.write_raw("\x1b[?2031l")
195
+
187
196
  def enable_extended_keys(self) -> None:
188
197
  """Request extended keys."""
189
198
  # xterm
@@ -225,11 +234,11 @@ class Vt100_Output(PtkVt100_Output):
225
234
  self.write_raw(
226
235
  "\x1b[s"
227
236
  + passthrough("\x1b_Gi=4294967295,s=1,v=1,a=q,t=d,f=24;aaaa\x1b\\")
228
- + "\x1b[u\x1b[2k"
237
+ + "\x1b[u\x1b[2K"
229
238
  )
230
239
 
231
- def get_sixel_graphics_status(self) -> None:
232
- """Query terminal for sixel graphics support."""
240
+ def get_device_attributes(self) -> None:
241
+ """Query terminal for device attributes."""
233
242
  self.write_raw(passthrough("\x1b[c"))
234
243
 
235
244
  def get_iterm_graphics_status(self) -> None:
@@ -38,7 +38,7 @@ class TextEntry:
38
38
  # Register default bindings for micro edit mode
39
39
  register_bindings(
40
40
  {
41
- "euporie.core.key_binding.bindings.basic.TextEntry": {
41
+ "euporie.core.key_binding.bindings.basic:TextEntry": {
42
42
  "type-key": "<any>",
43
43
  "complete-bracket-()": "(",
44
44
  "complete-bracket-[]": "[",
@@ -57,7 +57,7 @@ def load_basic_bindings(config: Config | None = None) -> KeyBindingsBase:
57
57
  from euporie.core import keys # noqa: F401
58
58
 
59
59
  return load_registered_bindings(
60
- "euporie.core.key_binding.bindings.basic.TextEntry", config=config
60
+ "euporie.core.key_binding.bindings.basic:TextEntry", config=config
61
61
  )
62
62
 
63
63
 
@@ -80,11 +80,13 @@ def type_key(event: KeyPressEvent) -> None:
80
80
 
81
81
 
82
82
  def _complete_bracket(right: str, event: KeyPressEvent) -> None:
83
+ """Automatically insert a closing bracket."""
83
84
  event.current_buffer.insert_text(right, move_cursor=False)
84
85
  event.key_processor.feed(event.key_sequence[0], first=True)
85
86
 
86
87
 
87
88
  def _skip_close_bracket(right: str, event: KeyPressEvent) -> None:
89
+ """Skip typing a close bracket if it already exists."""
88
90
  event.current_buffer.cursor_position += 1
89
91
 
90
92
 
@@ -75,7 +75,7 @@ class EditMode:
75
75
  # Register default bindings for micro edit mode
76
76
  register_bindings(
77
77
  {
78
- "euporie.core.key_binding.bindings.micro.EditMode": {
78
+ "euporie.core.key_binding.bindings.micro:EditMode": {
79
79
  "move-cursor-right": "right",
80
80
  "move-cursor-left": "left",
81
81
  "newline": "enter",
@@ -180,7 +180,7 @@ def load_micro_bindings(config: Config | None = None) -> KeyBindingsBase:
180
180
  """Load editor key-bindings in the style of the ``micro`` text editor."""
181
181
  return ConditionalKeyBindings(
182
182
  load_registered_bindings(
183
- "euporie.core.key_binding.bindings.micro.EditMode", config=config
183
+ "euporie.core.key_binding.bindings.micro:EditMode", config=config
184
184
  ),
185
185
  micro_mode,
186
186
  )
@@ -37,7 +37,7 @@ class PageNavigation:
37
37
  # Register default bindings for micro edit mode
38
38
  register_bindings(
39
39
  {
40
- "euporie.core.key_binding.bindings.page_navigation.PageNavigation": {
40
+ "euporie.core.key_binding.bindings.page_navigation:PageNavigation": {
41
41
  "scroll-page-up": "pageup",
42
42
  "scroll-page-down": "pagedown",
43
43
  },
@@ -54,7 +54,7 @@ def load_page_navigation_bindings(config: Config | None = None) -> KeyBindingsBa
54
54
  load_vi_page_navigation_bindings(),
55
55
  ConditionalKeyBindings(
56
56
  load_registered_bindings(
57
- "euporie.core.key_binding.bindings.page_navigation.PageNavigation",
57
+ "euporie.core.key_binding.bindings.page_navigation:PageNavigation",
58
58
  config=config,
59
59
  ),
60
60
  micro_mode,
@@ -53,6 +53,15 @@ def get_match(event: KeyPressEvent) -> dict[str, str] | None:
53
53
  return None
54
54
 
55
55
 
56
+ @add_cmd(hidden=True, is_global=True)
57
+ def _set_terminal_palette(event: KeyPressEvent) -> object:
58
+ from euporie.core.io import Vt100_Output
59
+
60
+ if isinstance(output := event.app.output, Vt100_Output):
61
+ output.get_colors()
62
+ return NotImplemented
63
+
64
+
56
65
  @add_cmd(hidden=True, is_global=True)
57
66
  def _set_terminal_color(event: KeyPressEvent) -> object:
58
67
  """Run when the terminal receives a terminal color query response.
@@ -72,6 +81,7 @@ def _set_terminal_color(event: KeyPressEvent) -> object:
72
81
  r, g, b = colors.get("r", "00"), colors.get("g", "00"), colors.get("b", "00")
73
82
  app.term_colors[_COLOR_NAMES.get(c, c)] = f"#{r[:2]}{g[:2]}{b[:2]}"
74
83
  app.update_style()
84
+ return None
75
85
  return NotImplemented
76
86
 
77
87
 
@@ -91,16 +101,20 @@ def _set_terminal_pixel_size(event: KeyPressEvent) -> object:
91
101
 
92
102
 
93
103
  @add_cmd(hidden=True, is_global=True)
94
- def _set_terminal_graphics_sixel(event: KeyPressEvent) -> object:
95
- """Run when the terminal receives a sixel graphics support query response."""
104
+ def _set_terminal_device_attributes(event: KeyPressEvent) -> object:
105
+ """Run when the terminal receives a device attributes query response."""
96
106
  from euporie.core.app.app import BaseApp
97
107
 
98
108
  if (
99
109
  isinstance(app := event.app, BaseApp)
100
110
  and (values := get_match(event))
101
- and values.get("sixel")
111
+ and (attrs_str := values.get("attrs"))
102
112
  ):
103
- app.term_graphics_sixel = True
113
+ attrs = {attr for attr in attrs_str.split(";") if attr}
114
+ if "4" in attrs:
115
+ app.term_graphics_sixel = True
116
+ if "52" in attrs:
117
+ app.term_osc52_clipboard = True
104
118
  return NotImplemented
105
119
 
106
120
 
@@ -173,11 +187,12 @@ class TerminalQueries:
173
187
 
174
188
  register_bindings(
175
189
  {
176
- "euporie.core.io.TerminalInfo": {
190
+ "euporie.core.key_binding.bindings.terminal:TerminalQueries": {
191
+ "set-terminal-palette": "<palette-dsr-response>",
177
192
  "set-terminal-color": "<colors-response>",
178
193
  "set-terminal-pixel-size": "<pixel-size-response>",
179
194
  "set-terminal-graphics-kitty": "<kitty-graphics-status-response>",
180
- "set-terminal-graphics-sixel": "<sixel-graphics-status-response>",
195
+ "set-terminal-device-attributes": "<device-attributes-response>",
181
196
  "set-terminal-graphics-iterm": "<iterm-graphics-status-response>",
182
197
  "set-terminal-sgr-pixel": "<sgr-pixel-status-response>",
183
198
  "set-terminal-clipboard-data": "<clipboard-data-response>",
@@ -189,5 +204,5 @@ register_bindings(
189
204
  def load_terminal_bindings(config: Config | None = None) -> KeyBindingsBase:
190
205
  """Load editor key-bindings in the style of the ``micro`` text editor."""
191
206
  return load_registered_bindings(
192
- "euporie.core.key_binding.bindings.terminal.TerminalQueries", config=config
207
+ "euporie.core.key_binding.bindings.terminal:TerminalQueries", config=config
193
208
  )
@@ -26,7 +26,7 @@ def _kp_init(
26
26
  ) -> None:
27
27
  """Include more keys when creating a KeyPress."""
28
28
  assert isinstance(key, (Keys | MoreKeys)) or len(key) == 1, (
29
- f"key {key!r} ({type(key)}) not recoognised {MoreKeys(key)}"
29
+ f"key {key!r} ({type(key)}) not recognised {MoreKeys(key)}"
30
30
  )
31
31
  if data is None:
32
32
  data = key.value if isinstance(key, (Keys, MoreKeys)) else key