自己动手开发最好的代码编辑器

开发 前端
这一年来我花了很多的时间在写一个代码编辑器。大部分时间都是在实现各种各样的功能,其中也遇到了不少的问题。现在把实现这个编辑控件的一些问题的解决方法写出来,以供参考。

这一年来我花了很多的时间在写一个代码编辑器。大部分时间都是在实现各种各样的功能,其中也遇到了不少的问题。现在把实现这个编辑控件的一些问题的解决方法写出来,以供参考。这里说明下,我用的是MFC,当然了,没有用现成的控件,而是直接从CWnd继承来实现自己的编辑控件。

先给大家弄个效果图吧,你可以在这里CuteC Editor下载,欢迎大家提出意见。

 

 

问题1:如何让控件接受所有的按键和汉字。

问题2:如何计算光标的位置。

问题3:如何存储编辑控件的文本内容。

问题4:如何实现关键字高亮。

问题5:如何实现自动换行。

问题6:如何解析脚本。呵呵,我自己写了个C语言解释器,那它来用还是很不错的。

一. 如何让控件接受所有的按键和汉字。

让CWnd接收所有的按键做法很简单,只需响应WM_GETDLGCODE,代码如下:

  1. afx_msg UINT OnGetDlgCode();  
  2.       ...  
  3.       ON_WM_GETDLGCODE()  
  4.       ...  
  5.       UINT CLEditWnd::OnGetDlgCode(){  
  6.           return DLGC_WANTALLKEYS;  
  7.       } 

接收汉字就比较麻烦了,必须响应WM_IME_CHAR消息。我得做法如下,不知有没有更简单的方法。

1. 重新设置窗体的WND_PROC函数。在这个函数中获取WM_IME_CHAR消息,并通过自定义消费返回我们的CWnd窗体。

  1. WNDPROC LEditWndProcOld;  
  2.      LRESULT LEditWndProcNew(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){  
  3.          CWnd *pWnd = CWnd::FromHandlePermanent( hWnd );  
  4.          if(uMsg==WM_IME_CHAR){    
  5.              pWnd->PostMessage(WM_LEDIT_ZW, wParam, lParam );  
  6.              return   0;  
  7.          }  
  8.          return CallWindowProc( LEditWndProcOld, hWnd, uMsg, wParam, lParam);  
  9.      }  
  10.      ...  
  11.      void CLEditWnd::PreSubclassWindow(){  
  12.          LEditWndProcOld = (WNDPROC)SetWindowLong(this->GetSafeHwnd(), GWL_WNDPROC,  (LONG)LEditWndProcNew);   
  13.          CWnd::PreSubclassWindow();  
  14.      } 

2.响应WM_LEDIT_ZW自定义消息,获取汉字内容。

在PreSubclassWindow设置了LEditWndProcNew回调函数,并把返回值赋给LEditWndProcOld。而在LEditWndProcNew函数中,把WM_IME_CHAR消息通过自定义消费WM_LEDIT_ZW发回CLEditWnd窗体。汉字就保存在wParam参数中。可以这样获得: char hz[3] = { wParam>>8, wParam, 0 };

二. 如何计算光标的位置。

这个问题看似简单,但其实在程序的开发过程中是最难调试的。首先我们要明确以下问题:

1. 知道光标所在的行的位置,要计算出他在界面中的像素位置。

2. 知道鼠标点击的位置,要把它转化成字符串中所对应的位置。

Windows提供GetTextExtent来计算字符串显示的宽度。我们知道调用这个函数就可以解决上述的问题了。但是当你这么去做的是后,你才知道效率有多低,当你在选择内容移动鼠标时,要及时的计算光标的位置,你就知道效率跟不上了。想了很久,终于想出了个办法:

在创建好控件后,首先调用GetTextExtent来计算所有英文字符和汉字的宽度,接下来我们就不直接调用GetTextExtent这个函数了。而是直接根据已经算到的字符宽度来计算字符串的宽度。效率得到大大的提高。我这里给出了我的相关代码。

  1. char data[2];  
  2.        m_cText.nCharWidth[0] = 0;  
  3.        for( i=1; i<256; i++ ){  
  4.            data[0] = i;  
  5.            data[1] = 0;  
  6.            m_cText.nCharWidth[i] = (unsigned char)pDC->GetTextExtent( data ).cx;  
  7.        } 

nCharWidth数组中的信息足以计算任何字符串的显示宽度。唯一不足的是在更换字体的时候,我们必须跟换这个数组的内容。

三(1). 如何存储编辑控件的文本内容

在打开文件,编辑文档时,我们必须在内存中存储这个文档的最新内容,并且实时的更新到界面上。在MFC上,没有什么比CStringArray更合适的了,虽然有人说CStringArray会内存泄露,但我测试下来没发现这个问题,总觉得是说这话的人自己的代码没写好造成的。CStringArray在很多行数据的数据估计插入的效率不高,但对于打文件的处理,我们分开来处理的。CStringArray提供了数组和字符串的功能,所以对字符串的操作就方便多了。唯一的不足是,我们必须预先处理文件,把文件的每行保存到CStringArray中。在大文件的读取中,这会浪费一定的时间。

三(2). 另一个重要的问题就是大文件的处理。对于大文件,我做了特殊的处理。

1. 采用内存映射文件扫描整个文件,提取出行信息。

2. 采用分块处理来操作整个文件,使控件中保存的数据仅仅是文件的一个块。

3. 当大文件被修改,当块被切换时,这个块数据必须保存在内存中,或者必须保存到另一个中间文件。而对于没有被修改的块,则不需做任何处理。

4. 在保存大文件时,必须根据每块的信息重新写入文件。

  1. * Block 01  
  2.     * Block 02  
  3.     * Block 03  
  4.     * ...  
  5.     * Block n 

每个Block我们必须保存它相关的信息。我定义了一个类,声明如下:

  1. class CBlockNode  
  2.          {  
  3.          public:  
  4.              CBlockNode();  
  5.              ~CBlockNode();  
  6.          public:  
  7.              __int64 lBlkBegin;        //块开始位置,在文件中的开始位置  
  8.              LONG lBlkSize;            //块大小  
  9.              LONG lLineTop;            //开始行  
  10.              LONG lLineLow;            //结束行  
  11.              CString sLeftString;      //该块的剩余行, 应为连个块之间的分割处,有可能会把一行分隔开,这里保存最后一行的前半部分。  
  12.                               //必须做特殊的处理,以保证两块的分割处就是换行符。则可以保证改字段为空。  
  13.              char *pDirtyCtx;          //脏数据,用来保存被修改过的块数据,如果为NULL,则表示该块没被修改过。  
  14.          public:  
  15.              CBlockNode & operator = ( CBlockNode &src );  
  16.          };  

四. 如何实现关键字高亮。

1. 关键字怎么保存在配置文件中每个人有每个人的做法。关键问题在于如何快速的查找字符串中存在这个关键字。

2. 当关键字很多的时候,查找的效率就有讲究了。

3. 如何在内存中保持信息,在界面中显示。

我们倒过来讲:

3. 首先在界面上显示一行文字很简单,调用TextOut就可以了。最好不要用DrawText,效率比TextOut低很多。

为了对每行显示的时候提供颜色信息,在内存中必须保持一个足够长的数组,来保持每个字符对应的颜色。而在显示的时候,一个一个字符先SetTextColor再TextOut就可了。然而这样效率不是很高,好的办法是,对相同的颜色的词一次性的重绘出来,尽量减少TextOut的调用。所以我又加了一个数组保存了每个关键字的长度。

这里有个问题,不能为稳定的每行都保存这样的数组,不然内存空间占用会很大。而是在绘制行的

2. 关键字很多的时候,我们必须对每个词一一去判断该词是否在关键字中。所以hash表是比较合适的选择了。这里不多讲。

1. 要提取出一个字符串中的词,然后根据词再去判断是否是关键字。所以就涉及到字符串的断词功能。例如一个字符串:

  1.  This is a test line string , 哈哈 :).  
  2.     我们必须提取出:  
  3.            This  
  4.      -  
  5.      is  
  6.      -  
  7.      a  
  8.      -  
  9.      line  
  10.      -  
  11.      string  
  12.      -  
  13.      ,  
  14.      -  
  15.      哈哈  
  16.      :  
  17.      )  
  18.      . 

其中 - 表示空格。然后再到关键字表中匹配,判断该词是否是关键字。如果是关键字,修改颜色数组的颜色信息,供界面使用。

五. 如何实现自动换行。

在显示行的时候,我们不是直接那保存在内存的行数据就直接TextOut出来,而是要经过几个步骤来处理改行数据。

1. 处理Tab键(0x09),当我们碰到0x09时,必须将它替换成空格,当然没个Tab在不同的位置用不同的空格补全,保证补全后能被TAB_LEN整除。这样就能得到去除TAB后的字符串。

2. 统计第1步得到的字符串,自动换行后,将每行保存为CStringArray,然后在界面中显示。

3. 添加自动换行功能,对光标的计算会有影响,所以在将界面像素点转成光标位置时,必须要统计当前界面的每行的子行数(自动换行后所得的行数)。然后才能确定在第几行。所以计算起来比较麻烦。

原文:http://www.cnblogs.com/linxr/archive/2011/10/30/2229256.html

【编辑推荐】

  1. 10个免费的PHP编辑器/开发工具推荐
  2. Web设计师必备:10款最佳免费CSS在线编辑器
  3. 推荐 15 款很棒的文本编辑器
  4. 云计算爱好者必备的5个在线代码编辑器
  5. 10个自由丰富的jQuery或JavaScript编辑器
责任编辑:陈贻新 来源: Linxr's space
相关推荐

2012-08-01 09:34:51

代码编辑器开发代码

2015-04-24 10:54:58

JavaScript开发工具代码编辑器

2014-06-25 09:15:33

JavaScript 编辑器

2012-07-27 11:38:30

开发人员

2013-08-26 14:58:35

开发工具代码

2022-11-25 12:25:27

Python代码编辑器

2023-03-17 10:03:51

服务器编辑器vscode

2020-04-09 14:23:44

PythonMarkdown编辑器

2014-11-10 15:12:46

2012-12-28 10:10:18

2009-06-11 10:03:57

NetBeans代码

2021-01-21 16:03:15

Java文本编辑器编程语言

2015-02-12 09:51:24

代码编辑

2022-03-03 20:57:53

代码编辑器VS code

2021-03-10 09:15:15

代码文本编辑器编程

2022-12-02 07:24:46

2020-07-15 15:12:17

Python代码编辑器编程语言

2011-11-17 14:17:09

IDE

2017-03-09 19:25:38

JavaScript代码编辑器

2024-08-14 08:33:46

前端编辑器
点赞
收藏

51CTO技术栈公众号