diff --git a/main.pb b/main.pb index 5ea3caa..977f651 100644 --- a/main.pb +++ b/main.pb @@ -507,6 +507,40 @@ EndProcedure ; ---- Helpers --------------------------------------------------------------- +Procedure.s _SupTrim(text.s) + If text = "" : ProcedureReturn "" : EndIf + + Protected i.i = 1 + Protected j.i = Len(text) + Protected c.l + + ; Trim gauche : avancer tant que <= 32 + While i <= j + c = Asc(Mid(text, i, 1)) + If c <= 32 + i + 1 + Else + Break + EndIf + Wend + + ; Trim droite : reculer tant que <= 32 + While j >= i + c = Asc(Mid(text, j, 1)) + If c <= 32 + j - 1 + Else + Break + EndIf + Wend + + If j < i + ProcedureReturn "" + Else + ProcedureReturn Mid(text, i, j - i + 1) + EndIf +EndProcedure + Procedure.s _JoinPath(base$, sub$) Protected out$ = Trim(base$) If out$ = "" : ProcedureReturn sub$ : EndIf @@ -552,6 +586,60 @@ Procedure Max(nb1, nb2) ProcedureReturn Result EndProcedure +; ------------------------------------------------------------------ +; Récupération récursive de TOUS les fichiers du workdir +; Remplit main\listFilesGit() avec un statut " " (clean/unmodified) +; ------------------------------------------------------------------ + +; --- Helper interne : scanne un dossier et alimente la liste +Procedure _ScanAndFillAllFiles(dir$, root$) + Protected did.i = ExamineDirectory(#PB_Any, dir$, "*") + If did = 0 : ProcedureReturn : EndIf + + While NextDirectoryEntry(did) + Protected name$ = DirectoryEntryName(did) + If name$ = "." Or name$ = ".." : Continue : EndIf + + Protected full$ = dir$ + name$ + Debug full$ + If DirectoryEntryType(did) = #PB_DirectoryEntry_Directory + ; Ignorer le dépôt interne + If LCase(name$) <> ".git" + If Right(full$, 1) <> #PS$ : full$ + #PS$ : EndIf + _ScanAndFillAllFiles(full$, root$) + EndIf + Else + ; Fichier : on ajoute une ligne dans main\listFilesGit() + Protected rel$ = Mid(full$, Len(root$) + 1) + rel$ = ReplaceString(rel$, "\", "/") ; chemins normalisés + + AddElement(main\listFilesGit()) + main\listFilesGit()\name = rel$ + main\listFilesGit()\status = " " ; 2 espaces = clean + main\listFilesGit()\indexStatus = " " + main\listFilesGit()\workingTreeStatus = " " + main\listFilesGit()\statusDescription = "Unmodified" + EndIf + Wend + + FinishDirectory(did) +EndProcedure + +; --- API publique : appelle ceci pour remplir la liste +Procedure.i FillAllFilesRecursively() + + root$ = GetCurrentDirectory() + + + ; Normalise avec un séparateur de fin + If Right(root$, 1) <> "\" And Right(root$, 1) <> "/" : root$ + "/" : EndIf + + ; On n'efface pas ici pour laisser le choix à l'appelant + _ScanAndFillAllFiles(root$, root$) + + ProcedureReturn ListSize(main\listFilesGit()) +EndProcedure + Procedure.i EnsureGitAvailable() Debug "EnsureGitAvailable()" @@ -702,6 +790,8 @@ EndProcedure Procedure.s GetStatusDescription(status.s) Select status + Case " " + ProcedureReturn "Unmodified" ;Non modifié Case "M " ProcedureReturn "Modified in index";Modifié dans index seulement Case " M" @@ -750,37 +840,37 @@ Procedure.s GetStatusDescription(status.s) EndProcedure Procedure GetGitStatus() - ; Vider la liste existante - ClearList(main\listFilesGit()) - + ; Ne PAS vider la liste ici : on veut pouvoir mettre à jour des entrées existantes + ; ClearList(main\listFilesGit()) ; <-- laissé intentionnellement commenté + ; Configuration pour git status --porcelain --ignored main\Gitcall\args = "status --porcelain --ignored" code = RunExe(@main\Gitcall) - + If code = 0 ; Parser la sortie ligne par ligne output$ = main\Gitcall\output - + ; Diviser en lignes Dim lines.s(0) lineCount = 0 currentPos = 1 - + ; Compter les lignes For i = 1 To Len(output$) If Mid(output$, i, 1) = #LF$ Or Mid(output$, i, 1) = #CR$ lineCount + 1 EndIf Next - + ; Redimensionner le tableau If lineCount > 0 ReDim lines.s(lineCount) - + ; Remplir le tableau avec les lignes lineIndex = 0 startPos = 1 - + For i = 1 To Len(output$) If Mid(output$, i, 1) = #LF$ Or Mid(output$, i, 1) = #CR$ If i > startPos @@ -795,46 +885,64 @@ Procedure GetGitStatus() EndIf EndIf Next - + ; Traiter la dernière ligne si elle n'a pas de retour à la ligne If startPos <= Len(output$) lines(lineIndex) = Mid(output$, startPos) EndIf EndIf - + ; Parser chaque ligne For i = 0 To ArraySize(lines()) - line$ = lines(i) + line$ = lines(i) If line$ <> "" - ; Le format est : XY filename + ; Le format est : XY␠ ; X = index status, Y = working tree status If Len(line$) >= 3 - AddElement(main\listFilesGit()) - - - ; Extraire le status (2 premiers caractères) - main\listFilesGit()\status = Left(line$, 2) - main\listFilesGit()\indexStatus = Left(line$, 1) - main\listFilesGit()\workingTreeStatus = Mid(line$, 2, 1) - - ; Extraire le nom du fichier (à partir du 4ème caractère) - main\listFilesGit()\name = Mid(line$, 4) - - ; Obtenir la description du status - main\listFilesGit()\statusDescription = GetStatusDescription(main\listFilesGit()\status) + status$ = Left(line$, 2) + index$ = Left(line$, 1) + worktree$ = Mid(line$, 2, 1) + name$ = Mid(line$, 4) ; chemin tel que renvoyé par Git + name$ = ReplaceString(name$, "\", "/") ; normalisation (par sécurité) + + ; ----- MODIF: chercher si l'entrée existe déjà ----- + found.b = #False + ForEach main\listFilesGit() + If main\listFilesGit()\name = name$ + found = #True + ; Mise à jour uniquement + main\listFilesGit()\status = status$ + main\listFilesGit()\indexStatus = index$ + main\listFilesGit()\workingTreeStatus = worktree$ + main\listFilesGit()\statusDescription = GetStatusDescription(status$) + Break + EndIf + Next + + ; Si non trouvée, on l'ajoute + If Not found + AddElement(main\listFilesGit()) + main\listFilesGit()\name = name$ + main\listFilesGit()\status = status$ + main\listFilesGit()\indexStatus = index$ + main\listFilesGit()\workingTreeStatus = worktree$ + main\listFilesGit()\statusDescription = GetStatusDescription(status$) + EndIf + ; ----- FIN MODIF ----- EndIf EndIf Next - - Debug "Récupération des status Git réussie. " + Str(ListSize(main\listFilesGit())) + " fichiers trouvés." + + Debug "Récupération des status Git réussie. " + Str(ListSize(main\listFilesGit())) + " fichiers (maj/ajout)." ProcedureReturn #True - + Else Debug "Erreur Git ("+Str(code)+") "+main\GitCall\errors ProcedureReturn #False EndIf EndProcedure + ; Récupère le nombre immédiatement avant un mot-clé dans une ligne Procedure.i ExtractNumberBefore(word.s, line.s) Protected pos = FindString(line, word, 1) @@ -1151,6 +1259,10 @@ EndProcedure ;-Suite Procedure AddRemoteRepo(Url.s,name.s="origin") + Url=_SupTrim(Url) + name=_SupTrim(name) + Debug "Url="+Url + Debug "name="+name ;Check if this remote already exists main\Gitcall\args = "remote get-url "+name If RunExe(@main\Gitcall) = 0 @@ -1172,7 +1284,7 @@ EndProcedure Procedure GetRemoteUrl(name.s="origin") main\Gitcall\args = "remote get-url "+name If RunExe(@main\Gitcall) = 0 - SetGadgetText(#GdtFieldRemote,main\GitCall\output) + SetGadgetText(#GdtFieldRemote,_SupTrim(main\GitCall\output)) Else SetGadgetText(#GdtFieldRemote,"") EndIf @@ -1208,6 +1320,111 @@ Procedure DoPull() EndIf EndProcedure +Procedure.s ExtractRepoNameFromUrl(url.s) + ; helper for Doclone + ; Extraire le nom du dépôt depuis l'URL + Protected repoName.s, parts.i, i.i + + ; Supprimer .git à la fin si présent + If Right(url, 4) = ".git" + url = Left(url, Len(url) - 4) + EndIf + + ; Remplacer les \ par des / pour uniformiser + url = ReplaceString(url, "\", "/") + + ; Compter le nombre de parties séparées par / + parts = CountString(url, "/") + + ; Extraire la dernière partie + If parts > 0 + repoName = StringField(url, parts + 1, "/") + Else + repoName = url + EndIf + + ProcedureReturn repoName +EndProcedure + +Procedure.s GetParentPath(path.s) + Protected parentPath.s + + ; Supprimer le séparateur final si présent + If Right(Path, 1) = "\" Or Right(Path, 1) = "/" + Path = Left(Path, Len(Path) - 1) + EndIf + + ; Utiliser GetPathPart() qui retourne le chemin sans le fichier/dossier final + parentPath = GetPathPart(path) + + ProcedureReturn parentPath +EndProcedure + +Procedure DoClone() + Protected remoteUrl.s, repoName.s, targetFolder.s, currentDir.s, choice.i + + remoteUrl = Trim(GetGadgetText(#GdtFieldRemote)) + If remoteUrl = "" + MessageRequester("Git Clone", "Échec: " + #LF$ + "You Must have a remote URL", #PB_MessageRequester_Error) + ProcedureReturn #False + EndIf + + ; Extraire le nom du dépôt depuis l'URL + Debug "remoteUrl="+remoteUrl + repoName = ExtractRepoNameFromUrl(remoteUrl) + Debug "repoName="+repoName + ; Obtenir le chemin complet du répertoire courant + currentDir = GetCurrentDirectory() + + ; Demander à l'utilisateur où cloner + choice = MessageRequester("Git Clone - Destination", + "Où voulez-vous cloner le dépôt ?" + #LF$ + #LF$ + + "OUI: Créer un répertoire '" + repoName + "' dans le répertoire courant" + #LF$ + + " → " + currentDir + repoName + "/" + #LF$ + #LF$ + + "NON: Cloner directement dans le répertoire courant" + #LF$ + + " → " + currentDir + #LF$ + #LF$ + + "ANNULER: Annuler l'opération", + #PB_MessageRequester_YesNoCancel) + + Select choice + Case #PB_MessageRequester_Yes + ; Créer un nouveau répertoire avec le nom du dépôt + targetFolder = repoName + main\Gitcall\args = "clone " + remoteUrl + " " + targetFolder + + Case #PB_MessageRequester_No + ; Cloner directement dans le répertoire courant (doit être vide) + SetCurrentDirectory(GetParentPath(GetGadgetText(#GgtFieldRepo))) + targetFolder = "." + main\Gitcall\args = "clone " + remoteUrl + " " + targetFolder + + Case #PB_MessageRequester_Cancel + ; Annuler l'opération + ProcedureReturn #False + + EndSelect + + ; Exécuter la commande git clone + If RunExe(@main\Gitcall) = 0 + If targetFolder = "." + MessageRequester("Git Clone", "Succès:" + #LF$ + + "Dépôt cloné dans le répertoire courant" + #LF$ + #LF$ + + main\Gitcall\output, #PB_MessageRequester_Info) + Else + MessageRequester("Git Clone", "Succès:" + #LF$ + + "Dépôt cloné dans le répertoire: " + targetFolder + "/" + #LF$ + #LF$ + + main\Gitcall\output, #PB_MessageRequester_Info) + EndIf + AddRemoteRepo(remoteUrl) + SetCurrentDirectory(GetGadgetText(#GgtFieldRepo)) + CreateThread(@RefreshFileList(),0) + ProcedureReturn #True + Else + MessageRequester("Git Clone", "Échec: " + #LF$ + main\Gitcall\errors, #PB_MessageRequester_Error) + ProcedureReturn #False + EndIf +EndProcedure + Procedure DoStatus() Protected *status.GitStatus=@main\GitStatus @@ -1397,7 +1614,7 @@ EndProcedure Procedure.s GetBranchesList() ClearGadgetItems(#GdtSlctBranch) - main\Gitcall\args = "branch" + main\Gitcall\args = "branch -a" If RunExe(@main\Gitcall) <> 0 ProcedureReturn "" EndIf @@ -1586,7 +1803,7 @@ Procedure RefreshFileList(null.i) ClearGadgetItems(#GdtListStatus) ClearList(main\listFilesGit()) ;-Init File List - DoLsFilesByType("C") + FillAllFilesRecursively() ;DoLsFilesByType("D") ;DoLsFilesByType("O") ;DoLsFilesByType("I") @@ -1604,6 +1821,8 @@ Procedure RefreshFileList(null.i) Next DisableGadget(#GdtListStatus,#False) DisableGadget(#GdtBtnRefresh,#False) + GetBranchesList() + GetRemoteUrl() EndProcedure Procedure UpdateHelp(txt.s) @@ -1706,6 +1925,13 @@ Procedure InitGadget() EndProcedure +Macro RightGadget(GDT) + GadgetX(GDT)+GadgetWidth(GDT) +EndMacro + +Macro DownGadget(GDT) + GadgetY(GDT)+GadgetHeight(GDT) +EndMacro Procedure OpenGUI() Protected argCount.l=CountProgramParameters(), w.l,a$ @@ -1731,11 +1957,9 @@ Procedure OpenGUI() EndIf ; --- Dimensions générales --- #WinW = 950 - #WinH = 930 ; plus haut pour la zone d’aide - #PanelX = 15 - #PanelY = 15 + #WinH = 720 ; plus haut pour la zone d’aide #PanelW = 920 - #PanelH = 700 + #PanelH = 500 #InPad = 15 ; marge interne au Panel #FrmGap = 10 ; espace vertical entre frames #RowH = 26 @@ -1743,15 +1967,16 @@ Procedure OpenGUI() If OpenWindow(#WinMain, 0, 0, #WinW, #WinH, "Git helpmate", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) - PanelGadget(#gdtPnl, #PanelX, #PanelY, #PanelW, #PanelH) + PanelGadget(#GdtPnl, #InPad,#InPad, #PanelW, #PanelH) ; ============================================================ ; Onglet 1 : Dépôt ; ============================================================ AddGadgetItem(#gdtPnl, -1, T("#gdtPnl-Repo","Dépôt")) - + Protected PanelH.l=WindowHeight(#WinMain)- 2*#InPad + Protected PanelW.l=900 ; ---- Cadre "Dépôt local" ---- - FrameGadget(#GdtFrmLocal, #InPad, #InPad, #PanelW - 2*#InPad, 100, T("GdtFrmLocal","Dépôt local")) + FrameGadget(#GdtFrmLocal, #InPad, #InPad, PanelW - 2*#InPad, 100, T("GdtFrmLocal","Dépôt local")) TextGadget(#GdtLblRepo, #InPad + 10, #InPad + 30, 60, 22, T("GdtLblRepo","Dépôt :")) StringGadget(#GgtFieldRepo, #InPad + 75, #InPad + 28, #PanelW - 2*#InPad - 75 - 105, #RowH, repoDir$) ButtonGadget(#GdtBtnBrowseRepo, #PanelW - #InPad - 95, #InPad + 28, 95, #RowH, T("GdtBtnBrowseRepo","Parcourir…")) @@ -1856,13 +2081,26 @@ Procedure OpenGUI() ; --- Fin des onglets --- CloseGadgetList() - ; ============================================================ - ; Zone d’aide (plus haute grâce à #PanelH réduit) - ; ============================================================ - Define helpY = #PanelY + #PanelH + 10 - ;FrameGadget(#GdtFrmHelp, #PanelX, helpY, #PanelW, #WinH - helpY - #PanelX, T("GdtFrmHelp","Aide")) - WebViewGadget(#GdtHelp, #PanelX + 10, helpY + 25, #PanelW - 20, #WinH - helpY - #PanelX - 35) + ; ============================================================ +; Aide à droite du Panel (sans splitter) +; ============================================================ +; On garde #PanelW / #PanelH tels quels +Define helpGap = 10 ; espace entre panel et aide +Define helpX = #InPad + #PanelW + helpGap ; à droite du panel +Define helpY = #InPad +Define helpW = WindowWidth(#WinMain)-GadgetWidth(#gdtPnl)- #InPad*4 ; reste de largeur jusqu'à la marge droite +Define helpH = #PanelH ; même hauteur que le panel + +; (Optionnel) borne minimale si besoin +If helpW < 100 : helpW = 100 : EndIf + +; Soit en direct : +WebViewGadget(#GdtHelp, helpX, helpY, helpW, helpH) + +; — ou, si tu veux un cadre : +; FrameGadget(#GdtFrmHelp, helpX, helpY, helpW, helpH, T("GdtFrmHelp","Aide")) +; WebViewGadget(#GdtHelp, helpX + 10, helpY + 25, helpW - 20, helpH - 35) SetGadgetText(#GgtFieldRepo,main\GitCall\workdir) @@ -1901,19 +2139,26 @@ Procedure OpenGUI() Case #GdtBtnRefresh CreateThread(@RefreshFileList(),0) Case #GgtFieldRepo - If EventType()=#PB_EventType_Change + If EventType()=#PB_EventType_LostFocus main\GitCall\workdir=GetGadgetText(#GgtFieldRepo) + CreateThread(@RefreshFileList(),0) EndIf Case #GdtBtnBrowseRepo Protected path.s=PathRequester("Select Folder",main\GitCall\workdir,WindowID(#WinMain)) If path<>"" And FileSize(path)=-2 main\GitCall\workdir=path + SetCurrentDirectory(main\GitCall\workdir) + SetGadgetText(#GgtFieldRepo,main\GitCall\workdir) + CreateThread(@RefreshFileList(),0) EndIf Case #GdtFieldRemote ;If EventType()=#PB_EventType_LostFocus ; AddRemoteRepo(GetGadgetText(#GdtFieldRemote),"origin") ; EndIf + + Case #GdtBtnClone + DoClone() Case #GdtBtnPush DoPush() Case #GdtBtnPull @@ -1957,9 +2202,9 @@ OpenGUI() ; IDE Options = PureBasic 6.21 (Windows - x64) -; CursorPosition = 1947 -; FirstLine = 1906 -; Folding = -------- +; CursorPosition = 541 +; FirstLine = 509 +; Folding = ---------- ; Optimizer ; EnableThread ; EnableXP