on master help sign in
add symbol picker (#230)

* add symbol picker use the lsp document_symbol request * fix errors from merging in master * add docs for symbol picker

commit9baf1ecc90117710e785467f59a99ee119f50c73 authorRobin <robin@robinjint.nl> dateSat 12 Jun 2021, 14:45 (5 years ago) parent44cc0d8
4 files changed+97−1
book/src/keymap.md+1 helix-lsp/src/client.rs+13 helix-lsp/src/lib.rs+11 helix-term/src/commands.rs+72−1
book/src/keymap.md+1
@@ -166,5 +166,6 @@ This layer is a kludge of mappings I had under leader key in neovim.
166166|-----|-----------|
167167| f | Open file picker |
168168| b | Open buffer picker |
169+| s | Open symbol picker (current document)|
169170| w | Enter window mode |
170171| space | Keep primary selection TODO: it's here because space mode replaced it |
helix-lsp/src/client.rs+13
@@ -660,4 +660,17 @@ impl Client {
660660
661661 self.call::<lsp::request::References>(params)
662662 }
663+
664+ pub fn document_symbols(
665+ &self,
666+ text_document: lsp::TextDocumentIdentifier,
667+ ) -> impl Future<Output = Result<Value>> {
668+ let params = lsp::DocumentSymbolParams {
669+ text_document,
670+ work_done_progress_params: lsp::WorkDoneProgressParams::default(),
671+ partial_result_params: lsp::PartialResultParams::default(),
672+ };
673+
674+ self.call::<lsp::request::DocumentSymbolRequest>(params)
675+ }
663676}
helix-lsp/src/lib.rs+11
@@ -138,6 +138,17 @@ pub mod util {
138138 lsp::Range::new(start, end)
139139 }
140140
141+ pub fn lsp_range_to_range(
142+ doc: &Rope,
143+ range: lsp::Range,
144+ offset_encoding: OffsetEncoding,
145+ ) -> Option<Range> {
146+ let start = lsp_pos_to_pos(doc, range.start, offset_encoding)?;
147+ let end = lsp_pos_to_pos(doc, range.end, offset_encoding)?;
148+
149+ Some(Range::new(start, end))
150+ }
151+
141152 pub fn generate_transaction_from_edits(
142153 doc: &Rope,
143154 edits: Vec<lsp::TextEdit>,
helix-term/src/commands.rs+72−1
@@ -16,7 +16,7 @@ use helix_view::{
1616
1717use helix_lsp::{
1818 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},
2020 OffsetEncoding,
2121};
2222use movement::Movement;
@@ -1194,6 +1194,76 @@ pub fn buffer_picker(cx: &mut Context) {
11941194 cx.push_layer(Box::new(picker));
11951195}
11961196
1197+pub fn symbol_picker(cx: &mut Context) {
1198+ fn nested_to_flat(
1199+ list: &mut Vec<lsp::SymbolInformation>,
1200+ file: &lsp::TextDocumentIdentifier,
1201+ symbol: lsp::DocumentSymbol,
1202+ ) {
1203+ #[allow(deprecated)]
1204+ list.push(lsp::SymbolInformation {
1205+ name: symbol.name,
1206+ kind: symbol.kind,
1207+ tags: symbol.tags,
1208+ deprecated: symbol.deprecated,
1209+ location: lsp::Location::new(file.uri.clone(), symbol.selection_range),
1210+ container_name: None,
1211+ });
1212+ for child in symbol.children.into_iter().flatten() {
1213+ nested_to_flat(list, file, child);
1214+ }
1215+ }
1216+ let (view, doc) = cx.current();
1217+
1218+ let language_server = match doc.language_server() {
1219+ Some(language_server) => language_server,
1220+ None => return,
1221+ };
1222+ let offset_encoding = language_server.offset_encoding();
1223+
1224+ let future = language_server.document_symbols(doc.identifier());
1225+
1226+ cx.callback(
1227+ future,
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)
1241+ }
1242+ flat_symbols
1243+ }
1244+ };
1245+
1246+ let picker = Picker::new(
1247+ symbols,
1248+ |symbol| (&symbol.name).into(),
1249+ move |editor: &mut Editor, symbol, _action| {
1250+ push_jump(editor);
1251+ let (view, doc) = editor.current();
1252+
1253+ if let Some(range) =
1254+ lsp_range_to_range(doc.text(), symbol.location.range, offset_encoding)
1255+ {
1256+ doc.set_selection(view.id, Selection::single(range.to(), range.from()));
1257+ align_view(doc, view, Align::Center);
1258+ }
1259+ },
1260+ );
1261+ compositor.push(Box::new(picker))
1262+ }
1263+ },
1264+ )
1265+}
1266+
11971267// I inserts at the first nonwhitespace character of each line with a selection
11981268pub fn prepend_to_line(cx: &mut Context) {
11991269 move_first_nonwhitespace(cx);
@@ -2548,6 +2618,7 @@ pub fn space_mode(cx: &mut Context) {
25482618 match ch {
25492619 'f' => file_picker(cx),
25502620 'b' => buffer_picker(cx),
2621+ 's' => symbol_picker(cx),
25512622 'w' => window_mode(cx),
25522623 // ' ' => toggle_alternate_buffer(cx),
25532624 // TODO: temporary since space mode took its old key