aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.md1
-rw-r--r--helix-lsp/src/client.rs67
-rw-r--r--helix-lsp/src/lib.rs9
-rw-r--r--helix-term/src/commands.rs41
-rw-r--r--helix-term/src/keymap.rs1
5 files changed, 117 insertions, 2 deletions
diff --git a/TODO.md b/TODO.md
index 9f20ebb2..67b4df97 100644
--- a/TODO.md
+++ b/TODO.md
@@ -17,7 +17,6 @@
- [x] retain horiz when moving vertically
- [w] retain horiz when moving via ctrl-u/d
- [x] deindent
-- [ ] ensure_cursor_in_view always before rendering? or always in app after event process?
- [x] update lsp on redo/undo
- [ ] Implement marks (superset of Selection/Range)
- [ ] ctrl-v/ctrl-x on file picker
diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs
index 77e877e4..45998c8c 100644
--- a/helix-lsp/src/client.rs
+++ b/helix-lsp/src/client.rs
@@ -506,4 +506,71 @@ impl Client {
Ok(response)
}
+
+ // formatting
+
+ pub async fn text_document_formatting(
+ &self,
+ text_document: lsp::TextDocumentIdentifier,
+ options: lsp::FormattingOptions,
+ ) -> anyhow::Result<Vec<lsp::TextEdit>> {
+ let capabilities = self.capabilities.as_ref().unwrap(); // TODO: needs post init
+
+ // check if we're able to format
+ let _capabilities = match capabilities.document_formatting_provider {
+ Some(lsp::OneOf::Left(true)) => (),
+ Some(lsp::OneOf::Right(_)) => (),
+ // None | Some(false)
+ _ => return Ok(Vec::new()),
+ };
+ // TODO: return err::unavailable so we can fall back to tree sitter formatting
+
+ let params = lsp::DocumentFormattingParams {
+ text_document,
+ options,
+ // TODO: support these tokens
+ work_done_progress_params: lsp::WorkDoneProgressParams {
+ work_done_token: None,
+ },
+ };
+
+ let response = self.request::<lsp::request::Formatting>(params).await?;
+
+ Ok(response.unwrap_or_default())
+ }
+
+ pub async fn text_document_range_formatting(
+ &self,
+ text_document: lsp::TextDocumentIdentifier,
+ range: lsp::Range,
+ options: lsp::FormattingOptions,
+ ) -> anyhow::Result<Vec<lsp::TextEdit>> {
+ let capabilities = self.capabilities.as_ref().unwrap(); // TODO: needs post init
+
+ log::info!("{:?}", capabilities.document_range_formatting_provider);
+ // check if we're able to format
+ let _capabilities = match capabilities.document_range_formatting_provider {
+ Some(lsp::OneOf::Left(true)) => (),
+ Some(lsp::OneOf::Right(_)) => (),
+ // None | Some(false)
+ _ => return Ok(Vec::new()),
+ };
+ // TODO: return err::unavailable so we can fall back to tree sitter formatting
+
+ let params = lsp::DocumentRangeFormattingParams {
+ text_document,
+ range,
+ options,
+ // TODO: support these tokens
+ work_done_progress_params: lsp::WorkDoneProgressParams {
+ work_done_token: None,
+ },
+ };
+
+ let response = self
+ .request::<lsp::request::RangeFormatting>(params)
+ .await?;
+
+ Ok(response.unwrap_or_default())
+ }
}
diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs
index a51a11fc..93bc06b5 100644
--- a/helix-lsp/src/lib.rs
+++ b/helix-lsp/src/lib.rs
@@ -28,7 +28,7 @@ pub enum Error {
pub mod util {
use super::*;
- use helix_core::{RopeSlice, State, Transaction};
+ use helix_core::{Range, RopeSlice, State, Transaction};
pub fn lsp_pos_to_pos(doc: RopeSlice, pos: lsp::Position) -> usize {
let line = doc.line_to_char(pos.line as usize);
@@ -43,6 +43,13 @@ pub mod util {
lsp::Position::new(line as u32, col as u32)
}
+ pub fn range_to_lsp_range(doc: RopeSlice, range: Range) -> lsp::Range {
+ let start = pos_to_lsp_pos(doc, range.from());
+ let end = pos_to_lsp_pos(doc, range.to());
+
+ lsp::Range::new(start, end)
+ }
+
pub fn generate_transaction_from_edits(
state: &State,
edits: Vec<lsp::TextEdit>,
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index c5c2ecf1..898981ae 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -888,6 +888,47 @@ pub fn unindent(cx: &mut Context) {
doc.append_changes_to_history();
}
+pub fn format_selections(cx: &mut Context) {
+ use helix_lsp::lsp;
+ let doc = cx.doc();
+
+ // via lsp if available
+ // else via tree-sitter indentation calculations
+
+ // TODO: blocking here is not ideal
+
+ let ranges: Vec<lsp::Range> = doc
+ .selection()
+ .ranges()
+ .iter()
+ .map(|range| {
+ helix_lsp::util::range_to_lsp_range(doc.text().slice(..), doc.selection().primary())
+ })
+ .collect();
+
+ for range in ranges {
+ let language_server = match doc.language_server.as_ref() {
+ Some(language_server) => language_server,
+ None => return,
+ };
+
+ // TODO: handle fails
+ // TODO: concurrent map
+ let edits = smol::block_on(language_server.text_document_range_formatting(
+ doc.identifier(),
+ range,
+ lsp::FormattingOptions::default(),
+ ))
+ .unwrap_or_default();
+
+ let transaction = helix_lsp::util::generate_transaction_from_edits(&doc.state, edits);
+
+ doc.apply(&transaction);
+ }
+
+ doc.append_changes_to_history();
+}
+
//
pub fn save(cx: &mut Context) {
diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs
index ac4a2bf2..589740fc 100644
--- a/helix-term/src/keymap.rs
+++ b/helix-term/src/keymap.rs
@@ -178,6 +178,7 @@ pub fn default() -> Keymaps {
vec![key!('p')] => commands::paste,
vec![key!('>')] => commands::indent,
vec![key!('<')] => commands::unindent,
+ vec![key!('=')] => commands::format_selections,
vec![key!(':')] => commands::command_mode,
vec![Key {
code: KeyCode::Esc,