on master help sign in
Merge remote-tracking branch 'origin/master' into debug
5b20f60Dmitry Sharshakov
Cargo.lock+24−24
@@ -19,9 +19,9 @@ checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf"
19
19
20[[package]]
20[[package]]
21name = "arc-swap"
21name = "arc-swap"
22−version = "1.3.1"
22+version = "1.3.2"
23source = "registry+https://github.com/rust-lang/crates.io-index"
23source = "registry+https://github.com/rust-lang/crates.io-index"
24−checksum = "34a23efe54373080cf871532e2d01076be41c4c896d32ef63af1b2dded924b03"
24+checksum = "b5ab7d9e73059c86c36473f459b52adbd99c3554a4fec492caef460806006f00"
25
25
26[[package]]
26[[package]]
27name = "autocfg"
27name = "autocfg"
@@ -232,15 +232,15 @@ dependencies = [
232
232
233[[package]]
233[[package]]
234name = "futures-core"
234name = "futures-core"
235−version = "0.3.16"
235+version = "0.3.17"
236source = "registry+https://github.com/rust-lang/crates.io-index"
236source = "registry+https://github.com/rust-lang/crates.io-index"
237−checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99"
237+checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
238
238
239[[package]]
239[[package]]
240name = "futures-executor"
240name = "futures-executor"
241−version = "0.3.16"
241+version = "0.3.17"
242source = "registry+https://github.com/rust-lang/crates.io-index"
242source = "registry+https://github.com/rust-lang/crates.io-index"
243−checksum = "4d0d535a57b87e1ae31437b892713aee90cd2d7b0ee48727cd11fc72ef54761c"
243+checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c"
244dependencies = [
244dependencies = [
245 "futures-core",
245 "futures-core",
246 "futures-task",
246 "futures-task",
@@ -249,15 +249,15 @@ dependencies = [
249
249
250[[package]]
250[[package]]
251name = "futures-task"
251name = "futures-task"
252−version = "0.3.16"
252+version = "0.3.17"
253source = "registry+https://github.com/rust-lang/crates.io-index"
253source = "registry+https://github.com/rust-lang/crates.io-index"
254−checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2"
254+checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
255
255
256[[package]]
256[[package]]
257name = "futures-util"
257name = "futures-util"
258−version = "0.3.16"
258+version = "0.3.17"
259source = "registry+https://github.com/rust-lang/crates.io-index"
259source = "registry+https://github.com/rust-lang/crates.io-index"
260−checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78"
260+checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
261dependencies = [
261dependencies = [
262 "autocfg",
262 "autocfg",
263 "futures-core",
263 "futures-core",
@@ -809,18 +809,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
809
809
810[[package]]
810[[package]]
811name = "serde"
811name = "serde"
812−version = "1.0.129"
812+version = "1.0.130"
813source = "registry+https://github.com/rust-lang/crates.io-index"
813source = "registry+https://github.com/rust-lang/crates.io-index"
814−checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1"
814+checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
815dependencies = [
815dependencies = [
816 "serde_derive",
816 "serde_derive",
817]
817]
818
818
819[[package]]
819[[package]]
820name = "serde_derive"
820name = "serde_derive"
821−version = "1.0.129"
821+version = "1.0.130"
822source = "registry+https://github.com/rust-lang/crates.io-index"
822source = "registry+https://github.com/rust-lang/crates.io-index"
823−checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3"
823+checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
824dependencies = [
824dependencies = [
825 "proc-macro2",
825 "proc-macro2",
826 "quote",
826 "quote",
@@ -829,9 +829,9 @@ dependencies = [
829
829
830[[package]]
830[[package]]
831name = "serde_json"
831name = "serde_json"
832−version = "1.0.66"
832+version = "1.0.67"
833source = "registry+https://github.com/rust-lang/crates.io-index"
833source = "registry+https://github.com/rust-lang/crates.io-index"
834−checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127"
834+checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950"
835dependencies = [
835dependencies = [
836 "itoa",
836 "itoa",
837 "ryu",
837 "ryu",
@@ -905,9 +905,9 @@ checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
905
905
906[[package]]
906[[package]]
907name = "slotmap"
907name = "slotmap"
908−version = "1.0.5"
908+version = "1.0.6"
909source = "registry+https://github.com/rust-lang/crates.io-index"
909source = "registry+https://github.com/rust-lang/crates.io-index"
910−checksum = "a952280edbecfb1d4bd3cf2dbc309dc6ab523e53487c438ae21a6df09fe84bc4"
910+checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
911dependencies = [
911dependencies = [
912 "version_check",
912 "version_check",
913]
913]
@@ -948,18 +948,18 @@ dependencies = [
948
948
949[[package]]
949[[package]]
950name = "thiserror"
950name = "thiserror"
951−version = "1.0.26"
951+version = "1.0.28"
952source = "registry+https://github.com/rust-lang/crates.io-index"
952source = "registry+https://github.com/rust-lang/crates.io-index"
953−checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
953+checksum = "283d5230e63df9608ac7d9691adc1dfb6e701225436eb64d0b9a7f0a5a04f6ec"
954dependencies = [
954dependencies = [
955 "thiserror-impl",
955 "thiserror-impl",
956]
956]
957
957
958[[package]]
958[[package]]
959name = "thiserror-impl"
959name = "thiserror-impl"
960−version = "1.0.26"
960+version = "1.0.28"
961source = "registry+https://github.com/rust-lang/crates.io-index"
961source = "registry+https://github.com/rust-lang/crates.io-index"
962−checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
962+checksum = "fa3884228611f5cd3608e2d409bf7dce832e4eb3135e3f11addbd7e41bd68e71"
963dependencies = [
963dependencies = [
964 "proc-macro2",
964 "proc-macro2",
965 "quote",
965 "quote",
@@ -1001,9 +1001,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
1001
1001
1002[[package]]
1002[[package]]
1003name = "tokio"
1003name = "tokio"
1004−version = "1.10.0"
1004+version = "1.10.1"
1005source = "registry+https://github.com/rust-lang/crates.io-index"
1005source = "registry+https://github.com/rust-lang/crates.io-index"
1006−checksum = "01cf844b23c6131f624accf65ce0e4e9956a8bb329400ea5bcc26ae3a5c20b0b"
1006+checksum = "92036be488bb6594459f2e03b60e42df6f937fe6ca5c5ffdcb539c6b84dc40f5"
1007dependencies = [
1007dependencies = [
1008 "autocfg",
1008 "autocfg",
1009 "bytes",
1009 "bytes",
README.md+1−1
@@ -43,7 +43,7 @@ cargo install --path helix-term
43
43
44This will install the `hx` binary to `$HOME/.cargo/bin`.
44This will install the `hx` binary to `$HOME/.cargo/bin`.
45
45
46−Helix also needs it's runtime files so make sure to copy/symlink the `runtime/` directory into the
46+Helix also needs its runtime files so make sure to copy/symlink the `runtime/` directory into the
47config directory (for example `~/.config/helix/runtime` on Linux/macOS). This location can be overriden
47config directory (for example `~/.config/helix/runtime` on Linux/macOS). This location can be overriden
48via the `HELIX_RUNTIME` environment variable.
48via the `HELIX_RUNTIME` environment variable.
49
49
book/src/keymap.md+10
@@ -122,6 +122,16 @@ in reverse, or searching via smartcase.
122| `[D` | Go to first diagnostic in document | `goto_first_diag` |
122| `[D` | Go to first diagnostic in document | `goto_first_diag` |
123| `]D` | Go to last diagnostic in document | `goto_last_diag` |
123| `]D` | Go to last diagnostic in document | `goto_last_diag` |
124
124
125+### Shell
126+
127+| Key | Description | Command |
128+| ------ | ----------- | ------- |
129+| `\|` | Pipe each selection through shell command, replacing with output | `shell_pipe` |
130+| `A-\|` | Pipe each selection into shell command, ignoring output | `shell_pipe_to` |
131+| `!` | Run shell command, inserting output before each selection | `shell_insert_output` |
132+| `A-!` | Run shell command, appending output after each selection | `shell_append_output` |
133+| `$` | Pipe each selection into shell command, removing if the command exits >0 | `shell_keep_pipe` |
134+
125## Select / extend mode
135## Select / extend mode
126
136
127I'm still pondering whether to keep this mode or not. It changes movement
137I'm still pondering whether to keep this mode or not. It changes movement
helix-lsp/Cargo.toml+1−1
@@ -23,5 +23,5 @@ lsp-types = { version = "0.89", features = ["proposed"] }
23serde = { version = "1.0", features = ["derive"] }
23serde = { version = "1.0", features = ["derive"] }
24serde_json = "1.0"
24serde_json = "1.0"
25thiserror = "1.0"
25thiserror = "1.0"
26−tokio = { version = "1.9", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] }
26+tokio = { version = "1.10", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] }
27tokio-stream = "0.1"
27tokio-stream = "0.1"
helix-lsp/src/transport.rs+12−11
@@ -1,7 +1,7 @@
1use crate::Result;
1use crate::Result;
2use anyhow::Context;
2use anyhow::Context;
3use jsonrpc_core as jsonrpc;
3use jsonrpc_core as jsonrpc;
4−use log::{error, info};
4+use log::{debug, error, info, warn};
5use serde::{Deserialize, Serialize};
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
6use serde_json::Value;
7use std::collections::HashMap;
7use std::collections::HashMap;
@@ -83,19 +83,20 @@ impl Transport {
83 break;
83 break;
84 }
84 }
85
85
86− let mut parts = header.split(": ");
86+ debug!("<- header {}", header);
87
87
88+ let parts = header.split_once(": ");
89+
88− match (parts.next(), parts.next(), parts.next()) {
90+ match parts {
89− (Some("Content-Length"), Some(value), None) => {
91+ Some(("Content-Length", value)) => {
90 content_length = Some(value.parse().context("invalid content length")?);
92 content_length = Some(value.parse().context("invalid content length")?);
91 }
93 }
94+ Some((_, _)) => {}
92− (Some(_), Some(_), None) => {}
95+ None => {
93− _ => {
96+ // Workaround: Some non-conformant language servers will output logging and other garbage
94− return Err(std::io::Error::new(
97+ // into the same stream as JSON-RPC messages. This can also happen from shell scripts that spawn
95− std::io::ErrorKind::Other,
98+ // the server. Skip such lines and log a warning.
96− "Failed to parse header",
99+ warn!("Failed to parse header: {:?}", header);
97− )
98− .into());
99 }
100 }
100 }
101 }
101 }
102 }
helix-term/src/commands.rs+138−15
@@ -135,7 +135,7 @@ pub struct Command {
135}
135}
136
136
137macro_rules! commands {
137macro_rules! commands {
138− ( $($name:ident, $doc:literal),* ) => {
138+ ( $($name:ident, $doc:literal,)* ) => {
139 $(
139 $(
140 #[allow(non_upper_case_globals)]
140 #[allow(non_upper_case_globals)]
141 pub const $name: Self = Self {
141 pub const $name: Self = Self {
@@ -317,7 +317,12 @@ impl Command {
317 dap_variables, "List variables",
317 dap_variables, "List variables",
318 dap_terminate, "End debug session",
318 dap_terminate, "End debug session",
319 dap_switch_thread, "Switch current thread",
319 dap_switch_thread, "Switch current thread",
320+ shell_pipe, "Pipe selections through shell command",
321+ shell_pipe_to, "Pipe selections into shell command, ignoring command output",
322+ shell_insert_output, "Insert output of shell command before each selection",
323+ shell_append_output, "Append output of shell command after each selection",
324+ shell_keep_pipe, "Filter selections with shell predicate",
320− suspend, "Suspend"
325+ suspend, "Suspend",
321 );
326 );
322}
327}
323
328
@@ -1089,7 +1094,7 @@ fn select_all(cx: &mut Context) {
1089}
1094}
1090
1095
1091fn select_regex(cx: &mut Context) {
1096fn select_regex(cx: &mut Context) {
1092− let prompt = ui::regex_prompt(cx, "select:".to_string(), move |view, doc, _, regex| {
1097+ let prompt = ui::regex_prompt(cx, "select:".into(), move |view, doc, _, regex| {
1093 let text = doc.text().slice(..);
1098 let text = doc.text().slice(..);
1094 if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex)
1099 if let Some(selection) = selection::select_on_matches(text, doc.selection(view.id), &regex)
1095 {
1100 {
@@ -1101,7 +1106,7 @@ fn select_regex(cx: &mut Context) {
1101}
1106}
1102
1107
1103fn split_selection(cx: &mut Context) {
1108fn split_selection(cx: &mut Context) {
1104− let prompt = ui::regex_prompt(cx, "split:".to_string(), move |view, doc, _, regex| {
1109+ let prompt = ui::regex_prompt(cx, "split:".into(), move |view, doc, _, regex| {
1105 let text = doc.text().slice(..);
1110 let text = doc.text().slice(..);
1106 let selection = selection::split_on_matches(text, doc.selection(view.id), &regex);
1111 let selection = selection::split_on_matches(text, doc.selection(view.id), &regex);
1107 doc.set_selection(view.id, selection);
1112 doc.set_selection(view.id, selection);
@@ -1167,15 +1172,11 @@ fn search(cx: &mut Context) {
1167 // feed chunks into the regex yet
1172 // feed chunks into the regex yet
1168 let contents = doc.text().slice(..).to_string();
1173 let contents = doc.text().slice(..).to_string();
1169
1174
1170− let prompt = ui::regex_prompt(
1171− cx,
1172− "search:".to_string(),
1173− move |view, doc, registers, regex| {
1175+ let prompt = ui::regex_prompt(cx, "search:".into(), move |view, doc, registers, regex| {
1174− search_impl(doc, view, &contents, &regex, false);
1176+ search_impl(doc, view, &contents, &regex, false);
1175− // TODO: only store on enter (accept), not update
1177+ // TODO: only store on enter (accept), not update
1176− registers.write('/', vec![regex.as_str().to_string()]);
1178+ registers.write('/', vec![regex.as_str().to_string()]);
1177− },
1178− );
1179+ });
1179
1180
1180 cx.push_layer(Box::new(prompt));
1181 cx.push_layer(Box::new(prompt));
1181}
1182}
@@ -2400,7 +2401,7 @@ mod cmd {
2400
2401
2401fn command_mode(cx: &mut Context) {
2402fn command_mode(cx: &mut Context) {
2402 let mut prompt = Prompt::new(
2403 let mut prompt = Prompt::new(
2403− ":".to_owned(),
2404+ ":".into(),
2404 Some(':'),
2405 Some(':'),
2405 |input: &str| {
2406 |input: &str| {
2406 // we use .this over split_whitespace() because we care about empty segments
2407 // we use .this over split_whitespace() because we care about empty segments
@@ -4009,7 +4010,7 @@ fn join_selections(cx: &mut Context) {
4009
4010
4010fn keep_selections(cx: &mut Context) {
4011fn keep_selections(cx: &mut Context) {
4011 // keep selections matching regex
4012 // keep selections matching regex
4012− let prompt = ui::regex_prompt(cx, "keep:".to_string(), move |view, doc, _, regex| {
4013+ let prompt = ui::regex_prompt(cx, "keep:".into(), move |view, doc, _, regex| {
4013 let text = doc.text().slice(..);
4014 let text = doc.text().slice(..);
4014
4015
4015 if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), &regex) {
4016 if let Some(selection) = selection::keep_matches(text, doc.selection(view.id), &regex) {
@@ -4487,6 +4488,128 @@ fn surround_delete(cx: &mut Context) {
4487 })
4488 })
4488}
4489}
4489
4490
4491+#[derive(Eq, PartialEq)]
4492+enum ShellBehavior {
4493+ Replace,
4494+ Ignore,
4495+ Insert,
4496+ Append,
4497+ Filter,
4498+}
4499+
4500+fn shell_pipe(cx: &mut Context) {
4501+ shell(cx, "pipe:".into(), ShellBehavior::Replace);
4502+}
4503+
4504+fn shell_pipe_to(cx: &mut Context) {
4505+ shell(cx, "pipe-to:".into(), ShellBehavior::Ignore);
4506+}
4507+
4508+fn shell_insert_output(cx: &mut Context) {
4509+ shell(cx, "insert-output:".into(), ShellBehavior::Insert);
4510+}
4511+
4512+fn shell_append_output(cx: &mut Context) {
4513+ shell(cx, "append-output:".into(), ShellBehavior::Append);
4514+}
4515+
4516+fn shell_keep_pipe(cx: &mut Context) {
4517+ shell(cx, "keep-pipe:".into(), ShellBehavior::Filter);
4518+}
4519+
4520+fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
4521+ use std::io::Write;
4522+ use std::process::{Command, Stdio};
4523+ if cx.editor.config.shell.is_empty() {
4524+ cx.editor.set_error("No shell set".to_owned());
4525+ return;
4526+ }
4527+ let pipe = match behavior {
4528+ ShellBehavior::Replace | ShellBehavior::Ignore | ShellBehavior::Filter => true,
4529+ ShellBehavior::Insert | ShellBehavior::Append => false,
4530+ };
4531+ let prompt = Prompt::new(
4532+ prompt,
4533+ Some('|'),
4534+ |_input: &str| Vec::new(),
4535+ move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
4536+ if event != PromptEvent::Validate {
4537+ return;
4538+ }
4539+ let shell = &cx.editor.config.shell;
4540+ let (view, doc) = current!(cx.editor);
4541+ let selection = doc.selection(view.id);
4542+
4543+ let mut changes = Vec::with_capacity(selection.len());
4544+
4545+ for range in selection.ranges() {
4546+ let mut process = match Command::new(&shell[0])
4547+ .args(&shell[1..])
4548+ .arg(input)
4549+ .stdin(Stdio::piped())
4550+ .stdout(Stdio::piped())
4551+ .stderr(Stdio::piped())
4552+ .spawn()
4553+ {
4554+ Ok(process) => process,
4555+ Err(e) => {
4556+ log::error!("Failed to start shell: {}", e);
4557+ cx.editor.set_error("Failed to start shell".to_owned());
4558+ return;
4559+ }
4560+ };
4561+ if pipe {
4562+ let stdin = process.stdin.as_mut().unwrap();
4563+ let fragment = range.fragment(doc.text().slice(..));
4564+ stdin.write_all(fragment.as_bytes()).unwrap();
4565+ }
4566+ let output = process.wait_with_output().unwrap();
4567+
4568+ if behavior != ShellBehavior::Filter {
4569+ if !output.status.success() {
4570+ if !output.stderr.is_empty() {
4571+ log::error!("Shell error: {}", String::from_utf8_lossy(&output.stderr));
4572+ }
4573+ cx.editor.set_error("Command failed".to_owned());
4574+ return;
4575+ }
4576+ let tendril = match Tendril::try_from_byte_slice(&output.stdout) {
4577+ Ok(tendril) => tendril,
4578+ Err(_) => {
4579+ cx.editor
4580+ .set_error("Process did not output valid UTF-8".to_owned());
4581+ return;
4582+ }
4583+ };
4584+ let (from, to) = match behavior {
4585+ ShellBehavior::Replace => (range.from(), range.to()),
4586+ ShellBehavior::Insert => (range.from(), range.from()),
4587+ ShellBehavior::Append => (range.to(), range.to()),
4588+ _ => (range.from(), range.from()),
4589+ };
4590+ changes.push((from, to, Some(tendril)));
4591+ } else {
4592+ // if the process exits successfully, keep the selection, otherwise delete it.
4593+ let keep = output.status.success();
4594+ changes.push((
4595+ range.from(),
4596+ if keep { range.from() } else { range.to() },
4597+ None,
4598+ ));
4599+ }
4600+ }
4601+
4602+ if behavior != ShellBehavior::Ignore {
4603+ let transaction = Transaction::change(doc.text(), changes.into_iter());
4604+ doc.apply(&transaction, view.id);
4605+ doc.append_changes_to_history(view.id);
4606+ }
4607+ },
4608+ );
4609+
4610+ cx.push_layer(Box::new(prompt));
4611+}
4612+
4490fn suspend(_cx: &mut Context) {
4613fn suspend(_cx: &mut Context) {
4491 #[cfg(not(windows))]
4614 #[cfg(not(windows))]
4492 signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap();
4615 signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap();
helix-term/src/keymap.rs+5
@@ -525,6 +525,11 @@ impl Default for Keymaps {
525 },
525 },
526
526
527 "\"" => select_register,
527 "\"" => select_register,
528+ "|" => shell_pipe,
529+ "A-|" => shell_pipe_to,
530+ "!" => shell_insert_output,
531+ "A-!" => shell_append_output,
532+ "$" => shell_keep_pipe,
528 "C-z" => suspend,
533 "C-z" => suspend,
529 });
534 });
530 let mut select = normal.clone();
535 let mut select = normal.clone();
helix-term/src/ui/editor.rs+17−17
@@ -155,21 +155,21 @@ impl EditorView {
155 syntax
155 syntax
156 .highlight_iter(text.slice(..), Some(range), None, |language| {
156 .highlight_iter(text.slice(..), Some(range), None, |language| {
157 loader
157 loader
158− .language_config_for_scope(&format!("source.{}", language))
158+ .language_config_for_scope(&format!("source.{}", language))
159− .and_then(|language_config| {
159+ .and_then(|language_config| {
160− let config = language_config.highlight_config(scopes)?;
160+ let config = language_config.highlight_config(scopes)?;
161− let config_ref = config.as_ref();
161+ let config_ref = config.as_ref();
162− // SAFETY: the referenced `HighlightConfiguration` behind
162+ // SAFETY: the referenced `HighlightConfiguration` behind
163− // the `Arc` is guaranteed to remain valid throughout the
163+ // the `Arc` is guaranteed to remain valid throughout the
164− // duration of the highlight.
164+ // duration of the highlight.
165− let config_ref = unsafe {
165+ let config_ref = unsafe {
166− std::mem::transmute::<
166+ std::mem::transmute::<
167− _,
167+ _,
168− &'static syntax::HighlightConfiguration,
168+ &'static syntax::HighlightConfiguration,
169− >(config_ref)
169+ >(config_ref)
170− };
170+ };
171− Some(config_ref)
171+ Some(config_ref)
172− })
172+ })
173 })
173 })
174 .map(|event| event.unwrap())
174 .map(|event| event.unwrap())
175 .collect() // TODO: we collect here to avoid holding the lock, fix later
175 .collect() // TODO: we collect here to avoid holding the lock, fix later
@@ -435,7 +435,7 @@ impl EditorView {
435
435
436 let current_line = doc
436 let current_line = doc
437 .text()
437 .text()
438− .char_to_line(doc.selection(view.id).primary().anchor);
438+ .char_to_line(doc.selection(view.id).primary().cursor(text));
439
439
440 // it's used inside an iterator so the collect isn't needless:
440 // it's used inside an iterator so the collect isn't needless:
441 // https://github.com/rust-lang/rust-clippy/issues/6164
441 // https://github.com/rust-lang/rust-clippy/issues/6164
@@ -749,7 +749,7 @@ impl EditorView {
749 _ => noop,
749 _ => noop,
750 };
750 };
751 Prompt::new(
751 Prompt::new(
752− format!("{}: ", name),
752+ format!("{}: ", name).into(),
753 None,
753 None,
754 completer,
754 completer,
755 move |cx: &mut crate::compositor::Context, input: &str, event: PromptEvent| {
755 move |cx: &mut crate::compositor::Context, input: &str, event: PromptEvent| {
helix-term/src/ui/mod.rs+1−1
@@ -27,7 +27,7 @@ use std::path::PathBuf;
27
27
28pub fn regex_prompt(
28pub fn regex_prompt(
29 cx: &mut crate::commands::Context,
29 cx: &mut crate::commands::Context,
30− prompt: String,
30+ prompt: std::borrow::Cow<'static, str>,
31 fun: impl Fn(&mut View, &mut Document, &mut Registers, Regex) + 'static,
31 fun: impl Fn(&mut View, &mut Document, &mut Registers, Regex) + 'static,
32) -> Prompt {
32) -> Prompt {
33 let (view, doc) = current!(cx.editor);
33 let (view, doc) = current!(cx.editor);
helix-term/src/ui/picker.rs+1−1
@@ -202,7 +202,7 @@ impl<T> Picker<T> {
202 callback_fn: impl Fn(&mut Editor, &T, Action) + 'static,
202 callback_fn: impl Fn(&mut Editor, &T, Action) + 'static,
203 ) -> Self {
203 ) -> Self {
204 let prompt = Prompt::new(
204 let prompt = Prompt::new(
205− "".to_string(),
205+ "".into(),
206 None,
206 None,
207 |_pattern: &str| Vec::new(),
207 |_pattern: &str| Vec::new(),
208 |_editor: &mut Context, _pattern: &str, _event: PromptEvent| {
208 |_editor: &mut Context, _pattern: &str, _event: PromptEvent| {
helix-term/src/ui/prompt.rs+2−2
@@ -15,7 +15,7 @@ use helix_view::{
15pub type Completion = (RangeFrom<usize>, Cow<'static, str>);
15pub type Completion = (RangeFrom<usize>, Cow<'static, str>);
16
16
17pub struct Prompt {
17pub struct Prompt {
18− prompt: String,
18+ prompt: Cow<'static, str>,
19 pub line: String,
19 pub line: String,
20 cursor: usize,
20 cursor: usize,
21 completion: Vec<Completion>,
21 completion: Vec<Completion>,
@@ -55,7 +55,7 @@ pub enum Movement {
55
55
56impl Prompt {
56impl Prompt {
57 pub fn new(
57 pub fn new(
58− prompt: String,
58+ prompt: Cow<'static, str>,
59 history_register: Option<char>,
59 history_register: Option<char>,
60 mut completion_fn: impl FnMut(&str) -> Vec<Completion> + 'static,
60 mut completion_fn: impl FnMut(&str) -> Vec<Completion> + 'static,
61 callback_fn: impl FnMut(&mut Context, &str, PromptEvent) + 'static,
61 callback_fn: impl FnMut(&mut Context, &str, PromptEvent) + 'static,
helix-view/src/editor.rs+7
@@ -36,6 +36,8 @@ pub struct Config {
36 pub scroll_lines: isize,
36 pub scroll_lines: isize,
37 /// Mouse support. Defaults to true.
37 /// Mouse support. Defaults to true.
38 pub mouse: bool,
38 pub mouse: bool,
39+ /// Shell to use for shell commands. Defaults to ["cmd", "/C"] on Windows and ["sh", "-c"] otherwise.
40+ pub shell: Vec<String>,
39 /// Line number mode.
41 /// Line number mode.
40 pub line_number: LineNumber,
42 pub line_number: LineNumber,
41 /// Middle click paste support. Defaults to true
43 /// Middle click paste support. Defaults to true
@@ -58,6 +60,11 @@ impl Default for Config {
58 scrolloff: 5,
60 scrolloff: 5,
59 scroll_lines: 3,
61 scroll_lines: 3,
60 mouse: true,
62 mouse: true,
63+ shell: if cfg!(windows) {
64+ vec!["cmd".to_owned(), "/C".to_owned()]
65+ } else {
66+ vec!["sh".to_owned(), "-c".to_owned()]
67+ },
61 line_number: LineNumber::Absolute,
68 line_number: LineNumber::Absolute,
62 middle_click_paste: true,
69 middle_click_paste: true,
63 }
70 }