rangerでコマンドを自作する方法(commands.pyの書き方)
rangerはTUIファイラ。
作ってみた
今回はcommands.pyの詳しい書き方について。
commands.py
所在
自作コマンドはcommands.py
に実装する。設定ファイル同様~/.config/ranger/
配下にある。
Python3言語で実装する。
ファイル | 役割 |
---|---|
commands.py | 自作コマンドの実装 |
commands_full.py | 全コマンドの実装 |
以下コマンドでファイルを作成できる。設定ファイルとともにデフォルト状態で。
ranger --copy-config=all
記法
class 任意コマンド名(Command): def execute(self): pass
これを~/.config/ranger/commands.py
に追記する。rangerを再起動したら反映される。rangerを起動して:任意コマンド名
とすると実行される。
コードはPythonにより実装する。Pythonはバージョンによって言語機能にかなり差がある。そこで、まずはrangerが使用しているPythonのバージョンを確認する。
$ ranger --version ranger version: ranger 1.9.2 Python version: 3.5.3 (default, Sep 27 2018, 17:25:39) [GCC 6.3.0 20170516] Locale: ja_JP.UTF-8
Pythonのドキュメントは読みにくい。ついでにPythonのクラス機能はJavaやC#などと違う。アクセス修飾子がなくすべて丸見え。JavaScript的。
上記が基礎。すべてのコマンドはCommand
クラスを継承していることがわかる。
api
上記設定ファイル
- ranger/
- api/
- core/
- fm.py class FM
- ui = ui if ui is not None else UI()
start_paths = paths if paths is not None else ['.']
directories = dict()
bookmarks = bookmarks
current_tab = 1
- tabs = {}
- tags = tags
restorable_tabs = deque([], ranger.MAX_RESTORABLE_TABS)
py3 = sys.version_info >= (3, )
previews = {}
default_linemodes = deque()
- loader = Loader()
copy_buffer = set()
do_cut = False
- metadata = MetadataManager()
image_displayer = None
run = None
rifle = None
thistab = None
- fm.py class FM
api/commands.py class Command
イベントハンドラ
継承元のapi/commands.py class Commandを見てみると以下のような部分がある。
class Command(FileManagerAware): ... def execute(self): """Override this""" def tab(self, tabnum): """Override this""" def quick(self): """Override this""" def cancel(self): """Override this""" ...
上記Command
クラスを継承した自作コマンドでは、execute
関数にコマンドの実行処理を書く。これは上記クラスのメソッドをオーバーライドしていると思われる。
~/.config/ranger/commands.py
class 任意コマンド名(Command): def execute(self): pass
オーバーライドできる関数は4つあるらしい。それぞれの役割は何なのか。以下ファイルの先頭コメントに記述があったので抜粋。
~/.config/ranger/commands_full.py
# execute(): called when the command is executed. # cancel(): called when closing the console. # tab(tabnum): called when <TAB> is pressed. # quick(): called after each keypress.
関数 | 呼出契機 |
---|---|
execute() |
コマンドが実行されると呼ばれる(<Enter> ) |
cancel() |
コンソールを閉じるときに呼ばれる(<ESC> ) |
tab(tabnum) | |
quick() |
キーを押すたびに呼ばれる |
イベントハンドラだった。<TAB>
はコンソールでよくある補完機能を実装するためのもの。
戻り値
各イベントハンドラの戻り値は以下。
tab()戻り値 | 意味 |
---|---|
なし | タブ補完なし |
文字列 | コンソールをこの文字列に変更する |
リスト/タプル/ジェネレータ | その中のすべてのアイテムを巡回する |
quick()戻り値 | 意味 |
---|---|
False |
何もしない |
True |
後からコマンドを実行する |
execute()
, cancel()
は戻り値不要。
引数
各コマンドはユーザ入力された引数を受け取ることができる。
引数の取得 | 意味 |
---|---|
self.line |
コンソールに書かれた行全体。 |
self.args |
コマンドへのすべての(スペース区切りの)引数のリスト。 |
self.quantifier |
このコマンドがキー "X"にマッピングされていた場合ユーザーが6Xを押すとself.quantifier は6 になる。 |
self.arg(n) |
n番目の引数。存在しない場合は空の文字列。 |
self.rest(n) |
n番目の引数に続くすべてのもの。例えばコマンドがsearch foo bar a b c の場合rest(2) はbar a b c になる。 |
self.start(n) |
n番目の引数の前にあるものすべて。例えばコマンドがsearch foo bar a b c の場合start(2) はsearch foo になる。 |
値
コマンドクラスが参照できる値や処理をいくつか紹介する。
値 | 概要 |
---|---|
self.fm |
ほとんどの情報を含むfmオブジェクトへの参照。 |
self.fm.thisdir |
現在の作業ディレクトリ(Fileオブジェクト) self.fm.thisdir.path とすると文字列を取得できる |
self.fm.thisfile |
現在のファイル(Fileオブジェクト) |
self.fm.thisdir
, self.fm.thisfile
, はPython言語におけるファイルオブジェクトを返す。fileObj = open('/tmp/a.txt')
。
FileObjectメンバ | 概要 |
---|---|
tfile.path |
ファイルへのパス。 |
tfile.basename |
ベース名のみ。 |
tfile.load_content() |
ディレクトリの内容を強制的にロードする。 |
tfile.is_directory |
ディレクトリかどうかに応じてTrue , False になる。 |
ファイルオブジェクトのメンバはPythonドキュメントから見つけられなかった。そこで以下のようにしてメンバを調べた。
$ echo -e "1\n2" > a.txt $ python3 Python 3.5.3 (default, Sep 27 2018, 17:25:39) [GCC 6.3.0 20170516] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
Pythonインタプリタ内で以下コードを実行。dir()
を使う。
Python 3.5.3 (default, Sep 27 2018, 17:25:39) [GCC 6.3.0 20170516] on linux Type "help", "copyright", "credits" or "license" for more information. >>> fileObj = open('/tmp/work/a.txt') >>> dir(fileObj) ['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines'] >>>
たとえばwrite
などのメンバがあることがわかる。一部メンバの使い方はドキュメントに書いてあった。
処理
コマンドクラスが参照できる処理をいくつか紹介する。
処理 | 概要 |
---|---|
self.fm.notify(string) |
与えられた文字列を画面に表示する。 |
self.fm.notify(string、bad = True) |
与えられた文字列を赤で印刷する。 |
self.fm.reload_cwd() |
現在の作業ディレクトリをリロードする。 |
self.fm.thistab.get_selection() |
選択されているすべてのファイルのリスト。 |
self.fm.execute_console(string) |
文字列をrangerコマンドとして実行する。 |
self.fm.open_console(string) |
与えられた文字列でコンソールを開く。 |
self.fm.move(direction) |
指定した方向にカーソルを移動する。引数例(down=3 , up=5 , right=1 , left=1 , to=6 )。core/actions.py |
以下のようにコードを追っていけば詳しく分かる。継承クラスを追えばいい。
処理についてはcore/actions.py class Actionsを見ればいい。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13
- bash 4.4.12
- python 2.7.13, pip 9.0.1
- python3 3.5.3, pip3 9.0.1
- ranger 1.9.2
$ uname -a Linux raspberrypi 4.14.98-v7+ #1200 SMP Tue Feb 12 20:27:48 GMT 2019 armv7l GNU/Linux