Bookmark Maker: Créer un bookmark sur un fichier texte.

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Marc56
Messages : 2146
Inscription : sam. 08/févr./2014 15:19

Bookmark Maker: Créer un bookmark sur un fichier texte.

Message par Marc56 »

(Sur une question de Zorro, et pour ne pas polluer son topic sur son editeur EPB, je poste ici le système que j'ai proposé à l'époque puisqu'il utilise finalement une autre méthode. Si ça peut servir, tant mieux.) :wink:

But recherché: créer un bookmark d'un fichier ( :idea: peut être utilisé pour n'importe quel type de fichier texte)

Méthode (pour un fichier source PureBasic)
  1. Ouvrir un fichier source PureBasic
  2. Chercher tous les mots-clés « Procedure » et certains types de commentaires « ;- » « ;{ ». (Les lignes de tirets seuls « ;------ » sont exclues)
  3. Créer une liste (liste + gadget)
  4. Quand on clique sur un des éléments de la liste, mettre la ligne en référence en haut de l'éditeur ainsi que le curseur et la ligne surlignée.
L’éditeur est un gadget Scintilla.

Principe de fonctionnement:
  • Si un nom de fichier est passé en paramètre, il est utilisé, sinon le programme ouvre une fenêtre de demande.
  • Le système recherche toutes les occurrences à l'aide d'une expression régulière
  • À chaque expression trouvée il mémorise l'offset (position depuis le début du fichier) et la chaîne (ligne 101). [Edit] et compte le nombre de CRLF dspuis le début et l'ajoute à la structure
    ̶D̶a̶n̶s̶ ̶u̶n̶e̶ ̶d̶e̶u̶x̶i̶è̶m̶e̶ ̶b̶o̶u̶c̶l̶e̶ ̶(̶l̶i̶g̶n̶e̶ ̶1̶1̶0̶)̶,̶ ̶p̶o̶u̶r̶ ̶t̶o̶u̶s̶ ̶l̶e̶s̶ ̶é̶l̶é̶m̶e̶n̶t̶s̶ ̶d̶e̶ ̶l̶a̶ ̶l̶i̶s̶t̶e̶,̶ ̶i̶l̶ ̶c̶o̶m̶p̶t̶e̶ ̶l̶e̶ ̶n̶o̶m̶b̶r̶e̶ ̶d̶e̶ ̶C̶R̶L̶F̶ ̶d̶e̶p̶u̶i̶s̶ ̶l̶e̶ ̶d̶é̶b̶u̶t̶ ̶e̶t̶ ̶m̶é̶m̶o̶r̶i̶s̶e̶ ̶c̶e̶ ̶n̶u̶m̶é̶r̶o̶ ̶d̶a̶n̶s̶ ̶l̶e̶ ̶t̶r̶o̶i̶s̶i̶è̶m̶e̶ ̶c̶h̶a̶m̶p̶s̶ ̶d̶e̶ ̶l̶a̶ ̶s̶t̶r̶u̶c̶t̶u̶r̶e̶.̶
    ̶O̶n̶ ̶p̶e̶u̶t̶ ̶m̶e̶t̶t̶r̶e̶ ̶l̶e̶s̶ ̶d̶e̶u̶x̶ ̶é̶t̶a̶p̶e̶s̶ ̶c̶i̶-̶d̶e̶s̶s̶u̶s̶ ̶e̶n̶ ̶u̶n̶e̶ ̶s̶e̶u̶l̶e̶.̶ ̶J̶e̶ ̶n̶'̶y̶ ̶a̶ ̶a̶v̶a̶i̶t̶ ̶p̶a̶s̶ ̶p̶e̶n̶s̶é̶ ̶à̶ ̶l̶'̶é̶p̶o̶q̶u̶e̶.̶.̶.̶
  • Il supprime le mot clé « procedure » et l'éventuel type « .x » (ligne 116)
  • Quand l'utilisateur clique sur un des éléments de la liste, il n'y a plus qu'à faire défiler l'écran sur ligne mémorisée (ligne 164).
Le système peut être modifié selon ce qu'on cherche et la position des mots (à la marge ou n'importe où)

Code : Tout sélectionner

; Bookmark_Maker_2.pb
; Marc56 - 19/06/19
;
; Crée une liste des procédures contenues dans un cource PB
; Ajoute certaines lignes de commentaires « ;- » et « ;{ »
; Fait défiler l'éditeur sur le ligne pointée dans la liste

EnableExplicit

Enumeration 
    #Win
    #Editor
    #Bookmark
    #Btn_Load_File
    #Btn_Refresh
EndEnumeration


Global File_Name$ = ProgramParameter(0)
Global Full_Txt$
Global RegEx$ = "\bprocedure(?:.\w)?\s+[\w.\(\)\s*,]+|;[-{][^-].+" 

Structure Type_Bookmark
    Offset.i    ; Position de la chaine "Procedure ..." dans le texte complet
    Title$      ; Texte de la procédure (pour l'instant juste le nom)
    NbL.i       ; Numéro de la ligne
EndStructure
Global NewList Bookmark.Type_Bookmark()

SetGadgetFont(#PB_Default, FontID(LoadFont(#PB_Any, "Consolas", 8)))

Procedure Init()
    OpenWindow(#Win, 0, 0, 1000, 800, "Auto-Bookmarker", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    If Not InitScintilla() : MessageRequester("Erreur", "Ne peut initialiser Scintilla") : End : EndIf
    
    ScintillaGadget(#Editor, 10, 10, 780, 780, 0)
    ListViewGadget(#Bookmark, 800, 40, 190, 750)
    ButtonGadget(#Btn_Load_File, 800, 10, 90, 25, "Ouvrir...")
    ButtonGadget(#Btn_Refresh,   900, 10, 90, 25, "Réindexer")
    
    ScintillaSendMessage(#Editor, #SCI_STYLESETFONT, #STYLE_DEFAULT, Ascii("Consolas")) 
    ScintillaSendMessage(#Editor, #SCI_STYLESETSIZE, #STYLE_DEFAULT, 10)
    ; Couleur texte
    ScintillaSendMessage(#Editor, #SCI_STYLESETBACK, #STYLE_DEFAULT, $F0FFFF)
    ScintillaSendMessage(#Editor, #SCI_STYLESETFORE, #STYLE_DEFAULT, $8B0000)
    ScintillaSendMessage(#Editor, #SCI_STYLECLEARALL)
    ; Marge de numéros de ligne
    ScintillaSendMessage(#Editor, #SCI_SETMARGINTYPEN, 0, #SC_MARGIN_NUMBER) ;
    ScintillaSendMessage(#Editor, #SCI_SETMARGINWIDTHN, 0, 50)
    ScintillaSendMessage(#Editor, #SCI_STYLESETBACK, #STYLE_LINENUMBER, $DBDBDB)
    ScintillaSendMessage(#Editor, #SCI_STYLESETFORE, #STYLE_LINENUMBER, $908070)
    ; Ligne en cours
    ScintillaSendMessage(#Editor, #SCI_SETCARETLINEBACK, $00FFFF)
    ScintillaSendMessage(#Editor, #SCI_SETCARETLINEVISIBLE, #True)
    
    If Not CreateRegularExpression(0, RegEx$, #PB_RegularExpression_NoCase)
        Debug RegularExpressionError()
        End
    EndIf
EndProcedure


Procedure Load_File()
    If Not ReadFile(0, File_Name$)
        Debug "Erreur ouverture fichier: " + File_Name$
        End
    EndIf 
    Protected FileType     = ReadStringFormat(0)
    Protected Size         = FileSize(File_Name$)
    Protected *Buffer      = AllocateMemory(Size)
    If *Buffer
        ReadData(0, *Buffer, Size)
        CloseFile(0)
        Full_Txt$ = PeekS(*Buffer, Size, FileType)
        FreeMemory(*Buffer)
    Else
        Debug "Erreur allocation mémoire"
    EndIf  
    ScintillaSendMessage(#Editor, #SCI_SETTEXT, 0, UTF8(Full_Txt$))
EndProcedure


Procedure Make_Bookmark()
    ClearList(Bookmark())
    ClearGadgetItems(#Bookmark)
    Full_Txt$ = GetGadgetText(#Editor)
    Protected Tmp_Remove$ 
    If ExamineRegularExpression(0, Full_Txt$)
        Protected i
        While NextRegularExpressionMatch(0)
            i + 1
            AddElement(Bookmark())
            With Bookmark()
                \Offset     = RegularExpressionMatchPosition(0) 
                \Title$     = RegularExpressionMatchString(0)   
                \NbL        = CountString(Left(Full_Txt$, \Offset), #CRLF$) + 1 
                If Mid(\Title$, 10, 1) = "."
                    Tmp_Remove$ = Left(\Title$, 11) ; Effacer le type (ex: procedure.q)
                Else
                    Tmp_Remove$ = "procedure"       ; Effacer le mot "procedure"
                EndIf
                AddGadgetItem(#Bookmark, -1, 
                              RemoveString(\Title$, Tmp_Remove$, #PB_String_NoCase) + 
                              " (" + Str(\NbL) + ")")            
            EndWith
        Wend    
    EndIf
    SetWindowTitle(#Win, File_Name$)
EndProcedure



Procedure Ask_File()
    File_Name$ = OpenFileRequester("Fichier à ouvrir", "", "Source PureBasic|*.pb", 0)
    If FileSize(File_Name$) > 0
        Full_Txt$ = ""
        ClearList(Bookmark())
        ClearGadgetItems(#Bookmark)
        Load_File() 
        Make_Bookmark()
    EndIf
EndProcedure


;- Début du programme
Init()
; Si aucun fichier passé en paramètre ou "droppé" sur l'icône, alors OpenFileRequester()
If ProgramParameter(0) = "" : Ask_File() : EndIf
Load_File()
Make_Bookmark()

Repeat 
    Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
            End
        Case #PB_Event_Gadget
            Select EventGadget()
                    
                Case #Btn_Load_File
                    Ask_File()
                    
                Case #Btn_Refresh
                    Make_Bookmark()
                    
                Case #Bookmark ; (Clic sur une ligne du bookmark)
                    SelectElement(Bookmark(), GetGadgetState(#Bookmark))
                    With Bookmark()
                        If \NbL - 1 > 0
                            ScintillaSendMessage(#Editor, #SCI_SETFIRSTVISIBLELINE, \NbL - 1)
                            ScintillaSendMessage(#Editor, #SCI_GOTOLINE, \NbL - 1)
                            SetActiveGadget(#Editor)
                        EndIf
                    EndWith       
            EndSelect
    EndSelect
ForEver

End
Ça peut paraître simpliste et ça peut sûrement être amélioré, mais tel quel, mon fichier de test (9800 lignes) est indexé en 70 ms sur une machine de 7 ans.
J'avais envisagé de compter les lignes par un déplacement relatif à la place d'un déplacement absolu, mais finalement ça va tellement vite que cela n'est pas nécessaire.

[Edit] version 2 : deux fonctions en une seule

:wink:
Dernière modification par Marc56 le mer. 19/juin/2019 13:05, modifié 1 fois.
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Bookmark Maker: Créer un bookmark sur un fichier texte.

Message par falsam »

C'est propre et trés rapide. Merci Marc.
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
Mindphazer
Messages : 635
Inscription : mer. 24/août/2005 10:42

Re: Bookmark Maker: Créer un bookmark sur un fichier texte.

Message par Mindphazer »

Sur mon Mac, j'ai dû remplacer

Code : Tout sélectionner

Full_Txt$ = GetGadgetText(#Editor)
par

Code : Tout sélectionner

ScintillaSendMessage(#Editor, #SCI_GETTEXT, 0, UTF8(Full_Txt$))
dans la procédure Make_Bookmark(), sinon Full_Txt$ était une chaîne vide
Bureau : Win10 64bits
Maison : Macbook Pro M1 14" SSD 512 Go / Ram 16 Go - iPad Pro 32 Go (pour madame) - iPhone 15 Pro Max 256 Go
Marc56
Messages : 2146
Inscription : sam. 08/févr./2014 15:19

Re: Bookmark Maker: Créer un bookmark sur un fichier texte.

Message par Marc56 »

@Mindphazer

Tu as raison, logiquement c'est la méthode Scintilla qui devrait fonctionner et pas gettext (j'ai oublié ça), cependant, sur Windows la méthode GetGadgetText() fonctionne mais pas celle #SCI_GETTEXT 8O :?:

L'aide PB sur GetGadgetText() ne spécifie pas si ça s'applique au gadget Editor :?
https://www.purebasic.com/french/docume ... ttext.html

Correctif en attendant

Code : Tout sélectionner

Procedure Make_Bookmark()
    ClearList(Bookmark())
    ClearGadgetItems(#Bookmark)
    
    ; Recharger en variable le contenu de l'éditeur en fonction de l'OS
    CompilerSelect #PB_Compiler_OS
        CompilerCase #PB_OS_Windows
            Full_Txt$ = GetGadgetText(#Editor) 
        CompilerCase #PB_OS_MacOS
            ScintillaSendMessage(#Editor, #SCI_GETTEXT, 0, UTF8(Full_Txt$))
        CompilerCase #PB_OS_Linux
            ScintillaSendMessage(#Editor, #SCI_GETTEXT, 0, UTF8(Full_Txt$))
    CompilerEndSelect
...
Je vais essayer sous Linux (mais je pense que ce sera comme pour Mac)

Merci
:wink:
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: Bookmark Maker: Créer un bookmark sur un fichier texte.

Message par Ollivier »

Marc56 a écrit :L'aide PB sur GetGadgetText() ne spécifie pas si ça s'applique au gadget Editor
https://www.purebasic.com/french/docume ... ttext.html
T'es pas réveillé ce matin. Ou alors tu cherches autre chose que de récupérer le texte tout entier.

EditorGadget() est bel et bien indiqué dans la page GetGadgetText(). Et dans la page EditorGadget(), GetGadgetText() est documenté. On ne peut pas mieux...
Marc56
Messages : 2146
Inscription : sam. 08/févr./2014 15:19

Re: Bookmark Maker: Créer un bookmark sur un fichier texte.

Message par Marc56 »

J'ai mal formulé, je voulais dire que l'aide sur GetGadgetText() n'indique pas que cette commande fonctionne aussi pour le gadget Scintilla alors qu'elle fonctionne (sous Windows mais pas Mac)

:wink:
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: Bookmark Maker: Créer un bookmark sur un fichier texte.

Message par Ollivier »

Ah... Peut-être qu'en mettant la technique décrite par Mindphazer dans Friture request and wishlist, ça fera partie des fonctionnalités ajoutées...
Avatar de l’utilisateur
Mindphazer
Messages : 635
Inscription : mer. 24/août/2005 10:42

Re: Bookmark Maker: Créer un bookmark sur un fichier texte.

Message par Mindphazer »

Ollivier a écrit :Ah... Peut-être qu'en mettant la technique décrite par Mindphazer dans Friture request and wishlist, ça fera partie des fonctionnalités ajoutées...
Y'a de la "Friture" sur ta ligne ? :mrgreen:

La question est de savoir s'il est logique ou pas que le contenu d'un scintillagadget puisse être récupéré par GetGadgetText()
Personnellement, j'aurai tendance à répondre non, mais c'est juste mon avis !
Bureau : Win10 64bits
Maison : Macbook Pro M1 14" SSD 512 Go / Ram 16 Go - iPad Pro 32 Go (pour madame) - iPhone 15 Pro Max 256 Go
Marc56
Messages : 2146
Inscription : sam. 08/févr./2014 15:19

Re: Bookmark Maker: Créer un bookmark sur un fichier texte.

Message par Marc56 »

Voilà une expression régulière qui affine un peu mieux la sélection. (évite aussi de prendre plusieurs lignes)

Code : Tout sélectionner

Global RegEx$ = "\bprocedure(?:..)?\s+[\w]+(?:..)?\s?\([\w., ]{0,}\)|;[-{][^-].+" 
Je pense que j'en ai peut-être oublié, car il y a des possibilités:
Procedure[.<type>] nom(<variable1[.<type>]> [, <variable2[.<type>]>, ...])
Mais avec ça on peut capturer () les différents éléments.

Sinon il suffit de s'arrêter à la parenthèse fermante.

Code : Tout sélectionner

\bprocedure.+\)
donc Procedures et commentaires

Code : Tout sélectionner

Global RegEx$ = "\bprocedure.+\)|;[-{][^-].+" 
Prend toutes les procédures (même en milieu de ligne) et les commentaires ;- et ;{ sauf les lignes de tirets


:idea: On peut améliorer le système en coloriant par exemple le fond de l'éditeur pour les procédures. Là encore, bien que ce soit faisable avec les commandes FindString, une expression régulière peut faire le job en une seule commande.
Cependant, il faut but l’empêcher de prendre "trop", car par défaut une regex est 'Greedy' (gourmande) c'est à dire qu'elle va "manger" tout ce qui ressemble au motif, y compris en rebouclant sur elle-même (comme une procédure récursive).
Si on demande de chercher Procedure.+EndProcedure on se retrouve donc avec toutes les procédures surlignées en une seule puisqu'il compte alors le premier 'procedure' et le dernier 'endprocedure' de tout le texte.
On doit donc lui demander donc d'être 'Lazy' (paresseuse) c'est à dire s'arrêter dès que la première occurence est trouvée
Procedure.+?EndProcedure
Le simple ? suivi du .+ (n'importe quel caractères) indique donc jusqu'à ce que ce qui suit corresponde (EndProcedure)
Ne pas oublier #PB_RegularExpression_DotAll pour signifier que le point = tous les caractères y compris les sauts de ligne.
Répondre