Wenbin's Blog

A Programmer's Blog

设置Emacs的中文输入法-ibus

2013-08-16

最近决定迁移到Emacs上,所以一直在折腾,不过总算在Emacs上找到了Sublime的感觉了。 Emacs24自带了插件管理系统,可以很方便地安装各种第3方插件。不过在安装输入法的时候,费了点功夫,本文记录这个过程。

起步

学习emacs,阅读自带的Tutorial绝对是第一步。

插件管理

由于license的原因,Emacs24官方的插件仓库的数量很少了。而现在社区里比较活跃的有下面几个: 官方的elpa, marmalade,和 melpa。 添加这些仓库,需要修改emacs的配置文件,一般是~/.emacs,或者~/.emacs.d/init.el, 在.emacs中添加如下代码:

(require 'package)
;; Add the original Emacs Lisp Package Archive
(add-to-list 'package-archives '("elpa" . "http://tromey.com/elpa/"))

;; Add the user-contributed repository
(add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/"))
(add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t)

(package-initialize)

保存好.emacs后,可以重启一下emacs,或者执行下面的命令使设置启作用:C-x C-e

起步好帮手

对于新手来说,要记住emacs下的那么多命令,还有快捷键是很困难的事情。于是有人就写了一个插件Starter Kit, 来帮助人们方便地输入各种命令,让你使用emacs再也没有负担了。在配置好插件库后,就可以方便地安装 Starter-kit了:

M-x package-refresh-contents    ;;更新本地索引
M-x package-list-packages       ;;可以在列出的包的里表中找到starter-kit,然后点开链接,再安装,或者执行下面的命令
M-x package-install
starter-kit

Starter-kit会做一些默认的配置,比如隐藏了菜单,工具栏等,但是你可以通过M-x后,输入任何命令来执行同样的功能。

项目管理

Emacs本身是不支持Project的,只能通过第3方插件来管理。这里推荐Graphene, 这个插件能把Emacs,配置的最像Sublime了。 screen 有了它在浏览project中的文件时就很方便了,你可以通过前面提到的命令来安装graphene。安装完成之后,在.emacs里添加下面的代码:

;;sublime liked project manager
(require 'graphene)

重启emacs后,然后M-x,输入project-persistant-create来创建一个项目, project-persistant-find来打开一个项目。切换到目录窗口C-c s,切换回代码窗口:C-x o, 不必担心记不住命令,安装了starter-kit,之后都能自动提示,命令补全。

中文输入法

Ubuntu自带的ibus拼音输入法,所以希望在emacs里也可以使用这个拼音输入法。所幸有人开发了ibus-el这个插件,根据文档上的说明配置一下.emacs,就可以了。 注意,系统可能要安装python-xlib库;系统的ibus已经能启动。下面是.emacs的配置:

;;C-空格,默认是用来设置mark的起始位置的,使用了ibus之后,就冲突了,需要给它重新指定一个快捷键。
(global-set-key [?\S- ] 'set-mark-command)
;;ibus
(add-to-list 'load-path "~/.emacs.d/elpa/ibus-el")
(require 'ibus)  
(add-hook 'after-init-hook 'ibus-mode-on)
(global-set-key (kbd "C-=") 'ibus-toggle) ;;这里既是绑定上面设置的C+=快捷键到ibus
;; Change cursor color depending on IBus status
(setq ibus-cursor-color '("red" "blue" "limegreen"))

其他

暂时就记这么多,都说emacs很好用,很难学,关键还是要坚持。后面再继续学习org,w3m,收发邮件等。

浏览器插件(Plugins)开发 - II

2013-04-24

前文中介绍了,浏览器插件实际上就是一个动态库(.dll/.so/.dylib)。那么这个动态库应该如何被加载,什么时候初始化自己,如何响应用户的操作,如何显示自己的内容等等,这些需要一个规范。而NPAPI就是这样的一套规范API。

NPAPI包含了一整套的API接口,它分为浏览器端的API接口(规定了可以被插件调用的API),和插件上的API接口(可以被浏览器调用的API)两部分。浏览器和插件通过调用特定的API来实现相互之间的交互。浏览器端的API都是以NPN_xxx 形式命名的,而插件端的API都是以NPP_xxx 形式命名的。插件上还有3个方法的命名不在这个规则中,这个3个方法是:

NP_Initialize
NP_Shutdown
NP_GetEntryPoint

这个3个方法是浏览器用来初始化插件,关闭插件的,因而在动态库中必须是可见的,所以应该放在extern "C"中。

extern "C" { NPError NP_Initialize(NPNetscapeFuncs *browserFuncs); NPError NP_GetEntryPoints(NPPluginFuncs *pluginFuncs); void NP_Shutdown(void); }

前文介绍了浏览器在检查到了一个插件对应的Tag后,会查找,加载插件,然后再初始化插件,创建插件实例。那么从code的角度,浏览器是如何做的呢?

注册Plugin

浏览器在启动的时候,会plugin的目录下,读取每个plugin的MIME类型,然后把他们的对应关系缓存起来。这个过程在不同的平台有不同的实现方式,这个过程也与NPAPI无关。

初始化Plugin

当浏览器需要为某个MIME创建一个plugin的实例时候,如果plugin还没有加载浏, 览器会调用Plugin的NP_Initialize 方法。 如果plugin已经加载了,那浏览器就直接去创建plugin的实例。

创建Plugin实例

浏览器会调用plugin的NPP_New 方法来创建一个Plugin实例, 浏览器会为页面上的每个MIME类型调用一次NPP_NEW来创建对应的插件实例, 所以如果同一个页面里也有可能有多个Plugin的实例会被创建。

Plugin的显示

创建plugin很多时候是因为浏览器无法显示我们的想要的内容,比如用flash来播放视频,动画。Plugin在创建的时候可以分为有窗口型和无窗口型,这两种类型的plugin在显示,绘制自己的时候差别很大。Plugin可以在NPP_NEW方法里通过API NPN_SetValue来设置自己的类型。 下面的代码片段是设置Plugin为无窗口类型:

NPN_Setvalue(plugin_Instance, NPPVpluginWindowBool, (void*)false);

窗口型(Window)

窗口型Plugin, 浏览器会为插件创建一个本地窗口,然后通调用NPP_SetWindow方法传给插件。插件自己负责自己窗口内的显示。窗口型的插件的好处是,插件可以拥有一个本地窗口,那么就可以像开发本地桌面应用一样, 它的缺点它会总是在当前页面的所有HTML标签的最上面,不能被其他标签覆盖,这样有些CSS效果就要失效了,比如即使设置了zindex也还是会被插件窗口覆盖。

无窗口型(Windowless)

无窗口型插件的绘制实际上就是离屏渲染的概念。 浏览器在调用NPP_SetWindow方法时给插件传了一个Drawable,插件只能在浏览器指定的Drawable上显示自己。不同平台这个Drawable不一样。Windows下是HDC,Linux下是X11的Drawable(Pixmap), Mac下是CoreGraphics的context。而具体的绘制过程,则还是有浏览器来控制,当浏览器知道插件需要被重新绘制了,它会调用插件端的API: NPP_HandleEvent,然插件绘制自己。 在API NPP_HandleEvent中浏览器会传递一个event对象:NPEvent,它可以表示不同的事件,包括paint。

当然插件也可以通知浏览器,我需要重绘了。这个时候插件可以调用浏览器的API:NPN_InvalidateRect。当插件调用了NPN_InvalidateRect之后,浏览器会在合适的时候(不是立即)调用插件的NPP_HandleEvent来重绘。虽然NPAPI也提供了一个NPN_ForceRedraw的API,但是很多浏览器没有实现这个API,可能是因为这个API的杀伤力太大了吧。

到现在为止,我们就可以用NPAPI创建一个插件,并且让浏览器能加载我们的插件,还能显示我们自己的内容。当然要实现一个想Flash Player这样的插件,还是有很多工作要做的。

例子

下面的链接提供了一个很好的例子: https://code.google.com/p/npapi-chrome-plugin-simple-http/

注意

在Windows下plugin的名字必须是np***.dll,否则浏览器不会注册你的plugin的。

参考

  1. https://developer.mozilla.org/en-US/docs/Gecko_Plugin_API_Reference/Plug-in_Basics
  2. https://developer.mozilla.org/en-US/docs/Gecko_Plugin_API_Reference/Plug-in_Development_Overview

浏览器插件(Plugins)开发 - I

2013-04-17

什么是浏览器插件

浏览器插件其实就是一个动态库,它被浏览器动态地加载。浏览器插件可以用来扩充浏览器的功能,比如:

  • 多媒体的表现能力,例如Flash Player插件用来在网页上播放视频
  • 保护用户信息安全,例如网上银行的插件,提供交易安全保证

在浏览器地址栏输入如下命令,可以查看你的浏览器都安装了哪些插件: Firefox下: about:plugins ,Chrome下: chrome://plugins

FireFox会把系统里的安装的Plugins全部列出来,而且还列出了插件对应的动态库文件名,版本,特别需要注意的是插件支持的MIME Type,这个浏览器决定加载某个插件的重要依据,后文会继续解释。

什么是NPAPI

NPAPI(Netscape Plugin Application Programming Interface )是Mozilla公司提出的一套开发浏览器插件的API接口。(世界上最早的浏览器就是Mozilla开发的Netscape,虽然它后来被IE给打败了)。现在大部分浏览器都兼容这个接口,例如Chrome,Safari,FireFox, Opera,因而如果基于NPAPI开发的插件可以跨浏览器,IE除外。当然微软也搞了一套自己的接口,那就是ActiveX。Google在Chrome中也另搞了一套API:Pepper API,当然Chrome还是兼容NPAPI的。

NPAPI是一套C/C++的开发接口。基于NPAPI开发的插件可以实现如下功能:

  • 注册插件能够处理的MIME类型
  • 在浏览器窗口内显示插件所要显示的内容
  • 收到和处理键盘/鼠标事件
  • 通过URL从网络获取数据
  • 使用URL的形式发布插件产生的数据
  • 提供网页Javascipt代码可以调用的API接口,让网页JS代码调用插件的C/C++代码;插件也可以通过调用Javascript代码来操作DOM

插件是如何被加载的

当浏览器启动的时候,它会去系统的固定的目录中搜索所有的动态库读取动态库资源中的关于MIME类型部分,同时把这个MIME类型和对应的动态库在内存中缓存起来。

当用户在浏览器中打开某个html页面时,如果它检测到了一个

或者标签, 浏览器会检查这个标签的type属性,根据type属性指定的MIME类型,去寻找对应的插件。 如果浏览器在缓存中到了这个MIME类型的注册信息(即这个MIME类型有对应的动态库), 那么浏览器再执行下面的动作:

  1. 浏览器检查对应的插件是否已经加载,如果没有加载,第2步;如果加载了,则第4步
  2. 浏览器加载动态库到内存
  3. 浏览器初始化动态库
  4. 浏览器创建一个新的插件实例

注意: 浏览器会为每个MIME标签创建一个插件实例,即使是在同一个网页里。

如果浏览器找不到这个MIME类型的注册信息,不同的浏览器会显示不同的错误信息。

插件的卸载

当用户关闭页面的时候,浏览器会销毁这个页面上对应的所有插件实例,如果这个插件的最后一个实例被销毁了,那么浏览器就会从内存中卸载掉这个插件。

网页中如何包含一个插件标签:

在网页中插入一个插件标签可以通过object或者embed:

插入Flash:

```

```

<embed src="audiplay.aiff" type="audio/x-aiff" hidden="false">

注意: 不可见的插件是不起作用的,浏览器也不会创建对应的插件实例。

插件的安装路径

通常浏览器会去下面的路径检查插件和插件的MIME类型:

Windows

  • Directory pointed to by MOZ_PLUGIN_PATH environment variable.
  • %APPDATA% \Mozilla\plugins
  • Application dir \plugins
  • Plug-ins within toolkit bundles.
  • Profile directory\plugins, where Profile directory is a user profile directory.
  • Directories pointed to by HKEY_CURRENT_USER\Software\MozillaPlugins\*\Path registry value, where * can be replaced by any name.
  • Directories pointed to by HKEY_LOCAL_MACHINE\Software\MozillaPlugins\*\Path registry value, where * can be replaced by any name.

Linux

  • Directory pointed to by MOZ_PLUGIN_PATH environment variable.
  • ~/.mozilla/plugins.
  • Application directory/plugins, where Application directory is a directory where the application is installed.
  • /usr/lib/mozilla/plugins (on 64-bit systems, /usr/lib64/mozilla/plugins is used instead).
  • Plug-ins within toolkit bundles.
  • Profile directory/plugins, where Profile directory is a user profile directory.

Mac

  • Application directory/plugins, where Application directory is a directory where the application is installed.
  • ~/Library/Internet Plug-Ins.
  • /Library/Internet Plug-Ins.
  • /System/Library/Frameworks/JavaVM.framework/Versions/Current/Resources.
  • Plug-ins within toolkit bundles.
  • Profile directory/plugins, where Profile directory is a user profile directory.

Node.js 介绍

2013-04-13

最近要在公司内部介绍一下Node.js, 顺便总结了一下自己的Node经验和教训。下面是使用的PPT, 为了看起来效果比较好请在新窗口浏览。 你也可以直接在本页查看,翻页: <-/PgUp, ->/PgDn

Scrum 简介

2013-04-09

Scrum简介

Scrum是Agile的一种方法,它是迭代的,增量型的开发流程。Scrum的开发迭代周期称为Sprint,一个Sprint一般为3-4周,并且相互衔接。Scrum有3种基本角色: Product Owner,Scrum Master,开发Team。Scrum有下面几个基本的活动: 准备product backlog, sprint 计划会议, 每日例会, sprint成果展示/评审,sprint 总结。然后开始一个sprint计划会议,准备下一个迭代。Scrum中的每项活动都有时间限制(time box),每项活动必须在规定的时间你完成。 scrum 通过不断地迭代的方式,产品功能不断地在每个sprint中被增加和完善。 每个sprint结束后,team可以提供一个可以发布的简易版的产品, 这个简易版产品包含了产品的核心功能和价值。这个简易版本会随着迭代的进行,功能不断地被增强和完善。

Scrum中的角色:

Product Owner 负责把从客户,市场得到的需求转化为一个根据重要性排列的需求列表--Product backlog。 Product Owner要求对产品的商业价值非常理解,能把最能体现这种价值的需求排在product backlog的最前面。这样能保证系统的核心功能被尽早地开发出来,可以尽早地进行测试,试用。 Product Owner可以是产品经理,或者最了解客户需求的人,他的工作成果是为产品开发提供product backlog,而且要给produc backlog中的每一项(每一个story)标记出重要性。 Scrum Master: Scrum Master的主要职责是为帮助scrum在团队顺利实施。Scrum Master 不是项目经理,他只是帮助team能顺利按照Scrum的流程运转, 协助team安排会议等。Scrum Master必须在一个sprint中防止team被外来的干扰或者突发的需求打扰, 这样开发团队可以在一个sprint的周期中集中精力完成sprint中的任务。 开发团队:就是具体做事,实现产品功能的人。Scrum 团队要求在每个sprint结束的时候提交他们在sprint开始的时候承诺的功能,这样就要求Scrum团队的成员有很强的责任心和自律性。

Scrum的开发流程和活动:

下面的这个图是Scrum中的流程图: Scrum Process

Scrum 启动: 准备product backlog

Scrum 启动的第一步是Product owner 展示产品的远景规划,然后把这些需求(story)列在product backlog中,然后按照这些需求的重要性对他们进行排序,把最重要的排在最前面。在Product Backlog列表中越底端的是更大和粗略的项目;当它们接近被开发时,产品所有者 (Product Owner)会添加更多的详细资料。 Product Backlog是由产品所有者 (Product Owner)随时更新,以反映客户需求的变化,新的想法和见识,竞争对手的发布,出现的技术障碍等等。团队提供给产品所有者 (Product Owner)在Product Backlog中每一项所需的相对工作的粗略估计,这可以帮助产品所有者 (Product Owner)作出优先权项的决策(一些项成为非优先权项,是当产品所有者 (Product Owner)了解到其交付需要花费大量的工作)。

product backlog

Sprint 计划会议:

Sprint计划会议在每一Sprint的启始阶段进行,时间限制:一般是4-8个小时 在Sprint计划会议的第一部分,产品所有者 (Product Owner)和Scrum开发团队(在Scrum Master的协助下)共同评审Product Backlog,讨论Backlog中各项目的目标和背景,并提供Scrum开发团队深入了解产品所有者(Product Owner)想法的机会。

在会议的第二部分,Scrum开发团队从Product Backlog中挑选项目并承诺在Sprint的末期完成任务,从Product Backlog的顶端开始(换而言之,从对产品所有者 (Product Owner)具有最高优先权的项目开始)并按列表顺序依次工作。这是Scrum的重要实践之一:开发团队决定承诺完成工作量的多少,而不是由产品所有者 (Product Owner)安排工作量。这就使任务的交付更可靠;第一,因为开发团队承诺工作量,而不是其他人代替其“承诺”工作量;第二,开发团队自己决定所需要的工作量,而不是其他人决定工作量“应该”是多少。产品的所有者对于开发团队承诺任务多少没有控制权,他或她只知道开发团队负责的项目是由Product Backlog中按顺序从上至下进行的——换而言之,是从他或她选择的最重要的项目开始。开发团队可以从列表底层选择项目提前完成,如果其对于整个开发具有意义(比如,提升和快速完成较低优先权的项目,作为完成较高优先权项目的一部分)。

Scrum的重要支柱之一是当Scrum开发团队确认承诺任务后,产品所有者 (Product Owner)在此Sprint期间不可以添加新的需求。这就意味着即使在Sprint中途,产品所有者(Product Owner)想要添加新要求,他在下一新的Sprint开始前不可能作出任何变更的决定。如果一个外部情况出现致使项目优先级的变化,意味着如果开发团队按原计划工作将会是浪费时间,产品所有者 (Product Owner)此时可以结束该Sprint;意味着开发团队停止一切工作,重新开始Sprint计划会议等等。此种决断会产生很大的影响, 除非在非常特别情况下,不会采用这种方式。

Sprint计划会议结束后的产物是sprint backlog。 Sprint backlog中的项目是从product backlog中复制来的,不过可能有些项目被进一步进行了分解和细化,下面是一个sprint backlog图:

sprint backlog

每日站立例会:

每当Sprint开始,Scrum开发团队将会实施另一个Scrum的重要实践方法:每日(站立)例会。这是在每个工作日特 定的时间(一般是早上)举行的短小(15分钟)的会议,Scrum开发团队的每一成员都将参与;每个与会者在发言是要回答下面3个问题: 1.我昨天做了什么 2.我今天打算做什么 3,我昨天碰到了什么问题 在每日站立例会中,不需要讨论,必要的讨论可以在会后进行。开会的时候,每个发言人是一个消息的广播者,而其他人则只是接受者。 Scrum Master 有必要把每个人发言时提到的问题记录下来,以便在会后协助碰到困难的人解决问题。每日例会之后的每个成员要更新它在sprint backlog中的每个任务的剩余工作时间。而Scrum Master则要帮助team 汇总每个人的更新后,更新整个team的工作进度到下面的一个图中:

sprint burndown

Sprint 成果展示/评审

在Sprint结束后,将进行Sprint评审,团队在此期间展示他们所构造的产品。出席此会议的有产 品所有者 (Product Owner),开发团队成员,ScrumMaster,加上客户,项目管理者,专家,高层人士和任何对此感 兴趣的人。这不是开发团队做成果“演讲”——会议上不会有PowerPoints图片文件,通常会议不会需要超过30分钟的准备时间——这只是简单的展示工作结果,所有与会人员可以提出问题和建议。会议可以持续10分钟,也可以是两个小时——会议目的只是对工作结果的展示和听取反馈。

Sprint 总结:

在Sprint评审之后,开发团队会进行Sprint回顾。有些开发团队会跳过此过程, 这是不合适的,因为它是Scrum使潜在的改进可视的重要技巧,并可以将其转化为结果。这是提供给开发团队的非常好的机会,来讨论什么方法能起作用而什么不起作用,并一致通过改进的方法。

更多资料:

http://zh.wikipedia.org/wiki/Scrum

http://www.controlchaos.com/

在Github上托管个人blog

2013-04-08

Github提供的Pages服务可以把相关的静态文件组合成一个博客站点。它不需要数据库,使用Git来管理博客的版本。你在本地写好文章(无需网络链接),在本地预览的文章效果,提交到Git仓库。发布文章的时候只需要Git Push一下就可以了。Github Pages是免费的,不限流量,而且速度非常快。

Github Pages使用Jekyll,是一个简洁的静态网站生成器, 来提供博客服务。Jekyll使用一个模板目录作为网站布局的基础框架,并在其上运行Textile,Markdown或Liquid标记语言的转换器,最终生成一个完整的静态Web站点,可以被放置在Apache或者你喜欢的其他任何Web服务器上。所以我们可以使用各种编辑器用Markdown语法来写博文。

配置过程:

Github

  1. 安装Git以及Git的使用请参考这里
  2. 在Github上创建一个新的repo来管理blog,这个repos的名字必须是USERNAME.github.com。 如果你还没有Github账号,那请先注册一个。
  3. 把这个新的repo clone到本地。

Jekyll

Jekyll是基于Ruby实现的,所以安装Jekyll之前,需要先安装Ruby,以及Ruby的包管理工具gem。

  1. 安装Ruby及Gem,请参考这里
  2. 执行如下命令安装Jekyll gem install jekyll
  3. 手动配置Jekyll站点 一个Jekyll站点具有如下结构:
    |-- _config.yml  
    |-- _includes  
    |-- _layouts  
    |-- default.html  
    |   -- post.html  
    |-- _posts  
    |   -- 2007-10-29-why-every-programmer-should-play-nethack.textile  
    |   -- 2009-04-26-barcamp-boston-4-roundup.textile  
    |-- _site  
    |-- index.html  

具体过程请参考这里

其实更快捷的做法是找到一个你喜欢的博客,然后把它clone下来,改成你自己的。Jekyll在这里列出了许多使用Jekyll的博客,可以打开看看,找到一个喜欢的主题风格,然后cone下来把它改成你自己的。我也是在网上找了好久,才发现现在这个主题(@imkerberos)是自己比较喜欢的,所以我在这里也借用一下。

4.Jekyll的配置
主要的配置文件是config.yml。在config.yml中除了可以设置博客的常规信息之外,还默认自带了一些常用功能,如评论(disqus)、Analytics跟踪(Google Analytics)等等。详细教程见这里

5.注意事项
Jekyll在处理中文的时候会有问题,解决方法:

  • 中文文件一定要保存为UTF-8格式
  • 设置环境变量:
      LC_ALL=en_US.UTF-8
      LANG=en_US.UTF-8
      

本地预览

在你的repo目录下,运行 jekyll --server ,然后再在浏览器里访问localhost:4000,就可以看到你的博客网站了。

发布博客

  1. 把改好的Jekyll站点copy到前面clone到本地的USERNAME.github.com目录里,覆盖里面的内容。注意不要拷贝.git目录。
  2. USERNAME.github.com目录/_posts/目录下创建你的博文。博文的文件名必须遵循下面的格式:YYYY-MM-DD-title.md
  3. 在当前目录提交所有的改到到Git
  4. git push USERNAME.github.com 到origin master分支。
  5. 10分钟左右,就可以访问http://USERNAME.github.com来 访问你的博客了。

以后写博客就只需要在_posts下新建一个Markdown文件,然后在把这个文件提交,push到Github,就可以完成发布了。

其他选择

如果你对Git不太熟,可以考虑使用octopress, octopres使用rake的任务来完成常见的git操作。 不过遗憾的是octopress push到Github上不是原始的markdown页面,而是翻译后的html,这样你需要一个额外的branch来保存原始的markdown博文,个人觉得不太方便。

参考资料

  1. http://www.ruanyifeng.com/blog/2012/08/blogging_with_jekyll.html
  2. http://www.soimort.org/posts/101/
  3. http://equation85.github.io/blog/blog-with-github-and-jekyll/
  4. http://colors4.us/blog/2012/01/08/octopressbo-ke-cong-ling-kai-shi-i/

Copyright © 2013 - Wenbin Ma - Powered by Jekyll