Show all occurrences of a word in the IDE

Working on new editor enhancements?
AZJIO
Addict
Addict
Posts: 1316
Joined: Sun May 14, 2017 1:48 am

Re: Show all occurrences of a word in the IDE

Post by AZJIO »

1. Added black color for ListIconGadget
2. Removed Header in ListIconGadget
3. Added the suggested copy option
4. Added regular expressions

Code: Select all

; ----------------------------------------------------------------------------
; File : FindAllReferences[Win].pb
; ----------------------------------------------------------------------------
;
;   Description: Find all references of a variable
;            OS: Windows
; English-Forum:
;  French-Forum:
;  German-Forum: http://www.purebasic.fr/german/viewtopic.php?f=8&t=28292
; ----------------------------------------------------------------------------

; MIT License
;
; Copyright (c) 2015 Kiffi
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.
;
; ----------------------------------------------------------------------------
; Change Log :
;   2023-02-12 : New Additions from Mesa, AZJIO, Axolotl, Dadlick, ChrisR
;
; English-Forum: https://www.purebasic.fr/english/viewtopic.php?t=80739
;
; History
;   - Mesa added search for constants and renewed interest in the tool
;   - AZJIO embedded highlight using ColorListIconGadget.pb by srod
;   - Axolotl qualitatively rewrote the functionality, making many changes
;   - Axolotl added the ability to test without compiling
;   - AZJIO added regular expressions
;   - ChrisR added a black theme
;   - Dadlick started his own tool fork
;   - AZJIO added a jump test to a string without compiling
;   - AZJIO added a black theme for Header and button
;
; ----------------------------------------------------------------------------

; FindAllReferences

CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
	CompilerError "Windows Only!"
CompilerEndIf

EnableExplicit

;- Structure

Structure sFoundReference
	LineNo.i
	Reference.s
	Selregexp.s
EndStructure

Structure xywhm
	x.i
	y.i
	w.i
	h.i
	m.i
EndStructure

;- Enumeration
Enumeration ; Windows
	#frmMain
EndEnumeration

Enumeration ; Gadgets
	#cmbRex
	#btnRex
	; 	#btnClose
	#frmMain_References
EndEnumeration

Enumeration ; Menu-/Toolbaritems
	#frmMain_Shortcut_Escape_Event
	#Shortcut_Ctrl_Shift_C
	#Shortcut_Enter
EndEnumeration

;- Constants
CompilerIf #PB_Compiler_Debugger
	#SelectedWordMarker$ = "|"
CompilerElse
	#SelectedWordMarker$ = Chr(1)  ; not used in source codes
CompilerEndIf
; "	 Line = Trim(Line)"
; "	 |Line| = Trim(|Line|)"
; --> ReplaceString(text$, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$)

;/-----------------------------------------------------------------------------
;| RGB() as HEX() -->     BB GG RR   .. i.e. RGB (1, 2, 3)    -->    03 02 01
;| RGB() as HEX() -->  AA BB GG RR   .. i.e. RGBA(1, 2, 3, 4) --> 04 03 02 01
;\

#coloredChars_Delimeter = "{***\"

;- Global
Global ini$ = LSet(ProgramFilename(), Len(ProgramFilename()) - 3) + "ini"
Global centered
Global xywh.xywhm
Global xywh2.xywhm
Global xywh\w = 600
Global xywh\h = 300
Global hHeader
Global frmMain_References
Global CursorLine
Global flgRead

Global PbIdeHandle, ScintillaHandle

Global SelectedWord.s, ScintillaText.s
Global CountSelectedWords    ; new, because we want to know all references (not only the lines)
Global NewList FoundReference.sFoundReference()
Global Dim Lines.s(0)

Global BackColor = $3f3f3f
Global ForeColor = $cccccc
Global BackColorHeader = $222222
Global ForeColorHeader = $72ADC0
Global BorderColor = $888888
Global HightLightBrush = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
; Global HightLightBrush = CreateSolidBrush_($423926)
Global BackColorBrush = CreateSolidBrush_(BackColor)
Global BackColorBrushHeader = CreateSolidBrush_(BackColorHeader)

; ; ; Global PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
; ; ; If PbIdeHandle = 0 : End : EndIf
; ; ;
; ; ; Global ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
; ; ; If ScintillaHandle = 0 : End : EndIf

; ---== Procedures ==--------------------------------------------------------------------------------------------------

; AZJIO
Procedure.s LTrimChar(String$, TrimChar$ = #CRLF$ + #TAB$ + #FF$ + #VT$ + " ")
	Protected *memChar, *c.Character, *jc.Character

	If Not Asc(String$)
		ProcedureReturn ""
	EndIf

	*c.Character = @String$
	*memChar = @TrimChar$

	While *c\c
		*jc.Character = *memChar

		While *jc\c
			If *c\c = *jc\c
				*c\c = 0
				Break
			EndIf
			*jc + SizeOf(Character)
		Wend

		If *c\c
			String$ = PeekS(*c)
			Break
		EndIf
		*c + SizeOf(Character)
	Wend

	ProcedureReturn String$
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

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

; ---------------------------------------------------------------------------------------------------------------------

; For test only
Global classText.s = Space(256)
; Finding a PureBasic Window
Procedure.l enumChildren0(hwnd.l)
	If hwnd
		GetClassName_(hwnd, @classText, 256)
		If classText = "WindowClass_2"
			GetWindowText_(hwnd, @classText, 256)
			If Left(classText, 9) = "PureBasic"
				PbIdeHandle = hwnd
				ProcedureReturn 0
			EndIf
		EndIf
		ProcedureReturn 1
	EndIf
	ProcedureReturn 0
EndProcedure

; Finding the Scintilla
Procedure.l enumChildren1(hwnd.l)
	If hwnd
		GetClassName_(hwnd, @classText, 256)
		If classText = "Scintilla"
			ScintillaHandle = hwnd
			ProcedureReturn 0
		EndIf
		ProcedureReturn 1
	EndIf
	ProcedureReturn 0
EndProcedure
; End: For test only

Procedure Initialization()

	PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
	CompilerIf Not #PB_Compiler_Debugger
		If PbIdeHandle = 0 : End : EndIf
	CompilerEndIf

	ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
	CompilerIf Not #PB_Compiler_Debugger
		If ScintillaHandle = 0 : End : EndIf
	CompilerEndIf


; For test only
	CompilerIf #PB_Compiler_Debugger
		EnumChildWindows_(0, @enumChildren0(), 0)
		EnumChildWindows_(PbIdeHandle, @enumChildren1(), 0)
	CompilerEndIf
; End: For test only


	SelectedWord.s = GetEnvironmentVariable("PB_TOOL_Word")
	CompilerIf Not #PB_Compiler_Debugger
		If SelectedWord = "" : End : EndIf
	CompilerEndIf

	ScintillaText.s = GetScintillaText()
	CompilerIf Not #PB_Compiler_Debugger
		If ScintillaText = "" : End : EndIf
	CompilerEndIf


	CursorLine = Int(Val(StringField(GetEnvironmentVariable("PB_TOOL_Cursor"), 1, "x")))


; For test only
	CompilerIf #PB_Compiler_Debugger

		If SelectedWord = ""
			;     SelectedWord = "Line"    ; try one of these
			;     SelectedWord = "#Line"   ; -"-  #Line could be in a comment also
			SelectedWord = "ScintillaText"   ; -"-
		EndIf

		If ScintillaText = ""
			#File = 0
			If ReadFile(#File, #PB_Compiler_File)
				ScintillaText = ReadString(#File, #PB_UTF8 | #PB_File_IgnoreEOL)
				CloseFile(#File)
			EndIf
			; 			RunProgram("explorer.exe", "/Select," + #PB_Compiler_File, "")

			; 			ScintillaText = "" + #CRLF$ +
			; 			                "#Line = #LF ;  #Line could be in a comment also " + #CRLF$ +
			; 			                "Procedure Test(*Line)  ; pointer *Line " + #CRLF$ +
			; 			                "" + #CRLF$ +
			; 			                "If SelectedWord = LCase(Tokens(TokenCounter))" + #CRLF$ +
			; 			                "	 AddElement(FoundReference())" + #CRLF$ +
			; 			                "	 FoundReference()\LineNo = LineCounter + 1" + #CRLF$ +
			; 			                "	 Line = Trim(Line)" + #CRLF$ +
			; 			                "	 Line = Mid(Line, 1, Len(Line)-2)" + #CRLF$ +
			; 			                "	 FoundReference()\Reference = Line" + #CRLF$ +
			; 			                "EndIf" + #CRLF$ +
			; 			                "" ; End of Text
		EndIf
	CompilerEndIf
; End: For test only

	ProcedureReturn 0  ; default (ZERO is returned by default, even if there is no ProcedureReturn)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

; ChrisR
Procedure CopyClipboard()
	Protected text$
	PushListPosition(FoundReference())
	ForEach FoundReference()
		If text$ : text$ + #CRLF$ : EndIf
		If Right(FoundReference()\Reference, 2) = #CRLF$
			text$ + Left(FoundReference()\Reference, Len(FoundReference()\Reference) - 2)
		Else
			text$ + FoundReference()\Reference
		EndIf
	Next
	PopListPosition(FoundReference())
	If text$
		text$ = ReplaceString(text$, #SelectedWordMarker$, "")
		SetClipboardText(text$)
		Protected Title$ = GetWindowTitle(#frmMain)
		SetWindowTitle(#frmMain, Title$ + " (Reference copied To the clipboard)")
		Delay(500)
		SetWindowTitle(#frmMain, Title$)
	EndIf
EndProcedure

; AZJIO
Procedure GoRegExp()
	; LINK : https://www.purebasic.fr/english/viewtopic.php?p=595832#p595832
	Protected rex, LSize, Pos = 0, i, tmp$
	Protected Dim Tokens.s(0)
	Protected timer
	
	timer = ElapsedMilliseconds()

	ClearGadgetItems(#frmMain_References)
	ClearList(FoundReference())

	tmp$ = GetGadgetText(#cmbRex)
	If Not Asc(tmp$)
		ProcedureReturn
	EndIf
	rex = CreateRegularExpression(#PB_Any, tmp$)
	; 	CountTokens = ExtractRegularExpression(rex, ScintillaText, Tokens()) ; tokenize the line

	; 	Debug ArraySize(Lines())

	If rex
		If ExamineRegularExpression(rex, ScintillaText)
			While NextRegularExpressionMatch(rex)
				If Not FindString(RegularExpressionMatchString(rex), #LF$)
					AddElement(FoundReference())
					FoundReference()\Selregexp = RegularExpressionMatchString(rex)
					FoundReference()\LineNo = RegularExpressionMatchPosition(rex)
					; 					Debug  FoundReference()\LineNo
				EndIf
			Wend
		EndIf
	Else
		MessageRequester("Regular expression error", RegularExpressionError())
		ProcedureReturn
	EndIf

	LSize = ListSize(FoundReference())
	If LSize > 0
		; 		If LSize > 5000 And MessageRequester("Continue?", "Found" + Str(LSize) + " rows, Continue?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_No
		; 			ProcedureReturn
		; 		EndIf

		Pos = 0
		i = 0
		SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 0, 0)
		ForEach FoundReference()
			While Pos < FoundReference()\LineNo
				Pos = FindString(ScintillaText, #LF$, Pos + 1)
				If Pos
					i + 1
				Else
					Break
				EndIf
				; 				Debug Str(FoundReference()\LineNo) + " "  + Str(Pos)
			Wend
			If i < 1 Or i > ArraySize(Lines())
				Continue
			EndIf
			FoundReference()\LineNo = i
			FoundReference()\Reference = Lines(i - 1)


			FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)

			; >> first attempt to mark the selected word in the string
			FoundReference()\Reference = ReplaceString(FoundReference()\Reference, FoundReference()\Selregexp, #SelectedWordMarker$ + FoundReference()\Selregexp + #SelectedWordMarker$, #PB_String_NoCase)

			AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference)
		Next
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER) ; last column -> fill the remaining rest
		SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 1, 0)

		SelectedWord = "regexp"
		SetWindowTitle(#frmMain, Str(ElapsedMilliseconds() - timer) + " ms, '"  + SelectedWord + "', Found " + " in " + Str(ListSize(FoundReference())) + " Lines")
	EndIf
EndProcedure


Procedure LookForWordUnderCursor()
	; LINK : http://www.purebasic.fr/english/viewtopic.php?f=12&t=37823
	Protected RegexLines, PbRegexTokens
	Protected CountLines, LineCounter, CountTokens, TokenCounter
	Protected Line.s, selWord.s, stx.s
	Protected Dim Tokens.s(0)

	RegexLines = CreateRegularExpression(#PB_Any , ".*\r\n")
	PbRegexTokens = CreateRegularExpression(#PB_Any, #DOUBLEQUOTE$ + "[^" + #DOUBLEQUOTE$ + "]*" + #DOUBLEQUOTE$ + "|[\*]?[a-zA-Z_]+[\w]*[\x24]?|#[a-zA-Z_]+[\w]*[\x24]?|[\[\]\(\)\{\}]|[-+]?[0-9]*\.?[0-9]+|;.*|\.|\+|-|[&@!\\\/\*,\|]|::|:|\|<>|>>|<<|=>{1}|>={1}|<={1}|=<{1}|={1}|<{1}|>{1}|\x24+[0-9a-fA-F]+|\%[0-1]*|%|'")

	CountLines = CountString(ScintillaText, #CRLF$)

	CountLines = ExtractRegularExpression(RegexLines, ScintillaText, Lines())

	selWord = LCase(SelectedWord)  ; keep the original writing
	CountSelectedWords = 0 ; init for new search

	For LineCounter = 0 To CountLines - 1
		Line = Lines(LineCounter)

		;Debug "tokenize Line '" + Line + "'"

		CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens()) ; tokenize the line

		For TokenCounter = 0 To CountTokens - 1
			;Debug "  check Token '" + Tokens(TokenCounter) + "'"

			If selWord = LCase(Tokens(TokenCounter))
				AddElement(FoundReference())
				FoundReference()\LineNo = LineCounter + 1

				Line = Trim(Line)
				Line = Mid(Line, 1, Len(Line) - 2)  ; remove the #CRLF$

				CountSelectedWords + CountString(LCase(Line), selWord)   ; <-- count SelectedWord in the codeline

				FoundReference()\Reference = Line
				Break  ; only one line (evenn if there are more than one SelectedWord )
			EndIf
		Next TokenCounter
	Next LineCounter

	; because of #Constant or *Pointer
	If ListSize(FoundReference()) = 0
		For LineCounter = 0 To CountLines - 1
			Line = Lines(LineCounter)
			CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens())
			For TokenCounter = 0 To CountTokens - 1
				stx = LCase(Tokens(TokenCounter))
				If stx = "#" + selWord Or stx = "*" + selWord
					AddElement(FoundReference())
					FoundReference()\LineNo = LineCounter + 1

					Line = Trim(Line)
					Line = Mid(Line, 1, Len(Line) - 2)

					CountSelectedWords + CountString(LCase(Line), stx)  ; <-- count SelectedWord in the codeline

					FoundReference()\Reference = Line
					Break
				EndIf
			Next
		Next

		CompilerIf Not #PB_Compiler_Debugger
			If ListSize(FoundReference()) = 0 : End : EndIf
		CompilerEndIf
	EndIf
EndProcedure



; ---------------------------------------------------------------------------------------------------------------------
; XIncludeFile "ColorListIconGadget.pbi"  ; I prefer .pbi (instead of .pb)
; ---------------------------------------------------------------------------------------------------------------------

;... Create brushes for painting item background
Structure MYBRUSHES
	brushDefault.l
	brushSelected.l
EndStructure

; Global brush.MYBRUSHES
Global Dim Colors(1)

; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_INFOBK))
; brush\brushDefault = GetStockObject_(#WHITE_BRUSH)
; brush\brushSelected = ForeColor
; brush\brushDefault = BackColor


; ---== Color for Default Text and Selected Word ==--------------------------------------------------------------------


; Colors(0) = #Red                                ; the SelectedWord
Colors(0) = $8080FF                           ; the SelectedWord
											  ; Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
											  ; Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT); the default text
Colors(1) = ForeColor ; the default text

; ---------------------------------------------------------------------------------------------------------------------

Procedure GetCharWidth(gad, c$)
	ProcedureReturn SendMessage_(gad, #LVM_GETSTRINGWIDTH, 0, @c$)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

;Here we add some text to the underlying cell text to store the color info.
Procedure SetColor(gad, row, column, startp, endp, color)
	Protected text$
	If column
		text$ = GetGadgetItemText(gad, row, column)
		;Now add the new text.
		text$ + #coloredChars_Delimeter + Str(startp) + "\" + Str(endp) + "\" + Str(color)
		SetGadgetItemText(gad, row, text$, column)
	EndIf
EndProcedure


; ---== MainWindow Procedures ==---------------------------------------------------------------------------------------

Procedure Resize_Event()
	Protected wlv
	xywh\w = WindowWidth(#frmMain)
	xywh\h = WindowHeight(#frmMain)
	ResizeGadget(#cmbRex, #PB_Ignore, #PB_Ignore, xywh\w - 34, 24)
	ResizeGadget(#btnRex, xywh\w - 28, #PB_Ignore, #PB_Ignore, #PB_Ignore)
	ResizeGadget(#frmMain_References, #PB_Ignore, #PB_Ignore, xywh\w - 6, xywh\h - 33)
; 	wlv = GetGadgetItemAttribute(#frmMain_References, 0 , #PB_ListIcon_ColumnWidth, 0)
; 	SetGadgetItemAttribute(#frmMain_References, 0, #PB_ListIcon_ColumnWidth, xywh\w - 6 - wlv, 1)
	SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
EndProcedure

Procedure JumpToLine(SelectedLine)
	Protected Count
; 	Debug SelectedLine
	SendMessage_(ScintillaHandle, #SCI_GOTOLINE, SelectedLine - 1, 0)
	Count = SendMessage_(ScintillaHandle, #SCI_LINESONSCREEN, 0, 0) / 2
	SendMessage_(ScintillaHandle, #SCI_SETFIRSTVISIBLELINE, SelectedLine - Count - 1, 0)
; 	Debug Count
; 	MessageRequester("", Str(Count))
; 	SendMessage_(ScintillaHandle, #SCI_ENSUREVISIBLE, SelectedLine - 1, 0)
	SetForegroundWindow_(PbIdeHandle)
	SetActiveWindow_(PbIdeHandle)
EndProcedure

Procedure Event_ListView()
	Protected SelectedLine
; 	Static SelLineOld = -1
; 	If SelLineOld = SelectedLine
; 		ProcedureReturn
; 	EndIf

	SelectedLine = Val(GetGadgetItemText(#frmMain_References, GetGadgetState(#frmMain_References), 0))
	If SelectedLine > 0
		JumpToLine(SelectedLine)
; 		SelLineOld = SelectedLine
	EndIf
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

Procedure Callback_Win(hwnd, msg, wParam, lParam)
	Protected Result, *nmhdr.NMHDR, *lvCD.NMLVCUSTOMDRAW, subItemRect.RECT, *DrawItem.DRAWITEMSTRUCT, Buffer.s
	Protected *pnmcd.NMCUSTOMDRAW, hdi.hd_item
	Protected thisRow, thisCol, idx
	Protected t$, text$
	Protected nNotifyCode
	Protected *NMITEM.NMITEMACTIVATE
	Protected SelectedLine

	Result = #PB_ProcessPureBasicEvents
	;;Dim LVColor(0)

	Select msg
		Case #WM_COMMAND
			If lParam = GadgetID(#cmbRex)
				nNotifyCode = wParam >> 16 ; HiWord
; 				If nNotifyCode = #CBN_SELCHANGE
				If nNotifyCode = #CBN_SELENDCANCEL
					flgRead = 0
				EndIf
				If nNotifyCode = #CBN_DROPDOWN
					flgRead = 1
; 					GoRegExp()
				EndIf
			EndIf

		Case #WM_NCDESTROY
			DeleteObject_(HightLightBrush)
			DeleteObject_(BackColorBrush)
			DeleteObject_(BackColorBrushHeader)

		Case #WM_NOTIFY
			*nmhdr.NMHDR = lParam
			*lvCD.NMLVCUSTOMDRAW = lParam
			*NMITEM.NMITEMACTIVATE = lParam


			If *nmhdr\code = #NM_CLICK
				If *NMITEM\iItem <> -1
					SelectedLine = Val(GetGadgetItemText(#frmMain_References, *NMITEM\iItem, 0))
					If SelectedLine > 0
						JumpToLine(SelectedLine)
					EndIf
				EndIf
			EndIf
			If *lvCD\nmcd\hdr\hwndFrom = GadgetID(#frmMain_References) And *lvCD\nmcd\hdr\code = #NM_CUSTOMDRAW
				Select *lvCD\nmcd\dwDrawStage
					Case #CDDS_PREPAINT
						Result = #CDRF_NOTIFYITEMDRAW
					Case #CDDS_ITEMPREPAINT
						Result = #CDRF_NOTIFYSUBITEMDRAW;
					Case #CDDS_ITEMPREPAINT | #CDDS_SUBITEM
						thisRow = *lvCD\nmcd\dwItemSpec
						thisCol = *lvCD\iSubItem
						If thisCol
							;... Define rect for text
							subItemRect.RECT\left = #LVIR_LABEL
							subItemRect.RECT\top = *lvCD\iSubItem
							;... Get the subitem rect
							SendMessage_(GadgetID(#frmMain_References), #LVM_GETSUBITEMRECT, thisRow, @subItemRect)

							text$ = GetGadgetItemText(#frmMain_References, thisRow, thisCol)

							; 							If GetGadgetState(#frmMain_References) = thisRow
							; 								;... If item is selected
							; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushSelected)
							; 								;               Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
							; 							Else
							; 								;... If item is not selected
							; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushDefault)
							; 								;               Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT)  ; the default text
							; 							EndIf
							InflateRect_(subItemRect, -8, 0)

							For idx = 1 To CountString(text$, #SelectedWordMarker$) + 1
								t$ = StringField(text$, idx, #SelectedWordMarker$)
								If t$
									SetTextColor_(*lvCD\nmcd\hdc, colors(idx & 1))
									SetBkColor_(*lvCD\nmcd\hdc, BackColor)
									DrawText_(*lvCD\nmcd\hdc, t$, -1, subItemRect, #DT_END_ELLIPSIS | #DT_VCENTER | #DT_SINGLELINE)
									subItemRect\left + GetCharWidth(*nmhdr\hwndFrom, t$)
								EndIf
							Next idx
							Result = #CDRF_SKIPDEFAULT
						Else
							Result = #CDRF_DODEFAULT
						EndIf
				EndSelect
			EndIf

		Case #WM_CTLCOLOREDIT
			Buffer = Space(64)
			If GetClassName_(GetParent_(lParam), @Buffer, 64)
				If Buffer = "ComboBox"
					SetTextColor_(wParam, #White)
					SetBkMode_(wParam, #TRANSPARENT)
					ProcedureReturn BackColorBrush
				EndIf
			EndIf

		Case #WM_DRAWITEM
			*DrawItem.DRAWITEMSTRUCT = lParam
			If *DrawItem\CtlType = #ODT_COMBOBOX
				If IsGadget(wParam)
					If *DrawItem\itemID <> -1
						If *DrawItem\itemstate & #ODS_SELECTED
							FillRect_(*DrawItem\hDC, *DrawItem\rcitem, HightLightBrush)
						Else
							FillRect_(*DrawItem\hDC, *DrawItem\rcitem, BackColorBrush)
						EndIf
						SetBkMode_(*DrawItem\hDC, #TRANSPARENT)
						SetTextColor_(*DrawItem\hDC, ForeColor)
						Text$ = GetGadgetItemText(*DrawItem\CtlID, *DrawItem\itemID)
						*DrawItem\rcItem\left + DesktopScaledX(4)
						DrawText_(*DrawItem\hDC, Text$, Len(Text$), *DrawItem\rcItem, #DT_LEFT | #DT_SINGLELINE | #DT_VCENTER)
					EndIf
				EndIf
			EndIf
	EndSelect
	ProcedureReturn Result
EndProcedure

Procedure Callback_Header(hWnd, Message, wParam, lParam)
	Protected *Header.HD_NOTIFY, SelectedLine, *lvCD.NMLVCUSTOMDRAW
	Protected *nmhdr.NMHDR, text$, *pnmcd.NMCUSTOMDRAW, hdi.hd_item
	Protected rc2.RECT, hDC
	Protected Result = CallWindowProc_(frmMain_References, hWnd, Message, wParam, lParam)


	*Header = lParam
	*nmhdr = lParam
	*lvCD = lParam
	Select Message
		Case #WM_NOTIFY
			Select *Header\hdr\code
				Case #HDN_ITEMCLICK
					If *Header\hdr\code = #HDN_ITEMCLICK
						;ColumnClicked=*Header\iItem
						SelectedLine = Val(GetGadgetItemText(#frmMain_References, -1, 0))
						If SelectedLine > 0
							JumpToLine(SelectedLine)
						EndIf
					EndIf
				Case #NM_CUSTOMDRAW
					If *nmhdr\hwndFrom = hHeader
						*pnmcd.NMCUSTOMDRAW = lParam
						Select *pnmcd\dwDrawStage
							Case #CDDS_PREPAINT
								result = #CDRF_NOTIFYITEMDRAW
							Case #CDDS_ITEMPREPAINT
								text$ = GetGadgetItemText(GetDlgCtrlID_(hWnd), -1, *pnmcd\dwItemSpec)
								hdi\mask = #HDI_TEXT
								hdi\psztext = @text$
								hdi\cchtextmax = Len(text$)
								SetBkMode_(*pnmcd\hdc, #TRANSPARENT)
								FillRect_(*pnmcd\hdc, *pnmcd\rc, BackColorBrushHeader)
; 								сдвигаем текст после закрашивания прямоуголников
								If *lvCD\nmcd\dwItemSpec
									InflateRect_(*pnmcd\rc, -8, 0)
									text$ = LTrimChar(text$)
								Else
									InflateRect_(*pnmcd\rc, -4, 0)
								EndIf
								SetTextColor_(*pnmcd\hdc, ForeColorHeader)
								DrawText_(*pnmcd\hdc, @text$, Len(text$), *pnmcd\rc, #DT_VCENTER | #DT_END_ELLIPSIS)
								result = #CDRF_SKIPDEFAULT
						EndSelect
					EndIf
			EndSelect
	EndSelect
	ProcedureReturn Result
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------
; ChrisR
Procedure SetWindowTheme()
	Protected Theme.s, cmbRexID, ChildGadget, Buffer.s
	If OSVersion() >= #PB_OS_Windows_10
		Theme = "DarkMode_Explorer"
	Else
		Theme = "Explorer"
	EndIf

	SetWindowTheme_(GadgetID(#frmMain_References), @Theme, 0)

	cmbRexID = GadgetID(#cmbRex)
	Buffer = Space(64)
	If GetClassName_(cmbRexID, @Buffer, 64)
		If Buffer = "ComboBox"
			If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
				SetWindowTheme_(cmbRexID, "DarkMode_CFD", "Combobox")
			Else
				SetWindowTheme_(cmbRexID, @Theme, 0)
			EndIf
		EndIf
	EndIf
	ChildGadget = GetWindow_(cmbRexID, #GW_CHILD)
	If ChildGadget
		Buffer = Space(64)
		If GetClassName_(ChildGadget, @Buffer, 64)
			If Buffer = "ComboBox"
				If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
					SetWindowTheme_(ChildGadget, "DarkMode_CFD", "Combobox")
				Else
					SetWindowTheme_(ChildGadget, @Theme, 0)
				EndIf
			EndIf
		EndIf
	EndIf
EndProcedure

Procedure SetGadgetBorderless(Gadget)
	Protected hGad = GadgetID(Gadget)
	SetWindowLongPtr_(hGad, #GWL_EXSTYLE, GetWindowLongPtr_(hGad, #GWL_EXSTYLE) & (~#WS_EX_CLIENTEDGE))
	ProcedureReturn SetWindowPos_(hGad, 0, 0, 0, 0, 0, #SWP_SHOWWINDOW | #SWP_NOZORDER | #SWP_NOSIZE | #SWP_NOMOVE | #SWP_FRAMECHANGED)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

Procedure main()
	Protected WWE ;, idx, pos, le
	Protected timer
	
	timer = ElapsedMilliseconds()

	Initialization()  ;
	LookForWordUnderCursor()

	;--> ini
	If OpenPreferences(ini$) ; открываем ini
		If PreferenceGroup("set")
			xywh\x = ReadPreferenceInteger("x", xywh\x)
			xywh\y = ReadPreferenceInteger("y", xywh\y)
			xywh\w = ReadPreferenceInteger("w", xywh\w)
			xywh\h = ReadPreferenceInteger("h", xywh\h)
		EndIf
		ClosePreferences()
	EndIf
	If xywh\x = 0 And xywh\y = 0
		centered = #PB_Window_ScreenCentered
	EndIf
	CopyStructure(@xywh, @xywh2, xywhm)


	;- GUI
	If OpenWindow(#frmMain, xywh\x, xywh\y, xywh\w, xywh\h,
	              Str(ElapsedMilliseconds() - timer) + " ms, '" + SelectedWord + "', Found " + CountSelectedWords + " in " + Str(ListSize(FoundReference())) + " Lines",
	              #PB_Window_SystemMenu | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget | #PB_Window_SizeGadget | centered)
		SetWindowColor(#frmMain, BackColor)
		StickyWindow(#frmMain, #True)
		SetWindowCallback(@Callback_Win())

		ComboBoxGadget(#cmbRex, 3, 3, WindowWidth(#frmMain) - 34, 24, #PB_ComboBox_Editable | #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED)
		;ComboBoxGadget(#cmbRex, 3, 3, WindowWidth(#frmMain) - 60, 24, #PB_ComboBox_Editable)
		AddGadgetItem(#cmbRex, -1, "(?# Debug, All )\bDebug\b")
		AddGadgetItem(#cmbRex, -1, "(?# Debug, real )(?m)^\h*\KDebug\b")
		AddGadgetItem(#cmbRex, -1, "(?# WinAPI )(?mi)[a-z][\da-z]*_(?=\h*\()")
		AddGadgetItem(#cmbRex, -1, "(?# Link )https?://[\w.:]+/?(?:[\w/?&=.~;\+!*_#%-]+)")
		AddGadgetItem(#cmbRex, -1, "(?# Procedure )(?mi)^\h*(?:Procedure[CDL$]{0,5}?(?:\h*\.[abcdfilqsuw])?\h+\K)[A-Za-z_]\w*\h*(?=\()")
		AddGadgetItem(#cmbRex, -1, "(?# Macro )(?mi)^\h*Macro\h+\K[A-Za-z_]\w*\h*(?=\()")
		AddGadgetItem(#cmbRex, -1, "(?# Var$ )(?<![#@\w])\w+\$")
		AddGadgetItem(#cmbRex, -1, "(?# @*Point, whole )[@*]{1,2}\w+\b\$?(?![\\.(])")
		AddGadgetItem(#cmbRex, -1, "(?# @*Point, var)[@*]{1,2}\w+(?!\()")
		AddGadgetItem(#cmbRex, -1, "(?# @Point, Procedure)@\w+\(\)")
		AddGadgetItem(#cmbRex, -1, "(?# Hex num )(?i)\$[\da-f]+")
		AddGadgetItem(#cmbRex, -1, "(?# Comments )(?m)^\h*\K;.*?(?=\r?$)")
		#q$ = Chr(34)
		AddGadgetItem(#cmbRex, -1, "(?# Comments, All )(?m)^(?:[^" + #q$ + ";]*" + #q$ + "[^" + #q$ + "]*?" + #q$ + ")*[^" + #q$ + ";]*(;.*?)(?=\r?$)")
		AddGadgetItem(#cmbRex, -1, "(?# Structures, Declare )(?i)(?<![=\w" + #q$ + "\\./-])[a-z]\w*\.[a-z]\w+(?![\w" + #q$ + "\\./-])")
		AddGadgetItem(#cmbRex, -1, "(?# Structures, item )(?<![\w.:" + #q$ + "\\])\*?\w+(?:(?:\(\))?\\[\d_a-zA-Z]+)+(?![\w" + #q$ + "\\])")
		AddGadgetItem(#cmbRex, -1, "(?# Structures, Content )(?m)^\h*Structure\h*\K\w+")
		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Comments )(?m)^(?:[^\";]*\"[^\"]*?\")*[^\";]*(;.*?)(?=\r?$)")
		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, Declare )(?i)(?<![=\\w\"\\./-])[a-z]\\w*\\.[a-z]\\w+(?![\\w\"\\\\./-])")
		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, item )(?<![\\w.:\"\\\\])\\*?\\w+(?:(?:\\(\\))?\\\\[\\d_a-zA-Z]+)+(?![\\w\"\\\\])")
		AddGadgetItem(#cmbRex, -1, "(?# Types )\b\w+\.[sfdqbliwcapu]\b")
		AddGadgetItem(#cmbRex, -1, "(?# Constants, Declare )(?m)^\h*\K#\w+\$?(?=\h*(?:=|\r?$))")
		AddGadgetItem(#cmbRex, -1, "(?# Constants, All )#\w+\b\$?")
		AddGadgetItem(#cmbRex, -1, "(?# CONSTANTS, DECLARE )(?m)^\h*\K#[A-Z\d_]+(?=\h*(?:=|\r?$))")
		AddGadgetItem(#cmbRex, -1, "(?# CONSTANTS, ALL )#[A-Z\d_]+\b")
		AddGadgetItem(#cmbRex, -1, "(?# CONSTANTS, X_X )#[A-Z\d]+_[A-Z\d]+\b")
		AddGadgetItem(#cmbRex, -1, "(?# Constants, #PB_ )#PB_\w+\b\$?")
		AddGadgetItem(#cmbRex, -1, "(?# Constants, Str )#\w+\$")
		AddGadgetItem(#cmbRex, -1, "(?# If )(?mi)^\h*\KIf(?=\h)")
		AddGadgetItem(#cmbRex, -1, "(?# Loop )(?mi)^\h*\K(For(Each)?|Repeat|While)(?=\h)")
		AddGadgetItem(#cmbRex, -1, "(?# Select )(?mi)^\h*\KSelect(?=\h)")
		AddGadgetItem(#cmbRex, -1, "(?# Include )(?mi)^\h*\KX?Include[a-z]{4,6}\b(?=\h)")

; 		ButtonGadget(#btnRex, WindowWidth(#frmMain) - 28, 3, 24, 24, ">")
; 		ButtonImageGadget(#btnRex, WindowWidth(#frmMain) - 28, 3, 24, 24, GetClassLongPtr_(WindowID(#frmMain), #GCL_HICONSM))

		#img = 0
; 		Protected tmp = GadgetHeight(#cmbRex)
		Protected tmp = 24
		If CreateImage(#img, tmp, tmp, 32, RGB(255, 255, 255))
			StartDrawing(ImageOutput(#img))
			Box(0, 0, tmp, tmp, BorderColor)
			Box(1, 1, tmp - 2, tmp - 2, BackColorHeader)
			DrawText((tmp - TextWidth(">")) / 2, (tmp - TextHeight(">")) / 2, ">", ForeColor, BackColorHeader)
			StopDrawing()
		EndIf
		ImageGadget(#btnRex, WindowWidth(#frmMain) - 28, 3, tmp, tmp, ImageID(0))
; 	    ButtonImageGadget(#btnRex, WindowWidth(#frmMain) - 28, 3, 24, 24, ImageID(0))
; 		CatchImage(0, GetClassLongPtr_(WindowID(#frmMain), #GCL_HICONSM))
; 		ButtonImageGadget(#btnRex, WindowWidth(#frmMain) - 28, 3, 24, 24, ImageID(0))
		; ButtonGadget(#btnClose, WindowWidth(#frmMain) - 27, 3, 24, 24, "x") ; to make a black theme

		If CursorLine < 1 Or CursorLine > ArraySize(Lines())
			CursorLine = 1
		EndIf
		ListIconGadget(#frmMain_References, 3, 30, WindowWidth(#frmMain) - 6, WindowHeight(#frmMain) - 33, Str(CursorLine), 96, #PB_ListIcon_FullRowSelect | #PB_ListIcon_GridLines | #PB_ListIcon_AlwaysShowSelection)
		; 		SetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE,GetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE) | #LVS_NOCOLUMNHEADER)
		frmMain_References = SetWindowLongPtr_(GadgetID(#frmMain_References), #GWL_WNDPROC, @Callback_Header())
		hHeader = SendMessage_(GadgetID(#frmMain_References), #LVM_GETHEADER, 0, 0)
		AddGadgetColumn(#frmMain_References, 1, Lines(CursorLine - 1), 400)
		SetGadgetColor(#frmMain_References, #PB_Gadget_BackColor, BackColor)
		SetGadgetColor(#frmMain_References, #PB_Gadget_FrontColor, ForeColor)
		SetGadgetBorderless(#frmMain_References)  ; by me
		; Optional DarkMode_Explorer theme if OSVersion >= Windows_10 Else Explorer Theme
		SetWindowTheme()

		ForEach FoundReference()
			FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)

			; >> first attempt to mark the selected word in the string
			FoundReference()\Reference = ReplaceString(FoundReference()\Reference, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$, #PB_String_NoCase)

			AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference)
		Next

		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER) ; last column -> fill the remaining rest


		AddKeyboardShortcut(#frmMain, #PB_Shortcut_Escape, #frmMain_Shortcut_Escape_Event)
		BindEvent(#PB_Event_SizeWindow, @Resize_Event(), #frmMain)
; 		BindGadgetEvent(#frmMain_References, @Event_ListView())
		SetActiveGadget(#frmMain_References)

		AddKeyboardShortcut(#frmMain, #PB_Shortcut_Control | #PB_Shortcut_Shift | #PB_Shortcut_C, #Shortcut_Ctrl_Shift_C)
		AddKeyboardShortcut(#frmMain, #PB_Shortcut_Return, #Shortcut_Enter)

;- Loop
		Repeat
			Select WaitWindowEvent()
				Case #PB_Event_MoveWindow
					xywh\x = WindowX(#frmMain)
					xywh\y = WindowY(#frmMain)
				Case #PB_Event_CloseWindow
; 					Если размеры окна изменились, то сохраняем.
					If Not CompareMemory(@xywh, @xywh2, SizeOf(xywhm))
						If OpenPreferences(ini$) Or CreatePreferences(ini$)
							PreferenceGroup("set")
							WritePreferenceInteger("x", xywh\x)
							WritePreferenceInteger("y", xywh\y)
							WritePreferenceInteger("w", xywh\w)
							WritePreferenceInteger("h", xywh\h)
							ClosePreferences()
						EndIf
					EndIf
					Break
				Case #PB_Event_Menu
					Select EventMenu()
						Case #Shortcut_Enter
							If GetActiveGadget() = #cmbRex
								flgRead = 0
								GoRegExp()
							EndIf
							If GetActiveGadget() = #frmMain_References
								Event_ListView()
							EndIf
						Case #Shortcut_Ctrl_Shift_C
							CopyClipboard()
						Case #frmMain_Shortcut_Escape_Event
							Break
					EndSelect
				Case #PB_Event_Gadget
					Select EventGadget()
						Case #cmbRex
							If EventType() = #PB_EventType_Change And flgRead = 1
								flgRead = 0
								GoRegExp()
							EndIf
						Case #btnRex
							GoRegExp()
					EndSelect
			EndSelect
		ForEver

	EndIf
	ProcedureReturn 0  ; not necessary, but looks good/better
EndProcedure

End main()

;- Bottom of File
Last edited by AZJIO on Mon Feb 20, 2023 11:51 pm, edited 11 times in total.
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: Show all occurrences of a word in the IDE

Post by ChrisR »

Great :)
AZJIO wrote: Wed Feb 15, 2023 3:18 pm 1. Added black color for ListIconGadget
If you wish, I Added the DarkMode_Explorer theme for Windows 10 and up (for ScrollBar) and the background color for the OwnerDrawFixed ComboBoxGadget

Code: Select all

; ----------------------------------------------------------------------------
; File : FindAllReferences[Win].pb
; ----------------------------------------------------------------------------
;
;   Description: Find all references of a variable
;            OS: Windows
; English-Forum:
;  French-Forum:
;  German-Forum: http://www.purebasic.fr/german/viewtopic.php?f=8&t=28292
; ----------------------------------------------------------------------------

; MIT License
;
; Copyright (c) 2015 Kiffi
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.
;
; ----------------------------------------------------------------------------
; Change Log :
;   2023-02-12 : New Additions from Mesa, AZJIO, Axolotl
;
; English-Forum: https://www.purebasic.fr/english/viewtopic.php?t=80739
;
; Change Details by Axolotl
;   - Integrated ColorListIconGadget.pb from AZJIO and modified
;   - Add a #PB_Compiler_Debugger mode (for development)
;   - New Structure of the entire application
;   - harmonize the different coding styles (still something to do)
;   - Changing the coloring of the selected words
;   - counting all appearances of the SelectedWord (quick and dirty)
;
; ----------------------------------------------------------------------------

; FindAllReferences

CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
  CompilerError "Windows Only!"
CompilerEndIf

EnableExplicit

; ---== Structure Definition ==----------------------------------------------------------------------------------------

Structure sFoundReference
  LineNo.i
  Reference.s
  Selregexp.s
  
EndStructure

; ---== Enumeration ==-------------------------------------------------------------------------------------------------

Enumeration ; Windows
  #frmMain
EndEnumeration

Enumeration ; Gadgets
  #cmbRex
  #btnRex
  ; 	#btnClose
  #frmMain_References
EndEnumeration

Enumeration ; Menu-/Toolbaritems
  #frmMain_Shortcut_Escape_Event
  #Shortcut_Ctrl_C
EndEnumeration

; ---== Constants ==---------------------------------------------------------------------------------------------------

CompilerIf #PB_Compiler_Debugger
  #SelectedWordMarker$ = "|"
CompilerElse
  #SelectedWordMarker$ = Chr(1)  ; not used in source codes
CompilerEndIf
; "	 Line = Trim(Line)"
; "	 |Line| = Trim(|Line|)"
; --> ReplaceString(text$, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$)

;/-----------------------------------------------------------------------------
;| RGB() as HEX() -->     BB GG RR   .. i.e. RGB (1, 2, 3)    -->    03 02 01
;| RGB() as HEX() -->  AA BB GG RR   .. i.e. RGBA(1, 2, 3, 4) --> 04 03 02 01
;\

#coloredChars_Delimeter = "{***\"

; ---== Global Variables ==--------------------------------------------------------------------------------------------

Global PbIdeHandle, ScintillaHandle

Global SelectedWord.s, ScintillaText.s
Global CountSelectedWords    ; new, because we want to know all references (not only the lines)
Global NewList FoundReference.sFoundReference()
Global Dim Lines.s(0)

Global BackColor = $3f3f3f
Global HightLightBrush = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
Global BackColorBrush = CreateSolidBrush_(BackColor)

; ; ; Global PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
; ; ; If PbIdeHandle = 0 : End : EndIf
; ; ;
; ; ; Global ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
; ; ; If ScintillaHandle = 0 : End : EndIf

; ---== Procedures ==--------------------------------------------------------------------------------------------------

; AZJIO
Procedure.s LTrimChar(String$, TrimChar$ = #CRLF$ + #TAB$ + #FF$ + #VT$ + " ")
  Protected *memChar, *c.Character, *jc.Character
  
  If Not Asc(String$)
    ProcedureReturn ""
  EndIf
  
  *c.Character = @String$
  *memChar = @TrimChar$
  
  While *c\c
    *jc.Character = *memChar
    
    While *jc\c
      If *c\c = *jc\c
        *c\c = 0
        Break
      EndIf
      *jc + SizeOf(Character)
    Wend
    
    If *c\c
      String$ = PeekS(*c)
      Break
    EndIf
    *c + SizeOf(Character)
  Wend
  
  ProcedureReturn String$
EndProcedure

Procedure.s RemoveLeadingWhitespaceFromString(InString.s)
  While Left(InString, 1) = Chr(32) Or Left(InString, 1) = Chr(9)
    InString = LTrim(InString, Chr(32))
    InString = LTrim(InString, Chr(9))
  Wend
  ProcedureReturn InString
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

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

; ---------------------------------------------------------------------------------------------------------------------

Procedure Initialization()
  
  PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
  CompilerIf Not #PB_Compiler_Debugger
    If PbIdeHandle = 0 : End : EndIf
  CompilerEndIf
  
  ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
  CompilerIf Not #PB_Compiler_Debugger
    If ScintillaHandle = 0 : End : EndIf
  CompilerEndIf
  
  
  SelectedWord.s = GetEnvironmentVariable("PB_TOOL_Word")
  CompilerIf Not #PB_Compiler_Debugger
    If SelectedWord = "" : End : EndIf
  CompilerEndIf
  
  ScintillaText.s = GetScintillaText()
  CompilerIf Not #PB_Compiler_Debugger
    If ScintillaText = "" : End : EndIf
  CompilerEndIf
  
  ; >> These are test pattern for development
  CompilerIf #PB_Compiler_Debugger
    
    If SelectedWord = ""
      ;     SelectedWord = "Line"    ; try one of these
      ;     SelectedWord = "#Line"   ; -"-  #Line could be in a comment also
      SelectedWord = "ScintillaText"   ; -"-
    EndIf
    
    If ScintillaText = ""
      #File = 0
      If ReadFile(#File, #PB_Compiler_File)
        ScintillaText = ReadString(#File, #PB_UTF8 | #PB_File_IgnoreEOL)
        CloseFile(#File)
      EndIf
      ; 			RunProgram("explorer.exe", "/Select," + #PB_Compiler_File, "")
      
      ; 			ScintillaText = "" + #CRLF$ +
      ; 			                "#Line = #LF ;  #Line could be in a comment also " + #CRLF$ +
      ; 			                "Procedure Test(*Line)  ; pointer *Line " + #CRLF$ +
      ; 			                "" + #CRLF$ +
      ; 			                "If SelectedWord = LCase(Tokens(TokenCounter))" + #CRLF$ +
      ; 			                "	 AddElement(FoundReference())" + #CRLF$ +
      ; 			                "	 FoundReference()\LineNo = LineCounter + 1" + #CRLF$ +
      ; 			                "	 Line = Trim(Line)" + #CRLF$ +
      ; 			                "	 Line = Mid(Line, 1, Len(Line)-2)" + #CRLF$ +
      ; 			                "	 FoundReference()\Reference = Line" + #CRLF$ +
      ; 			                "EndIf" + #CRLF$ +
      ; 			                "" ; End of Text
    EndIf
  CompilerEndIf
  ProcedureReturn 0  ; default (ZERO is returned by default, even if there is no ProcedureReturn)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

; ChrisR
Procedure CopyClipboardReference()
  Protected text$
  PushListPosition(FoundReference()) 
  ForEach FoundReference()
    If text$ : text$ + #CRLF$ : EndIf
    If Right(FoundReference()\Reference, 2) = #CRLF$
      text$ + Left(FoundReference()\Reference, Len(FoundReference()\Reference) - 2)
    Else
      text$ + FoundReference()\Reference
    EndIf
  Next
  PopListPosition(FoundReference())
  If text$
    text$ = ReplaceString(text$, #SelectedWordMarker$, "")
    SetClipboardText(text$)
    Protected Title$ = GetWindowTitle(#frmMain)
    SetWindowTitle(#frmMain, Title$ + " (Reference copied To the clipboard)")
    Delay(500)
    SetWindowTitle(#frmMain, Title$)
  EndIf
EndProcedure

; AZJIO
Procedure GoRegExp()
  ; LINK : https://www.purebasic.fr/english/viewtopic.php?p=595832#p595832
  Protected rex, LSize, Pos = 0, i, tmp$
  Protected Dim Tokens.s(0)
  
  ClearGadgetItems(#frmMain_References)
  ClearList(FoundReference())
  
  tmp$ = GetGadgetText(#cmbRex)
  If Not Asc(tmp$)
    ProcedureReturn
  EndIf
  rex = CreateRegularExpression(#PB_Any, tmp$)
  ; 	CountTokens = ExtractRegularExpression(rex, ScintillaText, Tokens()) ; tokenize the line
  
  ; 	Debug ArraySize(Lines())
  
  If rex
    If ExamineRegularExpression(rex, ScintillaText)
      While NextRegularExpressionMatch(rex)
        If Not FindString(RegularExpressionMatchString(rex), #LF$)
          AddElement(FoundReference())
          FoundReference()\Selregexp = RegularExpressionMatchString(rex)
          FoundReference()\LineNo = RegularExpressionMatchPosition(rex)
          ; 					Debug  FoundReference()\LineNo
        EndIf
      Wend
    EndIf
  Else
    MessageRequester("Regular expression error", RegularExpressionError())
    ProcedureReturn
  EndIf
  
  LSize = ListSize(FoundReference())
  If LSize > 0
    ; 		If LSize > 5000 And MessageRequester("Continue?", "Found" + Str(LSize) + " rows, Continue?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_No
    ; 			ProcedureReturn
    ; 		EndIf
    
    Pos = 0
    i = 0
    SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 0, 0)
    ForEach FoundReference()
      While Pos < FoundReference()\LineNo
        Pos = FindString(ScintillaText, #LF$, Pos + 1)
        If Pos
          i + 1
        Else
          Break
        EndIf
        ; 				Debug Str(FoundReference()\LineNo) + " "  + Str(Pos)
      Wend
      If i < 1 Or i > ArraySize(Lines())
        Continue
      EndIf
      FoundReference()\LineNo = i
      FoundReference()\Reference = Lines(i - 1)
      
      
      FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)
      
      ; >> first attempt to mark the selected word in the string
      FoundReference()\Reference = ReplaceString(FoundReference()\Reference, FoundReference()\Selregexp, #SelectedWordMarker$ + FoundReference()\Selregexp + #SelectedWordMarker$, #PB_String_NoCase)
      
      AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference)
    Next
    ; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
    ; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
    SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
    SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
    SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 1, 0)
    
    SelectedWord = "regexp"
    SetWindowTitle(#frmMain, "Display all: '" + SelectedWord + "', Found " + " in " + Str(ListSize(FoundReference())) + " Lines")
  EndIf
EndProcedure


Procedure LookForWordUnderCursor()
  ; LINK : http://www.purebasic.fr/english/viewtopic.php?f=12&t=37823
  Protected RegexLines, PbRegexTokens
  Protected CountLines, LineCounter, CountTokens, TokenCounter
  Protected Line.s, selWord.s, stx.s
  Protected Dim Tokens.s(0)
  
  RegexLines = CreateRegularExpression(#PB_Any , ".*\r\n")
  PbRegexTokens = CreateRegularExpression(#PB_Any, #DOUBLEQUOTE$ + "[^" + #DOUBLEQUOTE$ + "]*" + #DOUBLEQUOTE$ + "|[\*]?[a-zA-Z_]+[\w]*[\x24]?|#[a-zA-Z_]+[\w]*[\x24]?|[\[\]\(\)\{\}]|[-+]?[0-9]*\.?[0-9]+|;.*|\.|\+|-|[&@!\\\/\*,\|]|::|:|\|<>|>>|<<|=>{1}|>={1}|<={1}|=<{1}|={1}|<{1}|>{1}|\x24+[0-9a-fA-F]+|\%[0-1]*|%|'")
  
  CountLines = CountString(ScintillaText, #CRLF$)
  
  CountLines = ExtractRegularExpression(RegexLines, ScintillaText, Lines())
  
  selWord = LCase(SelectedWord)  ; keep the original writing
  CountSelectedWords = 0         ; init for new search
  
  For LineCounter = 0 To CountLines - 1
    Line = Lines(LineCounter)
    
    ;Debug "tokenize Line '" + Line + "'"
    
    CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens()) ; tokenize the line
    
    For TokenCounter = 0 To CountTokens - 1
      ;Debug "  check Token '" + Tokens(TokenCounter) + "'"
      
      If selWord = LCase(Tokens(TokenCounter))
        AddElement(FoundReference())
        FoundReference()\LineNo = LineCounter + 1
        
        Line = Trim(Line)
        Line = Mid(Line, 1, Len(Line)-2)  ; remove the #CRLF$
        
        CountSelectedWords + CountString(LCase(Line), selWord)   ; <-- count SelectedWord in the codeline
        
        FoundReference()\Reference = Line
        Break  ; only one line (evenn if there are more than one SelectedWord )
      EndIf
    Next TokenCounter
  Next LineCounter
  
  ; because of #Constant or *Pointer
  If ListSize(FoundReference()) = 0
    For LineCounter = 0 To CountLines - 1
      Line = Lines(LineCounter)
      CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens())
      For TokenCounter = 0 To CountTokens - 1
        stx = LCase(Tokens(TokenCounter))
        If stx = "#"+selWord Or stx = "*"+selWord
          AddElement(FoundReference())
          FoundReference()\LineNo = LineCounter + 1
          
          Line = Trim(Line)
          Line = Mid(Line, 1, Len(Line)-2)
          
          CountSelectedWords + CountString(LCase(Line), stx)  ; <-- count SelectedWord in the codeline
          
          FoundReference()\Reference = Line
          Break
        EndIf
      Next
    Next
    
    CompilerIf Not #PB_Compiler_Debugger
      If ListSize(FoundReference()) = 0 : End : EndIf
    CompilerEndIf
  EndIf
EndProcedure



; ---------------------------------------------------------------------------------------------------------------------
; XIncludeFile "ColorListIconGadget.pbi"  ; I prefer .pbi (instead of .pb)
; ---------------------------------------------------------------------------------------------------------------------

;... Create brushes for painting item background
Structure MYBRUSHES
  brushDefault.l
  brushSelected.l
EndStructure

; Global brush.MYBRUSHES
Global Dim Colors(1)

; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_INFOBK))
; brush\brushDefault = GetStockObject_(#WHITE_BRUSH)
; brush\brushSelected = $aaaaaa
; brush\brushDefault = $3f3f3f


; ---== Color for Default Text and Selected Word ==--------------------------------------------------------------------


; Colors(0) = #Red                                ; the SelectedWord
Colors(0) = $8080FF                           ; the SelectedWord
                                              ; Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
                                              ; Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT); the default text
Colors(1) = $aaaaaa                           ; the default text

; ---------------------------------------------------------------------------------------------------------------------

Procedure GetCharWidth(gad, c$)
  ProcedureReturn SendMessage_(gad, #LVM_GETSTRINGWIDTH, 0, @c$)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

;Here we add some text to the underlying cell text to store the color info.
Procedure SetColor(gad, row, column, startp, endp, color)
  Protected text$
  If column
    text$ = GetGadgetItemText(gad, row, column)
    ;Now add the new text.
    text$+#coloredChars_Delimeter+Str(startp)+"\"+Str(endp)+"\"+Str(color)
    SetGadgetItemText(gad,row,text$,column)
  EndIf
EndProcedure


; ---== MainWindow Procedures ==---------------------------------------------------------------------------------------

Procedure frmMain_SizeWindow_Event()
  ResizeGadget(#cmbRex, #PB_Ignore, #PB_Ignore, WindowWidth(#frmMain) - 60, 24)
  ResizeGadget(#btnRex, WindowWidth(#frmMain) - 54, #PB_Ignore, #PB_Ignore, #PB_Ignore)
  ResizeGadget(#frmMain_References, #PB_Ignore, #PB_Ignore, WindowWidth(#frmMain) - 6, WindowHeight(#frmMain) - 33)
  SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
EndProcedure

Procedure frmMain_References_Event()
  Protected SelectedLine
  
  SelectedLine = Val(GetGadgetItemText(#frmMain_References, GetGadgetState(#frmMain_References), 0))
  If SelectedLine > 0
    SendMessage_(ScintillaHandle, #SCI_GOTOLINE, SelectedLine - 1, 0)
    SendMessage_(ScintillaHandle, #SCI_ENSUREVISIBLE, SelectedLine - 1, 0)
    SetForegroundWindow_(PbIdeHandle)
    SetActiveWindow_(PbIdeHandle)
  EndIf
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

Procedure myWindowCallback(hwnd, msg, wParam, lParam)
  Protected Result, *nmhdr.NMHDR, *lvCD.NMLVCUSTOMDRAW, subItemRect.RECT, *DrawItem.DRAWITEMSTRUCT, Buffer.s
  Protected thisRow, thisCol, idx
  Protected t$, text$
  ; ; ; ; ; ; ; 	Protected subItemText$, pos, color, c$, nextColor, thisColor
  
  Result = #PB_ProcessPureBasicEvents
  ;;Dim LVColor(0)
  
  Select msg
    Case #WM_NCDESTROY
      DeleteObject_(HightLightBrush)
      DeleteObject_(BackColorBrush)
      
    Case #WM_NOTIFY
      *nmhdr.NMHDR = lParam
      *lvCD.NMLVCUSTOMDRAW = lParam
      If *lvCD\nmcd\hdr\hwndFrom = GadgetID(#frmMain_References) And *lvCD\nmcd\hdr\code = #NM_CUSTOMDRAW
        Select *lvCD\nmcd\dwDrawStage
          Case #CDDS_PREPAINT
            Result = #CDRF_NOTIFYITEMDRAW
          Case #CDDS_ITEMPREPAINT
            Result = #CDRF_NOTIFYSUBITEMDRAW;
          Case #CDDS_ITEMPREPAINT | #CDDS_SUBITEM
            thisRow = *lvCD\nmcd\dwItemSpec
            thisCol = *lvCD\iSubItem
            If thisCol
              ;... Define rect for text
              subItemRect.RECT\left = #LVIR_LABEL
              subItemRect.RECT\top = *lvCD\iSubItem
              ;... Get the subitem rect
              SendMessage_(GadgetID(#frmMain_References), #LVM_GETSUBITEMRECT, thisRow, @subItemRect)
              
              text$ = GetGadgetItemText(#frmMain_References, thisRow, thisCol)
              
              ; 							If GetGadgetState(#frmMain_References) = thisRow
              ; 								;... If item is selected
              ; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushSelected)
              ; 								;               Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
              ; 							Else
              ; 								;... If item is not selected
              ; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushDefault)
              ; 								;               Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT)  ; the default text
              ; 							EndIf
              InflateRect_(subItemRect, -8, 0)
              
              For idx = 1 To CountString(text$, #SelectedWordMarker$) + 1
                t$ = StringField(text$, idx, #SelectedWordMarker$)
                If t$
                  SetTextColor_(*lvCD\nmcd\hdc, colors(idx & 1))
                  SetBkColor_(*lvCD\nmcd\hdc, BackColor)
                  DrawText_(*lvCD\nmcd\hdc, t$, -1, subItemRect, #DT_END_ELLIPSIS|#DT_VCENTER|#DT_SINGLELINE)
                  subItemRect\left + GetCharWidth(*nmhdr\hwndFrom, t$)
                EndIf
              Next idx
              Result = #CDRF_SKIPDEFAULT
            Else
              Result = #CDRF_DODEFAULT
            EndIf
        EndSelect
      EndIf
      
    Case #WM_CTLCOLOREDIT
      Buffer = Space(64)
      If GetClassName_(GetParent_(lParam), @Buffer, 64)
        If Buffer = "ComboBox"        
          SetTextColor_(wParam, #White)
          SetBkMode_(wParam, #TRANSPARENT)
          ProcedureReturn BackColorBrush
        EndIf
      EndIf	
      
    Case #WM_DRAWITEM
      *DrawItem.DRAWITEMSTRUCT = lParam
      If *DrawItem\CtlType = #ODT_COMBOBOX
        If IsGadget(wParam)
          If *DrawItem\itemID <> -1
            If *DrawItem\itemstate & #ODS_SELECTED
              FillRect_(*DrawItem\hDC, *DrawItem\rcitem, HightLightBrush)
            Else
              FillRect_(*DrawItem\hDC, *DrawItem\rcitem, BackColorBrush)
            EndIf
            SetBkMode_(*DrawItem\hDC, #TRANSPARENT)
            SetTextColor_(*DrawItem\hDC, #White)
            Text$ = GetGadgetItemText(*DrawItem\CtlID, *DrawItem\itemID)
            *DrawItem\rcItem\left + DesktopScaledX(4)
            DrawText_(*DrawItem\hDC, Text$, Len(Text$), *DrawItem\rcItem, #DT_LEFT | #DT_SINGLELINE | #DT_VCENTER)
          EndIf
        EndIf
      EndIf
  EndSelect
  ProcedureReturn Result
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------
Procedure SetWindowTheme()
  Protected Theme.s, cmbRexID, ChildGadget, Buffer.s
  If OSVersion() >= #PB_OS_Windows_10
    Theme = "DarkMode_Explorer"
  Else
    Theme = "Explorer"
  EndIf
  
  SetWindowTheme_(GadgetID(#frmMain_References), @Theme, 0)
  
  cmbRexID = GadgetID(#cmbRex)
  Buffer = Space(64)
  If GetClassName_(cmbRexID, @Buffer, 64)
    If Buffer = "ComboBox"
      If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
        SetWindowTheme_(cmbRexID, "DarkMode_CFD", "Combobox")
      Else
        SetWindowTheme_(cmbRexID, @Theme, 0)
      EndIf
    EndIf
  EndIf
  ChildGadget = GetWindow_(cmbRexID, #GW_CHILD)
  If ChildGadget
    Buffer = Space(64)
    If GetClassName_(ChildGadget, @Buffer, 64)
      If Buffer = "ComboBox"
        If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
          SetWindowTheme_(ChildGadget, "DarkMode_CFD", "Combobox")
        Else
          SetWindowTheme_(ChildGadget, @Theme, 0)
        EndIf
      EndIf
    EndIf
  EndIf
EndProcedure

Procedure SetGadgetBorderless(Gadget)
  Protected hGad = GadgetID(Gadget)
  SetWindowLongPtr_(hGad, #GWL_EXSTYLE, GetWindowLongPtr_(hGad, #GWL_EXSTYLE) & (~#WS_EX_CLIENTEDGE))
  ProcedureReturn SetWindowPos_(hGad, 0, 0, 0, 0, 0, #SWP_SHOWWINDOW | #SWP_NOZORDER | #SWP_NOSIZE | #SWP_NOMOVE | #SWP_FRAMECHANGED)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------
Procedure main()
  Protected WWE ;, idx, pos, le
  Protected header
  
  Initialization()  ;
  LookForWordUnderCursor()
  
  ;- GUI
  If OpenWindow(#frmMain, #PB_Ignore, #PB_Ignore, 600, 300,
                "Display all: '" + SelectedWord + "', Found " + CountSelectedWords + " in " + Str(ListSize(FoundReference())) + " Lines",
                #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
    SetWindowColor(#frmMain, $3f3f3f) 
    StickyWindow(#frmMain, #True)
    SetWindowCallback(@myWindowCallback())
    
    ComboBoxGadget(#cmbRex, 3, 3, WindowWidth(#frmMain) - 60, 24, #PB_ComboBox_Editable | #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED)
    ;ComboBoxGadget(#cmbRex, 3, 3, WindowWidth(#frmMain) - 60, 24, #PB_ComboBox_Editable)
    AddGadgetItem(#cmbRex, -1, "(?# Debug, All )\bDebug\b")
    AddGadgetItem(#cmbRex, -1, "(?# Debug, real )(?m)^\h*Debug\b")
    AddGadgetItem(#cmbRex, -1, "(?# WinAPI )(?mi)[a-z][\da-z]*_(?=\h*\()")
    AddGadgetItem(#cmbRex, -1, "(?# Link )https?://[\w.:]+/?(?:[\w/?&=.~;\+!*_#%-]+)")
    AddGadgetItem(#cmbRex, -1, "(?# Procedure )(?mi)^\h*(?:Procedure[CDL$]{0,5}?(?:\h*\.[abcdfilqsuw])?\h+\K)[A-Za-z_]\w*\h*(?=\()")
    AddGadgetItem(#cmbRex, -1, "(?# Macro )(?mi)^\h*Macro\h+\K[A-Za-z_]\w*\h*(?=\()")
    AddGadgetItem(#cmbRex, -1, "(?# Var$ )(?<![#@\w])\w+\$")
    AddGadgetItem(#cmbRex, -1, "(?# Hex num )(?i)\$[\da-f]+")
    AddGadgetItem(#cmbRex, -1, "(?# Comments )(?m)^\h*;.*?(?=\r?$)")
    #q$ = Chr(34)
    AddGadgetItem(#cmbRex, -1, "(?# Comments, All )(?m)^(?:[^" + #q$ + ";]*" + #q$ + "[^" + #q$ + "]*?" + #q$ + ")*[^" + #q$ + ";]*(;.*?)(?=\r?$)")
    AddGadgetItem(#cmbRex, -1, "(?# Structures, Declare )(?i)(?<![=\w" + #q$ + "\\./-])[a-z]\w*\.[a-z]\w+(?![\w" + #q$ + "\\./-])")
    AddGadgetItem(#cmbRex, -1, "(?# Structures, item )(?<![\w.:" + #q$ + "\\])\*?\w+(?:(?:\(\))?\\[\d_a-zA-Z]+)+(?![\w" + #q$ + "\\])")
    AddGadgetItem(#cmbRex, -1, "(?# Structures, Content )(?m)^\h*Structure\h*\K\w+")
    ; 		AddGadgetItem(#cmbRex, -1, ~"(?# Comments )(?m)^(?:[^\";]*\"[^\"]*?\")*[^\";]*(;.*?)(?=\r?$)")
    ; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, Declare )(?i)(?<![=\\w\"\\./-])[a-z]\\w*\\.[a-z]\\w+(?![\\w\"\\\\./-])")
    ; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, item )(?<![\\w.:\"\\\\])\\*?\\w+(?:(?:\\(\\))?\\\\[\\d_a-zA-Z]+)+(?![\\w\"\\\\])")
    AddGadgetItem(#cmbRex, -1, "(?# Types )\b\w+\.[sfdqbliwcapu]\b")
    AddGadgetItem(#cmbRex, -1, "(?# Constants, Declare )(?m)^\h*\K#\w+(?=\h*(?:=|\r?$))")
    AddGadgetItem(#cmbRex, -1, "(?# Constants, All )#\w+\b")
    
    ButtonGadget(#btnRex, WindowWidth(#frmMain) - 54, 3, 24, 24, ">")
    ; ButtonGadget(#btnClose, WindowWidth(#frmMain) - 27, 3, 24, 24, "x") ; to make a black theme
    
    ListIconGadget(#frmMain_References, 3, 30, WindowWidth(#frmMain) - 6, WindowHeight(#frmMain) - 33, "Line ", 96, #PB_ListIcon_FullRowSelect|#PB_ListIcon_GridLines|#PB_ListIcon_AlwaysShowSelection)
    SetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE,GetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE) | #LVS_NOCOLUMNHEADER)
    AddGadgetColumn(#frmMain_References, 1, "", 400)
    SetGadgetColor(#frmMain_References, #PB_Gadget_BackColor, $3f3f3f)
    SetGadgetColor(#frmMain_References, #PB_Gadget_FrontColor, $aaaaaa)
    SetGadgetBorderless(#frmMain_References)  ; by me
    
    ; Optional DarkMode_Explorer theme if OSVersion >= Windows_10 Else Explorer Theme
    SetWindowTheme()
    
    ForEach FoundReference()
      FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)
      
      ; >> first attempt to mark the selected word in the string
      FoundReference()\Reference = ReplaceString(FoundReference()\Reference, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$, #PB_String_NoCase)
      
      AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference)
    Next
    
    ; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
    ; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
    SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
    SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
    
    
    AddKeyboardShortcut(#frmMain, #PB_Shortcut_Escape, #frmMain_Shortcut_Escape_Event)
    BindEvent(#PB_Event_SizeWindow, @frmMain_SizeWindow_Event(), #frmMain)
    BindGadgetEvent(#frmMain_References, @frmMain_References_Event())
    SetActiveGadget(#frmMain_References)
    
    AddKeyboardShortcut(#frmMain, #PB_Shortcut_Control | #PB_Shortcut_C, #Shortcut_Ctrl_C)
    
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
        Case #PB_Event_Menu
          Select EventMenu()
            Case #Shortcut_Ctrl_C
              CopyClipboardReference()
            Case #frmMain_Shortcut_Escape_Event
              Break
          EndSelect
        Case #PB_Event_Gadget
          Select EventGadget()
            Case #btnRex, #cmbRex
              GoRegExp()
          EndSelect
      EndSelect
    ForEver
    
    ; 		DeleteObject_(brush\brushSelected)  ; objects created by CreateSolidBrush_() needs this!
  EndIf
  ProcedureReturn 0  ; not necessary, but looks good/better
EndProcedure

End main()

;----== Bottom of File ==----------------------------------------------------------------------------------------------
AZJIO
Addict
Addict
Posts: 1316
Joined: Sun May 14, 2017 1:48 am

Re: Show all occurrences of a word in the IDE

Post by AZJIO »

ChrisR
Thank.
I also found an example from the author of srod, but there is a lot of code. Now it will be more difficult to make a cross-platform version. I already thought to make an analog for Linux. And also I wanted to make the transfer of the word through the command line, then this tool can work with another editor, such as Notepad ++, at least it will be easy to adapt. Since I use AkelPad, I would like compatibility with it. But so far I don't know how to do it universally. Can pass a flag on the command line, such as "FindAllReferences.exe -s:%word -e:AkelPad". Since the jump to the line is different for everyone.
User avatar
ChrisR
Addict
Addict
Posts: 1127
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: Show all occurrences of a word in the IDE

Post by ChrisR »

AZJIO wrote: Wed Feb 15, 2023 4:55 pm I also found an example from the author of srod, but there is a lot of code.
For Windows, I believe that ObjectColor, in my signature, can be a good source to color the gagdets.
Most of the callback came from Rashad, Srod, chi and others, who made the big part, and on my side, I spent quite a lot of time searching, trying and assembling them in ObjectColor.
AZJIO wrote: Wed Feb 15, 2023 4:55 pm Now it will be more difficult to make a cross-platform version.
Sure, with all the SendMessage_(ScintillaHandle) and now with the windows callback, theme, you move a little away from cross-platform but with some CompilerIF, you should be able to do it anyway.

Thanks for this nice tool and good luck for your future improvements, np++ or AkelPad, I only use PB IDE on my side.
AZJIO
Addict
Addict
Posts: 1316
Joined: Sun May 14, 2017 1:48 am

Re: Show all occurrences of a word in the IDE

Post by AZJIO »

ChrisR
Using your color tweaks, updated the code. Added window size saving. Now I like that the window opens on the right to the full height of the screen, while not closing the code with its window.
I only use PB IDE on my side.
I occasionally browse the AutoIt3 community and would like to share the tool. I think it's not that hard to get a window handle by specifying its class in the ini file, thereby reconfiguring the tool to work with a different Scintilla object. I like the auto-completion in AkelPad, it allows you to insert multi-line code snippets with indentation. In this case, the name of the fragment may differ from the inserted code, you can even write the name of the fragment in your native language, for example, in Russian, and the code will be inserted in English.
Dadlick
New User
New User
Posts: 6
Joined: Thu Feb 16, 2023 3:28 am

Re: Show all occurrences of a word in the IDE

Post by Dadlick »

Added to the header the name of the procedure from which the window was called, if the call was from the body of the procedure. Also added to the reference an indication of the procedure in which it was found.

Code: Select all

; ----------------------------------------------------------------------------
; File : FindAllReferences[Win].pb
; ----------------------------------------------------------------------------
;
;   Description: Find all references of a variable
;            OS: Windows
; English-Forum:
;  French-Forum:
;  German-Forum: http://www.purebasic.fr/german/viewtopic.php?f=8&t=28292
; ----------------------------------------------------------------------------

; MIT License
;
; Copyright (c) 2015 Kiffi
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.
;
; ----------------------------------------------------------------------------
; Change Log :
;   2023-02-12 : New Additions from Mesa, AZJIO, Axolotl
;
; English-Forum: https://www.purebasic.fr/english/viewtopic.php?t=80739
;
; Change Details by Axolotl
;   - Integrated ColorListIconGadget.pb from AZJIO and modified
;   - Add a #PB_Compiler_Debugger mode (for development)
;   - New Structure of the entire application
;   - harmonize the different coding styles (still something to do)
;   - Changing the coloring of the selected words
;   - counting all appearances of the SelectedWord (quick and dirty)
;
; ----------------------------------------------------------------------------

; FindAllReferences

CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
	CompilerError "Windows Only!"
CompilerEndIf

EnableExplicit

;- Structure

Structure sFoundReference
	LineNo.i
	Reference.s
	Selregexp.s
EndStructure

Structure xywhm
	x.i
	y.i
	w.i
	h.i
	m.i
EndStructure

;- Enumeration
Enumeration ; Windows
	#frmMain
EndEnumeration

Enumeration ; Gadgets
	#cmbRex
	#btnRex
	; 	#btnClose
	#frmMain_References
EndEnumeration

Enumeration ; Menu-/Toolbaritems
	#frmMain_Shortcut_Escape_Event
	#Shortcut_Ctrl_C
EndEnumeration

;- Constants
CompilerIf #PB_Compiler_Debugger
	#SelectedWordMarker$ = "|"
CompilerElse
	#SelectedWordMarker$ = Chr(1)  ; not used in source codes
CompilerEndIf
; "	 Line = Trim(Line)"
; "	 |Line| = Trim(|Line|)"
; --> ReplaceString(text$, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$)

;/-----------------------------------------------------------------------------
;| RGB() as HEX() -->     BB GG RR   .. i.e. RGB (1, 2, 3)    -->    03 02 01
;| RGB() as HEX() -->  AA BB GG RR   .. i.e. RGBA(1, 2, 3, 4) --> 04 03 02 01
;\

#coloredChars_Delimeter = "{***\"

;- Global
Global ini$ = LSet(ProgramFilename(), Len(ProgramFilename()) - 3) + "ini"
Global centered
Global xywh.xywhm
Global xywh2.xywhm
Global xywh\w = 600
Global xywh\h = 300

Global PbIdeHandle, ScintillaHandle

Global SelectedWord.s, ScintillaText.s
Global CountSelectedWords    ; new, because we want to know all references (not only the lines)
Global NewList FoundReference.sFoundReference()
Global Dim Lines.s(0)

Global BackColor = $3f3f3f
Global HightLightBrush = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
; Global HightLightBrush = CreateSolidBrush_($423926)
Global BackColorBrush = CreateSolidBrush_(BackColor)

; ; ; Global PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
; ; ; If PbIdeHandle = 0 : End : EndIf
; ; ;
; ; ; Global ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
; ; ; If ScintillaHandle = 0 : End : EndIf

; ---== Procedures ==--------------------------------------------------------------------------------------------------

; AZJIO
Procedure.s LTrimChar(String$, TrimChar$ = #CRLF$ + #TAB$ + #FF$ + #VT$ + " ")
	Protected *memChar, *c.Character, *jc.Character
	
	If Not Asc(String$)
		ProcedureReturn ""
	EndIf
	
	*c.Character = @String$
	*memChar = @TrimChar$
	
	While *c\c
		*jc.Character = *memChar
		
		While *jc\c
			If *c\c = *jc\c
				*c\c = 0
				Break
			EndIf
			*jc + SizeOf(Character)
		Wend
		
		If *c\c
			String$ = PeekS(*c)
			Break
		EndIf
		*c + SizeOf(Character)
	Wend
	
	ProcedureReturn String$
EndProcedure

Procedure.s RemoveLeadingWhitespaceFromString(InString.s)
	While Left(InString, 1) = Chr(32) Or Left(InString, 1) = Chr(9)
		InString = LTrim(InString, Chr(32))
		InString = LTrim(InString, Chr(9))
	Wend
	ProcedureReturn InString
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

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

; ---------------------------------------------------------------------------------------------------------------------

Procedure Initialization()
	
	PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
	CompilerIf Not #PB_Compiler_Debugger
		If PbIdeHandle = 0 : End : EndIf
	CompilerEndIf
	
	ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
	CompilerIf Not #PB_Compiler_Debugger
		If ScintillaHandle = 0 : End : EndIf
	CompilerEndIf
	
	
	SelectedWord.s = GetEnvironmentVariable("PB_TOOL_Word")
	CompilerIf Not #PB_Compiler_Debugger
		If SelectedWord = "" : End : EndIf
	CompilerEndIf
	
	ScintillaText.s = GetScintillaText()
	CompilerIf Not #PB_Compiler_Debugger
		If ScintillaText = "" : End : EndIf
	CompilerEndIf
	
	; >> These are test pattern for development
	CompilerIf #PB_Compiler_Debugger
		
		If SelectedWord = ""
			;     SelectedWord = "Line"    ; try one of these
			;     SelectedWord = "#Line"   ; -"-  #Line could be in a comment also
			SelectedWord = "ScintillaText"   ; -"-
		EndIf
		
		If ScintillaText = ""
			#File = 0
			If ReadFile(#File, #PB_Compiler_File)
				ScintillaText = ReadString(#File, #PB_UTF8 | #PB_File_IgnoreEOL)
				CloseFile(#File)
			EndIf
			; 			RunProgram("explorer.exe", "/Select," + #PB_Compiler_File, "")
			
			; 			ScintillaText = "" + #CRLF$ +
			; 			                "#Line = #LF ;  #Line could be in a comment also " + #CRLF$ +
			; 			                "Procedure Test(*Line)  ; pointer *Line " + #CRLF$ +
			; 			                "" + #CRLF$ +
			; 			                "If SelectedWord = LCase(Tokens(TokenCounter))" + #CRLF$ +
			; 			                "	 AddElement(FoundReference())" + #CRLF$ +
			; 			                "	 FoundReference()\LineNo = LineCounter + 1" + #CRLF$ +
			; 			                "	 Line = Trim(Line)" + #CRLF$ +
			; 			                "	 Line = Mid(Line, 1, Len(Line)-2)" + #CRLF$ +
			; 			                "	 FoundReference()\Reference = Line" + #CRLF$ +
			; 			                "EndIf" + #CRLF$ +
			; 			                "" ; End of Text
		EndIf
	CompilerEndIf
	ProcedureReturn 0  ; default (ZERO is returned by default, even if there is no ProcedureReturn)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

; ChrisR
Procedure CopyClipboardReference()
	Protected text$
	PushListPosition(FoundReference()) 
	ForEach FoundReference()
		If text$ : text$ + #CRLF$ : EndIf
		If Right(FoundReference()\Reference, 2) = #CRLF$
			text$ + Left(FoundReference()\Reference, Len(FoundReference()\Reference) - 2)
		Else
			text$ + FoundReference()\Reference
		EndIf
	Next
	PopListPosition(FoundReference())
	If text$
		text$ = ReplaceString(text$, #SelectedWordMarker$, "")
		SetClipboardText(text$)
		Protected Title$ = GetWindowTitle(#frmMain)
		SetWindowTitle(#frmMain, Title$ + " (Reference copied To the clipboard)")
		Delay(500)
		SetWindowTitle(#frmMain, Title$)
	EndIf
EndProcedure

; AZJIO
Procedure GoRegExp()
	; LINK : https://www.purebasic.fr/english/viewtopic.php?p=595832#p595832
	Protected rex, LSize, Pos = 0, i, tmp$
	Protected Dim Tokens.s(0)
	
	ClearGadgetItems(#frmMain_References)
	ClearList(FoundReference())
	
	tmp$ = GetGadgetText(#cmbRex)
	If Not Asc(tmp$)
		ProcedureReturn
	EndIf
	rex = CreateRegularExpression(#PB_Any, tmp$)
	; 	CountTokens = ExtractRegularExpression(rex, ScintillaText, Tokens()) ; tokenize the line
	
	; 	Debug ArraySize(Lines())
	
	If rex
		If ExamineRegularExpression(rex, ScintillaText)
			While NextRegularExpressionMatch(rex)
				If Not FindString(RegularExpressionMatchString(rex), #LF$)
					AddElement(FoundReference())
					FoundReference()\Selregexp = RegularExpressionMatchString(rex)
					FoundReference()\LineNo = RegularExpressionMatchPosition(rex)
					; 					Debug  FoundReference()\LineNo
				EndIf
			Wend
		EndIf
	Else
		MessageRequester("Regular expression error", RegularExpressionError())
		ProcedureReturn
	EndIf
	
	LSize = ListSize(FoundReference())
	If LSize > 0
		; 		If LSize > 5000 And MessageRequester("Continue?", "Found" + Str(LSize) + " rows, Continue?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_No
		; 			ProcedureReturn
		; 		EndIf
		
		Pos = 0
		i = 0
		SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 0, 0)
		ForEach FoundReference()
			While Pos < FoundReference()\LineNo
				Pos = FindString(ScintillaText, #LF$, Pos + 1)
				If Pos
					i + 1
				Else
					Break
				EndIf
				; 				Debug Str(FoundReference()\LineNo) + " "  + Str(Pos)
			Wend
			If i < 1 Or i > ArraySize(Lines())
				Continue
			EndIf
			FoundReference()\LineNo = i
			FoundReference()\Reference = Lines(i - 1)
			
			
			FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)
			
			; >> first attempt to mark the selected word in the string
			FoundReference()\Reference = ReplaceString(FoundReference()\Reference, FoundReference()\Selregexp, #SelectedWordMarker$ + FoundReference()\Selregexp + #SelectedWordMarker$, #PB_String_NoCase)
			
			AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference)
		Next
		; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
		; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 1, 0)
		
		SelectedWord = "regexp"
		SetWindowTitle(#frmMain, "Display all: '" + SelectedWord + "', Found " + " in " + Str(ListSize(FoundReference())) + " Lines")
	EndIf
EndProcedure


Procedure LookForWordUnderCursor()
	; LINK : http://www.purebasic.fr/english/viewtopic.php?f=12&t=37823
	Protected RegexLines, PbRegexTokens
	Protected CountLines, LineCounter, CountTokens, TokenCounter
	Protected Line.s, selWord.s, stx.s
	Protected Dim Tokens.s(0)
	
	RegexLines = CreateRegularExpression(#PB_Any , ".*\r\n")
	PbRegexTokens = CreateRegularExpression(#PB_Any, #DOUBLEQUOTE$ + "[^" + #DOUBLEQUOTE$ + "]*" + #DOUBLEQUOTE$ + "|[\*]?[a-zA-Z_]+[\w]*[\x24]?|#[a-zA-Z_]+[\w]*[\x24]?|[\[\]\(\)\{\}]|[-+]?[0-9]*\.?[0-9]+|;.*|\.|\+|-|[&@!\\\/\*,\|]|::|:|\|<>|>>|<<|=>{1}|>={1}|<={1}|=<{1}|={1}|<{1}|>{1}|\x24+[0-9a-fA-F]+|\%[0-1]*|%|'")
	
	CountLines = CountString(ScintillaText, #CRLF$)
	
	CountLines = ExtractRegularExpression(RegexLines, ScintillaText, Lines())
	
	selWord = LCase(SelectedWord)  ; keep the original writing
	CountSelectedWords = 0		   ; init for new search
	
	For LineCounter = 0 To CountLines - 1
		Line = Lines(LineCounter)
		
		;Debug "tokenize Line '" + Line + "'"
		
		CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens()) ; tokenize the line
		
		For TokenCounter = 0 To CountTokens - 1
			;Debug "  check Token '" + Tokens(TokenCounter) + "'"
			
			If selWord = LCase(Tokens(TokenCounter))
				AddElement(FoundReference())
				FoundReference()\LineNo = LineCounter + 1
				
				Line = Trim(Line)
				Line = Mid(Line, 1, Len(Line)-2)  ; remove the #CRLF$
				
				CountSelectedWords + CountString(LCase(Line), selWord)   ; <-- count SelectedWord in the codeline
				
				FoundReference()\Reference = Line
				Break  ; only one line (evenn if there are more than one SelectedWord )
			EndIf
		Next TokenCounter
	Next LineCounter
	
	; because of #Constant or *Pointer
	If ListSize(FoundReference()) = 0
		For LineCounter = 0 To CountLines - 1
			Line = Lines(LineCounter)
			CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens())
			For TokenCounter = 0 To CountTokens - 1
				stx = LCase(Tokens(TokenCounter))
				If stx = "#"+selWord Or stx = "*"+selWord
					AddElement(FoundReference())
					FoundReference()\LineNo = LineCounter + 1
					
					Line = Trim(Line)
					Line = Mid(Line, 1, Len(Line)-2)
					
					CountSelectedWords + CountString(LCase(Line), stx)  ; <-- count SelectedWord in the codeline
					
					FoundReference()\Reference = Line
					Break
				EndIf
			Next
		Next
		
		CompilerIf Not #PB_Compiler_Debugger
			If ListSize(FoundReference()) = 0 : End : EndIf
		CompilerEndIf
	EndIf
EndProcedure



; ---------------------------------------------------------------------------------------------------------------------
; XIncludeFile "ColorListIconGadget.pbi"  ; I prefer .pbi (instead of .pb)
; ---------------------------------------------------------------------------------------------------------------------

;... Create brushes for painting item background
Structure MYBRUSHES
	brushDefault.l
	brushSelected.l
EndStructure

; Global brush.MYBRUSHES
Global Dim Colors(1)

; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_INFOBK))
; brush\brushDefault = GetStockObject_(#WHITE_BRUSH)
; brush\brushSelected = $aaaaaa
; brush\brushDefault = $3f3f3f


; ---== Color for Default Text and Selected Word ==--------------------------------------------------------------------


; Colors(0) = #Red                                ; the SelectedWord
Colors(0) = $8080FF                           ; the SelectedWord
											  ; Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
											  ; Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT); the default text
Colors(1) = $cccccc							  ; the default text

; ---------------------------------------------------------------------------------------------------------------------

Procedure GetCharWidth(gad, c$)
	ProcedureReturn SendMessage_(gad, #LVM_GETSTRINGWIDTH, 0, @c$)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

;Here we add some text to the underlying cell text to store the color info.
Procedure SetColor(gad, row, column, startp, endp, color)
	Protected text$
	If column
		text$ = GetGadgetItemText(gad, row, column)
		;Now add the new text.
		text$+#coloredChars_Delimeter+Str(startp)+"\"+Str(endp)+"\"+Str(color)
		SetGadgetItemText(gad,row,text$,column)
	EndIf
EndProcedure


; ---== MainWindow Procedures ==---------------------------------------------------------------------------------------

Procedure frmMain_SizeWindow_Event()
	xywh\w = WindowWidth(#frmMain)
	xywh\h = WindowHeight(#frmMain)
	ResizeGadget(#cmbRex, #PB_Ignore, #PB_Ignore, xywh\w - 34, 24)
	ResizeGadget(#btnRex, xywh\w - 28, #PB_Ignore, #PB_Ignore, #PB_Ignore)
	ResizeGadget(#frmMain_References, #PB_Ignore, #PB_Ignore, xywh\w - 6, xywh\h - 33)
; 	SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
EndProcedure

Procedure frmMain_References_Event()
	Protected SelectedLine
	SelectedLine = Val(GetGadgetItemText(#frmMain_References, GetGadgetState(#frmMain_References), 0))
	If SelectedLine > 0
		SendMessage_(ScintillaHandle, #SCI_GOTOLINE, SelectedLine - 1, 0)
		SendMessage_(ScintillaHandle, #SCI_ENSUREVISIBLE, SelectedLine - 1, 0)
		SetForegroundWindow_(PbIdeHandle)
		SetActiveWindow_(PbIdeHandle)
	EndIf
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

Procedure myWindowCallback(hwnd, msg, wParam, lParam)
	Protected Result, *nmhdr.NMHDR, *lvCD.NMLVCUSTOMDRAW, subItemRect.RECT, *DrawItem.DRAWITEMSTRUCT, Buffer.s
	Protected thisRow, thisCol, idx
	Protected t$, text$
	; ; ; ; ; ; ; 	Protected subItemText$, pos, color, c$, nextColor, thisColor
	
	Result = #PB_ProcessPureBasicEvents
	;;Dim LVColor(0)
	
	Select msg
		Case #WM_NCDESTROY
			DeleteObject_(HightLightBrush)
			DeleteObject_(BackColorBrush)
			
		Case #WM_NOTIFY
			*nmhdr.NMHDR = lParam
			*lvCD.NMLVCUSTOMDRAW = lParam
			If *lvCD\nmcd\hdr\hwndFrom = GadgetID(#frmMain_References) And *lvCD\nmcd\hdr\code = #NM_CUSTOMDRAW
				Select *lvCD\nmcd\dwDrawStage
					Case #CDDS_PREPAINT
						Result = #CDRF_NOTIFYITEMDRAW
					Case #CDDS_ITEMPREPAINT
						Result = #CDRF_NOTIFYSUBITEMDRAW;
					Case #CDDS_ITEMPREPAINT | #CDDS_SUBITEM
						thisRow = *lvCD\nmcd\dwItemSpec
						thisCol = *lvCD\iSubItem
						If thisCol
							;... Define rect for text
							subItemRect.RECT\left = #LVIR_LABEL
							subItemRect.RECT\top = *lvCD\iSubItem
							;... Get the subitem rect
							SendMessage_(GadgetID(#frmMain_References), #LVM_GETSUBITEMRECT, thisRow, @subItemRect)
							
							text$ = GetGadgetItemText(#frmMain_References, thisRow, thisCol)
							
							; 							If GetGadgetState(#frmMain_References) = thisRow
							; 								;... If item is selected
							; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushSelected)
							; 								;               Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
							; 							Else
							; 								;... If item is not selected
							; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushDefault)
							; 								;               Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT)  ; the default text
							; 							EndIf
							InflateRect_(subItemRect, -8, 0)
							
							For idx = 1 To CountString(text$, #SelectedWordMarker$) + 1
								t$ = StringField(text$, idx, #SelectedWordMarker$)
								If t$
									SetTextColor_(*lvCD\nmcd\hdc, colors(idx & 1))
									SetBkColor_(*lvCD\nmcd\hdc, BackColor)
									DrawText_(*lvCD\nmcd\hdc, t$, -1, subItemRect, #DT_END_ELLIPSIS|#DT_VCENTER|#DT_SINGLELINE)
									subItemRect\left + GetCharWidth(*nmhdr\hwndFrom, t$)
								EndIf
							Next idx
							Result = #CDRF_SKIPDEFAULT
						Else
							Result = #CDRF_DODEFAULT
						EndIf
				EndSelect
			EndIf
			
		Case #WM_CTLCOLOREDIT
			Buffer = Space(64)
			If GetClassName_(GetParent_(lParam), @Buffer, 64)
				If Buffer = "ComboBox"        
					SetTextColor_(wParam, #White)
					SetBkMode_(wParam, #TRANSPARENT)
					ProcedureReturn BackColorBrush
				EndIf
			EndIf	
			
		Case #WM_DRAWITEM
			*DrawItem.DRAWITEMSTRUCT = lParam
			If *DrawItem\CtlType = #ODT_COMBOBOX
				If IsGadget(wParam)
					If *DrawItem\itemID <> -1
						If *DrawItem\itemstate & #ODS_SELECTED
							FillRect_(*DrawItem\hDC, *DrawItem\rcitem, HightLightBrush)
						Else
							FillRect_(*DrawItem\hDC, *DrawItem\rcitem, BackColorBrush)
						EndIf
						SetBkMode_(*DrawItem\hDC, #TRANSPARENT)
						SetTextColor_(*DrawItem\hDC, $cccccc)
						Text$ = GetGadgetItemText(*DrawItem\CtlID, *DrawItem\itemID)
						*DrawItem\rcItem\left + DesktopScaledX(4)
						DrawText_(*DrawItem\hDC, Text$, Len(Text$), *DrawItem\rcItem, #DT_LEFT | #DT_SINGLELINE | #DT_VCENTER)
					EndIf
				EndIf
			EndIf
	EndSelect
	ProcedureReturn Result
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------
Procedure SetWindowTheme()
	Protected Theme.s, cmbRexID, ChildGadget, Buffer.s
	If OSVersion() >= #PB_OS_Windows_10
		Theme = "DarkMode_Explorer"
	Else
		Theme = "Explorer"
	EndIf
	
	SetWindowTheme_(GadgetID(#frmMain_References), @Theme, 0)
	
	cmbRexID = GadgetID(#cmbRex)
	Buffer = Space(64)
	If GetClassName_(cmbRexID, @Buffer, 64)
		If Buffer = "ComboBox"
			If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
				SetWindowTheme_(cmbRexID, "DarkMode_CFD", "Combobox")
			Else
				SetWindowTheme_(cmbRexID, @Theme, 0)
			EndIf
		EndIf
	EndIf
	ChildGadget = GetWindow_(cmbRexID, #GW_CHILD)
	If ChildGadget
		Buffer = Space(64)
		If GetClassName_(ChildGadget, @Buffer, 64)
			If Buffer = "ComboBox"
				If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
					SetWindowTheme_(ChildGadget, "DarkMode_CFD", "Combobox")
				Else
					SetWindowTheme_(ChildGadget, @Theme, 0)
				EndIf
			EndIf
		EndIf
	EndIf
EndProcedure

Procedure SetGadgetBorderless(Gadget)
	Protected hGad = GadgetID(Gadget)
	SetWindowLongPtr_(hGad, #GWL_EXSTYLE, GetWindowLongPtr_(hGad, #GWL_EXSTYLE) & (~#WS_EX_CLIENTEDGE))
	ProcedureReturn SetWindowPos_(hGad, 0, 0, 0, 0, 0, #SWP_SHOWWINDOW | #SWP_NOZORDER | #SWP_NOSIZE | #SWP_NOMOVE | #SWP_FRAMECHANGED)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

Procedure.s FindProcedureName(CursorLine)
  Protected LineCounter,  Line.s
  For LineCounter = CursorLine - 1 To 1 Step - 1   
    Line = RemoveLeadingWhitespaceFromString(StringField(ScintillaText, LineCounter, #CRLF$))
    If Left(LCase(Line), Len("endprocedure")) = "endprocedure"
      Break
    EndIf
    If Left(LCase(Line), Len("procedure")) = "procedure"
      If Left(LCase(Line), Len("procedurereturn")) <> "procedurereturn"
        ProcedureReturn    Line
        Break
      EndIf
    EndIf 
  Next
  ProcedureReturn ""
EndProcedure

Procedure main()
	Protected WWE ;, idx, pos, le
	Protected YouAreHere.s 
	Protected CursorLine = Val(StringField(GetEnvironmentVariable("PB_TOOL_Cursor"), 1, "x"))
	
	Initialization()  ;
	LookForWordUnderCursor()
	If ScintillaText <> ""  
	  YouAreHere = FindProcedureName(CursorLine)
	  If YouAreHere <> ""
	    YouAreHere = "You are here:" + YouAreHere
	  EndIf
	EndIf
	
	;--> ini
	If OpenPreferences(ini$) ; открываем ini
		If PreferenceGroup("set")
			xywh\x = ReadPreferenceInteger("x", xywh\x)
			xywh\y = ReadPreferenceInteger("y", xywh\y)
			xywh\w = ReadPreferenceInteger("w", xywh\w)
			xywh\h = ReadPreferenceInteger("h", xywh\h)
		EndIf
		ClosePreferences()
	EndIf
	If xywh\x = 0 And xywh\y = 0
		centered = #PB_Window_ScreenCentered
	EndIf
	CopyStructure(@xywh, @xywh2, xywhm)
	
	
	;- GUI
	If OpenWindow(#frmMain, xywh\x, xywh\y, xywh\w, xywh\h,
	              "Display all: '" + SelectedWord + "', Found " + CountSelectedWords + " in " + Str(ListSize(FoundReference())) + " Lines " + YouAreHere,
	              #PB_Window_SystemMenu | #PB_Window_SizeGadget | centered)
		SetWindowColor(#frmMain, $3f3f3f) 
		StickyWindow(#frmMain, #True)
		SetWindowCallback(@myWindowCallback())
		
		ComboBoxGadget(#cmbRex, 3, 3, WindowWidth(#frmMain) - 34, 24, #PB_ComboBox_Editable | #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED)
		;ComboBoxGadget(#cmbRex, 3, 3, WindowWidth(#frmMain) - 60, 24, #PB_ComboBox_Editable)
		AddGadgetItem(#cmbRex, -1, "(?# Debug, All )\bDebug\b")
		AddGadgetItem(#cmbRex, -1, "(?# Debug, real )(?m)^\h*Debug\b")
		AddGadgetItem(#cmbRex, -1, "(?# WinAPI )(?mi)[a-z][\da-z]*_(?=\h*\()")
		AddGadgetItem(#cmbRex, -1, "(?# Link )https?://[\w.:]+/?(?:[\w/?&=.~;\+!*_#%-]+)")
		AddGadgetItem(#cmbRex, -1, "(?# Procedure )(?mi)^\h*(?:Procedure[CDL$]{0,5}?(?:\h*\.[abcdfilqsuw])?\h+\K)[A-Za-z_]\w*\h*(?=\()")
		AddGadgetItem(#cmbRex, -1, "(?# Macro )(?mi)^\h*Macro\h+\K[A-Za-z_]\w*\h*(?=\()")
		AddGadgetItem(#cmbRex, -1, "(?# Var$ )(?<![#@\w])\w+\$")
		AddGadgetItem(#cmbRex, -1, "(?# Hex num )(?i)\$[\da-f]+")
		AddGadgetItem(#cmbRex, -1, "(?# Comments )(?m)^\h*;.*?(?=\r?$)")
		#q$ = Chr(34)
		AddGadgetItem(#cmbRex, -1, "(?# Comments, All )(?m)^(?:[^" + #q$ + ";]*" + #q$ + "[^" + #q$ + "]*?" + #q$ + ")*[^" + #q$ + ";]*(;.*?)(?=\r?$)")
		AddGadgetItem(#cmbRex, -1, "(?# Structures, Declare )(?i)(?<![=\w" + #q$ + "\\./-])[a-z]\w*\.[a-z]\w+(?![\w" + #q$ + "\\./-])")
		AddGadgetItem(#cmbRex, -1, "(?# Structures, item )(?<![\w.:" + #q$ + "\\])\*?\w+(?:(?:\(\))?\\[\d_a-zA-Z]+)+(?![\w" + #q$ + "\\])")
		AddGadgetItem(#cmbRex, -1, "(?# Structures, Content )(?m)^\h*Structure\h*\K\w+")
		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Comments )(?m)^(?:[^\";]*\"[^\"]*?\")*[^\";]*(;.*?)(?=\r?$)")
		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, Declare )(?i)(?<![=\\w\"\\./-])[a-z]\\w*\\.[a-z]\\w+(?![\\w\"\\\\./-])")
		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, item )(?<![\\w.:\"\\\\])\\*?\\w+(?:(?:\\(\\))?\\\\[\\d_a-zA-Z]+)+(?![\\w\"\\\\])")
		AddGadgetItem(#cmbRex, -1, "(?# Types )\b\w+\.[sfdqbliwcapu]\b")
		AddGadgetItem(#cmbRex, -1, "(?# Constants, Declare )(?m)^\h*\K#\w+(?=\h*(?:=|\r?$))")
		AddGadgetItem(#cmbRex, -1, "(?# Constants, All )#\w+\b")
		
		ButtonGadget(#btnRex, WindowWidth(#frmMain) - 28, 3, 24, 24, ">")
		; ButtonGadget(#btnClose, WindowWidth(#frmMain) - 27, 3, 24, 24, "x") ; to make a black theme
		
		ListIconGadget(#frmMain_References, 3, GadgetY(#cmbRex)  + GadgetHeight(#cmbRex) + 3, WindowWidth(#frmMain) - 6, WindowHeight(#frmMain) - GadgetHeight(#cmbRex) - GadgetY(#cmbRex) - 6, "строка ", 96, #PB_ListIcon_FullRowSelect|#PB_ListIcon_GridLines|#PB_ListIcon_AlwaysShowSelection)
		SetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE,GetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE) | #LVS_NOCOLUMNHEADER)
		AddGadgetColumn(#frmMain_References, 1, "", 400)
		SetGadgetColor(#frmMain_References, #PB_Gadget_BackColor, $3f3f3f)
		SetGadgetColor(#frmMain_References, #PB_Gadget_FrontColor, $cccccc)
		SetGadgetBorderless(#frmMain_References)  ; by me
		
		; Optional DarkMode_Explorer theme if OSVersion >= Windows_10 Else Explorer Theme
		SetWindowTheme()
		
		ForEach FoundReference()
			FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)
			
			; >> first attempt to mark the selected word in the string
			FoundReference()\Reference = ReplaceString(FoundReference()\Reference, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$, #PB_String_NoCase)
			
			If Left(LCase(FoundReference()\Reference), Len("procedure")) <> "procedure"
			  FoundReference()\Reference = FoundReference()\Reference + " in: " + FindProcedureName(FoundReference()\LineNo)
			EndIf
			
			AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference)
		Next
		
		; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
		; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
		
		
		AddKeyboardShortcut(#frmMain, #PB_Shortcut_Escape, #frmMain_Shortcut_Escape_Event)
		BindEvent(#PB_Event_SizeWindow, @frmMain_SizeWindow_Event(), #frmMain)
		BindGadgetEvent(#frmMain_References, @frmMain_References_Event())
		SetActiveGadget(#frmMain_References)
		
		AddKeyboardShortcut(#frmMain, #PB_Shortcut_Control | #PB_Shortcut_C, #Shortcut_Ctrl_C)
		
		
		Repeat
			Select WaitWindowEvent()
				Case #PB_Event_MoveWindow
					xywh\x = WindowX(#frmMain) 
					xywh\y = WindowY(#frmMain) 
				Case #PB_Event_CloseWindow
; 					Если размеры окна изменились, то сохраняем.
					If Not CompareMemory(@xywh, @xywh2, SizeOf(xywhm))
						If OpenPreferences(ini$) Or CreatePreferences(ini$)
							PreferenceGroup("set")
							WritePreferenceInteger("x", xywh\x)
							WritePreferenceInteger("y", xywh\y)
							WritePreferenceInteger("w", xywh\w)
							WritePreferenceInteger("h", xywh\h)
							ClosePreferences()
						EndIf
					EndIf
					Break
				Case #PB_Event_Menu
					Select EventMenu()
						Case #Shortcut_Ctrl_C
							CopyClipboardReference()
						Case #frmMain_Shortcut_Escape_Event
							Break
					EndSelect
				Case #PB_Event_Gadget
					Select EventGadget()
						Case #btnRex, #cmbRex
							GoRegExp()
					EndSelect
			EndSelect
		ForEver
		
		; 		DeleteObject_(brush\brushSelected)  ; objects created by CreateSolidBrush_() needs this!
	EndIf
	ProcedureReturn 0  ; not necessary, but looks good/better
EndProcedure

End main()

;----== Bottom of File ==----------------------------------------------------------------------------------------------
AZJIO
Addict
Addict
Posts: 1316
Joined: Sun May 14, 2017 1:48 am

Re: Show all occurrences of a word in the IDE

Post by AZJIO »

Dadlick
I'm not sure if this information is needed, but it would be interesting to have information about the line from which the call was made, for example, the information in the title ("from 115"). As well as a button to return to the initial line. This would allow moving between two sections of the code, one of which is the one you are looking for, the second is the place where the work of the developed function takes place. That is, we move, for example, between the function call point and the function body. Can make a menu on the right mouse button, so as not to litter the interface with different buttons.
Dadlick
New User
New User
Posts: 6
Joined: Thu Feb 16, 2023 3:28 am

Re: Show all occurrences of a word in the IDE

Post by Dadlick »

AZJIO
I need this information, let's say I'm looking for in which procedure this variable or with the same name is still used, and here it's immediately obvious. With a back button, yes, such a position is also needed, but I think that it’s better not to have a button, but the very first entry in the table. And I think that it’s probably better to move the procedure name to the next column, and rework the algorithm for finding the procedure name, search from the end of the list and return the line number of the procedure name along with the name, then if the next reference has a number less than the procedure number, then don’t search, but output previous search result. If the procedure name is not found return -1 as a flag.
AZJIO
Addict
Addict
Posts: 1316
Joined: Sun May 14, 2017 1:48 am

Re: Show all occurrences of a word in the IDE

Post by AZJIO »

Dadlick
Usually, when you edit a procedure, you already know what procedure you are in. If the functional during the jump showed inside which procedure this variable is located, then it is informative.
With a back button, yes, such a position is also needed, but I think that it’s better not to have a button, but the very first entry in the table.
Maybe then return the table header and insert the line of the current call there? It will always be available at the top.

I also want to remember to make a function so that jumping to a line scrolls this line to the center, and not to the top or to the bottom. After all, you can get the number of visible lines and create scrolling so that the desired line is in the center.
Mesa
Enthusiast
Enthusiast
Posts: 345
Joined: Fri Feb 24, 2012 10:19 am

Re: Show all occurrences of a word in the IDE

Post by Mesa »

Is that possible to add GridLines ?

M.
Dadlick
New User
New User
Posts: 6
Joined: Thu Feb 16, 2023 3:28 am

Re: Show all occurrences of a word in the IDE

Post by Dadlick »

AZJIO wrote: Thu Feb 16, 2023 8:53 am Maybe then return the table header
Returned the table header, and placed the search text and line number in it on the first call, when calling the GoRegExp () procedure, the table header is hidden

Code: Select all

; ----------------------------------------------------------------------------
; File : FindAllReferences[Win].pb
; ----------------------------------------------------------------------------
;
;   Description: Find all references of a variable
;            OS: Windows
; English-Forum:
;  French-Forum:
;  German-Forum: http://www.purebasic.fr/german/viewtopic.php?f=8&t=28292
; ----------------------------------------------------------------------------

; MIT License
;
; Copyright (c) 2015 Kiffi
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.
;
; ----------------------------------------------------------------------------
; Change Log :
;   2023-02-12 : New Additions from Mesa, AZJIO, Axolotl
;
; English-Forum: https://www.purebasic.fr/english/viewtopic.php?t=80739
;
; Change Details by Axolotl
;   - Integrated ColorListIconGadget.pb from AZJIO and modified
;   - Add a #PB_Compiler_Debugger mode (for development)
;   - New Structure of the entire application
;   - harmonize the different coding styles (still something to do)
;   - Changing the coloring of the selected words
;   - counting all appearances of the SelectedWord (quick and dirty)
;
; ----------------------------------------------------------------------------

; FindAllReferences

CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
	CompilerError "Windows Only!"
CompilerEndIf

EnableExplicit

;- Structure

Structure sFoundReference
	LineNo.i
	Reference.s
	Selregexp.s
	InProcedure.s
EndStructure

Structure xywhm
	x.i
	y.i
	w.i
	h.i
	m.i
EndStructure

;- Enumeration
Enumeration ; Windows
	#frmMain
EndEnumeration

Enumeration ; Gadgets
	#cmbRex
	#btnRex
	; 	#btnClose
	#frmMain_References
EndEnumeration

Enumeration ; Menu-/Toolbaritems
	#frmMain_Shortcut_Escape_Event
	#Shortcut_Ctrl_C
EndEnumeration

;- Constants
CompilerIf #PB_Compiler_Debugger
	#SelectedWordMarker$ = "|"
CompilerElse
	#SelectedWordMarker$ = Chr(1)  ; not used in source codes
CompilerEndIf
; "	 Line = Trim(Line)"
; "	 |Line| = Trim(|Line|)"
; --> ReplaceString(text$, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$)

;/-----------------------------------------------------------------------------
;| RGB() as HEX() -->     BB GG RR   .. i.e. RGB (1, 2, 3)    -->    03 02 01
;| RGB() as HEX() -->  AA BB GG RR   .. i.e. RGBA(1, 2, 3, 4) --> 04 03 02 01
;\

#coloredChars_Delimeter = "{***\"

;- Global
Global ini$ = LSet(ProgramFilename(), Len(ProgramFilename()) - 3) + "ini"
Global centered
Global xywh.xywhm
Global xywh2.xywhm
Global xywh\w = 600
Global xywh\h = 300

Global PbIdeHandle, ScintillaHandle
Global frmMain_References

Global SelectedWord.s, ScintillaText.s
Global CountSelectedWords    ; new, because we want to know all references (not only the lines)
Global NewList FoundReference.sFoundReference()
Global Dim Lines.s(0)

Global BackColor = $3f3f3f
Global HightLightBrush = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
; Global HightLightBrush = CreateSolidBrush_($423926)
Global BackColorBrush = CreateSolidBrush_(BackColor)

; ; ; Global PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
; ; ; If PbIdeHandle = 0 : End : EndIf
; ; ;
; ; ; Global ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
; ; ; If ScintillaHandle = 0 : End : EndIf

; ---== Procedures ==--------------------------------------------------------------------------------------------------

; AZJIO
Procedure.s LTrimChar(String$, TrimChar$ = #CRLF$ + #TAB$ + #FF$ + #VT$ + " ")
	Protected *memChar, *c.Character, *jc.Character
	
	If Not Asc(String$)
		ProcedureReturn ""
	EndIf
	
	*c.Character = @String$
	*memChar = @TrimChar$
	
	While *c\c
		*jc.Character = *memChar
		
		While *jc\c
			If *c\c = *jc\c
				*c\c = 0
				Break
			EndIf
			*jc + SizeOf(Character)
		Wend
		
		If *c\c
			String$ = PeekS(*c)
			Break
		EndIf
		*c + SizeOf(Character)
	Wend
	
	ProcedureReturn String$
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

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

; ---------------------------------------------------------------------------------------------------------------------

Procedure Initialization()
	
	PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
	CompilerIf Not #PB_Compiler_Debugger
		If PbIdeHandle = 0 : End : EndIf
	CompilerEndIf
	
	ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
	CompilerIf Not #PB_Compiler_Debugger
		If ScintillaHandle = 0 : End : EndIf
	CompilerEndIf
	
	
	SelectedWord.s = GetEnvironmentVariable("PB_TOOL_Word")
	CompilerIf Not #PB_Compiler_Debugger
		If SelectedWord = "" : End : EndIf
	CompilerEndIf
	
	ScintillaText.s = GetScintillaText()
	CompilerIf Not #PB_Compiler_Debugger
		If ScintillaText = "" : End : EndIf
	CompilerEndIf
	
	; >> These are test pattern for development
	CompilerIf #PB_Compiler_Debugger
		
		If SelectedWord = ""
			;     SelectedWord = "Line"    ; try one of these
			;     SelectedWord = "#Line"   ; -"-  #Line could be in a comment also
			SelectedWord = "ScintillaText"   ; -"-
		EndIf
		
		If ScintillaText = ""
			#File = 0
			If ReadFile(#File, #PB_Compiler_File)
				ScintillaText = ReadString(#File, #PB_UTF8 | #PB_File_IgnoreEOL)
				CloseFile(#File)
			EndIf
			; 			RunProgram("explorer.exe", "/Select," + #PB_Compiler_File, "")
			
			; 			ScintillaText = "" + #CRLF$ +
			; 			                "#Line = #LF ;  #Line could be in a comment also " + #CRLF$ +
			; 			                "Procedure Test(*Line)  ; pointer *Line " + #CRLF$ +
			; 			                "" + #CRLF$ +
			; 			                "If SelectedWord = LCase(Tokens(TokenCounter))" + #CRLF$ +
			; 			                "	 AddElement(FoundReference())" + #CRLF$ +
			; 			                "	 FoundReference()\LineNo = LineCounter + 1" + #CRLF$ +
			; 			                "	 Line = Trim(Line)" + #CRLF$ +
			; 			                "	 Line = Mid(Line, 1, Len(Line)-2)" + #CRLF$ +
			; 			                "	 FoundReference()\Reference = Line" + #CRLF$ +
			; 			                "EndIf" + #CRLF$ +
			; 			                "" ; End of Text
		EndIf
	CompilerEndIf
	ProcedureReturn 0  ; default (ZERO is returned by default, even if there is no ProcedureReturn)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

; ChrisR
Procedure CopyClipboardReference()
	Protected text$
	PushListPosition(FoundReference()) 
	ForEach FoundReference()
		If text$ : text$ + #CRLF$ : EndIf
		If Right(FoundReference()\Reference, 2) = #CRLF$
			text$ + Left(FoundReference()\Reference, Len(FoundReference()\Reference) - 2)
		Else
			text$ + FoundReference()\Reference
		EndIf
	Next
	PopListPosition(FoundReference())
	If text$
		text$ = ReplaceString(text$, #SelectedWordMarker$, "")
		SetClipboardText(text$)
		Protected Title$ = GetWindowTitle(#frmMain)
		SetWindowTitle(#frmMain, Title$ + " (Reference copied To the clipboard)")
		Delay(500)
		SetWindowTitle(#frmMain, Title$)
	EndIf
EndProcedure

; AZJIO
Procedure GoRegExp()
	; LINK : https://www.purebasic.fr/english/viewtopic.php?p=595832#p595832
	Protected rex, LSize, Pos = 0, i, tmp$
	Protected Dim Tokens.s(0)
	
	ClearGadgetItems(#frmMain_References)
	ClearList(FoundReference())
	SetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE,GetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE) | #LVS_NOCOLUMNHEADER)
	tmp$ = GetGadgetText(#cmbRex)
	If Not Asc(tmp$)
		ProcedureReturn
	EndIf
	rex = CreateRegularExpression(#PB_Any, tmp$)
	; 	CountTokens = ExtractRegularExpression(rex, ScintillaText, Tokens()) ; tokenize the line
	
	; 	Debug ArraySize(Lines())
	
	If rex
		If ExamineRegularExpression(rex, ScintillaText)
			While NextRegularExpressionMatch(rex)
				If Not FindString(RegularExpressionMatchString(rex), #LF$)
					AddElement(FoundReference())
					FoundReference()\Selregexp = RegularExpressionMatchString(rex)
					FoundReference()\LineNo = RegularExpressionMatchPosition(rex)
					; 					Debug  FoundReference()\LineNo
				EndIf
			Wend
		EndIf
	Else
		MessageRequester("Regular expression error", RegularExpressionError())
		ProcedureReturn
	EndIf
	
	LSize = ListSize(FoundReference())
	If LSize > 0
		; 		If LSize > 5000 And MessageRequester("Continue?", "Found" + Str(LSize) + " rows, Continue?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_No
		; 			ProcedureReturn
		; 		EndIf
		
		Pos = 0
		i = 0
		SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 0, 0)
		ForEach FoundReference()
			While Pos < FoundReference()\LineNo
				Pos = FindString(ScintillaText, #LF$, Pos + 1)
				If Pos
					i + 1
				Else
					Break
				EndIf
				; 				Debug Str(FoundReference()\LineNo) + " "  + Str(Pos)
			Wend
			If i < 1 Or i > ArraySize(Lines())
				Continue
			EndIf
			FoundReference()\LineNo = i
			FoundReference()\Reference = Lines(i - 1)
			
			
			FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)
			
			; >> first attempt to mark the selected word in the string
			FoundReference()\Reference = ReplaceString(FoundReference()\Reference, FoundReference()\Selregexp, #SelectedWordMarker$ + FoundReference()\Selregexp + #SelectedWordMarker$, #PB_String_NoCase)
			
			AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference)
		Next
		; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
    ; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
		
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 1, 0)
		
		SelectedWord = "regexp"
		SetWindowTitle(#frmMain, "Display all: '" + SelectedWord + "', Found " + " in " + Str(ListSize(FoundReference())) + " Lines")
	EndIf
EndProcedure


Procedure LookForWordUnderCursor()
  ; LINK : http://www.purebasic.fr/english/viewtopic.php?f=12&t=37823
  Protected RegexLines, PbRegexTokens, StartTime.q
  Protected CountLines, LineCounter, CountTokens, TokenCounter
  Protected Line.s, selWord.s, stx.s
  Protected Dim Tokens.s(0)
  
  RegexLines = CreateRegularExpression(#PB_Any , ".*\r\n")
  PbRegexTokens = CreateRegularExpression(#PB_Any, #DOUBLEQUOTE$ + "[^" + #DOUBLEQUOTE$ + "]*" + #DOUBLEQUOTE$ + "|[\*]?[a-zA-Z_]+[\w]*[\x24]?|#[a-zA-Z_]+[\w]*[\x24]?|[\[\]\(\)\{\}]|[-+]?[0-9]*\.?[0-9]+|;.*|\.|\+|-|[&@!\\\/\*,\|]|::|:|\|<>|>>|<<|=>{1}|>={1}|<={1}|=<{1}|={1}|<{1}|>{1}|\x24+[0-9a-fA-F]+|\%[0-1]*|%|'")
  
  ;CountLines = CountString(ScintillaText, #CRLF$)
  CountLines = ExtractRegularExpression(RegexLines, ScintillaText, Lines())
  
  selWord = LCase(SelectedWord)  ; keep the original writing
  CountSelectedWords = 0         ; init for new search
  
  For LineCounter = 0 To CountLines - 1
    Line = Lines(LineCounter)
    
    ;Debug "tokenize Line '" + Line + "'"
    
    CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens()) ; tokenize the line
    
    For TokenCounter = 0 To CountTokens - 1
      ;Debug "  check Token '" + Tokens(TokenCounter) + "'"
      
      If selWord = LCase(Tokens(TokenCounter))
        AddElement(FoundReference())
        FoundReference()\LineNo = LineCounter + 1
        
        Line = Trim(Line)
        Line = Mid(Line, 1, Len(Line)-2)  ; remove the #CRLF$
        
        CountSelectedWords + CountString(LCase(Line), selWord)   ; <-- count SelectedWord in the codeline
        
        FoundReference()\Reference = Line
        Break  ; only one line (evenn if there are more than one SelectedWord )
      EndIf
    Next TokenCounter
  Next LineCounter
  
  ; because of #Constant or *Pointer
  If ListSize(FoundReference()) = 0
    For LineCounter = 0 To CountLines - 1
      Line = Lines(LineCounter)
      CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens())
      For TokenCounter = 0 To CountTokens - 1
        stx = LCase(Tokens(TokenCounter))
        If stx = "#"+selWord Or stx = "*"+selWord
          AddElement(FoundReference())
          FoundReference()\LineNo = LineCounter + 1
          
          Line = Trim(Line)
          Line = Mid(Line, 1, Len(Line)-2)
          
          CountSelectedWords + CountString(LCase(Line), stx)  ; <-- count SelectedWord in the codeline
          
          FoundReference()\Reference = Line
          Break
        EndIf
      Next
    Next
    
    CompilerIf Not #PB_Compiler_Debugger
      If ListSize(FoundReference()) = 0 : End : EndIf
    CompilerEndIf
  EndIf
EndProcedure



; ---------------------------------------------------------------------------------------------------------------------
; XIncludeFile "ColorListIconGadget.pbi"  ; I prefer .pbi (instead of .pb)
; ---------------------------------------------------------------------------------------------------------------------

;... Create brushes for painting item background
Structure MYBRUSHES
	brushDefault.l
	brushSelected.l
EndStructure

; Global brush.MYBRUSHES
Global Dim Colors(1)

; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_INFOBK))
; brush\brushDefault = GetStockObject_(#WHITE_BRUSH)
; brush\brushSelected = $aaaaaa
; brush\brushDefault = $3f3f3f


; ---== Color for Default Text and Selected Word ==--------------------------------------------------------------------


; Colors(0) = #Red                                ; the SelectedWord
Colors(0) = $8080FF                           ; the SelectedWord
											  ; Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
											  ; Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT); the default text
Colors(1) = $cccccc							  ; the default text

; ---------------------------------------------------------------------------------------------------------------------

Procedure GetCharWidth(gad, c$)
	ProcedureReturn SendMessage_(gad, #LVM_GETSTRINGWIDTH, 0, @c$)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

;Here we add some text to the underlying cell text to store the color info.
Procedure SetColor(gad, row, column, startp, endp, color)
	Protected text$
	If column
		text$ = GetGadgetItemText(gad, row, column)
		;Now add the new text.
		text$+#coloredChars_Delimeter+Str(startp)+"\"+Str(endp)+"\"+Str(color)
		SetGadgetItemText(gad,row,text$,column)
	EndIf
EndProcedure


; ---== MainWindow Procedures ==---------------------------------------------------------------------------------------

Procedure frmMain_SizeWindow_Event()
	xywh\w = WindowWidth(#frmMain)
	xywh\h = WindowHeight(#frmMain)
	ResizeGadget(#cmbRex, #PB_Ignore, #PB_Ignore, xywh\w - 34, 24)
	ResizeGadget(#btnRex, xywh\w - 28, #PB_Ignore, #PB_Ignore, #PB_Ignore)
	ResizeGadget(#frmMain_References, #PB_Ignore, #PB_Ignore, xywh\w - 6, xywh\h - 33)
; 	SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
EndProcedure

Procedure ReferencesSelectLine(SelectedLine)
  SendMessage_(ScintillaHandle, #SCI_GOTOLINE, SelectedLine - 1, 0)
  SendMessage_(ScintillaHandle, #SCI_ENSUREVISIBLE, SelectedLine - 1, 0)
  SetForegroundWindow_(PbIdeHandle)
  SetActiveWindow_(PbIdeHandle)
EndProcedure
  
Procedure frmMain_References_Event()
  Protected SelectedLine
  SelectedLine = Val(GetGadgetItemText(#frmMain_References, GetGadgetState(#frmMain_References), 0))
  If SelectedLine > 0
    ReferencesSelectLine(SelectedLine)
  EndIf
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

Procedure myWindowCallback(hwnd, msg, wParam, lParam)
	Protected Result, *nmhdr.NMHDR, *lvCD.NMLVCUSTOMDRAW, subItemRect.RECT, *DrawItem.DRAWITEMSTRUCT, Buffer.s
	Protected thisRow, thisCol, idx
	Protected t$, text$
	; ; ; ; ; ; ; 	Protected subItemText$, pos, color, c$, nextColor, thisColor
	
	Result = #PB_ProcessPureBasicEvents
	;;Dim LVColor(0)
	
	Select msg
		Case #WM_NCDESTROY
			DeleteObject_(HightLightBrush)
			DeleteObject_(BackColorBrush)
			
		Case #WM_NOTIFY
			*nmhdr.NMHDR = lParam
			*lvCD.NMLVCUSTOMDRAW = lParam
			If *lvCD\nmcd\hdr\hwndFrom = GadgetID(#frmMain_References) And *lvCD\nmcd\hdr\code = #NM_CUSTOMDRAW
				Select *lvCD\nmcd\dwDrawStage
					Case #CDDS_PREPAINT
						Result = #CDRF_NOTIFYITEMDRAW
					Case #CDDS_ITEMPREPAINT
						Result = #CDRF_NOTIFYSUBITEMDRAW;
					Case #CDDS_ITEMPREPAINT | #CDDS_SUBITEM
						thisRow = *lvCD\nmcd\dwItemSpec
						thisCol = *lvCD\iSubItem
						If thisCol
							;... Define rect for text
							subItemRect.RECT\left = #LVIR_LABEL
							subItemRect.RECT\top = *lvCD\iSubItem
							;... Get the subitem rect
							SendMessage_(GadgetID(#frmMain_References), #LVM_GETSUBITEMRECT, thisRow, @subItemRect)
							
							text$ = GetGadgetItemText(#frmMain_References, thisRow, thisCol)
							
							; 							If GetGadgetState(#frmMain_References) = thisRow
							; 								;... If item is selected
							; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushSelected)
							; 								;               Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
							; 							Else
							; 								;... If item is not selected
							; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushDefault)
							; 								;               Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT)  ; the default text
							; 							EndIf
							InflateRect_(subItemRect, -8, 0)
							
							For idx = 1 To CountString(text$, #SelectedWordMarker$) + 1
								t$ = StringField(text$, idx, #SelectedWordMarker$)
								If t$
									SetTextColor_(*lvCD\nmcd\hdc, colors(idx & 1))
									SetBkColor_(*lvCD\nmcd\hdc, BackColor)
									DrawText_(*lvCD\nmcd\hdc, t$, -1, subItemRect, #DT_END_ELLIPSIS|#DT_VCENTER|#DT_SINGLELINE)
									subItemRect\left + GetCharWidth(*nmhdr\hwndFrom, t$)
								EndIf
							Next idx
							Result = #CDRF_SKIPDEFAULT
						Else
							Result = #CDRF_DODEFAULT
						EndIf
				EndSelect
			EndIf
			
		Case #WM_CTLCOLOREDIT
			Buffer = Space(64)
			If GetClassName_(GetParent_(lParam), @Buffer, 64)
				If Buffer = "ComboBox"        
					SetTextColor_(wParam, #White)
					SetBkMode_(wParam, #TRANSPARENT)
					ProcedureReturn BackColorBrush
				EndIf
			EndIf	
			
		Case #WM_DRAWITEM
			*DrawItem.DRAWITEMSTRUCT = lParam
			If *DrawItem\CtlType = #ODT_COMBOBOX
				If IsGadget(wParam)
					If *DrawItem\itemID <> -1
						If *DrawItem\itemstate & #ODS_SELECTED
							FillRect_(*DrawItem\hDC, *DrawItem\rcitem, HightLightBrush)
						Else
							FillRect_(*DrawItem\hDC, *DrawItem\rcitem, BackColorBrush)
						EndIf
						SetBkMode_(*DrawItem\hDC, #TRANSPARENT)
						SetTextColor_(*DrawItem\hDC, $cccccc)
						Text$ = GetGadgetItemText(*DrawItem\CtlID, *DrawItem\itemID)
						*DrawItem\rcItem\left + DesktopScaledX(4)
						DrawText_(*DrawItem\hDC, Text$, Len(Text$), *DrawItem\rcItem, #DT_LEFT | #DT_SINGLELINE | #DT_VCENTER)
					EndIf
				EndIf
			EndIf
	EndSelect
	ProcedureReturn Result
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------
Procedure SetWindowTheme()
	Protected Theme.s, cmbRexID, ChildGadget, Buffer.s
	If OSVersion() >= #PB_OS_Windows_10
		Theme = "DarkMode_Explorer"
	Else
		Theme = "Explorer"
	EndIf
	
	SetWindowTheme_(GadgetID(#frmMain_References), @Theme, 0)
	
	cmbRexID = GadgetID(#cmbRex)
	Buffer = Space(64)
	If GetClassName_(cmbRexID, @Buffer, 64)
		If Buffer = "ComboBox"
			If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
				SetWindowTheme_(cmbRexID, "DarkMode_CFD", "Combobox")
			Else
				SetWindowTheme_(cmbRexID, @Theme, 0)
			EndIf
		EndIf
	EndIf
	ChildGadget = GetWindow_(cmbRexID, #GW_CHILD)
	If ChildGadget
		Buffer = Space(64)
		If GetClassName_(ChildGadget, @Buffer, 64)
			If Buffer = "ComboBox"
				If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
					SetWindowTheme_(ChildGadget, "DarkMode_CFD", "Combobox")
				Else
					SetWindowTheme_(ChildGadget, @Theme, 0)
				EndIf
			EndIf
		EndIf
	EndIf
EndProcedure

Procedure SetGadgetBorderless(Gadget)
	Protected hGad = GadgetID(Gadget)
	SetWindowLongPtr_(hGad, #GWL_EXSTYLE, GetWindowLongPtr_(hGad, #GWL_EXSTYLE) & (~#WS_EX_CLIENTEDGE))
	ProcedureReturn SetWindowPos_(hGad, 0, 0, 0, 0, 0, #SWP_SHOWWINDOW | #SWP_NOZORDER | #SWP_NOSIZE | #SWP_NOMOVE | #SWP_FRAMECHANGED)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

Procedure.s FindProcedureName(CursorLine, ProcedureLine)
  Protected LineCounter,  Line.s
  For LineCounter = CursorLine - 1 To 1 Step - 1   
    Line = LTrimChar(StringField(ScintillaText, LineCounter, #CRLF$))
    If Left(LCase(Line), Len("endprocedure")) = "endprocedure"
      Break
    EndIf
    If Left(LCase(Line), Len("procedure.")) = "procedure." Or Left(LCase(Line), Len("procedure ")) = "procedure "
        ProcedureLine = LineCounter
        ProcedureReturn    Line
    EndIf 
  Next
  ProcedureLine = -1
  ProcedureReturn ""
EndProcedure

Procedure ReferencesHeaderClick(hWnd, Message, wParam, lParam)
  Protected *Header.HD_NOTIFY, SelectedLine
  Protected Result=CallWindowProc_(frmMain_References, hWnd, Message, wParam, lParam)
  ;Protected ColumnClicked
  
  Select Message
    Case #WM_NOTIFY
      *Header=lParam
      If *Header\hdr\code=#HDN_ITEMCLICK
        ;ColumnClicked=*Header\iItem
        SelectedLine = Val(GetGadgetItemText(#frmMain_References, -1, 0))
        If SelectedLine > 0
          ReferencesSelectLine(SelectedLine)
        EndIf
      EndIf
  EndSelect 
  ProcedureReturn Result
EndProcedure

Procedure main()
	Protected WWE ;, idx, pos, le
	Protected YouAreHere.s, ProcedureLine, Ls, l, LastProcedureName.s
	Protected CursorLine = Int(Val(StringField(GetEnvironmentVariable("PB_TOOL_Cursor"), 1, "x")))
	
	Initialization()  ;
	LookForWordUnderCursor()
	If ScintillaText <> ""  
	  YouAreHere = FindProcedureName(CursorLine, @ProcedureLine)
	  ;YouAreHere = FindProcedureName(468, @ProcedureLine)
; 	  If YouAreHere <> ""
; 	    YouAreHere = "You are here:" + YouAreHere
; 	  EndIf
	EndIf
	
	;--> ini
	If OpenPreferences(ini$) ; открываем ini
		If PreferenceGroup("set")
			xywh\x = ReadPreferenceInteger("x", xywh\x)
			xywh\y = ReadPreferenceInteger("y", xywh\y)
			xywh\w = ReadPreferenceInteger("w", xywh\w)
			xywh\h = ReadPreferenceInteger("h", xywh\h)
		EndIf
		ClosePreferences()
	EndIf
	If xywh\x = 0 And xywh\y = 0
		centered = #PB_Window_ScreenCentered
	EndIf
	CopyStructure(@xywh, @xywh2, xywhm)
	
	
	;- GUI
	If OpenWindow(#frmMain, xywh\x, xywh\y, xywh\w, xywh\h,
	              "Display all: '" + SelectedWord + "', Found " + CountSelectedWords + " in " + Str(ListSize(FoundReference())) + " Lines ",
	              #PB_Window_SystemMenu | #PB_Window_SizeGadget | centered)
		SetWindowColor(#frmMain, $3f3f3f) 
		StickyWindow(#frmMain, #True)
		SetWindowCallback(@myWindowCallback())
		
		ComboBoxGadget(#cmbRex, 3, 3, WindowWidth(#frmMain) - 34, 24, #PB_ComboBox_Editable | #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED)
		;ComboBoxGadget(#cmbRex, 3, 3, WindowWidth(#frmMain) - 60, 24, #PB_ComboBox_Editable)
		AddGadgetItem(#cmbRex, -1, "(?# Debug, All )\bDebug\b")
		AddGadgetItem(#cmbRex, -1, "(?# Debug, real )(?m)^\h*Debug\b")
		AddGadgetItem(#cmbRex, -1, "(?# WinAPI )(?mi)[a-z][\da-z]*_(?=\h*\()")
		AddGadgetItem(#cmbRex, -1, "(?# Link )https?://[\w.:]+/?(?:[\w/?&=.~;\+!*_#%-]+)")
		AddGadgetItem(#cmbRex, -1, "(?# Procedure )(?mi)^\h*(?:Procedure[CDL$]{0,5}?(?:\h*\.[abcdfilqsuw])?\h+\K)[A-Za-z_]\w*\h*(?=\()")
		AddGadgetItem(#cmbRex, -1, "(?# Macro )(?mi)^\h*Macro\h+\K[A-Za-z_]\w*\h*(?=\()")
		AddGadgetItem(#cmbRex, -1, "(?# Var$ )(?<![#@\w])\w+\$")
		AddGadgetItem(#cmbRex, -1, "(?# Hex num )(?i)\$[\da-f]+")
		AddGadgetItem(#cmbRex, -1, "(?# Comments )(?m)^\h*;.*?(?=\r?$)")
		#q$ = Chr(34)
		AddGadgetItem(#cmbRex, -1, "(?# Comments, All )(?m)^(?:[^" + #q$ + ";]*" + #q$ + "[^" + #q$ + "]*?" + #q$ + ")*[^" + #q$ + ";]*(;.*?)(?=\r?$)")
		AddGadgetItem(#cmbRex, -1, "(?# Structures, Declare )(?i)(?<![=\w" + #q$ + "\\./-])[a-z]\w*\.[a-z]\w+(?![\w" + #q$ + "\\./-])")
		AddGadgetItem(#cmbRex, -1, "(?# Structures, item )(?<![\w.:" + #q$ + "\\])\*?\w+(?:(?:\(\))?\\[\d_a-zA-Z]+)+(?![\w" + #q$ + "\\])")
		AddGadgetItem(#cmbRex, -1, "(?# Structures, Content )(?m)^\h*Structure\h*\K\w+")
		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Comments )(?m)^(?:[^\";]*\"[^\"]*?\")*[^\";]*(;.*?)(?=\r?$)")
		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, Declare )(?i)(?<![=\\w\"\\./-])[a-z]\\w*\\.[a-z]\\w+(?![\\w\"\\\\./-])")
		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, item )(?<![\\w.:\"\\\\])\\*?\\w+(?:(?:\\(\\))?\\\\[\\d_a-zA-Z]+)+(?![\\w\"\\\\])")
		AddGadgetItem(#cmbRex, -1, "(?# Types )\b\w+\.[sfdqbliwcapu]\b")
		AddGadgetItem(#cmbRex, -1, "(?# Constants, Declare )(?m)^\h*\K#\w+(?=\h*(?:=|\r?$))")
		AddGadgetItem(#cmbRex, -1, "(?# Constants, All )#\w+\b")
		
		ButtonGadget(#btnRex, WindowWidth(#frmMain) - 28, 3, 24, 24, ">")
		; ButtonGadget(#btnClose, WindowWidth(#frmMain) - 27, 3, 24, 24, "x") ; to make a black theme
		
		ListIconGadget(#frmMain_References, 3, GadgetY(#cmbRex)  + GadgetHeight(#cmbRex) + 3, WindowWidth(#frmMain) - 6, WindowHeight(#frmMain) - GadgetHeight(#cmbRex) - GadgetY(#cmbRex) - 6, "", 96, #PB_ListIcon_FullRowSelect|#PB_ListIcon_GridLines|#PB_ListIcon_AlwaysShowSelection)
		frmMain_References = SetWindowLongPtr_(GadgetID(#frmMain_References), #GWL_WNDPROC, @ReferencesHeaderClick())
		;SetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE,GetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE) | #LVS_NOCOLUMNHEADER)
		AddGadgetColumn(#frmMain_References, 1, "", 300)
		AddGadgetColumn(#frmMain_References, 2, "", 100)
		SetGadgetColor(#frmMain_References, #PB_Gadget_BackColor, $3f3f3f)
		SetGadgetColor(#frmMain_References, #PB_Gadget_FrontColor, $cccccc)
		SetGadgetBorderless(#frmMain_References)  ; by me
		
		; Optional DarkMode_Explorer theme if OSVersion >= Windows_10 Else Explorer Theme
		SetWindowTheme()
		
		
		SetGadgetItemText(#frmMain_References, -1, Str(CursorLine) , 0)
    SetGadgetItemText(#frmMain_References, -1, SelectedWord , 1)
    SetGadgetItemText(#frmMain_References, -1, YouAreHere , 2)
    
    ProcedureLine = -1
    Ls = ListSize(FoundReference())  - 1
    For l = Ls To 0 Step -1
      SelectElement(FoundReference(), l)
       If FoundReference()\LineNo <> CursorLine  
        FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)
        ; >> first attempt to mark the selected word in the string
        FoundReference()\Reference = ReplaceString(FoundReference()\Reference, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$, #PB_String_NoCase)
        If Left(LCase(FoundReference()\Reference), Len("procedure.")) <> "procedure." Or Left(LCase(FoundReference()\Reference), Len("procedure ")) <> "procedure "
          If ProcedureLine = -1 Or FoundReference()\LineNo<ProcedureLine
            LastProcedureName = FindProcedureName(FoundReference()\LineNo, @ProcedureLine)
          EndIf 
          FoundReference()\InProcedure = LastProcedureName 
        EndIf
      EndIf  
    Next l
    
    ForEach FoundReference()
      If FoundReference()\LineNo <> CursorLine  
        AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference + #LF$ + FoundReference()\InProcedure)
      EndIf  
    Next
		
		; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
		; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 2, #LVSCW_AUTOSIZE)
		
		SetGadgetItemAttribute(#frmMain_References, 0, #PB_ListIcon_ColumnWidth, GetGadgetItemAttribute(#frmMain_References, 0, #PB_ListIcon_ColumnWidth, 0) + 5 ,0 ) 
		
		AddKeyboardShortcut(#frmMain, #PB_Shortcut_Escape, #frmMain_Shortcut_Escape_Event)
		BindEvent(#PB_Event_SizeWindow, @frmMain_SizeWindow_Event(), #frmMain)
		BindGadgetEvent(#frmMain_References, @frmMain_References_Event())
		SetActiveGadget(#frmMain_References)
		
		AddKeyboardShortcut(#frmMain, #PB_Shortcut_Control | #PB_Shortcut_C, #Shortcut_Ctrl_C)
		
		
		Repeat
			Select WaitWindowEvent()
				Case #PB_Event_MoveWindow
					xywh\x = WindowX(#frmMain) 
					xywh\y = WindowY(#frmMain) 
				Case #PB_Event_CloseWindow
; 					Если размеры окна изменились, то сохраняем.
					If Not CompareMemory(@xywh, @xywh2, SizeOf(xywhm))
						If OpenPreferences(ini$) Or CreatePreferences(ini$)
							PreferenceGroup("set")
							WritePreferenceInteger("x", xywh\x)
							WritePreferenceInteger("y", xywh\y)
							WritePreferenceInteger("w", xywh\w)
							WritePreferenceInteger("h", xywh\h)
							ClosePreferences()
						EndIf
					EndIf
					Break
				Case #PB_Event_Menu
					Select EventMenu()
						Case #Shortcut_Ctrl_C
							CopyClipboardReference()
						Case #frmMain_Shortcut_Escape_Event
							Break
					EndSelect
				Case #PB_Event_Gadget
					Select EventGadget()
						Case #btnRex, #cmbRex
							GoRegExp()
					EndSelect
			EndSelect
		ForEver
		
		; 		DeleteObject_(brush\brushSelected)  ; objects created by CreateSolidBrush_() needs this!
	EndIf
	ProcedureReturn 0  ; not necessary, but looks good/better
EndProcedure

End main()

;----== Bottom of File ==----------------------------------------------------------------------------------------------
Dadlick
New User
New User
Posts: 6
Joined: Thu Feb 16, 2023 3:28 am

Re: Show all occurrences of a word in the IDE

Post by Dadlick »

Implemented the launch of only one copy of the window, subsequent calls are redirected to the first instance.
Divided regular expressions into name and pattern. Only the name is displayed in the combobox.

AZJIO, do you implement scrolling of the selected line to the middle of the window?

Code: Select all

; ----------------------------------------------------------------------------
; File : FindAllReferences[Win].pb
; ----------------------------------------------------------------------------
;
;   Description: Find all references of a variable
;            OS: Windows
; English-Forum:
;  French-Forum:
;  German-Forum: http://www.purebasic.fr/german/viewtopic.php?f=8&t=28292
; ----------------------------------------------------------------------------

; MIT License
;
; Copyright (c) 2015 Kiffi
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.
;
; ----------------------------------------------------------------------------
; Change Log :
;   2023-02-12 : New Additions from Mesa, AZJIO, Axolotl
;
; English-Forum: https://www.purebasic.fr/english/viewtopic.php?t=80739
;
; Change Details by Axolotl
;   - Integrated ColorListIconGadget.pb from AZJIO and modified
;   - Add a #PB_Compiler_Debugger mode (for development)
;   - New Structure of the entire application
;   - harmonize the different coding styles (still something to do)
;   - Changing the coloring of the selected words
;   - counting all appearances of the SelectedWord (quick and dirty)
;
; ----------------------------------------------------------------------------

; FindAllReferences
EnableExplicit

Structure RegExp
  Name.s
  RegExp.s
EndStructure

Global NewList RegExpList.RegExp()

Procedure AddRegExp(Name.s, RegExp.s)
  AddElement(RegExpList())
  RegExpList()\Name = Name
  RegExpList()\RegExp = RegExp
EndProcedure  
#OneInstance = #True  ;running only one instance of the application
#q$ = Chr(34)

AddRegExp("Debug, All", "\bDebug\b")
AddRegExp("Debug, real", "(?m)^\h*Debug\b")
AddRegExp("WinAPI", "(?mi)[a-z][\da-z]*_(?=\h*\()")
AddRegExp("Link", "https?://[\w.:]+/?(?:[\w/?&=.~;\+!*_#%-]+)")
AddRegExp("Procedure", "(?mi)^\h*(?:Procedure[CDL$]{0,5}?(?:\h*\.[abcdfilqsuw])?\h+\K)[A-Za-z_]\w*\h*(?=\()")
AddRegExp("Macro", "(?mi)^\h*Macro\h+\K[A-Za-z_]\w*\h*(?=\()")
AddRegExp("Var$", "(?<![#@\w])\w+\$")
AddRegExp("Hex num", "(?i)\$[\da-f]+")
AddRegExp("Comments", "(?m)^\h*;.*?(?=\r?$)")
AddRegExp("Comments, All", "(?m)^(?:[^" + #q$ + ";]*" + #q$ + "[^" + #q$ + "]*?" + #q$ + ")*[^" + #q$ + ";]*(;.*?)(?=\r?$)")
AddRegExp("Structures, Declare", "(?i)(?<![=\w" + #q$ + "\\./-])[a-z]\w*\.[a-z]\w+(?![\w" + #q$ + "\\./-])")
AddRegExp("Structures, item", "(?<![\w.:" + #q$ + "\\])\*?\w+(?:(?:\(\))?\\[\d_a-zA-Z]+)+(?![\w" + #q$ + "\\])")
AddRegExp("Structures, Content", "(?m)^\h*Structure\h*\K\w+")
AddRegExp("Types", "\b\w+\.[sfdqbliwcapu]\b")
AddRegExp("Constants, Declare", "(?m)^\h*\K#\w+(?=\h*(?:=|\r?$))")
AddRegExp("Constants, All","#\w+\b")

CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
	CompilerError "Windows Only!"
CompilerEndIf

CompilerIf  #OneInstance
CompilerIf #PB_Compiler_Thread = 0 
  CompilerError "Thread-safe compile"
CompilerEndIf

  ;https://www.purebasic.fr/german/viewtopic.php?p=198712#p198712
DeclareModule FileMap
  Declare Create(Name.s, Size.i, Security = #False)
  Declare Open(Name.s)
  Declare Close(*Mem)
EndDeclareModule

Module FileMap
  Global NewMap hMap.i()
  Global SA.SECURITY_ATTRIBUTES, pSD.SECURITY_DESCRIPTOR, IsInitSecurity
 
  Procedure Create(Name.s, Size.i, Security = #False)
    Protected handle, *mem
    If Security
      If Not IsInitSecurity
        If Not InitializeSecurityDescriptor_(@pSD, #SECURITY_DESCRIPTOR_REVISION)
          ProcedureReturn 0
        EndIf
        If Not SetSecurityDescriptorDacl_(@pSD, #True, #Null, #False)
          ProcedureReturn 0
        EndIf
        SA\nLength = SizeOf(SA)
        SA\lpSecurityDescriptor = @pSD
        SA\bInheritHandle = #True
        IsInitSecurity = #True
      EndIf
      handle = CreateFileMapping_(#INVALID_HANDLE_VALUE, @SA, #PAGE_READWRITE | #SEC_COMMIT | #SEC_NOCACHE, 0, Size, Name)
    Else
      handle = CreateFileMapping_(#INVALID_HANDLE_VALUE, 0, #PAGE_READWRITE | #SEC_COMMIT | #SEC_NOCACHE, 0, Size, Name)
    EndIf 
    If handle
      *mem = MapViewOfFile_(handle, #FILE_MAP_ALL_ACCESS, 0, 0, 0)
      hMap(Str(*mem)) = handle
      ProcedureReturn *mem
    EndIf   
  EndProcedure
 
  Procedure Open(Name.s) 
    Protected handle, *mem
    handle = OpenFileMapping_(#FILE_MAP_ALL_ACCESS, 0, Name)
    If handle
      *mem = MapViewOfFile_(handle, #FILE_MAP_ALL_ACCESS, 0, 0, 0)
      hMap(Str(*mem)) = handle
      ProcedureReturn *mem
    EndIf
  EndProcedure
  
  Procedure Close(*Mem)
    Protected result 
    UnmapViewOfFile_(*Mem)
    result = CloseHandle_(hMap(Str(*Mem)))
    DeleteMapElement(hMap(), Str(*Mem))
    ProcedureReturn result
  EndProcedure
EndModule

Structure myMem
  ReInit.l
  CursorLine.i
  Word.s{100}
EndStructure

Global *mem.myMem, RefThread
Declare GetWordReferene(CursorLine, Word.s)
Procedure Thread(*xx) ; Процедура работает в отдельном потоке и в ней выполняется приём данных 
  Repeat
    If *mem
      If *mem\ReInit = #True
        *mem\ReInit = #False
        GetWordReferene(*mem\CursorLine, *mem\Word)
      EndIf
    EndIf
  ForEver
EndProcedure
CompilerEndIf


;- Structure

Structure sFoundReference
	LineNo.i
	Reference.s
	Selregexp.s
	InProcedure.s
EndStructure

Structure xywhm
	x.i
	y.i
	w.i
	h.i
	m.i
EndStructure

;- Enumeration
Enumeration ; Windows
	#frmMain
EndEnumeration

Enumeration ; Gadgets
	#cmbRex
	#btnRex
	; 	#btnClose
	#frmMain_References
EndEnumeration

Enumeration ; Menu-/Toolbaritems
	#frmMain_Shortcut_Escape_Event
	#Shortcut_Ctrl_C
EndEnumeration

;- Constants
CompilerIf #PB_Compiler_Debugger
	#SelectedWordMarker$ = "|"
CompilerElse
	#SelectedWordMarker$ = Chr(1)  ; not used in source codes
CompilerEndIf
; "	 Line = Trim(Line)"
; "	 |Line| = Trim(|Line|)"
; --> ReplaceString(text$, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$)

;/-----------------------------------------------------------------------------
;| RGB() as HEX() -->     BB GG RR   .. i.e. RGB (1, 2, 3)    -->    03 02 01
;| RGB() as HEX() -->  AA BB GG RR   .. i.e. RGBA(1, 2, 3, 4) --> 04 03 02 01
;\

#coloredChars_Delimeter = "{***\"

;- Global
Global ini$ = LSet(ProgramFilename(), Len(ProgramFilename()) - 3) + "ini"
Global centered
Global xywh.xywhm
Global xywh2.xywhm
Global xywh\w = 600
Global xywh\h = 300

Global PbIdeHandle, ScintillaHandle
Global frmMain_References

Global SelectedWord.s, ScintillaText.s
Global CountSelectedWords    ; new, because we want to know all references (not only the lines)
Global NewList FoundReference.sFoundReference()
Global Dim Lines.s(0)

Global BackColor = $3f3f3f
Global HightLightBrush = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
; Global HightLightBrush = CreateSolidBrush_($423926)
Global BackColorBrush = CreateSolidBrush_(BackColor)

; ; ; Global PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
; ; ; If PbIdeHandle = 0 : End : EndIf
; ; ;
; ; ; Global ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
; ; ; If ScintillaHandle = 0 : End : EndIf

; ---== Procedures ==--------------------------------------------------------------------------------------------------

; AZJIO
Procedure.s LTrimChar(String$, TrimChar$ = #CRLF$ + #TAB$ + #FF$ + #VT$ + " ")
	Protected *memChar, *c.Character, *jc.Character
	
	If Not Asc(String$)
		ProcedureReturn ""
	EndIf
	
	*c.Character = @String$
	*memChar = @TrimChar$
	
	While *c\c
		*jc.Character = *memChar
		
		While *jc\c
			If *c\c = *jc\c
				*c\c = 0
				Break
			EndIf
			*jc + SizeOf(Character)
		Wend
		
		If *c\c
			String$ = PeekS(*c)
			Break
		EndIf
		*c + SizeOf(Character)
	Wend
	
	ProcedureReturn String$
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

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

; ---------------------------------------------------------------------------------------------------------------------

Procedure Initialization()
  
CompilerIf  #OneInstance
  *mem = FileMap::Open("MyMemory")
  If *mem = 0
    *mem.myMem = FileMap::Create("MyMemory", SizeOf(myMem))
    If *mem
      RefThread =  CreateThread(@Thread(),0)
    EndIf
  Else
    *mem\CursorLine = Int(Val(StringField(GetEnvironmentVariable("PB_TOOL_Cursor"), 1, "x")))
    *mem\Word = GetEnvironmentVariable("PB_TOOL_Word")
    *mem\ReInit = #True
    End
  EndIf
CompilerEndIf

	
	PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
	CompilerIf Not #PB_Compiler_Debugger
		If PbIdeHandle = 0 : End : EndIf
	CompilerEndIf
	
	ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
	CompilerIf Not #PB_Compiler_Debugger
		If ScintillaHandle = 0 : End : EndIf
	CompilerEndIf
	ProcedureReturn 0  ; default (ZERO is returned by default, even if there is no ProcedureReturn)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

; ChrisR
Procedure CopyClipboardReference()
	Protected text$
	PushListPosition(FoundReference()) 
	ForEach FoundReference()
		If text$ : text$ + #CRLF$ : EndIf
		If Right(FoundReference()\Reference, 2) = #CRLF$
			text$ + Left(FoundReference()\Reference, Len(FoundReference()\Reference) - 2)
		Else
			text$ + FoundReference()\Reference
		EndIf
	Next
	PopListPosition(FoundReference())
	If text$
		text$ = ReplaceString(text$, #SelectedWordMarker$, "")
		SetClipboardText(text$)
		Protected Title$ = GetWindowTitle(#frmMain)
		SetWindowTitle(#frmMain, Title$ + " (Reference copied To the clipboard)")
		Delay(500)
		SetWindowTitle(#frmMain, Title$)
	EndIf
EndProcedure

; AZJIO
Procedure GoRegExp()
	; LINK : https://www.purebasic.fr/english/viewtopic.php?p=595832#p595832
	Protected rex, LSize, Pos = 0, i
	Protected Dim Tokens.s(0)
	
	ClearGadgetItems(#frmMain_References)
	ClearList(FoundReference())
	SetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE,GetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE) | #LVS_NOCOLUMNHEADER)
	i = GetGadgetState(#cmbRex)
	If i < 0
	  ProcedureReturn
	EndIf  
	SelectElement(RegExpList(), i)

	If Not Asc(RegExpList()\RegExp)
		ProcedureReturn
	EndIf

	rex = CreateRegularExpression(#PB_Any, RegExpList()\RegExp)
	; 	CountTokens = ExtractRegularExpression(rex, ScintillaText, Tokens()) ; tokenize the line
	
	; 	Debug ArraySize(Lines())
	
	If rex
		If ExamineRegularExpression(rex, ScintillaText)
			While NextRegularExpressionMatch(rex)
				If Not FindString(RegularExpressionMatchString(rex), #LF$)
					AddElement(FoundReference())
					FoundReference()\Selregexp = RegularExpressionMatchString(rex)
					FoundReference()\LineNo = RegularExpressionMatchPosition(rex)
					; 					Debug  FoundReference()\LineNo
				EndIf
			Wend
		EndIf
	Else
		MessageRequester("Regular expression error", RegularExpressionError())
		ProcedureReturn
	EndIf
	
	LSize = ListSize(FoundReference())
	If LSize > 0
		; 		If LSize > 5000 And MessageRequester("Continue?", "Found" + Str(LSize) + " rows, Continue?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_No
		; 			ProcedureReturn
		; 		EndIf
		
		Pos = 0
		i = 0
		SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 0, 0)
		ForEach FoundReference()
			While Pos < FoundReference()\LineNo
				Pos = FindString(ScintillaText, #LF$, Pos + 1)
				If Pos
					i + 1
				Else
					Break
				EndIf
				; 				Debug Str(FoundReference()\LineNo) + " "  + Str(Pos)
			Wend
			If i < 1 Or i > ArraySize(Lines())
				Continue
			EndIf
			FoundReference()\LineNo = i
			FoundReference()\Reference = Lines(i - 1)
			
			
			FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)
			
			; >> first attempt to mark the selected word in the string
			FoundReference()\Reference = ReplaceString(FoundReference()\Reference, FoundReference()\Selregexp, #SelectedWordMarker$ + FoundReference()\Selregexp + #SelectedWordMarker$, #PB_String_NoCase)
			
			AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference)
		Next
		; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
    ; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
		
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 1, 0)
		
		SelectedWord = "regexp"
		SetWindowTitle(#frmMain, "Display all: '" + SelectedWord + "', Found " + " in " + Str(ListSize(FoundReference())) + " Lines")
	EndIf
EndProcedure


Procedure LookForWordUnderCursor()
  ; LINK : http://www.purebasic.fr/english/viewtopic.php?f=12&t=37823
  Protected RegexLines, PbRegexTokens, StartTime.q
  Protected CountLines, LineCounter, CountTokens, TokenCounter
  Protected Line.s, selWord.s, stx.s
  Protected Dim Tokens.s(0)
  
  RegexLines = CreateRegularExpression(#PB_Any , ".*\r\n")
  PbRegexTokens = CreateRegularExpression(#PB_Any, #DOUBLEQUOTE$ + "[^" + #DOUBLEQUOTE$ + "]*" + #DOUBLEQUOTE$ + "|[\*]?[a-zA-Z_]+[\w]*[\x24]?|#[a-zA-Z_]+[\w]*[\x24]?|[\[\]\(\)\{\}]|[-+]?[0-9]*\.?[0-9]+|;.*|\.|\+|-|[&@!\\\/\*,\|]|::|:|\|<>|>>|<<|=>{1}|>={1}|<={1}|=<{1}|={1}|<{1}|>{1}|\x24+[0-9a-fA-F]+|\%[0-1]*|%|'")
  
  ;CountLines = CountString(ScintillaText, #CRLF$)
  CountLines = ExtractRegularExpression(RegexLines, ScintillaText, Lines())
  
  selWord = LCase(SelectedWord)  ; keep the original writing
  CountSelectedWords = 0         ; init for new search
  
  For LineCounter = 0 To CountLines - 1
    Line = Lines(LineCounter)
    
    ;Debug "tokenize Line '" + Line + "'"
    
    CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens()) ; tokenize the line
    
    For TokenCounter = 0 To CountTokens - 1
      ;Debug "  check Token '" + Tokens(TokenCounter) + "'"
      
      If selWord = LCase(Tokens(TokenCounter))
        AddElement(FoundReference())
        FoundReference()\LineNo = LineCounter + 1
        
        Line = Trim(Line)
        Line = Mid(Line, 1, Len(Line)-2)  ; remove the #CRLF$
        
        CountSelectedWords + CountString(LCase(Line), selWord)   ; <-- count SelectedWord in the codeline
        
        FoundReference()\Reference = Line
        Break  ; only one line (evenn if there are more than one SelectedWord )
      EndIf
    Next TokenCounter
  Next LineCounter
  
  ; because of #Constant or *Pointer
  If ListSize(FoundReference()) = 0
    For LineCounter = 0 To CountLines - 1
      Line = Lines(LineCounter)
      CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens())
      For TokenCounter = 0 To CountTokens - 1
        stx = LCase(Tokens(TokenCounter))
        If stx = "#"+selWord Or stx = "*"+selWord
          AddElement(FoundReference())
          FoundReference()\LineNo = LineCounter + 1
          
          Line = Trim(Line)
          Line = Mid(Line, 1, Len(Line)-2)
          
          CountSelectedWords + CountString(LCase(Line), stx)  ; <-- count SelectedWord in the codeline
          
          FoundReference()\Reference = Line
          Break
        EndIf
      Next
    Next
    
    CompilerIf Not #PB_Compiler_Debugger
      If ListSize(FoundReference()) = 0 : End : EndIf
    CompilerEndIf
  EndIf
EndProcedure



; ---------------------------------------------------------------------------------------------------------------------
; XIncludeFile "ColorListIconGadget.pbi"  ; I prefer .pbi (instead of .pb)
; ---------------------------------------------------------------------------------------------------------------------

;... Create brushes for painting item background
Structure MYBRUSHES
	brushDefault.l
	brushSelected.l
EndStructure

; Global brush.MYBRUSHES
Global Dim Colors(1)

; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_INFOBK))
; brush\brushDefault = GetStockObject_(#WHITE_BRUSH)
; brush\brushSelected = $aaaaaa
; brush\brushDefault = $3f3f3f


; ---== Color for Default Text and Selected Word ==--------------------------------------------------------------------


; Colors(0) = #Red                                ; the SelectedWord
Colors(0) = $8080FF                           ; the SelectedWord
											  ; Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
											  ; Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT); the default text
Colors(1) = $cccccc							  ; the default text

; ---------------------------------------------------------------------------------------------------------------------

Procedure GetCharWidth(gad, c$)
	ProcedureReturn SendMessage_(gad, #LVM_GETSTRINGWIDTH, 0, @c$)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

;Here we add some text to the underlying cell text to store the color info.
Procedure SetColor(gad, row, column, startp, endp, color)
	Protected text$
	If column
		text$ = GetGadgetItemText(gad, row, column)
		;Now add the new text.
		text$+#coloredChars_Delimeter+Str(startp)+"\"+Str(endp)+"\"+Str(color)
		SetGadgetItemText(gad,row,text$,column)
	EndIf
EndProcedure


; ---== MainWindow Procedures ==---------------------------------------------------------------------------------------

Procedure frmMain_SizeWindow_Event()
	xywh\w = WindowWidth(#frmMain)
	xywh\h = WindowHeight(#frmMain)
	ResizeGadget(#cmbRex, #PB_Ignore, #PB_Ignore, xywh\w - 34, 24)
	ResizeGadget(#btnRex, xywh\w - 28, #PB_Ignore, #PB_Ignore, #PB_Ignore)
	ResizeGadget(#frmMain_References, #PB_Ignore, #PB_Ignore, xywh\w - 6, xywh\h - 33)
; 	SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
EndProcedure

Procedure ReferencesSelectLine(SelectedLine)
  SendMessage_(ScintillaHandle, #SCI_GOTOLINE, SelectedLine - 1, 0)
  SendMessage_(ScintillaHandle, #SCI_ENSUREVISIBLE, SelectedLine - 1, 0)
  SetForegroundWindow_(PbIdeHandle)
  SetActiveWindow_(PbIdeHandle)
EndProcedure
  
Procedure frmMain_References_Event()
  Protected SelectedLine
  SelectedLine = Val(GetGadgetItemText(#frmMain_References, GetGadgetState(#frmMain_References), 0))
  If SelectedLine > 0
    ReferencesSelectLine(SelectedLine)
  EndIf
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

Procedure myWindowCallback(hwnd, msg, wParam, lParam)
	Protected Result, *nmhdr.NMHDR, *lvCD.NMLVCUSTOMDRAW, subItemRect.RECT, *DrawItem.DRAWITEMSTRUCT, Buffer.s
	Protected thisRow, thisCol, idx
	Protected t$, text$
	; ; ; ; ; ; ; 	Protected subItemText$, pos, color, c$, nextColor, thisColor
	
	Result = #PB_ProcessPureBasicEvents
	;;Dim LVColor(0)
	
	Select msg
		Case #WM_NCDESTROY
			DeleteObject_(HightLightBrush)
			DeleteObject_(BackColorBrush)
			
		Case #WM_NOTIFY
			*nmhdr.NMHDR = lParam
			*lvCD.NMLVCUSTOMDRAW = lParam
			If *lvCD\nmcd\hdr\hwndFrom = GadgetID(#frmMain_References) And *lvCD\nmcd\hdr\code = #NM_CUSTOMDRAW
				Select *lvCD\nmcd\dwDrawStage
					Case #CDDS_PREPAINT
						Result = #CDRF_NOTIFYITEMDRAW
					Case #CDDS_ITEMPREPAINT
						Result = #CDRF_NOTIFYSUBITEMDRAW;
					Case #CDDS_ITEMPREPAINT | #CDDS_SUBITEM
						thisRow = *lvCD\nmcd\dwItemSpec
						thisCol = *lvCD\iSubItem
						If thisCol
							;... Define rect for text
							subItemRect.RECT\left = #LVIR_LABEL
							subItemRect.RECT\top = *lvCD\iSubItem
							;... Get the subitem rect
							SendMessage_(GadgetID(#frmMain_References), #LVM_GETSUBITEMRECT, thisRow, @subItemRect)
							
							text$ = GetGadgetItemText(#frmMain_References, thisRow, thisCol)
							
							; 							If GetGadgetState(#frmMain_References) = thisRow
							; 								;... If item is selected
							; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushSelected)
							; 								;               Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
							; 							Else
							; 								;... If item is not selected
							; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushDefault)
							; 								;               Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT)  ; the default text
							; 							EndIf
							InflateRect_(subItemRect, -8, 0)
							
							For idx = 1 To CountString(text$, #SelectedWordMarker$) + 1
								t$ = StringField(text$, idx, #SelectedWordMarker$)
								If t$
									SetTextColor_(*lvCD\nmcd\hdc, colors(idx & 1))
									SetBkColor_(*lvCD\nmcd\hdc, BackColor)
									DrawText_(*lvCD\nmcd\hdc, t$, -1, subItemRect, #DT_END_ELLIPSIS|#DT_VCENTER|#DT_SINGLELINE)
									subItemRect\left + GetCharWidth(*nmhdr\hwndFrom, t$)
								EndIf
							Next idx
							Result = #CDRF_SKIPDEFAULT
						Else
							Result = #CDRF_DODEFAULT
						EndIf
				EndSelect
			EndIf
			
		Case #WM_CTLCOLOREDIT
			Buffer = Space(64)
			If GetClassName_(GetParent_(lParam), @Buffer, 64)
				If Buffer = "ComboBox"        
					SetTextColor_(wParam, #White)
					SetBkMode_(wParam, #TRANSPARENT)
					ProcedureReturn BackColorBrush
				EndIf
			EndIf	
			
		Case #WM_DRAWITEM
			*DrawItem.DRAWITEMSTRUCT = lParam
			If *DrawItem\CtlType = #ODT_COMBOBOX
				If IsGadget(wParam)
					If *DrawItem\itemID <> -1
						If *DrawItem\itemstate & #ODS_SELECTED
							FillRect_(*DrawItem\hDC, *DrawItem\rcitem, HightLightBrush)
						Else
							FillRect_(*DrawItem\hDC, *DrawItem\rcitem, BackColorBrush)
						EndIf
						SetBkMode_(*DrawItem\hDC, #TRANSPARENT)
						SetTextColor_(*DrawItem\hDC, $cccccc)
						Text$ = GetGadgetItemText(*DrawItem\CtlID, *DrawItem\itemID)
						*DrawItem\rcItem\left + DesktopScaledX(4)
						DrawText_(*DrawItem\hDC, Text$, Len(Text$), *DrawItem\rcItem, #DT_LEFT | #DT_SINGLELINE | #DT_VCENTER)
					EndIf
				EndIf
			EndIf
	EndSelect
	ProcedureReturn Result
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------
Procedure SetWindowTheme()
	Protected Theme.s, cmbRexID, ChildGadget, Buffer.s
	If OSVersion() >= #PB_OS_Windows_10
		Theme = "DarkMode_Explorer"
	Else
		Theme = "Explorer"
	EndIf
	
	SetWindowTheme_(GadgetID(#frmMain_References), @Theme, 0)
	
	cmbRexID = GadgetID(#cmbRex)
	Buffer = Space(64)
	If GetClassName_(cmbRexID, @Buffer, 64)
		If Buffer = "ComboBox"
			If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
				SetWindowTheme_(cmbRexID, "DarkMode_CFD", "Combobox")
			Else
				SetWindowTheme_(cmbRexID, @Theme, 0)
			EndIf
		EndIf
	EndIf
	ChildGadget = GetWindow_(cmbRexID, #GW_CHILD)
	If ChildGadget
		Buffer = Space(64)
		If GetClassName_(ChildGadget, @Buffer, 64)
			If Buffer = "ComboBox"
				If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
					SetWindowTheme_(ChildGadget, "DarkMode_CFD", "Combobox")
				Else
					SetWindowTheme_(ChildGadget, @Theme, 0)
				EndIf
			EndIf
		EndIf
	EndIf
EndProcedure

Procedure SetGadgetBorderless(Gadget)
	Protected hGad = GadgetID(Gadget)
	SetWindowLongPtr_(hGad, #GWL_EXSTYLE, GetWindowLongPtr_(hGad, #GWL_EXSTYLE) & (~#WS_EX_CLIENTEDGE))
	ProcedureReturn SetWindowPos_(hGad, 0, 0, 0, 0, 0, #SWP_SHOWWINDOW | #SWP_NOZORDER | #SWP_NOSIZE | #SWP_NOMOVE | #SWP_FRAMECHANGED)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

Procedure.s FindProcedureName(CursorLine, ProcedureLine)
  Protected LineCounter,  Line.s
  For LineCounter = CursorLine - 1 To 1 Step - 1   
    Line = LTrimChar(StringField(ScintillaText, LineCounter, #CRLF$))
    If Left(LCase(Line), Len("endprocedure")) = "endprocedure"
      Break
    EndIf
    If Left(LCase(Line), Len("procedure.")) = "procedure." Or Left(LCase(Line), Len("procedure ")) = "procedure "
        ProcedureLine = LineCounter
        ProcedureReturn    Line
    EndIf 
  Next
  ProcedureLine = -1
  ProcedureReturn ""
EndProcedure

Procedure ReferencesHeaderClick(hWnd, Message, wParam, lParam)
  Protected *Header.HD_NOTIFY, SelectedLine
  Protected Result=CallWindowProc_(frmMain_References, hWnd, Message, wParam, lParam)
  ;Protected ColumnClicked
  
  Select Message
    Case #WM_NOTIFY
      *Header=lParam
      If *Header\hdr\code=#HDN_ITEMCLICK
        ;ColumnClicked=*Header\iItem
        SelectedLine = Val(GetGadgetItemText(#frmMain_References, -1, 0))
        If SelectedLine > 0
          ReferencesSelectLine(SelectedLine)
        EndIf
      EndIf
  EndSelect 
  ProcedureReturn Result
EndProcedure

Procedure GetWordReferene(CursorLine, Word.s)
  Protected YouAreHere.s, ProcedureLine, Ls, l, LastProcedureName.s
  
  ClearGadgetItems(#frmMain_References)
  ClearList(FoundReference())
  SelectedWord = Word
  CompilerIf Not #PB_Compiler_Debugger
    If SelectedWord = "" : End : EndIf
  CompilerEndIf
  
  ScintillaText.s = GetScintillaText()
  CompilerIf Not #PB_Compiler_Debugger
    If ScintillaText = "" : End : EndIf
  CompilerEndIf
  
  ; >> These are test pattern for development
  CompilerIf #PB_Compiler_Debugger
    
    If SelectedWord = ""
      ;     SelectedWord = "Line"    ; try one of these
      ;     SelectedWord = "#Line"   ; -"-  #Line could be in a comment also
      SelectedWord = "ScintillaText"   ; -"-
    EndIf
    
    If ScintillaText = ""
      #File = 0
      If ReadFile(#File, #PB_Compiler_File)
        ScintillaText = ReadString(#File, #PB_UTF8 | #PB_File_IgnoreEOL)
        CloseFile(#File)
      EndIf
      ; 			RunProgram("explorer.exe", "/Select," + #PB_Compiler_File, "")
      
      ; 			ScintillaText = "" + #CRLF$ +
      ; 			                "#Line = #LF ;  #Line could be in a comment also " + #CRLF$ +
      ; 			                "Procedure Test(*Line)  ; pointer *Line " + #CRLF$ +
      ; 			                "" + #CRLF$ +
      ; 			                "If SelectedWord = LCase(Tokens(TokenCounter))" + #CRLF$ +
      ; 			                "	 AddElement(FoundReference())" + #CRLF$ +
      ; 			                "	 FoundReference()\LineNo = LineCounter + 1" + #CRLF$ +
      ; 			                "	 Line = Trim(Line)" + #CRLF$ +
      ; 			                "	 Line = Mid(Line, 1, Len(Line)-2)" + #CRLF$ +
      ; 			                "	 FoundReference()\Reference = Line" + #CRLF$ +
      ; 			                "EndIf" + #CRLF$ +
      ; 			                "" ; End of Text
    EndIf
  CompilerEndIf
  
  LookForWordUnderCursor()
  SetWindowTitle(#frmMain,"Display all: '" + SelectedWord + "', Found " + CountSelectedWords + " in " + Str(ListSize(FoundReference())) + " Lines ")
  If ScintillaText <> ""  
    YouAreHere = FindProcedureName(CursorLine, @ProcedureLine)
  EndIf
  SetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE,GetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE) &~ #LVS_NOCOLUMNHEADER)
  SetGadgetItemText(#frmMain_References, -1, Str(CursorLine) , 0)
  SetGadgetItemText(#frmMain_References, -1, SelectedWord , 1)
  SetGadgetItemText(#frmMain_References, -1, YouAreHere , 2)
  
  ProcedureLine = -1
  Ls = ListSize(FoundReference())  - 1
  For l = Ls To 0 Step -1
    SelectElement(FoundReference(), l)
    If FoundReference()\LineNo <> CursorLine  
      FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)
      ; >> first attempt to mark the selected word in the string
      FoundReference()\Reference = ReplaceString(FoundReference()\Reference, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$, #PB_String_NoCase)
      If Left(LCase(FoundReference()\Reference), Len("procedure.")) <> "procedure." Or Left(LCase(FoundReference()\Reference), Len("procedure ")) <> "procedure "
        If ProcedureLine = -1 Or FoundReference()\LineNo<ProcedureLine
          LastProcedureName = FindProcedureName(FoundReference()\LineNo, @ProcedureLine)
        EndIf 
        FoundReference()\InProcedure = LastProcedureName 
      EndIf
    EndIf  
  Next l
  
  ForEach FoundReference()
    If FoundReference()\LineNo <> CursorLine  
      AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference + #LF$ + FoundReference()\InProcedure)
    EndIf  
  Next
  
  ; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
  ; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
  SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
  SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
  SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 2, #LVSCW_AUTOSIZE)
  
  SetGadgetItemAttribute(#frmMain_References, 0, #PB_ListIcon_ColumnWidth, GetGadgetItemAttribute(#frmMain_References, 0, #PB_ListIcon_ColumnWidth, 0) + 5 ,0 ) 
EndProcedure

Procedure main()
	Protected WWE ;, idx, pos, le

	
	Initialization()  ;

	
	;--> ini
	If OpenPreferences(ini$) ; открываем ini
		If PreferenceGroup("set")
			xywh\x = ReadPreferenceInteger("x", xywh\x)
			xywh\y = ReadPreferenceInteger("y", xywh\y)
			xywh\w = ReadPreferenceInteger("w", xywh\w)
			xywh\h = ReadPreferenceInteger("h", xywh\h)
		EndIf
		ClosePreferences()
	EndIf
	If xywh\x = 0 And xywh\y = 0
		centered = #PB_Window_ScreenCentered
	EndIf
	CopyStructure(@xywh, @xywh2, xywhm)
	
	
	;- GUI
	If OpenWindow(#frmMain, xywh\x, xywh\y, xywh\w, xywh\h," ",#PB_Window_SystemMenu | #PB_Window_SizeGadget | centered)
		SetWindowColor(#frmMain, $3f3f3f) 
		StickyWindow(#frmMain, #True)
		SetWindowCallback(@myWindowCallback())
		
		ComboBoxGadget(#cmbRex, 3, 3, WindowWidth(#frmMain) - 34, 24, #PB_ComboBox_Editable | #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED)
		ForEach RegExpList()
		  AddGadgetItem(#cmbRex, -1, RegExpList()\Name)	  
		Next
; 		AddGadgetItem(#cmbRex, -1, "(?# Debug, All )\bDebug\b")
; 		AddGadgetItem(#cmbRex, -1, "(?# Debug, real )(?m)^\h*Debug\b")
; 		AddGadgetItem(#cmbRex, -1, "(?# WinAPI )(?mi)[a-z][\da-z]*_(?=\h*\()")
; 		AddGadgetItem(#cmbRex, -1, "(?# Link )https?://[\w.:]+/?(?:[\w/?&=.~;\+!*_#%-]+)")
; 		AddGadgetItem(#cmbRex, -1, "(?# Procedure )(?mi)^\h*(?:Procedure[CDL$]{0,5}?(?:\h*\.[abcdfilqsuw])?\h+\K)[A-Za-z_]\w*\h*(?=\()")
; 		AddGadgetItem(#cmbRex, -1, "(?# Macro )(?mi)^\h*Macro\h+\K[A-Za-z_]\w*\h*(?=\()")
; 		AddGadgetItem(#cmbRex, -1, "(?# Var$ )(?<![#@\w])\w+\$")
; 		AddGadgetItem(#cmbRex, -1, "(?# Hex num )(?i)\$[\da-f]+")
; 		AddGadgetItem(#cmbRex, -1, "(?# Comments )(?m)^\h*;.*?(?=\r?$)")
; 		#q$ = Chr(34)
; 		AddGadgetItem(#cmbRex, -1, "(?# Comments, All )(?m)^(?:[^" + #q$ + ";]*" + #q$ + "[^" + #q$ + "]*?" + #q$ + ")*[^" + #q$ + ";]*(;.*?)(?=\r?$)")
; 		AddGadgetItem(#cmbRex, -1, "(?# Structures, Declare )(?i)(?<![=\w" + #q$ + "\\./-])[a-z]\w*\.[a-z]\w+(?![\w" + #q$ + "\\./-])")
; 		AddGadgetItem(#cmbRex, -1, "(?# Structures, item )(?<![\w.:" + #q$ + "\\])\*?\w+(?:(?:\(\))?\\[\d_a-zA-Z]+)+(?![\w" + #q$ + "\\])")
; 		AddGadgetItem(#cmbRex, -1, "(?# Structures, Content )(?m)^\h*Structure\h*\K\w+")
; 		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Comments )(?m)^(?:[^\";]*\"[^\"]*?\")*[^\";]*(;.*?)(?=\r?$)")
; 		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, Declare )(?i)(?<![=\\w\"\\./-])[a-z]\\w*\\.[a-z]\\w+(?![\\w\"\\\\./-])")
; 		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, item )(?<![\\w.:\"\\\\])\\*?\\w+(?:(?:\\(\\))?\\\\[\\d_a-zA-Z]+)+(?![\\w\"\\\\])")
; 		AddGadgetItem(#cmbRex, -1, "(?# Types )\b\w+\.[sfdqbliwcapu]\b")
; 		AddGadgetItem(#cmbRex, -1, "(?# Constants, Declare )(?m)^\h*\K#\w+(?=\h*(?:=|\r?$))")
; 		AddGadgetItem(#cmbRex, -1, "(?# Constants, All )#\w+\b")
		
		ButtonGadget(#btnRex, WindowWidth(#frmMain) - 28, 3, 24, 24, ">")
		; ButtonGadget(#btnClose, WindowWidth(#frmMain) - 27, 3, 24, 24, "x") ; to make a black theme
		
		ListIconGadget(#frmMain_References, 3, GadgetY(#cmbRex)  + GadgetHeight(#cmbRex) + 3, WindowWidth(#frmMain) - 6, WindowHeight(#frmMain) - GadgetHeight(#cmbRex) - GadgetY(#cmbRex) - 6, "", 96, #PB_ListIcon_FullRowSelect|#PB_ListIcon_GridLines|#PB_ListIcon_AlwaysShowSelection)
		frmMain_References = SetWindowLongPtr_(GadgetID(#frmMain_References), #GWL_WNDPROC, @ReferencesHeaderClick())
		AddGadgetColumn(#frmMain_References, 1, "", 300)
		AddGadgetColumn(#frmMain_References, 2, "", 100)
		SetGadgetColor(#frmMain_References, #PB_Gadget_BackColor, $3f3f3f)
		SetGadgetColor(#frmMain_References, #PB_Gadget_FrontColor, $cccccc)
		SetGadgetBorderless(#frmMain_References)  ; by me
		
		; Optional DarkMode_Explorer theme if OSVersion >= Windows_10 Else Explorer Theme
		SetWindowTheme()

		AddKeyboardShortcut(#frmMain, #PB_Shortcut_Escape, #frmMain_Shortcut_Escape_Event)
		BindEvent(#PB_Event_SizeWindow, @frmMain_SizeWindow_Event(), #frmMain)
		BindGadgetEvent(#frmMain_References, @frmMain_References_Event())
		SetActiveGadget(#frmMain_References)
		
		AddKeyboardShortcut(#frmMain, #PB_Shortcut_Control | #PB_Shortcut_C, #Shortcut_Ctrl_C)
		
		GetWordReferene(Int(Val(StringField(GetEnvironmentVariable("PB_TOOL_Cursor"), 1, "x"))), GetEnvironmentVariable("PB_TOOL_Word"))
		
		Repeat
			Select WaitWindowEvent()
				Case #PB_Event_MoveWindow
					xywh\x = WindowX(#frmMain) 
					xywh\y = WindowY(#frmMain) 
				Case #PB_Event_CloseWindow
; 					Если размеры окна изменились, то сохраняем.
					If Not CompareMemory(@xywh, @xywh2, SizeOf(xywhm))
						If OpenPreferences(ini$) Or CreatePreferences(ini$)
							PreferenceGroup("set")
							WritePreferenceInteger("x", xywh\x)
							WritePreferenceInteger("y", xywh\y)
							WritePreferenceInteger("w", xywh\w)
							WritePreferenceInteger("h", xywh\h)
							ClosePreferences()
						EndIf
					EndIf
					Break
				Case #PB_Event_Menu
					Select EventMenu()
						Case #Shortcut_Ctrl_C
							CopyClipboardReference()
						Case #frmMain_Shortcut_Escape_Event
							Break
					EndSelect
				Case #PB_Event_Gadget
					Select EventGadget()
						Case #btnRex, #cmbRex
							GoRegExp()
					EndSelect
			EndSelect
		ForEver
		
		; 		DeleteObject_(brush\brushSelected)  ; objects created by CreateSolidBrush_() needs this!
	EndIf
	CompilerIf  #OneInstance
	  KillThread(RefThread)
	  FileMap::Close(*mem)
	CompilerEndIf
	ProcedureReturn 0  ; not necessary, but looks good/better
EndProcedure

End main()

;----== Bottom of File ==----------------------------------------------------------------------------------------------
AZJIO
Addict
Addict
Posts: 1316
Joined: Sun May 14, 2017 1:48 am

Re: Show all occurrences of a word in the IDE

Post by AZJIO »

Dadlick
I was busy. I already did, but I can't find it. While I found some constants by searching in the source "GOTO"

Code: Select all

line = ScintillaMsg(*sciptr, #SCI_GETFIRSTVISIBLELINE) ; first visible line
Count = ScintillaMsg(*sciptr, #SCI_LINESONSCREEN)	   ; number of visible rows
pos = ScintillaMsg(*sciptr, #SCI_POSITIONFROMLINE, line) ; the position of the beginning of the specified line number
ScintillaMsg(*sciptr, #SCI_GOTOPOS, 5) ; moves the cursor
Dadlick wrote: Thu Feb 16, 2023 3:25 pm Divided regular expressions into name and pattern. Only the name is displayed in the combobox.
This field is for entering your own regular expression. It doesn't work like this. You can make an option that if a certain prefix is ​​used, for example, the tilde "~", then consider this the name of a regular expression and insert it, but do not prohibit the use of your own regular expressions. Also, make regular expressions in the ini-file and load them, that is, the person himself decides what he will have in this list.

FindAllReferences.ini

Code: Select all

[regexp]
Debug, All=\bDebug\b
Debug, real=(?m)^\h*Debug\b
WinAPI=(?mi)[a-z][\da-z]*_(?=\h*\()
Link=https?://[\w.:]+/?(?:[\w/?&=.~;\+!*_#%-]+)
Procedure=(?mi)^\h*(?:Procedure[CDL$]{0,5}?(?:\h*\.[abcdfilqsuw])?\h+\K)[A-Za-z_]\w*\h*(?=\()
Macro=(?mi)^\h*Macro\h+\K[A-Za-z_]\w*\h*(?=\()
Var$=(?<![#@\w])\w+\$
Hex num=(?i)\$[\da-f]+
Comments=(?m)^\h*;.*?(?=\r?$)
Comments, All=(?m)^(?:[^" + #q$ + ";]*" + #q$ + "[^" + #q$ + "]*?" + #q$ + ")*[^" + #q$ + ";]*(;.*?)(?=\r?$)
Structures, Declare=(?i)(?<![=\w" + #q$ + "\\./-])[a-z]\w*\.[a-z]\w+(?![\w" + #q$ + "\\./-])
Structures, item=(?<![\w.:" + #q$ + "\\])\*?\w+(?:(?:\(\))?\\[\d_a-zA-Z]+)+(?![\w" + #q$ + "\\])
Structures, Content=(?m)^\h*Structure\h*\K\w+
Types=\b\w+\.[sfdqbliwcapu]\b
Constants, Declare=(?m)^\h*\K#\w+(?=\h*(?:=|\r?$))
Constants, All=#\w+\b
Dadlick
New User
New User
Posts: 6
Joined: Thu Feb 16, 2023 3:28 am

Re: Show all occurrences of a word in the IDE

Post by Dadlick »

This field is for entering your own regular expression.
fixed
Also, make regular expressions in the ini-file and load them, that is, the person himself decides what he will have in this list.
At the first start, the regular expressions already described here are saved in the ini file, at the next start, the entries from this file are used.

Now the highlighted line is third from the top if possible (total number of lines of text)

Code: Select all

; ----------------------------------------------------------------------------
; File : FindAllReferences[Win].pb
; ----------------------------------------------------------------------------
;
;   Description: Find all references of a variable
;            OS: Windows
; English-Forum:
;  French-Forum:
;  German-Forum: http://www.purebasic.fr/german/viewtopic.php?f=8&t=28292
; ----------------------------------------------------------------------------

; MIT License
;
; Copyright (c) 2015 Kiffi
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.
;
; ----------------------------------------------------------------------------
; Change Log :
;   2023-02-12 : New Additions from Mesa, AZJIO, Axolotl
;
; English-Forum: https://www.purebasic.fr/english/viewtopic.php?t=80739
;
; Change Details by Axolotl
;   - Integrated ColorListIconGadget.pb from AZJIO and modified
;   - Add a #PB_Compiler_Debugger mode (for development)
;   - New Structure of the entire application
;   - harmonize the different coding styles (still something to do)
;   - Changing the coloring of the selected words
;   - counting all appearances of the SelectedWord (quick and dirty)
;
; ----------------------------------------------------------------------------

; FindAllReferences
EnableExplicit

Structure RegExp
  Name.s
  RegExp.s
EndStructure

Global NewList RegExpList.RegExp()

Procedure AddRegExp(Name.s, RegExp.s)
  AddElement(RegExpList())
  RegExpList()\Name = Name
  RegExpList()\RegExp = RegExp
EndProcedure  
#OneInstance = #True  ;running only one instance of the application
#q$ = Chr(34)

; AddRegExp("Debug, All", "\bDebug\b")
; AddRegExp("Debug, real", "(?m)^\h*Debug\b")
; AddRegExp("WinAPI", "(?mi)[a-z][\da-z]*_(?=\h*\()")
; AddRegExp("Link", "https?://[\w.:]+/?(?:[\w/?&=.~;\+!*_#%-]+)")
; AddRegExp("Procedure", "(?mi)^\h*(?:Procedure[CDL$]{0,5}?(?:\h*\.[abcdfilqsuw])?\h+\K)[A-Za-z_]\w*\h*(?=\()")
; AddRegExp("Macro", "(?mi)^\h*Macro\h+\K[A-Za-z_]\w*\h*(?=\()")
; AddRegExp("Var$", "(?<![#@\w])\w+\$")
; AddRegExp("Hex num", "(?i)\$[\da-f]+")
; AddRegExp("Comments", "(?m)^\h*;.*?(?=\r?$)")
; AddRegExp("Comments, All", "(?m)^(?:[^" + #q$ + ";]*" + #q$ + "[^" + #q$ + "]*?" + #q$ + ")*[^" + #q$ + ";]*(;.*?)(?=\r?$)")
; AddRegExp("Structures, Declare", "(?i)(?<![=\w" + #q$ + "\\./-])[a-z]\w*\.[a-z]\w+(?![\w" + #q$ + "\\./-])")
; AddRegExp("Structures, item", "(?<![\w.:" + #q$ + "\\])\*?\w+(?:(?:\(\))?\\[\d_a-zA-Z]+)+(?![\w" + #q$ + "\\])")
; AddRegExp("Structures, Content", "(?m)^\h*Structure\h*\K\w+")
; AddRegExp("Types", "\b\w+\.[sfdqbliwcapu]\b")
; AddRegExp("Constants, Declare", "(?m)^\h*\K#\w+(?=\h*(?:=|\r?$))")
; AddRegExp("Constants, All","#\w+\b")

CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
	CompilerError "Windows Only!"
CompilerEndIf

CompilerIf  #OneInstance
CompilerIf #PB_Compiler_Thread = 0 
  CompilerError "Thread-safe compile"
CompilerEndIf

  ;https://www.purebasic.fr/german/viewtopic.php?p=198712#p198712
DeclareModule FileMap
  Declare Create(Name.s, Size.i, Security = #False)
  Declare Open(Name.s)
  Declare Close(*Mem)
EndDeclareModule

Module FileMap
  Global NewMap hMap.i()
  Global SA.SECURITY_ATTRIBUTES, pSD.SECURITY_DESCRIPTOR, IsInitSecurity
 
  Procedure Create(Name.s, Size.i, Security = #False)
    Protected handle, *mem
    If Security
      If Not IsInitSecurity
        If Not InitializeSecurityDescriptor_(@pSD, #SECURITY_DESCRIPTOR_REVISION)
          ProcedureReturn 0
        EndIf
        If Not SetSecurityDescriptorDacl_(@pSD, #True, #Null, #False)
          ProcedureReturn 0
        EndIf
        SA\nLength = SizeOf(SA)
        SA\lpSecurityDescriptor = @pSD
        SA\bInheritHandle = #True
        IsInitSecurity = #True
      EndIf
      handle = CreateFileMapping_(#INVALID_HANDLE_VALUE, @SA, #PAGE_READWRITE | #SEC_COMMIT | #SEC_NOCACHE, 0, Size, Name)
    Else
      handle = CreateFileMapping_(#INVALID_HANDLE_VALUE, 0, #PAGE_READWRITE | #SEC_COMMIT | #SEC_NOCACHE, 0, Size, Name)
    EndIf 
    If handle
      *mem = MapViewOfFile_(handle, #FILE_MAP_ALL_ACCESS, 0, 0, 0)
      hMap(Str(*mem)) = handle
      ProcedureReturn *mem
    EndIf   
  EndProcedure
 
  Procedure Open(Name.s) 
    Protected handle, *mem
    handle = OpenFileMapping_(#FILE_MAP_ALL_ACCESS, 0, Name)
    If handle
      *mem = MapViewOfFile_(handle, #FILE_MAP_ALL_ACCESS, 0, 0, 0)
      hMap(Str(*mem)) = handle
      ProcedureReturn *mem
    EndIf
  EndProcedure
  
  Procedure Close(*Mem)
    Protected result 
    UnmapViewOfFile_(*Mem)
    result = CloseHandle_(hMap(Str(*Mem)))
    DeleteMapElement(hMap(), Str(*Mem))
    ProcedureReturn result
  EndProcedure
EndModule

Structure myMem
  ReInit.l
  CursorLine.i
  Word.s{100}
EndStructure

Global *mem.myMem, RefThread
Declare GetWordReferene(CursorLine, Word.s)
Procedure Thread(*xx) ; Процедура работает в отдельном потоке и в ней выполняется приём данных 
  Repeat
    If *mem
      If *mem\ReInit = #True
        *mem\ReInit = #False
        GetWordReferene(*mem\CursorLine, *mem\Word)
      EndIf
    EndIf
  ForEver
EndProcedure
CompilerEndIf


;- Structure

Structure sFoundReference
	LineNo.i
	Reference.s
	Selregexp.s
	InProcedure.s
EndStructure

Structure xywhm
	x.i
	y.i
	w.i
	h.i
	m.i
EndStructure

;- Enumeration
Enumeration ; Windows
	#frmMain
EndEnumeration

Enumeration ; Gadgets
	#cmbRex
	#btnRex
	; 	#btnClose
	#frmMain_References
EndEnumeration

Enumeration ; Menu-/Toolbaritems
	#frmMain_Shortcut_Escape_Event
	#Shortcut_Ctrl_C
EndEnumeration

;- Constants
CompilerIf #PB_Compiler_Debugger
	#SelectedWordMarker$ = "|"
CompilerElse
	#SelectedWordMarker$ = Chr(1)  ; not used in source codes
CompilerEndIf
; "	 Line = Trim(Line)"
; "	 |Line| = Trim(|Line|)"
; --> ReplaceString(text$, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$)

;/-----------------------------------------------------------------------------
;| RGB() as HEX() -->     BB GG RR   .. i.e. RGB (1, 2, 3)    -->    03 02 01
;| RGB() as HEX() -->  AA BB GG RR   .. i.e. RGBA(1, 2, 3, 4) --> 04 03 02 01
;\

#coloredChars_Delimeter = "{***\"

;- Global
Global ini$ = LSet(ProgramFilename(), Len(ProgramFilename()) - 3) + "ini"
Global centered
Global xywh.xywhm
Global xywh2.xywhm
Global xywh\w = 600
Global xywh\h = 300

Global PbIdeHandle, ScintillaHandle
Global frmMain_References

Global SelectedWord.s, ScintillaText.s
Global CountSelectedWords    ; new, because we want to know all references (not only the lines)
Global NewList FoundReference.sFoundReference()
Global Dim Lines.s(0)

Global BackColor = $3f3f3f
Global HightLightBrush = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
; Global HightLightBrush = CreateSolidBrush_($423926)
Global BackColorBrush = CreateSolidBrush_(BackColor)

; ; ; Global PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
; ; ; If PbIdeHandle = 0 : End : EndIf
; ; ;
; ; ; Global ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
; ; ; If ScintillaHandle = 0 : End : EndIf

; ---== Procedures ==--------------------------------------------------------------------------------------------------

; AZJIO
Procedure.s LTrimChar(String.s, TrimChar.s = #CRLF$ + #TAB$ + #FF$ + #VT$ + " ")
	Protected *memChar, *c.Character, *jc.Character
	
	If Not Asc(String)
		ProcedureReturn ""
	EndIf
	
	*c.Character = @String
	*memChar = @TrimChar
	
	While *c\c
		*jc.Character = *memChar
		
		While *jc\c
			If *c\c = *jc\c
				*c\c = 0
				Break
			EndIf
			*jc + SizeOf(Character)
		Wend
		
		If *c\c
			String = PeekS(*c)
			Break
		EndIf
		*c + SizeOf(Character)
	Wend	
	ProcedureReturn String
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

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

; ---------------------------------------------------------------------------------------------------------------------

Procedure Initialization()
  
CompilerIf  #OneInstance
  *mem = FileMap::Open("MyMemory")
  If *mem = 0
    *mem.myMem = FileMap::Create("MyMemory", SizeOf(myMem))
    If *mem
      RefThread =  CreateThread(@Thread(),0)
    EndIf
  Else
    *mem\CursorLine = Int(Val(StringField(GetEnvironmentVariable("PB_TOOL_Cursor"), 1, "x")))
    *mem\Word = GetEnvironmentVariable("PB_TOOL_Word")
    *mem\ReInit = #True
    End
  EndIf
CompilerEndIf

	
	PbIdeHandle = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
	CompilerIf Not #PB_Compiler_Debugger
		If PbIdeHandle = 0 : End : EndIf
	CompilerEndIf
	
	ScintillaHandle = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
	CompilerIf Not #PB_Compiler_Debugger
		If ScintillaHandle = 0 : End : EndIf
	CompilerEndIf
	ProcedureReturn 0  ; default (ZERO is returned by default, even if there is no ProcedureReturn)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

; ChrisR
Procedure CopyClipboardReference()
	Protected text$
	PushListPosition(FoundReference()) 
	ForEach FoundReference()
		If text$ : text$ + #CRLF$ : EndIf
		If Right(FoundReference()\Reference, 2) = #CRLF$
			text$ + Left(FoundReference()\Reference, Len(FoundReference()\Reference) - 2)
		Else
			text$ + FoundReference()\Reference
		EndIf
	Next
	PopListPosition(FoundReference())
	If text$
		text$ = ReplaceString(text$, #SelectedWordMarker$, "")
		SetClipboardText(text$)
		Protected Title$ = GetWindowTitle(#frmMain)
		SetWindowTitle(#frmMain, Title$ + " (Reference copied To the clipboard)")
		Delay(500)
		SetWindowTitle(#frmMain, Title$)
	EndIf
EndProcedure

; AZJIO
Procedure GoRegExp()
	; LINK : https://www.purebasic.fr/english/viewtopic.php?p=595832#p595832
	Protected rex, LSize, Pos = 0, i, RegExp.s
	Protected Dim Tokens.s(0)
	
	ClearGadgetItems(#frmMain_References)
	ClearList(FoundReference())
	SetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE,GetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE) | #LVS_NOCOLUMNHEADER)
	i = GetGadgetState(#cmbRex)

	If i < 0
	  RegExp = GetGadgetText(#cmbRex)
	Else
	  SelectElement(RegExpList(), i)
	  RegExp = RegExpList()\RegExp
	EndIf  
	

	If Not Asc(RegExp)
		ProcedureReturn
	EndIf

	rex = CreateRegularExpression(#PB_Any, RegExp)
	; 	CountTokens = ExtractRegularExpression(rex, ScintillaText, Tokens()) ; tokenize the line
	
	; 	Debug ArraySize(Lines())
	
	If rex
		If ExamineRegularExpression(rex, ScintillaText)
			While NextRegularExpressionMatch(rex)
				If Not FindString(RegularExpressionMatchString(rex), #LF$)
					AddElement(FoundReference())
					FoundReference()\Selregexp = RegularExpressionMatchString(rex)
					FoundReference()\LineNo = RegularExpressionMatchPosition(rex)
					; 					Debug  FoundReference()\LineNo
				EndIf
			Wend
		EndIf
	Else
		MessageRequester("Regular expression error", RegularExpressionError())
		ProcedureReturn
	EndIf
	
	LSize = ListSize(FoundReference())
	If LSize > 0
		; 		If LSize > 5000 And MessageRequester("Continue?", "Found" + Str(LSize) + " rows, Continue?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_No
		; 			ProcedureReturn
		; 		EndIf
		
		Pos = 0
		i = 0
		SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 0, 0)
		ForEach FoundReference()
			While Pos < FoundReference()\LineNo
				Pos = FindString(ScintillaText, #LF$, Pos + 1)
				If Pos
					i + 1
				Else
					Break
				EndIf
				; 				Debug Str(FoundReference()\LineNo) + " "  + Str(Pos)
			Wend
			If i < 1 Or i > ArraySize(Lines())
				Continue
			EndIf
			FoundReference()\LineNo = i
			FoundReference()\Reference = Lines(i - 1)
			
			
			FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)
			
			; >> first attempt to mark the selected word in the string
			FoundReference()\Reference = ReplaceString(FoundReference()\Reference, FoundReference()\Selregexp, #SelectedWordMarker$ + FoundReference()\Selregexp + #SelectedWordMarker$, #PB_String_NoCase)
			
			AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference)
		Next
		; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
    ; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
		
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
		SendMessage_(GadgetID(#frmMain_References), #WM_SETREDRAW, 1, 0)
		
		SelectedWord = "regexp"
		SetWindowTitle(#frmMain, "Display all: '" + SelectedWord + "', Found " + " in " + Str(ListSize(FoundReference())) + " Lines")
	EndIf
EndProcedure


Procedure LookForWordUnderCursor()
  ; LINK : http://www.purebasic.fr/english/viewtopic.php?f=12&t=37823
  Protected RegexLines, PbRegexTokens, StartTime.q
  Protected CountLines, LineCounter, CountTokens, TokenCounter
  Protected Line.s, selWord.s, stx.s
  Protected Dim Tokens.s(0)
  
  RegexLines = CreateRegularExpression(#PB_Any , ".*\r\n")
  PbRegexTokens = CreateRegularExpression(#PB_Any, #DOUBLEQUOTE$ + "[^" + #DOUBLEQUOTE$ + "]*" + #DOUBLEQUOTE$ + "|[\*]?[a-zA-Z_]+[\w]*[\x24]?|#[a-zA-Z_]+[\w]*[\x24]?|[\[\]\(\)\{\}]|[-+]?[0-9]*\.?[0-9]+|;.*|\.|\+|-|[&@!\\\/\*,\|]|::|:|\|<>|>>|<<|=>{1}|>={1}|<={1}|=<{1}|={1}|<{1}|>{1}|\x24+[0-9a-fA-F]+|\%[0-1]*|%|'")
  
  ;CountLines = CountString(ScintillaText, #CRLF$)
  CountLines = ExtractRegularExpression(RegexLines, ScintillaText, Lines())
  
  selWord = LCase(SelectedWord)  ; keep the original writing
  CountSelectedWords = 0         ; init for new search
  
  For LineCounter = 0 To CountLines - 1
    Line = Lines(LineCounter)
    
    ;Debug "tokenize Line '" + Line + "'"
    
    CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens()) ; tokenize the line
    
    For TokenCounter = 0 To CountTokens - 1
      ;Debug "  check Token '" + Tokens(TokenCounter) + "'"
      
      If selWord = LCase(Tokens(TokenCounter))
        AddElement(FoundReference())
        FoundReference()\LineNo = LineCounter + 1
        
        Line = Trim(Line)
        Line = Mid(Line, 1, Len(Line)-2)  ; remove the #CRLF$
        
        CountSelectedWords + CountString(LCase(Line), selWord)   ; <-- count SelectedWord in the codeline
        
        FoundReference()\Reference = Line
        Break  ; only one line (evenn if there are more than one SelectedWord )
      EndIf
    Next TokenCounter
  Next LineCounter
  
  ; because of #Constant or *Pointer
  If ListSize(FoundReference()) = 0
    For LineCounter = 0 To CountLines - 1
      Line = Lines(LineCounter)
      CountTokens = ExtractRegularExpression(PbRegexTokens, Line, Tokens())
      For TokenCounter = 0 To CountTokens - 1
        stx = LCase(Tokens(TokenCounter))
        If stx = "#"+selWord Or stx = "*"+selWord
          AddElement(FoundReference())
          FoundReference()\LineNo = LineCounter + 1
          
          Line = Trim(Line)
          Line = Mid(Line, 1, Len(Line)-2)
          
          CountSelectedWords + CountString(LCase(Line), stx)  ; <-- count SelectedWord in the codeline
          
          FoundReference()\Reference = Line
          Break
        EndIf
      Next
    Next
    
    CompilerIf Not #PB_Compiler_Debugger
      If ListSize(FoundReference()) = 0 : End : EndIf
    CompilerEndIf
  EndIf
EndProcedure



; ---------------------------------------------------------------------------------------------------------------------
; XIncludeFile "ColorListIconGadget.pbi"  ; I prefer .pbi (instead of .pb)
; ---------------------------------------------------------------------------------------------------------------------

;... Create brushes for painting item background
Structure MYBRUSHES
	brushDefault.l
	brushSelected.l
EndStructure

; Global brush.MYBRUSHES
Global Dim Colors(1)

; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_HIGHLIGHT))
; brush\brushSelected = CreateSolidBrush_(GetSysColor_(#COLOR_INFOBK))
; brush\brushDefault = GetStockObject_(#WHITE_BRUSH)
; brush\brushSelected = $aaaaaa
; brush\brushDefault = $3f3f3f


; ---== Color for Default Text and Selected Word ==--------------------------------------------------------------------


; Colors(0) = #Red                                ; the SelectedWord
Colors(0) = $8080FF                           ; the SelectedWord
											  ; Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
											  ; Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT); the default text
Colors(1) = $cccccc							  ; the default text

; ---------------------------------------------------------------------------------------------------------------------

Procedure GetCharWidth(gad, c$)
	ProcedureReturn SendMessage_(gad, #LVM_GETSTRINGWIDTH, 0, @c$)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

;Here we add some text to the underlying cell text to store the color info.
Procedure SetColor(gad, row, column, startp, endp, color)
	Protected text$
	If column
		text$ = GetGadgetItemText(gad, row, column)
		;Now add the new text.
		text$+#coloredChars_Delimeter+Str(startp)+"\"+Str(endp)+"\"+Str(color)
		SetGadgetItemText(gad,row,text$,column)
	EndIf
EndProcedure


; ---== MainWindow Procedures ==---------------------------------------------------------------------------------------

Procedure frmMain_SizeWindow_Event()
	xywh\w = WindowWidth(#frmMain)
	xywh\h = WindowHeight(#frmMain)
	ResizeGadget(#cmbRex, #PB_Ignore, #PB_Ignore, xywh\w - 34, 24)
	ResizeGadget(#btnRex, xywh\w - 28, #PB_Ignore, #PB_Ignore, #PB_Ignore)
	ResizeGadget(#frmMain_References, #PB_Ignore, #PB_Ignore, xywh\w - 6, xywh\h - 33)
; 	SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
EndProcedure

Procedure ReferencesSelectLine(SelectedLine)
  Protected line, count, pos
  SendMessage_(ScintillaHandle, #SCI_SETFIRSTVISIBLELINE,SelectedLine - 3,0)
  SendMessage_(ScintillaHandle, #SCI_GOTOLINE, SelectedLine - 1, 0)
  SendMessage_(ScintillaHandle, #SCI_ENSUREVISIBLE, SelectedLine - 1, 0)  
  SetForegroundWindow_(PbIdeHandle)
  SetActiveWindow_(PbIdeHandle)
EndProcedure
  
Procedure frmMain_References_Event()
  Protected SelectedLine
  SelectedLine = Val(GetGadgetItemText(#frmMain_References, GetGadgetState(#frmMain_References), 0))
  If SelectedLine > 0
    ReferencesSelectLine(SelectedLine)
  EndIf
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

Procedure myWindowCallback(hwnd, msg, wParam, lParam)
	Protected Result, *nmhdr.NMHDR, *lvCD.NMLVCUSTOMDRAW, subItemRect.RECT, *DrawItem.DRAWITEMSTRUCT, Buffer.s
	Protected thisRow, thisCol, idx
	Protected t$, text$
	; ; ; ; ; ; ; 	Protected subItemText$, pos, color, c$, nextColor, thisColor
	
	Result = #PB_ProcessPureBasicEvents
	;;Dim LVColor(0)
	
	Select msg
		Case #WM_NCDESTROY
			DeleteObject_(HightLightBrush)
			DeleteObject_(BackColorBrush)
			
		Case #WM_NOTIFY
			*nmhdr.NMHDR = lParam
			*lvCD.NMLVCUSTOMDRAW = lParam
			If *lvCD\nmcd\hdr\hwndFrom = GadgetID(#frmMain_References) And *lvCD\nmcd\hdr\code = #NM_CUSTOMDRAW
				Select *lvCD\nmcd\dwDrawStage
					Case #CDDS_PREPAINT
						Result = #CDRF_NOTIFYITEMDRAW
					Case #CDDS_ITEMPREPAINT
						Result = #CDRF_NOTIFYSUBITEMDRAW;
					Case #CDDS_ITEMPREPAINT | #CDDS_SUBITEM
						thisRow = *lvCD\nmcd\dwItemSpec
						thisCol = *lvCD\iSubItem
						If thisCol
							;... Define rect for text
							subItemRect.RECT\left = #LVIR_LABEL
							subItemRect.RECT\top = *lvCD\iSubItem
							;... Get the subitem rect
							SendMessage_(GadgetID(#frmMain_References), #LVM_GETSUBITEMRECT, thisRow, @subItemRect)
							
							text$ = GetGadgetItemText(#frmMain_References, thisRow, thisCol)
							
							; 							If GetGadgetState(#frmMain_References) = thisRow
							; 								;... If item is selected
							; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushSelected)
							; 								;               Colors(1) = GetSysColor_(#COLOR_HIGHLIGHTTEXT)  ; the default text
							; 							Else
							; 								;... If item is not selected
							; 								FillRect_(*lvCD\nmcd\hdc, subItemRect, brush\brushDefault)
							; 								;               Colors(1) = GetSysColor_(#COLOR_WINDOWTEXT)  ; the default text
							; 							EndIf
							InflateRect_(subItemRect, -8, 0)
							
							For idx = 1 To CountString(text$, #SelectedWordMarker$) + 1
								t$ = StringField(text$, idx, #SelectedWordMarker$)
								If t$
									SetTextColor_(*lvCD\nmcd\hdc, colors(idx & 1))
									SetBkColor_(*lvCD\nmcd\hdc, BackColor)
									DrawText_(*lvCD\nmcd\hdc, t$, -1, subItemRect, #DT_END_ELLIPSIS|#DT_VCENTER|#DT_SINGLELINE)
									subItemRect\left + GetCharWidth(*nmhdr\hwndFrom, t$)
								EndIf
							Next idx
							Result = #CDRF_SKIPDEFAULT
						Else
							Result = #CDRF_DODEFAULT
						EndIf
				EndSelect
			EndIf
			
		Case #WM_CTLCOLOREDIT
			Buffer = Space(64)
			If GetClassName_(GetParent_(lParam), @Buffer, 64)
				If Buffer = "ComboBox"        
					SetTextColor_(wParam, #White)
					SetBkMode_(wParam, #TRANSPARENT)
					ProcedureReturn BackColorBrush
				EndIf
			EndIf	
			
		Case #WM_DRAWITEM
			*DrawItem.DRAWITEMSTRUCT = lParam
			If *DrawItem\CtlType = #ODT_COMBOBOX
				If IsGadget(wParam)
					If *DrawItem\itemID <> -1
						If *DrawItem\itemstate & #ODS_SELECTED
							FillRect_(*DrawItem\hDC, *DrawItem\rcitem, HightLightBrush)
						Else
							FillRect_(*DrawItem\hDC, *DrawItem\rcitem, BackColorBrush)
						EndIf
						SetBkMode_(*DrawItem\hDC, #TRANSPARENT)
						SetTextColor_(*DrawItem\hDC, $cccccc)
						Text$ = GetGadgetItemText(*DrawItem\CtlID, *DrawItem\itemID)
						*DrawItem\rcItem\left + DesktopScaledX(4)
						DrawText_(*DrawItem\hDC, Text$, Len(Text$), *DrawItem\rcItem, #DT_LEFT | #DT_SINGLELINE | #DT_VCENTER)
					EndIf
				EndIf
			EndIf
	EndSelect
	ProcedureReturn Result
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------
Procedure SetWindowTheme()
	Protected Theme.s, cmbRexID, ChildGadget, Buffer.s
	If OSVersion() >= #PB_OS_Windows_10
		Theme = "DarkMode_Explorer"
	Else
		Theme = "Explorer"
	EndIf
	
	SetWindowTheme_(GadgetID(#frmMain_References), @Theme, 0)
	
	cmbRexID = GadgetID(#cmbRex)
	Buffer = Space(64)
	If GetClassName_(cmbRexID, @Buffer, 64)
		If Buffer = "ComboBox"
			If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
				SetWindowTheme_(cmbRexID, "DarkMode_CFD", "Combobox")
			Else
				SetWindowTheme_(cmbRexID, @Theme, 0)
			EndIf
		EndIf
	EndIf
	ChildGadget = GetWindow_(cmbRexID, #GW_CHILD)
	If ChildGadget
		Buffer = Space(64)
		If GetClassName_(ChildGadget, @Buffer, 64)
			If Buffer = "ComboBox"
				If OSVersion() >= #PB_OS_Windows_10 And Theme = "DarkMode_Explorer"
					SetWindowTheme_(ChildGadget, "DarkMode_CFD", "Combobox")
				Else
					SetWindowTheme_(ChildGadget, @Theme, 0)
				EndIf
			EndIf
		EndIf
	EndIf
EndProcedure

Procedure SetGadgetBorderless(Gadget)
	Protected hGad = GadgetID(Gadget)
	SetWindowLongPtr_(hGad, #GWL_EXSTYLE, GetWindowLongPtr_(hGad, #GWL_EXSTYLE) & (~#WS_EX_CLIENTEDGE))
	ProcedureReturn SetWindowPos_(hGad, 0, 0, 0, 0, 0, #SWP_SHOWWINDOW | #SWP_NOZORDER | #SWP_NOSIZE | #SWP_NOMOVE | #SWP_FRAMECHANGED)
EndProcedure

; ---------------------------------------------------------------------------------------------------------------------

Procedure.s FindProcedureName(CursorLine, ProcedureLine)
  Protected LineCounter,  Line.s
  For LineCounter = CursorLine - 1 To 1 Step - 1   
    Line = LTrimChar(StringField(ScintillaText, LineCounter, #CRLF$))
    If Left(LCase(Line), Len("endprocedure")) = "endprocedure"
      Break
    EndIf
    If Left(LCase(Line), Len("procedure.")) = "procedure." Or Left(LCase(Line), Len("procedure ")) = "procedure "
        ProcedureLine = LineCounter
        ProcedureReturn    Line
    EndIf 
  Next
  ProcedureLine = -1
  ProcedureReturn ""
EndProcedure

Procedure ReferencesHeaderClick(hWnd, Message, wParam, lParam)
  Protected *Header.HD_NOTIFY, SelectedLine
  Protected Result=CallWindowProc_(frmMain_References, hWnd, Message, wParam, lParam)
  ;Protected ColumnClicked
  
  Select Message
    Case #WM_NOTIFY
      *Header=lParam
      If *Header\hdr\code=#HDN_ITEMCLICK
        ;ColumnClicked=*Header\iItem
        SelectedLine = Val(GetGadgetItemText(#frmMain_References, -1, 0))
        If SelectedLine > 0
          ReferencesSelectLine(SelectedLine)
        EndIf
      EndIf
  EndSelect 
  ProcedureReturn Result
EndProcedure

Procedure GetWordReferene(CursorLine, Word.s)
  Protected YouAreHere.s, ProcedureLine, Ls, l, LastProcedureName.s
  
  ClearGadgetItems(#frmMain_References)
  ClearList(FoundReference())
  SelectedWord = Word
  CompilerIf Not #PB_Compiler_Debugger
    If SelectedWord = "" : End : EndIf
  CompilerEndIf
  
  ScintillaText.s = GetScintillaText()
  CompilerIf Not #PB_Compiler_Debugger
    If ScintillaText = "" : End : EndIf
  CompilerEndIf
  
  ; >> These are test pattern for development
  CompilerIf #PB_Compiler_Debugger
    
    If SelectedWord = ""
      ;     SelectedWord = "Line"    ; try one of these
      ;     SelectedWord = "#Line"   ; -"-  #Line could be in a comment also
      SelectedWord = "ScintillaText"   ; -"-
    EndIf
    
    If ScintillaText = ""
      #File = 0
      If ReadFile(#File, #PB_Compiler_File)
        ScintillaText = ReadString(#File, #PB_UTF8 | #PB_File_IgnoreEOL)
        CloseFile(#File)
      EndIf
      ; 			RunProgram("explorer.exe", "/Select," + #PB_Compiler_File, "")
      
      ; 			ScintillaText = "" + #CRLF$ +
      ; 			                "#Line = #LF ;  #Line could be in a comment also " + #CRLF$ +
      ; 			                "Procedure Test(*Line)  ; pointer *Line " + #CRLF$ +
      ; 			                "" + #CRLF$ +
      ; 			                "If SelectedWord = LCase(Tokens(TokenCounter))" + #CRLF$ +
      ; 			                "	 AddElement(FoundReference())" + #CRLF$ +
      ; 			                "	 FoundReference()\LineNo = LineCounter + 1" + #CRLF$ +
      ; 			                "	 Line = Trim(Line)" + #CRLF$ +
      ; 			                "	 Line = Mid(Line, 1, Len(Line)-2)" + #CRLF$ +
      ; 			                "	 FoundReference()\Reference = Line" + #CRLF$ +
      ; 			                "EndIf" + #CRLF$ +
      ; 			                "" ; End of Text
    EndIf
  CompilerEndIf
  
  LookForWordUnderCursor()
  SetWindowTitle(#frmMain,"Display all: '" + SelectedWord + "', Found " + CountSelectedWords + " in " + Str(ListSize(FoundReference())) + " Lines ")
  If ScintillaText <> ""  
    YouAreHere = FindProcedureName(CursorLine, @ProcedureLine)
  EndIf
  SetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE,GetWindowLongPtr_(GadgetID(#frmMain_References),#GWL_STYLE) &~ #LVS_NOCOLUMNHEADER)
  SetGadgetItemText(#frmMain_References, -1, Str(CursorLine) , 0)
  SetGadgetItemText(#frmMain_References, -1, SelectedWord , 1)
  SetGadgetItemText(#frmMain_References, -1, YouAreHere , 2)
  
  ProcedureLine = -1
  Ls = ListSize(FoundReference())  - 1
  For l = Ls To 0 Step -1
    SelectElement(FoundReference(), l)
    If FoundReference()\LineNo <> CursorLine  
      FoundReference()\Reference = LTrimChar(FoundReference()\Reference, " " + #TAB$)
      ; >> first attempt to mark the selected word in the string
      FoundReference()\Reference = ReplaceString(FoundReference()\Reference, SelectedWord, #SelectedWordMarker$ + SelectedWord + #SelectedWordMarker$, #PB_String_NoCase)
      If Left(LCase(FoundReference()\Reference), Len("procedure.")) <> "procedure." Or Left(LCase(FoundReference()\Reference), Len("procedure ")) <> "procedure "
        If ProcedureLine = -1 Or FoundReference()\LineNo<ProcedureLine
          LastProcedureName = FindProcedureName(FoundReference()\LineNo, @ProcedureLine)
        EndIf 
        FoundReference()\InProcedure = LastProcedureName 
      EndIf
    EndIf  
  Next l
  
  ForEach FoundReference()
    If FoundReference()\LineNo <> CursorLine  
      AddGadgetItem(#frmMain_References, -1, Str(FoundReference()\LineNo) + #LF$ + FoundReference()\Reference + #LF$ + FoundReference()\InProcedure)
    EndIf  
  Next
  
  ; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE_USEHEADER)
  ; 		SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE_USEHEADER)  ; last column -> fill the remaining rest
  SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 0, #LVSCW_AUTOSIZE)
  SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 1, #LVSCW_AUTOSIZE)
  SendMessage_(GadgetID(#frmMain_References), #LVM_SETCOLUMNWIDTH, 2, #LVSCW_AUTOSIZE)
  
  SetGadgetItemAttribute(#frmMain_References, 0, #PB_ListIcon_ColumnWidth, GetGadgetItemAttribute(#frmMain_References, 0, #PB_ListIcon_ColumnWidth, 0) + 5 ,0 ) 
EndProcedure

Procedure WritePrefStr_AddRegExp(Name.s, RegExp.s)
  AddRegExp(Name, RegExp)
  WritePreferenceString(Name, RegExp)
EndProcedure

Procedure main()
	Protected WWE ;, idx, pos, le

	
	Initialization()  ;

	
	;--> ini
	If OpenPreferences(ini$) Or CreatePreferences(ini$); открываем ini
	  Debug ini$
	  If PreferenceGroup("set") 
	    xywh\x = ReadPreferenceInteger("x", xywh\x)
	    xywh\y = ReadPreferenceInteger("y", xywh\y)
	    xywh\w = ReadPreferenceInteger("w", xywh\w)
	    xywh\h = ReadPreferenceInteger("h", xywh\h)
	  EndIf  
	  If PreferenceGroup("regexp")
	    ExaminePreferenceKeys()
	    While  NextPreferenceKey() ; перебираем ключи
	      AddRegExp(PreferenceKeyName(), PreferenceKeyValue())
	    Wend
	  Else
	    WritePrefStr_AddRegExp("Debug, All", "\bDebug\b")
      WritePrefStr_AddRegExp("Debug, real", "(?m)^\h*Debug\b")
      WritePrefStr_AddRegExp("WinAPI", "(?mi)[a-z][\da-z]*_(?=\h*\()")
      WritePrefStr_AddRegExp("Link", "https?://[\w.:]+/?(?:[\w/?&=.~;\+!*_#%-]+)")
      WritePrefStr_AddRegExp("Procedure", "(?mi)^\h*(?:Procedure[CDL$]{0,5}?(?:\h*\.[abcdfilqsuw])?\h+\K)[A-Za-z_]\w*\h*(?=\()")
      WritePrefStr_AddRegExp("Macro", "(?mi)^\h*Macro\h+\K[A-Za-z_]\w*\h*(?=\()")
      WritePrefStr_AddRegExp("Var$", "(?<![#@\w])\w+\$")
      WritePrefStr_AddRegExp("Hex num", "(?i)\$[\da-f]+")
      WritePrefStr_AddRegExp("Comments", "(?m)^\h*;.*?(?=\r?$)")
      WritePrefStr_AddRegExp("Comments, All", "(?m)^(?:[^" + #q$ + ";]*" + #q$ + "[^" + #q$ + "]*?" + #q$ + ")*[^" + #q$ + ";]*(;.*?)(?=\r?$)")
      WritePrefStr_AddRegExp("Structures, Declare", "(?i)(?<![=\w" + #q$ + "\\./-])[a-z]\w*\.[a-z]\w+(?![\w" + #q$ + "\\./-])")
      WritePrefStr_AddRegExp("Structures, item", "(?<![\w.:" + #q$ + "\\])\*?\w+(?:(?:\(\))?\\[\d_a-zA-Z]+)+(?![\w" + #q$ + "\\])")
      WritePrefStr_AddRegExp("Structures, Content", "(?m)^\h*Structure\h*\K\w+")
      WritePrefStr_AddRegExp("Types", "\b\w+\.[sfdqbliwcapu]\b")
      WritePrefStr_AddRegExp("Constants, Declare", "(?m)^\h*\K#\w+(?=\h*(?:=|\r?$))")
      WritePrefStr_AddRegExp("Constants, All","#\w+\b")    
	  EndIf
	  ClosePreferences()
	EndIf
	
	If xywh\x = 0 And xywh\y = 0
		centered = #PB_Window_ScreenCentered
	EndIf
	CopyStructure(@xywh, @xywh2, xywhm)
	
	
	;- GUI
	If OpenWindow(#frmMain, xywh\x, xywh\y, xywh\w, xywh\h," ",#PB_Window_SystemMenu | #PB_Window_SizeGadget | centered)
		SetWindowColor(#frmMain, $3f3f3f) 
		StickyWindow(#frmMain, #True)
		SetWindowCallback(@myWindowCallback())
		
		ComboBoxGadget(#cmbRex, 3, 3, WindowWidth(#frmMain) - 34, 24, #PB_ComboBox_Editable | #CBS_HASSTRINGS | #CBS_OWNERDRAWFIXED)
		ForEach RegExpList()
		  AddGadgetItem(#cmbRex, -1, RegExpList()\Name)	  
		Next
; 		AddGadgetItem(#cmbRex, -1, "(?# Debug, All )\bDebug\b")
; 		AddGadgetItem(#cmbRex, -1, "(?# Debug, real )(?m)^\h*Debug\b")
; 		AddGadgetItem(#cmbRex, -1, "(?# WinAPI )(?mi)[a-z][\da-z]*_(?=\h*\()")
; 		AddGadgetItem(#cmbRex, -1, "(?# Link )https?://[\w.:]+/?(?:[\w/?&=.~;\+!*_#%-]+)")
; 		AddGadgetItem(#cmbRex, -1, "(?# Procedure )(?mi)^\h*(?:Procedure[CDL$]{0,5}?(?:\h*\.[abcdfilqsuw])?\h+\K)[A-Za-z_]\w*\h*(?=\()")
; 		AddGadgetItem(#cmbRex, -1, "(?# Macro )(?mi)^\h*Macro\h+\K[A-Za-z_]\w*\h*(?=\()")
; 		AddGadgetItem(#cmbRex, -1, "(?# Var$ )(?<![#@\w])\w+\$")
; 		AddGadgetItem(#cmbRex, -1, "(?# Hex num )(?i)\$[\da-f]+")
; 		AddGadgetItem(#cmbRex, -1, "(?# Comments )(?m)^\h*;.*?(?=\r?$)")
; 		#q$ = Chr(34)
; 		AddGadgetItem(#cmbRex, -1, "(?# Comments, All )(?m)^(?:[^" + #q$ + ";]*" + #q$ + "[^" + #q$ + "]*?" + #q$ + ")*[^" + #q$ + ";]*(;.*?)(?=\r?$)")
; 		AddGadgetItem(#cmbRex, -1, "(?# Structures, Declare )(?i)(?<![=\w" + #q$ + "\\./-])[a-z]\w*\.[a-z]\w+(?![\w" + #q$ + "\\./-])")
; 		AddGadgetItem(#cmbRex, -1, "(?# Structures, item )(?<![\w.:" + #q$ + "\\])\*?\w+(?:(?:\(\))?\\[\d_a-zA-Z]+)+(?![\w" + #q$ + "\\])")
; 		AddGadgetItem(#cmbRex, -1, "(?# Structures, Content )(?m)^\h*Structure\h*\K\w+")
; 		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Comments )(?m)^(?:[^\";]*\"[^\"]*?\")*[^\";]*(;.*?)(?=\r?$)")
; 		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, Declare )(?i)(?<![=\\w\"\\./-])[a-z]\\w*\\.[a-z]\\w+(?![\\w\"\\\\./-])")
; 		; 		AddGadgetItem(#cmbRex, -1, ~"(?# Structures, item )(?<![\\w.:\"\\\\])\\*?\\w+(?:(?:\\(\\))?\\\\[\\d_a-zA-Z]+)+(?![\\w\"\\\\])")
; 		AddGadgetItem(#cmbRex, -1, "(?# Types )\b\w+\.[sfdqbliwcapu]\b")
; 		AddGadgetItem(#cmbRex, -1, "(?# Constants, Declare )(?m)^\h*\K#\w+(?=\h*(?:=|\r?$))")
; 		AddGadgetItem(#cmbRex, -1, "(?# Constants, All )#\w+\b")
		
		ButtonGadget(#btnRex, WindowWidth(#frmMain) - 28, 3, 24, 24, ">")
		; ButtonGadget(#btnClose, WindowWidth(#frmMain) - 27, 3, 24, 24, "x") ; to make a black theme
		
		ListIconGadget(#frmMain_References, 3, GadgetY(#cmbRex)  + GadgetHeight(#cmbRex) + 3, WindowWidth(#frmMain) - 6, WindowHeight(#frmMain) - GadgetHeight(#cmbRex) - GadgetY(#cmbRex) - 6, "", 96, #PB_ListIcon_FullRowSelect|#PB_ListIcon_GridLines|#PB_ListIcon_AlwaysShowSelection)
		frmMain_References = SetWindowLongPtr_(GadgetID(#frmMain_References), #GWL_WNDPROC, @ReferencesHeaderClick())
		AddGadgetColumn(#frmMain_References, 1, "", 300)
		AddGadgetColumn(#frmMain_References, 2, "", 100)
		SetGadgetColor(#frmMain_References, #PB_Gadget_BackColor, $3f3f3f)
		SetGadgetColor(#frmMain_References, #PB_Gadget_FrontColor, $cccccc)
		SetGadgetBorderless(#frmMain_References)  ; by me
		
		; Optional DarkMode_Explorer theme if OSVersion >= Windows_10 Else Explorer Theme
		SetWindowTheme()

		AddKeyboardShortcut(#frmMain, #PB_Shortcut_Escape, #frmMain_Shortcut_Escape_Event)
		BindEvent(#PB_Event_SizeWindow, @frmMain_SizeWindow_Event(), #frmMain)
		BindGadgetEvent(#frmMain_References, @frmMain_References_Event())
		SetActiveGadget(#frmMain_References)
		
		AddKeyboardShortcut(#frmMain, #PB_Shortcut_Control | #PB_Shortcut_C, #Shortcut_Ctrl_C)
		
		GetWordReferene(Int(Val(StringField(GetEnvironmentVariable("PB_TOOL_Cursor"), 1, "x"))), GetEnvironmentVariable("PB_TOOL_Word"))
		
		Repeat
			Select WaitWindowEvent()
				Case #PB_Event_MoveWindow
					xywh\x = WindowX(#frmMain) 
					xywh\y = WindowY(#frmMain) 
				Case #PB_Event_CloseWindow
; 					Если размеры окна изменились, то сохраняем.
					If Not CompareMemory(@xywh, @xywh2, SizeOf(xywhm))
						If OpenPreferences(ini$) Or CreatePreferences(ini$)
							PreferenceGroup("set")
							WritePreferenceInteger("x", xywh\x)
							WritePreferenceInteger("y", xywh\y)
							WritePreferenceInteger("w", xywh\w)
							WritePreferenceInteger("h", xywh\h)
							ClosePreferences()
						EndIf
					EndIf
					Break
				Case #PB_Event_Menu
					Select EventMenu()
						Case #Shortcut_Ctrl_C
							CopyClipboardReference()
						Case #frmMain_Shortcut_Escape_Event
							Break
					EndSelect
				Case #PB_Event_Gadget
					Select EventGadget()
						Case #btnRex, #cmbRex
							GoRegExp()
					EndSelect
			EndSelect
		ForEver
		
		; 		DeleteObject_(brush\brushSelected)  ; objects created by CreateSolidBrush_() needs this!
	EndIf
	CompilerIf  #OneInstance
	  KillThread(RefThread)
	  FileMap::Close(*mem)
	CompilerEndIf
	ProcedureReturn 0  ; not necessary, but looks good/better
EndProcedure

End main()

;----== Bottom of File ==----------------------------------------------------------------------------------------------
AZJIO
Addict
Addict
Posts: 1316
Joined: Sun May 14, 2017 1:48 am

Re: Show all occurrences of a word in the IDE

Post by AZJIO »

Centered

Code: Select all

Procedure frmMain_References_Event()
	Protected SelectedLine, Count
	
	SelectedLine = Val(GetGadgetItemText(#frmMain_References, GetGadgetState(#frmMain_References), 0))
	If SelectedLine > 0
		SendMessage_(ScintillaHandle, #SCI_GOTOLINE, SelectedLine - 1, 0)
		Count = SendMessage_(ScintillaHandle, #SCI_LINESONSCREEN, 0, 0) / 2
		SendMessage_(ScintillaHandle, #SCI_SETFIRSTVISIBLELINE, SelectedLine - Count - 1, 0)
; 		SendMessage_(ScintillaHandle, #SCI_ENSUREVISIBLE, SelectedLine - 1, 0)
		SetForegroundWindow_(PbIdeHandle)
		SetActiveWindow_(PbIdeHandle)
	EndIf
EndProcedure
I need to change the input in the combobox, because I can't enter a regular expression, since it works when I enter a character. It is necessary to use WinAPI to respond not to character input, but to item selection.

It was necessary to insert events into the general procedure myWindowCallback, otherwise I tried to make this code work for half an hour.

Code: Select all

Global hHeader

Procedure ReferencesHeaderClick(hWnd, Message, wParam, lParam)
	Protected *nmhdr.NMHDR, text$, *pnmcd.NMCUSTOMDRAW, hdi.hd_item
----------------
			*nmhdr.NMHDR = lParam
			If *nmhdr\hwndFrom = hHeader And *nmhdr\code = #NM_CUSTOMDRAW
				*pnmcd.NMCUSTOMDRAW = lParam
				Select *pnmcd\dwDrawStage
					Case #CDDS_PREPAINT
						result = #CDRF_NOTIFYITEMDRAW
					Case #CDDS_ITEMPREPAINT
						text$=Space(200)
						hdi\mask = #HDI_TEXT
						hdi\psztext = @text$
						hdi\cchtextmax = Len(text$)
						SendMessage_(hHeader, #HDM_GETITEM, *pnmcd\dwItemSpec, hdi)
						If *lvCD\nmcd\dwItemSpec
							InflateRect_(*pnmcd\rc, -8, 0)
						Else
							InflateRect_(*pnmcd\rc, -6, 0)
						EndIf
						SetBkMode_(*pnmcd\hdc,#TRANSPARENT)
; 						SetBkColor_(*nmhdr\hwndFrom, BackColor)
; 						SetBkColor_(*pnmcd\hdc, BackColor)
						SetTextColor_(*pnmcd\hdc, $cccccc)
						DrawText_(*pnmcd\hdc, @text$, Len(text$), *pnmcd\rc, #DT_VCENTER|#DT_END_ELLIPSIS)
						result = #CDRF_SKIPDEFAULT
				EndSelect
			EndIf
			
----------------
hHeader = SendMessage_(GadgetID(#frmMain_References), #LVM_GETHEADER, 0, 0)
When choosing regular expressions, the initial line should not disappear, the Header should always be there. Header color should be different. Colors need to be added to FindAllReferences.ini

The line in the header is excluded from the list - don't do that, it causes confusion. I see it in the code and wonder why it is not in the list. If I'm traversing the lines and want to make all the changes inside some function, then I might run into a problem skipping one line if it's not in the list. I don't have to remember it's in the header.

An example of how to make an event only a method for selecting an element in a list. Now text input does not lead to automatic search.

Code: Select all

	Protected nNotifyCode;, nID
----------------
		Case #WM_COMMAND
			If lParam = GadgetID(#cmbRex)
				; nID = wParam & $FFF ; LoWord
				nNotifyCode = wParam >> 16 ; HiWord
				
				If nNotifyCode = #CBN_SELCHANGE
					GoRegExp()
				EndIf
			EndIf
Perhaps in the future you can make it possible to pass parameters through the command line, then it will work for any editor. There are several Scintilla events in the code, and you can disable them or make a different function for popular editors.

Code: Select all

If CountProgramParameters() > 1
	SelectedWord = ProgramParameter(0)
	PathFile = ProgramParameter(1)
	NameEditor = ProgramParameter(2)
Else
	Initialization()
EndIf
Due to the fact that you add unnecessary things (list of functions in the 3rd column), the program starts to work slowly. I have a PureAutoIt.pbi code with 6750 lines. If I use the "IIf" search, then the program shows the list after 7 seconds. It used to open instantly.

When you select a regular expression from the drop-down list, it's best to replace it with a real regular expression, as this allows you to edit it to suit your needs.
Post Reply