f0a1552
ZhenYi
feat(chat): add chat module with room management, message handling, and session revocation
f0a1552
use tree_sitter::{Language, Parser, TreeCursor};
#[derive(Debug, Clone)]
pub struct ParsedSymbol {
pub name: String,
pub kind: String,
pub line: u32,
pub end_line: u32,
pub is_definition: bool,
}
pub fn language_for_path(path: &str) -> Option<(&'static str, Language)> {
let name = path.rsplit('/').next().unwrap_or(path).to_ascii_lowercase();
let extension = name.rsplit_once('.').map(|(_, ext)| ext)?;
let language = match extension {
"rs" => ("Rust", tree_sitter_rust::LANGUAGE.into()),
"js" | "mjs" | "cjs" | "jsx" => ("JavaScript", tree_sitter_javascript::LANGUAGE.into()),
"ts" => (
"TypeScript",
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
),
"tsx" => ("TypeScript", tree_sitter_typescript::LANGUAGE_TSX.into()),
"py" => ("Python", tree_sitter_python::LANGUAGE.into()),
"go" => ("Go", tree_sitter_go::LANGUAGE.into()),
"java" => ("Java", tree_sitter_java::LANGUAGE.into()),
"c" | "h" => ("C", tree_sitter_c::LANGUAGE.into()),
"cc" | "cp" | "cpp" | "cxx" | "c++" | "hh" | "hpp" | "hxx" => {
("C++", tree_sitter_cpp::LANGUAGE.into())
}
_ => return None,
};
Some(language)
}
pub fn symbols_for(path: &str, text: &str) -> Vec<ParsedSymbol> {
let Some((_, language)) = language_for_path(path) else {
return Vec::new();
};
let mut parser = Parser::new();
if parser.set_language(&language).is_err() {
return Vec::new();
}
let Some(tree) = parser.parse(text, None) else {
return Vec::new();
};
let bytes = text.as_bytes();
let mut out = Vec::new();
let mut cursor = tree.root_node().walk();
visit(&mut cursor, bytes, &mut out);
out
}
fn visit(cursor: &mut TreeCursor<'_>, source: &[u8], out: &mut Vec<ParsedSymbol>) {
let node = cursor.node();
let kind = node.kind();
if is_definition_kind(kind) {
if let Some(name) = declaration_name(node, source) {
out.push(ParsedSymbol {
name,
kind: kind.to_string(),
line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
is_definition: true,
});
}
} else if is_identifier_kind(kind) {
if let Ok(name) = node.utf8_text(source) {
if !name.is_empty() {
out.push(ParsedSymbol {
name: name.to_string(),
kind: "reference".to_string(),
line: node.start_position().row as u32 + 1,
end_line: node.end_position().row as u32 + 1,
is_definition: false,
});
}
}
}
if cursor.goto_first_child() {
loop {
visit(cursor, source, out);
if !cursor.goto_next_sibling() {
break;
}
}
cursor.goto_parent();
}
}
fn declaration_name(node: tree_sitter::Node<'_>, source: &[u8]) -> Option<String> {
for field in ["name", "declarator", "type", "field"] {
if let Some(child) = node.child_by_field_name(field) {
if is_identifier_kind(child.kind()) {
return child.utf8_text(source).ok().map(ToOwned::to_owned);
}
if let Some(name) = first_identifier(child, source) {
return Some(name);
}
}
}
first_identifier(node, source)
}
fn first_identifier(node: tree_sitter::Node<'_>, source: &[u8]) -> Option<String> {
if is_identifier_kind(node.kind()) {
return node.utf8_text(source).ok().map(ToOwned::to_owned);
}
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
if let Some(name) = first_identifier(cursor.node(), source) {
return Some(name);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
None
}
fn is_identifier_kind(kind: &str) -> bool {
matches!(
kind,
"identifier" | "field_identifier" | "type_identifier" | "namespace_identifier"
)
}
fn is_definition_kind(kind: &str) -> bool {
matches!(
kind,
"function_item"
| "function_declaration"
| "function_definition"
| "method_definition"
| "method_declaration"
| "class_definition"
| "class_declaration"
| "interface_declaration"
| "struct_item"
| "struct_specifier"
| "enum_item"
| "enum_specifier"
| "trait_item"
| "impl_item"
| "type_item"
| "type_declaration"
| "module"
| "namespace_definition"
)
}