-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhtml_validator.py
More file actions
272 lines (218 loc) · 11.3 KB
/
Copy pathhtml_validator.py
File metadata and controls
272 lines (218 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
import tkinter as tk
from tkinter import filedialog, scrolledtext
contenido = ""
pila_de_etiquetas = []
pila_de_may_men = []
tags_que_siempre_se_cierran = {
"html", "head", "title", "style", "script", "body", "section", "nav", "article",
"aside", "h1", "h2", "h3", "h4", "h5", "h6", "header", "footer", "address", "p",
"blockquote", "ol", "ul", "dl", "figure", "figcaption", "main", "div", "a", "em",
"strong", "small", "cite", "q", "dfn", "abbr", "ruby", "rt", "rp", "data", "time",
"code", "var", "samp", "kbd", "sub", "sup", "i", "b", "u", "mark", "bdi", "bdo",
"span", "ins", "del", "iframe", "object", "video", "audio", "map", "table",
"caption", "colgroup", "tbody", "thead", "tfoot", "tr", "form", "fieldset",
"legend", "label", "button", "select", "optgroup", "textarea", "output",
"progress", "meter", "details", "summary", "dialog", "template", "canvas", "svg",
"math", "noscript", "path", "pre", "frameset", "noframes"
}
tags_que_pueden_no_cerrarse = {
"li", "dt", "dd", "td", "th", "tr", "thead", "tbody", "tfoot", "colgroup", "option"
}
tags_que_no_se_cierran = {
"img", "input", "link", "meta", "br", "hr", "source", "track", "area", "base",
"embed", "param", "command", "!doctype", "frame"
}
# Función que actualiza los números de línea
def actualizar_lineas(event=None):
contenido_lineas.config(state=tk.NORMAL)
contenido_lineas.delete(1.0, tk.END)
# Obtener el número de líneas del contenido
lineas = "\n".join(str(i) for i in range(1, int(cuadro_contenido.index("end-1c").split(".")[0]) + 1))
contenido_lineas.insert(tk.END, lineas)
contenido_lineas.config(state=tk.DISABLED)
# Vincular el desplazamiento del índice con el contenido
def sincronizar_scroll(*args):
contenido_lineas.yview(*args)
cuadro_contenido.yview(*args)
# Sincronizar el desplazamiento usando la rueda del ratón
def sincronizar_mousewheel(event):
contenido_lineas.yview_scroll(int(-1*(event.delta/120)), "units")
cuadro_contenido.yview_scroll(int(-1*(event.delta/120)), "units")
return "break"
# Función para limpiar contenido y resultados
def limpiar():
cuadro_contenido.delete(1.0, tk.END)
area_resultados.delete(1.0, tk.END)
etiqueta.config(text="No se ha seleccionado ningún archivo.")
actualizar_lineas()
# Función para seleccionar archivo y cargar contenido
def seleccionar_archivo():
archivo_seleccionado = filedialog.askopenfilename(
title="Selecciona un archivo de texto o HTML",
filetypes=[("Archivos de texto o HTML", "*.txt *.html")]
)
if archivo_seleccionado:
etiqueta.config(text=f"Archivo seleccionado: {archivo_seleccionado}")
with open(archivo_seleccionado, 'r', encoding='utf-8') as archivo:
contenido = archivo.read()
cuadro_contenido.delete(1.0, tk.END)
cuadro_contenido.insert(tk.END, contenido)
actualizar_lineas()
else:
etiqueta.config(text="No se seleccionó ningún archivo.") # Función para limpiar contenido y resultados
# Función para verificar sintaxis
def verificar_syntax():
pila_de_etiquetas.clear()
area_resultados.delete(1.0, tk.END) # Limpiar resultados previos
texto = cuadro_contenido.get("1.0", "end-1c")
if not texto:
area_resultados.insert(tk.END, "No hay contenido a verificar.")
return
errores_sintaxis = validar_errores(texto)
area_resultados.insert(tk.END, "Errores de sintaxis detectados:\n")
area_resultados.insert(tk.END, f"{errores_sintaxis}\n")
def validar_errores(contenido):
tags_de_apertura = 0
tags_de_cierre = 0
num_de_linea = 1
menor_que = False
i = 0
pila_de_etiquetas = [] # Inicializamos la pila de etiquetas
while i < len(contenido):
char = contenido[i]
if char == '\n':
num_de_linea += 1
i += 1
continue
if char == "<" and not menor_que: # Si estamos dentro de un tag
menor_que = True
# Detectar si es un comentario
if contenido[i + 1:i + 4] == "!--":
cierre_comentario = contenido.find("-->", i + 1)
if cierre_comentario != -1:
i = cierre_comentario # Saltamos hasta el final del comentario
else:
error = "Comentario HTML no cerrado en linea: " + str(num_de_linea) # Error si no se encuentra el cierre
return error
i += 1
continue
# Si es un tag de cierre
if contenido[i + 1] == "/":
tags_de_cierre += 1
primer_espacio = contenido.find(" ", i + 1)
primer_mayor_que = contenido.find(">", i + 1)
if primer_espacio == -1 and primer_mayor_que != -1:
indice = primer_mayor_que
elif primer_espacio != -1 and primer_mayor_que == -1:
indice = primer_espacio
else:
indice = min(primer_mayor_que, primer_espacio)
tag = contenido[i + 2:indice].lower() # Obtenemos el tag de cierre
i = indice
if len(pila_de_etiquetas) == 0:
error = "El tag: " + tag +" de la linea: " + str(num_de_linea) + " no fue abierto"
return error # Error si no hay tag de apertura en la pila
if tag == pila_de_etiquetas[-1][0]: # Comparar con el tag de apertura
pila_de_etiquetas.pop() # Sacamos el tag de la pila
continue
if pila_de_etiquetas[-1][0] in tags_que_pueden_no_cerrarse:
while pila_de_etiquetas and pila_de_etiquetas[-1][0] in tags_que_pueden_no_cerrarse:
pila_de_etiquetas.pop()
if pila_de_etiquetas and pila_de_etiquetas[-1][0] == tag:
pila_de_etiquetas.pop()
else:
error = "No se ha encontrado un tag de apertura correspondiente para " + tag + " en la linea " + str(num_de_linea)
return error # Error si no se encuentra el tag de apertura correspondiente
else:
error = "Tag de apertura y cierre no corresponden en lineas " + str(num_de_linea) + "y " + str(pila_de_etiquetas[-1][1])
return error
continue
# Si es un tag de apertura
primer_espacio = contenido.find(" ", i)
primer_mayor_que = contenido.find(">", i)
if primer_espacio == -1 and primer_mayor_que != -1:
indice = primer_mayor_que
elif primer_espacio != -1 and primer_mayor_que == -1:
indice = primer_espacio
else:
indice = min(primer_mayor_que, primer_espacio)
tag = contenido[i + 1:indice].lower() # String de Tag de apertura
i = indice
if tag == "script":
cierre_script = contenido.find("/script", i + 1)
if cierre_script != -1:
i = cierre_script # Saltamos hasta el final del comentario
else:
error = "No se ha encontrado un tag de cierre para script en la linea " + str(num_de_linea)
return error # Error si no se encuentra el cierre
continue
if tag in tags_que_siempre_se_cierran or tag in tags_que_pueden_no_cerrarse:
tags_de_apertura += 1
pila_de_etiquetas.append([tag, num_de_linea]) # Añadimos el tag a la pila
elif tag not in tags_que_no_se_cierran:
error = "Tag inválido en linea " + str(num_de_linea)
return error # Error si el tag no es válido
continue
elif char == ">":
if menor_que:
menor_que = False
else:
error = "Tag no cerrado correctamente en linea " + str(num_de_linea)
return error # Error de mal cierre de menor y mayor que
i += 1
continue
elif char == "<" and menor_que:
error = "Error de orden de corchetes (>) en linea " + str(num_de_linea)
return error # Error de abierto de menor que cuando ya había uno
# Si es cualquier otro caracter, seguimos avanzando
i += 1
# Verificar si hay etiquetas que no se cerraron
if len(pila_de_etiquetas) > 0:
if pila_de_etiquetas[-1][0] not in tags_que_pueden_no_cerrarse:
error = "Tag " + tag + " no cerrado en linea " + str(pila_de_etiquetas[-1][1])
return error # Retorna la línea donde falta el cierre
if menor_que:
error = "Error de orden de corchetes (<) en linea " + str(num_de_linea)
return error # Error si aún hay un menor que abierto sin cierre
# Crear la ventana principal
root = tk.Tk()
root.title("Verificador de Sintaxis HTML")
root.configure(bg="#2b2b2b")
# Configuración de colores y fuentes
color_boton = "#007acc"
color_texto = "#ffffff"
fuente_texto = ("Arial", 12)
# Widget para selección de archivo
boton_archivo = tk.Button(root, text="Seleccionar archivo", command=seleccionar_archivo,
bg=color_boton, fg=color_texto, font=("Arial", 10, "bold"))
boton_archivo.pack(pady=5)
etiqueta = tk.Label(root, text="No se ha seleccionado ningún archivo.", bg="#2b2b2b", fg=color_texto, font=fuente_texto)
etiqueta.pack(pady=5)
# Contenedor de texto con números de línea
frame_texto = tk.Frame(root)
frame_texto.pack(pady=5, fill=tk.BOTH, expand=True)
# Añadir Scroll horizontal
contenido_lineas = tk.Text(frame_texto, width=4, bg="#1e1e1e", fg=color_texto, font=fuente_texto, state=tk.DISABLED, wrap=tk.NONE)
contenido_lineas.pack(side=tk.LEFT, fill=tk.Y)
scroll_vertical = tk.Scrollbar(frame_texto, orient=tk.VERTICAL, command=sincronizar_scroll)
scroll_vertical.pack(side=tk.RIGHT, fill=tk.Y)
cuadro_contenido = tk.Text(frame_texto, wrap=tk.NONE, bg="#1e1e1e", fg=color_texto, font=fuente_texto,
yscrollcommand=scroll_vertical.set)
cuadro_contenido.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# Vincular desplazamiento de la rueda del ratón a ambos campos
cuadro_contenido.bind("<MouseWheel>", sincronizar_mousewheel)
contenido_lineas.bind("<MouseWheel>", sincronizar_mousewheel)
# Vincular la actualización de líneas al escribir y al desplazarse
cuadro_contenido.bind("<KeyRelease>", actualizar_lineas)
cuadro_contenido.bind("<Configure>", actualizar_lineas)
# Botones de acción
boton_verificar = tk.Button(root, text="Verificar Sintaxis", command=verificar_syntax, bg=color_boton, fg=color_texto, font=("Arial", 10, "bold"))
boton_verificar.pack(pady=5)
boton_limpiar = tk.Button(root, text="Limpiar", command=limpiar,
bg=color_boton, fg=color_texto, font=("Arial", 10, "bold"))
boton_limpiar.pack(pady=5)
area_resultados = scrolledtext.ScrolledText(root, width=80, height=10, bg="#1e1e1e", fg=color_texto, font=fuente_texto)
area_resultados.pack(pady=5)
area_resultados.insert(tk.END, "Aquí se mostrarán los errores de sintaxis o mensajes informativos...")
# Ejecutar la aplicación
root.mainloop()