Question on using External Tools

Just starting out? Need help? Post your questions and find answers here.
User avatar
Tristano
Enthusiast
Enthusiast
Posts: 190
Joined: Thu Nov 26, 2015 6:52 pm
Location: Italy
Contact:

Question on using External Tools

Post by Tristano »

I'm working on a tool which manipulates PB source code. It's manually invoked from the menu, and gets passed the %FILE and %TEMPFILE tokens.

I just wanted a confirmation on an issue, because documentation isn't too explicit about it.

If I've understood correctly, when configuring tools in PB IDE, the insertion of the parameter %TEMPFILE makes PB create a temporary file-copy of the current source code (ie: the one that has the focus and the cursor in it).

Instead, the %FILE will provide just the filename of the current code -- IF saved at least once (else it has no location on disk).

My question is about how to access the exact code as it is in the IDE, and I suspect that using %FILE is not safe because the file on disk might differ from what is actually in the IDE if the user didn't save changes before invoking the tool. On the other hand, it seems that %TEMPFILE enforces to create a snapshot of the code as-it-is-in-the-IDE (regardless of wheter it was saved at all).

So, to manipulate the source code I should use both these tokens: %TEMPFILE to acess a copy of the code as it is in the IDE, and %FILE as a reference to where to save the code after manipulation -- I've also set "Wait until tool quits" and "Reload Source after the tool has quit".

Is my approach/understanding correct? Am I missing out some important things?

Any tips or advise from anyone on the issue of tools that change the source code within PB IDE?

Thanks...
The PureBASIC Archives: FOSS Resources:
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Question on using External Tools

Post by Lunasole »

Hi. There is a more advanced way to access IDE active code/file through scintilla component and environment variables, maybe you find it useful.

When I created similar topic time ago someone posted following example of PB IDE tool by @danilo. It is based on scintilla messages instead, so you even no need to specify command line arguments.

That tool searches all lines in opened file where text (selected in IDE editor at tool launch) is present.
I didn't come yet to modify it for my targets, but I guess it is possible also to replace whole IDE text this way. That looks better as for me than parsing files.

To test it compile it to exe and add to PB IDE tools with "Menu or shortcut" trigger and without any other params

Code: Select all

;DESC: create a tool that underlines selected text and displays a list of matches
;INFO: danilo


CompilerIf #PB_Compiler_Unicode = 0
	CompilerError "Compile with Unicode mode enabled"
CompilerEndIf


#INDIC_STRAIGHTBOX = 8
#INDIC_DASH        = 9
#INDIC_DOTS        = 10
#INDIC_SQUIGGLELOW = 11
#INDIC_DOTBOX      = 12

#SCI_INDICSETALPHA          = 2523
#SCI_INDICSETOUTLINEALPHA   = 2558

#SCI_GOTOLINE =2024


;-----------------------------------
; SETUP

; #INDIC_PLAIN       ; Underlined With a single, straight line.
; #INDIC_SQUIGGLE    ; A squiggly underline. Requires 3 pixels of descender space.
; #INDIC_TT          ; A line of small T shapes.
; #INDIC_DIAGONAL    ; Diagonal hatching.
; #INDIC_STRIKE      ; Strike out.
; #INDIC_HIDDEN      ; An indicator With no visual effect.
; #INDIC_BOX         ; A rectangle around the text.
;
; - the following styles don't work with Scintilla used by PB 4.60
; - maybe useful for future versions
;
; #INDIC_ROUNDBOX    ; A rectangle with rounded corners around the text using translucent drawing
;                    ; with the interior usually more transparent than the border.
;                    ; You can use SCI_INDICSETALPHA And SCI_INDICSETOUTLINEALPHA to control the alpha transparency values.
;                    ; The default alpha values are 30 for fill colour and 50 for outline colour.
; #INDIC_STRAIGHTBOX ; A rectangle around the text using translucent drawing With the interior
;                    ; usually more transparent than the border.
;                    ; You can use SCI_INDICSETALPHA And SCI_INDICSETOUTLINEALPHA to control the alpha transparency values.
;                    ; The default alpha values are 30 for fill colour and 50 for outline colour.
; #INDIC_DASH        ; A dashed underline.
; #INDIC_DOTS        ; A dotted underline.
; #INDIC_SQUIGGLELOW ; Similar To INDIC_SQUIGGLE but only using 2 vertical pixels so will fit under small fonts.
; #INDIC_DOTBOX      ; A dotted rectangle around the text using translucent drawing.
;                    ; Translucency alternates between the alpha and outline alpha settings with the top-left pixel
;                    ; using the alpha setting.
;                    ; SCI_INDICSETALPHA And SCI_INDICSETOUTLINEALPHA control the alpha transparency values.
;                    ; The default values are 30 For alpha And 50 For outline alpha.
;                    ; To avoid excessive memory allocation the maximum width of a dotted box is 4000 pixels.
;                    ; Not available For OS X Carbon.

style = #INDIC_STRAIGHTBOX

color = RGB($00,$FF,$FF)

InnerAlpha  =  50
BorderAlpha = 200
totalcount=0
Structure iconlist
	line$
	text$
EndStructure



NewList linepos.iconlist()

#IGNORE_CASE = #True

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

NewList positions.i()
NewList source.s()

Global Scintilla = Val( GetEnvironmentVariable("PB_TOOL_Scintilla") )
Global numBytes, utf8Buffer,sline$,sline.i

If Scintilla
	
	SendMessageTimeout_(Scintilla,#SCI_GETSELECTIONSTART,0,0,#SMTO_ABORTIFHUNG,2000,@selectionStart)
	SendMessageTimeout_(Scintilla,#SCI_GETSELECTIONEND  ,0,0,#SMTO_ABORTIFHUNG,2000,@selectionEnd)
	
	If selectionStart <> selectionEnd
		word$=""
		For i = selectionStart To selectionEnd-1
			SendMessageTimeout_(Scintilla,#SCI_GETCHARAT,i,0,#SMTO_ABORTIFHUNG,2000,@result)
			word$ + Chr(result)
		Next i
	Else
		word$    = GetEnvironmentVariable("PB_TOOL_Word")
	EndIf
	
	SendMessageTimeout_(Scintilla,#SCI_SETINDICATORCURRENT  ,9, 0          ,#SMTO_ABORTIFHUNG,2000,@result)
	SendMessageTimeout_(Scintilla,#SCI_INDICSETSTYLE        ,9, style      ,#SMTO_ABORTIFHUNG,2000,@result)
	SendMessageTimeout_(Scintilla,#SCI_INDICSETFORE         ,9, color      ,#SMTO_ABORTIFHUNG,2000,@result)
	SendMessageTimeout_(Scintilla,#SCI_INDICSETALPHA        ,9, InnerAlpha ,#SMTO_ABORTIFHUNG,2000,@result)
	SendMessageTimeout_(Scintilla,#SCI_INDICSETOUTLINEALPHA ,9, BorderAlpha,#SMTO_ABORTIFHUNG,2000,@result)
	SendMessageTimeout_(Scintilla,#SCI_INDICSETUNDER        ,9, #False     ,#SMTO_ABORTIFHUNG,2000,@result)
	
	If word$
		wordlen = Len(word$)
		
		CompilerIf #IGNORE_CASE
			word$ = LCase(word$)
		CompilerEndIf
		
		#SCI_GETCHARACTERPOINTER = 2520
		
		SendMessageTimeout_(Scintilla,#SCI_SETREADONLY,1,0,#SMTO_ABORTIFHUNG,2000,@result)
		SendMessageTimeout_(Scintilla,#SCI_GETCHARACTERPOINTER,0,0,#SMTO_ABORTIFHUNG,2000,@result)
		If result
			SendMessageTimeout_(Scintilla,#SCI_GETTEXTLENGTH,0,0,#SMTO_ABORTIFHUNG,2000,@length)
			memory = AllocateMemory(length+2)
			
			If Not memory
				SendMessageTimeout_(Scintilla,#SCI_SETREADONLY,0,0,#SMTO_ABORTIFHUNG,2000,@result)
				End
			EndIf
			
			GetWindowThreadProcessId_(Scintilla, @processId)
			hProcess = OpenProcess_(#PROCESS_ALL_ACCESS, #False, processId)
			ReadProcessMemory_(hProcess,result,memory,length,0)
			
			*p.Ascii = memory
			length = memory + length
			line=1
			While *p < length And *p\a
				
				If *p\a = 10                     ; remove chr(10) + chr(13)
					*p+1
					If *p\a = 13 : *p+1 : EndIf
					line+1
				ElseIf *p\a = 13                 ; remove chr(13) + chr(10)
					*p+1
					If *p\a = 10 : *p+1 : EndIf
					line+1
				Else                             ; read line
					offset = *p - memory
					text$ = ""
					While *p\a And *p\a <> 10 And *p\a <> 13
						text$ + Chr(*p\a)
						*p+1
					Wend
					If text$
						CompilerIf #IGNORE_CASE
							text$ = LCase(text$)
						CompilerEndIf
						pos = 1
						Repeat
							pos = FindString(text$,word$,pos)
							If pos
								If AddElement(positions())
									positions() = offset + pos - 1
								EndIf
								pos + wordlen
								If AddElement(linepos())
									linepos()\line$=Str(Line)+":"+Str(pos) 
									linepos()\text$=text$ 
								EndIf
								
							EndIf
						Until pos = 0
						
					EndIf
					; line + 1
				EndIf
			Wend
		EndIf
		
		SendMessageTimeout_(Scintilla,#SCI_SETREADONLY,0,0,#SMTO_ABORTIFHUNG,2000,@result)
		
		SendMessageTimeout_(Scintilla,#SCI_GETTEXTLENGTH ,0,0,#SMTO_ABORTIFHUNG,2000,@length)
		PostMessage_(Scintilla,#SCI_INDICATORCLEARRANGE,0,length)
		
		
		ForEach positions()
			PostMessage_(Scintilla,#SCI_INDICATORFILLRANGE,positions(),wordlen)
		Next
	Else
		SendMessageTimeout_(Scintilla,#SCI_GETTEXTLENGTH ,0,0,#SMTO_ABORTIFHUNG,2000,@length)
		PostMessage_(Scintilla,#SCI_INDICATORCLEARRANGE,0,length)
	EndIf
	
	mainWnd = OpenWindow(#PB_Any, #PB_Any, #PB_Any, 425, 480, "Matches Found", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	TextGadget(#PB_Any,10,10,200,25,"Total:"+Str(ListSize(positions())) )     
	wordlist = ListIconGadget(#PB_Any, 10, 40, 400, 400, "Line#", 75,  #PB_ListIcon_GridLines )
	AddGadgetColumn(wordlist, 1, "Text", 350)
	
	ForEach linepos()
		AddGadgetItem(wordlist, -1, linepos()\line$+Chr(10)+linepos()\text$)
	Next
	;PostMessage_(Scintilla,#SCI_GOTOLINE,0,210)
; 	SendMessageTimeout_(Scintilla,#SCI_GOTOLINE,210,0,#SMTO_ABORTIFHUNG,2000,@result)
	Repeat
		Select WaitWindowEvent()
			Case #PB_Event_CloseWindow
				appQuit = 1
			Case #PB_Event_Gadget
				Select EventGadget()
					Case wordlist
						Select EventType() 
							Case #PB_EventType_LeftClick
								If GetGadgetState(wordlist) >= 0 
									sline$=GetGadgetItemText(wordlist, GetGadgetState(wordlist), 0)  
									sline$=Left(sline$,FindString(sline$,":")-1)
									sline=Val(sline$)-1
; 									PostMessage_(Scintilla,#SCI_GOTOLINE,0,sline)
									SendMessageTimeout_(Scintilla,#SCI_GOTOLINE,sline,0,#SMTO_ABORTIFHUNG,2000,@result)
; 									MessageRequester("SLCIT",sline$,#PB_MessageRequester_Ok  )
									SetForegroundWindow_(Scintilla)
								EndIf
						EndSelect
						
						;myMsgBox(msg, mainWnd)
				EndSelect
		EndSelect
	Until appQuit = 1
EndIf


Also here is list of IDE environment variables to be accessed using GetEnvironmentVariable() function.

Code: Select all

Tips for writing your own code processing tools
The IDE provides additional information for the tools in the form of environment variables. They can be easily read inside the tool with the commands of the Process library. 

This is a list of provided variables. Note that those that provide information about the active source are not present for tools executed on IDE startup or end. 
PB_TOOL_IDE         - Full path and filename of the IDE
PB_TOOL_Compiler    - Full path and filename of the Compiler
PB_TOOL_Preferences - Full path and filename of the IDE's Preference file
PB_TOOL_Project     - Full path and filename of the currently open project (if any)
PB_TOOL_Language    - Language currently used in the IDE
PB_TOOL_FileList    - A list of all open files in the IDE, separated by Chr(10)  

PB_TOOL_Debugger    - These variables provide the settings from the Compiler Options
PB_TOOL_InlineASM     window for the current source. They are set to "1" if the option
PB_TOOL_Unicode       is enabled, and "0" if not.
PB_TOOL_Thread     
PB_TOOL_XPSkin     
PB_TOOL_OnError    

PB_TOOL_SubSystem   - content of the "Subsystem" field in the compiler options
PB_TOOL_Executable  - same as the %COMPILEFILE token for the command-line
PB_TOOL_Cursor      - same as the %CURSOR token for the command-line
PB_TOOL_Selection   - same as the %SELECTION token for the command-line
PB_TOOL_Word        - same as the %WORD token for the command-line

PB_TOOL_MainWindow  - OS handle to the main IDE window
PB_TOOL_Scintilla   - OS handle to the Scintilla editing component of the current source
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: Question on using External Tools

Post by freak »

You only need %TEMPFILE. Your tool should read this file and write its modifications back to the same file. If you have "Reload Source after the tool has quit" set, then the IDE will read back the contents of %TEMPFILE after the tool has quit and put the result back in the editor.

You should not modify the contents of the %FILE file! This file contains the state of the source code as the user last saved it. This could be much different from what is currently visible in the editor. If you overwrite this file, the user might lose data!
quidquid Latine dictum sit altum videtur
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8433
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Question on using External Tools

Post by netmaestro »

freak wrote:You should not modify the contents of the %FILE file! This file contains the state of the source code as the user last saved it. This could be much different from what is currently visible in the editor. If you overwrite this file, the user might lose data!
Could you please arrange to have this warning exactly as you wrote it placed in the help file at the appropriate place? Seriously, it needs to be there.
BERESHEIT
User avatar
Tristano
Enthusiast
Enthusiast
Posts: 190
Joined: Thu Nov 26, 2015 6:52 pm
Location: Italy
Contact:

Re: Question on using External Tools

Post by Tristano »

Dear @Lunasole, thank so much for this code example --- it really gives a new light to my project! I did notice that in PB Help file it mentions two environment vars in relation to this topic: PB_TOOL_MainWindow and PB_TOOL_Scintilla, saying they are handles to the IDE, but without and example I couldn't really make much out of them. Now it makes a lot more sense.

@freak:
freak wrote:You only need %TEMPFILE. Your tool should read this file and write its modifications back to the same file. If you have "Reload Source after the tool has quit" set, then the IDE will read back the contents of %TEMPFILE after the tool has quit and put the result back in the editor.

You should not modify the contents of the %FILE file! This file contains the state of the source code as the user last saved it. This could be much different from what is currently visible in the editor. If you overwrite this file, the user might lose data!
Ah! I got that wrong, didn't realize the IDE would read back %TEMPFILE. I had a chance to expirement a bit, but I was taking the source from %TEMPFILE and the write the modified version to %FILE -- which then caused the IDE to warn that the source was changed and ask if I wanted to reload it.

The Help files is a bit cryptic on this topic, I agree with @netmaestro that it's worth mentioning it in the docs.

Thanks a lot!
The PureBASIC Archives: FOSS Resources:
Little John
Addict
Addict
Posts: 4527
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: Question on using External Tools

Post by Little John »

netmaestro wrote:
freak wrote:You should not modify the contents of the %FILE file! This file contains the state of the source code as the user last saved it. This could be much different from what is currently visible in the editor. If you overwrite this file, the user might lose data!
Could you please arrange to have this warning exactly as you wrote it placed in the help file at the appropriate place? Seriously, it needs to be there.
+1
Post Reply