方針 EditToHeaderToFooter

ページ名を日本語に設定できることが PukiWiki 派閥の売りの一つ。しかし、アンカー名は HTML の制約を受けて英字限定である。日本語のページ名を設定できることが PukiWiki 流派の売りの一つ。しかし、アンカー名は HTML の制約を受けて英字限定のままである。
Wiki は HTML ではないから、Wiki は HTML のできないことをできても良い。そして、日本語のページが必要とされるように、日本語のアンカー名も必要とされている。Wiki は HTML ではない。Wiki は HTML のできないことをして良い。日本語のページが必要とされるように、日本語のアンカー名も必要である。
また、日本語のページ名に対応するため、PukiWiki では「ブラケット名」と呼ばれる以下の構文を導入した。また、日本語のページ名に対応するため、PukiWiki では「ブラケット名」と呼ばれる以下の構文を導入している。
  1
   [[ページ名#アンカー名]]

これに対し、アンカーを下ろすには aname プラグインを使う必要がある。

  1
   &aname(アンカー名);

この非対称を無くすため、そして、アンカーを目立たせるため、

aname に対応した Wiki 構文 = ネイティブエイリアスを設ける。aname に対応した Wiki 構文 = ネイティブエイリアスを設けべし。

仕様 EditToHeaderToFooter

アンカープラグイン aname とブラケット書式 [[〜]]の仕様変更 EditToHeaderToFooter

アンカーに日本語を許す。ただし、半角記号「[>#]」は書式に使用するため、アンカー文字に使用不可とする。

アンカーのエンコード方式 EditToHeaderToFooter

アンカーのエンコードは PukiWiki に使われる URL のエンコード方式を流用する。ただし、HTML の id の仕様を満たすため、以下の変換とする。
変換規則
例:アンカー名
目的
1. rawurlencode で URL 文字列に変換
%A5%A2%A5%F3%A5%AB%A1%BC%CC%BE
PukiWiki の URL エンコード方式
2. 「%」を「_」に置換
_A5_A2_A5_F3_A5_AB_A1_BC_CC_BE
HTML の id では「%」を使用不能。
3. 「Anchor」を先頭に付加
Anchor_A5_A2_A5_F3_A5_AB_A1_BC_CC_BE
HTML の id は「_」を先頭に配置不能。Anchorであることを明示。

ネイティブエイリアス [[[〜]]] EditToHeaderToFooter

リンクのネイティブ書式が [[〜]] に因み、アンカーを [[[〜]]] とする。これは、似た目的のインライン書式に同じ記号を割り当てる Wiki の方針に則っている。
太字''〜'' ⇔ 斜体'''〜'''
削除%%〜%% ⇔ 更新%%%〜%%%
リンク[[〜]] ⇔ アンカー[[[〜]]]

[[〜]] 自体の拡張も検討したが、[[〜]] が既に複雑であることを考慮し、別書式とした。

  • 例: アンカー [[##〜]]  ⇔  リンク [[#〜]]
ネイティブエイリアス[[[〜]]]の書式を以下の3つのモードとする。
アンカーのみ [[[# Anchor ]]]
  • Anchor をアンカーに設定する。
  • アンカー位置には何も表示しない。
エイリアス指定 [[[ Alias ]]]
  • Aliasをアンカーに設定する。
  • アンカー位置にAliasを表示する。
エイリアスとアンカーの同時指定 [[[ Alias ># Anchor ]]]
  • Anchor をアンカーに設定する。
  • アンカー位置にAliasを表示する。
書式文字を「@」、対応するプラグイン名を「plugin」とする。
単一行インラインプラグイン
@@〜@@」は「&plugin(〜);」または「&plugin{〜};」と等価である。機能的違いは引数の展開/保持。どっちになるかはプラグインの性質次第。
複数行インラインプラグイン
@@ …
@@
$plugin(…){{{{{{{{{{{{{{{{
}}}}}}}}}}}}}}}};
と等価である。
複数行ブロックプラグイン
@@@ …
@@@
#plugin(…){{{{{{{{{{{{{{{{
}}}}}}}}}}}}}}}}
と等価である。数式プラグインの場合、書式文字は「$」、プラグイン名は「eq」。書式文字は TeX のインライン数式モード「$〜$」に由来。用例は書式/数式を参照。コードプラグインの場合、書式文字は「#」、プラグイン名は「code」。書式文字は、「#」の形が等幅表示のための桝目からの連想。また、C言語で、別言語であるプリプロセッサのコードを埋め込むときに用いられることからの連想。用例は書式/コードを参照。

実装 EditToHeaderToFooter

単一行インラインプラグイン EditToHeaderToFooter

  • lib/make_link.phpclass InlineConverterfunction InlineConverter にて、

    プラグイン aname EditToHeaderToFooter

  • 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.phpclass Link_plugin の後に以下を追加。 #code(php){{{{ // Native-alias Plugin class Link_plugin_alias extends Link_plugin 日本語アンカーのエンコード結果が、半角40文字を容易に超えるために拡張した。 HTML としては id の最大長を指定してないようだが、実用的文字数とブラウザの実装限界を考慮して 1024 と決め付けた。
  • plugin/aname.inc.phpfunction 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); + } }}}}

ブラケット書式 [[〜]] EditToHeaderToFooter

  • 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 Linkfunction 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 Linkfunction 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 }}}}

    ネイティブエイリアス [[[〜]]] EditToHeaderToFooter

  • lib/make_link.inc.php の先頭、class InlineConverterfunction 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 は引数展開。

    複数行インラインプラグイン EditToHeaderToFooter

  • lib/convert_html.phpclass Bodyfunction 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 プラグインの差分。

      複数行ブロックプラグイン EditToHeaderToFooter

  • lib/convert_html.phpclass Bodyfunction 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; } } } }}}}}}}}}}}}}}}}}}}}
    • !」に行は、それぞれの複数行インラインプラグインに対する差分。
      *1 継承しているLink_plugin_aliasネイティブエイリアス で実装したクラスである。
      *2 )(?:([^\[\>\#\]]+)>?)?(?:\#?([^\[\]\>\#]+?
      *3 )(.+?)(
      *4 )()(.+?

課題 EditToHeaderToFooter

  • 見出しのアンカー [#〜] をアンカーのネイティブエイリアス [[[〜]]] に統合すべし。
    • 見出しのハッシュではなく、文字列自体をアンカーにすべし。
      • 今でも **[[[見出し1]]] のようにコーディングすれば一応できるけど。
      • アンカーの禁止文字「[>#]」を何とかすべし。
    • 見出しの階層構造に応じたアンカーの階層化を検討すべし。
      • 例: ページ階層1/ページ階層2/ページ名#見出し1/見出し1-1/見出し1-1-1
      • 例: ページ階層1/ページ階層2/ページ名#見出し1-1-1 ← も衝突しない限り可能にしたい。
    初基 一覧 検索 最新 バックアップ リンク元   ヘルプ   最終更新のRSS