terminal user interface to jujutsu. Focused on speed and clarity
9
fork

Configure Feed

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

reorgansie things

faldor20 5958071d 41122b29

+253 -5902
+4
.envrc
··· 1 + # shellcheck shell=bash 2 + # For use with direnv. 3 + # Installing nix-direnv will ensure a smoother experience. 4 + use flake
+1
.gitignore
··· 1 1 _build 2 2 _opam 3 3 .jj 4 + duniverse
-6
eio-process/.gitattributes
··· 1 - # Tell github that .ml and .mli files are OCaml 2 - *.ml linguist-language=OCaml 3 - *.mli linguist-language=OCaml 4 - 5 - # Disable syntax detection for cram tests 6 - *.t linguist-language=Text
-64
eio-process/.github/workflows/ci.yml
··· 1 - name: ci 2 - 3 - on: 4 - - pull_request 5 - - push 6 - 7 - jobs: 8 - build: 9 - strategy: 10 - fail-fast: false 11 - matrix: 12 - os: 13 - - ubuntu-latest 14 - ocaml-compiler: 15 - - 5.1.x 16 - 17 - runs-on: ${{ matrix.os }} 18 - 19 - steps: 20 - - name: Checkout code 21 - uses: actions/checkout@v4 22 - 23 - - name: Setup OCaml 24 - uses: ocaml/setup-ocaml@v2 25 - with: 26 - ocaml-compiler: ${{ matrix.ocaml-compiler }} 27 - opam-repositories: | 28 - default: https://github.com/ocaml/opam-repository.git 29 - mbarbin: https://github.com/mbarbin/opam-repository.git 30 - # janestreet-bleeding: https://ocaml.janestreet.com/opam-repository 31 - # janestreet-bleeding-external: https://github.com/janestreet/opam-repository.git#external-packages 32 - 33 - - name: Install dependencies 34 - run: opam install . --deps-only --with-doc --with-test 35 - 36 - - name: Build 37 - run: opam exec -- dune build @all @lint 38 - 39 - - name: Run tests 40 - run: | 41 - mkdir $BISECT_DIR 42 - opam exec -- dune runtest --instrument-with bisect_ppx 43 - env: 44 - BISECT_DIR: ${{ runner.temp }}/_bisect_ppx_data 45 - BISECT_FILE: ${{ runner.temp }}/_bisect_ppx_data/data 46 - 47 - - name: Send coverage report to Coveralls 48 - run: opam exec -- bisect-ppx-report send-to Coveralls --coverage-path $BISECT_DIR 49 - env: 50 - BISECT_DIR: ${{ runner.temp }}/_bisect_ppx_data 51 - COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 - PULL_REQUEST_NUMBER: ${{ github.event.number }} 53 - 54 - - name: Check for uncommitted changes 55 - run: git diff --exit-code 56 - 57 - - name: Lint opam 58 - uses: ocaml/setup-ocaml/lint-opam@v2 59 - 60 - - name: Lint fmt 61 - uses: ocaml/setup-ocaml/lint-fmt@v2 62 - 63 - - name: Lint doc 64 - uses: ocaml/setup-ocaml/lint-doc@v2
-4
eio-process/.gitignore
··· 1 - _opam 2 - _build 3 - _coverage 4 - *.install
-2
eio-process/.ocamlformat
··· 1 - version=0.26.2 2 - profile=janestreet
-7
eio-process/.vscode/settings.json
··· 1 - { 2 - "cSpell.words": [ 3 - "janestreet", 4 - "odoc", 5 - "opam" 6 - ] 7 - }
-17
eio-process/CHANGES.md
··· 1 - ## 0.0.2 (2024-03-13) 2 - 3 - ### Changed 4 - 5 - - Upgrade `eio` to `1.0` (no change required). 6 - - Uses `expect-test-helpers` (reduce core dependencies) 7 - - Upgrade `eio` to `0.15`. 8 - - Run `ppx_js_style` as a linter & make it a `dev` dependency. 9 - - Upgrade GitHub workflows `actions/checkout` to v4. 10 - - In CI, specify build target `@all`, and add `@lint`. 11 - - List ppxs instead of `ppx_jane`. 12 - 13 - ## 0.0.1 (2024-02-25) 14 - 15 - ### Added 16 - 17 - - Add an initial API.
-21
eio-process/LICENSE
··· 1 - MIT License 2 - 3 - Copyright (c) 2023 Mathieu Barbin 4 - 5 - Permission is hereby granted, free of charge, to any person obtaining a copy 6 - of this software and associated documentation files (the "Software"), to deal 7 - in the Software without restriction, including without limitation the rights 8 - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 - copies of the Software, and to permit persons to whom the Software is 10 - furnished to do so, subject to the following conditions: 11 - 12 - The above copyright notice and this permission notice shall be included in all 13 - copies or substantial portions of the Software. 14 - 15 - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 - SOFTWARE.
-18
eio-process/LICENSE.eio
··· 1 - This project extends the module `Eio.Process` from the 2 - [eio](https://github.com/ocaml-multicore/eio) project, which has the 3 - following license: 4 - 5 - Copyright (C) 2021 Anil Madhavapeddy 6 - Copyright (C) 2022 Thomas Leonard 7 - 8 - Permission to use, copy, modify, and distribute this software for any 9 - purpose with or without fee is hereby granted, provided that the above 10 - copyright notice and this permission notice appear in all copies. 11 - 12 - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-25
eio-process/LICENSE.janestreet
··· 1 - This project took inspiration from the `Async_unix.Process` module, 2 - from the [async_unix](https://github.com/janestreet/async_unix) 3 - project, which has the following license: 4 - 5 - The MIT License 6 - 7 - Copyright (c) 2008--2023 Jane Street Group, LLC <opensource-contacts@janestreet.com> 8 - 9 - Permission is hereby granted, free of charge, to any person obtaining a copy 10 - of this software and associated documentation files (the "Software"), to deal 11 - in the Software without restriction, including without limitation the rights 12 - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 - copies of the Software, and to permit persons to whom the Software is 14 - furnished to do so, subject to the following conditions: 15 - 16 - The above copyright notice and this permission notice shall be included in all 17 - copies or substantial portions of the Software. 18 - 19 - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 - SOFTWARE.
-34
eio-process/Makefile
··· 1 - .PHONY: all 2 - all: build 3 - 4 - .PHONY: build 5 - build: 6 - opam exec -- dune build 7 - 8 - .PHONY: test 9 - test: 10 - opam exec -- dune runtest 11 - 12 - .PHONY: fmt 13 - fmt: 14 - opam exec -- dune build @fmt --auto-promote 15 - 16 - .PHONY: lint 17 - lint: 18 - opam lint 19 - opam exec -- opam-dune-lint 20 - 21 - .PHONY: deps 22 - deps: 23 - opam install . --deps-only --with-doc --with-test 24 - 25 - .PHONY: doc 26 - doc: 27 - opam exec -- dune build @doc 28 - 29 - .PHONY: clean 30 - clean: 31 - opam exec -- dune clean 32 - 33 - .PHONY: check-all 34 - check-all: deps all test doc clean lint fmt
-43
eio-process/README.md
··· 1 - # eio-process 2 - 3 - [![CI Status](https://github.com/mbarbin/eio-process/workflows/ci/badge.svg)](https://github.com/mbarbin/eio-process/actions/workflows/ci.yml) 4 - [![Coverage Status](https://coveralls.io/repos/github/mbarbin/eio-process/badge.svg?branch=main)](https://coveralls.io/github/mbarbin/eio-process?branch=main) 5 - 6 - This is an experimental library to spawn external processes in 7 - [Eio](https://github.com/ocaml-multicore/eio) with an api that resembles 8 - [Async.Process](https://github.com/janestreet/async_unix). 9 - 10 - This project re-uses some function and type names from the `Async_unix.Process` 11 - interface. The implementation however is quite different, since the original 12 - runs in the `Async` monad, whereas this lib targets `Eio`. 13 - 14 - ## Motivation 15 - 16 - We find that this API offers convenient wrappers that we believe are a good fit 17 - on top of the core functionality offered by `Eio.Process`. 18 - 19 - ## Usage 20 - 21 - `Eio_process` is meant to be used directly as a top-level module, alongside 22 - other `Eio` modules. We do not recommend shadowing `Eio.Process` with 23 - `Eio_process` in user code at this time. 24 - 25 - ## Acknowledgements 26 - 27 - We would like to express our gratitude to the `Eio` developers for their work on 28 - the [eio](https://github.com/ocaml-multicore/eio) project, and for the original 29 - module `Eio.Process` that this project extends. `Eio` is released under the 30 - terms of an ISC License. Its copyright and permission notice are included at the 31 - root of this project, in the file `LICENSE.eio`. 32 - 33 - We also appreciate the work done by the async team at Jane Street and their 34 - contribution to the open source community. We're thankful for the api exposed by 35 - the `Async_unix.Process` module which we took inspiration from in this project. 36 - `Async_unix` is released under the terms of an `MIT` License. Its copyright and 37 - permission notice are included at the root of this project, in the file 38 - `LICENSE.janestreet`. 39 - 40 - ## Code documentation 41 - 42 - The code documentation of the latest release is built with `odoc` and published 43 - to `GitHub` pages [here](https://mbarbin.github.io/eio-process).
-4
eio-process/dune
··· 1 - (env 2 - (dev 3 - (odoc 4 - (warnings fatal))))
-85
eio-process/dune-project
··· 1 - (lang dune 3.15) 2 - 3 - (name eio-process) 4 - 5 - (generate_opam_files) 6 - 7 - (license MIT) 8 - 9 - (authors "Mathieu Barbin") 10 - 11 - (maintainers "Mathieu Barbin") 12 - 13 - (source 14 - (github mbarbin/eio-process)) 15 - 16 - (documentation "https://mbarbin.github.io/eio-process/") 17 - 18 - (package 19 - (name eio-process) 20 - (synopsis 21 - "Run external processes in [Eio] with an api that resembles [Async_unix.Process]") 22 - (depends 23 - (ocaml 24 - (>= 5.1)) 25 - (base 26 - (and 27 - (>= v0.16) 28 - (< v0.17))) 29 - (bisect_ppx 30 - (and 31 - :dev 32 - (>= 2.8.3))) 33 - (eio 34 - (>= 1.0)) 35 - (eio_main 36 - (and 37 - :with-test 38 - (>= 1.0))) 39 - (expect-test-helpers 40 - (and 41 - :with-test 42 - (>= v0.16) 43 - (< v0.17))) 44 - (parsexp 45 - (and 46 - (>= v0.16) 47 - (< v0.17))) 48 - (ppx_compare 49 - (and 50 - (>= v0.16) 51 - (< v0.17))) 52 - (ppx_enumerate 53 - (and 54 - (>= v0.16) 55 - (< v0.17))) 56 - (ppx_expect 57 - (and 58 - :with-test 59 - (>= v0.16) 60 - (< v0.17))) 61 - (ppx_hash 62 - (and 63 - (>= v0.16) 64 - (< v0.17))) 65 - (ppx_here 66 - (and 67 - (>= v0.16) 68 - (< v0.17))) 69 - (ppx_js_style 70 - (and 71 - :dev 72 - (>= v0.16) 73 - (< v0.17))) 74 - (ppx_let 75 - (and 76 - (>= v0.16) 77 - (< v0.17))) 78 - (ppx_sexp_conv 79 - (and 80 - (>= v0.16) 81 - (< v0.17))) 82 - (ppx_sexp_value 83 - (and 84 - (>= v0.16) 85 - (< v0.17)))))
-45
eio-process/eio-process.opam
··· 1 - # This file is generated by dune, edit dune-project instead 2 - opam-version: "2.0" 3 - synopsis: 4 - "Run external processes in [Eio] with an api that resembles [Async_unix.Process]" 5 - maintainer: ["Mathieu Barbin"] 6 - authors: ["Mathieu Barbin"] 7 - license: "MIT" 8 - homepage: "https://github.com/mbarbin/eio-process" 9 - doc: "https://mbarbin.github.io/eio-process/" 10 - bug-reports: "https://github.com/mbarbin/eio-process/issues" 11 - depends: [ 12 - "dune" {>= "3.15"} 13 - "ocaml" {>= "5.1"} 14 - "base" {>= "v0.16" & < "v0.17"} 15 - "bisect_ppx" {dev & >= "2.8.3"} 16 - "eio" {>= "1.0"} 17 - "eio_main" {with-test & >= "1.0"} 18 - "expect-test-helpers" {with-test & >= "v0.16" & < "v0.17"} 19 - "parsexp" {>= "v0.16" & < "v0.17"} 20 - "ppx_compare" {>= "v0.16" & < "v0.17"} 21 - "ppx_enumerate" {>= "v0.16" & < "v0.17"} 22 - "ppx_expect" {with-test & >= "v0.16" & < "v0.17"} 23 - "ppx_hash" {>= "v0.16" & < "v0.17"} 24 - "ppx_here" {>= "v0.16" & < "v0.17"} 25 - "ppx_js_style" {dev & >= "v0.16" & < "v0.17"} 26 - "ppx_let" {>= "v0.16" & < "v0.17"} 27 - "ppx_sexp_conv" {>= "v0.16" & < "v0.17"} 28 - "ppx_sexp_value" {>= "v0.16" & < "v0.17"} 29 - "odoc" {with-doc} 30 - ] 31 - build: [ 32 - ["dune" "subst"] {dev} 33 - [ 34 - "dune" 35 - "build" 36 - "-p" 37 - name 38 - "-j" 39 - jobs 40 - "@install" 41 - "@runtest" {with-test} 42 - "@doc" {with-doc} 43 - ] 44 - ] 45 - dev-repo: "git+https://github.com/mbarbin/eio-process.git"
-27
eio-process/src/dune
··· 1 - (library 2 - (name eio_process) 3 - (public_name eio-process) 4 - (flags 5 - :standard 6 - -w 7 - +a-4-40-41-42-44-45-48-66 8 - -warn-error 9 - +a 10 - -open 11 - Base 12 - -open 13 - Or_error.Let_syntax) 14 - (libraries base eio parsexp) 15 - (instrumentation 16 - (backend bisect_ppx)) 17 - (lint 18 - (pps ppx_js_style -check-doc-comments)) 19 - (preprocess 20 - (pps 21 - ppx_compare 22 - ppx_enumerate 23 - ppx_hash 24 - ppx_here 25 - ppx_let 26 - ppx_sexp_conv 27 - ppx_sexp_value)))
-147
eio-process/src/eio_process.ml
··· 1 - module Exit_status = struct 2 - [@@@coverage off] 3 - 4 - type t = 5 - [ `Exited of int 6 - | `Signaled of int 7 - ] 8 - [@@deriving sexp_of] 9 - end 10 - 11 - module Lines_or_sexp = struct 12 - type t = 13 - | Lines of string list 14 - | Sexp of Sexp.t 15 - 16 - let sexp_of_t t = 17 - match t with 18 - | Lines [] -> [%sexp ""] 19 - | Lines lines -> [%sexp (lines : string list)] 20 - | Sexp sexp -> sexp 21 - ;; 22 - 23 - let create string = 24 - try Sexp (Parsexp.Conv_single.parse_string_exn string Fn.id) with 25 - | _ -> Lines (String.split_lines string) 26 - ;; 27 - end 28 - 29 - module Output = struct 30 - type t = 31 - { stdout : string 32 - ; stderr : string 33 - ; exit_status : Exit_status.t 34 - } 35 - 36 - let sexp_of_t { stdout; stderr; exit_status } = 37 - [%sexp 38 - { stdout = (Lines_or_sexp.create stdout : Lines_or_sexp.t) 39 - ; stderr = (Lines_or_sexp.create stderr : Lines_or_sexp.t) 40 - ; exit_status : Exit_status.t 41 - }] 42 - ;; 43 - 44 - let exited t ~accept_exit_codes = 45 - match 46 - match t.exit_status with 47 - | `Exited code -> List.Assoc.find accept_exit_codes ~equal:Int.equal code 48 - | `Signaled _ -> None 49 - with 50 - | Some a -> Ok a 51 - | None -> 52 - Or_error.error_s 53 - [%sexp 54 - "unexpected exit status" 55 - , { accept_exit_codes = (List.map accept_exit_codes ~f:fst : int list) }] 56 - ;; 57 - 58 - let exit ?(accept_nonzero_exit = []) t = 59 - exited 60 - t 61 - ~accept_exit_codes: 62 - ((0, ()) :: List.map accept_nonzero_exit ~f:(fun code -> code, ())) 63 - ;; 64 - 65 - let exit_and_stdout ?accept_nonzero_exit t = 66 - let%map () = exit t ?accept_nonzero_exit in 67 - t.stdout 68 - ;; 69 - 70 - let expect_no_output ?(accept_nonzero_exit = []) t = 71 - let%bind stdout = exit_and_stdout t ~accept_nonzero_exit in 72 - if String.is_empty stdout then Ok () else Or_error.error_string "expected no output" 73 - ;; 74 - end 75 - 76 - exception User_error of Error.t 77 - 78 - let run ~process_mgr ~cwd ?stdin ?env ~prog ~args () ~f = 79 - Eio.Switch.run 80 - @@ fun sw -> 81 - let r, w = Eio.Process.pipe process_mgr ~sw in 82 - let re, we = Eio.Process.pipe process_mgr ~sw in 83 - let exit_status_r : [ Exit_status.t | `Unknown ] ref = ref `Unknown in 84 - let stdout_r = ref "" in 85 - let stderr_r = ref "" in 86 - try 87 - let child = 88 - Eio.Process.spawn 89 - ~sw 90 - process_mgr 91 - ~cwd 92 - ?stdin 93 - ~stdout:w 94 - ~stderr:we 95 - ?env 96 - ?executable:None 97 - (prog :: args) 98 - in 99 - Eio.Flow.close w; 100 - Eio.Flow.close we; 101 - let stdout = Eio.Buf_read.parse_exn Eio.Buf_read.take_all r ~max_size:Int.max_value in 102 - stdout_r := stdout; 103 - let stderr = 104 - Eio.Buf_read.parse_exn Eio.Buf_read.take_all re ~max_size:Int.max_value 105 - in 106 - stderr_r := stderr; 107 - Eio.Flow.close r; 108 - let exit_status = Eio.Process.await child in 109 - exit_status_r := (exit_status :> [ Exit_status.t | `Unknown ]); 110 - match f { Output.stdout; stderr; exit_status } with 111 - | Ok _ as ok -> ok 112 - | Error err -> raise (User_error err) 113 - with 114 - | (Eio.Exn.Io _ | User_error _) as exn -> 115 - let error = 116 - match exn with 117 - | Eio.Exn.Io _ -> Error.of_exn exn 118 - | User_error error -> error 119 - | _ -> assert false 120 - in 121 - Or_error.error_s 122 - [%sexp 123 - { prog : string 124 - ; args : string list 125 - ; exit_status = (!exit_status_r : [ Exit_status.t | `Unknown ]) 126 - ; cwd = (snd cwd : string) 127 - ; stdout = (Lines_or_sexp.create !stdout_r : Lines_or_sexp.t) 128 - ; stderr = (Lines_or_sexp.create !stderr_r : Lines_or_sexp.t) 129 - ; error : Error.t 130 - }] 131 - ;; 132 - 133 - let run_stdout ~process_mgr ~cwd ?stdin ?accept_nonzero_exit ?env ~prog ~args () = 134 - run ~process_mgr ~cwd ?stdin ?env ~prog ~args () ~f:(fun output -> 135 - Output.exit_and_stdout output ?accept_nonzero_exit) 136 - ;; 137 - 138 - let run_lines ~process_mgr ~cwd ?stdin ?accept_nonzero_exit ?env ~prog ~args () = 139 - run ~process_mgr ~cwd ?stdin ?env ~prog ~args () ~f:(fun output -> 140 - Output.exit_and_stdout output ?accept_nonzero_exit >>| String.split_lines) 141 - ;; 142 - 143 - let run_expect_no_output ~process_mgr ~cwd ?stdin ?accept_nonzero_exit ?env ~prog ~args () 144 - = 145 - run ~process_mgr ~cwd ?stdin ?env ~prog ~args () ~f:(fun output -> 146 - Output.expect_no_output output ?accept_nonzero_exit) 147 - ;;
-68
eio-process/src/eio_process.mli
··· 1 - (** Spawn external processes in [Eio] with convenient wrappers inspired by 2 - [Async_unix.Process]. *) 3 - 4 - module Exit_status : sig 5 - type t = 6 - [ `Exited of int 7 - | `Signaled of int 8 - ] 9 - [@@deriving sexp_of] 10 - end 11 - 12 - module Output : sig 13 - type t = 14 - { stdout : string 15 - ; stderr : string 16 - ; exit_status : Exit_status.t 17 - } 18 - [@@deriving sexp_of] 19 - 20 - val exit : ?accept_nonzero_exit:int list -> t -> unit Or_error.t 21 - val exit_and_stdout : ?accept_nonzero_exit:int list -> t -> string Or_error.t 22 - val exited : t -> accept_exit_codes:(int * 'a) list -> 'a Or_error.t 23 - val expect_no_output : ?accept_nonzero_exit:int list -> t -> unit Or_error.t 24 - end 25 - 26 - val run 27 - : process_mgr:_ Eio.Process.mgr 28 - -> cwd:Eio.Fs.dir_ty Eio.Path.t 29 - -> ?stdin:_ Eio.Flow.source 30 - -> ?env:string array 31 - -> prog:string 32 - -> args:string list 33 - -> unit 34 - -> f:(Output.t -> 'a Or_error.t) 35 - -> 'a Or_error.t 36 - 37 - val run_stdout 38 - : process_mgr:_ Eio.Process.mgr 39 - -> cwd:Eio.Fs.dir_ty Eio.Path.t 40 - -> ?stdin:_ Eio.Flow.source 41 - -> ?accept_nonzero_exit:int list 42 - -> ?env:string array 43 - -> prog:string 44 - -> args:string list 45 - -> unit 46 - -> string Or_error.t 47 - 48 - val run_lines 49 - : process_mgr:_ Eio.Process.mgr 50 - -> cwd:Eio.Fs.dir_ty Eio.Path.t 51 - -> ?stdin:_ Eio.Flow.source 52 - -> ?accept_nonzero_exit:int list 53 - -> ?env:string array 54 - -> prog:string 55 - -> args:string list 56 - -> unit 57 - -> string list Or_error.t 58 - 59 - val run_expect_no_output 60 - : process_mgr:_ Eio.Process.mgr 61 - -> cwd:Eio.Fs.dir_ty Eio.Path.t 62 - -> ?stdin:_ Eio.Flow.source 63 - -> ?accept_nonzero_exit:int list 64 - -> ?env:string array 65 - -> prog:string 66 - -> args:string list 67 - -> unit 68 - -> unit Or_error.t
-8
eio-process/test/bin/dune
··· 1 - (executable 2 - (name main) 3 - (flags :standard -w +a-4-40-41-42-44-45-48-66 -warn-error +a -open Base) 4 - (libraries base unix) 5 - (instrumentation 6 - (backend bisect_ppx)) 7 - (preprocess 8 - (pps ppx_sexp_value ppx_js_style -check-doc-comments)))
-42
eio-process/test/bin/main.ml
··· 1 - let exit_code = ref 0 2 - let stdout = ref false 3 - let stderr = ref false 4 - let output_sexp = ref false 5 - let signal = ref false 6 - 7 - let spec_list = 8 - [ "--exit-code", Stdlib.Arg.Set_int exit_code, " Exit with given code" 9 - ; "--stdout", Stdlib.Arg.Set stdout, " Write to stdout" 10 - ; "--stderr", Stdlib.Arg.Set stderr, " Write to stderr" 11 - ; "--output-sexp", Stdlib.Arg.Set output_sexp, " Make output format a s-expression" 12 - ; "--signal", Stdlib.Arg.Set signal, " Send a kill signal to itself" 13 - ] 14 - ;; 15 - 16 - let lorem_ipsum = 17 - lazy 18 - ({| 19 - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas quis nisi id 20 - lorem scelerisque bibendum eget id felis. Pellentesque consectetur tincidunt 21 - ornare. 22 - |} 23 - |> String.strip) 24 - ;; 25 - 26 - let sexp = lazy [%sexp { words = [ "Lorem"; "ipsum"; "dolor"; "sit"; "amet" ] }] 27 - 28 - let () = 29 - Stdlib.Arg.parse spec_list ignore "test"; 30 - if !stdout 31 - then 32 - if !output_sexp 33 - then Stdlib.print_endline (force sexp |> Sexp.to_string_hum) 34 - else Stdlib.print_endline (force lorem_ipsum); 35 - if !stderr 36 - then 37 - if !output_sexp 38 - then Stdlib.prerr_endline "()" 39 - else Stdlib.prerr_endline (force lorem_ipsum); 40 - if !signal then Unix.kill (Unix.getpid ()) Stdlib.Sys.sigkill [@coverage off]; 41 - Stdlib.exit !exit_code [@coverage off] 42 - ;;
-31
eio-process/test/dune
··· 1 - (library 2 - (name eio_process_test) 3 - (flags 4 - :standard 5 - -w 6 - +a-4-40-41-42-44-45-48-66 7 - -warn-error 8 - +a 9 - -open 10 - Base 11 - -open 12 - Expect_test_helpers 13 - -open 14 - Or_error.Let_syntax) 15 - (libraries base eio_main eio_process expect-test-helpers) 16 - (inline_tests 17 - (deps ./bin/main.exe)) 18 - (instrumentation 19 - (backend bisect_ppx)) 20 - (lint 21 - (pps ppx_js_style -check-doc-comments)) 22 - (preprocess 23 - (pps 24 - ppx_compare 25 - ppx_enumerate 26 - ppx_expect 27 - ppx_hash 28 - ppx_here 29 - ppx_let 30 - ppx_sexp_conv 31 - ppx_sexp_value)))
-213
eio-process/test/test__eio_process.ml
··· 1 - let%expect_test "run" = 2 - (* Returning the [Output.t]. *) 3 - Eio_main.run 4 - @@ fun env -> 5 - let result = 6 - Eio_process.run 7 - ~process_mgr:(Eio.Stdenv.process_mgr env) 8 - ~cwd:(Eio.Stdenv.cwd env) 9 - ~stdin:(Eio.Flow.string_source "Hello World!") 10 - ~prog:"cat" 11 - ~args:[] 12 - () 13 - ~f:Or_error.return 14 - in 15 - print_s [%sexp (result : Eio_process.Output.t Or_error.t)]; 16 - [%expect {| (Ok ((stdout ("Hello World!")) (stderr "") (exit_status (Exited 0)))) |}]; 17 - (* Executable not found. *) 18 - let result = 19 - Eio_process.run 20 - ~process_mgr:(Eio.Stdenv.process_mgr env) 21 - ~cwd:(Eio.Stdenv.cwd env) 22 - ~stdin:(Eio.Flow.string_source "Hello World!") 23 - ~prog:"invalid-program-not-found-314" 24 - ~args:[ "foo"; "bar" ] 25 - () 26 - ~f:Or_error.return 27 - in 28 - print_s [%sexp (result : Eio_process.Output.t Or_error.t)]; 29 - [%expect 30 - {| 31 - (Error ( 32 - (prog invalid-program-not-found-314) 33 - (args (foo bar)) 34 - (exit_status Unknown) 35 - (cwd "") 36 - (stdout "") 37 - (stderr "") 38 - (error ( 39 - "Eio.Io Process Executable \"invalid-program-not-found-314\" not found")))) |}]; 40 - (* Accept nonzero exit code. *) 41 - let result = 42 - Eio_process.run_stdout 43 - ~process_mgr:(Eio.Stdenv.process_mgr env) 44 - ~cwd:(Eio.Stdenv.cwd env) 45 - ~prog:"./bin/main.exe" 46 - ~args:[] 47 - () 48 - in 49 - print_s [%sexp (result : string Or_error.t)]; 50 - [%expect {| (Ok "") |}]; 51 - let result = 52 - Eio_process.run_stdout 53 - ~process_mgr:(Eio.Stdenv.process_mgr env) 54 - ~cwd:(Eio.Stdenv.cwd env) 55 - ~prog:"./bin/main.exe" 56 - ~args:[ "--exit-code"; "128" ] 57 - () 58 - in 59 - print_s [%sexp (result : string Or_error.t)]; 60 - [%expect 61 - {| 62 - (Error ( 63 - (prog ./bin/main.exe) 64 - (args (--exit-code 128)) 65 - (exit_status (Exited 128)) 66 - (cwd "") 67 - (stdout "") 68 - (stderr "") 69 - (error ("unexpected exit status" ((accept_exit_codes (0))))))) |}]; 70 - let result = 71 - Eio_process.run_stdout 72 - ~process_mgr:(Eio.Stdenv.process_mgr env) 73 - ~cwd:(Eio.Stdenv.cwd env) 74 - ~accept_nonzero_exit:[ 128 ] 75 - ~prog:"./bin/main.exe" 76 - ~args:[ "--exit-code"; "128" ] 77 - () 78 - in 79 - print_s [%sexp (result : string Or_error.t)]; 80 - [%expect {| (Ok "") |}]; 81 - (* Signal. *) 82 - let result = 83 - Eio_process.run_stdout 84 - ~process_mgr:(Eio.Stdenv.process_mgr env) 85 - ~cwd:(Eio.Stdenv.cwd env) 86 - ~prog:"./bin/main.exe" 87 - ~args:[ "--signal" ] 88 - () 89 - in 90 - print_s [%sexp (result : string Or_error.t)]; 91 - [%expect 92 - {| 93 - (Error ( 94 - (prog ./bin/main.exe) 95 - (args (--signal)) 96 - (exit_status (Signaled -7)) 97 - (cwd "") 98 - (stdout "") 99 - (stderr "") 100 - (error ("unexpected exit status" ((accept_exit_codes (0))))))) |}]; 101 - (* Run lines. *) 102 - let result = 103 - Eio_process.run_lines 104 - ~process_mgr:(Eio.Stdenv.process_mgr env) 105 - ~cwd:(Eio.Stdenv.cwd env) 106 - ~prog:"./bin/main.exe" 107 - ~args:[] 108 - () 109 - in 110 - print_s [%sexp (result : string list Or_error.t)]; 111 - [%expect {| (Ok ()) |}]; 112 - let result = 113 - Eio_process.run_lines 114 - ~process_mgr:(Eio.Stdenv.process_mgr env) 115 - ~cwd:(Eio.Stdenv.cwd env) 116 - ~prog:"./bin/main.exe" 117 - ~args:[ "--stdout" ] 118 - () 119 - in 120 - print_s [%sexp (result : string list Or_error.t)]; 121 - [%expect 122 - {| 123 - (Ok ( 124 - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas quis nisi id" 125 - "lorem scelerisque bibendum eget id felis. Pellentesque consectetur tincidunt" 126 - ornare.)) |}]; 127 - (* Run expect no output. *) 128 - let result = 129 - Eio_process.run_expect_no_output 130 - ~process_mgr:(Eio.Stdenv.process_mgr env) 131 - ~cwd:(Eio.Stdenv.cwd env) 132 - ~prog:"./bin/main.exe" 133 - ~args:[ "--stderr" ] 134 - () 135 - in 136 - print_s [%sexp (result : unit Or_error.t)]; 137 - [%expect {| (Ok ()) |}]; 138 - let result = 139 - Eio_process.run_expect_no_output 140 - ~process_mgr:(Eio.Stdenv.process_mgr env) 141 - ~cwd:(Eio.Stdenv.cwd env) 142 - ~prog:"./bin/main.exe" 143 - ~args:[ "--stdout" ] 144 - () 145 - in 146 - print_s [%sexp (result : unit Or_error.t)]; 147 - [%expect 148 - {| 149 - (Error ( 150 - (prog ./bin/main.exe) 151 - (args (--stdout)) 152 - (exit_status (Exited 0)) 153 - (cwd "") 154 - (stdout ( 155 - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas quis nisi id" 156 - "lorem scelerisque bibendum eget id felis. Pellentesque consectetur tincidunt" 157 - ornare.)) 158 - (stderr "") 159 - (error "expected no output"))) |}]; 160 - (* User further processing the output. *) 161 - let result = 162 - Eio_process.run 163 - ~process_mgr:(Eio.Stdenv.process_mgr env) 164 - ~cwd:(Eio.Stdenv.cwd env) 165 - ~prog:"./bin/main.exe" 166 - ~args:[ "--stdout"; "--stderr"; "--output-sexp"; "--exit-code"; "128" ] 167 - () 168 - ~f:(fun output -> 169 - match%map 170 - Eio_process.Output.exited output ~accept_exit_codes:[ 0, `Zero; 1, `One ] 171 - with 172 - | `Zero | `One -> assert false) 173 - in 174 - (* When the user function [f] returned an Error, we include all the info in 175 - the error message. *) 176 - print_s [%sexp (result : int Or_error.t)]; 177 - [%expect 178 - {| 179 - (Error ( 180 - (prog ./bin/main.exe) 181 - (args (--stdout --stderr --output-sexp --exit-code 128)) 182 - (exit_status (Exited 128)) 183 - (cwd "") 184 - (stdout ((words (Lorem ipsum dolor sit amet)))) 185 - (stderr ()) 186 - (error ("unexpected exit status" ((accept_exit_codes (0 1))))))) |}]; 187 - let result = 188 - Eio_process.run 189 - ~process_mgr:(Eio.Stdenv.process_mgr env) 190 - ~cwd:(Eio.Stdenv.cwd env) 191 - ~prog:"./bin/main.exe" 192 - ~args:[ "--stdout"; "--output-sexp"; "--exit-code"; "1" ] 193 - () 194 - ~f:(fun output -> 195 - match%map 196 - Eio_process.Output.exited output ~accept_exit_codes:[ 0, `Zero; 1, `One ] 197 - with 198 - | `One -> 1 199 - | `Zero -> assert false) 200 - in 201 - print_s [%sexp (result : int Or_error.t)]; 202 - [%expect {| (Ok 1) |}]; 203 - () 204 - ;; 205 - 206 - let%expect_test "lines" = 207 - (* We monitor this as these results influence the implementation [Lines_or_sexp]. *) 208 - print_s [%sexp (String.split ~on:'\n' "" : string list)]; 209 - [%expect {| ("") |}]; 210 - print_s [%sexp (String.split_lines "" : string list)]; 211 - [%expect {| () |}]; 212 - () 213 - ;;
-1
eio-process/test/test__eio_process.mli
··· 1 - (*_ This signature is deliberately empty. *)
+189
flake.lock
··· 1 + { 2 + "nodes": { 3 + "flake-compat": { 4 + "flake": false, 5 + "locked": { 6 + "lastModified": 1627913399, 7 + "narHash": "sha256-hY8g6H2KFL8ownSiFeMOjwPC8P0ueXpCVEbxgda3pko=", 8 + "owner": "edolstra", 9 + "repo": "flake-compat", 10 + "rev": "12c64ca55c1014cdc1b16ed5a804aa8576601ff2", 11 + "type": "github" 12 + }, 13 + "original": { 14 + "owner": "edolstra", 15 + "repo": "flake-compat", 16 + "type": "github" 17 + } 18 + }, 19 + "flake-utils": { 20 + "inputs": { 21 + "systems": "systems" 22 + }, 23 + "locked": { 24 + "lastModified": 1710146030, 25 + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", 26 + "owner": "numtide", 27 + "repo": "flake-utils", 28 + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", 29 + "type": "github" 30 + }, 31 + "original": { 32 + "owner": "numtide", 33 + "repo": "flake-utils", 34 + "type": "github" 35 + } 36 + }, 37 + "flake-utils_2": { 38 + "locked": { 39 + "lastModified": 1638122382, 40 + "narHash": "sha256-sQzZzAbvKEqN9s0bzWuYmRaA03v40gaJ4+iL1LXjaeI=", 41 + "owner": "numtide", 42 + "repo": "flake-utils", 43 + "rev": "74f7e4319258e287b0f9cb95426c9853b282730b", 44 + "type": "github" 45 + }, 46 + "original": { 47 + "owner": "numtide", 48 + "repo": "flake-utils", 49 + "type": "github" 50 + } 51 + }, 52 + "mirage-opam-overlays": { 53 + "flake": false, 54 + "locked": { 55 + "lastModified": 1661959605, 56 + "narHash": "sha256-CPTuhYML3F4J58flfp3ZbMNhkRkVFKmBEYBZY5tnQwA=", 57 + "owner": "dune-universe", 58 + "repo": "mirage-opam-overlays", 59 + "rev": "05f1c1823d891ce4d8adab91f5db3ac51d86dc0b", 60 + "type": "github" 61 + }, 62 + "original": { 63 + "owner": "dune-universe", 64 + "repo": "mirage-opam-overlays", 65 + "type": "github" 66 + } 67 + }, 68 + "nixpkgs": { 69 + "locked": { 70 + "lastModified": 1682362401, 71 + "narHash": "sha256-/UMUHtF2CyYNl4b60Z2y4wwTTdIWGKhj9H301EDcT9M=", 72 + "owner": "nixos", 73 + "repo": "nixpkgs", 74 + "rev": "884ac294018409e0d1adc0cae185439a44bd6b0b", 75 + "type": "github" 76 + }, 77 + "original": { 78 + "owner": "nixos", 79 + "ref": "nixos-unstable", 80 + "repo": "nixpkgs", 81 + "type": "github" 82 + } 83 + }, 84 + "opam-nix": { 85 + "inputs": { 86 + "flake-compat": "flake-compat", 87 + "flake-utils": "flake-utils_2", 88 + "mirage-opam-overlays": "mirage-opam-overlays", 89 + "nixpkgs": "nixpkgs", 90 + "opam-overlays": "opam-overlays", 91 + "opam-repository": "opam-repository", 92 + "opam2json": "opam2json" 93 + }, 94 + "locked": { 95 + "lastModified": 1715087815, 96 + "narHash": "sha256-FjIg+rO+aIfVFzSbvNznVhCn/s2MS8HkPhht7LqzLlk=", 97 + "owner": "tweag", 98 + "repo": "opam-nix", 99 + "rev": "d42a3b8f234dd8c922020f8b2f4e326406bf23d1", 100 + "type": "github" 101 + }, 102 + "original": { 103 + "owner": "tweag", 104 + "repo": "opam-nix", 105 + "type": "github" 106 + } 107 + }, 108 + "opam-overlays": { 109 + "flake": false, 110 + "locked": { 111 + "lastModified": 1654162756, 112 + "narHash": "sha256-RV68fUK+O3zTx61iiHIoS0LvIk0E4voMp+0SwRg6G6c=", 113 + "owner": "dune-universe", 114 + "repo": "opam-overlays", 115 + "rev": "c8f6ef0fc5272f254df4a971a47de7848cc1c8a4", 116 + "type": "github" 117 + }, 118 + "original": { 119 + "owner": "dune-universe", 120 + "repo": "opam-overlays", 121 + "type": "github" 122 + } 123 + }, 124 + "opam-repository": { 125 + "flake": false, 126 + "locked": { 127 + "lastModified": 1705008664, 128 + "narHash": "sha256-TTjTal49QK2U0yVOmw6rJhTGYM7tnj3Kv9DiEEiLt7E=", 129 + "owner": "ocaml", 130 + "repo": "opam-repository", 131 + "rev": "fa77046c6497f8ca32926acdb7eb1e61777d4c17", 132 + "type": "github" 133 + }, 134 + "original": { 135 + "owner": "ocaml", 136 + "repo": "opam-repository", 137 + "type": "github" 138 + } 139 + }, 140 + "opam2json": { 141 + "inputs": { 142 + "nixpkgs": [ 143 + "opam-nix", 144 + "nixpkgs" 145 + ] 146 + }, 147 + "locked": { 148 + "lastModified": 1671540003, 149 + "narHash": "sha256-5pXfbUfpVABtKbii6aaI2EdAZTjHJ2QntEf0QD2O5AM=", 150 + "owner": "tweag", 151 + "repo": "opam2json", 152 + "rev": "819d291ea95e271b0e6027679de6abb4d4f7f680", 153 + "type": "github" 154 + }, 155 + "original": { 156 + "owner": "tweag", 157 + "repo": "opam2json", 158 + "type": "github" 159 + } 160 + }, 161 + "root": { 162 + "inputs": { 163 + "flake-utils": "flake-utils", 164 + "nixpkgs": [ 165 + "opam-nix", 166 + "nixpkgs" 167 + ], 168 + "opam-nix": "opam-nix" 169 + } 170 + }, 171 + "systems": { 172 + "locked": { 173 + "lastModified": 1681028828, 174 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 175 + "owner": "nix-systems", 176 + "repo": "default", 177 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 178 + "type": "github" 179 + }, 180 + "original": { 181 + "owner": "nix-systems", 182 + "repo": "default", 183 + "type": "github" 184 + } 185 + } 186 + }, 187 + "root": "root", 188 + "version": 7 189 + }
+47 -31
flake.nix
··· 1 1 { 2 - description = "Example JavaScript development environment for Zero to Nix"; 3 - 4 - # Flake inputs 5 2 inputs = { 6 - nixpkgs.url = "github:nixos/nixpkgs"; # also valid: "nixpkgs" 3 + opam-nix.url = "github:tweag/opam-nix"; 4 + flake-utils.url = "github:numtide/flake-utils"; 5 + nixpkgs.follows = "opam-nix/nixpkgs"; 7 6 }; 7 + outputs = { self, flake-utils, opam-nix, nixpkgs }@inputs: 8 + flake-utils.lib.eachDefaultSystem (system: 9 + let 10 + pkgs = nixpkgs.legacyPackages.${system}; 11 + on = opam-nix.lib.${system}; 12 + localPackagesQuery = builtins.mapAttrs (_: pkgs.lib.last) 13 + (on.listRepo (on.makeOpamRepo ./.)); 14 + devPackagesQuery = { 15 + # You can add "development" packages here. They will get added to the devShell automatically. 16 + ocaml-lsp-server = "*"; 17 + ocamlformat = "*"; 18 + }; 19 + query = devPackagesQuery // { 20 + ## You can force versions of certain packages here, e.g: 21 + ## - force the ocaml compiler to be taken from opam-repository: 22 + ocaml-base-compiler = "5.1.1"; 23 + ## - or force the compiler to be taken from nixpkgs and be a certain version: 24 + # ocaml-system = "5.1.1"; 25 + ## - or force ocamlfind to be a certain version: 26 + # ocamlfind = "1.9.2"; 27 + }; 28 + scope = on.buildOpamProject' { } ./. query; 29 + overlay = final: prev: 30 + { 31 + # You can add overrides here 32 + }; 33 + scope' = scope.overrideScope' overlay; 34 + # Packages from devPackagesQuery 35 + devPackages = builtins.attrValues 36 + (pkgs.lib.getAttrs (builtins.attrNames devPackagesQuery) scope'); 37 + # Packages in this workspace 38 + packages = 39 + pkgs.lib.getAttrs (builtins.attrNames localPackagesQuery) scope'; 40 + in { 41 + legacyPackages = scope'; 8 42 9 - # Flake outputs 10 - outputs = { self, nixpkgs }: 11 - let 12 - # Systems supported 13 - allSystems = [ 14 - "x86_64-linux" # 64-bit Intel/AMD Linux 15 - ]; 43 + # inherit packages; 16 44 17 - # Helper to provide system-specific attributes 18 - forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f { 19 - pkgs = import nixpkgs { inherit system; }; 20 - }); 21 - in 22 - { 23 - # Development environment output 24 - devShells = forAllSystems ({ pkgs }: { 25 - default = pkgs.mkShell { 26 - # The Nix packages provided in the environment 27 - packages = with pkgs; [ 28 - jj 29 - 45 + ## If you want to have a "default" package which will be built with just `nix build`, do this instead of `inherit packages;`: 46 + packages = packages // { default = packages.jj_tui; }; 47 + 48 + devShells.default = pkgs.mkShell { 49 + inputsFrom = builtins.attrValues packages; 50 + buildInputs = devPackages ++ [ 51 + # You can add packages from nixpkgs here 30 52 ]; 31 - shellHook = '' 32 - eval $(opam env) 33 - ''; 34 53 }; 35 - 36 - }); 37 - 38 - }; 54 + }); 39 55 }
+1 -1
jj_tui/bin/dune
··· 2 2 (public_name jj_tui) 3 3 (name main) 4 4 (flags (:standard -cclib -static -cclib -no-pie )) 5 - (libraries jj_tui feather lwd nottui base core stdio core_unix.command_unix eio_main eio-process ) 5 + (libraries jj_tui lwd nottui base stdio eio_main eio-process ) 6 6 )
-3
jj_tui/dune
··· 1 - (library 2 - (name widgets) 3 - (libraries nottui))
+1 -1
jj_tui/dune-project dune-project
··· 19 19 (name jj_tui) 20 20 (synopsis "A short synopsis") 21 21 (description "A longer description") 22 - (depends ocaml dune) 22 + (depends ocaml dune eio-project notty stdio nottui lwd base eio_main angstrom ppx_expect) 23 23 (tags 24 24 (topics "to describe" your project))) 25 25
-26
jj_tui/flake.lock
··· 1 - { 2 - "nodes": { 3 - "nixpkgs": { 4 - "locked": { 5 - "lastModified": 1715346633, 6 - "narHash": "sha256-A9vSieOHR7B41QoWZcb7fEY7r29E4Vq3liXE0h0edf0=", 7 - "owner": "nixos", 8 - "repo": "nixpkgs", 9 - "rev": "d42c1c8d447a388e1f2776d22c77f5642d703da6", 10 - "type": "github" 11 - }, 12 - "original": { 13 - "owner": "nixos", 14 - "repo": "nixpkgs", 15 - "type": "github" 16 - } 17 - }, 18 - "root": { 19 - "inputs": { 20 - "nixpkgs": "nixpkgs" 21 - } 22 - } 23 - }, 24 - "root": "root", 25 - "version": 7 26 - }
-53
jj_tui/flake.nix
··· 1 - { 2 - description = "Example JavaScript development environment for Zero to Nix"; 3 - 4 - # Flake inputs 5 - inputs = { 6 - 7 - # nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 8 - nixpkgs.url = "github:nixos/nixpkgs"; # also valid: "nixpkgs" 9 - 10 - # roc={ 11 - # url="github:roc-lang/roc"; 12 - # inputs.nixpkgs.follows="nixpkgs"; 13 - 14 - # }; 15 - 16 - }; 17 - # Flake outputs 18 - outputs = { self, nixpkgs, ... }@inputs: 19 - let 20 - # Systems supported 21 - allSystems = [ 22 - "x86_64-linux" # 64-bit Intel/AMD Linux 23 - ]; 24 - 25 - # Helper to provide system-specific attributes 26 - forAllSystems = f: 27 - nixpkgs.lib.genAttrs allSystems (system: 28 - f { 29 - pkgs = import nixpkgs { inherit system; }; 30 - 31 - }); 32 - in { 33 - # Development environment output 34 - devShells = forAllSystems ({ pkgs }: { 35 - default = 36 - 37 - pkgs.mkShell { 38 - packages = with pkgs; [ pkgs.pkgsStatic.pkg-config pkgs.pkgsStatic.gmp pkgsStatic.stdenv.cc.cc.lib jujutsu musl pkgs.pkgsStatic.dune ]; 39 - shellHook = let 40 - libPath = 41 - pkgs.lib.makeLibraryPath [ pkgs.pkgsStatic.stdenv.cc.cc.lib pkgs.pkgsStatic.gmp pkgs.pkgsStatic.musl ]; 42 - in '' 43 - export CC=${pkgs.pkgsStatic.musl.stdenv.cc} 44 - # yolo 45 - export CFLAGS="$CFLAGS -I${pkgs.pkgsStatic.stdenv.cc.cc.lib}/include -I${pkgs.pkgsStatic.gmp}/include" 46 - export LIBS="$LIBS -L${pkgs.pkgsStatic.stdenv.cc.cc.lib}/lib -L${pkgs.pkgsStatic.gmp}/lib" 47 - ''; 48 - }; 49 - 50 - }); 51 - 52 - }; 53 - }
-8
jj_tui/jj_tui.install
··· 1 - lib: [ 2 - "_build/install/default/lib/jj_tui/META" 3 - "_build/install/default/lib/jj_tui/dune-package" 4 - "_build/install/default/lib/jj_tui/opam" 5 - ] 6 - bin: [ 7 - "_build/install/default/bin/jj_tui" 8 - ]
+9
jj_tui/jj_tui.opam jj_tui.opam
··· 12 12 depends: [ 13 13 "ocaml" 14 14 "dune" {>= "3.9"} 15 + "eio-project" 16 + "notty" 17 + "stdio" 18 + "nottui" 19 + "lwd" 20 + "base" 21 + "eio_main" 22 + "angstrom" 23 + "ppx_expect" 15 24 "odoc" {with-doc} 16 25 ] 17 26 build: [
+1 -1
jj_tui/lib/dune
··· 1 1 (library 2 2 (name jj_tui) 3 3 (inline_tests) 4 - (libraries core stdio core_unix.command_unix notty nottui angstrom) 4 + (libraries stdio notty nottui angstrom) 5 5 (preprocess 6 6 (pps ppx_expect)))
-14
notty/.gitignore
··· 1 - _build 2 - *.install 3 - *.native 4 - *.byte 5 - .merlin 6 - 7 - tmp 8 - *~ 9 - \.\#* 10 - \#*# 11 - 12 - *.json 13 - gmon.out 14 - rondom
-14
notty/.ocamlinit
··· 1 - #require "uucp,uuseg,uutf" 2 - 3 - #directory "_build/default/src" 4 - #directory "_build/default/src/.notty.objs/byte" 5 - #directory "_build/default/src-unix" 6 - #directory "_build/default/src-unix/.notty_unix.objs/byte" 7 - #directory "_build/default/src-lwt" 8 - #directory "_build/default/src-lwt/.notty_lwt.objs/byte" 9 - 10 - #load "notty.cma" 11 - #load "notty_unix.cma" 12 - #load "notty_lwt.cma" 13 - 14 - #use "src/notty_top_init.ml"
-50
notty/CHANGES.md
··· 1 - ## v0.2.3 (2022-09-02) 2 - 3 - * Moved to Dune. 4 - * Renders faster, uses less memory. 5 - * Nested uses of `I.pp_attr` within `I.strf` now stack, instead of replacing. 6 - * Removed dependency on Uucp. Uses internal data instead (Unicode 13). 7 - * Support OCaml 4.08 - 4.14. Thanks to @kit-ty-kate for the 4.14 fixes. 8 - 9 - ## v0.2.2 (2019-02-19) 10 - 11 - * Fix a long-standing terminal cleanup bug. Reported by @ttamttam, fix by @cfcs. 12 - 13 - ## v0.2.1 (2017-11-06) 14 - 15 - * OCaml 4.06 compatible. 16 - * Cache the internal representation of Unicode strings. 17 - * Remove `I.ichar`. **breaking** 18 - 19 - ## v0.2.0 (2017-10-31) 20 - 21 - * All-around speed and memory improvements. 22 - * Draw over lines cell-by-cell instead of using erase-and-skip. 23 - Slower, but flicker-free drawing. 24 - * `Term.create`: optionally inhibit synthetic TTY signals. 25 - * Cursor origin moved from `(1, 1)` to `(0, 0)`. **breaking** 26 - * `#key` renamed to `#special`. **breaking** 27 - * Added `Term.fds` to get connected file descriptors. 28 - * Added `A.equal` and `I.equal`. 29 - * Switched over to `Uchar.t`. **breaking** 30 - * Separated ASCII from the rest of Unicode input. **breaking** 31 - * Added image pretty-printer `I.pp`. 32 - * Added `notty.top` for use in the toplevel. 33 - * Removed `I.tile`. **breaking** 34 - * Added `I.tabulate`, generalizing `I.tile`. 35 - * Added support for 24-bit color. 36 - * Added `Notty_*.show_cursor` and `Notty_*.move_cursor` for manual cursor 37 - positioning in inline mode. 38 - * Removed `output_image_endline`. Can be replaced by `eol`. **breaking** 39 - * `Notty_*.output_image` lost the `~clear` parameter. Can be replaced in various 40 - ways by cursor positioning. 41 - * `Notty_unix.output_image ~chan` renamed to `~fd`. **breaking** 42 - * Added support for bracketed paste. 43 - * More example programs. 44 - 45 - ## v0.1.1 (2016-02-09) 46 - * `Term.input` -> `Term.event` 47 - * Option to redraw the line 48 - 49 - ## v0.1.0 (2016-02-09) 50 - * Initial release
-13
notty/LICENSE.md
··· 1 - Copyright (c) 2016-2017 David Kaloper Meršinjak 2 - 3 - Permission to use, copy, modify, and/or distribute this software for any 4 - purpose with or without fee is hereby granted, provided that the above 5 - copyright notice and this permission notice appear in all copies. 6 - 7 - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-70
notty/README.md
··· 1 - # Notty — Declaring terminals 2 - 3 - <a href="https://asciinema.org/a/ZIXzn2ZmIxK39qoT3eJla5OyO" alt="dumper"><img src="https://asciinema.org/a/ZIXzn2ZmIxK39qoT3eJla5OyO.png" width="400"/></a> 4 - <a href="https://asciinema.org/a/TsIhDJv5S00AB2biVmhHRzZ8I" alt="input"><img src="https://asciinema.org/a/TsIhDJv5S00AB2biVmhHRzZ8I.png" width="400"/></a> 5 - <a href="https://asciinema.org/a/z1Pc0Mppg2JFzteZzdeigLwYc" alt="microdots"><img src="https://asciinema.org/a/z1Pc0Mppg2JFzteZzdeigLwYc.png" width="400"/></a> 6 - <a href="https://asciinema.org/a/NgpF9Im8qfUICC39GDDAe9Ede" alt="rain"><img src="https://asciinema.org/a/R94gnHTQhCFJAsWpRfVlZWcUB.png" width="400"/></a> 7 - 8 - Notty is a declarative terminal library for OCaml structured around a notion 9 - of composable images. It tries to abstract away the basic terminal programming 10 - model, providing something simpler and more expressive. 11 - 12 - The core layout engine and IO codecs are pure platform-independent OCaml. 13 - Distribution includes modules with input and output facilities for Unix, and Lwt 14 - on Unix. 15 - 16 - As an attempt to redefine terminal programming, Notty has to be 17 - _opinionated_. It assumes Unicode throughout, does not have universal support 18 - for various terminals out there, and has a peculiar programming and rendering 19 - model. 20 - 21 - Notty's core API was heavily influenced by Haskell's [Vty][vty]. 22 - 23 - ## Where to start 24 - 25 - Check out the [documentation], [examples], or peek directly into the [interface] 26 - file. 27 - 28 - Building with `dune build @ex` will produce several little example programs that 29 - also double as tests. 30 - 31 - ```OCaml 32 - (* Game of Life with ZX Spectrum kitsch. *) 33 - 34 - let dot : image = I.uchar A.(fg lightred) (Uchar.of_int 0x25cf) 1 1 35 - 36 - let background step (n, m) = 37 - let k = 24. *. sin (float (step + m + n) /. 10.) |> truncate in 38 - if k > 0 then I.char A.(fg (gray k)) '.' 1 1 else I.void 1 1 39 - 40 - let render (w, h) step life : image = 41 - I.tabulate w (h - 1) @@ fun x y -> 42 - let pt = (x, y) in 43 - if CSet.mem pt life then dot else background step pt 44 - ``` 45 - 46 - [documentation]: https://pqwy.github.io/notty/doc 47 - [examples]: http://pqwy.github.io/notty/doc/Notty.html#examples 48 - [interface]: https://github.com/pqwy/notty/blob/master/src/notty.mli 49 - [vty]: https://hackage.haskell.org/package/vty 50 - 51 - ## What? 52 - 53 - - _Notty?_ 54 - 55 - Terminals are tedious to program for. Notty tries to abstract the tedium away, 56 - leaving you with a more pleasant programming surface that's quite unlike a TTY. 57 - Hence, _No-TTY_. 58 - 59 - - A new kind of Rust terminal? 60 - 61 - This Notty has no connection to any other body of code named Notty. 62 - 63 - - Why make yet another terminal output library? 64 - 65 - Because: 66 - * It allows one to *describe* what should be seen, as opposed to *commanding* 67 - a terminal. 68 - * It's pretty compact. Both bells and whistles can be implemented separately. 69 - * Core is easy to glue onto various IO backends. 70 - * Pure platform-independent OCaml.
-3
notty/benchmarks/dune
··· 1 - (executable 2 - (name speed) 3 - (libraries notty notty.unix common unmark unmark.cli))
-113
notty/benchmarks/speed.ml
··· 1 - (* Copyright (c) 2016-2019 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - open Notty 5 - open Common 6 - open Common.Images 7 - 8 - 9 - let decode ?(n=1) str = 10 - let f cs _ = function `Uchar c -> c::cs | _ -> cs in 11 - let us = str |> Uutf.String.fold_utf_8 f [] |> List.rev in 12 - for _ = 1 to n do Unescape.decode us |> ignore done 13 - 14 - let input ?(n=1) str = 15 - let buf = Bytes.unsafe_of_string str in 16 - let rec go f n = match Unescape.next f with 17 - | #Unescape.event -> go f n 18 - | `Await when n > 0 -> 19 - Unescape.input f buf 0 (Bytes.length buf); go f (pred n) 20 - | `Await -> () 21 - | `End -> assert false in 22 - go (Unescape.create ()) n 23 - 24 - let escapes = 25 - "\027[5~\027[6~\027[1~\027[4~\027OP\027OQ\027OR\027OS\027[15~\027[17~" ^ 26 - "\027[18~\027[19~\027[20~\027[21~\027[23~\027[24~" 27 - 28 - let escapes_m = 29 - "\027[<0;59;7M\027[<32;58;7M\027[<32;57;7M\027[<32;56;7M\027[<32;54;7M" ^ 30 - "\027[<32;53;8M\027[<32;52;8M\027[<32;51;8M\027[<32;50;8M\027[<32;49;8M" ^ 31 - "\027[<32;47;9M\027[<32;46;9M\027[<32;44;9M\027[<32;42;10M\027[<32;41;10M" ^ 32 - "\027[<32;41;11M\027[<32;40;11M\027[<32;41;12M\027[<32;42;12M" ^ 33 - "\027[<32;42;13M\027[<32;43;13M\027[<32;44;13M\027[<0;44;13m" 34 - 35 - let chars = String.(make (length escapes) 'x') 36 - 37 - let buf = Buffer.create 1024 38 - let buf_render off dim image = 39 - Buffer.clear buf; Render.to_buffer buf Cap.ansi off dim image 40 - 41 - 42 - open Unmark 43 - 44 - let strf = Format.asprintf 45 - let group_of name xs f = group name (List.map f xs) 46 - let bench_fmt fmt = Format.kasprintf bench fmt 47 - let group_fmt fmt = Format.kasprintf group fmt 48 - 49 - let render = 50 - 51 - let clip i = I.(width i |> min 200, height i |> min 200) in 52 - let ops i = Operation.of_image (0, 0) (clip i) i 53 - and render i = buf_render (0, 0) (clip i) i in 54 - 55 - group "render" [ 56 - group "rasterize" [ 57 - bench "i2" (fun () -> ops i2) 58 - ; bench "i3" (fun () -> ops i3) 59 - ; bench "i4" (fun () -> ops i4) 60 - ; bench "i5" (fun () -> ops i5) 61 - ]; 62 - group "render" [ 63 - bench "i2" (fun () -> render i2) 64 - ; bench "i3" (fun () -> render i3) 65 - ; bench "i4" (fun () -> render i4) 66 - ; bench "i5" (fun () -> render i5) 67 - ]; 68 - group_f "draw" (fun t -> [ 69 - bench "i3" (fun () -> Term.image t i3) 70 - ; bench "i5" (fun () -> Term.image t i5) 71 - ]) ~init:Term.create ~fini:Term.release 72 - ] 73 - 74 - let input = group "input" [ 75 - group "decode" [ 76 - bench "escapes" (fun () -> decode ~n:100 escapes); 77 - bench "CSI escapes" (fun () -> decode ~n:100 escapes_m); 78 - bench "chars" (fun () -> decode ~n:100 chars); 79 - ]; 80 - group "input" [ 81 - bench "escapes" (fun () -> input ~n:100 escapes); 82 - bench "CSI escapes" (fun () -> input ~n:100 escapes_m); 83 - bench "chars" (fun () -> input ~n:100 chars); 84 - ] 85 - ] 86 - 87 - let construct = 88 - 89 - let strings = [ 90 - "s1", "a" 91 - ; "s2", "abcdefghij" 92 - ; "s3", String.repeat 10 "abcdefghij" 93 - ; "s4", String.repeat 100 "abcdefghij" 94 - ; "u1", "☭" 95 - ; "u2", String.repeat 10 "☭" 96 - ; "u3", String.repeat 100 "☭" 97 - ; "u4", String.repeat 1000 "☭" ] in 98 - 99 - group "construct" [ 100 - 101 - group "make" (strings |> List.map @@ fun (n, s) -> 102 - bench n (fun () -> I.string A.empty s)) 103 - 104 - ; group "repeat" ([0x40; 0x262d] |> List.map @@ fun x -> 105 - let u = Uchar.of_int x in 106 - group_fmt "U+%04x" x ([1; 10; 100] |> List.map @@ fun n -> 107 - bench_fmt "%dx" n (fun () -> I.uchar A.empty u n 1))) 108 - 109 - ; bench "pxmatrix" (fun () -> pxmatrix 200 200 @@ fun _ _ -> A.black) 110 - ] 111 - 112 - 113 - let _ = Unmark_cli.main "Notty" [ render; input; construct ]
-3
notty/dune-project
··· 1 - (lang dune 1.7) 2 - (name notty) 3 - (version %%VERSION_NUM%%)
-42
notty/examples/almondbread.ml
··· 1 - open Notty 2 - open Common 3 - 4 - let iter = 200 5 - 6 - let member x y = 7 - let rec go cx cy x y n = 8 - let xx = x *. x and yy = y *. y in 9 - if n = 0 || xx +. yy > 4. then n else 10 - go cx cy (xx -. yy +. cx) (2. *. x *. y +. cy) (n - 1) in 11 - float (iter - go x y 0. 0. iter) /. float iter 12 - 13 - let pi2 = 2. *. 3.14159 14 - let pi2_3 = pi2 /. 3. 15 - 16 - let mandelbrot x y = 17 - (* let esc = 1. -. member x y in *) 18 - (* 23. *. esc *. esc |> truncate |> A.gray *) 19 - match member x y with 20 - | 1. -> A.gray 0 21 - | esc -> 22 - let t = esc *. pi2 in 23 - let f d = (sin (t +. d) *. 128. +. 128.) |> truncate in 24 - A.rgb_888 ~b:(f (-.pi2_3)) ~g:(f 0.) ~r:(f pi2_3) 25 - 26 - let xlate dx dy f x y = f (x -. dx) (y -. dy) 27 - let scale k f = let k1 = 1./.k in fun x y -> f (x *. k1) (y *. k1) 28 - let rot a f = 29 - let sina = sin a and cosa = cos a in fun x y -> 30 - f (x *. cosa -. y *. sina) (x *. sina +. cosa *. y) 31 - 32 - let render_unit f (w, h) = 33 - let sw = 1. /. float w 34 - and sh = 1. /. float (2 * h) in 35 - pxmatrix w h (fun x y -> f (float x *. sw) (float y *. sh)) 36 - 37 - let () = 38 - let pix = 39 - render_unit @@ 40 - rot (-1.570795) @@ xlate (1.6) (-0.5) @@ 41 - mandelbrot in 42 - Notty_unix.(output_image_size @@ fun (w, h) -> pix (w, h - 1) |> eol)
-60
notty/examples/colors.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** 5 - * Demonstrates text attributes. 6 - *) 7 - open Notty 8 - open Common 9 - 10 - let colors = A.[ 11 - "black" , black 12 - ; "red" , red 13 - ; "green" , green 14 - ; "yellow" , yellow 15 - ; "blue" , blue 16 - ; "magenta" , magenta 17 - ; "cyan" , cyan 18 - ; "white" , white 19 - ; "lightblack" , lightblack 20 - ; "lightred" , lightred 21 - ; "lightgreen" , lightgreen 22 - ; "lightyellow" , lightyellow 23 - ; "lightblue" , lightblue 24 - ; "lightmagenta" , lightmagenta 25 - ; "lightcyan" , lightcyan 26 - ; "lightwhite" , lightwhite 27 - ] 28 - 29 - let styles = A.[ 30 - "empty" , empty 31 - ; "bold" , st bold 32 - ; "italic" , st italic 33 - ; "underline" , st underline 34 - ; "blink" , st blink 35 - ; "reverse" , st reverse 36 - ; "bold/italic", st bold ++ st italic 37 - ; "rev/underln", st underline ++ st reverse 38 - ; "bold/rev" , st reverse ++ st bold 39 - ] 40 - 41 - let image w = 42 - let open List in 43 - let core16 = 44 - let c1 = map (fun (n, c) -> I.string A.(fg c) n) colors 45 - and c2 = map (fun (n, c) -> I.string A.(fg black ++ bg c) n) colors 46 - in I.(vcat c1 <|> void 1 0 <|> vcat c2) 47 - and attr = 48 - I.( styles |> map (fun (n, s) -> hpad 0 1 (string s n)) |> hcat) in 49 - let combine imgs = 50 - List.map I.(fun (n, i) -> string A.empty n <-> i <-> void 0 1) imgs 51 - |> I.vcat |> I.pad ~l:1 ~t:1 in 52 - combine [ 53 - "System colors:", core16; 54 - "Color cube, 6x6x6:", Images.c_cube_ix; 55 - "Grayscale ramp:", Images.c_gray_ramp; 56 - "24bit:", Images.c_rainbow (w - 2) 1; 57 - "Text styles:", attr 58 - ] 59 - 60 - let () = Notty_unix.output_image_size @@ fun (w, _) -> image w
-151
notty/examples/common.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - open Notty 5 - open Notty.Infix 6 - 7 - let pow n e = int_of_float (float n ** float e) 8 - 9 - module List = struct 10 - 11 - include List 12 - 13 - let rec replicate n a = if n < 1 then [] else a :: replicate (n - 1) a 14 - 15 - let rec range a b = if a > b then [] else a :: range (a + 1) b 16 - 17 - let rec intersperse a = function 18 - | [] | [_] as t -> t 19 - | x::xs -> x :: a :: intersperse a xs 20 - 21 - let rec take n = function 22 - | x::xs when n > 0 -> x :: take (pred n) xs 23 - | _ -> [] 24 - 25 - let rec splitat n = function 26 - | x::xs when n > 0 -> 27 - let (a, b) = splitat (pred n) xs in (x::a, b) 28 - | xs -> ([], xs) 29 - 30 - let rec chunks n xs = 31 - match splitat n xs with 32 - | (a, []) -> [a] 33 - | (a, b) -> a :: chunks n b 34 - 35 - let rec zip xs ys = match (xs, ys) with 36 - | ([], _) | (_, []) -> [] 37 - | (x::xs, y::ys) -> (x, y) :: zip xs ys 38 - 39 - end 40 - 41 - module String = struct 42 - 43 - include String 44 - 45 - let repeat n str = 46 - let b = Buffer.create 16 in 47 - for _ = 1 to n do Buffer.add_string b str done; 48 - Buffer.contents b 49 - end 50 - 51 - let tile w h i = I.tabulate w h (fun _ _ -> i) 52 - 53 - (** A few images used in several places. *) 54 - module Images = struct 55 - 56 - let i1 = 57 - I.(string A.(fg lightblack) "omgbbq" <-> 58 - string A.(fg white ++ bg red) "@") 59 - <|> I.(pad ~t:2 @@ string A.(fg green) "xo") 60 - 61 - let i2 = I.(hpad 1 1 (hcrop 1 1 @@ tile 3 3 i1) <|> i1) 62 - 63 - let i3 = tile 5 5 i2 64 - 65 - let i4 = 66 - let i = I.(i3 <|> crop ~t:1 i3 <|> i3) in 67 - I.(crop ~l:1 i <-> crop ~r:1 i <-> crop ~b:2 i) 68 - 69 - let i5 = 70 - tile 5 1 List.( 71 - range 0 15 |> map (fun i -> I.pad ~t:i ~l:(i*2) i2) |> I.zcat 72 - ) 73 - 74 - let c_gray_ramp = 75 - I.tabulate 24 1 (fun g _ -> I.string A.(bg (gray g)) " ") 76 - 77 - let c_cube_ix = 78 - I.tabulate 6 1 @@ fun r _ -> 79 - I.hpad 0 1 @@ I.tabulate 6 6 @@ fun b g -> 80 - I.string A.(bg (rgb ~r ~g ~b)) " " 81 - 82 - let c_cube_rgb = 83 - let f x = [| 0x00; 0x5f; 0x87; 0xaf; 0xd7; 0xff |].(x) in 84 - I.tabulate 6 1 @@ fun r _ -> 85 - I.hpad 0 1 @@ I.tabulate 6 6 @@ fun b g -> 86 - I.string A.(bg (rgb_888 ~r:(f r) ~g:(f g) ~b:(f b))) " " 87 - 88 - let c_rainbow w h = 89 - let pi2 = 2. *. 3.14159 in 90 - let pi2_3 = pi2 /. 3. 91 - and f t off = sin (t +. off) *. 128. +. 128. |> truncate in 92 - let color t = A.rgb_888 ~r:(f t (-.pi2_3)) ~g:(f t 0.) ~b:(f t pi2_3) in 93 - I.tabulate (w - 1) 1 @@ fun x _ -> 94 - let t = (pi2 *. float x /. float w) +. 3.7 in 95 - I.char A.(bg (color t)) ' ' 1 h 96 - 97 - (* U+25CF BLACK CIRCLE *) 98 - let dot color = I.string (A.fg color) "●" 99 - (* U+25AA BLACK SMALL SQUARE *) 100 - let square color = I.string (A.fg color) "▪" 101 - 102 - let rec cantor = function 103 - | 0 -> square A.lightblue 104 - | n -> 105 - let sub = cantor (pred n) in 106 - I.hcat (List.replicate (pow 3 n) (square A.lightblue)) <-> 107 - (sub <|> I.void (pow 3 (n - 1)) 0 <|> sub) 108 - 109 - let checker n m i = 110 - let w = I.width i in 111 - I.(tile (n/2) (m/2) (hpad 0 w i <-> hpad w 0 i)) 112 - 113 - let checker1 = checker 20 20 I.(char A.(bg magenta) ' ' 2 1) 114 - 115 - let rec sierp c n = I.( 116 - if n > 1 then 117 - let ss = sierp c (pred n) in ss <-> (ss <|> ss) 118 - else hpad 1 0 (square c) 119 - ) 120 - 121 - let grid xxs = xxs |> List.map I.hcat |> I.vcat 122 - 123 - let outline attr i = 124 - let (w, h) = I.(width i, height i) in 125 - let chr x = I.uchar attr (Uchar.of_int x) 1 1 126 - and hbar = I.uchar attr (Uchar.of_int 0x2500) w 1 127 - and vbar = I.uchar attr (Uchar.of_int 0x2502) 1 h in 128 - let (a, b, c, d) = (chr 0x256d, chr 0x256e, chr 0x256f, chr 0x2570) in 129 - grid [ [a; hbar; b]; [vbar; i; vbar]; [d; hbar; c] ] 130 - end 131 - 132 - let halfblock = "▄" 133 - 134 - let pxmatrix w h f = I.tabulate w h @@ fun x y -> 135 - let y = y * 2 in 136 - I.string A.(bg (f x y) ++ fg (f x (y + 1))) halfblock 137 - 138 - module Term = Notty_unix.Term 139 - 140 - let simpleterm ~imgf ~f ~s = 141 - let term = Term.create () in 142 - let imgf (w, h) s = 143 - I.(string A.(fg lightblack) "[ESC quits.]" <-> imgf (w, h - 1) s) in 144 - let rec go s = 145 - Term.image term (imgf (Term.size term) s); 146 - match Term.event term with 147 - | `End | `Key (`Escape, []) | `Key (`ASCII 'C', [`Ctrl]) -> () 148 - | `Resize _ -> go s 149 - | #Unescape.event as e -> 150 - match f s e with Some s -> go s | _ -> () 151 - in go s
-52
notty/examples/common_lwt.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - open Notty 5 - open Lwt.Infix 6 - 7 - include Common 8 - 9 - module T = Notty_lwt.Term 10 - 11 - let simpleterm_lwt ~imgf ~f ~s = 12 - let term = T.create () in 13 - let imgf (w, h) s = 14 - I.(string A.(fg lightblack) "[ESC quits.]" <-> imgf (w, h - 1) s) in 15 - let step e s = match e with 16 - | `Key (`Escape, []) | `Key (`ASCII 'C', [`Ctrl]) -> 17 - T.release term >|= fun () -> s 18 - | `Resize dim -> T.image term (imgf dim s) >|= fun () -> s 19 - | #Unescape.event as e -> 20 - match f s e with 21 - | Some s -> T.image term (imgf (T.size term) s) >|= fun () -> s 22 - | None -> T.release term >|= fun () -> s 23 - in 24 - ( T.image term (imgf (T.size term) s) 25 - >>= fun () -> Lwt_stream.fold_s step (T.events term) s ) 26 - |> Lwt_main.run |> ignore 27 - 28 - 29 - let timer = function 30 - | None -> Lwt.wait () |> fst 31 - | Some t -> Lwt_unix.sleep t >|= fun _ -> `Timer 32 - 33 - let event e = Lwt_stream.get (T.events e) >|= function 34 - | Some (`Resize _ | #Unescape.event as x) -> x 35 - | None -> `End 36 - 37 - let simpleterm_lwt_timed ?delay ~f s0 = 38 - let term = T.create () in 39 - let rec loop (e, t) dim s = 40 - (e <?> t) >>= function 41 - | `End | `Key (`Escape, []) | `Key (`ASCII 'C', [`Ctrl]) -> 42 - Lwt.return_unit 43 - | `Resize dim as evt -> invoke (event term, t) dim s evt 44 - | #Unescape.event as evt -> invoke (event term, t) dim s evt 45 - | `Timer as evt -> invoke (e, timer delay) dim s evt 46 - and invoke es dim s e = 47 - match f dim s e with 48 - | `Continue s -> loop es dim s 49 - | `Redraw (s, i) -> T.image term i >>= fun () -> loop es dim s 50 - | `Stop -> Lwt.return_unit in 51 - let size = T.size term in 52 - loop (event term, timer delay) size s0
-36
notty/examples/crops.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** 5 - * Demonstrates edge-case behavior of functions that produce rectangle-like 6 - * things. 7 - *) 8 - open Notty 9 - open Common 10 - 11 - let hdistribute ?align w imgs = 12 - let n = List.length imgs in 13 - I.(List.map (hsnap ?align (w / n)) imgs |> hcat) 14 - 15 - let take w h i = I.(vsnap h i |> hsnap w) 16 - 17 - let () = 18 - simpleterm ~s:(2, 2) 19 - ~f:(fun (w, h as s) -> function 20 - `Key (`Arrow `Left, _) -> Some (w - 1, h) 21 - | `Key (`Arrow `Right, _) -> Some (w + 1, h) 22 - | `Key (`Arrow `Up, _) -> Some (w, h - 1) 23 - | `Key (`Arrow `Down, _) -> Some (w, h + 1) 24 - | `Key (`ASCII '0', _) -> Some (0, 0) 25 - | _ -> Some s) 26 - ~imgf:I.(fun (ow, oh) (w, h) -> 27 - let (a1, a2, a3) = A.(fg lightmagenta, fg lightred, fg lightblue) in 28 - strf "Sizing edge behavior. Dim: (%d, %d)" w h <-> 29 - ( hdistribute ow Images.[ 30 - outline a1 (uchar a1 (Uchar.of_int 0x2022) w h) 31 - ; outline a2 (uchar a2 (Uchar.of_int 0x2022) 300 300 |> take w h) 32 - ; outline a3 (void w h) 33 - ] |> vsnap (oh - 4) ) 34 - <-> 35 - hdistribute ow [string a1 "char"; string a2 "crop"; string a3 "void"] 36 - )
-26
notty/examples/cursor.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - open Notty 5 - open Common 6 - 7 - let rec main t (x, y as pos) = 8 - let img = 9 - let dot = I.string A.(bg lightred ++ fg black) "✓" |> I.pad ~l:x ~t:y 10 - and txt = I.strf ~attr:A.(fg lightblack) "@(%d, %d)" x y in 11 - I.(txt </> dot) in 12 - Term.image t img; 13 - Term.cursor t (Some pos); 14 - match Term.event t with 15 - | `End | `Key (`Escape, []) | `Key (`ASCII 'C', [`Ctrl]) -> () 16 - | `Resize _ -> main t pos 17 - | `Mouse ((`Press _ | `Drag), pos, _) -> main t pos 18 - | `Key (`Arrow d, _) -> 19 - ( main t @@ match d with 20 - | `Up -> (x, y - 1) 21 - | `Down -> (x, y + 1) 22 - | `Left -> (x - 1, y) 23 - | `Right -> (x + 1, y) ) 24 - | _ -> main t pos 25 - 26 - let () = main (Term.create ()) (0, 1)
-58
notty/examples/cuts.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** 5 - * Demonstrates text cropping, particularly of grapheme clusters and wide 6 - * characters. 7 - *) 8 - open Notty 9 - open Notty_unix 10 - open Common 11 - 12 - let hpadwith attr c a b i = 13 - I.(char attr c a 1 <|> i <|> char attr c b 1) 14 - 15 - let cuts i = 16 - let w = I.width i in 17 - List.( 18 - range 0 w |> map (fun a -> 19 - range 0 (w - a) |> map (fun b -> 20 - i |> I.hcrop a b |> hpadwith A.(fg lightblack) '.' a b 21 - ) |> I.vcat |> I.hpad 1 1 22 - ) |> I.hcat |> I.vpad 1 1 23 - ) 24 - 25 - let colors = A.[red; green; yellow; blue; magenta; cyan] 26 - 27 - let patterns = [ 28 - "desu" 29 - ; ".▪e\204\129●." 30 - ; "(茶‸茶‶)" 31 - ; "(⌐■_■)" 32 - (* ; "¯\\(ツ)/¯" *) 33 - (* ; "ಠ_ಠ" *) 34 - (* ; "ಡ_ಡ" *) 35 - (* ; "\xe0\xb2\xa0\x5f\xe0\xb1\x83" *) 36 - (* ; "ತಎತ" *) 37 - (* ; "ಥ_ಥ" *) 38 - ; "ᕕ( ᐛ )ᕗ" 39 - (* ; "ᕙ(⇀‸↼‶)ᕗ" *) 40 - (* ; "ᕦ(ò_óˇ)ᕤ" *) 41 - (* ; "(╯ ︵╰ )" *) 42 - (* ; "\x28\x20\xcd\xa1\xc2\xb0\x20\xcd\x9c\xca\x96\x20\xcd\xa1\xc2\xb0\x29" *) 43 - ] 44 - 45 - 46 - let () = 47 - let open I in 48 - 49 - patterns |> List.map (fun s -> 50 - cuts (string A.(fg lightmagenta ++ bg lightblack) s) 51 - ) |> I.vcat |> eol |> output_image ; 52 - 53 - colors |> List.mapi (fun i c -> 54 - pad ~l:i ~t:i ( 55 - string A.(fg black ++ bg c ++ st blink) "茶" <|> 56 - pad ~l:2 ~t:1 57 - (string A.(fg c ++ st blink) "PARTY!")) 58 - ) |> zcat |> pad ~l:2 ~t:2 ~b:2 |> output_image
-26
notty/examples/dune
··· 1 - (library 2 - (name common) 3 - (modules common) 4 - (libraries notty notty.unix)) 5 - 6 - (library 7 - (name common_lwt) 8 - (modules common_lwt) 9 - (libraries common notty.lwt)) 10 - 11 - (executables 12 - (names testpatterns colors almondbread crops cursor cuts emoji inline keys 13 - letters mouse thisbig runes sierpinski rain sierpinski_lwt linear 14 - life) 15 - (modules testpatterns colors almondbread crops cursor cuts emoji inline keys 16 - letters mouse thisbig runes sierpinski rain sierpinski_lwt linear 17 - life) 18 - (libraries common common_lwt)) 19 - 20 - (alias 21 - (name ex) 22 - (package notty) 23 - (deps testpatterns.exe colors.exe almondbread.exe crops.exe cursor.exe 24 - cuts.exe emoji.exe inline.exe keys.exe letters.exe mouse.exe 25 - thisbig.exe runes.exe sierpinski.exe rain.exe sierpinski_lwt.exe 26 - linear.exe life.exe))
-18
notty/examples/emoji.ml
··· 1 - open Notty 2 - open Common 3 - 4 - let es = [ 5 - [0x2e; 0x2e; 0x2e; 0x2e]; 6 - [0x25aa; 0x25fe; 0x25fc; 0x2b1b]; 7 - [0x1f346; 0x1f351; 0x1f605; 0x1f4a6]; 8 - [0x1f62d; 0x1f52a; 0x1f52a; 0x1f47c]; 9 - ] 10 - 11 - let image = 12 - es |> List.(map (map @@ fun x -> 13 - let i = I.uchar A.(fg lightwhite) (Uchar.of_int x) 1 1 in 14 - I.(pad ~r:(3 - width i) i) 15 - )) |> Images.grid |> I.pad ~l:1 |> Images.outline A.(fg lightblack) 16 - 17 - let () = Notty_unix.output_image_size @@ fun (w, _) -> 18 - I.(pad ~l:((w - width image) / 2) ~b:1 image)
-65
notty/examples/inline.ml
··· 1 - (* Copyright (c) 2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** Demonstrates manual cursor positioning. *) 5 - 6 - open Notty 7 - open Notty.Infix 8 - open Notty_unix 9 - 10 - let sleep n = flush stdout; Unix.select [] [] [] n |> ignore 11 - 12 - let pp_str attr = I.pp_attr attr Format.pp_print_string 13 - 14 - let rewind n = move_cursor `Home; move_cursor (`By (0, - (max n 0))) 15 - 16 - let output_subst ~prev i = 17 - let h = I.height prev in 18 - let d = h - I.height i in 19 - if d > 0 then ( rewind (d - 1); output_image (I.void 0 d) ); 20 - rewind (h - 1); output_image i 21 - 22 - let cmyk = function 23 - | 0 -> A.rgb ~r:0 ~g:5 ~b:5 24 - | 1 -> A.rgb ~r:5 ~g:0 ~b:5 25 - | 2 -> A.rgb ~r:5 ~g:5 ~b:0 26 - | 3 -> A.rgb ~r:0 ~g:0 ~b:0 27 - | _ -> A.rgb ~r:5 ~g:5 ~b:5 28 - 29 - let () = 30 - 31 - let (w, h) = match winsize Unix.stdout with 32 - Some dim -> dim | _ -> assert false 33 - and attr = A.(fg lightwhite ++ bg blue) in 34 - let img1 = 35 - I.(string attr "THE BLUE STRIPE ABOVE" <-> 36 - tabulate 1 h (fun _ _ -> I.strf "HIDDEN")) 37 - and img2 = 38 - I.(strf "Top line. There's a %a above. ^^^" 39 - (pp_str attr) "blue stripe" |> vpad 0 2) in 40 - 41 - output_image img1; output_subst ~prev:img1 img2; 42 - 43 - output_image I.(string A.(fg white) "[..]" |> eol); 44 - for i = 0 to 5 do 45 - let a = A.(bg (rgb ~r:i ~b:(5 - i) ~g:0)) in 46 - let bg = I.tabulate 1 i (fun _ -> I.strf "HIDDEN [%d]") |> eol 47 - and fg = I.char a ' ' 19 (5 - i) <|> I.char a '-' 1 (5 - i) |> eol in 48 - output_image bg; output_subst ~prev:bg fg; 49 - done; 50 - output_image I.(string A.(fg white) "[..]" |> vpad 0 2); 51 - 52 - let rec go prev n = 53 - if n <= w then 54 - let h = log (float n) |> truncate in 55 - let i = prev <|> I.tabulate 1 h (fun _ y -> I.char A.(bg (cmyk y)) ' ' 1 1) in 56 - output_subst ~prev i; sleep 0.01; go i (n + 1) 57 - else output_subst ~prev I.empty in 58 - show_cursor false; 59 - go I.empty 1; 60 - show_cursor true; 61 - 62 - output_image 63 - I.(strf "It doesn't say %a anywhere on screen, either." 64 - (pp_attr A.(fg white) Format.pp_print_string) "hidden" |> eol) 65 -
-73
notty/examples/keys.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** 5 - * Demonstrates input parsing. 6 - *) 7 - open Notty 8 - open Common 9 - 10 - let pps = Format.pp_print_string 11 - let ppi = Format.pp_print_int 12 - 13 - let pp_special fmt = function 14 - | `Escape -> pps fmt "ESCAPE" 15 - | `Enter -> pps fmt "ENTER" 16 - | `Tab -> pps fmt "TAB" 17 - | `Backspace -> pps fmt "BACKSPACE" 18 - | `Arrow `Up -> pps fmt "UP" 19 - | `Arrow `Down -> pps fmt "DOWN" 20 - | `Arrow `Left -> pps fmt "LEFT" 21 - | `Arrow `Right -> pps fmt "RIGHT" 22 - | `Page `Up -> pps fmt "PAGE UP" 23 - | `Page `Down -> pps fmt "PAGE DOWN" 24 - | `Home -> pps fmt "HOME" 25 - | `End -> pps fmt "END" 26 - | `Insert -> pps fmt "INSERT" 27 - | `Delete -> pps fmt "DELETE" 28 - | `Function n -> pps fmt "FN"; ppi fmt n 29 - 30 - let pp_mods fmt = function 31 - | [] -> () 32 - | ms -> ms |> List.iter (fun m -> 33 - pps fmt @@ match m with `Meta -> "M" | `Ctrl -> "C" | `Shift -> "S" 34 - ) 35 - 36 - let pp_mouse fmt = function 37 - | `Release -> pps fmt "Release" 38 - | `Drag -> pps fmt "Drag" 39 - | `Move -> pps fmt "Move" 40 - | `Press k -> 41 - pps fmt "Press "; 42 - pps fmt @@ match k with 43 - | `Left -> "Left" 44 - | `Middle -> "Middle" 45 - | `Right -> "Right" 46 - | `Scroll `Up -> "Scroll Up" 47 - | `Scroll `Down -> "Scroll Down" 48 - 49 - let pp_u ppf u = Format.fprintf ppf "U+%04X" (Uchar.to_int u) 50 - 51 - let () = 52 - let magenta = A.(fg lightmagenta ++ bg black) 53 - and green = A.(fg lightgreen ++ bg black) 54 - and blue = A.(fg lightblue ++ bg black) in 55 - let pp_mods = I.pp_attr green pp_mods 56 - and pp_mouse = I.pp_attr blue pp_mouse in 57 - simpleterm ~s:[] 58 - ~f:(fun xs x -> Some (List.take 100 (x::xs))) 59 - ~imgf:(fun (_, h) xs -> 60 - let attr = magenta in 61 - let msg = I.string A.empty "Push keys." 62 - and ks = List.map (function 63 - | `Key ((`ASCII _ | `Uchar _) as c, mods) -> 64 - let u = Unescape.uchar c in 65 - I.(uchar blue u 1 1 <|> strf ~attr " %a %a" pp_u u pp_mods mods) 66 - | `Key (#Unescape.special as k, mods) -> 67 - I.strf ~attr "%a %a" pp_special k pp_mods mods 68 - | `Mouse (e, (x, y), mods) -> 69 - I.strf ~attr "MOUSE %a (%d, %d) %a" pp_mouse e x y pp_mods mods 70 - | `Paste e -> 71 - I.strf ~attr "PASTE %s" (if e = `Start then "START" else "END") 72 - ) xs |> I.vcat in 73 - I.(vsnap ~align:`Top (h - 3) ks <-> void 0 1 <-> msg |> pad ~l:1 ~t:1))
-31
notty/examples/letters.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** 5 - * Dancing letters. 6 - *) 7 - open Notty 8 - open Common 9 - 10 - let nw = 6 11 - and nh = 5 12 - 13 - let () = 14 - simpleterm ~s:[] 15 - ~f:(fun us -> function 16 - | `Key ((`Delete|`Backspace), _) -> 17 - Some (match us with _::xs -> xs | _ -> us) 18 - | `Key ((`ASCII _|`Uchar _ as u), _) -> 19 - Some (List.take (nw * nh) (Unescape.uchar u :: us)) 20 - | _ -> Some us) 21 - ~imgf:(fun _ us -> 22 - let open List in 23 - let uus = chunks nw (rev us) in 24 - mapi (fun i us -> 25 - mapi (fun j u -> 26 - I.uchar A.(fg white ++ bg (rgb ~r:0 ~g:i ~b:j)) u 1 1 27 - ) us |> I.hcat 28 - ) uus |> I.vcat 29 - |> I.pad ~t:1 ~l:1 30 - |> I.hsnap ~align:`Left (nw + 1) 31 - |> tile nw 1)
-108
notty/examples/life.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (* 5 - * Game of Life with some ZX spectrum kitsch. 6 - *) 7 - 8 - let flip f a b = f b a 9 - 10 - (** Live, **) 11 - 12 - module Coord = struct 13 - type t = int * int 14 - let compare ((a, b) : t) (c, d) = 15 - match compare a c with 0 -> compare b d | r -> r 16 - let equal ((a, b) : t) (c, d) = a = c && b = d 17 - end 18 - 19 - module CSet = struct 20 - include Set.Make (Coord) 21 - let of_list = List.fold_left (flip add) empty 22 - let map f s = fold (fun x s -> add (f x) s) s empty 23 - end 24 - 25 - module CMap = struct 26 - include Map.Make (Coord) 27 - let preimg p m = 28 - fold (fun k v s -> if p v then CSet.add k s else s) m CSet.empty 29 - end 30 - 31 - let erem x y = (x mod y + y) mod y 32 - let square (w, h) (a, b as ab) = 33 - if a < 0 || a >= w || b < 0 || b >= h then (-1, -1) else ab 34 - let torus (w, h) (a, b) = (erem a w, erem b h) 35 - let moebius (w, h) (a, b as ab) = 36 - if a < 0 || a >= w then (erem a w, h - b - 1) else ab 37 - 38 - let neigh topo (a, b) = [ 39 - (a-1, b); (a+1, b); (a-1, b-1); (a-1, b+1) 40 - ; (a, b-1); (a, b+1); (a+1, b-1); (a+1, b+1) 41 - ] |> List.map topo 42 - 43 - let step topo life = 44 - let nlive pt = 45 - List.(neigh topo pt |> filter (flip CSet.mem life) |> length) in 46 - let f1 pt acc = 47 - pt :: neigh topo pt |> List.fold_left (fun acc -> function 48 - | (-1, -1) -> acc 49 - | pt when CMap.mem pt acc -> acc 50 - | pt -> 51 - let n = nlive pt in 52 - acc |> CMap.add pt 53 - (if n = 3 || (n = 2 && CSet.mem pt life) then 0 else 1) 54 - ) acc in 55 - CSet.fold f1 life CMap.empty |> CMap.preimg ((=) 0) 56 - 57 - let glider = CSet.of_list [(2,1); (3,2); (1,3); (2,3); (3,3)] 58 - 59 - (** ...render, **) 60 - 61 - open Notty 62 - open Notty.Infix 63 - 64 - let dot = I.string A.(fg lightred) "●" 65 - 66 - let background step (n, m) = 67 - let k = 24. *. sin (float (step + m + n) /. 10.) |> truncate in 68 - if k > 0 then I.string A.(fg (gray k)) "." else I.void 1 1 69 - 70 - let render (w, h) step life = 71 - I.tabulate w (h - 1) (fun x y -> 72 - let pt = (x, y) in if CSet.mem pt life then dot else background step pt 73 - ) <-> 74 - I.(strf ~attr:A.(fg lightblack) "[generation %04d]" step |> 75 - hsnap ~align:`Right w) 76 - 77 - (** ...and interact. **) 78 - 79 - open Lwt.Infix 80 - open Notty_lwt 81 - 82 - let timer () = Lwt_unix.sleep 0.1 >|= fun () -> `Timer 83 - let event term = Lwt_stream.get (Term.events term) >|= function 84 - | Some (`Resize _ | #Unescape.event as x) -> x 85 - | None -> `End 86 - 87 - let rec loop term (e, t) (dim, n, life as st) = 88 - (e <?> t) >>= function 89 - | `End | `Key (`Escape, []) | `Key (`ASCII 'C', [`Ctrl]) -> 90 - Lwt.return_unit 91 - | `Timer -> 92 - Term.image term (render dim n life) >>= fun () -> 93 - loop term (e, timer ()) 94 - (dim, n + 1, step (torus dim) life) 95 - | `Mouse ((`Press `Left|`Drag), (x, y), _) -> 96 - loop term (event term, t) 97 - (dim, n, CSet.add (torus dim (x, y)) life) 98 - | `Resize dim -> 99 - let life = CSet.map (torus dim) life in 100 - Term.image term (render dim n life) >>= fun () -> 101 - loop term (event term, t) (dim, n, life) 102 - | _ -> loop term (event term, t) st 103 - 104 - let main () = 105 - let t = Term.create () in 106 - loop t (event t, timer ()) (Term.size t, 0, glider) 107 - 108 - let () = Lwt_main.run @@ main ()
-64
notty/examples/linear.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (* 5 - * Elementary Cellular Automata 6 - *) 7 - open Notty 8 - open Notty.Infix 9 - open Common_lwt 10 - 11 - let flip f a b = f b a 12 - let rec take n = function 13 - | x::xs when n > 0 -> x :: take (pred n) xs 14 - | _ -> [] 15 - 16 - let getd arr d i = 17 - if i < 0 || i >= Array.length arr then d else arr.(i) 18 - 19 - let f ~rule a b c = 20 - if rule land (1 lsl (a lsl 2 + b lsl 1 + c)) > 0 then 1 else 0 21 - 22 - let step ~rule w arr = 23 - let get = getd arr 0 in 24 - Array.init w @@ fun i -> 25 - f ~rule (get (i - 1)) (get i) (get (i + 1)) 26 - 27 - let dot = I.char A.(bg lightwhite) ' ' 1 1 28 - let void = I.void 1 1 29 - 30 - let render ~rule ~h xss = 31 - let cons k = function 32 - | 0 -> I.void k 1 33 - | _ -> I.char A.(bg lightwhite) ' ' k 1 in 34 - let rec rline s k i arr = 35 - if i >= Array.length arr then 36 - cons k s 37 - else if arr.(i) = s then 38 - rline s (k + 1) (i + 1) arr 39 - else cons k s <|> rline (1 - s) 1 (i + 1) arr in 40 - ( xss |> List.rev |> List.map (rline 0 0 0) |> I.vcat 41 - |> I.vsnap ~align:`Top (h - 2) ) <-> 42 - ( I.strf ~attr:A.(fg lightgreen ++ bg black) " RULE %d " rule 43 - |> I.vpad 1 0 ) 44 - 45 - let rule = 124 (* 110 mirrored *) 46 - 47 - let main () = 48 - simpleterm_lwt_timed ~delay:0.1 ([], rule) 49 - ~f:(fun (w, h) (lines, rule) -> function 50 - | `Timer -> 51 - let prev = match lines with [] -> [|1|] | h::_ -> h in 52 - let lines = step ~rule w prev :: lines |> take (h - 2) in 53 - `Redraw ((lines, rule), render ~rule ~h lines) 54 - | `Resize _ -> 55 - let lines = lines |> take h in 56 - `Redraw ((lines, rule), render ~rule ~h lines) 57 - | `Key (`Arrow `Left, []) -> 58 - `Redraw (([], rule - 1), render ~rule ~h lines) 59 - | `Key (`Arrow `Right, []) -> 60 - `Redraw (([], rule + 1), render ~rule ~h lines) 61 - | _ -> `Continue (lines, rule) 62 - ) 63 - 64 - let () = Lwt_main.run @@ main ()
-47
notty/examples/mouse.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** 5 - * Demonstrates mouse input. 6 - *) 7 - open Notty 8 - open Common 9 - 10 - let lnv = Uchar.of_int 0x2502 11 - and lnh = Uchar.of_int 0x2500 12 - and crs = Uchar.of_int 0x253c 13 - 14 - let clip a b x = min b (max a x) 15 - 16 - let () = 17 - simpleterm ~s:(`Down, (0, 0), [], 11) 18 - ~f:(fun (st, pos, mods, scr as s) -> function 19 - | `Mouse ((`Press `Left|`Drag), pos, mods) -> Some (`Drag, pos, mods, scr) 20 - | `Mouse (`Press (`Scroll s), _, _) -> 21 - Some (st, pos, mods, clip 0 23 (scr + match s with `Up -> 1 | _ -> -1)) 22 - | `Mouse (`Release, pos, _) -> Some (`Down, pos, [], scr) 23 - | _ -> Some s) 24 - ~imgf:I.(fun (w, h) (st, (x, y), mods, scr) -> 25 - let cross = 26 - let a = match st with `Drag -> A.(fg lightgreen) | `Down -> A.(fg green) in 27 - (uchar a lnh x 1 |> vpad y 0) <|> 28 - (uchar a lnv 1 y <-> uchar a crs 1 1 <-> uchar a lnv 1 (h - y)) <|> 29 - (uchar a lnh (w - x - 1) 1 |> vpad y 0) 30 - |> crop ~t:1 ~l:1 ~r:3 31 - |> hpad 1 1 32 - |> vsnap ~align:`Top (h - 1) 33 - and scroll = 34 - List.(range 0 scr |> rev |> map @@ fun level -> 35 - Images.dot A.(gray level) 36 - ) |> vcat |> vsnap ~align:`Bottom (h - 1) 37 - and status = 38 - let a = A.(fg lightblack ++ bg black) in 39 - let fa m = if List.mem m mods then A.(fg lightgreen ++ bg black) else a in 40 - string A.empty "Use the mouse." </> 41 - (hcat [ string a "[" 42 - ; string (fa `Ctrl) "C" 43 - ; string (fa `Meta) "M" 44 - ; strf ~attr:a "] @(%03d, %03d)" x y ] 45 - |> hsnap ~align:`Right w) 46 - in (cross <|> scroll) <-> status 47 - )
-95
notty/examples/rain.ml
··· 1 - 2 - let () = Random.self_init () 3 - 4 - let rec (--) a b = if a > b then [] else a :: succ a -- b 5 - 6 - let utf8_of_code_point = 7 - let buf = Buffer.create 7 in fun cp -> 8 - Buffer.clear buf; 9 - Uutf.Buffer.add_utf_8 buf (Uchar.of_int cp); 10 - Buffer.contents buf 11 - 12 - let nsym = 4096 13 - let glitch = nsym / 20 14 - let symbols = Array.(concat [ 15 - init 58 (fun x -> utf8_of_code_point (0xff66 + x)); 16 - init 10 (fun x -> utf8_of_code_point (0x30 + x)); 17 - (* init 26 (fun x -> utf8_of_code_point (0x61 + x)); *) 18 - (* init 14 (fun x -> utf8_of_code_point (0x21 + x)); *) 19 - ]) 20 - let sym () = symbols.(Random.int (Array.length symbols)) 21 - let syms = Array.init nsym (fun _ -> sym ()) 22 - 23 - let gen_wait h = `Wait Random.(int (h / 2)) 24 - and gen_line h = 25 - `Line Random.(0, int (nsym - h), int (h + h / 2) + 1, int 2 + 1) 26 - let gen (w, h as dim) = 27 - let lines = 1 -- w |> List.map @@ fun _ -> 28 - if Random.float 1. < 0.1 then gen_line h else gen_wait h in 29 - (dim, lines) 30 - 31 - let step ((_, h as dim), xs) = 32 - let xs = xs |> List.map @@ function 33 - `Wait 0 -> gen_line h 34 - | `Wait n -> `Wait (n - 1) 35 - | `Line (i, _, win, k) when i - win + k >= h -> gen_wait h 36 - | `Line (i, s, win, k) -> `Line (i + k, s, win, k) in 37 - Random.(for _ = 0 to int glitch do syms.(int nsym) <- sym () done); 38 - (dim, xs) 39 - 40 - open Notty 41 - open Notty.Infix 42 - 43 - let bgc = A.(bg @@ rgb ~r:0 ~g:0 ~b:0) 44 - 45 - let color i n = 46 - let chan x = x *. 255. |> truncate 47 - and t = float i /. float n in 48 - let t1 = exp (-. t /. 0.02) |> chan 49 - and t2 = exp (-. t /. 0.45) |> chan in 50 - A.rgb_888 ~r:t1 ~b:t1 ~g:t2 51 - 52 - let show ((w, h), xs) = 53 - let f = function 54 - `Wait _ -> I.void 1 0 55 - | `Line (i, sym, win, _) -> 56 - let last = i - win 57 - and off = max 0 (i - h + 1) in 58 - let rec chars w = 59 - let ix = w + last in 60 - if 0 <= min ix w then syms.(sym + ix) :: chars (w - 1) else [] in 61 - let rec images acc i = function 62 - | [] -> acc 63 - | x::xs -> let img = I.string A.(fg (color i win) ++ bgc) x in 64 - images (img :: acc) (i + 1) xs in 65 - chars (win - off) |> images [] off 66 - |> I.vcat |> I.vpad (max 0 (i - win)) 0 in 67 - (List.map f xs |> I.hcat) </> I.char bgc ' ' w h 68 - 69 - open Notty_unix 70 - 71 - type r = [ Unescape.event | `Resize of int * int | `End | `Timer ] 72 - 73 - let event ~delay t = 74 - if Term.pending t then (Term.event t :> r) else 75 - let open Unix in 76 - match select [Term.fds t |> fst] [] [] delay with 77 - | ([], _, _) -> `Timer 78 - | (_::_, _, _) -> (Term.event t :> r) 79 - | exception Unix_error (EINTR, _, _) -> (Term.event t :> r) 80 - 81 - let loop t ~frame st = 82 - let rec go st deadline = 83 - let now = Unix.gettimeofday () in 84 - if deadline <= now then 85 - ( Term.image t (show st); go (step st) (frame +. deadline) ) 86 - else match event ~delay:(deadline -. now) t with 87 - | `End | `Key (`Escape, _) | `Key (`ASCII 'C', [`Ctrl]) -> () 88 - | `Resize _ | `Key (`ASCII ' ', _) -> go (gen (Term.size t)) deadline 89 - | _ -> go st deadline in 90 - go st (Unix.gettimeofday ()) 91 - 92 - let () = 93 - let t = Term.create () in 94 - loop t ~frame:0.075 (gen (Term.size t)); 95 - Term.release t
-92
notty/examples/runes.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** 5 - * Demonstrates geometry computation with various scripts. A few of those will 6 - * usually break. 7 - *) 8 - open Notty 9 - open Notty.Infix 10 - open Common 11 - 12 - let hpad_sp attr l r i = 13 - let h = I.height i in 14 - I.(char attr ' ' l h <|> i <|> char attr ' ' r h) 15 - 16 - let vpad_sp attr t b i = 17 - let w = I.width i in 18 - I.(char attr ' ' w t <-> i <-> char attr ' ' w b) 19 - 20 - let grid xxs = xxs |> List.map I.hcat |> I.vcat 21 - 22 - let centered attr xs = 23 - let lns = List.map I.(string attr) xs in 24 - let w = List.fold_left (fun a i -> max a I.(width i)) 0 lns in 25 - lns |> List.map I.(fun ln -> 26 - let d = w - I.width ln in 27 - char attr ' ' (d / 2) 1 <|> ln <|> char attr ' ' (d - d / 2) 1 28 - ) |> I.vcat 29 - 30 - let note xs = I.( 31 - string A.(st bold) "Note:" <|> 32 - (xs |> List.map (string A.empty) |> vcat |> hpad 1 0) 33 - ) 34 - 35 - let text = [ 36 - "\225\154\160\225\155\135\225\154\187\225\155\171\225\155\146\225\155\166\225\154\166\225\155\171\225\154\160\225\154\177\225\154\169\225\154\160\225\154\162\225\154\177\225\155\171\225\154\160\225\155\129\225\154\177\225\154\170\225\155\171\225\154\183\225\155\150\225\154\187\225\154\185\225\155\166\225\155\154\225\154\179\225\154\162\225\155\151" 37 - ; "\225\155\139\225\154\179\225\155\150\225\154\170\225\155\154\225\155\171\225\154\166\225\155\150\225\154\170\225\154\187\225\155\171\225\155\151\225\154\170\225\154\190\225\154\190\225\154\170\225\155\171\225\154\183\225\155\150\225\154\187\225\154\185\225\155\166\225\155\154\225\154\179\225\155\171\225\155\151\225\155\129\225\154\179\225\155\154\225\154\162\225\154\190\225\155\171\225\154\187\225\155\166\225\155\143\225\155\171\225\155\158\225\154\171\225\155\154\225\154\170\225\154\190" 38 - ; "\225\154\183\225\155\129\225\154\160\225\155\171\225\154\187\225\155\150\225\155\171\225\154\185\225\155\129\225\155\154\225\155\150\225\155\171\225\154\160\225\154\169\225\154\177\225\155\171\225\155\158\225\154\177\225\155\129\225\154\187\225\155\143\225\154\190\225\155\150\225\155\171\225\155\158\225\154\169\225\155\151\225\155\150\225\155\139\225\155\171\225\154\187\225\155\154\225\155\135\225\155\143\225\154\170\225\154\190\225\155\172" 39 - ; "" 40 - ; "\227\129\132\227\130\141\227\129\175\227\129\171\227\129\187\227\129\184\227\129\168\227\129\161\227\130\138\227\129\172\227\130\139\227\130\146" 41 - ; "\227\130\143\227\129\139\227\130\136\227\129\159\227\130\140\227\129\157\227\129\164\227\129\173\227\129\170\227\130\137\227\130\128" 42 - ; "\227\129\134\227\130\144\227\129\174\227\129\138\227\129\143\227\130\132\227\129\190\227\129\145\227\129\181\227\129\147\227\129\136\227\129\166" 43 - ; "\227\129\130\227\129\149\227\129\141\227\130\134\227\130\129\227\129\191\227\129\151\227\130\145\227\129\178\227\130\130\227\129\155\227\129\153" 44 - ; "" 45 - ; "\227\130\164\227\131\173\227\131\143\227\131\139\227\131\155\227\131\152\227\131\136 \227\131\129\227\131\170\227\131\140\227\131\171\227\131\178 \227\131\175\227\130\171\227\131\168\227\130\191\227\131\172\227\130\189 \227\131\132\227\131\141\227\131\138\227\131\169\227\131\160" 46 - ; "\227\130\166\227\131\176\227\131\142\227\130\170\227\130\175\227\131\164\227\131\158 \227\130\177\227\131\149\227\130\179\227\130\168\227\131\134 \227\130\162\227\130\181\227\130\173\227\131\166\227\131\161\227\131\159\227\130\183 \227\131\177\227\131\146\227\131\162\227\130\187\227\130\185\227\131\179" 47 - ; "" 48 - ; "\237\130\164\236\138\164\236\157\152 \234\179\160\236\156\160\236\161\176\234\177\180\236\157\128 \236\158\133\236\136\160\235\129\188\235\166\172 \235\167\140\235\130\152\236\149\188" 49 - ; "\237\149\152\234\179\160 \237\138\185\235\179\132\237\149\156 \234\184\176\236\136\160\236\157\128 \237\149\132\236\154\148\236\185\152 \236\149\138\235\139\164" 50 - ; "" 51 - ; "\206\158\206\181\207\131\206\186\206\181\207\128\206\172\206\182\207\137 \207\132\225\189\180\206\189 \207\136\207\133\207\135\206\191\207\134\206\184\207\140\207\129\206\177 \206\178\206\180\206\181\206\187\207\133\206\179\206\188\206\175\206\177" 52 - ; "" 53 - ; "\208\167\208\181\209\136\209\155\208\181 \209\134e\209\146\208\181\209\154\208\181 \208\188\209\128e\208\182\208\176\209\129\209\130\208\184\208\188 \209\159\208\176\208\186\208\190\208\188 \208\191\208\190\208\177\208\190\209\153\209\136\208\176\208\178\208\176" 54 - ; "\209\132\208\181\209\128\209\130\208\184\208\187\208\184\208\183\208\176\209\134\208\184\209\152\209\131 \208\179\208\181\208\189\209\129\208\186\208\184\209\133 \209\133\208\184\208\177\209\128\208\184\208\180\208\176!" 55 - ; "" 56 - ; "Heiz\195\182lr\195\188cksto\195\159abd\195\164mpfung." 57 - ; "" 58 - ; "\208\146 \209\135\208\176\209\137\208\176\209\133 \209\142\208\179\208\176 \208\182\208\184\208\187 \208\177\209\139 \209\134\208\184\209\130\209\128\209\131\209\129? \208\148\208\176, \208\189\208\190 \209\132\208\176\208\187\209\140\209\136\208\184\208\178\209\139\208\185 \209\141\208\186\208\183\208\181\208\188\208\191\208\187\209\143\209\128!" 59 - ; "" 60 - ; "\225\131\149\225\131\148\225\131\158\225\131\174\225\131\152\225\131\161 \225\131\162\225\131\167\225\131\144\225\131\157\225\131\161\225\131\144\225\131\156\225\131\152 \225\131\168\225\131\157\225\131\151\225\131\144 \225\131\160\225\131\163\225\131\161\225\131\151\225\131\144\225\131\149\225\131\148\225\131\154\225\131\152" 61 - ; "" 62 - ; "Lu\195\173s arg\195\188ia \195\160 J\195\186lia que \194\171bra\195\167\195\181es, f\195\169, ch\195\161," 63 - ; "\195\179xido, p\195\180r, z\195\162ng\195\163o\194\187 eram palavras do portugu\195\170s." 64 - ; "" 65 - ; "ding ↹ ∀ ⌘ ▓ ◭ ☃ ♠ ♋ ♕ ⚅ ♩ ☭ ✎ 🂡 bats" 66 - ; "" 67 - ; "\216\181\217\144\217\129 \216\174\217\142\217\132\217\130\217\142 \216\174\217\142\217\136\216\175\217\144 \217\131\217\142\217\133\217\144\216\171\217\132\217\144 \216\167\217\132\216\180\217\142\217\133\216\179\217\144 \216\165\217\144\216\176 \216\168\217\142\216\178\217\142\216\186\217\142\216\170 \226\128\148 \217\138\217\142\216\173\216\184\217\137 \216\167\217\132\216\182\217\142\216\172\217\138\216\185\217\143 \216\168\217\144\217\135\216\167 \217\134\217\142\216\172\217\132\216\167\216\161\217\142 \217\133\217\144\216\185\216\183\216\167\216\177\217\144" 68 - ; "" 69 - ; "\215\147\215\146 \215\161\215\167\215\168\215\159 \215\169\215\152 \215\145\215\153\215\157 \215\158\215\144\215\149\215\155\215\150\215\145 \215\149\215\156\215\164\215\170\215\162 \215\158\215\166\215\144 \215\156\215\149 \215\151\215\145\215\168\215\148 \215\144\215\153\215\154 \215\148\215\167\215\156\215\153\215\152\215\148" 70 - ; "" 71 - ; "\224\174\175\224\174\190\224\174\174\224\174\177\224\174\191\224\174\168\224\175\141\224\174\164 \224\174\174\224\175\138\224\174\180\224\174\191\224\174\149\224\174\179\224\174\191\224\174\178\224\175\135 \224\174\164\224\174\174\224\174\191\224\174\180\224\175\141\224\174\174\224\175\138\224\174\180\224\174\191 \224\174\170\224\175\139\224\174\178\224\175\141 \224\174\135\224\174\169\224\174\191\224\174\164\224\174\190\224\174\181\224\174\164\224\175\129 \224\174\142\224\174\153\224\175\141\224\174\149\224\175\129\224\174\174\224\175\141 \224\174\149\224\174\190\224\174\163\224\175\139\224\174\174\224\175\141," 72 - ; "\224\174\170\224\174\190\224\174\174\224\174\176\224\174\176\224\174\190\224\174\175\224\175\141 \224\174\181\224\174\191\224\174\178\224\174\153\224\175\141\224\174\149\224\175\129\224\174\149\224\174\179\224\174\190\224\174\175\224\175\141, \224\174\137\224\174\178\224\174\149\224\174\169\224\175\136\224\174\164\224\175\141\224\174\164\224\175\129\224\174\174\224\175\141 \224\174\135\224\174\149\224\174\180\224\175\141\224\174\154\224\175\141\224\174\154\224\174\191\224\174\154\224\175\138\224\174\178\224\174\170\224\175\141 \224\174\170\224\174\190\224\174\169\224\175\141\224\174\174\224\175\136 \224\174\149\224\175\134\224\174\159\224\175\141\224\174\159\224\175\129" 73 - ; "" 74 - ; "\224\178\172\224\178\190 \224\178\135\224\178\178\224\179\141\224\178\178\224\178\191 \224\178\184\224\178\130\224\178\173\224\178\181\224\178\191\224\178\184\224\179\129 \224\178\135\224\178\130\224\178\166\224\179\134\224\178\168\224\179\141\224\178\168 \224\178\185\224\179\131\224\178\166\224\178\175\224\178\166\224\178\178\224\178\191" 75 - ; "\224\178\168\224\178\191\224\178\164\224\179\141\224\178\175\224\178\181\224\179\130 \224\178\133\224\178\181\224\178\164\224\178\176\224\178\191\224\178\170 \224\178\184\224\178\164\224\179\141\224\178\175\224\178\190\224\178\181\224\178\164\224\178\190\224\178\176" 76 - ; "" 77 - ; "\224\164\139\224\164\183\224\164\191\224\164\175\224\165\139\224\164\130 \224\164\149\224\165\139 \224\164\184\224\164\164\224\164\190\224\164\168\224\165\135 \224\164\181\224\164\190\224\164\178\224\165\135 \224\164\166\224\165\129\224\164\183\224\165\141\224\164\159 \224\164\176\224\164\190\224\164\149\224\165\141\224\164\183\224\164\184\224\165\139\224\164\130 \224\164\149\224\165\135 \224\164\176\224\164\190\224\164\156\224\164\190 \224\164\176\224\164\190\224\164\181\224\164\163 \224\164\149\224\164\190 \224\164\184\224\164\176\224\165\141\224\164\181\224\164\168\224\164\190\224\164\182 \224\164\149\224\164\176\224\164\168\224\165\135 \224\164\181\224\164\190\224\164\178\224\165\135" 78 - ; "\224\164\181\224\164\191\224\164\183\224\165\141\224\164\163\224\165\129\224\164\181\224\164\164\224\164\190\224\164\176 \224\164\173\224\164\151\224\164\181\224\164\190\224\164\168 \224\164\182\224\165\141\224\164\176\224\165\128\224\164\176\224\164\190\224\164\174, \224\164\133\224\164\175\224\165\139\224\164\167\224\165\141\224\164\175\224\164\190 \224\164\149\224\165\135 \224\164\174\224\164\185\224\164\190\224\164\176\224\164\190\224\164\156 \224\164\166\224\164\182\224\164\176\224\164\165 \224\164\149\224\165\135 \224\164\172\224\164\161\224\164\188\224\165\135 \224\164\184\224\164\170\224\165\129\224\164\164\224\165\141\224\164\176 \224\164\165\224\165\135\224\165\164" 79 - ] 80 - 81 - let () = 82 - let attr = A.(fg lightmagenta) in 83 - let img = I.( 84 - centered attr text 85 - |> vpad_sp attr 1 1 |> hpad_sp attr 2 2 86 - |> Images.outline attr 87 - |> pad ~t:1 ~b:1 ~l:2 ~r:2 88 - ) <-> 89 - note [ "Alignment will usually break on the last few scripts." 90 - ; "This is at the limit of what terminals can do." 91 - ; ":(" ] 92 - in Notty_unix.(eol img |> output_image)
-24
notty/examples/sierpinski.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** 5 - * A classic example in combinatory graphics. 6 - * 7 - * Demonstrates interaction. 8 - *) 9 - open Notty 10 - open Common 11 - 12 - let () = 13 - simpleterm ~s:1 14 - ~f:(fun s -> function 15 - | `Key (`ASCII 'q', _) -> None 16 - | `Key (`Arrow a, _) -> 17 - ( match a with 18 - | `Up | `Left -> Some (max 1 (s - 1)) 19 - | `Down | `Right -> Some (min 10 (s + 1)) ) 20 - | _ -> Some s) 21 - ~imgf:I.(fun _ s -> 22 - string A.empty (string_of_int s) <-> 23 - pad ~l:2 ~t:1 (Images.sierp A.magenta s) 24 - )
-26
notty/examples/sierpinski_lwt.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** 5 - * Demonstrates Lwt interaction. 6 - *) 7 - open Notty 8 - open Common_lwt 9 - 10 - let img s = I.( 11 - string A.empty (string_of_int s) <-> hpad 2 0 (Images.sierp A.magenta s) 12 - ) 13 - 14 - let () = 15 - simpleterm_lwt ~s:1 16 - ~f:(fun s -> function 17 - | `Key (`ASCII 'q', _) -> None 18 - | `Key (`Arrow a, _) -> 19 - ( match a with 20 - | `Up | `Left -> Some (max 1 (s - 1)) 21 - | `Down | `Right -> Some (min 10 (s + 1)) ) 22 - | _ -> Some s) 23 - ~imgf:I.(fun _ s -> 24 - string A.empty (string_of_int s) <-> 25 - pad ~l:2 ~t:1 (Images.sierp A.magenta s) 26 - )
-11
notty/examples/testpatterns.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** 5 - * A few images that exercise image composition, cropping, and padding. This 6 - * test is a good canary. 7 - *) 8 - open Common 9 - open Notty_unix 10 - 11 - let () = Images.[i3; i5; checker1] |> List.map eol |> List.iter output_image
-12
notty/examples/thisbig.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - open Notty 5 - open Common 6 - 7 - let () = 8 - Notty_unix.output_image_size @@ fun (w, h) -> 9 - Images.outline A.(fg lightblue) 10 - I.(hsnap (w - 2) @@ 11 - vsnap (h - 3) @@ (* +1 for the prompt *) 12 - Images.sierp A.lightblue 5)
-24
notty/notty.opam
··· 1 - opam-version: "2.0" 2 - homepage: "https://github.com/pqwy/notty" 3 - dev-repo: "git+https://github.com/pqwy/notty.git" 4 - bug-reports: "https://github.com/pqwy/notty/issues" 5 - doc: "https://pqwy.github.io/notty/doc" 6 - author: "David Kaloper <dk505@cam.ac.uk>" 7 - maintainer: "David Kaloper <dk505@cam.ac.uk>" 8 - license: "ISC" 9 - synopsis: "Declaring terminals" 10 - description: 11 - "Notty is a declarative terminal library for OCaml structured around a notion 12 - of composable images. It tries to abstract away the basic terminal programming 13 - model, providing something simpler and more expressive." 14 - 15 - build: [ [ "dune" "subst" ] {dev} 16 - [ "dune" "build" "-p" name "-j" jobs ] ] 17 - depends: [ 18 - "ocaml" {>= "4.08.0"} 19 - "dune" {>= "1.7"} 20 - "cppo" {build & >= "1.1.0"} 21 - "uutf" {>= "1.0.0"} 22 - ] 23 - depopts: [ "lwt" ] 24 - conflicts: [ "lwt" {<"2.5.2"} ]
-7
notty/src-lwt/dune
··· 1 - (library 2 - (public_name notty.lwt) 3 - (synopsis "Notty Lwt+Unix IO") 4 - (name notty_lwt) 5 - (wrapped false) 6 - (optional) 7 - (libraries notty notty.unix lwt lwt.unix))
-140
notty/src-lwt/notty_lwt.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - open Lwt.Infix 5 - 6 - open Notty 7 - open Notty_unix 8 - open Private 9 - 10 - 11 - type ('a, 'b) either = Left of 'a | Right of 'b 12 - let left x = Left x 13 - let right y = Right y 14 - 15 - let (</>) a b = Lwt.pick [(a >|= left); (b >|= right)] 16 - let (<??>) a b = (a >|= left) <?> (b >|= right) 17 - 18 - let whenopt f = function Some x -> f x | None -> () 19 - 20 - let rec write fd buf off = function 21 - | 0 -> Lwt.return_unit 22 - | n -> Lwt_unix.write fd buf off n >>= fun w -> write fd buf (off + w) (n - w) 23 - 24 - module Lwt_condition = struct 25 - 26 - include Lwt_condition 27 - 28 - let map f c = 29 - let d = create () in 30 - let rec go () = wait c >>= fun x -> broadcast d (f x); go () 31 - in (Lwt.async go; d) 32 - 33 - let unburst ~t c = 34 - let d = create () in 35 - let rec delay x = Lwt_unix.sleep t </> wait c >>= function 36 - | Left () -> broadcast d x; start () 37 - | Right x -> delay x 38 - and start () = wait c >>= delay in 39 - Lwt.async start; d 40 - end 41 - 42 - module Term = struct 43 - 44 - let winches = lazy ( 45 - let c = Lwt_condition.create () in 46 - let `Revert _ = set_winch_handler (Lwt_condition.broadcast c) in 47 - c 48 - ) 49 - 50 - let winch () = Lazy.force winches |> Lwt_condition.wait 51 - 52 - let bsize = 1024 53 - 54 - let input_stream ~nosig fd stop = 55 - let `Revert f = setup_tcattr ~nosig (Lwt_unix.unix_file_descr fd) in 56 - let stream = 57 - let flt = Unescape.create () 58 - and ibuf = Bytes.create bsize in 59 - let rec next () = 60 - match Unescape.next flt with 61 - | #Unescape.event as r -> Lwt.return_some r 62 - | `End -> Lwt.return_none 63 - | `Await -> 64 - (Lwt_unix.read fd ibuf 0 bsize <??> stop) >>= function 65 - | Left n -> Unescape.input flt ibuf 0 n; next () 66 - | Right _ -> Lwt.return_none 67 - in Lwt_stream.from next in 68 - Lwt.async (fun () -> Lwt_stream.closed stream >|= f); 69 - stream 70 - 71 - type t = { 72 - ochan : Lwt_io.output_channel 73 - ; trm : Tmachine.t 74 - ; buf : Buffer.t 75 - ; fds : Lwt_unix.file_descr * Lwt_unix.file_descr 76 - ; events : [ Unescape.event | `Resize of (int * int) ] Lwt_stream.t 77 - ; stop : (unit -> unit) 78 - } 79 - 80 - let write t = 81 - Tmachine.output t.trm t.buf; 82 - let out = Buffer.contents t.buf in (* XXX There goes 0copy. :/ *) 83 - Buffer.clear t.buf; Lwt_io.write t.ochan out 84 - 85 - let refresh t = Tmachine.refresh t.trm; write t 86 - let image t image = Tmachine.image t.trm image; write t 87 - let cursor t curs = Tmachine.cursor t.trm curs; write t 88 - let set_size t dim = Tmachine.set_size t.trm dim 89 - let size t = Tmachine.size t.trm 90 - 91 - let release t = 92 - if Tmachine.release t.trm then 93 - ( t.stop (); write t >>= fun () -> Lwt_io.flush t.ochan ) 94 - else Lwt.return_unit 95 - 96 - let resizef fd stop on_resize = 97 - if Unix.isatty fd then 98 - let rcond = Lwt_condition.( 99 - Lazy.force winches |> unburst ~t:0.1 |> map (fun () -> winsize fd)) in 100 - let rec monitor () = 101 - (Lwt_condition.wait rcond <?> stop) >>= function 102 - | Some dim -> on_resize dim; monitor () 103 - | None -> Lwt.return_unit in 104 - Lwt.async monitor; 105 - Lwt_stream.from (fun () -> Lwt_condition.wait rcond <?> stop) 106 - |> Lwt_stream.map (fun dim -> `Resize dim) 107 - else Lwt_stream.of_list [] 108 - 109 - let create ?(dispose=true) ?(nosig=true) ?(mouse=true) ?(bpaste=true) 110 - ?(input=Lwt_unix.stdin) ?(output=Lwt_unix.stdout) () = 111 - let fd = Lwt_unix.unix_file_descr output in 112 - let (stop, stopw) = Lwt.wait () in 113 - let rec t = lazy { 114 - trm = Tmachine.create ~mouse ~bpaste (cap_for_fd fd) 115 - ; ochan = Lwt_io.(of_fd ~mode:output) output 116 - ; buf = Buffer.create 4096 117 - ; fds = (input, output) 118 - ; stop = (fun () -> Lwt.wakeup stopw None) 119 - ; events = Lwt_stream.choose 120 - [ input_stream ~nosig input stop 121 - ; resizef fd stop @@ fun dim -> 122 - let t = Lazy.force t in Buffer.reset t.buf; set_size t dim ] 123 - } in 124 - let t = Lazy.force t in 125 - winsize fd |> whenopt (set_size t); 126 - Lwt.async (fun () -> write t); (* XXX async? *) 127 - if dispose then Lwt_main.at_exit (fun () -> release t); 128 - t 129 - 130 - let events t = t.events 131 - let fds t = t.fds 132 - end 133 - 134 - let winsize fd = winsize (Lwt_unix.unix_file_descr fd) 135 - 136 - include Gen_output (struct 137 - type fd = Lwt_unix.file_descr and k = unit Lwt.t 138 - let (def, to_fd) = Lwt_unix.(stdout, unix_file_descr) 139 - and write fd buf = Buffer.(write fd (to_bytes buf) 0 (length buf)) 140 - end)
-100
notty/src-lwt/notty_lwt.mli
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** [Notty] IO [Lwt] on [Unix]. 5 - 6 - This is an IO module for {!Notty}. 7 - 8 - It mirrors {!Notty_unix} and the corresponding operations behave 9 - analogously. Consult its documentation for more info. 10 - 11 - {e %%VERSION%% — {{:%%PKG_HOMEPAGE%% }homepage}} *) 12 - 13 - open Notty 14 - 15 - (** {1:fullscreen Fullscreen input and output}. *) 16 - 17 - (** Terminal IO with concurrency. 18 - 19 - For more info, see {!Notty_unix.Term}. *) 20 - module Term : sig 21 - 22 - type t 23 - 24 - (** {1 Construction and destruction} *) 25 - 26 - val create : ?dispose:bool -> 27 - ?nosig:bool -> 28 - ?mouse:bool -> 29 - ?bpaste:bool -> 30 - ?input:Lwt_unix.file_descr -> 31 - ?output:Lwt_unix.file_descr -> 32 - unit -> t 33 - (** [create ~dispose ~nosig ~mouse ~input ~output ()] creates a new 34 - {{!t}terminal}. 35 - 36 - {b Note} [~dispose] arranges for the terminal to be disposed of at the end 37 - of the [Lwt] main loop, and not at process exit. 38 - 39 - See {!Notty_unix.Term.create}. *) 40 - 41 - val release : t -> unit Lwt.t 42 - 43 - (** {1 Commands} *) 44 - 45 - val image : t -> image -> unit Lwt.t 46 - val refresh : t -> unit Lwt.t 47 - val cursor : t -> (int * int) option -> unit Lwt.t 48 - 49 - (** {1 Events} *) 50 - 51 - val events : t -> [ Unescape.event | `Resize of (int * int) ] Lwt_stream.t 52 - (** [events t] is the stream of incoming events. 53 - 54 - Invoking {{!release}release} will terminate this stream. 55 - 56 - Events are: 57 - {ul 58 - {- [#Unescape.event], an {{!Notty.Unescape.event}event} from the input 59 - fd; or} 60 - {- [`Resize (cols, rows)] whenever the terminal size changes.}} 61 - 62 - {b Note} This stream is unique; for the same [t], [events t] always 63 - returns the same stream. *) 64 - 65 - (** {1 Properties} *) 66 - 67 - val size : t -> int * int 68 - 69 - val fds : t -> Lwt_unix.file_descr * Lwt_unix.file_descr 70 - 71 - (** {1 Window size change notifications} 72 - 73 - {{!create}Creating} a terminal will install a [SIGWINCH] handler. 74 - The handler should not be replaced directly. This API allows the user to 75 - monitor deliveries of the signal. 76 - 77 - See {!Notty_unix.Term.Winch}. *) 78 - 79 - val winch : unit -> unit Lwt.t 80 - (** [winch ()] is a thread completing after the next [SIGWINCH]. A single 81 - signal delivery will cause the completion of all waiting [winch] threads. *) 82 - end 83 - 84 - (** {1:inline Inline output} *) 85 - 86 - val winsize : Lwt_unix.file_descr -> (int * int) option 87 - 88 - val eol : image -> image 89 - 90 - val output_image : 91 - ?cap:Cap.t -> ?fd:Lwt_unix.file_descr -> image -> unit Lwt.t 92 - 93 - val output_image_size : 94 - ?cap:Cap.t -> ?fd:Lwt_unix.file_descr -> (int * int -> image) -> unit Lwt.t 95 - 96 - val show_cursor : ?cap:Cap.t -> ?fd:Lwt_unix.file_descr -> bool -> unit Lwt.t 97 - 98 - val move_cursor : 99 - ?cap:Cap.t -> ?fd:Lwt_unix.file_descr -> 100 - [ `Home | `By of int * int | `To of int * int ] -> unit Lwt.t
-11
notty/src-unix/dune
··· 1 - (library 2 - (public_name notty.unix) 3 - (synopsis "Notty Unix IO") 4 - (name notty_unix) 5 - (wrapped false) 6 - (c_names winsize) 7 - (c_flags (-Wall -Wextra -O3)) 8 - (optional) 9 - (libraries notty unix)) 10 - 11 - (include_subdirs unqualified)
-17
notty/src-unix/native/winsize.c
··· 1 - #include <sys/ioctl.h> 2 - #include <signal.h> 3 - #include <caml/mlvalues.h> 4 - 5 - CAMLprim value caml_notty_winsize (value vfd) { 6 - int fd = Int_val (vfd); 7 - struct winsize w; 8 - if (ioctl (fd, TIOCGWINSZ, &w) >= 0) 9 - return Val_int ((w.ws_col << 16) + ((w.ws_row & 0x7fff) << 1)); 10 - return Val_int (0); 11 - } 12 - 13 - #define __unit() value unit __attribute__((unused)) 14 - 15 - CAMLprim value caml_notty_winch_number (__unit()) { 16 - return Val_int (SIGWINCH); 17 - }
-181
notty/src-unix/notty_unix.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - open Notty 5 - 6 - external c_winsize : Unix.file_descr -> int = "caml_notty_winsize" [@@noalloc] 7 - external winch_number : unit -> int = "caml_notty_winch_number" [@@noalloc] 8 - 9 - let iter f = function Some x -> f x | _ -> () 10 - let value x = function Some a -> a | _ -> x 11 - 12 - let winsize fd = match c_winsize fd with 13 - | 0 -> None 14 - | wh -> Some (wh lsr 16, wh lsr 1 land 0x7fff) 15 - 16 - module Private = struct 17 - 18 - let once f = let v = lazy (f ()) in fun () -> Lazy.force v 19 - 20 - let cap_for_fd = 21 - let open Cap in 22 - match Sys.getenv "TERM" with 23 - | exception Not_found -> fun _ -> dumb 24 - | (""|"dumb") -> fun _ -> dumb 25 - | _ -> fun fd -> if Unix.isatty fd then ansi else dumb 26 - 27 - let setup_tcattr ~nosig fd = 28 - let open Unix in try 29 - let tc = tcgetattr fd in 30 - let tc1 = { tc with c_icanon = false; c_echo = false } in 31 - tcsetattr fd TCSANOW 32 - ( if nosig then { tc1 with c_isig = false; c_ixon = false } else tc1 ); 33 - `Revert (once @@ fun _ -> tcsetattr fd TCSANOW tc) 34 - with Unix_error (ENOTTY, _, _) -> `Revert ignore 35 - 36 - let set_winch_handler f = 37 - let signum = winch_number () in 38 - let old_hdl = Sys.(signal signum (Signal_handle (fun _ -> f ()))) in 39 - `Revert (once @@ fun () -> Sys.set_signal signum old_hdl) 40 - 41 - module Gen_output (O : sig 42 - type fd 43 - type k 44 - val def : fd 45 - val to_fd : fd -> Unix.file_descr 46 - val write : fd -> Buffer.t -> k 47 - end) = struct 48 - 49 - let scratch = lazy (Buffer.create 4096) 50 - 51 - let output ?cap ?(fd = O.def) f = 52 - let cap = cap |> value (cap_for_fd (O.to_fd fd)) in 53 - let buf = Lazy.force scratch in 54 - Buffer.reset buf; f buf cap fd; O.write fd buf 55 - 56 - let output_image_size ?cap ?fd f = 57 - output ?cap ?fd @@ fun buf cap fd -> 58 - let size = winsize (O.to_fd fd) in 59 - let i = f (value (80, 24) size) in 60 - let dim = match size with 61 - | Some (w, _) -> I.(w, height i) 62 - | None -> I.(width i, height i) in 63 - Render.to_buffer buf cap (0, 0) dim i 64 - 65 - let show_cursor ?cap ?fd x = 66 - output ?cap ?fd @@ fun buf cap _ -> Direct.show_cursor buf cap x 67 - 68 - let move_cursor ?cap ?fd x = 69 - output ?cap ?fd @@ fun buf cap _ -> Direct.move_cursor buf cap x 70 - 71 - let output_image ?cap ?fd i = output_image_size ?cap ?fd (fun _ -> i) 72 - 73 - let eol i = I.(i <-> void 0 1) 74 - end 75 - end 76 - 77 - open Private 78 - 79 - module Term = struct 80 - 81 - module Winch = struct 82 - 83 - let h = Hashtbl.create 3 84 - and id = ref 0 85 - 86 - let add fd f = 87 - let n = !id in 88 - set_winch_handler (fun () -> Hashtbl.iter (fun _ f -> f ()) h) |> ignore; 89 - Hashtbl.add h n (fun () -> winsize fd |> iter f); incr id; 90 - `Revert (fun () -> Hashtbl.remove h n) 91 - end 92 - 93 - module Input = struct 94 - 95 - type t = { 96 - fd : Unix.file_descr 97 - ; flt : Unescape.t 98 - ; ibuf : bytes 99 - ; cleanup : unit -> unit 100 - } 101 - 102 - let bsize = 1024 103 - 104 - let create ~nosig fd = 105 - let flt = Unescape.create () 106 - and ibuf = Bytes.create bsize 107 - and `Revert cleanup = setup_tcattr ~nosig fd in 108 - { fd; flt; ibuf; cleanup } 109 - 110 - let rec event t = 111 - match Unescape.next t.flt with 112 - | #Unescape.event | `End as r -> r 113 - | `Await -> 114 - let n = Unix.read t.fd t.ibuf 0 bsize in 115 - Unescape.input t.flt t.ibuf 0 n; event t 116 - end 117 - 118 - type t = { 119 - output : out_channel 120 - ; trm : Tmachine.t 121 - ; buf : Buffer.t 122 - ; input : Input.t 123 - ; fds : Unix.file_descr * Unix.file_descr 124 - ; unwinch : (unit -> unit) Lazy.t 125 - ; mutable winched : bool 126 - } 127 - 128 - let write t = 129 - Buffer.clear t.buf; 130 - Tmachine.output t.trm t.buf; 131 - Buffer.output_buffer t.output t.buf; flush t.output 132 - 133 - let set_size t dim = Tmachine.set_size t.trm dim 134 - let refresh t = Tmachine.refresh t.trm; write t 135 - let image t image = Tmachine.image t.trm image; write t 136 - let cursor t curs = Tmachine.cursor t.trm curs; write t 137 - let size t = Tmachine.size t.trm 138 - 139 - let release t = 140 - if Tmachine.release t.trm then 141 - ( Lazy.force t.unwinch (); 142 - t.input.Input.cleanup (); 143 - write t ) 144 - 145 - let create ?(dispose=true) ?(nosig=true) ?(mouse=true) ?(bpaste=true) 146 - ?(input=Unix.stdin) ?(output=Unix.stdout) () = 147 - let rec t = { 148 - output = Unix.out_channel_of_descr output 149 - ; trm = Tmachine.create ~mouse ~bpaste (cap_for_fd input) 150 - ; buf = Buffer.create 4096 151 - ; input = Input.create ~nosig input 152 - ; fds = (input, output) 153 - ; winched = false 154 - ; unwinch = lazy ( 155 - let `Revert f = Winch.add output @@ fun dim -> 156 - Buffer.reset t.buf; t.winched <- true; set_size t dim in f) 157 - } in 158 - winsize output |> iter (set_size t); 159 - (Lazy.force t.unwinch |> ignore) [@ocaml.warning "-5"]; 160 - if dispose then at_exit (fun () -> release t); 161 - write t; 162 - t 163 - 164 - let rec event = function 165 - | t when Tmachine.dead t.trm -> `End 166 - | t when t.winched -> t.winched <- false; `Resize (size t) 167 - | t -> Unix.(try Input.event t.input with Unix_error (EINTR, _, _) -> event t) 168 - 169 - let pending t = 170 - not (Tmachine.dead t.trm) && 171 - (t.winched || Unescape.pending t.input.Input.flt) 172 - 173 - let fds t = t.fds 174 - end 175 - 176 - include Gen_output (struct 177 - type fd = out_channel and k = unit 178 - let def = stdout 179 - and to_fd = Unix.descr_of_out_channel 180 - and write = Buffer.output_buffer 181 - end)
-222
notty/src-unix/notty_unix.mli
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** [Notty] IO for pure [Unix]. 5 - 6 - This is an IO module for {!Notty}. 7 - 8 - {e %%VERSION%% — {{:%%PKG_HOMEPAGE%% }homepage}} *) 9 - 10 - open Notty 11 - 12 - (** {1:fullscreen Fullscreen input and output}. *) 13 - 14 - (** Terminal IO abstraction for fullscreen, interactive applications. 15 - 16 - This module provides both input and output. It assumes exclusive ownership of 17 - the IO streams between {{!create}initialization} and {{!release}shutdown}. *) 18 - module Term : sig 19 - 20 - type t 21 - (** Representation of the terminal, giving structured access to IO. *) 22 - 23 - (** {1 Construction and destruction} *) 24 - 25 - val create : ?dispose:bool -> 26 - ?nosig:bool -> 27 - ?mouse:bool -> 28 - ?bpaste:bool -> 29 - ?input:Unix.file_descr -> 30 - ?output:Unix.file_descr -> 31 - unit -> t 32 - (** [create ~dispose ~nosig ~mouse ~input ~output ()] creates a fresh 33 - {{!t}terminal}. It has the following side effects: 34 - {ul 35 - {- [Unix.tcsetattr] is applied to [input] to disable {e echo} and 36 - {e canonical mode}.} 37 - {- [output] is set to {e alternate screen mode}, and the cursor is 38 - hidden. Mouse and {e bracketed paste} reporting are (optionally) 39 - enabled.} 40 - {- [SIGWINCH] signal, normally ignored, is handled.}} 41 - 42 - [~dispose] arranges for automatic {{!release}cleanup} of the terminal 43 - before the process terminates. The downside is that a reference to this 44 - terminal is retained until the program exits. Defaults to [true]. 45 - 46 - [~nosig] additionally turns off signal delivery and flow control 47 - ({e isig} and {e ixon}) on input. Inhibits automatic handling of 48 - {e CTRL-\{C,Z,\,S,Q\}}. Defaults to [true]. 49 - 50 - [~mouse] activates mouse reporting. Defaults to [true]. 51 - 52 - [~bpaste] activates bracketed paste reporting. Defaults to [true]. 53 - 54 - [~input] is the input file descriptor. Defaults to [stdin]. 55 - 56 - [~output] is the output file descriptor. Defaults to [stdout]. *) 57 - 58 - val release : t -> unit 59 - (** Dispose of this terminal. Original behavior of input fd is reinstated, 60 - cursor is restored, mouse reporting disabled, and alternate mode is 61 - terminated. 62 - 63 - It is an error to use the {{!cmds}commands} on a released terminal, and 64 - will raise [Invalid_argument], while [release] itself is idempotent. *) 65 - 66 - (** {1:cmds Commands} *) 67 - 68 - val image : t -> image -> unit 69 - (** [image t i] sets [i] as [t]'s current image and redraws the terminal. *) 70 - 71 - val refresh : t -> unit 72 - (** [refresh t] redraws the terminal using the current image. 73 - 74 - Useful if the output might have become garbled. *) 75 - 76 - val cursor : t -> (int * int) option -> unit 77 - (** [cursor t pos] sets and redraws the cursor. 78 - 79 - [None] hides it. [Some (x, y)] places it at column [x] and row [y], with 80 - the origin at [(0, 0)], mapping to the upper-left corner. *) 81 - 82 - (** {1 Events} *) 83 - 84 - val event : t -> [ Unescape.event | `Resize of (int * int) | `End ] 85 - (** Wait for a new event. [event t] can be: 86 - {ul 87 - {- [#Unescape.event], an {{!Notty.Unescape.event}[event]} from the input fd;} 88 - {- [`End] if the input fd is closed, or the terminal was released; or} 89 - {- [`Resize (cols, rows)] giving the current size of the output tty, if a 90 - [SIGWINCH] was delivered before or during this call to [event].}} 91 - 92 - {b Note} [event] is buffered. Calls can either block or immediately 93 - return. Use {{!pending}[pending]} to detect when the next call would not 94 - block. *) 95 - 96 - val pending : t -> bool 97 - (** [pending t] is [true] if the next call to {{!event}[event]} would not 98 - block and the terminal has not yet been released. *) 99 - 100 - (** {1 Properties} *) 101 - 102 - val size : t -> int * int 103 - (** [size t] is the current size of the terminal's output tty. *) 104 - 105 - val fds : t -> Unix.file_descr * Unix.file_descr 106 - (** [fds t] are [t]'s input and output file descriptors. *) 107 - 108 - (** {1 Window size change notifications} *) 109 - 110 - (** Manual [SIGWINCH] handling. 111 - 112 - Unix delivers notifications about tty size changes through the [SIGWINCH] 113 - signal. A handler for this signal is installed as soon as a new terminal 114 - is {{!create}created}. Replacing the global [SIGWINCH] handler using 115 - the [Sys] module will cause this module to malfunction, as the size change 116 - notifications will no longer be delivered. 117 - 118 - You might still want to ignore resizes reported by {{!event}[event]} and 119 - directly listen to [SIGWINCH]. This module allows installing such 120 - listeners without conflicting with the rest of the machinery. *) 121 - module Winch : sig 122 - 123 - val add : Unix.file_descr -> ((int * int) -> unit) -> [`Revert of unit -> unit] 124 - (** [add fd f] registers a [SIGWINCH] handler. Every time the signal is 125 - delivered, [f] is called with the current size of the tty backing [fd]. 126 - If [fd] is not a tty, [f] is never called. 127 - 128 - Return value is a function that removes the handler [f]. 129 - 130 - Handlers are called in an unspecified order. *) 131 - 132 - end 133 - end 134 - 135 - (** {1:inline Inline output} 136 - 137 - These operations do not assume exclusive access to the output. This means 138 - that they can be combined with other means of producing output. At the same 139 - time, it means that they are affected by the current terminal state, and 140 - that this state is not tracked. *) 141 - 142 - val winsize : Unix.file_descr -> (int * int) option 143 - (** [winsize fd] is [Some (columns, rows)], the current dimensions of [fd]'s 144 - backing tty, or [None], when [fd] is not backed by a tty. *) 145 - 146 - val eol : image -> image 147 - (** [eol image] is [image], producing an extra newline when printed. *) 148 - 149 - val output_image : 150 - ?cap:Cap.t -> ?fd:out_channel -> image -> unit 151 - (** [output_image ?cap ?fd image] writes [image] to [fd]. 152 - 153 - The image is displayed in its full height. If the output is a tty, image 154 - width is clipped to the output width. Otherwise, full width is used. 155 - 156 - [~cap] is the {{!caps}optional} terminal capability set. 157 - 158 - [~fd] defaults to [stdout]. *) 159 - 160 - val output_image_size : ?cap:Cap.t -> ?fd:out_channel -> (int * int -> image) -> unit 161 - (** [output_image_size ?cap ?fd f] is 162 - [output_image ?cap ?fd (f size)] where [size] are [fd]'s current 163 - {{!winsize}output dimensions}. 164 - 165 - If [fd] is not backed by a tty, as a matter of convenience, [f] is applied 166 - to [(80, 24)]. Use {!Unix.isatty} or {{!winsize}[winsize]} to detect whether 167 - the output has a well-defined size. *) 168 - 169 - val show_cursor : ?cap:Cap.t -> ?fd:out_channel -> bool -> unit 170 - (** [show_cursor ?cap ?fd visible] toggles the cursor visibility on [fd]. *) 171 - 172 - val move_cursor : 173 - ?cap:Cap.t -> ?fd:out_channel -> 174 - [ `Home | `By of int * int | `To of int * int ] -> unit 175 - (** [move_cursor ?cap ?fd motion] moves the cursor on [fd]. 176 - 177 - [motion] is one of: 178 - {ul 179 - {- [`To (column, line)], positioning the cursor to [(column, line)]. Origin 180 - is [(0, 0)], the upper-left corner of the screen.} 181 - {- [`Home], moving the cursor the beginning of line.} 182 - {- [`By (columns, lines)], moving the cursor [columns] to the right (left if 183 - negative) and [lines] down (up if negative). 184 - 185 - {b Note} Behavior is terminal dependent if the movement overshoots the 186 - output size.}} *) 187 - 188 - (** {1:caps Capability detection} 189 - 190 - All [image] output requires {{!Notty.Cap.t}terminal capabilities}. 191 - 192 - When not provided, capabilities are auto-detected, by checking that the 193 - output is a tty, that the environment variable [$TERM] is set, and that it 194 - is not set to either [""] or ["dumb"]. If these conditions hold, 195 - {{!Notty.Cap.ansi}ANSI} escapes are used. Otherwise, {{!Notty.Cap.dumb}no} 196 - escapes are used. *) 197 - 198 - (**/**) 199 - (** {1 Private} 200 - 201 - These are private interfaces, prone to breakage. Don't use them. *) 202 - module Private : sig 203 - 204 - val cap_for_fd : Unix.file_descr -> Cap.t 205 - val setup_tcattr : nosig:bool -> Unix.file_descr -> [ `Revert of (unit -> unit) ] 206 - val set_winch_handler : (unit -> unit) -> [ `Revert of (unit -> unit) ] 207 - 208 - module Gen_output (O : sig 209 - type fd 210 - type k 211 - val def : fd 212 - val to_fd : fd -> Unix.file_descr 213 - val write : fd -> Buffer.t -> k 214 - end ) : sig 215 - val output_image : ?cap:Cap.t -> ?fd:O.fd -> image -> O.k 216 - val output_image_size : ?cap:Cap.t -> ?fd:O.fd -> (int * int -> image) -> O.k 217 - val show_cursor : ?cap:Cap.t -> ?fd:O.fd -> bool -> O.k 218 - val move_cursor : ?cap:Cap.t -> ?fd:O.fd -> [ `Home | `By of int * int | `To of int * int ] -> O.k 219 - val eol : image -> image 220 - end 221 - end 222 - (**/**)
-22
notty/src/dune
··· 1 - (include_subdirs unqualified) 2 - 3 - (library 4 - (public_name notty) 5 - (synopsis "Declaring terminals") 6 - (libraries uutf) 7 - (wrapped false) 8 - (modules notty notty_grapheme_cluster notty_uucp notty_uucp_data) 9 - (private_modules notty_grapheme_cluster notty_uucp notty_uucp_data)) 10 - 11 - (library 12 - (public_name notty.top) 13 - (synopsis "Notty toplevel support") 14 - (name notty_top) 15 - (wrapped false) 16 - (modules notty_top) 17 - (preprocess (action (run %{bin:cppo} -V OCAML:%{ocaml_version} %{input-file}))) 18 - (libraries notty compiler-libs.toplevel)) 19 - 20 - (install 21 - (section lib) 22 - (files (notty_top_init.ml as top/notty_top_init.ml)))
-13
notty/src/no-uucp/README.md
··· 1 - Cannibalized bits of Uucp: 2 - 3 - - `Notty_uucp_data` is generated from an actual Uucp installation. 4 - - `Notty_uucp` uses it to provide the few Unicode properties that Notty needs. 5 - - `Notty_grapheme_cluster` is `Grapheme_cluster` from Uuseg, adapted to use the 6 - above. 7 - 8 - Compiled size of these is on the order of 70K. Uucp is presently a monolithic 10M. 9 - 10 - The idea is to remove these in favor of the actual Uucp/Uuseg, as soon as it 11 - becomes possible to depend only on the necessary parts of Uucp. 12 - 13 - Uucp and Uuseg are Copyright (c) 2014 Daniel C. Bünzli.
-133
notty/src/no-uucp/notty_grapheme_cluster.ml
··· 1 - (*--------------------------------------------------------------------------- 2 - Copyright (c) 2014 Daniel C. Bünzli. All rights reserved. 3 - Distributed under the ISC license, see terms at the end of the file. 4 - %%NAME%% %%VERSION%% 5 - ---------------------------------------------------------------------------*) 6 - 7 - (* These are the rules as found in [1], with property values aliases [2] 8 - substituted. 9 - 10 - GB1. sot ÷ Any 11 - GB2. Any ÷ eot 12 - GB3. CR × LF 13 - GB4. (CN|CR|LF) ÷ 14 - GB5. ÷ (CN|CR|LF) 15 - GB6. L × (L|V|LV|LVT) 16 - GB7. (LV|V) × (V|T) 17 - GB8. (LVT|T) × T 18 - GB9. × (EX|ZWJ) 19 - GB9a. × SM 20 - GB9b. PP × 21 - GB10. (v10.0.0) (EB|EBG) EX* × EM 22 - GB11. (v10.0.0) ZWJ × (GAZ|EBG) 23 - GB12. sot (RI RI)* RI × RI 24 - GB13. [^RI] (RI RI)* × RI 25 - GB999. Any ÷ Any 26 - 27 - [1]: http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries 28 - [2]: http://www.unicode.org/Public/7.0.0/ucd/PropertyValueAliases.txt 29 - [3]: http://www.unicode.org/Public/7.0.0/ucd/auxiliary/GraphemeBreakTest.html 30 - 31 - By the structure of the rules we see that grapheme clusters 32 - boundaries can *mostly* be determined by simply looking at the 33 - grapheme cluster break property value of the character on the left 34 - and on the right of a boundary. The exceptions are GB10 and GB12-13 35 - which are handled specially by enriching the segmenter state in 36 - a horribly ad-hoc fashion. *) 37 - 38 - type ret = [ `Await | `Boundary | `End | `Uchar of Uchar.t ] 39 - 40 - type gcb = 41 - | CN | CR | EX | EB | EBG | EM | GAZ | L | LF | LV | LVT | PP | RI 42 - | SM | T | V | XX | ZWJ | Sot 43 - 44 - (* WARNING. The indexes used here need to be synchronized with those 45 - assigned by uucp for Uucp.Break.Low.grapheme_cluster. *) 46 - 47 - let byte_to_gcb = 48 - [| CN; CR; EX; EB; EBG; EM; GAZ; L; LF; LV; LVT; PP; RI; 49 - SM; T; V; XX; ZWJ; |] 50 - 51 - let gcb u = byte_to_gcb.(Notty_uucp.grapheme_cluster_boundary u) 52 - 53 - type state = 54 - | Fill (* get next uchar to decide boundary. *) 55 - | Flush (* an uchar is buffered, client needs to get it out with `Await. *) 56 - | End (* `End was added. *) 57 - 58 - type t = 59 - { mutable state : state; (* current state. *) 60 - mutable left : gcb; (* break property value left of boundary. *) 61 - mutable odd_ri : bool; (* odd number of RI on the left. *) 62 - mutable emoji_seq : bool; (* (EB|EBG) Extend* on the left. *) 63 - mutable buf : [ `Uchar of Uchar.t ] } (* bufferized add. *) 64 - 65 - let nul_buf = `Uchar (Uchar.unsafe_of_int 0x0000) 66 - 67 - let create () = 68 - { state = Fill; left = Sot; 69 - odd_ri = false; emoji_seq = false; 70 - buf = nul_buf (* overwritten *); } 71 - 72 - let break s right = match s.left, right with 73 - | (* GB1 *) Sot, _ -> true 74 - (* GB2 is handled by `End *) 75 - | (* GB3 *) CR, LF -> false 76 - | (* GB4 *) (CN|CR|LF), _ -> true 77 - | (* GB5 *) _, (CN|CR|LF) -> true 78 - | (* GB6 *) L, (L|V|LV|LVT) -> false 79 - | (* GB7 *) (LV|V), (V|T) -> false 80 - | (* GB8 *) (LVT|T), T -> false 81 - | (* GB9+a *) _, (EX|ZWJ|SM) -> false 82 - | (* GB9b *) PP, _ -> false 83 - | (* GB10 *) _, EM when s.emoji_seq -> false 84 - | (* GB11 *) ZWJ, (GAZ|EBG) -> false 85 - | (* GB12+13 *) RI, RI when s.odd_ri -> false 86 - | (* GB999 *) _, _ -> true 87 - 88 - let update_left s right = 89 - s.left <- right; 90 - match s.left with 91 - | EX -> (* keep s.emoji_seq as is *) s.odd_ri <- false 92 - | EB | EBG -> s.emoji_seq <- true; s.odd_ri <- false 93 - | RI -> s.emoji_seq <- false; s.odd_ri <- not s.odd_ri 94 - | _ -> s.emoji_seq <- false; s.odd_ri <- false 95 - 96 - let add s = function 97 - | `Uchar u as add -> 98 - begin match s.state with 99 - | Fill -> 100 - let right = gcb u in 101 - let break = break s right in 102 - update_left s right; 103 - if not break then add else 104 - (s.state <- Flush; s.buf <- add; `Boundary) 105 - | Flush | End -> assert false 106 - end 107 - | `Await -> 108 - begin match s.state with 109 - | Flush -> s.state <- Fill; (s.buf :> ret) 110 - | End -> `End 111 - | Fill -> `Await 112 - end 113 - | `End -> 114 - begin match s.state with 115 - | Fill -> s.state <- End; if s.left = Sot then `End else `Boundary 116 - | Flush | End -> assert false 117 - end 118 - 119 - (*--------------------------------------------------------------------------- 120 - Copyright (c) 2014 Daniel C. Bünzli 121 - 122 - Permission to use, copy, modify, and/or distribute this software for any 123 - purpose with or without fee is hereby granted, provided that the above 124 - copyright notice and this permission notice appear in all copies. 125 - 126 - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 127 - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 128 - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 129 - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 130 - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 131 - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 132 - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 133 - ---------------------------------------------------------------------------*)
-27
notty/src/no-uucp/notty_grapheme_cluster.mli
··· 1 - (*--------------------------------------------------------------------------- 2 - Copyright (c) 2014 Daniel C. Bünzli. All rights reserved. 3 - Distributed under the ISC license, see terms at the end of the file. 4 - %%NAME%% %%VERSION%% 5 - ---------------------------------------------------------------------------*) 6 - 7 - type ret = [ `Await | `Boundary | `End | `Uchar of Uchar.t ] 8 - 9 - type t 10 - val create : unit -> t 11 - val add : t -> [ `Await | `End | `Uchar of Uchar.t ] -> ret 12 - 13 - (*--------------------------------------------------------------------------- 14 - Copyright (c) 2014 Daniel C. Bünzli 15 - 16 - Permission to use, copy, modify, and/or distribute this software for any 17 - purpose with or without fee is hereby granted, provided that the above 18 - copyright notice and this permission notice appear in all copies. 19 - 20 - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 21 - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 22 - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 23 - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 24 - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 25 - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 26 - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 27 - ---------------------------------------------------------------------------*)
-48
notty/src/no-uucp/notty_uucp.ml
··· 1 - (* Copyright (c) 2020 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (* Unpacked interval lookup table. *) 5 - let find_i ~def k (xs, _, _ as tab) = 6 - let rec go i j (los, his, vs as tab) (k: int) def = 7 - if i > j then def else 8 - let x = (i + j) / 2 in 9 - if k < Array.unsafe_get los x then go i (x - 1) tab k def else 10 - if k > Array.unsafe_get his x then go (x + 1) j tab k def else 11 - Array.unsafe_get vs x in 12 - go 0 (Array.length xs - 1) tab k def 13 - 14 - (* 12-6-6-bit (0xfff-0x3f-0x3f) trie, 3 levels, array-array-string. 15 - Root is variable; lower levels are either empty or complete. *) 16 - let find_t ~def k tab = 17 - let k = if k > 0xd7ff then k - 0x800 else k in (* Pack to continuous range. *) 18 - let b0 = (k lsr 12) land 0xfff in 19 - if Array.length tab <= b0 then def else 20 - match Array.unsafe_get tab b0 with 21 - | [||] -> def 22 - | arr -> match Array.unsafe_get arr ((k lsr 6) land 0x3f) with 23 - | "" -> def 24 - | str -> String.unsafe_get str (k land 0x3f) |> Char.code 25 - 26 - (* We catch w = -1 and default to w = 1 to minimize the table. *) 27 - let tty_width_hint u = match Uchar.to_int u with 28 - | 0 -> 0 29 - | u when u <= 0x001F || 0x007F <= u && u <= 0x009F -> -1 30 - | u when u <= 0x02ff -> 1 31 - | u -> find_i ~def:1 u Notty_uucp_data.tty_width_hint 32 - 33 - let grapheme_cluster_boundary u = 34 - find_t ~def:16 (Uchar.to_int u) Notty_uucp_data.grapheme_cluster_boundary 35 - 36 - (* let check () = *) 37 - (* let pp_u ppf u = Format.fprintf ppf "u+%04x" (Uchar.to_int u) in *) 38 - (* let rec go i u = *) 39 - (* let w1 = tty_width_hint u *) 40 - (* and w2 = Uucp.Break.tty_width_hint u in *) 41 - (* if w1 <> w2 then Format.printf "w: %a here: %d there: %d@." pp_u u w1 w2; *) 42 - (* let gc1 = grapheme_cluster_boundary u *) 43 - (* and gc2 = Uucp.Break.Low.grapheme_cluster u in *) 44 - (* if gc1 <> gc2 then Format.printf "gc: %a here: %d there: %d@." pp_u u gc1 gc2; *) 45 - (* if u = Uchar.max then i else go (i + 1) (Uchar.succ u) in *) 46 - (* let n = go 1 Uchar.min in *) 47 - (* Format.printf "Checked equality for %d code points.@." n *) 48 -
-13
notty/src/no-uucp/notty_uucp.mli
··· 1 - (* Copyright (c) 2020 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (* This is a local copy of the (very few) relevant [uucp] properties. *) 5 - 6 - val tty_width_hint : Uchar.t -> int 7 - (* [Uucp.Break.tty_width_hint]. *) 8 - 9 - val grapheme_cluster_boundary : Uchar.t -> int 10 - (* [Uucp.Break.Low.grapheme_cluster]. *) 11 - 12 - (* val check : unit -> unit *) 13 -
-446
notty/src/no-uucp/notty_uucp_data.ml
··· 1 - (* Do not edit. 2 - * 3 - * This module contains select unicode properties extracted from Uucp, 4 - * using `./support/gen_unicode_props.ml`. 5 - * 6 - * Unicode version 13.0.0. 7 - *) 8 - 9 - 10 - let tty_width_hint = 11 - ([|0x0000; 0x0300; 0x0483; 0x0591; 0x05bf; 0x05c1; 0x05c4; 0x05c7; 0x0600; 12 - 0x0610; 0x061c; 0x064b; 0x0670; 0x06d6; 0x06df; 0x06e7; 0x06ea; 0x070f; 13 - 0x0711; 0x0730; 0x07a6; 0x07eb; 0x07fd; 0x0816; 0x081b; 0x0825; 0x0829; 14 - 0x0859; 0x08d3; 0x093a; 0x093c; 0x0941; 0x094d; 0x0951; 0x0962; 0x0981; 15 - 0x09bc; 0x09c1; 0x09cd; 0x09e2; 0x09fe; 0x0a01; 0x0a3c; 0x0a41; 0x0a47; 16 - 0x0a4b; 0x0a51; 0x0a70; 0x0a75; 0x0a81; 0x0abc; 0x0ac1; 0x0ac7; 0x0acd; 17 - 0x0ae2; 0x0afa; 0x0b01; 0x0b3c; 0x0b3f; 0x0b41; 0x0b4d; 0x0b55; 0x0b62; 18 - 0x0b82; 0x0bc0; 0x0bcd; 0x0c00; 0x0c04; 0x0c3e; 0x0c46; 0x0c4a; 0x0c55; 19 - 0x0c62; 0x0c81; 0x0cbc; 0x0cbf; 0x0cc6; 0x0ccc; 0x0ce2; 0x0d00; 0x0d3b; 20 - 0x0d41; 0x0d4d; 0x0d62; 0x0d81; 0x0dca; 0x0dd2; 0x0dd6; 0x0e31; 0x0e34; 21 - 0x0e47; 0x0eb1; 0x0eb4; 0x0ec8; 0x0f18; 0x0f35; 0x0f37; 0x0f39; 0x0f71; 22 - 0x0f80; 0x0f86; 0x0f8d; 0x0f99; 0x0fc6; 0x102d; 0x1032; 0x1039; 0x103d; 23 - 0x1058; 0x105e; 0x1071; 0x1082; 0x1085; 0x108d; 0x109d; 0x1100; 0x135d; 24 - 0x1712; 0x1732; 0x1752; 0x1772; 0x17b4; 0x17b7; 0x17c6; 0x17c9; 0x17dd; 25 - 0x180b; 0x1885; 0x18a9; 0x1920; 0x1927; 0x1932; 0x1939; 0x1a17; 0x1a1b; 26 - 0x1a56; 0x1a58; 0x1a60; 0x1a62; 0x1a65; 0x1a73; 0x1a7f; 0x1ab0; 0x1b00; 27 - 0x1b34; 0x1b36; 0x1b3c; 0x1b42; 0x1b6b; 0x1b80; 0x1ba2; 0x1ba8; 0x1bab; 28 - 0x1be6; 0x1be8; 0x1bed; 0x1bef; 0x1c2c; 0x1c36; 0x1cd0; 0x1cd4; 0x1ce2; 29 - 0x1ced; 0x1cf4; 0x1cf8; 0x1dc0; 0x1dfb; 0x200b; 0x202a; 0x2060; 0x2066; 30 - 0x20d0; 0x231a; 0x2329; 0x23e9; 0x23f0; 0x23f3; 0x25fd; 0x2614; 0x2648; 31 - 0x267f; 0x2693; 0x26a1; 0x26aa; 0x26bd; 0x26c4; 0x26ce; 0x26d4; 0x26ea; 32 - 0x26f2; 0x26f5; 0x26fa; 0x26fd; 0x2705; 0x270a; 0x2728; 0x274c; 0x274e; 33 - 0x2753; 0x2757; 0x2795; 0x27b0; 0x27bf; 0x2b1b; 0x2b50; 0x2b55; 0x2cef; 34 - 0x2d7f; 0x2de0; 0x2e80; 0x2e9b; 0x2f00; 0x2ff0; 0x3000; 0x302a; 0x302e; 35 - 0x3041; 0x3099; 0x309b; 0x3105; 0x3131; 0x3190; 0x31f0; 0x3220; 0x3250; 36 - 0x4e00; 0xa490; 0xa66f; 0xa674; 0xa69e; 0xa6f0; 0xa802; 0xa806; 0xa80b; 37 - 0xa825; 0xa82c; 0xa8c4; 0xa8e0; 0xa8ff; 0xa926; 0xa947; 0xa960; 0xa980; 38 - 0xa9b3; 0xa9b6; 0xa9bc; 0xa9e5; 0xaa29; 0xaa31; 0xaa35; 0xaa43; 0xaa4c; 39 - 0xaa7c; 0xaab0; 0xaab2; 0xaab7; 0xaabe; 0xaac1; 0xaaec; 0xaaf6; 0xabe5; 40 - 0xabe8; 0xabed; 0xac00; 0xf900; 0xfb1e; 0xfe00; 0xfe10; 0xfe20; 0xfe30; 41 - 0xfe54; 0xfe68; 0xfeff; 0xff01; 0xffe0; 0xfff9; 0x101fd; 0x102e0; 42 - 0x10376; 0x10a01; 0x10a05; 0x10a0c; 0x10a38; 0x10a3f; 0x10ae5; 0x10d24; 43 - 0x10eab; 0x10f46; 0x11001; 0x11038; 0x1107f; 0x110b3; 0x110b9; 0x110bd; 44 - 0x110cd; 0x11100; 0x11127; 0x1112d; 0x11173; 0x11180; 0x111b6; 0x111c9; 45 - 0x111cf; 0x1122f; 0x11234; 0x11236; 0x1123e; 0x112df; 0x112e3; 0x11300; 46 - 0x1133b; 0x11340; 0x11366; 0x11370; 0x11438; 0x11442; 0x11446; 0x1145e; 47 - 0x114b3; 0x114ba; 0x114bf; 0x114c2; 0x115b2; 0x115bc; 0x115bf; 0x115dc; 48 - 0x11633; 0x1163d; 0x1163f; 0x116ab; 0x116ad; 0x116b0; 0x116b7; 0x1171d; 49 - 0x11722; 0x11727; 0x1182f; 0x11839; 0x1193b; 0x1193e; 0x11943; 0x119d4; 50 - 0x119da; 0x119e0; 0x11a01; 0x11a33; 0x11a3b; 0x11a47; 0x11a51; 0x11a59; 51 - 0x11a8a; 0x11a98; 0x11c30; 0x11c38; 0x11c3f; 0x11c92; 0x11caa; 0x11cb2; 52 - 0x11cb5; 0x11d31; 0x11d3a; 0x11d3c; 0x11d3f; 0x11d47; 0x11d90; 0x11d95; 53 - 0x11d97; 0x11ef3; 0x13430; 0x16af0; 0x16b30; 0x16f4f; 0x16f8f; 0x16fe0; 54 - 0x16fe4; 0x16ff0; 0x17000; 0x18800; 0x18d00; 0x1b000; 0x1b150; 0x1b164; 55 - 0x1b170; 0x1bc9d; 0x1bca0; 0x1d167; 0x1d173; 0x1d185; 0x1d1aa; 0x1d242; 56 - 0x1da00; 0x1da3b; 0x1da75; 0x1da84; 0x1da9b; 0x1daa1; 0x1e000; 0x1e008; 57 - 0x1e01b; 0x1e023; 0x1e026; 0x1e130; 0x1e2ec; 0x1e8d0; 0x1e944; 0x1f004; 58 - 0x1f0cf; 0x1f18e; 0x1f191; 0x1f200; 0x1f210; 0x1f240; 0x1f250; 0x1f260; 59 - 0x1f300; 0x1f32d; 0x1f337; 0x1f37e; 0x1f3a0; 0x1f3cf; 0x1f3e0; 0x1f3f4; 60 - 0x1f3f8; 0x1f440; 0x1f442; 0x1f4ff; 0x1f54b; 0x1f550; 0x1f57a; 0x1f595; 61 - 0x1f5a4; 0x1f5fb; 0x1f680; 0x1f6cc; 0x1f6d0; 0x1f6d5; 0x1f6eb; 0x1f6f4; 62 - 0x1f7e0; 0x1f90c; 0x1f93c; 0x1f947; 0x1f97a; 0x1f9cd; 0x1fa70; 0x1fa78; 63 - 0x1fa80; 0x1fa90; 0x1fab0; 0x1fac0; 0x1fad0; 0x20000; 0x30000; 0xe0001; 64 - 0xe0020; 0xe0100|], 65 - [|0x0000; 0x036f; 0x0489; 0x05bd; 0x05bf; 0x05c2; 0x05c5; 0x05c7; 0x0605; 66 - 0x061a; 0x061c; 0x065f; 0x0670; 0x06dd; 0x06e4; 0x06e8; 0x06ed; 0x070f; 67 - 0x0711; 0x074a; 0x07b0; 0x07f3; 0x07fd; 0x0819; 0x0823; 0x0827; 0x082d; 68 - 0x085b; 0x0902; 0x093a; 0x093c; 0x0948; 0x094d; 0x0957; 0x0963; 0x0981; 69 - 0x09bc; 0x09c4; 0x09cd; 0x09e3; 0x09fe; 0x0a02; 0x0a3c; 0x0a42; 0x0a48; 70 - 0x0a4d; 0x0a51; 0x0a71; 0x0a75; 0x0a82; 0x0abc; 0x0ac5; 0x0ac8; 0x0acd; 71 - 0x0ae3; 0x0aff; 0x0b01; 0x0b3c; 0x0b3f; 0x0b44; 0x0b4d; 0x0b56; 0x0b63; 72 - 0x0b82; 0x0bc0; 0x0bcd; 0x0c00; 0x0c04; 0x0c40; 0x0c48; 0x0c4d; 0x0c56; 73 - 0x0c63; 0x0c81; 0x0cbc; 0x0cbf; 0x0cc6; 0x0ccd; 0x0ce3; 0x0d01; 0x0d3c; 74 - 0x0d44; 0x0d4d; 0x0d63; 0x0d81; 0x0dca; 0x0dd4; 0x0dd6; 0x0e31; 0x0e3a; 75 - 0x0e4e; 0x0eb1; 0x0ebc; 0x0ecd; 0x0f19; 0x0f35; 0x0f37; 0x0f39; 0x0f7e; 76 - 0x0f84; 0x0f87; 0x0f97; 0x0fbc; 0x0fc6; 0x1030; 0x1037; 0x103a; 0x103e; 77 - 0x1059; 0x1060; 0x1074; 0x1082; 0x1086; 0x108d; 0x109d; 0x115f; 0x135f; 78 - 0x1714; 0x1734; 0x1753; 0x1773; 0x17b5; 0x17bd; 0x17c6; 0x17d3; 0x17dd; 79 - 0x180e; 0x1886; 0x18a9; 0x1922; 0x1928; 0x1932; 0x193b; 0x1a18; 0x1a1b; 80 - 0x1a56; 0x1a5e; 0x1a60; 0x1a62; 0x1a6c; 0x1a7c; 0x1a7f; 0x1ac0; 0x1b03; 81 - 0x1b34; 0x1b3a; 0x1b3c; 0x1b42; 0x1b73; 0x1b81; 0x1ba5; 0x1ba9; 0x1bad; 82 - 0x1be6; 0x1be9; 0x1bed; 0x1bf1; 0x1c33; 0x1c37; 0x1cd2; 0x1ce0; 0x1ce8; 83 - 0x1ced; 0x1cf4; 0x1cf9; 0x1df9; 0x1dff; 0x200f; 0x202e; 0x2064; 0x206f; 84 - 0x20f0; 0x231b; 0x232a; 0x23ec; 0x23f0; 0x23f3; 0x25fe; 0x2615; 0x2653; 85 - 0x267f; 0x2693; 0x26a1; 0x26ab; 0x26be; 0x26c5; 0x26ce; 0x26d4; 0x26ea; 86 - 0x26f3; 0x26f5; 0x26fa; 0x26fd; 0x2705; 0x270b; 0x2728; 0x274c; 0x274e; 87 - 0x2755; 0x2757; 0x2797; 0x27b0; 0x27bf; 0x2b1c; 0x2b50; 0x2b55; 0x2cf1; 88 - 0x2d7f; 0x2dff; 0x2e99; 0x2ef3; 0x2fd5; 0x2ffb; 0x3029; 0x302d; 0x303e; 89 - 0x3096; 0x309a; 0x30ff; 0x312f; 0x318e; 0x31e3; 0x321e; 0x3247; 0x4dbf; 90 - 0xa48c; 0xa4c6; 0xa672; 0xa67d; 0xa69f; 0xa6f1; 0xa802; 0xa806; 0xa80b; 91 - 0xa826; 0xa82c; 0xa8c5; 0xa8f1; 0xa8ff; 0xa92d; 0xa951; 0xa97c; 0xa982; 92 - 0xa9b3; 0xa9b9; 0xa9bd; 0xa9e5; 0xaa2e; 0xaa32; 0xaa36; 0xaa43; 0xaa4c; 93 - 0xaa7c; 0xaab0; 0xaab4; 0xaab8; 0xaabf; 0xaac1; 0xaaed; 0xaaf6; 0xabe5; 94 - 0xabe8; 0xabed; 0xd7a3; 0xfaff; 0xfb1e; 0xfe0f; 0xfe19; 0xfe2f; 0xfe52; 95 - 0xfe66; 0xfe6b; 0xfeff; 0xff60; 0xffe6; 0xfffb; 0x101fd; 0x102e0; 96 - 0x1037a; 0x10a03; 0x10a06; 0x10a0f; 0x10a3a; 0x10a3f; 0x10ae6; 0x10d27; 97 - 0x10eac; 0x10f50; 0x11001; 0x11046; 0x11081; 0x110b6; 0x110ba; 0x110bd; 98 - 0x110cd; 0x11102; 0x1112b; 0x11134; 0x11173; 0x11181; 0x111be; 0x111cc; 99 - 0x111cf; 0x11231; 0x11234; 0x11237; 0x1123e; 0x112df; 0x112ea; 0x11301; 100 - 0x1133c; 0x11340; 0x1136c; 0x11374; 0x1143f; 0x11444; 0x11446; 0x1145e; 101 - 0x114b8; 0x114ba; 0x114c0; 0x114c3; 0x115b5; 0x115bd; 0x115c0; 0x115dd; 102 - 0x1163a; 0x1163d; 0x11640; 0x116ab; 0x116ad; 0x116b5; 0x116b7; 0x1171f; 103 - 0x11725; 0x1172b; 0x11837; 0x1183a; 0x1193c; 0x1193e; 0x11943; 0x119d7; 104 - 0x119db; 0x119e0; 0x11a0a; 0x11a38; 0x11a3e; 0x11a47; 0x11a56; 0x11a5b; 105 - 0x11a96; 0x11a99; 0x11c36; 0x11c3d; 0x11c3f; 0x11ca7; 0x11cb0; 0x11cb3; 106 - 0x11cb6; 0x11d36; 0x11d3a; 0x11d3d; 0x11d45; 0x11d47; 0x11d91; 0x11d95; 107 - 0x11d97; 0x11ef4; 0x13438; 0x16af4; 0x16b36; 0x16f4f; 0x16f92; 0x16fe3; 108 - 0x16fe4; 0x16ff1; 0x187f7; 0x18cd5; 0x18d08; 0x1b11e; 0x1b152; 0x1b167; 109 - 0x1b2fb; 0x1bc9e; 0x1bca3; 0x1d169; 0x1d182; 0x1d18b; 0x1d1ad; 0x1d244; 110 - 0x1da36; 0x1da6c; 0x1da75; 0x1da84; 0x1da9f; 0x1daaf; 0x1e006; 0x1e018; 111 - 0x1e021; 0x1e024; 0x1e02a; 0x1e136; 0x1e2ef; 0x1e8d6; 0x1e94a; 0x1f004; 112 - 0x1f0cf; 0x1f18e; 0x1f19a; 0x1f202; 0x1f23b; 0x1f248; 0x1f251; 0x1f265; 113 - 0x1f320; 0x1f335; 0x1f37c; 0x1f393; 0x1f3ca; 0x1f3d3; 0x1f3f0; 0x1f3f4; 114 - 0x1f43e; 0x1f440; 0x1f4fc; 0x1f53d; 0x1f54e; 0x1f567; 0x1f57a; 0x1f596; 115 - 0x1f5a4; 0x1f64f; 0x1f6c5; 0x1f6cc; 0x1f6d2; 0x1f6d7; 0x1f6ec; 0x1f6fc; 116 - 0x1f7eb; 0x1f93a; 0x1f945; 0x1f978; 0x1f9cb; 0x1f9ff; 0x1fa74; 0x1fa7a; 117 - 0x1fa86; 0x1faa8; 0x1fab6; 0x1fac2; 0x1fad6; 0x2fffd; 0x3fffd; 0xe0001; 118 - 0xe007f; 0xe01ef|], 119 - [|0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 120 - 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 121 - 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 122 - 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 123 - 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 2; 0; 0; 0; 0; 124 - 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 125 - 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 126 - 0; 0; 0; 0; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 127 - 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 0; 0; 0; 2; 2; 2; 2; 2; 0; 2; 128 - 2; 0; 2; 2; 2; 2; 2; 2; 2; 2; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 129 - 0; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 2; 130 - 2; 0; 0; 2; 0; 2; 2; 2; 0; 2; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 131 - 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 132 - 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 133 - 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 134 - 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 2; 0; 2; 2; 2; 2; 2; 2; 2; 2; 0; 135 - 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 2; 2; 2; 136 - 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 137 - 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 2; 0; 0; 0|]) 138 - 139 - let s000 = "" 140 - let s001 = "\000\000\000\000\000\000\000\000\000\000\b\000\000\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 141 - let s002 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\000" 142 - let s003 = "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\016\016\016\016\016\016\016\016\016\016\016\016\016\000\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 143 - let s004 = "\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002" 144 - let s005 = "\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 145 - let s006 = "\016\016\016\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 146 - let s007 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\002" 147 - let s008 = "\016\002\002\016\002\002\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 148 - let s009 = "\011\011\011\011\011\011\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\016\000\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 149 - let s010 = "\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 150 - let s011 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\011\016\002\002\002\002\002\002\016\016\002\002\016\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 151 - let s012 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\011\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002" 152 - let s013 = "\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 153 - let s014 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 154 - let s015 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\002\016\016" 155 - let s016 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\016\002\002\002\002\002\002\002\002\002\016\002\002\002\016\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 156 - let s017 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 157 - let s018 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\011\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002" 158 - let s019 = "\002\002\002\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\r\002\016\r\r" 159 - let s020 = "\r\002\002\002\002\002\002\002\002\r\r\r\r\002\r\r\016\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 160 - let s021 = "\016\002\r\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\002\r" 161 - let s022 = "\r\002\002\002\002\016\016\r\r\016\016\r\r\002\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016" 162 - let s023 = "\016\002\002\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\r\r" 163 - let s024 = "\r\002\002\016\016\016\016\002\002\016\016\002\002\002\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\002\016\016\016\016\016\016\016\016\016\016" 164 - let s025 = "\r\002\002\002\002\002\016\002\002\r\016\r\r\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002" 165 - let s026 = "\016\002\r\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\002\002" 166 - let s027 = "\r\002\002\002\002\016\016\r\r\016\016\r\r\002\016\016\016\016\016\016\016\002\002\002\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 167 - let s028 = "\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\r" 168 - let s029 = "\002\r\r\016\016\016\r\r\r\016\r\r\r\002\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 169 - let s030 = "\002\r\r\r\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002" 170 - let s031 = "\002\r\r\r\r\016\002\002\002\016\002\002\002\002\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 171 - let s032 = "\016\002\r\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\r\002" 172 - let s033 = "\r\r\002\r\r\016\002\r\r\016\r\r\002\002\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 173 - let s034 = "\002\002\r\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\002\r" 174 - let s035 = "\r\002\002\002\002\016\r\r\r\016\r\r\r\002\011\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 175 - let s036 = "\016\002\r\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 176 - let s037 = "\016\016\016\016\016\016\016\016\016\016\002\016\016\016\016\002\r\r\002\002\002\016\002\016\r\r\r\r\r\r\r\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\016\016\016\016\016\016\016\016\016\016\016\016" 177 - let s038 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\r\002\002\002\002\002\002\002\016\016\016\016\016" 178 - let s039 = "\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 179 - let s040 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\r\002\002\002\002\002\002\002\002\002\016\016\016" 180 - let s041 = "\016\016\016\016\016\016\016\016\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 181 - let s042 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\002\016\002\016\016\016\016\r\r" 182 - let s043 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\r" 183 - let s044 = "\002\002\002\002\002\016\002\002\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\016\016" 184 - let s045 = "\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 185 - let s046 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\r\002\002\002\002\002\002\016\002\002\r\r\002\002\016" 186 - let s047 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\002\002\016\016\016\016\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016" 187 - let s048 = "\016\016\002\016\r\002\002\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 188 - let s049 = "\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007" 189 - let s050 = "\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015" 190 - let s051 = "\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014" 191 - let s052 = "\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014" 192 - let s053 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 193 - let s054 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\016\016\016\016\016\016\016\016\016\016\016" 194 - let s055 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016" 195 - let s056 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\r\002\002\002\002\002\002\002\r\r" 196 - let s057 = "\r\r\r\r\r\r\002\r\r\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 197 - let s058 = "\016\016\016\016\016\016\016\016\016\016\016\002\002\002\000\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 198 - let s059 = "\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 199 - let s060 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\r\r\r\r\002\002\r\r\r\016\016\016\016\r\r\002\r\r\r\r\r\r\002\002\002\016\016\016\016" 200 - let s061 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\r\r\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 201 - let s062 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\002\r\002\002\002\002\002\002\002\016\002\016\002\016\016\002\002\002\002\002\002\002\002\r\r\r\r\r\r\002\002\002\002\002\002\002\002\002\002\016\016\002" 202 - let s063 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002" 203 - let s064 = "\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 204 - let s065 = "\002\002\002\002\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\r\002\r\r\r" 205 - let s066 = "\r\r\002\r\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016" 206 - let s067 = "\002\002\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\002\002\002\002\r\r\002\002\r\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 207 - let s068 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\r\002\002\r\r\r\002\r\002\002\002\r\r\016\016\016\016\016\016\016\016\016\016\016\016" 208 - let s069 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\r\r\r\r\r\r\002\002\002\002\002\002\002\002\r\r\002\002\016\016\016\016\016\016\016\016" 209 - let s070 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\016\002\002\002\002\002\002\002\002\002\002\002\002\002\r\002\002\002\002\002\002\002\016\016\016\016\002\016\016\016\016\016\016\002\016\016\r\002\002\016\016\016\016\016\016" 210 - let s071 = "\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\002\002\002\002\002" 211 - let s072 = "\016\016\016\016\016\016\016\016\016\016\016\000\002\017\000\000\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\000\000\000\000\000\000\000\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 212 - let s073 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 213 - let s074 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 214 - let s075 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 215 - let s076 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002" 216 - let s077 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002" 217 - let s078 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 218 - let s079 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 219 - let s080 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\016\002\002\002\002\002\002\002\002\002\002\016\016" 220 - let s081 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 221 - let s082 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 222 - let s083 = "\016\016\002\016\016\016\002\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\002\002\r\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 223 - let s084 = "\r\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\r\r\r\r\r\r\r\r\r\r" 224 - let s085 = "\r\r\r\r\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\002" 225 - let s086 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 226 - let s087 = "\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\r\r\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\016\016\016" 227 - let s088 = "\002\002\002\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\r\r\002\002\002\002\r\r\002\002\r\r" 228 - let s089 = "\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 229 - let s090 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\r\r\002\002\r\r\002\002\016\016\016\016\016\016\016\016\016" 230 - let s091 = "\016\016\016\002\016\016\016\016\016\016\016\016\002\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\016\016" 231 - let s092 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\002\002\002\016\016\002\002\016\016\016\016\016\002\002" 232 - let s093 = "\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\002\002\r\r\016\016\016\016\016\r\002\016\016\016\016\016\016\016\016\016" 233 - let s094 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\002\r\r\002\r\r\016\r\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 234 - let s095 = "\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n" 235 - let s096 = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" 236 - let s097 = "\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" 237 - let s098 = "\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n" 238 - let s099 = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n" 239 - let s100 = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" 240 - let s101 = "\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" 241 - let s102 = "\n\n\n\n\n\n\n\n\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\016\016\016\016\016\016\016\016\016\016\016\016\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015" 242 - let s103 = "\015\015\015\015\015\015\015\016\016\016\016\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\016\016\016\016" 243 - let s104 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 244 - let s105 = "\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 245 - let s106 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\000\000\000\000\000\000\000\000\000\000\000\000\016\016\016\016" 246 - let s107 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\016" 247 - let s108 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 248 - let s109 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\016\016\016\016\016" 249 - let s110 = "\016\002\002\002\016\002\002\016\016\016\016\016\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\016\016\016\016\002" 250 - let s111 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 251 - let s112 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 252 - let s113 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 253 - let s114 = "\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 254 - let s115 = "\r\002\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002" 255 - let s116 = "\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002" 256 - let s117 = "\002\002\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\r\002\002\002\002\r\r\002\002\016\016\011\016\016" 257 - let s118 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\011\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 258 - let s119 = "\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\r\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016" 259 - let s120 = "\016\016\016\016\016\r\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016" 260 - let s121 = "\002\002\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\r\002\002\002\002\002\002\002\002\002\r" 261 - let s122 = "\r\016\011\011\016\016\016\016\016\002\002\002\002\016\r\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 262 - let s123 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\r\002\002\002\r\r\002\r\002\002\016\016\016\016\016\016\002\016" 263 - let s124 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\r\r\r\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 264 - let s125 = "\002\r\r\r\r\016\016\r\r\016\016\r\r\r\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\r\r\016\016\002\002\002\002\002\002\002\016\016\016\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016" 265 - let s126 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\r\002\002\002\002\002\002\002\002" 266 - let s127 = "\r\r\002\002\002\r\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 267 - let s128 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\r\r\002\002\002\002\002\002\r\002\r\r\002\r\002" 268 - let s129 = "\002\r\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 269 - let s130 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\r\r\002\002\002\002\016\016\r\r\r\r\002\002\r\002" 270 - let s131 = "\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 271 - let s132 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\r\002\002\002\002\002\002\002\002\r\r\002\r\002" 272 - let s133 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\r\002\r\r\002\002\002\002\002\002\r\002\016\016\016\016\016\016\016\016" 273 - let s134 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\r\r\002\002\002\002\r\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 274 - let s135 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\r\002\002\002\002\002\002\002\002\002\r\002\002\016\016\016\016\016" 275 - let s136 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\r\r\r\r\r\016\r\r\016\016\002\002\r\002\011" 276 - let s137 = "\r\011\r\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 277 - let s138 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\r\r\002\002\002\002\016\016\002\002\r\r\r\r\002\016\016\016\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 278 - let s139 = "\016\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\r\011\002\002\002\002\016" 279 - let s140 = "\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\r\r\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 280 - let s141 = "\016\016\016\016\011\011\011\011\011\011\002\002\002\002\002\002\002\002\002\002\002\002\002\r\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 281 - let s142 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\r\002\002\002\002\002\002\002\016\002\002\002\002\002\002\r\002" 282 - let s143 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\r\002\002\002\002\002\002\002\r\002\002\r\002\002\016\016\016\016\016\016\016\016\016" 283 - let s144 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\016\016\016\002\016\002\002\016\002" 284 - let s145 = "\002\002\002\002\002\002\011\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 285 - let s146 = "\016\016\016\016\016\016\016\016\016\016\r\r\r\r\r\016\002\002\016\r\r\002\r\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 286 - let s147 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\r\r\016\016\016\016\016\016\016\016\016" 287 - let s148 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\000\000\000\000\000\000\000\000\000\016\016\016\016\016\016\016" 288 - let s149 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016" 289 - let s150 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016" 290 - let s151 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r" 291 - let s152 = "\r\r\r\r\r\r\r\r\016\016\016\016\016\016\016\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 292 - let s153 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\r\r\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 293 - let s154 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\016\000\000\000\000\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 294 - let s155 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\r\002\002\002\016\016\016\r\002\002\002\002\002\000\000\000\000\000\000\000\000\002\002\002\002\002" 295 - let s156 = "\002\002\002\016\016\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 296 - let s157 = "\016\016\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 297 - let s158 = "\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\002\002\002\002\002" 298 - let s159 = "\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016" 299 - let s160 = "\016\016\016\016\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 300 - let s161 = "\002\002\002\002\002\002\002\016\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\016\016\002\002\002\002\002\002\002\016\002\002\016\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 301 - let s162 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 302 - let s163 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 303 - let s164 = "\016\016\016\016\002\002\002\002\002\002\002\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" 304 - let s165 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012" 305 - let s166 = "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\002\002\002\002\002" 306 - let s167 = "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002" 307 - let s168 = "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" 308 - let s169 = "\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\002\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" 309 - 310 - let grapheme_cluster_boundary = 311 - [|[|s001; s002; s003; s000; s000; s000; s000; s000; s000; s000; s000; s000; 312 - s004; s005; s000; s000; s000; s000; s006; s000; s000; s000; s007; s008; 313 - s009; s010; s000; s011; s012; s013; s014; s015; s016; s017; s000; s018; 314 - s019; s020; s021; s022; s023; s024; s023; s025; s026; s027; s028; s029; 315 - s030; s031; s032; s033; s034; s035; s036; s037; s038; s039; s040; s041; 316 - s042; s043; s044; s045|]; 317 - [|s046; s047; s048; s000; s049; s050; s051; s052; s000; s000; s000; s000; 318 - s000; s053; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 319 - s000; s000; s000; s000; s054; s055; s056; s057; s058; s000; s059; s000; 320 - s060; s000; s000; s000; s061; s062; s063; s064; s065; s066; s067; s068; 321 - s069; s000; s000; s070; s000; s000; s000; s071; s000; s000; s000; s000; 322 - s000; s000; s000; s000|]; 323 - [|s072; s073; s000; s074; s000; s000; s000; s000; s000; s000; s000; s000; 324 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 325 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 326 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 327 - s000; s000; s000; s075; s000; s076; s000; s077; s000; s000; s000; s000; 328 - s000; s000; s000; s000|]; 329 - [|s078; s000; s079; s000; s000; s000; s000; s000; s000; s000; s000; s000; 330 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 331 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 332 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 333 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 334 - s000; s000; s000; s000|]; 335 - [||]; [||]; [||]; [||]; [||]; [||]; 336 - [|s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 337 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 338 - s000; s080; s081; s082; s000; s000; s000; s000; s083; s000; s084; s085; 339 - s086; s087; s088; s089; s090; s091; s092; s093; s000; s000; s000; s094; 340 - s095; s096; s097; s098; s099; s100; s101; s095; s096; s097; s098; s099; 341 - s100; s101; s095; s096|]; 342 - [|s097; s098; s099; s100; s101; s095; s096; s097; s098; s099; s100; s101; 343 - s095; s096; s097; s098; s099; s100; s101; s095; s096; s097; s098; s099; 344 - s100; s101; s095; s096; s097; s098; s099; s100; s101; s095; s096; s097; 345 - s098; s099; s100; s101; s095; s096; s097; s098; s099; s100; s101; s095; 346 - s096; s097; s098; s099; s100; s101; s095; s096; s097; s098; s099; s100; 347 - s101; s095; s096; s097|]; 348 - [|s098; s099; s100; s101; s095; s096; s097; s098; s099; s100; s101; s095; 349 - s096; s097; s098; s099; s100; s101; s095; s096; s097; s098; s099; s100; 350 - s101; s095; s096; s097; s098; s099; s100; s101; s095; s096; s097; s098; 351 - s099; s100; s101; s095; s096; s097; s098; s099; s100; s101; s095; s096; 352 - s097; s098; s099; s100; s101; s095; s096; s097; s098; s099; s100; s101; 353 - s095; s096; s097; s098|]; 354 - [|s099; s100; s101; s095; s096; s097; s098; s099; s100; s101; s095; s096; 355 - s097; s098; s099; s100; s101; s095; s096; s097; s098; s099; s100; s101; 356 - s095; s096; s097; s098; s099; s100; s102; s103; s000; s000; s000; s000; 357 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 358 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 359 - s000; s000; s000; s000|]; 360 - [||]; 361 - [|s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 362 - s104; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 363 - s105; s000; s000; s002; s000; s000; s081; s106; s000; s000; s000; s000; 364 - s000; s000; s000; s107; s000; s000; s000; s108; s000; s109; s000; s000; 365 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 366 - s000; s000; s000; s000|]; 367 - [|s000; s000; s000; s000; s000; s000; s000; s000; s110; s000; s000; s111; 368 - s000; s000; s000; s000; s000; s000; s000; s000; s112; s000; s000; s000; 369 - s000; s000; s113; s000; s000; s114; s000; s000; s115; s116; s117; s118; 370 - s119; s120; s121; s122; s123; s000; s000; s124; s034; s125; s000; s000; 371 - s126; s127; s128; s129; s000; s000; s130; s131; s132; s064; s133; s000; 372 - s134; s000; s000; s000|]; 373 - [|s135; s000; s000; s000; s136; s137; s000; s138; s139; s140; s141; s000; 374 - s000; s000; s000; s000; s142; s000; s143; s000; s144; s145; s146; s000; 375 - s000; s000; s000; s147; s000; s000; s000; s000; s000; s000; s000; s000; 376 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 377 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 378 - s000; s000; s000; s000|]; 379 - [|s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 380 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 381 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 382 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 383 - s148; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 384 - s000; s000; s000; s000|]; 385 - [||]; [||]; [||]; 386 - [|s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s149; 387 - s150; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 388 - s000; s000; s000; s000; s000; s151; s152; s153; s000; s000; s000; s000; 389 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 390 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 391 - s000; s000; s000; s000|]; 392 - [||]; [||]; [||]; [||]; 393 - [|s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 394 - s000; s000; s000; s000; s000; s000; s154; s000; s000; s000; s000; s000; 395 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 396 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 397 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 398 - s000; s000; s000; s000|]; 399 - [|s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 400 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 401 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 402 - s000; s155; s156; s000; s000; s157; s000; s000; s000; s000; s000; s000; 403 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 404 - s000; s000; s000; s000|]; 405 - [|s000; s000; s000; s000; s000; s000; s000; s000; s158; s159; s160; s000; 406 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 407 - s000; s000; s000; s000; s000; s000; s000; s000; s161; s000; s000; s000; 408 - s150; s000; s000; s000; s000; s000; s000; s162; s000; s000; s000; s000; 409 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 410 - s000; s000; s000; s000|]; 411 - [|s000; s000; s000; s163; s000; s164; s000; s000; s000; s000; s000; s000; 412 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 413 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 414 - s000; s000; s000; s165; s000; s000; s000; s000; s000; s000; s000; s166; 415 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 416 - s000; s000; s000; s000|]; 417 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 418 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 419 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 420 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 421 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 422 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 423 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 424 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 425 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 426 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 427 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 428 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 429 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 430 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 431 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 432 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 433 - [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; [||]; 434 - [||]; [||]; [||]; [||]; [||]; 435 - [|s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 436 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 437 - s000; s000; s000; s000; s000; s000; s000; s000; s167; s004; s168; s168; 438 - s004; s004; s004; s169; s168; s168; s168; s168; s168; s168; s168; s168; 439 - s168; s168; s168; s168; s168; s168; s168; s168; s168; s168; s168; s168; 440 - s168; s168; s168; s168|]; 441 - [|s168; s168; s168; s168; s168; s168; s168; s168; s168; s168; s168; s168; 442 - s168; s168; s168; s168; s168; s168; s168; s168; s168; s168; s168; s168; 443 - s168; s168; s168; s168; s168; s168; s168; s168; s000; s000; s000; s000; 444 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 445 - s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; s000; 446 - s000; s000; s000; s000|]|]
-14
notty/src/no-uucp/notty_uucp_data.mli
··· 1 - (* Do not edit. 2 - * 3 - * This module contains select unicode properties extracted from Uucp, 4 - * using `./support/gen_unicode_props.ml`. 5 - * 6 - * Unicode version 13.0.0. 7 - *) 8 - 9 - (* Uucp.Break.tty_width_hint *) 10 - val tty_width_hint: int array * int array * int array 11 - 12 - (* Uucp.Break.Low.grapheme_cluster. *) 13 - val grapheme_cluster_boundary: string array array 14 -
-920
notty/src/notty.ml
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - let invalid_arg fmt = Format.kasprintf invalid_arg fmt 5 - 6 - let (&.) f g x = f (g x) 7 - 8 - let btw (x : int) a b = a <= x && x <= b 9 - let bit n b = b land (1 lsl n) > 0 10 - 11 - let max (a : int) b = if a > b then a else b 12 - let min (a : int) b = if a < b then a else b 13 - 14 - let is_C0 x = (x < 0x20 || x = 0x7f) && x!= 0x09 15 - and is_C1 x = 0x80 <= x && x < 0xa0 16 - let is_ctrl x = is_C0 x || is_C1 x 17 - and is_ascii x = x < 0x80 18 - 19 - let rec concatm z (@) xs = 20 - let rec accum (@) = function 21 - | []|[_] as xs -> xs 22 - | a::b::xs -> (a @ b) :: accum (@) xs in 23 - match xs with [] -> z | [x] -> x | xs -> concatm z (@) (accum (@) xs) 24 - 25 - let rec linspcm z (@) x n f = match n with 26 - | 0 -> z 27 - | 1 -> f x 28 - | _ -> let m = n / 2 in linspcm z (@) x m f @ linspcm z (@) (x + m) (n - m) f 29 - 30 - let memo (type a) ?(hash=Hashtbl.hash) ?(eq=(=)) ~size f = 31 - let module H = Ephemeron.K1.Make 32 - (struct type t = a let (hash, equal) = (hash, eq) end) in 33 - let t = H.create size in fun x -> 34 - try H.find t x with Not_found -> let y = f x in H.add t x y; y 35 - 36 - module Buffer = struct 37 - include Buffer 38 - let buf = Buffer.create 1024 39 - let mkstring f = f buf; let res = contents buf in reset buf; res 40 - let add_decimal b = function 41 - | x when btw x 0 999 -> 42 - let d1 = x / 100 and d2 = (x mod 100) / 10 and d3 = x mod 10 in 43 - if d1 > 0 then 0x30 + d1 |> Char.unsafe_chr |> add_char b; 44 - if (d1 + d2) > 0 then 0x30 + d2 |> Char.unsafe_chr |> add_char b; 45 - 0x30 + d3 |> Char.unsafe_chr |> add_char b 46 - | x -> string_of_int x |> add_string b 47 - let add_chars b c n = for _ = 1 to n do add_char b c done 48 - end 49 - 50 - module String = struct 51 - include String 52 - let sub0cp s i len = if i > 0 || len < length s then sub s i len else s 53 - let of_chars_rev = function 54 - | [] -> "" 55 - | [c] -> String.make 1 c 56 - | cs -> 57 - let n = List.length cs in 58 - let rec go bs i = Bytes.(function 59 - | [] -> unsafe_to_string bs 60 - | x::xs -> unsafe_set bs i x; go bs (pred i) xs 61 - ) in go (Bytes.create n) (n - 1) cs 62 - end 63 - 64 - module Option = struct 65 - 66 - let map f = function Some x -> Some (f x) | _ -> None 67 - let get def = function Some x -> x | _ -> def 68 - let to_list = function Some x -> [x] | _ -> [] 69 - let (>>|) a f = map f a 70 - let (>>=) a f = match a with Some x -> f x | _ -> None 71 - end 72 - 73 - module Text = struct 74 - 75 - let err_ctrl u = invalid_arg "Notty: control char: U+%02X, %S" (Char.code u) 76 - let err_malformed = invalid_arg "Notty: malformed UTF-8: %s, %S" 77 - 78 - type t = 79 - | Ascii of string * int * int 80 - | Utf8 of string * int array * int * int 81 - 82 - let equal t1 t2 = match (t1, t2) with 83 - | (Utf8 (s1, _, i1, n1), Utf8 (s2, _, i2, n2)) 84 - | (Ascii (s1, i1, n1), Ascii (s2, i2, n2)) -> i1 = i2 && n1 = n2 && s1 = s2 85 - | _ -> false 86 - 87 - let width = function Utf8 (_, _, _, w) -> w | Ascii (_, _, w) -> w 88 - 89 - let empty = Ascii ("", 0, 0) 90 - 91 - let is_empty t = width t = 0 92 - 93 - let graphemes ?(should_throw=false) str = 94 - let module Uuseg = Notty_grapheme_cluster in 95 - let seg = Uuseg.create () in 96 - let rec f (is, w as acc) i evt = 97 - match Uuseg.add seg evt with 98 - | `Await | `End -> acc 99 - | `Uchar u -> f (is, w + Notty_uucp.tty_width_hint u) i `Await 100 - | `Boundary -> 101 - let is = match w with 0 -> is | 1 -> i::is | _ -> i::(-1)::is in 102 - f (is, 0) i `Await in 103 - let acc = Uutf.String.fold_utf_8(fun acc i -> function 104 - | `Malformed err -> 105 - if should_throw then 106 - err_malformed err str 107 - else 108 - f acc i (`Uchar (Uchar.of_int 0xffd )) 109 - | `Uchar _ as u -> f acc i u 110 - ) ([0], 0) str in 111 - f acc (String.length str) `End |> fst |> List.rev |> Array.of_list (*XXX*) 112 - 113 - let dead = ' ' 114 - 115 - let to_buffer buf = function 116 - | Ascii (s, off, w) -> Buffer.add_substring buf s off w 117 - | Utf8 (s, ix, off, w) -> 118 - let x1 = match ix.(off) with 119 - | -1 -> Buffer.add_char buf dead; ix.(off + 1) | x -> x 120 - and x2 = ix.(off + w) in 121 - Buffer.add_substring buf s x1 @@ 122 - (if x2 = -1 then ix.(off + w - 1) else x2) - x1; 123 - if x2 = -1 then Buffer.add_char buf dead 124 - 125 - let sub t x w = 126 - let w1 = width t in 127 - if w = 0 || x >= w1 then empty else 128 - let w = min w (w1 - x) in 129 - if w = w1 then t else match t with 130 - Ascii (s, off, _) -> Ascii (s, off + x, w) 131 - | Utf8 (s, ix, off, _) -> Utf8 (s, ix, off + x, w) 132 - 133 - let is_ascii_or_raise_ctrl s = 134 - let (@!) s i = String.unsafe_get s i |> Char.code in 135 - let rec go s acc i n = 136 - if n = 0 then acc else 137 - let x = s @! i in 138 - if is_C0 x then 139 - err_ctrl s.[i] s 140 - else if x = 0xc2 && n > 1 && is_C1 (s @! (i + 1)) then 141 - err_ctrl s.[i + 1] s 142 - else go s (acc && is_ascii x) (i + 1) (n - 1) in 143 - go s true 0 (String.length s) 144 - 145 - let of_ascii s = Ascii (s, 0, String.length s) 146 - and of_unicode s = let x = graphemes s in Utf8 (s, x, 0, Array.length x - 1) 147 - let of_unicode = memo ~eq:String.equal ~size:128 of_unicode 148 - 149 - let of_string = function 150 - | "" -> empty 151 - | s -> if is_ascii_or_raise_ctrl s then of_ascii s else of_unicode s 152 - 153 - let of_uchars ucs = of_string @@ Buffer.mkstring @@ fun buf -> 154 - Array.iter (Buffer.add_utf_8_uchar buf) ucs 155 - 156 - let replicateu w u = 157 - if is_ctrl (Uchar.to_int u) then 158 - err_ctrl (Uchar.unsafe_to_char u) "<repeated character>" 159 - else if w < 1 then empty 160 - else if is_ascii (Uchar.to_int u) then 161 - of_ascii (String.make w (Uchar.unsafe_to_char u)) 162 - else of_unicode @@ Buffer.mkstring @@ fun buf -> 163 - for _ = 1 to w do Buffer.add_utf_8_uchar buf u done 164 - 165 - let replicatec w c = replicateu w (Uchar.of_char c) 166 - end 167 - 168 - module A = struct 169 - 170 - type color = int 171 - type style = int 172 - type t = { fg : color; bg : color; st : style } 173 - 174 - let equal t1 t2 = t1.fg = t2.fg && t1.bg = t2.bg && t1.st = t2.st 175 - let unsafe_color_of_int int= int 176 - let unsafe_style_of_int int= int 177 - 178 - let black = 0x01000000 179 - and red = 0x01000001 180 - and green = 0x01000002 181 - and yellow = 0x01000003 182 - and blue = 0x01000004 183 - and magenta = 0x01000005 184 - and cyan = 0x01000006 185 - and white = 0x01000007 186 - and lightblack = 0x01000008 187 - and lightred = 0x01000009 188 - and lightgreen = 0x0100000a 189 - and lightyellow = 0x0100000b 190 - and lightblue = 0x0100000c 191 - and lightmagenta = 0x0100000d 192 - and lightcyan = 0x0100000e 193 - and lightwhite = 0x0100000f 194 - 195 - let tag c = (c land 0x03000000) lsr 24 196 - 197 - let rgb ~r ~g ~b = 198 - if r < 0 || g < 0 || b < 0 || r > 5 || g > 5 || b > 5 then 199 - invalid_arg "Notty.A.rgb %d %d %d: channel out of range" r g b 200 - else 0x01000000 lor (r * 36 + g * 6 + b + 16) 201 - 202 - let gray level = 203 - if level < 0 || level > 23 then 204 - invalid_arg "Notty.A.gray %d: level out of range" level 205 - else 0x01000000 lor (level + 232) 206 - 207 - let rgb_888 ~r ~g ~b = 208 - if r < 0 || g < 0 || b < 0 || r > 255 || g > 255 || b > 255 then 209 - invalid_arg "Notty.A.rgb_888 %d %d %d: channel out of range" r g b 210 - else 0x02000000 lor ((r lsl 16) lor (g lsl 8) lor b) 211 - 212 - let i x = x land 0xff 213 - and r x = x lsr 16 land 0xff 214 - and g x = x lsr 8 land 0xff 215 - and b x = x land 0xff 216 - 217 - let bold = 1 218 - and italic = 2 219 - and underline = 4 220 - and blink = 8 221 - and reverse = 16 222 - 223 - let empty = { fg = 0; bg = 0; st = 0 } 224 - 225 - let (++) a1 a2 = 226 - if a1 == empty then a2 else if a2 == empty then a1 else 227 - { fg = (match a2.fg with 0 -> a1.fg | x -> x) 228 - ; bg = (match a2.bg with 0 -> a1.bg | x -> x) 229 - ; st = a1.st lor a2.st } 230 - 231 - let fg fg = { empty with fg } 232 - let bg bg = { empty with bg } 233 - let st st = { empty with st } 234 - end 235 - 236 - module I = struct 237 - 238 - type dim = int * int 239 - 240 - type t = 241 - | Empty 242 - | Segment of A.t * Text.t 243 - | Hcompose of (t * t) * dim 244 - | Vcompose of (t * t) * dim 245 - | Zcompose of (t * t) * dim 246 - | Hcrop of (t * int * int) * dim 247 - | Vcrop of (t * int * int) * dim 248 - | Void of dim 249 - 250 - let width = function 251 - | Empty -> 0 252 - | Segment (_, text) -> Text.width text 253 - | Hcompose (_, (w, _)) -> w 254 - | Vcompose (_, (w, _)) -> w 255 - | Zcompose (_, (w, _)) -> w 256 - | Hcrop (_, (w, _)) -> w 257 - | Vcrop (_, (w, _)) -> w 258 - | Void (w, _) -> w [@@inline] 259 - 260 - let height = function 261 - | Empty -> 0 262 - | Segment _ -> 1 263 - | Hcompose (_, (_, h)) -> h 264 - | Vcompose (_, (_, h)) -> h 265 - | Zcompose (_, (_, h)) -> h 266 - | Hcrop (_, (_, h)) -> h 267 - | Vcrop (_, (_, h)) -> h 268 - | Void (_, h) -> h [@@inline] 269 - 270 - let equal t1 t2 = 271 - let rec eq t1 t2 = match (t1, t2) with 272 - | (Empty, Empty) -> true 273 - | (Segment (a1, t1), Segment (a2, t2)) -> 274 - A.equal a1 a2 && Text.equal t1 t2 275 - | (Hcompose ((a, b), _), Hcompose ((c, d), _)) 276 - | (Vcompose ((a, b), _), Vcompose ((c, d), _)) 277 - | (Zcompose ((a, b), _), Zcompose ((c, d), _)) -> eq a c && eq b d 278 - | (Hcrop ((a, i1, n1), _), Hcrop ((b, i2, n2), _)) 279 - | (Vcrop ((a, i1, n1), _), Vcrop ((b, i2, n2), _)) -> 280 - i1 = i2 && n1 = n2 && eq a b 281 - | (Void (a, b), Void (c, d)) -> a = c && b = d 282 - | _ -> false in 283 - width t1 = width t2 && height t1 = height t2 && eq t1 t2 284 - 285 - let empty = Empty 286 - 287 - let (<|>) t1 t2 = match (t1, t2) with 288 - | (_, Empty) -> t1 289 - | (Empty, _) -> t2 290 - | _ -> 291 - let w = width t1 + width t2 292 - and h = max (height t1) (height t2) in 293 - Hcompose ((t1, t2), (w, h)) 294 - 295 - let (<->) t1 t2 = match (t1, t2) with 296 - | (_, Empty) -> t1 297 - | (Empty, _) -> t2 298 - | _ -> 299 - let w = max (width t1) (width t2) 300 - and h = height t1 + height t2 in 301 - Vcompose ((t1, t2), (w, h)) 302 - 303 - let (</>) t1 t2 = match (t1, t2) with 304 - | (_, Empty) -> t1 305 - | (Empty, _) -> t2 306 - | _ -> 307 - let w = max (width t1) (width t2) 308 - and h = max (height t1) (height t2) in 309 - Zcompose ((t1, t2), (w, h)) 310 - 311 - let void w h = 312 - if w < 1 && h < 1 then Empty else Void (max 0 w, max 0 h) 313 - 314 - let lincropinv crop void (++) init fini img = 315 - match (init >= 0, fini >= 0) with 316 - | (true, true) -> crop init fini img 317 - | (true, _ ) -> crop init 0 img ++ void (-fini) 318 - | (_ , true) -> void (-init) ++ crop 0 fini img 319 - | _ -> void (-init) ++ img ++ void (-fini) 320 - 321 - let hcrop = 322 - let ctor left right img = 323 - let h = height img and w = width img - left - right in 324 - if w > 0 then Hcrop ((img, left, right), (w, h)) else void w h 325 - in lincropinv ctor (fun w -> void w 0) (<|>) 326 - 327 - let vcrop = 328 - let ctor top bottom img = 329 - let w = width img and h = height img - top - bottom in 330 - if h > 0 then Vcrop ((img, top, bottom), (w, h)) else void w h 331 - in lincropinv ctor (void 0) (<->) 332 - 333 - let crop ?(l=0) ?(r=0) ?(t=0) ?(b=0) img = 334 - let img = if l <> 0 || r <> 0 then hcrop l r img else img in 335 - if t <> 0 || b <> 0 then vcrop t b img else img 336 - 337 - let hpad left right img = hcrop (-left) (-right) img 338 - 339 - let vpad top bottom img = vcrop (-top) (-bottom) img 340 - 341 - let pad ?(l=0) ?(r=0) ?(t=0) ?(b=0) img = 342 - crop ~l:(-l) ~r:(-r) ~t:(-t) ~b:(-b) img 343 - 344 - let hcat = concatm empty (<|>) 345 - 346 - let vcat = concatm empty (<->) 347 - 348 - let zcat xs = List.fold_right (</>) xs empty 349 - 350 - let text attr tx = 351 - if Text.is_empty tx then void 0 1 else Segment (attr, tx) 352 - 353 - let string attr s = text attr (Text.of_string s) 354 - 355 - let uchars attr a = text attr (Text.of_uchars a) 356 - 357 - let tabulate m n f = 358 - let m = max m 0 and n = max n 0 in 359 - linspcm empty (<->) 0 n (fun y -> linspcm empty (<|>) 0 m (fun x -> f x y)) 360 - 361 - let chars ctor attr c w h = 362 - if w < 1 || h < 1 then void w h else 363 - let line = text attr (ctor w c) in tabulate 1 h (fun _ _ -> line) 364 - 365 - let char = chars Text.replicatec 366 - let uchar = chars Text.replicateu 367 - 368 - let hsnap ?(align=`Middle) w img = 369 - let off = width img - w in match align with 370 - | `Left -> hcrop 0 off img 371 - | `Right -> hcrop off 0 img 372 - | `Middle -> let w1 = off / 2 in hcrop w1 (off - w1) img 373 - 374 - let vsnap ?(align=`Middle) h img = 375 - let off = height img - h in match align with 376 - | `Top -> vcrop 0 off img 377 - | `Bottom -> vcrop off 0 img 378 - | `Middle -> let h1 = off / 2 in vcrop h1 (off - h1) img 379 - 380 - module Fmt = struct 381 - 382 - open Format 383 - 384 - type stag += Attr of A.t 385 - 386 - let push r x = r := x :: !r 387 - let pop r = r := (match !r with _::xs -> xs | _ -> []) 388 - let top_a r = match !r with a::_ -> a | _ -> A.empty 389 - 390 - let create () = 391 - let img, line, attr = ref empty, ref empty, ref [] in 392 - let fmt = formatter_of_out_functions { 393 - out_flush = (fun () -> 394 - img := !img <-> !line; line := empty; attr := []) 395 - ; out_newline = (fun () -> 396 - img := !img <-> !line; line := void 0 1) 397 - ; out_string = (fun s i n -> 398 - line := !line <|> string (top_a attr) String.(sub0cp s i n)) 399 - (* Not entirely clear; either or both could be void: *) 400 - ; out_spaces = (fun w -> line := !line <|> char (top_a attr) ' ' w 1) 401 - ; out_indent = (fun w -> line := !line <|> char (top_a attr) ' ' w 1) 402 - } in 403 - pp_set_formatter_stag_functions fmt { 404 - (pp_get_formatter_stag_functions fmt ()) with 405 - mark_open_stag = 406 - (function Attr a -> push attr A.(top_a attr ++ a); "" | _ -> "") 407 - ; mark_close_stag = (fun _ -> pop attr; "") }; 408 - pp_set_mark_tags fmt true; 409 - fmt, fun () -> let i = !img in img := empty; line := empty; attr := []; i 410 - 411 - let ppf, reset = create () 412 - 413 - let kstrf ?(attr = A.empty) ?(w = 1000000) k format = 414 - let m = ref 0 in 415 - let f1 _ () = 416 - m := pp_get_margin ppf (); 417 - pp_set_margin ppf w; 418 - pp_open_stag ppf (Attr attr) 419 - and k _ = 420 - pp_print_flush ppf (); 421 - pp_set_margin ppf !m; 422 - reset () |> k 423 - in kfprintf k ppf ("%a" ^^ format) f1 () 424 - 425 - let strf ?attr ?w format = kstrf ?attr ?w (fun i -> i) format 426 - 427 - let attr attr f fmt x = 428 - pp_open_stag fmt (Attr attr); f fmt x; pp_close_stag fmt () 429 - end 430 - 431 - let kstrf, strf, pp_attr = Fmt.(kstrf, strf, attr) 432 - end 433 - 434 - module Operation = struct 435 - 436 - type t = 437 - End 438 - | Skip of int * t 439 - | Text of A.t * Text.t * t 440 - 441 - let skip n k = if n = 0 then k else match k with 442 - End -> End 443 - | Skip (m, k) -> Skip (m + n, k) 444 - | _ -> Skip (n, k) [@@inline] 445 - 446 - let rec scan x w row i k = 447 - let open I in match i with 448 - 449 - | Empty | Void _ -> skip w k 450 - 451 - | Segment _ when row > 0 -> skip w k 452 - | Segment (attr, text) -> 453 - let t = Text.sub text x w in 454 - let w1 = Text.width t in 455 - let p = if w > w1 then skip (w - w1) k else k in 456 - if w1 > 0 then Text (attr, t, p) else p 457 - 458 - | Hcompose ((i1, i2), _) -> 459 - let w1 = width i1 460 - and w2 = width i2 in 461 - if x >= w1 + w2 then skip w k else 462 - if x >= w1 then scan (x - w1) w row i2 k else 463 - if x + w <= w1 then scan x w row i1 k else 464 - scan x (w1 - x) row i1 @@ scan 0 (w - w1 + x) row i2 @@ k 465 - 466 - | Vcompose ((i1, i2), _) -> 467 - let h1 = height i1 468 - and h2 = height i2 in 469 - if row >= h1 + h2 then skip w k else 470 - if row >= h1 then scan x w (row - h1) i2 k else scan x w row i1 k 471 - 472 - | Zcompose ((i1, i2), _) -> 473 - let rec stitch x w row i = function 474 - | End -> scan x w row i End 475 - | Text (a, t, ops) as opss -> 476 - let w1 = Text.width t in 477 - if w1 >= w then opss else 478 - Text (a, t, stitch (x + w1) (w - w1) row i ops) 479 - | Skip (w1, ops) -> 480 - scan x w1 row i @@ 481 - if w1 >= w then ops else stitch (x + w1) (w - w1) row i ops 482 - in stitch x w row i2 @@ scan x w row i1 @@ k 483 - 484 - | Hcrop ((i, left, _), (w1, _)) -> 485 - if x >= w1 then skip w k else 486 - if x + w <= w1 then scan (x + left) w row i k else 487 - scan (x + left) (w1 - x) row i @@ skip (w - w1 + x) k 488 - 489 - | Vcrop ((i, top, _), (_, h1)) -> 490 - if row < h1 then scan x w (top + row) i k else skip w k 491 - 492 - let of_image (x, y) (w, h) i = 493 - List.init h (fun off -> scan x (x + w) (y + off) i End) 494 - end 495 - 496 - module Cap = struct 497 - 498 - type op = Buffer.t -> unit 499 - 500 - let (&) op1 op2 buf = op1 buf; op2 buf 501 - 502 - type t = { 503 - skip : int -> op 504 - ; sgr : A.t -> op 505 - ; newline : op 506 - ; clreol : op 507 - ; cursvis : bool -> op 508 - ; cursat : int -> int -> op 509 - ; cubcuf : int -> op 510 - ; cuucud : int -> op 511 - ; cr : op 512 - ; altscr : bool -> op 513 - ; mouse : bool -> op 514 - ; bpaste : bool -> op 515 - } 516 - 517 - let ((<|), (<.), (<!)) = Buffer.(add_string, add_char, add_decimal) 518 - 519 - let sts = [ ";1"; ";3"; ";4"; ";5"; ";7" ] 520 - 521 - let sgr { A.fg; bg; st } buf = 522 - buf <| "\x1b[0"; 523 - let rgb888 buf x = 524 - buf <! A.r x; buf <. ';'; buf <! A.g x; buf <. ';'; buf <! A.b x in 525 - ( match A.tag fg with 526 - 0 -> () 527 - | 1 -> let c = A.i fg in 528 - if c < 8 then ( buf <. ';'; buf <! (c + 30) ) 529 - else if c < 16 then ( buf <. ';'; buf <! (c + 82) ) 530 - else ( buf <| ";38;5;"; buf <! c ) 531 - | _ -> buf <| ";38;2;"; rgb888 buf fg ); 532 - ( match A.tag bg with 533 - 0 -> () 534 - | 1 -> let c = A.i bg in 535 - if c < 8 then ( buf <. ';'; buf <! (c + 40) ) 536 - else if c < 16 then ( buf <. ';'; buf <! (c + 92) ) 537 - else ( buf <| ";48;5;"; buf <! c ) 538 - | _ -> buf <| ";48;2;"; rgb888 buf bg ); 539 - if st <> 0 then 540 - ( let rec go f xs = match (f, xs) with 541 - | (0, _) | (_, []) -> () 542 - | (_, x::xs) -> if f land 1 > 0 then buf <| x; go (f lsr 1) xs in 543 - go st sts ); 544 - buf <. 'm' 545 - 546 - let ansi = { 547 - skip = (fun n b -> b <| "\x1b[0m"; Buffer.add_chars b ' ' n) 548 - ; newline = (fun b -> b <| "\x1bE") 549 - ; altscr = (fun x b -> b <| if x then "\x1b[?1049h" else "\x1b[?1049l") 550 - ; cursat = (fun w h b -> b <| "\x1b["; b <! h; b <. ';'; b <! w; b <. 'H') 551 - ; cubcuf = (fun x b -> b <| "\x1b["; b <! abs x; b <. if x < 0 then 'D' else 'C') 552 - ; cuucud = (fun y b -> b <| "\x1b["; b <! abs y; b <. if y < 0 then 'A' else 'B') 553 - ; cr = (fun b -> b <| "\x1b[1G") 554 - ; clreol = (fun b -> b <| "\x1b[K") 555 - ; cursvis = (fun x b -> b <| if x then "\x1b[34h\x1b[?25h" else "\x1b[?25l") 556 - ; mouse = (fun x b -> b <| if x then "\x1b[?1000;1002;1005;1015;1006h" 557 - else "\x1b[?1000;1002;1005;1015;1006l") 558 - ; bpaste = (fun x b -> b <| if x then "\x1b[?2004h" else "\x1b[?2004l") 559 - ; sgr } 560 - 561 - let no0 _ = () 562 - and no1 _ _ = () 563 - and no2 _ _ _ = () 564 - 565 - let dumb = { 566 - skip = (fun n b -> Buffer.add_chars b ' ' n) 567 - ; newline = (fun b -> b <| "\n") 568 - ; altscr = no1 569 - ; cursat = no2 570 - ; cubcuf = no1 571 - ; cuucud = no1 572 - ; cr = no0 573 - ; clreol = no0 574 - ; cursvis = no1 575 - ; sgr = no1 576 - ; mouse = no1 577 - ; bpaste = no1 578 - } 579 - 580 - let erase cap buf = cap.sgr A.empty buf; cap.clreol buf (* KEEP ETA-LONG. *) 581 - let cursat0 cap w h = cap.cursat (max w 0 + 1) (max h 0 + 1) 582 - end 583 - 584 - module Render = struct 585 - 586 - open Cap 587 - open Operation 588 - 589 - let skip_op cap buf n = cap.skip n buf 590 - let text_op cap buf a x = cap.sgr a buf; Text.to_buffer buf x 591 - 592 - let rec line cap buf = function 593 - End -> erase cap buf 594 - | Skip (n, End) -> erase cap buf; skip_op cap buf n 595 - | Text (a, x, End) -> erase cap buf; text_op cap buf a x 596 - | Skip (n, ops) -> skip_op cap buf n; line cap buf ops 597 - | Text (a, x, ops) -> text_op cap buf a x; line cap buf ops 598 - 599 - let rec lines cap buf = function 600 - [] -> () 601 - | [ln] -> line cap buf ln; cap.sgr A.empty buf 602 - | ln::lns -> line cap buf ln; cap.newline buf; lines cap buf lns 603 - 604 - let to_buffer buf cap off dim img = 605 - Operation.of_image off dim img |> lines cap buf 606 - 607 - let pp cap ppf img = 608 - let open Format in 609 - let buf = Buffer.create (I.width img * 2) in 610 - let h, w = I.(height img, width img |> min (pp_get_margin ppf ())) in 611 - let img = I.(img </> vpad (h - 1) 0 (char A.empty ' ' w 1)) in 612 - pp_open_vbox ppf 0; 613 - for y = 0 to h - 1 do 614 - Buffer.clear buf; to_buffer buf cap (0, y) (w, 1) img; 615 - pp_print_as ppf w (Buffer.contents buf); 616 - if y < h - 1 then pp_print_cut ppf () 617 - done; 618 - pp_close_box ppf () 619 - 620 - let pp_image = pp Cap.ansi 621 - let pp_attr ppf a = 622 - let string_ = I.string A.empty in 623 - pp_image ppf I.(string_ "<" <|> string a "ATTR" <|> string_ ">") 624 - end 625 - 626 - module Unescape = struct 627 - 628 - type special = [ 629 - `Escape 630 - | `Enter 631 - | `Tab 632 - | `Backspace 633 - | `Insert 634 - | `Delete 635 - | `Home | `End 636 - | `Arrow of [ `Up | `Down | `Left | `Right ] 637 - | `Page of [ `Up | `Down ] 638 - | `Function of int 639 - ] 640 - 641 - type button = [ `Left | `Middle | `Right | `Scroll of [ `Up | `Down ] ] 642 - 643 - type mods = [ `Meta | `Ctrl | `Shift ] list 644 - 645 - type key = [ special | `Uchar of Uchar.t | `ASCII of char ] * mods 646 - 647 - type mouse = [ `Press of button | `Drag | `Release ] * (int * int) * mods 648 - 649 - type paste = [ `Start | `End ] 650 - 651 - type event = [ `Key of key | `Mouse of mouse | `Paste of paste ] 652 - 653 - type esc = 654 - C0 of char 655 - | C1 of char 656 - | SS2 of char 657 - | CSI of string * int list * char 658 - | Esc_M of int * int * int 659 - | Uchar of Uchar.t 660 - 661 - let uchar = function `Uchar u -> u | `ASCII c -> Uchar.of_char c 662 - 663 - let csi = 664 - let open Option in 665 - let rec priv acc = function 666 - | x::xs when btw x 0x3c 0x3f -> priv (Char.unsafe_chr x::acc) xs 667 - | xs -> param (String.of_chars_rev acc) None [] xs 668 - and param prv p ps = function 669 - | x::xs when btw x 0x30 0x39 -> param prv (Some (get 0 p * 10 + x - 0x30)) ps xs 670 - | 0x3b::xs -> param prv None (get 0 p :: ps) xs 671 - | xs -> code prv (List.rev (to_list p @ ps)) xs 672 - and code prv ps = function (* Conflate two classes because urxvt... *) 673 - | x::xs when btw x 0x20 0x2f || btw x 0x40 0x7e -> 674 - Some (CSI (prv, ps, (Char.chr x)), xs) 675 - | _ -> None in 676 - priv [] 677 - 678 - let rec demux = 679 - let chr = Char.chr in function 680 - | 0x1b::0x5b::0x4d::a::b::c::xs -> Esc_M (a, b, c) :: demux xs 681 - | 0x1b::0x5b::xs | 0x9b::xs -> 682 - let (r, xs) = csi xs |> Option.get (C1 '\x5b', xs) in r :: demux xs 683 - | 0x1b::0x4f::x::xs | 0x8f::x::xs 684 - when is_ascii x -> SS2 (chr x) :: demux xs 685 - | 0x1b::x::xs when is_C1 (x + 0x40) -> C1 (chr x) :: demux xs 686 - | x::xs when is_C1 x -> C1 (chr (x - 0x40)) :: demux xs 687 - | x::xs when is_C0 x -> C0 (chr x) :: demux xs 688 - | x::xs -> Uchar (Uchar.unsafe_of_int x) :: demux xs 689 - | [] -> [] 690 - 691 - let xtrm_mod_flags = function 692 - | 2 -> Some [`Shift] 693 - | 3 -> Some [`Meta] 694 - | 4 -> Some [`Shift; `Meta] 695 - | 5 -> Some [`Ctrl] 696 - | 6 -> Some [`Shift; `Ctrl] 697 - | 7 -> Some [`Meta; `Ctrl] 698 - | 8 -> Some [`Shift; `Meta; `Ctrl] 699 - | _ -> None 700 - 701 - let mods_xtrm = function 702 - | [1;p] -> xtrm_mod_flags p 703 - | [] -> Some [] 704 - | _ -> None 705 - 706 - let mods_rxvt = function 707 - | '~' -> Some [] 708 - | '$' -> Some [`Shift] 709 - | '^' -> Some [`Ctrl] 710 - | '@' -> Some [`Ctrl; `Shift] 711 - | _ -> None 712 - 713 - let mods_common ps code = match (ps, code) with 714 - | ([], '~') -> Some [] 715 - | ([], c) -> mods_rxvt c 716 - | ([p], '~') -> xtrm_mod_flags p 717 - | _ -> None 718 - 719 - let mouse_p p = 720 - let btn = match p land 3 with 721 - | 0 when bit 6 p -> `Scroll `Up 722 - | 0 -> `Left 723 - | 1 when bit 6 p -> `Scroll `Down 724 - | 1 -> `Middle 725 - | 2 when bit 6 p -> `ALL (* `Scroll `Left *) 726 - | 2 -> `Right 727 - | 3 when bit 6 p -> `ALL (* `Scroll `Right *) 728 - | _ -> `ALL 729 - and drag = bit 5 p 730 - and mods = 731 - (if bit 3 p then [`Meta] else []) @ 732 - (if bit 4 p then [`Ctrl] else []) 733 - in (btn, drag, mods) 734 - 735 - let key k mods = Some (`Key (k, mods)) 736 - 737 - let event_of_control_code = 738 - let open Option in function 739 - | Uchar u when Uchar.to_int u |> is_ascii -> 740 - Some (`Key (`ASCII (Uchar.unsafe_to_char u), [])) 741 - | Uchar u -> Some (`Key (`Uchar u, [])) 742 - 743 - | C0 '\x1b' -> key `Escape [] 744 - | C0 ('\b'|'\x7f') -> key `Backspace [] 745 - | C0 '\n' -> key `Enter [] 746 - | C0 '\t' -> key `Tab [] 747 - 748 - | C0 x -> key (`ASCII Char.(code x + 0x40 |> unsafe_chr)) [`Ctrl] 749 - | C1 x -> key (`ASCII x) [`Meta] 750 - 751 - | CSI ("",[],'Z') -> key `Tab [`Shift] 752 - 753 - | CSI ("",p,'A') -> mods_xtrm p >>= key (`Arrow `Up) 754 - | CSI ("",p,'B') -> mods_xtrm p >>= key (`Arrow `Down) 755 - | CSI ("",p,'C') -> mods_xtrm p >>= key (`Arrow `Right) 756 - | CSI ("",p,'D') -> mods_xtrm p >>= key (`Arrow `Left) 757 - 758 - | CSI ("",[],'a') -> key (`Arrow `Up) [`Shift] 759 - | CSI ("",[],'b') -> key (`Arrow `Down) [`Shift] 760 - | CSI ("",[],'c') -> key (`Arrow `Right) [`Shift] 761 - | CSI ("",[],'d') -> key (`Arrow `Left) [`Shift] 762 - | SS2 ('A'|'a') -> key (`Arrow `Up) [`Ctrl] 763 - | SS2 ('B'|'b') -> key (`Arrow `Down) [`Ctrl] 764 - | SS2 ('C'|'c') -> key (`Arrow `Right) [`Ctrl] 765 - | SS2 ('D'|'d') -> key (`Arrow `Left) [`Ctrl] 766 - 767 - | CSI ("",5::p,c) -> mods_common p c >>= key (`Page `Up) 768 - | CSI ("",6::p,c) -> mods_common p c >>= key (`Page `Down) 769 - 770 - | CSI ("",2::p,c) -> mods_common p c >>= key `Insert 771 - | CSI ("",3::p,c) -> mods_common p c >>= key `Delete 772 - 773 - | CSI ("",[4],'h') -> key `Insert [] 774 - | CSI ("",[],'L') -> key `Insert [`Ctrl] 775 - | CSI ("",[],'P') -> key `Delete [] 776 - | CSI ("",[],'M') -> key `Delete [`Ctrl] 777 - 778 - | CSI ("",p,'H') -> mods_xtrm p >>= key `Home 779 - | CSI ("",[7|1],c) -> mods_rxvt c >>= key `Home 780 - 781 - | CSI ("",p,'F') -> mods_xtrm p >>= key `End 782 - | CSI ("",[8|4],c) -> mods_rxvt c >>= key `End 783 - | CSI ("",[],'J') -> key `End [`Ctrl] 784 - 785 - | SS2 ('P'..'S' as c) -> key (`Function (Char.code c - 0x4f)) [] 786 - 787 - | CSI ("",p,('P'..'S' as c)) -> 788 - mods_xtrm p >>= key (`Function (Char.code c - 0x4f)) 789 - 790 - | CSI ("",k::p,c) when btw k 11 15 || btw k 17 21 || btw k 23 26 -> 791 - mods_common p c >>= key (`Function ((k - 10) - (k - 10) / 6)) 792 - 793 - | CSI ("<",[p;x;y],('M'|'m' as c)) -> 794 - let (btn, drag, mods) = mouse_p p in 795 - ( match (c, btn, drag) with 796 - | ('M', (#button as b), false) -> Some (`Press b) 797 - | ('M', #button, true) -> Some `Drag 798 - | ('m', #button, false) -> Some `Release 799 - (* | ('M', `ALL , true) -> Some `Move *) 800 - | _ -> None 801 - ) >>| fun e -> `Mouse (e, (x - 1, y - 1), mods) 802 - 803 - | CSI ("",[p;x;y],'M') | Esc_M (p,x,y) as evt -> 804 - let (x, y) = match evt with Esc_M _ -> x - 32, y - 32 | _ -> x, y 805 - and (btn, drag, mods) = mouse_p (p - 32) in 806 - ( match (btn, drag) with 807 - | (#button as b, false) -> Some (`Press b) 808 - | (#button , true ) -> Some `Drag 809 - | (`ALL , false) -> Some `Release 810 - (* | (`ALL , true) -> Some `Move *) 811 - | _ -> None 812 - ) >>| fun e -> `Mouse (e, (x - 1, y - 1), mods) 813 - 814 - | CSI ("",[200],'~') -> Some (`Paste `Start) 815 - | CSI ("",[201],'~') -> Some (`Paste `End) 816 - 817 - | CSI _ | SS2 _ -> None 818 - 819 - let rec events = function 820 - | C0 '\x1b' :: cc :: ccs -> 821 - ( match event_of_control_code cc with 822 - | Some (`Key (k, mods)) -> `Key (k, `Meta :: mods) :: events ccs 823 - | Some _ -> `Key (`Escape, []) :: events (cc::ccs) 824 - | None -> events ccs ) 825 - | cc::ccs -> (event_of_control_code cc |> Option.to_list) @ events ccs 826 - | [] -> [] 827 - 828 - let decode = events &. demux &. List.map Uchar.to_int 829 - 830 - type t = (event list * bool) ref 831 - 832 - let create () = ref ([], false) 833 - 834 - let next t = match !t with 835 - | (#event as e::es, eof) -> t := (es, eof) ; e 836 - | ([], false) -> `Await 837 - | _ -> `End 838 - 839 - let list_of_utf8 buf i l = 840 - let f cs _ = function `Uchar c -> c::cs | _ -> cs in 841 - String.sub0cp (Bytes.unsafe_to_string buf) i l 842 - |> Uutf.String.fold_utf_8 f [] |> List.rev 843 - 844 - let input t buf i l = t := match !t with 845 - | (es, false) when l > 0 -> (es @ (list_of_utf8 buf i l |> decode), false) 846 - | (es, _) -> (es, true) 847 - 848 - let pending t = match !t with ([], false) -> false | _ -> true 849 - end 850 - 851 - module Tmachine = struct 852 - 853 - open Cap 854 - (* XXX This is sad. This should be a composable, stateless transducer. *) 855 - 856 - type t = { 857 - cap : Cap.t 858 - ; mutable write : Buffer.t -> unit 859 - ; mutable curs : (int * int) option 860 - ; mutable dim : (int * int) 861 - ; mutable image : I.t 862 - ; mutable dead : bool 863 - } 864 - 865 - let emit t op = 866 - if t.dead then 867 - invalid_arg "Notty: use of released terminal" 868 - else t.write <- t.write & op 869 - 870 - let cursor cap = function 871 - | None -> cap.cursvis false 872 - | Some (w, h) -> cap.cursvis true & cursat0 cap w h 873 - 874 - let create ~mouse ~bpaste cap = { 875 - cap 876 - ; curs = None 877 - ; dim = (0, 0) 878 - ; image = I.empty 879 - ; dead = false 880 - ; write = 881 - cap.altscr true & cursor cap None & cap.mouse mouse & cap.bpaste bpaste 882 - } 883 - 884 - let release t = 885 - if t.dead then false else 886 - ( emit t ( t.cap.altscr false & t.cap.cursvis true & 887 - t.cap.mouse false & t.cap.bpaste false ); 888 - t.dead <- true; true ) 889 - 890 - let output t buf = t.write buf; t.write <- ignore 891 - 892 - let refresh ({ dim; image; _ } as t) = 893 - emit t ( cursor t.cap None & cursat0 t.cap 0 0 & 894 - (fun buf -> Render.to_buffer buf t.cap (0, 0) dim image) & 895 - cursor t.cap t.curs ) 896 - 897 - let set_size t dim = t.dim <- dim 898 - let image t image = t.image <- image; refresh t 899 - let cursor t curs = t.curs <- curs; emit t (cursor t.cap curs) 900 - 901 - let size t = t.dim 902 - let dead t = t.dead 903 - end 904 - 905 - module Direct = struct 906 - let show_cursor buf cap x = cap.Cap.cursvis x buf 907 - and move_cursor buf cap cmd = match cmd with 908 - | `To (w, h) -> Cap.cursat0 cap w h buf 909 - | `Home -> cap.Cap.cr buf 910 - | `By (x, y) -> 911 - Cap.(if x <> 0 then cap.cubcuf x buf; if y <> 0 then cap.cuucud y buf) 912 - end 913 - 914 - type attr = A.t 915 - type image = I.t 916 - 917 - module Infix = struct 918 - let ((<->), (<|>), (</>)) = I.((<->), (<|>), (</>)) 919 - let (++) = A.(++) 920 - end
-967
notty/src/notty.mli
··· 1 - (* Copyright (c) 2016-2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (** Declaring terminals. 5 - 6 - Notty is a terminal library that revolves around construction and 7 - composition of displayable images. 8 - 9 - This module provides the core {{!I}[image]} abstraction, standalone 10 - {{!Render}rendering}, and escape sequence {{!Unescape}parsing}. It does not 11 - depend on any platform code, and does not interact with the environment. 12 - Input and output are provided by {!Notty_unix} and {!Notty_lwt}. 13 - 14 - Consult the {{!basics}basics}, {{!examples}examples} and 15 - {{!limitations}limitations}. 16 - 17 - {e %%VERSION%% — {{:%%PKG_HOMEPAGE%% }homepage}} *) 18 - 19 - (** {1 Interface} *) 20 - 21 - type attr 22 - (** Visual characteristics of displayed text. *) 23 - 24 - type image 25 - (** Rectangles of styled characters. *) 26 - 27 - (** [A] is for attribute. 28 - 29 - Construction and composition of styling characteristics of text. 30 - 31 - Consult the {{!basics}basics} for an overview. *) 32 - module A : sig 33 - 34 - (** {1 Colors} *) 35 - 36 - type color 37 - (** An ineffable quality of light. 38 - 39 - There are three kinds of colors: 40 - {ul 41 - {- {e Core 16 colors.} 42 - 43 - ANSI defines 8 color {e names}, with the actual display colors 44 - considered an implementation detail. Historically, this palette was 45 - extended with their light (sometimes {e bright} or {e high-intensity}) 46 - counterparts. Their presentation is undefined too, but typically 47 - produces a brighter shade. These colors - often called the {e ANSI 48 - colors} - tend to be unpredictable, but ubiquitously supported. 49 - 50 - } 51 - {- {e Extended 256-color palette.} 52 - 53 - This common feature extends the palette by further 240 colors. They 54 - come in two groups: 55 - 56 - {ul 57 - {- The {e color cube}, a 6*6*6 approximation to the usual 24-bit RGB 58 - color cube; and} 59 - {- the {e grayscale ramp}, containing (merely) 24 shades of gray.}} 60 - 61 - XTerm was the first to support this extension. Many terminals have 62 - since cloned it, so the support is wide, but not universal. 63 - 64 - As the extended colors are still palette-driven they do not have a 65 - fixed presentation, and the presentation can be changed in some 66 - terminals. Default palette tends to match {{: 67 - https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg} 68 - XTerm's}. 69 - 70 - } 71 - {- {e True color} 72 - 73 - A recently established convention allows directly sending 24-bit colors 74 - to the terminal. This has been adopted by a growing minority of 75 - terminals. A reasonably up-to-date status document maintained by the 76 - community can be found {{:https://gist.github.com/XVilka/8346728}here}.}} 77 - 78 - Some of the technical and historical background can be found in {{: 79 - http://invisible-island.net/xterm/xterm.faq.html#problems_colors} 80 - XTerm's FAQ}. 81 - 82 - {b Note} No attempt is made to remap colors depending on the terminal. 83 - Terminals might ignore, remap, or completely misinterpret unsupported 84 - colors. *) 85 - 86 - (** {2:corecolors Core 16 colors} 87 - 88 - The first 8 have their standard ANSI names. *) 89 - val unsafe_color_of_int:int->color 90 - 91 - val black : color 92 - val red : color 93 - val green : color 94 - val yellow : color 95 - val blue : color 96 - val magenta : color 97 - val cyan : color 98 - val white : color 99 - val lightblack : color 100 - val lightred : color 101 - val lightgreen : color 102 - val lightyellow : color 103 - val lightblue : color 104 - val lightmagenta : color 105 - val lightcyan : color 106 - val lightwhite : color 107 - 108 - (** {2 Extended 256-color palette} *) 109 - 110 - val rgb : r:int -> g:int -> b:int -> color 111 - (** [rgb ~r:red ~g:green ~b:blue] is an extended-palette color from the color cube. 112 - 113 - All three channels must be in the range [0 - 5]. XTerm default palette maps 114 - this to [0x00], [0x5f], [0x87], [0xaf], [0xd7], and [0xff] independently 115 - per channel. 116 - 117 - @raise Invalid_argument if a channel is outside the range. *) 118 - 119 - val gray : int -> color 120 - (** [gray level] is an extended-palette color from the grayscale ramp. 121 - 122 - [level] must be in the range [0 - 23]. XTerm default palette maps this to 123 - [8 + level * 10] on all three channels. 124 - 125 - @raise Invalid_argument if the [level] is outside the range. *) 126 - 127 - (** {2 True Color} *) 128 - 129 - val rgb_888 : r:int -> g:int -> b:int -> color 130 - (** [rgb_888 ~r:red ~g:green ~b:blue] is a 24-bit color. 131 - 132 - All three channels must be in the range [0 - 255]. 133 - 134 - @raise Invalid_argument if a channel is outside the range. *) 135 - 136 - (** {1 Text styles} *) 137 - 138 - type style 139 - (** Additional text properties. *) 140 - 141 - val unsafe_style_of_int: int-> style 142 - 143 - val bold : style 144 - val italic : style 145 - val underline : style 146 - val blink : style 147 - val reverse : style 148 - 149 - (** {1 Attribute construction and composition} *) 150 - 151 - type t = attr 152 - 153 - val equal : t -> t -> bool 154 - 155 - val empty : attr 156 - (** [empty] is the attribute with the default foreground and background color 157 - and empty style set. *) 158 - 159 - val (++) : attr -> attr -> attr 160 - (** [a1 ++ a2] is the concatenation of [a1] and [a2], the attribute that has 161 - [a2]'s foreground (resp. background), unless {e unset}, in which case it 162 - is [a1]'s, and the union of both style sets. 163 - 164 - [++] is left-associative, and forms a monoid with [empty]. *) 165 - 166 - val fg : color -> attr 167 - (** [fg c] is [empty] with foreground [c]. *) 168 - 169 - val bg : color -> attr 170 - (** [bg c] is [empty] with background [c]. *) 171 - 172 - val st : style -> attr 173 - (** [st s] is [empty] with style [s]. *) 174 - end 175 - 176 - (** [I] is for image. 177 - 178 - Construction and composition of images. 179 - 180 - Consult the {{!basics}basics} for an overview. *) 181 - module I : sig 182 - 183 - type t = image 184 - 185 - val height : image -> int 186 - val width : image -> int 187 - 188 - val equal : t -> t -> bool 189 - (** [equal t1 t2] is [true] iff [t1] and [t2] are constructed by the same term. 190 - 191 - {b Note} This is a weak form of equality. Images that are not [equal] 192 - could still render the same. *) 193 - 194 - (** {1:imgprims Primitives} *) 195 - 196 - val empty : image 197 - (** [empty] is a zero-sized image. *) 198 - 199 - val string : attr -> string -> image 200 - (** [string attr s] is an image containing text [s], styled with [attr]. 201 - 202 - @raise Invalid_argument if [string] is not a valid UTF-8 sequence, or 203 - contains {{!ctrls}control characters}. *) 204 - 205 - val uchars : attr -> Uchar.t array -> image 206 - (** [uchars attr us] is an image containing text [us], styled with [attr]. 207 - 208 - @raise Invalid_argument if [us] contains {{!ctrls}control characters}. *) 209 - 210 - val char : attr -> char -> int -> int -> image 211 - (** [char attr c w h] is a [w * h] grid of [c]. 212 - 213 - @raise Invalid_argument if [c] is a {{!ctrls}control character}. *) 214 - 215 - val uchar : attr -> Uchar.t -> int -> int -> image 216 - (** [uchar attr u w h] is a [w * h] grid of [u]. 217 - 218 - @raise Invalid_argument if [u] is a {{!ctrls}control character}. *) 219 - 220 - val void : int -> int -> image 221 - (** [void w h] is a [w * h] rectangle of transparent cells. 222 - 223 - [void] is magical: it has geometry, but no displayable content. This is 224 - different, for example, from the space character [U+0020], which renders 225 - as a cell filled with the background color. This means that [void] 226 - interacts specially with {{!(</>)}overlays}. 227 - 228 - [void 0 0 = empty]. 229 - [void] with only one dimension [0] acts as a spacing element in the other 230 - dimension. Negative size is treated as [0]. *) 231 - 232 - (** {1:imgcomp Image composition} 233 - 234 - Three basic composition modes allow construction of more complex images 235 - from simpler ones. 236 - 237 - Composition operators are left-associative and form a monoid with [void]. 238 - *) 239 - 240 - val (<|>) : image -> image -> image 241 - (** [i1 <|> i2] is the horizontal combination of [i1] and [i2]. 242 - 243 - [width (i1 <|> i2) = width i1 + width i2] 244 - [height (i1 <|> i2) = max (height i1) (height i2)] 245 - 246 - Images are top-aligned. The missing region is implicitly filled with 247 - {{!void}[void]}. 248 - 249 - {v 250 - [x] <|> [y] = [xy] 251 - [y] [.y] 252 - v} 253 - 254 - where [.] denotes {{!void}[void]}. *) 255 - 256 - val (<->) : image -> image -> image 257 - (** [i1 <-> i2] is the vertical combination of [i1] and [i2]. 258 - 259 - [width (i1 <-> i2) = max (width i1) (width i2)] 260 - [height (i1 <-> i2) = height i1 + height i2] 261 - 262 - Images are left-aligned. The missing region is implicitly filled with 263 - {{!void}[void]}. 264 - 265 - {v 266 - [xx] <-> [y] = [xx] 267 - [y.] 268 - v} 269 - *) 270 - 271 - val (</>) : image -> image -> image 272 - (** [i1 </> i2] is [i1] overlaid over [i2]. 273 - 274 - [width (i1 </> i2) = max (width i1) (width i2)] 275 - [height (i1 </> i2) = max (height i1) (height i2)] 276 - 277 - Images are top-left-aligned. In the region of their overlap, only the 278 - {{!void}[void]} cells of [i1] show fragments of [i2]. 279 - 280 - {v 281 - [x.x] </> [yyyy] = [xyxy] 282 - v} 283 - *) 284 - 285 - (** {1:imgcrop Cropping and padding} *) 286 - 287 - val hcrop : int -> int -> image -> image 288 - (** [hcrop left right i] is [i] with [left] leftmost, and [right] 289 - rightmost columns missing. If [left + right >= width i] the result is 290 - [empty]. 291 - 292 - If either [left] or [right] is negative, instead of being cropped, the 293 - image is padded on that side. 294 - 295 - For example: 296 - {ul 297 - {- [hcrop 0 1 [abc]] = [[ab]]} 298 - {- [hcrop 1 1 [abc]] = [[b]]} 299 - {- [hcrop (-1) 1 [abc]] = [void 1 1 <|> hcrop 0 1 [abc]] = [[.ab]]} 300 - {- [hcrop 2 2 [abc]] = [empty]}} *) 301 - 302 - val vcrop : int -> int -> image -> image 303 - (** [vcrop top bottom i] is the vertical analogue to {{!hcrop}[hcrop]}. *) 304 - 305 - val crop : ?l:int -> ?r:int -> ?t:int -> ?b:int -> image -> image 306 - (** [crop ~l:left ~r:right ~t:top ~b:bottom i] is 307 - [vcrop left right (hcrop top bottom) i]. 308 - 309 - Missing arguments default to [0]. *) 310 - 311 - val hpad : int -> int -> image -> image 312 - (** {{!hcrop}[hcrop]} with margins negated. *) 313 - 314 - val vpad : int -> int -> image -> image 315 - (** {{!vcrop}[vcrop]} with margins negated. *) 316 - 317 - val pad : ?l:int -> ?r:int -> ?t:int -> ?b:int -> image -> image 318 - (** {{!crop}[crop]} with margins negated. *) 319 - 320 - 321 - (** {1 Additional combinators} *) 322 - 323 - val hcat : image list -> image 324 - (** [hcat xs] horizontally concatenates [xs]. See {{!(<|>)}beside}. *) 325 - 326 - val vcat : image list -> image 327 - (** [vcat xs] vertically concatenates [xs]. See {{!(<->)}above}. *) 328 - 329 - val zcat : image list -> image 330 - (** [zcat xs] overlays [xs]. See {{!(</>)}over}. *) 331 - 332 - val tabulate : int -> int -> (int -> int -> image) -> image 333 - (** [tabulate m n f] is the grid of values [f x y] with [x = 0..m-1] 334 - and [y = 0..n-1], where [x] grows to the right, and [y] growns down. 335 - 336 - [f a y] is to the left of [f b y] if [a < b], and [f x a] is above [f x b] 337 - if [a < b], but the exact alignment is unspecified if the various [f x y] 338 - have different dimensions. *) 339 - 340 - val hsnap : ?align:[ `Left | `Middle | `Right ] -> int -> image -> image 341 - (** [hsnap ~align w i] is an image of width strictly [w] obtained by either 342 - horizontally padding or cropping [i] and positioning it according to 343 - [~align]. 344 - 345 - [~align] defaults to [`Middle]. *) 346 - 347 - val vsnap : ?align:[ `Top | `Middle | `Bottom ] -> int -> image -> image 348 - (** [vsnap ~align h i] is an image of height strictly [h] obtained by either 349 - vertically padding or cropping [i] and positioning it according to 350 - [~align]. 351 - 352 - [~align] defaults to [`Middle]. *) 353 - 354 - (** {1 [Format] interoperability} *) 355 - 356 - val strf : ?attr:attr -> ?w:int -> ('a, Format.formatter, unit, image) format4 -> 'a 357 - (** [strf ?attr ?w:width format ...] pretty-prints like 358 - [Format.asprintf format ...], but returns an [image]. 359 - 360 - [attr] is the (outermost) attribute. Defaults to {!A.empty}. 361 - 362 - [width] is used to set the margin on the formatter. This is only a hint, 363 - and does not guarantee the width of the result. Consult 364 - {{: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Format.html#VALset_margin} 365 - [Format.set_margin]} for details. Defaults to an unspecified, large 366 - number. 367 - 368 - @raise Invalid_argument if the printing process attempts to directly 369 - output {{!ctrls}control characters}, by embedding them in [format] or a 370 - string printed with the [%s] conversion, for example. 371 - {{: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Format.html#fpp} 372 - Formatted printing} is allowed. *) 373 - 374 - val kstrf : ?attr:attr -> ?w:int -> (image -> 'a) -> ('b, Format.formatter, unit, 'a) format4 -> 'b 375 - (** [kstrf ?attr ?w k format ...] is continuation-based [strf ?attr ?w format ...]. *) 376 - 377 - val pp_attr : attr -> (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a -> unit 378 - (** [pp_attr a f] is a pretty-printer like [f], except its output is styled 379 - with [a]. This applies only outside of any styling [f] itself might embed. *) 380 - end 381 - 382 - (** Operators, repeated. *) 383 - module Infix : sig 384 - 385 - (** {2 [I]} 386 - 387 - See {{!I}[I]}. *) 388 - 389 - val (<->) : image -> image -> image 390 - val (<|>) : image -> image -> image 391 - val (</>) : image -> image -> image 392 - 393 - (** {2 [A]} 394 - 395 - See {{!A}[A]}. *) 396 - 397 - val (++) : attr -> attr -> attr 398 - end 399 - 400 - (** {1 Low-level interface} 401 - 402 - You can ignore it, unless you are porting [Notty] to a new platform not 403 - supported by the existing IO backends. *) 404 - 405 - (** Terminal capabilities. 406 - 407 - This module describes how to output things so that a terminal understands 408 - them. *) 409 - module Cap : sig 410 - 411 - type t 412 - (** A set of capabilities that distinguish terminals from one another. 413 - 414 - A bundle of magic strings, really. *) 415 - 416 - val ansi : t 417 - (** The usual ANSI terminal, with colors, text styles and cursor 418 - positioning. *) 419 - 420 - val dumb : t 421 - (** Pure text output. Text attributes are stripped and positioning is done 422 - with the character [U+0020], SPACE. *) 423 - end 424 - 425 - (** Dump images to string buffers. *) 426 - module Render : sig 427 - 428 - val to_buffer : Buffer.t -> Cap.t -> int * int -> int * int -> image -> unit 429 - (** [to_buffer buf cap (x, y) (w, h) i] writes the string representation of 430 - [i] to [buf], as interpreted by [cap]. 431 - 432 - It renders the [w * h] rectangle of [i], offset by [(x, y)] from the top 433 - left. *) 434 - 435 - val pp : Cap.t -> Format.formatter -> image -> unit 436 - (** [pp cap ppf i] renders [i] to the pretty-printer [ppf]. 437 - 438 - {b Note} [pp] is generally meant for development and debugging. It tries 439 - to be reasonable, but dedicated IO modules handle the actual output 440 - better. *) 441 - 442 - (**/**) 443 - (* Toplevel. *) 444 - val pp_image : Format.formatter -> image -> unit 445 - val pp_attr : Format.formatter -> attr -> unit 446 - (**/**) 447 - end 448 - 449 - (** Parse and decode escape sequences in character streams. *) 450 - module Unescape : sig 451 - 452 - (** {1 Input events} *) 453 - 454 - type special = [ 455 - `Escape 456 - | `Enter 457 - | `Tab 458 - | `Backspace 459 - | `Insert 460 - | `Delete 461 - | `Home | `End 462 - | `Arrow of [ `Up | `Down | `Left | `Right ] 463 - | `Page of [ `Up | `Down ] 464 - | `Function of int 465 - ] 466 - (** A selection of extra keys on the keyboard. *) 467 - 468 - type button = [ `Left | `Middle | `Right | `Scroll of [ `Up | `Down ] ] 469 - (** Mouse buttons. *) 470 - 471 - type mods = [ `Meta | `Ctrl | `Shift ] list 472 - (** Modifier state. *) 473 - 474 - type key = [ special | `Uchar of Uchar.t | `ASCII of char ] * mods 475 - (** Keypress event. *) 476 - 477 - type mouse = [ `Press of button | `Drag | `Release ] * (int * int) * mods 478 - (** Mouse event. *) 479 - 480 - type paste = [ `Start | `End ] 481 - (** Paste event. *) 482 - 483 - type event = [ `Key of key | `Mouse of mouse | `Paste of paste ] 484 - (** Things that terminals say to applications. 485 - 486 - {ul 487 - {- [`Key (k, mods)] is keyboard input. 488 - 489 - [k] is a {{!key}key}, one of: 490 - {ul 491 - {- [`ASCII c] where [c] is a [char] in the 492 - {{: https://tools.ietf.org/html/rfc20}ASCII} range;} 493 - {- [`Uchar u] where [u] is any other {{!Uchar.t}unicode character}; or} 494 - {- a {{!special}special key}.}} 495 - 496 - [`ASCII] and [`Uchar] together represent the textual part of the input. 497 - These characters are guaranteed not to be {{!ctrls}control 498 - characters}, and are safe to use when constructing images. ASCII is 499 - separated from the rest of Unicode for convenient pattern-matching. 500 - 501 - [mods] are the extra {{!mods}modifier keys}. 502 - 503 - } 504 - {- [`Mouse (event, (x, y), mods)] is mouse input. 505 - 506 - [event] is the actual mouse event: {{!button}[button]} press, release, 507 - or motion of the mouse with buttons depressed. 508 - 509 - [(x, y)] are column and row position of the mouse. The origin is 510 - [(0,0)], the upper-left corner. 511 - 512 - {b Note} Every [`Press (`Left|`Middle|`Right)] generates a corresponding 513 - [`Release], but there is no portable way to detect which button was 514 - released. [`Scroll (`Up|`Down)] presses are not followed by releases. 515 - 516 - } 517 - {- [`Paste (`Start|`End)] are {e bracketed paste} events, signalling the 518 - beginning and end of a sequence of events pasted into the terminal. 519 - 520 - {b Note} This mechanism is useful, but not reliable. The pasted text 521 - could contain spurious start-of-paste or end-of-paste markers, or they 522 - could be entered by hand. }} 523 - 524 - Terminal input protocols are historical cruft, and heavily overload the 525 - ASCII range. For instance: 526 - {ul 527 - {- It is impossible to distinguish lower- and upper-case ASCII characters 528 - if {b Ctrl} is pressed;} 529 - {- several combinations of key-presses are aliased as special keys; and} 530 - {- in a UTF-8 encoded stream, there is no representation for non-ASCII 531 - characters with modifier keys.}} 532 - 533 - This means that many values that inhabit the [event] type are impossible, 534 - while some reflect multiple different user actions. Limitations include: 535 - 536 - {ul 537 - {- [`Shift] is reported only with special keys, and not all of them.} 538 - {- [`Meta] and [`Control] are reported with mouse events, key events with 539 - special keys, and key events with values in the ranges [0x40-0x5f] 540 - ([@] to [_]) and [0x60-0x7e] ([`] to [~]). If {b Ctrl} is pressed, the higher 541 - range is mapped into the lower range.} 542 - {- Terminals will variously under-report modifier key state.}} 543 - 544 - Perform own experiments before relying on elaborate key combinations. *) 545 - 546 - val uchar : [< `Uchar of Uchar.t | `ASCII of char ] -> Uchar.t 547 - (** [uchar x] is the {!Uchar.t} corresponding to [x]. This operations merges 548 - the ASCII and Unicode variants of {{!key}key}. *) 549 - 550 - (** {1 Decoding filter} 551 - 552 - Simple IO-less terminal input processor. It can be used for building 553 - custom terminal input abstractions. *) 554 - 555 - type t 556 - (** Input decoding filter. 557 - 558 - The filter should be {{!input}fed} strings, which it first decodes from 559 - UTF-8, and then extracts the input events. 560 - 561 - Malformed UTF-8 input bytes and unrecognized escape sequences are silently 562 - discarded. *) 563 - 564 - val create : unit -> t 565 - (** [create ()] is a new, empty filter. *) 566 - 567 - val input : t -> bytes -> int -> int -> unit 568 - (** [input t buffer i len] feeds [len] bytes of [string] into [t], starting 569 - from position [len]. 570 - 571 - [len = 0] signals the end of input. 572 - 573 - [buffer] is immediately processed and can be reused after the call 574 - returns. *) 575 - 576 - val next : t -> [ event | `Await | `End ] 577 - (** [next t] is the next event in the filter's input stream: 578 - 579 - {ul 580 - {- [#event], an input {{!event}[event]}.} 581 - {- [`Await] if the filter needs more {{!input}input}.} 582 - {- [`End] if the input had ended.}} *) 583 - 584 - val pending : t -> bool 585 - (** [pending t] is [true] if a call to [next], without any intervening input, 586 - would {e not} return [`Await]. *) 587 - 588 - (** {1 Low-level parsing} 589 - 590 - {b Warning} The parsing interface is subject to change. 591 - 592 - Implementation of small parts of 593 - {{: http://www.ecma-international.org/publications/standards/Ecma-035.htm}ECMA-35} 594 - and 595 - {{: http://www.ecma-international.org/publications/standards/Ecma-048.htm}ECMA-48}, 596 - as needed by terminal emulators in common use. *) 597 - 598 - val decode : Uchar.t list -> event list 599 - (** [decode us] are the events encoded by [us]. 600 - 601 - [us] are assumed to have been generated in a burst, and the end of the 602 - list is taken to mean a pause. 603 - Therefore, [decode us1 @ decode us2 <> decode (us1 @ us2)] if [us1] ends 604 - with a partial escape sequence, including a lone [\x1b]. 605 - 606 - Unsupported escape sequences are silently discarded. *) 607 - end 608 - 609 - (**/**) 610 - (** {1 Private} 611 - 612 - These are private interfaces, prone to breakage. Don't use them. *) 613 - 614 - module Operation : sig 615 - type t 616 - val of_image : (int * int) -> int * int -> image -> t list 617 - end 618 - 619 - module Tmachine : sig 620 - 621 - type t 622 - 623 - val create : mouse:bool -> bpaste:bool -> Cap.t -> t 624 - val release : t -> bool 625 - val output : t -> Buffer.t -> unit 626 - 627 - val refresh : t -> unit 628 - val cursor : t -> (int * int) option -> unit 629 - val image : t -> image -> unit 630 - 631 - val set_size : t -> int * int -> unit 632 - 633 - val size : t -> int * int 634 - val dead : t -> bool 635 - end 636 - 637 - module Direct : sig 638 - val move_cursor : Buffer.t -> Cap.t -> [ `Home | `By of int * int | `To of int * int ] -> unit 639 - val show_cursor : Buffer.t -> Cap.t -> bool -> unit 640 - end 641 - (**/**) 642 - 643 - (** {1:basics Basics} 644 - 645 - Print a red-on-black ["Wow!"] above its right-shifted copy: 646 - {[ 647 - let wow = I.string A.(fg red ++ bg black) "Wow!" in 648 - I.(wow <-> (void 2 0 <|> wow)) |> Notty_unix.output_image 649 - ]} 650 - 651 - {2:meaning The meaning of images} 652 - 653 - An {{!image}[image]} value is a rectangle of styled character cells. It has a 654 - width and height, but is not anchored to an origin. A single character with 655 - associated display attributes, or a short fragment of text, are simple 656 - examples of images. 657 - 658 - Images are created by combining text fragments with {{!attributes}display 659 - attributes}, and composed by placing them {{!I.(<|>)}beside} each other, 660 - {{!I.(<->)}above} each other, and {{!I.(</>)}over} each other. 661 - 662 - Once constructed, an image can be rendered, and only at that point it obtains 663 - absolute placement. 664 - 665 - Consult {{!I}[I]} for more details. 666 - 667 - {2:attributes Display attributes} 668 - 669 - {{!attr}[attr]} values describe the styling characteristics of fragments of 670 - text. 671 - 672 - They combine a foreground and a background {{!A.color}[color]} with a 673 - set of {{!A.style}[styles]}. Either color can be {e unset}, which corresponds to 674 - the terminal's default foreground (resp. background) color. 675 - 676 - Attributes are used to construct primitive images. 677 - 678 - Consult {{!A}[A]} for more details. 679 - 680 - {2:ctrls Control characters} 681 - 682 - These are taken to be characters in the ranges [0x00-0x1f] ({b C0}), [0x7f] 683 - (BACKSPACE), [0x80-0x9f] ({b C1}). This is the 684 - {{: http://unicode.org/reports/tr44/#General_Category_Values}Unicode 685 - general category} {b Cc}. 686 - 687 - As control characters directly influence the cursor positioning, they 688 - cannot be used to create images. 689 - 690 - This, in particular, means that images cannot contain [U+000a] (NEWLINE). 691 - 692 - {1:limitations Limitations} 693 - 694 - [Notty] does not use Terminfo. If your terminal is particularly 695 - idiosyncratic, things might fail to work. Get in touch with the author to 696 - expand support. 697 - 698 - [Notty] assumes that the terminal is using UTF-8 for input and output. 699 - Things might break arbitrarily if this is not the case. 700 - 701 - For performance considerations, consult the {{!perf}performance model}. 702 - 703 - {2:cwidth Unicode vs. Text geometry} 704 - 705 - [Notty] uses [Uucp.Break.tty_width_hint] to guess the width of text 706 - fragments when computing geometry, and it suffers from the same 707 - shortcomings: 708 - 709 - {ul 710 - {- Geometry in general works for alphabets and east Asian scripts, mostly 711 - works for abjad scripts, and is a matter of luck for abugidas.} 712 - {- East Asian scripts work better when in 713 - {{:http://unicode.org/glossary/#normalization_form_c}NFC}.} 714 - {- For proper emoji display, [Uucp] and the terminal have to agree on the 715 - Unicode version.}} 716 - 717 - When in doubt, see 718 - {{: http://erratique.ch/software/uucp/doc/Uucp.Break.html#VALtty_width_hint} 719 - [Uucp.Break.tty_width_hint]}. 720 - 721 - Unicode has special interaction with {{!I.hcrop}horizontal cropping}: 722 - {ul 723 - {- Strings within images are cropped at {{: 724 - http://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries}grapheme 725 - cluster} boundaries. This means that scalar value sequences that are 726 - rendered combined, or overlaid, stay unbroken.} 727 - {- When a crop splits a wide character in two, the remaining half is 728 - replaced by [U+0020] (SPACE). Hence, character-cell-accurate cropping is 729 - possible even in the presence of characters that horizontally occupy 730 - more than one cell.}} 731 - 732 - {1:examples Examples} 733 - 734 - We assume a toplevel with [Notty] support ([#require "notty.top"]). 735 - 736 - {2 Hello} 737 - 738 - ["Rad!"] with default foreground and background: 739 - 740 - {[I.string A.empty "Rad!"]} 741 - 742 - Everything has to start somewhere. 743 - 744 - {2 Colors} 745 - 746 - ["Rad!"] in rad letters: 747 - 748 - {[I.string A.(fg lightred) "Rad!"]} 749 - 750 - {2 Padding and spacing} 751 - 752 - {[ 753 - let a1 = A.(fg lightwhite ++ bg red) 754 - and a2 = A.(fg red) 755 - ]} 756 - 757 - ["Rad"] and [" stuff!"] in different colors: 758 - 759 - {[I.(string a1 "Rad" <|> string a2 " stuff!")]} 760 - 761 - The second word hanging on a line below: 762 - 763 - {[I.(string a1 "Rad" <|> (string a2 "stuff!" |> vpad 1 0))]} 764 - 765 - {2 More geometry} 766 - 767 - Sierpinski triangle: 768 - 769 - {[ 770 - let square = "\xe2\x96\xaa" 771 - 772 - let rec sierp n = 773 - if n > 1 then 774 - let ss = sierp (pred n) in I.(ss <-> (ss <|> ss)) 775 - else I.(string A.(fg magenta) square |> hpad 1 0) 776 - ]} 777 - 778 - {[sierp 8]} 779 - 780 - A triangle overlaid over its shifted copy: 781 - 782 - {[let s = sierp 6 in I.(s </> vpad 1 0 s)]} 783 - 784 - Blinkenlights: 785 - 786 - {[ 787 - let rad n color = 788 - let a1 = A.fg color in 789 - let a2 = A.(st blink ++ a1) in 790 - I.((string a2 "Rad" |> hpad n 0) <-> 791 - (string a1 "(⌐■_■)" |> hpad (n + 7) 0)) 792 - 793 - let colors = A.[red; green; yellow; blue; magenta; cyan] 794 - ]} 795 - 796 - {[ 797 - colors |> List.mapi I.(fun i c -> rad i c |> pad ~t:i ~l:(2 * i)) 798 - |> I.zcat 799 - ]} 800 - 801 - {b Note} Usage of {{!A.blink}[blink]} might be regulated by law in some 802 - jurisdictions. 803 - 804 - {2 Pretty-printing} 805 - 806 - Images can be pretty-printed into: 807 - 808 - {[I.strf "(%d)" 42]} 809 - 810 - Attributes can be applied to the entire format string, or by decorating 811 - {e user-defined printers} that are supplied with [%a] conversions: 812 - 813 - {[let pp = Format.pp_print_int]} 814 - 815 - {[I.strf ~attr:A.(fg lightwhite) "(%a)" (I.pp_attr A.(fg green) pp) 42]} 816 - 817 - {2 Now with output} 818 - 819 - The core module has no real IO. Examples above are simple [image]-valued 820 - expressions, displayed by the pretty-printer that is installed by the 821 - toplevel support. Self-contained programs need a separate IO module: 822 - 823 - {[#require "notty.unix"]} 824 - 825 - {[sierp 8 |> Notty_unix.output_image]} 826 - 827 - (Note the difference in cropping behavior.) 828 - 829 - Computations can be adapted to the current terminal size. A line can stretch 830 - end-to-end: 831 - 832 - {[ 833 - Notty_unix.output_image_size @@ fun (w, _) -> 834 - let i1 = I.string A.(fg green) "very" 835 - and i2 = I.string A.(fg yellow) "melon" in 836 - I.(i1 <|> void (w - width i1 - width i2) 1 <|> i2) 837 - ]} 838 - 839 - The largest triangle that horizontally fits into the terminal: 840 - 841 - {[ 842 - Notty_unix.output_image_size @@ fun (w, _) -> 843 - let steps = int_of_float ((log (float w)) /. log 2.) in 844 - sierp steps |> I.vpad 0 1 845 - ]} 846 - 847 - {2 Simple interaction} 848 - 849 - Interactive Sierpinski: 850 - 851 - {[open Notty_unix]} 852 - 853 - {[ 854 - let img (double, n) = 855 - let s = sierp n in 856 - if double then I.(s </> vpad 1 0 s) else s 857 - in 858 - let rec update t state = Term.image t (img state); loop t state 859 - and loop t (double, n as state) = 860 - match Term.event t with 861 - | `Key (`Enter,_) -> () 862 - | `Key (`Arrow `Left,_) -> update t (double, max 1 (n - 1)) 863 - | `Key (`Arrow `Right,_) -> update t (double, min 8 (n + 1)) 864 - | `Key (`ASCII ' ', _) -> update t (not double, n) 865 - | `Resize _ -> update t state 866 - | _ -> loop t state 867 - in 868 - let t = Term.create () 869 - in 870 - update t (false, 1); Term.release t 871 - ]} 872 - 873 - The program uses a fullscreen {{!Notty_unix.Term}terminal} and loops reading 874 - the {{!Notty_unix.Term.event}input}. LEFT and RIGHT control the iteration 875 - count, and SPACE toggles double-drawing. Resizing the window causes a 876 - redraw. When the loop exits on ENTER, the terminal is 877 - {{!Notty_unix.Term.release}cleaned up}. 878 - 879 - {1:perf Performance model} 880 - 881 - This section is only relevant if using [Notty] becomes your bottleneck. 882 - 883 - {b TL;DR} Shared sub-expressions do not share work, so operators stick with 884 - you. 885 - 886 - The main performance parameter is {e image complexity}. This roughly 887 - corresponds to the number of image {{!I.imgcomp}composition} and 888 - {{!I.imgcrop}cropping} operators in the fully expanded [image] term, 889 - {b ignoring all sharing}. 890 - 891 - Outline numbers: 892 - 893 - {ul 894 - {- Highly complex images can be rendered and pushed out to a full-screen 895 - terminal more than 1000 times per second.} 896 - {- With more realistic images, this number is closer to 30,000.} 897 - {- Input processing is somewhere around 50MB/s.}} 898 - 899 - 900 - Image complexity [cplx] of an image [i] is: 901 - {ul 902 - {- For a {{!I.imgprims}primitive} [i], [cplx i = 1].} 903 - {- For a {{!I.imgcomp}composition} operator [op], 904 - [cplx (op i1 i2) = 1 + cplx i1 + cplx i2].} 905 - {- For a {{!I.imgcomp}crop} [cr], 906 - [cplx (cr i1) = 1 + cplx i1 - k], where [k] is the combined complexity of 907 - all the {e maximal} sub-terms that do not contribute to the output.}} 908 - 909 - For example (assuming an image [i]): 910 - 911 - {[ 912 - let img1 = I.((i <|> i) <-> (i <|> i)) 913 - let img2 = I.(let x = i <|> i in x <-> x) 914 - let img3 = I.(((i <|> i) <|> i) <|> i) 915 - ]} 916 - 917 - Complexity of each of these is [4 * cplx i + 3]. This might be surprising 918 - for [img2]. 919 - 920 - If [width i = 1], [cplx (hcrop 1 0 img1) = 3 + 2 * cplx i], and 921 - [cplx (hcrop 2 0 img3) = 2 + 2 * cplx i]. 922 - 923 - While [Notty] strives to be accommodating to all usage scenarios, these are 924 - the things to keep in mind if the rendering becomes slow: 925 - 926 - {ol 927 - {- Image composition is cheap. 928 - 929 - Combining images performs a negligible amount of computation. 930 - 931 - Constructing primitive images that contain scalar values outside of the 932 - ASCII range does a little more work upfront and is worth holding onto. 933 - 934 - } 935 - {- {{!Render}Rendering} depends on image complexity. 936 - 937 - As a consequence, this real-world example of wrapping renders in time 938 - O(n{^ 2}) in the number of lines: 939 - 940 - {[ 941 - let wrap1 width img = 942 - let rec go img = img :: 943 - if I.width img > width then go (I.hcrop width 0 img) else [] 944 - in go img |> I.vcat |> I.hsnap ~align:`Left width 945 - ]} 946 - 947 - Although [crop] is applied only [lines] times, the image complexity of 948 - each line depends on the number of preceding lines. 949 - 950 - An O(n) version does not iterate [crop]: 951 - 952 - {[ 953 - let wrap2 width img = 954 - let rec go off = I.hcrop off 0 img :: 955 - if I.width img - off > width then go (off + width) else [] 956 - in go 0 |> I.vcat |> I.hsnap ~align:`Left width 957 - ]} 958 - } 959 - {- Rendering depends on the {e output} dimensions, but not on the {e image} 960 - dimensions. 961 - 962 - Rendering an image to [w * h] implicitly crops it to its leftmost [w] 963 - columns and topmost [h] rows. While [w] and [h] will have an impact on 964 - the rendering performance, the complexity of the (cropped) image tends to 965 - be more important.}} 966 - 967 - *)
-11
notty/src/notty_top.ml
··· 1 - (* Copyright (c) 2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - (* Force linking with Notty for e.g. Omod. *) 5 - let _ = Sys.opaque_identity Notty.I.empty 6 - 7 - #if OCAML_VERSION >= (4,14,0) 8 - let _ = Toploop.use_silently Format.err_formatter (Toploop.File "notty_top_init.ml") 9 - #else 10 - let _ = Toploop.use_silently Format.err_formatter "notty_top_init.ml" 11 - #endif
-7
notty/src/notty_top_init.ml
··· 1 - (* Copyright (c) 2017 David Kaloper Meršinjak. All rights reserved. 2 - See LICENSE.md. *) 3 - 4 - open Notty;; 5 - 6 - #install_printer Notty.Render.pp_image;; 7 - #install_printer Notty.Render.pp_attr;;
-146
notty/support/gen_unicode_props.ml
··· 1 - #!/usr/bin/env ocaml 2 - (* Copyright (c) 2020 David Kaloper Meršinjak. All rights reserved. 3 - See LICENSE.md. *) 4 - 5 - #use "topfind" 6 - #require "uucp" 7 - 8 - let filter p seq i = seq (fun x -> if p x then i x) 9 - let map f seq i = seq (fun x -> i (f x)) 10 - let uchars it = 11 - let rec go it u = it u; go it (Uchar.succ u) in 12 - try go it Uchar.min with Invalid_argument _ -> () 13 - let to_list seq = 14 - let xs = ref [] in 15 - seq (fun x -> xs := x :: !xs); 16 - List.rev !xs 17 - 18 - let intervals_kv seq i = 19 - let s = ref None in 20 - let f (x, v) = match !s with 21 - | None -> s := Some (x, x, v) 22 - | Some (a, b, v0) when v = v0 && x = Uchar.succ b -> s := Some (a, x, v0) 23 - | Some e -> i e; s := Some (x, x, v) in 24 - seq f; 25 - match !s with Some e -> i e | _ -> () 26 - 27 - let intervals_p seq = 28 - map (fun x -> x, ()) seq |> intervals_kv |> map (fun (a, b, _) -> a, b) 29 - 30 - (* Condenses code points into continuous range. *) 31 - let pack_u u = let i = Uchar.to_int u in if i > 0xd7ff then i - 0x800 else i 32 - let unpack_u i = Uchar.of_int (if i < 0xd800 then i else i + 0x800) 33 - 34 - (* 12-6-6-bit (0xfff-0x3f-0x3f) trie, 3 levels, array-array-string. 35 - Root is variable; lower levels are either empty or complete. 36 - 37 - At the moment, packed Uchar.max is 0x10f7ff; this can map up to 0xffffff 38 - distinct code points. *) 39 - let trie ~default f = 40 - let xs = List.init ((pack_u Uchar.max lsr 12) + 1) @@ fun b0 -> 41 - let mask = b0 lsl 12 in 42 - let arr = Array.init 0x40 @@ fun b1 -> 43 - let mask = mask lor (b1 lsl 6) in 44 - let v b2 = match unpack_u (mask lor b2) with 45 - | x -> f x 46 - | exception Invalid_argument _ -> default in 47 - match (for b2 = 0 to 0x3f do if v b2 <> default then raise Exit done) with 48 - | exception Exit -> String.init 0x40 (fun b2 -> Char.chr (v b2)) 49 - | () -> "" 50 - in 51 - if Array.for_all ((=) "") arr then [||] else arr 52 - in 53 - let rec trim = function [||]::xs -> trim xs | xs -> xs in 54 - List.rev (trim (List.rev xs)) |> Array.of_list 55 - 56 - let pf = Format.fprintf 57 - let strf = Format.sprintf 58 - let pp_iter ?(sep = fun _ _ -> ()) iter pp ppf x = 59 - let fst = ref true in 60 - let f x = (if !fst then fst := false else sep ppf ()); pp ppf x in 61 - iter f x 62 - let pp_u ppf u = pf ppf "0x%04x" (Uchar.to_int u) 63 - let pp_as_array iter pp ppf x = 64 - let sep ppf () = pf ppf ";@ " in 65 - pf ppf "@[<2>[|%a|]@]" (pp_iter ~sep iter pp) x 66 - 67 - let intern ppf_ml iter = 68 - let t = Hashtbl.create 16 in 69 - let n = ref 0 in 70 - iter (fun s -> if not (Hashtbl.mem t s) then begin 71 - let name = strf "s%03d" !n in 72 - Hashtbl.add t s name; incr n; 73 - pf ppf_ml "let %s = %S@." name s 74 - end); 75 - pf ppf_ml "@."; 76 - (fun ppf s -> match Hashtbl.find_opt t s with 77 - | Some name -> pf ppf "%s" name 78 - | None -> pf ppf "%S" s) 79 - 80 - let dump_interval_map (ppf_mli, ppf_ml) ~name ~desc seq = 81 - pf ppf_mli "(* %s *)@.val %s: int array * int array * int array@.@." desc name; 82 - let xs = to_list (intervals_kv seq) in 83 - let aa = List.map (fun (a, _, _) -> a) xs 84 - and bb = List.map (fun (_, b, _) -> b) xs 85 - and cc = List.map (fun (_, _, c) -> c) xs in 86 - let pp_arr pp = pp_as_array List.iter pp in 87 - let pp_arr_u = pp_arr pp_u and pp_arr_i = pp_arr Format.pp_print_int in 88 - pf ppf_ml "@[<2>let %s =@ @[<1>(%a,@ %a,@ %a)@]@]@.@." 89 - name pp_arr_u aa pp_arr_u bb pp_arr_i cc 90 - 91 - let dump_trie_map (ppf_mli, ppf_ml) ~name ~desc ~default f = 92 - pf ppf_mli "(* %s *)@.val %s: string array array@.@." desc name; 93 - let xs = trie ~default f in 94 - let pp_s = intern ppf_ml Array.(fun i -> i ""; iter (iter i) xs) in 95 - pf ppf_ml "@[<2>let %s =@ %a@]" name 96 - Array.(pp_as_array iter (pp_as_array iter pp_s)) xs 97 - 98 - let pp_header ppf = Format.fprintf ppf 99 - "(* Do not edit. 100 - * 101 - * This module contains select unicode properties extracted from Uucp, 102 - * using `%s`. 103 - * 104 - * Unicode version %s. 105 - *) 106 - 107 - " Sys.argv.(0) Uucp.unicode_version 108 - 109 - let extract (ppmli, ppml as ppfs) = 110 - 111 - pp_header ppmli; pp_header ppml; 112 - 113 - dump_interval_map ppfs 114 - ~name:"tty_width_hint" 115 - ~desc:"Uucp.Break.tty_width_hint" 116 - (* w = -1 is easy to detect. 117 - w = 1 covers the most intervals, so we default it. *) 118 - (uchars |> map (fun u -> u, Uucp.Break.tty_width_hint u) 119 - |> filter (fun (_, w) -> w <> -1 && w <> 1)); 120 - 121 - (* dump_interval_map ppfs *) 122 - (* ~name:"grapheme_cluster_boundary" *) 123 - (* ~desc:"Uucp.Break.Low.grapheme_cluster." *) 124 - (* (1* No single value dominates the histogram. *1) *) 125 - (* (uchars |> map (fun u -> u, Uucp.Break.Low.grapheme_cluster u)); *) 126 - 127 - dump_trie_map ppfs 128 - ~name:"grapheme_cluster_boundary" 129 - ~desc:"Uucp.Break.Low.grapheme_cluster." 130 - ~default:16 (* 16 - `XX - is by far the most prevalent value *) 131 - Uucp.Break.Low.grapheme_cluster; 132 - 133 - () 134 - 135 - let file = "src/no-uucp/notty_uucp_data" 136 - 137 - let with_new name f = 138 - let o = open_out_gen [Open_trunc; Open_creat; Open_wronly] 0o664 name in 139 - let ppf = Format.formatter_of_out_channel o in 140 - f ppf; Format.pp_print_flush ppf (); close_out o 141 - 142 - let () = 143 - Format.printf "Dumping Unicode v%s data to %s.@." Uucp.unicode_version file; 144 - with_new (file ^ ".mli") @@ fun ppmli -> 145 - with_new (file ^ ".ml") @@ fun ppml -> 146 - extract (ppmli, ppml)