Add Buffer::outline method

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2022-01-12 18:17:19 -08:00
parent 057dc62b90
commit 63a401ac5d
11 changed files with 224 additions and 4 deletions

12
Cargo.lock generated
View File

@ -3121,6 +3121,17 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85"
[[package]]
name = "outline"
version = "0.1.0"
dependencies = [
"editor",
"gpui",
"postage",
"text",
"workspace",
]
[[package]]
name = "p256"
version = "0.9.0"
@ -5724,6 +5735,7 @@ dependencies = [
"log-panics",
"lsp",
"num_cpus",
"outline",
"parking_lot",
"postage",
"project",

View File

@ -7,7 +7,7 @@ use collections::{HashMap, HashSet};
use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
use language::{
Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, Language, Selection,
ToOffset as _, ToPoint as _, TransactionId,
ToOffset as _, ToPoint as _, TransactionId, Outline,
};
use std::{
cell::{Ref, RefCell},
@ -1698,6 +1698,10 @@ impl MultiBufferSnapshot {
})
}
pub fn outline(&self) -> Option<Outline> {
self.as_singleton().and_then(move |buffer| buffer.outline())
}
fn buffer_snapshot_for_excerpt<'a>(
&'a self,
excerpt_id: &'a ExcerptId,

View File

@ -6,7 +6,8 @@ pub use crate::{
};
use crate::{
diagnostic_set::{DiagnosticEntry, DiagnosticGroup},
range_from_lsp,
outline::OutlineItem,
range_from_lsp, Outline,
};
use anyhow::{anyhow, Result};
use clock::ReplicaId;
@ -193,7 +194,7 @@ pub trait File {
fn as_any(&self) -> &dyn Any;
}
struct QueryCursorHandle(Option<QueryCursor>);
pub(crate) struct QueryCursorHandle(Option<QueryCursor>);
#[derive(Clone)]
struct SyntaxTree {
@ -1264,6 +1265,13 @@ impl Buffer {
self.edit_internal(ranges_iter, new_text, true, cx)
}
/*
impl Buffer
pub fn edit
pub fn edit_internal
pub fn edit_with_autoindent
*/
pub fn edit_internal<I, S, T>(
&mut self,
ranges_iter: I,
@ -1827,6 +1835,82 @@ impl BufferSnapshot {
}
}
pub fn outline(&self) -> Option<Outline> {
let tree = self.tree.as_ref()?;
let grammar = self
.language
.as_ref()
.and_then(|language| language.grammar.as_ref())?;
let mut cursor = QueryCursorHandle::new();
let matches = cursor.matches(
&grammar.outline_query,
tree.root_node(),
TextProvider(self.as_rope()),
);
let item_capture_ix = grammar.outline_query.capture_index_for_name("item")?;
let context_capture_ix = grammar.outline_query.capture_index_for_name("context")?;
let name_capture_ix = grammar.outline_query.capture_index_for_name("name")?;
let mut id = 0;
let mut items = matches
.filter_map(|mat| {
let item_node = mat.nodes_for_capture_index(item_capture_ix).next()?;
let mut name_node = Some(mat.nodes_for_capture_index(name_capture_ix).next()?);
let mut context_nodes = mat.nodes_for_capture_index(context_capture_ix).peekable();
let id = post_inc(&mut id);
let range = item_node.start_byte()..item_node.end_byte();
let mut text = String::new();
let mut name_range_in_text = 0..0;
loop {
let node;
let node_is_name;
match (context_nodes.peek(), name_node.as_ref()) {
(None, None) => break,
(None, Some(_)) => {
node = name_node.take().unwrap();
node_is_name = true;
}
(Some(_), None) => {
node = context_nodes.next().unwrap();
node_is_name = false;
}
(Some(context_node), Some(name)) => {
if context_node.start_byte() < name.start_byte() {
node = context_nodes.next().unwrap();
node_is_name = false;
} else {
node = name_node.take().unwrap();
node_is_name = true;
}
}
}
if !text.is_empty() {
text.push(' ');
}
let range = node.start_byte()..node.end_byte();
if node_is_name {
name_range_in_text = text.len()..(text.len() + range.len())
}
text.extend(self.text_for_range(range));
}
Some(OutlineItem {
id,
range,
text,
name_range_in_text,
})
})
.collect::<Vec<_>>();
Some(Outline(items))
}
pub fn enclosing_bracket_ranges<T: ToOffset>(
&self,
range: Range<T>,
@ -1854,6 +1938,12 @@ impl BufferSnapshot {
.min_by_key(|(open_range, close_range)| close_range.end - open_range.start)
}
/*
impl BufferSnapshot
pub fn remote_selections_in_range(&self, Range<Anchor>) -> impl Iterator<Item = (ReplicaId, impl Iterator<Item = &Selection<Anchor>>)>
pub fn remote_selections_in_range(&self, Range<Anchor>) -> impl Iterator<Item = (ReplicaId, i
*/
pub fn remote_selections_in_range<'a>(
&'a self,
range: Range<Anchor>,
@ -2108,7 +2198,7 @@ impl<'a> Iterator for BufferChunks<'a> {
}
impl QueryCursorHandle {
fn new() -> Self {
pub(crate) fn new() -> Self {
QueryCursorHandle(Some(
QUERY_CURSORS
.lock()

View File

@ -1,6 +1,7 @@
mod buffer;
mod diagnostic_set;
mod highlight_map;
mod outline;
pub mod proto;
#[cfg(test)]
mod tests;
@ -13,6 +14,7 @@ pub use diagnostic_set::DiagnosticEntry;
use gpui::AppContext;
use highlight_map::HighlightMap;
use lazy_static::lazy_static;
pub use outline::Outline;
use parking_lot::Mutex;
use serde::Deserialize;
use std::{ops::Range, path::Path, str, sync::Arc};
@ -74,6 +76,7 @@ pub struct Grammar {
pub(crate) highlights_query: Query,
pub(crate) brackets_query: Query,
pub(crate) indents_query: Query,
pub(crate) outline_query: Query,
pub(crate) highlight_map: Mutex<HighlightMap>,
}
@ -127,6 +130,7 @@ impl Language {
brackets_query: Query::new(ts_language, "").unwrap(),
highlights_query: Query::new(ts_language, "").unwrap(),
indents_query: Query::new(ts_language, "").unwrap(),
outline_query: Query::new(ts_language, "").unwrap(),
ts_language,
highlight_map: Default::default(),
})
@ -164,6 +168,16 @@ impl Language {
Ok(self)
}
pub fn with_outline_query(mut self, source: &str) -> Result<Self> {
let grammar = self
.grammar
.as_mut()
.and_then(Arc::get_mut)
.ok_or_else(|| anyhow!("grammar does not exist or is already being used"))?;
grammar.outline_query = Query::new(grammar.ts_language, source)?;
Ok(self)
}
pub fn name(&self) -> &str {
self.config.name.as_str()
}

View File

@ -0,0 +1,12 @@
use std::ops::Range;
#[derive(Debug)]
pub struct Outline(pub Vec<OutlineItem>);
#[derive(Debug)]
pub struct OutlineItem {
pub id: usize,
pub range: Range<usize>,
pub text: String,
pub name_range_in_text: Range<usize>,
}

14
crates/outline/Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[package]
name = "outline"
version = "0.1.0"
edition = "2021"
[lib]
path = "src/outline.rs"
[dependencies]
text = { path = "../text" }
editor = { path = "../editor" }
gpui = { path = "../gpui" }
workspace = { path = "../workspace" }
postage = { version = "0.4", features = ["futures-traits"] }

View File

@ -0,0 +1,53 @@
use editor::{display_map::ToDisplayPoint, Autoscroll, Editor, EditorSettings};
use gpui::{
action, elements::*, geometry::vector::Vector2F, keymap::Binding, Axis, Entity,
MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
};
use postage::watch;
use std::sync::Arc;
use text::{Bias, Point, Selection};
use workspace::{Settings, Workspace};
action!(Toggle);
action!(Confirm);
pub fn init(cx: &mut MutableAppContext) {
cx.add_bindings([
Binding::new("cmd-shift-O", Toggle, Some("Editor")),
Binding::new("escape", Toggle, Some("GoToLine")),
Binding::new("enter", Confirm, Some("GoToLine")),
]);
cx.add_action(OutlineView::toggle);
cx.add_action(OutlineView::confirm);
}
struct OutlineView {}
impl Entity for OutlineView {
type Event = ();
}
impl View for OutlineView {
fn ui_name() -> &'static str {
"OutlineView"
}
fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox {
todo!()
}
}
impl OutlineView {
fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
let editor = workspace
.active_item(cx)
.unwrap()
.to_any()
.downcast::<Editor>()
.unwrap();
let buffer = editor.read(cx).buffer().read(cx);
dbg!(buffer.read(cx).outline());
}
fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {}
}

View File

@ -43,6 +43,7 @@ gpui = { path = "../gpui" }
journal = { path = "../journal" }
language = { path = "../language" }
lsp = { path = "../lsp" }
outline = { path = "../outline" }
project = { path = "../project" }
project_panel = { path = "../project_panel" }
rpc = { path = "../rpc" }

View File

@ -0,0 +1,17 @@
(impl_item
"impl" @context
type: (_) @name) @item
(function_item
(visibility_modifier)? @context
"fn" @context
name: (identifier) @name) @item
(struct_item
(visibility_modifier)? @context
"struct" @context
name: (type_identifier) @name) @item
(field_declaration
(visibility_modifier)? @context
name: (field_identifier) @name) @item

View File

@ -24,6 +24,8 @@ fn rust() -> Language {
.unwrap()
.with_indents_query(load_query("rust/indents.scm").as_ref())
.unwrap()
.with_outline_query(load_query("rust/outline.scm").as_ref())
.unwrap()
}
fn markdown() -> Language {

View File

@ -59,6 +59,7 @@ fn main() {
go_to_line::init(cx);
file_finder::init(cx);
chat_panel::init(cx);
outline::init(cx);
project_panel::init(cx);
diagnostics::init(cx);
cx.spawn({