work save
This commit is contained in:
parent
9066bc4897
commit
6020323818
|
@ -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,22 +325,96 @@ void Editor::focusOutEvent(QFocusEvent *event)
|
|||
void Editor::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
bool handled = false;
|
||||
if (!readOnly()) {
|
||||
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
|
||||
//fLastIdCharPressed:=0;
|
||||
mLastIdCharPressed = 0;
|
||||
undoSymbolCompletion(caretX());
|
||||
break;;
|
||||
return;
|
||||
case Qt::Key_Backspace:
|
||||
// remove completed character
|
||||
//fLastIdCharPressed:=0;
|
||||
mLastIdCharPressed = 0;
|
||||
undoSymbolCompletion(caretX()-1);
|
||||
break;;
|
||||
default: {
|
||||
return;
|
||||
}
|
||||
|
||||
QString t = event->text();
|
||||
if (!t.isEmpty()) {
|
||||
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 '\'':
|
||||
|
@ -351,16 +428,14 @@ void Editor::keyPressEvent(QKeyEvent *event)
|
|||
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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue