cli-justify 0.1.20

A CLI text justify tool
Documentation
use crate::text_utils::{char_len, leading_whitespace};

use super::structure::looks_like_command_prompt_line;

const DEFAULT_CODE_BLOCK_BASE_INDENT_CHARS: usize = 2;
const DEEP_CALLOUT_CODE_BLOCK_EXTRA_INDENT_CHARS: usize = 2;

pub(super) fn ensure_code_block_padding_above(
  out: &mut Vec<String>,
  in_code_block: &mut bool,
) {
  if !*in_code_block && out.last().is_some_and(|last| !last.is_empty()) {
    out.push(String::new());
  }
  *in_code_block = true;
}

pub(super) fn ensure_code_block_padding_below_if_needed(
  out: &mut Vec<String>,
  in_code_block: &mut bool,
) {
  if !*in_code_block {
    return;
  }
  if out.last().is_some_and(|last| !last.is_empty()) {
    out.push(String::new());
  }
  *in_code_block = false;
}

pub(super) fn ensure_code_block_padding_below_if_needed_and_reset(
  out: &mut Vec<String>,
  in_code_block: &mut bool,
  source_base_indent: &mut Option<usize>,
  target_base_indent: &mut Option<usize>,
) {
  ensure_code_block_padding_below_if_needed(out, in_code_block);
  if !*in_code_block {
    reset_code_block_indent_state(source_base_indent, target_base_indent);
  }
}

pub(super) fn reset_code_block_indent_state(
  source_base_indent: &mut Option<usize>,
  target_base_indent: &mut Option<usize>,
) {
  *source_base_indent = None;
  *target_base_indent = None;
}

pub(super) fn reindent_code_block_line(
  line: &str,
  source_base_indent: &mut Option<usize>,
  target_base_indent: &mut Option<usize>,
  pending_parent_callout_indent: &mut Option<usize>,
) -> String {
  let trimmed = line.trim_start_matches([' ', '\t']);
  if trimmed.is_empty() {
    return line.to_string();
  }

  let line_indent = leading_whitespace(line);
  let line_indent_width = char_len(line_indent);

  if source_base_indent.is_none() || target_base_indent.is_none() {
    *source_base_indent = Some(line_indent_width);
    let target_base = pending_parent_callout_indent
      .map(|indent| indent + DEEP_CALLOUT_CODE_BLOCK_EXTRA_INDENT_CHARS)
      .unwrap_or(DEFAULT_CODE_BLOCK_BASE_INDENT_CHARS);
    *target_base_indent = Some(target_base);
    *pending_parent_callout_indent = None;
  }

  let source_base = source_base_indent.unwrap_or(line_indent_width);
  if line_indent_width < source_base {
    *source_base_indent = Some(line_indent_width);
  }
  let source_base = source_base_indent.unwrap_or(line_indent_width);
  let target_base =
    target_base_indent.unwrap_or(DEFAULT_CODE_BLOCK_BASE_INDENT_CHARS);
  let relative_indent = if looks_like_command_prompt_line(trimmed) {
    0
  } else {
    line_indent_width.saturating_sub(source_base)
  };
  let new_indent = target_base + relative_indent;
  format!("{}{}", " ".repeat(new_indent), trimmed)
}