Scintillaとmrubyでテキストエディタを作る(6) : folding
Scintillaの豊富な機能のひとつにfoldingがある。”折り畳み”である。 プログラムを編集する際、クラスやメソッド単位で表示・非表示を切り替えるようなものである。
作成中のmrbmacsというエディタでの例だが、folding機能を有効にすると、以下のようにmoduleやclassの左に「-」マークが付く。
folding機能を有効にするには、fold
というpropertyを1
にする、という他とは違う処理が必要。
scintillaの内部構造を理解できていないので、おまじないの領域。
機能としてのfoldingは上記の処理で良いが、画面表示上は別の手順が必要となる。 「-」や「+」を表示する領域を用意するため、mrbmacsでは以下のような処理を行っている。
@view_win.sci_set_margin_widthn(1, 1) @view_win.sci_set_margin_typen(1, 0) @view_win.sci_set_margin_maskn(1, Scintilla::SC_MASK_FOLDERS)
これで、編集画面の左側にfoldingのためのマークが表示される。
マウスのクリックで表示・非表示を切り替えるための準備は、以下。
@view_win.sci_set_marginsensitiven(1, 1) @view_win.sci_set_automatic_fold(Scintilla::SC_AUTOMATICFOLD_CLICK)
sci_set_automatic_fold
というメッセージは簡単に見付かったが、sci_set_marginsensitiven
の存在に気付くのに時間がかかった。
これで、マウス操作での表示・非表示が実現できる。
折り畳んだ例は以下。
ところで、Scintillaのterminal版実装であるscintermにはjinxというサンプルプログラムが付属している。 jinx.cは100行ちょっとのプログラムだが、マウスによる折り畳みまで実装しており、 非常に参考になる。
Scintillaのsyntax highlightingの調査
Scintillaは多数のプログラミング言語に対応していて、syntax highlighting機能もある。
編集中のファイルを言語と対応付けるには、SCI_SETLEXERLANGUAGE(<unused>, const char *name)
というコマンドが使える。
name
には言語名を入れれば良い。
syntax highlighting機能で色を設定するには、SCI_STYLESETFORE(int styleNumber, int colour)
を使う。
背景色を設定するSCI_STYLESETBACK
やSCI_STYLESETBOLD
、SCI_STYLESETITALIC
というものもある。
styleNumber
は各言語の各書式で番号が決まっている。
Scintillaのinclude/SciLexer.h
というヘッダファイルに定義がある。
例えば、
#define SCE_C_DEFAULT 0 #define SCE_C_COMMENT 1 #define SCE_C_COMMENTLINE 2 #define SCE_C_COMMENTDOC 3 #define SCE_C_NUMBER 4
のような感じ。C言語用だとコメント文のstyleNumber
は1と思われる。
このような設定が各言語毎にあるので、いちいち設定するのはかなり面倒だし、 なんのことか分らない定義もある。 どこかにドキュメントは無いものか?
rubyの場合、以下のような感じだ。
定義名 | 番号 | 意味 |
---|---|---|
SCE_RB_DEFAULT | 0 | デフォルト? |
SCE_RB_ERROR | 1 | エラー?不明 |
SCE_RB_COMMENTLINE | 2 | コメント行 |
SCE_RB_POD | 3 | =begin から=end まで |
SCE_RB_NUMBER | 4 | 数字 |
SCE_RB_WORD | 5 | SCI_SETKEYWORDS(int keyWordSet, const char *keyWordList) で指定したキーワードに対応 |
SCE_RB_STRING | 6 | " で囲まれた文字列 |
SCE_RB_CHARACTER | 7 | ' で囲まれた文字列 |
SCE_RB_CLASSNAME | 8 | クラス名 |
SCE_RB_DEFNAME | 9 | メソッド名 |
SCE_RB_OPERATOR | 10 | 各種演算子 |
SCE_RB_IDENTIFIER | 11 | クラス名でもメソッド名でもモジュール名でもキーワードでも無いもの |
SCE_RB_REGEX | 12 | 正規表現。/ で囲まれた箇所 |
SCE_RB_GLOBAL | 13 | グローバル変数。$hoge など |
SCE_RB_SYMBOL | 14 | シンボル:hoge とか |
SCE_RB_MODULE_NAME | 15 | モジュール名 |
SCE_RB_INSTANCE_VAR | 16 | インスタンス変数。@hoge みたいな| |
SCE_RB_CLASS_VAR | 17 | クラス変数。@@hoge とかね |
SCE_RB_BACKTICKS | 18 | バッククォートで囲まれた文字列 |
SCE_RB_DATASECTION | 19 | DATA 。__END__ 以降の行。この機能使ったことないな |
SCE_RB_HERE_DELIM | 20 | ヒアドキュメントの始まりと終りを示す文字列 |
SCE_RB_HERE_Q | 21 | ヒアドキュメントの中身 |
SCE_RB_HERE_QQ | 22 | 未使用? |
SCE_RB_HERE_QX | 23 | 未使用? |
SCE_RB_STRING_Q | 24 | よくわからない? |
SCE_RB_STRING_QQ | 25 | 不明? |
SCE_RB_STRING_QX | 26 | これもわからない? |
SCE_RB_STRING_QR | 27 | なんだろう?| |
SCE_RB_STRING_QW | 28 | はて? |
SCE_RB_WORD_DEMOTED | 29 | 例えば、stmt if test のif みたいなの |
SCE_RB_STDIN | 30 | 未使用? |
SCE_RB_STDOUT | 31 | 未使用? |
SCE_RB_STDERR | 40 | 未使用? |
SCE_RB_UPPER_BOUND | 41 | 未使用? |
実験しても、よくわからないのもあった。
Scintillaとmrubyでテキストエディタを作る(5) : 単純な検索と置換機能
ScintillaとmrubyでEmacsライクなテキストエディタ(mrbmacs)を作成している。
Scintillaにはテキストエディタに必要な様々な機能が用意されているので、自作エディタになにかしらの機能を追加する手順は、
- 実装したい機能を考える
- Scintillaのドキュメントから使えそうな機能を探す
- 幾つかの機能を組み合わせて実験して、作っていく
という感じ。
検索と置換に関しては、”Searching and replacing”というセクションに各種機能が用意されている。
Scintillaの機能は、SCI_SETxxxxx や SCI_GETxxxxx といった名前のメッセージを送ることで動く。 パラメータは最大2つ設定できる。 mrbmacsで使っているscintermの場合、
scintilla_send_message (sci, iMessage, wParam, lParam)
という関数を使うことで、機能を実行する。
検索
検索にはSCI_SEARCHINTARGET というメッセージが使えそうだ。 これは、あるtargetの範囲からパラメータで指定した文字列を検索するメッセージである。
mrbmacsでは、前方検索の機能をこれで実現し、
view_win.sci_set_target_start(start_pos) view_win.sci_set_target_end(end_pos)
としておき、
ret = view_win.sci_search_in_target(search_text.length, search_text) if ret != -1 view_win.sci_set_sel(view_win.sci_get_target_start, view_win.sci_get_target_end) end
という処理で検索結果を選択している。
後方検索の場合も、startとendの設定方法を変えれば簡単にできる。
置換
置換に関しては、SCI_REPLACETARGETというメッセージが使えそう。 これは、targetで指定された範囲を引数で指定した文字列で置換するメッセージ。
まず、SCI_SEARCHINTARGETで検索する。 検索結果がTARGETに指定されるので、SCI_REPLACETARGETで置換する。
replace.rb ( https://github.com/masahino/mruby-bin-mrbmacs-curses/blob/master/mrblib/replace.rb ) というファイルで実装した。
Scintillaとmrubyでテキストエディタを作る(4) : GTK版
curses版 のエディタは、そこそこ動くところまで作れたので、他のUIのものも用意できないか検討してみた。
そもそも、ScintillaはWindows、Mac OS X、GTKに対応しているので、まずはその辺りから。 一番簡単そうな、GTKから着手。
curses版(mruby-bin-mrbmacs-curses)と同じように、
mrubyでのGTKバインディングは、mruby-gtk2とmruby-gtk3と2つあった。 どちらか選んで、scintillaもそれ用にビルドする。GTK3でコンパイルするには、GTK3を定義してmakeだ。 あたらしいほうが良いだろう。
で、こんな感じ。
それっぽい見た目。ただし、まだ実用不可。 編集用のwindowは問題無く作れそうだが、ファイルのopen/saveや検索/置換のダイアログが出来ていない。
せっかくGTKなのだから、FileChooserDialogなど使おうとしたが、runしたところで、以下のエラー。
(<unknown>:27316): Gtk-CRITICAL **: gtk_dialog_run: assertion 'GTK_IS_DIALOG (dialog)' failed
やりかたが分らない・・・
一旦、あきらめて、FileSelectionを試してみる。 FileSelectionはGTK3には無いので、GTK2用にビルドし直し。
やってみると、 なぜか、メインのwindowを作成する前にFileSelection.newを書かないと、ファイル選択画面が出てこない。
そして、FileSelectionのok_buttonとcancel_buttonを取得する方法も分らない・・・
ここで、挫折。
GTKのウィンドウの描画やsignal_connect関係は、単純にC言語で書いたほうが悩みが少ないかも。 ただ、そうすると、mruby-scintilla-gtkはどう使う?
また、今度考えることにして、しばらくcurses版のみをがんばって作っていこう。
Scintillaとmrubyでテキストエディタを作る(3) : mruby-cli化
前回のさらに続き。
ビルド方法の検討
作成中のエディタは Scintillaやcurses等のライブラリに依存しまくっているので、ビルドが面倒であった。
また、複数のプラットフォームで利用できないか、検討した。
当初は、buildスクリプトを頑張って用意していたが、なかなか大変だったので、 mruby-cliを使ってマルチプラットフォーム化に取り組んだ。
まず、どっか新しいディレクトリで、
$ mruby-cli --setup mruby-bin-mrbmacs-curses
を実行して、必要なファイルを用意する。
作成された、Dockerfile、Rakefile、build_config.rb、docker-compose.ymlをガシガシ弄る。
x86_64-pc-linux-gnu、i386-pc-linux-gnu、x86_64-apple-darwin14、i386-apple-darwin14、i686-w64-mingw32、x86_64-w64-mingw32 で、コンパイルできるところまでは行き着いた。動くかどうかは、未確認なものもあり。
Dockerfileでは、ビルドに必要なcursesとiconvを用意する処理を追加している。
色々と悩んだ末に、WIndows環境ではPDCursesを使うようにしている。 また、mruby-requireを一時的に組込まないようにしている(確か、ビルドでエラーが出たため)。
Linux環境では、iconvはglibcに含まれているため、コンパイル時の-liconvが不要。 build_config.rbでは以下のような場当たり的な対処を施している。
conf.gem :github => 'masahino/mruby-iconv', :branch => 'add_iconvlist' do |g| g.linker.libraries.delete 'iconv' end
ここで、実験的にmruby-iconvをフォークして、iconvlistを追加していたことを思い出した。
我ながら、色々と中途半端で良くない・・・