Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > SynUniHighlighter и SynEdit > Подсветка парных скобок


Автор: Kordub 21.2.2005, 10:16
В общем, поюзал Delphi 8 и обнаружил там классную новую подсветку - когда курсор расположен возле одной из парных скобок, обе скобки выделены голубым бэкграундом. Сильно упрощает чтение выражений со скобками. Реализовано ли это у вас а если нет, то можно ли сделать?

Автор: Vitalik 22.2.2005, 23:55
Сделать можно. Вот пример кода:
Код
var
 // позиция скобок при предыдущем выделении цветом
 PrevPos1, PrevPos2: TBufferCoord;

const
 BracketSet = ['{', '[', '(', '}', ']', ')', '<', '>'];
 OpenChars: array[0..3] of Char = ('{', '[', '(', '<');
 CloseChars: array[0..3] of Char = ('}', ']', ')', '>');
 BgColorHighlight = clAqua; // фоновый цвет выделения
 FgColorHighlight = $FFFFFF - BgColorHighlight; // цвет шрифта выделения
 needHighlightBrackets = True; // Флаг - нужно ли подсвечивать скобки

function GetOppositeBracket(C: Char): Char;
// Поиск соответствующей скобки
var
 i: integer;
begin
 Result := #0;
 for i := 0 to High(OpenChars) do
   if C = OpenChars[i] then begin
     Result := CloseChars[i];
     Exit;
   end;
 for i := 0 to High(CloseChars) do
   if C = CloseChars[i] then begin
     Result := OpenChars[i];
     Exit;
   end;
end;

procedure TForm1.SynEditPaintTransient(Sender: TObject; Canvas: TCanvas;
 TransientType: TTransientType);
// Процедура для рисования на канве редактора
var
 Pos1, Pos2: TBufferCoord; // позиции парных скобок
 P1, P2: TPoint; // координаты скобок на канве Canvas
 Token: string;
 Bracket1, Bracket2: Char; // символы скобок
 Attri: TSynHighlighterAttributes; // атрибуты скобки (из подсветки)
 Rect1, Rect2: TRect; // прямоугольник скобки на канве Canvas
begin
 if (not needHighlightBrackets) or (SynEdit.Highlighter = nil) then
   Exit;
 Pos1 := SynEdit.CaretXY;
 SynEdit.GetHighlighterAttriAtRowCol(Pos1, Token, Attri);
 Pos2 := SynEdit.GetMatchingBracketEx(Pos1); // поиск парной скобки

 if (Pos1.char > 1) and (Pos2.char = 0) then begin
   // если не найдена парная скобка
   Dec(Pos1.char); // проверить для предыдущего символа
   SynEdit.GetHighlighterAttriAtRowCol(Pos1, Token, Attri);
   Pos2 := SynEdit.GetMatchingBracketEx(Pos1);
 end;

 if (PrevPos1.char <> Pos1.char) or (PrevPos1.line <> Pos1.line) or
    (PrevPos2.char <> Pos2.char) or (PrevPos2.line <> Pos2.line) then
 begin // если позиция подсвечиваемой скобки изменилась, то перерисовать
   SynEdit.InvalidateLine(PrevPos1.line);
   PrevPos1 := Pos1;
   SynEdit.InvalidateLine(PrevPos2.line);
   PrevPos2 := Pos2;
 end;
 
 if ((Pos2.char = 0) and (Pos2.line = 0)) or SynEdit.SelAvail or
   (Length(Token) <> 1) then
     Exit;

 Bracket1 := Token[1];
 if Bracket1 in BracketSet then begin
   Bracket2 := GetOppositeBracket(Bracket1); // парная скобка
   // получение для скобок экранных координат на канве
   P1 := SynEdit.RowColumnToPixels(SynEdit.BufferToDisplayPos(Pos1));
   P2 := SynEdit.RowColumnToPixels(SynEdit.BufferToDisplayPos(Pos2));
   // установка свойств шрифта для рисования скобки
   with Canvas do begin
     Font.Assign(SynEdit.Font);
     Font.Style := Attri.Style;
     Font.Color := FgColorHighlight;
     Brush.Color := BgColorHighlight;
   end;

   // рисование подсвеченной скобки
   Rect1.Top := P1.Y;
   Rect1.Left := P1.X;
   Rect1.Bottom := Rect1.Top + Canvas.TextHeight(Bracket1);
   Rect1.Right := Rect1.Left + Canvas.TextWidth(Bracket1);
   Canvas.FillRect(Rect1);
   Canvas.TextOut(P1.X, P1.Y, Bracket1); // была опечатка

   // рисование парной подсвеченной скобки
   Rect2.Top := P2.Y;
   Rect2.Left := P2.X;
   Rect2.Bottom := Rect2.Top + Canvas.TextHeight(Bracket2);
   Rect2.Right := Rect2.Left + Canvas.TextWidth(Bracket2);
   Canvas.FillRect(Rect2);
   Canvas.TextOut(P2.X, P2.Y, Bracket2); // была опечатка
 end;
end;

Если есть вопросы по коду - задавай! smile

P.S. Была небольшая опечатка в коде, только что исправил.

Автор: Kordub 2.3.2005, 21:59
Не работает, так как не может найти парную скобку. Не работает почему-то GetMatchingBracketEx - все время возвращает нулевую позицию. Pos2 = (0,0) и обработчик события завершается по условию
Код
 if ((Pos2.char = 0) and (Pos2.line = 0)) or SynEdit1.SelAvail or
   (Length(Token) <> 1) then
     Exit;


Автор: Vitalik 3.3.2005, 23:01
Странно...

А у тебя какая подсветка для SynEdit'а выбрана?

Автор: Kordub 4.3.2005, 19:02
SynUniSyn

Автор: Vitalik 4.3.2005, 20:59
В правилах этой подсветки есть список ключевых слов, в которых присутствуют скобки? Если нет - создай такое правило http://forum.sources.ru/smiles/Main/wink.gif

Автор: Quadr0 10.5.2005, 21:27
...

Автор: Vitalik 11.5.2005, 17:23
Quadr0, добро пожаловать к нам на огонёк! (я про форум)
Очень рад новому участнику! smile

Цитата(Quadr0 @ 10.5.2005, 21:27)
procedure TMainForm.FindBracketExecute(Sender: TObject);
...

Эта процедура только найдёт парную скобку, а Kordub просил сделать подсветку парной скобки при попадании на неё курсора smile
Но всё равно спасибо за попытку помочь!

Автор: Quadr0 11.5.2005, 20:42
...

Автор: Vitalik 12.5.2005, 00:36
Цитата(Quadr0 @ 11.5.2005, 20:42)
Тогда ему прямая дорога в сэмплы SynEdut 2.0b. Там лежит дэмка PaintTransient которая этим как раз и занимается.

А чем не устраивает приведённый мной код? smile
А за демку - спасибо, не замечал её до селе... smile

Автор: Coriolis 16.9.2005, 16:57
Нефигасебе здоровый код! Чтобы реализовать эту удобственность...
В смысле это вообще в синэдите не предусмотрено... А ведь такая удобная весчь!
Странно.

Автор: Coriolis 16.9.2005, 17:13
Добавлю, что код Vitalik работает, а из демки - нет.

Автор: ActioN 16.9.2005, 20:23
Quadr0, Vitalik, а у вас случайно нету готового рабочего кода на C++ Builder'e для реализации этой функции?

Автор: miksayer 16.9.2005, 21:19
угу, у меня тот же вопрос. Не могу перевести

Автор: Coriolis 28.10.2005, 18:44
Вот, вернулся к проекту, возникла трябла.
В ключевых словах у меня прописаны операторы.
Существуют и переменные с именем оператора.
Поэтому возникает ситуация: переменная подсвечивается как оператор.
Чтобы избежать этого я начал извращаться: в ключевых словах к операторам добавляю символы, например
Операторы:
B
S
M
B:
M:
S:

Вот, и ещё создал набор KeyWords, с названием NOToperators, и цветом как обычные символы:
B=
B,
B)
M=
M,
M)
S=
S,
S)
т.е. операторы не могут встречаться в тексте рядом с такими символами.
Но вот проблема: код для подсветки парных скобок, который привёл Vitalic, не видет такой скобки. Не могу понять - почему.
пример:
s a=$$abc^abc(1,2,s)
Первую скобку видит, а последнюю - нет. Игнорирует.

Автор: Vitalik 29.10.2005, 21:02
Coriolis, извини, что не ответил вчера. Сейчас немного не хватает времени... Плюс я немного приболел smile

Цитата(Coriolis @ 28.10.2005, 18:44)
Существуют и переменные с именем оператора.

А что это за язык такой, если не секрет? smile
Можешь также прислать мне свою подсветку для него? Пиши на highlighters[at]gmail.com smile

Цитата(Coriolis @ 28.10.2005, 18:44)
Поэтому возникает ситуация: переменная подсвечивается как оператор.

Гм... А по каким правилам определяется является слово переменной или оператором?..

Цитата(Coriolis @ 28.10.2005, 18:44)
Но вот проблема: код для подсветки парных скобок, который привёл Vitalik, не видет такой скобки. Не могу понять - почему.

А это всё из-за того, что в приведённом алгоритме скобки будут подсвечиваются только если они в тексте являются отдельными лексемами... (или что-то вроде того)
Цитата
var
  ...
  Token: string;
  ...
begin

  ...
  Pos1 := SynEdit.CaretXY;
  SynEdit.GetHighlighterAttriAtRowCol(Pos1, Token, Attri);
  Pos2 := SynEdit.GetMatchingBracketEx(Pos1); // поиск парной скобки
  if (Pos1.char > 1) and (Pos2.char = 0) then begin
    // если не найдена парная скобка
    Dec(Pos1.char); // проверить для предыдущего символа
    SynEdit.GetHighlighterAttriAtRowCol(Pos1, Token, Attri);
    Pos2 := SynEdit.GetMatchingBracketEx(Pos1);
  end;
  ...
 
  if ((Pos2.char = 0) and (Pos2.line = 0)) or SynEdit.SelAvail or (Length(Token) <> 1) then
      Exit;
  Bracket1 := Token[1];
  ...

В принципе можешь удалить эти строчки и проверку... Но тогда получай Bracket1 другим способом...

Автор: Coriolis 29.10.2005, 21:45
Цитата(Vitalik @ 29.10.2005, 21:02)
Coriolis, извини, что не ответил вчера. Сейчас немного не хватает времени... Плюс я немного прибол

Да брось - не за что. Выздоравливай. smile


Цитата(Vitalik @ 29.10.2005, 21:02)
А что это за язык такой, если не секрет?

Язык - M, процедурный, идёт вместе с БД. Интерпретируется, программы хранятся на сервере.
Чё ещё сказать? О! Cashe - это потомок эМа, просто прикрутили (как кто-то сказал - за уши) объекты. А так - cashe тот же эМ.
Конкретно - для СУБД MSM (Micronetic Systems M кажется).

а чем переменные отличаются - это от контекста зависит. Для экономии памяти принято сокращать операторы до одной буквы, напр оператор SET (аналог в васике - LET) - s, GOTO - g, DO (аналог в вdвасике GOSUB) - d.
вот, напр
s s=13
переменной s присваевается значение 13, т.е.
SET s=13
Поэтому если перед и после s стоит пробел - это оператор.
Если после/перед s запятая или скобка - это переменная (например её передали как параметр)
Вот пример кода:
; функция find - чего-то там ищет в файле
find(fstr)
n str,findfl
s findfl=""
f d q:((findfl)!($zc))
.s str=$$read()
.q:str=""
.;s str=$zzup($zzdsp(str,5))
.s:str[fstr findfl=1
q:$zc ""
q str
(табуляция в начале строки, кста -обязат)

Цитата(Vitalik @ 29.10.2005, 21:02)
Можешь также прислать мне свою подсветку для него? Пиши на highlighters[at]gmail.com

Не понял - куда слать?

Автор: Vitalik 29.10.2005, 21:56
Цитата(Coriolis @ 29.10.2005, 21:45)
Для экономии памяти принято сокращать операторы до одной буквы, напр оператор SET (аналог в васике - LET) - s, GOTO - g, DO (аналог в вdвасике GOSUB) - d.

Ничего себе smile Бывает же такое! smile
По-моему читабельность программы от этого сильно падает smile

Цитата(Coriolis @ 29.10.2005, 21:45)
(табуляция в начале строки, кста -обязат)

Это там надо считать, что вместо точек стоит табуляция? smile

Цитата(Coriolis @ 29.10.2005, 21:45)
Не понял - куда слать?

Шли на мыло: highlighters (собачка) gmail.com smile
Или можешь прикрепить здесь к своему сообщению (этот вариант более желателен) smile

Автор: Coriolis 30.10.2005, 19:43
Цитата(Vitalik @ 29.10.2005, 21:56)
Ничего себе  Бывает же такое!

Ага, я тоже так подумал - когда знакомился с языком.
Но это позволяет уменьшить память, т.к. текст интерпретируется.

Цитата(Vitalik @ 29.10.2005, 21:56)
По-моему читабельность программы от этого сильно падает

Да не, быстро привыкаешь и без проблем читаешь исходняк, поверь мне. smile

Цитата(Vitalik @ 29.10.2005, 21:56)
Это там надо считать, что вместо точек стоит табуляция?

Нет-нет, точки - это часть синтаксиса, это аналок {} в сях или begin end в паскале - уровень обозначают. А табуляция - именно #9.

Сейчас прикреплю, но учти - сырой файл, т.к. "в процессе".
Тяяяяк. Не вижу кнопки прикрепит файл. Мож я маленький есчё?
Тада шлю на мыло.

Автор: GORI 31.10.2005, 09:44
Если использовать SynPHPSyn1 то все работает, а если SynUni и подгрузить лайтер для PHP то нет.

В чем причина?

Автор: Vitalik 31.10.2005, 17:28
Цитата(Coriolis @ 30.10.2005, 19:43)
Тяяяяк. Не вижу кнопки прикрепит файл. Мож я маленький есчё?

Эта кнопка должна появляться после предварительного просмотра сообщения smile


Цитата(GORI @ 31.10.2005, 09:44)
Если использовать SynPHPSyn1 то все работает, а если SynUni и подгрузить лайтер для PHP то нет.
В чем причина?

Значится так...
Coriolis и GORI, можете модифицировать код следующим образом:
Цитата
  ... // всё, что выше - оставляем без изменений

  if ((Pos2.char = 0) and (Pos2.line = 0)) or SynEdit.SelAvail {or (Length(Token) <> 1)} then
    Exit;

  Bracket1 := SynEdit.Lines[Pos1.Line-1][Pos1.Char];
  if Bracket1 in BracketSet then begin
    ...

Теперь скобки будут подсвечиваться абсолютно везде, где будут найдены smile
Потому как теперь мы будет отталкиваться не от найденной лексемы (Token), а от реальных симоволов в тексте (Lines) smile
Добавлено @ 17:32
Цитата(Coriolis @ 30.10.2005, 19:43)
Тада шлю на мыло.

Да, письмо получил smile
Когда подсветка будет готова можешь выложить её на сайте http://www.unihighlighter.com/index.php?download=highlighters smile

Автор: Coriolis 31.10.2005, 19:20
Ой, не в ту тему написал! сорри.

Автор: GORI 1.11.2005, 00:18
Блин smile Ну не работает оно с UniHighlighter... Можете примерчик простенький набросать? чтоб именно с загрузкой лайтера из файла...

smile Виталик самый щедрый на примеры smile smile

Автор: Vitalik 1.11.2005, 01:04
Заказывали примерчик? smile

Автор: Vitalik 1.11.2005, 01:07
А вот вместе с EXE smile
(жаль нельзя к одному сообщению несколько файлов прикрепить)

Автор: Coriolis 1.11.2005, 12:43
Ок, спасибо за пример. Я его только поправил - чтобы не светилась скобка, которая находится за перед курсором.

Тут мне Snowy указал на глюк: алгоритм подсветки НЕ игнорирует скобки внутри кавычек.
Чего-то не пойму, как это профиксить...

Автор: Vitalik 2.11.2005, 15:44
Цитата(Coriolis @ 1.11.2005, 12:43)
Тут мне Snowy указал на глюк: алгоритм подсветки НЕ игнорирует скобки внутри кавычек.
Чего-то не пойму, как это профиксить...

Гм... Так сходу ничего и получается... smile

Есть как минимум два пути:

1. Переписать метод SynEdit.GetMatchingBracketEx под свои нужды. Например, вставить нужные фильтры. Как то, игнорирование скобок во всех диапазонах или в диапазонах с определёнными именнами или еще что-нить smile

2. Сделать возможность в компоненте задавать свойство StringAttribute (которое объявлено только для чтения у потомка всех подсветок TCustomHighlighter). Просто в реализации метода SynEdit.GetMatchingBracketEx уже есть фильтрация диапазонов строки и комментария. Нужно лишь реализовать возможность в нашем компоненте задавать какие именно диапазоны (а точнее их атрибуты) являются диапазонами "строки" и "комментария" smile

Автор: zvyagaaa 1.6.2009, 05:17
а можно ли как-то сделать, чтобы парные скобки выделялись бы еще и разными цветами.? Как в экселе в строке формул?
поясню, к примеру (((1/2)*2)-2)

Автор: Coriolis 2.6.2012, 23:12
Кстати да, крутая штука. 
zvyagaaa, ты если реализовал - поделись пожалуйста)

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)