2016/06/03


僕はもっぱらコマンドラインで作業するので peco を使う事が多いです。

Big Sky :: Windows のコマンドプロンプトを10倍便利にするコマンド「peco」

Windows ユーザのごく一部には、コマンドプロンプトが無いと生きられない民族がいます。そしてその民族の一部には cygwin や msys bash 等といった、サードパーティなシェル(power...

http://mattn.kaoriya.net/software/peco.htm
GitHub - mattn/pcd: peco + cd = awesome!

README.md pcd peco + cd = awesome! Requirements peco Windows Installation Copy pcd.bat into your fav...

https://github.com/mattn/pcd

ただユースケースとして peco だと若干大げさになる事があって、絞り込んだりしなくて良くただ単純に候補を選ぶだけでいいって事がたまにあります。例えば画面に表示された内容を見ながら候補を選びたいであったり、「以下の中から選んで下さい」といった説明のあとユーザに数個の候補から選ばせるといったバッチコマンドを作る場合です。peco だと画面が隠れてしまうんです。さらに peco は termbox-go を使っていて termbox-go のバグっぽい物を踏んだりするし、Windows でワイド文字 API を使っている手前ラスタフォントを使わないと画面が崩れる事があります。正直エスケープシーケンスを使えば候補を選ぶくらいなら出来るし、Windows でも go-colorable があればある程度のエスケープシーケンスは扱えると分かっていたので peco とは違う路線で薄いコマンドラインセレクタを作ってみました。

GitHub - mattn/cho

README.md cho choice! Why cho? Why not choice ? Because Windows already have choice command. Why not...

https://github.com/mattn/cho

はじめは「choice」というコマンド名で開発していたのだけど、よく考えたら Windows には choice コマンドが既にあって、「じゃぁ choic?」「いやいや」「では choi は?」「ダメだろ」「それなら cho だ!」くらいの感覚でネーミングしました。ただ適当に名付けた割にキーボードのホームポジションからそれ程手を動かさなくてもいいので実はちょっと気に入っています。使い方は peco と変わりません。ただし絞り込み機能は提供していませんので、ながーいファイルから1つ選びたいなら頑張って j/k するか大人しく peco を使って下さい。

cho

Windows、Linux、BSD、Mac OSX で動作します。go-colorable にいくらか修正を入れているので最新版を使ったほうがいいです。

おまけ

例えば Windows でコマンドを実行し cho で選んだ結果を変数に入れる様にしたい場合(UNIX の FOO=`ls | cho`)は以下の様なバッチファイル「setvar.bat」を用意しておくと便利です。

@echo off
for /f "delims=;" %%i in ('%2 ^| nkf -Sw ^| cho -cl ^| nkf -Ws') do set %1=%%i

nkf 持ってない人はコチラ(結果がUTF-8になっちゃうけど

@echo off
for /f "delims=;" %%i in ('%2 ^| cho -cl') do set %1=%%i

すると以下のコマンドで dir の出力結果を cho で選んだ内容が変数 foo に入ります。

setvar foo "dir /b"

2016/05/30


C言語とか C++ でマイクロフレームワークとか聞くと鼓動の高鳴りを抑えられなくなるmattnですこんにちわ。

Balde — A microframework for C based on GLib and bad intentions.

Features Simple templating engine, that converts markup to C code that is linked directly to the app...

https://balde.rgm.io/

2016年に glib で Web だと?と言われそうな気がしなくないですが面白そうな物を見つけました。特徴は

  • 簡単なテンプレートエンジンを提供し、マークアップからC言語のコードへ変換されアプリケーションのバイナリに直接埋め込まれる。
  • 静的リソースはアプリケーションのバイナリに埋め込まれサーブされる。
  • RESTful なリクエストのディスパッチを行い、ほぼ全ての HTTP メソッドをサポート。
  • URL の逆引き。 (Flask の "url_for" 関数に似ている)
  • セキュアクッキー(クライアントサイドセッション)
  • ファイルのアップロード
  • SCGI や CGI サーバとの親和性
  • GLib が稼働するあらゆるシステムで稼働する

まずは簡単なアプリケーションから。

#include <balde.h>

balde_response_t*
hello(balde_app_t *app, balde_request_t *request) {
  return balde_make_response("Hello World! I'm the balde! :D");
}

int
main(int argc, char *argv[]) {
  balde_app_t *app = balde_app_init();
  balde_app_add_url_rule(app, "hello""/", BALDE_HTTP_GET, hello);
  balde_app_run(app, argc, argv);
  balde_app_free(app);
  return 0;
}

pkg-config が提供されるのでコンパイルも楽ちん。

$ gcc -o app `pkg-config --cflags balde` main.c `pkg-config --libs balde`

静的ファイルを扱うには少し癖があり、定義ファイルからリソースとしてコンパイルする必要があります。

<?xml version="1.0" encoding="UTF-8"?>
<gresources>
    <gresource prefix="/static">
        <file>foo.js</file>
        <file>foo.css</file>
        <file>asd/bola.txt</file>
    </gresource>
</gresources>

もちろんこの XML と同様に static/foo.js, static/foo.css, static/asd/bola.txt が置かれているとします。リソースの生成方法は以下の通り。

$ glib-compile-resources --generate --sourcedir static --target static-resources.c static-resources.xml

ソースファイルは以下の様にルーティングを変更します。

#include <balde.h>
#include "static-resources.h"

balde_response_t*
hello(balde_app_t *app, balde_request_t *request) {
  return balde_make_response("Hello World! I'm the balde! :D");
}

int
main(int argc, char *argv[]) {
  balde_app_t *app = balde_app_init();
  balde_resources_load(app, static_resources_get_resource());
  balde_app_add_url_rule(app, "hello""/", BALDE_HTTP_GET, hello);
  balde_app_run(app, argc, argv);
  balde_app_free(app);
  return 0;
}

そしてコンパイル

$ gcc -o app `pkg-config --cflags balde` main.c static-resources.c `pkg-config --libs balde`

テンプレートを使う場合は専用ツールを利用します。templates/hello.html というファイルを用意します。

<h1>Hello, {{ name }}!</h1>

コマンドラインから balde-template-gen コマンドを実行します。

$ balde-template-gen templates/hello.html templates/hello.h
$ balde-template-gen templates/hello.html templates/hello.c

ソースコードを修正します。

#include <balde.h>
#include "static-resources.h"
#include "templates/hello.h"

balde_response_t*
hello(balde_app_t *app, balde_request_t *request) {
  balde_response_t *response = balde_make_response("");
  const gchar *name = balde_request_get_arg(request, "name");
  balde_response_set_tmpl_var(response, "name", name != NULL ? name : "World");
  balde_template_hello(app, request, response);
  return response;
}

int
main(int argc, char *argv[]) {
  balde_app_t *app = balde_app_init();
  balde_resources_load(app, static_resources_get_resource());
  balde_app_add_url_rule(app, "hello""/", BALDE_HTTP_GET, hello);
  balde_app_run(app, argc, argv);
  balde_app_free(app);
  return 0;
}

そしてコンパイル。

$ gcc -o app `pkg-config --cflags balde` main.c static-resources.c templates/hello.c `pkg-config --libs balde`

出来上がったアプリケーションに -s オプションを付けるとスタンドアローンサーバとして起動します。ブラウザから http://localhost:8080/?name=mattn の様なURLを開くとちゃんと動作しているのが分かるかと思います。尚、msys2 でビルドして見ましたが問題なく動作しています。glib がインストールされている同じOS同士ならばバイナリ1つでデプロイが完了します。便利そうですね。

LGPL 2.1 のライセンスのもと利用出来ます。


2016/04/22


コルタナさん、とても便利なのですが Vim を起動する事が出来ない。どこかで見たショートカットファイルを所定の場所に置く方法もうまく動かない。

C:\ProgramData\Microsoft\Windows\Start Menu\Programs

調べると、ボイスコマンドという API が出ているのだけど、Universal Application でないといけない。Universal Application だと System.Diagnostic からプロセスを起動できない。そこで別に立てたウェブサーバから Vim を起動させる。サーバのコードは至って簡単。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            HttpListener listener = new HttpListener();
            listener.Prefixes.Add("http://localhost:8888/");
            listener.Start();
            while (true)
            {
                HttpListenerContext context = listener.GetContext();
                HttpListenerRequest req = context.Request;
                HttpListenerResponse res = context.Response;

                if (req.RawUrl == "/vim")
                {
                    Process.Start("gvim");
                    res.StatusCode = 200;
                }
                else
                {
                    res.StatusCode = 404;
                }
                res.Close();
            }
        }
    }
}

次に空の Universal Application を作り、OnLaunched イベントでボイスコマンドAPIにコマンド定義を食わせる。

        protected async override void OnLaunched(LaunchActivatedEventArgs e)
        {
#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                this.DebugSettings.EnableFrameRateCounter = true;
            }
#endif
            Frame rootFrame = Window.Current.Content as Frame;

            // ウィンドウに既にコンテンツが表示されている場合は、アプリケーションの初期化を繰り返さずに、
            // ウィンドウがアクティブであることだけを確認してください
            if (rootFrame == null)
            {
                // ナビゲーション コンテキストとして動作するフレームを作成し、最初のページに移動します
                rootFrame = new Frame();

                rootFrame.NavigationFailed += OnNavigationFailed;

                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    //TODO: 以前中断したアプリケーションから状態を読み込みます
                }

                // フレームを現在のウィンドウに配置します
                Window.Current.Content = rootFrame;
            }

            if (e.PrelaunchActivated == false)
            {
                if (rootFrame.Content == null)
                {
                    // ナビゲーション スタックが復元されない場合は、最初のページに移動します。
                    // このとき、必要な情報をナビゲーション パラメーターとして渡して、新しいページを
                    //構成します
                    rootFrame.Navigate(typeof(MainPage), e.Arguments);
                }
                // 現在のウィンドウがアクティブであることを確認します
                Window.Current.Activate();

                try
                {
                    // 俺コマンドの登録
                    StorageFile vcdStorageFile = await Package.Current.InstalledLocation.GetFileAsync(@"MyCommands.xml");
                    await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(vcdStorageFile);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("ボイスコマンドの定義でエラーが発生しました", ex);
                }
            }
        }

食わせるコマンド定義は XML で記述し、以下のようにした。

<?xml version="1.0" encoding="utf-8" ?>

<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
  <CommandSet xml:lang="ja-jp" Name="HayCortanaLaunchVim">
    <CommandPrefix>ねぇ</CommandPrefix>
    <Example>ビムを起動して</Example>
    <Command Name="OpenVim">
      <Example>ビムを起動して</Example>
      <ListenFor>ビムを起動して[下さい]</ListenFor>
      <Feedback>ビムを起動しています</Feedback>
      <Navigate/>
    </Command>
  </CommandSet>
</VoiceCommands>

これで「コルタナさん、ねぇ Vim を起動して」と喋るだけで http://localhost:8888/vim にリクエストが飛び、サーバを経由して Vim が起動する。

サーバ起動しておかないといけないのが面倒臭い。ちなみにちゃんと作るならクライアントサーバ間でトークンか何かをやりとりする必要があるし、サーバも localhost からのアクセスに絞るべきです。

ソースは GitHub に置いておきます。