实现代码编辑器
上一次曾经发布过一篇如何实现一个代码编辑器。今年工作中得空,所以对这个编辑器进一步做了些更新,把名字改成了从CuteC改成了CEditor。主要是重写了软件的界面(最终还原朴素),重写了编辑控件语法高亮的着色方式,还有增加了一个简单的SSH客户端和SFTP文件编辑的功能。感觉基本的功能已经实现了,所以再次写点东西记录一下。并且发布一下编辑控件的源代码,有兴趣的可下载下来看看,虽然代码写得比较乱。
上个版本:http://www.cnblogs.com/linxr/archive/2011/10/30/2229256.html
编辑控件源码链接:https://files.cnblogs.com/linxr/LEdit.rar
软件下载链接:https://files.cnblogs.com/linxr/CEditor.rar
软件界面:
一、编辑控件
除了上一次写到的一些内容外,主要在编辑控件的语法高亮上做了修改。
1. 原来的颜色信息是在绘制每行时,动态分词查找关键字,形成一个颜色数组供绘制模块使用。这种方式的优点就是逻辑简单,绘制代码很容易编写。但是缺点就是,每次绘制一行,都要重新产生颜色数组,影响效率;其二,在一些场合下,高亮不是依赖于关键字,这时候这种方法就无能为力了。
2. 考虑到上面的缺点,对编辑控件做了修改。去除了颜色数组,改用标注信息来标注颜色字体信息。所以行数据经过着色之后不再是原来的内容了。
例如一行数据:
int main( int argc, char ** argv );
那么经过着色后,该行的数据会变成:
\ec3,0~int\ezc~ main( \ec3,0~int\ezc~ argc, \ec3,0~char\ezc~ ** argv );
其中,int和char被着色,标注信息为:
\ec3,0~ 和 \ezc~
着色信息以\e(0x1b)开始,以~结束。c表示颜色,z表示还原格式格式。
着色完成后,绘制模块在进行绘制文本信息时,从头到尾,扫描字符串,根据着色信息,不断的设置还原DC的颜色信息,直到绘制完成。
3. 标注颜色带来了一些额外的问题,主要表现在:
a.光标位置到字符串中的位置的转换的计算。
b.获取编辑器的内容时,要清除所有的标注信息,影响一定的性能。
c.计算tab键的宽度时,不能用tab键在字符串中的位置来计算,要考虑标注信息。
d.光标在屏幕中的位置的计算,要根据光标前面数据的字体来实时计算宽度。
二、函数列表
对于代码编辑软件来说,函数符号列表功能是必备的,就算是纯写C的人来说,有一个函数列表会大大提高滚动查找代码的效率。我虽然写了一个C语言的解释器,对符号的解析稍作修改就可以实现,但是各种各样的语言,我就无能为力了。好在有ctags.exe,他可以支持非常的语言符号分析。我还是直接拿来用吧,然后再加上一个树形控件,就能实现简单实用的功能。
我调用ctags.exe的代码片段,主要是调用了ctags.exe,将结果保存到一个文件,然后我们再解析这个文件:
CString strParam;
CString strTags = strFilePath + _T(".tags.txt");
strParam.Format( _T("-f \"%s\" --language-force=C++ -n \"%s\"" ), strTags.GetBuffer(0), strFilePath.GetBuffer(0) );
SHELLEXECUTEINFO sei;
ZeroMemory( &sei, sizeof(SHELLEXECUTEINFO) );
sei.cbSize = sizeof(SHELLEXECUTEINFO);
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.hwnd = NULL;
sei.lpVerb = NULL;
sei.lpFile = "ctags.exe";
sei.lpParameters = strParam.GetBuffer(0);
sei.lpDirectory = CLxrFile::GetExecutePath().GetBuffer(0);
sei.nShow = SW_HIDE;
sei.hInstApp = NULL;
ShellExecuteEx(&sei);
WaitForSingleObject( sei.hProcess, INFINITE );
/*
** 可以看到就是调用了ShellExecuteEx,但是有几个地方可以提一下:
** 1. sei.nShow = SW_HIDE; 这使得进程启动时不会显示控制台窗口。
** 2. WaitForSingleObject( sei.hProcess, INFINITE );
** 等待进程结束,这个很重要,不然ctags启动后,我们无法同步获取结果。
*/
ctags.exe支持的语言,可以用命令 ctags.exe --list-languages 查看:
Asm
Asp
Awk
Basic
BETA
C
C++
C#
Cobol
Eiffel
Erlang
Fortran
HTML
Java
JavaScript
Lisp
Lua
Make
Pascal
Perl
PHP
Python
REXX
Ruby
Scheme
Sh
SLang
SML
SQL
Tcl
Vera
Verilog
Vim
YACC
可以看到,他几乎涵盖了所有流行的语言,而他的执行文件大小只有256kb,太强悍了,让我非常的佩服。
三、脚本管理
让编辑软件支持脚本是必要的,不仅仅可以对常用的功能提供扩展。我实现了类C的解析执行,所以对简单的计算功能,软件的功能,都能给外部脚本提供接口函数,让脚本轻松的操作编辑器,或者做一些好玩的事情。我主要做的是将脚本直接映射到右键菜单,当添加一个脚本时,他将直接被添加进右键菜单。并且支持多级目录。
例如,我实现了几个接口供脚本使用:
int ceditor_exe_2( char * cmd, char * out );
该函数用于执行cmd指令,out为输出结果。cmd为编辑器的的命令,形如rep_sel$hello world$。
int ceditor_exe_4( char * cmd, char * p1, char *p2, char * out );
该函数效果同ceditor_exe_2,但是cmd仅是命令,p1,p2分别是两个参数。
int clear();
该函数用于清除输出框的内容。
int ceditor_hotkey( char * ctrlKeys, int vKey, char * scriptPath );
该函数用于注册热键。
C的解释器,大家可以看我以前发过的文章,代码可以在CSDN上面找到,搜索"xrc C语言解释器"就能找到,或者试试这个链接:http://download.csdn.net/download/linxren/4204728。在cnblogs上可以看看相关的实现方法,算是讲得比较清楚的吧。
四、数据存储
数据存储在不同的软件中都必须用到,我这里主要是配置的存储和脚本的存储,正好我写了个key-value存储系统,所以这个直接拿来用就非常合适了,效率也不错。跟直接读写硬盘没什么区别,省去了烦人的fopen,fread,fwrite,fclose调用,直接用windows的api更是烦人,所以写这样一个简单的库,非常方便。
http://www.cnblogs.com/linxr/p/3252541.html,在这里可以看到实现方法和下载到源代码。我觉得这个简单的key-value存储库,比我写的解释器,和这个编辑器本身更加实用,所有的软件都可以用,接口也很简单。无法就是open,set,get,close四个函数。省去了文件操作的各种麻烦。