add plugin manager to ais (add/ls/rm)
This commit is contained in:
7
.vimrc
7
.vimrc
@@ -33,6 +33,13 @@ inoremap <C-j> <Esc>
|
||||
nnoremap Q :q!<CR>
|
||||
vnoremap v V
|
||||
|
||||
" load plugins from ~/.vim/plugged/
|
||||
for s:dir in glob('~/.vim/plugged/*', 0, 1)
|
||||
if isdirectory(s:dir)
|
||||
exe 'set rtp+=' . s:dir
|
||||
endif
|
||||
endfor
|
||||
|
||||
syntax on
|
||||
filetype plugin indent on
|
||||
au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") | exe "normal! g`\"" | endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// ais - ai selector
|
||||
// unified fuzzy finder: directories, files, history
|
||||
// usage: ais [cd|file|hist] or pipe: echo "a\nb" | ais
|
||||
// unified fuzzy finder + plugin manager
|
||||
// usage: ais [cd|file|hist|plugin] or pipe: echo "a\nb" | ais
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
@@ -11,6 +11,16 @@ fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let mode = args.get(1).map(|s| s.as_str()).unwrap_or("cd");
|
||||
|
||||
match mode {
|
||||
"plugin" | "p" => {
|
||||
let sub = args.get(2).map(|s| s.as_str()).unwrap_or("ls");
|
||||
let rest: Vec<&str> = args.iter().skip(3).map(|s| s.as_str()).collect();
|
||||
plugin_cmd(sub, &rest);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let items = if !is_tty_stdin() {
|
||||
read_stdin_lines()
|
||||
} else {
|
||||
@@ -19,7 +29,7 @@ fn main() {
|
||||
"file" | "f" => get_files(),
|
||||
"hist" | "h" => get_history(),
|
||||
_ => {
|
||||
eprintln!("usage: ais [cd|file|hist]");
|
||||
eprintln!("usage: ais [cd|file|hist|plugin]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +44,125 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// --- plugin manager ---
|
||||
|
||||
fn plugin_dir() -> String {
|
||||
let home = env::var("HOME").unwrap_or_default();
|
||||
format!("{}/.vim/plugged", home)
|
||||
}
|
||||
|
||||
fn plugin_cmd(sub: &str, args: &[&str]) {
|
||||
match sub {
|
||||
"add" | "a" => {
|
||||
if args.is_empty() {
|
||||
eprintln!("usage: ais plugin add <user/repo>");
|
||||
return;
|
||||
}
|
||||
plugin_add(args[0]);
|
||||
}
|
||||
"rm" | "r" => plugin_rm(args.first().copied()),
|
||||
"ls" | "l" | "" => plugin_ls(),
|
||||
_ => eprintln!("usage: ais plugin [add|ls|rm]"),
|
||||
}
|
||||
}
|
||||
|
||||
fn plugin_add(name: &str) {
|
||||
let dir = plugin_dir();
|
||||
std::fs::create_dir_all(&dir).ok();
|
||||
|
||||
// accept "user/repo" or full github url
|
||||
let (url, repo_name) = if name.contains("://") {
|
||||
let repo = name.rsplit('/').next().unwrap_or(name);
|
||||
let repo = repo.strip_suffix(".git").unwrap_or(repo);
|
||||
(name.to_string(), repo.to_string())
|
||||
} else {
|
||||
let repo = name.split('/').last().unwrap_or(name);
|
||||
(format!("https://github.com/{}.git", name), repo.to_string())
|
||||
};
|
||||
|
||||
let dest = format!("{}/{}", dir, repo_name);
|
||||
if std::path::Path::new(&dest).exists() {
|
||||
eprintln!("{} already installed", repo_name);
|
||||
return;
|
||||
}
|
||||
|
||||
eprintln!("installing {} ...", repo_name);
|
||||
let status = Command::new("git")
|
||||
.args(["clone", "--depth", "1", &url, &dest])
|
||||
.status();
|
||||
|
||||
match status {
|
||||
Ok(s) if s.success() => eprintln!("ok: {}", repo_name),
|
||||
_ => eprintln!("failed: {}", url),
|
||||
}
|
||||
}
|
||||
|
||||
fn plugin_ls() {
|
||||
let dir = plugin_dir();
|
||||
let entries = match std::fs::read_dir(&dir) {
|
||||
Ok(e) => e,
|
||||
Err(_) => {
|
||||
eprintln!("no plugins");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
for entry in entries.flatten() {
|
||||
if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) {
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
if !name.starts_with('.') {
|
||||
println!("{}", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn plugin_rm(name: Option<&str>) {
|
||||
let dir = plugin_dir();
|
||||
|
||||
let target = if let Some(n) = name {
|
||||
n.to_string()
|
||||
} else {
|
||||
// fuzzy select from installed plugins
|
||||
let entries = match std::fs::read_dir(&dir) {
|
||||
Ok(e) => e,
|
||||
Err(_) => {
|
||||
eprintln!("no plugins");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let plugins: Vec<String> = entries
|
||||
.flatten()
|
||||
.filter(|e| e.file_type().map(|t| t.is_dir()).unwrap_or(false))
|
||||
.map(|e| e.file_name().to_string_lossy().to_string())
|
||||
.filter(|n| !n.starts_with('.'))
|
||||
.collect();
|
||||
|
||||
if plugins.is_empty() {
|
||||
eprintln!("no plugins");
|
||||
return;
|
||||
}
|
||||
|
||||
match fuzzy_select(&plugins, "rm plugin") {
|
||||
Some(s) => s,
|
||||
None => return,
|
||||
}
|
||||
};
|
||||
|
||||
let path = format!("{}/{}", dir, target);
|
||||
if !std::path::Path::new(&path).exists() {
|
||||
eprintln!("{} not found", target);
|
||||
return;
|
||||
}
|
||||
|
||||
match std::fs::remove_dir_all(&path) {
|
||||
Ok(_) => eprintln!("removed: {}", target),
|
||||
Err(e) => eprintln!("failed: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
// --- selectors ---
|
||||
|
||||
fn is_tty_stdin() -> bool {
|
||||
extern "C" {
|
||||
fn isatty(fd: i32) -> i32;
|
||||
@@ -123,6 +252,8 @@ fn read_stdin_lines() -> Vec<String> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
// --- fuzzy select TUI ---
|
||||
|
||||
fn fuzzy_match(item: &str, query: &str) -> bool {
|
||||
if query.is_empty() {
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user