Fix saving files as `*.sql` on macOS Sequoia (#20195)
Conrad Irwin
created
Closes #16969
Release Notes:
- Fixed a bug in macOS Sequoia where you can't save a new file as
`*.sql`, it would rename to `.sql.s`. As a side effect you can no longer
save a new file as `*sql.s`. We hope to remove this workaround when the
operating system fixes its bug; in the meantime you can either set
`"use_system_path_prompts": false` in your settings file to skip the
macOS dialogues, or create new files by right clicking in the project
panel.
@@ -391,7 +391,7 @@ impl MacPlatform {
}
}
- fn os_version(&self) -> Result<SemanticVersion> {
+ fn os_version() -> Result<SemanticVersion> {
unsafe {
let process_info = NSProcessInfo::processInfo(nil);
let version = process_info.operatingSystemVersion();
@@ -585,7 +585,7 @@ impl Platform for MacPlatform {
// API only available post Monterey
// https://developer.apple.com/documentation/appkit/nsworkspace/3753004-setdefaultapplicationaturl
let (done_tx, done_rx) = oneshot::channel();
- if self.os_version().ok() < Some(SemanticVersion::new(12, 0, 0)) {
+ if Self::os_version().ok() < Some(SemanticVersion::new(12, 0, 0)) {
return Task::ready(Err(anyhow!(
"macOS 12.0 or later is required to register URL schemes"
)));
@@ -695,7 +695,36 @@ impl Platform for MacPlatform {
if response == NSModalResponse::NSModalResponseOk {
let url = panel.URL();
if url.isFileURL() == YES {
- result = ns_url_to_path(panel.URL()).ok()
+ result = ns_url_to_path(panel.URL()).ok().map(|mut result| {
+ let Some(filename) = result.file_name() else {
+ return result;
+ };
+ let chunks = filename
+ .as_bytes()
+ .split(|&b| b == b'.')
+ .collect::<Vec<_>>();
+
+ // https://github.com/zed-industries/zed/issues/16969
+ // Workaround a bug in macOS Sequoia that adds an extra file-extension
+ // sometimes. e.g. `a.sql` becomes `a.sql.s` or `a.txtx` becomes `a.txtx.txt`
+ //
+ // This is conditional on OS version because I'd like to get rid of it, so that
+ // you can manually create a file called `a.sql.s`. That said it seems better
+ // to break that use-case than breaking `a.sql`.
+ if chunks.len() == 3 && chunks[1].starts_with(chunks[2]) {
+ if Self::os_version()
+ .is_ok_and(|v| v >= SemanticVersion::new(15, 0, 0))
+ {
+ let new_filename = OsStr::from_bytes(
+ &filename.as_bytes()
+ [..chunks[0].len() + 1 + chunks[1].len()],
+ )
+ .to_owned();
+ result.set_file_name(&new_filename);
+ }
+ }
+ return result;
+ })
}
}