summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlaž Hrastnik2021-03-24 09:17:00 +0000
committerBlaž Hrastnik2021-03-24 09:17:00 +0000
commit9a36d2c2a8c4c7c3932de59d1ec145faf3301f2d (patch)
tree5a9a98879c6a87c5fdeff7370999a6f70af71299
parent025d63bc3065529b68945816e9652c779e4be862 (diff)
wip: Hooks & trigger characters for completion/signature_help.
-rw-r--r--helix-core/src/auto_pairs.rs26
-rw-r--r--helix-lsp/src/client.rs6
-rw-r--r--helix-term/src/commands.rs105
-rw-r--r--helix-term/src/keymap.rs2
4 files changed, 130 insertions, 9 deletions
diff --git a/helix-core/src/auto_pairs.rs b/helix-core/src/auto_pairs.rs
index 7bb87e14..cd052489 100644
--- a/helix-core/src/auto_pairs.rs
+++ b/helix-core/src/auto_pairs.rs
@@ -28,13 +28,11 @@ const CLOSE_BEFORE: &str = ")]}'\":;> \n"; // includes space and newline
pub fn hook(doc: &Rope, selection: &Selection, ch: char) -> Option<Transaction> {
for &(open, close) in PAIRS {
if open == ch {
- let t = if open == close {
- return None;
- // handle_same()
+ if open == close {
+ return handle_same(doc, selection, open);
} else {
- handle_open(doc, selection, open, close, CLOSE_BEFORE)
- };
- return Some(t);
+ return Some(handle_open(doc, selection, open, close, CLOSE_BEFORE));
+ }
}
if close == ch {
@@ -115,7 +113,21 @@ fn handle_close(doc: &Rope, selection: &Selection, _open: char, close: char) ->
}
// handle cases where open and close is the same, or in triples ("""docstring""")
-fn handle_same() {
+fn handle_same(doc: &Rope, selection: &Selection, token: char) -> Option<Transaction> {
// if not cursor but selection, wrap
// let next = next char
+
+ // if next == bracket {
+ // // if start of syntax node, insert token twice (new pair because node is complete)
+ // // elseif colsedBracketAt
+ // // is_triple == allow triple && next 3 is equal
+ // // cursor jump over
+ // }
+ //} else if allow_triple && followed by triple {
+ //}
+ //} else if next != word char && prev != bracket && prev != word char {
+ // // condition checks for cases like I' where you don't want I'' (or I'm)
+ // insert pair ("")
+ //}
+ None
}
diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs
index 672247a9..7c9de90c 100644
--- a/helix-lsp/src/client.rs
+++ b/helix-lsp/src/client.rs
@@ -85,6 +85,12 @@ impl Client {
}
}
+ pub fn capabilities(&self) -> &lsp::ServerCapabilities {
+ self.capabilities
+ .as_ref()
+ .expect("language server not yet initialized!")
+ }
+
/// Execute a RPC request on the language server.
pub async fn request<R: lsp::request::Request>(&self, params: R::Params) -> Result<R::Result>
where
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 24fb1709..dd7de06e 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -1122,14 +1122,106 @@ pub fn goto_reference(cx: &mut Context) {
goto(cx, res);
}
+pub fn signature_help(cx: &mut Context) {
+ let doc = cx.doc();
+
+ let language_server = match doc.language_server() {
+ Some(language_server) => language_server,
+ None => return,
+ };
+
+ // TODO: blocking here is not ideal
+ let pos = helix_lsp::util::pos_to_lsp_pos(doc.text(), doc.selection().cursor());
+
+ // TODO: handle fails
+
+ let res = smol::block_on(language_server.text_document_signature_help(doc.identifier(), pos))
+ .unwrap_or_default();
+
+ if let Some(signature_help) = res {
+ log::info!("{:?}", signature_help);
+ // signatures
+ // active_signature
+ // active_parameter
+ // render as:
+
+ // signature
+ // ----------
+ // doc
+
+ // with active param highlighted
+ }
+}
+
// NOTE: Transactions in this module get appended to history when we switch back to normal mode.
pub mod insert {
use super::*;
pub type Hook = fn(&Rope, &Selection, char) -> Option<Transaction>;
+ pub type PostHook = fn(&mut Context, char);
use helix_core::auto_pairs;
const HOOKS: &[Hook] = &[auto_pairs::hook];
+ fn completion(cx: &mut Context, ch: char) {
+ // if ch matches completion char, trigger completion
+ let doc = cx.doc();
+ let language_server = match doc.language_server() {
+ Some(language_server) => language_server,
+ None => return,
+ };
+
+ let capabilities = language_server.capabilities();
+
+ if let lsp::ServerCapabilities {
+ completion_provider:
+ Some(lsp::CompletionOptions {
+ trigger_characters: Some(triggers),
+ ..
+ }),
+ ..
+ } = capabilities
+ {
+ // TODO: what if trigger is multiple chars long
+ let is_trigger = triggers.iter().any(|trigger| trigger.contains(ch));
+
+ if is_trigger {
+ super::completion(cx);
+ }
+ }
+ }
+
+ // TODO: the pre-hook handles ( so post hook never gets called
+ fn signature_help(cx: &mut Context, ch: char) {
+ // if ch matches signature_help char, trigger
+ let doc = cx.doc();
+ let language_server = match doc.language_server() {
+ Some(language_server) => language_server,
+ None => return,
+ };
+
+ let capabilities = language_server.capabilities();
+
+ if let lsp::ServerCapabilities {
+ signature_help_provider:
+ Some(lsp::SignatureHelpOptions {
+ trigger_characters: Some(triggers),
+ // TODO: retrigger_characters
+ ..
+ }),
+ ..
+ } = capabilities
+ {
+ // TODO: what if trigger is multiple chars long
+ let is_trigger = triggers.iter().any(|trigger| trigger.contains(ch));
+
+ if is_trigger {
+ super::signature_help(cx);
+ }
+ }
+ }
+
+ const POST_HOOKS: &[PostHook] = &[completion, signature_help];
+
// TODO: insert means add text just before cursor, on exit we should be on the last letter.
pub fn insert_char(cx: &mut Context, c: char) {
let doc = cx.doc();
@@ -1142,10 +1234,17 @@ pub mod insert {
}
}
- let c = Tendril::from_char(c);
- let transaction = Transaction::insert(doc.text(), doc.selection(), c);
+ let t = Tendril::from_char(c);
+ let transaction = Transaction::insert(doc.text(), doc.selection(), t);
doc.apply(&transaction);
+
+ // TODO: need a post insert hook too for certain triggers (autocomplete, signature help, etc)
+ // this could also generically look at Transaction, but it's a bit annoying to look at
+ // Operation instead of Change.
+ for hook in POST_HOOKS {
+ hook(cx, c);
+ }
}
pub fn insert_tab(cx: &mut Context) {
@@ -1162,6 +1261,7 @@ pub mod insert {
let doc = cx.doc();
let text = doc.text().slice(..);
let transaction = Transaction::change_by_selection(doc.text(), doc.selection(), |range| {
+ // TODO: offset range.head by 1? when calculating?
let indent_level =
helix_core::indent::suggested_indent_for_pos(doc.syntax(), text, range.head, true);
let indent = doc.indent_unit().repeat(indent_level);
@@ -1514,6 +1614,7 @@ pub fn completion(cx: &mut Context) {
//
// or we could simply use doc.undo + apply when changing between options
+ // always present here
let item = item.unwrap();
use helix_lsp::{lsp, util};
diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs
index 824d469d..aa1f8cce 100644
--- a/helix-term/src/keymap.rs
+++ b/helix-term/src/keymap.rs
@@ -196,7 +196,9 @@ pub fn default() -> Keymaps {
key!('[') => commands::expand_selection,
key!('/') => commands::search,
+ // ? for search_reverse
key!('n') => commands::search_next,
+ // N for search_prev
key!('*') => commands::search_selection,
key!('u') => commands::undo,