PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » PureBasic для Windows » Marker (инструмент)


Marker (инструмент)

Сообщений 1 страница 11 из 11

1

Тема и исходник на оф.форуме
Заинтересовала идея как во многих редакторах подсветить выделенный текст, тем более что проба показала что это работает. Но в итоге столкнулся с тем что подсвечивает случайным образом, то есть как надо но не всё, а от начала до средины или меньше, каждый раз по разному, может вообще одно в начале подсветить, а может до 70% но никогда чтобы всё. Код уже рабочий, скомпилированный и проверенный как exe-файл, но тестить можно просто запуская с отладкой.
Может брать параметры (заголовок, класс окна, слово, путь), а может и без параметров работать через переменные (GetEnvironmentVariable). Я тестирую без параметров, в этом случае нет переменной пути ион берётся с заголовка, а если в заголовке не отображается путь, то берёт имя и ищет в списке путей (переменная PB_TOOL_FileList) нужный путь в котором есть имя файла из заголовка.
Сначала хотел сделать через SCI_SEARCHINTARGET непосредственно в окне Scintilla, но это не работало, как обычно проблема с функциями, которые работают с памятью и чужой процесс им недоступен, а значит помучившись забросил этот способ. И перешёл к способу открытия исходника как файла и поиск в нём и потом получая байтовую позицию для функции индикатора.
Так как индикатор выделяет в среднем 50% то можно сказать что проблемы позиций нет, так как пробовал SCI_GOTOPOS (закомментированная строк) и это стабильно перемещает по всем позициям в точности, остаётся только сбой функции индикатора. Задержку Delay() вставлял, не помогает. В общем идей больше нет, а инструмент вполне мог бы получится неплохой, внутри IDE он точно бы работал.

0

2

Получается, что код использует Scintilla ? Лишняя примочка.

0

3

AZJIO написал(а):

а инструмент вполне мог бы получится неплохой

Быть может ещё получится:

Код:
; #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)

0

4

Сделал как инструмент: скачать
видео как это работает.

https://www.upload.ee/image/17200669/Marker.png

Обновление
Добавлена кнопка "+" для добавления индикатора и параметр AutoAddIndic=1 для автоматического добавления при выборе нового цвета.
Добавлена кнопка выбора цвета
Добавлена кнопка "Далее", чтобы перемещаться по найденным элементам.
Ускорил поиск за счёт вычисления позиции не от начала каждый раз, а от предыдущего вхождения.

Отредактировано AZJIO (05.10.2024 01:50:58)

0

5

Честное слово: выглядит как попытка повторить VS Code.

0

6

Замабувараев написал(а):

Честное слово: выглядит как попытка повторить VS Code.

Пометки есть в любом редакторе AkelPad, Notepad++, RJ TextEd. Сам движок Scintilla поддерживает это программно. Когда VS Code в помине не было.

Кстати, плаг Highlight для Notepad++ я уже делал, почему бы тогда не сделать для PureBasic? Проблема что в плаге был прямой доступ. А тут вопрос, нужен ли такой плаг для PureBasic, нужно ли это делать инструментом. Было бы лучше сделать это внутри IDE поддерживаемым из коробки, минимум хотя бы отдельную кнопку "пометки" вместо "закрыть", а максимум отдельное окно с возможностью задать цвет.

Отредактировано AZJIO (06.10.2024 13:15:33)

0

7

Всё-таки с задержкой бывают пропуски, особенно на большом количестве кода. Вместо 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

0

8

Webarion
Использовал твой цикл, с задержкой было не то, прикинул 500 вхождений с задержкой от 10 мсек, получаем 5 секунд ожидания на пустом месте.
И ещё у тебя старая версия, в новой индикатор в списке indicatorList(), что позволяет подсвечивать разными цветами.

Отредактировано AZJIO (07.10.2024 18:44:18)

0

9

Ещё несколько моментов. Желательно при запуске определять, какие индикаторы, остались отмечены после прошлых запусков инструмента. Это позволит очистить все эти прошлые индикаторы:

Код:
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)

0

10

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)

0

11

AZJIO написал(а):

INDICATOR_MAX(43), раньше был INDIC_MAX (35)

Индикатор больше 42 сейчас не работает.

AZJIO написал(а):

Функция чтения данных у меня была

Слишком замудрённое описание в документации у SCI_GETCHARACTERPOINTER, я предпочёл стандартное и более понятное чтение через SCI_GETTEXT. Надо исходники просматривать. Хотя, конечно читать из стороннего процесса без выделения там памяти, интересный вариант, SCI_GETTEXT этого не позволяет.

0

Похожие темы


Вы здесь » PureBasic - форум » PureBasic для Windows » Marker (инструмент)