@@ -16,7 +16,7 @@ use helix_view::{
17use helix_lsp::{
17use helix_lsp::{
19− util::{lsp_pos_to_pos, pos_to_lsp_pos, range_to_lsp_range},
19+ util::{lsp_pos_to_pos, lsp_range_to_range, pos_to_lsp_pos, range_to_lsp_range},
20 OffsetEncoding,
20 OffsetEncoding,
22use movement::Movement;
22use movement::Movement;
@@ -1194,6 +1194,76 @@ pub fn buffer_picker(cx: &mut Context) {
1194 cx.push_layer(Box::new(picker));
1194 cx.push_layer(Box::new(picker));
1197+pub fn symbol_picker(cx: &mut Context) {
1199+ list: &mut Vec<lsp::SymbolInformation>,
1200+ file: &lsp::TextDocumentIdentifier,
1201+ symbol: lsp::DocumentSymbol,
1203+ #[allow(deprecated)]
1204+ list.push(lsp::SymbolInformation {
1208+ deprecated: symbol.deprecated,
1209+ location: lsp::Location::new(file.uri.clone(), symbol.selection_range),
1210+ container_name: None,
1212+ for child in symbol.children.into_iter().flatten() {
1213+ nested_to_flat(list, file, child);
1216+ let (view, doc) = cx.current();
1218+ let language_server = match doc.language_server() {
1219+ Some(language_server) => language_server,
1222+ let offset_encoding = language_server.offset_encoding();
1224+ let future = language_server.document_symbols(doc.identifier());
1228+ move |editor: &mut Editor,
1229+ compositor: &mut Compositor,
1230+ response: Option<lsp::DocumentSymbolResponse>| {
1231+ if let Some(symbols) = response {
1232+ // lsp has two ways to represent symbols (flat/nested)
1233+ // convert the nested variant to flat, so that we have a homogeneous list
1234+ let symbols = match symbols {
1235+ lsp::DocumentSymbolResponse::Flat(symbols) => symbols,
1236+ lsp::DocumentSymbolResponse::Nested(symbols) => {
1237+ let (_view, doc) = editor.current();
1238+ let mut flat_symbols = Vec::new();
1239+ for symbol in symbols {
1240+ nested_to_flat(&mut flat_symbols, &doc.identifier(), symbol)
1246+ let picker = Picker::new(
1248+ |symbol| (&symbol.name).into(),
1249+ move |editor: &mut Editor, symbol, _action| {
1251+ let (view, doc) = editor.current();
1253+ if let Some(range) =
1254+ lsp_range_to_range(doc.text(), symbol.location.range, offset_encoding)
1256+ doc.set_selection(view.id, Selection::single(range.to(), range.from()));
1257+ align_view(doc, view, Align::Center);
1261+ compositor.push(Box::new(picker))
1197// I inserts at the first nonwhitespace character of each line with a selection
1267// I inserts at the first nonwhitespace character of each line with a selection
1198pub fn prepend_to_line(cx: &mut Context) {
1268pub fn prepend_to_line(cx: &mut Context) {
1199 move_first_nonwhitespace(cx);
1269 move_first_nonwhitespace(cx);
@@ -2548,6 +2618,7 @@ pub fn space_mode(cx: &mut Context) {
2548 match ch {
2618 match ch {
2549 'f' => file_picker(cx),
2619 'f' => file_picker(cx),
2550 'b' => buffer_picker(cx),
2620 'b' => buffer_picker(cx),
2621+ 's' => symbol_picker(cx),
2551 'w' => window_mode(cx),
2622 'w' => window_mode(cx),
2552 // ' ' => toggle_alternate_buffer(cx),
2623 // ' ' => toggle_alternate_buffer(cx),
2553 // TODO: temporary since space mode took its old key
2624 // TODO: temporary since space mode took its old key