Skip to content

Commit

Permalink
cli: better support for bash completion (#3230)
Browse files Browse the repository at this point in the history
better support for bash completion, with instructions how to install.

<img width="540" alt="image"
src="https://github.com/user-attachments/assets/f59a6b81-c596-4e90-b050-21ec9c1fc60a"
/>
  • Loading branch information
mscolnick authored Dec 19, 2024
1 parent 8e78743 commit d7c0e43
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 27 deletions.
67 changes: 56 additions & 11 deletions marimo/_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

import json
import os
import pathlib
import sys
import tempfile
from pathlib import Path
from typing import Any, Optional

import click
Expand Down Expand Up @@ -286,7 +286,7 @@ def main(
help=sandbox_message,
)
@click.option("--profile-dir", default=None, type=str, hidden=True)
@click.argument("name", required=False)
@click.argument("name", required=False, type=click.Path())
@click.argument("args", nargs=-1, type=click.UNPROCESSED)
def edit(
port: Optional[int],
Expand Down Expand Up @@ -575,7 +575,7 @@ def new(
type=bool,
help=sandbox_message,
)
@click.argument("name", required=True)
@click.argument("name", required=True, type=click.Path())
@click.argument("args", nargs=-1, type=click.UNPROCESSED)
def run(
port: Optional[int],
Expand Down Expand Up @@ -643,15 +643,12 @@ def run(


@main.command(help="Recover a marimo notebook from JSON.")
@click.argument("name", required=True)
@click.argument(
"name",
required=True,
type=click.Path(exists=True, file_okay=True, dir_okay=False),
)
def recover(name: str) -> None:
path = pathlib.Path(name)
if not os.path.exists(name):
raise click.UsageError("Invalid NAME - %s does not exist" % name)

if not path.is_file():
raise click.UsageError("Invalid NAME - %s is not a file" % name)

click.echo(codegen.recover(name))


Expand Down Expand Up @@ -754,6 +751,54 @@ def env() -> None:
click.echo(json.dumps(get_system_info(), indent=2))


@main.command(
help="Install shell completions for marimo. Supports bash, zsh, fish, and elvish."
)
def shell_completion() -> None:
shell = os.environ.get("SHELL", "")
if not shell:
click.echo(
"Could not determine shell. Please set $SHELL environment variable.",
err=True,
)
return

shell_name = Path(shell).name

commands = {
"bash": (
'eval "$(_MARIMO_COMPLETE=bash_source marimo)"',
".bashrc",
),
"zsh": (
'eval "$(_MARIMO_COMPLETE=zsh_source marimo)"',
".zshrc",
),
"fish": (
"_MARIMO_COMPLETE=fish_source marimo | source",
".config/fish/completions/marimo.fish",
),
}

if shell_name not in commands:
supported = ", ".join(commands.keys())
click.echo(
f"Unsupported shell: {shell_name}. Supported shells: {supported}",
err=True,
)
return

cmd, rc_file = commands[shell_name]
click.secho("Run this command to enable completions:", fg="green")
click.secho(f"\n echo '{cmd}' >> ~/{rc_file}\n", fg="yellow")
click.secho(
"\nThen restart your shell or run 'source ~/"
+ rc_file
+ "' to enable completions",
fg="green",
)


main.command()(convert)
main.add_command(export)
main.add_command(config)
Expand Down
2 changes: 1 addition & 1 deletion marimo/_cli/convert/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
@click.option(
"-o",
"--output",
type=str,
type=click.Path(),
default=None,
help=(
"Output file to save the converted notebook to. "
Expand Down
10 changes: 6 additions & 4 deletions marimo/_cli/development/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,10 +340,12 @@ def killall() -> None:
@click.command(
help="Inline packages according to PEP 723", name="inline-packages"
)
@click.argument("name", required=True)
def inline_packages(
name: str,
) -> None:
@click.argument(
"name",
required=True,
type=click.Path(exists=True, file_okay=True, dir_okay=False),
)
def inline_packages(name: str) -> None:
"""
Example usage:
Expand Down
41 changes: 30 additions & 11 deletions marimo/_cli/export/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import click

import marimo._cli.cli_validators as validators
from marimo._cli.parse_args import parse_args
from marimo._cli.print import echo, green
from marimo._dependencies.dependencies import DependencyManager
Expand Down Expand Up @@ -126,14 +125,18 @@ async def start() -> None:
@click.option(
"-o",
"--output",
type=str,
type=click.Path(),
default=None,
help=(
"Output file to save the HTML to. "
"If not provided, the HTML will be printed to stdout."
),
)
@click.argument("name", required=True, callback=validators.is_file_path)
@click.argument(
"name",
required=True,
type=click.Path(exists=True, file_okay=True, dir_okay=False),
)
@click.argument("args", nargs=-1, type=click.UNPROCESSED)
def html(
name: str,
Expand Down Expand Up @@ -183,14 +186,18 @@ def export_callback(file_path: MarimoPath) -> ExportResult:
@click.option(
"-o",
"--output",
type=str,
type=click.Path(),
default=None,
help=(
"Output file to save the script to. "
"If not provided, the script will be printed to stdout."
),
)
@click.argument("name", required=True, callback=validators.is_file_path)
@click.argument(
"name",
required=True,
type=click.Path(exists=True, file_okay=True, dir_okay=False),
)
def script(
name: str,
output: str,
Expand Down Expand Up @@ -229,14 +236,18 @@ def export_callback(file_path: MarimoPath) -> ExportResult:
@click.option(
"-o",
"--output",
type=str,
type=click.Path(),
default=None,
help=(
"Output file to save the markdown to. "
"If not provided, markdown will be printed to stdout."
),
)
@click.argument("name", required=True, callback=validators.is_file_path)
@click.argument(
"name",
required=True,
type=click.Path(exists=True, file_okay=True, dir_okay=False),
)
def md(
name: str,
output: str,
Expand Down Expand Up @@ -284,7 +295,7 @@ def export_callback(file_path: MarimoPath) -> ExportResult:
@click.option(
"-o",
"--output",
type=str,
type=click.Path(),
default=None,
help=(
"Output file to save the ipynb file to. "
Expand All @@ -298,7 +309,11 @@ def export_callback(file_path: MarimoPath) -> ExportResult:
type=bool,
help="Run the notebook and include outputs in the exported ipynb file.",
)
@click.argument("name", required=True, callback=validators.is_file_path)
@click.argument(
"name",
required=True,
type=click.Path(exists=True, file_okay=True, dir_okay=False),
)
def ipynb(
name: str,
output: str,
Expand Down Expand Up @@ -348,7 +363,7 @@ def export_callback(file_path: MarimoPath) -> ExportResult:
@click.option(
"-o",
"--output",
type=str,
type=click.Path(),
required=True,
help="Output directory to save the HTML to.",
)
Expand All @@ -366,7 +381,11 @@ def export_callback(file_path: MarimoPath) -> ExportResult:
show_default=True,
help="Whether to show code by default in the exported HTML file.",
)
@click.argument("name", required=True, callback=validators.is_file_path)
@click.argument(
"name",
required=True,
type=click.Path(exists=True, file_okay=True, dir_okay=False),
)
def html_wasm(
name: str,
output: str,
Expand Down
9 changes: 9 additions & 0 deletions tests/_cli/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,15 @@ def test_cli_run_sandbox_prompt_yes() -> None:
p.kill()


def test_shell_completion() -> None:
p = subprocess.run(
["marimo", "shell-completion"],
capture_output=True,
)
assert p.returncode == 0
assert p.stdout is not None


HAS_DOCKER = DependencyManager.which("docker")


Expand Down

0 comments on commit d7c0e43

Please sign in to comment.