mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Introjucer: some code editor fixes and functionality.
This commit is contained in:
parent
21d7a993ab
commit
8ee1897916
5 changed files with 202 additions and 131 deletions
|
|
@ -158,3 +158,143 @@ void SourceCodeEditor::valueTreeChildRemoved (ValueTree&, ValueTree&)
|
|||
void SourceCodeEditor::valueTreeChildOrderChanged (ValueTree&) { updateColourScheme(); }
|
||||
void SourceCodeEditor::valueTreeParentChanged (ValueTree&) { updateColourScheme(); }
|
||||
void SourceCodeEditor::valueTreeRedirected (ValueTree&) { updateColourScheme(); }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
namespace CppUtils
|
||||
{
|
||||
static CPlusPlusCodeTokeniser* getCppTokeniser()
|
||||
{
|
||||
static CPlusPlusCodeTokeniser cppTokeniser;
|
||||
return &cppTokeniser;
|
||||
}
|
||||
|
||||
static String getLeadingWhitespace (String line)
|
||||
{
|
||||
line = line.removeCharacters ("\r\n");
|
||||
const String::CharPointerType endOfLeadingWS (line.getCharPointer().findEndOfWhitespace());
|
||||
return String (line.getCharPointer(), endOfLeadingWS);
|
||||
}
|
||||
|
||||
static bool getIndentForCurrentBlock (CodeDocument::Position pos, String& whitespace)
|
||||
{
|
||||
int braceCount = 0;
|
||||
|
||||
while (pos.getLineNumber() > 0)
|
||||
{
|
||||
pos = pos.movedByLines (-1);
|
||||
|
||||
const String line (pos.getLineText());
|
||||
const String trimmedLine (line.trimStart());
|
||||
|
||||
String::CharPointerType l (trimmedLine.getCharPointer());
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const juce_wchar c = l.getAndAdvance();
|
||||
|
||||
if (c == 0)
|
||||
break;
|
||||
|
||||
if (c == '}')
|
||||
++braceCount;
|
||||
|
||||
if (c == '{')
|
||||
{
|
||||
if (--braceCount < 0)
|
||||
{
|
||||
whitespace = getLeadingWhitespace (line);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '"' || c == '\'')
|
||||
{
|
||||
while (! (l.isEmpty() || l.getAndAdvance() == c))
|
||||
{}
|
||||
}
|
||||
|
||||
if (c == '/' && *l == '/')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CppCodeEditorComponent::CppCodeEditorComponent (CodeDocument& codeDocument)
|
||||
: CodeEditorComponent (codeDocument, CppUtils::getCppTokeniser())
|
||||
{
|
||||
}
|
||||
|
||||
void CppCodeEditorComponent::handleReturnKey()
|
||||
{
|
||||
CodeEditorComponent::handleReturnKey();
|
||||
|
||||
const CodeDocument::Position pos (getCaretPos());
|
||||
|
||||
if (pos.getLineNumber() > 0 && pos.getLineText().trim().isEmpty())
|
||||
{
|
||||
String indent;
|
||||
CppUtils::getIndentForCurrentBlock (pos, indent);
|
||||
|
||||
const String previousLine (pos.movedByLines (-1).getLineText());
|
||||
const String trimmedPreviousLine (previousLine.trim());
|
||||
const String leadingWhitespace (CppUtils::getLeadingWhitespace (previousLine));
|
||||
|
||||
insertTextAtCaret (leadingWhitespace);
|
||||
|
||||
if (trimmedPreviousLine.endsWithChar ('{')
|
||||
|| ((trimmedPreviousLine.startsWith ("if ")
|
||||
|| trimmedPreviousLine.startsWith ("for ")
|
||||
|| trimmedPreviousLine.startsWith ("while "))
|
||||
&& trimmedPreviousLine.endsWithChar (')')))
|
||||
insertTabAtCaret();
|
||||
}
|
||||
}
|
||||
|
||||
void CppCodeEditorComponent::insertTextAtCaret (const String& newText)
|
||||
{
|
||||
if (getHighlightedRegion().isEmpty())
|
||||
{
|
||||
const CodeDocument::Position pos (getCaretPos());
|
||||
|
||||
if ((newText == "{" || newText == "}")
|
||||
&& pos.getLineNumber() > 0
|
||||
&& pos.getLineText().trim().isEmpty())
|
||||
{
|
||||
moveCaretToStartOfLine (true);
|
||||
|
||||
String whitespace;
|
||||
if (CppUtils::getIndentForCurrentBlock (pos, whitespace))
|
||||
{
|
||||
CodeEditorComponent::insertTextAtCaret (whitespace);
|
||||
|
||||
if (newText == "{")
|
||||
insertTabAtCaret();
|
||||
}
|
||||
}
|
||||
else if (newText == getDocument().getNewLineCharacters()
|
||||
&& pos.getLineNumber() > 0)
|
||||
{
|
||||
const String remainderOfLine (pos.getLineText().substring (pos.getIndexInLine()));
|
||||
|
||||
if (remainderOfLine.startsWithChar ('{') || remainderOfLine.startsWithChar ('}'))
|
||||
{
|
||||
String whitespace;
|
||||
if (CppUtils::getIndentForCurrentBlock (pos, whitespace))
|
||||
{
|
||||
CodeEditorComponent::insertTextAtCaret (newText + whitespace);
|
||||
|
||||
if (remainderOfLine.startsWithChar ('{'))
|
||||
insertTabAtCaret();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CodeEditorComponent::insertTextAtCaret (newText);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
class SourceCodeDocument : public OpenDocumentManager::Document
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
SourceCodeDocument (Project*, const File&);
|
||||
|
||||
bool loadedOk() const { return true; }
|
||||
|
|
@ -114,128 +113,13 @@ private:
|
|||
class CppCodeEditorComponent : public CodeEditorComponent
|
||||
{
|
||||
public:
|
||||
CppCodeEditorComponent (CodeDocument& codeDocument)
|
||||
: CodeEditorComponent (codeDocument, getCppTokeniser())
|
||||
{
|
||||
}
|
||||
CppCodeEditorComponent (CodeDocument& codeDocument);
|
||||
|
||||
void handleReturnKey()
|
||||
{
|
||||
CodeEditorComponent::handleReturnKey();
|
||||
|
||||
const CodeDocument::Position pos (getCaretPos());
|
||||
|
||||
if (pos.getLineNumber() > 0 && pos.getLineText().trim().isEmpty())
|
||||
{
|
||||
String indent;
|
||||
getIndentForCurrentBlock (pos, indent);
|
||||
|
||||
const String previousLine (pos.movedByLines (-1).getLineText());
|
||||
const String trimmedPreviousLine (previousLine.trim());
|
||||
const String leadingWhitespace (getLeadingWhitespace (previousLine));
|
||||
|
||||
insertTextAtCaret (leadingWhitespace);
|
||||
|
||||
if (trimmedPreviousLine.endsWithChar ('{')
|
||||
|| ((trimmedPreviousLine.startsWith ("if ")
|
||||
|| trimmedPreviousLine.startsWith ("for ")
|
||||
|| trimmedPreviousLine.startsWith ("while "))
|
||||
&& trimmedPreviousLine.endsWithChar (')')))
|
||||
insertTabAtCaret();
|
||||
}
|
||||
}
|
||||
|
||||
void insertTextAtCaret (const String& newText)
|
||||
{
|
||||
if (getHighlightedRegion().isEmpty())
|
||||
{
|
||||
const CodeDocument::Position pos (getCaretPos());
|
||||
|
||||
if ((newText == "{" || newText == "}")
|
||||
&& pos.getLineNumber() > 0
|
||||
&& pos.getLineText().trim().isEmpty())
|
||||
{
|
||||
moveCaretToStartOfLine (true);
|
||||
|
||||
String whitespace;
|
||||
if (getIndentForCurrentBlock (pos, whitespace))
|
||||
{
|
||||
CodeEditorComponent::insertTextAtCaret (whitespace);
|
||||
|
||||
if (newText == "{")
|
||||
insertTabAtCaret();
|
||||
}
|
||||
}
|
||||
else if (newText == getDocument().getNewLineCharacters()
|
||||
&& pos.getLineNumber() > 0)
|
||||
{
|
||||
const String remainderOfLine (pos.getLineText().substring (pos.getIndexInLine()));
|
||||
|
||||
if (remainderOfLine.startsWithChar ('{') || remainderOfLine.startsWithChar ('}'))
|
||||
{
|
||||
String whitespace;
|
||||
if (getIndentForCurrentBlock (pos, whitespace))
|
||||
{
|
||||
CodeEditorComponent::insertTextAtCaret (newText + whitespace);
|
||||
|
||||
if (remainderOfLine.startsWithChar ('{'))
|
||||
insertTabAtCaret();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CodeEditorComponent::insertTextAtCaret (newText);
|
||||
}
|
||||
void handleReturnKey();
|
||||
void insertTextAtCaret (const String& newText);
|
||||
|
||||
private:
|
||||
static CPlusPlusCodeTokeniser* getCppTokeniser()
|
||||
{
|
||||
static CPlusPlusCodeTokeniser cppTokeniser;
|
||||
return &cppTokeniser;
|
||||
}
|
||||
|
||||
static String getLeadingWhitespace (String line)
|
||||
{
|
||||
line = line.removeCharacters ("\r\n");
|
||||
const String::CharPointerType endOfLeadingWS (line.getCharPointer().findEndOfWhitespace());
|
||||
return String (line.getCharPointer(), endOfLeadingWS);
|
||||
}
|
||||
|
||||
static bool getIndentForCurrentBlock (CodeDocument::Position pos, String& whitespace)
|
||||
{
|
||||
int braceCount = 0;
|
||||
|
||||
while (pos.getLineNumber() > 0)
|
||||
{
|
||||
pos = pos.movedByLines (-1);
|
||||
|
||||
const String line (pos.getLineText());
|
||||
const String trimmedLine (line.trimStart());
|
||||
|
||||
StringArray tokens;
|
||||
tokens.addTokens (trimmedLine, true);
|
||||
|
||||
for (int i = tokens.size(); --i >= 0;)
|
||||
{
|
||||
if (tokens[i] == "}")
|
||||
++braceCount;
|
||||
|
||||
if (tokens[i] == "{")
|
||||
{
|
||||
if (--braceCount < 0)
|
||||
{
|
||||
whitespace = getLeadingWhitespace (line);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CppCodeEditorComponent);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ namespace Ids
|
|||
DECLARE_ID (line);
|
||||
DECLARE_ID (index);
|
||||
DECLARE_ID (type);
|
||||
DECLARE_ID (time);
|
||||
DECLARE_ID (extraCompilerFlags);
|
||||
DECLARE_ID (extraLinkerFlags);
|
||||
DECLARE_ID (extraDefs);
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ namespace CodeEditorHelpers
|
|||
class CodeEditorComponent::GutterComponent : public Component
|
||||
{
|
||||
public:
|
||||
GutterComponent() {}
|
||||
GutterComponent() : lastNumLines (0) {}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
|
|
@ -272,7 +272,8 @@ public:
|
|||
const Rectangle<int> clip (g.getClipBounds());
|
||||
const int lineHeight = editor.lineHeight;
|
||||
const int firstLineToDraw = jmax (0, clip.getY() / lineHeight);
|
||||
const int lastLineToDraw = jmin (editor.lines.size(), clip.getBottom() / lineHeight + 1);
|
||||
const int lastLineToDraw = jmin (editor.lines.size(), clip.getBottom() / lineHeight + 1,
|
||||
lastNumLines - editor.firstLineOnScreen);
|
||||
|
||||
const Font lineNumberFont (editor.getFont().withHeight (jmin (13.0f, lineHeight * 0.8f)));
|
||||
const float y = lineHeight - editor.getFont().getDescent();
|
||||
|
|
@ -286,6 +287,19 @@ public:
|
|||
g.setColour (editor.findColour (lineNumberTextId));
|
||||
ga.draw (g);
|
||||
}
|
||||
|
||||
void documentChanged (CodeDocument& doc)
|
||||
{
|
||||
const int newNumLines = doc.getNumLines();
|
||||
if (newNumLines != lastNumLines)
|
||||
{
|
||||
lastNumLines = newNumLines;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int lastNumLines;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -417,8 +431,9 @@ void CodeEditorComponent::codeDocumentChanged (const CodeDocument::Position& aff
|
|||
|
||||
void CodeEditorComponent::resized()
|
||||
{
|
||||
const int visibleWidth = getWidth() - scrollbarThickness - getGutterSize();
|
||||
linesOnScreen = jmax (1, (getHeight() - scrollbarThickness) / lineHeight);
|
||||
columnsOnScreen = jmax (1, (int) ((getWidth() - scrollbarThickness) / charWidth));
|
||||
columnsOnScreen = jmax (1, (int) (visibleWidth / charWidth));
|
||||
lines.clear();
|
||||
rebuildLineTokens();
|
||||
updateCaretPosition();
|
||||
|
|
@ -430,7 +445,7 @@ void CodeEditorComponent::resized()
|
|||
scrollbarThickness, getHeight() - scrollbarThickness);
|
||||
|
||||
horizontalScrollBar.setBounds (getGutterSize(), getHeight() - scrollbarThickness,
|
||||
getWidth() - scrollbarThickness - getGutterSize(), scrollbarThickness);
|
||||
visibleWidth, scrollbarThickness);
|
||||
updateScrollBars();
|
||||
}
|
||||
|
||||
|
|
@ -510,6 +525,9 @@ void CodeEditorComponent::rebuildLineTokens()
|
|||
if (minLineToRepaint <= maxLineToRepaint)
|
||||
repaint (0, lineHeight * minLineToRepaint - 1,
|
||||
verticalScrollBar.getX(), lineHeight * (1 + maxLineToRepaint - minLineToRepaint) + 2);
|
||||
|
||||
if (gutter != nullptr)
|
||||
gutter->documentChanged (document);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -639,10 +657,12 @@ void CodeEditorComponent::scrollToKeepCaretOnScreen()
|
|||
{
|
||||
if (getWidth() > 0 && getHeight() > 0)
|
||||
{
|
||||
if (caretPos.getLineNumber() < firstLineOnScreen)
|
||||
scrollBy (caretPos.getLineNumber() - firstLineOnScreen);
|
||||
else if (caretPos.getLineNumber() >= firstLineOnScreen + linesOnScreen)
|
||||
scrollBy (caretPos.getLineNumber() - (firstLineOnScreen + linesOnScreen - 1));
|
||||
const int caretLine = caretPos.getLineNumber();
|
||||
|
||||
if (caretLine < firstLineOnScreen)
|
||||
scrollBy (caretLine - firstLineOnScreen);
|
||||
else if (caretLine >= firstLineOnScreen + linesOnScreen)
|
||||
scrollBy (caretLine - (firstLineOnScreen + linesOnScreen - 1));
|
||||
|
||||
const int column = indexToColumn (caretPos.getLineNumber(), caretPos.getIndexInLine());
|
||||
if (column >= xOffset + columnsOnScreen - 1)
|
||||
|
|
@ -957,13 +977,38 @@ bool CodeEditorComponent::deleteBackwards (const bool moveInWholeWordSteps)
|
|||
else
|
||||
{
|
||||
if (selectionStart == selectionEnd)
|
||||
selectionStart.moveBy (-1);
|
||||
{
|
||||
if (! skipBackwardsToPreviousTab())
|
||||
selectionStart.moveBy (-1);
|
||||
}
|
||||
}
|
||||
|
||||
cut();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CodeEditorComponent::skipBackwardsToPreviousTab()
|
||||
{
|
||||
const String currentLineText (caretPos.getLineText().removeCharacters ("\r\n"));
|
||||
const int currentIndex = caretPos.getIndexInLine();
|
||||
|
||||
if (currentLineText.isNotEmpty() && currentLineText.length() == currentIndex)
|
||||
{
|
||||
const int currentLine = caretPos.getLineNumber();
|
||||
const int currentColumn = indexToColumn (currentLine, currentIndex);
|
||||
const int previousTabColumn = (currentColumn - 1) - ((currentColumn - 1) % spacesPerTab);
|
||||
const int previousTabIndex = columnToIndex (currentLine, previousTabColumn);
|
||||
|
||||
if (currentLineText.substring (previousTabIndex, currentIndex).trim().isEmpty())
|
||||
{
|
||||
selectionStart.moveBy (previousTabIndex - currentIndex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CodeEditorComponent::deleteForwards (const bool moveInWholeWordSteps)
|
||||
{
|
||||
if (moveInWholeWordSteps)
|
||||
|
|
@ -1445,8 +1490,8 @@ CodeEditorComponent::State::State (const String& s)
|
|||
StringArray tokens;
|
||||
tokens.addTokens (s, ":", String::empty);
|
||||
|
||||
lastTopLine = tokens[0].getIntValue();
|
||||
lastCaretPos = tokens[1].getIntValue();
|
||||
lastTopLine = tokens[0].getIntValue();
|
||||
lastCaretPos = tokens[1].getIntValue();
|
||||
lastSelectionEnd = tokens[2].getIntValue();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -389,6 +389,7 @@ private:
|
|||
void newTransaction();
|
||||
void cut();
|
||||
void indentSelectedLines (int spacesToAdd);
|
||||
bool skipBackwardsToPreviousTab();
|
||||
|
||||
int indexToColumn (int line, int index) const noexcept;
|
||||
int columnToIndex (int line, int column) const noexcept;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue