euporie 2.8.1__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.
Files changed (129) hide show
  1. euporie/console/_commands.py +143 -0
  2. euporie/console/_settings.py +58 -0
  3. euporie/console/app.py +25 -71
  4. euporie/console/tabs/console.py +267 -147
  5. euporie/core/__init__.py +1 -9
  6. euporie/core/__main__.py +31 -5
  7. euporie/core/_settings.py +104 -0
  8. euporie/core/app/__init__.py +3 -0
  9. euporie/core/app/_commands.py +70 -0
  10. euporie/core/app/_settings.py +427 -0
  11. euporie/core/{app.py → app/app.py} +214 -572
  12. euporie/core/app/base.py +51 -0
  13. euporie/core/{current.py → app/current.py} +13 -4
  14. euporie/core/app/cursor.py +35 -0
  15. euporie/core/app/dummy.py +12 -0
  16. euporie/core/app/launch.py +28 -0
  17. euporie/core/bars/__init__.py +11 -0
  18. euporie/core/bars/command.py +182 -0
  19. euporie/core/bars/menu.py +258 -0
  20. euporie/core/{widgets → bars}/search.py +154 -57
  21. euporie/core/{widgets → bars}/status.py +9 -26
  22. euporie/core/clipboard.py +19 -80
  23. euporie/core/comm/base.py +8 -6
  24. euporie/core/comm/ipywidgets.py +21 -12
  25. euporie/core/comm/registry.py +2 -1
  26. euporie/core/commands.py +11 -5
  27. euporie/core/completion.py +3 -2
  28. euporie/core/config.py +368 -341
  29. euporie/core/convert/__init__.py +0 -30
  30. euporie/core/convert/datum.py +131 -60
  31. euporie/core/convert/formats/__init__.py +31 -0
  32. euporie/core/convert/formats/ansi.py +46 -30
  33. euporie/core/convert/formats/common.py +11 -23
  34. euporie/core/convert/formats/html.py +45 -40
  35. euporie/core/convert/formats/pil.py +1 -1
  36. euporie/core/convert/formats/png.py +3 -5
  37. euporie/core/convert/formats/sixel.py +3 -3
  38. euporie/core/convert/registry.py +11 -8
  39. euporie/core/convert/utils.py +50 -23
  40. euporie/core/diagnostics.py +2 -2
  41. euporie/core/filters.py +72 -82
  42. euporie/core/format.py +13 -2
  43. euporie/core/ft/ansi.py +1 -1
  44. euporie/core/ft/html.py +36 -36
  45. euporie/core/ft/table.py +1 -3
  46. euporie/core/ft/utils.py +4 -1
  47. euporie/core/graphics.py +216 -124
  48. euporie/core/history.py +2 -2
  49. euporie/core/inspection.py +3 -2
  50. euporie/core/io.py +207 -28
  51. euporie/core/kernel/__init__.py +1 -0
  52. euporie/core/{kernel.py → kernel/client.py} +100 -139
  53. euporie/core/kernel/manager.py +114 -0
  54. euporie/core/key_binding/bindings/__init__.py +2 -8
  55. euporie/core/key_binding/bindings/basic.py +47 -7
  56. euporie/core/key_binding/bindings/completion.py +3 -8
  57. euporie/core/key_binding/bindings/micro.py +5 -7
  58. euporie/core/key_binding/bindings/mouse.py +26 -24
  59. euporie/core/key_binding/bindings/terminal.py +193 -0
  60. euporie/core/key_binding/bindings/vi.py +46 -0
  61. euporie/core/key_binding/key_processor.py +43 -2
  62. euporie/core/key_binding/registry.py +2 -0
  63. euporie/core/key_binding/utils.py +22 -2
  64. euporie/core/keys.py +7156 -93
  65. euporie/core/layout/cache.py +35 -25
  66. euporie/core/layout/containers.py +280 -74
  67. euporie/core/layout/decor.py +5 -5
  68. euporie/core/layout/mouse.py +1 -1
  69. euporie/core/layout/print.py +16 -3
  70. euporie/core/layout/scroll.py +26 -28
  71. euporie/core/log.py +75 -60
  72. euporie/core/lsp.py +118 -24
  73. euporie/core/margins.py +60 -31
  74. euporie/core/path.py +2 -1
  75. euporie/core/renderer.py +58 -17
  76. euporie/core/style.py +60 -40
  77. euporie/core/suggest.py +103 -85
  78. euporie/core/tabs/__init__.py +34 -0
  79. euporie/core/tabs/_settings.py +113 -0
  80. euporie/core/tabs/base.py +11 -435
  81. euporie/core/tabs/kernel.py +420 -0
  82. euporie/core/tabs/notebook.py +20 -54
  83. euporie/core/utils.py +98 -6
  84. euporie/core/validation.py +1 -1
  85. euporie/core/widgets/_settings.py +188 -0
  86. euporie/core/widgets/cell.py +90 -158
  87. euporie/core/widgets/cell_outputs.py +25 -36
  88. euporie/core/widgets/decor.py +11 -41
  89. euporie/core/widgets/dialog.py +55 -44
  90. euporie/core/widgets/display.py +27 -24
  91. euporie/core/widgets/file_browser.py +5 -26
  92. euporie/core/widgets/forms.py +16 -12
  93. euporie/core/widgets/inputs.py +37 -81
  94. euporie/core/widgets/layout.py +7 -6
  95. euporie/core/widgets/logo.py +49 -0
  96. euporie/core/widgets/menu.py +13 -11
  97. euporie/core/widgets/pager.py +8 -11
  98. euporie/core/widgets/palette.py +6 -6
  99. euporie/hub/app.py +52 -31
  100. euporie/notebook/_commands.py +24 -0
  101. euporie/notebook/_settings.py +107 -0
  102. euporie/notebook/app.py +109 -210
  103. euporie/notebook/filters.py +1 -1
  104. euporie/notebook/tabs/__init__.py +46 -7
  105. euporie/notebook/tabs/_commands.py +714 -0
  106. euporie/notebook/tabs/_settings.py +32 -0
  107. euporie/notebook/tabs/display.py +2 -2
  108. euporie/notebook/tabs/edit.py +12 -7
  109. euporie/notebook/tabs/json.py +3 -3
  110. euporie/notebook/tabs/log.py +1 -18
  111. euporie/notebook/tabs/notebook.py +21 -674
  112. euporie/notebook/widgets/_commands.py +11 -0
  113. euporie/notebook/widgets/_settings.py +19 -0
  114. euporie/notebook/widgets/side_bar.py +14 -34
  115. euporie/preview/_settings.py +104 -0
  116. euporie/preview/app.py +8 -30
  117. euporie/preview/tabs/notebook.py +15 -86
  118. euporie/web/tabs/web.py +4 -6
  119. euporie/web/widgets/webview.py +5 -12
  120. {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/METADATA +11 -15
  121. euporie-2.8.5.dist-info/RECORD +172 -0
  122. {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/WHEEL +1 -1
  123. {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/entry_points.txt +2 -2
  124. {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/licenses/LICENSE +1 -1
  125. euporie/core/launch.py +0 -59
  126. euporie/core/terminal.py +0 -527
  127. euporie-2.8.1.dist-info/RECORD +0 -146
  128. {euporie-2.8.1.data → euporie-2.8.5.data}/data/share/applications/euporie-console.desktop +0 -0
  129. {euporie-2.8.1.data → euporie-2.8.5.data}/data/share/applications/euporie-notebook.desktop +0 -0
euporie/core/margins.py CHANGED
@@ -9,13 +9,15 @@ from typing import TYPE_CHECKING, cast
9
9
 
10
10
  from prompt_toolkit.data_structures import Point
11
11
  from prompt_toolkit.filters import FilterOrBool, to_filter
12
+ from prompt_toolkit.layout.containers import ScrollOffsets, WindowRenderInfo
12
13
  from prompt_toolkit.layout.controls import FormattedTextControl
13
14
  from prompt_toolkit.layout.dimension import Dimension
14
15
  from prompt_toolkit.layout.margins import Margin
16
+ from prompt_toolkit.layout.screen import WritePosition
15
17
  from prompt_toolkit.mouse_events import MouseButton, MouseEventType
16
18
  from prompt_toolkit.mouse_events import MouseEvent as PtkMouseEvent
17
19
 
18
- from euporie.core.current import get_app
20
+ from euporie.core.app.current import get_app
19
21
  from euporie.core.key_binding.bindings.mouse import MouseEvent, RelativePosition
20
22
  from euporie.core.layout.containers import Window
21
23
 
@@ -27,10 +29,10 @@ if TYPE_CHECKING:
27
29
  KeyBindingsBase,
28
30
  NotImplementedOrNone,
29
31
  )
30
- from prompt_toolkit.layout.containers import Container, WindowRenderInfo
32
+ from prompt_toolkit.layout.containers import Container
31
33
  from prompt_toolkit.layout.controls import UIContent
32
34
  from prompt_toolkit.layout.mouse_handlers import MouseHandlers
33
- from prompt_toolkit.layout.screen import Screen, WritePosition
35
+ from prompt_toolkit.layout.screen import Screen
34
36
 
35
37
  from euporie.core.diagnostics import Report
36
38
 
@@ -49,9 +51,9 @@ class ClickableMargin(Margin, metaclass=ABCMeta):
49
51
 
50
52
  write_position: WritePosition | None
51
53
 
52
- def set_write_position(self, write_position: WritePosition) -> None:
54
+ def set_margin_window(self, margin_window: Window) -> None:
53
55
  """Set the write position of the menu."""
54
- self.write_position = write_position
56
+ self.margin_window = margin_window
55
57
 
56
58
 
57
59
  class MarginContainer(Window):
@@ -65,6 +67,9 @@ class MarginContainer(Window):
65
67
  self.content = FormattedTextControl(self.create_fragments)
66
68
  self.always_hide_cursor = to_filter(True)
67
69
 
70
+ if isinstance(self.margin, ClickableMargin):
71
+ self.margin.set_margin_window(self)
72
+
68
73
  def create_fragments(self) -> StyleAndTextTuples:
69
74
  """Generate text fragments to display."""
70
75
  return self.margin.create_margin(
@@ -102,8 +107,6 @@ class MarginContainer(Window):
102
107
  ) -> None:
103
108
  """Write the actual content to the screen."""
104
109
  self.write_position = write_position
105
- if isinstance(self.margin, ClickableMargin):
106
- self.margin.set_write_position(write_position)
107
110
 
108
111
  margin_content: UIContent = self.content.create_content(
109
112
  write_position.width + 1, write_position.height
@@ -112,6 +115,21 @@ class MarginContainer(Window):
112
115
  margin_content, screen, write_position, 0, write_position.width
113
116
  )
114
117
 
118
+ self.render_info = WindowRenderInfo(
119
+ window=self,
120
+ ui_content=margin_content,
121
+ horizontal_scroll=0,
122
+ vertical_scroll=0,
123
+ window_width=write_position.width,
124
+ window_height=write_position.height,
125
+ configured_scroll_offsets=ScrollOffsets(),
126
+ visible_line_to_row_col=visible_line_to_row_col,
127
+ rowcol_to_yx=rowcol_to_yx,
128
+ x_offset=write_position.xpos,
129
+ y_offset=write_position.ypos,
130
+ wrap_lines=False,
131
+ )
132
+
115
133
  # Set mouse handlers.
116
134
  def mouse_handler(mouse_event: PtkMouseEvent) -> NotImplementedOrNone:
117
135
  """Turn screen coordinates into line coordinates."""
@@ -164,6 +182,8 @@ class MarginContainer(Window):
164
182
  handler=mouse_handler,
165
183
  )
166
184
 
185
+ screen.visible_windows_to_write_positions[self] = write_position
186
+
167
187
  def is_modal(self) -> bool:
168
188
  """When this container is modal."""
169
189
  return False
@@ -214,8 +234,7 @@ class ScrollbarMargin(ClickableMargin):
214
234
  self.thumb_top = 0.0
215
235
  self.thumb_size = 0.0
216
236
 
217
- self.window_render_info: WindowRenderInfo | None = None
218
- self.write_position: WritePosition | None = None
237
+ self.target_render_info: WindowRenderInfo | None = None
219
238
 
220
239
  def get_width(self, get_ui_content: Callable[[], UIContent]) -> int:
221
240
  """Return the scrollbar width: always 1."""
@@ -226,13 +245,11 @@ class ScrollbarMargin(ClickableMargin):
226
245
  window_render_info: WindowRenderInfo | None,
227
246
  width: int,
228
247
  height: int,
229
- margin_render_info: WindowRenderInfo | None = None,
230
248
  ) -> StyleAndTextTuples:
231
249
  """Create the margin's formatted text."""
232
250
  result: StyleAndTextTuples = []
233
251
 
234
- self.window_render_info = window_render_info
235
- self.margin_render_info = margin_render_info
252
+ self.target_render_info = window_render_info
236
253
 
237
254
  # If this is the first time the target is being drawn, it may not yet have a
238
255
  # render_info yet. Thus, invalidate the app so we can immediately redraw the
@@ -400,11 +417,11 @@ class ScrollbarMargin(ClickableMargin):
400
417
  :py:const:`None` is returned when the mouse event is handled successfully
401
418
 
402
419
  """
403
- render_info = self.window_render_info
404
- if render_info is None:
420
+ target_render_info = self.target_render_info
421
+ if target_render_info is None:
405
422
  return NotImplemented
406
423
 
407
- content_height = render_info.content_height
424
+ content_height = target_render_info.content_height
408
425
  if isinstance(mouse_event, MouseEvent):
409
426
  cell_position = mouse_event.cell_position
410
427
  else:
@@ -413,9 +430,9 @@ class ScrollbarMargin(ClickableMargin):
413
430
 
414
431
  # Handle scroll events on the scrollbar
415
432
  if mouse_event.event_type == MouseEventType.SCROLL_UP:
416
- render_info.window._scroll_up()
433
+ target_render_info.window._scroll_up()
417
434
  elif mouse_event.event_type == MouseEventType.SCROLL_DOWN:
418
- render_info.window._scroll_down()
435
+ target_render_info.window._scroll_down()
419
436
 
420
437
  # Mouse drag events
421
438
  elif self.dragging and mouse_event.event_type == MouseEventType.MOUSE_MOVE:
@@ -425,8 +442,8 @@ class ScrollbarMargin(ClickableMargin):
425
442
  + 0.5
426
443
  )
427
444
  # Use the window's current vertical scroll as it may have changed since the
428
- # window_render_info was generated
429
- window = render_info.window
445
+ # target_render_info was generated
446
+ window = target_render_info.window
430
447
  delta = window.vertical_scroll - target_scroll
431
448
 
432
449
  if isinstance(window, Window):
@@ -435,7 +452,7 @@ class ScrollbarMargin(ClickableMargin):
435
452
  func()
436
453
  # Hack to speed up scrolling on the :py:`ScrollingContainer`
437
454
  elif hasattr(window, "scrolling"):
438
- setattr(render_info.window, "scrolling", delta) # noqa: B010
455
+ setattr(target_render_info.window, "scrolling", delta) # noqa: B010
439
456
 
440
457
  # Mouse down events
441
458
  elif mouse_event.event_type == MouseEventType.MOUSE_DOWN:
@@ -443,20 +460,30 @@ class ScrollbarMargin(ClickableMargin):
443
460
  arrows = self.display_arrows()
444
461
  if arrows and int(row) == 0:
445
462
  offset = -1
446
- elif arrows and int(row) == render_info.window_height - 1:
463
+ elif arrows and int(row) == target_render_info.window_height - 1:
447
464
  offset = 1
448
465
  # Scroll up or down one page if clicking on the background
449
466
  elif row < self.thumb_top + 1 or self.thumb_top + 1 + self.thumb_size < row:
450
467
  direction = (row < (self.thumb_top + self.thumb_size // 2)) * -2 + 1
451
- offset = direction * render_info.window_height
468
+ offset = direction * target_render_info.window_height
452
469
  # We are on the scroll button - start a drag event if this is not a
453
470
  # repeated mouse event
454
471
  elif not repeated:
455
- self.dragging = True
456
- self.drag_start_scroll = render_info.vertical_scroll
457
- self.drag_start_offset = row - self.thumb_top
458
- # Restrict mouse events to this area
459
- get_app().mouse_limits = self.write_position
472
+ # Restrict mouse events to the scrollbar's area. Recalculate the area
473
+ # based on the margin's window's render_info, in case this is not the
474
+ # main screen
475
+ if margin_render_info := self.margin_window.render_info:
476
+ y_min, x_min = min(margin_render_info._rowcol_to_yx.values())
477
+ y_max, x_max = max(margin_render_info._rowcol_to_yx.values())
478
+ get_app().mouse_limits = WritePosition(
479
+ xpos=x_min,
480
+ ypos=y_min,
481
+ width=x_max - x_min + 1,
482
+ height=y_max - y_min + 1,
483
+ )
484
+ self.dragging = True
485
+ self.drag_start_scroll = target_render_info.vertical_scroll
486
+ self.drag_start_offset = row - self.thumb_top
460
487
  return None
461
488
  # Otherwise this is a click on the centre scroll button - do nothing
462
489
  else:
@@ -465,9 +492,9 @@ class ScrollbarMargin(ClickableMargin):
465
492
  if mouse_event.button == MouseButton.LEFT:
466
493
  func = None
467
494
  if offset < 0:
468
- func = render_info.window._scroll_up
495
+ func = target_render_info.window._scroll_up
469
496
  elif offset > 0:
470
- func = render_info.window._scroll_down
497
+ func = target_render_info.window._scroll_down
471
498
  if func is not None:
472
499
  # Scroll the window multiple times to scroll by the offset
473
500
  for _ in range(abs(int(offset))):
@@ -517,7 +544,7 @@ class NumberedMargin(Margin):
517
544
  def get_width(self, get_ui_content: Callable[[], UIContent]) -> int:
518
545
  """Return the width of the margin."""
519
546
  line_count = get_ui_content().line_count
520
- return len("%s" % line_count) + 2
547
+ return len(f"{line_count}") + 2
521
548
 
522
549
  def create_margin(
523
550
  self, window_render_info: WindowRenderInfo, width: int, height: int
@@ -561,8 +588,10 @@ class NumberedMargin(Margin):
561
588
  style = f"{self_style} class:line-number.current"
562
589
  else:
563
590
  style = self_style
591
+ if last_lineno is None and lineno == 0 and not multiline:
592
+ linestr = ">"
564
593
  # Only display line number if this line is not a continuation of the previous line.
565
- if lineno != last_lineno:
594
+ elif lineno != last_lineno:
566
595
  linestr = str(lineno + 1).rjust(width - 2)
567
596
  else:
568
597
  linestr = " " * (width - 2)
euporie/core/path.py CHANGED
@@ -13,8 +13,9 @@ from fsspec.registry import register_implementation as fs_register_implementatio
13
13
  from upath import UPath
14
14
 
15
15
  if TYPE_CHECKING:
16
+ from collections.abc import Mapping
16
17
  from os import PathLike
17
- from typing import Any, Mapping
18
+ from typing import Any
18
19
 
19
20
 
20
21
  log = logging.getLogger(__name__)
euporie/core/renderer.py CHANGED
@@ -3,11 +3,13 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import logging
6
+ from hashlib import md5
6
7
  from typing import TYPE_CHECKING
7
8
 
8
9
  from prompt_toolkit.data_structures import Point, Size
9
10
  from prompt_toolkit.filters import to_filter
10
11
  from prompt_toolkit.layout.mouse_handlers import MouseHandlers
12
+ from prompt_toolkit.layout.screen import Char
11
13
  from prompt_toolkit.renderer import Renderer as PtkRenderer
12
14
  from prompt_toolkit.renderer import _StyleStringHasStyleCache, _StyleStringToAttrsCache
13
15
 
@@ -25,7 +27,6 @@ if TYPE_CHECKING:
25
27
  from prompt_toolkit.output import ColorDepth, Output
26
28
  from prompt_toolkit.styles import BaseStyle
27
29
 
28
-
29
30
  __all__ = ["Renderer"]
30
31
 
31
32
  log = logging.getLogger(__name__)
@@ -117,19 +118,27 @@ def _output_screen_diff(
117
118
  write(char.char)
118
119
  last_style = char.style
119
120
 
121
+ def hash_screen_row(row: dict[int, Char], zwe_row: dict[int, str]) -> str:
122
+ """Generate a hash for a screen row to quickly detect changes."""
123
+ hasher = md5(usedforsecurity=False)
124
+ # Hash the character data
125
+ for idx in sorted(row.keys()):
126
+ cell = row[idx]
127
+ hasher.update(f"{idx}:{cell.char}:{cell.style}".encode())
128
+ # Hash the zero-width escapes
129
+ for idx in sorted(zwe_row.keys()):
130
+ hasher.update(f"{idx}:{zwe_row[idx]}".encode())
131
+ return hasher.hexdigest()
132
+
120
133
  def get_max_column_index(row: dict[int, Char], zwe_row: dict[int, str]) -> int:
121
134
  """Return max used column index, ignoring trailing unstyled whitespace."""
122
- return max(
123
- {
124
- index
125
- for index, cell in row.items()
126
- if cell.char != " " or style_string_has_style[cell.style]
127
- }
128
- # Lag ZWE indices by one, as one could exist after the last line character
129
- # but we don't want that to count towards the line width
130
- | {x - 1 for x in zwe_row}
131
- | {0}
132
- )
135
+ max_idx = 0
136
+ for idx, cell in row.items():
137
+ if cell.char != " " or style_string_has_style[cell.style]:
138
+ max_idx = max(max_idx, idx)
139
+ for idx in zwe_row:
140
+ max_idx = max(max_idx, idx - 1)
141
+ return max_idx
133
142
 
134
143
  # Render for the first time: reset styling.
135
144
  if not previous_screen:
@@ -169,6 +178,14 @@ def _output_screen_diff(
169
178
  zwe_row = screen.zero_width_escapes[y]
170
179
  previous_zwe_row = previous_screen.zero_width_escapes[y]
171
180
 
181
+ # Quick comparison using row hashes
182
+ new_hash = hash_screen_row(new_row, zwe_row)
183
+ prev_hash = hash_screen_row(previous_row, previous_zwe_row)
184
+
185
+ if new_hash == prev_hash:
186
+ # Rows are identical, skip to next row
187
+ continue
188
+
172
189
  new_max_line_len = min(width - 1, get_max_column_index(new_row, zwe_row))
173
190
  previous_max_line_len = min(
174
191
  width - 1, get_max_column_index(previous_row, previous_zwe_row)
@@ -275,26 +292,34 @@ class Renderer(PtkRenderer):
275
292
  )
276
293
  self._extended_keys_enabled = False
277
294
  self._private_sixel_colors_enabled = False
295
+ self._sgr_pixel_enabled = False
278
296
  self.extend_height = to_filter(extend_height)
279
297
  self.extend_width = to_filter(extend_width)
280
298
 
281
299
  def reset(self, _scroll: bool = False, leave_alternate_screen: bool = True) -> None:
282
300
  """Reset the output."""
283
- if isinstance(self.output, Vt100_Output):
301
+ output = self.output
302
+ if isinstance(output, Vt100_Output):
284
303
  # Disable extended keys before resetting the output
285
- self.output.disable_extended_keys()
304
+ output.disable_extended_keys()
286
305
  self._extended_keys_enabled = False
287
306
 
288
307
  # Disable private sixel colors before resetting the output
289
- self.output.disable_private_sixel_colors()
308
+ output.disable_private_sixel_colors()
290
309
  self._private_sixel_colors_enabled = False
291
310
 
311
+ # Disable sgr pixel mode
312
+ output.disable_sgr_pixel()
313
+ self._sgr_pixel_enabled = False
314
+
292
315
  super().reset(_scroll, leave_alternate_screen)
293
316
 
294
317
  def render(
295
318
  self, app: Application[Any], layout: Layout, is_done: bool = False
296
319
  ) -> None:
297
320
  """Render the current interface to the output."""
321
+ from euporie.core.app.app import BaseApp
322
+
298
323
  output = self.output
299
324
  self.app = app
300
325
 
@@ -320,13 +345,29 @@ class Renderer(PtkRenderer):
320
345
  output.enable_mouse_support()
321
346
  self._mouse_support_enabled = True
322
347
 
348
+ if (
349
+ isinstance(output, Vt100_Output)
350
+ and isinstance(app, BaseApp)
351
+ and app.term_sgr_pixel
352
+ ):
353
+ output.enable_sgr_pixel()
354
+ self._sgr_pixel_enabled = True
355
+
323
356
  elif not needs_mouse_support and self._mouse_support_enabled:
324
357
  output.disable_mouse_support()
325
358
  self._mouse_support_enabled = False
326
359
 
360
+ if (
361
+ isinstance(output, Vt100_Output)
362
+ and isinstance(app, BaseApp)
363
+ and (app.term_sgr_pixel or self._sgr_pixel_enabled)
364
+ ):
365
+ output.disable_sgr_pixel()
366
+ self._sgr_pixel_enabled = False
367
+
327
368
  # Ensable extended keys
328
- if not self._extended_keys_enabled and isinstance(self.output, Vt100_Output):
329
- self.output.enable_extended_keys()
369
+ if not self._extended_keys_enabled and isinstance(output, Vt100_Output):
370
+ output.enable_extended_keys()
330
371
  self._extended_keys_enabled = True
331
372
 
332
373
  # Ensable private sixel graphic color registers
euporie/core/style.py CHANGED
@@ -4,16 +4,19 @@ from __future__ import annotations
4
4
 
5
5
  import logging
6
6
  from colorsys import hls_to_rgb, rgb_to_hls
7
- from functools import partial
7
+ from functools import cache, partial
8
8
  from typing import TYPE_CHECKING
9
9
 
10
10
  from prompt_toolkit.cache import SimpleCache
11
11
  from prompt_toolkit.styles.defaults import default_ui_style
12
12
  from prompt_toolkit.styles.style import Style
13
+ from pygments.styles import get_style_by_name as pyg_get_style_by_name
13
14
 
14
15
  if TYPE_CHECKING:
15
16
  from typing import Any
16
17
 
18
+ from pygments.style import Style as PygmentsStyle
19
+
17
20
 
18
21
  log = logging.getLogger(__name__)
19
22
 
@@ -207,9 +210,9 @@ IPYWIDGET_STYLE = [
207
210
  class ColorPaletteColor:
208
211
  """A representation of a color with adjustment methods."""
209
212
 
210
- _cache: SimpleCache[
211
- tuple[str, float, float, float, bool], ColorPaletteColor
212
- ] = SimpleCache()
213
+ _cache: SimpleCache[tuple[str, float, float, float, bool], ColorPaletteColor] = (
214
+ SimpleCache()
215
+ )
213
216
 
214
217
  def __init__(self, base: str, _base_override: str = "") -> None:
215
218
  """Create a new color.
@@ -440,8 +443,8 @@ def build_style(
440
443
  # Statusbar
441
444
  # "status": f"fg:{cp.fg.more(0.05)} bg:{cp.bg.less(0.15)}",
442
445
  "status": f"fg:{cp.fg.more(0.05)} bg:{cp.bg.more(0.05)}",
443
- "status-field": f"fg:{cp.fg.more(0.1)} bg:{cp.bg.more(0.1)}",
444
- "status-sep": f"fg:{cp.bg.more(0.05)} bg:{cp.bg.more(0.1)}",
446
+ "status-field": f"bg:{cp.fg.more(0.1)} fg:{cp.bg.more(0.1)} reverse",
447
+ "status-sep": f"bg:{cp.bg.more(0.05)} fg:{cp.bg.more(0.1)} reverse",
445
448
  # Menus & Menu bar
446
449
  "menu": f"fg:{cp.fg.more(0.05)} bg:{cp.bg.more(0.05)}",
447
450
  "menu bar": f"bg:{cp.bg.less(0.15)}",
@@ -450,11 +453,11 @@ def build_style(
450
453
  "menu shortcut disabled": f"fg:{cp.fg.more(0.4).towards(cp.bg, 0.5)}",
451
454
  "menu prefix": f"fg:{cp.fg.more(0.2)}",
452
455
  "menu prefix disabled": f"fg:{cp.fg.more(0.2).towards(cp.bg, 0.5)}",
453
- "menu selection": f"fg:{cp.hl.more(1)} bg:{cp.hl}",
454
- "menu selection shortcut": f"fg:{cp.hl.more(1).more(0.05)} bg:{cp.hl}",
455
- "menu selection prefix": f"fg:{cp.hl.more(1).more(0.05)} bg:{cp.hl}",
456
+ "menu selection": f"bg:{cp.hl.more(1)} fg:{cp.hl} reverse",
457
+ "menu selection shortcut": f"bg:{cp.hl.more(1).more(0.05)} fg:{cp.hl} reverse",
458
+ "menu selection prefix": f"bg:{cp.hl.more(1).more(0.05)} fg:{cp.hl} reverse",
456
459
  "menu border": f"fg:{cp.bg.more(0.15)} bg:{cp.bg.more(0.05)}",
457
- "menu border selection": f"bg:{cp.hl}",
460
+ "menu border selection": f"fg:{cp.bg.more(0.15)} bg:{cp.hl} noreverse",
458
461
  # Tab bar
459
462
  "app-tab-bar": f"bg:{cp.bg.less(0.15)}",
460
463
  "app-tab-bar border": f"fg:{cp.bg.more(0.1)}",
@@ -466,10 +469,10 @@ def build_style(
466
469
  # Tabs
467
470
  "loading": "fg:#888888",
468
471
  # Buffer
469
- "line-number": f"fg:{cp.fg.more(0.5)} bg:{cp.bg.more(0.05)}",
470
- "line-number.current": f"bold orange bg:{cp.bg.more(0.1)}",
471
- "line-number edge": f"fg:{cp.bg.darker(0.1)}",
472
- "line-number.current edge": f"fg:{cp.bg.darker(0.1)}",
472
+ "line-number": f"bg:{cp.fg.more(0.5)} fg:{cp.bg.more(0.05)} reverse",
473
+ "line-number.current": f"bg:orange fg:{cp.bg.more(0.1)} bold",
474
+ "line-number edge": f"bg:{cp.bg.darker(0.1)}",
475
+ "line-number.current edge": f"bg:{cp.bg.darker(0.1)}",
473
476
  "cursor-line": f"bg:{cp.bg.more(0.05)}",
474
477
  "cursor-line search": f"bg:{cp.bg.more(0.02)}",
475
478
  "cursor-line search.current": f"bg:{cp.bg.more(0.02)}",
@@ -479,13 +482,11 @@ def build_style(
479
482
  "matching-bracket.other": "fg:yellow bold",
480
483
  "trailing-whitespace": f"fg:{cp.fg.more(0.66)}",
481
484
  "tab": f"fg:{cp.fg.more(0.66)}",
482
- # Search
485
+ # Search results
483
486
  "search": f"bg:{cp.bg.more(0.05)}",
484
487
  "search.current": f"bg:{cp.bg.more(0.05)}",
485
488
  "incsearch": "bg:ansibrightyellow",
486
489
  "incsearch.current": "bg:ansibrightgreen",
487
- "search-toolbar": f"fg:{cp.fg.more(0.05)} bg:{cp.bg.more(0.05)}",
488
- "search-toolbar.title": f"fg:{cp.fg.more(0.1)} bg:{cp.bg.more(0.1)}",
489
490
  # Inputs
490
491
  "kernel-input": f"fg:default bg:{cp.bg.more(0.02)}",
491
492
  # Cells
@@ -496,20 +497,22 @@ def build_style(
496
497
  "cell border edit": f"fg:{cp.hl.adjust(hue=-0.3333, rel=False)}",
497
498
  "cell input prompt": "fg:blue",
498
499
  "cell output prompt": "fg:red",
499
- "cell show outputs": "bg:#888",
500
- "cell show inputs": "bg:#888",
500
+ "cell show outputs": f"bg:{cp.fg.more(0.5)} fg:{cp.bg.more(0.05)} reverse",
501
+ "cell show inputs": f"bg:{cp.fg.more(0.5)} fg:{cp.bg.more(0.05)} reverse",
502
+ "cell show inputs border": f"bg:{cp.bg.darker(0.1)} fg:{cp.bg.more(0.05)} reverse",
503
+ "cell show outputs border": f"bg:{cp.bg.darker(0.1)} fg:{cp.bg.more(0.05)} reverse",
501
504
  # Scrollbars
502
505
  "scrollbar": f"fg:{cp.bg.more(0.75)} bg:{cp.bg.more(0.15)}",
503
506
  "scrollbar.background": f"fg:{cp.bg.more(0.75)} bg:{cp.bg.more(0.15)}",
504
- "scrollbar.arrow": f"fg:{cp.bg.more(0.75)} bg:{cp.bg.more(0.20)}",
507
+ "scrollbar.arrow": f"bg:{cp.bg.more(0.75)} fg:{cp.bg.more(0.20)} reverse",
505
508
  "scrollbar.start": "",
506
509
  # "scrollbar.start": f"fg:{cp.bg.more(0.75)} bg:{cp.bg.more(0.25)}",
507
- "scrollbar.button": f"fg:{cp.bg.more(0.75)} bg:{cp.bg.more(0.75)}",
508
- "scrollbar.end": f"fg:{cp.bg.more(0.15)} bg:{cp.bg.more(0.75)}",
510
+ "scrollbar.button": f"bg:{cp.bg.more(0.75)} fg:{cp.bg.more(0.75)} reverse",
511
+ "scrollbar.end": f"bg:{cp.bg.more(0.15)} fg:{cp.bg.more(0.75)} reverse",
509
512
  # Overflow margin
510
513
  "overflow": f"fg:{cp.fg.more(0.5)}",
511
514
  # Dialogs
512
- "dialog dialog-title": f"fg:white bg:{cp.hl.darker(0.25)} bold",
515
+ "dialog dialog-title": f"bg:white fg:{cp.hl.darker(0.25)} bold reverse",
513
516
  "dialog": f"fg:{cp.fg.base} bg:{cp.bg.darker(0.1)}",
514
517
  "dialog text-area": f"bg:{cp.bg.lighter(0.05)}",
515
518
  "dialog input text text-area": f"fg:default bg:{cp.bg.less(0.1)}",
@@ -517,6 +520,17 @@ def build_style(
517
520
  "dialog border": f"fg:{cp.bg.darker(0.1).more(0.1)}",
518
521
  # Horizontals rule
519
522
  "hr": "fg:ansired",
523
+ # Toolbars
524
+ "toolbar": f"fg:{cp.fg.more(0.05)} bg:{cp.bg.more(0.05)}",
525
+ "toolbar.title": f"fg:{cp.fg.more(0.1)} bg:{cp.bg.more(0.1)}",
526
+ # Search bar
527
+ "search-toolbar": f"fg:{cp.fg.more(0.05)} bg:{cp.bg.more(0.05)}",
528
+ # Command bar
529
+ "toolbar menu": f"fg:{cp.fg.more(0.05)} bg:{cp.bg.more(0.05)}",
530
+ "toolbar menu completion": f"fg:{cp.fg.more(0.1)} bg:{cp.bg.more(0.1)}",
531
+ "toolbar menu completion current": f"fg:{cp.hl} bg:{cp.fg} reverse",
532
+ "toolbar menu overflow": f"fg:{cp.fg.more(0.5)}",
533
+ "toolbar menu meta": f"bg:{cp.bg.more(0.25)} bold",
520
534
  # Completions menu
521
535
  "menu completion-keyword": "fg:#d700af",
522
536
  "menu completion-function": "fg:#005faf",
@@ -528,28 +542,28 @@ def build_style(
528
542
  "menu completion-path": "fg:#aa8800",
529
543
  "menu completion-dict-key": "fg:#ddbb00",
530
544
  "menu selection completion-keyword": (
531
- f"fg:{ColorPaletteColor('#d700af').lighter(0.75)}"
545
+ f"bg:{ColorPaletteColor('#d700af').lighter(0.75)} fg:{cp.hl} reverse"
532
546
  ),
533
547
  "menu selection completion-function": (
534
- f"fg:{ColorPaletteColor('#005faf').lighter(0.75)}"
548
+ f"bg:{ColorPaletteColor('#005faf').lighter(0.75)} fg:{cp.hl} reverse"
535
549
  ),
536
550
  "menu selection completion-class": (
537
- f"fg:{ColorPaletteColor('#008700').lighter(0.75)}"
551
+ f"bg:{ColorPaletteColor('#008700').lighter(0.75)} fg:{cp.hl} reverse"
538
552
  ),
539
553
  "menu selection completion-statement": (
540
- f"fg:{ColorPaletteColor('#5f0000').lighter(0.75)}"
554
+ f"bg:{ColorPaletteColor('#5f0000').lighter(0.75)} fg:{cp.hl} reverse"
541
555
  ),
542
556
  "menu selection completion-instance": (
543
- f"fg:{ColorPaletteColor('#d75f00').lighter(0.75)}"
557
+ f"bg:{ColorPaletteColor('#d75f00').lighter(0.75)} fg:{cp.hl} reverse"
544
558
  ),
545
559
  "menu selection completion-module": (
546
- f"fg:{ColorPaletteColor('#d70000').lighter(0.75)}"
560
+ f"bg:{ColorPaletteColor('#d70000').lighter(0.75)} fg:{cp.hl} reverse"
547
561
  ),
548
562
  "menu selection completion-magic": (
549
- f"fg:{ColorPaletteColor('#888888').lighter(0.75)}"
563
+ f"bg:{ColorPaletteColor('#888888').lighter(0.75)} fg:{cp.hl} reverse"
550
564
  ),
551
565
  "menu selection completion-path": (
552
- f"fg:{ColorPaletteColor('#aa8800').lighter(0.75)}"
566
+ f"bg:{ColorPaletteColor('#aa8800').lighter(0.75)} fg:{cp.hl} reverse"
553
567
  ),
554
568
  # Log
555
569
  "log.level.nonset": "fg:grey",
@@ -565,7 +579,7 @@ def build_style(
565
579
  "file-browser face": f"bg:{cp.bg.lighter(0.1)}",
566
580
  "file-browser face row alt-row": f"bg:{cp.bg.lighter(0.1).more(0.01)}",
567
581
  "file-browser face row hovered": f"bg:{cp.bg.more(0.2)}",
568
- "file-browser face row selection": f"bg:{cp.hl}",
582
+ "file-browser face row selection": f"bg:{cp.fg} fg:{cp.hl} reverse",
569
583
  # Shortcuts
570
584
  "shortcuts.group": f"bg:{cp.bg.more(0.4)} bold underline",
571
585
  # "shortcuts.row": f"bg:{cp.bg.base} nobold",
@@ -574,7 +588,7 @@ def build_style(
574
588
  # Palette
575
589
  "palette.item": f"fg:{cp.fg.more(0.05)} bg:{cp.bg.more(0.05)}",
576
590
  "palette.item.alt": f"bg:{cp.bg.more(0.15)}",
577
- "palette.item.selected": f"fg:{cp.hl.more(1)} bg:{cp.hl}",
591
+ "palette.item.selected": f"bg:{cp.hl.more(1)} fg:{cp.hl} reverse",
578
592
  # Pager
579
593
  "pager": f"bg:{cp.bg.more(0.05)}",
580
594
  "pager.border": f"fg:{cp.bg.towards(cp.ansiblack, 0.15)} reverse",
@@ -594,13 +608,13 @@ def build_style(
594
608
  "side_bar border outer": f"bg:{cp.bg}",
595
609
  "side_bar buttons": f"bg:{cp.bg.less(0.15)}",
596
610
  "side_bar buttons hovered": f"fg:{cp.hl}",
597
- "side_bar buttons selection": f"fg:{cp.fg} bg:{cp.hl}",
598
- "side_bar buttons separator": f"fg:{cp.bg.less(0.15)} bg:{cp.bg.less(0.15)}",
611
+ "side_bar buttons separator": f"bg:{cp.bg.less(0.15)} fg:{cp.bg.less(0.15)}",
612
+ "side_bar buttons selection": f"bg:{cp.fg} fg:{cp.hl} reverse",
599
613
  "side_bar buttons separator selection before": (
600
- f"fg:{cp.bg.less(0.15)} bg:{cp.hl}"
614
+ f"bg:{cp.bg.less(0.15)} fg:{cp.hl} reverse"
601
615
  ),
602
616
  "side_bar buttons separator selection after": (
603
- f"fg:{cp.hl} bg:{cp.bg.less(0.15)}"
617
+ f"fg:{cp.hl} bg:{cp.bg.less(0.15)} noreverse"
604
618
  ),
605
619
  # Tabbed split
606
620
  "tabbed-split border": f"fg:{cp.bg.more(0.2)}",
@@ -675,9 +689,9 @@ def build_style(
675
689
  "text-area focused selected": "reverse",
676
690
  # Buttons
677
691
  "input button face": f"fg:default bg:{cp.bg.more(0.05)}",
678
- "input button face hovered": f"fg:default bg:{cp.bg.more(0.2)}",
679
- "input button face selection": f"bg:{cp.bg.more(0.05)}",
680
- "input button face focused": f"fg:default bg:{cp.bg.towards(cp.hl, 0.1)}",
692
+ "input button face hovered": f"fg:{cp.fg} bg:{cp.bg.more(0.2)}",
693
+ "input button face selection": f"bg:{cp.fg} fg:{cp.bg.more(0.05)} reverse",
694
+ "input button face focused": f"bg:{cp.fg} fg:{cp.bg.towards(cp.hl, 0.1)} reverse",
681
695
  # Input widgets
682
696
  "input border top": f"fg:{cp.bg.lighter(0.5)}",
683
697
  "input border left": f"fg:{cp.bg.lighter(0.5)}",
@@ -751,3 +765,9 @@ def build_style(
751
765
  }
752
766
 
753
767
  return Style.from_dict(style_dict)
768
+
769
+
770
+ @cache
771
+ def get_style_by_name(name: str) -> type[PygmentsStyle]:
772
+ """Get Pygments style, caching the result."""
773
+ return pyg_get_style_by_name(name)