Code Copied

基于highlight.js的WLW插件制作

了解highlight.js并使用

关于highlight.js

highlight.js是一个能够给代码着色的js插件。项目地址:http://highlightjs.org/
它有以下几个非常不错的优点:

  1. 能够为71种编程语言进行语法高亮,并且包含了44种样式。
  2. 该优点基本涵盖了所有主流的编程语言。
  3. 自动检测语言机制
  4. 能够高亮混合代码
  5. 在node.js中也能使用
  6. 能够在任何标记中运行
  7. 兼容所有的js框架

当然网络上还有其他很多代码高亮的js插件,以前用过最多的是SynataxHighlighter
但是看了上述highlight.js的各项优点后,你会发现仅凭第一个 优点highlight.js就能完爆SynataxHighlighter

关于highlight.js的使用

了解如何使用highlight.js有两种途径:

参照hightlight.js的demo/test页面:http://highlightjs.org/static/test.html

页面是纯静态的,想要获得代码并在本地运行是很容易的。

按照官方的Useage画面的指引操作:http://highlightjs.org/usage/

那么现在我就简单介绍下方法2的做法,代码如下:

<html>
<head>
	<link rel="stylesheet" href="http://yandex.st/highlightjs/8.0/styles/default.min.css">
	<style type="text/css">
		pre code{
			font-family:​Consolas;
		}
	</style>
	
	<script src="http://highlightjs.org/static/highlight.pack.js"></script>
	<script>hljs.initHighlightingOnLoad();</script>
</head>
<body>
<pre>
	<code class="cs">
	private void GetCookie()
	{
		HttpCookie cookie = Request.Cookies["comment"];
		if (cookie != null)
		{
			txtName.Text = Server.UrlDecode(cookie.Values["name"]);
			txtEmail.Text = cookie.Values["email"];
			ViewState["url"] = cookie.Values["url"];
			ViewState["country"] = cookie.Values["country"];
		}
	}
	</code>
</pre>
</body>
</html>

在引用了hightlight.js,并执行了hljs.initHighlightingOnLoad();这行代码后,highlight.js会将页面中所有<pre><code>…</pre></code>之间的内容应用高亮。
通过上面的示例代码,是否觉得highlight的使用竟然如此简单?接下 来我们来构思一下如何在WLW中应用highlight.js。

如何在WLW的插件中应用hightlight.js

无论您使用的是什么博客系统,最终每篇文章都会被解析为html,就像上面的示例代码一样。
那么我们的思路应该如下:

  1. 将highlight.js添加到博客系统的目录下。
  2. 在博客系统的模板页中添加对hightlight.js的引用
  3. 在WLW中插入代码时生成<pre><code>…</code></pre>标记。
    以上的第1、2两个步骤我以blogengine为例进行演示(wordpress也基本差不多)。
  4. 将highlight.js添加到博客系统的目录下。

以blogengine为例,在网站根目录下创建一个highlight目录,放入highlight.pack.js和代码样式css。
image 

修改主题模板页
你的博客系统中可能会有多个主题,当然你只需要当前所用的主题就可以了。
我在blogengine中使用了默认主题,所以修改的是Standard主题下的site.master文件。
image 

<!--禁用syntaxhighlighter-->
<!--<link href="~/editors/tiny_mce_3_5_8/plugins/syntaxhighlighter/styles/shCore.css" rel="stylesheet" type="text/css" />
<link href="~/editors/tiny_mce_3_5_8/plugins/syntaxhighlighter/styles/shThemeDefault.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="<%# ShRoot %>scripts/XRegExp.js"></script>
<script type="text/javascript" src="<%# ShRoot %>scripts/shCore.js"></script>
<script type="text/javascript" src="<%# ShRoot %>scripts/shAutoloader.js"></script>
<script type="text/javascript" src="<%# ShRoot %>shActivator.js"></script>--!>

<!--引用hightlight.js-->
<link rel="stylesheet" title="ir_black" href="/hightlight/styles/ir_black.css">
<script type="text/javascript" src="/hightlight/highlight.pack.js"></script>
<script>
    hljs.configure({ tabReplace: '    ' });
    hljs.initHighlightingOnLoad();
</script>
完成1,2两个步骤,现在就剩制作WLW插件这一步骤了。

制作Windows Live Writer插件

WIndows Live Writer制作步骤

请参照MSDN的链接:http://msdn.microsoft.com/en-us/library/aa702851

下面是实际的操作截图

1. 创建class library项目

2. 添加以下三个dll的引用:

  • WindowsLive.Writer.Api(插件api)
  • System.Windows.Form(用于显示代码窗口)
  • System.Web(调用HtmlEncode方法)
     image

3. 设定Post-build event和.Net Framework的版本

设定Post-build event能够在编译成功后将dll直接复制到Windows Live Write的Plugins目录下。
x86 →  XCOPY /D /Y /R "$(TargetPath)" "C:\Program Files\Windows Live\Writer\Plugins\"
x64 →  XCOPY /D /Y /R "$(TargetPath)" "C: \Program Files (x86)\Windows Live\Writer\Plugins"

image 
MSDN上明确说明了Windows Live Writer插件仅仅支持.NET 1.0/2.0版本的,否则无法加载插件。
由于我使用的是VS 2013/.NET 4.5,所以我这里选择了.NET 3.5(3.5也被认为是2.0)。
image 

4. 创建一个继承ContentSource的类SourceCodePlugin.cs

using System.Windows.Forms;
using WindowsLive.Writer.Api;

namespace SourceCodePlugin
{
    /// <summary>
    /// Main class for the plugin. It registers the plugin and overrides the <see cref="CreateContent"/> method.
    /// </summary>
    [WriterPlugin("E17FCDF7-1CD7-4FA3-9A91-8D303C903ABF", "Source code plug-in", 
        ImagePath = "Resources.code.png",
        Description = "SourceCodePlugin based on highloght.js", 
        PublisherUrl = "http://blog.64cm.com")]
    [InsertableContentSource("source code plugin v1.0")]
    public class SourceCodePlugin : ContentSource
    {
        /// <summary>
        /// Create content using an Insert dialog box.
        /// </summary>
        /// <param name="dialogOwner">Owner for any dialog boxes shown.</param>
        /// <param name="content">Newly created content. If there is an existing contiguous selection within the editor when CreateContent is called, 
        /// the parameter will contain the contents of the selection by default.</param>
        /// <returns><see cref="DialogResult.OK"/> if content was successfully created, <see cref="DialogResult.Cancel"/> if the user cancels the Insert dialog.</returns>
        public override DialogResult CreateContent(IWin32Window dialogOwner, ref string content)
        {
            DialogResult result = DialogResult.Cancel;

            using (CodeForm form = new CodeForm(content))
            {
                result = form.ShowDialog(dialogOwner);
                if (result == DialogResult.OK)
                {
                    content = form.Code;
                }
            }
            return result;
        }
    }
}

5. 创建代码窗体CodeForm

image 

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Windows.Forms;

namespace SourceCodePlugin
{
    public partial class CodeForm : Form
    {
        private string _code;
        private const string PrePrefix = "<pre><code class=\"";
        private const string PreSuffix = "\">";
        private const string PreClose = "</code></pre>";

        public string Code
        {
            get
            {
                var lang = ddlLanguage.Text;
                StringBuilder builder = new StringBuilder();
                builder.AppendFormat("{0}{1}{2}", PrePrefix, lang, PreSuffix);
                builder.Append(HttpUtility.HtmlEncode(_code));
                builder.Append(PreClose);

                return builder.ToString();
            }
            set
            {
                int preFrom = value.IndexOf(PrePrefix);
                int fromPos = value.IndexOf(PreSuffix);
                int toPos = value.LastIndexOf(PreClose);
                if ((0 <= preFrom) && (preFrom < fromPos) && (fromPos < toPos))
                {
                    preFrom += PrePrefix.Length;
                    string parameters = value.Substring(preFrom, fromPos - preFrom - 1);
                    fromPos += PreSuffix.Length;
                    _code = value.Substring(fromPos, toPos - fromPos);
                }
                else if (string.IsNullOrEmpty(value) == false)
                {
                    _code = value;
                }
                else
                {
                    _code = Clipboard.GetText();
                }
            }
        }

        /// <summary>
        /// 窗体初始化
        /// </summary>
        public CodeForm(string code)
        {
            Code = code;
            InitializeComponent();
            LoadLanguages();
        }

        /// <summary>
        /// OK按钮事件
        /// </summary>
        private void btnOK_Click(object sender, EventArgs e)
        {
            _code = FormatCode(textCode.Text);
            base.DialogResult = DialogResult.OK;
            base.Close();
        }

        /// <summary>
        /// Cancel按钮事件
        /// </summary>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            base.DialogResult = DialogResult.Cancel;
            base.Close();
        }


        /// <summary>
        /// 加载语言种类
        /// </summary>
        private void LoadLanguages()
        {
            ddlLanguage.Items.Clear();

            string[] languages = new[] {"cs", "xml", "sql", "java", "php", "cpp"};
            
            ddlLanguage.Items.AddRange(languages);
        }

        /// <summary>
        /// 当代码第一行有缩进时,将缩进去掉
        /// </summary>
        private string FormatCode(string code)
        {
            var sb = new StringBuilder();
            if (code.StartsWith("\t"))
            {
                string[] lines = code.Split(new[] { "\r\n" }, StringSplitOptions.None);
                foreach (var line in lines)
                {
                    sb.Append(line.Substring(1));
                    sb.Append("\r\n");
                }
                return sb.ToString();
            }
            else
            {
                return code;
            }
        }
    }
}

6. 编译并运行Windows Live Writer

在Visual Studio中编译时,请先不要开启Windows Live Writer。
因为我们之前设定了工程的Post-build event,它是指向Windows Live Writer的Plugins目录的。
如果在工程编译时Windows Live Writer处于启动状态,将导致dll文件写入Plugins目录失败。
编译成功后再运行Windows Live Writer,在Plugins目录下就能看到dll文件了。
image

2014-2-24 22-35-06

7. 使用插件测试代效果

image 
image
在Windows Live Writer中,所有代码都显示在一行上了。
分析一下我们生成的源代码,并在Windows Live Write中进行html代码调试,会发现是<code>节点导致所有代码显示在一行上了。
 2014-2-24 22-47-03
2014-2-24 22-46-34 

8. 改造插件,去掉<code>节点

上面的图片也显示了只要去掉<code>节点就能够将代码正常显示。
那么我们需要做以下两个步骤:
(1) 去除SourceCodePlugin中的<code>节点

private const string PrePrefix = "<pre class=\"";
private const string PreSuffix = "\">";
private const string PreClose = "</pre>";

(2) 修改highlight.js

var v=document.querySelectorAll("pre code");

将这一小段代码变成

var v=document.querySelectorAll("pre");

源代码下载

本文源码 (v1.0 beta):http://blog.64cm.com/source/WLWPlugin_64CM.zip