diff --git a/kdevplatform/util/kdevstringhandler.cpp b/kdevplatform/util/kdevstringhandler.cpp index 72ce174879..6b162bedac 100644 --- a/kdevplatform/util/kdevstringhandler.cpp +++ b/kdevplatform/util/kdevstringhandler.cpp @@ -1,244 +1,244 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. This file mostly code takes from Qt's QSettings class, the copyright header from that file follows: **************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** **************************************************************************** */ #include "kdevstringhandler.h" #include #include #include #include #include #include #include #include namespace KDevelop { -QString joinWithEscaping(const QStringList& input, const QChar& joinchar, const QChar& escapechar) +QString joinWithEscaping(const QStringList& input, QChar joinchar, QChar escapechar) { QStringList tmp = input; return tmp.replaceInStrings(joinchar, QString(joinchar) + QString(escapechar)).join(joinchar); } -QStringList splitWithEscaping(const QString& input, const QChar& splitchar, const QChar& escapechar) +QStringList splitWithEscaping(const QString& input, QChar splitchar, QChar escapechar) { enum State { Normal, SeenEscape } state; state = Normal; QStringList result; QString currentstring; for (int i = 0; i < input.size(); i++) { switch (state) { case Normal: if (input[i] == escapechar) { state = SeenEscape; } else if (input[i] == splitchar) { result << currentstring; currentstring.clear(); } else { currentstring += input[i]; } break; case SeenEscape: currentstring += input[i]; state = Normal; break; } } if (!currentstring.isEmpty()) { result << currentstring; } return result; } QVariant stringToQVariant(const QString& s) { // Taken from qsettings.cpp, stringToVariant() if (s.startsWith(QLatin1Char('@'))) { if (s.endsWith(QLatin1Char(')'))) { if (s.startsWith(QLatin1String("@Variant("))) { QByteArray a(s.toLatin1().mid(9)); QDataStream stream(&a, QIODevice::ReadOnly); stream.setVersion(QDataStream::Qt_4_4); QVariant result; stream >> result; return result; } } } return QVariant(); } QString qvariantToString(const QVariant& variant) { // Taken from qsettings.cpp, variantToString() QByteArray a; { QDataStream s(&a, QIODevice::WriteOnly); s.setVersion(QDataStream::Qt_4_4); s << variant; } QString result = QStringLiteral("@Variant(") + QString::fromLatin1(a.constData(), a.size()) + QLatin1Char(')'); return result; } QString htmlToPlainText(const QString& s, HtmlToPlainTextMode mode) { switch (mode) { case FastMode: { QString result(s); result.remove(QRegExp(QStringLiteral("<[^>]+>"))); return result; } case CompleteMode: { QTextDocument doc; doc.setHtml(s); return doc.toPlainText(); } } return QString(); // never reached } } QString KDevelop::stripAnsiSequences(const QString& str) { if (str.isEmpty()) { return QString(); // fast path } enum { PLAIN, ANSI_START, ANSI_CSI, ANSI_SEQUENCE, ANSI_WAITING_FOR_ST, ANSI_ST_STARTED } state = PLAIN; QString result; result.reserve(str.count()); for (const QChar c : str) { const auto val = c.unicode(); switch (state) { case PLAIN: if (val == 27) // 'ESC' state = ANSI_START; else if (val == 155) // equivalent to 'ESC'-'[' state = ANSI_CSI; else result.append(c); break; case ANSI_START: if (val == 91) // [ state = ANSI_CSI; else if (val == 80 || val == 93 || val == 94 || val == 95) // 'P', ']', '^' and '_' state = ANSI_WAITING_FOR_ST; else if (val >= 64 && val <= 95) state = PLAIN; else state = ANSI_SEQUENCE; break; case ANSI_CSI: if (val >= 64 && val <= 126) // Anything between '@' and '~' state = PLAIN; break; case ANSI_SEQUENCE: if (val >= 64 && val <= 95) // Anything between '@' and '_' state = PLAIN; break; case ANSI_WAITING_FOR_ST: if (val == 7) // 'BEL' state = PLAIN; else if (val == 27) // 'ESC' state = ANSI_ST_STARTED; break; case ANSI_ST_STARTED: if (val == 92) // '\' state = PLAIN; else state = ANSI_WAITING_FOR_ST; break; } } return result; } void KDevelop::normalizeLineEndings(QByteArray& text) { for (int i = 0, s = text.size(); i < s; ++i) { if (text[i] != '\r') { continue; } if (i + 1 < s && text[i + 1] == '\n') { text.remove(i, 1); } else { text[i] = '\n'; } } } diff --git a/kdevplatform/util/kdevstringhandler.h b/kdevplatform/util/kdevstringhandler.h index 3e1a2d7562..fd76d26870 100644 --- a/kdevplatform/util/kdevstringhandler.h +++ b/kdevplatform/util/kdevstringhandler.h @@ -1,77 +1,77 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_KDEVSTRINGHANDLER_H #define KDEVPLATFORM_KDEVSTRINGHANDLER_H #include "utilexport.h" class QString; class QByteArray; class QChar; class QStringList; class QVariant; namespace KDevelop { -KDEVPLATFORMUTIL_EXPORT QStringList splitWithEscaping(const QString& input, const QChar& splitChar, - const QChar& escapeChar); -KDEVPLATFORMUTIL_EXPORT QString joinWithEscaping(const QStringList& input, const QChar& joinChar, - const QChar& escapeChar); +KDEVPLATFORMUTIL_EXPORT QStringList splitWithEscaping(const QString& input, QChar splitChar, + QChar escapeChar); +KDEVPLATFORMUTIL_EXPORT QString joinWithEscaping(const QStringList& input, QChar joinChar, + QChar escapeChar); /** * convert the @p variant into a string which can then be stored * easily in a KConfig entry. This supports any QVariant type (including custom types) * for which there is a QDataStream operator defined * @returns a QString containing the data from the QVariant. */ KDEVPLATFORMUTIL_EXPORT QString qvariantToString(const QVariant& variant); /** * convert the @p s into a QVariant, usually the string is read from KConfig. * This supports any QVariant type (including custom types) * for which there is a QDataStream operator defined * @returns a QVariant created from the bytearray */ KDEVPLATFORMUTIL_EXPORT QVariant stringToQVariant(const QString& s); enum HtmlToPlainTextMode { FastMode, /**< Fast (conversion via regular expression) */ CompleteMode, /**< Slower, but with expected behavior (conversion via QTextDocument::toPlainText). This also replaces
with newline chars, for example. */ }; /** * Strip HTML tags from string @p s * * @return String no longer containing any HTML tags */ KDEVPLATFORMUTIL_EXPORT QString htmlToPlainText(const QString& s, HtmlToPlainTextMode mode = FastMode); /** * Strip ANSI sequences from string @p str */ KDEVPLATFORMUTIL_EXPORT QString stripAnsiSequences(const QString& str); /** * Replace all occurrences of "\r" or "\r\n" in @p text with "\n". */ KDEVPLATFORMUTIL_EXPORT void normalizeLineEndings(QByteArray& text); } #endif // KDEVPLATFORM_KDEVSTRINGHANDLER_H diff --git a/plugins/cmake/cmakecodecompletionmodel.cpp b/plugins/cmake/cmakecodecompletionmodel.cpp index ed80f5a7e8..ec72607697 100644 --- a/plugins/cmake/cmakecodecompletionmodel.cpp +++ b/plugins/cmake/cmakecodecompletionmodel.cpp @@ -1,316 +1,316 @@ /* KDevelop CMake Support * * Copyright 2008 Aleix Pol * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "cmakecodecompletionmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include "cmakeutils.h" #include "icmakedocumentation.h" #include #include #include #include using namespace KTextEditor; using namespace KDevelop; QVector CMakeCodeCompletionModel::s_commands; CMakeCodeCompletionModel::CMakeCodeCompletionModel(QObject* parent) : CodeCompletionModel(parent) , m_inside(false) {} bool isFunction(const Declaration* decl) { return decl->abstractType().cast(); } -bool isPathChar(const QChar& c) +bool isPathChar(QChar c) { return c.isLetterOrNumber() || c == QLatin1Char('/') || c == QLatin1Char('.'); } QString escapePath(QString path) { // see https://cmake.org/Wiki/CMake/Language_Syntax#Escapes static const QString toBeEscaped = QStringLiteral("\"()#$^"); for(const QChar &ch : toBeEscaped) { path.replace(ch, QLatin1Char('\\') + ch); } return path; } void CMakeCodeCompletionModel::completionInvoked(View* view, const Range& range, InvocationType invocationType) { beginResetModel(); if(s_commands.isEmpty()) { ICMakeDocumentation* cmakedoc=CMake::cmakeDocumentation(); if(cmakedoc) s_commands=cmakedoc->names(ICMakeDocumentation::Command); } Q_UNUSED(invocationType); m_declarations.clear(); DUChainReadLocker lock(DUChain::lock()); KTextEditor::Document* d=view->document(); TopDUContext* ctx = DUChain::self()->chainForDocument( d->url() ); QString line=d->line(range.end().line()); // m_inside=line.lastIndexOf('(', range.end().column())>=0; m_inside=line.lastIndexOf(QLatin1Char('('), range.end().column()-line.size()-1)>=0; for(int l=range.end().line(); l>=0 && !m_inside; --l) { QString cline=d->line(l); const QStringRef line = cline.leftRef(cline.indexOf(QLatin1Char('#'))); int close=line.lastIndexOf(QLatin1Char(')')), open=line.indexOf(QLatin1Char('(')); if(close>=0 && open>=0) { m_inside=open>close; break; } else if(open>=0) { m_inside=true; break; } else if(close>=0) { m_inside=false; break; } } int numRows = 0; if(m_inside) { Cursor start=range.start(); for(; isPathChar(d->characterAt(start)); start-=Cursor(0,1)) {} start+=Cursor(0,1); QString tocomplete=d->text(Range(start, range.end()-Cursor(0,1))); int lastdir=tocomplete.lastIndexOf(QLatin1Char('/')); QString path = KIO::upUrl(QUrl(d->url())).adjusted(QUrl::StripTrailingSlash).toLocalFile()+QLatin1Char('/'); if(lastdir>=0) { const QStringRef basePath = tocomplete.midRef(0, lastdir); path += basePath; } QDir dir(path); const QFileInfoList paths = dir.entryInfoList(QStringList(tocomplete.midRef(lastdir+1)+QLatin1Char('*')), QDir::AllEntries | QDir::NoDotAndDotDot); m_paths.clear(); m_paths.reserve(paths.size()); for (const QFileInfo& f : paths) { QString currentPath = f.fileName(); if(f.isDir()) currentPath += QLatin1Char('/'); m_paths += currentPath; } numRows += m_paths.count(); } else numRows += s_commands.count(); if(ctx) { const auto list = ctx->allDeclarations( ctx->transformToLocalRevision(KTextEditor::Cursor(range.start())), ctx ); for (const auto& pair : list) { bool func=isFunction(pair.first); if((func && !m_inside) || (!func && m_inside)) m_declarations.append(pair.first); } numRows+=m_declarations.count(); } setRowCount(numRows); endResetModel(); } CMakeCodeCompletionModel::Type CMakeCodeCompletionModel::indexType(int row) const { if(m_inside) { if(row < m_declarations.count()) { KDevelop::DUChainReadLocker lock; Declaration* dec = m_declarations.at(row).declaration(); if (dec && dec->type()) return Target; else return Variable; } else return Path; } else { if(rowidentifier().toString() : i18n("INVALID"); } } } else if(role==Qt::DisplayRole && index.column()==CodeCompletionModel::Prefix) { switch(type) { case Command: return i18n("Command"); case Variable: return i18n("Variable"); case Macro: return i18n("Macro"); case Path: return i18n("Path"); case Target: return i18n("Target"); } } else if(role==Qt::DecorationRole && index.column()==CodeCompletionModel::Icon) { switch(type) { case Command: return QIcon::fromTheme(QStringLiteral("code-block")); case Variable: return QIcon::fromTheme(QStringLiteral("code-variable")); case Macro: return QIcon::fromTheme(QStringLiteral("code-function")); case Target: return QIcon::fromTheme(QStringLiteral("system-run")); case Path: { QUrl url = QUrl::fromUserInput(m_paths[index.row()-m_declarations.size()]); QString iconName; if (url.isLocalFile()) { // don't read contents even if it is a local file iconName = QMimeDatabase().mimeTypeForFile(url.toLocalFile(), QMimeDatabase::MatchExtension).iconName(); } else { // remote always only looks at the extension iconName = QMimeDatabase().mimeTypeForUrl(url).iconName(); } return QIcon::fromTheme(iconName); } } } else if(role==Qt::DisplayRole && index.column()==CodeCompletionModel::Arguments) { switch(type) { case Variable: case Command: case Path: case Target: break; case Macro: { DUChainReadLocker lock(DUChain::lock()); int pos=index.row(); FunctionType::Ptr func; if(m_declarations[pos].data()) func = m_declarations[pos].data()->abstractType().cast(); if(!func) return QVariant(); QStringList args; const auto arguments = func->arguments(); args.reserve(arguments.size()); for (const AbstractType::Ptr& t : arguments) { DelayedType::Ptr delay = t.cast(); args.append(delay ? delay->identifier().toString() : i18n("wrong")); } return QString(QLatin1Char('(')+args.join(QStringLiteral(", "))+QLatin1Char(')')); } } } return QVariant(); } void CMakeCodeCompletionModel::executeCompletionItem(View* view, const Range& word, const QModelIndex& idx) const { Document* document = view->document(); const int row = idx.row(); switch(indexType(row)) { case Path: { Range r=word; for (QChar c=document->characterAt(r.end()); c.isLetterOrNumber() || c==QLatin1Char('.'); c=document->characterAt(r.end())) { r.setEnd(KTextEditor::Cursor(r.end().line(), r.end().column()+1)); } QString path = data(index(row, Name, QModelIndex())).toString(); document->replaceText(r, escapePath(path)); } break; case Macro: case Command: { QString code=data(index(row, Name, QModelIndex())).toString(); if (!document->line(word.start().line()).contains(QLatin1Char('('))) code.append(QLatin1Char('(')); document->replaceText(word, code); } break; case Variable: { Range r=word, prefix(word.start()-Cursor(0,2), word.start()); QString bef=document->text(prefix); if(r.start().column()>=2 && bef==QLatin1String("${")) r.setStart(KTextEditor::Cursor(r.start().line(), r.start().column()-2)); QString code = QLatin1String("${")+data(index(row, Name, QModelIndex())).toString(); if(document->characterAt(word.end())!=QLatin1Char('}')) code += QLatin1Char('}'); document->replaceText(r, code); } break; case Target: document->replaceText(word, data(index(row, Name, QModelIndex())).toString()); break; } }