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.

Change summary

crates/gpui/src/platform/mac/platform.rs | 35 +++++++++++++++++++++++--
1 file changed, 32 insertions(+), 3 deletions(-)

Detailed changes

crates/gpui/src/platform/mac/platform.rs 🔗

@@ -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;
+                                })
                             }
                         }