module List =
struct include List
let singleton x = List.cons x []
end

module Grep =
struct

open Cprg
open Cprg.Syntax

let blank, blank0 =
let ignore = lift (fun _ -> ()) in
let is_ws = function ' ' | '\t' -> true | _ -> false in
ignore @@ take_while1 is_ws, ignore @@ take_while is_ws

let word = take_while1 @@ function
'a' .. 'z' | '_' | '-' -> true | _ -> false

let equal = char '='

let listitem : (_ * [ `flag | `pair of string ]) code =
(lift2 (fun x y -> x, \`pair y) (word <* equal) word)
or (lift (fun x -> x, \`flag) word)

let chain = fix @@ fun chain ->
(lift2 List.cons (listitem <* blank) chain)
or (lift List.singleton listitem)

let chain' =
blank0 *> ((char 's' *> char 'e' *> char 't' *> blank *> chain) or chain) <* blank0

let parse_inner =
let [@warning "-26"] to_string = String.concat ";" % List.map (function
| x, \`flag -> "'" ^ x ^ "'"
| x, \`pair y -> "'" ^ x ^ "','" ^ y ^ "'") in
Cprg.parse_string ~consume:\`All chain'
%> function Result.Ok x ->
(* Eio.traceln "oar: oar-code: grep: cprg dict is [%s]" (to_string x); *) x
| Result.Error e -> failwith (Printf.sprintf "oar: oar-code: grep: cprg error is '%s'" e)

open Re

let vi_sign = alt [str "vim"; str "vi"]

let pat = seq [vi_sign; char ':'; group (any |> rep1); char ':']

let parse_outer_opt =
Re.exec_opt (Re.compile pat)
%> Option.map (fun g ->
let content = Re.Group.get g 1 in
(* Eio.traceln "oar: oar-code: grep: regexp content output is '%s'" content; *) content)

let vimline_opt = parse_outer_opt %> Option.map parse_inner

end

(* let filetype_to_language = function *)
(* | ("ocaml" | "typst") as x -> x *)
(* | s -> failwith (Printf.sprintf "oar: oar-code: filetype_to_language: unhandled filetype '%s'" s) *)

let extension_to_language filename =
match Filename.extension filename with
| ".ml" -> "ocaml"
| ".typ" -> "typst"
| ".yml" -> "yaml"
| ".org" -> "org"
| s -> failwith (Printf.sprintf "oar: oar-code: extension_to_language: unhandled extension '%s' of filename '%s'" s filename)

open Eio

include struct
module Language_infer = struct end end

let%include [@in (module Language_infer) [@as? of_file]]
lang_infer__of_file path =
let lang = Option.bind (Grep.vimline_opt @@ Path.load path) (
List.assoc_opt "ft" %> function Some (\`pair lang) -> Some lang | _ -> None) in
match lang with Some l -> l | None ->
extension_to_language @@ Path.native_exn path