on master help sign in
wip: Hooks & trigger characters for completion/signature_help.
commit9a36d2c2a8c4c7c3932de59d1ec145faf3301f2d authorBlaž Hrastnik <blaz@mxxn.io> dateWed 24 Mar 2021, 18:17 (6 years ago) parent025d63b
4 files changed+130−9
helix-core/src/auto_pairs.rs+19−7 helix-lsp/src/client.rs+6 helix-term/src/commands.rs+103−2 helix-term/src/keymap.rs+2
helix-core/src/auto_pairs.rs+19−7
@@ -28,13 +28,11 @@ const CLOSE_BEFORE: &str = ")]}'\":;> \n"; // includes space and newline
2828pub fn hook(doc: &Rope, selection: &Selection, ch: char) -> Option<Transaction> {
2929 for &(open, close) in PAIRS {
3030 if open == ch {
31− let t = if open == close {
32− return None;
33− // handle_same()
31+ if open == close {
32+ return handle_same(doc, selection, open);
3433 } else {
35− handle_open(doc, selection, open, close, CLOSE_BEFORE)
36− };
37− return Some(t);
34+ return Some(handle_open(doc, selection, open, close, CLOSE_BEFORE));
35+ }
3836 }
3937
4038 if close == ch {
@@ -115,7 +113,21 @@ fn handle_close(doc: &Rope, selection: &Selection, _open: char, close: char) ->
115113}
116114
117115// handle cases where open and close is the same, or in triples ("""docstring""")
118−fn handle_same() {
116+fn handle_same(doc: &Rope, selection: &Selection, token: char) -> Option<Transaction> {
119117 // if not cursor but selection, wrap
120118 // let next = next char
119+
120+ // if next == bracket {
121+ // // if start of syntax node, insert token twice (new pair because node is complete)
122+ // // elseif colsedBracketAt
123+ // // is_triple == allow triple && next 3 is equal
124+ // // cursor jump over
125+ // }
126+ //} else if allow_triple && followed by triple {
127+ //}
128+ //} else if next != word char && prev != bracket && prev != word char {
129+ // // condition checks for cases like I' where you don't want I'' (or I'm)
130+ // insert pair ("")
131+ //}
132+ None
121133}
helix-lsp/src/client.rs+6
@@ -85,6 +85,12 @@ impl Client {
8585 }
8686 }
8787
88+ pub fn capabilities(&self) -> &lsp::ServerCapabilities {
89+ self.capabilities
90+ .as_ref()
91+ .expect("language server not yet initialized!")
92+ }
93+
8894 /// Execute a RPC request on the language server.
8995 pub async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result>
9096 where
helix-term/src/commands.rs+103−2
@@ -1122,14 +1122,106 @@ pub fn goto_reference(cx: &mut Context) {
11221122 goto(cx, res);
11231123}
11241124
1125+pub fn signature_help(cx: &mut Context) {
1126+ let doc = cx.doc();
1127+
1128+ let language_server = match doc.language_server() {
1129+ Some(language_server) => language_server,
1130+ None => return,
1131+ };
1132+
1133+ // TODO: blocking here is not ideal
1134+ let pos = helix_lsp::util::pos_to_lsp_pos(doc.text(), doc.selection().cursor());
1135+
1136+ // TODO: handle fails
1137+
1138+ let res = smol::block_on(language_server.text_document_signature_help(doc.identifier(), pos))
1139+ .unwrap_or_default();
1140+
1141+ if let Some(signature_help) = res {
1142+ log::info!("{:?}", signature_help);
1143+ // signatures
1144+ // active_signature
1145+ // active_parameter
1146+ // render as:
1147+
1148+ // signature
1149+ // ----------
1150+ // doc
1151+
1152+ // with active param highlighted
1153+ }
1154+}
1155+
11251156// NOTE: Transactions in this module get appended to history when we switch back to normal mode.
11261157pub mod insert {
11271158 use super::*;
11281159 pub type Hook = fn(&Rope, &Selection, char) -> Option<Transaction>;
1160+ pub type PostHook = fn(&mut Context, char);
11291161
11301162 use helix_core::auto_pairs;
11311163 const HOOKS: &[Hook] = &[auto_pairs::hook];
11321164
1165+ fn completion(cx: &mut Context, ch: char) {
1166+ // if ch matches completion char, trigger completion
1167+ let doc = cx.doc();
1168+ let language_server = match doc.language_server() {
1169+ Some(language_server) => language_server,
1170+ None => return,
1171+ };
1172+
1173+ let capabilities = language_server.capabilities();
1174+
1175+ if let lsp::ServerCapabilities {
1176+ completion_provider:
1177+ Some(lsp::CompletionOptions {
1178+ trigger_characters: Some(triggers),
1179+ ..
1180+ }),
1181+ ..
1182+ } = capabilities
1183+ {
1184+ // TODO: what if trigger is multiple chars long
1185+ let is_trigger = triggers.iter().any(|trigger| trigger.contains(ch));
1186+
1187+ if is_trigger {
1188+ super::completion(cx);
1189+ }
1190+ }
1191+ }
1192+
1193+ // TODO: the pre-hook handles ( so post hook never gets called
1194+ fn signature_help(cx: &mut Context, ch: char) {
1195+ // if ch matches signature_help char, trigger
1196+ let doc = cx.doc();
1197+ let language_server = match doc.language_server() {
1198+ Some(language_server) => language_server,
1199+ None => return,
1200+ };
1201+
1202+ let capabilities = language_server.capabilities();
1203+
1204+ if let lsp::ServerCapabilities {
1205+ signature_help_provider:
1206+ Some(lsp::SignatureHelpOptions {
1207+ trigger_characters: Some(triggers),
1208+ // TODO: retrigger_characters
1209+ ..
1210+ }),
1211+ ..
1212+ } = capabilities
1213+ {
1214+ // TODO: what if trigger is multiple chars long
1215+ let is_trigger = triggers.iter().any(|trigger| trigger.contains(ch));
1216+
1217+ if is_trigger {
1218+ super::signature_help(cx);
1219+ }
1220+ }
1221+ }
1222+
1223+ const POST_HOOKS: &[PostHook] = &[completion, signature_help];
1224+
11331225 // TODO: insert means add text just before cursor, on exit we should be on the last letter.
11341226 pub fn insert_char(cx: &mut Context, c: char) {
11351227 let doc = cx.doc();
@@ -1142,10 +1234,17 @@ pub mod insert {
11421234 }
11431235 }
11441236
1145− let c = Tendril::from_char(c);
1146− let transaction = Transaction::insert(doc.text(), doc.selection(), c);
1237+ let t = Tendril::from_char(c);
1238+ let transaction = Transaction::insert(doc.text(), doc.selection(), t);
11471239
11481240 doc.apply(&transaction);
1241+
1242+ // TODO: need a post insert hook too for certain triggers (autocomplete, signature help, etc)
1243+ // this could also generically look at Transaction, but it's a bit annoying to look at
1244+ // Operation instead of Change.
1245+ for hook in POST_HOOKS {
1246+ hook(cx, c);
1247+ }
11491248 }
11501249
11511250 pub fn insert_tab(cx: &mut Context) {
@@ -1162,6 +1261,7 @@ pub mod insert {
11621261 let doc = cx.doc();
11631262 let text = doc.text().slice(..);
11641263 let transaction = Transaction::change_by_selection(doc.text(), doc.selection(), |range| {
1264+ // TODO: offset range.head by 1? when calculating?
11651265 let indent_level =
11661266 helix_core::indent::suggested_indent_for_pos(doc.syntax(), text, range.head, true);
11671267 let indent = doc.indent_unit().repeat(indent_level);
@@ -1514,6 +1614,7 @@ pub fn completion(cx: &mut Context) {
15141614 //
15151615 // or we could simply use doc.undo + apply when changing between options
15161616
1617+ // always present here
15171618 let item = item.unwrap();
15181619
15191620 use helix_lsp::{lsp, util};
helix-term/src/keymap.rs+2
@@ -196,7 +196,9 @@ pub fn default() -> Keymaps {
196196 key!('[') => commands::expand_selection,
197197
198198 key!('/') => commands::search,
199+ // ? for search_reverse
199200 key!('n') => commands::search_next,
201+ // N for search_prev
200202 key!('*') => commands::search_selection,
201203
202204 key!('u') => commands::undo,