public static void SaveSnapshot()

in src/Editor/Text/Impl/TextModel/FileUtilities.cs [18:165]


        public static void SaveSnapshot(ITextSnapshot snapshot,
                                        FileMode fileMode,
                                        Encoding encoding,
                                        string filePath)
        {
            Debug.Assert((fileMode == FileMode.Create) || (fileMode == FileMode.CreateNew));

            //Save the contents of the text buffer to disk.

            string temporaryFilePath = null;
            try
            {
                FileStream originalFileStream = null;
                FileStream temporaryFileStream = FileUtilities.CreateFileStream(filePath, fileMode, out temporaryFilePath, out originalFileStream);
                if (originalFileStream == null)
                {
                    //The "normal" scenario: save the snapshot directly to disk. Either:
                    // there are no hard links to the target file so we can write the snapshot to the temporary and use File.Replace.
                    // we're creating a new file (in which case, temporaryFileStream is a misnomer: it is the stream for the file we are creating).
                    try
                    {
                        using (StreamWriter streamWriter = new StreamWriter(temporaryFileStream, encoding))
                        {
                            snapshot.Write(streamWriter);
                        }
                    }
                    finally
                    {
                        //This is somewhat redundant: disposing of streamWriter had the side-effect of disposing of temporaryFileStream
                        temporaryFileStream.Dispose();
                        temporaryFileStream = null;
                    }

                    if (temporaryFilePath != null)
                    {
                        //We were saving to the original file and already have a copy of the file on disk.
                        int remainingAttempts = 3;
                        do
                        {
                            try
                            {
                                //Replace the contents of filePath with the contents of the temporary using File.Replace to
                                //preserve the various attributes of the original file.
                                File.Replace(temporaryFilePath, filePath, null, true);
                                temporaryFilePath = null;

                                return;
                            }
                            catch (FileNotFoundException)
                            {
                                // The target file doesn't exist (someone deleted it after we detected it earlier).
                                // This is an acceptable condition so don't throw.
                                File.Move(temporaryFilePath, filePath);
                                temporaryFilePath = null;

                                return;
                            }
                            catch (IOException)
                            {
                                //There was some other exception when trying to replace the contents of the file
                                //(probably because some other process had the file locked).
                                //Wait a few ms and try again.
                                System.Threading.Thread.Sleep(5);
                            }
                        }
                        while (--remainingAttempts > 0);

                        //We're giving up on replacing the file. Try overwriting it directly (this is essentially the old Dev11 behavior).
                        //Do not try approach we are using for hard links (copying the original & restoring it if there is a failure) since
                        //getting here implies something strange is going on with the file system (Git or the like locking files) so we
                        //want the simplest possible fallback.

                        //Failing here causes the exception to be passed to the calling code.
                        using (FileStream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read))
                        {
                            using (StreamWriter streamWriter = new StreamWriter(stream, encoding))
                            {
                                snapshot.Write(streamWriter);
                            }
                        }
                    }
                }
                else
                {
                    //filePath has hard links so we need to use a different approach to save the file:
                    // copy the original file to the temporary
                    // write directly to the original
                    // restore the original in the event of errors (which could be encoding errors and not disk issues) if there's a problem.
                    try
                    {
                        // Copy the contents of the original file to the temporary.
                        originalFileStream.CopyTo(temporaryFileStream);

                        //We've got a clean copy, try writing the snapshot directly to the original file
                        try
                        {
                            originalFileStream.Seek(0, SeekOrigin.Begin);
                            originalFileStream.SetLength(0);

                            //Make sure the StreamWriter is flagged leaveOpen == true. Otherwise disposing of the StreamWriter will dispose of originalFileStream and we need to
                            //leave originalFileStream open so we can use it to restore the original from the temporary copy we made.
                            using (var streamWriter = new StreamWriter(originalFileStream, encoding, bufferSize: 1024, leaveOpen: true))        //1024 == the default buffer size for a StreamWriter.
                            {
                                snapshot.Write(streamWriter);
                            }
                        }
                        catch
                        {
                            //Restore the original from the temporary copy we made (but rethrow the original exception since we didn't save the file).
                            temporaryFileStream.Seek(0, SeekOrigin.Begin);

                            originalFileStream.Seek(0, SeekOrigin.Begin);
                            originalFileStream.SetLength(0);

                            temporaryFileStream.CopyTo(originalFileStream);

                            throw;
                        }
                    }
                    finally
                    {
                        originalFileStream.Dispose();
                        originalFileStream = null;

                        temporaryFileStream.Dispose();
                        temporaryFileStream = null;
                    }
                }
            }
            finally
            {
                if (temporaryFilePath != null)
                {
                    try
                    {
                        //We do not need the temporary any longer.
                        if (File.Exists(temporaryFilePath))
                        {
                            File.Delete(temporaryFilePath);
                        }
                    }
                    catch
                    {
                        //Failing to clean up the temporary is an ignorable exception.
                    }
                }
            }
        }