カテゴリ「Programming」の3件の記事 Feed

2011年12月18日 (日)

eoblogをMarkdown記法で書く3(プレビュー)

の続きです。

Markdown(+kramdown拡張)記法で書くといってもやっぱりHTML表示もしたいですよね。
まあ昨日のHTMLでも一応表示は出来るのは出来る(IEやchromeでは文字化けするからエンコードをUTF-8にしないと駄目だけど)んですが、極力ブログ上の見た目と同じになるようにするのが目的です。

準備

まずローカルの適当なフォルダを基準フォルダに決めて、その下にSyntax Highlighterからダウンロードしたファイルを解凍したファイル群をコピーします(最低限stylesとscriptsフォルダは必要)。フォルダはどこでも良いですが、後であまりスクリプトソース修正したくない人はlocal/syntaxに入れといて下さい。

フォルダイメージ

次にスタイルシートファイルをサーバーよりゲットします。HTMLソース表示して、それっぽいスタイルシートファイル(.css)を保存します(eoblogの場合はstyles.css)。保存する場所はどこでもいいですが、後であまりスクリプトソース修正したくない人はlocal/css1にstyles.cssという名前で保存しといて下さい。

スタイルシートファイルを参照して、画像ファイルとか他にインポートしてるスタイルシートファイルがあるなら、それもローカルに保存します。

補足

eoblogのstyles.cssは

@import url(/.shared-asp26/themes/common/base-weblog.css);

のようなcssファイルをインポートしてます。(テーマを使用してる場合はテーマ用のcssファイルもある。)

これの場所は http://xxx.blog.eonet.jp/.shared-asp26/themes/common/base-weblog.css から保存します。まあ当然といえば当然なんですが・・・。
あと保存したstyles.cssの上インポートするスタイルシートの場所を書き換えるのをお忘れなく(styles.cssとbase-weblog.cssを同じフォルダに置く場合@import(base-weblog.css)でOK)

テーマ用も同様です。テーマ用の場合使用してる画像ファイル(.png)を保存しておくと見た目がだいぶ近づきます。

スクリプトソース

# -*- coding: utf-8 -*-
require 'rubygems'
require 'kramdown'
require 'optparse'
require 'date'

@option_hash = {
  :tabwidth => 4,
  :blogmode => true,
  :date_fmt => "%Y/%m/%d (%a)",
  :title => "ブログタイトル",
  :subtitle => "ブログ副題",
  }

#markdown記法をHTML(body)に変換する
#返り値:body文字列
def mkd2htmlbody(text)
  text.encode!("UTF-8")
  #text = text.lines.to_a[1..-1].join # Just ignore the first line
  body = Kramdown::Document.new(text).to_html
  if ( @option_hash[:blogmode] )
    if (false)
      # <pre class="brush~><code>~</code></pre>
      # の場合、<code>、</code>が邪魔なので取り除く
      # こちらを使う場合、SyntaxHighlighter.config.tagName = "code"を消すこと
      body = body.gsub(\
          %r!<pre[\s\t]+([^<>]*?)class[\s\t]*="brush(.*?)>[\s\t\r\n]*<code>(.+?)</code>[\s\t\r\n]*</pre>!mi,\
          '<pre \1class="brush\2>\3</pre>')
    else
      # <pre class="brush~><code>~</code></pre>を
      # <pre><code class="brush~>~</code></pre>に変更
      body = body.gsub(%r!<pre[\s\t]+([^<>]*?)class[\s\t]*="brush(.+?)>[\s\t\r\n]*<code>(.+?)</code>[\s\t\r\n]*</pre>!mi){|match|
        strg = "<pre><code #{$1}class=\"brush#{$2}>#{$3}</code></pre>"
        match = strg
      }
    end
  end
  #<pre>~</pre>内のTABを取り除く。タブ幅はtabwidthで与える
  body = body.split(/\n/).map {|line|
    if (/<pre/ === line) .. (/<\/pre>/ === line) 
      line.gsub(/\t/, "\s"*@option_hash[:tabwidth])
    else
      line
    end
  }.join "\n"
  str_h1 = nil
  # H1→消去 H2→H4 H3→H5 H4→H6に変更する
  if ( @option_hash[:blogmode] )
    #H1消去
    body = body.gsub(%r!<h1.*?>(.+?)</h1>!i, "")
    str_h1 = $1
    #
    for i in [4,3,2]
      body = body.gsub(/<h#{i}(.+?)<\/h#{i}>/,"<h#{i+2}\\1<\/h#{i+2}>")
    end
  end
  return body, str_h1
end

def mkd2htmlbodyfile(inputname, outputname)
  sbody = nil
  File.open(inputname, "r") { |file_in|
    sbody  = mkd2htmlbody(file_in.read)
  }
  #outputname = 1 if (outputname == nil)
  File.open(outputname, "w") { |file_ou|
    file_ou.puts sbody
  }
end

def mkd2bloghtmlfile(inputname, outputname)
  sbody, h1_str = nil
  
  File.open(inputname, "r") { |file_in|
    sbody, h1_str = mkd2htmlbody(file_in.read)
  }
  #outputname = 1 if (outputname == nil)
  File.open(outputname, "w") { |file_ou|
    #inputnameから日付を取得する
    date_match = nil
    date_match = $1 if (File.basename(inputname) =~ /(^[0-9]+)/)
    dt = nil
    dt_today = Date.today()
    if (date_match == nil)
      dt = dt_today
    elsif (date_match.length == 2)    #日付を表すとする
      dt = Date.new(dt_today.year, dt_today.month, date_match.to_i)
    elsif (date_match.length == 4)    #月日を表すとする
      dt = Date.new(dt_today.year, date_match[0,2].to_i, date_match[2,2].to_i)
    elsif (date_match.length == 6)    #年月日を表すとする
      year = dt_today.year/100*100
      year += date_match[0,2].to_i
      dt = Date.new(year, date_match[2,2].to_i, date_match[4,2].to_i)
    elsif (date_match.length == 8)    #年月日を表すとする
      dt = Date.new(date_match[0,4].to_i, date_match[4,2].to_i, date_match[6,2].to_i)
    end
    str_dt = @option_hash[:date_fmt].to_s.gsub(/\#\{(.+?)\}/){|match|
      match = eval($1)
    }
    str_dt = dt.strftime(str_dt)
    file_ou.puts <<-EOF.gsub(/^[\s\t]+\|/, '') 
    |<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"  
    |     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    |<html>
    |<head>
    |<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    |<title>#{@option_hash[:title]}(preview)</title>
    |<!-- Style Sheet -->
    |<link rel="stylesheet" href="./local/css1/styles.css" type="text/css" media="screen" />
    |</head>
    |<body class="layout-two-column-right">
    |<div id="container">
    |  <div id="container-inner" class="pkg">
    |    <div id="banner">
    |     <div id="banner-inner" class="pkg">
    |       <h1 id="banner-header">#{@option_hash[:title]}</h1>
    |       <h2 id="banner-description">#{@option_hash[:subtitle]}</h2>
    |     </div>
    |  </div>
    |   <div id="pagebody">
    |    <div id="pagebody-inner" class="pkg">
    |      <div id="alpha">
    |        <div id="alpha-inner" class="pkg">    
    |         <h2 class="date-header">#{str_dt}</h2>
    |         <div class="entry" id="entry-43937939">
    |           <h3 class="entry-header">#{h1_str}</h3>
    |           <div class="entry-content">
    |             <div class="entry-body">
    EOF
    file_ou.puts sbody
    file_ou.puts <<-EOF.gsub(/^[\s\t]+\|/, '') 
    |             </div>
    |           </div>
    |         </div>
    |       </div>
    |     </div>
    |   </div>
    | </div>
    |</div>
    |</body>
    |<!-- head内だと動作しないみたい。公式だとこの位置に書いてあるのでここに記述 -->
    |<!-- Syntax Highlighter -->
    |<script type="text/javascript" src="./local/syntax/scripts/shCore.js"></script>
    |<script type="text/javascript" src="./local/syntax/scripts/shAutoloader.js"></script>
    |<link type="text/css" rel="stylesheet" href="./local/syntax/styles/shCore.css"/>
    |<link type="text/css" rel="stylesheet" href="./local/syntax/styles/shThemeFadeToGrey.css"/>
    |<script type="text/javascript">
    |<!-- configulation Autoloadの前でないと駄目? -->
    |SyntaxHighlighter.defaults['toolbar'] = false;     <!--お好みで-->
    |SyntaxHighlighter.defaults['auto-links'] = false;  <!--お好みで-->
    |SyntaxHighlighter.config.tagName = "code";
    |
    |function path()
    |{
    |  var args = arguments,
    |      result = []
    |      ;
    |       
    |  for(var i = 0; i < args.length; i++)
    |      result.push(args[i].replace('@', './local/syntax/scripts/'));
    |       
    |  return result
    |};
    |SyntaxHighlighter.autoloader.apply(null, path(
    |  'applescript            @shBrushAppleScript.js',
    |  'actionscript3 as3      @shBrushAS3.js',
    |  'bash shell             @shBrushBash.js',
    |  'coldfusion cf          @shBrushColdFusion.js',
    |  'cpp c                  @shBrushCpp.js',
    |  'c# c-sharp csharp      @shBrushCSharp.js',
    |  'css                    @shBrushCss.js',
    |  'delphi pascal          @shBrushDelphi.js',
    |  'diff patch pas         @shBrushDiff.js',
    |  'erl erlang             @shBrushErlang.js',
    |  'groovy                 @shBrushGroovy.js',
    |  'java                   @shBrushJava.js',
    |  'jfx javafx             @shBrushJavaFX.js',
    |  'js jscript javascript  @shBrushJScript.js',
    |  'perl pl                @shBrushPerl.js',
    |  'php                    @shBrushPhp.js',
    |  'text plain             @shBrushPlain.js',
    |  'py python              @shBrushPython.js',
    |  'ruby rails ror rb      @shBrushRuby.js',
    |  'sass scss              @shBrushSass.js',
    |  'scala                  @shBrushScala.js',
    |  'sql                    @shBrushSql.js',
    |  'vb vbnet               @shBrushVb.js',
    |  'xml xhtml xslt html    @shBrushXml.js'
    |));
    |SyntaxHighlighter.all();
    |</script>
    |</html>
    EOF
  }
end

def mkd2htmlfile(inputname, outputname)
  sbody = nil
  
  File.open(inputname, "r") { |file_in|
    sbody = mkd2htmlbody(file_in.read)
  }
  #output!
  #outputname = 1 if (outputname == nil)
  File.open(outputname, "w") { |file_ou|
    file_ou.puts <<-EOF.gsub(/^[\s\t]+\|/, '') 
    |<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"  
    |     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    |<html>
    |<head>
    |<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    |<title>#{@option_hash[:title]}(preview)</title>
    |</head>
    |<body>
    EOF
    file_ou.puts sbody
    file_ou.puts <<-EOF.gsub(/^[\s\t]+\|/, '') 
    |</body>
    |</html>
    EOF
  }
end

##### main ######
OptionParser.new{|opt|
  opt.on('-f','--full', "make full html file") {|v| @option_hash[:full] = v}
  opt.on('-w VAL', '--tabwidth VAL', /[0-9]+/, "<pre>tag expand tabwidth(default:4)"){|v| @option_hash[:tabwidth] = v}
  opt.on('-d VAL', '--date_fmt VAL', "Date.strftime Format String"){|v| 
    @option_hash[:date_fmt] = v
    @option_hash[:date_fmt].encode!("UTF-8")
    }
  opt.on('--[no-]blog', "blog mode(default:true)"){|v| @option_hash[:blogmode] = v}
  opt.on('-t VAL', '--title VAL', "HTML&blog's title"){|v| 
    @option_hash[:title] = v
    @option_hash[:title].encode!("UTF-8")
    }
  opt.on('-s VAL', '--sub-title VAL', "blog's sub-title"){|v| 
    @option_hash[:subtitle] = v
    @option_hash[:subtitle].encode!("UTF-8")
    }
  opt.parse!(ARGV)
}
#p @option_hash

if (@option_hash[:full])
  if (@option_hash[:blogmode])
    mkd2bloghtmlfile(ARGV[0],ARGV[1])
  else
    mkd2htmlfile(ARGV[0],ARGV[1])
  end  
else
  mkd2htmlbodyfile(ARGV[0], ARGV[1])
end

通常、107~128行目辺りを自分の環境に合うように調整すればOKだと思う。
必要なら132~192行目辺りも修正して下さい(特にSyntax Highlighterの相対パスが違う場合)。

自己満足の世界なんでどこまでやるかはご自由に。ブログのウィジェットとかまでは埋め込んでません。

$ ruby mkd2html -f -t "nukinoの日記" -s "パソコン関係の備忘録" 111218.mkd 111218.html

みたいな感じで実行してやると以下のようなプレビュー画面になります

プレビュー画面1

テーマ:Morgan Noirの場合のスタイルシートファイルと必要な画像ファイルをローカルに入れておくと以下のような感じ

プレビュー画面2

スクリプトの説明

ファイル名から日付の取得(80~100行目)

渡されたMarkdownファイルのファイル名によって日付を取得します。

現在は見出しにしか使われてませんが、イメージのパス書くの面倒くさくなってきたので、スクリプト内で置換する機能をつくる際に使用予定です。

先頭から何文字が数字かによって意味が異なります。

現在の日付が2011/12/18だとすると、以下のようなルールになります

  1. 入力ファイル名の先頭2文字が数字(13_hoge.mkdなど)
    年=現在、月=現在、日=13と評価(2011/12/13)
  2. 入力ファイル名の先頭4文字が数字(1113_hoge.mkdなど)
    年=現在、月=11、日=13と評価(2011/11/13)
  3. 入力ファイル名の先頭6文字が数字(121113_hoge.mkdなど)
    年(下2桁)=12、月=11、日=13と評価(2012/11/13)
  4. 入力ファイル名の先頭8文字が数字(20121113_hoge.mkdなど)
    年=2012、月=11、日=13と評価(2012/11/13)
5.それ以外
年=現在、月=現在、日=現在と評価(2011/12/18)
Syntax Highlighter用のJavaScript定義について

<head>内に記述すると動かなかったので、</body>の後にまとめて書いてあります。(Syntax Highlightr公式のページソースがそうなってたので)

<script type="text/javascript" src="~">みたいにソースコード埋め込みの部分は<head>内に書くのが流儀なのかもしれませんが、その辺専門じゃないんでよく分からなかったりします(^^ゞ

あと昨日の導入例の際には言及しませんでしたが、configurationはAutoloadの前でないと動きませんでした(Syntax Highlighter 3.0.83)。仕様かバグかは分かりません。

スクリプトの使い方

基本的には昨日のと同じ

$ ruby mkd2html [option] input_mkd_file output_html_file

こんな感じで実行します。
入力ファイルは規定の外部エンコーディングEncoding.default_externalのファイルを指定します。Windows XPなら’Windows-31J’(cp932、Shift-JIS)で書いたファイルを渡します。出力のHTMLファイルはUTF-8エンコーディングで出力されます。

オプションの説明
-f、–full
<HTML><HEAD><BODY>タグなどを付加した本当のHTMLファイルを作ります。
-w VAL、–tabwidth VAL
<pre>タグ内のタブをスペースに変換する際のタブ幅を指定します。
例:-w 4
-d VAL、–date_fmt VAL
ファイル名から日付取得した日付を見出し文字列にする際のフォーマット文字列を指定します。rubyドキュメントのDate#strftime参照。
‘#{xxx}`と書くと式展開される(多分)。規定の外部エンコーディング文字列で指定する
例:-d “%Y年%m月%d日(#{%w(日 月 火 水 木 金 土)[dt.wday]})”
–[no-]blog
option  
–blog -fが指定されてるとき、ブログ表示に近づける完全なHTMLファイルを作成します
  -fが指定されてないとき、ブログアップ用表記(見出しシフトなど)で作成します
–no-blog kramdown変換だけ行います(<pre>タグ内のタブ→スペース変換は行う)
-t VAL、–title VAL
ブログタイトルを指定する。規定の外部エンコーディング文字列で指定する 例:-t “Archive of nukino”
-s VAL、–sub-title VAL
ブログサブタイトルを指定する。規定の外部エンコーディング文字列で指定する 例:-s “パソコン関係の備忘録”
使い方のヒント

私は今のところこんなバッチファイルを作って運用してます

ruby mkd2html.rb -t "Archive of  nukino" -s "パソコン、プログラム関係の備忘録"  %1 %2 %3
if %ERRORLEVEL%==0 goto SUCCESS
PAUSE
EXIT
:SUCCESS
if "%1"=="-f" GOTO FULL
notepad "%2"
EXIT
:FULL

1.作業中プレビューしたいとき

$ mkd2html.bat -f 111218.mkd 111218.html

とするとfirefoxでプレビュー出来ます。

2.ブログにアップするときは

$ mkd2html.bat 111218.mkd 111218.html

とすると、notepad が起動するのでコピペします。ちなみに一番最後に記事タイトル(除去されたH1見出し)がくっついてるので、これを記事タイトルの方へコピペします。

vim使いの場合、Markdownファイルがカレントバッファの状態で

:!start mkd2html.bat % %:t:r.html
:!start mkd2html.bat -f % %:t:r.html

とかするとファイル名打たなくて済むんで便利です。

quickrun.vimとか導入するともっと便利になるのかもしれません。

Internet Explorer使用時の注意

Internet ExplorerでHTMLファイルを閲覧しようとすると

フォルダイメージ

みたいな表示が出て鬱陶しい!という人は

  • Internet Explorer以外のブラウザを使う(ぉぃ

または

  • [ツール インターネットオプション]の詳細設定ページの
    • セキュリティ → マイコンピュータでのCDのアクティブ コンテンツの実行を許可する(不要かも)
    • セキュリティ → マイコンピュータのファイルでのアクティブコンテンツの実行を許可する

    の2つにチェックを入れる
    フォルダイメージ

のどれかで対策して下さい。


今日はここまで。あとはイメージ(画像)系をもう少しだけ便利にする予定。

2011年12月17日 (土)

eoblogをMarkdown記法で書く2(SyntaxHighlighter導入)

昨日の記事(eoblogをMarkdown記法で書く)の続き。

見た目的な問題からソースコードを構文強調表示してくれるスクリプトの導入を行います。またkramdownと組み合わせたときの問題点を修正してます。

構文強調表示JavaScript

ソースコードを載せるとき、普通に<pre>タグだけではこんな感じになってしまい、味気ないです(カスタムCSS使用して枠は出してるけど)。

OptionParser.new{|opt|
  opt.on('-f','--full') {|v| @option_hash[:full] = v}        # オプション引数 なし
  opt.parse!(ARGV)
}
p @option_hash

調べてみると、構文強調表示するJavaScriptがいくつかあるみたい。

  1. Markdown記法(kramdown)で作成するので、<pre>または<code>タグで装飾されてる
  2. Ruby、JavaScript、C/C++、できればHTMLの構文強調ができる
  3. 行番号が表示できる。開始の行番号が指定できるのが望ましい
  4. <ol><li>で行番号実現してるのは嫌(HTMLソースが汚い。コピペしにくい)
  5. ソースコードの折り畳み(出来れば任意位置)ができるのがいい

以上の条件で探してみた結果Syntax Highlighterが良さげなので、これを導入してみます(任意位置の折り畳みは出来ないけど)なお今回導入したのは 3.0.83 です。バージョンが違ってたりすると導入方法が微妙に異なります。また同じSyntax Highlighterという名前の別のJava Scriptなんかもあったりして、調べててかなり混乱しました(^^ゞ

Syntax Highlighter ダウンロードとサーバへのアップロード

Syntax Highlighterのダウンロードページからダウンロードして、解凍します。

解凍画面

解凍すると、上のようなフォルダやファイルができます。重要なのは

scripts
各言語の構文定義JavaScriptや本体JavaScriptが入ったフォルダ
styles
デザインを決めるCSSファイルの入ったフォルダ

の2つ。とりあえず何も考えずにこの2つのフォルダを適当な場所にアップロードします。

eoblogの場合、コントロールパネル→ファイルで操作します(下図参照)

ファイル操作

イメージを見れば分かりますが、私はホーム/default/syntaxを作成し、その下にscripts、stylesフォルダを作成してそれぞれのファイルをアップしました。

HTMLコードの記述

次にブログ上で使えるようにするためカスタムHTMLコードを記述します。
eoblogの場合、「ブログ」→「デザイン」→「レイアウトを変更」を選択し、「モジュールの追加」をクリックします。

モジュール追加

そして、HTMLコードを記述する(モジュールの名前は何でもいい)んですが、記述の方法は2通りあります。

1.直接使用する言語のJavaScriptを埋め込む(ver 2.x方式)

<script type="text/javascript" src="$YourUploadPath$/scripts/shCore.js"></script>
<!--使用する言語のjsファイルを記述 start-->
<script type="text/javascript" src="$YourUploadPath$/scripts/shBrushJScript.js"></script>
<script type="text/javascript" src="$YourUploadPath$/scripts/shBrushRuby.js"></script>
<script type="text/javascript" src="$YourUploadPath$/scripts/shBrushXml.js"></script>
<script type="text/javascript" src="$YourUploadPath$/scripts/shBrushCpp.js"></script>
<!--使用する言語のjsファイルを記述 end-->
<link type="text/css" rel="stylesheet" href="$YourUploadPath$/styles/shCore.css"/>
<!--使用するCSSファイル(stylesフォルダのshTheme~のどれか)を指定 -->
<link type="text/css" rel="stylesheet" href="$YourUploadPath$/styles/shThemeFadeToGrey.css"/>
<script type="text/javascript">
<!--SyntaxHighlighter.config.tagName = "code";     「スクリプトの作成」参照-->
SyntaxHighlighter.defaults['toolbar'] = false;     <!--お好みで-->
SyntaxHighlighter.defaults['auto-links'] = false;  <!--お好みで-->
SyntaxHighlighter.all();                           <!--必ず必要-->
</script>

言語とJavaScriptの関係についてはSyntax Highlighter(公式)のSyntaxesを参照して下さい。(ファイル名でだいたい分かりますが・・・。)

2.Autoloaderを使用する(ver 3.x方式)

<script type="text/javascript" src="$YourUploadPath$/syntax/scripts/shCore.js"></script>
<script type="text/javascript" src="$YourUploadPath$/syntax/scripts/shAutoloader.js"></script>
<link type="text/css" rel="stylesheet" href="$YourUploadPath$/syntax/styles/shCore.css"/>
<!--使用するCSSファイル(stylesフォルダのshTheme~のどれか)を指定 -->
<link type="text/css" rel="stylesheet" href="$YourUploadPath$/syntax/styles/shThemeFadeToGrey.css"/>
<script type="text/javascript">
<!--SyntaxHighlighter.config.tagName = "code";     「スクリプトの作成」参照-->
SyntaxHighlighter.defaults['toolbar'] = false;     <!--お好みで-->
SyntaxHighlighter.defaults['auto-links'] = false;  <!--お好みで-->

function path()
{
  var args = arguments,
      result = []
      ;
       
  for(var i = 0; i < args.length; i++)
      result.push(args[i].replace('@', '$YourUploadPath$/syntax/scripts/'));
       
  return result
};
SyntaxHighlighter.autoloader.apply(null, path(
  'applescript            @shBrushAppleScript.js',
  'actionscript3 as3      @shBrushAS3.js',
  'bash shell             @shBrushBash.js',
  'coldfusion cf          @shBrushColdFusion.js',
  'cpp c                  @shBrushCpp.js',
  'c# c-sharp csharp      @shBrushCSharp.js',
  'css                    @shBrushCss.js',
  'delphi pascal          @shBrushDelphi.js',
  'diff patch pas         @shBrushDiff.js',
  'erl erlang             @shBrushErlang.js',
  'groovy                 @shBrushGroovy.js',
  'java                   @shBrushJava.js',
  'jfx javafx             @shBrushJavaFX.js',
  'js jscript javascript  @shBrushJScript.js',
  'perl pl                @shBrushPerl.js',
  'php                    @shBrushPhp.js',
  'text plain             @shBrushPlain.js',
  'py python              @shBrushPython.js',
  'ruby rails ror rb      @shBrushRuby.js',
  'sass scss              @shBrushSass.js',
  'scala                  @shBrushScala.js',
  'sql                    @shBrushSql.js',
  'vb vbnet               @shBrushVb.js',
  'xml xhtml xslt html    @shBrushXml.js'
));
SyntaxHighlighter.all();                           <!--必ず必要-->
</script>

$YourUploadPath$はscriptsやstylesをアップしたURL(http://~)で置換して下さい
ブログサービスに依るかもしれませんが、相対パス表記は駄目です。

どちらを使用しても大丈夫ですが、「Autoloaderを使用する」方はページ内で使用してる言語定義JavaScriptを動的にロードするみたいなんでお奨めです(表示が速い・・・のかな?)。なお<head>部分しかいじれないブログサービスの場合はAutoloaderを使用しない従来方式の方がいいような気がします(後述しますが、プレビュー時にもSyntax Highlighter使用しようとして<head>の最後の部分に上記コードを入れてたら上手く動かなかったので)。

書き方

<pre class="brush: ruby">
OptionParser.new{|opt|
  opt.on('-f','--full') {|v| @option_hash[:full] = v}        # オプション引数 なし
  opt.parse!(ARGV)
}
p @option_hash
</pre>

のように書けば

OptionParser.new{|opt|
  opt.on('-f','--full') {|v| @option_hash[:full] = v}        # オプション引数 なし
  opt.parse!(ARGV)
}
p @option_hash

のように表示されます1

また、タイトルを付けることも可能です。

<pre class="brush: ruby" title="sample">
OptionParser.new{|opt|
  opt.on('-f','--full') {|v| @option_hash[:full] = v}        # オプション引数 なし
  opt.parse!(ARGV)
}
p @option_hash
</pre>

表示はこんな感じ。

OptionParser.new{|opt|
  opt.on('-f','--full') {|v| @option_hash[:full] = v}        # オプション引数 なし
  opt.parse!(ARGV)
}
p @option_hash

その他指定できるパラメータの詳細については Syntax Highlighter公式のconfiguration を参照して下さい。

日本語のページなら SyntaxHighlighter - ソースコードを見やすく表示させるJavaScript(3) などが詳しいです(ver 2.x系の説明ですが、この辺りは恐らく同じです)

Markdown記法の+kramdown拡張ではブロックレベル属性の場合、{: xxx=yyy}をブロックの前に記述すると属性を指定できるので

{: class="brush:ruby"}
~~~
OptionParser.new{|opt|
  opt.on('-f','--full') {|v| @option_hash[:full] = v}        # オプション引数 なし
  opt.parse!(ARGV)
}
p @option_hash
~~~

もしくは

{: class="brush:ruby"}
    OptionParser.new{|opt|
      opt.on('-f','--full') {|v| @option_hash[:full] = v}        # オプション引数 なし
      opt.parse!(ARGV)
    }
    p @option_hash

と書けばOKだと思ってたんですが、これだと<code></code>が表示されます。

<code>
OptionParser.new{|opt|
  opt.on('-f','--full') {|v| @option_hash[:full] = v}        # オプション引数 なし
  opt.parse!(ARGV)
}
p @option_hash
</code>

こんな感じ2。毎回置換するのも鬱陶しいので、スクリプトを作って対策します。

スクリプトの作成

で、対策案ですが

  1. <pre class="brush:~">という表記の時、 <code></code>を除去する

  2. <pre class="brush:~">という表記の時、属性を<code>タグの方へ移す。

のいずれかが考えられます。HTMLマークアップ的には「2.」の方が本道のような気がするので、こちらで行きます。
(Syntax Highlighterでは構文強調を動作させるタグを変更することが可能ですし)

まずSyntax Highlighterの設定を変更します。

「直接使用する言語のJavaScriptを埋め込む(ver 2.x方式)」場合は

SyntaxHighlighter.config.tagName = "code";         <!--追加-->

のように12行目のコメントを外す。「Autoloaderを使用する(ver 3.x方式)」場合は

SyntaxHighlighter.config.tagName = "code";         <!--追加-->

のように7行目のコメントを外して、サーバーに変更を保存(&反映)します。

んで、rubyスクリプトは以下のような感じ。
お手軽なんで、変換・置換に正規表現使いまくってるけどいいのかな?(遅そう)

# -*- coding: utf-8 -*-
#require 'rubygems'
require 'kramdown'
require 'optparse'

@option_hash = {
  :tabwidth => 4,
  :blogmode => true,
  }

#markdown記法をHTML(body)に変換する
#返り値:body文字列
def mkd2htmlbody(text)
  text.encode!("UTF-8")
  #text = text.lines.to_a[1..-1].join # Just ignore the first line
  body = Kramdown::Document.new(text).to_html
  if ( @option_hash[:blogmode] )
    if (false)
      # <pre class="brush~><code>~</code></pre>
      # の場合、<code>、</code>が邪魔なので取り除く
      body = body.gsub(\
          %r!<pre[\s\t]+([^<>]*?)class[\s\t]*="brush(.*?)>[\s\t\r\n]*<code>(.+?)</code>[\s\t\r\n]*</pre>!mi,\
          '<pre \1class="brush\2>\3</pre>')
    else
      # <pre class="brush~><code>~</code></pre>を
      # <pre><code class="brush~>~</code></pre>に変更
      body = body.gsub(\
          %r!<pre[\s\t]+([^<>]*?)class[\s\t]*="brush(.+?)>[\s\t\r\n]*<code>(.+?)</code>[\s\t\r\n]*</pre>!mi,\
          '<pre><code \1class="brush\2>\3</code></pre>')
    end
  end
  #<pre>~</pre>内のTABを取り除く。タブ幅はtabwidthで与える
  body = body.split(/\n/).map {|line|
    if (/<pre/ === line) .. (/<\/pre>/ === line) 
      line.gsub(/\t/, "\s"*@option_hash[:tabwidth])
    else
      line
    end
  }.join "\n"
  str_h1 = nil
  # H1→消去 H2→H4 H3→H5 H4→H6に変更する
  if ( @option_hash[:blogmode] )
    #H1消去
    body = body.gsub(%r!<h1.*?>(.+?)</h1>!i, "")
    str_h1 = $1
    #H4→H6、H3→H5、H2→H4
    for i in [4,3,2]
      body = body.gsub(/<h#{i}(.+?)<\/h#{i}>/,"<h#{i+2}\\1<\/h#{i+2}>")
    end
  end
  return body, str_h1
end

def mkd2htmlbodyfile(inputname, outputname)
  sbody = nil
  File.open(inputname, "r") { |file_in|
    sbody, = mkd2htmlbody(file_in.read)
  }
  File.open(outputname, "w") { |file_ou|
    file_ou.puts sbody
  }
end

##### main ######
OptionParser.new{|opt|
  opt.on('-w VAL', '--tabwidth VAL', /[0-9]+/, "<pre>tag expand tabwidth(default:4)"){|v| @option_hash[:tabwidth] = v}
  opt.on('--[no-]blog', "blog mode(default:true)"){|v| @option_hash[:blogmode] = v}
  opt.parse!(ARGV)
}

mkd2htmlbodyfile(ARGV[0], ARGV[1])

参考
Ruby 1.9.2 リファレンスマニュアル
逆引きRuby
ujihisa/mdv - github(kramdownを使ったrubyコード)
<code></code>を除去したい場合

Syntax HighlighterのtagNameを変更したくない場合(対策案1)はmkd2html.rbの18行目をfalse→trueとすれば動くと思います。

生成されたタグをいじるのはなんとなく後ろめたい&将来的になんか困るかもしれないので、変換する/しないを切り替えられるコマンドラインオプションをつけてあります。

<pre>タグ内のTABをスペースに

ソースコードにTABが入ってるとコピペしたときにタブ幅の設定によってはインデントがずれてしまうので、スクリプト内で変換するようにしてみました。

rubyの「条件式としての範囲式」て便利ですね。githubから取ってきたサンプルコード見てて、「何これ?」と思ってたんですが、理解できるとちょっと感動。C++とかだと

void tab2space_inpre( vector<string> &lines. int tab_width )
{
    bool is_pre_flg = false;
    for ( vector<string>::iterator p = lines.begin(); p != lines.end(); p++ )
    {
        string& line = *p;
        if ( /*lineの中に<pre がみつかった*/ )
        {
            /*lineの中のTABをSPACEに変換*/
            if ( /*lineの中に</pre>が見つからなかった*/ ) is_pre_flg = true;
        }
        else if ( is_pre_flg )
        {
            /*lineの中のTABをSPACEに変換*/
            if ( /*lineの中に</pre>が見つかった*/ ) is_pre_flg = false;
        }
    }
}

みたいにどうしても状態遷移フラグが必要なんだけど、これが必要ないし、コードもすっきりする。

あとmkd2html.rbの33行目を

if (/<pre/ =~ line) .. (/<\/pre>/ =~ line) 

としても動くのが、最初理解できなかった。’RegExp =~ String’はマッチしたらマッチしたインデックス。マッチしなかったらnilを返すんですが、「先頭で見つかったら(0が返ってきたら)動かないじゃん」と思ってたけどrubyではif(0)if(true)と評価されるんですね( ・_・;)
早めに気付いてよかった。いつかどこかで大嵌まりするところでした。

見出しレベルの変換

mkd2html.rbの42行目~50行目で見出しレベルのシフトを行ってます。

eoblogでは

H1
ブログタイトル
H2
ブログ副題と日付
H3
記事タイトル

となっていて、ブログ内で使用するのはH4~が自然な感じです。ということでローカルで編集するMarkdownファイル自体は

eoblogをMarkdown記法で書く2(SyntaxHighlighter導入)
====
本文1

スクリプトの作成
----
本文2

### 見出しレベルの変換
本文3

みたいにH1から書いて、アップする際には、H1を除去、H2→H4、H3→H5とかって変換するようにしてみました。
今回は除去したH1ですが、HTMLプレビューの際には使用します。

必ず見出しレベルの変換すると将来的に困るかもしれないんで、一応コマンドライン引数オプションで変更できるようにしてあります。(デフォルトは変換)

使い方
$ ruby mkd2html [option] input_mkd_file output_html_file

こんな感じで実行します。
入力ファイルは規定の外部エンコーディングEncoding.default_externalのファイルを指定します。Windows XPなら’Windows-31J’(cp932、Shift-JIS)で書いたファイルを渡します。出力のHTMLファイルはUTF-8エンコーディングで出力されます。

長くなってきたんで、今日はこれで終了。 続く・・・と思う。


  1. 当ページではpreタグではなく、codeタグで装飾してるので、実際のHTMLソースは説明と異なります。

  2. あくまで見た目ね。既に対策済みなので、実際のHTMLソースは説明と異なります

続きを読む »

2011年12月14日 (水)

Ruby1.9.2&NetBeans7インストール

Rubyをお勉強してみようと、Ruby1.9.2とNetBeans7.0をインストールしたので、それのメモ。
環境はWindowsXP(SP3)です。

Rubyのインストール

Ruby Installer Downloads
のRuby Installerから"Ruby 1.9.2-p290.exe"をダウンロードして実行(Installer形式のモノはgemが予め入っていて便利)。

インストールは途中

Ws000002_2

PATHを通すチェックボックスをつけておく位で特に注意することはない(.rb、.rbwの関連づけはご自由に)。

インストール終わったらPATH環境変数の変更を有効にするためにログオフ→ログイン。もしくは再起動します。

DevKitのインストール

次はNetBeansをインストール・・・してもいいんですが、
NetBeansでrubyデバッグ(Fast Debugger)を行うためにはruby-debug-ideが必要。
ruby-debug-ideをインストールするためにはruby-debug-baseが必要。
ruby-debug-baseをインストールするためにはlinecacheが必要。
linecacheをインストールするためにはDevKit(ていうかビルド環境)が必要。

・・・ということで、DevKitをインストールします。

DevKitもRuby Installer Downloadsからダウンロードして実行します。解凍先フォルダは適当でOK。(私はC:\Ruby192\Devkitにインストールしました)

ここからがややこしいんですが、
WindowsでRuby1.9.2+Rails3+NetBeans6.9.1+Debugger。
を参考に・・・。

  1. コマンドプロンプトを開いて、先ほどDevKitを展開したフォルダに移動
  2. ruby dk.rb init を実行
  3. ruby dk.rb reviewを実行し、rubyインストールフォルダ(C:\Ruby192)が表示されるかを確認(違ってる場合、DevKitを展開したフォルダのconfig.ymlを編集)
  4. ruby dk.rb installを実行(Installing 'C:/Ruby192/lib/ruby/site_ruby/devkit.rb'とかって表示が出れば成功)
  5. gem install rdiscount --platform=ruby を実行してインストール

これで終了らしいです。

FastDebuggerのインストール

先ほど参考にしたサイトではruby-debug19をインストールする例が載ってましたが、ruby-debug-base19をインストールしてみたら出来たので、これで行きます。

コマンドラインプロンプトを開いて

$ gem install linecache19
$ gem install ruby-debug-base19
$ gem install ruby-debug-ide19

の順で実行すればOK。今回のrubyは1.9.2で1.9系列なので、全て末尾に"19"が付いてます。ちなみに今回インストールしたバージョンは

  • linecache19-0.5.12
  • ruby-debug-base19-0.11.25
  • ruby-debug-ide19-0.4.12

です。

補足

なお、最初ruby1.9.3-p0をインストールしてたんですが、ruby-debug-base19のインストールでrb_iseq_compile_with_option()の宣言が以前と違う・・・みたいなメッセージが出てビルドが失敗します。何となくC:\Ruby192\lib\ruby\gems\1.9.1\gems\ruby-debug-base19-0.11.25\ext\ruby_debug\ruby_debug..cの#ifdef RB_ISEQ_COMPILE_6ARGSの所に入ってないんだろーな―。と予想しますが、修正して無理矢理インストールする方法が私には分からなかったんで、ruby1.9.2をインストールしてみました。

が、ruby1.9.2のDevKitが生成したヘッダファイルを検索しても該当する関数が見つからない。なにがどうなってるのかさっぱり分かりませんが、とりあえず動いてるっぽいからいいや。

NetBeans7.0のインストール

NetBeans IDE ダウンロード
から、ダウンロードします。7.0になってからruby専用パッケージがなくなってるみたいなので、私はC/C++を選択しました。

ダウンロードが終わったら、インストール。特に注意することはないと思う。

インストールが終わったらNetBeans7.0を起動。[ツール|プラグイン]を選択し、利用可能なプラグインからrubyにチェックを入れて、インストール。

Ws000001

あとはNetBeansを再起動すればOK。

せっかくだからデバッグできるかどうか確認。

[ファイル|新規プロジェクト]で"Rubyアプリケーション"を選択して、適当な名前でプロジェクトを作成。

Ws000003

適当にブレークポイント貼って、[デバッグ|プロジェクト(???)のデバッグ]を選択して、サンプルプロジェクトがブレークされたら成功です。

独り言

私がやりたいことに比べると、NetBeansってなんかこうオーバースペックなんだよね。ソースはエディタで編集する予定なので、要はGUIデバッガさえ付いてればいい(コマンドラインデバッグは嫌だ!)んだけど、そういうツールがない(1つ見つけたけど最近リリースされてないし、インストールも面倒くさそう)。debug.rbとかをGUIで蓋してくれてるだけのツールってないのかな?

最近のコメント