1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-20 00:50:25 +02:00

Paired char hiliting tweaks

This commit is contained in:
Brian Fiete 2022-01-18 13:26:43 -05:00
parent fa01e7ad17
commit 7e0d3407cc

View file

@ -183,11 +183,11 @@ namespace IDE.ui
OnlyShowInvoke = 4 OnlyShowInvoke = 4
} }
enum HiliteMatchingParensPositionCache enum HilitePairedCharState
{ {
case NeedToRecalculate; case NeedToRecalculate;
case UnmatchedParens; case UnmatchedParens;
case Valid(int cachedCursorTextPos, float x1, float y1, float x2, float y2); case Valid(int64 stateHash, float x1, float y1, float x2, float y2, float charWidth);
} }
public delegate void(char32, AutoCompleteOptions) mOnGenerateAutocomplete ~ delete _; public delegate void(char32, AutoCompleteOptions) mOnGenerateAutocomplete ~ delete _;
@ -235,7 +235,7 @@ namespace IDE.ui
bool mHasCustomColors; bool mHasCustomColors;
FastCursorState mFastCursorState ~ delete _; FastCursorState mFastCursorState ~ delete _;
public HashSet<int32> mCurParenPairIdSet = new .() ~ delete _; public HashSet<int32> mCurParenPairIdSet = new .() ~ delete _;
HiliteMatchingParensPositionCache mMatchingParensPositionCache = .NeedToRecalculate; HilitePairedCharState mHilitePairedCharState = .NeedToRecalculate;
public List<PersistentTextPosition> PersistentTextPositions public List<PersistentTextPosition> PersistentTextPositions
{ {
@ -3055,7 +3055,7 @@ namespace IDE.ui
{ {
base.ContentChanged(); base.ContentChanged();
mCursorStillTicks = 0; mCursorStillTicks = 0;
mMatchingParensPositionCache = .NeedToRecalculate; mHilitePairedCharState = .NeedToRecalculate;
if (mSourceViewPanel != null) if (mSourceViewPanel != null)
{ {
if (mSourceViewPanel.mProjectSource != null) if (mSourceViewPanel.mProjectSource != null)
@ -4578,7 +4578,7 @@ namespace IDE.ui
base.PhysCursorMoved(moveKind); base.PhysCursorMoved(moveKind);
mCursorStillTicks = 0; mCursorStillTicks = 0;
mMatchingParensPositionCache = .NeedToRecalculate; mHilitePairedCharState = .NeedToRecalculate;
if ((mSourceViewPanel != null) && (mSourceViewPanel.mHoverWatch != null)) if ((mSourceViewPanel != null) && (mSourceViewPanel.mHoverWatch != null))
mSourceViewPanel.mHoverWatch.Close(); mSourceViewPanel.mHoverWatch.Close();
@ -4725,35 +4725,38 @@ namespace IDE.ui
{ {
base.Draw(g); base.Draw(g);
// Highlight matching parenthesis under cursor // Highlight matching paired characters under cursor
if (mEditWidget.mHasFocus && !HasSelection()) if (mEditWidget.mHasFocus && !HasSelection())
{ {
if (mMatchingParensPositionCache case .Valid(var cachedCursorTextPos, ?, ?, ?, ?)) int64 stateHash = (.)(CursorTextPos ^ ((int64)mData.mCurTextVersionId << 31));
if (mHilitePairedCharState case .Valid(var cachedStateHash, ?, ?, ?, ?, ?))
{ {
//HACK: //HACK:
// We can't just rely on setting .NeedToRecalculate in PhysCursorMoved because it looks like sometimes the // We can't just rely on setting .NeedToRecalculate in PhysCursorMoved because it looks like sometimes the
// cursor moves without that being called. For example when you open a text buffer that was already opened // cursor moves without that being called. For example when you open a text buffer that was already opened
// before, the cursor will initially be at the old position where you left it before being moved to (0,0) // before, the cursor will initially be at the old position where you left it before being moved to (0,0)
// but this move to 0,0 won't be detected. // but this move to 0,0 won't be detected.
if (cachedCursorTextPos != CursorTextPos) if (cachedStateHash != cachedStateHash)
mMatchingParensPositionCache = .NeedToRecalculate; mHilitePairedCharState = .NeedToRecalculate;
} }
if (mMatchingParensPositionCache case .NeedToRecalculate) if (mHilitePairedCharState case .NeedToRecalculate)
{ {
mMatchingParensPositionCache = .UnmatchedParens; mHilitePairedCharState = .UnmatchedParens;
bool IsParenthesisAt(int textIndex) bool HasPairingCharAt(int textIndex)
{ {
switch (SafeGetChar(textIndex)) switch (SafeGetChar(textIndex))
{ {
// Ignore parentheses in comments. // Ignore parentheses in comments.
case '(',')', '[',']', '{','}': return (SourceElementType)mData.mText[textIndex].mDisplayTypeId != .Comment; case '(',')', '[',']', '{','}':
var typeId = (SourceElementType)mData.mText[textIndex].mDisplayTypeId;
return (typeId != .Comment) && (typeId != .Literal);
default: return false; default: return false;
} }
} }
bool IsOpenParenthesis(char8 c) bool IsOpenChar(char8 c)
{ {
switch (c) switch (c)
{ {
@ -4762,7 +4765,7 @@ namespace IDE.ui
} }
} }
Result<char8> GetMatchingParenthesis(char8 paren) Result<char8> GetOppositeChar(char8 paren)
{ {
switch (paren) switch (paren)
{ {
@ -4779,26 +4782,24 @@ namespace IDE.ui
} }
int parenIndex = -1; int parenIndex = -1;
// If there is a parenthesis to the right of the cursor, match that one. if (HasPairingCharAt(CursorTextPos - 1))
// 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; parenIndex = CursorTextPos - 1;
else if (HasPairingCharAt(CursorTextPos))
parenIndex = CursorTextPos;
if (parenIndex != -1) if (parenIndex != -1)
{ {
char8 paren = mData.mText[parenIndex].mChar; char8 paren = mData.mText[parenIndex].mChar;
char8 matchingParen = GetMatchingParenthesis(paren); let charWidth = mFont.GetWidth(paren);
int dir = IsOpenParenthesis(paren) ? +1 : -1; char8 matchingParen = GetOppositeChar(paren);
int dir = IsOpenChar(paren) ? +1 : -1;
int matchingParenIndex = -1; int matchingParenIndex = -1;
int stackCount = 1; int stackCount = 1;
for (int i = parenIndex + dir; i >= 0 && i < mData.mTextLength; i += dir) for (int i = parenIndex + dir; i >= 0 && i < mData.mTextLength; i += dir)
{ {
if ((SourceElementType)mData.mText[i].mDisplayTypeId != .Comment) var typeId = (SourceElementType)mData.mText[i].mDisplayTypeId;
if ((typeId != .Comment) && (typeId != .Literal))
{ {
char8 char = mData.mText[i].mChar; char8 char = mData.mText[i].mChar;
if (char == paren) if (char == paren)
@ -4820,19 +4821,18 @@ namespace IDE.ui
GetLineColumnAtIdx(matchingParenIndex, var line2, var column2); GetLineColumnAtIdx(matchingParenIndex, var line2, var column2);
GetTextCoordAtLineAndColumn(line1, column1, var x1, var y1); GetTextCoordAtLineAndColumn(line1, column1, var x1, var y1);
GetTextCoordAtLineAndColumn(line2, column2, var x2, var y2); GetTextCoordAtLineAndColumn(line2, column2, var x2, var y2);
mMatchingParensPositionCache = .Valid(CursorTextPos, x1, y1, x2, y2); mHilitePairedCharState = .Valid(stateHash, x1, y1, x2, y2, charWidth);
} }
} }
} }
if (mMatchingParensPositionCache case .Valid(?, let x1, let y1, let x2, let y2)) if (mHilitePairedCharState case .Valid(?, let x1, let y1, let x2, let y2, let charWidth))
{ {
let width = mFont.GetWidth(' ');
let height = mFont.GetHeight() + GS!(2); let height = mFont.GetHeight() + GS!(2);
using (g.PushColor(DarkTheme.COLOR_MATCHING_PARENS_HILITE)) using (g.PushColor(DarkTheme.COLOR_MATCHING_PARENS_HILITE))
{ {
g.FillRect(x1, y1, width, height); g.FillRect(x1, y1, charWidth, height);
g.FillRect(x2, y2, width, height); g.FillRect(x2, y2, charWidth, height);
} }
} }
} }