fix: moved escape_snippet_bits to ide_db under SnippetEdit
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index 540089c..5a3a3ac 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -8,6 +8,7 @@
     RootDatabase, SnippetCap,
     documentation::{Documentation, HasDocs},
     imports::insert_use::ImportScope,
+    source_change::SnippetEdit,
     syntax_helpers::suggest_name::NameGenerator,
     text_edit::TextEdit,
     ty_filter::TryEnum,
@@ -401,7 +402,7 @@ fn get_receiver_text(
 
     // The receiver texts should be interpreted as-is, as they are expected to be
     // normal Rust expressions.
-    escape_snippet_bits(&mut text);
+    SnippetEdit::escape_snippet_bits(&mut text);
     return text;
 
     fn indent_of_tail_line(text: &str) -> usize {
@@ -411,15 +412,6 @@ fn indent_of_tail_line(text: &str) -> usize {
     }
 }
 
-/// Escapes `\` and `$` so that they don't get interpreted as snippet-specific constructs.
-///
-/// Note that we don't need to escape the other characters that can be escaped,
-/// because they wouldn't be treated as snippet-specific constructs without '$'.
-fn escape_snippet_bits(text: &mut String) {
-    stdx::replace(text, '\\', "\\\\");
-    stdx::replace(text, '$', "\\$");
-}
-
 fn receiver_accessor(receiver: &ast::Expr) -> ast::Expr {
     receiver
         .syntax()
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
index 3b22e8a..76eddc5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
@@ -18,14 +18,13 @@
 
 use ide_db::{
     SnippetCap,
+    source_change::SnippetEdit,
     syntax_helpers::format_string_exprs::{Arg, parse_format_exprs, with_placeholders},
 };
 use syntax::{AstToken, ast};
 
 use crate::{
-    Completions,
-    completions::postfix::{build_postfix_snippet_builder, escape_snippet_bits},
-    context::CompletionContext,
+    Completions, completions::postfix::build_postfix_snippet_builder, context::CompletionContext,
 };
 
 /// Mapping ("postfix completion item" => "macro to use")
@@ -57,10 +56,10 @@ pub(crate) fn add_format_like_completions(
 
     if let Ok((mut out, mut exprs)) = parse_format_exprs(receiver_text.text()) {
         // Escape any snippet bits in the out text and any of the exprs.
-        escape_snippet_bits(&mut out);
+        SnippetEdit::escape_snippet_bits(&mut out);
         for arg in &mut exprs {
             if let Arg::Ident(text) | Arg::Expr(text) = arg {
-                escape_snippet_bits(text)
+                SnippetEdit::escape_snippet_bits(text)
             }
         }
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
index 07bf294..1a3fc0f 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
@@ -210,6 +210,15 @@ pub fn apply(&self, text: &mut String) {
     pub fn into_edit_ranges(self) -> Vec<(u32, TextRange)> {
         self.0
     }
+
+    /// Escapes `\` and `$` so that they don't get interpreted as snippet-specific constructs.
+    ///
+    /// Note that we don't need to escape the other characters that can be escaped,
+    /// because they wouldn't be treated as snippet-specific constructs without '$'.
+    pub fn escape_snippet_bits(text: &mut String) {
+        stdx::replace(text, '\\', "\\\\");
+        stdx::replace(text, '$', "\\$");
+    }
 }
 
 pub struct SourceChangeBuilder {
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
index 4811dd6..4e3c491 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
@@ -1,7 +1,7 @@
 //! Handles the `Enter` key press, including comment continuation and
 //! indentation in brace-delimited constructs.
 
-use ide_db::{FilePosition, RootDatabase};
+use ide_db::{FilePosition, RootDatabase, source_change::SnippetEdit};
 use syntax::{
     AstNode, SmolStr, SourceFile,
     SyntaxKind::*,
@@ -108,19 +108,13 @@ fn on_enter_in_comment(
     Some(edit)
 }
 
-// for lack of a better place to put this function
-fn escape_snippet_bits(text: &mut String) {
-    stdx::replace(text, '\\', "\\\\");
-    stdx::replace(text, '$', "\\$");
-}
-
 fn on_enter_in_braces(l_curly: SyntaxToken, position: FilePosition) -> Option<TextEdit> {
     if l_curly.text_range().end() != position.offset {
         return None;
     }
 
     let (r_curly, mut content) = brace_contents_on_same_line(&l_curly)?;
-    escape_snippet_bits(&mut content);
+    SnippetEdit::escape_snippet_bits(&mut content);
     let indent = IndentLevel::from_token(&l_curly);
     Some(TextEdit::replace(
         TextRange::new(position.offset, r_curly.text_range().start()),