···618618 Buffer.clear res_buff;
619619 Buffer.clear stderr_buff;
620620 Buffer.clear stdout_buff;
621621+ let input_len = String.length phrase in
621622 let phrase =
622622- let l = String.length phrase in
623623- if l >= 2 && String.sub phrase (l - 2) 2 = ";;" then phrase
623623+ if input_len >= 2 && String.sub phrase (input_len - 2) 2 = ";;" then phrase
624624 else phrase ^ ";;"
625625 in
626626 (* Bind the merlin-lib local store so Toploop.execute_phrase can access
···639639 let phr = Toploop.preprocess_phrase Format.err_formatter phr in
640640 ignore (Toploop.execute_phrase true pp_result phr : bool);
641641 (* Get location from phrase AST — use the last
642642- structure item so output appears after all defs *)
642642+ structure item so output appears after all defs.
643643+ Clamp to input_len so positions from the appended
644644+ ";;" suffix are never returned to the caller. *)
643645 let loc = match phr with
644646 | Parsetree.Ptop_def (_ :: _ as items) ->
645647 let last = List.nth items (List.length items - 1) in
646646- last.pstr_loc.loc_end.pos_cnum
648648+ min input_len last.pstr_loc.loc_end.pos_cnum
647649 | Parsetree.Ptop_dir { pdir_loc; _ } ->
648648- pdir_loc.loc_end.pos_cnum
649649- | _ -> lb.lex_curr_p.pos_cnum
650650+ min input_len pdir_loc.loc_end.pos_cnum
651651+ | _ -> min input_len lb.lex_curr_p.pos_cnum
650652 in
651653 (* Flush and get current output *)
652654 Format.pp_print_flush pp_result ();
···3838 in
3939 Jv.call deco_facet "from" [| field |] |> Code_mirror.Extension.of_jv)
40404141+let source_of_state s =
4242+ String.concat "\n" @@ Array.to_list @@ Array.map Jstr.to_string
4343+ @@ Code_mirror.Text.to_jstr_array
4444+ @@ Code_mirror.Editor.State.doc s
4545+4646+let source t = source_of_state @@ Code_mirror.Editor.View.state t.view
4747+4148let build_range_set cm =
4249 let open Code_mirror.Decoration in
4350 let doc = cm.current_doc in
5151+ (* Use the CM document length (in UTF-16 code units) for clamping, not
5252+ String.length which counts bytes — they differ for non-ASCII text. *)
5353+ let cm_len =
5454+ Code_mirror.Text.length
5555+ (Code_mirror.Editor.State.doc
5656+ (Code_mirror.Editor.View.state cm.view))
5757+ in
4458 let ranges =
4559 Array.of_list
4660 @@ List.map (fun (at, msg) ->
···5165 @@ List.map (fun (at, msg) ->
5266 let at = min at (String.length doc) in
5367 let at = find_line_ends at doc in
5454- let at = min at (max 0 (String.length doc - 1)) in
6868+ let at = min at (max 0 (cm_len - 1)) in
5569 (at, msg))
5670 @@ List.concat
5771 @@ List.map (fun (loc, lst) -> List.map (fun m -> (loc, m)) lst)
···92106 refresh_lines x;
93107 refresh_messages x;
94108 refresh_merlin x
9595-9696-let source_of_state s =
9797- String.concat "\n" @@ Array.to_list @@ Array.map Jstr.to_string
9898- @@ Code_mirror.Text.to_jstr_array
9999- @@ Code_mirror.Editor.State.doc s
100100-101101-let source t = source_of_state @@ Code_mirror.Editor.View.state t.view
102109103110let prefix_length a b =
104111 let rec go i =
···191198let add_message t loc msg = set_messages t ((loc, msg) :: t.messages)
192199193200let set_source t doc =
194194- Code_mirror.Editor.View.set_doc t.view (Jstr.of_string doc);
195195- (* Read back from CodeMirror to get the canonical form — the raw
196196- string may differ (e.g., HTML entity decoding, line ending
197197- normalization) from what CodeMirror stores internally. *)
201201+ (* Clear decorations AND replace the document in a single transaction
202202+ so CodeMirror never sees stale decoration positions. *)
203203+ let state = Code_mirror.Editor.View.state t.view in
204204+ let doc_len =
205205+ Code_mirror.Text.length (Code_mirror.Editor.State.doc state)
206206+ in
207207+ t.messages <- [];
208208+ let changes =
209209+ Jv.obj
210210+ [| ("from", Jv.of_int 0);
211211+ ("to", Jv.of_int doc_len);
212212+ ("insert", Jv.of_jstr (Jstr.of_string doc)) |]
213213+ in
214214+ let effect =
215215+ Code_mirror.State_effect.of_ set_messages_effect
216216+ (Code_mirror.Decoration.Range_set.to_jv
217217+ (Code_mirror.Decoration.Range_set.empty))
218218+ in
219219+ let txn =
220220+ Jv.obj [| ("changes", changes); ("effects", effect) |]
221221+ |> Code_mirror.Editor.View.Transaction.of_jv
222222+ in
223223+ Code_mirror.Editor.View.dispatch t.view txn;
224224+ (* Read back canonical form *)
198225 let canonical = source t in
199199- set_current_doc t canonical
226226+ t.current_doc <- canonical