bool getIndentForCurrentBlock (CodeDocument::Position pos, const String& tab, String& blockIndent, String& lastLineIndent) { int braceCount = 0; bool indentFound = false; while (pos.getLineNumber() > 0) { pos = pos.movedByLines (-1); const String line (pos.getLineText()); const String trimmedLine (line.trimStart()); braceCount += getBraceCount (trimmedLine.getCharPointer()); if (braceCount > 0) { blockIndent = getLeadingWhitespace (line); if (! indentFound) lastLineIndent = blockIndent + tab; return true; } if ((! indentFound) && trimmedLine.isNotEmpty()) { indentFound = true; lastLineIndent = getLeadingWhitespace (line); } } return false; }
const String CtrlrMidiBufferStatus::currentByte(const CodeDocument::Position &pos) { const String current = owner.getCodeEditor().getTextInRange(Range<int>(pos.getPosition(), pos.getPosition()+1)); const String next = owner.getCodeEditor().getTextInRange(Range<int>(pos.getPosition()+1, pos.getPosition()+2)); const String previous = owner.getCodeEditor().getTextInRange(Range<int>(pos.getPosition()-1, pos.getPosition())); String byte; String ret; if (next.trim().isEmpty()) { byte << previous << current; } if (previous.trim().isEmpty()) { byte << current << next; } if (!byte.isEmpty()) { return (hexString(byte)); } return (String::empty); }
const Rectangle CodeEditorComponent::getCharacterBounds (const CodeDocument::Position& pos) const throw() { return Rectangle (roundToInt ((gutter - xOffset * charWidth) + indexToColumn (pos.getLineNumber(), pos.getIndexInLine()) * charWidth), (pos.getLineNumber() - firstLineOnScreen) * lineHeight, roundToInt (charWidth), lineHeight); }
void CtrlrMidiBufferStatus::update(const int x, const int y) { size.setText(STR(owner.getDataSize()/3)); status.setFont (statusFont); status.setText ("Byte: "); CodeDocument::Position pos = owner.getCodeEditor().getPositionAt (x,y); status.append (STR(pos.getPosition()/3), statusFont.boldened()); status.append ("/"+STR(pos.getPosition()), statusFont); status.append (" "+currentByte(pos), statusFont); Range<int> sel = owner.getCodeEditor().getHighlightedRegion(); if (!sel.isEmpty()) { const int end = sel.getEnd() / 3; const int start = sel.getStart() / 3; String selection = owner.getCodeEditor().getTextInRange (Range<int> (sel.getStart(),sel.getEnd())).trim(); status.append (" Selected("+STR((end - start)+1)+"): ", statusFont); status.append (STR(start), statusFont.boldened()); status.append (" - ", statusFont); status.append (STR(end), statusFont.boldened()); status.append (" "+hexString(selection), statusFont); } repaint(); }
void LivePropertyEditorBase::findOriginalValueInCode() { CodeDocument::Position pos (document, value.sourceLine, 0); String line (pos.getLineText()); String::CharPointerType p (line.getCharPointer()); p = CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT")); if (p.isEmpty()) { // Not sure how this would happen - some kind of mix-up between source code and line numbers.. jassertfalse; return; } p += (int) (sizeof ("JUCE_LIVE_CONSTANT") - 1); p = p.findEndOfWhitespace(); if (! CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT")).isEmpty()) { // Aargh! You've added two JUCE_LIVE_CONSTANT macros on the same line! // They're identified by their line number, so you must make sure each // one goes on a separate line! jassertfalse; } if (p.getAndAdvance() == '(') { String::CharPointerType start (p), end (p); int depth = 1; while (! end.isEmpty()) { const juce_wchar c = end.getAndAdvance(); if (c == '(') ++depth; if (c == ')') --depth; if (depth == 0) { --end; break; } } if (end > start) { valueStart = CodeDocument::Position (document, value.sourceLine, (int) (start - line.getCharPointer())); valueEnd = CodeDocument::Position (document, value.sourceLine, (int) (end - line.getCharPointer())); valueStart.setPositionMaintained (true); valueEnd.setPositionMaintained (true); wasHex = String (start, end).containsIgnoreCase ("0x"); } } }
void CtrlrLuaMethodCodeEditor::mouseDown (const MouseEvent &e) { CodeDocument::Position pos = editorComponent->getCaretPos(); String url; if (isMouseOverUrl (pos, &url)) { URL(url).launchInDefaultBrowser(); } owner.setPositionLabelText ("Line: " + String(pos.getLineNumber()+1) + " Column: " + String(pos.getIndexInLine())); }
void GenericCodeEditorComponent::findNext (bool forwards, bool skipCurrentSelection) { const Range<int> highlight (getHighlightedRegion()); const CodeDocument::Position startPos (getDocument(), skipCurrentSelection ? highlight.getEnd() : highlight.getStart()); int lineNum = startPos.getLineNumber(); int linePos = startPos.getIndexInLine(); const int totalLines = getDocument().getNumLines(); const String searchText (getSearchString()); const bool caseSensitive = isCaseSensitiveSearch(); for (int linesToSearch = totalLines; --linesToSearch >= 0;) { String line (getDocument().getLine (lineNum)); int index; if (forwards) { index = caseSensitive ? line.indexOf (linePos, searchText) : line.indexOfIgnoreCase (linePos, searchText); } else { if (linePos >= 0) line = line.substring (0, linePos); index = caseSensitive ? line.lastIndexOf (searchText) : line.lastIndexOfIgnoreCase (searchText); } if (index >= 0) { const CodeDocument::Position p (getDocument(), lineNum, index); selectRegion (p, p.movedBy (searchText.length())); break; } if (forwards) { linePos = 0; lineNum = (lineNum + 1) % totalLines; } else { if (--lineNum < 0) lineNum = totalLines - 1; linePos = -1; } } }
void CodeEditorComponent::moveLineDelta (const int delta, const bool selecting) { CodeDocument::Position pos (caretPos); const int newLineNum = pos.getLineNumber() + delta; if (columnToTryToMaintain < 0) columnToTryToMaintain = indexToColumn (pos.getLineNumber(), pos.getIndexInLine()); pos.setLineAndIndex (newLineNum, columnToIndex (newLineNum, columnToTryToMaintain)); const int colToMaintain = columnToTryToMaintain; moveCaretTo (pos, selecting); columnToTryToMaintain = colToMaintain; }
bool update (CodeDocument& codeDoc, int lineNum, CodeDocument::Iterator& source, CodeTokeniser* tokeniser, const int tabSpaces, const CodeDocument::Position& selStart, const CodeDocument::Position& selEnd) { Array <SyntaxToken> newTokens; newTokens.ensureStorageAllocated (8); if (tokeniser == nullptr) { const String line (codeDoc.getLine (lineNum)); addToken (newTokens, line, line.length(), -1); } else if (lineNum < codeDoc.getNumLines()) { const CodeDocument::Position pos (codeDoc, lineNum, 0); createTokens (pos.getPosition(), pos.getLineText(), source, *tokeniser, newTokens); } replaceTabsWithSpaces (newTokens, tabSpaces); int newHighlightStart = 0; int newHighlightEnd = 0; if (selStart.getLineNumber() <= lineNum && selEnd.getLineNumber() >= lineNum) { const String line (codeDoc.getLine (lineNum)); CodeDocument::Position lineStart (codeDoc, lineNum, 0), lineEnd (codeDoc, lineNum + 1, 0); newHighlightStart = indexToColumn (jmax (0, selStart.getPosition() - lineStart.getPosition()), line, tabSpaces); newHighlightEnd = indexToColumn (jmin (lineEnd.getPosition() - lineStart.getPosition(), selEnd.getPosition() - lineStart.getPosition()), line, tabSpaces); } if (newHighlightStart != highlightColumnStart || newHighlightEnd != highlightColumnEnd) { highlightColumnStart = newHighlightStart; highlightColumnEnd = newHighlightEnd; } else if (tokens == newTokens) { return false; } tokens.swapWith (newTokens); return true; }
void CtrlrLuaMethodCodeEditor::reportFoundMatch (CodeDocument &document, const String &methodName, const Range<int> range) { CodeDocument::Position pos (document, range.getStart()); AttributedString as; as.append ("Method: ", Colours::black); as.append (methodName, Colours::blue); as.append ("\tline: ", Colours::black); as.append (String(pos.getLineNumber()+1), Colours::darkgreen); as.append ("\tstart: ", Colours::black); as.append (String(range.getStart()), Colours::darkgreen); as.append ("\tend: ", Colours::black); as.append (String(range.getEnd()), Colours::darkgreen); owner.getMethodEditArea()->insertOutput (as); }
//============================================================================== void CodeEditorComponent::codeDocumentChanged (const CodeDocument::Position& affectedTextStart, const CodeDocument::Position& affectedTextEnd) { clearCachedIterators (affectedTextStart.getLineNumber()); triggerAsyncUpdate(); ((CaretComponent*) caret)->updatePosition (*this); if (affectedTextEnd.getPosition() >= selectionStart.getPosition() && affectedTextStart.getPosition() <= selectionEnd.getPosition()) deselectAll(); if (caretPos.getPosition() > affectedTextEnd.getPosition() || caretPos.getPosition() < affectedTextStart.getPosition()) moveCaretTo (affectedTextStart, false); updateScrollBars(); }
void MLDebugDisplay::display() { const ScopedLock lock(mStreamLock); // if any text is selected let debug output accumulate in stream. int selectSize = mpComp->getHighlightedRegion().getLength(); if(selectSize > 0) return; mStream.flush(); std::string outStr = mStream.str(); const char* pOutput = outStr.c_str(); String newStr(pOutput); // erase stream contents mStream.str(std::string()); int len = newStr.length(); if (len > 0) { // delete beginning if too big const CodeDocument::Position pos = mpComp->getCaretPos(); const int tc = pos.getPosition(); if (tc > kMLDebugMaxChars) { mpComp->loadContent(""); debug() << "----debug data > " << (int)kMLDebugMaxChars << " bytes, truncated----\n\n"; } int lastDocLine = mDoc.getNumLines(); int startLine = mpComp->getFirstLineOnScreen(); int endLine = startLine + mpComp->getNumLinesOnScreen(); mpComp->moveCaretToEnd(false); mpComp->insertTextAtCaret(newStr); // if end of doc is off screen, reset saved position (don't scroll to text) if(lastDocLine > endLine + 1) { mpComp->scrollToLine(startLine); } } }
void CodeEditorComponent::indentSelectedLines (const int spacesToAdd) { newTransaction(); CodeDocument::Position oldSelectionStart (selectionStart), oldSelectionEnd (selectionEnd), oldCaret (caretPos); oldSelectionStart.setPositionMaintained (true); oldSelectionEnd.setPositionMaintained (true); oldCaret.setPositionMaintained (true); const int lineStart = selectionStart.getLineNumber(); int lineEnd = selectionEnd.getLineNumber(); if (lineEnd > lineStart && selectionEnd.getIndexInLine() == 0) --lineEnd; for (int line = lineStart; line <= lineEnd; ++line) { const String lineText (document.getLine (line)); const int nonWhitespaceStart = CodeEditorHelpers::findFirstNonWhitespaceChar (lineText); if (nonWhitespaceStart > 0 || lineText.trimStart().isNotEmpty()) { const CodeDocument::Position wsStart (document, line, 0); const CodeDocument::Position wsEnd (document, line, nonWhitespaceStart); const int numLeadingSpaces = indexToColumn (line, wsEnd.getIndexInLine()); const int newNumLeadingSpaces = jmax (0, numLeadingSpaces + spacesToAdd); if (newNumLeadingSpaces != numLeadingSpaces) { document.deleteSection (wsStart, wsEnd); document.insertText (wsStart, getTabString (newNumLeadingSpaces)); } } } selectionStart = oldSelectionStart; selectionEnd = oldSelectionEnd; caretPos = oldCaret; }
void CodeEditor::codeDocumentChanged(const CodeDocument::Position& affectedTextStart, const CodeDocument::Position& affectedTextEnd) { static const String BRACKETS[] = {String(L"("), String(L")"), String(L"{"), String(L"}"), String(L"["), String(L"]"), String(L"<"), String(L">"), String(L"'"), String(L"'"), String(L"\""), String(L"\"")}; if(do_bracket_closing.getValue() && affectedTextEnd.getPosition() - affectedTextStart.getPosition() == 1){ //assume that the length of the affected text is 1 //We're finding an opening character and inserting the other one. const String C = String::charToString(affectedTextStart.getCharacter()); int index = 0; for(; index < numElementsInArray(BRACKETS); index += 2){ //search for an existing parenthesis if(C == BRACKETS[index]){ break; } } if(index < numElementsInArray(BRACKETS)){ //If the index is out of range, we couldn't find the first character editor->insertTextAtCaret(BRACKETS[index+1]); editor->moveCaretTo(affectedTextEnd, false); } } static const String LOOK_UP_CHARS[] = {String(L"{"), String(L"}")}; if(do_auto_indent.getValue() && affectedTextStart.getLineNumber() < affectedTextEnd.getLineNumber()){ //assume that we're at the beginning of a line //calculate the number of tabs that will be inserted to the next line const String LINE_ABOVE = affectedTextStart.getLineText(), LINE = affectedTextEnd.getLineText(); const int TAB_SIZE = editor->getTabSize(); const int LENGTH_BEFORE = LINE_ABOVE.trimEnd().length(); const int LENGTH_AFTER = LINE_ABOVE.trimStart().length(); const String TARGET_STRINGS[] = {String::charToString(affectedTextStart.movedBy(-1).getCharacter()), String::charToString(affectedTextEnd.getCharacter())}; int tab_size_to_add = (TARGET_STRINGS[0] == LOOK_UP_CHARS[0] && TARGET_STRINGS[1] != LOOK_UP_CHARS[1]) ? ((LENGTH_AFTER - LENGTH_BEFORE) / TAB_SIZE) + 1 //add another tab : (TARGET_STRINGS[1] == LOOK_UP_CHARS[1] && TARGET_STRINGS[0] != LOOK_UP_CHARS[0]) ? ((LENGTH_AFTER - LENGTH_BEFORE) / TAB_SIZE) - 1 //remove one tab : ((LENGTH_AFTER - LENGTH_BEFORE) / TAB_SIZE); for(; tab_size_to_add > 0; --tab_size_to_add){ editor->insertTabAtCaret(); } } }
const CodeDocument::Position CodeDocument::Position::movedBy (const int characterDelta) const { CodeDocument::Position p (*this); p.moveBy (characterDelta); return p; }
bool update (CodeDocument& document, int lineNum, CodeDocument::Iterator& source, CodeTokeniser* analyser, const int spacesPerTab, const CodeDocument::Position& selectionStart, const CodeDocument::Position& selectionEnd) { OwnedArray <SyntaxToken> newTokens; if (analyser == 0) { newTokens.add (new SyntaxToken (document.getLine (lineNum), -1)); } else if (lineNum < document.getNumLines()) { const CodeDocument::Position pos (&document, lineNum, 0); createTokens (pos.getPosition(), pos.getLineText(), source, analyser, newTokens); } replaceTabsWithSpaces (newTokens, spacesPerTab); int newHighlightStart = 0; int newHighlightEnd = 0; if (selectionStart.getLineNumber() <= lineNum && selectionEnd.getLineNumber() >= lineNum) { const String line (document.getLine (lineNum)); CodeDocument::Position lineStart (&document, lineNum, 0), lineEnd (&document, lineNum + 1, 0); newHighlightStart = indexToColumn (jmax (0, selectionStart.getPosition() - lineStart.getPosition()), line, spacesPerTab); newHighlightEnd = indexToColumn (jmin (lineEnd.getPosition() - lineStart.getPosition(), selectionEnd.getPosition() - lineStart.getPosition()), line, spacesPerTab); } if (newHighlightStart != highlightColumnStart || newHighlightEnd != highlightColumnEnd) { highlightColumnStart = newHighlightStart; highlightColumnEnd = newHighlightEnd; } else { if (tokens.size() == newTokens.size()) { bool allTheSame = true; for (int i = newTokens.size(); --i >= 0;) { if (*tokens.getUnchecked(i) != *newTokens.getUnchecked(i)) { allTheSame = false; break; } } if (allTheSame) return false; } } tokens.swapWithArray (newTokens); return true; }
bool CtrlrLuaMethodCodeEditor::keyStateChanged (bool isKeyDown, Component *originatingComponent) { CodeDocument::Position pos = editorComponent->getCaretPos(); owner.setPositionLabelText ("Line: " + String(pos.getLineNumber()+1) + " Column: " + String(pos.getIndexInLine())); return (false); }
bool CtrlrLuaMethodCodeEditor::keyPressed (const KeyPress &key, Component *originatingComponent) { if (key.getModifiers().isCommandDown() ) { if(key.getKeyCode() == 9) { owner.keyPressed (key, originatingComponent); return (true); } if (key.getKeyCode() == 83) // CTRL + S { saveDocument(); return (true); } if (CharacterFunctions::toUpperCase ((juce_wchar) (key.getKeyCode())) == 70) // CTRL + F { // Show search Dialog editorComponent->showFindPanel(); return (true); } if (CharacterFunctions::toUpperCase ((juce_wchar) (key.getKeyCode())) == 71) // CTRL + G { // Show Go To Dialog editorComponent->showGoTOPanel(); return (true); } if (CharacterFunctions::toUpperCase ((juce_wchar) (key.getKeyCode())) == 72) // CTRL + H { // Show search Dialog editorComponent->showFindPanel(true); return (true); } if (key.getKeyCode() == KeyPress::deleteKey) // CTRL + Delete { owner.keyPressed(key, originatingComponent); return (true); } // search selected previous in current if (key.getModifiers().isShiftDown() && key.getKeyCode() == KeyPress::F3Key) // CTRL + SHIFT + F3 { editorComponent->findSelection(false); return (true); } } // search selected next in current if (key.getModifiers().isShiftDown() && key.getKeyCode() == KeyPress::F3Key) // SHIFT + F3 { editorComponent->findSelection(true); return (true); } if (key.getKeyCode() == KeyPress::F7Key) { saveAndCompileDocument(); return (true); } if (key.getKeyCode() == KeyPress::F8Key) { owner.saveAndCompilAllMethods(); return (true); } CodeDocument::Position pos = editorComponent->getCaretPos(); owner.setPositionLabelText ("Line: " + String(pos.getLineNumber()+1) + " Column: " + String(pos.getIndexInLine())); return (false); }
void CodeEditorComponent::mouseDoubleClick (const MouseEvent& e) { CodeDocument::Position tokenStart (getPositionAt (e.x, e.y)); CodeDocument::Position tokenEnd (tokenStart); if (e.getNumberOfClicks() > 2) { tokenStart.setLineAndIndex (tokenStart.getLineNumber(), 0); tokenEnd.setLineAndIndex (tokenStart.getLineNumber() + 1, 0); } else { while (CharacterFunctions::isLetterOrDigit (tokenEnd.getCharacter())) tokenEnd.moveBy (1); tokenStart = tokenEnd; while (tokenStart.getIndexInLine() > 0 && CharacterFunctions::isLetterOrDigit (tokenStart.movedBy (-1).getCharacter())) tokenStart.moveBy (-1); } moveCaretTo (tokenEnd, false); moveCaretTo (tokenStart, true); }
const CodeDocument::Position CodeDocument::Position::movedByLines (const int deltaLines) const { CodeDocument::Position p (*this); p.setLineAndIndex (getLineNumber() + deltaLines, getIndexInLine()); return p; }
void CodeDocument::remove (const int startPos, const int endPos, const bool undoable) { if (endPos <= startPos) return; if (undoable) { undoManager.perform (new CodeDocumentDeleteAction (*this, startPos, endPos)); } else { Position startPosition (this, startPos); Position endPosition (this, endPos); maximumLineLength = -1; const int firstAffectedLine = startPosition.getLineNumber(); const int endLine = endPosition.getLineNumber(); int lastAffectedLine = firstAffectedLine + 1; CodeDocumentLine* const firstLine = lines.getUnchecked (firstAffectedLine); if (firstAffectedLine == endLine) { firstLine->line = firstLine->line.substring (0, startPosition.getIndexInLine()) + firstLine->line.substring (endPosition.getIndexInLine()); firstLine->updateLength(); } else { lastAffectedLine = lines.size(); CodeDocumentLine* const lastLine = lines.getUnchecked (endLine); jassert (lastLine != nullptr); firstLine->line = firstLine->line.substring (0, startPosition.getIndexInLine()) + lastLine->line.substring (endPosition.getIndexInLine()); firstLine->updateLength(); int numLinesToRemove = endLine - firstAffectedLine; lines.removeRange (firstAffectedLine + 1, numLinesToRemove); } int i; for (i = firstAffectedLine + 1; i < lines.size(); ++i) { CodeDocumentLine* const l = lines.getUnchecked (i); const CodeDocumentLine* const previousLine = lines.getUnchecked (i - 1); l->lineStartInFile = previousLine->lineStartInFile + previousLine->lineLength; } checkLastLineStatus(); const int totalChars = getNumCharacters(); for (i = 0; i < positionsToMaintain.size(); ++i) { CodeDocument::Position* p = positionsToMaintain.getUnchecked(i); if (p->getPosition() > startPosition.getPosition()) p->setPosition (jmax (startPos, p->getPosition() + startPos - endPos)); if (p->getPosition() > totalChars) p->setPosition (totalChars); } sendListenerChangeMessage (firstAffectedLine, lastAffectedLine); } }
const bool CtrlrLuaMethodCodeEditor::isMouseOverUrl(CodeDocument::Position &position, String *url) { if (position.getPosition() >= document.getNumCharacters()) { return (false); } int moveLeft=0; while (!CharacterFunctions::isWhitespace(position.getCharacter())) { if (position.getPosition() <= 0) break; position.moveBy(-1); moveLeft++; } int start = position.getPosition(); position.setPosition (position.getPosition()+moveLeft); while (!CharacterFunctions::isWhitespace(position.getCharacter())) { if (position.getPosition() >= document.getNumCharacters()) break; position.moveBy(1); } int end = position.getPosition(); const String word = document.getTextBetween (CodeDocument::Position(document, start), CodeDocument::Position(document, end)).trim(); if (word.startsWith ("http://")) { if (url) { *url = word; } return (URL::isProbablyAWebsiteURL (word)); } return (false); }