BareCode V 1.1.0
This commit is contained in:
222
src/editor/SignatureHelper.cpp
Normal file
222
src/editor/SignatureHelper.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
#include "SignatureHelper.h"
|
||||
#include "SignatureTooltip.h"
|
||||
#include "CodeEditor.h"
|
||||
|
||||
#include <QTextCursor>
|
||||
#include <QTextBlock>
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QRect>
|
||||
#include <QRegularExpression>
|
||||
|
||||
SignatureHelper::SignatureHelper(CodeEditor *editor)
|
||||
: QObject(editor)
|
||||
, m_editor(editor)
|
||||
{
|
||||
// Tooltip als Kind des Viewports — bleibt im Fenster
|
||||
m_tooltip = new SignatureTooltip(editor->window());
|
||||
|
||||
loadDatabase();
|
||||
|
||||
connect(m_editor, &CodeEditor::cursorPositionChanged,
|
||||
this, &SignatureHelper::onCursorPositionChanged);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Datenbank laden
|
||||
// ---------------------------------------------------------------------------
|
||||
void SignatureHelper::loadDatabase()
|
||||
{
|
||||
QFile f(":/php_functions.json");
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(f.readAll());
|
||||
if (!doc.isArray())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const QJsonValue &val : doc.array())
|
||||
{
|
||||
const QJsonObject obj = val.toObject();
|
||||
const QString name = obj["name"].toString();
|
||||
if (name.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FunctionInfo info;
|
||||
info.signature = obj["signature"].toString();
|
||||
info.description = obj["desc"].toString();
|
||||
m_functions.insert(name.toLower(), info);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cursor-Bewegung auswerten
|
||||
// ---------------------------------------------------------------------------
|
||||
void SignatureHelper::onCursorPositionChanged()
|
||||
{
|
||||
const QTextCursor cursor = m_editor->textCursor();
|
||||
const QString block = cursor.block().text();
|
||||
const int col = cursor.columnNumber();
|
||||
const QString leftText = block.left(col);
|
||||
|
||||
const QString funcName = extractFunctionName(leftText);
|
||||
|
||||
if (funcName.isEmpty())
|
||||
{
|
||||
m_tooltip->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Klammern zählen — wenn alle geschlossen, Tooltip ausblenden
|
||||
if (countOpenParens(leftText) <= 0)
|
||||
{
|
||||
m_tooltip->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString key = funcName.toLower();
|
||||
if (!m_functions.contains(key))
|
||||
{
|
||||
m_tooltip->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const FunctionInfo &info = m_functions[key];
|
||||
|
||||
// Position unter dem Cursor berechnen
|
||||
const QRect cursorRect = m_editor->cursorRect(cursor);
|
||||
const QPoint globalPos = m_editor->viewport()->mapToGlobal(
|
||||
QPoint(cursorRect.left(), cursorRect.bottom() + 4)
|
||||
);
|
||||
|
||||
m_tooltip->showSignature(info.signature, info.description, globalPos);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Funktionsnamen links vor der öffnenden Klammer extrahieren
|
||||
// ---------------------------------------------------------------------------
|
||||
QString SignatureHelper::extractFunctionName(const QString &text) const
|
||||
{
|
||||
// Wir suchen das letzte '(' das zu einem Funktionsnamen gehört.
|
||||
// Dabei müssen wir verschachtelte Klammern korrekt behandeln.
|
||||
int depth = 0;
|
||||
int openPos = -1;
|
||||
|
||||
for (int i = text.length() - 1; i >= 0; --i)
|
||||
{
|
||||
const QChar ch = text[i];
|
||||
if (ch == ')')
|
||||
{
|
||||
++depth;
|
||||
}
|
||||
else if (ch == '(')
|
||||
{
|
||||
if (depth == 0)
|
||||
{
|
||||
openPos = i;
|
||||
break;
|
||||
}
|
||||
--depth;
|
||||
}
|
||||
}
|
||||
|
||||
if (openPos <= 0)
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
// Funktionsnamen direkt links von '(' lesen
|
||||
int end = openPos - 1;
|
||||
|
||||
// Leerzeichen überspringen
|
||||
while (end >= 0 && text[end].isSpace())
|
||||
{
|
||||
--end;
|
||||
}
|
||||
|
||||
if (end < 0)
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
// Bezeichner-Zeichen sammeln (Buchstaben, Ziffern, _, :, \)
|
||||
int start = end;
|
||||
while (start > 0 &&
|
||||
(text[start - 1].isLetterOrNumber() ||
|
||||
text[start - 1] == '_' ||
|
||||
text[start - 1] == ':' ||
|
||||
text[start - 1] == '\\'))
|
||||
{
|
||||
--start;
|
||||
}
|
||||
|
||||
const QString name = text.mid(start, end - start + 1);
|
||||
|
||||
// Schlüsselwörter und leere Namen ausschließen
|
||||
static const QStringList keywords = {
|
||||
"if", "else", "elseif", "while", "for", "foreach",
|
||||
"switch", "match", "catch", "function", "fn"
|
||||
};
|
||||
|
||||
if (name.isEmpty() || keywords.contains(name.toLower()))
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
// Nur den letzten Teil nach :: oder -> nehmen
|
||||
const int colonPos = name.lastIndexOf("::");
|
||||
const int arrowPos = name.lastIndexOf("->");
|
||||
const int backslashPos = name.lastIndexOf("\\");
|
||||
const int splitPos = qMax(backslashPos, qMax(colonPos, arrowPos));
|
||||
|
||||
if (splitPos >= 0)
|
||||
{
|
||||
return name.mid(splitPos + (name[splitPos] == ':' ? 2 : (name[splitPos] == '\\' ? 1 : 2)));
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Offene Klammern zählen
|
||||
// ---------------------------------------------------------------------------
|
||||
int SignatureHelper::countOpenParens(const QString &text) const
|
||||
{
|
||||
int depth = 0;
|
||||
bool inString = false;
|
||||
QChar stringChar;
|
||||
|
||||
for (int i = 0; i < text.length(); ++i)
|
||||
{
|
||||
const QChar ch = text[i];
|
||||
|
||||
// Einfache String-Erkennung (kein vollständiger PHP-Parser)
|
||||
if (!inString && (ch == '\'' || ch == '"'))
|
||||
{
|
||||
inString = true;
|
||||
stringChar = ch;
|
||||
continue;
|
||||
}
|
||||
if (inString)
|
||||
{
|
||||
if (ch == stringChar && (i == 0 || text[i - 1] != '\\'))
|
||||
{
|
||||
inString = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '(') { ++depth; }
|
||||
else if (ch == ')') { --depth; }
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
Reference in New Issue
Block a user