Last modified: Fri Apr 21 00:43:15 JST 2023

善き魔術師への道(大人編)

善き魔術師への道(少年編), 善き魔術師への道(青年編)に 続き大人編です. 特にテーマありません. 久々に使った機能や新たに知った機能を つらつらと書いていきたいと思います. (少しづつ書き足して行きます)

A. お久しぶり, 懐かしい機能

1. シェル(shell-mode)

UNIX + X Window を使っていた頃は, kterm や xterm などの端末エミュレータが 常に起動されており, そこでシェルが走っている状態だったので Emacs 上の シェルモードはあまり活躍していなかった. 「画面出力を編集したい」など 特殊な要求がある時に, たまに起動する程度でした.

最近, Windows 上で C 言語のテストプログラムを組み, コンパイル & RUN する仕事が発生し, Cygwin 環境を導入しました.

インストール先は C:\cygwin64 とし, 環境変数 Path に C:\cygwin64\usr\local\bin;C:\cygwin64\usr\bin;C:\cygwin64\bin; を付け加えました.

Cygwin terminal を立ち上げれば bash が使えるんですが, ちょっとしたコンパイルの際にいちいち terminal を立ち上げるのが面倒です. デフォルトで Cygwin terminal を立ち上げておくと言う方法もありますが, Windows 環境ではプログラミング以外の作業で terminal はほとんど使わない. 多くの場合邪魔. プログラムを書いている Emacs 上で作業するのが一番便利かな... と言うことで Emacs 上でのシェルモード復活です.

特に何も設定していない状態で M-x shell を実行すると, DOS プロンプトが 立ち上がります. これを bash に入れ替えるだけ. Meadow のときと同じ設定です.

;;====================
;; シェル(shell-mode)
;;====================
;; Cygwin の bash を使う.
(setq explicit-shell-file-name "bash")
;(setq shell-file-name "bash")
(setq shell-command-switch "-c") 

で, 立ち上げて見るとこんな画面. あれれ格好悪いぞ.

bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
^[]0;~^G
uranov@Devil ~
$

まずは最初の 2 行のメッセージ. bash からの情報のようです. terminal process group 云々の問題? このメッセージが出ないようにしようと あれこれ格闘してみましたが消えません. 調べてみると今の所どうしようもないらしい. 古い cygwin の bash を使うなどの 変則的な手段はあっても根本的な解決は cygwin 側の対応を待つしかなさそう. 根性があれば bash のソースコードを入手して, メッセージが出ないようにすることもできそうですが... とりあえずここはエラーでなく警告程度に考えて放置します.

3 行目の変な制御文字は, 上記問題を調べている時に解決してしまいました. 原因はデフォルトのプロンプト設定です. 具体的には以下のようになっていました.

$ echo $PS1
\[\e]0;\w\a\]\n\[\e[32m\]\u@\h \[\e[33m\]\w\[\e[0m\]\n\$

出だしの '\[\e]0;\w\a\]' はターミナルのタイトル変更指示です. 例えば Cygwin64 Terminal 上の bash で

$ PS1='\$ '               ← まずは現在のプロンプト設定をシンプルに.
$ echo -e '\e]0;uranov\a' ← タイトルを uranov とする.

とすればターミナルのタイトルバーの表示が変わります. デフォルトでは '\w' が 指定されているので, タイトルに現在のパスが表示されます. Emacs の中の shell-mode で bash を動かしているときは(ターミナルではないので) この指示が伝わらずに変な制御文字が表示されてしまいます.

対応方法は色々ありますが, 私は Emacs でのシェルに限り, タイトルへのパス表示をやめる様にしました. 同様に Windows 主体の動作では 1 PC 1 ユーザなのでホスト名やユーザ名も不要です. 具体的には以下のような .bashrc ファイルを作成し, ホームディレクトリに置きました. ついでにログインシェルとしての立ち上げ時に読み込まれる共通設定を ~/.bash_local として分離し, ~/.bashrc, ~/bash_profile 共にこれをソースする(= 読み込む)ようにしました.

実際には Emacs の shell-mode だけでなく, Cygwin Term のシェル(これも bash)や Windows のコマンドプロンプト(cmd.exe)から bash を起動した場合も プロンプトが変わります.

[~/.bashrc]

#!/bin/bash -*- coding: utf-8-unix -*-
# last modified: Sat Nov 29 19:53:16 JST 2020
##=================
## System Settings (Will be done in /etc/bash.bashrc)
##=================
#echo "reading ~/.bashrc"
##-----------------------
## Environment Variables
##-----------------------
##export LANG=ja_JP.SJIS
##export OUTPUT_CHARSET=sjis
##export EDITOR=meadow
#export LANG=ja_JP.UTF-8
#export PS1='\[\e]0;\w\a\]\n\[\e[32m\]\u@\h \[\e[33m\]\w\[\e[0m\]\n\$ '
#export PS1='\n\[\e[35m\]\w\[\e[0m\]\n\$ '
export PS1='\[\e[32m\][\w]\[\e[0m\]\$ '
#export PS1='[\w]\$ '
export PAGER=less
export EDITOR=emacs
##---------
## Aliases
##---------
#alias ls='ls --show-control-chars'
alias ls='ls -aF --color=auto --show-control-chars'
alias gcc='gcc -O3 --input-charset=cp932 --exec-charset=cp932'

#================
# Local Settings
#================
# read local settings
source ~/.bash_local

# end
[~/.bash_profile]

#!/bin/bash -*- coding: utf-8-unix -*-
# last modified: Sat Nov 29 19:53:16 JST 2020
##=================
## System Settings (Will be done in /etc/bash.bashrc)
##=================
#echo "reading ~/.bash_profile"
##-----------------------
## Environment Variables
##-----------------------
#export LANG=ja_JP.UTF-8
export PAGER=less
export EDITOR=emacs
##---------
## Aliases
##---------
#alias ls='ls --show-control-chars'
alias ls='ls -aF --color=auto --show-control-chars'
alias gcc='gcc -O3 --input-charset=cp932 --exec-charset=cp932'

#================
# Local Settings
#================
# read local settings
source ~/.bash_local

# end
[~/.bash_local]

#!/bin/bash -*- coding: utf-8-unix -*-
# last modified: Sat Nov 29 19:52:04 JST 2020
#================
# Local Settings
#================
#echo "reading ~/.bash_local"
##-----------------------
## Environment Variables
##-----------------------
##---------
## Aliases
##---------
alias cc=gcc
alias l=less
alias ll='ls -l'
alias lt='ls -ltr'
alias g=grep
alias od='od -t x1 -A x'
alias pu=pushd
alias po=popd

# end

私の記述では ~/.bashrc と ~/.bash_profile にまだ共通記述が残っています. 共通記述は全てまとめて ~/.bash_common とでもするのもありだと思います.

bash の設定ファイルの読み込み順序については改めて調べてみました. Emacs の shell mode は指定されたシェルを立ち上げます. 指定シェルの検索順は (1) explicit-shell-file-name (2) 環境変数 ESHELL (3) shell-command 検索順 で (3) の shell-command 検索順は (3-1) 環境変数 SHELL (3-2) shell-file-name となっています.

これで「シェルを通常通り立ち上げた」後, 設定ファイルを 1 つ読み込みます. その設定ファイルの検索順は(例えば指定シェルが bash の場合) (1) ~/.emacs_bash (2) ~/.emacs.d/init_bash.sh が読み込まれます. (どちらもなければ Emacs 用の設定は読み込まれません.)

上述の「シェルの通常通りの立ち上げ」では bash の場合, (A) ~/.bashrc が読み込まれます. 但し ログインシェルに限りその代わりに (B) ~/.bash_profile が読み込まれます.

Emacs の shell-mode での立ち上げの場合の扱いは, ログインシェルではなく 通常シェルで, ~/.bashrc が読み込まれます. このため私はサンプルのように ~/.bashrc の中でシンプルなプロンプトを 設定しています.

今回の設定で, プロンプトを変えるとカーソル位置がプロンプトの位置(直後) ではなく, 行頭に来てしまうと言うおかしな状態に悩まされました. 同じ Cygwin を利用していても PC によって正常のもの, 異常なものに分かれます. 原因は単純で改行コードの問題でした. Windows や Mail, HTML 系の改行コード \r\n は \r (行頭に戻る)部分もプロンプト記述と解釈されてしまっていました. もちろん UNIX 系の \n なら大丈夫です. 皆さん, ご注意を.

この事があってから .bashrc などの行頭の coding 宣言を utf-8 から utf-8-unix に変更しました. Emacs でしか有効になりませんが, 自分に対する注意の意味も込めています.

2. c-mode

先にも書いた通り, 最近 Windows 上で C 言語のプログラムを組んでおり, c-mode をよく使っています. デフォルトの設定で大きな問題はないのですが, 今の所 2 点だけ設定しています.

1 つ目は他の開発環境にソースコードを持っていった時に書式が崩れないように TAB でなくスペースを使う事. 2 つ目は Migemo を OFF にすることです.

;;================
;; C, C++(c-mode)
;;================
(add-hook 'c-mode-common-hook ; 言語共通(c/c++/objc/java/idl/pike/awk)
          '(lambda()
             (setq indent-tabs-mode nil)
             (setq migemo-isearch-enable-p nil)
             (hide-ifdef-mode t)))

後, 自分では見通しの悪い長い行数の関数を書かないようにしていますが, リファレンスとなるプログラムには, 可読性の悪いものがあります. あまりにも長いとカッコの対応がインデントだけでは分からないことが... 「このループ, どこで終わってるんだ?」「この条件分岐, どこまで続いている?」 このため, 今まで使ってなかったジャンプ機能をよく使うようになりました.

更に #if や #ifdef などのプリプロセッサを多用したリファレンスに出会いました. ここで活躍したのがマイナーモードの hide-ifdef-mode. プリプロセッサ構文はインデントがうまく働かないため, 入れ子になっていたりすると可読性が極端に悪くなります. ブロック内を一気に隠してくれる hide (C-c @ h), 再表示する show(C-c @ s)が活躍しました. 結局, 変なソースを読むときにしか使いませんが, c-mode に hook しました.

バインド動作
M-a式の先頭へ.
M-e式の末尾へ.
C-M-a関数の先頭へ.
C-M-e関数の末尾へ.
C-c C-pプリプロセッサ条件文の外から(前の)条件文先頭へ.
C-c C-nプリプロセッサ条件文の外から(後の)条件文末尾へ.
C-c C-uプリプロセッサ条件文の中から(その)条件文先頭へ.
C-c @ hプリプロセッサブロックを隠す. (hide-ifdef-mode)
C-c @ sプリプロセッサブロックを再表示. (hide-ifdef-mode)

B. こんな機能もありました

1. 矩形領域に連番を入れる

題名通り「矩形領域に連番を入れる」機能です. 私は所持している CD を整理する際に以下のようなデータを残しているのですが, この曲目リストに連番を入れています. この連番挿入作業が結構頻繁に発生し, なおかつ面倒... Emacs なら良いマクロがあるんじゃないと探したら, あっさり見つかりました. M-x rectangle-number-lines (= C-x r N) です. 便利.

last modified: Wed Feb 18 23:18:22 JST 2020

アルバム名:     Believe Me  ビリーヴ・ミー (1982, ETP-60452)
アーティスト名: DAIZO & ELEPHANTS  小柴大造&エレファント
曲目リスト:
        1.      Tokyo Night
        2.      (さしずめ)三文オペラ
        3.      別離のラブソング
        4.      デッド・ゾーン・ジャンクション  DEAD ZONE JUNCTION
        5.      リバイバル・ソング  REVIVAL SONG
        6.      テル・ミー  TELL ME
        7.      フリーウェイ  FREEWAY
        8.      ALONE TONIGHT
コメント:
        Source: CD (MEG-CD)

2. markdown-mode

GitHub を使ったプロジェクト運用を始めてから, markdown ファイル(*.md)の 読み書きの機会が増えてきた. Atom や Visual Studio Code を用いて作業することも できるが, そのためだけにもう一つエディタを立ち上げるのは面倒.

と言うことで, markdown-mode を試してみた. 以下覚書.

以下にあれこれ書いたが, 結局 Chrome の拡張機能の Markdown Viewer を導入し 利用している. これで直接 Chrome で表示できるため, Pandoc でのコンパイル, コンパイル後のプレビューは不要になった.

a. Markdown Mode のインストール

markdown-mode は標準では用意されておらず, パッケージのインストールが必要. (パッケージのインストールについては Mozc パッケージのインストール を参照ください.) インストール自体はそれで OK で, これだけで *.md ファイルを読み込むと 自動的に markdown モードになります.

この後プレビューのための設定などが必要なのですが, 設定の前に是非本家の文書 OUT14: Markdown Mode for Emacs を読んでおきましょう.

b. Pandoc 設定

基本的に markdown 文書を読む際は, 一旦 html 形式に変換(コンパイル)し, それをブラウザで表示するという操作が必要です. この変換のために何らかの外部プログラムを利用することになります.

上述の本家のページにはこの Markdown processor の例として Markdown.pl, MultiMarkdown, Pandoc が挙げられています. 私の場合は Dokuwiki 文書の変換 のために既に Pandoc がインストールされているのでこれ一択.

Dokuwiki の変換の際もそうでしたが, 自分の好みの体裁に変換したい! このためにやはりテンプレートを準備することに. スタイルシートは GitHub 風(?)のもの OUT15: github-markdown-css を見つけました. ここにある css ファイルを Pandoc のテンプレートディレクトリ (<ユーザーディレクトリ>/AppData/Roaming/pandoc/templates/)に置きました.

このスタイルシートを読み込むだけではうまく行かなかったので, (Dokuwiki と同様に)テンプレートを用意し github.html と名付けてテンプレートディレクトリに置きました. (やはり Dokuwiki と同様にデフォルトのテンプレートを加工したものです.)

Pandoc の設定が終わったら, 適当な md ファイルを変換してテストし, 求めるブラウザ表示が得られれば OK.

  $ pandoc -s -M title:Markdown --template=github.html test.md -o test.html

pandoc に path が通っていなければ C:/Users/<ユーザ名>/Appdata/Local/Pandoc/pandoc のようにフルパス指定ください. ここでは title 文字列は Markdown としていますが何でもいいです. title 指定が無いと警告が出力され, それが html に含まれてしまうので とりあえずこうして逃げています.

c. Emacs 設定

後は Emacs から Pandoc を呼び出せるように markdown-command を設定します. 文書の Customization の項にもありますが私は以下のように設定しています.

(setq markdown-command "pandoc -s -M title:Markdown --template=github.html")

d. (Emacs から)使ってみる

早速使ってみます. Emacs に md ファイルを読み込ませ, C-c C-c e で html への変換です. C-c C-c と押した段階で, 外部コマンド(ここでは Pandoc)を利用する際の キーバインドが表示されます. e は export (外部出力)で, ちょっと待つと「Wrote *.html」とメッセージが出ます. md ファイルと同じディレクトリに html ファイルができています. このファイルをブラウザで開けば OK.

C-c C-c v (export & preview) でファイルの作成から ブラウザでの表示までを行うことも可能です.

e. リアルタイムプレビューへ

頻繁に md ファイルのエディットと表示確認を行う場合, いちいち上記のような操作をするのは面倒です. このため, リアルタイムプレビューをサポートした markdown-preview-mode と言うパッケージもある. (今のところインストールしていない.) 試したらレポートします.

3. テンプレート挿入

自動テンプレート挿入機能. 今まで何故か使っていませんでした. ファイルタイプに合わせて用意したテンプレートを自動的に挿入してくれます. 最近はテキストファイル作成の際の日本語コーディングは UTF-8 一択です. 毎回ファイル先頭にコーディング宣言を入れるのが面倒なので, 調べたらやはりありましたテンプレート挿入機能.

詳細は Emacs Wiki の OUT16: Auto Insert Mode が詳しいです.

私の場合, テキストファイル用に以下のテンプレートを用意し, これを決まったディレクトリに置きました.

[template.txt]
# -*- coding: utf-8-unix -*-
# last modified: 

title
~~~~~

# EOF

後は init.el にこれを読み込ませる設定を記述するだけ. テキストファイルを新規作成すると, "Perform .txt auto-insertion? (y or n)" と問い合わせが発生し, 'y' と答えればテンプレートが挿入されます. skeleton を使えばより複雑な挿入も可能です.

;;------------------
;; テンプレート挿入
;;------------------
;; https://www.emacswiki.org/emacs/AutoInsertMode
(auto-insert-mode)  ; Adds hook to find-files-hook
(setq auto-insert-directory "~/.emacs.d/autoinsert/")
;(setq auto-insert-query nil) ; query off
(define-auto-insert "\.txt" "template.txt")

4. モードラインの変更

仕事で BOM 付きの UTF-8 エンコーディングファイルを使う必要がありました. Visual Studio (以下 VS) のコンパイラを使うためです.

VS は過去のファイルの扱いと互換のためか, 内部では Shift-JIS エンコーディングを使っています. このため UTF-8 などエンコーディングが異なるファイルは, (内部で)一旦 S-JIS に変換して処理している様ですが, BOM なしの UTF-8 ファイルを読み込ませると警告が出ます. 単なる警告で実害はないのですが煩わしいので, VS に読み込ませる UTF-8 は BOM 付きにしています.

でも Emacs からこの UTF-8 は BOM 付きかどうか が分かりづらいなと思っていました. そこでネットの情報を基に, モードラインに BOM 情報を追加しました.

;; モードラインに BOM 情報追加
;; (http://blog.livedoor.jp/tek_nishi/archives/9657406.html)
(defun my-mode-line-bom-info ()
  "if with-signature text -> output [BOM]"
  (let ((bc (format "%s" buffer-file-coding-system)))
    (concat (and (string-match "with-signature" bc) "[BOM]"))))
;; customize-variable を使って mode-line-format の mode-line-mule-info の後に
;; (:eval (my-mode-line-bom-info)) を追加.
(setq-default
 mode-line-format
 '("%e"
   mode-line-front-space
   mode-line-mule-info
   (:eval (my-mode-line-bom-info))
   mode-line-client
   mode-line-modified
   mode-line-remote
   mode-line-frame-identification
   mode-line-buffer-identification
   "   "
   mode-line-position
   (vc-mode vc-mode)
   "  "
   mode-line-modes
   mode-line-misc-info
   mode-line-end-spaces))
;; https://qiita.com/kai2nenobu/items/ddf94c0e5a36919bc6db の方法も良さそう.

リンク一覧