#fzf #bindings #cli-tool #api-bindings #api

fzf-wrapped

一个将fzf命令行工具集成到你的Rust程序中的库!

5个版本

0.1.4 2024年7月28日
0.1.3 2023年11月11日
0.1.2 2023年11月11日
0.1.1 2023年11月10日
0.1.0 2023年11月3日

#286GUI

Download history 5/week @ 2024-05-02 7/week @ 2024-05-09 13/week @ 2024-05-16 17/week @ 2024-05-23 12/week @ 2024-05-30 16/week @ 2024-06-06 17/week @ 2024-06-13 19/week @ 2024-06-20 7/week @ 2024-06-27 2/week @ 2024-07-04 6/week @ 2024-07-11 16/week @ 2024-07-18 188/week @ 2024-07-25 38/week @ 2024-08-01 10/week @ 2024-08-08 56/week @ 2024-08-15

297 每月下载量
2 crates 中使用

MIT 许可证

39KB
318

fzf-wrapper

一个将fzf命令行工具集成到你的Rust程序中的库!

此库提供了将fzf命令行程序集成到你的Rust项目中的绑定。

注意 这意味着最终用户必须在他们的系统上安装fzf

fzf版本

这个crate是在考虑使用fzf v0.40.0的情况下开发的,然而这并不意味着它不能在更高的版本上工作,而且大多数功能应该也能在较低的版本上工作。如果你的程序依赖于v0.40.0的功能,检查你的程序可以访问的fzf版本可能是一个好主意。

示例

假设我们想要使用模糊查找的强大功能让用户选择他们最喜欢的颜色。首先,我们需要创建一个颜色列表,这将是一个我们将传递给fzf的集合

let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

下一步是构建一个Fzf实例并启动它

let mut fzf = Fzf::default();

fzf.run().expect("Failed to start fzf");

上面的代码获取了默认的Fzf配置,它会无参数运行fzf,然后调用run()方法。这将向用户显示fzf

目前显示的将是一个空白屏幕,因为我们没有实际告诉fzf显示任何内容。有两种方法可以做到这一点,即add_item()方法,和add_items()方法。它们几乎相同,唯一的区别是add_items()接受一个包含String的向量作为项目,并将它们逐个传递给add_item()

let mut fzf = Fzf::default();
fzf.run().expect("Failed to start fzf");
fzf.add_items(colours).expect("Failed to add items");

剩下要做的就是获取用户所选的内容!这将作为一个包含 NoneOption 返回,如果用户退出了 fzf,或者以用户选择的项为字符串的 Some(String) 返回。要获取输出,我们只需调用 output() 方法,该方法将阻塞执行,直到用户使用 fzf 选择一个项目。

let mut fzf = Fzf::default();
fzf.run().expect("Failed to start fzf");
fzf.add_items(colours).expect("Failed to add items");
let users_selection = fzf.output().expect("Failed to get the user's output");

完整的代码如下。

use fzf_wrapped::Fzf;

fn main() {
    let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
    
    let mut fzf = Fzf::default();
    fzf.run().expect("Failed to start fzf");

    fzf.add_items(colours).expect("Failed to add items");

    let users_selection = fzf.output().expect("Failed to get the user's output");
}

使用 fzf 从一个预定义的 [Vec] 项中选择操作非常常见,因此存在一个辅助函数来简化相关工作。使用它,代码看起来如下所示。

use fzf_wrapped::Fzf;
use fzf_wrapped::run_with_output;

fn main() {
    let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
    
    let users_selection = run_with_output(Fzf::default(), colours).expect("Something went wrong!");
}

运行带有参数的 fzf

现在,我们最喜欢的颜色选择程序相当酷,但我们的 fzf 界面有点令人困惑,用户该如何知道他们选择了什么?

幸运的是,fzf 提供了许多不同的自定义方式,其中许多已经在库中实现。到目前为止,我们一直在调用 [Fzf] 结构体的默认实现,但是我们可以使用 FzfBuilder 构建更复杂的实例。

我们可以通过两种不同的方式获取 FzfBuilder,要么通过它的 new() 方法,要么通过 [Fzf] 的 builder() 方法。让我们将默认调用更改为构建器调用。

let fzf = Fzf::builder().build().unwrap();

let users_selection = run_with_output(fzf, colours).expect("Something went wrong!");

注意:由于每个字段都有默认实现,因此可以安全地解包 build() 调用。

如果我们现在运行程序,我们会注意到... 没有变化。这是因为 default() 方法调用我们刚刚替换的精确行代码。

让我们让事情变得有趣!首先,我们可能需要给查找器一个标签。没有边框,标签将不会显示,所以让我们也给它一个边框!

添加边框和边框标签

如果您直接从命令行使用 fzf,您将使用 --border 标志与支持的边框类型之一,但是这可能会导致错误,如果边框不存在。因此,fzf-wrapped 使用枚举来确保这些选择始终有效!例如,要选择边框,我们会调用我们的 FzfBuilder 上的 border() 方法,包含我们选择的 Border 枚举的变体。

添加圆角边框后,代码看起来如下所示。

use fzf_wrapped::Fzf;
use fzf_wrapped::Border;
use fzf_wrapped::run_with_output;

fn main() {
    let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

    let fzf = Fzf::builder()
        .border(Border::Rounded)
        .build()
        .unwrap();
    
    let users_selection = run_with_output(fzf, colours);
}

添加标签甚至更简单。

use fzf_wrapped::Fzf;
use fzf_wrapped::Border;
use fzf_wrapped::run_with_output;

fn main() {
    let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

    let fzf = Fzf::builder()
        .border(Border::Rounded)
        .border_label("Favourite Colour")
        .build()
        .unwrap();
    
    let users_selection = run_with_output(fzf, colours);
}

运行此操作应显示具有圆角边框和包含 "最喜欢的颜色" 的居中标签的 fzf 查找器。

更改 fzf 的布局

现在我们有了边框,不妨来调整一下布局。这几乎和改变边框一样,因为所有可能的布局都映射到一个枚举。

我们只需要在我们的构建器中添加一个名为 layout() 的方法,并选择我们的 Layout 变体。

use fzf_wrapped::Fzf;
use fzf_wrapped::{Border, Layout};
use fzf_wrapped::run_with_output;

fn main() {
    let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

    let fzf = Fzf::builder()
        .layout(Layout::Reverse)
        .border(Border::Rounded)
        .border_label("Favourite Colour")
        .build()
        .unwrap();
    
    let users_selection = run_with_output(fzf, colours);
}

选择颜色

fzf 允许我们选择几个颜色主题,但这次我们将其保持简单,使用黑白主题。与边框和布局类似,这也是通过枚举选择的。将其添加到我们的构建器中,代码如下:

use fzf_wrapped::Fzf;
use fzf_wrapped::{Border, Color, Layout};
use fzf_wrapped::run_with_output;

fn main() {
    let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

    let fzf = Fzf::builder()
        .layout(Layout::Reverse)
        .border(Border::Rounded)
        .border_label("Favourite Colour")
        .color(Color::Bw)
        .build()
        .unwrap();
    
    let users_selection = run_with_output(fzf, colours);
}

现在如果您运行程序,会发现 UI 是黑白显示的!

添加标题

用户可能仍然不清楚他们在选择什么,因此为了提供更多上下文,fzf 允许我们设置一个标题。为此,我们只需在 FzfBuilder 结构体上调用 header() 方法,并传递任何具有 Into<String> 特性的内容。我们还希望标题出现在搜索字段上方,因此我们将调用 header_first() 方法并传递 true

use fzf_wrapped::Fzf;
use fzf_wrapped::{Border, Color, Layout};
use fzf_wrapped::run_with_output;

fn main() {
    let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

    let fzf = Fzf::builder()
        .layout(Layout::Reverse)
        .border(Border::Rounded)
        .border_label("Favourite Colour")
        .color(Color::Bw)
        .header("Pick your favourite colour")
        .header_first(true)
        .build()
        .unwrap();
    
    let users_selection = run_with_output(fzf, colours);
}

使用 fzf_wrapped 不支持的参数

如果希望与 fzf 一起运行的参数不是 [Fzf] 结构体上的方法,请不要担心!custom_args() 命令将允许您传递任何想要的参数!例如,假设我们想要让我们的颜色选择程序只占用屏幕的 10%,而不是整个屏幕。虽然 fzf--height= 标志,但 [Fzf] 结构体不支持它!为了添加它,我们只需在我们的构建器上调用 custom_args() 命令,并将传递给它的参数与 run() 方法一起运行。

实现方式如下:

use fzf_wrapped::Fzf;
use fzf_wrapped::{Border, Color, Layout};
use fzf_wrapped::run_with_output;

fn main() {
    let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

    let fzf = Fzf::builder()
        .layout(Layout::Reverse)
        .border(Border::Rounded)
        .border_label("Favourite Colour")
        .color(Color::Bw)
        .header("Pick your favourite colour")
        .header_first(true)
        .custom_args(vec!["--height=10".to_string()])
        .build()
        .unwrap();
    
    let users_selection = run_with_output(fzf, colours);

    if let Some(colour) = users_selection {
        println!("{} is an awesome colour!", colour);
    }
}

至此,我们的程序几乎完成了!

我们只需打印一些友好的信息,同时,我们也可以使用一些合适的错误处理。

use fzf_wrapped::Fzf;
use fzf_wrapped::{Border, Color, Layout};
use fzf_wrapped::run_with_output;

fn main() {
    let colours = vec!["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

    let fzf = Fzf::builder()
        .layout(Layout::Reverse)
        .border(Border::Rounded)
        .border_label("Favourite Colour")
        .color(Color::Bw)
        .header("Pick your favourite colour")
        .header_first(true)
        .custom_args(vec!["--height=10".to_string()])
        .build()
        .unwrap();
    
    let users_selection = run_with_output(fzf, colours);

    if let Some(colour) = users_selection {
        println!("{} is an awesome colour!", colour);
    }
}

fzf 运行时添加项

利用这个库的强大功能,您可以使用 fzf 从项目列表中选择,即使这些项目尚未检索。使用 add_itemadd_items 方法,即使在 fzf 运行时,也可以向 fzf 的列表中添加项目。这意味着如果您从 REST API 获取信息,可以实时将结果显示在 fzf 中,或者通过启动 fzf 来隐藏轻微的延迟。

有关此示例,请查看我的 workflows 项目。

依赖项

约 2MB
约 45K SLoC