PSGMLのススメ

概要

皆さん普段何使ってXMLやHTML書いてます?

ここでは☆Emacsのメジャーモードの一つである☆PSGMLをご紹介し,楽々マークアップ生活をおくる為の準備と,PSGMLの使い方を簡単に説明したいと思います.

特徴

PSGMLは極めて強力で,機能も豊富にあるのですが,最大の特徴は『その文書型のDTDをPSGML自身が解釈してその場所に最適な要素名で補完してくれる(若しくは挿入してくれたり,リストアップしてくれる)事』,それによって,『全部の要素の親子関係を自分が憶えていなくても意識せず正しいマークアップをすることが出来る事』にあると云えるのではないかと思います.

例えば文書型がXHTML1.1のHTMLを編集しているとしましょう.body要素直下のある箇所に番号付き箇条書き (ol)を入れたくなったとします.PSGMLでは要素を挿入したい時 Control + c (Controlキーを押しながらcを押す) 続けて Control + e というショートカットキー入力をします.[※1]

……云えるのではないかと思います.</p>
  ← ここで C-c C-e
キーバインド

Emacsのinfoやhelpで既に御馴染みかと思いますが,Emacs関連の文書では,Control + c を C-c という書き方をします.ここでもこの先はそういう表記を行なって説明していきます.

表記の仕方は Esc x help t で表示されるEmacs チュートリアルに従いますので,表記に馴染みのない人は,まずはそちらをご覧ください.

すると,画面下部のミニバッファに


E:**   index.html  2003-11-23(Sun) 10:37    (XML)-- [html/body]-----
Element:

と表示され,何要素を入れるのか訊いてきます.この場合,2文字で ol と判っていますから,そのまま olと入力しても良いのですが,PSGMLの特徴を見る為,oを入力してTabキーを押してみましょう.


E:**   index.html  2003-11-23(Sun) 10:37    (XML)-- [html/body]-----
Element: o ←ここで Tab

すると,body要素直下に子要素として記述できる o から始まる要素は ol しかありませんので,自動的に ol と補完してくれます.


E:**   index.html  2003-11-23(Sun) 10:37    (XML)-- [html/body]-----
Element: ol

ここで Return を押します.すると…….

……云えるのではないかと思います.</p>
<ol>
  <li></li>
</ol>

なんということでしょう.ol要素直下に記述できる子要素のli要素のタグまで自動的に補って挿入してくれるではありませんか.おじいちゃんの思い出がこもったOrderedList…….それを匠は得意の技でListItemまで添えて家族にプレゼントしたのです.[※2]

駄文を楽しむ為に……
  1. 大改造!!劇的ビフォーアフターの加藤みどり風に.(サザエさんの加藤みどり風ではないことに注意されたし)
  2. BGMは松谷卓の『TAKUMI/匠』で.
  3. リフォーム内容からみて費用が安すぎるじゃないか! と苦情を送らない.

上記の操作をもしselect要素内で行なった場合,Tabを押した時点では以下のように opt まで補完され,更なる入力を待つ状態になります.


E:**   index.html  2003-11-23(Sun) 10:37    (XML)-- [html/select]-----
Element: opt

そこで更にTabを押すと,以下の様なバッファを開いてその位置に入力できる要素をリストアップしてくれます.

Click <mouse-2> on a completion to select it.
In this buffer, type RET to select the completion near point.

Possible completions are:
optgroup                        option

この様に,DTDに沿った記述が半自動的に行なえますので,「どの要素を子要素として持てるのだろうか?」と迷ったとしてもPSGMLが助けてくれるのです.まさに匠の技

PSGMLの入手とインストール

さて,ここまで読んだ貴方は是非リフォームしてみたい使ってみたいと思った事でしょう.

PSGMLはリリースバージョンの★1.2.5SourceForge☆一次配布元のhttpから,アルファバージョンの★1.3.1☆一次配布元のftpから入手可能です.まずはそれをダウンロードしてきましょう.特に宗教的理由が無い限り version 1.3.1がお奨めです.

ただ,version 1.3.1はMule-UCSと併用する時に,文字から数値文字参照へ変換する sgml-make-character-reference という関数でバグがありますので,以下の手順で●パッチを当ててから make してください.(このパッチ,作者に送ったんだけど音沙汰無し……私の英語もどきメールではダメだったか……哀しいねぇ……)

以下にソースの伸張からmake installまでの手順を上げておきます.コマンドライン中の <site-lisp>は,貴方にroot権限があり,システムのユーザ全員で使いたいなら /usr/share/emacs/site-lisp (OSによってこのパスは異なる),自分だけが使うのなら,自分用の elispディレクトリ (例えば ~/.emacs-lisp) を掘っておいて,適宜そのパスに置き換えてください.

尚,手順中の $# はプロンプトです.

$ tar zxf psgml-1.3.1.tar.gz
$ patch -p1 <  psgml-1.3.1-char_ref.patch
$ cd psgml-1.3.1
$ ./configure --prefix=/usr
$ make EMACS=emacs
管理権限がありシステムのsite-lispにInstallする場合
$ sudo make lispdir=<site-lisp>/psgml
      もしくは
$ su
Password: ぱすわーどを入力
# make  lispdir=<site-lisp>/psgml
自分だけが使用し以下にInstallする場合
$ make lispdir=<site-lisp>/psgml

DTDの入手とインストール

PSGMLを有効に活用するには,なんと言ってもDTDをPSGMLからきちんと読めるようにしておく事が大事です.

ここでは例としてユーザが自分用にXHTML1.1のDTDを入手して設置する手順を上げておきます.($HOMEで作業をしていると仮定)

  1. ☆W3Cのサイトから★アーカイヴをダウンロード
  2. 自分のホームディレクトリに ~/DTD というディレクトリを掘ってそこへ伸張.

    $ mkdir -m755 DTD
    $ tar zxf xhtml11.tgz -C DTD
  3. ~/DTD/xhtml11-20010531/DTD/ 以下に catalogファイルとdtdがあることを確認してみましょう.

    $ ls DTD/xhtml11-20010531/DTD/*
    DTD/xhtml11-20010531/DTD/VERSION
    DTD/xhtml11-20010531/DTD/xhtml11-flat.dtd     ← これを使う
    DTD/xhtml11-20010531/DTD/xhtml11-model-1.mod
    DTD/xhtml11-20010531/DTD/xhtml11.cat          ← このカタログを指定する
    DTD/xhtml11-20010531/DTD/xhtml11.dtd
    DTD/xhtml11-20010531/DTD/xml1.dcl
    DTD/xhtml11-20010531/DTD/xml1n.dcl

.emacsの設定

XHTMLをEmacsで開いた時にPSGMLがautoloadされるように .emacs に必要な設定を施します.

以下は設定例です.ここではXHTMLの編集を例に挙げていますので,それ以外の設定を省いてあります.(★テキスト版)

font-lockをpsgmlデフォルトの色分けで使う場合は,該当部分のコメントを外し,;;; My original font-lock-keywordsから下をコメントアウトしてください.(お好みでどうぞ)

My original font-lock部分だけ自分の .emacsの設定に取りこんで使いたい方は,そこだけ抜きだした ★xml-font-lock.elを .emacs で(require 'xml-font-lock)するなりして使ってください.

上記のfont-lock別スクリーンショット
;;; dot emacs for psgml
(autoload 'sgml-mode "psgml" "Major mode to edit SGML files." t)
(autoload 'xml-mode "psgml" "Major mode to edit XML files." t)
(setq auto-mode-alist
      (append
       '(("\\.html$" . xml-mode)
         ("\\.xhtml$" . xml-mode))
       auto-mode-alist))

;;; カタログファイルの指定
(setq sgml-catalog-files '("~/DTD/xhtml11-20010531/DTD/xhtml11.cat"))

;;; DOCTYPE 宣言の設定
(setq sgml-custom-dtd
      '(
        ("XHTML 1.1"
         "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"
                      \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">")
        ))

;;; hookで変数をsetq
(add-hook 'sgml-mode-hook
          (lambda ()
            (setq tab-width                             2
                  sgml-indent-step                      2
                  indent-tabs-mode                      nil
                  sgml-xml-p                            t
                  sgml-always-quote-attributes          t
                  sgml-system-identifiers-are-preferred t
                  sgml-auto-activate-dtd                t
                  sgml-recompile-out-of-date-cdtd       t
                  sgml-auto-insert-required-elements    t
                  sgml-insert-missing-element-comment   t
                  sgml-balanced-tag-edit                t
                  sgml-default-doctype-name             "XHTML 1.1"
                  sgml-ecat-files                       nil
                  sgml-general-insert-case              'lower
                  sgml-entity-insert-case               'lower
                  sgml-normalize-trims                  t
                  sgml-insert-defaulted-attributes      nil
                  sgml-live-element-indicator           t
                  sgml-active-dtd-indicator             t
                  sgml-minimize-attributes              nil
                  sgml-omittag                          nil
                  sgml-omittag-transparent              nil
                  sgml-shorttag                         nil
                  sgml-tag-region-if-active             t
                  sgml-xml-validate-command             "xmllint --noout --valid %s %s"
                  )
            ))

;; これ以下はお好みで

;;; font-lock
(font-lock-mode 1)
(setq font-lock-support-mode   'jit-lock-mode
      jit-lock-stealth-verbose nil
      font-lock-verbose nil)

;;;;; PSGML デフォルトのfont-lockを使う場合
;;(setq sgml-set-face t
;;    sgml-markup-faces '((start-tag  . font-lock-builtin-face)
;;                          (end-tag    . font-lock-builtin-face)
;;                          (ms-start   . font-lock-variable-name-face)
;;                          (ms-end     . font-lock-variable-name-face)
;;                          (comment    . font-lock-comment-face)
;;                          (ignored    . font-lock-warning-face)
;;                          (pi         . font-lock-preprocessor-face)
;;                          (sgml       . font-lock-type-face)
;;                          (doctype    . font-lock-constant-face)
;;                          (entity     . font-lock-string-face)
;;                          (shortref   . font-lock-reference-face)))

;;; My original font-lock-keywords
(add-hook 'sgml-mode-hook
          '(lambda ()
             (make-local-variable 'font-lock-defaults)
             (setq sgml-set-face nil
                   font-lock-defaults '(xml-font-lock-keywords-2 nil))
             (turn-on-font-lock)
             ))

(defvar xml-font-lock-keywords-1
  (list
   ;; タグ開始区切子 & タグ終了区切子
   '("<\\|>" 0 font-lock-keyword-face t)
   ;; スラッシュ
   '("\\(/\\)>" 1 font-lock-keyword-face t)
   '("<\\(/\\)" 1 font-lock-keyword-face t)
   ;; 要素名
   '("\\(</?\\)\\([a-zA-Z]+[a-zA-Z0-9-_:]*\\)" 2  font-lock-builtin-face t)
   ;; コメント
   '("\\(<!--\\([^-]\\|-[^-]\\|--[^>]\\)*-->\\)" 1 font-lock-comment-face t)
   ;; 命令処理
   '("\\(<\\?[a-zA-Z]*\\>[^<>]*\\(<[^>]*>[^<>]*\\)*\\?>\\)" 1 font-lock-type-face t)
   ;; DOCTYPE, ENTITY, ATTLIST, NOTATION等々 マーク宣言
   '("\\(<![a-zA-Z]+\\>[^<>]*\\(<[^>]*>[^<>]*\\)*>\\)" 1 font-lock-constant-face t)
   ;; °
   '("\\<\\([a-zA-Z]+[a-zA-Z-_:]*\\)=" 1 font-lock-variable-name-face t)
   ;; 属性値
   '("=?\\(\"[^\"]*\"\\|'[^\']*'\\)" 1 font-lock-string-face t)
   ;; 数値文字参照, 文字実体参照, パラメータ実体参照
   '("\\(&#[0-9]+;\\|&[a-zA-Z]+;\\|%[^'\";]+;\\)" 1 font-lock-reference-face t)
   ;; CDATA 等々 マーク区間 (マーク指定区域)
   '("\\(<!\\[[^\\[]+\\[[^]]+]]>\\)" 1 font-lock-warning-face t)
   ))

(defvar xml-font-lock-keywords-2
  (append
   xml-font-lock-keywords-1
   (list
    ;; SSI
    `(,(concat "\\(<!--#\\(fsize\\|flastmod\\|printenv\\|"
               "include\\|echo\\|config\\|exec\\|set\\|"
               "if\\|elif\\|else\\|endif\\)\\>[ \t\n]+"
               "\\([^-]\\|-[^-]\\|--[^>]\\)*-->\\)")
      1 'bold t)
    ;; php
    '("\\(<\\?\\(php\\|=\\)[^?>]+\\?>\\)" 1 font-lock-function-name-face t)
    ;; eRuby, JSP, ASP
    '("\\(<%\\(=\\)?\\>[^%>]+%>\\)" 1 font-lock-function-name-face t)
    )))

各変数の詳細はマニュアルを読んでください.ここでは,仕様から外れた糞な文書を書かない為に大事な部分を抜粋して説明します.

sgml-catalog-files
カタログファイルのパス.これを指定しておかないとDTDを読んでくれません.幸いXHTML1.1はW3Cの方でxhtml11.catというカタログを用意してくれていますので,自分でカタログを書く必要はありません.
sgml-live-element-indicator
モードラインに現在の位置を表示するかどうか.例えば p の内容の位置にカーソルが居れば [html/p]と表示される.ちなみにこの文章の位置は [html/dd]
sgml-omittag
省略可能なタグ(OMITTAG)のタグ省略を許可するか否か.SGML宣言で OMITTAG YESとされている場合で省略が自明な場合などに,タグの省略を許可するかどうか.XHTMLは全ての終了タグの省略は出来ませんのでnilにしておきます.
sgml-omittag-transparent
透過的にOMITTAGを許可するか否か.例えば省略可能な終了タグを越えて内容に省略可能なタグのスタートタグや終了タグを書くようなマークアップを許可するかどうか.ここもnil
sgml-shorttag
短縮タグを許可するか否か.SGML 宣言で SHORTTAG YES とされている場合,例えば 終了タグの </> やエンプティスタートタグの <> のような書き方が許容される場合があるのですが,XMLはこれは認められませんので nil にしておきます.
sgml-balanced-tag-edit
上のsgml-omittag-transparentにも関わってきますが,コンテキストメニューからstart-endタグペアで挿入するか否か.
sgml-warn-about-undefined-elements
定義の無いタグの存在で警告するか否か.
sgml-warn-about-undefined-entities
定義の無いエンティティの存在で警告するか否か.
sgml-auto-activate-dtd
ファイルを開いた時に自動的にDTDを解釈してパースするか否か.
sgml-auto-insert-required-elements
自動的にその要素に必要な子要素を挿入するか否か.例えば前述のol liの関係.
sgml-insert-defaulted-attributes
#DEFAULTとされている属性値を挿入するか否か.
sgml-always-quote-attributes
属性値を常に引用符で括るか否か.XHTMLは属性値はつねに引用符で括られなければなりません.
sgml-general-insert-case
大文字小文字どちらで要素名を入れるか.XHTMLに大文字の要素はありません.
sgml-minimize-attributes
属性最小化を許可するか否か.例えば,input要素のcheckboxで checked="checked"をcheckedと書くことを許すか否か.XHTMLではこういう略記は許されません.

使いかた

憶えておきたいキーとコマンドの対応,機能を上げておきます.( [] 内はコマンド)

C-c C-e [sgml-insert-element]
カーソル位置にタグペアを挿入します.前出の様に Tab で補完が出来ます.
C-c C-r [sgml-tag-region]
選択リージョンがアクティブならば,そのリージョンを囲んでタグを挿入します.
C-c = [sgml-change-element-name]
カーソル位置の要素の要素名を変更します.
C-c - [sgml-untag-element]
カーソル位置の要素のタグペアを除去します.
C-c + [sgml-insert-attribute]
カーソル位置の要素に属性を挿入します.
C-c C-a [sgml-edit-attributes]
カーソル位置の要素の属性値を別バッファを開いて編集します.
C-c # [sgml-make-character-reference]
文字を数値文字参照へ変換します.(例: & → &#38;)
webmaster Shigeyuki Yamashita (@). This server is sickhack.homelinux.org.

LastModified: 2006-12-20T11:01:17+0900