diff options
Diffstat (limited to 'src')
7 files changed, 404 insertions, 4 deletions
diff --git a/src/plugins/cmakeprojectmanager/cmakeautocompleter.cpp b/src/plugins/cmakeprojectmanager/cmakeautocompleter.cpp new file mode 100644 index 0000000000..c74befd95b --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeautocompleter.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jan Dalheimer <jan@dalheimer.de> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** 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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "cmakeautocompleter.h" + +#include <QTextCursor> +#include <QTextBlock> +#include <QDebug> + +#include <texteditor/tabsettings.h> + +namespace CMakeProjectManager { +namespace Internal { + +CMakeAutoCompleter::CMakeAutoCompleter() +{ + setAutoParenthesesEnabled(true); +} + +bool CMakeAutoCompleter::isInComment(const QTextCursor &cursor) const +{ + // NOTE: This doesn't handle '#' inside quotes, nor multi-line comments + QTextCursor moved = cursor; + moved.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); + if (moved.selectedText().contains(QLatin1Char('#'))) + return true; + else + return false; +} + +bool CMakeAutoCompleter::isInString(const QTextCursor &cursor) const +{ + // NOTE: multiline strings are currently not supported, since they rarely, if ever, seem to be used + QTextCursor moved = cursor; + moved.movePosition(QTextCursor::StartOfLine); + const int positionInLine = cursor.position() - moved.position(); + moved.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); + const QString line = moved.selectedText(); + + bool isEscaped = false; + bool inString = false; + for (int i = 0; i < positionInLine; ++i) { + const QChar c = line.at(i); + if (c == QLatin1Char('\\') && !isEscaped) + isEscaped = true; + else if (c == QLatin1Char('"') && !isEscaped) + inString = !inString; + else + isEscaped = false; + } + return inString; +} + +QString CMakeAutoCompleter::insertMatchingBrace(const QTextCursor &cursor, const QString &text, QChar la, int *skippedChars) const +{ + Q_UNUSED(cursor) + Q_UNUSED(skippedChars); + if (text.isEmpty()) + return QString(); + const QChar current = text.at(0); + switch (current.unicode()) { + case '"': + if (la != current) + return QStringLiteral("\""); + ++*skippedChars; + break; + + case '(': + return QStringLiteral(")"); + + case ')': + if (current == la) + ++*skippedChars; + break; + + default: + break; + } + + return QString(); +} + +int CMakeAutoCompleter::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor, const TextEditor::TabSettings &tabSettings) +{ + const QString line = cursor.block().text().trimmed(); + if (line.contains(QRegExp(QStringLiteral("^(endfunction|endmacro|endif|endforeach|endwhile)\\w*\(")))) + tabSettings.indentLine(cursor.block(), tabSettings.indentationColumn(cursor.block().text())); + return 0; +} + +bool CMakeAutoCompleter::contextAllowsAutoParentheses(const QTextCursor &cursor, const QString &textToInsert) const +{ + if (textToInsert.isEmpty()) + return false; + + const QChar c = textToInsert.at(0); + if (c == QLatin1Char('"') || c == QLatin1Char('(') || c == QLatin1Char(')')) + return !isInComment(cursor); + return false; +} + +bool CMakeAutoCompleter::contextAllowsElectricCharacters(const QTextCursor &cursor) const +{ + return !isInComment(cursor) && !isInString(cursor); +} + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeautocompleter.h b/src/plugins/cmakeprojectmanager/cmakeautocompleter.h new file mode 100644 index 0000000000..25bfa93ac4 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeautocompleter.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jan Dalheimer <jan@dalheimer.de> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** 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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef CMAKEAUTOCOMPLETER_H +#define CMAKEAUTOCOMPLETER_H + +#include "cmake_global.h" + +#include <texteditor/autocompleter.h> + +namespace CMakeProjectManager { +namespace Internal { + +class CMAKE_EXPORT CMakeAutoCompleter : public TextEditor::AutoCompleter +{ +public: + CMakeAutoCompleter(); + virtual ~CMakeAutoCompleter() {} + + bool isInComment(const QTextCursor &cursor) const override; + bool isInString(const QTextCursor &cursor) const override; + QString insertMatchingBrace(const QTextCursor &cursor, const QString &text, QChar la, int *skippedChars) const override; + int paragraphSeparatorAboutToBeInserted(QTextCursor &cursor, const TextEditor::TabSettings &tabSettings) override; + bool contextAllowsAutoParentheses(const QTextCursor &cursor, const QString &textToInsert) const override; + bool contextAllowsElectricCharacters(const QTextCursor &cursor) const override; +}; + +} // namespace Internal +} // namespace CMakeProjectManager + +#endif // CMAKEAUTOCOMPLETER_H diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp index 89990da317..3cdd1eccfc 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp @@ -33,6 +33,8 @@ #include "cmakefilecompletionassist.h" #include "cmakeprojectconstants.h" #include "cmakeproject.h" +#include "cmakeindenter.h" +#include "cmakeautocompleter.h" #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -274,14 +276,17 @@ CMakeEditorFactory::CMakeEditorFactory() setEditorCreator([]() { return new CMakeEditor; }); setEditorWidgetCreator([]() { return new CMakeEditorWidget; }); setDocumentCreator([]() { return new CMakeDocument; }); + setIndenterCreator([]() { return new CMakeIndenter; }); setUseGenericHighlighter(true); setCommentStyle(Utils::CommentDefinition::HashStyle); setCodeFoldingSupported(true); setCompletionAssistProvider(new CMakeFileCompletionAssistProvider); + setAutoCompleterCreator([]() { return new CMakeAutoCompleter; }); setEditorActionHandlers(TextEditorActionHandler::UnCommentSelection - | TextEditorActionHandler::JumpToFileUnderCursor); + | TextEditorActionHandler::JumpToFileUnderCursor + | TextEditorActionHandler::Format); ActionContainer *contextMenu = ActionManager::createMenu(Constants::M_CONTEXT); contextMenu->addAction(ActionManager::command(TextEditor::Constants::JUMP_TO_FILE_UNDER_CURSOR)); diff --git a/src/plugins/cmakeprojectmanager/cmakeindenter.cpp b/src/plugins/cmakeprojectmanager/cmakeindenter.cpp new file mode 100644 index 0000000000..cb424dc17a --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeindenter.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jan Dalheimer <jan@dalheimer.de> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** 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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "cmakeindenter.h" + +#include <QStack> +#include <QDebug> + +#include <texteditor/tabsettings.h> +#include <texteditor/textdocumentlayout.h> + +namespace CMakeProjectManager { +namespace Internal { + +CMakeIndenter::CMakeIndenter() +{ +} + +bool CMakeIndenter::isElectricCharacter(const QChar &ch) const +{ + return ch == QLatin1Char('(') || ch == QLatin1Char(')'); +} + +static bool lineContainsFunction(const QString &line, const QString &function) +{ + const int indexOfFunction = line.indexOf(function); + if (indexOfFunction == -1) + return false; + for (int i = 0; i < indexOfFunction; ++i) { + if (!line.at(i).isSpace()) + return false; + } + for (int i = indexOfFunction + function.size(); i < line.size(); ++i) { + if (line.at(i) == QLatin1Char('(')) + return true; + else if (!line.at(i).isSpace()) + return false; + } + return false; +} +static bool lineStartsBlock(const QString &line) +{ + return lineContainsFunction(line, QStringLiteral("function")) || + lineContainsFunction(line, QStringLiteral("macro")) || + lineContainsFunction(line, QStringLiteral("foreach")) || + lineContainsFunction(line, QStringLiteral("while")) || + lineContainsFunction(line, QStringLiteral("if")) || + lineContainsFunction(line, QStringLiteral("elseif")) || + lineContainsFunction(line, QStringLiteral("else")); +} +static bool lineEndsBlock(const QString &line) +{ + return lineContainsFunction(line, QStringLiteral("endfunction")) || + lineContainsFunction(line, QStringLiteral("endmacro")) || + lineContainsFunction(line, QStringLiteral("endforeach")) || + lineContainsFunction(line, QStringLiteral("endwhile")) || + lineContainsFunction(line, QStringLiteral("endif")) || + lineContainsFunction(line, QStringLiteral("elseif")) || + lineContainsFunction(line, QStringLiteral("else")); +} +static bool lineIsEmpty(const QString &line) +{ + for (const QChar &c : line) { + if (!c.isSpace()) + return false; + } + return true; +} + +static int paranthesesLevel(const QString &line) +{ + const QStringRef beforeComment = line.midRef(0, line.indexOf(QLatin1Char('#'))); + const int opening = beforeComment.count(QLatin1Char('(')); + const int closing = beforeComment.count(QLatin1Char(')')); + if (opening == closing) + return 0; + else if (opening > closing) + return 1; + else + return -1; +} + +void CMakeIndenter::indentBlock(QTextDocument *doc, const QTextBlock &block, const QChar &typedChar, const TextEditor::TabSettings &tabSettings) +{ + Q_UNUSED(doc) + Q_UNUSED(typedChar) + + QTextBlock previousBlock = block.previous(); + // find the next previous block that is non-empty (contains non-whitespace characters) + while (previousBlock.isValid() && lineIsEmpty(previousBlock.text())) + previousBlock = previousBlock.previous(); + if (previousBlock.isValid()) { + const QString previousLine = previousBlock.text(); + const QString currentLine = block.text(); + int indentation = tabSettings.indentationColumn(previousLine); + + if (lineStartsBlock(previousLine)) + indentation += tabSettings.m_indentSize; + if (lineEndsBlock(currentLine)) + indentation = qMax(0, indentation - tabSettings.m_indentSize); + + // increase/decrease/keep the indentation level depending on if we have more opening or closing parantheses + indentation = qMax(0, indentation + tabSettings.m_indentSize * paranthesesLevel(previousLine)); + + tabSettings.indentLine(block, indentation); + } else { + // First line in whole document + tabSettings.indentLine(block, 0); + } +} + +} // namespace Internal +} // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeindenter.h b/src/plugins/cmakeprojectmanager/cmakeindenter.h new file mode 100644 index 0000000000..0e8c03ce4b --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeindenter.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jan Dalheimer <jan@dalheimer.de> +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** 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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef CMAKEINDENTER_H +#define CMAKEINDENTER_H + +#include "cmake_global.h" + +#include <texteditor/indenter.h> + +namespace CMakeProjectManager { +namespace Internal { + +class CMAKE_EXPORT CMakeIndenter : public TextEditor::Indenter +{ +public: + CMakeIndenter(); + virtual ~CMakeIndenter() {} + + bool isElectricCharacter(const QChar &ch) const override; + void indentBlock(QTextDocument *doc, const QTextBlock &block, const QChar &typedChar, const TextEditor::TabSettings &tabSettings) override; +}; + +} // namespace Internal +} // namespace CMakeProjectManager + +#endif // CMAKEINDENTER_H diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro index 355f996643..a4d06cf713 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro @@ -24,7 +24,9 @@ HEADERS = cmakebuildinfo.h \ cmakekitconfigwidget.h \ cmakecbpparser.h \ cmakefile.h \ - cmakebuildsettingswidget.h + cmakebuildsettingswidget.h \ + cmakeindenter.h \ + cmakeautocompleter.h SOURCES = cmakeproject.cpp \ cmakeprojectplugin.cpp \ @@ -46,7 +48,9 @@ SOURCES = cmakeproject.cpp \ cmakekitconfigwidget.cpp \ cmakecbpparser.cpp \ cmakefile.cpp \ - cmakebuildsettingswidget.cpp + cmakebuildsettingswidget.cpp \ + cmakeindenter.cpp \ + cmakeautocompleter.cpp RESOURCES += cmakeproject.qrc diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs index 7679217062..eddd1c5d7e 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs @@ -62,6 +62,10 @@ QtcPlugin { "cmakesettingspage.h", "cmakesettingspage.cpp", "generatorinfo.h", - "generatorinfo.cpp" + "generatorinfo.cpp", + "cmakeindenter.h", + "cmakeindenter.cpp", + "cmakeautocompleter.h", + "cmakeautocompleter.cpp" ] } |