Salut les mecs,

Tout d'abord, pour en profiter pour répondre aux contributeur suivant ma dernière demande d'aide, je dois dire qu'après avoir appliqué la suggestion d'Abdel, l'affichage de l'erreur a disparue.

Cependant, je n'ai pas retiré la ligne de décodage juste après le popen.

Ce qui à mon avis élimine les autres types d'erreurs d'encodage qui ont fait la crainte de Quentin.

Maintenant ma proposition du jour.

Certes j'en suis toujours à la phase de complétion et d'expérimentation des fonctionnalités que je vous ai annoncées ces dernières semaines, mais je juge que je peux à présent vous en donner un aperçu.
Attention, je dis bien un aperçu car, c'est pas encore fini.

En pièce jointe, je vous ai mis l'extension temporaire dans laquel j'ai travaillé tools.py et deux fichiers annexes qui doivent être mis dans le même dossier.

Je demanderais pour commencer à Abdel de m'excuser car pour des raisons pratique j'ai gardé le listage des versions de python dans cette extension temporaire. Ce qui fait qu'il y aura doublons. On va dire qu'on va tolérer cela uniquement pour les tests. Cela se trouve toujours dans le menu outils et est nommé "Versions de python". Ainsi, si vous voulez que certaines des fonctionnalités décrites ci-dessous marchent, vous devrez cocher un élément de ce sous-menu. D'ailleurs si de quelconques menu il y a, vous les retrouverez dans le menu outils.

A présent, rappel des nouvelles fonctionnalités proposées:

1. l'ajout et le retrait de balises de fin de bloc au code correctement indenté. Idéal pour lire du code python plus aisément. Dans le menu outils il sera créé un élément avec sous-menus nommé "balises de fin de block, et les ajouts et retraits sont deux éléments de ce sous-menu.

2. l'ajustement des indentations à partir de balises de fin de blocs.
Est également présent dans "outils", "balises de fin de bloc".

Attention, je dois encore perfectionner cette fonctionnalité.
Il y a notamment les cas des continuation d'instructions à la ligne suivante, et certains cas de relance de l'indentation par des else précédés de elif et autres subtilités de ce genre à prendre en compte.

Quoi que pour des codes simples/classiques, ça fonctionne plutôt bien.

3. la vérification de la syntaxe d'une ligne ayant été modifiée juste après une tentative de déplacement à une autre ligne.
Il sera notamment affiché un message d'avertissement :
* si il y a un déséquilibre des parenthèses, guillemets, accolades, crochets, etc...
* si le deux points obligatoire est manquant sur une ligne le requerant;
* si deux types de caractères d'indentation sont présents en début de ligne;

La ligne sera par ailleurs automatiquement et imperceptiblement rectifiée dans les cas suivants:
* si  elle se termine par des espaces ou des tabulations (par leur retrait)

Je suis toujours en train de réfléchir comment vérifier et synchroniser l'écriture de mots clés et variables par la même fonctionnalité.
C'est plus complexe que je ne l'avais envisagé au départ.

Le but ici est d'avertir ou de corriger immédiatement des erreurs d'inattention. Ce qui constitue un gain en temps considérable, au moins dores et déjà dans mes développements à moi-même.


4. La complétion de code ou intellicence.
Par la combinaison ctrl+j, une liste déroulante apparaîtra pour vous proposer une liste de mots clé, Cela qu'une expression ou non soit détectée sous le curseur.

Par mots clés, j’entend les nom de variables, fonctions, classes, objets, modules, et mots réservés du langage python.

Si aucune expression n'est détectée sous le curseur, c'est une liste de mots clés généraux qui seront proposés, prenant en compte les imports et déclarations qui auront été réalisés jusqu'à cet emplacement du code.

Si une expression sans point est détectée sous le curseur, la liste des mots clés généraux est proposée et vous êtes positionnés sur celle qui commence de la même façon que le mot sous le curseur.

Si une expression avec des points est détectée sous le curseur, il vous sera proposé une liste de sous objets et mots clés possibles à insérer juste après le dernier point.

Si la fonctionnalité détecte que vous êtes juste après le mot clé "import", ce seront toutes les bibliothèques disponible dans l'interpréteur que vous aurez coché qui seront proposés.


Attention, ici j'ai fait de gros effort d'optimisation, mais dans les cas où on a beaucoup de bibliothèques installées ou beaucoup de sous-mots clés à un objet, ça peut encore prendre un peu de temps avant d'afficher la liste de propositions. Certaines listes ont plus de 3000 éléments, qu'il faut trier sans respect de la cass avant.

5. L'aide au mot clé sous le curseur.
Elle a  la même base que la complétion de code.
Mais au lieu d'afficher une liste de mots clés insérables, elle ouvre la console pour vous afficher l'aide pour le mot clé sous le curseur.
Le raccourci clavier est ctrl+I.

Donc cela évite de passer par la console pour taper des help. Vous pouvez simplement parcourir un code, vous placer sur un mot clé et faire ctrl+i.

Au cas où le mot clé n'a pas d'aide,  vous entendrez un beep d'échec.

L'astuce ici a été de retrouver toutes les déclarations liées à l'élément sous le curseur, avant de tenter une exécution pour capter le texte d'aide en background.

Mais cependant Ici aussi, il y a encore des cas que je dois prendre en compte. Notamment certaines déclarations implicites faite dans les boucles for, certaines déclarations multiples sur une même ligne, ou les déclarations entre les parenthèses de paramètres à une fonction.

Voilà !

Tout ça n'a fait que 1850 lignes de codes.

Bon allez, avec un bon nombre de lignes de commentaires comme balises de fin de bloc... que vous pourrez d'ailleurs enlever d'un click grace à une fonctionnalité de qui, de qui ????
Hé oui, hé oui !

Au delà de ça, je continue d'avoir d'autres idées en réserve, mais me mets un point d'honneur à terminer celle que j'ai entamer.

Il y a par exemple:
* L'augmentation ou la réduction de la sélection au blocs parent ou enfant; * le chargement de fichiers chm présent dans les dossier doc de toutes les version de python et leur affichage comme sous élément du menu aide lors du chargement des versions de python; * la refonte du collage de texte dans l'éditeur pour une meilleure prise en compte des indentations;
* l'interdiction du caractère tabulation si on n'est pas en début de ligne;
* l'interdiction d'une indentation supérieure à un niveau par rapport à la ligne précédente; * un déplacement uniquement de classes en classes par CTRL+F2 et CTRL+SHIFT+F2 parce que cela m'est paru évident en ouvrant des fichiers python volumineux dans lesquels de nombreuses classes avaient été mises; * un élément de menu pour la création d'un exécutable via le py2exe (un rappel).

Je m'arrête là.

J'attends vos retours avisés.

Amicalement,

Yannick Daniel Youalé
La programmation est une religion. Aimez-la ou quittez-la.
www.visuweb.net






# -*- coding: utf-8 -*-
# extension 6pad++ pour la création d'outils de développement en langage python
# en attendant d'être transféré dans le module forPython
# développé par Yannick Daniel Youalé
# [email protected]

import os
import re
import sys
import time
import subprocess
import inspect
import pkgutil
import sixpad

# raccourci
sp=sixpad
alert=sp.window.alert
# chemin vers l'exécutable de la version du python choisi
pythonVersion = ""
# la liste des versions de python sur cet ordi
pythonList=[]
# pour déterminer si changement de ligne
idTmrLineMove=0
iLastLine=0
sLastLine=""
flagCheckLineMove=True
# pour contrôler le processus en cours d'exécution
curProc = object()

def decode2(s):
        # décodage alternatif de texte renvoyé
        s=str(s)
        s=re.sub("^[ \\t]*[>]+[ \\t]+[a-z*]('|\")([\\w\\W]+)('|\")[ \\t]*$", 
"\\2", s, re.I)
        s=re.sub(r"\\r\\n", "\r\n", s)
        return s
# end def

def finditer2List(pt, s, flags=0):
        # recherche par regexp renvoyant un objet liste d'objets match
        lst=[]
        found=re.finditer(pt, s, flags)
        for e in found: lst.append(e)
        return lst
# end def

def findall2List(pt, s, flags=0):
        # recherche par regexp renvoyant un objet liste de string trouvés
        lst=[]
        found=re.findall(pt, s, flags)
        for e in found: lst.append(e)
        return lst
# end def

def getCurReturnCharacter():
        # renvoi le caractère de retour à la ligne courant
        lst=["\r\n", "\n", "\r"]
        return lst[sp.window.curPage.lineEnding]        
# end def

def getCurExpression():
        # renvoi l'expression sous le curseur
        # ainsi que ses positions de début et de fin
        s = sp.window.curPage.text
        if s == "": return "", 0, 0
        # recueillement de la position du curseur
        l = sp.window.curPage.position
        iStart=l
        iEnd=l
        flag=False
        # recherche vers la droite
        expression = ""
        i=l
        while(i < len(s)):
                sChar = s[i]
                if re.match("[a-zA-Z_\\d]", sChar, re.I):
                        expression = expression+sChar
                        iEnd=i
                        flag=True
                else:
                        break
                # end if
                i=i+1 # incrémentation
        # end while
        # recherche vers la gauche
        # on incluera les points dans les caractères recherchés
        # ainsi que les englobants de crochets, accolades,  et parenthèses
        i=l-1
        while(i >= 0):
                sChar = s[i]
                if re.match("[a-zA-Z_\\d\\.]", sChar, re.I):
                        expression = sChar+expression
                        iStart=i
                        if flag==False:
                                iEnd=iStart
                                flag=True
                        # end if
                elif (sChar==")" or sChar=="]" or sChar=="}") and s[i+1]==".":
                        if sChar==")": limit="("
                        if sChar=="]": limit="["
                        if sChar=="}": limit="{"
                        start=sChar
                        balance=1
                        quote=""
                        inQuote=False
                        # on inclus le caractère
                        expression = sChar+expression
                        iStart=i
                        # on va également inclure le texte à gauche jusqu'à la 
limite
                        i=i-1
                        while(i>=0):
                                sChar = s[i]
                                if inQuote==True:
                                        if sChar==quote:
                                                if s[i-1]!="\\": inQuote=False
                                        # end if
                                else: # pas dans des quotes
                                        if sChar=="'" or sChar=='"':
                                                quote=sChar
                                                inQuote=True
                                        elif sChar==start:
                                                balance=balance+1
                                        elif sChar==limit:
                                                balance=balance-1
                                                if balance==0:
                                                        iStart=i
                                                        break
                                                # end if
                                        # end if
                                # end if inQuote
                                expression = sChar+expression
                                iStart=i
                                i=i-1
                        # end while
                else:
                        break
                # end if
                iStart=i
                i=i-1 # décrémentation
        # end while 
        # if iEnd>iStart: iEnd=iEnd+1
        return expression, iStart, iEnd
# end def

def getCurModuleDirPath():
        # renvoi le chemin vers le dossier contenant le script courant
        path=inspect.getfile(inspect.currentframe())
        return os.path.dirname(path)
# end def-

def getModuleRefByPath(path):
        # renvoi le module de 6pad à partir de son chemin
        s=sp.appdir
        l = path.split("\\")
        if path.find(os.path.join(s, "lib"))>=0:
                s2=os.path.join(s, "lib")
                l2=s2.split("\\")
                l=l[len(l2):]
        elif path.find(os.path.join(s, "plugins"))>=0:
                s2=os.path.join(s, "plugins")
                l2=s2.split("\\")
                l=l[len(l2):]
        else:
                return ""
        # end if
        path=".".join(l)
        l=path.split(".")
        del l[-1]
        path=".".join(l)
        return path
# end def

def getStringAndCommentPos(s):
        # renvoi les limites des chaînes string et commentaires déclarées dans 
le texte en paramètre.
        # suppose que la syntaxe du texte est correcte.
        lst=[]
        k=-1
        prev=""
        quote=""
        inQuote=False
        litteral=False
        d, f=0, 0
        i=0
        n=len(s)
        while(i<n):
                e=s[i]
                if inQuote==False:
                        if e=="'" or e=='"':
                                quote=e
                                inQuote=True
                                if i>0 and s[i-1]=="r": literal=True
                                else: literral=False
                                d=i
                        elif e=="#": # début de commentaire
                                # on prend jusqu'à la fin de la ligne ou du 
texte
                                d=i
                                for k in range(i, n):
                                        if s[k]=="\r" or s[k]=="\n":
                                                k=k-1
                                                break
                                        # end if
                                # end for
                                i=k # avancement
                                f=k
                                lst.append((d, f))
                        # end if
                else: # inQuote==True
                        if e==quote:
                                if prev=="\\" and litteral==True:
                                        prev=e
                                        i=i+1
                                        continue
                                elif prev=="\\" and litteral==False:
                                        k=i-1
                                        s2=""
                                        while(k>=0):
                                                if s[k]=="\\": s2=s2+"\\"
                                                else: break
                                                k=k-1
                                        # end while
                                        # ici, si le nombre d'antislashes 
comptés est un multiple de 2
                                        if len(s2) % 2==0:
                                                inQuote=False
                                                quote=""
                                                f=i
                                                lst.append((d, f))
                                        # end if
                                else: # simple fermeture de guillemets
                                        inQuote=False
                                        quote=""
                                        f=i
                                        lst.append((d, f))
                                # end if fin test plusieurs possibilités pour 
le quote
                        # end if fin si quote
                # end if fin si inQuote ou pas
                prev=e
                i=i+1
        # end while
        return lst
# end def

def getTripleQuotePos(s):
        #  renvoi les positions de tous les triples quotes dans un objet liste
        l=[]
        lst=[]
        tripleQuote=""
        flag=True
        polarity=True
        flagBalance=True
        # recherche de toutes les lignes qui ont au moins un triple quotes
        found=finditer2List("([\\r\\n]+)[^\\r\\n]*?('''|\"\"\")[^\\r\\n]*", s)
        # parcours
        i=0
        n=len(found)
        while(i<n):
                e=found[i]
                sLine=e.group(0)
                start=e.start(0)
                flag, polarity, tripleQuote, l = getTripleQuoteInfos(sLine)
                # si au moins un triple quotes valide
                if flag==True:
                        # on ajuste les valeurs de la sous-liste des positions
                        o=-1
                        for (d, f) in l:
                                o=o+1
                                d=start+d
                                if f>-1: f=start+f
                                l[o]=(d, f)
                        # end for
                        # on ajoute la sous liste des positions à la liste 
principale
                        lst=lst+l
                        # si tous les triples quotes ne se referment pas sur la 
même ligne
                        # ca veut dire qu'on doit rechercher la position de fin 
du dernier triple quotes commencé sur cette ligne
                        if polarity==True:
                                flagBalance=False
                                j=i+1
                                while(j<n):
                                        sLine2=found[j].group(0)
                                        start=found[j].start(0)
                                        if sLine2.find(tripleQuote)>=0:
                                                d, f=lst[len(lst)-1]
                                                
f=start+sLine2.find(tripleQuote)+3
                                                lst[len(lst)-1] = (d, f)
                                                flagBalance=True
                                                i=j # avancement 
                                                break
                                        # end if
                                        j=j+1
                                # end while
                        # end if
                # end if
                i=i+1
        # end while
        # si il manque une fermeture de triple quote
        if flagBalance==False:
                # on va marquer la fin du texte comme position de fin
                d, f=lst[n-1]
                f=len(s)-1
                lst[n-1]=(d, f)
        # end if
        # renvoi de la liste des positions recencées
        return lst
# end def

def getTripleQuoteInfos(sLine):
        #  renvoi des infos sur les triples quotes dans cette ligne
        # où commencent et où s'arrêtent-elles ?
        sLine="  "+sLine+"  "
        flag=False
        flagQuote=False
        flagTripleQuote=False
        flagLitteral=False
        quote=""
        tripleQuote=""
        i=2
        lst=[]
        d, f= 0, 0
        # parcours
        while i<len(sLine):
                e=sLine[i]
                if flagQuote==False:
                        if e=="'" or e=='"':
                                if sLine[i+1]==e and sLine[i+2]!=e:
                                        i=i+2
                                        continue
                                elif sLine[i+1]==e and sLine[i+2]==e:
                                        tripleQuote=e
                                        if flagTripleQuote==False: 
                                                flagTripleQuote=True
                                                flag=True
                                                d=i-2
                                        elif flagTripleQuote==True: 
                                                flagTripleQuote=False
                                                f=i
                                                # ajout des limites à la liste 
des triples quotes repérés
                                                lst.append((d, f))
                                        # end if
                                else:
                                        flagQuote=True
                                        quote=e
                                        flagLitteral=(sLine[i-1]=="r")
                                # end if                                
                        # end if
                else: # flagQuote==True
                        if e==quote:
                                if flagLitteral==False:
                                        if sLine[i-1]=="\\" and 
sLine[i-2]!="\\":
                                                i=i+1
                                                continue
                                        else:
                                                flagQuote=False
                                                quote=""
                                        # end if
                                else: # flagLitteral==True
                                        if sLine[i-1]=="\\":
                                                i=i+1
                                                continue
                                        else:
                                                flagQuote=False
                                                quote=""
                                        # end if                                
        
                                # end if
                        # end if
                # end if                
                i=i+1
        # end while
        # on force la fermeture même si polarité positive
        if flagTripleQuote==True:
                f=-1
                lst.append((d, f))
        else:
                tripleQuote=""
        # end if
        # on retourne: 
        # si True ou False des triples quotes ont été trouvés
        # la polarité de flagTripleQuote, 
        # le dernier type de triples quotes non refermé
        # le tableau des positions de triples quotes
        return flag, flagTripleQuote, tripleQuote+tripleQuote+tripleQuote, lst
# end def

def getBlockLimits(s):
        # retrouve les lignes de début et fin des blocks principaux
        # recueillement de toutes les lignes non vides
        lines=findall2List("[^\\r\\n]+", s, re.M) 
        # on ajoute deux lignes vides à indentation 0
        lines.append("")
        lines.append("")
        lst=[]
        key=""
        curKey=""
        d=0
        f=0
        # parcours
        i=-1
        for e in lines:
                i=i+1
                pt="^([ \\t]*)(class|def)[^a-zA-Z\\d_]"
                if re.match(pt, e):
                        if isDirectLine(e)==True: # cas improbable, mais à 
prendre en compte
                                continue
                        # end if
                        found=finditer2List(pt, e)
                        curIndent=found[0].group(1)
                        curLevel=len(curIndent)
                        curKey=found[0].group(2)
                        d=i
                        # recherche de la fin du block
                        tripleQuote=""
                        inTripleQuote=False
                        for j in range(i+1, len(lines)):
                                sLine=lines[j]
                                if inTripleQuote==True: # des triples 
guillemets ont été préalablement ouvert
                                        sp.say("oui")
                                        if sLine.count(tripleQuote)>=1:
                                                inTripleQuote=False # fermeture
                                                tripleQuote=""
                                                continue
                                        else: # ce n'est pas une ligne de 
fermeture des triples guillemets
                                                continue
                                        # end if
                                else: # pas dans les triples quotes
                                        if sLine.count("'''")>=1 or 
sLine.count('"""')>=1:
                                                inTripleQuote, polarity, 
tripleQuote, lstTripleQuote=getTripleQuoteInfos(sLine)
                                # end if
                                level=len(findall2List("^[ \\t]*", lines[j])[0])
                                if level<=curLevel:
                                        # fin du block
                                        f=j-1
                                        lst.append((curKey, d, f))
                                        curKey="" # réinitialisation
                                        break
                                # end if
                        # end for
                        if curKey!="": lines.append(curIndent+"# end "+curKey)
                # end if
        # end for
        if curKey!="":
                f=j
                lst.append(curKey, d, f)
        # end if
        return lst
# end def

def getOnlyRelatedCode(s):
        # Restriction et renvoi du code uniquement relatif à la dernière ligne
        #
        # élimination des commentaires uniligne
        s = re.sub("[\\r\\n]+[ \\t]*#[^\\r\\n]*", "", s)
        # remplacement des parties de triples quotes
        lst=getTripleQuotePos(s)
        if len(lst)>0:
                s2=""
                i=0
                for (d, f) in lst:
                        s2=s2+s[i: d-1]+"'''comment'''"
                        i=f+1
                # end for
                s=s2+s[i:]
        # end if
        rt=getCurReturnCharacter() # le type de retours à la ligne courant
        # retrait de toutes les lignes non vides
        lines=findall2List("[^\\r\\n]+", s)
        n=len(lines)-1 # la ligne limite
        s=rt.join(lines)
        # recherche des positions de lignes de classes et fonctions
        found=getBlockLimits(s)
        if len(found)>0: # il y a des classes et def 
                # on ne va garder  que les infos des blocks qui n'incluent pas 
la ligne courante
                i=len(found)-1
                while(i>=0):
                        key, d, f = found[i]
                        if n>=d and n<=f:
                                del found[i]
                        # end if
                        i=i-1
                # end while
                if len(found)==0: found.append(("key", -1, -1))
                # on va rassembler les lignes reliées
                # en éliminant celles qui ne le sont pas
                i=0
                lst=[]
                while(i<=n):
                        flag=True
                        for (key, d, f) in found:
                                if i>=d and i<=f:
                                        flag=False
                                # end if
                        # end for
                        if flag==True:
                                lst.append(lines[i])
                        # end if
                        i=i+1
                # end while
                # reconstitution
                return rt.join(lst)
        else: # pas de classe et def avant
                # on renvoi simplement le texte depuis le début
                return s
        # end if
# end def

def getNierestExpression(lst, expression):
        # renvoi la position de l'expression la plus proche contenue dans la 
liste ordonnée de manière croissante
        if expression=="": return 0
        #  élimination de la cass
        lst2=[]
        for e in lst:
                lst2.append(e.lower())
        # end for
        expression=expression.lower()
        s=""
        pos=0
        selec=0
        flag=False
        # pour chaque lettres de l'expression
        n=len(expression)
        for i in range(0, n):
                s=expression[0: i+1].lower()
                for j in range(pos, len(lst2)):
                        if lst2[j].startswith(s):
                                selec=j
                                if i>=n: return selec
                                pos=j
                                break
                        else:
                                if i>=n: return selec
                        # end if
                # end for
        # end for
        return selec
# end def

def removeTags(s=""):
        # retire les commentaires de fin de block à un texte
        return re.sub("[\\r\\n]+[ \\t]*#[ \\t]*end[ 
\\t]*(class|def|while|for|if|try|with)[^\\r\\n]*", "", s)
# end def

def removeTagsInCurPage():
        # sur la page courante
        sp.window.curPage.text=removeTags(sp.window.curPage.text)
# end def

def addTags(s=""):
        # ajoute des balises de fin de block
        s=removeTags(s) # préalable
        # détermination du type de retour à la ligne
        rt=getCurReturnCharacter()
        # recueillement de toutes les lignes non vides
        lines=findall2List("[^\\r\\n]+", s, re.M) 
        lines.append("")
        lines.append("")
        # recueillement de tous les triples quotes
        lstTripleQuote=getTripleQuotePos(s)
        tripleQuote=""
        lst=[]
        # initialisation de variables
        polarity=False
        pt1="class|def|if|for|while|try|with"
        pt2="elif|else|except|finally"
        # parcours ligne par ligne
        i=0
        n=len(lines)
        while(i<n):
                e=lines[i]
                # si ligne d'influence de l'indentation pour les lignes 
suivantes
                pt="^([ \\t]*)("+pt1+")[^a-zA-Z\\d_]"
                if re.match(pt, e, re.I):
                        # recueillement de certaines infos sur cette ligne
                        found=finditer2List(pt, e, re.I)
                        curIndent=found[0].group(1)
                        curLevel=len(curIndent)
                        curKey=found[0].group(2)
                        # si ligne d'exécution d'instruction après le deux 
points
                        if isDirectLine(e)==True:
                                # on va rechercher une éventuelle ligne de 
relance qui la suivraient directement
                                flag=False
                                pt="^([ \\t]*)("+pt2+")[^a-zA-Z\\d_]"
                                for k in range(i+1, n):
                                        if re.match(pt, lines[k], re.I):
                                                found2=finditer2List(pt, 
lines[k], re.I)
                                                indent=found2[0].group(1)
                                                level=len(indent)
                                                if curLevel!=level: break
                                                if 
isDirectLine(lines[k])==False:
                                                        flag=True # ligne 
d'influence de l'indentation trouvée
                                                        break
                                                # end if
                                        else: # ce n'est pas une ligne de 
relance
                                                break
                                        # end if
                                # end for
                                if flag==False: # pas de ligne de relance 
trouvée
                                        i=i+1
                                        continue
                                # end if
                        # end if
                        # continuation du traitement de la ligne d'augmentation 
de l'indentation
                        suplement="" # pour les cas particulier des class et def
                        if curKey=="def" or curKey=="class": 
supplement=rt+curIndent # pour créer des lignes de séparation
                        else: supplement=""
                        # recherche de la fin du block pour y positionner la 
balise
                        tripleQuote=""
                        inTripleQuote=False
                        for j in range((i+1), (len(lines))):
                                sLine=lines[j]
                                if inTripleQuote==True: # des triples 
guillemets ont été préalablement ouvert
                                        if sLine.count(tripleQuote)>=1:
                                                inTripleQuote=False # fermeture
                                                tripleQuote=""
                                        # end if
                                        j=j+1
                                        continue
                                else: # inTripleQuote==False
                                        # si dans la ligne il y a au moins un 
triple quote
                                        if sLine.count("'''")>0 or 
sLine.count("\"\"\"")>0:
                                                inTripleQuote, polarity, 
tripleQuote, lst=getTripleQuoteInfos(sLine)
                                                # si ouverture de triple quote 
sur la ligne
                                                if polarity==True:
                                                        # si  la ligne commence 
immédiatement par un triple quotes
                                                        if re.match("^[ 
\\t]*(\"\"\"|''')", sLine):
                                                                j=j+1
                                                                continue
                                                        # end if
                                                # end if
                                        # end if
                                # end if
                                level=len(findall2List("^[ \\t]*", lines[j])[0])
                                if level<=curLevel:
                                        # il ne faut pas que ce soit une ligne 
de relance
                                        # et qu'elle soit de même niveau
                                        pt="^([ \\t]*)("+pt2+")[^a-zA-Z\\d_]"
                                        if re.match(pt, sLine, re.I) and 
curLevel==level:
                                                j=j+1
                                                continue
                                        # end if
                                        # insertion de la ligne commentaire 
marquant la fin du block
                                        lines.insert(j, curIndent+"# end 
"+curKey+supplement)
                                        curKey="" # réinitialisation
                                        n=n+1
                                        break
                                # end if
                        # end for
                        if curKey!="":
                                lines.append(curIndent+"# end 
"+curKey+supplement)
                        # end if
                # end if
                i=i+1
        # end while
        del lines[len(lines)-1]
        del lines[len(lines)-1]
        s=rt.join(lines)        
        return s
# end def

def addTagsInCurPage():
        # sur la page courante
        sp.window.curPage.text=addTags(sp.window.curPage.text)  
# end def

def adjustIndentsByTags(s, selection=False, iStart=0, iEnd=0):
        # ajuste les indentation à partir des balises commentaires de fin de 
block
        if selection==True:
                level=len(findall2List("^[ \\t]*", s)[0])
        # end if
        # retrait de toutes les indentations
        s= re.sub("([\\r\\n]+)[ \\t]+", "\\1", s)
        # reécriture des balises de fin de block, pour notamment éviter les cas 
où on aurait oublié le #
        s=re.sub("(\\r\\n]+)[#]*[ \\t]*end[ 
\\t]*(class|def|if|for|try|while|with)", "\\1# end \\2", s, re.I)
        # repérage du type de retours à la ligne
        rt=getCurReturnCharacter()
        lst2=[]
        # ensuite, décomposition des lignes dans une liste
        lines=s.split(rt)
        lst=[]
        pt1="class|def|if|for|while|try|with"
        pt2="elif|else|except|finally"
        flagRebounce=False
        # parcours des lignes de la fin vers le début
        if selection==False: level=0
        
        n=len(lines)
        i=n-1
        while(i>=0):
                sLine=lines[i]
                # vérification
                if re.match("^#[ \\t]*end[ \\t]*("+pt1+")", sLine):
                        # xxx c'est un commentaire de fin de block
                        flagRebounce=False
                        indent="\t" * level
                        lines[i]=indent+lines[i]
                        key=finditer2List("^[ \\t]*#[ \\t]*end[ 
\\t]*("+pt1+")", sLine)[0].group(1)
                        lst.append(key)
                        lst2.append(i)
                        level=level+1
                elif re.match("^("+pt1+")[^a-zA-Z_\\d]", sLine):
                        # xxx c'est une instruction de début de block
                        # si n'est pas une ligne d'exécution directe après le 
deux points
                        flag=isDirectLine(sLine)
                        if flag==False: # la ligne n'exécute rien après le deux 
points
                                flagRebounce=False
                                # on vérifie si correspond à la fermeture
                                key1=finditer2List("^("+pt1+")[^a-zA-Z_\\d]", 
sLine)[0].group(1)
                                key2=""
                                if len(lst)>0: key2=lst[len(lst)-1]
                                if key1.lower()==key2.lower():
                                        level=level-1
                                        del lst[len(lst)-1]
                                        del lst2[len(lst2)-1]
                                        indent="\t" * level
                                        lines[i]=indent+lines[i]
                                else: # les mots clés ne correspondent pas
                                        if selection==True: 
i=i+sp.window.curPage.curLine-1
                                        sp.window.alert("A la ligne 
"+str(i+1)+"\r\n"+sLine, "'"+key1+"' sans 'end "+key1+"'")
                                        return
                                # end if
                        else: # c'est une ligne d'exécution directe 
d'instruction après le deux points
                                if flagRebounce==True:
                                        flagRebounce=False
                                        # vérification que les mots clés 
correspondent
                                        
key1=finditer2List("^("+pt1+")[^a-zA-Z_\\d]", sLine)[0].group(1)
                                        key2=""
                                        if len(lst)>0: key2=lst[len(lst)-1]
                                        if key1.lower()==key2.lower():
                                                level=level-1
                                                del lst[len(lst)-1]
                                                del lst2[len(lst2)-1]
                                                indent="\t" * level
                                                lines[i]=indent+lines[i]
                                        # end if
                                # end if
                                indent="\t" * level
                                lines[i]=indent+lines[i]
                        # end if
                elif re.match("^("+pt2+")[^a-zA-Z_\\d]", sLine):
                        # xxx c'est une ligne de relance d'indentation
                        if isDirectLine(sLine)==False: # pas une ligne 
d'exécution directe
                                # on diminue le niveau d'indentation uniquement 
pour cette ligne
                                indent="\t" * (level-1)
                                lines[i]=indent+lines[i]
                                flagRebounce=True
                        else: # c'est une ligne d'exécution directe
                                if flagRebounce==True: level=level-1 # modif 
temporaire
                                indent="\t" * level
                                lines[i]=indent+lines[i]
                                if flagRebounce==True: level=level+1 # 
restauration
                                i=i
                        # end if
                else: # xxx c'est une ligne quelconque
                        indent="\t" * level
                        lines[i]=indent+lines[i]
                        flagRebounce=False
                # end if
                i=i-1
        # end while
        if len(lst)>0:
                key=lst[len(lst)-1]
                i=lst2[len(lst)-1]
                if selection==True: i=i+sp.window.curPage.curLine-1
                sp.window.alert("A la ligne "+str(i+1)+"\r\n"+lines[i], "'end 
"+key+"' sans '"+key+"'")
                return
        # end if
        s=rt.join(lines)
        if selection==False:
                sp.window.curPage.text=s
        else:
                sp.window.curPage.replace(iStart, iEnd, s)
        # end if
# end def

def adjustIndentsByTags2(s, selection=False):
        # ajuste les indentation à partir des balises commentaires de fin de 
block
        #
        # élimination de toutes les indentations
        s=re.sub("([\\r\\n]+)[ \\t]+", "", s)
        # reécriture des balises de block pour meilleure conformité
        s=re.sub("^[#]*[ \\t]*end[ \\t]*("+pt1+")", "# end \\1", s, re.I+re.M)
        # décomposition des lignes dans une liste
        rt=getCurReturnCharacter()
        lines=s.split(rt)
        # des variables à utiliser
        flagRebounce=False
        lstKey=[]
        level=0
        inTripleQuote=False
        pt1="class|def|if|for|while|try|with"
        pt2="elif|else|except|finally"
        tripleQuote=""
        i=-1
        n=len(lines)
        # parcours ligne par ligne
        for e in lines:
                i=i+1
                if inTripleQuote==False:
                        if re.match("^[ \\t]*("+pt1+")[^a-zA-Z\\d_]", e, re.I):
                                # xxx c'est une ligne d'initialisation 
d'indentation pour les lignes suivantes
                                indent="\t" * level
                                lines[i]=indent+lines[i]
                                if isDirectLine(e)==False:
                                        level=level+1
                                        flagRebounce=False
                                else: # c'est une ligne d'exécution directe
                                        flagRebounce=True
                                # end if
                        elif re.match("^("+pt2+")[^a-zA-Z\\d_]", e, re.I):
                                #  xxx c'est une ligne de relance de 
l'indentation
                                if flagRebounce==False:
                                        indent="\t" * (level-1)
                                else: # flagRebounce==True
                                        indent= "\t" * level
                                # end if
                                lines[i]=indent+lines[i]
                                if isDirectLine(e)==False:
                                        flagRebounce=False
                                        level=level+1
                                else: # c'est une ligne d'exécution directe
                                        flagRebounce=True
                                # end if
                        elif re.match("^[#][ \\t]*end[ \\t]*("+pt1+")", e, 
re.I):
                                # xxx c'est une balise de fin de block
                                # vérification que la clée correspond
                                key=finditer2List("[#][ \\t]*end[ 
\\t]*("+pt1+")", e, re.I)[0].group(1)
                                if key==lstKey[len(lstKey)-1]:
                                        level=level-1
                                        indent="\t" * level
                                        lines[i]=indent+lines[i]
                                        del lstKey[len(lstKey)-1]
                                else: # non correspondance de clées
                                        if selection==True: 
i=i+sp.window.curPage.curLine-1
                                        sp.window.alert("'end "+key+"' sans 
'"+key+"' à la ligne "+str(i+1))
                                        return
                                # end if
                                flagRebounce=False
                        else: # xxx c'est une ligne quelconque
                                indent= "\t" * level
                                lines[i]=indent+lines[i]
                                flagRebounce=False
                        # end if
                else: # inTripleQuote==True
                        i=i
                # end if
        # end for
        s=rt.join(lines)
        sp.window.curPage.text = s
# end def

def AdjustIndentsByTagsInCurPage():
        # sur la page courante
        if len(sp.window.curPage.selectedText)>0:
                page=sp.window.curPage
                s=page.text
                
iStart=page.lineStartOffset(page.lineOfOffset(page.selectionStart))
                iEnd=page.lineEndOffset(page.lineOfOffset(page.selectionEnd))
                s=s[iStart: iEnd]
                adjustIndentsByTags(s, True, iStart, iEnd)
        else:
                adjustIndentsByTags(sp.window.curPage.text)
        # end if
# end def

def curPosHelp():
        # affiche de l'aide pour le mot clé sous le curseur
        global pythonVersion
        # vérification qu'une version du python a bien été choisie
        if pythonVersion=="":
                sp.window.alert("Vous n'avez choisi aucune version de python 
installée sur votre ordinateur.\nVeuillez le faire par le menu 'outils' et 
retenter cette action.", "Erreur- version de python")
                return
        # end if
        sPythonPath=pythonVersion
        sPythonScript=sp.window.curPage.file
        sPythonScript=os.path.dirname(sPythonScript)
        if os.path.isdir(sPythonScript)==False: 
sPythonScript=getCurModuleDirPath()
        sPythonScript=sPythonScript+"\\pe_tmp.py"
        # recueillement du texte du document
        s = sp.window.curPage.text
        # quelques initialisations
        flagPoint=False
        flag6pad=False
        iStart=sp.window.curPage.position
        iEnd=iStart
        curKey=""
        lst=[]
        lstVars=[]
        lstImports=[]
        lstClassDef=[]
        # détermination si l'interpréteur choisi est celui du 6pad
        if pythonVersion.find("6pad++.exe")>=0: flag6pad=True
        # identification d'une éventuelle expression sous le curseur
        expression, iStart, iEnd=getCurExpression()
        if flagPoint==True or flagPoint==False:
                # on circonscrit le code relatif à la ligne sous le curseur
                sCode=getOnlyRelatedCode(s[0: 
sp.window.curPage.lineStartOffset(sp.window.curPage.curLine)])
                # décomposition des branches de l'expression dans une liste
                lstKey=expression.split(".")
                curKey=lstKey[len(lstKey)-1]
                if len(curKey)>0: iStart=iEnd-(len(curKey)-1)
                else: iStart=iEnd
                # recherche de toutes les déclarations liées à l'expression 
dans le code restraint
                s=""
                s=extractDeclarationsFromText(sCode, expression)
                s=s+"\nhelp("+".".join(lstKey)+")"
                if flag6pad==False:
                        writeFile(sPythonScript, s)
                else: # c'est le 6pad++ qui exécute
                        try: exec(s)
                        except:
                                sp.say("erreur")
                                sp.window.messageBeep(1)
                                return
                        # end try
                # end if
        # end if
        if flag6pad==False:
                si = subprocess.STARTUPINFO()   
                si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                # exécution
                curProc=subprocess.Popen([sPythonPath, sPythonScript], 
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, 
startupinfo=si, universal_newlines=True)
                sResultConsole, sResultError = curProc.communicate()
                try: sResultConsole = sResultConsole.decode()
                except:
                        sResultConsole=decode2(sResultConsole)
                # end try
                # suppression du fichier temporaire
                os.remove(sPythonScript)
                if sResultConsole != "":
                        print(sResultConsole)
                else:
                        sp.window.messageBeep(1)
                # end if
        # end if
# end def

def curPosCompletion():
        # simule l'intellicence pour le python
        global pythonVersion
        # vérification qu'une version du python a bien été choisie
        if pythonVersion=="":
                sp.window.alert("Vous n'avez choisi aucune version de python 
installée sur votre ordinateur.\nVeuillez le faire par le menu 'outils' et 
retenter cette action.", "Erreur- version de python")
                return
        # end if
        sPythonPath=pythonVersion
        sPythonScript=sp.window.curPage.file
        sPythonScript=os.path.dirname(sPythonScript)
        if os.path.isdir(sPythonScript)==False: 
sPythonScript=getCurModuleDirPath()
        sPythonScript=sPythonScript+"\\pe_tmp.py"
        # recueillement du texte du document
        s = sp.window.curPage.text
        # somme-nous à une position d'importation de module ?
        flagModuleOnly=isInPosOfImport()
        # quelques autres initialisations
        flagPoint=False
        flag6pad=False
        iStart=sp.window.curPage.position
        iEnd=iStart
        curKey=""
        lst=[]
        lstVars=[]
        lstImports=[]
        lstClassDef=[]
        lstModules=[]
        # détermination si l'interpréteur choisi est celui du 6pad
        if pythonVersion.find("6pad++.exe")>=0: flag6pad=True
        # identification d'une éventuelle expression sous le curseur
        expression, iStart, iEnd=getCurExpression()
        # xxx s'il n'y a pas de point dans l'expression
        if expression.find(".")<=-1:
                flagPoint=False
                curKey=expression
                if flagModuleOnly==False:
                        # on circonscrit le code relatif à la ligne sous le 
curseur
                        sCode=getOnlyRelatedCode(s[0: 
sp.window.curPage.lineStartOffset(sp.window.curPage.curLine)])
                        # de ce code, on extrait tous les imports
                        lstImports=extractImportsFromText(sCode)
                        # de ce code, on extrait toutes les déclarations de 
variables
                        lstVars=extractVarsFromText(sCode)
                        # du code entier, on extrait les déclarations de 
classes et fonctions
                        lstClassDef=extractClassDefFromText(s)
                        # si l'interpréteur choisi est le 6pad++
                        if flag6pad==True:
                                # on recherche les sous-éléments du module 
__builtins__
                                try: lst=lst+dir(__builtins__)
                                except: pass
                                # on recherche les mots clés du langage
                                try:
                                        import keyword
                                        lst=lst+keyword.kwlist
                                except: pass
                                # selon les imports détectés
                                for e in lstImports:
                                        if re.match(".+?[ \\t]+as[ 
\\t]+([a-zA-Z\\d_]+)", e, re.I): # from x import x as x
                                                s2=finditer2List(".+?[ \\t]+as[ 
\\t]+([a-zA-Z\\d_]+)", e, re.I)[0].group(1)
                                                lst.append(s2)
                                        # end if
                                        if re.match("^[ \\t]*from[ \\t]+([^ 
\\t]+)[ \\t]+import[ \\t]+\\*", e, re.I): # from x import *
                                                s2=finditer2List("^[ \\t]*from[ 
\\t]+([^ \\t]+)[ \\t]+import[ \\t]+\\*", e)[0].group(1)
                                                try:
                                                        mo=__import__(s2)
                                                        lst=lst+dir(mo)
                                                except: pass
                                        elif re.match("^[ \\t]*from[ \\t]+([^ 
\\t]+)[ \\t]+import[ \\t]+([a-zA-Z\\d_\\.]+)", e, re.I): # from x import x
                                                s2=finditer2List("^[ \\t]*from[ 
\\t]+([^ \\t]+)[ \\t]+import[ \\t]+\([a-zA-Z\\d_\\.]+)", e)[0].group(2)
                                                if s2.find(".")<0: 
lst.append(s2)
                                        # end if
                                # end for
                        else: # l'interpréteur est une version de python 
classique
                                # pour trouver les mots clés et d'éventuelles 
sous-fonctions des imports,
                                # on va passer par un fichier intermédiaire 
dont le sample se trouve à la racine du dossier forPython
                                if 
os.path.isfile(getCurModuleDirPath()+"\\sampleCompletion.txt")==False:
                                        sp.window.alert("Un fichier sample 
d'exécution de code intermédiaire est introuvable", "Erreur- fichier manquant")
                                        return
                                # end if
                                
s2=readFile(getCurModuleDirPath()+"\\sampleCompletion.txt")
                                # construction de texte à exécuter
                                s=""
                                e=""
                                for e in lstImports:
                                        s=s+"try: "+e+"\n\texcept: pass\n\t"
                                # end for
                                s=s2.replace("[imports]", s)
                                # écriture et exécution
                                writeFile(sPythonScript, s)
                        # end if
                else: # flagModuleOnly==True
                        if flag6pad==False:
                                s="try:\n\timport pkgutil\n\tfor i in 
pkgutil.walk_packages():\n\t\tf,j,k=i\n\t\tprint(j)\nexcept: pass"
                                s=s+"\ntry:\n\timport sys\n\tfor e in 
sys.modules: print(e)\nexcept: pass"
                                # on recherche également parmi les fichiers 
python qui sont dans le même dossier
                                path=os.path.dirname(sPythonScript)
                                found=os.listdir(path)
                                for f in found:
                                        if re.match("\\.(py|pyw)$", f, re.I):
                                                
s=s+"\nprint('"+re.sub("\\.(py|pyw)$", "", f, re.I)+"')"        /reppp
                                        # end if
                                # end for
                                # écriture dans le fichier à exécuter
                                writeFile(sPythonScript, s)
                                lst=[]
                        else: # flag6pad==True
                                lst=listModules()
                        # end if
                # end if
        else: # xxx au moins un point est présent dans l'expression
                flagPoint=True
                # on circonscrit le code relatif à la ligne sous le curseur
                sCode=getOnlyRelatedCode(s[0: 
sp.window.curPage.lineStartOffset(sp.window.curPage.curLine)])
                # décomposition des branches de l'expression dans une liste
                lstKey=expression.split(".")
                curKey=lstKey[len(lstKey)-1]
                if len(curKey)>0: iStart=iEnd-(len(curKey)-1)
                else:
                        iStart=iEnd+1
                        iEnd=iEnd+1
                # end if
                # recherche de toutes les déclarations liées à l'expression 
dans le code restraint
                s=""
                s=extractDeclarationsFromText(sCode, expression)
                if s!="":
                        lst=findall2List("[^\\r\\n]+", s)
                        s=""
                        for e in lst:
                                s=s+"\ntry:\n\t"+e+"\nexcept: pass"
                        # end for
                # end if
                del lstKey[len(lstKey)-1]
                if flag6pad==False:
                        s=s+"\ntry:\n\tlst=dir("+".".join(lstKey)+")\n\tfor e 
in lst: print(e)\nexcept: pass"
                        writeFile(sPythonScript, s)
                else: # c'est le 6pad++ qui exécute
                        lst=[]
                        try: exec(s)
                        except: pass
                        try: lst=eval("dir("+".".join(lstKey)+")")
                        except: pass
                        if len(lst)==0:
                                sp.window.messageBeep(1)
                                return
                        # end if
                # end if
        # end if
        if flag6pad==False:
                si = subprocess.STARTUPINFO()   
                si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                # exécution
                curProc=subprocess.Popen([sPythonPath, sPythonScript], 
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, 
startupinfo=si, universal_newlines=True)
                sResultConsole, sResultError = curProc.communicate()
                try: sResultConsole = sResultConsole.decode()
                except: sResultConsole=decode2(sResultConsole)
                # suppression du fichier temporaire
                os.remove(sPythonScript)
                if sResultConsole != "":
                        lst=findall2List("[^\\r\\n]+", sResultConsole)
                else: # rien n'a été retourné
                        sp.window.messageBeep(1)
                        return
                # end if
        # end if
        # on ajoute les listes en attente
        lst=lst+lstVars+lstClassDef+lstModules
        if len(lst)==0:
                sp.window.messageBeep(1)
                return
        # end if
        lst=strSortList(lst)
        # doit-on enlever les doublons ?
        # attention, il y a une petite astuce.
        if flagPoint==True or flagModuleOnly==True: flagPoint=True
        else: lst=removeDuplicatesFromList(lst)
        # détermination de l'item qui sera sélectionné
        selec=0
        selec=getNierestExpression(lst, curKey)
        # proposition dans une liste de choix
        i=sp.window.choice("Complétion de code", "Complétion de code", lst, 
selec)
        if i>=0:
                if iEnd>iStart: iEnd=iEnd+1
                sp.window.curPage.replace(iStart, iEnd, lst[i])
                sp.window.curPage.position=iStart+len(lst[i])
        # end if
# end def

def listModules():
        # liste les modules appelables dans le scripting pour 6pad++
        lst=[]
        try:
                import pkgutil
                for i in pkgutil.walk_packages():
                        f,j,k=i
                        lst.append(j)
                # end for
        except: pass
        try:
                import sys
                for e in sys.modules: lst.append(e)
        except: pass
        return lst
# end def

def searchDeclarations(code, key):
        # rechercher recurssivement les déclarations
        lst=[]
        code="\r\n"+code+"\r\n"
        pt="([\\r\\n]+|:)[ \\t]*("+key+"[ \\t]*=[^\\r\\n]+|.+?[ \\t]+as[ 
\\t]+"+key+"[^a-zA-Z\\d_]?[^\\r\\n]*|from[ \\t]+"+key+"[ 
\\t]+import[^\\r\\n]+|from[ \\t]+[^ \\t]+[ \\t]+import[ 
\\t]+"+key+"[^a-zA-Z\\d_]?[^\\r\\n]*|import[ 
\\t]+"+key+"[^a-zA-Z\\d_]?[^\\r\\n]*)"
        found=finditer2List(pt, code, re.I)
        # parcours
        for e in found:
                # si ligne d'assignation avec un =
                pt2="^([\\r\\n]+|:)[ \\t]*"+key+"[ 
\\t]*=([a-zA-Z_][a-zA-Z\\d_]*)"
                if re.match(pt2, e.group(0), re.I):
                        key2=finditer2List(pt2, e.group(0), re.I)[0].group(1)
                        try: lst.append(searchDeclarations(code[0: e.start(0)], 
key2))
                        except: pass
                # end if
                lst.append(e.group(2))
        # end for
        s="\n".join(lst)
        return s
# end def

def extractDeclarationsFromText(code, expression):
        # extrait et renvoi toutes les déclarations liées à l'expression dans 
le texte
        s=""
        # décomposition des branches de l'expression dans une liste
        lstKey=expression.split(".")
        # on va rechercher toutes les déclarations grace à une fonction 
récursive
        s=searchDeclarations(code, lstKey[0])
        return s
# end def

def extractImportsFromText(s):
        # extrait et renvoi les imports du texte dans une liste
        lst=[]
        pt="^[ \\t]*(import[ \\t]+[^\\r\\n]+|from[ \\t]+[^\\r\\n]+)"
        found=re.finditer(pt, s, re.I+re.M)
        for e in found:
                lst.append(e.group(1))
        # end for
        # lst=removeDuplicatesFromList(lst)
        return lst
# end def

def extractVarsFromText(s):
        # extrait les assignations de variables du texte
        lst=[]
        # pour les déclarations uniques sur une ligne
        pt="[\\r\\n]+[ \\t]*([a-zA-Z][a-zA-Z\\d_]*)[ \\t]*=[^=]"
        found=finditer2List(pt, s, re.I)
        for e in found:
                lst.append(e.group(1))
        # end for
        # pour les déclarations multiples sur une ligne
        pt="([\\r\\n]+[ \\t]*|[ \\t]*,[ \\t]*)([a-zA-Z][a-zA-Z\\d_]*)([ 
\\t]*=|[ \\t]*,)"
        found=finditer2List(pt, s, re.I)
        for e in found:
                lst.append(e.group(2))
        # end for
        # lst.sort()
        # lst=removeDuplicatesFromList(lst)
        return lst
# end def

def extractClassDefFromText(s):
        # extrait les déclarations de classes et fonctions du texte
        lst=[]
        pt="[\\r\\n]+[ \\t]*(class|def)[ \\t]+([a-zA-Z\\d_]+)"
        found=finditer2List(pt, s, re.I)
        for e in found: lst.append(e.group(2))
        return lst
# end def

def checkLineValidity(page, line):
        # vérifie certains élément de validité de la syntaxe d'une ligne
        curLine=page.curLine
        s=page.line(line)
        # on s'assure qu'il n'y a pas d'espace ou de tabulation à la fin de la 
ligne
        if re.match("(.*?[^ \\t]+)[ \\t]+$", s):
                s=re.sub("([^ \\t]+)[ \\t]+$", "\\1", s)
                page.replace(page.lineStartOffset(line), 
page.lineEndOffset(line), s)
                page.position=page.lineStartOffset(curLine)
        # end if
        s2=s # sauvegarde de l'original
        # premièrement, si le début ou la fin de la ligne est à l'intérieur de 
triples quotes, alors elle est de toute façon valide
        lstTripleQuote=getTripleQuotePos(page.text)
        if isPosWithinTripleQuotes(page, page.lineStartOffset(line), 
lstTripleQuote) or isPosWithinTripleQuotes(page, page.lineEndOffset(line), 
lstTripleQuote):
                return True
        # end if
        # Vérification qu'on ne doit pas mélanger les types d'indentation en 
début de ligne
        lst=finditer2List("^[ \\t]+", s)
        if len(lst)>0:
                r=lst[0].group(0)
                if re.match(" ", r) and re.match("\\t", r):
                        sp.window.alert("Deux types d'indentation ont été 
détectés au début de la ligne "+str(line+1)+":\r\n '"+s2+"'", "Erreur 
d'indentation")
                        return False
                # end if
        # end if
        # retrait de l'indentation
        s=re.sub("^[ \\t]+", "", s)
        # si chaîne vide
        if s=="": return True
        # si les lignes précédentes se terminent par une virgule ou un 
antislash, on les inclus
        i=line
        while ((i-1)>=0 and re.match("(,|\\\\)[ \\t]*$", page.line(i-1))):
                i=i-1
                s=page.line(i)+" "+s
        # end while
        # vérification de l'équilibre des guillemets et appostrophes
        # on en profitera pour faire certains repérages
        flag=False
        char=""
        prev=""
        stringList=[]
        d=-1
        f=-1
        commentStart=-1
        litteral=False
        i=-1
        for e in s:
                i=i+1
                if flag==False:
                        if e=="#": # caractère dièze
                                commentStart=i
                                break
                        elif e=='"': # caractère guillemet
                                char='"'
                                d=i
                                flag=True
                                if s[i-1]=="r": litteral=True
                                else: litteral=False
                        elif e=="'": # caractère appostrophe
                                char="'"
                                d=i
                                flag=True
                                if s[i-1]: litteral=True
                                else: litteral=False
                        # end if
                else: # flag==True
                        if e==char:
                                if prev=="\\" and litteral==True: 
                                        prev=e
                                        continue
                                elif prev=="\\" and litteral==False: 
                                        k=i-1
                                        s3=""
                                        while(k>=0):
                                                if s[k]=="\\": s3=s3+"\\"
                                                k=k-1
                                        # end while
                                        # le nombre d'antislash doit être 
impaire
                                        if len(s3) % 2 != 0:
                                                prev=e
                                                continue
                                        # end if
                                # end if
                                f=i
                                stringList.append((d, f))
                                flag=False # fermeture du guillemet
                        # end if
                # end if
                prev=e
        # end for
        if flag==True: # quotation non refermée
                sp.window.alert("Une chaîne string a été ouverte par le 
caractère ("+char+" et n'a pas été refermé à la ligne "+str(line+1)+":\r\n 
'"+s2+"'", "Erreur de chaîne string sans fermeture")
                return False
        # end if
        # pour plus de facilité, retrait de la partie commentaire
        if commentStart>=0: s=s[0:commentStart]
        # pour plus de facilité, remplacement des déclarations de string par 
des pseudo variables
        lst=[]
        e=""
        for e in s: lst.append(e)
        for d, f in stringList:
                for i in range(d, f+1): lst[i]="a" # remplacement par des a
        # end for
        s="".join(lst)
        #  si la ligne se termine par une virgule ou un antislash
        # ce sont des instructions de continuation à la ligne suivante.
        if re.match("(,|\\\\)[ \\t]*$", s): return True
        # Vérification de l'équilibre des accolades
        if s.find("{")>=0 or s.find("}")>=0:
                if s.count("{") != s.count("}"):
                        sp.window.alert("Il y a un déséquilibre des accolades 
'{}' à la ligne "+str(line+1)+":\r\n '"+s2+"'", "Erreur- déséquilibre des 
accolades")
                        return False 
                # end if
        # end if
        # Vérification de l'équilibre des parenthèses
        if s.find("(") or s.find(")"):
                if s.count("(") != s.count(")"):
                        sp.window.alert("Il y a un déséquilibre des parenthèses 
'()' à la ligne "+str(line+1)+":\r\n '"+s2+"'", "Erreur- déséquilibre des 
parenthèses")
                        return False
                # end if
        # end if
        # vérification de l'équilibre des crochets
        if s.find("[")>=0 or s.find("]")>=0:
                if s.count("[") != s.count("]"):
                        sp.window.alert("Il y a un déséquilibre des crochets 
'[]' à la ligne "+str(line+1)+":\r\n '"+s2+"'", "Erreur- déséquilibre des 
crochets")
                        return False
                # end if
        # end if
        # vérification si il doit y avoir un deux point à la fin de la ligne 
        if re.match("^[ 
\\t]*(class|def|while|for|if|with|elif|else|try|except|finally)[^:]*", s, re.I):
                # on masque préalablement les éventuels deux points dans les 
crochets
                s=re.sub("\\[[^\\]]+\\]", "", s)
                if s.find(":")<=0:
                        sp.window.alert("Il manque le caractère deux points ':' 
à la fin de la ligne "+str(line+1)+":\r\n '"+s2+"'", "Erreur- absence des deux 
points")
                        return False
                # end if
        # end if
        return True # ligne valide
# end def

def readFile(sFilePath, sMode='r'):
        # lit et renvoi le contenu d'un fichier texte
        path = open(sFilePath, sMode)
        s = path.read()
        path.close() # Fermeture du fichier
        return s
# end def

def writeFile(sFilePath, s, sMode='w'):
        # écrit dans un fichier
        path = open(sFilePath, sMode)
        path.write(s)
        path.close() # Fermeture du fichier
# end def

def runFile(path, params):
                # execute un fichier en lui passant un paramètre
                return subprocess.Popen([path, params], 0, 
stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, 
universal_newlines=True)
# end def

def runPythonScript(pythonExePath, pythonScriptPath, blDisplayInConsole):
        # exécute le code python et fait éventuellement apparaître le renvoi # 
dans la fenêtre console.
        global curProc
        i = 0
        # sResultConsole = ""
        # sResultError = ""
        # vérification si chemin vers exécutable python existe
        if os.path.isfile(pythonExePath) == False:
                sp.window.alert("Aucun interpréteur python n'a été assigné ou 
le chemin de l'interpréteur actuel est éronné.\nVeuillez choisir un 
interpréteur valide.", "Erreur- interpréteur introuvable")
                return ""
        # end if ' fin si exécutable python n'a pas été repéré
        
        # création de l'objet de retour
        file2 = ""
        # instruction qui envéront toute écriture dans la console vers le 
fichier créé.
        sys.stdout = file2
        sys.stderr = file2
        # variable à utiliser
        variable1 = ''
        variable2 = ''
        
        # changement du repertoire de travail
        # pour éviter les erreurs de fichiers non trouvés
        os.chdir(os.path.dirname(pythonScriptPath))
        
        si = subprocess.STARTUPINFO()
        si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        # exécution
        curProc=subprocess.Popen([pythonExePath, pythonScriptPath], 
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, 
startupinfo=si, universal_newlines=True)
        # curProc=subprocess.Popen([pythonExePath, pythonScriptPath], 0, 
stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, 
universal_newlines=True)
        sResultConsole, sResultError = curProc.communicate()
        # try: sResultConsole = sResultConsole.decode()
        # except: sResultConsole=decode2(sResultConsole)
        if sResultConsole != "":
                if blDisplayInConsole==True:
                        sp.console.print(sResultConsole)
                # end if fin si autorisation affichage dans pseudo console
        # end if fin si texte de retour
        # renvoi
        return sResultConsole
# End def

def runCurPythonScript():
        # exécute le fichier python courant avec la version de python 
actuellement sélectionnée
        # vérification qu'une version du python a bien été choisie
        if pythonVersion=="":
                sp.window.alert("Vous n'avez choisi aucune version de python 
installée sur votre ordinateur.\nVeuillez le faire par le menu 'outils' et 
retenter cette action.", "Erreur- version de python")
                return
        # end if
        sPythonPath=pythonVersion
        # recherche du fichier à exécuter
        sFilePath=sp.window.curPage.file
        if os.path.isfile(sFilePath)==False:
                # on va créer un fichier temporaire
                sFilePath=getCurModuleDirPath()+"\\exec\\tmp.py"
                writeFile(sFilePath, sp.window.curPage.text)
                if os.path.isfile(sFilePath)==False:
                        sp.window.alert("Le fichier temporaire d'exécution du 
script python n'a pas pu être créé.", "Erreur d'exécution") 
                        return
                else: # le fichier existe
                        sp.window.curPage.save() # on enregistre ses 
modifications
        # end if
        # si l'interpréteur est le 6pad++ lui-même
        if pythonVersion.find("6pad++.exe")>-1:
                # on va utiliser la ligne de commande
                sFilePath="extension="+getModuleRefByPath(sFilePath)
        # end if
        sp.say("Exécution", True)
        # exécution
        goToLineOfError(runPythonScript(sPythonPath, sFilePath, True))
# end def

def stopCurPythonScript():
        # Stoppe l'exécution du script courant
        try:
                curProc.terminate()             
        except:
                sp.window.messageBeep(1)
        # end try
# end def

def strSortList(lst):
        # trie la liste de str sans se soucier de la cass
        e=""
        # astuce pour  réduire la cass sans perdre les repères
        i=-1
        for e in lst:
                i=i+1
                # on va doubler l'expression
                # la première partie sera en minuscule et servira à la 
comparaison
                # la seconde partie gardera la cass d'origine et sera restaurée 
à la fin des traitements.
                lst[i]=e.lower()+e
        # end for
        # tri proprement dit
        lst.sort()
        # restauration
        i=-1
        for e in lst:
                i=i+1
                lst[i]=e[int(len(e)/2):]
        # end for
        return lst
# end def

def strSortList2(lst):
        # trie la liste de str sans se soucier de la cass
        tmp=""
        lst2=[] # liste de sauvegarde
        n=len(lst)
        # mise en minuscule et duplication
        for i in range(0, n):
                lst2.append(lst[i])
                lst[i]=lst[i].lower()
        # end for
        # parcours de tri
        flag=True
        iStart=1
        while(flag==True):
                flag=False
                for i in range(iStart, n):
                        if lst[i]<lst[i-1]:
                                tmp=lst[i]
                                lst[i]=lst[i-1]
                                lst[i-1]=tmp
                                tmp=lst2[i]
                                lst2[i]=lst2[i-1]
                                lst2[i-1]=tmp
                                # if flag==False: iStart=i+1
                                flag=True
                        # end if
                # end for
        # end while
        return lst2
# end def

def removeDuplicatesFromList(lst):
        # supprime les str doublons dans la liste en paramètre  
        # cette liste doit être ordonnée/triée au préalable
        n=len(lst)
        i=n-1
        while(i>0):
                if lst[i]==lst[i-1]: del lst[i]
                if i>1:
                        if lst[i]==lst[i-2]: del lst[i]
                # end if
                i=i-1
        # end while
        return lst
# end def

def CreateMenusForPythonInstalled(mnu):
        # liste les versions de python installés et les référence dans le 
menu 'versions de python
        # dans tous les repertoires où elles peuvent se trouver.
        global pythonVersion
        lastPythonVersion=sp.getConfig("lastPythonVersion", "")
        # premièrement, reférencement de l'interpréteur pour le 6pad++ comme 
le premier d'entre eux
        i=mnu.length
        sFld=sp.appfullpath
        mnu.add(label = "6pad++", index = -1, action=lambda 
arg=i:choosePythonVersion(arg))
        # si correspond à la dernière version choisie
        if lastPythonVersion==sFld:
                mnu[i].checked=True
                pythonVersion=sFld
        # end if
        pythonList.append(sFld)
        # recherche dans les repertoires
        alphabet="CDEFGHIJ"
        for i in range(0, len(alphabet)):
                SearchPythonInDir(mnu, alphabet[i]+":\\")
                SearchPythonInDir(mnu, alphabet[i]+":\\Programs\\")
                SearchPythonInDir(mnu, alphabet[i]+":\\Program Files\\")
                SearchPythonInDir(mnu, alphabet[i]+":\\Program Files (x86\\)")
        # end for
# end def

def SearchPythonInDir(mnu, path):
        # Liste les versions de python pour un repertoire particulier.
        global pythonVersion
        lastPythonVersion=""
        i = 0
        f=""
        l = []
        fld=""
        if os.path.isdir(path)==False: return
        # rappel de la version ayant été enregistrée la dernière fois
        lastPythonVersion=sp.getConfig("lastPythonVersion", "")
        # parcours du dossier
        found=os.listdir(path)
        for f in found:
                if re.match("^python\\d\\.*\\d", f, re.I):
                        #  on détermine si il existe un fichier python.exe
                        if os.path.isfile(path+f+"\\python.exe"):
                                fld=path+f+"\\python.exe"
                                i=mnu.length
                                mnu.add(label = f+" de "+os.path.dirname(fld), 
index = -1, action=lambda arg=i:choosePythonVersion(arg))
                                # si correspond à la dernière version choisie
                                if fld == lastPythonVersion:
                                        mnu[i].checked=True
                                        pythonVersion=fld
                                # end if
                                pythonList.append(fld)
                        # end if
                # end if
        # end for
# end def

def choosePythonVersion(i):
        # choix d'une version de python via les menus
        global pythonVersion
        # on décoche tous les autres sauf celui cliqué
        mnu=sp.window.menus['tools']['pythonVersions']
        for x in range(0, mnu.length):
                if x==i:
                        mnu[x].checked = True
                else:
                        mnu[x].checked = False
                # end if
        # end for
        pythonVersion=pythonList[i]
        sp.setConfig("lastPythonVersion", pythonVersion)
# end def

def isPythonPage(page=None):
        # ""détermine si la page en paramètre contient un fichier python
        if page==None: page=sixpad.window.curPage
        sFile=page.file
        iLength=len(sFile)
        if sFile[iLength-3:].lower()==".py" or 
sFile[iLength-4:].lower()==".pyw":
                return True
        else:
                return False
        # end if
# end def

def isCommentInLine(sLine):
        # détermine s'il y a un commentaire commençant sur la ligne
        lst=getStringAndCommentPos(sLine)
        if len(lst)==0: return False
        if lst[len(lst)-1].startswith("#"): return True
        return False
# end def

def isPosWithinTripleQuotes(page, pos, lst=None):
        # La position se trouve-t-elle dans des triples quotes ?
        s=page.text
        if lst==None: lst=getTripleQuotePos(s)
        if len(lst)==0: return False
        for d, f in lst:
                if d<=pos and pos<=f: return True
        # end for
        return False
# end def

def isInPosOfImport():
        # détermine si on se trouve sur une ligne d'import, à la position d'un 
mot clé
        page=sp.window.curPage
        iLine=page.curLine
        pos=page.position
        sLine=page.line(iLine)
        start=page.lineStartOffset(iLine)
        sLine=sLine[0: (pos-start)]
        if re.match("^[ \\t]*(import[ \\t]+[a-zA-Z_\\d_]*|from[ 
\\t]+[a-zA-Z\\d_]*)$", sLine, re.I):
                # import x ou from x
                return True
        elif re.match(".*?__import__[ \\t]*\\([ \\t]*[a-zA-Z\\d_\\.]*", sLine, 
re.I):
                # __import(x
                return True
        else:
                return False
        # end if
# end def

def isDirectLine(sLine):
        # vérifie si la ligne exécute plusieurs instructions au lieu d'une 
seule après un ':'
        d, f=0, 0
        # D'abord, Transfert dans une liste classique
        l=[]
        for e in sLine: l.append(e)
        # si potentiellement présence de strings ou partie de commentaire
        # on va les masquer par des caractères neutres
        if sLine.find("'")>=0 or sLine.find('"')>=0:
                lst=getStringAndCommentPos(sLine)
                for (d, f) in lst:
                        if l[d]=="#": masc=" "
                        else: masc="a"
                        for i in range(d, f+1):
                                l[i]=masc
                        # end for
                # end for
        # end if
        # si présence de crochets,
        # on les masque également
        ibrac=0
        flag=False
        for i in range(0, len(l)):
                e=l[i]
                if e=="[":
                        ibrac=ibrac+1
                        flag=True
                elif e=="]":
                        l[i]="b"
                        ibrac=ibrac-1
                        if ibrac==0: flag=False
                # end if
                if flag==True: l[i]="b"
        # end for
        sLine="".join(l)
        # test
        if re.match("^.+?:[ \\t]*[a-zA-Z_]", sLine):
                return True
        else:
                return False
        # end if
# end def

def isModuleExists(module_name):
        try: 
                __import__(module_name)
        except ImportError: 
                return False 
        else: 
                return True
        # end try
# end def

def isSyntaxValid(code):
        # vérifie la syntaxe d'un bout de code
        s="try:\r\n\texec(\""+code+"\")\r\nexcept SyntaxError:\r\n\traise"
        sp.window.alert(s)
        exec(s)
# end def

def onPageOpened(newPage):
        # à l'ouverture d'une nouvelle page
        if isPythonPage(newPage)==True:
                CreatePythonTools()
        else:
                removePythonTools()
        # end if
        sixpad.say("Nouvelle page ouverte")
# end def

def tmrLineMove():
        # vérificateur de changement de ligne
        global iLastLine
        global sLastLine
        global flagCheckLineMove
        if flagCheckLineMove==False: return
        iLine= sp.window.curPage.curLine
        # s'il y a eu  changement de ligne
        if iLine!=iLastLine:
                # si la ligne a été modifiée entre temps
                sLine=sp.window.curPage.line(iLastLine)
                if sLine!=sLastLine:
                        flagCheckLineMove=False # suspension
                        if checkLineValidity(sp.window.curPage, 
iLastLine)==False:
                                sLastLine=sp.window.curPage.line(iLastLine)
                                
sp.window.curPage.position=sp.window.curPage.lineStartOffset(iLastLine)
                                flagCheckLineMove=True # relance
                                return
                        # end if
                        flagCheckLineMove=True # relance
                # end if
                iLastLine=iLine
                sLastLine=sp.window.curPage.line(iLine)
        # end if
# end def

def goToLineOfError(errorMessage):
        # Amène le focus sur une ligne d'erreur après l'avoir reconnue en 
parsant le message d'erreur et Renvoi True en cas de succès de déplacement
        found=re.finditer("error", errorMessage, re.I)
        i =0
        for e in found: i=i+1
        if i==0: return False
        file=""
        line=0
        found=re.finditer('file[ \\t]+"([^"]+)"[ \\t]*,[ \\t]+line[ 
\\t]+(\\d+)', errorMessage, re.I)   
        for e in found: file, line= e.group(1), int(e.group(2))
        if line>0 and os.path.isfile(file):
                page=sp.window.curPage # par défaut
                # si le fichier pointé n'est pas le fichier de la page courante
                if os.path.samefile(file, sp.window.curPage.file)==False:
                        # on vérifie s'il n'est pas déjà ouvert dans les 
onglets inactifs
                        flag=False
                        for p in sp.window.pages:
                                if os.path.samefile(file, p.file)==True:
                                        page=p
                                        flag=True
                                        break
                                # end if
                        # end for
                        if flag==False: page=sp.window.open(file)
                # end if
                # positionnement à la ligne de l'erreur
                page.position=page.lineStartOffset(line-1)
                page.focus()
                return True
        else:
                return False
        # end if
# end def

def openPythonManual():
        # ouvre le manuel du langage python associé à la version en cours
        global pythonVersion
        path=os.path.dirname(pythonVersion)+"\\Doc"
        if os.path.isdir(path)==False:
                sp.window.messageBeep(1)
                return
        # end if
        found=os.listdir(path)
        for f in found:
                if re.match(".*?\\.chm", f, re.I):
                        os.system(path+"\\"+f)
                        return                  
                # end if
        # end for
        sp.window.messageBeep(1)
# end def

def CreatePythonTools ():
        # fonction de personnalisation de l'interface et l'arrière-plan
        global idTmrLineMove
        # ajout de menus spécifique au menu outils
        mnu = sp.window.menus['tools']
        if mnu['pythonVersions']==None:
                mnu2=mnu.add("Versions de python", None, -1, "", 
"pythonVersions", True, False, True)
                # on cherche et ajoute les versions de python installées au 
menu
                CreateMenusForPythonInstalled(mnu2)
                #
                mnu2=mnu.add("Balises de fin de block", None, -1, "", 
"pythonTags", True, False, True)
                mnu2.add(label="Retirer les balises de fin de block", 
action=removeTagsInCurPage)
                mnu2.add(label="Ajouter/raffraîchir les balises de fin de 
block", action=addTagsInCurPage)
                mnu2.add(label="Ajuster l'indentation aux balises de fin de 
block", action=AdjustIndentsByTagsInCurPage, accelerator="CTRL+SHIFT+E")
                #
                mnu.add(label="Exécuter", accelerator="CTRL+F5", 
action=runCurPythonScript)
                mnu.add(label="Arrêter l'exécution", action=stopCurPythonScript)
                mnu.add(label="Définition mot clé", accelerator="CTRL+I", 
action=curPosHelp)
                mnu.add(label="Complétion de code", accelerator="CTRL+J", 
action=curPosCompletion)
        # ajout au menu aide
        mnu=sp.window.menus['help']
        mnu.add(label="Manuel du langage python", action=openPythonManual)
        # end if
        # ajout de raccourci clavier
        # sixpad.window.addAccelerator("CTRL+B", testeur1)
        # création du timer de vérification
        idTmrLineMove=sp.window.setInterval(tmrLineMove, 400)
# end def

def removePythonTools():
        # commentaire
        global idTmrLineMove
        sp.window.clearInterval(idTmrLineMove)
# end def

def testeur1():
        sp.say("test")
        page=sp.window.curPage
        lst=listModules()
        alert(str(lst))
        return
        s=page.text
        lst=getTripleQuotePos(s)
        return
        s=addTags(s)
        # sp.window.alert(s)
        sp.console.write(s)
        # # getBlocksPositions()
        return
        i=sp.window.menus['tools']['pythonVersions'].length
        sp.say(str(i))
        return
        curPosHelp()
# end def

# création des outils d'interface spécifiques au python
if isPythonPage(sp.window.curPage)==True: CreatePythonTools()
# ajout d'évènement
sixpad.window.addEvent("pageOpened", onPageOpened)


# [4] listage des noms des modules disponibles pour cet interpréteur
try:
        import pkgutil
        for i in pkgutil.walk_packages():
                f,j,k=i
                print(j)
        # end for
except: pass
# [/4]
# -*- coding: utf-8 -*-
# script sample de listage des objets et mots clés du langage python

# [1] listage de mots clés liés à des imports particuliers
try:
        [imports]
        lst=dir()
        for e in lst: print(e)
except: pass
# [/1]

# [2] listages des mots clés du module builtins
try:
        lst=dir(__builtins__)
        for e in lst:           print(e)
except: pass
# [/2]

# [3] listage des mots clés standards du langage python
try:
        import keyword
        lst=keyword.kwlist
        for e in lst: print(e)
except: pass
# [/3]

Répondre à