work save: hint

This commit is contained in:
royqh1979@gmail.com 2021-08-29 17:23:40 +08:00
parent 2ae8521bca
commit 3d103ddee9
6 changed files with 349 additions and 5 deletions

View File

@ -21,9 +21,11 @@
#include <QGuiApplication> #include <QGuiApplication>
#include <QClipboard> #include <QClipboard>
#include <QPainter> #include <QPainter>
#include <QToolTip>
#include "iconsmanager.h" #include "iconsmanager.h"
#include "debugger.h" #include "debugger.h"
#include "editorlist.h" #include "editorlist.h"
#include <QDebug>
using namespace std; using namespace std;
@ -63,7 +65,9 @@ Editor::Editor(QWidget *parent, const QString& filename,
mSyntaxWarningColor("orange"), mSyntaxWarningColor("orange"),
mLineCount(0), mLineCount(0),
mActiveBreakpointLine(-1), mActiveBreakpointLine(-1),
mLastIdCharPressed(0) mLastIdCharPressed(0),
mCurrentWord(),
mCurrentTipType(TipType::None)
{ {
if (mFilename.isEmpty()) { if (mFilename.isEmpty()) {
newfileCount++; newfileCount++;
@ -575,6 +579,115 @@ void Editor::onPreparePaintHighlightToken(int row, int column, const QString &to
} }
} }
bool Editor::event(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
BufferCoord p;
TipType reason = getTipType(helpEvent->pos(),p);
qDebug()<<(int)reason;
PSyntaxIssue pError;
int line ;
if (reason == TipType::Error) {
pError = getSyntaxIssueAtPosition(p);
} else if ((reason == TipType::None) && GetLineOfMouse(line)) {
//it's on gutter
//see if its error;
PSyntaxIssueList issues = getSyntaxIssuesAtLine(line);
if (issues && !issues->isEmpty()) {
reason = TipType::Error;
pError = issues->front();
}
}
// Get subject
bool isIncludeLine = false;
BufferCoord pBeginPos,pEndPos;
QString s;
switch (reason) {
case TipType::Preprocessor:
// When hovering above a preprocessor line, determine if we want to show an include or a identifier hint
s = lines()->getString(p.Line - 1);
isIncludeLine = mParser->isIncludeLine(s);
if (!isIncludeLine)
s = WordAtRowCol(p);
break;
case TipType::Identifier:
if (pMainWindow->debugger()->executing())
s = getWordAtPosition(p, pBeginPos,pEndPos, WordPurpose::wpEvaluation); // debugging
else if (//devEditor.ParserHints and
!mCompletionPopup->isVisible()
&& !mHeaderCompletionPopup->isVisible())
s = getWordAtPosition(p, pBeginPos,pEndPos, WordPurpose::wpInformation); // information during coding
break;
case TipType::Selection:
s = selText(); // when a selection is available, always only use that
break;
case TipType::Error:
s = pError->token;
break;
case TipType::None:
//fText.Cursor := crIBeam; // nope
cancelHint();
event->ignore();
return true;
}
qDebug()<<s<<" - "<<(int)reason;
// Don't rescan the same stuff over and over again (that's slow)
// if (s = fCurrentWord) and (fText.Hint<>'') then
s = s.trimmed();
if ((s == mCurrentWord) && (mCurrentTipType == reason)) {
event->ignore();
return true; // do NOT remove hint when subject stays the same
}
// Remove hint
cancelHint();
mCurrentWord = s;
mCurrentTipType = reason;
// We are allowed to change the cursor
// if (ssCtrl in Shift) then
// fText.Cursor := crHandPoint
// else
// fText.Cursor := crIBeam;
// Determine what to do with subject
QString hint = "";
switch (reason) {
case TipType::Preprocessor:
if (isIncludeLine) {
hint = getFileHint(s);
} else if (//devEditor.ParserHints and
!mCompletionPopup->isVisible()
&& !mHeaderCompletionPopup->isVisible()) {
hint = getParserHint(s,p.Line);
}
break;
case TipType::Identifier:
case TipType::Selection:
if (!mCompletionPopup->isVisible()
&& !mHeaderCompletionPopup->isVisible()) {
if (pMainWindow->debugger()->executing()) {
hint = getDebugHint(s);
} else { //if devEditor.ParserHints {
hint = getParserHint(s, p.Line);
}
}
break;
case TipType::Error:
hint = getErrorHint(s);
}
if (!hint.isEmpty()) {
QToolTip::showText(helpEvent->globalPos(),hint);
} else
event->ignore();
}
return SynEdit::event(event);
}
void Editor::copyToClipboard() void Editor::copyToClipboard()
{ {
if (pSettings->editor().copySizeLimit()) { if (pSettings->editor().copySizeLimit()) {
@ -766,6 +879,8 @@ Editor::PSyntaxIssueList Editor::getSyntaxIssuesAtLine(int line)
Editor::PSyntaxIssue Editor::getSyntaxIssueAtPosition(const BufferCoord &pos) Editor::PSyntaxIssue Editor::getSyntaxIssueAtPosition(const BufferCoord &pos)
{ {
PSyntaxIssueList lst = getSyntaxIssuesAtLine(pos.Line); PSyntaxIssueList lst = getSyntaxIssuesAtLine(pos.Line);
if (!lst)
return PSyntaxIssue();
foreach (const PSyntaxIssue& issue, *lst) { foreach (const PSyntaxIssue& issue, *lst) {
if (issue->startChar<=pos.Char && pos.Char<=issue->endChar) if (issue->startChar<=pos.Char && pos.Char<=issue->endChar)
return issue; return issue;
@ -1735,6 +1850,119 @@ bool Editor::onHeaderCompletionKeyPressed(QKeyEvent *event)
return processed; return processed;
} }
Editor::TipType Editor::getTipType(QPoint point, BufferCoord& pos)
{
// Only allow in the text area...
if (PointToCharLine(point, pos)) {
if (!pMainWindow->debugger()->executing()
&& getSyntaxIssueAtPosition(pos)) {
return TipType::Error;
}
PSynHighlighterAttribute attr;
QString s;
// Only allow hand tips in highlighted areas
if (GetHighlighterAttriAtRowCol(pos,s,attr)) {
// Only allow Identifiers, Preprocessor directives, and selection
if (attr) {
if (selAvail()) {
// do not allow when dragging selection
if (IsPointInSelection(pos))
return TipType::Selection;
} else if (attr->name() == SYNS_AttrAreaAIdentifier)
return TipType::Identifier;
else if (attr->name() == SYNS_AttrPreprocessor)
return TipType::Preprocessor;
}
}
}
return TipType::None;
}
void Editor::cancelHint()
{
//MainForm.Debugger.OnEvalReady := nil;
// disable editor hint
QToolTip::hideText();
mCurrentWord = "";
mCurrentTipType = TipType::None;
}
QString Editor::getFileHint(const QString &s)
{
QString fileName = mParser->getHeaderFileName(mFilename, s);
if (QFileInfo(fileName).exists()) {
return fileName + " - " + tr("Ctrl+click for more info");
}
return "";
}
QString Editor::getParserHint(const QString &s, int line)
{
// This piece of code changes the parser database, possibly making hints and code completion invalid...
QString result;
PStatement statement = mParser->findStatementOf(mFilename, s, line);
if (!statement)
return result;
if (statement->kind == StatementKind::skFunction
|| statement->kind == StatementKind::skConstructor
|| statement->kind == StatementKind::skDestructor) {
PStatement parentScope = statement->parentScope.lock();
if (parentScope && parentScope->kind == StatementKind::skNamespace) {
PStatementList namespaceStatementsList =
mParser->findNamespace(parentScope->command);
if (namespaceStatementsList) {
foreach (const PStatement& namespaceStatement, *namespaceStatementsList) {
QString hint = getHintForFunction(statement,namespaceStatement,
mFilename,line);
if (!hint.isEmpty()) {
if (!result.isEmpty())
result += "<BR />";
result += hint;
}
}
}
} else
result = getHintForFunction(statement, parentScope,
mFilename,line);
} else if (statement->line>0) {
QFileInfo fileInfo(statement->fileName);
result = mParser->prettyPrintStatement(statement) + " - " +
QString(" %1 (%2) ")
.arg(fileInfo.fileName())
.arg(statement->line)
+ tr("Ctrl+click for more info");
} else { // hard defines
result = mParser->prettyPrintStatement(statement);
}
// Result := StringReplace(Result, '|', #5, [rfReplaceAll]);
return result;
}
QString Editor::getHintForFunction(const PStatement &statement, const PStatement &scopeStatement, const QString& filename, int line)
{
QString result;
const StatementMap& children = mParser->statementList().childrenStatements(scopeStatement);
foreach (const PStatement& childStatement, children){
if (statement->command == childStatement->command
&& statement->kind == childStatement->kind) {
if ((line < childStatement->line) &&
childStatement->fileName == filename)
continue;
if (!result.isEmpty())
result += "<BR />";
result = mParser->prettyPrintStatement(childStatement) + " - " +
QString(" %1 (%2) ")
.arg(filename)
.arg(childStatement->line)
+ tr("Ctrl+click for more info");
}
}
return result;
}
QString Editor::getWordAtPosition(const BufferCoord &p, BufferCoord &pWordBegin, BufferCoord &pWordEnd, WordPurpose purpose) QString Editor::getWordAtPosition(const BufferCoord &p, BufferCoord &pWordBegin, BufferCoord &pWordEnd, WordPurpose purpose)
{ {
QString result = ""; QString result = "";

View File

@ -61,6 +61,14 @@ public:
wpInformation // walk backwards over words, array, functions, parents, forwards over words wpInformation // walk backwards over words, array, functions, parents, forwards over words
}; };
enum class TipType {
Preprocessor, // cursor hovers above preprocessor line
Identifier, // cursor hovers above identifier
Selection, // cursor hovers above selection
None, // mouseover not allowed
Error //Cursor hovers above error line/item;
};
struct SyntaxIssue { struct SyntaxIssue {
int col; int col;
int endCol; int endCol;
@ -176,6 +184,15 @@ private:
bool onCompletionKeyPressed(QKeyEvent* event); bool onCompletionKeyPressed(QKeyEvent* event);
bool onHeaderCompletionKeyPressed(QKeyEvent* event); bool onHeaderCompletionKeyPressed(QKeyEvent* event);
TipType getTipType(QPoint point, BufferCoord& pos);
void cancelHint();
QString getFileHint(const QString& s);
QString getParserHint(const QString& s, int line);
QString getDebugHint(const QString& s);
QString getErrorHint(const QString& s);
QString getHintForFunction(const PStatement& statement, const PStatement& scope,
const QString& filename, int line);
private: private:
static int newfileCount; static int newfileCount;
QByteArray mEncodingOption; // the encoding type set by the user QByteArray mEncodingOption; // the encoding type set by the user
@ -201,6 +218,8 @@ private:
std::shared_ptr<HeaderCompletionPopup> mHeaderCompletionPopup; std::shared_ptr<HeaderCompletionPopup> mHeaderCompletionPopup;
int mLastIdCharPressed; int mLastIdCharPressed;
bool mUseCppSyntax; bool mUseCppSyntax;
QString mCurrentWord;
TipType mCurrentTipType;
// QWidget interface // QWidget interface
protected: protected:
@ -221,6 +240,10 @@ protected:
// SynEdit interface // SynEdit interface
protected: protected:
void onPreparePaintHighlightToken(int row, int column, const QString &token, PSynHighlighterAttribute attr, SynFontStyles &style, QColor &foreground, QColor &background) override; void onPreparePaintHighlightToken(int row, int column, const QString &token, PSynHighlighterAttribute attr, SynFontStyles &style, QColor &foreground, QColor &background) override;
// QObject interface
public:
bool event(QEvent *event) override;
}; };
#endif // EDITOR_H #endif // EDITOR_H

View File

@ -3,9 +3,11 @@
#include "../utils.h" #include "../utils.h"
#include <QApplication> #include <QApplication>
#include <QDate>
#include <QHash> #include <QHash>
#include <QQueue> #include <QQueue>
#include <QThread> #include <QThread>
#include <QTime>
static QAtomicInt cppParserCount(0); static QAtomicInt cppParserCount(0);
CppParser::CppParser(QObject *parent) : QObject(parent) CppParser::CppParser(QObject *parent) : QObject(parent)
@ -811,10 +813,53 @@ void CppParser::unFreeze()
mLockCount--; mLockCount--;
} }
QString CppParser::prettyPrintStatement(const PStatement& statement, int line) QString CppParser::prettyPrintStatement(const PStatement& statement, const QString& filename, int line)
{ {
//TODO: implement it QString result;
return "not implemented yet"; if (!statement->hintText.isEmpty()) {
if (statement->kind != StatementKind::skPreprocessor)
result = statement->hintText;
else if (statement->command == "__FILE__")
result = '"'+filename+'"';
else if (statement->command == "__LINE__")
result = QString("\"%1\"").arg(line);
else if (statement->command == "__DATE__")
result = QString("\"%1\"").arg(QDate::currentDate().toString(Qt::ISODate));
else if (statement->command == "__TIME__")
result = QString("\"%1\"").arg(QTime::currentTime().toString(Qt::ISODate));
else
result = statement->hintText;
} else {
switch(statement->kind) {
case StatementKind::skFunction:
case StatementKind::skVariable:
case StatementKind::skParameter:
case StatementKind::skClass:
if (statement->scope!= StatementScope::ssLocal)
result = getScopePrefix(statement); // public
result += statement->type + ' '; // void
result += statement->fullName; // A::B::C::Bar
result += getArgsSuffix(statement); // (int a)
break;
case StatementKind::skNamespace:
result = statement->fullName; // Bar
break;
case StatementKind::skConstructor:
result = getScopePrefix(statement); // public
result += QObject::tr("constructor") + ' '; // constructor
result += statement->type + ' '; // void
result += statement->fullName; // A::B::C::Bar
result += getArgsSuffix(statement); // (int a)
break;
case StatementKind::skDestructor:
result = getScopePrefix(statement); // public
result += QObject::tr("destructor") + ' '; // constructor
result += statement->type + ' '; // void
result += statement->fullName; // A::B::C::Bar
result += getArgsSuffix(statement); // (int a)
break;
}
}
} }
QString CppParser::getFirstTemplateParam(const PStatement& statement, QString CppParser::getFirstTemplateParam(const PStatement& statement,

View File

@ -93,7 +93,7 @@ public:
//QString statementKindStr(StatementKind value); //QString statementKindStr(StatementKind value);
//QString statementClassScopeStr(StatementClassScope value); //QString statementClassScopeStr(StatementClassScope value);
QString prettyPrintStatement(const PStatement& statement, int line = -1); QString prettyPrintStatement(const PStatement& statement, const QString& filename, int line = -1);

View File

@ -535,6 +535,49 @@ BufferCoord SynEdit::getMatchingBracketEx(BufferCoord APoint)
return BufferCoord{0,0}; return BufferCoord{0,0};
} }
bool SynEdit::GetPositionOfMouse(BufferCoord &aPos)
{
QPoint point = QCursor::pos();
point = mapFromGlobal(point);
return PointToCharLine(point,aPos);
}
bool SynEdit::GetLineOfMouse(int &line)
{
QPoint point = QCursor::pos();
point = mapFromGlobal(point);
return PointToLine(point,line);
}
bool SynEdit::PointToCharLine(const QPoint &point, BufferCoord &coord)
{
// Make sure it fits within the SynEdit bounds (and on the gutter)
if ((point.x() < gutterWidth() + clientLeft())
|| (point.x()>clientWidth()+clientLeft())
|| (point.y() < clientTop())
|| (point.y() > clientTop()+clientHeight())) {
return false;
}
coord = displayToBufferPos(pixelsToRowColumn(point.x(),point.y()));
return true;
}
bool SynEdit::PointToLine(const QPoint &point, int &line)
{
// Make sure it fits within the SynEdit bounds
if ((point.x() < clientLeft())
|| (point.x()>clientWidth()+clientLeft())
|| (point.y() < clientTop())
|| (point.y() > clientTop()+clientHeight())) {
return false;
}
BufferCoord coord = displayToBufferPos(pixelsToRowColumn(point.x(),point.y()));
line = coord.Line;
return true;
}
void SynEdit::invalidateGutter() void SynEdit::invalidateGutter()
{ {
invalidateGutterLines(-1, -1); invalidateGutterLines(-1, -1);

View File

@ -250,6 +250,11 @@ public:
virtual BufferCoord getMatchingBracket(); virtual BufferCoord getMatchingBracket();
virtual BufferCoord getMatchingBracketEx(BufferCoord APoint); virtual BufferCoord getMatchingBracketEx(BufferCoord APoint);
bool GetPositionOfMouse(BufferCoord& aPos);
bool GetLineOfMouse(int& line);
bool PointToCharLine(const QPoint& point, BufferCoord& coord);
bool PointToLine(const QPoint& point, int& line);
// setter && getters // setter && getters
int topLine() const; int topLine() const;