package lsp

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file io.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
118
119
120
121
122
123
124
125
126
127
128
129
open Import

exception Error of string

let () =
  Printexc.register_printer (function
    | Error msg -> Some ("Error: " ^ msg)
    | _ -> None)

let caseless_equal a b =
  if a == b then
    true
  else
    let len = String.length a in
    len = String.length b
    &&
    let stop = ref false in
    let idx = ref 0 in
    while (not !stop) && !idx < len do
      let c1 = String.unsafe_get a !idx in
      let c2 = String.unsafe_get b !idx in
      if Char.lowercase_ascii c1 <> Char.lowercase_ascii c2 then stop := true;
      incr idx
    done;
    not !stop

let content_type_lowercase =
  String.lowercase_ascii Header.Private.Key.content_type

let content_length_lowercase =
  String.lowercase_ascii Header.Private.Key.content_length

module Make (Io : sig
  type 'a t

  val return : 'a -> 'a t

  val raise : exn -> 'a t

  module O : sig
    val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t

    val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
  end
end) (Chan : sig
  type input

  type output

  val read_line : input -> string option Io.t

  val read_exactly : input -> int -> string option Io.t

  val write : output -> string -> unit Io.t
end) =
struct
  open Io.O

  let read_header =
    let init_content_length = -1 in
    let rec loop chan content_length content_type =
      let* line = Chan.read_line chan in
      match line with
      | None -> Io.return None
      | Some ""
      | Some "\r" ->
        Io.return (Some (content_length, content_type))
      | Some line -> (
        match String.lsplit2 ~on:':' line with
        | None -> loop chan content_length content_type
        | Some (k, v) ->
          let k = String.trim k in
          if
            caseless_equal k content_length_lowercase
            && content_length = init_content_length
          then
            let content_length = int_of_string_opt (String.trim v) in
            match content_length with
            | None -> Io.raise (Error "Content-Length is invalid")
            | Some content_length -> loop chan content_length content_type
          else if caseless_equal k content_type_lowercase && content_type = None
          then
            let content_type = String.trim v in
            loop chan content_length (Some content_type)
          else
            loop chan content_length content_type)
    in
    fun chan ->
      let open Io.O in
      let* res = loop chan init_content_length None in
      match res with
      | None -> Io.return None
      | Some (content_length, content_type) ->
        let+ () =
          if content_length = init_content_length then
            Io.raise (Error "content length absent")
          else
            Io.return ()
        in
        Some (Header.create ?content_type ~content_length ())

  let read =
    let req json = Jsonrpc.Message (Jsonrpc.Message.either_of_yojson json) in
    let resp json = Jsonrpc.Response (Jsonrpc.Response.t_of_yojson json) in
    let packet =
      let open Json.O in
      req <|> resp
    in
    fun chan ->
      let* header = read_header chan in
      match header with
      | None -> Io.return None
      | Some header -> (
        let len = Header.content_length header in
        let* buf = Chan.read_exactly chan len in
        match buf with
        | None -> Io.raise (Error "unable to read json")
        | Some buf ->
          let json = Json.of_string buf in
          Io.return (Some (packet json)))

  let write chan packet =
    let json = Jsonrpc.yojson_of_packet packet in
    let data = Json.to_string json in
    let content_length = String.length data in
    let header = Header.create ~content_length () in
    let* () = Chan.write chan (Header.to_string header) in
    Chan.write chan data
end
OCaml

Innovation. Community. Security.