summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo/config.toml8
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml2
-rw-r--r--macros/comp_system_macros/Cargo.toml12
-rw-r--r--macros/comp_system_macros/src/lib.rs73
-rw-r--r--resources/locales/jvn/cmds/sheetdump/en.yml6
-rw-r--r--resources/locales/jvn/cmds/sheetdump/zh-CN.yml6
-rw-r--r--resources/locales/jvn/cmds/sheetedit/en.yml4
-rw-r--r--resources/locales/jvn/cmds/sheetedit/zh-CN.yml4
-rw-r--r--resources/locales/jvn/cmds/version/en.yml6
-rw-r--r--resources/locales/jvn/cmds/version/zh-CN.yml6
-rw-r--r--resources/locales/jvn/cmds/workspace/alias/en.yml6
-rw-r--r--resources/locales/jvn/cmds/workspace/alias/zh-CN.yml6
-rw-r--r--resources/locales/jvn/cmds/workspace/sheet/en.yml10
-rw-r--r--resources/locales/jvn/cmds/workspace/sheet/zh-CN.yml10
-rw-r--r--resources/locales/jvn/en.yml20
-rw-r--r--resources/locales/jvn/zh-CN.yml20
-rw-r--r--scripts/deploy/completions/fish.fish58
-rw-r--r--scripts/deploy/completions/zsh.zsh25
-rw-r--r--scripts/deploy/jvn_powershell.ps1 (renamed from scripts/deploy/jvn.ps1)0
-rw-r--r--src/bin/jvn_comp.rs220
-rw-r--r--src/cmds/comp/helpdoc.rs22
-rw-r--r--src/cmds/comp/sheetdump.rs16
-rw-r--r--src/cmds/comp/sheetedit.rs17
-rw-r--r--src/cmds/comp/version.rs17
-rw-r--r--src/cmds/comp/workspace_alias.rs24
-rw-r--r--src/cmds/comp/workspace_sheet.rs46
-rw-r--r--src/systems/comp.rs1
-rw-r--r--src/systems/comp/result.rs205
-rw-r--r--templates/_comps.rs.template7
30 files changed, 738 insertions, 129 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml
index 213cffe..0e29823 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -41,17 +41,17 @@ platform = [ "linux", "macos" ]
# Entries
[copies.entry_bash]
from = "scripts/deploy/jvn_bash.sh"
-to = "jvn_bash.sh"
+to = "jvn.sh"
platform = [ "linux", "macos" ]
[copies.entry_zsh]
from = "scripts/deploy/jvn_zsh.zsh"
-to = "jvn_zsh.zsh"
+to = "jvn.zsh"
platform = [ "linux", "macos" ]
[copies.entry_fish]
from = "scripts/deploy/jvn_fish.fish"
-to = "jvn_fish.fish"
+to = "jvn.fish"
platform = [ "linux", "macos" ]
##################
@@ -66,7 +66,7 @@ platform = [ "windows" ]
# Entries
[copies.entry_powershell]
-from = "scripts/deploy/jvn.ps1"
+from = "scripts/deploy/jvn_powershell.ps1"
to = "jvn.ps1"
platform = [ "windows" ]
diff --git a/Cargo.lock b/Cargo.lock
index 7c13b7e..98d8d00 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -421,6 +421,15 @@ dependencies = [
]
[[package]]
+name = "comp_system_macros"
+version = "0.1.1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "config_system"
version = "0.1.0"
dependencies = [
@@ -1181,6 +1190,7 @@ dependencies = [
"cli_utils",
"cmd_system_macros",
"colored",
+ "comp_system_macros",
"crossterm",
"env_logger",
"helpdoc_system_macros",
diff --git a/Cargo.toml b/Cargo.toml
index d805dad..d66723d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,7 @@ members = [
"utils/",
"tools/build_helper",
"macros/cmd_system_macros",
+ "macros/comp_system_macros",
"macros/helpdoc_system_macros",
"macros/render_system_macros",
]
@@ -61,6 +62,7 @@ serde = { version = "1", features = ["derive"] }
cli_utils = { path = "utils" }
just_enough_vcs = { path = "../VersionControl", features = ["all"] }
cmd_system_macros = { path = "macros/cmd_system_macros" }
+comp_system_macros = { path = "macros/comp_system_macros" }
helpdoc_system_macros = { path = "macros/helpdoc_system_macros" }
render_system_macros = { path = "macros/render_system_macros" }
diff --git a/macros/comp_system_macros/Cargo.toml b/macros/comp_system_macros/Cargo.toml
new file mode 100644
index 0000000..f571f29
--- /dev/null
+++ b/macros/comp_system_macros/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "comp_system_macros"
+version.workspace = true
+edition = "2024"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2.workspace = true
+quote.workspace = true
+syn = { workspace = true, features = ["visit"] }
diff --git a/macros/comp_system_macros/src/lib.rs b/macros/comp_system_macros/src/lib.rs
new file mode 100644
index 0000000..dd1fb01
--- /dev/null
+++ b/macros/comp_system_macros/src/lib.rs
@@ -0,0 +1,73 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+use syn::parse::{Parse, ParseStream};
+use syn::punctuated::Punctuated;
+use syn::{Expr, LitStr, Token, parse_macro_input};
+
+struct SuggestInput {
+ items: Punctuated<SuggestItem, Token![,]>,
+}
+
+enum SuggestItem {
+ WithDesc(Box<(LitStr, Expr)>), // "-i" = "Insert something"
+ Simple(LitStr), // "-I"
+}
+
+impl Parse for SuggestInput {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let items = Punctuated::parse_terminated(input)?;
+ Ok(SuggestInput { items })
+ }
+}
+
+impl Parse for SuggestItem {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let key: LitStr = input.parse()?;
+
+ if input.peek(Token![=]) {
+ let _eq: Token![=] = input.parse()?;
+ let value: Expr = input.parse()?;
+ Ok(SuggestItem::WithDesc(Box::new((key, value))))
+ } else {
+ Ok(SuggestItem::Simple(key))
+ }
+ }
+}
+
+#[proc_macro]
+pub fn suggest(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as SuggestInput);
+
+ let mut tokens = TokenStream2::new();
+
+ tokens.extend(quote! {
+ CompletionResult::empty_comp()
+ });
+
+ for item in input.items {
+ match item {
+ SuggestItem::WithDesc(boxed) => {
+ let (key, value) = *boxed;
+ tokens.extend(quote! {
+ .with_suggest_desc(#key, #value)
+ });
+ }
+ SuggestItem::Simple(key) => {
+ tokens.extend(quote! {
+ .with_suggest(#key)
+ });
+ }
+ }
+ }
+
+ tokens.into()
+}
+
+#[proc_macro]
+pub fn file_suggest(_input: TokenStream) -> TokenStream {
+ quote! {
+ CompletionResult::file_comp()
+ }
+ .into()
+}
diff --git a/resources/locales/jvn/cmds/sheetdump/en.yml b/resources/locales/jvn/cmds/sheetdump/en.yml
new file mode 100644
index 0000000..ddce64d
--- /dev/null
+++ b/resources/locales/jvn/cmds/sheetdump/en.yml
@@ -0,0 +1,6 @@
+sheetdump:
+ comp:
+ no_sort: |
+ Output results without sorting
+ no_pretty: |
+ Enable simplified output
diff --git a/resources/locales/jvn/cmds/sheetdump/zh-CN.yml b/resources/locales/jvn/cmds/sheetdump/zh-CN.yml
new file mode 100644
index 0000000..177fc93
--- /dev/null
+++ b/resources/locales/jvn/cmds/sheetdump/zh-CN.yml
@@ -0,0 +1,6 @@
+sheetdump:
+ comp:
+ no_sort: |
+ 不排序输出结果
+ no_pretty: |
+ 启用简易输出
diff --git a/resources/locales/jvn/cmds/sheetedit/en.yml b/resources/locales/jvn/cmds/sheetedit/en.yml
index 9c3e15f..56e71ae 100644
--- a/resources/locales/jvn/cmds/sheetedit/en.yml
+++ b/resources/locales/jvn/cmds/sheetedit/en.yml
@@ -25,3 +25,7 @@ sheetedit:
no_file_input: |
No file specified for parsing
Use `jvn sheetedit <sheet_file>` to edit
+
+ comp:
+ editor: |
+ Specify a specific editor
diff --git a/resources/locales/jvn/cmds/sheetedit/zh-CN.yml b/resources/locales/jvn/cmds/sheetedit/zh-CN.yml
index cd95a52..261dd55 100644
--- a/resources/locales/jvn/cmds/sheetedit/zh-CN.yml
+++ b/resources/locales/jvn/cmds/sheetedit/zh-CN.yml
@@ -24,3 +24,7 @@ sheetedit:
no_file_input: |
没有指定需要解析的文件
使用 `jvn sheetedit <文件>` 来编辑
+
+ comp:
+ editor: |
+ 指定特定的编辑器
diff --git a/resources/locales/jvn/cmds/version/en.yml b/resources/locales/jvn/cmds/version/en.yml
index 2f48936..a9a1a17 100644
--- a/resources/locales/jvn/cmds/version/en.yml
+++ b/resources/locales/jvn/cmds/version/en.yml
@@ -18,3 +18,9 @@ version:
__ CORE:[[cyan]]%{core_branch}[[/]] (Commit: %{core_commit})
__ CLI:[[cyan]]%{cli_branch}[[/]] (Commit: %{cli_commit})
+
+ comp:
+ with_compile_info: |
+ Show compile info
+ no_banner: |
+ Do not show banner
diff --git a/resources/locales/jvn/cmds/version/zh-CN.yml b/resources/locales/jvn/cmds/version/zh-CN.yml
index 0afd168..7844c5f 100644
--- a/resources/locales/jvn/cmds/version/zh-CN.yml
+++ b/resources/locales/jvn/cmds/version/zh-CN.yml
@@ -18,3 +18,9 @@ version:
__ 核心库:[[cyan]]%{core_branch}[[/]] (Commit: %{core_commit})
__ 命令行:[[cyan]]%{cli_branch}[[/]] (Commit: %{cli_commit})
+
+ comp:
+ with_compile_info: |
+ 显示编译信息
+ no_banner: |
+ 不显示横幅
diff --git a/resources/locales/jvn/cmds/workspace/alias/en.yml b/resources/locales/jvn/cmds/workspace/alias/en.yml
index 0fe993a..6d7448e 100644
--- a/resources/locales/jvn/cmds/workspace/alias/en.yml
+++ b/resources/locales/jvn/cmds/workspace/alias/en.yml
@@ -9,3 +9,9 @@ workspace_alias:
Local index `~%{local}` is mapped to `%{remote}`
no_map: |
Local index `~%{local}` has no mapping
+
+ comp:
+ insert: Insert alias
+ query: Query alias
+ erase: Delete alias
+ to: Remote index
diff --git a/resources/locales/jvn/cmds/workspace/alias/zh-CN.yml b/resources/locales/jvn/cmds/workspace/alias/zh-CN.yml
index ee71845..f13ce61 100644
--- a/resources/locales/jvn/cmds/workspace/alias/zh-CN.yml
+++ b/resources/locales/jvn/cmds/workspace/alias/zh-CN.yml
@@ -9,3 +9,9 @@ workspace_alias:
本地索引 `~%{local}` 被指向 `%{remote}`
no_map: |
本地索引 `~%{local}` 不存在指向
+
+ comp:
+ insert: 插入别名
+ query: 查询别名
+ erase: 删除别名
+ to: 远程索引
diff --git a/resources/locales/jvn/cmds/workspace/sheet/en.yml b/resources/locales/jvn/cmds/workspace/sheet/en.yml
index 0eaec26..4718afa 100644
--- a/resources/locales/jvn/cmds/workspace/sheet/en.yml
+++ b/resources/locales/jvn/cmds/workspace/sheet/en.yml
@@ -9,3 +9,13 @@ workspace_sheet:
exactly_one_required: |
Exactly one of `--new`, `--delete`, `--list-all`, or `--print-path` is required.
You can use `jvn helpdoc commands/workspace/sheet` to view usage.
+
+ comp:
+ list_all: |
+ List all structure sheets
+ print_path: |
+ Print the file path of the specified structure sheet
+ new: |
+ Create a new structure sheet
+ delete: |
+ Delete a structure sheet
diff --git a/resources/locales/jvn/cmds/workspace/sheet/zh-CN.yml b/resources/locales/jvn/cmds/workspace/sheet/zh-CN.yml
index 3998078..636a68f 100644
--- a/resources/locales/jvn/cmds/workspace/sheet/zh-CN.yml
+++ b/resources/locales/jvn/cmds/workspace/sheet/zh-CN.yml
@@ -9,3 +9,13 @@ workspace_sheet:
exactly_one_required: |
需要指定 `--new`、`--delete`、`--list-all` 或 `--print-path` 中的任意符号
您可以使用 `jvn helpdoc commands/workspace/sheet` 来查看使用方式
+
+ comp:
+ list_all: |
+ 列出所有结构表
+ print_path: |
+ 打印指定结构表的文件路径
+ new: |
+ 创建一个新的结构表
+ delete: |
+ 删除一个结构表
diff --git a/resources/locales/jvn/en.yml b/resources/locales/jvn/en.yml
index bcc9cdc..2805f04 100644
--- a/resources/locales/jvn/en.yml
+++ b/resources/locales/jvn/en.yml
@@ -381,3 +381,23 @@ verbose:
cmd_process_exec: Entering execution phase
cmd_process_exec_failed: Execution phase failed!
+
+global_flag:
+ confirm: |
+ Confirm all operations without prompting
+ help: |
+ Show help information
+ lang: |
+ Set language
+ no_error_logs: |
+ Disable error output
+ no_progress: |
+ Disable progress bar
+ quiet: |
+ Suppress all non-error output
+ renderer: |
+ Specify output renderer
+ verbose: |
+ Enable verbose output
+ version: |
+ Show version information
diff --git a/resources/locales/jvn/zh-CN.yml b/resources/locales/jvn/zh-CN.yml
index f37e85e..e7ed8e9 100644
--- a/resources/locales/jvn/zh-CN.yml
+++ b/resources/locales/jvn/zh-CN.yml
@@ -358,3 +358,23 @@ verbose:
cmd_process_exec: 进入执行阶段
cmd_process_exec_failed: 执行阶段失败!
+
+global_flag:
+ confirm: |
+ 确认所有操作,无需提示
+ help: |
+ 显示帮助信息
+ lang: |
+ 设置语言
+ no_error_logs: |
+ 禁用错误输出
+ no_progress: |
+ 禁用进度条
+ quiet: |
+ 抑制所有非错误输出
+ renderer: |
+ 指定输出渲染器
+ verbose: |
+ 启用详细输出
+ version: |
+ 显示版本信息
diff --git a/scripts/deploy/completions/fish.fish b/scripts/deploy/completions/fish.fish
index 45205a3..cb31936 100644
--- a/scripts/deploy/completions/fish.fish
+++ b/scripts/deploy/completions/fish.fish
@@ -3,6 +3,7 @@ function __jvn_fish_complete
set -l cmdline (commandline -opc)
set -l buffer (commandline -b)
set -l cursor (commandline -C)
+ set -l current_token (commandline -ct)
# Calculate current word and word index
set -l current_word ""
@@ -10,27 +11,48 @@ function __jvn_fish_complete
set -l word_index 0
set -l char_count 0
- for i in (seq (count $cmdline))
- set word $cmdline[$i]
- if test $i -gt 1
- set char_count (math $char_count + 1)
+ set -l found false
+ if test -n "$current_token"
+ for i in (seq (count $cmdline))
+ if test "$cmdline[$i]" = "$current_token"
+ set word_index $i
+ set current_word $current_token
+ if test $i -gt 1
+ set previous_word $cmdline[(math $i - 1)]
+ end
+ set found true
+ break
+ end
end
- set char_count (math $char_count + (string length -- "$word"))
+ end
- if test $cursor -le $char_count
- set word_index $i
- set current_word $word
+ if not $found
+ for i in (seq (count $cmdline))
+ set word $cmdline[$i]
if test $i -gt 1
- set previous_word $cmdline[(math $i - 1)]
+ set char_count (math $char_count + 1)
+ end
+ set char_count (math $char_count + (string length -- "$word"))
+
+ if test $cursor -le $char_count
+ set word_index $i
+ set current_word $word
+ if test $i -gt 1
+ set previous_word $cmdline[(math $i - 1)]
+ end
+ break
end
- break
end
end
# Handle cursor after last word
if test $word_index -eq 0 -a (count $cmdline) -gt 0
set word_index (count $cmdline)
- set current_word ""
+ if test -n "$current_token" -a "$current_token" != "$cmdline[-1]"
+ set current_word $current_token
+ else
+ set current_word ""
+ end
set previous_word $cmdline[-1]
end
@@ -62,6 +84,20 @@ function __jvn_fish_complete
for word in $cmdline
set -a all_words_replaced (string replace -a "-" "^" -- "$word")
end
+
+ if test -n "$current_token" -a "$current_word" = "$current_token"
+ set -l found_in_cmdline false
+ for word in $cmdline
+ if test "$word" = "$current_token"
+ set found_in_cmdline true
+ break
+ end
+ end
+ if not $found_in_cmdline -a $word_index -eq (math (count $cmdline) + 1)
+ set -a all_words_replaced (string replace -a "-" "^" -- "$current_token")
+ end
+ end
+
set -a args -a $all_words_replaced
else
set -a args -a ""
diff --git a/scripts/deploy/completions/zsh.zsh b/scripts/deploy/completions/zsh.zsh
index 2b9e7f9..99ecd09 100644
--- a/scripts/deploy/completions/zsh.zsh
+++ b/scripts/deploy/completions/zsh.zsh
@@ -34,10 +34,29 @@ _jvn_completion() {
if [[ "${completions[1]}" == "_file_" ]]; then
shift completions
_files
- elif (( $+functions[_describe] )); then
- _describe 'jvn commands' completions
else
- compadd -a completions
+ local -a parsed_completions
+ for item in "${completions[@]}"; do
+ if [[ "$item" =~ '^([^$]+)\$\((.+)\)$' ]]; then
+ parsed_completions+=("${match[1]}:${match[2]}")
+ else
+ parsed_completions+=("$item")
+ fi
+ done
+
+ if (( $+functions[_describe] )); then
+ _describe 'jvn commands' parsed_completions
+ else
+ local -a simple_completions
+ for item in "${parsed_completions[@]}"; do
+ if [[ "$item" =~ '^([^:]+):(.+)$' ]]; then
+ simple_completions+=("${match[1]}")
+ else
+ simple_completions+=("$item")
+ fi
+ done
+ compadd -a simple_completions
+ fi
fi
fi
}
diff --git a/scripts/deploy/jvn.ps1 b/scripts/deploy/jvn_powershell.ps1
index 495ed43..495ed43 100644
--- a/scripts/deploy/jvn.ps1
+++ b/scripts/deploy/jvn_powershell.ps1
diff --git a/src/bin/jvn_comp.rs b/src/bin/jvn_comp.rs
index de52253..d0b258a 100644
--- a/src/bin/jvn_comp.rs
+++ b/src/bin/jvn_comp.rs
@@ -1,46 +1,69 @@
use std::{fs::OpenOptions, process::exit};
use clap::Parser;
+use cli_utils::env::locales::current_locales;
+use comp_system_macros::{file_suggest, suggest};
use env_logger::Target;
use jvcli::systems::{
cmd::_commands::jv_cmd_nodes,
comp::{
_comps::{jv_cmd_comp_nodes, match_comp},
- context::CompletionContext,
+ context::{CompletionContext, ShellFlag},
+ result::{CompletionResult, CompletionSuggestion},
},
render::renderer::jv_override_renderers,
};
#[cfg(debug_assertions)]
use log::debug;
+#[cfg(debug_assertions)]
use log::{LevelFilter, error, trace};
+use rust_i18n::{set_locale, t};
+
+rust_i18n::i18n!("resources/locales/jvn", fallback = "en");
+
+macro_rules! global_flags_suggest {
+ () => {
+ suggest!(
+ "--confirm" = t!("global_flag.confirm").trim(),
+ "-C" = t!("global_flag.confirm").trim(),
+ "--help" = t!("global_flag.help").trim(),
+ "-h" = t!("global_flag.help").trim(),
+ "--lang" = t!("global_flag.lang").trim(),
+ "--no-error-logs" = t!("global_flag.no_error_logs").trim(),
+ "--no-progress" = t!("global_flag.no_progress").trim(),
+ "--quiet" = t!("global_flag.quiet").trim(),
+ "-q" = t!("global_flag.quiet").trim(),
+ "--renderer" = t!("global_flag.renderer").trim(),
+ "--verbose" = t!("global_flag.verbose").trim(),
+ "-V" = t!("global_flag.verbose").trim(),
+ "--version" = t!("global_flag.version").trim(),
+ "-v" = t!("global_flag.version").trim(),
+ )
+ .into()
+ };
+}
-const GLOBAL_FLAGS: &[&str] = &[
- "--confirm",
- "-C",
- "--help",
- "-h",
- "--lang",
- "--no-error-logs",
- "--no-progress",
- "--quiet",
- "-q",
- "--renderer",
- "--verbose",
- "-V",
- "--version",
- "-v",
-];
-
-const LANGUAGES: [&str; 2] = [
- "en", // English
- "zh-CN", // 简体中文
-];
+macro_rules! language_suggest {
+ () => {
+ // Sort in A - Z order
+ suggest!(
+ // English
+ "en" = "English",
+ // Simplified Chinese
+ "zh-CN" = "简体中文"
+ )
+ .into()
+ };
+}
fn main() {
// If not in release mode, initialize env_logger to capture logs
#[cfg(debug_assertions)]
init_env_logger();
+ let lang = current_locales();
+ set_locale(&lang);
+
// Check if help flag is present in arguments
let args: Vec<String> = std::env::args().collect();
if args.iter().any(|arg| arg == "-h" || arg == "--help") {
@@ -68,6 +91,7 @@ fn main() {
}
Err(e) => {
// An error occurred, collecting information for output
+ #[cfg(debug_assertions)]
error!(
"Error: {}, origin=\"{}\"",
e,
@@ -81,42 +105,56 @@ fn main() {
#[cfg(debug_assertions)]
trace_ctx(&ctx);
- trace!("Try using specific completion");
- let specific_result = comp(&ctx);
- trace!("Using default completion");
+ #[cfg(debug_assertions)]
+ trace!("Generate specific completion");
+ let specific_result = specific_comp(&ctx);
+
+ #[cfg(debug_assertions)]
+ trace!("Generate default completion");
let default_result = default_comp(&ctx);
+ #[cfg(debug_assertions)]
+ trace!("specific_result: {}", specific_result.to_string());
+ #[cfg(debug_assertions)]
+ trace!("default_result: {}", default_result.to_string());
+
let combined_result = match (specific_result, default_result) {
- (None, None) => None,
- (Some(s), None) => Some(s),
- (None, Some(d)) => Some(d),
- (Some(mut s), Some(d)) => {
+ (CompletionResult::FileCompletion, CompletionResult::FileCompletion) => {
+ CompletionResult::file_comp()
+ }
+ (CompletionResult::Suggestions(s), CompletionResult::FileCompletion) => {
+ CompletionResult::Suggestions(s)
+ }
+ (CompletionResult::FileCompletion, CompletionResult::Suggestions(d)) => {
+ CompletionResult::Suggestions(d)
+ }
+ (CompletionResult::Suggestions(mut s), CompletionResult::Suggestions(d)) => {
s.extend(d);
- Some(s)
+ CompletionResult::Suggestions(s)
}
};
- handle_comp_result(&combined_result);
+ handle_comp_result(combined_result, &ctx);
}
-fn default_comp(ctx: &CompletionContext) -> Option<Vec<String>> {
+fn default_comp(ctx: &CompletionContext) -> CompletionResult {
if ctx.current_word.starts_with('-') {
- return Some(GLOBAL_FLAGS.iter().map(|s| s.to_string()).collect());
+ return global_flags_suggest!();
}
// Match and comp Override Renderers
if ctx.previous_word == "--renderer" {
- return Some(jv_override_renderers());
+ return jv_override_renderers().into();
}
if ctx.previous_word == "--lang" {
- return Some(LANGUAGES.iter().map(|s| s.to_string()).collect());
+ return language_suggest!();
}
- None
+ file_suggest!()
}
-fn comp(ctx: &CompletionContext) -> Option<Vec<String>> {
+fn specific_comp(ctx: &CompletionContext) -> CompletionResult {
let args: Vec<String> = ctx.all_words.iter().skip(1).cloned().collect();
let nodes = jv_cmd_comp_nodes();
let command = format!("{} ", args.join(" "));
@@ -137,23 +175,39 @@ fn comp(ctx: &CompletionContext) -> Option<Vec<String>> {
let match_node: Option<String> = match matching_nodes.len() {
0 => {
- if let Some(result) = try_comp_cmd_nodes(ctx) {
- return Some(result);
+ #[cfg(debug_assertions)]
+ trace!("No matching nodes found, trying command nodes");
+ let r = try_comp_cmd_nodes(ctx);
+ if r.is_suggestion() {
+ #[cfg(debug_assertions)]
+ trace!("try_comp_cmd_nodes returned suggestions");
+ return r;
}
+ #[cfg(debug_assertions)]
+ trace!("try_comp_cmd_nodes returned file completion");
// No matching node found
None
}
1 => {
// Single matching node found
+ #[cfg(debug_assertions)]
+ trace!("Single matching node found: {}", matching_nodes[0]);
Some(matching_nodes[0].clone())
}
_ => {
// Multiple matching nodes found
// Find the node with the longest length (most specific match)
- matching_nodes
+ #[cfg(debug_assertions)]
+ trace!("Multiple matching nodes found: {:?}", matching_nodes);
+ let longest_node = matching_nodes
.iter()
.max_by_key(|node| node.len())
- .map(|node| node.to_string())
+ .map(|node| node.to_string());
+ #[cfg(debug_assertions)]
+ if let Some(ref node) = longest_node {
+ trace!("Selected longest node: {}", node);
+ }
+ longest_node
}
};
@@ -163,17 +217,29 @@ fn comp(ctx: &CompletionContext) -> Option<Vec<String>> {
None => trace!("No completions matched."),
}
- let match_node = match_node?;
+ let match_node = match match_node {
+ Some(node) => node,
+ None => {
+ #[cfg(debug_assertions)]
+ trace!("No match node found, returning file completion");
+ return file_suggest!();
+ }
+ };
- match_comp(match_node, ctx.clone())
+ #[cfg(debug_assertions)]
+ trace!("Calling match_comp with node: {}", match_node);
+ let result = match_comp(match_node, ctx.clone());
+ #[cfg(debug_assertions)]
+ trace!("match_comp returned: {}", result.to_string());
+ result
}
-fn try_comp_cmd_nodes(ctx: &CompletionContext) -> Option<Vec<String>> {
+fn try_comp_cmd_nodes(ctx: &CompletionContext) -> CompletionResult {
let cmd_nodes = jv_cmd_nodes();
// If the current position is less than 1, do not perform completion
if ctx.word_index < 1 {
- return None;
+ return file_suggest!();
};
// Get the current input path
@@ -226,7 +292,7 @@ fn try_comp_cmd_nodes(ctx: &CompletionContext) -> Option<Vec<String>> {
"try_comp_cmd_nodes: current word suggestions = {:?}",
suggestions
);
- return Some(suggestions);
+ return suggestions.into();
}
}
@@ -279,28 +345,68 @@ fn try_comp_cmd_nodes(ctx: &CompletionContext) -> Option<Vec<String>> {
debug!("try_comp_cmd_nodes: suggestions = {:?}", suggestions);
if suggestions.is_empty() {
- None
+ file_suggest!()
} else {
- Some(suggestions)
+ suggestions.into()
}
}
-fn handle_comp_result(r: &Option<Vec<String>>) {
+fn handle_comp_result(r: CompletionResult, ctx: &CompletionContext) {
match r {
- Some(suggestions) => {
- suggestions
- .iter()
- .for_each(|suggest| println!("{}", suggest));
- exit(0)
- }
- None => {
- // Output "_file_" to notify the completion script to perform "file completion"
+ CompletionResult::FileCompletion => {
println!("_file_");
exit(0)
}
+ CompletionResult::Suggestions(suggestions) => match ctx.shell_flag {
+ ShellFlag::Zsh => print_suggest_with_description(suggestions),
+ ShellFlag::Fish => print_suggest_with_description_fish(suggestions),
+ _ => print_suggest(suggestions),
+ },
}
}
+fn print_suggest(mut suggestions: Vec<CompletionSuggestion>) {
+ suggestions.sort();
+ #[cfg(debug_assertions)]
+ trace!("print_suggest suggestions: {:?}", suggestions);
+ suggestions
+ .iter()
+ .for_each(|suggest| println!("{}", suggest.suggest));
+ exit(0)
+}
+
+fn print_suggest_with_description(mut suggestions: Vec<CompletionSuggestion>) {
+ suggestions.sort();
+ #[cfg(debug_assertions)]
+ trace!(
+ "print_suggest_with_description suggestions: {:?}",
+ suggestions
+ );
+ suggestions
+ .iter()
+ .for_each(|suggest| match &suggest.description {
+ Some(desc) => println!("{}$({})", suggest.suggest, desc),
+ None => println!("{}", suggest.suggest),
+ });
+ exit(0)
+}
+
+fn print_suggest_with_description_fish(mut suggestions: Vec<CompletionSuggestion>) {
+ suggestions.sort();
+ #[cfg(debug_assertions)]
+ trace!(
+ "print_suggest_with_description_fish suggestions: {:?}",
+ suggestions
+ );
+ suggestions
+ .iter()
+ .for_each(|suggest| match &suggest.description {
+ Some(desc) => println!("{}\t{}", suggest.suggest, desc),
+ None => println!("{}", suggest.suggest),
+ });
+ exit(0)
+}
+
#[cfg(debug_assertions)]
fn trace_ctx(ctx: &CompletionContext) {
log::trace!("command_line={}", ctx.command_line);
diff --git a/src/cmds/comp/helpdoc.rs b/src/cmds/comp/helpdoc.rs
index 7f07cad..423e2bf 100644
--- a/src/cmds/comp/helpdoc.rs
+++ b/src/cmds/comp/helpdoc.rs
@@ -1,13 +1,17 @@
-use crate::systems::{comp::context::CompletionContext, helpdoc};
+use comp_system_macros::file_suggest;
-pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> {
+use crate::systems::{
+ comp::{context::CompletionContext, result::CompletionResult},
+ helpdoc,
+};
+
+pub fn comp(ctx: CompletionContext) -> CompletionResult {
if ctx.previous_word == "helpdoc" {
- return Some(
- helpdoc::get_helpdoc_list()
- .iter()
- .map(|s| s.to_string())
- .collect(),
- );
+ return helpdoc::get_helpdoc_list()
+ .iter()
+ .map(|s| s.to_string())
+ .collect::<Vec<String>>()
+ .into();
}
- None
+ file_suggest!()
}
diff --git a/src/cmds/comp/sheetdump.rs b/src/cmds/comp/sheetdump.rs
index 3528cf3..e3105c0 100644
--- a/src/cmds/comp/sheetdump.rs
+++ b/src/cmds/comp/sheetdump.rs
@@ -1,11 +1,15 @@
-use cli_utils::string_vec;
+use comp_system_macros::{file_suggest, suggest};
+use rust_i18n::t;
-use crate::systems::comp::context::CompletionContext;
+use crate::systems::comp::{context::CompletionContext, result::CompletionResult};
-pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> {
+pub fn comp(ctx: CompletionContext) -> CompletionResult {
if ctx.current_word.starts_with('-') {
- return Some(string_vec!["--no-sort", "--no-pretty"]);
+ return suggest!(
+ "--no-sort" = t!("sheetdump.comp.no_sort").trim(),
+ "--no-pretty" = t!("sheetdump.comp.no_pretty").trim()
+ )
+ .into();
}
-
- None
+ file_suggest!()
}
diff --git a/src/cmds/comp/sheetedit.rs b/src/cmds/comp/sheetedit.rs
index d210028..1ebf63d 100644
--- a/src/cmds/comp/sheetedit.rs
+++ b/src/cmds/comp/sheetedit.rs
@@ -1,15 +1,20 @@
-use cli_utils::string_vec;
+use comp_system_macros::{file_suggest, suggest};
+use rust_i18n::t;
-use crate::systems::comp::context::CompletionContext;
+use crate::systems::comp::{context::CompletionContext, result::CompletionResult};
-pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> {
+pub fn comp(ctx: CompletionContext) -> CompletionResult {
if ctx.current_word.starts_with('-') {
- return Some(string_vec!["-e", "--editor"]);
+ return suggest!(
+ "-e" = t!("sheetedit.comp.editor").trim(),
+ "--editor" = t!("sheetedit.comp.editor").trim(),
+ )
+ .into();
}
if ctx.previous_word == "-e" || ctx.previous_word == "--editor" {
- return Some(vec![]);
+ return suggest!().into();
}
- None
+ file_suggest!()
}
diff --git a/src/cmds/comp/version.rs b/src/cmds/comp/version.rs
index 1460214..2c6b674 100644
--- a/src/cmds/comp/version.rs
+++ b/src/cmds/comp/version.rs
@@ -1,11 +1,16 @@
-use cli_utils::string_vec;
+use comp_system_macros::{file_suggest, suggest};
+use rust_i18n::t;
-use crate::systems::comp::context::CompletionContext;
+use crate::systems::comp::{context::CompletionContext, result::CompletionResult};
-pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> {
+pub fn comp(ctx: CompletionContext) -> CompletionResult {
if ctx.current_word.starts_with('-') {
- return Some(string_vec!["-c", "--with-compile-info", "--no-banner"]);
+ return suggest!(
+ "-c" = t!("version.comp.with_compile_info").trim(),
+ "--with-compile-info" = t!("version.comp.with_compile_info").trim(),
+ "--no-banner" = t!("version.comp.no_banner").trim()
+ )
+ .into();
}
-
- None
+ file_suggest!()
}
diff --git a/src/cmds/comp/workspace_alias.rs b/src/cmds/comp/workspace_alias.rs
index a8ac495..cd39c9d 100644
--- a/src/cmds/comp/workspace_alias.rs
+++ b/src/cmds/comp/workspace_alias.rs
@@ -1,17 +1,25 @@
-use cli_utils::string_vec;
+use comp_system_macros::{file_suggest, suggest};
+use rust_i18n::t;
-use crate::systems::comp::context::CompletionContext;
+use crate::systems::comp::{context::CompletionContext, result::CompletionResult};
-pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> {
+pub fn comp(ctx: CompletionContext) -> CompletionResult {
if ctx.current_word.starts_with('-') {
- return Some(string_vec![
- "-i", "--insert", "-Q", "--query", "-e", "--erase", "--to",
- ]);
+ return suggest!(
+ "-i" = t!("workspace_alias.comp.insert").trim(),
+ "--insert" = t!("workspace_alias.comp.insert").trim(),
+ "-Q" = t!("workspace_alias.comp.query").trim(),
+ "--query" = t!("workspace_alias.comp.query").trim(),
+ "-e" = t!("workspace_alias.comp.erase").trim(),
+ "--erase" = t!("workspace_alias.comp.erase").trim(),
+ "--to" = t!("workspace_alias.comp.to").trim()
+ )
+ .into();
}
if ctx.previous_word == "--to" {
- return Some(vec![]);
+ return suggest!().into();
}
- None
+ file_suggest!()
}
diff --git a/src/cmds/comp/workspace_sheet.rs b/src/cmds/comp/workspace_sheet.rs
index 3162442..8318fbe 100644
--- a/src/cmds/comp/workspace_sheet.rs
+++ b/src/cmds/comp/workspace_sheet.rs
@@ -1,25 +1,39 @@
-use crate::systems::comp::context::CompletionContext;
-use cli_utils::string_vec;
+use comp_system_macros::{file_suggest, suggest};
use just_enough_vcs::system::workspace::workspace::manager::WorkspaceManager;
+use rust_i18n::t;
-pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> {
+use crate::systems::comp::{context::CompletionContext, result::CompletionResult};
+
+pub fn comp(ctx: CompletionContext) -> CompletionResult {
if ctx.current_word.starts_with('-') {
- return Some(string_vec![
- "-A",
- "--list-all",
- "-p",
- "--print-path",
- "-n",
- "--new",
- "-d",
- "--delete",
- ]);
+ return suggest!(
+ "-A" = t!("workspace_sheet.comp.list_all").trim(),
+ "--list-all" = t!("workspace_sheet.comp.list_all").trim(),
+ "-p" = t!("workspace_sheet.comp.print_path").trim(),
+ "--print-path" = t!("workspace.sheet.comp.print_path").trim(),
+ "-n" = t!("workspace_sheet.comp.new").trim(),
+ "--new" = t!("workspace_sheet.comp.new").trim(),
+ "-d" = t!("workspace_sheet.comp.delete").trim(),
+ "--delete" = t!("workspace_sheet.comp.delete").trim()
+ )
+ .into();
}
if ctx.previous_word == "--new" || ctx.previous_word == "-n" {
- return Some(vec![]);
+ return suggest!().into();
+ }
+
+ if ctx.previous_word == "--list-all"
+ || ctx.previous_word == "-A"
+ || ctx.previous_word == "--print-path"
+ || ctx.previous_word == "-p"
+ || ctx.previous_word == "--delete"
+ || ctx.previous_word == "-d"
+ {
+ let rt = tokio::runtime::Runtime::new().unwrap();
+ let names = rt.block_on(WorkspaceManager::new().list_sheet_names());
+ return names.into();
}
- let rt = tokio::runtime::Runtime::new().unwrap();
- Some(rt.block_on(WorkspaceManager::new().list_sheet_names()))
+ file_suggest!()
}
diff --git a/src/systems/comp.rs b/src/systems/comp.rs
index c7c6577..077fb7f 100644
--- a/src/systems/comp.rs
+++ b/src/systems/comp.rs
@@ -1,2 +1,3 @@
pub mod _comps;
pub mod context;
+pub mod result;
diff --git a/src/systems/comp/result.rs b/src/systems/comp/result.rs
new file mode 100644
index 0000000..e64021d
--- /dev/null
+++ b/src/systems/comp/result.rs
@@ -0,0 +1,205 @@
+pub enum CompletionResult {
+ FileCompletion,
+ Suggestions(Vec<CompletionSuggestion>),
+}
+
+impl CompletionResult {
+ /// Creates a `CompletionResult` representing file completion.
+ pub fn file_comp() -> Self {
+ CompletionResult::FileCompletion
+ }
+
+ /// Creates a `CompletionResult` with an empty suggestions list.
+ pub fn empty_comp() -> CompletionSuggestInsert {
+ CompletionSuggestInsert {
+ suggestions: Vec::new(),
+ }
+ }
+
+ /// Returns `true` if this is a `FileCompletion` variant.
+ pub fn is_file(&self) -> bool {
+ matches!(self, Self::FileCompletion)
+ }
+
+ /// Returns `true` if this is a `Suggestions` variant.
+ pub fn is_suggestion(&self) -> bool {
+ matches!(self, Self::Suggestions(_))
+ }
+
+ /// Returns a reference to the suggestions vector if this is a `Suggestions` variant,
+ /// otherwise returns `None`.
+ pub fn suggestion(&self) -> Option<&Vec<CompletionSuggestion>> {
+ match self {
+ Self::FileCompletion => None,
+ Self::Suggestions(v) => Some(v),
+ }
+ }
+
+ /// Returns the unit value if this is a `FileCompletion` variant, otherwise panics.
+ pub fn unwrap_file(&self) {
+ match self {
+ Self::FileCompletion => (),
+ Self::Suggestions(_) => {
+ panic!("called `CompletionResult::unwrap_file()` on a `Suggestions` value")
+ }
+ }
+ }
+
+ /// Returns a reference to the suggestions vector if this is a `Suggestions` variant,
+ /// otherwise panics.
+ pub fn unwrap_suggestion(&self) -> &Vec<CompletionSuggestion> {
+ match self {
+ Self::FileCompletion => {
+ panic!("called `CompletionResult::unwrap_suggestion()` on a `FileCompletion` value")
+ }
+ Self::Suggestions(v) => v,
+ }
+ }
+
+ /// Returns the unit value if this is a `FileCompletion` variant, otherwise returns `default`.
+ pub fn unwrap_file_or(&self, default: ()) {
+ match self {
+ Self::FileCompletion => (),
+ Self::Suggestions(_) => default,
+ }
+ }
+
+ /// Returns a reference to the suggestions vector if this is a `Suggestions` variant,
+ /// otherwise returns `default`.
+ pub fn unwrap_suggestion_or<'a>(
+ &'a self,
+ default: &'a Vec<CompletionSuggestion>,
+ ) -> &'a Vec<CompletionSuggestion> {
+ match self {
+ Self::FileCompletion => default,
+ Self::Suggestions(v) => v,
+ }
+ }
+
+ /// Returns the unit value if this is a `FileCompletion` variant, otherwise calls `f`.
+ pub fn unwrap_file_or_else<F>(&self, f: F)
+ where
+ F: FnOnce(),
+ {
+ match self {
+ Self::FileCompletion => (),
+ Self::Suggestions(_) => f(),
+ }
+ }
+
+ /// Returns a reference to the suggestions vector if this is a `Suggestions` variant,
+ /// otherwise calls `f`.
+ pub fn unwrap_suggestion_or_else<'a, F>(&'a self, f: F) -> &'a Vec<CompletionSuggestion>
+ where
+ F: FnOnce() -> &'a Vec<CompletionSuggestion>,
+ {
+ match self {
+ Self::FileCompletion => f(),
+ Self::Suggestions(v) => v,
+ }
+ }
+}
+
+pub struct CompletionSuggestInsert {
+ suggestions: Vec<CompletionSuggestion>,
+}
+
+impl CompletionSuggestInsert {
+ /// Adds a suggestion with the given text and no description.
+ pub fn with_suggest<S: Into<String>>(mut self, suggest: S) -> Self {
+ self.suggestions.push(CompletionSuggestion {
+ suggest: suggest.into(),
+ description: None,
+ });
+ self
+ }
+
+ /// Adds a suggestion with the given text and description.
+ pub fn with_suggest_desc<S: Into<String>, D: Into<String>>(
+ mut self,
+ suggest: S,
+ description: D,
+ ) -> Self {
+ self.suggestions.push(CompletionSuggestion {
+ suggest: suggest.into(),
+ description: Some(description.into()),
+ });
+ self
+ }
+}
+
+impl From<CompletionSuggestInsert> for CompletionResult {
+ fn from(insert: CompletionSuggestInsert) -> Self {
+ CompletionResult::Suggestions(insert.suggestions)
+ }
+}
+
+impl From<Vec<String>> for CompletionResult {
+ fn from(strings: Vec<String>) -> Self {
+ CompletionResult::Suggestions(
+ strings
+ .into_iter()
+ .map(|s| CompletionSuggestion {
+ suggest: s,
+ description: None,
+ })
+ .collect(),
+ )
+ }
+}
+
+impl std::fmt::Display for CompletionResult {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ CompletionResult::FileCompletion => write!(f, "_file_"),
+ CompletionResult::Suggestions(suggestions) => {
+ let suggestions_str: Vec<String> =
+ suggestions.iter().map(|s| s.suggest.clone()).collect();
+ write!(f, "{}", suggestions_str.join(", "))
+ }
+ }
+ }
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+pub struct CompletionSuggestion {
+ pub suggest: String,
+ pub description: Option<String>,
+}
+
+impl std::ops::Deref for CompletionSuggestion {
+ type Target = String;
+
+ fn deref(&self) -> &Self::Target {
+ &self.suggest
+ }
+}
+
+impl std::ops::DerefMut for CompletionSuggestion {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.suggest
+ }
+}
+
+impl AsRef<str> for CompletionSuggestion {
+ fn as_ref(&self) -> &str {
+ &self.suggest
+ }
+}
+
+impl PartialOrd for CompletionSuggestion {
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for CompletionSuggestion {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ match (&self.description, &other.description) {
+ (None, None) => self.suggest.cmp(&other.suggest),
+ (Some(_), None) => std::cmp::Ordering::Less,
+ (None, Some(_)) => std::cmp::Ordering::Greater,
+ (Some(_), Some(_)) => self.suggest.cmp(&other.suggest),
+ }
+ }
+}
diff --git a/templates/_comps.rs.template b/templates/_comps.rs.template
index b7f10db..9561243 100644
--- a/templates/_comps.rs.template
+++ b/templates/_comps.rs.template
@@ -1,11 +1,12 @@
// Auto generated by build.rs
-use crate::systems::comp::context::CompletionContext;
+use comp_system_macros::file_suggest;
+use crate::systems::comp::{context::CompletionContext, result::CompletionResult};
-pub fn match_comp(node: String, ctx: CompletionContext) -> Option<Vec<String>> {
+pub fn match_comp(node: String, ctx: CompletionContext) -> CompletionResult {
let node_str = node.as_str();
match node_str {
>>>>>>>>>> comp_match_arms
- _ => None,
+ _ => file_suggest!(),
}
}