this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix RangeError: decoration positions exceeding document length

Three fixes for "Position N is out of range for changeset" errors:

1. js_top_worker: clamp output_at loc to input length — the ";;"
appended for parsing made pos_cnum extend past the original source

2. x-ocaml/editor: clamp decoration positions against CM document
length (Text.length) not OCaml String.length — they differ for
non-ASCII text (UTF-16 vs bytes). Also combine doc replacement +
decoration clear into a single transaction in set_source, and
read actual CM doc in build_range_set

3. jsoo-code-mirror/decoration: defensive safe_map that catches
JS RangeError in RangeSet.map and returns empty

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+40 -13
+40 -13
src/editor.ml
··· 38 38 in 39 39 Jv.call deco_facet "from" [| field |] |> Code_mirror.Extension.of_jv) 40 40 41 + let source_of_state s = 42 + String.concat "\n" @@ Array.to_list @@ Array.map Jstr.to_string 43 + @@ Code_mirror.Text.to_jstr_array 44 + @@ Code_mirror.Editor.State.doc s 45 + 46 + let source t = source_of_state @@ Code_mirror.Editor.View.state t.view 47 + 41 48 let build_range_set cm = 42 49 let open Code_mirror.Decoration in 43 50 let doc = cm.current_doc in 51 + (* Use the CM document length (in UTF-16 code units) for clamping, not 52 + String.length which counts bytes — they differ for non-ASCII text. *) 53 + let cm_len = 54 + Code_mirror.Text.length 55 + (Code_mirror.Editor.State.doc 56 + (Code_mirror.Editor.View.state cm.view)) 57 + in 44 58 let ranges = 45 59 Array.of_list 46 60 @@ List.map (fun (at, msg) -> ··· 51 65 @@ List.map (fun (at, msg) -> 52 66 let at = min at (String.length doc) in 53 67 let at = find_line_ends at doc in 54 - let at = min at (max 0 (String.length doc - 1)) in 68 + let at = min at (max 0 (cm_len - 1)) in 55 69 (at, msg)) 56 70 @@ List.concat 57 71 @@ List.map (fun (loc, lst) -> List.map (fun m -> (loc, m)) lst) ··· 92 106 refresh_lines x; 93 107 refresh_messages x; 94 108 refresh_merlin x 95 - 96 - let source_of_state s = 97 - String.concat "\n" @@ Array.to_list @@ Array.map Jstr.to_string 98 - @@ Code_mirror.Text.to_jstr_array 99 - @@ Code_mirror.Editor.State.doc s 100 - 101 - let source t = source_of_state @@ Code_mirror.Editor.View.state t.view 102 109 103 110 let prefix_length a b = 104 111 let rec go i = ··· 191 198 let add_message t loc msg = set_messages t ((loc, msg) :: t.messages) 192 199 193 200 let set_source t doc = 194 - Code_mirror.Editor.View.set_doc t.view (Jstr.of_string doc); 195 - (* Read back from CodeMirror to get the canonical form — the raw 196 - string may differ (e.g., HTML entity decoding, line ending 197 - normalization) from what CodeMirror stores internally. *) 201 + (* Clear decorations AND replace the document in a single transaction 202 + so CodeMirror never sees stale decoration positions. *) 203 + let state = Code_mirror.Editor.View.state t.view in 204 + let doc_len = 205 + Code_mirror.Text.length (Code_mirror.Editor.State.doc state) 206 + in 207 + t.messages <- []; 208 + let changes = 209 + Jv.obj 210 + [| ("from", Jv.of_int 0); 211 + ("to", Jv.of_int doc_len); 212 + ("insert", Jv.of_jstr (Jstr.of_string doc)) |] 213 + in 214 + let effect = 215 + Code_mirror.State_effect.of_ set_messages_effect 216 + (Code_mirror.Decoration.Range_set.to_jv 217 + (Code_mirror.Decoration.Range_set.empty)) 218 + in 219 + let txn = 220 + Jv.obj [| ("changes", changes); ("effects", effect) |] 221 + |> Code_mirror.Editor.View.Transaction.of_jv 222 + in 223 + Code_mirror.Editor.View.dispatch t.view txn; 224 + (* Read back canonical form *) 198 225 let canonical = source t in 199 - set_current_doc t canonical 226 + t.current_doc <- canonical