方針
ページ名を日本語に設定できることが PukiWiki 派閥の売りの一つ。しかし、アンカー名は HTML の制約を受けて英字限定である。日本語のページ名を設定できることが PukiWiki 流派の売りの一つ。しかし、アンカー名は HTML の制約を受けて英字限定のままである。Wiki は HTML ではないから、Wiki は HTML のできないことをできても良い。そして、日本語のページが必要とされるように、日本語のアンカー名も必要とされている。Wiki は HTML ではない。Wiki は HTML のできないことをして良い。日本語のページが必要とされるように、日本語のアンカー名も必要である。また、日本語のページ名に対応するため、PukiWiki では「ブラケット名」と呼ばれる以下の構文を導入した。また、日本語のページ名に対応するため、PukiWiki では「ブラケット名」と呼ばれる以下の構文を導入している。これに対し、アンカーを下ろすには aname プラグインを使う必要がある。
この非対称を無くすため、そして、アンカーを目立たせるため、
aname に対応した Wiki 構文 = ネイティブエイリアスを設ける。aname に対応した Wiki 構文 = ネイティブエイリアスを設けべし。
仕様
aname の仕様変更
アンカープラグイン aname のアンカーに日本語を許す。
アンカープラグイン aname とブラケット書式 [[〜]]の仕様変更
アンカーに日本語を許す。ただし、半角記号「[>#]」は書式に使用するため、アンカー文字に使用不可とする。
アンカーのエンコード方式
アンカーのエンコード方式
アンカーのエンコードは PukiWiki に使われる URL のエンコード方式を流用する。ただし、HTML の id の仕様を満たすため、以下の変換とする。 | | |
1. rawurlencode で URL 文字列に変換 | %A5;%A2%A5%F3%A5%AB%A1%BC%CC%BE | |
| _A5_A2_A5_F3_A5_AB_A1_BC_CC_BE | |
| Anchor_A5_A2_A5_F3_A5_AB_A1_BC_CC_BE | HTML の id は「_」を先頭に配置不能。Anchorであることを明示。 |
ネイティブエイリアス [[[〜]]]
ネイティブエイリアス [[[〜]]]
リンクのネイティブ書式が [[〜]] に因み、アンカーを [[[〜]]] とする。これは、似た目的のインライン書式に同じ記号を割り当てる Wiki の方針に則っている。太字 | : ''〜'' | ⇔ | 斜体 | : '''〜''' |
削除 | : %%〜%% | ⇔ | 更新 | : %%%〜%%% |
リンク | : [[〜]] | ⇔ | アンカー | : [[[〜]]] |
[[〜]] 自体の拡張も検討したが、[[〜]] の仕様の複雑さを考慮し、別書式とした。
[[〜]] 自体の拡張も検討したが、[[〜]] が既に複雑であることを考慮し、別書式とした。
- 例: アンカー [[##〜]] ⇔ リンク [[#〜]]
ネイティブエイリアス[[[〜]]]の書式を以下の3つのモードとする。
- アンカーのみ [[[# Anchor ]]]
- Anchor をアンカーに設定する。
- アンカー位置には何も表示しない。
- エイリアス指定 [[[ Alias ]]]
- Aliasをアンカーに設定する。
- アンカー位置にAliasを表示する。
- エイリアスとアンカーの同時指定 [[[ Alias ># Anchor ]]]
- Anchor をアンカーに設定する。
- アンカー位置にAliasを表示する。
数式プラグインの場合、書式文字は「$」、プラグイン名は「eq」。書式文字は TeX のインライン数式モード「$〜$」に由来。用例は書式/数式を参照。コードプラグインの場合、書式文字は「#」、プラグイン名は「code」。書式文字は、「#」の形が等幅表示のための桝目からの連想。また、C言語で、別言語であるプリプロセッサのコードを埋め込むときに用いられることからの連想。用例は書式/コードを参照。
実装
単一行インラインプラグイン
- lib/make_link.php の class InlineConverter の function InlineConverter にて、
プラグイン aname
- plugin/aname.inc.php の先頭、グローバル領域にて、
1
2
3
4
5
6
7
8
9
10
11
12
13
| | function InlineConverter($converters = NULL, $excludes = NULL)
{
if ($converters === NULL) {
$converters = array(
'plugin', // Inline plugins
+ 'eq', // eq plugin
+ 'code', // code plugin
'escape', // Escapes
'note', // Footnotes
……
// Max length of ID
- define('PLUGIN_ANAME_ID_MAX', 40);
+ define('PLUGIN_ANAME_ID_MAX', 1024);
|
- lib/make_link.php の class Link_plugin の後に以下を追加。 #code(php){{{{ // Native-alias Plugin class Link_plugin_alias extends Link_plugin 日本語アンカーのエンコード結果が、半角40文字を容易に超えるために拡張した。 HTML としては id の最大長を指定してないようだが、実用的文字数とブラウザの実装限界を考慮して 1024 と決め付けた。
- plugin/aname.inc.php の function lugin_aname_tag の中、id を取得直後に以下を追加。 #code(diff){{{{ function plugin_aname_tag($args = array(), $convert = TRUE){ var $name = 'echo'; // プラグイン名 var $pattern = ''; // 正規表現 Link_plugin に対応させるため、 // $matches[1] = plain // $matches[2] = plugin name // $matches[3] = parameter // $matches[4] = body // 独自拡張 // 引数展開 ...
function Link_eq($start) { parent::Link_plugin($start); } $id = array_shift($args); + if (! preg_match(PLUGIN_ANAME_ID_REGEX, $id)) + { + $id = rawurlencode($id); + $id = 'Anchor' . str_replace('%', '_', $id); + } }}}}
ブラケット書式 [[〜]]
- lib/make_link.inc.php の先頭、グローバル領域にて、plugin/aname.inc.php で設定されているアンカー名の正規表現を設定する。 #code(diff){{{{ + // Pattern of ID + define('PLUGIN_ANAME_ID_REGEX', '/^[A-Za-z][\w\-]*$/'); }}}}
- lib/make_link.inc.php の中ほど、class Link_bracketname extends Link の function get_pattern にて、return 対象の正規表現を変更。 #code(diff){{{{function get_pattern(){ return $this->pattern; } global $WikiName, $BracketName;
function get_count() { return 4; $s2 = $this->start + 2; return <<<EOD \[\[ # Open bracket (?:((?:(?!\]\]).)+)>)? # (1) Alias (\[\[)? # (2) Open bracket ( # (3) PageName (?:$WikiName) | (?:$BracketName) )? - (\#(?:[a-zA-Z][\w-]*)?)? # (4) Anchor + (?:\#([^\[\>\#\]]+)?)? # (4) Anchor (?($s2)\]\]) # Close bracket if (2) \]\] # Close bracket EOD;} }}}}
- lib/make_link.inc.php の中ほど、class Link_bracketname extends Link の function toString にて、 #code(diff){{{{ + if (! preg_match(PLUGIN_ANAME_ID_REGEX,$this->anchor)) + { + $this->anchor = rawurlencode($this->anchor); + $this->anchor = 'Anchor' . str_replace('%', '_', $this->anchor); + } + $this->anchor = '#'.$this->anchor; + return make_pagelink( $this->name, $this->alias, $this->anchor, $this->page }}}}
ネイティブエイリアス [[[〜]]]
- lib/make_link.inc.php の先頭、class InlineConverter の function InlineConverter にて、'bracketname' を登録直前、 #code(diff){{{{ function InlineConverter($converters = NULL, $excludes = NULL) { if ($converters === NULL) { $converters = array( ... + 'bracketanchor', // BracketAnchor 'bracketname', // BracketName ... ); } }}}} 正規表現として [[[〜]]] は [[〜]] に包含されるため、'bracketanchor' は必ず 'bracketname' の前に登録する必要がある。
- lib/make_link.inc.php の中ほど、class Link_bracketname の前にて、以下を挿入 *1。 #code(php){{{{ // BracketAnchor // Native-alias for &aname plugin class Link_bracketanchor extends Link_plugin_alias { var $name = 'aname'; var $pattern = '\[\[\[*2?)\]\]\]';
function set($arr, $page){ list(, $this->plain, , $this->param, $body) = $this->splice($arr); list(, $this->plain, , $body, $this->param) = $this->splice($arr); if ($this->param == '') {$this->param = $body;} // アンカー省略=エイリアスをアンカーに使用return parent::setParam($page, $this->name, $body, 'plugin');}} // Native-alias for &eq plugin class Link_eq extends Link_plugin_alias { var $name = 'eq'; var $pattern = '\$\$*3\$\$'; // no name, no body } // Native-alias for &code plugin class Link_code extends Link_plugin_alias { var $name = 'code'; var $pattern = '\#\#*4\#\#'; // no name, no parameter }}}}}
- Link_plugin_alias は基底クラス。直接生成されることはない。
- eq は引数保持、code は引数展開。
複数行インラインプラグイン
- lib/convert_html.php の class Body の function parse にて、 複数行プラグインの後あたり。 #code(php){{{{{{{{{{{{{{{{{{{{ // Native-alias for $eq-Plugin $pattern = '/^\s*\$\$\s*$/'; if (preg_match($pattern, $line)) { $delimiter = "\r"; $line = '&eq{{{{{{{{{{{{{{{{'.$delimiter; while (! empty($lines)) { $next_line = rtrim(array_shift($lines), "\r\n"); if ($indent_format) { preg_match('/^(\s{'.$indent.'}?)?(.*)$/', $next_line, $matches); $next_line = $matches[2]; } if (preg_match($pattern, $next_line)) { $line .= ltrim('}}}}}}}}}}}}}}}};'); break; } else { $line .= $next_line . $delimiter; } } } }}}}}}}}}}}}}}}}}}}} #code(diff){{{{{{{{{{{{{{{{{{{{ ! // Native-alias for $code-Plugin ! $pattern = '/^\s*##\s*$/'; if (preg_match($pattern, $line)) { $delimiter = "\r"; ! $line = '$code{{{{{{{{{{{{{{{{'.$delimiter; while (! empty($lines)) { $next_line = rtrim(array_shift($lines), "\r\n"); if ($indent_format) { preg_match('/^(\s{'.$indent.'}?)?(.*)$/', $next_line, $matches); $next_line = $matches[2]; } if (preg_match($pattern, $next_line)) { $line .= ltrim('}}}}}}}}}}}}}}}};'); break; } else { $line .= $next_line . $delimiter; } } } }}}}}}}}}}}}}}}}}}}}
- 「!」に行は、eqプラグイン に対する code プラグインの差分。
複数行ブロックプラグイン
- lib/convert_html.php の class Body の function parse にて、 複数行プラグインの後あたり。 #code(diff){{{{{{{{{{{{{{{{{{{{ ! // Native-alias for #eq-Plugin ! $pattern = '/^\s*\$\$\$\s*$/'; if (preg_match($pattern, $line)) { $delimiter = "\r"; ! $line = '#eq{{{{{{{{{{{{{{{{'.$delimiter; while (! empty($lines)) { $next_line = rtrim(array_shift($lines), "\r\n"); if ($indent_format) { preg_match('/^(\s{'.$indent.'}?)?(.*)$/', $next_line, $matches); $next_line = $matches[2]; } if (preg_match($pattern, $next_line)) { $line .= ltrim('}}}}}}}}}}}}}}}}'); break; } else { $line .= $next_line . $delimiter; } } } }}}}}}}}}}}}}}}}}}}} #code(diff){{{{{{{{{{{{{{{{{{{{ ! // Native-alias for #code-Plugin ! $pattern = '/^\s*###\s*(\w*)\s*$/'; ! if (preg_match($pattern, $line, $matches)) { $delimiter = "\r"; ! $line = '#code('.$matches[1].'){{{{{{{{{{{{{{{{'.$delimiter; while (! empty($lines)) { $next_line = rtrim(array_shift($lines), "\r\n"); if ($indent_format) { preg_match('/^(\s{'.$indent.'}?)?(.*)$/', $next_line, $matches); $next_line = $matches[2]; } if (preg_match($pattern, $next_line)) { $line .= ltrim('}}}}}}}}}}}}}}}}'); break; } else { $line .= $next_line . $delimiter; } } } }}}}}}}}}}}}}}}}}}}}
- 「!」に行は、それぞれの複数行インラインプラグインに対する差分。
継承しているLink_plugin_alias は ネイティブエイリアス で実装したクラスである。
)(?:([^\[\>\#\]]+)>?)?(?:\#?([^\[\]\>\#]+?
)(.+?)(
)()(.+?
課題
- 見出しのアンカー [#〜] をアンカーのネイティブエイリアス [[[〜]]] に統合すべし。
- 見出しのハッシュではなく、文字列自体をアンカーにすべし。
- 今でも **[[[見出し1]]] のようにコーディングすれば一応できるけど。
- アンカーの禁止文字「[>#]」を何とかすべし。
- 見出しの階層構造に応じたアンカーの階層化を検討すべし。
- 例: ページ階層1/ページ階層2/ページ名#見出し1/見出し1-1/見出し1-1-1
- 例: ページ階層1/ページ階層2/ページ名#見出し1-1-1 ← も衝突しない限り可能にしたい。