*textobj-sandwich.jax* 『挟まれた』テキストを選択する。 Last change:28-Nov-2021. 書いた人 : machakann ライセンス : NYSL license 日本語 English (Unofficial) 必須要件: Vim 7.4 かそれ以降のバージョン |+reltime| 機能 (任意) |+float| 機能 (任意) ============================================================================== CONTENTS *textobj-sandwich-contents* TL;DR INTRODUCTION |textobj-sandwich-introduction| KEYMAPPINGS |textobj-sandwich-keymappings| CONFIGURATION |textobj-sandwich-configuration| REQUISITE buns external FILTERS filetype kind mode operator LOCAL OPTIONS nesting expand_range regex skip_regex skip_regex_head skip_regex_tail quoteescape expr noremap syntax inner_syntax match_syntax skip_break skip_expr GLOBAL OPTIONS timeout |g:textobj#sandwich#timeout| timeoutlen |g:textobj#sandwich#timeoutlen| stimeoutlen |g:textobj#sandwich#stimeoutlen| latest_jump |g:textobj#sandwich#latest_jump| MISCELLANEOUS |textobj-sandwich-miscellaneous| dib で欲しいのはそれじゃない 頻繁に「 Timed out 」となる 誤操作でカーソルが飛んでいった! ============================================================================== TL;DR こちらをご覧ください。 |sandwich-quick-start|. ============================================================================== INTRODUCTION *textobj-sandwich-introduction* *textobj-sandwich* は (foo) や "bar" のような括弧などに挟まれたテキストを検索 し選択するためのテキストオブジェクトです。これは次の四つの独立したテキストオブ ジェクトからなります。 |(textobj-sandwich-auto-i)|, |(textobj-sandwich-auto-a)|, |(textobj-sandwich-query-i)|, |(textobj-sandwich-query-a)| |(textobj-sandwich-auto-i)| および |(textobj-sandwich-auto-a)| は 対象テキストを自動的に検索します。対象テキストはレシピ recipe と呼ばれる括弧の セットが事前に用意されており、これに依って検索されます。これについては |textobj-sandwich-configuration| に詳しく書かれています。 |(textobj-sandwich-auto-i)| は囲みの内側のテキストを選択します。 |(textobj-sandwich-auto-a)| は囲みを含めてテキストを選択します。これらは デフォルトでは ib 及び ab にマップされています。 |(textobj-sandwich-query-i)| 及び |(textobj-sandwich-query-a)| は 対象テキストをユーザーの入力に応じて検索します。ユーザーの入力にマッチするレシ ピが見つかればそのレシピに応じて、あるいはマッチするレシピがなければ入力された 一文字に挟まれた範囲を検索します。 |(textobj-sandwich-query-i)| は囲みの 内側のテキストを選択します。 |(textobj-sandwich-query-a)| は囲みを含めた テキストを選択します。これらはデフォルトでは is 及び as にマップされています。 ------------------------------------------------------------------------------ ビジュアルモードではこれらのテキストオブジェクトは選択領域を拡張するように動き ます。これは Vim 組み込みのテキストオブジェクト |iw| の |v|iw|iw|iw|... とした 時の動作に似ています。例えば次のようなテキストの foo の上にカーソルがあると仮 定しましょう。 > {baz[bar(foo)bar]baz} < vib と入力します > <-> : selected region {baz[bar(foo)bar]baz} < さらに ib > <---------> : selected region {baz[bar(foo)bar]baz} < さらに ib > <-----------------> : selected region {baz[bar(foo)bar]baz} < とこのように選択領域を拡張します。 ------------------------------------------------------------------------------ 矩形ビジュアルモードではカーソル行内で対象テキストを検索し、選択の逆端はこれに 従います。例えば次の5行5桁のテキストがあるとします。 > (foo) (bar) (baz) < カーソルが第一行目にある時 2jibd と押下すると > () () () < こうなります。次にこのような場合を考えてみましょう。 > foooo baaar (baz) < カーソルが第一行目にある時 2jibd と押下すると > fo br () < このようにカーソルのあった第三行目に合わせて動作します。 ============================================================================== KEYMAPPINGS *textobj-sandwich-keymappings* このプラグインは下記のキーマッピングを定義します。これらはオペレータ待機モード とビジュアルモードの両方で使用可能です。 キーマッピング デフォルト ----------------------------------------------------------- (textobj-sandwich-auto-i) ib (textobj-sandwich-auto-a) ab (textobj-sandwich-query-i) is (textobj-sandwich-query-a) as ----------------------------------------------------------- これらのデフォルト設定が必要なければ vimrc で g:textobj_sandwich_no_default_key_mappings を定義してください。 > let g:textobj_sandwich_no_default_key_mappings = 1 < これでデフォルトキーマッピングは定義されません。あとはお好きなようにマップしな おしてください。 > omap ia (textobj-sandwich-auto-i) xmap ia (textobj-sandwich-auto-i) omap aa (textobj-sandwich-auto-a) xmap aa (textobj-sandwich-auto-a) < ------------------------------------------------------------------------------ キーマッピング~ (textobj-sandwich-auto-i) *(textobj-sandwich-auto-i)* 括弧などに挟まれた文字列を自動的に検索するテキストオブジェクトです。こ のキーマッピングは括弧などに囲まれた内側の文字列を選択します。デフォル トではオペレータ待機モードとビジュアルモードの ib にマップされていま す。また、オペレータ待機モード |:omap| 、ビジュアルモード |:xmap| に加 えノーマルモード |:nmap| で使用することもできます。 (textobj-sandwich-auto-a) *(textobj-sandwich-auto-a)* 括弧などに挟まれた文字列を自動的に検索するテキストオブジェクトです。こ のキーマッピングは括弧などの囲みを含む文字列を選択します。デフォルトで はオペレータ待機モードとビジュアルモードの ab にマップされています。ま た、オペレータ待機モード |:omap| 、ビジュアルモード |:xmap| に加えノー マルモード |:nmap| で使用することもできます。 (textobj-sandwich-query-i) *(textobj-sandwich-query-i)* 括弧などに挟まれた文字列をユーザーの入力に応じて検索するテキストオブジ ェクトです。このキーマッピングは括弧などに囲まれた内側の文字列を選択し ます。デフォルトではオペレータ待機モードとビジュアルモードの ib にマッ プされています。また、オペレータ待機モード |:omap| 、ビジュアルモード |:xmap| に加えノーマルモード |:nmap| で使用することもできます。 (textobj-sandwich-query-a) *(textobj-sandwich-query-a)* 括弧などに挟まれた文字列をユーザーの入力に応じて検索するテキストオブジ ェクトです。このキーマッピングは括弧などの囲みを含む文字列を選択します 。デフォルトではオペレータ待機モードとビジュアルモードの ib にマップさ れています。また、オペレータ待機モード |:omap| 、ビジュアルモード |:xmap| に加えノーマルモード |:nmap| で使用することもできます。 *(textobj-sandwich-literal-query-i)* (textobj-sandwich-literal-query-i) これは |(textobj-sandwich-query-i)| に似ていますが、常にユーザー 設定 |g:sandwich#recipes| 及び |g:textobj#sandwich#recipes| を無視しま す。ユーザーの入力はそのままに解釈され、その文字に囲まれたテキストを選 択します。オペレータ待機モード |:omap| 、ビジュアルモード |:xmap| に加 えノーマルモード |:nmap| で使用することができます。 > omap if (textobj-sandwich-literal-query-i) xmap if (textobj-sandwich-literal-query-i) " press difa to delete a text surrounded by 'a'. " abcba -> aa < *(textobj-sandwich-literal-query-a)* (textobj-sandwich-literal-query-a) これは |(textobj-sandwich-query-a)| に似ていますが、常にユーザー 設定 |g:sandwich#recipes| 及び |g:textobj#sandwich#recipes| を無視しま す。ユーザーの入力はそのままに解釈され、その文字に囲まれたテキストを選 択します。オペレータ待機モード |:omap| 、ビジュアルモード |:xmap| に加 えノーマルモード |:nmap| で使用することができます。 > omap if (textobj-sandwich-literal-query-a) xmap if (textobj-sandwich-literal-query-a) " press dafb to delete a text surrounded by 'b' including 'b'. " abcba -> aa < キーマッピング関数~ ユーザーは新しいキーマッピングを定義するのに以下の関数を使うこともできます。 *textobj#sandwich#auto()* textobj#sandwich#auto(mode, a_or_i[, options[, recipes]]) この関数はテキストオブジェクトのキーマッピングを定義するのにつかわれま す。 > onoremap ib textobj#sandwich#auto('o', 'i') xnoremap ib textobj#sandwich#auto('x', 'i') onoremap ab textobj#sandwich#auto('o', 'a') xnoremap ab textobj#sandwich#auto('x', 'a') < 省略可能な第三引数に空でない辞書変数が与えられた場合、辞書に含まれる local option はそのキーマッピングで使われるオプションのデフォルト値 を上書きします。 > " example 1 onoremap ib \ textobj#sandwich#auto('o', 'i', {'expand_range': 0}) " example 2 let g:sandwich_alt_options = {'expand_range': 0} onoremap ib \ textobj#sandwich#auto('o', 'i', g:sandwich_alt_options) < 省略可能な第四引数にリストが与えられた場合、そのキーマッピングは |g:sandwich#recipes| (|g:sandwich#default_recipes|) や |g:textobj#sandwich#recipes| (|g:textobj#sandwich#default_recipes|) の かわりに引数に与えられたリストを参照します。 > " example 1 onoremap ib \ textobj#sandwich#auto('o', 'i', {}, [{'buns': ['(', ')']}]) " example 2 let g:sandwich_alt_recipes = [{'buns': ['(', ')']}] onoremap ib \ textobj#sandwich#auto('o', 'i', {}, g:sandwich_alt_recipes) < *textobj#sandwich#query()* textobj#sandwich#query(mode, a_or_i[, options[, recipes]]) この関数は |textobj#sandwich#auto()| と同様の関数ですが、 |(textobj-sandwich-query-i)| や |(textobj-sandwich-query-a)| などの query 系列の テキストオブジェクトを定義するのに使われます。 > onoremap is textobj#sandwich#query('o', 'i') xnoremap is textobj#sandwich#query('x', 'i') onoremap as textobj#sandwich#query('o', 'a') xnoremap as textobj#sandwich#query('x', 'a') < ============================================================================== CONFIGURATION *textobj-sandwich-configuration* 囲み文字のセットと付加情報をまとめたものをレシピ "recipe" と呼びます。一つ一つ のレシピは |Dictionary| であり、これを集めた |list| がテキストオブジェクトの動 作を決めます。 |g:sandwich#default_recipes| はそのリストのうちの一つ で、|operator-sandwich| と |textobj-sandwich| の両方から参照されます。これは多 くの場合、共有された方が便利であるためです。もしユーザーが |g:sandwich#recipes| を定義した場合はかわりにそちらが使用されます。デフォルト の|g:sandwich#default_recipes| は |:echo| コマンドで確認できます。 > :echo g:sandwich#default_recipes < この変数はロックされていますが、 |g:sandwich#recipes| をユーザーが宣言する際に 必要ならコピーすることができます。 > :let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) < デフォルトでは (), [], {}, <> のセットが登録されています。より詳しくは |g:sandwich#default_recipes| をご覧ください。 |g:textobj#sandwich#default_recipes| はまた別のリストで、こちらは |textobj-sandwich| のみからしか参照されません。ユーザーが |g:textobj#sandwich#recipes| を定義した際にはそちらがかわりに使用されます。 |g:textobj#sandwich#default_recipes| は |g:sandwich#default_recipes| と同様に ロックされています。 g:textobj#sandwich#recipes *g:textobj#sandwich#recipes* テキストオブジェクトのみから参照されるレシピのリストです。このリストが 存在しない場合は |g:textobj#sandwich#default_recipes| が使用されます。 *b:textobj_sandwich_recipes* |b:textobj_sandwich_recipes| が存在する場合は、 |g:textobj#sandwich#recipes| のかわりにそちらが使われます。これはバッ ファについてローカルな値なので、ファイルタイプ固有な設定が増えすぎた時 に使うと便利かもしれません。 g:textobj#sandwich#default_recipes *g:textobj#sandwich#default_recipes* デフォルトで用意されるレシピのリストの一つです。テキストオブジェクトの みから参照されます。ユーザーが |g:textobj#sandwich#recipes| を定義した 場合にはそちらがかわりにつかわれます。その際必要ならコピーすることが できます。 > :let g:textobj#sandwich#recipes \ = deepcopy(g:textobj#sandwich#default_recipes) < NOTE: もしレシピが競合する場合は |g:textobj#sandwich#default_recipes| か |g:textobj#sandwich#recipes| が |g:sandwich#default_recipes| か |g:sandwich#recipes| よりも優先されます。また同じリスト内であれば、リス ト後方のレシピほど優先されます。 ------------------------------------------------------------------------------ 一つ一つのレシピは |Dictionary| 変数であり、 requisite, input, filter, local optionの三種の情報を持ちえます。 requisite はすべてのレシピに必須の情報で囲み 文字を特定するためのものです。 input はレシピを指定するためのユーザー入力を決 めます。 filter は使用する状況によってレシピを選別するためにあります。 local option はレシピによってテキストオブジェクトの働きを細かく制御するために使われ ます。さらにこれら以外にテキストオブジェクトの基本的な動作を制御するための global option もいくつか存在します。 設定を変更する場合はまず必要なリストを宣言しましょう。 > let g:sandwich#recipes = [] < あるいはデフォルト設定を引き継ぐためにコピーしてもよいでしょう。 > let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) < Requisite~ requisite には buns と external の二種類あり、すべてのレシピはどちらかを持たな ければなりません。 buns テキストを囲む文字列のセットを定義するためのキーです。値に文字列を二つ 要素に持つ |list| を持ちます。このセットに囲まれた文字列がテキストオブ ジェクトに検索されます。 "regex" オプションが真のときは正規表現をとし て扱われます。また、レシピが "input" キーを持たない場合、 |(textobj-sandwich-query-i)| 及び |(textobj-sandwich-query-a)| でレシピを指定するためのキー入力に も使われます。 > let g:sandwich#recipes += [ \ {'buns': ['(', ')']} \ ] " Press dis( or dis) " (foo) ---> () let g:sandwich#recipes += [ \ {'buns': ['ab', 'cd']} \ ] " Press disab or discd " abfoocd ---> abcd let g:sandwich#recipes += [ \ {'buns': ['ab', 'cd'], 'input': ['a']} \ ] " Press disa " abfoocd ---> abcd < external こちらは補助的に使われる requisite です。文字列二要素の |list| でそれ ぞれの要素は外部のテキストオブジェクトになります。これを使うと外部のテ キストオブジェクトをレシピ化することができます。 |(textobj-sandwich-auto-i)| 及び |(textobj-sandwich-query-i)| ではリストの前方の要素が選択する範 囲を、 |(textobj-sandwich-auto-a)| 及び |(textobj-sandwich-query-a)| では後方の要素が選択する範囲を選択 します。"buns" とは異なり、多くの local option が無効になります。 > let g:sandwich#recipes += [ \ {'external': ['it', 'at']} \ ] " "it" selects the text inside tags, "at" selects including tags. " <---it---> " title here " <----------at-----------> " Press dib " title here ---> " Press dab " title here ---> < "noremap" オプションは適用されます。内部的にはビジュアル選択がテキスト オブジェクトの指定する範囲を調べるのに使われるので "noremap" が適用さ れるのはビジュアルモードへのマッピングになります。 > let g:sandwich#recipes += [ \ {'external': ['i[', 'a['], 'noremap': 0} \ ] " Press dib " [foo] ---> [] xnoremap i[ i{ xnoremap a[ a{ " Press dib " {foo} ---> {} < "noremap" オプションを偽にすることでユーザー定義テキストオブジェクトを 同様にレシピ化することができます。 > " "noremap" option should be false. let g:sandwich#recipes += [ \ { \ 'external': ["\(textobj-sandwich-auto-i)", \ "\(textobj-sandwich-auto-a)"], \ 'noremap': 0, \ 'kind': ['query'], \ 'input': ['b'] \ } \ ] < NOTE: 使用するテキストオブジェクトはビジュアルモードで正しく動作するこ とを期待されます。 NOTE: すべてのテキストオブジェクトが動作するとは限りません。 Input~ input |(textobj-sandwich-query-i)| 及び |(textobj-sandwich-query-a)| は囲みを決定するためにユーザーに入 力を促します。この時の入力に使われるのがこのキーの値になります。値はリ ストで複数の文字列を含むことができます。このキーを持たないレシピでは "buns" がかわりにつかわれます。以下の例では、 "input" キーを持たない ので "buns" がそのまま使われます。 > let g:sandwich#recipes += [ \ {'buns': ['"""', '"""']} \ ] " Press dis""" " """foo""" ---> """""" < "input" キーがあればそちらがかわりにつかわれます。 > let g:sandwich#recipes += [ \ {'buns': ['"""', '"""'], 'input': ['"']} \ ] " Press dis" " """foo""" ---> """""" < Filter~ filetype ファイルタイプによってレシピを選別するフィルターです。値はリストでファ イルタイプ名の文字列が要素になります。レシピが "filetype" キーを持たな いか、要素として "all" を持つ場合はすべてのファイルタイプで有効になり ます。 > " The following recipes are valid on any filetype. let g:sandwich#recipes += [ \ {'buns': ['(', ')']} \ {'buns': ['[', ']'], 'filetype': ['all']} \ ] " The textobj "it" and "at" is not versatile and might be heavy on " large files, thus it would be better to activate only on specific " filetypes. let g:sandwich#recipes += [ \ {'external': ['it', 'at'], 'filetype': ['html']} \ ] < kind テキストオブジェクトの種類によってレシピを選別するフィルターです。値は リストで要素にテキストオブジェクトの種類を文字列として持ちます。使用可 能な種類は "auto", "query", "textobj", "all" です。 "textobj" は "auto", "query" の二つを指定した場合と同じです。 "textobj" と"all" の 違いは、 "all" は |operator-sandwich| の種類も含むことがある点にありま す。あわせて |operator-sandwich-configuration| もご覧ください。レシピ が "kind" キーを持たなければすべての種類で有効です。 > " The following recipe is valid only with " (textobj-sandwich-auto-i) and (textobj-sandwich-auto-a) let g:sandwich#recipes += [ \ {'buns': ['"""', '"""'], 'kind': ['auto']} \ ] < mode 使用されたモードによってレシピを選別するフィルターです。値はモードを表 す文字のリストです。使用可能な文字は "o" と "x" のみです。"o" はオペ レータ待機モードを、 "x" はビジュアルモードを表します。レシピが "mode" キーを持たなければすべてのモードで有効です。 > " These recipes are switch behaviors on modes with the same input. let g:sandwich#recipes += [ \ {'buns': ['"', '"'], 'mode': ['o']} \ {'buns': ['"""', '"""'], 'mode': ['x'], 'input': ['"']} \ ] < action 編集の種類によってレシピを選別するフィルターです。"add" 及び "delete" が |operator-sandwich| のために用意されています。"all" は先の二つを包 含します。"add" のみが指定されている場合は |textobj-sandwich| では無効 になります。しかし、"delete" か "all" が含まれた場合は有効になります。 これは削除においてそれらのテキストオブジェクトが連動するためです。 > " This recipe does not take effect for textobjects let g:sandwich#recipes += [ \ {'buns': ['"""', '"""'], 'action': ['add'], 'input': ['"']} \ ] " This recipe is valid for delete actions and textobjects let g:sandwich#recipes += [ \ {'buns': ['"""', '"""'], 'action': ['delete'], 'input': ['"']} \ ] < expr_filter ユーザーがフィルターを自作して使うことができます。リストの要素は式 |expression| として評価され、評価値が真 (1) の場合にはそのレシピは有効 となり、評価値が偽 (0) の場合にはレシピは無効となります。 > " A filter should be defined in somewhere, for example in your vimrc. function! FilterValid() return 1 endfunction function! FilterInvalid() return 0 endfunction " This recipe is valid let g:sandwich#recipes += [ \ {'buns': ['(', ')'], 'expr_filter': ['FilterValid()']} \ ] " This recipe is invalid let g:sandwich#recipes += [ \ {'buns': ['(', ')'], 'expr_filter': ['FilterInvalid()']} \ ] < 例えば、次のようなフィルターを定義すると特定のオペレーターでのみ有効な レシピを作ることができます。 > function! FilterOperator(operator) return a:operator ==# v:operator ? 1 : 0 endfunction " This recipe is valid only with d operator. let g:sandwich#recipes += [ \ {'buns': ['(', ')'], 'expr_filter': ['FilterOperator("d")']} \ ] < Local option~ local option はレシピごとに細かく挙動を調整するために設定されます。もし指定の ない場合はデフォルトで設定されている値が使われます。この値はテキストオブジェク トの種類に依って違う値をもち、 |g:textobj#sandwich#options| に格納されていま す。 |(textobj-sandwich-query-i)| と |(textobj-sandwich-query-a)| の "skip_break" オプションのデフォルト値を変更したい場合は次のようにします。 > let g:textobj#sandwich#options.query.skip_break = 1 < あるいは関数インターフェースも用意されています。 |textobj#sandwich#set()| > call textobj#sandwich#set('query', 'skip_break', 1) < g:textobj#sandwich#options *g:textobj#sandwich#options* local option のデフォルト値を格納した |Dictionary| 変数です。 > " let textobj#sandwich#options[kind][option] = {value} " For example let g:textobj#sandwich#options['query']['skip_break'] = 1 " or let g:textobj#sandwich#options.query.skip_break = 1 < *b:textobj_sandwich_options* |b:textobj_sandwich_options| が存在する場合はそちらが優先されます。 バッファについてローカルなオプション設定を使うのに便利でしょう。 使用可能な引数を次に列挙します。 * kind - query - auto * option - nesting - expand_range - regex - skip_regex - skip_regex_head - skip_regex_tail - quoteescape - expr - noremap - syntax - inner_syntax - match_syntax - skip_break - skip_expr *textobj#sandwich#set()* textobj#sandwich#set(kind, option, value) local option のデフォルト値を安全かつ簡単に変更するための関数です。引 数の組み合わせが不正な場合、関数はメッセージを表示します。使用可能な引 数は |g:textobj#sandwich#options| の項に列挙されています。また、これに 加えて、この関数では "all" を kind に指定することができます。 *textobj#sandwich#setlocal()* textobj#sandwich#setlocal(kind, option, value) local option のバッファについてローカルなデフォルト値を安全かつ簡単に 変更するための関数です。引数の組み合わせが不正な場合、関数はメッセージ を表示します。使用可能な引数は |g:textobj#sandwich#options| の項に列挙 されています。また、これに加えて、この関数では "all" を kind に指定す ることができます。 textobj#sandwich#set_default() *textobj#sandwich#set_default()* local option のデフォルト値を初期化します。 nesting このオプションは対象範囲の検索アルゴリズムを切り替えます。もし、"buns" がネスト構造を作る場合はこのオプションを真に設定してください。そうでな ければ偽に設定してください。例を挙げれば、一般的に () はネスト構造を作 りますが、"" は作りません。次のようなテキストではネスト構造の有無に よって期待する範囲が異なることがわかるでしょう。 キー入力 yib によって文字列をヤンクします。 > # : cursor case1: (foo(bar)baz) : foo(bar)baz is the expected result case2: "foo"bar"baz" : foo is the expected result < このオプションは特にレシピごとに設定されることが望ましいです。 デフォルト値 * query: 0 * auto : 0 expand_range このオプションが 0 なら検索はカーソル行内のみで行われます。正の整数を 与えた場合、その行数だけカーソル行から上下に検索範囲を拡大します。負の 整数を与えると、すべてのバッファを対象にして検索します。 NOTE: 内部的には広い検索範囲を一度に対象とはせず、段階的に広げながら検 索します。この間経過時間を監視しながら |g:textobj#sandwich#stimeoutlen| に設定された時間が経過してなお [count] 個の候補が見つかっていない場合、検索をそこで諦めます。 おおよその場合 0 か負数がよい選択かと思われます。 デフォルト値 * query: -1 * auto : -1 regex もしこのオプションの値が真なら "buns" は正規表現だとみなされます。 > let g:sandwich#recipes += [ \ {'buns': ['\d\+', '\d\+'], 'regex': 1} \ ] " Press dib " 123foo456 ---> 123456 < デフォルト値 * query: 0 * auto : 0 skip_regex skip_regex_head skip_regex_tail 選択範囲候補を検索の際に、候補をスキップするための正規表現を収めた |list| です。"buns" にマッチしたとしても同時にこのリストのどれかにも同 時にマッチした場合、この候補はスキップされます。正規表現は "buns" に マッチした文字列の先頭にマッチしなければなりません。 > let g:sandwich#recipes += [ \ {'buns': ['b', 'a'], 'skip_regex': ['a\zea']} \ ] " Press dib " baaaaaaaar ---> bar < "skip_regex" の要素は前方と後方の、両方の囲みの検索で使われます。通常 はこれで十分に便利なのですが、両方の囲みとエスケープ文字がすべて同一の 場合などに問題になります。例えば Vim script では、シングルクオーテーシ ョンに囲まれた領域内の連続した二つのシングルクオーテーションは一つのシ ングルクオーテーションとみなされます。 > :echo 'foo''bar' " -> foo'bar < これをスキップするためには前方の検索か、後方の検索かを区別しなければな りません。なので、 "skip_regex_head" 及び "skip_regex_tail" を使いまし ょう。 > let g:sandwich#recipes += [ \ { \ 'buns': ["'", "'"], \ 'filetype': ['vim'], \ 'expand_range': 0, \ 'skip_regex_head': \ ['\%(\%#\zs''\|''\%#\zs\)''\%(''''\)*[^'']'], \ 'skip_regex_tail': \ ['[^'']\%(''''\)*\%(\%#\zs''\|''\%#\zs\)'''], \ 'nesting': 0 \ 'match_syntax': 2, \ } \ ] < NOTE: 残念ながら、カーソルが両端の連続するシングルクオーテーションの上 にある場合はこの設定でもうまく働きません。 > ### ### : Cursor should not be on '''foo '' bar''' : the positions pointed by #. < 検索の間、内部的にカーソルを動かしているので、カーソルを表す正規表現の \%# |/\%#| が使えます。 デフォルト値 * query: [] * auto : [] quoteescape このオプションが真なら、'quoteescape' オプションに含まれる文字でエス ケープされた候補をスキップします。実用的には、'', "", ``, 専用のオプ ションです。 > let g:sandwich#recipes += [ \ {'buns': ['"', '"'], 'quoteescape': 1} \ ] " Press dib " "foo\"bar" ---> foo\"bar < デフォルト値 * query: 0 * auto : 0 expr このオプションの値が 1 か 2 なら "buns" は式として評価されてから検索さ れます。値が 1 の場合は一度だけ評価され、|.| コマンドでは同じものが使 用されます。値が 2 の場合は |.| コマンドで繰り返される毎に評価されます 。例えば以下のレシピはその時々の無名レジスタ |quotequote| の内容に挟ま れた範囲を検索します。 (参考: |:let-@|) > let g:sandwich#recipes += [ \ {'buns': ['@@', '@@'], 'expr': 1, 'input': ['@']} \ ] < NOTE: このオプションが真の時、 "buns" はレシピを呼ぶためのキー入力とは みなされませんので "input" の指定が必要になります。 NOTE: 評価の結果 "buns" が空文字列を含むと |textobj-sandwich| は選択を 中止します。 NOTE: もし式が |getchar()| や |input()| でユーザーに入力を求める場合、 |operator-sandwich| の囲みを外すオペレータや |textobj-sandwich| の |(textobj-sandwich-auto-i)| や |(textobj-sandwich-auto-a)| で不都合が出ることがあります。 この問題を避けるために、 "kind" フィルターを併用することが推奨さ れます。例えば次のレシピは二文字の入力を求め、その二文字に囲まれ たテキストを選択します。 > let g:textobj#sandwich#recipes += [ \ { \ 'buns': ['GetChar()', 'GetChar()'], \ 'kind': ['query'], \ 'expr': 1, \ 'input': ['f'] \ }, \ ] function! GetChar() abort let c = getchar() let c = type(c) == type(0) ? nr2char(c) : c return c ==# "\" || c ==# "\" ? '' : c endfunction < デフォルト値 * query: 0 * auto : 0 noremap このオプションは "external" を持つレシピにのみ適用されます。このオプ ションが真なら "external" テキストオブジェクトのマッピングは展開されま せん。このオプションが偽ならマッピングが展開されます。"external" の項 もご覧ください。 NOTE: 内部的にはテキストオブジェクトの選択範囲を調べるのに、ビジュアル モード選択を使いますので、このオプションが適用されるのはビジュア ルモードへのマッピングになります。 デフォルト値 * query: 1 * auto : 1 syntax テキストの囲みに適用されていることを期待するシンタックスグループ名のリ ストです。このリストが空でなければ、テキストオブジェクトは選択範囲の候 補検索時にシンタックスを確認し、リスト要素にマッチするものがなければス キップします。構文ハイライトがオフになっていれば単に無視されます。 > " check the position pointed by # " # # " "foo" < 例えば "" は一般的にいくつかの限られたハイライトグループが適用されてい ることが期待できます。 > let g:sandwich#recipes += [ \ { \ 'buns': ['"', '"'], \ 'quoteescape': 1, \ 'expand_range': 0, \ 'nesting': 0, \ 'match_syntax': 1, \ 'syntax': \ ['Constant', 'Statement', 'Special', 'String', 'Comment'], \ 'inner_syntax': \ ['Constant', 'PreProc', 'Special', 'String', 'Comment'], \ } \ ] < このレシピは次のような場合も文字列としての "" に囲まれた領域をよく認識 します。 > key press vas" selects as following. # means cursor positions. <-#-> list = ["foo", "bar"] <------#-----> < しかし残念ながらこのレシピは汎用的とはいえずマークダウンのようなファイ ルタイプでうまく働きません。なので可能ならば使用するファイルタイプごと に設定するのが望ましいでしょう。 > let g:sandwich#recipes += [ \ { \ 'buns': ['"', '"'], \ 'filetype': ['ruby'], \ 'quoteescape': 1, \ 'expand_range': 0, \ 'nesting': 0, \ 'match_syntax': 1, \ 'syntax': \ ['Constant', 'Special', 'String', 'Comment'], \ 'inner_syntax': \ ['Constant', 'Special', 'String', 'Comment'], \ } \ ] let g:sandwich#recipes += [ \ { \ 'buns': ['"', '"'], \ 'filetype': ['sh'], \ 'quoteescape': 1, \ 'expand_range': 0, \ 'nesting': 0, \ 'match_syntax': 1, \ 'syntax': \ ['Constant', 'Statement', 'String', 'Comment'], \ 'inner_syntax': \ ['Constant', 'Special', 'PreProc', 'String', 'Comment'], \ } \ ] < よく使われるハイライトグループは多くのカラースキーマで共有されていま す。こちらをご覧ください。 |group-name| NOTE: リストの要素がシンタックススタックの中に含まれていれば成功とみな されます。シンタックススタックは次のようなキーマッピングを対象文 字列の上で使用することにより確認できます。 > nnoremap s :echo map(synstack(line('.'), col('.')), \ 'synIDattr(synIDtrans(v:val), "name")') < デフォルト値 * query: [] * auto : [] inner_syntax 囲まれたテキストの両端に適用されていることを期待するシンタックスグルー プ名のリストです。このリストが空でなければ、テキストオブジェクトは選択 範囲の候補検索時にシンタックスを確認し、リスト要素にマッチするものがな ければスキップします。構文ハイライトがオフになっていれば単に無視されま す。 > let g:sandwich#recipes += [ \ { \ 'buns': ['"', '"'], \ 'quoteescape': 1, \ 'inner_syntax': ['Constant', 'String'] \ } \ ] " check the position pointed by # " # # " "foo" < 一つ上の local option "syntax" の項に実用的な使用例があります。こちら もご覧ください。 よく使われるハイライトグループは多くのカラースキーマで共有されていま す。こちらをご覧ください。 |group-name| NOTE: リストの要素がシンタックススタックの中に含まれていれば成功とみな されます。シンタックススタックは次のようなキーマッピングを対象文 字列の上で使用することにより確認できます。 > nnoremap s :echo map(synstack(line('.'), col('.')), \ 'synIDattr(synIDtrans(v:val), "name")') < デフォルト値 * query: [] * auto : [] match_syntax もしこのオプションが 1 のとき、テキストオブジェクトは文字列両端の囲み のシンタックスハイライトが一致していることを確認します。一致しなければ その候補はスキップされるでしょう。さらにこのオプションが 2 の時には、 これに加えて囲まれたテキストの両端も同じハイライトが適用されているかを 確かめます。このオプションが 3 の場合は、 2 の場合とほとんど同じですが 囲みと囲まれたテキストをそれぞれ独立に調べます。すなわち、両方の囲みの ハイライトが同じ且つ囲まれた文字列の両端のハイライトが同じ必要がありま すが、囲みと内側の文字列のハイライトが一致する必要はありません。 > AB CD (foo) match_syntax is 1: A == D is required. match_syntax is 2: A == D == B == C is required. match_syntax is 3: A == D and B == C is required. < 囲み文字が一文字でない場合はそれぞれの先頭位置が確認されます。 例えば '' は一般的にそれ自体を含み、内部の文字列もConstant か String シンタックスが適用されているでしょう。 > let g:sandwich#recipes += [ \ { \ 'buns': ["'", "'"], \ 'quoteescape' : 1, \ 'syntax' : ['Constant', 'String'], \ 'match_syntax': 2, \ } \ ] < Local option "syntax" の項にも使用例がありますのでご覧ください。 NOTE: 適用されたシンタックスアイテムは次のようなキーマッピングを対象文 字列の上で使用することで調べられます。 > nnoremap S \ :echo synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'name') < 文字列両端の囲みの表示されているシンタックスは厳密に一致している 必要があります。囲みの内側のシンタックスについては少し条件が緩和 され、文字列囲みのシンタックスがシンタックススタックに含まれてい れば一致とみなされます。これは例えば変換指定子 "%s" のようなもの がしばしば String 以外の色でハイライトされることを考慮するためで す。表示上の色は違ってもシンタックススタックに String が含まれて いることが期待できるため、表示上の色は違っても通常の文字列リテラ ルと同様に扱われます。シンタックススタックは次のようなキーマッピ ングを調べたいテキスト上で使用することで確認できます。透過属性が 指定されていない限りリストの最後の要素が表示されている実効シンタ ックスアイテムです。 > nnoremap s :echo map(synstack(line('.'), col('.')), \ 'synIDattr(synIDtrans(v:val), "name")') < デフォルト値 * query: 0 * auto : 0 skip_break 次のようなテキストについて考えます。 > { foo } < {} に囲まれたテキストは "\n foo\n" なので、キー入力 dib の結果は次の ようになります。 > {} < しかし、次のようになったほうが便利な場合もありますね。 > { } < このオプションが真のときテキストオブジェクトは両端の改行文字をスキップ して後者を与えます。さらにインデントを保存するために改行に続くホワイト スペースも一緒にスキップします。つまり "\n\\s*" というパターンにマッチ する文字列がスキップされます。 このオプションは |(textobj-sandwich-query-i)| 及び |(textobj-sandwich-auto-i)| に対してのみ有効です。 デフォルト値 * query: 0 * auto : 0 skip_expr 式 |expression| のリストを値にとります。選択範囲を検索する際、 buns に マッチする箇所を見つけるたびに式が評価され、もし返り値が真 (非ゼロ値) ならそのマッチ箇所をスキップします。 > " skip 'a's other than head and tail of a line. let g:sandwich#recipes += [ \ { \ 'buns': ['a', 'a'], \ 'skip_expr': [ \ 'col(".")[2] != 1 && col(".")[2] != col([line("."), "$"])-1' \ ] \ } \ ] " Press dib " aaaaa ---> aa < もし、リストの要素が |Funcref| の場合は注意が必要です。その関数は二つ の引数をとれるようにする必要があります。 > :function {func}(is_head, pos) < もし、 a:is_head が1なら前方の囲みを検索中です。もし、 a:is_head が0な ら後方の囲みを検索中になります。 a:pos は buns にマッチした先頭位置を 表す四要素のリストです。これは |getpos()| 関数とおなじフォーマットを持 ちます。必ずしもこれらの引数を使う必要はありません。将来、引数が追加さ れることがありうるので、可変長引数を指定しておくことをお勧めします。 > " skip 'a's other than head and tail of a line. function! SkipIntermediates(is_head, pos, ...) if a:is_head return a:pos[2] != 1 else return a:pos[2] != col([a:pos[1], '$'])-1 endif endfunction let g:sandwich#recipes += [ \ { \ 'buns': ['a', 'a'], \ 'skip_expr': [function('SkipIntermediates')] \ } \ ] " Press dib " aaaaa ---> aa < デフォルト値 * query: [] * auto : [] Global option~ いくつか基本的な動作を制御するためのオプションが存在します。 g:textobj#sandwich#timeout *g:textobj#sandwich#timeout* このオプションに偽値が設定されている場合、テキストオブジェクトは一つの レシピを指定する完全な入力がなされるまで待ちます。例えば、下記のレシピ を用意します。 > let g:sandwich#recipes = [ \ {'buns': ['for {', '}'], 'nesting': 1, 'input': ['bf']} \ {'buns': ['if {', '}'], 'nesting': 1, 'input': ['bi']} \ {'buns': ['else {', '}'], 'nesting': 1, 'input': ['be']} \ ] < このオプションが真の場合は `saiwb` とタイプして少し待つと、テキストオ ブジェクトはタイムアウトのため `b` で囲まれたテキストを選択します。 しかし、偽の場合は一つのレシピを指定する入力が完成するまで待ちます。 この変数が定義されていなければ |g:sandwich#timeout| が代わりに参照され ます。特別な理由がなければ |g:sandwich#timeout| を使ってください。 関連:|g:textobj#sandwich#timeoutlen| g:textobj#sandwich#timeoutlen *g:textobj#sandwich#timeoutlen* 入力に前方一致で重複するレシピが存在する場合に次のキーシーケンスを待つ 時間をミリ秒単位で指定します。 > let g:sandwich#recipes = [ \ {'buns': ['(', ')']} \ {'buns': ['((', '))']} \ ] < vis( とキー入力するとテキストオブジェクトは次に ( が入力されるかこの時 間だけ待ちます。この間にもう一度 ( を押下すると '((' と '))' のレシピ が使われるでしょう。キーの押下なしでこの時間が過ぎると '(' と ')' のレ シピが使われるでしょう。この変数が定義されていなければ |g:sandwich#timeoutlen| が代わりに参照されます。特別な理由がなければ |g:sandwich#timeoutlen| を使ってください。 タイムアウト機能(|g:textobj#sandwich#timeout|, |g:sandwich#timeout|, 'timeout')がオフの場合はこのオプションは無視されます。 g:textobj#sandwich#stimeoutlen *g:textobj#sandwich#stimeoutlen* テキストオブジェクトが選択領域の検索を諦めるまでの時間をミリ秒単位で指 定します。カウントに指定された数の候補を見つけられずにこの時間が経過す ると検索を諦め、何も選択せずにユーザーに操作をかえします。デフォルト値 は 500 です。 g:textobj#sandwich#latest_jump *g:textobj#sandwich#latest_jump* このオプションの値が真ならテキストオブジェクトは範囲選択の際にもとのカ ーソル位置をマーク |``| に記録します。 これにより |``| コマンドでカー ソルをその位置に戻すことができます。意図しない広い範囲を選択してしまっ た際にカーソル位置を戻すのに便利です。デフォルト値は 1 です。 ============================================================================== MISCELLANEOUS *textobj-sandwich-miscellaneous* dib で欲しいのはそれじゃない~ キー入力 dib は次のように働きます。これを > ( foo ) < このようにします。 > () < なぜならば、 () に囲まれたテキストは ' foo' ではなく '\n foo\n' だからです。しかし次のような結果 > ( ) < を期待する場合は local option の "skip_break" が使えます。 > call textobj#sandwich#set('all', 'skip_break', 1) < 頻繁に「 Timed out 」となる~ |g:textobj#sandwich#stimeoutlen| に大きめの値を設定しましょう。デフォ ルト設定は 500 です。 誤操作でカーソルが飛んでいった!~ 慌てないでください。編集結果は |u| コマンドで戻せるでしょう。カーソル 位置は |``| コマンドでもとの位置に戻すことができます。こちらも合わせて ご覧ください。|g:textobj#sandwich#latest_jump| ============================================================================== vim:tw=78:ts=8:ft=help:norl:noet: