1
0
Fork 0
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:
blat-blatnik 2022-01-11 22:48:08 +01:00
parent ed6959973a
commit 85ac4d5f83
4 changed files with 129 additions and 7 deletions

View file

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

View file

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

View file

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

View file

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