Merge branch 'develop' into debugger

This commit is contained in:
Martín Lucas Golini
2024-12-20 20:22:57 -03:00
17 changed files with 411 additions and 210 deletions

View File

@@ -34,13 +34,13 @@
},
{
"language": "rust",
"file_patterns": ["%.rs"],
"file_patterns": ["%.rs$"],
"command": "rustfmt --emit stdout --color never $FILENAME",
"url": "https://rust-lang.github.io/rustfmt/"
},
{
"language": "go",
"file_patterns": ["%.go"],
"file_patterns": ["%.go$"],
"command": "gopls format $FILENAME",
"url": "https://pkg.go.dev/golang.org/x/tools/gopls"
},
@@ -53,21 +53,21 @@
},
{
"language": [ "xml" ],
"file_patterns": ["%.xml"],
"file_patterns": ["%.xml$"],
"command": "xml",
"type": "native",
"url": "#native"
},
{
"language": "css",
"file_patterns": ["%.css"],
"file_patterns": ["%.css$"],
"command": "css",
"type": "native",
"url": "#native"
},
{
"language": "zig",
"file_patterns": ["%.zig"],
"file_patterns": ["%.zig$"],
"command": "zig fmt $FILENAME",
"type": "inplace",
"url": "https://ziglang.org"

View File

@@ -132,21 +132,21 @@
"name": "lua-language-server",
"url": "https://github.com/sumneko/lua-language-server",
"command": "lua-language-server",
"file_patterns": ["%.lua"]
"file_patterns": ["%.lua$"]
},
{
"language": "kotlin",
"name": "kotlin-language-server",
"url": "https://github.com/fwcd/kotlin-language-server",
"command": "kotlin-language-server",
"file_patterns": ["%.kt"]
"file_patterns": ["%.kt$"]
},
{
"language": "nim",
"name": "nimlsp",
"url": "https://github.com/PMunch/nimlsp",
"command": "nimlsp",
"file_patterns": ["%.nim"]
"file_patterns": ["%.nim$"]
},
{
"language": "ruby",
@@ -154,28 +154,28 @@
"url": "https://solargraph.org",
"command": "solargraph stdio",
"rootIndicationFileNames": ["Gemfile", "Gemfile.lock", "gems.rb", "gems.lock", "Rakefile"],
"file_patterns": ["%.rb"]
"file_patterns": ["%.rb$"]
},
{
"language": "yaml",
"name": "yaml-language-server",
"url": "https://github.com/redhat-developer/yaml-language-server",
"command": "yaml-language-server --stdio",
"file_patterns": ["%.yaml", "%.yml"]
"file_patterns": ["%.yaml$", "%.yml$"]
},
{
"language": "dart",
"name": "dart language-server",
"url": "https://github.com/dart-lang/sdk/blob/main/pkg/analysis_server/tool/lsp_spec",
"command": "dart language-server --client-id ecode",
"file_patterns": ["%.dart"]
"file_patterns": ["%.dart$"]
},
{
"language": "shellscript",
"name": "bash-language-server",
"url": "https://github.com/bash-lsp/bash-language-server",
"command": "bash-language-server start",
"file_patterns": ["%.sh", "%.bash"]
"file_patterns": ["%.sh$", "%.bash$"]
},
{
"language": "html",
@@ -317,7 +317,7 @@
"name": "glsl_analyzer",
"url": "https://github.com/nolanderc/glsl_analyzer",
"command": "glsl_analyzer",
"file_patterns": ["%.glsl$", "%.frag$", "%.vert$", "%.fs$", "%.vs$", "%.tesc", "%.tese"]
"file_patterns": ["%.glsl$", "%.frag$", "%.vert$", "%.fs$", "%.vs$", "%.tesc$", "%.tese$"]
},
{
"language": "vala",
@@ -366,7 +366,7 @@
"name": "LanguageServer.jl",
"url": "https://github.com/julia-vscode/LanguageServer.jl",
"command": "julia --project=\"$HOME/.julia/packages/LanguageServer/Fwm1f/src/LanguageServer.jl\" -e \"using LanguageServer; runserver()\"",
"file_patterns": ["%.jl"]
"file_patterns": ["%.jl$"]
},
{
"language": "fortran",

View File

@@ -2,23 +2,19 @@
#define EE_UI_DOC_TEXTRANGE_HPP
#include <algorithm>
#include <eepp/core/debug.hpp>
#include <eepp/ui/doc/textposition.hpp>
namespace EE { namespace UI { namespace Doc {
class EE_API TextRange {
public:
TextRange() {}
TextRange( const TextPosition& start, const TextPosition& end ) :
mStart( start ), mEnd( end ) {}
TextRange();
bool isValid() const { return mStart.isValid() && mEnd.isValid(); }
TextRange( const TextPosition& start, const TextPosition& end );
void clear() {
mStart = {};
mEnd = {};
}
bool isValid() const;
void clear();
TextPosition& start() { return mStart; }
@@ -28,14 +24,9 @@ class EE_API TextRange {
const TextPosition& end() const { return mEnd; }
TextRange normalized() const { return TextRange( normalizedStart(), normalizedEnd() ); }
TextRange normalized() const;
TextRange& normalize() {
auto normalize( normalized() );
mStart = normalize.start();
mEnd = normalize.end();
return *this;
}
TextRange& normalize();
void reverse() { std::swap( mEnd, mStart ); }
@@ -45,10 +36,7 @@ class EE_API TextRange {
void setEnd( const TextPosition& position ) { mEnd = position; }
void set( const TextPosition& start, const TextPosition& end ) {
mStart = start;
mEnd = end;
}
void set( const TextPosition& start, const TextPosition& end );
bool operator==( const TextRange& other ) const {
return mStart == other.mStart && mEnd == other.mEnd;
@@ -90,21 +78,9 @@ class EE_API TextRange {
return TextRange( mStart - other.mStart, mEnd - other.mEnd );
}
bool contains( const TextPosition& position ) const {
if ( !( position.line() > mStart.line() ||
( position.line() == mStart.line() && position.column() >= mStart.column() ) ) )
return false;
if ( !( position.line() < mEnd.line() ||
( position.line() == mEnd.line() && position.column() <= mEnd.column() ) ) )
return false;
return true;
}
bool contains( const TextPosition& position ) const;
bool intersectsLineRange( const TextRange& range ) const {
eeASSERT( range.isNormalized() );
return mStart.line() <= static_cast<Int64>( range.end().line() ) &&
static_cast<Int64>( range.start().line() ) <= mEnd.line();
}
bool intersectsLineRange( const TextRange& range ) const;
template <typename T> bool intersectsLineRange( T fromLine, T toLine ) const {
return mStart.line() <= static_cast<Int64>( toLine ) &&
@@ -116,44 +92,25 @@ class EE_API TextRange {
static_cast<Int64>( range.first ) <= mEnd.line();
}
bool containsLine( const Int64& line ) const {
return line >= mStart.line() && line <= mEnd.line();
}
bool containsLine( const Int64& line ) const;
bool contains( const TextRange& range ) const {
return range.start() >= start() && range.end() <= end();
}
bool contains( const TextRange& range ) const;
bool intersects( const TextRange& range ) const;
TextRange merge( const TextRange& range ) const;
bool hasSelection() const { return isValid() && mStart != mEnd; }
bool inSameLine() const { return isValid() && mStart.line() == mEnd.line(); }
Int64 height() const {
if ( mEnd.line() > mStart.line() )
return mEnd.line() - mStart.line() + 1;
return mStart.line() - mStart.line() + 1;
}
Int64 height() const;
Int64 length() const {
if ( !inSameLine() )
return 0;
if ( mEnd.column() > mStart.column() )
return mEnd.column() - mStart.column();
return mStart.column() - mEnd.column();
}
Int64 length() const;
std::string toString() const {
return String::format( "%s - %s", mStart.toString().c_str(), mEnd.toString().c_str() );
}
std::string toString() const;
static TextRange fromString( const std::string& range ) {
auto split = String::split( range, "-" );
if ( split.size() == 2 ) {
return { TextPosition::fromString( String::trim( split[0] ) ),
TextPosition::fromString( String::trim( split[1] ) ) };
}
return {};
}
static TextRange fromString( const std::string& range );
bool isNormalized() const { return mStart <= mEnd; }
@@ -168,96 +125,31 @@ class EE_API TextRange {
class EE_API TextRanges : public std::vector<TextRange> {
public:
TextRanges() {}
TextRanges();
TextRanges( const std::vector<TextRange>& ranges ) : std::vector<TextRange>( ranges ) {}
TextRanges( const std::vector<TextRange>& ranges );
TextRanges( const TextRange& ranges ) : std::vector<TextRange>( { ranges } ) {}
TextRanges( const TextRange& ranges );
bool isSorted() const { return mIsSorted; }
bool isSorted() const;
bool isValid() const {
for ( const auto& selection : *this ) {
if ( !selection.isValid() )
return false;
}
return true;
}
bool isValid() const;
bool exists( const TextRange& range ) const {
if ( !mIsSorted )
return std::find( begin(), end(), range ) != end();
return std::binary_search( begin(), end(), range );
}
bool exists( const TextRange& range ) const;
size_t findIndex( const TextRange& range ) const {
if ( !mIsSorted ) {
auto it = std::find( begin(), end(), range );
return it != end() ? std::distance( begin(), it ) : static_cast<size_t>( -1 );
} else {
auto it = std::lower_bound( begin(), end(), range );
return ( it != end() && *it == range ) ? std::distance( begin(), it )
: static_cast<size_t>( -1 );
}
}
size_t findIndex( const TextRange& range ) const;
bool hasSelection() const {
for ( const auto& r : *this )
if ( r.hasSelection() )
return true;
return false;
}
bool hasSelection() const;
void sort() {
std::sort( begin(), end() );
setSorted();
}
void sort();
void setSorted() { mIsSorted = true; }
void setSorted();
bool merge() {
if ( size() <= 1 )
return false;
bool merge();
if ( !mIsSorted )
sort();
std::string toString() const;
auto itUnique = std::unique(
begin(), end(), []( const TextRange& a, const TextRange& b ) { return a == b; } );
bool merged = itUnique != end();
erase( itUnique, end() );
auto it = begin();
while ( it != end() ) {
auto next = std::next( it );
while ( next != end() && it != end() && next->contains( *it ) ) {
erase( it );
it = std::prev( next );
}
it = next;
}
return merged;
}
std::string toString() const {
std::string str;
for ( size_t i = 0; i < size(); ++i ) {
str += ( *this )[i].toString();
if ( i != size() - 1 )
str += ";";
}
return str;
}
static TextRanges fromString( const std::string& str ) {
auto rangesStr = String::split( str, ';' );
TextRanges ranges;
for ( const auto& rangeStr : rangesStr )
ranges.emplace_back( TextRange::fromString( rangeStr ) );
return ranges;
}
static TextRanges fromString( const std::string& str );
protected:
bool mIsSorted{ false };

View File

@@ -1127,6 +1127,7 @@
../../src/eepp/ui/doc/syntaxtokenizer.cpp
../../src/eepp/ui/doc/textdocument.cpp
../../src/eepp/ui/doc/textformat.cpp
../../src/eepp/ui/doc/textrange.cpp
../../src/eepp/ui/doc/textundostack.cpp
../../src/eepp/ui/doc/documentview.cpp
../../src/eepp/ui/keyboardshortcut.cpp

View File

@@ -8,7 +8,7 @@ void addC() {
auto& sd = SyntaxDefinitionManager::instance()->add(
{ "C",
{ "%.c$", "%.C", "%.h$", "%.icc" },
{ "%.c$", "%.C$", "%.h$", "%.icc$" },
{
{ { "//.-\n" }, "comment" },
{ { "/%*", "%*/" }, "comment" },

View File

@@ -9,7 +9,7 @@ void addHTML() {
->add(
{ "HTML",
{ "%.[mp]?html?$", "%.handlebars" },
{ "%.[mp]?html?$", "%.handlebars$" },
{
{ { "<%s*[sS][cC][rR][iI][pP][tT]%s+[tT][yY][pP][eE]%s*=%s*['\"]%a+/"
"[jJ][aA][vV][aA][sS][cC][rR][iI][pP][tT]['\"]%s*>",

View File

@@ -8,7 +8,7 @@ void addJSON() {
auto& sd = SyntaxDefinitionManager::instance()->add(
{ "JSON",
{ "%.json$", "%.cson$", "%.webmanifest" },
{ "%.json$", "%.cson$", "%.webmanifest$" },
{
{ { "(%b\"\")(:)" }, { "normal", "keyword", "operator" } },
{ { "\"", "\"", "\\" }, "string" },

View File

@@ -1,24 +0,0 @@
#include <eepp/ui/doc/languages/plaintext.hpp>
#include <eepp/ui/doc/syntaxdefinitionmanager.hpp>
namespace EE { namespace UI { namespace Doc { namespace Language {
void addPlaintext() {
SyntaxDefinitionManager::instance()->add(
{"Plain Text",
{},
{
},
{
},
"",
{},
"plaintext"
});
}
}}}} // namespace EE::UI::Doc::Language

View File

@@ -1,10 +0,0 @@
#ifndef EE_UI_DOC_Plaintext
#define EE_UI_DOC_Plaintext
namespace EE { namespace UI { namespace Doc { namespace Language {
extern void addPlaintext();
}}}}
#endif

View File

@@ -37,7 +37,8 @@ SyntaxDefinitionManager::createSingleton( std::size_t reserveSpaceForLanguages )
}
static void addPlainText() {
SyntaxDefinitionManager::instance()->add( { "Plain Text", {}, {}, {}, "", {}, "plaintext" } );
SyntaxDefinitionManager::instance()->add(
{ "Plain Text", { "%.txt$" }, {}, {}, "", {}, "plaintext" } );
}
// Syntax definitions can be directly converted from the lite (https://github.com/rxi/lite) and

View File

@@ -59,7 +59,9 @@ TextDocument::~TextDocument() {
Lock l( mLoadingMutex );
}
{ Lock l( mClientsMutex ); }
{
Lock l( mClientsMutex );
}
// Loading has been stopped
while ( mLoadingAsync ) {
@@ -1305,13 +1307,13 @@ size_t TextDocument::remove( const size_t& cursorIdx, TextRange range,
mLines.emplace_back( String( "\n" ) );
if ( mSelection.size() > 1 ) {
auto ranNorm( originalRange.normalized() );
Int64 lineRem = ranNorm.end().line() - ranNorm.start().line();
auto oriNm( originalRange.normalized() );
Int64 lineRem = oriNm.end().line() - oriNm.start().line();
size_t curIdx = 0;
Int64 colRem = ranNorm.start().line() == ranNorm.end().line()
? ranNorm.end().column() - ranNorm.start().column()
: ranNorm.end().column();
Int64 colRem = oriNm.start().line() == oriNm.end().line()
? oriNm.end().column() - oriNm.start().column()
: oriNm.end().column();
for ( auto& sel : mSelection ) {
if ( curIdx == cursorIdx ) {
@@ -1319,9 +1321,10 @@ size_t TextDocument::remove( const size_t& cursorIdx, TextRange range,
continue;
}
auto selNorm( sel.normalized() );
auto selNm( sel.normalized() );
auto selOri( sel );
if ( selNorm.start().line() < ranNorm.end().line() ) {
if ( selNm.start().line() < oriNm.end().line() ) {
curIdx++;
continue;
}
@@ -1329,13 +1332,22 @@ size_t TextDocument::remove( const size_t& cursorIdx, TextRange range,
if ( lineRem != 0 ) {
sel.start().setLine( sel.start().line() - lineRem );
sel.end().setLine( sel.end().line() - lineRem );
}
sel.start().setColumn( ranNorm.start().column() + sel.start().column() - colRem );
sel.end().setColumn( ranNorm.start().column() + sel.end().column() - colRem );
} else if ( selNorm.end().line() == ranNorm.end().line() &&
ranNorm.end().column() <= selNorm.start().column() ) {
sel.start().setColumn( sel.start().column() - colRem );
sel.end().setColumn( sel.end().column() - colRem );
if ( selOri.start().line() == oriNm.end().line() &&
selOri.start().column() >= oriNm.end().column() ) {
if ( sel.start().line() != selOri.start().line() )
sel.start().setColumn( oriNm.start().column() + sel.start().column() - colRem );
else
sel.start().setColumn( sel.start().column() - colRem );
}
if ( selOri.end().line() == oriNm.end().line() &&
selOri.end().column() >= oriNm.end().column() ) {
if ( selOri.end().line() != sel.end().line() )
sel.end().setColumn( oriNm.start().column() + sel.end().column() - colRem );
else
sel.end().setColumn( sel.end().column() - colRem );
}
sel = sanitizeRange( sel );
@@ -1912,7 +1924,7 @@ void TextDocument::deleteToNextChar() {
void TextDocument::deleteToEndOfLine() {
for ( size_t i = 0; i < mSelection.size(); ++i )
deleteTo( i, endOfLine( getSelectionIndex( i ).start() ));
deleteTo( i, endOfLine( getSelectionIndex( i ).start() ) );
mergeSelection();
}

View File

@@ -0,0 +1,199 @@
#include <eepp/core/debug.hpp>
#include <eepp/ui/doc/textrange.hpp>
namespace EE { namespace UI { namespace Doc {
TextRange::TextRange() {}
TextRange::TextRange( const TextPosition& start, const TextPosition& end ) :
mStart( start ), mEnd( end ) {}
bool TextRange::isValid() const {
return mStart.isValid() && mEnd.isValid();
}
void TextRange::clear() {
mStart = {};
mEnd = {};
}
TextRange TextRange::normalized() const {
return TextRange( normalizedStart(), normalizedEnd() );
}
TextRange& TextRange::normalize() {
auto normalize( normalized() );
mStart = normalize.start();
mEnd = normalize.end();
return *this;
}
void TextRange::set( const TextPosition& start, const TextPosition& end ) {
mStart = start;
mEnd = end;
}
bool TextRange::contains( const TextPosition& position ) const {
if ( !( position.line() > mStart.line() ||
( position.line() == mStart.line() && position.column() >= mStart.column() ) ) )
return false;
if ( !( position.line() < mEnd.line() ||
( position.line() == mEnd.line() && position.column() <= mEnd.column() ) ) )
return false;
return true;
}
bool TextRange::intersectsLineRange( const TextRange& range ) const {
eeASSERT( range.isNormalized() );
return mStart.line() <= static_cast<Int64>( range.end().line() ) &&
static_cast<Int64>( range.start().line() ) <= mEnd.line();
}
bool TextRange::containsLine( const Int64& line ) const {
return line >= mStart.line() && line <= mEnd.line();
}
bool TextRange::contains( const TextRange& range ) const {
return range.start() >= start() && range.end() <= end();
}
bool TextRange::intersects( const TextRange& range ) const {
return range.start() <= end() && range.end() >= start();
}
TextRange TextRange::merge( const TextRange& range ) const {
bool wasNormalized = mStart <= mEnd;
TextRange normalizedThis = this->normalized();
TextRange normalizedRange = range.normalized();
TextPosition mergedStart = normalizedThis.start() < normalizedRange.start()
? normalizedThis.start()
: normalizedRange.start();
TextPosition mergedEnd =
normalizedThis.end() > normalizedRange.end() ? normalizedThis.end() : normalizedRange.end();
return TextRange( wasNormalized ? mergedStart : mergedEnd,
wasNormalized ? mergedEnd : mergedStart );
}
Int64 TextRange::height() const {
if ( mEnd.line() > mStart.line() )
return mEnd.line() - mStart.line() + 1;
return mStart.line() - mStart.line() + 1;
}
Int64 TextRange::length() const {
if ( !inSameLine() )
return 0;
if ( mEnd.column() > mStart.column() )
return mEnd.column() - mStart.column();
return mStart.column() - mEnd.column();
}
std::string TextRange::toString() const {
return String::format( "%s - %s", mStart.toString().c_str(), mEnd.toString().c_str() );
}
TextRange TextRange::fromString( const std::string& range ) {
auto split = String::split( range, "-" );
if ( split.size() == 2 ) {
return { TextPosition::fromString( String::trim( split[0] ) ),
TextPosition::fromString( String::trim( split[1] ) ) };
}
return {};
}
TextRanges::TextRanges() {}
TextRanges::TextRanges( const std::vector<TextRange>& ranges ) : std::vector<TextRange>( ranges ) {}
TextRanges::TextRanges( const TextRange& ranges ) : std::vector<TextRange>( { ranges } ) {}
bool TextRanges::isSorted() const {
return mIsSorted;
}
bool TextRanges::isValid() const {
for ( const auto& selection : *this ) {
if ( !selection.isValid() )
return false;
}
return true;
}
bool TextRanges::exists( const TextRange& range ) const {
if ( !mIsSorted )
return std::find( begin(), end(), range ) != end();
return std::binary_search( begin(), end(), range );
}
size_t TextRanges::findIndex( const TextRange& range ) const {
if ( !mIsSorted ) {
auto it = std::find( begin(), end(), range );
return it != end() ? std::distance( begin(), it ) : static_cast<size_t>( -1 );
} else {
auto it = std::lower_bound( begin(), end(), range );
return ( it != end() && *it == range ) ? std::distance( begin(), it )
: static_cast<size_t>( -1 );
}
}
bool TextRanges::hasSelection() const {
for ( const auto& r : *this )
if ( r.hasSelection() )
return true;
return false;
}
void TextRanges::sort() {
std::sort( begin(), end() );
setSorted();
}
void TextRanges::setSorted() {
mIsSorted = true;
}
bool TextRanges::merge() {
if ( size() <= 1 )
return false;
if ( !mIsSorted )
sort();
auto itUnique = std::unique( begin(), end(),
[]( const TextRange& a, const TextRange& b ) { return a == b; } );
bool merged = itUnique != end();
erase( itUnique, end() );
for ( auto it = begin(); it != end(); ++it ) {
auto next = std::next( it );
while ( next != end() &&
( it->intersects( *next ) || it->contains( *next ) || next->contains( *it ) ) ) {
*it = it->merge( *next );
next = erase( next );
merged = true;
}
}
return merged;
}
std::string TextRanges::toString() const {
std::string str;
for ( size_t i = 0; i < size(); ++i ) {
str += ( *this )[i].toString();
if ( i != size() - 1 )
str += ";";
}
return str;
}
TextRanges TextRanges::fromString( const std::string& str ) {
auto rangesStr = String::split( str, ';' );
TextRanges ranges;
for ( const auto& rangeStr : rangesStr )
ranges.emplace_back( TextRange::fromString( rangeStr ) );
return ranges;
}
}}} // namespace EE::UI::Doc

View File

@@ -3130,6 +3130,7 @@ void UICodeEditor::selectToPreviousLine() {
TextPosition position = mDoc->getSelectionIndex( i ).start();
mDoc->selectTo( i, moveToLineOffset( position, -1 ) );
}
mDoc->mergeSelection();
}
void UICodeEditor::selectToNextLine() {
@@ -3141,6 +3142,7 @@ void UICodeEditor::selectToNextLine() {
mDoc->selectTo( i, moveToLineOffset( position, 1 ) );
}
}
mDoc->mergeSelection();
}
void UICodeEditor::moveScrollUp() {

View File

@@ -8,7 +8,7 @@ void addGLSL() {
auto& sd = SyntaxDefinitionManager::instance()->add(
{ "GLSL",
{ "%.glsl$", "%.frag$", "%.vert$", "%.fs$", "%.vs$", "%.tesc", "%.tese" },
{ "%.glsl$", "%.frag$", "%.vert$", "%.fs$", "%.vs$", "%.tesc$", "%.tese$" },
{
{ { "//.-\n" }, "comment" },
{ { "/%*", "%*/" }, "comment" },

View File

@@ -8,7 +8,7 @@ void addRuby() {
auto& sd = SyntaxDefinitionManager::instance()->add(
{ "Ruby",
{ "%.rb", "%.gemspec", "%.ruby" },
{ "%.rb$", "%.gemspec$", "%.ruby$" },
{
{ { "\"", "\"", "\\" }, "string" },
{ { "'", "'", "\\" }, "string" },

View File

@@ -0,0 +1,76 @@
#include "utest.hpp"
#include <eepp/system/filesystem.hpp>
#include <eepp/system/sys.hpp>
#include <eepp/ui/doc/textdocument.hpp>
using namespace EE::UI::Doc;
using namespace EE::System;
UTEST( TextDocument, multicursor ) {
FileSystem::changeWorkingDirectory( Sys::getProcessPath() );
TextDocument doc;
doc.loadFromFile( "assets/textformat/english.utf8.lf.nobom.txt" );
EXPECT_EQ( doc.linesCount() > 0, true );
// Same line delete
for ( int op = 0; op < 2; op++ ) {
doc.setSelection( { { 0, 4 }, { 0, 5 } } );
EXPECT_STRINGEQ( "a", doc.getSelectedText() );
// Select all "a" from first line
doc.selectWord();
doc.selectWord();
doc.selectWord();
switch ( op ) {
case 0:
doc.deleteToPreviousChar();
break;
case 1:
doc.deleteToNextChar();
break;
}
EXPECT_STRINGEQ( "It ws bright cold dy in April, nd the clocks were striking thirteen.\n",
doc.line( 0 ).getText() );
doc.resetSelection( TextRange{ { 0, 0 }, { 0, 0 } } );
doc.undo();
}
// Multi-line delete
for ( int op = 0; op < 4; op++ ) {
TextRanges ranges(
{ TextRange( { 3, 65 }, { 4, 11 } ), TextRange( { 17, 66 }, { 18, 67 } ) } );
if ( op >= 2 )
for ( auto& range : ranges )
range.reverse();
doc.resetSelection( ranges );
switch ( op % 2 ) {
case 0:
doc.deleteToPreviousChar();
break;
case 1:
doc.deleteToNextChar();
break;
}
EXPECT_STRINGEQ( "though not quickly enough to prevent a swirl of gritty dust from him.\n",
doc.line( 3 ).getText() );
EXPECT_STRINGEQ( "one of those pictures which are so contrived that the eyes follow ran.\n",
doc.line( 16 ).getText() );
EXPECT_STDSTREQ( TextRange( { 3, 65 }, { 3, 65 } ).toString(),
doc.getSelectionIndex( 0 ).toString() );
EXPECT_STDSTREQ( TextRange( { 16, 66 }, { 16, 66 } ).toString(),
doc.getSelectionIndex( 1 ).toString() );
doc.undo();
}
doc.resetUndoRedo();
doc.resetSelection( TextRange{ { 0, 0 }, { 0, 0 } } );
}

View File

@@ -0,0 +1,52 @@
#pragma once
#include "utest.h"
#define UTEST_STDSTREQ(x, y, msg, is_assert) \
UTEST_SURPRESS_WARNING_BEGIN do { \
const std::string xEval = (x); \
const std::string yEval = (y); \
if (xEval != yEval) { \
UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \
UTEST_PRINTF(" Expected : \"%s\"\n", xEval.c_str()); \
UTEST_PRINTF(" Actual : \"%s\"\n", yEval.c_str()); \
if (strlen(msg) > 0) { \
UTEST_PRINTF(" Message : %s\n", msg); \
} \
*utest_result = UTEST_TEST_FAILURE; \
if (is_assert) { \
return; \
} \
} \
} \
while (0) \
UTEST_SURPRESS_WARNING_END
#define EXPECT_STDSTREQ(x, y) UTEST_STDSTREQ(x, y, "", 0)
#define EXPECT_STDSTREQ_MSG(x, y, msg) UTEST_STDSTREQ(x, y, msg, 0)
#define ASSERT_STDSTREQ(x, y) UTEST_STDSTREQ(x, y, "", 1)
#define ASSERT_STDSTREQ_MSG(x, y, msg) UTEST_STDSTREQ(x, y, msg, 1)
#define UTEST_STRINGEQ(x, y, msg, is_assert) \
UTEST_SURPRESS_WARNING_BEGIN do { \
const String xEval = (x); \
const String yEval = (y); \
if (xEval != yEval) { \
UTEST_PRINTF("%s:%i: Failure\n", __FILE__, __LINE__); \
UTEST_PRINTF(" Expected : \"%s\"\n", xEval.toUtf8().c_str()); \
UTEST_PRINTF(" Actual : \"%s\"\n", yEval.toUtf8().c_str()); \
if (strlen(msg) > 0) { \
UTEST_PRINTF(" Message : %s\n", msg); \
} \
*utest_result = UTEST_TEST_FAILURE; \
if (is_assert) { \
return; \
} \
} \
} \
while (0) \
UTEST_SURPRESS_WARNING_END
#define EXPECT_STRINGEQ(x, y) UTEST_STRINGEQ(x, y, "", 0)
#define EXPECT_STRINGEQ_MSG(x, y, msg) UTEST_STRINGEQ(x, y, msg, 0)
#define ASSERT_STRINGEQ(x, y) UTEST_STRINGEQ(x, y, "", 1)
#define ASSERT_STRINGEQ_MSG(x, y, msg) UTEST_STRINGEQ(x, y, msg, 1)