pax_global_header00006660000000000000000000000064136641523330014520gustar00rootroot0000000000000052 comment=ee2e80c3eb5bd6049c7e505bf2f3c3a75a887063 mew_vi-0.5.0/000077500000000000000000000000001366415233300130105ustar00rootroot00000000000000mew_vi-0.5.0/.gitignore000066400000000000000000000001621366415233300147770ustar00rootroot00000000000000**/*.annot **/.\#* **/*~ **/Session.vim **/.*.swp **/*.o **/*.a **/*.so **/*.cm* _build .merlin mew_vi.install mew_vi-0.5.0/.travis.yml000066400000000000000000000003731366415233300151240ustar00rootroot00000000000000language: c sudo: required install: wget https://raw.githubusercontent.com/ocaml/ocaml-ci-scripts/master/.travis-opam.sh script: bash -ex .travis-opam.sh env: - OCAML_VERSION=4.02 - OCAML_VERSION=4.06 - OCAML_VERSION=4.10 os: - linux - osx mew_vi-0.5.0/CHANGES.md000066400000000000000000000012071366415233300144020ustar00rootroot000000000000000.5.0 (2020-05-29) ------------------ * register support * text object: ab ib 0.4.0 (2020-04-26) ------------------ * visual mode 0.3.0 (2020-04-25) ------------------ * motions: * h l 0 ^ $ * j k gg G * w W e E b B ge gE * f F t T * aw iw aW iW * include or inner ( ), [ ], { }, < >, ' and " * generic quote: aq? iq? where ? could be any character * bracket matching: jump back and forth between matched brakcets * delete, change, yank with motions * paste: p P * line joining: J 0.2.0 (2020-04-20) ------------------ * paste * W B E gE * ( ) [ ] < > { } * f F * % 0.1.0 (2020-04-14) ------------------ initial release mew_vi-0.5.0/LICENSE000066400000000000000000000021121366415233300140110ustar00rootroot00000000000000The MIT License Copyright (c) 2019 - 2020, ZAN DoYe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mew_vi-0.5.0/Makefile000066400000000000000000000004641366415233300144540ustar00rootroot00000000000000default: dune build install: dune install uninstall: dune uninstall doc: dune build @doc clean: dune clean runtest: dune runtest all-supported-ocaml-versions: dune build @install --workspace dune-workspace.4.02.dev --root . dune build @install @runtest --workspace dune-workspace.dev --root . mew_vi-0.5.0/README.md000066400000000000000000000007411366415233300142710ustar00rootroot00000000000000# mew\_vi — Modal editing witch, VI interpreter [![Build Status](https://travis-ci.org/kandu/mew_vi.svg?branch=master)](https://travis-ci.org/kandu/mew_vi) This is a vi-like modal editing engine generator. ## Usage Provide your `Key`, `Mode`, `Concurrent` modules to define the real world environment to get a handy vi-like modal editing engine. Feed the the `i` channel user input and get the vi actions from the `action_output` channel. ## WIP * `map` & `imap` command mew_vi-0.5.0/VERSION000066400000000000000000000000061366415233300140540ustar00rootroot000000000000000.5.0 mew_vi-0.5.0/dune-project000066400000000000000000000000201366415233300153220ustar00rootroot00000000000000(lang dune 1.1) mew_vi-0.5.0/dune-workspace.4.02.dev000066400000000000000000000001041366415233300170140ustar00rootroot00000000000000(lang dune 1.1) (context (opam (switch 4.02.3))) (profile release) mew_vi-0.5.0/dune-workspace.dev000066400000000000000000000001641366415233300164400ustar00rootroot00000000000000(lang dune 1.1) (context (opam (switch 4.03.0))) (context (opam (switch 4.06.1))) (context (opam (switch 4.08.1))) mew_vi-0.5.0/mew_vi.opam000066400000000000000000000007661366415233300151650ustar00rootroot00000000000000opam-version: "2.0" maintainer: "zandoye@gmail.com" authors: [ "ZAN DoYe" ] homepage: "https://github.com/kandu/mew_vi" bug-reports: "https://github.com/kandu/mew_vi/issues" license: "MIT" dev-repo: "git+https://github.com/kandu/mew_vi.git" build: [ ["dune" "build" "-p" name "-j" jobs] ] depends: [ "ocaml" {>= "4.02.3"} "mew" {>= "0.1.0" & < "0.2"} "react" "dune" {>= "1.1.0"} ] synopsis: "Modal editing witch, VI interpreter" description: """ A vi-like modal editing engine generator.""" mew_vi-0.5.0/src/000077500000000000000000000000001366415233300135775ustar00rootroot00000000000000mew_vi-0.5.0/src/core.ml000066400000000000000000000035251366415233300150660ustar00rootroot00000000000000(* * core.ml * ----------- * Copyright : (c) 2019 - 2020, ZAN DoYe * Licence : MIT * * This file is a part of mew_vi. *) module Make (Concurrent:Mew.Concurrent.S) = struct module Base = Mew.Make(Modal)(Concurrent) module Interpret = Interpret.Make(Concurrent) module Edit_action = Edit_action module Vi_action = Vi_action class edit state =object inherit Base.edit state val action_output : Edit_action.t Concurrent.MsgBox.t = Concurrent.MsgBox.create () method action_output= action_output val config= Interpret.Resolver.make_config () method get_register : string -> Interpret.Register.content option = state#get_register method set_register : string -> Interpret.Register.content -> unit = state#set_register initializer let status= let open Interpret.Resolver in { register= None; count= None } in Concurrent.Thread.async (Interpret.Resolver.interpret config status o action_output) end class state= let modes= let open Mode in Modes.singleton Name.Normal { name= Name.Normal; timeout= None; bindings= Mode.KeyTrie.create None} in object(self) inherit Base.state modes method vi_edit= new edit self val mutable registers : Interpret.Register.content Interpret.RegisterMap.t = Interpret.RegisterMap.empty method get_register name= let name= if name = "" then "\"" else name in let content= try Some (Interpret.RegisterMap.find name registers) with _-> None in content method set_register name content= let name= if name= "" then "\"" else name in registers <- Interpret.RegisterMap.add name content registers method get_registers= registers method set_registers regs= registers <- regs end end mew_vi-0.5.0/src/dune000066400000000000000000000001541366415233300144550ustar00rootroot00000000000000(library (name mew_vi) (public_name mew_vi) (libraries mew react) (flags (:standard -safe-string))) mew_vi-0.5.0/src/edit_action.ml000066400000000000000000000003461366415233300164160ustar00rootroot00000000000000(* * edit_action.ml * ----------- * Copyright : (c) 2019 - 2020, ZAN DoYe * Licence : MIT * * This file is a part of mew_vi. *) type t= | Dummy | Bypass of Key.t list | Vi of Vi_action.t list mew_vi-0.5.0/src/interpret.ml000066400000000000000000000675321366415233300161620ustar00rootroot00000000000000(* * interpret.ml * ----------- * Copyright : (c) 2019 - 2020, ZAN DoYe * Licence : MIT * * This file is a part of mew_vi. *) open Vi_action open Edit_action open React module Make (Concurrent:Mew.Concurrent.S) = struct module MsgBox = Concurrent.MsgBox module Thread = Concurrent.Thread let (>>=)= Thread.bind module Register = struct type t= string let compare= String.compare type content= | Seq of string | Line of string let compare_content t1 t2= match t1, t2 with | Seq s1, Seq s2-> String.compare s1 s2 | Line s1, Line s2-> String.compare s1 s2 | Seq _, Line _-> 1 | Line _, Seq _-> -1 end module RegisterMap = Map.Make(Register) type register= string option type count= int option type keyseq= Modal.Key.t list module Resolver = struct type t= config -> status -> keyseq -> result and config= { mode: Mode.Name.t signal; set_mode: ?step:step -> Mode.Name.t -> unit; keyseq: keyseq signal; set_keyseq: ?step:step -> keyseq -> unit; mutable resolver_insert: t; mutable resolver_normal: t; mutable resolver_visual: t; mutable resolver_command: t; } and status= { register: register; count: count; } and result= | Accept of (Edit_action.t * keyseq * Mode.Name.t) | Continue of (t * status * keyseq) | Rejected of keyseq let resolver_dummy= fun _config _status keyseq-> Rejected keyseq let resolver_insert _config _status keyseq= match keyseq with | []-> Rejected [] | key::tl-> if key.Key.control && key.code = Char "[" then Accept ( Vi [Motion (Left, 1); ChangeMode Normal] , tl , Mode.Name.Normal) else if key.code = Escape then Accept ( Vi [Motion (Left, 1); ChangeMode Normal] , tl , Mode.Name.Normal) else Accept ( Bypass [key] , tl , Mode.Name.Insert) module Common = struct let get_count status= match status.count with | Some count-> count | None-> 1 let get_register status= match status.register with | None | Some "\'"-> "\"" | Some reg-> reg let try_count continuation config status keyseq= let get_count numseq= match numseq with | ""-> status.count | _-> let num= int_of_string numseq in match status.count with | Some count-> Some (count * num) | None-> Some num in let rec other_num numseq config status keyseq= match keyseq with | []-> Rejected keyseq | key::tl-> match key.Key.code with | Char code-> if String.length code = 1 && code >= "0" && code <= "9" && not (key.Key.control || key.Key.meta || key.Key.shift) then let resolver= other_num (numseq ^ code) in Continue (resolver, status, tl) else continuation config { status with count= (get_count numseq) } keyseq | Escape-> Rejected tl | _-> continuation config { status with count= get_count numseq } keyseq in let first_num ()= match keyseq with | []-> Rejected keyseq | key::tl-> match key.Key.code with | Char code-> if String.length code = 1 && not (key.Key.control || key.Key.meta || key.Key.shift) then if code >= "1" && code <= "9" then let resolver= other_num code in Continue (resolver, status, tl) else continuation config { status with count= get_count "" } keyseq else continuation config { status with count= get_count "" } keyseq | Escape-> Rejected tl | _-> continuation config status keyseq in first_num () let try_register next_mode continuation config status keyseq= let get_register _config status keyseq= match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char code-> let resolver= continuation in Continue (resolver, { status with register= Some code }, tl) | _-> Rejected keyseq else Accept (Bypass [key], tl, Mode.Name.Normal) in match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char code-> if code = "\"" then Continue (get_register, status, tl) else continuation config status keyseq | _-> Rejected keyseq else Accept (Bypass [key], tl, next_mode) let try_motion next_mode config status keyseq= let try_motion_g _config status keyseq= let count= get_count status in match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char "e"-> Accept ( Vi [Motion (Word_back_end, count)] , tl , next_mode) | Char "E"-> Accept ( Vi [Motion (WORD_back_end, count)] , tl , next_mode) | Char "g"-> Accept ( match status.count with | None-> Vi [Motion (GotoLine_first, count)] , tl , next_mode | Some count-> Vi [Motion (GotoLine, (count-1))] , tl , next_mode) | _-> Rejected keyseq else Accept (Bypass [key], tl, next_mode) in let try_motion_occurence ?(backward=false) _config status keyseq= let count= get_count status in match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char chr-> if backward then Accept ( Vi [Motion (Occurrence_inline_back chr, count)] , tl , next_mode) else Accept ( Vi [Motion (Occurrence_inline chr, count)] , tl , next_mode) | _-> Rejected keyseq else Accept (Bypass [key], tl, next_mode) in let try_motion_occurence_till ?(backward=false) _config status keyseq= let count= get_count status in match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char chr-> if backward then Accept ( Vi [Motion (Occurrence_inline_till_back chr, count)] , tl , next_mode) else Accept ( Vi [Motion (Occurrence_inline_till chr, count)] , tl , next_mode) | _-> Rejected keyseq else Accept (Bypass [key], tl, next_mode) in let try_motion_n _config status keyseq= let count= get_count status in match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char "h"-> Accept ( Vi [Motion (Left, count)] , tl , next_mode) | Char "l"-> Accept ( Vi [Motion (Right, count)] , tl , next_mode) | Char "j"-> Accept ( Vi [Motion (Downward, count)] , tl , next_mode) | Char "k"-> Accept ( Vi [Motion (Upward, count)] , tl , next_mode) | Char "0"-> Accept ( Vi [Motion (Line_FirstChar, count)] , tl , next_mode) | Char "$"-> Accept ( Vi [Motion (Line_LastChar, count)] , tl , next_mode) | Char "^"-> Accept ( Vi [Motion (Line_FirstNonBlank, count)] , tl , next_mode) | Char "w"-> Accept ( Vi [Motion (Word, count)] , tl , next_mode) | Char "W"-> Accept ( Vi [Motion (WORD, count)] , tl , next_mode) | Char "b"-> Accept ( Vi [Motion (Word_back, count)] , tl , next_mode) | Char "B"-> Accept ( Vi [Motion (WORD_back, count)] , tl , next_mode) | Char "e"-> Accept ( Vi [Motion (Word_end, count)] , tl , next_mode) | Char "E"-> Accept ( Vi [Motion (WORD_end, count)] , tl , next_mode) | Char "G"-> Accept ( match status.count with | None-> Vi [Motion (GotoLine_last, count)] , tl , next_mode | Some count-> Vi [Motion (GotoLine, (count-1))] , tl , next_mode) | Char "g"-> let resolver= try_motion_g in Continue (resolver, status, tl) | Char "f"-> let backward= false in let resolver= try_motion_occurence ~backward in Continue (resolver, status, tl) | Char "F"-> let backward= true in let resolver= try_motion_occurence ~backward in Continue (resolver, status, tl) | Char "t"-> let backward= false in let resolver= try_motion_occurence_till ~backward in Continue (resolver, status, tl) | Char "T"-> let backward= true in let resolver= try_motion_occurence_till ~backward in Continue (resolver, status, tl) | Char "%"-> Accept ( Vi [Motion (Match, 1)] , tl , next_mode) | _-> Rejected keyseq else Accept (Bypass [key], tl, next_mode) in try_count try_motion_n config status keyseq end module Normal = struct let try_change_mode _config _status keyseq= match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char "i"-> Accept ( Vi [ChangeMode Insert] , tl , Mode.Name.Insert) | Char "I"-> Accept ( Vi [ Motion (Line_FirstNonBlank, 1); ChangeMode Insert] , tl , Mode.Name.Insert) | Char "a"-> Accept ( Vi [ Motion (Right_nl, 1); ChangeMode Insert] , tl , Mode.Name.Insert) | Char "A"-> Accept ( Vi [ Motion (Line_LastChar_nl, 1); ChangeMode Insert] , tl , Mode.Name.Insert) | Char "v"-> Accept ( Vi [ ChangeMode Visual] , tl , Mode.Name.Visual) | _-> Rejected keyseq else Rejected keyseq let try_modify config status keyseq= let open Common in let try_motion_n ~action _config status keyseq = let next_mode= if action = `Change then Mode.Name.Insert else Mode.Name.Normal in let make_actions status tl motion= let action= let register= get_register status and count= get_count status in match action with | `Change-> Change (register, motion, count) | `Delete-> Delete (register, motion, count) | `Yank-> Yank (register, motion, count) in Accept ( Vi [action] , tl , next_mode) in let try_motion_g _config status keyseq= match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char "e"-> make_actions status tl Word_back_end | Char "E"-> make_actions status tl WORD_back_end | _-> Rejected keyseq else Accept (Bypass [key], tl, Mode.Name.Normal) in let try_motion_quote ?(inner=false) _config status keyseq= match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char chr-> if inner then make_actions status tl (Quote_inner chr) else make_actions status tl (Quote_include chr) | _-> Rejected keyseq else Accept (Bypass [key], tl, Mode.Name.Normal) in let try_motion_object ?(inner=false) _config status keyseq= match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char "(" | Char ")" | Char "b"-> if inner then make_actions status tl Parenthesis_inner else make_actions status tl Parenthesis_include | Char "[" | Char "]"-> if inner then make_actions status tl Bracket_inner else make_actions status tl Bracket_include | Char "<" | Char ">"-> if inner then make_actions status tl AngleBracket_inner else make_actions status tl AngleBracket_include | Char "{" | Char "}"-> if inner then make_actions status tl Brace_inner else make_actions status tl Brace_include | Char "'"-> if inner then make_actions status tl (Quote_inner "'") else make_actions status tl (Quote_include "'") | Char "\""-> if inner then make_actions status tl (Quote_inner "\"") else make_actions status tl (Quote_include "\"") | Char "w"-> if inner then make_actions status tl Word_inner else make_actions status tl Word_include | Char "W"-> if inner then make_actions status tl WORD_inner else make_actions status tl WORD_include | Char "q"-> let resolver= try_motion_quote ~inner in Continue (resolver, status, tl) | _-> Rejected keyseq else Accept (Bypass [key], tl, Mode.Name.Normal) in let try_motion_occurence ?(backward=false) _config _status keyseq= match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char chr-> if backward then make_actions status tl (Occurrence_inline_back chr) else make_actions status tl (Occurrence_inline chr) | _-> Rejected keyseq else Accept (Bypass [key], tl, Mode.Name.Normal) in let try_motion_occurence_till ?(backward=false) _config status keyseq= match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char chr-> if backward then make_actions status tl (Occurrence_inline_till_back chr) else make_actions status tl (Occurrence_inline_till chr) | _-> Rejected keyseq else Accept (Bypass [key], tl, Mode.Name.Normal) in match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char "h"-> make_actions status tl Left | Char "l"-> make_actions status tl Right | Char "j"-> make_actions status tl Downward | Char "k"-> make_actions status tl Upward | Char "0"-> make_actions status tl Line_FirstChar | Char "$"-> make_actions status tl Line_LastChar | Char "^"-> make_actions status tl Line_FirstNonBlank | Char "w"-> make_actions status tl Word | Char "W"-> make_actions status tl WORD | Char "b"-> make_actions status tl Word_back | Char "B"-> make_actions status tl WORD_back | Char "e"-> make_actions status tl Word_end | Char "E"-> make_actions status tl WORD_end | Char "g"-> let resolver= try_motion_g in Continue (resolver, status, tl) | Char "d"-> if action = `Delete then make_actions status tl Line else Rejected keyseq | Char "a"-> let inner= false in let resolver= try_motion_object ~inner in Continue (resolver, status, tl) | Char "i"-> let inner= true in let resolver= try_motion_object ~inner in Continue (resolver, status, tl) | Char "f"-> let backward= false in let resolver= try_motion_occurence ~backward in Continue (resolver, status, tl) | Char "F"-> let backward= true in let resolver= try_motion_occurence ~backward in Continue (resolver, status, tl) | Char "t"-> let backward= false in let resolver= try_motion_occurence_till ~backward in Continue (resolver, status, tl) | Char "T"-> let backward= true in let resolver= try_motion_occurence_till ~backward in Continue (resolver, status, tl) | Char "%"-> make_actions status tl Match | Char "y"-> if action = `Yank then make_actions status tl Line else Rejected keyseq | _-> Rejected keyseq else Accept (Bypass [key], tl, Mode.Name.Normal) in let determin _config status keyseq= let count= get_count status and register= get_register status in match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char "u"-> Accept (Vi [Undo count], tl, Mode.Name.Normal) | Char "p"-> Accept (Vi [Paste_after (register, count)], tl, Mode.Name.Normal) | Char "P"-> Accept (Vi [Paste_before (register, count)], tl, Mode.Name.Normal) | Char "d"-> let resolver= Common.try_count (try_motion_n ~action:`Delete) in Continue (resolver, status, tl) | Char "c"-> let resolver= Common.try_count (try_motion_n ~action:`Change) in Continue (resolver, status, tl) | Char "D"-> Accept ( Vi [Delete (register, Line_LastChar, count)], tl, Mode.Name.Normal) | Char "C"-> Accept ( Vi [Delete (register, Line_LastChar, count)], tl, Mode.Name.Insert) | Char "x"-> Accept ( Vi [Delete (register, Right, count)] , tl , Mode.Name.Normal) | Char "s"-> Accept ( Vi [Delete (register, Right, count)] , tl , Mode.Name.Insert) | Char "J"-> Accept ( Vi [(Join count)] , tl , Mode.Name.Normal) | Char "y"-> let resolver= Common.try_count (try_motion_n ~action:`Yank) in Continue (resolver, status, tl) | _-> Rejected keyseq else Accept (Bypass [key], tl, Mode.Name.Normal) in determin config status keyseq let try_insert _config status keyseq= let count= Common.get_count status in match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char "o"-> Accept ( Vi [Insert ((Newline_below ""), count)] , tl , Mode.Name.Insert) | Char "O"-> Accept ( Vi [Insert ((Newline_above ""), count)] , tl , Mode.Name.Insert) | _-> Rejected keyseq else Accept (Bypass [key], tl, Mode.Name.Normal) let try_motion_modify_insert config status keyseq= match Common.try_motion Mode.Name.Normal config status keyseq with | Rejected keyseq-> let resolver config status keyseq= match try_modify config status keyseq with | Rejected keyseq-> let resolver= try_insert in Continue (resolver, status, keyseq) | r-> r in Continue (resolver, status, keyseq) | r-> r let resolver_normal config status keyseq= match keyseq with | []-> Rejected [] | _-> match try_change_mode config status keyseq with | Rejected keyseq-> Common.try_register Mode.Name.Normal (Common.try_count (Common.try_register Mode.Name.Normal try_motion_modify_insert)) config status keyseq | r-> r end module Visual = struct let try_change_mode _config _status keyseq= match keyseq with | []-> Rejected [] | key::tl-> if key.Key.control && key.code = Char "[" then Accept ( Vi [ChangeMode Normal] , tl , Mode.Name.Normal) else if key.code = Escape then Accept ( Vi [ChangeMode Normal] , tl , Mode.Name.Normal) else if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char "v"-> Accept ( Vi [ ChangeMode Normal] , tl , Mode.Name.Normal) | _-> Rejected keyseq else Rejected keyseq let try_motion= Common.try_motion Mode.Name.Visual let try_modify _config status keyseq= let register= Common.get_register status in match keyseq with | []-> Rejected [] | key::tl-> if not (key.Key.control || key.Key.meta || key.Key.shift) then match key.Key.code with | Char "c" | Char "s"-> Accept ( Vi [ DeleteSelected register; ChangeMode Insert] , tl , Mode.Name.Insert) | Char "d" | Char "x"-> Accept ( Vi [ DeleteSelected register; ChangeMode Normal ] , tl , Mode.Name.Normal) | Char "y"-> Accept ( Vi [ YankSelected register; ChangeMode Normal ] , tl , Mode.Name.Normal) | _-> Rejected keyseq else Rejected keyseq let try_motion_modify config status keyseq= match try_motion config status keyseq with | Rejected keyseq-> Continue (try_modify, status, keyseq) | r-> r let resolver_visual config status keyseq= match keyseq with | []-> Rejected [] | _-> match try_change_mode config status keyseq with | Rejected keyseq-> Common.try_register Mode.Name.Visual (Common.try_count try_motion_modify) config status keyseq | r-> r end let make_config ?(mode= Mode.Name.Insert) ?(keyseq=[]) ?(resolver_insert= resolver_insert) ?(resolver_normal= Normal.resolver_normal) ?(resolver_visual= Visual.resolver_visual) ?(resolver_command= resolver_dummy) () = let mode, set_mode= React.S.create mode in let keyseq, set_keyseq= React.S.create keyseq in { mode; set_mode; keyseq; set_keyseq; resolver_insert; resolver_normal; resolver_visual; resolver_command; } let rec interpret ?resolver ?(keyseq=[]) config status (keyIn: Modal.Key.t MsgBox.t) (action: Edit_action.t MsgBox.t) () = let resolver= match resolver with | Some resolver-> resolver | None-> match S.value config.mode with | Mode.Name.Insert-> config.resolver_insert | Mode.Name.Visual-> config.resolver_visual | _-> config.resolver_normal in (match keyseq with | []-> MsgBox.get keyIn >>= fun key-> Thread.return [key] | _-> Thread.return keyseq) >>= fun keyseq-> match resolver config status keyseq with | Accept (edit, keyseq, next_mode)-> config.set_mode next_mode; MsgBox.put action edit >>= interpret config { status with count= None} ~keyseq keyIn action | Continue (resolver, status, keyseq)-> interpret config status ~resolver ~keyseq keyIn action () | Rejected _keyseq-> MsgBox.put action Dummy >>= interpret config { status with count= None } keyIn action end end mew_vi-0.5.0/src/key.ml000066400000000000000000000063511366415233300147260ustar00rootroot00000000000000(* * key.ml * ----------- * Copyright : (c) 2019 - 2020, ZAN DoYe * Licence : MIT * * This file is a part of mew_vi. *) type code = | Char of string | Enter | Escape | Tab | Up | Down | Left | Right | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | Next_page | Prev_page | Home | End | Insert | Delete | Backspace let code_to_int= let bin_to_int bin= let len= String.length bin in let rec calc s pos acc= if pos < len then calc s (pos+1) (acc + (int_of_char s.[pos]) lsl (pos * 8)) else acc in calc bin 0 0 in function | Enter-> 1 | Escape-> 2 | Tab-> 3 | Up-> 4 | Down-> 5 | Left-> 6 | Right-> 7 | F1-> 8 | F2-> 9 | F3-> 10 | F4-> 11 | F5-> 12 | F6-> 13 | F7-> 14 | F8-> 15 | F9-> 16 | F10-> 17 | F11-> 18 | F12-> 19 | Next_page-> 20 | Prev_page-> 21 | Home-> 22 | End-> 23 | Insert-> 24 | Delete-> 25 | Backspace-> 26 | Char bin-> 27 + (bin_to_int bin) let code_to_string= function | Char bin-> Printf.sprintf "Char %s" bin | Enter-> "Enter" | Escape-> "Escape" | Tab-> "Tab" | Up-> "Up" | Down-> "Down" | Left-> "Left" | Right-> "Right" | F1-> "F1" | F2-> "F2" | F3-> "F3" | F4-> "F4" | F5-> "F5" | F6-> "F6" | F7-> "F7" | F8-> "F8" | F9-> "F9" | F10-> "F10" | F11-> "F11" | F12-> "F12" | Next_page-> "Next_page" | Prev_page-> "Prev_page" | Home-> "Home" | End-> "End" | Insert-> "Insert" | Delete-> "Delete" | Backspace-> "Backspace" type t = { control: bool; meta: bool; shift: bool; code : code; } let t_to_int t= let b= function true -> 1 | false -> 0 in (code_to_int t.code, [b t.control; b t.meta; b t.shift]) let t_to_string t= Printf.sprintf "{ %s%s%s%s }" (code_to_string t.code) (if t.control then "; control" else "") (if t.meta then "; meta" else "") (if t.shift then "; shift" else "") type modifier= | Control | Meta | Shift let compare_code t1 t2= compare t1.code t2.code let compare_modifier t1 t2= let c= compare t1.control t2.control in if c <> 0 then c else let c= compare t1.meta t2.meta in if c <> 0 then c else compare t1.shift t2.shift module Modifiers = Set.Make( struct type t= modifier let compare= compare end) type modifiers= Modifiers.t let compare t1 t2= let c= compare_code t1 t2 in if c <> 0 then c else compare_modifier t1 t2 let control key = key.control let meta key = key.meta let shift key = key.shift let code key = key.code let create ~code ~modifiers= { control= Modifiers.mem Control modifiers; meta= Modifiers.mem Meta modifiers; shift= Modifiers.mem Shift modifiers; code } let create_modifiers= Modifiers.of_list let modifiers t= let l= [] in let l= if t.control then Control::l else l in let l= if t.meta then Meta::l else l in let l= if t.shift then Shift::l else l in Modifiers.of_list l let modifier ~key ~modifier= match modifier with | Control-> key.control | Meta-> key.meta | Shift-> key.shift let equal k1 k2= compare k1 k2 = 0 let hash k= let code, modifier= t_to_int k in Mew.Key.hash code modifier let to_string= t_to_string mew_vi-0.5.0/src/mew_vi.ml000066400000000000000000000004451366415233300154220ustar00rootroot00000000000000(* * mew_vi.ml * ----------- * Copyright : (c) 2019 - 2020, ZAN DoYe * Licence : MIT * * This file is a part of mew_vi. *) module Core = Core module Modal = Modal module Key = Key module Edit_action = Edit_action module Vi_action = Vi_action module Mode = Mode mew_vi-0.5.0/src/modal.ml000066400000000000000000000003521366415233300152250ustar00rootroot00000000000000(* * modal.ml * ----------- * Copyright : (c) 2019 - 2020, ZAN DoYe * Licence : MIT * * This file is a part of mew_vi. *) module Key = Key module Name = Mode.Name module Mode = Mew.Mode.Make(Key)(Name) mew_vi-0.5.0/src/mode.ml000066400000000000000000000004571366415233300150630ustar00rootroot00000000000000(* * mode.ml * ----------- * Copyright : (c) 2019 - 2020, ZAN DoYe * Licence : MIT * * This file is a part of mew_vi. *) module Name = struct type t= | Normal | Visual | Insert | Commandline let compare= compare end include Mew.Mode.Make(Key)(Name) mew_vi-0.5.0/src/vi_action.ml000066400000000000000000000043601366415233300161070ustar00rootroot00000000000000(* * vi_action.ml * ----------- * Copyright : (c) 2019 - 2020, ZAN DoYe * Licence : MIT * * This file is a part of mew_vi. *) type insert= | Append of string (* a *) | AppendEol of string (* A *) | Insert of string (* i *) | InsertBol of string (* I *) | Newline_below of string (* o *) | Newline_above of string (* O *) type motion= (* left right *) | Left (* h *) | Right (* l *) | Right_nl (* l, including newline *) | Line_FirstChar (* 0 *) | Line_FirstNonBlank (* ^ *) | Line_LastChar (* $ *) | Line_LastChar_nl (* $ *) | Line_LastNonBlank (* g_ *) | Line_LastNonBlank_nl (* g_ *) (* up down *) | Upward (* k *) | Downward (* j *) | GotoLine (* gg or G *) | GotoLine_first (* gg *) | GotoLine_last (* G *) (* word *) | Word (* w *) | WORD (* W *) | Word_end (* e *) | WORD_end (* E *) | Word_back (* b *) | WORD_back (* B *) | Word_back_end (* ge *) | WORD_back_end (* gE *) (* line *) | Line (* occurrence *) | Occurrence_inline of string | Occurrence_inline_back of string | Occurrence_inline_till of string | Occurrence_inline_till_back of string (* text object *) | Sentence_backword (* ( *) | Sentence_forward (* ) *) | Paragraph_backward (* { *) | Paragraph_forward (* } *) (* text object selection *) | Word_include (* aw *) | Word_inner (* iw *) | WORD_include (* aW *) | WORD_inner (* iW *) | Sentence_include (* as *) | Sentence_inner (* is *) | Paragraph_include (* ap *) | Paragraph_inner (* ip *) | Parenthesis_include (* a( a) *) | Parenthesis_inner (* i( i) *) | Bracket_include (* a[ a] *) | Bracket_inner (* i[ i] *) | AngleBracket_include (* a< a> *) | AngleBracket_inner (* i< i> *) | Brace_include (* a{ a} *) | Brace_inner (* i{ i} *) | Quote_include of string | Quote_inner of string (* match *) | Match type register= string type t= | Insert of insert * int | Motion of motion * int | Delete of register * motion * int | Change of register * motion * int | Join of int | Undo of int | Paste_before of register * int | Paste_after of register * int | Yank of register * motion * int | DeleteSelected of register | YankSelected of register | ChangeMode of Mode.Name.t mew_vi-0.5.0/test/000077500000000000000000000000001366415233300137675ustar00rootroot00000000000000mew_vi-0.5.0/test/dune000066400000000000000000000002011366415233300146360ustar00rootroot00000000000000(library (name test) (flags (:standard -safe-string)) (libraries mew_vi) (inline_tests) (preprocess (pps ppx_expect))) mew_vi-0.5.0/test/test.ml000066400000000000000000000000001366415233300152660ustar00rootroot00000000000000