mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
Auto-Close brackets improvements:
1. Word boundary behavior: Typing an opening bracket directly preceding an alphanumeric character prevents auto-closing. (e.g. typing ( before word correctly inserts ( without closing it). 2. Whitespace boundary behavior: Typing an opening bracket before a space triggers normal auto-closing. 3. Forward unbalanced brackets: Typing an opening bracket when there's an unmatched closing bracket ahead on the same line prevents auto-closing (so typing ( when the line is () ) doesn't insert an extra )). 4. Balanced brackets: Regular auto-closing when brackets are completely matched. 5. Quote balancing: If the line already has balanced quotes and your cursor is right before a quote, typing a quote will just overwrite/step over the right quote instead of adding a new pair. (SpartanJ/ecode#888)
This commit is contained in:
@@ -2214,27 +2214,67 @@ std::vector<bool> TextDocument::autoCloseBrackets( const String& text ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isClose && !isSame )
|
||||
if ( isClose && !isSame ) {
|
||||
mustClose = false;
|
||||
} else if ( !isClose && !isNonWord( ch ) ) {
|
||||
mustClose = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( mustClose && isSame ) {
|
||||
Int64 left = sel.start().column() - 1;
|
||||
Int64 right = sel.start().column();
|
||||
const String& lineText = line( sel.start().line() ).getText();
|
||||
Int64 len = lineText.size();
|
||||
Int64 limitLeft = eemax<Int64>( 0ll, sel.start().column() - 512 );
|
||||
Int64 limitRight = eemin<Int64>( len, sel.start().column() + 512 );
|
||||
int unclosedQuotes = 0;
|
||||
while ( left >= limitLeft || right < limitRight ) {
|
||||
bool matchLeft = left >= limitLeft && lineText[left] == text[0];
|
||||
bool matchRight = right < limitRight && lineText[right] == text[0];
|
||||
if ( matchLeft && matchRight ) {
|
||||
left--;
|
||||
right++;
|
||||
} else if ( matchLeft ) {
|
||||
unclosedQuotes++;
|
||||
left--;
|
||||
} else if ( matchRight ) {
|
||||
unclosedQuotes++;
|
||||
right++;
|
||||
} else {
|
||||
if ( left >= limitLeft )
|
||||
left--;
|
||||
if ( right < limitRight )
|
||||
right++;
|
||||
}
|
||||
}
|
||||
if ( unclosedQuotes % 2 != 0 )
|
||||
mustClose = false;
|
||||
}
|
||||
|
||||
if ( mustClose && !isSame && !isClose ) {
|
||||
int balance = 0;
|
||||
int unmatchedRight = 0;
|
||||
const String& lineText = line( sel.start().line() ).getText();
|
||||
Int64 len = lineText.size();
|
||||
Int64 limitLeft = eemax<Int64>( 0, sel.start().column() - 512 );
|
||||
Int64 limitRight = eemin<Int64>( len, sel.start().column() + 512 );
|
||||
for ( Int64 k = limitLeft; k < limitRight; ++k ) {
|
||||
if ( lineText[k] == text[0] ) {
|
||||
balance++;
|
||||
} else if ( lineText[k] == closeChar ) {
|
||||
if ( balance > 0 ) {
|
||||
balance--;
|
||||
} else if ( k >= sel.start().column() ) {
|
||||
unmatchedRight++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( unmatchedRight > 0 )
|
||||
mustClose = false;
|
||||
}
|
||||
|
||||
if ( mustClose ) {
|
||||
/* // I'm not entirely convinced about this
|
||||
TextPosition openStart = positionOffset( sel.start(), 1 );
|
||||
if ( openStart != sel.start() ) {
|
||||
int maxIt = 100;
|
||||
while ( maxIt-- > 0 && openStart < endOfDoc() &&
|
||||
isSpace( getChar( openStart ) ) ) {
|
||||
openStart = nextChar( openStart );
|
||||
}
|
||||
if ( openStart < endOfDoc() && maxIt > 0 &&
|
||||
getChar( openStart ) == closeChar ) {
|
||||
inserted.push_back( false );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
setSelection(
|
||||
i, positionOffset( insert( i, sel.start(), text + String( closeChar ) ), -1 ) );
|
||||
inserted.push_back( true );
|
||||
|
||||
@@ -113,9 +113,9 @@ UTEST( TextDocument, newLineMultiCursorAutoIndent ) {
|
||||
doc.insert( 0, { 2, 3 }, ")" );
|
||||
|
||||
// Cursors between all pairs
|
||||
doc.resetSelection( TextRanges( std::vector<TextRange>{
|
||||
TextRange( { 0, 1 }, { 0, 1 } ), TextRange( { 1, 2 }, { 1, 2 } ),
|
||||
TextRange( { 2, 3 }, { 2, 3 } ) } ) );
|
||||
doc.resetSelection( TextRanges( std::vector<TextRange>{ TextRange( { 0, 1 }, { 0, 1 } ),
|
||||
TextRange( { 1, 2 }, { 1, 2 } ),
|
||||
TextRange( { 2, 3 }, { 2, 3 } ) } ) );
|
||||
|
||||
doc.newLine();
|
||||
|
||||
@@ -145,4 +145,48 @@ UTEST( TextDocument, newLineNormal ) {
|
||||
EXPECT_STDSTREQ( TextPosition( 1, 2 ).toString(), doc.getSelection().start().toString() );
|
||||
}
|
||||
|
||||
UTEST( TextDocument, autoCloseBrackets ) {
|
||||
TextDocument doc;
|
||||
doc.setAutoCloseBrackets( true );
|
||||
|
||||
// Test word boundary
|
||||
doc.insert( 0, { 0, 0 }, "word" );
|
||||
doc.setSelection( { 0, 0 } ); // Before 'word'
|
||||
doc.textInput( "(" ); // Next char 'w' is a word char, shouldn't auto close
|
||||
EXPECT_STRINGEQ( "(word\n", doc.line( 0 ).getText() );
|
||||
|
||||
doc.reset();
|
||||
doc.setAutoCloseBrackets( true );
|
||||
doc.insert( 0, { 0, 0 }, " word" );
|
||||
doc.setSelection( { 0, 0 } ); // Before ' word'
|
||||
doc.textInput( "(" ); // Next char ' ' is not a word char, should auto close
|
||||
EXPECT_STRINGEQ( "() word\n", doc.line( 0 ).getText() );
|
||||
|
||||
doc.reset();
|
||||
doc.setAutoCloseBrackets( true );
|
||||
doc.insert( 0, { 0, 0 }, "() )" );
|
||||
doc.setSelection( { 0, 1 } ); // Inside first parens
|
||||
doc.textInput( "(" ); // Unmatched right paren ahead, shouldn't auto close
|
||||
EXPECT_STRINGEQ( "(() )\n", doc.line( 0 ).getText() );
|
||||
|
||||
doc.reset();
|
||||
doc.setAutoCloseBrackets( true );
|
||||
doc.insert( 0, { 0, 0 }, "()" );
|
||||
doc.setSelection( { 0, 1 } ); // Inside first parens
|
||||
doc.textInput( "(" ); // Balanced right paren ahead, should auto close
|
||||
EXPECT_STRINGEQ( "(())\n", doc.line( 0 ).getText() );
|
||||
|
||||
doc.reset();
|
||||
doc.setAutoCloseBrackets( true );
|
||||
doc.insert( 0, { 0, 0 }, "(\"\")" );
|
||||
doc.setSelection( { 0, 2 } ); // Inside quotes
|
||||
doc.textInput( "\"" ); // Overwrites existing quote (stepping over)
|
||||
EXPECT_STRINGEQ( "(\"\")\n", doc.line( 0 ).getText() );
|
||||
|
||||
doc.reset();
|
||||
doc.setAutoCloseBrackets( true );
|
||||
doc.insert( 0, { 0, 0 }, "()" );
|
||||
doc.setSelection( { 0, 1 } ); // Inside parens
|
||||
doc.textInput( "\"" ); // Balanced quotes (0), should auto close
|
||||
EXPECT_STRINGEQ( "(\"\")\n", doc.line( 0 ).getText() );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user