@@ -46,10 +46,11 @@ func AppendEntry(
4646 idx := strings .Index (existingStr , targetSection )
4747 if idx != - 1 {
4848 // Find the end of the section header line
49- lineEnd := strings . Index (existingStr [idx :], " \n " )
49+ lineEnd := findNewline (existingStr [idx :])
5050 if lineEnd != - 1 {
51- insertPoint := idx + lineEnd + 1
52- return []byte (existingStr [:insertPoint ] + "\n " +
51+ insertPoint := idx + lineEnd
52+ insertPoint = skipNewline (existingStr , insertPoint )
53+ return []byte (existingStr [:insertPoint ] + config .NewlineLF +
5354 entry + existingStr [insertPoint :])
5455 }
5556 }
@@ -66,10 +67,10 @@ func AppendEntry(
6667 }
6768
6869 // Default (conventions): append at the end
69- if ! strings . HasSuffix (existingStr , " \n " ) {
70- existingStr += " \n "
70+ if ! endsWithNewline (existingStr ) {
71+ existingStr += config . NewlineLF
7172 }
72- return []byte (existingStr + " \n " + entry )
73+ return []byte (existingStr + config . NewlineLF + entry )
7374}
7475
7576// prependAfterHeader inserts an entry after a header line.
@@ -89,7 +90,7 @@ func prependAfterHeader(content, entry, header string) []byte {
8990 entryIdx := strings .Index (content , "## [" )
9091 if entryIdx != - 1 {
9192 // Insert before the first entry, with separator after
92- return []byte (content [:entryIdx ] + entry + " \n ---\n \n " + content [entryIdx :])
93+ return []byte (content [:entryIdx ] + entry + config . NewlineLF + " ---" + config . NewlineLF + config . NewlineLF + content [entryIdx :])
9394 }
9495
9596 // No existing entries - find header and insert after it
@@ -111,12 +112,12 @@ func prependAfterSeparator(content, entry string) []byte {
111112 entryIdx := strings .Index (content , "- **[" )
112113 if entryIdx != - 1 {
113114 // Insert before the first entry
114- return []byte (content [:entryIdx ] + entry + " \n " + content [entryIdx :])
115+ return []byte (content [:entryIdx ] + entry + config . NewlineLF + content [entryIdx :])
115116 }
116117
117118 // Also check for section-style learnings "## ["
118119 if entryIdx = strings .Index (content , "## [" ); entryIdx != - 1 {
119- return []byte (content [:entryIdx ] + entry + " \n ---\n \n " + content [entryIdx :])
120+ return []byte (content [:entryIdx ] + entry + config . NewlineLF + " ---" + config . NewlineLF + config . NewlineLF + content [entryIdx :])
120121 }
121122
122123 // No existing entries - find header and insert after it
@@ -125,7 +126,7 @@ func prependAfterSeparator(content, entry string) []byte {
125126
126127// insertAfterHeader finds a header line and inserts content after it.
127128//
128- // Skips blank lines and HTML comments between the header and insertion point.
129+ // Skips blank lines and ctx markers between the header and insertion point.
129130// Falls back to appending at the end if header is not found.
130131//
131132// Parameters:
@@ -138,23 +139,22 @@ func prependAfterSeparator(content, entry string) []byte {
138139func insertAfterHeader (content , entry , header string ) []byte {
139140 idx := strings .Index (content , header )
140141 if idx != - 1 {
141- lineEnd := strings . Index (content [idx :], " \n " )
142+ lineEnd := findNewline (content [idx :])
142143 if lineEnd != - 1 {
143- insertPoint := idx + lineEnd + 1
144- // Skip blank lines and comments
144+ insertPoint := idx + lineEnd
145+ insertPoint = skipNewline (content , insertPoint )
146+ // Skip blank lines and ctx markers
145147 for insertPoint < len (content ) {
146- if content [ insertPoint ] == '\n' {
147- insertPoint ++
148+ if n := skipNewline ( content , insertPoint ); n > insertPoint {
149+ insertPoint = n
148150 } else if insertPoint + len (config .CommentOpen ) <= len (content ) &&
149151 content [insertPoint :insertPoint + len (config .CommentOpen )] == config .CommentOpen {
150- // Skip HTML comment
152+ // Skip ctx marker
151153 endComment := strings .Index (content [insertPoint :], config .CommentClose )
152154 if endComment != - 1 {
153155 insertPoint += endComment + len (config .CommentClose )
154- // Skip trailing whitespace after comment
155- for insertPoint < len (content ) && (content [insertPoint ] == '\n' || content [insertPoint ] == ' ' ) {
156- insertPoint ++
157- }
156+ // Skip trailing whitespace after marker
157+ insertPoint = skipWhitespace (content , insertPoint )
158158 } else {
159159 break
160160 }
@@ -167,8 +167,56 @@ func insertAfterHeader(content, entry, header string) []byte {
167167 }
168168
169169 // Fallback: append at the end
170- if ! strings . HasSuffix (content , " \n " ) {
171- content += " \n "
170+ if ! endsWithNewline (content ) {
171+ content += config . NewlineLF
172172 }
173- return []byte (content + "\n " + entry )
173+ return []byte (content + config .NewlineLF + entry )
174+ }
175+
176+ // findNewline returns the index of the first newline (CRLF or LF) in s.
177+ // Returns -1 if no newline is found.
178+ func findNewline (s string ) int {
179+ for i := 0 ; i < len (s ); i ++ {
180+ if i + 1 < len (s ) && s [i ] == '\r' && s [i + 1 ] == '\n' {
181+ return i
182+ }
183+ if s [i ] == '\n' {
184+ return i
185+ }
186+ }
187+ return - 1
188+ }
189+
190+ // skipNewline advances pos past a newline (CRLF or LF) if present.
191+ // Returns the new position (unchanged if no newline at pos).
192+ func skipNewline (s string , pos int ) int {
193+ if pos >= len (s ) {
194+ return pos
195+ }
196+ if pos + 1 < len (s ) && s [pos ] == '\r' && s [pos + 1 ] == '\n' {
197+ return pos + 2
198+ }
199+ if s [pos ] == '\n' {
200+ return pos + 1
201+ }
202+ return pos
203+ }
204+
205+ // skipWhitespace advances pos past any whitespace (space, tab, newline).
206+ func skipWhitespace (s string , pos int ) int {
207+ for pos < len (s ) {
208+ if n := skipNewline (s , pos ); n > pos {
209+ pos = n
210+ } else if s [pos ] == ' ' || s [pos ] == '\t' {
211+ pos ++
212+ } else {
213+ break
214+ }
215+ }
216+ return pos
217+ }
218+
219+ // endsWithNewline reports whether s ends with a newline (CRLF or LF).
220+ func endsWithNewline (s string ) bool {
221+ return strings .HasSuffix (s , config .NewlineCRLF ) || strings .HasSuffix (s , config .NewlineLF )
174222}
0 commit comments