core: Add a Uri variant for scratch buffers

spellbook
Michael Davis 2025-03-20 12:53:27 -04:00
parent 14a969e538
commit a8d96db493
No known key found for this signature in database
7 changed files with 48 additions and 45 deletions

View File

@ -1,18 +1,44 @@
use std::{ use std::{
fmt, fmt,
num::NonZeroUsize,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc, sync::Arc,
}; };
// uses NonZeroUsize so Option<DocumentId> 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. /// 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 or data which is Copy.
///
/// Cloning this type is cheap: the internal representation uses an Arc.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive] #[non_exhaustive]
pub enum Uri { pub enum Uri {
File(Arc<Path>), File(Arc<Path>),
Scratch(DocumentId),
} }
impl Uri { impl Uri {
@ -21,12 +47,14 @@ impl Uri {
pub fn to_url(&self) -> Result<url::Url, ()> { pub fn to_url(&self) -> Result<url::Url, ()> {
match self { match self {
Uri::File(path) => url::Url::from_file_path(path), Uri::File(path) => url::Url::from_file_path(path),
Uri::Scratch(_) => Err(()),
} }
} }
pub fn as_path(&self) -> Option<&Path> { pub fn as_path(&self) -> Option<&Path> {
match self { match self {
Self::File(path) => Some(path), 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 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::File(path) => write!(f, "{}", path.display()), Self::File(path) => write!(f, "{}", path.display()),
Self::Scratch(id) => write!(f, "[scratch {id}]"),
} }
} }
} }

View File

@ -335,9 +335,7 @@ pub fn symbol_picker(cx: &mut Context) {
let request = language_server.document_symbols(doc.identifier()).unwrap(); let request = language_server.document_symbols(doc.identifier()).unwrap();
let offset_encoding = language_server.offset_encoding(); let offset_encoding = language_server.offset_encoding();
let doc_id = doc.identifier(); let doc_id = doc.identifier();
let doc_uri = doc let doc_uri = doc.uri();
.uri()
.expect("docs with active language servers must be backed by paths");
async move { async move {
let symbols = match request.await? { let symbols = match request.await? {
@ -555,12 +553,11 @@ pub fn workspace_symbol_picker(cx: &mut Context) {
pub fn diagnostics_picker(cx: &mut Context) { pub fn diagnostics_picker(cx: &mut Context) {
let doc = doc!(cx.editor); let doc = doc!(cx.editor);
if let Some(uri) = doc.uri() { let uri = doc.uri();
let diagnostics = cx.editor.diagnostics.get(&uri).cloned().unwrap_or_default(); let diagnostics = cx.editor.diagnostics.get(&uri).cloned().unwrap_or_default();
let picker = diag_picker(cx, [(uri, diagnostics)], DiagnosticsFormat::HideSourcePath); let picker = diag_picker(cx, [(uri, diagnostics)], DiagnosticsFormat::HideSourcePath);
cx.push_layer(Box::new(overlaid(picker))); cx.push_layer(Box::new(overlaid(picker)));
} }
}
pub fn workspace_diagnostics_picker(cx: &mut Context) { pub fn workspace_diagnostics_picker(cx: &mut Context) {
// TODO not yet filtered by LanguageServerFeature, need to do something similar as Document::shown_diagnostics here for all open documents // TODO not yet filtered by LanguageServerFeature, need to do something similar as Document::shown_diagnostics here for all open documents

View File

@ -1905,8 +1905,11 @@ impl Document {
Url::from_file_path(self.path()?).ok() Url::from_file_path(self.path()?).ok()
} }
pub fn uri(&self) -> Option<helix_core::Uri> { pub fn uri(&self) -> helix_core::Uri {
Some(self.path()?.clone().into()) self.path
.clone()
.map(|path| path.into())
.unwrap_or(helix_core::Uri::Scratch(self.id))
} }
#[inline] #[inline]

View File

@ -29,7 +29,6 @@ use std::{
collections::{BTreeMap, HashMap, HashSet}, collections::{BTreeMap, HashMap, HashSet},
fs, fs,
io::{self, stdin}, io::{self, stdin},
num::NonZeroUsize,
path::{Path, PathBuf}, path::{Path, PathBuf},
pin::Pin, pin::Pin,
sync::Arc, sync::Arc,
@ -1711,9 +1710,7 @@ impl Editor {
/// Generate an id for a new document and register it. /// Generate an id for a new document and register it.
fn new_document(&mut self, mut doc: Document) -> DocumentId { fn new_document(&mut self, mut doc: Document) -> DocumentId {
let id = self.next_document_id; let id = self.next_document_id;
// Safety: adding 1 from 1 is fine, probably impossible to reach usize max self.next_document_id = self.next_document_id.next();
self.next_document_id =
DocumentId(unsafe { NonZeroUsize::new_unchecked(self.next_document_id.0.get() + 1) });
doc.id = id; doc.id = id;
self.documents.insert(id, doc); self.documents.insert(id, doc);
@ -2031,9 +2028,8 @@ impl Editor {
) -> impl Iterator<Item = helix_core::Diagnostic> + 'a { ) -> impl Iterator<Item = helix_core::Diagnostic> + 'a {
let text = document.text().clone(); let text = document.text().clone();
let language_config = document.language.clone(); let language_config = document.language.clone();
document diagnostics
.uri() .get(&document.uri())
.and_then(|uri| diagnostics.get(&uri))
.map(|diags| { .map(|diags| {
diags.iter().filter_map(move |(diagnostic, provider)| { diags.iter().filter_map(move |(diagnostic, provider)| {
let server_id = provider.language_server_id()?; let server_id = provider.language_server_id()?;

View File

@ -1,5 +1,4 @@
use std::cell::Cell; use std::cell::Cell;
use std::num::NonZeroUsize;
use std::sync::atomic::{self, AtomicUsize}; use std::sync::atomic::{self, AtomicUsize};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@ -86,7 +85,7 @@ impl DiagnosticsHandler {
active_generation, active_generation,
generation: Cell::new(0), generation: Cell::new(0),
events, events,
last_doc: Cell::new(DocumentId(NonZeroUsize::new(usize::MAX).unwrap())), last_doc: Cell::new(DocumentId::MAX),
last_cursor_line: Cell::new(usize::MAX), last_cursor_line: Cell::new(usize::MAX),
active: true, active: true,
} }

View File

@ -289,10 +289,7 @@ impl Editor {
version: Option<i32>, version: Option<i32>,
mut diagnostics: Vec<lsp::Diagnostic>, mut diagnostics: Vec<lsp::Diagnostic>,
) { ) {
let doc = self let doc = self.documents.values_mut().find(|doc| doc.uri() == uri);
.documents
.values_mut()
.find(|doc| doc.uri().is_some_and(|u| u == uri));
if let Some((version, doc)) = version.zip(doc.as_ref()) { if let Some((version, doc)) = version.zip(doc.as_ref()) {
if version != doc.version() { if version != doc.version() {

View File

@ -20,25 +20,6 @@ pub mod theme;
pub mod tree; pub mod tree;
pub mod view; pub mod view;
use std::num::NonZeroUsize;
// uses NonZeroUsize so Option<DocumentId> 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! { slotmap::new_key_type! {
pub struct ViewId; pub struct ViewId;
} }
@ -78,5 +59,6 @@ pub use action::Action;
pub use document::Document; pub use document::Document;
pub use editor::Editor; pub use editor::Editor;
use helix_core::char_idx_at_visual_offset; use helix_core::char_idx_at_visual_offset;
pub use helix_core::uri::DocumentId;
pub use theme::Theme; pub use theme::Theme;
pub use view::View; pub use view::View;