Skip to content

Commit 427b09a

Browse files
committed
Ameliore l'editeur avance (coloration, diff, monospace)
Ajoute une coloration simple YAML/JSON Ajoute un bouton de diff avec affichage unifié Affiche le chemin du fichier et désactive le wrap Garde l'éditeur léger et rapide
1 parent c1b1aff commit 427b09a

1 file changed

Lines changed: 167 additions & 124 deletions

File tree

Core/AdvancedConfigEditor.py

Lines changed: 167 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@
1515

1616
from __future__ import annotations
1717

18+
import difflib
1819
import json
1920
import os
2021
from pathlib import Path
22+
from typing import Callable
2123

2224
import yaml
25+
from PySide6.QtCore import QRegularExpression
26+
from PySide6.QtGui import QFont, QSyntaxHighlighter, QTextCharFormat, QColor
2327
from PySide6.QtWidgets import (
2428
QDialog,
2529
QTabWidget,
@@ -63,6 +67,53 @@ def _safe_parse_json(text: str) -> bool:
6367
return False
6468

6569

70+
class _SimpleHighlighter(QSyntaxHighlighter):
71+
def __init__(self, doc, mode: str = "yaml"):
72+
super().__init__(doc)
73+
self.mode = mode
74+
self.rules: list[tuple[QRegularExpression, QTextCharFormat]] = []
75+
76+
def _fmt(color: str, bold: bool = False) -> QTextCharFormat:
77+
fmt = QTextCharFormat()
78+
fmt.setForeground(QColor(color))
79+
if bold:
80+
fmt.setFontWeight(QFont.Weight.Bold)
81+
return fmt
82+
83+
key_fmt = _fmt("#9cdcfe", bold=True)
84+
str_fmt = _fmt("#ce9178")
85+
num_fmt = _fmt("#b5cea8")
86+
bool_fmt = _fmt("#569cd6", bold=True)
87+
sym_fmt = _fmt("#d4d4d4")
88+
com_fmt = _fmt("#6a9955")
89+
90+
# Strings
91+
self.rules.append((QRegularExpression(r"'[^']*'"), str_fmt))
92+
self.rules.append((QRegularExpression(r'"[^"]*"'), str_fmt))
93+
# Numbers
94+
self.rules.append((QRegularExpression(r"\b\d+(?:\.\d+)?\b"), num_fmt))
95+
# Booleans/null
96+
self.rules.append((QRegularExpression(r"\b(true|false|null|yes|no)\b", QRegularExpression.CaseInsensitiveOption), bool_fmt))
97+
# Symbols
98+
self.rules.append((QRegularExpression(r"[{}\[\]:,]"), sym_fmt))
99+
# Comments
100+
self.rules.append((QRegularExpression(r"#.*$"), com_fmt))
101+
102+
if mode == "yaml":
103+
# YAML keys (simple: key:)
104+
self.rules.append((QRegularExpression(r"^\s*[A-Za-z0-9_\-\.]+(?=\s*:)"), key_fmt))
105+
else:
106+
# JSON keys ("key":)
107+
self.rules.append((QRegularExpression(r'"[A-Za-z0-9_\-\.]+"(?=\s*:)'), key_fmt))
108+
109+
def highlightBlock(self, text: str) -> None:
110+
for pattern, fmt in self.rules:
111+
it = pattern.globalMatch(text)
112+
while it.hasNext():
113+
match = it.next()
114+
self.setFormat(match.capturedStart(), match.capturedLength(), fmt)
115+
116+
66117
class AdvancedConfigEditor(QDialog):
67118
def __init__(self, gui):
68119
super().__init__(gui)
@@ -91,152 +142,144 @@ def __init__(self, gui):
91142
def _workspace_dir(self) -> str | None:
92143
return getattr(self.gui, "workspace_dir", None)
93144

94-
def _setup_tab_ark(self) -> None:
95-
tab = QDialog(self)
96-
lay = QVBoxLayout(tab)
97-
lay.setSpacing(6)
98-
title = QLabel(self.gui.tr("ARK_Main_Config.yml", "ARK_Main_Config.yml"))
99-
lay.addWidget(title)
100-
101-
self.ark_edit = QPlainTextEdit(tab)
102-
self.ark_edit.setPlaceholderText(
103-
self.gui.tr("Sélectionnez un workspace pour éditer ARK_Main_Config.yml", "Select a workspace to edit ARK_Main_Config.yml")
145+
def _make_editor(self, parent) -> QPlainTextEdit:
146+
edit = QPlainTextEdit(parent)
147+
edit.setFont(QFont("Consolas", 10))
148+
edit.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
149+
return edit
150+
151+
def _show_diff(self, title: str, before: str, after: str) -> None:
152+
diff = "\n".join(
153+
difflib.unified_diff(
154+
before.splitlines(),
155+
after.splitlines(),
156+
fromfile="original",
157+
tofile="modifié",
158+
lineterm="",
159+
)
104160
)
105-
lay.addWidget(self.ark_edit, 1)
106-
107-
btns = QHBoxLayout()
108-
btn_reload = QPushButton(self.gui.tr("Recharger", "Reload"))
109-
btn_save = QPushButton(self.gui.tr("Enregistrer", "Save"))
110-
btn_open = QPushButton(self.gui.tr("Ouvrir fichier…", "Open file…"))
111-
btns.addWidget(btn_reload)
112-
btns.addWidget(btn_save)
113-
btns.addStretch(1)
114-
btns.addWidget(btn_open)
115-
lay.addLayout(btns)
116-
117-
btn_reload.clicked.connect(self._load_ark_config)
118-
btn_save.clicked.connect(self._save_ark_config)
119-
btn_open.clicked.connect(lambda: self._open_any_file(self.ark_edit))
120-
121-
self.tabs.addTab(tab, self.gui.tr("ARK Config", "ARK Config"))
122-
self._load_ark_config()
123-
124-
def _setup_tab_bcasl(self) -> None:
161+
if not diff.strip():
162+
QMessageBox.information(self, title, self.gui.tr("Aucune différence.", "No differences."))
163+
return
164+
dlg = QDialog(self)
165+
dlg.setWindowTitle(title)
166+
dlg.resize(900, 600)
167+
lay = QVBoxLayout(dlg)
168+
view = QPlainTextEdit(dlg)
169+
view.setReadOnly(True)
170+
view.setFont(QFont("Consolas", 10))
171+
view.setPlainText(diff)
172+
lay.addWidget(view)
173+
btn = QPushButton(self.gui.tr("Fermer", "Close"))
174+
btn.clicked.connect(dlg.close)
175+
lay.addWidget(btn)
176+
dlg.exec()
177+
178+
def _build_tab(
179+
self,
180+
label: str,
181+
path_getter: Callable[[], str | None],
182+
is_yaml: bool,
183+
tab_title: str,
184+
) -> tuple[QDialog, QPlainTextEdit, QLabel]:
125185
tab = QDialog(self)
126186
lay = QVBoxLayout(tab)
127187
lay.setSpacing(6)
128-
title = QLabel(self.gui.tr("bcasl.yml", "bcasl.yml"))
129-
lay.addWidget(title)
130-
131-
self.bcasl_edit = QPlainTextEdit(tab)
132-
self.bcasl_edit.setPlaceholderText(
133-
self.gui.tr("Sélectionnez un workspace pour éditer bcasl.yml", "Select a workspace to edit bcasl.yml")
134-
)
135-
lay.addWidget(self.bcasl_edit, 1)
136188

137-
btns = QHBoxLayout()
138-
btn_reload = QPushButton(self.gui.tr("Recharger", "Reload"))
139-
btn_save = QPushButton(self.gui.tr("Enregistrer", "Save"))
140-
btn_open = QPushButton(self.gui.tr("Ouvrir fichier…", "Open file…"))
141-
btns.addWidget(btn_reload)
142-
btns.addWidget(btn_save)
143-
btns.addStretch(1)
144-
btns.addWidget(btn_open)
145-
lay.addLayout(btns)
189+
title = QLabel(label)
190+
lay.addWidget(title)
146191

147-
btn_reload.clicked.connect(self._load_bcasl_config)
148-
btn_save.clicked.connect(self._save_bcasl_config)
149-
btn_open.clicked.connect(lambda: self._open_any_file(self.bcasl_edit))
192+
path_label = QLabel("")
193+
path_label.setStyleSheet("color: #888;")
194+
lay.addWidget(path_label)
150195

151-
self.tabs.addTab(tab, self.gui.tr("BCASL Config", "BCASL Config"))
152-
self._load_bcasl_config()
196+
editor = self._make_editor(tab)
197+
lay.addWidget(editor, 1)
153198

154-
def _setup_tab_pref(self) -> None:
155-
tab = QDialog(self)
156-
lay = QVBoxLayout(tab)
157-
lay.setSpacing(6)
158-
title = QLabel(self.gui.tr("Préférences Workspace (.ark/pref.json)", "Workspace Preferences (.ark/pref.json)"))
159-
lay.addWidget(title)
160-
161-
self.pref_edit = QPlainTextEdit(tab)
162-
self.pref_edit.setPlaceholderText(
163-
self.gui.tr("Sélectionnez un workspace pour éditer .ark/pref.json", "Select a workspace to edit .ark/pref.json")
164-
)
165-
lay.addWidget(self.pref_edit, 1)
199+
_SimpleHighlighter(editor.document(), "yaml" if is_yaml else "json")
166200

167201
btns = QHBoxLayout()
168202
btn_reload = QPushButton(self.gui.tr("Recharger", "Reload"))
169203
btn_save = QPushButton(self.gui.tr("Enregistrer", "Save"))
204+
btn_diff = QPushButton(self.gui.tr("Voir diff", "View diff"))
170205
btn_open = QPushButton(self.gui.tr("Ouvrir fichier…", "Open file…"))
171206
btns.addWidget(btn_reload)
172207
btns.addWidget(btn_save)
208+
btns.addWidget(btn_diff)
173209
btns.addStretch(1)
174210
btns.addWidget(btn_open)
175211
lay.addLayout(btns)
176212

177-
btn_reload.clicked.connect(self._load_pref)
178-
btn_save.clicked.connect(self._save_pref)
179-
btn_open.clicked.connect(lambda: self._open_any_file(self.pref_edit))
213+
def _load():
214+
path = path_getter()
215+
if not path:
216+
editor.setPlainText("")
217+
path_label.setText("")
218+
return
219+
path_label.setText(path)
220+
editor.setPlainText(_read_text(path))
221+
222+
def _save():
223+
path = path_getter()
224+
if not path:
225+
return
226+
text = editor.toPlainText()
227+
if text.strip():
228+
if is_yaml and not _safe_parse_yaml(text):
229+
QMessageBox.warning(self, self.gui.tr("Erreur", "Error"), self.gui.tr("YAML invalide.", "Invalid YAML."))
230+
return
231+
if not is_yaml and not _safe_parse_json(text):
232+
QMessageBox.warning(self, self.gui.tr("Erreur", "Error"), self.gui.tr("JSON invalide.", "Invalid JSON."))
233+
return
234+
_write_text(path, text)
235+
236+
def _diff():
237+
path = path_getter()
238+
if not path:
239+
return
240+
before = _read_text(path)
241+
after = editor.toPlainText()
242+
self._show_diff(self.gui.tr("Diff du fichier", "File diff"), before, after)
243+
244+
def _open_any():
245+
path, _ = QFileDialog.getOpenFileName(self, self.gui.tr("Ouvrir un fichier", "Open file"), "", "*.*")
246+
if not path:
247+
return
248+
editor.setPlainText(_read_text(path))
249+
path_label.setText(path)
250+
251+
btn_reload.clicked.connect(_load)
252+
btn_save.clicked.connect(_save)
253+
btn_diff.clicked.connect(_diff)
254+
btn_open.clicked.connect(_open_any)
255+
256+
self.tabs.addTab(tab, tab_title)
257+
_load()
258+
return tab, editor, path_label
180259

181-
self.tabs.addTab(tab, self.gui.tr("Workspace Pref", "Workspace Pref"))
182-
self._load_pref()
183-
184-
def _open_any_file(self, editor: QPlainTextEdit) -> None:
185-
path, _ = QFileDialog.getOpenFileName(self, self.gui.tr("Ouvrir un fichier", "Open file"), "", "*.*")
186-
if not path:
187-
return
188-
editor.setPlainText(_read_text(path))
189-
190-
def _load_ark_config(self) -> None:
191-
ws = self._workspace_dir()
192-
if not ws:
193-
return
194-
path = os.path.join(ws, "ARK_Main_Config.yml")
195-
self.ark_edit.setPlainText(_read_text(path))
196-
197-
def _save_ark_config(self) -> None:
198-
ws = self._workspace_dir()
199-
if not ws:
200-
return
201-
text = self.ark_edit.toPlainText()
202-
if text.strip() and not _safe_parse_yaml(text):
203-
QMessageBox.warning(self, self.gui.tr("Erreur", "Error"), self.gui.tr("YAML invalide.", "Invalid YAML."))
204-
return
205-
path = os.path.join(ws, "ARK_Main_Config.yml")
206-
_write_text(path, text)
207-
208-
def _load_bcasl_config(self) -> None:
209-
ws = self._workspace_dir()
210-
if not ws:
211-
return
212-
path = os.path.join(ws, "bcasl.yml")
213-
self.bcasl_edit.setPlainText(_read_text(path))
214-
215-
def _save_bcasl_config(self) -> None:
260+
def _setup_tab_ark(self) -> None:
216261
ws = self._workspace_dir()
217-
if not ws:
218-
return
219-
text = self.bcasl_edit.toPlainText()
220-
if text.strip() and not _safe_parse_yaml(text):
221-
QMessageBox.warning(self, self.gui.tr("Erreur", "Error"), self.gui.tr("YAML invalide.", "Invalid YAML."))
222-
return
223-
path = os.path.join(ws, "bcasl.yml")
224-
_write_text(path, text)
262+
self._build_tab(
263+
self.gui.tr("ARK_Main_Config.yml", "ARK_Main_Config.yml"),
264+
lambda: os.path.join(ws, "ARK_Main_Config.yml") if ws else None,
265+
True,
266+
self.gui.tr("ARK Config", "ARK Config"),
267+
)
225268

226-
def _load_pref(self) -> None:
269+
def _setup_tab_bcasl(self) -> None:
227270
ws = self._workspace_dir()
228-
if not ws:
229-
return
230-
path = os.path.join(ws, ".ark", "pref.json")
231-
self.pref_edit.setPlainText(_read_text(path))
271+
self._build_tab(
272+
self.gui.tr("bcasl.yml", "bcasl.yml"),
273+
lambda: os.path.join(ws, "bcasl.yml") if ws else None,
274+
True,
275+
self.gui.tr("BCASL Config", "BCASL Config"),
276+
)
232277

233-
def _save_pref(self) -> None:
278+
def _setup_tab_pref(self) -> None:
234279
ws = self._workspace_dir()
235-
if not ws:
236-
return
237-
text = self.pref_edit.toPlainText()
238-
if text.strip() and not _safe_parse_json(text):
239-
QMessageBox.warning(self, self.gui.tr("Erreur", "Error"), self.gui.tr("JSON invalide.", "Invalid JSON."))
240-
return
241-
path = os.path.join(ws, ".ark", "pref.json")
242-
_write_text(path, text)
280+
self._build_tab(
281+
self.gui.tr("Préférences Workspace (.ark/pref.json)", "Workspace Preferences (.ark/pref.json)"),
282+
lambda: os.path.join(ws, ".ark", "pref.json") if ws else None,
283+
False,
284+
self.gui.tr("Workspace Pref", "Workspace Pref"),
285+
)

0 commit comments

Comments
 (0)