package miaou-driver-matrix

  1. Overview
  2. Docs

Source file matrix_ansi_writer.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
(*****************************************************************************)
(*                                                                           *)
(* SPDX-License-Identifier: MIT                                              *)
(* Copyright (c) 2025 Nomadic Labs <contact@nomadic-labs.com>                *)
(*                                                                           *)
(*****************************************************************************)

type t = {
  mutable current_style : Matrix_cell.style;
  mutable current_url : string;
}

let create () = {current_style = Matrix_cell.default_style; current_url = ""}

let reset t =
  t.current_style <- Matrix_cell.default_style ;
  t.current_url <- ""

(* ANSI control sequences *)
let cursor_hide = "\027[?25l"

let cursor_show = "\027[?25h"

let cursor_home = "\027[H"

let reset_style = "\027[0m"

(* Move cursor to row, col (converting from 0-indexed to 1-indexed for ANSI) *)
let cursor_move ~row ~col = Printf.sprintf "\027[%d;%dH" (row + 1) (col + 1)

(* Generate SGR sequence for a style *)
let style_to_sgr style =
  let open Matrix_cell in
  let buf = Buffer.create 32 in

  (* Always start with reset to clear previous state *)
  Buffer.add_string buf "\027[0" ;

  (* Bold *)
  if style.bold then Buffer.add_string buf ";1" ;

  (* Dim *)
  if style.dim then Buffer.add_string buf ";2" ;

  (* Underline *)
  if style.underline then Buffer.add_string buf ";4" ;

  (* Reverse *)
  if style.reverse then Buffer.add_string buf ";7" ;

  (* Foreground color: 0-15 use basic ANSI, 16-255 use 256-color *)
  if style.fg >= 0 && style.fg <= 7 then
    Buffer.add_string buf (Printf.sprintf ";%d" (30 + style.fg))
  else if style.fg >= 8 && style.fg <= 15 then
    Buffer.add_string buf (Printf.sprintf ";%d" (90 + style.fg - 8))
  else if style.fg >= 16 && style.fg <= 255 then
    Buffer.add_string buf (Printf.sprintf ";38;5;%d" style.fg) ;

  (* Background color: same logic *)
  if style.bg >= 0 && style.bg <= 7 then
    Buffer.add_string buf (Printf.sprintf ";%d" (40 + style.bg))
  else if style.bg >= 8 && style.bg <= 15 then
    Buffer.add_string buf (Printf.sprintf ";%d" (100 + style.bg - 8))
  else if style.bg >= 16 && style.bg <= 255 then
    Buffer.add_string buf (Printf.sprintf ";48;5;%d" style.bg) ;

  Buffer.add_char buf 'm' ;
  Buffer.contents buf

(* Render changes to ANSI string *)
let render t changes =
  let buf = Buffer.create 1024 in

  List.iter
    (fun change ->
      match change with
      | Matrix_diff.MoveTo (row, col) ->
          Buffer.add_string buf (cursor_move ~row ~col)
      | Matrix_diff.SetStyle style ->
          if not (Matrix_cell.style_equal style t.current_style) then begin
            (* Handle OSC 8 hyperlink changes *)
            if not (String.equal style.url t.current_url) then begin
              (* Close current hyperlink if active *)
              if t.current_url <> "" then Buffer.add_string buf "\027]8;;\027\\" ;
              (* Open new hyperlink if needed *)
              if style.url <> "" then (
                Buffer.add_string buf "\027]8;;" ;
                Buffer.add_string buf style.url ;
                Buffer.add_string buf "\027\\") ;
              t.current_url <- style.url
            end ;
            (* Handle SGR style changes (fg, bg, bold, dim, underline, reverse) *)
            let sgr_differs =
              style.fg <> t.current_style.fg
              || style.bg <> t.current_style.bg
              || style.bold <> t.current_style.bold
              || style.dim <> t.current_style.dim
              || style.underline <> t.current_style.underline
              || style.reverse <> t.current_style.reverse
            in
            if sgr_differs then begin
              if
                style.fg = -1 && style.bg = -1 && (not style.bold)
                && (not style.dim) && (not style.underline) && not style.reverse
              then Buffer.add_string buf reset_style
              else Buffer.add_string buf (style_to_sgr style)
            end ;
            t.current_style <- style
          end
      | Matrix_diff.WriteChar c -> Buffer.add_string buf c
      | Matrix_diff.WriteRun (c, n) ->
          for _ = 1 to n do
            Buffer.add_string buf c
          done)
    changes ;

  Buffer.contents buf