euporie 2.8.4__py3-none-any.whl → 2.8.6__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 (131) 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 +58 -62
  5. euporie/core/__init__.py +1 -1
  6. euporie/core/__main__.py +28 -11
  7. euporie/core/_settings.py +109 -0
  8. euporie/core/app/__init__.py +3 -0
  9. euporie/core/app/_commands.py +95 -0
  10. euporie/core/app/_settings.py +457 -0
  11. euporie/core/{app.py → app/app.py} +212 -576
  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 +205 -0
  19. euporie/core/bars/menu.py +258 -0
  20. euporie/core/{widgets → bars}/search.py +20 -16
  21. euporie/core/{widgets → bars}/status.py +6 -23
  22. euporie/core/clipboard.py +19 -80
  23. euporie/core/comm/base.py +8 -6
  24. euporie/core/comm/ipywidgets.py +16 -7
  25. euporie/core/comm/registry.py +2 -1
  26. euporie/core/commands.py +10 -20
  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 +116 -53
  31. euporie/core/convert/formats/__init__.py +31 -0
  32. euporie/core/convert/formats/ansi.py +9 -23
  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 +4 -6
  39. euporie/core/convert/utils.py +41 -4
  40. euporie/core/diagnostics.py +2 -2
  41. euporie/core/filters.py +98 -40
  42. euporie/core/format.py +2 -3
  43. euporie/core/ft/ansi.py +1 -1
  44. euporie/core/ft/html.py +12 -21
  45. euporie/core/ft/table.py +1 -3
  46. euporie/core/ft/utils.py +4 -1
  47. euporie/core/graphics.py +386 -133
  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} +45 -108
  53. euporie/core/kernel/manager.py +114 -0
  54. euporie/core/key_binding/bindings/__init__.py +1 -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 +1 -6
  58. euporie/core/key_binding/bindings/mouse.py +2 -2
  59. euporie/core/key_binding/bindings/terminal.py +193 -0
  60. euporie/core/key_binding/key_processor.py +43 -2
  61. euporie/core/key_binding/registry.py +2 -0
  62. euporie/core/key_binding/utils.py +22 -2
  63. euporie/core/keys.py +7156 -93
  64. euporie/core/layout/cache.py +3 -3
  65. euporie/core/layout/containers.py +48 -4
  66. euporie/core/layout/decor.py +2 -2
  67. euporie/core/layout/mouse.py +1 -1
  68. euporie/core/layout/print.py +2 -1
  69. euporie/core/layout/scroll.py +39 -34
  70. euporie/core/log.py +76 -64
  71. euporie/core/lsp.py +118 -24
  72. euporie/core/margins.py +1 -1
  73. euporie/core/path.py +62 -13
  74. euporie/core/renderer.py +58 -17
  75. euporie/core/style.py +57 -39
  76. euporie/core/suggest.py +103 -85
  77. euporie/core/tabs/__init__.py +32 -0
  78. euporie/core/tabs/_settings.py +113 -0
  79. euporie/core/tabs/base.py +80 -470
  80. euporie/core/tabs/kernel.py +419 -0
  81. euporie/core/tabs/notebook.py +24 -101
  82. euporie/core/utils.py +92 -15
  83. euporie/core/validation.py +1 -1
  84. euporie/core/widgets/_settings.py +188 -0
  85. euporie/core/widgets/cell.py +19 -50
  86. euporie/core/widgets/cell_outputs.py +25 -36
  87. euporie/core/widgets/decor.py +11 -41
  88. euporie/core/widgets/dialog.py +62 -27
  89. euporie/core/widgets/display.py +12 -15
  90. euporie/core/widgets/file_browser.py +2 -23
  91. euporie/core/widgets/forms.py +8 -5
  92. euporie/core/widgets/inputs.py +13 -70
  93. euporie/core/widgets/layout.py +2 -1
  94. euporie/core/widgets/logo.py +49 -0
  95. euporie/core/widgets/menu.py +10 -8
  96. euporie/core/widgets/pager.py +6 -10
  97. euporie/core/widgets/palette.py +6 -6
  98. euporie/hub/app.py +52 -35
  99. euporie/notebook/_commands.py +24 -0
  100. euporie/notebook/_settings.py +107 -0
  101. euporie/notebook/app.py +49 -171
  102. euporie/notebook/filters.py +1 -1
  103. euporie/notebook/tabs/__init__.py +46 -7
  104. euporie/notebook/tabs/_commands.py +714 -0
  105. euporie/notebook/tabs/_settings.py +32 -0
  106. euporie/notebook/tabs/display.py +4 -4
  107. euporie/notebook/tabs/edit.py +11 -44
  108. euporie/notebook/tabs/json.py +5 -5
  109. euporie/notebook/tabs/log.py +1 -18
  110. euporie/notebook/tabs/notebook.py +11 -660
  111. euporie/notebook/widgets/_commands.py +11 -0
  112. euporie/notebook/widgets/_settings.py +19 -0
  113. euporie/notebook/widgets/side_bar.py +14 -34
  114. euporie/preview/_settings.py +104 -0
  115. euporie/preview/app.py +6 -31
  116. euporie/preview/tabs/notebook.py +6 -72
  117. euporie/web/__init__.py +1 -0
  118. euporie/web/tabs/__init__.py +14 -0
  119. euporie/web/tabs/web.py +11 -6
  120. euporie/web/widgets/__init__.py +1 -0
  121. euporie/web/widgets/webview.py +5 -15
  122. {euporie-2.8.4.dist-info → euporie-2.8.6.dist-info}/METADATA +10 -8
  123. euporie-2.8.6.dist-info/RECORD +175 -0
  124. {euporie-2.8.4.dist-info → euporie-2.8.6.dist-info}/WHEEL +1 -1
  125. {euporie-2.8.4.dist-info → euporie-2.8.6.dist-info}/entry_points.txt +2 -2
  126. {euporie-2.8.4.dist-info → euporie-2.8.6.dist-info}/licenses/LICENSE +1 -1
  127. euporie/core/launch.py +0 -64
  128. euporie/core/terminal.py +0 -522
  129. euporie-2.8.4.dist-info/RECORD +0 -147
  130. {euporie-2.8.4.data → euporie-2.8.6.data}/data/share/applications/euporie-console.desktop +0 -0
  131. {euporie-2.8.4.data → euporie-2.8.6.data}/data/share/applications/euporie-notebook.desktop +0 -0
@@ -3,7 +3,6 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import logging
6
- import traceback
7
6
  from abc import ABCMeta, abstractmethod
8
7
  from functools import partial
9
8
  from pathlib import Path
@@ -34,26 +33,26 @@ from prompt_toolkit.layout.screen import WritePosition
34
33
  from prompt_toolkit.mouse_events import MouseButton, MouseEventType
35
34
  from prompt_toolkit.widgets.base import Label
36
35
 
37
- from euporie.core.app import get_app
36
+ from euporie.core.app.current import get_app
38
37
  from euporie.core.border import (
39
38
  FullLine,
40
39
  LowerLeftHalfLine,
41
40
  UpperRightHalfLine,
42
41
  )
43
42
  from euporie.core.commands import add_cmd
44
- from euporie.core.filters import tab_has_focus
43
+ from euporie.core.filters import tab_can_save
45
44
  from euporie.core.ft.utils import FormattedTextAlign, align, lex
46
45
  from euporie.core.key_binding.registry import register_bindings
47
46
  from euporie.core.layout.containers import HSplit, VSplit, Window
48
47
  from euporie.core.layout.decor import FocusedStyle
49
- from euporie.core.tabs.base import Tab
50
48
  from euporie.core.widgets.decor import Border, Shadow
51
49
  from euporie.core.widgets.file_browser import FileBrowser
52
50
  from euporie.core.widgets.forms import Button, LabelledWidget, Select, Text
53
51
  from euporie.core.widgets.layout import Box
54
52
 
55
53
  if TYPE_CHECKING:
56
- from typing import Any, Callable, Hashable
54
+ from collections.abc import Hashable
55
+ from typing import Any, Callable
57
56
 
58
57
  from prompt_toolkit.buffer import Buffer
59
58
  from prompt_toolkit.data_structures import Point
@@ -64,9 +63,9 @@ if TYPE_CHECKING:
64
63
  from prompt_toolkit.layout.layout import FocusableElement
65
64
  from prompt_toolkit.mouse_events import MouseEvent
66
65
 
67
- from euporie.core.app import BaseApp
68
- from euporie.core.tabs.base import KernelTab
69
- from euporie.core.widgets.file_browser import FileBrowserControl
66
+ from euporie.core.app.app import BaseApp
67
+ from euporie.core.tabs.base import Tab
68
+ from euporie.core.tabs.kernel import KernelTab
70
69
 
71
70
  log = logging.getLogger(__name__)
72
71
 
@@ -139,8 +138,12 @@ class DialogTitleControl(UIControl):
139
138
  dl_width, max_y
140
139
  ).preferred
141
140
  # Calculate new dialog position
142
- new_x = max(1, min(gx - self.drag_start.x, max_x - dl_width))
143
- new_y = max(1, min(gy - self.drag_start.y, max_y - dl_height))
141
+ new_x = max(
142
+ 1, min(gx - self.drag_start.x, max_x - dl_width + 1)
143
+ )
144
+ new_y = max(
145
+ 1, min(gy - self.drag_start.y, max_y - dl_height + 1)
146
+ )
144
147
  # Move dialog
145
148
  self.dialog.left = new_x - 1
146
149
  self.dialog.top = new_y - 1
@@ -409,7 +412,7 @@ class AboutDialog(Dialog):
409
412
  @add_cmd()
410
413
  def _about() -> None:
411
414
  """Show the about dialog."""
412
- from euporie.core.current import get_app
415
+ from euporie.core.app.current import get_app
413
416
 
414
417
  if dialog := get_app().dialogs.get("about"):
415
418
  dialog.toggle()
@@ -498,12 +501,13 @@ class OpenFileDialog(FileDialog):
498
501
 
499
502
  self.tab_dd = tab_dd = Dropdown(options=[])
500
503
 
501
- def _update_options(fb: FileBrowserControl) -> None:
502
- tabs = get_app().get_file_tabs(path) if (path := fb.path).is_file() else []
504
+ def _update_options(path: Path) -> None:
505
+ tabs = get_app().get_file_tabs(path) if path.is_file() else []
503
506
  tab_dd.options = tabs
504
507
  tab_dd.labels = [tab.name for tab in tabs]
505
508
 
506
- self.file_browser.control.on_select += _update_options
509
+ self.file_browser.control.on_select += lambda fb: _update_options(fb.path)
510
+ self.filepath.buffer.on_text_changed += lambda b: _update_options(Path(b.text))
507
511
 
508
512
  if isinstance(self.body, HSplit):
509
513
  self.body.children.append(
@@ -543,7 +547,7 @@ class OpenFileDialog(FileDialog):
543
547
  self.file_browser.control.dir = path
544
548
  elif path.is_file():
545
549
  self.hide()
546
- self.app.open_file(path, tab_class=self.tab_dd.value)
550
+ self.app.open_file(path, tab_class=self.tab_dd.value.tab_class)
547
551
  return
548
552
  else:
549
553
  self.show(
@@ -556,7 +560,7 @@ class OpenFileDialog(FileDialog):
556
560
  @add_cmd(menu_title="Open File…")
557
561
  def _open_file() -> None:
558
562
  """Open a file."""
559
- from euporie.core.current import get_app
563
+ from euporie.core.app.current import get_app
560
564
 
561
565
  if dialog := get_app().dialogs.get("open-file"):
562
566
  dialog.show()
@@ -565,7 +569,7 @@ class OpenFileDialog(FileDialog):
565
569
 
566
570
  register_bindings(
567
571
  {
568
- "euporie.core.app.BaseApp": {
572
+ "euporie.core.app.app:BaseApp": {
569
573
  "open-file": "c-o",
570
574
  }
571
575
  }
@@ -598,11 +602,11 @@ class SaveAsDialog(FileDialog):
598
602
  @staticmethod
599
603
  @add_cmd(
600
604
  menu_title="Save As…",
601
- filter=tab_has_focus,
605
+ filter=tab_can_save,
602
606
  )
603
607
  def _save_as() -> None:
604
608
  """Save the current file at a new location."""
605
- from euporie.core.current import get_app
609
+ from euporie.core.app.current import get_app
606
610
 
607
611
  app = get_app()
608
612
  if dialog := app.dialogs.get("save-as"):
@@ -612,7 +616,7 @@ class SaveAsDialog(FileDialog):
612
616
 
613
617
  register_bindings(
614
618
  {
615
- "euporie.core.app.BaseApp": {
619
+ "euporie.core.app.app:BaseApp": {
616
620
  "save-as": ("A-s"),
617
621
  }
618
622
  }
@@ -773,6 +777,8 @@ class ErrorDialog(Dialog):
773
777
 
774
778
  def load(self, exception: Exception | None = None, when: str = "") -> None:
775
779
  """Load dialog body & buttons."""
780
+ import traceback
781
+
776
782
  from euporie.core.margins import MarginContainer, ScrollbarMargin
777
783
  from euporie.core.widgets.formatted_text_area import FormattedTextArea
778
784
  from euporie.core.widgets.forms import Checkbox
@@ -798,7 +804,7 @@ class ErrorDialog(Dialog):
798
804
  ("bold", f" when {when}" if when else ""),
799
805
  ("bold", ":"),
800
806
  ("", "\n\n"),
801
- ("fg:ansired", exception.__repr__()),
807
+ ("fg:ansired", repr(exception)),
802
808
  ("", "\n"),
803
809
  ],
804
810
  )
@@ -828,7 +834,36 @@ class ErrorDialog(Dialog):
828
834
  def _copy_traceback() -> None:
829
835
  self.app.clipboard.set_data(ClipboardData(tb_text))
830
836
 
831
- self.buttons = {"Close": None, "Copy Traceback": _copy_traceback}
837
+ def _report() -> None:
838
+ import webbrowser
839
+ from importlib.metadata import metadata
840
+ from urllib.parse import urlencode, urlparse, urlunparse
841
+
842
+ data = metadata("euporie")
843
+ if issue_url := dict(
844
+ x.split(", ", 1) for x in data.json["project_url"]
845
+ ).get("Issues"):
846
+ parsed_url = urlparse(issue_url)
847
+ url = urlunparse(
848
+ parsed_url._replace(
849
+ path=f"{parsed_url.path.rstrip('/')}/new"
850
+ )._replace(
851
+ query=urlencode(
852
+ {
853
+ "title": f"Error: {exception!r}",
854
+ "body": "(Please describe what you did)\n\n"
855
+ f"## Traceback\n\n```python\n{tb_text}\n```\n",
856
+ }
857
+ )
858
+ )
859
+ )
860
+ webbrowser.open(url, new=2, autoraise=True)
861
+
862
+ self.buttons = {
863
+ "Report on GitHub": _report,
864
+ "Copy Traceback": _copy_traceback,
865
+ "Close": None,
866
+ }
832
867
 
833
868
 
834
869
  class UnsavedDialog(Dialog):
@@ -859,6 +894,8 @@ class UnsavedDialog(Dialog):
859
894
  tab.save(cb=partial(tab.close, cb))
860
895
 
861
896
  def no_cb() -> None:
897
+ from euporie.core.tabs.base import Tab
898
+
862
899
  assert tab is not None
863
900
  self.hide()
864
901
  Tab.close(tab, cb)
@@ -903,7 +940,7 @@ class ShortcutsDialog(Dialog):
903
940
 
904
941
  def format_key_info(self) -> StyleAndTextTuples:
905
942
  """Generate a table with the current key bindings."""
906
- import importlib
943
+ import pkgutil
907
944
  from textwrap import dedent
908
945
 
909
946
  from prompt_toolkit.formatted_text.base import to_formatted_text
@@ -920,9 +957,7 @@ class ShortcutsDialog(Dialog):
920
957
 
921
958
  for group, bindings in BINDINGS.items():
922
959
  if any(not get_cmd(cmd_name).hidden() for cmd_name in bindings):
923
- mod_name, cls_name = group.rsplit(".", maxsplit=1)
924
- mod = importlib.import_module(mod_name)
925
- app_cls = getattr(mod, cls_name)
960
+ app_cls = pkgutil.resolve_name(group)
926
961
  section_title = (
927
962
  dedent(app_cls.__doc__).strip().split("\n")[0].rstrip(".")
928
963
  )
@@ -965,7 +1000,7 @@ class ShortcutsDialog(Dialog):
965
1000
  @add_cmd()
966
1001
  def _keyboard_shortcuts() -> None:
967
1002
  """Display details of registered key-bindings in a dialog."""
968
- from euporie.core.current import get_app
1003
+ from euporie.core.app.current import get_app
969
1004
 
970
1005
  if dialog := get_app().dialogs.get("shortcuts"):
971
1006
  dialog.toggle()
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import asyncio
5
6
  import logging
6
7
  from functools import partial
7
8
  from math import ceil
@@ -16,9 +17,9 @@ from prompt_toolkit.layout.controls import GetLinePrefixCallable, UIContent, UIC
16
17
  from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
17
18
  from prompt_toolkit.utils import Event, to_str
18
19
 
20
+ from euporie.core.app.current import get_app
19
21
  from euporie.core.commands import add_cmd
20
22
  from euporie.core.convert.datum import Datum
21
- from euporie.core.current import get_app
22
23
  from euporie.core.filters import display_has_focus, scrollable
23
24
  from euporie.core.ft.utils import wrap
24
25
  from euporie.core.graphics import GraphicProcessor
@@ -28,10 +29,10 @@ from euporie.core.key_binding.registry import (
28
29
  )
29
30
  from euporie.core.layout.containers import VSplit, Window
30
31
  from euporie.core.margins import MarginContainer, ScrollbarMargin
31
- from euporie.core.utils import run_in_thread_with_context
32
32
 
33
33
  if TYPE_CHECKING:
34
- from typing import Any, Callable, Iterable
34
+ from collections.abc import Iterable
35
+ from typing import Any, Callable
35
36
 
36
37
  from prompt_toolkit.filters import FilterOrBool
37
38
  from prompt_toolkit.formatted_text import StyleAndTextTuples
@@ -157,7 +158,7 @@ class DisplayControl(UIControl):
157
158
  extend=not self.dont_extend_width(),
158
159
  )
159
160
  if width and height:
160
- key = Datum.add_size(datum, Size(self.height, self.width))
161
+ key = Datum.add_size(datum, Size(height, width))
161
162
  ft = [(f"[Graphic_{key}]", ""), *ft]
162
163
  lines = list(split_lines(ft))
163
164
  if wrap_lines and width:
@@ -194,13 +195,14 @@ class DisplayControl(UIControl):
194
195
  )
195
196
 
196
197
  def render(self) -> None:
197
- """Render the HTML DOM in a thread."""
198
+ """Render the content in a thread."""
198
199
  datum = self.datum
199
200
  wrap_lines = self.wrap_lines()
200
201
 
201
- max_cols, aspect = self.datum.cell_size()
202
- cols = min(max_cols, self.width) if max_cols else self.width
203
- rows = ceil(cols * aspect) if aspect else self.height
202
+ cols = self.preferred_width(self.width)
203
+ rows = self.preferred_height(
204
+ self.width, self.height, wrap_lines=wrap_lines, get_line_prefix=None
205
+ )
204
206
 
205
207
  def _render() -> None:
206
208
  cp = self.color_palette
@@ -215,7 +217,7 @@ class DisplayControl(UIControl):
215
217
  if not self.rendering:
216
218
  self.rendering = True
217
219
  if self.threaded:
218
- run_in_thread_with_context(_render)
220
+ get_app().create_background_task(asyncio.to_thread(_render))
219
221
  else:
220
222
  _render()
221
223
 
@@ -224,12 +226,7 @@ class DisplayControl(UIControl):
224
226
 
225
227
  def preferred_width(self, max_available_width: int) -> int | None:
226
228
  """Calculate and return the preferred width of the control."""
227
- max_cols, aspect = self.datum.cell_size()
228
- if max_cols:
229
- return min(max_cols, max_available_width)
230
- return self._max_line_width_cache[
231
- self.datum, max_available_width, None, self.wrap_lines()
232
- ]
229
+ return max_available_width
233
230
 
234
231
  def preferred_height(
235
232
  self,
@@ -20,9 +20,8 @@ from prompt_toolkit.mouse_events import MouseButton, MouseEvent, MouseEventType
20
20
  from prompt_toolkit.utils import Event
21
21
  from upath import UPath
22
22
 
23
- from euporie.core.app import get_app
23
+ from euporie.core.app.current import get_app
24
24
  from euporie.core.border import InsetGrid
25
- from euporie.core.config import add_setting
26
25
  from euporie.core.layout.containers import HSplit, VSplit, Window
27
26
  from euporie.core.layout.decor import FocusedStyle
28
27
  from euporie.core.margins import MarginContainer, ScrollbarMargin
@@ -41,7 +40,7 @@ if TYPE_CHECKING:
41
40
  from prompt_toolkit.layout.dimension import AnyDimension
42
41
  from upath.core import PT
43
42
 
44
- from euporie.core.widgets.status import StatusBarFields
43
+ from euporie.core.bars.status import StatusBarFields
45
44
 
46
45
  log = logging.getLogger(__name__)
47
46
 
@@ -704,23 +703,3 @@ class FileBrowser:
704
703
  def __pt_status__(self) -> StatusBarFields:
705
704
  """Show the selected or hovered path in the statusbar."""
706
705
  return self.control.__pt_status__()
707
-
708
- # ################################### Settings ####################################
709
-
710
- add_setting(
711
- name="show_file_icons",
712
- flags=["--show-file-icons"],
713
- type_=bool,
714
- title="File icons",
715
- help_="Show file icons in the file manager",
716
- default=False,
717
- schema={
718
- "type": "boolean",
719
- },
720
- description="""
721
- Whether file icons should be shown in the file manager.
722
-
723
- These icons exist in the unicode private use area, and may require custom
724
- fonts such as ``awesome-terminal-fonts`` or ``nerdfonts`` to be installed.
725
- """,
726
- )
@@ -8,7 +8,7 @@ from abc import ABCMeta, abstractmethod
8
8
  from collections import deque
9
9
  from functools import partial
10
10
  from math import ceil, floor
11
- from typing import TYPE_CHECKING, Dict, cast
11
+ from typing import TYPE_CHECKING, cast
12
12
  from weakref import finalize
13
13
 
14
14
  from prompt_toolkit.buffer import ValidationState
@@ -47,8 +47,8 @@ from prompt_toolkit.utils import Event
47
47
  from prompt_toolkit.validation import Validator
48
48
  from prompt_toolkit.widgets.base import TextArea
49
49
 
50
+ from euporie.core.app.current import get_app
50
51
  from euporie.core.border import InsetGrid
51
- from euporie.core.current import get_app
52
52
  from euporie.core.data_structures import DiBool
53
53
  from euporie.core.ft.utils import FormattedTextAlign, align
54
54
  from euporie.core.margins import MarginContainer, ScrollbarMargin
@@ -56,7 +56,8 @@ from euporie.core.widgets.decor import Border, Shadow
56
56
  from euporie.core.widgets.layout import Box, ConditionalSplit
57
57
 
58
58
  if TYPE_CHECKING:
59
- from typing import Any, Callable, Sequence
59
+ from collections.abc import Sequence
60
+ from typing import Any, Callable
60
61
 
61
62
  from prompt_toolkit.buffer import Buffer, BufferAcceptHandler
62
63
  from prompt_toolkit.completion.base import Completer
@@ -728,9 +729,11 @@ class Label:
728
729
  value = self.value
729
730
  data = value() if callable(value) else value
730
731
  if self.html():
732
+ from prompt_toolkit.formatted_text.utils import to_plain_text
733
+
731
734
  from euporie.core.ft.html import HTML
732
735
 
733
- return HTML(data, collapse_root_margin=True, fill=False)
736
+ return HTML(to_plain_text(data), collapse_root_margin=True, fill=False)
734
737
  return data
735
738
 
736
739
  def __pt_container__(self) -> AnyContainer:
@@ -956,7 +959,7 @@ class Progress:
956
959
  return self.container
957
960
 
958
961
 
959
- class SizedMask(Dict[int, bool]):
962
+ class SizedMask(dict[int, bool]):
960
963
  """Mask with restricted number of True items."""
961
964
 
962
965
  def __init__(self, size: int | None = None) -> None:
@@ -49,9 +49,8 @@ from prompt_toolkit.widgets import TextArea
49
49
  from prompt_toolkit.widgets.toolbars import SearchToolbar
50
50
  from pygments.lexers import ClassNotFound, get_lexer_by_name
51
51
 
52
+ from euporie.core.app.current import get_app
52
53
  from euporie.core.commands import add_cmd
53
- from euporie.core.config import add_setting
54
- from euporie.core.current import get_app
55
54
  from euporie.core.diagnostics import Report
56
55
  from euporie.core.filters import buffer_is_code, scrollable
57
56
  from euporie.core.key_binding.registry import (
@@ -71,10 +70,10 @@ from euporie.core.processors import (
71
70
  ShowTrailingWhiteSpaceProcessor,
72
71
  )
73
72
  from euporie.core.suggest import ConditionalAutoSuggestAsync
74
- from euporie.core.widgets.pager import PagerState
75
73
 
76
74
  if TYPE_CHECKING:
77
- from typing import Callable, Sequence
75
+ from collections.abc import Sequence
76
+ from typing import Callable
78
77
 
79
78
  from prompt_toolkit.buffer import BufferAcceptHandler
80
79
  from prompt_toolkit.filters import Filter, FilterOrBool
@@ -89,10 +88,10 @@ if TYPE_CHECKING:
89
88
  from prompt_toolkit.layout.margins import Margin
90
89
  from prompt_toolkit.lexers.base import Lexer
91
90
 
91
+ from euporie.core.bars.status import StatusBarFields
92
92
  from euporie.core.format import Formatter
93
93
  from euporie.core.inspection import Inspector
94
- from euporie.core.tabs.base import KernelTab
95
- from euporie.core.widgets.status import StatusBarFields
94
+ from euporie.core.tabs.kernel import KernelTab
96
95
 
97
96
 
98
97
  log = logging.getLogger(__name__)
@@ -296,7 +295,7 @@ class KernelInput(TextArea):
296
295
  diagnostics=_get_diagnostics,
297
296
  show_diagnostics=to_filter(show_diagnostics),
298
297
  ),
299
- app.config.filter("line_numbers") & self.buffer.multiline,
298
+ app.config.filters.line_numbers & self.buffer.multiline,
300
299
  ),
301
300
  ]
302
301
  right_margins = [OverflowMargin()]
@@ -366,6 +365,8 @@ class KernelInput(TextArea):
366
365
 
367
366
  async def inspect(self, auto: bool = False) -> None:
368
367
  """Get contextual help for the current cursor position in the current cell."""
368
+ from euporie.core.widgets.pager import PagerState
369
+
369
370
  if self.inspector is None:
370
371
  return
371
372
 
@@ -427,64 +428,6 @@ class KernelInput(TextArea):
427
428
  """Return the widget's container."""
428
429
  return self.container
429
430
 
430
- # ################################### Settings ####################################
431
-
432
- add_setting(
433
- name="line_numbers",
434
- flags=["--line-numbers"],
435
- type_=bool,
436
- help_="Show or hide line numbers",
437
- default=True,
438
- description="""
439
- Whether line numbers are shown by default.
440
- """,
441
- hooks=[lambda x: get_app().refresh()],
442
- )
443
-
444
- add_setting(
445
- name="autoformat",
446
- flags=["--autoformat"],
447
- type_=bool,
448
- help_="Automatically re-format code cells when run",
449
- default=False,
450
- description="""
451
- Whether to automatically reformat code cells before they are run.
452
- """,
453
- )
454
-
455
- add_setting(
456
- name="autocomplete",
457
- flags=["--autocomplete"],
458
- type_=bool,
459
- help_="Provide completions suggestions automatically",
460
- default=False,
461
- description="""
462
- Whether to automatically suggestion completions while typing in code cells.
463
- """,
464
- )
465
-
466
- add_setting(
467
- name="autosuggest",
468
- flags=["--autosuggest"],
469
- type_=bool,
470
- help_="Provide line completion suggestions",
471
- default=True,
472
- description="""
473
- Whether to automatically suggestion line content while typing in code cells.
474
- """,
475
- )
476
-
477
- add_setting(
478
- name="autoinspect",
479
- flags=["--autoinspect"],
480
- type_=bool,
481
- help_="Display contextual help automatically",
482
- default=False,
483
- description="""
484
- Whether to automatically display contextual help when navigating through code cells.
485
- """,
486
- )
487
-
488
431
  # ################################### Commands ####################################
489
432
 
490
433
  @staticmethod
@@ -493,7 +436,7 @@ class KernelInput(TextArea):
493
436
  )
494
437
  async def _show_contextual_help() -> None:
495
438
  """Display contextual help."""
496
- from euporie.core.tabs.base import KernelTab
439
+ from euporie.core.tabs.kernel import KernelTab
497
440
 
498
441
  tab = get_app().tab
499
442
  if isinstance(tab, KernelTab) and (input_box := tab.current_input) is not None:
@@ -503,7 +446,7 @@ class KernelInput(TextArea):
503
446
  @add_cmd(filter=buffer_is_code & buffer_has_focus)
504
447
  def _history_prev() -> None:
505
448
  """Get the previous history entry."""
506
- from euporie.core.app import get_app
449
+ from euporie.core.app.current import get_app
507
450
 
508
451
  get_app().current_buffer.history_backward()
509
452
 
@@ -511,7 +454,7 @@ class KernelInput(TextArea):
511
454
  @add_cmd(filter=buffer_is_code & buffer_has_focus)
512
455
  def _history_next() -> None:
513
456
  """Get the next history entry."""
514
- from euporie.core.app import get_app
457
+ from euporie.core.app.current import get_app
515
458
 
516
459
  get_app().current_buffer.history_forward()
517
460
 
@@ -519,8 +462,8 @@ class KernelInput(TextArea):
519
462
  @add_cmd()
520
463
  def _reformat_input() -> None:
521
464
  """Format the contents of the current input field."""
522
- from euporie.core.app import get_app
523
- from euporie.core.tabs.base import KernelTab
465
+ from euporie.core.app.current import get_app
466
+ from euporie.core.tabs.kernel import KernelTab
524
467
 
525
468
  if (
526
469
  (tab := get_app().tab)
@@ -35,7 +35,8 @@ from euporie.core.layout.containers import HSplit, VSplit, Window
35
35
  from euporie.core.widgets.decor import Border
36
36
 
37
37
  if TYPE_CHECKING:
38
- from typing import Any, Callable, Sequence
38
+ from collections.abc import Sequence
39
+ from typing import Any, Callable
39
40
 
40
41
  from prompt_toolkit.filters import FilterOrBool
41
42
  from prompt_toolkit.formatted_text.base import AnyFormattedText, StyleAndTextTuples
@@ -0,0 +1,49 @@
1
+ """Defines a logo widget."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Callable
6
+
7
+ from prompt_toolkit.layout.containers import WindowAlign
8
+ from prompt_toolkit.layout.controls import FormattedTextControl
9
+
10
+ from euporie.core import __logo__
11
+ from euporie.core.filters import has_tabs
12
+ from euporie.core.layout.containers import Window
13
+
14
+ if TYPE_CHECKING:
15
+ from typing import Any
16
+
17
+ from prompt_toolkit.filters import FilterOrBool
18
+ from prompt_toolkit.layout.controls import UIControl
19
+ from prompt_toolkit.layout.dimension import AnyDimension
20
+
21
+
22
+ class Logo(Window):
23
+ """A widget to display the application's logo."""
24
+
25
+ def __init__(
26
+ self,
27
+ content: UIControl | None = None,
28
+ height: AnyDimension = 1,
29
+ width: AnyDimension = 3,
30
+ style: str | Callable[[], str] = "class:menu,logo",
31
+ dont_extend_width: FilterOrBool = True,
32
+ align: WindowAlign | Callable[[], WindowAlign] = WindowAlign.CENTER,
33
+ **kwargs: Any,
34
+ ) -> None:
35
+ """Create a new window with defaults specified."""
36
+ if content is None:
37
+ content = FormattedTextControl(
38
+ [("", f" {__logo__} ")],
39
+ focusable=~has_tabs,
40
+ show_cursor=False,
41
+ )
42
+ super().__init__(
43
+ content=content,
44
+ height=height,
45
+ width=width,
46
+ style=style,
47
+ dont_extend_width=dont_extend_width,
48
+ **kwargs,
49
+ )
@@ -37,14 +37,15 @@ from prompt_toolkit.layout.utils import explode_text_fragments
37
37
  from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
38
38
  from prompt_toolkit.utils import get_cwidth
39
39
 
40
+ from euporie.core.app.current import get_app
41
+ from euporie.core.bars.status import StatusContainer
40
42
  from euporie.core.border import OuterHalfGrid
41
- from euporie.core.current import get_app
42
43
  from euporie.core.layout.containers import HSplit, VSplit, Window
43
44
  from euporie.core.widgets.decor import Shadow
44
- from euporie.core.widgets.status import StatusContainer
45
45
 
46
46
  if TYPE_CHECKING:
47
- from typing import Any, Callable, Iterable, Sequence
47
+ from collections.abc import Iterable, Sequence
48
+ from typing import Any, Callable
48
49
 
49
50
  from prompt_toolkit.filters import Filter, FilterOrBool
50
51
  from prompt_toolkit.formatted_text.base import (
@@ -57,10 +58,10 @@ if TYPE_CHECKING:
57
58
  from prompt_toolkit.layout.containers import AnyContainer
58
59
  from prompt_toolkit.layout.controls import UIControl
59
60
 
60
- from euporie.core.app import BaseApp
61
+ from euporie.core.app.app import BaseApp
62
+ from euporie.core.bars.status import StatusBarFields
61
63
  from euporie.core.border import GridStyle
62
64
  from euporie.core.commands import Command
63
- from euporie.core.widgets.status import StatusBarFields
64
65
 
65
66
 
66
67
  log = logging.getLogger(__name__)
@@ -149,7 +150,7 @@ class MenuItem:
149
150
  """Create a menu item from a command."""
150
151
  return cls(
151
152
  formatted_text=command.menu_title,
152
- handler=command.menu_handler,
153
+ handler=command.run,
153
154
  shortcut=command.key_str,
154
155
  disabled=~command.filter,
155
156
  hidden=command.hidden,
@@ -326,7 +327,6 @@ class MenuBar:
326
327
  def _cancel(event: KeyPressEvent) -> None:
327
328
  """Leave menu."""
328
329
  self.selected_menu = []
329
- log.debug(self.last_focused)
330
330
  self.refocus()
331
331
 
332
332
  # Sub menu navigation.
@@ -521,7 +521,9 @@ class MenuBar:
521
521
  def mouse_handler(index: int, mouse_event: MouseEvent) -> NotImplementedOrNone:
522
522
  focused = self.focused()
523
523
  hover = mouse_event.event_type == MouseEventType.MOUSE_MOVE
524
- if mouse_event.event_type == MouseEventType.MOUSE_DOWN or hover and focused:
524
+ if mouse_event.event_type == MouseEventType.MOUSE_DOWN or (
525
+ hover and focused
526
+ ):
525
527
  # Toggle focus.
526
528
  if not hover and focused and self.selected_menu == [index]:
527
529
  self.selected_menu = []