diff --git a/Cargo.lock b/Cargo.lock index 8c8e97481..c2f2735d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1535,6 +1535,7 @@ dependencies = [ "serde_json", "slotmap", "tempfile", + "thiserror", "tokio", "tokio-stream", "toml", diff --git a/Cargo.toml b/Cargo.toml index 6206281b7..e3ee10319 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ package.helix-term.opt-level = 2 tree-sitter = { version = "0.22" } nucleo = "0.2.0" slotmap = "1.0.7" +thiserror = "1.0" [workspace.package] version = "24.3.0" diff --git a/helix-dap/Cargo.toml b/helix-dap/Cargo.toml index 3521f5890..c37340cc6 100644 --- a/helix-dap/Cargo.toml +++ b/helix-dap/Cargo.toml @@ -20,8 +20,8 @@ anyhow = "1.0" log = "0.4" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -thiserror = "1.0" tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "net", "sync"] } +thiserror.workspace = true [dev-dependencies] fern = "0.6" diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index c6c94b84e..ab9251ebe 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -26,9 +26,9 @@ log = "0.4" lsp-types = { version = "0.95" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -thiserror = "1.0" tokio = { version = "1.38", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } tokio-stream = "0.1.15" parking_lot = "0.12.3" arc-swap = "1" slotmap.workspace = true +thiserror.workspace = true diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index f71eed209..9adc764cc 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -9,7 +9,7 @@ use helix_stdx::path::get_relative_path; use helix_view::{ align_view, - document::DocumentSavedEventResult, + document::{DocumentOpenError, DocumentSavedEventResult}, editor::{ConfigEvent, EditorEvent}, graphics::Rect, theme, @@ -186,9 +186,15 @@ pub fn new(args: Args, config: Config, lang_loader: syntax::Loader) -> Result Action::HorizontalSplit, None => Action::Load, }; - let doc_id = editor - .open(&file, action) - .context(format!("open '{}'", file.to_string_lossy()))?; + let doc_id = match editor.open(&file, action) { + // Ignore irregular files during application init. + Err(DocumentOpenError::IrregularFile) => { + nr_of_files -= 1; + continue; + } + Err(err) => return Err(anyhow::anyhow!(err)), + Ok(doc_id) => doc_id, + }; // with Action::Load all documents have the same view // NOTE: this isn't necessarily true anymore. If // `--vsplit` or `--hsplit` are used, the file which is @@ -199,15 +205,21 @@ pub fn new(args: Args, config: Config, lang_loader: syntax::Loader) -> Result, } +#[derive(Debug, thiserror::Error)] +pub enum DocumentOpenError { + #[error("path must be a regular file, simlink, or directory")] + IrregularFile, + #[error(transparent)] + IoError(#[from] io::Error), +} + pub struct Document { pub(crate) id: DocumentId, text: Rope, @@ -380,7 +390,7 @@ fn encode_from_utf8( pub fn from_reader( reader: &mut R, encoding: Option<&'static Encoding>, -) -> Result<(Rope, &'static Encoding, bool), Error> { +) -> Result<(Rope, &'static Encoding, bool), io::Error> { // These two buffers are 8192 bytes in size each and are used as // intermediaries during the decoding process. Text read into `buf` // from `reader` is decoded into `buf_out` as UTF-8. Once either @@ -523,7 +533,7 @@ fn read_and_detect_encoding( reader: &mut R, encoding: Option<&'static Encoding>, buf: &mut [u8], -) -> Result<(&'static Encoding, bool, encoding::Decoder, usize), Error> { +) -> Result<(&'static Encoding, bool, encoding::Decoder, usize), io::Error> { let read = reader.read(buf)?; let is_empty = read == 0; let (encoding, has_bom) = encoding @@ -685,11 +695,18 @@ pub fn open( encoding: Option<&'static Encoding>, config_loader: Option>>, config: Arc>, - ) -> Result { + ) -> Result { + // If the path is not a regular file (e.g.: /dev/random) it should not be opened. + if path + .metadata() + .map_or(false, |metadata| !metadata.is_file()) + { + return Err(DocumentOpenError::IrregularFile); + } + // Open the file if it exists, otherwise assume it is a new file (and thus empty). let (rope, encoding, has_bom) = if path.exists() { - let mut file = - std::fs::File::open(path).context(format!("unable to open {:?}", path))?; + let mut file = std::fs::File::open(path)?; from_reader(&mut file, encoding)? } else { let line_ending: LineEnding = config.load().default_line_ending.into(); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 635f72619..3eeb4830e 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1,6 +1,8 @@ use crate::{ align_view, - document::{DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint}, + document::{ + DocumentOpenError, DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint, + }, graphics::{CursorKind, Rect}, handlers::Handlers, info::Info, @@ -1677,7 +1679,7 @@ pub fn new_file_from_stdin(&mut self, action: Action) -> Result Result { + pub fn open(&mut self, path: &Path, action: Action) -> Result { let path = helix_stdx::path::canonicalize(path); let id = self.document_by_path(&path).map(|doc| doc.id);