Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/bin/edit/documents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl Document {
let filename = path.file_name().unwrap_or_default().to_string_lossy().into_owned();
let dir = path.parent().map(ToOwned::to_owned).unwrap_or_default();
self.filename = filename;
self.dir = Some(DisplayablePathBuf::new(dir));
self.dir = Some(DisplayablePathBuf::from_path(dir));
self.path = Some(path);
self.update_file_mode();
}
Expand Down
36 changes: 30 additions & 6 deletions src/bin/edit/draw_filepicker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use std::cmp::Ordering;
use std::fs;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

use edit::framebuffer::IndexedColor;
use edit::helpers::*;
Expand Down Expand Up @@ -196,19 +196,31 @@ pub fn draw_file_picker(ctx: &mut Context, state: &mut State) {

// Returns Some(path) if the path refers to a file.
fn draw_file_picker_update_path(state: &mut State) -> Option<PathBuf> {
let path = state.file_picker_pending_dir.as_path();
let path = path.join(&state.file_picker_pending_name);
let old_path = state.file_picker_pending_dir.as_path();
let path = old_path.join(&state.file_picker_pending_name);
let path = path::normalize(&path);

let (dir, name) = if path.is_dir() {
(path.as_path(), PathBuf::new())
// If the current path is C:\ and the user selects "..", we want to
// navigate to the drive picker. Since `path::normalize` will turn C:\.. into C:\,
// we can detect this by checking if the length of the path didn't change.
let dir = if cfg!(windows)
&& state.file_picker_pending_name == Path::new("..")
// It's unneccessary to check the contents of the paths.
&& old_path.as_os_str().len() == path.as_os_str().len()
{
Path::new("")
} else {
path.as_path()
};
(dir, PathBuf::new())
} else {
let dir = path.parent().unwrap_or(&path);
let name = path.file_name().map_or(Default::default(), |s| s.into());
(dir, name)
};
if dir != state.file_picker_pending_dir.as_path() {
state.file_picker_pending_dir = DisplayablePathBuf::new(dir.to_path_buf());
state.file_picker_pending_dir = DisplayablePathBuf::from_path(dir.to_path_buf());
state.file_picker_entries = None;
}

Expand All @@ -220,7 +232,19 @@ fn draw_dialog_saveas_refresh_files(state: &mut State) {
let dir = state.file_picker_pending_dir.as_path();
let mut files = Vec::new();

if dir.parent().is_some() {
#[cfg(windows)]
if dir.as_os_str().is_empty() {
// If the path is empty, we are at the drive picker.
// Add all drives as entries.
for drive in sys::drives() {
files.push(DisplayablePathBuf::from_string(format!("{drive}:\\")));
}

state.file_picker_entries = Some(files);
return;
}

if cfg!(windows) || dir.parent().is_some() {
files.push(DisplayablePathBuf::from(".."));
}

Expand Down
2 changes: 1 addition & 1 deletion src/bin/edit/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ fn handle_args(state: &mut State) -> apperr::Result<bool> {
state.documents.add_untitled()?;
}

state.file_picker_pending_dir = DisplayablePathBuf::new(cwd);
state.file_picker_pending_dir = DisplayablePathBuf::from_path(cwd);
Ok(false)
}

Expand Down
14 changes: 10 additions & 4 deletions src/bin/edit/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ pub struct DisplayablePathBuf {
}

impl DisplayablePathBuf {
pub fn new(value: PathBuf) -> Self {
#[allow(dead_code, reason = "only used on Windows")]
pub fn from_string(str: String) -> Self {
let value = PathBuf::from(&str);
Self { value, str: Cow::Owned(str) }
}

pub fn from_path(value: PathBuf) -> Self {
let str = value.to_string_lossy();
let str = unsafe { mem::transmute::<Cow<'_, str>, Cow<'_, str>>(str) };
Self { value, str }
Expand All @@ -68,19 +74,19 @@ impl Default for DisplayablePathBuf {

impl Clone for DisplayablePathBuf {
fn clone(&self) -> Self {
Self::new(self.value.clone())
Self::from_path(self.value.clone())
}
}

impl From<OsString> for DisplayablePathBuf {
fn from(s: OsString) -> Self {
Self::new(PathBuf::from(s))
Self::from_path(PathBuf::from(s))
}
}

impl<T: ?Sized + AsRef<OsStr>> From<&T> for DisplayablePathBuf {
fn from(s: &T) -> Self {
Self::new(PathBuf::from(s))
Self::from_path(PathBuf::from(s))
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/sys/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,21 @@ pub fn open_stdin_if_redirected() -> Option<File> {
}
}

pub fn drives() -> impl Iterator<Item = char> {
unsafe {
let mut mask = FileSystem::GetLogicalDrives();
std::iter::from_fn(move || {
let bit = mask.trailing_zeros();
if bit >= 26 {
None
} else {
mask &= !(1 << bit);
Some((b'A' + bit as u8) as char)
}
})
}
}

/// A unique identifier for a file.
pub enum FileId {
Id(FileSystem::FILE_ID_INFO),
Expand Down