4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

K3カレンダーAdvent Calendar 2019

Day 20

Vimで作業時間を計測→モチベ維持作戦

Last updated at Posted at 2019-12-20

About

この記事はK3 Advent Calender20日目の記事です。区切りだよ(o・∇・o)

今回はVimにはまったり、VimScriptいじったりしてみていたので、それを題材にしてみました。

今回行ったこと

  • 時報機能を追加
  • 作業開始時間を記録&時報を呼ぶと何分やってるかを教えてくれる
  • なんとなく労ってくれる

経緯

  1. HaskellでghcもghciもVimから簡単に呼べるようにしたい→自作コマンドを作ろう
  2. あれ、Vim scriptさん凄く色々できそうじゃない?
  3. なんかやろう

こんな経緯からお察しの通り、私自身はここ1ヶ月くらいでVim熱が一気に高まった弱々Vimmerです。何かアドバイス有りましたら頂ければ嬉しいです。

環境

  • Windows 10 Education
  • Vim 8.0.596

前準備

実際にコーディングに入る前の段階です。

Scriptファイルの作成

今回はUserディレクトリの下に各種設定用のフォルダを作り、その下にmycmd.vimを作成しました。

この中身をいじっていくのが中心となります。

.vimrcの設定

上記のことを.vimrcに教えてやり、vim起動時に有効になるようにします。

常時起動にしない場合は普通に各コマンドと同じようにコマンド打てばOKですが、私は面倒でした。

source C:\Users\XXX\setting\mycmd.vim

基礎知識

とりあえず、必要だった知識を先にまとめます。スルーしていることがあるかもしれないです…そしたらすみません………
ここから先のことは試すときはmycmd.vimに書くのをおすすめします。

Vimスクリプト基礎文法最速マスター←この辺を参考にさせていただきました。

独自コマンドの作成

とりあえず、Hello, (o・∇・o)と出力するだけのコマンドMcoを作ってみましょう。独自コマンドは大文字から始めなければならない規則があるようです。

command Mco echo "Hello, (o・∇・o)"

簡単ですね。command [コマンド名] [実行する内容]です。

echoは単純に出力するやつなので、Cのprintfとか、JavaのSystem.out.printlnとか、pythonのprintとか、JSのconsole.logとか、そのイメージですね。

関数を作ろう&ついでに変数

Vim scriptさん、関数を定義できます。正直この辺りナメてました。すみません。

上のやつをmcoechoとか関数にしてみます。

function s:mcoecho()
    echo "Hello, (o・∇・o)"
endfunction

あまり難しいことはないですね。よくある関数定義と一緒です。functionfunction!と末尾にエクスクラメーションマークを付けると既存の関数を上書きしてくれるようですが、あまり既存のものを壊すのが好きではないので、なんとなく付けていません。かぶらないような名前をつけていきたい(願望)。

関数名の頭のs:についてはこの辺りを参考にしました。要するにprivateというか、そんなもんです。変数についての話ですが関数も大方同じみたい?
vimスクリプトの変数

変数の代入時は頭に毎度letが必要です。この辺りも注意。宣言ではなく、代入の頭に必要です。むしろ宣言は不要。一度上のリンクで確認頂くのが良いと思います。

引数を取る関数&while/for/if

引数も勿論取れます。

function s:mcoechorepeat(n)
    let i = 0
    while i < a:n
        echo "Hello, (o・∇・o)"
        let i += 1
    endwhile
endfunction

引数を取るときはnに対してa:nのように頭に接頭辞がいります。ここだけご注意。
ぬるっと使いましたが、whileは大方の言語と似ています。括弧がいらない。
ところで閉じるときは大体endXXXXです。BASICを思い出す。

この引数、実は可変長を取れます。...で示します。

function s:multifunc(...)
    [やること]
endfunction

こいつ、a:0で引数の個数を、以降はa:1, a:2と順番に引数が格納されていきます。便利です。詳細は最初に提示したサイトさんに掲載されていました。

forはリストの中身を回します。pythonのforとか各種言語のforeachに近いですね。

for i in [1,2,3,4,5]
    echo "Hello, (o・∇・o)" . i
endfor

この出力はこんな感じです。
image.png

最後にifはこんな感じです。

for i in [1,2,3,4,5]
    if i % 2 == 0
        echo "Even"
    elseif i == 1
        echo "One"
    else
        echo "Odd"
    endif
endfor

関数を独自コマンドとして適用

超大事。これできないとあんまり意味なくなっちゃう。

command Mco call s:mcoecho()

function s:mcoecho()
    echo "Hello, (o・∇・o)"
endfunction

commandで使うときはcallが必要です。関数内で関数を呼び出す等のときはcallなしで普段の手続き型言語のように書いてOKです。

文字の結合

文字の結合は.です。

let strQiita = "Qii" . "ta"

って感じですね。数値とか大丈夫かな~って思ってたんですけど結構柔軟に扱ってくれます。良い子です。

モチベ維持作戦スタート

ざっくり説明したんだか他のサイトさんに丸投げしたんだかわかりませんが、とりあえず本題に入ります。以降メインで実行させる関数はs:nowtimeです。

時間を言ってもらおう

まずは時間を取得しなければなりません。strftimeという関数を使います。

どこかの言語で時刻を扱ったことのある人なら見たことはあるかもしれない形で扱えます。

let nowtime = strftime("%Y-%m-%d %H:%M:%S")

とりあえずこれで現在時間が2019-11-25 11:00:00という感じにnowtime変数に代入されます。第2引数としてUNIX時間をつっこんでやるとその時間出したりもできます。

command Mtime call s:nowtime()

function s:nowtime()
    let nowtime = strftime("%m月%d日%H時%M分")
    echo ("(o・∇・o)<いま、" . nowtime . "だよ~")
endfunction

じゃあこれを使って、

これで、:Mtimeと打ってやればとりあえず時報は言ってくれるようになります。

image.png

こんな感じ。可愛いですね。

作業時間を記録しよう

記録はUNIX時間で行いました。経過時間の計算の際に、現在時刻のUNIXとの差を求めていい感じにやると楽できるかなぁと思ったので。

というところで現在のUNIX時間を取ってきましょう。localtime()で持ってきます。

let nowUnix = localtime()

これでnowUnixには1574698112みたいなのが入ってくれます。

とりあえず単純に記録機能を追加させてみます。

command Mtime call s:nowtime()

let s:timerec = -1
function s:nowtime()
    let nowtime = strftime("%m月%d日%H時%M分")
    echo ("(o・∇・o)<いま、" . nowtime . "だよ~")
    let s:timerec = localtime()
endfunction

はい、これで記録できますね。ただ無限に記録させ続けてしまう……コマンドでなんとかしたいですね。

引数にrを取るときだけ記録させてみます。

command -nargs=? Mtime call s:nowtime(<f-args>)

let s:timerec = -1
function s:nowtime(...)
    let nowtime = strftime("%m月%d日%H時%M分")
    echo ("(o・∇・o)<いま、" . nowtime . "だよ~")
    if a:0 >= 1
        if a:1 == "r"
            let s:timerec = localtime()
        endif
    endif
endfunction

ちょっと面倒になりました。まず1行目コマンド定義の行。

コマンドに引数を取るときは-nargs=1のように、何個引数を取るか書かねばなりません。引数が可変の場合は-nargs=?です。今回は0個or1個なので可変ですね。nowtime()nowtime(<f-args>)と変えとく必要がある。

そして、function側も引数に可変を表す...を入れる必要があります。内部では引数を持つ場合1つ目のif文がTrue,その引数が"r"であればs:timereclocaltime()を入れています。

作業終了させる

同様に、引数にbを取るときだけ作業終了の処理をします。

これはs:timerecと現在時刻の差を出力、s:timerecを初期化する、の2点ができればOKです。

とりあえずs:timerecと現在時刻の差を整形してくれる関数を作りましょう、ということで、引数に現在時刻をとって「4時間17分56秒」とか返してくれるs:getElapsedTimeを作りました。

function s:getElapsedTime(loctime)
    let worktime = a:loctime - s:timerec
    let wsec = worktime % 60
    let worktime = worktime / 60
    let wmin = worktime % 60
    let whour = worktime / 60
    let strtime = wsec . "秒"
    if whour > 0
        let strtime = whour . "時間" . wmin . "分" . strtime
    elseif wmin > 0
        let strtime = wmin . "分" . strtime
    endif
    return strtime
endfunction

特に難しいことはなく。letif文べたーっと書いてreturnしているくらいです。returnは関数の戻り値を指定する、多くの言語であるやつです。

これを使って、(o・∇・o)に経過時間を喋らせてみましょう。

        elseif a:1 == "b"
            if s:timerec == -1
                echo("(o・∇・o)<時間が記録されてないよ~")
                return
            end
            let strtime = s:getElapsedTime(loctime)
            echo ("(o・∇・o)<いま、" . nowtime . "! " . strtime . "作業したよ~おつかれさま~")
            let s:timerec = -1

s:timerecは記録されていないときに-1にしています。スタートしてないのに終了しようとしたら時間が記録されてないよ~と煽ってあげましょう。

正しく実行されるとこんな感じになります。

image.png

可愛いですね。

単に時間を出力するコマンドの修正

こうなると、単に時間を出力させるのも味気ないですし、そのときに今何分やったかを知らせてほしいですね。
ついでに、先のコードだと、2回も時間を出力させられてしまうので、そこも上手く修正したいところです。

ざっくり、s:nowtime()関数の概形はこんな感じにしましょう。

let s:timerec = -1
funtion s:nowtime(...)
   if [引数が1つ以上]
      if [引数が"r"]
         " 記録スタート
      elseif [引数が"b"]
         " 記録終了
      end
   else 
      if [時間が記録されていない]
         " 単に時間を出力
      else
         " 時間と一緒に経過時間も表示
      end
   end
endfunction

完成!

これらを踏まえて最終的なs:nowtime()はこうなります。

let s:timerec = -1
function s:nowtime(...)
    let nowtime = strftime("%m月%d日%H時%M分")
    let loctime = localtime()
    if a:0 >= 1
        if a:1 == "r"
            echo ("(o・∇・o)<すたーと~ いま" . nowtime . "です~がんばっていこ~")
            let s:timerec = loctime
        elseif a:1 == "b"
            if s:timerec == -1
                echo("(o・∇・o)<時間が記録されてないよ~")
                return
            end
            let strtime = s:getElapsedTime(loctime)
            echo ("(o・∇・o)<いま、" . nowtime . "! " . strtime . "作業したよ~おつかれさま~")
            let s:timerec = -1
        end
    else
        if s:timerec == -1
            echo ("(o・∇・o)<いま、" . nowtime . "だよ~")
        else
            let strtime = s:getElapsedTime(loctime)
            echo ("(o・∇・o)<いま、" . nowtime . "! " . strtime . "作業したよ~がんばっていこ~")
        end
    end
endfunction

結論

  • 可愛い
  • 進捗は言うても変わんない
  • でも可愛い
4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?