SDL2 イメージ操作

コードを載せる

import SDL.Video (Renderer)
import SDL.Video.Renderer (Surface,Texture,SurfacePixelFormat(..),Rectangle(..),PixelFormat(..)
                          ,rendererDrawColor,clear,destroyTexture
                          ,createTextureFromSurface,copyEx,present
                          ,lockSurface,unlockSurface,surfacePixels,surfaceFormat,createRGBSurfaceFrom)
import SDL.Vect (Point(P),V2(..),V4(..))
import SDL.Internal.Numbered (fromNumber)
import qualified SDL.Raw.Types as SDLT
import Foreign.Ptr (castPtr)
import Foreign.ForeignPtr (newForeignPtr_)
import Foreign.Storable (peek)
import qualified Data.Vector.Storable.Mutable  as VM
import System.Random.Shuffle (shuffleM)
import Data.Word (Word8)

draw :: Renderer -> [Surface] -> IO ()
draw re imageS = do
  imageTO <- mapM (createTextureFromSurface re) imageS
  let imageS0 = head imageS

  -- メモリ操作のため surfaceをロックする
  lockSurface imageS0

  -- surfaceのPixelForat情報があるポインタを取得
  SurfacePixelFormat pointerPixFormat <- surfaceFormat imageS0

  -- ポインタ(アドレス)の情報を読む
  surPixFormat <- peek pointerPixFormat

  -- このPixelFormatは SDL.Raw.Type で定義されてゐるもので
  -- pixelFormatFormat といふデータに SDL.Video.Renderer で定義される PixelFormat の情報が
  -- Word32といふ型で格納されてゐる
  -- これは print で表示すると 整数なのだが これを fromNumber で PixelFormatの形式に變換する
  let sPixFormat = fromNumber (SDLT.pixelFormatFormat surPixFormat) :: PixelFormat

  -- イメージのsurfaceのピクセル情報が格納されてゐるアドレスを得る(ポインタ)
  pointer0 <- surfacePixels imageS0

  -- データからsurfaceをつくるためには IOVector Word8 といふ型でデータを保持しなければならない
  -- IOVector は MVector (PrimState IO) と同義であり
  -- surfacePixels函數で得られる Ptr () を castPtr で Ptr Word8 へ變換し
  -- Ptr Word8 を newForeignPtr_ で ForeignPtr Word8 へ變換する
  frPointer <- newForeignPtr_ (castPtr pointer0)

  -- イメージの1ピクセルは 4つのWord8(0〜255)のデータで表される
  -- Word8の総数は 4*64*64 となる (64*64ピクセルだから)
  -- この長さを ポインタを起点としたアドレスから讀み込み MVector型のデータを得る
  let mvector = VM.unsafeFromForeignPtr0 frPointer (4*64*64) :: (VM.MVector (VM.PrimState IO) Word8)
  -- ロックを解除
  unlockSurface imageS0

  -- mvector のクローンを作成
  -- mvectorを書き替えると imageS0 の内容がそのまま書き替はる
  -- surface が變更されると 表示してゐたtextureも變はる
  mvector2 <- VM.clone mvector

  -- 4x4のピクセルを單位として ランダムにピクセル情報を變更する
  -- 水の中にあるやうな効果を出すことができた
  mapM_ (\y -> mapM_ (\x -> sfl4x4 mvector2 (V2 x y)) [0..15]) [0..15]

  -- MVector Word8 型のデータ, surfaceのサイズ, ピッチ(1行のピクセル(64px)のバイト數),
  -- そして 先程求めたsPixFormat (このイメージはABGR8888だった)を使い 新たなsurfaceをつくる
  newImageS0 <- createRGBSurfaceFrom mvector2 (V2 64 64) (4*64) sPixFormat

  newImageT <- createTextureFromSurface re newImageS0
  let imageTextures = imageTO ++ [newImageT]
  initDraw re
  imageDraw re imageTextures 
  mapM_ destroyTexture imageTextures 
  present re

きっかけは ブロックを使った操画(ゲーム)を考へてゐて そのブロックのイメージを 「もうちょっと柔らかいものにしたいな〜」 といふことだった
BABA IS YOU といふ 超名作ゲームのブロックの ほわほわする感じが すごく良くて そんなやうなイメージが創れたらいいな〜 といふ 安直な思ひがあった
しかし おそらく それをやるためには イメージを大量に用意して アニメーションさせるのではなく イメージそれ自體にアクセスして その情報を變へるようなことが必要だ と考へた
イメージにアクセスするためには SDL2の場合 surfaceの情報を取得する必要がある
SDL.Video.Renderer のドキュメントを見ると

surfacePixels :: MonadIO m => Surface -> m (Ptr ())

とあり
Obtain the pointer to the underlying pixels in a surface. You should bracket this call with lockSurface and unlockSurface, respectively.
と説明されてゐる
要は 「ほら 情報にアクセスできるポインタだよ これで後はよろしく」
みたいな感じだ
何の例もない
どうすればいいか どこにも書いてゐない

ちなみに 仮にデータが得られたとして それを どう使ふのかといへば

createRGBSurfaceFrom
  :: (Functor m, MonadIO m)
=> IOVector Word8  --(The existing pixel data)
 -> V2 CInt  --(The size of the surface)
 -> CInt  --(The pitch - the length of a row of pixels in bytes)
 -> PixelFormat  --(The bit depth,red,green,blue and alpha mask for the pixels)
 -> m Surface

最終的に IOVector Word8 といふ型でデータが供給できればよい
んで Data.Vector.Storable.Mutable のドキュメントを見ると

unsafeFromForeignPtr0
  :: Storable a
=> ForeignPtr a
 -> Int
 -> MVector s a

これが 私の見たところ このモジュール内で ポインタからMVector をつくる唯一(offsetなしなら)の方法だった
ちなみに つくりたいのは IOVector Word8 である
ここで

type IOVector = MVector RealWorld

と書いてある
また
MVector s a の s は (PrimMonad m => PrimState m) であり
PrimState IO が RealWordl である(らしい) https://stackoverflow.com/questions/8959226/constructing-iovector-from-storable-mvector

この邊の理屈は 正直よく分からんかった
が 先程の

unsafeFromForeignPtr0 :: Storable a => ForeignPtr a -> Int -> MVector s a

の a を Word8 として s を PrimState IO とすれば 返り値の型は MVector RealWorld Word8
すなはち IOVector Wrod8 となる
つまり ForeignPtr Word8 型のポインタと データサイズ(Int)を與へることで
目的の型をもつデータがゲットできる といふわけだ
そこで Ptr () を ForeignPtr Word8 にしなければならない
Foreign.Ptr モジュールのドキュメントによると

castPtr :: Ptr a -> Ptr b

といふのがある
これで () を Word8にできるのでは?
といふことで castPtr を適用し
Foreign.ForeginPtr モジュールにある

newForeignPtr_ :: Ptr a -> IO (ForeignPtr a)

を適用して めでたく ForeginPtr Word8 をつくりあげることができた

さて 一番説明がやっかいさうなのは PixelFormat だ
SDL.Video.Renderer に PixelFormatは定義されてゐて
RGB888 や RGBA8888 ABGR8888 BGRA8888 など いくつものフォーマットがある
最終目的を思ひ出して欲しい
createRGBSurfaceFrom に必要なデータを揃えることだった
IOVector Word8 はゲットしたし イメージサイズも分かってゐる
ピッチといふのは ピクセル一行分が 何バイトに相當するかを示すもので
1ピクセルにつき4バイト必要な RGBA8888 や ABGR8888などは (横の幅(px)) × 4 で求まる
殘るは PixelFormat だけなのだが
色々と適當に フォーマットを試してみて できたイメージの結果を確認していくと
これは ABGR8888 だな といふのが分かる
それ以外のものは 明らかに色が もとのイメージと異なったからだ
(といふことは ある意味 PixelFormatだけ變へてやれば 瞬時に画像の色合いを變へることができるわけだ)
けれども これから色々なイメージを讀み込まうと思ってゐる場合
常に ABGR8888とは限らないだらう
だから イメージの情報を讀み取ることで PixelFormatの情報を得たいわけだ
これに關して

surfaceFormat :: MonadIO m => Surface -> m SurfacePixelFormat

といふ函數が SDL.Video.Renderer で用意されてゐる
しかし 返ってくるのは なぜか PixelFormat ではなく SurfacePixelFormat といふ謎なやつだ
そこの部分のドキュメントを見ると

newtype SurfacePixelFormat

Constructors:
SurfacePixelFormat (Ptr PixelFormat)
となってゐる
Ptrだとおおお?!? すなはち surface の PixelFormat を知りたくて尋ねたら 「ハイ! アドレスだよ! 見といてね?」 なんて言はれ ポカーン とするしかない状況が生じたのだ

ここで メモリのアドレスにあるデータを見る方法が必要になってくる
これは 色々検索して

https://naohaq.github.io/

を見たときに分かったのだが
「peek」といふものだ
Foreign.Storable モジュールに書かれてゐる
やった〜!
といふことで peekを使ひ その内容をPixelFormatとして createRGBSurfaceFrom に入れてみた
が・・・エラー
なんやねん といふことで peekした内容を表示してみたら

PixelFormat {pixelFormatFormat = 376840196, pixelFormatPalette = 0x0000000000000000, pixelFormatBitsPerPixel = 32, pixelFormatBytesPerPixel = 4, pixelFormatRMask = 255, pixelFormatGMask = 65280, pixelFormatBMask = 16711680, pixelFormatAMask = 4278190080}

な〜〜?! SDL.Video.Renderer のドキュメントで
SurfacePixelFormat (Ptr PixelFormat) とあるうち PixelFormatのリンクをクリックすると
全然別のドキュメントに飛ばされる
それが
SDL.Raw.Types だ
ここで定義される PixelFormatは

data PixelFormat

Constructors:

PixelFormat  
pixelFormatFormat :: !Word32     
pixelFormatPalette :: !(Ptr Palette)     
pixelFormatBitsPerPixel :: !Word8    
pixelFormatBytesPerPixel :: !Word8   
pixelFormatRMask :: !Word32  
pixelFormatGMask :: !Word32  
pixelFormatBMask :: !Word32  
pixelFormatAMask :: !Word32

この内容は さきほど プリントさせたものと合致してゐる
んで どないしろっちゅーねん!
まう一度 SDL.Video.Renderer にある PixelFormat のドキュメントとにらめっこした
何か このふたつの PixelFormatには 關聯性がある筈なんだ・・・
ん?

 FromNumber PixelFormat Word32
 ToNumber PixelFormat Word32

Word32?
さういへば SDL.Raw.Types のPixelFormat の最初に Word32の型があったな・・・
それは 意味不明な數字だった・・・
そして fromNumber といふ函數があるらしい・・・
まさか あの數字が ABGR8888 を表はしてるとか???
fromNumber函數は モジュール SDL.Internal.Numbered にあった
これを SDL.Raw.Types の PixelFormat 内にある PixelFormatFormat の數字に適用すると・・・
動いた!!!

Youtube のストリーム配信で 説明しやうとやってみた

www.youtube.com

コードは githubに載せてゐる

github.com

一言いはせてほしい
Haskell は 冒険だ!

StateTだとぉ〜!

ちょっと どうやってまとめていいか 整理できてゐないのだが
とにかく今 感動してゐるので 書き殘しておきたい

ゲーム制作でなくとも アプリかなんかつくるんであれば
色々と画面に表示(描画)しながら 何らかの「状態」を變化させていくだらう

Haskell では その「状態」を表す データ型をつくって それを函數に渡しながら そのデータ型に基いた 新たなデータをつくり 「状態」を變化させていったりできる

だが つねに 「今」の状態を示すデータを 受けとって それをもとに 新しい「状態」データを作成し また それを次の函數に渡していく〜といふのは なんといふか 「必ずやる」ことなのに 毎回函數に加へなくてはならない情報であって やはり 「なんか もっと うまいやり方はないのかな〜」などと思ってしまふ

そんで 本などを見ると
State s モナド なんていふものが書いてある
これを利用すると 「状態」を簡単に扱へる といふことらしいのだが
いまいち 私には その具体的な使ひ方が 腑に落ちなかった

とにかく使ってみやう といふことで SDL2を使ってイメージの讀み込みと ウインドウ表示だけをするコードを書き 色々と實驗してみた

しかし うまくいかない

State s モナド は (State 状態 結果) を返す函數と 初期の状態を runState といふ函數に與へることで機能する
だけど runState に與へたい函數は 単に状態を變化させる といふだけでなく 様々な出力に對應する つまり IO型を扱ふ函數であってほしい〜といふか さうでなくては困るのだ

それを念頭に Hackage の Control.Monad.State.Strict のドキュメントとにらめっこしてゐて
StateT についての項目をながめてゐた

newtype StateT s (m :: * -> *) a

s - The state.
m - The inner monad.
と書かれてゐる
もしかして m を IOにできるってこと?
StateT モナドを實行する runStateT といふ函數もある
・・・もしかして・・・
コードを書いてみた (一部を載せる)

module App(appMain) where

import Control.Monad.State.Strict (runStateT)
import MySDL.Load (loadFiles)
import MySDL.Loop (loop)
import MySDL.Init (withInit)
import MySDL.Video (withVideo)
import MyData (initWorld)

appMain :: IO ()
appMain =
  withInit $ do
    imageS <- loadFiles
    withVideo $
      \renderer -> runStateT (loop renderer imageS) initWorld
module MySDL.Loop where

import Control.Monad.State.Strict (StateT,get,put)
import Control.Monad.IO.Class (liftIO)
import SDL.Video.Renderer (Renderer,Surface)
import SDL.Time (delay)
import MyData (World(..), Input(..),  delayTime)
import Event (inputEvent)

loop :: Renderer -> [Surface] -> StateT World IO ()
loop re imageS = do
  inp <- liftIO inputEvent
  wld <- get
  let newWorld = wld{abc=abc wld + 1}
  liftIO (print (abc wld))
  put newWorld
  delay delayTime
  if inp==QIT then return () else loop re imageS

今回實驗で使った「状態」は Worldといふ型で abc=0, def=1 といふ値を初期値とする 單純なものだ
これを メインループ上で取り出し abcに1を加へてターミナルに表示させることができた
ここで loop函數は 状態を表す引數をもってゐない
それでも get函數で 状態を取得し put函數で 状態を書き込むことができる! この函數のなかで IOアクションを實行するときは liftIO を適用すればよい

これで 「状態」をかなりスマートに扱ふことができるやうな氣がする〜

コード全体は

github.com

Haskell 多重リスト

この言葉が 廣く使はれてゐるのかは知らない
多次元リスト といふ言葉も ネットに載ってゐる
リストの中に リストがある といった意味合ひで使はれるのだらうとは思ふ
例へば
[[1,2],[1,2,3],[4],[6,5,4,3]]
といったものだらう
だが 私としては 「多重リスト」といふ言葉を
「リストの中に 「リストのリスト」も 「リストのリストのリスト」も含むやうなリスト」
の意味で使ひたい
いや 既にそれを表す用語があるのなら それを使ふのだが 今私はそれを知らないから

つまり 多重リスト といふのは 私にとって
[1, [2,3], [3,[4,5]], [[[6,7],[8]]]]
のやうに書けるやうなものだ
多分 Haskell では かういうのは リストと言はないだらうが

これは おそらく 構造としては 「木」の構造だから 「木」と呼ぶべきなのだらう
けれども 上の表記は それなりに データの構造が分かりやすく可視化できてゐると思ふ
Data.Tree モジュールを利用して この構造を實装し 可視化しやうとしたのが 私のつくった MyTreeモジュールだ

github.com

module MyTree (Elm(..), showF, addElem) where

import Data.Tree 
import Data.List (intercalate)

data Elm a = El a Int Int

showT :: Show a => Tree a -> String 
showT (Node x []) = show x
showT (Node x tr) = "["++show x++", "++tail (showF tr)

showF :: Show a => Forest a -> String 
showF [] = ""
showF tr = "[" ++ intercalate ", " (map showT tr) ++ "]"

addElem :: Elm a -> Forest a -> Forest a 
addElem (El mn _ _) [] = [Node mn []]
addElem (El mn 0 0) fo = fo ++ [Node mn []]
addElem (El mn l r) fo
  | lng > l && l > 0 = let (h,(Node s sf):t) = splitAt (lng - l) fo
                           newNode = Node s (t++addElem (El mn l r) sf)
                        in h ++ [newNode]
  | r > 0 = let (it,lt) = (init fo,last fo)
                Node x subf = lt  -- last tree
             in if null subf then it ++ [lt, Node mn []]
                             else it ++ [Node x (addElem (El mn l r) subf)]
  | otherwise = fo ++ [Node mn []]
  where lng = length fo

addElem といふのは リストに 新たな要素を加へやうとしてゐる
Tree a といふ型のリストが Forest a と呼ばれるものだ
Forest に要素を加へていき showF で その内容を リストっぽく見せやうとしてゐる

Elm といふ型は 加へたい要素の型と
「今まで加へた要素の 最後のいくつを 今回のものといっしょにまとめるか」 「次に加へる要素の いくつを 今回のものといっしょにまとめるか」 といふ3つの情報を指定して 要素を加へやうといふためのものだ
ちなみに最後の情報は 別のコードから呼び出すときに使ふためのもので 今回 ghciでは利用しない

かういふことをする場合 このやうな「木」のデータ構造にする意義があるのではないかと思ふ

たとはば このモジュールをghci で読みこんで

> a = addElem (El 1 0 0) []
> b = addElem (El 2 0 0) a
> c = addElem (El 3 0 0) b
> d = addElem (El 4 0 0) c
> e = addElem (El 5 2 0) d
> f = addElem (El 6 1 0) e

としたとき showF f は
"[1, 2, [3, 4, [5, 6]]]" となる

水が教へてくれる

ネット で 「水 国語辞典」 などと調べると
「水」の意味などが載ってゐる
例へば goo辞書の 「意味」のところに 見出しとして

みず [みづ]

と書かれてゐる

小學校では いまだに 水の讀みを 「みず」と教へてゐる
では 「みづ」 と讀んだとき それは間違ひなのだらうか

いはゆる 「歴史的仮名使い」で書かれた文章は
日本語として 間違ひ とは言へない

けれども 「日常的」には使はれないので
さういふ文章で書かれたブログは 多くの人に 良い印象を與へない

なぜだらう?

そもそも 「歴史的仮名使ひ」で「わざわざ」ブログを書く人といふのは
どんな価値観 世界観を持ってゐるのだらうか

なぜ 昔は 「みづ」 と讀んで 今は 「みず」と讀むのだらう

なんで 昔は 「讀」 と書いてゐたのに 今は 「読」と書くのだらう

ただ 時代が變はって 「自然」に 価値観が 變はったから?

それとも 別の原因があるのかな?

ここには 「洗脳」と 「學び」との大きな違ひが 潜んでゐる

あなたが もし

「學校で習ふ知識が 正しい知識」

と信じ續け
學校で教はってゐない 學校で習ったものと違ふことを言ったりする人を
あまり關はりたくない おかしな人と 決めつけた場合

あなたは どこかのカルト宗教(これも決めつけた不適当な言ひ方ではあるが)に洗脳されてゐる人と
何も違はない

「何が正しいんだらう」

「どうして正しいんだらう」

と 常に問ひ續けること

これが 「洗脳」を抜け 「學び」を 「つくりだす」 意識のはたらき と私は考へる

さういふ意味で

「みづ」といふ言葉ひとつが

多くのことを 語ってくれてゐる

ちょっと覺へ書き

ちょっと考へたこと

理解することと それが良いのか惡いのかは 關係がない

理解することは 見ることに似てゐる

英語で see を 「分かる」の意味に使ふのが納得できる

見る とは それが「何」であるか確認すること(物事を分割すること わかつこと)

理解もそれと同じ

良い 惡いを判断するのは 自分の「観念」

そして 私達は 何かを 「良い」 とするも 「惡い」とするも どちらも許された「自由」を持ってゐる

極言すれば すべてを「良い」とする自由もある

私は 「良い」「惡い」は観念であり 自由に變へられることを 「理解」した

だが それを「理解」しないのも 「良い」といふ「観念」を持ってゐる

gitの使ひ方

ちょっと前置き
gitの使ひ方についてのページは 山ほどある
なのに どうして それについて書くのか
それは
初心者にとって (私にとって) 必要だった情報は何だったか
といふことに絞って書きたいからだ
あと git関連ページは ほとんどの場合 何らかのビジネスサイトに繋がってゐる
その広告とかが煩はしくて 肝心の内容に集中できないのでは と思ふからだ
では早速やっていかう

gitは何で便利なのか

たとへば ひとりで開發をしてゐるとき コードを更新したけど 古いものも とっておきたくなる
後で何か問題が起こって その古いコードに戻りたくなるかも知れない
だから バックアップしたりするのだが そのファイルがかさばってしまふ
gitを使ふと そこに登録したファイルは 古いものも含めて ずっと保存される
だから 古いものを使ひたいとき 自在に戻って そのコードを入手できる
あと これは ひとりでも 多人数でも 起こり得ることだが
あるフォルダの中にあるファイルを いろんなパソコンで編集したい場合がある
このときgitを使ふと 簡單に目的のフォルダを ネットから取り込み(repositoryからcloneする)
それを編集して すぐに 共有フォルダ(リポジトリ)に アップロードできるし
それを また 途中まで編集してゐた 他のパソコンヘ 取り入れて(pull する)
新しいファイルを編集することが 簡單にできる
と 今では それらしきことが言へるのだが それが分かるまでは 全然簡單ぢゃなかった・・・
でも 脇道に潜り込んで迷ひさへしなければ gitは簡單なツールで それゆへに 多くの人に利用されてゐるのだと思ふ

使ひ方

それでは gitの讀み方も知らなかった私が(ジットと讀んでゐたーみんなギットと言ふらしい)
一番分かりやすいと思へる説明をしてみる

まづ フォルダを作る
これが 作業フォルダだ
そのフォルダの中で いろんなコードを書くといふことを前提としやう
フォルダの作り方が分からない といふ人は まづ ターミナルで作業する環境を作るのが良いと思ふ
windowsであれば wsl で Ubuntuを入れたりする

mkdir test

と ターミナルに打ちこめば test といふ名前のフォルダ(ディレクトリ)ができる(自分の好きな名前にする)

cd test

で そのフォルダの中に入る
git がインストールされてゐなければ

sudo apt update

のあとに

sudo apt install git

として git をインストールする
次に ブラウザを開いて github のページへ行く
そこへユーザー登録する (このとき登録したユーザー名は大切なので 覺へておきたい)
ユーザー登録の詳細は省くが ここ など ネットをググれば色々でてくる
ユーザーページに行けたら 右上のアイコンをクリックして setting を選ぶ

setting
さうしたら 左下に Developer setting といふ項目があるので そこをクリック
developer_setting
そこにある Personal access tokens の Tokens(classic) を選ぶ
token
んで 適當な名前をつけて 適當にチェックして(適當ぢゃだめかもーとりあへず repo のところをチェックしてゐれば使へると思ふ) トークンを發効してもらふ(これが データをリポジトリに入れるときのパスワードになる)
このトークン(英数字の文字列)をコピーしておく

次にリポジトリを作成する(ユーザー登録したときに作っても良い)

repository
リポジトリの名前は さっき自分でつくったフォルダの名前にする
必須項目意外 何も變更しないで 一番下の Create repository といふ緑のボタンをクリック
さうしたら 上の画像に見へてゐる部分のコマンドを 先程作ったフォルダ内で實行する(ここでは testといふフォルダを作ったことにしてゐる)
實際には ユーザー名や フォルダ名が違ふので 上をそのまま入力してはいけない
さいごの

git push -u origin main

をやると ユーザー名とパスワードが聞かれる
ユーザー名は 設定したユーザー名なのだが
パスワードは githubに登録したときのパスワードではない!!
ここに 先程コピーしていた トークンの文字列を貼りつける(ちなみに貼りつけても 文字は隠されてゐるので トークンを間違ってコピーしてゐたら ファイルをアップできない)
これでエラーが出なければ とりあへず README.md ファイルが githubにアップされた
確認してみると良いと思ふ

さて 自分の編集した他のファイルもアップするのだが これは
アッド・コミット・プッシュ と覺へやう(!?)
これは add, commit, push のことなのだが ファイルをアップしたいときは この順番にコマンドを實行させる
これらの作業は エイリアスを使って自動化できるが とりあへず 順番に打っていくパターンを説明する

git add *

これで 現在自分の居るフォルダ内のすべてのファイルが準備され

git commit -m update

これで 準備されたファイルが登録され(update といふメッセージとともに)

git push -u origin main

で ネット上の github にアップロードされる
やはり最後のコマンドで ユーザー名とパスワードが聞かれるので
ユーザー名とトークンを入れてやらなければいけない
これがいちいち 煩はしい場合

git push https://syouzan420:ghp_M6NUf3shexdJUvkpdzhncSmOLKKyL4RUreM1@github.com/syouzan420/test.git main

のやうにする

git push https://ユーザー名:トークン@github.com/ユーザー名/フォルダ名(リポジトリ名).git main

となってゐるのが分かるだらうか(tokenは適當に改變してゐるので これは使へない)
そして これらを ひとつのコマンドで簡單に實行させるために エイリアスを作成する
まづ ホームディレクトリ(ホームフォルダ)へ移動する

cd ~

.bashrc といふファイルをエディタで開く

vim .bashrc

(bashrcの前にドット(ピリオド)が付いてゐる これは隠しファイルであることを示す)
ファイルの最後までスクロールさせ 次を追加する

alias gitup="git add *; git commit -m update; git push https://syouzan420:ghp_M6NUf3shexdJUvkpdzhncSmOLKKyL4RUreM1@github.com/syouzan420/test.git main"

これで 一回ターミナルを再起動させるか

source ~/.bashrc

として 設定を讀み込み
自分がさきほど作ったフォルダの中へ移動して

gitup

とすれば フォルダの中のファイルが またたく間に githubへアップロードされるであらう・・・(笑)
ちなみに ここでは 勝手に gitup といふ名前を付けただけであって 他のコマンドにかぶらないのであれば
自由に名前を付けられる

さて これで 自分のパソコンから githubへファイルをアップロードできた
それでは 別のパソコンで アップロードしたファイルを編集するにはどうするか
別のパソコンで 適當なフォルダへ移動する(アップロードしたリポジトリの名前のフォルダを作る必要はない)
ここで

git clone https://github.com/syouzan420/test.git

などとする (syouzan420は 自分のユーザー名 test は 自分のフォルダ名を使ふ)
すると! またたく間に リポジトリから このパソコンへファイルが転送される(フォルダもつくられる)
この場合 test といふフォルダができるので

cd test

として そのフォルダ内に入り いろいろとファイルを變更した後
先程の
アッド・コミット・プッシュ
を行へば リポジトリに 變更がアップロードされる
これも エイリアスを設定すれば 自動化されて アップロードが樂になる筈だ

さうしたら 元のパソコンのファイルは 古いままで リポジトリ(ネット上にアップロードされてゐるもの)が新しくなってゐることになる
この状態で 元のパソコンを使って その新しいファイルを編集したいならば
作ってゐたフォルダ内で プル(pull)してやればよい

git pull

とするだけだ
ユーザー名も パスワードもいらない
これで 自分のパソコンのデータが リポジトリの新しいデータに更新され それを編集することができる!!!

Haskell Language Server を Vim で利用する

ブログで書いたつもりになってゐて 書いてゐなかったので まとめてみる
1. coc.nvimをインストールする
2. CocConfigコマンドを實行して Language Server の設定を書く
3. GHCと Haskell Language Server (HLS) をインストール (GHCup)

1について

まづ vim がそもそもない といふ人で windowしか知らない といふ人は
windows に PowerShell 7 をインストールして
管理者として實行し wsl をインストールする
多分 Ubuntuがインストールされると思ふが うまくいかなければ ここらへん を參考にするとよいと思ふ

sudo apt install vim

でvimをインストール
vim-plug を入れる
(Installation の Unix といふところに貼ってあるコードをコピーして コマンドライン(シェル)に貼りつけ 實行させる)

cd ~

として ホームディレクトリに移動したら

vim .vimrc

として .vimrcファイルを編集する
(あと vimの使ひ方が分からなければ vimtutorを實行して學ぶ)

syntax on

set number
set smartindent
set autoindent
set expandtab
set tabstop=2
set softtabstop=2
set shiftwidth=2
set encoding=utf-8

call plug#begin()

Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'neovimhaskell/haskell-vim'
Plug 'Twinside/vim-haskellFold'

call plug#end()

colorscheme torte

filetype plugin indent on

augroup remember_folds
  autocmd!
  autocmd BufWinLeave * silent mkview
  autocmd BufWinEnter * silent loadview
augroup END

ここでカラースキームを設定してゐるが それは この設定が結果として見易くなったからだ
あと haskell-vim は インデントとかをhaskell向けにしたり 色々してくれるっぽい

それから 最初にup したとき書き忘れてたんだけど
coc.nvimの設定を.vimrcに加へた方がいいと思ふ(私の場合は何も考へず そのまま ここ の Example Vim configuration といふところをコピーして .vimrcに貼りつけた)

:PlugInstall

として プラグインをインストール
これで coc.nvimが入ると思ふ

2について

vimを開いた状態で

:CocConfig

とコマンドを入力
すると編集画面に移るので

{
"languageserver": {
  "haskell": {
    "command": "haskell-language-server-wrapper",
    "args": ["--lsp"],
    "rootPatterns": ["*.cabal", "stack.yaml", "cabal.project", "package.yaml", "hie.yaml"],
    "filetypes": ["haskell", "lhaskell"],
    // Settings are optional, here are some example values
    "settings": {
      "haskell": {
        "checkParents": "CheckOnSave",
        "checkProject": true,
        "maxCompletions": 40,
        "formattingProvider": "ormolu",
        "plugin": {
          "stan": { "globalOn": true }
        }
      }
    }
  }
}
}

基本的に ここ に書いてあるものを貼りつけただけだが 最初と最後に中括弧を追加しないとエラーになると思ふ

3について

Language Server 使へる設定にしたのに そもそもHaskell入れてへんかった~ といふことで インストール
ブラウザを開き(もとのOSのやつでいい)
GHCupの Installation ページで
System Requirements といふところを見る
Ubuntu のwslであれば Ubuntu のところを見て
そこに書いてある

build-essential curl libffi-dev libffi6 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5

といったものをコピーし
Ubuntuのターミナルで

sudo apt install コピーしたやつ  

とやって 必要なプログラムや ライブラリをインストールする
ここで 何かエラーが出たら そのエラーを出したっぽいプログラムorライブラリを 上のインストールリストから除いて もういっかい試す
例へば 何か libncurses-dev のインストールでエラーが出てゐた場合は

sudo apt install build-essential curl libffi-dev libffi6 libgmp-dev libgmp10 libncurses5 libtinfo5

とやって libncurses-dev を除いてゐる ってな感じ
無事インストールできたら
GHCup の Installation ページの 上の方にある For Linux ,.... うんちゃら といふところに書いてある内容をコピーして

curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh

ターミナルに貼りつけて實行する
途中で Haskell Language Server を入れるか聞かれるので Yes の方を選ぶ
インストールが終わったら

ghcup tui

として HLSがインストールされて さらに Set されてゐることを確認 ちゃんと Set されてゐると チェックマークが二つ入ってゐる

GHCup tui

てなことで
Happy Haskell Holidays!!!