Code: Select all
;-{ ct_sci_trimlines.pb
; ==============================================
; REV: 18.02.18, skywalk
; Code Tool for PureBasic IDE Scintilla editor.
; Trim whitespace only lines and trailing whitespace.
; =========================================================================
; COMPILE TO: ct_sci-trimlines.exe
; COMPILER OPTIONS:
; [ ] Use Compiler: PureBasic 5.62 (Windows-x64)
; [ ] Use Icon:
; [ ] Enable inline ASM syntax coloring
; [ ] Create threadsafe executable
; [ ] Enable OnError lines support
; [x] Enable modern theme support (for Windows XP and above)
; [ ] Request Administrator mode for Windows Vista and above
; [ ] Request User mode for Windows Vista and above (no virtualization)
; Library Subsystem:
; Executable Format: Windows ;|Console|Shared DLL
; CPU: All ;|Dynamic|w/MMX|w/3DNOW|w/SSE|w/SSE2
; Linker options file:
; File Format: UTF-8
; Version Info: auto set fields --> %yy.%mm.%dd
; =========================================================================
; TOOL SETTINGS:
; Edit Tool Settings for each Case:
; Ex. Commandline: ct_sci-trimlines.exe
; Arguments: NONE
; Working Directory:
; Name: TRIM_LINES
; Event to trigger the tool: Menu or Shortcut: [Ctrl+t]
; ToolName Arguments Shortcut Description
; ---------------------- ---------------------- ----------------- -----------------------------------------------------------
; TRIM_LINES NONE [Ctrl+t] Trim whitespace only lines and trailing whitespace.
;-} ct_sci_trimlines.pb
EnableExplicit
ImportC ""
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
_wcslen_(*String) As "wcslen"
_wcsstr_(*String1, *String2) As "wcsstr"
CompilerElse
_wcslen_(*String) As "_wcslen"
_wcsstr_(*String1, *String2) As "_wcsstr"
CompilerEndIf
EndImport
Procedure SplitC(*String.Character, Array a$(1), *Delimiter)
;nco2k, http://www.purebasic.fr/english/viewtopic.php?p=516082#p516082
;skywalk; modified to catch trailing null's.
; Ex. "1,2,3,," Splits with ',' -> [1][2][3][][].
Protected.i *StringEnd, DelimiterLength, ArrayIndex
Protected.i Resize = 511
Protected.i nStrings = ArraySize(a$())
If *String\c
DelimiterLength = _wcslen_(*Delimiter) * SizeOf(Character)
If DelimiterLength
Repeat
*StringEnd = _wcsstr_(*String, *Delimiter)
If *StringEnd
a$(ArrayIndex) = PeekS(*String, (*StringEnd - *String) / SizeOf(Character))
ArrayIndex + 1
If ArrayIndex > nStrings
nStrings + Resize
ReDim a$(nStrings)
EndIf
*String = *StringEnd + DelimiterLength
Else
Break
EndIf
ForEver
EndIf
ReDim a$(ArrayIndex)
a$(ArrayIndex) = PeekS(*String) ; Catch final trailing null's if any.
ArrayIndex + 1
EndIf
ProcedureReturn ArrayIndex
EndProcedure
Procedure.s Join(Array a$(1), Delm$=#Empty$, iStart.i=0, iStop.i=-1)
; REV: 110301, skywalk
; Speed up String concatenation. 600x faster than x$ + y$.
; REV: 120104, skywalk
; Unicode support requires careful use of Enc above.
; If #PB_Compiler_Unicode=1 then Enc = #PB_Unicode
; ElseIf #PB_Compiler_Unicode=0 then Enc = #PB_Ascii or UTF8 works.
; #PB_Compiler_Unicode=1, forces Ascii strings to terminate early.
; The allocated memory is then too small creating an IMA.
; REV: 120410, skywalk
; Fixed bug when joining A$(0) of 2 or less elements.
; REV: 140720, skywalk
; Dropped Enc.i variable. Follows #PB_Compiler_Unicode now.
; UTF8 needs work since it cannot work if compiled as Ascii.
; REV: 140826, skywalk
; Added iStart+iStop to allow joining subsets of A$().
; And error logic for iStart/iStop conditions.
Protected.i i, k, nBytes, *p, *s
Protected.s r$
If iStart < 0
iStart = 0
EndIf
If iStop < 0
k = ArraySize(a$())
iStop = k
Else
If iStart >= iStop
iStop = iStart
k = iStop - iStart
Else
k = iStop - iStart + 1
EndIf
EndIf
; Determine nBytes required to hold string array contents
For i = iStart To iStop
nBytes + StringByteLength(a$(i))
Next i
If Delm$
nBytes + k * StringByteLength(Delm$) ; Add room for delimiters
EndIf
nBytes + SizeOf(Character) ; add #Empty$
;If nBytes < 1024
; nBytes = 1024
;EndIf
*s = AllocateMemory(nBytes)
If *s
If nBytes <= MemorySize(*s) ; Verify enough memory created
*p = *s ; Create tracking pointer for concatenating memory
If Delm$
CopyMemoryString(@a$(iStart), @*p)
If k > 0
CopyMemoryString(@Delm$)
k = iStop - 1 ; Avoid recalculating k-1 in For-Next loop
For i = iStart + 1 To k
CopyMemoryString(@a$(i))
CopyMemoryString(@Delm$)
Next i
CopyMemoryString(@a$(i))
EndIf
Else
CopyMemoryString(@a$(iStart), @*p)
If k > 0
For i = iStart + 1 To iStop
CopyMemoryString(@a$(i))
Next i
EndIf
EndIf
r$ = PeekS(*s, -1) ; Grab the concatenated memory
FreeMemory(*s)
EndIf
EndIf
ProcedureReturn r$
EndProcedure
Procedure.i ct_hpid_from_hW(hW.i)
Protected.i pid, hpid
If GetWindowThreadProcessId_(hW, @pid)
hpid = OpenProcess_(#PROCESS_ALL_ACCESS, 0, pid)
EndIf
ProcedureReturn hpid
EndProcedure
Procedure.s ct_sci_GetSelectedText(hSci.i, hpid.i)
; REV: 180218, skywalk modified from Stargate:
; http://www.purebasic.fr/english/viewtopic.php?p=507821#p507821
; USE: Get current selected text in the editor.
Protected.i Length, Format
Protected.i *mSci, *mtxt
Protected.s txt$
If hSci And hpid
Select SendMessage_(hSci, #SCI_GETCODEPAGE, 0, 0)
Case 0
Format = #PB_Ascii
Case 65001
Format = #PB_UTF8
EndSelect
Length = SendMessage_(hSci, #SCI_GETSELTEXT, 0, 0) + SizeOf(Character)
*mtxt = AllocateMemory(Length)
If *mtxt
*mSci = VirtualAllocEx_(hpid, 0, Length, #MEM_RESERVE | #MEM_COMMIT, #PAGE_EXECUTE_READWRITE)
If *mSci
SendMessage_(hSci, #SCI_GETSELTEXT, 0, *mSci)
ReadProcessMemory_(hpid, *mSci, *mtxt, Length - SizeOf(Character), 0)
VirtualFreeEx_(hpid, *mSci, Length, #MEM_RELEASE)
EndIf
txt$ = PeekS(*mtxt, -1, Format)
FreeMemory(*mtxt)
EndIf
EndIf
;MessageRequester("GetSelectionText()", txt$)
ProcedureReturn txt$
EndProcedure
Procedure ct_sci_SetSelectedText(hSci.i, hpid.i, txt$)
; REV: 180218, skywalk modified from Stargate:
; http://www.purebasic.fr/english/viewtopic.php?p=507821#p507821
; USE: Replace currently selected editor text(*mSci) with specified text(*mtxt).
Protected.i Length, Position, Format
Protected.i *mtxt, *mSci
If hSci And hpid
Select SendMessage_(hSci, #SCI_GETCODEPAGE, 0, 0)
Case 0
Format = #PB_Ascii
Case 65001
Format = #PB_UTF8
EndSelect
Length = StringByteLength(txt$, Format)
*mtxt = AllocateMemory(Length + SizeOf(Character))
If *mtxt
PokeS(*mtxt, txt$, #PB_Default, Format)
;MessageRequester("SetSelectionText()", txt$)
*mSci = VirtualAllocEx_(hpid, 0, Length, #MEM_RESERVE | #MEM_COMMIT, #PAGE_EXECUTE_READWRITE)
If *mSci
WriteProcessMemory_(hpid, *mSci, *mtxt, Length, 0)
Position = SendMessage_(hSci, #SCI_GETSELECTIONSTART, 0, 0)
SendMessage_(hSci, #SCI_REPLACESEL, 0, *mSci)
SendMessage_(hSci, #SCI_SETSELECTIONSTART, Position, 0)
SendMessage_(hSci, #SCI_SETSELECTIONEND, Position + Length, 0)
VirtualFreeEx_(hpid, *mSci, Length, #MEM_RELEASE)
EndIf
FreeMemory(*mtxt)
EndIf
EndIf
EndProcedure
Procedure.s SF_TitleCase(s$)
; skywalk; Modified from luis, Little John
; http://www.forums.purebasic.com/english/viewtopic.php?p=370491&sid=c211be8dff9e7412095071dd1feee541#p370491
; Capitalize 1st letter of each word found. Ascii and most Unicode but fails with some accented char's.
; Proper case capitalizes 1st letter of 1st word.
; Words defined with the following delimiters:
; space , ! " # $ % & ' ( ) * + - . /
; tab
; : ; < = > ? @
; [ \ ] ^ _ `
;Debug SF_TitleCase(" hmm, w::w w[we are one] we, went to neW,"+Chr(9)+"yorK toDay. to buy sausages ")
Protected *p.Character = @s$
Protected.i newWord = 1
While *p\c
If newWord
*p\c = Asc(UCase(Chr(*p\c)))
newWord = 0
Else
*p\c = Asc(LCase(Chr(*p\c)))
EndIf
If *p\c > 31 And *p\c < 48 ; space , ! " # $ % & ' ( ) * + - . /
newWord = 1
ElseIf *p\c = #TAB
newWord = 2
ElseIf *p\c > 57 And *p\c < 65 ; : ; < = > ? @
newWord = 3
ElseIf *p\c > 90 And *p\c < 97 ; [ \ ] ^ _ `
newWord = 4
EndIf
*p + SizeOf(Character)
Wend
ProcedureReturn s$
EndProcedure
Procedure.s SF_TrimLines(txt$)
; Trim empty or whitespace only lines and RTrim any trailing whitespaces.
Protected.i i, nLines, nLines2
Protected.s Delm$ = #CRLF$
Protected Dim a$(0)
nLines = SplitC(@txt$, a$(), @Delm$) - 1
Protected Dim b$(nLines)
For i = 0 To nLines
If Len(a$(i))
If Len(Trim(a$(i)))
b$(nLines2) = RTrim(a$(i))
nLines2 + 1
EndIf
EndIf
Next i
If nLines2
ReDim b$(nLines2 - 1)
; Retain final [CR+LF] in original txt$. Prevents collapsing lines with smaller selections.
If Right(txt$, 2) = #CRLF$
b$(nLines2 - 1) + #CRLF$
EndIf
txt$ = Join(b$(), #CRLF$)
Else
txt$ = " " ; Write at least 1 empty space. #Empty$ fails.
EndIf
;MessageRequester("SF_TrimLines()", "#SOS" + txt$ + "#EOS")
ProcedureReturn txt$
EndProcedure
Procedure Main()
Protected.i hSci = Val(GetEnvironmentVariable("PB_TOOL_Scintilla"))
Protected.i hpid = ct_hpid_from_hW(hSci)
;Protected.i hIDE = Val(GetEnvironmentVariable("PB_TOOL_MainWindow"))
If hSci And hpid
Protected.s r$ = ct_sci_GetSelectedText(hSci, hpid)
If r$
r$ = SF_TrimLines(r$)
ct_sci_SetSelectedText(hSci, hpid, r$)
;ct_sci_SetSelectedText(SF_TitleCase(r$))
EndIf
CloseHandle_(hpid)
EndIf
EndProcedure
Main()