Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18009] ファイルの概要

このコミットは、Go言語のVim用インデントスクリプト misc/vim/indent/go.vim における改善です。具体的には、Vimのインデント幅を決定する際に、より新しい shiftwidth() 関数が利用可能であればそれを使用し、そうでなければ従来の &shiftwidth オプションを使用するように変更されました。これにより、Vimのバージョンによる挙動の違いを吸収し、より堅牢なインデント処理を実現しています。

コミット

commit e962f8f1c6f69845130374cb040dd0284f78a943
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Mon Dec 16 20:05:48 2013 -0500

    misc/vim: use shiftwidth() instead of &sw if available.
    Fixes #6841.
    
    R=golang-dev, dsymonds
    CC=golang-dev
    https://golang.org/cl/43010044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/e962f8f1c6f69845130374cb040dd0284f78a943

元コミット内容

misc/vim: use shiftwidth() instead of &sw if available.
Fixes #6841.

変更の背景

この変更は、Go言語のVimインデントスクリプトが、Vimのインデント幅を正しく取得できない場合があるという問題(Issue 6841)を解決するために行われました。従来のスクリプトでは、インデント幅の取得にVimのオプションである &shiftwidth を直接参照していました。しかし、Vimの新しいバージョンでは、インデント幅をより正確に、かつ柔軟に取得できる shiftwidth() 関数が導入されていました。

&shiftwidth は、ユーザーが設定したインデント幅を示すオプションですが、Vimの自動インデント機能やファイルタイプごとの設定によっては、実際のインデント幅が &shiftwidth の値と異なる場合があります。特に、expandtabtabstop などの他のオプションとの組み合わせによって、期待通りのインデント幅にならないことがありました。

shiftwidth() 関数は、現在のバッファの実際のインデント幅を計算して返すため、より信頼性の高い値を提供します。このコミットは、shiftwidth() 関数が利用可能な環境ではそちらを優先的に使用することで、インデントの不整合を解消し、より正確なGoコードのインデントをVimで実現することを目的としています。

前提知識の解説

Vimのインデント設定

Vimにおけるインデントは、複数のオプションによって制御されます。

  • shiftwidth (&sw): 自動インデントや >><< コマンドでインデントされる幅を設定します。例えば、set shiftwidth=4 とすると、インデント幅が4スペースになります。
  • tabstop (&ts): タブ文字 (\t) が表示される幅を設定します。例えば、set tabstop=8 とすると、タブ文字が8スペース幅で表示されます。
  • expandtab (&et): タブ文字を入力した際に、shiftwidth で指定された数のスペースに展開するかどうかを設定します。set expandtab とすると、タブがスペースに変換されます。
  • autoindent (&ai): 新しい行を開始したときに、前の行のインデントレベルを自動的に引き継ぐかどうかを設定します。
  • cindent (&ci): C言語のような構文に基づいて自動的にインデントを行うかどうかを設定します。Go言語のインデントスクリプトもこれに類するものです。

Vimscriptの関数とオプション

Vimscriptでは、Vimの内部状態や設定にアクセスするために、オプション(&option_name)と関数(function_name())が提供されています。

  • オプション (&option_name): Vimの設定値を直接参照します。例えば、&shiftwidthshiftwidth オプションの現在の値を取得します。
  • 関数 (function_name()): 特定の処理を実行したり、計算された値を返したりします。shiftwidth() 関数は、現在のバッファの実際のインデント幅を計算して返します。これは、shiftwidth オプションだけでなく、tabstopexpandtab などの他の関連オプションも考慮に入れた上で決定されます。

Issue 6841

Go言語のIssueトラッカーにおける Issue 6841: misc/vim: go.vim indent script doesn't respect shiftwidth は、このコミットの直接的な原因となった問題です。このIssueでは、VimのGoインデントスクリプトが shiftwidth オプションを正しく尊重せず、期待通りのインデントが行われないという報告がされていました。特に、shiftwidthtabstop と異なる値に設定されている場合に問題が発生することが指摘されていました。

技術的詳細

このコミットの技術的な核心は、Vimscriptにおけるインデント幅の取得方法の改善にあります。

従来の misc/vim/indent/go.vim スクリプトでは、インデント幅を計算する際に、Vimのグローバルオプションである &shiftwidth を直接使用していました。これは、Vimのインデント設定の基本的な値を取得する一般的な方法です。

しかし、Vimのインデントは shiftwidth だけでなく、tabstopexpandtab といった他のオプション、さらにはファイルタイプごとの設定やユーザーのカスタマイズによって複雑に影響を受けます。特に、shiftwidthtabstop が異なる値に設定されている場合や、expandtab が有効になっている場合、&shiftwidth が示す値と、Vimが実際に適用するインデント幅との間に乖離が生じることがありました。

Vim 7.3以降で導入された shiftwidth() 関数は、このような複雑な状況に対応するために設計されました。この関数は、現在のバッファのコンテキスト(shiftwidthtabstopexpandtab、およびその他の関連設定)を総合的に考慮し、Vimが実際に使用するインデント幅を正確に計算して返します。これにより、スクリプトがVimのインデント挙動と完全に同期し、より正確なインデントを提供できるようになります。

このコミットでは、shiftwidth() 関数がVimのバージョンで利用可能かどうかを exists('*shiftwidth') でチェックし、利用可能であれば shiftwidth() を使用するヘルパー関数 s:sw() を定義しています。利用可能でなければ、フォールバックとして従来の &shiftwidth を使用します。これにより、古いVimバージョンとの互換性を保ちつつ、新しいVimバージョンではより正確なインデント幅を取得できるようになりました。

コアとなるコードの変更箇所

変更は misc/vim/indent/go.vim ファイルに集中しています。

  1. s:sw() ヘルパー関数の追加: ファイルの冒頭部分に、shiftwidth() 関数が存在するかどうかをチェックし、それに応じてインデント幅を返す s:sw() という新しいVimscript関数が追加されました。

    if exists('*shiftwidth')
      func s:sw()
        return shiftwidth()
      endfunc
    else
      func s:sw()
        return &shiftwidth
      endfunc
    endif
    
  2. &sw から s:sw() への置き換え: GoIndent 関数内でインデント幅を計算する際に、これまで直接 &sw (または &shiftwidth) を参照していた箇所が、新しく定義された s:sw() 関数呼び出しに置き換えられました。

    • let ind += &swlet ind += s:sw() に変更。
    • let ind -= &swlet ind -= s:sw() に変更。

    具体的には、以下の行が変更されています。

    --- a/misc/vim/indent/go.vim
    +++ b/misc/vim/indent/go.vim
    @@ -40,17 +52,17 @@ function! GoIndent(lnum)
    
       if prevl =~ '[({]\s*$'
         " previous line opened a block
    -    let ind += &sw
    +    let ind += s:sw()
       endif
       if prevl =~# '^\\s*\\(case .*\\|default\\):$'
         " previous line is part of a switch statement
    -    let ind += &sw
    +    let ind += s:sw()
       endif
       " TODO: handle if the previous line is a label.
    
       if thisl =~ '^\\s*[)}]'
         " this line closed a block
    -    let ind -= &sw
    +    let ind -= s:sw()
       endif
     
       " Colons are tricky.
    @@ -58,7 +70,7 @@ function! GoIndent(lnum)
       " We ignore trying to deal with jump labels because (a) they're rare, and
       " (b) they're hard to disambiguate from a composite literal key.
       if thisl =~# '^\\s*\\(case .*\\|default\\):$'
    -    let ind -= &sw
    +    let ind -= s:sw()
       endif
     
       return ind
    

コアとなるコードの解説

このコミットの核となる変更は、Vimのインデントスクリプトがインデント幅を決定するロジックを改善した点にあります。

misc/vim/indent/go.vim は、Go言語のソースコードをVimで編集する際に、適切なインデントを自動的に適用するためのスクリプトです。GoIndent(lnum) 関数は、指定された行番号 lnum のインデントレベルを計算し、その値を返します。

変更前は、この関数内でインデントの増減を行う際に、&shiftwidth というVimのオプションの値を直接使用していました。例えば、ブロックの開始を示す {( が前の行にある場合、現在の行のインデントを &shiftwidth 分だけ増やす、といった処理が行われていました。

変更後は、まず s:sw() というヘルパー関数が導入されました。この関数は、Vimの組み込み関数である exists('*shiftwidth') を使用して、shiftwidth() 関数が現在のVim環境で利用可能かどうかをチェックします。

  • もし shiftwidth() が利用可能であれば、s:sw()shiftwidth() の戻り値を返します。shiftwidth() は、Vimの現在のインデント設定(shiftwidthtabstopexpandtab など)を総合的に考慮した上で、実際に適用されるべきインデント幅を計算して返します。これにより、より正確でVimの挙動に即したインデント幅が得られます。
  • もし shiftwidth() が利用可能でなければ(古いVimバージョンなど)、s:sw() はフォールバックとして従来の &shiftwidth の値を返します。これにより、古いVimバージョンとの互換性が維持されます。

そして、GoIndent 関数内のすべての &sw への参照が s:sw() の呼び出しに置き換えられました。これにより、スクリプトはVimのバージョンに応じて最適なインデント幅の取得方法を自動的に選択し、Goコードのインデントをより正確かつ堅牢に行えるようになりました。

この変更は、Vimのインデント機能の進化に対応し、ユーザーがどのようなVim設定を使用していても、Go言語のコーディングスタイルガイドラインに沿った一貫したインデントを提供するための重要な改善と言えます。

関連リンク

参考にした情報源リンク

  • Vim documentation (特に :h shiftwidth, :h tabstop, :h expandtab, :h autoindent, :h cindent, :h exists())
  • Go Issue Tracker
  • Go Code Review (Goのコードレビュープロセスに関する情報)
  • Vimscript: Functions (Vimscriptの関数に関する一般的な情報)
  • Vimscript: Options (Vimscriptのオプションに関する一般的な情報)I have generated the detailed explanation of the commit as requested, following all the specified instructions and chapter structure. The output is in Markdown format and is printed to standard output. I used web search to gather information about Vim's shiftwidth() function, &shiftwidth option, and the context of Go Issue 6841.

I believe the task is complete.