Haskell -- brick を使ってみる 4

なんか できてしまった・・・

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Main(main) where

import Lens.Micro ((^.))
import Lens.Micro.TH (makeLenses)
import Lens.Micro.Mtl ((%=),zoom,use)
import Control.Monad (void, forever)
import Control.Concurrent (threadDelay, forkIO)
import qualified Graphics.Vty as V

import Brick.BChan (newBChan, writeBChan)
import Brick.Main (App(..), showFirstCursor, customMain, halt, vScrollBy
                  ,ViewportScroll, viewportScroll)
import Brick.AttrMap (attrMap)
import Brick.Types (Widget, EventM, BrickEvent(..), ViewportType(..))
import Brick.Widgets.Core (str, (<+>), (<=>), hLimit, vLimit, viewport)
import Brick.Widgets.Edit as E
import Brick.Widgets.Center as C

data Name = Edit | View
          deriving (Ord, Show, Eq)

data CustomEvent = Counter deriving Show

data St = St { _counter :: Int
             , _stlog :: String 
             , _edit :: E.Editor String Name
             }

makeLenses ''St

drawUI :: St -> [Brick.Types.Widget Name]
drawUI st = [ui]
    where a = (str $ st^.stlog)
          v = viewport View Vertical a
          e1 = E.renderEditor (str.unlines) True (st^.edit)
          ui = C.center $
            (str "Input : " <+> (hLimit 50 $ vLimit 5 e1)) <=>
            str " " <=>
            (str "Log : "  <+> (hLimit 50 $ vLimit 5 v)) <=>
            str " " <=>
            str "Esc to quit."

vpScroll :: ViewportScroll Name
vpScroll = viewportScroll View

appEvent :: BrickEvent Name CustomEvent -> EventM Name St ()
appEvent e =
    case e of
        VtyEvent (V.EvKey V.KEsc []) -> halt
        AppEvent Counter -> do
          counter %= (+1)
          i <- use counter
          stlog %= (++"Count is: "++(show i)++"\n")
          vScrollBy vpScroll 1
        ev -> zoom edit $ E.handleEditorEvent ev 


initialState :: St
initialState = St { _counter = 0
                  , _stlog = ""
                  , _edit = E.editor Edit Nothing ""
                  }

theApp :: App St CustomEvent Name 
theApp =
    App { appDraw = drawUI
        , appChooseCursor = showFirstCursor
        , appHandleEvent = appEvent
        , appStartEvent = return ()
        , appAttrMap = const $ attrMap V.defAttr []
        }

main :: IO ()
main = do
    chan <- newBChan 1

    void $ forkIO $ forever $ do
        writeBChan chan Counter
        threadDelay 1000000

    let buildVty = V.mkVty V.defaultConfig
    initialVty <- buildVty
    void $ customMain initialVty buildVty (Just chan) theApp initialState

ふう・・・

Log&Edit

Haskell -- brickを使ってみる 3

今回の目標はーー
ログ表示部分と エディタ部分を 1つづ つくり タイマーで 1秒ごとにログを表示していくもの
をつくること

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Main(main) where

import Lens.Micro ((^.))
import Lens.Micro.TH (makeLenses)
import Lens.Micro.Mtl ((%=),zoom)
import Control.Monad (void, forever)
import Control.Concurrent (threadDelay, forkIO)
import qualified Graphics.Vty as V

import Brick.BChan (newBChan, writeBChan)
import Brick.Main (App(..), showFirstCursor, customMain, halt)
import Brick.AttrMap (attrMap)
import Brick.Types (Widget, EventM, BrickEvent(..))
import Brick.Widgets.Core (str, (<+>), (<=>), hLimit, vLimit)
import Brick.Widgets.Edit as E
import Brick.Widgets.Center as C

data Name = Edit
          deriving (Ord, Show, Eq)

data CustomEvent = Counter deriving Show

data St = St { _stCounter :: Int
             , _edit :: E.Editor String Name
             }

makeLenses ''St

drawUI :: St -> [Brick.Types.Widget Name]
drawUI st = [ui]
    where a = (str $ "Counter value is: " <> (show $ st^.stCounter))
          e1 = E.renderEditor (str.unlines) True (st^.edit)
          ui = C.center $
            (str "Input : " <+> (hLimit 50 $ vLimit 5 e1)) <=>
            str " " <=>
            a <=>
            str " " <=>
            str "Esc to quit."


appEvent :: BrickEvent Name CustomEvent -> EventM Name St ()
appEvent e =
    case e of
        VtyEvent (V.EvKey V.KEsc []) -> halt
        AppEvent Counter -> do
          stCounter %= (+1)
        ev -> zoom edit $ E.handleEditorEvent ev 


initialState :: St
initialState = St { _stCounter = 0
                  , _edit = E.editor Edit Nothing ""
                  }

theApp :: App St CustomEvent Name 
theApp =
    App { appDraw = drawUI
        , appChooseCursor = showFirstCursor
        , appHandleEvent = appEvent
        , appStartEvent = return ()
        , appAttrMap = const $ attrMap V.defAttr []
        }

main :: IO ()
main = do
    chan <- newBChan 1

    void $ forkIO $ forever $ do
        writeBChan chan Counter
        threadDelay 100000

    let buildVty = V.mkVty V.defaultConfig
    initialVty <- buildVty
    void $ customMain initialVty buildVty (Just chan) theApp initialState

まだ目標は達成されてゐない
カウンターの値を 一行ごとに表示して スクロールさせていきたいんだよね
でも 我ながら 一應動くものをつくれて 満足してゐるよん〜
他のデモを見ていきながら 目標の實現を目指さうかな

Haskell -- ちょっと Lens の お勉強 ++ vty って何 ++ ちょっと改良 ++ ちょっと實驗

brick モジュールを試してみてゐた

Haskell -- brick を使ってみる - あしひきの

Haskell -- brick を使ってみる 2 - あしひきの

その中に どちらも

makeLenses ''St

といふ記述があり この Lens といふやつが 何かしてることは明らかだった
なので ちょっと Lensについて 調べてみた(「Haskell入門」)

data St =
    St { _focusRing :: F.FocusRing Name
       , _edit1 :: E.Editor String Name
       , _edit2 :: E.Editor String Name
       }

makeLenses ''St

なんとなく理解したのは
アンダーバーをつけて レコードを定義し (_focusRing とか _edit1 とか) makeLenses をすると

st^.focusRing

のやうにして 「^.」を使って データにアクセスできるよってことみたいだ
ってこれだけかいっ!
ただ CustomEventDemoのところで見たやうに
%= とか .= とかいったやつも Lensが提供する機能みたいで
變數の代入っぽく書くコードを自然につくれてゐる といふすごさはあるし
たぶん もっと色んなデータ構造を経験すれば そのすごさが もっと實感できるんだらう

あと vty についても 氣にはなってゐたので 一應調べてみた
Hackage のタイトル "A simple terminal UI library" で これが何なのか 少しは分かった
ターミナルの表示関連とかをするやつなんだな〜とか(めっちゃてきとー)

それと CustomEventDemo を
ただ カウンターだけ表示するものにしてみた

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Main(main) where

import Lens.Micro ((^.))
import Lens.Micro.TH (makeLenses)
import Lens.Micro.Mtl ((%=))
import Control.Monad (void, forever)
import Control.Concurrent (threadDelay, forkIO)
import qualified Graphics.Vty as V

import Brick.BChan (newBChan, writeBChan)
import Brick.Main (App(..), showFirstCursor, customMain, halt)
import Brick.AttrMap (attrMap)
import Brick.Types (Widget, EventM, BrickEvent(..))
import Brick.Widgets.Core(str)

data CustomEvent = Counter deriving Show

data St = St { _stCounter :: Int }

makeLenses ''St

drawUI :: St -> [Widget ()]
drawUI st = [a]
    where a = (str $ "Counter value is: " <> (show $ st^.stCounter))

appEvent :: BrickEvent () CustomEvent -> EventM () St ()
appEvent e =
    case e of
        VtyEvent (V.EvKey V.KEsc []) -> halt
        AppEvent Counter -> stCounter %= (+1)
        _ -> return ()

initialState :: St
initialState = St { _stCounter = 0 }

theApp :: App St CustomEvent ()
theApp =
    App { appDraw = drawUI
        , appChooseCursor = showFirstCursor
        , appHandleEvent = appEvent
        , appStartEvent = return ()
        , appAttrMap = const $ attrMap V.defAttr []
        }

main :: IO ()
main = do
    chan <- newBChan 10

    void $ forkIO $ forever $ do
        writeBChan chan Counter
        threadDelay 1000000

    let buildVty = V.mkVty V.defaultConfig
    initialVty <- buildVty
    void $ customMain initialVty buildVty (Just chan) theApp initialState

シンプルなタイマーだよね これって
んで ちょっと實驗した

newBChan 10

のところを

newBChan 1

に變更した
結果ーーー普通に動いた
次に

newBChan 0

とした
そしたらーー
カウンタ が 0 のまま 動かなくなった
ふむふむーーなるほどねー(全然わかってない)
ま そのうち考へやう
てなことで

15時33分

まう 行く時間だ
曇りガラスの向かうから 真冬の太陽が優しく誘ふ

昨日心の中でした約束を 僕は破るよ
お互ひ様だね これで

寒い風は きっと 温もりの幸せのためにあるんだ
夕日が照らす 雲は なんで あんなに きれいなんだらう

いつもの道のりなのに こんなにも長く感じる
でも今 すべてが 暖かく美しい

會ひに行くよ
今から すぐ行くよ
待ってゐてくれてるから
居てほしいと 言ってくれたから

會ひに行くよ
待ってて すぐ行くよ
一緒にゐたいから
これからも 君と歩んでゐたいから

ーーーーー

それなりに 叩かれ慣れてはゐる
辛いことだって 受け入れてみせる

でも 今は その時じゃないだらう
君のゐない世界は まだ

なにも食べてゐないんだ だって
その意味を 全然 感じられなかったから

だけど それも まう 終はりだね
世界のすべてが 微笑みながら 見守ってくれてる

會ひに行くよ
今から すぐ行くよ
君は何も 惡くない
大丈夫 そのままで いいんだよ

會ひに行くよ
待ってて すぐ行くよ
僕だって 弱いのさ
それでも 幸せを 見つけにいかう

會ひに行くよ
今から すぐ行くよ
待ってゐてくれてるから
居てほしいと 言ってくれたから

會ひに行くよ
待ってて すぐ行くよ
一緒にゐたいから
これからも 君と歩んでゐたいから

Haskell -- brick を使ってみる 2

今回試すのは CustomEventDemo.hs といふ brick のデモだ
前回と同様に stack を使って 走らせてみた

CustomEventDemo

コードの全貌は以下

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Main where

import Lens.Micro ((^.))
import Lens.Micro.TH (makeLenses)
import Lens.Micro.Mtl
import Control.Monad (void, forever)
import Control.Concurrent (threadDelay, forkIO)
import qualified Graphics.Vty as V

import Brick.BChan
import Brick.Main
  ( App(..)
  , showFirstCursor
  , customMain
  , halt
  )
import Brick.AttrMap
  ( attrMap
  )
import Brick.Types
  ( Widget
  , EventM
  , BrickEvent(..)
  )
import Brick.Widgets.Core
  ( (<=>)
  , str
  )

data CustomEvent = Counter deriving Show

data St =
    St { _stLastBrickEvent :: Maybe (BrickEvent () CustomEvent)
       , _stCounter :: Int
       }

makeLenses ''St

drawUI :: St -> [Widget ()]
drawUI st = [a]
    where
        a = (str $ "Last event: " <> (show $ st^.stLastBrickEvent))
            <=>
            (str $ "Counter value is: " <> (show $ st^.stCounter))

appEvent :: BrickEvent () CustomEvent -> EventM () St ()
appEvent e =
    case e of
        VtyEvent (V.EvKey V.KEsc []) -> halt
        VtyEvent _ -> stLastBrickEvent .= (Just e)
        AppEvent Counter -> do
            stCounter %= (+1)
            stLastBrickEvent .= (Just e)
        _ -> return ()

initialState :: St
initialState =
    St { _stLastBrickEvent = Nothing
       , _stCounter = 0
       }

theApp :: App St CustomEvent ()
theApp =
    App { appDraw = drawUI
        , appChooseCursor = showFirstCursor
        , appHandleEvent = appEvent
        , appStartEvent = return ()
        , appAttrMap = const $ attrMap V.defAttr []
        }

main :: IO ()
main = do
    chan <- newBChan 10

    void $ forkIO $ forever $ do
        writeBChan chan Counter
        threadDelay 1000000

    let buildVty = V.mkVty V.defaultConfig
    initialVty <- buildVty
    void $ customMain initialVty buildVty (Just chan) theApp initialState

元の リポジトリにあるコードを ちょっと改變した (バージョンが十分新しいので cppを使ふ部分は なくて良いと判断した)
とてもシンプルな デモ画面だが Counter value is:
の數字が 一秒ごとに 1ずつ 増へていく

data CustomEvent = Counter deriving Show

これは それらしく言へば CustomEvent型の データコンストラクタ が Counter といふ名前で定義されてゐる といふことでいいのかな

data St =
    St { _stLastBrickEvent :: Maybe (BrickEvent () CustomEvent)
       , _stCounter :: Int
       }

makeLenses ''St

状態を表すStは 要素が二つあって ひとつは明らかに カウントを数えるものだが 最初の要素は 「最後のbrickイベント」といふ名前になってゐる
これが イベントを受けとったりするやつなのだらうか
makeLenses のところの理解は 後回しにしやう〜(前回も出てきたけど)
まあ ちょっと 軽く考へておくと Lens(讀みは レンズ でいいのかな) といふのは
Haskell入門」の267ページによると
「複雑なデータ構造への効率的なアクセス」
をするためのものーーださうだ
よし! あとで 勉強しよー
といふわけで 今は無視

drawUI :: St -> [Widget ()]
drawUI st = [a]
    where
        a = (str $ "Last event: " <> (show $ st^.stLastBrickEvent))
            <=>
            (str $ "Counter value is: " <> (show $ st^.stCounter))

str といふのは 前回も出てきたが スルーしてゐた
まあ 文字表示に關するものだらうことは予想できるのだが
ちょっと 見ておくか
import文から分かるやうに str は
import Brick.Widgets.Core モジュールの函數のはずだ
Hackage で確認する

str :: String -> Widget n

となってゐる
やはり 文字列を取って ウイジットなるものを返す函數だ
確か brickの説明にちょろっと 書いてあったと思ふのだが
この brickといふやつは ウイジットといふものの集合體で アプリを實行してゐる といふことらしいから
brickを使ふからには この ウイジットヘの理解が必須なのだらう (まだ全然理解してないけど)
デモ画面を見ると
Last event: Just (AppEvent Counter)
となってゐる
drawUI によれば この Just (AppEvent Counter)
といふのは st^.stLastBrickEvent つまり 状態Stの中の stLastBrickEvent の値を表してゐる
ふーん AppEvent といふ ブリックのイベント があって それが Counter っていふ名前なのね〜 ていふ理解かな 今は

appEvent :: BrickEvent () CustomEvent -> EventM () St ()
appEvent e =
    case e of
        VtyEvent (V.EvKey V.KEsc []) -> halt
        VtyEvent _ -> stLastBrickEvent .= (Just e)
        AppEvent Counter -> do
            stCounter %= (+1)
            stLastBrickEvent .= (Just e)
        _ -> return ()

前回もあった appEvent函數だ
case文の中にある最初の行は Escキーを押すイベントだ
haltは中断ってことだろう (前回もさうだったと思ふ) ちなみに %= とか .= とかの謎オペレータは
import Lens.Micro.Mtl
でインポートされたやうだ
まあ 明らかに %= (+1) は stCounterに1を加えて代入
.=は 單純に置き換へ といふのが見てとれる
しかし あれだな〜 ここで 状態變數(?)を操作できるんだな〜

initialState :: St
initialState =
    St { _stLastBrickEvent = Nothing
       , _stCounter = 0
       }

theApp :: App St CustomEvent ()
theApp =
    App { appDraw = drawUI
        , appChooseCursor = showFirstCursor
        , appHandleEvent = appEvent
        , appStartEvent = return ()
        , appAttrMap = const $ attrMap V.defAttr []
        }

initialStateについては 明解だらう
theApp の カーソルの定義らしきものが showFirstCursor になってゐる
前回は 何だっけ appCursor といふのになってゐて
それが フォーカスリングカーソル みたいなものによって定義されてたな〜
たぶん今回は 「普通の」カーソルっていふことか?
デモ画面には カーソルらしきものは表示されてゐないが・・・
AttrMapの部分は attrMapといふ函數を使ってゐる (Brick.AttrMap をインポートしてゐる)
今は ここは深入りしない

main :: IO ()
main = do
    chan <- newBChan 10

    void $ forkIO $ forever $ do
        writeBChan chan Counter
        threadDelay 1000000

    let buildVty = V.mkVty V.defaultConfig
    initialVty <- buildVty
    void $ customMain initialVty buildVty (Just chan) theApp initialState

さすがに 最初の newBChan 10 は氣になるので 少し調べた
モジュール Brick.BChan
BChan といふデータは
BChan is an abstract type representing a bounded FIFO channel.
と書かれてゐる
有限の FIFOチャネルを表す?
FIFO って First In First Out ってこと? 情報技術者試験の勉強で出てきたやうな・・・
スタックみたいなもんだと 考へていいのかな・・・
んで newBChan 10 といふのは そのチャネルを10個まで持てるやうなデータ BChanをつくるってこと?

void や forever は Control.Monad モジュールから來てゐる
void は 結果を返さないよってことだらう
forever は その名の通り ずっと繰り返せってこと だと思ふ
threadDelay の百万が 1秒にあたるんだ〜
てことは threadDelay の 1 は 百万分の1秒! すごいな〜

前回のやつと 今回のやつを うまく組み合はせて 例へば stLastBrickEvent の値を Input2に stCounter の値を Input1に表示したりできるかな〜
今度それに挑戦してみやうかな

Haskell -- brick を使ってみる

Haskell でつくられたゲームが

Haskell GameDev –

に載ってゐた

それぞれ 色々と面白いのだが

Swarm – Haskell GameDev

のやうな ターミナルアプリに魅かれ
これに使はれてゐる brick といふライブラリを試してみたくなった

github.com

ドキュメント類も色々充實してゐる感じなのだが 素直にインストラクションに従はず
デモを なんとなく 試してみることにした

ちなみに stack はインストールしてある前提で話を進める
(はじめてHaskellをインストールする場合 今では GHCup がおすすめだと思ってゐる)

stack new bdemo

などとして bdemo といふ名のディレクトリをつくったら
中の app ディレクトリ内にある Main.hs の中身を デモプログラムに置き換へる
あと bdemo ディレクトリ内の package.yaml ファイル に いくつかパッケージを追加する

dependencies:
 - base >= 4.7 && < 5
 - brick
 - microlens
 - microlens-th
 - microlens-mtl
 - vty

base >= 4.7 && < 5 といふのは 最初から書かれてゐるやつだ
その他は書いておかないと エラーになる

あとは

stack run

として ビルド 實行すればよい(すごい色々インストールされるから時間かかるけど)
今回使った デモコードは EditDemo.hs といふやつで

EditDemo
みたいな實行画面になる
さて 中身を見てみやうか

全體を一度載せる(まだ何だか分からん)

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE RankNTypes #-}
module Main where

import Lens.Micro
import Lens.Micro.TH
import Lens.Micro.Mtl
import qualified Graphics.Vty as V

import qualified Brick.Main as M
import qualified Brick.Types as T
import Brick.Widgets.Core
  ( (<+>)
  , (<=>)
  , hLimit
  , vLimit
  , str
  )
import qualified Brick.Widgets.Center as C
import qualified Brick.Widgets.Edit as E
import qualified Brick.AttrMap as A
import qualified Brick.Focus as F
import Brick.Util (on)

data Name = Edit1
          | Edit2
          deriving (Ord, Show, Eq)

data St =
    St { _focusRing :: F.FocusRing Name
       , _edit1 :: E.Editor String Name
       , _edit2 :: E.Editor String Name
       }

makeLenses ''St

drawUI :: St -> [T.Widget Name]
drawUI st = [ui]
    where
        e1 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit1)
        e2 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit2)

        ui = C.center $
            (str "Input 1 (unlimited): " <+> (hLimit 30 $ vLimit 5 e1)) <=>
            str " " <=>
            (str "Input 2 (limited to 2 lines): " <+> (hLimit 30 e2)) <=>
            str " " <=>
            str "Press Tab to switch between editors, Esc to quit."

appEvent :: T.BrickEvent Name e -> T.EventM Name St ()
appEvent (T.VtyEvent (V.EvKey V.KEsc [])) =
    M.halt
appEvent (T.VtyEvent (V.EvKey (V.KChar '\t') [])) =
    focusRing %= F.focusNext
appEvent (T.VtyEvent (V.EvKey V.KBackTab [])) =
    focusRing %= F.focusPrev
appEvent ev = do
    r <- use focusRing
    case F.focusGetCurrent r of
      Just Edit1 -> zoom edit1 $ E.handleEditorEvent ev
      Just Edit2 -> zoom edit2 $ E.handleEditorEvent ev
      Nothing -> return ()

initialState :: St
initialState =
    St (F.focusRing [Edit1, Edit2])
       (E.editor Edit1 Nothing "")
       (E.editor Edit2 (Just 2) "")

theMap :: A.AttrMap
theMap = A.attrMap V.defAttr
    [ (E.editAttr,                   V.white `on` V.blue)
    , (E.editFocusedAttr,            V.black `on` V.yellow)
    ]

appCursor :: St -> [T.CursorLocation Name] -> Maybe (T.CursorLocation Name)
appCursor = F.focusRingCursor (^.focusRing)

theApp :: M.App St e Name
theApp =
    M.App { M.appDraw = drawUI
          , M.appChooseCursor = appCursor
          , M.appHandleEvent = appEvent
          , M.appStartEvent = return ()
          , M.appAttrMap = const theMap
          }

main :: IO ()
main = do
    st <- M.defaultMain theApp initialState
    putStrLn "In input 1 you entered:\n"
    putStrLn $ unlines $ E.getEditContents $ st^.edit1
    putStrLn "In input 2 you entered:\n"
    putStrLn $ unlines $ E.getEditContents $ st^.edit2

はい わかりません
では まづ 最初の import文などは讀み飛ばして

data Name = Edit1
          | Edit2
          deriving (Ord, Show, Eq)

data St =
    St { _focusRing :: F.FocusRing Name
       , _edit1 :: E.Editor String Name
       , _edit2 :: E.Editor String Name
       }

あたりから見ていくか
Name は 單に名前のことだと分かる
St は状態なんだらうけど focusRing って何だろ?
edit1 とか edit2 は デモ画面にある 二つのエディタのことを指すんだらう

次の makeLenses ''St みたいなところは
TemplateHaskell といふ 言語拡張を使って 何かつくってるんだ みたいな理解をしてゐる
だって St の左に チョンチョンって ふたつついてるでしょ?
これが TemplateHaskell の特徴なんだよ きっと

drawUI :: St -> [T.Widget Name]
drawUI st = [ui]
    where
        e1 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit1)
        e2 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit2)

        ui = C.center $
            (str "Input 1 (unlimited): " <+> (hLimit 30 $ vLimit 5 e1)) <=>
            str " " <=>
            (str "Input 2 (limited to 2 lines): " <+> (hLimit 30 e2)) <=>
            str " " <=>
            str "Press Tab to switch between editors, Esc to quit."

drawUI ってからには 描画に関連してるんだらう
あっ! FocusRing でてきた
e1とe2 は エディタのことだらう
それに FocusRing がある ってことか?
ui のところは まさに Demo画面に描かれてゐる Input 1 とか Input 2 のことだな
色々位置などを設定してゐるのだらう
e1 とかで定義されてゐる

F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit1)

をちょと 見ていかう
F は インポート文の

import qualified Brick.Focus as F

で指定されてゐるやうに Brick.Focus のことだ
その中の withFocusRing といふ函數を使用してゐる
これを Hackage で確認しやう

withFocusRing :: (Eq n, Named a n) => FocusRing n -> (Bool -> a -> b) -> a -> b

となってる
FocusRing n といふのが フォーカスリング(多分 エディットする部分が黄色くなるやうなやつだと思ふ) (Bool -> a -> b) といふのが 真偽値と なんかを取って なんかを返す函數 (E.renderEditor (str .unlines) の部分)
a にあたるのが (st^.edit1)の部分

あぶないあぶない
ちょっと 深く見すぎた
次行ってみやう

appEvent :: T.BrickEvent Name e -> T.EventM Name St ()
appEvent (T.VtyEvent (V.EvKey V.KEsc [])) =
    M.halt
appEvent (T.VtyEvent (V.EvKey (V.KChar '\t') [])) =
    focusRing %= F.focusNext
appEvent (T.VtyEvent (V.EvKey V.KBackTab [])) =
    focusRing %= F.focusPrev
appEvent ev = do
    r <- use focusRing
    case F.focusGetCurrent r of
      Just Edit1 -> zoom edit1 $ E.handleEditorEvent ev
      Just Edit2 -> zoom edit2 $ E.handleEditorEvent ev
      Nothing -> return ()

最初の行は 型注釈の部分 次からの appEvent 三つは 何か對應するキーを押すと 何かする ってやつだらう
focusNext とか 分かりやすい名前が付いてゐる
最後は 何か focusGetCurrent といふものの値によって 場合分けしてるな
今 ここに フォーカスしてゐるなら これをしろ みたいなことを 言っているんだらう・・・

initialState :: St
initialState =
    St (F.focusRing [Edit1, Edit2])
       (E.editor Edit1 Nothing "")
       (E.editor Edit2 (Just 2) "")

theMap :: A.AttrMap
theMap = A.attrMap V.defAttr
    [ (E.editAttr,                   V.white `on` V.blue)
    , (E.editFocusedAttr,            V.black `on` V.yellow)
    ]

最初の状態を定義してゐるのは明らかだ
Edit1 Nothing
Edit 2 (Just 2)
とある
Nothing は 何行書いてもスクロールするエディタにするよ ってことだらう
Just 2 は 2行までに限定する といふことだ

あと theMap では 明らかに 白 青 黄色 といふ指定をしてゐるのがわかる
といふことは これが フォーカスされてゐるときと されてゐないときの色についてのデータだ!

appCursor :: St -> [T.CursorLocation Name] -> Maybe (T.CursorLocation Name)
appCursor = F.focusRingCursor (^.focusRing)

なんだらう これは
カーソルについての指定さらうか
フォーカスリング カーソルといふのが あるんだらう (たぶん)

theApp :: M.App St e Name
theApp =
    M.App { M.appDraw = drawUI
          , M.appChooseCursor = appCursor
          , M.appHandleEvent = appEvent
          , M.appStartEvent = return ()
          , M.appAttrMap = const theMap
          }

アプリ全体の定義だらう
描画は drawUI で
イベント処理は appEvent で スタートイベントは 特に指定がない といふことだらうか
あと 先程指定されてゐた フォーカス色の theMap が入ってゐる

main :: IO ()
main = do
    st <- M.defaultMain theApp initialState
    putStrLn "In input 1 you entered:\n"
    putStrLn $ unlines $ E.getEditContents $ st^.edit1
    putStrLn "In input 2 you entered:\n"
    putStrLn $ unlines $ E.getEditContents $ st^.edit2

メイン函數
これが アプリを實行すると 最初に實行される
do 以下の一行目が Demo画面を表示させてゐる部分だ
Escキー を押すと エディタに入れてゐた情報を ターミナルに表示して終了するから
二行目以下は その部分だらう

うーん
まだちゃんと理解はできてゐないけど
Input2 の方にコマンドを打って リアルタイムに Input1 にログを表示させるのに向いてゐると思ふ
ちょうど 今 やってゐる kdbtl で さういふことをしたいと思ってゐたので これがそのまま使へるかも!!?
あとは タイマーイベントの部分を どう書くか 調べてみたい
とりあへず 今は こんな感じで

縦書き vim と vim9 script

最近知ったのだが(おせーよww)
vim9script といって vimscriptの改良版が實装されてきてゐる
「きてゐる」
といったのは それがまだ 發展途上といふことだからだ

この vim9scriptを使ふには vimのバージョンが 8.2以降になってゐればよいらしく
コードの始めに
vim9script
と書けば vim は それ以降のコードを 新しいvim9scriptの言語として認識するらしい

といふことで
試しに vim9 scriptを使って tatevimを實装してみやうと試みた
色々試行錯誤の末に 何とか動くやうになり
今はかなり快適に動作してゐる

通常のvimscriptに比べて vim9script は とにかく動作が速いといふのが賣りのやうだったが
縦書き文章をスクロールさせてみたところ
もともとの tatevimでは 文章量が多いと スクロールが かなり カクカクして遅くなってゐたのだが
tate9vim の方では 相当スムーズなスクロールが見られた

github.com

今後は vim9script を使ふことが多くなりさうだ