hikidoc_pre.rb: Hiki の を拡張する plugin
Hiki では「<<<」と「>>>」で <pre></pre> を表現できるけど、それを拡張する plugin。
- 行番号をつける。開始番号も指定できる。また JavaScript で on/off できる。
- インラインで <strong> と <em> が使える。「{{*foo*}}」が「<strong>foo</foo>」に、「{{/bar/}}」が「<em>bar</em>」になる。
- class 属性を指定できる。例えば、ソースコードとコンソールとで別々の class 属性を指定できる。
例:
<<<: linenum=5, inline=true, class=console, format='%03d| ' x: {{*foo*}} y: {{/bar/}} >>>
結果:
<div style="text-align:right"> <a onclick="javascript:toggle_linenums(this)">hide line numbers</a> </div> <pre class="console"> <span>005|</span> x: <strong>foo</strong> <span>006|</span> y: <em>bar</em> </pre>
短縮記法も用意されている。
- 「<<<#」は「<<<: linenum=1」と同じ。
- 「<<<*」は「<<<: inline=true」と同じ。
- 「<<<%」は「<<<: class=console」と同じ。
- 「<<<#*%」は「<<<: linenum=1, inline=true, class=console」と同じ。「#*%」の順番は問わない。「<<<#*: class=name%」のような指定も可。
plugin を使うには、hiki/misc/plugins にコピーするだけ。
以下がその plugin。
hikidoc_pre.rb:
## parse from '<<<' to '>>>' ## ## ex. preformatted text ## <<< ## x: foo ## y: bar ## >>> ## ## result: ## <pre> ## x: foo ## y: bar ## </pre> ## ## ex. preformatted text with line numbers, inline marking, and class attribute ## <<<: linenum=5, inline=true, class=console, format='%03d| ' ## x: {{*foo*}} ## y: {{/bar/}} ## >>> ## ## result: ## <div style="text-align:right"> ## <a onclick="javascript:toggle_linenums(this)">hide line numbers</a> ## </div> ## <pre class="console"> ## <span>005|</span> x: <strong>foo</strong> ## <span>006|</span> y: <em>bar</em> ## </pre> ## ## If 'linenum' option is specified, '<a>hide line numbers</a>' is placed ## which can hide/show line numbers. ## ## Several short notations are provided. ## * '<<<#' is equivarent to '<<<: linenum=1' ## * '<<<*' is equivarent to '<<<: inline=true' ## * '<<<%' is equivarent to '<<<: class=console' ## * '<<<#*%' is equivarent to '<<<: linenum=1, inline=true, class=console' ## (order of '#', '*', and '%' is not important) ## * '<<<#*: class=name' is also available ## ## Default format is '%03d: '. ## ## $Revision$ ## $Date$ require 'style/default/hikidoc' class ::HikiDoc def parse_pre( text ) ret = text ret.gsub!( /^#{MULTI_PRE_OPEN_RE}([#*%]*)(?::[ \t]+(.*?))?$(.*?)^#{MULTI_PRE_CLOSE_RE}$/m ) do |str| opts = _parse_options($2) content = restore_pre($3) content = _manipulate_pre_content(content, opts, $1) prefix = _toggle_linenums_anchor(opts, $1) stag, etag = _create_pre_tags(opts, $1) "\n" + prefix + store_block( "#{stag}#{content}#{etag}" ) + "\n\n" end ret.gsub!( /(?:#{PRE_RE}.*\n?)+/ ) do |str| str.chomp! str.gsub!( PRE_RE, '' ) "\n" + store_block( "<pre>\n#{restore_pre(str)}\n</pre>" ) + "\n\n" end ret end def _toggle_linenums_script() return <<END <script language="javascript" type="text/javascript"> <!-- ;function toggle_linenum(elem_anchor) { ; var label = elem_anchor.firstChild.nodeValue; ; var action = label.substring(0, 4) == 'show' ? 'hide' : 'show'; ; elem_anchor.firstChild.nodeValue = action + ' line numbers'; ; var display = action == 'show' ? 'none' : 'inline'; ; var elem_pre = elem_anchor.parentNode.nextSibling; ; for (var i = 0, n = elem_pre.childNodes.length; i < n; i++) { ; var child = elem_pre.childNodes[i]; ; if (child.tagName == 'SPAN') { ; child.style.display = display; ; } ; } ;} --> </script> END end def _toggle_linenums_anchor(opts, optstr) flag_linenum = opts.fetch('linenum', optstr.include?('#') ? 1 : nil).is_a?(Integer) return '' unless flag_linenum anchor = '<div style="text-align:right;margin-bottom:-2em;font-size:small;"><a onclick="javascript:toggle_linenum(this);return false">hide line numbers</a></div>' return anchor if @_toggle_linenums @_toggle_linenums = true script = _toggle_linenums_script() return script + anchor end def _create_pre_tags(opts, optstr) class_attr = opts.fetch('class', optstr.include?('%') ? 'console' : nil) attr = class_attr ? " class=\"#{escape_quote(class_attr)}\"" : "" return "<pre#{attr}>", "</pre>" end LINENUMS_DEFAULT_FORMAT = '%3d: ' def _manipulate_pre_content(content, opts, optstr) optstr ||= '' linenum = opts.fetch('linenum', optstr.include?('#') ? 1 : nil) inline = opts.fetch('inline', optstr.include?('*')) # add line numbers if linenum.is_a?(Integer) n = linenum - 1 format = opts.fetch('format', LINENUMS_DEFAULT_FORMAT) content.gsub!(/\A\r?\n/, '') content.gsub!(/^/) { "<span>#{format % (n += 1)}</span>" } content = "\n" + content end # {{*text*}} => <strong>text</strong>, {{/text/}} => <em>text</em> if inline == true content.gsub!(/\{\{\*(.*?)\*\}\}/, '<strong>\1</strong>') content.gsub!(/\{\{\/(.*?)\/\}\}/, '<em>\1</em>') content.gsub!(/\{\{\}\}/, '') end return content end def _parse_options(option_str) opts = {} option_str.split(/\s*,\s*/).each do |option| if option =~ /\A(\w+)(?:=(.*))?/ key, val = $1, $2 case val when nil ; val = true when 'true', 'yes' ; val = true when 'false', 'no' ; val = false when 'nil', 'null' ; val = nil when /\A[-+]?\d+\z/ ; val = val.to_i when /\A[-+]?\d+\.\d+\z/ ; val = val.to_f when /\A'(.*)'\z/ ; val = $1 when /\A"(.*)"\z/ ; val = $1 end else key, val = option, true end opts[key.strip] = val end if option_str return opts end end