- Fehler in mehrzeiligen Kommentaren behoben
- Klammerpaare werden farblich hervorgehoben - Beim einrücken oder ausrücken eines markierten Blocks wird die Zeile, in der der Coursor steht, nicht mehr mitgerückt - Beim eintippen von Variablen wird eine Liste mit bereits deklarierten Variablen angezeigt
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
project(BareCode
|
project(BareCode
|
||||||
VERSION 1.1.0
|
VERSION 1.2.0
|
||||||
DESCRIPTION "A modular code editor built with Qt6"
|
DESCRIPTION "A modular code editor built with Qt6"
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
|
|||||||
8
PKGBUILD
8
PKGBUILD
@@ -2,7 +2,7 @@
|
|||||||
# Projekt Hirnfrei - https://www.projekt-hirnfrei.de
|
# Projekt Hirnfrei - https://www.projekt-hirnfrei.de
|
||||||
|
|
||||||
pkgname=barecode
|
pkgname=barecode
|
||||||
pkgver=1.1.0
|
pkgver=1.2.0
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Schlanker modularer Code-Editor für HTML, PHP, CSS und C++"
|
pkgdesc="Schlanker modularer Code-Editor für HTML, PHP, CSS und C++"
|
||||||
arch=('x86_64' 'aarch64')
|
arch=('x86_64' 'aarch64')
|
||||||
@@ -13,10 +13,10 @@ makedepends=('cmake' 'ninja')
|
|||||||
provides=('barecode')
|
provides=('barecode')
|
||||||
conflicts=('barecode-git')
|
conflicts=('barecode-git')
|
||||||
source=("$pkgname-$pkgver.tar.gz::https://git.projekt-hirnfrei.de/diabolus/BareCode/archive/v$pkgver.tar.gz")
|
source=("$pkgname-$pkgver.tar.gz::https://git.projekt-hirnfrei.de/diabolus/BareCode/archive/v$pkgver.tar.gz")
|
||||||
sha256sums=('04b11561920101b0444e5427e50658886d1fc62018426586fe512e405f2dc0e6') # Nach erstem Download ersetzen: sha256sum barecode-1.1.0.tar.gz
|
sha256sums=('SKIP') # Nach erstem Download ersetzen: sha256sum barecode-1.1.0.tar.gz
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "barecode"
|
cd "BareCode"
|
||||||
cmake -B build \
|
cmake -B build \
|
||||||
-G Ninja \
|
-G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
@@ -30,7 +30,7 @@ check() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
cd "barecode"
|
cd "BareCode"
|
||||||
DESTDIR="$pkgdir" cmake --install build
|
DESTDIR="$pkgdir" cmake --install build
|
||||||
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
|
install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ makedepends=('cmake' 'ninja' 'git')
|
|||||||
provides=('barecode')
|
provides=('barecode')
|
||||||
conflicts=('barecode')
|
conflicts=('barecode')
|
||||||
source=("$pkgname::git+https://git.projekt-hirnfrei.de/diabolus/BareCode.git")
|
source=("$pkgname::git+https://git.projekt-hirnfrei.de/diabolus/BareCode.git")
|
||||||
sha256sums=('04b11561920101b0444e5427e50658886d1fc62018426586fe512e405f2dc0e6')
|
sha256sums=('SKIP')
|
||||||
|
|
||||||
pkgver() {
|
pkgver() {
|
||||||
cd "$pkgname"
|
cd "$pkgname"
|
||||||
|
|||||||
27
README.md
27
README.md
@@ -1,7 +1,7 @@
|
|||||||
# BareCode
|
# BareCode
|
||||||
|
|
||||||
**Schlanker, modularer Code-Editor für Web-Entwickler**
|
**Schlanker, modularer Code-Editor für Web-Entwickler**
|
||||||
Version 1.1.0 — von **Dany Thinnes** | [Projekt Hirnfrei](https://www.projekt-hirnfrei.de) | [Discord](https://discord.projekt-hirnfrei.de)
|
Version 1.2.0 — von **Dany Thinnes** | [Projekt Hirnfrei](https://www.projekt-hirnfrei.de) | [Discord](https://discord.projekt-hirnfrei.de)
|
||||||
|
|
||||||
Entwickelt mit **C++17**, **Qt6** und **CMake**.
|
Entwickelt mit **C++17**, **Qt6** und **CMake**.
|
||||||
Kompiliert auf **Linux**, **Windows** und **Haiku**.
|
Kompiliert auf **Linux**, **Windows** und **Haiku**.
|
||||||
@@ -34,17 +34,19 @@ Siehe Abschnitt **Bauen** weiter unten.
|
|||||||
### Editor
|
### Editor
|
||||||
| Feature | Details |
|
| Feature | Details |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Syntax-Highlighting | HTML, PHP, CSS, C/C++ — inkl. mehrzeiliger Kommentare |
|
| Syntax-Highlighting | HTML, PHP, CSS, C/C++ — inkl. mehrzeiliger Kommentare über Leerzeilen |
|
||||||
| Tabs | Mehrere Dateien gleichzeitig, verschiebbar und schließbar |
|
| Tabs | Mehrere Dateien gleichzeitig, verschiebbar und schließbar |
|
||||||
| Änderungsindikator | ● im Tab-Titel bei ungespeicherten Änderungen |
|
| Änderungsindikator | ● im Tab-Titel bei ungespeicherten Änderungen |
|
||||||
| Zeilennummern | Eigener Gutter, aktuelle Zeile hervorgehoben |
|
| Zeilennummern | Eigener Gutter, aktuelle Zeile hervorgehoben |
|
||||||
| Einrück-Führungslinien | Vertikale Linien wie in VS Code |
|
| Einrück-Führungslinien | Vertikale Linien wie in VS Code |
|
||||||
|
| Klammerzugehörigkeit | Zusammengehörige Klammern werden grün hervorgehoben, fehlende rot |
|
||||||
| Auto-Indent | Einrückungstiefe wird bei Enter übernommen |
|
| Auto-Indent | Einrückungstiefe wird bei Enter übernommen |
|
||||||
| Tab / Shift+Tab | Ein- und Ausrücken, auch für mehrere Zeilen gleichzeitig |
|
| Tab / Shift+Tab | Ein- und Ausrücken für einzelne Zeilen und Selektionen |
|
||||||
| Smart Backspace | Springt zur vorherigen Tab-Stop-Position |
|
| Smart Backspace | Springt zur vorherigen Tab-Stop-Position |
|
||||||
| Farbvorschau | CSS-Farbwerte (#hex, rgb, hsl) werden inline angezeigt |
|
| Farbvorschau | CSS-Farbwerte (#hex, rgb, hsl) werden inline angezeigt |
|
||||||
| Farbauswahl | Klick auf das Farbquadrat öffnet einen Farbauswahl-Dialog |
|
| Farbauswahl | Klick auf das Farbquadrat öffnet einen Farbauswahl-Dialog |
|
||||||
| Funktionssignatur | Tooltip mit Parametern beim Tippen von PHP-Funktionen |
|
| Funktionssignatur | Tooltip mit Parametern beim Tippen von PHP-Funktionen |
|
||||||
|
| Variablen-Popup | Vorschläge für $variablen beim Tippen (nur Anzeige, kein Autocomplete) |
|
||||||
|
|
||||||
### Navigation
|
### Navigation
|
||||||
| Feature | Details |
|
| Feature | Details |
|
||||||
@@ -109,14 +111,15 @@ BareCode/
|
|||||||
├── editor/ # EditorPanel, EditorTab, CodeEditor,
|
├── editor/ # EditorPanel, EditorTab, CodeEditor,
|
||||||
│ # SearchPanel, FileSearchPanel,
|
│ # SearchPanel, FileSearchPanel,
|
||||||
│ # ColorIndicator, SignatureHelper,
|
│ # ColorIndicator, SignatureHelper,
|
||||||
│ # FunctionScanner, FunctionIndex,
|
│ # VariableCompleter, FunctionScanner,
|
||||||
│ # FunctionListPanel, FunctionListDialog,
|
│ # FunctionIndex, FunctionListPanel,
|
||||||
│ # DeadCodeAnalyzer, DeadCodeDialog
|
│ # FunctionListDialog, DeadCodeAnalyzer,
|
||||||
|
│ # DeadCodeDialog
|
||||||
├── filetree/ # FileTreePanel
|
├── filetree/ # FileTreePanel
|
||||||
└── highlighter/ # SyntaxHighlighter, HighlighterFactory
|
└── highlighter/ # SyntaxHighlighter, HighlighterFactory
|
||||||
```
|
```
|
||||||
|
|
||||||
Jedes Unterverzeichnis kompiliert als eigene statische Bibliothek — Komponenten lassen sich unabhängig austauschen und erweitern.
|
Jedes Unterverzeichnis kompiliert als eigene statische Bibliothek.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -160,11 +163,6 @@ cmake -B build -G "Visual Studio 17 2022" -A x64
|
|||||||
cmake --build build --config Release
|
cmake --build build --config Release
|
||||||
```
|
```
|
||||||
|
|
||||||
**Installer erstellen** (mit [Inno Setup](https://jrsoftware.org/isinfo.php)):
|
|
||||||
```bat
|
|
||||||
windeployqt --release build\Release\BareCode.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Haiku
|
### Haiku
|
||||||
@@ -176,8 +174,7 @@ cmake --build build -j$(nproc)
|
|||||||
./build/BareCode
|
./build/BareCode
|
||||||
```
|
```
|
||||||
|
|
||||||
> Getestet auf Haiku R1/beta5. `qt6_base_devel` enthält auf Haiku
|
> Getestet auf Haiku R1/beta5.
|
||||||
> sowohl die Header als auch die Build-Tools.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -187,8 +184,6 @@ cmake --build build -j$(nproc)
|
|||||||
2. `m_rules` im Konstruktor befüllen (siehe `CppHighlighter` als Vorlage)
|
2. `m_rules` im Konstruktor befüllen (siehe `CppHighlighter` als Vorlage)
|
||||||
3. Dateiendung in `HighlighterFactory.cpp` registrieren
|
3. Dateiendung in `HighlighterFactory.cpp` registrieren
|
||||||
|
|
||||||
Keine weiteren Dateien müssen geändert werden.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Lizenz
|
## Lizenz
|
||||||
|
|||||||
Binary file not shown.
2
main.cpp
2
main.cpp
@@ -7,7 +7,7 @@ int main(int argc, char *argv[])
|
|||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
app.setApplicationName("BareCode");
|
app.setApplicationName("BareCode");
|
||||||
app.setApplicationVersion("1.1.0");
|
app.setApplicationVersion("1.2.0");
|
||||||
app.setOrganizationName("BareCode");
|
app.setOrganizationName("BareCode");
|
||||||
|
|
||||||
// Icon in allen verfügbaren Größen setzen
|
// Icon in allen verfügbaren Größen setzen
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ AboutDialog::AboutDialog(QWidget *parent)
|
|||||||
info->addLayout(row);
|
info->addLayout(row);
|
||||||
};
|
};
|
||||||
|
|
||||||
makeRow(tr("Version"), "1.1.0");
|
makeRow(tr("Version"), "1.2.0");
|
||||||
makeRow(tr("Entwickler"), "Dany Thinnes");
|
makeRow(tr("Entwickler"), "Dany Thinnes");
|
||||||
makeRow(tr("Projekt"), "Projekt Hirnfrei");
|
makeRow(tr("Projekt"), "Projekt Hirnfrei");
|
||||||
makeRow(tr("Framework"), QString("Qt %1").arg(qVersion()));
|
makeRow(tr("Framework"), QString("Qt %1").arg(qVersion()));
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ set(EDITOR_SOURCES
|
|||||||
DeadCodeAnalyzer.h
|
DeadCodeAnalyzer.h
|
||||||
DeadCodeDialog.cpp
|
DeadCodeDialog.cpp
|
||||||
DeadCodeDialog.h
|
DeadCodeDialog.h
|
||||||
|
VariableCompleter.cpp
|
||||||
|
VariableCompleter.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(BareCode_Editor STATIC ${EDITOR_SOURCES})
|
add_library(BareCode_Editor STATIC ${EDITOR_SOURCES})
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "ColorIndicator.h"
|
#include "ColorIndicator.h"
|
||||||
#include "SignatureHelper.h"
|
#include "SignatureHelper.h"
|
||||||
#include "FunctionIndex.h"
|
#include "FunctionIndex.h"
|
||||||
|
#include "VariableCompleter.h"
|
||||||
|
|
||||||
#include "core/Settings.h"
|
#include "core/Settings.h"
|
||||||
#include "highlighter/HighlighterFactory.h"
|
#include "highlighter/HighlighterFactory.h"
|
||||||
@@ -27,6 +28,7 @@ CodeEditor::CodeEditor(Settings *settings, QWidget *parent)
|
|||||||
m_lineNumberArea = new LineNumberArea(this);
|
m_lineNumberArea = new LineNumberArea(this);
|
||||||
m_colorIndicator = new ColorIndicator(this);
|
m_colorIndicator = new ColorIndicator(this);
|
||||||
m_signatureHelper = new SignatureHelper(this);
|
m_signatureHelper = new SignatureHelper(this);
|
||||||
|
m_varCompleter = new VariableCompleter(this);
|
||||||
setupEditor();
|
setupEditor();
|
||||||
|
|
||||||
connect(this, &CodeEditor::blockCountChanged,
|
connect(this, &CodeEditor::blockCountChanged,
|
||||||
@@ -390,7 +392,7 @@ void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Current line highlight
|
// Current line highlight + Klammerzugehörigkeit
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
void CodeEditor::highlightCurrentLine()
|
void CodeEditor::highlightCurrentLine()
|
||||||
{
|
{
|
||||||
@@ -398,20 +400,161 @@ void CodeEditor::highlightCurrentLine()
|
|||||||
|
|
||||||
if (!isReadOnly())
|
if (!isReadOnly())
|
||||||
{
|
{
|
||||||
QTextEdit::ExtraSelection selection;
|
// Aktuelle Zeile hervorheben
|
||||||
|
QTextEdit::ExtraSelection lineSelection;
|
||||||
|
lineSelection.format.setBackground(palette().color(QPalette::AlternateBase));
|
||||||
|
lineSelection.format.setProperty(QTextFormat::FullWidthSelection, true);
|
||||||
|
lineSelection.cursor = textCursor();
|
||||||
|
lineSelection.cursor.clearSelection();
|
||||||
|
extraSelections.append(lineSelection);
|
||||||
|
|
||||||
const QColor lineColor = palette().color(QPalette::AlternateBase);
|
// Klammerzugehörigkeit
|
||||||
selection.format.setBackground(lineColor);
|
matchBrackets(extraSelections);
|
||||||
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
|
|
||||||
selection.cursor = textCursor();
|
|
||||||
selection.cursor.clearSelection();
|
|
||||||
|
|
||||||
extraSelections.append(selection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setExtraSelections(extraSelections);
|
setExtraSelections(extraSelections);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CodeEditor::matchBrackets(QList<QTextEdit::ExtraSelection> &selections)
|
||||||
|
{
|
||||||
|
static const QString openBrackets = "({[";
|
||||||
|
static const QString closeBrackets = ")}]";
|
||||||
|
|
||||||
|
QTextCursor cursor = textCursor();
|
||||||
|
const QString blockText = cursor.block().text();
|
||||||
|
const int col = cursor.columnNumber();
|
||||||
|
|
||||||
|
// Zeichen unter oder links vom Cursor prüfen
|
||||||
|
QChar ch;
|
||||||
|
int charPos = -1;
|
||||||
|
|
||||||
|
// Zuerst Zeichen unter dem Cursor
|
||||||
|
if (col < blockText.length())
|
||||||
|
{
|
||||||
|
ch = blockText[col];
|
||||||
|
if (openBrackets.contains(ch) || closeBrackets.contains(ch))
|
||||||
|
{
|
||||||
|
charPos = col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dann Zeichen links vom Cursor
|
||||||
|
if (charPos == -1 && col > 0)
|
||||||
|
{
|
||||||
|
ch = blockText[col - 1];
|
||||||
|
if (openBrackets.contains(ch) || closeBrackets.contains(ch))
|
||||||
|
{
|
||||||
|
charPos = col - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charPos == -1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Passende Klammer suchen
|
||||||
|
const bool isOpen = openBrackets.contains(ch);
|
||||||
|
const int bracketIndex = isOpen
|
||||||
|
? openBrackets.indexOf(ch)
|
||||||
|
: closeBrackets.indexOf(ch);
|
||||||
|
const QChar matchChar = isOpen
|
||||||
|
? closeBrackets[bracketIndex]
|
||||||
|
: openBrackets[bracketIndex];
|
||||||
|
|
||||||
|
// Dokumentposition der gefundenen Klammer
|
||||||
|
const int startPos = cursor.block().position() + charPos;
|
||||||
|
|
||||||
|
// Passende Klammer suchen — vorwärts oder rückwärts
|
||||||
|
int depth = 0;
|
||||||
|
int matchPos = -1;
|
||||||
|
|
||||||
|
if (isOpen)
|
||||||
|
{
|
||||||
|
// Vorwärts suchen
|
||||||
|
QTextCursor search(document());
|
||||||
|
search.setPosition(startPos);
|
||||||
|
|
||||||
|
while (!search.atEnd())
|
||||||
|
{
|
||||||
|
search.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
|
||||||
|
const QChar c = search.selectedText()[0];
|
||||||
|
search.clearSelection();
|
||||||
|
|
||||||
|
if (c == ch) { ++depth; }
|
||||||
|
else if (c == matchChar)
|
||||||
|
{
|
||||||
|
--depth;
|
||||||
|
if (depth == 0)
|
||||||
|
{
|
||||||
|
matchPos = search.position() - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Rückwärts suchen
|
||||||
|
QTextCursor search(document());
|
||||||
|
search.setPosition(startPos + 1);
|
||||||
|
|
||||||
|
while (search.position() > 0)
|
||||||
|
{
|
||||||
|
search.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
|
||||||
|
const QChar c = search.selectedText()[0];
|
||||||
|
search.clearSelection();
|
||||||
|
|
||||||
|
if (c == ch) { ++depth; }
|
||||||
|
else if (c == matchChar)
|
||||||
|
{
|
||||||
|
--depth;
|
||||||
|
if (depth == 0)
|
||||||
|
{
|
||||||
|
matchPos = search.position();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatierung
|
||||||
|
QTextCharFormat matchFormat;
|
||||||
|
if (matchPos >= 0)
|
||||||
|
{
|
||||||
|
// Gefunden — beide Klammern grün hervorheben
|
||||||
|
matchFormat.setBackground(QColor("#1a5a1a"));
|
||||||
|
matchFormat.setForeground(QColor("#88ff88"));
|
||||||
|
matchFormat.setFontWeight(QFont::Bold);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Kein Match — rot markieren
|
||||||
|
matchFormat.setBackground(QColor("#5a1a1a"));
|
||||||
|
matchFormat.setForeground(QColor("#ff8888"));
|
||||||
|
matchFormat.setFontWeight(QFont::Bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Öffnende / schließende Klammer markieren
|
||||||
|
QTextEdit::ExtraSelection sel1;
|
||||||
|
sel1.cursor = QTextCursor(document());
|
||||||
|
sel1.cursor.setPosition(startPos);
|
||||||
|
sel1.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
|
||||||
|
sel1.format = matchFormat;
|
||||||
|
selections.append(sel1);
|
||||||
|
|
||||||
|
// Passende Klammer markieren (nur wenn gefunden)
|
||||||
|
if (matchPos >= 0)
|
||||||
|
{
|
||||||
|
QTextEdit::ExtraSelection sel2;
|
||||||
|
sel2.cursor = QTextCursor(document());
|
||||||
|
sel2.cursor.setPosition(matchPos);
|
||||||
|
sel2.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
|
||||||
|
sel2.format = matchFormat;
|
||||||
|
selections.append(sel2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Key handling – Tab/Shift+Tab, Smart-Backspace, Auto-Indent
|
// Key handling – Tab/Shift+Tab, Smart-Backspace, Auto-Indent
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -430,6 +573,16 @@ void CodeEditor::keyPressEvent(QKeyEvent *event)
|
|||||||
QTextBlock startBlock = document()->findBlock(cursor.selectionStart());
|
QTextBlock startBlock = document()->findBlock(cursor.selectionStart());
|
||||||
QTextBlock endBlock = document()->findBlock(cursor.selectionEnd());
|
QTextBlock endBlock = document()->findBlock(cursor.selectionEnd());
|
||||||
|
|
||||||
|
// Wenn die Selektion genau am Anfang des letzten Blocks endet,
|
||||||
|
// diesen Block nicht mit einbeziehen — der Cursor steht dort nur
|
||||||
|
// mit Position 0, der Nutzer hat die Zeile nicht markiert
|
||||||
|
if (cursor.hasSelection() &&
|
||||||
|
cursor.selectionEnd() == endBlock.position() &&
|
||||||
|
endBlock != startBlock)
|
||||||
|
{
|
||||||
|
endBlock = endBlock.previous();
|
||||||
|
}
|
||||||
|
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
for (QTextBlock b = startBlock; b != endBlock.next(); b = b.next())
|
for (QTextBlock b = startBlock; b != endBlock.next(); b = b.next())
|
||||||
{
|
{
|
||||||
@@ -438,22 +591,14 @@ void CodeEditor::keyPressEvent(QKeyEvent *event)
|
|||||||
|
|
||||||
if (m_settings->useSpacesForTabs())
|
if (m_settings->useSpacesForTabs())
|
||||||
{
|
{
|
||||||
// Bis zu tabSize führende Leerzeichen entfernen
|
|
||||||
for (int i = 0; i < tabSize && i < lineText.length(); ++i)
|
for (int i = 0; i < tabSize && i < lineText.length(); ++i)
|
||||||
{
|
{
|
||||||
if (lineText[i] == ' ')
|
if (lineText[i] == ' ') { ++toRemove; }
|
||||||
{
|
else { break; }
|
||||||
++toRemove;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Einen führenden Tab entfernen
|
|
||||||
if (!lineText.isEmpty() && lineText[0] == '\t')
|
if (!lineText.isEmpty() && lineText[0] == '\t')
|
||||||
{
|
{
|
||||||
toRemove = 1;
|
toRemove = 1;
|
||||||
@@ -483,10 +628,16 @@ void CodeEditor::keyPressEvent(QKeyEvent *event)
|
|||||||
|
|
||||||
if (cursor.hasSelection())
|
if (cursor.hasSelection())
|
||||||
{
|
{
|
||||||
// Mehrere Zeilen einrücken
|
|
||||||
QTextBlock startBlock = document()->findBlock(cursor.selectionStart());
|
QTextBlock startBlock = document()->findBlock(cursor.selectionStart());
|
||||||
QTextBlock endBlock = document()->findBlock(cursor.selectionEnd());
|
QTextBlock endBlock = document()->findBlock(cursor.selectionEnd());
|
||||||
|
|
||||||
|
// Gleiche Korrektur: Cursor am Zeilenanfang → Zeile nicht einrücken
|
||||||
|
if (cursor.selectionEnd() == endBlock.position() &&
|
||||||
|
endBlock != startBlock)
|
||||||
|
{
|
||||||
|
endBlock = endBlock.previous();
|
||||||
|
}
|
||||||
|
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
for (QTextBlock b = startBlock; b != endBlock.next(); b = b.next())
|
for (QTextBlock b = startBlock; b != endBlock.next(); b = b.next())
|
||||||
{
|
{
|
||||||
@@ -591,5 +742,8 @@ void CodeEditor::keyPressEvent(QKeyEvent *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
QPlainTextEdit::keyPressEvent(event);
|
QPlainTextEdit::keyPressEvent(event);
|
||||||
|
|
||||||
|
// Variablen-Popup nach jedem Tastendruck aktualisieren
|
||||||
|
m_varCompleter->handleKeyPress(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class SyntaxHighlighter;
|
|||||||
class ColorIndicator;
|
class ColorIndicator;
|
||||||
class SignatureHelper;
|
class SignatureHelper;
|
||||||
class FunctionIndex;
|
class FunctionIndex;
|
||||||
|
class VariableCompleter;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// CodeEditor – Core editing widget.
|
// CodeEditor – Core editing widget.
|
||||||
@@ -55,8 +56,7 @@ signals:
|
|||||||
void fileSaved(const QString &filePath);
|
void fileSaved(const QString &filePath);
|
||||||
void navigateToRequested(const QString &filePath, int line);
|
void navigateToRequested(const QString &filePath, int line);
|
||||||
|
|
||||||
protected:
|
protected: void resizeEvent(QResizeEvent *event) override;
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
@@ -71,6 +71,7 @@ private:
|
|||||||
void setupEditor();
|
void setupEditor();
|
||||||
void installHighlighter(const QString &filePath);
|
void installHighlighter(const QString &filePath);
|
||||||
bool writeToFile(const QString &filePath);
|
bool writeToFile(const QString &filePath);
|
||||||
|
void matchBrackets(QList<QTextEdit::ExtraSelection> &selections);
|
||||||
|
|
||||||
Settings *m_settings = nullptr;
|
Settings *m_settings = nullptr;
|
||||||
LineNumberArea *m_lineNumberArea = nullptr;
|
LineNumberArea *m_lineNumberArea = nullptr;
|
||||||
@@ -78,5 +79,6 @@ private:
|
|||||||
ColorIndicator *m_colorIndicator = nullptr;
|
ColorIndicator *m_colorIndicator = nullptr;
|
||||||
SignatureHelper *m_signatureHelper = nullptr;
|
SignatureHelper *m_signatureHelper = nullptr;
|
||||||
FunctionIndex *m_functionIndex = nullptr;
|
FunctionIndex *m_functionIndex = nullptr;
|
||||||
|
VariableCompleter *m_varCompleter = nullptr;
|
||||||
QString m_filePath;
|
QString m_filePath;
|
||||||
};
|
};
|
||||||
|
|||||||
243
src/editor/VariableCompleter.cpp
Normal file
243
src/editor/VariableCompleter.cpp
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
#include "VariableCompleter.h"
|
||||||
|
#include "CodeEditor.h"
|
||||||
|
|
||||||
|
#include <QTextCursor>
|
||||||
|
#include <QTextBlock>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QListWidgetItem>
|
||||||
|
#include <QAbstractItemView>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QScreen>
|
||||||
|
|
||||||
|
VariableCompleter::VariableCompleter(CodeEditor *editor)
|
||||||
|
: QObject(editor)
|
||||||
|
, m_editor(editor)
|
||||||
|
{
|
||||||
|
// Popup als eigenständiges, rahmenloses Werkzeugfenster
|
||||||
|
m_popup = new QListWidget(nullptr);
|
||||||
|
m_popup->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint |
|
||||||
|
Qt::WindowStaysOnTopHint);
|
||||||
|
m_popup->setAttribute(Qt::WA_ShowWithoutActivating);
|
||||||
|
m_popup->setFocusPolicy(Qt::NoFocus);
|
||||||
|
m_popup->setMaximumHeight(200);
|
||||||
|
m_popup->setMinimumWidth(180);
|
||||||
|
m_popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
m_popup->setStyleSheet(
|
||||||
|
"QListWidget {"
|
||||||
|
" border: 1px solid palette(mid);"
|
||||||
|
" background: palette(toolTipBase);"
|
||||||
|
" color: palette(toolTipText);"
|
||||||
|
" font-family: Monospace;"
|
||||||
|
"}"
|
||||||
|
"QListWidget::item:selected {"
|
||||||
|
" background: palette(highlight);"
|
||||||
|
" color: palette(highlightedText);"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
connect(m_popup, &QListWidget::itemActivated,
|
||||||
|
this, &VariableCompleter::onItemActivated);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Nach jedem Tastendruck aufrufen
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
void VariableCompleter::handleKeyPress(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
// Popup-Navigation
|
||||||
|
if (m_popup->isVisible())
|
||||||
|
{
|
||||||
|
if (event->key() == Qt::Key_Escape)
|
||||||
|
{
|
||||||
|
hidePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
|
||||||
|
{
|
||||||
|
if (m_popup->currentItem())
|
||||||
|
{
|
||||||
|
onItemActivated(m_popup->currentItem());
|
||||||
|
}
|
||||||
|
hidePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event->key() == Qt::Key_Down)
|
||||||
|
{
|
||||||
|
const int next = qMin(m_popup->currentRow() + 1,
|
||||||
|
m_popup->count() - 1);
|
||||||
|
m_popup->setCurrentRow(next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event->key() == Qt::Key_Up)
|
||||||
|
{
|
||||||
|
const int prev = qMax(m_popup->currentRow() - 1, 0);
|
||||||
|
m_popup->setCurrentRow(prev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nach dem normalen Tastendruck Popup aktualisieren
|
||||||
|
// (der Event wurde bereits an QPlainTextEdit weitergegeben)
|
||||||
|
updatePopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Popup aktualisieren
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
void VariableCompleter::updatePopup()
|
||||||
|
{
|
||||||
|
const QString prefix = currentPrefix();
|
||||||
|
|
||||||
|
// Nur bei $ und mindestens einem weiteren Zeichen anzeigen
|
||||||
|
if (prefix.length() < 2 || !prefix.startsWith('$'))
|
||||||
|
{
|
||||||
|
hidePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList allVars = collectVariables();
|
||||||
|
const QString filter = prefix.toLower();
|
||||||
|
|
||||||
|
QStringList matches;
|
||||||
|
for (const QString &var : allVars)
|
||||||
|
{
|
||||||
|
if (var.toLower().startsWith(filter) && var != prefix)
|
||||||
|
{
|
||||||
|
matches.append(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches.isEmpty())
|
||||||
|
{
|
||||||
|
hidePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Popup befüllen
|
||||||
|
m_popup->clear();
|
||||||
|
for (const QString &var : matches)
|
||||||
|
{
|
||||||
|
m_popup->addItem(var);
|
||||||
|
}
|
||||||
|
m_popup->setCurrentRow(0);
|
||||||
|
|
||||||
|
// Größe anpassen
|
||||||
|
const int itemHeight = m_popup->sizeHintForRow(0) + 2;
|
||||||
|
const int height = qMin(matches.size() * itemHeight + 4, 200);
|
||||||
|
m_popup->setFixedHeight(height);
|
||||||
|
|
||||||
|
// Position unter dem Cursor
|
||||||
|
const QRect cursorRect = m_editor->cursorRect();
|
||||||
|
QPoint pos = m_editor->viewport()->mapToGlobal(
|
||||||
|
QPoint(cursorRect.left(), cursorRect.bottom() + 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Nicht aus dem Bildschirm herausragen
|
||||||
|
const QRect screen = QApplication::primaryScreen()->availableGeometry();
|
||||||
|
if (pos.x() + m_popup->width() > screen.right())
|
||||||
|
{
|
||||||
|
pos.setX(screen.right() - m_popup->width());
|
||||||
|
}
|
||||||
|
if (pos.y() + height > screen.bottom())
|
||||||
|
{
|
||||||
|
pos.setY(cursorRect.top() - height - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_popup->move(pos);
|
||||||
|
m_popup->show();
|
||||||
|
m_popup->raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VariableCompleter::hidePopup()
|
||||||
|
{
|
||||||
|
m_popup->hide();
|
||||||
|
m_popup->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Variablen im aktuellen Dokument sammeln
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
QStringList VariableCompleter::collectVariables() const
|
||||||
|
{
|
||||||
|
static const QRegularExpression varRegex(R"(\$[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)");
|
||||||
|
|
||||||
|
QSet<QString> seen;
|
||||||
|
QStringList result;
|
||||||
|
|
||||||
|
const QString text = m_editor->toPlainText();
|
||||||
|
QRegularExpressionMatchIterator it = varRegex.globalMatch(text);
|
||||||
|
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
const QString var = it.next().captured(0);
|
||||||
|
if (!seen.contains(var))
|
||||||
|
{
|
||||||
|
seen.insert(var);
|
||||||
|
result.append(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.sort(Qt::CaseInsensitive);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Text links vom Cursor — das angefangene $variable
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
QString VariableCompleter::currentPrefix() const
|
||||||
|
{
|
||||||
|
const QTextCursor cursor = m_editor->textCursor();
|
||||||
|
const QString line = cursor.block().text();
|
||||||
|
const int col = cursor.columnNumber();
|
||||||
|
|
||||||
|
if (col == 0)
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rückwärts gehen bis zum $ oder einem Nicht-Bezeichner-Zeichen
|
||||||
|
int start = col - 1;
|
||||||
|
while (start > 0)
|
||||||
|
{
|
||||||
|
const QChar c = line[start - 1];
|
||||||
|
if (!c.isLetterOrNumber() && c != '_' && c != '$')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
--start;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString token = line.mid(start, col - start);
|
||||||
|
return token.startsWith('$') ? token : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Klick oder Enter — Variable einfügen
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
void VariableCompleter::onItemActivated(QListWidgetItem *item)
|
||||||
|
{
|
||||||
|
if (!item)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString selected = item->text();
|
||||||
|
const QString prefix = currentPrefix();
|
||||||
|
|
||||||
|
if (prefix.isEmpty())
|
||||||
|
{
|
||||||
|
hidePopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix durch den vollständigen Variablennamen ersetzen
|
||||||
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
|
cursor.movePosition(QTextCursor::Left,
|
||||||
|
QTextCursor::KeepAnchor,
|
||||||
|
prefix.length());
|
||||||
|
cursor.insertText(selected);
|
||||||
|
|
||||||
|
m_editor->setTextCursor(cursor);
|
||||||
|
hidePopup();
|
||||||
|
}
|
||||||
38
src/editor/VariableCompleter.h
Normal file
38
src/editor/VariableCompleter.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QListWidget>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
class CodeEditor;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// VariableCompleter – Zeigt ein Popup mit passenden Variablennamen
|
||||||
|
// wenn der Nutzer $ tippt und weiter eingibt.
|
||||||
|
//
|
||||||
|
// Kein Autocomplete — nur Anzeige. Klick oder Enter übernimmt.
|
||||||
|
// Escape oder Weiterschreiben ohne Treffer schließt das Popup.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
class VariableCompleter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VariableCompleter(CodeEditor *editor);
|
||||||
|
|
||||||
|
// Muss nach jedem Tastendruck aufgerufen werden
|
||||||
|
void handleKeyPress(QKeyEvent *event);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onItemActivated(QListWidgetItem *item);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updatePopup();
|
||||||
|
void hidePopup();
|
||||||
|
QStringList collectVariables() const;
|
||||||
|
QString currentPrefix() const; // "$me" etc. links vom Cursor
|
||||||
|
|
||||||
|
CodeEditor *m_editor = nullptr;
|
||||||
|
QListWidget *m_popup = nullptr;
|
||||||
|
};
|
||||||
@@ -11,6 +11,7 @@ SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent)
|
|||||||
|
|
||||||
void SyntaxHighlighter::highlightBlock(const QString &text)
|
void SyntaxHighlighter::highlightBlock(const QString &text)
|
||||||
{
|
{
|
||||||
|
// Einzel-Zeilen-Regeln anwenden
|
||||||
for (const HighlightRule &rule : m_rules)
|
for (const HighlightRule &rule : m_rules)
|
||||||
{
|
{
|
||||||
QRegularExpressionMatchIterator it = rule.pattern.globalMatch(text);
|
QRegularExpressionMatchIterator it = rule.pattern.globalMatch(text);
|
||||||
@@ -27,21 +28,65 @@ void SyntaxHighlighter::highlightBlock(const QString &text)
|
|||||||
|
|
||||||
if (!m_hasMultiLineComment)
|
if (!m_hasMultiLineComment)
|
||||||
{
|
{
|
||||||
|
setCurrentBlockState(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mehrzeilige Kommentare
|
||||||
|
// Zustand 0 = normal, 1 = mitten in /* ... */
|
||||||
setCurrentBlockState(0);
|
setCurrentBlockState(0);
|
||||||
|
|
||||||
int startIndex = 0;
|
// Leere Zeile mitten im Kommentar — Zustand weitertragen
|
||||||
if (previousBlockState() != 1)
|
if (text.isEmpty())
|
||||||
{
|
{
|
||||||
|
if (previousBlockState() == 1)
|
||||||
|
{
|
||||||
|
setCurrentBlockState(1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startIndex = 0;
|
||||||
|
|
||||||
|
if (previousBlockState() == 1)
|
||||||
|
{
|
||||||
|
// Vorherige Zeile war mitten im Kommentar — ab Zeilenanfang nach Ende suchen
|
||||||
|
QRegularExpressionMatch endMatch = m_commentEndExpression.match(text, 0);
|
||||||
|
|
||||||
|
if (endMatch.hasMatch())
|
||||||
|
{
|
||||||
|
// Ende des Kommentars gefunden
|
||||||
|
const int commentLength = static_cast<int>(endMatch.capturedStart())
|
||||||
|
+ static_cast<int>(endMatch.capturedLength());
|
||||||
|
setFormat(0, commentLength, m_multiLineCommentFormat);
|
||||||
|
|
||||||
|
// Nach weiteren Kommentaren in der gleichen Zeile suchen
|
||||||
|
QRegularExpressionMatch nextStart =
|
||||||
|
m_commentStartExpression.match(text, commentLength);
|
||||||
|
startIndex = nextStart.hasMatch()
|
||||||
|
? static_cast<int>(nextStart.capturedStart())
|
||||||
|
: -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Noch kein Ende — gesamte Zeile ist Kommentar
|
||||||
|
setFormat(0, text.length(), m_multiLineCommentFormat);
|
||||||
|
setCurrentBlockState(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Neuen Kommentaranfang suchen
|
||||||
QRegularExpressionMatch m = m_commentStartExpression.match(text);
|
QRegularExpressionMatch m = m_commentStartExpression.match(text);
|
||||||
startIndex = m.hasMatch() ? static_cast<int>(m.capturedStart()) : -1;
|
startIndex = m.hasMatch() ? static_cast<int>(m.capturedStart()) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (startIndex >= 0)
|
while (startIndex >= 0)
|
||||||
{
|
{
|
||||||
QRegularExpressionMatch endMatch = m_commentEndExpression.match(text, startIndex);
|
QRegularExpressionMatch endMatch =
|
||||||
|
m_commentEndExpression.match(text, startIndex);
|
||||||
|
|
||||||
int commentLength = 0;
|
int commentLength = 0;
|
||||||
|
|
||||||
if (endMatch.hasMatch())
|
if (endMatch.hasMatch())
|
||||||
@@ -52,6 +97,7 @@ void SyntaxHighlighter::highlightBlock(const QString &text)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Kommentar geht über Zeilenende
|
||||||
setCurrentBlockState(1);
|
setCurrentBlockState(1);
|
||||||
commentLength = text.length() - startIndex;
|
commentLength = text.length() - startIndex;
|
||||||
}
|
}
|
||||||
@@ -63,6 +109,7 @@ void SyntaxHighlighter::highlightBlock(const QString &text)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nach weiteren Kommentaren in dieser Zeile suchen
|
||||||
QRegularExpressionMatch nextStart =
|
QRegularExpressionMatch nextStart =
|
||||||
m_commentStartExpression.match(text, startIndex + commentLength);
|
m_commentStartExpression.match(text, startIndex + commentLength);
|
||||||
startIndex = nextStart.hasMatch()
|
startIndex = nextStart.hasMatch()
|
||||||
@@ -374,14 +421,22 @@ void PhpHighlighter::highlightPhpRange(const QString &text, int start, int lengt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /* */ Block-Kommentare innerhalb des PHP-Bereichs mehrzeilig behandeln.
|
|
||||||
// Block-Zustand 3 = wir sind mitten in einem /* ... */ PHP-Kommentar.
|
|
||||||
static const QRegularExpression blockOpen(R"(/\*)");
|
static const QRegularExpression blockOpen(R"(/\*)");
|
||||||
static const QRegularExpression blockClose(R"(\*/)");
|
static const QRegularExpression blockClose(R"(\*/)");
|
||||||
|
|
||||||
int searchFrom = 0;
|
int searchFrom = 0;
|
||||||
|
|
||||||
// Waren wir bereits in einem Block-Kommentar?
|
// Leere Zeile mitten im PHP-Blockkommentar — Zustand weitertragen
|
||||||
|
if (phpText.trimmed().isEmpty())
|
||||||
|
{
|
||||||
|
if (previousBlockState() == 3)
|
||||||
|
{
|
||||||
|
setFormat(start, length, m_phpCommentFormat);
|
||||||
|
setCurrentBlockState(3);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (previousBlockState() == 3)
|
if (previousBlockState() == 3)
|
||||||
{
|
{
|
||||||
QRegularExpressionMatch closeMatch = blockClose.match(phpText, 0);
|
QRegularExpressionMatch closeMatch = blockClose.match(phpText, 0);
|
||||||
@@ -391,18 +446,15 @@ void PhpHighlighter::highlightPhpRange(const QString &text, int start, int lengt
|
|||||||
+ static_cast<int>(closeMatch.capturedLength());
|
+ static_cast<int>(closeMatch.capturedLength());
|
||||||
setFormat(start, end, m_phpCommentFormat);
|
setFormat(start, end, m_phpCommentFormat);
|
||||||
searchFrom = end;
|
searchFrom = end;
|
||||||
// Block-Kommentar geschlossen — Zustand wird weiter unten gesetzt
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Gesamter Bereich ist noch Kommentar
|
|
||||||
setFormat(start, length, m_phpCommentFormat);
|
setFormat(start, length, m_phpCommentFormat);
|
||||||
setCurrentBlockState(3);
|
setCurrentBlockState(3);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Neue /* ... */ Kommentare innerhalb dieses PHP-Bereichs suchen
|
|
||||||
while (searchFrom < phpText.length())
|
while (searchFrom < phpText.length())
|
||||||
{
|
{
|
||||||
QRegularExpressionMatch openMatch = blockOpen.match(phpText, searchFrom);
|
QRegularExpressionMatch openMatch = blockOpen.match(phpText, searchFrom);
|
||||||
@@ -423,7 +475,6 @@ void PhpHighlighter::highlightPhpRange(const QString &text, int start, int lengt
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Kein schließendes */ gefunden — geht über Zeilenende
|
|
||||||
setFormat(start + openPos, length - openPos, m_phpCommentFormat);
|
setFormat(start + openPos, length - openPos, m_phpCommentFormat);
|
||||||
setCurrentBlockState(3);
|
setCurrentBlockState(3);
|
||||||
return;
|
return;
|
||||||
@@ -434,13 +485,39 @@ void PhpHighlighter::highlightPhpRange(const QString &text, int start, int lengt
|
|||||||
void PhpHighlighter::highlightBlock(const QString &text)
|
void PhpHighlighter::highlightBlock(const QString &text)
|
||||||
{
|
{
|
||||||
// Zuerst HTML-Basis-Regeln auf den gesamten Text anwenden
|
// Zuerst HTML-Basis-Regeln auf den gesamten Text anwenden
|
||||||
|
// (setzt auch den State für HTML <!-- --> Kommentare)
|
||||||
SyntaxHighlighter::highlightBlock(text);
|
SyntaxHighlighter::highlightBlock(text);
|
||||||
|
|
||||||
// Block-Zustände:
|
// Block-Zustände:
|
||||||
// 0 = HTML-Modus
|
// 0 = HTML-Modus
|
||||||
|
// 1 = HTML <!-- --> Kommentar (von Basisklasse verwaltet)
|
||||||
// 2 = innerhalb PHP-Block (kein /* */ Kommentar)
|
// 2 = innerhalb PHP-Block (kein /* */ Kommentar)
|
||||||
// 3 = innerhalb PHP /* */ Block-Kommentar
|
// 3 = innerhalb PHP /* */ Block-Kommentar
|
||||||
|
|
||||||
|
// State 1 (HTML-Kommentar) wurde von der Basisklasse gesetzt — nicht überschreiben
|
||||||
|
if (currentBlockState() == 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn der vorherige Block ein HTML-Kommentar war und dieser noch nicht
|
||||||
|
// abgeschlossen wurde, hat die Basisklasse das bereits korrekt behandelt.
|
||||||
|
// Wir setzen nur dann auf 0 zurück wenn wir sicher nicht in HTML-Kommentar sind.
|
||||||
|
if (previousBlockState() != 1)
|
||||||
|
{
|
||||||
setCurrentBlockState(0);
|
setCurrentBlockState(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leere Zeile — Zustand weitertragen
|
||||||
|
if (text.isEmpty())
|
||||||
|
{
|
||||||
|
const int prev = previousBlockState();
|
||||||
|
if (prev == 2 || prev == 3)
|
||||||
|
{
|
||||||
|
setCurrentBlockState(prev);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static const QRegularExpression phpOpen(R"(<\?(?:php|=)?\s?)",
|
static const QRegularExpression phpOpen(R"(<\?(?:php|=)?\s?)",
|
||||||
QRegularExpression::CaseInsensitiveOption);
|
QRegularExpression::CaseInsensitiveOption);
|
||||||
@@ -466,8 +543,6 @@ void PhpHighlighter::highlightBlock(const QString &text)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
highlightPhpRange(text, 0, text.length());
|
highlightPhpRange(text, 0, text.length());
|
||||||
// highlightPhpRange setzt den Zustand auf 3 falls nötig,
|
|
||||||
// sonst behalten wir 2 (offener PHP-Block ohne Kommentar)
|
|
||||||
if (currentBlockState() != 3)
|
if (currentBlockState() != 3)
|
||||||
{
|
{
|
||||||
setCurrentBlockState(2);
|
setCurrentBlockState(2);
|
||||||
|
|||||||
Reference in New Issue
Block a user