语法高亮 Richedit 控件无法正常工作

Posted

技术标签:

【中文标题】语法高亮 Richedit 控件无法正常工作【英文标题】:syntax highlight richedit control not working poperly 【发布时间】:2014-07-06 19:28:23 【问题描述】:

我正在尝试使用 Richedit 实现语法高亮编辑器,它与当前选定的行配合良好,但我可能遗漏了一些东西。 CRichEdit 是我自己的richedit 控制器的包装器实现,问题似乎是文本没有正确选择,即使我确保使用代码生成的选定范围是我通过 EM_EXGETSEL 消息得到的。 随着行的下降,选择似乎增加了 1,因此我决定将 ed_source.sendMessage(EM_LINEFROMCHAR, pos, 0) 设置为部分解决问题的范围,除了一些颜色似乎某个时间或位置之前的几行和真正合适的,所以这就是为什么我觉得我可能不理解某些东西。

void parse(WIN::CRichEdit &ed_source, bool curseline)

    int pos, offset = 0;
    char delimiter[]=" \n\r();", *tok, *start;
    CStringA s;
    CString text;
    CWnd api;

    if(curseline)      
        ed_source.getLine(ed_source.getRow() - 1, text);
        offset = ed_source.sendMessage(EM_LINEINDEX, -1, 0);
    else
        text = ed_source.getCaption();
    

    s = text;
    start = s.c_str();
    if(!start) return;

    tok = strtok(s.c_str(), delimiter);

    CHARRANGE cr = ed_source.getSelecteRange();
    ed_source.sendMessage(EM_HIDESELECTION, 1, 0) ;
    CHARRANGE range;
    while(tok)
    
        int len = strlen(tok);

        pos = (tok - start);
        int x = ed_source.sendMessage(EM_LINEFROMCHAR, pos, 0);
        range.cpMin = offset + pos - x;
        range.cpMax = range.cpMin + len;

        ed_source.selectRange(range);
        if(isReserved(tok))

            ed_source.setTextStyle(true, false);
            ed_source.setTextColor(keyboardColor);
        else
            if(isType(tok))
                ed_source.setTextStyle(false, false);
                ed_source.setTextColor(typeColor);
            else 
                ed_source.setTextStyle(false, true);
                ed_source.setTextColor(textColor);
            
        tok = strtok(0, delimiter);
    

    ed_source.sendMessage(EM_HIDESELECTION, 0, 0) ;
    ed_source.selectRange(cr);

更具体地说,我调用上述函数的那一刻是在加载文本之后立即。我假设您可能想查看上述某些功能的实现,所以在这里。

CHARRANGE CRichEdit::getSelecteRange()

    CHARRANGE crg = 0 ;
    sendMessage(EM_EXGETSEL, 0, (LPARAM)&crg);
    return crg;


void CRichEdit::selectRange(const CHARRANGE &cr)

    sendMessage( EM_EXSETSEL, 0, (LPARAM) &cr);



void CRichEdit::setTextColor(COLORREF col)
 
    CHARFORMAT format;
    memset(&format, 0, sizeof(CHARFORMAT));
    format.cbSize       = sizeof(CHARFORMAT);
    format.dwMask       = CFM_COLOR;
    format.crTextColor  = col;

    sendMessage( EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &format);

【问题讨论】:

richedit 控件之上的编辑器中的语法突出显示将永远无法正常工作。您将永远返回已格式化的文本并重新进行处理。 【参考方案1】:

请看这篇文章以获得一些想法:

更快的富编辑语法高亮 http://bcbjournal.org/articles/vol3/9910/Faster_rich_edit_syntax_highlighting.htm

它是为 C++Builder 中的TRichEdit 控件编写的,但其中的大部分技巧都使用直接的 Win32 API 调用,并且使用 VCL 惯用语的少数地方可以轻松地适应 Win32 等效项。

更新:尝试从循环中删除 EM_LINEFROMCHARoffset + pos 已经是 RichEdit 中的绝对字符位置,不需要在每次循环迭代时对其进行调整。如果您真的想考虑行索引,那么您应该一次遍历一行,分别解析每一行,而不是将整个内容解析为单个字符串。尝试更多类似的方法:

void parse(WIN::CRichEdit &ed_source, bool curseline)

    int startLine, endLine, offset;
    const char* delimiters = " \n\r();";
    char *tok, *start;
    CStringA s;
    CWnd api;

    if (curseline)
    
        startLine = ed_source.getRow() - 1;
        endLine = startLine + 1;
    
    else
    
        startLine = 0;
        endLine = ed_source.sendMessage(EM_GETLINECOUNT, 0, 0);
    

    CHARRANGE cr = ed_source.getSelecteRange();

    int eventMask = ed_source.SendMessage(EM_SETEVENTMASK, 0, 0);
    ed_source.SendMessage(WM_SETREDRAW, FALSE, 0);

    for (int line = startLine; line < endLine; ++line)
    
        CString text;
        ed_source.getLine(line, text);

        s = text;
        start = s.c_str();
        if (!start) continue;

        offset = ed_source.sendMessage(EM_LINEINDEX, line, 0);

        tok = strtok(start, delimiters);
        while (tok)
        
            CHARRANGE range;
            range.cpMin = offset + (int)(tok - start);
            range.cpMax = range.cpMin + strlen(tok);

            ed_source.selectRange(range);
            if (isReserved(tok))
            
                ed_source.setTextStyle(true, false);
                ed_source.setTextColor(keyboardColor);
            
            else if (isType(tok))
            
                ed_source.setTextStyle(false, false);
                ed_source.setTextColor(typeColor);
            
            else
            
                ed_source.setTextStyle(false, true);
                ed_source.setTextColor(textColor);
            

            tok = strtok(0, delimiters);
        
    

    ed_source.SendMessage(WM_SETREDRAW, TRUE, 0);
    ed_source.Invalidate(); // whatever your wrapper does to call ::InvalidateRect()

    ed_source.SendMessage(EM_SETEVENTMASK, 0, eventMask);

    ed_source.selectRange(cr);

话虽如此,您可以考虑使用EM_FINDWORDBREAK 定位单词并使用EM_EXSETSEL/EM_GETSELTEXT 检索每个单词的字符,而不是使用getLine()strtok() 来解析文本。这样,您将使用更少的内存并让 RichEdit 为您完成更多的搜索。如果要自定义搜索的单词分隔符,可以使用EM_SETWORDBREAKPROC/EX

【讨论】:

我看了看,但看不到我不理解的部分。无论如何,我的大部分代码都在工作。例如,当我只修改一行时(即当我使用 curseline=true 调用 parse 时),颜色会正确应用。但是当我把它变成错误的意思时,我想给每一行上色是选择看起来有点不对。 我的第一次尝试是没有 EM_LINEFROMCHAR,因为它应该是你提到的绝对位置,但注意到随着行号的增加,它正在为前面增加的 char 索引着色。如您所说,逐行阅读效果很好,只是它遗漏了超出可见行的文本行。 您所说的“它在可见行之外留下了更多的文本行”是什么意思? 好吧,当我向下滚动时,我看到有些线条没有颜色,就像它没有更新那些一样。 从我从第 27 行开始的文本(这是一个片段着色器代码)开始,它开始留下没有颜色的文本。

以上是关于语法高亮 Richedit 控件无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

使用ICSharpCode.TextEditor制作一个语法高亮显示的XML编辑器

推荐第 3 方编辑/语法高亮控件 - WinForms

自定义编辑控件win32

语法高亮:.NET 的富文本框控件

wxpython 支持python语法高亮的自定义文本框控件的代码

jekyll 高亮标签打破了降价语法