Тема и исходник на оф.форуме
Заинтересовала идея как во многих редакторах подсветить выделенный текст, тем более что проба показала что это работает. Но в итоге столкнулся с тем что подсвечивает случайным образом, то есть как надо но не всё, а от начала до средины или меньше, каждый раз по разному, может вообще одно в начале подсветить, а может до 70% но никогда чтобы всё. Код уже рабочий, скомпилированный и проверенный как exe-файл, но тестить можно просто запуская с отладкой.
Может брать параметры (заголовок, класс окна, слово, путь), а может и без параметров работать через переменные (GetEnvironmentVariable). Я тестирую без параметров, в этом случае нет переменной пути ион берётся с заголовка, а если в заголовке не отображается путь, то берёт имя и ищет в списке путей (переменная PB_TOOL_FileList) нужный путь в котором есть имя файла из заголовка.
Сначала хотел сделать через SCI_SEARCHINTARGET непосредственно в окне Scintilla, но это не работало, как обычно проблема с функциями, которые работают с памятью и чужой процесс им недоступен, а значит помучившись забросил этот способ. И перешёл к способу открытия исходника как файла и поиск в нём и потом получая байтовую позицию для функции индикатора.
Так как индикатор выделяет в среднем 50% то можно сказать что проблемы позиций нет, так как пробовал SCI_GOTOPOS (закомментированная строк) и это стабильно перемещает по всем позициям в точности, остаётся только сбой функции индикатора. Задержку Delay() вставлял, не помогает. В общем идей больше нет, а инструмент вполне мог бы получится неплохой, внутри IDE он точно бы работал.
Marker (инструмент)
Сообщений 1 страница 11 из 11
Поделиться129.07.2024 23:34:38
Поделиться230.07.2024 13:24:32
Получается, что код использует Scintilla ? Лишняя примочка.
Поделиться302.10.2024 09:39:47
а инструмент вполне мог бы получится неплохой
Быть может ещё получится:
; #SCI_PBTOOL# ======================================================================================================================= ; Description .: Scintilla PureBasic Tool - this is for working with Scintilla in a third party process ; Author ......: Webarion ; Link ........: https://github.com/webarion ; Created and tested on .: PureBasic6.10; Windows10x64 ; ==================================================================================================================================== ; Примечание ..: Здесь только пример для SCI_SEARCHINTARGET EnableExplicit ;- Structures Structure TextObject *Pointer Format.u LenBytes.i EndStructure Structure Examine_Search *SciMem_Search LenSearchBytes.i Current_Len_AllText.i Search_StartPos.i Search_EndPos.i Found_StartPos.i Found_EndPos.i EndStructure ;- Variables Global PB_TOOL_MainWindow, PB_TOOL_ProcessID, PB_TOOL_Scintilla, ProcessID Global classText.s = Space(256) Global *Current_Examine_Search.Examine_Search = 0 ;- Procedures ; найти окно редактора ; Взято у AZJIO Procedure.l enumChildren0(hwnd.l) If hwnd GetClassName_(hwnd, @classText, 256) If classText = "WindowClass_2" GetWindowText_(hwnd, @classText, 256) If FindString(classText, "PureBasic") PB_TOOL_MainWindow = hwnd ProcedureReturn 0 EndIf EndIf ProcedureReturn 1 EndIf ProcedureReturn 0 EndProcedure ; найти Scintilla ; Взято у AZJIO Procedure.l enumChildren1(hwnd.l) If hwnd GetClassName_(hwnd, @classText, 256) If classText = "Scintilla" PB_TOOL_Scintilla = hwnd ProcedureReturn 0 EndIf ProcedureReturn 1 EndIf ProcedureReturn 0 EndProcedure Procedure GetProcessFromWindow(Hwnd) Protected PID, Result If GetWindowThreadProcessId_(Hwnd, @PID) Result = OpenProcess_( #PROCESS_ALL_ACCESS, #False, PID ) EndIf ProcedureReturn Result EndProcedure CompilerIf #PB_Compiler_Debugger EnumChildWindows_(0, @enumChildren0(), 0) EnumChildWindows_(PB_TOOL_MainWindow, @enumChildren1(), 0) CompilerElse ; for PureBasic Tool PB_TOOL_MainWindow = Val(GetEnvironmentVariable("PB_TOOL_MainWindow")) If Not PB_TOOL_MainWindow : End : EndIf PB_TOOL_Scintilla = Val(GetEnvironmentVariable("PB_TOOL_Scintilla")) If Not PB_TOOL_Scintilla : End : EndIf CompilerEndIf ProcessID = GetProcessFromWindow(PB_TOOL_Scintilla) If Not ProcessID : End : EndIf ;-. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ; Получает кодировку Scintilla и возвращает в формате принятом в PureBasic. На данный момент поддерживается только #PB_UTF8 и #PB_Ascii Procedure _Sci_CodePage_To_EncodingFormatPB() Select SendMessage_(PB_TOOL_Scintilla, #SCI_GETCODEPAGE, #Null, #Null) Case 65001 : ProcedureReturn #PB_UTF8 EndSelect ProcedureReturn #PB_Ascii EndProcedure ; преобразует стандартную строку PB (Unicode), в строку кодировки текущего документа Scintilla ; возвращает объект текста в указанную структуру: [ указатель | кодировка в PB формате | длина в байтах ] ; если кодировка отличается от #PB_Unicode, вызывающему самостоятельно нужно позаботиться об освобождении памяти *TextObj\Pointer (на данный момент, это нужно делать всегда, когда *TextObj больше не нужен) Procedure _MakeScintillaText( Text$, *TextObj.TextObject ) With *TextObj \Format = _Sci_CodePage_To_EncodingFormatPB() Select \Format Case #PB_UTF8 \Pointer = UTF8(Text$) \LenBytes = MemorySize(\Pointer) - 1 ; нулевой символ тут не нужен Case #PB_Unicode ; TODO на будущее (эта кодировка в данной библиотеке, в работе с Scintilla, пока не используется) \Pointer = @Text$ \LenBytes = Len(Text$) * 2 Default \Pointer = Ascii(Text$) \LenBytes = MemorySize(\Pointer) - 1 EndSelect EndWith EndProcedure ;-............................................................................................................................................ ; сбрасывает, либо устанавливает диапазон поиска для перебора *Examine.Examine_Search Procedure Sci_ResetExamine_Search( *Examine.Examine_Search, SearchStartPos.i = 0, SearchEndPos.i = -1 ) With *Examine \Found_StartPos = 0 \Current_Len_AllText = SendMessage_( PB_TOOL_Scintilla, #SCI_GETTEXTLENGTH, 0, 0 ) ; получает всю длину текста в Scintilla \Search_StartPos = SearchStartPos If SearchEndPos = -1 : SearchEndPos = \Current_Len_AllText : EndIf \Search_EndPos = SearchEndPos EndWith EndProcedure ; Инициализирует перебор поиска. Также выделяет в стороннем процессе память с необходимыми данными Procedure Sci_Examine_SearchInTarget( Search$, Flags = 0, StartPos = 0, EndPos = -1 ) If Not *Current_Examine_Search Protected SearchObj.TextObject _MakeScintillaText( Search$, @SearchObj ) If SearchObj\LenBytes Protected *SciMem_Search = VirtualAllocEx_( ProcessID, 0, SearchObj\LenBytes, #MEM_RESERVE | #MEM_COMMIT, #PAGE_EXECUTE_READWRITE ) If *SciMem_Search And WriteProcessMemory_( ProcessID, *SciMem_Search, SearchObj\Pointer, SearchObj\LenBytes, #Null ) SendMessage_( PB_TOOL_Scintilla, #SCI_SETSEARCHFLAGS, Flags, 0 ) *Current_Examine_Search = AllocateStructure(Examine_Search) With *Current_Examine_Search \SciMem_Search = *SciMem_Search \LenSearchBytes = SearchObj\LenBytes Sci_ResetExamine_Search( *Current_Examine_Search ) EndWith If SearchObj\Format <> #PB_Unicode : FreeMemory(SearchObj\Pointer) : EndIf ProcedureReturn *Current_Examine_Search EndIf EndIf EndIf ProcedureReturn 0 EndProcedure ; освобождает ресурсы, связанные с конкретным перебором поиска (в том числе выделенную память в стороннем процессе) Procedure Sci_FreeExamine_SearchInTarget( *Examine.Examine_Search = 0 ) If Not *Examine *Examine = *Current_Examine_Search EndIf VirtualFreeEx_( ProcessID, *Examine\SciMem_Search, *Examine\LenSearchBytes, #MEM_RELEASE ) FreeStructure(*Examine) If *Examine = *Current_Examine_Search *Current_Examine_Search = 0 EndIf EndProcedure ; Ищет следующий элемент поиска ; Когда FreeExamine = #True (по умолчанию) - если ничего не найдено, память связанная с *Examine.Examine_Search будет осовобождена, включая выделенную память в процессе содержащем Scintilla ; Возвращает позицию найденного Procedure Sci_Next_SearchInTarget( *Examine.Examine_Search = 0, FreeExamine.a = #True ) If Not *Examine *Examine = *Current_Examine_Search EndIf With *Examine SendMessage_( PB_TOOL_Scintilla, #SCI_SETTARGETSTART, \Search_StartPos, 0 ) SendMessage_( PB_TOOL_Scintilla, #SCI_SETTARGETEND, \Search_EndPos, 0 ) \Found_StartPos = SendMessage_( PB_TOOL_Scintilla, #SCI_SEARCHINTARGET, \LenSearchBytes, \SciMem_Search ) If \Found_StartPos <> -1 ; найдено \Found_EndPos = SendMessage_( PB_TOOL_Scintilla, #SCI_GETTARGETEND, 0, 0 ) \Search_StartPos = \Found_EndPos + 1 ElseIf FreeExamine Sci_FreeExamine_SearchInTarget( *Examine ) EndIf ProcedureReturn \Found_StartPos EndWith EndProcedure ; Гарантированный способ, отметить индикатор Scintilla в стороннем процессе Procedure Sci_IndicatorFillRange( Indicator, StartPos, LenBytes, MaxIterations = 10 ) Protected Iteration = 0 SendMessage_(PB_TOOL_Scintilla, #SCI_SETINDICATORCURRENT, Indicator, 0) SendMessage_(PB_TOOL_Scintilla, #SCI_INDICATORFILLRANGE, StartPos, LenBytes) While SendMessage_(PB_TOOL_Scintilla, #SCI_GETINDICATORCURRENT, #Null, #Null) <> Indicator SendMessage_(PB_TOOL_Scintilla, #SCI_SETINDICATORCURRENT, Indicator, #Null) SendMessage_(PB_TOOL_Scintilla, #SCI_INDICATORFILLRANGE, StartPos, LenBytes) If Iteration > MaxIterations DebuggerWarning("The indicator position is actively overwritten by Scintilla's own process. Try increasing the MaxIterations value") Break EndIf Iteration + 1 Wend EndProcedure ; Гарантированный способ, очистить всё отмеченное индикатором Indicator в Scintilla стороннего процесса Procedure Sci_IndicatorClearRange( Indicator, MaxIterations = 10 ) Protected Iteration = 0 SendMessage_(PB_TOOL_Scintilla, #SCI_SETINDICATORCURRENT, Indicator, 0) SendMessage_(PB_TOOL_Scintilla, #SCI_INDICATORCLEARRANGE, 0, SendMessage_(PB_TOOL_Scintilla, #SCI_GETLENGTH, 0, 0)) While SendMessage_(PB_TOOL_Scintilla, #SCI_GETINDICATORCURRENT, #Null, #Null) <> Indicator SendMessage_(PB_TOOL_Scintilla, #SCI_SETINDICATORCURRENT, Indicator, 0) SendMessage_(PB_TOOL_Scintilla, #SCI_INDICATORCLEARRANGE, 0, SendMessage_(PB_TOOL_Scintilla, #SCI_GETLENGTH, 0, 0)) If Iteration > MaxIterations DebuggerWarning("The indicator position is actively overwritten by Scintilla's own process. Try increasing the MaxIterations value") Break EndIf Iteration + 1 Wend EndProcedure ;- EXAMPLE. ПРИМЕР ........................................................................................................................... #num_indicator = 19 SendMessage_(PB_TOOL_Scintilla, #SCI_INDICSETSTYLE, #num_indicator, #INDIC_STRAIGHTBOX ) ; индикатор со стилем INDIC_STRAIGHTBOX SendMessage_(PB_TOOL_Scintilla, #SCI_INDICSETFORE, #num_indicator, #Red) ; красный цвет SendMessage_(PB_TOOL_Scintilla, #SCI_INDICSETUNDER, #num_indicator, 1) ; индикатор под текстом SendMessage_(PB_TOOL_Scintilla, #SCI_INDICSETALPHA, #num_indicator, 127) ; Прозрачность Define Reset.a = #False ; Reset = #True - сбрасывает всё отмеченное индикатором #num_indicator If Reset Sci_IndicatorClearRange( #num_indicator ) ; сброс всего отмеченного индикатором #num_indicator Else Sci_Examine_SearchInTarget( "Sen\w+age_", #SCFIND_REGEXP ) ; пример поиска по регулярке While Sci_Next_SearchInTarget() <> -1 With *Current_Examine_Search Sci_IndicatorFillRange( #num_indicator, \Found_StartPos, \Found_EndPos - \Found_StartPos ) ; отмечаем найденное EndWith Wend EndIf
Отредактировано Webarion (02.10.2024 10:22:14)
Поделиться404.10.2024 16:46:34
Сделал как инструмент: скачать
видео как это работает.
Обновление
Добавлена кнопка "+" для добавления индикатора и параметр AutoAddIndic=1 для автоматического добавления при выборе нового цвета.
Добавлена кнопка выбора цвета
Добавлена кнопка "Далее", чтобы перемещаться по найденным элементам.
Ускорил поиск за счёт вычисления позиции не от начала каждый раз, а от предыдущего вхождения.
Отредактировано AZJIO (05.10.2024 01:50:58)
Поделиться506.10.2024 04:27:57
Честное слово: выглядит как попытка повторить VS Code.
Поделиться606.10.2024 11:49:17
Честное слово: выглядит как попытка повторить VS Code.
Пометки есть в любом редакторе AkelPad, Notepad++, RJ TextEd. Сам движок Scintilla поддерживает это программно. Когда VS Code в помине не было.
Кстати, плаг Highlight для Notepad++ я уже делал, почему бы тогда не сделать для PureBasic? Проблема что в плаге был прямой доступ. А тут вопрос, нужен ли такой плаг для PureBasic, нужно ли это делать инструментом. Было бы лучше сделать это внутри IDE поддерживаемым из коробки, минимум хотя бы отдельную кнопку "пометки" вместо "закрыть", а максимум отдельное окно с возможностью задать цвет.
Отредактировано AZJIO (06.10.2024 13:15:33)
Поделиться706.10.2024 22:15:16
Всё-таки с задержкой бывают пропуски, особенно на большом количестве кода. Вместо Delay, лучше проверить, установился ли индикатор в позицию, если нет, то повторить установку. Это и работает быстрее и гарантированно отмечает все позиции индикатора:
; Delay(Delay1) While Not SendMessage_( ScintillaHandle, #SCI_INDICATORVALUEAT, #num_indicator, BytePos ) SendMessage_(ScintillaHandle, #SCI_SETINDICATORCURRENT, #num_indicator, 0) SendMessage_(ScintillaHandle, #SCI_INDICATORFILLRANGE, BytePos, len2) Wend
Поделиться807.10.2024 18:42:59
Webarion
Использовал твой цикл, с задержкой было не то, прикинул 500 вхождений с задержкой от 10 мсек, получаем 5 секунд ожидания на пустом месте.
И ещё у тебя старая версия, в новой индикатор в списке indicatorList(), что позволяет подсвечивать разными цветами.
Отредактировано AZJIO (07.10.2024 18:44:18)
Поделиться908.10.2024 08:52:31
Ещё несколько моментов. Желательно при запуске определять, какие индикаторы, остались отмечены после прошлых запусков инструмента. Это позволит очистить все эти прошлые индикаторы:
AddIndicator() ; Найдём все индикаторы отмеченные в прошлых сессиях Define i For i = 20 To 42 ; поиск с 20-го, так как индикатор 19 уже добавлен по умолчанию через AddIndicator() If SendMessage_(ScintillaHandle, #SCI_INDICATOREND, i, 0 ) AddElement( indicatorList() ) : indicatorList() = i EndIf Next ; ... ;-┌──GUI──┐ ; ...
Если понадобится читать документ не из файла, а напрямую из Scintilla:
Procedure.s Sci_GetText() Protected PID, Text$ GetWindowThreadProcessId_(ScintillaHandle, @PID) Protected hProcess = OpenProcess_( #PROCESS_ALL_ACCESS, #False, PID ) If hProcess Protected LenBytes = SendMessage_( ScintillaHandle, #SCI_GETTEXT, #Null, #Null ) ; получаем всю длину текста в байтах Protected *SciMemory = VirtualAllocEx_( hProcess, #Null, LenBytes, #MEM_RESERVE | #MEM_COMMIT, #PAGE_EXECUTE_READWRITE ) If *SciMemory ; если память выделена SendMessage_( ScintillaHandle, #SCI_GETTEXT, LenBytes, *SciMemory ) ; получаем текст в выделенной памяти стороннего процесса Protected *ThisBuffer = AllocateMemory( LenBytes + 1 ) If *ThisBuffer And ReadProcessMemory_( hProcess, *SciMemory, *ThisBuffer, LenBytes, #Null ) ; читаем текст из стороннего процесса в текущий Protected Format = #PB_Ascii If SendMessage_( ScintillaHandle, #SCI_GETCODEPAGE, #Null, #Null ) Format = #PB_UTF8 | #PB_ByteLength EndIf Text$ = PeekS( *ThisBuffer, LenBytes, Format ) FreeMemory(*ThisBuffer) ; освобождаем текущий буфер EndIf VirtualFreeEx_( hProcess, *SciMemory, LenBytes, #MEM_RELEASE ) ; освобождаем выделенную память в стороннем процессе EndIf CloseHandle_(hProcess) EndIf ProcedureReturn Text$ EndProcedure
После цикла очистки, список индикаторов лучше очистить и добавить первый:
ClearList( indicatorList() ) : AddIndicator()
Ну и позаботиться о том, что номер индикатора не может быть больше 42.
Отредактировано Webarion (08.10.2024 09:37:26)
Поделиться1008.10.2024 10:03:38
Webarion
INDICATOR_MAX(43), раньше был INDIC_MAX (35)
Функция чтения данных у меня была, надо смотреть чем она отличается от твоей и вспоминать почему я отказался от неё.
Procedure.s GetScintillaText() Protected ReturnValue.s Protected length Protected buffer Protected processId Protected hProcess Protected result length = SendMessage_(ScintillaHandle, #SCI_GETLENGTH, 0, 0) If length length + 2 buffer = AllocateMemory(length) If buffer SendMessageTimeout_(ScintillaHandle, #SCI_GETCHARACTERPOINTER, 0, 0, #SMTO_ABORTIFHUNG, 2000, @result) If result GetWindowThreadProcessId_(ScintillaHandle, @processId) hProcess = OpenProcess_(#PROCESS_ALL_ACCESS, #False, processId) If hProcess ReadProcessMemory_(hProcess, result, buffer, length, 0) ReturnValue = PeekS(buffer, -1, #PB_UTF8) CloseHandle_(hProcess) ; <-- Axolotl, added acc. to MSDN EndIf EndIf EndIf FreeMemory(buffer) EndIf ProcedureReturn ReturnValue EndProcedure
На счёт остального надо посмотреть. У меня уже возникает желание создать полноценный как для Notepad++ я делал, поэтому пока не уверен. Я уже начал делать и уже сделал поменял ButtonImageGadget на ImageGadget, так как последний может обрабатывать правый клик, на левый сделал меню выбором готовых цветов, на правый клик вызов виндового, при этом хочу выбор виндового вставить в само меню пунктов. Но как сделать чтобы не конфликтовать с генерируемыми новыми цветами. Надо изначально системный подход, но для этого надо определиться оставить инструмент примитивным или сделать его полноценным с выбором стиля индикатора, с ini-файл где будет номер_индикатора_цвет, с регулировкой цвета в реальном времени. И при этом ещё желание вставить это в исходный код IDE создав в нём новый файл pb и встроить в меню, так как только в этом случае будет обеспечена поддержка в Linux. В общем пока не знаю как пойдёт, разве что для сегодняшнего момента подкорректировать исправность кода.
Я добавил в архив экспериментальный код с ПКМ, но если делать полноценный, то этот выбор был через комбо и все эти эксперименты бесполезны.
Отредактировано AZJIO (08.10.2024 11:23:50)
Поделиться1108.10.2024 22:43:50
INDICATOR_MAX(43), раньше был INDIC_MAX (35)
Индикатор больше 42 сейчас не работает.
Функция чтения данных у меня была
Слишком замудрённое описание в документации у SCI_GETCHARACTERPOINTER, я предпочёл стандартное и более понятное чтение через SCI_GETTEXT. Надо исходники просматривать. Хотя, конечно читать из стороннего процесса без выделения там памяти, интересный вариант, SCI_GETTEXT этого не позволяет.
Похожие темы
Миди синтезатор | PureBasic для Windows | 03.10.2021 |