use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ScopeKind {
Function,
Class,
Module,
Block,
Heading,
Namespace,
}
impl ScopeKind {
#[must_use]
pub const fn is_definition(self) -> bool {
matches!(self, Self::Function | Self::Class | Self::Module | Self::Namespace)
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Function => "fn",
Self::Class => "class",
Self::Module => "mod",
Self::Block => "block",
Self::Heading => "heading",
Self::Namespace => "namespace",
}
}
}
impl fmt::Display for ScopeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScopeRange {
pub start_line: u32,
pub end_line: u32,
pub kind: ScopeKind,
pub display_text: String,
pub name: Option<String>,
}
impl ScopeRange {
#[must_use]
pub fn new(
start_line: u32,
end_line: u32,
kind: ScopeKind,
display_text: impl Into<String>,
name: Option<String>,
) -> Self {
Self {
start_line,
end_line,
kind,
display_text: display_text.into(),
name,
}
}
#[must_use]
pub const fn contains_line(&self, line: u32) -> bool {
line >= self.start_line && line <= self.end_line
}
#[must_use]
pub const fn line_count(&self) -> u32 {
self.end_line - self.start_line + 1
}
#[must_use]
pub const fn is_multiline(&self) -> bool {
self.end_line > self.start_line
}
}
#[derive(Debug, Clone, Default)]
pub struct ContextHierarchy {
pub items: Vec<ScopeRange>,
pub buffer_id: usize,
pub line: u32,
pub col: u32,
}
impl ContextHierarchy {
#[must_use]
pub const fn new(buffer_id: usize, line: u32, col: u32, items: Vec<ScopeRange>) -> Self {
Self {
items,
buffer_id,
line,
col,
}
}
#[must_use]
pub const fn empty() -> Self {
Self {
items: Vec::new(),
buffer_id: 0,
line: 0,
col: 0,
}
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.items.is_empty()
}
#[must_use]
pub const fn len(&self) -> usize {
self.items.len()
}
#[must_use]
pub fn current_scope(&self) -> Option<&ScopeRange> {
self.items.last()
}
#[must_use]
pub fn to_breadcrumb(&self, separator: &str) -> String {
self.items
.iter()
.map(|s| s.display_text.as_str())
.collect::<Vec<_>>()
.join(separator)
}
#[must_use]
pub fn to_breadcrumb_max(&self, separator: &str, max: usize) -> String {
if self.items.len() <= max {
return self.to_breadcrumb(separator);
}
let skip = self.items.len() - max;
let mut parts = vec!["..."];
parts.extend(self.items[skip..].iter().map(|s| s.display_text.as_str()));
parts.join(separator)
}
}
#[cfg(test)]
#[path = "scope_tests.rs"]
mod tests;