mirror of https://github.com/helix-editor/helix
145 lines
4.2 KiB
Rust
145 lines
4.2 KiB
Rust
use helix_core::{tree_sitter::Query, Selection, Uri};
|
|
use helix_view::{align_view, Align, DocumentId};
|
|
|
|
use crate::ui::{overlay::overlaid, picker::PathOrId, Picker, PickerColumn};
|
|
|
|
use super::Context;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum SymbolKind {
|
|
Function,
|
|
Macro,
|
|
Module,
|
|
Constant,
|
|
Struct,
|
|
Interface,
|
|
Type,
|
|
Class,
|
|
}
|
|
|
|
impl SymbolKind {
|
|
fn as_str(&self) -> &'static str {
|
|
match self {
|
|
Self::Function => "function",
|
|
Self::Macro => "macro",
|
|
Self::Module => "module",
|
|
Self::Constant => "constant",
|
|
Self::Struct => "struct",
|
|
Self::Interface => "interface",
|
|
Self::Type => "type",
|
|
Self::Class => "class",
|
|
}
|
|
}
|
|
}
|
|
|
|
fn definition_symbol_kind_for_capture(symbols: &Query, capture_index: usize) -> Option<SymbolKind> {
|
|
match symbols.capture_names()[capture_index] {
|
|
"definition.function" => Some(SymbolKind::Function),
|
|
"definition.macro" => Some(SymbolKind::Macro),
|
|
"definition.module" => Some(SymbolKind::Module),
|
|
"definition.constant" => Some(SymbolKind::Constant),
|
|
"definition.struct" => Some(SymbolKind::Struct),
|
|
"definition.interface" => Some(SymbolKind::Interface),
|
|
"definition.type" => Some(SymbolKind::Type),
|
|
"definition.class" => Some(SymbolKind::Class),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
// NOTE: Uri is cheap to clone and DocumentId is Copy
|
|
#[derive(Debug, Clone)]
|
|
enum UriOrDocumentId {
|
|
// TODO: the workspace symbol picker will take advantage of this.
|
|
#[allow(dead_code)]
|
|
Uri(Uri),
|
|
Id(DocumentId),
|
|
}
|
|
|
|
impl UriOrDocumentId {
|
|
fn path_or_id(&self) -> Option<PathOrId<'_>> {
|
|
match self {
|
|
Self::Id(id) => Some(PathOrId::Id(*id)),
|
|
Self::Uri(uri) => uri.as_path().map(PathOrId::Path),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Symbol {
|
|
kind: SymbolKind,
|
|
name: String,
|
|
start: usize,
|
|
end: usize,
|
|
start_line: usize,
|
|
end_line: usize,
|
|
doc: UriOrDocumentId,
|
|
}
|
|
|
|
pub fn syntax_symbol_picker(cx: &mut Context) {
|
|
let doc = doc!(cx.editor);
|
|
let Some((syntax, lang_config)) = doc.syntax().zip(doc.language_config()) else {
|
|
cx.editor
|
|
.set_error("Syntax tree is not available on this buffer");
|
|
return;
|
|
};
|
|
let Some(symbols_query) = lang_config.symbols_query() else {
|
|
cx.editor
|
|
.set_error("Syntax-based symbols information not available for this language");
|
|
return;
|
|
};
|
|
|
|
let doc_id = doc.id();
|
|
let text = doc.text();
|
|
|
|
let columns = vec![
|
|
PickerColumn::new("kind", |symbol: &Symbol, _| symbol.kind.as_str().into()),
|
|
PickerColumn::new("name", |symbol: &Symbol, _| symbol.name.as_str().into()),
|
|
];
|
|
|
|
let symbols = syntax
|
|
.captures(symbols_query, text.slice(..), None)
|
|
.filter_map(move |(match_, capture_index)| {
|
|
let capture = match_.captures[capture_index];
|
|
let kind = definition_symbol_kind_for_capture(symbols_query, capture.index as usize)?;
|
|
let node = capture.node;
|
|
let start = text.byte_to_char(node.start_byte());
|
|
let end = text.byte_to_char(node.end_byte());
|
|
|
|
Some(Symbol {
|
|
kind,
|
|
name: text.slice(start..end).to_string(),
|
|
start,
|
|
end,
|
|
|
|
start_line: text.char_to_line(start),
|
|
end_line: text.char_to_line(end),
|
|
doc: UriOrDocumentId::Id(doc_id),
|
|
})
|
|
});
|
|
|
|
let picker = Picker::new(
|
|
columns,
|
|
1, // name
|
|
symbols,
|
|
(),
|
|
move |cx, symbol, action| {
|
|
cx.editor.switch(doc_id, action);
|
|
let view = view_mut!(cx.editor);
|
|
let doc = doc_mut!(cx.editor, &doc_id);
|
|
doc.set_selection(view.id, Selection::single(symbol.start, symbol.end));
|
|
if action.align_view(view, doc.id()) {
|
|
align_view(doc, view, Align::Center)
|
|
}
|
|
},
|
|
)
|
|
.with_preview(|_editor, symbol| {
|
|
Some((
|
|
symbol.doc.path_or_id()?,
|
|
Some((symbol.start_line, symbol.end_line)),
|
|
))
|
|
})
|
|
.truncate_start(false);
|
|
|
|
cx.push_layer(Box::new(overlaid(picker)));
|
|
}
|