##// END OF EJS Templates
back to dev
back to dev

File last commit:

r28181:2c9a826a
r28276:b3cd3b05
Show More
autogen_shortcuts.py
221 lines | 6.6 KiB | text/x-python | PythonLexer
/ docs / autogen_shortcuts.py
krassowski
Restore shortcuts in documentation, define identifiers
r28010 from dataclasses import dataclass
from inspect import getsource
NotWearingPants
use pathlib in docs/autogen_shortcuts.py + minor refactor
r26119 from pathlib import Path
krassowski
Allow to customize shortcuts
r28074 from typing import cast, List, Union
krassowski
Restore shortcuts in documentation, define identifiers
r28010 from html import escape as html_escape
import re
from prompt_toolkit.keys import KEY_ALIASES
from prompt_toolkit.key_binding import KeyBindingsBase
from prompt_toolkit.filters import Filter, Condition
from prompt_toolkit.shortcuts import PromptSession
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Allow to customize shortcuts
r28074 from IPython.terminal.shortcuts import create_ipython_shortcuts, create_identifier
from IPython.terminal.shortcuts.filters import KEYBINDING_FILTERS
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Restore shortcuts in documentation, define identifiers
r28010 @dataclass
class Shortcut:
#: a sequence of keys (each element on the list corresponds to pressing one or more keys)
Philippe Blain
docs/autogen_shortcuts.py: support Python 3.8...
r28181 keys_sequence: List[str]
krassowski
Restore shortcuts in documentation, define identifiers
r28010 filter: str
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Restore shortcuts in documentation, define identifiers
r28010 @dataclass
class Handler:
description: str
identifier: str
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Restore shortcuts in documentation, define identifiers
r28010 @dataclass
class Binding:
handler: Handler
shortcut: Shortcut
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Restore shortcuts in documentation, define identifiers
r28010 class _NestedFilter(Filter):
"""Protocol reflecting non-public prompt_toolkit's `_AndList` and `_OrList`."""
filters: List[Filter]
class _Invert(Filter):
"""Protocol reflecting non-public prompt_toolkit's `_Invert`."""
filter: Filter
krassowski
Allow to customize shortcuts
r28074 conjunctions_labels = {"_AndList": "&", "_OrList": "|"}
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Restore shortcuts in documentation, define identifiers
r28010 ATOMIC_CLASSES = {"Never", "Always", "Condition"}
krassowski
Allow to customize shortcuts
r28074 HUMAN_NAMES_FOR_FILTERS = {
filter_: name for name, filter_ in KEYBINDING_FILTERS.items()
}
krassowski
Restore shortcuts in documentation, define identifiers
r28010 def format_filter(
filter_: Union[Filter, _NestedFilter, Condition, _Invert],
is_top_level=True,
skip=None,
) -> str:
"""Create easily readable description of the filter."""
s = filter_.__class__.__name__
if s == "Condition":
func = cast(Condition, filter_).func
krassowski
Allow to customize shortcuts
r28074 if filter_ in HUMAN_NAMES_FOR_FILTERS:
return HUMAN_NAMES_FOR_FILTERS[filter_]
krassowski
Restore shortcuts in documentation, define identifiers
r28010 name = func.__name__
if name == "<lambda>":
source = getsource(func)
return source.split("=")[0].strip()
return func.__name__
elif s == "_Invert":
operand = cast(_Invert, filter_).filter
if operand.__class__.__name__ in ATOMIC_CLASSES:
krassowski
Allow to customize shortcuts
r28074 return f"~{format_filter(operand, is_top_level=False)}"
return f"~({format_filter(operand, is_top_level=False)})"
krassowski
Restore shortcuts in documentation, define identifiers
r28010 elif s in conjunctions_labels:
filters = cast(_NestedFilter, filter_).filters
krassowski
Allow to customize shortcuts
r28074 if filter_ in HUMAN_NAMES_FOR_FILTERS:
return HUMAN_NAMES_FOR_FILTERS[filter_]
krassowski
Restore shortcuts in documentation, define identifiers
r28010 conjunction = conjunctions_labels[s]
glue = f" {conjunction} "
result = glue.join(format_filter(x, is_top_level=False) for x in filters)
if len(filters) > 1 and not is_top_level:
result = f"({result})"
return result
elif s in ["Never", "Always"]:
return s.lower()
else:
raise ValueError(f"Unknown filter type: {filter_}")
def sentencize(s) -> str:
"""Extract first sentence"""
s = re.split(r"\.\W", s.replace("\n", " ").strip())
s = s[0] if len(s) else ""
if not s.endswith("."):
s += "."
try:
return " ".join(s.split())
except AttributeError:
return s
klonuo
Added keyboard shortcuts docs
r22590
Matthias Bussonnier
try to fix test/building-docs
r26160 class _DummyTerminal:
Gabriel Potter
Fix autogen_shortcuts.py for prompt_toolkit 2.0
r24389 """Used as a buffer to get prompt_toolkit bindings
"""
handle_return = None
Thomas Kluyver
Fix generating shortcuts docs
r24402 input_transformer_manager = None
Gabriel Potter
Fix autogen_shortcuts.py for prompt_toolkit 2.0
r24389 display_completions = None
Matthias Bussonnier
try to fix test/building-docs
r26160 editing_mode = "emacs"
krassowski
Implement traversal of autosuggestions and by-character fill
r28014 auto_suggest = None
Matthias Bussonnier
try to fix test/building-docs
r26160
Gabriel Potter
Fix autogen_shortcuts.py for prompt_toolkit 2.0
r24389
krassowski
Restore shortcuts in documentation, define identifiers
r28010 def bindings_from_prompt_toolkit(prompt_bindings: KeyBindingsBase) -> List[Binding]:
"""Collect bindings to a simple format that does not depend on prompt-toolkit internals"""
bindings: List[Binding] = []
for kb in prompt_bindings.bindings:
bindings.append(
Binding(
handler=Handler(
description=kb.handler.__doc__ or "",
identifier=create_identifier(kb.handler),
),
shortcut=Shortcut(
keys_sequence=[
str(k.value) if hasattr(k, "value") else k for k in kb.keys
],
filter=format_filter(kb.filter, skip={"has_focus_filter"}),
),
)
)
return bindings
INDISTINGUISHABLE_KEYS = {**KEY_ALIASES, **{v: k for k, v in KEY_ALIASES.items()}}
def format_prompt_keys(keys: str, add_alternatives=True) -> str:
"""Format prompt toolkit key with modifier into an RST representation."""
def to_rst(key):
escaped = key.replace("\\", "\\\\")
return f":kbd:`{escaped}`"
Philippe Blain
docs/autogen_shortcuts.py: support Python 3.8...
r28181 keys_to_press: List[str]
krassowski
Restore shortcuts in documentation, define identifiers
r28010
prefixes = {
"c-s-": [to_rst("ctrl"), to_rst("shift")],
"s-c-": [to_rst("ctrl"), to_rst("shift")],
"c-": [to_rst("ctrl")],
"s-": [to_rst("shift")],
}
for prefix, modifiers in prefixes.items():
if keys.startswith(prefix):
remainder = keys[len(prefix) :]
keys_to_press = [*modifiers, to_rst(remainder)]
break
else:
keys_to_press = [to_rst(keys)]
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Restore shortcuts in documentation, define identifiers
r28010 result = " + ".join(keys_to_press)
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Restore shortcuts in documentation, define identifiers
r28010 if keys in INDISTINGUISHABLE_KEYS and add_alternatives:
alternative = INDISTINGUISHABLE_KEYS[keys]
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Restore shortcuts in documentation, define identifiers
r28010 result = (
result
+ " (or "
+ format_prompt_keys(alternative, add_alternatives=False)
+ ")"
)
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Restore shortcuts in documentation, define identifiers
r28010 return result
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Allow to customize shortcuts
r28074
klonuo
Added keyboard shortcuts docs
r22590 if __name__ == '__main__':
NotWearingPants
use pathlib in docs/autogen_shortcuts.py + minor refactor
r26119 here = Path(__file__).parent
Matthias Bussonnier
reformat with darker
r26175 dest = here / "source" / "config" / "shortcuts"
klonuo
Added keyboard shortcuts docs
r22590
krassowski
Allow to customize shortcuts
r28074 ipy_bindings = create_ipython_shortcuts(_DummyTerminal())
krassowski
Restore shortcuts in documentation, define identifiers
r28010
session = PromptSession(key_bindings=ipy_bindings)
prompt_bindings = session.app.key_bindings
assert prompt_bindings
# Ensure that we collected the default shortcuts
assert len(prompt_bindings.bindings) > len(ipy_bindings.bindings)
bindings = bindings_from_prompt_toolkit(prompt_bindings)
def sort_key(binding: Binding):
return binding.handler.identifier, binding.shortcut.filter
filters = []
with (dest / "table.tsv").open("w", encoding="utf-8") as csv:
for binding in sorted(bindings, key=sort_key):
sequence = ", ".join(
[format_prompt_keys(keys) for keys in binding.shortcut.keys_sequence]
)
if binding.shortcut.filter == "always":
condition_label = "-"
else:
# we cannot fit all the columns as the filters got too complex over time
condition_label = "ⓘ"
csv.write(
"\t".join(
[
sequence,
sentencize(binding.handler.description)
+ f" :raw-html:`<br>` `{binding.handler.identifier}`",
f':raw-html:`<span title="{html_escape(binding.shortcut.filter)}" style="cursor: help">{condition_label}</span>`',
]
)
+ "\n"
)