rrubocop 0.8.15

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

use tree_sitter::Tree;

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

pub struct SpaceBeforeComment;

fn line_prefix<'a>(bytes: &'a [u8], start: usize) -> &'a [u8] {
    let before = &bytes[bytes[..start]
        .iter()
        .rposition(|&b| b == b'\n')
        .map_or(0, |i| i + 1)..start];
    before.strip_prefix(b"\xEF\xBB\xBF").unwrap_or(before)
}

fn only_indent(bytes: &[u8], start: usize) -> bool {
    line_prefix(bytes, start)
        .iter()
        .all(|&b| b == b' ' || b == b'\t')
}

fn is_char_esc(bytes: &[u8], start: usize) -> bool {
    start >= 3 && bytes[start - 2] == b'\\' && bytes[start - 3] == b'?'
}

fn needs_space(bytes: &[u8], start: usize) -> bool {
    if start == 0 {
        return false;
    }
    let prev = bytes[start - 1];
    if prev == b'\n' || prev == b'\r' || only_indent(bytes, start) {
        return false;
    }
    (prev != b' ' && prev != b'\t') || (prev == b' ' && is_char_esc(bytes, start))
}

fn check_one(
    cop: &dyn Cop,
    source: &SourceFile,
    bytes: &[u8],
    start: usize,
    diagnostics: &mut Vec<Diagnostic>,
    corrections: &mut Option<&mut Vec<Correction>>,
) {
    if !needs_space(bytes, start) {
        return;
    }
    report::insert_space(
        cop,
        source,
        start,
        "Put a space before an end-of-line comment.".into(),
        diagnostics,
        corrections,
        start,
    );
}

impl Cop for SpaceBeforeComment {
    fn name(&self) -> &'static str {
        "Layout/SpaceBeforeComment"
    }
    fn supports_autocorrect(&self) -> bool {
        true
    }

    fn uses_source_phase(&self) -> bool {
        true
    }

    fn check_source(
        &self,
        source: &SourceFile,
        tree: &Tree,
        code_map: &CodeMap,
        config: &CopConfig,
        diagnostics: &mut Vec<Diagnostic>,
        mut corrections: Option<&mut Vec<Correction>>,
    ) {
        let _ = (code_map, config);
        let bytes = source.as_bytes();
        for comment in shared::collect_comments(tree.root_node()) {
            check_one(
                self,
                source,
                bytes,
                comment.start_byte(),
                diagnostics,
                &mut corrections,
            );
        }
    }
}