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/suggest.py CHANGED
@@ -3,7 +3,9 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import logging
6
- from collections import deque
6
+ from collections import defaultdict
7
+ from difflib import SequenceMatcher
8
+ from functools import lru_cache
7
9
  from typing import TYPE_CHECKING
8
10
 
9
11
  from prompt_toolkit.auto_suggest import AutoSuggest, ConditionalAutoSuggest, Suggestion
@@ -15,7 +17,6 @@ if TYPE_CHECKING:
15
17
  from prompt_toolkit.filters import Filter
16
18
  from prompt_toolkit.history import History
17
19
 
18
- from euporie.core.kernel import Kernel
19
20
 
20
21
  log = logging.getLogger(__name__)
21
22
 
@@ -23,99 +24,116 @@ log = logging.getLogger(__name__)
23
24
  class HistoryAutoSuggest(AutoSuggest):
24
25
  """Suggest line completions from a :class:`History` object."""
25
26
 
26
- def __init__(self, history: History, cache_size: int = 100_000) -> None:
27
+ def __init__(self, history: History) -> None:
27
28
  """Set the kernel instance in initialization."""
28
29
  self.history = history
30
+ self.calculate_similarity = lru_cache(maxsize=1024)(self._calculate_similarity)
29
31
 
30
- self.cache_size = cache_size
31
- self.cache_keys: deque[str] = deque()
32
- self.cache: dict[str, Suggestion] = {}
32
+ self.n_texts = 0
33
+ self.n_lines = 0
34
+ self.prefix_dict: dict[str, dict[str, list[dict[str, int]]]] = defaultdict(
35
+ lambda: defaultdict(list)
36
+ )
37
+
38
+ def process_history(self) -> None:
39
+ """Process the entire history and store in prefix_dict."""
40
+ texts = self.history._loaded_strings
41
+ if texts := texts[: len(texts) - self.n_texts]:
42
+ n_lines = self.n_lines
43
+ prefix_dict = self.prefix_dict
44
+ for i, text in enumerate(reversed(texts)):
45
+ for line in text.strip().splitlines():
46
+ n_lines += 1
47
+ line = line.strip()
48
+ for j in range(1, len(line)):
49
+ prefix, suffix = line[:j], line[j:]
50
+ prefix_dict[prefix][suffix].append(
51
+ {"index": -1 - i, "line": n_lines}
52
+ )
53
+ # for k in range(1, len(prefix)):
54
+ # prefix_dict[prefix[-k:]] = prefix_dict[prefix]
55
+ self.n_lines = n_lines
56
+ self.n_texts += len(texts)
57
+
58
+ def _calculate_similarity(self, text_1: str, text_2: str) -> float:
59
+ """Calculate and cache the similarity between two texts."""
60
+ return SequenceMatcher(None, text_1, text_2).quick_ratio()
33
61
 
34
62
  def get_suggestion(self, buffer: Buffer, document: Document) -> Suggestion | None:
35
63
  """Get a line completion suggestion."""
36
- result: Suggestion | None = None
64
+ self.process_history()
65
+
37
66
  line = document.current_line.lstrip()
38
- if line:
39
- if line in self.cache:
40
- result = self.cache[line]
41
- else:
42
- result = self.lookup_suggestion(line)
43
- if result:
44
- if len(self.cache) > self.cache_size:
45
- key_to_remove = self.cache_keys.popleft()
46
- if key_to_remove in self.cache:
47
- del self.cache[key_to_remove]
48
-
49
- self.cache_keys.append(line)
50
- self.cache[line] = result
51
- return result
52
-
53
- def lookup_suggestion(self, line: str) -> Suggestion | None:
54
- """Find the most recent matching line in the history."""
55
- # Loop history, most recent item first
56
- for text in self.history._loaded_strings:
57
- if line in text:
58
- # Loop over lines of item in reverse order
59
- for hist_line in text.splitlines()[::-1]:
60
- hist_line = hist_line.strip()
61
- if hist_line.startswith(line):
62
- # Return from the match to end from the history line
63
- suggestion = hist_line[len(line) :]
64
- return Suggestion(suggestion)
67
+ if not line:
68
+ return None
69
+
70
+ suffixes = self.prefix_dict[line]
71
+
72
+ texts = self.history._loaded_strings
73
+ n_lines = self.n_lines
74
+
75
+ best_score = 0.0
76
+ best_suffix = ""
77
+
78
+ # Rank candidates
79
+ max_count = max([1, *(len(x) for x in suffixes.values())])
80
+ for suffix, instances in suffixes.items():
81
+ count = len(instances)
82
+ for instance in instances:
83
+ text = texts[instance["index"]]
84
+ context_similarity = self.calculate_similarity(document.text, text)
85
+ score = (
86
+ 0
87
+ # Similarity of prefix to line
88
+ # 0.333 * len(line) / len(match.group("prefix"))
89
+ # NUmber of instances in history
90
+ + 0.3 * count / max_count
91
+ # Recentness
92
+ + 0.3 * instance["line"] / n_lines
93
+ # Similarity of context to document
94
+ + 0.4 * context_similarity
95
+ )
96
+ # log.debug("%s %r", score, suffix)
97
+ if score > 0.95:
98
+ return Suggestion(suffix)
99
+ if score > best_score:
100
+ best_score = score
101
+ best_suffix = suffix
102
+ if best_suffix:
103
+ return Suggestion(best_suffix)
65
104
  return None
66
105
 
67
- '''
68
- def lookup_suggestion(self, line: "str") -> "Optional[Suggestion]":
69
- ""Suggest most commonly used line."""
70
- results = {}
71
- pat = re.compile(f"^\\s*{re.escape(line)}(.*)$", re.MULTILINE)
72
- for text in self.history.get_strings():
73
- if line in text:
74
- for hist_line in text.splitlines()[::-1]:
75
- hist_line = hist_line.strip()
76
- if hist_line.startswith(line):
77
- # Return from the match to end from the history line
78
- suggestion = hist_line[len(line) :]
79
- if suggestion not in results:
80
- results[suggestion] = 1
81
- else:
82
- results[suggestion] += 1
83
- return Suggestion(
84
- {b: a for a, b in results.items()}[max(results.values())]
85
- )
86
- '''
87
-
88
-
89
- class KernelAutoSuggest(AutoSuggest):
90
- """Suggest line completions from kernel history."""
91
-
92
- def __init__(self, kernel: Kernel) -> None:
93
- """Set the kernel instance in initialization."""
94
- self.kernel = kernel
95
-
96
- def get_suggestion(self, buffer: Buffer, document: Document) -> Suggestion | None:
97
- """Doe nothing."""
98
- return None
99
106
 
100
- async def get_suggestion_async(
101
- self, buff: Buffer, document: Document
102
- ) -> Suggestion | None:
103
- """Return suggestions based on matching kernel history."""
104
- line = document.current_line.strip()
105
- if line:
106
- suggestions = await self.kernel.history_(f"*{line}*")
107
- log.debug("Suggestor got suggestions %s", suggestions)
108
- if suggestions:
109
- _, _, text = suggestions[0]
110
- # Find matching line
111
- for hist_line in text.split("\n"):
112
- hist_line = hist_line.strip()
113
- if hist_line.startswith(line):
114
- # Return from the match to end from the history line
115
- suggestion = hist_line[len(line) :]
116
- log.debug("Suggesting %s", suggestion)
117
- return Suggestion(suggestion)
118
- return None
107
+ # class KernelAutoSuggest(AutoSuggest):
108
+ # """Suggest line completions from kernel history."""
109
+
110
+ # def __init__(self, kernel: Kernel) -> None:
111
+ # """Set the kernel instance in initialization."""
112
+ # self.kernel = kernel
113
+
114
+ # def get_suggestion(self, buffer: Buffer, document: Document) -> Suggestion | None:
115
+ # """Doe nothing."""
116
+ # return None
117
+
118
+ # async def get_suggestion_async(
119
+ # self, buff: Buffer, document: Document
120
+ # ) -> Suggestion | None:
121
+ # """Return suggestions based on matching kernel history."""
122
+ # line = document.current_line.strip()
123
+ # if line:
124
+ # suggestions = await self.kernel.history_(f"*{line}*")
125
+ # log.debug("Suggestor got suggestions %s", suggestions)
126
+ # if suggestions:
127
+ # _, _, text = suggestions[0]
128
+ # # Find matching line
129
+ # for hist_line in text.split("\n"):
130
+ # hist_line = hist_line.strip()
131
+ # if hist_line.startswith(line):
132
+ # # Return from the match to end from the history line
133
+ # suggestion = hist_line[len(line) :]
134
+ # log.debug("Suggesting %s", suggestion)
135
+ # return Suggestion(suggestion)
136
+ # return None
119
137
 
120
138
 
121
139
  class ConditionalAutoSuggestAsync(ConditionalAutoSuggest):
@@ -1 +1,35 @@
1
1
  """Contain various application tab implementations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from importlib import import_module
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from euporie.core.tabs.base import Tab
11
+
12
+
13
+ @dataclass
14
+ class TabRegistryEntry:
15
+ """Class to store tab information."""
16
+
17
+ path: str
18
+ name: str
19
+ mime_types: set = field(default_factory=set)
20
+ file_extensions: dict[str, None] = field(default_factory=dict)
21
+ weight: int = 0
22
+
23
+ @property
24
+ def tab_class(self) -> type[Tab]:
25
+ """Import and return the tab class."""
26
+ module_path, _, attribute = self.path.partition(":")
27
+ module = import_module(module_path)
28
+ return getattr(module, attribute)
29
+
30
+ def __lt__(self, other: TabRegistryEntry) -> bool:
31
+ """Sort by weight."""
32
+ return self.weight < other.weight
33
+
34
+
35
+ _TAB_REGISTRY: list[TabRegistryEntry] = []
@@ -0,0 +1,113 @@
1
+ """Defines tab settings."""
2
+
3
+ from prompt_toolkit.filters import buffer_has_focus
4
+
5
+ from euporie.core.config import add_setting
6
+
7
+ add_setting(
8
+ name="kernel_name",
9
+ group="euporie.core.tabs.kernel",
10
+ flags=["--kernel-name", "--kernel"],
11
+ type_=str,
12
+ help_="The name of the kernel to start by default",
13
+ default="python3",
14
+ description="""
15
+ The name of the kernel selected automatically by the console app or in new
16
+ notebooks. If set to an empty string, the user will be asked which kernel
17
+ to launch.
18
+ """,
19
+ )
20
+
21
+ add_setting(
22
+ name="record_cell_timing",
23
+ group="euporie.core.tabs.kernel",
24
+ title="cell timing recording",
25
+ flags=["--record-cell-timing"],
26
+ type_=bool,
27
+ help_="Should timing data be recorded in cell metadata.",
28
+ default=False,
29
+ schema={
30
+ "type": "boolean",
31
+ },
32
+ description="""
33
+ When set, execution timing data will be recorded in cell metadata.
34
+ """,
35
+ )
36
+
37
+ add_setting(
38
+ name="show_remote_inputs",
39
+ group="euporie.core.tabs.kernel",
40
+ flags=["--show-remote-inputs"],
41
+ type_=bool,
42
+ help_="Display inputs sent to the kernel by other clients",
43
+ default=True,
44
+ description="""
45
+ If set to `True`, all code input sent to the kernel by any client will be
46
+ displayed.
47
+
48
+ If set to `False`, only inputs sent to the kernel by the current instance
49
+ of euporie will be displayed, and all other inputs will be ignored.
50
+
51
+ """,
52
+ )
53
+
54
+ add_setting(
55
+ name="show_remote_outputs",
56
+ group="euporie.core.tabs.kernel",
57
+ flags=["--show-remote-outputs"],
58
+ type_=bool,
59
+ help_="Display kernel outputs triggered by other clients",
60
+ default=True,
61
+ description="""
62
+ If set to `False`, only outputs generated by code input from the current
63
+ instance of euporie will be displayed, and all other outputs will be
64
+ ignored.
65
+
66
+ If set to `True`, all outputs generated by the kernel will be
67
+ displayed.
68
+ """,
69
+ )
70
+
71
+ # euporie.core.tabs.notebook
72
+
73
+ add_setting(
74
+ name="save_widget_state",
75
+ group="euporie.core.tabs.notebook",
76
+ flags=["--save-widget-state"],
77
+ type_=bool,
78
+ help_="Save a notebook's widget state in the notebook metadata",
79
+ default=True,
80
+ description="""
81
+ When set to ``True``, the state of any widgets in the current notebook will
82
+ be saves in the notebook's metadata. This enables widgets to be displayed
83
+ when the notebook is re-opened without having to re-run the notebook.
84
+ """,
85
+ )
86
+
87
+ add_setting(
88
+ name="max_notebook_width",
89
+ group="euporie.core.tabs.notebook",
90
+ flags=["--max-notebook-width"],
91
+ type_=int,
92
+ help_="Maximum width of notebooks",
93
+ default=120,
94
+ schema={
95
+ "minimum": 1,
96
+ },
97
+ description="""
98
+ The maximum width at which to display a notebook.
99
+ """,
100
+ )
101
+
102
+ add_setting(
103
+ name="expand",
104
+ group="euporie.core.tabs.notebook",
105
+ flags=["--expand"],
106
+ type_=bool,
107
+ help_="Use the full width to display notebooks",
108
+ default=False,
109
+ description="""
110
+ Whether the notebook page should expand to fill the available width
111
+ """,
112
+ cmd_filter=~buffer_has_focus,
113
+ )