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