Added String::escape and String::unescape.

This commit is contained in:
Martín Lucas Golini
2022-03-08 00:24:41 -03:00
parent cd2db3897c
commit ca2d01d790
3 changed files with 187 additions and 33 deletions

View File

@@ -69,6 +69,12 @@ class EE_API String {
return 0;
}
/** Escape string sequence */
static String escape( const String& str );
/** Unescape string sequence */
static String unescape( const String& str );
/** @return string hash */
static String::HashType hash( const std::string& str );
@@ -776,6 +782,10 @@ class EE_API String {
String& toUpper();
String& escape();
String& unescape();
StringBaseType lastChar() const;
std::vector<String> split( const StringBaseType& delim = '\n',

View File

@@ -112,6 +112,154 @@ Int64 String::BMH::find( const std::string& haystack, const std::string& needle,
return find( haystack, needle, haystackOffset, occ );
}
String String::escape( const String& str ) {
String output;
for ( size_t i = 0; i < str.size(); i++ ) {
switch ( str[i] ) {
case '\r':
output += "\\r";
break;
case '\t':
output += "\\t";
break;
case '\n':
output += "\\n";
break;
case '\a':
output += "\\a";
break;
case '\b':
output += "\\b";
break;
case '\f':
output += "\\f";
break;
case '\v':
output += "\\v";
break;
default: {
output += str[i];
break;
}
}
}
return output;
}
String String::unescape( const String& str ) {
String output;
for ( size_t i = 0; i < str.size(); i++ ) {
if ( i > 0 && str[i - 1] == '\\' ) {
switch ( str[i] ) {
case '\\':
output.push_back( '\\' );
break;
case 'r':
output.push_back( '\r' );
break;
case 't':
output.push_back( '\t' );
break;
case 'n':
output.push_back( '\n' );
break;
case '\'':
output.push_back( '\'' );
break;
case '"':
output.push_back( '"' );
break;
case '?':
output.push_back( '\?' );
break;
case 'a':
output.push_back( '\a' );
break;
case 'b':
output.push_back( '\b' );
break;
case 'f':
output.push_back( '\f' );
break;
case 'v':
output.push_back( '\v' );
break;
case 'x': {
size_t len = 2;
if ( i + len < str.size() ) {
std::string buffer;
for ( i = i + 1; i < str.size(); i++ )
if ( std::isdigit( str[i] ) || ( str[i] >= 'a' && str[i] <= 'f' ) ||
( str[i] >= 'A' && str[i] <= 'F' ) ) {
buffer.push_back( str[i] );
} else {
break;
}
if ( buffer.size() >= len ) {
Uint32 value;
if ( String::fromString( value, buffer, std::hex ) )
output.push_back( value );
}
}
break;
}
case 'u':
case 'U': {
size_t len = str[i] == 'u' ? 4 : 8;
if ( i + len < str.size() ) {
size_t to = i + len;
std::string buffer;
for ( i = i + 1; i <= to; i++ ) {
if ( std::isdigit( str[i] ) || ( str[i] >= 'a' && str[i] <= 'f' ) ||
( str[i] >= 'A' && str[i] <= 'F' ) ) {
buffer.push_back( str[i] );
}
}
if ( buffer.size() == len ) {
Uint32 value;
if ( String::fromString( value, buffer, std::hex ) )
output.push_back( value );
}
}
break;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8': {
size_t to = eemin( (size_t)i + 3, str.size() );
std::string buffer;
for ( ; i < to; i++ ) {
if ( ( str[i] >= '0' && str[i] <= '8' ) ) {
buffer.push_back( str[i] );
} else {
break;
}
}
if ( buffer.size() <= 3 ) {
Uint32 value;
if ( String::fromString( value, buffer, std::oct ) )
output.push_back( value );
}
break;
}
default: {
// Undefined behavior
break;
}
}
} else if ( str[i] != '\\' ) {
output.push_back( str[i] );
}
}
return output;
}
String::HashType String::hash( const std::string& str ) {
return String::hash( str.c_str() );
}
@@ -400,6 +548,16 @@ String& String::toUpper() {
return *this;
}
String& String::escape() {
this->assign( String::escape( *this ) );
return *this;
}
String& String::unescape() {
this->assign( String::unescape( *this ) );
return *this;
}
String::StringBaseType String::lastChar() const {
return mString.empty() ? std::numeric_limits<StringBaseType>::max()
: mString[mString.size() - 1];

View File

@@ -1,26 +1,6 @@
#include "docsearchcontroller.hpp"
#include "codeeditor.hpp"
static void replaceAllEscapedSequences( String& target, const String& that, const String& with ) {
std::string::size_type pos = 0;
while ( ( pos = target.find( that, pos ) ) != String::InvalidPos ) {
if ( pos > 0 && target[pos - 1] == '\\' ) {
target.erase( pos, 1 );
} else {
target.erase( pos, that.length() );
target.insert( pos, with );
}
pos += with.length();
}
}
static void unescapeSequences( String& txt ) {
replaceAllEscapedSequences( txt, "\\n", String( '\n' ) );
replaceAllEscapedSequences( txt, "\\t", String( '\t' ) );
replaceAllEscapedSequences( txt, "\\r", String( '\r' ) );
replaceAllEscapedSequences( txt, "\\\\", String( '\\' ) );
}
DocSearchController::DocSearchController( UICodeEditorSplitter* editorSplitter, App* app ) :
mEditorSplitter( editorSplitter ), mApp( app ) {}
@@ -165,8 +145,14 @@ void DocSearchController::showFindView() {
const TextDocument& doc = editor->getDocument();
if ( doc.getSelection().hasSelection() && doc.getSelection().inSameLine() ) {
if ( doc.getSelection().hasSelection() ) {
String text = doc.getSelectedText();
if ( !doc.getSelection().inSameLine() ) {
text.escape();
UICheckBox* escapeSequenceChk = mSearchBarLayout->find<UICheckBox>( "escape_sequence" );
if ( !escapeSequenceChk->isChecked() )
escapeSequenceChk->setChecked( true );
}
if ( !text.empty() ) {
findInput->setText( text );
findInput->getDocument().selectAll();
@@ -203,17 +189,17 @@ bool DocSearchController::findPrevText( SearchState& search ) {
String txt( search.text );
if ( search.escapeSequences )
unescapeSequences( txt );
txt.unescape();
TextRange found = doc.findLast( txt, from, search.caseSensitive, search.wholeWord,
search.type, search.range );
TextRange found = doc.findLast( txt, from, search.caseSensitive, search.wholeWord, search.type,
search.range );
if ( found.isValid() ) {
doc.setSelection( found );
findInput->removeClass( "error" );
return true;
} else {
found = doc.findLast( txt, range.end(), search.caseSensitive, search.wholeWord,
search.type, range );
found = doc.findLast( txt, range.end(), search.caseSensitive, search.wholeWord, search.type,
range );
if ( found.isValid() ) {
doc.setSelection( found );
findInput->removeClass( "error" );
@@ -243,7 +229,7 @@ bool DocSearchController::findNextText( SearchState& search ) {
String txt( search.text );
if ( search.escapeSequences )
unescapeSequences( txt );
txt.unescape();
TextRange found =
doc.find( txt, from, search.caseSensitive, search.wholeWord, search.type, range );
@@ -252,8 +238,8 @@ bool DocSearchController::findNextText( SearchState& search ) {
findInput->removeClass( "error" );
return true;
} else {
found = doc.find( txt, range.start(), search.caseSensitive, search.wholeWord,
search.type, range );
found = doc.find( txt, range.start(), search.caseSensitive, search.wholeWord, search.type,
range );
if ( found.isValid() ) {
doc.setSelection( found.reversed() );
findInput->removeClass( "error" );
@@ -288,8 +274,8 @@ int DocSearchController::replaceAll( SearchState& search, const String& replace
String txt( search.text );
String repl( replace );
if ( search.escapeSequences ) {
unescapeSequences( txt );
unescapeSequences( repl );
txt.unescape();
repl.unescape();
}
int count = doc.replaceAll( txt, repl, search.caseSensitive, search.wholeWord, search.type,
@@ -312,8 +298,8 @@ bool DocSearchController::findAndReplace( SearchState& search, const String& rep
String txt( search.text );
String repl( replace );
if ( search.escapeSequences ) {
unescapeSequences( txt );
unescapeSequences( repl );
txt.unescape();
repl.unescape();
}
if ( doc.hasSelection() && doc.getSelectedText() == txt ) {