синтаксическая подсветка richedit control не работает poperly

Я пытаюсь реализовать редактор подсветки синтаксиса с помощью 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);
}

1

Решение

Посмотрите на эту статью для некоторых идей:

Подсветка синтаксиса при редактировании быстрее
http://bcbjournal.org/articles/vol3/9910/Faster_rich_edit_syntax_highlighting.htm

Это было написано для TRichEdit управление в C ++ Builder, но большинство советов в нем используют прямые вызовы Win32 API, и те немногие места, где используются идиомы VCL, могут быть легко адаптированы в эквиваленты Win32.

Обновить: Попробуйте удалить EM_LINEFROMCHAR из вашей петли. offset + 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);
}

С этим сказал, вместо того, чтобы использовать getLine() а также strtok() для разбора текста, вы можете рассмотреть возможность использования EM_FINDWORDBREAK найти слова и EM_EXSETSEL/EM_GETSELTEXT чтобы восстановить символы каждого слова. Таким образом, вы используете меньше памяти и позволяете RichEdit больше искать вас. Ты можешь использовать EM_SETWORDBREAKPROC/EX если вы хотите настроить искомые слова.

1

Другие решения


По вопросам рекламы [email protected]