1use crate::eval_call;
2use fancy_regex::{Captures, Regex};
3use nu_protocol::{
4 Category, Config, IntoPipelineData, PipelineData, PositionalArg, Signature, Span, SpanId,
5 Spanned, SyntaxShape, Type, Value,
6 ast::{Argument, Call, Expr, Expression, RecordItem},
7 debugger::WithoutDebug,
8 engine::CommandType,
9 engine::{Command, EngineState, Stack, UNKNOWN_SPAN_ID},
10 record,
11};
12use nu_utils::terminal_size;
13use std::{
14 borrow::Cow,
15 collections::{HashMap, HashSet},
16 fmt::Write,
17 sync::{Arc, LazyLock},
18};
19
20const RESET: &str = "\x1b[0m";
22const DEFAULT_COLOR: &str = "\x1b[39m";
24const DEFAULT_DIMMED: &str = "\x1b[2;39m";
26const DEFAULT_ITALIC: &str = "\x1b[3;39m";
28
29pub fn get_full_help(
30 command: &dyn Command,
31 engine_state: &EngineState,
32 stack: &mut Stack,
33 head: Span,
34) -> String {
35 let stack = &mut stack.start_collect_value();
40
41 let nu_config = stack.get_config(engine_state);
42
43 let sig = engine_state
44 .get_signature(command)
45 .update_from_command(command);
46
47 let mut help_style = HelpStyle::default();
49 help_style.update_from_config(engine_state, &nu_config, head);
50
51 let mut long_desc = String::new();
52
53 let desc = &sig.description;
54 if !desc.is_empty() {
55 long_desc.push_str(&highlight_code(desc, engine_state, stack, head));
56 long_desc.push_str("\n\n");
57 }
58
59 let extra_desc = &sig.extra_description;
60 if !extra_desc.is_empty() {
61 long_desc.push_str(&highlight_code(extra_desc, engine_state, stack, head));
62 long_desc.push_str("\n\n");
63 }
64
65 match command.command_type() {
66 CommandType::Alias => get_alias_documentation(
67 &mut long_desc,
68 command,
69 &sig,
70 &help_style,
71 engine_state,
72 stack,
73 head,
74 ),
75 _ => get_command_documentation(
76 &mut long_desc,
77 command,
78 &sig,
79 &nu_config,
80 &help_style,
81 engine_state,
82 stack,
83 head,
84 ),
85 };
86
87 let mut final_help = if !nu_config.use_ansi_coloring.get(engine_state) {
88 nu_utils::strip_ansi_string_likely(long_desc)
89 } else {
90 long_desc
91 };
92
93 if let Some(cmd) = command.as_alias().and_then(|alias| alias.command.as_ref()) {
94 let nested_help = get_full_help(cmd.as_ref(), engine_state, stack, head);
95 if !nested_help.is_empty() {
96 final_help.push_str("\n\n");
97 final_help.push_str(&nested_help);
98 }
99 }
100
101 final_help
102}
103
104fn try_nu_highlight(
106 code_string: &str,
107 reject_garbage: bool,
108 engine_state: &EngineState,
109 stack: &mut Stack,
110 head: Span,
111) -> Option<String> {
112 let highlighter = engine_state.find_decl(b"nu-highlight", &[])?;
113
114 let decl = engine_state.get_decl(highlighter);
115 let mut call = Call::new(head);
116 if reject_garbage {
117 call.add_named((
118 Spanned {
119 item: "reject-garbage".into(),
120 span: head,
121 },
122 None,
123 None,
124 ));
125 }
126
127 decl.run(
128 engine_state,
129 stack,
130 &(&call).into(),
131 Value::string(code_string, head).into_pipeline_data(),
132 )
133 .and_then(|pipe| pipe.into_value(head))
134 .and_then(|val| val.coerce_into_string())
135 .ok()
136}
137
138fn nu_highlight_string(
140 code_string: &str,
141 engine_state: &EngineState,
142 stack: &mut Stack,
143 head: Span,
144) -> String {
145 try_nu_highlight(code_string, false, engine_state, stack, head)
146 .unwrap_or_else(|| code_string.to_string())
147}
148
149fn highlight_capture_group(
151 captures: &Captures,
152 engine_state: &EngineState,
153 stack: &mut Stack,
154 head: Span,
155) -> String {
156 let Some(content) = captures.get(1) else {
157 return String::new();
159 };
160
161 let config_old = stack.get_config(engine_state);
163 let mut config = (*config_old).clone();
164
165 let code_style = Value::record(
169 record! {
170 "attr" => Value::string("di", head),
171 },
172 head,
173 );
174 let color_config = &mut config.color_config;
175 color_config.insert("shape_external".into(), code_style.clone());
176 color_config.insert("shape_external_resolved".into(), code_style.clone());
177 color_config.insert("shape_externalarg".into(), code_style);
178
179 stack.config = Some(Arc::new(config));
181
182 let highlighted = try_nu_highlight(content.into(), true, engine_state, stack, head)
184 .map(|text| {
186 let resets = text.match_indices(RESET).count();
187 let text = text.replacen(
189 RESET,
190 &format!("{RESET}{DEFAULT_ITALIC}"),
191 resets.saturating_sub(1),
192 );
193 format!("{DEFAULT_ITALIC}{text}")
195 });
196
197 stack.config = Some(config_old);
199
200 highlighted.unwrap_or_else(|| highlight_fallback(content.into()))
202}
203
204fn highlight_fallback(text: &str) -> String {
206 format!("{DEFAULT_DIMMED}{DEFAULT_ITALIC}{text}{RESET}")
207}
208
209fn highlight_code<'a>(
213 text: &'a str,
214 engine_state: &EngineState,
215 stack: &mut Stack,
216 head: Span,
217) -> Cow<'a, str> {
218 let config = stack.get_config(engine_state);
219 if !config.use_ansi_coloring.get(engine_state) {
220 return Cow::Borrowed(text);
221 }
222
223 static PATTERN: &str = r"(?x) # verbose mode
225 (?<![\p{Letter}\d]) # negative look-behind for alphanumeric: ensure backticks are not directly preceded by letter/number.
226 `
227 ([^`\n]+?) # capture characters inside backticks, excluding backticks and newlines. ungreedy.
228 `
229 (?![\p{Letter}\d]) # negative look-ahead for alphanumeric: ensure backticks are not directly followed by letter/number.
230 ";
231 static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(PATTERN).expect("valid regex"));
232
233 let do_try_highlight =
234 |captures: &Captures| highlight_capture_group(captures, engine_state, stack, head);
235 RE.replace_all(text, do_try_highlight)
236}
237
238#[allow(clippy::too_many_arguments)]
239fn get_alias_documentation(
240 long_desc: &mut String,
241 command: &dyn Command,
242 sig: &Signature,
243 help_style: &HelpStyle,
244 engine_state: &EngineState,
245 stack: &mut Stack,
246 head: Span,
247) {
248 let help_section_name = &help_style.section_name;
249 let help_subcolor_one = &help_style.subcolor_one;
250
251 let alias_name = &sig.name;
252
253 write!(
254 long_desc,
255 "{help_section_name}Alias{RESET}: {help_subcolor_one}{alias_name}{RESET}"
256 )
257 .expect("writing to a String is infallible");
258 long_desc.push_str("\n\n");
259
260 let Some(alias) = command.as_alias() else {
261 return;
263 };
264
265 let alias_expansion =
266 String::from_utf8_lossy(engine_state.get_span_contents(alias.wrapped_call.span));
267
268 write!(
269 long_desc,
270 "{help_section_name}Expansion{RESET}:\n {}",
271 nu_highlight_string(&alias_expansion, engine_state, stack, head)
272 )
273 .expect("writing to a String is infallible");
274}
275
276#[allow(clippy::too_many_arguments)]
277fn get_command_documentation(
278 long_desc: &mut String,
279 command: &dyn Command,
280 sig: &Signature,
281 nu_config: &Config,
282 help_style: &HelpStyle,
283 engine_state: &EngineState,
284 stack: &mut Stack,
285 head: Span,
286) {
287 let help_section_name = &help_style.section_name;
288 let help_subcolor_one = &help_style.subcolor_one;
289
290 let cmd_name = &sig.name;
291
292 if !sig.search_terms.is_empty() {
293 write!(
294 long_desc,
295 "{help_section_name}Search terms{RESET}: {help_subcolor_one}{}{RESET}\n\n",
296 sig.search_terms.join(", "),
297 )
298 .expect("writing to a String is infallible");
299 }
300
301 write!(
302 long_desc,
303 "{help_section_name}Usage{RESET}:\n > {}\n",
304 sig.call_signature()
305 )
306 .expect("writing to a String is infallible");
307
308 let mut subcommands = vec![];
318 let signatures = engine_state.get_signatures_and_declids(true);
319 let mut seen = HashSet::new();
321 for (sig, decl_id) in signatures {
322 let display_name = engine_state
325 .find_decl_name(decl_id, &[])
326 .map(|bytes| String::from_utf8_lossy(bytes).to_string())
327 .unwrap_or_else(|| sig.name.clone());
328
329 if (display_name.starts_with(&format!("{cmd_name} "))
333 || sig.name.starts_with(&format!("{cmd_name} ")))
334 && !matches!(sig.category, Category::Removed)
335 && seen.insert(decl_id)
336 {
337 let command_type = engine_state.get_decl(decl_id).command_type();
338
339 let name_to_print = if display_name.starts_with(&format!("{cmd_name} ")) {
342 display_name.clone()
343 } else {
344 sig.name.clone()
345 };
346
347 if command_type == CommandType::Plugin
349 || command_type == CommandType::Alias
350 || command_type == CommandType::Custom
351 {
352 subcommands.push(format!(
353 " {help_subcolor_one}{} {help_section_name}({}){RESET} - {}",
354 name_to_print,
355 command_type,
356 highlight_code(&sig.description, engine_state, stack, head)
357 ));
358 } else {
359 subcommands.push(format!(
360 " {help_subcolor_one}{}{RESET} - {}",
361 name_to_print,
362 highlight_code(&sig.description, engine_state, stack, head)
363 ));
364 }
365 }
366 }
367
368 if !subcommands.is_empty() {
369 write!(long_desc, "\n{help_section_name}Subcommands{RESET}:\n")
370 .expect("writing to a String is infallible");
371 subcommands.sort();
372 subcommands.dedup();
374 long_desc.push_str(&subcommands.join("\n"));
375 long_desc.push('\n');
376 }
377
378 if !sig.named.is_empty() {
379 long_desc.push_str(&get_flags_section(sig, help_style, |v| match v {
380 FormatterValue::DefaultValue(value) => nu_highlight_string(
381 &value.to_parsable_string(", ", nu_config),
382 engine_state,
383 stack,
384 head,
385 ),
386 FormatterValue::CodeString(text) => {
387 highlight_code(text, engine_state, stack, head).to_string()
388 }
389 }))
390 }
391
392 write!(
393 long_desc,
394 "\n{help_section_name}Command Type{RESET}:\n > {}\n",
395 command.command_type()
396 )
397 .expect("writing to a String is infallible");
398
399 if !sig.required_positional.is_empty()
400 || !sig.optional_positional.is_empty()
401 || sig.rest_positional.is_some()
402 {
403 write!(long_desc, "\n{help_section_name}Parameters{RESET}:\n")
404 .expect("writing to a String is infallible");
405 for positional in &sig.required_positional {
406 write_positional(
407 long_desc,
408 positional,
409 PositionalKind::Required,
410 help_style,
411 nu_config,
412 engine_state,
413 stack,
414 head,
415 );
416 }
417 for positional in &sig.optional_positional {
418 write_positional(
419 long_desc,
420 positional,
421 PositionalKind::Optional,
422 help_style,
423 nu_config,
424 engine_state,
425 stack,
426 head,
427 );
428 }
429
430 if let Some(rest_positional) = &sig.rest_positional {
431 write_positional(
432 long_desc,
433 rest_positional,
434 PositionalKind::Rest,
435 help_style,
436 nu_config,
437 engine_state,
438 stack,
439 head,
440 );
441 }
442 }
443
444 fn get_term_width() -> usize {
445 if let Ok((w, _h)) = terminal_size() {
446 w as usize
447 } else {
448 80
449 }
450 }
451
452 if !command.is_keyword()
453 && !sig.input_output_types.is_empty()
454 && let Some(decl_id) = engine_state.find_decl(b"table", &[])
455 {
456 let mut vals = vec![];
457 for (input, output) in &sig.input_output_types {
458 vals.push(Value::record(
459 record! {
460 "input" => Value::string(input.to_string(), head),
461 "output" => Value::string(output.to_string(), head),
462 },
463 head,
464 ));
465 }
466
467 let caller_stack = &mut Stack::new().collect_value();
468 if let Ok(result) = eval_call::<WithoutDebug>(
469 engine_state,
470 caller_stack,
471 &Call {
472 decl_id,
473 head,
474 arguments: vec![Argument::Named((
475 Spanned {
476 item: "width".to_string(),
477 span: head,
478 },
479 None,
480 Some(Expression::new_unknown(
481 Expr::Int(get_term_width() as i64 - 2), head,
483 Type::Int,
484 )),
485 ))],
486 parser_info: HashMap::new(),
487 },
488 PipelineData::value(Value::list(vals, head), None),
489 ) && let Ok((str, ..)) = result.collect_string_strict(head)
490 {
491 writeln!(long_desc, "\n{help_section_name}Input/output types{RESET}:")
492 .expect("writing to a String is infallible");
493 for line in str.lines() {
494 writeln!(long_desc, " {line}").expect("writing to a String is infallible");
495 }
496 }
497 }
498
499 let examples = command.examples();
500
501 if !examples.is_empty() {
502 write!(long_desc, "\n{help_section_name}Examples{RESET}:")
503 .expect("writing to a String is infallible");
504 }
505
506 for example in examples {
507 long_desc.push('\n');
508 long_desc.push_str(" ");
509 long_desc.push_str(&highlight_code(
510 example.description,
511 engine_state,
512 stack,
513 head,
514 ));
515
516 if !nu_config.use_ansi_coloring.get(engine_state) {
517 write!(long_desc, "\n > {}\n", example.example)
518 .expect("writing to a String is infallible");
519 } else {
520 let code_string = nu_highlight_string(example.example, engine_state, stack, head);
521 write!(long_desc, "\n > {code_string}\n").expect("writing to a String is infallible");
522 };
523
524 if let Some(result) = &example.result {
525 let mut table_call = Call::new(head);
526 if example.example.ends_with("--collapse") {
527 table_call.add_named((
529 Spanned {
530 item: "collapse".to_string(),
531 span: head,
532 },
533 None,
534 None,
535 ))
536 } else {
537 table_call.add_named((
539 Spanned {
540 item: "expand".to_string(),
541 span: head,
542 },
543 None,
544 None,
545 ))
546 }
547 table_call.add_named((
548 Spanned {
549 item: "width".to_string(),
550 span: head,
551 },
552 None,
553 Some(Expression::new_unknown(
554 Expr::Int(get_term_width() as i64 - 2),
555 head,
556 Type::Int,
557 )),
558 ));
559
560 let table = engine_state
561 .find_decl("table".as_bytes(), &[])
562 .and_then(|decl_id| {
563 engine_state
564 .get_decl(decl_id)
565 .run(
566 engine_state,
567 stack,
568 &(&table_call).into(),
569 PipelineData::value(result.clone(), None),
570 )
571 .ok()
572 });
573
574 for item in table.into_iter().flatten() {
575 writeln!(
576 long_desc,
577 " {}",
578 item.to_expanded_string("", nu_config)
579 .trim_end()
580 .trim_start_matches(|c: char| c.is_whitespace() && c != ' ')
581 .replace('\n', "\n ")
582 )
583 .expect("writing to a String is infallible");
584 }
585 }
586 }
587
588 long_desc.push('\n');
589}
590
591fn update_ansi_from_config(
592 ansi_code: &mut String,
593 engine_state: &EngineState,
594 nu_config: &Config,
595 theme_component: &str,
596 head: Span,
597) {
598 if let Some(color) = &nu_config.color_config.get(theme_component) {
599 let caller_stack = &mut Stack::new().collect_value();
600 let span_id = UNKNOWN_SPAN_ID;
601
602 let argument_opt = get_argument_for_color_value(nu_config, color, head, span_id);
603
604 if let Some(argument) = argument_opt
606 && let Some(decl_id) = engine_state.find_decl(b"ansi", &[])
607 && let Ok(result) = eval_call::<WithoutDebug>(
608 engine_state,
609 caller_stack,
610 &Call {
611 decl_id,
612 head,
613 arguments: vec![argument],
614 parser_info: HashMap::new(),
615 },
616 PipelineData::empty(),
617 )
618 && let Ok((str, ..)) = result.collect_string_strict(head)
619 {
620 *ansi_code = str;
621 }
622 }
623}
624
625fn get_argument_for_color_value(
626 nu_config: &Config,
627 color: &Value,
628 span: Span,
629 span_id: SpanId,
630) -> Option<Argument> {
631 match color {
632 Value::Record { val, .. } => {
633 let record_exp: Vec<RecordItem> = (**val)
634 .iter()
635 .map(|(k, v)| {
636 RecordItem::Pair(
637 Expression::new_existing(
638 Expr::String(k.clone()),
639 span,
640 span_id,
641 Type::String,
642 ),
643 Expression::new_existing(
644 Expr::String(v.clone().to_expanded_string("", nu_config)),
645 span,
646 span_id,
647 Type::String,
648 ),
649 )
650 })
651 .collect();
652
653 Some(Argument::Positional(Expression::new_existing(
654 Expr::Record(record_exp),
655 span,
656 span_id,
657 Type::Record(
658 [
659 ("fg".to_string(), Type::String),
660 ("attr".to_string(), Type::String),
661 ]
662 .into(),
663 ),
664 )))
665 }
666 Value::String { val, .. } => Some(Argument::Positional(Expression::new_existing(
667 Expr::String(val.clone()),
668 span,
669 span_id,
670 Type::String,
671 ))),
672 _ => None,
673 }
674}
675
676pub struct HelpStyle {
682 section_name: String,
683 subcolor_one: String,
684 subcolor_two: String,
685}
686
687impl Default for HelpStyle {
688 fn default() -> Self {
689 HelpStyle {
690 section_name: "\x1b[32m".to_string(),
692 subcolor_one: "\x1b[36m".to_string(),
694 subcolor_two: "\x1b[94m".to_string(),
696 }
697 }
698}
699
700impl HelpStyle {
701 pub fn update_from_config(
709 &mut self,
710 engine_state: &EngineState,
711 nu_config: &Config,
712 head: Span,
713 ) {
714 update_ansi_from_config(
715 &mut self.section_name,
716 engine_state,
717 nu_config,
718 "shape_string",
719 head,
720 );
721 update_ansi_from_config(
722 &mut self.subcolor_one,
723 engine_state,
724 nu_config,
725 "shape_external",
726 head,
727 );
728 update_ansi_from_config(
729 &mut self.subcolor_two,
730 engine_state,
731 nu_config,
732 "shape_block",
733 head,
734 );
735 }
736}
737
738#[derive(PartialEq)]
739enum PositionalKind {
740 Required,
741 Optional,
742 Rest,
743}
744
745#[allow(clippy::too_many_arguments)]
746fn write_positional(
747 long_desc: &mut String,
748 positional: &PositionalArg,
749 arg_kind: PositionalKind,
750 help_style: &HelpStyle,
751 nu_config: &Config,
752 engine_state: &EngineState,
753 stack: &mut Stack,
754 head: Span,
755) {
756 let help_subcolor_one = &help_style.subcolor_one;
757 let help_subcolor_two = &help_style.subcolor_two;
758
759 long_desc.push_str(" ");
761 if arg_kind == PositionalKind::Rest {
762 long_desc.push_str("...");
763 }
764 match &positional.shape {
765 SyntaxShape::Keyword(kw, shape) => {
766 write!(
767 long_desc,
768 "{help_subcolor_one}\"{}\" + {RESET}<{help_subcolor_two}{}{RESET}>",
769 String::from_utf8_lossy(kw),
770 shape,
771 )
772 .expect("writing to a String is infallible");
773 }
774 _ => {
775 write!(
776 long_desc,
777 "{help_subcolor_one}{}{RESET} <{help_subcolor_two}{}{RESET}>",
778 positional.name, &positional.shape,
779 )
780 .expect("writing to a String is infallible");
781 }
782 };
783 if !positional.desc.is_empty() || arg_kind == PositionalKind::Optional {
784 write!(
785 long_desc,
786 ": {}",
787 highlight_code(&positional.desc, engine_state, stack, head)
788 )
789 .expect("writing to a String is infallible");
790 }
791 if arg_kind == PositionalKind::Optional {
792 if let Some(value) = &positional.default_value {
793 write!(
794 long_desc,
795 " (optional, default: {})",
796 nu_highlight_string(
797 &value.to_parsable_string(", ", nu_config),
798 engine_state,
799 stack,
800 head
801 )
802 )
803 .expect("writing to a String is infallible");
804 } else {
805 long_desc.push_str(" (optional)");
806 };
807 }
808 long_desc.push('\n');
809}
810
811pub enum FormatterValue<'a> {
817 DefaultValue(&'a Value),
819 CodeString(&'a str),
821}
822
823fn write_flag_to_long_desc<F>(
824 flag: &nu_protocol::Flag,
825 long_desc: &mut String,
826 help_subcolor_one: &str,
827 help_subcolor_two: &str,
828 formatter: &mut F,
829) where
830 F: FnMut(FormatterValue) -> String,
831{
832 long_desc.push_str(" ");
834 if let Some(short) = flag.short {
836 write!(long_desc, "{help_subcolor_one}-{short}{RESET}")
837 .expect("writing to a String is infallible");
838 if !flag.long.is_empty() {
839 write!(long_desc, "{DEFAULT_COLOR},{RESET} ")
840 .expect("writing to a String is infallible");
841 }
842 }
843 if !flag.long.is_empty() {
844 write!(long_desc, "{help_subcolor_one}--{}{RESET}", flag.long)
845 .expect("writing to a String is infallible");
846 }
847 if flag.required {
848 long_desc.push_str(" (required parameter)")
849 }
850 if let Some(arg) = &flag.arg {
852 write!(long_desc, " <{help_subcolor_two}{arg}{RESET}>")
853 .expect("writing to a String is infallible");
854 }
855 if !flag.desc.is_empty() {
856 write!(
857 long_desc,
858 ": {}",
859 &formatter(FormatterValue::CodeString(&flag.desc))
860 )
861 .expect("writing to a String is infallible");
862 }
863 if let Some(value) = &flag.default_value {
864 write!(
865 long_desc,
866 " (default: {})",
867 &formatter(FormatterValue::DefaultValue(value))
868 )
869 .expect("writing to a String is infallible");
870 }
871 long_desc.push('\n');
872}
873
874pub fn get_flags_section<F>(
875 signature: &Signature,
876 help_style: &HelpStyle,
877 mut formatter: F, ) -> String
879where
880 F: FnMut(FormatterValue) -> String,
881{
882 let help_section_name = &help_style.section_name;
883 let help_subcolor_one = &help_style.subcolor_one;
884 let help_subcolor_two = &help_style.subcolor_two;
885
886 let mut long_desc = String::new();
887 write!(long_desc, "\n{help_section_name}Flags{RESET}:\n")
888 .expect("writing to a String is infallible");
889
890 let help = signature.named.iter().find(|flag| flag.long == "help");
891 let required = signature.named.iter().filter(|flag| flag.required);
892 let optional = signature
893 .named
894 .iter()
895 .filter(|flag| !flag.required && flag.long != "help");
896
897 let flags = required.chain(help).chain(optional);
898
899 for flag in flags {
900 write_flag_to_long_desc(
901 flag,
902 &mut long_desc,
903 help_subcolor_one,
904 help_subcolor_two,
905 &mut formatter,
906 );
907 }
908
909 long_desc
910}
911
912#[cfg(test)]
913mod tests {
914 use nu_protocol::UseAnsiColoring;
915
916 use super::*;
917
918 #[test]
919 fn test_code_formatting() {
920 let mut engine_state = EngineState::new();
921 let mut stack = Stack::new();
922
923 let mut config = (*engine_state.config).clone();
925 config.use_ansi_coloring = UseAnsiColoring::True;
926 engine_state.config = Arc::new(config);
927
928 let haystack = "Run the `foo` command";
933 assert!(matches!(
934 highlight_code(haystack, &engine_state, &mut stack, Span::test_data()),
935 Cow::Owned(_)
936 ));
937
938 let haystack = "foo`bar`";
940 assert!(matches!(
941 highlight_code(haystack, &engine_state, &mut stack, Span::test_data()),
942 Cow::Borrowed(_)
943 ));
944
945 let haystack = "`my-command` is cool";
947 assert!(matches!(
948 highlight_code(haystack, &engine_state, &mut stack, Span::test_data()),
949 Cow::Owned(_)
950 ));
951
952 let haystack = "
954 `command`
955 ";
956 assert!(matches!(
957 highlight_code(haystack, &engine_state, &mut stack, Span::test_data()),
958 Cow::Owned(_)
959 ));
960
961 let haystack = "// hello `beautiful \n world`";
963 assert!(matches!(
964 highlight_code(haystack, &engine_state, &mut stack, Span::test_data()),
965 Cow::Borrowed(_)
966 ));
967
968 let haystack = "try running `my cool command`.";
970 assert!(matches!(
971 highlight_code(haystack, &engine_state, &mut stack, Span::test_data()),
972 Cow::Owned(_)
973 ));
974
975 let haystack = "a command (`my cool command`).";
977 assert!(matches!(
978 highlight_code(haystack, &engine_state, &mut stack, Span::test_data()),
979 Cow::Owned(_)
980 ));
981
982 let haystack = "```\ncode block\n```";
985 assert!(matches!(
986 highlight_code(haystack, &engine_state, &mut stack, Span::test_data()),
987 Cow::Borrowed(_)
988 ));
989 }
990}