diff options
author | David Schulz <david.schulz@qt.io> | 2019-07-15 12:50:29 +0200 |
---|---|---|
committer | David Schulz <david.schulz@qt.io> | 2019-07-17 06:21:23 +0000 |
commit | 3213e12ce7411efd05a544645aaa1ecd74e533a2 (patch) | |
tree | ae7187904453f530d4660832ff834371905cefaf /src/plugins/python/pythonhighlighter.cpp | |
parent | 2778a5adab13a16f4a6b3cda0980fa3b4f2b3a5e (diff) | |
download | qt-creator-3213e12ce7411efd05a544645aaa1ecd74e533a2.tar.gz |
rename PythonEditor plugin to Python
The plugin does not only contain a pure editor, but all kind of support
for a programming language like project and run support.
Change-Id: I1251367c8db2e7a54986415ffc5b860cb210de3c
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Diffstat (limited to 'src/plugins/python/pythonhighlighter.cpp')
-rw-r--r-- | src/plugins/python/pythonhighlighter.cpp | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/src/plugins/python/pythonhighlighter.cpp b/src/plugins/python/pythonhighlighter.cpp new file mode 100644 index 0000000000..c809e25b84 --- /dev/null +++ b/src/plugins/python/pythonhighlighter.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +/** + * @brief The Highlighter class pre-highlights Python source using simple scanner. + * + * Highlighter doesn't highlight user types (classes and enumerations), syntax + * and semantic errors, unnecessary code, etc. It's implements only + * basic highlight mechanism. + * + * Main highlight procedure is highlightBlock(). + */ + +#include "pythonhighlighter.h" +#include "pythonscanner.h" + +#include <texteditor/textdocument.h> +#include <texteditor/textdocumentlayout.h> +#include <texteditor/texteditorconstants.h> +#include <utils/qtcassert.h> + +namespace Python { +namespace Internal { + +/** + * @class PythonEditor::Internal::PythonHighlighter + * @brief Handles incremental lexical highlighting, but not semantic + * + * Incremental lexical highlighting works every time when any character typed + * or some text inserted (i.e. copied & pasted). + * Each line keeps associated scanner state - integer number. This state is the + * scanner context for next line. For example, 3 quotes begin a multiline + * string, and each line up to next 3 quotes has state 'MultiLineString'. + * + * @code + * def __init__: # Normal + * self.__doc__ = """ # MultiLineString (next line is inside) + * banana # MultiLineString + * """ # Normal + * @endcode + */ + +static TextEditor::TextStyle styleForFormat(int format) +{ + using namespace TextEditor; + const auto f = Format(format); + switch (f) { + case Format_Number: return C_NUMBER; + case Format_String: return C_STRING; + case Format_Keyword: return C_KEYWORD; + case Format_Type: return C_TYPE; + case Format_ClassField: return C_FIELD; + case Format_MagicAttr: return C_JS_SCOPE_VAR; + case Format_Operator: return C_OPERATOR; + case Format_Comment: return C_COMMENT; + case Format_Doxygen: return C_DOXYGEN_COMMENT; + case Format_Identifier: return C_TEXT; + case Format_Whitespace: return C_VISUAL_WHITESPACE; + case Format_ImportedModule: return C_STRING; + case Format_FormatsAmount: + QTC_CHECK(false); // should never get here + return C_TEXT; + } + QTC_CHECK(false); // should never get here + return C_TEXT; +} + +PythonHighlighter::PythonHighlighter() +{ + setTextFormatCategories(Format_FormatsAmount, styleForFormat); +} + +/** + * @brief PythonHighlighter::highlightBlock highlights single line of Python code + * @param text is single line without EOLN symbol. Access to all block data + * can be obtained through inherited currentBlock() function. + * + * This function receives state (int number) from previously highlighted block, + * scans block using received state and sets initial highlighting for current + * block. At the end, it saves internal state in current block. + */ +void PythonHighlighter::highlightBlock(const QString &text) +{ + int initialState = previousBlockState(); + if (initialState == -1) + initialState = 0; + setCurrentBlockState(highlightLine(text, initialState)); +} + +/** + * @return True if this keyword is acceptable at start of import line + */ +static bool isImportKeyword(const QString &keyword) +{ + return keyword == "import" || keyword == "from"; +} + +static int indent(const QString &line) +{ + for (int i = 0, size = line.size(); i < size; ++i) { + if (!line.at(i).isSpace()) + return i; + } + return -1; +} + +static void setFoldingIndent(const QTextBlock &block, int indent) +{ + if (TextEditor::TextBlockUserData *userData = TextEditor::TextDocumentLayout::userData(block)) { + userData->setFoldingIndent(indent); + userData->setFoldingStartIncluded(false); + userData->setFoldingEndIncluded(false); + } +} + +/** + * @brief Highlight line of code, returns new block state + * @param text Source code to highlight + * @param initialState Initial state of scanner, retrieved from previous block + * @return Final state of scanner, should be saved with current block + */ +int PythonHighlighter::highlightLine(const QString &text, int initialState) +{ + Scanner scanner(text.constData(), text.size()); + scanner.setState(initialState); + + const int pos = indent(text); + if (pos < 0) { + // Empty lines do not change folding indent + setFoldingIndent(currentBlock(), m_lastIndent); + } else { + m_lastIndent = pos; + if (pos == 0 && text.startsWith('#') && !text.startsWith("#!")) { + // A comment block at indentation 0. Fold on first line. + setFoldingIndent(currentBlock(), withinLicenseHeader ? 1 : 0); + withinLicenseHeader = true; + } else { + // Normal Python code. Line indentation can be used as folding indent. + setFoldingIndent(currentBlock(), m_lastIndent); + withinLicenseHeader = false; + } + } + + FormatToken tk; + bool hasOnlyWhitespace = true; + while (!(tk = scanner.read()).isEndOfBlock()) { + Format format = tk.format(); + if (format == Format_Keyword && isImportKeyword(scanner.value(tk)) && hasOnlyWhitespace) { + setFormat(tk.begin(), tk.length(), formatForCategory(format)); + highlightImport(scanner); + } else if (format == Format_Comment + || format == Format_String + || format == Format_Doxygen) { + setFormatWithSpaces(text, tk.begin(), tk.length(), formatForCategory(format)); + } else { + setFormat(tk.begin(), tk.length(), formatForCategory(format)); + } + if (format != Format_Whitespace) + hasOnlyWhitespace = false; + } + return scanner.state(); +} + +/** + * @brief Highlights rest of line as import directive + */ +void PythonHighlighter::highlightImport(Scanner &scanner) +{ + FormatToken tk; + while (!(tk = scanner.read()).isEndOfBlock()) { + Format format = tk.format(); + if (tk.format() == Format_Identifier) + format = Format_ImportedModule; + setFormat(tk.begin(), tk.length(), formatForCategory(format)); + } +} + +} // namespace Internal +} // namespace Python |