+1 Would be a nice feature.
Here's a quick and dirty tool to search your history. Fun 2-day project
Had to do a little reverse-engineering on the IDE's History.db file.
Code: Select all
; +----------------+
; | SessionHistory |
; +----------------+
; | 2018-03-03 . Creation
; | 2018-03-05 . Cleanup, ASCII/UTF-8 detection, EnableExplicit
; | 2018-03-06 . Improvements based on new Database/Diff format info
CompilerIf (Not Defined(_SessionHistory_Included, #PB_Constant))
#_SessionHistory_Included = #True
CompilerIf (#PB_Compiler_IsMainFile)
EnableExplicit
CompilerEndIf
;-
;- Constants
Enumeration 1
#DATA_Empty ; data is null. the file is empty
#DATA_Same ; data is null. the file is the same as on the previous event
#DATA_Diff ; data is a diff since the previous event
#DATA_Full ; data is the full file content (new files in a session that are not empty, also used if diff is too large or if the chain of diff'ed events becomes too long)
#DATA_DiffZ ; data is a diff (zlib compressed)
#DATA_FullZ ; data is full file (zlib compressed)
EndEnumeration
;-
;- Structures
Structure SessionHistoryFileStruct
Filename.s
Event_ID.s
Session_ID.s
Text.s
Time.i
EndStructure
;-
;- Globals
Global NewList SessionHistoryFile.SessionHistoryFileStruct()
;-
;- Procedures
Procedure.i UnpackHistoryBlob(*Blob)
Protected *Result = #Null
If (*Blob)
Protected UnpackedSize.i = PeekL(*Blob)
If (UnpackedSize > 0)
*Result = AllocateMemory(UnpackedSize, #PB_Memory_NoClear)
If (*Result)
UseZipPacker()
If (UncompressMemory(*Blob + 4, MemorySize(*Blob) - 4, *Result, UnpackedSize))
; OK
Else
FreeMemory(*Result)
*Result = #Null
EndIf
EndIf
EndIf
EndIf
ProcedureReturn (*Result)
EndProcedure
Procedure.i GetDatabaseBlobBuffer(Database.i, Column.i)
Protected *Result = #Null
Protected BlobSize.i = DatabaseColumnSize(Database, Column)
If (BlobSize > 0)
*Result = AllocateMemory(BlobSize, #PB_Memory_NoClear)
If (*Result)
If (GetDatabaseBlob(Database, Column, *Result, BlobSize))
; OK
Else
FreeMemory(*Result)
*Result = #Null
EndIf
EndIf
EndIf
ProcedureReturn (*Result)
EndProcedure
Procedure.s ApplyHistoryChanges(Text.s, *Mem, MemSize.i, Format.i)
If (*Mem And (MemSize > 0))
Protected PartLen.i
Protected TargetSize.i = PeekL(*Mem)
If (TargetSize > 0)
Protected *In
If (Format & #PB_UTF8)
*In = UTF8(Text)
Else
*In = Ascii(Text)
EndIf
Protected *InPtr = *In
Protected *Out = AllocateMemory(TargetSize + 1)
Protected *OutPtr = *Out
Protected *DiffPtr.BYTE = *Mem + 4
While (*DiffPtr < *Mem + MemSize)
If (*DiffPtr\b = 'C') ; Copy (from source)
*DiffPtr + 1
PartLen = PeekL(*DiffPtr)
*DiffPtr + 4
CopyMemory(*InPtr, *OutPtr, PartLen)
*InPtr + PartLen
*OutPtr + PartLen
ElseIf (*DiffPtr\b = 'A') ; Add (from diff)
*DiffPtr + 1
PartLen = PeekL(*DiffPtr)
*DiffPtr + 4
CopyMemory(*DiffPtr, *OutPtr, PartLen)
*DiffPtr + PartLen
*OutPtr + PartLen
ElseIf (*DiffPtr\b = 'D') ; Delete (skip from source)
*DiffPtr + 1
PartLen = PeekL(*DiffPtr)
*DiffPtr + 4
*InPtr + PartLen
Else
;Debug "Unknown: " + Hex(*DiffPtr\b)
Break
EndIf
Wend
; (*OutPtr - *Out) always matches TargetSize (good!)
Text = PeekS(*Out, TargetSize, Format)
; but StringByteLength(Text, ...) doesn't always match TargetSize ... ???
; seems to be because of $00 bytes in the output, which shortens the string
If (*In)
FreeMemory(*In)
EndIf
If (*Out)
FreeMemory(*Out)
EndIf
EndIf
EndIf
ProcedureReturn (Text)
EndProcedure
Procedure.i ExamineSessionHistory(File.s = "")
Protected Result.i = #False
ClearList(SessionHistoryFile())
; Search for History file
If (File = "")
; Try PB Prefs location
File = GetEnvironmentVariable("PB_TOOL_Preferences")
If (File)
File = GetPathPart(File) + "History.db"
If (FileSize(File) < 0)
File = ""
EndIf
EndIf
; Try AppData
If (File = "")
File = GetUserDirectory(#PB_Directory_ProgramData)
If (File)
File = File + "PureBasic" + Right(File, 1) + "History.db"
If (FileSize(File) < 0)
File = ""
EndIf
EndIf
EndIf
EndIf
; Open Database
If (File)
UseSQLiteDatabase()
Protected DB.i = OpenDatabase(#PB_Any, File, "", "")
If (DB)
; Find all
If (DatabaseQuery(DB, "SELECT * FROM event WHERE previous_event='0'"))
Result = #True
While (NextDatabaseRow(DB))
AddElement(SessionHistoryFile())
SessionHistoryFile()\Event_ID = GetDatabaseString(DB, 0)
SessionHistoryFile()\Session_ID = GetDatabaseString(DB, 1)
SessionHistoryFile()\Filename = GetDatabaseString(DB, 2)
SessionHistoryFile()\Time = GetDatabaseLong(DB, 4)
Protected Format.i
If (GetDatabaseLong(DB, 7))
Format = #PB_UTF8 | #PB_ByteLength
Else
Format = #PB_Ascii
EndIf
If (FindString(SessionHistoryFile()\Filename, "::unsaved"))
SessionHistoryFile()\Filename = "Unsaved File"
EndIf
Protected *Blob = GetDatabaseBlobBuffer(DB, 8)
If (*Blob)
Select (GetDatabaseLong(DB, 5))
Case #DATA_FullZ
Protected *Unpacked = UnpackHistoryBlob(*Blob)
If (*Unpacked)
SessionHistoryFile()\Text = PeekS(*Unpacked, MemorySize(*Unpacked), Format)
FreeMemory(*Unpacked)
EndIf
Case #DATA_Full
SessionHistoryFile()\Text = PeekS(*Blob, MemorySize(*Unpacked), Format)
EndSelect
FreeMemory(*Blob)
EndIf
Wend
FinishDatabaseQuery(DB)
; Parse incremental code changes
ForEach (SessionHistoryFile())
Protected ID.s = SessionHistoryFile()\Event_ID
While (ID)
If (DatabaseQuery(DB, "SELECT * FROM event WHERE previous_event='" + ID + "'"))
If (NextDatabaseRow(DB))
SessionHistoryFile()\Time = GetDatabaseLong(DB, 4)
ID = GetDatabaseString(DB, 0)
*Blob = GetDatabaseBlobBuffer(DB, 8)
If (*Blob)
If (GetDatabaseLong(DB, 7))
Format = #PB_UTF8 | #PB_ByteLength
Else
Format = #PB_Ascii
EndIf
Select (GetDatabaseLong(DB, 5))
Case #DATA_FullZ ; Full text, zipped
*Unpacked = UnpackHistoryBlob(*Blob)
If (*Unpacked)
SessionHistoryFile()\Text = PeekS(*Unpacked, MemorySize(*Unpacked), Format)
FreeMemory(*Unpacked)
EndIf
Case #DATA_Full ; Full text
SessionHistoryFile()\Text = PeekS(*Blob, MemorySize(*Blob), Format)
Case #DATA_DiffZ ; Diff, zipped
*Unpacked = UnpackHistoryBlob(*Blob)
If (*Unpacked)
SessionHistoryFile()\Text = ApplyHistoryChanges(SessionHistoryFile()\Text,
*Unpacked, MemorySize(*Unpacked), Format)
FreeMemory(*Unpacked)
EndIf
Case #DATA_Diff ; Diff
SessionHistoryFile()\Text = ApplyHistoryChanges(SessionHistoryFile()\Text,
*Blob, MemorySize(*Blob), Format)
EndSelect
FreeMemory(*Blob)
EndIf
Else
ID.s = ""
EndIf
FinishDatabaseQuery(DB)
Else
Break
EndIf
Wend
Next
; Remove blank files
ForEach SessionHistoryFile()
If (Trim(SessionHistoryFile()\Text) = "")
DeleteElement(SessionHistoryFile())
EndIf
Next
; Sort reverse-chronological
SortStructuredList(SessionHistoryFile(), #PB_Sort_Descending,
OffsetOf(SessionHistoryFileStruct\Time), #PB_Integer)
EndIf
CloseDatabase(DB)
EndIf
EndIf
ProcedureReturn (Result)
EndProcedure
;-
;-
;- Demo Program (Search)
CompilerIf (#PB_Compiler_IsMainFile)
DisableExplicit
If (Not ExamineSessionHistory())
MessageRequester("SessionSearch", "Could not examine session history!", #PB_MessageRequester_Error)
End
EndIf
Procedure Search()
Query.s = Trim(GetGadgetText(0))
ClearGadgetItems(1)
ForEach SessionHistoryFile()
If ((Query = "") Or (FindString(SessionHistoryFile()\Text, Query, 1, #PB_String_NoCase)))
AddGadgetItem(1, n, GetFilePart(SessionHistoryFile()\Filename) + #LF$ +
FormatDate("%yyyy-%mm-%dd %hh:%ii", SessionHistoryFile()\Time))
SetGadgetItemData(1, n, @SessionHistoryFile())
n + 1
EndIf
Next
EndProcedure
Procedure ResizeCB()
ResizeGadget(0, #PB_Ignore, #PB_Ignore, WindowWidth(0) - 10, #PB_Ignore)
ResizeGadget(3, #PB_Ignore, #PB_Ignore, WindowWidth(0), WindowHeight(0) - GadgetHeight(0) - StatusBarHeight(0) - 15)
EndProcedure
Procedure PreviewCode()
i = GetGadgetState(1)
If (i >= 0)
ChangeCurrentElement(SessionHistoryFile(), GetGadgetItemData(1, i))
SetGadgetText(2, "")
SetGadgetText(2, SessionHistoryFile()\Text)
EndIf
EndProcedure
Procedure LaunchCode()
i = GetGadgetState(1)
If (i >= 0)
ChangeCurrentElement(SessionHistoryFile(), GetGadgetItemData(1, i))
j = 1
Repeat
File.s = GetTemporaryDirectory() +
GetFilePart(SessionHistoryFile()\Filename, #PB_FileSystem_NoExtension) +
"-" + Str(j) + ".pb"
j + 1
Until (FileSize(File) = -1)
If CreateFile(1, File)
WriteString(1, SessionHistoryFile()\Text)
CloseFile(1)
CompilerIf (#PB_Compiler_OS = #PB_OS_MacOS)
RunProgram("open", #DQUOTE$ + File + #DQUOTE$, GetCurrentDirectory())
CompilerElse
RunProgram(File)
CompilerEndIf
EndIf
EndIf
EndProcedure
OpenWindow(0, 0, 0, 640, 480, "SessionSearch", #PB_Window_ScreenCentered |
#PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget)
CreateStatusBar(0, WindowID(0))
AddStatusBarField(#PB_Ignore)
StatusBarText(0, 0, "Press Enter to search, Click to view code, Double-Click to launch code in PB")
StringGadget(0, 5, 5, 640, 20, "")
ResizeGadget(0, #PB_Ignore, #PB_Ignore, 640, GadgetHeight(0, #PB_Gadget_RequiredSize))
ListIconGadget(1, 0, 20, 320, 460, "File", 145, #PB_ListIcon_FullRowSelect)
AddGadgetColumn(1, 1, "Date/Time", 145)
EditorGadget(2, 320, 20, 320, 460, #PB_Editor_ReadOnly)
SetGadgetFont(2, LoadFont(0, "Courier New", 10))
SplitterGadget(3, 0, GadgetHeight(0) + 10, WindowWidth(0),
WindowHeight(0)-GadgetHeight(0), 1, 2, #PB_Splitter_Vertical | #PB_Splitter_FirstFixed)
BindEvent(#PB_Event_SizeWindow, @ResizeCB())
Search()
SetActiveGadget(0)
AddKeyboardShortcut(0, #PB_Shortcut_Return, 0)
AddKeyboardShortcut(0, #PB_Shortcut_Escape, 1)
ResizeCB()
Repeat
Event = WaitWindowEvent()
If (Event = #PB_Event_CloseWindow)
Done = #True
ElseIf (Event = #PB_Event_Menu)
Select EventMenu()
Case 0
Select GetActiveGadget()
Case 0
Search()
Case 1
LaunchCode()
EndSelect
Case 1
If GetGadgetText(0)
SetGadgetText(0, "")
Search()
EndIf
SetActiveGadget(0)
EndSelect
ElseIf (Event = #PB_Event_Gadget)
If (EventGadget() = 1) And ((EventType() = #PB_EventType_LeftClick) Or (EventType() = #PB_EventType_Change))
PreviewCode()
ElseIf (EventGadget() = 1) And (EventType() = #PB_EventType_LeftDoubleClick)
LaunchCode()
EndIf
EndIf
Until (Done)
CompilerEndIf
CompilerEndIf
;-