work save

This commit is contained in:
royqh1979@gmail.com 2021-08-25 08:48:33 +08:00
parent 9066bc4897
commit 6020323818
5 changed files with 286 additions and 54 deletions

View File

@ -60,7 +60,8 @@ Editor::Editor(QWidget *parent, const QString& filename,
mSyntaxErrorColor(QColorConstants::Red), mSyntaxErrorColor(QColorConstants::Red),
mSyntaxWarningColor("orange"), mSyntaxWarningColor("orange"),
mLineCount(0), mLineCount(0),
mActiveBreakpointLine(-1) mActiveBreakpointLine(-1),
mLastIdCharPressed(0)
{ {
if (mFilename.isEmpty()) { if (mFilename.isEmpty()) {
newfileCount++; newfileCount++;
@ -96,6 +97,8 @@ Editor::Editor(QWidget *parent, const QString& filename,
initParser(); initParser();
} }
mCompletionPopup = std::make_shared<CodeCompletionView>();
applySettings(); applySettings();
applyColorScheme(pSettings->editor().colorScheme()); applyColorScheme(pSettings->editor().colorScheme());
@ -322,45 +325,117 @@ void Editor::focusOutEvent(QFocusEvent *event)
void Editor::keyPressEvent(QKeyEvent *event) void Editor::keyPressEvent(QKeyEvent *event)
{ {
bool handled = false; bool handled = false;
if (!readOnly()) { auto action = finally([&,this]{
switch (event->key()) { if (!handled) {
case Qt::Key_Delete: SynEdit::keyPressEvent(event);
// remove completed character } else {
//fLastIdCharPressed:=0; event->accept();
undoSymbolCompletion(caretX()); }
break;; });
case Qt::Key_Backspace: if (readOnly())
// remove completed character return;
//fLastIdCharPressed:=0;
undoSymbolCompletion(caretX()-1); switch (event->key()) {
break;; case Qt::Key_Delete:
default: { // remove completed character
QString t = event->text(); mLastIdCharPressed = 0;
if (!t.isEmpty()) { undoSymbolCompletion(caretX());
QChar ch = t[0]; return;
switch (ch.unicode()) { case Qt::Key_Backspace:
case '"': // remove completed character
case '\'': mLastIdCharPressed = 0;
case '(': undoSymbolCompletion(caretX()-1);
case ')': return;
case '{': }
case '}':
case '[': QString t = event->text();
case ']': if (t.isEmpty())
case '<': return;
case '>':
case '*': QChar ch = t[0];
handled = handleSymbolCompletion(ch); if (isIdentChar(ch)) {
mLastIdCharPressed++;
// if devCodeCompletion.Enabled and devCodeCompletion.ShowCompletionWhileInput then begin
if (mLastIdCharPressed==1) {
if (mParser->isIncludeLine(lineText())) {
// is a #include line
setSelText(ch);
showHeaderCompletion(false);
handled=true;
return;
} else {
QString lastWord = getPreviousWordAtPositionForSuggestion(caretXY());
if (!lastWord.isEmpty()) {
if (CppTypeKeywords.contains(lastWord)) {
//last word is a type keyword, this is a var or param define, and dont show suggestion
// if devEditor.UseTabnine then
// ShowTabnineCompletion;
return;
}
PStatement statement = mParser->findStatementOf(
mFilename,
lastWord,
caretY());
StatementKind kind = mParser->getKindOfStatement(statement);
if (kind == StatementKind::skClass
|| kind == StatementKind::skEnumClassType
|| kind == StatementKind::skEnumType
|| kind == StatementKind::skTypedef) {
//last word is a typedef/class/struct, this is a var or param define, and dont show suggestion
// if devEditor.UseTabnine then
// ShowTabnineCompletion;
return;
}
} }
setSelText(ch);
showCompletion(false);
handled=true;
} }
} }
// }
} else {
//preprocessor ?
if ((mLastIdCharPressed=0) && (ch=='#') && lineText().isEmpty()) {
// and devCodeCompletion.Enabled and devCodeCompletion.ShowCompletionWhileInput
mLastIdCharPressed++;
setSelText(ch);
showCompletion(false);
handled=true;
return;
}
//javadoc directive?
if ((mLastIdCharPressed=0) && (ch=='#') &&
lineText().trimmed().startsWith('*')) {
// and devCodeCompletion.Enabled and devCodeCompletion.ShowCompletionWhileInput
mLastIdCharPressed++;
setSelText(ch);
showCompletion(false);
handled=true;
return;
}
mLastIdCharPressed = 0;
switch (ch.unicode()) {
case '"':
case '\'':
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
case '<':
case '>':
case '*':
handled = handleSymbolCompletion(ch);
return;
} }
} }
if (!handled) {
SynEdit::keyPressEvent(event); // Spawn code completion window if we are allowed to
} else { // if devCodeCompletion.Enabled then begin
event->accept(); handleCodeCompletion(ch);
} // end;
} }
void Editor::onGutterPaint(QPainter &painter, int aLine, int X, int Y) void Editor::onGutterPaint(QPainter &painter, int aLine, int X, int Y)
@ -1068,6 +1143,32 @@ bool Editor::handleGlobalIncludeSkip()
return false; return false;
} }
void Editor::handleCodeCompletion(QChar key)
{
if (!mCompletionPopup->isEnabled())
return;
switch(key.unicode()) {
case '.':
showCompletion(false);
break;
case '>':
if ((caretX() > 1) && (lineText().length() >= 1) &&
(lineText()[caretX() - 2] == '-'))
showCompletion(false);
break;
case ':':
if ((caretX() > 1) && (lineText().length() >= 1) &&
(lineText()[caretX() - 2] == ':'))
showCompletion(false);
break;
case '/':
case '\\':
if (mParser->isIncludeLine(lineText())) {
showHeaderCompletion(false);
}
}
}
void Editor::initParser() void Editor::initParser()
{ {
mParser = std::make_shared<CppParser>(); mParser = std::make_shared<CppParser>();
@ -1193,6 +1294,80 @@ void Editor::reparse()
parseFile(mParser,mFilename,mInProject); parseFile(mParser,mFilename,mInProject);
} }
void Editor::showCompletion(bool autoComplete)
{
// if not devCodeCompletion.Enabled then
// Exit;
if (!mParser->enabled())
return;
if (mCompletionPopup->isVisible()) // already in search, don't do it again
return;
QString word="";
QString s;
PSynHighlighterAttribute attr;
bool tokenFinished;
SynHighlighterTokenType tokenType;
BufferCoord pBeginPos, pEndPos;
if (GetHighlighterAttriAtRowCol(
BufferCoord{caretX() - 1,
caretY()}, s, tokenFinished,tokenType, attr)) {
if (tokenType == SynHighlighterTokenType::PreprocessDirective) {//Preprocessor
word = getWordAtPosition(this, caretXY(),pBeginPos,pEndPos, wpDirective);
if not StartsStr('#',word) then begin
ShowTabnineCompletion;
Exit;
end;
end else if attr = dmMain.Cpp.CommentAttri then begin //Comment, javadoc tag
word:=GetWordAtPosition(fText, fText.CaretXY,pBeginPos,pEndPos, wpJavadoc);
if not StartsStr('@',word) then begin
Exit;
end;
end else if (attr <> fText.Highlighter.SymbolAttribute) and
(attr <> fText.Highlighter.WhitespaceAttribute) and
(attr <> fText.Highlighter.IdentifierAttribute) then begin
Exit;
end;
}
// Position it at the top of the next line
P := fText.RowColumnToPixels(fText.DisplayXY);
Inc(P.Y, fText.LineHeight + 2);
fCompletionBox.Position := fText.ClientToScreen(P);
fCompletionBox.RecordUsage := devCodeCompletion.RecordUsage;
fCompletionBox.SortByScope := devCodeCompletion.SortByScope;
fCompletionBox.ShowKeywords := devCodeCompletion.ShowKeywords;
fCompletionBox.ShowCodeIns := devCodeCompletion.ShowCodeIns;
fCompletionBox.IgnoreCase := devCodeCompletion.IgnoreCase;
fCompletionBox.CodeInsList := dmMain.CodeInserts.ItemList;
fCompletionBox.SymbolUsage := dmMain.SymbolUsage;
fCompletionBox.ShowCount := devCodeCompletion.MaxCount;
//Set Font size;
fCompletionBox.FontSize := fText.Font.Size;
// Redirect key presses to completion box if applicable
fCompletionBox.OnKeyPress := CompletionKeyPress;
fCompletionBox.OnKeyDown := CompletionKeyDown;
fCompletionBox.Parser := fParser;
fCompletionBox.useCppKeyword := fUseCppSyntax;
fCompletionBox.Show;
// Scan the current function body
fCompletionBox.CurrentStatement := fParser.FindAndScanBlockAt(fFileName, fText.CaretY);
if word='' then
word:=GetWordAtPosition(fText, fText.CaretXY,pBeginPos,pEndPos, wpCompletion);
//if not fCompletionBox.Visible then
fCompletionBox.PrepareSearch(word, fFileName,pBeginPos.Line);
// Filter the whole statement list
if fCompletionBox.Search(word, fFileName, autoComplete) then //only one suggestion and it's not input while typing
CompletionInsert(); // if only have one suggestion, just use it
}
const PCppParser &Editor::parser() const const PCppParser &Editor::parser() const
{ {
return mParser; return mParser;

View File

@ -8,6 +8,7 @@
#include "colorscheme.h" #include "colorscheme.h"
#include "common.h" #include "common.h"
#include "parser/cppparser.h" #include "parser/cppparser.h"
#include "widgets/codecompletionview.h"
class SaveException: public std::exception { class SaveException: public std::exception {
@ -48,6 +49,16 @@ public:
RawStringNoEscape RawStringNoEscape
}; };
enum class WordPurpose {
wpCompletion, // walk backwards over words, array, functions, parents, no forward movement
wpEvaluation, // walk backwards over words, array, functions, parents, forwards over words, array
wpHeaderCompletion, // walk backwards over path
wpHeaderCompletionStart, // walk backwards over path, including start '<' or '"'
wpDirective, // preprocessor
wpJavadoc, //javadoc
wpInformation // walk backwards over words, array, functions, parents, forwards over words
};
struct SyntaxIssue { struct SyntaxIssue {
int col; int col;
int endCol; int endCol;
@ -139,11 +150,15 @@ private:
bool handleDoubleQuoteCompletion(); bool handleDoubleQuoteCompletion();
bool handleGlobalIncludeCompletion(); bool handleGlobalIncludeCompletion();
bool handleGlobalIncludeSkip(); bool handleGlobalIncludeSkip();
void handleCodeCompletion(QChar key);
void initParser(); void initParser();
void undoSymbolCompletion(int pos); void undoSymbolCompletion(int pos);
QuoteStatus getQuoteStatus(); QuoteStatus getQuoteStatus();
void reparse(); void reparse();
void showCompletion(bool autoComplete);
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
@ -165,6 +180,8 @@ private:
QSet<int> mBreakpointLines; QSet<int> mBreakpointLines;
int mActiveBreakpointLine; int mActiveBreakpointLine;
PCppParser mParser; PCppParser mParser;
std::shared_ptr<CodeCompletionView> mCompletionPopup;
int mLastIdCharPressed;
// QWidget interface // QWidget interface
protected: protected:

View File

@ -353,6 +353,7 @@ signals:
void tabSizeChanged(); void tabSizeChanged();
protected: protected:
bool isIdentChar(const QChar& ch);
protected: protected:
virtual bool onGetSpecialLineColors(int Line, virtual bool onGetSpecialLineColors(int Line,
@ -366,7 +367,6 @@ protected:
virtual void ExecuteCommand(SynEditorCommand Command, QChar AChar, void * pData); virtual void ExecuteCommand(SynEditorCommand Command, QChar AChar, void * pData);
private: private:
bool isIdentChar(const QChar& ch);
void clearAreaList(SynEditingAreaList areaList); void clearAreaList(SynEditingAreaList areaList);
void computeCaret(int X, int Y); void computeCaret(int X, int Y);
void computeScroll(int X, int Y); void computeScroll(int X, int Y);

View File

@ -9,6 +9,8 @@ CodeCompletionView::CodeCompletionView(QWidget *parent) :
{ {
setWindowFlags(Qt::Popup); setWindowFlags(Qt::Popup);
mListView = new CodeCompletionListView(this); mListView = new CodeCompletionListView(this);
mModel=new CodeCompletionListModel(&mCompletionStatementList);
mListView->setModel(mModel);
setLayout(new QVBoxLayout()); setLayout(new QVBoxLayout());
layout()->addWidget(mListView); layout()->addWidget(mListView);
layout()->setMargin(0); layout()->setMargin(0);
@ -16,17 +18,18 @@ CodeCompletionView::CodeCompletionView(QWidget *parent) :
mShowKeywords=true; mShowKeywords=true;
mUseCppKeyword=true; mUseCppKeyword=true;
mEnabled = true;
mOnlyGlobals = false; mOnlyGlobals = false;
mShowCount = 1000; mShowCount = 1000;
mShowCodeIns = true; mShowCodeIns = true;
mIgnoreCase = false; mIgnoreCase = false;
} }
CodeCompletionView::~CodeCompletionView() CodeCompletionView::~CodeCompletionView()
{ {
delete mListView;
delete mModel;
} }
void CodeCompletionView::setKeypressedCallback(const KeyPressedCallback &newKeypressedCallback) void CodeCompletionView::setKeypressedCallback(const KeyPressedCallback &newKeypressedCallback)
@ -37,7 +40,7 @@ void CodeCompletionView::setKeypressedCallback(const KeyPressedCallback &newKeyp
void CodeCompletionView::prepareSearch(const QString &phrase, const QString &filename, int line) void CodeCompletionView::prepareSearch(const QString &phrase, const QString &filename, int line)
{ {
QMutexLocker locker(&mMutex); QMutexLocker locker(&mMutex);
if (!mEnabled) if (!isEnabled())
return; return;
mPhrase = phrase; mPhrase = phrase;
//Screen.Cursor := crHourglass; //Screen.Cursor := crHourglass;
@ -55,10 +58,9 @@ void CodeCompletionView::prepareSearch(const QString &phrase, const QString &fil
setCursor(oldCursor); setCursor(oldCursor);
} }
bool CodeCompletionView::search(const QString &phrase, const QString &filename, bool autoHideOnSingleResult) bool CodeCompletionView::search(const QString &phrase, bool autoHideOnSingleResult)
{ {
QMutexLocker locker(&mMutex); QMutexLocker locker(&mMutex);
bool result = false;
mPhrase = phrase; mPhrase = phrase;
@ -66,7 +68,7 @@ bool CodeCompletionView::search(const QString &phrase, const QString &filename,
hide(); hide();
return false; return false;
} }
if (!mEnabled) { if (!isEnabled()) {
hide(); hide();
return false; return false;
} }
@ -86,12 +88,10 @@ bool CodeCompletionView::search(const QString &phrase, const QString &filename,
QString symbol = phrase.mid(i); QString symbol = phrase.mid(i);
// filter fFullCompletionStatementList to fCompletionStatementList // filter fFullCompletionStatementList to fCompletionStatementList
filterList(symbol); filterList(symbol);
mModel->notifyUpdated();
setCursor(oldCursor);
if (!mCompletionStatementList.isEmpty()) { if (!mCompletionStatementList.isEmpty()) {
//todo:update model
setCursor(oldCursor);
// if only one suggestion, and is exactly the symbol to search, hide the frame (the search is over) // if only one suggestion, and is exactly the symbol to search, hide the frame (the search is over)
// if only one suggestion and auto hide , don't show the frame // if only one suggestion and auto hide , don't show the frame
if(mCompletionStatementList.count() == 1) if(mCompletionStatementList.count() == 1)
@ -100,10 +100,9 @@ bool CodeCompletionView::search(const QString &phrase, const QString &filename,
return true; return true;
} }
} else { } else {
//todo:update(clear) the model
setCursor(oldCursor);
hide(); hide();
} }
return false;
} }
void CodeCompletionView::addChildren(PStatement scopeStatement, const QString &fileName, int line) void CodeCompletionView::addChildren(PStatement scopeStatement, const QString &fileName, int line)
@ -673,6 +672,7 @@ bool CodeCompletionView::isIncluded(const QString &fileName)
void CodeCompletionView::hideEvent(QHideEvent *event) void CodeCompletionView::hideEvent(QHideEvent *event)
{ {
QMutexLocker locker(&mMutex); QMutexLocker locker(&mMutex);
mListView->setKeypressedCallback(nullptr);
mCompletionStatementList.clear(); mCompletionStatementList.clear();
mFullCompletionStatementList.clear(); mFullCompletionStatementList.clear();
mIncludedFiles.clear(); mIncludedFiles.clear();
@ -688,7 +688,7 @@ CodeCompletionListView::CodeCompletionListView(QWidget *parent) : QListView(pare
void CodeCompletionListView::keyPressEvent(QKeyEvent *event) void CodeCompletionListView::keyPressEvent(QKeyEvent *event)
{ {
if (!mKeypressedCallback(event)) { if (!mKeypressedCallback || !mKeypressedCallback(event)) {
QListView::keyPressEvent(event); QListView::keyPressEvent(event);
} }
} }
@ -702,3 +702,32 @@ void CodeCompletionListView::setKeypressedCallback(const KeyPressedCallback &new
{ {
mKeypressedCallback = newKeypressedCallback; mKeypressedCallback = newKeypressedCallback;
} }
CodeCompletionListModel::CodeCompletionListModel(StatementList *statements, QObject *parent):
QAbstractListModel(parent),
mStatements(statements)
{
}
int CodeCompletionListModel::rowCount(const QModelIndex &parent) const
{
return mStatements->count();
}
QVariant CodeCompletionListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role == Qt::DisplayRole) {
PStatement statement = mStatements->at(index.row());
return statement->command;
}
return QVariant();
}
void CodeCompletionListModel::notifyUpdated()
{
beginResetModel();
endResetModel();
}

View File

@ -22,6 +22,18 @@ private:
KeyPressedCallback mKeypressedCallback; KeyPressedCallback mKeypressedCallback;
}; };
class CodeCompletionListModel : public QAbstractListModel {
Q_OBJECT
public:
explicit CodeCompletionListModel(StatementList* statements,QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
void notifyUpdated();
private:
const StatementList* mStatements;
};
class CodeCompletionView : public QWidget class CodeCompletionView : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -32,8 +44,7 @@ public:
void setKeypressedCallback(const KeyPressedCallback &newKeypressedCallback); void setKeypressedCallback(const KeyPressedCallback &newKeypressedCallback);
void prepareSearch(const QString& phrase, const QString& filename, int line); void prepareSearch(const QString& phrase, const QString& filename, int line);
bool search(const QString& phrase, const QString& filename, bool search(const QString& phrase, bool autoHideOnSingleResult);
bool autoHideOnSingleResult);
private: private:
@ -45,12 +56,12 @@ private:
bool isIncluded(const QString& fileName); bool isIncluded(const QString& fileName);
private: private:
CodeCompletionListView * mListView; CodeCompletionListView * mListView;
CodeCompletionListModel* mModel;
QList<PCodeIns> mCodeInsList; //(Code template list) QList<PCodeIns> mCodeInsList; //(Code template list)
//QList<PStatement> mCodeInsStatements; //temporary (user code template) statements created when show code suggestion //QList<PStatement> mCodeInsStatements; //temporary (user code template) statements created when show code suggestion
PCppParser mParser; PCppParser mParser;
QList<PStatement> mFullCompletionStatementList; StatementList mFullCompletionStatementList;
QList<PStatement> mCompletionStatementList; StatementList mCompletionStatementList;
bool mEnabled;
int mShowCount; int mShowCount;
bool mOnlyGlobals; bool mOnlyGlobals;
PStatement mCurrentStatement; PStatement mCurrentStatement;