diff --git a/examples/dialogs/checkbox_dialog.py b/examples/dialogs/checkbox_dialog.py index 90be2636ab..b267711896 100755 --- a/examples/dialogs/checkbox_dialog.py +++ b/examples/dialogs/checkbox_dialog.py @@ -2,11 +2,12 @@ """ Example of a checkbox-list-based dialog. """ + from prompt_toolkit.formatted_text import HTML from prompt_toolkit.shortcuts import checkboxlist_dialog, message_dialog from prompt_toolkit.styles import Style -results = checkboxlist_dialog( +if results := checkboxlist_dialog( title="CheckboxList dialog", text="What would you like in your breakfast ?", values=[ @@ -26,8 +27,7 @@ "dialog.body label": "#fd8bb6", } ), -).run() -if results: +).run(): message_dialog( title="Room service", text="You selected: %s\nGreat choice sir !" % ",".join(results), diff --git a/examples/full-screen/calculator.py b/examples/full-screen/calculator.py index fda6567047..6bbc59e54c 100755 --- a/examples/full-screen/calculator.py +++ b/examples/full-screen/calculator.py @@ -50,9 +50,7 @@ def main(): def accept(buff): # Evaluate "calculator" expression. try: - output = "\n\nIn: {}\nOut: {}".format( - input_field.text, eval(input_field.text) - ) # Don't do 'eval' in real code! + output = f"\n\nIn: {input_field.text}\nOut: {eval(input_field.text)}" except BaseException as e: output = f"\n\n{e}" new_text = output_field.text + output diff --git a/examples/full-screen/pager.py b/examples/full-screen/pager.py index 799c834377..d6d925a7ad 100755 --- a/examples/full-screen/pager.py +++ b/examples/full-screen/pager.py @@ -25,13 +25,10 @@ def get_statusbar_text(): return [ - ("class:status", _pager_py_path + " - "), + ("class:status", f"{_pager_py_path} - "), ( "class:status.position", - "{}:{}".format( - text_area.document.cursor_position_row + 1, - text_area.document.cursor_position_col + 1, - ), + f"{text_area.document.cursor_position_row + 1}:{text_area.document.cursor_position_col + 1}", ), ("class:status", " - Press "), ("class:status.key", "Ctrl-C"), diff --git a/examples/full-screen/simple-demos/line-prefixes.py b/examples/full-screen/simple-demos/line-prefixes.py index ab47ef6403..89bde1dd03 100755 --- a/examples/full-screen/simple-demos/line-prefixes.py +++ b/examples/full-screen/simple-demos/line-prefixes.py @@ -35,7 +35,7 @@ def get_line_prefix(lineno, wrap_count): if wrap_count == 0: return HTML('[%s] ') % lineno - text = str(lineno) + "-" + "*" * (lineno // 2) + ": " + text = f"{str(lineno)}-" + "*" * (lineno // 2) + ": " return HTML('[%s.%s] ') % ( lineno, wrap_count, diff --git a/examples/full-screen/text-editor.py b/examples/full-screen/text-editor.py index 9c0a414201..624bc9357d 100755 --- a/examples/full-screen/text-editor.py +++ b/examples/full-screen/text-editor.py @@ -53,10 +53,7 @@ def get_statusbar_text(): def get_statusbar_right_text(): - return " {}:{} ".format( - text_field.document.cursor_position_row + 1, - text_field.document.cursor_position_col + 1, - ) + return f" {text_field.document.cursor_position_row + 1}:{text_field.document.cursor_position_col + 1} " search_toolbar = SearchToolbar() diff --git a/examples/print-text/named-colors.py b/examples/print-text/named-colors.py index ea3f0ba0d6..177098d77f 100755 --- a/examples/print-text/named-colors.py +++ b/examples/print-text/named-colors.py @@ -11,7 +11,7 @@ def main(): - tokens = FormattedText([("fg:" + name, name + " ") for name in NAMED_COLORS]) + tokens = FormattedText([(f"fg:{name}", f"{name} ") for name in NAMED_COLORS]) print(HTML("\nNamed colors, using 16 color output.")) print("(Note that it doesn't really make sense to use named colors ") diff --git a/examples/print-text/true-color-demo.py b/examples/print-text/true-color-demo.py index a241006065..000029d7c4 100755 --- a/examples/print-text/true-color-demo.py +++ b/examples/print-text/true-color-demo.py @@ -21,10 +21,7 @@ def main(): "bg:#00{0:02x}{0:02x}", # Cyan. "bg:#{0:02x}{0:02x}{0:02x}", # Gray. ]: - fragments = [] - for i in range(0, 256, 4): - fragments.append((template.format(i), " ")) - + fragments = [(template.format(i), " ") for i in range(0, 256, 4)] print(FormattedText(fragments), color_depth=ColorDepth.DEPTH_4_BIT) print(FormattedText(fragments), color_depth=ColorDepth.DEPTH_8_BIT) print(FormattedText(fragments), color_depth=ColorDepth.DEPTH_24_BIT) diff --git a/examples/progress-bar/a-lot-of-parallel-tasks.py b/examples/progress-bar/a-lot-of-parallel-tasks.py index 31110ac096..256b6f17e3 100755 --- a/examples/progress-bar/a-lot-of-parallel-tasks.py +++ b/examples/progress-bar/a-lot-of-parallel-tasks.py @@ -18,7 +18,7 @@ def main(): def run_task(label, total, sleep_time): """Complete a normal run.""" - for i in pb(range(total), label=label): + for _ in pb(range(total), label=label): time.sleep(sleep_time) def stop_task(label, total, sleep_time): diff --git a/examples/progress-bar/colored-title-and-label.py b/examples/progress-bar/colored-title-and-label.py index 0b5e73a225..248b6fcd82 100755 --- a/examples/progress-bar/colored-title-and-label.py +++ b/examples/progress-bar/colored-title-and-label.py @@ -14,7 +14,7 @@ def main(): label = HTML("some file: ") with ProgressBar(title=title) as pb: - for i in pb(range(800), label=label): + for _ in pb(range(800), label=label): time.sleep(0.01) diff --git a/examples/progress-bar/many-parallel-tasks.py b/examples/progress-bar/many-parallel-tasks.py index dc34ef2eb2..b21c3188e1 100755 --- a/examples/progress-bar/many-parallel-tasks.py +++ b/examples/progress-bar/many-parallel-tasks.py @@ -16,7 +16,7 @@ def main(): ) as pb: def run_task(label, total, sleep_time): - for i in pb(range(total), label=label): + for _ in pb(range(total), label=label): time.sleep(sleep_time) threads = [ diff --git a/examples/progress-bar/nested-progress-bars.py b/examples/progress-bar/nested-progress-bars.py index a585ca1233..bb262f79e8 100755 --- a/examples/progress-bar/nested-progress-bars.py +++ b/examples/progress-bar/nested-progress-bars.py @@ -10,12 +10,12 @@ def main(): with ProgressBar( - title=HTML('Nested progress bars'), - bottom_toolbar=HTML(" [Control-L] clear [Control-C] abort"), - ) as pb: + title=HTML('Nested progress bars'), + bottom_toolbar=HTML(" [Control-L] clear [Control-C] abort"), + ) as pb: for i in pb(range(6), label="Main task"): - for j in pb(range(200), label=f"Subtask <{i + 1}>", remove_when_done=True): + for _ in pb(range(200), label=f"Subtask <{i + 1}>", remove_when_done=True): time.sleep(0.01) diff --git a/examples/progress-bar/scrolling-task-name.py b/examples/progress-bar/scrolling-task-name.py index bce155f0c5..83486ad683 100755 --- a/examples/progress-bar/scrolling-task-name.py +++ b/examples/progress-bar/scrolling-task-name.py @@ -10,9 +10,9 @@ def main(): with ProgressBar( - title="Scrolling task name (make sure the window is not too big)." - ) as pb: - for i in pb( + title="Scrolling task name (make sure the window is not too big)." + ) as pb: + for _ in pb( range(800), label="This is a very very very long task that requires horizontal scrolling ...", ): diff --git a/examples/progress-bar/simple-progress-bar.py b/examples/progress-bar/simple-progress-bar.py index c8776e5036..a76aee6f25 100755 --- a/examples/progress-bar/simple-progress-bar.py +++ b/examples/progress-bar/simple-progress-bar.py @@ -10,7 +10,7 @@ def main(): with ProgressBar() as pb: - for i in pb(range(800)): + for _ in pb(range(800)): time.sleep(0.01) diff --git a/examples/progress-bar/styled-1.py b/examples/progress-bar/styled-1.py index d972e55e91..da58d9ae18 100755 --- a/examples/progress-bar/styled-1.py +++ b/examples/progress-bar/styled-1.py @@ -26,9 +26,9 @@ def main(): with ProgressBar( - style=style, title="Progress bar example with custom styling." - ) as pb: - for i in pb(range(1600), label="Downloading..."): + style=style, title="Progress bar example with custom styling." + ) as pb: + for _ in pb(range(1600), label="Downloading..."): time.sleep(0.01) diff --git a/examples/progress-bar/styled-2.py b/examples/progress-bar/styled-2.py index eb2c479c27..cac0eb4d35 100755 --- a/examples/progress-bar/styled-2.py +++ b/examples/progress-bar/styled-2.py @@ -37,12 +37,12 @@ def main(): formatters.TimeLeft(), ] with ProgressBar( - title="Progress bar example with custom formatter.", - formatters=custom_formatters, - style=style, - ) as pb: + title="Progress bar example with custom formatter.", + formatters=custom_formatters, + style=style, + ) as pb: - for i in pb(range(20), label="Downloading..."): + for _ in pb(range(20), label="Downloading..."): time.sleep(1) diff --git a/examples/progress-bar/styled-apt-get-install.py b/examples/progress-bar/styled-apt-get-install.py index bafe70ba43..4105e20f8a 100755 --- a/examples/progress-bar/styled-apt-get-install.py +++ b/examples/progress-bar/styled-apt-get-install.py @@ -30,7 +30,7 @@ def main(): ] with ProgressBar(style=style, formatters=custom_formatters) as pb: - for i in pb(range(1600), label="Installing"): + for _ in pb(range(1600), label="Installing"): time.sleep(0.01) diff --git a/examples/progress-bar/styled-rainbow.py b/examples/progress-bar/styled-rainbow.py index e45a91632b..2f0dbbc70d 100755 --- a/examples/progress-bar/styled-rainbow.py +++ b/examples/progress-bar/styled-rainbow.py @@ -21,13 +21,9 @@ def main(): formatters.Rainbow(formatters.TimeLeft()), ] - if true_color: - color_depth = ColorDepth.DEPTH_24_BIT - else: - color_depth = ColorDepth.DEPTH_8_BIT - + color_depth = ColorDepth.DEPTH_24_BIT if true_color else ColorDepth.DEPTH_8_BIT with ProgressBar(formatters=custom_formatters, color_depth=color_depth) as pb: - for i in pb(range(20), label="Downloading..."): + for _ in pb(range(20), label="Downloading..."): time.sleep(1) diff --git a/examples/progress-bar/styled-tqdm-1.py b/examples/progress-bar/styled-tqdm-1.py index 9484ac051b..f9b6e020e8 100755 --- a/examples/progress-bar/styled-tqdm-1.py +++ b/examples/progress-bar/styled-tqdm-1.py @@ -32,7 +32,7 @@ def main(): ] with ProgressBar(style=style, formatters=custom_formatters) as pb: - for i in pb(range(1600), label="Installing"): + for _ in pb(range(1600), label="Installing"): time.sleep(0.01) diff --git a/examples/progress-bar/styled-tqdm-2.py b/examples/progress-bar/styled-tqdm-2.py index 0e66e90073..720179b39d 100755 --- a/examples/progress-bar/styled-tqdm-2.py +++ b/examples/progress-bar/styled-tqdm-2.py @@ -30,7 +30,7 @@ def main(): ] with ProgressBar(style=style, formatters=custom_formatters) as pb: - for i in pb(range(1600), label="Installing"): + for _ in pb(range(1600), label="Installing"): time.sleep(0.01) diff --git a/examples/progress-bar/two-tasks.py b/examples/progress-bar/two-tasks.py index c78604ef4d..33c0271ecf 100755 --- a/examples/progress-bar/two-tasks.py +++ b/examples/progress-bar/two-tasks.py @@ -12,11 +12,11 @@ def main(): with ProgressBar() as pb: # Two parallal tasks. def task_1(): - for i in pb(range(100)): + for _ in pb(range(100)): time.sleep(0.05) def task_2(): - for i in pb(range(150)): + for _ in pb(range(150)): time.sleep(0.08) # Start threads. diff --git a/examples/progress-bar/unknown-length.py b/examples/progress-bar/unknown-length.py index e39ac399dc..d6d8c5325f 100755 --- a/examples/progress-bar/unknown-length.py +++ b/examples/progress-bar/unknown-length.py @@ -18,7 +18,7 @@ def data(): def main(): with ProgressBar() as pb: - for i in pb(data()): + for _ in pb(data()): time.sleep(0.1) diff --git a/examples/prompts/accept-default.py b/examples/prompts/accept-default.py index 311ef46c96..0ef45d986d 100644 --- a/examples/prompts/accept-default.py +++ b/examples/prompts/accept-default.py @@ -6,6 +6,7 @@ This should display the prompt with all the formatting like usual, but not allow any editing. """ + from prompt_toolkit import HTML, prompt if __name__ == "__main__": @@ -13,4 +14,4 @@ HTML("Type some input: "), accept_default=True, default="test" ) - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/auto-completion/autocomplete-with-control-space.py b/examples/prompts/auto-completion/autocomplete-with-control-space.py index 61160a3626..dad7b2475b 100755 --- a/examples/prompts/auto-completion/autocomplete-with-control-space.py +++ b/examples/prompts/auto-completion/autocomplete-with-control-space.py @@ -68,7 +68,7 @@ def main(): complete_while_typing=False, key_bindings=kb, ) - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/auto-completion/autocompletion-like-readline.py b/examples/prompts/auto-completion/autocompletion-like-readline.py index 613d3e7408..31835b3279 100755 --- a/examples/prompts/auto-completion/autocompletion-like-readline.py +++ b/examples/prompts/auto-completion/autocompletion-like-readline.py @@ -51,7 +51,7 @@ def main(): completer=animal_completer, complete_style=CompleteStyle.READLINE_LIKE, ) - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/auto-completion/autocompletion.py b/examples/prompts/auto-completion/autocompletion.py index fc9dda0589..34b4d926b4 100755 --- a/examples/prompts/auto-completion/autocompletion.py +++ b/examples/prompts/auto-completion/autocompletion.py @@ -53,7 +53,7 @@ def main(): text = prompt( "Give some animals: ", completer=animal_completer, complete_while_typing=False ) - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/auto-completion/colored-completions.py b/examples/prompts/auto-completion/colored-completions.py index 9c5cba365b..ed10075374 100755 --- a/examples/prompts/auto-completion/colored-completions.py +++ b/examples/prompts/auto-completion/colored-completions.py @@ -28,8 +28,8 @@ def get_completions(self, document, complete_event): yield Completion( color, start_position=-len(word), - style="fg:" + color, - selected_style="fg:white bg:" + color, + style=f"fg:{color}", + selected_style=f"fg:white bg:{color}", ) diff --git a/examples/prompts/auto-completion/combine-multiple-completers.py b/examples/prompts/auto-completion/combine-multiple-completers.py index 273ebe561d..67237b1c6a 100755 --- a/examples/prompts/auto-completion/combine-multiple-completers.py +++ b/examples/prompts/auto-completion/combine-multiple-completers.py @@ -69,7 +69,7 @@ def main(): text = prompt( "Give some animals: ", completer=completer, complete_while_typing=False ) - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/auto-completion/fuzzy-custom-completer.py b/examples/prompts/auto-completion/fuzzy-custom-completer.py index 405292946b..3896929393 100755 --- a/examples/prompts/auto-completion/fuzzy-custom-completer.py +++ b/examples/prompts/auto-completion/fuzzy-custom-completer.py @@ -28,8 +28,8 @@ def get_completions(self, document, complete_event): yield Completion( color, start_position=-len(word), - style="fg:" + color, - selected_style="fg:white bg:" + color, + style=f"fg:{color}", + selected_style=f"fg:white bg:{color}", ) diff --git a/examples/prompts/auto-completion/fuzzy-word-completer.py b/examples/prompts/auto-completion/fuzzy-word-completer.py index 329c0c1e36..616d7db82c 100755 --- a/examples/prompts/auto-completion/fuzzy-word-completer.py +++ b/examples/prompts/auto-completion/fuzzy-word-completer.py @@ -52,7 +52,7 @@ def main(): text = prompt( "Give some animals: ", completer=animal_completer, complete_while_typing=True ) - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/auto-completion/multi-column-autocompletion-with-meta.py b/examples/prompts/auto-completion/multi-column-autocompletion-with-meta.py index 5ba3ab5088..a9f9bd5184 100755 --- a/examples/prompts/auto-completion/multi-column-autocompletion-with-meta.py +++ b/examples/prompts/auto-completion/multi-column-autocompletion-with-meta.py @@ -43,7 +43,7 @@ def main(): completer=animal_completer, complete_style=CompleteStyle.MULTI_COLUMN, ) - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/auto-completion/multi-column-autocompletion.py b/examples/prompts/auto-completion/multi-column-autocompletion.py index 7fcfc52cbc..cdc745d972 100755 --- a/examples/prompts/auto-completion/multi-column-autocompletion.py +++ b/examples/prompts/auto-completion/multi-column-autocompletion.py @@ -50,7 +50,7 @@ def main(): completer=animal_completer, complete_style=CompleteStyle.MULTI_COLUMN, ) - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/auto-completion/nested-autocompletion.py b/examples/prompts/auto-completion/nested-autocompletion.py index cd85b8cb49..ea45f595ed 100755 --- a/examples/prompts/auto-completion/nested-autocompletion.py +++ b/examples/prompts/auto-completion/nested-autocompletion.py @@ -15,7 +15,7 @@ def main(): text = prompt("Type a command: ", completer=completer) - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/auto-suggestion.py b/examples/prompts/auto-suggestion.py index 6660777804..97438ee930 100755 --- a/examples/prompts/auto-suggestion.py +++ b/examples/prompts/auto-suggestion.py @@ -41,7 +41,7 @@ def main(): else: break - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/autocorrection.py b/examples/prompts/autocorrection.py index 63781326af..53bf2a369e 100755 --- a/examples/prompts/autocorrection.py +++ b/examples/prompts/autocorrection.py @@ -28,10 +28,9 @@ def _(event): b = event.app.current_buffer w = b.document.get_word_before_cursor() - if w is not None: - if w in corrections: - b.delete_before_cursor(count=len(w)) - b.insert_text(corrections[w]) + if w is not None and w in corrections: + b.delete_before_cursor(count=len(w)) + b.insert_text(corrections[w]) b.insert_text(" ") diff --git a/examples/prompts/clock-input.py b/examples/prompts/clock-input.py index e43abd8915..e1b8b5de1f 100755 --- a/examples/prompts/clock-input.py +++ b/examples/prompts/clock-input.py @@ -18,7 +18,7 @@ def get_prompt(): def main(): result = prompt(get_prompt, refresh_interval=0.5) - print("You said: %s" % result) + print(f"You said: {result}") if __name__ == "__main__": diff --git a/examples/prompts/colored-prompt.py b/examples/prompts/colored-prompt.py index 1e63e296b1..f3f7f3e92b 100755 --- a/examples/prompts/colored-prompt.py +++ b/examples/prompts/colored-prompt.py @@ -40,7 +40,7 @@ def example_1(): ] answer = prompt(prompt_fragments, style=style) - print("You said: %s" % answer) + print(f"You said: {answer}") def example_2(): @@ -57,7 +57,7 @@ def example_2(): ), style=style, ) - print("You said: %s" % answer) + print(f"You said: {answer}") def example_3(): @@ -72,7 +72,7 @@ def example_3(): "# " ) ) - print("You said: %s" % answer) + print(f"You said: {answer}") if __name__ == "__main__": diff --git a/examples/prompts/confirmation-prompt.py b/examples/prompts/confirmation-prompt.py index bd52b9e96a..18687fe6c4 100755 --- a/examples/prompts/confirmation-prompt.py +++ b/examples/prompts/confirmation-prompt.py @@ -2,8 +2,9 @@ """ Example of a confirmation prompt. """ + from prompt_toolkit.shortcuts import confirm if __name__ == "__main__": answer = confirm("Should we do that?") - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/custom-key-binding.py b/examples/prompts/custom-key-binding.py index 32d889ede0..1f1615ebf4 100755 --- a/examples/prompts/custom-key-binding.py +++ b/examples/prompts/custom-key-binding.py @@ -60,7 +60,7 @@ async def _(event): Example of asyncio coroutine as a key binding. """ try: - for i in range(5): + for _ in range(5): async with in_terminal(): print("hello") await asyncio.sleep(1) diff --git a/examples/prompts/custom-lexer.py b/examples/prompts/custom-lexer.py index 1255d9f3f9..5c53f6a1ce 100755 --- a/examples/prompts/custom-lexer.py +++ b/examples/prompts/custom-lexer.py @@ -22,7 +22,7 @@ def get_line(lineno): def main(): answer = prompt("Give me some input: ", lexer=RainbowLexer()) - print("You said: %s" % answer) + print(f"You said: {answer}") if __name__ == "__main__": diff --git a/examples/prompts/fancy-zsh-prompt.py b/examples/prompts/fancy-zsh-prompt.py index 4761c08ad0..a8fc846f53 100755 --- a/examples/prompts/fancy-zsh-prompt.py +++ b/examples/prompts/fancy-zsh-prompt.py @@ -72,7 +72,7 @@ def get_prompt() -> HTML: def main() -> None: while True: answer = prompt(get_prompt, style=style, refresh_interval=1) - print("You said: %s" % answer) + print(f"You said: {answer}") if __name__ == "__main__": diff --git a/examples/prompts/finalterm-shell-integration.py b/examples/prompts/finalterm-shell-integration.py index 30c7a7fb33..379b5a433f 100755 --- a/examples/prompts/finalterm-shell-integration.py +++ b/examples/prompts/finalterm-shell-integration.py @@ -39,5 +39,5 @@ def get_prompt_text(): # Output. sys.stdout.write(BEFORE_OUTPUT) - print("You said: %s" % answer) + print(f"You said: {answer}") sys.stdout.write(AFTER_OUTPUT.format(command_status=0)) diff --git a/examples/prompts/get-input-vi-mode.py b/examples/prompts/get-input-vi-mode.py index 566ffd5ed5..4073bcdd2b 100755 --- a/examples/prompts/get-input-vi-mode.py +++ b/examples/prompts/get-input-vi-mode.py @@ -4,4 +4,4 @@ if __name__ == "__main__": print("You have Vi keybindings here. Press [Esc] to go to navigation mode.") answer = prompt("Give me some input: ", multiline=False, vi_mode=True) - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/get-input-with-default.py b/examples/prompts/get-input-with-default.py index 67446d57f9..974888f951 100755 --- a/examples/prompts/get-input-with-default.py +++ b/examples/prompts/get-input-with-default.py @@ -3,10 +3,11 @@ Example of a call to `prompt` with a default value. The input is pre-filled, but the user can still edit the default. """ + import getpass from prompt_toolkit import prompt if __name__ == "__main__": - answer = prompt("What is your name: ", default="%s" % getpass.getuser()) - print("You said: %s" % answer) + answer = prompt("What is your name: ", default=f"{getpass.getuser()}") + print(f"You said: {answer}") diff --git a/examples/prompts/get-input.py b/examples/prompts/get-input.py index 5529bbbb5b..6db7e2c49e 100755 --- a/examples/prompts/get-input.py +++ b/examples/prompts/get-input.py @@ -2,8 +2,9 @@ """ The most simple prompt example. """ + from prompt_toolkit import prompt if __name__ == "__main__": answer = prompt("Give me some input: ") - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/get-multiline-input.py b/examples/prompts/get-multiline-input.py index eda35bee42..48bcb1903a 100755 --- a/examples/prompts/get-multiline-input.py +++ b/examples/prompts/get-multiline-input.py @@ -16,9 +16,8 @@ def prompt_continuation(width, line_number, wrap_count): """ if wrap_count > 0: return " " * (width - 3) + "-> " - else: - text = ("- %i - " % (line_number + 1)).rjust(width) - return HTML("%s") % text + text = ("- %i - " % (line_number + 1)).rjust(width) + return HTML("%s") % text if __name__ == "__main__": @@ -26,4 +25,4 @@ def prompt_continuation(width, line_number, wrap_count): answer = prompt( "Multiline input: ", multiline=True, prompt_continuation=prompt_continuation ) - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/get-password.py b/examples/prompts/get-password.py index 1c9517c60f..a9c0c27f78 100755 --- a/examples/prompts/get-password.py +++ b/examples/prompts/get-password.py @@ -3,4 +3,4 @@ if __name__ == "__main__": password = prompt("Password: ", is_password=True) - print("You said: %s" % password) + print(f"You said: {password}") diff --git a/examples/prompts/history/persistent-history.py b/examples/prompts/history/persistent-history.py index 2bdb7587f4..395bfdfd97 100755 --- a/examples/prompts/history/persistent-history.py +++ b/examples/prompts/history/persistent-history.py @@ -18,7 +18,7 @@ def main(): while True: text = session.prompt("Say something: ") - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/history/slow-history.py b/examples/prompts/history/slow-history.py index 5b6a7a2f54..ccc0270775 100755 --- a/examples/prompts/history/slow-history.py +++ b/examples/prompts/history/slow-history.py @@ -41,7 +41,7 @@ def main(): while True: text = session.prompt("Say something: ") - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/html-input.py b/examples/prompts/html-input.py index 4c51737773..ce3575130b 100755 --- a/examples/prompts/html-input.py +++ b/examples/prompts/html-input.py @@ -11,7 +11,7 @@ def main(): text = prompt("Enter HTML: ", lexer=PygmentsLexer(HtmlLexer)) - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/input-validation.py b/examples/prompts/input-validation.py index d8bd3eef94..18e0b117f8 100755 --- a/examples/prompts/input-validation.py +++ b/examples/prompts/input-validation.py @@ -22,13 +22,13 @@ def main(): text = prompt( "Enter e-mail address: ", validator=validator, validate_while_typing=False ) - print("You said: %s" % text) + print(f"You said: {text}") # While typing text = prompt( "Enter e-mail address: ", validator=validator, validate_while_typing=True ) - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/prompts/inputhook.py b/examples/prompts/inputhook.py index 852079042f..d75f53f29d 100755 --- a/examples/prompts/inputhook.py +++ b/examples/prompts/inputhook.py @@ -77,7 +77,7 @@ def main(): "Python >>> ", inputhook=inputhook, lexer=PygmentsLexer(PythonLexer) ) result = session.prompt() - print("You said: %s" % result) + print(f"You said: {result}") if __name__ == "__main__": diff --git a/examples/prompts/mouse-support.py b/examples/prompts/mouse-support.py index 1e4ee76005..b1b4bb70ed 100755 --- a/examples/prompts/mouse-support.py +++ b/examples/prompts/mouse-support.py @@ -7,4 +7,4 @@ ) print("You can click with the mouse in order to select text.") answer = prompt("Multiline input: ", multiline=True, mouse_support=True) - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/multiline-prompt.py b/examples/prompts/multiline-prompt.py index d6a7698cbc..f2e70ef30e 100755 --- a/examples/prompts/multiline-prompt.py +++ b/examples/prompts/multiline-prompt.py @@ -2,10 +2,11 @@ """ Demonstration of how the input can be indented. """ + from prompt_toolkit import prompt if __name__ == "__main__": answer = prompt( "Give me some input: (ESCAPE followed by ENTER to accept)\n > ", multiline=True ) - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/no-wrapping.py b/examples/prompts/no-wrapping.py index 371486ea66..170e7d53c8 100755 --- a/examples/prompts/no-wrapping.py +++ b/examples/prompts/no-wrapping.py @@ -3,4 +3,4 @@ if __name__ == "__main__": answer = prompt("Give me some input: ", wrap_lines=False, multiline=True) - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/placeholder-text.py b/examples/prompts/placeholder-text.py index e113330280..46d1ce5086 100755 --- a/examples/prompts/placeholder-text.py +++ b/examples/prompts/placeholder-text.py @@ -2,6 +2,7 @@ """ Example of a placeholer that's displayed as long as no input is given. """ + from prompt_toolkit import prompt from prompt_toolkit.formatted_text import HTML @@ -10,4 +11,4 @@ "Give me some input: ", placeholder=HTML(''), ) - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/regular-language.py b/examples/prompts/regular-language.py index cbe7256da8..59a39152e4 100755 --- a/examples/prompts/regular-language.py +++ b/examples/prompts/regular-language.py @@ -72,8 +72,7 @@ def create_grammar(): text = prompt( "Calculate: ", lexer=lexer, completer=completer, style=example_style ) - m = g.match(text) - if m: + if m := g.match(text): vars = m.variables() else: print("Invalid command\n") diff --git a/examples/prompts/rprompt.py b/examples/prompts/rprompt.py index f7656b7af6..b78653e48b 100755 --- a/examples/prompts/rprompt.py +++ b/examples/prompts/rprompt.py @@ -30,23 +30,23 @@ def get_rprompt_text(): def main(): # Option 1: pass a string to 'rprompt': answer = prompt("> ", rprompt=" ", style=example_style) - print("You said: %s" % answer) + print(f"You said: {answer}") # Option 2: pass HTML: answer = prompt("> ", rprompt=HTML(" <rprompt> "), style=example_style) - print("You said: %s" % answer) + print(f"You said: {answer}") # Option 3: pass ANSI: answer = prompt( "> ", rprompt=ANSI(" \x1b[4m\x1b[0m "), style=example_style ) - print("You said: %s" % answer) + print(f"You said: {answer}") # Option 4: Pass a callable. (This callable can either return plain text, # an HTML object, an ANSI object or a list of (style, text) # tuples. answer = prompt("> ", rprompt=get_rprompt_text, style=example_style) - print("You said: %s" % answer) + print(f"You said: {answer}") if __name__ == "__main__": diff --git a/examples/prompts/swap-light-and-dark-colors.py b/examples/prompts/swap-light-and-dark-colors.py index e602449d45..8f5e9d8762 100755 --- a/examples/prompts/swap-light-and-dark-colors.py +++ b/examples/prompts/swap-light-and-dark-colors.py @@ -48,11 +48,7 @@ def _(event): swapped[0] = not swapped[0] def bottom_toolbar(): - if swapped[0]: - on = "on=true" - else: - on = "on=false" - + on = "on=true" if swapped[0] else "on=false" return ( HTML( 'Press ' diff --git a/examples/prompts/system-clipboard-integration.py b/examples/prompts/system-clipboard-integration.py index f6059217fb..0b9136b9bd 100755 --- a/examples/prompts/system-clipboard-integration.py +++ b/examples/prompts/system-clipboard-integration.py @@ -3,6 +3,7 @@ Demonstration of a custom clipboard class. This requires the 'pyperclip' library to be installed. """ + from prompt_toolkit import prompt from prompt_toolkit.clipboard.pyperclip import PyperclipClipboard @@ -14,4 +15,4 @@ print("") answer = prompt("Give me some input: ", clipboard=PyperclipClipboard()) - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/system-prompt.py b/examples/prompts/system-prompt.py index 47aa2a5620..e9ff46095b 100755 --- a/examples/prompts/system-prompt.py +++ b/examples/prompts/system-prompt.py @@ -7,14 +7,14 @@ "(1/3) If you press meta-! or esc-! at the following prompt, you can enter system commands." ) answer = prompt("Give me some input: ", enable_system_prompt=True) - print("You said: %s" % answer) + print(f"You said: {answer}") # Enable suspend. print("(2/3) If you press Control-Z, the application will suspend.") answer = prompt("Give me some input: ", enable_suspend=True) - print("You said: %s" % answer) + print(f"You said: {answer}") # Enable open_in_editor print("(3/3) If you press Control-X Control-E, the prompt will open in $EDITOR.") answer = prompt("Give me some input: ", enable_open_in_editor=True) - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/terminal-title.py b/examples/prompts/terminal-title.py index 14f945999c..dc49c2671a 100755 --- a/examples/prompts/terminal-title.py +++ b/examples/prompts/terminal-title.py @@ -7,4 +7,4 @@ answer = prompt("Give me some input: ") set_title("") - print("You said: %s" % answer) + print(f"You said: {answer}") diff --git a/examples/prompts/up-arrow-partial-string-matching.py b/examples/prompts/up-arrow-partial-string-matching.py index 742a12eda2..8ff704559d 100755 --- a/examples/prompts/up-arrow-partial-string-matching.py +++ b/examples/prompts/up-arrow-partial-string-matching.py @@ -34,7 +34,7 @@ def main(): else: break - print("You said: %s" % text) + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/ssh/asyncssh-server.py b/examples/ssh/asyncssh-server.py index 1fa1800a46..2192610897 100755 --- a/examples/ssh/asyncssh-server.py +++ b/examples/ssh/asyncssh-server.py @@ -73,7 +73,7 @@ async def interact(ssh_session: PromptToolkitSSHSession) -> None: # Simple progress bar. with ProgressBar() as pb: - for i in pb(range(50)): + for _ in pb(range(50)): await asyncio.sleep(0.1) # Normal prompt. diff --git a/examples/telnet/chat-app.py b/examples/telnet/chat-app.py index 1880a362b9..5b0bc72f52 100755 --- a/examples/telnet/chat-app.py +++ b/examples/telnet/chat-app.py @@ -84,9 +84,9 @@ def _send_to_everyone(sender_connection, name, message, color): if c != sender_connection: c.send_above_prompt( [ - ("fg:" + color, "[%s]" % name), + (f"fg:{color}", f"[{name}]"), ("", " "), - ("fg:" + color, "%s\n" % message), + (f"fg:{color}", "%s\n" % message), ] ) diff --git a/examples/tutorial/sqlite-cli.py b/examples/tutorial/sqlite-cli.py index ea3e2c81c4..11c6f32f97 100755 --- a/examples/tutorial/sqlite-cli.py +++ b/examples/tutorial/sqlite-cli.py @@ -176,9 +176,5 @@ def main(database): if __name__ == "__main__": - if len(sys.argv) < 2: - db = ":memory:" - else: - db = sys.argv[1] - + db = ":memory:" if len(sys.argv) < 2 else sys.argv[1] main(db) diff --git a/setup.py b/setup.py index 567e58e47d..555176784a 100755 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ def get_version(package): path = os.path.join(os.path.dirname(__file__), "src", package, "__init__.py") with open(path, "rb") as f: init_py = f.read().decode("utf-8") - return re.search("__version__ = ['\"]([^'\"]+)['\"]", init_py).group(1) + return re.search("__version__ = ['\"]([^'\"]+)['\"]", init_py)[1] setup( diff --git a/src/prompt_toolkit/application/application.py b/src/prompt_toolkit/application/application.py index 5a09918da5..d99a322036 100644 --- a/src/prompt_toolkit/application/application.py +++ b/src/prompt_toolkit/application/application.py @@ -359,10 +359,7 @@ def _create_merged_style(self, include_default_pygments_style: Filter) -> BaseSt @DynamicStyle def conditional_pygments_style() -> BaseStyle: - if include_default_pygments_style(): - return pygments_style - else: - return dummy_style + return pygments_style if include_default_pygments_style() else dummy_style return merge_styles( [ @@ -521,29 +518,30 @@ def _redraw(self, render_as_done: bool = False) -> None: def run_in_context() -> None: # Only draw when no sub application was started. - if self._is_running and not self._running_in_terminal: - if self.min_redraw_interval: - self._last_redraw_time = time.time() - - # Render - self.render_counter += 1 - self.before_render.fire() - - if render_as_done: - if self.erase_when_done: - self.renderer.erase() - else: - # Draw in 'done' state and reset renderer. - self.renderer.render(self, self.layout, is_done=render_as_done) + if not self._is_running or self._running_in_terminal: + return + if self.min_redraw_interval: + self._last_redraw_time = time.time() + + # Render + self.render_counter += 1 + self.before_render.fire() + + if render_as_done: + if self.erase_when_done: + self.renderer.erase() else: - self.renderer.render(self, self.layout) + # Draw in 'done' state and reset renderer. + self.renderer.render(self, self.layout, is_done=render_as_done) + else: + self.renderer.render(self, self.layout) - self.layout.update_parents_relations() + self.layout.update_parents_relations() - # Fire render event. - self.after_render.fire() + # Fire render event. + self.after_render.fire() - self._update_invalidate_events() + self._update_invalidate_events() # NOTE: We want to make sure this Application is the active one. The # invalidate function is often called from a context where this @@ -962,7 +960,7 @@ async def in_term() -> None: # but don't use logger. (This works better on Python 2.) print("\nUnhandled exception in event loop:") print(formatted_tb) - print("Exception {}".format(context.get("exception"))) + print(f'Exception {context.get("exception")}') await _do_wait_for_enter("Press ENTER to continue...") @@ -1246,18 +1244,14 @@ def is_running(self) -> bool: @property def is_done(self) -> bool: - if self.future: - return self.future.done() - return False + return self.future.done() if self.future else False def get_used_style_strings(self) -> List[str]: """ Return a list of used style strings. This is helpful for debugging, and for writing a new `Style`. """ - attrs_for_style = self.renderer._attrs_for_style - - if attrs_for_style: + if attrs_for_style := self.renderer._attrs_for_style: return sorted( re.sub(r"\s+", " ", style_str).strip() for style_str in attrs_for_style.keys() @@ -1329,14 +1323,15 @@ def _create_key_bindings( if self.app.key_bindings: key_bindings.append(self.app.key_bindings) - # Add mouse bindings. - key_bindings.append( - ConditionalKeyBindings( - self.app._page_navigation_bindings, - self.app.enable_page_navigation_bindings, + key_bindings.extend( + ( + ConditionalKeyBindings( + self.app._page_navigation_bindings, + self.app.enable_page_navigation_bindings, + ), + self.app._default_bindings, ) ) - key_bindings.append(self.app._default_bindings) # Reverse this list. The current control's key bindings should come # last. They need priority. diff --git a/src/prompt_toolkit/application/run_in_terminal.py b/src/prompt_toolkit/application/run_in_terminal.py index d5ef8aafa3..a66e5dc794 100644 --- a/src/prompt_toolkit/application/run_in_terminal.py +++ b/src/prompt_toolkit/application/run_in_terminal.py @@ -50,10 +50,7 @@ def run_in_terminal( async def run() -> _T: async with in_terminal(render_cli_done=render_cli_done): - if in_executor: - return await run_in_executor_with_context(func) - else: - return func() + return await run_in_executor_with_context(func) if in_executor else func() return ensure_future(run()) diff --git a/src/prompt_toolkit/auto_suggest.py b/src/prompt_toolkit/auto_suggest.py index 7099b8e974..7a8d1b63ac 100644 --- a/src/prompt_toolkit/auto_suggest.py +++ b/src/prompt_toolkit/auto_suggest.py @@ -44,7 +44,7 @@ def __init__(self, text: str) -> None: self.text = text def __repr__(self) -> str: - return "Suggestion(%s)" % self.text + return f"Suggestion({self.text})" class AutoSuggest(metaclass=ABCMeta): diff --git a/src/prompt_toolkit/buffer.py b/src/prompt_toolkit/buffer.py index 6c006a258b..6ad3d2c0b7 100644 --- a/src/prompt_toolkit/buffer.py +++ b/src/prompt_toolkit/buffer.py @@ -119,19 +119,19 @@ def new_text_and_position(self) -> Tuple[str, int]: """ if self.complete_index is None: return self.original_document.text, self.original_document.cursor_position - else: - original_text_before_cursor = self.original_document.text_before_cursor - original_text_after_cursor = self.original_document.text_after_cursor - - c = self.completions[self.complete_index] - if c.start_position == 0: - before = original_text_before_cursor - else: - before = original_text_before_cursor[: c.start_position] + original_text_before_cursor = self.original_document.text_before_cursor + original_text_after_cursor = self.original_document.text_after_cursor + + c = self.completions[self.complete_index] + before = ( + original_text_before_cursor + if c.start_position == 0 + else original_text_before_cursor[: c.start_position] + ) - new_text = before + c.text + original_text_after_cursor - new_cursor_position = len(before) + len(c.text) - return new_text, new_cursor_position + new_text = before + c.text + original_text_after_cursor + new_cursor_position = len(before) + len(c.text) + return new_text, new_cursor_position @property def current_completion(self) -> Optional["Completion"]: @@ -313,11 +313,7 @@ def __init__( self.reset(document=document) def __repr__(self) -> str: - if len(self.text) < 15: - text = self.text - else: - text = self.text[:12] + "..." - + text = self.text if len(self.text) < 15 else f"{self.text[:12]}..." return f"" def reset( @@ -449,15 +445,13 @@ def _set_text(self, value: str) -> bool: working_lines[working_index] = value # Return True when this text has been changed. - if len(value) != len(original_value): + if len(value) != len(original_value) or value != original_value: # For Python 2, it seems that when two strings have a different # length and one is a prefix of the other, Python still scans # character by character to see whether the strings are different. # (Some benchmarking showed significant differences for big # documents. >100,000 of lines.) return True - elif value != original_value: - return True return False def _set_cursor_position(self, value: int) -> bool: @@ -479,16 +473,12 @@ def text(self, value: str) -> None: otherwise set a Document instead.) """ # Ensure cursor position remains within the size of the text. - if self.cursor_position > len(value): - self.cursor_position = len(value) - + self.cursor_position = min(self.cursor_position, len(value)) # Don't allow editing of read-only buffers. if self.read_only(): raise EditReadOnlyBuffer() - changed = self._set_text(value) - - if changed: + if changed := self._set_text(value): self._text_changed() # Reset history search text. @@ -509,14 +499,9 @@ def cursor_position(self, value: int) -> None: assert isinstance(value, int) # Ensure cursor position is within the size of the text. - if value > len(self.text): - value = len(self.text) - if value < 0: - value = 0 - - changed = self._set_cursor_position(value) - - if changed: + value = min(value, len(self.text)) + value = max(value, 0) + if changed := self._set_cursor_position(value): self._cursor_position_changed() @property @@ -959,24 +944,22 @@ def start_history_lines_completion(self) -> None: for i, string in enumerate(self._working_lines): for j, l in enumerate(string.split("\n")): l = l.strip() - if l and l.startswith(current_line): - # When a new line has been found. - if l not in found_completions: - found_completions.add(l) + if l and l.startswith(current_line) and l not in found_completions: + found_completions.add(l) # Create completion. - if i == self.working_index: - display_meta = "Current, line %s" % (j + 1) - else: - display_meta = f"History {i + 1}, line {j + 1}" - - completions.append( - Completion( - text=l, - start_position=-len(current_line), - display_meta=display_meta, - ) + if i == self.working_index: + display_meta = f"Current, line {j + 1}" + else: + display_meta = f"History {i + 1}, line {j + 1}" + + completions.append( + Completion( + text=l, + start_position=-len(current_line), + display_meta=display_meta, ) + ) self._set_completions(completions=completions[::-1]) self.go_to_completion(0) @@ -1421,19 +1404,18 @@ def search_once( working_index, Document(document.text, document.cursor_position + new_index), ) - else: - # No match, go forward in the history. (Include len+1 to wrap around.) - # (Here we should always include all cursor positions, because - # it's a different line.) - for i in range(working_index + 1, len(self._working_lines) + 1): - i %= len(self._working_lines) - - document = Document(self._working_lines[i], 0) - new_index = document.find( - text, include_current_position=True, ignore_case=ignore_case - ) - if new_index is not None: - return (i, Document(document.text, new_index)) + # No match, go forward in the history. (Include len+1 to wrap around.) + # (Here we should always include all cursor positions, because + # it's a different line.) + for i in range(working_index + 1, len(self._working_lines) + 1): + i %= len(self._working_lines) + + document = Document(self._working_lines[i], 0) + new_index = document.find( + text, include_current_position=True, ignore_case=ignore_case + ) + if new_index is not None: + return (i, Document(document.text, new_index)) else: # Try find at the current input. new_index = document.find_backwards(text, ignore_case=ignore_case) @@ -1443,22 +1425,21 @@ def search_once( working_index, Document(document.text, document.cursor_position + new_index), ) - else: - # No match, go back in the history. (Include -1 to wrap around.) - for i in range(working_index - 1, -2, -1): - i %= len(self._working_lines) + # No match, go back in the history. (Include -1 to wrap around.) + for i in range(working_index - 1, -2, -1): + i %= len(self._working_lines) - document = Document( - self._working_lines[i], len(self._working_lines[i]) - ) - new_index = document.find_backwards( - text, ignore_case=ignore_case + document = Document( + self._working_lines[i], len(self._working_lines[i]) + ) + new_index = document.find_backwards( + text, ignore_case=ignore_case + ) + if new_index is not None: + return ( + i, + Document(document.text, len(document.text) + new_index), ) - if new_index is not None: - return ( - i, - Document(document.text, len(document.text) + new_index), - ) return None # Do 'count' search iterations. @@ -1485,18 +1466,16 @@ def document_for_search(self, search_state: SearchState) -> Document: if search_result is None: return self.document - else: - working_index, cursor_position = search_result + working_index, cursor_position = search_result # Keep selection, when `working_index` was not changed. - if working_index == self.working_index: - selection = self.selection_state - else: - selection = None + selection = ( + self.selection_state if working_index == self.working_index else None + ) - return Document( - self._working_lines[working_index], cursor_position, selection=selection - ) + return Document( + self._working_lines[working_index], cursor_position, selection=selection + ) def get_search_position( self, @@ -1515,9 +1494,8 @@ def get_search_position( if search_result is None: return self.cursor_position - else: - working_index, cursor_position = search_result - return cursor_position + working_index, cursor_position = search_result + return cursor_position def apply_search( self, @@ -1839,14 +1817,12 @@ async def async_suggestor() -> None: suggestion = await self.auto_suggest.get_suggestion_async(self, document) - # Set suggestion only if the text was not yet changed. - if self.document == document: - # Set suggestion and redraw interface. - self.suggestion = suggestion - self.on_suggestion_set.fire() - else: + if self.document != document: # Otherwise, restart thread. raise _Retry + # Set suggestion and redraw interface. + self.suggestion = suggestion + self.on_suggestion_set.fire() return async_suggestor @@ -1868,15 +1844,8 @@ def validate_and_handle(self) -> None: """ Validate buffer and handle the accept action. """ - valid = self.validate(set_cursor=True) - - # When the validation succeeded, accept the input. - if valid: - if self.accept_handler: - keep_text = self.accept_handler(self) - else: - keep_text = False - + if valid := self.validate(set_cursor=True): + keep_text = self.accept_handler(self) if self.accept_handler else False self.append_to_history() if not keep_text: @@ -1952,10 +1921,7 @@ def unindent(buffer: Buffer, from_row: int, to_row: int, count: int = 1) -> None def transform(text: str) -> str: remove = " " * count - if text.startswith(remove): - return text[len(remove) :] - else: - return text.lstrip() + return text[len(remove) :] if text.startswith(remove) else text.lstrip() # Apply transformation. new_text = buffer.transform_lines(line_range, transform) @@ -1978,9 +1944,7 @@ def reshape_text(buffer: Buffer, from_row: int, to_row: int) -> None: lines = buffer.text.splitlines(True) lines_before = lines[:from_row] lines_after = lines[to_row + 1 :] - lines_to_reformat = lines[from_row : to_row + 1] - - if lines_to_reformat: + if lines_to_reformat := lines[from_row : to_row + 1]: # Take indentation from the first line. match = re.search(r"^\s*", lines_to_reformat[0]) length = match.end() if match else 0 # `match` can't be None, actually. diff --git a/src/prompt_toolkit/clipboard/in_memory.py b/src/prompt_toolkit/clipboard/in_memory.py index 1902e36a55..8d60ebb92f 100644 --- a/src/prompt_toolkit/clipboard/in_memory.py +++ b/src/prompt_toolkit/clipboard/in_memory.py @@ -35,10 +35,7 @@ def set_data(self, data: ClipboardData) -> None: self._ring.pop() def get_data(self) -> ClipboardData: - if self._ring: - return self._ring[0] - else: - return ClipboardData() + return self._ring[0] if self._ring else ClipboardData() def rotate(self) -> None: if self._ring: diff --git a/src/prompt_toolkit/completion/base.py b/src/prompt_toolkit/completion/base.py index 371c0ea6e5..505839e79f 100644 --- a/src/prompt_toolkit/completion/base.py +++ b/src/prompt_toolkit/completion/base.py @@ -79,13 +79,15 @@ def __repr__(self) -> str: ) def __eq__(self, other: object) -> bool: - if not isinstance(other, Completion): - return False return ( - self.text == other.text - and self.start_position == other.start_position - and self.display == other.display - and self._display_meta == other._display_meta + ( + self.text == other.text + and self.start_position == other.start_position + and self.display == other.display + and self._display_meta == other._display_meta + ) + if isinstance(other, Completion) + else False ) def __hash__(self) -> int: @@ -381,16 +383,10 @@ def get_suffix(completion: Completion) -> str: def _commonprefix(strings: Iterable[str]) -> str: - # Similar to os.path.commonprefix if not strings: return "" - else: - s1 = min(strings) - s2 = max(strings) - - for i, c in enumerate(s1): - if c != s2[i]: - return s1[:i] + s1 = min(strings) + s2 = max(strings) - return s1 + return next((s1[:i] for i, c in enumerate(s1) if c != s2[i]), s1) diff --git a/src/prompt_toolkit/completion/filesystem.py b/src/prompt_toolkit/completion/filesystem.py index 719eac6097..faa0948f02 100644 --- a/src/prompt_toolkit/completion/filesystem.py +++ b/src/prompt_toolkit/completion/filesystem.py @@ -53,9 +53,7 @@ def get_completions( if self.expanduser: text = os.path.expanduser(text) - # Directories where to look. - dirname = os.path.dirname(text) - if dirname: + if dirname := os.path.dirname(text): directories = [ os.path.dirname(os.path.join(p, text)) for p in self.get_paths() ] @@ -70,9 +68,11 @@ def get_completions( for directory in directories: # Look for matches in this directory. if os.path.isdir(directory): - for filename in os.listdir(directory): - if filename.startswith(prefix): - filenames.append((directory, filename)) + filenames.extend( + (directory, filename) + for filename in os.listdir(directory) + if filename.startswith(prefix) + ) # Sort filenames = sorted(filenames, key=lambda k: k[1]) diff --git a/src/prompt_toolkit/completion/fuzzy_completer.py b/src/prompt_toolkit/completion/fuzzy_completer.py index 627adc8738..85a94c3432 100644 --- a/src/prompt_toolkit/completion/fuzzy_completer.py +++ b/src/prompt_toolkit/completion/fuzzy_completer.py @@ -137,10 +137,10 @@ def _get_display( # additional styling or characters). return m.completion.display - result: StyleAndTextTuples = [] + result: StyleAndTextTuples = [ + ("class:fuzzymatch.outside", word[: m.start_pos]) + ] - # Text before match. - result.append(("class:fuzzymatch.outside", word[: m.start_pos])) # The match itself. characters = list(word_before_cursor) diff --git a/src/prompt_toolkit/contrib/regular_languages/compiler.py b/src/prompt_toolkit/contrib/regular_languages/compiler.py index fed78498c0..efa9f90080 100644 --- a/src/prompt_toolkit/contrib/regular_languages/compiler.py +++ b/src/prompt_toolkit/contrib/regular_languages/compiler.py @@ -96,7 +96,7 @@ def __init__( counter = [0] def create_group_func(node: Variable) -> str: - name = "n%s" % counter[0] + name = f"n{counter[0]}" self._group_names_to_nodes[name] = node.varname counter[0] += 1 return name @@ -153,13 +153,11 @@ def _transform( def transform(node: Node) -> str: # Turn `AnyNode` into an OR. if isinstance(node, AnyNode): - return "(?:%s)" % "|".join(transform(c) for c in node.children) + return f'(?:{"|".join((transform(c) for c in node.children))})' - # Concatenate a `NodeSequence` elif isinstance(node, NodeSequence): return "".join(transform(c) for c in node.children) - # For Regex and Lookahead nodes, just insert them literally. elif isinstance(node, Regex): return node.regex @@ -167,14 +165,9 @@ def transform(node: Node) -> str: before = "(?!" if node.negative else "(=" return before + transform(node.childnode) + ")" - # A `Variable` wraps the children into a named group. elif isinstance(node, Variable): - return "(?P<{}>{})".format( - create_group_func(node), - transform(node.childnode), - ) + return f"(?P<{create_group_func(node)}>{transform(node.childnode)})" - # `Repeat`. elif isinstance(node, Repeat): if node.max_repeat is None: if node.min_repeat == 0: @@ -187,11 +180,8 @@ def transform(node: Node) -> str: ("" if node.max_repeat is None else str(node.max_repeat)), ) - return "(?:{}){}{}".format( - transform(node.childnode), - repeat_sign, - ("" if node.greedy else "?"), - ) + return f'(?:{transform(node.childnode)}){repeat_sign}{"" if node.greedy else "?"}' + else: raise TypeError(f"Got {node!r}") @@ -263,11 +253,6 @@ def transform(node: Node) -> Iterable[str]: r for c in children_without_variable for r in transform(c) ) - # For a sequence, generate a pattern for each prefix that ends with - # a variable + one pattern of the complete sequence. - # (This is because, for autocompletion, we match the text before - # the cursor, and completions are given for the variable that we - # match right before the cursor.) elif isinstance(node, NodeSequence): # For all components in the sequence, compute prefix patterns, # as well as full patterns. @@ -295,9 +280,7 @@ def transform(node: Node) -> Iterable[str]: # Start with complete patterns. for i in range(len(node.children)): - result.append("(?:") - result.append(complete[i]) - + result.extend(("(?:", complete[i])) # Add prefix patterns. for i in range(len(node.children) - 1, -1, -1): if variable_nodes[i]: @@ -308,17 +291,15 @@ def transform(node: Node) -> Iterable[str]: result.append("|(?:") # If this yields multiple, we should yield all combinations. assert len(prefixes[i]) == 1 - result.append(prefixes[i][0]) - result.append("))") - + result.extend((prefixes[i][0], "))")) yield "".join(result) elif isinstance(node, Regex): - yield "(?:%s)?" % node.regex + yield f"(?:{node.regex})?" elif isinstance(node, Lookahead): if node.negative: - yield "(?!%s)" % cls._transform(node.childnode, create_group_func) + yield f"(?!{cls._transform(node.childnode, create_group_func)})" else: # Not sure what the correct semantics are in this case. # (Probably it's not worth implementing this.) @@ -340,16 +321,8 @@ def transform(node: Node) -> Iterable[str]: yield from transform(node.childnode) else: for c_str in transform(node.childnode): - if node.max_repeat: - repeat_sign = "{,%i}" % (node.max_repeat - 1) - else: - repeat_sign = "*" - yield "(?:{}){}{}{}".format( - prefix, - repeat_sign, - ("" if node.greedy else "?"), - c_str, - ) + repeat_sign = "{,%i}" % (node.max_repeat - 1) if node.max_repeat else "*" + yield f'(?:{prefix}){repeat_sign}{"" if node.greedy else "?"}{c_str}' else: raise TypeError("Got %r" % node) @@ -364,9 +337,7 @@ def match(self, string: str) -> Optional["Match"]: :param string: The input string. """ - m = self._re.match(string) - - if m: + if m := self._re.match(string): return Match( string, [(self._re, m)], self._group_names_to_nodes, self.unescape_funcs ) @@ -469,9 +440,11 @@ def trailing_input(self) -> Optional["MatchVariable"]: # Find all regex group for the name _INVALID_TRAILING_INPUT. for r, re_match in self._re_matches: - for group_name, group_index in r.groupindex.items(): - if group_name == _INVALID_TRAILING_INPUT: - slices.append(re_match.regs[group_index]) + slices.extend( + re_match.regs[group_index] + for group_name, group_index in r.groupindex.items() + if group_name == _INVALID_TRAILING_INPUT + ) # Take the smallest part. (Smaller trailing text means that a larger input has # been matched, so that is better.) @@ -499,10 +472,7 @@ def __init__(self, tuples: List[Tuple[str, str, Tuple[int, int]]]) -> None: self._tuples = tuples def __repr__(self) -> str: - return "{}({})".format( - self.__class__.__name__, - ", ".join(f"{k}={v!r}" for k, v, _ in self._tuples), - ) + return f'{self.__class__.__name__}({", ".join((f"{k}={v!r}" for k, v, _ in self._tuples))})' def get(self, key: str, default: Optional[str] = None) -> Optional[str]: items = self.getall(key) diff --git a/src/prompt_toolkit/contrib/regular_languages/completion.py b/src/prompt_toolkit/contrib/regular_languages/completion.py index 3cebcc03f6..5d0a0651c1 100644 --- a/src/prompt_toolkit/contrib/regular_languages/completion.py +++ b/src/prompt_toolkit/contrib/regular_languages/completion.py @@ -33,15 +33,11 @@ def __init__( def get_completions( self, document: Document, complete_event: CompleteEvent ) -> Iterable[Completion]: - m = self.compiled_grammar.match_prefix(document.text_before_cursor) - - if m: - completions = self._remove_duplicates( + if m := self.compiled_grammar.match_prefix(document.text_before_cursor): + yield from self._remove_duplicates( self._get_completions_for_match(m, complete_event) ) - yield from completions - def _get_completions_for_match( self, match: Match, complete_event: CompleteEvent ) -> Iterable[Completion]: @@ -54,9 +50,7 @@ def _get_completions_for_match( varname = match_variable.varname start = match_variable.start - completer = self.completers.get(varname) - - if completer: + if completer := self.completers.get(varname): text = match_variable.value # Unwrap text. diff --git a/src/prompt_toolkit/contrib/regular_languages/lexer.py b/src/prompt_toolkit/contrib/regular_languages/lexer.py index 1ddeede802..15dc4a38d9 100644 --- a/src/prompt_toolkit/contrib/regular_languages/lexer.py +++ b/src/prompt_toolkit/contrib/regular_languages/lexer.py @@ -43,42 +43,33 @@ def __init__( self.lexers = lexers or {} def _get_text_fragments(self, text: str) -> StyleAndTextTuples: - m = self.compiled_grammar.match_prefix(text) - - if m: - characters: StyleAndTextTuples = [(self.default_style, c) for c in text] - - for v in m.variables(): - # If we have a `Lexer` instance for this part of the input. - # Tokenize recursively and apply tokens. - lexer = self.lexers.get(v.varname) - - if lexer: - document = Document(text[v.start : v.stop]) - lexer_tokens_for_line = lexer.lex_document(document) - text_fragments: StyleAndTextTuples = [] - for i in range(len(document.lines)): - text_fragments.extend(lexer_tokens_for_line(i)) - text_fragments.append(("", "\n")) - if text_fragments: - text_fragments.pop() - - i = v.start - for t, s, *_ in text_fragments: - for c in s: - if characters[i][0] == self.default_style: - characters[i] = (t, characters[i][1]) - i += 1 - - # Highlight trailing input. - trailing_input = m.trailing_input() - if trailing_input: - for i in range(trailing_input.start, trailing_input.stop): - characters[i] = ("class:trailing-input", characters[i][1]) - - return characters - else: + if not (m := self.compiled_grammar.match_prefix(text)): return [("", text)] + characters: StyleAndTextTuples = [(self.default_style, c) for c in text] + + for v in m.variables(): + if lexer := self.lexers.get(v.varname): + document = Document(text[v.start : v.stop]) + lexer_tokens_for_line = lexer.lex_document(document) + text_fragments: StyleAndTextTuples = [] + for i in range(len(document.lines)): + text_fragments.extend(lexer_tokens_for_line(i)) + text_fragments.append(("", "\n")) + if text_fragments: + text_fragments.pop() + + i = v.start + for t, s, *_ in text_fragments: + for _ in s: + if characters[i][0] == self.default_style: + characters[i] = (t, characters[i][1]) + i += 1 + + if trailing_input := m.trailing_input(): + for i in range(trailing_input.start, trailing_input.stop): + characters[i] = ("class:trailing-input", characters[i][1]) + + return characters def lex_document(self, document: Document) -> Callable[[int], StyleAndTextTuples]: lines = list(split_lines(self._get_text_fragments(document.text))) diff --git a/src/prompt_toolkit/contrib/regular_languages/regex_parser.py b/src/prompt_toolkit/contrib/regular_languages/regex_parser.py index 87e39d498c..5e8745a272 100644 --- a/src/prompt_toolkit/contrib/regular_languages/regex_parser.py +++ b/src/prompt_toolkit/contrib/regular_languages/regex_parser.py @@ -180,8 +180,7 @@ def tokenize_regex(input: str) -> List[str]: tokens = [] while input: - m = p.match(input) - if m: + if m := p.match(input): token, input = input[: m.end()], input[m.end() :] if not token.isspace(): tokens.append(token) @@ -200,10 +199,7 @@ def parse_regex(regex_tokens: List[str]) -> Node: def wrap(lst: List[Node]) -> Node: """Turn list into sequence when it contains several items.""" - if len(lst) == 1: - return lst[0] - else: - return NodeSequence(lst) + return lst[0] if len(lst) == 1 else NodeSequence(lst) def _parse() -> Node: or_list: List[List[Node]] = [] @@ -212,9 +208,8 @@ def _parse() -> Node: def wrapped_result() -> Node: if or_list == []: return wrap(result) - else: - or_list.append(result) - return AnyNode([wrap(i) for i in or_list]) + or_list.append(result) + return AnyNode([wrap(i) for i in or_list]) while tokens: t = tokens.pop() diff --git a/src/prompt_toolkit/contrib/regular_languages/validation.py b/src/prompt_toolkit/contrib/regular_languages/validation.py index 71d3434035..d27e8de526 100644 --- a/src/prompt_toolkit/contrib/regular_languages/validation.py +++ b/src/prompt_toolkit/contrib/regular_languages/validation.py @@ -31,30 +31,22 @@ def __init__( self.validators = validators def validate(self, document: Document) -> None: - # Parse input document. - # We use `match`, not `match_prefix`, because for validation, we want - # the actual, unambiguous interpretation of the input. - m = self.compiled_grammar.match(document.text) - - if m: - for v in m.variables(): - validator = self.validators.get(v.varname) - - if validator: - # Unescape text. - unwrapped_text = self.compiled_grammar.unescape(v.varname, v.value) - - # Create a document, for the completions API (text/cursor_position) - inner_document = Document(unwrapped_text, len(unwrapped_text)) - - try: - validator.validate(inner_document) - except ValidationError as e: - raise ValidationError( - cursor_position=v.start + e.cursor_position, - message=e.message, - ) from e - else: + if not (m := self.compiled_grammar.match(document.text)): raise ValidationError( cursor_position=len(document.text), message="Invalid command" ) + for v in m.variables(): + if validator := self.validators.get(v.varname): + # Unescape text. + unwrapped_text = self.compiled_grammar.unescape(v.varname, v.value) + + # Create a document, for the completions API (text/cursor_position) + inner_document = Document(unwrapped_text, len(unwrapped_text)) + + try: + validator.validate(inner_document) + except ValidationError as e: + raise ValidationError( + cursor_position=v.start + e.cursor_position, + message=e.message, + ) from e diff --git a/src/prompt_toolkit/contrib/ssh/server.py b/src/prompt_toolkit/contrib/ssh/server.py index 98f3b68177..f2c5f1a24a 100644 --- a/src/prompt_toolkit/contrib/ssh/server.py +++ b/src/prompt_toolkit/contrib/ssh/server.py @@ -41,10 +41,10 @@ def write(s, data: str) -> None: except BrokenPipeError: pass # Channel not open for sending. - def isatty(s) -> bool: + def isatty(self) -> bool: return True - def flush(s) -> None: + def flush(self) -> None: pass @property @@ -60,9 +60,8 @@ def _get_size(self) -> Size: """ if self._chan is None: return Size(rows=20, columns=79) - else: - width, height, pixwidth, pixheight = self._chan.get_terminal_size() - return Size(rows=height, columns=width) + width, height, pixwidth, pixheight = self._chan.get_terminal_size() + return Size(rows=height, columns=width) def connection_made(self, chan: Any) -> None: self._chan = chan diff --git a/src/prompt_toolkit/contrib/telnet/protocol.py b/src/prompt_toolkit/contrib/telnet/protocol.py index 68f3639880..fb627e40e6 100644 --- a/src/prompt_toolkit/contrib/telnet/protocol.py +++ b/src/prompt_toolkit/contrib/telnet/protocol.py @@ -131,7 +131,7 @@ def ttype(self, data: bytes) -> None: """ Received terminal type. """ - subcmd, data = data[0:1], data[1:] + subcmd, data = data[:1], data[1:] if subcmd == IS: ttype = data.decode("ascii") self.ttype_received_callback(ttype) @@ -142,7 +142,7 @@ def negotiate(self, data: bytes) -> None: """ Got negotiate data. """ - command, payload = data[0:1], data[1:] + command, payload = data[:1], data[1:] if command == NAWS: self.naws(payload) diff --git a/src/prompt_toolkit/contrib/telnet/server.py b/src/prompt_toolkit/contrib/telnet/server.py index b6248cd3e5..98be17f2cd 100644 --- a/src/prompt_toolkit/contrib/telnet/server.py +++ b/src/prompt_toolkit/contrib/telnet/server.py @@ -181,8 +181,7 @@ async def run_application(self) -> None: """ def handle_incoming_data() -> None: - data = self.conn.recv(1024) - if data: + if data := self.conn.recv(1024): self.feed(data) else: # Connection closed by client. @@ -364,7 +363,7 @@ async def run() -> None: # Unhandled control-c propagated by a prompt. logger.info("Unhandled KeyboardInterrupt in telnet application.") except BaseException as e: - print("Got %s" % type(e).__name__, e) + print(f"Got {type(e).__name__}", e) import traceback traceback.print_exc() diff --git a/src/prompt_toolkit/document.py b/src/prompt_toolkit/document.py index 1984155205..91ea8e2d0d 100644 --- a/src/prompt_toolkit/document.py +++ b/src/prompt_toolkit/document.py @@ -140,13 +140,14 @@ def __repr__(self) -> str: return f"{self.__class__.__name__}({self.text!r}, {self.cursor_position!r})" def __eq__(self, other: object) -> bool: - if not isinstance(other, Document): - return False - return ( - self.text == other.text - and self.cursor_position == other.cursor_position - and self.selection == other.selection + ( + self.text == other.text + and self.cursor_position == other.cursor_position + and self.selection == other.selection + ) + if isinstance(other, Document) + else False ) @property @@ -398,10 +399,7 @@ def find( try: for i, match in enumerate(iterator): if i + 1 == count: - if include_current_position: - return match.start(0) - else: - return match.start(0) + 1 + return match.start(0) if include_current_position else match.start(0) + 1 except StopIteration: pass return None @@ -604,11 +602,7 @@ def find_next_word_ending( if i + 1 == count: value = match.end(1) - if include_current_position: - return value - else: - return value + 1 - + return value if include_current_position else value + 1 except StopIteration: pass return None @@ -771,11 +765,7 @@ def find_enclosing_bracket_right( if self.current_char == right_ch: return 0 - if end_pos is None: - end_pos = len(self.text) - else: - end_pos = min(len(self.text), end_pos) - + end_pos = len(self.text) if end_pos is None else min(len(self.text), end_pos) stack = 1 # Look forward. @@ -804,11 +794,7 @@ def find_enclosing_bracket_left( if self.current_char == left_ch: return 0 - if start_pos is None: - start_pos = 0 - else: - start_pos = max(0, start_pos) - + start_pos = 0 if start_pos is None else max(0, start_pos) stack = 1 # Look backward. @@ -855,15 +841,14 @@ def get_end_of_document_position(self) -> int: def get_start_of_line_position(self, after_whitespace: bool = False) -> int: """Relative position for the start of this line.""" - if after_whitespace: - current_line = self.current_line - return ( - len(current_line) - - len(current_line.lstrip()) - - self.cursor_position_col - ) - else: + if not after_whitespace: return -len(self.current_line_before_cursor) + current_line = self.current_line + return ( + len(current_line) + - len(current_line.lstrip()) + - self.cursor_position_col + ) def get_end_of_line_position(self) -> int: """Relative position for the end of this line.""" @@ -917,46 +902,47 @@ def selection_ranges(self) -> Iterable[Tuple[int, int]]: This will return zero ranges, like (8,8) for empty lines in a block selection. """ - if self.selection: - from_, to = sorted( - [self.cursor_position, self.selection.original_cursor_position] - ) - - if self.selection.type == SelectionType.BLOCK: - from_line, from_column = self.translate_index_to_position(from_) - to_line, to_column = self.translate_index_to_position(to) - from_column, to_column = sorted([from_column, to_column]) - lines = self.lines - - if vi_mode(): - to_column += 1 - - for l in range(from_line, to_line + 1): - line_length = len(lines[l]) + if not self.selection: + return + from_, to = sorted( + [self.cursor_position, self.selection.original_cursor_position] + ) - if from_column <= line_length: - yield ( - self.translate_row_col_to_index(l, from_column), - self.translate_row_col_to_index( - l, min(line_length, to_column) - ), - ) - else: - # In case of a LINES selection, go to the start/end of the lines. - if self.selection.type == SelectionType.LINES: - from_ = max(0, self.text.rfind("\n", 0, from_) + 1) + if self.selection.type == SelectionType.BLOCK: + from_line, from_column = self.translate_index_to_position(from_) + to_line, to_column = self.translate_index_to_position(to) + from_column, to_column = sorted([from_column, to_column]) + lines = self.lines + + if vi_mode(): + to_column += 1 + + for l in range(from_line, to_line + 1): + line_length = len(lines[l]) + + if from_column <= line_length: + yield ( + self.translate_row_col_to_index(l, from_column), + self.translate_row_col_to_index( + l, min(line_length, to_column) + ), + ) + else: + # In case of a LINES selection, go to the start/end of the lines. + if self.selection.type == SelectionType.LINES: + from_ = max(0, self.text.rfind("\n", 0, from_) + 1) - if self.text.find("\n", to) >= 0: - to = self.text.find("\n", to) - else: - to = len(self.text) - 1 + if self.text.find("\n", to) >= 0: + to = self.text.find("\n", to) + else: + to = len(self.text) - 1 - # In Vi mode, the upper boundary is always included. For Emacs, - # that's not the case. - if vi_mode(): - to += 1 + # In Vi mode, the upper boundary is always included. For Emacs, + # that's not the case. + if vi_mode(): + to += 1 - yield from_, to + yield from_, to def selection_range_at_line(self, row: int) -> Optional[Tuple[int, int]]: """ @@ -1014,35 +1000,34 @@ def cut_selection(self) -> Tuple["Document", ClipboardData]: document represents the new document when the selection is cut, and the clipboard data, represents whatever has to be put on the clipboard. """ - if self.selection: - cut_parts = [] - remaining_parts = [] - new_cursor_position = self.cursor_position + if not self.selection: + return self, ClipboardData("") + cut_parts = [] + remaining_parts = [] + new_cursor_position = self.cursor_position - last_to = 0 - for from_, to in self.selection_ranges(): - if last_to == 0: - new_cursor_position = from_ + last_to = 0 + for from_, to in self.selection_ranges(): + if last_to == 0: + new_cursor_position = from_ - remaining_parts.append(self.text[last_to:from_]) - cut_parts.append(self.text[from_:to]) - last_to = to + remaining_parts.append(self.text[last_to:from_]) + cut_parts.append(self.text[from_:to]) + last_to = to - remaining_parts.append(self.text[last_to:]) + remaining_parts.append(self.text[last_to:]) - cut_text = "\n".join(cut_parts) - remaining_text = "".join(remaining_parts) + cut_text = "\n".join(cut_parts) + remaining_text = "".join(remaining_parts) - # In case of a LINES selection, don't include the trailing newline. - if self.selection.type == SelectionType.LINES and cut_text.endswith("\n"): - cut_text = cut_text[:-1] + # In case of a LINES selection, don't include the trailing newline. + if self.selection.type == SelectionType.LINES and cut_text.endswith("\n"): + cut_text = cut_text[:-1] - return ( - Document(text=remaining_text, cursor_position=new_cursor_position), - ClipboardData(cut_text, self.selection.type), - ) - else: - return self, ClipboardData("") + return ( + Document(text=remaining_text, cursor_position=new_cursor_position), + ClipboardData(cut_text, self.selection.type), + ) def paste_clipboard_data( self, @@ -1080,13 +1065,11 @@ def paste_clipboard_data( l = self.cursor_position_row if before: lines = self.lines[:l] + [data.text] * count + self.lines[l:] - new_text = "\n".join(lines) new_cursor_position = len("".join(self.lines[:l])) + l else: lines = self.lines[: l + 1] + [data.text] * count + self.lines[l + 1 :] new_cursor_position = len("".join(self.lines[: l + 1])) + l + 1 - new_text = "\n".join(lines) - + new_text = "\n".join(lines) elif data.type == SelectionType.BLOCK: lines = self.lines[:] start_line = self.cursor_position_row diff --git a/src/prompt_toolkit/eventloop/async_context_manager.py b/src/prompt_toolkit/eventloop/async_context_manager.py index 39146165a0..3ee735682f 100644 --- a/src/prompt_toolkit/eventloop/async_context_manager.py +++ b/src/prompt_toolkit/eventloop/async_context_manager.py @@ -89,9 +89,11 @@ async def __aexit__(self, typ, value, traceback): # have this behavior). But do this only if the exception wrapped # by the RuntimeError is actully Stop(Async)Iteration (see # issue29692). - if isinstance(value, (StopIteration, StopAsyncIteration)): - if exc.__cause__ is value: - return False + if ( + isinstance(value, (StopIteration, StopAsyncIteration)) + and exc.__cause__ is value + ): + return False raise except BaseException as exc: if exc is not value: diff --git a/src/prompt_toolkit/eventloop/inputhook.py b/src/prompt_toolkit/eventloop/inputhook.py index 05d298117e..1a3d830a2b 100644 --- a/src/prompt_toolkit/eventloop/inputhook.py +++ b/src/prompt_toolkit/eventloop/inputhook.py @@ -54,8 +54,7 @@ def new_eventloop_with_inputhook( Create a new event loop with the given inputhook. """ selector = InputHookSelector(selectors.DefaultSelector(), inputhook) - loop = asyncio.SelectorEventLoop(selector) - return loop + return asyncio.SelectorEventLoop(selector) def set_eventloop_with_inputhook( diff --git a/src/prompt_toolkit/eventloop/utils.py b/src/prompt_toolkit/eventloop/utils.py index 2e5a05e838..afa4cf92e6 100644 --- a/src/prompt_toolkit/eventloop/utils.py +++ b/src/prompt_toolkit/eventloop/utils.py @@ -90,8 +90,7 @@ def get_traceback_from_context(context: Dict[str, Any]) -> Optional[TracebackTyp """ Get the traceback object from the context. """ - exception = context.get("exception") - if exception: + if exception := context.get("exception"): if hasattr(exception, "__traceback__"): return cast(TracebackType, exception.__traceback__) else: diff --git a/src/prompt_toolkit/eventloop/win32.py b/src/prompt_toolkit/eventloop/win32.py index fbc02d493a..9639ccd1e2 100644 --- a/src/prompt_toolkit/eventloop/win32.py +++ b/src/prompt_toolkit/eventloop/win32.py @@ -52,10 +52,7 @@ def wait_for_handles( len(handle_array), handle_array, BOOL(False), DWORD(timeout) ) - if ret == WAIT_TIMEOUT: - return None - else: - return handles[ret] + return None if ret == WAIT_TIMEOUT else handles[ret] def create_win32_event() -> HANDLE: diff --git a/src/prompt_toolkit/filters/app.py b/src/prompt_toolkit/filters/app.py index dcc3fc0c6e..668c80b214 100644 --- a/src/prompt_toolkit/filters/app.py +++ b/src/prompt_toolkit/filters/app.py @@ -87,10 +87,10 @@ def test() -> bool: # focused. current_window = get_app().layout.current_window - for c in walk(cast(Container, value)): - if isinstance(c, Window) and c == current_window: - return True - return False + return any( + isinstance(c, Window) and c == current_window + for c in walk(cast(Container, value)) + ) @Condition def has_focus_filter() -> bool: diff --git a/src/prompt_toolkit/formatted_text/ansi.py b/src/prompt_toolkit/formatted_text/ansi.py index 2a30b09c21..d68272cb83 100644 --- a/src/prompt_toolkit/formatted_text/ansi.py +++ b/src/prompt_toolkit/formatted_text/ansi.py @@ -98,7 +98,6 @@ def _parse_corot(self) -> Generator[None, str, None]: if char.isdigit(): current += char - # Eval number else: # Limit and save number value params.append(min(int(current or 0), 9999)) @@ -107,16 +106,14 @@ def _parse_corot(self) -> Generator[None, str, None]: if char == ";": current = "" - # Check and evaluate color codes elif char == "m": # Set attributes and token. self._select_graphic_rendition(params) style = self._create_style_string() break - # Check and evaluate cursor forward elif char == "C": - for i in range(params[0]): + for _ in range(params[0]): # add using current style formatted_text.append((style, " ")) break @@ -137,11 +134,7 @@ def _select_graphic_rendition(self, attrs: List[int]) -> None: """ Taken a list of graphics attributes and apply changes. """ - if not attrs: - attrs = [0] - else: - attrs = list(attrs[::-1]) - + attrs = list(attrs[::-1]) if attrs else [0] while attrs: attr = attrs.pop() @@ -151,8 +144,6 @@ def _select_graphic_rendition(self, attrs: List[int]) -> None: self._bgcolor = _bg_colors[attr] elif attr == 1: self._bold = True - # elif attr == 2: - # self._faint = True elif attr == 3: self._italic = True elif attr == 4: @@ -197,7 +188,7 @@ def _select_graphic_rendition(self, attrs: List[int]) -> None: n = attrs.pop() # 256 colors. - if n == 5 and len(attrs) >= 1: + if n == 5 and attrs: if attr == 38: m = attrs.pop() self._color = _256_colors.get(m) @@ -229,7 +220,7 @@ def _create_style_string(self) -> str: if self._color: result.append(self._color) if self._bgcolor: - result.append("bg:" + self._bgcolor) + result.append(f"bg:{self._bgcolor}") if self._bold: result.append("bold") if self._underline: @@ -276,10 +267,10 @@ def __mod__(self, value: object) -> "ANSI": _bg_colors = {v: k for k, v in BG_ANSI_COLORS.items()} # Mapping of the escape codes for 256colors to their 'ffffff' value. -_256_colors = {} - -for i, (r, g, b) in enumerate(_256_colors_table.colors): - _256_colors[i] = f"#{r:02x}{g:02x}{b:02x}" +_256_colors = { + i: f"#{r:02x}{g:02x}{b:02x}" + for i, (r, g, b) in enumerate(_256_colors_table.colors) +} def ansi_escape(text: object) -> str: diff --git a/src/prompt_toolkit/formatted_text/base.py b/src/prompt_toolkit/formatted_text/base.py index e88c5935a5..5d1bd281f8 100644 --- a/src/prompt_toolkit/formatted_text/base.py +++ b/src/prompt_toolkit/formatted_text/base.py @@ -89,16 +89,14 @@ def to_formatted_text( if style: result = cast( StyleAndTextTuples, - [(style + " " + item_style, *rest) for item_style, *rest in result], + [(f"{style} {item_style}", *rest) for item_style, *rest in result], ) + # Make sure the result is wrapped in a `FormattedText`. Among other # reasons, this is important for `print_formatted_text` to work correctly # and distinguish between lists and formatted text. - if isinstance(result, FormattedText): - return result - else: - return FormattedText(result) + return result if isinstance(result, FormattedText) else FormattedText(result) def is_formatted_text(value: object) -> "TypeGuard[AnyFormattedText]": @@ -128,7 +126,7 @@ def __pt_formatted_text__(self) -> StyleAndTextTuples: return self def __repr__(self) -> str: - return "FormattedText(%s)" % super().__repr__() + return f"FormattedText({super().__repr__()})" class Template: diff --git a/src/prompt_toolkit/formatted_text/html.py b/src/prompt_toolkit/formatted_text/html.py index 0af2b18b57..aa53f3d153 100644 --- a/src/prompt_toolkit/formatted_text/html.py +++ b/src/prompt_toolkit/formatted_text/html.py @@ -44,9 +44,9 @@ def get_current_style() -> str: parts.append("class:" + ",".join(name_stack)) if fg_stack: - parts.append("fg:" + fg_stack[-1]) + parts.append(f"fg:{fg_stack[-1]}") if bg_stack: - parts.append("bg:" + bg_stack[-1]) + parts.append(f"bg:{bg_stack[-1]}") return " ".join(parts) def process_node(node: Any) -> None: @@ -63,13 +63,10 @@ def process_node(node: Any) -> None: fg = bg = "" for k, v in child.attributes.items(): - if k == "fg": - fg = v if k == "bg": bg = v - if k == "color": - fg = v # Alias for 'fg'. - + elif k in ["fg", "color"]: + fg = v # Check for spaces in attributes. This would result in # invalid style strings otherwise. if " " in fg: diff --git a/src/prompt_toolkit/formatted_text/pygments.py b/src/prompt_toolkit/formatted_text/pygments.py index dd16f0efbe..1ac32e4324 100644 --- a/src/prompt_toolkit/formatted_text/pygments.py +++ b/src/prompt_toolkit/formatted_text/pygments.py @@ -22,9 +22,7 @@ def __init__(self, token_list: List[Tuple["Token", str]]) -> None: self.token_list = token_list def __pt_formatted_text__(self) -> StyleAndTextTuples: - result: StyleAndTextTuples = [] - - for token, text in self.token_list: - result.append(("class:" + pygments_token_to_classname(token), text)) - - return result + return [ + (f"class:{pygments_token_to_classname(token)}", text) + for token, text in self.token_list + ] diff --git a/src/prompt_toolkit/history.py b/src/prompt_toolkit/history.py index 987d7175de..3e8f21d3ee 100644 --- a/src/prompt_toolkit/history.py +++ b/src/prompt_toolkit/history.py @@ -225,10 +225,7 @@ class InMemoryHistory(History): def __init__(self, history_strings: Optional[Sequence[str]] = None) -> None: super().__init__() # Emulating disk storage. - if history_strings is None: - self._storage = [] - else: - self._storage = list(history_strings) + self._storage = [] if history_strings is None else list(history_strings) def load_history_strings(self) -> Iterable[str]: yield from self._storage[::-1] diff --git a/src/prompt_toolkit/input/ansi_escape_sequences.py b/src/prompt_toolkit/input/ansi_escape_sequences.py index 2e6c5b9b28..59238952c5 100644 --- a/src/prompt_toolkit/input/ansi_escape_sequences.py +++ b/src/prompt_toolkit/input/ansi_escape_sequences.py @@ -333,9 +333,8 @@ def _get_reverse_ansi_sequences() -> Dict[Keys, str]: result: Dict[Keys, str] = {} for sequence, key in ANSI_SEQUENCES.items(): - if not isinstance(key, tuple): - if key not in result: - result[key] = sequence + if not isinstance(key, tuple) and key not in result: + result[key] = sequence return result diff --git a/src/prompt_toolkit/input/base.py b/src/prompt_toolkit/input/base.py index 313622de5a..ad7e6f3d5e 100644 --- a/src/prompt_toolkit/input/base.py +++ b/src/prompt_toolkit/input/base.py @@ -114,7 +114,7 @@ def fileno(self) -> int: raise NotImplementedError def typeahead_hash(self) -> str: - return "dummy-%s" % id(self) + return f"dummy-{id(self)}" def read_keys(self) -> List[KeyPress]: return [] diff --git a/src/prompt_toolkit/input/posix_pipe.py b/src/prompt_toolkit/input/posix_pipe.py index 1e7dec77fd..9947dcad6b 100644 --- a/src/prompt_toolkit/input/posix_pipe.py +++ b/src/prompt_toolkit/input/posix_pipe.py @@ -66,7 +66,7 @@ def __init__(self, _pipe: _Pipe, _text: str = "") -> None: class Stdin: encoding = "utf-8" - def isatty(stdin) -> bool: + def isatty(self) -> bool: return True def fileno(stdin) -> int: diff --git a/src/prompt_toolkit/input/vt100.py b/src/prompt_toolkit/input/vt100.py index 45ce37208a..7539a7c522 100644 --- a/src/prompt_toolkit/input/vt100.py +++ b/src/prompt_toolkit/input/vt100.py @@ -289,10 +289,8 @@ def _patch_iflag(cls, attrs: int) -> int: def __exit__(self, *a: object) -> None: if self.attrs_before is not None: - try: + with contextlib.suppress(termios.error): termios.tcsetattr(self.fileno, termios.TCSANOW, self.attrs_before) - except termios.error: - pass # # Put the terminal in application mode. # self._stdout.write('\x1b[?1h') diff --git a/src/prompt_toolkit/input/vt100_parser.py b/src/prompt_toolkit/input/vt100_parser.py index 3ee1e14fdd..f2d596f147 100644 --- a/src/prompt_toolkit/input/vt100_parser.py +++ b/src/prompt_toolkit/input/vt100_parser.py @@ -145,27 +145,26 @@ def _input_parser_generator(self) -> Generator[None, Union[str, _Flush], None]: match = self._get_match(prefix) # Exact matches found, call handlers.. - if (flush or not is_prefix_of_longer_match) and match: - self._call_handler(match, prefix) - prefix = "" - - # No exact match found. - elif (flush or not is_prefix_of_longer_match) and not match: - found = False - retry = True - - # Loop over the input, try the longest match first and - # shift. - for i in range(len(prefix), 0, -1): - match = self._get_match(prefix[:i]) - if match: - self._call_handler(match, prefix[:i]) - prefix = prefix[i:] - found = True - - if not found: - self._call_handler(prefix[0], prefix[0]) - prefix = prefix[1:] + if (flush or not is_prefix_of_longer_match): + if match: + self._call_handler(match, prefix) + prefix = "" + + else: + found = False + retry = True + + # Loop over the input, try the longest match first and + # shift. + for i in range(len(prefix), 0, -1): + if match := self._get_match(prefix[:i]): + self._call_handler(match, prefix[:i]) + prefix = prefix[i:] + found = True + + if not found: + self._call_handler(prefix[0], prefix[0]) + prefix = prefix[1:] def _call_handler( self, key: Union[str, Keys, Tuple[Keys, ...]], insert_text: str @@ -180,12 +179,11 @@ def _call_handler( # multiple times). for i, k in enumerate(key): self._call_handler(k, insert_text if i == 0 else "") + elif key == Keys.BracketedPaste: + self._in_bracketed_paste = True + self._paste_buffer = "" else: - if key == Keys.BracketedPaste: - self._in_bracketed_paste = True - self._paste_buffer = "" - else: - self.feed_key_callback(KeyPress(key, insert_text)) + self.feed_key_callback(KeyPress(key, insert_text)) def feed(self, data: str) -> None: """ diff --git a/src/prompt_toolkit/input/win32.py b/src/prompt_toolkit/input/win32.py index db3fa2badd..13cb3788ff 100644 --- a/src/prompt_toolkit/input/win32.py +++ b/src/prompt_toolkit/input/win32.py @@ -404,15 +404,14 @@ def _event_to_key_presses(self, ev: KEY_EVENT_RECORD) -> List[KeyPress]: if u_char == "\x00": if ev.VirtualKeyCode in self.keycodes: result = KeyPress(self.keycodes[ev.VirtualKeyCode], "") + elif ascii_char in self.mappings: + if self.mappings[ascii_char] == Keys.ControlJ: + u_char = ( + "\n" # Windows sends \n, turn into \r for unix compatibility. + ) + result = KeyPress(self.mappings[ascii_char], u_char) else: - if ascii_char in self.mappings: - if self.mappings[ascii_char] == Keys.ControlJ: - u_char = ( - "\n" # Windows sends \n, turn into \r for unix compatibility. - ) - result = KeyPress(self.mappings[ascii_char], u_char) - else: - result = KeyPress(u_char, u_char) + result = KeyPress(u_char, u_char) # First we handle Shift-Control-Arrow/Home/End (need to do this first) if ( @@ -496,24 +495,12 @@ def _event_to_key_presses(self, ev: KEY_EVENT_RECORD) -> List[KeyPress]: ): return [KeyPress(Keys.Escape, ""), result] - # Return result. If alt was pressed, prefix the result with an - # 'Escape' key, just like unix VT100 terminals do. - - # NOTE: Only replace the left alt with escape. The right alt key often - # acts as altgr and is used in many non US keyboard layouts for - # typing some special characters, like a backslash. We don't want - # all backslashes to be prefixed with escape. (Esc-\ has a - # meaning in E-macs, for instance.) - if result: - meta_pressed = control_key_state & self.LEFT_ALT_PRESSED - - if meta_pressed: - return [KeyPress(Keys.Escape, ""), result] - else: - return [result] - - else: + if not result: return [] + if meta_pressed := control_key_state & self.LEFT_ALT_PRESSED: + return [KeyPress(Keys.Escape, ""), result] + else: + return [result] def _handle_mouse(self, ev: MOUSE_EVENT_RECORD) -> List[KeyPress]: """ @@ -527,23 +514,22 @@ def _handle_mouse(self, ev: MOUSE_EVENT_RECORD) -> List[KeyPress]: # Scroll events. if event_flags & MOUSE_WHEELED: - if button_state > 0: - event_type = MouseEventType.SCROLL_UP - else: - event_type = MouseEventType.SCROLL_DOWN - else: - # Handle button state for non-scroll events. - if button_state == FROM_LEFT_1ST_BUTTON_PRESSED: - button = MouseButton.LEFT + event_type = ( + MouseEventType.SCROLL_UP + if button_state > 0 + else MouseEventType.SCROLL_DOWN + ) + + elif button_state == FROM_LEFT_1ST_BUTTON_PRESSED: + button = MouseButton.LEFT - elif button_state == RIGHTMOST_BUTTON_PRESSED: - button = MouseButton.RIGHT + elif button_state == RIGHTMOST_BUTTON_PRESSED: + button = MouseButton.RIGHT # Move events. if event_flags & MOUSE_MOVED: event_type = MouseEventType.MOUSE_MOVE - # No key pressed anymore: mouse up. if event_type is None: if button_state > 0: # Some button pressed. diff --git a/src/prompt_toolkit/key_binding/bindings/auto_suggest.py b/src/prompt_toolkit/key_binding/bindings/auto_suggest.py index c016c0688f..0688b50fcf 100644 --- a/src/prompt_toolkit/key_binding/bindings/auto_suggest.py +++ b/src/prompt_toolkit/key_binding/bindings/auto_suggest.py @@ -43,9 +43,7 @@ def _accept(event: E) -> None: Accept suggestion. """ b = event.current_buffer - suggestion = b.suggestion - - if suggestion: + if suggestion := b.suggestion: b.insert_text(suggestion.text) @handle("escape", "f", filter=suggestion_available & emacs_mode) @@ -54,9 +52,7 @@ def _fill(event: E) -> None: Fill partial suggestion. """ b = event.current_buffer - suggestion = b.suggestion - - if suggestion: + if suggestion := b.suggestion: t = re.split(r"(\S+\s+)", suggestion.text) b.insert_text(next(x for x in t if x)) diff --git a/src/prompt_toolkit/key_binding/bindings/emacs.py b/src/prompt_toolkit/key_binding/bindings/emacs.py index a4a5e348f8..df76aa6c60 100644 --- a/src/prompt_toolkit/key_binding/bindings/emacs.py +++ b/src/prompt_toolkit/key_binding/bindings/emacs.py @@ -496,10 +496,13 @@ def _extend_selection(event: E) -> None: unshift_move(event) buff = event.current_buffer - if buff.selection_state is not None: - if buff.cursor_position == buff.selection_state.original_cursor_position: - # selection is now empty, so cancel selection - buff.exit_selection() + if ( + buff.selection_state is not None + and buff.cursor_position + == buff.selection_state.original_cursor_position + ): + # selection is now empty, so cancel selection + buff.exit_selection() @handle(Keys.Any, filter=shift_selection_mode) def _replace_selection(event: E) -> None: diff --git a/src/prompt_toolkit/key_binding/bindings/named_commands.py b/src/prompt_toolkit/key_binding/bindings/named_commands.py index e0796ef0b8..af5bf77364 100644 --- a/src/prompt_toolkit/key_binding/bindings/named_commands.py +++ b/src/prompt_toolkit/key_binding/bindings/named_commands.py @@ -126,9 +126,7 @@ def forward_word(event: E) -> None: digits. """ buff = event.current_buffer - pos = buff.document.find_next_word_ending(count=event.arg) - - if pos: + if pos := buff.document.find_next_word_ending(count=event.arg): buff.cursor_position += pos @@ -139,9 +137,7 @@ def backward_word(event: E) -> None: of letters and digits. """ buff = event.current_buffer - pos = buff.document.find_previous_word_beginning(count=event.arg) - - if pos: + if pos := buff.document.find_previous_word_beginning(count=event.arg): buff.cursor_position += pos @@ -296,7 +292,7 @@ def uppercase_word(event: E) -> None: """ buff = event.current_buffer - for i in range(event.arg): + for _ in range(event.arg): pos = buff.document.find_next_word_ending() words = buff.document.text_after_cursor[:pos] buff.insert_text(words.upper(), overwrite=True) @@ -309,7 +305,7 @@ def downcase_word(event: E) -> None: """ buff = event.current_buffer - for i in range(event.arg): # XXX: not DRY: see meta_c and meta_u!! + for _ in range(event.arg): pos = buff.document.find_next_word_ending() words = buff.document.text_after_cursor[:pos] buff.insert_text(words.lower(), overwrite=True) @@ -322,7 +318,7 @@ def capitalize_word(event: E) -> None: """ buff = event.current_buffer - for i in range(event.arg): + for _ in range(event.arg): pos = buff.document.find_next_word_ending() words = buff.document.text_after_cursor[:pos] buff.insert_text(words.title(), overwrite=True) @@ -356,11 +352,10 @@ def kill_line(event: E) -> None: deleted = buff.delete_before_cursor( count=-buff.document.get_start_of_line_position() ) + elif buff.document.current_char == "\n": + deleted = buff.delete(1) else: - if buff.document.current_char == "\n": - deleted = buff.delete(1) - else: - deleted = buff.delete(count=buff.document.get_end_of_line_position()) + deleted = buff.delete(count=buff.document.get_end_of_line_position()) event.app.clipboard.set_text(deleted) @@ -371,9 +366,7 @@ def kill_word(event: E) -> None: end of the next word. Word boundaries are the same as forward-word. """ buff = event.current_buffer - pos = buff.document.find_next_word_ending(count=event.arg) - - if pos: + if pos := buff.document.find_next_word_ending(count=event.arg): deleted = buff.delete(count=pos) if event.is_repeat: @@ -490,10 +483,10 @@ def yank_pop(event: E) -> None: """ buff = event.current_buffer doc_before_paste = buff.document_before_paste - clipboard = event.app.clipboard - if doc_before_paste is not None: buff.document = doc_before_paste + clipboard = event.app.clipboard + clipboard.rotate() buff.paste_clipboard_data(clipboard.get_data(), paste_mode=PasteMode.EMACS) @@ -562,10 +555,7 @@ def call_last_kbd_macro(event: E) -> None: the body of the called macro back into the KeyProcessor, so these keys will be added later on to the macro of their handlers have `record_in_macro=True`. """ - # Insert the macro. - macro = event.app.emacs_state.macro - - if macro: + if macro := event.app.emacs_state.macro: event.app.key_processor.feed_multiple(macro, first=True) @@ -576,8 +566,7 @@ def print_last_kbd_macro(event: E) -> None: """ # TODO: Make the format suitable for the inputrc file. def print_macro() -> None: - macro = event.app.emacs_state.macro - if macro: + if macro := event.app.emacs_state.macro: for k in macro: print(k) @@ -617,7 +606,7 @@ def change(line: str) -> str: else: def change(line: str) -> str: - return "#" + line + return f"#{line}" buff.document = Document( text="\n".join(map(change, buff.text.splitlines())), cursor_position=0 diff --git a/src/prompt_toolkit/key_binding/bindings/scroll.py b/src/prompt_toolkit/key_binding/bindings/scroll.py index 4a43ff585a..34b2c3d19a 100644 --- a/src/prompt_toolkit/key_binding/bindings/scroll.py +++ b/src/prompt_toolkit/key_binding/bindings/scroll.py @@ -101,16 +101,14 @@ def scroll_one_line_down(event: E) -> None: w = event.app.layout.current_window b = event.app.current_buffer - if w: - # When the cursor is at the top, move to the next line. (Otherwise, only scroll.) - if w.render_info: - info = w.render_info + if w and w.render_info: + info = w.render_info - if w.vertical_scroll < info.content_height - info.window_height: - if info.cursor_position.y <= info.configured_scroll_offsets.top: - b.cursor_position += b.document.get_cursor_down_position() + if w.vertical_scroll < info.content_height - info.window_height: + if info.cursor_position.y <= info.configured_scroll_offsets.top: + b.cursor_position += b.document.get_cursor_down_position() - w.vertical_scroll += 1 + w.vertical_scroll += 1 def scroll_one_line_up(event: E) -> None: @@ -118,30 +116,27 @@ def scroll_one_line_up(event: E) -> None: scroll_offset -= 1 """ w = event.app.layout.current_window - b = event.app.current_buffer + if w and w.render_info and w.vertical_scroll > 0: + b = event.app.current_buffer - if w: - # When the cursor is at the bottom, move to the previous line. (Otherwise, only scroll.) - if w.render_info: - info = w.render_info + info = w.render_info - if w.vertical_scroll > 0: - first_line_height = info.get_height_for_line(info.first_visible_line()) + first_line_height = info.get_height_for_line(info.first_visible_line()) - cursor_up = info.cursor_position.y - ( - info.window_height - - 1 - - first_line_height - - info.configured_scroll_offsets.bottom - ) + cursor_up = info.cursor_position.y - ( + info.window_height + - 1 + - first_line_height + - info.configured_scroll_offsets.bottom + ) - # Move cursor up, as many steps as the height of the first line. - # TODO: not entirely correct yet, in case of line wrapping and many long lines. - for _ in range(max(0, cursor_up)): - b.cursor_position += b.document.get_cursor_up_position() + # Move cursor up, as many steps as the height of the first line. + # TODO: not entirely correct yet, in case of line wrapping and many long lines. + for _ in range(max(0, cursor_up)): + b.cursor_position += b.document.get_cursor_up_position() - # Scroll window - w.vertical_scroll -= 1 + # Scroll window + w.vertical_scroll -= 1 def scroll_page_down(event: E) -> None: diff --git a/src/prompt_toolkit/key_binding/bindings/vi.py b/src/prompt_toolkit/key_binding/bindings/vi.py index efbb107de0..73f991aad9 100644 --- a/src/prompt_toolkit/key_binding/bindings/vi.py +++ b/src/prompt_toolkit/key_binding/bindings/vi.py @@ -52,7 +52,7 @@ ascii_lowercase = string.ascii_lowercase -vi_register_names = ascii_lowercase + "0123456789" +vi_register_names = f"{ascii_lowercase}0123456789" class TextObjectType(Enum): @@ -646,7 +646,7 @@ def _delete_line(event: E) -> None: # Set new text. if before and after: - before = before + "\n" + before += "\n" # Set text and cursor position. buffer.document = Document( @@ -727,7 +727,7 @@ def _join(event: E) -> None: """ Join lines. """ - for i in range(event.arg): + for _ in range(event.arg): event.current_buffer.join_next_line() @handle("g", "J", filter=vi_navigation_mode & ~is_read_only) @@ -735,7 +735,7 @@ def _join_nospace(event: E) -> None: """ Join lines without space. """ - for i in range(event.arg): + for _ in range(event.arg): event.current_buffer.join_next_line(separator="") @handle("J", filter=vi_selection_mode & ~is_read_only) @@ -781,8 +781,7 @@ def _paste_register(event: E) -> None: """ c = event.key_sequence[1].data if c in vi_register_names: - data = event.app.vi_state.named_registers.get(c) - if data: + if data := event.app.vi_state.named_registers.get(c): event.current_buffer.paste_clipboard_data( data, count=event.arg, paste_mode=PasteMode.VI_AFTER ) @@ -794,8 +793,7 @@ def _paste_register_before(event: E) -> None: """ c = event.key_sequence[1].data if c in vi_register_names: - data = event.app.vi_state.named_registers.get(c) - if data: + if data := event.app.vi_state.named_registers.get(c): event.current_buffer.paste_clipboard_data( data, count=event.arg, paste_mode=PasteMode.VI_BEFORE ) @@ -826,7 +824,7 @@ def _substitute(event: E) -> None: @handle("u", filter=vi_navigation_mode, save_before=(lambda e: False)) def _undo(event: E) -> None: - for i in range(event.arg): + for _ in range(event.arg): event.current_buffer.undo() @handle("V", filter=vi_navigation_mode) @@ -912,16 +910,14 @@ def _delete(event: E) -> None: Delete character. """ buff = event.current_buffer - count = min(event.arg, len(buff.document.current_line_after_cursor)) - if count: + if count := min(event.arg, len(buff.document.current_line_after_cursor)): text = event.current_buffer.delete(count=count) event.app.clipboard.set_text(text) @handle("X", filter=vi_navigation_mode) def _delete_before_cursor(event: E) -> None: buff = event.current_buffer - count = min(event.arg, len(buff.document.current_line_before_cursor)) - if count: + if count := min(event.arg, len(buff.document.current_line_before_cursor)): text = event.current_buffer.delete_before_cursor(count=count) event.app.clipboard.set_text(text) @@ -1359,12 +1355,11 @@ def handler(event: E) -> TextObject: ci_start, ci_end ) - if start is not None and end is not None: - offset = 0 if inner else 1 - return TextObject(start + 1 - offset, end + offset) - else: + if start is None or end is None: # Nothing found. return TextObject(0) + offset = 0 if inner else 1 + return TextObject(start + 1 - offset, end + offset) if key is None: text_object("ai"[inner], ci_start, no_move_handler=True)(handler) @@ -1416,10 +1411,9 @@ def _next_occurence(event: E) -> TextObject: cursor to the next occurrence of character. 'x'. """ event.app.vi_state.last_character_find = CharacterFind(event.data, False) - match = event.current_buffer.document.find( + if match := event.current_buffer.document.find( event.data, in_current_line=True, count=event.arg - ) - if match: + ): return TextObject(match, type=TextObjectType.INCLUSIVE) else: return TextObject(0) @@ -1444,10 +1438,9 @@ def _t(event: E) -> TextObject: Move right to the next occurrence of c, then one char backward. """ event.app.vi_state.last_character_find = CharacterFind(event.data, False) - match = event.current_buffer.document.find( + if match := event.current_buffer.document.find( event.data, in_current_line=True, count=event.arg - ) - if match: + ): return TextObject(match - 1, type=TextObjectType.INCLUSIVE) else: return TextObject(0) @@ -1494,10 +1487,7 @@ def _(event: E) -> TextObject: char, in_current_line=True, count=event.arg ) type = TextObjectType.INCLUSIVE - if pos: - return TextObject(pos, type=type) - else: - return TextObject(0) + return TextObject(pos, type=type) if pos else TextObject(0) repeat(True) repeat(False) @@ -1722,27 +1712,25 @@ def _goto_corresponding_bracket(event: E) -> TextObject: """ buffer = event.current_buffer - if event._arg: - # If 'arg' has been given, the meaning of % is to go to the 'x%' - # row in the file. - if 0 < event.arg <= 100: - absolute_index = buffer.document.translate_row_col_to_index( - int((event.arg * buffer.document.line_count - 1) / 100), 0 - ) - return TextObject( - absolute_index - buffer.document.cursor_position, - type=TextObjectType.LINEWISE, - ) - else: - return TextObject(0) # Do nothing. + if not event._arg: + return ( + TextObject(match, type=TextObjectType.INCLUSIVE) + if (match := buffer.document.find_matching_bracket_position()) + else TextObject(0) + ) + # If 'arg' has been given, the meaning of % is to go to the 'x%' + # row in the file. + if 0 < event.arg <= 100: + absolute_index = buffer.document.translate_row_col_to_index( + int((event.arg * buffer.document.line_count - 1) / 100), 0 + ) + return TextObject( + absolute_index - buffer.document.cursor_position, + type=TextObjectType.LINEWISE, + ) else: - # Move to the corresponding opening/closing bracket (()'s, []'s and {}'s). - match = buffer.document.find_matching_bracket_position() - if match: - return TextObject(match, type=TextObjectType.INCLUSIVE) - else: - return TextObject(0) + return TextObject(0) # Do nothing. @text_object("|") def _to_column(event: E) -> TextObject: @@ -1912,8 +1900,7 @@ def _insert_text_multiple_cursors(event: E) -> None: p = 0 for p2 in buff.multiple_cursor_positions: - text.append(original_text[p:p2]) - text.append(event.data) + text.extend((original_text[p:p2], event.data)) p = p2 text.append(original_text[p:]) diff --git a/src/prompt_toolkit/key_binding/key_bindings.py b/src/prompt_toolkit/key_binding/key_bindings.py index 03bc79ef01..6b5cd48990 100644 --- a/src/prompt_toolkit/key_binding/key_bindings.py +++ b/src/prompt_toolkit/key_binding/key_bindings.py @@ -344,7 +344,7 @@ def remove(self, *args: Union[Keys, str, KeyHandlerCallable]) -> None: found = True else: - assert len(args) > 0 + assert args args = cast(Tuple[Union[Keys, str]], args) # Remove this sequence of key bindings. @@ -383,7 +383,7 @@ def get() -> List[Binding]: any_count = 0 for i, j in zip(b.keys, keys): - if i != j and i != Keys.Any: + if i not in [j, Keys.Any]: match = False break @@ -414,11 +414,7 @@ def get() -> List[Binding]: result = [] for b in self.bindings: if len(keys) < len(b.keys): - match = True - for i, j in zip(b.keys, keys): - if i != j and i != Keys.Any: - match = False - break + match = all(i in [j, Keys.Any] for i, j in zip(b.keys, keys)) if match: result.append(b) return result diff --git a/src/prompt_toolkit/key_binding/key_processor.py b/src/prompt_toolkit/key_binding/key_processor.py index 6fdd519179..692c24d13c 100644 --- a/src/prompt_toolkit/key_binding/key_processor.py +++ b/src/prompt_toolkit/key_binding/key_processor.py @@ -40,11 +40,7 @@ def __init__(self, key: Union[Keys, str], data: Optional[str] = None) -> None: assert isinstance(key, Keys) or len(key) == 1 if data is None: - if isinstance(key, Keys): - data = key.value - else: - data = key # 'key' is a one character string. - + data = key.value if isinstance(key, Keys) else key self.key = key self.data = data @@ -52,9 +48,11 @@ def __repr__(self) -> str: return f"{self.__class__.__name__}(key={self.key!r}, data={self.data!r})" def __eq__(self, other: object) -> bool: - if not isinstance(other, KeyPress): - return False - return self.key == other.key and self.data == other.data + return ( + self.key == other.key and self.data == other.data + if isinstance(other, KeyPress) + else False + ) """ @@ -172,35 +170,30 @@ def _process(self) -> Generator[None, KeyPress, None]: else: is_prefix_of_longer_match = self._is_prefix_of_longer_match(buffer) - # When eager matches were found, give priority to them and also - # ignore all the longer matches. - eager_matches = [m for m in matches if m.eager()] - - if eager_matches: + if eager_matches := [m for m in matches if m.eager()]: matches = eager_matches is_prefix_of_longer_match = False # Exact matches found, call handler. - if not is_prefix_of_longer_match and matches: - self._call_handler(matches[-1], key_sequence=buffer[:]) - del buffer[:] # Keep reference. - - # No match found. - elif not is_prefix_of_longer_match and not matches: - retry = True - found = False - - # Loop over the input, try longest match first and shift. - for i in range(len(buffer), 0, -1): - matches = self._get_matches(buffer[:i]) - if matches: - self._call_handler(matches[-1], key_sequence=buffer[:i]) - del buffer[:i] - found = True - break - - if not found: - del buffer[:1] + if not is_prefix_of_longer_match: + if matches: + self._call_handler(matches[-1], key_sequence=buffer[:]) + del buffer[:] # Keep reference. + + else: + retry = True + found = False + + # Loop over the input, try longest match first and shift. + for i in range(len(buffer), 0, -1): + if matches := self._get_matches(buffer[:i]): + self._call_handler(matches[-1], key_sequence=buffer[:i]) + del buffer[:i] + found = True + break + + if not found: + del buffer[:1] def feed(self, key_press: KeyPress, first: bool = False) -> None: """ @@ -246,13 +239,12 @@ def not_empty() -> bool: return bool(self.input_queue) def get_next() -> KeyPress: - if app.is_done: - # Only process CPR responses. Everything else is typeahead. - cpr = [k for k in self.input_queue if k.key == Keys.CPRResponse][0] - self.input_queue.remove(cpr) - return cpr - else: + if not app.is_done: return self.input_queue.popleft() + # Only process CPR responses. Everything else is typeahead. + cpr = [k for k in self.input_queue if k.key == Keys.CPRResponse][0] + self.input_queue.remove(cpr) + return cpr is_flush = False @@ -372,10 +364,12 @@ def _leave_vi_temp_navigation_mode(self, event: "KeyPressEvent") -> None: """ app = event.app - if app.editing_mode == EditingMode.VI: - # Not waiting for a text object and no argument has been given. - if app.vi_state.operator_func is None and self.arg is None: - app.vi_state.temporary_navigation_mode = False + if ( + app.editing_mode == EditingMode.VI + and app.vi_state.operator_func is None + and self.arg is None + ): + app.vi_state.temporary_navigation_mode = False def _start_timeout(self) -> None: """ @@ -491,7 +485,7 @@ def arg(self) -> int: result = int(self._arg or 1) # Don't exceed a million. - if int(result) >= 1000000: + if result >= 1000000: result = 1 return result diff --git a/src/prompt_toolkit/layout/containers.py b/src/prompt_toolkit/layout/containers.py index 03f9e7d248..4eabe37271 100644 --- a/src/prompt_toolkit/layout/containers.py +++ b/src/prompt_toolkit/layout/containers.py @@ -347,14 +347,17 @@ def get() -> List[Container]: # The children with padding. for child in self.children: - result.append(child) - result.append( - Window( - height=self.padding, - char=self.padding_char, - style=self.padding_style, + result.extend( + ( + child, + Window( + height=self.padding, + char=self.padding_char, + style=self.padding_style, + ), ) ) + if result: result.pop() @@ -382,7 +385,7 @@ def write_to_screen( to which the output has to be written. """ sizes = self._divide_heights(write_position) - style = parent_style + " " + to_str(self.style) + style = f"{parent_style} {to_str(self.style)}" z_index = z_index if self.z_index is None else self.z_index if sizes is None: @@ -574,12 +577,11 @@ def preferred_height(self, width: int, max_available_height: int) -> Dimension: if sizes is None: return Dimension() - else: - dimensions = [ - c.preferred_height(s, max_available_height) - for s, c in zip(sizes, children) - ] - return max_layout_dimensions(dimensions) + dimensions = [ + c.preferred_height(s, max_available_height) + for s, c in zip(sizes, children) + ] + return max_layout_dimensions(dimensions) def reset(self) -> None: for c in self.children: @@ -600,14 +602,17 @@ def get() -> List[Container]: # The children with padding. for child in self.children: - result.append(child) - result.append( - Window( - width=self.padding, - char=self.padding_char, - style=self.padding_style, + result.extend( + ( + child, + Window( + width=self.padding, + char=self.padding_char, + style=self.padding_style, + ), ) ) + if result: result.pop() @@ -690,7 +695,7 @@ def write_to_screen( children = self._all_children sizes = self._divide_widths(write_position.width) - style = parent_style + " " + to_str(self.style) + style = f"{parent_style} {to_str(self.style)}" z_index = z_index if self.z_index is None else self.z_index # If there is not enough space. @@ -804,7 +809,7 @@ def write_to_screen( erase_bg: bool, z_index: Optional[int], ) -> None: - style = parent_style + " " + to_str(self.style) + style = f"{parent_style} {to_str(self.style)}" z_index = z_index if self.z_index is None else self.z_index self.content.write_to_screen( @@ -815,7 +820,7 @@ def write_to_screen( # z_index of a Float is computed by summing the z_index of the # container and the `Float`. new_z_index = (z_index or 0) + fl.z_index - style = parent_style + " " + to_str(self.style) + style = f"{parent_style} {to_str(self.style)}" # If the float that we have here, is positioned relative to the # cursor position, but the Window that specifies the cursor @@ -1094,14 +1099,10 @@ def __init__( self.transparent = to_filter(transparent) def get_width(self) -> Optional[int]: - if callable(self.width): - return self.width() - return self.width + return self.width() if callable(self.width) else self.width def get_height(self) -> Optional[int]: - if callable(self.height): - return self.height() - return self.height + return self.height() if callable(self.height) else self.height def __repr__(self) -> str: return "Float(content=%r)" % self.content @@ -1233,10 +1234,7 @@ def input_line_to_visible_line(self) -> Dict[int, int]: """ result: Dict[int, int] = {} for k, v in self.visible_line_to_input_line.items(): - if v in result: - result[v] = min(result[v], k) - else: - result[v] = k + result[v] = min(result[v], k) if v in result else k return result def first_visible_line(self, after_scroll_offset: bool = False) -> int: @@ -1693,9 +1691,7 @@ def _get_digraph_char(self) -> Optional[str]: if app.quoted_insert: return "^" if app.vi_state.waiting_for_digraph: - if app.vi_state.digraph_symbol1: - return app.vi_state.digraph_symbol1 - return "?" + return app.vi_state.digraph_symbol1 or "?" return None def write_to_screen( @@ -1987,11 +1983,7 @@ def copy_line( multiple lines in the output. It will call the prefix (prompt) function before every line. """ - if is_input: - current_rowcol_to_yx = rowcol_to_yx - else: - current_rowcol_to_yx = {} # Throwaway dictionary. - + current_rowcol_to_yx = rowcol_to_yx if is_input else {} # Draw line prefix. if is_input and get_line_prefix: prompt = to_formatted_text(get_line_prefix(lineno, 0)) @@ -2188,11 +2180,7 @@ def _fill_bg( (Useful for floats and when a `char` has been given.) """ char: Optional[str] - if callable(self.char): - char = self.char() - else: - char = self.char - + char = self.char() if callable(self.char) else self.char if erase_bg or char: wp = write_position char_obj = _CHAR_CACHE[char or " ", ""] @@ -2207,7 +2195,7 @@ def _apply_style( ) -> None: # Apply `self.style`. - style = parent_style + " " + to_str(self.style) + style = f"{parent_style} {to_str(self.style)}" new_screen.fill_area(write_position, style=style, after=False) @@ -2226,8 +2214,7 @@ def _highlight_digraph(self, new_screen: Screen) -> None: When we are in Vi digraph mode, put a question mark underneath the cursor. """ - digraph_char = self._get_digraph_char() - if digraph_char: + if digraph_char := self._get_digraph_char(): cpos = new_screen.get_cursor_position(self) new_screen.data_buffer[cpos.y][cpos.x] = _CHAR_CACHE[ digraph_char, "class:digraph" @@ -2262,14 +2249,12 @@ def _highlight_cursorlines( """ Highlight cursor row/column. """ - cursor_line_style = " class:cursor-line " - cursor_column_style = " class:cursor-column " - data_buffer = new_screen.data_buffer # Highlight cursor line. if self.cursorline(): row = data_buffer[cpos.y] + cursor_line_style = " class:cursor-line " for x in range(x, x + width): original_char = row[x] row[x] = _CHAR_CACHE[ @@ -2278,6 +2263,8 @@ def _highlight_cursorlines( # Highlight cursor column. if self.cursorcolumn(): + cursor_column_style = " class:cursor-column " + for y2 in range(y, y + height): row = data_buffer[y2] original_char = row[cpos.x] @@ -2295,7 +2282,7 @@ def _highlight_cursorlines( column = cc.position if column < x + width: # Only draw when visible. - color_column_style = " " + cc.style + color_column_style = f" {cc.style}" for y2 in range(y, y + height): row = data_buffer[y2] @@ -2497,9 +2484,7 @@ def do_scroll( ) # Prevent negative scroll offsets. - if current_scroll < 0: - current_scroll = 0 - + current_scroll = max(current_scroll, 0) # Scroll back if we scrolled to much and there's still space to show more of the document. if ( not self.allow_scroll_beyond_bottom() diff --git a/src/prompt_toolkit/layout/controls.py b/src/prompt_toolkit/layout/controls.py index 016d289466..0bdd1ec56c 100644 --- a/src/prompt_toolkit/layout/controls.py +++ b/src/prompt_toolkit/layout/controls.py @@ -368,15 +368,14 @@ def preferred_height( Return the preferred height for this control. """ content = self.create_content(width, None) - if wrap_lines: - height = 0 - for i in range(content.line_count): - height += content.get_height_for_line(i, width, get_line_prefix) - if height >= max_available_height: - return max_available_height - return height - else: + if not wrap_lines: return content.line_count + height = 0 + for i in range(content.line_count): + height += content.get_height_for_line(i, width, get_line_prefix) + if height >= max_available_height: + return max_available_height + return height def create_content(self, width: int, height: Optional[int]) -> UIContent: # Get fragments @@ -598,8 +597,7 @@ def search_state(self) -> SearchState: for searching multiple `BufferControls`, then they share the same `SearchState`. """ - search_buffer_control = self.search_buffer_control - if search_buffer_control: + if search_buffer_control := self.search_buffer_control: return search_buffer_control.searcher_search_state else: return SearchState() @@ -885,18 +883,16 @@ def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone": # Don't handle scroll events here. return NotImplemented - # Not focused, but focusing on click events. - else: - if ( + elif ( self.focus_on_click() and mouse_event.event_type == MouseEventType.MOUSE_UP ): - # Focus happens on mouseup. (If we did this on mousedown, the - # up event will be received at the point where this widget is - # focused and be handled anyway.) - get_app().layout.current_control = self - else: - return NotImplemented + # Focus happens on mouseup. (If we did this on mousedown, the + # up event will be received at the point where this widget is + # focused and be handled anyway.) + get_app().layout.current_control = self + else: + return NotImplemented return None diff --git a/src/prompt_toolkit/layout/dimension.py b/src/prompt_toolkit/layout/dimension.py index 04c21637cb..4a7da26e31 100644 --- a/src/prompt_toolkit/layout/dimension.py +++ b/src/prompt_toolkit/layout/dimension.py @@ -111,7 +111,7 @@ def __repr__(self) -> str: if self.weight_specified: fields.append("weight=%r" % self.weight) - return "Dimension(%s)" % ", ".join(fields) + return f'Dimension({", ".join(fields)})' def sum_layout_dimensions(dimensions: List[Dimension]) -> Dimension: @@ -139,10 +139,7 @@ def max_layout_dimensions(dimensions: List[Dimension]) -> Dimension: if all(d.is_zero() for d in dimensions): return dimensions[0] - # Ignore empty dimensions. (They should not reduce the size of others.) - dimensions = [d for d in dimensions if not d.is_zero()] - - if dimensions: + if dimensions := [d for d in dimensions if not d.is_zero()]: # Take the highest minimum dimension. min_ = max(d.min for d in dimensions) diff --git a/src/prompt_toolkit/layout/layout.py b/src/prompt_toolkit/layout/layout.py index 62a3184ee2..c51b52ee97 100644 --- a/src/prompt_toolkit/layout/layout.py +++ b/src/prompt_toolkit/layout/layout.py @@ -105,7 +105,6 @@ def focus(self, value: FocusableElement) -> None: return raise ValueError(f"Couldn't find Buffer in the current layout: {value!r}.") - # BufferControl by buffer object. elif isinstance(value, Buffer): for control in self.find_all_controls(): if isinstance(control, BufferControl) and control.buffer == value: @@ -113,7 +112,6 @@ def focus(self, value: FocusableElement) -> None: return raise ValueError(f"Couldn't find Buffer in the current layout: {value!r}.") - # Focus UIControl. elif isinstance(value, UIControl): if value not in self.find_all_controls(): raise ValueError( @@ -124,7 +122,6 @@ def focus(self, value: FocusableElement) -> None: self.current_control = value - # Otherwise, expecting any Container object. else: value = to_container(value) @@ -143,10 +140,11 @@ def focus(self, value: FocusableElement) -> None: # of them have been focused before, take the last focused # item. (This is very useful when the UI is composed of more # complex sub components.) - windows = [] - for c in walk(value, skip_hidden=True): - if isinstance(c, Window) and c.content.is_focusable(): - windows.append(c) + windows = [ + c + for c in walk(value, skip_hidden=True) + if isinstance(c, Window) and c.content.is_focusable() + ] # Take the first one that was focused before. for w in reversed(self._stack): @@ -176,17 +174,12 @@ def has_focus(self, value: FocusableElement) -> bool: return self.current_buffer == value if isinstance(value, UIControl): return self.current_control == value - else: - value = to_container(value) - if isinstance(value, Window): - return self.current_window == value - else: - # Check whether this "container" is focused. This is true if - # one of the elements inside is focused. - for element in walk(value): - if element == self.current_window: - return True - return False + value = to_container(value) + return ( + self.current_window == value + if isinstance(value, Window) + else any(element == self.current_window for element in walk(value)) + ) @property def current_control(self) -> UIControl: @@ -269,11 +262,16 @@ def get_buffer_by_name(self, buffer_name: str) -> Optional[Buffer]: Look in the layout for a buffer with the given name. Return `None` when nothing was found. """ - for w in self.walk(): - if isinstance(w, Window) and isinstance(w.content, BufferControl): - if w.content.buffer.name == buffer_name: - return w.content.buffer - return None + return next( + ( + w.content.buffer + for w in self.walk() + if isinstance(w, Window) + and isinstance(w.content, BufferControl) + and w.content.buffer.name == buffer_name + ), + None, + ) @property def buffer_has_focus(self) -> bool: diff --git a/src/prompt_toolkit/layout/margins.py b/src/prompt_toolkit/layout/margins.py index 7c46819c24..c8094db0fc 100644 --- a/src/prompt_toolkit/layout/margins.py +++ b/src/prompt_toolkit/layout/margins.py @@ -82,7 +82,7 @@ def __init__( def get_width(self, get_ui_content: Callable[[], UIContent]) -> int: line_count = get_ui_content().line_count - return max(3, len("%s" % line_count) + 1) + return max(3, len(f"{line_count}") + 1) def create_margin( self, window_render_info: "WindowRenderInfo", width: int, height: int @@ -142,10 +142,7 @@ def __init__(self, margin: Margin, filter: FilterOrBool) -> None: self.filter = to_filter(filter) def get_width(self, get_ui_content: Callable[[], UIContent]) -> int: - if self.filter(): - return self.margin.get_width(get_ui_content) - else: - return 0 + return self.margin.get_width(get_ui_content) if self.filter() else 0 def create_margin( self, window_render_info: "WindowRenderInfo", width: int, height: int diff --git a/src/prompt_toolkit/layout/menus.py b/src/prompt_toolkit/layout/menus.py index 24d6e46af0..43f6e1d3e5 100644 --- a/src/prompt_toolkit/layout/menus.py +++ b/src/prompt_toolkit/layout/menus.py @@ -74,8 +74,7 @@ def has_focus(self) -> bool: return False def preferred_width(self, max_available_width: int) -> Optional[int]: - complete_state = get_app().current_buffer.complete_state - if complete_state: + if complete_state := get_app().current_buffer.complete_state: menu_width = self._get_menu_width(500, complete_state) menu_meta_width = self._get_menu_meta_width(500, complete_state) @@ -91,8 +90,7 @@ def preferred_height( get_line_prefix: Optional[GetLinePrefixCallable], ) -> Optional[int]: - complete_state = get_app().current_buffer.complete_state - if complete_state: + if complete_state := get_app().current_buffer.complete_state: return len(complete_state.completions) else: return 0 @@ -219,12 +217,10 @@ def _get_menu_item_fragments( width. """ if is_current_completion: - style_str = "class:completion-menu.completion.current {} {}".format( - completion.style, - completion.selected_style, - ) + style_str = f"class:completion-menu.completion.current {completion.style} {completion.selected_style}" + else: - style_str = "class:completion-menu.completion " + completion.style + style_str = f"class:completion-menu.completion {completion.style}" text, tw = _trim_formatted_text( completion.display, (width - 2 if space_after else width - 1) @@ -247,25 +243,23 @@ def _trim_formatted_text( """ width = fragment_list_width(formatted_text) - # When the text is too wide, trim it. - if width > max_width: - result = [] # Text fragments. - remaining_width = max_width - 3 + if width <= max_width: + return formatted_text, width + result = [] # Text fragments. + remaining_width = max_width - 3 - for style_and_ch in explode_text_fragments(formatted_text): - ch_width = get_cwidth(style_and_ch[1]) + for style_and_ch in explode_text_fragments(formatted_text): + ch_width = get_cwidth(style_and_ch[1]) - if ch_width <= remaining_width: - result.append(style_and_ch) - remaining_width -= ch_width - else: - break + if ch_width <= remaining_width: + result.append(style_and_ch) + remaining_width -= ch_width + else: + break - result.append(("", "...")) + result.append(("", "...")) - return result, max_width - remaining_width - else: - return formatted_text, width + return result, max_width - remaining_width class CompletionsMenu(ConditionalContainer): @@ -705,7 +699,6 @@ def get_line(i: int) -> StyleAndTextTuples: return UIContent(get_line=get_line, line_count=1 if fragments else 0) def _get_text_fragments(self) -> StyleAndTextTuples: - style = "class:completion-menu.multi-column-meta" state = get_app().current_buffer.complete_state if ( @@ -713,6 +706,7 @@ def _get_text_fragments(self) -> StyleAndTextTuples: and state.current_completion and state.current_completion.display_meta_text ): + style = "class:completion-menu.multi-column-meta" return to_formatted_text( cast(StyleAndTextTuples, [("", " ")]) + state.current_completion.display_meta diff --git a/src/prompt_toolkit/layout/processors.py b/src/prompt_toolkit/layout/processors.py index 722658a846..b5ee8cb532 100644 --- a/src/prompt_toolkit/layout/processors.py +++ b/src/prompt_toolkit/layout/processors.py @@ -285,29 +285,25 @@ def apply_transformation( _, ) = transformation_input.unpack() - selected_fragment = " class:selected " - - # In case of selection, highlight all matches. - selection_at_line = document.selection_range_at_line(lineno) - - if selection_at_line: + if selection_at_line := document.selection_range_at_line(lineno): from_, to = selection_at_line from_ = source_to_display(from_) to = source_to_display(to) fragments = explode_text_fragments(fragments) + selected_fragment = " class:selected " + if from_ == 0 and to == 0 and len(fragments) == 0: # When this is an empty line, insert a space in order to # visualise the selection. return Transformation([(selected_fragment, " ")]) - else: - for i in range(from_, to): - if i < len(fragments): - old_fragment, old_text, *_ = fragments[i] - fragments[i] = (old_fragment + selected_fragment, old_text) - elif i == len(fragments): - fragments.append((selected_fragment, " ")) + for i in range(from_, to): + if i < len(fragments): + old_fragment, old_text, *_ = fragments[i] + fragments[i] = (old_fragment + selected_fragment, old_text) + elif i == len(fragments): + fragments.append((selected_fragment, " ")) return Transformation(fragments) @@ -417,12 +413,9 @@ def apply_transformation( # Get the highlight positions. key = (get_app().render_counter, document.text, document.cursor_position) - positions = self._positions_cache.get( + if positions := self._positions_cache.get( key, lambda: self._get_positions_to_highlight(document) - ) - - # Apply if positions were found at this line. - if positions: + ): for row, col in positions: if row == lineno: col = source_to_display(col) @@ -484,9 +477,7 @@ def apply_transformation( style += fragment_suffix fragments[column] = (style, text) - return Transformation(fragments) - else: - return Transformation(fragments) + return Transformation(fragments) class BeforeInput(Processor): @@ -544,14 +535,13 @@ def _get_text_fragments(self) -> StyleAndTextTuples: app = get_app() if app.key_processor.arg is None: return [] - else: - arg = app.key_processor.arg + arg = app.key_processor.arg - return [ - ("class:prompt.arg", "(arg: "), - ("class:prompt.arg.text", str(arg)), - ("class:prompt.arg", ") "), - ] + return [ + ("class:prompt.arg", "(arg: "), + ("class:prompt.arg.text", str(arg)), + ("class:prompt.arg", ") "), + ] def __repr__(self) -> str: return "ShowArg()" @@ -571,13 +561,11 @@ def __init__(self, text: AnyFormattedText, style: str = "") -> None: self.style = style def apply_transformation(self, ti: TransformationInput) -> Transformation: - # Insert fragments after the last line. - if ti.lineno == ti.document.line_count - 1: - # Get fragments. - fragments_after = to_formatted_text(self.text, self.style) - return Transformation(fragments=ti.fragments + fragments_after) - else: + if ti.lineno != ti.document.line_count - 1: return Transformation(fragments=ti.fragments) + # Get fragments. + fragments_after = to_formatted_text(self.text, self.style) + return Transformation(fragments=ti.fragments + fragments_after) def __repr__(self) -> str: return f"{self.__class__.__name__}({self.text!r}, style={self.style!r})" @@ -593,18 +581,17 @@ def __init__(self, style: str = "class:auto-suggestion") -> None: self.style = style def apply_transformation(self, ti: TransformationInput) -> Transformation: - # Insert fragments after the last line. - if ti.lineno == ti.document.line_count - 1: - buffer = ti.buffer_control.buffer + if ti.lineno != ti.document.line_count - 1: + return Transformation(fragments=ti.fragments) + buffer = ti.buffer_control.buffer - if buffer.suggestion and ti.document.is_cursor_at_the_end: - suggestion = buffer.suggestion.text - else: - suggestion = "" + suggestion = ( + buffer.suggestion.text + if buffer.suggestion and ti.document.is_cursor_at_the_end + else "" + ) - return Transformation(fragments=ti.fragments + [(self.style, suggestion)]) - else: - return Transformation(fragments=ti.fragments) + return Transformation(fragments=ti.fragments + [(self.style, suggestion)]) class ShowLeadingWhiteSpaceProcessor(Processor): @@ -816,16 +803,12 @@ def filter_processor(item: Processor) -> Optional[Processor]: [p for p in accepted_processors if p is not None] ) - # For a `ConditionalProcessor`, check the body. elif isinstance(item, ConditionalProcessor): - p = filter_processor(item.processor) - if p: + if p := filter_processor(item.processor): return ConditionalProcessor(p, item.filter) - # Otherwise, check the processor itself. - else: - if not isinstance(item, excluded_processors): - return item + elif not isinstance(item, excluded_processors): + return item return None @@ -966,7 +949,7 @@ def merge_processors(processors: List[Processor]) -> Processor: """ Merge multiple `Processor` objects into one. """ - if len(processors) == 0: + if not processors: return DummyProcessor() if len(processors) == 1: diff --git a/src/prompt_toolkit/layout/screen.py b/src/prompt_toolkit/layout/screen.py index 8874fba6f8..7f300fea46 100644 --- a/src/prompt_toolkit/layout/screen.py +++ b/src/prompt_toolkit/layout/screen.py @@ -106,11 +106,7 @@ class Char: def __init__(self, char: str = " ", style: str = "") -> None: # If this character has to be displayed otherwise, take that one. if char in self.display_mappings: - if char == "\xa0": - style += " class:nbsp " # Will be underlined. - else: - style += " class:control-character " - + style += " class:nbsp " if char == "\xa0" else " class:control-character " char = self.display_mappings[char] self.char = char @@ -268,7 +264,7 @@ def append_style_to_content(self, style_str: str) -> None: b = self.data_buffer char_cache = _CHAR_CACHE - append_style = " " + style_str + append_style = f" {style_str}" for y, row in b.items(): for x, char in row.items(): @@ -290,11 +286,11 @@ def fill_area( data_buffer = self.data_buffer if after: - append_style = " " + style + append_style = f" {style}" prepend_style = "" else: append_style = "" - prepend_style = style + " " + prepend_style = f"{style} " for y in range( write_position.ypos, write_position.ypos + write_position.height diff --git a/src/prompt_toolkit/layout/scrollable_pane.py b/src/prompt_toolkit/layout/scrollable_pane.py index a5500d7f7c..a232e6d41f 100644 --- a/src/prompt_toolkit/layout/scrollable_pane.py +++ b/src/prompt_toolkit/layout/scrollable_pane.py @@ -375,16 +375,14 @@ def _make_window_visible( min_scroll = 0 max_scroll = virtual_height - visible_height - if self.keep_cursor_visible(): - # Reduce min/max scroll according to the cursor in the focused window. - if cursor_position is not None: - offsets = self.scroll_offsets - cpos_min_scroll = ( - cursor_position.y - visible_height + 1 + offsets.bottom - ) - cpos_max_scroll = cursor_position.y - offsets.top - min_scroll = max(min_scroll, cpos_min_scroll) - max_scroll = max(0, min(max_scroll, cpos_max_scroll)) + if self.keep_cursor_visible() and cursor_position is not None: + offsets = self.scroll_offsets + cpos_min_scroll = ( + cursor_position.y - visible_height + 1 + offsets.bottom + ) + cpos_max_scroll = cursor_position.y - offsets.top + min_scroll = max(min_scroll, cpos_min_scroll) + max_scroll = max(0, min(max_scroll, cpos_max_scroll)) if self.keep_focused_window_visible(): # Reduce min/max scroll according to focused window position. diff --git a/src/prompt_toolkit/layout/utils.py b/src/prompt_toolkit/layout/utils.py index 2e0f34388b..cfa24710d4 100644 --- a/src/prompt_toolkit/layout/utils.py +++ b/src/prompt_toolkit/layout/utils.py @@ -74,7 +74,5 @@ def explode_text_fragments(fragments: Iterable[_T]) -> _ExplodedList[_T]: result: List[_T] = [] for style, string, *rest in fragments: # type: ignore - for c in string: # type: ignore - result.append((style, c, *rest)) # type: ignore - + result.extend((style, c, *rest) for c in string) return _ExplodedList(result) diff --git a/src/prompt_toolkit/lexers/pygments.py b/src/prompt_toolkit/lexers/pygments.py index d50f8afde4..799e45d119 100644 --- a/src/prompt_toolkit/lexers/pygments.py +++ b/src/prompt_toolkit/lexers/pygments.py @@ -97,17 +97,13 @@ def get_sync_start_position( # Scan upwards, until we find a point where we can start the syntax # synchronisation. for i in range(lineno, max(-1, lineno - self.MAX_BACKWARDS), -1): - match = pattern.match(lines[i]) - if match: + if match := pattern.match(lines[i]): return i, match.start() # No synchronisation point found. If we aren't that far from the # beginning, start at the very beginning, otherwise, just try to start # at the current line. - if lineno < self.FROM_START_IF_NO_SYNC_POS_FOUND: - return 0, 0 - else: - return lineno, 0 + return (0, 0) if lineno < self.FROM_START_IF_NO_SYNC_POS_FOUND else (lineno, 0) @classmethod def from_pygments_lexer_cls(cls, lexer_cls: "PygmentsLexerCls") -> "RegexSync": @@ -138,7 +134,7 @@ class _TokenCache(Dict[Tuple[str, ...], str]): """ def __missing__(self, key: Tuple[str, ...]) -> str: - result = "class:" + pygments_token_to_classname(key) + result = f"class:{pygments_token_to_classname(key)}" self[key] = result return result @@ -237,17 +233,18 @@ def lex_document(self, document: Document) -> Callable[[int], StyleAndTextTuples def get_syntax_sync() -> SyntaxSync: "The Syntax synchronisation object that we currently use." - if self.sync_from_start(): - return SyncFromStart() - else: - return self.syntax_sync + return SyncFromStart() if self.sync_from_start() else self.syntax_sync def find_closest_generator(i: int) -> Optional[LineGenerator]: "Return a generator close to line 'i', or None if none was found." - for generator, lineno in line_generators.items(): - if lineno < i and i - lineno < self.REUSE_GENERATOR_MAX_DISTANCE: - return generator - return None + return next( + ( + generator + for generator, lineno in line_generators.items() + if lineno < i and i - lineno < self.REUSE_GENERATOR_MAX_DISTANCE + ), + None, + ) def create_line_generator(start_lineno: int, column: int = 0) -> LineGenerator: """ diff --git a/src/prompt_toolkit/output/conemu.py b/src/prompt_toolkit/output/conemu.py index fc46cc4afd..e6ddfd4b43 100644 --- a/src/prompt_toolkit/output/conemu.py +++ b/src/prompt_toolkit/output/conemu.py @@ -45,7 +45,7 @@ def responds_to_cpr(self) -> bool: return False # We don't need this on Windows. def __getattr__(self, name: str) -> Any: - if name in ( + if name in { "get_size", "get_rows_below_cursor_position", "enable_mouse_support", @@ -54,7 +54,7 @@ def __getattr__(self, name: str) -> Any: "get_win32_screen_buffer_info", "enable_bracketed_paste", "disable_bracketed_paste", - ): + }: return getattr(self.win32_output, name) else: return getattr(self.vt100_output, name) diff --git a/src/prompt_toolkit/output/flush_stdout.py b/src/prompt_toolkit/output/flush_stdout.py index 805a81e010..fd2a838f58 100644 --- a/src/prompt_toolkit/output/flush_stdout.py +++ b/src/prompt_toolkit/output/flush_stdout.py @@ -40,15 +40,7 @@ def flush_stdout(stdout: TextIO, data: str) -> None: # resize signal. (Just ignore. The resize handler will render # again anyway.) pass - elif e.args and e.args[0] == 0: - # This can happen when there is a lot of output and the user - # sends a KeyboardInterrupt by pressing Control-C. E.g. in - # a Python REPL when we execute "while True: print('test')". - # (The `ptpython` REPL uses this `Output` class instead of - # `stdout` directly -- in order to be network transparent.) - # So, just ignore. - pass - else: + elif not e.args or e.args[0] != 0: raise diff --git a/src/prompt_toolkit/output/vt100.py b/src/prompt_toolkit/output/vt100.py index 3a03de6fa5..9ca5e7b2ee 100644 --- a/src/prompt_toolkit/output/vt100.py +++ b/src/prompt_toolkit/output/vt100.py @@ -184,11 +184,7 @@ def _get( match = _get_closest_ansi_color(r, g, b, exclude=exclude) # Turn color name into code. - if self.bg: - code = BG_ANSI_COLORS[match] - else: - code = FG_ANSI_COLORS[match] - + code = BG_ANSI_COLORS[match] if self.bg else FG_ANSI_COLORS[match] return code, match @@ -199,25 +195,25 @@ class _256ColorCache(Dict[Tuple[int, int, int], int]): def __init__(self) -> None: # Build color table. - colors: List[Tuple[int, int, int]] = [] - - # colors 0..15: 16 basic colors - colors.append((0x00, 0x00, 0x00)) # 0 - colors.append((0xCD, 0x00, 0x00)) # 1 - colors.append((0x00, 0xCD, 0x00)) # 2 - colors.append((0xCD, 0xCD, 0x00)) # 3 - colors.append((0x00, 0x00, 0xEE)) # 4 - colors.append((0xCD, 0x00, 0xCD)) # 5 - colors.append((0x00, 0xCD, 0xCD)) # 6 - colors.append((0xE5, 0xE5, 0xE5)) # 7 - colors.append((0x7F, 0x7F, 0x7F)) # 8 - colors.append((0xFF, 0x00, 0x00)) # 9 - colors.append((0x00, 0xFF, 0x00)) # 10 - colors.append((0xFF, 0xFF, 0x00)) # 11 - colors.append((0x5C, 0x5C, 0xFF)) # 12 - colors.append((0xFF, 0x00, 0xFF)) # 13 - colors.append((0x00, 0xFF, 0xFF)) # 14 - colors.append((0xFF, 0xFF, 0xFF)) # 15 + colors: List[Tuple[int, int, int]] = [ + (0x00, 0x00, 0x00), + (0xCD, 0x00, 0x00), + (0x00, 0xCD, 0x00), + (0xCD, 0xCD, 0x00), + (0x00, 0x00, 0xEE), + (0xCD, 0x00, 0xCD), + (0x00, 0xCD, 0xCD), + (0xE5, 0xE5, 0xE5), + (0x7F, 0x7F, 0x7F), + (0xFF, 0x00, 0x00), + (0x00, 0xFF, 0x00), + (0xFF, 0xFF, 0x00), + (0x5C, 0x5C, 0xFF), + (0xFF, 0x00, 0xFF), + (0x00, 0xFF, 0xFF), + (0xFF, 0xFF, 0xFF), + ] + # colors 16..232: the 6x6x6 color cube valuerange = (0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF) @@ -306,11 +302,7 @@ def __missing__(self, attrs: Attrs) -> str: if strike: parts.append("9") - if parts: - result = "\x1b[0;" + ";".join(parts) + "m" - else: - result = "\x1b[0m" - + result = "\x1b[0;" + ";".join(parts) + "m" if parts else "\x1b[0m" self[attrs] = result return result @@ -343,11 +335,9 @@ def get(color: str, bg: bool) -> List[int]: if not color or self.color_depth == ColorDepth.DEPTH_1_BIT: return [] - # 16 ANSI colors. (Given by name.) elif color in table: return [table[color]] - # RGB colors. (Defined as 'ffffff'.) else: try: rgb = self._color_name_to_rgb(color) @@ -357,23 +347,16 @@ def get(color: str, bg: bool) -> List[int]: # When only 16 colors are supported, use that. if self.color_depth == ColorDepth.DEPTH_4_BIT: if bg: # Background. - if fg_color != bg_color: - exclude = [fg_ansi] - else: - exclude = [] + exclude = [fg_ansi] if fg_color != bg_color else [] code, name = _16_bg_colors.get_code(rgb, exclude=exclude) - return [code] else: # Foreground. code, name = _16_fg_colors.get_code(rgb) fg_ansi = name - return [code] - - # True colors. (Only when this feature is enabled.) + return [code] elif self.color_depth == ColorDepth.DEPTH_24_BIT: r, g, b = rgb return [(48 if bg else 38), 2, r, g, b] - # 256 RGB colors. else: return [(48 if bg else 38), 5, _256_colors[rgb]] diff --git a/src/prompt_toolkit/output/win32.py b/src/prompt_toolkit/output/win32.py index 1724eae5da..3f398bef77 100644 --- a/src/prompt_toolkit/output/win32.py +++ b/src/prompt_toolkit/output/win32.py @@ -205,14 +205,9 @@ def get_win32_screen_buffer_info(self) -> CONSOLE_SCREEN_BUFFER_INFO: self.flush() sbinfo = CONSOLE_SCREEN_BUFFER_INFO() - success = windll.kernel32.GetConsoleScreenBufferInfo( + if success := windll.kernel32.GetConsoleScreenBufferInfo( self.hconsole, byref(sbinfo) - ) - - # success = self._winapi(windll.kernel32.GetConsoleScreenBufferInfo, - # self.hconsole, byref(sbinfo)) - - if success: + ): return sbinfo else: raise NoConsoleScreenBufferError @@ -301,7 +296,7 @@ def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: if color_depth != ColorDepth.DEPTH_1_BIT: # Override the last four bits: foreground color. if fgcolor: - win_attrs = win_attrs & ~0xF + win_attrs &= ~0xF win_attrs |= self.color_lookup_table.lookup_fg_color(fgcolor) # Override the next four bits: background color. @@ -649,7 +644,7 @@ def _color_indexes(self, color: str) -> Tuple[int, int]: indexes = self.best_match.get(color, None) if indexes is None: try: - rgb = int(str(color), 16) + rgb = int(color, 16) except ValueError: rgb = 0 diff --git a/src/prompt_toolkit/output/windows10.py b/src/prompt_toolkit/output/windows10.py index d5d55f18ca..b7fc01812e 100644 --- a/src/prompt_toolkit/output/windows10.py +++ b/src/prompt_toolkit/output/windows10.py @@ -63,7 +63,7 @@ def responds_to_cpr(self) -> bool: return False # We don't need this on Windows. def __getattr__(self, name: str) -> Any: - if name in ( + if name in { "get_size", "get_rows_below_cursor_position", "enable_mouse_support", @@ -73,7 +73,7 @@ def __getattr__(self, name: str) -> Any: "enable_bracketed_paste", "disable_bracketed_paste", "get_default_color_depth", - ): + }: return getattr(self.win32_output, name) else: return getattr(self.vt100_output, name) diff --git a/src/prompt_toolkit/patch_stdout.py b/src/prompt_toolkit/patch_stdout.py index 0abbcdb847..aca063b777 100644 --- a/src/prompt_toolkit/patch_stdout.py +++ b/src/prompt_toolkit/patch_stdout.py @@ -157,9 +157,7 @@ def _write_thread(self) -> None: if not item: continue - text = [] - text.append(item) - + text = [item] # Read the rest of the queue if more data was queued up. while True: try: diff --git a/src/prompt_toolkit/renderer.py b/src/prompt_toolkit/renderer.py index 463555c9dd..58c0fd976c 100644 --- a/src/prompt_toolkit/renderer.py +++ b/src/prompt_toolkit/renderer.py @@ -110,7 +110,7 @@ def move_cursor(new: Point) -> Point: if current_x >= width - 1: write("\r") _output_cursor_forward(new.x) - elif new.x < current_x or current_x >= width - 1: + elif new.x < current_x: _output_cursor_backward(current_x - new.x) elif new.x > current_x: _output_cursor_forward(new.x - current_x) @@ -360,10 +360,11 @@ def __init__( # Future set when we are waiting for a CPR flag. self._waiting_for_cpr_futures: Deque[Future[None]] = deque() - self.cpr_support = CPR_Support.UNKNOWN - - if not output.responds_to_cpr: - self.cpr_support = CPR_Support.NOT_SUPPORTED + self.cpr_support = ( + CPR_Support.UNKNOWN + if output.responds_to_cpr + else CPR_Support.NOT_SUPPORTED + ) # Cache for the style. self._attrs_for_style: Optional[_StyleStringToAttrsCache] = None diff --git a/src/prompt_toolkit/search.py b/src/prompt_toolkit/search.py index 413cc6ad9c..f027264b5d 100644 --- a/src/prompt_toolkit/search.py +++ b/src/prompt_toolkit/search.py @@ -103,10 +103,7 @@ def start_search( return buffer_control = layout.current_control - # Only if this control is searchable. - search_buffer_control = buffer_control.search_buffer_control - - if search_buffer_control: + if search_buffer_control := buffer_control.search_buffer_control: buffer_control.search_state.direction = direction # Make sure to focus the search BufferControl diff --git a/src/prompt_toolkit/shortcuts/dialogs.py b/src/prompt_toolkit/shortcuts/dialogs.py index eacb05a00c..c0c58d4d2d 100644 --- a/src/prompt_toolkit/shortcuts/dialogs.py +++ b/src/prompt_toolkit/shortcuts/dialogs.py @@ -284,7 +284,7 @@ def progress_dialog( app = _create_app(dialog, style) def set_percentage(value: int) -> None: - progressbar.percentage = int(value) + progressbar.percentage = value app.invalidate() def log_text(text: str) -> None: diff --git a/src/prompt_toolkit/shortcuts/progress_bar/base.py b/src/prompt_toolkit/shortcuts/progress_bar/base.py index c22507e25c..4f76b137bd 100644 --- a/src/prompt_toolkit/shortcuts/progress_bar/base.py +++ b/src/prompt_toolkit/shortcuts/progress_bar/base.py @@ -336,20 +336,19 @@ def __init__( self.total = total def __iter__(self) -> Iterator[_CounterItem]: - if self.data is not None: - try: - for item in self.data: - yield item - self.item_completed() - - # Only done if we iterate to the very end. - self.done = True - finally: - # Ensure counter has stopped even if we did not iterate to the - # end (e.g. break or exceptions). - self.stopped = True - else: + if self.data is None: raise NotImplementedError("No data defined to iterate over.") + try: + for item in self.data: + yield item + self.item_completed() + + # Only done if we iterate to the very end. + self.done = True + finally: + # Ensure counter has stopped even if we did not iterate to the + # end (e.g. break or exceptions). + self.stopped = True def item_completed(self) -> None: """ diff --git a/src/prompt_toolkit/shortcuts/progress_bar/formatters.py b/src/prompt_toolkit/shortcuts/progress_bar/formatters.py index 1383d7a6b5..5af006450b 100644 --- a/src/prompt_toolkit/shortcuts/progress_bar/formatters.py +++ b/src/prompt_toolkit/shortcuts/progress_bar/formatters.py @@ -116,8 +116,9 @@ def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: if self.width: return self.width - all_labels = [self._add_suffix(c.label) for c in progress_bar.counters] - if all_labels: + if all_labels := [ + self._add_suffix(c.label) for c in progress_bar.counters + ]: max_widths = max(fragment_list_width(l) for l in all_labels) return D(preferred=max_widths, max=max_widths) else: @@ -181,12 +182,7 @@ def format( sym_a, sym_b, sym_c = self.sym_a, self.sym_b, self.sym_c # Compute pb_a based on done, total, or stopped states. - if progress.done: - # 100% completed irrelevant of how much was actually marked as completed. - percent = 1.0 - else: - # Show percentage completed. - percent = progress.percentage / 100 + percent = 1.0 if progress.done else progress.percentage / 100 else: # Total is unknown and bar is still running. sym_a, sym_b, sym_c = self.sym_c, self.unknown, self.sym_c @@ -265,10 +261,9 @@ def format( ) def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: - all_values = [ + if all_values := [ len(_format_timedelta(c.time_elapsed)) for c in progress_bar.counters - ] - if all_values: + ]: return max(all_values) return 0 @@ -326,11 +321,10 @@ def format( return HTML(self.template.format(iterations_per_second=value)) def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: - all_values = [ + if all_values := [ len(f"{c.items_completed / c.time_elapsed.total_seconds():.2f}") for c in progress_bar.counters - ] - if all_values: + ]: return max(all_values) return 0 @@ -408,8 +402,9 @@ def format( for i, (style, text, *_) in enumerate(result): result2.append( - (style + " " + self.colors[(i + shift) % len(self.colors)], text) + (f"{style} {self.colors[(i + shift) % len(self.colors)]}", text) ) + return result2 def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: diff --git a/src/prompt_toolkit/shortcuts/prompt.py b/src/prompt_toolkit/shortcuts/prompt.py index 4dc1b18d1c..6f6be3bddc 100644 --- a/src/prompt_toolkit/shortcuts/prompt.py +++ b/src/prompt_toolkit/shortcuts/prompt.py @@ -165,10 +165,7 @@ def _split_multiline_prompt( """ def has_before_fragments() -> bool: - for fragment, char, *_ in get_prompt_text(): - if "\n" in char: - return True - return False + return any("\n" in char for fragment, char, *_ in get_prompt_text()) def before() -> StyleAndTextTuples: result: StyleAndTextTuples = [] @@ -1315,13 +1312,12 @@ def _get_line_prefix( Return whatever needs to be inserted before every line. (the prompt, or a line continuation.) """ - # First line: display the "arg" or the prompt. if line_number == 0 and wrap_count == 0: - if not is_true(self.multiline) and get_app().key_processor.arg is not None: - return self._inline_arg() - else: + if is_true(self.multiline) or get_app().key_processor.arg is None: return get_prompt_text_2() + else: + return self._inline_arg() # For the next lines, display the appropriate continuation. prompt_width = get_cwidth(fragment_list_to_text(get_prompt_text_2())) return self._get_continuation(prompt_width, line_number, wrap_count) @@ -1343,14 +1339,13 @@ def _inline_arg(self) -> StyleAndTextTuples: app = get_app() if app.key_processor.arg is None: return [] - else: - arg = app.key_processor.arg + arg = app.key_processor.arg - return [ - ("class:prompt.arg", "(arg: "), - ("class:prompt.arg.text", str(arg)), - ("class:prompt.arg", ") "), - ] + return [ + ("class:prompt.arg", "(arg: "), + ("class:prompt.arg.text", str(arg)), + ("class:prompt.arg", ") "), + ] # Expose the Input and Output objects as attributes, mainly for # backward-compatibility. diff --git a/src/prompt_toolkit/shortcuts/utils.py b/src/prompt_toolkit/shortcuts/utils.py index a628f4b6ae..82f7821a20 100644 --- a/src/prompt_toolkit/shortcuts/utils.py +++ b/src/prompt_toolkit/shortcuts/utils.py @@ -178,11 +178,7 @@ def print_container( print_container( Frame(TextArea(text='Hello world!'))) """ - if file: - output = create_output(stdout=file) - else: - output = get_app_session().output - + output = create_output(stdout=file) if file else get_app_session().output app: Application[None] = Application( layout=Layout(container=container), output=output, diff --git a/src/prompt_toolkit/styles/defaults.py b/src/prompt_toolkit/styles/defaults.py index 4ac554562c..2872da2771 100644 --- a/src/prompt_toolkit/styles/defaults.py +++ b/src/prompt_toolkit/styles/defaults.py @@ -1,6 +1,7 @@ """ The default styling. """ + from prompt_toolkit.cache import memoized from .base import ANSI_COLOR_NAMES, BaseStyle @@ -126,11 +127,12 @@ # Style that will turn for instance the class 'red' into 'red'. -COLORS_STYLE = [(name, "fg:" + name) for name in ANSI_COLOR_NAMES] + [ - (name.lower(), "fg:" + name) for name in NAMED_COLORS +COLORS_STYLE = [(name, f"fg:{name}") for name in ANSI_COLOR_NAMES] + [ + (name.lower(), f"fg:{name}") for name in NAMED_COLORS ] + WIDGETS_STYLE = [ # Dialog windows. ("dialog", "bg:#4444ff"), diff --git a/src/prompt_toolkit/styles/pygments.py b/src/prompt_toolkit/styles/pygments.py index 382e5e315b..500abb2d4c 100644 --- a/src/prompt_toolkit/styles/pygments.py +++ b/src/prompt_toolkit/styles/pygments.py @@ -48,10 +48,11 @@ def style_from_pygments_dict(pygments_dict: Dict["Token", str]) -> Style: Create a :class:`.Style` instance from a Pygments style dictionary. (One that maps Token objects to style strings.) """ - pygments_style = [] + pygments_style = [ + (pygments_token_to_classname(token), style) + for token, style in pygments_dict.items() + ] - for token, style in pygments_dict.items(): - pygments_style.append((pygments_token_to_classname(token), style)) return Style(pygments_style) diff --git a/src/prompt_toolkit/styles/style.py b/src/prompt_toolkit/styles/style.py index 6e4bd1f43c..16c6969236 100644 --- a/src/prompt_toolkit/styles/style.py +++ b/src/prompt_toolkit/styles/style.py @@ -49,26 +49,24 @@ def parse_color(text: str) -> str: pass # Hex codes. - if text[0:1] == "#": + if text.startswith("#"): col = text[1:] # Keep this for backwards-compatibility (Pygments does it). # I don't like the '#' prefix for named colors. - if col in ANSI_COLOR_NAMES: + if ( + col in ANSI_COLOR_NAMES + or col not in ANSI_COLOR_NAMES_ALIASES + and len(col) == 6 + ): return col elif col in ANSI_COLOR_NAMES_ALIASES: return ANSI_COLOR_NAMES_ALIASES[col] - # 6 digit hex color. - elif len(col) == 6: - return col - - # 3 digit hex color. elif len(col) == 3: return col[0] * 2 + col[1] * 2 + col[2] * 2 - # Default. - elif text in ("", "default"): + elif text in {"", "default"}: return text raise ValueError("Wrong color format %r" % text) @@ -95,13 +93,9 @@ def _expand_classname(classname: str) -> List[str]: E.g. 'a.b.c' becomes ['a', 'a.b', 'a.b.c'] """ - result = [] parts = classname.split(".") - for i in range(1, len(parts) + 1): - result.append(".".join(parts[:i]).lower()) - - return result + return [".".join(parts[:i]).lower() for i in range(1, len(parts) + 1)] def _parse_style_str(style_str: str) -> Attrs: @@ -110,16 +104,12 @@ def _parse_style_str(style_str: str) -> Attrs: and return a `Attrs` instance. """ # Start from default Attrs. - if "noinherit" in style_str: - attrs = DEFAULT_ATTRS - else: - attrs = _EMPTY_ATTRS - + attrs = DEFAULT_ATTRS if "noinherit" in style_str else _EMPTY_ATTRS # Now update with the given attributes. for part in style_str.split(): if part == "noinherit": - pass - elif part == "bold": + continue + if part == "bold": attrs = attrs._replace(bold=True) elif part == "nobold": attrs = attrs._replace(bold=False) @@ -136,7 +126,6 @@ def _parse_style_str(style_str: str) -> Attrs: elif part == "nostrike": attrs = attrs._replace(strike=False) - # prompt_toolkit extensions. Not in Pygments. elif part == "blink": attrs = attrs._replace(blink=True) elif part == "noblink": @@ -150,18 +139,14 @@ def _parse_style_str(style_str: str) -> Attrs: elif part == "nohidden": attrs = attrs._replace(hidden=False) - # Pygments properties that we ignore. elif part in ("roman", "sans", "mono"): pass elif part.startswith("border:"): pass - # Ignore pieces in between square brackets. This is internal stuff. - # Like '[transparent]' or '[set-cursor-position]'. elif part.startswith("[") and part.endswith("]"): pass - # Colors. elif part.startswith("bg:"): attrs = attrs._replace(bgcolor=parse_color(part[3:])) elif part.startswith("fg:"): # The 'fg:' prefix is optional. @@ -291,9 +276,7 @@ def get_attrs_for_style_str( for new_name in new_class_names: # Build a set of all possible class combinations to be applied. - combos = set() - combos.add(frozenset([new_name])) - + combos = {frozenset([new_name])} for count in range(1, len(class_names) + 1): for c2 in itertools.combinations(class_names, count): combos.add(frozenset(c2 + (new_name,))) @@ -305,7 +288,6 @@ def get_attrs_for_style_str( class_names.add(new_name) - # Process inline style. else: inline_attrs = _parse_style_str(part) list_of_attrs.append(inline_attrs) diff --git a/src/prompt_toolkit/styles/style_transformation.py b/src/prompt_toolkit/styles/style_transformation.py index 91308f9127..dc38358a80 100644 --- a/src/prompt_toolkit/styles/style_transformation.py +++ b/src/prompt_toolkit/styles/style_transformation.py @@ -203,7 +203,7 @@ def _color_to_rgb(self, color: str) -> Tuple[float, float, float]: # Parse RRGGBB format. return ( - int(color[0:2], 16) / 255.0, + int(color[:2], 16) / 255.0, int(color[2:4], 16) / 255.0, int(color[4:6], 16) / 255.0, ) diff --git a/src/prompt_toolkit/utils.py b/src/prompt_toolkit/utils.py index 4ceded34c4..2cbddd367a 100644 --- a/src/prompt_toolkit/utils.py +++ b/src/prompt_toolkit/utils.py @@ -250,7 +250,7 @@ def take_using_weights( integers, not floats.) """ assert len(items) == len(weights) - assert len(items) > 0 + assert items # Remove items with zero-weight. items2 = [] @@ -268,7 +268,7 @@ def take_using_weights( raise ValueError("Did't got any items with a positive weight.") # - already_taken = [0 for i in items] + already_taken = [0 for _ in items] item_count = len(items) max_weight = max(weights) @@ -290,18 +290,12 @@ def take_using_weights( def to_str(value: Union[Callable[[], str], str]) -> str: "Turn callable or string into string." - if callable(value): - return to_str(value()) - else: - return str(value) + return to_str(value()) if callable(value) else str(value) def to_int(value: Union[Callable[[], int], int]) -> int: "Turn callable or int into int." - if callable(value): - return to_int(value()) - else: - return int(value) + return to_int(value()) if callable(value) else int(value) AnyFloat = Union[Callable[[], float], float] @@ -309,10 +303,7 @@ def to_int(value: Union[Callable[[], int], int]) -> int: def to_float(value: AnyFloat) -> float: "Turn callable or float into float." - if callable(value): - return to_float(value()) - else: - return float(value) + return to_float(value()) if callable(value) else float(value) def is_dumb_terminal(term: Optional[str] = None) -> bool: diff --git a/src/prompt_toolkit/validation.py b/src/prompt_toolkit/validation.py index 8bdffff524..8896d72d55 100644 --- a/src/prompt_toolkit/validation.py +++ b/src/prompt_toolkit/validation.py @@ -118,11 +118,7 @@ def __repr__(self) -> str: def validate(self, document: Document) -> None: if not self.func(document.text): - if self.move_cursor_to_end: - index = len(document.text) - else: - index = 0 - + index = len(document.text) if self.move_cursor_to_end else 0 raise ValidationError(cursor_position=index, message=self.error_message) diff --git a/src/prompt_toolkit/widgets/base.py b/src/prompt_toolkit/widgets/base.py index bd2d332209..f3d1230178 100644 --- a/src/prompt_toolkit/widgets/base.py +++ b/src/prompt_toolkit/widgets/base.py @@ -248,20 +248,14 @@ def __init__( ) if multiline: - if scrollbar: - right_margins = [ScrollbarMargin(display_arrows=True)] - else: - right_margins = [] - if line_numbers: - left_margins = [NumberedMargin()] - else: - left_margins = [] + right_margins = [ScrollbarMargin(display_arrows=True)] if scrollbar else [] + left_margins = [NumberedMargin()] if line_numbers else [] else: height = D.exact(1) left_margins = [] right_margins = [] - style = "class:text-area " + style + style = f"class:text-area {style}" # If no height was given, guarantee height of at least 1. if height is None: @@ -352,16 +346,14 @@ def __init__( self.text = text def get_width() -> AnyDimension: - if width is None: - text_fragments = to_formatted_text(self.text) - text = fragment_list_to_text(text_fragments) - if text: - longest_line = max(get_cwidth(line) for line in text.splitlines()) - else: - return D(preferred=0) - return D(preferred=longest_line) - else: + if width is not None: return width + text_fragments = to_formatted_text(self.text) + if text := fragment_list_to_text(text_fragments): + longest_line = max(get_cwidth(line) for line in text.splitlines()) + else: + return D(preferred=0) + return D(preferred=longest_line) self.formatted_text_control = FormattedTextControl(text=lambda: self.text) @@ -858,11 +850,7 @@ def __init__( values: Sequence[Tuple[_T, AnyFormattedText]], default: Optional[_T] = None, ) -> None: - if default is None: - default_values = None - else: - default_values = [default] - + default_values = None if default is None else [default] super().__init__(values, default_values=default_values) @@ -901,10 +889,7 @@ def checked(self) -> bool: @checked.setter def checked(self, value: bool) -> None: - if value: - self.current_values = ["value"] - else: - self.current_values = [] + self.current_values = ["value"] if value else [] class VerticalLine: @@ -943,10 +928,6 @@ def __init__(self) -> None: self.container = FloatContainer( content=Window(height=1), floats=[ - # We first draw the label, then the actual progress bar. Right - # now, this is the only way to have the colors of the progress - # bar appear on top of the label. The problem is that our label - # can't be part of any `Window` below. Float(content=self.label, top=0, bottom=0), Float( left=0, @@ -957,11 +938,13 @@ def __init__(self) -> None: [ Window( style="class:progress-bar.used", - width=lambda: D(weight=int(self._percentage)), + width=lambda: D(weight=self._percentage), ), Window( style="class:progress-bar", - width=lambda: D(weight=int(100 - self._percentage)), + width=lambda: D( + weight=int(100 - self._percentage) + ), ), ] ), diff --git a/src/prompt_toolkit/widgets/menus.py b/src/prompt_toolkit/widgets/menus.py index 6827ebecc7..f099afa0a9 100644 --- a/src/prompt_toolkit/widgets/menus.py +++ b/src/prompt_toolkit/widgets/menus.py @@ -115,13 +115,11 @@ def _up_in_submenu(event: E) -> None: menu = self._get_menu(len(self.selected_menu) - 2) index = self.selected_menu[-1] - previous_indexes = [ + if previous_indexes := [ i for i, item in enumerate(menu.children) if i < index and not item.disabled - ] - - if previous_indexes: + ]: self.selected_menu[-1] = previous_indexes[-1] elif len(self.selected_menu) == 2: # Return to main menu. @@ -133,13 +131,11 @@ def _down_in_submenu(event: E) -> None: menu = self._get_menu(len(self.selected_menu) - 2) index = self.selected_menu[-1] - next_indexes = [ + if next_indexes := [ i for i, item in enumerate(menu.children) if i > index and not item.disabled - ] - - if next_indexes: + ]: self.selected_menu[-1] = next_indexes[0] @kb.add("enter") @@ -368,7 +364,4 @@ def __init__( @property def width(self) -> int: - if self.children: - return max(get_cwidth(c.text) for c in self.children) - else: - return 0 + return max(get_cwidth(c.text) for c in self.children) if self.children else 0 diff --git a/src/prompt_toolkit/widgets/toolbars.py b/src/prompt_toolkit/widgets/toolbars.py index 402ecaa982..c126f65d7f 100644 --- a/src/prompt_toolkit/widgets/toolbars.py +++ b/src/prompt_toolkit/widgets/toolbars.py @@ -346,23 +346,19 @@ def __init__(self, show_position: bool = False) -> None: def get_formatted_text() -> StyleAndTextTuples: buff = get_app().current_buffer - if buff.validation_error: - row, column = buff.document.translate_index_to_position( - buff.validation_error.cursor_position - ) + if not buff.validation_error: + return [] + row, column = buff.document.translate_index_to_position( + buff.validation_error.cursor_position + ) - if show_position: - text = "{} (line={} column={})".format( - buff.validation_error.message, - row + 1, - column + 1, - ) - else: - text = buff.validation_error.message + text = ( + f"{buff.validation_error.message} (line={row + 1} column={column + 1})" + if show_position + else buff.validation_error.message + ) - return [("class:validation-toolbar", text)] - else: - return [] + return [("class:validation-toolbar", text)] self.control = FormattedTextControl(get_formatted_text) diff --git a/tests/test_buffer.py b/tests/test_buffer.py index 413ec98743..1edc40d6e9 100644 --- a/tests/test_buffer.py +++ b/tests/test_buffer.py @@ -5,8 +5,7 @@ @pytest.fixture def _buffer(): - buff = Buffer() - return buff + return Buffer() def test_initial(_buffer): diff --git a/tests/test_completion.py b/tests/test_completion.py index 26f6644cd4..539229ada8 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -42,7 +42,7 @@ def test_pathcompleter_completes_in_current_directory(): doc = Document(doc_text, len(doc_text)) event = CompleteEvent() completions = list(completer.get_completions(doc, event)) - assert len(completions) > 0 + assert completions def test_pathcompleter_completes_files_in_current_directory(): @@ -203,7 +203,7 @@ def test_pathcompleter_can_expanduser(): doc = Document(doc_text, len(doc_text)) event = CompleteEvent() completions = list(completer.get_completions(doc, event)) - assert len(completions) > 0 + assert completions def test_pathcompleter_can_apply_file_filter(): @@ -268,7 +268,7 @@ def test_word_completer_static_word_list(): assert [c.text for c in completions] == ["abc", "aaa"] completions = completer.get_completions(Document("A"), CompleteEvent()) - assert [c.text for c in completions] == [] + assert not [c.text for c in completions] # Multiple words ending with space. (Accept all options) completions = completer.get_completions(Document("test "), CompleteEvent()) @@ -342,7 +342,7 @@ def test_word_completer_pattern(): # Without pattern completer = WordCompleter(["abc", "a.b.c", "a.b", "xyz"]) completions = completer.get_completions(Document("a."), CompleteEvent()) - assert [c.text for c in completions] == [] + assert not [c.text for c in completions] def test_fuzzy_completer(): diff --git a/tests/test_regular_languages.py b/tests/test_regular_languages.py index 7404b231a4..ca74a541b8 100644 --- a/tests/test_regular_languages.py +++ b/tests/test_regular_languages.py @@ -76,13 +76,13 @@ def test_prefix(): def test_completer(): class completer1(Completer): def get_completions(self, document, complete_event): - yield Completion("before-%s-after" % document.text, -len(document.text)) - yield Completion("before-%s-after-B" % document.text, -len(document.text)) + yield Completion(f"before-{document.text}-after", -len(document.text)) + yield Completion(f"before-{document.text}-after-B", -len(document.text)) class completer2(Completer): def get_completions(self, document, complete_event): - yield Completion("before2-%s-after2" % document.text, -len(document.text)) - yield Completion("before2-%s-after2-B" % document.text, -len(document.text)) + yield Completion(f"before2-{document.text}-after2", -len(document.text)) + yield Completion(f"before2-{document.text}-after2-B", -len(document.text)) # Create grammar. "var1" + "whitespace" + "var2" g = compile(r"(?P[a-z]*) \s+ (?P[a-z]*)")