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),
mSyntaxWarningColor("orange"),
mLineCount(0),
mActiveBreakpointLine(-1)
mActiveBreakpointLine(-1),
mLastIdCharPressed(0)
{
if (mFilename.isEmpty()) {
newfileCount++;
@ -96,6 +97,8 @@ Editor::Editor(QWidget *parent, const QString& filename,
initParser();
}
mCompletionPopup = std::make_shared<CodeCompletionView>();
applySettings();
applyColorScheme(pSettings->editor().colorScheme());
@ -322,45 +325,117 @@ void Editor::focusOutEvent(QFocusEvent *event)
void Editor::keyPressEvent(QKeyEvent *event)
{
bool handled = false;
if (!readOnly()) {
switch (event->key()) {
case Qt::Key_Delete:
// remove completed character
//fLastIdCharPressed:=0;
undoSymbolCompletion(caretX());
break;;
case Qt::Key_Backspace:
// remove completed character
//fLastIdCharPressed:=0;
undoSymbolCompletion(caretX()-1);
break;;
default: {
QString t = event->text();
if (!t.isEmpty()) {
QChar ch = t[0];
switch (ch.unicode()) {
case '"':
case '\'':
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
case '<':
case '>':
case '*':
handled = handleSymbolCompletion(ch);
auto action = finally([&,this]{
if (!handled) {
SynEdit::keyPressEvent(event);
} else {
event->accept();
}
});
if (readOnly())
return;
switch (event->key()) {
case Qt::Key_Delete:
// remove completed character
mLastIdCharPressed = 0;
undoSymbolCompletion(caretX());
return;
case Qt::Key_Backspace:
// remove completed character
mLastIdCharPressed = 0;
undoSymbolCompletion(caretX()-1);
return;
}
QString t = event->text();
if (t.isEmpty())
return;
QChar ch = t[0];
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);
} else {
event->accept();
}
// Spawn code completion window if we are allowed to
// if devCodeCompletion.Enabled then begin
handleCodeCompletion(ch);
// end;
}
void Editor::onGutterPaint(QPainter &painter, int aLine, int X, int Y)
@ -1068,6 +1143,32 @@ bool Editor::handleGlobalIncludeSkip()
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()
{
mParser = std::make_shared<CppParser>();
@ -1193,6 +1294,80 @@ void Editor::reparse()
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
{
return mParser;

View File

@ -8,6 +8,7 @@
#include "colorscheme.h"
#include "common.h"
#include "parser/cppparser.h"
#include "widgets/codecompletionview.h"
class SaveException: public std::exception {
@ -48,6 +49,16 @@ public:
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 {
int col;
int endCol;
@ -139,11 +150,15 @@ private:
bool handleDoubleQuoteCompletion();
bool handleGlobalIncludeCompletion();
bool handleGlobalIncludeSkip();
void handleCodeCompletion(QChar key);
void initParser();
void undoSymbolCompletion(int pos);
QuoteStatus getQuoteStatus();
void reparse();
void showCompletion(bool autoComplete);
private:
static int newfileCount;
QByteArray mEncodingOption; // the encoding type set by the user
@ -165,6 +180,8 @@ private:
QSet<int> mBreakpointLines;
int mActiveBreakpointLine;
PCppParser mParser;
std::shared_ptr<CodeCompletionView> mCompletionPopup;
int mLastIdCharPressed;
// QWidget interface
protected:

View File

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

View File

@ -9,6 +9,8 @@ CodeCompletionView::CodeCompletionView(QWidget *parent) :
{
setWindowFlags(Qt::Popup);
mListView = new CodeCompletionListView(this);
mModel=new CodeCompletionListModel(&mCompletionStatementList);
mListView->setModel(mModel);
setLayout(new QVBoxLayout());
layout()->addWidget(mListView);
layout()->setMargin(0);
@ -16,17 +18,18 @@ CodeCompletionView::CodeCompletionView(QWidget *parent) :
mShowKeywords=true;
mUseCppKeyword=true;
mEnabled = true;
mOnlyGlobals = false;
mShowCount = 1000;
mShowCodeIns = true;
mIgnoreCase = false;
}
CodeCompletionView::~CodeCompletionView()
{
delete mListView;
delete mModel;
}
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)
{
QMutexLocker locker(&mMutex);
if (!mEnabled)
if (!isEnabled())
return;
mPhrase = phrase;
//Screen.Cursor := crHourglass;
@ -55,10 +58,9 @@ void CodeCompletionView::prepareSearch(const QString &phrase, const QString &fil
setCursor(oldCursor);
}
bool CodeCompletionView::search(const QString &phrase, const QString &filename, bool autoHideOnSingleResult)
bool CodeCompletionView::search(const QString &phrase, bool autoHideOnSingleResult)
{
QMutexLocker locker(&mMutex);
bool result = false;
mPhrase = phrase;
@ -66,7 +68,7 @@ bool CodeCompletionView::search(const QString &phrase, const QString &filename,
hide();
return false;
}
if (!mEnabled) {
if (!isEnabled()) {
hide();
return false;
}
@ -86,12 +88,10 @@ bool CodeCompletionView::search(const QString &phrase, const QString &filename,
QString symbol = phrase.mid(i);
// filter fFullCompletionStatementList to fCompletionStatementList
filterList(symbol);
mModel->notifyUpdated();
setCursor(oldCursor);
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 auto hide , don't show the frame
if(mCompletionStatementList.count() == 1)
@ -100,10 +100,9 @@ bool CodeCompletionView::search(const QString &phrase, const QString &filename,
return true;
}
} else {
//todo:update(clear) the model
setCursor(oldCursor);
hide();
}
return false;
}
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)
{
QMutexLocker locker(&mMutex);
mListView->setKeypressedCallback(nullptr);
mCompletionStatementList.clear();
mFullCompletionStatementList.clear();
mIncludedFiles.clear();
@ -688,7 +688,7 @@ CodeCompletionListView::CodeCompletionListView(QWidget *parent) : QListView(pare
void CodeCompletionListView::keyPressEvent(QKeyEvent *event)
{
if (!mKeypressedCallback(event)) {
if (!mKeypressedCallback || !mKeypressedCallback(event)) {
QListView::keyPressEvent(event);
}
}
@ -702,3 +702,32 @@ void CodeCompletionListView::setKeypressedCallback(const KeyPressedCallback &new
{
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;
};
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
{
Q_OBJECT
@ -32,8 +44,7 @@ public:
void setKeypressedCallback(const KeyPressedCallback &newKeypressedCallback);
void prepareSearch(const QString& phrase, const QString& filename, int line);
bool search(const QString& phrase, const QString& filename,
bool autoHideOnSingleResult);
bool search(const QString& phrase, bool autoHideOnSingleResult);
private:
@ -45,12 +56,12 @@ private:
bool isIncluded(const QString& fileName);
private:
CodeCompletionListView * mListView;
CodeCompletionListModel* mModel;
QList<PCodeIns> mCodeInsList; //(Code template list)
//QList<PStatement> mCodeInsStatements; //temporary (user code template) statements created when show code suggestion
PCppParser mParser;
QList<PStatement> mFullCompletionStatementList;
QList<PStatement> mCompletionStatementList;
bool mEnabled;
StatementList mFullCompletionStatementList;
StatementList mCompletionStatementList;
int mShowCount;
bool mOnlyGlobals;
PStatement mCurrentStatement;