Prozedur Aufrufe in einem Projekt finden [Updated: 11.08.18]

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
MenschMarkus
Beiträge: 220
Registriert: 30.04.2009 21:21
Computerausstattung: i5-2300 (2.8 Ghz) Win10 -64bit / PB 5.73 LTS

Prozedur Aufrufe in einem Projekt finden [Updated: 11.08.18]

Beitrag von MenschMarkus »

Hi,

ich weiß ja nicht wie es Euch geht, aber zeitweise will ich wissen in welchen Projektdateien eine bestimmte Prozedur aufgerufen wird.
Hier ein kleines Tool, welches die Arbeit erleichtert.

Code: Alles auswählen

;
;
;
;   Projekt: Find ProcedureCall
;
;   Alle Dateien eines Projektes nach einem Prozeduraufruf durchsuchen
;   Find all procedure calls within a project
;   Date: 23.07.2018
;   Author: menschmarkus
;
;   Edit: 24.07.2018    - Procedure TrimTabsAndSpaces() eingefügt  (Danke an Sicro)
;   Edit: 27.07.2018    - Alphabetische Anordnung aller Prozeduren in einer Combo Box
;                       - Anzeige der aktiven Prozedur über der Aufrufliste
;   Edit: 11.08.2018    - Procedure mit Übergabeparameter werden jetzt korrekt erfasst
;
;   free for any use
;
EnableExplicit

Enumeration Program
  #XML
  #File
EndEnumeration

Enumeration FormWindow
  #Window_ProcedureFinder
EndEnumeration

Enumeration FormGadget
  #Combo_Procedures
  #ListIcon_Procedures
  #Tree_ProcedureFinder
  #Text_Procedure
EndEnumeration

Enumeration FormFont
  #Font_Window_ProcedureFinder 
EndEnumeration

Structure prc
  procedureName.s
  Line.i
EndStructure

Structure xml
  loaderror.s
  filename.s
  path.s
  List ProcedureName.prc()
EndStructure

LoadFont(#Font_Window_ProcedureFinder,"Arial", 12, #PB_Font_Bold)

Global NewList xmlstruct.xml()
Global projectname.s,strTMP.s,*MainNode, Event.i,actualdir.s, iint.i

Procedure.s TrimTabsAndSpaces(String.s)   ;by Sicro
 
  Protected StringStartPos.i = 1, StringEndPos.i = Len(String)
 
  ; Suche die erste Position, bei der sich kein Leerzeichen oder Tabulator befindet
  Repeat
    Select Mid(String, StringStartPos, 1)
      Case " ", #TAB$
        StringStartPos + 1
      Default
        Break
    EndSelect
  ForEver
 
  ; Suche von hinten die erste Position, bei der sich kein Leerzeichen oder Tabulator befindet
  Repeat
    Select Mid(String, StringEndPos, 1)
      Case " ", #TAB$
        StringEndPos - 1
        If StringEndPos < 0 : Break : EndIf     ;Verhindert eine Endlosschleife
      Default
        Break
    EndSelect
  ForEver
 
  ProcedureReturn Mid(String, StringStartPos, StringEndPos - StringStartPos + 1)
 
EndProcedure

Procedure OpenWindow_ProcedureFinder(x = 0, y = 0, width = 830, height = 370)
  OpenWindow(#Window_ProcedureFinder, x, y, width, height, "", #PB_Window_SystemMenu | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget | #PB_Window_SizeGadget)
  WindowBounds(#Window_ProcedureFinder,830,370,#PB_Ignore,#PB_Ignore)
  TextGadget(#Text_Procedure,10,10,580,20,"")
  ListIconGadget(#ListIcon_Procedures, 10, 40, 580, 310, "Dateiname", 450)
  AddGadgetColumn(#ListIcon_Procedures, 1, "ZNr.", 75)
  ComboBoxGadget(#Combo_Procedures,600,10,220,20)
  TreeGadget(#Tree_ProcedureFinder, 600, 40, 220, 310)
  SetGadgetFont(#Text_Procedure, FontID(#Font_Window_ProcedureFinder))
EndProcedure

Procedure ChangeWindowSize()
  Protected width.i, height.i
  width = WindowWidth(#Window_ProcedureFinder)
  height = WindowHeight(#Window_ProcedureFinder)
  ResizeGadget(#Text_Procedure,#PB_Ignore,#PB_Ignore,width - 250,#PB_Ignore)
  ResizeGadget(#Combo_Procedures,width - 230,#PB_Ignore,#PB_Ignore,#PB_Ignore)
  ResizeGadget(#ListIcon_Procedures,#PB_Ignore,#PB_Ignore,width-250,height-50)
  SendMessage_(GadgetID(#ListIcon_Procedures), #LVM_SETCOLUMNWIDTH, 0, width - 370) ;#LVSCW_AUTOSIZE_USEHEADER)
  ResizeGadget(#Tree_ProcedureFinder,width-230,#PB_Ignore,#PB_Ignore,height - 50)
EndProcedure

Procedure ReadXMLFile(*CurrentNode,Level.i)
  Protected *ChildNode
    If XMLNodeType(*CurrentNode) = #PB_XML_Normal Or XMLNodeType(*CurrentNode) = #PB_XML_Comment
      If GetXMLNodeName(*CurrentNode) = "file"
        If ExamineXMLAttributes(*CurrentNode)
          While NextXMLAttribute(*CurrentNode)
            If XMLAttributeName(*CurrentNode) = "name"
              AddElement(xmlstruct())
              xmlstruct()\filename = XMLAttributeValue(*CurrentNode)
              xmlstruct()\path = actualdir
            EndIf
          Wend
        EndIf
      EndIf
      *ChildNode = ChildXMLNode(*CurrentNode)
      While *ChildNode <> 0
        ReadXMLFile(*ChildNode,Level + 1)
        *ChildNode = NextXMLNode(*ChildNode)
      Wend
    EndIf
EndProcedure

Procedure getprocedurenames()
  Protected string.s, LineCounter.i=0, BOM.i
  NewList procc.s()
  ResetList(xmlstruct())
  While NextElement(xmlstruct())
    If ReadFile(#File,xmlstruct()\path + xmlstruct()\filename)
      BOM = ReadStringFormat(#File)
      AddGadgetItem(#Tree_ProcedureFinder,-1,xmlstruct()\filename,0,0)
      LineCounter = 0
      While Not Eof(#File)
        string = ReadString(#File,BOM)
        string = TrimTabsAndSpaces(string)
        If UCase(Left(string,10)) = "PROCEDURE "
          AddElement(xmlstruct()\ProcedureName())
          xmlstruct()\ProcedureName()\procedureName = Trim(Right(string,Len(string)-9))
          xmlstruct()\ProcedureName()\Line = LineCounter
          AddGadgetItem(#Tree_ProcedureFinder,-1,xmlstruct()\ProcedureName()\procedureName,0,1)
          AddElement(procc())
          procc() = xmlstruct()\ProcedureName()\procedureName
       EndIf
       LineCounter + 1
      Wend
    EndIf
  Wend
  If IsFile(#File) : CloseFile(#File) : EndIf
  SortList(procc(),#PB_Sort_Ascending)
  ResetList(procc())
  While NextElement(procc())
    AddGadgetItem(#Combo_Procedures,-1,procc())
  Wend
 
EndProcedure

Procedure getprocedurecalls(mode.i)
  Protected strTMP.s, fproc.s, linecounter.i, filename.s,BOM.i
  If mode = 0
    fproc = GetGadgetItemText(#Tree_ProcedureFinder,GetGadgetState(#Tree_ProcedureFinder))
  ElseIf  mode = 1
    fproc = GetGadgetItemText(#Combo_Procedures,GetGadgetState(#Combo_Procedures))
  EndIf
  SetGadgetText(#Text_Procedure,fproc)
  linecounter = FindString(fproc,"(")
  fproc = Left(fproc,linecounter)
  linecounter = 0
  ResetList(xmlstruct())
  ClearGadgetItems(#ListIcon_Procedures)
  While NextElement(xmlstruct())
    filename = xmlstruct()\path + xmlstruct()\filename
    If ReadFile(#File,filename)
      BOM = ReadStringFormat(#File)
      linecounter = 0
      While Not Eof(#File)
        linecounter + 1
        strTMP = ReadString(#File,BOM)
        If FindString(strTMP,fproc) > 0
          AddGadgetItem(#ListIcon_Procedures,-1,filename + Chr(10) + Str(linecounter))
        EndIf
      Wend
      If IsFile(#File) : CloseFile(#File) : EndIf
    EndIf
  Wend
EndProcedure

projectname = OpenFileRequester("Projektdatei","*.pbp","*.pbp | *.pbp",0)
If projectname <> ""
  actualdir = GetPathPart(projectname)
  If LoadXML(#XML,projectname,#PB_UTF8)
    If XMLStatus(#XML) <> #XML_STATUS_ERROR
      xmlstruct()\loaderror = "XMLError: " + XMLError(#XML) + " |  Line: " + Str(XMLErrorLine(#XML)) + " |  Position: " + Str(XMLErrorPosition(#XML))
    EndIf
    *MainNode = MainXMLNode(#XML)
    If *MainNode
      ReadXMLFile(*MainNode,0)
    EndIf
  EndIf
Else
  MessageRequester("Event Information","Die Datei '" + projectname + "' konnte nicht geladen werden oder existiert nicht",#MB_ICONINFORMATION)
EndIf

OpenWindow_ProcedureFinder()
SetWindowTitle(#Window_ProcedureFinder,projectname)
getprocedurenames()

Repeat
  Event = WaitWindowEvent()
  Select Event()
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #Tree_ProcedureFinder
          SetGadgetState(#Combo_Procedures,-1)
          getprocedurecalls(0)
        Case #Combo_Procedures
          iint = CountGadgetItems(#Tree_ProcedureFinder)
          Repeat
            SetGadgetItemState(#Tree_ProcedureFinder,iint,#PB_Tree_Collapsed)
            iint - 1
          Until iint = 0
          SetGadgetState(#Tree_ProcedureFinder,-1)
          getprocedurecalls(1)
      EndSelect
    Case #PB_Event_MaximizeWindow,#PB_Event_MinimizeWindow,#PB_Event_SizeWindow
      ChangeWindowSize()
  EndSelect
Until Event = #PB_Event_CloseWindow
End
Wie funktioniert es:

1. Projektdatei auswählen
2. Auf der rechten Seite im Baum die Prozedur auswählen
3. Auf der linken Seite erscheinen alle Dateien des Projektes in denen die Prozedur aufgerufen wird mit Angabe der entsprechenden Zeilennummer

Wer es mag, zur freien Verwendung.

LG
menschmarkus
Zuletzt geändert von MenschMarkus am 11.08.2018 09:39, insgesamt 6-mal geändert.
Wissen schadet nur dem, der es nicht hat !
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Prozedur Aufrufe in einem Projekt finden

Beitrag von Sicro »

Gute Idee 8)

Bedenke, dass Trim() standardmäßig nur Leerzeichen berücksichtigt. Möglicherweise haben mache Programmierer den PB-Editor so eingestellt, dass echte Tabulatoren verwendet werden. So könnte am Ende sogar ein Code entstehen, der mit einem Mix aus Leerzeichen und Tabulatoren eingerückt wurde. Nutze anstatt Trim() z. B. folgende Procedure, die alle Tabulatoren und Leerzeichen am Anfang und Ende vom String entfernt:

Code: Alles auswählen

Procedure$ TrimTabsAndSpaces(String$)
  
  Protected StringStartPos = 1, StringEndPos = Len(String$)
  
  ; Suche die erste Position, bei der sich kein Leerzeichen oder Tabulator befindet
  Repeat 
    Select Mid(String$, StringStartPos, 1)
      Case " ", #TAB$
        StringStartPos + 1
      Default
        Break
    EndSelect
  ForEver
  
  ; Suche von hinten die erste Position, bei der sich kein Leerzeichen oder Tabulator befindet
  Repeat 
    Select Mid(String$, StringEndPos, 1)
      Case " ", #TAB$
        StringEndPos - 1
      Default
        Break
    EndSelect
  ForEver
  
  ProcedureReturn Mid(String$, StringStartPos, StringEndPos - StringStartPos + 1)
  
EndProcedure
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
MenschMarkus
Beiträge: 220
Registriert: 30.04.2009 21:21
Computerausstattung: i5-2300 (2.8 Ghz) Win10 -64bit / PB 5.73 LTS

Re: Prozedur Aufrufe in einem Projekt finden

Beitrag von MenschMarkus »

Dein Einwand ist korrekt und gerechtfertigt. Habe ich in der Tat nicht berücksichtigt.
Danke für den Code Vorschlag, aber tut es nach dem Lesen des Strings nicht auch ein einfaches

Code: Alles auswählen

...
string = ReadString(#File)
string = RemoveString(string,Chr(9))   ;ggf. vorhandene TAB löschen
If UCase(Trim(Left(string,9))) = "PROCEDURE"
...
LG
menschmarkus
Wissen schadet nur dem, der es nicht hat !
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Prozedur Aufrufe in einem Projekt finden

Beitrag von Sicro »

MenschMarkus hat geschrieben:Danke für den Code Vorschlag, aber tut es nach dem Lesen des Strings nicht auch ein einfaches

Code: Alles auswählen

...
string = ReadString(#File)
string = RemoveString(string,Chr(9))   ;ggf. vorhandene TAB löschen
If UCase(Trim(Left(string,9))) = "PROCEDURE"
...
Ja, mit RemoveString() kannst du es natürlich auch machen. Dieser Befehl entfernt aber alle Tabulatoren im gesamten String und nicht, wie der Befehl "Trim()", nur am Anfang und am Ende vom String. Meine Procedure arbeitet wie der Befehl "Trim()", aber entfernt gleichzeitig Tabulatoren und Leerzeichen -- auch, wenn diese gemischt sind. Nimm die Methode, die dir mehr zusagt.
MenschMarkus hat geschrieben:

Code: Alles auswählen

If UCase(Trim(Left(string,9))) = "PROCEDURE"
Überdenke hier die Reihenfolge der Befehle.
Wenn im Code vor

Code: Alles auswählen

   Procedure GetText()
Leerzeichen sind erhältst du bei

Code: Alles auswählen

Left(string, 9)
das hier z. B.:

Code: Alles auswählen

   Proced
Achte darauf, dass vor dem Aufruf von "CloseFile()" die Datei erfolgreich geöffnet wurde. Eine Datei, die nicht geöffnet werden konnte, sollte nicht geschlossen werden (Debugger meldet Fehler).

Der PB-Editor kann die Codes im ASCII- oder UTF8-Format in Dateien abspeichern (siehe PB-Editor => Datei => Datei-Format => "Codierung ..."). Nach OpenFile() - hier reicht eigentlich auch ReadFile(), weil du schreibst ja nichts - solltest du also erstmal den BOM mit "ReadStringFormat()" auslesen und dem Befehl "ReadString()" den ermittelten BOM als Parameter übergeben.

Teste dein Programm zusätzlich mal mit Procedure-Blöcken anderer Schreibweisen:

Code: Alles auswählen

Procedure$ GetText()
  
EndProcedure

    Procedure.i   GetValue    (          )      ; Kommentare
  
EndProcedure

  Procedure CalculateValue(    Parameter1, Parameter2    ,    Paramter3)
  
EndProcedure

Procedure.i    DownloadFile    ( URL$,            ; Kommentar
                                 OutputFilePath$, ; Kommentar
                                 StatusCallback)
  
EndProcedure
Anstatt nur Leerzeichen kann es auch eine Mischung aus Leerzeichen und Tabulatoren sein.
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
MenschMarkus
Beiträge: 220
Registriert: 30.04.2009 21:21
Computerausstattung: i5-2300 (2.8 Ghz) Win10 -64bit / PB 5.73 LTS

Re: Prozedur Aufrufe in einem Projekt finden

Beitrag von MenschMarkus »

Sicro,

ich habe mich für Dein Codeschnipsel entschieden. Ich habe noch eine Zeile beim "TRIM" vom Ende der Zeile einfügen müssen. Bestand der String$ nur aus Leerzeichen landete ich in einer Endlosschleife.
Ein einfaches

Code: Alles auswählen

If StringEndPos < 0 : Break : EndIf
hat das Problem gelöst.

Danke nochmal Sicro

LG
menschmarkus
(code updated)
27.06.2018 code updated
11.08.2018 code updated
Wissen schadet nur dem, der es nicht hat !
Antworten