rrubocop 0.8.15

10x faster RuboCop drop-in. Same output format and configs, without maintaining RuboCop gems.
//! Layout/SpaceAroundBlockParameters.

use tree_sitter::Node;

use crate::cop::layout::report;
use crate::cop::{Cop, CopConfig};
use crate::correction::Correction;
use crate::diagnostic::Diagnostic;
use crate::parse::source::SourceFile;

pub struct SpaceAroundBlockParameters;

fn strip_leading(bytes: &[u8], inner_s: usize, close: usize) -> usize {
    let mut e = inner_s;
    while e < close && matches!(bytes[e], b' ' | b'\t') { e += 1; }
    e
}

fn strip_trailing(bytes: &[u8], inner_s: usize, close: usize) -> usize {
    let mut s = close;
    while s > inner_s && matches!(bytes[s - 1], b' ' | b'\t') { s -= 1; }
    s
}

fn want_spaces(
    cop: &dyn Cop, source: &SourceFile, inner_s: usize, close: usize,
    after: Option<u8>, before: Option<u8>, sp_a: bool, sp_b: bool,
    diagnostics: &mut Vec<Diagnostic>, corrections: &mut Option<&mut Vec<Correction>>,
) {
    let msg = "Space missing inside block parameter pipes.";
    if !sp_a && after != Some(b'|') {
        report::insert_space(cop, source, inner_s, msg.into(), diagnostics, corrections, inner_s);
    }
    if !sp_b && before != Some(b'|') {
        report::insert_space(cop, source, close, msg.into(), diagnostics, corrections, close);
    }
}

fn no_spaces(
    cop: &dyn Cop, source: &SourceFile, bytes: &[u8], inner_s: usize, close: usize,
    sp_a: bool, sp_b: bool,
    diagnostics: &mut Vec<Diagnostic>, corrections: &mut Option<&mut Vec<Correction>>,
) {
    let msg = "Space present inside block parameter pipes.";
    if sp_a {
        report::report_fix(
            cop, source, inner_s, msg.into(), diagnostics, corrections,
            inner_s, strip_leading(bytes, inner_s, close), String::new(),
        );
    }
    if sp_b {
        report::report_fix(
            cop, source, close.saturating_sub(1), msg.into(), diagnostics, corrections,
            strip_trailing(bytes, inner_s, close), close, String::new(),
        );
    }
}

fn pipe_span(bytes: &[u8], node: Node<'_>) -> Option<(usize, usize)> {
    if bytes.get(node.start_byte()) != Some(&b'|') { return None; }
    if bytes.get(node.end_byte() - 1) != Some(&b'|') { return None; }
    Some((node.start_byte() + 1, node.end_byte() - 1))
}

impl Cop for SpaceAroundBlockParameters {
    fn name(&self) -> &'static str { "Layout/SpaceAroundBlockParameters" }
    fn supports_autocorrect(&self) -> bool { true }
    fn interested_node_kinds(&self) -> &'static [&'static str] { &["block_parameters"] }

    fn check_node(
        &self, source: &SourceFile, node: Node<'_>, config: &CopConfig,
        diagnostics: &mut Vec<Diagnostic>, mut corrections: Option<&mut Vec<Correction>>,
    ) {
        let style = config.get_str("EnforcedStyleInsidePipes", "no_space");
        let bytes = source.as_bytes();
        let Some((inner_s, close)) = pipe_span(bytes, node) else { return; };
        let after = bytes.get(inner_s).copied();
        let before = bytes.get(close.saturating_sub(1)).copied();
        let sp_a = matches!(after, Some(b' ') | Some(b'\t'));
        let sp_b = matches!(before, Some(b' ') | Some(b'\t'));
        if style == "space" {
            want_spaces(self, source, inner_s, close, after, before, sp_a, sp_b, diagnostics, &mut corrections);
        } else {
            no_spaces(self, source, bytes, inner_s, close, sp_a, sp_b, diagnostics, &mut corrections);
        }
    }
}