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]