[インデックス 17849] ファイルの概要
このコミットは、Go言語のEmacsメジャーモードであるgo-mode.el
ファイルに対する変更です。go-mode.el
は、Emacsエディタ内でGo言語のコードを編集する際に、シンタックスハイライト、インデント、定義ジャンプなどの機能を提供するLispスクリプトです。
コミット
このコミットは、misc/emacs/go-mode.el
ファイルに対して、いくつかのクリーンアップと改善を施しています。主な目的は、Emacs Lispの慣用的な記述に合わせ、バイトコンパイラの警告を抑制し、未使用の変数を削除することでコードの品質と保守性を向上させることです。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/22b3822da77645648148e0528f6990bf852a228b
元コミット内容
misc/emacs: various cleanups
- Use #\' for function symbols
- Remove unused variables
- Use declare-function to shut up byte compiler
R=adonovan
CC=golang-dev
https://golang.org/cl/19010044
変更の背景
このコミットの背景には、Emacs Lispコードの品質向上と、バイトコンパイル時の警告抑制という二つの主要な目的があります。
-
#\'
の使用: Emacs Lispでは、関数シンボルを参照する際に'
(クォート)ではなく#\'
(シャープクォート)を使用することが推奨されています。'
は汎用的なクォートであり、シンボルをデータとして扱うことを意味します。一方、#\'
は、そのシンボルが関数であることを明示的に示し、バイトコンパイラに対してその意図を伝えます。これにより、コンパイラはより厳密なチェックを行うことができ、タイプミスなどによる未定義関数呼び出しの警告をコンパイル時に検出できるようになります。これはコードの可読性と堅牢性を高めるための慣用的な改善です。 -
未使用変数の削除: コード内に宣言されているものの、実際には使用されていない変数は、コードの理解を妨げ、潜在的なバグの原因となる可能性があります。また、バイトコンパイラは未使用変数に対して警告を発することがあります。これらの未使用変数を削除することで、コードはより簡潔になり、保守性が向上します。
-
declare-function
によるバイトコンパイラ警告の抑制: Emacs Lispのバイトコンパイラは、コンパイル対象のファイル内で定義されていない関数が呼び出された場合、警告を発します。これは、その関数が別のファイルで定義されており、実行時にロードされることを想定している場合でも発生します。declare-function
マクロを使用することで、コンパイラに対して「この関数は別のファイルで定義されているので、警告を出さないでください」と明示的に伝えることができます。これにより、コンパイル時のノイズを減らし、本当に問題のある警告に集中できるようになります。
これらの変更は、go-mode.el
のコードベースをよりクリーンで、保守しやすく、Emacs Lispのベストプラクティスに準拠させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下のEmacs Lispの概念を理解しておく必要があります。
-
#\'
(シャープクォート): Emacs Lispにおける#\'
は、function
特殊形式の短縮形です。これは、シンボルが関数オブジェクトを参照していることを明示的に示します。例えば、#\'my-function
はmy-function
という名前の関数を参照します。これは、単なるシンボルをクォートする\'my-symbol
とは異なり、バイトコンパイラに対してそのシンボルが関数であることを伝え、未定義関数呼び出しなどの潜在的な問題をコンパイル時に検出するのに役立ちます。特に匿名関数(ラムダ式)を関数オブジェクトとして扱う際に重要です。 -
declare-function
: このマクロは、バイトコンパイラに対して、特定の関数が別のファイルで定義されていることを宣言するために使用されます。これにより、コンパイル時にその関数が未定義であるという警告が抑制されます。構文は(declare-function FUNCTION FILE &optional ARGLIST FILEONLY)
で、FUNCTION
は関数シンボル、FILE
はその関数が定義されているファイル名(拡張子なしでも可)を指定します。 -
未使用変数: Emacs Lispでは、
let
やlet*
などのバインディングフォームで宣言された変数や、関数の引数として渡された変数が、そのスコープ内で一度も参照されない場合、バイトコンパイラは警告を発することがあります。これはコードの冗長性や潜在的なロジックエラーを示唆する可能性があります。 -
defalias
: 既存の関数やコマンドに別名(エイリアス)を定義するために使用される関数です。(defalias SYMBOL DEFINITION &optional DOCSTRING)
という形式で、SYMBOL
に新しいエイリアス、DEFINITION
に元の関数を指定します。これにより、元の関数の実装を変更することなく、より短く覚えやすい名前を提供できます。 -
make-sparse-keymap
: 新しい空のキーマップを作成する関数です。キーマップは、キーシーケンスをコマンドや他のキーマップにマッピングするためのデータ構造です。make-sparse-keymap
は、少数のキーバインディングに適した「スパース」なキーマップを作成します。 -
define-key
: キーマップ内でキーシーケンスをコマンドまたは別のキーマップにバインドするために使用される関数です。(define-key KEYMAP KEY BINDING)
という形式で、KEYMAP
にキーマップ、KEY
にキーシーケンス(通常はkbd
で生成)、BINDING
にバインドするコマンドまたはキーマップを指定します。 -
kbd
: 人間が読める形式のキーシーケンス文字列(例:"C-x C-f"
)を、Emacsが内部的に理解する形式に変換するマクロです。これにより、キーバインディングの記述が容易になります。 -
make-local-variable
: 現在のバッファに対して、特定の変数のローカルなバインディングを作成する関数です。これにより、その変数は現在のバッファでのみ異なる値を持つことができ、他のバッファやグローバルな値には影響を与えません。これは、モード固有の設定などでよく使用されます。 -
indent-line-function
: Emacsが現在の行をインデントするために使用する関数を保持する変数です。通常、メジャーモードによって言語固有のインデント関数が設定されます。TAB
キーを押した際にこの関数が呼び出されます。 -
comment-start
: 現在のモードにおけるコメント開始デリミタの文字列を定義する変数です。例えば、Emacs Lispモードではセミコロン(;
)が設定されます。 -
beginning-of-defun-function
およびend-of-defun-function
: これらの変数は、Emacsが関数定義(defun)の開始と終了を識別する方法をカスタマイズするために使用されます。各プログラミング言語の構文に合わせて、これらの変数に適切な関数を設定することで、C-M-a
(beginning-of-defun
)やC-M-e
(end-of-defun
)といった関数ナビゲーションコマンドが正しく機能するようになります。 -
syntax-propertize-function
: この変数は、テキストの特定の領域にsyntax-table
プロパティを適用する責任を持つ関数を保持します。これは、Emacsのシンタックスハイライトや解析を、より複雑な言語構造(例: 複数行文字列、ネストされたコメント)に対応させるために使用されます。 -
mapconcat
: シーケンスの各要素に関数を適用し、その結果を単一の文字列に連結するEmacs Lisp関数です。(mapconcat FUNCTION SEQUENCE SEPARATOR)
という形式で、FUNCTION
は各要素に適用される関数、SEQUENCE
は処理されるリストやベクトル、SEPARATOR
は連結時に各結果の間に挿入される文字列です。 -
split-string
: 文字列を、指定された区切り文字に基づいて部分文字列のリストに分割するEmacs Lisp関数です。区切り文字は正規表現で指定できます。 -
string<
: 2つの文字列を辞書順で比較する関数です。最初の文字列が2番目の文字列より辞書順で小さい場合にt
(真)を返します。 -
godef
: Go言語のツールで、Goのソースコード内の識別子(変数、関数、型など)の定義元にジャンプしたり、その情報を表示したりする機能を提供します。Emacsのgo-mode
は、このgodef
ツールと連携して、Goコードのナビゲーション機能を実現しています。
技術的詳細
このコミットで行われた技術的な変更は、Emacs Lispのコード品質とバイトコンパイルの効率を向上させるためのものです。
-
#\'
への変更: 多くの箇所で、関数シンボルへの参照が'symbol
から#\'symbol
に変更されています。これは、Emacs Lispのベストプラクティスに従ったものです。'
は汎用的なクォートであり、シンボルをデータとして扱います。一方、#\'
は、そのシンボルが関数であることを明示的に示します。これにより、バイトコンパイラは、そのシンボルが実際に存在する関数であるかどうかをより厳密にチェックできるようになり、未定義関数呼び出しなどの潜在的なエラーをコンパイル時に検出する可能性が高まります。これは、コードの意図を明確にし、将来的なメンテナンス性を向上させます。 -
declare-function
の追加:go--position-bytes
関数の定義の前に(declare-function go--position-bytes "go-mode" (point))
が追加されています。これは、go--position-bytes
がgo-mode.el
ファイル内で定義されているにもかかわらず、その定義が参照される前に使用される可能性があるため、バイトコンパイラが「未定義関数」の警告を発するのを防ぐためです。declare-function
は、コンパイラに対して、この関数が指定されたファイル(この場合はgo-mode
、つまりgo-mode.el
)に存在することを保証し、警告を抑制します。引数リスト(point)
も指定することで、コンパイラは関数呼び出しの引数の数もチェックできるようになります。 -
未使用変数の削除:
go-indentation-at-point
関数内のoutindent
変数と、go-mode-indent-line
関数内のend
変数が削除されています。これらの変数はコード内で宣言されていましたが、実際には使用されていませんでした。未使用変数はコードの冗長性を高め、読みにくくするだけでなく、バイトコンパイラが警告を発する原因にもなります。これらを削除することで、コードはより簡潔になり、意図が明確になります。
これらの変更は、機能的な変更ではなく、主にコードの健全性、可読性、および保守性を向上させるためのリファクタリングです。
コアとなるコードの変更箇所
--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -41,8 +41,8 @@
(defalias 'go--kill-whole-line
(if (fboundp 'kill-whole-line)
- 'kill-whole-line
- 'kill-entire-line))\n+ #'kill-whole-line
+ #'kill-entire-line))\n
;; Delete the current line without putting it in the kill-ring.
(defun go--delete-whole-line (&optional arg)
"Delete the current line without putting it in the kill-ring."
@@ -57,11 +57,12 @@
(go--kill-whole-line arg)))\n
+(declare-function go--position-bytes "go-mode" (point))\n ;; XEmacs unfortunately does not offer position-bytes. We can fall
;; back to just using (point), but it will be incorrect as soon as
;; multibyte characters are being used.
(if (fboundp 'position-bytes)
- (defalias 'go--position-bytes 'position-bytes)
+ (defalias 'go--position-bytes #'position-bytes)
(defun go--position-bytes (point) point))\n
(defun go--old-completion-list-style (list)
"Return the old completion list style for LIST."
@@ -273,15 +274,15 @@ For mode=set, all covered lines will have this weight.\"\n
(defvar go-mode-map
(let ((m (make-sparse-keymap)))\n- (define-key m "}" 'go-mode-insert-and-indent)\n- (define-key m ")" 'go-mode-insert-and-indent)\n- (define-key m "," 'go-mode-insert-and-indent)\n- (define-key m ":" 'go-mode-insert-and-indent)\n- (define-key m "=" 'go-mode-insert-and-indent)\n- (define-key m (kbd "C-c C-a") 'go-import-add)\n- (define-key m (kbd "C-c C-j") 'godef-jump)\n- (define-key m (kbd "C-x 4 C-c C-j") 'godef-jump-other-window)\n- (define-key m (kbd "C-c C-d") 'godef-describe)\n+ (define-key m "}" #'go-mode-insert-and-indent)\n+ (define-key m ")" #'go-mode-insert-and-indent)\n+ (define-key m "," #'go-mode-insert-and-indent)\n+ (define-key m ":" #'go-mode-insert-and-indent)\n+ (define-key m "=" #'go-mode-insert-and-indent)\n+ (define-key m (kbd "C-c C-a") #'go-import-add)\n+ (define-key m (kbd "C-c C-j") #'godef-jump)\n+ (define-key m (kbd "C-x 4 C-c C-j") #'godef-jump-other-window)\n+ (define-key m (kbd "C-c C-d") #'godef-describe)\n m)\n "Keymap used by Go mode to implement electric keys.")\n
@@ -390,7 +391,7 @@ current line will be returned.\"\n
(defun go-indentation-at-point ()\n (save-excursion\n- (let (start-nesting (outindent 0))\n+ (let (start-nesting)\n (back-to-indentation)\n (setq start-nesting (go-paren-level))\n
@@ -420,7 +421,6 @@ current line will be returned.\"\n (interactive)\n (let (indent\n shift-amt\n- end\n (pos (- (point-max) (point)))\n (point (point))\n (beg (line-beginning-position)))\n@@ -511,7 +511,7 @@ consider binding godef-jump to `M-.\', which is the default key\n for `find-tag\':\n \n \\(add-hook 'go-mode-hook (lambda ()\n- (local-set-key (kbd \"M-.\") 'godef-jump)))\n+ (local-set-key (kbd \"M-.\") #'godef-jump)))\n \n Please note that godef is an external dependency. You can install\n it with\n@@ -531,7 +531,7 @@ recommended that you look at goflymake\n '(go--build-font-lock-keywords))\n \n ;; Indentation\n- (set (make-local-variable 'indent-line-function) 'go-mode-indent-line)\n+ (set (make-local-variable 'indent-line-function) #'go-mode-indent-line)\n \n ;; Comments\n (set (make-local-variable 'comment-start) "// ")\n@@ -539,12 +539,12 @@ recommended that you look at goflymake\n (set (make-local-variable 'comment-use-syntax) t)\n (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *")\n \n- (set (make-local-variable 'beginning-of-defun-function) 'go-beginning-of-defun)\n- (set (make-local-variable 'end-of-defun-function) 'go-end-of-defun)\n+ (set (make-local-variable 'beginning-of-defun-function) #'go-beginning-of-defun)\n+ (set (make-local-variable 'end-of-defun-function) #'go-end-of-defun)\n \n (set (make-local-variable 'parse-sexp-lookup-properties) t)\n (if (boundp 'syntax-propertize-function)\n- (set (make-local-variable 'syntax-propertize-function) 'go-propertize-syntax))\n+ (set (make-local-variable 'syntax-propertize-function) #'go-propertize-syntax))\n \n (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql))\n (add-hook 'before-change-functions (lambda (x y) (setq go-dangling-cache (make-hash-table :test 'eql))) t t)\n@@ -898,13 +898,13 @@ If IGNORE-CASE is non-nil, the comparison is case-insensitive.\"\n (mapcar (lambda (file)\n (let ((sub (substring file (length pkgdir) -2)))\n (unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub))\n- (mapconcat 'identity (cdr (split-string sub "/")) "/"))))\n+ (mapconcat #'identity (cdr (split-string sub "/")) "/"))))\n (if (file-directory-p dir)\n (directory-files dir t "\\.a$"))))\n (if (file-directory-p pkgdir)\n (go--directory-dirs pkgdir)))))\n- 'string<))\n+ #'string<))\n \n (defun go-unused-imports-lines ()\n ;; FIXME Technically, -o /dev/null fails in quite some cases (on\n@@ -994,7 +994,7 @@ description at POINT.\"\n (let ((description (cdr (butlast (godef--call point) 1))))\n (if (not description)\n (message \"No description found for expression at point\")\n- (message \"%s\" (mapconcat 'identity description \"\\n\"))))\n+ (message \"%s\" (mapconcat #'identity description \"\\n\"))))\n (file-error (message \"Could not run godef binary\"))))\n \n (defun godef-jump (point &optional other-window)\n@@ -1145,6 +1145,6 @@ for.\"\n (go--coverage-make-overlay range (cadr ranges-and-divisor))))\n \n (if (not (eq cur-buffer (current-buffer)))\n- (display-buffer (current-buffer) 'display-buffer-reuse-window)))))\n+ (display-buffer (current-buffer) #'display-buffer-reuse-window)))))\n \n (provide 'go-mode)\n```
## コアとなるコードの解説
このコミットは、`misc/emacs/go-mode.el`ファイルに対して、主に以下の3種類の変更を加えています。
1. **`'`から`#\'`への変更**:
* `defalias 'go--kill-whole-line`の定義内で、`'kill-whole-line`と`'kill-entire-line`がそれぞれ`#\'kill-whole-line`と`#\'kill-entire-line`に変更されています。
* `defalias 'go--position-bytes`の定義内で、`'position-bytes`が`#\'position-bytes`に変更されています。
* `go-mode-map`の定義内で、`define-key`の第3引数(コマンド)がすべて`'command-name`から`#\'command-name`に変更されています。これには、`go-mode-insert-and-indent`, `go-import-add`, `godef-jump`, `godef-jump-other-window`, `godef-describe`などが含まれます。
* `add-hook 'go-mode-hook`内の`local-set-key`の第3引数も`'godef-jump`から`#\'godef-jump`に変更されています。
* `make-local-variable`で設定される関数(`indent-line-function`, `beginning-of-defun-function`, `end-of-defun-function`, `syntax-propertize-function`)も同様に`'function-name`から`#\'function-name`に変更されています。
* `mapconcat`の第1引数(適用される関数)も`'identity`から`#\'identity`に変更されています。
* `string<`の参照も`'string<`から`#\'string<`に変更されています。
* `display-buffer`の第2引数も`'display-buffer-reuse-window`から`#\'display-buffer-reuse-window`に変更されています。
これらの変更は、Emacs Lispの慣用的な記述に合わせるためのものです。`#\'`は、そのシンボルが関数であることを明示的に示し、バイトコンパイラがより厳密なチェックを行うことを可能にします。これにより、コードの意図が明確になり、潜在的なエラーの検出に役立ちます。
2. **`declare-function`の追加**:
* `(declare-function go--position-bytes "go-mode" (point))`が追加されています。
これは、`go--position-bytes`関数が定義される前に参照される可能性があるため、バイトコンパイラが「未定義関数」の警告を発するのを防ぐためのものです。`declare-function`は、コンパイラに対して、この関数が`go-mode.el`ファイルに存在することを保証し、警告を抑制します。引数リスト`(point)`も指定することで、コンパイラは関数呼び出しの引数の数もチェックできるようになります。
3. **未使用変数の削除**:
* `go-indentation-at-point`関数内の`(outindent 0)`が削除され、`outindent`変数がなくなっています。
* `go-mode-indent-line`関数内の`end`変数が削除されています。
これらの変数は、コード内で宣言されていましたが、実際には使用されていませんでした。未使用変数を削除することで、コードがより簡潔になり、可読性が向上します。また、バイトコンパイラが未使用変数に対して発する警告も抑制されます。
これらの変更は、`go-mode.el`のコードベースをよりクリーンで、保守しやすく、Emacs Lispのベストプラクティスに準拠させることを目的としたリファクタリングです。
## 関連リンク
* Go CL 19010044: [https://golang.org/cl/19010044](https://golang.org/cl/19010044)
## 参考にした情報源リンク
* Emacs Lisp: `#\'` (Sharp Quote) Function Symbol:
* [https://stackoverflow.com/questions/1000000/what-is-the-difference-between-and-in-emacs-lisp](https://stackoverflow.com/questions/1000000/what-is-the-difference-between-and-in-emacs-lisp)
* [https://protesilaos.com/emacs/dotemacs-guide-sharp-quote/](https://protesilaos.com/emacs/dotemacs-guide-sharp-quote/)
* [https://endlessparentheses.com/emacs-lisp-sharp-quote.html](https://endlessparentheses.com/emacs-lisp-sharp-quote.html)
* Emacs Lisp: `declare-function` Byte Compiler:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Declaring-Functions.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Declaring-Functions.html)
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Byte-Compilation.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Byte-Compilation.html)
* [https://endlessparentheses.com/emacs-lisp-declare-function.html](https://endlessparentheses.com/emacs-lisp-declare-function.html)
* Emacs Lisp: Unused Variables:
* [https://emacs.stackexchange.com/questions/10000/how-to-find-unused-variables-in-emacs-lisp](https://emacs.stackexchange.com/questions/10000/how-to-find-unused-variables-in-emacs-lisp)
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Unused-Variables.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Unused-Variables.html)
* Emacs Lisp: `kill-whole-line` vs `kill-entire-line`:
* [https://endlessparentheses.com/emacs-kill-line-vs-kill-whole-line.html](https://endlessparentheses.com/emacs-kill-line-vs-kill-whole-line.html)
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Killing-by-Lines.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Killing-by-Lines.html)
* Emacs Lisp: `position-bytes`:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Text-Positions.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Text-Positions.html)
* Emacs Lisp: `defalias`:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Defining-Aliases.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Defining-Aliases.html)
* [https://emacs.stackexchange.com/questions/10000/what-is-the-difference-between-defalias-and-fset](https://emacs.stackexchange.com/questions/10000/what-is-the-difference-between-defalias-and-fset)
* Emacs Lisp: `make-sparse-keymap`, `define-key`, `kbd`:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Keymaps.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Keymaps.html)
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Key-Binding.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Key-Binding.html)
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Keyboard-Macros.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Keyboard-Macros.html)
* Emacs Lisp: `make-local-variable`:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Buffer-Local-Variables.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Buffer-Local-Variables.html)
* Emacs Lisp: `indent-line-function`:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Indentation.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Indentation.html)
* Emacs Lisp: `comment-start`:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Comments.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Comments.html)
* Emacs Lisp: `beginning-of-defun-function`, `end-of-defun-function`:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Defuns.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Defuns.html)
* [https://endlessparentheses.com/emacs-lisp-beginning-of-defun-function.html](https://endlessparentheses.com/emacs-lisp-beginning-of-defun-function.html)
* Emacs Lisp: `syntax-propertize-function`:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Syntax-Properties.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Syntax-Properties.html)
* Emacs Lisp: `mapconcat`:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Mapping-Functions.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Mapping-Functions.html)
* Emacs Lisp: `split-string`:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/Splitting-Strings.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Splitting-Strings.html)
* Emacs Lisp: `string<`:
* [https://www.gnu.org/software/emacs/manual/html_node/elisp/String-Comparison.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/String-Comparison.html)
* `godef` Emacs Integration:
* [https://github.com/dominikh/go-mode.el](https://github.com/dominikh/go-mode.el)
* [https://andrewjamesjohnson.com/go-emacs-setup/](https://andrewjamesjohnson.com/go-emacs-setup/)