mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-10 04:22:20 +02:00
Hilite matching parentheses under cursor
This commit is contained in:
parent
ed6959973a
commit
85ac4d5f83
4 changed files with 129 additions and 7 deletions
|
@ -185,13 +185,14 @@ namespace Beefy.theme.dark
|
|||
COUNT
|
||||
};
|
||||
|
||||
public static uint32 COLOR_TEXT = 0xFFFFFFFF;
|
||||
public static uint32 COLOR_WINDOW = 0xFF595962;
|
||||
public static uint32 COLOR_BKG = 0xFF26262A;
|
||||
public static uint32 COLOR_SELECTED_OUTLINE = 0xFFCFAE11;
|
||||
public static uint32 COLOR_MENU_FOCUSED = 0xFFE5A910;
|
||||
public static uint32 COLOR_MENU_SELECTED = 0xFFCB9B80;
|
||||
public static uint32 COLOR_CURRENT_LINE_HILITE = 0xFF4C4C54;
|
||||
public static uint32 COLOR_TEXT = 0xFFFFFFFF;
|
||||
public static uint32 COLOR_WINDOW = 0xFF595962;
|
||||
public static uint32 COLOR_BKG = 0xFF26262A;
|
||||
public static uint32 COLOR_SELECTED_OUTLINE = 0xFFCFAE11;
|
||||
public static uint32 COLOR_MENU_FOCUSED = 0xFFE5A910;
|
||||
public static uint32 COLOR_MENU_SELECTED = 0xFFCB9B80;
|
||||
public static uint32 COLOR_CURRENT_LINE_HILITE = 0xFF4C4C54;
|
||||
public static uint32 COLOR_MATCHING_PARENS_HILITE = 0x28FFFFFF;
|
||||
|
||||
public static float sScale = 1.0f;
|
||||
public static int32 sSrcImgScale = 1;
|
||||
|
|
|
@ -2661,6 +2661,12 @@ namespace Beefy.widgets
|
|||
GetLineAndColumnAtCoord(x, y, out line2, out column);
|
||||
}
|
||||
|
||||
public void GetLineColumnAtIdx(int idx, out int line, out int column)
|
||||
{
|
||||
GetLineCharAtIdx(idx, out line, var lineChar);
|
||||
GetColumnAtLineChar(line, lineChar, out column);
|
||||
}
|
||||
|
||||
public virtual void GetLineCharAtIdx(int idx, out int line, out int theChar)
|
||||
{
|
||||
int lineA;
|
||||
|
|
|
@ -326,6 +326,7 @@ namespace IDE
|
|||
public Color mVisibleWhiteSpace = 0xFF9090C0;
|
||||
public Color mCurrentLineHilite = 0xFF4C4C54;
|
||||
public Color mCurrentLineNumberHilite = 0x18FFFFFF;
|
||||
public Color mMatchingParensHilite = 0x28FFFFFF;
|
||||
|
||||
public void Deserialize(StructuredData sd)
|
||||
{
|
||||
|
@ -386,6 +387,7 @@ namespace IDE
|
|||
GetColor("VisibleWhiteSpace", ref mVisibleWhiteSpace);
|
||||
GetColor("CurrentLineHilite", ref mCurrentLineHilite);
|
||||
GetColor("CurrentLineNumberHilite", ref mCurrentLineNumberHilite);
|
||||
GetColor("MatchingParensHilite", ref mMatchingParensHilite);
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
|
@ -417,6 +419,7 @@ namespace IDE
|
|||
DarkTheme.COLOR_MENU_FOCUSED = mMenuFocused;
|
||||
DarkTheme.COLOR_MENU_SELECTED = mMenuSelected;
|
||||
DarkTheme.COLOR_CURRENT_LINE_HILITE = mCurrentLineHilite;
|
||||
DarkTheme.COLOR_MATCHING_PARENS_HILITE = mMatchingParensHilite;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -183,6 +183,14 @@ namespace IDE.ui
|
|||
OnlyShowInvoke = 4
|
||||
}
|
||||
|
||||
enum HiliteMatchingParensPositionCache
|
||||
{
|
||||
//TODO: Better naming?
|
||||
case NeedToRecalculate;
|
||||
case UnmatchedParens;
|
||||
case Valid(float x1, float y1, float x2, float y2);
|
||||
}
|
||||
|
||||
public delegate void(char32, AutoCompleteOptions) mOnGenerateAutocomplete ~ delete _;
|
||||
public Action mOnFinishAsyncAutocomplete ~ delete _;
|
||||
public Action mOnCancelAsyncAutocomplete ~ delete _;
|
||||
|
@ -228,6 +236,7 @@ namespace IDE.ui
|
|||
bool mHasCustomColors;
|
||||
FastCursorState mFastCursorState ~ delete _;
|
||||
public HashSet<int32> mCurParenPairIdSet = new .() ~ delete _;
|
||||
HiliteMatchingParensPositionCache mMatchingParensPositionCache = .NeedToRecalculate;
|
||||
|
||||
public List<PersistentTextPosition> PersistentTextPositions
|
||||
{
|
||||
|
@ -3047,6 +3056,7 @@ namespace IDE.ui
|
|||
{
|
||||
base.ContentChanged();
|
||||
mCursorStillTicks = 0;
|
||||
mMatchingParensPositionCache = .NeedToRecalculate;
|
||||
if (mSourceViewPanel != null)
|
||||
{
|
||||
if (mSourceViewPanel.mProjectSource != null)
|
||||
|
@ -4557,6 +4567,7 @@ namespace IDE.ui
|
|||
|
||||
base.PhysCursorMoved(moveKind);
|
||||
mCursorStillTicks = 0;
|
||||
mMatchingParensPositionCache = .NeedToRecalculate;
|
||||
|
||||
if ((mSourceViewPanel != null) && (mSourceViewPanel.mHoverWatch != null))
|
||||
mSourceViewPanel.mHoverWatch.Close();
|
||||
|
@ -4703,6 +4714,107 @@ namespace IDE.ui
|
|||
{
|
||||
base.Draw(g);
|
||||
|
||||
// Highlight matching parenthesis under cursor
|
||||
if (mEditWidget.mHasFocus && !HasSelection())
|
||||
{
|
||||
if (mMatchingParensPositionCache case .NeedToRecalculate)
|
||||
{
|
||||
mMatchingParensPositionCache = .UnmatchedParens;
|
||||
|
||||
bool IsParenthesisAt(int textIndex)
|
||||
{
|
||||
switch (SafeGetChar(textIndex))
|
||||
{
|
||||
// Ignore parentheses in comments.
|
||||
case '(',')', '[',']', '{','}': return (SourceElementType)mData.mText[textIndex].mDisplayTypeId != .Comment;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsOpenParenthesis(char8 c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '(', '{', '[': return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result<char8> GetMatchingParenthesis(char8 paren)
|
||||
{
|
||||
switch (paren)
|
||||
{
|
||||
case '(': return ')';
|
||||
case '{': return '}';
|
||||
case '[': return ']';
|
||||
|
||||
case ')': return '(';
|
||||
case '}': return '{';
|
||||
case ']': return '[';
|
||||
|
||||
default: return .Err;
|
||||
}
|
||||
}
|
||||
|
||||
int parenIndex = -1;
|
||||
// If there is a parenthesis to the right of the cursor, match that one.
|
||||
// Otherwise, if there is one to the left of the cursor, match that.
|
||||
// This is what Visual Studio and VS Code do.
|
||||
// Notepad++ tries to match to the left of the cursor first, but I think the other way makes more sense.
|
||||
if (IsParenthesisAt(CursorTextPos))
|
||||
parenIndex = CursorTextPos;
|
||||
else if (IsParenthesisAt(CursorTextPos - 1))
|
||||
parenIndex = CursorTextPos - 1;
|
||||
|
||||
if (parenIndex != -1)
|
||||
{
|
||||
char8 paren = mData.mText[parenIndex].mChar;
|
||||
char8 matchingParen = GetMatchingParenthesis(paren);
|
||||
int dir = IsOpenParenthesis(paren) ? +1 : -1;
|
||||
|
||||
int matchingParenIndex = -1;
|
||||
int stackCount = 1;
|
||||
for (int i = parenIndex + dir; i >= 0 && i < mData.mTextLength; i += dir)
|
||||
{
|
||||
if ((SourceElementType)mData.mText[i].mDisplayTypeId != .Comment)
|
||||
{
|
||||
char8 char = mData.mText[i].mChar;
|
||||
if (char == paren)
|
||||
++stackCount;
|
||||
else if (char == matchingParen)
|
||||
--stackCount;
|
||||
|
||||
if (stackCount == 0)
|
||||
{
|
||||
matchingParenIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingParenIndex != -1)
|
||||
{
|
||||
GetLineColumnAtIdx(parenIndex, var line1, var column1);
|
||||
GetLineColumnAtIdx(matchingParenIndex, var line2, var column2);
|
||||
GetTextCoordAtLineAndColumn(line1, column1, var x1, var y1);
|
||||
GetTextCoordAtLineAndColumn(line2, column2, var x2, var y2);
|
||||
mMatchingParensPositionCache = .Valid(x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mMatchingParensPositionCache case .Valid(let x1, let y1, let x2, let y2))
|
||||
{
|
||||
let width = mFont.GetWidth(' ');
|
||||
let height = mFont.GetHeight() + GS!(2);
|
||||
using (g.PushColor(DarkTheme.COLOR_MATCHING_PARENS_HILITE))
|
||||
{
|
||||
g.FillRect(x1, y1, width, height);
|
||||
g.FillRect(x2, y2, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using (g.PushTranslate(mTextInsets.mLeft, mTextInsets.mTop))
|
||||
{
|
||||
for (var queuedUnderline in mQueuedUnderlines)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue