RedPanda-CPP/RedPandaIDE/editor.cpp

1751 lines
56 KiB
C++
Raw Normal View History

2021-04-06 23:10:57 +08:00
#include "editor.h"
#include <QtCore/QFileInfo>
2021-04-09 17:48:25 +08:00
#include <QFont>
#include <QTextCodec>
2021-04-07 22:44:08 +08:00
#include <QVariant>
2021-04-09 17:48:25 +08:00
#include <QWheelEvent>
2021-04-07 21:13:15 +08:00
#include <memory>
#include "settings.h"
#include "mainwindow.h"
2021-04-11 21:33:08 +08:00
#include "systemconsts.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QDebug>
#include <QMimeData>
2021-05-24 00:41:00 +08:00
#include "qsynedit/highlighter/cpp.h"
2021-05-26 00:04:20 +08:00
#include "HighlighterManager.h"
#include "qsynedit/exporter/synrtfexporter.h"
#include "qsynedit/exporter/synhtmlexporter.h"
#include "qsynedit/Constants.h"
#include <QGuiApplication>
#include <QClipboard>
#include <QPainter>
#include "iconsmanager.h"
2021-08-01 09:13:38 +08:00
#include "debugger.h"
2021-08-23 10:16:06 +08:00
#include "editorlist.h"
2021-04-07 21:13:15 +08:00
using namespace std;
2021-04-06 23:10:57 +08:00
2021-04-11 21:33:08 +08:00
SaveException::SaveException(const QString& reason) {
mReason = reason;
}
SaveException::SaveException(const QString&& reason) {
mReason = reason;
}
const QString& SaveException::reason() const noexcept{
return mReason;
}
const char *SaveException::what() const noexcept {
return mReason.toLocal8Bit();
}
2021-05-26 00:04:20 +08:00
int Editor::newfileCount=0;
2021-04-06 23:10:57 +08:00
Editor::Editor(QWidget *parent):
Editor(parent,QObject::tr("untitled"),ENCODING_SYSTEM_DEFAULT,false,true,nullptr)
{
}
2021-04-09 17:48:25 +08:00
Editor::Editor(QWidget *parent, const QString& filename,
const QByteArray& encoding,
2021-04-06 23:10:57 +08:00
bool inProject, bool isNew,
QTabWidget* parentPageControl):
2021-05-24 00:41:00 +08:00
SynEdit(parent),
mEncodingOption(encoding),
2021-06-20 14:30:47 +08:00
mFilename(filename),
mParentPageControl(parentPageControl),
2021-04-06 23:10:57 +08:00
mInProject(inProject),
mIsNew(isNew),
mSyntaxErrorColor(QColorConstants::Red),
2021-07-26 00:22:08 +08:00
mSyntaxWarningColor("orange"),
mLineCount(0),
2021-08-25 08:48:33 +08:00
mActiveBreakpointLine(-1),
mLastIdCharPressed(0)
2021-04-06 23:10:57 +08:00
{
2021-04-07 21:13:15 +08:00
if (mFilename.isEmpty()) {
2021-05-26 00:04:20 +08:00
newfileCount++;
mFilename = tr("untitled%1").arg(newfileCount);
2021-04-07 21:13:15 +08:00
}
2021-04-06 23:10:57 +08:00
QFileInfo fileInfo(mFilename);
if (mParentPageControl!=nullptr) {
mParentPageControl->addTab(this,QString());
updateCaption();
}
PSynHighlighter highlighter;
2021-04-06 23:10:57 +08:00
if (!isNew) {
loadFile();
2021-06-07 11:02:03 +08:00
highlighter = highlighterManager.getHighlighter(mFilename);
2021-04-06 23:10:57 +08:00
} else {
if (mEncodingOption == ENCODING_AUTO_DETECT)
mFileEncoding = ENCODING_ASCII;
2021-04-07 21:13:15 +08:00
else
mFileEncoding = mEncodingOption;
2021-06-07 11:02:03 +08:00
highlighter=highlighterManager.getCppHighlighter();
}
if (highlighter) {
setHighlighter(highlighter);
setUseCodeFolding(true);
} else {
setUseCodeFolding(false);
2021-04-06 23:10:57 +08:00
}
2021-04-09 17:48:25 +08:00
2021-08-23 17:27:17 +08:00
if (inProject) {
//todo:
} else {
initParser();
}
2021-08-25 08:48:33 +08:00
mCompletionPopup = std::make_shared<CodeCompletionView>();
2021-06-07 11:02:03 +08:00
applySettings();
2021-06-20 14:30:47 +08:00
applyColorScheme(pSettings->editor().colorScheme());
2021-06-10 09:34:59 +08:00
connect(this,&SynEdit::statusChanged,this,&Editor::onStatusChanged);
connect(this,&SynEdit::gutterClicked,this,&Editor::onGutterClicked);
2021-08-23 17:27:17 +08:00
onStatusChanged(SynStatusChange::scOpenFile);
2021-04-07 22:44:08 +08:00
}
2021-04-06 23:10:57 +08:00
2021-04-07 22:44:08 +08:00
Editor::~Editor() {
if (mParentPageControl!=nullptr) {
2021-04-09 17:48:25 +08:00
int index = mParentPageControl->indexOf(this);
mParentPageControl->removeTab(index);
}
this->setParent(0);
2021-04-06 23:10:57 +08:00
}
void Editor::loadFile() {
QFile file(mFilename);
2021-05-24 00:41:00 +08:00
this->lines()->LoadFromFile(file,mEncodingOption,mFileEncoding);
2021-05-24 18:11:07 +08:00
this->setModified(false);
updateCaption();
pMainWindow->updateForEncodingInfo();
2021-06-25 12:40:11 +08:00
if (pSettings->editor().syntaxCheck() && pSettings->editor().syntaxCheckWhenSave())
pMainWindow->checkSyntaxInBack(this);
2021-04-07 21:13:15 +08:00
}
void Editor::saveFile(const QString &filename) {
QFile file(filename);
2021-05-24 00:41:00 +08:00
this->lines()->SaveToFile(file,mEncodingOption,mFileEncoding);
pMainWindow->updateForEncodingInfo();
2021-06-25 12:40:11 +08:00
if (pSettings->editor().syntaxCheck() && pSettings->editor().syntaxCheckWhenSave())
pMainWindow->checkSyntaxInBack(this);
}
void Editor::convertToEncoding(const QByteArray &encoding)
{
mEncodingOption = encoding;
setModified(true);
save();
2021-04-06 23:10:57 +08:00
}
2021-08-23 10:16:06 +08:00
bool Editor::save(bool force, bool doReparse) {
if (this->mIsNew) {
return saveAs();
}
QFileInfo info(mFilename);
2021-04-11 21:33:08 +08:00
//is this file writable;
if (!force && !info.isWritable()) {
QMessageBox::critical(pMainWindow,tr("Error"),
2021-06-20 22:54:16 +08:00
tr("File %1 is not writable!").arg(mFilename));
return false;
}
2021-05-24 00:41:00 +08:00
if (this->modified()|| force) {
2021-04-11 21:33:08 +08:00
try {
saveFile(mFilename);
setModified(false);
mIsNew = false;
this->updateCaption();
2021-04-11 21:33:08 +08:00
} catch (SaveException& exception) {
2021-08-23 10:16:06 +08:00
if (!force) {
QMessageBox::critical(pMainWindow,tr("Error"),
2021-04-11 21:33:08 +08:00
exception.reason());
2021-08-23 10:16:06 +08:00
}
2021-04-11 21:33:08 +08:00
return false;
}
}
2021-08-23 10:16:06 +08:00
if (doReparse && mParser) {
reparse();
}
return true;
}
bool Editor::saveAs(){
2021-04-11 21:33:08 +08:00
QString selectedFileFilter = pSystemConsts->defaultFileFilter();
QString newName = QFileDialog::getSaveFileName(pMainWindow,
2021-04-11 21:33:08 +08:00
tr("Save As"), QString(), pSystemConsts->defaultFileFilters().join(";;"),
&selectedFileFilter);
if (newName.isEmpty()) {
return false;
}
2021-04-11 21:33:08 +08:00
try {
mFilename = newName;
saveFile(mFilename);
2021-04-11 21:33:08 +08:00
mIsNew = false;
setModified(false);
this->updateCaption();
2021-04-11 21:33:08 +08:00
} catch (SaveException& exception) {
QMessageBox::critical(pMainWindow,tr("Error"),
2021-04-11 21:33:08 +08:00
exception.reason());
return false;
}
//todo: update (reassign highlighter)
//todo: remove old file from parser and reparse file
//todo: unmoniter/ monitor file
//todo: update windows caption
//todo: update class browser;
return true;
2021-04-06 23:10:57 +08:00
}
void Editor::activate()
{
if (mParentPageControl!=nullptr)
mParentPageControl->setCurrentWidget(this);
setFocus();
}
2021-04-11 21:33:08 +08:00
const QByteArray& Editor::encodingOption() const noexcept{
return mEncodingOption;
2021-04-06 23:10:57 +08:00
}
2021-04-11 21:33:08 +08:00
void Editor::setEncodingOption(const QByteArray& encoding) noexcept{
mEncodingOption = encoding;
2021-06-20 09:27:37 +08:00
if (!isNew())
loadFile();
else
pMainWindow->updateForEncodingInfo();
}
2021-04-11 21:33:08 +08:00
const QByteArray& Editor::fileEncoding() const noexcept{
2021-04-06 23:10:57 +08:00
return mFileEncoding;
}
2021-04-11 21:33:08 +08:00
const QString& Editor::filename() const noexcept{
2021-04-06 23:10:57 +08:00
return mFilename;
}
2021-04-11 21:33:08 +08:00
bool Editor::inProject() const noexcept{
2021-04-06 23:10:57 +08:00
return mInProject;
}
2021-04-11 21:33:08 +08:00
bool Editor::isNew() const noexcept {
2021-04-06 23:10:57 +08:00
return mIsNew;
}
2021-04-09 17:48:25 +08:00
2021-04-11 21:33:08 +08:00
QTabWidget* Editor::pageControl() noexcept{
2021-04-09 17:48:25 +08:00
return mParentPageControl;
}
void Editor::undoSymbolCompletion(int pos)
{
PSynHighlighterAttribute Attr;
QString Token;
bool tokenFinished;
SynHighlighterTokenType tokenType;
if (!highlighter())
return;
if (!pSettings->editor().removeSymbolPairs())
return;
if (!GetHighlighterAttriAtRowCol(caretXY(), Token, tokenFinished, tokenType, Attr))
return;
if ((tokenType == SynHighlighterTokenType::Comment) && (!tokenFinished))
return ;
//convert caret x to string index;
pos--;
if (pos<0 || pos+1>=lineText().length())
return;
QChar DeletedChar = lineText()[pos];
QChar NextChar = lineText()[pos+1];
if ((tokenType == SynHighlighterTokenType::Character) && (DeletedChar != '\''))
return;
if (tokenType == SynHighlighterTokenType::StringEscapeSequence)
return;
if (tokenType == SynHighlighterTokenType::String) {
if ((DeletedChar!='"') && (DeletedChar!='('))
return;
if ((DeletedChar=='"') && (Token!="\"\""))
return;
if ((DeletedChar=='(') && (!Token.startsWith("R\"")))
return;
}
if ((DeletedChar == '\'') && (tokenType == SynHighlighterTokenType::Number))
return;
if ((DeletedChar == '<') &&
((tokenType != SynHighlighterTokenType::PreprocessDirective)
|| !lineText().startsWith("#include")))
return;
if ( (pSettings->editor().completeBracket() && (DeletedChar == '[') && (NextChar == ']')) ||
(pSettings->editor().completeParenthese() && (DeletedChar == '(') && (NextChar == ')')) ||
(pSettings->editor().completeGlobalInclude() && (DeletedChar == '<') && (NextChar == '>')) ||
(pSettings->editor().completeBrace() && (DeletedChar == '{') && (NextChar == '}')) ||
(pSettings->editor().completeSingleQuote() && (DeletedChar == '\'') && (NextChar == '\'')) ||
(pSettings->editor().completeDoubleQuote() && (DeletedChar == '\"') && (NextChar == '\"'))) {
CommandProcessor(SynEditorCommand::ecDeleteChar);
}
}
2021-04-09 17:48:25 +08:00
void Editor::wheelEvent(QWheelEvent *event) {
if ( (event->modifiers() & Qt::ControlModifier)!=0) {
int size = pSettings->editor().fontSize();
2021-04-09 17:48:25 +08:00
if (event->angleDelta().y()>0) {
size = std::min(99,size+1);
pSettings->editor().setFontSize(size);
pMainWindow->updateEditorSettings();
event->accept();
return;
2021-07-01 19:44:38 +08:00
} else if (event->angleDelta().y()<0) {
size = std::max(2,size-1);
pSettings->editor().setFontSize(size);
pMainWindow->updateEditorSettings();
event->accept();
return;
2021-04-09 17:48:25 +08:00
}
}
SynEdit::wheelEvent(event);
}
void Editor::focusInEvent(QFocusEvent *event)
{
SynEdit::focusInEvent(event);
pMainWindow->updateEditorActions();
pMainWindow->updateStatusbarForLineCol();
pMainWindow->updateForStatusbarModeInfo();
2021-08-23 17:27:17 +08:00
pMainWindow->updateClassBrowserForEditor(this);
}
void Editor::focusOutEvent(QFocusEvent *event)
{
SynEdit::focusOutEvent(event);
pMainWindow->updateEditorActions();
pMainWindow->updateStatusbarForLineCol();
pMainWindow->updateForStatusbarModeInfo();
}
void Editor::keyPressEvent(QKeyEvent *event)
{
bool handled = false;
2021-08-25 08:48:33 +08:00
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;
}
2021-08-18 13:42:32 +08:00
}
2021-08-25 08:48:33 +08:00
setSelText(ch);
showCompletion(false);
handled=true;
}
}
2021-08-25 08:48:33 +08:00
// }
} else {
2021-08-25 08:48:33 +08:00
//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;
}
}
2021-08-25 08:48:33 +08:00
// 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)
{
// Get point where to draw marks
//X := (fText.Gutter.RealGutterWidth(fText.CharWidth) - fText.Gutter.RightOffset) div 2 - 3;
X = 5;
Y += (this->textHeight() - 16) / 2;
2021-07-26 00:22:08 +08:00
if (mActiveBreakpointLine == aLine) {
painter.drawPixmap(X,Y,*(pIconsManager->activeBreakpoint()));
} else if (hasBreakpoint(aLine)) {
painter.drawPixmap(X,Y,*(pIconsManager->breakpoint()));
2021-08-01 01:06:43 +08:00
} else {
PSyntaxIssueList lst = getSyntaxIssuesAtLine(aLine);
if (lst) {
bool hasError=false;
for (PSyntaxIssue issue : *lst) {
if (issue->issueType == CompileIssueType::Error) {
hasError = true;
break;;
}
}
if (hasError) {
painter.drawPixmap(X,Y,*(pIconsManager->syntaxError()));
} else {
painter.drawPixmap(X,Y,*(pIconsManager->syntaxWarning()));
}
return;
}
}
}
void Editor::onGetEditingAreas(int Line, SynEditingAreaList &areaList)
{
areaList.clear();
// if (fTabStopBegin >=0) and (fTabStopY=Line) then begin
// areaType:=eatEditing;
// System.new(p);
// spaceCount := fText.LeftSpacesEx(fLineBeforeTabStop,True);
// spaceBefore := Length(fLineBeforeTabStop) - Length(TrimLeft(fLineBeforeTabStop));
// p.beginX := fTabStopBegin + spaceCount - spaceBefore ;
// p.endX := fTabStopEnd + spaceCount - spaceBefore ;
// p.color := dmMain.Cpp.StringAttri.Foreground;
// areaList.Add(p);
// ColBorder := dmMain.Cpp.StringAttri.Foreground;
// Exit;
// end;
// StrToThemeColor(tc,devEditor.Syntax.Values[cWN]);
PSyntaxIssueList lst = getSyntaxIssuesAtLine(Line);
if (lst) {
for (PSyntaxIssue issue: *lst) {
PSynEditingArea p=std::make_shared<SynEditingArea>();
p->beginX = issue->col;
p->endX = issue->endCol;
if (issue->issueType == CompileIssueType::Error) {
p->color = mSyntaxErrorColor;
} else {
2021-07-26 00:22:08 +08:00
p->color = mSyntaxWarningColor;
}
p->type = SynEditingAreaType::eatWaveUnderLine;
areaList.append(p);
}
}
}
bool Editor::onGetSpecialLineColors(int Line, QColor &foreground, QColor &backgroundColor)
{
2021-07-27 00:14:24 +08:00
if (Line == mActiveBreakpointLine &&
mActiveBreakpointForegroundColor.isValid()
&& mActiveBreakpointBackgroundColor.isValid()) {
2021-07-26 00:22:08 +08:00
foreground = mActiveBreakpointForegroundColor;
backgroundColor = mActiveBreakpointBackgroundColor;
2021-07-27 00:14:24 +08:00
return true;
} else if (hasBreakpoint(Line) &&
mBreakpointForegroundColor.isValid()
&& mBreakpointBackgroundColor.isValid()) {
2021-07-26 00:22:08 +08:00
foreground = mBreakpointForegroundColor;
backgroundColor = mBreakpointBackgroundColor;
2021-07-27 00:14:24 +08:00
return true;
2021-07-26 00:22:08 +08:00
}
// end else if Line = fErrorLine then begin
// StrToThemeColor(tc, devEditor.Syntax.Values[cErr]);
// BG := tc.Background;
// FG := tc.Foreground;
// if (BG <> clNone) or (FG<>clNone) then
// Special := TRUE;
// end;
2021-07-27 00:14:24 +08:00
return false;
}
void Editor::copyToClipboard()
{
if (pSettings->editor().copySizeLimit()) {
if (lines()->count() > pSettings->editor().copyLineLimits()) {
QMessageBox::critical(pMainWindow,tr("Error"),
tr("The text to be copied exceeds count limit!"));
return;
}
if (lines()->getTextLength() > pSettings->editor().copyCharLimits() * 1000) {
QMessageBox::critical(pMainWindow,tr("Error"),
tr("The text to be copied exceeds character limit!"));
return;
}
}
switch(pSettings->editor().copyWithFormatAs()) {
case 1: //HTML
copyAsHTML();
break;;
default:
SynEdit::copyToClipboard();
}
}
void Editor::cutToClipboard()
{
if (pSettings->editor().copySizeLimit()) {
if (lines()->count() > pSettings->editor().copyLineLimits()) {
QMessageBox::critical(pMainWindow,tr("Error"),
tr("The text to be cut exceeds count limit!"));
return;
}
if (lines()->getTextLength() > pSettings->editor().copyCharLimits() * 1000) {
QMessageBox::critical(pMainWindow,tr("Error"),
tr("The text to be cut exceeds character limit!"));
return;
}
}
SynEdit::cutToClipboard();
}
void Editor::copyAsHTML()
{
if (!selAvail())
return;
SynHTMLExporter SynExporterHTML;
SynExporterHTML.setTitle(QFileInfo(mFilename).fileName());
SynExporterHTML.setExportAsText(false);
2021-06-20 22:54:16 +08:00
SynExporterHTML.setUseBackground(pSettings->editor().copyHTMLUseBackground());
SynExporterHTML.setFont(font());
2021-06-20 22:54:16 +08:00
PSynHighlighter hl = highlighter();
if (!pSettings->editor().copyHTMLUseEditorColor()) {
hl = highlighterManager.copyHighlighter(highlighter());
highlighterManager.applyColorScheme(hl,pSettings->editor().copyHTMLColorScheme());
}
SynExporterHTML.setHighlighter(hl);
SynExporterHTML.setCreateHTMLFragment(true);
SynExporterHTML.ExportRange(lines(),blockBegin(),blockEnd());
QMimeData * mimeData = new QMimeData;
//sethtml will convert buffer to QString , which will cause encoding trouble
mimeData->setData(SynExporterHTML.clipboardFormat(),SynExporterHTML.buffer());
mimeData->setText(selText());
QGuiApplication::clipboard()->clear();
QGuiApplication::clipboard()->setMimeData(mimeData);
2021-04-11 13:55:31 +08:00
}
void Editor::setCaretPosition(int line, int col)
{
this->uncollapseAroundLine(line);
this->setCaretXYCentered(true,BufferCoord{col,line});
}
void Editor::setCaretPositionAndActivate(int line, int col)
{
this->uncollapseAroundLine(line);
if (!this->hasFocus())
this->activate();
this->setCaretXYCentered(true,BufferCoord{col,line});
}
void Editor::addSyntaxIssues(int line, int startChar, int endChar, CompileIssueType errorType, const QString &hint)
2021-06-23 22:38:02 +08:00
{
PSyntaxIssue pError;
BufferCoord p;
QString token;
SynHighlighterTokenType tokenType;
int tokenKind,start;
PSynHighlighterAttribute attr;
PSyntaxIssueList lst;
if ((line<1) || (line>lines()->count()))
return;
pError = std::make_shared<SyntaxIssue>();
p.Char = startChar;
p.Line = line;
if (startChar >= lines()->getString(line-1).length()) {
start = 1;
token = lines()->getString(line-1);
} else if (endChar < 1) {
2021-06-23 22:38:02 +08:00
if (!GetHighlighterAttriAtRowColEx(p,token,tokenType,tokenKind,start,attr))
return;
} else {
start = startChar;
token = lines()->getString(line-1).mid(start-1,endChar-startChar);
2021-06-23 22:38:02 +08:00
}
pError->startChar = start;
pError->endChar = start + token.length();
pError->col = charToColumn(line,pError->startChar);
pError->endCol = charToColumn(line,pError->endChar);
pError->hint = hint;
pError->token = token;
pError->issueType = errorType;
if (mSyntaxIssues.contains(line)) {
lst = mSyntaxIssues[line];
} else {
lst = std::make_shared<SyntaxIssueList>();
mSyntaxIssues[line] = lst;
}
lst->append(pError);
}
void Editor::clearSyntaxIssues()
{
mSyntaxIssues.clear();
}
void Editor::gotoNextSyntaxIssue()
{
auto iter = mSyntaxIssues.find(caretY());
if (iter==mSyntaxIssues.end())
return;
iter++;
if (iter==mSyntaxIssues.end())
return;
BufferCoord p;
p.Char = (*iter)->at(0)->startChar;
p.Line = iter.key();
setCaretXY(p);
}
void Editor::gotoPrevSyntaxIssue()
{
auto iter = mSyntaxIssues.find(caretY());
if (iter==mSyntaxIssues.end())
return;
if (iter==mSyntaxIssues.begin())
return;
iter--;
BufferCoord p;
p.Char = (*iter)->at(0)->startChar;
p.Line = iter.key();
setCaretXY(p);
}
bool Editor::hasNextSyntaxIssue() const
{
auto iter = mSyntaxIssues.find(caretY());
if (iter==mSyntaxIssues.end())
return false;
iter++;
if (iter==mSyntaxIssues.end())
return false;
return true;
}
bool Editor::hasPrevSyntaxIssue() const
{
auto iter = mSyntaxIssues.find(caretY());
if (iter==mSyntaxIssues.end())
return true;
if (iter==mSyntaxIssues.begin())
return true;
return false;
}
Editor::PSyntaxIssueList Editor::getSyntaxIssuesAtLine(int line)
2021-06-23 22:38:02 +08:00
{
if (mSyntaxIssues.contains(line))
return mSyntaxIssues[line];
return PSyntaxIssueList();
}
Editor::PSyntaxIssue Editor::getSyntaxIssueAtPosition(const BufferCoord &pos)
2021-06-23 22:38:02 +08:00
{
PSyntaxIssueList lst = getSyntaxIssuesAtLine(pos.Line);
2021-06-23 22:38:02 +08:00
for (PSyntaxIssue issue: *lst) {
if (issue->startChar<=pos.Char && pos.Char<=issue->endChar)
return issue;
}
return PSyntaxIssue();
}
void Editor::onModificationChanged(bool) {
updateCaption();
2021-04-11 13:55:31 +08:00
}
2021-06-10 09:34:59 +08:00
void Editor::onStatusChanged(SynStatusChanges changes)
{
2021-08-23 17:27:17 +08:00
if (!changes.testFlag(SynStatusChange::scReadOnly)
2021-07-02 10:32:29 +08:00
&& !changes.testFlag(SynStatusChange::scInsertMode)
&& (lines()->count()!=mLineCount)
2021-06-24 20:43:09 +08:00
&& (lines()->count()!=0) && ((mLineCount>0) || (lines()->count()>1))) {
2021-08-23 10:16:06 +08:00
reparse();
2021-07-02 10:32:29 +08:00
if (!readOnly() && pSettings->editor().syntaxCheck() && pSettings->editor().syntaxCheckWhenLineChanged())
2021-06-25 12:40:11 +08:00
pMainWindow->checkSyntaxInBack(this);
2021-06-24 20:43:09 +08:00
}
mLineCount = lines()->count();
2021-06-10 09:34:59 +08:00
// if (not (scOpenFile in Changes)) and (fText.Lines.Count <> fLineCount)
// and (fText.Lines.Count <> 0) and ((fLineCount>0) or (fText.Lines.Count>1)) then begin
// if devCodeCompletion.Enabled
// and SameStr(mainForm.ClassBrowser.CurrentFile,FileName) // Don't reparse twice
// then begin
// Reparse;
// end;
// if fText.Focused and devEditor.AutoCheckSyntax and devEditor.CheckSyntaxWhenReturn
// and (fText.Highlighter = dmMain.Cpp) then begin
// mainForm.CheckSyntaxInBack(self);
// end;
// end;
// fLineCount := fText.Lines.Count;
// // scModified is only fired when the modified state changes
if (changes.testFlag(scModified)) {
updateCaption();
}
// if (fTabStopBegin >=0) and (fTabStopY=fText.CaretY) then begin
// if StartsStr(fLineBeforeTabStop,fText.LineText) and EndsStr(fLineAfterTabStop, fText.LineText) then
// fTabStopBegin := Length(fLineBeforeTabStop);
// if fLineAfterTabStop = '' then
// fTabStopEnd := Length(fText.LineText)+1
// else
// fTabStopEnd := Length(fText.LineText) - Length(fLineAfterTabStop);
// fXOffsetSince := fTabStopEnd - fText.CaretX;
// if (fText.CaretX < fTabStopBegin) or (fText.CaretX > (fTabStopEnd+1)) then begin
// fTabStopBegin :=-1;
// end;
// end;
// scSelection includes anything caret related
if (changes.testFlag(SynStatusChange::scSelection)) {
pMainWindow->updateStatusbarForLineCol();
2021-06-10 09:34:59 +08:00
// // Update the function tip
// fFunctionTip.ForceHide := false;
// if Assigned(fFunctionTipTimer) then begin
// if fFunctionTip.Activated and FunctionTipAllowed then begin
// fFunctionTip.Parser := fParser;
// fFunctionTip.FileName := fFileName;
// fFunctionTip.Show;
// end else begin // Reset the timer
// fFunctionTipTimer.Enabled := false;
// fFunctionTipTimer.Enabled := true;
// end;
}
// // Remove error line colors
// if not fIgnoreCaretChange then begin
// if (fErrorLine <> -1) and not fText.SelAvail then begin
// fText.InvalidateLine(fErrorLine);
// fText.InvalidateGutterLine(fErrorLine);
// fErrorLine := -1;
// end;
// end else
// fIgnoreCaretChange := false;
// if fText.SelAvail then begin
// if fText.GetWordAtRowCol(fText.CaretXY) = fText.SelText then begin
// fSelChanged:=True;
// BeginUpdate;
// EndUpdate;
// end else if fSelChanged then begin
// fSelChanged:=False; //invalidate to unhighlight others
// BeginUpdate;
// EndUpdate;
// end;
// end else if fSelChanged then begin
// fSelChanged:=False; //invalidate to unhighlight others
// BeginUpdate;
// EndUpdate;
// end;
// end;
if (changes.testFlag(scInsertMode) | changes.testFlag(scReadOnly))
pMainWindow->updateForStatusbarModeInfo();
pMainWindow->updateEditorActions();
2021-06-10 09:34:59 +08:00
// mainForm.CaretList.AddCaret(self,fText.CaretY,fText.CaretX);
}
void Editor::onGutterClicked(Qt::MouseButton button, int x, int y, int line)
{
if (button == Qt::LeftButton) {
toggleBreakpoint(line);
}
mGutterClickedLine = line;
}
2021-06-22 23:00:34 +08:00
QChar Editor::getCurrentChar()
{
if (lineText().length()<caretX())
return QChar();
else
return lineText()[caretX()-1];
}
2021-06-22 13:24:26 +08:00
bool Editor::handleSymbolCompletion(QChar key)
{
2021-06-22 13:24:26 +08:00
if (!pSettings->editor().completeSymbols() || selAvail())
return false;
2021-06-22 23:00:34 +08:00
if (!insertMode())
return false;
2021-06-22 13:24:26 +08:00
//todo: better methods to detect current caret type
if (caretX() <= 1) {
if (caretY()>1) {
if (highlighter()->isLastLineCommentNotFinished(lines()->ranges(caretY() - 2).state))
return false;
if (highlighter()->isLastLineStringNotFinished(lines()->ranges(caretY() - 2).state)
&& (key!='\"') && (key!='\''))
return false;
}
} else {
BufferCoord HighlightPos = BufferCoord{caretX()-1, caretY()};
// Check if that line is highlighted as comment
PSynHighlighterAttribute Attr;
QString Token;
bool tokenFinished;
SynHighlighterTokenType tokenType;
if (GetHighlighterAttriAtRowCol(HighlightPos, Token, tokenFinished, tokenType,Attr)) {
if ((tokenType == SynHighlighterTokenType::Comment) && (!tokenFinished))
return false;
if ((tokenType == SynHighlighterTokenType::String) && (!tokenFinished)
&& (key!='\'') && (key!='\"') && (key!='(') && (key!=')'))
return false;
if (( key=='<' || key =='>') && (tokenType != SynHighlighterTokenType::PreprocessDirective))
return false;
if ((key == '\'') && (Attr->name() == "SYNS_AttrNumber"))
return false;
}
}
// Check if that line is highlighted as string or character or comment
// if (Attr = fText.Highlighter.StringAttribute) or (Attr = fText.Highlighter.CommentAttribute) or SameStr(Attr.Name,
// 'Character') then
// Exit;
QuoteStatus status;
switch(key.unicode()) {
case '(':
if (pSettings->editor().completeParenthese()) {
2021-06-22 23:00:34 +08:00
return handleParentheseCompletion();
2021-06-22 13:24:26 +08:00
}
return false;
case ')':
if (pSettings->editor().completeParenthese() && pSettings->editor().overwriteSymbols()) {
2021-06-22 23:00:34 +08:00
return handleParentheseSkip();
2021-06-22 13:24:26 +08:00
}
return false;
case '[':
if (pSettings->editor().completeBracket()) {
2021-06-22 23:00:34 +08:00
return handleBracketCompletion();
2021-06-22 13:24:26 +08:00
}
return false;
case ']':
if (pSettings->editor().completeBracket() && pSettings->editor().overwriteSymbols()) {
2021-06-22 23:00:34 +08:00
return handleBracketSkip();
2021-06-22 13:24:26 +08:00
}
return false;
case '*':
2021-06-22 23:00:34 +08:00
status = getQuoteStatus();
2021-06-22 13:24:26 +08:00
if (pSettings->editor().completeComment() && (status == QuoteStatus::NotQuote)) {
2021-06-22 23:00:34 +08:00
return handleMultilineCommentCompletion();
2021-06-22 13:24:26 +08:00
}
return false;
case '{':
if (pSettings->editor().completeBrace()) {
2021-06-22 23:00:34 +08:00
return handleBraceCompletion();
2021-06-22 13:24:26 +08:00
}
return false;
case '}':
if (pSettings->editor().completeBrace() && pSettings->editor().overwriteSymbols()) {
2021-06-22 23:00:34 +08:00
return handleBraceSkip();
2021-06-22 13:24:26 +08:00
}
return false;
case '\'':
if (pSettings->editor().completeSingleQuote()) {
2021-06-22 23:00:34 +08:00
return handleSingleQuoteCompletion();
2021-06-22 13:24:26 +08:00
}
return false;
case '\"':
if (pSettings->editor().completeDoubleQuote()) {
2021-06-22 23:00:34 +08:00
return handleDoubleQuoteCompletion();
2021-06-22 13:24:26 +08:00
}
return false;
case '<':
if (pSettings->editor().completeGlobalInclude()) { // #include <>
2021-06-22 23:00:34 +08:00
return handleGlobalIncludeCompletion();
2021-06-22 13:24:26 +08:00
}
return false;
case '>':
if (pSettings->editor().completeGlobalInclude() && pSettings->editor().overwriteSymbols()) { // #include <>
2021-06-22 23:00:34 +08:00
return handleGlobalIncludeSkip();
2021-06-22 13:24:26 +08:00
}
return false;
}
return false;
}
2021-06-22 23:00:34 +08:00
bool Editor::handleParentheseCompletion()
{
QuoteStatus status = getQuoteStatus();
if (status == QuoteStatus::RawString || status == QuoteStatus::NotQuote) {
beginUpdate();
CommandProcessor(SynEditorCommand::ecChar,'(');
BufferCoord oldCaret = caretXY();
CommandProcessor(SynEditorCommand::ecChar,')');
setCaretXY(oldCaret);
endUpdate();
return true;
}
// if (status == QuoteStatus::NotQuote) && FunctionTipAllowed then
// fFunctionTip.Activated := true;
return false;
}
bool Editor::handleParentheseSkip()
2021-06-22 13:24:26 +08:00
{
2021-06-22 23:00:34 +08:00
if (getCurrentChar() != ')')
return false;
QuoteStatus status = getQuoteStatus();
if (status == QuoteStatus::RawStringNoEscape) {
setCaretXY( BufferCoord{caretX() + 1, caretY()}); // skip over
return true;
}
if (status != QuoteStatus::NotQuote)
return false;
BufferCoord pos = getMatchingBracket();
if (pos.Line != 0) {
setCaretXY( BufferCoord{caretX() + 1, caretY()}); // skip over
return true;
}
// if FunctionTipAllowed then
// fFunctionTip.Activated := false;
return false;
}
bool Editor::handleBracketCompletion()
{
// QuoteStatus status = getQuoteStatus();
// if (status == QuoteStatus::RawString || status == QuoteStatus::NotQuote) {
beginUpdate();
CommandProcessor(SynEditorCommand::ecChar,'[');
BufferCoord oldCaret = caretXY();
CommandProcessor(SynEditorCommand::ecChar,']');
setCaretXY(oldCaret);
endUpdate();
return true;
// }
}
bool Editor::handleBracketSkip()
{
if (getCurrentChar() != ']')
return false;
BufferCoord pos = getMatchingBracket();
if (pos.Line != 0) {
setCaretXY( BufferCoord{caretX() + 1, caretY()}); // skip over
return true;
}
return false;
}
bool Editor::handleMultilineCommentCompletion()
{
if (((caretX() > 1) && (caretX()-1 < lineText().length())) && (lineText()[caretX() - 1] == '/')) {
beginUpdate();
CommandProcessor(SynEditorCommand::ecChar,'*');
BufferCoord oldCaret = caretXY();
CommandProcessor(SynEditorCommand::ecChar,'*');
CommandProcessor(SynEditorCommand::ecChar,'/');
setCaretXY(oldCaret);
endUpdate();
return true;
}
return false;
}
bool Editor::handleBraceCompletion()
{
QString s = lineText().trimmed();
int i= caretY()-2;
while ((s.isEmpty()) && (i>=0)) {
s=lines()->getString(i);
i--;
}
beginUpdate();
CommandProcessor(SynEditorCommand::ecChar,'{');
BufferCoord oldCaret = caretXY();
CommandProcessor(SynEditorCommand::ecChar,'}');
if (
( (s.startsWith("struct")
|| s.startsWith("class")
|| s.startsWith("union")
|| s.startsWith("typedef")
|| s.startsWith("public")
|| s.startsWith("private")
|| s.startsWith("enum") )
&& !s.contains(';')
) || s.endsWith('=')) {
CommandProcessor(SynEditorCommand::ecChar,';');
}
setCaretXY(oldCaret);
endUpdate();
return true;
}
bool Editor::handleBraceSkip()
{
if (getCurrentChar() != '}')
return false;
BufferCoord pos = getMatchingBracket();
if (pos.Line != 0) {
setCaretXY( BufferCoord{caretX() + 1, caretY()}); // skip over
return true;
}
return false;
}
bool Editor::handleSingleQuoteCompletion()
{
QuoteStatus status = getQuoteStatus();
QChar ch = getCurrentChar();
if (ch == '\'') {
if (status == QuoteStatus::SingleQuote) {
setCaretXY( BufferCoord{caretX() + 1, caretY()}); // skip over
return true;
}
} else {
if (status == QuoteStatus::NotQuote) {
if (ch == 0 || highlighter()->isWordBreakChar(ch) || highlighter()->isSpaceChar(ch)) {
2021-06-22 23:00:34 +08:00
// insert ''
beginUpdate();
CommandProcessor(SynEditorCommand::ecChar,'\'');
BufferCoord oldCaret = caretXY();
CommandProcessor(SynEditorCommand::ecChar,'\'');
setCaretXY(oldCaret);
endUpdate();
return true;
}
}
}
return false;
}
bool Editor::handleDoubleQuoteCompletion()
{
QuoteStatus status = getQuoteStatus();
QChar ch = getCurrentChar();
if (ch == '"') {
if (status == QuoteStatus::DoubleQuote || status == QuoteStatus::RawString) {
2021-06-22 23:00:34 +08:00
setCaretXY( BufferCoord{caretX() + 1, caretY()}); // skip over
return true;
}
} else {
if (status == QuoteStatus::NotQuote) {
if ((ch == 0) || highlighter()->isWordBreakChar(ch) || highlighter()->isSpaceChar(ch)) {
2021-06-22 23:00:34 +08:00
// insert ""
beginUpdate();
CommandProcessor(SynEditorCommand::ecChar,'"');
BufferCoord oldCaret = caretXY();
CommandProcessor(SynEditorCommand::ecChar,'"');
setCaretXY(oldCaret);
endUpdate();
return true;
}
}
}
return false;
}
bool Editor::handleGlobalIncludeCompletion()
{
if (!lineText().startsWith('#'))
return false;
QString s= lineText().mid(1).trimmed();
if (!s.startsWith("include")) //it's not #include
return false;
beginUpdate();
CommandProcessor(SynEditorCommand::ecChar,'<');
BufferCoord oldCaret = caretXY();
CommandProcessor(SynEditorCommand::ecChar,'>');
setCaretXY(oldCaret);
endUpdate();
return true;
}
bool Editor::handleGlobalIncludeSkip()
{
if (getCurrentChar()!='>')
return false;
QString s= lineText().mid(1).trimmed();
if (!s.startsWith("include")) //it's not #include
return false;
BufferCoord pos = getMatchingBracket();
if (pos.Line != 0) {
setCaretXY( BufferCoord{caretX() + 1, caretY()}); // skip over
return true;
}
return false;
2021-06-22 13:24:26 +08:00
}
2021-08-25 08:48:33 +08:00
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);
}
}
}
2021-08-23 10:16:06 +08:00
void Editor::initParser()
{
mParser = std::make_shared<CppParser>();
mParser->setOnGetFileStream(
std::bind(
&EditorList::getContentFromOpenedEditor,pMainWindow->editorList(),
std::placeholders::_1, std::placeholders::_2));
resetCppParser(mParser);
mParser->setEnabled((highlighter() && highlighter()->getClass() == SynHighlighterClass::CppHighlighter));
}
2021-06-22 13:24:26 +08:00
Editor::QuoteStatus Editor::getQuoteStatus()
{
QuoteStatus Result = QuoteStatus::NotQuote;
if ((caretY()>1) && highlighter()->isLastLineStringNotFinished(lines()->ranges(caretY() - 2).state))
Result = QuoteStatus::DoubleQuote;
QString Line = lines()->getString(caretY()-1);
2021-06-22 23:00:34 +08:00
int posX = caretX()-1;
if (posX >= Line.length()) {
posX = Line.length()-1;
}
for (int i=0; i<posX;i++) {
2021-06-22 23:00:34 +08:00
if (i+1<Line.length() && (Line[i] == 'R') && (Line[i+1] == '"') && (Result == QuoteStatus::NotQuote)) {
Result = QuoteStatus::RawString;
i++; // skip R
} else if (Line[i] == '(') {
switch(Result) {
case QuoteStatus::RawString:
Result=QuoteStatus::RawStringNoEscape;
break;
//case RawStringNoEscape: do nothing
}
} else if (Line[i] == ')') {
switch(Result) {
case QuoteStatus::RawStringNoEscape:
Result=QuoteStatus::RawString;
break;
}
} else if (Line[i] == '"') {
switch(Result) {
case QuoteStatus::NotQuote:
Result = QuoteStatus::DoubleQuote;
break;
case QuoteStatus::SingleQuote:
Result = QuoteStatus::SingleQuote;
break;
case QuoteStatus::SingleQuoteEscape:
Result = QuoteStatus::SingleQuote;
break;
case QuoteStatus::DoubleQuote:
Result = QuoteStatus::NotQuote;
break;
case QuoteStatus::DoubleQuoteEscape:
Result = QuoteStatus::DoubleQuote;
break;
case QuoteStatus::RawString:
Result=QuoteStatus::NotQuote;
break;
//RawStringNoEscape: do nothing
}
} else if (Line[i] == '\'') {
switch(Result) {
case QuoteStatus::NotQuote:
Result = QuoteStatus::SingleQuote;
break;
case QuoteStatus::SingleQuote:
Result = QuoteStatus::NotQuote;
break;
case QuoteStatus::SingleQuoteEscape:
Result = QuoteStatus::SingleQuote;
break;
case QuoteStatus::DoubleQuote:
Result = QuoteStatus::DoubleQuote;
break;
case QuoteStatus::DoubleQuoteEscape:
Result = QuoteStatus::DoubleQuote;
break;
}
} else if (Line[i] == '\\') {
switch(Result) {
case QuoteStatus::NotQuote:
Result = QuoteStatus::NotQuote;
break;
case QuoteStatus::SingleQuote:
Result = QuoteStatus::SingleQuoteEscape;
break;
case QuoteStatus::SingleQuoteEscape:
Result = QuoteStatus::SingleQuote;
break;
case QuoteStatus::DoubleQuote:
Result = QuoteStatus::DoubleQuoteEscape;
break;
case QuoteStatus::DoubleQuoteEscape:
Result = QuoteStatus::DoubleQuote;
break;
}
} else {
switch(Result) {
case QuoteStatus::NotQuote:
Result = QuoteStatus::NotQuote;
break;
case QuoteStatus::SingleQuote:
Result = QuoteStatus::SingleQuote;
break;
case QuoteStatus::SingleQuoteEscape:
Result = QuoteStatus::SingleQuote;
break;
case QuoteStatus::DoubleQuote:
Result = QuoteStatus::DoubleQuote;
break;
case QuoteStatus::DoubleQuoteEscape:
Result = QuoteStatus::DoubleQuote;
break;
}
}
}
2021-06-22 13:24:26 +08:00
return Result;
2021-06-10 09:34:59 +08:00
}
2021-08-23 10:16:06 +08:00
void Editor::reparse()
{
parseFile(mParser,mFilename,mInProject);
}
2021-08-25 08:48:33 +08:00
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
2021-08-25 23:53:35 +08:00
word = getWordAtPosition(caretXY(),pBeginPos,pEndPos, WordPurpose::wpDirective);
if (!word.startsWith('#')) {
//showTabnineCompletion();
return;
}
} else if (tokenType == SynHighlighterTokenType::Comment) { //Comment, javadoc tag
word = getWordAtPosition(caretXY(),pBeginPos,pEndPos, WordPurpose::wpJavadoc);
if (!word.startsWith('@')) {
return;
}
} else if (
(tokenType != SynHighlighterTokenType::Symbol) &&
(tokenType != SynHighlighterTokenType::Space) &&
(tokenType != SynHighlighterTokenType::Identifier)
) {
return;
}
2021-08-25 08:48:33 +08:00
}
// Position it at the top of the next line
2021-08-25 23:53:35 +08:00
QPoint p = RowColumnToPixels(displayXY());
p+=QPoint(0,textHeight()+2);
mCompletionPopup->move(mapToGlobal(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;
2021-08-25 08:48:33 +08:00
//Set Font size;
2021-08-25 23:53:35 +08:00
mCompletionPopup->setFont(font());
2021-08-25 08:48:33 +08:00
// Redirect key presses to completion box if applicable
2021-08-25 23:53:35 +08:00
//todo:
mCompletionPopup->setKeypressedCallback([this](QKeyEvent *event)->bool{
return onCompletionKeyPressed(event);
});
mCompletionPopup->setParser(mParser);
//todo:
//mCompletionPopup->setUseCppKeyword(mUseCppSyntax);
mCompletionPopup->show();
2021-08-25 08:48:33 +08:00
// Scan the current function body
2021-08-25 23:53:35 +08:00
mCompletionPopup->setCurrentStatement(
mParser->findAndScanBlockAt(mFilename, caretY())
);
2021-08-25 08:48:33 +08:00
2021-08-25 23:53:35 +08:00
if (word.isEmpty())
word=getWordAtPosition(caretXY(),pBeginPos,pEndPos, WordPurpose::wpCompletion);
2021-08-25 08:48:33 +08:00
//if not fCompletionBox.Visible then
2021-08-25 23:53:35 +08:00
mCompletionPopup->prepareSearch(word, mFilename, pBeginPos.Line);
2021-08-25 08:48:33 +08:00
// Filter the whole statement list
2021-08-25 23:53:35 +08:00
if (mCompletionPopup->search(word, autoComplete)) { //only one suggestion and it's not input while typing
completionInsert(); // if only have one suggestion, just use it
}
}
void Editor::showHeaderCompletion(bool autoComplete)
{
//todo:
}
QString Editor::getWordAtPosition(const BufferCoord &p, BufferCoord &pWordBegin, BufferCoord &pWordEnd, WordPurpose purpose)
{
QString result = "";
QString s;
if ((p.Line >= 1) && (p.Line <= lines()->count())) {
s = lines()->getString(p.Line - 1);
int len = s.length();
int wordBegin = p.Char - 1 - 1; //BufferCoord::Char starts with 1
int wordEnd = p.Char - 1 - 1;
// Copy forward until end of word
if (purpose == WordPurpose::wpEvaluation
|| purpose == WordPurpose::wpInformation) {
while (wordEnd + 1 < len) do {
if ((purpose == WordPurpose::wpEvaluation)
&& (s[wordEnd + 1] == '[')) {
if (!findComplement(s, '[', ']', wordEnd, 1))
break;
} else if (isIdentChar(s[wordEnd + 1])) {
wordEnd++;
} else
break;
}
}
// Copy backward until #
if (purpose == WordPurpose::wpDirective) {
while ((wordBegin >= 0) && (wordBegin < len)) {
if (isIdentChar(s[wordBegin]))
wordBegin--;
else if (s[wordBegin] == '#') {
wordBegin--;
break;
} else
break;
}
}
// Copy backward until @
if (purpose == WordPurpose::wpJavadoc) {
while ((wordBegin >= 0) && (wordBegin < len)) {
if (isIdentChar(s[wordBegin]))
wordBegin--;
else if (s[wordBegin] == '@') {
wordBegin--;
break;
} else
break;
}
}
// Copy backward until begin of path
if (purpose == wpHeaderCompletion) {
while ((wordBegin >= 0) && (wordBegin < len)) {
if (isIdentChar(s[wordBegin]))
wordBegin--;
else if (s[wordBegin] == '/'
|| s[wordBegin] == '\\'
|| s[wordBegin] == '.') {
wordBegin--;
break;
} else
break;
}
}
if (purpose == WordPurpose::wpHeaderCompletionStart) {
while ((wordBegin >= 0) && (wordBegin < len)) {
if (s[wordBegin] == '"'
|| s[wordBegin] == '<']) {
wordBegin--;
break;
} else if (s[wordBegin] == '/'
|| s[wordBegin] == '\\'
|| s[wordBegin] == '.') {
wordBegin--;
} else if (isIdentChar(s[wordBegin]))
wordBegin--;
else
break;
}
}
// Copy backward until begin of word
if (purpose == WordPurpose::wpCompletion
|| purpose == WordPurpose::wpEvaluation
|| purpose == WordPurpose::wpInformation) {
while ((wordBegin >= 0) && (wordBegin < len)) {
if (s[wordBegin] == ']') then {
if (!findComplement(s, ']', '[', wordBegin, -1))
break
else
wordBegin++; // step over [
} else if (isIdentChar(s[wordBegin])) {
wordBegin--;
} else if (s[wordBegin] == '.'
|| s[wordBegin] == ':'
|| s[wordBegin] == '~') { // allow destructor signs
wordBegin--;
} else if (
(s[wordBegin] == '>')
&& (wordBegin+2<len)
&& (s[WordBegin+1]==':')
&& (s[WordBegin+2]==':')
) { // allow template
if (!findComplement(s, '>', '<', wordBegin, -1))
break;
else
wordBegin--; // step over >
} else if ((wordBegin-1 >= 0)
&& (s[wordBegin - 1] == '-')
&& (s[wordBegin] == '>')) {
wordBegin-=2;
} else if ((wordBegin-1 >= 0)
&& (s[wordBegin - 1] == ':')
&& (s[wordBegin] == ':')) {
wordBegin-=2;
} else if ((wordBegin > 0)
&& (s[wordBegin] == ')')) {
if (!findComplement(s, ')', '(', WordBegin, -1))
break;
else
wordBegin--; // step over (
} else
break;
}
}
}
// Get end result
result = s.mid(wordBegin + 1, wordEnd - wordBegin);
pWordBegin.Line = p.Line;
pWordBegin.Char = wordBegin+1;
pWordEnd.Line = p.Line;
pWordEnd.Char = wordEnd;
if (!result.isEmpty() && (
result[0] in ['.','-'])
and (Purpose in [wpCompletion, wpEvaluation, wpInformation]) then begin
i:=WordBegin;
line:=p.Line;
while line>=1 do begin
while i>=1 do begin
if S[i] in [' ',#9] then
dec(i)
else
break;
end;
if i<1 then begin
dec(line);
if (line>=1) then begin
S:=editor.Lines[line-1];
i:=Length(s);
continue;
end else
break;
end else begin
HighlightPos.Line := line;
HighlightPos.Char := i+1;
Result := GetWordAtPosition(editor,highlightPos,pWordBegin,pDummy,Purpose)+Result;
break;
end;
end;
end;
// Strip function parameters
while true do begin
ParamBegin := Pos('(', Result);
if ParamBegin > 0 then begin
ParamEnd := ParamBegin;
if (ParamBegin=1) and FindComplement(Result, '(', ')', ParamEnd, 1)
and (ParamEnd = Length(Result)) then begin
Delete(Result,ParamEnd,1);
Delete(Result,ParamBegin,1);
continue;
end else begin
ParamEnd := ParamBegin;
if FindComplement(Result, '(', ')', ParamEnd, 1) then begin
Delete(Result, ParamBegin, ParamEnd - ParamBegin + 1);
end else
break;
end;
end else
break;
end;
ParamBegin := 1;
while (ParamBegin <= Length(Result)) and (Result[ParamBegin] = '*') do begin
inc(ParamBegin);
end;
Delete(Result,1,ParamBegin-1);
2021-08-25 08:48:33 +08:00
}
2021-08-23 17:27:17 +08:00
const PCppParser &Editor::parser() const
{
return mParser;
}
int Editor::gutterClickedLine() const
{
return mGutterClickedLine;
}
void Editor::toggleBreakpoint(int line)
{
if (hasBreakpoint(line)) {
mBreakpointLines.remove(line);
2021-08-01 09:13:38 +08:00
pMainWindow->debugger()->removeBreakpoint(line,this);
} else {
mBreakpointLines.insert(line);
2021-08-01 09:13:38 +08:00
pMainWindow->debugger()->addBreakpoint(line,this);
}
invalidateGutterLine(line);
invalidateLine(line);
}
bool Editor::hasBreakpoint(int line)
{
return mBreakpointLines.contains(line);
}
2021-07-26 00:22:08 +08:00
void Editor::removeBreakpointFocus()
{
if (mActiveBreakpointLine!=-1) {
int oldLine = mActiveBreakpointLine;
mActiveBreakpointLine = -1;
invalidateGutterLine(oldLine);
invalidateLine(oldLine);
}
}
2021-07-26 18:22:09 +08:00
void Editor::setActiveBreakpointFocus(int Line, bool setFocus)
{
if (Line != mActiveBreakpointLine) {
removeBreakpointFocus();
// Put the caret at the active breakpoint
mActiveBreakpointLine = Line;
if (setFocus)
setCaretPositionAndActivate(Line,1);
else
setCaretPosition(Line,1);
// Invalidate new active line
invalidateGutterLine(Line);
invalidateLine(Line);
}
}
2021-06-07 11:02:03 +08:00
void Editor::applySettings()
{
SynEditorOptions options = eoAltSetsColumnMode |
eoDragDropEditing | eoDropFiles | eoKeepCaretX | eoTabsToSpaces |
eoRightMouseMovesCursor | eoScrollByOneLess | eoTabIndent | eoHideShowScrollbars;
//options
2021-06-07 11:02:03 +08:00
options.setFlag(eoAddIndent,pSettings->editor().addIndent());
options.setFlag(eoAutoIndent,pSettings->editor().autoIndent());
options.setFlag(eoTabsToSpaces,pSettings->editor().tabToSpaces());
options.setFlag(eoKeepCaretX,pSettings->editor().keepCaretX());
options.setFlag(eoEnhanceHomeKey,pSettings->editor().enhanceHomeKey());
options.setFlag(eoEnhanceEndKey,pSettings->editor().enhanceEndKey());
options.setFlag(eoHideShowScrollbars,pSettings->editor().autoHideScrollbar());
options.setFlag(eoScrollPastEol,pSettings->editor().scrollPastEol());
options.setFlag(eoScrollPastEof,pSettings->editor().scrollPastEof());
options.setFlag(eoScrollByOneLess,pSettings->editor().scrollByOneLess());
options.setFlag(eoHalfPageScroll,pSettings->editor().halfPageScroll());
2021-06-07 11:02:03 +08:00
setOptions(options);
setTabWidth(pSettings->editor().tabWidth());
setInsertCaret(pSettings->editor().caretForInsert());
setOverwriteCaret(pSettings->editor().caretForOverwrite());
setCaretColor(pSettings->editor().caretColor());
QFont f=QFont(pSettings->editor().fontName(),pSettings->editor().fontSize());
f.setStyleStrategy(QFont::PreferAntialias);
setFont(f);
// Set gutter properties
gutter().setLeftOffset(pSettings->editor().gutterLeftOffset());
gutter().setRightOffset(pSettings->editor().gutterRightOffset());
gutter().setBorderStyle(SynGutterBorderStyle::None);
gutter().setUseFontStyle(pSettings->editor().gutterUseCustomFont());
if (pSettings->editor().gutterUseCustomFont()) {
f=QFont(pSettings->editor().gutterFontName(),pSettings->editor().gutterFontSize());
} else {
f=QFont(pSettings->editor().fontName(),pSettings->editor().fontSize());
}
f.setStyleStrategy(QFont::PreferAntialias);
gutter().setFont(f);
gutter().setDigitCount(pSettings->editor().gutterDigitsCount());
gutter().setVisible(pSettings->editor().gutterVisible());
gutter().setAutoSize(pSettings->editor().gutterAutoSize());
gutter().setShowLineNumbers(pSettings->editor().gutterShowLineNumbers());
gutter().setLeadingZeros(pSettings->editor().gutterAddLeadingZero());
if (pSettings->editor().gutterLineNumbersStartZero())
gutter().setLineNumberStart(0);
else
gutter().setLineNumberStart(1);
//font color
2021-06-07 11:02:03 +08:00
}
void Editor::applyColorScheme(const QString& schemeName)
{
2021-06-20 22:54:16 +08:00
highlighterManager.applyColorScheme(highlighter(),schemeName);
PColorSchemeItem item = pColorManager->getItem(schemeName,COLOR_SCHEME_ACTIVE_LINE);
if (item) {
setActiveLineColor(item->background());
}
item = pColorManager->getItem(schemeName,COLOR_SCHEME_GUTTER);
if (item) {
gutter().setTextColor(item->foreground());
gutter().setColor(item->background());
}
item = pColorManager->getItem(schemeName,COLOR_SCHEME_FOLD_LINE);
if (item) {
2021-06-24 22:33:57 +08:00
codeFolding().folderBarLinesColor = item->foreground();
}
item = pColorManager->getItem(schemeName,COLOR_SCHEME_INDENT_GUIDE_LINE);
if (item) {
2021-06-24 22:33:57 +08:00
codeFolding().indentGuidesColor = item->foreground();
}
2021-07-26 00:22:08 +08:00
item = pColorManager->getItem(schemeName,COLOR_SCHEME_ERROR);
if (item) {
this->mSyntaxErrorColor = item->foreground();
}
item = pColorManager->getItem(schemeName,COLOR_SCHEME_WARNING);
if (item) {
this->mSyntaxWarningColor = item->foreground();
}
item = pColorManager->getItem(schemeName,COLOR_SCHEME_ACTIVE_BREAKPOINT);
if (item) {
this->mActiveBreakpointForegroundColor = item->foreground();
this->mActiveBreakpointBackgroundColor = item->background();
}
item = pColorManager->getItem(schemeName,COLOR_SCHEME_BREAKPOINT);
if (item) {
this->mBreakpointForegroundColor = item->foreground();
2021-07-27 00:14:24 +08:00
this->mBreakpointBackgroundColor = item->background();
2021-07-26 00:22:08 +08:00
}
this->invalidate();
}
void Editor::updateCaption(const QString& newCaption) {
if (mParentPageControl==nullptr) {
return;
}
int index = mParentPageControl->indexOf(this);
if (index==-1)
return;
if (newCaption.isEmpty()) {
QString caption = QFileInfo(mFilename).fileName();
2021-05-24 00:41:00 +08:00
if (this->modified()) {
caption.append("[*]");
}
mParentPageControl->setTabText(index,caption);
} else {
mParentPageControl->setTabText(index,newCaption);
}
}