- enhancement: code completion find words with char in the middle
This commit is contained in:
parent
8e179dd659
commit
b114eb216c
1
NEWS.md
1
NEWS.md
|
@ -9,6 +9,7 @@ Red Panda C++ Version 0.13.4
|
|||
- enhancement: when there are tips showing, don't show mouse tips
|
||||
- enhancement: setting non-ascii font for editors
|
||||
- enhancement: correct handle windows dpi change event
|
||||
- enhancement: code completion find words with char in the middle
|
||||
|
||||
Red Panda C++ Version 0.13.3
|
||||
- enhancement: restore editor position after rename symbol
|
||||
|
|
|
@ -2693,7 +2693,6 @@ void Editor::showCompletion(const QString& preWord,bool autoComplete)
|
|||
// fCompletionBox.ShowCount := devCodeCompletion.MaxCount;
|
||||
//Set Font size;
|
||||
mCompletionPopup->setFont(font());
|
||||
|
||||
// Redirect key presses to completion box if applicable
|
||||
//todo:
|
||||
mCompletionPopup->setKeypressedCallback([this](QKeyEvent *event)->bool{
|
||||
|
|
|
@ -125,6 +125,14 @@ enum class EvalStatementKind {
|
|||
|
||||
using PRemovedStatement = std::shared_ptr<RemovedStatement>;
|
||||
|
||||
struct StatementMatchPosition{
|
||||
int start;
|
||||
int end;
|
||||
};
|
||||
|
||||
using PStatementMathPosition = std::shared_ptr<StatementMatchPosition>;
|
||||
|
||||
|
||||
struct Statement;
|
||||
using PStatement = std::shared_ptr<Statement>;
|
||||
using StatementList = QList<PStatement>;
|
||||
|
@ -157,10 +165,14 @@ struct Statement {
|
|||
bool isInherited; // inherted member;
|
||||
QString fullName; // fullname(including class and namespace), ClassA::foo
|
||||
QSet<QString> usingList; // using namespaces
|
||||
int usageCount; //Usage Count, used by TCodeCompletion
|
||||
int freqTop; // Usage Count Rank, used by TCodeCompletion
|
||||
bool caseMatch; // if match with case, used by TCodeCompletion
|
||||
QString noNameArgs;// Args without name
|
||||
// fields for code completion
|
||||
int usageCount; //Usage Count
|
||||
int freqTop; // Usage Count Rank
|
||||
int matchPosTotal; // total of matched positions
|
||||
int firstMatchLength; // length of first match;
|
||||
int caseMatched; // if match with case
|
||||
QList<PStatementMathPosition> matchPositions;
|
||||
};
|
||||
|
||||
struct EvalStatement;
|
||||
|
|
|
@ -19,8 +19,13 @@
|
|||
#include "../editor.h"
|
||||
#include "../editorlist.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QTextDocument>
|
||||
#include <qabstracttextdocumentlayout.h>
|
||||
|
||||
CodeCompletionListView::CodeCompletionListView(QWidget *parent) : QListView(parent)
|
||||
{
|
||||
setItemDelegate(&mDelegate);
|
||||
}
|
||||
|
||||
void CodeCompletionListView::keyPressEvent(QKeyEvent *event)
|
||||
|
@ -58,3 +63,53 @@ void CodeCompletionListView::setKeypressedCallback(const KeyPressedCallback &new
|
|||
mKeypressedCallback = newKeypressedCallback;
|
||||
}
|
||||
|
||||
|
||||
void CodeCompletionListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QVariant data = index.data();
|
||||
if (data.canConvert<QString>()) {
|
||||
painter->save();
|
||||
QString richText = qvariant_cast<QString>(data);
|
||||
|
||||
if (option.state & QStyle::State_Selected)
|
||||
painter->fillRect(option.rect, option.palette.highlight());
|
||||
|
||||
QColor color = index.data(Qt::ForegroundRole).value<QColor>();
|
||||
if (!color.isValid()) {
|
||||
color = option.palette.color(QPalette::Text);
|
||||
}
|
||||
painter->setPen(color);
|
||||
QTextDocument doc;
|
||||
doc.setHtml(richText);
|
||||
doc.setDefaultFont(painter->font());
|
||||
QTransform transform;
|
||||
transform.translate(option.rect.left(),option.rect.top());
|
||||
painter->setTransform(transform);
|
||||
QRect clipRect = option.rect;
|
||||
clipRect.moveTopLeft(QPoint(0,0));
|
||||
painter->setClipRect(clipRect);
|
||||
QAbstractTextDocumentLayout::PaintContext ctx;
|
||||
|
||||
ctx.palette.setColor(QPalette::Text, color);
|
||||
ctx.clip = clipRect;
|
||||
doc.documentLayout()->draw(painter,ctx);
|
||||
painter->restore();
|
||||
} else {
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
}
|
||||
}
|
||||
|
||||
QSize CodeCompletionListItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QVariant data = index.data();
|
||||
if (data.canConvert<QString>()) {
|
||||
QString richText = qvariant_cast<QString>(data);
|
||||
|
||||
QTextDocument doc;
|
||||
doc.setHtml(richText);
|
||||
return QSize(doc.size().width(),doc.size().height());
|
||||
} else {
|
||||
return QStyledItemDelegate::sizeHint(option, index);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,10 +19,21 @@
|
|||
|
||||
#include <QListView>
|
||||
#include <QKeyEvent>
|
||||
#include <QStyledItemDelegate>
|
||||
#include "../parser/parserutils.h"
|
||||
using KeyPressedCallback = std::function<bool (QKeyEvent *)>;
|
||||
using InputMethodCallback = std::function<bool (QInputMethodEvent*)>;
|
||||
|
||||
class CodeCompletionListItemDelegate: public QStyledItemDelegate {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
// QAbstractItemDelegate interface
|
||||
public:
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
class CodeCompletionListView: public QListView {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -39,6 +50,7 @@ protected:
|
|||
void keyPressEvent(QKeyEvent *event) override;
|
||||
private:
|
||||
KeyPressedCallback mKeypressedCallback;
|
||||
CodeCompletionListItemDelegate mDelegate;
|
||||
|
||||
// QWidget interface
|
||||
protected:
|
||||
|
|
|
@ -216,9 +216,19 @@ static bool nameComparator(PStatement statement1,PStatement statement2) {
|
|||
}
|
||||
|
||||
static bool defaultComparator(PStatement statement1,PStatement statement2) {
|
||||
if (statement1->caseMatch && !statement2->caseMatch) {
|
||||
if (statement1->firstMatchLength > statement2->firstMatchLength) {
|
||||
return true;
|
||||
} else if (!statement1->caseMatch && statement2->caseMatch) {
|
||||
} else if (statement1->firstMatchLength < statement2->firstMatchLength) {
|
||||
return false;
|
||||
}
|
||||
if (statement1->matchPosTotal < statement2->matchPosTotal) {
|
||||
return true;
|
||||
} else if (statement1->matchPosTotal > statement2->matchPosTotal) {
|
||||
return false;
|
||||
}
|
||||
if (statement1->caseMatched > statement2->caseMatched) {
|
||||
return true;
|
||||
} else if (statement1->caseMatched < statement2->caseMatched) {
|
||||
return false;
|
||||
}
|
||||
// Show user template first
|
||||
|
@ -241,9 +251,19 @@ static bool defaultComparator(PStatement statement1,PStatement statement2) {
|
|||
}
|
||||
|
||||
static bool sortByScopeComparator(PStatement statement1,PStatement statement2){
|
||||
if (statement1->caseMatch && !statement2->caseMatch) {
|
||||
if (statement1->firstMatchLength > statement2->firstMatchLength) {
|
||||
return true;
|
||||
} else if (!statement1->caseMatch && statement2->caseMatch) {
|
||||
} else if (statement1->firstMatchLength < statement2->firstMatchLength) {
|
||||
return false;
|
||||
}
|
||||
if (statement1->matchPosTotal < statement2->matchPosTotal) {
|
||||
return true;
|
||||
} else if (statement1->matchPosTotal > statement2->matchPosTotal) {
|
||||
return false;
|
||||
}
|
||||
if (statement1->caseMatched > statement2->caseMatched) {
|
||||
return true;
|
||||
} else if (statement1->caseMatched < statement2->caseMatched) {
|
||||
return false;
|
||||
}
|
||||
// Show user template first
|
||||
|
@ -279,9 +299,19 @@ static bool sortByScopeComparator(PStatement statement1,PStatement statement2){
|
|||
}
|
||||
|
||||
static bool sortWithUsageComparator(PStatement statement1,PStatement statement2) {
|
||||
if (statement1->caseMatch && !statement2->caseMatch) {
|
||||
if (statement1->firstMatchLength > statement2->firstMatchLength) {
|
||||
return true;
|
||||
} else if (!statement1->caseMatch && statement2->caseMatch) {
|
||||
} else if (statement1->firstMatchLength < statement2->firstMatchLength) {
|
||||
return false;
|
||||
}
|
||||
if (statement1->matchPosTotal < statement2->matchPosTotal) {
|
||||
return true;
|
||||
} else if (statement1->matchPosTotal > statement2->matchPosTotal) {
|
||||
return false;
|
||||
}
|
||||
if (statement1->caseMatched > statement2->caseMatched) {
|
||||
return true;
|
||||
} else if (statement1->caseMatched < statement2->caseMatched) {
|
||||
return false;
|
||||
}
|
||||
// Show user template first
|
||||
|
@ -309,9 +339,19 @@ static bool sortWithUsageComparator(PStatement statement1,PStatement statement2)
|
|||
}
|
||||
|
||||
static bool sortByScopeWithUsageComparator(PStatement statement1,PStatement statement2){
|
||||
if (statement1->caseMatch && !statement2->caseMatch) {
|
||||
if (statement1->firstMatchLength > statement2->firstMatchLength) {
|
||||
return true;
|
||||
} else if (!statement1->caseMatch && statement2->caseMatch) {
|
||||
} else if (statement1->firstMatchLength < statement2->firstMatchLength) {
|
||||
return false;
|
||||
}
|
||||
if (statement1->matchPosTotal < statement2->matchPosTotal) {
|
||||
return true;
|
||||
} else if (statement1->matchPosTotal > statement2->matchPosTotal) {
|
||||
return false;
|
||||
}
|
||||
if (statement1->caseMatched > statement2->caseMatched) {
|
||||
return true;
|
||||
} else if (statement1->caseMatched < statement2->caseMatched) {
|
||||
return false;
|
||||
}
|
||||
// Show user template first
|
||||
|
@ -372,25 +412,66 @@ void CodeCompletionPopup::filterList(const QString &member)
|
|||
// }
|
||||
|
||||
mCompletionStatementList.clear();
|
||||
if (!member.isEmpty()) { // filter
|
||||
mCompletionStatementList.reserve(mFullCompletionStatementList.size());
|
||||
foreach (const PStatement& statement, mFullCompletionStatementList) {
|
||||
Qt::CaseSensitivity cs = (mIgnoreCase?
|
||||
Qt::CaseInsensitive:
|
||||
Qt::CaseSensitive);
|
||||
if (statement->command.startsWith(member, cs)) {
|
||||
if (mIgnoreCase) {
|
||||
statement->caseMatch =
|
||||
statement->command.startsWith(
|
||||
member,Qt::CaseSensitive);
|
||||
} else {
|
||||
statement->caseMatch = true;
|
||||
}
|
||||
mCompletionStatementList.append(statement);
|
||||
mCompletionStatementList.reserve(mFullCompletionStatementList.size());
|
||||
foreach (const PStatement& statement, mFullCompletionStatementList) {
|
||||
Qt::CaseSensitivity cs = (mIgnoreCase?
|
||||
Qt::CaseInsensitive:
|
||||
Qt::CaseSensitive);
|
||||
|
||||
int matched = 0;
|
||||
int caseMatched = 0;
|
||||
QString command = statement->command;
|
||||
int pos = 0;
|
||||
int lastPos = -10;
|
||||
int totalPos = 0;
|
||||
statement->matchPositions.clear();
|
||||
foreach (const QChar& ch, member) {
|
||||
if (mIgnoreCase)
|
||||
pos = command.indexOf(ch,pos,Qt::CaseInsensitive);
|
||||
else
|
||||
pos = command.indexOf(ch,pos,Qt::CaseSensitive);
|
||||
if (pos<0) {
|
||||
break;
|
||||
}
|
||||
if (pos == lastPos+1) {
|
||||
statement->matchPositions.last()->end++;
|
||||
} else {
|
||||
PStatementMathPosition matchPosition=std::make_shared<StatementMatchPosition>();
|
||||
matchPosition->start = pos;
|
||||
matchPosition->end = pos+1;
|
||||
statement->matchPositions.append(matchPosition);
|
||||
}
|
||||
if (ch==command[pos])
|
||||
caseMatched++;
|
||||
matched++;
|
||||
totalPos += pos;
|
||||
lastPos = pos;
|
||||
pos+=1;
|
||||
}
|
||||
} else
|
||||
mCompletionStatementList.append(mFullCompletionStatementList);
|
||||
|
||||
if (mIgnoreCase && matched==member.length()) {
|
||||
statement->caseMatched = caseMatched;
|
||||
statement->matchPosTotal = totalPos;
|
||||
if (member.length()>0)
|
||||
statement->firstMatchLength = statement->matchPositions.front()->end - statement->matchPositions.front()->start;
|
||||
else
|
||||
statement->firstMatchLength = 0;
|
||||
mCompletionStatementList.append(statement);
|
||||
} else if (caseMatched == member.length()) {
|
||||
statement->caseMatched = caseMatched;
|
||||
statement->matchPosTotal = totalPos;
|
||||
if (member.length()>0)
|
||||
statement->firstMatchLength = statement->matchPositions.front()->end - statement->matchPositions.front()->start;
|
||||
else
|
||||
statement->firstMatchLength = 0;
|
||||
mCompletionStatementList.append(statement);
|
||||
} else {
|
||||
statement->matchPositions.clear();
|
||||
statement->caseMatched = 0;
|
||||
statement->matchPosTotal = 0;
|
||||
statement->firstMatchLength = 0;
|
||||
}
|
||||
}
|
||||
if (mRecordUsage) {
|
||||
int topCount = 0;
|
||||
int secondCount = 0;
|
||||
|
@ -959,7 +1040,12 @@ QVariant CodeCompletionListModel::data(const QModelIndex &index, int role) const
|
|||
switch(role) {
|
||||
case Qt::DisplayRole: {
|
||||
PStatement statement = mStatements->at(index.row());
|
||||
return statement->command;
|
||||
QString text = statement->command;
|
||||
for (int i = statement->matchPositions.size()-1;i>=0;i--) {
|
||||
text.insert(statement->matchPositions[i]->end,"</b></u>");
|
||||
text.insert(statement->matchPositions[i]->start,"<u><b>");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
case Qt::ForegroundRole: {
|
||||
PStatement statement = mStatements->at(index.row());
|
||||
|
|
Loading…
Reference in New Issue