1
0
Fork 0
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:
jules 2012-07-20 20:57:39 +01:00
parent 21d7a993ab
commit 8ee1897916
5 changed files with 202 additions and 131 deletions

View file

@ -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);
}

View file

@ -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);
};

View file

@ -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);

View file

@ -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();
}

View file

@ -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;