[utilities/kate] addons/openlink: Improve file link matching

Waqar Ahmed <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit d9c356f2ff75071c99de56edc0cfc1d6c81c515e by Waqar Ahmed.
Committed on 04/08/2026 at 09:13.
Pushed by waqar into branch 'master'.

Improve file link matching

Skip punctuation at the beginning and end

M  +5    -0    addons/openlink/linkmatchtest.cpp
M  +30   -18   addons/openlink/matchers.h

https://invent.kde.org/utilities/kate/-/commit/d9c356f2ff75071c99de56edc0cfc1d6c81c515e

diff --git a/addons/openlink/linkmatchtest.cpp b/addons/openlink/linkmatchtest.cpp
index 13d6f6ae08..ecbddac871 100644
--- a/addons/openlink/linkmatchtest.cpp
+++ b/addons/openlink/linkmatchtest.cpp
@@ -30,6 +30,7 @@ private Q_SLOTS:
         QTest::addColumn<std::vector<OpenLinkRange>>("expected");
 
         using R = std::vector<OpenLinkRange>;
+
         QTest::addRow("1") << "Line has https://google.com"
                            << R{OpenLinkRange{.start = 9, .end = 27, .link = QStringLiteral("https://google.com"), .type = HttpLink}};
         QTest::addRow("2") << "Line has https://google.com and https://google.com"
@@ -106,6 +107,10 @@ private Q_SLOTS:
         QTest::addRow("22") << QStringLiteral("<https://cullmann.dev> xxx <https://hello.dev>")
                             << R{OpenLinkRange{.start = 1, .end = 21, .link = QStringLiteral("https://cullmann.dev"), .type = HttpLink},
                                  OpenLinkRange{.start = 28, .end = 45, .link = QStringLiteral("https://hello.dev"), .type = HttpLink}};
+
+        // something like: (/home/user/projects/file/Extensions/xyz/File.cpp:713,
+        QTest::addRow("23") << QLatin1String("(%1:713,").arg(filePath)
+                            << R{OpenLinkRange{.start = 1, .end = 1 + fileLen + 4, .link = filePath, .startPos = {713, 0}, .type = FileLink}};
     }
 
     void test()
diff --git a/addons/openlink/matchers.h b/addons/openlink/matchers.h
index 50164b47eb..a97e7a989c 100644
--- a/addons/openlink/matchers.h
+++ b/addons/openlink/matchers.h
@@ -87,8 +87,22 @@ static KTextEditor::Cursor parseLineCol(QStringView &link)
     return KTextEditor::Cursor(line, col);
 }
 
+static void pushLink(int s, int e, QStringView line, std::vector<OpenLinkRange> *outColumnRanges)
+{
+    QStringView linkView(QStringView(line).mid(s, e - s));
+    KTextEditor::Cursor c = parseLineCol(linkView);
+    QString link = linkView.toString();
+    if (QFileInfo(link).isFile()) {
+        outColumnRanges->push_back({.start = s, .end = e, .link = link, .startPos = c, .type = FileLink});
+    }
+}
+
 static void matchFilePaths(const QString &line, std::vector<OpenLinkRange> *outColumnRanges)
 {
+    const auto isPrevCharAcceptable = [](QChar c) {
+        return c == u' ' || c == u'"' || c == u'(' || c == u')' || c == u'=';
+    };
+
 #ifdef Q_OS_WIN
     const auto isValidDriveLetter = [](QChar letter) {
         return (letter.isLetter() && letter.toUpper() >= u'A' && letter.toUpper() <= u'Z');
@@ -112,8 +126,8 @@ static void matchFilePaths(const QString &line, std::vector<OpenLinkRange> *outC
             s = isAbsoloutePath ? s - 2 : s - 1;
             s = isDotDotRelativePath ? s - 1 : s;
 
-            // must be preceded by a space or d-quote
-            if (s != 0 && line[s - 1] != u'"' && line[s - 1] != u' ') {
+            // must be preceded by a space or a symbol
+            if (s != 0 && !isPrevCharAcceptable(line[s - 1])) {
                 s = orignalS + 1;
                 continue;
             }
@@ -123,6 +137,11 @@ static void matchFilePaths(const QString &line, std::vector<OpenLinkRange> *outC
             if (!matchNextQuote) {
                 e = line.indexOf(QLatin1String(" "), s);
                 e = e == -1 ? line.size() : e;
+
+                // Strip trailing punctuation
+                while (e > s && (line[e - 1] == u',' || line[e - 1] == u'.')) {
+                    e--;
+                }
             } else {
                 e = line.indexOf(u'"', s);
                 if (e == -1) {
@@ -137,15 +156,9 @@ static void matchFilePaths(const QString &line, std::vector<OpenLinkRange> *outC
             }
 
             if (e != -1) {
-                QStringView linkView(QStringView(line).mid(s, e - s));
-                KTextEditor::Cursor c = parseLineCol(linkView);
-                QString link = linkView.toString();
-                if (QFileInfo(link).isFile()) {
-                    outColumnRanges->push_back({s, e, link, c, FileLink});
-                }
+                pushLink(s, e, line, outColumnRanges);
             }
             s = e;
-            continue;
         } else {
             s++;
         }
@@ -158,17 +171,21 @@ static void matchFilePaths(const QString &line, std::vector<OpenLinkRange> *outC
         if (s == -1) {
             break;
         }
-        // must be preceded by a space or d-quote
-        if (s != 0 && line[s - 1] != u'"' && line[s - 1] != u' ') {
+        // must be preceded by a space or a symbol
+        if (s != 0 && !isPrevCharAcceptable(line[s - 1])) {
             s++;
             continue;
         }
-
         const bool matchNextQuote = s > 0 && line[s - 1] == u'"'; // last char is quote?
         int e = -1;
         if (!matchNextQuote) {
             e = line.indexOf(QLatin1String(" "), s);
             e = e == -1 ? line.size() : e;
+
+            // Strip trailing punctuation
+            while (e > s && (line[e - 1] == u',' || line[e - 1] == u'.')) {
+                e--;
+            }
         } else {
             e = line.indexOf(u'"', s);
             if (e == -1) {
@@ -177,12 +194,7 @@ static void matchFilePaths(const QString &line, std::vector<OpenLinkRange> *outC
         }
 
         if (e != -1) {
-            QStringView linkView(QStringView(line).mid(s, e - s));
-            KTextEditor::Cursor c = parseLineCol(linkView);
-            QString link = linkView.toString();
-            if (QFileInfo(link).isFile()) {
-                outColumnRanges->push_back({s, e, link, c, FileLink});
-            }
+            pushLink(s, e, line, outColumnRanges);
         }
         s = e;
     }
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.