iiyama ProLite E2473HDSが信号なしになってしまった場合の入力切替

iiyama ProLite E2473HDSの入力切替でちょっと躓いたのでメモ。

http://www.iiyama.co.jp/products/lcd/24/PLE2473HDS/

ProLite E2473HDSに2台のPCをVGAとDVIでつないでいるとする(
HDMIを使っている場合や3台以上の場合でも同様)。

このとき、入力としてVGAを選択した状態で、VGAにつないでいるPCの電源を落とした場合、ProLite E2473HDSは入力を自動的にDVIに切り替えてくれないためDVIからの入力があるのにもかかわらず信号なし状態になってしまう。

このような場合に、DVIに入力を切り替えるには[2]ボタンを押す(電源のランプがオレンジになっていても問題ない)。

また、入力切り替えでAutoを選択していれば、画面を出力しているPCの電源が落ちた際、自動的に別の入力に切り替えてくれる。よって、特定の入力を選択し画面を切り替えた後、入力切替でAutoを選択し直しておくと画面が真っ黒になって戸惑わずに済む。

pygmentizeで自作のlexerやstyleを使う

pygmentizeで使えるlexerやsytleに気に入るものがなかったり、変更を加えたい場合には自分でlexerやstyleを作ることができる。

参考: http://pygments.org/docs/

今回は、Pygmentsにプラグインとして自作のlexer、styleを追加する。

setuptoolsが必要なのでインストールされていない場合には、インストールしておく。

http://pypi.python.org/pypi/setuptools

次に自作のlexerやstyle用のパッケージを作る。

今回はpygments_pluginという名前にすることにした。

ディレクトリをつくって、__init__.pyを配置。

mkdir pygments_plugin
mkdir pygments_plugin/lexers
mkdir pygmets_plugin/styles
touch pygments_plugin/__init__.py
touch pygments_plugin/lexers/__init__.py
touch pygments_plugin/styles/__init__.py

bash用のlexerが実装されているpygments/lexers/other.pyをpygments_plugin/lexersに、pygments/styles/から好みのstyleをpygments/stylesにコピー。

cp /path/to/pytments/lexers/others.py pygments_plugin/lexers
cp /path/to/pytments/styles/default.py pygments_plugin/styles

pygments_plugin/lexers/other.pyの中に以下のようなBash用のlexerがあるので、オリジナルとかぶっているnameやaliasesを変更する。例えば、name='My Shell'やaliases = ['mysh']など。
aliasesに設定した文字列で、pygmentizeからlexerを選択することができる。

class BashLexer(RegexLexer):
    """
    Lexer for (ba|k|)sh shell scripts.

    *New in Pygments 0.6.*
    """

    name = 'Bash' # これを変更 name = 'My Shell'
    aliases = ['bash', 'sh', 'ksh'] # これを変更 aliases = ['mysh']
    filenames = ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass']
    mimetypes = ['application/x-sh', 'application/x-shellscript']

    tokens = {
        'root': [
            include('basic'),
            (r'\$\(\(', Keyword, 'math'),
            (r'\$\(', Keyword, 'paren'),
            (r'\${#?', Keyword, 'curly'),
            (r'`', String.Backtick, 'backticks'),
            include('data'),
        ],   
        'basic': [
            (r'\b(if|fi|else|while|do|done|for|then|return|function|case|'
             r'select|continue|until|esac|elif)\s*\b',
             Keyword),
            (r'\b(alias|bg|bind|break|builtin|caller|cd|command|compgen|'
             r'complete|declare|dirs|disown|echo|enable|eval|exec|exit|'
             r'export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|'
             r'local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|'
             r'shopt|source|suspend|test|time|times|trap|true|type|typeset|'
             r'ulimit|umask|unalias|unset|wait)\s*\b(?!\.)',
             Name.Builtin),
            (r'#.*\n', Comment),
            (r'\\[\w\W]', String.Escape),
            (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)),
            (r'[\[\]{}()=]', Operator),
            (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
            (r'&&|\|\|', Operator),
        ],   
        'data': [
            (r'(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double),
            (r"(?s)\$?'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
            (r';', Text),
            (r'\s+', Text),
            (r'[^=\s\n\[\]{}()$"\'`\\<]+', Text),
            (r'\d+(?= |\Z)', Number),
            (r'\$#?(\w+|.)', Name.Variable),
            (r'<', Text),
        ],   
        'curly': [
            (r'}', Keyword, '#pop'),
            (r':-', Keyword),
            (r'[a-zA-Z0-9_]+', Name.Variable),
            (r'[^}:"\'`$]+', Punctuation),
            (r':', Punctuation),
            include('root'),
        ],   
        'paren': [
            (r'\)', Keyword, '#pop'),
            include('root'),
        ],   
        'math': [
            (r'\)\)', Keyword, '#pop'),
            (r'[-+*/%^|&]|\*\*|\|\|', Operator),
            (r'\d+', Number),
            include('root'),
        ],
        'backticks': [
            (r'`', String.Backtick, '#pop'),
            include('root'),
        ],
    }

    def analyse_text(text):
        return shebang_matches(text, r'(ba|z|)sh')

次に以下のような内容のpygments_plugin/setup.pyを作成する。myshlexerやmystyleは好きな名前に変更してもよい。pygmentizeでstyleを選択するときは、ここでのmystyleに相当する文字列が使用される。

stylesからdefault.py以外を取ってきたときには、mystyle=の行のdefaultを取ってきたファイル名から.pyを除いたもの、DefaultStyleをとってきたファイル中で定義されているクラス名に変更する必要がある。

from setuptools import setup

setup(
        name = "pygments_plugin",
        version = "0.1",
        package_dir = {"pygments_plugin": "", 
                       "pygments_plugin.lexers": "lexers",
                       "pygments_plugin.styles": "styles"},
        packages = ["pygments_plugin",
                    "pygments_plugin.lexers",
                    "pygments_plugin.styles"],
        entry_points = """ 
        [pygments.lexers]
        myshlexer = pygments_plugin.lexers.other:BashLexer
        [pygments.styles]
        mystyle = pygments_plugin.styles.default:DefaultStyle
        """
     )   

ここまでで、以下のようなディレクトリ構成になっている。

% tree 
.
├── __init__.py
├── lexers
│&#160;&#160; ├── __init__.py
│&#160;&#160; └── other.py
├── setup.py
└── styles
    ├── default.py
    └── __init__.py

2 directories, 6 files

pygments_pluginディレクトリで以下のコマンドを実行して、pygments_pluginをインストール。

python setup.py install

pygmentizeから追加されていることを確認。上記の例ではstyleにmystyle、lexerにmyshが追加されているはず。

pygmentize -L style
pygmentize -L lexer

あとは、pygments_plugin/lexers/other.pyやpygments_plugin/sytles/default.pyを好きに変更する。

sytleに関しては基本的にカラーコードや修飾を変更するだけ。lexerに関してはtokens['basic']の中の正規表現に色を付けたいパターンを追加するのが簡単だと思う。詳細についてはPygmentsのドキュメントやソースコードを参照。

変更を加えたら再び下記のコマンドを実行すると、変更が反映される。

python setup.py install

実際に使用する際には、以下のようにする。formatterを指定しないとstyleを変えても意味がないので注意。

pygmentize -l mysh -f 256 -O syle=mystyle

コマンドが長いので、適当にaliasを作っておくと楽かもしれない。

pygmentizeでmakeやsconsの出力のシンタックスハイライト

makeやsconsを実行が失敗してしまったら、どのコマンドで失敗したのか調べなくてはならない。しかし、makeやsconsによって実行されるコマンドは引数が多かったり、絶対パスが使われたりしていて非常に読みにくい場合も多い。

そこで、これをシンタックスハイライトすることで少しでも読みやすくして、原因を見つけやすくする方法を考える。

今回はpygmentizeというコマンドを使って、シンタックスハイライトを行う。
pygmentizeコマンドはPygmentsをインストールすれば使えるようになるので、easy_installやpipでPygmentsをインストールすれば良い。

pip install pygments

pygmentizeは引数にファイルをとることもできるが、今回はmakeやsconsの出力をパイプで渡し、-lで入力がsh形式であることを教えてやる。

これで、echoやcdといったコマンドや文字列がハイライトされる。

make -n | pygmentize -l sh

日本語が含まれている場合にはencodingを指定する必要がある。

make -n | pygmentize -l sh -O encoding=utf-8

色を変更したい場合には、styleを変更する。formatterを256色ターミナル用に変更しないとstyleは反映されないので注意。

make -n | pygmentize -l sh -O -f256 style=vim

出力が長い場合にはlessを使ったり、行番号を追加すれば見やすくなる。

make -n | pygmentize -l sh | less -R
make -n | pygmentize -l sh | cat -n | less -R

同じようなことはGNU Source-highlightを使って以下のようにもできるが、インストールが面倒そうだったし、ハイライトの結果もそんなに変わらないのでpygmentizeを使うことにした。

make -n | source-highlight -s sh -f esc | cat -n | less -R

追記:あるいは以下のようにvimなどをつかうのもありかもしれない。

make -n | vim +"set filetype=sh" -

不満点の一つはハイライトされる項目がかなり限られているため、せっかくハイライトしても見た目がほとんど変わらない場合が多いこと。例えば、この記事のようにオプションがハイライトされたりはしない。lexerとかを自分で書けば、エディタなどでの見た目に近づけられるだろうか

追記:本当は認識されている単語や記号でも、styleによっては色がついていないように見えることがある(例えば、&&や||とか)。上記の様にstyleを変えれば見えるようになることもある。

プロンプトで履歴を使って補完する

昨日作っていた関数を使うときに変数が長くなったり、何度も調べるときいちいち入力しなおすのが面倒なので履歴を使って補完できるようにした。

awful.promptにやりたいことと大体同じことをしてくれる関数があるので、それを使えればいいのだがlocalがついていて外からアクセスできなさそうだったのと、書き換えるのが面倒だったので単にrc.luaにコピーすることにした。

-- 履歴を格納するテーブル

data = {}
data.history = {}

-- 履歴をロードする関数 awful/prompt.luaからのコピー
function history_check_load(id, max)
    if id and id ~= ""
        and not data.history[id] then
        data.history[id] = { max = 50, table = {} }
        
        if max then
                data.history[id].max = max
        end
        
        local f = io.open(id, "r")
            
        -- Read history file
        if f then
                for line in f:lines() do
                    table.insert(data.history[id].table, line)
                    if #data.history[id].table >= data.history[id].max then
                       break
                    end
                end
                f:close()
        end
    end
end

-- data.historyに新しく入力されたデータを追加する関数 awful/prompt.luaからのコピー
-- history_saveは元の関数がやってくれるのでコメントアウト
function history_add(id, command)
    if data.history[id] then
        if command ~= ""
            and command ~= data.history[id].table[#data.history[id].table] then
            table.insert(data.history[id].table, command)

            -- Do not exceed our max_cmd
            if #data.history[id].table > data.history[id].max then
                table.remove(data.history[id].table, 1)
            end

            --history_save(id)
        end
    end
end

-- completion_callbackを返す関数
function completion_callback_using_history (id)
    history_check_load(id)
    f =  function (text, cur_pos, ncomp)
            return awful.completion.generic(text, cur_pos, ncomp, data.history[id].table)
         end
    return f
end

あとは、前回作った関数を以下のように書き換える。

以下に対応する引数あるいは行を追加した。

  • 履歴ファイルの指定
  • 履歴ファイルからの補間関数の作成
  • 実行に履歴を保存
    awful.key({ modkey, "Shift" }, "x",
              function ()
                  awful.prompt.run({ prompt = "variable name: " },
                  mypromptbox[mouse.screen].widget,
                  function (var) 
                      history_add(awful.util.getdir("cache") .. "/history_variable_check", var)
                      awful.util.eval("dbg('" .. var .. "'," .. var .. ")")
                  end,
                  completion_callback_using_history(awful.util.getdir("cache") .. "/history_variable_check"),
                  awful.util.getdir("cache") .. "/history_variable_check")
              end),

これで何度も同じ文字列を入力することはなくなり、だいぶ便利になった。補完にはシェルを利用した関数も用意されているので、うまく利用すればパスの入力の手間を省いたりすることもできそう。

今回はコードをコピーして書いて、二度手間のような動作になってしまったが、実際にはawful.promptを少し書き換えたほうが(localを外せば)もっと簡単できれいにできると
思う。

キーバインドが増えてきて忘れそうなので、awful.keyやawful.promptを書き換えて、
キーバインドのリストを表示できるようにしたい。

Naughtyを使ったrc.luaのデバッグ

~/.config/awesome/rc.luaを書き換えた後に期待通り動かないときに、原因を探るのにはNaughtyを使って変数の値を調べるのが便利。

下記のリンク先を参考にデバッグ用のスクリプトを書いてみた。

http://awesome.naquadah.org/wiki/Naughty

rc.luaに以下のような関数を定義する

function dbg(msg, var)
    local text = "<b>" .. msg .. "</b><br/>"
    text = text .. "type: " .. type(var) .. "<br/>"
    if var ~= nil then
        if type(var) == "string" or type(var) == "number" or type(var) == "nil" then 
            text = text .. "value: " .. var 
        elseif type(var) == "table" then
            text = text .. "value:<br/> "
            for k, v in pairs(var) do
                text = text .. "  " .. tostring(k)  .. " | " .. tostring(v) .. "<br/>"
            end 
        else
            text = text .. "value: " .. tostring(var) .. "<br/>" 
        end 
    end 
    naughty.notify({ text = text, timeout = 0}) 
end

この関数を適当なキーバインドからよび出せるようにしておくと、値が知りたい関数の名前を入力するだけなので楽。

    awful.key({ modkey, "Shift" }, "x",
              function ()
                  awful.prompt.run({ prompt = "variable name: " },
                  mypromptbox[mouse.screen].widget,
                  function (var) awful.util.eval("dbg('" .. var .. "'," .. var .. ")") end)
              end),

typeがtableでない時に属性を取得できれば、もっと便利になるのだがやり方がわからなかった。例えば、変数の型がtagの時にnameを表示してくれたりすると、コードを調べたりリファレンスを調べる手間が省けるのでありがたい。

あと変数名が長くなってくると毎回入力するのが面倒なので履歴を使えるようにしたい。

タイトルバーにつけているタグを表示してみる (awesome)

タイル型WMのawesomeを使っていて、複数のタグを同時に表示していると各クライアントにどのタグをつけているのかわからなくなってしまうことがあったので、各クライントのタイトルバーにつけているタグを表示するようにしてみた。

まず、awful.titlebar.addでタイトルバーをつけるときに、タグ名を書いたウィジェットを追加する。

titlebar_taglist = widget({ type = "textbox"})
titlebar_taglist.text = "tag:"
for i, v in ipairs(c:tags()) do
    titlebar_taglist.text = titlebar_taglist.text .. " " .. v.name
end
awful.titlebar.add(c, { modkey = modkey, widget=titlebar_taglist })

あとは、クライアントのタグを付け替えたときに、表示が変わるように設定する。
タグリストからマウスでタグを付け替えることはないので、そちらには対応しなかった。

for i = 1, keynumber do
        awful.key({ modkey, "Shift" }, "#" .. i + 9,
                  function ()
                      if client.focus and tags[client.focus.screen][i] then
                          client.focus.titlebar.widgets[2].text = "tag: " .. tags[client.focus.screen][i].name
                          awful.client.movetotag(tags[client.focus.screen][i])
                      end
                  end),
        awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
                  function ()
                      if client.focus and tags[client.focus.screen][i] then
                          local old_tags = {}
                          local old_tags_count = 0
                          for _, v in ipairs(client.focus:tags()) do
                              old_tags[v] = true  
                              old_tags_count = old_tags_count + 1
                          end

                          if old_tags[tags[client.focus.screen][i]] then
                              if old_tags_count > 1 then
                                  old_tags[tags[client.focus.screen][i]] = nil
                              end
                          else
                              old_tags[tags[client.focus.screen][i]] = true
                          end
                              
                          local client_tags = {}
                          local index = 1
                          for i, v in ipairs(tags[client.focus.screen]) do
                              if old_tags[v] then
                                  client_tags[index] = v
                                  index = index + 1
                              end
                          end

                          local c = client.focus
                          c.titlebar.widgets[2].text = "tag:"
                          for i, v in ipairs(client_tags) do
                              c.titlebar.widgets[2].text = c.titlebar.widgets[2].text .. " " .. v.name
                          end

                          awful.client.toggletag(tags[client.focus.screen][i])
                      end
                  end))
end

Luaの配列の扱いにけっこう苦戦したけど、慣れたらいろいろ設定できて面白そう。

ReadCubeで文献管理

新しい文献管理ツールReadCubeというのを見つけたので使ってみた。

http://www.readcube.com/

PDFのインポートはPDFのあるディレクトリを選ぶだけで簡単。

検索、スナップショット、ハイライト、ノートといったアノテーション機能やリストを使ったファイル管理機能がある。ただし、ReadCubeでつけたアノテーションをFoxit Readerなど他のリーダーで見ることはできないようだ。

論文の管理に特化した機能として、PDFからのタイトルや著者の認識、論文のレコメンデーション、PubMed, Google Scholarの検索機能などがある。とくに、関連論文、リファレンス、引用情報をPubMedGoogle Scholarなどを使って検索してくれる機能は便利だと感じた。検索結果を選択するとブラウザが開き比較的簡単にPDFを入手することができるので、関連論文のPDFを集めるのが捗りそうだ。

公開されているWeb Readerが便利だったので、自分のPDFをWebでも見られるようになったら嬉しい。