From d30dfc89bf1b673b2fdc0638766b930adaec228c Mon Sep 17 00:00:00 2001
From: nsfisis
#include <iostream>
-
-[[alignas]] [[alignof]] [[and]] [[and_eq]] [[asm]] [[auto]] [[bitand]]
-[[bitor]] [[bool]] [[break]] [[case]] [[catch]] [[char]] [[char16_t]]
-[[char32_t]] [[class]] [[compl]] [[const]] [[const_cast]] [[constexpr]]
-[[continue]] [[decltype]] [[default]] [[delete]] [[do]] [[double]]
-[[dynamic_cast]] [[else]] [[enum]] [[explicit]] [[export]] [[extern]] [[false]]
-[[final]] [[float]] [[for]] [[friend]] [[goto]] [[if]] [[inline]] [[int]]
-[[long]] [[mutable]] [[namespace]] [[new]] [[noexcept]] [[not]] [[not_eq]]
-[[nullptr]] [[operator]] [[or]] [[or_eq]] [[override]] [[private]]
-[[protected]] [[public]] [[register]] [[reinterpret_cast]] [[return]] [[short]]
-[[signed]] [[sizeof]] [[static]] [[static_assert]] [[static_cast]] [[struct]]
-[[switch]] [[template]] [[this]] [[thread_local]] [[throw]] [[true]] [[try]]
-[[typedef]] [[typeid]] [[typename]] [[union]] [[unsigned]]
-[[virtual]] [[void]] [[volatile]] [[wchar_t]] [[while]] [[xor]] [[xor_eq]]
-// [[using]]
-int main() {
- std::cout << "Hello, World!" << std::endl;
-}
+ #include <iostream>
+
+[[alignas]] [[alignof]] [[and]] [[and_eq]] [[asm]] [[auto]] [[bitand]]
+[[bitor]] [[bool]] [[break]] [[case]] [[catch]] [[char]] [[char16_t]]
+[[char32_t]] [[class]] [[compl]] [[const]] [[const_cast]] [[constexpr]]
+[[continue]] [[decltype]] [[default]] [[delete]] [[do]] [[double]]
+[[dynamic_cast]] [[else]] [[enum]] [[explicit]] [[export]] [[extern]] [[false]]
+[[final]] [[float]] [[for]] [[friend]] [[goto]] [[if]] [[inline]] [[int]]
+[[long]] [[mutable]] [[namespace]] [[new]] [[noexcept]] [[not]] [[not_eq]]
+[[nullptr]] [[operator]] [[or]] [[or_eq]] [[override]] [[private]]
+[[protected]] [[public]] [[register]] [[reinterpret_cast]] [[return]] [[short]]
+[[signed]] [[sizeof]] [[static]] [[static_assert]] [[static_cast]] [[struct]]
+[[switch]] [[template]] [[this]] [[thread_local]] [[throw]] [[true]] [[try]]
+[[typedef]] [[typeid]] [[typename]] [[union]] [[unsigned]]
+[[virtual]] [[void]] [[volatile]] [[wchar_t]] [[while]] [[xor]] [[xor_eq]]
+// [[using]]
+int main() {
+ std::cout << "Hello, World!" << std::endl;
+}
+ -@@ -137,8 +138,10 @@ 上のコードでは
-[[using]]をコメントアウトしているが、これはusingキーワードのみ属性構文の中で意味を持つからであり、このコメントアウトを外すとコンパイルに失敗する。+// using の例 -[[using foo: attr1, attr2]] int x; // [[foo::attr1, foo::attr2]] の糖衣構文++// using の例 +[[using foo: attr1, attr2]] int x; // [[foo::attr1, foo::attr2]] の糖衣構文C++17 の仕様も見てみる (正確には標準化前のドラフト)。 diff --git a/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html b/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html index 91ec5d0a..f3a11356 100644 --- a/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html +++ b/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html @@ -14,8 +14,7 @@
【Python】 クロージャとUnboundLocalError: local variable 'x' referenced before assignment|REPL: Rest-Eat-Program Loop - - +@@ -78,13 +77,15 @@ Python でクロージャを作ろうと、次のようなコードを書いた。 - +def f(): - x = 0 - def g(): - x += 1 - g() - -f()++def f(): + x = 0 + def g(): + x += 1 + g() + +f()関数
-gから 関数fのスコープ内で定義された変数xを参照し、それに 1 を足そうとしている。 これを実行するとx += 1の箇所でエラーが発生する。 @@ -100,27 +101,31 @@ f() local変数xが代入前に参照された、とある。これは、fのxを参照するのではなく、新しく別の変数をg内に作ってしまっているため。前述のコードを宣言と代入を便宜上分けて書き直すと次のようになる。varを変数宣言のための構文として擬似的に利用している。+# 注: var は正しい Python の文法ではない。上記参照のこと -def f(): - var x # f の local変数 'x' を宣言 - x = 0 # x に 0 を代入 - def g(): # f の内部関数 g を定義 - var x # g の local変数 'x' を宣言 - # たまたま f にも同じ名前の変数があるが、それとは別の変数 - x += 1 # x に 1 を加算 (x = x + 1 の糖衣構文) - # 加算する前の値を参照しようとするが、まだ代入されていないためエラー - g()++# 注: var は正しい Python の文法ではない。上記参照のこと +def f(): + var x # f の local変数 'x' を宣言 + x = 0 # x に 0 を代入 + def g(): # f の内部関数 g を定義 + var x # g の local変数 'x' を宣言 + # たまたま f にも同じ名前の変数があるが、それとは別の変数 + x += 1 # x に 1 を加算 (x = x + 1 の糖衣構文) + # 加算する前の値を参照しようとするが、まだ代入されていないためエラー + g()当初の意図を表現するには、次のように書けばよい。
-+def f(): - x = 0 - def g(): - nonlocal x ## (*) - x += 1 - g()++def f(): + x = 0 + def g(): + nonlocal x ## (*) + x += 1 + g()
(*)のように、nonlocalを追加する。これにより一つ外側のスコープ (gの一つ外側 =f) で定義されているxを探しに行くようになる。 diff --git a/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html b/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html index 0e9ee932..cf2eb729 100644 --- a/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html +++ b/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html @@ -14,8 +14,7 @@【Ruby】 自身を実行している処理系の種類を判定する|REPL: Rest-Eat-Program Loop - - +@@ -83,12 +82,14 @@ 上記ページの例から引用する: - +$ ruby-1.9.1 -ve 'p RUBY_ENGINE' -ruby 1.9.1p0 (2009-03-04 revision 22762) [x86_64-linux] -"ruby" -$ jruby -ve 'p RUBY_ENGINE' -jruby 1.2.0 (ruby 1.8.6 patchlevel 287) (2009-03-16 rev 9419) [i386-java] -"jruby"++$ ruby-1.9.1 -ve 'p RUBY_ENGINE' +ruby 1.9.1p0 (2009-03-04 revision 22762) [x86_64-linux] +"ruby" +$ jruby -ve 'p RUBY_ENGINE' +jruby 1.2.0 (ruby 1.8.6 patchlevel 287) (2009-03-16 rev 9419) [i386-java] +"jruby"それぞれの処理系がどのような値を返すかだが、stack overflow に良い質問と回答があった。 @@ -208,10 +209,12 @@ jruby 1.2.0 (ruby 1.8.6 patchlevel 287) (2009-03-16 rev 9419) [i386-java] mruby 該当部分のソース より引用:
-+/* -* Ruby engine. -*/ -#define MRUBY_RUBY_ENGINE "mruby"+diff --git a/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html b/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html index a11a2f15..76f7058c 100644 --- a/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html +++ b/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html @@ -14,8 +14,7 @@+/* +* Ruby engine. +*/ +#define MRUBY_RUBY_ENGINE "mruby"【Ruby】 then キーワードと case in|REPL: Rest-Eat-Program Loop - - +@@ -83,36 +82,40 @@ 使われることは稀だが、Ruby では thenがキーワードになっている。次のように使う: -+if cond then - puts "Y" -else - puts "N" -end++if cond then + puts "Y" +else + puts "N" +endこのキーワードが現れうる場所はいくつかあり、
-if、unless、rescue、case構文がそれに当たる。 上記のように、何か条件を書いた後thenを置き、式がそこで終了していることを示すマーカーとして機能する。+# Example: - -if x then - a -end - -unless x then - a -end - -begin - a -rescue then - b -end - -case x -when p then - a -end++# Example: + +if x then + a +end + +unless x then + a +end + +begin + a +rescue then + b +end + +case x +when p then + a +end@@ -121,17 +124,21 @@ 普通 Ruby のコードで thenを書くことはない。なぜか。次のコードを実行してみるとわかる。 -+if true puts 'Hello, World!' end++if true puts 'Hello, World!' end次のような構文エラーが出力される。
-+20:1: syntax error, unexpected local variable or method, expecting `then' or ';' or '\n' -if true puts 'Hello, World!' end - ^~~~ -20:1: syntax error, unexpected `end', expecting end-of-input -...f true puts 'Hello, World!' end++20:1: syntax error, unexpected local variable or method, expecting `then' or ';' or '\n' +if true puts 'Hello, World!' end + ^~~~ +20:1: syntax error, unexpected `end', expecting end-of-input +...f true puts 'Hello, World!' end二つ目のメッセージは無視して一つ目を読むと、
-thenか;か改行が来るはずのところ変数だかメソッドだかが現れたことによりエラーとなっているようだ。 @@ -141,8 +148,10 @@ if true puts 'Hello, World!' end ポイントは改行がthen(や;) の代わりとなることである。trueの後に改行を入れてみる。+if true -puts 'Hello, World!' end++if true +puts 'Hello, World!' end無事 Hello, World! と出力されるようになった。 @@ -155,21 +164,27 @@ puts 'Hello, World!'
if a b end+++if a b end-
thenも;も改行もないのでエラーになるが、これは条件式がどこまで続いているのかわからないためだ。この例は二通りに解釈できる。+# a という変数かメソッドの評価結果が truthy なら b という変数かメソッドを評価 -if a then -b -end+-+# a という変数かメソッドの評価結果が truthy なら b という変数かメソッドを評価 +if a then +b +end+# a というメソッドに b という変数かメソッドの評価結果を渡して呼び出し、 -# その結果が truthy なら何もしない -if a(b) then -end++# a というメソッドに b という変数かメソッドの評価結果を渡して呼び出し、 +# その結果が truthy なら何もしない +if a(b) then +end-
then等はこの曖昧性を排除するためにあり、条件式はifからthen等までの間にある、ということを明確にする。 C系のif後に来る(/)や、Python の:、Rust/Go/Swift などの{も同じ役割を持つ。 @@ -190,39 +205,43 @@ b https://github.com/ruby/ruby/blob/221ca0f8281d39f0dfdfe13b2448875384bbf735/parse.y#L3961-L3986+p_case_body : keyword_in -{ - SET_LEX_STATE(EXPR_BEG|EXPR_LABEL); - p->command_start = FALSE; - $<ctxt>1 = p->ctxt; - p->ctxt.in_kwarg = 1; - $<tbl>$ = push_pvtbl(p); -} -{ - $<tbl>$ = push_pktbl(p); -} -p_top_expr then -{ - pop_pktbl(p, $<tbl>3); - pop_pvtbl(p, $<tbl>2); - p->ctxt.in_kwarg = $<ctxt>1.in_kwarg; -} -compstmt -p_cases -{ - /*%%%*/ - $$ = NEW_IN($4, $7, $8, &@$); - /*% %*/ - /*% ripper: in!($4, $7, escape_Qundef($8)) %*/ -} -;++p_case_body : keyword_in +{ + SET_LEX_STATE(EXPR_BEG|EXPR_LABEL); + p->command_start = FALSE; + $<ctxt>1 = p->ctxt; + p->ctxt.in_kwarg = 1; + $<tbl>$ = push_pvtbl(p); +} +{ + $<tbl>$ = push_pktbl(p); +} +p_top_expr then +{ + pop_pktbl(p, $<tbl>3); + pop_pvtbl(p, $<tbl>2); + p->ctxt.in_kwarg = $<ctxt>1.in_kwarg; +} +compstmt +p_cases +{ + /*%%%*/ + $$ = NEW_IN($4, $7, $8, &@$); + /*% %*/ + /*% ripper: in!($4, $7, escape_Qundef($8)) %*/ +} +;簡略版:
-+p_case_body : keyword_in p_top_expr then compstmt p_cases -;++p_case_body : keyword_in p_top_expr then compstmt p_cases +;ここで、
-keyword_inは文字通りin、p_top_exprはいわゆるパターン、thenはthenキーワードのことではなく、この記事でthen等と呼んでいるもの、つまりthenキーワード、;、改行のいずれかである。 @@ -232,36 +251,40 @@ p_cases これにより、case-whenによる従来の構文と同じように、then等をパターンの後ろに挿入すればよいことがわかった。つまり次の3通りのいずれかになる:+case x -in 1 then a -in 2 then b -in 3 then c -end - -case x -in 1 - a -in 2 - b -in 3 - c -end - -case x -in 1; a -in 2; b -in 3; c -end++case x +in 1 then a +in 2 then b +in 3 then c +end + +case x +in 1 + a +in 2 + b +in 3 + c +end + +case x +in 1; a +in 2; b +in 3; c +endところで、
-p_top_exprにはifによる guard clause が書けるので、その場合はif-thenと似たような見た目になる。+case x -in 0 then a -in n if n < 0 then b -in n then c -end++case x +in 0 then a +in n if n < 0 then b +in n then c +enddiff --git a/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html b/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html index 69cfb8a4..4ae7f235 100644 --- a/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html +++ b/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html @@ -14,8 +14,7 @@ Rust のプリミティブ型はどこからやって来るか|REPL: Rest-Eat-Program Loop - - +@@ -73,26 +72,28 @@ Rust において、プリミティブ型の名前は予約語でない。したがって、次のコードは合法である。 - +#![allow(non_camel_case_types)] -#![allow(dead_code)] - -struct bool; -struct char; -struct i8; -struct i16; -struct i32; -struct i64; -struct i128; -struct isize; -struct u8; -struct u16; -struct u32; -struct u64; -struct u128; -struct usize; -struct f32; -struct f64; -struct str;++#![allow(non_camel_case_types)] +#![allow(dead_code)] + +struct bool; +struct char; +struct i8; +struct i16; +struct i32; +struct i64; +struct i128; +struct isize; +struct u8; +struct u16; +struct u32; +struct u64; +struct u128; +struct usize; +struct f32; +struct f64; +struct str;では、普段単に
-boolと書いたとき、このboolは一体どこから来ているのか。rustc のソースを追ってみた。 @@ -127,60 +128,66 @@rustcはセルフホストされている (=rustc自身が Rust で書かれている) ので、boolやcharなどで適当に検索をかけてもノイズが多すぎて話にならない。しかし、お誂え向きなことにi128/u128というコンパイラ自身が使うことがなさそうな型が存在するのでこれを使ってgit grepしてみる。+$ git grep "\bi128\b" | wc # i128 -165 1069 15790 - -$ git grep "\bu128\b" | wc # u128 -293 2127 26667 - -$ git grep "\bbool\b" | wc # cf. bool の結果 -3563 23577 294659++$ git grep "\bi128\b" | wc # i128 +165 1069 15790 + +$ git grep "\bu128\b" | wc # u128 +293 2127 26667 + +$ git grep "\bbool\b" | wc # cf. bool の結果 +3563 23577 294659165 程度であれば探すことができそうだ。今回は、クレート名を見ておおよその当たりをつけた。
-+$ git grep "\bi128\b" -... -rustc_resolve/src/lib.rs: table.insert(sym::i128, Int(IntTy::I128)); -...++$ git grep "\bi128\b" +... +rustc_resolve/src/lib.rs: table.insert(sym::i128, Int(IntTy::I128)); +...-
rustc_resolveというのはいかにも名前解決を担いそうなクレート名である。該当箇所を見てみる。+/// Interns the names of the primitive types. -/// -/// All other types are defined somewhere and possibly imported, but the primitive ones need -/// special handling, since they have no place of origin. -struct PrimitiveTypeTable { - primitive_types: FxHashMap<Symbol, PrimTy>, -} - -impl PrimitiveTypeTable { - fn new() -> PrimitiveTypeTable { - let mut table = FxHashMap::default(); - - table.insert(sym::bool, Bool); - table.insert(sym::char, Char); - table.insert(sym::f32, Float(FloatTy::F32)); - table.insert(sym::f64, Float(FloatTy::F64)); - table.insert(sym::isize, Int(IntTy::Isize)); - table.insert(sym::i8, Int(IntTy::I8)); - table.insert(sym::i16, Int(IntTy::I16)); - table.insert(sym::i32, Int(IntTy::I32)); - table.insert(sym::i64, Int(IntTy::I64)); - table.insert(sym::i128, Int(IntTy::I128)); - table.insert(sym::str, Str); - table.insert(sym::usize, Uint(UintTy::Usize)); - table.insert(sym::u8, Uint(UintTy::U8)); - table.insert(sym::u16, Uint(UintTy::U16)); - table.insert(sym::u32, Uint(UintTy::U32)); - table.insert(sym::u64, Uint(UintTy::U64)); - table.insert(sym::u128, Uint(UintTy::U128)); - Self { primitive_types: table } - } -}++/// Interns the names of the primitive types. +/// +/// All other types are defined somewhere and possibly imported, but the primitive ones need +/// special handling, since they have no place of origin. +struct PrimitiveTypeTable { + primitive_types: FxHashMap<Symbol, PrimTy>, +} + +impl PrimitiveTypeTable { + fn new() -> PrimitiveTypeTable { + let mut table = FxHashMap::default(); + + table.insert(sym::bool, Bool); + table.insert(sym::char, Char); + table.insert(sym::f32, Float(FloatTy::F32)); + table.insert(sym::f64, Float(FloatTy::F64)); + table.insert(sym::isize, Int(IntTy::Isize)); + table.insert(sym::i8, Int(IntTy::I8)); + table.insert(sym::i16, Int(IntTy::I16)); + table.insert(sym::i32, Int(IntTy::I32)); + table.insert(sym::i64, Int(IntTy::I64)); + table.insert(sym::i128, Int(IntTy::I128)); + table.insert(sym::str, Str); + table.insert(sym::usize, Uint(UintTy::Usize)); + table.insert(sym::u8, Uint(UintTy::U8)); + table.insert(sym::u16, Uint(UintTy::U16)); + table.insert(sym::u32, Uint(UintTy::U32)); + table.insert(sym::u64, Uint(UintTy::U64)); + table.insert(sym::u128, Uint(UintTy::U128)); + Self { primitive_types: table } + } +}これは初めに列挙したプリミティブ型の一覧と一致している。doc comment にも、 @@ -196,27 +203,29 @@ rustc_resolve/src/lib.rs: table.insert(sym::i128, Int(IntTy::I128)); とある。次はこの struct の使用箇所を追う。追うと言っても使われている箇所は次の一箇所しかない。なお説明に不要な箇所は大きく削っている。
-+/// This resolves the identifier `ident` in the namespace `ns` in the current lexical scope. -/// (略) -fn resolve_ident_in_lexical_scope( - &mut self, - mut ident: Ident, - ns: Namespace, - // (略) -) -> Option<LexicalScopeBinding<'a>> { - // (略) - - if ns == TypeNS { - if let Some(prim_ty) = self.primitive_type_table.primitive_types.get(&ident.name) { - let binding = - (Res::PrimTy(*prim_ty), ty::Visibility::Public, DUMMY_SP, ExpnId::root()) - .to_name_binding(self.arenas); - return Some(LexicalScopeBinding::Item(binding)); - } - } - - None -}++/// This resolves the identifier `ident` in the namespace `ns` in the current lexical scope. +/// (略) +fn resolve_ident_in_lexical_scope( + &mut self, + mut ident: Ident, + ns: Namespace, + // (略) +) -> Option<LexicalScopeBinding<'a>> { + // (略) + + if ns == TypeNS { + if let Some(prim_ty) = self.primitive_type_table.primitive_types.get(&ident.name) { + let binding = + (Res::PrimTy(*prim_ty), ty::Visibility::Public, DUMMY_SP, ExpnId::root()) + .to_name_binding(self.arenas); + return Some(LexicalScopeBinding::Item(binding)); + } + } + + None +}関数名や doc comment が示している通り、この関数は識別子 (identifier, ident) を現在のレキシカルスコープ内で解決 (resolve) する。
-if ns == TypeNSのブロック内では、primitive_type_table(上記のPrimitiveTypeTable::new()で作られた変数) に含まれている識別子 (bool、i32など) かどうか判定し、そうであればそれに紐づけられたプリミティブ型を返している。 @@ -234,13 +243,15 @@ rustc_resolve/src/lib.rs: table.insert(sym::i128, Int(IntTy::I128)); 動作がわかったところで、例として次のコードを考える。+#![allow(non_camel_case_types)] - -struct bool; - -fn main() { - let _: bool = bool; -}++#![allow(non_camel_case_types)] + +struct bool; + +fn main() { + let _: bool = bool; +}ここで
main()のboolはstruct boolとして解決される。なぜなら、プリミティブ型の判定をする前にboolという名前の別の型が見つかるからだ。 diff --git a/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html b/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html index 72c04ee1..ddf70cae 100644 --- a/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html +++ b/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html @@ -14,8 +14,7 @@【Vim】 autocmd events の BufWrite/BufWritePre の違い|REPL: Rest-Eat-Program Loop - - +@@ -124,24 +123,30 @@ https://github.com/vim/vim/blob/8e6be34338f13a6a625f19bcef82019c9adc65f2/src/autocmd.c#L85-L86 - +{"BufAdd", EVENT_BUFADD}, -{"BufCreate", EVENT_BUFADD},++{"BufAdd", EVENT_BUFADD}, +{"BufCreate", EVENT_BUFADD},https://github.com/vim/vim/blob/8e6be34338f13a6a625f19bcef82019c9adc65f2/src/autocmd.c#L95-L97
-+{"BufRead", EVENT_BUFREADPOST}, -{"BufReadCmd", EVENT_BUFREADCMD}, -{"BufReadPost", EVENT_BUFREADPOST},++{"BufRead", EVENT_BUFREADPOST}, +{"BufReadCmd", EVENT_BUFREADCMD}, +{"BufReadPost", EVENT_BUFREADPOST},https://github.com/vim/vim/blob/8e6be34338f13a6a625f19bcef82019c9adc65f2/src/autocmd.c#L103-L105
-+{"BufWrite", EVENT_BUFWRITEPRE}, -{"BufWritePost", EVENT_BUFWRITEPOST}, -{"BufWritePre", EVENT_BUFWRITEPRE},++{"BufWrite", EVENT_BUFWRITEPRE}, +{"BufWritePost", EVENT_BUFWRITEPOST}, +{"BufWritePre", EVENT_BUFWRITEPRE},@@ -154,20 +159,24 @@ https://github.com/neovim/neovim/blob/71d4f5851f068eeb432af34850dddda8cc1c71e3/src/nvim/auevents.lua#L119-L124 - diff --git a/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html b/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html index 00a6265b..afee803f 100644 --- a/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html +++ b/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html @@ -14,8 +14,7 @@+aliases = { -BufCreate = 'BufAdd', -BufRead = 'BufReadPost', -BufWrite = 'BufWritePre', -FileEncoding = 'EncodingChanged', -},++aliases = { + BufCreate = 'BufAdd', + BufRead = 'BufReadPost', + BufWrite = 'BufWritePre', + FileEncoding = 'EncodingChanged', +},ところで、上では取り上げなかった
-FileEncodingだが、これは:help FileEncodingにしっかりと書いてある。+*FileEncoding* -FileEncoding Obsolete. It still works and is equivalent - to |EncodingChanged|.++*FileEncoding* +FileEncoding Obsolete. It still works and is equivalent + to |EncodingChanged|.Vimで選択した行の順番を入れ替える|REPL: Rest-Eat-Program Loop - - +@@ -69,11 +68,13 @@ TL; DR
-+" License: Public Domain - -command! -bar -range=% - \ Reverse - \ keeppatterns <line1>,<line2>g/^/m<line1>-1++" License: Public Domain + +command! -bar -range=% + \ Reverse + \ keeppatterns <line1>,<line2>g/^/m<line1>-1@@ -142,9 +143,11 @@ command! -bar -range=% なお、 :g/^/m0は全ての行を入れ替えるが、:N,Mg/^/mN-1とすることで N行目から M行目を処理範囲とするよう拡張できる。手でこれを入力するわけにはいかないので、次のようなコマンドを用意する。 -+command! -bar -range=% - \ Reverse - \ <line1>,<line2>g/^/m<line1>-1++command! -bar -range=% + \ Reverse + \ <line1>,<line2>g/^/m<line1>-1これは望みの動作をするが、実際に実行してみると全行がハイライトされてしまう。次節で詳細を述べる。 @@ -177,13 +180,15 @@ command! -bar -range=% 前述した
-:Reverseコマンドの定義を少し変えて、次のようにする:+function! s:reverse_lines(from, to) abort - execute printf("%d,%dg/^/m%d", a:from, a:to, a:from - 1) -endfunction - -command! -bar -range=% - \ Reverse - \ call <SID>reverse_lines(<line1>, <line2>)++function! s:reverse_lines(from, to) abort + execute printf("%d,%dg/^/m%d", a:from, a:to, a:from - 1) +endfunction + +command! -bar -range=% + \ Reverse + \ call <SID>reverse_lines(<line1>, <line2>)実行しているコマンドが変わったわけではないが、関数呼び出しを経由するようにした。これだけで前述の問題が解決する。 @@ -234,9 +239,11 @@ command! -bar -range=%
command! -bar -range=%
- \ Reverse
- \ keeppatterns <line1>,<line2>g/^/m<line1>-1
+ command! -bar -range=%
+ \ Reverse
+ \ keeppatterns <line1>,<line2>g/^/m<line1>-1
+
まさにこのための Exコマンド、:keeppatterns が存在する。:keeppatterns {command} のように使い、読んで字の如く、後ろに続く Exコマンドを「現在の検索パターンを保ったまま」実行する。はるかに分かりやすく意図を表現できる。
--
cgit v1.2.3-70-g09d2