summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
9 files changed, 460 insertions, 108 deletions
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),
+ }
+ }
+}