File indexing completed on 2024-04-28 05:49:36

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2015 Milian Wolff <mail@milianw.de>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include <KTextEditor/Cursor>
0010 
0011 #include <QDir>
0012 #include <QRegularExpression>
0013 #include <QString>
0014 #include <QUrl>
0015 #include <QUrlQuery>
0016 
0017 /**
0018  * Represents a file to be opened, consisting of its URL and the cursor to jump to.
0019  */
0020 class UrlInfo
0021 {
0022 public:
0023     /**
0024      * Parses a file path argument and determines its line number and column and full path,
0025      * we use absolute file paths because we will e.g. pass this over dbus to other processes.
0026      *
0027      * @param path path passed on e.g. command line to parse into an URL
0028      */
0029     UrlInfo(QString path)
0030         : cursor(KTextEditor::Cursor::invalid())
0031     {
0032         QString currentDirPath = QDir::current().absolutePath();
0033         if (!currentDirPath.endsWith(QLatin1Char('/'))) {
0034             currentDirPath += QLatin1Char('/');
0035         }
0036 
0037         // QDir::isAbsolutePath()/absoluteFilePath() treat paths starting with ':' as Qt
0038         // Resource (qrc) paths, and consider them absolute
0039         if (!path.startsWith(QLatin1Char(':')) && QDir::isAbsolutePath(path)) {
0040             if (QFile::exists(path)) { // Existing absolute path, no cursor can be detected
0041                 url = QUrl::fromLocalFile(path);
0042                 return;
0043             }
0044         } else {
0045             // Relative path, maybe starting with ':'; we concatenate the absolute path manually
0046             QString absolutePath = currentDirPath + path;
0047 
0048             if (QFile::exists(absolutePath)) { // Existing absolute path, no cursor can be detected
0049                 url = QUrl::fromLocalFile(absolutePath);
0050                 return;
0051             }
0052         }
0053 
0054         /**
0055          * ok, the path as is, is no existing file, now, cut away :xx:yy stuff as cursor
0056          * this will make test:50 to test with line 50
0057          */
0058         const auto match = QRegularExpression(QStringLiteral(":(\\d+)(?::(\\d+))?:?$")).match(path);
0059         if (match.isValid()) {
0060             /**
0061              * cut away the line/column specification from the path
0062              */
0063             path.chop(match.capturedLength());
0064 
0065             /**
0066              * After cutting the line/column part, if the file exists make "path" absolute.
0067              * Note that we can't rely on QUrl::fromUserInput() because of paths starting with
0068              * ':', see comment above about QDir::isAbsolutePath().
0069              */
0070             const QString absolutePath = currentDirPath + path;
0071             if (QFile::exists(absolutePath)) {
0072                 path = absolutePath;
0073             }
0074 
0075             /**
0076              * set right cursor position
0077              * don't use an invalid column when the line is valid
0078              */
0079             const int line = match.captured(1).toInt() - 1;
0080             const int column = qMax(0, match.captured(2).toInt() - 1);
0081             cursor.setPosition(line, column);
0082         }
0083 
0084         /**
0085          * Construct url: "path" has already been made absolute above.
0086          * This should work with:
0087          * - local paths, "/path/to/somefile" becomes file:///path/to/some/file
0088          * - file: urls, file:///path/to/some/file
0089          * - remote urls, e.g. sftp://1.2.3.4:22/path/to/some/file
0090          */
0091         url = QUrl::fromUserInput(path, currentDirPath, QUrl::AssumeLocalFile);
0092 
0093         /**
0094          * Set cursor position if we can extract from URL query string
0095          */
0096         if (url.hasQuery()) {
0097             QUrlQuery urlQuery(url);
0098             QString lineStr = urlQuery.queryItemValue(QStringLiteral("line"));
0099             QString columnStr = urlQuery.queryItemValue(QStringLiteral("column"));
0100 
0101             int line = 0;
0102             int column = 0;
0103             bool setCursor = false;
0104 
0105             if (!lineStr.isEmpty()) {
0106                 line = lineStr.toInt();
0107                 line > 0 && line--;
0108                 setCursor = true;
0109             }
0110 
0111             if (!columnStr.isEmpty()) {
0112                 column = columnStr.toInt();
0113                 column > 0 && column--;
0114                 setCursor = true;
0115             }
0116 
0117             if (setCursor) {
0118                 cursor.setPosition(line, column);
0119             }
0120         }
0121     }
0122 
0123     /**
0124      * url computed out of the passed path
0125      */
0126     QUrl url;
0127 
0128     /**
0129      * initial cursor position, if any found inside the path as line/column specification at the end
0130      */
0131     KTextEditor::Cursor cursor;
0132 };