Practical Vim中文版 -《Vim实用技巧》已出版

车文隆与我合译的《Vim实用技巧》一书已由人民邮电出版社出版,本书的英文书名是 Practical Vim,原书作者是Drew Neil。这本书在Amazon网站上的评分是五颗星,是关于Vim的一本很好的书。

这本书在2014年5月已经正式出版了,但由于种种原因,我直到上周末才收到样书。拿到样书之后,第一时间翻阅了一下,书的纸质和印刷都相当不错,是我喜欢的类型。至于内容和版式,我早已熟悉的不能再熟了,不过看纸质版的感觉与看电子版很是不同,多了一份厚重感和书香气。

车文隆与我的分工如下:

  1. 车文隆负责:序、第10章到第21章、附录A
  2. Easwy负责:读者对本书的评论、自序、致谢、写作体例说明、第1章到第9章

作为本书的译者之一,我既逐字逐句翻译了部分内容,也曾多次对本书的内容及排版进行过审阅,因此我有充分的信心向读者推荐本书。对Vim用户而言,无论是刚接触 Vim 的初学者还是对 Vim 有了一定了解的中级用户,阅读本书都是学习 Vim 思维方式的很好途径。

因为我曾经对本书的内容和排版进行过多次细致的审阅,所以自信不会像某些书籍那样错误连篇,不过任何软件都有bug,任何书也都会有错误,如果读者发现书中的错误,请在《Vim实用技巧》勘误表中留言,我会一一记录下来,在本书再版时改正。

感谢本书的责任编辑陈冀康!也感谢另外一位译者车文隆,我们一起为这本书度过了不知多少个的日日夜夜!

感谢大家的支持!Happy Viming!

PS,本书在下列网站有售:

翻看了上述网站上的读者评论,大部分读者的反映都很好,也有几个读者吐槽书的纸质和价格,不过幸好没读者抱怨翻译质量,这就是对我们最好的评价了。 :-)

2015/04/28更新: 本书随书的源文件可以在这里下载,大家可以参照原书中的例子自己操作一下,以加深理解。

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/practical-vim-chinese-version/

文章的脚注信息由WordPress的wp-posturl插件自动生成

升级及迁移svn本地版本库

Subversion有一点很麻烦,每次版本升级时格式都会变,导致本地版本库不能用。这几天公司的版本库由1.6升级到了1.7,并且版本库的URL也发生了变化。以前遇到这种情况,一般都会删除本地版本库,然后再重新check out。不过因为版本库比较大,check out一次需要很长时间,于是在网上查了一下,找到一种简单的解决办法。

首先要解决svn版本升级的格式不兼容问题,如果用1.7版本的svn在1.6版生成的work copy上使用命令,会遇到如下错误:

$ svn info
svn: E155036: Please see the 'svn upgrade' command
svn: E155036: Working copy '/home/easwy/dev' is too old (format 10, created by Subversion 1.6)

此时可以使用svn upgrade命令,对本地版本库的格式升级:

$ svn upgrade
Upgraded '.'
Upgraded 'scripts'
Upgraded 'merge-info'
Upgraded 'merge-log'
......

因为服务器的地址也变化了,所以升级完本地版本库后,需要把版本库的地址迁移到新地址上去,可以使用以下命令:

$ svn switch --relocate http://server-url/svn/dev http://new-server-url/svn/repos/dev

此命令执行完后,再用svn info看一次,会发现本地版本库的地址已经切换到新的服务器地址了。
这种操作方式,比重新check out要方便太多了!

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/svn-upgrade-and-switch/

文章的脚注信息由WordPress的wp-posturl插件自动生成

使用Scala + sbt + sbt-android-plugin开发Android应用程序

开发Android程序通常所使用的语言是Java,但Scala做为一种有希望替代Java的语言,也逐渐被越来越多的Android者所熟悉。本文简要的介绍如何使用Scalasbt工具(Simple Build Tool)及sbt-android-plugin创建,编译,安装运行一个简单的Android程序。

  1. 环境准备

    Easwy的开发环境搭建在Ubuntu 10.04.3上,所以第一步是安装Ubuntu,具体过程略过。

    然后需要下载Android SDK,这一步可以参考文档Download the Android SDK完成。

    接下来在Ubuntu里安装Sun Java JDK,如果你使用OpenJDK,可以跳过这一步。首先去掉/etc/apt/sources.list中这两行前的注释,使能Java 6源:

    $ sudo vim /etc/apt/sources.list
    deb http://archive.canonical.com/ubuntu lucid partner
    deb-src http://archive.canonical.com/ubuntu lucid partner 

    然后安装Java 6 JDK:

    $ sudo aptitude install sun-java6-jdk 
  2. 安装Scala、sbt和sbt-android-plugin

    首先安装Scala。到Scala Download Page下载Scala,Easwy下载的是Scala 2.9.1的IzPack Installer,下载后执行:

    $ java -jar scala-2.9.1.final-installer.jar 

    按提示安装即可。

    然后安装sbt。到sbt wiki上下载sbt-launch.jar,然后创建一个脚本来调用它:

    $ vim ~/bin/sbt
    java -Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=384M -jar `dirname $0`/sbt-launch.jar "$@"
    
    $ chmod u+x ~/bin/sbt 

    最后安装sbt-android-plugin。因为sbt-android-plugin还在开发中,在此Easwy直接checkout它的源代码,然后安装在本地:

    $ git clone git://github.com/jberkel/android-plugin.git
    $ cd android-plugin
    $ sbt publish-local 

    sbt在第一次运行时会下载它所依赖的包,耗时会久一些,此过程只需进行一次。

  3. 自动生成Android应用程序框架

    设置好上面的环境后,可以使用sbt-android-plugin来自动生成一个简单的Android应用程序框架。插件的作者推荐使用一个名为giter8的模板工具来生成框架。首先需要下载giter8工具:

    $ cd ~/bin
    $ curl https://raw.github.com/n8han/conscript/master/setup.sh | sh
    $ ~/bin/cs n8han/giter8 

    这几条命令会下载一些它们所需的包,需要一些时间。接下来就可以用giter8工具,根据指定的模板自动生成一个Android应用程序:

    $ ~/bin/g8 jberkel/android-app
    
    Template for Android apps in Scala
    
    package [my.android.project]: com.easwy.projects.helloworld
    name [My Android Project]: Hello World
    main_activity [MainActivity]:
    scala_version [2.9.1]:
    api_level [10]:
    useProguard [true]:
    
    Applied jberkel/android-app.g8 in hello-world 

    这条命令会提示你输入一些参数,然后根据你输入的参数生成一个Android应用程序的框架。

    事实上,此命令直接找到保存在github.com上的模板,然后根据模板生成Android应用程序的目录框架。在本例中所使用的模板是jberkel/android-app。你也可以自己创建一套模板,生成更适合自己的目录框架。

  4. 编译、安装、运行Android程序

    由上面的模板生成的Android应用程序,其实就是一个简单的“Hello World!”应用,你可以直接编译、安装、运行它。

    首先进入sbt的交互模式:

    $ cd hello-world
    $ export ANDROID_SDK_ROOT=~/bin/android-sdk-linux_86
    $ sbt

    开始编译:

    > android:package-debug 

    然后用下面的命令启动你的Android模拟器(如果你还没有模拟器,参考Managing Virtual Devices创建一个):

    > android:emulator-start api10

    上面的命令启动了名为api10的模拟器,下面安装运行我们编译出来的Hello World程序:

     > android:start-emulator 

    现在可以看到Hello World程序已经在api10 emulator中运行了。

在后续的文章中,Easwy会继续介绍使用Scala开发Android程序的心得。

更多内容,请阅读易水博客上的其它文章。

[ 参考文档 ]

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/using-scala-sbt-for-android-development/

文章的脚注信息由WordPress的wp-posturl插件自动生成

在scala中判断一个对象是否是元组(Tuple)

在scala user邮件列表中看到一个函数,判断一个对象是否是元组。从这个函数中,Easwy才了解到原来模式匹配(Pattern Match)也可以应用于正则表达式,这使得在scala中使用正则表达式(regex)更加容易了。另外,这个函数还展现了scala的另外两个特点:

  1. 尽量使用递归解决方案,而不是使用循环。这样做的优点之一是避免使用变量,优点之二是代码简洁。是否有其它优点,Easwy仍在总结。但Easwy始终有个担心,递归会不会导致效率降低?会不会有堆栈溢出风险?
  2. 使用Option类型做为函数返回值。使用Option类型的好处很明显,这样你的函数既可以返回执行失败的情况(None),也可以在执行成功时给出有用的返回值。这比使用tru/false作为返回值方便很多。

函数主体如下,为了方便讲解,在前面加了行号:

    
1 val Ptrn = """scala.Tuple(\d+)""".r
2
3 def checka( x: Class[ _ ]) : Option[ Int ] = x.getName match {
4      case Ptrn( i ) => Some( i.toInt )
5      case _ => { val sc = x.getSuperclass; if( sc != null ) checka( sc ) else None }
6 }
7
8 def isTuple( x: AnyRef ) = if( x.isInstanceOf[ Product ]) checka( x.getClass ) else None
     
  1. 行1定义了一个Pattern对象,可以看到,在scala中使用正则表达式非常的简单。
  2. 行3定义了一个递归函数checka(),它的参数是Class[_],返回值是Option[Int]类型。参数”Class[_]”的意思是这个函数可以接受任意Class[A]类型,也就是接受任何参数化的Class类型(Type Parameterization)。

  3. 从行3的后半句,到行5,是一个模式匹配,检查类的名字是否匹配正则表达式Ptrn。如果类名匹配Ptrn,也就是说是一个Tuple,则返回它的维数。例如,对Tuple3返回Some(3)。如果类名不匹配Ptrn,递归调用checka()检查其父类是否为Tuple,如果全部失败,则返回None。
  4. 行8定义isTuple()函数,调用checka()判断是否为Tuple。它首先会检查x是否是一个Product实例,满足时才调用checka(),否则直接返回None。

Easwy感觉checka()函数中的递归写的不是很好,在看过”The Little Schemer”后,Easwy更倾向与下面的写法:


    
  def checka(x: Class[_]): Option[Int] = x match {
    case null => None
    case _ => x.getName match {
      case Ptrn(i) => Some(i.toInt)
      case _ => checka(x.getSuperclass)
    }
  }
     

下面是Easwy用来测试该函数的程序,全文如下:


    
object TestTuple {
  def main(args: Array[String]) {
    class ttt(a: Any, b: Any, c: Any) extends Tuple3(a, b, c)

    val test = List(new Tuple2(1, 2), new ttt(1, 2, 3), "Hello World")

    for (elem <- test)
      isTuple(elem) match {
        case None => println("Not Tuple")
        case Some(x) => println("Is Tuple" + x)
      }
  }

  val Ptrn = """scala.Tuple(\d+)""".r

  def checka(x: Class[_]): Option[Int] = x match {
    case null => None
    case _ => x.getName match {
      case Ptrn(i) => Some(i.toInt)
      case _ => checka(x.getSuperclass)
    }
  }

  def isTuple( x: AnyRef ) = if( x.isInstanceOf[ Product ]) checka( x.getClass ) else None
}
    
  

有兴趣的朋友可以编译运行一下,体会一下scala的简捷与优雅。

更多内容,请阅读易水博客上的其它文章。

[ 参考文档 ]

  • [scala-user] Trying to discover if a object is a tuple

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/test_if_a_object_is_a_tuple_in_scala/

文章的脚注信息由WordPress的wp-posturl插件自动生成

scala学习:添加行号

我的第一个scala脚本完成的功能就是为文本文件添加行号,不过那个脚本中使用了变量,不太符合scala编程的思想。现在把这个脚本改造了一下,去掉var变量的使用,完全使用val值。另外,把脚本改成了需编译的程序。

LineNumber.scala程序如下:

import scala.io.Source

object LineNumber {
  def main(args: Array[String]) {
    for (file <- args) {
      println("File: " + file)
      println("-" * 32)
      for (line <- addLineNumber(file))
        print(line)
    }
  }

  def addLineNumber(fileName: String): List[String] = {
    def padding(m: Int, i: Int) = " " * (m - i.toString.length)

    val lines = Source.fromFile(fileName).getLines.toList
    val maxWidth = lines.length.toString.length

    for {
      (l, i) <- lines.zipWithIndex
      j = i + 1
      line = padding(maxWidth, j) + j + " " + l
    } yield line
  }
} 

这个程序会从命令行获取要添加行号的文件名,把加了行号的文本打印到屏幕上。

用下面的命令编译该程序:

    fsc LineNumber.scala 

然后可以用下面的命令执行该程序,输出如下:

$ scala -cp . LineNumber LineNumber.scala
File: LineNumber.scala
--------------------------------
 1 import scala.io.Source
 2
 3 object LineNumber {
 4   def main(args: Array[String]) {
 5     for (file <- args) {
 6       println("File: " + file)
 7       println("-" * 32)
 8       for (line <- addLineNumber(file))
 9         print(line)
10     }
11   }
12
13   def addLineNumber(fileName: String): List[String] = {
14     def padding(m: Int, i: Int) = " " * (m - i.toString.length)
15
16     val lines = Source.fromFile(fileName).getLines.toList
17     val maxWidth = lines.length.toString.length
18
19     for {
20       (l, i) <- lines.zipWithIndex
21       j = i + 1
22       line = padding(maxWidth, j) + j + " " + l
23     } yield line
24   }
25 }
  

学习scala有大半个月了,越来越感受到了scala的简洁与强大。

更多内容,请阅读易水博客上的其它文章。

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/scala-learning-add-line-number/

文章的脚注信息由WordPress的wp-posturl插件自动生成

Easwy的第一个scala脚本

正在学习scala语言,还谈不上有很深入的认识,至于为什么学习scala,最初的动力大概是它简洁的语法以及与Java的兼容性。它使用类似脚本语言的语法,实现了编译语言的执行效率,是最让Easwy心动的地方了。
看完”Programming in Scala”的前三章,参照书中的例子,我写了我的第一个scala脚本,功能很简单,它读入一个文本文件,然后把文件的内容加上行号后打印出来。
脚本程序如下:

import scala.io.Source

if (args.length > 0) {
  val lines = Source.fromFile(args(0)).getLines.toList
  val maxWidth = lines.length.toString.length
  var i = 1
  for (line <- lines) {
    val padding = maxWidth - i.toString.length
    print(" " * padding + i + " " + line)
    i += 1
  }
}
else
  println("A file name is needed")

这个脚本要使用下面的命令执行:

scala printfile.scala a-text-file-here

如果你还没有安装scala,请到scala的主页下载并安装。
2010/04/07更新
在这个脚本中使用var变量,不太符合scala的编程思想,在文章scala学习:添加行号中,Easwy将其改成符合scala方式的val形式。

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/first-scala-script-of-easwy/

文章的脚注信息由WordPress的wp-posturl插件自动生成

Windows下cscope -q选项出错的解决

以前曾有网友留言,在windows下使用cscope时,使用-q选项出现如下提示:

D:\Temp> cscope -Rbkq
Input file specified two times.

cscope: cannot create inverted index; ignoring -q option
cscope: removed files ncscope.in.out and ncscope.po.out 

当时Easwy以为是windows版本的cscope不支持-q选项。

不过网友Liang Feng昨天留言说:

Windows版本的cscope是支持-q选项的,出现以上错误是因为没找到合适的sort命令。所以只要cscope进程的工作目录下有GNU版本的sort就可以了。注意是进程的工作目录,不是可执行程序的目录。具体原因看一下cscope的代码片段。

build.c

#ifdef WIN32
snprintf(sortcommand, sizeof(sortcommand), “set LC_ALL=C && sort -T %s %s”, tmpdir, temp1);
#else
snprintf(sortcommand, sizeof(sortcommand), “env LC_ALL=C sort -T %s %s”, tmpdir, temp1);
#endif 

于是重新做了一下试验。在安装了Cygwin后,在cygwin的shell界面中使用cscope,没有出现上面的错误提示。但如果打开windows的命令行窗口cmd.exe,在里面执行cscope,则会出现上面的错误。

在仔细比较了cygwin和cmd.exe的环境后发现,原来是Windows自带的sort.exe搞的鬼。

在cygwin的shell窗口中,PATH环境变量的设置会使其先找到GNU版的sort.exe,所以上述cscope命令执行可以成功。但在cmd.exe窗口中,PATH环境变量会使其先找到windows自带的sort.exe而不是GNU sort.exe,所以会出现上面的错误提示。

知道了问题的原因,解决方法就很简单了,我们可以写一个批处理程序,在此程序中重设PATH环境变量,使cscope使用GNU版的sort.exe。示例程序如下:

D:\Temp> type cs.bat
@echo off
set path=c:\cygwin\bin;
cscope -Rbkq 

在这个批处理里,首先把path环境变量指向c:\cygwin\bin;,我的cscope.exe和GNU版的sort.exe都在此目录中。接下来现执行cscope命令,现在它使用的就是GNU版的sort.exe了。

在此感谢网友Liang Feng,谢谢他的提醒。

更多内容,请阅读易水博客上的其它文章。

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/cscope_sort_option_on_windows/

文章的脚注信息由WordPress的wp-posturl插件自动生成

Linux使用笔记: subversion使用

  1. 查看某次提交所修改的文件列表:
  2. svn -v -r 12345 log
    
  3. 设置忽略文件列表
  4. svn pe svn:ignore .
    
  5. 设置全局忽略文件列表
  6. 编辑~/.subversion/config文件,修改此文件中的global-ignores,例如,想让subversion忽略vim的交换文件文件,可以这样设置:

    global-ignores = *.o *.swp
    

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/several-subversion-tips/

文章的脚注信息由WordPress的wp-posturl插件自动生成

Exuberant Ctags用法:选择tag文件中所包含的tag类型

网友SamPeng问:

请问哪个参数是不解析方法中变量的。

方法中的变量纯粹是浪费tag标签。从来不用。因为一个文件里有多个同明变量

这个问题比较普遍,因此写一篇文章来解释一下。

大多数人在使用Exuberant Ctags时,一般都使用缺省的ctags -R。一般来讲,这条缺省命令已经可以满足大部分人的需求了。在某些情况下,可能对tag文件中标签的类型有特殊要求,这时就需要进行定制了。

对SamPeng所提出的问题,解决方法如下:

  1. 首先用ctags –list-kinds=<LANG>列出这个语言所支持的标签类型
  2. 然后在生成tag文件时,用ctags –<LANG>-kinds=[+|-]kinds来增加或去掉这种tag类型
  3. 需要把<LANG>换成你所用的编程语言的名字,用ctags –list-languages查看ctags支持哪些语言。

其实我们在文章vim使用进阶: 智能补全中已经用过Exuberant Ctags的定制功能了,在那篇文章里,我们使用命令

ctags -R --c++-kinds=+p --fields=+iaS --extra=+q src 

来为OmniCppComplete插件生成定制的tag文件。仔细分析一下上面这条命令,相信你对Exuberant Ctags的了解会更加深一层的。

从上面的介绍我们可以看到,Exuberant Ctags的功能非常强大(例如,甚至可以用它为任意文件提取tag,你只需要指定生成tag的规则就行了),只是大多数人并不了解。如果你对tag文件的内容有特殊的需求,建议你仔细阅读一下Exuberant Ctags的手册。你可以在这里找到我翻译的Exuberant Ctags中文手册

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/ctags-usage-select-tag-kinds/

文章的脚注信息由WordPress的wp-posturl插件自动生成

如何将cscope安装到指定目录

登录博客,看到joecgs问

easwy:您好,感谢您的这一系列的文章,帮助我很快地熟悉使用vim,谢谢。
我现在是用远程的shell来写程序,服务器没有安装cscope,而又不能获取root权限。所以,我想能不能通过和安装vim一样的方式来安装cscope?

随着Linux的普及,使用Linux进行软件开发的人也越来越多。而大多数公司都采用这种方式:提供一台高性能的中央服务器做为开发编译服务器,每个人登录这台服务器进行开发编译。在这种情况下,用户通常没有root权限,怎么安装应用程序呢?

其实,Linux作为老牌多用户操作系统UNIX的一个分支,这当然不是一个问题。多用户操作系统中,允许用户根据自己的喜好安装软件,是再自然不过的事情了。

做法也很简单,UNIX的程序,一般都使用autoconf来自动检测安装环境完成配置,然后再进行编译、安装,这三步就是俗称的三板斧。

在使用autoconf进行配置时,可以使用参数来更改缺省的配置,比如,通过使用–prefix参数,就可以改变程序的安装目录,还有一些其它参数,允许用户打开或关闭某些特性。每个程序所支持的参数不一定相同,可以通过./configure –help命令查看程序支持哪些参数。

回到joecgs的问题上来,如果想把cscope安装到自己的HOME目录下,其实只需要在configure时,指定–prefix=$HOME就可以了,全部命令如下:

cd cscope-15.7
./configure --prefix=$HOME
make
make install 

如果你留心一下,我们在编译vim时,也可以指定–prefix参数。其实,对绝大多数支持autoconf的程序来说,都可以用这一参数来指定你程序的安装位置,不信你试试!

原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]

本文链接地址: http://easwy.com/blog/archives/install-cscope-to-home/

文章的脚注信息由WordPress的wp-posturl插件自动生成