Adds completion support
| 226 | |
| 227 | @dataclass |
| 228 | class CompletingReader(Reader): |
| 229 | """Adds completion support""" |
| 230 | |
| 231 | ### Class variables |
| 232 | # see the comment for the complete command |
| 233 | assume_immutable_completions = True |
| 234 | use_brackets = True # display completions inside [] |
| 235 | sort_in_column = False |
| 236 | |
| 237 | ### Instance variables |
| 238 | cmpltn_menu: list[str] = field(init=False) |
| 239 | cmpltn_menu_visible: bool = field(init=False) |
| 240 | cmpltn_message_visible: bool = field(init=False) |
| 241 | cmpltn_menu_end: int = field(init=False) |
| 242 | cmpltn_menu_choices: list[str] = field(init=False) |
| 243 | |
| 244 | def __post_init__(self) -> None: |
| 245 | super().__post_init__() |
| 246 | self.cmpltn_reset() |
| 247 | for c in (complete, self_insert): |
| 248 | self.commands[c.__name__] = c |
| 249 | self.commands[c.__name__.replace('_', '-')] = c |
| 250 | |
| 251 | def collect_keymap(self) -> tuple[tuple[KeySpec, CommandName], ...]: |
| 252 | return super().collect_keymap() + ( |
| 253 | (r'\t', 'complete'),) |
| 254 | |
| 255 | def after_command(self, cmd: Command) -> None: |
| 256 | super().after_command(cmd) |
| 257 | if not isinstance(cmd, (complete, self_insert)): |
| 258 | self.cmpltn_reset() |
| 259 | |
| 260 | def calc_screen(self) -> list[str]: |
| 261 | screen = super().calc_screen() |
| 262 | if self.cmpltn_menu_visible: |
| 263 | # We display the completions menu below the current prompt |
| 264 | ly = self.lxy[1] + 1 |
| 265 | screen[ly:ly] = self.cmpltn_menu |
| 266 | # If we're not in the middle of multiline edit, don't append to screeninfo |
| 267 | # since that screws up the position calculation in pos2xy function. |
| 268 | # This is a hack to prevent the cursor jumping |
| 269 | # into the completions menu when pressing left or down arrow. |
| 270 | if self.pos != len(self.buffer): |
| 271 | self.screeninfo[ly:ly] = [(0, [])]*len(self.cmpltn_menu) |
| 272 | return screen |
| 273 | |
| 274 | def finish(self) -> None: |
| 275 | super().finish() |
| 276 | self.cmpltn_reset() |
| 277 | |
| 278 | def cmpltn_reset(self) -> None: |
| 279 | self.cmpltn_menu = [] |
| 280 | self.cmpltn_menu_visible = False |
| 281 | self.cmpltn_message_visible = False |
| 282 | self.cmpltn_menu_end = 0 |
| 283 | self.cmpltn_menu_choices = [] |
| 284 | |
| 285 | def get_stem(self) -> str: |