From 7743cb988cabb462068a7ec16e3afe2f544e314c Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Wed, 14 Jul 2021 23:58:09 +0800 Subject: [PATCH] Allow specifying file start position Like helix-term/src/commands.rs:3426:15 --- helix-term/src/application.rs | 29 ++++++++++++++++++++++++----- helix-term/src/args.rs | 24 ++++++++++++++++++++---- helix-term/src/commands.rs | 20 ++++++++++++++------ 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index b04eef0d8914e..2a86cff65e17e 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -1,8 +1,15 @@ -use helix_core::{merge_toml_values, syntax}; +use helix_core::{line_ending::line_end_char_index, merge_toml_values, syntax, Selection}; use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap}; use helix_view::{theme, Editor}; -use crate::{args::Args, compositor::Compositor, config::Config, job::Jobs, ui}; +use crate::{ + args::Args, + commands::{align_view, Align}, + compositor::Compositor, + config::Config, + job::Jobs, + ui, +}; use log::{error, warn}; @@ -116,7 +123,7 @@ impl Application { // Unset path to prevent accidentally saving to the original tutor file. doc_mut!(editor).set_path(None)?; } else if !args.files.is_empty() { - let first = &args.files[0]; // we know it's not empty + let first = &args.files[0].0; // we know it's not empty if first.is_dir() { std::env::set_current_dir(&first)?; editor.new_file(Action::VerticalSplit); @@ -124,16 +131,28 @@ impl Application { } else { let nr_of_files = args.files.len(); editor.open(first.to_path_buf(), Action::VerticalSplit)?; - for file in args.files { + for (file, row, col) in args.files { if file.is_dir() { return Err(anyhow::anyhow!( "expected a path to file, found a directory. (to open a directory pass it as first argument)" )); } else { - editor.open(file.to_path_buf(), Action::Load)?; + let doc_id = editor.open(file, Action::Load)?; + // with Action::Load all documents have the same view + let view_id = editor.tree.focus; + let doc = editor.document_mut(doc_id).unwrap(); + let pos = Selection::point( + (doc.text().line_to_char(row) + col) + .min(line_end_char_index(&doc.text().slice(..), row)), + ); + doc.set_selection(view_id, pos); } } editor.set_status(format!("Loaded {} files.", nr_of_files)); + // align the view to center after all files are loaded, + // does not affect views without pos since it is at the top + let (view, doc) = current!(editor); + align_view(doc, view, Align::Center); } } else if stdin().is_tty() { editor.new_file(Action::VerticalSplit); diff --git a/helix-term/src/args.rs b/helix-term/src/args.rs index 40113db92fd8b..2f70f040a6246 100644 --- a/helix-term/src/args.rs +++ b/helix-term/src/args.rs @@ -7,7 +7,7 @@ pub struct Args { pub display_version: bool, pub load_tutor: bool, pub verbosity: u64, - pub files: Vec, + pub files: Vec<(PathBuf, usize, usize)>, } impl Args { @@ -41,15 +41,31 @@ impl Args { } } } - arg => args.files.push(PathBuf::from(arg)), + arg => args.files.push(parse_file(arg)), } } // push the remaining args, if any to the files - for filename in iter { - args.files.push(PathBuf::from(filename)); + for arg in iter { + args.files.push(parse_file(arg)); } Ok(args) } } + +/// Parse arg into [`PathBuf`] and position. +pub(crate) fn parse_file(s: &str) -> (PathBuf, usize, usize) { + split_path_pos(s).unwrap_or_else(|| (PathBuf::from(s), 0, 0)) +} + +/// Split file.rs:10:2 into [`PathBuf`] and position. +/// +/// Does not validate if file.rs is a file or directory. +fn split_path_pos(s: &str) -> Option<(PathBuf, usize, usize)> { + let mut s = s.rsplitn(3, ':'); + let col: usize = s.next()?.parse().ok()?; + let row: usize = s.next()?.parse().ok()?; + let path = s.next()?.into(); + Some((path, row.saturating_sub(1), col.saturating_sub(1))) +} diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 48fd0ee01690f..9a7cd8e57c295 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -31,6 +31,7 @@ use insert::*; use movement::Movement; use crate::{ + args, compositor::{self, Component, Compositor}, ui::{self, FilePicker, Picker, Popup, Prompt, PromptEvent}, }; @@ -109,13 +110,13 @@ impl<'a> Context<'a> { } } -enum Align { +pub(crate) enum Align { Top, Center, Bottom, } -fn align_view(doc: &Document, view: &mut View, align: Align) { +pub(crate) fn align_view(doc: &Document, view: &mut View, align: Align) { let pos = doc .selection(view.id) .primary() @@ -1682,10 +1683,17 @@ mod cmd { _event: PromptEvent, ) -> anyhow::Result<()> { use helix_core::path::expand_tilde; - let path = args.get(0).context("wrong argument count")?; - let _ = cx - .editor - .open(expand_tilde(Path::new(path)), Action::Replace)?; + let arg = args.get(0).context("wrong argument count")?; + let (path, row, col) = args::parse_file(arg); + let _ = cx.editor.open(expand_tilde(&path), Action::Replace)?; + let (view, doc) = current!(cx.editor); + let pos = Selection::point( + (doc.text().line_to_char(row) + col) + .min(line_end_char_index(&doc.text().slice(..), row)), + ); + doc.set_selection(view.id, pos); + // does not affect opening a buffer without pos + align_view(doc, view, Align::Center); Ok(()) }