relay-knowledge 1.1.16

Graph-database-based knowledge graph project.
Documentation
#[derive(Default)]
pub(super) struct CodeScanState {
    in_block_comment: bool,
    in_string: bool,
    in_char: bool,
    escaped: bool,
}

impl CodeScanState {
    pub(super) fn line_complete(&self) -> bool {
        !self.in_string && !self.in_char && !self.escaped
    }

    pub(super) fn closed(&self) -> bool {
        !self.in_block_comment && self.line_complete()
    }
}

pub(super) fn first_code_char_index(text: &str, wanted: char) -> Option<usize> {
    let mut state = CodeScanState::default();
    let mut offset = 0usize;
    for segment in text.split_inclusive('\n') {
        let line = segment
            .strip_suffix('\n')
            .unwrap_or(segment)
            .strip_suffix('\r')
            .unwrap_or(segment.strip_suffix('\n').unwrap_or(segment));
        let mut found = None;
        scan_code_line_indices_with_state(line, &mut state, |index, character| {
            if character == wanted && found.is_none() {
                found = Some(offset + index);
            }
        });
        if found.is_some() {
            return found;
        }
        if !state.line_complete() {
            return None;
        }
        offset += segment.len();
    }

    None
}

pub(super) fn line_has_balanced_delimiters(line: &str) -> bool {
    let mut parentheses = 0isize;
    let mut brackets = 0isize;
    let mut invalid_order = false;
    let literals_closed = scan_code_line(line, |character| {
        match character {
            '(' => parentheses += 1,
            ')' => parentheses -= 1,
            '[' => brackets += 1,
            ']' => brackets -= 1,
            _ => {}
        }
        if parentheses < 0 || brackets < 0 {
            invalid_order = true;
        }
    });

    literals_closed && !invalid_order && parentheses == 0 && brackets == 0
}

pub(super) fn scan_code_line(line: &str, mut visit: impl FnMut(char)) -> bool {
    scan_code_line_indices(line, |_, character| visit(character))
}

pub(in crate::code::parser) fn scan_code_line_indices(
    line: &str,
    mut visit: impl FnMut(usize, char),
) -> bool {
    let mut state = CodeScanState::default();
    scan_code_line_indices_with_state(line, &mut state, |index, character| {
        visit(index, character);
    });
    state.closed()
}

pub(in crate::code::parser) fn token_starts_in_angle_arguments(
    head: &str,
    token_start: usize,
) -> bool {
    let mut angle_depth = 0isize;
    scan_code_line_indices(head, |index, character| {
        if index >= token_start {
            return;
        }
        match character {
            '<' => angle_depth += 1,
            '>' => angle_depth = angle_depth.saturating_sub(1),
            _ => {}
        }
    });
    angle_depth > 0
}

pub(in crate::code::parser) fn code_contains_char(text: &str, wanted: char) -> bool {
    let mut found = false;
    scan_code_line_indices(text, |_, character| found |= character == wanted);
    found
}

pub(in crate::code::parser) fn parameter_list_has_empty_slot(parameters: &str) -> bool {
    let mut paren_depth = 0isize;
    let mut bracket_depth = 0isize;
    let mut angle_depth = 0isize;
    let mut segment_has_code = false;
    let mut empty_slot = false;
    let literals_closed = scan_code_line_indices(parameters, |_, character| {
        if empty_slot {
            return;
        }
        match character {
            '(' => paren_depth += 1,
            ')' => paren_depth -= 1,
            '[' => bracket_depth += 1,
            ']' => bracket_depth -= 1,
            '<' if paren_depth == 0 && bracket_depth == 0 => angle_depth += 1,
            '>' if angle_depth > 0 => angle_depth -= 1,
            ',' if paren_depth == 0 && bracket_depth == 0 && angle_depth == 0 => {
                if !segment_has_code {
                    empty_slot = true;
                }
                segment_has_code = false;
                return;
            }
            _ => {}
        }
        if !character.is_ascii_whitespace() {
            segment_has_code = true;
        }
    });

    !literals_closed
        || empty_slot
        || (parameters.trim_end().ends_with(',') && !parameters.is_empty())
}

pub(super) fn scan_code_line_indices_with_state(
    mut line: &str,
    state: &mut CodeScanState,
    mut visit: impl FnMut(usize, char),
) {
    let mut offset = 0usize;

    while !line.is_empty() {
        let Some(character) = line.chars().next() else {
            break;
        };
        let width = character.len_utf8();
        let rest = &line[width..];

        if state.in_block_comment {
            if character == '*' && rest.starts_with('/') {
                line = &rest[1..];
                offset += width + 1;
                state.in_block_comment = false;
            } else {
                line = rest;
                offset += width;
            }
            continue;
        }
        if state.in_string {
            if state.escaped {
                state.escaped = false;
            } else if character == '\\' {
                state.escaped = true;
            } else if character == '"' {
                state.in_string = false;
            }
            line = rest;
            offset += width;
            continue;
        }
        if state.in_char {
            if state.escaped {
                state.escaped = false;
            } else if character == '\\' {
                state.escaped = true;
            } else if character == '\'' {
                state.in_char = false;
            }
            line = rest;
            offset += width;
            continue;
        }

        if character == '/' && rest.starts_with('/') {
            let Some(newline_index) = rest.find('\n') else {
                break;
            };
            line = &rest[newline_index + 1..];
            offset += width + newline_index + 1;
            continue;
        }
        if character == '/' && rest.starts_with('*') {
            line = &rest[1..];
            offset += width + 1;
            state.in_block_comment = true;
            continue;
        }
        if character == '"' {
            state.in_string = true;
        } else if character == '\'' {
            state.in_char = true;
        } else {
            visit(offset, character);
        }
        line = rest;
        offset += width;
    }
}