diff options
| -rw-r--r-- | .cargo/registry.toml | 3 | ||||
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | build.rs | 18 | ||||
| -rw-r--r-- | gen.rs | 1 | ||||
| -rw-r--r-- | gen/constants.rs | 7 | ||||
| -rw-r--r-- | gen/gen_completions_entries.rs | 79 | ||||
| -rw-r--r-- | gen/gen_override_renderer.rs | 64 | ||||
| -rw-r--r-- | scripts/deploy/completions/bash.sh | 8 | ||||
| -rw-r--r-- | scripts/deploy/completions/fish.fish | 8 | ||||
| -rw-r--r-- | scripts/deploy/completions/powershell.ps1 | 8 | ||||
| -rw-r--r-- | scripts/deploy/completions/zsh.sh | 8 | ||||
| -rw-r--r-- | src/bin/jvn_comp.rs | 245 | ||||
| -rw-r--r-- | src/cmds.rs | 1 | ||||
| -rw-r--r-- | src/cmds/comp/helpdoc.rs | 13 | ||||
| -rw-r--r-- | src/systems.rs | 1 | ||||
| -rw-r--r-- | src/systems/comp.rs | 2 | ||||
| -rw-r--r-- | src/systems/comp/context.rs | 33 | ||||
| -rw-r--r-- | src/systems/render/renderer.rs | 2 | ||||
| -rw-r--r-- | templates/_comps.rs.template | 26 | ||||
| -rw-r--r-- | templates/_override_renderers.rs.template | 10 |
20 files changed, 487 insertions, 51 deletions
diff --git a/.cargo/registry.toml b/.cargo/registry.toml index d019c45..4f3d64b 100644 --- a/.cargo/registry.toml +++ b/.cargo/registry.toml @@ -54,6 +54,9 @@ path = "src/cmds/cmd.rs" [collect.collects] path = "src/cmds/collect.rs" +[collect.completions] +path = "src/cmds/comp.rs" + [collect.converters] path = "src/cmds/converter.rs" @@ -29,6 +29,7 @@ _*.rs /src/cmds/arg.rs /src/cmds/cmd.rs /src/cmds/collect.rs +/src/cmds/comp.rs /src/cmds/converter.rs /src/cmds/in.rs /src/cmds/out.rs @@ -2,9 +2,13 @@ use std::env; use std::path::PathBuf; use crate::r#gen::{ - gen_commands_file::generate_commands_file, gen_compile_info::generate_compile_info, - gen_iscc_script::generate_installer_script, gen_mod_files::generate_collect_files, - gen_override_renderer::generate_override_renderer, gen_renderers_file::generate_renderers_file, + gen_commands_file::generate_commands_file, + gen_compile_info::generate_compile_info, + gen_completions_entries::generate_completions_file, + gen_iscc_script::generate_installer_script, + gen_mod_files::generate_collect_files, + gen_override_renderer::{generate_override_renderer, generate_override_renderers_list}, + gen_renderers_file::generate_renderers_file, gen_specific_renderer::generate_specific_renderer, }; @@ -31,6 +35,10 @@ async fn main() { }), tokio::spawn({ let repo_root = repo_root.clone(); + async move { generate_completions_file(&repo_root).await } + }), + tokio::spawn({ + let repo_root = repo_root.clone(); async move { generate_collect_files(&repo_root).await } }), tokio::spawn({ @@ -39,6 +47,10 @@ async fn main() { }), tokio::spawn({ let repo_root = repo_root.clone(); + async move { generate_override_renderers_list(&repo_root).await } + }), + tokio::spawn({ + let repo_root = repo_root.clone(); async move { generate_specific_renderer(&repo_root).await } }), tokio::spawn({ @@ -2,6 +2,7 @@ pub mod constants; pub mod env; pub mod gen_commands_file; pub mod gen_compile_info; +pub mod gen_completions_entries; pub mod gen_iscc_script; pub mod gen_mod_files; pub mod gen_override_renderer; diff --git a/gen/constants.rs b/gen/constants.rs index e26317f..140743d 100644 --- a/gen/constants.rs +++ b/gen/constants.rs @@ -1,4 +1,5 @@ pub const COMMANDS_PATH: &str = "./src/cmds/cmd/"; +pub const COMPLETIONS_PATH: &str = "./src/cmds/comp/"; pub const RENDERERS_PATH: &str = "./src/cmds/renderer/"; pub const COMPILE_INFO_RS_TEMPLATE: &str = "./templates/compile_info.rs.template"; @@ -10,6 +11,9 @@ pub const SETUP_JV_CLI_ISS: &str = "./scripts/setup/windows/setup_jv_cli.iss"; pub const COMMAND_LIST_TEMPLATE: &str = "./templates/_commands.rs.template"; pub const COMMAND_LIST: &str = "./src/systems/cmd/_commands.rs"; +pub const COMPLETIONS_TEMPLATE: &str = "./templates/_comps.rs.template"; +pub const COMPLETIONS: &str = "./src/systems/comp/_comps.rs"; + pub const OVERRIDE_RENDERER_DISPATCHER_TEMPLATE: &str = "./templates/_override_renderer_dispatcher.rs.template"; pub const OVERRIDE_RENDERER_DISPATCHER: &str = @@ -19,6 +23,9 @@ pub const OVERRIDE_RENDERER_ENTRY_TEMPLATE: &str = "./templates/_override_renderer_entry.rs.template"; pub const OVERRIDE_RENDERER_ENTRY: &str = "./src/systems/render/_override_renderer_entry.rs"; +pub const OVERRIDE_RENDERERS_TEMPLATE: &str = "./templates/_override_renderers.rs.template"; +pub const OVERRIDE_RENDERERS: &str = "./src/systems/render/_override_renderers.rs"; + pub const SPECIFIC_RENDERER_MATCHING_TEMPLATE: &str = "./templates/_specific_renderer_matching.rs.template"; pub const SPECIFIC_RENDERER_MATCHING: &str = "./src/systems/render/_specific_renderer_matching.rs"; diff --git a/gen/gen_completions_entries.rs b/gen/gen_completions_entries.rs new file mode 100644 index 0000000..0e030e6 --- /dev/null +++ b/gen/gen_completions_entries.rs @@ -0,0 +1,79 @@ +use just_template::{Template, tmpl}; +use std::path::PathBuf; + +use crate::r#gen::constants::{COMPLETIONS, COMPLETIONS_PATH, COMPLETIONS_TEMPLATE}; + +/// Generate completions file from comp directory using just_template +pub async fn generate_completions_file(repo_root: &PathBuf) { + let template_path = repo_root.join(COMPLETIONS_TEMPLATE); + let output_path = repo_root.join(COMPLETIONS); + let comps_dir = repo_root.join(COMPLETIONS_PATH); + + // Read the template + let template_content = tokio::fs::read_to_string(&template_path).await.unwrap(); + + // Collect all completion files + let mut all_completions: Vec<(String, String)> = Vec::new(); + let mut all_nodes: Vec<String> = Vec::new(); + + if comps_dir.exists() && comps_dir.is_dir() { + let mut entries = tokio::fs::read_dir(&comps_dir).await.unwrap(); + while let Some(entry) = entries.next_entry().await.unwrap() { + let path = entry.path(); + + if !path.is_file() { + continue; + } + + let extension = match path.extension() { + Some(ext) => ext, + None => continue, + }; + + if extension != "rs" { + continue; + } + + let file_name = match path.file_stem().and_then(|s| s.to_str()) { + Some(name) => name, + None => continue, + }; + + let node_name = just_fmt::lower_case!(file_name); + + all_completions.push((file_name.to_string(), node_name.clone())); + all_nodes.push(node_name); + } + } + + // Create template + let mut template = Template::from(template_content); + + // Generate match arms for each completion + for (comp_name, node_name) in &all_completions { + tmpl!(template += { + comp_match_arms { + (comp_name = comp_name, comp_node_name = node_name) + } + }); + } + + for node in all_nodes { + tmpl!(template += { + comp_node_name { + (comp_node_name = just_fmt::lower_case!(node)) + } + }); + } + + // Expand the template + let final_content = template.expand().unwrap(); + + // Write the generated code + tokio::fs::write(output_path, final_content).await.unwrap(); + + println!( + "Generated completions file with {} completions using just_template", + all_completions.len() + ); +} diff --git a/gen/gen_override_renderer.rs b/gen/gen_override_renderer.rs index 1e67be4..2a8ba37 100644 --- a/gen/gen_override_renderer.rs +++ b/gen/gen_override_renderer.rs @@ -5,7 +5,10 @@ use regex::Regex; use tokio::fs; use crate::r#gen::{ - constants::{COMMANDS_PATH, OVERRIDE_RENDERER_ENTRY, OVERRIDE_RENDERER_ENTRY_TEMPLATE}, + constants::{ + COMMANDS_PATH, OVERRIDE_RENDERER_ENTRY, OVERRIDE_RENDERER_ENTRY_TEMPLATE, + OVERRIDE_RENDERERS, OVERRIDE_RENDERERS_TEMPLATE, REGISTRY_TOML, + }, resolve_types::resolve_type_paths, }; @@ -41,6 +44,65 @@ pub async fn generate_override_renderer(repo_root: &PathBuf) { ); } +/// Generate override renderers list file from Registry.toml configuration using just_template +pub async fn generate_override_renderers_list(repo_root: &PathBuf) { + let template_path = repo_root.join(OVERRIDE_RENDERERS_TEMPLATE); + let output_path = repo_root.join(OVERRIDE_RENDERERS); + let config_path = repo_root.join(REGISTRY_TOML); + + // Read the template + let template_content = tokio::fs::read_to_string(&template_path).await.unwrap(); + + // Read and parse the TOML configuration + let config_content = tokio::fs::read_to_string(&config_path).await.unwrap(); + let config: toml::Value = toml::from_str(&config_content).unwrap(); + + // Collect all renderer names + let mut renderer_names = Vec::new(); + + let Some(table) = config.as_table() else { + return; + }; + let Some(renderer_table) = table.get("renderer") else { + return; + }; + let Some(renderer_table) = renderer_table.as_table() else { + return; + }; + + for (_, renderer_value) in renderer_table { + let Some(renderer_config) = renderer_value.as_table() else { + continue; + }; + let Some(name) = renderer_config.get("name").and_then(|v| v.as_str()) else { + continue; + }; + + renderer_names.push(name.to_string()); + } + + // Create template + let mut template = Template::from(template_content); + + for renderer_name in &renderer_names { + tmpl!(template += { + renderer { + (renderer_name = renderer_name) + } + }); + } + + let final_content = template.expand().unwrap(); + + // Write the generated code + tokio::fs::write(output_path, final_content).await.unwrap(); + + println!( + "Generated override renderers list with {} renderers using just_template", + renderer_names.len() + ); +} + pub async fn collect_all_possible_types(dir: &PathBuf) -> HashSet<String> { let mut all_types = HashSet::new(); let mut dirs_to_visit = vec![dir.clone()]; diff --git a/scripts/deploy/completions/bash.sh b/scripts/deploy/completions/bash.sh index 418105d..e1b2a31 100644 --- a/scripts/deploy/completions/bash.sh +++ b/scripts/deploy/completions/bash.sh @@ -40,13 +40,13 @@ _jvn_bash_completion() { fi local args=( - -f "$COMP_LINE" + -f "${COMP_LINE//-/^}" -C "$COMP_POINT" - -w "$cur" - -p "$prev" + -w "${cur//-/^}" + -p "${prev//-/^}" -c "${words[0]}" -i "$cword" - -a "${words[@]}" + -a "${words[@]//-/^}" ) local suggestions diff --git a/scripts/deploy/completions/fish.fish b/scripts/deploy/completions/fish.fish index 2904495..558b602 100644 --- a/scripts/deploy/completions/fish.fish +++ b/scripts/deploy/completions/fish.fish @@ -24,13 +24,13 @@ function __jvn_fish_complete end set -l args \ - -f "$buffer" \ + -f (string replace -a - ^ -- "$buffer") \ -C "$cursor" \ - -w "$current_word" \ - -p "$previous_word" \ + -w (string replace -a - ^ -- "$current_word") \ + -p (string replace -a - ^ -- "$previous_word") \ -c "$cmdline[1]" \ -i "$word_index" \ - -a $cmdline + -a (string replace -a - ^ -- "$cmdline") set -l output (jvn_comp $args 2>/dev/null) if test "$output" = "_file_" diff --git a/scripts/deploy/completions/powershell.ps1 b/scripts/deploy/completions/powershell.ps1 index 0c3cddc..ec91038 100644 --- a/scripts/deploy/completions/powershell.ps1 +++ b/scripts/deploy/completions/powershell.ps1 @@ -18,13 +18,13 @@ Register-ArgumentCompleter -CommandName jvn -ScriptBlock { } $args = @( - "-f", $line + "-f", ($line -replace '-', '^') "-C", $cursorPosition.ToString() - "-w", $wordToComplete - "-p", if ($words.Count -gt 1) { $words[-2] } else { "" } + "-w", ($wordToComplete -replace '-', '^') + "-p", (if ($words.Count -gt 1) { $words[-2] } else { "" }) -replace '-', '^' "-c", $commandName "-i", ($words.Count - 1).ToString() - "-a", $words + "-a", ($words | ForEach-Object { $_ -replace '-', '^' }) ) $suggestions = jvn_comp $args 2>$null diff --git a/scripts/deploy/completions/zsh.sh b/scripts/deploy/completions/zsh.sh index 2b2a96b..dd1ff38 100644 --- a/scripts/deploy/completions/zsh.sh +++ b/scripts/deploy/completions/zsh.sh @@ -15,13 +15,13 @@ _jvn_completion() { fi args=( - -f "$buffer" + -f "${buffer//-/^}" -C "$cursor" - -w "$current_word" - -p "$previous_word" + -w "${current_word//-/^}" + -p "${previous_word//-/^}" -c "$command_name" -i "$word_index" - -a "${words[@]}" + -a "${(@)words//-/^}" ) suggestions=$(jvn_comp "${args[@]}" 2>/dev/null) diff --git a/src/bin/jvn_comp.rs b/src/bin/jvn_comp.rs index d00916c..30a9f81 100644 --- a/src/bin/jvn_comp.rs +++ b/src/bin/jvn_comp.rs @@ -1,54 +1,237 @@ +use std::{fs::OpenOptions, process::exit}; + use clap::Parser; +use env_logger::Target; +use just_enough_vcs_cli::systems::{ + cmd::_commands::jv_cmd_nodes, + comp::{ + _comps::{jv_cmd_comp_nodes, match_comp}, + context::CompletionContext, + }, + render::renderer::jv_override_renderers, +}; +#[cfg(debug_assertions)] +use log::debug; +use log::{LevelFilter, error, trace}; + +fn main() { + // If not in release mode, initialize env_logger to capture logs + #[cfg(debug_assertions)] + init_env_logger(); + + // Get context parameters from clap + let ctx = match CompletionContext::try_parse() { + Ok(args) => CompletionContext { + // In completion scripts, "-" is replaced with "^", need to convert back here + command_line: args.command_line.replace('^', "-"), + cursor_position: args.cursor_position, + current_word: args.current_word.replace('^', "-"), + previous_word: args.previous_word.replace('^', "-"), + command_name: args.command_name.replace('^', "-"), + word_index: args.word_index, + all_words: args.all_words.iter().map(|w| w.replace('^', "-")).collect(), + }, + Err(e) => { + // An error occurred, collecting information for output + error!( + "Error: {}, origin=\"{}\"", + e, + std::env::args().collect::<Vec<String>>().join(" ") + ); + std::process::exit(1); + } + }; + + // Trace context information + #[cfg(debug_assertions)] + trace_ctx(&ctx); + + // Perform pre-completion for common flags; + // if completion fails, start matching command nodes for completion + let result = pre_comp(&ctx); + if let Some(suggestions) = result { + handle_comp_result(&Some(suggestions)); + } else { + trace!("Using specific completion"); + let result = comp(ctx); + handle_comp_result(&result); + } +} + +fn pre_comp(ctx: &CompletionContext) -> Option<Vec<String>> { + // Match and comp Override Renderers + if ctx.previous_word == "--renderer" { + return Some(jv_override_renderers()); + } -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -struct CompletionContext { - /// The full command line - #[arg(short = 'f', long)] - command_line: String, + None +} + +fn comp(ctx: CompletionContext) -> Option<Vec<String>> { + let args: Vec<String> = ctx.all_words.iter().skip(1).cloned().collect(); + let nodes = jv_cmd_comp_nodes(); + let command = format!("{} ", args.join(" ")); - /// Cursor position - #[arg(short = 'C', long)] - cursor_position: usize, + #[cfg(debug_assertions)] + debug!("Arguments: `{}`", command); - /// Current word - #[arg(short = 'w', long)] - current_word: String, + // Find all nodes that match the command prefix + let matching_nodes: Vec<&String> = nodes + .iter() + .filter(|node| { + let matches = command.starts_with(&format!("{} ", node)); + #[cfg(debug_assertions)] + debug!("Checking node '{}': matches = {}", node, matches); + matches + }) + .collect(); - /// Previous word - #[arg(short = 'p', long)] - previous_word: String, + let match_node: Option<String> = match matching_nodes.len() { + 0 => { + if let Some(result) = try_comp_cmd_nodes(&ctx) { + return Some(result); + } + // No matching node found + None + } + 1 => { + // Single matching node found + Some(matching_nodes[0].clone()) + } + _ => { + // Multiple matching nodes found + // Find the node with the longest length (most specific match) + matching_nodes + .iter() + .max_by_key(|node| node.len()) + .map(|node| node.to_string()) + } + }; - /// Command name - #[arg(short = 'c', long)] - command_name: String, + #[cfg(debug_assertions)] + match &match_node { + Some(node) => trace!("Matched `{}`", node), + None => trace!("No competions matched."), + } - /// Word index - #[arg(short = 'i', long)] - word_index: usize, + let Some(match_node) = match_node else { + return None; + }; - /// All words - #[arg(short = 'a', long, num_args = 1..)] - all_words: Vec<String>, + match_comp(match_node, ctx) } -fn main() { - let args = match CompletionContext::try_parse() { - Ok(args) => args, - Err(_) => std::process::exit(1), +fn try_comp_cmd_nodes(ctx: &CompletionContext) -> Option<Vec<String>> { + 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; }; - match comp(args) { + + // Get the current input path + let input_path: Vec<&str> = ctx.all_words[1..ctx.word_index] + .iter() + .filter(|s| !s.is_empty()) + .map(|s| s.as_str()) + .collect(); + + #[cfg(debug_assertions)] + debug!( + "try_comp_cmd_nodes: input_path = {:?}, word_index = {}, all_words = {:?}", + input_path, ctx.word_index, ctx.all_words + ); + + // Filter command nodes that match the input path + let mut suggestions = Vec::new(); + + for node in cmd_nodes { + let node_parts: Vec<&str> = node.split(' ').collect(); + + #[cfg(debug_assertions)] + debug!("Checking node: '{}', parts: {:?}", node, node_parts); + + // If input path is longer than node parts, skip + if input_path.len() > node_parts.len() { + continue; + } + + // Check if input path matches the beginning of node parts + let mut matches = true; + for i in 0..input_path.len() { + if i >= node_parts.len() || input_path[i] != node_parts[i] { + matches = false; + break; + } + } + + if matches && input_path.len() < node_parts.len() { + // Add the next part as suggestion + suggestions.push(node_parts[input_path.len()].to_string()); + } + } + + // Remove duplicates and sort + suggestions.sort(); + suggestions.dedup(); + + #[cfg(debug_assertions)] + debug!("try_comp_cmd_nodes: suggestions = {:?}", suggestions); + + if suggestions.is_empty() { + None + } else { + Some(suggestions) + } +} + +fn handle_comp_result(r: &Option<Vec<String>>) { + 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" println!("_file_"); + exit(0) } } } -fn comp(_args: CompletionContext) -> Option<Vec<String>> { - None +#[cfg(debug_assertions)] +fn trace_ctx(ctx: &CompletionContext) { + log::trace!("command_line={}", ctx.command_line); + log::trace!("cursor_position={}", ctx.cursor_position); + log::trace!("current_word={}", ctx.current_word); + log::trace!("previous_word={}", ctx.previous_word); + log::trace!("command_name={}", ctx.command_name); + log::trace!("word_index={}", ctx.word_index); + log::trace!("all_words={:?}", ctx.all_words); +} + +#[cfg(debug_assertions)] +fn init_env_logger() { + let mut log_path = std::env::current_exe() + .expect("Failed to get current executable path") + .parent() + .expect("Failed to get parent directory") + .to_path_buf(); + log_path.push("../../jv_comp_log.txt"); + + // Only initialize logger if log file exists + if log_path.exists() { + let log_file = OpenOptions::new() + .create(true) + .append(true) + .open(&log_path) + .expect("Failed to open log file"); + + env_logger::Builder::new() + .filter_level(LevelFilter::Trace) + .target(Target::Pipe(Box::new(log_file))) + .init(); + } } diff --git a/src/cmds.rs b/src/cmds.rs index d3c7a27..aa41aec 100644 --- a/src/cmds.rs +++ b/src/cmds.rs @@ -1,6 +1,7 @@ pub mod arg; pub mod cmd; pub mod collect; +pub mod comp; pub mod converter; pub mod r#in; pub mod out; diff --git a/src/cmds/comp/helpdoc.rs b/src/cmds/comp/helpdoc.rs new file mode 100644 index 0000000..7f07cad --- /dev/null +++ b/src/cmds/comp/helpdoc.rs @@ -0,0 +1,13 @@ +use crate::systems::{comp::context::CompletionContext, helpdoc}; + +pub fn comp(ctx: CompletionContext) -> Option<Vec<String>> { + if ctx.previous_word == "helpdoc" { + return Some( + helpdoc::get_helpdoc_list() + .iter() + .map(|s| s.to_string()) + .collect(), + ); + } + None +} diff --git a/src/systems.rs b/src/systems.rs index e0d4491..5ca0801 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -1,4 +1,5 @@ pub mod cmd; +pub mod comp; pub mod debug; pub mod helpdoc; pub mod render; diff --git a/src/systems/comp.rs b/src/systems/comp.rs new file mode 100644 index 0000000..c7c6577 --- /dev/null +++ b/src/systems/comp.rs @@ -0,0 +1,2 @@ +pub mod _comps; +pub mod context; diff --git a/src/systems/comp/context.rs b/src/systems/comp/context.rs new file mode 100644 index 0000000..36d1840 --- /dev/null +++ b/src/systems/comp/context.rs @@ -0,0 +1,33 @@ +use clap::{Parser, command}; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub struct CompletionContext { + /// The full command line + #[arg(short = 'f', long)] + pub command_line: String, + + /// Cursor position + #[arg(short = 'C', long)] + pub cursor_position: usize, + + /// Current word + #[arg(short = 'w', long)] + pub current_word: String, + + /// Previous word + #[arg(short = 'p', long)] + pub previous_word: String, + + /// Command name + #[arg(short = 'c', long)] + pub command_name: String, + + /// Word index + #[arg(short = 'i', long)] + pub word_index: usize, + + /// All words + #[arg(short = 'a', long, num_args = 1..)] + pub all_words: Vec<String>, +} diff --git a/src/systems/render/renderer.rs b/src/systems/render/renderer.rs index dab4c23..13de8c6 100644 --- a/src/systems/render/renderer.rs +++ b/src/systems/render/renderer.rs @@ -60,3 +60,5 @@ macro_rules! r_println { $result.println(&format!($($arg)*)) }; } + +include!("_override_renderers.rs"); diff --git a/templates/_comps.rs.template b/templates/_comps.rs.template new file mode 100644 index 0000000..b7f10db --- /dev/null +++ b/templates/_comps.rs.template @@ -0,0 +1,26 @@ +// Auto generated by build.rs +use crate::systems::comp::context::CompletionContext; + +pub fn match_comp(node: String, ctx: CompletionContext) -> Option<Vec<String>> { + let node_str = node.as_str(); + match node_str { +>>>>>>>>>> comp_match_arms + _ => None, + } +} + +pub fn jv_cmd_comp_nodes() -> Vec<String> { + vec![ +>>>>>>>>>> comp_node_name + ] +} + +@@@ >>> comp_match_arms + // <<<comp_name>>>.rs + "<<<comp_node_name>>>" => crate::cmds::comp::<<<comp_name>>>::comp(ctx), +@@@ <<< + +@@@ >>> comp_node_name + // <<<comp_node_name>>> + "<<<comp_node_name>>>".to_string(), +@@@ <<< diff --git a/templates/_override_renderers.rs.template b/templates/_override_renderers.rs.template new file mode 100644 index 0000000..a947c49 --- /dev/null +++ b/templates/_override_renderers.rs.template @@ -0,0 +1,10 @@ +/// Get all override renderers +pub fn jv_override_renderers() -> Vec<String> { + vec![ +>>>>>>>>>> renderer + ] +} + +@@@ >>> renderer + "<<<renderer_name>>>".to_string(), +@@@ <<< |
