diff --git a/helix-core/src/uri.rs b/helix-core/src/uri.rs index cbe0fadda..c9d39ea6c 100644 --- a/helix-core/src/uri.rs +++ b/helix-core/src/uri.rs @@ -1,18 +1,44 @@ use std::{ fmt, + num::NonZeroUsize, path::{Path, PathBuf}, sync::Arc, }; +// uses NonZeroUsize so Option takes the same space +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct DocumentId(NonZeroUsize); + +impl DocumentId { + pub const MAX: Self = Self(unsafe { NonZeroUsize::new_unchecked(usize::MAX) }); + + pub fn next(&self) -> Self { + // Safety: adding 1 from 1 is fine, probably impossible to reach usize max + Self(unsafe { NonZeroUsize::new_unchecked(self.0.get() + 1) }) + } +} + +impl Default for DocumentId { + fn default() -> DocumentId { + // Safety: 1 is non-zero + DocumentId(unsafe { NonZeroUsize::new_unchecked(1) }) + } +} + +impl std::fmt::Display for DocumentId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + /// A generic pointer to a file location. /// -/// Currently this type only supports paths to local files. -/// -/// Cloning this type is cheap: the internal representation uses an Arc. +/// Cloning this type is cheap: the internal representation uses an Arc or data which is Copy. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[non_exhaustive] pub enum Uri { File(Arc), + Scratch(DocumentId), } impl Uri { @@ -21,12 +47,14 @@ impl Uri { pub fn to_url(&self) -> Result { match self { Uri::File(path) => url::Url::from_file_path(path), + Uri::Scratch(_) => Err(()), } } pub fn as_path(&self) -> Option<&Path> { match self { Self::File(path) => Some(path), + Self::Scratch(_) => None, } } } @@ -41,6 +69,7 @@ impl fmt::Display for Uri { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::File(path) => write!(f, "{}", path.display()), + Self::Scratch(id) => write!(f, "[scratch {id}]"), } } } diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 1f8117c4c..be8081854 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -335,9 +335,7 @@ pub fn symbol_picker(cx: &mut Context) { let request = language_server.document_symbols(doc.identifier()).unwrap(); let offset_encoding = language_server.offset_encoding(); let doc_id = doc.identifier(); - let doc_uri = doc - .uri() - .expect("docs with active language servers must be backed by paths"); + let doc_uri = doc.uri(); async move { let symbols = match request.await? { @@ -555,11 +553,10 @@ pub fn workspace_symbol_picker(cx: &mut Context) { pub fn diagnostics_picker(cx: &mut Context) { let doc = doc!(cx.editor); - if let Some(uri) = doc.uri() { - let diagnostics = cx.editor.diagnostics.get(&uri).cloned().unwrap_or_default(); - let picker = diag_picker(cx, [(uri, diagnostics)], DiagnosticsFormat::HideSourcePath); - cx.push_layer(Box::new(overlaid(picker))); - } + let uri = doc.uri(); + let diagnostics = cx.editor.diagnostics.get(&uri).cloned().unwrap_or_default(); + let picker = diag_picker(cx, [(uri, diagnostics)], DiagnosticsFormat::HideSourcePath); + cx.push_layer(Box::new(overlaid(picker))); } pub fn workspace_diagnostics_picker(cx: &mut Context) { diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 41c9ee1ef..00bdc8732 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1905,8 +1905,11 @@ impl Document { Url::from_file_path(self.path()?).ok() } - pub fn uri(&self) -> Option { - Some(self.path()?.clone().into()) + pub fn uri(&self) -> helix_core::Uri { + self.path + .clone() + .map(|path| path.into()) + .unwrap_or(helix_core::Uri::Scratch(self.id)) } #[inline] diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 27a985ac3..a5d364b15 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -29,7 +29,6 @@ use std::{ collections::{BTreeMap, HashMap, HashSet}, fs, io::{self, stdin}, - num::NonZeroUsize, path::{Path, PathBuf}, pin::Pin, sync::Arc, @@ -1711,9 +1710,7 @@ impl Editor { /// Generate an id for a new document and register it. fn new_document(&mut self, mut doc: Document) -> DocumentId { let id = self.next_document_id; - // Safety: adding 1 from 1 is fine, probably impossible to reach usize max - self.next_document_id = - DocumentId(unsafe { NonZeroUsize::new_unchecked(self.next_document_id.0.get() + 1) }); + self.next_document_id = self.next_document_id.next(); doc.id = id; self.documents.insert(id, doc); @@ -2031,9 +2028,8 @@ impl Editor { ) -> impl Iterator + 'a { let text = document.text().clone(); let language_config = document.language.clone(); - document - .uri() - .and_then(|uri| diagnostics.get(&uri)) + diagnostics + .get(&document.uri()) .map(|diags| { diags.iter().filter_map(move |(diagnostic, provider)| { let server_id = provider.language_server_id()?; diff --git a/helix-view/src/handlers/diagnostics.rs b/helix-view/src/handlers/diagnostics.rs index 2b8ff6325..9ce595e5f 100644 --- a/helix-view/src/handlers/diagnostics.rs +++ b/helix-view/src/handlers/diagnostics.rs @@ -1,5 +1,4 @@ use std::cell::Cell; -use std::num::NonZeroUsize; use std::sync::atomic::{self, AtomicUsize}; use std::sync::Arc; use std::time::Duration; @@ -86,7 +85,7 @@ impl DiagnosticsHandler { active_generation, generation: Cell::new(0), events, - last_doc: Cell::new(DocumentId(NonZeroUsize::new(usize::MAX).unwrap())), + last_doc: Cell::new(DocumentId::MAX), last_cursor_line: Cell::new(usize::MAX), active: true, } diff --git a/helix-view/src/handlers/lsp.rs b/helix-view/src/handlers/lsp.rs index c1041b2aa..e9a9c6548 100644 --- a/helix-view/src/handlers/lsp.rs +++ b/helix-view/src/handlers/lsp.rs @@ -289,10 +289,7 @@ impl Editor { version: Option, mut diagnostics: Vec, ) { - let doc = self - .documents - .values_mut() - .find(|doc| doc.uri().is_some_and(|u| u == uri)); + let doc = self.documents.values_mut().find(|doc| doc.uri() == uri); if let Some((version, doc)) = version.zip(doc.as_ref()) { if version != doc.version() { diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index 6d0903b16..46bcf7e66 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -20,25 +20,6 @@ pub mod theme; pub mod tree; pub mod view; -use std::num::NonZeroUsize; - -// uses NonZeroUsize so Option use a byte rather than two -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct DocumentId(NonZeroUsize); - -impl Default for DocumentId { - fn default() -> DocumentId { - // Safety: 1 is non-zero - DocumentId(unsafe { NonZeroUsize::new_unchecked(1) }) - } -} - -impl std::fmt::Display for DocumentId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("{}", self.0)) - } -} - slotmap::new_key_type! { pub struct ViewId; } @@ -78,5 +59,6 @@ pub use action::Action; pub use document::Document; pub use editor::Editor; use helix_core::char_idx_at_visual_offset; +pub use helix_core::uri::DocumentId; pub use theme::Theme; pub use view::View;