Detailed changes
@@ -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",
@@ -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,
@@ -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()
@@ -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()
}
@@ -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>,
+}
@@ -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"] }
@@ -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>) {}
+}
@@ -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" }
@@ -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
@@ -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 {
@@ -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({