[education/labplot] src/frontend/script: [scripting] detect more errors and better handling of them.

Alexander Semke <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 89f32b3b18eebf448a1fa37bbc4a59cf82ca0f08 by Alexander Semke.
Committed on 28/07/2026 at 14:43.
Pushed by asemke into branch 'master'.

[scripting] detect more errors and better handling of them.

M  +54   -20   src/frontend/script/ScriptEditor.cpp

https://invent.kde.org/education/labplot/-/commit/89f32b3b18eebf448a1fa37bbc4a59cf82ca0f08

diff --git a/src/frontend/script/ScriptEditor.cpp b/src/frontend/script/ScriptEditor.cpp
index f96d8f7e55..8c30e3553b 100644
--- a/src/frontend/script/ScriptEditor.cpp
+++ b/src/frontend/script/ScriptEditor.cpp
@@ -188,19 +188,18 @@ void ScriptEditor::clearOutput() {
 }
 
 QString ScriptEditor::processOutputText(bool isErr, const QString& text) {
-	QString html;
-	QString escapedText = text.toHtmlEscaped();
+	DEBUG(Q_FUNC_INFO << ", isErr = " << isErr << ", text = '" << text.toStdString() << "'")
 
-	// Detect and format Python traceback patterns
-	// Pattern 1: "File "<string>", line 5, in <module>"
+	// Detect and format Python traceback patterns BEFORE HTML escaping
+	// Pattern 1: "File "<string>", line 5, in <module>" or "File "somefile.py", line 5"
 	static QRegularExpression fileLinePattern(
 		QStringLiteral(R"(File\s+"[^"]*",\s+line\s+(\d+))"),
 		QRegularExpression::CaseInsensitiveOption
 	);
 
-	// Pattern 2: Error/Exception names (e.g., "NameError:", "ValueError:", "TypeError:")
+	// Pattern 2: Error/Exception names (e.g., "NameError:", "ValueError:", "IndentationError:", "Traceback:")
 	static QRegularExpression errorPattern(
-		QStringLiteral(R"(^(\w+Error|\w+Exception|Traceback):)"),
+		QStringLiteral(R"(^(\w+Error|\w+Exception|Traceback)(\s*\(.*\))?:)"),
 		QRegularExpression::MultilineOption
 	);
 
@@ -210,31 +209,64 @@ QString ScriptEditor::processOutputText(bool isErr, const QString& text) {
 		QRegularExpression::CaseInsensitiveOption
 	);
 
-	QString processedText = escapedText;
+	// Parse line references BEFORE escaping HTML
+	struct LineMatch {
+		int start;
+		int length;
+		QString lineNum;
+		QString matchedText;
+	};
 
-	// Add clickable links for line references
-	QRegularExpressionMatchIterator it = fileLinePattern.globalMatch(processedText);
-	int offset = 0;
+	QList<LineMatch> matches;
+	QRegularExpressionMatchIterator it = fileLinePattern.globalMatch(text);
 	while (it.hasNext()) {
 		QRegularExpressionMatch match = it.next();
-		QString lineNum = match.captured(1);
-		QString matchedText = match.captured(0);
-		QString link = QStringLiteral("<a href=\"line:%1\">%2</a>").arg(lineNum, matchedText);
-
-		int startPos = match.capturedStart(0) + offset;
-		int length = match.capturedLength(0);
-		processedText.replace(startPos, length, link);
-		offset += link.length() - length;
+		LineMatch lm;
+		lm.start = match.capturedStart(0);
+		lm.length = match.capturedLength(0);
+		lm.lineNum = match.captured(1);
+		lm.matchedText = match.captured(0);
+		matches.append(lm);
+
+		DEBUG(Q_FUNC_INFO << ", Found line reference: " << lm.matchedText.toStdString() << " at line " << lm.lineNum.toStdString())
+	}
+
+	// Now escape HTML
+	QString processedText = text.toHtmlEscaped();
+
+	// Replace matches in reverse order to preserve positions
+	for (int i = matches.size() - 1; i >= 0; --i) {
+		const LineMatch& lm = matches[i];
+
+		// Escape the matched text for HTML
+		QString escapedMatch = lm.matchedText.toHtmlEscaped();
+
+		// Create clickable link with blue underlined style
+		QString link = QStringLiteral("<a href=\"line:%1\" style=\"color: #1976d2; text-decoration: underline;\">%2</a>")
+			.arg(lm.lineNum, escapedMatch);
+
+		// Replace in the escaped text
+		int startPos = lm.start;
+		int length = lm.matchedText.length();
+
+		// Find the escaped version in processedText
+		QString searchText = lm.matchedText.toHtmlEscaped();
+		int pos = processedText.indexOf(searchText, startPos);
+		if (pos != -1) {
+			processedText.replace(pos, searchText.length(), link);
+		}
 	}
 
+	QString html;
+
 	// Apply color formatting
 	if (isErr) {
 		// Error output - make it red
 		html = QStringLiteral("<span style=\"color: #d32f2f;\">%1</span>").arg(processedText);
-	} else if (warningPattern.match(processedText).hasMatch()) {
+	} else if (warningPattern.match(text).hasMatch()) {
 		// Warning - make it orange
 		html = QStringLiteral("<span style=\"color: #f57c00;\">%1</span>").arg(processedText);
-	} else if (errorPattern.match(processedText).hasMatch()) {
+	} else if (errorPattern.match(text).hasMatch()) {
 		// Exception names - make them red
 		html = QStringLiteral("<span style=\"color: #d32f2f;\">%1</span>").arg(processedText);
 	} else {
@@ -242,6 +274,8 @@ QString ScriptEditor::processOutputText(bool isErr, const QString& text) {
 		html = processedText;
 	}
 
+	DEBUG(Q_FUNC_INFO << ", Generated HTML: " << html.toStdString())
+
 	return html;
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.