diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index eb480869..01892f53 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -434,10 +434,20 @@ void Editor::onStatusChanged(SynStatusChanges changes) // mainForm.CaretList.AddCaret(self,fText.CaretY,fText.CaretX); } +QChar Editor::getCurrentChar() +{ + if (lineText().length()editor().completeSymbols() || selAvail()) return false; + if (!insertMode()) + return false; //todo: better methods to detect current caret type if (caretX() <= 1) { @@ -477,83 +487,265 @@ bool Editor::handleSymbolCompletion(QChar key) switch(key.unicode()) { case '(': if (pSettings->editor().completeParenthese()) { - handleParentheseCompletion(); - return true; + return handleParentheseCompletion(); } return false; case ')': if (pSettings->editor().completeParenthese() && pSettings->editor().overwriteSymbols()) { - handleParentheseSkip(); - return true; + return handleParentheseSkip(); } return false; case '[': if (pSettings->editor().completeBracket()) { - handleBracketCompletion(); - return true; + return handleBracketCompletion(); } return false; case ']': if (pSettings->editor().completeBracket() && pSettings->editor().overwriteSymbols()) { - HandleBracketSkip(); - return true; + return handleBracketSkip(); } return false; case '*': - status = getQuoteState(); + status = getQuoteStatus(); if (pSettings->editor().completeComment() && (status == QuoteStatus::NotQuote)) { - handleMultilineCommentCompletion(); - return true; + return handleMultilineCommentCompletion(); } return false; case '{': if (pSettings->editor().completeBrace()) { - handleBraceCompletion(); - return true; + return handleBraceCompletion(); } return false; case '}': if (pSettings->editor().completeBrace() && pSettings->editor().overwriteSymbols()) { - handleBraceSkip(); - return true; + return handleBraceSkip(); } return false; case '\'': if (pSettings->editor().completeSingleQuote()) { - handleSingleQuoteCompletion(); - return true; + return handleSingleQuoteCompletion(); } return false; case '\"': if (pSettings->editor().completeDoubleQuote()) { - handleDoubleQuoteCompletion(); - return true; + return handleDoubleQuoteCompletion(); } return false; case '<': if (pSettings->editor().completeGlobalInclude()) { // #include <> - handleGlobalIncludeCompletion(); - return true; + return handleGlobalIncludeCompletion(); } return false; case '>': if (pSettings->editor().completeGlobalInclude() && pSettings->editor().overwriteSymbols()) { // #include <> - handleGlobalIncludeSkip(); - return true; + return handleGlobalIncludeSkip(); } return false; } return false; } -void Editor::handleParentheseCompletion() +bool Editor::handleParentheseCompletion() { - status := GetQuoteState; -if (status in [RawString,NotQuote]) then begin - InsertString(')', false); -end; -if (status=NotQuote) and FunctionTipAllowed then - fFunctionTip.Activated := true; + 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() +{ + 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 (highlighter()->isWordBreakChar(ch) || highlighter()->isSpaceChar(ch)) { + // 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) { + setCaretXY( BufferCoord{caretX() + 1, caretY()}); // skip over + return true; + } + } else { + if (status == QuoteStatus::NotQuote) { + if (highlighter()->isWordBreakChar(ch) || highlighter()->isSpaceChar(ch)) { + // 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; } Editor::QuoteStatus Editor::getQuoteStatus() @@ -563,62 +755,105 @@ Editor::QuoteStatus Editor::getQuoteStatus() Result = QuoteStatus::DoubleQuote; QString Line = lines()->getString(caretY()-1); - int posX =fText.CaretX-1; -if posX > Length(Line) then begin - posX := Length(Line); -end; -i:=1; -while (i<=posX) do begin - if (Line[i] = 'R') and (Line[i+1] = '"') and (Result = NotQuote) then begin - Result := RawString; - inc(i); // skip R - end else if Line[i] = '(' then begin - Case Result of - RawString: Result:=RawStringNoEscape; - //RawStringNoEscape: do nothing - end - end else if Line[i] = ')' then begin - Case Result of - RawStringNoEscape: Result:=RawString; - end - end else if Line[i] = '"' then begin - Case Result of - NotQuote: Result := DoubleQuote; - SingleQuote: Result := SingleQuote; - SingleQuoteEscape: Result := SingleQuote; - DoubleQuote: Result := NotQuote; - DoubleQuoteEscape: Result := DoubleQuote; - RawString: Result:=NotQuote; - //RawStringNoEscape: do nothing - end - end else if Line[i] = '''' then - Case Result of - NotQuote: Result := SingleQuote; - SingleQuote: Result := NotQuote; - SingleQuoteEscape: Result := SingleQuote; - DoubleQuote: Result := DoubleQuote; - DoubleQuoteEscape: Result := DoubleQuote; - end - else if Line[i] = '\' then - Case Result of - NotQuote: Result := NotQuote; - SingleQuote: Result := SingleQuoteEscape; - SingleQuoteEscape: Result := SingleQuote; - DoubleQuote: Result := DoubleQuoteEscape; - DoubleQuoteEscape: Result := DoubleQuote; - end - else begin - Case Result of - NotQuote: Result := NotQuote; - SingleQuote: Result := SingleQuote; - SingleQuoteEscape: Result := SingleQuote; - DoubleQuote: Result := DoubleQuote; - DoubleQuoteEscape: Result := DoubleQuote; - end; - end; - inc(i); -end; -end; + int posX = caretX()-1; + if (posX >= Line.length()) { + posX = Line.length()-1; + } + for (int i=0; i<=posX;i++) { + if (i+1'}; + QString Line; + int i, PosX, PosY, Len; + QChar Test, BracketInc, BracketDec; + int NumBrackets; + QString vDummy; + PSynHighlighterAttribute attr; + BufferCoord p; + bool isCommentOrStringOrChar; + int nBrackets = sizeof(Brackets) / sizeof(QChar); + + if (mLines->count()<1) + return BufferCoord{0,0}; + if (!mHighlighter) + return BufferCoord{0,0}; + // get char at caret + PosX = std::max(APoint.Char,1); + PosY = std::max(APoint.Line,1); + Line = mLines->getString(APoint.Line - 1); + if (Line.length() >= PosX ) { + Test = Line[PosX-1]; + // is it one of the recognized brackets? + for (i = 0; i 1, 1 -> 0, ... + // search for the matching bracket (that is until NumBrackets = 0) + NumBrackets = 1; + if (i%2==1) { + do { + // search until start of line + while (PosX > 1) { + PosX--; + Test = Line[PosX-1]; + p.Char = PosX; + p.Line = PosY; + if ((Test == BracketInc) || (Test == BracketDec)) { + if (GetHighlighterAttriAtRowCol(p, vDummy, attr)) + isCommentOrStringOrChar = + (attr == mHighlighter->stringAttribute()) || + (attr == mHighlighter->commentAttribute()) || + (attr->name() == SYNS_AttrCharacter); + else + isCommentOrStringOrChar = false; + if ((Test == BracketInc) && (!isCommentOrStringOrChar)) + NumBrackets++; + else if ((Test == BracketDec) && (!isCommentOrStringOrChar)) { + NumBrackets--; + if (NumBrackets == 0) { + // matching bracket found, set caret and bail out + return p; + } + } + } + } + // get previous line if possible + if (PosY == 1) + break; + PosY--; + Line = mLines->getString(PosY - 1); + PosX = Line.length() + 1; + } while (true); + } else { + do { + // search until end of line + Len = Line.length(); + while (PosX < Len) { + PosX++; + Test = Line[PosX-1]; + p.Char = PosX; + p.Line = PosY; + if ((Test == BracketInc) || (Test == BracketDec)) { + if (GetHighlighterAttriAtRowCol(p, vDummy, attr)) + isCommentOrStringOrChar = + (attr == mHighlighter->stringAttribute()) || + (attr == mHighlighter->commentAttribute()) || + (attr->name() == SYNS_AttrCharacter); + else + isCommentOrStringOrChar = false; + if ((Test == BracketInc) && (!isCommentOrStringOrChar)) + NumBrackets++; + else if ((Test == BracketDec) && (!isCommentOrStringOrChar)) { + NumBrackets--; + if (NumBrackets == 0) { + // matching bracket found, set caret and bail out + return p; + } + } + } + } + // get next line if possible + if (PosY == mLines->count()) + break; + PosY++; + Line = mLines->getString(PosY - 1); + PosX = 0; + } while (true); + } + // don't test the other brackets, we're done + break; + } + } + } + return BufferCoord{0,0}; +} + void SynEdit::invalidateGutter() { invalidateGutterLines(-1, -1); diff --git a/RedPandaIDE/qsynedit/SynEdit.h b/RedPandaIDE/qsynedit/SynEdit.h index 58bbad0e..d933a21b 100644 --- a/RedPandaIDE/qsynedit/SynEdit.h +++ b/RedPandaIDE/qsynedit/SynEdit.h @@ -234,6 +234,11 @@ public: virtual void untab() { CommandProcessor(SynEditorCommand::ecShiftTab);} virtual void toggleComment() { CommandProcessor(SynEditorCommand::ecToggleComment);} + virtual void beginUpdate(); + virtual void endUpdate(); + virtual BufferCoord getMatchingBracket(); + virtual BufferCoord getMatchingBracketEx(BufferCoord APoint); + // setter && getters int topLine() const; diff --git a/RedPandaIDE/qsynedit/highlighter/base.cpp b/RedPandaIDE/qsynedit/highlighter/base.cpp index e81a8b10..f25ceb31 100644 --- a/RedPandaIDE/qsynedit/highlighter/base.cpp +++ b/RedPandaIDE/qsynedit/highlighter/base.cpp @@ -84,6 +84,40 @@ bool SynHighlighter::isSpaceChar(const QChar &ch) return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; } +bool SynHighlighter::isWordBreakChar(const QChar &ch) +{ + switch (ch.unicode()) { + case '.': + case ',': + case ';': + case ':': + case '"': + case '\'': + case '!': + case '?': + case '[': + case ']': + case '(': + case ')': + case '{': + case '}': + case '<': + case '>': + case '^': + case '|': + case '&': + case '-': + case '=': + case '+': + case '*': + case '/': + case '\\': + return true; + default: + return false; + } +} + bool SynHighlighter::isIdentChar(const QChar &ch) const { if (ch == '_') { diff --git a/RedPandaIDE/qsynedit/highlighter/base.h b/RedPandaIDE/qsynedit/highlighter/base.h index 47bd9d77..ab76e141 100644 --- a/RedPandaIDE/qsynedit/highlighter/base.h +++ b/RedPandaIDE/qsynedit/highlighter/base.h @@ -108,7 +108,8 @@ public: virtual QString languageName() = 0; virtual SynHighlighterLanguage language() = 0; - static bool isSpaceChar(const QChar& ch); + virtual bool isSpaceChar(const QChar& ch); + virtual bool isWordBreakChar(const QChar& ch); bool enabled() const; void setEnabled(bool value); diff --git a/RedPandaIDE/settings.cpp b/RedPandaIDE/settings.cpp index 2f46deaf..2d1f7c9b 100644 --- a/RedPandaIDE/settings.cpp +++ b/RedPandaIDE/settings.cpp @@ -1271,6 +1271,7 @@ void Settings::CompilerSet::setProperties(const QString &binDir) addExistingDirectory(mCppIncludeDirs, includeTrailingPathDelimiter(folder) + "include"); // Find default directories + // C include dirs arguments.clear(); arguments.append("-xc"); arguments.append("-v"); @@ -1278,7 +1279,7 @@ void Settings::CompilerSet::setProperties(const QString &binDir) arguments.append(NULL_FILE); output = getCompilerOutput(binDir,GCC_PROGRAM,arguments); - // C include dirs + delimPos1 = output.indexOf("#include <...> search starts here:"); delimPos2 = output.indexOf("End of search list."); if (delimPos1 >0 && delimPos2>0 ) { @@ -1291,35 +1292,9 @@ void Settings::CompilerSet::setProperties(const QString &binDir) } } } - // bin dirs - targetStr = QByteArray("COMPILER_PATH="); - delimPos1 = output.indexOf(targetStr); - if (delimPos1>=0) { - delimPos1+=targetStr.length(); - delimPos2 = delimPos1; - while (delimPos2 < output.length() && output[delimPos2]!='\n') - delimPos2+=1; - QList lines = output.mid(delimPos1,delimPos2-delimPos1).split(';'); - for (QByteArray& line:lines) { - QByteArray trimmedLine = line.trimmed(); - addExistingDirectory(mBinDirs,trimmedLine); - } - } - // lib dirs - targetStr = QByteArray("LIBRARY_PATH="); - delimPos1 = output.indexOf(targetStr); - if (delimPos1>=0) { - delimPos1+=targetStr.length(); - delimPos2 = delimPos1; - while (delimPos2 < output.length() && output[delimPos2]!='\n') - delimPos2+=1; - QList lines = output.mid(delimPos1,delimPos2-delimPos1).split(';'); - for (QByteArray& line:lines) { - QByteArray trimmedLine = line.trimmed(); - addExistingDirectory(mLibDirs,trimmedLine); - } - } + // Find default directories + // C++ include dirs arguments.clear(); arguments.append("-xc++"); arguments.append("-E"); @@ -1328,7 +1303,6 @@ void Settings::CompilerSet::setProperties(const QString &binDir) output = getCompilerOutput(binDir,GCC_PROGRAM,arguments); //gcc -xc++ -E -v NUL - // C include dirs delimPos1 = output.indexOf("#include <...> search starts here:"); delimPos2 = output.indexOf("End of search list."); if (delimPos1 >0 && delimPos2>0 ) { @@ -1342,6 +1316,41 @@ void Settings::CompilerSet::setProperties(const QString &binDir) } } + // Find default directories + arguments.clear(); + arguments.append("-print-search-dirs"); + arguments.append(NULL_FILE); + output = getCompilerOutput(binDir,GCC_PROGRAM,arguments); + // bin dirs + targetStr = QByteArray("programs: ="); + delimPos1 = output.indexOf(targetStr); + if (delimPos1>=0) { + delimPos1+=targetStr.length(); + delimPos2 = delimPos1; + while (delimPos2 < output.length() && output[delimPos2]!='\n') + delimPos2+=1; + QList lines = output.mid(delimPos1,delimPos2-delimPos1).split(';'); + for (QByteArray& line:lines) { + QByteArray trimmedLine = line.trimmed(); + if (!trimmedLine.isEmpty()) + addExistingDirectory(mBinDirs,trimmedLine); + } + } + // lib dirs + targetStr = QByteArray("libraries: ="); + delimPos1 = output.indexOf(targetStr); + if (delimPos1>=0) { + delimPos1+=targetStr.length(); + delimPos2 = delimPos1; + while (delimPos2 < output.length() && output[delimPos2]!='\n') + delimPos2+=1; + QList lines = output.mid(delimPos1,delimPos2-delimPos1).split(';'); + for (QByteArray& line:lines) { + QByteArray trimmedLine = line.trimmed(); + if (!trimmedLine.isEmpty()) + addExistingDirectory(mLibDirs,trimmedLine); + } + } } void Settings::CompilerSet::setDefines() {