Add code editor syntax highlighter & validate customized frame parser code
@ -18,6 +18,7 @@
|
||||
<file>icons/compass.svg</file>
|
||||
<file>icons/connect.svg</file>
|
||||
<file>icons/copy.svg</file>
|
||||
<file>icons/cut.svg</file>
|
||||
<file>icons/dashboard.svg</file>
|
||||
<file>icons/dataset.svg</file>
|
||||
<file>icons/delete-item.svg</file>
|
||||
@ -39,6 +40,7 @@
|
||||
<file>icons/graphs.svg</file>
|
||||
<file>icons/group.svg</file>
|
||||
<file>icons/gyro.svg</file>
|
||||
<file>icons/heart-broken.svg</file>
|
||||
<file>icons/help.svg</file>
|
||||
<file>icons/hide-all.svg</file>
|
||||
<file>icons/info.svg</file>
|
||||
@ -59,6 +61,7 @@
|
||||
<file>icons/points.svg</file>
|
||||
<file>icons/power.svg</file>
|
||||
<file>icons/ram.svg</file>
|
||||
<file>icons/redo.svg</file>
|
||||
<file>icons/refresh.svg</file>
|
||||
<file>icons/registration.svg</file>
|
||||
<file>icons/restore.svg</file>
|
||||
@ -75,7 +78,9 @@
|
||||
<file>icons/settings.svg</file>
|
||||
<file>icons/show-all.svg</file>
|
||||
<file>icons/start-sequence.svg</file>
|
||||
<file>icons/template.svg</file>
|
||||
<file>icons/thermometer.svg</file>
|
||||
<file>icons/undo.svg</file>
|
||||
<file>icons/up.svg</file>
|
||||
<file>icons/update.svg</file>
|
||||
<file>icons/usb.svg</file>
|
||||
@ -107,12 +112,6 @@
|
||||
<file>qml/FramelessWindow/Titlebar.qml</file>
|
||||
<file>qml/FramelessWindow/WindowButton.qml</file>
|
||||
<file>qml/FramelessWindow/WindowButtonMacOS.qml</file>
|
||||
<file>qml/ProjectEditor/Footer.qml</file>
|
||||
<file>qml/ProjectEditor/GroupEditor.qml</file>
|
||||
<file>qml/ProjectEditor/Header.qml</file>
|
||||
<file>qml/ProjectEditor/JsonDatasetDelegate.qml</file>
|
||||
<file>qml/ProjectEditor/JsonGroupDelegate.qml</file>
|
||||
<file>qml/ProjectEditor/TreeView.qml</file>
|
||||
<file>qml/Panes/SetupPanes/Devices/BluetoothLE.qml</file>
|
||||
<file>qml/Panes/SetupPanes/Devices/Network.qml</file>
|
||||
<file>qml/Panes/SetupPanes/Devices/Serial.qml</file>
|
||||
@ -126,6 +125,12 @@
|
||||
<file>qml/PlatformDependent/DecentMenuItem.qml</file>
|
||||
<file>qml/PlatformDependent/Menubar.qml</file>
|
||||
<file>qml/PlatformDependent/MenubarMacOS.qml</file>
|
||||
<file>qml/ProjectEditor/Footer.qml</file>
|
||||
<file>qml/ProjectEditor/GroupEditor.qml</file>
|
||||
<file>qml/ProjectEditor/Header.qml</file>
|
||||
<file>qml/ProjectEditor/JsonDatasetDelegate.qml</file>
|
||||
<file>qml/ProjectEditor/JsonGroupDelegate.qml</file>
|
||||
<file>qml/ProjectEditor/TreeView.qml</file>
|
||||
<file>qml/Widgets/Icon.qml</file>
|
||||
<file>qml/Widgets/JSONDropArea.qml</file>
|
||||
<file>qml/Widgets/Shadow.qml</file>
|
||||
@ -135,9 +140,9 @@
|
||||
<file>qml/Windows/Acknowledgements.qml</file>
|
||||
<file>qml/Windows/CsvPlayer.qml</file>
|
||||
<file>qml/Windows/Donate.qml</file>
|
||||
<file>qml/Windows/ProjectEditor.qml</file>
|
||||
<file>qml/Windows/MainWindow.qml</file>
|
||||
<file>qml/Windows/MQTTConfiguration.qml</file>
|
||||
<file>qml/Windows/ProjectEditor.qml</file>
|
||||
<file>qml/main.qml</file>
|
||||
<file>themes/1_Flat.json</file>
|
||||
<file>themes/2_Dark.json</file>
|
||||
@ -173,6 +178,6 @@
|
||||
<file>window-border/minimize.svg</file>
|
||||
<file>window-border/restore.svg</file>
|
||||
<file>window-border/unmaximize.svg</file>
|
||||
<file>icons/heart-broken.svg</file>
|
||||
<file>icons/paste.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4l6 6v10c0 1.1-.9 2-2 2H7.99C6.89 23 6 22.1 6 21l.01-14c0-1.1.89-2 1.99-2h7zm-1 7h5.5L14 6.5V12z"/></svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" /></svg>
|
Before Width: | Height: | Size: 274 B After Width: | Height: | Size: 417 B |
1
assets/icons/cut.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M19,3L13,9L15,11L22,4V3M12,12.5A0.5,0.5 0 0,1 11.5,12A0.5,0.5 0 0,1 12,11.5A0.5,0.5 0 0,1 12.5,12A0.5,0.5 0 0,1 12,12.5M6,20A2,2 0 0,1 4,18C4,16.89 4.9,16 6,16A2,2 0 0,1 8,18C8,19.11 7.1,20 6,20M6,8A2,2 0 0,1 4,6C4,4.89 4.9,4 6,4A2,2 0 0,1 8,6C8,7.11 7.1,8 6,8M9.64,7.64C9.87,7.14 10,6.59 10,6A4,4 0 0,0 6,2A4,4 0 0,0 2,6A4,4 0 0,0 6,10C6.59,10 7.14,9.87 7.64,9.64L10,12L7.64,14.36C7.14,14.13 6.59,14 6,14A4,4 0 0,0 2,18A4,4 0 0,0 6,22A4,4 0 0,0 10,18C10,17.41 9.87,16.86 9.64,16.36L12,14L19,21H22V20L9.64,7.64Z" /></svg>
|
After Width: | Height: | Size: 806 B |
1
assets/icons/paste.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M19,20H5V4H7V7H17V4H19M12,2A1,1 0 0,1 13,3A1,1 0 0,1 12,4A1,1 0 0,1 11,3A1,1 0 0,1 12,2M19,2H14.82C14.4,0.84 13.3,0 12,0C10.7,0 9.6,0.84 9.18,2H5A2,2 0 0,0 3,4V20A2,2 0 0,0 5,22H19A2,2 0 0,0 21,20V4A2,2 0 0,0 19,2Z" /></svg>
|
After Width: | Height: | Size: 509 B |
1
assets/icons/redo.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M18.4,10.6C16.55,9 14.15,8 11.5,8C6.85,8 2.92,11.03 1.54,15.22L3.9,16C4.95,12.81 7.95,10.5 11.5,10.5C13.45,10.5 15.23,11.22 16.62,12.38L13,16H22V7L18.4,10.6Z" /></svg>
|
After Width: | Height: | Size: 452 B |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/></svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z" /></svg>
|
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 445 B |
1
assets/icons/template.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M14 2H6C4.89 2 4 2.9 4 4V20C4 21.11 4.89 22 6 22H18C19.11 22 20 21.11 20 20V8L14 2M18 20H6V4H13V9H18V20M9.54 15.65L11.63 17.74L10.35 19L7 15.65L10.35 12.3L11.63 13.56L9.54 15.65M17 15.65L13.65 19L12.38 17.74L14.47 15.65L12.38 13.56L13.65 12.3L17 15.65Z" /></svg>
|
After Width: | Height: | Size: 547 B |
1
assets/icons/undo.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12.5,8C9.85,8 7.45,9 5.6,10.6L2,7V16H11L7.38,12.38C8.77,11.22 10.54,10.5 12.5,10.5C16.04,10.5 19.05,12.81 20.1,16L22.47,15.22C21.08,11.03 17.15,8 12.5,8Z" /></svg>
|
After Width: | Height: | Size: 449 B |
@ -27,13 +27,13 @@ along with Foobar. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
## FFTReal
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
## KLed
|
||||
## KLed
|
||||
|
||||
Copyright © 1998 Jörg Habenicht <j.habenicht@europemail.com>
|
||||
|
||||
@ -52,6 +52,28 @@ License along with this library; if not, write to the
|
||||
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
|
||||
## QSourceHighlite
|
||||
|
||||
Copyright (c) 2019-2020 Waqar Ahmed -- <waqar.17a@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
## QMapControl
|
||||
|
||||
Copyright (C) 2007 - 2008 Kai Winter
|
||||
|
1
libs/Libraries.pri
vendored
@ -48,6 +48,7 @@ include($$PWD/qmqtt/qmqtt.pri)
|
||||
include($$PWD/QMapControl/QMapControl.pri)
|
||||
include($$PWD/QRealFourier/QRealFourier.pri)
|
||||
include($$PWD/QSimpleUpdater/QSimpleUpdater.pri)
|
||||
include($$PWD/QSourceHighlite/QSourceHighlite.pri)
|
||||
|
||||
macx* {
|
||||
DEFINES += KDMACTOUCHBAR_BUILD_KDMACTOUCHBAR_SRC
|
||||
|
2
libs/QSourceHighlite/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
build-*
|
||||
*.user
|
20
libs/QSourceHighlite/LICENSE
Executable file
@ -0,0 +1,20 @@
|
||||
Copyright (c) 2019-2020 Waqar Ahmed -- <waqar.17a@gmail.com>
|
||||
|
||||
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.
|
||||
|
11
libs/QSourceHighlite/QSourceHighlite.pri
Executable file
@ -0,0 +1,11 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/qsourcehighliter.h \
|
||||
$$PWD/qsourcehighliterthemes.h \
|
||||
$$PWD/languagedata.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/qsourcehighliter.cpp \
|
||||
$$PWD/languagedata.cpp \
|
||||
$$PWD/qsourcehighliterthemes.cpp
|
65
libs/QSourceHighlite/README.md
Executable file
@ -0,0 +1,65 @@
|
||||
# Background
|
||||
|
||||
It started as an internal component of [qmarkdowntextedit](https://github.com/pbek/qmarkdowntextedit) to provide syntax highlighting(kind of like **highlight.js**). Ithttps://github.com/pbek/qmarkdowntextedit is currently being used in QOwnNotes and you can test it there or run the demo here.
|
||||
|
||||
It doesn't use any regex because I want it to be fast. It can currently load a 100,000 lines of source code in ~0.4 seconds on my 4th Gen Intel Core i5 4300U.
|
||||
|
||||
# Screenshot
|
||||
|
||||
![Cpp](screenshot/syntax.png)
|
||||
|
||||
# Usage
|
||||
|
||||
Add the `.pri` file to your project. Then initialize it like this:
|
||||
```cpp
|
||||
highlighter = new QSourceHighliter(plainTextEdit->document());
|
||||
highlighter->setCurrentLanguage(QSourceHighlighter::CodeCpp);
|
||||
```
|
||||
|
||||
# Themes
|
||||
|
||||
Currently there is only one theme 'Monokai' apart from the one that is created during highlighter initialization. More themes will be added soon. You can add more themes in QSourceHighlighterThemes.
|
||||
|
||||
## Supported Languages
|
||||
|
||||
Currently the following languages are supported (more being added):
|
||||
- Bash script
|
||||
- C
|
||||
- C++
|
||||
- C#
|
||||
- CMake
|
||||
- CSS
|
||||
- Go
|
||||
- Html
|
||||
- INI
|
||||
- Java
|
||||
- Javascript
|
||||
- JSON
|
||||
- Make
|
||||
- PHP
|
||||
- Python
|
||||
- QML
|
||||
- Rust
|
||||
- SQL
|
||||
- Typescript
|
||||
- V lang
|
||||
- XML
|
||||
- YAML
|
||||
- Houdini Vex
|
||||
|
||||
## Adding more languages
|
||||
|
||||
If you want to add a language, collect the language data like keywords and types and add it to the `languagedata.h` file. For some languages it may not work, so create an issue and I will write a separate parser for that language.
|
||||
|
||||
## Dependencies
|
||||
|
||||
It has no dependency except Qt ofcourse. It should work with any Qt version > 5 but if it fails please create an issue.
|
||||
|
||||
## Building
|
||||
|
||||
Load the project into Qt Creator and click run.
|
||||
|
||||
## LICENSE
|
||||
|
||||
MIT License
|
||||
|
6324
libs/QSourceHighlite/languagedata.cpp
Executable file
222
libs/QSourceHighlite/languagedata.h
Executable file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 Waqar Ahmed -- <waqar.17a@gmail.com>
|
||||
*
|
||||
* 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
|
||||
|
||||
template<typename key, typename val>
|
||||
class QMultiHash;
|
||||
|
||||
class QLatin1String;
|
||||
|
||||
namespace QSourceHighlite {
|
||||
|
||||
using LanguageData = QMultiHash<char, QLatin1String>;
|
||||
|
||||
/**********************************************************/
|
||||
/* LuaData ************************************************/
|
||||
/**********************************************************/
|
||||
void loadLuaData(LanguageData &typess,
|
||||
LanguageData &keywordss,
|
||||
LanguageData &builtins,
|
||||
LanguageData &literalss,
|
||||
LanguageData &others);
|
||||
|
||||
/**********************************************************/
|
||||
/* C/C++ Data *********************************************/
|
||||
/**********************************************************/
|
||||
void loadCppData(LanguageData &typess,
|
||||
LanguageData &keywordss,
|
||||
LanguageData &builtins,
|
||||
LanguageData &literalss,
|
||||
LanguageData &others);
|
||||
|
||||
/**********************************************************/
|
||||
/* Shell Data *********************************************/
|
||||
/**********************************************************/
|
||||
void loadShellData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/**********************************************************/
|
||||
/* JS Data *********************************************/
|
||||
/**********************************************************/
|
||||
void loadJSData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/**********************************************************/
|
||||
/* PHP Data *********************************************/
|
||||
/**********************************************************/
|
||||
void loadPHPData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/**********************************************************/
|
||||
/* QML Data *********************************************/
|
||||
/**********************************************************/
|
||||
void loadQMLData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/**********************************************************/
|
||||
/* Python Data *********************************************/
|
||||
/**********************************************************/
|
||||
void loadPythonData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** Rust DATA ***********************************/
|
||||
/********************************************************/
|
||||
void loadRustData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** Java DATA ***********************************/
|
||||
/********************************************************/
|
||||
void loadJavaData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** C# DATA *************************************/
|
||||
/********************************************************/
|
||||
void loadCSharpData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** Go DATA *************************************/
|
||||
/********************************************************/
|
||||
void loadGoData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** V DATA **************************************/
|
||||
/********************************************************/
|
||||
void loadVData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** SQL DATA ************************************/
|
||||
/********************************************************/
|
||||
void loadSQLData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** JSON DATA ***********************************/
|
||||
/********************************************************/
|
||||
void loadJSONData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** CSS DATA ***********************************/
|
||||
/********************************************************/
|
||||
void loadCSSData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** Typescript DATA *********************************/
|
||||
/********************************************************/
|
||||
void loadTypescriptData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** YAML DATA ***************************************/
|
||||
/********************************************************/
|
||||
void loadYAMLData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** VEX DATA ***************************************/
|
||||
/********************************************************/
|
||||
void loadVEXData(LanguageData &types,
|
||||
LanguageData &keywords,
|
||||
LanguageData &builtin,
|
||||
LanguageData &literals,
|
||||
LanguageData &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** CMake DATA **************************************/
|
||||
/********************************************************/
|
||||
void loadCMakeData(QMultiHash<char, QLatin1String> &types,
|
||||
QMultiHash<char, QLatin1String> &keywords,
|
||||
QMultiHash<char, QLatin1String> &builtin,
|
||||
QMultiHash<char, QLatin1String> &literals,
|
||||
QMultiHash<char, QLatin1String> &other);
|
||||
|
||||
/********************************************************/
|
||||
/*** Make DATA ***************************************/
|
||||
/********************************************************/
|
||||
void loadMakeData(QMultiHash<char, QLatin1String>& types,
|
||||
QMultiHash<char, QLatin1String>& keywords,
|
||||
QMultiHash<char, QLatin1String>& builtin,
|
||||
QMultiHash<char, QLatin1String>& literals,
|
||||
QMultiHash<char, QLatin1String>& other);
|
||||
|
||||
void loadAsmData(QMultiHash<char, QLatin1String>& types,
|
||||
QMultiHash<char, QLatin1String>& keywords,
|
||||
QMultiHash<char, QLatin1String>& builtin,
|
||||
QMultiHash<char, QLatin1String>& literals,
|
||||
QMultiHash<char, QLatin1String>& other);
|
||||
}
|
||||
#endif
|
85
libs/QSourceHighlite/mainwindow.ui
Executable file
@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Language:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QComboBox" name="langComboBox">
|
||||
<property name="currentText">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Theme</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QComboBox" name="themeComboBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="plainTextEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
940
libs/QSourceHighlite/qsourcehighliter.cpp
Executable file
@ -0,0 +1,940 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 Waqar Ahmed -- <waqar.17a@gmail.com>
|
||||
*
|
||||
* 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 "qsourcehighliter.h"
|
||||
#include "languagedata.h"
|
||||
#include "qsourcehighliterthemes.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <algorithm>
|
||||
#include <QTextDocument>
|
||||
|
||||
namespace QSourceHighlite {
|
||||
|
||||
QSourceHighliter::QSourceHighliter(QTextDocument *doc)
|
||||
: QSyntaxHighlighter(doc),
|
||||
_language(CodeC)
|
||||
{
|
||||
initFormats();
|
||||
}
|
||||
|
||||
QSourceHighliter::QSourceHighliter(QTextDocument *doc, QSourceHighliter::Themes theme)
|
||||
: QSyntaxHighlighter(doc),
|
||||
_language(CodeC)
|
||||
{
|
||||
setTheme(theme);
|
||||
}
|
||||
|
||||
void QSourceHighliter::initFormats() {
|
||||
/****************************************
|
||||
* Formats for syntax highlighting
|
||||
***************************************/
|
||||
|
||||
QTextCharFormat format = QTextCharFormat();
|
||||
|
||||
_formats[Token::CodeBlock] = format;
|
||||
format = QTextCharFormat();
|
||||
|
||||
format.setForeground(QColor("#F92672"));
|
||||
_formats[Token::CodeKeyWord] = format;
|
||||
format = QTextCharFormat();
|
||||
|
||||
format.setForeground(QColor("#a39b4e"));
|
||||
_formats[Token::CodeString] = format;
|
||||
format = QTextCharFormat();
|
||||
|
||||
format.setForeground(QColor("#75715E"));
|
||||
_formats[Token::CodeComment] = format;
|
||||
format = QTextCharFormat();
|
||||
|
||||
format.setForeground(QColor("#54aebf"));
|
||||
_formats[Token::CodeType] = format;
|
||||
|
||||
format = QTextCharFormat();
|
||||
format.setForeground(QColor("#db8744"));
|
||||
_formats[Token::CodeOther] = format;
|
||||
|
||||
format = QTextCharFormat();
|
||||
format.setForeground(QColor("#AE81FF"));
|
||||
_formats[Token::CodeNumLiteral] = format;
|
||||
|
||||
format = QTextCharFormat();
|
||||
format.setForeground(QColor("#018a0f"));
|
||||
_formats[Token::CodeBuiltIn] = format;
|
||||
}
|
||||
|
||||
void QSourceHighliter::setCurrentLanguage(Language language) {
|
||||
if (language != _language)
|
||||
_language = language;
|
||||
}
|
||||
|
||||
QSourceHighliter::Language QSourceHighliter::currentLanguage() {
|
||||
return _language;
|
||||
}
|
||||
|
||||
void QSourceHighliter::setTheme(QSourceHighliter::Themes theme)
|
||||
{
|
||||
_formats = QSourceHighliterTheme::theme(theme);
|
||||
rehighlight();
|
||||
}
|
||||
|
||||
void QSourceHighliter::highlightBlock(const QString &text)
|
||||
{
|
||||
if (currentBlock() == document()->firstBlock()) {
|
||||
setCurrentBlockState(_language);
|
||||
} else {
|
||||
previousBlockState() == _language ?
|
||||
setCurrentBlockState(_language) :
|
||||
setCurrentBlockState(_language + 1);
|
||||
}
|
||||
|
||||
highlightSyntax(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Does the code syntax highlighting
|
||||
* @param text
|
||||
*/
|
||||
void QSourceHighliter::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 isAsm = false;
|
||||
bool isSQL = false;
|
||||
|
||||
LanguageData keywords{},
|
||||
others{},
|
||||
types{},
|
||||
builtin{},
|
||||
literals{};
|
||||
|
||||
switch (currentBlockState()) {
|
||||
case CodeLua :
|
||||
case CodeLuaComment :
|
||||
loadLuaData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeCpp :
|
||||
case CodeCppComment :
|
||||
loadCppData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeJs :
|
||||
case CodeJsComment :
|
||||
loadJSData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeC :
|
||||
case CodeCComment :
|
||||
loadCppData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeBash :
|
||||
loadShellData(types, keywords, builtin, literals, others);
|
||||
comment = QLatin1Char('#');
|
||||
break;
|
||||
case CodePHP :
|
||||
case CodePHPComment :
|
||||
loadPHPData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeQML :
|
||||
case CodeQMLComment :
|
||||
loadQMLData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodePython :
|
||||
loadPythonData(types, keywords, builtin, literals, others);
|
||||
comment = QLatin1Char('#');
|
||||
break;
|
||||
case CodeRust :
|
||||
case CodeRustComment :
|
||||
loadRustData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeJava :
|
||||
case CodeJavaComment :
|
||||
loadJavaData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeCSharp :
|
||||
case CodeCSharpComment :
|
||||
loadCSharpData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeGo :
|
||||
case CodeGoComment :
|
||||
loadGoData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeV :
|
||||
case CodeVComment :
|
||||
loadVData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeSQL :
|
||||
isSQL = true;
|
||||
loadSQLData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeJSON :
|
||||
loadJSONData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeXML :
|
||||
xmlHighlighter(text);
|
||||
return;
|
||||
case CodeCSS :
|
||||
case CodeCSSComment :
|
||||
isCSS = true;
|
||||
loadCSSData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeTypeScript:
|
||||
case CodeTypeScriptComment:
|
||||
loadTypescriptData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeYAML:
|
||||
isYAML = true;
|
||||
loadYAMLData(types, keywords, builtin, literals, others);
|
||||
comment = QLatin1Char('#');
|
||||
break;
|
||||
case CodeINI:
|
||||
comment = QLatin1Char('#');
|
||||
break;
|
||||
case CodeVex:
|
||||
case CodeVexComment:
|
||||
loadVEXData(types, keywords, builtin, literals, others);
|
||||
break;
|
||||
case CodeCMake:
|
||||
loadCMakeData(types, keywords, builtin, literals, others);
|
||||
comment = QLatin1Char('#');
|
||||
break;
|
||||
case CodeMake:
|
||||
isMake = true;
|
||||
loadMakeData(types, keywords, builtin, literals, others);
|
||||
comment = QLatin1Char('#');
|
||||
break;
|
||||
case CodeAsm:
|
||||
isAsm = true;
|
||||
loadAsmData(types, keywords, builtin, literals, others);
|
||||
comment = QLatin1Char('#');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// keep the default code block format
|
||||
// this statement is very slow
|
||||
// TODO: do this formatting when necessary instead of
|
||||
// applying it to the whole block in the beginning
|
||||
setFormat(0, textLen, _formats[CodeBlock]);
|
||||
|
||||
auto applyCodeFormat =
|
||||
[this](int i, const LanguageData &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 auto wordList = data.values(text.at(i).toLatin1());
|
||||
for (const QLatin1String &word : wordList) {
|
||||
// we have a word match check
|
||||
// 1. if we are at the end
|
||||
// 2. if we have a complete word
|
||||
if (word == strMidRef(text, 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() % 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) return;
|
||||
if (text[i].isLetter()) break;
|
||||
else 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("*/"));
|
||||
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 (isSQL && comment.isNull() && text[i] == QLatin1Char('-')) {
|
||||
if((i+1) < textLen){
|
||||
if(text[i+1] == QLatin1Char('-')) {
|
||||
setFormat(i, textLen, formatComment);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (text[i] == comment) {
|
||||
setFormat(i, textLen, formatComment);
|
||||
i = textLen;
|
||||
//integer literal
|
||||
} else if (text[i].isNumber()) {
|
||||
i = highlightNumericLiterals(text, i);
|
||||
//string literals
|
||||
} else if (text[i] == QLatin1Char('\"')) {
|
||||
i = highlightStringLiterals('\"', text, i);
|
||||
} else if (text[i] == QLatin1Char('\'')) {
|
||||
i = highlightStringLiterals('\'', 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;
|
||||
We can ++i here and go to the beginning of the next word
|
||||
so that the next formatter can check for formatting but this will
|
||||
cause problems in case the next word is also of 'Type' or the current
|
||||
type(keyword/builtin). We can work around it and reset the value of i
|
||||
in the beginning of the loop to the word's first letter but I am not
|
||||
sure about its efficiency yet.
|
||||
************************************************/
|
||||
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()) && others.contains(text[i].toLatin1())) {
|
||||
const QList<QLatin1String> wordList = others.values(text[i].toLatin1());
|
||||
for(const QLatin1String &word : wordList) {
|
||||
if (word == strMidRef(text, i, word.size()) // we have a word match
|
||||
&&
|
||||
(i + word.size() == text.length() // check if we are at the end
|
||||
||
|
||||
!text.at(i + word.size()).isLetter()) //OR if we have a complete word
|
||||
) {
|
||||
currentBlockState() == CodeCpp ?
|
||||
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 count = i;
|
||||
while (count < textLen) {
|
||||
if (!text[count].isLetter()) break;
|
||||
++count;
|
||||
}
|
||||
i = count;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCSS) cssHighlighter(text);
|
||||
if (isYAML) ymlHighlighter(text);
|
||||
if (isMake) makeHighlighter(text);
|
||||
if (isAsm) asmHighlighter(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 QSourceHighliter::highlightStringLiterals(const QChar strType, const QString &text, int i) {
|
||||
setFormat(i, 1, _formats[CodeString]);
|
||||
++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, _formats[CodeString]);
|
||||
++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()) {
|
||||
bool isCurrentOctal = true;
|
||||
if (!isOctal(text.at(i+2).toLatin1())) {
|
||||
isCurrentOctal = false;
|
||||
break;
|
||||
}
|
||||
if (!isOctal(text.at(i+3).toLatin1())) {
|
||||
isCurrentOctal = false;
|
||||
break;
|
||||
}
|
||||
len = isCurrentOctal ? 4 : 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
//hex numbers \xFA
|
||||
case 'x':
|
||||
{
|
||||
if (i + 3 <= text.length()) {
|
||||
bool isCurrentHex = true;
|
||||
if (!isHex(text.at(i+2).toLatin1())) {
|
||||
isCurrentHex = false;
|
||||
break;
|
||||
}
|
||||
if (!isHex(text.at(i+3).toLatin1())) {
|
||||
isCurrentHex = false;
|
||||
break;
|
||||
}
|
||||
len = isCurrentHex ? 4 : 0;
|
||||
}
|
||||
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, _formats[CodeString]);
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
setFormat(i, len, _formats[CodeNumLiteral]);
|
||||
i += len;
|
||||
continue;
|
||||
}
|
||||
setFormat(i, 1, _formats[CodeString]);
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Highlight number literals in code
|
||||
* @param text the text being scanned
|
||||
* @param i pos of i in loop
|
||||
* @return pos of i after the number
|
||||
*/
|
||||
int QSourceHighliter::highlightNumericLiterals(const QString &text, int i)
|
||||
{
|
||||
bool isPreAllowed = false;
|
||||
if (i == 0) isPreAllowed = true;
|
||||
else {
|
||||
//these values are allowed before a number
|
||||
switch(text.at(i - 1).toLatin1()) {
|
||||
//css number
|
||||
case ':':
|
||||
if (currentBlockState() == CodeCSS)
|
||||
isPreAllowed = true;
|
||||
break;
|
||||
case '$':
|
||||
if (currentBlockState() == CodeAsm)
|
||||
isPreAllowed = true;
|
||||
break;
|
||||
case '[':
|
||||
case '(':
|
||||
case '{':
|
||||
case ' ':
|
||||
case ',':
|
||||
case '=':
|
||||
case '+':
|
||||
case '-':
|
||||
case '*':
|
||||
case '/':
|
||||
case '%':
|
||||
case '<':
|
||||
case '>':
|
||||
isPreAllowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPreAllowed) 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)
|
||||
if (text.at(i) == QChar('x') && text.at(i - 1) == QChar('0'))
|
||||
++i;
|
||||
|
||||
while (i < text.length()) {
|
||||
if (!text.at(i).isNumber() && text.at(i) != QChar('.') &&
|
||||
text.at(i) != QChar('e')) //exponent
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
|
||||
bool isPostAllowed = false;
|
||||
if (i == text.length()) {
|
||||
//cant have e at the end
|
||||
if (text.at(i - 1) != QChar('e'))
|
||||
isPostAllowed = 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 ';':
|
||||
isPostAllowed = 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())
|
||||
isPostAllowed = 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())
|
||||
isPostAllowed = true;
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
case 'l':
|
||||
case 'f':
|
||||
case 'U':
|
||||
case 'L':
|
||||
case 'F':
|
||||
if (i + 1 == text.length() || !text.at(i+1).isLetterOrNumber()) {
|
||||
isPostAllowed = true;
|
||||
++i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isPostAllowed) {
|
||||
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 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 QSourceHighliter::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) == QLatin1Char('#'))
|
||||
return;
|
||||
|
||||
for (int i = 0; i < textLen; ++i) {
|
||||
if (!text.at(i).isLetter()) continue;
|
||||
|
||||
if (colonNotFound && text.at(i) != QLatin1Char('h')) continue;
|
||||
|
||||
//we found a string literal, skip it
|
||||
if (i != 0 && (text.at(i-1) == QLatin1Char('"') || text.at(i-1) == QLatin1Char('\''))) {
|
||||
const int next = text.indexOf(text.at(i-1), i);
|
||||
if (next == -1) break;
|
||||
i = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int colon = text.indexOf(QLatin1Char(':'), 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;
|
||||
} else {
|
||||
//colon is found, check if it isn't some path or something else
|
||||
if (!(text.at(colon+1) == QLatin1Char('\\') && text.at(colon+1) == QLatin1Char('/'))) {
|
||||
setFormat(i, colon - i, _formats[CodeKeyWord]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//underlined links
|
||||
if (text.at(i) == QLatin1Char('h')) {
|
||||
if (strMidRef(text, i, 5) == QLatin1String("https") ||
|
||||
strMidRef(text, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QSourceHighliter::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('{');
|
||||
if (space < 0) {
|
||||
space = textLen;
|
||||
}
|
||||
}
|
||||
setFormat(i, space - i, _formats[CodeKeyWord]);
|
||||
i = space;
|
||||
} else if (text[i] == QLatin1Char('c')) {
|
||||
if (strMidRef(text, i, 5) == QLatin1String("color")) {
|
||||
i += 5;
|
||||
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(';'));
|
||||
if (semicolon < 0) semicolon = textLen;
|
||||
const QString color = text.mid(i, semicolon-i);
|
||||
QTextCharFormat f = _formats[CodeBlock];
|
||||
QColor c(color);
|
||||
if (color.startsWith(QLatin1String("rgb"))) {
|
||||
int t = text.indexOf(QLatin1Char('('), i);
|
||||
int rPos = text.indexOf(QLatin1Char(','), t);
|
||||
int gPos = text.indexOf(QLatin1Char(','), rPos+1);
|
||||
int bPos = text.indexOf(QLatin1Char(')'), gPos);
|
||||
if (rPos > -1 && gPos > -1 && bPos > -1) {
|
||||
const auto r = strMidRef(text, t+1, rPos - (t+1));
|
||||
const auto g = strMidRef(text, rPos+1, gPos - (rPos + 1));
|
||||
const auto b = strMidRef(text, gPos+1, bPos - (gPos+1));
|
||||
c.setRgb(r.toInt(), g.toInt(), b.toInt());
|
||||
} else {
|
||||
c = _formats[CodeBlock].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("#ccc");
|
||||
} else if (c.lightness() > 51 && c.lightness() <= 78){
|
||||
foreground = QColor("#bbb");
|
||||
} else if (c.lightness() > 78 && c.lightness() <= 110){
|
||||
foreground = QColor("#bbb");
|
||||
} else if (c.lightness() > 127) {
|
||||
lightness = c.lightness() + 100;
|
||||
foreground = c.darker(lightness);
|
||||
}
|
||||
else {
|
||||
lightness = c.lightness() + 100;
|
||||
foreground = c.lighter(lightness);
|
||||
}
|
||||
|
||||
f.setBackground(c);
|
||||
f.setForeground(foreground);
|
||||
setFormat(i, semicolon - i, QTextCharFormat()); //clear prev format
|
||||
setFormat(i, semicolon - i, f);
|
||||
i = semicolon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QSourceHighliter::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 (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 QSourceHighliter::makeHighlighter(const QString &text)
|
||||
{
|
||||
int colonPos = text.indexOf(QLatin1Char(':'));
|
||||
if (colonPos == -1)
|
||||
return;
|
||||
setFormat(0, colonPos, _formats[Token::CodeBuiltIn]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief highlight inline labels such as 'func()' in "call func()"
|
||||
* @param text
|
||||
*/
|
||||
void QSourceHighliter::highlightInlineAsmLabels(const QString &text)
|
||||
{
|
||||
#define Q(s) QStringLiteral(s)
|
||||
static const QString jumps[27] = {
|
||||
//0 - 19
|
||||
Q("jmp"), Q("je"), Q("jne"), Q("jz"), Q("jnz"), Q("ja"), Q("jb"), Q("jg"), Q("jge"), Q("jae"), Q("jl"), Q("jle"),
|
||||
Q("jbe"), Q("jo"), Q("jno"), Q("js"), Q("jns"), Q("jcxz"), Q("jecxz"), Q("jrcxz"),
|
||||
//20 - 24
|
||||
Q("loop"), Q("loope"), Q("loopne"), Q("loopz"), Q("loopnz"),
|
||||
//25 - 26
|
||||
Q("call"), Q("callq")
|
||||
};
|
||||
#undef Q
|
||||
|
||||
auto format = _formats[Token::CodeBuiltIn];
|
||||
format.setFontUnderline(true);
|
||||
|
||||
const QString trimmed = text.trimmed();
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
char c{};
|
||||
if (!trimmed.isEmpty())
|
||||
c = trimmed.at(0).toLatin1();
|
||||
if (c == 'j') {
|
||||
start = 0; end = 20;
|
||||
} else if (c == 'c') {
|
||||
start = 25; end = 27;
|
||||
} else if (c == 'l') {
|
||||
start = 20; end = 25;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
auto skipSpaces = [&text](int& j){
|
||||
while (text.at(j).isSpace()) j++;
|
||||
return j;
|
||||
};
|
||||
|
||||
for (int i = start; i < end; ++i) {
|
||||
if (trimmed.startsWith(jumps[i])) {
|
||||
int j = 0;
|
||||
skipSpaces(j);
|
||||
j = j + jumps[i].length() + 1;
|
||||
skipSpaces(j);
|
||||
int len = text.length() - j;
|
||||
setFormat(j, len, format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QSourceHighliter::asmHighlighter(const QString& text)
|
||||
{
|
||||
highlightInlineAsmLabels(text);
|
||||
//label highlighting
|
||||
//examples:
|
||||
//L1:
|
||||
//LFB1: # local func begin
|
||||
//
|
||||
//following e.gs are not a label
|
||||
//mov %eax, Count::count(%rip)
|
||||
//.string ": #%s"
|
||||
|
||||
//look for the last occurence of a colon
|
||||
int colonPos = text.lastIndexOf(QLatin1Char(':'));
|
||||
if (colonPos == -1)
|
||||
return;
|
||||
//check if this colon is in a comment maybe?
|
||||
bool isComment = text.lastIndexOf('#', colonPos) != -1;
|
||||
if (isComment) {
|
||||
int commentPos = text.lastIndexOf('#', colonPos);
|
||||
colonPos = text.lastIndexOf(':', commentPos);
|
||||
}
|
||||
|
||||
auto format = _formats[Token::CodeBuiltIn];
|
||||
format.setFontUnderline(true);
|
||||
|
||||
if (colonPos >= text.length() - 1) {
|
||||
setFormat(0, colonPos, format);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
bool isLabel = true;
|
||||
for (i = colonPos + 1; i < text.length(); ++i) {
|
||||
if (!text.at(i).isSpace()) {
|
||||
isLabel = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isLabel && i < text.length() && text.at(i) == QLatin1Char('#'))
|
||||
setFormat(0, colonPos, format);
|
||||
}
|
||||
}
|
169
libs/QSourceHighlite/qsourcehighliter.h
Executable file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 Waqar Ahmed -- <waqar.17a@gmail.com>
|
||||
*
|
||||
* 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 QSOURCEHIGHLITER_H
|
||||
#define QSOURCEHIGHLITER_H
|
||||
|
||||
#include <QSyntaxHighlighter>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
#include <QStringView>
|
||||
#endif
|
||||
|
||||
namespace QSourceHighlite {
|
||||
|
||||
class QSourceHighliter : public QSyntaxHighlighter
|
||||
{
|
||||
public:
|
||||
enum Themes {
|
||||
Monokai = 1
|
||||
};
|
||||
|
||||
explicit QSourceHighliter(QTextDocument *doc);
|
||||
QSourceHighliter(QTextDocument *doc, Themes theme);
|
||||
|
||||
//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
|
||||
*/
|
||||
enum Language {
|
||||
//languages
|
||||
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,
|
||||
CodeVex = 238,
|
||||
CodeVexComment = 239,
|
||||
CodeCMake = 240,
|
||||
CodeMake = 242,
|
||||
CodeAsm = 244,
|
||||
CodeLua = 246,
|
||||
CodeLuaComment = 247
|
||||
};
|
||||
Q_ENUM(Language)
|
||||
|
||||
enum Token {
|
||||
CodeBlock,
|
||||
CodeKeyWord,
|
||||
CodeString,
|
||||
CodeComment,
|
||||
CodeType,
|
||||
CodeOther,
|
||||
CodeNumLiteral,
|
||||
CodeBuiltIn,
|
||||
};
|
||||
Q_ENUM(Token)
|
||||
|
||||
void setCurrentLanguage(Language language);
|
||||
Q_REQUIRED_RESULT Language currentLanguage();
|
||||
void setTheme(Themes theme);
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString &text) override;
|
||||
|
||||
private:
|
||||
void highlightSyntax(const QString &text);
|
||||
Q_REQUIRED_RESULT int highlightNumericLiterals(const QString &text, int i);
|
||||
Q_REQUIRED_RESULT int highlightStringLiterals(const QChar strType, const QString &text, int i);
|
||||
|
||||
/**
|
||||
* @brief returns true if c is octal
|
||||
* @param c the char being checked
|
||||
* @returns true if the number is octal, false otherwise
|
||||
*/
|
||||
Q_REQUIRED_RESULT static constexpr inline bool isOctal(const char c) {
|
||||
return (c >= '0' && c <= '7');
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns true if c is hex
|
||||
* @param c the char being checked
|
||||
* @returns true if the number is hex, false otherwise
|
||||
*/
|
||||
Q_REQUIRED_RESULT static constexpr inline bool isHex(const char c) {
|
||||
return (
|
||||
(c >= '0' && c <= '9') ||
|
||||
(c >= 'a' && c <= 'f') ||
|
||||
(c >= 'A' && c <= 'F')
|
||||
);
|
||||
}
|
||||
|
||||
void cssHighlighter(const QString &text);
|
||||
void ymlHighlighter(const QString &text);
|
||||
void xmlHighlighter(const QString &text);
|
||||
void makeHighlighter(const QString &text);
|
||||
void highlightInlineAsmLabels(const QString& text);
|
||||
void asmHighlighter(const QString& text);
|
||||
void initFormats();
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
static inline QStringView strMidRef(const QString& str, qsizetype position, qsizetype n = -1)
|
||||
{
|
||||
return QStringView(str).mid(position, n);
|
||||
}
|
||||
#else
|
||||
static inline QStringRef strMidRef(const QString& str, int position, int n = -1)
|
||||
{
|
||||
return str.midRef(position, n);
|
||||
}
|
||||
#endif
|
||||
|
||||
QHash<Token, QTextCharFormat> _formats;
|
||||
Language _language;
|
||||
};
|
||||
}
|
||||
#endif // QSOURCEHIGHLITER_H
|
72
libs/QSourceHighlite/qsourcehighliterthemes.cpp
Executable file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 Waqar Ahmed -- <waqar.17a@gmail.com>
|
||||
*
|
||||
* 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 "qsourcehighliterthemes.h"
|
||||
|
||||
namespace QSourceHighlite {
|
||||
|
||||
static QHash<QSourceHighliter::Token, QTextCharFormat> formats()
|
||||
{
|
||||
QHash<QSourceHighliter::Token, QTextCharFormat> _formats;
|
||||
|
||||
QTextCharFormat defaultFormat = QTextCharFormat();
|
||||
|
||||
_formats[QSourceHighliter::Token::CodeBlock] = defaultFormat;
|
||||
_formats[QSourceHighliter::Token::CodeKeyWord] = defaultFormat;
|
||||
_formats[QSourceHighliter::Token::CodeString] = defaultFormat;
|
||||
_formats[QSourceHighliter::Token::CodeComment] = defaultFormat;
|
||||
_formats[QSourceHighliter::Token::CodeType] = defaultFormat;
|
||||
_formats[QSourceHighliter::Token::CodeOther] = defaultFormat;
|
||||
_formats[QSourceHighliter::Token::CodeNumLiteral] = defaultFormat;
|
||||
_formats[QSourceHighliter::Token::CodeBuiltIn] = defaultFormat;
|
||||
|
||||
return _formats;
|
||||
}
|
||||
|
||||
static QHash<QSourceHighliter::Token, QTextCharFormat> monokai()
|
||||
{
|
||||
QHash<QSourceHighliter::Token, QTextCharFormat> _formats = formats();
|
||||
|
||||
_formats[QSourceHighliter::Token::CodeBlock].setForeground(QColor(227, 226, 214));
|
||||
_formats[QSourceHighliter::Token::CodeKeyWord].setForeground(QColor(249, 38, 114));
|
||||
_formats[QSourceHighliter::Token::CodeString].setForeground(QColor(230, 219, 116));
|
||||
_formats[QSourceHighliter::Token::CodeComment].setForeground(QColor(117, 113, 94));
|
||||
_formats[QSourceHighliter::Token::CodeType].setForeground(QColor(102, 217, 239));
|
||||
_formats[QSourceHighliter::Token::CodeOther].setForeground(QColor(249, 38, 114));
|
||||
_formats[QSourceHighliter::Token::CodeNumLiteral].setForeground(QColor(174, 129, 255));
|
||||
_formats[QSourceHighliter::Token::CodeBuiltIn].setForeground(QColor(166, 226, 46));
|
||||
|
||||
return _formats;
|
||||
}
|
||||
|
||||
QHash<QSourceHighliter::Token, QTextCharFormat>
|
||||
QSourceHighliterTheme::theme(QSourceHighliter::Themes theme) {
|
||||
switch (theme) {
|
||||
case QSourceHighliter::Themes::Monokai:
|
||||
return monokai();
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
38
libs/QSourceHighlite/qsourcehighliterthemes.h
Executable file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 Waqar Ahmed -- <waqar.17a@gmail.com>
|
||||
*
|
||||
* 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 QSOURCEHIGHLITERTHEMES_H
|
||||
#define QSOURCEHIGHLITERTHEMES_H
|
||||
|
||||
#include "qsourcehighliter.h"
|
||||
|
||||
namespace QSourceHighlite {
|
||||
|
||||
namespace QSourceHighliterTheme
|
||||
{
|
||||
QHash<QSourceHighliter::Token, QTextCharFormat> theme(QSourceHighliter::Themes);
|
||||
|
||||
} // namespace QSourceHighliterTheme
|
||||
|
||||
} // namespace QSourceHighlite
|
||||
#endif // QSOURCEHIGHLITERTHEMES_H
|
@ -22,6 +22,12 @@
|
||||
|
||||
#include "CodeEditor.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QJSEngine>
|
||||
#include <QFileDialog>
|
||||
#include <Misc/Utilities.h>
|
||||
#include <Misc/ThemeManager.h>
|
||||
|
||||
static const QString DEFAULT_CODE
|
||||
= "/* \n"
|
||||
" * Frame parsing function, you can modify this to suit your\n"
|
||||
@ -42,15 +48,67 @@ static const QString DEFAULT_CODE
|
||||
|
||||
Project::CodeEditor::CodeEditor()
|
||||
{
|
||||
setWindowTitle(tr("Frame parser code"));
|
||||
m_textEdit.setPlainText(DEFAULT_CODE);
|
||||
// Setup syntax highlighter
|
||||
m_highlighter = new QSourceHighlite::QSourceHighliter(m_textEdit.document());
|
||||
m_highlighter->setCurrentLanguage(QSourceHighlite::QSourceHighliter::CodeJs);
|
||||
|
||||
// Setup text editor
|
||||
onNewClicked();
|
||||
m_textEdit.setFont(QFont("Roboto Mono"));
|
||||
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->addWidget(&m_textEdit);
|
||||
// Setup toolbar
|
||||
auto acNew = m_toolbar.addAction(QIcon(":/icons/template.svg"), tr("New"));
|
||||
auto acOpen = m_toolbar.addAction(QIcon(":/icons/open.svg"), tr("Open"));
|
||||
auto acSave = m_toolbar.addAction(QIcon(":/icons/save.svg"), tr("Save"));
|
||||
m_toolbar.addSeparator();
|
||||
auto acUndo = m_toolbar.addAction(QIcon(":/icons/undo.svg"), tr("Undo"));
|
||||
auto acRedo = m_toolbar.addAction(QIcon(":/icons/redo.svg"), tr("Redo"));
|
||||
m_toolbar.addSeparator();
|
||||
auto acCut = m_toolbar.addAction(QIcon(":/icons/cut.svg"), tr("Cut"));
|
||||
auto acCopy = m_toolbar.addAction(QIcon(":/icons/copy.svg"), tr("Copy"));
|
||||
auto acPaste = m_toolbar.addAction(QIcon(":/icons/paste.svg"), tr("Paste"));
|
||||
m_toolbar.addSeparator();
|
||||
auto acHelp = m_toolbar.addAction(QIcon(":/icons/help.svg"), tr("Help"));
|
||||
|
||||
// Connect action signals/slots
|
||||
connect(acUndo, &QAction::triggered, &m_textEdit, &QPlainTextEdit::undo);
|
||||
connect(acRedo, &QAction::triggered, &m_textEdit, &QPlainTextEdit::redo);
|
||||
connect(acCut, &QAction::triggered, &m_textEdit, &QPlainTextEdit::cut);
|
||||
connect(acCopy, &QAction::triggered, &m_textEdit, &QPlainTextEdit::copy);
|
||||
connect(acPaste, &QAction::triggered, &m_textEdit, &QPlainTextEdit::paste);
|
||||
connect(acNew, &QAction::triggered, this, &Project::CodeEditor::onNewClicked);
|
||||
connect(acNew, &QAction::triggered, this, &Project::CodeEditor::onNewClicked);
|
||||
connect(acOpen, &QAction::triggered, this, &Project::CodeEditor::onOpenClicked);
|
||||
connect(acSave, &QAction::triggered, this, &Project::CodeEditor::onSaveClicked);
|
||||
connect(acHelp, &QAction::triggered, this, &Project::CodeEditor::onHelpClicked);
|
||||
|
||||
// Set widget palette
|
||||
QPalette palette;
|
||||
auto theme = &Misc::ThemeManager::instance();
|
||||
palette.setColor(QPalette::Text, theme->consoleText());
|
||||
palette.setColor(QPalette::Base, theme->consoleBase());
|
||||
palette.setColor(QPalette::Button, theme->consoleButton());
|
||||
palette.setColor(QPalette::Window, theme->consoleWindow());
|
||||
palette.setColor(QPalette::Highlight, theme->consoleHighlight());
|
||||
palette.setColor(QPalette::HighlightedText, theme->consoleHighlightedText());
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
palette.setColor(QPalette::PlaceholderText, theme->consolePlaceholderText());
|
||||
#endif
|
||||
m_textEdit.setPalette(palette);
|
||||
|
||||
// Setup layout
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->addWidget(&m_toolbar);
|
||||
layout->addWidget(&m_textEdit);
|
||||
layout->setStretch(0, 0);
|
||||
layout->setStretch(1, 1);
|
||||
layout->setSpacing(0);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
|
||||
// Setup window
|
||||
setMinimumSize(QSize(640, 480));
|
||||
setWindowTitle(tr("Customize frame parser"));
|
||||
}
|
||||
|
||||
Project::CodeEditor::~CodeEditor() { }
|
||||
@ -70,3 +128,155 @@ void Project::CodeEditor::displayWindow()
|
||||
{
|
||||
showNormal();
|
||||
}
|
||||
|
||||
void Project::CodeEditor::onNewClicked()
|
||||
{
|
||||
// Document has been modified, ask user if he/she wants to continue
|
||||
if (m_textEdit.document()->isModified())
|
||||
{
|
||||
auto ret = Misc::Utilities::showMessageBox(
|
||||
tr("The document has been modified!"),
|
||||
tr("Are you sure you want to continue?"), qAppName(),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
if (ret == QMessageBox::No)
|
||||
return;
|
||||
}
|
||||
|
||||
// Load default template
|
||||
m_textEdit.setPlainText(DEFAULT_CODE);
|
||||
save(true);
|
||||
}
|
||||
|
||||
void Project::CodeEditor::onOpenClicked()
|
||||
{
|
||||
// Document has been modified, ask user if he/she wants to continue
|
||||
if (m_textEdit.document()->isModified())
|
||||
{
|
||||
auto ret = Misc::Utilities::showMessageBox(
|
||||
tr("The document has been modified!"),
|
||||
tr("Are you sure you want to continue?"), qAppName(),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
if (ret == QMessageBox::No)
|
||||
return;
|
||||
}
|
||||
|
||||
// Get file from system
|
||||
auto path = QFileDialog::getOpenFileName(
|
||||
Q_NULLPTR, tr("Select Javascript file to import"), QDir::homePath(), "*.js");
|
||||
|
||||
// Load file into code editor
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
QFile file(path);
|
||||
if (file.open(QFile::ReadOnly))
|
||||
{
|
||||
auto data = file.readAll();
|
||||
m_textEdit.setPlainText(QString::fromUtf8(data));
|
||||
save(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Project::CodeEditor::onSaveClicked()
|
||||
{
|
||||
if (save(false))
|
||||
close();
|
||||
}
|
||||
|
||||
void Project::CodeEditor::onHelpClicked() { }
|
||||
|
||||
bool Project::CodeEditor::save(const bool silent)
|
||||
{
|
||||
// Validate code
|
||||
QJSEngine engine;
|
||||
QStringList errors;
|
||||
QJSValueList args = { "", "," };
|
||||
auto fun = engine.evaluate("(" + m_textEdit.toPlainText() + ")", "", 1, &errors);
|
||||
auto ret = fun.call(args);
|
||||
|
||||
// Error on engine evaluation
|
||||
if (!errors.isEmpty())
|
||||
{
|
||||
Misc::Utilities::showMessageBox(tr("Frame parser syntax error!"),
|
||||
tr("Error on line %1.").arg(errors.first()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Error on function execution
|
||||
else if (ret.isError())
|
||||
{
|
||||
QString errorStr;
|
||||
switch (ret.errorType())
|
||||
{
|
||||
case QJSValue::GenericError:
|
||||
errorStr = tr("Generic error");
|
||||
break;
|
||||
case QJSValue::EvalError:
|
||||
errorStr = tr("Evaluation error");
|
||||
break;
|
||||
case QJSValue::RangeError:
|
||||
errorStr = tr("Range error");
|
||||
break;
|
||||
case QJSValue::ReferenceError:
|
||||
errorStr = tr("Reference error");
|
||||
break;
|
||||
case QJSValue::SyntaxError:
|
||||
errorStr = tr("Syntax error");
|
||||
break;
|
||||
case QJSValue::TypeError:
|
||||
errorStr = tr("Type error");
|
||||
break;
|
||||
case QJSValue::URIError:
|
||||
errorStr = tr("URI error");
|
||||
break;
|
||||
default:
|
||||
errorStr = tr("Unknown error");
|
||||
break;
|
||||
}
|
||||
|
||||
Misc::Utilities::showMessageBox(tr("Frame parser error detected!"), errorStr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update text edit
|
||||
m_textEdit.document()->setModified(false);
|
||||
|
||||
// Show save messagebox
|
||||
if (!silent)
|
||||
Misc::Utilities::showMessageBox(tr("Frame parser code updated successfully!"),
|
||||
tr("No errors have been detected in the code."));
|
||||
|
||||
// Everything good
|
||||
return true;
|
||||
}
|
||||
|
||||
void Project::CodeEditor::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
if (m_textEdit.document()->isModified())
|
||||
{
|
||||
// Ask user if he/she wants to save changes
|
||||
auto ret = Misc::Utilities::showMessageBox(
|
||||
tr("The document has been modified!"), tr("Do you want to save the changes?"),
|
||||
qAppName(), QMessageBox::Yes | QMessageBox::Discard | QMessageBox::Cancel);
|
||||
|
||||
// User wants to save changes, validate code & apply
|
||||
if (ret == QMessageBox::Yes)
|
||||
{
|
||||
if (!save(true))
|
||||
{
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// User wants to continue editing the code
|
||||
if (ret == QMessageBox::Cancel)
|
||||
{
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// User saved changes (with no errors) or discarded the changes
|
||||
event->accept();
|
||||
}
|
||||
|
@ -24,11 +24,14 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QDialog>
|
||||
#include <QToolBar>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
#include <QSourceHighlite/qsourcehighliter.h>
|
||||
|
||||
namespace Project
|
||||
{
|
||||
class CodeEditor : public QDialog
|
||||
@ -50,7 +53,20 @@ public:
|
||||
public Q_SLOTS:
|
||||
void displayWindow();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onNewClicked();
|
||||
void onOpenClicked();
|
||||
void onSaveClicked();
|
||||
void onHelpClicked();
|
||||
|
||||
private:
|
||||
bool checkModified();
|
||||
bool save(const bool silent = false);
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private:
|
||||
QToolBar m_toolbar;
|
||||
QPlainTextEdit m_textEdit;
|
||||
QSourceHighlite::QSourceHighliter *m_highlighter;
|
||||
};
|
||||
}
|
||||
|