diff --git a/client/qmarkdowntextedit/CMakeLists.txt b/client/qmarkdowntextedit/CMakeLists.txt index cdf5e5e..a3af903 100644 --- a/client/qmarkdowntextedit/CMakeLists.txt +++ b/client/qmarkdowntextedit/CMakeLists.txt @@ -1,49 +1,106 @@ -cmake_minimum_required(VERSION 3.3) -project(qmarkdowntextedit) +cmake_minimum_required(VERSION 3.16) # Qt requires CMake 3.16 +project(qmarkdowntextedit LANGUAGES CXX VERSION 1.0.0) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) -find_package( Qt5Core REQUIRED ) -find_package( Qt5Widgets REQUIRED ) -find_package( Qt5Gui REQUIRED ) +# add option to disable test executable +option(QMARKDOWNTEXTEDIT_EXE "Build test executable" ON) -qt5_wrap_ui(ui_qplaintexteditsearchwidget.h qplaintexteditsearchwidget.ui) +# find qt +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) +find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Quick) -set(RESOURCE_FILES - media.qrc - ) +# needed for windows +if(WIN32) + set(INTL_LDFLAGS -lintl) +endif(WIN32) -qt5_add_resources(RESOURCE_ADDED ${RESOURCE_FILES}) +# QMarkdownTextEdit library +set(RC_FILES + media.qrc +) -set(SOURCE_FILES +# Translations arent loaded so don't include them +set(TS_FILES + trans/qmarkdowntextedit_de.ts + trans/qmarkdowntextedit_ur.ts + trans/qmarkdowntextedit_zh_CN.ts +) + +set(QMARKDOWNTEXTEDIT_SOURCES + ${RC_FILES} + linenumberarea.h # We need to keep this here, otherwise the build fails markdownhighlighter.cpp - markdownhighlighter.h - main.cpp - mainwindow.cpp - mainwindow.h - mainwindow.ui qmarkdowntextedit.cpp - qmarkdowntextedit.h - qplaintexteditsearchwidget.ui + qownlanguagedata.cpp + qownlanguagedata.h qplaintexteditsearchwidget.cpp + qplaintexteditsearchwidget.ui +) +set(QMARKDOWNTEXTEDIT_HEADERS + markdownhighlighter.h + qmarkdowntextedit.h qplaintexteditsearchwidget.h +) + +add_library(qmarkdowntextedit ${QMARKDOWNTEXTEDIT_SOURCES}) +set_target_properties(qmarkdowntextedit PROPERTIES + PUBLIC_HEADER "${QMARKDOWNTEXTEDIT_HEADERS}" +) + +target_link_libraries(qmarkdowntextedit PUBLIC + Qt${QT_VERSION_MAJOR}::Widgets + ${INTL_LDFLAGS} +) + +if (Qt${QT_VERSION_MAJOR}Quick_FOUND) + target_link_libraries(qmarkdowntextedit PUBLIC Qt${QT_VERSION_MAJOR}::Quick) + + add_executable(QtQuickExample examples/qml/example.cpp examples/qml/ressources.qrc) + target_link_libraries(QtQuickExample PRIVATE Qt${QT_VERSION_MAJOR}::Quick qmarkdowntextedit) +endif() + +# QMarkdownTextEdit executable +if(QMARKDOWNTEXTEDIT_EXE) + set(SOURCE_FILES + main.cpp + mainwindow.cpp + mainwindow.h + mainwindow.ui ) -add_executable(qmarkdowntextedit ${SOURCE_FILES} ${RESOURCE_ADDED}) + add_executable(qmarkdowntextedit-exe ${SOURCE_FILES}) + set_target_properties(qmarkdowntextedit-exe PROPERTIES OUTPUT_NAME "qmarkdowntextedit") + target_link_libraries(qmarkdowntextedit-exe PRIVATE + Qt${QT_VERSION_MAJOR}::Widgets + ${INTL_LDFLAGS} + qmarkdowntextedit + ) +endif() -include_directories(${Qt5Widgets_INCLUDES}) +include(GNUInstallDirs) # Doesn't fail on windows -# We need add -DQT_WIDGETS_LIB when using QtWidgets in Qt 5. -add_definitions(${Qt5Widgets_DEFINITIONS}) +# Install the lib +install(TARGETS qmarkdowntextedit + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) -# Executables fail to build with Qt 5 in the default configuration -# without -fPIE. We add that here. -set(CMAKE_CXX_FLAGS "${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") +# Add PkgConfig config file +configure_file(qmarkdowntextedit.pc.in ${CMAKE_BINARY_DIR}/qmarkdowntextedit.pc @ONLY) +install(FILES ${CMAKE_BINARY_DIR}/qmarkdowntextedit.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) -# The Qt5Widgets_LIBRARIES variable also includes QtGui and QtCore -target_link_libraries(qmarkdowntextedit Qt5::Widgets) +# Install exe +if(QMARKDOWNTEXTEDIT_EXE) + install(TARGETS qmarkdowntextedit-exe DESTINATION bin) +endif() diff --git a/client/qmarkdowntextedit/LICENSE b/client/qmarkdowntextedit/LICENSE index 41f4fbb..27fd2d4 100644 --- a/client/qmarkdowntextedit/LICENSE +++ b/client/qmarkdowntextedit/LICENSE @@ -1,5 +1,5 @@ The MIT License (MIT) -Copyright (c) 2014-2019 Patrizio Bekerle +Copyright (c) 2014-2023 Patrizio Bekerle -- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/client/qmarkdowntextedit/README.md b/client/qmarkdowntextedit/README.md index b94bd77..279ea16 100644 --- a/client/qmarkdowntextedit/README.md +++ b/client/qmarkdowntextedit/README.md @@ -1,31 +1,73 @@ # [QMarkdownTextEdit](https://github.com/pbek/qmarkdowntextedit) +[![Build Status GitHub Actions](https://github.com/pbek/qmarkdowntextedit/workflows/Build/badge.svg?branch=develop)](https://github.com/pbek/qmarkdowntextedit/actions) [![Build Status Linux/OS X](https://travis-ci.org/pbek/qmarkdowntextedit.svg?branch=develop)](https://travis-ci.org/pbek/qmarkdowntextedit) [![Build Status Windows](https://ci.appveyor.com/api/projects/status/github/pbek/qmarkdowntextedit)](https://ci.appveyor.com/project/pbek/qmarkdowntextedit) -QMarkdownTextEdit is a C++ Qt [QPlainTextEdit](http://doc.qt.io/qt-5/qtextplainedit.html) widget with [markdown](https://en.wikipedia.org/wiki/Markdown) highlighting and some other goodies. +QMarkdownTextEdit is a C++ Qt [QPlainTextEdit](http://doc.qt.io/qt-5/qplaintextedit.html) widget with [markdown](https://en.wikipedia.org/wiki/Markdown) highlighting and some other goodies. + +## Widget Features +- Markdown highlighting +- Code syntax highlighting +- Clickable links with `Ctrl + Click` +- Block indent with `Tab` and `Shift + Tab` +- Duplicate text with `Ctrl + Alt + Down` +- Searching of text with `Ctrl + F` + - Jump between search results with `Up` and `Down` + - Close search field with `Escape` +- Replacing of text with `Ctrl + R` + - You can also replace text with regular expressions or whole words +- Line numbers (Qt >= 5.5) +- Very fast +- And much more... + +## Supported Markdown Features +Commonmark compliance is enforced where possible however we are not fully Commonmark compliant yet. Following is a list of features/extensions supported by the highlighter. Please note that this is just a plaintext editor and as such, it only does the highlighting and not rendering of the markdown to HTML. + +| Feature | Availablity | +| --------------------------------------------------------------------------------------- | ---------------------------------------------------- | +| Bolds and Italics | Yes | +| Lists (Unordered/Orderered) | Yes | +| Links and Images
(Inline/Reference/Autolinks/E-mail) | Yes (Cannot handle nested links or complex cases yet) | +| Heading (ATX and Setext) | Yes | +| Codeblocks (indented and fenced)
Both backtick and tilde code fences are supported | Yes (Only fenced code block has syntax highlighting) | +| Inline code | Yes | +| Strikethrough | Yes | +| Underline | Yes (Optional) | +| Blockquotes | Yes | +| Table | Yes | -## Features -- markdown highlighting -- clickable links with `Ctrl + Click` -- block indent with `Tab` and `Shift + Tab` -- duplicate text with `Ctrl + Alt + Down` -- searching of text with `Ctrl + F` - - jump between search results with `Up` and `Down` - - close search field with `Escape` -- replacing of text with `Ctrl + R` - - you can also replace text with regular expressions or whole words -- and much more... ## Screenshot ![Screenhot](screenshot.png) -## How to use this widget -- include [qmarkdowntextedit.pri](https://github.com/pbek/qmarkdowntextedit/blob/develop/qmarkdowntextedit.pri) +## Usage + +There are multiple ways to use this. You can use the editor directly, or you can subclass it or you can just use the highlighter. +### Using the editor + +#### QMake +- Include [qmarkdowntextedit.pri](https://github.com/pbek/qmarkdowntextedit/blob/develop/qmarkdowntextedit.pri) to your project like this `include (qmarkdowntextedit/qmarkdowntextedit.pri)` - add a normal `QPlainTextEdit` to your UI and promote it to `QMarkdownTextEdit` (base class `QPlainTextEdit`) -## References -- [QOwnNotes - cross-platform open source plain-text file notepad](http://www.qownnotes.org) +#### CMake +- Include [CMakeLists.txt](https://github.com/pbek/qmarkdowntextedit/blob/develop/CMakeLists.txt) + to your project like this `add_subdirectory(qmarkdowntextedit)` +- add a normal `QPlainTextEdit` to your UI and promote it to `QMarkdownTextEdit` (base class `QPlainTextEdit`) + + +### Using the highlighter only +Highlighter can work with both `QPlainTextEdit` and `QTextEdit`. Example: +```cpp +auto doc = ui->plainTextEdit->document(); +auto *highlighter = new MarkdownHighlighter(doc); +``` + +## Projects using QMarkdownTextEdit +- [QOwnNotes](https://github.com/pbek/QOwnNotes) +- [Notes](https://github.com/nuttyartist/notes) +- [CuteMarkEd-NG](https://github.com/Waqar144/CuteMarkEd-NG) + ## Disclaimer This SOFTWARE PRODUCT is provided by THE PROVIDER "as is" and "with all faults." THE PROVIDER makes no representations or warranties of any kind concerning the safety, suitability, lack of viruses, inaccuracies, typographical errors, or other harmful components of this SOFTWARE PRODUCT. diff --git a/client/qmarkdowntextedit/examples/qml/example.cpp b/client/qmarkdowntextedit/examples/qml/example.cpp new file mode 100644 index 0000000..fda2bea --- /dev/null +++ b/client/qmarkdowntextedit/examples/qml/example.cpp @@ -0,0 +1,24 @@ +#include +#include + +#include "markdownhighlighter.h" + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + + qmlRegisterType("MarkdownHighlighter", 1, 0, + "MarkdownHighlighter"); + + QQmlApplicationEngine engine; + + const QUrl url(QStringLiteral("qrc:/example.qml")); + QObject::connect( + &engine, &QQmlApplicationEngine::objectCreated, &app, + [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) QCoreApplication::exit(-1); + }, + Qt::QueuedConnection); + engine.load(url); + + return app.exec(); +} diff --git a/client/qmarkdowntextedit/examples/qml/example.qml b/client/qmarkdowntextedit/examples/qml/example.qml new file mode 100644 index 0000000..c375701 --- /dev/null +++ b/client/qmarkdowntextedit/examples/qml/example.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import MarkdownHighlighter 1.0 + +Window { + id: mainwindow + width: 640 + height: 400 + visible: true + title: qsTr("QtQuick Project") + + TextEdit { + id: editor + text: "# Hello world!" + focus: true + } + + MarkdownHighlighter { + id: syntaxHighlighter + textDocument: editor.textDocument + } +} diff --git a/client/qmarkdowntextedit/examples/qml/ressources.qrc b/client/qmarkdowntextedit/examples/qml/ressources.qrc new file mode 100644 index 0000000..ac94188 --- /dev/null +++ b/client/qmarkdowntextedit/examples/qml/ressources.qrc @@ -0,0 +1,5 @@ + + + example.qml + + diff --git a/client/qmarkdowntextedit/linenumberarea.h b/client/qmarkdowntextedit/linenumberarea.h new file mode 100644 index 0000000..93a26cf --- /dev/null +++ b/client/qmarkdowntextedit/linenumberarea.h @@ -0,0 +1,124 @@ +#ifndef LINENUMBERAREA_H +#define LINENUMBERAREA_H + +#include +#include +#include +#include + +#include "qmarkdowntextedit.h" + +class LineNumArea final : public QWidget { + Q_OBJECT + +public: + explicit LineNumArea(QMarkdownTextEdit *parent) + : QWidget(parent), + textEdit(parent) + { + Q_ASSERT(parent); + + _currentLineColor = QColor(QStringLiteral("#eef067")); + _otherLinesColor = QColor(QStringLiteral("#a6a6a6")); + setHidden(true); + + // We always use fixed font to avoid "width" issues + setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + } + + void setCurrentLineColor(QColor color) { + _currentLineColor = color; + } + + void setOtherLineColor(QColor color) { + _otherLinesColor = std::move(color); + } + + int lineNumAreaWidth() const + { + if (!enabled) { + return 0; + } + + int digits = 2; + int max = std::max(1, textEdit->blockCount()); + while (max >= 10) { + max /= 10; + ++digits; + } + +#if QT_VERSION >= 0x050B00 + int space = 13 + textEdit->fontMetrics().horizontalAdvance(u'9') * digits; +#else + int space = 13 + textEdit->fontMetrics().width(QLatin1Char('9')) * digits; +#endif + + return space; + } + + bool isLineNumAreaEnabled() const + { + return enabled; + } + + void setLineNumAreaEnabled(bool e) + { + enabled = e; + setHidden(!e); + } + + QSize sizeHint() const override + { + return {lineNumAreaWidth(), 0}; + } + +protected: + void paintEvent(QPaintEvent *event) override + { + QPainter painter(this); + + painter.fillRect(event->rect(), palette().color(QPalette::Active, QPalette::Window)); + + auto block = textEdit->firstVisibleBlock(); + int blockNumber = block.blockNumber(); + qreal top = textEdit->blockBoundingGeometry(block).translated(textEdit->contentOffset()).top(); + // Maybe the top is not 0? + top += textEdit->viewportMargins().top(); + qreal bottom = top; + + const QPen currentLine = _currentLineColor; + const QPen otherLines = _otherLinesColor; + painter.setFont(font()); + + while (block.isValid() && top <= event->rect().bottom()) { + top = bottom; + bottom = top + textEdit->blockBoundingRect(block).height(); + if (block.isVisible() && bottom >= event->rect().top()) { + QString number = QString::number(blockNumber + 1); + + auto isCurrentLine = textEdit->textCursor().blockNumber() == blockNumber; + painter.setPen(isCurrentLine ? currentLine : otherLines); + + painter.drawText( + -5, + top, + sizeHint().width(), + textEdit->fontMetrics().height(), + Qt::AlignRight, + number + ); + } + + block = block.next(); + ++blockNumber; + } + } + +private: + bool enabled = false; + QMarkdownTextEdit *textEdit; + QColor _currentLineColor; + QColor _otherLinesColor; +}; + +#endif // LINENUMBERAREA_H diff --git a/client/qmarkdowntextedit/main.cpp b/client/qmarkdowntextedit/main.cpp index e960337..3c3e704 100644 --- a/client/qmarkdowntextedit/main.cpp +++ b/client/qmarkdowntextedit/main.cpp @@ -1,41 +1,35 @@ /* - * Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com + * MIT License * - * 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; version 2 of the License. + * Copyright (c) 2014-2023 Patrizio Bekerle -- * - * 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. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ -#include "mainwindow.h" #include -#include -#include -int main(int argc, char *argv[]) -{ +#include "mainwindow.h" + +int main(int argc, char *argv[]) { QApplication a(argc, argv); - QString filename; - if (argc > 1) { - filename = argv[1]; - } - if (!filename.isEmpty() && !QFileInfo(filename).isReadable()) { - qWarning() << filename << "is not a readable file"; - return 1; - } - MainWindow w; w.show(); - if (!filename.isEmpty()) { - w.loadFile(filename); - } - - return a.exec(); } diff --git a/client/qmarkdowntextedit/mainwindow.cpp b/client/qmarkdowntextedit/mainwindow.cpp index be3adde..c456953 100644 --- a/client/qmarkdowntextedit/mainwindow.cpp +++ b/client/qmarkdowntextedit/mainwindow.cpp @@ -1,14 +1,25 @@ /* - * Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com + * MIT License * - * 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; version 2 of the License. + * Copyright (c) 2014-2023 Patrizio Bekerle -- * - * 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. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. * * mainwindow.cpp * @@ -16,103 +27,12 @@ */ #include "mainwindow.h" + #include "ui_mainwindow.h" -#include "qmarkdowntextedit.h" -#include -#include -#include -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow) -{ +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); - QToolBar *toolBar = new QToolBar; - addToolBar(toolBar); - QAction *openAction = new QAction(QIcon::fromTheme("document-open"), tr("Open...")); - openAction->setShortcut(QKeySequence::Open); - connect(openAction, &QAction::triggered, this, &MainWindow::open); - - QAction *saveAction = new QAction(QIcon::fromTheme("document-save"), tr("Save")); - saveAction->setShortcut(QKeySequence::Save); - QAction *saveAsAction = new QAction(QIcon::fromTheme("document-save-as"), tr("Save as...")); - saveAsAction->setShortcut(QKeySequence::SaveAs); - QAction *quitAction = new QAction(QIcon::fromTheme("view-close"), tr("Quit")); - quitAction->setShortcut(QKeySequence::Quit); - connect(quitAction, &QAction::triggered, this, &MainWindow::onQuit); - - m_loadedContent = ui->textEdit->toPlainText(); - - toolBar->addActions({openAction, saveAction, saveAsAction, quitAction}); } -MainWindow::~MainWindow() -{ - delete ui; -} - -void MainWindow::loadFile(const QString &filename) -{ - QFile file(filename); - if (!file.open(QIODevice::ReadOnly)) { - qWarning() << "Failed to open" << filename; - return; - } - - m_filename = filename; - m_loadedContent = QString::fromLocal8Bit(file.readAll()); - ui->textEdit->setPlainText(m_loadedContent); -} - -void MainWindow::saveToFile(const QString &filename) -{ - QFile file(filename); - if (!file.open(QIODevice::WriteOnly)) { - qWarning() << "Failed to open" << filename; - return; - } - - m_filename = filename; - - m_loadedContent = ui->textEdit->toPlainText(); - file.write(m_loadedContent.toLocal8Bit()); -} - -void MainWindow::open() -{ - QString filename = QFileDialog::getOpenFileName(); - if (filename.isEmpty()) { - return; - } - loadFile(filename); -} - -void MainWindow::save() -{ - if (!m_filename.isEmpty()) { - saveAs(); - return; - } - - saveToFile(m_filename); -} - -void MainWindow::saveAs() -{ - QString filename = QFileDialog::getSaveFileName(); - if (filename.isEmpty()) { - return; - } - - saveToFile(filename); -} - -void MainWindow::onQuit() -{ - if (ui->textEdit->toPlainText() != m_loadedContent) { - if (QMessageBox::question(this, tr("Not saved"), tr("Document not saved, sure you want to quit?")) != QMessageBox::Yes) { - return; - } - } - close(); -} +MainWindow::~MainWindow() { delete ui; } diff --git a/client/qmarkdowntextedit/mainwindow.h b/client/qmarkdowntextedit/mainwindow.h index f3487ac..6e7c2c9 100644 --- a/client/qmarkdowntextedit/mainwindow.h +++ b/client/qmarkdowntextedit/mainwindow.h @@ -1,15 +1,25 @@ /* - * Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com + * MIT License * - * 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; version 2 of the License. + * Copyright (c) 2014-2023 Patrizio Bekerle -- * - * 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. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ #pragma once @@ -20,25 +30,13 @@ namespace Ui { class MainWindow; } -class MainWindow : public QMainWindow -{ +class MainWindow : public QMainWindow { Q_OBJECT -public: + public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); - void loadFile(const QString &filename); - void saveToFile(const QString &filename); - -private slots: - void open(); - void save(); - void saveAs(); - void onQuit(); - -private: + private: Ui::MainWindow *ui; - QString m_loadedContent; - QString m_filename; }; diff --git a/client/qmarkdowntextedit/mainwindow.ui b/client/qmarkdowntextedit/mainwindow.ui index a5b9a5b..3ee341f 100644 --- a/client/qmarkdowntextedit/mainwindow.ui +++ b/client/qmarkdowntextedit/mainwindow.ui @@ -26,7 +26,9 @@ ## Features - markdown highlighting +- syntax highlighting - clickable links with `Ctrl + Click` +- ~strikedout~ text and `inline code;` - block indent with `Tab` and `Shift + Tab` - duplicate text with `Ctrl + Alt + Down` - searching of text with `Ctrl + F` @@ -36,7 +38,7 @@ ## References -- [QOwnNotes - cross-platform open source plain-text notepad](http://www.qownnotes.org) +- [QOwnNotes - cross-platform open source plain-text file markdown note taking](https://www.qownnotes.org) ## Disclaimer @@ -55,7 +57,7 @@ There are inherent dangers in the use of any software, and you are solely respon 0 0 1070 - 25 + 23 diff --git a/client/qmarkdowntextedit/markdownhighlighter.cpp b/client/qmarkdowntextedit/markdownhighlighter.cpp index f99daed..bf49f7d 100644 --- a/client/qmarkdowntextedit/markdownhighlighter.cpp +++ b/client/qmarkdowntextedit/markdownhighlighter.cpp @@ -1,42 +1,67 @@ + /* - * Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com + * MIT License * - * 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; version 2 of the License. + * Copyright (c) 2014-2023 Patrizio Bekerle -- + * Copyright (c) 2019-2021 Waqar Ahmed -- * - * 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. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * QPlainTextEdit markdown highlighter + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * QPlainTextEdit Markdown highlighter */ -#include -#include #include "markdownhighlighter.h" +#include "qownlanguagedata.h" + +#include #include #include #include +#include +#include +#include +// We enable QStringView with Qt 5.15.1 +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 1) + #define MH_SUBSTR(pos, len) text.midRef(pos, len) +#else + #define MH_SUBSTR(pos, len) QStringView(text).mid(pos, len) +#endif + +QHash + MarkdownHighlighter::_langStringToEnum; +QHash + MarkdownHighlighter::_formats; +QVector MarkdownHighlighter::_highlightingRules; /** * Markdown syntax highlighting - * - * markdown syntax: - * http://daringfireball.net/projects/markdown/syntax - * * @param parent * @return */ MarkdownHighlighter::MarkdownHighlighter( - QTextDocument *parent, HighlightingOptions highlightingOptions) - : QSyntaxHighlighter(parent) { - _highlightingOptions = highlightingOptions; + QTextDocument *parent, HighlightingOptions highlightingOptions) + : QSyntaxHighlighter(parent), _highlightingOptions(highlightingOptions) { + // _highlightingOptions = highlightingOptions; _timer = new QTimer(this); - QObject::connect(_timer, SIGNAL(timeout()), - this, SLOT(timerTick())); + connect(_timer, &QTimer::timeout, this, &MarkdownHighlighter::timerTick); + _timer->start(1000); // initialize the highlighting rules @@ -44,21 +69,22 @@ MarkdownHighlighter::MarkdownHighlighter( // initialize the text formats initTextFormats(); + + // initialize code languages + initCodeLangs(); } /** * Does jobs every second */ void MarkdownHighlighter::timerTick() { - // qDebug() << "timerTick: " << this << ", " << this->parent()->parent()->parent()->objectName(); - // re-highlight all dirty blocks reHighlightDirtyBlocks(); // emit a signal every second if there was some highlighting done if (_highlightingFinished) { _highlightingFinished = false; - emit(highlightingFinished()); + Q_EMIT highlightingFinished(); } } @@ -77,6 +103,7 @@ void MarkdownHighlighter::reHighlightDirtyBlocks() { * Clears the dirty blocks vector */ void MarkdownHighlighter::clearDirtyBlocks() { + _ranges.clear(); _dirtyTextBlocks.clear(); } @@ -85,7 +112,7 @@ void MarkdownHighlighter::clearDirtyBlocks() { * * @param block */ -void MarkdownHighlighter::addDirtyBlock(QTextBlock block) { +void MarkdownHighlighter::addDirtyBlock(const QTextBlock &block) { if (!_dirtyTextBlocks.contains(block)) { _dirtyTextBlocks.append(block); } @@ -101,178 +128,49 @@ void MarkdownHighlighter::addDirtyBlock(QTextBlock block) { * /usr/share/kde4/apps/katepart/syntax/markdown.xml */ void MarkdownHighlighter::initHighlightingRules() { - // highlight the reference of reference links - { - HighlightingRule rule(HighlighterState::MaskedSyntax); - rule.pattern = QRegularExpression("^\\[.+?\\]: \\w+://.+$"); - _highlightingRulesPre.append(rule); - } - - // highlight unordered lists - { - HighlightingRule rule(HighlighterState::List); - rule.pattern = QRegularExpression("^\\s*[-*+]\\s"); - rule.useStateAsCurrentBlockState = true; - _highlightingRulesPre.append(rule); - - // highlight ordered lists - rule.pattern = QRegularExpression("^\\s*\\d+\\.\\s"); - _highlightingRulesPre.append(rule); - } - // highlight block quotes { HighlightingRule rule(HighlighterState::BlockQuote); rule.pattern = QRegularExpression( - _highlightingOptions.testFlag( - HighlightingOption::FullyHighlightedBlockQuote) ? - "^\\s*(>\\s*.+)" : "^\\s*(>\\s*)+"); - _highlightingRulesPre.append(rule); - } - - // highlight horizontal rulers - { - HighlightingRule rule(HighlighterState::HorizontalRuler); - rule.pattern = QRegularExpression("^([*\\-_]\\s?){3,}$"); - _highlightingRulesPre.append(rule); + _highlightingOptions.testFlag( + HighlightingOption::FullyHighlightedBlockQuote) + ? QStringLiteral("^\\s*(>\\s*.+)") + : QStringLiteral("^\\s*(>\\s*)+")); + rule.shouldContain = QStringLiteral("> "); + _highlightingRules.append(rule); } // highlight tables without starting | // we drop that for now, it's far too messy to deal with -// rule = HighlightingRule(); -// rule.pattern = QRegularExpression("^.+? \\| .+? \\| .+$"); -// rule.state = HighlighterState::Table; -// _highlightingRulesPre.append(rule); - - /* - * highlight italic - * this goes before bold so that bold can overwrite italic - * - * text to test: - * **bold** normal **bold** - * *start of line* normal - * normal *end of line* - * * list item *italic* - */ + // rule = HighlightingRule(); + // rule.pattern = QRegularExpression("^.+? \\| .+? \\| .+$"); + // rule.state = HighlighterState::Table; + // _highlightingRulesPre.append(rule); + // highlight trailing spaces { - HighlightingRule rule(HighlighterState::Italic); - // we don't allow a space after the starting * to prevent problems with - // unordered lists starting with a * - rule.pattern = QRegularExpression( - "(?:^|[^\\*\\b])(?:\\*([^\\* ][^\\*]*?)\\*)(?:[^\\*\\b]|$)"); + HighlightingRule rule(HighlighterState::TrailingSpace); + rule.pattern = QRegularExpression(QStringLiteral("( +)$")); + rule.shouldContain = QStringLiteral(" "); rule.capturingGroup = 1; - _highlightingRulesAfter.append(rule); - - rule.pattern = QRegularExpression("\\b_([^_]+)_\\b"); - _highlightingRulesAfter.append(rule); - } - - { - HighlightingRule rule(HighlighterState::Bold); - // highlight bold - rule.pattern = QRegularExpression("\\B\\*{2}(.+?)\\*{2}\\B"); - rule.capturingGroup = 1; - _highlightingRulesAfter.append(rule); - rule.pattern = QRegularExpression("\\b__(.+?)__\\b"); - _highlightingRulesAfter.append(rule); - } - - // highlight urls - { - HighlightingRule rule(HighlighterState::Link); - - // highlight urls without any other markup - rule.pattern = QRegularExpression("\\b\\w+?:\\/\\/[^\\s]+"); - rule.capturingGroup = 1; - _highlightingRulesAfter.append(rule); - - // rule.pattern = QRegularExpression("<(.+?:\\/\\/.+?)>"); - rule.pattern = QRegularExpression("<([^\\s`][^`]*?[^\\s`])>"); - rule.capturingGroup = 1; - _highlightingRulesAfter.append(rule); - - // highlight urls with title - // rule.pattern = QRegularExpression("\\[(.+?)\\]\\(.+?://.+?\\)"); - // rule.pattern = QRegularExpression("\\[(.+?)\\]\\(.+\\)\\B"); - rule.pattern = QRegularExpression("\\[([^\\[\\]]+)\\]\\((\\S+|.+?)\\)\\B"); - _highlightingRulesAfter.append(rule); - - // highlight urls with empty title - // rule.pattern = QRegularExpression("\\[\\]\\((.+?://.+?)\\)"); - rule.pattern = QRegularExpression("\\[\\]\\((.+?)\\)"); - _highlightingRulesAfter.append(rule); - - // highlight email links - rule.pattern = QRegularExpression("<(.+?@.+?)>"); - _highlightingRulesAfter.append(rule); - - // highlight reference links - rule.pattern = QRegularExpression("\\[(.+?)\\]\\s?\\[.+?\\]"); - _highlightingRulesAfter.append(rule); - } - - // Images - { - // highlight images with text - HighlightingRule rule(HighlighterState::Image); - rule.pattern = QRegularExpression("!\\[(.+?)\\]\\(.+?\\)"); - rule.capturingGroup = 1; - _highlightingRulesAfter.append(rule); - - // highlight images without text - rule.pattern = QRegularExpression("!\\[\\]\\((.+?)\\)"); - _highlightingRulesAfter.append(rule); - } - - // highlight images links - { -// HighlightingRule rule; - HighlightingRule rule(HighlighterState::Link); - rule.pattern = QRegularExpression("\\[!\\[(.+?)\\]\\(.+?\\)\\]\\(.+?\\)"); - rule.capturingGroup = 1; - _highlightingRulesAfter.append(rule); - - // highlight images links without text - rule.pattern = QRegularExpression("\\[!\\[\\]\\(.+?\\)\\]\\((.+?)\\)"); - _highlightingRulesAfter.append(rule); - } - - // highlight inline code - { - HighlightingRule rule(HighlighterState::InlineCodeBlock); -// HighlightingRule rule; - rule.pattern = QRegularExpression("`(.+?)`"); - rule.capturingGroup = 1; - _highlightingRulesAfter.append(rule); - } - - // highlight code blocks with four spaces or tabs in front of them - // and no list character after that - { - HighlightingRule rule(HighlighterState::CodeBlock); -// HighlightingRule rule; - rule.pattern = QRegularExpression("^((\\t)|( {4,})).+$"); - rule.disableIfCurrentStateIsSet = true; - _highlightingRulesAfter.append(rule); + _highlightingRules.append(rule); } // highlight inline comments { + // highlight comments for R Markdown for academic papers HighlightingRule rule(HighlighterState::Comment); - rule.pattern = QRegularExpression(""); - rule.capturingGroup = 1; - _highlightingRulesAfter.append(rule); - - // highlight comments for Rmarkdown for academic papers - rule.pattern = QRegularExpression("^\\[.+?\\]: # \\(.+?\\)$"); - _highlightingRulesAfter.append(rule); + rule.pattern = + QRegularExpression(QStringLiteral(R"(^\[.+?\]: # \(.+?\)$)")); + rule.shouldContain = QStringLiteral("]: # ("); + _highlightingRules.append(rule); } // highlight tables with starting | { HighlightingRule rule(HighlighterState::Table); - rule.pattern = QRegularExpression("^\\|.+?\\|$"); - _highlightingRulesAfter.append(rule); + rule.shouldContain = QStringLiteral("|"); + rule.pattern = QRegularExpression(QStringLiteral("^\\|.+?\\|$")); + _highlightingRules.append(rule); } } @@ -286,7 +184,7 @@ void MarkdownHighlighter::initTextFormats(int defaultFontSize) { // set character formats for headlines format = QTextCharFormat(); - //format.setForeground(QBrush(QColor(0, 49, 110))); + format.setForeground(QColor(2, 69, 150)); format.setFontWeight(QFont::Bold); format.setFontPointSize(defaultFontSize * 1.6); _formats[H1] = format; @@ -304,31 +202,40 @@ void MarkdownHighlighter::initTextFormats(int defaultFontSize) { // set character format for horizontal rulers format = QTextCharFormat(); - //format.setForeground(QBrush(Qt::darkGray)); - //format.setBackground(QBrush(Qt::lightGray)); - _formats[HorizontalRuler] = format; + format.setForeground(Qt::darkGray); + format.setBackground(Qt::lightGray); + _formats[HorizontalRuler] = std::move(format); // set character format for lists format = QTextCharFormat(); - //format.setForeground(QBrush(QColor(163, 0, 123))); + format.setForeground(QColor(163, 0, 123)); _formats[List] = format; + // set character format for checkbox + format = QTextCharFormat(); + format.setForeground(QColor(123, 100, 223)); + _formats[CheckBoxUnChecked] = std::move(format); + // set character format for checked checkbox + format = QTextCharFormat(); + format.setForeground(QColor(223, 50, 123)); + _formats[CheckBoxChecked] = std::move(format); + // set character format for links format = QTextCharFormat(); - //format.setForeground(QBrush(QColor(0, 128, 255))); + format.setForeground(QColor(0, 128, 255)); format.setFontUnderline(true); - _formats[Link] = format; + _formats[Link] = std::move(format); // set character format for images format = QTextCharFormat(); - //format.setForeground(QBrush(QColor(0, 191, 0))); - //format.setBackground(QBrush(QColor(228, 255, 228))); - _formats[Image] = format; + format.setForeground(QColor(0, 191, 0)); + format.setBackground(QColor(228, 255, 228)); + _formats[Image] = std::move(format); // set character format for code blocks format = QTextCharFormat(); format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - //format.setBackground(QColor(220, 220, 220)); + // format.setBackground(QColor(220, 220, 220)); _formats[CodeBlock] = format; _formats[InlineCodeBlock] = format; @@ -336,39 +243,128 @@ void MarkdownHighlighter::initTextFormats(int defaultFontSize) { format = QTextCharFormat(); format.setFontWeight(QFont::StyleItalic); format.setFontItalic(true); - _formats[Italic] = format; + _formats[Italic] = std::move(format); + + // set character format for underline + format = QTextCharFormat(); + format.setFontUnderline(true); + _formats[StUnderline] = std::move(format); // set character format for bold format = QTextCharFormat(); format.setFontWeight(QFont::Bold); - _formats[Bold] = format; + _formats[Bold] = std::move(format); // set character format for comments format = QTextCharFormat(); - format.setForeground(QBrush(Qt::lightGray)); - _formats[Comment] = format; + format.setForeground(QBrush(Qt::gray)); + _formats[Comment] = std::move(format); // set character format for masked syntax format = QTextCharFormat(); - //format.setForeground(QBrush("#cccccc")); - _formats[MaskedSyntax] = format; + format.setForeground(QColor(204, 204, 204)); + _formats[MaskedSyntax] = std::move(format); // set character format for tables format = QTextCharFormat(); format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - //format.setForeground(QBrush(QColor("#649449"))); - _formats[Table] = format; + format.setForeground(QColor(100, 148, 73)); + _formats[Table] = std::move(format); // set character format for block quotes format = QTextCharFormat(); - //format.setForeground(QBrush(QColor(Qt::darkRed))); - _formats[BlockQuote] = format; + format.setForeground(Qt::darkRed); + _formats[BlockQuote] = std::move(format); format = QTextCharFormat(); - _formats[HeadlineEnd] = format; + _formats[HeadlineEnd] = std::move(format); + _formats[NoState] = std::move(format); + + // set character format for trailing spaces + format.setBackground(QColor(252, 175, 62)); + _formats[TrailingSpace] = std::move(format); + + /**************************************** + * Formats for syntax highlighting + ***************************************/ format = QTextCharFormat(); - _formats[NoState] = format; + format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + format.setForeground(QColor(249, 38, 114)); + _formats[CodeKeyWord] = std::move(format); + + format = QTextCharFormat(); + format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + format.setForeground(QColor(163, 155, 78)); + _formats[CodeString] = std::move(format); + + format = QTextCharFormat(); + format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + format.setForeground(QColor(117, 113, 94)); + _formats[CodeComment] = std::move(format); + + format = QTextCharFormat(); + format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + format.setForeground(QColor(84, 174, 191)); + _formats[CodeType] = std::move(format); + + format = QTextCharFormat(); + format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + format.setForeground(QColor(219, 135, 68)); + _formats[CodeOther] = std::move(format); + + format = QTextCharFormat(); + format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + format.setForeground(QColor(174, 129, 255)); + _formats[CodeNumLiteral] = std::move(format); + + format = QTextCharFormat(); + format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + format.setForeground(QColor(1, 138, 15)); + _formats[CodeBuiltIn] = std::move(format); +} + +/** + * @brief initializes the langStringToEnum + */ +void MarkdownHighlighter::initCodeLangs() { + MarkdownHighlighter::_langStringToEnum = + QHash{ + {QLatin1String("bash"), MarkdownHighlighter::CodeBash}, + {QLatin1String("c"), MarkdownHighlighter::CodeC}, + {QLatin1String("cpp"), MarkdownHighlighter::CodeCpp}, + {QLatin1String("cxx"), MarkdownHighlighter::CodeCpp}, + {QLatin1String("c++"), MarkdownHighlighter::CodeCpp}, + {QLatin1String("c#"), MarkdownHighlighter::CodeCSharp}, + {QLatin1String("cmake"), MarkdownHighlighter::CodeCMake}, + {QLatin1String("csharp"), MarkdownHighlighter::CodeCSharp}, + {QLatin1String("css"), MarkdownHighlighter::CodeCSS}, + {QLatin1String("go"), MarkdownHighlighter::CodeGo}, + {QLatin1String("html"), MarkdownHighlighter::CodeXML}, + {QLatin1String("ini"), MarkdownHighlighter::CodeINI}, + {QLatin1String("java"), MarkdownHighlighter::CodeJava}, + {QLatin1String("javascript"), MarkdownHighlighter::CodeJava}, + {QLatin1String("js"), MarkdownHighlighter::CodeJs}, + {QLatin1String("json"), MarkdownHighlighter::CodeJSON}, + {QLatin1String("make"), MarkdownHighlighter::CodeMake}, + {QLatin1String("nix"), MarkdownHighlighter::CodeNix}, + {QLatin1String("php"), MarkdownHighlighter::CodePHP}, + {QLatin1String("py"), MarkdownHighlighter::CodePython}, + {QLatin1String("python"), MarkdownHighlighter::CodePython}, + {QLatin1String("qml"), MarkdownHighlighter::CodeQML}, + {QLatin1String("rust"), MarkdownHighlighter::CodeRust}, + {QLatin1String("sh"), MarkdownHighlighter::CodeBash}, + {QLatin1String("sql"), MarkdownHighlighter::CodeSQL}, + {QLatin1String("taggerscript"), + MarkdownHighlighter::CodeTaggerScript}, + {QLatin1String("ts"), MarkdownHighlighter::CodeTypeScript}, + {QLatin1String("typescript"), MarkdownHighlighter::CodeTypeScript}, + {QLatin1String("v"), MarkdownHighlighter::CodeV}, + {QLatin1String("vex"), MarkdownHighlighter::CodeVex}, + {QLatin1String("xml"), MarkdownHighlighter::CodeXML}, + {QLatin1String("yml"), MarkdownHighlighter::CodeYAML}, + {QLatin1String("yaml"), MarkdownHighlighter::CodeYAML}, + {QLatin1String("forth"), MarkdownHighlighter::CodeForth}}; } /** @@ -377,8 +373,8 @@ void MarkdownHighlighter::initTextFormats(int defaultFontSize) { * @param formats */ void MarkdownHighlighter::setTextFormats( - QHash formats) { - _formats = formats; + QHash formats) { + _formats = std::move(formats); } /** @@ -388,33 +384,63 @@ void MarkdownHighlighter::setTextFormats( */ void MarkdownHighlighter::setTextFormat(HighlighterState state, QTextCharFormat format) { - _formats[state] = format; + _formats[state] = std::move(format); } /** - * Does the markdown highlighting + * Does the Markdown highlighting * * @param text */ void MarkdownHighlighter::highlightBlock(const QString &text) { + if (currentBlockState() == HeadlineEnd) { + currentBlock().previous().setUserState(NoState); + addDirtyBlock(currentBlock().previous()); + } setCurrentBlockState(HighlighterState::NoState); currentBlock().setUserState(HighlighterState::NoState); + highlightMarkdown(text); _highlightingFinished = true; } -void MarkdownHighlighter::highlightMarkdown(QString text) { - if (!text.isEmpty()) { - highlightAdditionalRules(_highlightingRulesPre, text); +void MarkdownHighlighter::highlightMarkdown(const QString &text) { + const bool isBlockCodeBlock = isCodeBlock(previousBlockState()) || + text.startsWith(QLatin1String("```")) || + text.startsWith(QLatin1String("~~~")); + + if (!text.isEmpty() && !isBlockCodeBlock) { + highlightAdditionalRules(_highlightingRules, text); + + highlightThematicBreak(text); // needs to be called after the horizontal ruler highlighting highlightHeadline(text); - highlightAdditionalRules(_highlightingRulesAfter, text); + highlightIndentedCodeBlock(text); + + highlightLists(text); + + highlightInlineRules(text); } highlightCommentBlock(text); - highlightCodeBlock(text); + if (isBlockCodeBlock) highlightCodeFence(text); + highlightFrontmatterBlock(text); +} + +/** + * @brief gets indentation(spaces) of text + * @param text + * @return 1, if 1 space, 2 if 2 spaces, 3 if 3 spaces. Otherwise 0 + */ +int getIndentation(const QString &text) { + int spaces = 0; + // no more than 3 spaces + while (spaces < 4 && spaces < text.length() && + text.at(spaces) == QLatin1Char(' ')) + ++spaces; + return spaces; } /** @@ -422,163 +448,170 @@ void MarkdownHighlighter::highlightMarkdown(QString text) { * * @param text */ -void MarkdownHighlighter::highlightHeadline(QString text) { - QRegularExpression regex("^(#+)\\s+(.+?)$"); - QRegularExpressionMatch match = regex.match(text); - QTextCharFormat &maskedFormat = _formats[HighlighterState::MaskedSyntax]; +void MarkdownHighlighter::highlightHeadline(const QString &text) { + // three spaces indentation is allowed in headings + const int spacesOffset = getIndentation(text); - // check for headline blocks with # in front of them - if (match.hasMatch()) { - int count = match.captured(1).count(); + if (spacesOffset >= text.length() || spacesOffset == 4) return; - // we just have H1 to H6 - count = qMin(count, 6); + const bool headingFound = text.at(spacesOffset) == QLatin1Char('#'); - HighlighterState state = HighlighterState( - HighlighterState::H1 + count - 1); + if (headingFound) { + int headingLevel = 0; + int i = spacesOffset; + if (i >= text.length()) return; + while (i < text.length() && text.at(i) == QLatin1Char('#') && + i < (spacesOffset + 6)) + ++i; - QTextCharFormat &format = _formats[state]; - QTextCharFormat currentMaskedFormat = maskedFormat; + if (i < text.length() && text.at(i) == QLatin1Char(' ')) + headingLevel = i - spacesOffset; - // set the font size from the current rule's font format - currentMaskedFormat.setFontPointSize(format.fontPointSize()); + if (headingLevel > 0) { + const auto state = + HighlighterState(HighlighterState::H1 + headingLevel - 1); - // first highlight everything as MaskedSyntax - setFormat(match.capturedStart(), match.capturedLength(), - currentMaskedFormat); + // Set styling of the "#"s to "masked syntax", but with the size of the heading + auto maskedFormat = _formats[MaskedSyntax]; + maskedFormat.setFontPointSize(_formats[state].fontPointSize()); + setFormat(0, headingLevel, maskedFormat); - // then highlight with the real format - setFormat(match.capturedStart(2), match.capturedLength(2), - _formats[state]); + // Set the styling of the rest of the heading + setFormat(headingLevel + 1, text.length() - 1 - headingLevel, _formats[state]); - // set a margin for the current block - setCurrentBlockMargin(state); - - setCurrentBlockState(state); - currentBlock().setUserState(state); - return; + setCurrentBlockState(state); + return; + } } + auto hasOnlyHeadChars = [](const QString &txt, const QChar c, + int spaces) -> bool { + if (txt.isEmpty()) return false; + for (int i = spaces; i < txt.length(); ++i) { + if (txt.at(i) != c) return false; + } + return true; + }; + // take care of ==== and ---- headlines - QRegularExpression patternH1 = QRegularExpression("^=+$"); - QRegularExpression patternH2 = QRegularExpression("^-+$"); + const QString prev = currentBlock().previous().text(); + auto prevSpaces = getIndentation(prev); + + if (text.at(spacesOffset) == QLatin1Char('=') && prevSpaces < 4) { + const bool pattern1 = + !prev.isEmpty() && hasOnlyHeadChars(text, QLatin1Char('='), spacesOffset); + if (pattern1) { + highlightSubHeadline(text, H1); + return; + } + } else if (text.at(spacesOffset) == QLatin1Char('-') && prevSpaces < 4) { + const bool pattern2 = + !prev.isEmpty() && hasOnlyHeadChars(text, QLatin1Char('-'), spacesOffset); + if (pattern2) { + highlightSubHeadline(text, H2); + return; + } + } + + const QString nextBlockText = currentBlock().next().text(); + if (nextBlockText.isEmpty()) return; + const int nextSpaces = getIndentation(nextBlockText); + + if (nextSpaces >= nextBlockText.length()) return; + + if (nextBlockText.at(nextSpaces) == QLatin1Char('=') && nextSpaces < 4) { + const bool nextHasEqualChars = + hasOnlyHeadChars(nextBlockText, QLatin1Char('='), nextSpaces); + if (nextHasEqualChars) { + setFormat(0, text.length(), _formats[HighlighterState::H1]); + setCurrentBlockState(HighlighterState::H1); + } + } else if (nextBlockText.at(nextSpaces) == QLatin1Char('-') && + nextSpaces < 4) { + const bool nextHasMinusChars = + hasOnlyHeadChars(nextBlockText, QLatin1Char('-'), nextSpaces); + if (nextHasMinusChars) { + setFormat(0, text.length(), _formats[HighlighterState::H2]); + setCurrentBlockState(HighlighterState::H2); + } + } +} + +void MarkdownHighlighter::highlightSubHeadline(const QString &text, + HighlighterState state) { + const QTextCharFormat &maskedFormat = + _formats[HighlighterState::MaskedSyntax]; QTextBlock previousBlock = currentBlock().previous(); - QString previousText = previousBlock.text(); - previousText.trimmed().remove(QRegularExpression("[=-]")); - // check for ===== after a headline text and highlight as H1 - if (patternH1.match(text).hasMatch()) { - if (((previousBlockState() == HighlighterState::H1) || - (previousBlockState() == HighlighterState::NoState)) && - (previousText.length() > 0)) { - // set the font size from the current rule's font format - QTextCharFormat currentMaskedFormat = maskedFormat; - currentMaskedFormat.setFontPointSize( - _formats[HighlighterState::H1].fontPointSize()); + // we check for both H1/H2 so that if the user changes his mind, and changes + // === to ---, changes be reflected immediately + if (previousBlockState() == H1 || previousBlockState() == H2 || + previousBlockState() == NoState) { + QTextCharFormat currentMaskedFormat = maskedFormat; + // set the font size from the current rule's font format + currentMaskedFormat.setFontPointSize(_formats[state].fontPointSize()); - setFormat(0, text.length(), currentMaskedFormat); - setCurrentBlockState(HighlighterState::HeadlineEnd); - previousBlock.setUserState(HighlighterState::H1); + setFormat(0, text.length(), currentMaskedFormat); + setCurrentBlockState(HeadlineEnd); - // set a margin for the current block - setCurrentBlockMargin(HighlighterState::H1); - - // we want to re-highlight the previous block - // this must not done directly, but with a queue, otherwise it - // will crash - // setting the character format of the previous text, because this - // causes text to be formatted the same way when writing after - // the text + // we want to re-highlight the previous block + // this must not be done directly, but with a queue, otherwise it + // will crash + // setting the character format of the previous text, because this + // causes text to be formatted the same way when writing after + // the text + if (previousBlockState() != state) { addDirtyBlock(previousBlock); + previousBlock.setUserState(state); } - - return; - } - - // check for ----- after a headline text and highlight as H2 - if (patternH2.match(text).hasMatch()) { - if (((previousBlockState() == HighlighterState::H2) || - (previousBlockState() == HighlighterState::NoState)) && - (previousText.length() > 0)) { - // set the font size from the current rule's font format - QTextCharFormat currentMaskedFormat = maskedFormat; - currentMaskedFormat.setFontPointSize( - _formats[HighlighterState::H2].fontPointSize()); - - setFormat(0, text.length(), currentMaskedFormat); - setCurrentBlockState(HighlighterState::HeadlineEnd); - previousBlock.setUserState(HighlighterState::H2); - - // set a margin for the current block - setCurrentBlockMargin(HighlighterState::H2); - - // we want to re-highlight the previous block - addDirtyBlock(previousBlock); - } - - return; - } - - QTextBlock nextBlock = currentBlock().next(); - QString nextBlockText = nextBlock.text(); - - // highlight as H1 if next block is ===== - if (patternH1.match(nextBlockText).hasMatch() || - patternH2.match(nextBlockText).hasMatch()) { - setFormat(0, text.length(), _formats[HighlighterState::H1]); - setCurrentBlockState(HighlighterState::H1); - currentBlock().setUserState(HighlighterState::H1); - } - - // highlight as H2 if next block is ----- - if (patternH2.match(nextBlockText).hasMatch()) { - setFormat(0, text.length(), _formats[HighlighterState::H2]); - setCurrentBlockState(HighlighterState::H2); - currentBlock().setUserState(HighlighterState::H2); } } /** - * Sets a margin for the current block - * - * @param state + * @brief highlight code blocks with four spaces or tabs in front of them + * and no list character after that + * @param text */ -void MarkdownHighlighter::setCurrentBlockMargin( - MarkdownHighlighter::HighlighterState state) { - // this is currently disabled because it causes multiple problems: - // - it prevents "undo" in headlines - // https://github.com/pbek/QOwnNotes/issues/520 - // - invisible lines at the end of a note - // https://github.com/pbek/QOwnNotes/issues/667 - // - a crash when reaching the invisible lines when the current line is - // highlighted - // https://github.com/pbek/QOwnNotes/issues/701 - return; +void MarkdownHighlighter::highlightIndentedCodeBlock(const QString &text) { + if (text.isEmpty() || (!text.startsWith(QLatin1String(" ")) && + !text.startsWith(QLatin1Char('\t')))) + return; - qreal margin; + const QString prevTrimmed = currentBlock().previous().text().trimmed(); + // previous line must be empty according to CommonMark except if it is a + // heading https://spec.commonmark.org/0.29/#indented-code-block + if (!prevTrimmed.isEmpty() && previousBlockState() != CodeBlockIndented && + !isHeading(previousBlockState()) && previousBlockState() != HeadlineEnd) + return; - switch (state) { - case HighlighterState::H1: - margin = 5; - break; - case HighlighterState::H2: - case HighlighterState::H3: - case HighlighterState::H4: - case HighlighterState::H5: - case HighlighterState::H6: - margin = 3; - break; - default: - return; + const QString trimmed = text.trimmed(); + + // should not be in a list + if (trimmed.startsWith(QLatin1String("- ")) || + trimmed.startsWith(QLatin1String("+ ")) || + trimmed.startsWith(QLatin1String("* ")) || + (trimmed.length() >= 1 && trimmed.at(0).isNumber())) + return; + + setCurrentBlockState(CodeBlockIndented); + setFormat(0, text.length(), _formats[CodeBlock]); +} + +void MarkdownHighlighter::highlightCodeFence(const QString &text) { + // already in tilde block + if ((previousBlockState() == CodeBlockTilde || + previousBlockState() == CodeBlockTildeComment || + previousBlockState() >= CodeCpp + tildeOffset)) { + highlightCodeBlock(text, QStringLiteral("~~~")); + // start of a tilde block + } else if ((previousBlockState() != CodeBlock && + previousBlockState() < CodeCpp) && + text.startsWith(QLatin1String("~~~"))) { + highlightCodeBlock(text, QStringLiteral("~~~")); + } else { + // back tick block + highlightCodeBlock(text); } - - QTextBlockFormat blockFormat = currentBlock().blockFormat(); - blockFormat.setTopMargin(2); - blockFormat.setBottomMargin(margin); - - // this prevents "undo" in headlines! - QTextCursor* myCursor = new QTextCursor(currentBlock()); - myCursor->setBlockFormat(blockFormat); } /** @@ -586,24 +619,1018 @@ void MarkdownHighlighter::setCurrentBlockMargin( * * @param text */ -void MarkdownHighlighter::highlightCodeBlock(QString text) { - QRegularExpression regex("^```\\w*?$"); - QRegularExpressionMatch match = regex.match(text); +void MarkdownHighlighter::highlightCodeBlock(const QString &text, + const QString &opener) { + if (text.startsWith(opener)) { + // if someone decides to put these on the same line + // interpret it as inline code, not code block + if (text.endsWith(QLatin1String("```")) && text.length() > 3) { + setFormat(3, text.length() - 3, + _formats[HighlighterState::InlineCodeBlock]); + setFormat(0, 3, _formats[HighlighterState::MaskedSyntax]); + setFormat(text.length() - 3, 3, + _formats[HighlighterState::MaskedSyntax]); + return; + } + if ((previousBlockState() != CodeBlock && + previousBlockState() != CodeBlockTilde) && + (previousBlockState() != CodeBlockComment && + previousBlockState() != CodeBlockTildeComment) && + previousBlockState() < CodeCpp) { + const QString &lang = text.mid(3, text.length()).toLower(); + HighlighterState progLang = _langStringToEnum.value(lang); + + if (progLang >= CodeCpp) { + const int state = text.startsWith(QLatin1String("```")) + ? progLang + : progLang + tildeOffset; + setCurrentBlockState(state); + } else { + const int state = + opener == QLatin1String("```") ? CodeBlock : CodeBlockTilde; + setCurrentBlockState(state); + } + } else if (isCodeBlock(previousBlockState())) { + const int state = opener == QLatin1String("```") + ? CodeBlockEnd + : CodeBlockTildeEnd; + setCurrentBlockState(state); + } - if (match.hasMatch()) { - setCurrentBlockState( - previousBlockState() == HighlighterState::CodeBlock ? - HighlighterState::CodeBlockEnd : HighlighterState::CodeBlock); // set the font size from the current rule's font format - QTextCharFormat &maskedFormat = - _formats[HighlighterState::MaskedSyntax]; - maskedFormat.setFontPointSize( - _formats[HighlighterState::CodeBlock].fontPointSize()); + QTextCharFormat &maskedFormat = _formats[MaskedSyntax]; + maskedFormat.setFontPointSize(_formats[CodeBlock].fontPointSize()); setFormat(0, text.length(), maskedFormat); - } else if (previousBlockState() == HighlighterState::CodeBlock) { - setCurrentBlockState(HighlighterState::CodeBlock); - setFormat(0, text.length(), _formats[HighlighterState::CodeBlock]); + } else if (isCodeBlock(previousBlockState())) { + setCurrentBlockState(previousBlockState()); + highlightSyntax(text); + } +} + +/** + * @brief Does the code syntax highlighting + * @param text + */ +void MarkdownHighlighter::highlightSyntax(const QString &text) { + if (text.isEmpty()) return; + + const auto textLen = text.length(); + + QChar comment; + bool isCSS = false; + bool isYAML = false; + bool isMake = false; + bool isForth = false; + + QMultiHash keywords{}; + QMultiHash others{}; + QMultiHash types{}; + QMultiHash builtin{}; + QMultiHash literals{}; + + // apply the default code block format first + setFormat(0, textLen, _formats[CodeBlock]); + + switch (currentBlockState()) { + case HighlighterState::CodeCpp: + case HighlighterState::CodeCpp + tildeOffset: + case HighlighterState::CodeCppComment: + case HighlighterState::CodeCppComment + tildeOffset: + loadCppData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeJs: + case HighlighterState::CodeJs + tildeOffset: + case HighlighterState::CodeJsComment: + case HighlighterState::CodeJsComment + tildeOffset: + loadJSData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeC: + case HighlighterState::CodeC + tildeOffset: + case HighlighterState::CodeCComment: + case HighlighterState::CodeCComment + tildeOffset: + loadCppData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeBash: + case HighlighterState::CodeBash + tildeOffset: + loadShellData(types, keywords, builtin, literals, others); + comment = QLatin1Char('#'); + break; + case HighlighterState::CodePHP: + case HighlighterState::CodePHP + tildeOffset: + case HighlighterState::CodePHPComment: + case HighlighterState::CodePHPComment + tildeOffset: + loadPHPData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeQML: + case HighlighterState::CodeQML + tildeOffset: + case HighlighterState::CodeQMLComment: + case HighlighterState::CodeQMLComment + tildeOffset: + loadQMLData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodePython: + case HighlighterState::CodePython + tildeOffset: + loadPythonData(types, keywords, builtin, literals, others); + comment = QLatin1Char('#'); + break; + case HighlighterState::CodeRust: + case HighlighterState::CodeRust + tildeOffset: + case HighlighterState::CodeRustComment: + case HighlighterState::CodeRustComment + tildeOffset: + loadRustData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeJava: + case HighlighterState::CodeJava + tildeOffset: + case HighlighterState::CodeJavaComment: + case HighlighterState::CodeJavaComment + tildeOffset: + loadJavaData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeCSharp: + case HighlighterState::CodeCSharp + tildeOffset: + case HighlighterState::CodeCSharpComment: + case HighlighterState::CodeCSharpComment + tildeOffset: + loadCSharpData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeGo: + case HighlighterState::CodeGo + tildeOffset: + case HighlighterState::CodeGoComment: + case HighlighterState::CodeGoComment + tildeOffset: + loadGoData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeV: + case HighlighterState::CodeV + tildeOffset: + case HighlighterState::CodeVComment: + case HighlighterState::CodeVComment + tildeOffset: + loadVData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeSQL: + case HighlighterState::CodeSQL + tildeOffset: + loadSQLData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeJSON: + case HighlighterState::CodeJSON + tildeOffset: + loadJSONData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeXML: + case HighlighterState::CodeXML + tildeOffset: + xmlHighlighter(text); + return; + case HighlighterState::CodeCSS: + case HighlighterState::CodeCSS + tildeOffset: + case HighlighterState::CodeCSSComment: + case HighlighterState::CodeCSSComment + tildeOffset: + isCSS = true; + loadCSSData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeTypeScript: + case HighlighterState::CodeTypeScript + tildeOffset: + case HighlighterState::CodeTypeScriptComment: + case HighlighterState::CodeTypeScriptComment + tildeOffset: + loadTypescriptData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeYAML: + case HighlighterState::CodeYAML + tildeOffset: + isYAML = true; + comment = QLatin1Char('#'); + loadYAMLData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeINI: + case HighlighterState::CodeINI + tildeOffset: + iniHighlighter(text); + return; + case HighlighterState::CodeTaggerScript: + case HighlighterState::CodeTaggerScript + tildeOffset: + taggerScriptHighlighter(text); + return; + case HighlighterState::CodeVex: + case HighlighterState::CodeVex + tildeOffset: + case HighlighterState::CodeVexComment: + case HighlighterState::CodeVexComment + tildeOffset: + loadVEXData(types, keywords, builtin, literals, others); + break; + case HighlighterState::CodeCMake: + case HighlighterState::CodeCMake + tildeOffset: + loadCMakeData(types, keywords, builtin, literals, others); + comment = QLatin1Char('#'); + break; + case HighlighterState::CodeMake: + case HighlighterState::CodeMake + tildeOffset: + isMake = true; + loadMakeData(types, keywords, builtin, literals, others); + comment = QLatin1Char('#'); + break; + case HighlighterState::CodeNix: + case HighlighterState::CodeNix + tildeOffset: + loadNixData(types, keywords, builtin, literals, others); + comment = QLatin1Char('#'); + break; + case HighlighterState::CodeForth: + case HighlighterState::CodeForth + tildeOffset: + case HighlighterState::CodeForthComment: + case HighlighterState::CodeForthComment + tildeOffset: + isForth = true; + loadForthData(types, keywords, builtin, literals, others); + break; + default: + setFormat(0, textLen, _formats[CodeBlock]); + return; + } + + auto applyCodeFormat = + [this](int i, const QMultiHash &data, + const QString &text, const QTextCharFormat &fmt) -> int { + // check if we are at the beginning OR if this is the start of a word + if (i == 0 || (!text.at(i - 1).isLetterOrNumber() && + text.at(i-1) != QLatin1Char('_'))) { + const char c = text.at(i).toLatin1(); + auto it = data.find(c); + for (; it != data.end() && it.key() == c; ++it) { + // we have a word match check + // 1. if we are at the end + // 2. if we have a complete word + const QLatin1String &word = it.value(); + if (word == MH_SUBSTR(i, word.size()) && + (i + word.size() == text.length() || + (!text.at(i + word.size()).isLetterOrNumber() && + text.at(i + word.size()) != QLatin1Char('_')))) { + setFormat(i, word.size(), fmt); + i += word.size(); + } + } + } + return i; + }; + + const QTextCharFormat &formatType = _formats[CodeType]; + const QTextCharFormat &formatKeyword = _formats[CodeKeyWord]; + const QTextCharFormat &formatComment = _formats[CodeComment]; + const QTextCharFormat &formatNumLit = _formats[CodeNumLiteral]; + const QTextCharFormat &formatBuiltIn = _formats[CodeBuiltIn]; + const QTextCharFormat &formatOther = _formats[CodeOther]; + + for (int i = 0; i < textLen; ++i) { + if (currentBlockState() != -1 && currentBlockState() % 2 != 0) + goto Comment; + + while (i < textLen && !text[i].isLetter()) { + if (text[i].isSpace()) { + ++i; + // make sure we don't cross the bound + if (i == textLen) break; + if (text[i].isLetter()) break; + continue; + } + // inline comment + if (comment.isNull() && text[i] == QLatin1Char('/')) { + if ((i + 1) < textLen) { + if (text[i + 1] == QLatin1Char('/')) { + setFormat(i, textLen, formatComment); + return; + } else if (text[i + 1] == QLatin1Char('*')) { + Comment: + int next = text.indexOf(QLatin1String("*/"), i); + if (next == -1) { + // we didn't find a comment end. + // Check if we are already in a comment block + if (currentBlockState() % 2 == 0) + setCurrentBlockState(currentBlockState() + 1); + setFormat(i, textLen, formatComment); + return; + } else { + // we found a comment end + // mark this block as code if it was previously + // comment. First check if the comment ended on the + // same line. if modulo 2 is not equal to zero, it + // means we are in a comment, -1 will set this + // block's state as language + if (currentBlockState() % 2 != 0) { + setCurrentBlockState(currentBlockState() - 1); + } + next += 2; + setFormat(i, next - i, formatComment); + i = next; + if (i >= textLen) return; + } + } + } + } else if (text[i] == comment) { + setFormat(i, textLen, formatComment); + i = textLen; + break; + // integer literal + } else if (text[i].isNumber()) { + i = highlightNumericLiterals(text, i); + // string literals + } else if (text[i] == QLatin1Char('\"') || + text[i] == QLatin1Char('\'')) { + i = highlightStringLiterals(text.at(i), text, i); + } + if (i >= textLen) { + break; + } + ++i; + } + + const int pos = i; + + if (i == textLen || !text[i].isLetter()) continue; + + /* Highlight Types */ + i = applyCodeFormat(i, types, text, formatType); + /************************************************ + next letter is usually a space, in that case + going forward is useless, so continue; + ************************************************/ + if (i == textLen || !text[i].isLetter()) continue; + + /* Highlight Keywords */ + i = applyCodeFormat(i, keywords, text, formatKeyword); + if (i == textLen || !text[i].isLetter()) continue; + + /* Highlight Literals (true/false/NULL,nullptr) */ + i = applyCodeFormat(i, literals, text, formatNumLit); + if (i == textLen || !text[i].isLetter()) continue; + + /* Highlight Builtin library stuff */ + i = applyCodeFormat(i, builtin, text, formatBuiltIn); + if (i == textLen || !text[i].isLetter()) continue; + + /* Highlight other stuff (preprocessor etc.) */ + if (i == 0 || !text.at(i - 1).isLetter()) { + const char c = text.at(i).toLatin1(); + auto it = others.find(c); + for (; it != others.end() && it.key() == c; ++it) { + const QLatin1String &word = it.value(); + if (word == MH_SUBSTR(i, word.size()) && + (i + word.size() == text.length() || + !text.at(i + word.size()).isLetter())) { + currentBlockState() == CodeCpp || + currentBlockState() == CodeC + ? setFormat(i - 1, word.size() + 1, formatOther) + : setFormat(i, word.size(), formatOther); + i += word.size(); + } + } + } + + // we were unable to find any match, lets skip this word + if (pos == i) { + int cnt = i; + while (cnt < textLen) { + if (!text[cnt].isLetter()) break; + ++cnt; + } + i = cnt - 1; + } + } + + /*********************** + **** POST PROCESSORS *** + ***********************/ + + if (isCSS) cssHighlighter(text); + if (isYAML) ymlHighlighter(text); + if (isMake) makeHighlighter(text); + if (isForth) forthHighlighter(text); +} + +/** + * @brief Highlight string literals in code + * @param strType str type i.e., ' or " + * @param text the text being scanned + * @param i pos of i in loop + * @return pos of i after the string + */ +int MarkdownHighlighter::highlightStringLiterals(QChar strType, + const QString &text, int i) { + const auto& strFormat = _formats[CodeString]; + setFormat(i, 1, strFormat); + ++i; + + while (i < text.length()) { + // look for string end + // make sure it's not an escape seq + if (text.at(i) == strType && text.at(i - 1) != QLatin1Char('\\')) { + setFormat(i, 1, strFormat); + ++i; + break; + } + // look for escape sequence + if (text.at(i) == QLatin1Char('\\') && (i + 1) < text.length()) { + int len = 0; + switch (text.at(i + 1).toLatin1()) { + case 'a': + case 'b': + case 'e': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + case '\'': + case '"': + case '\\': + case '\?': + // 2 because we have to highlight \ as well as the following + // char + len = 2; + break; + // octal esc sequence \123 + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + if (i + 4 <= text.length()) { + if (!isOctal(text.at(i + 2).toLatin1())) { + break; + } + if (!isOctal(text.at(i + 3).toLatin1())) { + break; + } + len = 4; + } + break; + } + // hex numbers \xFA + case 'x': { + if (i + 3 <= text.length()) { + if (!isHex(text.at(i + 2).toLatin1())) { + break; + } + if (!isHex(text.at(i + 3).toLatin1())) { + break; + } + len = 4; + } + break; + } + // TODO: implement Unicode code point escaping + default: + break; + } + + // if len is zero, that means this wasn't an esc seq + // increment i so that we skip this backslash + if (len == 0) { + setFormat(i, 1, strFormat); + ++i; + continue; + } + + setFormat(i, len, _formats[CodeNumLiteral]); + i += len; + continue; + } + setFormat(i, 1, strFormat); + ++i; + } + return i - 1; +} + +/** + * @brief Highlight numeric literals in code + * @param text the text being scanned + * @param i pos of i in loop + * @return pos of i after the number + * + * @details it doesn't highlight the following yet: + * - 1000'0000 + */ +int MarkdownHighlighter::highlightNumericLiterals(const QString &text, int i) { + bool isPrefixAllowed = false; + if (i == 0) { + isPrefixAllowed = true; + } else { + // these values are allowed before a number + switch (text.at(i - 1).toLatin1()) { + // CSS number + case ':': + if (currentBlockState() == CodeCSS) { + isPrefixAllowed = true; + } + break; + case '[': + case '(': + case '{': + case ' ': + case ',': + case '=': + case '+': + case '-': + case '*': + case '/': + case '%': + case '<': + case '>': + isPrefixAllowed = true; + break; + } + } + + if (!isPrefixAllowed) return i; + + const int start = i; + + if ((i + 1) >= text.length()) { + setFormat(i, 1, _formats[CodeNumLiteral]); + return ++i; + } + + ++i; + // hex numbers highlighting (only if there's a preceding zero) + bool isCurrentHex = false; + if (text.at(i) == QChar('x') && text.at(i - 1) == QChar('0')) { + isCurrentHex = true; + ++i; + } + + while (i < text.length()) { + if (!text.at(i).isNumber() && text.at(i) != QChar('.') && + text.at(i) != QChar('e') && + !(isCurrentHex && isHex(text.at(i).toLatin1()))) + break; + ++i; + } + + bool isPostfixAllowed = false; + if (i == text.length()) { + // cant have e at the end + if (isCurrentHex || text.at(i - 1) != QChar('e')) { + isPostfixAllowed = true; + } + } else { + // these values are allowed after a number + switch (text.at(i).toLatin1()) { + case ']': + case ')': + case '}': + case ' ': + case ',': + case '=': + case '+': + case '-': + case '*': + case '/': + case '%': + case '>': + case '<': + case ';': + isPostfixAllowed = true; + break; + // for 100u, 1.0F + case 'p': + if (currentBlockState() == CodeCSS) { + if (i + 1 < text.length() && text.at(i + 1) == QChar('x')) { + if (i + 2 == text.length() || + !text.at(i + 2).isLetterOrNumber()) { + isPostfixAllowed = true; + } + } + } + break; + case 'e': + if (currentBlockState() == CodeCSS) { + if (i + 1 < text.length() && text.at(i + 1) == QChar('m')) { + if (i + 2 == text.length() || + !text.at(i + 2).isLetterOrNumber()) { + isPostfixAllowed = true; + } + } + } + break; + case 'u': + case 'l': + case 'f': + case 'U': + case 'L': + case 'F': + if (i + 1 == text.length() || + !text.at(i + 1).isLetterOrNumber()) { + isPostfixAllowed = true; + ++i; + } + break; + } + } + if (isPostfixAllowed) { + int end = i--; + setFormat(start, end - start, _formats[CodeNumLiteral]); + } + // decrement so that the index is at the last number, not after it + return i; +} + +/** + * @brief The Tagger Script highlighter + * @param text + * @details his function is responsible for taggerscript highlighting. + * It highlights anything between a (inclusive) '&' and a (exclusive) '(' as a + * function. An exception is the '$noop()'function, which get highlighted as a + * comment. + * + * It has basic error detection when there is an unlcosed %Metadata Variable% + */ +void MarkdownHighlighter::taggerScriptHighlighter(const QString &text) { + if (text.isEmpty()) return; + const auto textLen = text.length(); + + for (int i = 0; i < textLen; ++i) { + // highlight functions, unless it's a comment function + if (text.at(i) == QChar('$') && + MH_SUBSTR(i, 5) != QLatin1String("$noop")) { + const int next = text.indexOf(QChar('('), i); + if (next == -1) break; + setFormat(i, next - i, _formats[CodeKeyWord]); + i = next; + } + + // highlight variables + if (text.at(i) == QChar('%')) { + const int next = text.indexOf(QChar('%'), i + 1); + const int start = i; + i++; + if (next != -1) { + setFormat(start, next - start + 1, _formats[CodeType]); + } else { + // error highlighting + QTextCharFormat errorFormat = _formats[NoState]; + errorFormat.setUnderlineColor(Qt::red); + errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); + setFormat(start, 1, errorFormat); + } + } + + // highlight comments + if (MH_SUBSTR(i, 5) == QLatin1String("$noop")) { + const int next = text.indexOf(QChar(')'), i); + if (next == -1) break; + setFormat(i, next - i + 1, _formats[CodeComment]); + i = next; + } + + // highlight escape chars + if (text.at(i) == QChar('\\')) { + setFormat(i, 2, _formats[CodeOther]); + i++; + } + } +} + +/** + * @brief The YAML highlighter + * @param text + * @details This function post processes a line after the main syntax + * highlighter has run for additional highlighting. It does these things + * + * If the current line is a comment, skip it + * + * Highlight all the words that have a colon after them as 'keyword' except: + * If the word is a string, skip it. + * If the colon is in between a path, skip it (C:\) + * + * Once the colon is found, the function will skip every character except 'h' + * + * If an h letter is found, check the next 4/5 letters for http/https and + * highlight them as a link (underlined) + */ +void MarkdownHighlighter::ymlHighlighter(const QString &text) { + if (text.isEmpty()) return; + const auto textLen = text.length(); + bool colonNotFound = false; + + // if this is a comment don't do anything and just return + if (text.trimmed().at(0) == QChar('#')) return; + + for (int i = 0; i < textLen; ++i) { + if (!text.at(i).isLetter()) continue; + + if (colonNotFound && text.at(i) != QChar('h')) continue; + + // we found a string literal, skip it + if (i != 0 && text.at(i - 1) == QChar('"')) { + const int next = text.indexOf(QChar('"'), i); + if (next == -1) break; + i = next; + continue; + } + + if (i != 0 && text.at(i - 1) == QChar('\'')) { + const int next = text.indexOf(QChar('\''), i); + if (next == -1) break; + i = next; + continue; + } + + const int colon = text.indexOf(QChar(':'), i); + + // if colon isn't found, we set this true + if (colon == -1) colonNotFound = true; + + if (!colonNotFound) { + // if the line ends here, format and return + if (colon + 1 == textLen) { + setFormat(i, colon - i, _formats[CodeKeyWord]); + return; + } + // colon is found, check if it isn't some path or something else + if (!(text.at(colon + 1) == QChar('\\') && + text.at(colon + 1) == QChar('/'))) { + setFormat(i, colon - i, _formats[CodeKeyWord]); + } + } + + // underlined links + if (text.at(i) == QChar('h')) { + if (MH_SUBSTR(i, 4) == QLatin1String("http")) { + int space = text.indexOf(QChar(' '), i); + if (space == -1) space = textLen; + QTextCharFormat f = _formats[CodeString]; + f.setUnderlineStyle(QTextCharFormat::SingleUnderline); + setFormat(i, space - i, f); + i = space; + } + } + } +} + +/** + * @brief The INI highlighter + * @param text The text being highlighted + * @details This function is responsible for ini highlighting. + * It has basic error detection when + * (1) You opened a section but didn't close with bracket e.g [Section + * (2) You wrote an option but it didn't have an '=' + * Such errors will be marked with a dotted red underline + * + * It has comment highlighting support. Everything after a ';' will + * be highlighted till the end of the line. + * + * An option value pair will be highlighted regardless of space. Example: + * Option 1 = value + * In this, 'Option 1' will be highlighted completely and not just '1'. + * I am not sure about its correctness but for now its like this. + * + * The loop is unrolled frequently upon a match. Before adding anything + * new be sure to test in debug mode and apply bound checking as required. + */ +void MarkdownHighlighter::iniHighlighter(const QString &text) { + if (text.isEmpty()) return; + const auto textLen = text.length(); + + for (int i = 0; i < textLen; ++i) { + // start of a [section] + if (text.at(i) == QChar('[')) { + QTextCharFormat sectionFormat = _formats[CodeType]; + int sectionEnd = text.indexOf(QChar(']'), i); + // if an end bracket isn't found, we apply red underline to show + // error + if (sectionEnd == -1) { + sectionFormat.setUnderlineStyle(QTextCharFormat::DotLine); + sectionFormat.setUnderlineColor(Qt::red); + sectionEnd = textLen; + } + sectionEnd++; + setFormat(i, sectionEnd - i, sectionFormat); + i = sectionEnd; + if (i >= textLen) break; + } + + // comment ';' + else if (text.at(i) == QChar(';')) { + setFormat(i, textLen - i, _formats[CodeComment]); + i = textLen; + break; + } + + // key-val + else if (text.at(i).isLetter()) { + QTextCharFormat format = _formats[CodeKeyWord]; + int equalsPos = text.indexOf(QChar('='), i); + if (equalsPos == -1) { + format.setUnderlineColor(Qt::red); + format.setUnderlineStyle(QTextCharFormat::DotLine); + equalsPos = textLen; + } + setFormat(i, equalsPos - i, format); + i = equalsPos - 1; + if (i >= textLen) break; + } + // skip everything after '=' (except comment) + else if (text.at(i) == QChar('=')) { + const int findComment = text.indexOf(QChar(';'), i); + if (findComment == -1) break; + i = findComment - 1; + } + } +} + +void MarkdownHighlighter::cssHighlighter(const QString &text) { + if (text.isEmpty()) return; + const auto textLen = text.length(); + for (int i = 0; i < textLen; ++i) { + if (text[i] == QLatin1Char('.') || text[i] == QLatin1Char('#')) { + if (i + 1 >= textLen) return; + if (text[i + 1].isSpace() || text[i + 1].isNumber()) continue; + int space = text.indexOf(QLatin1Char(' '), i); + if (space < 0) { + space = text.indexOf(QLatin1Char('{'), i); + if (space < 0) { + space = textLen; + } + } + setFormat(i, space - i, _formats[CodeKeyWord]); + i = space; + } else if (text[i] == QLatin1Char('c')) { + if (MH_SUBSTR(i, 5) == QLatin1String("color")) { + i += 5; + const int colon = text.indexOf(QLatin1Char(':'), i); + if (colon < 0) continue; + i = colon; + ++i; + while (i < textLen) { + if (!text[i].isSpace()) break; + ++i; + } + int semicolon = text.indexOf(QLatin1Char(';'), i); + if (semicolon < 0) semicolon = textLen; + const QString color = text.mid(i, semicolon - i); + QColor c(color); + if (color.startsWith(QLatin1String("rgb"))) { + const int t = text.indexOf(QLatin1Char('('), i); + const int rPos = text.indexOf(QLatin1Char(','), t); + const int gPos = text.indexOf(QLatin1Char(','), rPos + 1); + const int bPos = text.indexOf(QLatin1Char(')'), gPos); + if (rPos > -1 && gPos > -1 && bPos > -1) { + const QString r = text.mid(t + 1, rPos - (t + 1)); + const QString g = text.mid(rPos + 1, gPos - (rPos + 1)); + const QString b = text.mid(gPos + 1, bPos - (gPos + 1)); + c.setRgb(r.toInt(), g.toInt(), b.toInt()); + } else { + c = _formats[HighlighterState::NoState] + .background() + .color(); + } + } + + if (!c.isValid()) { + continue; + } + + int lightness{}; + QColor foreground; + // really dark + if (c.lightness() <= 20) { + foreground = Qt::white; + } else if (c.lightness() > 20 && c.lightness() <= 51) { + foreground = QColor(204, 204, 204); + } else if (c.lightness() > 51 && c.lightness() <= 110) { + foreground = QColor(187, 187, 187); + } else if (c.lightness() > 127) { + lightness = c.lightness() + 100; + foreground = c.darker(lightness); + } else { + lightness = c.lightness() + 100; + foreground = c.lighter(lightness); + } + + QTextCharFormat f = _formats[CodeBlock]; + f.setBackground(c); + f.setForeground(foreground); + // clear prev format + setFormat(i, semicolon - i, QTextCharFormat()); + setFormat(i, semicolon - i, f); + i = semicolon; + } + } + } +} + +void MarkdownHighlighter::xmlHighlighter(const QString &text) { + if (text.isEmpty()) return; + const auto textLen = text.length(); + + setFormat(0, textLen, _formats[CodeBlock]); + + for (int i = 0; i < textLen; ++i) { + if (i + 1 < textLen && text[i] == QLatin1Char('<') && + text[i + 1] != QLatin1Char('!')) { + const int found = text.indexOf(QLatin1Char('>'), i); + if (found > 0) { + ++i; + if (text[i] == QLatin1Char('/')) ++i; + setFormat(i, found - i, _formats[CodeKeyWord]); + } + } + + if (text[i] == QLatin1Char('=')) { + int lastSpace = text.lastIndexOf(QLatin1Char(' '), i); + if (lastSpace == i - 1) + lastSpace = text.lastIndexOf(QLatin1Char(' '), i - 2); + if (lastSpace > 0) { + setFormat(lastSpace, i - lastSpace, _formats[CodeBuiltIn]); + } + } + + if (text[i] == QLatin1Char('\"')) { + const int pos = i; + int cnt = 1; + ++i; + // bound check + if ((i + 1) >= textLen) return; + while (i < textLen) { + if (text[i] == QLatin1Char('\"')) { + ++cnt; + ++i; + break; + } + ++i; + ++cnt; + // bound check + if ((i + 1) >= textLen) { + ++cnt; + break; + } + } + setFormat(pos, cnt, _formats[CodeString]); + } + } +} + +void MarkdownHighlighter::makeHighlighter(const QString &text) { + const int colonPos = text.indexOf(QLatin1Char(':')); + if (colonPos == -1) return; + setFormat(0, colonPos, _formats[CodeBuiltIn]); +} + +/** + * @brief The Forth highlighter + * @param text + * @details This function performs filtering of Forth code and high lights + * the specific details. + * 1. It highlights the "\ " comments + * 2. It highlights the "( " comments + */ +void MarkdownHighlighter::forthHighlighter(const QString &text) { + if (text.isEmpty()) return; + + const auto textLen = text.length(); + + // Default Format + setFormat(0, textLen, _formats[CodeBlock]); + + for (int i = 0; i < textLen; ++i) { + // 1, It highlights the "\ " comments + if (i + 1 <= textLen && text[i] == QLatin1Char('\\') && + text[i + 1] == QLatin1Char(' ')) { + // The full line is commented + setFormat(i + 1, textLen - 1, _formats[CodeComment]); + break; + } + // 2. It highlights the "( " comments + else if (i + 1 <= textLen && text[i] == QLatin1Char('(') && + text[i + 1] == QLatin1Char(' ')) { + // Find the End bracket + int lastBracket = text.lastIndexOf(QLatin1Char(')'), i); + // Can't Handle wrong Format + if (lastBracket <= 0) return; + // ' )' at the end of the comment + if (lastBracket <= textLen && + text[lastBracket] == QLatin1Char(' ')) { + setFormat(i, lastBracket, _formats[CodeComment]); + } + } + } +} + +/** + * Highlight multi-line frontmatter blocks + * + * @param text + */ +void MarkdownHighlighter::highlightFrontmatterBlock(const QString &text) { + if (text == QLatin1String("---")) { + const bool foundEnd = + previousBlockState() == HighlighterState::FrontmatterBlock; + + // return if the frontmatter block was already highlighted in previous + // blocks, there just can be one frontmatter block + if (!foundEnd && document()->firstBlock() != currentBlock()) { + return; + } + + setCurrentBlockState(foundEnd ? HighlighterState::FrontmatterBlockEnd + : HighlighterState::FrontmatterBlock); + + QTextCharFormat &maskedFormat = + _formats[HighlighterState::MaskedSyntax]; + setFormat(0, text.length(), maskedFormat); + } else if (previousBlockState() == HighlighterState::FrontmatterBlock) { + setCurrentBlockState(HighlighterState::FrontmatterBlock); + setFormat(0, text.length(), _formats[HighlighterState::MaskedSyntax]); } } @@ -612,29 +1639,178 @@ void MarkdownHighlighter::highlightCodeBlock(QString text) { * * @param text */ -void MarkdownHighlighter::highlightCommentBlock(QString text) { - bool highlight = false; - text = text.trimmed(); - QString startText = ""; +void MarkdownHighlighter::highlightCommentBlock(const QString &text) { + if (text.startsWith(QLatin1String(" ")) || + text.startsWith(QLatin1Char('\t'))) + return; + + const QString &trimmedText = text.trimmed(); + const QString startText(QStringLiteral("")); // we will skip this case because that is an inline comment and causes // troubles here - if (text.startsWith(startText) && text.contains(endText)) { + if (trimmedText.startsWith(startText) && trimmedText.contains(endText)) { return; } - if (text.startsWith(startText) || - (!text.endsWith(endText) && - (previousBlockState() == HighlighterState::Comment))) { - setCurrentBlockState(HighlighterState::Comment); - highlight = true; - } else if (text.endsWith(endText)) { - highlight = true; + if (!trimmedText.startsWith(startText) && trimmedText.contains(startText)) + return; + + const bool isComment = + trimmedText.startsWith(startText) || + (!trimmedText.endsWith(endText) && previousBlockState() == Comment); + const bool isCommentEnd = + trimmedText.endsWith(endText) && previousBlockState() == Comment; + const bool highlight = isComment || isCommentEnd; + + if (isComment) setCurrentBlockState(Comment); + if (highlight) setFormat(0, text.length(), _formats[Comment]); +} + +/** + * @brief Highlights thematic breaks i.e., horizontal ruler
+ * @param text + */ +void MarkdownHighlighter::highlightThematicBreak(const QString &text) { + int i = 0; + for (; i < 4 && i < text.length(); ++i) { + if (text.at(i) != QLatin1Char(' ')) + break; } - if (highlight) { - setFormat(0, text.length(), _formats[HighlighterState::Comment]); + const QString sText = text.mid(i); + if (sText.isEmpty() || i == 4 || text.startsWith(QLatin1Char('\t'))) + return; + + const char c = sText.at(0).toLatin1(); + if (c != '-' && c != '_' && c != '*') + return; + + int len = 0; + bool hasSameChars = true; + for (int i = 0; i < sText.length(); ++i) { + if (c != sText.at(i) && sText.at(i) != QLatin1Char(' ')) { + hasSameChars = false; + break; + } + if (sText.at(i) != QLatin1Char(' ')) ++len; + } + if (len < 3) return; + + if (hasSameChars) + setFormat(0, text.length(), _formats[HorizontalRuler]); +} + +void MarkdownHighlighter::highlightCheckbox(const QString &text, int curPos) +{ + if (curPos + 4 >= text.length()) + return; + + const bool hasOpeningBracket = text.at(curPos + 2) == QLatin1Char('['); + const bool hasClosingBracket = text.at(curPos + 4) == QLatin1Char(']'); + const QChar midChar = text.at(curPos + 3); + const bool hasXorSpace = midChar == QLatin1Char(' ') || midChar == QLatin1Char('x') || midChar == QLatin1Char('X'); + const bool hasDash = midChar == QLatin1Char('-'); + + if (hasOpeningBracket && hasClosingBracket && (hasXorSpace || hasDash)) { + const int start = curPos + 2; + constexpr int length = 3; + + const auto fmt = hasXorSpace ? + (midChar == QLatin1Char(' ') ? CheckBoxUnChecked : CheckBoxChecked) : + MaskedSyntax; + + setFormat(start, length, _formats[fmt]); + } +} + +static bool isBeginningOfList(QChar front) +{ + return front == QLatin1Char('-') || front == QLatin1Char('+') || + front == QLatin1Char('*') || front.isNumber(); +} + +/** + * @brief Highlight lists in markdown + * @param text - current text block + */ +void MarkdownHighlighter::highlightLists(const QString &text) { + int spaces = 0; + // Skip any spaces in the beginning + while (spaces < text.length() && text.at(spaces).isSpace()) ++spaces; + + // return if we reached the end + if (spaces >= text.length()) + return; + + const QChar front = text.at(spaces); + // check for start of list + if (!isBeginningOfList(front)) { + return; + } + + const int curPos = spaces; + + // Ordered List + if (front.isNumber()) { + int number = curPos; + // move forward till first non-number char + while (number < text.length() && text.at(number).isNumber()) ++number; + + // reached end? + if (number + 1 >= text.length()) return; + + // there should be a '.' or ')' after a number + if ((text.at(number) == QLatin1Char('.') || + text.at(number) == QLatin1Char(')')) && + (text.at(number + 1) == QLatin1Char(' '))) { + setCurrentBlockState(List); + setFormat(curPos, number - curPos + 1, _formats[List]); + + // highlight checkbox if any + highlightCheckbox(text, number); + } + + return; + } + + // if its just a '-' etc, no highlighting + if (curPos + 1 >= text.length()) return; + + // check for a space after it + if (text.at(curPos + 1) != QLatin1Char(' ')) + return; + + // check if we are in checkbox list + highlightCheckbox(text, curPos); + + /* Unordered List */ + setCurrentBlockState(List); + setFormat(curPos, 1, _formats[List]); +} + +/** + * Format italics, bolds and links in headings(h1-h6) + * + * @param format The format that is being applied + * @param match The regex match + * @param capturedGroup The captured group + */ +void MarkdownHighlighter::setHeadingStyles(HighlighterState rule, + const QRegularExpressionMatch &match, + const int capturedGroup) { + auto state = static_cast(currentBlockState()); + const QTextCharFormat &f = _formats[state]; + + if (rule == HighlighterState::Link) { + auto linkFmt = _formats[Link]; + linkFmt.setFontPointSize(f.fontPointSize()); + if (capturedGroup == 1) { + setFormat(match.capturedStart(capturedGroup), + match.capturedLength(capturedGroup), linkFmt); + } + return; } } @@ -644,55 +1820,802 @@ void MarkdownHighlighter::highlightCommentBlock(QString text) { * @param text */ void MarkdownHighlighter::highlightAdditionalRules( - QVector &rules, QString text) { - QTextCharFormat &maskedFormat = _formats[HighlighterState::MaskedSyntax]; + const QVector &rules, const QString &text) { + const auto &maskedFormat = _formats[HighlighterState::MaskedSyntax]; - for(const HighlightingRule &rule : rules) { - // continue if an other current block state was already set if - // disableIfCurrentStateIsSet is set - if (rule.disableIfCurrentStateIsSet && - (currentBlockState() != HighlighterState::NoState)) { - continue; - } + for (const HighlightingRule &rule : rules) { + // continue if another current block state was already set if + // disableIfCurrentStateIsSet is set + if (currentBlockState() != NoState) continue; - QRegularExpression expression(rule.pattern); - QRegularExpressionMatchIterator iterator = expression.globalMatch(text); - int capturingGroup = rule.capturingGroup; - int maskedGroup = rule.maskedGroup; - QTextCharFormat &format = _formats[rule.state]; + const bool contains = text.contains(rule.shouldContain); + if (!contains) continue; - // store the current block state if useStateAsCurrentBlockState - // is set - if (iterator.hasNext() && rule.useStateAsCurrentBlockState) { - setCurrentBlockState(rule.state); - } + auto iterator = rule.pattern.globalMatch(text); + const uint8_t capturingGroup = rule.capturingGroup; + const uint8_t maskedGroup = rule.maskedGroup; + const QTextCharFormat &format = _formats[rule.state]; - // find and format all occurrences - while (iterator.hasNext()) { - QRegularExpressionMatch match = iterator.next(); + // find and format all occurrences + while (iterator.hasNext()) { + QRegularExpressionMatch match = iterator.next(); - // if there is a capturingGroup set then first highlight - // everything as MaskedSyntax and highlight capturingGroup - // with the real format - if (capturingGroup > 0) { - QTextCharFormat currentMaskedFormat = maskedFormat; - // set the font size from the current rule's font format - if (format.fontPointSize() > 0) { - currentMaskedFormat.setFontPointSize(format.fontPointSize()); - } + // if there is a capturingGroup set then first highlight + // everything as MaskedSyntax and highlight capturingGroup + // with the real format + if (capturingGroup > 0) { + QTextCharFormat currentMaskedFormat = maskedFormat; + // set the font size from the current rule's font format + if (format.fontPointSize() > 0) { + currentMaskedFormat.setFontPointSize( + format.fontPointSize()); + } + if (isHeading(currentBlockState())) { + // setHeadingStyles(format, match, maskedGroup); + + } else { setFormat(match.capturedStart(maskedGroup), match.capturedLength(maskedGroup), currentMaskedFormat); } + } + if (isHeading(currentBlockState())) { + setHeadingStyles(rule.state, match, capturingGroup); + } else { setFormat(match.capturedStart(capturingGroup), - match.capturedLength(capturingGroup), - format); + match.capturedLength(capturingGroup), format); } } + } } -void MarkdownHighlighter::setHighlightingOptions(HighlightingOptions options) { +/** + * @brief helper function to check if we are in a link while highlighting inline + * rules + * @param pos + * @param range + */ +int isInLinkRange(int pos, QVector> &range) { + int j = 0; + for (const auto &i : range) { + if (pos >= i.first && pos <= i.second) { + // return the length of the range so that we can skip it + const int len = i.second - i.first; + range.remove(j); + return len; + } + ++j; + } + return -1; +} + +/** + * @brief highlight inline rules aka Emphasis, bolds, inline code spans, + * underlines, strikethrough, links, and images. + */ +void MarkdownHighlighter::highlightInlineRules(const QString &text) { + bool isEmStrongDone = false; + + for (int i = 0; i < text.length(); ++i) { + QChar currentChar = text.at(i); + + if (currentChar == QLatin1Char('`') || + currentChar == QLatin1Char('~')) { + i = highlightInlineSpans(text, i, currentChar); + } else if (currentChar == QLatin1Char('<') && + MH_SUBSTR(i, 4) == QLatin1String(" + * @param text + * @param pos + * @return position after the comment + */ +int MarkdownHighlighter::highlightInlineComment(const QString &text, int pos) { + const int start = pos; + pos += 4; + + if (pos >= text.length()) return pos; + + int commentEnd = text.indexOf(QLatin1String("-->"), pos); + if (commentEnd == -1) return pos; + + commentEnd += 3; + setFormat(start, commentEnd - start, _formats[Comment]); + return commentEnd - 1; +} + +/**************************************** + * EM and Strong Parsing + Highlighting * + ****************************************/ + +struct Delimiter { + int pos; + int len; + int end; + int jump; + bool open; + bool close; + char marker; +}; + +inline bool isMDAsciiPunct(const int ch) noexcept { + return (ch >= 33 && ch <= 47) || (ch >= 58 && ch <= 64) || + (ch >= 91 && ch <= 96) || (ch >= 123 && ch <= 126); +} + +/** + * @brief scans a chain of '*' or '_' + * @param text: current text block + * @param start: current position in the text + * @param canSplitWord: is Underscore + * @return length, canOpen, canClose + * @details Helper function for Em and strong highlighting + */ +QPair> scanDelims(const QString &text, const int start, + const bool canSplitWord) { + int pos = start; + const int textLen = text.length(); + const QChar marker = text.at(start); + bool leftFlanking = true; + bool rightFlanking = true; + + const QChar lastChar = start > 0 ? text.at(start - 1) : QChar('\0'); + + while (pos < textLen && text.at(pos) == marker) ++pos; + const int length = pos - start; + + const QChar nextChar = pos + 1 < textLen ? text.at(pos) : QChar('\0'); + + const bool isLastPunct = + isMDAsciiPunct(lastChar.toLatin1()) || lastChar.isPunct(); + const bool isNextPunct = + isMDAsciiPunct(nextChar.toLatin1()) || nextChar.isPunct(); + // treat line end and start as whitespace + const bool isLastWhiteSpace = lastChar.isNull() ? true : lastChar.isSpace(); + const bool isNextWhiteSpace = nextChar.isNull() ? true : nextChar.isSpace(); + + if (isNextWhiteSpace) { + leftFlanking = false; + } else if (isNextPunct) { + if (!(isLastWhiteSpace || isLastPunct)) leftFlanking = false; + } + + if (isLastWhiteSpace) { + rightFlanking = false; + } else if (isLastPunct) { + if (!(isNextWhiteSpace || isNextPunct)) rightFlanking = false; + } + + // qDebug () << isNextWhiteSpace << marker; + // qDebug () << text << leftFlanking << rightFlanking << lastChar << + // nextChar; + + const bool canOpen = canSplitWord + ? leftFlanking + : leftFlanking && (!rightFlanking || isLastPunct); + const bool canClose = canSplitWord + ? rightFlanking + : rightFlanking && (!leftFlanking || isNextPunct); + + return QPair>{length, {canOpen, canClose}}; +} + +int collectEmDelims(const QString &text, int curPos, + QVector &delims) { + const char marker = text.at(curPos).toLatin1(); + const auto result = scanDelims(text, curPos, marker == '*'); + const int length = result.first; + const bool canOpen = result.second.first; + const bool canClose = result.second.second; + for (int i = 0; i < length; ++i) { + const Delimiter d = {curPos + i, length, -1, i, + canOpen, canClose, marker}; + delims.append(d); + } + return curPos + length; +} + +void balancePairs(QVector &delims) { + for (int i = 0; i < delims.length(); ++i) { + const auto &lastDelim = delims.at(i); + + if (!lastDelim.close) continue; + + int j = i - lastDelim.jump - 1; + + while (j >= 0) { + const auto &curDelim = delims.at(j); + if (curDelim.open && curDelim.marker == lastDelim.marker && + curDelim.end < 0) { + const bool oddMatch = (curDelim.close || lastDelim.open) && + curDelim.len != -1 && + lastDelim.len != -1 && + (curDelim.len + lastDelim.len) % 3 == 0; + if (!oddMatch) { + delims[i].jump = i - j; + delims[i].open = false; + delims[j].end = i; + delims[j].jump = 0; + break; + } + } + j -= curDelim.jump + 1; + } + } +} + +void MarkdownHighlighter::clearRangesForBlock(int blockNumber, RangeType type) +{ + if (!_ranges.value(blockNumber).isEmpty()) { + auto& rangeList = _ranges[currentBlock().blockNumber()]; + rangeList.erase(std::remove_if(rangeList.begin(), rangeList.end(), + [type](const InlineRange& range) { + return range.type == type; + }), rangeList.end()); + } +} + +QPair +MarkdownHighlighter::findPositionInRanges(MarkdownHighlighter::RangeType type, + int blockNum, int pos) const { + const QVector rangeList = _ranges.value(blockNum); + auto it = std::find_if(rangeList.cbegin(), rangeList.cend(), + [pos, type](const InlineRange& range){ + if ((pos == range.begin || pos == range.end) && range.type == type) + return true; + return false; + }); + if (it == rangeList.cend()) + return {-1, -1}; + return { it->begin, it->end }; +} + +bool MarkdownHighlighter::isPosInACodeSpan(int blockNumber, int position) const +{ + const QVector rangeList = _ranges.value(blockNumber); + return std::find_if(rangeList.cbegin(), rangeList.cend(), + [position](const InlineRange& range){ + if (position > range.begin && position < range.end && range.type == RangeType::CodeSpan) + return true; + return false; + }) != rangeList.cend(); +} + +bool MarkdownHighlighter::isPosInALink(int blockNumber, int position) const { + const QVector rangeList = _ranges.value(blockNumber); + return std::find_if(rangeList.cbegin(), rangeList.cend(), + [position](const InlineRange &range) { + return position > range.begin && + position < range.end && + range.type == RangeType::Link; + }) != rangeList.cend(); +} + +QPair MarkdownHighlighter::getSpanRange(MarkdownHighlighter::RangeType rangeType, int blockNumber, int position) const +{ + const QVector rangeList = _ranges.value(blockNumber); + const auto it = std::find_if(rangeList.cbegin(), rangeList.cend(), + [position, rangeType](const InlineRange& range){ + if (position > range.begin && position < range.end && range.type == rangeType) + return true; + return false; + }); + + if (it == rangeList.cend()) { + return QPair(-1, -1); + } else { + return QPair(it->begin, it->end); + } +} + +/** + * @brief highlights Em/Strong in text editor + */ +void MarkdownHighlighter::highlightEmAndStrong(const QString &text, + const int pos) { + clearRangesForBlock(currentBlock().blockNumber(), RangeType::Emphasis); + + // 1. collect all em/strong delimiters + QVector delims; + for (int i = pos; i < text.length(); ++i) { + if (text.at(i) != QLatin1Char('_') && text.at(i) != QLatin1Char('*')) + continue; + + bool isInCodeSpan = isPosInACodeSpan(currentBlock().blockNumber(), i); + if (isInCodeSpan) + continue; + + i = collectEmDelims(text, i, delims); + --i; + } + + // 2. Balance pairs + balancePairs(delims); + + // start,length -> helper for applying masking later + QVector> masked; + masked.reserve(delims.size() / 2); + + // 3. final processing & highlighting + for (int i = delims.length() - 1; i >= 0; --i) { + const auto &startDelim = delims.at(i); + if (startDelim.marker != QLatin1Char('_') && + startDelim.marker != QLatin1Char('*')) + continue; + if (startDelim.end == -1) continue; + + const auto &endDelim = delims.at(startDelim.end); + auto state = static_cast(currentBlockState()); + + const bool isStrong = + i > 0 && delims.at(i - 1).end == startDelim.end + 1 && + delims.at(i - 1).pos == startDelim.pos - 1 && + delims.at(startDelim.end + 1).pos == endDelim.pos + 1 && + delims.at(i - 1).marker == startDelim.marker; + if (isStrong) { + // qDebug () << "St: " << startDelim.pos << endDelim.pos; + // qDebug () << "St Txt: "<< text.mid(startDelim.pos, + // endDelim.pos - startDelim.pos); + int k = startDelim.pos; + while (text.at(k) == startDelim.marker) + ++k; // look for first letter after the delim chain + // per character highlighting + const int boldLen = endDelim.pos - startDelim.pos; + const bool underline = _highlightingOptions.testFlag(Underline) && + startDelim.marker == QLatin1Char('_'); + while (k != (startDelim.pos + boldLen)) { + QTextCharFormat fmt = QSyntaxHighlighter::format(k); +#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) + fmt.setFontFamily(_formats[Bold].fontFamily()); +#else + const QStringList fontFamilies = _formats[Bold].fontFamilies().toStringList(); + if (!fontFamilies.isEmpty()) + fmt.setFontFamilies(fontFamilies); +#endif + + if (_formats[state].fontPointSize() > 0) + fmt.setFontPointSize(_formats[state].fontPointSize()); + + // if we are in plain text, use the format's specified color + if (fmt.foreground() == QTextCharFormat().foreground()) + fmt.setForeground(_formats[Bold].foreground()); + if (underline) { + fmt.setForeground(_formats[StUnderline].foreground()); + fmt.setFont(_formats[StUnderline].font()); + fmt.setFontUnderline(_formats[StUnderline].fontUnderline()); + } else if (_formats[Bold].font().bold()) + fmt.setFontWeight(QFont::Bold); + setFormat(k, 1, fmt); + ++k; + } + masked.append({startDelim.pos - 1, 2}); + masked.append({endDelim.pos, 2}); + + int block = currentBlock().blockNumber(); + _ranges[block].append(InlineRange( + startDelim.pos, + endDelim.pos + 1, + RangeType::Emphasis + )); + _ranges[block].append(InlineRange( + startDelim.pos - 1, + endDelim.pos, + RangeType::Emphasis + )); + --i; + } else { + // qDebug () << "Em: " << startDelim.pos << endDelim.pos; + // qDebug () << "Em Txt: " << text.mid(startDelim.pos, + // endDelim.pos - startDelim.pos); + int k = startDelim.pos; + while (text.at(k) == startDelim.marker) ++k; + const bool underline = _highlightingOptions.testFlag(Underline) && + startDelim.marker == QLatin1Char('_'); + const int itLen = endDelim.pos - startDelim.pos; + while (k != (startDelim.pos + itLen)) { + QTextCharFormat fmt = QSyntaxHighlighter::format(k); + +#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) + fmt.setFontFamily(_formats[Italic].fontFamily()); +#else + const QStringList fontFamilies = _formats[Italic].fontFamilies().toStringList(); + if (!fontFamilies.isEmpty()) + fmt.setFontFamilies(fontFamilies); +#endif + + if (_formats[state].fontPointSize() > 0) + fmt.setFontPointSize(_formats[state].fontPointSize()); + + if (fmt.foreground() == QTextCharFormat().foreground()) + fmt.setForeground(_formats[Italic].foreground()); + + if (underline) + fmt.setFontUnderline(_formats[StUnderline].fontUnderline()); + else + fmt.setFontItalic(_formats[Italic].fontItalic()); + setFormat(k, 1, fmt); + ++k; + } + masked.append({startDelim.pos, 1}); + masked.append({endDelim.pos, 1}); + + int block = currentBlock().blockNumber(); + _ranges[block].append(InlineRange( + startDelim.pos, + endDelim.pos, + RangeType::Emphasis + )); + } + } + + // 4. Apply masked syntax + for (int i = 0; i < masked.length(); ++i) { + QTextCharFormat maskedFmt = _formats[MaskedSyntax]; + auto state = static_cast(currentBlockState()); + if (_formats[state].fontPointSize() > 0) + maskedFmt.setFontPointSize(_formats[state].fontPointSize()); + setFormat(masked.at(i).first, masked.at(i).second, maskedFmt); + } +} + +void MarkdownHighlighter::setHighlightingOptions( + const HighlightingOptions options) { _highlightingOptions = options; } + +#undef MH_SUBSTR diff --git a/client/qmarkdowntextedit/markdownhighlighter.h b/client/qmarkdowntextedit/markdownhighlighter.h index e66f86f..4210a84 100644 --- a/client/qmarkdowntextedit/markdownhighlighter.h +++ b/client/qmarkdowntextedit/markdownhighlighter.h @@ -1,52 +1,125 @@ /* - * Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com + * MIT License * - * 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; version 2 of the License. + * Copyright (c) 2014-2023 Patrizio Bekerle -- * - * 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. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * QPlainTextEdit markdown highlighter + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * QPlainTextEdit Markdown highlighter */ - #pragma once -#include -#include #include +#include +#include + +#ifdef QT_QUICK_LIB +#include +#endif QT_BEGIN_NAMESPACE class QTextDocument; QT_END_NAMESPACE -class MarkdownHighlighter : public QSyntaxHighlighter -{ -Q_OBJECT +class MarkdownHighlighter : public QSyntaxHighlighter { + Q_OBJECT -public: - enum HighlightingOption{ +#ifdef QT_QUICK_LIB + Q_PROPERTY(QQuickTextDocument *textDocument READ textDocument WRITE + setTextDocument NOTIFY textDocumentChanged) + + QQuickTextDocument *m_quickDocument = nullptr; + + signals: + void textDocumentChanged(); + + public: + inline QQuickTextDocument *textDocument() const { return m_quickDocument; }; + void setTextDocument(QQuickTextDocument *textDocument) { + if (!textDocument) return; + m_quickDocument = textDocument; + setDocument(m_quickDocument->textDocument()); + Q_EMIT textDocumentChanged(); + }; +#endif + + public: + enum HighlightingOption { None = 0, - FullyHighlightedBlockQuote = 0x01 + FullyHighlightedBlockQuote = 0x01, + Underline = 0x02 }; Q_DECLARE_FLAGS(HighlightingOptions, HighlightingOption) - MarkdownHighlighter(QTextDocument *parent = nullptr, - HighlightingOptions highlightingOptions = - HighlightingOption::None); + MarkdownHighlighter( + QTextDocument *parent = nullptr, + HighlightingOptions highlightingOptions = HighlightingOption::None); - // we use some predefined numbers here to be compatible with - // the peg-markdown parser + static inline QColor codeBlockBackgroundColor() { + const QBrush brush = _formats[CodeBlock].background(); + + if (!brush.isOpaque()) { + return QColor(Qt::transparent); + } + + return brush.color(); + } + + static constexpr inline bool isOctal(const char c) { + return (c >= '0' && c <= '7'); + } + static constexpr inline bool isHex(const char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'); + } + static constexpr inline bool isCodeBlock(const int state) { + return state == MarkdownHighlighter::CodeBlock || + state == MarkdownHighlighter::CodeBlockTilde || + state == MarkdownHighlighter::CodeBlockComment || + state == MarkdownHighlighter::CodeBlockTildeComment || + state >= MarkdownHighlighter::CodeCpp; + } + static constexpr inline bool isCodeBlockEnd(const int state) { + return state == MarkdownHighlighter::CodeBlockEnd || + state == MarkdownHighlighter::CodeBlockTildeEnd; + } + static constexpr inline bool isHeading(const int state) { + return state >= H1 && state <= H6; + } + + enum class RangeType { CodeSpan, Emphasis, Link }; + + QPair findPositionInRanges(MarkdownHighlighter::RangeType type, int blockNum, int pos) const; + bool isPosInACodeSpan(int blockNumber, int position) const; + bool isPosInALink(int blockNumber, int position) const; + QPair getSpanRange(RangeType rangeType, int blockNumber, int position) const; + + // we used some predefined numbers here to be compatible with + // the peg-Markdown parser enum HighlighterState { NoState = -1, Link = 0, Image = 3, CodeBlock, + CodeBlockComment, Italic = 7, Bold, List, @@ -64,74 +137,217 @@ public: MaskedSyntax, CurrentLineBackgroundColor, BrokenLink, + FrontmatterBlock, + TrailingSpace, + CheckBoxUnChecked, + CheckBoxChecked, + StUnderline, + + // code highlighting + CodeKeyWord = 1000, + CodeString = 1001, + CodeComment = 1002, + CodeType = 1003, + CodeOther = 1004, + CodeNumLiteral = 1005, + CodeBuiltIn = 1006, // internal + CodeBlockIndented = 96, + CodeBlockTildeEnd = 97, + CodeBlockTilde = 98, + CodeBlockTildeComment, CodeBlockEnd = 100, - HeadlineEnd + HeadlineEnd, + FrontmatterBlockEnd, + + // languages + /********* + * When adding a language make sure that its value is a multiple of 2 + * This is because we use the next number as comment for that language + * In case the language doesn't support multiline comments in the + * traditional C++ sense, leave the next value empty. Otherwise mark the + * next value as comment for that language. e.g CodeCpp = 200 + * CodeCppComment = 201 + */ + CodeCpp = 200, + CodeCppComment = 201, + CodeJs = 202, + CodeJsComment = 203, + CodeC = 204, + CodeCComment = 205, + CodeBash = 206, + CodePHP = 208, + CodePHPComment = 209, + CodeQML = 210, + CodeQMLComment = 211, + CodePython = 212, + CodeRust = 214, + CodeRustComment = 215, + CodeJava = 216, + CodeJavaComment = 217, + CodeCSharp = 218, + CodeCSharpComment = 219, + CodeGo = 220, + CodeGoComment = 221, + CodeV = 222, + CodeVComment = 223, + CodeSQL = 224, + CodeJSON = 226, + CodeXML = 228, + CodeCSS = 230, + CodeCSSComment = 231, + CodeTypeScript = 232, + CodeTypeScriptComment = 233, + CodeYAML = 234, + CodeINI = 236, + CodeTaggerScript = 238, + CodeVex = 240, + CodeVexComment = 241, + CodeCMake = 242, + CodeMake = 244, + CodeNix = 246, + CodeForth = 248, + CodeForthComment = 249 }; Q_ENUM(HighlighterState) -// enum BlockState { -// NoBlockState = 0, -// H1, -// H2, -// H3, -// Table, -// CodeBlock, -// CodeBlockEnd -// }; - - void setTextFormats(QHash formats); - void setTextFormat(HighlighterState state, QTextCharFormat format); + static void setTextFormats( + QHash formats); + static void setTextFormat(HighlighterState state, QTextCharFormat format); void clearDirtyBlocks(); - void setHighlightingOptions(HighlightingOptions options); + void setHighlightingOptions(const HighlightingOptions options); void initHighlightingRules(); -signals: + Q_SIGNALS: void highlightingFinished(); -protected slots: + protected Q_SLOTS: void timerTick(); -protected: + protected: struct HighlightingRule { - HighlightingRule(const HighlighterState state_) : state(state_) {} + explicit HighlightingRule(const HighlighterState state_) + : state(state_) {} HighlightingRule() = default; QRegularExpression pattern; + QString shouldContain; HighlighterState state = NoState; - int capturingGroup = 0; - int maskedGroup = 0; - bool useStateAsCurrentBlockState = false; - bool disableIfCurrentStateIsSet = false; + uint8_t capturingGroup = 0; + uint8_t maskedGroup = 0; + }; + struct InlineRange { + int begin; + int end; + RangeType type; + InlineRange() = default; + InlineRange(int begin_, int end_, RangeType type_) : + begin{begin_}, end{end_}, type{type_} + {} }; - void highlightBlock(const QString &text) Q_DECL_OVERRIDE; - void initTextFormats(int defaultFontSize = 12); + void highlightBlock(const QString &text) override; - void highlightMarkdown(QString text); + static void initTextFormats(int defaultFontSize = 12); - void highlightHeadline(QString text); + static void initCodeLangs(); - void highlightAdditionalRules(QVector &rules, - QString text); + void highlightMarkdown(const QString &text); - void highlightCodeBlock(QString text); + void formatAndMaskRemaining(int formatBegin, int formatLength, + int beginningText, int endText, + const QTextCharFormat &format); - void highlightCommentBlock(QString text); + /****************************** + * BLOCK LEVEL FUNCTIONS + ******************************/ - void addDirtyBlock(QTextBlock block); + void highlightHeadline(const QString &text); + + void highlightSubHeadline(const QString &text, HighlighterState state); + + void highlightAdditionalRules(const QVector &rules, + const QString &text); + + void highlightFrontmatterBlock(const QString &text); + + void highlightCommentBlock(const QString &text); + + void highlightThematicBreak(const QString &text); + + void highlightLists(const QString &text); + + void highlightCheckbox(const QString &text, int curPos); + + /****************************** + * INLINE FUNCTIONS + ******************************/ + + void highlightInlineRules(const QString &text); + + int highlightInlineSpans(const QString &text, + int currentPos, const QChar c); + + void highlightEmAndStrong(const QString &text, const int pos); + + Q_REQUIRED_RESULT int highlightInlineComment(const QString &text, int pos); + + int highlightLinkOrImage(const QString &text, int startIndex); + + void setHeadingStyles(MarkdownHighlighter::HighlighterState rule, + const QRegularExpressionMatch &match, + const int capturedGroup); + + /****************************** + * CODE HIGHLIGHTING FUNCTIONS + ******************************/ + + void highlightIndentedCodeBlock(const QString &text); + + void highlightCodeFence(const QString &text); + + void highlightCodeBlock(const QString &text, + const QString &opener = QStringLiteral("```")); + + void highlightSyntax(const QString &text); + + Q_REQUIRED_RESULT int highlightNumericLiterals(const QString &text, int i); + + Q_REQUIRED_RESULT int highlightStringLiterals(QChar strType, + const QString &text, int i); + + void ymlHighlighter(const QString &text); + + void iniHighlighter(const QString &text); + + void cssHighlighter(const QString &text); + + void xmlHighlighter(const QString &text); + + void makeHighlighter(const QString &text); + + void forthHighlighter(const QString &text); + + void taggerScriptHighlighter(const QString &text); + + void addDirtyBlock(const QTextBlock &block); void reHighlightDirtyBlocks(); - QVector _highlightingRulesPre; - QVector _highlightingRulesAfter; - QVector _dirtyTextBlocks; - QHash _formats; - QTimer *_timer; + void clearRangesForBlock(int blockNumber, RangeType type); + bool _highlightingFinished; HighlightingOptions _highlightingOptions; + QTimer *_timer; + QVector _dirtyTextBlocks; + QVector> _linkRanges; - void setCurrentBlockMargin(HighlighterState state); + QHash> _ranges; + + static QVector _highlightingRules; + static QHash _formats; + static QHash _langStringToEnum; + static constexpr int tildeOffset = 300; }; diff --git a/client/qmarkdowntextedit/media/edit-find-replace.svg b/client/qmarkdowntextedit/media/edit-find-replace.svg index c1c4671..1193301 100644 --- a/client/qmarkdowntextedit/media/edit-find-replace.svg +++ b/client/qmarkdowntextedit/media/edit-find-replace.svg @@ -1,15 +1 @@ - - - - - - + \ No newline at end of file diff --git a/client/qmarkdowntextedit/media/format-text-superscript.svg b/client/qmarkdowntextedit/media/format-text-superscript.svg index 871845e..c9d239c 100644 --- a/client/qmarkdowntextedit/media/format-text-superscript.svg +++ b/client/qmarkdowntextedit/media/format-text-superscript.svg @@ -1,16 +1 @@ - - - - - - - - + \ No newline at end of file diff --git a/client/qmarkdowntextedit/media/go-bottom.svg b/client/qmarkdowntextedit/media/go-bottom.svg index 9b85210..42de59c 100644 --- a/client/qmarkdowntextedit/media/go-bottom.svg +++ b/client/qmarkdowntextedit/media/go-bottom.svg @@ -1,156 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/client/qmarkdowntextedit/media/go-top.svg b/client/qmarkdowntextedit/media/go-top.svg index 50b4ca6..98d91cf 100644 --- a/client/qmarkdowntextedit/media/go-top.svg +++ b/client/qmarkdowntextedit/media/go-top.svg @@ -1,476 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/client/qmarkdowntextedit/media/window-close.svg b/client/qmarkdowntextedit/media/window-close.svg index 5d1539d..c7e44ec 100644 --- a/client/qmarkdowntextedit/media/window-close.svg +++ b/client/qmarkdowntextedit/media/window-close.svg @@ -1,148 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - + \ No newline at end of file diff --git a/client/qmarkdowntextedit/old_screenshot.png b/client/qmarkdowntextedit/old_screenshot.png new file mode 100644 index 0000000..f2a1508 Binary files /dev/null and b/client/qmarkdowntextedit/old_screenshot.png differ diff --git a/client/qmarkdowntextedit/qmarkdowntextedit-app.pro b/client/qmarkdowntextedit/qmarkdowntextedit-app.pro new file mode 100644 index 0000000..9d82f8d --- /dev/null +++ b/client/qmarkdowntextedit/qmarkdowntextedit-app.pro @@ -0,0 +1,17 @@ +TARGET = QMarkdownTextedit +TEMPLATE = app +QT += core gui widgets +CONFIG += c++11 + +SOURCES = main.cpp mainwindow.cpp +HEADERS = mainwindow.h +FORMS = mainwindow.ui + +LIBS += -lQMarkdownTextedit -L$$OUT_PWD + +win32: LIBS += -L$$OUT_PWD/release -L$$OUT_PWD/debug + +target.path = $$[QT_INSTALL_BINS] + +INSTALLS += target + diff --git a/client/qmarkdowntextedit/qmarkdowntextedit-headers.pri b/client/qmarkdowntextedit/qmarkdowntextedit-headers.pri index 646e593..68ec123 100644 --- a/client/qmarkdowntextedit/qmarkdowntextedit-headers.pri +++ b/client/qmarkdowntextedit/qmarkdowntextedit-headers.pri @@ -1,6 +1,8 @@ INCLUDEPATH += $$PWD/ HEADERS += \ + $$PWD/linenumberarea.h \ $$PWD/markdownhighlighter.h \ $$PWD/qmarkdowntextedit.h \ + $$PWD/qownlanguagedata.h \ $$PWD/qplaintexteditsearchwidget.h diff --git a/client/qmarkdowntextedit/qmarkdowntextedit-lib.pro b/client/qmarkdowntextedit/qmarkdowntextedit-lib.pro new file mode 100644 index 0000000..b37bcda --- /dev/null +++ b/client/qmarkdowntextedit/qmarkdowntextedit-lib.pro @@ -0,0 +1,34 @@ +TARGET = QMarkdownTextedit +TEMPLATE = lib +QT += core gui widgets +CONFIG += c++11 create_prl no_install_prl create_pc + +include(qmarkdowntextedit.pri) + +TRANSLATIONS += trans/qmarkdowntextedit_de.ts \ + trans/qmarkdowntextedit_zh_CN.ts \ + trans/qmarkdowntextedit_es.ts + +isEmpty(PREFIX):PREFIX=$$[QT_INSTALL_PREFIX] +isEmpty(LIBDIR):LIBDIR=$$[QT_INSTALL_LIBS] +isEmpty(HEADERDIR):HEADERDIR=$${PREFIX}/include/$$TARGET/ +isEmpty(DSRDIR):DSRDIR=$${PREFIX}/share/$$TARGET + +target.path = $${LIBDIR} + +headers.files = $$HEADERS +headers.path = $${HEADERDIR} + +license.files = LICENSE +license.path = $${DSRDIR}/licenses/ + +trans.files = trans/*.qm +trans.path = $${DSRDIR}/translations/ + +QMAKE_PKGCONFIG_NAME = QMarkdownTextedit +QMAKE_PKGCONFIG_DESCRIPTION = C++ Qt QPlainTextEdit widget with markdown highlighting and some other goodies +QMAKE_PKGCONFIG_INCDIR = $${headers.path} +QMAKE_PKGCONFIG_LIBDIR = $${LIBDIR} +QMAKE_PKGCONFIG_DESTDIR = pkgconfig + +INSTALLS += target license headers trans diff --git a/client/qmarkdowntextedit/qmarkdowntextedit-sources.pri b/client/qmarkdowntextedit/qmarkdowntextedit-sources.pri index 352e0bf..43d5fac 100644 --- a/client/qmarkdowntextedit/qmarkdowntextedit-sources.pri +++ b/client/qmarkdowntextedit/qmarkdowntextedit-sources.pri @@ -6,6 +6,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets SOURCES += \ $$PWD/markdownhighlighter.cpp \ $$PWD/qmarkdowntextedit.cpp \ + $$PWD/qownlanguagedata.cpp \ $$PWD/qplaintexteditsearchwidget.cpp RESOURCES += \ diff --git a/client/qmarkdowntextedit/qmarkdowntextedit.cpp b/client/qmarkdowntextedit/qmarkdowntextedit.cpp index a6e786d..8440fed 100644 --- a/client/qmarkdowntextedit/qmarkdowntextedit.cpp +++ b/client/qmarkdowntextedit/qmarkdowntextedit.cpp @@ -1,68 +1,89 @@ /* - * Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com + * MIT License * - * 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; version 2 of the License. + * Copyright (c) 2014-2023 Patrizio Bekerle -- * - * 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. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ - #include "qmarkdowntextedit.h" -#include -#include + +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include +#include #include #include -#include -#include -#include +#include +#include +#include +#include "linenumberarea.h" +#include "markdownhighlighter.h" + +static const QByteArray _openingCharacters = QByteArrayLiteral("([{<*\"'_~"); +static const QByteArray _closingCharacters = QByteArrayLiteral(")]}>*\"'_~"); QMarkdownTextEdit::QMarkdownTextEdit(QWidget *parent, bool initHighlighter) - : QPlainTextEdit(parent) { + : QPlainTextEdit(parent) { installEventFilter(this); viewport()->installEventFilter(this); - _autoTextOptions = AutoTextOption::None; - _openingCharacters = QStringList() << "(" << "[" << "{" << "<" << "*" - << "\"" << "'" << "_" << "~"; - _closingCharacters = QStringList() << ")" << "]" << "}" << ">" << "*" - << "\"" << "'" << "_" << "~"; + _autoTextOptions = AutoTextOption::BracketClosing; - // markdown highlighting is enabled by default - _highlightingEnabled = true; + _lineNumArea = new LineNumArea(this); + updateLineNumberAreaWidth(0); + + // Markdown highlighting is enabled by default + _highlightingEnabled = initHighlighter; if (initHighlighter) { _highlighter = new MarkdownHighlighter(document()); } -// setHighlightingEnabled(true); QFont font = this->font(); // set the tab stop to the width of 4 spaces in the editor - const int tabStop = 4; + constexpr int tabStop = 4; QFontMetrics metrics(font); + +#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) setTabStopWidth(tabStop * metrics.width(' ')); +#else + setTabStopDistance(tabStop * metrics.horizontalAdvance(QLatin1Char(' '))); +#endif // add shortcuts for duplicating text -// new QShortcut( QKeySequence( "Ctrl+D" ), this, SLOT( duplicateText() ) ); -// new QShortcut( QKeySequence( "Ctrl+Alt+Down" ), this, SLOT( duplicateText() ) ); + // new QShortcut( QKeySequence( "Ctrl+D" ), this, SLOT( duplicateText() ) + // ); new QShortcut( QKeySequence( "Ctrl+Alt+Down" ), this, SLOT( + // duplicateText() ) ); // add a layout to the widget - QVBoxLayout *layout = new QVBoxLayout; + auto *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); - layout->setMargin(0); layout->addStretch(); this->setLayout(layout); @@ -70,56 +91,118 @@ QMarkdownTextEdit::QMarkdownTextEdit(QWidget *parent, bool initHighlighter) _searchWidget = new QPlainTextEditSearchWidget(this); this->layout()->addWidget(_searchWidget); - QObject::connect(this, SIGNAL(textChanged()), - this, SLOT(adjustRightMargin())); + connect(this, &QPlainTextEdit::textChanged, this, + &QMarkdownTextEdit::adjustRightMargin); + connect(this, &QPlainTextEdit::cursorPositionChanged, this, + &QMarkdownTextEdit::centerTheCursor); + connect(verticalScrollBar(), &QScrollBar::valueChanged, this, [this](int) { + _lineNumArea->update(); + }); + connect(this, &QPlainTextEdit::cursorPositionChanged, this, [this]() { + _lineNumArea->update(); + + auto oldArea = blockBoundingGeometry(_textCursor.block()).translated(contentOffset()); + _textCursor = textCursor(); + auto newArea = blockBoundingGeometry(_textCursor.block()).translated(contentOffset()); + auto areaToUpdate = oldArea | newArea; + viewport()->update(areaToUpdate.toRect()); + }); + connect(document(), &QTextDocument::blockCountChanged, + this, &QMarkdownTextEdit::updateLineNumberAreaWidth); + connect(this, &QPlainTextEdit::updateRequest, + this, &QMarkdownTextEdit::updateLineNumberArea); + + updateSettings(); // workaround for disabled signals up initialization - QTimer::singleShot(300, this, SLOT(adjustRightMargin())); + QTimer::singleShot(300, this, &QMarkdownTextEdit::adjustRightMargin); +} + +void QMarkdownTextEdit::setLineNumbersCurrentLineColor(QColor color) { + _lineNumArea->setCurrentLineColor(std::move(color)); +} + +void QMarkdownTextEdit::setLineNumbersOtherLineColor(QColor color) { + _lineNumArea->setOtherLineColor(std::move(color)); +} + +void QMarkdownTextEdit::setSearchWidgetDebounceDelay(uint debounceDelay) +{ + _debounceDelay = debounceDelay; + searchWidget()->setDebounceDelay(_debounceDelay); +} + +void QMarkdownTextEdit::setHighlightCurrentLine(bool set) +{ + _highlightCurrentLine = set; +} + +bool QMarkdownTextEdit::highlightCurrentLine() +{ + return _highlightCurrentLine; +} + +void QMarkdownTextEdit::setCurrentLineHighlightColor(const QColor &color) +{ + _currentLineHighlightColor = color; +} + +QColor QMarkdownTextEdit::currentLineHighlightColor() +{ + return _currentLineHighlightColor; } /** - * Enables or disables the markdown highlighting + * Enables or disables the Markdown highlighting * * @param enabled */ void QMarkdownTextEdit::setHighlightingEnabled(bool enabled) { - if (_highlightingEnabled == enabled) { + if (_highlightingEnabled == enabled || _highlighter == nullptr) { return; } - _highlighter->setDocument(enabled ? document() : Q_NULLPTR); _highlightingEnabled = enabled; + _highlighter->setDocument(enabled ? document() : Q_NULLPTR); if (enabled) { _highlighter->rehighlight(); } } +/** + * @brief Returns if highlighting is enabled + * @return Returns true if highlighting is enabled, otherwise false + */ +bool QMarkdownTextEdit::highlightingEnabled() const { + return _highlightingEnabled && _highlighter != nullptr; +} + /** * Leave a little space on the right side if the document is too long, so * that the search buttons don't get visually blocked by the scroll bar */ void QMarkdownTextEdit::adjustRightMargin() { QMargins margins = layout()->contentsMargins(); - int rightMargin = document()->size().height() > - viewport()->size().height() ? 24 : 0; + const int rightMargin = + document()->size().height() > viewport()->size().height() ? 24 : 0; margins.setRight(rightMargin); layout()->setContentsMargins(margins); } bool QMarkdownTextEdit::eventFilter(QObject *obj, QEvent *event) { - //qDebug() << event->type(); + // qDebug() << event->type(); if (event->type() == QEvent::HoverMove) { - QMouseEvent *mouseEvent = static_cast(event); + auto *mouseEvent = static_cast(event); QWidget *viewPort = this->viewport(); // toggle cursor when control key has been pressed or released - viewPort->setCursor(mouseEvent->modifiers().testFlag( - Qt::ControlModifier) ? - Qt::PointingHandCursor : - Qt::IBeamCursor); + viewPort->setCursor( + mouseEvent->modifiers().testFlag(Qt::ControlModifier) + ? Qt::PointingHandCursor + : Qt::IBeamCursor); } else if (event->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast(event); + auto *keyEvent = static_cast(event); // set cursor to pointing hand if control key was pressed if (keyEvent->modifiers().testFlag(Qt::ControlModifier)) { @@ -136,91 +219,96 @@ bool QMarkdownTextEdit::eventFilter(QObject *obj, QEvent *event) { _searchWidget->deactivate(); return true; } else if ((keyEvent->key() == Qt::Key_Tab) || - (keyEvent->key() == Qt::Key_Backtab)) { + (keyEvent->key() == Qt::Key_Backtab)) { // handle entered tab and reverse tab keys - return handleTabEntered( - keyEvent->key() == Qt::Key_Backtab); + return handleTabEntered(keyEvent->key() == Qt::Key_Backtab); } else if ((keyEvent->key() == Qt::Key_F) && - keyEvent->modifiers().testFlag(Qt::ControlModifier)) { + keyEvent->modifiers().testFlag(Qt::ControlModifier)) { _searchWidget->activate(); return true; } else if ((keyEvent->key() == Qt::Key_R) && - keyEvent->modifiers().testFlag(Qt::ControlModifier)) { + keyEvent->modifiers().testFlag(Qt::ControlModifier)) { _searchWidget->activateReplace(); return true; -// } else if (keyEvent->key() == Qt::Key_Delete) { + // } else if (keyEvent->key() == Qt::Key_Delete) { } else if (keyEvent->key() == Qt::Key_Backspace) { - return handleBracketRemoval(); + return handleBackspaceEntered(); } else if (keyEvent->key() == Qt::Key_Asterisk) { - return handleBracketClosing("*"); + return handleBracketClosing(QLatin1Char('*')); } else if (keyEvent->key() == Qt::Key_QuoteDbl) { - return quotationMarkCheck("\""); + return quotationMarkCheck(QLatin1Char('"')); // apostrophe bracket closing is temporary disabled because // apostrophes are used in different contexts -// } else if (keyEvent->key() == Qt::Key_Apostrophe) { -// return handleBracketClosing("'"); + // } else if (keyEvent->key() == Qt::Key_Apostrophe) { + // return handleBracketClosing("'"); // underline bracket closing is temporary disabled because // underlines are used in different contexts -// } else if (keyEvent->key() == Qt::Key_Underscore) { -// return handleBracketClosing("_"); - } - else if (keyEvent->key() == Qt::Key_QuoteLeft) { - return quotationMarkCheck("`"); + // } else if (keyEvent->key() == Qt::Key_Underscore) { + // return handleBracketClosing("_"); + } else if (keyEvent->key() == Qt::Key_QuoteLeft) { + return quotationMarkCheck(QLatin1Char('`')); } else if (keyEvent->key() == Qt::Key_AsciiTilde) { - return handleBracketClosing("~"); + return handleBracketClosing(QLatin1Char('~')); #ifdef Q_OS_MAC } else if (keyEvent->modifiers().testFlag(Qt::AltModifier) && - keyEvent->key() == Qt::Key_ParenLeft) { + keyEvent->key() == Qt::Key_ParenLeft) { // bracket closing for US keyboard on macOS - return handleBracketClosing("{", "}"); + return handleBracketClosing(QLatin1Char('{'), QLatin1Char('}')); #endif } else if (keyEvent->key() == Qt::Key_ParenLeft) { - return handleBracketClosing("(", ")"); + return handleBracketClosing(QLatin1Char('('), QLatin1Char(')')); } else if (keyEvent->key() == Qt::Key_BraceLeft) { - return handleBracketClosing("{", "}"); + return handleBracketClosing(QLatin1Char('{'), QLatin1Char('}')); } else if (keyEvent->key() == Qt::Key_BracketLeft) { - return handleBracketClosing("[", "]"); + return handleBracketClosing(QLatin1Char('['), QLatin1Char(']')); } else if (keyEvent->key() == Qt::Key_Less) { - return handleBracketClosing("<", ">"); + return handleBracketClosing(QLatin1Char('<'), QLatin1Char('>')); #ifdef Q_OS_MAC } else if (keyEvent->modifiers().testFlag(Qt::AltModifier) && keyEvent->key() == Qt::Key_ParenRight) { // bracket closing for US keyboard on macOS - return bracketClosingCheck("{", "}"); + return bracketClosingCheck(QLatin1Char('{'), QLatin1Char('}')); #endif } else if (keyEvent->key() == Qt::Key_ParenRight) { - return bracketClosingCheck("(", ")"); + return bracketClosingCheck(QLatin1Char('('), QLatin1Char(')')); } else if (keyEvent->key() == Qt::Key_BraceRight) { - return bracketClosingCheck("{", "}"); + return bracketClosingCheck(QLatin1Char('{'), QLatin1Char('}')); } else if (keyEvent->key() == Qt::Key_BracketRight) { - return bracketClosingCheck("[", "]"); - } else if (keyEvent->key() == Qt::Key_Return && - keyEvent->modifiers().testFlag(Qt::ShiftModifier)) { + return bracketClosingCheck(QLatin1Char('['), QLatin1Char(']')); + } else if (keyEvent->key() == Qt::Key_Greater) { + return bracketClosingCheck(QLatin1Char('<'), QLatin1Char('>')); + } else if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && + keyEvent->modifiers().testFlag(Qt::ShiftModifier)) { QTextCursor cursor = this->textCursor(); cursor.insertText(" \n"); return true; - } else if (keyEvent->key() == Qt::Key_Return && + } else if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && keyEvent->modifiers().testFlag(Qt::ControlModifier)) { QTextCursor cursor = this->textCursor(); - cursor.movePosition(QTextCursor::EndOfLine); - cursor.insertText("\n"); + cursor.movePosition(QTextCursor::EndOfBlock); + cursor.insertText(QStringLiteral("\n")); setTextCursor(cursor); return true; - } else if (keyEvent == QKeySequence::Copy || keyEvent == QKeySequence::Cut) { + } else if (keyEvent == QKeySequence::Copy || + keyEvent == QKeySequence::Cut) { QTextCursor cursor = this->textCursor(); if (!cursor.hasSelection()) { QString text; - if (cursor.block().length() <= 1) // no content + if (cursor.block().length() <= 1) // no content text = "\n"; else { - //cursor.select(QTextCursor::BlockUnderCursor); // negative, it will include the previous paragraph separator + // cursor.select(QTextCursor::BlockUnderCursor); // + // negative, it will include the previous paragraph + // separator cursor.movePosition(QTextCursor::StartOfBlock); - cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::EndOfBlock, + QTextCursor::KeepAnchor); text = cursor.selectedText(); if (!cursor.atEnd()) { text += "\n"; // this is the paragraph separator - cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 1); + cursor.movePosition(QTextCursor::NextCharacter, + QTextCursor::KeepAnchor, 1); } } if (keyEvent == QKeySequence::Cut) { @@ -228,52 +316,49 @@ bool QMarkdownTextEdit::eventFilter(QObject *obj, QEvent *event) { cursor.deletePreviousChar(); else cursor.removeSelectedText(); - cursor.movePosition(QTextCursor::StartOfLine); + cursor.movePosition(QTextCursor::StartOfBlock); setTextCursor(cursor); } qApp->clipboard()->setText(text); return true; } - } - else if (keyEvent == QKeySequence::Paste) { - if (qApp->clipboard()->ownsClipboard() && - QRegExp("[^\n]*\n$").exactMatch(qApp->clipboard()->text())) { - QTextCursor cursor = this->textCursor(); - if (!cursor.hasSelection()) { - cursor.movePosition(QTextCursor::StartOfLine); - setTextCursor(cursor); - } - } } else if ((keyEvent->key() == Qt::Key_Down) && - keyEvent->modifiers().testFlag(Qt::ControlModifier) && - keyEvent->modifiers().testFlag(Qt::AltModifier)) { + keyEvent->modifiers().testFlag(Qt::ControlModifier) && + keyEvent->modifiers().testFlag(Qt::AltModifier)) { // duplicate text with `Ctrl + Alt + Down` duplicateText(); return true; #ifndef Q_OS_MAC } else if ((keyEvent->key() == Qt::Key_Down) && - keyEvent->modifiers().testFlag(Qt::ControlModifier)) { + keyEvent->modifiers().testFlag(Qt::ControlModifier) && + !keyEvent->modifiers().testFlag(Qt::ShiftModifier)) { // scroll the page down auto *scrollBar = verticalScrollBar(); scrollBar->setSliderPosition(scrollBar->sliderPosition() + 1); return true; } else if ((keyEvent->key() == Qt::Key_Up) && - keyEvent->modifiers().testFlag(Qt::ControlModifier)) { + keyEvent->modifiers().testFlag(Qt::ControlModifier) && + !keyEvent->modifiers().testFlag(Qt::ShiftModifier)) { // scroll the page up auto *scrollBar = verticalScrollBar(); scrollBar->setSliderPosition(scrollBar->sliderPosition() - 1); return true; #endif } else if ((keyEvent->key() == Qt::Key_Down) && - keyEvent->modifiers().testFlag(Qt::NoModifier)) { + keyEvent->modifiers().testFlag(Qt::NoModifier)) { // if you are in the last line and press cursor down the cursor will // jump to the end of the line QTextCursor cursor = textCursor(); if (cursor.position() >= document()->lastBlock().position()) { cursor.movePosition(QTextCursor::EndOfLine); - setTextCursor(cursor); + + // check if we are really in the last line, not only in + // the last block + if (cursor.atBlockEnd()) { + setTextCursor(cursor); + } } - return false; + return QPlainTextEdit::eventFilter(obj, event); } else if ((keyEvent->key() == Qt::Key_Up) && keyEvent->modifiers().testFlag(Qt::NoModifier)) { // if you are in the first line and press cursor up the cursor will @@ -284,29 +369,74 @@ bool QMarkdownTextEdit::eventFilter(QObject *obj, QEvent *event) { if (cursor.position() <= endOfFirstLinePos) { cursor.movePosition(QTextCursor::StartOfLine); - setTextCursor(cursor); + + // check if we are really in the first line, not only in + // the first block + if (cursor.atBlockStart()) { + setTextCursor(cursor); + } } - return false; - } else if (keyEvent->key() == Qt::Key_Return) { + return QPlainTextEdit::eventFilter(obj, event); + } else if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { return handleReturnEntered(); } else if ((keyEvent->key() == Qt::Key_F3)) { _searchWidget->doSearch( - !keyEvent->modifiers().testFlag(Qt::ShiftModifier)); + !keyEvent->modifiers().testFlag(Qt::ShiftModifier)); return true; + } else if ((keyEvent->key() == Qt::Key_Z) && + (keyEvent->modifiers().testFlag(Qt::ControlModifier)) && + !(keyEvent->modifiers().testFlag(Qt::ShiftModifier))) { + undo(); + return true; + } else if ((keyEvent->key() == Qt::Key_Down) && + (keyEvent->modifiers().testFlag(Qt::ControlModifier)) && + (keyEvent->modifiers().testFlag(Qt::ShiftModifier))) { + moveTextUpDown(false); + return true; + } else if ((keyEvent->key() == Qt::Key_Up) && + (keyEvent->modifiers().testFlag(Qt::ControlModifier)) && + (keyEvent->modifiers().testFlag(Qt::ShiftModifier))) { + moveTextUpDown(true); + return true; +#ifdef Q_OS_MAC + // https://github.com/pbek/QOwnNotes/issues/1593 + // https://github.com/pbek/QOwnNotes/issues/2643 + } else if (keyEvent->key() == Qt::Key_Home) { + QTextCursor cursor = textCursor(); + // Meta is Control on macOS + cursor.movePosition( + keyEvent->modifiers().testFlag(Qt::MetaModifier) ? + QTextCursor::Start : QTextCursor::StartOfLine, + keyEvent->modifiers().testFlag(Qt::ShiftModifier) ? + QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); + this->setTextCursor(cursor); + return true; + } else if (keyEvent->key() == Qt::Key_End) { + QTextCursor cursor = textCursor(); + // Meta is Control on macOS + cursor.movePosition( + keyEvent->modifiers().testFlag(Qt::MetaModifier) ? + QTextCursor::End : QTextCursor::EndOfLine, + keyEvent->modifiers().testFlag(Qt::ShiftModifier) ? + QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); + this->setTextCursor(cursor); + return true; +#endif } - return false; + return QPlainTextEdit::eventFilter(obj, event); } else if (event->type() == QEvent::KeyRelease) { - QKeyEvent *keyEvent = static_cast(event); + auto *keyEvent = static_cast(event); // reset cursor if control key was released if (keyEvent->key() == Qt::Key_Control) { resetMouseCursor(); } - return false; + return QPlainTextEdit::eventFilter(obj, event); } else if (event->type() == QEvent::MouseButtonRelease) { - QMouseEvent *mouseEvent = static_cast(event); + _mouseButtonDown = false; + auto *mouseEvent = static_cast(event); // track `Ctrl + Click` in the text edit if ((obj == this->viewport()) && @@ -315,6 +445,23 @@ bool QMarkdownTextEdit::eventFilter(QObject *obj, QEvent *event) { // open the link (if any) at the current position // in the noteTextEdit openLinkAtCursorPosition(); + return true; + } + } else if (event->type() == QEvent::MouseButtonPress) { + _mouseButtonDown = true; + } else if (event->type() == QEvent::MouseButtonDblClick) { + _mouseButtonDown = true; + } else if (event->type() == QEvent::Wheel) { + auto *wheel = dynamic_cast(event); + + // emit zoom signals + if (wheel->modifiers() == Qt::ControlModifier) { + if (wheel->angleDelta().y() > 0) { + Q_EMIT zoomIn(); + } else { + Q_EMIT zoomOut(); + } + return true; } } @@ -322,6 +469,179 @@ bool QMarkdownTextEdit::eventFilter(QObject *obj, QEvent *event) { return QPlainTextEdit::eventFilter(obj, event); } +void QMarkdownTextEdit::centerTheCursor() { + if (_mouseButtonDown || !_centerCursor) { + return; + } + + // centers the cursor every time, but not on the top and bottom + // bottom is done by setCenterOnScroll() in updateSettings() + centerCursor(); + + /* + QRect cursor = cursorRect(); + QRect vp = viewport()->rect(); + + qDebug() << __func__ << " - 'cursor.top': " << cursor.top(); + qDebug() << __func__ << " - 'cursor.bottom': " << cursor.bottom(); + qDebug() << __func__ << " - 'vp': " << vp.bottom(); + + int bottom = 0; + int top = 0; + + qDebug() << __func__ << " - 'viewportMargins().top()': " + << viewportMargins().top(); + + qDebug() << __func__ << " - 'viewportMargins().bottom()': " + << viewportMargins().bottom(); + + int vpBottom = viewportMargins().top() + viewportMargins().bottom() + + vp.bottom(); int vpCenter = vpBottom / 2; int cBottom = cursor.bottom() + + viewportMargins().top(); + + qDebug() << __func__ << " - 'vpBottom': " << vpBottom; + qDebug() << __func__ << " - 'vpCenter': " << vpCenter; + qDebug() << __func__ << " - 'cBottom': " << cBottom; + + + if (cBottom >= vpCenter) { + bottom = cBottom + viewportMargins().top() / 2 + + viewportMargins().bottom() / 2 - (vp.bottom() / 2); + // bottom = cBottom - (vp.bottom() / 2); + // bottom *= 1.5; + } + + // setStyleSheet(QString("QPlainTextEdit {padding-bottom: + %1px;}").arg(QString::number(bottom))); + + // if (cursor.top() < (vp.bottom() / 2)) { + // top = (vp.bottom() / 2) - cursor.top() + viewportMargins().top() / + 2 + viewportMargins().bottom() / 2; + //// top *= -1; + //// bottom *= 1.5; + // } + qDebug() << __func__ << " - 'top': " << top; + qDebug() << __func__ << " - 'bottom': " << bottom; + setViewportMargins(0,top,0, bottom); + + + // QScrollBar* scrollbar = verticalScrollBar(); + // + // qDebug() << __func__ << " - 'scrollbar->value();': " << + scrollbar->value();; + // qDebug() << __func__ << " - 'scrollbar->maximum();': " + // << scrollbar->maximum();; + + + // scrollbar->setValue(scrollbar->value() - offset.y()); + // + // setViewportMargins + + // setViewportMargins(0, 0, 0, bottom); + */ +} + +/* + * Handle the undo event ourselves + * Retains the selected text as selected after undo if + * bracket closing was used otherwise performs normal undo + */ +void QMarkdownTextEdit::undo() { + QTextCursor cursor = textCursor(); + // if no text selected, call undo + if (!cursor.hasSelection()) { + QPlainTextEdit::undo(); + return; + } + + // if text is selected and bracket closing was used + // we retain our selection + if (_handleBracketClosingUsed) { + // get the selection + int selectionEnd = cursor.selectionEnd(); + int selectionStart = cursor.selectionStart(); + // call undo + QPlainTextEdit::undo(); + // select again + cursor.setPosition(selectionStart - 1); + cursor.setPosition(selectionEnd - 1, QTextCursor::KeepAnchor); + this->setTextCursor(cursor); + _handleBracketClosingUsed = false; + } else { + // if text was selected but bracket closing wasn't used + // do normal undo + QPlainTextEdit::undo(); + return; + } +} + +void QMarkdownTextEdit::moveTextUpDown(bool up) { + QTextCursor cursor = textCursor(); + QTextCursor move = cursor; + + move.setVisualNavigation(false); + + move.beginEditBlock(); // open an edit block to keep undo operations sane + bool hasSelection = cursor.hasSelection(); + + if (hasSelection) { + // if there's a selection inside the block, we select the whole block + move.setPosition(cursor.selectionStart()); + move.movePosition(QTextCursor::StartOfBlock); + move.setPosition(cursor.selectionEnd(), QTextCursor::KeepAnchor); + move.movePosition( + move.atBlockStart() ? QTextCursor::Left : QTextCursor::EndOfBlock, + QTextCursor::KeepAnchor); + } else { + move.movePosition(QTextCursor::StartOfBlock); + move.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + } + + // get the text of the current block + QString text = move.selectedText(); + + move.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + move.removeSelectedText(); + + if (up) { // up key + move.movePosition(QTextCursor::PreviousBlock); + move.insertBlock(); + move.movePosition(QTextCursor::Left); + } else { // down key + move.movePosition(QTextCursor::EndOfBlock); + if (move.atBlockStart()) { // empty block + move.movePosition(QTextCursor::NextBlock); + move.insertBlock(); + move.movePosition(QTextCursor::Left); + } else { + move.insertBlock(); + } + } + + int start = move.position(); + move.clearSelection(); + move.insertText(text); + int end = move.position(); + + // reselect + if (hasSelection) { + move.setPosition(end); + move.setPosition(start, QTextCursor::KeepAnchor); + } else { + move.setPosition(start); + } + + move.endEditBlock(); + + setTextCursor(move); +} + +void QMarkdownTextEdit::setLineNumberEnabled(bool enabled) +{ + _lineNumArea->setLineNumAreaEnabled(enabled); + updateLineNumberAreaWidth(0); +} + /** * Resets the cursor to Qt::IBeamCursor */ @@ -345,98 +665,111 @@ void QMarkdownTextEdit::focusOutEvent(QFocusEvent *event) { * @param closingCharacter * @return */ -bool QMarkdownTextEdit::handleBracketClosing(QString openingCharacter, - QString closingCharacter) { - // check if bracket closing is enabled - if (!(_autoTextOptions & AutoTextOption::BracketClosing)) { +bool QMarkdownTextEdit::handleBracketClosing(const QChar openingCharacter, + QChar closingCharacter) { + // check if bracket closing or read-only are enabled + if (!(_autoTextOptions & AutoTextOption::BracketClosing) || isReadOnly()) { return false; } QTextCursor cursor = textCursor(); - // get the current text from the block (inserted character not included) - QString text = cursor.block().text(); - - if (closingCharacter.isEmpty()) { + if (closingCharacter.isNull()) { closingCharacter = openingCharacter; } - QString selectedText = cursor.selectedText(); + const QString selectedText = cursor.selectedText(); // When user currently has text selected, we prepend the openingCharacter // and append the closingCharacter. E.g. 'text' -> '(text)'. We keep the // current selectedText selected. - // - // TODO(sanderboom): how to make ctrl-z keep the selectedText selected? - if (selectedText != "") { + if (!selectedText.isEmpty()) { // Insert. The selectedText is overwritten. - cursor.insertText(openingCharacter); - cursor.insertText(selectedText); - cursor.insertText(closingCharacter); + const QString newText = + openingCharacter + selectedText + closingCharacter; + cursor.insertText(newText); // Re-select the selectedText. - int selectionEnd = cursor.position() - 1; - int selectionStart = selectionEnd - selectedText.length(); + const int selectionEnd = cursor.position() - 1; + const int selectionStart = selectionEnd - selectedText.length(); + cursor.setPosition(selectionStart); cursor.setPosition(selectionEnd, QTextCursor::KeepAnchor); this->setTextCursor(cursor); - + _handleBracketClosingUsed = true; return true; - } else { - // if not text was selected check if we are inside the text - int positionInBlock = cursor.position() - cursor.block().position(); - - // only allow the closing if the cursor was at the end of a block - // we are making a special allowance for openingCharacter == * - if ((positionInBlock != text.count()) && - !((openingCharacter == "*") && - (positionInBlock == (text.count() - 1)))) { - return false; - } } - + // get the current text from the block (inserted character not included) // Remove whitespace at start of string (e.g. in multilevel-lists). - text = text.remove(QRegExp("^\\s+")); + const QString text = cursor.block().text().remove(QRegularExpression("^\\s+")); + + const int pib = cursor.positionInBlock(); + bool isPreviousAsterisk = pib > 0 && pib < text.length() && text.at(pib - 1) == '*'; + bool isNextAsterisk = pib < text.length() && text.at(pib) == '*'; + bool isMaybeBold = isPreviousAsterisk && isNextAsterisk; + if (pib < text.length() && !isMaybeBold && !text.at(pib).isSpace()) { + return false; + } // Default positions to move the cursor back. int cursorSubtract = 1; - // Special handling for `*` opening character, as this could be: // - start of a list (or sublist); // - start of a bold text; - if (openingCharacter == "*") { - // User wants: '*'. + if (openingCharacter == QLatin1Char('*')) { + // don't auto complete in code block + bool isInCode = + MarkdownHighlighter::isCodeBlock(cursor.block().userState()); + // we only do auto completion if there is a space before the cursor pos + bool hasSpaceOrAsteriskBefore = !text.isEmpty() && pib > 0 && + (text.at(pib - 1).isSpace() || + text.at(pib - 1) == QLatin1Char('*')); // This could be the start of a list, don't autocomplete. - if (text == "") { + bool isEmpty = text.isEmpty(); + + if (isInCode || !hasSpaceOrAsteriskBefore || isEmpty) { return false; } + + // bold + if (isPreviousAsterisk && isNextAsterisk) { + cursorSubtract = 1; + } + // User wants: '**'. // Not the start of a list, probably bold text. We autocomplete with // extra closingCharacter and cursorSubtract to 'catchup'. - else if (text == "*") { - closingCharacter = "**"; + if (text == QLatin1String("*")) { + cursor.insertText(QStringLiteral("*")); cursorSubtract = 2; } - // User wants: '* *'. - // We are in a list already, proceed as normal (autocomplete). - else if (text == "* ") { - // no-op. - } } // Auto completion for ``` pair - if (openingCharacter == "`") { - if (QRegExp("[^`]*``").exactMatch(text)) { - cursor.insertText(openingCharacter); - cursor.insertText(openingCharacter); + if (openingCharacter == QLatin1Char('`')) { +#if QT_VERSION < QT_VERSION_CHECK(5, 12, 0) + if (QRegExp(QStringLiteral("[^`]*``")).exactMatch(text)) { +#else + if (QRegularExpression(QRegularExpression::anchoredPattern(QStringLiteral("[^`]*``"))).match(text).hasMatch()) { +#endif + cursor.insertText(QStringLiteral("``")); cursorSubtract = 3; } } + // don't auto complete in code block + if (openingCharacter == QLatin1Char('<') && + MarkdownHighlighter::isCodeBlock(cursor.block().userState())) { + return false; + } + + cursor.beginEditBlock(); cursor.insertText(openingCharacter); cursor.insertText(closingCharacter); cursor.setPosition(cursor.position() - cursorSubtract); + cursor.endEditBlock(); + setTextCursor(cursor); return true; } @@ -448,47 +781,46 @@ bool QMarkdownTextEdit::handleBracketClosing(QString openingCharacter, * @param closingCharacter * @return */ -bool QMarkdownTextEdit::bracketClosingCheck(QString openingCharacter, - QString closingCharacter) { - // check if bracket closing is enabled - if (!(_autoTextOptions & AutoTextOption::BracketClosing)) { +bool QMarkdownTextEdit::bracketClosingCheck(const QChar openingCharacter, + QChar closingCharacter) { + // check if bracket closing or read-only are enabled + if (!(_autoTextOptions & AutoTextOption::BracketClosing) || isReadOnly()) { return false; } - if (closingCharacter.isEmpty()) { + if (closingCharacter.isNull()) { closingCharacter = openingCharacter; } QTextCursor cursor = textCursor(); - int positionInBlock = cursor.position() - cursor.block().position(); + const int positionInBlock = cursor.positionInBlock(); // get the current text from the block - QString text = cursor.block().text(); - int textLength = text.length(); + const QString text = cursor.block().text(); + const int textLength = text.length(); // if we are at the end of the line we just want to enter the character if (positionInBlock >= textLength) { return false; } - QString currentChar = text.at(positionInBlock); + const QChar currentChar = text.at(positionInBlock); - if (closingCharacter == openingCharacter) { + // if (closingCharacter == openingCharacter) { - } + // } qDebug() << __func__ << " - 'currentChar': " << currentChar; - // if the current character is not the closing character we just want to // enter the character if (currentChar != closingCharacter) { return false; } - QString leftText = text.left(positionInBlock); - int openingCharacterCount = leftText.count(openingCharacter); - int closingCharacterCount = leftText.count(closingCharacter); + const QString leftText = text.left(positionInBlock); + const int openingCharacterCount = leftText.count(openingCharacter); + const int closingCharacterCount = leftText.count(closingCharacter); // if there were enough opening characters just enter the character if (openingCharacterCount < (closingCharacterCount + 1)) { @@ -508,25 +840,32 @@ bool QMarkdownTextEdit::bracketClosingCheck(QString openingCharacter, * @param quotationCharacter * @return */ -bool QMarkdownTextEdit::quotationMarkCheck(QString quotationCharacter) { - // check if bracket closing is enabled - if (!(_autoTextOptions & AutoTextOption::BracketClosing)) { +bool QMarkdownTextEdit::quotationMarkCheck(const QChar quotationCharacter) { + // check if bracket closing or read-only are enabled + if (!(_autoTextOptions & AutoTextOption::BracketClosing) || isReadOnly()) { return false; } QTextCursor cursor = textCursor(); - int positionInBlock = cursor.position() - cursor.block().position(); + const int positionInBlock = cursor.positionInBlock(); // get the current text from the block - QString text = cursor.block().text(); - int textLength = text.length(); + const QString text = cursor.block().text(); + const int textLength = text.length(); + + // if last char is not space, we are at word end, no autocompletion + const bool isBacktick = quotationCharacter == '`'; + if (!isBacktick && positionInBlock != 0 && + !text.at(positionInBlock - 1).isSpace()) { + return false; + } // if we are at the end of the line we just want to enter the character if (positionInBlock >= textLength) { return handleBracketClosing(quotationCharacter); } - QString currentChar = text.at(positionInBlock); + const QChar currentChar = text.at(positionInBlock); // if the current character is not the quotation character we just want to // enter the character @@ -540,15 +879,31 @@ bool QMarkdownTextEdit::quotationMarkCheck(QString quotationCharacter) { return true; } +/*********************************** + * helper methods for char removal + * Rules for (') and ("): + * if [sp]" -> opener (sp = space) + * if "[sp] -> closer + ***********************************/ +bool isQuotOpener(int position, const QString &text) { + if (position == 0) return true; + const int prevCharPos = position - 1; + return text.at(prevCharPos).isSpace(); +} +bool isQuotCloser(int position, const QString &text) { + const int nextCharPos = position + 1; + if (nextCharPos >= text.length()) return true; + return text.at(nextCharPos).isSpace(); +} + /** - * Handles removing of matching brackets and other markdown characters + * Handles removing of matching brackets and other Markdown characters * Only works with backspace to remove text * * @return */ -bool QMarkdownTextEdit::handleBracketRemoval() { - // check if bracket removal is enabled - if (!(_autoTextOptions & AutoTextOption::BracketRemoval)) { +bool QMarkdownTextEdit::handleBackspaceEntered() { + if (!(_autoTextOptions & AutoTextOption::BracketRemoval) || isReadOnly()) { return false; } @@ -560,7 +915,12 @@ bool QMarkdownTextEdit::handleBracketRemoval() { } int position = cursor.position(); - int positionInBlock = position - cursor.block().position(); + const int positionInBlock = cursor.positionInBlock(); + int block = cursor.block().blockNumber(); + + if (_highlighter) + if (_highlighter->isPosInACodeSpan(block, positionInBlock - 1)) + return false; // return if backspace was pressed at the beginning of a block if (positionInBlock == 0) { @@ -568,32 +928,77 @@ bool QMarkdownTextEdit::handleBracketRemoval() { } // get the current text from the block - QString text = cursor.block().text(); - QString charInFront = text.at(positionInBlock - 1); - int openingCharacterIndex = _openingCharacters.indexOf(charInFront); + const QString text = cursor.block().text(); - // return if the character in front of the cursor is no opening character - if (openingCharacterIndex == -1) { - return false; + char charToRemove{}; + + // current char + const char charInFront = text.at(positionInBlock - 1).toLatin1(); + + if (charInFront == '*') + return handleCharRemoval(MarkdownHighlighter::RangeType::Emphasis, + block, positionInBlock - 1); + else if (charInFront == '`') + return handleCharRemoval(MarkdownHighlighter::RangeType::CodeSpan, + block, positionInBlock - 1); + + //handle removal of ", ', and brackets + + // is it opener? + int pos = _openingCharacters.indexOf(charInFront); + // for " and ' + bool isOpener = false; + bool isCloser = false; + if (pos == 5 || pos == 6) { + isOpener = isQuotOpener(positionInBlock - 1, text); + } else { + isOpener = pos != -1; + } + if (isOpener) { + charToRemove = _closingCharacters.at(pos); + } else { + // is it closer? + pos = _closingCharacters.indexOf(charInFront); + if (pos == 5 || pos == 6) + isCloser = isQuotCloser(positionInBlock - 1, text); + else + isCloser = pos != -1; + if (isCloser) + charToRemove = _openingCharacters.at(pos); + else + return false; } - QString closingCharacter = _closingCharacters.at(openingCharacterIndex); - - // remove everything in front of the cursor - text.remove(0, positionInBlock); - int closingCharacterIndex = text.indexOf(closingCharacter); - - // return if no closing character was found in the text after the cursor - if (closingCharacterIndex == -1) { - return false; + int charToRemoveIndex = -1; + if (isOpener) { + bool closer = true; + charToRemoveIndex = text.indexOf(charToRemove, positionInBlock); + if (charToRemoveIndex == -1) return false; + if (pos == 5 || pos == 6) + closer = isQuotCloser(charToRemoveIndex, text); + if (!closer) return false; + cursor.setPosition(position + (charToRemoveIndex - positionInBlock)); + cursor.deleteChar(); + } else if (isCloser) { + charToRemoveIndex = text.lastIndexOf(charToRemove, positionInBlock - 2); + if (charToRemoveIndex == -1) return false; + bool opener = true; + if (pos == 5 || pos == 6) + opener = isQuotOpener(charToRemoveIndex, text); + if (!opener) return false; + const int pos = position - (positionInBlock - charToRemoveIndex); + cursor.setPosition(pos); + cursor.deleteChar(); + position -= 1; + } else { + charToRemoveIndex = text.lastIndexOf(charToRemove, positionInBlock - 2); + if (charToRemoveIndex == -1) return false; + const int pos = position - (positionInBlock - charToRemoveIndex); + cursor.setPosition(pos); + cursor.deleteChar(); + position -= 1; } - // removing the closing character - cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, - closingCharacterIndex); - cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - // moving the cursor back to the old position so the previous character // can be removed cursor.setPosition(position); @@ -601,79 +1006,166 @@ bool QMarkdownTextEdit::handleBracketRemoval() { return false; } +bool QMarkdownTextEdit::handleCharRemoval(MarkdownHighlighter::RangeType type, + int block, int position) +{ + if (!_highlighter) + return false; + + auto range = _highlighter->findPositionInRanges(type, block, position); + if (range == QPair{-1, -1}) + return false; + + int charToRemovePos = range.first; + if (position == range.first) + charToRemovePos = range.second; + + QTextCursor cursor = textCursor(); + auto gpos = cursor.position(); + + if (charToRemovePos > position) { + cursor.setPosition(gpos + (charToRemovePos - (position + 1))); + } else { + cursor.setPosition(gpos - (position - charToRemovePos + 1)); + gpos--; + } + + cursor.deleteChar(); + cursor.setPosition(gpos); + setTextCursor(cursor); + return false; +} + +void QMarkdownTextEdit::updateLineNumAreaGeometry() +{ + const auto contentsRect = this->contentsRect(); + const QRect newGeometry = {contentsRect.left(), contentsRect.top(), + _lineNumArea->sizeHint().width(), contentsRect.height()}; + auto oldGeometry = _lineNumArea->geometry(); + if (newGeometry != oldGeometry) { + _lineNumArea->setGeometry(newGeometry); + } +} + +void QMarkdownTextEdit::resizeEvent(QResizeEvent *event) +{ + QPlainTextEdit::resizeEvent(event); + updateLineNumAreaGeometry(); +} + /** * Increases (or decreases) the indention of the selected text * (if there is a text selected) in the noteTextEdit * @return */ -bool QMarkdownTextEdit::increaseSelectedTextIndention(bool reverse) { +bool QMarkdownTextEdit::increaseSelectedTextIndention( + bool reverse, const QString &indentCharacters) { QTextCursor cursor = this->textCursor(); QString selectedText = cursor.selectedText(); - if (selectedText != "") { + if (!selectedText.isEmpty()) { + // Start the selection at start of the first block of the selection + int end = cursor.selectionEnd(); + cursor.setPosition(cursor.selectionStart()); + cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); + cursor.setPosition(end, QTextCursor::KeepAnchor); + this->setTextCursor(cursor); + selectedText = cursor.selectedText(); + // we need this strange newline character we are getting in the // selected text for newlines - QString newLine = QString::fromUtf8(QByteArray::fromHex("e280a9")); + const QString newLine = + QString::fromUtf8(QByteArray::fromHex(QByteArrayLiteral("e280a9"))); QString newText; if (reverse) { // un-indent text - // remove strange newline characters - newText = selectedText.replace( - QRegularExpression(newLine + "[\\t ]"), "\n"); + // QSettings settings; + const int indentSize = indentCharacters == QStringLiteral("\t") + ? 4 + : indentCharacters.length(); - // remove leading \t or space - newText.remove(QRegularExpression("^[\\t ]")); + // remove leading \t or spaces in following lines + newText = selectedText.replace( + QRegularExpression(newLine + QStringLiteral("(\\t| {1,") + + QString::number(indentSize) + + QStringLiteral("})")), + QStringLiteral("\n")); + + // remove leading \t or spaces in first line + newText.remove(QRegularExpression(QStringLiteral("^(\\t| {1,") + + QString::number(indentSize) + + QStringLiteral("})"))); } else { + // replace trailing new line to prevent an indent of the line after + // the selection + newText = selectedText.replace( + QRegularExpression(QRegularExpression::escape(newLine) + + QStringLiteral("$")), + QStringLiteral("\n")); + // indent text - newText = selectedText.replace(newLine, "\n\t").prepend("\t"); + newText.replace(newLine, QStringLiteral("\n") + indentCharacters) + .prepend(indentCharacters); // remove trailing \t - newText.replace(QRegularExpression("\\t$"), ""); + newText.remove(QRegularExpression(QStringLiteral("\\t$"))); } // insert the new text cursor.insertText(newText); // update the selection to the new text - cursor.setPosition(cursor.position() - newText.size(), QTextCursor::KeepAnchor); + cursor.setPosition(cursor.position() - newText.size(), + QTextCursor::KeepAnchor); this->setTextCursor(cursor); return true; } else if (reverse) { - // if nothing was selected but we want to reverse the indention check - // if there is a \t in front or after the cursor and remove it if so - int position = cursor.position(); + const int indentSize = indentCharacters.length(); - if (!cursor.atStart()) { - // get character in front of cursor - cursor.setPosition(position - 1, QTextCursor::KeepAnchor); - } + // do the check as often as we have characters to un-indent + for (int i = 1; i <= indentSize; i++) { + // if nothing was selected but we want to reverse the indention + // check if there is a \t in front or after the cursor and remove it + // if so + const int position = cursor.position(); - // check for \t or space in front of cursor - QRegularExpression re("[\\t ]"); - QRegularExpressionMatch match = re.match(cursor.selectedText()); - - if (!match.hasMatch()) { - // (select to) check for \t or space after the cursor - cursor.setPosition(position); - - if (!cursor.atEnd()) { - cursor.setPosition(position + 1, QTextCursor::KeepAnchor); + if (!cursor.atStart()) { + // get character in front of cursor + cursor.setPosition(position - 1, QTextCursor::KeepAnchor); } - } - match = re.match(cursor.selectedText()); + // check for \t or space in front of cursor + QRegularExpression re(QStringLiteral("[\\t ]")); + QRegularExpressionMatch match = re.match(cursor.selectedText()); - if (match.hasMatch()) { - cursor.removeSelectedText(); + if (!match.hasMatch()) { + // (select to) check for \t or space after the cursor + cursor.setPosition(position); + + if (!cursor.atEnd()) { + cursor.setPosition(position + 1, QTextCursor::KeepAnchor); + } + } + + match = re.match(cursor.selectedText()); + + if (match.hasMatch()) { + cursor.removeSelectedText(); + } + + cursor = this->textCursor(); } return true; } - return false; + // else just insert indentCharacters + cursor.insertText(indentCharacters); + + return true; } /** @@ -681,31 +1173,34 @@ bool QMarkdownTextEdit::increaseSelectedTextIndention(bool reverse) { */ bool QMarkdownTextEdit::openLinkAtCursorPosition() { QTextCursor cursor = this->textCursor(); - int clickedPosition = cursor.position(); + const int clickedPosition = cursor.position(); // select the text in the clicked block and find out on // which position we clicked cursor.movePosition(QTextCursor::StartOfBlock); - int positionFromStart = clickedPosition - cursor.position(); + const int positionFromStart = clickedPosition - cursor.position(); cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); - QString selectedText = cursor.selectedText(); + const QString selectedText = cursor.selectedText(); // find out which url in the selected text was clicked - QString urlString = getMarkdownUrlAtPosition(selectedText, - positionFromStart); - QUrl url = QUrl(urlString); - bool isRelativeFileUrl = urlString.startsWith("file://.."); + const QString urlString = + getMarkdownUrlAtPosition(selectedText, positionFromStart); + const QUrl url = QUrl(urlString); + const bool isRelativeFileUrl = + urlString.startsWith(QLatin1String("file://..")); + const bool isLegacyAttachmentUrl = + urlString.startsWith(QLatin1String("file://attachments")); - qDebug() << __func__ << " - 'emit urlClicked( urlString )': " - << urlString; + qDebug() << __func__ << " - 'emit urlClicked( urlString )': " << urlString; - emit urlClicked(urlString); + Q_EMIT urlClicked(urlString); - if ((url.isValid() && isValidUrl(urlString)) || isRelativeFileUrl) { + if ((url.isValid() && isValidUrl(urlString)) || isRelativeFileUrl || + isLegacyAttachmentUrl) { // ignore some schemata if (!(_ignoredClickUrlSchemata.contains(url.scheme()) || - isRelativeFileUrl)) { + isRelativeFileUrl || isLegacyAttachmentUrl)) { // open the url openUrl(urlString); } @@ -722,9 +1217,9 @@ bool QMarkdownTextEdit::openLinkAtCursorPosition() { * @param urlString * @return */ -bool QMarkdownTextEdit::isValidUrl(QString urlString) { - QRegularExpressionMatch match = - QRegularExpression("^\\w+:\\/\\/.+").match(urlString); +bool QMarkdownTextEdit::isValidUrl(const QString &urlString) { + const QRegularExpressionMatch match = + QRegularExpression(R"(^\w+:\/\/.+)").match(urlString); return match.hasMatch(); } @@ -737,9 +1232,9 @@ bool QMarkdownTextEdit::isValidUrl(QString urlString) { * "/path/to/my/file/QOwnNotes.pdf" if the operating system supports that * handler */ -void QMarkdownTextEdit::openUrl(QString urlString) { - qDebug() << "QMarkdownTextEdit " << __func__ << " - 'urlString': " - << urlString; +void QMarkdownTextEdit::openUrl(const QString &urlString) { + qDebug() << "QMarkdownTextEdit " << __func__ + << " - 'urlString': " << urlString; QDesktopServices::openUrl(QUrl(urlString)); } @@ -748,9 +1243,7 @@ void QMarkdownTextEdit::openUrl(QString urlString) { * @brief Returns the highlighter instance * @return */ -MarkdownHighlighter *QMarkdownTextEdit::highlighter() { - return _highlighter; -} +MarkdownHighlighter *QMarkdownTextEdit::highlighter() { return _highlighter; } /** * @brief Returns the searchWidget instance @@ -765,25 +1258,25 @@ QPlainTextEditSearchWidget *QMarkdownTextEdit::searchWidget() { * @param urlSchemes */ void QMarkdownTextEdit::setIgnoredClickUrlSchemata( - QStringList ignoredUrlSchemata) { - _ignoredClickUrlSchemata = ignoredUrlSchemata; + QStringList ignoredUrlSchemata) { + _ignoredClickUrlSchemata = std::move(ignoredUrlSchemata); } /** - * @brief Returns a map of parsed markdown urls with their link texts as key + * @brief Returns a map of parsed Markdown urls with their link texts as key * * @param text * @return parsed urls */ QMap QMarkdownTextEdit::parseMarkdownUrlsFromText( - QString text) { + const QString &text) { QMap urlMap; QRegularExpression regex; QRegularExpressionMatchIterator iterator; // match urls like this: -// re = QRegularExpression("(<(.+?:\\/\\/.+?)>)"); - regex = QRegularExpression("(<(.+?)>)"); + // re = QRegularExpression("(<(.+?:\\/\\/.+?)>)"); + regex = QRegularExpression(QStringLiteral("(<(.+?)>)")); iterator = regex.globalMatch(text); while (iterator.hasNext()) { QRegularExpressionMatch match = iterator.next(); @@ -793,8 +1286,8 @@ QMap QMarkdownTextEdit::parseMarkdownUrlsFromText( } // match urls like this: [this url](http://mylink) -// QRegularExpression re("(\\[.*?\\]\\((.+?:\\/\\/.+?)\\))"); - regex = QRegularExpression("(\\[.*?\\]\\((.+?)\\))"); + // QRegularExpression re("(\\[.*?\\]\\((.+?:\\/\\/.+?)\\))"); + regex = QRegularExpression(R"((\[.*?\]\((.+?)\)))"); iterator = regex.globalMatch(text); while (iterator.hasNext()) { QRegularExpressionMatch match = iterator.next(); @@ -804,7 +1297,7 @@ QMap QMarkdownTextEdit::parseMarkdownUrlsFromText( } // match urls like this: http://mylink - regex = QRegularExpression("\\b\\w+?:\\/\\/[^\\s]+[^\\s>\\)]"); + regex = QRegularExpression(R"(\b\w+?:\/\/[^\s]+[^\s>\)])"); iterator = regex.globalMatch(text); while (iterator.hasNext()) { QRegularExpressionMatch match = iterator.next(); @@ -812,9 +1305,18 @@ QMap QMarkdownTextEdit::parseMarkdownUrlsFromText( urlMap[url] = url; } + // match urls like this: www.github.com + regex = QRegularExpression(R"(\bwww\.[^\s]+\.[^\s]+\b)"); + iterator = regex.globalMatch(text); + while (iterator.hasNext()) { + QRegularExpressionMatch match = iterator.next(); + QString url = match.captured(0); + urlMap[url] = QStringLiteral("http://") + url; + } + // match reference urls like this: [this url][1] with this later: // [1]: http://domain - regex = QRegularExpression("\\[(.*?)\\]\\s?\\[(.+?)\\]"); + regex = QRegularExpression(R"((\[.*?\]\[(.+?)\]))"); iterator = regex.globalMatch(text); while (iterator.hasNext()) { QRegularExpressionMatch match = iterator.next(); @@ -822,11 +1324,12 @@ QMap QMarkdownTextEdit::parseMarkdownUrlsFromText( QString referenceId = match.captured(2); // search for the referenced url in the whole text edit -// QRegularExpression refRegExp( -// "\\[" + QRegularExpression::escape(referenceId) + -// "\\]: (.+?:\\/\\/.+)"); - QRegularExpression refRegExp( - "\\[" + QRegularExpression::escape(referenceId) + "\\]: (.+?)"); + // QRegularExpression refRegExp( + // "\\[" + QRegularExpression::escape(referenceId) + + // "\\]: (.+?:\\/\\/.+)"); + QRegularExpression refRegExp(QStringLiteral("\\[") + + QRegularExpression::escape(referenceId) + + QStringLiteral("\\]: (.+)")); QRegularExpressionMatch urlMatch = refRegExp.match(toPlainText()); if (urlMatch.hasMatch()) { @@ -839,29 +1342,27 @@ QMap QMarkdownTextEdit::parseMarkdownUrlsFromText( } /** - * @brief Returns the markdown url at position + * @brief Returns the Markdown url at position * @param text * @param position * @return url string */ -QString QMarkdownTextEdit::getMarkdownUrlAtPosition( - QString text, int position) { +QString QMarkdownTextEdit::getMarkdownUrlAtPosition(const QString &text, + int position) { QString url; - // get a map of parsed markdown urls with their link texts as key - QMap urlMap = parseMarkdownUrlsFromText(text); + // get a map of parsed Markdown urls with their link texts as key + const QMap urlMap = parseMarkdownUrlsFromText(text); + QMap::const_iterator i = urlMap.constBegin(); + for (; i != urlMap.constEnd(); ++i) { + const QString &linkText = i.key(); + const QString &urlString = i.value(); - QMapIterator iterator(urlMap); - while (iterator.hasNext()) { - iterator.next(); - QString linkText = iterator.key(); - QString urlString = iterator.value(); - - int foundPositionStart = text.indexOf(linkText); + const int foundPositionStart = text.indexOf(linkText); if (foundPositionStart >= 0) { // calculate end position of found linkText - int foundPositionEnd = foundPositionStart + linkText.size(); + const int foundPositionEnd = foundPositionStart + linkText.size(); // check if position is in found string range if ((position >= foundPositionStart) && @@ -883,14 +1384,14 @@ void QMarkdownTextEdit::duplicateText() { QString selectedText = cursor.selectedText(); // duplicate line if no text was selected - if (selectedText == "") { - int position = cursor.position(); + if (selectedText.isEmpty()) { + const int position = cursor.position(); // select the whole line - cursor.movePosition(QTextCursor::StartOfLine); - cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::StartOfBlock); + cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); - int positionDiff = cursor.position() - position; + const int positionDiff = cursor.position() - position; selectedText = "\n" + cursor.selectedText(); // insert text with new line at end of the selected line @@ -902,11 +1403,11 @@ void QMarkdownTextEdit::duplicateText() { } else { // duplicate selected text cursor.setPosition(cursor.selectionEnd()); - int selectionStart = cursor.position(); + const int selectionStart = cursor.position(); // insert selected text cursor.insertText(selectedText); - int selectionEnd = cursor.position(); + const int selectionEnd = cursor.position(); // select the inserted text cursor.setPosition(selectionStart); @@ -916,21 +1417,20 @@ void QMarkdownTextEdit::duplicateText() { this->setTextCursor(cursor); } -void QMarkdownTextEdit::setText(const QString & text) { - setPlainText(text); -} +void QMarkdownTextEdit::setText(const QString &text) { setPlainText(text); } -void QMarkdownTextEdit::setPlainText(const QString & text) { +void QMarkdownTextEdit::setPlainText(const QString &text) { // clear the dirty blocks vector to increase performance and prevent // a possible crash in QSyntaxHighlighter::rehighlightBlock - _highlighter->clearDirtyBlocks(); + if (_highlighter) + _highlighter->clearDirtyBlocks(); QPlainTextEdit::setPlainText(text); adjustRightMargin(); } /** - * Uses an other widget as parent for the search widget + * Uses another widget as parent for the search widget */ void QMarkdownTextEdit::initSearchFrame(QWidget *searchFrame, bool darkMode) { _searchFrame = searchFrame; @@ -941,8 +1441,8 @@ void QMarkdownTextEdit::initSearchFrame(QWidget *searchFrame, bool darkMode) { QLayout *layout = _searchFrame->layout(); // create a grid layout for the frame and add the search widget to it - if (layout == NULL) { - layout = new QVBoxLayout(); + if (layout == nullptr) { + layout = new QVBoxLayout(_searchFrame); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); } @@ -964,44 +1464,79 @@ void QMarkdownTextEdit::hide() { * Handles an entered return key */ bool QMarkdownTextEdit::handleReturnEntered() { + if (isReadOnly()) { + return true; + } + QTextCursor cursor = this->textCursor(); - int position = cursor.position(); + const int position = cursor.position(); cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); - QString currentLineText = cursor.selectedText(); + const QString currentLineText = cursor.selectedText(); - // if return is pressed and there is just a list symbol then we want to - // remove the list symbol - // Valid listCharacters: '+ ', '-' , '* ', '+ [ ] ', '+ [x] ', '- [ ] ', '- [x] ', '* [ ] ', '* [x] '. - QRegularExpression regex("^(\\s*)([+|\\-|\\*] \\[(x| )\\]|[+\\-\\*])(\\s+)$"); - QRegularExpressionMatchIterator iterator = regex.globalMatch(currentLineText); + // if return is pressed and there is just an unordered list symbol then we + // want to remove the list symbol Valid listCharacters: '+ ', '-' , '* ', '+ + // [ ] ', '+ [x] ', '- [ ] ', '- [-] ', '- [x] ', '* [ ] ', '* [x] '. + QRegularExpression regex(R"(^(\s*)([+|\-|\*] \[(x|-| |)\]|[+\-\*])(\s+)$)"); + QRegularExpressionMatchIterator iterator = + regex.globalMatch(currentLineText); if (iterator.hasNext()) { cursor.removeSelectedText(); return true; } - // Check if we are in a list. + // if return is pressed and there is just an ordered list symbol then we + // want to remove the list symbol + regex = QRegularExpression(R"(^(\s*)(\d+[\.|\)])(\s+)$)"); + iterator = regex.globalMatch(currentLineText); + if (iterator.hasNext()) { + qDebug() << cursor.selectedText(); + cursor.removeSelectedText(); + return true; + } + + // Check if we are in an unordered list. // We are in a list when we have '* ', '- ' or '+ ', possibly with preceding // whitespace. If e.g. user has entered '**text**' and pressed enter - we - // don't want do anymore list-stuff. - QChar char0 = currentLineText.trimmed()[0]; - QChar char1 = currentLineText.trimmed()[1]; - bool inList = ((char0 == '*' || char0 == '-' || char0 == '+') && char1 == ' '); + // don't want to do more list-stuff. + QString currentLine = currentLineText.trimmed(); + QChar char0; + QChar char1; + if (currentLine.length() >= 1) + char0 = currentLine.at(0); + if (currentLine.length() >= 2) + char1 = currentLine.at(1); + const bool inList = + ((char0 == QLatin1Char('*') || char0 == QLatin1Char('-') || + char0 == QLatin1Char('+')) && + char1 == QLatin1Char(' ')); if (inList) { // if the current line starts with a list character (possibly after // whitespaces) add the whitespaces at the next line too - // Valid listCharacters: '+ ', '-' , '* ', '+ [ ] ', '+ [x] ', '- [ ] ', '- [x] ', '* [ ] ', '* [x] '. - regex = QRegularExpression("^(\\s*)([+|\\-|\\*] \\[(x| )\\]|[+\\-\\*])(\\s+)"); + // Valid listCharacters: '+ ', '-' , '* ', '+ [ ] ', '+ [x] ', '- [ ] ', + // '- [x] ', '- [-] ', '* [ ] ', '* [x] '. + regex = + QRegularExpression(R"(^(\s*)([+|\-|\*] \[(x|-| |)\]|[+\-\*])(\s+))"); iterator = regex.globalMatch(currentLineText); if (iterator.hasNext()) { - QRegularExpressionMatch match = iterator.next(); - QString whitespaces = match.captured(1); + const QRegularExpressionMatch match = iterator.next(); + const QString whitespaces = match.captured(1); QString listCharacter = match.captured(2); - QString whitespaceCharacter = match.captured(4); + const QString whitespaceCharacter = match.captured(4); + + // start new checkbox list item with an unchecked checkbox + iterator = QRegularExpression(R"(^([+|\-|\*]) \[(x| |)\])") + .globalMatch(listCharacter); + if (iterator.hasNext()) { + const QRegularExpressionMatch match = iterator.next(); + const QString realListCharacter = match.captured(1); + listCharacter = realListCharacter + QStringLiteral(" [ ]"); + } cursor.setPosition(position); - cursor.insertText("\n" + whitespaces + listCharacter + whitespaceCharacter); + cursor.insertText("\n" + whitespaces + listCharacter + + whitespaceCharacter); // scroll to the cursor if we are at the bottom of the document ensureCursorVisible(); @@ -1009,45 +1544,114 @@ bool QMarkdownTextEdit::handleReturnEntered() { } } + // check for ordered lists and increment the list number in the next line + regex = QRegularExpression(R"(^(\s*)(\d+)([\.|\)])(\s+))"); + iterator = regex.globalMatch(currentLineText); + if (iterator.hasNext()) { + const QRegularExpressionMatch match = iterator.next(); + const QString whitespaces = match.captured(1); + const uint listNumber = match.captured(2).toUInt(); + const QString listMarker = match.captured(3); + const QString whitespaceCharacter = match.captured(4); + + cursor.setPosition(position); + cursor.insertText("\n" + whitespaces + QString::number(listNumber + 1) + + listMarker + whitespaceCharacter); + + // scroll to the cursor if we are at the bottom of the document + ensureCursorVisible(); + return true; + } + + // intent next line with same whitespaces as in current line + regex = QRegularExpression(R"(^(\s+))"); + iterator = regex.globalMatch(currentLineText); + if (iterator.hasNext()) { + const QRegularExpressionMatch match = iterator.next(); + const QString whitespaces = match.captured(1); + + cursor.setPosition(position); + cursor.insertText("\n" + whitespaces); + + // scroll to the cursor if we are at the bottom of the document + ensureCursorVisible(); + return true; + } + return false; } /** * Handles entered tab or reverse tab keys */ -bool QMarkdownTextEdit::handleTabEntered(bool reverse) { +bool QMarkdownTextEdit::handleTabEntered(bool reverse, + const QString &indentCharacters) { + if (isReadOnly()) { + return true; + } + QTextCursor cursor = this->textCursor(); // only check for lists if we haven't a text selected if (cursor.selectedText().isEmpty()) { - cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); - QString currentLineText = cursor.selectedText(); + cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); + const QString currentLineText = cursor.selectedText(); - // check if we want to indent or un-indent a list - // Valid listCharacters: '+ ', '-' , '* ', '+ [ ] ', '+ [x] ', '- [ ] ', '- [x] ', '* [ ] ', '* [x] '. - QRegularExpression re("^(\\s*)([+|\\-|\\*] \\[(x| )\\]|[+\\-\\*])(\\s+)$"); + // check if we want to indent or un-indent an ordered list + // Valid listCharacters: '+ ', '-' , '* ', '+ [ ] ', '+ [x] ', '- [ ] ', + // '- [x] ', '- [-] ', '* [ ] ', '* [x] '. + QRegularExpression re(R"(^(\s*)([+|\-|\*] \[(x|-| )\]|[+\-\*])(\s+)$)"); QRegularExpressionMatchIterator i = re.globalMatch(currentLineText); if (i.hasNext()) { QRegularExpressionMatch match = i.next(); QString whitespaces = match.captured(1); - QString listCharacter = match.captured(2); - QString whitespaceCharacter = match.captured(4); + const QString listCharacter = match.captured(2); + const QString whitespaceCharacter = match.captured(4); + + // add or remove one tabulator key + if (reverse) { + // remove one set of indentCharacters or a tabulator + whitespaces.remove(QRegularExpression( + QStringLiteral("^(\\t|") + + QRegularExpression::escape(indentCharacters) + + QStringLiteral(")"))); + + } else { + whitespaces += indentCharacters; + } + + cursor.insertText(whitespaces + listCharacter + + whitespaceCharacter); + return true; + } + + // check if we want to indent or un-indent an ordered list + re = QRegularExpression(R"(^(\s*)(\d+)([\.|\)])(\s+)$)"); + i = re.globalMatch(currentLineText); + + if (i.hasNext()) { + const QRegularExpressionMatch match = i.next(); + QString whitespaces = match.captured(1); + const QString listCharacter = match.captured(2); + const QString listMarker = match.captured(3); + const QString whitespaceCharacter = match.captured(4); // add or remove one tabulator key if (reverse) { whitespaces.chop(1); } else { - whitespaces += "\t"; + whitespaces += indentCharacters; } - cursor.insertText(whitespaces + listCharacter + whitespaceCharacter); + cursor.insertText(whitespaces + listCharacter + listMarker + + whitespaceCharacter); return true; } } - // check if we want to intent the whole text - return increaseSelectedTextIndention(reverse); + // check if we want to indent the whole text + return increaseSelectedTextIndention(reverse, indentCharacters); } /** @@ -1057,30 +1661,178 @@ void QMarkdownTextEdit::setAutoTextOptions(AutoTextOptions options) { _autoTextOptions = options; } +void QMarkdownTextEdit::updateLineNumberArea(const QRect rect, int dy) +{ + if (dy) + _lineNumArea->scroll(0, dy); + else + _lineNumArea->update(0, rect.y(), _lineNumArea->sizeHint().width(), rect.height()); + + updateLineNumAreaGeometry(); + + if (rect.contains(viewport()->rect())) { + updateLineNumberAreaWidth(0); + } +} + +void QMarkdownTextEdit::updateLineNumberAreaWidth(int) +{ + QSignalBlocker blocker(this); + const auto oldMargins = viewportMargins(); + const int width = _lineNumArea->isLineNumAreaEnabled() ? + _lineNumArea->sizeHint().width() + _lineNumberLeftMarginOffset : + oldMargins.left(); + const auto newMargins = QMargins{width, oldMargins.top(), oldMargins.right(), oldMargins.bottom()}; + + if (newMargins != oldMargins) { + setViewportMargins(newMargins); + } +} + /** - * Overrides QPlainTextEdit::paintEvent to fix the RTL bug of QPlainTextEdit - * * @param e + * @details This does two things + * 1. Overrides QPlainTextEdit::paintEvent to fix the RTL bug of QPlainTextEdit + * 2. Paints a rectangle around code block fences [Code taken from + * ghostwriter(which in turn is based on QPlaintextEdit::paintEvent() with + * modifications and minor improvements for our use */ void QMarkdownTextEdit::paintEvent(QPaintEvent *e) { QTextBlock block = firstVisibleBlock(); - while (block.isValid()) { - QTextLayout *layout = block.layout(); + QPainter painter(viewport()); + const QRect viewportRect = viewport()->rect(); + // painter.fillRect(viewportRect, Qt::transparent); + bool firstVisible = true; + QPointF offset(contentOffset()); + QRectF blockAreaRect; // Code or block quote rect. + bool inBlockArea = false; + + bool clipTop = false; + bool drawBlock = false; + qreal dy = 0.0; + bool done = false; + + const QColor &color = MarkdownHighlighter::codeBlockBackgroundColor(); + const int cornerRadius = 5; + + while (block.isValid() && !done) { + const QRectF r = blockBoundingRect(block).translated(offset); + const int state = block.userState(); + + if (!inBlockArea && MarkdownHighlighter::isCodeBlock(state)) { + // skip the backticks + if (!block.text().startsWith(QLatin1String("```")) && + !block.text().startsWith(QLatin1String("~~~"))) { + blockAreaRect = r; + dy = 0.0; + inBlockArea = true; + } + + // If this is the first visible block within the viewport + // and if the previous block is part of the text block area, + // then the rectangle to draw for the block area will have + // its top clipped by the viewport and will need to be + // drawn specially. + const int prevBlockState = block.previous().userState(); + if (firstVisible && + MarkdownHighlighter::isCodeBlock(prevBlockState)) { + clipTop = true; + } + } + // Else if the block ends a text block area... + else if (inBlockArea && MarkdownHighlighter::isCodeBlockEnd(state)) { + drawBlock = true; + inBlockArea = false; + blockAreaRect.setHeight(dy); + } + // If the block is at the end of the document and ends a text + // block area... + // + if (inBlockArea && block == this->document()->lastBlock()) { + drawBlock = true; + inBlockArea = false; + dy += r.height(); + blockAreaRect.setHeight(dy); + } + offset.ry() += r.height(); + dy += r.height(); + + // If this is the last text block visible within the viewport... + if (offset.y() > viewportRect.height()) { + if (inBlockArea) { + blockAreaRect.setHeight(dy); + drawBlock = true; + } + + // Finished drawing. + done = true; + } + // If this is the last text block visible within the viewport... + if (offset.y() > viewportRect.height()) { + if (inBlockArea) { + blockAreaRect.setHeight(dy); + drawBlock = true; + } + // Finished drawing. + done = true; + } + + if (drawBlock) { + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.setPen(Qt::NoPen); + painter.setBrush(QBrush(color)); + + // If the first visible block is "clipped" such that the previous + // block is part of the text block area, then only draw a rectangle + // with the bottom corners rounded, and with the top corners square + // to reflect that the first visible block is part of a larger block + // of text. + // + if (clipTop) { + QPainterPath path; + path.setFillRule(Qt::WindingFill); + path.addRoundedRect(blockAreaRect, cornerRadius, cornerRadius); + qreal adjustedHeight = blockAreaRect.height() / 2; + path.addRect(blockAreaRect.adjusted(0, 0, 0, -adjustedHeight)); + painter.drawPath(path.simplified()); + clipTop = false; + } + // Else draw the entire rectangle with all corners rounded. + else { + painter.drawRoundedRect(blockAreaRect, cornerRadius, + cornerRadius); + } + + drawBlock = false; + } // this fixes the RTL bug of QPlainTextEdit // https://bugreports.qt.io/browse/QTBUG-7516 - if (block.text().isRightToLeft()) - { - QTextOption opt = document()->defaultTextOption(); - opt = QTextOption(Qt::AlignRight); + if (block.text().isRightToLeft()) { + QTextLayout *layout = block.layout(); + // opt = document()->defaultTextOption(); + QTextOption opt = QTextOption(Qt::AlignRight); opt.setTextDirection(Qt::RightToLeft); layout->setTextOption(opt); } + // Current line highlight + QTextCursor cursor = textCursor(); + if (highlightCurrentLine() && cursor.block() == block) { + QTextLine line = block.layout()->lineForTextPosition(cursor.positionInBlock()); + QRectF lineRect = line.rect(); + lineRect.moveTop(lineRect.top() + r.top()); + lineRect.setLeft(0.); + lineRect.setRight(viewportRect.width()); + painter.fillRect(lineRect.toAlignedRect(), currentLineHighlightColor()); + } + block = block.next(); + firstVisible = false; } + painter.end(); QPlainTextEdit::paintEvent(e); } @@ -1097,3 +1849,33 @@ void QMarkdownTextEdit::setReadOnly(bool ro) { // @see https://github.com/pbek/QOwnNotes/issues/976 setAttribute(Qt::WA_InputMethodEnabled, !isReadOnly()); } + +void QMarkdownTextEdit::doSearch( + QString &searchText, QPlainTextEditSearchWidget::SearchMode searchMode) { + _searchWidget->setSearchText(searchText); + _searchWidget->setSearchMode(searchMode); + _searchWidget->doSearchCount(); + _searchWidget->activate(false); +} + +void QMarkdownTextEdit::hideSearchWidget(bool reset) { + _searchWidget->deactivate(); + + if (reset) { + _searchWidget->reset(); + } +} + +void QMarkdownTextEdit::updateSettings() { + // if true: centers the screen if cursor reaches bottom (but not top) + searchWidget()->setDebounceDelay(_debounceDelay); + setCenterOnScroll(_centerCursor); +} + +void QMarkdownTextEdit::setLineNumberLeftMarginOffset(int offset) { + _lineNumberLeftMarginOffset = offset; +} + +QMargins QMarkdownTextEdit::viewportMargins() { + return QPlainTextEdit::viewportMargins(); +} diff --git a/client/qmarkdowntextedit/qmarkdowntextedit.h b/client/qmarkdowntextedit/qmarkdowntextedit.h index 5463f70..af637ca 100644 --- a/client/qmarkdowntextedit/qmarkdowntextedit.h +++ b/client/qmarkdowntextedit/qmarkdowntextedit.h @@ -1,87 +1,146 @@ /* - * Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com + * MIT License * - * 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; version 2 of the License. + * Copyright (c) 2014-2023 Patrizio Bekerle -- * - * 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. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ #pragma once -#include #include +#include + #include "markdownhighlighter.h" #include "qplaintexteditsearchwidget.h" +class LineNumArea; -class QMarkdownTextEdit : public QPlainTextEdit -{ +class QMarkdownTextEdit : public QPlainTextEdit { Q_OBJECT + Q_PROPERTY( + bool highlighting READ highlightingEnabled WRITE setHighlightingEnabled) -public: + friend class LineNumArea; + + public: enum AutoTextOption { None = 0x0000, - // inserts closing characters for brackets and markdown characters + // inserts closing characters for brackets and Markdown characters BracketClosing = 0x0001, - // removes matching brackets and markdown characters + // removes matching brackets and Markdown characters BracketRemoval = 0x0002 }; Q_DECLARE_FLAGS(AutoTextOptions, AutoTextOption) - explicit QMarkdownTextEdit(QWidget *parent = 0, bool initHighlighter = true); + explicit QMarkdownTextEdit(QWidget *parent = nullptr, + bool initHighlighter = true); MarkdownHighlighter *highlighter(); QPlainTextEditSearchWidget *searchWidget(); void setIgnoredClickUrlSchemata(QStringList ignoredUrlSchemata); - virtual void openUrl(QString urlString); - QString getMarkdownUrlAtPosition(QString text, int position); + virtual void openUrl(const QString &urlString); + QString getMarkdownUrlAtPosition(const QString &text, int position); void initSearchFrame(QWidget *searchFrame, bool darkMode = false); void setAutoTextOptions(AutoTextOptions options); - void setHighlightingEnabled(bool enabled); - static bool isValidUrl(QString urlString); + static bool isValidUrl(const QString &urlString); void resetMouseCursor() const; void setReadOnly(bool ro); + void doSearch(QString &searchText, + QPlainTextEditSearchWidget::SearchMode searchMode = + QPlainTextEditSearchWidget::SearchMode::PlainTextMode); + void hideSearchWidget(bool reset); + void updateSettings(); + void setLineNumbersCurrentLineColor(QColor color); + void setLineNumbersOtherLineColor(QColor color); + void setSearchWidgetDebounceDelay(uint debounceDelay); -public slots: + void setHighlightingEnabled(bool enabled); + [[nodiscard]] bool highlightingEnabled() const; + + void setHighlightCurrentLine(bool set); + bool highlightCurrentLine(); + + void setCurrentLineHighlightColor(const QColor &c); + QColor currentLineHighlightColor(); + + public Q_SLOTS: void duplicateText(); - void setText(const QString & text); - void setPlainText(const QString & text); + void setText(const QString &text); + void setPlainText(const QString &text); void adjustRightMargin(); void hide(); bool openLinkAtCursorPosition(); - bool handleBracketRemoval(); + bool handleBackspaceEntered(); + void centerTheCursor(); + void undo(); + void moveTextUpDown(bool up); + void setLineNumberEnabled(bool enabled); -protected: - MarkdownHighlighter *_highlighter; + protected: + QTextCursor _textCursor; + MarkdownHighlighter *_highlighter = nullptr; bool _highlightingEnabled; QStringList _ignoredClickUrlSchemata; QPlainTextEditSearchWidget *_searchWidget; QWidget *_searchFrame; AutoTextOptions _autoTextOptions; - QStringList _openingCharacters; - QStringList _closingCharacters; + bool _mouseButtonDown = false; + bool _centerCursor = false; + bool _highlightCurrentLine = false; + QColor _currentLineHighlightColor = QColor(); + uint _debounceDelay = 0; - bool eventFilter(QObject *obj, QEvent *event); - bool increaseSelectedTextIndention(bool reverse); - bool handleTabEntered(bool reverse); - QMap parseMarkdownUrlsFromText(QString text); + bool eventFilter(QObject *obj, QEvent *event) override; + QMargins viewportMargins(); + bool increaseSelectedTextIndention( + bool reverse, + const QString &indentCharacters = QChar::fromLatin1('\t')); + bool handleTabEntered(bool reverse, const QString &indentCharacters = + QChar::fromLatin1('\t')); + QMap parseMarkdownUrlsFromText(const QString &text); bool handleReturnEntered(); - bool handleBracketClosing(QString openingCharacter, - QString closingCharacter = ""); - bool bracketClosingCheck(QString openingCharacter, - QString closingCharacter); - bool quotationMarkCheck(QString quotationCharacter); - void focusOutEvent(QFocusEvent *event); - void paintEvent(QPaintEvent *e); + bool handleBracketClosing(const QChar openingCharacter, + QChar closingCharacter = QChar()); + bool bracketClosingCheck(const QChar openingCharacter, + QChar closingCharacter); + bool quotationMarkCheck(const QChar quotationCharacter); + void focusOutEvent(QFocusEvent *event) override; + void paintEvent(QPaintEvent *e) override; + bool handleCharRemoval(MarkdownHighlighter::RangeType type, int block, int position); + void resizeEvent(QResizeEvent *event) override; + void setLineNumberLeftMarginOffset(int offset); + int _lineNumberLeftMarginOffset = 0; + LineNumArea *lineNumberArea() + { + return _lineNumArea; + } + void updateLineNumAreaGeometry(); + void updateLineNumberArea(const QRect rect, int dy); + Q_SLOT void updateLineNumberAreaWidth(int); + bool _handleBracketClosingUsed; + LineNumArea *_lineNumArea; -signals: + Q_SIGNALS: void urlClicked(QString url); + void zoomIn(); + void zoomOut(); }; diff --git a/client/qmarkdowntextedit/qmarkdowntextedit.pc.in b/client/qmarkdowntextedit/qmarkdowntextedit.pc.in new file mode 100644 index 0000000..b616d65 --- /dev/null +++ b/client/qmarkdowntextedit/qmarkdowntextedit.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@/ + +Name: @PROJECT_NAME@ +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION@ + +Requires: +Libs: -L${libdir} -lqmarkdowntextedit +Cflags: -I${includedir} diff --git a/client/qmarkdowntextedit/qmarkdowntextedit.pro b/client/qmarkdowntextedit/qmarkdowntextedit.pro index 518ae85..929c719 100644 --- a/client/qmarkdowntextedit/qmarkdowntextedit.pro +++ b/client/qmarkdowntextedit/qmarkdowntextedit.pro @@ -1,23 +1,5 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2016-01-11T16:56:21 -# -#------------------------------------------------- - -QT += core gui - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -TARGET = QMarkdownTextedit -TEMPLATE = app -TRANSLATIONS = trans/qmarkdowntextedit_de.ts -CONFIG += c++11 - -SOURCES += main.cpp \ - mainwindow.cpp \ - -HEADERS += mainwindow.h - -FORMS += mainwindow.ui - -include(qmarkdowntextedit.pri) +TEMPLATE = subdirs +SUBDIRS = app lib +app.file = qmarkdowntextedit-app.pro +lib.file = qmarkdowntextedit-lib.pro +app.depends = lib diff --git a/client/qmarkdowntextedit/qownlanguagedata.cpp b/client/qmarkdowntextedit/qownlanguagedata.cpp new file mode 100644 index 0000000..0d044c5 --- /dev/null +++ b/client/qmarkdowntextedit/qownlanguagedata.cpp @@ -0,0 +1,5740 @@ +/* + * MIT License + * + * Copyright (c) 2019-2021 Waqar Ahmed -- + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "qownlanguagedata.h" + +#include +#include +/* ------------------------ + * TEMPLATE FOR LANG DATA + * ------------------------- + * + * loadXXXData, where XXX is the language + * keywords are the language keywords e.g, const + * types are built-in types i.e, int, char, var + * literals are words like, true false + * builtin are the library functions + * other can contain any other thing, for e.g, in cpp it contains the + preprocessor + + xxx_keywords = { + }; + + xxx_types = { + }; + + xxx_literals = { + }; + + xxx_builtin = { + }; + + xxx_other = { + }; + +*/ + +/**********************************************************/ +/* C/C++ Data *********************************************/ +/**********************************************************/ + +static bool cppDataInitialized = false; +static QMultiHash cpp_keywords; +static QMultiHash cpp_types; +static QMultiHash cpp_builtin; +static QMultiHash cpp_literals; +static QMultiHash cpp_other; +void initCppData() { + cpp_keywords = QMultiHash{ + {('a'), QLatin1String("alignas")}, + {('a'), QLatin1String("alignof")}, + {('a'), QLatin1String("and")}, + {('a'), QLatin1String("and_eq")}, + {('a'), QLatin1String("asm")}, + {('b'), QLatin1String("bit_and")}, + {('b'), QLatin1String("bit_or")}, + {('b'), QLatin1String("break")}, + {('c'), QLatin1String("case")}, + {('c'), QLatin1String("catch")}, + {('c'), QLatin1String("compl")}, + {('c'), QLatin1String("concept")}, + {('c'), QLatin1String("const")}, + {('c'), QLatin1String("constinit")}, + {('c'), QLatin1String("constexpr")}, + {('c'), QLatin1String("consteval")}, + {('c'), QLatin1String("const_cast")}, + {('c'), QLatin1String("continue")}, + {('c'), QLatin1String("co_await")}, + {('c'), QLatin1String("co_return")}, + {('c'), QLatin1String("co_yield")}, + {('d'), QLatin1String("decltype")}, + {('d'), QLatin1String("default")}, + {('d'), QLatin1String("delete")}, + {('d'), QLatin1String("do")}, + {('d'), QLatin1String("dynamic_cast")}, + {('e'), QLatin1String("else")}, + {('e'), QLatin1String("explicit")}, + {('e'), QLatin1String("export")}, + {('e'), QLatin1String("extern")}, + {('f'), QLatin1String("for")}, + {('f'), QLatin1String("friend")}, + {('g'), QLatin1String("goto")}, + {('i'), QLatin1String("if")}, + {('i'), QLatin1String("inline")}, + {('m'), QLatin1String("mutable")}, + {('n'), QLatin1String("new")}, + {('n'), QLatin1String("not")}, + {('n'), QLatin1String("not_eq")}, + {('n'), QLatin1String("noexcept")}, + {('o'), QLatin1String("or")}, + {('o'), QLatin1String("or_eq")}, + {('o'), QLatin1String("operator")}, + {('p'), QLatin1String("private")}, + {('p'), QLatin1String("protected")}, + {('p'), QLatin1String("public")}, + {('r'), QLatin1String("register")}, + {('r'), QLatin1String("reinterpret_cast")}, + {('r'), QLatin1String("requires")}, + {('r'), QLatin1String("return")}, + {('s'), QLatin1String("signal")}, + {('s'), QLatin1String("sizeof")}, + {('s'), QLatin1String("slot")}, + {('s'), QLatin1String("static")}, + {('s'), QLatin1String("static_assert")}, + {('s'), QLatin1String("static_cast")}, + {('s'), QLatin1String("switch")}, + {('t'), QLatin1String("template")}, + {('t'), QLatin1String("this")}, + {('t'), QLatin1String("thread_local")}, + {('t'), QLatin1String("throw")}, + {('t'), QLatin1String("try")}, + {('t'), QLatin1String("typeid")}, + {('t'), QLatin1String("typedef")}, + {('t'), QLatin1String("typename")}, + {('u'), QLatin1String("using")}, + {('v'), QLatin1String("volatile")}, + {('w'), QLatin1String("while")}, + {('x'), QLatin1String("xor")}, + {('x'), QLatin1String("xor_eq")}}; + + cpp_types = { + {('a'), QLatin1String("auto")}, + {('b'), QLatin1String("bool")}, + {('c'), QLatin1String("char")}, + {('c'), QLatin1String("char8_t")}, + {('c'), QLatin1String("char16_t")}, + {('c'), QLatin1String("char32_t")}, + {('c'), QLatin1String("class")}, + {('d'), QLatin1String("double")}, + {('e'), QLatin1String("enum")}, + {('f'), QLatin1String("float")}, + {('i'), QLatin1String("int")}, + {('i'), QLatin1String("int8_t")}, + {('i'), QLatin1String("int16_t")}, + {('i'), QLatin1String("int32_t")}, + {('i'), QLatin1String("int64_t")}, + {('i'), QLatin1String("int_fast8_t")}, + {('i'), QLatin1String("int_fast16_t")}, + {('i'), QLatin1String("int_fast32_t")}, + {('i'), QLatin1String("int_fast64_t")}, + {('i'), QLatin1String("intmax_t")}, + {('i'), QLatin1String("intptr_t")}, + {('l'), QLatin1String("long")}, + {('n'), QLatin1String("namespace")}, + {('Q'), QLatin1String("QHash")}, + {('Q'), QLatin1String("QList")}, + {('Q'), QLatin1String("QMap")}, + {('Q'), QLatin1String("QString")}, + {('Q'), QLatin1String("QVector")}, + {('s'), QLatin1String("short")}, + {('s'), QLatin1String("size_t")}, + {('s'), QLatin1String("signed")}, + {('s'), QLatin1String("struct")}, + {('s'), QLatin1String("ssize_t")}, + {('u'), QLatin1String("uint8_t")}, + {('u'), QLatin1String("uint16_t")}, + {('u'), QLatin1String("uint32_t")}, + {('u'), QLatin1String("uint64_t")}, + {('u'), QLatin1String("uint_fast8_t")}, + {('u'), QLatin1String("uint_fast16_t")}, + {('u'), QLatin1String("uint_fast32_t")}, + {('u'), QLatin1String("uint_fast64_t")}, + {('u'), QLatin1String("uint_least8_t")}, + {('u'), QLatin1String("uint_least16_t")}, + {('u'), QLatin1String("uint_least32_t")}, + {('u'), QLatin1String("uint_least64_t")}, + {('u'), QLatin1String("uintmax_t")}, + {('u'), QLatin1String("uintptr_t")}, + {('u'), QLatin1String("unsigned")}, + {('u'), QLatin1String("union")}, + {('v'), QLatin1String("void")}, + {('w'), QLatin1String("wchar_t")}}; + + cpp_literals = {{('f'), QLatin1String("false")}, + {('n'), QLatin1String("nullptr")}, + {('N'), QLatin1String("NULL")}, + {('t'), QLatin1String("true")}}; + + cpp_builtin = {{('s'), QLatin1String("std")}, + {('s'), QLatin1String("string")}, + {('s'), QLatin1String("string_view")}, + {('w'), QLatin1String("wstring")}, + {('c'), QLatin1String("cin")}, + {('c'), QLatin1String("cout")}, + {('c'), QLatin1String("cerr")}, + {('c'), QLatin1String("clog")}, + {('s'), QLatin1String("stdin")}, + {('s'), QLatin1String("stdout")}, + {('s'), QLatin1String("stderr")}, + {('s'), QLatin1String("stringstream")}, + {('i'), QLatin1String("istringstream")}, + {('o'), QLatin1String("ostringstream")}, + {('a'), QLatin1String("auto_ptr")}, + {('d'), QLatin1String("deque")}, + {('l'), QLatin1String("list")}, + {('q'), QLatin1String("queue")}, + {('s'), QLatin1String("stack")}, + {('v'), QLatin1String("vector")}, + {('m'), QLatin1String("map")}, + {('s'), QLatin1String("set")}, + {('b'), QLatin1String("bitset")}, + {('m'), QLatin1String("multiset")}, + {('m'), QLatin1String("multimap")}, + {('u'), QLatin1String("unordered_set")}, + {('u'), QLatin1String("unordered_map")}, + {('u'), QLatin1String("unordered_multiset")}, + {('u'), QLatin1String("unordered_multimap")}, + {('a'), QLatin1String("array")}, + {('s'), QLatin1String("shared_ptr")}, + {('a'), QLatin1String("abort")}, + {('t'), QLatin1String("terminate")}, + {('a'), QLatin1String("abs")}, + {('a'), QLatin1String("acos")}, + {('a'), QLatin1String("asin")}, + {('a'), QLatin1String("atan2")}, + {('a'), QLatin1String("atan")}, + {('c'), QLatin1String("calloc")}, + {('c'), QLatin1String("ceil")}, + {('c'), QLatin1String("cosh")}, + {('c'), QLatin1String("cos")}, + {('e'), QLatin1String("exit")}, + {('e'), QLatin1String("exp")}, + {('f'), QLatin1String("fabs")}, + {('f'), QLatin1String("floor")}, + {('f'), QLatin1String("fmod")}, + {('f'), QLatin1String("fprintf")}, + {('f'), QLatin1String("fputs")}, + {('f'), QLatin1String("free")}, + {('f'), QLatin1String("frexp")}, + {('f'), QLatin1String("fscanf")}, + {('f'), QLatin1String("future")}, + {('i'), QLatin1String("isalnum")}, + {('i'), QLatin1String("isalpha")}, + {('i'), QLatin1String("iscntrl")}, + {('i'), QLatin1String("isdigit")}, + {('i'), QLatin1String("isgraph")}, + {('i'), QLatin1String("islower")}, + {('i'), QLatin1String("isprint")}, + {('i'), QLatin1String("ispunct")}, + {('i'), QLatin1String("isspace")}, + {('i'), QLatin1String("isupper")}, + {('i'), QLatin1String("isxdigit")}, + {('t'), QLatin1String("tolower")}, + {('t'), QLatin1String("toupper")}, + {('l'), QLatin1String("labs")}, + {('l'), QLatin1String("ldexp")}, + {('l'), QLatin1String("log10")}, + {('l'), QLatin1String("log")}, + {('m'), QLatin1String("malloc")}, + {('r'), QLatin1String("realloc")}, + {('m'), QLatin1String("main")}, + {('m'), QLatin1String("memchr")}, + {('m'), QLatin1String("memcmp")}, + {('m'), QLatin1String("memcpy")}, + {('m'), QLatin1String("memset")}, + {('m'), QLatin1String("modf")}, + {('p'), QLatin1String("pow")}, + {('p'), QLatin1String("printf")}, + {('p'), QLatin1String("putchar")}, + {('p'), QLatin1String("puts")}, + {('s'), QLatin1String("scanf")}, + {('s'), QLatin1String("sinh")}, + {('s'), QLatin1String("sin")}, + {('s'), QLatin1String("snprintf")}, + {('s'), QLatin1String("sprintf")}, + {('s'), QLatin1String("sqrt")}, + {('s'), QLatin1String("sscanf")}, + {('s'), QLatin1String("strcat")}, + {('s'), QLatin1String("strchr")}, + {('s'), QLatin1String("strcmp")}, + {('s'), QLatin1String("strcpy")}, + {('s'), QLatin1String("strcspn")}, + {('s'), QLatin1String("strlen")}, + {('s'), QLatin1String("strncat")}, + {('s'), QLatin1String("strncmp")}, + {('s'), QLatin1String("strncpy")}, + {('s'), QLatin1String("strpbrk")}, + {('s'), QLatin1String("strrchr")}, + {('s'), QLatin1String("strspn")}, + {('s'), QLatin1String("strstr")}, + {('t'), QLatin1String("tanh")}, + {('t'), QLatin1String("tan")}, + {('v'), QLatin1String("vfprintf")}, + {('v'), QLatin1String("vprintf")}, + {('v'), QLatin1String("vsprintf")}, + {('e'), QLatin1String("endl")}, + {('i'), QLatin1String("initializer_list")}, + {('u'), QLatin1String("unique_ptr")}, + {('c'), QLatin1String("complex")}, + {('i'), QLatin1String("imaginary")}}; + + cpp_other = { + {('d'), QLatin1String("define")}, {('e'), QLatin1String("else")}, + {('e'), QLatin1String("elif")}, {('e'), QLatin1String("endif")}, + {('e'), QLatin1String("error")}, {('i'), QLatin1String("if")}, + {('i'), QLatin1String("ifdef")}, {('i'), QLatin1String("ifndef")}, + {('i'), QLatin1String("include")}, {('l'), QLatin1String("line")}, + {('p'), QLatin1String("pragma")}, {('P'), QLatin1String("_Pragma")}, + {('u'), QLatin1String("undef")}, {('w'), QLatin1String("warning")}}; +} +void loadCppData(QMultiHash &typess, + QMultiHash &keywordss, + QMultiHash &builtins, + QMultiHash &literalss, + QMultiHash &others) { + if (!cppDataInitialized) { + initCppData(); + cppDataInitialized = true; + } + + typess = cpp_types; + keywordss = cpp_keywords; + builtins = cpp_builtin; + literalss = cpp_literals; + others = cpp_other; +} + +/**********************************************************/ +/* Shell Data *********************************************/ +/**********************************************************/ + +static bool shellDataInitialized = false; +static QMultiHash shell_keywords; +static QMultiHash shell_types; +static QMultiHash shell_literals; +static QMultiHash shell_builtin; +static QMultiHash shell_other; +void initShellData() { + shell_keywords = { + {('i'), QLatin1String("if")}, {('t'), QLatin1String("then")}, + {('e'), QLatin1String("else")}, {('e'), QLatin1String("elif")}, + {('f'), QLatin1String("fi")}, {('f'), QLatin1String("for")}, + {('w'), QLatin1String("while")}, {('i'), QLatin1String("in")}, + {('d'), QLatin1String("do")}, {('d'), QLatin1String("done")}, + {('c'), QLatin1String("case")}, {('e'), QLatin1String("esac")}, + {('f'), QLatin1String("function")}}; + + shell_types = {}; + + shell_literals = {{('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}}; + + shell_builtin = {{('b'), QLatin1String("break")}, + {('c'), QLatin1String("cd")}, + {('c'), QLatin1String("continue")}, + {('e'), QLatin1String("eval")}, + {('e'), QLatin1String("exec")}, + {('e'), QLatin1String("exit")}, + {('e'), QLatin1String("export")}, + {('g'), QLatin1String("getopts")}, + {('h'), QLatin1String("hash")}, + {('p'), QLatin1String("pwd")}, + {('r'), QLatin1String("readonly")}, + {('r'), QLatin1String("return")}, + {('s'), QLatin1String("shift")}, + {('t'), QLatin1String("test")}, + {('t'), QLatin1String("timestrap")}, + {('u'), QLatin1String("umask")}, + {('u'), QLatin1String("unset")}, + {('B'), QLatin1String("Bash")}, + {('a'), QLatin1String("alias")}, + {('b'), QLatin1String("bind")}, + {('b'), QLatin1String("builtin")}, + {('c'), QLatin1String("caller")}, + {('c'), QLatin1String("command")}, + {('d'), QLatin1String("declare")}, + {('e'), QLatin1String("echo")}, + {('e'), QLatin1String("enable")}, + {('h'), QLatin1String("help")}, + {('l'), QLatin1String("let")}, + {('l'), QLatin1String("local")}, + {('l'), QLatin1String("logout")}, + {('m'), QLatin1String("mapfile")}, + {('p'), QLatin1String("printfread")}, + {('r'), QLatin1String("readarray")}, + {('s'), QLatin1String("source")}, + {('t'), QLatin1String("type")}, + {('t'), QLatin1String("typeset")}, + {('u'), QLatin1String("ulimit")}, + {('u'), QLatin1String("unalias")}, + {('m'), QLatin1String("modifiers")}, + {('s'), QLatin1String("set")}, + {('s'), QLatin1String("shopt")}, + {('a'), QLatin1String("autoload")}, + {('b'), QLatin1String("bg")}, + {('b'), QLatin1String("bindkey")}, + {('b'), QLatin1String("bye")}, + {('c'), QLatin1String("cap")}, + {('c'), QLatin1String("chdir")}, + {('c'), QLatin1String("clone")}, + {('c'), QLatin1String("comparguments")}, + {('c'), QLatin1String("compcall")}, + {('c'), QLatin1String("compctl")}, + {('c'), QLatin1String("compdescribe")}, + {('c'), QLatin1String("compfilescompgroups")}, + {('c'), QLatin1String("compquote")}, + {('c'), QLatin1String("comptags")}, + {('c'), QLatin1String("comptry")}, + {('c'), QLatin1String("compvalues")}, + {('d'), QLatin1String("dirs")}, + {('d'), QLatin1String("disable")}, + {('d'), QLatin1String("disown")}, + {('e'), QLatin1String("echotc")}, + {('e'), QLatin1String("echoti")}, + {('e'), QLatin1String("emulatefc")}, + {('f'), QLatin1String("fg")}, + {('f'), QLatin1String("float")}, + {('f'), QLatin1String("functions")}, + {('g'), QLatin1String("getcap")}, + {('g'), QLatin1String("getln")}, + {('h'), QLatin1String("history")}, + {('i'), QLatin1String("integer")}, + {('j'), QLatin1String("jobs")}, + {('k'), QLatin1String("kill")}, + {('l'), QLatin1String("limit")}, + {('l'), QLatin1String("log")}, + {('n'), QLatin1String("noglob")}, + {('p'), QLatin1String("popd")}, + {('p'), QLatin1String("printpushd")}, + {('p'), QLatin1String("pushln")}, + {('r'), QLatin1String("rehash")}, + {('s'), QLatin1String("sched")}, + {('s'), QLatin1String("setcap")}, + {('s'), QLatin1String("setopt")}, + {('s'), QLatin1String("stat")}, + {('s'), QLatin1String("suspend")}, + {('t'), QLatin1String("ttyctl")}, + {('u'), QLatin1String("unfunction")}, + {('u'), QLatin1String("unhash")}, + {('u'), QLatin1String("unlimitunsetopt")}, + {('v'), QLatin1String("vared")}, + {('w'), QLatin1String("wait")}, + {('w'), QLatin1String("whence")}, + {('w'), QLatin1String("where")}, + {('w'), QLatin1String("which")}, + {('z'), QLatin1String("zcompile")}, + {('z'), QLatin1String("zformat")}, + {('z'), QLatin1String("zftp")}, + {('z'), QLatin1String("zle")}, + {('z'), QLatin1String("zmodload")}, + {('z'), QLatin1String("zparseopts")}, + {('z'), QLatin1String("zprof")}, + {('z'), QLatin1String("zpty")}, + {('z'), QLatin1String("zregexparse")}, + {('z'), QLatin1String("zsocket")}, + {('z'), QLatin1String("zstyle")}, + {('z'), QLatin1String("ztcp")}, + {('g'), QLatin1String("git")}, + {('r'), QLatin1String("rm")}, + {('s'), QLatin1String("sudo")}, + {('f'), QLatin1String("fdisk")}, + {('a'), QLatin1String("apt")}, + {('s'), QLatin1String("snap")}, + {('f'), QLatin1String("flatpak")}, + {('s'), QLatin1String("snapcraft")}, + {('y'), QLatin1String("yaourt")}, + {('n'), QLatin1String("nmcli")}, + {('p'), QLatin1String("pacman")}, + {('p'), QLatin1String("pamac")}, + {('f'), QLatin1String("fsck")}, + {('m'), QLatin1String("mount")}, + {('m'), QLatin1String("mkdir")}, + {('m'), QLatin1String("mkswap")}, + {('s'), QLatin1String("sleep")}, + {('l'), QLatin1String("ls")}, + {('w'), QLatin1String("wget")}, + {('k'), QLatin1String("kill")}, + {('k'), QLatin1String("killall")}, + {('g'), QLatin1String("gdb")}, + {('Q'), QLatin1String("QOwnNotes")}, + {('q'), QLatin1String("qownnotes")}, + {('d'), QLatin1String("docker")}, + {('o'), QLatin1String("openssl")}, + {('p'), QLatin1String("php")}, + {('p'), QLatin1String("python")}, + {('p'), QLatin1String("perl")}, + {('g'), QLatin1String("go")}, + {('c'), QLatin1String("curl")}}; + + shell_other = {}; +} + +void loadShellData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!shellDataInitialized) { + initShellData(); + shellDataInitialized = true; + } + types = shell_types; + keywords = shell_keywords; + builtin = shell_builtin; + literals = shell_literals; + other = shell_other; +} + +/**********************************************************/ +/* JS Data *********************************************/ +/**********************************************************/ +static bool JSDataInitialized = false; +static QMultiHash js_keywords; +static QMultiHash js_types; +static QMultiHash js_literals; +static QMultiHash js_builtin; +static QMultiHash js_other; +void initJSData() { + js_keywords = {{('i'), QLatin1String("in")}, + {('o'), QLatin1String("of")}, + {('i'), QLatin1String("if")}, + {('f'), QLatin1String("for")}, + {('w'), QLatin1String("while")}, + {('f'), QLatin1String("finally")}, + {('n'), QLatin1String("new")}, + {('f'), QLatin1String("function")}, + {('d'), QLatin1String("do")}, + {('r'), QLatin1String("return")}, + {('v'), QLatin1String("void")}, + {('e'), QLatin1String("else")}, + {('b'), QLatin1String("break")}, + {('c'), QLatin1String("catch")}, + {('i'), QLatin1String("instanceof")}, + {('w'), QLatin1String("with")}, + {('t'), QLatin1String("throw")}, + {('c'), QLatin1String("case")}, + {('d'), QLatin1String("default")}, + {('t'), QLatin1String("try")}, + {('t'), QLatin1String("this")}, + {('s'), QLatin1String("switch")}, + {('c'), QLatin1String("continue")}, + {('t'), QLatin1String("typeof")}, + {('d'), QLatin1String("delete")}, + {('l'), QLatin1String("let")}, + {('y'), QLatin1String("yield")}, + {('c'), QLatin1String("const")}, + {('e'), QLatin1String("export")}, + {('s'), QLatin1String("super")}, + {('d'), QLatin1String("debugger")}, + {('a'), QLatin1String("as")}, + {('a'), QLatin1String("async")}, + {('a'), QLatin1String("await")}, + {('s'), QLatin1String("static")}, + {('i'), QLatin1String("import")}, + {('f'), QLatin1String("from")}, + {('a'), QLatin1String("as")}}; + + js_types = { + {('v'), QLatin1String("var")}, {('c'), QLatin1String("class")}, + {('b'), QLatin1String("byte")}, {('e'), QLatin1String("enum")}, + {('f'), QLatin1String("float")}, {('s'), QLatin1String("short")}, + {('l'), QLatin1String("long")}, {('i'), QLatin1String("int")}, + {('v'), QLatin1String("void")}, {('b'), QLatin1String("boolean")}, + {('d'), QLatin1String("double")}}; + + js_literals = { + {('f'), QLatin1String("false")}, {('n'), QLatin1String("null")}, + {('t'), QLatin1String("true")}, {('u'), QLatin1String("undefined")}, + {('N'), QLatin1String("NaN")}, {('I'), QLatin1String("Infinity")}}; + + js_builtin = {{('e'), QLatin1String("eval")}, + {('i'), QLatin1String("isFinite")}, + {('i'), QLatin1String("isNaN")}, + {('p'), QLatin1String("parseFloat")}, + {('p'), QLatin1String("parseInt")}, + {('d'), QLatin1String("decodeURI")}, + {('d'), QLatin1String("decodeURIComponent")}, + {('e'), QLatin1String("encodeURI")}, + {('e'), QLatin1String("encodeURIComponent")}, + {('e'), QLatin1String("escape")}, + {('u'), QLatin1String("unescape")}, + {('O'), QLatin1String("Object")}, + {('F'), QLatin1String("Function")}, + {('B'), QLatin1String("Boolean")}, + {('E'), QLatin1String("Error")}, + {('E'), QLatin1String("EvalError")}, + {('I'), QLatin1String("InternalError")}, + {('R'), QLatin1String("RangeError")}, + {('R'), QLatin1String("ReferenceError")}, + {('S'), QLatin1String("StopIteration")}, + {('S'), QLatin1String("SyntaxError")}, + {('T'), QLatin1String("TypeError")}, + {('U'), QLatin1String("URIError")}, + {('N'), QLatin1String("Number")}, + {('M'), QLatin1String("Math")}, + {('D'), QLatin1String("Date")}, + {('S'), QLatin1String("String")}, + {('R'), QLatin1String("RegExp")}, + {('A'), QLatin1String("Array")}, + {('F'), QLatin1String("Float32Array")}, + {('F'), QLatin1String("Float64Array")}, + {('I'), QLatin1String("Int16Array")}, + {('I'), QLatin1String("Int32Array")}, + {('I'), QLatin1String("Int8Array")}, + {('U'), QLatin1String("Uint16Array")}, + {('U'), QLatin1String("Uint32Array")}, + {('U'), QLatin1String("Uint8Array")}, + {('U'), QLatin1String("Uint8ClampedArray")}, + {('A'), QLatin1String("ArrayBuffer")}, + {('D'), QLatin1String("DataView")}, + {('J'), QLatin1String("JSON")}, + {('I'), QLatin1String("Intl")}, + {('a'), QLatin1String("arguments")}, + {('r'), QLatin1String("require")}, + {('m'), QLatin1String("module")}, + {('c'), QLatin1String("console")}, + {('w'), QLatin1String("window")}, + {('d'), QLatin1String("document")}, + {('S'), QLatin1String("Symbol")}, + {('S'), QLatin1String("Set")}, + {('M'), QLatin1String("Map")}, + {('W'), QLatin1String("WeakSet")}, + {('W'), QLatin1String("WeakMap")}, + {('P'), QLatin1String("Proxy")}, + {('R'), QLatin1String("Reflect")}, + {('P'), QLatin1String("Promise")}}; + + js_other = {}; +} + +void loadJSData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!JSDataInitialized) { + initJSData(); + JSDataInitialized = true; + } + types = js_types; + keywords = js_keywords; + builtin = js_builtin; + literals = js_literals; + other = js_other; +} + +/**********************************************************/ +/* Nix Data ***********************************************/ +/**********************************************************/ +static bool NixDataInitialized = false; +static QMultiHash nix_keywords; +static QMultiHash nix_types; +static QMultiHash nix_literals; +static QMultiHash nix_builtin; +static QMultiHash nix_other; + +// also see https://github.com/KDE/syntax-highlighting/blob/master/data/syntax/nix.xml +void initNixData() { + nix_keywords = {{('i'), QLatin1String("in")}, + {('a'), QLatin1String("assert")}, + {('k'), QLatin1String("keywords")}, + {('r'), QLatin1String("rec")}, + {('a'), QLatin1String("and")}, + {('o'), QLatin1String("or")}, + {('o'), QLatin1String("of")}, + {('i'), QLatin1String("if")}, + {('f'), QLatin1String("for")}, + {('w'), QLatin1String("while")}, + {('d'), QLatin1String("do")}, + {('r'), QLatin1String("return")}, + {('e'), QLatin1String("else")}, + {('b'), QLatin1String("break")}, + {('w'), QLatin1String("with")}, + {('c'), QLatin1String("case")}, + {('c'), QLatin1String("continue")}, + {('d'), QLatin1String("delete")}, + {('l'), QLatin1String("let")}, + {('e'), QLatin1String("export")}, + {('a'), QLatin1String("as")}, + {('i'), QLatin1String("import")}, + {('f'), QLatin1String("from")}, + {('a'), QLatin1String("as")}}; + + nix_types = {}; + + nix_literals = { + {('f'), QLatin1String("false")}, {('n'), QLatin1String("null")}, + {('t'), QLatin1String("true")}, {('u'), QLatin1String("undefined")}, + {('N'), QLatin1String("NaN")}, {('I'), QLatin1String("Infinity")}}; + + nix_builtin = {{('a'), QLatin1String("abort")}, + {('b'), QLatin1String("baseNameOf")}, + {('d'), QLatin1String("derivation")}, + {('d'), QLatin1String("dirOf")}, + {('f'), QLatin1String("fetchTarball")}, + {('f'), QLatin1String("fetchFromGitHub")}, + {('i'), QLatin1String("import")}, + {('i'), QLatin1String("isNull")}, + {('m'), QLatin1String("map")}, + {('r'), QLatin1String("removeAttrs")}, + {('t'), QLatin1String("throw")}, + {('t'), QLatin1String("toString")}}; + + nix_other = { + {('b'), QLatin1String("builtins")}, + {('c'), QLatin1String("config")}, + {('p'), QLatin1String("pkgs")}, + {('i'), QLatin1String("inputs")}, + {('x'), QLatin1String("xdg")}, + {('e'), QLatin1String("environment")}, + }; +} + +void loadNixData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!NixDataInitialized) { + initNixData(); + NixDataInitialized = true; + } + types = nix_types; + keywords = nix_keywords; + builtin = nix_builtin; + literals = nix_literals; + other = nix_other; +} + +/**********************************************************/ +/* PHP Data *********************************************/ +/**********************************************************/ +static bool PHPDataInitialized = false; +static QMultiHash php_keywords; +static QMultiHash php_types; +static QMultiHash php_literals; +static QMultiHash php_builtin; +static QMultiHash php_other; +void initPHPData() { + php_keywords = {{('a'), QLatin1String("and")}, + {('l'), QLatin1String("list")}, + {('a'), QLatin1String("abstract")}, + {('g'), QLatin1String("global")}, + {('p'), QLatin1String("private")}, + {('e'), QLatin1String("echo")}, + {('i'), QLatin1String("interface")}, + {('a'), QLatin1String("as")}, + {('s'), QLatin1String("static")}, + {('e'), QLatin1String("endswitch")}, + {('i'), QLatin1String("if")}, + {('e'), QLatin1String("endwhile")}, + {('o'), QLatin1String("or")}, + {('c'), QLatin1String("const")}, + {('f'), QLatin1String("for")}, + {('e'), QLatin1String("endforeach")}, + {('s'), QLatin1String("self")}, + {('w'), QLatin1String("while")}, + {('i'), QLatin1String("isset")}, + {('p'), QLatin1String("public")}, + {('p'), QLatin1String("protected")}, + {('e'), QLatin1String("exit")}, + {('f'), QLatin1String("foreach")}, + {('t'), QLatin1String("throw")}, + {('e'), QLatin1String("elseif")}, + {('e'), QLatin1String("empty")}, + {('d'), QLatin1String("do")}, + {('x'), QLatin1String("xor")}, + {('r'), QLatin1String("return")}, + {('p'), QLatin1String("parent")}, + {('c'), QLatin1String("clone")}, + {('u'), QLatin1String("use")}, + {('e'), QLatin1String("else")}, + {('b'), QLatin1String("break")}, + {('p'), QLatin1String("print")}, + {('e'), QLatin1String("eval")}, + {('n'), QLatin1String("new")}, + {('c'), QLatin1String("catch")}, + {('c'), QLatin1String("case")}, + {('e'), QLatin1String("exception")}, + {('d'), QLatin1String("default")}, + {('d'), QLatin1String("die")}, + {('e'), QLatin1String("enddeclare")}, + {('f'), QLatin1String("final")}, + {('t'), QLatin1String("try")}, + {('s'), QLatin1String("switch")}, + {('c'), QLatin1String("continue")}, + {('e'), QLatin1String("endfor")}, + {('e'), QLatin1String("endif")}, + {('d'), QLatin1String("declare")}, + {('u'), QLatin1String("unset")}, + {('t'), QLatin1String("trait")}, + {('g'), QLatin1String("goto")}, + {('i'), QLatin1String("instanceof")}, + {('i'), QLatin1String("insteadof")}, + {('y'), QLatin1String("yield")}, + {('f'), QLatin1String("finally")}}; + + php_types = {{('v'), QLatin1String("var")}, + {('c'), QLatin1String("class")}, + {('e'), QLatin1String("enum")}, + {('a'), QLatin1String("array")}}; + + php_literals = {{('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}, + {('n'), QLatin1String("null")}}; + + php_builtin = { + + }; + + php_other = {{('i'), QLatin1String("include_once")}, + {('i'), QLatin1String("include")}, + {('_'), QLatin1String("__FILE__")}, + {('r'), QLatin1String("require")}, + {('r'), QLatin1String("require_once")}, + {('_'), QLatin1String("__CLASS__")}, + {('_'), QLatin1String("__LINE__")}, + {('_'), QLatin1String("__METHOD__")}, + {('_'), QLatin1String("__FUNCTION__")}, + {('_'), QLatin1String("__DIR__")}, + {('_'), QLatin1String("__NAMESPACE__")}, + + {('S'), QLatin1String("SERVER")}, + {('G'), QLatin1String("GET")}, + {('P'), QLatin1String("POST")}, + {('F'), QLatin1String("FILES")}, + {('R'), QLatin1String("REQUEST")}, + {('S'), QLatin1String("SESSION")}, + {('E'), QLatin1String("ENV")}, + {('C'), QLatin1String("COOKIE")}, + {('G'), QLatin1String("GLOBALS")}, + {('H'), QLatin1String("HTTP_RAW_POST_DATA")}, + {('a'), QLatin1String("argc")}, + {('a'), QLatin1String("argv")}, + {('p'), QLatin1String("php_errormsg")}, + {('h'), QLatin1String("http_response_header")}}; +} +void loadPHPData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!PHPDataInitialized) { + initPHPData(); + PHPDataInitialized = true; + } + types = php_types; + keywords = php_keywords; + builtin = php_builtin; + literals = php_literals; + other = php_other; +} + +/**********************************************************/ +/* QML Data *********************************************/ +/**********************************************************/ +static bool QMLDataInitialized = false; +static QMultiHash qml_keywords; +static QMultiHash qml_types; +static QMultiHash qml_literals; +static QMultiHash qml_builtin; +static QMultiHash qml_other; + +void initQMLData() { + qml_keywords = {{('d'), QLatin1String("default")}, + {('p'), QLatin1String("property")}, + {('i'), QLatin1String("int")}, + {('v'), QLatin1String("var")}, + {('s'), QLatin1String("string")}, + {('f'), QLatin1String("function")}, + {('r'), QLatin1String("readonly")}, + {('M'), QLatin1String("MouseArea")}, + {('d'), QLatin1String("delegate")}, + {('i'), QLatin1String("if")}, + {('e'), QLatin1String("else")}, + + {('e'), QLatin1String("eval")}, + {('i'), QLatin1String("isFinite")}, + {('i'), QLatin1String("isNaN")}, + {('p'), QLatin1String("parseFloat")}, + {('p'), QLatin1String("parseInt")}, + {('d'), QLatin1String("decodeURI")}, + {('d'), QLatin1String("decodeURIComponent")}, + {('e'), QLatin1String("encodeURI")}, + {('e'), QLatin1String("encodeURIComponent")}, + {('e'), QLatin1String("escape")}, + {('u'), QLatin1String("unescape")}, + {('O'), QLatin1String("Object")}, + {('E'), QLatin1String("Error")}, + {('E'), QLatin1String("EvalError")}, + {('I'), QLatin1String("InternalError")}, + {('R'), QLatin1String("RangeError")}, + {('R'), QLatin1String("ReferenceError")}, + {('S'), QLatin1String("StopIteration")}, + {('S'), QLatin1String("SyntaxError")}, + {('T'), QLatin1String("TypeError")}, + {('U'), QLatin1String("URIError")}, + {('N'), QLatin1String("Number")}, + {('M'), QLatin1String("Math")}, + {('D'), QLatin1String("Date")}, + {('S'), QLatin1String("String")}, + {('R'), QLatin1String("RegExp")}, + {('A'), QLatin1String("Array")}, + {('F'), QLatin1String("Float32Array")}, + {('F'), QLatin1String("Float64Array")}, + {('I'), QLatin1String("Int16Array")}, + {('I'), QLatin1String("Int32Array")}, + {('I'), QLatin1String("Int8Array")}, + {('U'), QLatin1String("Uint16Array")}, + {('U'), QLatin1String("Uint32Array")}, + {('U'), QLatin1String("Uint8Array")}, + {('U'), QLatin1String("Uint8ClampedArray")}, + {('A'), QLatin1String("ArrayBuffer")}, + {('D'), QLatin1String("DataView")}, + {('J'), QLatin1String("JSON")}, + {('I'), QLatin1String("Intl")}, + {('a'), QLatin1String("arguments")}, + {('m'), QLatin1String("module")}, + {('c'), QLatin1String("console")}, + {('w'), QLatin1String("window")}, + {('d'), QLatin1String("document")}, + {('S'), QLatin1String("Symbol")}, + {('S'), QLatin1String("Set")}, + {('M'), QLatin1String("Map")}, + {('W'), QLatin1String("WeakSet")}, + {('W'), QLatin1String("WeakMap")}, + {('P'), QLatin1String("Proxy")}, + {('R'), QLatin1String("Reflect")}, + {('B'), QLatin1String("Behavior")}, + {('c'), QLatin1String("color")}, + {('c'), QLatin1String("coordinate")}, + {('d'), QLatin1String("date")}, + {('e'), QLatin1String("enumeration")}, + {('f'), QLatin1String("font")}, + {('g'), QLatin1String("geocircle")}, + {('g'), QLatin1String("georectangle")}, + {('g'), QLatin1String("geoshape")}, + {('l'), QLatin1String("list")}, + {('m'), QLatin1String("matrix4x4")}, + {('p'), QLatin1String("parent")}, + {('p'), QLatin1String("point")}, + {('q'), QLatin1String("quaternion")}, + {('r'), QLatin1String("real")}, + {('s'), QLatin1String("size")}, + {('s'), QLatin1String("string")}, + {('v'), QLatin1String("variant")}, + {('v'), QLatin1String("vector2d")}, + {('v'), QLatin1String("vector3d")}, + {('v'), QLatin1String("vector4d")}, + {('P'), QLatin1String("Promise")}}; + + qml_types = { + {('R'), QLatin1String("Rectangle")}, + {('T'), QLatin1String("Text")}, + {('c'), QLatin1String("color")}, + {('I'), QLatin1String("Item")}, + {('u'), QLatin1String("url")}, + {('C'), QLatin1String("Component")}, + {('B'), QLatin1String("Button")}, + {('T'), QLatin1String("TextInput")}, + {('L'), QLatin1String("ListView")}, + + }; + + qml_literals = {{('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}}; + + qml_builtin = { + + }; + + qml_other = {{('i'), QLatin1String("import")}}; +} +void loadQMLData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!QMLDataInitialized) { + initQMLData(); + QMLDataInitialized = true; + } + types = qml_types; + keywords = qml_keywords; + builtin = qml_builtin; + literals = qml_literals; + other = qml_other; +} + +/**********************************************************/ +/* Python Data *********************************************/ +/**********************************************************/ +static bool PyDataInitialized = false; +static QMultiHash py_keywords; +static QMultiHash py_types; +static QMultiHash py_literals; +static QMultiHash py_builtin; +static QMultiHash py_other; + +void initPyData() { + py_keywords = { + {('a'), QLatin1String("and")}, {('e'), QLatin1String("elif")}, + {('i'), QLatin1String("is")}, {('g'), QLatin1String("global")}, + {('a'), QLatin1String("as")}, {('i'), QLatin1String("in")}, + {('i'), QLatin1String("if")}, {('f'), QLatin1String("from")}, + {('r'), QLatin1String("raise")}, {('f'), QLatin1String("for")}, + {('e'), QLatin1String("except")}, {('f'), QLatin1String("finally")}, + {('p'), QLatin1String("print")}, {('p'), QLatin1String("pass")}, + {('r'), QLatin1String("return")}, {('e'), QLatin1String("exec")}, + {('e'), QLatin1String("else")}, {('b'), QLatin1String("break")}, + {('n'), QLatin1String("not")}, {('w'), QLatin1String("with")}, + {('c'), QLatin1String("class")}, {('a'), QLatin1String("assert")}, + {('y'), QLatin1String("yield")}, {('t'), QLatin1String("try")}, + {('w'), QLatin1String("while")}, {('c'), QLatin1String("continue")}, + {('d'), QLatin1String("del")}, {('o'), QLatin1String("or")}, + {('d'), QLatin1String("def")}, {('l'), QLatin1String("lambda")}, + {('a'), QLatin1String("async")}, {('a'), QLatin1String("await")}, + {('n'), QLatin1String("nonlocal")}, + }; + + py_types = { + + }; + + py_literals = {{('F'), QLatin1String("False")}, + {('T'), QLatin1String("True")}, + {('N'), QLatin1String("None")}}; + + py_builtin = {{('_'), QLatin1String("__import__")}, + {('a'), QLatin1String("abs")}, + {('a'), QLatin1String("all")}, + {('a'), QLatin1String("any")}, + {('a'), QLatin1String("apply")}, + {('a'), QLatin1String("ascii")}, + {('b'), QLatin1String("basestring")}, + {('b'), QLatin1String("bin")}, + {('b'), QLatin1String("bool")}, + {('b'), QLatin1String("buffer")}, + {('b'), QLatin1String("bytearray")}, + {('b'), QLatin1String("bytes")}, + {('c'), QLatin1String("callable")}, + {('c'), QLatin1String("chr")}, + {('c'), QLatin1String("classmethod")}, + {('c'), QLatin1String("cmp")}, + {('c'), QLatin1String("coerce")}, + {('c'), QLatin1String("compile")}, + {('c'), QLatin1String("complex")}, + {('d'), QLatin1String("delattr")}, + {('d'), QLatin1String("dict")}, + {('d'), QLatin1String("dir")}, + {('d'), QLatin1String("divmod")}, + {('e'), QLatin1String("enumerate")}, + {('e'), QLatin1String("eval")}, + {('e'), QLatin1String("execfile")}, + {('f'), QLatin1String("file")}, + {('f'), QLatin1String("filter")}, + {('f'), QLatin1String("float")}, + {('f'), QLatin1String("format")}, + {('f'), QLatin1String("frozenset")}, + {('g'), QLatin1String("getattr")}, + {('g'), QLatin1String("globals")}, + {('h'), QLatin1String("hasattr")}, + {('h'), QLatin1String("hash")}, + {('h'), QLatin1String("help")}, + {('h'), QLatin1String("hex")}, + {('i'), QLatin1String("id")}, + {('i'), QLatin1String("input")}, + {('i'), QLatin1String("int")}, + {('i'), QLatin1String("intern")}, + {('i'), QLatin1String("isinstance")}, + {('i'), QLatin1String("issubclass")}, + {('i'), QLatin1String("iter")}, + {('l'), QLatin1String("len")}, + {('l'), QLatin1String("list")}, + {('l'), QLatin1String("locals")}, + {('l'), QLatin1String("long")}, + {('m'), QLatin1String("map")}, + {('m'), QLatin1String("max")}, + {('m'), QLatin1String("memoryview")}, + {('m'), QLatin1String("min")}, + {('n'), QLatin1String("next")}, + {('o'), QLatin1String("object")}, + {('o'), QLatin1String("oct")}, + {('o'), QLatin1String("open")}, + {('o'), QLatin1String("ord")}, + {('p'), QLatin1String("pow")}, + {('p'), QLatin1String("property")}, + {('r'), QLatin1String("range")}, + {('r'), QLatin1String("raw_input")}, + {('r'), QLatin1String("reduce")}, + {('r'), QLatin1String("reload")}, + {('r'), QLatin1String("repr")}, + {('r'), QLatin1String("reversed")}, + {('r'), QLatin1String("round")}, + {('s'), QLatin1String("set")}, + {('s'), QLatin1String("setattr")}, + {('s'), QLatin1String("slice")}, + {('s'), QLatin1String("sorted")}, + {('s'), QLatin1String("staticmethod")}, + {('s'), QLatin1String("str")}, + {('s'), QLatin1String("sum")}, + {('s'), QLatin1String("super")}, + {('t'), QLatin1String("tuple")}, + {('t'), QLatin1String("type")}, + {('u'), QLatin1String("unichr")}, + {('u'), QLatin1String("unicode")}, + {('v'), QLatin1String("vars")}, + {('x'), QLatin1String("xrange")}, + {('z'), QLatin1String("zip")}}; + + py_other = {{('i'), QLatin1String("import")}}; +} +void loadPythonData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!PyDataInitialized) { + initPyData(); + PyDataInitialized = true; + } + types = py_types; + keywords = py_keywords; + builtin = py_builtin; + literals = py_literals; + other = py_other; +} + +/********************************************************/ +/*** Rust DATA ***********************************/ +/********************************************************/ +static bool rustDataInitialized = false; +static QMultiHash rust_keywords; +static QMultiHash rust_types; +static QMultiHash rust_literals; +static QMultiHash rust_builtin; +static QMultiHash rust_other; +void initRustData() { + rust_keywords = { + {('a'), QLatin1String("abstract")}, {('a'), QLatin1String("alignof")}, + {('a'), QLatin1String("as")}, {('a'), QLatin1String("async")}, + {('a'), QLatin1String("await")}, {('b'), QLatin1String("be")}, + {('b'), QLatin1String("box")}, {('b'), QLatin1String("break")}, + {('c'), QLatin1String("const")}, {('c'), QLatin1String("continue")}, + {('c'), QLatin1String("crate")}, {('d'), QLatin1String("do")}, + {('d'), QLatin1String("dyn")}, {('e'), QLatin1String("else")}, + {('e'), QLatin1String("extern")}, {('f'), QLatin1String("final")}, + {('f'), QLatin1String("fn")}, {('f'), QLatin1String("for")}, + {('i'), QLatin1String("if")}, {('i'), QLatin1String("impl")}, + {('i'), QLatin1String("in")}, {('l'), QLatin1String("let")}, + {('l'), QLatin1String("loop")}, {('m'), QLatin1String("match")}, + {('m'), QLatin1String("mod")}, {('m'), QLatin1String("move")}, + {('m'), QLatin1String("mut")}, {('o'), QLatin1String("offsetof")}, + {('o'), QLatin1String("once")}, {('o'), QLatin1String("override")}, + {('p'), QLatin1String("priv")}, {('p'), QLatin1String("pub")}, + {('p'), QLatin1String("pure")}, {('r'), QLatin1String("ref")}, + {('r'), QLatin1String("return")}, {('s'), QLatin1String("sizeof")}, + {('s'), QLatin1String("static")}, {('s'), QLatin1String("self")}, + {('S'), QLatin1String("Self")}, {('s'), QLatin1String("super")}, + {('t'), QLatin1String("trait")}, {('t'), QLatin1String("type")}, + {('t'), QLatin1String("typeof")}, {('u'), QLatin1String("unsafe")}, + {('u'), QLatin1String("unsized")}, {('u'), QLatin1String("use")}, + {('v'), QLatin1String("virtual")}, {('w'), QLatin1String("where")}, + {('w'), QLatin1String("while")}, {('y'), QLatin1String("yield")}, + }; + + rust_types = { + {('u'), QLatin1String("union")}, {('e'), QLatin1String("enum")}, + {('s'), QLatin1String("struct")}, + + {('i'), QLatin1String("i8")}, {('i'), QLatin1String("i16")}, + {('i'), QLatin1String("i32")}, {('i'), QLatin1String("i64")}, + {('i'), QLatin1String("i128")}, {('i'), QLatin1String("isize")}, + {('u'), QLatin1String("u8")}, {('u'), QLatin1String("u16")}, + {('u'), QLatin1String("u32")}, {('u'), QLatin1String("u64")}, + {('u'), QLatin1String("u128")}, {('u'), QLatin1String("usize")}, + {('f'), QLatin1String("f32")}, {('f'), QLatin1String("f64")}, + {('s'), QLatin1String("str")}, {('c'), QLatin1String("char")}, + {('b'), QLatin1String("bool")}, {('B'), QLatin1String("Box")}, + {('O'), QLatin1String("Option")}, {('R'), QLatin1String("Result")}, + {('S'), QLatin1String("String")}, {('V'), QLatin1String("Vec")}}; + + rust_literals = {{('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}}; + + rust_builtin = { + + }; + + rust_other = {{('a'), QLatin1String("assert!")}, + {('a'), QLatin1String("assert_eq!")}, + {('b'), QLatin1String("bitflags!")}, + {('b'), QLatin1String("bytes!")}, + {('c'), QLatin1String("cfg!")}, + {('c'), QLatin1String("col!")}, + {('c'), QLatin1String("concat!")}, + {('c'), QLatin1String("concat_idents!")}, + {('d'), QLatin1String("debug_assert!")}, + {('d'), QLatin1String("debug_assert_eq!")}, + {('e'), QLatin1String("env!")}, + {('p'), QLatin1String("panic!")}, + {('f'), QLatin1String("file!")}, + {('f'), QLatin1String("format!")}, + {('f'), QLatin1String("format_args!")}, + {('i'), QLatin1String("include_bin!")}, + {('i'), QLatin1String("include_str!")}, + {('l'), QLatin1String("line!")}, + {('l'), QLatin1String("local_data_key!")}, + {('m'), QLatin1String("module_path!")}, + {('o'), QLatin1String("option_env!")}, + {('p'), QLatin1String("print!")}, + {('p'), QLatin1String("println!")}, + {('s'), QLatin1String("select!")}, + {('s'), QLatin1String("stringify!")}, + {('t'), QLatin1String("try!")}, + {('u'), QLatin1String("unimplemented!")}, + {('u'), QLatin1String("unreachable!")}, + {('v'), QLatin1String("vec!")}, + {('w'), QLatin1String("write!")}, + {('w'), QLatin1String("writeln!")}, + {('m'), QLatin1String("macro_rules!")}, + {('a'), QLatin1String("assert_ne!")}, + {('d'), QLatin1String("debug_assert_ne!")}}; +} +void loadRustData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!rustDataInitialized) { + initRustData(); + rustDataInitialized = true; + } + types = rust_types; + keywords = rust_keywords; + builtin = rust_builtin; + literals = rust_literals; + other = rust_other; +} + +/********************************************************/ +/*** Java DATA ***********************************/ +/********************************************************/ +static bool javaDataInitialized = false; +static QMultiHash java_keywords; +static QMultiHash java_types; +static QMultiHash java_literals; +static QMultiHash java_builtin; +static QMultiHash java_other; +void initJavaData() { + java_keywords = {{('a'), QLatin1String("abstract")}, + {('a'), QLatin1String("assert")}, + {('b'), QLatin1String("break")}, + {('c'), QLatin1String("case")}, + {('c'), QLatin1String("catch")}, + {('c'), QLatin1String("const")}, + {('c'), QLatin1String("continue")}, + {('d'), QLatin1String("default")}, + {('d'), QLatin1String("do")}, + {('e'), QLatin1String("else")}, + {('e'), QLatin1String("exports")}, + {('e'), QLatin1String("extends")}, + {('f'), QLatin1String("final")}, + {('f'), QLatin1String("finally")}, + {('f'), QLatin1String("for")}, + {('g'), QLatin1String("goto")}, + {('i'), QLatin1String("if")}, + {('i'), QLatin1String("implements")}, + {('i'), QLatin1String("import")}, + {('i'), QLatin1String("instanceof")}, + {('i'), QLatin1String("interface")}, + {('l'), QLatin1String("long")}, + {('m'), QLatin1String("module")}, + {('n'), QLatin1String("native")}, + {('n'), QLatin1String("new")}, + {('n'), QLatin1String("null")}, + {('o'), QLatin1String("open")}, + {('o'), QLatin1String("opens")}, + {('p'), QLatin1String("package")}, + {('p'), QLatin1String("private")}, + {('p'), QLatin1String("protected")}, + {('p'), QLatin1String("provides")}, + {('p'), QLatin1String("public")}, + {('r'), QLatin1String("requires")}, + {('r'), QLatin1String("return")}, + {('s'), QLatin1String("static")}, + {('s'), QLatin1String("strictfp")}, + {('s'), QLatin1String("super")}, + {('s'), QLatin1String("switch")}, + {('s'), QLatin1String("synchronized")}, + {('t'), QLatin1String("this")}, + {('t'), QLatin1String("throw")}, + {('t'), QLatin1String("throws")}, + {('t'), QLatin1String("to")}, + {('t'), QLatin1String("transient")}, + {('t'), QLatin1String("transitive")}, + {('t'), QLatin1String("try")}, + {('u'), QLatin1String("uses")}, + {('v'), QLatin1String("var")}, + {('v'), QLatin1String("volatile")}, + {('w'), QLatin1String("while")}, + {('w'), QLatin1String("with")}, + {('y'), QLatin1String("yield")}}; + + java_types = { + {('v'), QLatin1String("void")}, {('f'), QLatin1String("float")}, + {('b'), QLatin1String("boolean")}, {('b'), QLatin1String("byte")}, + {('i'), QLatin1String("int")}, {('c'), QLatin1String("char")}, + {('c'), QLatin1String("class")}, {('d'), QLatin1String("double")}, + {('e'), QLatin1String("enum")}, {('s'), QLatin1String("short")}, + + }; + + java_literals = {{('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}}; + + java_builtin = { + + }; + + java_other = { + + }; +} +void loadJavaData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!javaDataInitialized) { + initJavaData(); + javaDataInitialized = true; + } + types = java_types; + keywords = java_keywords; + builtin = java_builtin; + literals = java_literals; + other = java_other; +} + +/********************************************************/ +/*** C# DATA *************************************/ +/********************************************************/ +static bool csharpDataInitialized = false; +static QMultiHash csharp_keywords; +static QMultiHash csharp_types; +static QMultiHash csharp_literals; +static QMultiHash csharp_builtin; +static QMultiHash csharp_other; +void initCSharpData() { + csharp_keywords = {{('a'), QLatin1String("abstract")}, + {('a'), QLatin1String("add")}, + {('a'), QLatin1String("alias")}, + {('a'), QLatin1String("as")}, + {('a'), QLatin1String("ascending")}, + {('a'), QLatin1String("async")}, + {('a'), QLatin1String("await")}, + {('b'), QLatin1String("base")}, + {('b'), QLatin1String("break")}, + {('c'), QLatin1String("case")}, + {('c'), QLatin1String("catch")}, + {('c'), QLatin1String("checked")}, + {('c'), QLatin1String("const")}, + {('c'), QLatin1String("continue")}, + {('d'), QLatin1String("decimal")}, + {('d'), QLatin1String("default")}, + {('d'), QLatin1String("delegate")}, + {('d'), QLatin1String("descending")}, + {('d'), QLatin1String("do")}, + {('d'), QLatin1String("dynamic")}, + {('e'), QLatin1String("else")}, + {('e'), QLatin1String("event")}, + {('e'), QLatin1String("explicit")}, + {('e'), QLatin1String("extern")}, + {('f'), QLatin1String("finally")}, + {('f'), QLatin1String("fixed")}, + {('f'), QLatin1String("for")}, + {('f'), QLatin1String("foreach")}, + {('f'), QLatin1String("from")}, + {('g'), QLatin1String("get")}, + {('g'), QLatin1String("global")}, + {('g'), QLatin1String("goto")}, + {('g'), QLatin1String("group")}, + {('i'), QLatin1String("if")}, + {('i'), QLatin1String("implicit")}, + {('i'), QLatin1String("in")}, + {('i'), QLatin1String("interface")}, + {('i'), QLatin1String("internal")}, + {('i'), QLatin1String("into")}, + {('i'), QLatin1String("is")}, + {('j'), QLatin1String("join")}, + {('l'), QLatin1String("let")}, + {('l'), QLatin1String("lock")}, + {('l'), QLatin1String("long")}, + {('n'), QLatin1String("namespace")}, + {('n'), QLatin1String("new")}, + {('o'), QLatin1String("object")}, + {('o'), QLatin1String("operator")}, + {('o'), QLatin1String("orderby")}, + {('o'), QLatin1String("out")}, + {('o'), QLatin1String("override")}, + {('p'), QLatin1String("params")}, + {('p'), QLatin1String("partial")}, + {('p'), QLatin1String("private")}, + {('p'), QLatin1String("protected")}, + {('p'), QLatin1String("public")}, + {('r'), QLatin1String("readonly")}, + {('r'), QLatin1String("ref")}, + {('r'), QLatin1String("remove")}, + {('r'), QLatin1String("return")}, + {('s'), QLatin1String("sealed")}, + {('s'), QLatin1String("select")}, + {('s'), QLatin1String("set")}, + {('s'), QLatin1String("sizeof")}, + {('s'), QLatin1String("stackalloc")}, + {('s'), QLatin1String("static")}, + {('s'), QLatin1String("switch")}, + {('t'), QLatin1String("this")}, + {('t'), QLatin1String("throw")}, + {('t'), QLatin1String("try")}, + {('t'), QLatin1String("typeof")}, + {('u'), QLatin1String("unchecked")}, + {('u'), QLatin1String("unsafe")}, + {('u'), QLatin1String("using")}, + {('v'), QLatin1String("value")}, + {('v'), QLatin1String("virtual")}, + {('v'), QLatin1String("volatile")}, + {('w'), QLatin1String("where")}, + {('w'), QLatin1String("while")}, + {('y'), QLatin1String("yield")}}; + + csharp_types = { + {('b'), QLatin1String("bool")}, {('b'), QLatin1String("byte")}, + {('c'), QLatin1String("char")}, {('c'), QLatin1String("class")}, + {('d'), QLatin1String("double")}, {('e'), QLatin1String("enum")}, + {('f'), QLatin1String("float")}, {('i'), QLatin1String("int")}, + {('s'), QLatin1String("sbyte")}, {('s'), QLatin1String("short")}, + {('s'), QLatin1String("string")}, {('s'), QLatin1String("struct")}, + {('u'), QLatin1String("uint")}, {('u'), QLatin1String("ulong")}, + {('u'), QLatin1String("ushort")}, {('v'), QLatin1String("var")}, + {('v'), QLatin1String("void")}, + }; + + csharp_literals = {{('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}, + {('n'), QLatin1String("null")}}; + + csharp_builtin = { + + }; + + csharp_other = { + {('d'), QLatin1String("define")}, {('e'), QLatin1String("elif")}, + {('e'), QLatin1String("else")}, {('e'), QLatin1String("endif")}, + {('e'), QLatin1String("endregion")}, {('e'), QLatin1String("error")}, + {('i'), QLatin1String("if")}, {('l'), QLatin1String("line")}, + {('p'), QLatin1String("pragma")}, {('r'), QLatin1String("region")}, + {('u'), QLatin1String("undef")}, {('w'), QLatin1String("warning")}}; +} +void loadCSharpData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!csharpDataInitialized) { + initCSharpData(); + csharpDataInitialized = true; + } + types = csharp_types; + keywords = csharp_keywords; + builtin = csharp_builtin; + literals = csharp_literals; + other = csharp_other; +} + +/********************************************************/ +/*** Go DATA *************************************/ +/********************************************************/ +static bool goDataInitialized = false; +static QMultiHash go_keywords; +static QMultiHash go_types; +static QMultiHash go_literals; +static QMultiHash go_builtin; +static QMultiHash go_other; +void initGoData() { + go_keywords = { + {('b'), QLatin1String("break")}, + {('c'), QLatin1String("case")}, + {('c'), QLatin1String("chan")}, + {('c'), QLatin1String("const")}, + {('c'), QLatin1String("continue")}, + {('d'), QLatin1String("default")}, + {('d'), QLatin1String("defer")}, + {('e'), QLatin1String("else")}, + {('f'), QLatin1String("fallthrough")}, + {('f'), QLatin1String("for")}, + {('f'), QLatin1String("func")}, + {('g'), QLatin1String("go")}, + {('t'), QLatin1String("to")}, + {('i'), QLatin1String("if")}, + {('i'), QLatin1String("import")}, + {('i'), QLatin1String("interface")}, + {('p'), QLatin1String("package")}, + {('r'), QLatin1String("range")}, + {('r'), QLatin1String("return")}, + {('s'), QLatin1String("select")}, + {('s'), QLatin1String("struct")}, + {('s'), QLatin1String("switch")}, + {('t'), QLatin1String("type")}, + }; + + go_types = {{('m'), QLatin1String("map")}, + {('s'), QLatin1String("struct")}, + {('v'), QLatin1String("var")}, + {('b'), QLatin1String("bool")}, + {('b'), QLatin1String("byte")}, + {('c'), QLatin1String("complex64")}, + {('c'), QLatin1String("complex128")}, + {('f'), QLatin1String("float32")}, + {('f'), QLatin1String("float64")}, + {('i'), QLatin1String("int8")}, + {('i'), QLatin1String("int16")}, + {('i'), QLatin1String("int32")}, + {('i'), QLatin1String("int64")}, + {('s'), QLatin1String("string")}, + {('u'), QLatin1String("uint8")}, + {('u'), QLatin1String("uint16")}, + {('u'), QLatin1String("uint32")}, + {('u'), QLatin1String("uint64")}, + {('i'), QLatin1String("int")}, + {('u'), QLatin1String("uint")}, + {('u'), QLatin1String("uintptr")}, + {('r'), QLatin1String("rune")}}; + + go_literals = {{('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}, + {('n'), QLatin1String("nil")}, + {('i'), QLatin1String("iota")}}; + + go_builtin = { + {('a'), QLatin1String("append")}, {('c'), QLatin1String("cap")}, + {('c'), QLatin1String("close")}, {('c'), QLatin1String("complex")}, + {('c'), QLatin1String("copy")}, {('i'), QLatin1String("imag")}, + {('l'), QLatin1String("len")}, {('m'), QLatin1String("make")}, + {('n'), QLatin1String("new")}, {('p'), QLatin1String("panic")}, + {('p'), QLatin1String("print")}, {('p'), QLatin1String("println")}, + {('r'), QLatin1String("real")}, {('r'), QLatin1String("recover")}, + {('d'), QLatin1String("delete")}}; + + go_other = { + + }; +} +void loadGoData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!goDataInitialized) { + initGoData(); + goDataInitialized = true; + } + types = go_types; + keywords = go_keywords; + builtin = go_builtin; + literals = go_literals; + other = go_other; +} + +/********************************************************/ +/*** V DATA **************************************/ +/********************************************************/ +static bool vDataInitialized = false; +static QMultiHash v_keywords; +static QMultiHash v_types; +static QMultiHash v_literals; +static QMultiHash v_builtin; +static QMultiHash v_other; +void initVData() { + v_keywords = { + {('b'), QLatin1String("break")}, {('c'), QLatin1String("const")}, + {('c'), QLatin1String("continue")}, {('d'), QLatin1String("defer")}, + {('e'), QLatin1String("else")}, {('f'), QLatin1String("for")}, + {('f'), QLatin1String("fn")}, {('g'), QLatin1String("go")}, + {('g'), QLatin1String("goto")}, {('i'), QLatin1String("if")}, + {('i'), QLatin1String("import")}, {('i'), QLatin1String("interface")}, + {('r'), QLatin1String("return")}, {('s'), QLatin1String("struct")}, + {('s'), QLatin1String("switch")}, {('t'), QLatin1String("type")}, + {('p'), QLatin1String("pub")}, {('o'), QLatin1String("or")}, + {('n'), QLatin1String("none")}}; + + v_types = { + {('m'), QLatin1String("map")}, {('s'), QLatin1String("struct")}, + {('b'), QLatin1String("bool")}, {('b'), QLatin1String("byte")}, + {('f'), QLatin1String("f32")}, {('f'), QLatin1String("f64")}, + {('i'), QLatin1String("i8")}, {('i'), QLatin1String("i16")}, + {('i'), QLatin1String("int")}, {('i'), QLatin1String("i64")}, + {('i'), QLatin1String("i128")}, {('s'), QLatin1String("string")}, + {('u'), QLatin1String("u16")}, {('u'), QLatin1String("u32")}, + {('u'), QLatin1String("u64")}, {('u'), QLatin1String("u128")}, + {('u'), QLatin1String("byteptr")}, {('u'), QLatin1String("voidptr")}, + {('r'), QLatin1String("rune")}}; + + v_literals = { + {('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}, + }; + + v_builtin = {}; + + v_other = { + + }; +} +void loadVData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!vDataInitialized) { + initVData(); + vDataInitialized = true; + } + types = v_types; + keywords = v_keywords; + builtin = v_builtin; + literals = v_literals; + other = v_other; +} + +/********************************************************/ +/*** SQL DATA ************************************/ +/********************************************************/ +static bool sqlDataInitialized = false; +static QMultiHash sql_keywords; +static QMultiHash sql_types; +static QMultiHash sql_literals; +static QMultiHash sql_builtin; +static QMultiHash sql_other; +void initSQLData() { + sql_keywords = {{('A'), QLatin1String("ACTION")}, + {('A'), QLatin1String("ADD")}, + {('A'), QLatin1String("AFTER")}, + {('A'), QLatin1String("ALGORITHM")}, + {('A'), QLatin1String("ALL")}, + {('A'), QLatin1String("ALTER")}, + {('A'), QLatin1String("ANALYZE")}, + {('A'), QLatin1String("ANY")}, + {('A'), QLatin1String("APPLY")}, + {('A'), QLatin1String("AS")}, + {('A'), QLatin1String("ASC")}, + {('A'), QLatin1String("AUTHORIZATION")}, + {('A'), QLatin1String("AUTO_INCREMENT")}, + {('B'), QLatin1String("BACKUP")}, + {('B'), QLatin1String("BDB")}, + {('B'), QLatin1String("BEGIN")}, + {('B'), QLatin1String("BERKELEYDB")}, + {('B'), QLatin1String("BIGINT")}, + {('B'), QLatin1String("BINARY")}, + {('B'), QLatin1String("BIT")}, + {('B'), QLatin1String("BLOB")}, + {('B'), QLatin1String("BOOL")}, + {('B'), QLatin1String("BOOLEAN")}, + {('B'), QLatin1String("BREAK")}, + {('B'), QLatin1String("BROWSE")}, + {('B'), QLatin1String("BTREE")}, + {('B'), QLatin1String("BULK")}, + {('B'), QLatin1String("BY")}, + {('C'), QLatin1String("CALL")}, + {('C'), QLatin1String("CASCADED")}, + {('C'), QLatin1String("CASE")}, + {('C'), QLatin1String("CHAIN")}, + {('C'), QLatin1String("CHARACTER")}, + {('S'), QLatin1String("SET")}, + {('C'), QLatin1String("CHECKPOINT")}, + {('C'), QLatin1String("CLOSE")}, + {('C'), QLatin1String("CLUSTERED")}, + {('C'), QLatin1String("COALESCE")}, + {('C'), QLatin1String("COLLATE")}, + {('C'), QLatin1String("COLUMNS")}, + {('C'), QLatin1String("COMMENT")}, + {('C'), QLatin1String("COMMITTED")}, + {('C'), QLatin1String("COMPUTE")}, + {('C'), QLatin1String("CONNECT")}, + {('C'), QLatin1String("CONSISTENT")}, + {('C'), QLatin1String("CONSTRAINT")}, + {('C'), QLatin1String("CONTAINSTABLE")}, + {('C'), QLatin1String("CONTINUE")}, + {('C'), QLatin1String("CONVERT")}, + {('C'), QLatin1String("CREATE")}, + {('C'), QLatin1String("CROSS")}, + {('C'), QLatin1String("CURRENT_DATE")}, + {('_'), QLatin1String("_TIME")}, + {('_'), QLatin1String("_TIMESTAMP")}, + {('_'), QLatin1String("_USER")}, + {('C'), QLatin1String("CURSOR")}, + {('C'), QLatin1String("CYCLE")}, + {('D'), QLatin1String("DATABASES")}, + {('D'), QLatin1String("DATETIME")}, + {('D'), QLatin1String("DAY")}, + {('D'), QLatin1String("DBCC")}, + {('D'), QLatin1String("DEALLOCATE")}, + {('D'), QLatin1String("DEC")}, + {('D'), QLatin1String("DECIMAL")}, + {('D'), QLatin1String("DECLARE")}, + {('D'), QLatin1String("DEFAULT")}, + {('D'), QLatin1String("DEFINER")}, + {('D'), QLatin1String("DELAYED")}, + {('D'), QLatin1String("DELETE")}, + {('D'), QLatin1String("DELIMITERS")}, + {('D'), QLatin1String("DENY")}, + {('D'), QLatin1String("DESC")}, + {('D'), QLatin1String("DESCRIBE")}, + {('D'), QLatin1String("DETERMINISTIC")}, + {('D'), QLatin1String("DISABLE")}, + {('D'), QLatin1String("DISCARD")}, + {('D'), QLatin1String("DISK")}, + {('D'), QLatin1String("DISTINCT")}, + {('D'), QLatin1String("DISTINCTROW")}, + {('D'), QLatin1String("DISTRIBUTED")}, + {('D'), QLatin1String("DO")}, + {('D'), QLatin1String("DOUBLE")}, + {('D'), QLatin1String("DROP")}, + {('D'), QLatin1String("DUMMY")}, + {('D'), QLatin1String("DUMPFILE")}, + {('D'), QLatin1String("DUPLICATE")}, + {('E'), QLatin1String("ELSEIF")}, + {('E'), QLatin1String("ENABLE")}, + {('E'), QLatin1String("ENCLOSED")}, + {('E'), QLatin1String("END")}, + {('E'), QLatin1String("ENGINE")}, + {('E'), QLatin1String("ENUM")}, + {('E'), QLatin1String("ERRLVL")}, + {('E'), QLatin1String("ERRORS")}, + {('E'), QLatin1String("ESCAPED")}, + {('E'), QLatin1String("EXCEPT")}, + {('E'), QLatin1String("EXECUTE")}, + {('E'), QLatin1String("EXISTS")}, + {('E'), QLatin1String("EXIT")}, + {('E'), QLatin1String("EXPLAIN")}, + {('E'), QLatin1String("EXTENDED")}, + {('F'), QLatin1String("FETCH")}, + {('F'), QLatin1String("FIELDS")}, + {('F'), QLatin1String("FILE")}, + {('F'), QLatin1String("FILLFACTOR")}, + {('F'), QLatin1String("FIRST")}, + {('F'), QLatin1String("FIXED")}, + {('F'), QLatin1String("FLOAT")}, + {('F'), QLatin1String("FOLLOWING")}, + {('F'), QLatin1String("FOR")}, + {('E'), QLatin1String("EACH")}, + {('R'), QLatin1String("ROW")}, + {('F'), QLatin1String("FORCE")}, + {('F'), QLatin1String("FOREIGN")}, + {('F'), QLatin1String("FREETEXTTABLE")}, + {('F'), QLatin1String("FROM")}, + {('F'), QLatin1String("FULL")}, + {('F'), QLatin1String("FUNCTION")}, + {('G'), QLatin1String("GEOMETRYCOLLECTION")}, + {('G'), QLatin1String("GLOBAL")}, + {('G'), QLatin1String("GOTO")}, + {('G'), QLatin1String("GRANT")}, + {('G'), QLatin1String("GROUP")}, + {('H'), QLatin1String("HANDLER")}, + {('H'), QLatin1String("HASH")}, + {('H'), QLatin1String("HAVING")}, + {('H'), QLatin1String("HOLDLOCK")}, + {('H'), QLatin1String("HOUR")}, + {('I'), QLatin1String("IDENTITY_INSERT")}, + {('C'), QLatin1String("COL")}, + {('I'), QLatin1String("IF")}, + {('I'), QLatin1String("IGNORE")}, + {('I'), QLatin1String("IMPORT")}, + {('I'), QLatin1String("INDEX")}, + {('I'), QLatin1String("INFILE")}, + {('I'), QLatin1String("INNER")}, + {('I'), QLatin1String("INNODB")}, + {('I'), QLatin1String("INOUT")}, + {('I'), QLatin1String("INSERT")}, + {('I'), QLatin1String("INT")}, + {('I'), QLatin1String("INTEGER")}, + {('I'), QLatin1String("INTERSECT")}, + {('I'), QLatin1String("INTERVAL")}, + {('I'), QLatin1String("INTO")}, + {('I'), QLatin1String("INVOKER")}, + {('I'), QLatin1String("ISOLATION")}, + {('I'), QLatin1String("ITERATE")}, + {('J'), QLatin1String("JOIN")}, + {('K'), QLatin1String("KEYS")}, + {('K'), QLatin1String("KILL")}, + {('L'), QLatin1String("LANGUAGE")}, + {('L'), QLatin1String("LAST")}, + {('L'), QLatin1String("LEAVE")}, + {('L'), QLatin1String("LEFT")}, + {('L'), QLatin1String("LEVEL")}, + {('L'), QLatin1String("LIMIT")}, + {('L'), QLatin1String("LINENO")}, + {('L'), QLatin1String("LINES")}, + {('L'), QLatin1String("LINESTRING")}, + {('L'), QLatin1String("LOAD")}, + {('L'), QLatin1String("LOCAL")}, + {('L'), QLatin1String("LOCK")}, + {('L'), QLatin1String("LONGBLOB")}, + {('T'), QLatin1String("TEXT")}, + {('L'), QLatin1String("LOOP")}, + {('M'), QLatin1String("MATCHED")}, + {('M'), QLatin1String("MEDIUMBLOB")}, + {('I'), QLatin1String("INT")}, + {('T'), QLatin1String("TEXT")}, + {('M'), QLatin1String("MERGE")}, + {('M'), QLatin1String("MIDDLEINT")}, + {('M'), QLatin1String("MINUTE")}, + {('M'), QLatin1String("MODE")}, + {('M'), QLatin1String("MODIFIES")}, + {('M'), QLatin1String("MODIFY")}, + {('M'), QLatin1String("MONTH")}, + {('M'), QLatin1String("MULTILINESTRING")}, + {('P'), QLatin1String("POINT")}, + {('P'), QLatin1String("POLYGON")}, + {('N'), QLatin1String("NATIONAL")}, + {('N'), QLatin1String("NATURAL")}, + {('N'), QLatin1String("NCHAR")}, + {('N'), QLatin1String("NEXT")}, + {('N'), QLatin1String("NO")}, + {('N'), QLatin1String("NONCLUSTERED")}, + {('N'), QLatin1String("NULLIF")}, + {('N'), QLatin1String("NUMERIC")}, + {('O'), QLatin1String("OFF")}, + {('O'), QLatin1String("OFFSETS")}, + {('O'), QLatin1String("ON")}, + {('O'), QLatin1String("OPENDATASOURCE")}, + {('Q'), QLatin1String("QUERY")}, + {('R'), QLatin1String("ROWSET")}, + {('O'), QLatin1String("OPTIMIZE")}, + {('O'), QLatin1String("OPTIONALLY")}, + {('O'), QLatin1String("ORDER")}, + {('O'), QLatin1String("OUTER")}, + {('F'), QLatin1String("FILE")}, + {('O'), QLatin1String("OVER")}, + {('P'), QLatin1String("PARTIAL")}, + {('P'), QLatin1String("PARTITION")}, + {('P'), QLatin1String("PERCENT")}, + {('P'), QLatin1String("PIVOT")}, + {('P'), QLatin1String("PLAN")}, + {('P'), QLatin1String("POINT")}, + {('P'), QLatin1String("POLYGON")}, + {('P'), QLatin1String("PRECEDING")}, + {('P'), QLatin1String("PRECISION")}, + {('P'), QLatin1String("PREPARE")}, + {('P'), QLatin1String("PREV")}, + {('P'), QLatin1String("PRIMARY")}, + {('P'), QLatin1String("PRINT")}, + {('P'), QLatin1String("PRIVILEGES")}, + {('P'), QLatin1String("PROCEDURE")}, + {('P'), QLatin1String("PUBLIC")}, + {('P'), QLatin1String("PURGE")}, + {('Q'), QLatin1String("QUICK")}, + {('R'), QLatin1String("RAISERROR")}, + {('R'), QLatin1String("READS")}, + {('R'), QLatin1String("REAL")}, + {('R'), QLatin1String("RECONFIGURE")}, + {('R'), QLatin1String("REFERENCES")}, + {('R'), QLatin1String("RELEASE")}, + {('R'), QLatin1String("RENAME")}, + {('R'), QLatin1String("REPEATABLE")}, + {('R'), QLatin1String("REPLACE")}, + {('R'), QLatin1String("REPLICATION")}, + {('R'), QLatin1String("REQUIRE")}, + {('R'), QLatin1String("RESIGNAL")}, + {('R'), QLatin1String("RESTORE")}, + {('R'), QLatin1String("RESTRICT")}, + {('R'), QLatin1String("RETURNS")}, + {('R'), QLatin1String("REVOKE")}, + {('R'), QLatin1String("RIGHT")}, + {('R'), QLatin1String("ROLLBACK")}, + {('R'), QLatin1String("ROUTINE")}, + {('R'), QLatin1String("ROWCOUNT")}, + {('G'), QLatin1String("GUIDCOL")}, + {('R'), QLatin1String("RTREE")}, + {('R'), QLatin1String("RULE")}, + {('S'), QLatin1String("SAVEPOINT")}, + {('S'), QLatin1String("SCHEMA")}, + {('S'), QLatin1String("SECOND")}, + {('S'), QLatin1String("SELECT")}, + {('S'), QLatin1String("SERIALIZABLE")}, + {('S'), QLatin1String("SESSION_USER")}, + {('S'), QLatin1String("SETUSER")}, + {('S'), QLatin1String("SHARE")}, + {('S'), QLatin1String("SHOW")}, + {('S'), QLatin1String("SHUTDOWN")}, + {('S'), QLatin1String("SIMPLE")}, + {('S'), QLatin1String("SMALLINT")}, + {('S'), QLatin1String("SNAPSHOT")}, + {('S'), QLatin1String("SOME")}, + {('S'), QLatin1String("SONAME")}, + {('S'), QLatin1String("SQL")}, + {('S'), QLatin1String("STARTING")}, + {('S'), QLatin1String("STATISTICS")}, + {('S'), QLatin1String("STATUS")}, + {('S'), QLatin1String("STRIPED")}, + {('S'), QLatin1String("SYSTEM_USER")}, + {('T'), QLatin1String("TABLES")}, + {('T'), QLatin1String("TABLESPACE")}, + {('T'), QLatin1String("TEMPORARY")}, + {('T'), QLatin1String("TABLE")}, + {('T'), QLatin1String("TERMINATED")}, + {('T'), QLatin1String("TEXTSIZE")}, + {('T'), QLatin1String("THEN")}, + {('T'), QLatin1String("TIMESTAMP")}, + {('T'), QLatin1String("TINYBLOB")}, + {('I'), QLatin1String("INT")}, + {('T'), QLatin1String("TEXT")}, + {('T'), QLatin1String("TOP")}, + {('T'), QLatin1String("TRANSACTIONS")}, + {('T'), QLatin1String("TRIGGER")}, + {('T'), QLatin1String("TRUNCATE")}, + {('T'), QLatin1String("TSEQUAL")}, + {('T'), QLatin1String("TYPES")}, + {('U'), QLatin1String("UNBOUNDED")}, + {('U'), QLatin1String("UNCOMMITTED")}, + {('U'), QLatin1String("UNDEFINED")}, + {('U'), QLatin1String("UNION")}, + {('U'), QLatin1String("UNIQUE")}, + {('U'), QLatin1String("UNLOCK")}, + {('U'), QLatin1String("UNPIVOT")}, + {('U'), QLatin1String("UNSIGNED")}, + {('U'), QLatin1String("UPDATETEXT")}, + {('U'), QLatin1String("USAGE")}, + {('U'), QLatin1String("USE")}, + {('U'), QLatin1String("USER")}, + {('U'), QLatin1String("USING")}, + {('V'), QLatin1String("VALUES")}, + {('V'), QLatin1String("VARBINARY")}, + {('C'), QLatin1String("CHAR")}, + {('C'), QLatin1String("CHARACTER")}, + {('Y'), QLatin1String("YING")}, + {('V'), QLatin1String("VIEW")}, + {('W'), QLatin1String("WAITFOR")}, + {('W'), QLatin1String("WARNINGS")}, + {('W'), QLatin1String("WHEN")}, + {('W'), QLatin1String("WHERE")}, + {('W'), QLatin1String("WHILE")}, + {('W'), QLatin1String("WITH")}, + {('R'), QLatin1String("ROLLUP")}, + {('I'), QLatin1String("IN")}, + {('W'), QLatin1String("WORK")}, + {('W'), QLatin1String("WRITETEXT")}, + {('Y'), QLatin1String("YEAR")}}; + + sql_types = { + + }; + + sql_literals = { + {('A'), QLatin1String("TRUE")}, + {('F'), QLatin1String("FALSE")}, + {('N'), QLatin1String("NULL")}, + }; + + sql_builtin = { + {('A'), QLatin1String("AVG")}, {('C'), QLatin1String("COUNT")}, + {('F'), QLatin1String("FIRST")}, {('F'), QLatin1String("FORMAT")}, + {('L'), QLatin1String("LAST")}, {('L'), QLatin1String("LCASE")}, + {('L'), QLatin1String("LEN")}, {('M'), QLatin1String("MAX")}, + {('M'), QLatin1String("MID")}, {('M'), QLatin1String("MIN")}, + {('M'), QLatin1String("MOD")}, {('N'), QLatin1String("NOW")}, + {('R'), QLatin1String("ROUND")}, {('S'), QLatin1String("SUM")}, + {('U'), QLatin1String("UCASE")}}; + + sql_other = { + + }; +} +void loadSQLData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!sqlDataInitialized) { + initSQLData(); + sqlDataInitialized = true; + } + types = sql_types; + keywords = sql_keywords; + builtin = sql_builtin; + literals = sql_literals; + other = sql_other; +} + +/********************************************************/ +/*** JSON DATA ***********************************/ +/********************************************************/ +static bool jsonDataInitialized = false; +static QMultiHash json_keywords; +static QMultiHash json_types; +static QMultiHash json_literals; +static QMultiHash json_builtin; +static QMultiHash json_other; +void initJSONData() { + json_keywords = {}; + + json_types = {}; + + json_literals = {{('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}, + {('n'), QLatin1String("null")}}; + + json_builtin = {}; + + json_other = {}; +} +void loadJSONData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!jsonDataInitialized) { + initJSONData(); + jsonDataInitialized = true; + } + types = json_types; + keywords = json_keywords; + builtin = json_builtin; + literals = json_literals; + other = json_other; +} + +/********************************************************/ +/*** CSS DATA ***********************************/ +/********************************************************/ +static bool cssDataInitialized = false; +static QMultiHash css_keywords; +static QMultiHash css_types; +static QMultiHash css_literals; +static QMultiHash css_builtin; +static QMultiHash css_other; +void initCSSData() { + css_keywords = {{'i', QLatin1String("important")}, + {'p', QLatin1String("px")}, + {'e', QLatin1String("em")}}; + + css_types = {{'a', QLatin1String("align")}, + {'c', QLatin1String("content")}, + {'i', QLatin1String("items")}, + {'s', QLatin1String("self")}, + {'a', QLatin1String("all")}, + {'a', QLatin1String("animation")}, + {'d', QLatin1String("delay")}, + {'d', QLatin1String("direction")}, + {'d', QLatin1String("duration")}, + {'f', QLatin1String("fill")}, + {'m', QLatin1String("mode")}, + {'i', QLatin1String("iteration")}, + {'c', QLatin1String("count")}, + {'n', QLatin1String("name")}, + {'p', QLatin1String("play")}, + {'s', QLatin1String("state")}, + {'t', QLatin1String("timing")}, + {'f', QLatin1String("function")}, + {'a', QLatin1String("azimuth")}, + {'b', QLatin1String("backface")}, + {'v', QLatin1String("visibility")}, + {'a', QLatin1String("attachment")}, + {'b', QLatin1String("blend")}, + {'m', QLatin1String("mode")}, + {'c', QLatin1String("clip")}, + {'c', QLatin1String("color")}, + {'i', QLatin1String("image")}, + {'o', QLatin1String("origin")}, + {'p', QLatin1String("position")}, + {'r', QLatin1String("repeat")}, + {'s', QLatin1String("size")}, + {'b', QLatin1String("background")}, + {'b', QLatin1String("bleed")}, + {'c', QLatin1String("color")}, + {'r', QLatin1String("radius")}, + {'r', QLatin1String("radius")}, + {'s', QLatin1String("style")}, + {'w', QLatin1String("width")}, + {'b', QLatin1String("bottom")}, + {'c', QLatin1String("collapse")}, + {'c', QLatin1String("color")}, + {'i', QLatin1String("image")}, + {'o', QLatin1String("outset")}, + {'r', QLatin1String("repeat")}, + {'s', QLatin1String("source")}, + {'s', QLatin1String("slice")}, + {'w', QLatin1String("width")}, + {'c', QLatin1String("color")}, + {'s', QLatin1String("style")}, + {'w', QLatin1String("width")}, + {'l', QLatin1String("left")}, + {'r', QLatin1String("radius")}, + {'c', QLatin1String("color")}, + {'s', QLatin1String("style")}, + {'w', QLatin1String("width")}, + {'r', QLatin1String("right")}, + {'s', QLatin1String("spacing")}, + {'s', QLatin1String("style")}, + {'c', QLatin1String("color")}, + {'l', QLatin1String("left")}, + {'r', QLatin1String("radius")}, + {'r', QLatin1String("radius")}, + {'s', QLatin1String("style")}, + {'w', QLatin1String("width")}, + {'t', QLatin1String("top")}, + {'w', QLatin1String("width")}, + {'b', QLatin1String("border")}, + {'b', QLatin1String("bottom")}, + {'b', QLatin1String("break")}, + {'b', QLatin1String("box")}, + {'s', QLatin1String("shadow")}, + {'b', QLatin1String("box")}, + {'s', QLatin1String("sizing")}, + {'a', QLatin1String("after")}, + {'b', QLatin1String("before")}, + {'b', QLatin1String("break")}, + {'i', QLatin1String("inside")}, + {'c', QLatin1String("caption")}, + {'s', QLatin1String("side")}, + {'c', QLatin1String("caret")}, + {'c', QLatin1String("color")}, + {'c', QLatin1String("clear")}, + {'c', QLatin1String("clip")}, + {'c', QLatin1String("color")}, + {'c', QLatin1String("columns")}, + {'c', QLatin1String("column")}, + {'c', QLatin1String("count")}, + {'f', QLatin1String("fill")}, + {'g', QLatin1String("gap")}, + {'r', QLatin1String("rule")}, + {'c', QLatin1String("color")}, + {'s', QLatin1String("style")}, + {'w', QLatin1String("width")}, + {'s', QLatin1String("span")}, + {'w', QLatin1String("width")}, + {'c', QLatin1String("content")}, + {'i', QLatin1String("increment")}, + {'c', QLatin1String("counter")}, + {'r', QLatin1String("reset")}, + {'a', QLatin1String("after")}, + {'b', QLatin1String("before")}, + {'c', QLatin1String("cue")}, + {'c', QLatin1String("cursor")}, + {'d', QLatin1String("direction")}, + {'d', QLatin1String("display")}, + {'e', QLatin1String("elevation")}, + {'e', QLatin1String("empty")}, + {'c', QLatin1String("cells")}, + {'f', QLatin1String("filter")}, + {'f', QLatin1String("flex")}, + {'b', QLatin1String("basis")}, + {'d', QLatin1String("direction")}, + {'f', QLatin1String("feature")}, + {'s', QLatin1String("settings")}, + {'f', QLatin1String("flex")}, + {'f', QLatin1String("flow")}, + {'g', QLatin1String("grow")}, + {'s', QLatin1String("shrink")}, + {'w', QLatin1String("wrap")}, + {'f', QLatin1String("float")}, + {'f', QLatin1String("family")}, + {'k', QLatin1String("kerning")}, + {'l', QLatin1String("language")}, + {'o', QLatin1String("override")}, + {'a', QLatin1String("adjust")}, + {'s', QLatin1String("size")}, + {'s', QLatin1String("stretch")}, + {'s', QLatin1String("style")}, + {'s', QLatin1String("synthesis")}, + {'v', QLatin1String("variant")}, + {'a', QLatin1String("alternates")}, + {'c', QLatin1String("caps")}, + {'e', QLatin1String("east")}, + {'a', QLatin1String("asian")}, + {'l', QLatin1String("ligatures")}, + {'n', QLatin1String("numeric")}, + {'p', QLatin1String("position")}, + {'w', QLatin1String("weight")}, + {'f', QLatin1String("font")}, + {'a', QLatin1String("area")}, + {'a', QLatin1String("auto")}, + {'c', QLatin1String("columns")}, + {'f', QLatin1String("flow")}, + {'r', QLatin1String("rows")}, + {'e', QLatin1String("end")}, + {'g', QLatin1String("gap")}, + {'s', QLatin1String("start")}, + {'c', QLatin1String("column")}, + {'g', QLatin1String("gap")}, + {'e', QLatin1String("end")}, + {'g', QLatin1String("gap")}, + {'s', QLatin1String("start")}, + {'r', QLatin1String("row")}, + {'a', QLatin1String("areas")}, + {'c', QLatin1String("columns")}, + {'r', QLatin1String("rows")}, + {'t', QLatin1String("template")}, + {'g', QLatin1String("grid")}, + {'h', QLatin1String("hanging")}, + {'p', QLatin1String("punctuation")}, + {'h', QLatin1String("height")}, + {'h', QLatin1String("hyphens")}, + {'i', QLatin1String("isolation")}, + {'j', QLatin1String("justify")}, + {'c', QLatin1String("content")}, + {'i', QLatin1String("items")}, + {'s', QLatin1String("self")}, + {'l', QLatin1String("leftimage")}, + {'l', QLatin1String("letter")}, + {'s', QLatin1String("spacing")}, + {'b', QLatin1String("break")}, + {'l', QLatin1String("line")}, + {'s', QLatin1String("style")}, + {'i', QLatin1String("image")}, + {'s', QLatin1String("style")}, + {'p', QLatin1String("position")}, + {'t', QLatin1String("type")}, + {'l', QLatin1String("list")}, + {'s', QLatin1String("style")}, + {'b', QLatin1String("bottom")}, + {'l', QLatin1String("left")}, + {'r', QLatin1String("right")}, + {'t', QLatin1String("top")}, + {'m', QLatin1String("margin")}, + {'m', QLatin1String("marker")}, + {'o', QLatin1String("offset")}, + {'m', QLatin1String("marks")}, + {'m', QLatin1String("max")}, + {'h', QLatin1String("height")}, + {'w', QLatin1String("width")}, + {'m', QLatin1String("min")}, + {'m', QLatin1String("mix")}, + {'b', QLatin1String("blend")}, + {'m', QLatin1String("mode")}, + {'n', QLatin1String("nav")}, + {'u', QLatin1String("up")}, + {'d', QLatin1String("down")}, + {'l', QLatin1String("left")}, + {'r', QLatin1String("right")}, + {'o', QLatin1String("opacity")}, + {'o', QLatin1String("order")}, + {'o', QLatin1String("orphans")}, + {'c', QLatin1String("color")}, + {'o', QLatin1String("offset")}, + {'s', QLatin1String("style")}, + {'w', QLatin1String("width")}, + {'o', QLatin1String("outline")}, + {'w', QLatin1String("wrap")}, + {'o', QLatin1String("overflow")}, + {'b', QLatin1String("bottom")}, + {'l', QLatin1String("left")}, + {'r', QLatin1String("right")}, + {'t', QLatin1String("top")}, + {'p', QLatin1String("padding")}, + {'b', QLatin1String("break")}, + {'a', QLatin1String("after")}, + {'b', QLatin1String("before")}, + {'i', QLatin1String("inside")}, + {'p', QLatin1String("page")}, + {'a', QLatin1String("after")}, + {'b', QLatin1String("before")}, + {'p', QLatin1String("pause")}, + {'p', QLatin1String("perspective")}, + {'o', QLatin1String("origin")}, + {'r', QLatin1String("range")}, + {'p', QLatin1String("pitch")}, + {'c', QLatin1String("content")}, + {'i', QLatin1String("items")}, + {'p', QLatin1String("place")}, + {'s', QLatin1String("self")}, + {'p', QLatin1String("play")}, + {'d', QLatin1String("during")}, + {'p', QLatin1String("position")}, + {'q', QLatin1String("quotes")}, + {'r', QLatin1String("resize")}, + {'r', QLatin1String("rest")}, + {'a', QLatin1String("after")}, + {'b', QLatin1String("before")}, + {'r', QLatin1String("rest")}, + {'r', QLatin1String("richness")}, + {'r', QLatin1String("right")}, + {'s', QLatin1String("size")}, + {'h', QLatin1String("header")}, + {'n', QLatin1String("numeral")}, + {'s', QLatin1String("speak")}, + {'p', QLatin1String("punctuation")}, + {'s', QLatin1String("speak")}, + {'s', QLatin1String("speech")}, + {'r', QLatin1String("rate")}, + {'s', QLatin1String("stress")}, + {'t', QLatin1String("tab")}, + {'s', QLatin1String("size")}, + {'t', QLatin1String("table")}, + {'l', QLatin1String("layout")}, + {'t', QLatin1String("text")}, + {'a', QLatin1String("align")}, + {'l', QLatin1String("last")}, + {'d', QLatin1String("decoration")}, + {'c', QLatin1String("color")}, + {'l', QLatin1String("line")}, + {'s', QLatin1String("skip")}, + {'s', QLatin1String("style")}, + {'i', QLatin1String("indent")}, + {'o', QLatin1String("overflow")}, + {'s', QLatin1String("shadow")}, + {'t', QLatin1String("transform")}, + {'u', QLatin1String("underline")}, + {'p', QLatin1String("position")}, + {'t', QLatin1String("top")}, + {'t', QLatin1String("transform")}, + {'o', QLatin1String("origin")}, + {'s', QLatin1String("style")}, + {'t', QLatin1String("transition")}, + {'d', QLatin1String("delay")}, + {'d', QLatin1String("duration")}, + {'p', QLatin1String("property")}, + {'t', QLatin1String("timing")}, + {'f', QLatin1String("function")}, + {'u', QLatin1String("unicode")}, + {'b', QLatin1String("bidi")}, + {'v', QLatin1String("vertical")}, + {'a', QLatin1String("align")}, + {'v', QLatin1String("visibility")}, + {'b', QLatin1String("balance")}, + {'d', QLatin1String("duration")}, + {'f', QLatin1String("family")}, + {'p', QLatin1String("pitch")}, + {'r', QLatin1String("range")}, + {'r', QLatin1String("rate")}, + {'s', QLatin1String("stress")}, + {'v', QLatin1String("volume")}, + {'v', QLatin1String("voice")}, + {'v', QLatin1String("volume")}, + {'w', QLatin1String("white")}, + {'s', QLatin1String("space")}, + {'w', QLatin1String("widows")}, + {'w', QLatin1String("width")}, + {'w', QLatin1String("will")}, + {'c', QLatin1String("change")}, + {'w', QLatin1String("word")}, + {'b', QLatin1String("break")}, + {'s', QLatin1String("spacing")}, + {'w', QLatin1String("wrap")}, + {'x', QLatin1String("x")}, + {'y', QLatin1String("y")}, + {'z', QLatin1String("z")}, + {'i', QLatin1String("index")}, + {'r', QLatin1String("rgb")}, + {'s', QLatin1String("sans")}, + {'s', QLatin1String("serif")}, + {'n', QLatin1String("normal")}}; + + css_literals = {}; + + css_builtin = {}; + + css_other = {}; +} +void loadCSSData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!cssDataInitialized) { + initCSSData(); + cssDataInitialized = true; + } + types = css_types; + keywords = css_keywords; + builtin = css_builtin; + literals = css_literals; + other = css_other; +} + +/********************************************************/ +/*** Typescript DATA *********************************/ +/********************************************************/ +static bool typescriptDataInitialized = false; +static QMultiHash typescript_keywords; +static QMultiHash typescript_types; +static QMultiHash typescript_literals; +static QMultiHash typescript_builtin; +static QMultiHash typescript_other; +void initTypescriptData() { + typescript_keywords = { + {'i', QLatin1String("in")}, {'i', QLatin1String("if")}, + {'f', QLatin1String("for")}, {'w', QLatin1String("while")}, + {'f', QLatin1String("finally")}, {'n', QLatin1String("new")}, + {'f', QLatin1String("function")}, {'d', QLatin1String("do")}, + {'r', QLatin1String("return")}, {'v', QLatin1String("void")}, + {'e', QLatin1String("else")}, {'b', QLatin1String("break")}, + {'c', QLatin1String("catch")}, {'i', QLatin1String("instanceof")}, + {'w', QLatin1String("with")}, {'t', QLatin1String("throw")}, + {'c', QLatin1String("case")}, {'d', QLatin1String("default")}, + {'t', QLatin1String("try")}, {'t', QLatin1String("this")}, + {'s', QLatin1String("switch")}, {'c', QLatin1String("continue")}, + {'t', QLatin1String("typeof")}, {'d', QLatin1String("delete")}, + {'l', QLatin1String("let")}, {'y', QLatin1String("yield")}, + {'c', QLatin1String("const")}, {'p', QLatin1String("public")}, + {'p', QLatin1String("private")}, {'p', QLatin1String("protected")}, + {'g', QLatin1String("get")}, {'s', QLatin1String("set")}, + {'s', QLatin1String("super")}, {'s', QLatin1String("static")}, + {'i', QLatin1String("implements")}, {'e', QLatin1String("export")}, + {'i', QLatin1String("import")}, {'d', QLatin1String("declare")}, + {'t', QLatin1String("type")}, {'n', QLatin1String("namespace")}, + {'a', QLatin1String("abstract")}, {'a', QLatin1String("as")}, + {'f', QLatin1String("from")}, {'e', QLatin1String("extends")}, + {'a', QLatin1String("async")}, {'a', QLatin1String("await")}}; + + typescript_types = {{'v', QLatin1String("var")}, + {'c', QLatin1String("class")}, + {'e', QLatin1String("enum")}}; + + typescript_literals = { + {('f'), QLatin1String("false")}, {('n'), QLatin1String("null")}, + {('t'), QLatin1String("true")}, {('u'), QLatin1String("undefined")}, + {('N'), QLatin1String("NaN")}, {('I'), QLatin1String("Infinity")}}; + + typescript_builtin = {{'e', QLatin1String("eval")}, + {'i', QLatin1String("isFinite")}, + {'i', QLatin1String("isNaN")}, + {'p', QLatin1String("parseFloat")}, + {'p', QLatin1String("parseInt")}, + {'d', QLatin1String("decodeURI")}, + {'d', QLatin1String("decodeURIComponent")}, + {'e', QLatin1String("encodeURI")}, + {'e', QLatin1String("encodeURIComponent")}, + {'e', QLatin1String("escape")}, + {'u', QLatin1String("unescape")}, + {'O', QLatin1String("Object")}, + {'F', QLatin1String("Function")}, + {'B', QLatin1String("Boolean")}, + {'E', QLatin1String("Error")}, + {'E', QLatin1String("EvalError")}, + {'I', QLatin1String("InternalError")}, + {'R', QLatin1String("RangeError")}, + {'R', QLatin1String("ReferenceError")}, + {'S', QLatin1String("StopIteration")}, + {'S', QLatin1String("SyntaxError")}, + {'T', QLatin1String("TypeError")}, + {'U', QLatin1String("URIError")}, + {'N', QLatin1String("Number")}, + {'M', QLatin1String("Math")}, + {'D', QLatin1String("Date")}, + {'S', QLatin1String("String")}, + {'R', QLatin1String("RegExp")}, + {'A', QLatin1String("Array")}, + {'F', QLatin1String("Float32Array")}, + {'F', QLatin1String("Float64Array")}, + {'I', QLatin1String("Int16Array")}, + {'I', QLatin1String("Int32Array")}, + {'I', QLatin1String("Int8Array")}, + {'U', QLatin1String("Uint16Array")}, + {'U', QLatin1String("Uint32Array")}, + {'U', QLatin1String("Uint8Array")}, + {'U', QLatin1String("Uint8ClampedArray")}, + {'A', QLatin1String("ArrayBuffer")}, + {'D', QLatin1String("DataView")}, + {'J', QLatin1String("JSON")}, + {'I', QLatin1String("Intl")}, + {'a', QLatin1String("arguments")}, + {'r', QLatin1String("require")}, + {'m', QLatin1String("module")}, + {'c', QLatin1String("console")}, + {'w', QLatin1String("window")}, + {'d', QLatin1String("document")}, + {'a', QLatin1String("any")}, + {'n', QLatin1String("number")}, + {'b', QLatin1String("boolean")}, + {'s', QLatin1String("string")}, + {'v', QLatin1String("void")}, + {'P', QLatin1String("Promise")}}; + + typescript_other = {}; +} +void loadTypescriptData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!typescriptDataInitialized) { + initTypescriptData(); + typescriptDataInitialized = true; + } + types = typescript_types; + keywords = typescript_keywords; + builtin = typescript_builtin; + literals = typescript_literals; + other = typescript_other; +} + +/********************************************************/ +/*** YAML DATA ***************************************/ +/********************************************************/ +static bool YAMLDataInitialized = false; +static QMultiHash YAML_keywords; +static QMultiHash YAML_types; +static QMultiHash YAML_literals; +static QMultiHash YAML_builtin; +static QMultiHash YAML_other; +void initYAMLData() { + YAML_keywords = {}; + YAML_types = {}; + YAML_literals = { + {('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}, + {('n'), QLatin1String("null")}, + }; + + YAML_builtin = {}; + YAML_other = {}; +} +void loadYAMLData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!YAMLDataInitialized) { + initYAMLData(); + YAMLDataInitialized = true; + } + types = YAML_types; + keywords = YAML_keywords; + builtin = YAML_builtin; + literals = YAML_literals; + other = YAML_other; +} + +/********************************************************/ +/*** VEX DATA ***************************************/ +/********************************************************/ +static bool vexDataInitialized = false; +static QMultiHash vex_keywords; +static QMultiHash vex_types; +static QMultiHash vex_literals; +static QMultiHash vex_builtin; +static QMultiHash vex_other; +void initVEXData() { + vex_keywords = { + {'b', QLatin1String("break")}, {'c', QLatin1String("continue")}, + {'d', QLatin1String("do")}, {'e', QLatin1String("else")}, + {'f', QLatin1String("for")}, {'f', QLatin1String("foreach")}, + {'f', QLatin1String("forpoints")}, {'f', QLatin1String("function")}, + {'g', QLatin1String("gather")}, {'i', QLatin1String("if")}, + {'i', QLatin1String("illuminance")}, {'r', QLatin1String("return")}, + {'w', QLatin1String("while")}}; + vex_types = { + {'b', QLatin1String("bsdf")}, {'c', QLatin1String("char")}, + {'c', QLatin1String("color")}, {'f', QLatin1String("float")}, + {'i', QLatin1String("int")}, {'i', QLatin1String("integer")}, + {'m', QLatin1String("matrix")}, {'m', QLatin1String("matrix2")}, + {'m', QLatin1String("matrix3")}, {'m', QLatin1String("matrix4")}, + {'n', QLatin1String("normal")}, {'p', QLatin1String("point")}, + {'s', QLatin1String("string")}, {'s', QLatin1String("struct")}, + {'t', QLatin1String("typedef")}, {'u', QLatin1String("union")}, + {'v', QLatin1String("vector")}, {'v', QLatin1String("vector2")}, + {'v', QLatin1String("vector4")}, {'v', QLatin1String("void")}, + }; + vex_literals = { + {('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}, + {('n'), QLatin1String("null")}, + }; + + vex_builtin = {{'D', QLatin1String("Du")}, + {'D', QLatin1String("Dv")}, + {'D', QLatin1String("Dw")}, + {'a', QLatin1String("abs")}, + {'a', QLatin1String("accessframe")}, + {'a', QLatin1String("acos")}, + {'a', QLatin1String("addattrib")}, + {'a', QLatin1String("addattribute")}, + {'a', QLatin1String("adddetailattrib")}, + {'a', QLatin1String("addgroup")}, + {'a', QLatin1String("addpoint")}, + {'a', QLatin1String("addpointattrib")}, + {'a', QLatin1String("addprim")}, + {'a', QLatin1String("addprimattrib")}, + {'a', QLatin1String("addvariablename")}, + {'a', QLatin1String("addvertex")}, + {'a', QLatin1String("addvertexattrib")}, + {'a', QLatin1String("addvisualizer")}, + {'a', QLatin1String("agentaddclip")}, + {'a', QLatin1String("agentclipcatalog")}, + {'a', QLatin1String("agentclipchannel")}, + {'a', QLatin1String("agentcliplength")}, + {'a', QLatin1String("agentclipnames")}, + {'a', QLatin1String("agentclipsample")}, + {'a', QLatin1String("agentclipsamplelocal")}, + {'a', QLatin1String("agentclipsamplerate")}, + {'a', QLatin1String("agentclipsampleworld")}, + {'a', QLatin1String("agentcliptimes")}, + {'a', QLatin1String("agentclipweights")}, + {'a', QLatin1String("agentcollisionlayer")}, + {'a', QLatin1String("agentcurrentlayer")}, + {'a', QLatin1String("agentlayerbindings")}, + {'a', QLatin1String("agentlayers")}, + {'a', QLatin1String("agentlayershapes")}, + {'a', QLatin1String("agentlocaltransform")}, + {'a', QLatin1String("agentlocaltransforms")}, + {'a', QLatin1String("agentrigchildren")}, + {'a', QLatin1String("agentrigfind")}, + {'a', QLatin1String("agentrigparent")}, + {'a', QLatin1String("agenttransformcount")}, + {'a', QLatin1String("agenttransformnames")}, + {'a', QLatin1String("agenttransformtolocal")}, + {'a', QLatin1String("agenttransformtoworld")}, + {'a', QLatin1String("agentworldtransform")}, + {'a', QLatin1String("agentworldtransforms")}, + {'a', QLatin1String("albedo")}, + {'a', QLatin1String("alphaname")}, + {'a', QLatin1String("ambient")}, + {'a', QLatin1String("anoise")}, + {'a', QLatin1String("append")}, + {'a', QLatin1String("area")}, + {'a', QLatin1String("argsort")}, + {'a', QLatin1String("array")}, + {'a', QLatin1String("ashikhmin")}, + {'a', QLatin1String("asin")}, + {'a', QLatin1String("assert_enabled")}, + {'a', QLatin1String("assign")}, + {'a', QLatin1String("atan")}, + {'a', QLatin1String("atan2")}, + {'a', QLatin1String("atof")}, + {'a', QLatin1String("atoi")}, + {'a', QLatin1String("atten")}, + {'a', QLatin1String("attrib")}, + {'a', QLatin1String("attribclass")}, + {'a', QLatin1String("attribsize")}, + {'a', QLatin1String("attribtype")}, + {'a', QLatin1String("attribtypeinfo")}, + {'a', QLatin1String("avg")}, + {'b', QLatin1String("binput")}, + {'b', QLatin1String("blackbody")}, + {'b', QLatin1String("blinn")}, + {'b', QLatin1String("blinnBRDF")}, + {'b', QLatin1String("bouncelabel")}, + {'b', QLatin1String("bouncemask")}, + {'b', QLatin1String("bumpmap")}, + {'b', QLatin1String("bumpmapA")}, + {'b', QLatin1String("bumpmapB")}, + {'b', QLatin1String("bumpmapG")}, + {'b', QLatin1String("bumpmapL")}, + {'b', QLatin1String("bumpmapR")}, + {'b', QLatin1String("bumpname")}, + {'c', QLatin1String("cbrt")}, + {'c', QLatin1String("ceil")}, + {'c', QLatin1String("ch")}, + {'c', QLatin1String("ch3")}, + {'c', QLatin1String("ch4")}, + {'c', QLatin1String("chend")}, + {'c', QLatin1String("chendf")}, + {'c', QLatin1String("chendt")}, + {'c', QLatin1String("chf")}, + {'c', QLatin1String("chi")}, + {'c', QLatin1String("chinput")}, + {'c', QLatin1String("chname")}, + {'c', QLatin1String("chnumchan")}, + {'c', QLatin1String("chp")}, + {'c', QLatin1String("chr")}, + {'c', QLatin1String("chramp")}, + {'c', QLatin1String("chrate")}, + {'c', QLatin1String("chs")}, + {'c', QLatin1String("chsraw")}, + {'c', QLatin1String("chstart")}, + {'c', QLatin1String("chstartf")}, + {'c', QLatin1String("chstartt")}, + {'c', QLatin1String("chv")}, + {'c', QLatin1String("cinput")}, + {'c', QLatin1String("ckspline")}, + {'c', QLatin1String("clamp")}, + {'c', QLatin1String("clip")}, + {'c', QLatin1String("colormap")}, + {'c', QLatin1String("colorname")}, + {'c', QLatin1String("computenormal")}, + {'c', QLatin1String("concat")}, + {'c', QLatin1String("cone")}, + {'c', QLatin1String("cos")}, + {'c', QLatin1String("cosh")}, + {'c', QLatin1String("cracktransform")}, + {'c', QLatin1String("cross")}, + {'c', QLatin1String("cspline")}, + {'c', QLatin1String("ctransform")}, + {'c', QLatin1String("curlnoise")}, + {'c', QLatin1String("curlnoise2d")}, + {'c', QLatin1String("curlxnoise")}, + {'c', QLatin1String("curlxnoise2d")}, + {'c', QLatin1String("cvex_bsdf")}, + {'d', QLatin1String("degrees")}, + {'d', QLatin1String("depthmap")}, + {'d', QLatin1String("depthname")}, + {'d', QLatin1String("detail")}, + {'d', QLatin1String("detailattrib")}, + {'d', QLatin1String("detailattribsize")}, + {'d', QLatin1String("detailattribtype")}, + {'d', QLatin1String("detailattribtypeinfo")}, + {'d', QLatin1String("detailintrinsic")}, + {'d', QLatin1String("determinant")}, + {'d', QLatin1String("diffuse")}, + {'d', QLatin1String("diffuseBRDF")}, + {'d', QLatin1String("dihedral")}, + {'d', QLatin1String("dimport")}, + {'d', QLatin1String("distance")}, + {'d', QLatin1String("distance2")}, + {'d', QLatin1String("dot")}, + {'d', QLatin1String("dsmpixel")}, + {'e', QLatin1String("eigenvalues")}, + {'e', QLatin1String("endswith")}, + {'e', QLatin1String("environment")}, + {'e', QLatin1String("erf")}, + {'e', QLatin1String("erf_inv")}, + {'e', QLatin1String("erfc")}, + {'e', QLatin1String("error")}, + {'e', QLatin1String("eulertoquaternion")}, + {'e', QLatin1String("eval_bsdf")}, + {'e', QLatin1String("exp")}, + {'e', QLatin1String("expand_udim")}, + {'e', QLatin1String("expandpointgroup")}, + {'e', QLatin1String("expandprimgroup")}, + {'f', QLatin1String("fastshadow")}, + {'f', QLatin1String("filamentsample")}, + {'f', QLatin1String("file_stat")}, + {'f', QLatin1String("filtershadow")}, + {'f', QLatin1String("filterstep")}, + {'f', QLatin1String("find")}, + {'f', QLatin1String("findattribval")}, + {'f', QLatin1String("findattribvalcount")}, + {'f', QLatin1String("finput")}, + {'f', QLatin1String("fit")}, + {'f', QLatin1String("fit01")}, + {'f', QLatin1String("fit10")}, + {'f', QLatin1String("fit11")}, + {'f', QLatin1String("floor")}, + {'f', QLatin1String("flownoise")}, + {'f', QLatin1String("flowpnoise")}, + {'f', QLatin1String("frac")}, + {'f', QLatin1String("fresnel")}, + {'f', QLatin1String("fromNDC")}, + {'f', QLatin1String("frontface")}, + {'f', QLatin1String("fuzzify")}, + {'f', QLatin1String("fuzzy_and")}, + {'f', QLatin1String("fuzzy_defuzz_centroid")}, + {'f', QLatin1String("fuzzy_nand")}, + {'f', QLatin1String("fuzzy_nor")}, + {'f', QLatin1String("fuzzy_not")}, + {'f', QLatin1String("fuzzy_nxor")}, + {'f', QLatin1String("fuzzy_or")}, + {'f', QLatin1String("fuzzy_xor")}, + {'g', QLatin1String("geoself")}, + {'g', QLatin1String("getattrib")}, + {'g', QLatin1String("getattribute")}, + {'g', QLatin1String("getbbox")}, + {'g', QLatin1String("getblurP")}, + {'g', QLatin1String("getbounces")}, + {'g', QLatin1String("getbounds")}, + {'g', QLatin1String("getcomp")}, + {'g', QLatin1String("getcomponents")}, + {'g', QLatin1String("getderiv")}, + {'g', QLatin1String("getfogname")}, + {'g', QLatin1String("getglobalraylevel")}, + {'g', QLatin1String("getlight")}, + {'g', QLatin1String("getlightid")}, + {'g', QLatin1String("getlightname")}, + {'g', QLatin1String("getlights")}, + {'g', QLatin1String("getlightscope")}, + {'g', QLatin1String("getmaterial")}, + {'g', QLatin1String("getobjectname")}, + {'g', QLatin1String("getphotonlight")}, + {'g', QLatin1String("getpointbbox")}, + {'g', QLatin1String("getprimid")}, + {'g', QLatin1String("getptextureid")}, + {'g', QLatin1String("getraylevel")}, + {'g', QLatin1String("getrayweight")}, + {'g', QLatin1String("getsamplestore")}, + {'g', QLatin1String("getscope")}, + {'g', QLatin1String("getsmoothP")}, + {'g', QLatin1String("getspace")}, + {'g', QLatin1String("getuvobjects")}, + {'g', QLatin1String("getuvtangents")}, + {'g', QLatin1String("gradient")}, + {'h', QLatin1String("hair")}, + {'h', QLatin1String("hasattrib")}, + {'h', QLatin1String("hasdetailattrib")}, + {'h', QLatin1String("haslight")}, + {'h', QLatin1String("hasplane")}, + {'h', QLatin1String("haspointattrib")}, + {'h', QLatin1String("hasprimattrib")}, + {'h', QLatin1String("hasvertexattrib")}, + {'h', QLatin1String("hedge_dstpoint")}, + {'h', QLatin1String("hedge_dstvertex")}, + {'h', QLatin1String("hedge_equivcount")}, + {'h', QLatin1String("hedge_isequiv")}, + {'h', QLatin1String("hedge_isprimary")}, + {'h', QLatin1String("hedge_isvalid")}, + {'h', QLatin1String("hedge_next")}, + {'h', QLatin1String("hedge_nextequiv")}, + {'h', QLatin1String("hedge_postdstpoint")}, + {'h', QLatin1String("hedge_postdstvertex")}, + {'h', QLatin1String("hedge_presrcpoint")}, + {'h', QLatin1String("hedge_presrcvertex")}, + {'h', QLatin1String("hedge_prev")}, + {'h', QLatin1String("hedge_prim")}, + {'h', QLatin1String("hedge_primary")}, + {'h', QLatin1String("hedge_srcpoint")}, + {'h', QLatin1String("hedge_srcvertex")}, + {'h', QLatin1String("henyeygreenstein")}, + {'h', QLatin1String("hscript_noise")}, + {'h', QLatin1String("hscript_rand")}, + {'h', QLatin1String("hscript_snoise")}, + {'h', QLatin1String("hscript_sturb")}, + {'h', QLatin1String("hscript_turb")}, + {'h', QLatin1String("hsvtorgb")}, + {'i', QLatin1String("iaspect")}, + {'i', QLatin1String("ichname")}, + {'i', QLatin1String("ident")}, + {'i', QLatin1String("idtopoint")}, + {'i', QLatin1String("idtoprim")}, + {'i', QLatin1String("iend")}, + {'i', QLatin1String("iendtime")}, + {'i', QLatin1String("ihasplane")}, + {'i', QLatin1String("import")}, + {'i', QLatin1String("ingroup")}, + {'i', QLatin1String("inpointgroup")}, + {'i', QLatin1String("inprimgroup")}, + {'i', QLatin1String("insert")}, + {'i', QLatin1String("instance")}, + {'i', QLatin1String("interpolate")}, + {'i', QLatin1String("intersect")}, + {'i', QLatin1String("intersect_all")}, + {'i', QLatin1String("intersect_lights")}, + {'i', QLatin1String("inumplanes")}, + {'i', QLatin1String("invert")}, + {'i', QLatin1String("invertexgroup")}, + {'i', QLatin1String("iplaneindex")}, + {'i', QLatin1String("iplanename")}, + {'i', QLatin1String("iplanesize")}, + {'i', QLatin1String("irate")}, + {'i', QLatin1String("irradiance")}, + {'i', QLatin1String("isalpha")}, + {'i', QLatin1String("isbound")}, + {'i', QLatin1String("isconnected")}, + {'i', QLatin1String("isdigit")}, + {'i', QLatin1String("isfinite")}, + {'i', QLatin1String("isfogray")}, + {'i', QLatin1String("isframes")}, + {'i', QLatin1String("isnan")}, + {'i', QLatin1String("isotropic")}, + {'i', QLatin1String("israytracing")}, + {'i', QLatin1String("issamples")}, + {'i', QLatin1String("isseconds")}, + {'i', QLatin1String("isshadowray")}, + {'i', QLatin1String("istart")}, + {'i', QLatin1String("istarttime")}, + {'i', QLatin1String("isuvrendering")}, + {'i', QLatin1String("isvalidindex")}, + {'i', QLatin1String("isvarying")}, + {'i', QLatin1String("itoa")}, + {'i', QLatin1String("ixres")}, + {'i', QLatin1String("iyres")}, + {'j', QLatin1String("join")}, + {'k', QLatin1String("kspline")}, + {'l', QLatin1String("len")}, + {'l', QLatin1String("length")}, + {'l', QLatin1String("length2")}, + {'l', QLatin1String("lerp")}, + {'l', QLatin1String("lightid")}, + {'l', QLatin1String("limit_sample_space")}, + {'l', QLatin1String("limport")}, + {'l', QLatin1String("lkspline")}, + {'l', QLatin1String("log")}, + {'l', QLatin1String("log10")}, + {'l', QLatin1String("lookat")}, + {'l', QLatin1String("lspline")}, + {'l', QLatin1String("lstrip")}, + {'l', QLatin1String("luminance")}, + {'l', QLatin1String("lumname")}, + {'m', QLatin1String("makebasis")}, + {'m', QLatin1String("maketransform")}, + {'m', QLatin1String("maskname")}, + {'m', QLatin1String("match")}, + {'m', QLatin1String("matchvex_blinn")}, + {'m', QLatin1String("matchvex_specular")}, + {'m', QLatin1String("mattrib")}, + {'m', QLatin1String("max")}, + {'m', QLatin1String("mdensity")}, + {'m', QLatin1String("metaimport")}, + {'m', QLatin1String("metamarch")}, + {'m', QLatin1String("metanext")}, + {'m', QLatin1String("metastart")}, + {'m', QLatin1String("metaweight")}, + {'m', QLatin1String("min")}, + {'m', QLatin1String("minpos")}, + {'m', QLatin1String("mspace")}, + {'n', QLatin1String("nametopoint")}, + {'n', QLatin1String("nametoprim")}, + {'n', QLatin1String("nbouncetypes")}, + {'n', QLatin1String("nearpoint")}, + {'n', QLatin1String("nearpoints")}, + {'n', QLatin1String("neighbour")}, + {'n', QLatin1String("neighbourcount")}, + {'n', QLatin1String("neighbours")}, + {'n', QLatin1String("newgroup")}, + {'n', QLatin1String("newsampler")}, + {'n', QLatin1String("nextsample")}, + {'n', QLatin1String("ninput")}, + {'n', QLatin1String("noise")}, + {'n', QLatin1String("noised")}, + {'n', QLatin1String("normal_bsdf")}, + {'n', QLatin1String("normalize")}, + {'n', QLatin1String("normalname")}, + {'n', QLatin1String("npoints")}, + {'n', QLatin1String("npointsgroup")}, + {'n', QLatin1String("nprimitives")}, + {'n', QLatin1String("nprimitivesgroup")}, + {'n', QLatin1String("nrandom")}, + {'n', QLatin1String("ntransform")}, + {'n', QLatin1String("nuniqueval")}, + {'n', QLatin1String("nvertices")}, + {'n', QLatin1String("nverticesgroup")}, + {'o', QLatin1String("occlusion")}, + {'o', QLatin1String("onoise")}, + {'o', QLatin1String("opdigits")}, + {'o', QLatin1String("opend")}, + {'o', QLatin1String("opfullpath")}, + {'o', QLatin1String("opstart")}, + {'o', QLatin1String("optransform")}, + {'o', QLatin1String("ord")}, + {'o', QLatin1String("osd_facecount")}, + {'o', QLatin1String("osd_firstpatch")}, + {'o', QLatin1String("osd_limitsurface")}, + {'o', QLatin1String("osd_limitsurfacevertex")}, + {'o', QLatin1String("osd_patchcount")}, + {'o', QLatin1String("osd_patches")}, + {'o', QLatin1String("outerproduct")}, + {'o', QLatin1String("ow_nspace")}, + {'o', QLatin1String("ow_space")}, + {'o', QLatin1String("ow_vspace")}, + {'p', QLatin1String("pack_inttosafefloat")}, + {'p', QLatin1String("pathtrace")}, + {'p', QLatin1String("pcclose")}, + {'p', QLatin1String("pcconvex")}, + {'p', QLatin1String("pcexport")}, + {'p', QLatin1String("pcfarthest")}, + {'p', QLatin1String("pcfilter")}, + {'p', QLatin1String("pcfind")}, + {'p', QLatin1String("pcfind_radius")}, + {'p', QLatin1String("pcgenerate")}, + {'p', QLatin1String("pcimport")}, + {'p', QLatin1String("pcimportbyidx3")}, + {'p', QLatin1String("pcimportbyidx4")}, + {'p', QLatin1String("pcimportbyidxf")}, + {'p', QLatin1String("pcimportbyidxi")}, + {'p', QLatin1String("pcimportbyidxp")}, + {'p', QLatin1String("pcimportbyidxs")}, + {'p', QLatin1String("pcimportbyidxv")}, + {'p', QLatin1String("pciterate")}, + {'p', QLatin1String("pcnumfound")}, + {'p', QLatin1String("pcopen")}, + {'p', QLatin1String("pcopenlod")}, + {'p', QLatin1String("pcsampleleaf")}, + {'p', QLatin1String("pcsize")}, + {'p', QLatin1String("pcunshaded")}, + {'p', QLatin1String("pcwrite")}, + {'p', QLatin1String("pgfind")}, + {'p', QLatin1String("phong")}, + {'p', QLatin1String("phongBRDF")}, + {'p', QLatin1String("phonglobe")}, + {'p', QLatin1String("photonmap")}, + {'p', QLatin1String("planeindex")}, + {'p', QLatin1String("planename")}, + {'p', QLatin1String("planesize")}, + {'p', QLatin1String("pluralize")}, + {'p', QLatin1String("pnoise")}, + {'p', QLatin1String("point")}, + {'p', QLatin1String("pointattrib")}, + {'p', QLatin1String("pointattribsize")}, + {'p', QLatin1String("pointattribtype")}, + {'p', QLatin1String("pointattribtypeinfo")}, + {'p', QLatin1String("pointedge")}, + {'p', QLatin1String("pointhedge")}, + {'p', QLatin1String("pointhedgenext")}, + {'p', QLatin1String("pointname")}, + {'p', QLatin1String("pointprims")}, + {'p', QLatin1String("pointvertex")}, + {'p', QLatin1String("pointvertices")}, + {'p', QLatin1String("polardecomp")}, + {'p', QLatin1String("pop")}, + {'p', QLatin1String("pow")}, + {'p', QLatin1String("prim")}, + {'p', QLatin1String("prim_attribute")}, + {'p', QLatin1String("prim_normal")}, + {'p', QLatin1String("primattrib")}, + {'p', QLatin1String("primattribsize")}, + {'p', QLatin1String("primattribtype")}, + {'p', QLatin1String("primattribtypeinfo")}, + {'p', QLatin1String("primhedge")}, + {'p', QLatin1String("primintrinsic")}, + {'p', QLatin1String("primpoint")}, + {'p', QLatin1String("primpoints")}, + {'p', QLatin1String("primuv")}, + {'p', QLatin1String("primvertex")}, + {'p', QLatin1String("primvertexcount")}, + {'p', QLatin1String("primvertices")}, + {'p', QLatin1String("print_once")}, + {'p', QLatin1String("printf")}, + {'p', QLatin1String("product")}, + {'p', QLatin1String("ptexture")}, + {'p', QLatin1String("ptlined")}, + {'p', QLatin1String("ptransform")}, + {'p', QLatin1String("push")}, + {'q', QLatin1String("qconvert")}, + {'q', QLatin1String("qdistance")}, + {'q', QLatin1String("qinvert")}, + {'q', QLatin1String("qmultiply")}, + {'q', QLatin1String("qrotate")}, + {'q', QLatin1String("quaternion")}, + {'r', QLatin1String("radians")}, + {'r', QLatin1String("rand")}, + {'r', QLatin1String("random")}, + {'r', QLatin1String("random_fhash")}, + {'r', QLatin1String("random_ihash")}, + {'r', QLatin1String("random_shash")}, + {'r', QLatin1String("random_sobol")}, + {'r', QLatin1String("rawbumpmap")}, + {'r', QLatin1String("rawbumpmapA")}, + {'r', QLatin1String("rawbumpmapB")}, + {'r', QLatin1String("rawbumpmapG")}, + {'r', QLatin1String("rawbumpmapL")}, + {'r', QLatin1String("rawbumpmapR")}, + {'r', QLatin1String("rawcolormap")}, + {'r', QLatin1String("rayhittest")}, + {'r', QLatin1String("rayimport")}, + {'r', QLatin1String("re_find")}, + {'r', QLatin1String("re_findall")}, + {'r', QLatin1String("re_match")}, + {'r', QLatin1String("re_replace")}, + {'r', QLatin1String("re_split")}, + {'r', QLatin1String("reflect")}, + {'r', QLatin1String("reflectlight")}, + {'r', QLatin1String("refract")}, + {'r', QLatin1String("refractlight")}, + {'r', QLatin1String("relativepath")}, + {'r', QLatin1String("relbbox")}, + {'r', QLatin1String("relpointbbox")}, + {'r', QLatin1String("removegroup")}, + {'r', QLatin1String("removeindex")}, + {'r', QLatin1String("removepoint")}, + {'r', QLatin1String("removeprim")}, + {'r', QLatin1String("removevalue")}, + {'r', QLatin1String("renderstate")}, + {'r', QLatin1String("reorder")}, + {'r', QLatin1String("resample_linear")}, + {'r', QLatin1String("resize")}, + {'r', QLatin1String("resolvemissedray")}, + {'r', QLatin1String("reverse")}, + {'r', QLatin1String("rgbtohsv")}, + {'r', QLatin1String("rgbtoxyz")}, + {'r', QLatin1String("rint")}, + {'r', QLatin1String("rotate")}, + {'r', QLatin1String("rotate_x_to")}, + {'r', QLatin1String("rstrip")}, + {'s', QLatin1String("sample_bsdf")}, + {'s', QLatin1String("sample_cauchy")}, + {'s', QLatin1String("sample_circle_arc")}, + {'s', QLatin1String("sample_circle_edge_uniform")}, + {'s', QLatin1String("sample_circle_slice")}, + {'s', QLatin1String("sample_circle_uniform")}, + {'s', QLatin1String("sample_direction_cone")}, + {'s', QLatin1String("sample_direction_uniform")}, + {'s', QLatin1String("sample_discrete")}, + {'s', QLatin1String("sample_exponential")}, + {'s', QLatin1String("sample_geometry")}, + {'s', QLatin1String("sample_hemisphere")}, + {'s', QLatin1String("sample_hypersphere_cone")}, + {'s', QLatin1String("sample_hypersphere_uniform")}, + {'s', QLatin1String("sample_light")}, + {'s', QLatin1String("sample_lognormal")}, + {'s', QLatin1String("sample_lognormal_by_median")}, + {'s', QLatin1String("sample_normal")}, + {'s', QLatin1String("sample_orientation_cone")}, + {'s', QLatin1String("sample_orientation_uniform")}, + {'s', QLatin1String("sample_photon")}, + {'s', QLatin1String("sample_sphere_cone")}, + {'s', QLatin1String("sample_sphere_uniform")}, + {'s', QLatin1String("sampledisk")}, + {'s', QLatin1String("scale")}, + {'s', QLatin1String("select")}, + {'s', QLatin1String("sensor_panorama_create")}, + {'s', QLatin1String("sensor_panorama_getcolor")}, + {'s', QLatin1String("sensor_panorama_getcone")}, + {'s', QLatin1String("sensor_panorama_getdepth")}, + {'s', QLatin1String("sensor_save")}, + {'s', QLatin1String("serialize")}, + {'s', QLatin1String("set")}, + {'s', QLatin1String("setagentclipnames")}, + {'s', QLatin1String("setagentcliptimes")}, + {'s', QLatin1String("setagentclipweights")}, + {'s', QLatin1String("setagentcollisionlayer")}, + {'s', QLatin1String("setagentcurrentlayer")}, + {'s', QLatin1String("setagentlocaltransform")}, + {'s', QLatin1String("setagentlocaltransforms")}, + {'s', QLatin1String("setagentworldtransform")}, + {'s', QLatin1String("setagentworldtransforms")}, + {'s', QLatin1String("setattrib")}, + {'s', QLatin1String("setattribtypeinfo")}, + {'s', QLatin1String("setcomp")}, + {'s', QLatin1String("setcurrentlight")}, + {'s', QLatin1String("setdetailattrib")}, + {'s', QLatin1String("setpointattrib")}, + {'s', QLatin1String("setpointgroup")}, + {'s', QLatin1String("setprimattrib")}, + {'s', QLatin1String("setprimgroup")}, + {'s', QLatin1String("setprimintrinsic")}, + {'s', QLatin1String("setprimvertex")}, + {'s', QLatin1String("setsamplestore")}, + {'s', QLatin1String("setvertexattrib")}, + {'s', QLatin1String("setvertexgroup")}, + {'s', QLatin1String("setvertexpoint")}, + {'s', QLatin1String("shadow")}, + {'s', QLatin1String("shadow_light")}, + {'s', QLatin1String("shadowmap")}, + {'s', QLatin1String("shimport")}, + {'s', QLatin1String("shl")}, + {'s', QLatin1String("shr")}, + {'s', QLatin1String("shrz")}, + {'s', QLatin1String("sign")}, + {'s', QLatin1String("simport")}, + {'s', QLatin1String("sin")}, + {'s', QLatin1String("sinh")}, + {'s', QLatin1String("sleep")}, + {'s', QLatin1String("slerp")}, + {'s', QLatin1String("slice")}, + {'s', QLatin1String("slideframe")}, + {'s', QLatin1String("smooth")}, + {'s', QLatin1String("smoothrotation")}, + {'s', QLatin1String("snoise")}, + {'s', QLatin1String("solvecubic")}, + {'s', QLatin1String("solvepoly")}, + {'s', QLatin1String("solvequadratic")}, + {'s', QLatin1String("sort")}, + {'s', QLatin1String("specular")}, + {'s', QLatin1String("specularBRDF")}, + {'s', QLatin1String("spline")}, + {'s', QLatin1String("split")}, + {'s', QLatin1String("splitpath")}, + {'s', QLatin1String("sprintf")}, + {'s', QLatin1String("sqrt")}, + {'s', QLatin1String("startswith")}, + {'s', QLatin1String("storelightexport")}, + {'s', QLatin1String("strip")}, + {'s', QLatin1String("strlen")}, + {'s', QLatin1String("sum")}, + {'s', QLatin1String("switch")}, + {'s', QLatin1String("swizzle")}, + {'t', QLatin1String("tan")}, + {'t', QLatin1String("tanh")}, + {'t', QLatin1String("tet_adjacent")}, + {'t', QLatin1String("tet_faceindex")}, + {'t', QLatin1String("teximport")}, + {'t', QLatin1String("texprintf")}, + {'t', QLatin1String("texture")}, + {'t', QLatin1String("texture3d")}, + {'t', QLatin1String("texture3dBox")}, + {'t', QLatin1String("titlecase")}, + {'t', QLatin1String("toNDC")}, + {'t', QLatin1String("tolower")}, + {'t', QLatin1String("toupper")}, + {'t', QLatin1String("trace")}, + {'t', QLatin1String("translate")}, + {'t', QLatin1String("translucent")}, + {'t', QLatin1String("transpose")}, + {'t', QLatin1String("trunc")}, + {'t', QLatin1String("tw_nspace")}, + {'t', QLatin1String("tw_space")}, + {'t', QLatin1String("tw_vspace")}, + {'u', QLatin1String("uniqueval")}, + {'u', QLatin1String("unpack_intfromsafefloat")}, + {'u', QLatin1String("unserialize")}, + {'u', QLatin1String("upush")}, + {'u', QLatin1String("uvunwrap")}, + {'v', QLatin1String("variance")}, + {'v', QLatin1String("velocityname")}, + {'v', QLatin1String("vertex")}, + {'v', QLatin1String("vertexattrib")}, + {'v', QLatin1String("vertexattribsize")}, + {'v', QLatin1String("vertexattribtype")}, + {'v', QLatin1String("vertexattribtypeinfo")}, + {'v', QLatin1String("vertexhedge")}, + {'v', QLatin1String("vertexindex")}, + {'v', QLatin1String("vertexnext")}, + {'v', QLatin1String("vertexpoint")}, + {'v', QLatin1String("vertexprev")}, + {'v', QLatin1String("vertexprim")}, + {'v', QLatin1String("vertexprimindex")}, + {'v', QLatin1String("vnoise")}, + {'v', QLatin1String("volume")}, + {'v', QLatin1String("volumegradient")}, + {'v', QLatin1String("volumeindex")}, + {'v', QLatin1String("volumeindexorigin")}, + {'v', QLatin1String("volumeindextopos")}, + {'v', QLatin1String("volumeindexv")}, + {'v', QLatin1String("volumepostoindex")}, + {'v', QLatin1String("volumeres")}, + {'v', QLatin1String("volumesample")}, + {'v', QLatin1String("volumesamplev")}, + {'v', QLatin1String("vtransform")}, + {'w', QLatin1String("warning")}, + {'w', QLatin1String("wireblinn")}, + {'w', QLatin1String("wirediffuse")}, + {'w', QLatin1String("wnoise")}, + {'w', QLatin1String("wo_nspace")}, + {'w', QLatin1String("wo_space")}, + {'w', QLatin1String("wo_vspace")}, + {'w', QLatin1String("writepixel")}, + {'w', QLatin1String("wt_nspace")}, + {'w', QLatin1String("wt_space")}, + {'w', QLatin1String("wt_vspace")}, + {'x', QLatin1String("xnoise")}, + {'x', QLatin1String("xnoised")}, + {'x', QLatin1String("xyzdist")}, + {'x', QLatin1String("xyztorgb")}}; + vex_other = { + {('d'), QLatin1String("define")}, {('e'), QLatin1String("else")}, + {('e'), QLatin1String("endif")}, {('i'), QLatin1String("if")}, + {('i'), QLatin1String("ifdef")}, {('i'), QLatin1String("ifndef")}, + {('i'), QLatin1String("include")}, {('p'), QLatin1String("pragma")}, + {('u'), QLatin1String("undef")}, + }; +} +void loadVEXData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!vexDataInitialized) { + initVEXData(); + vexDataInitialized = true; + } + types = vex_types; + keywords = vex_keywords; + builtin = vex_builtin; + literals = vex_literals; + other = vex_other; +} + +/********************************************************/ +/*** CMAKE DATA ***************************************/ +/********************************************************/ +static bool cmakeDataInitialized = false; +static QMultiHash cmake_keywords; +static QMultiHash cmake_types; +static QMultiHash cmake_literals; +static QMultiHash cmake_builtin; +static QMultiHash cmake_other; +void initCMakeData() { + cmake_keywords = {{'b', QLatin1String("break")}, + {'c', QLatin1String("cmake_host_system_information")}, + {'c', QLatin1String("cmake_minimum_required")}, + {'c', QLatin1String("cmake_parse_arguments")}, + {'c', QLatin1String("cmake_policy")}, + {'c', QLatin1String("configure_file")}, + {'c', QLatin1String("continue")}, + {'e', QLatin1String("elseif")}, + {'e', QLatin1String("else")}, + {'e', QLatin1String("endforeach")}, + {'e', QLatin1String("endfunction")}, + {'e', QLatin1String("endif")}, + {'e', QLatin1String("endmacro")}, + {'e', QLatin1String("endwhile")}, + {'e', QLatin1String("execute_process")}, + {'f', QLatin1String("file")}, + {'f', QLatin1String("find_file")}, + {'f', QLatin1String("find_library")}, + {'f', QLatin1String("find_package")}, + {'f', QLatin1String("find_path")}, + {'f', QLatin1String("find_program")}, + {'f', QLatin1String("foreach")}, + {'f', QLatin1String("function")}, + {'g', QLatin1String("get_cmake_property")}, + {'g', QLatin1String("get_directory_property")}, + {'g', QLatin1String("get_filename_component")}, + {'g', QLatin1String("get_property")}, + {'i', QLatin1String("if")}, + {'i', QLatin1String("include")}, + {'i', QLatin1String("include_guard")}, + {'l', QLatin1String("list")}, + {'m', QLatin1String("macro")}, + {'m', QLatin1String("mark_as_advanced")}, + {'m', QLatin1String("math")}, + {'m', QLatin1String("message")}, + {'o', QLatin1String("option")}, + {'r', QLatin1String("return")}, + {'s', QLatin1String("separate_arguments")}, + {'s', QLatin1String("set_directory_properties")}, + {'s', QLatin1String("set")}, + {'s', QLatin1String("set_property")}, + {'s', QLatin1String("site_name")}, + {'s', QLatin1String("string")}, + {'u', QLatin1String("unset")}, + {'v', QLatin1String("variable_watch")}, + {'w', QLatin1String("while")}, + {'a', QLatin1String("add_compile_definitions")}, + {'a', QLatin1String("add_compile_options")}, + {'A', QLatin1String("ADD_COMPILE_OPTIONS")}, + {'a', QLatin1String("add_custom_command")}, + {'a', QLatin1String("add_custom_target")}, + {'a', QLatin1String("add_definitions")}, + {'a', QLatin1String("add_dependencies")}, + {'a', QLatin1String("add_executable")}, + {'a', QLatin1String("add_library")}, + {'a', QLatin1String("add_link_options")}, + {'a', QLatin1String("add_subdirectory")}, + {'a', QLatin1String("add_test")}, + {'a', QLatin1String("aux_source_directory")}, + {'b', QLatin1String("build_command")}, + {'c', QLatin1String("create_test_sourcelist")}, + {'d', QLatin1String("define_property")}, + {'e', QLatin1String("enable_language")}, + {'e', QLatin1String("enable_testing")}, + {'e', QLatin1String("export")}, + {'f', QLatin1String("fltk_wrap_ui")}, + {'g', QLatin1String("get_source_file_property")}, + {'g', QLatin1String("get_target_property")}, + {'g', QLatin1String("get_test_property")}, + {'i', QLatin1String("include_directories")}, + {'i', QLatin1String("include_external_msproject")}, + {'i', QLatin1String("include_regular_expression")}, + {'i', QLatin1String("install")}, + {'l', QLatin1String("link_directories")}, + {'l', QLatin1String("link_libraries")}, + {'l', QLatin1String("load_cache")}, + {'p', QLatin1String("project")}, + {'q', QLatin1String("qt_wrap_cpp")}, + {'q', QLatin1String("qt_wrap_ui")}, + {'r', QLatin1String("remove_definitions")}, + {'s', QLatin1String("set_source_files_properties")}, + {'s', QLatin1String("set_target_properties")}, + {'s', QLatin1String("set_tests_properties")}, + {'s', QLatin1String("source_group")}, + {'t', QLatin1String("target_compile_definitions")}, + {'t', QLatin1String("target_compile_features")}, + {'t', QLatin1String("target_compile_options")}, + {'t', QLatin1String("target_include_directories")}, + {'t', QLatin1String("target_link_directories")}, + {'t', QLatin1String("target_link_libraries")}, + {'t', QLatin1String("target_link_options")}, + {'t', QLatin1String("target_sources")}, + {'t', QLatin1String("try_compile")}, + {'t', QLatin1String("try_run")}, + {'c', QLatin1String("ctest_build")}, + {'c', QLatin1String("ctest_configure")}, + {'c', QLatin1String("ctest_coverage")}, + {'c', QLatin1String("ctest_empty_binary_directory")}, + {'c', QLatin1String("ctest_memcheck")}, + {'c', QLatin1String("ctest_read_custom_files")}, + {'c', QLatin1String("ctest_run_script")}, + {'c', QLatin1String("ctest_sleep")}, + {'c', QLatin1String("ctest_start")}, + {'c', QLatin1String("ctest_submit")}, + {'c', QLatin1String("ctest_test")}, + {'c', QLatin1String("ctest_update")}, + {'c', QLatin1String("ctest_upload")}, + {'b', QLatin1String("build_name")}, + {'e', QLatin1String("exec_program")}, + {'e', QLatin1String("export_library_dependencies")}, + {'i', QLatin1String("install_files")}, + {'i', QLatin1String("install_programs")}, + {'i', QLatin1String("install_targets")}, + {'l', QLatin1String("load_command")}, + {'m', QLatin1String("make_directory")}, + {'o', QLatin1String("output_required_files")}, + {'r', QLatin1String("remove")}, + {'s', QLatin1String("subdir_depends")}, + {'s', QLatin1String("subdirs")}, + {'u', QLatin1String("use_mangled_mesa")}, + {'u', QLatin1String("utility_source")}, + {'v', QLatin1String("variable_requires")}, + {'w', QLatin1String("write_file")}, + {'q', QLatin1String("qt5_use_modules")}, + {'q', QLatin1String("qt5_use_package")}, + {'q', QLatin1String("qt5_wrap_cpp")}, + {'a', QLatin1String("and")}, + {'o', QLatin1String("or")}, + {'n', QLatin1String("not")}, + {'c', QLatin1String("command")}, + {'p', QLatin1String("policy")}, + {'t', QLatin1String("target")}, + {'t', QLatin1String("test")}, + {'e', QLatin1String("exists")}, + {'i', QLatin1String("is_newer_than")}, + {'i', QLatin1String("is_directory")}, + {'i', QLatin1String("is_symlink")}, + {'i', QLatin1String("is_absolute")}, + {'m', QLatin1String("matches")}, + {'l', QLatin1String("less")}, + {'g', QLatin1String("greater")}, + {'e', QLatin1String("equal")}, + {'l', QLatin1String("less_equal")}, + {'g', QLatin1String("greater_equal")}, + {'s', QLatin1String("strless")}, + {'s', QLatin1String("strgreater")}, + {'s', QLatin1String("strequal")}, + {'s', QLatin1String("strless_equal")}, + {'s', QLatin1String("strgreater_equal")}, + {'v', QLatin1String("version_less")}, + {'v', QLatin1String("version_greater")}, + {'v', QLatin1String("version_equal")}, + {'v', QLatin1String("version_less_equal")}, + {'v', QLatin1String("version_greater_equal")}, + {'i', QLatin1String("in_list")}, + {'d', QLatin1String("defined")}}; + cmake_types = {}; + cmake_literals = { + {'o', QLatin1String("on")}, {'o', QLatin1String("off")}, + {'O', QLatin1String("ON")}, {'O', QLatin1String("OFF")}, + {'t', QLatin1String("true")}, {'f', QLatin1String("false")}, + {'T', QLatin1String("TRUE")}, {'F', QLatin1String("FALSE")}}; + cmake_builtin = { + {'A', QLatin1String("ALLOW_DUPLICATE_CUSTOM_TARGETS")}, + {'A', QLatin1String("AUTOGEN_TARGETS_FOLDER")}, + {'A', QLatin1String("AUTOMOC_TARGETS_FOLDER")}, + {'D', QLatin1String("DEBUG_CONFIGURATIONS")}, + {'D', QLatin1String("DISABLED_FEATURES")}, + {'E', QLatin1String("ENABLED_FEATURES")}, + {'E', QLatin1String("ENABLED_LANGUAGES")}, + {'F', QLatin1String("FIND_LIBRARY_USE_LIB64_PATHS")}, + {'F', QLatin1String("FIND_LIBRARY_USE_OPENBSD_VERSIONING")}, + {'G', QLatin1String("GLOBAL_DEPENDS_DEBUG_MODE")}, + {'G', QLatin1String("GLOBAL_DEPENDS_NO_CYCLES")}, + {'I', QLatin1String("IN_TRY_COMPILE")}, + {'P', QLatin1String("PACKAGES_FOUND")}, + {'P', QLatin1String("PACKAGES_NOT_FOUND")}, + {'J', QLatin1String("JOB_POOLS")}, + {'P', QLatin1String("PREDEFINED_TARGETS_FOLDER")}, + {'E', QLatin1String("ECLIPSE_EXTRA_NATURES")}, + {'R', QLatin1String("REPORT_UNDEFINED_PROPERTIES")}, + {'R', QLatin1String("RULE_LAUNCH_COMPILE")}, + {'R', QLatin1String("RULE_LAUNCH_CUSTOM")}, + {'R', QLatin1String("RULE_LAUNCH_LINK")}, + {'R', QLatin1String("RULE_MESSAGES")}, + {'T', QLatin1String("TARGET_ARCHIVES_MAY_BE_SHARED_LIBS")}, + {'T', QLatin1String("TARGET_SUPPORTS_SHARED_LIBS")}, + {'U', QLatin1String("USE_FOLDERS")}, + {'A', QLatin1String("ADDITIONAL_MAKE_CLEAN_FILES")}, + {'C', QLatin1String("CACHE_VARIABLES")}, + {'C', QLatin1String("CLEAN_NO_CUSTOM")}, + {'C', QLatin1String("CMAKE_CONFIGURE_DEPENDS")}, + {'C', QLatin1String("COMPILE_DEFINITIONS")}, + {'C', QLatin1String("COMPILE_OPTIONS")}, + {'D', QLatin1String("DEFINITIONS")}, + {'E', QLatin1String("EXCLUDE_FROM_ALL")}, + {'I', QLatin1String("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")}, + {'I', QLatin1String("INCLUDE_DIRECTORIES")}, + {'I', QLatin1String("INCLUDE_REGULAR_EXPRESSION")}, + {'I', QLatin1String("INTERPROCEDURAL_OPTIMIZATION")}, + {'L', QLatin1String("LINK_DIRECTORIES")}, + {'L', QLatin1String("LISTFILE_STACK")}, + {'M', QLatin1String("MACROS")}, + {'P', QLatin1String("PARENT_DIRECTORY")}, + {'R', QLatin1String("RULE_LAUNCH_COMPILE")}, + {'R', QLatin1String("RULE_LAUNCH_CUSTOM")}, + {'R', QLatin1String("RULE_LAUNCH_LINK")}, + {'T', QLatin1String("TEST_INCLUDE_FILE")}, + {'V', QLatin1String("VARIABLES")}, + {'A', QLatin1String("ALIASED_TARGET")}, + {'A', QLatin1String("ARCHIVE_OUTPUT_DIRECTORY")}, + {'A', QLatin1String("ARCHIVE_OUTPUT_NAME")}, + {'A', QLatin1String("AUTOGEN_TARGET_DEPENDS")}, + {'A', QLatin1String("AUTOMOC_MOC_OPTIONS")}, + {'A', QLatin1String("AUTOMOC")}, + {'A', QLatin1String("AUTOUIC")}, + {'A', QLatin1String("AUTOUIC_OPTIONS")}, + {'A', QLatin1String("AUTORCC")}, + {'A', QLatin1String("AUTORCC_OPTIONS")}, + {'B', QLatin1String("BUILD_WITH_INSTALL_RPATH")}, + {'B', QLatin1String("BUNDLE_EXTENSION")}, + {'B', QLatin1String("BUNDLE")}, + {'C', QLatin1String("COMPATIBLE_INTERFACE_BOOL")}, + {'C', QLatin1String("COMPATIBLE_INTERFACE_NUMBER_MAX")}, + {'C', QLatin1String("COMPATIBLE_INTERFACE_NUMBER_MIN")}, + {'C', QLatin1String("COMPATIBLE_INTERFACE_STRING")}, + {'C', QLatin1String("COMPILE_DEFINITIONS")}, + {'C', QLatin1String("COMPILE_FLAGS")}, + {'C', QLatin1String("COMPILE_OPTIONS")}, + {'D', QLatin1String("DEBUG_POSTFIX")}, + {'D', QLatin1String("DEFINE_SYMBOL")}, + {'E', QLatin1String("EchoString")}, + {'E', QLatin1String("ENABLE_EXPORTS")}, + {'E', QLatin1String("EXCLUDE_FROM_ALL")}, + {'E', QLatin1String("EXCLUDE_FROM_DEFAULT_BUILD")}, + {'E', QLatin1String("EXPORT_NAME")}, + {'F', QLatin1String("FOLDER")}, + {'F', QLatin1String("Fortran_FORMAT")}, + {'F', QLatin1String("Fortran_MODULE_DIRECTORY")}, + {'F', QLatin1String("FRAMEWORK")}, + {'G', QLatin1String("GENERATOR_FILE_NAME")}, + {'G', QLatin1String("GNUtoMS")}, + {'H', QLatin1String("HAS_CXX")}, + {'I', QLatin1String("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")}, + {'I', QLatin1String("IMPORTED_CONFIGURATIONS")}, + {'I', QLatin1String("IMPORTED_IMPLIB")}, + {'I', QLatin1String("IMPORTED_LINK_DEPENDENT_LIBRARIES")}, + {'I', QLatin1String("IMPORTED_LINK_INTERFACE_LANGUAGES")}, + {'I', QLatin1String("IMPORTED_LINK_INTERFACE_LIBRARIES")}, + {'I', QLatin1String("IMPORTED_LINK_INTERFACE_MULTIPLICITY")}, + {'I', QLatin1String("IMPORTED_LOCATION")}, + {'I', QLatin1String("IMPORTED_NO_SONAME")}, + {'I', QLatin1String("IMPORTED")}, + {'I', QLatin1String("IMPORTED_SONAME")}, + {'I', QLatin1String("IMPORT_PREFIX")}, + {'I', QLatin1String("IMPORT_SUFFIX")}, + {'I', QLatin1String("INCLUDE_DIRECTORIES")}, + {'I', QLatin1String("INSTALL_NAME_DIR")}, + {'I', QLatin1String("INSTALL_RPATH")}, + {'I', QLatin1String("INSTALL_RPATH_USE_LINK_PATH")}, + {'I', QLatin1String("INTERFACE_AUTOUIC_OPTIONS")}, + {'I', QLatin1String("INTERFACE_COMPILE_DEFINITIONS")}, + {'I', QLatin1String("INTERFACE_COMPILE_OPTIONS")}, + {'I', QLatin1String("INTERFACE_INCLUDE_DIRECTORIES")}, + {'I', QLatin1String("INTERFACE_LINK_LIBRARIES")}, + {'I', QLatin1String("INTERFACE_POSITION_INDEPENDENT_CODE")}, + {'I', QLatin1String("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES")}, + {'I', QLatin1String("INTERPROCEDURAL_OPTIMIZATION")}, + {'J', QLatin1String("JOB_POOL_COMPILE")}, + {'J', QLatin1String("JOB_POOL_LINK")}, + {'L', QLatin1String("LABELS")}, + {'L', QLatin1String("LIBRARY_OUTPUT_DIRECTORY")}, + {'L', QLatin1String("LIBRARY_OUTPUT_NAME")}, + {'L', QLatin1String("LINK_DEPENDS_NO_SHARED")}, + {'L', QLatin1String("LINK_DEPENDS")}, + {'L', QLatin1String("LINKER_LANGUAGE")}, + {'L', QLatin1String("LINK_FLAGS")}, + {'L', QLatin1String("LINK_INTERFACE_LIBRARIES")}, + {'L', QLatin1String("LINK_INTERFACE_MULTIPLICITY")}, + {'L', QLatin1String("LINK_LIBRARIES")}, + {'L', QLatin1String("LINK_SEARCH_END_STATIC")}, + {'L', QLatin1String("LINK_SEARCH_START_STATIC")}, + {'L', QLatin1String("LOCATION")}, + {'M', QLatin1String("MACOSX_BUNDLE_INFO_PLIST")}, + {'M', QLatin1String("MACOSX_BUNDLE")}, + {'M', QLatin1String("MACOSX_FRAMEWORK_INFO_PLIST")}, + {'M', QLatin1String("MACOSX_RPATH")}, + // {'N', QLatin1String("NAME")}, + {'N', QLatin1String("NO_SONAME")}, + {'N', QLatin1String("NO_SYSTEM_FROM_IMPORTED")}, + {'O', QLatin1String("OSX_ARCHITECTURES")}, + {'O', QLatin1String("OUTPUT_NAME")}, + {'P', QLatin1String("PDB_NAME")}, + {'P', QLatin1String("PDB_OUTPUT_DIRECTORY")}, + {'P', QLatin1String("POSITION_INDEPENDENT_CODE")}, + {'P', QLatin1String("POST_INSTALL_SCRIPT")}, + {'P', QLatin1String("PREFIX")}, + {'P', QLatin1String("PROPERTY")}, + {'P', QLatin1String("PRE_INSTALL_SCRIPT")}, + {'P', QLatin1String("PRIVATE_HEADER")}, + {'P', QLatin1String("PROJECT_LABEL")}, + {'P', QLatin1String("PUBLIC_HEADER")}, + {'R', QLatin1String("RESOURCE")}, + {'R', QLatin1String("RULE_LAUNCH_COMPILE")}, + {'R', QLatin1String("RULE_LAUNCH_CUSTOM")}, + {'R', QLatin1String("RULE_LAUNCH_LINK")}, + {'R', QLatin1String("RUNTIME_OUTPUT_DIRECTORY")}, + {'R', QLatin1String("RUNTIME_OUTPUT_NAME")}, + {'S', QLatin1String("SKIP_BUILD_RPATH")}, + {'S', QLatin1String("SOURCES")}, + {'S', QLatin1String("SOVERSION")}, + {'S', QLatin1String("STATIC_LIBRARY_FLAGS")}, + {'S', QLatin1String("SUFFIX")}, + {'T', QLatin1String("TARGET")}, + {'T', QLatin1String("TYPE")}, + {'V', QLatin1String("VERSION")}, + {'V', QLatin1String("VISIBILITY_INLINES_HIDDEN")}, + {'V', QLatin1String("VS_DOTNET_REFERENCES")}, + {'V', QLatin1String("VS_DOTNET_TARGET_FRAMEWORK_VERSION")}, + {'V', QLatin1String("VS_GLOBAL_KEYWORD")}, + {'V', QLatin1String("VS_GLOBAL_PROJECT_TYPES")}, + {'V', QLatin1String("VS_GLOBAL_ROOTNAMESPACE")}, + {'V', QLatin1String("VS_KEYWORD")}, + {'V', QLatin1String("VS_SCC_AUXPATH")}, + {'V', QLatin1String("VS_SCC_LOCALPATH")}, + {'V', QLatin1String("VS_SCC_PROJECTNAME")}, + {'V', QLatin1String("VS_SCC_PROVIDER")}, + {'V', QLatin1String("VS_WINRT_EXTENSIONS")}, + {'V', QLatin1String("VS_WINRT_REFERENCES")}, + {'W', QLatin1String("WIN32_EXECUTABLE")}, + {'A', QLatin1String("ATTACHED_FILES_ON_FAIL")}, + {'A', QLatin1String("ATTACHED_FILES")}, + {'C', QLatin1String("COST")}, + {'D', QLatin1String("DEPENDS")}, + {'E', QLatin1String("ENVIRONMENT")}, + {'F', QLatin1String("FAIL_REGULAR_EXPRESSION")}, + {'L', QLatin1String("LABELS")}, + {'M', QLatin1String("MEASUREMENT")}, + {'P', QLatin1String("PASS_REGULAR_EXPRESSION")}, + {'P', QLatin1String("PROCESSORS")}, + {'R', QLatin1String("REQUIRED_FILES")}, + {'R', QLatin1String("RESOURCE_LOCK")}, + {'R', QLatin1String("RUN_SERIAL")}, + {'S', QLatin1String("SKIP_RETURN_CODE")}, + {'T', QLatin1String("TIMEOUT")}, + {'W', QLatin1String("WILL_FAIL")}, + {'W', QLatin1String("WORKING_DIRECTORY")}, + {'A', QLatin1String("ABSTRACT")}, + {'A', QLatin1String("AUTOUIC_OPTIONS")}, + {'A', QLatin1String("AUTORCC_OPTIONS")}, + {'C', QLatin1String("COMPILE_DEFINITIONS")}, + {'C', QLatin1String("COMPILE_FLAGS")}, + {'E', QLatin1String("EXTERNAL_OBJECT")}, + {'F', QLatin1String("Fortran_FORMAT")}, + {'G', QLatin1String("GENERATED")}, + {'H', QLatin1String("HEADER_FILE_ONLY")}, + {'K', QLatin1String("KEEP_EXTENSION")}, + {'L', QLatin1String("LABELS")}, + // {'L', QLatin1String("LANGUAGE")}, + {'L', QLatin1String("LOCATION")}, + {'M', QLatin1String("MACOSX_PACKAGE_LOCATION")}, + {'O', QLatin1String("OBJECT_DEPENDS")}, + {'O', QLatin1String("OBJECT_OUTPUTS")}, + {'S', QLatin1String("SYMBOLIC")}, + {'W', QLatin1String("WRAP_EXCLUDE")}, + {'A', QLatin1String("ADVANCED")}, + {'H', QLatin1String("HELPSTRING")}, + {'M', QLatin1String("MODIFIED")}, + {'S', QLatin1String("STRINGS")}, + {'T', QLatin1String("TYPE")}, + {'V', QLatin1String("VALUE")}}; + cmake_other = { + {'C', QLatin1String("CMAKE_ARGC")}, + {'C', QLatin1String("CMAKE_ARGV0")}, + {'C', QLatin1String("CMAKE_AR")}, + {'C', QLatin1String("CMAKE_BINARY_DIR")}, + {'C', QLatin1String("CMAKE_BUILD_TOOL")}, + {'C', QLatin1String("CMAKE_CACHEFILE_DIR")}, + {'C', QLatin1String("CMAKE_CACHE_MAJOR_VERSION")}, + {'C', QLatin1String("CMAKE_CACHE_MINOR_VERSION")}, + {'C', QLatin1String("CMAKE_CACHE_PATCH_VERSION")}, + {'C', QLatin1String("CMAKE_CFG_INTDIR")}, + {'C', QLatin1String("CMAKE_COMMAND")}, + {'C', QLatin1String("CMAKE_CROSSCOMPILING")}, + {'C', QLatin1String("CMAKE_CTEST_COMMAND")}, + {'C', QLatin1String("CMAKE_CURRENT_BINARY_DIR")}, + {'C', QLatin1String("CMAKE_CURRENT_LIST_DIR")}, + {'C', QLatin1String("CMAKE_CURRENT_LIST_FILE")}, + {'C', QLatin1String("CMAKE_CURRENT_LIST_LINE")}, + {'C', QLatin1String("CMAKE_CURRENT_SOURCE_DIR")}, + {'C', QLatin1String("CMAKE_DL_LIBS")}, + {'C', QLatin1String("CMAKE_EDIT_COMMAND")}, + {'C', QLatin1String("CMAKE_EXECUTABLE_SUFFIX")}, + {'C', QLatin1String("CMAKE_EXTRA_GENERATOR")}, + {'C', QLatin1String("CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES")}, + {'C', QLatin1String("CMAKE_GENERATOR")}, + {'C', QLatin1String("CMAKE_GENERATOR_TOOLSET")}, + {'C', QLatin1String("CMAKE_HOME_DIRECTORY")}, + {'C', QLatin1String("CMAKE_IMPORT_LIBRARY_PREFIX")}, + {'C', QLatin1String("CMAKE_IMPORT_LIBRARY_SUFFIX")}, + {'C', QLatin1String("CMAKE_JOB_POOL_COMPILE")}, + {'C', QLatin1String("CMAKE_JOB_POOL_LINK")}, + {'C', QLatin1String("CMAKE_LINK_LIBRARY_SUFFIX")}, + {'C', QLatin1String("CMAKE_MAJOR_VERSION")}, + {'C', QLatin1String("CMAKE_MAKE_PROGRAM")}, + {'C', QLatin1String("CMAKE_MINIMUM_REQUIRED_VERSION")}, + {'C', QLatin1String("CMAKE_MINOR_VERSION")}, + {'C', QLatin1String("CMAKE_PARENT_LIST_FILE")}, + {'C', QLatin1String("CMAKE_PATCH_VERSION")}, + {'C', QLatin1String("CMAKE_PROJECT_NAME")}, + {'C', QLatin1String("CMAKE_RANLIB")}, + {'C', QLatin1String("CMAKE_ROOT")}, + {'C', QLatin1String("CMAKE_SCRIPT_MODE_FILE")}, + {'C', QLatin1String("CMAKE_SHARED_LIBRARY_PREFIX")}, + {'C', QLatin1String("CMAKE_SHARED_LIBRARY_SUFFIX")}, + {'C', QLatin1String("CMAKE_SHARED_MODULE_PREFIX")}, + {'C', QLatin1String("CMAKE_SHARED_MODULE_SUFFIX")}, + {'C', QLatin1String("CMAKE_SIZEOF_VOID_P")}, + {'C', QLatin1String("CMAKE_SKIP_INSTALL_RULES")}, + {'C', QLatin1String("CMAKE_SKIP_RPATH")}, + {'C', QLatin1String("CMAKE_SOURCE_DIR")}, + {'C', QLatin1String("CMAKE_STANDARD_LIBRARIES")}, + {'C', QLatin1String("CMAKE_STATIC_LIBRARY_PREFIX")}, + {'C', QLatin1String("CMAKE_STATIC_LIBRARY_SUFFIX")}, + {'C', QLatin1String("CMAKE_TOOLCHAIN_FILE")}, + {'C', QLatin1String("CMAKE_TWEAK_VERSION")}, + {'C', QLatin1String("CMAKE_VERBOSE_MAKEFILE")}, + {'C', QLatin1String("CMAKE_VERSION")}, + {'C', QLatin1String("CMAKE_VS_DEVENV_COMMAND")}, + {'C', QLatin1String("CMAKE_VS_INTEL_Fortran_PROJECT_VERSION")}, + {'C', QLatin1String("CMAKE_VS_MSBUILD_COMMAND")}, + {'C', QLatin1String("CMAKE_VS_MSDEV_COMMAND")}, + {'C', QLatin1String("CMAKE_VS_PLATFORM_TOOLSET")}, + {'C', QLatin1String("CMAKE_XCODE_PLATFORM_TOOLSET")}, + {'P', QLatin1String("PROJECT_BINARY_DIR")}, + // {'P', QLatin1String("PROJECT_NAME")}, + {'P', QLatin1String("PROJECT_SOURCE_DIR")}, + {'P', QLatin1String("PROJECT_VERSION")}, + {'P', QLatin1String("PROJECT_VERSION_MAJOR")}, + {'P', QLatin1String("PROJECT_VERSION_MINOR")}, + {'P', QLatin1String("PROJECT_VERSION_PATCH")}, + {'P', QLatin1String("PROJECT_VERSION_TWEAK")}, + {'B', QLatin1String("BUILD_SHARED_LIBS")}, + {'C', QLatin1String("CMAKE_ABSOLUTE_DESTINATION_FILES")}, + {'C', QLatin1String("CMAKE_APPBUNDLE_PATH")}, + {'C', QLatin1String("CMAKE_AUTOMOC_RELAXED_MODE")}, + {'C', QLatin1String("CMAKE_BACKWARDS_COMPATIBILITY")}, + {'C', QLatin1String("CMAKE_BUILD_TYPE")}, + {'C', QLatin1String("CMAKE_COLOR_MAKEFILE")}, + {'C', QLatin1String("CMAKE_CONFIGURATION_TYPES")}, + {'C', QLatin1String("CMAKE_DEBUG_TARGET_PROPERTIES")}, + {'C', QLatin1String("CMAKE_ERROR_DEPRECATED")}, + {'C', QLatin1String("CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION")}, + {'C', QLatin1String("CMAKE_SYSROOT")}, + {'C', QLatin1String("CMAKE_FIND_LIBRARY_PREFIXES")}, + {'C', QLatin1String("CMAKE_FIND_LIBRARY_SUFFIXES")}, + {'C', QLatin1String("CMAKE_FIND_NO_INSTALL_PREFIX")}, + {'C', QLatin1String("CMAKE_FIND_PACKAGE_WARN_NO_MODULE")}, + {'C', QLatin1String("CMAKE_FIND_ROOT_PATH")}, + {'C', QLatin1String("CMAKE_FIND_ROOT_PATH_MODE_INCLUDE")}, + {'C', QLatin1String("CMAKE_FIND_ROOT_PATH_MODE_LIBRARY")}, + {'C', QLatin1String("CMAKE_FIND_ROOT_PATH_MODE_PACKAGE")}, + {'C', QLatin1String("CMAKE_FIND_ROOT_PATH_MODE_PROGRAM")}, + {'C', QLatin1String("CMAKE_FRAMEWORK_PATH")}, + {'C', QLatin1String("CMAKE_IGNORE_PATH")}, + {'C', QLatin1String("CMAKE_INCLUDE_PATH")}, + {'C', QLatin1String("CMAKE_INCLUDE_DIRECTORIES_BEFORE")}, + {'C', QLatin1String("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE")}, + {'C', QLatin1String("CMAKE_INSTALL_DEFAULT_COMPONENT_NAME")}, + {'C', QLatin1String("CMAKE_INSTALL_PREFIX")}, + {'C', QLatin1String("CMAKE_LIBRARY_PATH")}, + {'C', QLatin1String("CMAKE_MFC_FLAG")}, + {'C', QLatin1String("CMAKE_MODULE_PATH")}, + {'C', QLatin1String("CMAKE_NOT_USING_CONFIG_FLAGS")}, + {'C', QLatin1String("CMAKE_PREFIX_PATH")}, + {'C', QLatin1String("CMAKE_PROGRAM_PATH")}, + {'C', QLatin1String("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY")}, + {'C', QLatin1String("CMAKE_STAGING_PREFIX")}, + {'C', QLatin1String("CMAKE_SYSTEM_IGNORE_PATH")}, + {'C', QLatin1String("CMAKE_SYSTEM_INCLUDE_PATH")}, + {'C', QLatin1String("CMAKE_SYSTEM_LIBRARY_PATH")}, + {'C', QLatin1String("CMAKE_SYSTEM_PREFIX_PATH")}, + {'C', QLatin1String("CMAKE_SYSTEM_PROGRAM_PATH")}, + {'C', QLatin1String("CMAKE_USER_MAKE_RULES_OVERRIDE")}, + {'C', QLatin1String("CMAKE_WARN_DEPRECATED")}, + {'C', QLatin1String("CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION")}, + {'A', QLatin1String("APPLE")}, + {'B', QLatin1String("BORLAND")}, + {'C', QLatin1String("CMAKE_CL_64")}, + {'C', QLatin1String("CMAKE_COMPILER_2005")}, + {'C', QLatin1String("CMAKE_HOST_APPLE")}, + {'C', QLatin1String("CMAKE_HOST_SYSTEM_NAME")}, + {'C', QLatin1String("CMAKE_HOST_SYSTEM_PROCESSOR")}, + {'C', QLatin1String("CMAKE_HOST_SYSTEM")}, + {'C', QLatin1String("CMAKE_HOST_SYSTEM_VERSION")}, + {'C', QLatin1String("CMAKE_HOST_UNIX")}, + {'C', QLatin1String("CMAKE_HOST_WIN32")}, + {'C', QLatin1String("CMAKE_LIBRARY_ARCHITECTURE_REGEX")}, + {'C', QLatin1String("CMAKE_LIBRARY_ARCHITECTURE")}, + {'C', QLatin1String("CMAKE_OBJECT_PATH_MAX")}, + {'C', QLatin1String("CMAKE_SYSTEM_NAME")}, + {'C', QLatin1String("CMAKE_SYSTEM_PROCESSOR")}, + {'C', QLatin1String("CMAKE_SYSTEM")}, + {'C', QLatin1String("CMAKE_SYSTEM_VERSION")}, + {'C', QLatin1String("CYGWIN")}, + {'E', QLatin1String("ENV")}, + {'M', QLatin1String("MSVC10")}, + {'M', QLatin1String("MSVC11")}, + {'M', QLatin1String("MSVC12")}, + {'M', QLatin1String("MSVC60")}, + {'M', QLatin1String("MSVC70")}, + {'M', QLatin1String("MSVC71")}, + {'M', QLatin1String("MSVC80")}, + {'M', QLatin1String("MSVC90")}, + {'M', QLatin1String("MSVC_IDE")}, + {'M', QLatin1String("MSVC")}, + {'M', QLatin1String("MSVC_VERSION")}, + {'U', QLatin1String("UNIX")}, + {'W', QLatin1String("WIN32")}, + {'X', QLatin1String("XCODE_VERSION")}, + {'C', QLatin1String("CMAKE_ARCHIVE_OUTPUT_DIRECTORY")}, + {'C', QLatin1String("CMAKE_AUTOMOC_MOC_OPTIONS")}, + {'C', QLatin1String("CMAKE_AUTOMOC")}, + {'C', QLatin1String("CMAKE_AUTORCC")}, + {'C', QLatin1String("CMAKE_AUTORCC_OPTIONS")}, + {'C', QLatin1String("CMAKE_AUTOUIC")}, + {'C', QLatin1String("CMAKE_AUTOUIC_OPTIONS")}, + {'C', QLatin1String("CMAKE_BUILD_WITH_INSTALL_RPATH")}, + {'C', QLatin1String("CMAKE_DEBUG_POSTFIX")}, + {'C', QLatin1String("CMAKE_EXE_LINKER_FLAGS")}, + {'C', QLatin1String("CMAKE_Fortran_FORMAT")}, + {'C', QLatin1String("CMAKE_Fortran_MODULE_DIRECTORY")}, + {'C', QLatin1String("CMAKE_GNUtoMS")}, + {'C', QLatin1String("CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE")}, + {'C', QLatin1String("CMAKE_INCLUDE_CURRENT_DIR")}, + {'C', QLatin1String("CMAKE_INSTALL_NAME_DIR")}, + {'C', QLatin1String("CMAKE_INSTALL_RPATH")}, + {'C', QLatin1String("CMAKE_INSTALL_RPATH_USE_LINK_PATH")}, + {'C', QLatin1String("CMAKE_LIBRARY_OUTPUT_DIRECTORY")}, + {'C', QLatin1String("CMAKE_LIBRARY_PATH_FLAG")}, + {'C', QLatin1String("CMAKE_LINK_DEF_FILE_FLAG")}, + {'C', QLatin1String("CMAKE_LINK_DEPENDS_NO_SHARED")}, + {'C', QLatin1String("CMAKE_LINK_INTERFACE_LIBRARIES")}, + {'C', QLatin1String("CMAKE_LINK_LIBRARY_FILE_FLAG")}, + {'C', QLatin1String("CMAKE_LINK_LIBRARY_FLAG")}, + {'C', QLatin1String("CMAKE_MACOSX_BUNDLE")}, + {'C', QLatin1String("CMAKE_MACOSX_RPATH")}, + {'C', QLatin1String("CMAKE_MODULE_LINKER_FLAGS")}, + {'C', QLatin1String("CMAKE_NO_BUILTIN_CHRPATH")}, + {'C', QLatin1String("CMAKE_NO_SYSTEM_FROM_IMPORTED")}, + {'C', QLatin1String("CMAKE_OSX_ARCHITECTURES")}, + {'C', QLatin1String("CMAKE_OSX_DEPLOYMENT_TARGET")}, + {'C', QLatin1String("CMAKE_OSX_SYSROOT")}, + {'C', QLatin1String("CMAKE_PDB_OUTPUT_DIRECTORY")}, + {'C', QLatin1String("CMAKE_POSITION_INDEPENDENT_CODE")}, + {'C', QLatin1String("CMAKE_RUNTIME_OUTPUT_DIRECTORY")}, + {'C', QLatin1String("CMAKE_SHARED_LINKER_FLAGS")}, + {'C', QLatin1String("CMAKE_SKIP_BUILD_RPATH")}, + {'C', QLatin1String("CMAKE_SKIP_INSTALL_RPATH")}, + {'C', QLatin1String("CMAKE_STATIC_LINKER_FLAGS")}, + {'C', QLatin1String("CMAKE_TRY_COMPILE_CONFIGURATION")}, + {'C', QLatin1String("CMAKE_USE_RELATIVE_PATHS")}, + {'C', QLatin1String("CMAKE_VISIBILITY_INLINES_HIDDEN")}, + {'C', QLatin1String("CMAKE_WIN32_EXECUTABLE")}, + {'E', QLatin1String("EXECUTABLE_OUTPUT_PATH")}, + {'L', QLatin1String("LIBRARY_OUTPUT_PATH")}, + {'C', QLatin1String("CMAKE_Fortran_MODDIR_DEFAULT")}, + {'C', QLatin1String("CMAKE_Fortran_MODDIR_FLAG")}, + {'C', QLatin1String("CMAKE_Fortran_MODOUT_FLAG")}, + {'C', QLatin1String("CMAKE_INTERNAL_PLATFORM_ABI")}, + {'C', QLatin1String("CPACK_ABSOLUTE_DESTINATION_FILES")}, + {'C', QLatin1String("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")}, + {'C', QLatin1String("CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION")}, + {'C', QLatin1String("CPACK_INCLUDE_TOPLEVEL_DIRECTORY")}, + {'C', QLatin1String("CPACK_INSTALL_SCRIPT")}, + {'C', QLatin1String("CPACK_PACKAGING_INSTALL_PREFIX")}, + {'C', QLatin1String("CPACK_SET_DESTDIR")}, + {'C', QLatin1String("CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION")}}; +} + +void loadCMakeData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!cmakeDataInitialized) { + initCMakeData(); + cmakeDataInitialized = true; + } + types = cmake_types; + keywords = cmake_keywords; + builtin = cmake_builtin; + literals = cmake_literals; + other = cmake_other; +} + +/********************************************************/ +/*** MAKE DATA **************************************/ +/********************************************************/ +static bool makeDataInitialized = false; +static QMultiHash make_keywords; +static QMultiHash make_types; +static QMultiHash make_literals; +static QMultiHash make_builtin; +static QMultiHash make_other; +void initMakeData() { + make_keywords = { + {'i', QLatin1String("include")}, {'d', QLatin1String("define")}, + {'e', QLatin1String("else")}, {'e', QLatin1String("endef")}, + {'e', QLatin1String("endif")}, {'e', QLatin1String("export")}, + {'i', QLatin1String("ifn?def")}, {'i', QLatin1String("ifn?eq")}, + {'i', QLatin1String("include")}, {'o', QLatin1String("override")}, + {'p', QLatin1String("private")}, {'s', QLatin1String("sinclude")}, + {'u', QLatin1String("undefine")}, {'u', QLatin1String("unexport")}, + {'v', QLatin1String("vpath")}}; + make_types = { + {'a', QLatin1String("addsuffix")}, {'a', QLatin1String("abspath")}, + {'a', QLatin1String("and")}, {'a', QLatin1String("ar")}, + {'b', QLatin1String("basename")}, {'c', QLatin1String("call")}, + {'d', QLatin1String("dir")}, {'e', QLatin1String("error")}, + {'e', QLatin1String("eval")}, {'f', QLatin1String("file")}, + {'f', QLatin1String("filter")}, {'f', QLatin1String("find")}, + {'f', QLatin1String("findstring")}, {'f', QLatin1String("firstword")}, + {'f', QLatin1String("flavor")}, {'f', QLatin1String("foreach")}, + {'g', QLatin1String("guile")}, {'i', QLatin1String("if")}, + {'i', QLatin1String("info")}, {'i', QLatin1String("install")}, + {'j', QLatin1String("join")}, {'l', QLatin1String("lastword")}, + {'l', QLatin1String("load")}, {'n', QLatin1String("notdir")}, + {'o', QLatin1String("or")}, {'o', QLatin1String("origin")}, + {'p', QLatin1String("patsubst")}, {'r', QLatin1String("ranlib")}, + {'r', QLatin1String("realpath")}, {'r', QLatin1String("rm")}, + {'s', QLatin1String("shell")}, {'s', QLatin1String("sort")}, + {'s', QLatin1String("strip")}, {'s', QLatin1String("subst")}, + {'s', QLatin1String("suffix")}, {'v', QLatin1String("value")}, + {'w', QLatin1String("warning")}, {'w', QLatin1String("wildcard")}, + {'w', QLatin1String("word")}}; + make_literals = { + {'t', QLatin1String("true")}, + {'f', QLatin1String("false")}, + }; + make_builtin = {}; + make_other = { + {'C', QLatin1String("CFLAGS")}, + {'L', QLatin1String("LIBS")}, + {'P', QLatin1String("PREFIX")}, + }; +} + +void loadMakeData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!makeDataInitialized) { + initMakeData(); + makeDataInitialized = true; + } + types = make_types; + keywords = make_keywords; + builtin = make_builtin; + literals = make_literals; + other = make_other; +} + +/********************************************************/ +/*** Forth DATA *************************************/ +/********************************************************/ +static bool forthDataInitialized = false; +static QMultiHash forth_keywords; +static QMultiHash forth_types; +static QMultiHash forth_builtin; +static QMultiHash forth_literals; +static QMultiHash forth_other; +void initForthData() { + forth_keywords = { + {('d'), QLatin1String("disasm")}, + {('d'), QLatin1String("disassembler")}, + {('b'), QLatin1String("base-addr")}, + {('s'), QLatin1String("show-name")}, + {('d'), QLatin1String("default-32bit")}, + {('d'), QLatin1String("default-16bit")}, + {('d'), QLatin1String("default-16bit?")}, + {('c'), QLatin1String("col")}, + {('w'), QLatin1String("w@")}, + {('('), QLatin1String("(D.)")}, + {('M'), QLatin1String("MAXCOUNTED")}, + {('S'), QLatin1String("SPCS")}, + {('S'), QLatin1String("SPCS-MAX")}, + {('m'), QLatin1String("maxstring")}, + {('d'), QLatin1String("dffield:")}, + {('s'), QLatin1String("sffield:")}, + {('f'), QLatin1String("ffield:")}, + {('2'), QLatin1String("2field:")}, + {('f'), QLatin1String("field:")}, + {('c'), QLatin1String("cfield:")}, + {('e'), QLatin1String("end-structure")}, + {('b'), QLatin1String("begin-structure")}, + {('+'), QLatin1String("+field")}, + {('i'), QLatin1String("init-libcc")}, + {('e'), QLatin1String("end-c-library")}, + {('c'), QLatin1String("c-library")}, + {('c'), QLatin1String("c-library-name")}, + {('c'), QLatin1String("c-library-incomplete")}, + {('c'), QLatin1String("clear-libs")}, + {('c'), QLatin1String("c-function")}, + {('c'), QLatin1String("c-function-rt")}, + {('c'), QLatin1String("c-function-ft")}, + {('l'), QLatin1String("link-wrapper-function")}, + {('c'), QLatin1String("compile-wrapper-function1")}, + {('c'), QLatin1String("compile-wrapper-function")}, + {('.'), QLatin1String(".lib-error")}, + {('c'), QLatin1String("c-source-file-execute")}, + {('n'), QLatin1String("notype-execute")}, + {('c'), QLatin1String("c-source-file")}, + {('i'), QLatin1String("init-c-source-file")}, + {('l'), QLatin1String("lib-handle")}, + {('c'), QLatin1String("c-tmp-library-name")}, + {('c'), QLatin1String("c-named-library-name")}, + {('c'), QLatin1String("c-library-name-create")}, + {('c'), QLatin1String("c-library-name-setup")}, + {('o'), QLatin1String("open-wrappers")}, + {('p'), QLatin1String("prepend-dirname")}, + {('l'), QLatin1String("libcc-tmp-dir")}, + {('l'), QLatin1String("libcc-named-dir")}, + {('g'), QLatin1String("gen-filename")}, + {('b'), QLatin1String("basename")}, + {('d'), QLatin1String("dirname")}, + {('s'), QLatin1String("scan-back")}, + {('g'), QLatin1String("gen-wrapper-function")}, + {('w'), QLatin1String("wrapper-function-name")}, + {('g'), QLatin1String("gen-wrapped-stmt")}, + {('g'), QLatin1String("gen-wrapped-types")}, + {('g'), QLatin1String("gen-wrapped-func")}, + {('g'), QLatin1String("gen-wrapped-r")}, + {('g'), QLatin1String("gen-wrapped-d")}, + {('g'), QLatin1String("gen-wrapped-a")}, + {('g'), QLatin1String("gen-wrapped-n")}, + {('g'), QLatin1String("gen-wrapped-void")}, + {('g'), QLatin1String("gen-wrapped-call")}, + {('g'), QLatin1String("gen-par")}, + {('g'), QLatin1String("gen-par-types")}, + {('g'), QLatin1String("gen-par-void")}, + {('g'), QLatin1String("gen-par-func")}, + {('g'), QLatin1String("gen-par-r")}, + {('g'), QLatin1String("gen-par-d")}, + {('g'), QLatin1String("gen-par-a")}, + {('g'), QLatin1String("gen-par-n")}, + {('c'), QLatin1String("count-stacks")}, + {('c'), QLatin1String("count-stacks-types")}, + {('c'), QLatin1String("count-stacks-void")}, + {('c'), QLatin1String("count-stacks-func")}, + {('c'), QLatin1String("count-stacks-r")}, + {('c'), QLatin1String("count-stacks-d")}, + {('c'), QLatin1String("count-stacks-a")}, + {('c'), QLatin1String("count-stacks-n")}, + {('t'), QLatin1String("type-letter")}, + {('p'), QLatin1String("parse-function-types")}, + {('p'), QLatin1String("parse-libcc-type")}, + {('l'), QLatin1String("libcc-types")}, + {('\\'), QLatin1String("\\c")}, + {('s'), QLatin1String("save-c-prefix-line")}, + {('p'), QLatin1String("print-c-prefix-lines")}, + {('p'), QLatin1String("print-c-prefix-line")}, + {('c'), QLatin1String("c-prefix-lines-end")}, + {('c'), QLatin1String("c-prefix-lines")}, + {('c'), QLatin1String("c-prefix%")}, + {('c'), QLatin1String("c-prefix-chars")}, + {('c'), QLatin1String("c-prefix-count")}, + {('a'), QLatin1String("append-l")}, + {('a'), QLatin1String("add-lib")}, + {('c'), QLatin1String("c-libs")}, + {('c'), QLatin1String("c-lib%")}, + {('c'), QLatin1String("c-lib-string")}, + {('l'), QLatin1String("list-map")}, + {('l'), QLatin1String("list-append")}, + {('l'), QLatin1String("list-insert")}, + {('l'), QLatin1String("list%")}, + {('l'), QLatin1String("list-payload")}, + {('l'), QLatin1String("list-next")}, + {('a'), QLatin1String("append")}, + {('s'), QLatin1String("s+")}, + {('f'), QLatin1String("front-char")}, + {('f'), QLatin1String("front-string")}, + {('c'), QLatin1String("const+")}, + {('.'), QLatin1String(".nb")}, + {('r'), QLatin1String("replace-rpath")}, + {('l'), QLatin1String("libcc-path")}, + {('l'), QLatin1String("libcc-named-dir-v")}, + {('l'), QLatin1String("lib-modulename")}, + {('l'), QLatin1String("lib-filename")}, + {('l'), QLatin1String("lib-handle-addr")}, + {('c'), QLatin1String("c-source-file-id")}, + {('c'), QLatin1String("cff%")}, + {('c'), QLatin1String("cff-ptypes")}, + {('c'), QLatin1String("cff-np")}, + {('c'), QLatin1String("cff-rtype")}, + {('c'), QLatin1String("cff-lha")}, + {('c'), QLatin1String("cff-deferred")}, + {('c'), QLatin1String("cff-cfr")}, + {('m'), QLatin1String("mkdir-parents")}, + {('d'), QLatin1String("disasm-gdb")}, + {('a'), QLatin1String("append-extend-string")}, + {('e'), QLatin1String("end-code")}, + {(';'), QLatin1String(";code")}, + {('('), QLatin1String("(;code)")}, + {('c'), QLatin1String("code")}, + {('i'), QLatin1String("init-asm")}, + {('a'), QLatin1String("assembler")}, + {('b'), QLatin1String("break\"")}, + {('('), QLatin1String("(break\")")}, + {('b'), QLatin1String("break:")}, + {('('), QLatin1String("(break:)")}, + {('b'), QLatin1String("break:,")}, + {('d'), QLatin1String("dbg")}, + {('('), QLatin1String("(debug)")}, + {('('), QLatin1String("(_debug)")}, + {('D'), QLatin1String("D-KEY")}, + {('U'), QLatin1String("Unnest")}, + {('N'), QLatin1String("Nesting")}, + {('n'), QLatin1String("nestXT")}, + {('n'), QLatin1String("nestXT-checkSpecial")}, + {('B'), QLatin1String("Body")}, + {('r'), QLatin1String("restore-bp")}, + {('s'), QLatin1String("set-bp")}, + {('D'), QLatin1String("DT")}, + {('B'), QLatin1String("BP")}, + {('b'), QLatin1String("breaker")}, + {('b'), QLatin1String("breaker-size")}, + {('D'), QLatin1String("DebugLoop")}, + {('j'), QLatin1String("jump")}, + {('g'), QLatin1String("get-next")}, + {('d'), QLatin1String("disp-step")}, + {('L'), QLatin1String("Leave-D")}, + {('N'), QLatin1String("NoFine")}, + {('d'), QLatin1String("d.s")}, + {('.'), QLatin1String(".n")}, + {('s'), QLatin1String("scanword")}, + {('r'), QLatin1String("restore-see-flags")}, + {('s'), QLatin1String("save-see-flags")}, + {('d'), QLatin1String("dbg-ip")}, + {('s'), QLatin1String("see-code")}, + {('s'), QLatin1String("see-code-range")}, + {('s'), QLatin1String("see-code-next-inline")}, + {('s'), QLatin1String("simple-see")}, + {('s'), QLatin1String("simple-see-range")}, + {('s'), QLatin1String("simple-see-word")}, + {('p'), QLatin1String("print-backtrace")}, + {('p'), QLatin1String("print-bt-entry")}, + {('b'), QLatin1String("backtrace-return-stack")}, + {('i'), QLatin1String("init-backtrace")}, + {('b'), QLatin1String("backtrace-rs-buffer")}, + {('a'), QLatin1String("adjust-buffer")}, + {('i'), QLatin1String("init-buffer")}, + {('b'), QLatin1String("buffer%")}, + {('b'), QLatin1String("buffer-maxlength")}, + {('b'), QLatin1String("buffer-address")}, + {('b'), QLatin1String("buffer-length")}, + {('b'), QLatin1String("buffer-descriptor")}, + {('c'), QLatin1String("c-extend1")}, + {('c'), QLatin1String("c-lp+!#")}, + {('c'), QLatin1String("c-laddr#")}, + {('c'), QLatin1String("c-f@local#")}, + {('c'), QLatin1String("c-flit")}, + {('c'), QLatin1String("c-@local#")}, + {('c'), QLatin1String("c-branch-lp+!#")}, + {('c'), QLatin1String("c-?branch-lp+!#")}, + {('c'), QLatin1String("c-loop-lp+!#")}, + {('s'), QLatin1String("see")}, + {('n'), QLatin1String("name-see")}, + {('('), QLatin1String("(.immediate)")}, + {('('), QLatin1String("(xt-see-xt)")}, + {('x'), QLatin1String("xt-see")}, + {('s'), QLatin1String("seefield")}, + {('s'), QLatin1String("seecol")}, + {('s'), QLatin1String("seedoes")}, + {('s'), QLatin1String("see-threaded")}, + {('s'), QLatin1String("seedefer")}, + {('s'), QLatin1String("seevalue")}, + {('s'), QLatin1String("seecon")}, + {('s'), QLatin1String("seeuser")}, + {('s'), QLatin1String("seevar")}, + {('s'), QLatin1String("seecode")}, + {('n'), QLatin1String("next-prim")}, + {('n'), QLatin1String("next-head")}, + {('d'), QLatin1String("discode")}, + {('.'), QLatin1String(".defname")}, + {('x'), QLatin1String("xt-see-xt")}, + {('m'), QLatin1String("makepass")}, + {('c'), QLatin1String("c-init")}, + {('a'), QLatin1String("analyse")}, + {('B'), QLatin1String("BranchTo?")}, + {('D'), QLatin1String("DoTable")}, + {('c'), QLatin1String("c-extender")}, + {('C'), QLatin1String("C-Table")}, + {('c'), QLatin1String("c-(compile)")}, + {('c'), QLatin1String("c-does>")}, + {('c'), QLatin1String("c-abort\"")}, + {('c'), QLatin1String("c-exit")}, + {('c'), QLatin1String("c-?do")}, + {('c'), QLatin1String("c-do")}, + {('c'), QLatin1String("c-loop")}, + {('c'), QLatin1String("c-for")}, + {('c'), QLatin1String("c-?branch")}, + {('D'), QLatin1String("DebugBranch")}, + {('c'), QLatin1String("c-branch")}, + {('R'), QLatin1String("RepeatCheck")}, + {('F'), QLatin1String("Forward?")}, + {('c'), QLatin1String("c-string?")}, + {('c'), QLatin1String("c-c\"")}, + {('.'), QLatin1String(".name-without")}, + {('c'), QLatin1String("c-lit+")}, + {('c'), QLatin1String("c-lit")}, + {('c'), QLatin1String("c-callxt")}, + {('c'), QLatin1String("c-call")}, + {('.'), QLatin1String(".word")}, + {('b'), QLatin1String("back?")}, + {('D'), QLatin1String("Debug?")}, + {('D'), QLatin1String("Display?")}, + {('S'), QLatin1String("Scan?")}, + {('D'), QLatin1String("DebugMode")}, + {('D'), QLatin1String("DisplayMode")}, + {('S'), QLatin1String("ScanMode")}, + {('C'), QLatin1String("C-Pass")}, + {('N'), QLatin1String("NoOutput")}, + {('B'), QLatin1String("Branch!")}, + {('T'), QLatin1String("Type!")}, + {(','), QLatin1String(",Branch")}, + {('C'), QLatin1String("CheckWhile")}, + {('M'), QLatin1String("MyBranch")}, + {('C'), QLatin1String("CheckEnd")}, + {('M'), QLatin1String("MoreBranchAddr?")}, + {('B'), QLatin1String("BranchAddr?")}, + {('('), QLatin1String("(BranchAddr?)")}, + {('F'), QLatin1String("FirstBranch")}, + {('M'), QLatin1String("MaxTable")}, + {('B'), QLatin1String("BranchTable")}, + {('S'), QLatin1String("SearchPointer")}, + {('B'), QLatin1String("BranchPointer")}, + {('B'), QLatin1String("Branches")}, + {('C'), QLatin1String("C-Stop")}, + {('L'), QLatin1String("LeaveCode")}, + {('D'), QLatin1String("Disable")}, + {('W'), QLatin1String("WhileCode2")}, + {('A'), QLatin1String("AheadCode")}, + {('E'), QLatin1String("ElseCode")}, + {('U'), QLatin1String("UntilCode")}, + {('A'), QLatin1String("AgainCode")}, + {('R'), QLatin1String("RepeatCode")}, + {('.'), QLatin1String(".struc")}, + {('c'), QLatin1String("c-\\type")}, + {('('), QLatin1String("(.string)")}, + {('.'), QLatin1String(".string")}, + {('c'), QLatin1String("cemit")}, + {('c'), QLatin1String("ctype")}, + {('w'), QLatin1String("warp?")}, + {('('), QLatin1String("(nl)")}, + {('n'), QLatin1String("nl")}, + {('n'), QLatin1String("nlcount")}, + {('u'), QLatin1String("uppercase")}, + {('n'), QLatin1String("nlflag")}, + {('l'), QLatin1String("level-")}, + {('l'), QLatin1String("level+")}, + {('F'), QLatin1String("Format")}, + {('L'), QLatin1String("Level")}, + {('Y'), QLatin1String("YPos")}, + {('X'), QLatin1String("XPos")}, + {('C'), QLatin1String("C-Clearline")}, + {('C'), QLatin1String("C-Highlight")}, + {('C'), QLatin1String("C-Formated")}, + {('C'), QLatin1String("C-Output")}, + {('.'), QLatin1String(".\"")}, + {('s'), QLatin1String("s\"")}, + {('\\'), QLatin1String("\\\"-parse")}, + {('\\'), QLatin1String("\\-escape")}, + {('\\'), QLatin1String("\\-escape-table")}, + {('p'), QLatin1String("parse-num")}, + {('p'), QLatin1String("parse-num-x")}, + {('c'), QLatin1String("char/")}, + {('e'), QLatin1String("ekey?")}, + {('e'), QLatin1String("ekey>fkey")}, + {('e'), QLatin1String("ekey>char")}, + {('e'), QLatin1String("ekey")}, + {('c'), QLatin1String("clear-ekey-buffer")}, + {('e'), QLatin1String("esc-sequence")}, + {('e'), QLatin1String("esc-prefix")}, + {('e'), QLatin1String("ekey-buffered")}, + {('e'), QLatin1String("ekey-buffer")}, + {('e'), QLatin1String("esc-sequences")}, + {('u'), QLatin1String("unkeys")}, + {('u'), QLatin1String("unkey")}, + {('c'), QLatin1String("char-append-buffer")}, + {('k'), QLatin1String("key-buffered")}, + {('k'), QLatin1String("key-buffer")}, + {('s'), QLatin1String("s-k12")}, + {('s'), QLatin1String("s-k11")}, + {('s'), QLatin1String("s-k10")}, + {('s'), QLatin1String("s-k9")}, + {('s'), QLatin1String("s-k8")}, + {('s'), QLatin1String("s-k7")}, + {('s'), QLatin1String("s-k6")}, + {('s'), QLatin1String("s-k5")}, + {('s'), QLatin1String("s-k4")}, + {('s'), QLatin1String("s-k3")}, + {('s'), QLatin1String("s-k2")}, + {('s'), QLatin1String("s-k1")}, + {('k'), QLatin1String("k12")}, + {('k'), QLatin1String("k11")}, + {('k'), QLatin1String("k10")}, + {('k'), QLatin1String("k9")}, + {('k'), QLatin1String("k8")}, + {('k'), QLatin1String("k7")}, + {('k'), QLatin1String("k6")}, + {('k'), QLatin1String("k5")}, + {('k'), QLatin1String("k4")}, + {('k'), QLatin1String("k3")}, + {('k'), QLatin1String("k2")}, + {('k'), QLatin1String("k1")}, + {('k'), QLatin1String("k-f12")}, + {('k'), QLatin1String("k-f11")}, + {('k'), QLatin1String("k-f10")}, + {('k'), QLatin1String("k-f9")}, + {('k'), QLatin1String("k-f8")}, + {('k'), QLatin1String("k-f7")}, + {('k'), QLatin1String("k-f6")}, + {('k'), QLatin1String("k-f5")}, + {('k'), QLatin1String("k-f4")}, + {('k'), QLatin1String("k-f3")}, + {('k'), QLatin1String("k-f2")}, + {('k'), QLatin1String("k-f1")}, + {('k'), QLatin1String("k-delete")}, + {('k'), QLatin1String("k-insert")}, + {('k'), QLatin1String("k-next")}, + {('k'), QLatin1String("k-prior")}, + {('k'), QLatin1String("k-end")}, + {('k'), QLatin1String("k-home")}, + {('k'), QLatin1String("k-down")}, + {('k'), QLatin1String("k-up")}, + {('k'), QLatin1String("k-right")}, + {('k'), QLatin1String("k-left")}, + {('k'), QLatin1String("k-alt-mask")}, + {('k'), QLatin1String("k-ctrl-mask")}, + {('k'), QLatin1String("k-shift-mask")}, + {('k'), QLatin1String("keycode")}, + {('t'), QLatin1String("table")}, + {('t'), QLatin1String("tablesearch-map")}, + {('t'), QLatin1String("table-find")}, + {('s'), QLatin1String("savesystem")}, + {('d'), QLatin1String("dump-fi")}, + {('u'), QLatin1String("update-image-included-files")}, + {('d'), QLatin1String("delete-prefix")}, + {('s'), QLatin1String("save-mem-dict")}, + {('<'), QLatin1String("")}, + {('('), QLatin1String("(compilation>1)")}, + {('<'), QLatin1String("")}, + {('('), QLatin1String("(interpretation>1)")}, + {('f'), QLatin1String("fix-does-code")}, + {('c'), QLatin1String("create-interpret/compile")}, + {('n'), QLatin1String("no-interpretation-does-code")}, + {('n'), QLatin1String("no-compilation-does-code")}, + {('b'), QLatin1String("bye")}, + {('b'), QLatin1String("block-included")}, + {('-'), QLatin1String("-->")}, + {('+'), QLatin1String("+thru")}, + {('+'), QLatin1String("+load")}, + {('t'), QLatin1String("thru")}, + {('l'), QLatin1String("load")}, + {('b'), QLatin1String("block-input")}, + {('l'), QLatin1String("list")}, + {('u'), QLatin1String("updated?")}, + {('s'), QLatin1String("scr")}, + {('b'), QLatin1String("buffer")}, + {('b'), QLatin1String("block")}, + {('g'), QLatin1String("get-buffer")}, + {('f'), QLatin1String("flush")}, + {('e'), QLatin1String("empty-buffers")}, + {('s'), QLatin1String("save-buffers")}, + {('e'), QLatin1String("empty-buffer")}, + {('s'), QLatin1String("save-buffer")}, + {('u'), QLatin1String("update")}, + {('b'), QLatin1String("block-position")}, + {('g'), QLatin1String("get-block-fid")}, + {('u'), QLatin1String("use")}, + {('o'), QLatin1String("open-blocks")}, + {('f'), QLatin1String("flush-blocks")}, + {('b'), QLatin1String("block-cold")}, + {('o'), QLatin1String("offset")}, + {('b'), QLatin1String("block-offset")}, + {('b'), QLatin1String("block-fid")}, + {('b'), QLatin1String("block-limit")}, + {('b'), QLatin1String("buffers")}, + {('l'), QLatin1String("last-block")}, + {('b'), QLatin1String("block-buffers")}, + {('b'), QLatin1String("buffer-struct")}, + {('n'), QLatin1String("next-buffer")}, + {('b'), QLatin1String("block-buffer")}, + {('b'), QLatin1String("buffer-dirty")}, + {('b'), QLatin1String("buffer-fid")}, + {('b'), QLatin1String("buffer-block")}, + {(')'), QLatin1String(")")}, + {('('), QLatin1String("(end-assert)")}, + {('a'), QLatin1String("assert(")}, + {('a'), QLatin1String("assert3(")}, + {('a'), QLatin1String("assert2(")}, + {('a'), QLatin1String("assert1(")}, + {('a'), QLatin1String("assert0(")}, + {('a'), QLatin1String("assertn")}, + {('a'), QLatin1String("assert-level")}, + {('~'), QLatin1String("~~")}, + {('.'), QLatin1String(".debugline-stderr")}, + {('('), QLatin1String("(.debugline)")}, + {('.'), QLatin1String(".debugline")}, + {('p'), QLatin1String("printdebugdata")}, + {('.'), QLatin1String(".sourcepos")}, + {('c'), QLatin1String("compile-sourcepos")}, + {('c'), QLatin1String("current-sourcepos")}, + {('s'), QLatin1String("str>loadfilename#")}, + {('l'), QLatin1String("loadfilename#>str")}, + {('v'), QLatin1String("vt100-decode")}, + {('t'), QLatin1String("tcode")}, + {('t'), QLatin1String("trans:")}, + {('t'), QLatin1String("transcode")}, + {('t'), QLatin1String("translate")}, + {('h'), QLatin1String("history-cold")}, + {('g'), QLatin1String("get-history")}, + {('x'), QLatin1String("xchar-history")}, + {('x'), QLatin1String("xtab-expand")}, + {('i'), QLatin1String("insert")}, + {('x'), QLatin1String("xkill-expand")}, + {('('), QLatin1String("(xenter)")}, + {('x'), QLatin1String("xclear-tib")}, + {('x'), QLatin1String("xclear-line")}, + {('x'), QLatin1String("xend-pos")}, + {('x'), QLatin1String("xfirst-pos")}, + {('x'), QLatin1String("xeof")}, + {('<'), QLatin1String("")}, + {('?'), QLatin1String("?xdel")}, + {('('), QLatin1String("(xdel)")}, + {('x'), QLatin1String("xforw")}, + {('x'), QLatin1String("xback")}, + {('('), QLatin1String("(xins)")}, + {('<'), QLatin1String("")}, + {('x'), QLatin1String("xretype")}, + {('.'), QLatin1String(".all")}, + {('.'), QLatin1String(".rest")}, + {('x'), QLatin1String("xback-restore")}, + {('x'), QLatin1String("xcur-correct")}, + {('a'), QLatin1String("at-deltaxy")}, + {('#'), QLatin1String("#esc")}, + {('k'), QLatin1String("kill-prefix")}, + {('t'), QLatin1String("tib-full?")}, + {('s'), QLatin1String("search-prefix")}, + {('p'), QLatin1String("prefix-string")}, + {('p'), QLatin1String("prefix-off")}, + {('s'), QLatin1String("search-voc")}, + {('w'), QLatin1String("word-lex")}, + {('c'), QLatin1String("capscomp")}, + {('s'), QLatin1String("sgn")}, + {('p'), QLatin1String("prefix-found")}, + {('e'), QLatin1String("extract-word")}, + {('('), QLatin1String("(enter)")}, + {('p'), QLatin1String("prev-line")}, + {('f'), QLatin1String("find-prev-line")}, + {('n'), QLatin1String("next-line")}, + {('g'), QLatin1String("get-line")}, + {('h'), QLatin1String("hist-setpos")}, + {('h'), QLatin1String("hist-pos")}, + {('c'), QLatin1String("clear-line")}, + {('l'), QLatin1String("linew-off")}, + {('s'), QLatin1String("screenw")}, + {('l'), QLatin1String("linew")}, + {('c'), QLatin1String("cur-correct")}, + {('b'), QLatin1String("back-restore")}, + {('h'), QLatin1String("history-file")}, + {('f'), QLatin1String("force-open")}, + {('e'), QLatin1String("end^")}, + {('b'), QLatin1String("backward^")}, + {('f'), QLatin1String("forward^")}, + {('h'), QLatin1String("history")}, + {('b'), QLatin1String("bindkey")}, + {('>'), QLatin1String(">string")}, + {('c'), QLatin1String("ctrl")}, + {('c'), QLatin1String("ctrl-i")}, + {('u'), QLatin1String("utf-8-cold")}, + {('s'), QLatin1String("set-encoding-utf-8")}, + {('u'), QLatin1String("u8width")}, + {('-'), QLatin1String("-u8trailing-garbage")}, + {('u'), QLatin1String("u8addrlen")}, + {('u'), QLatin1String("u8!+?")}, + {('u'), QLatin1String("u8@")}, + {('u'), QLatin1String("u8\\string-")}, + {('+'), QLatin1String("+u8/string")}, + {('u'), QLatin1String("u8emit")}, + {('u'), QLatin1String("u8key")}, + {('c'), QLatin1String("check-xy")}, + {('u'), QLatin1String("u8<<")}, + {('u'), QLatin1String("u8>>")}, + {('u'), QLatin1String("u8!+")}, + {('u'), QLatin1String("u8@+")}, + {('u'), QLatin1String("u8len")}, + {('m'), QLatin1String("max-single-byte")}, + {('U'), QLatin1String("UTF-8-err")}, + {('O'), QLatin1String("O-PNT@")}, + {('O'), QLatin1String("O-DEINIT")}, + {('O'), QLatin1String("O-INIT")}, + {('T'), QLatin1String("TypeXT")}, + {('E'), QLatin1String("EmitXT")}, + {('O'), QLatin1String("O-EMIT")}, + {('O'), QLatin1String("O-TYPE")}, + {('O'), QLatin1String("O-PNT")}, + {('O'), QLatin1String("O-Buffer")}, + {('p'), QLatin1String("page")}, + {('a'), QLatin1String("at-xy")}, + {('E'), QLatin1String("ESC[")}, + {(';'), QLatin1String(";pn")}, + {('p'), QLatin1String("pn")}, + {('W'), QLatin1String("WordInfo")}, + {('I'), QLatin1String("InfoTable")}, + {('C'), QLatin1String("Com#")}, + {('S'), QLatin1String("Str#")}, + {('A'), QLatin1String("Ali#")}, + {('U'), QLatin1String("Use#")}, + {('C'), QLatin1String("Col#")}, + {('D'), QLatin1String("Def#")}, + {('D'), QLatin1String("Doe#")}, + {('V'), QLatin1String("Val#")}, + {('V'), QLatin1String("Var#")}, + {('C'), QLatin1String("Con#")}, + {('P'), QLatin1String("Pri#")}, + {('p'), QLatin1String("prim?")}, + {('x'), QLatin1String("xtprim?")}, + {('c'), QLatin1String("colon?")}, + {('d'), QLatin1String("defered?")}, + {('d'), QLatin1String("does?")}, + {('u'), QLatin1String("user?")}, + {('c'), QLatin1String("con?")}, + {('v'), QLatin1String("var?")}, + {('a'), QLatin1String("alias?")}, + {('>'), QLatin1String(">head")}, + {('>'), QLatin1String(">name")}, + {('t'), QLatin1String("threaded>name")}, + {('l'), QLatin1String("look")}, + {('p'), QLatin1String("prim>name")}, + {('P'), QLatin1String("PrimStart")}, + {('t'), QLatin1String("threaded>xt")}, + {('s'), QLatin1String("search-name")}, + {('x'), QLatin1String("xt>threaded")}, + {('b'), QLatin1String("base-execute")}, + {('i'), QLatin1String("infile-execute")}, + {('o'), QLatin1String("outfile-execute")}, + {('l'), QLatin1String("l@")}, + {('w'), QLatin1String("w@")}, + {('/'), QLatin1String("/l")}, + {('/'), QLatin1String("/w")}, + {('t'), QLatin1String("typewhite")}, + {('w'), QLatin1String("what's")}, + {('a'), QLatin1String("action-of")}, + {('f'), QLatin1String("f.s")}, + {('f'), QLatin1String("f.rdp")}, + {('f'), QLatin1String("f>str-rdp")}, + {('f'), QLatin1String("f>buf-rdp")}, + {('f'), QLatin1String("f>buf-rdp-try")}, + {('p'), QLatin1String("push-right")}, + {(']'), QLatin1String("]]")}, + {('p'), QLatin1String("postponer1")}, + {('['), QLatin1String("[[")}, + {('c'), QLatin1String("compile-compile-2literal")}, + {('c'), QLatin1String("compile-2literal")}, + {('c'), QLatin1String("compile-compile-literal")}, + {('c'), QLatin1String("compile-literal")}, + {('s'), QLatin1String("slurp-fid")}, + {('s'), QLatin1String("slurp-file")}, + {('c'), QLatin1String("const-does>")}, + {('('), QLatin1String("(const-does>)")}, + {('c'), QLatin1String("compile-fliterals")}, + {('c'), QLatin1String("compile-literals")}, + {('i'), QLatin1String("in-return-stack?")}, + {(']'), QLatin1String("]L")}, + {('s'), QLatin1String("sh")}, + {('s'), QLatin1String("system")}, + {('$'), QLatin1String("$?")}, + {('d'), QLatin1String("dmax")}, + {('d'), QLatin1String("dmin")}, + {('?'), QLatin1String("?CSP")}, + {('!'), QLatin1String("!CSP")}, + {('C'), QLatin1String("CSP")}, + {('n'), QLatin1String("needs")}, + {('l'), QLatin1String("locals|")}, + {('T'), QLatin1String("TO")}, + {('d'), QLatin1String("definer!")}, + {('>'), QLatin1String(">definer")}, + {('('), QLatin1String("(local)")}, + {('('), QLatin1String("(exit-like)")}, + {('('), QLatin1String("(until-like)")}, + {('('), QLatin1String("(again-like)")}, + {('('), QLatin1String("(begin-like)")}, + {('('), QLatin1String("(then-like)")}, + {('l'), QLatin1String("locals-;-hook")}, + {('l'), QLatin1String("locals-:-hook")}, + {('e'), QLatin1String("endscope")}, + {('a'), QLatin1String("adjust-locals-list")}, + {('s'), QLatin1String("scope")}, + {('{'), QLatin1String("{")}, + {('o'), QLatin1String("old-dpp")}, + {('n'), QLatin1String("new-locals-wl")}, + {('n'), QLatin1String("new-locals-map")}, + {('n'), QLatin1String("new-locals-reveal")}, + {('n'), QLatin1String("new-locals-find")}, + {('s'), QLatin1String("some-wlocal")}, + {('s'), QLatin1String("some-flocal")}, + {('s'), QLatin1String("some-dlocal")}, + {('s'), QLatin1String("some-clocal")}, + {('l'), QLatin1String("locals-types")}, + {('l'), QLatin1String("lp-offset,")}, + {('l'), QLatin1String("lp-offset")}, + {('c'), QLatin1String("create-local")}, + {('c'), QLatin1String("compile-pushlocal-c")}, + {('c'), QLatin1String("compile-pushlocal-d")}, + {('c'), QLatin1String("compile-pushlocal-f")}, + {('c'), QLatin1String("check-begin")}, + {('s'), QLatin1String("set-locals-size-list")}, + {('l'), QLatin1String("list-size")}, + {('s'), QLatin1String("sub-list?")}, + {('c'), QLatin1String("common-list")}, + {('c'), QLatin1String("compile-pushlocal-w")}, + {('a'), QLatin1String("alignlp-f")}, + {('a'), QLatin1String("alignlp-w")}, + {('l'), QLatin1String("locals-dp")}, + {('l'), QLatin1String("locals-buffer")}, + {('l'), QLatin1String("locals")}, + {('a'), QLatin1String("adjust-locals-size")}, + {('c'), QLatin1String("compile-lp+!")}, + {('c'), QLatin1String("compile-f@local")}, + {('c'), QLatin1String("compile-@local")}, + {('F'), QLatin1String("FMOD")}, + {('F'), QLatin1String("FTRUNC")}, + {('f'), QLatin1String("f~")}, + {('f'), QLatin1String("f~rel")}, + {('f'), QLatin1String("f~abs")}, + {('1'), QLatin1String("1/f")}, + {('f'), QLatin1String("f2/")}, + {('f'), QLatin1String("f2*")}, + {('p'), QLatin1String("pi")}, + {('f'), QLatin1String("fvariable")}, + {('s'), QLatin1String("sfnumber")}, + {('f'), QLatin1String("fs.")}, + {('f'), QLatin1String("fe.")}, + {('f'), QLatin1String("f.")}, + {('f'), QLatin1String("f$")}, + {('-'), QLatin1String("-zeros")}, + {('z'), QLatin1String("zeros")}, + {('s'), QLatin1String("scratch")}, + {('s'), QLatin1String("set-precision")}, + {('p'), QLatin1String("precision")}, + {('F'), QLatin1String("FLiteral")}, + {('f'), QLatin1String("fdepth")}, + {('f'), QLatin1String("fconstant")}, + {('f'), QLatin1String("f,")}, + {('d'), QLatin1String("dfloat+")}, + {('s'), QLatin1String("sfloat+")}, + {('d'), QLatin1String("dfalign")}, + {('s'), QLatin1String("sfalign")}, + {('.'), QLatin1String(".words")}, + {('h'), QLatin1String("hash-cold")}, + {('m'), QLatin1String("make-hash")}, + {('('), QLatin1String("(hashsearch-map)")}, + {('h'), QLatin1String("hashdouble")}, + {('('), QLatin1String("(rehash)")}, + {('r'), QLatin1String("rehashall")}, + {('c'), QLatin1String("clearhash")}, + {('a'), QLatin1String("addall")}, + {('i'), QLatin1String("inithash")}, + {('h'), QLatin1String("hash-reveal")}, + {('('), QLatin1String("(reveal")}, + {('l'), QLatin1String("lastlink!")}, + {('h'), QLatin1String("hash-find")}, + {('b'), QLatin1String("bucket")}, + {('N'), QLatin1String("NewFix")}, + {('D'), QLatin1String("DelFix")}, + {('h'), QLatin1String("hash-alloc")}, + {('h'), QLatin1String("hashsearch-map")}, + {('H'), QLatin1String("HashTable")}, + {('H'), QLatin1String("HashPop")}, + {('H'), QLatin1String("HashIndex")}, + {('H'), QLatin1String("HashPointer")}, + {('r'), QLatin1String("revealed")}, + {('i'), QLatin1String("insRule")}, + {('h'), QLatin1String("hash")}, + {('H'), QLatin1String("Hashlen")}, + {('h'), QLatin1String("hashbits")}, + {('r'), QLatin1String("reserve-mem")}, + {('m'), QLatin1String("marker")}, + {('m'), QLatin1String("marker!")}, + {('m'), QLatin1String("marker,")}, + {('i'), QLatin1String("included-files-mark")}, + {('e'), QLatin1String("expect")}, + {('s'), QLatin1String("span")}, + {('s'), QLatin1String("search")}, + {('b'), QLatin1String("blank")}, + {('e'), QLatin1String("erase")}, + {('c'), QLatin1String("convert")}, + {('['), QLatin1String("[compile]")}, + {('C'), QLatin1String("C\"")}, + {('e'), QLatin1String("endcase")}, + {('e'), QLatin1String("endof")}, + {('o'), QLatin1String("of")}, + {('c'), QLatin1String("case")}, + {('m'), QLatin1String("m*/")}, + {('d'), QLatin1String("d>s")}, + {('.'), QLatin1String(".(")}, + {('b'), QLatin1String("broken-pipe-error")}, + {('e'), QLatin1String("exception")}, + {('n'), QLatin1String("next-exception")}, + {('e'), QLatin1String("errstring")}, + {('l'), QLatin1String("linked")}, + {('i'), QLatin1String("include-ffi.h-string")}, + {('l'), QLatin1String("libffi-present")}, + {('f'), QLatin1String("ffcall-present")}, + {('l'), QLatin1String("libtool-flags")}, + {('l'), QLatin1String("libtool-cc")}, + {('l'), QLatin1String("libtool-command")}, + {('h'), QLatin1String("has?")}, + {('$'), QLatin1String("$has?")}, + {('e'), QLatin1String("e?")}, + {('e'), QLatin1String("environment?")}, + {('e'), QLatin1String("environment-wordlist")}, + {('e'), QLatin1String("environment")}, + {('v'), QLatin1String("vocs")}, + {('o'), QLatin1String("order")}, + {('.'), QLatin1String(".voc")}, + {('.'), QLatin1String(".name")}, + {('.'), QLatin1String(".id")}, + {('i'), QLatin1String("id.")}, + {('s'), QLatin1String("seal")}, + {('s'), QLatin1String("set-order")}, + {('g'), QLatin1String("get-order")}, + {('i'), QLatin1String("init-vp")}, + {('u'), QLatin1String("update-image-order")}, + {('O'), QLatin1String("Only")}, + {('R'), QLatin1String("Root")}, + {('F'), QLatin1String("Forth")}, + {('v'), QLatin1String("vocsearch")}, + {('('), QLatin1String("(localsvocfind)")}, + {('l'), QLatin1String("locals-wordlist")}, + {('('), QLatin1String("(vocfind)")}, + {('p'), QLatin1String("previous")}, + {('a'), QLatin1String("also")}, + {('>'), QLatin1String(">order")}, + {('c'), QLatin1String("check-maxvp")}, + {('V'), QLatin1String("Vocabulary")}, + {('w'), QLatin1String("wordlist")}, + {('m'), QLatin1String("mappedwordlist")}, + {('s'), QLatin1String("slowvoc")}, + {('d'), QLatin1String("definitions")}, + {('v'), QLatin1String("vp!")}, + {('s'), QLatin1String("set-current")}, + {('g'), QLatin1String("get-current")}, + {('v'), QLatin1String("vp")}, + {('m'), QLatin1String("maxvp-limit")}, + {('m'), QLatin1String("maxvp")}, + {('%'), QLatin1String("%alloc")}, + {('%'), QLatin1String("%allocate")}, + {('%'), QLatin1String("%allot")}, + {('%'), QLatin1String("%align")}, + {('%'), QLatin1String("%size")}, + {('%'), QLatin1String("%alignment")}, + {('d'), QLatin1String("double%")}, + {('s'), QLatin1String("sfloat%")}, + {('d'), QLatin1String("dfloat%")}, + {('f'), QLatin1String("float%")}, + {('c'), QLatin1String("char%")}, + {('c'), QLatin1String("cell%")}, + {('s'), QLatin1String("struct")}, + {('e'), QLatin1String("end-struct")}, + {('f'), QLatin1String("field")}, + {('c'), QLatin1String("create-field")}, + {('f'), QLatin1String("field,")}, + {('d'), QLatin1String("dozerofield")}, + {('n'), QLatin1String("nalign")}, + {('n'), QLatin1String("naligned")}, + {('e'), QLatin1String("endtry-iferror")}, + {('e'), QLatin1String("endtry")}, + {('r'), QLatin1String("restore")}, + {('i'), QLatin1String("iferror")}, + {('h'), QLatin1String("handler-intro,")}, + {('('), QLatin1String("(endtry)")}, + {('t'), QLatin1String("try")}, + {('('), QLatin1String("(try)")}, + {('n'), QLatin1String("nothrow")}, + {('f'), QLatin1String("first-throw")}, + {('s'), QLatin1String("store-backtrace")}, + {('d'), QLatin1String("dodoes:")}, + {('d'), QLatin1String("dofield:")}, + {('d'), QLatin1String("dodefer:")}, + {('d'), QLatin1String("douser:")}, + {('d'), QLatin1String("dovar:")}, + {('d'), QLatin1String("docol:")}, + {('d'), QLatin1String("dovalue:")}, + {('d'), QLatin1String("docon:")}, + {('v'), QLatin1String("vlist")}, + {('w'), QLatin1String("words")}, + {('w'), QLatin1String("wordlist-words")}, + {('c'), QLatin1String("cols")}, + {('r'), QLatin1String("rows")}, + {('?'), QLatin1String("?")}, + {('d'), QLatin1String("dump")}, + {('.'), QLatin1String(".line")}, + {('.'), QLatin1String(".chars")}, + {('.'), QLatin1String(".4")}, + {('/'), QLatin1String("/dump")}, + {('.'), QLatin1String(".s")}, + {('m'), QLatin1String("maxdepth-.s")}, + {('['), QLatin1String("[WHILE]")}, + {('['), QLatin1String("[AGAIN]")}, + {('['), QLatin1String("[REPEAT]")}, + {('['), QLatin1String("[UNTIL]")}, + {('['), QLatin1String("[BEGIN]")}, + {('['), QLatin1String("[I]")}, + {('['), QLatin1String("[NEXT]")}, + {('['), QLatin1String("[FOR]")}, + {('['), QLatin1String("[LOOP]")}, + {('['), QLatin1String("[+LOOP]")}, + {('['), QLatin1String("[?DO]")}, + {('['), QLatin1String("[DO]")}, + {('('), QLatin1String("(i)")}, + {('['), QLatin1String("[ENDIF]")}, + {('['), QLatin1String("[THEN]")}, + {('['), QLatin1String("[ELSE]")}, + {('['), QLatin1String("[IFUNDEF]")}, + {('['), QLatin1String("[IFDEF]")}, + {('['), QLatin1String("[IF]")}, + {('['), QLatin1String("[undefined]")}, + {('d'), QLatin1String("defined")}, + {('['), QLatin1String("[defined]")}, + {('?'), QLatin1String("?if")}, + {('['), QLatin1String("[struct]-voc")}, + {('['), QLatin1String("[struct]-search")}, + {('s'), QLatin1String("scanIF")}, + {('>'), QLatin1String(">exec")}, + {('d'), QLatin1String("dummy")}, + {('c'), QLatin1String("countif")}, + {('.'), QLatin1String(".\"")}, + {('S'), QLatin1String("S\"")}, + {('a'), QLatin1String("abort\"")}, + {('S'), QLatin1String("SLiteral")}, + {('C'), QLatin1String("CLiteral")}, + {('?'), QLatin1String("?EXIT")}, + {('E'), QLatin1String("EXIT")}, + {('e'), QLatin1String("exit-like")}, + {('N'), QLatin1String("NEXT")}, + {('S'), QLatin1String("S+LOOP")}, + {('-'), QLatin1String("-LOOP")}, + {('+'), QLatin1String("+LOOP")}, + {('L'), QLatin1String("LOOP")}, + {('l'), QLatin1String("loop-like")}, + {('F'), QLatin1String("FOR")}, + {('U'), QLatin1String("U-DO")}, + {('-'), QLatin1String("-DO")}, + {('U'), QLatin1String("U+DO")}, + {('+'), QLatin1String("+DO")}, + {('?'), QLatin1String("?DO")}, + {('?'), QLatin1String("?do-like")}, + {('D'), QLatin1String("DO")}, + {('?'), QLatin1String("?LEAVE")}, + {('L'), QLatin1String("LEAVE")}, + {('D'), QLatin1String("DONE")}, + {('l'), QLatin1String("leave>")}, + {('>'), QLatin1String(">leave")}, + {('c'), QLatin1String("clear-leave-stack")}, + {('l'), QLatin1String("leave-sp")}, + {('l'), QLatin1String("leave-stack")}, + {('l'), QLatin1String("leave-stack-size")}, + {('R'), QLatin1String("REPEAT")}, + {('W'), QLatin1String("WHILE")}, + {('U'), QLatin1String("UNTIL")}, + {('u'), QLatin1String("until-like")}, + {('A'), QLatin1String("AGAIN")}, + {('a'), QLatin1String("again-like")}, + {('B'), QLatin1String("BEGIN")}, + {('b'), QLatin1String("begin-like")}, + {('E'), QLatin1String("ELSE")}, + {('E'), QLatin1String("ENDIF")}, + {('T'), QLatin1String("THEN")}, + {('c'), QLatin1String("cs>addr")}, + {('t'), QLatin1String("then-like")}, + {('?'), QLatin1String("?DUP-0=-IF")}, + {('?'), QLatin1String("?DUP-IF")}, + {('I'), QLatin1String("IF")}, + {('A'), QLatin1String("AHEAD")}, + {('Y'), QLatin1String("YET")}, + {('B'), QLatin1String("BUT")}, + {('<'), QLatin1String("'), QLatin1String(">resolve")}, + {('>'), QLatin1String(">mark")}, + {('s'), QLatin1String("sys?")}, + {('?'), QLatin1String("?struc")}, + {('o'), QLatin1String("other-control-flow")}, + {('c'), QLatin1String("cs-push-orig")}, + {('c'), QLatin1String("cs-push-part")}, + {('C'), QLatin1String("CS-ROLL")}, + {('C'), QLatin1String("CS-PICK")}, + {('c'), QLatin1String("cs-item-size")}, + {('c'), QLatin1String("cs-item?")}, + {('n'), QLatin1String("non-orig?")}, + {('s'), QLatin1String("scope?")}, + {('d'), QLatin1String("do-dest?")}, + {('d'), QLatin1String("dest?")}, + {('o'), QLatin1String("orig?")}, + {('d'), QLatin1String("def?")}, + {('s'), QLatin1String("scopestart")}, + {('d'), QLatin1String("do-dest")}, + {('d'), QLatin1String("dest")}, + {('d'), QLatin1String("dead-orig")}, + {('l'), QLatin1String("live-orig")}, + {('A'), QLatin1String("ASSUME-LIVE")}, + {('U'), QLatin1String("UNREACHABLE")}, + {('b'), QLatin1String("backedge-locals")}, + {('d'), QLatin1String("dead-code")}, + {('l'), QLatin1String("locals-list")}, + {('.'), QLatin1String(".included")}, + {('.'), QLatin1String(".strings")}, + {('r'), QLatin1String("require")}, + {('i'), QLatin1String("include")}, + {('r'), QLatin1String("required")}, + {('i'), QLatin1String("included")}, + {('i'), QLatin1String("included1")}, + {('a'), QLatin1String("add-included-file")}, + {('i'), QLatin1String("included?")}, + {('i'), QLatin1String("init-included-files")}, + {('s'), QLatin1String("sourceline#")}, + {('s'), QLatin1String("sourcefilename")}, + {('i'), QLatin1String("image-included-files")}, + {('i'), QLatin1String("included-files")}, + {('o'), QLatin1String("open-fpath-file")}, + {('o'), QLatin1String("open-path-file")}, + {('c'), QLatin1String("check-path")}, + {('o'), QLatin1String("open-ofile")}, + {('r'), QLatin1String("reworkdir")}, + {('c'), QLatin1String("compact-filename")}, + {('s'), QLatin1String("skip-..-prefixes")}, + {('p'), QLatin1String("preserve-root")}, + {('d'), QLatin1String("del-./s")}, + {('d'), QLatin1String("del-string")}, + {('e'), QLatin1String("expandtopic")}, + {('r'), QLatin1String("remove~+")}, + {('e'), QLatin1String("extractpath")}, + {('n'), QLatin1String("need/")}, + {('p'), QLatin1String("pathsep?")}, + {('t'), QLatin1String("tfile")}, + {('o'), QLatin1String("ofile")}, + {('a'), QLatin1String("absolut-path?")}, + {('.'), QLatin1String(".fpath")}, + {('.'), QLatin1String(".path")}, + {('p'), QLatin1String("previous-path")}, + {('n'), QLatin1String("next-path")}, + {('p'), QLatin1String("path>string")}, + {('f'), QLatin1String("fpath=")}, + {('p'), QLatin1String("path=")}, + {('f'), QLatin1String("fpath+")}, + {('p'), QLatin1String("path+")}, + {('o'), QLatin1String("only-path")}, + {('c'), QLatin1String("clear-path")}, + {('a'), QLatin1String("also-path")}, + {('o'), QLatin1String("os-cold")}, + {('m'), QLatin1String("make-path")}, + {('f'), QLatin1String("fpath")}, + {('+'), QLatin1String("+place")}, + {('p'), QLatin1String("path-allot")}, + {('('), QLatin1String("(")}, + {('w'), QLatin1String("write-line")}, + {('b'), QLatin1String("bin")}, + {('r'), QLatin1String("r/o")}, + {('r'), QLatin1String("r/w")}, + {('w'), QLatin1String("w/o")}, + {('o'), QLatin1String("os-boot")}, + {('('), QLatin1String("(process-args)")}, + {('p'), QLatin1String("process-option")}, + {('a'), QLatin1String("args-evaluate")}, + {('a'), QLatin1String("args-required")}, + {('a'), QLatin1String("args-required1")}, + {('o'), QLatin1String("os-execute-parsing")}, + {('n'), QLatin1String("next-arg")}, + {('s'), QLatin1String("shift-args")}, + {('s'), QLatin1String("script?")}, + {('a'), QLatin1String("argc")}, + {('a'), QLatin1String("argv")}, + {('p'), QLatin1String("pathdirs")}, + {('p'), QLatin1String("pathstring")}, + {('#'), QLatin1String("#!")}, + {('a'), QLatin1String("arg")}, + {('c'), QLatin1String("cstring>sstring")}, + {('s'), QLatin1String("set-encoding-fixed-width")}, + {('c'), QLatin1String("c-size")}, + {('c'), QLatin1String("c!+?")}, + {('s'), QLatin1String("string-")}, + {('+'), QLatin1String("+string")}, + {('c'), QLatin1String("char-")}, + {('x'), QLatin1String("xhold")}, + {('x'), QLatin1String("x@+/string")}, + {('-'), QLatin1String("-trailing-garbage")}, + {('x'), QLatin1String("x-width")}, + {('x'), QLatin1String("x-size")}, + {('x'), QLatin1String("xc-size")}, + {('x'), QLatin1String("xc@+")}, + {('x'), QLatin1String("xc!+?")}, + {('x'), QLatin1String("xc@")}, + {('x'), QLatin1String("x\\string-")}, + {('+'), QLatin1String("+x/string")}, + {('x'), QLatin1String("xchar-")}, + {('x'), QLatin1String("xchar+")}, + {('x'), QLatin1String("xkey")}, + {('x'), QLatin1String("xemit")}, + {('l'), QLatin1String("license")}, + {('i'), QLatin1String("include-file")}, + {('e'), QLatin1String("execute-parsing-file")}, + {('e'), QLatin1String("execute-parsing-named-file")}, + {('r'), QLatin1String("read-loop")}, + {('l'), QLatin1String("line-end-hook")}, + {('q'), QLatin1String("query")}, + {('c'), QLatin1String("clear-tibstack")}, + {('e'), QLatin1String("evaluate")}, + {('e'), QLatin1String("execute-parsing")}, + {('e'), QLatin1String("execute-parsing-wrapper")}, + {('c'), QLatin1String("create-input")}, + {('r'), QLatin1String("restore-input")}, + {('s'), QLatin1String("save-input")}, + {('p'), QLatin1String("pop-file")}, + {('p'), QLatin1String("push-file")}, + {('e'), QLatin1String("expand-tib")}, + {('n'), QLatin1String("new-tib")}, + {('f'), QLatin1String("file-input")}, + {('r'), QLatin1String("read-line")}, + {('e'), QLatin1String("evaluate-input")}, + {('t'), QLatin1String("terminal-input")}, + {('i'), QLatin1String("input-start-line")}, + {('i'), QLatin1String("input-lexeme!")}, + {('t'), QLatin1String("tib+")}, + {('t'), QLatin1String("tib")}, + {('l'), QLatin1String("loadfilename")}, + {('#'), QLatin1String("#fill-bytes")}, + {('b'), QLatin1String("blk")}, + {('l'), QLatin1String("loadfile")}, + {('l'), QLatin1String("loadline")}, + {('o'), QLatin1String("old-input")}, + {('m'), QLatin1String("max#tib")}, + {('#'), QLatin1String("#tib")}, + {('i'), QLatin1String("input-lexeme")}, + {('>'), QLatin1String(">in")}, + {('('), QLatin1String("(restore-input)")}, + {('('), QLatin1String("(save-input)")}, + {('s'), QLatin1String("source-id")}, + {('r'), QLatin1String("refill")}, + {('s'), QLatin1String("source")}, + {('i'), QLatin1String("input-var")}, + {('i'), QLatin1String("input-method")}, + {('a'), QLatin1String("accept")}, + {('e'), QLatin1String("edit-line")}, + {('d'), QLatin1String("decode")}, + {('e'), QLatin1String("everyline")}, + {('e'), QLatin1String("everychar")}, + {('i'), QLatin1String("insert-char")}, + {('c'), QLatin1String("ctrlkeys")}, + {('('), QLatin1String("(ret)")}, + {('('), QLatin1String("(bs)")}, + {('('), QLatin1String("(ins)")}, + {('r'), QLatin1String("recursive")}, + {('r'), QLatin1String("rehash")}, + {('r'), QLatin1String("reveal")}, + {('c'), QLatin1String("check-shadow")}, + {('('), QLatin1String("(reveal)")}, + {('w'), QLatin1String("warnings")}, + {('l'), QLatin1String("last?")}, + {(';'), QLatin1String(";")}, + {(':'), QLatin1String(":noname")}, + {(':'), QLatin1String(":")}, + {('('), QLatin1String("(:noname)")}, + {('d'), QLatin1String("defstart")}, + {(';'), QLatin1String(";-hook")}, + {(':'), QLatin1String(":-hook")}, + {('i'), QLatin1String("interpret/compile?")}, + {('T'), QLatin1String("TO")}, + {('I'), QLatin1String("IS")}, + {('['), QLatin1String("[IS]")}, + {('<'), QLatin1String("")}, + {('d'), QLatin1String("defer!")}, + {('D'), QLatin1String("DOES>")}, + {('D'), QLatin1String("Defers")}, + {('d'), QLatin1String("defer@")}, + {('D'), QLatin1String("Defer")}, + {('d'), QLatin1String("defer-default")}, + {('i'), QLatin1String("interpret/compile:")}, + {('i'), QLatin1String("interpret/compile-struct")}, + {('i'), QLatin1String("interpret/compile-comp")}, + {('i'), QLatin1String("interpret/compile-int")}, + {('('), QLatin1String("(Field)")}, + {('2'), QLatin1String("2Constant")}, + {('A'), QLatin1String("AValue")}, + {('V'), QLatin1String("Value")}, + {('A'), QLatin1String("AConstant")}, + {('C'), QLatin1String("Constant")}, + {('('), QLatin1String("(Value)")}, + {('('), QLatin1String("(Constant)")}, + {('A'), QLatin1String("AUser")}, + {('U'), QLatin1String("User")}, + {('u'), QLatin1String("uallot")}, + {('2'), QLatin1String("2Variable")}, + {('A'), QLatin1String("AVariable")}, + {('V'), QLatin1String("Variable")}, + {('C'), QLatin1String("Create")}, + {('A'), QLatin1String("Alias")}, + {('c'), QLatin1String("compile-only")}, + {('r'), QLatin1String("restrict")}, + {('i'), QLatin1String("immediate")}, + {('l'), QLatin1String("lastflags")}, + {('c'), QLatin1String("ctoggle")}, + {('c'), QLatin1String("creset")}, + {('c'), QLatin1String("cset")}, + {(','), QLatin1String(",\"")}, + {('m'), QLatin1String("mem,")}, + {('S'), QLatin1String("S,")}, + {(']'), QLatin1String("]")}, + {('['), QLatin1String("[")}, + {('c'), QLatin1String("compiler1")}, + {('r'), QLatin1String("recurse")}, + {('P'), QLatin1String("POSTPONE")}, + {('p'), QLatin1String("postpone,")}, + {('['), QLatin1String("[COMP']")}, + {('C'), QLatin1String("COMP'")}, + {('['), QLatin1String("[']")}, + {('['), QLatin1String("[(')]")}, + {('n'), QLatin1String("name>comp")}, + {('('), QLatin1String("(compile)")}, + {('d'), QLatin1String("dodoes,")}, + {('('), QLatin1String("(does>2)")}, + {('('), QLatin1String("(does>)")}, + {('!'), QLatin1String("!does")}, + {('c'), QLatin1String("compile-to-prims,")}, + {('p'), QLatin1String("peephole-compile,")}, + {('b'), QLatin1String("basic-block-end")}, + {('c'), QLatin1String("compile,")}, + {('c'), QLatin1String("cfa,")}, + {('['), QLatin1String("[char]")}, + {('c'), QLatin1String("char")}, + {('c'), QLatin1String("char@")}, + {('A'), QLatin1String("ALiteral")}, + {('2'), QLatin1String("2Literal")}, + {('L'), QLatin1String("Literal")}, + {('l'), QLatin1String("latest")}, + {('l'), QLatin1String("lastxt")}, + {('l'), QLatin1String("latestxt")}, + {('n'), QLatin1String("noname")}, + {('n'), QLatin1String("noname-header")}, + {('n'), QLatin1String("nextname")}, + {('n'), QLatin1String("nextname-header")}, + {('n'), QLatin1String("nextname-string")}, + {('i'), QLatin1String("input-stream")}, + {('i'), QLatin1String("input-stream-header")}, + {('h'), QLatin1String("header,")}, + {('l'), QLatin1String("longstring,")}, + {('s'), QLatin1String("string,")}, + {('h'), QLatin1String("header")}, + {('('), QLatin1String("(header)")}, + {('c'), QLatin1String("const")}, + {('A'), QLatin1String("A,")}, + {('c'), QLatin1String("cfalign")}, + {('m'), QLatin1String("maxalign")}, + {('f'), QLatin1String("falign")}, + {('a'), QLatin1String("align")}, + {('2'), QLatin1String("2,")}, + {(','), QLatin1String(",")}, + {('c'), QLatin1String("c,")}, + {('a'), QLatin1String("allot")}, + {('b'), QLatin1String("bye")}, + {('b'), QLatin1String("boot")}, + {('c'), QLatin1String("cold")}, + {('\''), QLatin1String("'cold")}, + {('p'), QLatin1String("process-args")}, + {('b'), QLatin1String("bootmessage")}, + {('('), QLatin1String("(bootmessage)")}, + {('q'), QLatin1String("quit")}, + {('('), QLatin1String("(DoError)")}, + {('.'), QLatin1String(".error-frame")}, + {('.'), QLatin1String(".error-line")}, + {('p'), QLatin1String("part-type")}, + {('m'), QLatin1String("mark-end")}, + {('m'), QLatin1String("mark-start")}, + {('u'), QLatin1String("umin")}, + {('.'), QLatin1String(".error-string")}, + {('d'), QLatin1String("dobacktrace")}, + {('D'), QLatin1String("DOERROR")}, + {('-'), QLatin1String("-trailing")}, + {('h'), QLatin1String("hex.")}, + {('d'), QLatin1String("dec.r")}, + {('d'), QLatin1String("dec.")}, + {('i'), QLatin1String("input-error-data")}, + {('>'), QLatin1String(">error")}, + {('e'), QLatin1String("error>")}, + {('e'), QLatin1String("error-stack")}, + {('/'), QLatin1String("/error")}, + {('m'), QLatin1String("max-errors")}, + {('('), QLatin1String("(quit)")}, + {('p'), QLatin1String("prompt")}, + {('.'), QLatin1String(".status")}, + {('\''), QLatin1String("'quit")}, + {('e'), QLatin1String("extend-mem")}, + {('f'), QLatin1String("free-mem-var")}, + {('s'), QLatin1String("save-mem")}, + {('i'), QLatin1String("interpreter1")}, + {('i'), QLatin1String("interpret")}, + {('i'), QLatin1String("interpret1")}, + {('b'), QLatin1String("before-word")}, + {('n'), QLatin1String("no.extensions")}, + {('i'), QLatin1String("interpreter-notfound1")}, + {('c'), QLatin1String("compiler-notfound1")}, + {('n'), QLatin1String("name")}, + {('p'), QLatin1String("parse-word")}, + {('p'), QLatin1String("parse-name")}, + {('p'), QLatin1String("parser")}, + {('p'), QLatin1String("parser1")}, + {('\''), QLatin1String("'")}, + {('('), QLatin1String("(')")}, + {('f'), QLatin1String("find")}, + {('s'), QLatin1String("sfind")}, + {('/'), QLatin1String("/does-handler")}, + {('d'), QLatin1String("does-handler!")}, + {('d'), QLatin1String("does-code!")}, + {('c'), QLatin1String("code-address!")}, + {('f'), QLatin1String("flashc!")}, + {('f'), QLatin1String("flash!")}, + {('>'), QLatin1String(">does-code")}, + {('>'), QLatin1String(">code-address")}, + {('b'), QLatin1String("body>")}, + {('>'), QLatin1String(">body")}, + {('>'), QLatin1String(">head-noprim")}, + {('h'), QLatin1String("head?")}, + {('?'), QLatin1String("???")}, + {('('), QLatin1String("(name>intn)")}, + {('('), QLatin1String("(name>comp)")}, + {('n'), QLatin1String("name?int")}, + {('n'), QLatin1String("name>int")}, + {('('), QLatin1String("(name>x)")}, + {('('), QLatin1String("((name>))")}, + {('n'), QLatin1String("name>string")}, + {('('), QLatin1String("(x>int)")}, + {('('), QLatin1String("(cfa>int)")}, + {('c'), QLatin1String("compile-only-error")}, + {('t'), QLatin1String("ticking-compile-only-error")}, + {('f'), QLatin1String("flag-sign")}, + {('l'), QLatin1String("lcount-mask")}, + {('r'), QLatin1String("restrict-mask")}, + {('i'), QLatin1String("immediate-mask")}, + {('a'), QLatin1String("alias-mask")}, + {('f'), QLatin1String("find-name")}, + {('s'), QLatin1String("search-wordlist")}, + {('('), QLatin1String("(search-wordlist)")}, + {('c'), QLatin1String("context")}, + {('v'), QLatin1String("voclink")}, + {('c'), QLatin1String("current")}, + {('l'), QLatin1String("lookup")}, + {('f'), QLatin1String("forth-wordlist")}, + {('f'), QLatin1String("f83search")}, + {('i'), QLatin1String("initvoc")}, + {('f'), QLatin1String("f83find")}, + {('w'), QLatin1String("wordlist-struct")}, + {('w'), QLatin1String("wordlist-extend")}, + {('w'), QLatin1String("wordlist-link")}, + {('w'), QLatin1String("wordlist-id")}, + {('w'), QLatin1String("wordlist-map")}, + {('w'), QLatin1String("wordlist-map-struct")}, + {('h'), QLatin1String("hash-method")}, + {('r'), QLatin1String("rehash-method")}, + {('r'), QLatin1String("reveal-method")}, + {('f'), QLatin1String("find-method")}, + {('\\'), QLatin1String("\\G")}, + {('\\'), QLatin1String("\\")}, + {('('), QLatin1String("(")}, + {('n'), QLatin1String("number")}, + {('n'), QLatin1String("number?")}, + {('s'), QLatin1String("snumber?")}, + {('s'), QLatin1String("s>number")}, + {('s'), QLatin1String("s>number?")}, + {('s'), QLatin1String("s>unumber?")}, + {('s'), QLatin1String("s'>unumber?")}, + {('?'), QLatin1String("?dnegate")}, + {('s'), QLatin1String("sign?")}, + {('g'), QLatin1String("getbase")}, + {('b'), QLatin1String("bases")}, + {('n'), QLatin1String("name-too-long?")}, + {('n'), QLatin1String("name-too-short?")}, + {('('), QLatin1String("(name)")}, + {('p'), QLatin1String("parse")}, + {('w'), QLatin1String("word")}, + {('s'), QLatin1String("sword")}, + {('('), QLatin1String("(word)")}, + {('v'), QLatin1String("version-string")}, + {('.'), QLatin1String(".error")}, + {('>'), QLatin1String(">stderr")}, + {('E'), QLatin1String("ErrLink")}, + {('u'), QLatin1String("u.")}, + {('.'), QLatin1String(".")}, + {('u'), QLatin1String("ud.")}, + {('d'), QLatin1String("d.")}, + {('u'), QLatin1String("u.r")}, + {('.'), QLatin1String(".r")}, + {('u'), QLatin1String("ud.r")}, + {('d'), QLatin1String("d.r")}, + {('#'), QLatin1String("#s")}, + {('#'), QLatin1String("#")}, + {('s'), QLatin1String("sign")}, + {('#'), QLatin1String("#>>")}, + {('<'), QLatin1String("<<#")}, + {('#'), QLatin1String("#>")}, + {('<'), QLatin1String("<#")}, + {('h'), QLatin1String("hold")}, + {('p'), QLatin1String("pad")}, + {('b'), QLatin1String("backspaces")}, + {('s'), QLatin1String("spaces")}, + {('s'), QLatin1String("space")}, + {('c'), QLatin1String("cr")}, + {('b'), QLatin1String("bell")}, + {('#'), QLatin1String("#lf")}, + {('#'), QLatin1String("#ff")}, + {('#'), QLatin1String("#cr")}, + {('#'), QLatin1String("#del")}, + {('#'), QLatin1String("#tab")}, + {('#'), QLatin1String("#bs")}, + {('#'), QLatin1String("#bell")}, + {('#'), QLatin1String("#eof")}, + {('('), QLatin1String("(S\")")}, + {('('), QLatin1String("(.\")")}, + {('k'), QLatin1String("key?")}, + {('k'), QLatin1String("key")}, + {('e'), QLatin1String("emit")}, + {('t'), QLatin1String("type")}, + {('('), QLatin1String("(key?)")}, + {('('), QLatin1String("(key)")}, + {('('), QLatin1String("(emit)")}, + {('('), QLatin1String("(type)")}, + {('i'), QLatin1String("infile-id")}, + {('o'), QLatin1String("outfile-id")}, + {('h'), QLatin1String("hex")}, + {('d'), QLatin1String("decimal")}, + {('"'), QLatin1String("\"lit")}, + {('c'), QLatin1String("clearstacks")}, + {('c'), QLatin1String("clearstack")}, + {('d'), QLatin1String("depth")}, + {('?'), QLatin1String("?stack")}, + {('a'), QLatin1String("abort")}, + {('('), QLatin1String("(abort\")")}, + {('c'), QLatin1String("c(abort\")")}, + {('t'), QLatin1String("throw")}, + {('c'), QLatin1String("catch")}, + {('l'), QLatin1String("lp@")}, + {('u'), QLatin1String("ud/mod")}, + {('s'), QLatin1String("s>d")}, + {('>'), QLatin1String(">number")}, + {('a'), QLatin1String("accumulate")}, + {('d'), QLatin1String("digit?")}, + {('s'), QLatin1String("skip")}, + {('s'), QLatin1String("scan")}, + {('b'), QLatin1String("bounds")}, + {('p'), QLatin1String("place")}, + {('r'), QLatin1String("roll")}, + {('d'), QLatin1String("dabs")}, + {('o'), QLatin1String("off")}, + {('o'), QLatin1String("on")}, + {('h'), QLatin1String("here")}, + {('d'), QLatin1String("dp")}, + {('i'), QLatin1String("in-dictionary?")}, + {('u'), QLatin1String("unused")}, + {('u'), QLatin1String("usable-dictionary-end")}, + {('d'), QLatin1String("dictionary-end")}, + {('A'), QLatin1String("A!")}, + {('c'), QLatin1String("chars")}, + {('c'), QLatin1String("cfaligned")}, + {('m'), QLatin1String("maxaligned")}, + {('r'), QLatin1String("r@")}, + {('N'), QLatin1String("NIL")}, + {('s'), QLatin1String("str<")}, + {('s'), QLatin1String("string-prefix?")}, + {('s'), QLatin1String("str=")}, + {('l'), QLatin1String("locals-size")}, + {('m'), QLatin1String("max-name-length")}, + {('L'), QLatin1String("Last")}, + {('L'), QLatin1String("LastCFA")}, + {('d'), QLatin1String("dpp")}, + {('n'), QLatin1String("normal-dp")}, + {('s'), QLatin1String("state")}, + {('d'), QLatin1String("dpl")}, + {('b'), QLatin1String("base")}, + {('i'), QLatin1String("includefilename")}, + {('c'), QLatin1String("current-input")}, + {('"'), QLatin1String("\"error")}, + {('e'), QLatin1String("errorhandler")}, + {('b'), QLatin1String("backtrace-rp0")}, + {('h'), QLatin1String("handler")}, + {('l'), QLatin1String("lp0")}, + {('f'), QLatin1String("fp0")}, + {('r'), QLatin1String("rp0")}, + {('s'), QLatin1String("sp0")}, + {('s'), QLatin1String("save-task")}, + {('p'), QLatin1String("prev-task")}, + {('n'), QLatin1String("next-task")}, + {('u'), QLatin1String("udp")}, + {('m'), QLatin1String("main-task")}, + {('d'), QLatin1String("def#tib")}, + {('p'), QLatin1String("pad-minsize")}, + {('h'), QLatin1String("holdend")}, + {('h'), QLatin1String("holdptr")}, + {('h'), QLatin1String("holdbuf-end")}, + {('h'), QLatin1String("holdbuf")}, + {('w'), QLatin1String("word-pno-size")}, + {('c'), QLatin1String("chars/block")}, + {('l'), QLatin1String("l/s")}, + {('c'), QLatin1String("c/l")}, + {('/'), QLatin1String("/line")}, + {('b'), QLatin1String("bl")}, + {('f'), QLatin1String("float")}, + {('c'), QLatin1String("cell")}, + {('f'), QLatin1String("false")}, + {('t'), QLatin1String("true")}, + {('f'), QLatin1String("forthstart")}, + {('i'), QLatin1String("image-header")}, + {('t'), QLatin1String("tag-offsets")}, + {('c'), QLatin1String("call2")}, + {('s'), QLatin1String("set-next-code")}, + {('d'), QLatin1String("decompile-prim")}, + {('f'), QLatin1String("forget-dyncode")}, + {('f'), QLatin1String("finish-code")}, + {('c'), QLatin1String("compile-prim1")}, + {('l'), QLatin1String("lib-error")}, + {('l'), QLatin1String("l!")}, + {('s'), QLatin1String("sl@")}, + {('u'), QLatin1String("ul@")}, + {('w'), QLatin1String("w!")}, + {('s'), QLatin1String("sw@")}, + {('u'), QLatin1String("uw@")}, + {('w'), QLatin1String("wcall")}, + {('l'), QLatin1String("lib-sym")}, + {('o'), QLatin1String("open-lib")}, + {('f'), QLatin1String("fpick")}, + {('f'), QLatin1String("f>l")}, + {('>'), QLatin1String(">l")}, + {('l'), QLatin1String("lp!")}, + {('l'), QLatin1String("lp+2")}, + {('l'), QLatin1String("lp+")}, + {('l'), QLatin1String("lp-")}, + {('l'), QLatin1String("lp+!#")}, + {('l'), QLatin1String("laddr#")}, + {('f'), QLatin1String("f@local1")}, + {('f'), QLatin1String("f@local0")}, + {('f'), QLatin1String("f@local#")}, + {('@'), QLatin1String("@local3")}, + {('@'), QLatin1String("@local2")}, + {('@'), QLatin1String("@local1")}, + {('@'), QLatin1String("@local0")}, + {('@'), QLatin1String("@local#")}, + {('f'), QLatin1String("faxpy")}, + {('v'), QLatin1String("v*")}, + {('d'), QLatin1String("dfaligned")}, + {('s'), QLatin1String("sfaligned")}, + {('d'), QLatin1String("dfloats")}, + {('s'), QLatin1String("sfloats")}, + {('f'), QLatin1String("fatanh")}, + {('f'), QLatin1String("facosh")}, + {('f'), QLatin1String("fasinh")}, + {('f'), QLatin1String("ftanh")}, + {('f'), QLatin1String("fcosh")}, + {('f'), QLatin1String("fsinh")}, + {('f'), QLatin1String("ftan")}, + {('f'), QLatin1String("fsqrt")}, + {('f'), QLatin1String("fsincos")}, + {('f'), QLatin1String("fsin")}, + {('f'), QLatin1String("falog")}, + {('f'), QLatin1String("flog")}, + {('f'), QLatin1String("flnp1")}, + {('f'), QLatin1String("fln")}, + {('f'), QLatin1String("fexpm1")}, + {('f'), QLatin1String("fexp")}, + {('f'), QLatin1String("fcos")}, + {('f'), QLatin1String("fatan2")}, + {('f'), QLatin1String("fatan")}, + {('f'), QLatin1String("fasin")}, + {('f'), QLatin1String("facos")}, + {('f'), QLatin1String("fabs")}, + {('>'), QLatin1String(">float")}, + {('r'), QLatin1String("represent")}, + {('f'), QLatin1String("fmin")}, + {('f'), QLatin1String("fmax")}, + {('f'), QLatin1String("fround")}, + {('f'), QLatin1String("floor")}, + {('f'), QLatin1String("floats")}, + {('f'), QLatin1String("float+")}, + {('f'), QLatin1String("ftuck")}, + {('f'), QLatin1String("fnip")}, + {('f'), QLatin1String("frot")}, + {('f'), QLatin1String("fover")}, + {('f'), QLatin1String("fswap")}, + {('f'), QLatin1String("fdup")}, + {('f'), QLatin1String("fdrop")}, + {('f'), QLatin1String("fnegate")}, + {('f'), QLatin1String("f**2")}, + {('f'), QLatin1String("fm*/")}, + {('f'), QLatin1String("fm/")}, + {('f'), QLatin1String("fm*")}, + {('f'), QLatin1String("f**")}, + {('f'), QLatin1String("f/")}, + {('f'), QLatin1String("f*")}, + {('f'), QLatin1String("f-")}, + {('f'), QLatin1String("f+")}, + {('s'), QLatin1String("sf!")}, + {('s'), QLatin1String("sf@")}, + {('d'), QLatin1String("df!")}, + {('d'), QLatin1String("df@")}, + {('f'), QLatin1String("f@")}, + {('f'), QLatin1String("f!")}, + {('f'), QLatin1String("f>s")}, + {('f'), QLatin1String("f>d")}, + {('d'), QLatin1String("d>f")}, + {('s'), QLatin1String("s>f")}, + {('f'), QLatin1String("f0>=")}, + {('f'), QLatin1String("f0<=")}, + {('f'), QLatin1String("f0>")}, + {('f'), QLatin1String("f0<")}, + {('f'), QLatin1String("f0<>")}, + {('f'), QLatin1String("f0=")}, + {('f'), QLatin1String("f>=")}, + {('f'), QLatin1String("f<=")}, + {('f'), QLatin1String("f>")}, + {('f'), QLatin1String("f<")}, + {('f'), QLatin1String("f<>")}, + {('f'), QLatin1String("f=")}, + {('c'), QLatin1String("cputime")}, + {('u'), QLatin1String("utime")}, + {('n'), QLatin1String("newline")}, + {('='), QLatin1String("=mkdir")}, + {('g'), QLatin1String("get-dir")}, + {('s'), QLatin1String("set-dir")}, + {('f'), QLatin1String("filename-match")}, + {('c'), QLatin1String("close-dir")}, + {('r'), QLatin1String("read-dir")}, + {('o'), QLatin1String("open-dir")}, + {('f'), QLatin1String("file-eof?")}, + {('f'), QLatin1String("file-status")}, + {('f'), QLatin1String("flush-file")}, + {('e'), QLatin1String("emit-file")}, + {('w'), QLatin1String("write-file")}, + {('('), QLatin1String("(read-line)")}, + {('r'), QLatin1String("read-file")}, + {('r'), QLatin1String("resize-file")}, + {('f'), QLatin1String("file-size")}, + {('r'), QLatin1String("reposition-file")}, + {('f'), QLatin1String("file-position")}, + {('r'), QLatin1String("rename-file")}, + {('d'), QLatin1String("delete-file")}, + {('c'), QLatin1String("create-file")}, + {('o'), QLatin1String("open-file")}, + {('c'), QLatin1String("close-file")}, + {('c'), QLatin1String("call-c")}, + {('s'), QLatin1String("strsignal")}, + {('s'), QLatin1String("strerror")}, + {('r'), QLatin1String("resize")}, + {('f'), QLatin1String("free")}, + {('a'), QLatin1String("allocate")}, + {('m'), QLatin1String("ms")}, + {('t'), QLatin1String("time&date")}, + {('c'), QLatin1String("close-pipe")}, + {('o'), QLatin1String("open-pipe")}, + {('g'), QLatin1String("getenv")}, + {('('), QLatin1String("(system)")}, + {('('), QLatin1String("(bye)")}, + {('f'), QLatin1String("flush-icache")}, + {('w'), QLatin1String("wcwidth")}, + {('f'), QLatin1String("form")}, + {('s'), QLatin1String("stderr")}, + {('s'), QLatin1String("stdout")}, + {('s'), QLatin1String("stdin")}, + {('k'), QLatin1String("key?-file")}, + {('k'), QLatin1String("key-file")}, + {('t'), QLatin1String("threading-method")}, + {('f'), QLatin1String("faligned")}, + {('a'), QLatin1String("aligned")}, + {('('), QLatin1String("(parse-white)")}, + {('('), QLatin1String("(hashkey1)")}, + {('('), QLatin1String("(tablelfind)")}, + {('('), QLatin1String("(hashlfind)")}, + {('('), QLatin1String("(listlfind)")}, + {('c'), QLatin1String("count")}, + {('('), QLatin1String("(chars)")}, + {('c'), QLatin1String("char+")}, + {('c'), QLatin1String("cells")}, + {('c'), QLatin1String("cell+")}, + {('2'), QLatin1String("2@")}, + {('2'), QLatin1String("2!")}, + {('c'), QLatin1String("c!")}, + {('c'), QLatin1String("c@")}, + {('+'), QLatin1String("+!")}, + {('!'), QLatin1String("!")}, + {('l'), QLatin1String("lit@")}, + {('@'), QLatin1String("@")}, + {('2'), QLatin1String("2tuck")}, + {('2'), QLatin1String("2nip")}, + {('2'), QLatin1String("2rot")}, + {('2'), QLatin1String("2swap")}, + {('2'), QLatin1String("2over")}, + {('2'), QLatin1String("2dup")}, + {('2'), QLatin1String("2drop")}, + {('p'), QLatin1String("pick")}, + {('?'), QLatin1String("?dup")}, + {('t'), QLatin1String("tuck")}, + {('n'), QLatin1String("nip")}, + {('-'), QLatin1String("-rot")}, + {('r'), QLatin1String("rot")}, + {('d'), QLatin1String("dup")}, + {('s'), QLatin1String("swap")}, + {('d'), QLatin1String("drop")}, + {('o'), QLatin1String("over")}, + {('2'), QLatin1String("2rdrop")}, + {('2'), QLatin1String("2r@")}, + {('2'), QLatin1String("2r>")}, + {('2'), QLatin1String("2>r")}, + {('r'), QLatin1String("rdrop")}, + {('r'), QLatin1String("r>")}, + {('>'), QLatin1String(">r")}, + {('f'), QLatin1String("fp!")}, + {('f'), QLatin1String("fp@")}, + {('r'), QLatin1String("rp!")}, + {('r'), QLatin1String("rp@")}, + {('s'), QLatin1String("sp!")}, + {('s'), QLatin1String("sp@")}, + {('u'), QLatin1String("up!")}, + {('u'), QLatin1String("useraddr")}, + {('w'), QLatin1String("within")}, + {('d'), QLatin1String("du>=")}, + {('d'), QLatin1String("du<=")}, + {('d'), QLatin1String("du>")}, + {('d'), QLatin1String("du<")}, + {('d'), QLatin1String("du<>")}, + {('d'), QLatin1String("du=")}, + {('d'), QLatin1String("d0>=")}, + {('d'), QLatin1String("d0<=")}, + {('d'), QLatin1String("d0>")}, + {('d'), QLatin1String("d0<")}, + {('d'), QLatin1String("d0<>")}, + {('d'), QLatin1String("d0=")}, + {('d'), QLatin1String("d>=")}, + {('d'), QLatin1String("d<=")}, + {('d'), QLatin1String("d>")}, + {('d'), QLatin1String("d<")}, + {('d'), QLatin1String("d<>")}, + {('d'), QLatin1String("d=")}, + {('u'), QLatin1String("u>=")}, + {('u'), QLatin1String("u<=")}, + {('u'), QLatin1String("u>")}, + {('u'), QLatin1String("u<")}, + {('u'), QLatin1String("u<>")}, + {('u'), QLatin1String("u=")}, + {('>'), QLatin1String(">=")}, + {('<'), QLatin1String("<=")}, + {('>'), QLatin1String(">")}, + {('<'), QLatin1String("<")}, + {('<'), QLatin1String("<>")}, + {('='), QLatin1String("=")}, + {('0'), QLatin1String("0>=")}, + {('0'), QLatin1String("0<=")}, + {('0'), QLatin1String("0>")}, + {('0'), QLatin1String("0<")}, + {('0'), QLatin1String("0<>")}, + {('0'), QLatin1String("0=")}, + {('l'), QLatin1String("lshift")}, + {('r'), QLatin1String("rshift")}, + {('i'), QLatin1String("invert")}, + {('x'), QLatin1String("xor")}, + {('o'), QLatin1String("or")}, + {('a'), QLatin1String("and")}, + {('d'), QLatin1String("d2/")}, + {('d'), QLatin1String("d2*")}, + {('d'), QLatin1String("dnegate")}, + {('d'), QLatin1String("d-")}, + {('d'), QLatin1String("d+")}, + {('m'), QLatin1String("m+")}, + {('u'), QLatin1String("um/mod")}, + {('u'), QLatin1String("um*")}, + {('m'), QLatin1String("m*")}, + {('s'), QLatin1String("sm/rem")}, + {('f'), QLatin1String("fm/mod")}, + {('2'), QLatin1String("2/")}, + {('2'), QLatin1String("2*")}, + {('*'), QLatin1String("*/")}, + {('*'), QLatin1String("*/mod")}, + {('/'), QLatin1String("/mod")}, + {('m'), QLatin1String("mod")}, + {('/'), QLatin1String("/")}, + {('*'), QLatin1String("*")}, + {('a'), QLatin1String("abs")}, + {('m'), QLatin1String("min")}, + {('m'), QLatin1String("max")}, + {('1'), QLatin1String("1-")}, + {('1'), QLatin1String("1+")}, + {('n'), QLatin1String("negate")}, + {('-'), QLatin1String("-")}, + {('u'), QLatin1String("under+")}, + {('l'), QLatin1String("lit+")}, + {('+'), QLatin1String("+")}, + {('l'), QLatin1String("lit")}, + {('/'), QLatin1String("/string")}, + {('c'), QLatin1String("capscompare")}, + {('t'), QLatin1String("toupper")}, + {('c'), QLatin1String("compare")}, + {('f'), QLatin1String("fill")}, + {('c'), QLatin1String("cmove>")}, + {('c'), QLatin1String("cmove")}, + {('m'), QLatin1String("move")}, + {('k'), QLatin1String("k")}, + {('j'), QLatin1String("j")}, + {('i'), QLatin1String("i'")}, + {('i'), QLatin1String("i")}, + {('('), QLatin1String("(u-do)")}, + {('('), QLatin1String("(-do)")}, + {('('), QLatin1String("(u+do)")}, + {('('), QLatin1String("(+do)")}, + {('('), QLatin1String("(?do)")}, + {('('), QLatin1String("(do)")}, + {('('), QLatin1String("(for)")}, + {('('), QLatin1String("(s+loop)-lp+!#")}, + {('('), QLatin1String("(s+loop)")}, + {('('), QLatin1String("(-loop)-lp+!#")}, + {('('), QLatin1String("(-loop)")}, + {('('), QLatin1String("(+loop)-lp+!#")}, + {('('), QLatin1String("(+loop)")}, + {('('), QLatin1String("(loop)-lp+!#")}, + {('('), QLatin1String("(loop)")}, + {('('), QLatin1String("(next)-lp+!#")}, + {('('), QLatin1String("(next)")}, + {('?'), QLatin1String("?dup-0=-?branch")}, + {('?'), QLatin1String("?dup-?branch")}, + {('?'), QLatin1String("?branch-lp+!#")}, + {('?'), QLatin1String("?branch")}, + {('b'), QLatin1String("branch")}, + {('b'), QLatin1String("branch-lp+!#")}, + {('d'), QLatin1String("does-exec")}, + {('l'), QLatin1String("lit-perform")}, + {('u'), QLatin1String("unloop")}, + {(';'), QLatin1String(";s")}, + {('p'), QLatin1String("perform")}, + {('e'), QLatin1String("execute")}, + {('c'), QLatin1String("call")}, + {('n'), QLatin1String("noop")}, + }; + + forth_types = {}; + + forth_literals = {}; + + forth_builtin = {}; + + forth_other = {}; +} +void loadForthData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other) { + if (!forthDataInitialized) { + initForthData(); + forthDataInitialized = true; + } + types = forth_types; + keywords = forth_keywords; + builtin = forth_builtin; + literals = forth_literals; + other = forth_other; +} diff --git a/client/qmarkdowntextedit/qownlanguagedata.h b/client/qmarkdowntextedit/qownlanguagedata.h new file mode 100644 index 0000000..cb7faa3 --- /dev/null +++ b/client/qmarkdowntextedit/qownlanguagedata.h @@ -0,0 +1,245 @@ +/* + * MIT License + * + * Copyright (c) 2019-2021 Waqar Ahmed -- + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef QOWNLANGUAGEDATA_H +#define QOWNLANGUAGEDATA_H + +#include + +/* ------------------------ + * TEMPLATE FOR LANG DATA + * ------------------------- + * + * loadXXXData, where XXX is the language + * keywords are the language keywords e.g, const + * types are built-in types i.e, int, char, var + * literals are words like, true false + * builtin are the library functions + * other can contain any other thing, for e.g, in cpp it contains the + preprocessor + + static const QMultiHash xxx_keywords = { + }; + + static const QMultiHash xxx_types = { + }; + + static const QMultiHash xxx_literals = { + }; + + static const QMultiHash xxx_builtin = { + }; + + static const QMultiHash xxx_other = { + }; + +*/ + +/**********************************************************/ +/* C/C++ Data *********************************************/ +/**********************************************************/ +void loadCppData(QMultiHash &typess, + QMultiHash &keywordss, + QMultiHash &builtins, + QMultiHash &literalss, + QMultiHash &others); + +/**********************************************************/ +/* Shell Data *********************************************/ +/**********************************************************/ +void loadShellData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/**********************************************************/ +/* JS Data *********************************************/ +/**********************************************************/ +void loadJSData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/**********************************************************/ +/* JS Data *********************************************/ +/**********************************************************/ +void loadNixData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/**********************************************************/ +/* PHP Data *********************************************/ +/**********************************************************/ +void loadPHPData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/**********************************************************/ +/* QML Data *********************************************/ +/**********************************************************/ +void loadQMLData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/**********************************************************/ +/* Python Data *********************************************/ +/**********************************************************/ +void loadPythonData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** Rust DATA ***********************************/ +/********************************************************/ +void loadRustData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** Java DATA ***********************************/ +/********************************************************/ +void loadJavaData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** C# DATA *************************************/ +/********************************************************/ +void loadCSharpData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** Go DATA *************************************/ +/********************************************************/ +void loadGoData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** V DATA **************************************/ +/********************************************************/ +void loadVData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** SQL DATA ************************************/ +/********************************************************/ +void loadSQLData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** JSON DATA ***********************************/ +/********************************************************/ +void loadJSONData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** CSS DATA ***********************************/ +/********************************************************/ +void loadCSSData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** Typescript DATA *********************************/ +/********************************************************/ +void loadTypescriptData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** YAML DATA ***************************************/ +/********************************************************/ +void loadYAMLData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** VEX DATA ****************************************/ +/********************************************************/ +void loadVEXData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** CMake DATA **************************************/ +/********************************************************/ +void loadCMakeData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); + +/********************************************************/ +/*** Make DATA ***************************************/ +/********************************************************/ +void loadMakeData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); +/********************************************************/ +/*** Forth DATA **************************************/ +/********************************************************/ +void loadForthData(QMultiHash &types, + QMultiHash &keywords, + QMultiHash &builtin, + QMultiHash &literals, + QMultiHash &other); +#endif diff --git a/client/qmarkdowntextedit/qplaintexteditsearchwidget.cpp b/client/qmarkdowntextedit/qplaintexteditsearchwidget.cpp index 54fb975..06e32e2 100644 --- a/client/qmarkdowntextedit/qplaintexteditsearchwidget.cpp +++ b/client/qmarkdowntextedit/qplaintexteditsearchwidget.cpp @@ -1,46 +1,66 @@ /* - * Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com + * MIT License * - * 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; version 2 of the License. + * Copyright (c) 2014-2023 Patrizio Bekerle -- * - * 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. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ #include "qplaintexteditsearchwidget.h" -#include "ui_qplaintexteditsearchwidget.h" + +#include #include #include -#include -QPlainTextEditSearchWidget::QPlainTextEditSearchWidget(QPlainTextEdit *parent) : - QWidget(parent), - ui(new Ui::QPlainTextEditSearchWidget) -{ +#include "ui_qplaintexteditsearchwidget.h" + +QPlainTextEditSearchWidget::QPlainTextEditSearchWidget(QPlainTextEdit *parent) + : QWidget(parent), + ui(new Ui::QPlainTextEditSearchWidget), + selectionColor(0, 180, 0, 100) { ui->setupUi(this); _textEdit = parent; _darkMode = false; hide(); + ui->searchCountLabel->setStyleSheet(QStringLiteral("* {color: grey}")); + // hiding will leave a open space in the horizontal layout + ui->searchCountLabel->setEnabled(false); + _currentSearchResult = 0; + _searchResultCount = 0; - QObject::connect(ui->closeButton, SIGNAL(clicked()), - this, SLOT(deactivate())); - QObject::connect(ui->searchLineEdit, SIGNAL(textChanged(const QString &)), - this, SLOT(searchLineEditTextChanged(const QString &))); - QObject::connect(ui->searchDownButton, SIGNAL(clicked()), - this, SLOT(doSearchDown())); - QObject::connect(ui->searchUpButton, SIGNAL(clicked()), - this, SLOT(doSearchUp())); - QObject::connect(ui->replaceToggleButton, SIGNAL(toggled(bool)), - this, SLOT(setReplaceMode(bool))); - QObject::connect(ui->replaceButton, SIGNAL(clicked()), - this, SLOT(doReplace())); - QObject::connect(ui->replaceAllButton, SIGNAL(clicked()), - this, SLOT(doReplaceAll())); + connect(ui->closeButton, &QPushButton::clicked, this, + &QPlainTextEditSearchWidget::deactivate); + connect(ui->searchLineEdit, &QLineEdit::textChanged, this, + &QPlainTextEditSearchWidget::searchLineEditTextChanged); + connect(ui->searchDownButton, &QPushButton::clicked, this, + &QPlainTextEditSearchWidget::doSearchDown); + connect(ui->searchUpButton, &QPushButton::clicked, this, + &QPlainTextEditSearchWidget::doSearchUp); + connect(ui->replaceToggleButton, &QPushButton::toggled, this, + &QPlainTextEditSearchWidget::setReplaceMode); + connect(ui->replaceButton, &QPushButton::clicked, this, + &QPlainTextEditSearchWidget::doReplace); + connect(ui->replaceAllButton, &QPushButton::clicked, this, + &QPlainTextEditSearchWidget::doReplaceAll); + + connect(&_debounceTimer, &QTimer::timeout, + this, &QPlainTextEditSearchWidget::performSearch); installEventFilter(this); ui->searchLineEdit->installEventFilter(this); @@ -52,7 +72,7 @@ QPlainTextEditSearchWidget::QPlainTextEditSearchWidget(QPlainTextEdit *parent) : ui->buttonFrame->layout()->setSpacing(9); // set the margin to 0 for the top buttons for OS X - QString buttonStyle = "QPushButton {margin: 0}"; + QString buttonStyle = QStringLiteral("QPushButton {margin: 0}"); ui->closeButton->setStyleSheet(buttonStyle); ui->searchDownButton->setStyleSheet(buttonStyle); ui->searchUpButton->setStyleSheet(buttonStyle); @@ -61,25 +81,9 @@ QPlainTextEditSearchWidget::QPlainTextEditSearchWidget(QPlainTextEdit *parent) : #endif } -QPlainTextEditSearchWidget::~QPlainTextEditSearchWidget() { - delete ui; -} +QPlainTextEditSearchWidget::~QPlainTextEditSearchWidget() { delete ui; } -void QPlainTextEditSearchWidget::activate() { - setReplaceMode(false); - show(); - - // preset the selected text as search text if there is any and there is no - // other search text - QString selectedText = _textEdit->textCursor().selectedText(); - if (!selectedText.isEmpty() && ui->searchLineEdit->text().isEmpty()) { - ui->searchLineEdit->setText(selectedText); - } - - ui->searchLineEdit->setFocus(); - ui->searchLineEdit->selectAll(); - doSearchDown(); -} +void QPlainTextEditSearchWidget::activate() { activate(true); } void QPlainTextEditSearchWidget::activateReplace() { // replacing is prohibited if the text edit is readonly @@ -94,7 +98,13 @@ void QPlainTextEditSearchWidget::activateReplace() { } void QPlainTextEditSearchWidget::deactivate() { + stopDebounce(); + hide(); + + // Clear the search extra selections when closing the search bar + clearSearchExtraSelections(); + _textEdit->setFocus(); } @@ -109,29 +119,32 @@ void QPlainTextEditSearchWidget::setReplaceMode(bool enabled) { bool QPlainTextEditSearchWidget::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast(event); + auto *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Escape) { deactivate(); return true; - } else if ((keyEvent->modifiers().testFlag(Qt::ShiftModifier) && - (keyEvent->key() == Qt::Key_Return)) || - (keyEvent->key() == Qt::Key_Up)) { + } else if ((!_debounceTimer.isActive() && + keyEvent->modifiers().testFlag(Qt::ShiftModifier) && + (keyEvent->key() == Qt::Key_Return)) || + (keyEvent->key() == Qt::Key_Up)) { doSearchUp(); return true; - } else if ((keyEvent->key() == Qt::Key_Return) || - (keyEvent->key() == Qt::Key_Down)) { + } else if (!_debounceTimer.isActive() && + ((keyEvent->key() == Qt::Key_Return) || + (keyEvent->key() == Qt::Key_Down))) { doSearchDown(); return true; - } else if (keyEvent->key() == Qt::Key_F3) { + } else if (!_debounceTimer.isActive() && keyEvent->key() == Qt::Key_F3) { doSearch(!keyEvent->modifiers().testFlag(Qt::ShiftModifier)); return true; } -// if ((obj == ui->replaceLineEdit) && (keyEvent->key() == Qt::Key_Tab) -// && ui->replaceToggleButton->isChecked()) { -// ui->replaceLineEdit->setFocus(); -// } + // if ((obj == ui->replaceLineEdit) && (keyEvent->key() == + // Qt::Key_Tab) + // && ui->replaceToggleButton->isChecked()) { + // ui->replaceLineEdit->setFocus(); + // } return false; } @@ -139,19 +152,75 @@ bool QPlainTextEditSearchWidget::eventFilter(QObject *obj, QEvent *event) { return QWidget::eventFilter(obj, event); } -void QPlainTextEditSearchWidget::searchLineEditTextChanged(const QString &arg1) { - Q_UNUSED(arg1); +void QPlainTextEditSearchWidget::searchLineEditTextChanged( + const QString &arg1) { + _searchTerm = arg1; + + if (_debounceTimer.interval() != 0 && !_searchTerm.isEmpty()) { + _debounceTimer.start(); + ui->searchDownButton->setEnabled(false); + ui->searchUpButton->setEnabled(false); + } else { + performSearch(); + } +} + +void QPlainTextEditSearchWidget::performSearch() +{ + doSearchCount(); + updateSearchExtraSelections(); doSearchDown(); } -void QPlainTextEditSearchWidget::doSearchUp() { - doSearch(false); +void QPlainTextEditSearchWidget::clearSearchExtraSelections() { + _searchExtraSelections.clear(); + setSearchExtraSelections(); } -void QPlainTextEditSearchWidget::doSearchDown() { - doSearch(true); +void QPlainTextEditSearchWidget::updateSearchExtraSelections() { + _searchExtraSelections.clear(); + const auto textCursor = _textEdit->textCursor(); + _textEdit->moveCursor(QTextCursor::Start); + const QColor color = selectionColor; + QTextCharFormat extraFmt; + extraFmt.setBackground(color); + int findCounter = 0; + const int searchMode = ui->modeComboBox->currentIndex(); + + while (doSearch(true, false, false)) { + findCounter++; + + // prevent infinite loops from regular expression searches like "$", "^" or "\b" + if (searchMode == RegularExpressionMode && findCounter >= 10000) { + break; + } + + QTextEdit::ExtraSelection extra = QTextEdit::ExtraSelection(); + extra.format = extraFmt; + + extra.cursor = _textEdit->textCursor(); + _searchExtraSelections.append(extra); + } + + _textEdit->setTextCursor(textCursor); + this->setSearchExtraSelections(); } +void QPlainTextEditSearchWidget::setSearchExtraSelections() const { + this->_textEdit->setExtraSelections(this->_searchExtraSelections); +} + +void QPlainTextEditSearchWidget::stopDebounce() +{ + _debounceTimer.stop(); + ui->searchDownButton->setEnabled(true); + ui->searchUpButton->setEnabled(true); +} + +void QPlainTextEditSearchWidget::doSearchUp() { doSearch(false); } + +void QPlainTextEditSearchWidget::doSearchDown() { doSearch(true); } + bool QPlainTextEditSearchWidget::doReplace(bool forAll) { if (_textEdit->isReadOnly()) { return false; @@ -163,18 +232,18 @@ bool QPlainTextEditSearchWidget::doReplace(bool forAll) { return false; } - int searchMode = ui->modeComboBox->currentIndex(); + const int searchMode = ui->modeComboBox->currentIndex(); if (searchMode == RegularExpressionMode) { QString text = cursor.selectedText(); - text.replace(QRegExp(ui->searchLineEdit->text()), - ui->replaceLineEdit->text()); + text.replace(QRegularExpression(ui->searchLineEdit->text()), + ui->replaceLineEdit->text()); cursor.insertText(text); } else { cursor.insertText(ui->replaceLineEdit->text()); } if (!forAll) { - int position = cursor.position(); + const int position = cursor.position(); if (!doSearch(true)) { // restore the last cursor position if text wasn't found any more @@ -195,72 +264,237 @@ void QPlainTextEditSearchWidget::doReplaceAll() { _textEdit->moveCursor(QTextCursor::Start); // replace until everything to the bottom is replaced - while (doSearch(true, false) && doReplace(true)) {} + while (doSearch(true, false) && doReplace(true)) { + } } /** * @brief Searches for text in the text edit * @returns true if found */ -bool QPlainTextEditSearchWidget::doSearch(bool searchDown, bool allowRestartAtTop) { - QString text = ui->searchLineEdit->text(); +bool QPlainTextEditSearchWidget::doSearch(bool searchDown, + bool allowRestartAtTop, + bool updateUI) { + if (_debounceTimer.isActive()) { + stopDebounce(); + } + + const QString text = ui->searchLineEdit->text(); + + if (text.isEmpty()) { + if (updateUI) { + ui->searchLineEdit->setStyleSheet(QLatin1String("")); + } - if (text == "") { - ui->searchLineEdit->setStyleSheet(""); return false; } - int searchMode = ui->modeComboBox->currentIndex(); + const int searchMode = ui->modeComboBox->currentIndex(); + const bool caseSensitive = ui->matchCaseSensitiveButton->isChecked(); - QFlags options = searchDown ? - QTextDocument::FindFlag(0) - : QTextDocument::FindBackward; + QFlags options = + searchDown ? QTextDocument::FindFlag(0) : QTextDocument::FindBackward; if (searchMode == WholeWordsMode) { options |= QTextDocument::FindWholeWords; } - if (ui->matchCaseSensitiveButton->isChecked()) { + if (caseSensitive) { options |= QTextDocument::FindCaseSensitively; } - bool found; - if (searchMode == RegularExpressionMode) { - found = _textEdit->find(QRegExp(text), options); - } else { - found = _textEdit->find(text, options); + // block signal to reduce too many signals being fired and too many updates + _textEdit->blockSignals(true); + + bool found = + searchMode == RegularExpressionMode + ? +#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + _textEdit->find( + QRegularExpression( + text, caseSensitive + ? QRegularExpression::NoPatternOption + : QRegularExpression::CaseInsensitiveOption), + options) + : +#else + _textEdit->find(QRegExp(text, caseSensitive ? Qt::CaseSensitive + : Qt::CaseInsensitive), + options) + : +#endif + _textEdit->find(text, options); + + _textEdit->blockSignals(false); + + if (found) { + const int result = + searchDown ? ++_currentSearchResult : --_currentSearchResult; + _currentSearchResult = std::min(result, _searchResultCount); + + updateSearchCountLabelText(); } // start at the top (or bottom) if not found if (!found && allowRestartAtTop) { - _textEdit->moveCursor( - searchDown ? QTextCursor::Start : QTextCursor::End); - found = _textEdit->find(text, options); + _textEdit->moveCursor(searchDown ? QTextCursor::Start + : QTextCursor::End); + found = + searchMode == RegularExpressionMode + ? +#if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + _textEdit->find( + QRegularExpression( + text, caseSensitive + ? QRegularExpression::NoPatternOption + : QRegularExpression::CaseInsensitiveOption), + options) + : +#else + _textEdit->find( + QRegExp(text, caseSensitive ? Qt::CaseSensitive + : Qt::CaseInsensitive), + options) + : +#endif + _textEdit->find(text, options); + + if (found && updateUI) { + _currentSearchResult = searchDown ? 1 : _searchResultCount; + updateSearchCountLabelText(); + } } - QRect rect = _textEdit->cursorRect(); - QMargins margins = _textEdit->layout()->contentsMargins(); - int searchWidgetHotArea = _textEdit->height() - this->height(); - int marginBottom = (rect.y() > searchWidgetHotArea) ? (this->height() + 10) - : 0; + if (updateUI) { + const QRect rect = _textEdit->cursorRect(); + QMargins margins = _textEdit->layout()->contentsMargins(); + const int searchWidgetHotArea = _textEdit->height() - this->height(); + const int marginBottom = + (rect.y() > searchWidgetHotArea) ? (this->height() + 10) : 0; - // move the search box a bit up if we would block the search result - if (margins.bottom() != marginBottom) { - margins.setBottom(marginBottom); - _textEdit->layout()->setContentsMargins(margins); + // move the search box a bit up if we would block the search result + if (margins.bottom() != marginBottom) { + margins.setBottom(marginBottom); + _textEdit->layout()->setContentsMargins(margins); + } + + // add a background color according if we found the text or not + const QString bgColorCode = + _darkMode + ? (found ? QStringLiteral("#135a13") + : QStringLiteral("#8d2b36")) + : found ? QStringLiteral("#D5FAE2") : QStringLiteral("#FAE9EB"); + const QString fgColorCode = + _darkMode ? QStringLiteral("#cccccc") : QStringLiteral("#404040"); + + ui->searchLineEdit->setStyleSheet( + QStringLiteral("* { background: ") + bgColorCode + + QStringLiteral("; color: ") + fgColorCode + QStringLiteral("; }")); + + // restore the search extra selections after the find command + this->setSearchExtraSelections(); } - // add a background color according if we found the text or not - QString colorCode = found ? "#D5FAE2" : "#FAE9EB"; - - if (_darkMode) { - colorCode = found ? "#135a13" : "#8d2b36"; - } - - ui->searchLineEdit->setStyleSheet("* { background: " + colorCode + "; }"); - return found; } +/** + * @brief Counts the search results + */ +void QPlainTextEditSearchWidget::doSearchCount() { + // Note that we are moving the anchor, so the search will start from the top + // again! Alternative: Restore cursor position afterward, but then we will + // not know + // at what _currentSearchResult we currently are + _textEdit->moveCursor(QTextCursor::Start, QTextCursor::MoveAnchor); + + bool found; + _searchResultCount = 0; + _currentSearchResult = 0; + const int searchMode = ui->modeComboBox->currentIndex(); + + do { + found = doSearch(true, false, false); + if (found) { + _searchResultCount++; + } + + // prevent infinite loops from regular expression searches like "$", "^" or "\b" + if (searchMode == RegularExpressionMode && _searchResultCount >= 10000) { + break; + } + } while (found); + + updateSearchCountLabelText(); +} + void QPlainTextEditSearchWidget::setDarkMode(bool enabled) { _darkMode = enabled; } + +void QPlainTextEditSearchWidget::setSearchText(const QString &searchText) { + ui->searchLineEdit->setText(searchText); +} + +void QPlainTextEditSearchWidget::setSearchMode(SearchMode searchMode) { + ui->modeComboBox->setCurrentIndex(searchMode); +} + +void QPlainTextEditSearchWidget::setDebounceDelay(uint debounceDelay) +{ + _debounceTimer.setInterval(static_cast(debounceDelay)); +} + +void QPlainTextEditSearchWidget::activate(bool focus) { + setReplaceMode(ui->modeComboBox->currentIndex() != + SearchMode::PlainTextMode); + show(); + + // preset the selected text as search text if there is any and there is no + // other search text + const QString selectedText = _textEdit->textCursor().selectedText(); + if (!selectedText.isEmpty() && ui->searchLineEdit->text().isEmpty()) { + ui->searchLineEdit->setText(selectedText); + } + + if (focus) { + ui->searchLineEdit->setFocus(); + } + + ui->searchLineEdit->selectAll(); + updateSearchExtraSelections(); + doSearchDown(); +} + +void QPlainTextEditSearchWidget::reset() { + ui->searchLineEdit->clear(); + setSearchMode(SearchMode::PlainTextMode); + setReplaceMode(false); + ui->searchCountLabel->setEnabled(false); +} + +void QPlainTextEditSearchWidget::updateSearchCountLabelText() { + ui->searchCountLabel->setEnabled(true); + ui->searchCountLabel->setText(QStringLiteral("%1/%2").arg( + _currentSearchResult == 0 ? QChar('-') + : QString::number(_currentSearchResult), + _searchResultCount == 0 ? QChar('-') + : QString::number(_searchResultCount))); +} + +void QPlainTextEditSearchWidget::setSearchSelectionColor(const QColor &color) { + selectionColor = color; +} + +void QPlainTextEditSearchWidget::on_modeComboBox_currentIndexChanged( + int index) { + Q_UNUSED(index) + doSearchCount(); + doSearchDown(); +} + +void QPlainTextEditSearchWidget::on_matchCaseSensitiveButton_toggled( + bool checked) { + Q_UNUSED(checked) + doSearchCount(); + doSearchDown(); +} diff --git a/client/qmarkdowntextedit/qplaintexteditsearchwidget.h b/client/qmarkdowntextedit/qplaintexteditsearchwidget.h index aaeb8b0..71dd5d7 100644 --- a/client/qmarkdowntextedit/qplaintexteditsearchwidget.h +++ b/client/qmarkdowntextedit/qplaintexteditsearchwidget.h @@ -1,51 +1,73 @@ /* - * Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com + * MIT License * - * 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; version 2 of the License. + * Copyright (c) 2014-2023 Patrizio Bekerle -- * - * 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. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ #pragma once -#include #include +#include +#include namespace Ui { class QPlainTextEditSearchWidget; } -class QPlainTextEditSearchWidget : public QWidget -{ +class QPlainTextEditSearchWidget : public QWidget { Q_OBJECT -public: - enum SearchMode { - PlainTextMode, - WholeWordsMode, - RegularExpressionMode - }; + public: + enum SearchMode { PlainTextMode, WholeWordsMode, RegularExpressionMode }; - explicit QPlainTextEditSearchWidget(QPlainTextEdit *parent = 0); - bool doSearch(bool searchDown = true, bool allowRestartAtTop = true); + explicit QPlainTextEditSearchWidget(QPlainTextEdit *parent = nullptr); + bool doSearch(bool searchDown = true, bool allowRestartAtTop = true, + bool updateUI = true); void setDarkMode(bool enabled); ~QPlainTextEditSearchWidget(); -private: - Ui::QPlainTextEditSearchWidget *ui; + void setSearchText(const QString &searchText); + void setSearchMode(SearchMode searchMode); + void setDebounceDelay(uint debounceDelay); + void activate(bool focus); + void clearSearchExtraSelections(); + void updateSearchExtraSelections(); -protected: + private: + Ui::QPlainTextEditSearchWidget *ui; + int _searchResultCount; + int _currentSearchResult; + QList _searchExtraSelections; + QColor selectionColor; + QTimer _debounceTimer; + QString _searchTerm; + void setSearchExtraSelections() const; + void stopDebounce(); + + protected: QPlainTextEdit *_textEdit; bool _darkMode; - bool eventFilter(QObject *obj, QEvent *event); + bool eventFilter(QObject *obj, QEvent *event) override; -public slots: + public Q_SLOTS: void activate(); void deactivate(); void doSearchDown(); @@ -54,7 +76,15 @@ public slots: void activateReplace(); bool doReplace(bool forAll = false); void doReplaceAll(); + void reset(); + void doSearchCount(); -protected slots: + protected Q_SLOTS: void searchLineEditTextChanged(const QString &arg1); + void performSearch(); + void updateSearchCountLabelText(); + void setSearchSelectionColor(const QColor &color); + private Q_SLOTS: + void on_modeComboBox_currentIndexChanged(int index); + void on_matchCaseSensitiveButton_toggled(bool checked); }; diff --git a/client/qmarkdowntextedit/qplaintexteditsearchwidget.ui b/client/qmarkdowntextedit/qplaintexteditsearchwidget.ui index efbba09..78ccb1d 100644 --- a/client/qmarkdowntextedit/qplaintexteditsearchwidget.ui +++ b/client/qmarkdowntextedit/qplaintexteditsearchwidget.ui @@ -26,10 +26,85 @@ 0 + + + + Find in text + + + + + + + Replace with + + + + + + + -/- + + + + + + + Find: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + Search backward + + + + + + + :/media/go-top.svg:/media/go-top.svg + + + true + + + + + + + Replace: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Close search + + + + + + + :/media/window-close.svg:/media/window-close.svg + + + true + + + + - replace text + Advanced search / replace text @@ -46,44 +121,10 @@ - - - - Find: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - close search - - - - - - - :/media/window-close.svg:/media/window-close.svg - - - true - - - - - - - find in text - - - - + - search forward + Search forward @@ -97,41 +138,37 @@ - - + + - search backward + Match case sensitive - - :/media/go-top.svg:/media/go-top.svg + + :/media/format-text-superscript.svg:/media/format-text-superscript.svg + + + true true - - - - replace with - - - - - + + - Replace: + Mode: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + QFrame::NoFrame @@ -147,7 +184,7 @@ 0 - 9 + 0 @@ -183,6 +220,9 @@ + + Replace one text occurrence + Replace @@ -193,8 +233,11 @@ + + Replace all text occurrences + - Replace All + Replace all false @@ -204,36 +247,6 @@ - - - - Mode: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Match case sensitive - - - - - - - :/media/format-text-superscript.svg:/media/format-text-superscript.svg - - - true - - - true - - - diff --git a/client/qmarkdowntextedit/screenshot.png b/client/qmarkdowntextedit/screenshot.png index ca3bf87..861749c 100644 Binary files a/client/qmarkdowntextedit/screenshot.png and b/client/qmarkdowntextedit/screenshot.png differ diff --git a/client/qmarkdowntextedit/scripts/clang-format-project.sh b/client/qmarkdowntextedit/scripts/clang-format-project.sh new file mode 100755 index 0000000..c2086d3 --- /dev/null +++ b/client/qmarkdowntextedit/scripts/clang-format-project.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# +# A tool to run clang-format on the entire project +# +# Some inspirations were taken from https://github.com/eklitzke/clang-format-all + +# Variable that will hold the name of the clang-format command +FMT="" + +# Some distros just call it clang-format. Others (e.g. Ubuntu) are insistent +# that the version number be part of the command. We prefer clang-format if +# that's present, otherwise we work backwards from highest version to lowest +# version. +for clangfmt in clang-format{,-{4,3}.{9,8,7,6,5,4,3,2,1,0}}; do + if which "$clangfmt" &>/dev/null; then + FMT="$clangfmt" + break + fi +done + +# Check if we found a working clang-format +if [ -z "$FMT" ]; then + echo "failed to find clang-format" + exit 1 +fi + +$FMT -i *.cpp +$FMT -i *.h diff --git a/client/qmarkdowntextedit/trans/qmarkdowntextedit_es.ts b/client/qmarkdowntextedit/trans/qmarkdowntextedit_es.ts new file mode 100644 index 0000000..d6a15b6 --- /dev/null +++ b/client/qmarkdowntextedit/trans/qmarkdowntextedit_es.ts @@ -0,0 +1,92 @@ + + + + + QPlainTextEditSearchWidget + + + Find in text + Buscar en el texto + + + + Replace with + Reemplazar por + + + + Find: + Buscar: + + + + Search backward + Buscar hacia atrás + + + + Replace: + Reemplazar: + + + + Close search + Cerrar búsqueda + + + + Advanced search / replace text + Búsqueda avanzada / reemplazar texto + + + + Search forward + Buscar hacia adelante + + + + Match case sensitive + Distingue mayúsculas y minúsculas + + + + Mode: + Modo: + + + + Plain text + Texto plano + + + + Whole words + Palabras enteras + + + + Regular expression + Expresión regular + + + + Replace one text occurrence + Reemplazar una ocurrencia del texto + + + + Replace + Reemplazar + + + + Replace all text occurrences + Reemplazar todas las ocurrencias del texto + + + + Replace all + Reemplazar todo + + + diff --git a/client/qmarkdowntextedit/trans/qmarkdowntextedit_ur.ts b/client/qmarkdowntextedit/trans/qmarkdowntextedit_ur.ts new file mode 100644 index 0000000..0f90b57 --- /dev/null +++ b/client/qmarkdowntextedit/trans/qmarkdowntextedit_ur.ts @@ -0,0 +1,57 @@ + + + + + QPlainTextEditSearchWidget + + + close search + تلاش بند کریں + + + + Find: + تلاش: + + + + replace text + ٹیکصٹ بدلیں + + + + find in text + متن میں تلاش کریں + + + + search forward + آگے تلاش کریں + + + + search backward + پیچھے تلاش کریں + + + + replace with + بدلیں اس سے + + + + Replace: + بدلیں: + + + + Replace + بدلیں + + + + Replace All + تمام کو بدل دیں + + + diff --git a/client/qmarkdowntextedit/trans/qmarkdowntextedit_zh_CN.ts b/client/qmarkdowntextedit/trans/qmarkdowntextedit_zh_CN.ts new file mode 100644 index 0000000..b9eb4b7 --- /dev/null +++ b/client/qmarkdowntextedit/trans/qmarkdowntextedit_zh_CN.ts @@ -0,0 +1,92 @@ + + + + + QPlainTextEditSearchWidget + + + Find in text + 在文本中查找 + + + + Replace with + 替换为 + + + + Find: + 查找 + + + + Search backward + 上一个匹配项 + + + + Replace: + 替换 + + + + Close search + 关闭搜索框 + + + + Advanced search / replace text + 高级搜索/替换 + + + + Search forward + 下一个匹配项 + + + + Match case sensitive + 区分大小写 + + + + Mode: + 模式 + + + + Plain text + 字符匹配 + + + + Whole words + 全字匹配 + + + + Regular expression + 正则表达式 + + + + Replace one text occurrence + 替换第一个匹配的文本 + + + + Replace + 替换 + + + + Replace all text occurrences + 替换所有匹配的文本 + + + + Replace all + 全部替换 + + + diff --git a/client/qtkeychain/.gitignore b/client/qtkeychain/.gitignore index 8fcb5ea..f3cc1d9 100644 --- a/client/qtkeychain/.gitignore +++ b/client/qtkeychain/.gitignore @@ -1,3 +1,76 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + #CMake files CMakeCache.txt CMakeFiles @@ -37,7 +110,6 @@ testclient install_manifest.txt *.manifest *.lib -*.exe #Mac build files qtkeychain.xcodeproj @@ -45,6 +117,6 @@ qtkeychain.build #Temporary files *.sw? -*~ - +/build-*/ +/compile_commands.json diff --git a/client/qtkeychain/CMakeLists.txt b/client/qtkeychain/CMakeLists.txt index 7e74837..0e2c883 100644 --- a/client/qtkeychain/CMakeLists.txt +++ b/client/qtkeychain/CMakeLists.txt @@ -1,31 +1,44 @@ -cmake_minimum_required (VERSION 2.8.11) -project (qtkeychain) +cmake_minimum_required(VERSION 3.16) -include (FindPkgConfig) +set(QTKEYCHAIN_VERSION 0.14.2) +set(QTKEYCHAIN_SOVERSION 1) + +project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX) + +# Enable C++11 +SET(CMAKE_CXX_STANDARD 11) + +include(FindPkgConfig) ### -set (QTKEYCHAIN_VERSION 0.9.90) -set (QTKEYCHAIN_SOVERSION 1) +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules") +include(GNUInstallDirs) +include(GenerateExportHeader) +include(CMakePackageConfigHelpers) +include(ECMSetupVersion) +include(ECMGeneratePriFile) +include(CMakeDependentOption) -### - -set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules") -include (GNUInstallDirs) -include (GenerateExportHeader) -include (ECMPackageConfigHelpers) -include (ECMSetupVersion) -include (ECMGeneratePriFile) - -option (BUILD_WITH_QT4 "Build qtkeychain with Qt4 no matter if Qt5 was found" OFF) -option (BUILD_TEST_APPLICATION "Build test application" OFF) -option (BUILD_TRANSLATIONS "Build translations" ON) -option (QTKEYCHAIN_STATIC "Build static library" ON) +option(BUILD_WITH_QT6 "Build qtkeychain with Qt 6" OFF) +option(BUILD_TEST_APPLICATION "Build test application" OFF) +option(BUILD_TRANSLATIONS "Build translations" OFF) +option(BUILD_SHARED_LIBS "Build dynamic library" OFF) +if(QTKEYCHAIN_STATIC) + set(BUILD_SHARED_LIBS OFF) + message(WARNING "QTKEYCHAIN_STATIC is deprecated. Use BUILD_SHARED_LIBS=OFF instead.") +endif() +CMAKE_DEPENDENT_OPTION(BUILD_TRANSLATIONS_AS_RESOURCES "Bundle translations with the library" OFF + "BUILD_TRANSLATIONS" OFF) if(CMAKE_SYSTEM_NAME STREQUAL Android) set(ANDROID 1) endif() +if(CMAKE_SYSTEM_NAME STREQUAL Haiku) + set(HAIKU 1) +endif() + if (WIN32) option(USE_CREDENTIAL_STORE "Build with windows CredentialStore support" ON) @@ -34,79 +47,78 @@ if (WIN32) endif() endif() -if( NOT BUILD_WITH_QT4 ) - # try Qt5 first, and prefer that if found - find_package(Qt5Core QUIET) +if( NOT BUILD_WITH_QT6 ) + find_package(Qt5 COMPONENTS Core REQUIRED) endif() -if (Qt5Core_FOUND AND NOT BUILD_WITH_QT4) - set(QTKEYCHAIN_VERSION_INFIX 5) +if (Qt5Core_FOUND AND NOT BUILD_WITH_QT6) + set(QTKEYCHAIN_VERSION_INFIX 5) - if(UNIX AND NOT APPLE AND NOT ANDROID) - find_package(Qt5DBus REQUIRED) - include_directories(${Qt5DBus_INCLUDE_DIRS}) - set(QTDBUS_LIBRARIES ${Qt5DBus_LIBRARIES}) - macro(qt_add_dbus_interface) - qt5_add_dbus_interface(${ARGN}) - endmacro() + if(ANDROID) + if(Qt5Core_VERSION VERSION_LESS 5.7) + find_package(Qt5 COMPONENTS Core REQUIRED Private) + include_directories(${Qt5Core_PRIVATE_INCLUDE_DIRS}) endif() - if(BUILD_TRANSLATIONS) - find_package(Qt5LinguistTools REQUIRED) - macro(qt_add_translation) - qt5_add_translation(${ARGN}) - endmacro(qt_add_translation) - macro(qt_create_translation) - qt5_create_translation(${ARGN}) - endmacro(qt_create_translation) - endif() + find_package(Qt5 COMPONENTS AndroidExtras REQUIRED) + include_directories(${Qt5AndroidExtras_INCLUDE_DIRS}) + set(QTANDROIDEXTRAS_LIBRARIES ${Qt5AndroidExtras_LIBRARIES}) + endif() - macro(qt_wrap_cpp) - qt5_wrap_cpp(${ARGN}) + if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + find_package(Qt5 COMPONENTS DBus REQUIRED) + include_directories(${Qt5DBus_INCLUDE_DIRS}) + set(QTDBUS_LIBRARIES ${Qt5DBus_LIBRARIES}) + macro(qt_add_dbus_interface) + qt5_add_dbus_interface(${ARGN}) endmacro() + endif() - set(QTCORE_LIBRARIES ${Qt5Core_LIBRARIES}) - include_directories (${Qt5Core_INCLUDE_DIRS}) - - if (NOT Qt5Core_VERSION VERSION_LESS "5.7.0") - if (CMAKE_COMPILER_IS_GNUCXX) - if ((NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "6.1.0")) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0") - message(FATAL_ERROR "Can't build QtKeychain using g++-${CMAKE_CXX_COMPILER_VERSION} and Qt ${Qt5Core_VERSION}: compiler supporting C++11 is required") - endif() - elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - if (NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 3.3) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - else() - message(FATAL_ERROR "Can't build QtKeychain using clang++-${CMAKE_CXX_COMPILER_VERSION} and Qt ${Qt5Core_VERSION}: compiler supporting C++11 is required") - endif() - elseif ((CMAKE_CXX_COMPILER_ID MATCHES "MSVC") AND (MSVC_VERSION LESS 1700)) - message(FATAL_ERROR "Can't build QtKeychain using VC++-${MSVC_VERSION} and Qt ${Qt5Core_VERSION}: compiler supporting C++11 is required") - endif() - endif() -else() - set(QTKEYCHAIN_VERSION_INFIX "") - if(UNIX AND NOT APPLE) - find_package(Qt4 COMPONENTS QtCore QtDBus REQUIRED) - set(QTDBUS_LIBRARIES ${QT_QTDBUS_LIBRARY}) - macro(qt_add_dbus_interface) - qt4_add_dbus_interface(${ARGN}) - endmacro() - else() - find_package(Qt4 COMPONENTS QtCore REQUIRED) - endif() - include_directories(${QT_INCLUDES}) - set(QTCORE_LIBRARIES ${QT_QTCORE_LIBRARY}) + if(BUILD_TRANSLATIONS) + find_package(Qt5 COMPONENTS LinguistTools REQUIRED) macro(qt_add_translation) - qt4_add_translation(${ARGN}) + qt5_add_translation(${ARGN}) endmacro(qt_add_translation) macro(qt_create_translation) - qt4_create_translation(${ARGN}) + qt5_create_translation(${ARGN}) endmacro(qt_create_translation) - macro(qt_wrap_cpp) - qt4_wrap_cpp(${ARGN}) + endif() + + macro(qt_wrap_cpp) + qt5_wrap_cpp(${ARGN}) + endmacro() + + set(QTCORE_LIBRARIES ${Qt5Core_LIBRARIES}) + include_directories(${Qt5Core_INCLUDE_DIRS}) +else() + find_package(Qt6 COMPONENTS Core REQUIRED) + set(QTKEYCHAIN_VERSION_INFIX 6) + + + if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + find_package(Qt6 COMPONENTS DBus REQUIRED) + include_directories(${Qt6DBus_INCLUDE_DIRS}) + set(QTDBUS_LIBRARIES ${Qt6DBus_LIBRARIES}) + macro(qt_add_dbus_interface) + qt6_add_dbus_interface(${ARGN}) endmacro() + endif() + + if(BUILD_TRANSLATIONS) + find_package(Qt6 COMPONENTS LinguistTools REQUIRED) + macro(qt_add_translation) + qt6_add_translation(${ARGN}) + endmacro(qt_add_translation) + macro(qt_create_translation) + qt6_create_translation(${ARGN}) + endmacro(qt_create_translation) + endif() + + macro(qt_wrap_cpp) + qt6_wrap_cpp(${ARGN}) + endmacro() + + set(QTCORE_LIBRARIES ${Qt6Core_LIBRARIES}) endif() @@ -117,9 +129,20 @@ set(qtkeychain_SOURCES keychain.cpp qkeychain_export.h keychain.h - ) +) -add_definitions( -Wall ) +if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + # CMake < 3.15 sneaks in /W# flags for us, so we need a replacement, + # or we'll get a warning (cf. CMP0092) + if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif() +else() + # MSVC's STL / Qt headers are not MSVC -Wall clean, so don't enable it there + add_definitions( -Wall -Werror=return-type ) +endif() if(WIN32) list(APPEND qtkeychain_SOURCES keychain_win.cpp) @@ -134,105 +157,139 @@ if(WIN32) endif() if(APPLE) - if(IOS) - list(APPEND qtkeychain_SOURCES keychain_ios.cpp) - else() - list(APPEND qtkeychain_SOURCES keychain_mac.cpp) - endif() - - find_library(COREFOUNDATION_LIBRARY CoreFoundation REQUIRED) - list(APPEND qtkeychain_LIBRARIES ${COREFOUNDATION_LIBRARY}) - - find_library(SECURITY_LIBRARY Security REQUIRED) - list(APPEND qtkeychain_LIBRARIES ${SECURITY_LIBRARY}) + list(APPEND qtkeychain_SOURCES keychain_apple.mm) + list(APPEND qtkeychain_LIBRARIES "-framework Foundation" "-framework Security") endif() -if(UNIX AND NOT APPLE AND NOT ANDROID) - option(LIBSECRET_SUPPORT "Build with libsecret support" ON) +if(HAIKU) + list(APPEND qtkeychain_SOURCES keychain_haiku.cpp) - if(LIBSECRET_SUPPORT) - pkg_check_modules(LIBSECRET libsecret-1) + find_library(BE_LIBRARY be REQUIRED) + list(APPEND qtkeychain_LIBRARIES ${BE_LIBRARY}) +endif() - if (LIBSECRET_FOUND) - add_definitions(-DHAVE_LIBSECRET=1) - endif() - INCLUDE_DIRECTORIES(${LIBSECRET_INCLUDE_DIRS}) - list(APPEND qtkeychain_LIBRARIES ${LIBSECRET_LIBRARIES}) - endif() +if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) + option(LIBSECRET_SUPPORT "Build with libsecret support" OFF) + #if(LIBSECRET_SUPPORT) + # pkg_check_modules(LIBSECRET REQUIRED libsecret-1) + # add_definitions(-DHAVE_LIBSECRET=1) + # INCLUDE_DIRECTORIES(${LIBSECRET_INCLUDE_DIRS}) + # LINK_DIRECTORIES(${LIBSECRET_LIBRARY_DIRS}) + # list(APPEND qtkeychain_LIBRARIES_PRIVATE ${LIBSECRET_LIBRARIES}) + #endif() + + add_definitions(-DKEYCHAIN_DBUS=1) list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.cpp libsecret.cpp plaintextstore.cpp) qt_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface) list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES} ) endif() +if(ANDROID) + list(APPEND qtkeychain_SOURCES keychain_android.cpp androidkeystore.cpp plaintextstore.cpp) + list(APPEND qtkeychain_LIBRARIES_PRIVATE ${QTANDROIDEXTRAS_LIBRARIES} ) +endif() + QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h gnomekeyring_p.h) set(qtkeychain_TR_FILES - translations/qtkeychain_de.ts - translations/qtkeychain_ro.ts - ) + translations/qtkeychain_de.ts + translations/qtkeychain_fr.ts + translations/qtkeychain_ro.ts + translations/qtkeychain_ru.ts + translations/qtkeychain_zh.ts +) + +set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain) +add_library(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) +if(WIN32) + set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" ) +endif() file(GLOB qtkeychain_TR_SOURCES *.cpp *.h *.ui) if ( BUILD_TRANSLATIONS ) qt_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES}) qt_add_translation(qtkeychain_QM_FILES ${qtkeychain_TR_FILES}) add_custom_target(messages DEPENDS ${qtkeychain_MESSAGES}) - add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES}) + add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES} messages) + # https://github.com/frankosterfeld/qtkeychain/issues/185 + add_dependencies(${QTKEYCHAIN_TARGET_NAME} translations) - if(NOT QT_TRANSLATIONS_DIR) - # If this directory is missing, we are in a Qt5 environment. - # Extract the qmake executable location - get_target_property(QT5_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) - # Ask Qt5 where to put the translations - execute_process( COMMAND ${QT5_QMAKE_EXECUTABLE} -query QT_INSTALL_TRANSLATIONS - OUTPUT_VARIABLE qt_translations_dir OUTPUT_STRIP_TRAILING_WHITESPACE ) - # make sure we have / and not \ as qmake gives on windows - file( TO_CMAKE_PATH "${qt_translations_dir}" qt_translations_dir) - set( QT_TRANSLATIONS_DIR ${qt_translations_dir} CACHE PATH - "The location of the Qt translations" FORCE) + if (BUILD_TRANSLATIONS_AS_RESOURCES) + set(QM_FILE_LIST "") + foreach(FILE ${qtkeychain_QM_FILES}) + list(APPEND QM_FILE_LIST "${FILE}") + endforeach() + string(REPLACE ";" "" QM_FILE_LIST ${QM_FILE_LIST}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/translations/translations.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) + target_sources(${QTKEYCHAIN_TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) + else() + if(QTKEYCHAIN_VERSION_INFIX EQUAL 5 AND QT_TRANSLATIONS_DIR AND NOT QTKEYCHAIN_TRANSLATIONS_DIR) + # Back compatibility with pre-0.11 versions + message (WARNING "QT_TRANSLATIONS_DIR is deprecated, use QTKEYCHAIN_TRANSLATIONS_DIR instead") + set(QTKEYCHAIN_TRANSLATIONS_DIR ${QT_TRANSLATIONS_DIR} + CACHE PATH "The location of the QtKeychain translations" FORCE) + else() + set(QTKEYCHAIN_TRANSLATIONS_DIR + ${CMAKE_INSTALL_DATADIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/translations + CACHE PATH "The location of the QtKeychain translations" ) + endif() + + install(FILES ${qtkeychain_QM_FILES} DESTINATION ${QTKEYCHAIN_TRANSLATIONS_DIR}) endif() - - install(FILES ${qtkeychain_QM_FILES} - DESTINATION ${QT_TRANSLATIONS_DIR}) endif( BUILD_TRANSLATIONS ) -set(QTKEYCHAIN_TARGET_NAME qtkeychain) -if(NOT QTKEYCHAIN_STATIC) - add_library(${QTKEYCHAIN_TARGET_NAME} SHARED ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) -else() - add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES}) +target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE}) +if(NOT INTERFACE_INCLUDE_SUFFIX) + set(INTERFACE_INCLUDE_SUFFIX include) endif() - -target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES}) -target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $) +target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $) generate_export_header(${QTKEYCHAIN_TARGET_NAME} - EXPORT_FILE_NAME qkeychain_export.h - EXPORT_MACRO_NAME QKEYCHAIN_EXPORT - ) + EXPORT_FILE_NAME qkeychain_export.h + EXPORT_MACRO_NAME QKEYCHAIN_EXPORT +) set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES VERSION ${QTKEYCHAIN_VERSION} SOVERSION ${QTKEYCHAIN_SOVERSION} - MACOSX_RPATH 1 - INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" INSTALL_RPATH_USE_LINK_PATH TRUE - ) +) + +if (NOT APPLE) + set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES + INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" + ) +endif() install(FILES keychain.h ${CMAKE_CURRENT_BINARY_DIR}/qkeychain_export.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/ - ) + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/ +) install(TARGETS ${QTKEYCHAIN_TARGET_NAME} - EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - ) + EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) if(BUILD_TEST_APPLICATION) + set( testclient_LIBRARIES ${QTKEYCHAIN_TARGET_NAME} ) + + if(APPLE) + list(APPEND testclient_LIBRARIES "-framework Cocoa") + + if (BUILD_WITH_QT6) + find_package(Qt6 COMPONENTS Gui REQUIRED) + list(APPEND testclient_LIBRARIES Qt6::Gui) + else() + find_package(Qt5 COMPONENTS Gui REQUIRED) + list(APPEND testclient_LIBRARIES Qt5::Gui) + endif() + + endif() add_executable( testclient testclient.cpp ) - target_link_libraries( testclient ${QTKEYCHAIN_TARGET_NAME}) + target_link_libraries( testclient ${testclient_LIBRARIES}) endif() @@ -240,32 +297,32 @@ endif() ### CMake config file ### -ecm_configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/QtKeychainConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake" - INSTALL_DESTINATION Qt${QTKEYCHAIN_VERSION_INFIX}Keychain) +configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/QtKeychainConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake" + INSTALL_DESTINATION Qt${QTKEYCHAIN_VERSION_INFIX}Keychain) ecm_setup_version("${QTKEYCHAIN_VERSION}" VARIABLE_PREFIX SNORE - PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake" - SOVERSION ${QTKEYCHAIN_VERSION}) + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake" + SOVERSION ${QTKEYCHAIN_VERSION}) -if(UNIX AND NOT APPLE AND NOT ANDROID) +if(UNIX AND NOT APPLE AND NOT ANDROID AND NOT HAIKU) set(PRI_EXTRA_DEPS "dbus") endif() ecm_generate_pri_file(BASE_NAME Qt${QTKEYCHAIN_VERSION_INFIX}Keychain - LIB_NAME ${QTKEYCHAIN_TARGET_NAME} - DEPS "core ${PRI_EXTRA_DEPS}" - INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR} - FILENAME_VAR pri_filename) + LIB_NAME ${QTKEYCHAIN_TARGET_NAME} + DEPS "core ${PRI_EXTRA_DEPS}" + INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR} + FILENAME_VAR pri_filename) install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) install(EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain" - ) +) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake - ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain - ) + ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain +) diff --git a/client/qtkeychain/COPYING b/client/qtkeychain/COPYING index cca2a5c..69f70ff 100644 --- a/client/qtkeychain/COPYING +++ b/client/qtkeychain/COPYING @@ -7,6 +7,9 @@ are met: 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES diff --git a/client/qtkeychain/ChangeLog b/client/qtkeychain/ChangeLog index d04ac0b..6d8b6eb 100644 --- a/client/qtkeychain/ChangeLog +++ b/client/qtkeychain/ChangeLog @@ -1,6 +1,65 @@ ChangeLog ========= +version 0.14.2 (release 2023-12-17) + + - Add support for KWallet 6 (Volker Krause ) + +version 0.14.1 (release 2023-06-01) + + - Export QKeychain::isAvailable() to make it usable in a shared build (Volker Krause ) + - Protect against creating the QtKeychain::QtKeychain alias target twice (Volker Krause ) + +version 0.14.0 (release 2023-05-12) + + - Add Qt 6 Android support (Igor Bugaev ) + - Add QtQuick client example ((Igor Bugaev ) + - Added Dutch translation (Heimen Stoffels ) + - Fix potential freezing with Apple keychain (Claudio Cambra ) + - Add API to check whether a secure backend is available at all (Volker Krause ) + +version 0.13.2 (release 2021-11-18) + + - CMake: Deprecate QTKEYCHAIN_STATIC in favor of BUILD_SHARED_LIBS (be@mixxx.org) + +version 0.13.1 (release 2021-11-08) + + - KWallet: Fix deletion of entries (Issue #199) + +version 0.13.0 (release 2021-11-07) + + - Linux: Require libsecret if not explicitly disabled + - Unify implementations for macOS and iOS + - CMake: lots of fixes + +version 0.12.0 (release 2020-12-16) + + * Add Qt 6 support, drop Qt 4 support + * Require C++11 + * Add Android support (Mathias Hasselmann) + +version 0.11.1 (release 2020-09-08) + + * Build system fixes + +version 0.11.0 (release 2020-09-08) + + * Important: Debug builds on Windows now get the "d" suffix + * Various build system fixes + * Add Haiku support (François Revol ) + * Translation: Russian (Alexander Gorishnyak ) + * Translation: Update French (David Geiger ) + +version 0.10.0 (release 2019-12-17) + + * Detect XFCE desktop correctly. (Sandro Knauß ) + * Windows Use CRED_PERSIST_ENTERPRISE (Olivier Goffart ) + * Windows: Improve CredWrite() error handling (Christian Kamm ) + * Fix build with Qt 5.12.x (Sergey Ilinykh ) + * Fix Qt 4 build (Robert-André Mauchin ) + * Translation: Mandarin (Taiwan) (Poren Chiang ) + * Translation: French (François Revol ) + version 0.9.1 (release 2018-08-20) * Windows Credential Store: Use CRED_PERSIST_ENTERPRISE (Olivier Goffart ) * Secret: Don't match the schema name #114 (Christian Kamm ) diff --git a/client/qtkeychain/QtKeychainConfig.cmake.in b/client/qtkeychain/QtKeychainConfig.cmake.in index 3196bff..ead0005 100644 --- a/client/qtkeychain/QtKeychainConfig.cmake.in +++ b/client/qtkeychain/QtKeychainConfig.cmake.in @@ -2,21 +2,27 @@ # It defines the following variables # QTKEYCHAIN_INCLUDE_DIRS - include directories for QtKeychain # QTKEYCHAIN_LIBRARIES - libraries to link against +# as well as the following imported targets +# qt5keychain / qt6keychain +# Qt5Keychain::Qt5Keychain / Qt6Keychain::Qt6Keychain @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainLibraryDepends.cmake") +include(CMakeFindDependencyMacro) -if("@QTKEYCHAIN_VERSION_INFIX@" STREQUAL "5") - find_dependency(Qt5Core) - - if(UNIX AND NOT APPLE) - find_dependency(Qt5DBus) - endif() -else() - find_dependency(Qt4 COMPONENTS QtCore) +find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@Core) + +if(UNIX AND NOT APPLE AND NOT ANDROID) + find_dependency(Qt@QTKEYCHAIN_VERSION_INFIX@DBus) endif() set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@") get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES) + +if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0 AND NOT TARGET Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain) + add_library(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain::Qt@QTKEYCHAIN_VERSION_INFIX@Keychain ALIAS qt@QTKEYCHAIN_VERSION_INFIX@keychain) +endif() + +check_required_components(Qt@QTKEYCHAIN_VERSION_INFIX@Keychain) diff --git a/client/qtkeychain/ReadMe.markdown b/client/qtkeychain/ReadMe.markdown deleted file mode 120000 index 3cefeef..0000000 --- a/client/qtkeychain/ReadMe.markdown +++ /dev/null @@ -1 +0,0 @@ -ReadMe.txt \ No newline at end of file diff --git a/client/qtkeychain/ReadMe.md b/client/qtkeychain/ReadMe.md new file mode 100644 index 0000000..cf0a2e1 --- /dev/null +++ b/client/qtkeychain/ReadMe.md @@ -0,0 +1,29 @@ +QtKeychain +========== + +QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform: + + * **macOS:** Passwords are stored in the macOS Keychain. + + * **Linux/Unix:** If running, GNOME Keyring is used, otherwise QtKeychain tries to use KWallet (via D-Bus), if available. Libsecret (common API for desktop-specific solutions) + is also supported. + + * **Windows:** By default, the Windows Credential Store is used (requires Windows 7 or newer). +Pass `-DUSE_CREDENTIAL_STORE=OFF` to cmake to disable it. If disabled, QtKeychain uses the Windows API function +[CryptProtectData](http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261%28v=vs.85%29.aspx "CryptProtectData function") +to encrypt the password with the user's logon credentials. The encrypted data is then persisted via QSettings. + + * **Android and iOS:** Passwords are stored in the Android keystore system and iOS keychain, respectively. + +In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (`setInsecureFallback( true )`). + + +Requirements +------------ + +QtKeychain 0.12 and newer supports Qt 5 and Qt 6 and requires a compiler with C++11 support. Older versions support Qt 4 and Qt 5. + +License +------- + +QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details. diff --git a/client/qtkeychain/ReadMe.txt b/client/qtkeychain/ReadMe.txt deleted file mode 100644 index 6fa68b6..0000000 --- a/client/qtkeychain/ReadMe.txt +++ /dev/null @@ -1,17 +0,0 @@ -QtKeychain -========== - -QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform: - - * **Mac OS X:** Passwords are stored in the OS X Keychain. - - * **Linux/Unix:** If running, GNOME Keyring is used, otherwise qtkeychain tries to use KWallet (via D-Bus), if available. - - * **Windows:** By default, the Windows Credential Store is used (requires Windows 7 or newer). -Pass -DUSE_CREDENTIAL_STORE=OFF to cmake use disable it. If disabled, QtKeychain uses the Windows API function -[CryptProtectData](http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261%28v=vs.85%29.aspx "CryptProtectData function") -to encrypt the password with the user's logon credentials. The encrypted data is then persisted via QSettings. - -In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (setInsecureFallback( true )). - -**License:** QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details. diff --git a/client/qtkeychain/TestAppExample/.gitignore b/client/qtkeychain/TestAppExample/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/client/qtkeychain/TestAppExample/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/client/qtkeychain/TestAppExample/TestAppExample.pro b/client/qtkeychain/TestAppExample/TestAppExample.pro new file mode 100644 index 0000000..3ba33ef --- /dev/null +++ b/client/qtkeychain/TestAppExample/TestAppExample.pro @@ -0,0 +1,29 @@ +QT += quick + +CONFIG += c++11 + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +include(../qtkeychain.pri) + +SOURCES += \ + keychainclass.cpp \ + main.cpp + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +HEADERS += \ + keychainclass.h diff --git a/client/qtkeychain/TestAppExample/keychainclass.cpp b/client/qtkeychain/TestAppExample/keychainclass.cpp new file mode 100644 index 0000000..1f09ced --- /dev/null +++ b/client/qtkeychain/TestAppExample/keychainclass.cpp @@ -0,0 +1,61 @@ +#include + +#include "keychainclass.h" + +KeyChainClass::KeyChainClass(QObject* parent) : + QObject(parent), + m_readCredentialJob(QLatin1String("keychain.example.project.app")), + m_writeCredentialJob(QLatin1String("keychain.example.project.app")), + m_deleteCredentialJob(QLatin1String("keychain.example.project.app")) +{ + m_readCredentialJob.setAutoDelete(false); + m_writeCredentialJob.setAutoDelete(false); + m_deleteCredentialJob.setAutoDelete(false); +} + +void KeyChainClass::readKey(const QString &key) +{ + m_readCredentialJob.setKey(key); + + QObject::connect(&m_readCredentialJob, &QKeychain::ReadPasswordJob::finished, [=](){ + if (m_readCredentialJob.error()) { + emit error(tr("Read key failed: %1").arg(qPrintable(m_readCredentialJob.errorString()))); + return; + } + emit keyRestored(key, m_readCredentialJob.textData()); + }); + + m_readCredentialJob.start(); +} + +void KeyChainClass::writeKey(const QString &key, const QString &value) +{ + m_writeCredentialJob.setKey(key); + + QObject::connect(&m_writeCredentialJob, &QKeychain::WritePasswordJob::finished, [=](){ + if (m_writeCredentialJob.error()) { + emit error(tr("Write key failed: %1").arg(qPrintable(m_writeCredentialJob.errorString()))); + return; + } + + emit keyStored(key); + }); + + m_writeCredentialJob.setTextData(value); + m_writeCredentialJob.start(); +} + +void KeyChainClass::deleteKey(const QString &key) +{ + m_deleteCredentialJob.setKey(key); + + QObject::connect(&m_deleteCredentialJob, &QKeychain::DeletePasswordJob::finished, [=](){ + if (m_deleteCredentialJob.error()) { + emit error(tr("Delete key failed: %1").arg(qPrintable(m_deleteCredentialJob.errorString()))); + return; + } + emit keyDeleted(key); + }); + + m_deleteCredentialJob.start(); +} diff --git a/client/qtkeychain/TestAppExample/keychainclass.h b/client/qtkeychain/TestAppExample/keychainclass.h new file mode 100644 index 0000000..ea747d7 --- /dev/null +++ b/client/qtkeychain/TestAppExample/keychainclass.h @@ -0,0 +1,30 @@ +#ifndef KEYCHAINCLASS_H +#define KEYCHAINCLASS_H + +#include + +#include + +class KeyChainClass: public QObject +{ + Q_OBJECT +public: + KeyChainClass(QObject* parent = nullptr); + + Q_INVOKABLE void readKey(const QString& key); + Q_INVOKABLE void writeKey(const QString& key, const QString& value); + Q_INVOKABLE void deleteKey(const QString& key); + +Q_SIGNALS: + void keyStored(const QString& key); + void keyRestored(const QString& key, const QString& value); + void keyDeleted(const QString& key); + void error(const QString& errorText); + +private: + QKeychain::ReadPasswordJob m_readCredentialJob; + QKeychain::WritePasswordJob m_writeCredentialJob; + QKeychain::DeletePasswordJob m_deleteCredentialJob; +}; + +#endif // KEYCHAINCLASS_H diff --git a/client/qtkeychain/TestAppExample/main.cpp b/client/qtkeychain/TestAppExample/main.cpp new file mode 100644 index 0000000..6136784 --- /dev/null +++ b/client/qtkeychain/TestAppExample/main.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "keychainclass.h" + +int main(int argc, char *argv[]) +{ +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + const QUrl url(QStringLiteral("qrc:/main.qml")); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + &app, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + + KeyChainClass keyChainClass; + + engine.rootContext()->setContextProperty("KeyChain", &keyChainClass); + engine.load(url); + + return app.exec(); +} diff --git a/client/qtkeychain/TestAppExample/main.qml b/client/qtkeychain/TestAppExample/main.qml new file mode 100644 index 0000000..4023aca --- /dev/null +++ b/client/qtkeychain/TestAppExample/main.qml @@ -0,0 +1,131 @@ +import QtQuick 2.13 +import QtQuick.Window 2.13 +import QtQuick.Controls 2.12 + +Window { + id: root + + width: 640 + height: 480 + visible: true + + Column { + anchors { + fill: parent + margins: 50 + } + spacing: 20 + + Label { + text: 'Key name:' + font.pixelSize: 20 + } + + TextField { + id: keyNameTextField + + width: parent.width + height: 50 + + text: 'default key name' + } + + Label { + text: 'Key value:' + font.pixelSize: 20 + } + + TextField { + id: keyValueTextField + + width: parent.width + height: 50 + + text: 'some value' + } + + Label { + id: infoLabel + + width: parent.width + wrapMode: Text.Wrap + visible: false + + onVisibleChanged: if (visible) hideAnimation.start(); + + SequentialAnimation { + id: hideAnimation + + PauseAnimation { + duration: 10000 + } + + ScriptAction { + script: infoLabel.visible = false + } + } + + Component.onCompleted: { + KeyChain.keyStored.connect((key) => { + infoLabel.text = String("Key '%1' successfully stored").arg(key) + infoLabel.color = 'green' + infoLabel.visible = true + }) + + KeyChain.keyRestored.connect((key, value) => { + infoLabel.text = String("Key '%1' successfully restored with data '%2'").arg(key).arg(value) + infoLabel.color = 'green' + infoLabel.visible = true + }) + + KeyChain.keyDeleted.connect((key) => { + infoLabel.text = String("Key '%1' successfully deleted").arg(key) + infoLabel.color = 'green' + infoLabel.visible = true + }) + + KeyChain.error.connect((errorText) => { + infoLabel.text = errorText + infoLabel.color = 'red' + infoLabel.visible = true + }) + } + } + + Row { + width: parent.width + height: 50 + spacing: 20 + + Button { + width: 80 + height: parent.height + text: 'Store' + + onClicked: { + KeyChain.writeKey(keyNameTextField.text.trim(), keyValueTextField.text.trim()) + } + } + + Button { + width: 80 + height: parent.height + text: 'Restore' + + onClicked: { + KeyChain.readKey(keyNameTextField.text.trim()) + } + } + + Button { + width: 80 + height: parent.height + text: 'Delete' + onClicked: { + KeyChain.deleteKey(keyNameTextField.text.trim()) + } + } + } + + } +} diff --git a/client/qtkeychain/TestAppExample/qml.qrc b/client/qtkeychain/TestAppExample/qml.qrc new file mode 100644 index 0000000..5f6483a --- /dev/null +++ b/client/qtkeychain/TestAppExample/qml.qrc @@ -0,0 +1,5 @@ + + + main.qml + + diff --git a/client/qtkeychain/androidkeystore.cpp b/client/qtkeychain/androidkeystore.cpp new file mode 100644 index 0000000..da58084 --- /dev/null +++ b/client/qtkeychain/androidkeystore.cpp @@ -0,0 +1,304 @@ +#include "androidkeystore_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) +#include "private/qjni_p.h" +#endif + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#endif + +using namespace QKeychain; + +using namespace android::content; +using namespace android::security; + +using namespace java::io; +using namespace java::lang; +using namespace java::math; +using namespace java::util; +using namespace java::security; +using namespace java::security::spec; + +using namespace javax::crypto; +using namespace javax::security::auth::x500; +using namespace javax::security::cert; + +const BigInteger BigInteger::ONE = BigInteger::getStaticObjectField("java/math/BigInteger", "ONE", "Ljava/math/BigInteger;"); + +const int Calendar::YEAR = Calendar::getStaticField("java/util/Calendar", "YEAR"); + +const int Cipher::DECRYPT_MODE = Cipher::getStaticField("javax/crypto/Cipher", "DECRYPT_MODE"); +const int Cipher::ENCRYPT_MODE = Cipher::getStaticField("javax/crypto/Cipher", "ENCRYPT_MODE"); + +namespace { + +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) + +struct JNIObject +{ + JNIObject(QSharedPointer d): d(d) {} + + static JNIObject fromLocalRef(jobject o) + { + return JNIObject(QSharedPointer::create(QJNIObjectPrivate::fromLocalRef(o))); + } + + jobject object() const { return d->object(); } + QSharedPointer d; +}; + +#else + +using JNIObject = QAndroidJniObject; + +#endif + +QByteArray fromArray(const jbyteArray array) +{ + QAndroidJniEnvironment env; + jbyte *const bytes = env->GetByteArrayElements(array, nullptr); + const QByteArray result(reinterpret_cast(bytes), env->GetArrayLength(array)); + env->ReleaseByteArrayElements(array, bytes, JNI_ABORT); + return result; +} + +JNIObject toArray(const QByteArray &bytes) +{ + QAndroidJniEnvironment env; + const int length = bytes.length(); + JNIObject array = JNIObject::fromLocalRef(env->NewByteArray(length)); + env->SetByteArrayRegion(static_cast(array.object()), + 0, length, reinterpret_cast(bytes.constData())); + return array; +} + +} + +bool Object::handleExceptions() +{ + QAndroidJniEnvironment env; + + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + + return true; +} + + +KeyPairGenerator KeyPairGenerator::getInstance(const QString &algorithm, const QString &provider) +{ + return handleExceptions(callStaticObjectMethod("java/security/KeyPairGenerator", "getInstance", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/security/KeyPairGenerator;", + fromString(algorithm).object(), fromString(provider).object())); +} + +KeyPair KeyPairGenerator::generateKeyPair() const +{ + return handleExceptions(callObjectMethod("generateKeyPair", "()Ljava/security/KeyPair;")); +} + +bool KeyPairGenerator::initialize(const AlgorithmParameterSpec &spec) const +{ + callMethod("initialize", "(Ljava/security/spec/AlgorithmParameterSpec;)V", spec.object()); + return handleExceptions(); +} + +bool KeyStore::containsAlias(const QString &alias) const +{ + return handleExceptions(callMethod("containsAlias", "(Ljava/lang/String;)Z", + fromString(alias).object())); +} + +bool KeyStore::deleteEntry(const QString &alias) const +{ + callMethod("deleteEntry", "(Ljava/lang/String;)V", fromString(alias).object()); + return handleExceptions(); +} + +KeyStore KeyStore::getInstance(const QString &type) +{ + return handleExceptions(callStaticObjectMethod("java/security/KeyStore", "getInstance", + "(Ljava/lang/String;)Ljava/security/KeyStore;", + fromString(type).object())); +} + +KeyStore::Entry KeyStore::getEntry(const QString &alias, const KeyStore::ProtectionParameter ¶m) const +{ + return handleExceptions(callObjectMethod("getEntry", + "(Ljava/lang/String;Ljava/security/KeyStore$ProtectionParameter;)Ljava/security/KeyStore$Entry;", + fromString(alias).object(), param.object())); +} + +bool KeyStore::load(const KeyStore::LoadStoreParameter ¶m) const +{ + callMethod("load", "(Ljava/security/KeyStore$LoadStoreParameter;)V", param.object()); + return handleExceptions(); +} + + +Calendar Calendar::getInstance() +{ + return handleExceptions(callStaticObjectMethod("java/util/Calendar", "getInstance", + "()Ljava/util/Calendar;")); + +} + +bool Calendar::add(int field, int amount) const +{ + callMethod("add", "(II)V", field, amount); + return handleExceptions(); +} + +Date Calendar::getTime() const +{ + return handleExceptions(callObjectMethod("getTime", "()Ljava/util/Date;")); +} + +KeyPairGeneratorSpec::Builder::Builder(const Context &context) + : Object(QAndroidJniObject("android/security/KeyPairGeneratorSpec$Builder", + "(Landroid/content/Context;)V", + context.object())) +{ + handleExceptions(); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setAlias(const QString &alias) const +{ + return handleExceptions(callObjectMethod("setAlias", + "(Ljava/lang/String;)Landroid/security/KeyPairGeneratorSpec$Builder;", + fromString(alias).object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setSubject(const X500Principal &subject) const +{ + return handleExceptions(callObjectMethod("setSubject", + "(Ljavax/security/auth/x500/X500Principal;)Landroid/security/KeyPairGeneratorSpec$Builder;", + subject.object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setSerialNumber(const BigInteger &serial) const +{ + return handleExceptions(callObjectMethod("setSerialNumber", + "(Ljava/math/BigInteger;)Landroid/security/KeyPairGeneratorSpec$Builder;", + serial.object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setStartDate(const Date &date) const +{ + return handleExceptions(callObjectMethod("setStartDate", + "(Ljava/util/Date;)Landroid/security/KeyPairGeneratorSpec$Builder;", + date.object())); +} + +KeyPairGeneratorSpec::Builder KeyPairGeneratorSpec::Builder::setEndDate(const Date &date) const +{ + return handleExceptions(callObjectMethod("setEndDate", + "(Ljava/util/Date;)Landroid/security/KeyPairGeneratorSpec$Builder;", + date.object())); +} + +KeyPairGeneratorSpec KeyPairGeneratorSpec::Builder::build() const +{ + return handleExceptions(callObjectMethod("build", "()Landroid/security/KeyPairGeneratorSpec;")); +} + +X500Principal::X500Principal(const QString &name) + : Object(QAndroidJniObject("javax/security/auth/x500/X500Principal", + "(Ljava/lang/String;)V", + fromString(name).object())) +{ + handleExceptions(); +} + +Certificate KeyStore::PrivateKeyEntry::getCertificate() const +{ + return handleExceptions(callObjectMethod("getCertificate", "()Ljava/security/cert/Certificate;")); +} + +PrivateKey KeyStore::PrivateKeyEntry::getPrivateKey() const +{ + return handleExceptions(callObjectMethod("getPrivateKey", "()Ljava/security/PrivateKey;")); +} + +PublicKey Certificate::getPublicKey() const +{ + return handleExceptions(callObjectMethod("getPublicKey", "()Ljava/security/PublicKey;")); +} + +ByteArrayInputStream::ByteArrayInputStream(const QByteArray &bytes) + : InputStream(QAndroidJniObject("java/io/ByteArrayInputStream", "([B)V", toArray(bytes).object())) +{ +} + +ByteArrayOutputStream::ByteArrayOutputStream() + : OutputStream(QAndroidJniObject("java/io/ByteArrayOutputStream")) +{ + handleExceptions(); +} + +QByteArray ByteArrayOutputStream::toByteArray() const +{ + const QAndroidJniObject wrapper = callObjectMethod("toByteArray"); + + if (!handleExceptions()) + return QByteArray(); + + return fromArray(static_cast(wrapper.object())); +} + +int InputStream::read() const +{ + return handleExceptions(callMethod("read"), -1); +} + +bool OutputStream::write(const QByteArray &bytes) const +{ + callMethod("write", "([B)V", toArray(bytes).object()); + return handleExceptions(); +} + +bool OutputStream::close() const +{ + callMethod("close"); + return handleExceptions(); +} + +bool OutputStream::flush() const +{ + callMethod("flush"); + return handleExceptions(); +} + +Cipher Cipher::getInstance(const QString &transformation) +{ + return handleExceptions(callStaticObjectMethod("javax/crypto/Cipher", "getInstance", + "(Ljava/lang/String;)Ljavax/crypto/Cipher;", + fromString(transformation).object())); +} + +bool Cipher::init(int opMode, const Key &key) const +{ + callMethod("init", "(ILjava/security/Key;)V", opMode, key.object()); + return handleExceptions(); +} + + +CipherOutputStream::CipherOutputStream(const OutputStream &stream, const Cipher &cipher) + : FilterOutputStream(QAndroidJniObject("javax/crypto/CipherOutputStream", + "(Ljava/io/OutputStream;Ljavax/crypto/Cipher;)V", + stream.object(), cipher.object())) +{ + handleExceptions(); +} + +CipherInputStream::CipherInputStream(const InputStream &stream, const Cipher &cipher) + : FilterInputStream(QAndroidJniObject("javax/crypto/CipherInputStream", + "(Ljava/io/InputStream;Ljavax/crypto/Cipher;)V", + stream.object(), cipher.object())) +{ + handleExceptions(); +} diff --git a/client/qtkeychain/androidkeystore_p.h b/client/qtkeychain/androidkeystore_p.h new file mode 100644 index 0000000..925c741 --- /dev/null +++ b/client/qtkeychain/androidkeystore_p.h @@ -0,0 +1,382 @@ +/****************************************************************************** + * Copyright (C) 2016 Mathias Hasselmann * + * * + * 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. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ + +#ifndef QTKEYCHAIN_ANDROIDKEYSTORE_P_H +#define QTKEYCHAIN_ANDROIDKEYSTORE_P_H + +#include + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#else +#include +#include + +typedef QJniObject QAndroidJniObject; +typedef QJniEnvironment QAndroidJniEnvironment; + +#endif + +namespace QKeychain { + +namespace javax { +namespace security { + +namespace auth { namespace x500 { class X500Principal; } } +namespace cert { class Certificate; } + +} +} + +namespace java { +namespace lang { + +class Object : protected QAndroidJniObject +{ +public: + inline Object(jobject object) : QAndroidJniObject(object) {} + inline Object(const QAndroidJniObject &object) : QAndroidJniObject(object) {} + inline operator bool() const { return isValid(); } + + using QAndroidJniObject::object; + using QAndroidJniObject::toString; + +protected: + static bool handleExceptions(); + + template + static T handleExceptions(const T &result, const T &resultOnError = T()); +}; + +template +inline T Object::handleExceptions(const T &result, const T &resultOnError) +{ + if (!handleExceptions()) + return resultOnError; + + return result; +} + +} // namespace lang + +namespace io { + +class InputStream : public java::lang::Object +{ +public: + using Object::Object; + + int read() const; +}; + +class ByteArrayInputStream : public InputStream +{ +public: + using InputStream::InputStream; + + explicit ByteArrayInputStream(const QByteArray &bytes); +}; + +class FilterInputStream : public InputStream +{ +public: + using InputStream::InputStream; +}; + +class OutputStream : public java::lang::Object +{ +public: + using Object::Object; + + bool write(const QByteArray &bytes) const; + bool flush() const; + bool close() const; +}; + +class ByteArrayOutputStream : public OutputStream +{ +public: + using OutputStream::OutputStream; + + ByteArrayOutputStream(); + + QByteArray toByteArray() const; +}; + +class FilterOutputStream : public OutputStream +{ +public: + using OutputStream::OutputStream; +}; + +} // namespace io + +namespace math { + +class BigInteger : public java::lang::Object +{ +public: + using Object::Object; + + static const BigInteger ZERO; + static const BigInteger ONE; + static const BigInteger TEN; +}; + +} // namespace math + +namespace util { + +class Date : public java::lang::Object +{ +public: + using Object::Object; +}; + +class Calendar : public java::lang::Object +{ +public: + using Object::Object; + + static const int YEAR; + static const int MONTH; + static const int DAY; + static const int HOUR; + static const int MINUTE; + static const int SECOND; + static const int MILLISECOND; + + static Calendar getInstance(); + + bool add(int field, int amount) const; + Date getTime() const; +}; + +} // namespace util + +namespace security { +namespace spec { + +class AlgorithmParameterSpec : public java::lang::Object +{ +public: + using Object::Object; +}; + +} // namespace spec + +class Key : public java::lang::Object +{ +public: + using Object::Object; +}; + +class PrivateKey : public Key +{ +public: + using Key::Key; + + PrivateKey(const Key &init): Key(init) {} +}; + +class PublicKey : public Key +{ +public: + using Key::Key; + + PublicKey(const Key &init): Key(init) {} +}; + +class KeyPair : public java::lang::Object +{ +public: + using Object::Object; +}; + +class KeyPairGenerator : public java::lang::Object +{ +public: + using Object::Object; + + static KeyPairGenerator getInstance(const QString &algorithm, const QString &provider); + KeyPair generateKeyPair() const; + bool initialize(const spec::AlgorithmParameterSpec &spec) const; + +}; + +class KeyStore : public java::lang::Object +{ +public: + class Entry : public java::lang::Object + { + public: + using Object::Object; + }; + + class PrivateKeyEntry : public Entry + { + public: + using Entry::Entry; + + inline PrivateKeyEntry(const Entry &init): Entry(init) {} + + javax::security::cert::Certificate getCertificate() const; + java::security::PrivateKey getPrivateKey() const; + }; + + class LoadStoreParameter : public java::lang::Object + { + public: + using Object::Object; + }; + + class ProtectionParameter : public java::lang::Object + { + public: + using Object::Object; + }; + + using Object::Object; + + bool containsAlias(const QString &alias) const; + bool deleteEntry(const QString &alias) const; + static KeyStore getInstance(const QString &type); + Entry getEntry(const QString &alias, const ProtectionParameter ¶m = nullptr) const; + bool load(const LoadStoreParameter ¶m = nullptr) const; +}; + +namespace interfaces { + +class RSAPrivateKey : public PrivateKey +{ +public: + using PrivateKey::PrivateKey; + + RSAPrivateKey(const PrivateKey &init): PrivateKey(init) {} +}; + +class RSAPublicKey : public PublicKey +{ +public: + using PublicKey::PublicKey; + + RSAPublicKey(const PublicKey &init): PublicKey(init) {} +}; + +} // namespace interfaces + +} // namespace security +} // namespace java + +namespace android { +namespace content { + +class Context : public java::lang::Object +{ +public: + using Object::Object; +}; + +} // namespace content + +namespace security { + +class KeyPairGeneratorSpec : public java::security::spec::AlgorithmParameterSpec +{ +public: + class Builder : public java::lang::Object + { + public: + using Object::Object; + + explicit Builder(const android::content::Context &context); + + Builder setAlias(const QString &alias) const; + Builder setSubject(const javax::security::auth::x500::X500Principal &subject) const; + Builder setSerialNumber(const java::math::BigInteger &serial) const; + Builder setStartDate(const java::util::Date &date) const; + Builder setEndDate(const java::util::Date &date) const; + KeyPairGeneratorSpec build() const; + + }; + + using AlgorithmParameterSpec::AlgorithmParameterSpec; +}; + +} // namespace security +} // namespace android + +namespace javax { +namespace crypto { + +class Cipher : public java::lang::Object +{ +public: + static const int DECRYPT_MODE; + static const int ENCRYPT_MODE; + + using Object::Object; + + static Cipher getInstance(const QString &transformation); + bool init(int opMode, const java::security::Key &key) const; +}; + +class CipherInputStream : public java::io::FilterInputStream +{ +public: + using FilterInputStream::FilterInputStream; + + explicit CipherInputStream(const InputStream &stream, const Cipher &cipher); +}; + +class CipherOutputStream : public java::io::FilterOutputStream +{ +public: + using FilterOutputStream::FilterOutputStream; + + explicit CipherOutputStream(const OutputStream &stream, const Cipher &cipher); +}; + +} + +namespace security { +namespace auth { +namespace x500 { + +class X500Principal; + +class X500Principal : public java::lang::Object +{ +public: + using Object::Object; + + explicit X500Principal(const QString &name); +}; + +} // namespace x500 +} // namespace auth + +namespace cert { + +class Certificate : public java::lang::Object +{ +public: + using Object::Object; + + java::security::PublicKey getPublicKey() const; +}; + +} // namespace cert + +} // namespace security +} // namespace javax + +} // namespace QKeychain + +#endif // QTKEYCHAIN_ANDROIDKEYSTORE_P_H diff --git a/client/qtkeychain/cmake/Modules/ECMGeneratePriFile.cmake b/client/qtkeychain/cmake/Modules/ECMGeneratePriFile.cmake index 8e00c10..8910cd9 100644 --- a/client/qtkeychain/cmake/Modules/ECMGeneratePriFile.cmake +++ b/client/qtkeychain/cmake/Modules/ECMGeneratePriFile.cmake @@ -1,125 +1,124 @@ -#.rst: -# ECMGeneratePriFile -# ------------------ +# SPDX-FileCopyrightText: 2014 David Faure # -# Generate a ``.pri`` file for the benefit of qmake-based projects. -# -# As well as the function below, this module creates the cache variable -# ``ECM_MKSPECS_INSTALL_DIR`` and sets the default value to ``mkspecs/modules``. -# This assumes Qt and the current project are both installed to the same -# non-system prefix. Packagers who use ``-DCMAKE_INSTALL_PREFIX=/usr`` will -# certainly want to set ``ECM_MKSPECS_INSTALL_DIR`` to something like -# ``share/qt5/mkspecs/modules``. -# -# The main thing is that this should be the ``modules`` subdirectory of either -# the default qmake ``mkspecs`` directory or of a directory that will be in the -# ``$QMAKEPATH`` environment variable when qmake is run. -# -# :: -# -# ecm_generate_pri_file(BASE_NAME -# LIB_NAME -# [DEPS " [ [...]]"] -# [FILENAME_VAR ] -# [INCLUDE_INSTALL_DIR ] -# [LIB_INSTALL_DIR ]) -# -# If your CMake project produces a Qt-based library, you may expect there to be -# applications that wish to use it that use a qmake-based build system, rather -# than a CMake-based one. Creating a ``.pri`` file will make use of your -# library convenient for them, in much the same way that CMake config files make -# things convenient for CMake-based applications. -# -# ecm_generate_pri_file() generates just such a file. It requires the -# ``PROJECT_VERSION_STRING`` variable to be set. This is typically set by -# :module:`ECMSetupVersion`, although the project() command in CMake 3.0.0 and -# later can also set this. -# -# BASE_NAME specifies the name qmake project (.pro) files should use to refer to -# the library (eg: KArchive). LIB_NAME is the name of the actual library to -# link to (ie: the first argument to add_library()). DEPS is a space-separated -# list of the base names of other libraries (for Qt libraries, use the same -# names you use with the ``QT`` variable in a qmake project file, such as "core" -# for QtCore). FILENAME_VAR specifies the name of a variable to store the path -# to the generated file in. -# -# INCLUDE_INSTALL_DIR is the path (relative to ``CMAKE_INSTALL_PREFIX``) that -# include files will be installed to. It defaults to -# ``${INCLUDE_INSTALL_DIR}/`` if the ``INCLUDE_INSTALL_DIR`` variable -# is set. If that variable is not set, the ``CMAKE_INSTALL_INCLUDEDIR`` variable -# is used instead, and if neither are set ``include`` is used. LIB_INSTALL_DIR -# operates similarly for the installation location for libraries; it defaults to -# ``${LIB_INSTALL_DIR}``, ``${CMAKE_INSTALL_LIBDIR}`` or ``lib``, in that order. -# -# Example usage: -# -# .. code-block:: cmake -# -# ecm_generate_pri_file( -# BASE_NAME KArchive -# LIB_NAME KF5KArchive -# DEPS "core" -# FILENAME_VAR pri_filename -# ) -# install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) -# -# A qmake-based project that wished to use this would then do:: -# -# QT += KArchive -# -# in their ``.pro`` file. -# -# Since pre-1.0.0. +# SPDX-License-Identifier: BSD-3-Clause -#============================================================================= -# Copyright 2014 David Faure -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#[=======================================================================[.rst: +ECMGeneratePriFile +------------------ + +Generate a ``.pri`` file for the benefit of qmake-based projects. + +As well as the function below, this module creates the cache variable +``ECM_MKSPECS_INSTALL_DIR`` and sets the default value to ``mkspecs/modules``. +This assumes Qt and the current project are both installed to the same +non-system prefix. Packagers who use ``-DCMAKE_INSTALL_PREFIX=/usr`` will +certainly want to set ``ECM_MKSPECS_INSTALL_DIR`` to something like +``share/qt5/mkspecs/modules``. + +The main thing is that this should be the ``modules`` subdirectory of either +the default qmake ``mkspecs`` directory or of a directory that will be in the +``$QMAKEPATH`` environment variable when qmake is run. + +:: + + ecm_generate_pri_file(BASE_NAME + LIB_NAME + [VERSION ] # since 5.83 + [DEPS " [ [...]]"] + [FILENAME_VAR ] + [INCLUDE_INSTALL_DIRS [ [...]]] # since 5.92 + [INCLUDE_INSTALL_DIR ] # deprecated since 5.92 + [LIB_INSTALL_DIR ]) + +If your CMake project produces a Qt-based library, you may expect there to be +applications that wish to use it that use a qmake-based build system, rather +than a CMake-based one. Creating a ``.pri`` file will make use of your +library convenient for them, in much the same way that CMake config files make +things convenient for CMake-based applications. ``ecm_generate_pri_file()`` +generates just such a file. + +``VERSION`` specifies the version of the library the ``.pri`` file describes. If +not set, the value is taken from the context variable ``PROJECT_VERSION``. +This variable is usually set by the ``project(... VERSION ...)`` command or, +if CMake policy CMP0048 is not ``NEW``, by :module:`ECMSetupVersion`. +For backward-compatibility with older ECM versions the +``PROJECT_VERSION_STRING`` variable as set by :module:`ECMSetupVersion` +will be preferred over ``PROJECT_VERSION`` if set, unless the minimum +required version of ECM is 5.83 and newer. Since 5.83. + +``BASE_NAME`` specifies the name qmake project (.pro) files should use to refer to +the library (eg: KArchive). ``LIB_NAME`` is the name of the actual library to +link to (ie: the first argument to add_library()). ``DEPS`` is a space-separated +list of the base names of other libraries (for Qt libraries, use the same +names you use with the ``QT`` variable in a qmake project file, such as "core" +for QtCore). ``FILENAME_VAR`` specifies the name of a variable to store the path +to the generated file in. + +``INCLUDE_INSTALL_DIRS`` are the paths (relative to ``CMAKE_INSTALL_PREFIX``) that +include files will be installed to. It defaults to +``${INCLUDE_INSTALL_DIR}/`` if the ``INCLUDE_INSTALL_DIR`` variable +is set. If that variable is not set, the ``CMAKE_INSTALL_INCLUDEDIR`` variable +is used instead, and if neither are set ``include`` is used. ``LIB_INSTALL_DIR`` +operates similarly for the installation location for libraries; it defaults to +``${LIB_INSTALL_DIR}``, ``${CMAKE_INSTALL_LIBDIR}`` or ``lib``, in that order. + +``INCLUDE_INSTALL_DIR`` is the old variant of ``INCLUDE_INSTALL_DIRS``, taking only one +directory. + +Example usage: + +.. code-block:: cmake + + ecm_generate_pri_file( + BASE_NAME KArchive + LIB_NAME KF5KArchive + DEPS "core" + FILENAME_VAR pri_filename + VERSION 4.2.0 + ) + install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) + +A qmake-based project that wished to use this would then do:: + + QT += KArchive + +in their ``.pro`` file. + +Since pre-1.0.0. +#]=======================================================================] # Replicate the logic from KDEInstallDirs.cmake as we can't depend on it # Ask qmake if we're using the same prefix as Qt -set(_askqmake OFF) +set(_should_query_qt OFF) if(NOT DEFINED KDE_INSTALL_USE_QT_SYS_PATHS) - include(ECMQueryQmake) - query_qmake(qt_install_prefix_dir QT_INSTALL_PREFIX) + include(ECMQueryQt) + ecm_query_qt(qt_install_prefix_dir QT_INSTALL_PREFIX TRY) if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}") - set(_askqmake ON) + set(_should_query_qt ON) endif() endif() -if(KDE_INSTALL_USE_QT_SYS_PATHS OR _askqmake) - include(ECMQueryQmake) - query_qmake(qt_host_data_dir QT_HOST_DATA) - set(ECM_MKSPECS_INSTALL_DIR ${qt_host_data_dir}/mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.") +if(KDE_INSTALL_USE_QT_SYS_PATHS OR _should_query_qt) + include(ECMQueryQt) + ecm_query_qt(qt_install_prefix_dir QT_INSTALL_PREFIX) + ecm_query_qt(qt_host_data_dir QT_HOST_DATA) + if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}") + file(RELATIVE_PATH qt_host_data_dir ${qt_install_prefix_dir} ${qt_host_data_dir}) + endif() + if(qt_host_data_dir STREQUAL "") + set(mkspecs_install_dir mkspecs/modules) + else() + set(mkspecs_install_dir ${qt_host_data_dir}/mkspecs/modules) + endif() + set(ECM_MKSPECS_INSTALL_DIR ${mkspecs_install_dir} CACHE PATH "The directory where mkspecs will be installed to.") else() set(ECM_MKSPECS_INSTALL_DIR mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.") endif() function(ECM_GENERATE_PRI_FILE) set(options ) - set(oneValueArgs BASE_NAME LIB_NAME DEPS FILENAME_VAR INCLUDE_INSTALL_DIR LIB_INSTALL_DIR) - set(multiValueArgs ) + set(oneValueArgs BASE_NAME LIB_NAME DEPS FILENAME_VAR INCLUDE_INSTALL_DIR LIB_INSTALL_DIR VERSION) + set(multiValueArgs INCLUDE_INSTALL_DIRS) cmake_parse_arguments(EGPF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -127,22 +126,42 @@ function(ECM_GENERATE_PRI_FILE) message(FATAL_ERROR "Unknown keywords given to ECM_GENERATE_PRI_FILE(): \"${EGPF_UNPARSED_ARGUMENTS}\"") endif() + if(ECM_GLOBAL_FIND_VERSION VERSION_LESS 5.83.0) + set(_support_backward_compat_version_string_var TRUE) + else() + set(_support_backward_compat_version_string_var FALSE) + endif() + if(NOT EGPF_BASE_NAME) message(FATAL_ERROR "Required argument BASE_NAME missing in ECM_GENERATE_PRI_FILE() call") endif() if(NOT EGPF_LIB_NAME) message(FATAL_ERROR "Required argument LIB_NAME missing in ECM_GENERATE_PRI_FILE() call") endif() - if(NOT PROJECT_VERSION_STRING) - message(FATAL_ERROR "Required variable PROJECT_VERSION_STRING not set before ECM_GENERATE_PRI_FILE() call. Did you call ecm_setup_version?") + if(NOT EGPF_VERSION) + if(_support_backward_compat_version_string_var) + if(NOT PROJECT_VERSION_STRING AND NOT PROJECT_VERSION) + message(FATAL_ERROR "Required variable PROJECT_VERSION_STRING or PROJECT_VERSION not set before ECM_GENERATE_PRI_FILE() call. Missing call of ecm_setup_version() or project(VERSION)?") + endif() + else() + if(NOT PROJECT_VERSION) + message(FATAL_ERROR "Required variable PROJECT_VERSION not set before ECM_GENERATE_PRI_FILE() call. Missing call of ecm_setup_version() or project(VERSION)?") + endif() + endif() endif() - if(NOT EGPF_INCLUDE_INSTALL_DIR) + if(EGPF_INCLUDE_INSTALL_DIR) + if(EGPF_INCLUDE_INSTALL_DIRS) + message(FATAL_ERROR "Only one argument of INCLUDE_INSTALL_DIR & INCLUDE_INSTALL_DIRS can be used in ECM_GENERATE_PRI_FILE() call") + endif() + set(EGPF_INCLUDE_INSTALL_DIRS ${EGPF_INCLUDE_INSTALL_DIR}) + endif() + if(NOT EGPF_INCLUDE_INSTALL_DIRS) if(INCLUDE_INSTALL_DIR) - set(EGPF_INCLUDE_INSTALL_DIR "${INCLUDE_INSTALL_DIR}/${EGPF_BASE_NAME}") + set(EGPF_INCLUDE_INSTALL_DIRS "${INCLUDE_INSTALL_DIR}/${EGPF_BASE_NAME}") elseif(CMAKE_INSTALL_INCLUDEDIR) - set(EGPF_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}/${EGPF_BASE_NAME}") + set(EGPF_INCLUDE_INSTALL_DIRS "${CMAKE_INSTALL_INCLUDEDIR}/${EGPF_BASE_NAME}") else() - set(EGPF_INCLUDE_INSTALL_DIR "include/${EGPF_BASE_NAME}") + set(EGPF_INCLUDE_INSTALL_DIRS "include/${EGPF_BASE_NAME}") endif() endif() if(NOT EGPF_LIB_INSTALL_DIR) @@ -155,22 +174,48 @@ function(ECM_GENERATE_PRI_FILE) endif() endif() - string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" PROJECT_VERSION_MAJOR "${PROJECT_VERSION_STRING}") - string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" PROJECT_VERSION_MINOR "${PROJECT_VERSION_STRING}") - string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PROJECT_VERSION_PATCH "${PROJECT_VERSION_STRING}") + if(EGPF_VERSION) + set(PRI_VERSION "${EGPF_VERSION}") + else() + if(_support_backward_compat_version_string_var AND PROJECT_VERSION_STRING) + set(PRI_VERSION "${PROJECT_VERSION_STRING}") + if(NOT PROJECT_VERSION_STRING STREQUAL PROJECT_VERSION) + message(DEPRECATION "ECM_GENERATE_PRI_FILE() will no longer support PROJECT_VERSION_STRING when the required minimum version of ECM is 5.83 or newer. Set VERSION parameter or use PROJECT_VERSION instead.") + endif() + else() + set(PRI_VERSION "${PROJECT_VERSION}") + endif() + endif() + + string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" PRI_VERSION_MAJOR "${PRI_VERSION}") + string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" PRI_VERSION_MINOR "${PRI_VERSION}") + string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PRI_VERSION_PATCH "${PRI_VERSION}") + + # Prepare the right number of "../.." to go from ECM_MKSPECS_INSTALL_DIR to the install prefix + # This allows to make the generated pri files relocatable (no absolute paths) + if (IS_ABSOLUTE ${ECM_MKSPECS_INSTALL_DIR}) + set(BASEPATH ${CMAKE_INSTALL_PREFIX}) + else() + string(REGEX REPLACE "[^/]+" ".." PRI_ROOT_RELATIVE_TO_MKSPECS ${ECM_MKSPECS_INSTALL_DIR}) + set(BASEPATH "$$PWD/${PRI_ROOT_RELATIVE_TO_MKSPECS}") + endif() set(PRI_TARGET_BASENAME ${EGPF_BASE_NAME}) set(PRI_TARGET_LIBNAME ${EGPF_LIB_NAME}) set(PRI_TARGET_QTDEPS ${EGPF_DEPS}) - if(IS_ABSOLUTE "${EGPF_INCLUDE_INSTALL_DIR}") - set(PRI_TARGET_INCLUDES "${EGPF_INCLUDE_INSTALL_DIR}") - else() - set(PRI_TARGET_INCLUDES "${CMAKE_INSTALL_PREFIX}/${EGPF_INCLUDE_INSTALL_DIR}") - endif() + set(PRI_TARGET_INCLUDES) + foreach(_dir ${EGPF_INCLUDE_INSTALL_DIRS}) + # separate list entries with space + if(IS_ABSOLUTE "${_dir}") + string(APPEND PRI_TARGET_INCLUDES " ${_dir}") + else() + string(APPEND PRI_TARGET_INCLUDES " ${BASEPATH}/${_dir}") + endif() + endforeach() if(IS_ABSOLUTE "${EGPF_LIB_INSTALL_DIR}") set(PRI_TARGET_LIBS "${EGPF_LIB_INSTALL_DIR}") else() - set(PRI_TARGET_LIBS "${CMAKE_INSTALL_PREFIX}/${EGPF_LIB_INSTALL_DIR}") + set(PRI_TARGET_LIBS "${BASEPATH}/${EGPF_LIB_INSTALL_DIR}") endif() set(PRI_TARGET_DEFINES "") @@ -179,13 +224,25 @@ function(ECM_GENERATE_PRI_FILE) set(${EGPF_FILENAME_VAR} ${PRI_FILENAME} PARENT_SCOPE) endif() + set(PRI_TARGET_MODULE_CONFIG "") + # backward compat: it was not obvious LIB_NAME needs to be a target name, + # and some projects where the target name was not the actual library output name + # passed the output name for LIB_NAME, so .name & .module prperties are correctly set. + # TODO: improve API dox, allow control over module name if target name != output name + if(TARGET ${EGPF_LIB_NAME}) + get_target_property(target_type ${EGPF_LIB_NAME} TYPE) + if (target_type STREQUAL "STATIC_LIBRARY") + set(PRI_TARGET_MODULE_CONFIG "staticlib") + endif() + endif() + file(GENERATE OUTPUT ${PRI_FILENAME} CONTENT - "QT.${PRI_TARGET_BASENAME}.VERSION = ${PROJECT_VERSION_STRING} -QT.${PRI_TARGET_BASENAME}.MAJOR_VERSION = ${PROJECT_VERSION_MAJOR} -QT.${PRI_TARGET_BASENAME}.MINOR_VERSION = ${PROJECT_VERSION_MINOR} -QT.${PRI_TARGET_BASENAME}.PATCH_VERSION = ${PROJECT_VERSION_PATCH} + "QT.${PRI_TARGET_BASENAME}.VERSION = ${PRI_VERSION} +QT.${PRI_TARGET_BASENAME}.MAJOR_VERSION = ${PRI_VERSION_MAJOR} +QT.${PRI_TARGET_BASENAME}.MINOR_VERSION = ${PRI_VERSION_MINOR} +QT.${PRI_TARGET_BASENAME}.PATCH_VERSION = ${PRI_VERSION_PATCH} QT.${PRI_TARGET_BASENAME}.name = ${PRI_TARGET_LIBNAME} QT.${PRI_TARGET_BASENAME}.module = ${PRI_TARGET_LIBNAME} QT.${PRI_TARGET_BASENAME}.defines = ${PRI_TARGET_DEFINES} @@ -193,6 +250,7 @@ QT.${PRI_TARGET_BASENAME}.includes = ${PRI_TARGET_INCLUDES} QT.${PRI_TARGET_BASENAME}.private_includes = QT.${PRI_TARGET_BASENAME}.libs = ${PRI_TARGET_LIBS} QT.${PRI_TARGET_BASENAME}.depends = ${PRI_TARGET_QTDEPS} +QT.${PRI_TARGET_BASENAME}.module_config = ${PRI_TARGET_MODULE_CONFIG} " ) endfunction() diff --git a/client/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake b/client/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake index a118fea..8d48772 100644 --- a/client/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake +++ b/client/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake @@ -6,7 +6,7 @@ # # ``write_basic_package_version_file()`` is the same as the one provided by the # `CMakePackageConfigHelpers -# `_ +# `_ # module in CMake; see that module's documentation for # more information. # @@ -23,7 +23,7 @@ # 2.8.12, except that it adds an extra helper macro: find_dependency(). It is # highly recommended that you read the `documentation for # CMakePackageConfigHelpers -# `_ +# `_ # for more information, particularly with regard to the PATH_VARS argument. # # Note that there is no argument that will disable the find_dependency() macro; @@ -50,31 +50,10 @@ # Since pre-1.0.0. #============================================================================= -# Copyright 2014 Alex Merry -# Copyright 2013 Stephen Kelly +# SPDX-FileCopyrightText: 2014 Alex Merry +# SPDX-FileCopyrightText: 2013 Stephen Kelly # -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# SPDX-License-Identifier: BSD-3-Clause include(${CMAKE_ROOT}/Modules/CMakePackageConfigHelpers.cmake) diff --git a/client/qtkeychain/cmake/Modules/ECMQueryQmake.cmake b/client/qtkeychain/cmake/Modules/ECMQueryQmake.cmake deleted file mode 100644 index fa0949d..0000000 --- a/client/qtkeychain/cmake/Modules/ECMQueryQmake.cmake +++ /dev/null @@ -1,32 +0,0 @@ -find_package(Qt5Core QUIET) - -if (Qt5Core_FOUND) - set(_qmake_executable_default "qmake-qt5") -endif () -if (TARGET Qt5::qmake) - get_target_property(_qmake_executable_default Qt5::qmake LOCATION) -endif() -set(QMAKE_EXECUTABLE ${_qmake_executable_default} - CACHE FILEPATH "Location of the Qt5 qmake executable") - -# This is not public API (yet)! -function(query_qmake result_variable qt_variable) - if(NOT QMAKE_EXECUTABLE) - set(${result_variable} "" PARENT_SCOPE) - message(WARNING "Should specify a qmake Qt5 binary. Can't check ${qt_variable}") - return() - endif() - execute_process( - COMMAND ${QMAKE_EXECUTABLE} -query "${qt_variable}" - RESULT_VARIABLE return_code - OUTPUT_VARIABLE output - ) - if(return_code EQUAL 0) - string(STRIP "${output}" output) - file(TO_CMAKE_PATH "${output}" output_path) - set(${result_variable} "${output_path}" PARENT_SCOPE) - else() - message(WARNING "Failed call: ${QMAKE_EXECUTABLE} -query \"${qt_variable}\"") - message(FATAL_ERROR "QMake call failed: ${return_code}") - endif() -endfunction() diff --git a/client/qtkeychain/cmake/Modules/ECMQueryQt.cmake b/client/qtkeychain/cmake/Modules/ECMQueryQt.cmake new file mode 100644 index 0000000..98eb500 --- /dev/null +++ b/client/qtkeychain/cmake/Modules/ECMQueryQt.cmake @@ -0,0 +1,100 @@ +# SPDX-FileCopyrightText: 2014 Rohan Garg +# SPDX-FileCopyrightText: 2014 Alex Merry +# SPDX-FileCopyrightText: 2014-2016 Aleix Pol +# SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau +# SPDX-FileCopyrightText: 2022 Ahmad Samir +# +# SPDX-License-Identifier: BSD-3-Clause +#[=======================================================================[.rst: +ECMQueryQt +--------------- +This module can be used to query the installation paths used by Qt. + +For Qt5 this uses ``qmake``, and for Qt6 this used ``qtpaths`` (the latter has built-in +support to query the paths of a target platform when cross-compiling). + +This module defines the following function: +:: + + ecm_query_qt( [TRY]) + +Passing ``TRY`` will result in the method not making the build fail if the executable +used for querying has not been found, but instead simply print a warning message and +return an empty string. + +Example usage: + +.. code-block:: cmake + + include(ECMQueryQt) + ecm_query_qt(bin_dir QT_INSTALL_BINS) + +If the call succeeds ``${bin_dir}`` will be set to ``/path/to/bin/dir`` (e.g. +``/usr/lib64/qt/bin/``). + +Since: 5.93 +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake) +include(CheckLanguage) +check_language(CXX) +if (CMAKE_CXX_COMPILER) + # Enable the CXX language to let CMake look for config files in library dirs. + # See: https://gitlab.kitware.com/cmake/cmake/-/issues/23266 + enable_language(CXX) +endif() + +if (QT_MAJOR_VERSION STREQUAL "5") + # QUIET to accommodate the TRY option + find_package(Qt${QT_MAJOR_VERSION}Core QUIET) + if(TARGET Qt5::qmake) + get_target_property(_qmake_executable_default Qt5::qmake LOCATION) + + set(QUERY_EXECUTABLE ${_qmake_executable_default} + CACHE FILEPATH "Location of the Qt5 qmake executable") + set(_exec_name_text "Qt5 qmake") + set(_cli_option "-query") + endif() +elseif(QT_MAJOR_VERSION STREQUAL "6") + # QUIET to accommodate the TRY option + find_package(Qt6 COMPONENTS CoreTools QUIET CONFIG) + if (TARGET Qt6::qtpaths) + get_target_property(_qtpaths_executable Qt6::qtpaths LOCATION) + + set(QUERY_EXECUTABLE ${_qtpaths_executable} + CACHE FILEPATH "Location of the Qt6 qtpaths executable") + set(_exec_name_text "Qt6 qtpaths") + set(_cli_option "--query") + endif() +endif() + +function(ecm_query_qt result_variable qt_variable) + set(options TRY) + set(oneValueArgs) + set(multiValueArgs) + + cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT QUERY_EXECUTABLE) + if(ARGS_TRY) + set(${result_variable} "" PARENT_SCOPE) + message(STATUS "No ${_exec_name_text} executable found. Can't check ${qt_variable}") + return() + else() + message(FATAL_ERROR "No ${_exec_name_text} executable found. Can't check ${qt_variable} as required") + endif() + endif() + execute_process( + COMMAND ${QUERY_EXECUTABLE} ${_cli_option} "${qt_variable}" + RESULT_VARIABLE return_code + OUTPUT_VARIABLE output + ) + if(return_code EQUAL 0) + string(STRIP "${output}" output) + file(TO_CMAKE_PATH "${output}" output_path) + set(${result_variable} "${output_path}" PARENT_SCOPE) + else() + message(WARNING "Failed call: ${_command} \"${qt_variable}\"") + message(FATAL_ERROR "${_exec_name_text} call failed: ${return_code}") + endif() +endfunction() diff --git a/client/qtkeychain/cmake/Modules/ECMSetupVersion.cmake b/client/qtkeychain/cmake/Modules/ECMSetupVersion.cmake index f5a3cce..65c1688 100644 --- a/client/qtkeychain/cmake/Modules/ECMSetupVersion.cmake +++ b/client/qtkeychain/cmake/Modules/ECMSetupVersion.cmake @@ -28,7 +28,7 @@ # _SOVERSION - , or if SOVERSION was not given # # If CMake policy CMP0048 is not NEW, the following CMake variables will also -# be set: +# be set:: # # PROJECT_VERSION_MAJOR - # PROJECT_VERSION_MINOR - @@ -75,34 +75,13 @@ # # Since pre-1.0.0. # -# COMPATIBLITY option available since 1.6.0. +# COMPATIBILITY option available since 1.6.0. #============================================================================= -# Copyright 2014 Alex Merry -# Copyright 2012 Alexander Neundorf +# SPDX-FileCopyrightText: 2014 Alex Merry +# SPDX-FileCopyrightText: 2012 Alexander Neundorf # -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# SPDX-License-Identifier: BSD-3-Clause include(CMakePackageConfigHelpers) @@ -154,9 +133,9 @@ function(ecm_setup_version _version) set(_minor "${PROJECT_VERSION_MINOR}") set(_patch "${PROJECT_VERSION_PATCH}") else() - string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" _major "${_version}") - string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" _minor "${_version}") - string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" _patch "${_version}") + string(REGEX REPLACE "^0*([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" _major "${_version}") + string(REGEX REPLACE "^[0-9]+\\.0*([0-9]+)\\.[0-9]+.*" "\\1" _minor "${_version}") + string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.0*([0-9]+).*" "\\1" _patch "${_version}") endif() if(NOT ESV_SOVERSION) diff --git a/client/qtkeychain/cmake/Modules/GNUInstallDirs.cmake b/client/qtkeychain/cmake/Modules/GNUInstallDirs.cmake deleted file mode 100644 index 0302e4b..0000000 --- a/client/qtkeychain/cmake/Modules/GNUInstallDirs.cmake +++ /dev/null @@ -1,188 +0,0 @@ -# - Define GNU standard installation directories -# Provides install directory variables as defined for GNU software: -# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html -# Inclusion of this module defines the following variables: -# CMAKE_INSTALL_ - destination for files of a given type -# CMAKE_INSTALL_FULL_ - corresponding absolute path -# where is one of: -# BINDIR - user executables (bin) -# SBINDIR - system admin executables (sbin) -# LIBEXECDIR - program executables (libexec) -# SYSCONFDIR - read-only single-machine data (etc) -# SHAREDSTATEDIR - modifiable architecture-independent data (com) -# LOCALSTATEDIR - modifiable single-machine data (var) -# LIBDIR - object code libraries (lib or lib64 or lib/ on Debian) -# INCLUDEDIR - C header files (include) -# OLDINCLUDEDIR - C header files for non-gcc (/usr/include) -# DATAROOTDIR - read-only architecture-independent data root (share) -# DATADIR - read-only architecture-independent data (DATAROOTDIR) -# INFODIR - info documentation (DATAROOTDIR/info) -# LOCALEDIR - locale-dependent data (DATAROOTDIR/locale) -# MANDIR - man documentation (DATAROOTDIR/man) -# DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME) -# Each CMAKE_INSTALL_ value may be passed to the DESTINATION options of -# install() commands for the corresponding file type. If the includer does -# not define a value the above-shown default will be used and the value will -# appear in the cache for editing by the user. -# Each CMAKE_INSTALL_FULL_ value contains an absolute path constructed -# from the corresponding destination by prepending (if necessary) the value -# of CMAKE_INSTALL_PREFIX. - -#============================================================================= -# Copyright 2011 Nikita Krupen'ko -# Copyright 2011 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -# Installation directories -# -if(NOT DEFINED CMAKE_INSTALL_BINDIR) - set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_SBINDIR) - set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR) - set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) - set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR) - set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR) - set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_LIBDIR) - set(_LIBDIR_DEFAULT "lib") - # Override this default 'lib' with 'lib64' iff: - # - we are on Linux system but NOT cross-compiling - # - we are NOT on debian - # - we are on a 64 bits system - # reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf - # For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if - # CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu" - # See http://wiki.debian.org/Multiarch - if(CMAKE_SYSTEM_NAME MATCHES "Linux" - AND NOT CMAKE_CROSSCOMPILING) - if (EXISTS "/etc/debian_version") # is this a debian system ? - if(CMAKE_LIBRARY_ARCHITECTURE) - set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}") - endif() - else() # not debian, rely on CMAKE_SIZEOF_VOID_P: - if(NOT DEFINED CMAKE_SIZEOF_VOID_P) - message(AUTHOR_WARNING - "Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. " - "Please enable at least one language before including GNUInstallDirs.") - else() - if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(_LIBDIR_DEFAULT "lib64") - endif() - endif() - endif() - endif() - set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})") -endif() - -if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) - set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR) - set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)") -endif() - -if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR) - set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)") -endif() - -#----------------------------------------------------------------------------- -# Values whose defaults are relative to DATAROOTDIR. Store empty values in -# the cache and store the defaults in local variables if the cache values are -# not set explicitly. This auto-updates the defaults as DATAROOTDIR changes. - -if(NOT CMAKE_INSTALL_DATADIR) - set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)") - set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}") -endif() - -if(NOT CMAKE_INSTALL_INFODIR) - set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)") - set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info") -endif() - -if(NOT CMAKE_INSTALL_LOCALEDIR) - set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)") - set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale") -endif() - -if(NOT CMAKE_INSTALL_MANDIR) - set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)") - set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man") -endif() - -if(NOT CMAKE_INSTALL_DOCDIR) - set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)") - set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}") -endif() - -#----------------------------------------------------------------------------- - -mark_as_advanced( - CMAKE_INSTALL_BINDIR - CMAKE_INSTALL_SBINDIR - CMAKE_INSTALL_LIBEXECDIR - CMAKE_INSTALL_SYSCONFDIR - CMAKE_INSTALL_SHAREDSTATEDIR - CMAKE_INSTALL_LOCALSTATEDIR - CMAKE_INSTALL_LIBDIR - CMAKE_INSTALL_INCLUDEDIR - CMAKE_INSTALL_OLDINCLUDEDIR - CMAKE_INSTALL_DATAROOTDIR - CMAKE_INSTALL_DATADIR - CMAKE_INSTALL_INFODIR - CMAKE_INSTALL_LOCALEDIR - CMAKE_INSTALL_MANDIR - CMAKE_INSTALL_DOCDIR - ) - -# Result directories -# -foreach(dir - BINDIR - SBINDIR - LIBEXECDIR - SYSCONFDIR - SHAREDSTATEDIR - LOCALSTATEDIR - LIBDIR - INCLUDEDIR - OLDINCLUDEDIR - DATAROOTDIR - DATADIR - INFODIR - LOCALEDIR - MANDIR - DOCDIR - ) - if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}}) - set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}") - else() - set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}") - endif() -endforeach() diff --git a/client/qtkeychain/cmake/Modules/QtVersionOption.cmake b/client/qtkeychain/cmake/Modules/QtVersionOption.cmake new file mode 100644 index 0000000..ea37da2 --- /dev/null +++ b/client/qtkeychain/cmake/Modules/QtVersionOption.cmake @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2021 Volker Krause +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +QtVersionOption +--------------- + +Adds a build option to select the major Qt version if necessary, +that is, if the major Qt version has not yet been determined otherwise +(e.g. by a corresponding ``find_package()`` call). +This module is typically included by other modules requiring knowledge +about the major Qt version. + +``QT_MAJOR_VERSION`` is defined to either be "5" or "6". + +Since 5.82.0. +#]=======================================================================] + +if (DEFINED QT_MAJOR_VERSION) + return() +endif() + +if (TARGET Qt5::Core) + set(QT_MAJOR_VERSION 5) +elseif (TARGET Qt6::Core) + set(QT_MAJOR_VERSION 6) +else() + option(BUILD_WITH_QT6 "Build against Qt 6" OFF) + + if (BUILD_WITH_QT6) + set(QT_MAJOR_VERSION 6) + else() + set(QT_MAJOR_VERSION 5) + endif() +endif() diff --git a/client/qtkeychain/gnomekeyring.cpp b/client/qtkeychain/gnomekeyring.cpp index dd35670..6347052 100644 --- a/client/qtkeychain/gnomekeyring.cpp +++ b/client/qtkeychain/gnomekeyring.cpp @@ -1,6 +1,6 @@ #include "gnomekeyring_p.h" -const char* GnomeKeyring::GNOME_KEYRING_DEFAULT = NULL; +const char* GnomeKeyring::GNOME_KEYRING_DEFAULT = nullptr; bool GnomeKeyring::isAvailable() { diff --git a/client/qtkeychain/keychain.h b/client/qtkeychain/keychain.h index 1d7a5fd..8c006cb 100644 --- a/client/qtkeychain/keychain.h +++ b/client/qtkeychain/keychain.h @@ -47,7 +47,7 @@ class JobPrivate; class QKEYCHAIN_EXPORT Job : public QObject { Q_OBJECT public: - ~Job(); + ~Job() override; /** * @return The QSettings instance used as plaintext storage if insecureFallback() is true. @@ -150,7 +150,7 @@ Q_SIGNALS: void finished( QKeychain::Job* ); protected: - explicit Job( JobPrivate *q, QObject* parent=0 ); + explicit Job( JobPrivate *q, QObject* parent=nullptr ); Q_INVOKABLE void doStart(); private: @@ -185,8 +185,8 @@ public: * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ - explicit ReadPasswordJob( const QString& service, QObject* parent=0 ); - ~ReadPasswordJob(); + explicit ReadPasswordJob( const QString& service, QObject* parent=nullptr ); + ~ReadPasswordJob() override; /** * @return The binary data stored as value of this job's key(). @@ -222,8 +222,8 @@ public: * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ - explicit WritePasswordJob( const QString& service, QObject* parent=0 ); - ~WritePasswordJob(); + explicit WritePasswordJob( const QString& service, QObject* parent=nullptr ); + ~WritePasswordJob() override; /** * Set the @p data that the job will store in the keychain as binary data. @@ -259,13 +259,24 @@ public: * @param service The service string used by this job (can be empty). * @param parent The parent of this job. */ - explicit DeletePasswordJob( const QString& service, QObject* parent=0 ); - ~DeletePasswordJob(); + explicit DeletePasswordJob( const QString& service, QObject* parent=nullptr ); + ~DeletePasswordJob() override; private: friend class QKeychain::DeletePasswordJobPrivate; }; +/** + * Checks whether there is a viable secure backend available. + * This particularly matters on UNIX platforms where multiple different backends + * exist and none might be available. + * + * Note that using the insecure fallback will work even if no secure backend is available. + * + * @since 0.14.0 + */ +QKEYCHAIN_EXPORT bool isAvailable(); + } // namespace QtKeychain #endif diff --git a/client/qtkeychain/keychain_android.cpp b/client/qtkeychain/keychain_android.cpp new file mode 100644 index 0000000..8cea484 --- /dev/null +++ b/client/qtkeychain/keychain_android.cpp @@ -0,0 +1,195 @@ +/****************************************************************************** + * Copyright (C) 2016 Mathias Hasselmann * + * * + * 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. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ + +#include "keychain_p.h" + +#include "androidkeystore_p.h" +#include "plaintextstore_p.h" + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#endif + +using namespace QKeychain; + +using android::content::Context; +using android::security::KeyPairGeneratorSpec; + +using java::io::ByteArrayInputStream; +using java::io::ByteArrayOutputStream; +using java::security::interfaces::RSAPrivateKey; +using java::security::interfaces::RSAPublicKey; +using java::security::KeyPair; +using java::security::KeyPairGenerator; +using java::security::KeyStore; +using java::util::Calendar; + +using javax::crypto::Cipher; +using javax::crypto::CipherInputStream; +using javax::crypto::CipherOutputStream; +using javax::security::auth::x500::X500Principal; + +namespace { + +inline QString makeAlias(const QString &service, const QString &key) +{ + return service + QLatin1Char('/') + key; +} + +} + +void ReadPasswordJobPrivate::scheduledStart() +{ + PlainTextStore plainTextStore(q->service(), q->settings()); + + if (!plainTextStore.contains(q->key())) { + q->emitFinishedWithError(Error::EntryNotFound, tr("Entry not found")); + return; + } + + const QByteArray &encryptedData = plainTextStore.readData(q->key()); + const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); + + if (!keyStore || !keyStore.load()) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore")); + return; + } + + const auto &alias = makeAlias(q->service(), q->key()); + const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias); + + if (!entry) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not retrieve private key from keystore")); + return; + } + + const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); + + if (!cipher || !cipher.init(Cipher::DECRYPT_MODE, entry.getPrivateKey())) { + q->emitFinishedWithError(Error::OtherError, tr("Could not create decryption cipher")); + return; + } + + QByteArray plainData; + const CipherInputStream inputStream(ByteArrayInputStream(encryptedData), cipher); + + for (int nextByte; (nextByte = inputStream.read()) != -1; ) + plainData.append(nextByte); + + mode = plainTextStore.readMode(q->key()); + data = plainData; + q->emitFinished(); +} + +void WritePasswordJobPrivate::scheduledStart() +{ + const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); + + if (!keyStore || !keyStore.load()) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore")); + return; + } + + const auto &alias = makeAlias(q->service(), q->key()); + if (!keyStore.containsAlias(alias)) { + const Calendar start = Calendar::getInstance(); + const Calendar end = Calendar::getInstance(); + end.add(Calendar::YEAR, 99); + + const KeyPairGeneratorSpec spec = + #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + KeyPairGeneratorSpec::Builder(Context(QtAndroid::androidActivity())). + #elif QT_VERSION < QT_VERSION_CHECK(6, 4, 0) + KeyPairGeneratorSpec::Builder(Context(QNativeInterface::QAndroidApplication::context())). + #else + KeyPairGeneratorSpec::Builder(Context((jobject)QNativeInterface::QAndroidApplication::context())). + #endif + setAlias(alias). + setSubject(X500Principal(QStringLiteral("CN=QtKeychain, O=Android Authority"))). + setSerialNumber(java::math::BigInteger::ONE). + setStartDate(start.getTime()). + setEndDate(end.getTime()). + build(); + + const KeyPairGenerator generator = KeyPairGenerator::getInstance(QStringLiteral("RSA"), + QStringLiteral("AndroidKeyStore")); + + if (!generator) { + q->emitFinishedWithError(Error::OtherError, tr("Could not create private key generator")); + return; + } + + generator.initialize(spec); + + if (!generator.generateKeyPair()) { + q->emitFinishedWithError(Error::OtherError, tr("Could not generate new private key")); + return; + } + } + + const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias); + + if (!entry) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not retrieve private key from keystore")); + return; + } + + const RSAPublicKey publicKey = entry.getCertificate().getPublicKey(); + const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding")); + + if (!cipher || !cipher.init(Cipher::ENCRYPT_MODE, publicKey)) { + q->emitFinishedWithError(Error::OtherError, tr("Could not create encryption cipher")); + return; + } + + ByteArrayOutputStream outputStream; + CipherOutputStream cipherOutputStream(outputStream, cipher); + + if (!cipherOutputStream.write(data) || !cipherOutputStream.close()) { + q->emitFinishedWithError(Error::OtherError, tr("Could not encrypt data")); + return; + } + + PlainTextStore plainTextStore(q->service(), q->settings()); + plainTextStore.write(q->key(), outputStream.toByteArray(), mode); + + if (plainTextStore.error() != NoError) + q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); + else + q->emitFinished(); +} + +void DeletePasswordJobPrivate::scheduledStart() +{ + const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore")); + + if (!keyStore || !keyStore.load()) { + q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore")); + return; + } + + const auto &alias = makeAlias(q->service(), q->key()); + if (!keyStore.deleteEntry(alias)) { + q->emitFinishedWithError(Error::OtherError, tr("Could not remove private key from keystore")); + return; + } + + PlainTextStore plainTextStore(q->service(), q->settings()); + plainTextStore.remove(q->key()); + + if (plainTextStore.error() != NoError) + q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString()); + else + q->emitFinished(); +} + +bool QKeychain::isAvailable() +{ + return true; +} diff --git a/client/qtkeychain/keychain_apple.mm b/client/qtkeychain/keychain_apple.mm new file mode 100644 index 0000000..f9a8266 --- /dev/null +++ b/client/qtkeychain/keychain_apple.mm @@ -0,0 +1,263 @@ +/****************************************************************************** + * Copyright (C) 2016 Mathias Hasselmann * + * * + * 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. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ + +#include "keychain_p.h" + +#import +#import + +using namespace QKeychain; + +struct ErrorDescription +{ + QKeychain::Error code; + QString message; + + ErrorDescription(QKeychain::Error code, const QString &message) + : code(code), message(message) {} + + static ErrorDescription fromStatus(OSStatus status) + { + switch(status) { + case errSecSuccess: + return ErrorDescription(QKeychain::NoError, Job::tr("No error")); + case errSecItemNotFound: + return ErrorDescription(QKeychain::EntryNotFound, Job::tr("The specified item could not be found in the keychain")); + case errSecUserCanceled: + return ErrorDescription(QKeychain::AccessDeniedByUser, Job::tr("User canceled the operation")); + case errSecInteractionNotAllowed: + return ErrorDescription(QKeychain::AccessDenied, Job::tr("User interaction is not allowed")); + case errSecNotAvailable: + return ErrorDescription(QKeychain::AccessDenied, Job::tr("No keychain is available. You may need to restart your computer")); + case errSecAuthFailed: + return ErrorDescription(QKeychain::AccessDenied, Job::tr("The user name or passphrase you entered is not correct")); + case errSecVerifyFailed: + return ErrorDescription(QKeychain::AccessDenied, Job::tr("A cryptographic verification failure has occurred")); + case errSecUnimplemented: + return ErrorDescription(QKeychain::NotImplemented, Job::tr("Function or operation not implemented")); + case errSecIO: + return ErrorDescription(QKeychain::OtherError, Job::tr("I/O error")); + case errSecOpWr: + return ErrorDescription(QKeychain::OtherError, Job::tr("Already open with with write permission")); + case errSecParam: + return ErrorDescription(QKeychain::OtherError, Job::tr("Invalid parameters passed to a function")); + case errSecAllocate: + return ErrorDescription(QKeychain::OtherError, Job::tr("Failed to allocate memory")); + case errSecBadReq: + return ErrorDescription(QKeychain::OtherError, Job::tr("Bad parameter or invalid state for operation")); + case errSecInternalComponent: + return ErrorDescription(QKeychain::OtherError, Job::tr("An internal component failed")); + case errSecDuplicateItem: + return ErrorDescription(QKeychain::OtherError, Job::tr("The specified item already exists in the keychain")); + case errSecDecode: + return ErrorDescription(QKeychain::OtherError, Job::tr("Unable to decode the provided data")); + } + + return ErrorDescription(QKeychain::OtherError, Job::tr("Unknown error")); + } +}; + +@interface AppleKeychainInterface : NSObject + +- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob; +- (void)keychainTaskFinished; +- (void)keychainReadTaskFinished:(NSData *)retrievedData; +- (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSString *)descriptiveMessage; + +@end + +@interface AppleKeychainInterface() +{ + QPointer _job; + QPointer _privateJob; +} +@end + +@implementation AppleKeychainInterface + +- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob +{ + self = [super init]; + if (self) { + _job = job; + _privateJob = privateJob; + } + return self; +} + +- (void)dealloc +{ + [NSNotificationCenter.defaultCenter removeObserver:self]; + [super dealloc]; +} + +- (void)keychainTaskFinished +{ + if (_job) { + _job->emitFinished(); + } +} + +- (void)keychainReadTaskFinished:(NSData *)retrievedData +{ + _privateJob->data.clear(); + _privateJob->mode = JobPrivate::Binary; + + if (retrievedData != nil) { + if (_privateJob) { + _privateJob->data = QByteArray::fromNSData(retrievedData); + } + } + + if (_job) { + _job->emitFinished(); + } +} + +- (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSString *)descriptiveMessage +{ + const auto localisedDescriptiveMessage = Job::tr([descriptiveMessage UTF8String]); + + const ErrorDescription error = ErrorDescription::fromStatus(status); + const auto fullMessage = localisedDescriptiveMessage.isEmpty() ? error.message : QStringLiteral("%1: %2").arg(localisedDescriptiveMessage, error.message); + + if (_job) { + _job->emitFinishedWithError(error.code, fullMessage); + } +} + +@end + + +static void StartReadPassword(const QString &service, const QString &key, AppleKeychainInterface * const interface) +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + NSDictionary * const query = @{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: service.toNSString(), + (__bridge NSString *)kSecAttrAccount: key.toNSString(), + (__bridge NSString *)kSecReturnData: @YES, + }; + + CFTypeRef dataRef = nil; + const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef); + + if (status == errSecSuccess) { + const CFDataRef castedDataRef = (CFDataRef)dataRef; + NSData * const data = (__bridge NSData *)castedDataRef; + dispatch_async(dispatch_get_main_queue(), ^{ + [interface keychainReadTaskFinished:data]; + [interface release]; + }); + } else { + NSString * const descriptiveErrorString = @"Could not retrieve private key from keystore"; + dispatch_async(dispatch_get_main_queue(), ^{ + [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; + [interface release]; + }); + } + + if (dataRef) { + CFRelease(dataRef); + } + }); +} + +static void StartWritePassword(const QString &service, const QString &key, const QByteArray &data, AppleKeychainInterface * const interface) +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + NSDictionary * const query = @{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: service.toNSString(), + (__bridge NSString *)kSecAttrAccount: key.toNSString(), + }; + + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, nil); + + if (status == errSecSuccess) { + NSDictionary * const update = @{ + (__bridge NSString *)kSecValueData: data.toNSData(), + }; + + status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update); + } else { + NSDictionary * const insert = @{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: service.toNSString(), + (__bridge NSString *)kSecAttrAccount: key.toNSString(), + (__bridge NSString *)kSecValueData: data.toNSData(), + }; + + status = SecItemAdd((__bridge const CFDictionaryRef)insert, nil); + } + + if (status == errSecSuccess) { + dispatch_async(dispatch_get_main_queue(), ^{ + [interface keychainTaskFinished]; + [interface release]; + }); + } else { + NSString * const descriptiveErrorString = @"Could not store data in settings"; + + dispatch_async(dispatch_get_main_queue(), ^{ + [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; + [interface release]; + }); + } + }); +} + +static void StartDeletePassword(const QString &service, const QString &key, AppleKeychainInterface * const interface) +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + NSDictionary * const query = @{ + (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrService: service.toNSString(), + (__bridge NSString *)kSecAttrAccount: key.toNSString(), + }; + + const OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); + + if (status == errSecSuccess) { + dispatch_async(dispatch_get_main_queue(), ^{ + [interface keychainTaskFinished]; + [interface release]; + }); + } else { + NSString * const descriptiveErrorString = @"Could not remove private key from keystore"; + dispatch_async(dispatch_get_main_queue(), ^{ + [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString]; + [interface release]; + }); + } + }); +} + +void ReadPasswordJobPrivate::scheduledStart() +{ + AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + StartReadPassword(service, key, interface); +} + +void WritePasswordJobPrivate::scheduledStart() +{ + AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + StartWritePassword(service, key, data, interface); +} + +void DeletePasswordJobPrivate::scheduledStart() +{ + AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this]; + StartDeletePassword(service, key, interface); +} + +bool QKeychain::isAvailable() +{ + return true; +} diff --git a/client/qtkeychain/keychain_haiku.cpp b/client/qtkeychain/keychain_haiku.cpp new file mode 100644 index 0000000..16abe1c --- /dev/null +++ b/client/qtkeychain/keychain_haiku.cpp @@ -0,0 +1,192 @@ +/****************************************************************************** + * Copyright (C) 2018 François Revol * + * * + * 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. For licensing and distribution * + * details, check the accompanying file 'COPYING'. * + *****************************************************************************/ +#include "keychain_p.h" + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace QKeychain; + +class AutoApp { +public: + AutoApp(); + ~AutoApp(); + BApplication *app; +}; + + +AutoApp::AutoApp() + : app(nullptr) +{ + if (be_app) + return; + + // no BApplication object, probably using QCoreApplication + // but we need one around + + QString appSignature; + + char signature[B_MIME_TYPE_LENGTH]; + signature[0] = '\0'; + + QString appPath = QCoreApplication::applicationFilePath(); + + BFile appFile(appPath.toUtf8(), B_READ_ONLY); + if (appFile.InitCheck() == B_OK) { + BAppFileInfo info(&appFile); + if (info.InitCheck() == B_OK) { + if (info.GetSignature(signature) != B_OK) + signature[0] = '\0'; + } + } + + if (signature[0] != '\0') + appSignature = QLatin1String(signature); + else + appSignature = QLatin1String("application/x-vnd.qtkeychain-") + + QCoreApplication::applicationName().remove("_x86"); + + app = new BApplication(appSignature.toUtf8().constData()); +} + +AutoApp::~AutoApp() +{ + delete app; +} + +static QString strForStatus( status_t os ) { + const char * const buf = strerror(os) ; + return QObject::tr( "error 0x%1: %2" ) + .arg( os, 8, 16 ).arg( QString::fromUtf8( buf, strlen( buf ) ) ); +} + +void ReadPasswordJobPrivate::scheduledStart() +{ + AutoApp aa; + QString errorString; + Error error = NoError; + BKeyStore keyStore; + BPasswordKey password; + + status_t result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, + q->service().toUtf8().constData(), + q->key().toUtf8().constData(), + false, password); + + data = QByteArray(reinterpret_cast(password.Data()), password.DataLength()); + + switch ( result ) { + case B_OK: + q->emitFinished(); + return; + case B_ENTRY_NOT_FOUND: + errorString = tr("Password not found"); + error = EntryNotFound; + break; + default: + errorString = strForStatus( result ); + error = OtherError; + break; + } + + q->emitFinishedWithError( error, errorString ); +} + +void WritePasswordJobPrivate::scheduledStart() +{ + AutoApp aa; + QString errorString; + Error error = NoError; + BKeyStore keyStore; + BPasswordKey password(data.constData(), + B_KEY_PURPOSE_GENERIC, + q->service().toUtf8().constData(), + q->key().toUtf8().constData()); + status_t result = B_OK; + + // re-add as binary if it's not text + if (mode == Binary) + result = password.SetData(reinterpret_cast(data.constData()), data.size()); + + if (result == B_OK) + result = keyStore.AddKey(password); + + if (result == B_NAME_IN_USE) { + BPasswordKey old_password; + result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, + q->service().toUtf8().constData(), + q->key().toUtf8().constData(), + false, old_password); + if (result == B_OK) + result = keyStore.RemoveKey(old_password); + if (result == B_OK) + result = keyStore.AddKey(password); + } + + switch ( result ) { + case B_OK: + q->emitFinished(); + return; + case B_ENTRY_NOT_FOUND: + errorString = tr("Password not found"); + error = EntryNotFound; + break; + default: + errorString = strForStatus( result ); + error = OtherError; + break; + } + + q->emitFinishedWithError( error, errorString ); +} + +void DeletePasswordJobPrivate::scheduledStart() +{ + AutoApp aa; + QString errorString; + Error error = NoError; + BKeyStore keyStore; + BPasswordKey password; + + status_t result = keyStore.GetKey(B_KEY_TYPE_PASSWORD, + q->service().toUtf8().constData(), + q->key().toUtf8().constData(), + false, password); + + if (result == B_OK) + result = keyStore.RemoveKey(password); + + switch ( result ) { + case B_OK: + q->emitFinished(); + return; + case B_ENTRY_NOT_FOUND: + errorString = tr("Password not found"); + error = EntryNotFound; + break; + default: + errorString = strForStatus( result ); + error = CouldNotDeleteEntry; + break; + } + + q->emitFinishedWithError( error, errorString ); +} + +bool QKeychain::isAvailable() +{ + return true; +} diff --git a/client/qtkeychain/keychain_ios.mm b/client/qtkeychain/keychain_ios.mm deleted file mode 100644 index 4662e40..0000000 --- a/client/qtkeychain/keychain_ios.mm +++ /dev/null @@ -1,146 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2016 Mathias Hasselmann * - * * - * 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. For licensing and distribution * - * details, check the accompanying file 'COPYING'. * - *****************************************************************************/ - -#include "keychain_p.h" - -#import -#import - -using namespace QKeychain; - -struct ErrorDescription -{ - QKeychain::Error code; - QString message; - - ErrorDescription(QKeychain::Error code, const QString &message) - : code(code), message(message) {} - - static ErrorDescription fromStatus(OSStatus status) - { - switch(status) { - case errSecSuccess: - return ErrorDescription(QKeychain::NoError, Job::tr("No error")); - case errSecItemNotFound: - return ErrorDescription(QKeychain::EntryNotFound, Job::tr("The specified item could not be found in the keychain")); - case errSecUserCanceled: - return ErrorDescription(QKeychain::AccessDeniedByUser, Job::tr("User canceled the operation")); - case errSecInteractionNotAllowed: - return ErrorDescription(QKeychain::AccessDenied, Job::tr("User interaction is not allowed")); - case errSecNotAvailable: - return ErrorDescription(QKeychain::AccessDenied, Job::tr("No keychain is available. You may need to restart your computer")); - case errSecAuthFailed: - return ErrorDescription(QKeychain::AccessDenied, Job::tr("The user name or passphrase you entered is not correct")); - case errSecVerifyFailed: - return ErrorDescription(QKeychain::AccessDenied, Job::tr("A cryptographic verification failure has occurred")); - case errSecUnimplemented: - return ErrorDescription(QKeychain::NotImplemented, Job::tr("Function or operation not implemented")); - case errSecIO: - return ErrorDescription(QKeychain::OtherError, Job::tr("I/O error")); - case errSecOpWr: - return ErrorDescription(QKeychain::OtherError, Job::tr("Already open with with write permission")); - case errSecParam: - return ErrorDescription(QKeychain::OtherError, Job::tr("Invalid parameters passed to a function")); - case errSecAllocate: - return ErrorDescription(QKeychain::OtherError, Job::tr("Failed to allocate memory")); - case errSecBadReq: - return ErrorDescription(QKeychain::OtherError, Job::tr("Bad parameter or invalid state for operation")); - case errSecInternalComponent: - return ErrorDescription(QKeychain::OtherError, Job::tr("An internal component failed")); - case errSecDuplicateItem: - return ErrorDescription(QKeychain::OtherError, Job::tr("The specified item already exists in the keychain")); - case errSecDecode: - return ErrorDescription(QKeychain::OtherError, Job::tr("Unable to decode the provided data")); - } - - return ErrorDescription(QKeychain::OtherError, Job::tr("Unknown error")); - } -}; - -void ReadPasswordJobPrivate::scheduledStart() -{ - NSDictionary *const query = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - (__bridge id) kSecReturnData: @YES, - }; - - CFTypeRef dataRef = nil; - const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef); - - data.clear(); - mode = Binary; - - if (status == errSecSuccess) { - if (dataRef) - data = QByteArray::fromCFData((CFDataRef) dataRef); - - q->emitFinished(); - } else { - const ErrorDescription error = ErrorDescription::fromStatus(status); - q->emitFinishedWithError(error.code, Job::tr("Could not retrieve private key from keystore: %1").arg(error.message)); - } - - if (dataRef) - [dataRef release]; -} - -void WritePasswordJobPrivate::scheduledStart() -{ - NSDictionary *const query = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - }; - - OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, nil); - - if (status == errSecSuccess) { - NSDictionary *const update = @{ - (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(), - }; - - status = SecItemUpdate((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef) update); - } else { - NSDictionary *const insert = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(), - }; - - status = SecItemAdd((__bridge CFDictionaryRef) insert, nil); - } - - if (status == errSecSuccess) { - q->emitFinished(); - } else { - const ErrorDescription error = ErrorDescription::fromStatus(status); - q->emitFinishedWithError(error.code, tr("Could not store data in settings: %1").arg(error.message)); - } -} - -void DeletePasswordJobPrivate::scheduledStart() -{ - const NSDictionary *const query = @{ - (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword, - (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(), - (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(), - }; - - const OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query); - - if (status == errSecSuccess) { - q->emitFinished(); - } else { - const ErrorDescription error = ErrorDescription::fromStatus(status); - q->emitFinishedWithError(error.code, Job::tr("Could not remove private key from keystore: %1").arg(error.message)); - } -} diff --git a/client/qtkeychain/keychain_mac.cpp b/client/qtkeychain/keychain_mac.cpp deleted file mode 100644 index dc8365b..0000000 --- a/client/qtkeychain/keychain_mac.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2011-2015 Frank Osterfeld * - * * - * 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. For licensing and distribution * - * details, check the accompanying file 'COPYING'. * - *****************************************************************************/ -#include "keychain_p.h" - -#include -#include -#include - -using namespace QKeychain; - -template -struct Releaser { - explicit Releaser( const T& v ) : value( v ) {} - ~Releaser() { - CFRelease( value ); - } - - const T value; -}; - -static QString strForStatus( OSStatus os ) { - const Releaser str( SecCopyErrorMessageString( os, 0 ) ); - const char * const buf = CFStringGetCStringPtr( str.value, kCFStringEncodingUTF8 ); - if ( !buf ) - return QObject::tr( "OS X Keychain error (OSStatus %1)" ).arg( os ); - return QObject::tr( "%1 (OSStatus %2)" ) - .arg( QString::fromUtf8( buf, strlen( buf ) ) ).arg( os ); -} - -static OSStatus readPw( QByteArray* pw, - const QString& service, - const QString& account, - SecKeychainItemRef* ref ) { - Q_ASSERT( pw ); - pw->clear(); - const QByteArray serviceData = service.toUtf8(); - const QByteArray accountData = account.toUtf8(); - - void* data = 0; - UInt32 len = 0; - - const OSStatus ret = SecKeychainFindGenericPassword( NULL, // default keychain - serviceData.size(), - serviceData.constData(), - accountData.size(), - accountData.constData(), - &len, - &data, - ref ); - if ( ret == noErr ) { - *pw = QByteArray( reinterpret_cast( data ), len ); - const OSStatus ret2 = SecKeychainItemFreeContent ( 0, data ); - if ( ret2 != noErr ) - qWarning() << "Could not free item content: " << strForStatus( ret2 ); - } - return ret; -} - -void ReadPasswordJobPrivate::scheduledStart() -{ - QString errorString; - Error error = NoError; - const OSStatus ret = readPw( &data, q->service(), q->key(), 0 ); - - switch ( ret ) { - case noErr: - break; - case errSecItemNotFound: - errorString = tr("Password not found"); - error = EntryNotFound; - break; - default: - errorString = strForStatus( ret ); - error = OtherError; - break; - } - q->emitFinishedWithError( error, errorString ); -} - - -static QKeychain::Error deleteEntryImpl( const QString& service, const QString& account, QString* err ) { - SecKeychainItemRef ref; - QByteArray pw; - const OSStatus ret1 = readPw( &pw, service, account, &ref ); - if ( ret1 == errSecItemNotFound ) - return NoError; // No item stored, we're done - if ( ret1 != noErr ) { - *err = strForStatus( ret1 ); - //TODO map error code, set errstr - return OtherError; - } - const Releaser releaser( ref ); - - const OSStatus ret2 = SecKeychainItemDelete( ref ); - - if ( ret2 == noErr ) - return NoError; - //TODO map error code - *err = strForStatus( ret2 ); - return CouldNotDeleteEntry; -} - -static QKeychain::Error writeEntryImpl( const QString& service, - const QString& account, - const QByteArray& data, - QString* err ) { - Q_ASSERT( err ); - err->clear(); - const QByteArray serviceData = service.toUtf8(); - const QByteArray accountData = account.toUtf8(); - const OSStatus ret = SecKeychainAddGenericPassword( NULL, //default keychain - serviceData.size(), - serviceData.constData(), - accountData.size(), - accountData.constData(), - data.size(), - data.constData(), - NULL //item reference - ); - if ( ret != noErr ) { - switch ( ret ) { - case errSecDuplicateItem: - { - Error derr = deleteEntryImpl( service, account, err ); - if ( derr != NoError ) - return CouldNotDeleteEntry; - else - return writeEntryImpl( service, account, data, err ); - } - default: - *err = strForStatus( ret ); - return OtherError; - } - } - - return NoError; -} - -void WritePasswordJobPrivate::scheduledStart() -{ - QString errorString; - Error error = NoError; - - error = writeEntryImpl( q->service(), key, data, &errorString ); - q->emitFinishedWithError( error, errorString ); -} - -void DeletePasswordJobPrivate::scheduledStart() -{ - QString errorString; - Error error = NoError; - - const Error derr = deleteEntryImpl( q->service(), key, &errorString ); - if ( derr != NoError ) - error = CouldNotDeleteEntry; - q->emitFinishedWithError( error, errorString ); -} diff --git a/client/qtkeychain/keychain_p.h b/client/qtkeychain/keychain_p.h index ab7f0be..a66cb42 100644 --- a/client/qtkeychain/keychain_p.h +++ b/client/qtkeychain/keychain_p.h @@ -15,7 +15,7 @@ #include #include -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) +#if defined(KEYCHAIN_DBUS) #include @@ -49,7 +49,7 @@ public: Mode mode; QByteArray data; -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) +#if defined(KEYCHAIN_DBUS) org::kde::KWallet* iface; int walletHandle; @@ -91,20 +91,20 @@ class ReadPasswordJobPrivate : public JobPrivate { Q_OBJECT public: explicit ReadPasswordJobPrivate( const QString &service_, ReadPasswordJob* qq ); - void scheduledStart(); + void scheduledStart() override; -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) - void fallbackOnError(const QDBusError& err); +#if defined(KEYCHAIN_DBUS) + void fallbackOnError(const QDBusError& err) override; private Q_SLOTS: - void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ); + void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) override; void kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ); - void kwalletFinished( QDBusPendingCallWatcher* watcher ); + void kwalletFinished( QDBusPendingCallWatcher* watcher ) override; #else //moc's too dumb to respect above macros, so just define empty slot implementations private Q_SLOTS: - void kwalletOpenFinished( QDBusPendingCallWatcher* ) {} + void kwalletOpenFinished( QDBusPendingCallWatcher* ) override {} void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {} - void kwalletFinished( QDBusPendingCallWatcher* ) {} + void kwalletFinished( QDBusPendingCallWatcher* ) override {} #endif friend class ReadPasswordJob; @@ -114,10 +114,10 @@ class WritePasswordJobPrivate : public JobPrivate { Q_OBJECT public: explicit WritePasswordJobPrivate( const QString &service_, WritePasswordJob* qq ); - void scheduledStart(); + void scheduledStart() override; -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) - void fallbackOnError(const QDBusError& err); +#if defined(KEYCHAIN_DBUS) + void fallbackOnError(const QDBusError& err) override; #endif friend class WritePasswordJob; @@ -128,10 +128,10 @@ class DeletePasswordJobPrivate : public JobPrivate { public: explicit DeletePasswordJobPrivate( const QString &service_, DeletePasswordJob* qq ); - void scheduledStart(); + void scheduledStart() override; -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) - void fallbackOnError(const QDBusError& err); +#if defined(KEYCHAIN_DBUS) + void fallbackOnError(const QDBusError& err) override; #endif protected: diff --git a/client/qtkeychain/keychain_unix.cpp b/client/qtkeychain/keychain_unix.cpp index 958927a..a3e83a9 100644 --- a/client/qtkeychain/keychain_unix.cpp +++ b/client/qtkeychain/keychain_unix.cpp @@ -19,24 +19,35 @@ enum KeyringBackend { Backend_LibSecretKeyring, Backend_GnomeKeyring, Backend_Kwallet4, - Backend_Kwallet5 + Backend_Kwallet5, + Backend_Kwallet6, }; enum DesktopEnvironment { DesktopEnv_Gnome, DesktopEnv_Kde4, DesktopEnv_Plasma5, + DesktopEnv_Plasma6, DesktopEnv_Unity, DesktopEnv_Xfce, DesktopEnv_Other }; +static constexpr const char KWALLET6_DBUS_IFACE[] = "org.kde.kwalletd6"; +static constexpr const char KWALLET6_DBUS_PATH[] = "/modules/kwalletd6"; +static constexpr const char KWALLET5_DBUS_IFACE[] = "org.kde.kwalletd5"; +static constexpr const char KWALLET5_DBUS_PATH[] = "/modules/kwalletd5"; +static constexpr const char KWALLET4_DBUS_IFACE[] = "org.kde.kwalletd"; +static constexpr const char KWALLET4_DBUS_PATH[] = "/modules/kwalletd"; + // the following detection algorithm is derived from chromium, // licensed under BSD, see base/nix/xdg_util.cc static DesktopEnvironment getKdeVersion() { QByteArray value = qgetenv("KDE_SESSION_VERSION"); - if ( value == "5" ) { + if ( value == "6" ) { + return DesktopEnv_Plasma6; + } else if ( value == "5" ) { return DesktopEnv_Plasma5; } else if (value == "4" ) { return DesktopEnv_Kde4; @@ -78,14 +89,14 @@ static DesktopEnvironment detectDesktopEnvironment() { return DesktopEnv_Other; } -static bool isKwallet5Available() +static bool isKwalletAvailable(const char *dbusIface, const char *dbusPath) { if (!QDBusConnection::sessionBus().isConnected()) return false; org::kde::KWallet iface( - QLatin1String("org.kde.kwalletd5"), - QLatin1String("/modules/kwalletd5"), + QLatin1String(dbusIface), + QLatin1String(dbusPath), QDBusConnection::sessionBus()); // At this point iface.isValid() can return false even though the @@ -93,7 +104,7 @@ static bool isKwallet5Available() // a wallet can be opened. iface.setTimeout(500); - QDBusMessage reply = iface.call(QStringLiteral("networkWallet")); + QDBusMessage reply = iface.call(QLatin1String("networkWallet")); return reply.type() == QDBusMessage::ReplyMessage; } @@ -118,7 +129,7 @@ static KeyringBackend detectKeyringBackend() return Backend_Kwallet4; case DesktopEnv_Plasma5: - if (isKwallet5Available()) { + if (isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH)) { return Backend_Kwallet5; } if (LibSecretKeyring::isAvailable()) { @@ -130,6 +141,19 @@ static KeyringBackend detectKeyringBackend() // During startup the keychain backend might just not have started yet return Backend_Kwallet5; + case DesktopEnv_Plasma6: + if (isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH)) { + return Backend_Kwallet6; + } + if (LibSecretKeyring::isAvailable()) { + return Backend_LibSecretKeyring; + } + if (GnomeKeyring::isAvailable()) { + return Backend_GnomeKeyring; + } + // During startup the keychain backend might just not have started yet + return Backend_Kwallet6; + case DesktopEnv_Gnome: case DesktopEnv_Unity: case DesktopEnv_Xfce: @@ -141,7 +165,10 @@ static KeyringBackend detectKeyringBackend() if (GnomeKeyring::isAvailable()) { return Backend_GnomeKeyring; } - if (isKwallet5Available()) { + if (isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH)) { + return Backend_Kwallet6; + } + if (isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH)) { return Backend_Kwallet5; } // During startup the keychain backend might just not have started yet @@ -198,10 +225,13 @@ void ReadPasswordJobPrivate::scheduledStart() { break; case Backend_Kwallet4: - kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd", "/modules/kwalletd", this); + kwalletReadPasswordScheduledStartImpl(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this); break; case Backend_Kwallet5: - kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd5", "/modules/kwalletd5", this); + kwalletReadPasswordScheduledStartImpl(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this); + break; + case Backend_Kwallet6: + kwalletReadPasswordScheduledStartImpl(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this); break; } } @@ -454,10 +484,13 @@ void WritePasswordJobPrivate::scheduledStart() { break; case Backend_Kwallet4: - kwalletWritePasswordScheduledStart("org.kde.kwalletd", "/modules/kwalletd", this); + kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this); break; case Backend_Kwallet5: - kwalletWritePasswordScheduledStart("org.kde.kwalletd5", "/modules/kwalletd5", this); + kwalletWritePasswordScheduledStart(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this); + break; + case Backend_Kwallet6: + kwalletWritePasswordScheduledStart(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this); break; } } @@ -512,12 +545,16 @@ void JobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) { QDBusPendingReply nextReply; - if ( mode == Text ) - nextReply = iface->writePassword( handle, q->service(), key, QString::fromUtf8(data), q->service() ); - else if ( mode == Binary ) - nextReply = iface->writeEntry( handle, q->service(), key, data, q->service() ); - else + if ( !data.isNull() ) { + if ( mode == Text ) { + nextReply = iface->writePassword( handle, q->service(), key, QString::fromUtf8(data), q->service() ); + } else { + Q_ASSERT( mode == Binary ); + nextReply = iface->writeEntry( handle, q->service(), key, data, q->service() ); + } + } else { nextReply = iface->removeEntry( handle, q->service(), key, q->service() ); + } QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this ); connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(QDBusPendingCallWatcher*)) ); @@ -558,10 +595,13 @@ void DeletePasswordJobPrivate::scheduledStart() { break; case Backend_Kwallet4: - kwalletWritePasswordScheduledStart("org.kde.kwalletd", "/modules/kwalletd", this); + kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this); break; case Backend_Kwallet5: - kwalletWritePasswordScheduledStart("org.kde.kwalletd5", "/modules/kwalletd5", this); + kwalletWritePasswordScheduledStart(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this); + break; + case Backend_Kwallet6: + kwalletWritePasswordScheduledStart(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this); break; } } @@ -584,3 +624,10 @@ void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) { q->emitFinished(); } + +bool QKeychain::isAvailable() +{ + return LibSecretKeyring::isAvailable() || GnomeKeyring::isAvailable() + || isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH) + || isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH); +} diff --git a/client/qtkeychain/keychain_win.cpp b/client/qtkeychain/keychain_win.cpp index 2366b47..98dd34c 100644 --- a/client/qtkeychain/keychain_win.cpp +++ b/client/qtkeychain/keychain_win.cpp @@ -24,20 +24,20 @@ void ReadPasswordJobPrivate::scheduledStart() { PCREDENTIALW cred; if (!CredReadW(name, CRED_TYPE_GENERIC, 0, &cred)) { - Error error; + Error err; QString msg; switch(GetLastError()) { case ERROR_NOT_FOUND: - error = EntryNotFound; + err = EntryNotFound; msg = tr("Password entry not found"); break; default: - error = OtherError; + err = OtherError; msg = tr("Could not decrypt data"); break; } - q->emitFinishedWithError( error, msg ); + q->emitFinishedWithError( err, msg ); return; } @@ -96,20 +96,20 @@ void DeletePasswordJobPrivate::scheduledStart() { LPCWSTR name = (LPCWSTR)key.utf16(); if (!CredDeleteW(name, CRED_TYPE_GENERIC, 0)) { - Error error; + Error err; QString msg; switch(GetLastError()) { case ERROR_NOT_FOUND: - error = EntryNotFound; + err = EntryNotFound; msg = tr("Password entry not found"); break; default: - error = OtherError; + err = OtherError; msg = tr("Could not decrypt data"); break; } - q->emitFinishedWithError( error, msg ); + q->emitFinishedWithError( err, msg ); } else { q->emitFinished(); } @@ -129,10 +129,10 @@ void ReadPasswordJobPrivate::scheduledStart() { blob_in.cbData = encrypted.size(); const BOOL ret = CryptUnprotectData( &blob_in, - NULL, - NULL, - NULL, - NULL, + nullptr, + nullptr, + nullptr, + nullptr, 0, &blob_out ); if ( !ret ) { @@ -153,9 +153,9 @@ void WritePasswordJobPrivate::scheduledStart() { blob_in.cbData = data.size(); const BOOL res = CryptProtectData( &blob_in, L"QKeychain-encrypted data", - NULL, - NULL, - NULL, + nullptr, + nullptr, + nullptr, 0, &blob_out ); if ( !res ) { @@ -186,3 +186,8 @@ void DeletePasswordJobPrivate::scheduledStart() { } } #endif + +bool QKeychain::isAvailable() +{ + return true; +} diff --git a/client/qtkeychain/libsecret.cpp b/client/qtkeychain/libsecret.cpp index aed91a0..2289a12 100644 --- a/client/qtkeychain/libsecret.cpp +++ b/client/qtkeychain/libsecret.cpp @@ -5,7 +5,6 @@ #include "libsecret_p.h" #include -#include #if defined(HAVE_LIBSECRET) const SecretSchema* qtkeychainSchema(void) { @@ -54,14 +53,14 @@ typedef gboolean (*secret_password_clear_finish_t) (GAsyncResult *result, typedef void (*secret_password_free_t) (gchar *password); typedef GQuark (*secret_error_get_quark_t) (void) G_GNUC_CONST; -static secret_password_lookup_t secret_password_lookup_fn = NULL; -static secret_password_lookup_finish_t secret_password_lookup_finish_fn = NULL; -static secret_password_store_t secret_password_store_fn = NULL; -static secret_password_store_finish_t secret_password_store_finish_fn = NULL; -static secret_password_clear_t secret_password_clear_fn = NULL; -static secret_password_clear_finish_t secret_password_clear_finish_fn = NULL; -static secret_password_free_t secret_password_free_fn = NULL; -static secret_error_get_quark_t secret_error_get_quark_fn = NULL; +static secret_password_lookup_t secret_password_lookup_fn = nullptr; +static secret_password_lookup_finish_t secret_password_lookup_finish_fn = nullptr; +static secret_password_store_t secret_password_store_fn = nullptr; +static secret_password_store_finish_t secret_password_store_finish_fn = nullptr; +static secret_password_clear_t secret_password_clear_fn = nullptr; +static secret_password_clear_finish_t secret_password_clear_finish_fn = nullptr; +static secret_password_free_t secret_password_free_fn = nullptr; +static secret_error_get_quark_t secret_error_get_quark_fn = nullptr; static QKeychain::Error gerrorToCode(const GError *error) { if (error->domain != secret_error_get_quark_fn()) { @@ -83,7 +82,7 @@ on_password_lookup (GObject *source, GAsyncResult *result, gpointer inst) { - GError *error = NULL; + GError *error = nullptr; callbackArg *arg = (callbackArg*)inst; gchar *password = secret_password_lookup_finish_fn (result, &error); @@ -95,7 +94,7 @@ on_password_lookup (GObject *source, arg->self->q->emitFinishedWithError( code, QString::fromUtf8(error->message) ); } else { - if (password != NULL) { + if (password) { QByteArray raw = QByteArray(password); switch(arg->self->mode) { case QKeychain::JobPrivate::Binary: @@ -109,12 +108,12 @@ on_password_lookup (GObject *source, arg->self->q->emitFinished(); } else if (arg->self->mode == QKeychain::JobPrivate::Text) { arg->self->mode = QKeychain::JobPrivate::Binary; - secret_password_lookup_fn (qtkeychainSchema(), NULL, + secret_password_lookup_fn (qtkeychainSchema(), nullptr, on_password_lookup, arg, "user", arg->user.toUtf8().constData(), "server", arg->server.toUtf8().constData(), "type", "base64", - NULL); + nullptr); return; } else { arg->self->q->emitFinishedWithError( QKeychain::EntryNotFound, QObject::tr("Entry not found") ); @@ -139,7 +138,7 @@ on_password_stored (GObject *source, GAsyncResult *result, gpointer inst) { - GError *error = NULL; + GError *error = nullptr; QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst; Q_UNUSED(source); @@ -147,14 +146,14 @@ on_password_stored (GObject *source, secret_password_store_finish_fn (result, &error); if (self) { - if (error != NULL) { + if (error) { self->q->emitFinishedWithError( gerrorToCode(error), QString::fromUtf8(error->message) ); } else { self->q->emitFinished(); } } - if (error != NULL) { + if (error) { g_error_free (error); } } @@ -164,7 +163,7 @@ on_password_cleared (GObject *source, GAsyncResult *result, gpointer inst) { - GError *error = NULL; + GError *error = nullptr; QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst; gboolean removed = secret_password_clear_finish_fn (result, &error); @@ -178,7 +177,7 @@ on_password_cleared (GObject *source, self->q->emitFinished(); } } - if (error != NULL) { + if (error) { g_error_free (error); } } @@ -198,21 +197,21 @@ bool LibSecretKeyring::isAvailable() { const LibSecretKeyring& keyring = instance(); if (!keyring.isLoaded()) return false; - if (secret_password_lookup_fn == NULL) + if (secret_password_lookup_fn == nullptr) return false; - if (secret_password_lookup_finish_fn == NULL) + if (secret_password_lookup_finish_fn == nullptr) return false; - if (secret_password_store_fn == NULL) + if (secret_password_store_fn == nullptr) return false; - if (secret_password_store_finish_fn == NULL) + if (secret_password_store_finish_fn == nullptr) return false; - if (secret_password_clear_fn == NULL) + if (secret_password_clear_fn == nullptr) return false; - if (secret_password_clear_finish_fn == NULL) + if (secret_password_clear_finish_fn == nullptr) return false; - if (secret_password_free_fn == NULL) + if (secret_password_free_fn == nullptr) return false; - if (secret_error_get_quark_fn == NULL) + if (secret_error_get_quark_fn == nullptr) return false; return true; #else @@ -236,14 +235,16 @@ bool LibSecretKeyring::findPassword(const QString &user, const QString &server, arg->user = user; arg->server = server; - qDebug() << Q_FUNC_INFO; - secret_password_lookup_fn (qtkeychainSchema(), NULL, on_password_lookup, arg, + secret_password_lookup_fn (qtkeychainSchema(), nullptr, on_password_lookup, arg, "user", user.toUtf8().constData(), "server", server.toUtf8().constData(), "type", "plaintext", - NULL); + nullptr); return true; #else + Q_UNUSED(user) + Q_UNUSED(server) + Q_UNUSED(self) return false; #endif } @@ -271,16 +272,21 @@ bool LibSecretKeyring::writePassword(const QString &display_name, break; } - qDebug() << Q_FUNC_INFO; secret_password_store_fn (qtkeychainSchema(), SECRET_COLLECTION_DEFAULT, display_name.toUtf8().constData(), - pwd.constData(), NULL, on_password_stored, self, + pwd.constData(), nullptr, on_password_stored, self, "user", user.toUtf8().constData(), "server", server.toUtf8().constData(), "type", type.toUtf8().constData(), - NULL); + nullptr); return true; #else + Q_UNUSED(display_name) + Q_UNUSED(user) + Q_UNUSED(server) + Q_UNUSED(mode) + Q_UNUSED(password) + Q_UNUSED(self) return false; #endif } @@ -293,19 +299,21 @@ bool LibSecretKeyring::deletePassword(const QString &key, const QString &service return false; } - qDebug() << Q_FUNC_INFO; - secret_password_clear_fn (qtkeychainSchema(), NULL, on_password_cleared, self, + secret_password_clear_fn (qtkeychainSchema(), nullptr, on_password_cleared, self, "user", key.toUtf8().constData(), "server", service.toUtf8().constData(), - NULL); + nullptr); return true; #else + Q_UNUSED(key) + Q_UNUSED(service) + Q_UNUSED(self) return false; #endif } LibSecretKeyring::LibSecretKeyring() - : QLibrary(QStringLiteral("secret-1"), 0) + : QLibrary(QLatin1String("secret-1"), 0) { #ifdef HAVE_LIBSECRET if (load()) { diff --git a/client/qtkeychain/org.kde.KWallet.xml b/client/qtkeychain/org.kde.KWallet.xml index 548c2f8..ec4bd6e 100644 --- a/client/qtkeychain/org.kde.KWallet.xml +++ b/client/qtkeychain/org.kde.KWallet.xml @@ -156,7 +156,7 @@ - + @@ -164,7 +164,7 @@ - + @@ -172,7 +172,7 @@ - + diff --git a/client/qtkeychain/qt5keychain.pri b/client/qtkeychain/qtkeychain.pri similarity index 51% rename from client/qtkeychain/qt5keychain.pri rename to client/qtkeychain/qtkeychain.pri index ee6d4e8..5877641 100644 --- a/client/qtkeychain/qt5keychain.pri +++ b/client/qtkeychain/qtkeychain.pri @@ -2,23 +2,27 @@ # This file is provided as is without any warranty. # It can break at anytime or be removed without notice. -QT5KEYCHAIN_PWD = $$PWD +lessThan(QT_MAJOR_VERSION, 5) { + error("qtkeychain requires Qt 5 or later") +} + +QTKEYCHAIN_PWD = $$PWD CONFIG += depend_includepath DEFINES += QTKEYCHAIN_NO_EXPORT INCLUDEPATH += \ $$PWD/.. \ - $$QT5KEYCHAIN_PWD + $$QTKEYCHAIN_PWD HEADERS += \ - $$QT5KEYCHAIN_PWD/keychain_p.h \ - $$QT5KEYCHAIN_PWD/keychain.h + $$QTKEYCHAIN_PWD/keychain_p.h \ + $$QTKEYCHAIN_PWD/keychain.h SOURCES += \ - $$QT5KEYCHAIN_PWD/keychain.cpp + $$QTKEYCHAIN_PWD/keychain.cpp -unix:!macx:!ios { +unix:!android:!macx:!ios { # Remove the following LIBSECRET_SUPPORT line # to build without libsecret support. DEFINES += LIBSECRET_SUPPORT @@ -37,19 +41,34 @@ unix:!macx:!ios { } # Generate D-Bus interface: + DEFINES += KEYCHAIN_DBUS QT += dbus kwallet_interface.files = $$PWD/org.kde.KWallet.xml DBUS_INTERFACES += kwallet_interface HEADERS += \ - $$QT5KEYCHAIN_PWD/gnomekeyring_p.h \ - $$QT5KEYCHAIN_PWD/plaintextstore_p.h \ - $$QT5KEYCHAIN_PWD/libsecret_p.h + $$QTKEYCHAIN_PWD/gnomekeyring_p.h \ + $$QTKEYCHAIN_PWD/plaintextstore_p.h \ + $$QTKEYCHAIN_PWD/libsecret_p.h SOURCES += \ - $$QT5KEYCHAIN_PWD/keychain_unix.cpp \ - $$QT5KEYCHAIN_PWD/plaintextstore.cpp \ - $$QT5KEYCHAIN_PWD/gnomekeyring.cpp \ - $$QT5KEYCHAIN_PWD/libsecret.cpp + $$QTKEYCHAIN_PWD/keychain_unix.cpp \ + $$QTKEYCHAIN_PWD/plaintextstore.cpp \ + $$QTKEYCHAIN_PWD/gnomekeyring.cpp \ + $$QTKEYCHAIN_PWD/libsecret.cpp +} + +android { + lessThan(QT_MAJOR_VERSION, 6) { + QT += androidextras + } + + HEADERS += \ + $$QTKEYCHAIN_PWD/androidkeystore_p.h \ + $$QTKEYCHAIN_PWD/plaintextstore_p.h + SOURCES += \ + $$QTKEYCHAIN_PWD/androidkeystore.cpp \ + $$QTKEYCHAIN_PWD/keychain_android.cpp \ + $$QTKEYCHAIN_PWD/plaintextstore.cpp } win32 { @@ -59,25 +78,20 @@ win32 { DEFINES += USE_CREDENTIAL_STORE contains(DEFINES, USE_CREDENTIAL_STORE) { !build_pass:message("Windows Credential Store support: on") - LIBS += -lAdvapi32 + LIBS += -ladvapi32 } else { !build_pass:message("Windows Credential Store support: off") - LIBS += -lCrypt32 - HEADERS += $$QT5KEYCHAIN_PWD/plaintextstore_p.h - SOURCES += $$QT5KEYCHAIN_PWD/plaintextstore.cpp + LIBS += -lcrypt32 + HEADERS += $$QTKEYCHAIN_PWD/plaintextstore_p.h + SOURCES += $$QTKEYCHAIN_PWD/plaintextstore.cpp } - HEADERS += $$QT5KEYCHAIN_PWD/libsecret_p.h + HEADERS += $$QTKEYCHAIN_PWD/libsecret_p.h SOURCES += \ - $$QT5KEYCHAIN_PWD/keychain_win.cpp \ - $$QT5KEYCHAIN_PWD/libsecret.cpp + $$QTKEYCHAIN_PWD/keychain_win.cpp \ + $$QTKEYCHAIN_PWD/libsecret.cpp } -macx:!ios { - LIBS += "-framework Security" "-framework Foundation" - SOURCES += $$QT5KEYCHAIN_PWD/keychain_mac.cpp -} - -ios { - LIBS += "-framework Security" "-framework Foundation" - OBJECTIVE_SOURCES += $$QT5KEYCHAIN_PWD/keychain_ios.mm +macx|ios { + LIBS += -framework Security -framework Foundation + OBJECTIVE_SOURCES += $$QTKEYCHAIN_PWD/keychain_apple.mm } diff --git a/client/qtkeychain/testclient.cpp b/client/qtkeychain/testclient.cpp index 94189cf..f745bd3 100644 --- a/client/qtkeychain/testclient.cpp +++ b/client/qtkeychain/testclient.cpp @@ -6,7 +6,14 @@ * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * * details, check the accompanying file 'COPYING'. * *****************************************************************************/ + +#include +#ifdef Q_OS_DARWIN +#include +#else #include +#endif + #include #include "keychain.h" @@ -22,7 +29,14 @@ static int printUsage() { } int main( int argc, char** argv ) { +#ifdef Q_OS_DARWIN + // Since we use NSNotificationCenter under the hood in keychain_apple, + // we use QGuiApplication to automatically configure the platform + // integration stuff done in this class and not in QCoreApplication + QGuiApplication app( argc, argv ); +#else QCoreApplication app( argc, argv ); +#endif const QStringList args = app.arguments(); if ( args.count() < 2 ) return printUsage(); diff --git a/client/qtkeychain/translations/qtkeychain_de.ts b/client/qtkeychain/translations/qtkeychain_de.ts index 0543230..6610c0a 100644 --- a/client/qtkeychain/translations/qtkeychain_de.ts +++ b/client/qtkeychain/translations/qtkeychain_de.ts @@ -1,239 +1,175 @@ - - - QKeychain::DeletePasswordJobPrivate - - - Password entry not found - - - - - Could not decrypt data - - - - - - Unknown error - - - - - Could not open wallet: %1; %2 - - - - - QKeychain::JobPrivate - - - Unknown error - - - - - Access to keychain denied - - - - - QKeychain::PlainTextStore - - - Could not store data in settings: access error - - - - - Could not store data in settings: format error - - - - - Could not delete data from settings: access error - - - - - Could not delete data from settings: format error - - - - - Entry not found - - - + QKeychain::ReadPasswordJobPrivate - - Password entry not found - + + Unknown error + Unbekannter Fehler - - - Could not decrypt data - - - - + D-Bus is not running - - - Unknown error - - - - + No keychain service available - + Kein Schlüsselbund-Dienst verfügbar - + Could not open wallet: %1; %2 - + Konnte Brieftasche nicht öffnen: %1; %2 - + Access to keychain denied - + Zugriff auf Schlüsselbund verweigert - + Could not determine data type: %1; %2 - + Datentyp kann nicht ermittelt werden: %1: %2 - - Entry not found - - - - + Unsupported entry type 'Map' - + Unknown kwallet entry type '%1' - + + Could not read password: %1; %2 + Passwort konnte nicht ausgelesen werden: %1; %2 + + + Password not found - + Passwort nicht gefunden + + + + + Entry not found + Eintrag nicht gefunden + + + + Could not decrypt data + Kann Daten nicht entschlüsseln QKeychain::WritePasswordJobPrivate - - Credential size exceeds maximum size of %1 - + + + Unknown error + Unbekannter Fehler - - Credential key exceeds maximum size of %1 - - - - - Writing credentials failed: Win32 error code %1 - - - - - Encryption failed - - - - + D-Bus is not running - - - Unknown error - + + + Could not open wallet: %1; %2 + Konnte Brieftasche nicht öffnen: %1; %2 - - Could not open wallet: %1; %2 - + + Access to keychain denied + Zugriff auf Schlüsselbund verweigert + + + + Could not delete encrypted data from settings: access error + Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Zugriffsfehler + + + + Could not delete encrypted data from settings: format error + Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Formatfehler + + + + Encryption failed + Verschlüsselung fehlgeschlagen + + + + Could not store encrypted data in settings: access error + Kann verschlüsselte Daten nicht in den Einstellungen speichern: Zugriffsfehler + + + + Could not store encrypted data in settings: format error + Kann verschlüsselte Daten nicht in den Einstellungen speichern: Formatfehler QObject - - Entry not found - - - - + Access to keychain denied - + Zugriff auf Schlüsselbund verweigert - + No keyring daemon - + Kein Schlüsselbund-Dienst - + Already unlocked - + Bereits entsperrt - + No such keyring - + Kein solcher Schlüsselbund - + Bad arguments - + Ungültige Argumente - + I/O error - + Ein-/Ausgabe-Fehler - + Cancelled - + Abgebrochen - + Keyring already exists - + Schlüsselbund existiert bereits - + No match - + Kein Treffer - + Unknown error - + Unbekannter Fehler - OS X Keychain error (OSStatus %1) - - - - + %1 (OSStatus %2) diff --git a/client/qtkeychain/translations/qtkeychain_fr.ts b/client/qtkeychain/translations/qtkeychain_fr.ts new file mode 100644 index 0000000..35c60b9 --- /dev/null +++ b/client/qtkeychain/translations/qtkeychain_fr.ts @@ -0,0 +1,267 @@ + + + + + QKeychain::DeletePasswordJobPrivate + + + Password entry not found + Mot de passe introuvable + + + + Could not decrypt data + Impossible de déchiffrer les données + + + + + Unknown error + Erreur inconnue + + + + Could not open wallet: %1; %2 + Impossible d'ouvrir le portefeuille : %1; %2 + + + + Password not found + Mot de passe introuvable + + + + QKeychain::JobPrivate + + + Unknown error + Erreur inconnue + + + + Access to keychain denied + Accès au trousseau refusé + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + Impossible de stocker les données dans les paramètres : Erreur d'accès + + + + Could not store data in settings: format error + Impossible de stocker les données dans les paramètres : Erreur de format + + + + Could not delete data from settings: access error + Impossible de supprimer les données depuis les paramètres : Erreur d'accès + + + + Could not delete data from settings: format error + Impossible de supprimer les données depuis les paramètres : Erreur de format + + + + Entry not found + Entrée introuvable + + + + QKeychain::ReadPasswordJobPrivate + + + + Unknown error + Erreur inconnue + + + + D-Bus is not running + D-Bus n'est pas en cours d'exécution + + + + No keychain service available + Aucun service de trousseau disponible + + + + Could not open wallet: %1; %2 + Impossible d'ouvrir le trousseau : %1; %2 + + + + Access to keychain denied + Accès au trousseau refusé + + + + Could not determine data type: %1; %2 + Impossible de déterminer le type de données : %1: %2 + + + + Unsupported entry type 'Map' + Type d'entrée non supporté 'Map' + + + + Unknown kwallet entry type '%1' + Type de trousseau inconnu '%1' + + + Could not read password: %1; %2 + Impossible de lire le mot de passe : %1; %2 + + + + + Password not found + Mot de passe introuvable + + + + Entry not found + Entrée introuvable + + + + Password entry not found + Entrée de mot de passe introuvable + + + + + Could not decrypt data + Impossible de déchiffrer les données + + + + QKeychain::WritePasswordJobPrivate + + + + Unknown error + Erreur inconnue + + + + D-Bus is not running + D-Bus n'est pas en cours d'exécution + + + + Could not open wallet: %1; %2 + Impossible d'ouvrir le trousseau : %1; %2 + + + Access to keychain denied + Accès au trousseau refusé + + + Could not delete encrypted data from settings: access error + Impossible de supprimer des données chiffrées dans les paramètres : Erreur d'accès + + + Could not delete encrypted data from settings: format error + Impossible de supprimer des données chiffrées dans les paramètres : Erreur de format + + + + + Encryption failed + Le chiffrement a échoué + + + Could not store encrypted data in settings: access error + Impossible de stocker des données chiffrées dans les paramètres : Erreur d'accès + + + Could not store encrypted data in settings: format error + Impossible de stocker des données chiffrées dans les paramètres : Erreur de format + + + + Password not found + Mot de passe introuvable + + + + QObject + + + Access to keychain denied + Accès au trousseau refusé + + + + No keyring daemon + Aucun démon de trousseau + + + + Already unlocked + Déjà déverrouillé + + + + No such keyring + Aucun trousseau + + + + Bad arguments + Mauvais arguments + + + + I/O error + Erreur d'E/S + + + + Cancelled + Annulé + + + + Keyring already exists + Trousseau déjà existant + + + + No match + Aucune correspondance + + + + Unknown error + Erreur inconnue + + + + OS X Keychain error (OSStatus %1) + OS X Keychain error (OSStatus %1) + + + + %1 (OSStatus %2) + %1 (OSStatus %2) + + + + Entry not found + Entrée introuvable + + + + error 0x%1: %2 + Erreur 0x%1 : %2 + + + diff --git a/client/qtkeychain/translations/qtkeychain_nl.ts b/client/qtkeychain/translations/qtkeychain_nl.ts new file mode 100644 index 0000000..7033a98 --- /dev/null +++ b/client/qtkeychain/translations/qtkeychain_nl.ts @@ -0,0 +1,320 @@ + + + + + KeyChainClass + + + Read key failed: %1 + De sleutel kan niet worden ingelezen: %1 + + + + Write key failed: %1 + De sleutel kan niet worden weggeschreven: %1 + + + + Delete key failed: %1 + De sleutel kan niet worden verwijderd: %1 + + + + QKeychain::DeletePasswordJobPrivate + + + Could not open keystore + De sleutelbos kan niet worden geopend + + + + Could not remove private key from keystore + De privésleutel kan niet worden verwijderd uit de sleutelbos + + + + Password not found + Het wachtwoord is niet gevonden + + + + + Unknown error + Onbekende foutmelding + + + + Could not open wallet: %1; %2 + De sleutelbos kan niet worden geopend: %1; %2 + + + + Password entry not found + Het wachtwoord is niet gevonden + + + + Could not decrypt data + De gegevens kunnen niet worden ongrendeld + + + + QKeychain::JobPrivate + + + Unknown error + Onbekende foutmelding + + + + Access to keychain denied + U heeft geen toegang tot de sleutelbos + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + De gegevens kunnen niet worden opgeslagen in de instellingen: toegangsfout + + + + Could not store data in settings: format error + De gegevens kunnen niet worden opgeslagen in de instellingen: opmaakfout + + + + Could not delete data from settings: access error + De gegevens kunnen niet worden verwijderd uit de instellingen: toegangsfout + + + + Could not delete data from settings: format error + De gegevens kunnen niet worden verwijderd uit de instellingen: opmaakfout + + + + Entry not found + Het item is niet gevonden + + + + QKeychain::ReadPasswordJobPrivate + + + + Entry not found + Het item is niet gevonden + + + + Could not open keystore + De sleutelbos kan niet worden geopend + + + + Could not retrieve private key from keystore + De privésleutel kan niet worden verwijderd uit de sleutelbos + + + + Could not create decryption cipher + De ontgrendelcode kan niet worden aangemaakt + + + + Password not found + Het wachtwoord is niet gevonden + + + + D-Bus is not running + D-Bus is niet actief + + + + + Unknown error + Onbekende foutmelding + + + + No keychain service available + De sleutelbosdienst is niet beschikbaar + + + + Could not open wallet: %1; %2 + De sleutelbos kan niet worden geopend: %1; %2 + + + + Access to keychain denied + U heeft geen toegang tot de sleutelbos + + + + Could not determine data type: %1; %2 + De gegevensdrager kan niet worden bepaald: %1; %2 + + + + Unsupported entry type 'Map' + Niet-ondersteund itemtype ‘Map’ + + + + Unknown kwallet entry type '%1' + Onbekend KWallet-item van type ‘%1’ + + + + Password entry not found + Het wachtwoord is niet gevonden + + + + + Could not decrypt data + De gegevens kunnen niet worden ongrendeld + + + + QKeychain::WritePasswordJobPrivate + + + Could not open keystore + De sleutelbos kan niet worden geopend + + + + Could not create private key generator + De privésleutelgenerator kan niet worden gestart + + + + Could not generate new private key + Er kan geen nieuwe privésleutel worden gegenereerd + + + + Could not retrieve private key from keystore + De privésleutel kan niet worden opgehaald uit de sleutelbos + + + + Could not create encryption cipher + De vergrendelcode kan niet worden aangemaakt + + + + Could not encrypt data + De gegevens kunnen niet worden ontgrendeld + + + + Password not found + Het wachtwoord is niet gevonden + + + + D-Bus is not running + D-Bus is niet actief + + + + + Unknown error + Onbekende foutmelding + + + + Could not open wallet: %1; %2 + De sleutelbos kan niet worden geopend: %1; %2 + + + + Credential size exceeds maximum size of %1 + De omvang overschrijdt de maximumomvang van %1 + + + + Credential key exceeds maximum size of %1 + De sleutel overschrijdt de maximumomvang van %1 + + + + Writing credentials failed: Win32 error code %1 + De gegevens kunnen niet worden weggeschreven: Win32-foutcode %1 + + + + Encryption failed + Het ontgrendelen is mislukt + + + + QObject + + + error 0x%1: %2 + foutmelding 0x%1: %2 + + + + Access to keychain denied + U heeft geen toegang tot de sleutelbos + + + + No keyring daemon + De sleutelbosdienst is niet actief + + + + Already unlocked + De sleutelbos is reeds ontgrendeld + + + + No such keyring + De sleutelbos bestaat niet + + + + Bad arguments + Onjuiste opties + + + + I/O error + I/O-fout + + + + Cancelled + Geannuleerd + + + + Keyring already exists + De sleutelbos bestaat reeds + + + + No match + Er zijn geen overeenkomsten + + + + Unknown error + Onbekende foutmelding + + + + Entry not found + Het item is niet gevonden + + + diff --git a/client/qtkeychain/translations/qtkeychain_ro.ts b/client/qtkeychain/translations/qtkeychain_ro.ts index 73dd773..337f1aa 100644 --- a/client/qtkeychain/translations/qtkeychain_ro.ts +++ b/client/qtkeychain/translations/qtkeychain_ro.ts @@ -1,241 +1,178 @@ - - - QKeychain::DeletePasswordJobPrivate - - - Password entry not found - - - - - Could not decrypt data - - - - - - Unknown error - - - - - Could not open wallet: %1; %2 - - - - - QKeychain::JobPrivate - - - Unknown error - - - - - Access to keychain denied - - - - - QKeychain::PlainTextStore - - - Could not store data in settings: access error - - - - - Could not store data in settings: format error - - - - - Could not delete data from settings: access error - - - - - Could not delete data from settings: format error - - - - - Entry not found - - - + QKeychain::ReadPasswordJobPrivate - - Password entry not found - - - - - - Could not decrypt data - - - - - D-Bus is not running - - - - - + Unknown error - + Eroare necunoscută - + + D-Bus is not running + D-Bus nu rulează + + + No keychain service available - + Nu există niciun serviciu de chei disponibil + Kein Schlüsselbund-Dienst verfügbar + + + + Could not open wallet: %1; %2 + Nu se poate deschide portofelul: %1; %2 + + + + Access to keychain denied + Acces interzis la serviciul de chei + + + + Could not determine data type: %1; %2 + Nu se poate stabili tipul de date: %1: %2 + + + + Unsupported entry type 'Map' + Tip de înregistrare nesuportat 'Map' + + + + Unknown kwallet entry type '%1' + Tip de înregistrare kwallet necunoscut '%1' + + + + Could not read password: %1; %2 + Nu se poate citi parola: %1; %2 + + + + Password not found + Parola nu a fost găsită - Could not open wallet: %1; %2 - - - - - Access to keychain denied - - - - - Could not determine data type: %1; %2 - - - - + Entry not found - + Înregistrarea nu a fost găsită - - Unsupported entry type 'Map' - - - - - Unknown kwallet entry type '%1' - - - - - Password not found - + + Could not decrypt data + Nu se poate decripta data QKeychain::WritePasswordJobPrivate - - Credential size exceeds maximum size of %1 - - - - - Credential key exceeds maximum size of %1 - - - - - Writing credentials failed: Win32 error code %1 - - - - - Encryption failed - - - - - D-Bus is not running - - - - - + + Unknown error - + Eroare necunoscută - + + D-Bus is not running + D-Bus nu rulează + + + + Could not open wallet: %1; %2 - + Nu se poate deschide portofelul: %1; %2 + + + + Access to keychain denied + Acces interzis la serviciul de chei + + + + Could not delete encrypted data from settings: access error + Nu se pot șterge datele criptate din setări: eroare de acces + + + + Could not delete encrypted data from settings: format error + Nu se pot șterge datele criptate din setări: eroare de format + + + + Encryption failed + Criptarea a eșuat + + + + Could not store encrypted data in settings: access error + Nu se pot stoca datele criptate în setări: eroare de acces + + + + Could not store encrypted data in settings: format error + Nu se pot stoca datele criptate în setări: eroare de format QObject - - Entry not found - - - - + Access to keychain denied - + Acces interzis la serviciul de chei - + No keyring daemon - + Niciun demon pentru inelul de chei - + Already unlocked - + Deja deblocat - + No such keyring - + Nu există astfel de inel de chei - + Bad arguments - + Argumente greșite - + I/O error - + Eroare de I/E - + Cancelled - + Anulat - + Keyring already exists - + Inelul de chei deja există - + No match - + Nicio potrivire - + Unknown error - + Eroare necunoscută - OS X Keychain error (OSStatus %1) - - - - + %1 (OSStatus %2) - + %1 (OSStatus %2) diff --git a/client/qtkeychain/translations/qtkeychain_ru.ts b/client/qtkeychain/translations/qtkeychain_ru.ts new file mode 100644 index 0000000..1d61129 --- /dev/null +++ b/client/qtkeychain/translations/qtkeychain_ru.ts @@ -0,0 +1,241 @@ + + + + + QKeychain::DeletePasswordJobPrivate + + + + Unknown error + Неизвестная ошибка + + + + Could not open wallet: %1; %2 + Не удалось открыть бумажник: %1; %2 + + + + Password entry not found + Пароль не найден + + + + Could not decrypt data + Не удалось расшифровать данные + + + + QKeychain::JobPrivate + + + Unknown error + Неизвестная ошибка + + + + Access to keychain denied + Доступ к связке ключей запрещён + + + + QKeychain::PlainTextStore + + + Could not store data in settings: access error + Не удалось сохранить данные в настройках: ошибка доступа + + + + Could not store data in settings: format error + Не удалось сохранить данные в настройках: ошибка формата + + + + Could not delete data from settings: access error + Не удалось удалить данные из настроек: ошибка доступа + + + + Could not delete data from settings: format error + Не удалось удалить данные из настроек: ошибка формата + + + + Entry not found + Запись не найдена + + + + QKeychain::ReadPasswordJobPrivate + + + Password not found + Пароль не найден + + + + D-Bus is not running + D-Bus не запущен + + + + + Unknown error + Неизвестная ошибка + + + + No keychain service available + Служба связки ключей недоступна + + + + Could not open wallet: %1; %2 + Не удалось открыть кошелёк: %1; %2 + + + + Access to keychain denied + Доступ к связке ключей запрещён + + + + Could not determine data type: %1; %2 + Не удалось определить тип данных: %1; %2 + + + + Entry not found + Запись не найдена + + + + Unsupported entry type 'Map' + Неподдерживаемый тип записи 'Map' + + + + Unknown kwallet entry type '%1' + Неизвестный тип записи kwallet '%1' + + + + Password entry not found + Пароль не найден + + + + + Could not decrypt data + Не удалось расшифровать данные + + + + QKeychain::WritePasswordJobPrivate + + + D-Bus is not running + D-Bus не запущен + + + + + Unknown error + Неизвестная ошибка + + + + Could not open wallet: %1; %2 + Не удалось открыть кошелёк: %1; %2 + + + + Credential size exceeds maximum size of %1 + Учётные данные превышают максимальный размер %1 + + + + Credential key exceeds maximum size of %1 + Ключ учётных данных превышает максимальный размер %1 + + + + Writing credentials failed: Win32 error code %1 + Не удалось сохранить учётные данные: код ошибки win32 %1 + + + + Encryption failed + Шифрование не удалось + + + + QObject + + + OS X Keychain error (OSStatus %1) + Ошибка связки ключей OS X (OSStatus %1) + + + + %1 (OSStatus %2) + %1 (OSStatus %2) + + + + Access to keychain denied + Доступ к связке ключей запрещён + + + + No keyring daemon + Нет демона связки ключей + + + + Already unlocked + Уже разблокировано + + + + No such keyring + Связка ключей не найдена + + + + Bad arguments + Неверные аргументы + + + + I/O error + Ошибка ввода/вывода + + + + Cancelled + Отменено + + + + Keyring already exists + Связка ключей уже существует + + + + No match + Нет совпадений + + + + Unknown error + Неизвестная ошибка + + + + Entry not found + Запись не найдена + + + diff --git a/client/qtkeychain/translations/translations.qrc.in b/client/qtkeychain/translations/translations.qrc.in new file mode 100644 index 0000000..f49df66 --- /dev/null +++ b/client/qtkeychain/translations/translations.qrc.in @@ -0,0 +1,5 @@ + + + @QM_FILE_LIST@ + +