MCPcopy Index your code
hub / github.com/coder/coder / fuzzyReplace

Function fuzzyReplace

agent/agentfiles/files.go:1100–1213  ·  view source on GitHub ↗

fuzzyReplace attempts to find `search` inside `content` and replace it with `replace`. It uses a cascading match strategy inspired by openai/codex's apply_patch: 1. Exact substring match (byte-for-byte). 2. Line-by-line match ignoring trailing whitespace on each line. 3. Line-by-line match ignoring

(content string, edit workspacesdk.FileEdit)

Source from the content-addressed store, hash-verified

1098// lines) while letting the caller drive deliberate rewrites of
1099// leading whitespace or endings.
1100func fuzzyReplace(content string, edit workspacesdk.FileEdit) (string, error) {
1101 search := edit.Search
1102 replace := edit.Replace
1103
1104 // An empty search string has no meaningful interpretation: it
1105 // matches at every byte position, which means the caller has not
1106 // told us what they want to replace. Reject explicitly so
1107 // replace_all=true can't silently inject the replacement between
1108 // every byte.
1109 if search == "" {
1110 return "", xerrors.New("search string must not be empty; include the " +
1111 "text you want to match")
1112 }
1113
1114 // Split up front so the ending-normalization rule can inspect
1115 // all three before any matching pass.
1116 contentLines := strings.SplitAfter(content, "\n")
1117 searchLines := strings.SplitAfter(search, "\n")
1118 // A trailing newline in the search produces an empty final element
1119 // from SplitAfter. Drop it so it doesn't interfere with line
1120 // matching.
1121 if len(searchLines) > 0 && searchLines[len(searchLines)-1] == "" {
1122 searchLines = searchLines[:len(searchLines)-1]
1123 }
1124 replaceLines := strings.SplitAfter(replace, "\n")
1125 if len(replaceLines) > 0 && replaceLines[len(replaceLines)-1] == "" {
1126 replaceLines = replaceLines[:len(replaceLines)-1]
1127 }
1128
1129 // Ending normalization. If replace has a consistent internal
1130 // ending, force every spliced interior line to the file's
1131 // dominant ending. If search also has a consistent internal
1132 // ending and it disagrees with replace's, the caller signaled
1133 // intent to rewrite endings; restrict the match to pass 1 so
1134 // CRLF/LF interchange at pass 2 can't silently bridge a search
1135 // that doesn't actually occur in the file.
1136 var forcedEnding string
1137 searchInternal, searchOK := internalLineEnding(searchLines)
1138 replaceInternal, replaceOK := internalLineEnding(replaceLines)
1139 if replaceOK {
1140 forcedEnding = dominantFileEnding(contentLines)
1141 }
1142 callerEndingIntent := searchOK && replaceOK && searchInternal != replaceInternal
1143
1144 // Pass 1 - exact substring match. Normalize replace's interior
1145 // endings to the file's style unless the caller's search/replace
1146 // disagreement signaled intent to rewrite endings.
1147 pass1Replace := replace
1148 if forcedEnding != "" && !callerEndingIntent && replaceInternal != forcedEnding {
1149 pass1Replace = rewriteInternalEnding(replaceLines, forcedEnding)
1150 }
1151 if strings.Contains(content, search) {
1152 if edit.ReplaceAll {
1153 return strings.ReplaceAll(content, search, pass1Replace), nil
1154 }
1155 count := strings.Count(content, search)
1156 if count > 1 {
1157 return "", xerrors.Errorf("search string matches %d occurrences "+

Callers 1

prepareFileEditMethod · 0.85

Calls 13

internalLineEndingFunction · 0.85
dominantFileEndingFunction · 0.85
rewriteInternalEndingFunction · 0.85
splitEndingFunction · 0.85
endingsMatchFunction · 0.85
fuzzyReplaceLinesFunction · 0.85
miscountHintFunction · 0.85
inversionHintFunction · 0.85
CountMethod · 0.80
ReplaceMethod · 0.80
NewMethod · 0.65
ContainsMethod · 0.45

Tested by

no test coverage detected