Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
 SynEdit и подсветка текущего выделенного слова по документу  [new]
DelDelphi
Member

Откуда:
Сообщений: 13
Друзья, кто нибудь делал такое:
http://www.freepascal.ru/forum/viewtopic.php?f=44&t=7746&sid=ef6b81c47b1fe6b001bbc706a42111ac

Только в Delphi? Крайне не хочется портировать lazarus вариант, так как зависимостей там сотни и по простому вряд ли получится. Может кто-то уже делал подобное в Delphi?
2 ноя 21, 22:21    [22391453]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
Кроик Семён
Member

Откуда: СПб --> Dortmund
Сообщений: 6915
На вскидку, начал бы искать в 2-ух направлениях:
- какое-нибудь событие, получающее на вход, скажем, очередное слово из сканируемого документа и возвращающее атрибуты текста
- либо, что лучше, возможность добавить зарезервированное слово, которое есть выделяемый текст
2 ноя 21, 22:29    [22391455]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
DelDelphi
Member

Откуда:
Сообщений: 13
Тут скорее вопрос в том, есть ли что-то уже готовое и отлаженное. Сделать то велосипед можно, но займет немало времени, так как нужно чтобы работал на больших листингах и не тормозил приложение при постоянных щелчках по тексту туда-сюда. При этом адекватно работало обычное выделение. А это не "просто добавить кейворд" к сожалению.
3 ноя 21, 00:50    [22391466]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
Softologic
Member

Откуда: Питер
Сообщений: 393
DelDelphi
Тут скорее вопрос в том, есть ли что-то уже готовое и отлаженное. Сделать то велосипед можно, но займет немало времени, так как нужно чтобы работал на больших листингах и не тормозил приложение при постоянных щелчках по тексту туда-сюда. При этом адекватно работало обычное выделение. А это не "просто добавить кейворд" к сожалению.

Готовое и отлаженное решение, не тормозит: https://delphihtmlcomponents.com/index.html
Покрывает вашу задачу с легкостью на больших листингах, ну и много чего еще "из каропки". В вашем случае достаточно HTML Component Library, который стоит $349.

Сообщение было отредактировано: 3 ноя 21, 01:03
3 ноя 21, 01:01    [22391468]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
Softologic
Member

Откуда: Питер
Сообщений: 393
Две строчки кода:

MailView.Doc.HighlightText := AnsiLowerCase(fSearchText);
MailView.Refresh();


Ниже результат - почтовый клиент на базе вышеупомянутого HTML Components (Bundle).

К сообщению приложен файл. Размер - 133Kb


Сообщение было отредактировано: 3 ноя 21, 01:16
3 ноя 21, 01:13    [22391470]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
Softologic
Member

Откуда: Питер
Сообщений: 393
Точнее хотел сказать, что результат выполнения двух строчек кода представлен желтым выделенным фрагментом на скрине проекта. Вот :)
3 ноя 21, 01:18    [22391471]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
Softologic
Member

Откуда: Питер
Сообщений: 393
Еще есть вариант подешевле - https://www.trichview.ru/features/trichview.html
Не помню досконально, но вероятнее всего там тоже есть функция выделения повторяющегося фрагмента во всем тексте.

Сообщение было отредактировано: 3 ноя 21, 01:38
3 ноя 21, 01:38    [22391474]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
DelDelphi
Member

Откуда:
Сообщений: 13
Softologic,

К сожалению ваши варианты вообще не к месту, так как в продукте уже реализован SynEdit с кастомно написанными хайлайтерами под разные языки, поддерживаемые нашим продуктом. Плюс море кастомных плюшек типа CodeFolding. Задача стоит добавить подсветку выделенных кейвордов по всему листингу используя именно SynEdit. Смена компонента не рассматривается.
3 ноя 21, 01:43    [22391475]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
Softologic
Member

Откуда: Питер
Сообщений: 393
DelDelphi
Softologic,

К сожалению ваши варианты вообще не к месту, так как в продукте уже реализован SynEdit с кастомно написанными хайлайтерами под разные языки, поддерживаемые нашим продуктом. Плюс море кастомных плюшек типа CodeFolding. Задача стоит добавить подсветку выделенных кейвордов по всему листингу используя именно SynEdit. Смена компонента не рассматривается.

Ну ок, если так. Просто изначально было не понятно, что все только в пределах SynEdit
3 ноя 21, 01:54    [22391476]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
ma1tus
Member

Откуда:
Сообщений: 811
DelDelphi, пример
3 ноя 21, 05:36    [22391479]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
DelDelphi
Member

Откуда:
Сообщений: 13
Softologic
Ну ок, если так. Просто изначально было не понятно, что все только в пределах SynEdit


Я потому сразу ссылку на Lazarus форум дал, так как нужен аналог. Задача в листингах до пары мегабайт щелкать по опкодам и операндам и чтобы аналогичные подсвечивались по всему тексту. Тупой search и подсветка тут непокатят - повиснет все после тысячного найденного eax или ebx (как пример). Надеялся что есть что-то уже отлаженное нативное, которое следит за скроллом, кареткой, перемещениями по тексту и подсвечивает асинхронно блоками динамически.

ma1tus, благодарю. Погляжу.
3 ноя 21, 11:20    [22391542]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
kapas
Member

Откуда:
Сообщений: 54
В SynEdit для подобных вещей существует спец. класс TSynEditPlugin

С ним работать достаточно просто:

1. Определяем нужный тип
    TSelectTextMarkupHighlighter = class(TSynEditPlugin)
    private
        FAttribute: TSyntaxHighlighterAttribute;
        FEnabled: boolean;
        FWholeWordOnly: boolean;
        FCaseSensitivity: boolean;
    protected
      function GetEnabled(): boolean; overload; virtual;
      procedure SetEnabled(const AValue: boolean); overload; virtual;
      function GetWholeWordOnly(): boolean; overload; virtual;
      procedure SetWholeWordOnly(const AValue: boolean); overload; virtual;
      function GetCaseSensitivity(): boolean; overload; virtual;
      procedure SetCaseSensitivity(const AValue: boolean); overload; virtual;
    protected
      procedure AfterPaint(ACanvas: TCanvas; const AClip: TRect; AFirstRow, ALastRow: Integer); override;
    public
      procedure AfterConstruction(); override;
      destructor Destroy(); override;
    published
        property Attribute: TSyntaxHighlighterAttribute read FAttribute write FAttribute;
        property Enabled: boolean read GetEnabled write SetEnabled;
        property WholeWordOnly: boolean read GetWholeWordOnly write SetWholeWordOnly;
        property CaseSensitivity: boolean read GetCaseSensitivity write SetCaseSensitivity;
    end;

или
    TBlockMarker = class(TSynEditPlugin)
....
    protected
      procedure AfterPaint(ACanvas: TCanvas; const AClip: TRect; AFirstRow, ALastRow: integer); overload; override;
      procedure LinesInserted(AFirstRow, ACountRows: integer); overload; override; // обрабатываем вставки
      procedure LinesDeleted(AFirstRow, ACountRows: integer); overload; override;  // обрабатываем удаления
....
    public
//      procedure AfterConstruction(); override; // необязательно

    end;


2. Главная процедура (привожу рабочий вариант)
+

      procedure TSelectTextMarkupHighlighter.AfterPaint(ACanvas: TCanvas; const AClip: TRect; AFirstRow, ALastRow: Integer);
      var
        AStoreFont: TFont;
        AStoreBrush: TBrush;
        ASelectText: string;
        ACurrPt: TPoint;
        ARect: TRect;
        AIndexRow: Integer;
        ACurrCoord: TBufferCoord;
        ASelectCoord: TBufferCoord;
        ASelectEndCoord: TBufferCoord;
        ASearchPos: integer;
        AOffset: integer;
        ASelectSize: integer;
        ARow: string;
        AOrgRow: string;
        AOrgText: string;
      begin
        inherited AfterPaint(ACanvas, AClip, AFirstRow, ALastRow);
        if (not Enabled) then exit;
        if (not Editor.SelAvail) then exit;
        if (Editor.SelectionMode <> smNormal) then exit;
        ASelectText := Editor.SelText;
        ASelectSize := Editor.SelLength;
        ASelectCoord := Editor.CharIndexToRowCol(Editor.SelStart);
        ASelectEndCoord := Editor.CharIndexToRowCol(Editor.SelEnd);
        if (ASelectCoord.Line <> ASelectEndCoord.Line) then exit;
// Выделение располагается в одной строке!!!
        ARow := Editor.Lines[ASelectCoord.Line - 1];
        if (WholeWordOnly and (not ASelectText.WordInRow(ASelectCoord.Char, ARow))) then exit;
// Выделение есть слово!!!
        AStoreFont := TFont.Create();
        try
          AStoreFont.Assign(ACanvas.Font);
          AStoreBrush := TBrush.Create();
          try
            AStoreBrush.Assign(ACanvas.Brush);
            if (Attribute.Background <> clNone) then
              begin
                ACanvas.Brush.Color := Attribute.Background;
                ACanvas.Brush.Style := bsSolid
              end
            else
              begin
                ACanvas.Brush.Style := bsClear;
              end;
// Внимание, очень важно: свойства ACanvas.Pen нельзя менять - иначе будут проб-
// лемы с отрисовкой CodeFolding!
            AFirstRow := Editor.TopLine;
            ALastRow := Editor.TopLine + Editor.LinesInWindow - 1;
            if (not CaseSensitivity) then
              ASelectText := ASelectText.UpperCase();
            for AIndexRow := AFirstRow to ALastRow do
              begin
                AOffset := 1;
                ARow := Editor.Lines[AIndexRow - 1];
                AOrgRow := ARow;
                if (not CaseSensitivity) then
                  begin
                    ARow := Editor.Lines[AIndexRow - 1].UpperCase();
                  end;
                while (true) do
                  begin
                    ASearchPos := System.Pos(ASelectText, ARow, AOffset);
                    if (ASearchPos <= 0) then break;
                    if ((ASearchPos = ASelectCoord.Char) and (AIndexRow = ASelectCoord.Line)) then
                      begin
                        AOffset := ASearchPos + ASelectSize;
                        continue;
                      end;
                    if (WholeWordOnly and (not ASelectText.WordInRow(ASearchPos, ARow))) then
                      begin
                        AOffset := ASearchPos + ASelectSize;
                        continue;
                      end;
                    AOrgText := System.Copy(AOrgRow, ASearchPos, ASelectSize);
                    ACurrCoord := BufferCoord(ASearchPos, AIndexRow);
                    ACurrPt := Editor.RowColumnToPixels(Editor.BufferToDisplayPos(ACurrCoord));
                    ARect := Rect(ACurrPt.X, ACurrPt.Y, ACurrPt.X + Editor.CharWidth * ASelectSize, ACurrPt.Y + Editor.LineHeight);
                    ACanvas.FillRect(ARect);
                    ACanvas.TextRect(ARect, ACurrPt.X, ACurrPt.Y, AOrgText);
                    AOffset := ASearchPos + ASelectSize;
                  end
              end;
            ACanvas.Brush.Assign(AStoreBrush);
          finally
            System.SysUtils.FreeAndNil(AStoreBrush);
          end;
          ACanvas.Font.Assign(AStoreFont);
        finally
          System.SysUtils.FreeAndNil(AStoreFont);
        end;
        Editor.InvalidateGutterLines(AFirstRow, ALastRow);
        Editor.InvalidateLines(AFirstRow, ALastRow);
      end;


или (альтернативный, кода больше....)
      procedure TBlockMarker.AfterPaint(ACanvas: TCanvas; const AClip: TRect; AFirstRow, ALastRow: integer);
      var
        ABlockMarkup: TBlockMarkup;
      begin
        if (not Enabled) then exit;
// Рисуем то что нужно...
//        for ABlockMarkup in FMarkups do
//          begin
//            if (not ABlockMarkup.Check(Editor)) then continue;
//            ABlockMarkup.Draw(Editor, ACanvas);
//          end;
      end;


3. Инициализация
      procedure TfmEditDocument.FormCreate(Sender: TObject);
      begin
.............................
        ASelectTextMarkup := TSelectTextMarkupHighlighter.Create(edTextEdit);
        with ASelectTextMarkup do
          begin
            Attribute.Background := $008FA0EF; // $008FA0EF; // $00CFAAE7; // clYellow; // clMoneyGreen; //$0078AAFF; //$0078AAFF;
            Enabled := false;
          end;
        AMarkupBlocks := TBlockMarker.Create(edTextEdit);
        with AMarkupBlocks do
          begin
            Enabled := false;
          end;
......................
 end;    


Сообщение было отредактировано: 3 ноя 21, 13:45
3 ноя 21, 13:37    [22391595]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
DelDelphi
Member

Откуда:
Сообщений: 13
kapas,

благодарю за ответ! К сожалению как вижу существует несколько форков SynEdit. Мой переработанный вариант основан на официальном sourceforge.net/projects/synedit/ (который мультиплатформенный под Delphi/Kylix). Пытался по простому интегрировать Ваш код в него - море всего не находит и в типах и в интерфейсе. Взять другой форк проблематично - так как много интегрировано собственных наработок в этот. Если есть время и желание - просьба подсказать пошагово сие внедрение. Увы без документации на интерфейсы SynEdit - равносильно это написать самому с нуля, чем внедрять то, что не внедряется.
Заранее благодарю если возникнет желание помочь.
4 ноя 21, 18:27    [22392040]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
kapas
Member

Откуда:
Сообщений: 54
DelDelphi, могу только более подробно прокомментировать код. Не сегодня... :-(
1. Сам работаю с версией https://github.com/SynEdit/SynEdit
2. Этом коде в основном все стандартное из TSynEdit. Отдельные функции можно дописать самому

Успехов.
5 ноя 21, 07:15    [22392089]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
Кроик Семён
Member

Откуда: СПб --> Dortmund
Сообщений: 6915
DelDelphi,

посмотрите примеры кода отсюда, это не оно? (https://forum.lazarus.freepascal.org/index.php?topic=19424.0)
в смысле, если по событию "выделение" определить, какая часть текста выделена и передать её в SynEdit.SetHighlightSearch(...)
5 ноя 21, 15:51    [22392220]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
ma1tus
Member

Откуда:
Сообщений: 811
не обязательно через search, не обязательно в плагине... отрисовкой только в видимых строках в его же обработчике; топорно, нативно, "на официальном"

К сообщению приложен файл (syn_hlt_ident.zip - 2Kb) cкачать

Сообщение было отредактировано: 6 ноя 21, 03:15
6 ноя 21, 03:06    [22392423]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
kapas
Member

Откуда:
Сообщений: 54
ma1tus,
Можно и так :-) Дело вкуса.

..........................
 if DoTransient then
  begin
    // plugins
    if FPlugins <> nil then
      for i := 0 to FPlugins.Count - 1 do
        TSynEditPlugin(FPlugins[i]).PaintTransient(Canvas, TransientType);
    // event
    if Assigned(FOnPaintTransient) then
    begin
      Canvas.Font.Assign(Font);
      Canvas.Brush.Color := Color;
      HideCaret;
      try
        FOnPaintTransient(Self, Canvas, TransientType);
      finally
        ShowCaret;
      end;
    end;
6 ноя 21, 07:33    [22392431]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
ma1tus
Member

Откуда:
Сообщений: 811
kapas, ну да ) Привёл упрощенный.
С т.з. декомпозиции плагин предпочтительней
6 ноя 21, 14:08    [22392505]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
DelDelphi
Member

Откуда:
Сообщений: 13
Кроик Семён,

Это форум лазарус. Да, в нем все это сделано и работает. У меня как раз вопрос как это сделать в Delphi, не портируя горы лазарусовского кода. Задача усложняется тем, что нужна динамическая подсветка огромного числа кейвордов что под курсором где щелкнул пользователь. При этом листинги порой на пару мегабайт. В Lazarus реализовано с учетом этих вещей насколько я понял. То есть адаптивная подсветка по мере скролла (и также отмена подсветки старого кейворда), а не скопом всех мегабайт. Если реализовывать по простому - будет все жутко тормозить при щелчках пользователя с кейворда на кейворд, когда каждый встречается в листинге несколько сотен или тысяч раз (операнды и регистры ассемблера). А если начать щелкать быстрее - или повесит приложение или приведет к тормозам. Это не вариант.

Пока к сожалению все что вижу из советов - либо не потянет эту задачу или требует несколько дней на на внедрение/адаптацию и тестирование, учитывая что мой форк основан на SynEdit 2016 года + был полностью адаптирован под юникод (для продукта критичен корректный показ строк из иероглифов, строк справа налево и так далее со всем функционалом SynEdit. К сожалению взять другой форк не получится - много своих продуктозависимых вещей добавлено и исправлено. Мерджить десятки файлов и переписывать сотню функций - тестирования и отладки на месяц.
6 ноя 21, 19:32    [22392600]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
DelDelphi
Member

Откуда:
Сообщений: 13
ma1tus,
kapas,
Вот это уже гораздо ближе, благодарю парни! Надо проверить на своей задаче. Отпишу.
6 ноя 21, 19:42    [22392603]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
DelDelphi
Member

Откуда:
Сообщений: 13
ma1tus,

Благодарю! Именно то что искал!
6 ноя 21, 19:50    [22392607]     Ответить | Цитировать Сообщить модератору
 Re: SynEdit и подсветка текущего выделенного слова по документу  [new]
SergSni
Member

Откуда:
Сообщений: 7
Я как-то имел опыт использования PaintTransient и ...
Есть там ряд нюансов при рисовании. Например, надо следить чтобы не нарисовать на Gutter-e
А при перемещении курсора по строкам это событие всегда вызывается и как следствие все эти выделенные куски мигают при перерисовке. Мелочь, но неприятно и это я так и не победил
23 ноя 21, 21:43    [22399965]     Ответить | Цитировать Сообщить модератору
Все форумы / Delphi Ответить