mirror of
https://github.com/textmate/textmate.git
synced 2026-04-28 03:00:34 -04:00
Highlight pairs can now be regular expressions
These settings are also consulted for “pair movement”, i.e. ⌃↓/⌃↑ and ⇧⌘B, so by using regular expressions we can match begin/end tags and navigate between these.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include <buffer/buffer.h>
|
||||
#include <bundles/bundles.h>
|
||||
#include <regexp/find.h>
|
||||
#include <regexp/regexp.h>
|
||||
#include <text/classification.h>
|
||||
#include <text/utf8.h>
|
||||
#include <text/ctype.h>
|
||||
@@ -178,141 +179,171 @@ namespace ng
|
||||
// = Typing Pair Support =
|
||||
// =======================
|
||||
|
||||
static std::vector< std::pair<std::string, std::string> > character_pairs (scope::context_t const& scope, std::string const& key)
|
||||
{
|
||||
std::vector< std::pair<std::string, std::string> > res;
|
||||
|
||||
plist::any_t value = bundles::value_for_setting(key, scope);
|
||||
if(plist::array_t const* array = boost::get<plist::array_t>(&value))
|
||||
{
|
||||
iterate(pair, *array)
|
||||
{
|
||||
if(plist::array_t const* value = boost::get<plist::array_t>(&*pair))
|
||||
{
|
||||
std::string const* a1 = value->size() == 2 ? boost::get<std::string>(&(*value)[0]) : NULL;
|
||||
std::string const* a2 = value->size() == 2 ? boost::get<std::string>(&(*value)[1]) : NULL;
|
||||
if(a1 && a2 && *a1 != *a2)
|
||||
res.push_back(std::make_pair(*a1, *a2));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res.push_back(std::make_pair("(", ")"));
|
||||
res.push_back(std::make_pair("{", "}"));
|
||||
res.push_back(std::make_pair("[", "]"));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct character_pair_t
|
||||
struct pattern_t
|
||||
{
|
||||
character_pair_t () { }
|
||||
character_pair_t (std::string const& first, std::string const& second, bool matchedFirst) : first(first), second(second), matched_first(matchedFirst), initialized(true) { }
|
||||
explicit operator bool () const { return initialized; }
|
||||
pattern_t () { }
|
||||
|
||||
std::string first, second;
|
||||
bool matched_first, initialized = false;
|
||||
pattern_t (std::string const& plain) : plain(plain)
|
||||
{
|
||||
if(plain.size() > 2 && plain[0] == '/' && plain[plain.size()-1] == '/')
|
||||
{
|
||||
left_anchored_regexp = "\\G" + plain.substr(1, plain.size()-2);
|
||||
right_anchored_regexp = plain.substr(1, plain.size()-2) + "\\G";
|
||||
is_regexp = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string plain;
|
||||
regexp::pattern_t left_anchored_regexp;
|
||||
regexp::pattern_t right_anchored_regexp;
|
||||
bool is_regexp = false;
|
||||
};
|
||||
|
||||
struct match_t
|
||||
{
|
||||
match_t (std::string const& match = NULL_STR, pattern_t const& counterpart_ptrn = pattern_t(), bool matched_opener = false) : match(match), counterpart_ptrn(counterpart_ptrn), matched_opener(matched_opener) { }
|
||||
operator bool () const { return match != NULL_STR; }
|
||||
std::string match;
|
||||
pattern_t counterpart_ptrn;
|
||||
bool matched_opener;
|
||||
};
|
||||
|
||||
struct enclosed_range_t
|
||||
{
|
||||
enclosed_range_t (std::string const& opener = NULL_STR, std::string const& closer = NULL_STR) : opener(opener), closer(closer) { }
|
||||
enclosed_range_t () { }
|
||||
enclosed_range_t (std::pair<pattern_t, pattern_t> const& pair) : opener_ptrn(pair.first), closer_ptrn(pair.second) { }
|
||||
|
||||
operator bool () const { return open_index != SIZE_T_MAX && close_index != 0; }
|
||||
|
||||
std::string opener, closer;
|
||||
pattern_t opener_ptrn, closer_ptrn;
|
||||
std::string opener_match, closer_match;
|
||||
size_t open_index = SIZE_T_MAX, close_index = 0;
|
||||
ssize_t open_count = 0, close_count = 0;
|
||||
};
|
||||
}
|
||||
|
||||
static bool does_match (buffer_t const& buffer, size_t index, std::string const& ch)
|
||||
static std::vector<std::pair<pattern_t, pattern_t>> character_pairs (scope::context_t const& scope, std::string const& key)
|
||||
{
|
||||
return index + ch.size() <= buffer.size() && ch == buffer.substr(index, index + ch.size());
|
||||
std::vector<std::pair<pattern_t, pattern_t>> res;
|
||||
plist::any_t value = bundles::value_for_setting(key, scope);
|
||||
if(plist::array_t const* array = boost::get<plist::array_t>(&value))
|
||||
{
|
||||
for(auto const& pair : *array)
|
||||
{
|
||||
if(plist::array_t const* value = boost::get<plist::array_t>(&pair))
|
||||
{
|
||||
std::string const* a1 = value->size() == 2 ? boost::get<std::string>(&(*value)[0]) : NULL;
|
||||
std::string const* a2 = value->size() == 2 ? boost::get<std::string>(&(*value)[1]) : NULL;
|
||||
if(a1 && a2 && *a1 != *a2)
|
||||
res.emplace_back(*a1, *a2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res.emplace_back(pattern_t("("), pattern_t(")"));
|
||||
res.emplace_back(pattern_t("{"), pattern_t("}"));
|
||||
res.emplace_back(pattern_t("["), pattern_t("]"));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static enclosed_range_t find_enclosed_range (buffer_t const& buffer, size_t index, std::vector< std::pair<std::string, std::string> > const& pairs)
|
||||
static bool does_match (regexp::pattern_t const& ptrn, buffer_t const& buffer, size_t from, size_t to, std::string* didMatch)
|
||||
{
|
||||
std::vector<enclosed_range_t> records;
|
||||
for(auto pair : pairs)
|
||||
records.emplace_back(pair.first, pair.second);
|
||||
if(ptrn)
|
||||
{
|
||||
size_t bol = std::min(from, to), eol = std::max(from, to);
|
||||
std::string line = buffer.substr(bol, eol);
|
||||
if(auto m = regexp::search(ptrn, line.data(), line.data() + line.size(), line.data() + (from - bol), line.data() + (to - bol), ONIG_OPTION_FIND_NOT_EMPTY))
|
||||
{
|
||||
*didMatch = line.substr(m.begin(), m.end() - m.begin());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool does_match_left (pattern_t const& ptrn, buffer_t const& buffer, size_t index, std::string* didMatch)
|
||||
{
|
||||
if(!ptrn.is_regexp && ptrn.plain.size() <= index && ptrn.plain == buffer.substr(index - ptrn.plain.size(), index))
|
||||
{
|
||||
*didMatch = ptrn.plain;
|
||||
return true;
|
||||
}
|
||||
return ptrn.is_regexp && does_match(ptrn.right_anchored_regexp, buffer, index, buffer.begin(buffer.convert(index).line), didMatch);
|
||||
}
|
||||
|
||||
static bool does_match_right (pattern_t const& ptrn, buffer_t const& buffer, size_t index, std::string* didMatch)
|
||||
{
|
||||
if(!ptrn.is_regexp && index + ptrn.plain.size() <= buffer.size() && ptrn.plain == buffer.substr(index, index + ptrn.plain.size()))
|
||||
{
|
||||
*didMatch = ptrn.plain;
|
||||
return true;
|
||||
}
|
||||
return ptrn.is_regexp && does_match(ptrn.left_anchored_regexp, buffer, index, buffer.eol(buffer.convert(index).line), didMatch);
|
||||
}
|
||||
|
||||
static enclosed_range_t find_enclosed_range (buffer_t const& buffer, size_t index, std::vector<std::pair<pattern_t, pattern_t>> const& pairs)
|
||||
{
|
||||
std::vector<enclosed_range_t> records(pairs.begin(), pairs.end());
|
||||
|
||||
size_t left = index, right = index;
|
||||
while(left > 0 || right < buffer.size())
|
||||
while(0 < left || right < buffer.size())
|
||||
{
|
||||
if(left > 0)
|
||||
for(auto& r : records)
|
||||
{
|
||||
left -= buffer[left-1].size();
|
||||
for(auto& r : records)
|
||||
std::string match;
|
||||
if(0 < left && r.open_index == SIZE_T_MAX)
|
||||
{
|
||||
if(does_match(buffer, left, r.opener))
|
||||
if(does_match_left(r.opener_ptrn, buffer, left, &match) && ++r.open_count == 1)
|
||||
{
|
||||
if(++r.open_count == 1 && r.open_index == SIZE_T_MAX)
|
||||
{
|
||||
r.open_index = left;
|
||||
if(r)
|
||||
return r;
|
||||
}
|
||||
r.open_index = left;
|
||||
r.opener_match = match;
|
||||
}
|
||||
else if(does_match(buffer, left, r.closer))
|
||||
else if(does_match_left(r.closer_ptrn, buffer, left, &match))
|
||||
{
|
||||
--r.open_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(right < buffer.size())
|
||||
{
|
||||
for(auto& r : records)
|
||||
if(right < buffer.size() && r.close_index == 0)
|
||||
{
|
||||
if(does_match(buffer, right, r.closer))
|
||||
if(does_match_right(r.closer_ptrn, buffer, right, &match) && ++r.close_count == 1)
|
||||
{
|
||||
if(++r.close_count == 1 && r.close_index == 0)
|
||||
{
|
||||
r.close_index = right;
|
||||
if(r)
|
||||
return r;
|
||||
}
|
||||
r.close_index = right;
|
||||
r.closer_match = match;
|
||||
}
|
||||
else if(does_match(buffer, right, r.opener))
|
||||
else if(does_match_right(r.opener_ptrn, buffer, right, &match))
|
||||
{
|
||||
--r.close_count;
|
||||
}
|
||||
}
|
||||
right += buffer[right].size();
|
||||
|
||||
if(r)
|
||||
return r;
|
||||
}
|
||||
|
||||
if(0 < left)
|
||||
left -= buffer[left-1].size();
|
||||
if(right < buffer.size())
|
||||
right += buffer[right].size();
|
||||
}
|
||||
return enclosed_range_t();
|
||||
}
|
||||
|
||||
static character_pair_t first_match (buffer_t const& buffer, size_t index, std::vector< std::pair<std::string, std::string> > const& pairs)
|
||||
static match_t first_match (buffer_t const& buffer, size_t index, std::vector<std::pair<pattern_t, pattern_t>> const& pairs, bool(*matcher)(pattern_t const&, buffer_t const&, size_t, std::string*))
|
||||
{
|
||||
citerate(pair, pairs)
|
||||
std::string didMatch;
|
||||
for(auto const& pair : pairs)
|
||||
{
|
||||
if(does_match(buffer, index, pair->first))
|
||||
return character_pair_t(pair->first, pair->second, true);
|
||||
else if(does_match(buffer, index, pair->second))
|
||||
return character_pair_t(pair->first, pair->second, false);
|
||||
if(matcher(pair.first, buffer, index, &didMatch))
|
||||
return match_t(didMatch, pair.second, true);
|
||||
else if(matcher(pair.second, buffer, index, &didMatch))
|
||||
return match_t(didMatch, pair.first, false);
|
||||
}
|
||||
return character_pair_t();
|
||||
}
|
||||
|
||||
static character_pair_t first_match_left (buffer_t const& buffer, size_t index, std::vector< std::pair<std::string, std::string> > const& pairs)
|
||||
{
|
||||
citerate(pair, pairs)
|
||||
{
|
||||
if(index < pair->first.size())
|
||||
continue;
|
||||
if(does_match(buffer, index - pair->first.size(), pair->first))
|
||||
return character_pair_t(pair->first, pair->second, true);
|
||||
else if(does_match(buffer, index - pair->second.size(), pair->second))
|
||||
return character_pair_t(pair->first, pair->second, false);
|
||||
}
|
||||
return character_pair_t();
|
||||
return match_t();
|
||||
}
|
||||
|
||||
static size_t begin_of_typing_pair (buffer_t const& buffer, size_t caret, bool moveToBefore)
|
||||
@@ -320,23 +351,24 @@ namespace ng
|
||||
size_t orgCaret = caret;
|
||||
auto pairs = character_pairs(buffer.scope(caret), "highlightPairs");
|
||||
|
||||
std::string skipOpener = NULL_STR;
|
||||
pattern_t openerPtrn;
|
||||
if(!moveToBefore)
|
||||
{
|
||||
if(auto pair = first_match_left(buffer, caret, pairs))
|
||||
if(auto m = first_match(buffer, caret, pairs, &does_match_left))
|
||||
{
|
||||
skipOpener = pair.matched_first && pair.first != pair.second ? NULL_STR : pair.first;
|
||||
caret -= pair.matched_first ? pair.first.size() : pair.second.size();
|
||||
openerPtrn = m.matched_opener ? pattern_t() : m.counterpart_ptrn;
|
||||
caret -= m.match.size();
|
||||
}
|
||||
}
|
||||
|
||||
if(auto range = find_enclosed_range(buffer, caret, pairs))
|
||||
{
|
||||
caret = range.open_index;
|
||||
if(!moveToBefore && !does_match(buffer, caret, skipOpener))
|
||||
caret += range.opener.size();
|
||||
if(moveToBefore || does_match_left(openerPtrn, buffer, caret, &range.opener_match))
|
||||
caret -= range.opener_match.size();
|
||||
return caret;
|
||||
}
|
||||
|
||||
return orgCaret;
|
||||
}
|
||||
|
||||
@@ -345,23 +377,24 @@ namespace ng
|
||||
size_t orgCaret = caret;
|
||||
auto pairs = character_pairs(buffer.scope(caret), "highlightPairs");
|
||||
|
||||
std::string skipCloser = NULL_STR;
|
||||
pattern_t closerPtrn;
|
||||
if(!moveToAfter)
|
||||
{
|
||||
if(auto pair = first_match(buffer, caret, pairs))
|
||||
if(auto m = first_match(buffer, caret, pairs, &does_match_right))
|
||||
{
|
||||
skipCloser = pair.matched_first ? pair.second : NULL_STR;
|
||||
caret += pair.matched_first ? pair.first.size() : pair.second.size();
|
||||
closerPtrn = m.matched_opener ? m.counterpart_ptrn : pattern_t();
|
||||
caret += m.match.size();
|
||||
}
|
||||
}
|
||||
|
||||
if(auto range = find_enclosed_range(buffer, caret, pairs))
|
||||
{
|
||||
caret = range.close_index;
|
||||
if(moveToAfter || does_match(buffer, caret, skipCloser))
|
||||
caret += range.closer.size();
|
||||
if(moveToAfter || does_match_right(closerPtrn, buffer, caret, &range.closer_match))
|
||||
caret += range.closer_match.size();
|
||||
return caret;
|
||||
}
|
||||
|
||||
return orgCaret;
|
||||
}
|
||||
|
||||
@@ -1127,24 +1160,25 @@ namespace ng
|
||||
size_t curCaret = newRange->last.index;
|
||||
auto pairs = character_pairs(buffer.scope(std::min(prevCaret, curCaret)), "highlightPairs");
|
||||
|
||||
std::string didMatch;
|
||||
if(prevCaret < curCaret && prevCaret + buffer[prevCaret].size() == curCaret) // moved right, check end character
|
||||
{
|
||||
auto pair = first_match(buffer, prevCaret, pairs);
|
||||
if(pair && !pair.matched_first)
|
||||
auto m = first_match(buffer, curCaret, pairs, &does_match_left);
|
||||
if(m && !m.matched_opener)
|
||||
{
|
||||
size_t from = begin_of_typing_pair(buffer, prevCaret, true);
|
||||
if(from != prevCaret && does_match(buffer, from, pair.first))
|
||||
res.push_back(range_t(from, from + pair.first.size()));
|
||||
size_t from = begin_of_typing_pair(buffer, curCaret - m.match.size(), true);
|
||||
if(from != prevCaret && does_match_right(m.counterpart_ptrn, buffer, from, &didMatch))
|
||||
res.push_back(range_t(from, from + didMatch.size()));
|
||||
}
|
||||
}
|
||||
else if(curCaret < prevCaret && curCaret + buffer[curCaret].size() == prevCaret) // moved left, check begin character
|
||||
{
|
||||
auto pair = first_match(buffer, curCaret, pairs);
|
||||
if(pair && pair.matched_first)
|
||||
auto m = first_match(buffer, curCaret, pairs, &does_match_right);
|
||||
if(m && m.matched_opener)
|
||||
{
|
||||
size_t to = end_of_typing_pair(buffer, prevCaret, true);
|
||||
if(to != prevCaret && to >= pair.second.size() && does_match(buffer, to - pair.second.size(), pair.second))
|
||||
res.push_back(range_t(to - pair.second.size(), to));
|
||||
size_t to = end_of_typing_pair(buffer, curCaret + m.match.size(), true);
|
||||
if(to != prevCaret && does_match_left(m.counterpart_ptrn, buffer, to, &didMatch))
|
||||
res.push_back(range_t(to - didMatch.size(), to));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
#include <buffer/buffer.h>
|
||||
#include <selection/selection.h>
|
||||
#include <test/bundle_index.h>
|
||||
|
||||
class HighlightPairFixtures
|
||||
{
|
||||
public:
|
||||
HighlightPairFixtures ()
|
||||
{
|
||||
static std::string HighlightPairs =
|
||||
"{ name = 'Highlight Pairs';"
|
||||
" settings = {"
|
||||
" highlightPairs = ("
|
||||
" ( '(', ')' ),"
|
||||
" ( '{', '}' ),"
|
||||
" ( '[', ']' ),"
|
||||
" ( '“', '”' ),"
|
||||
" ( '/<\\w+[^>]*>/', '/</\\w+>/' ),"
|
||||
" );"
|
||||
" };"
|
||||
"}";
|
||||
|
||||
test::bundle_index_t bundleIndex;
|
||||
bundleIndex.add(bundles::kItemTypeSettings, HighlightPairs);
|
||||
bundleIndex.commit();
|
||||
}
|
||||
|
||||
} singleton;
|
||||
|
||||
class BraceSelectionTests : public CxxTest::TestSuite
|
||||
{
|
||||
@@ -55,6 +81,32 @@ public:
|
||||
TS_ASSERT_EQUALS(move("({bad‸ly) nested}", kSelectionMoveToEndOfTypingPair), "({badly‸) nested}");
|
||||
}
|
||||
|
||||
void test_tag_movement ()
|
||||
{
|
||||
TS_ASSERT_EQUALS(move("<html></html‸>", kSelectionMoveToBeginOfTypingPair), "<html></html‸>");
|
||||
TS_ASSERT_EQUALS(move("<html></html>‸", kSelectionMoveToBeginOfTypingPair), "‸<html></html>");
|
||||
TS_ASSERT_EQUALS(move("<html><body></body‸></html>", kSelectionMoveToBeginOfTypingPair), "<html><body>‸</body></html>");
|
||||
TS_ASSERT_EQUALS(move("<html><body>‸</body></html>", kSelectionMoveToBeginOfTypingPair), "<html>‸<body></body></html>");
|
||||
|
||||
TS_ASSERT_EQUALS(move("<‸html></html>", kSelectionMoveToEndOfTypingPair), "<‸html></html>");
|
||||
TS_ASSERT_EQUALS(move("‸<html></html>", kSelectionMoveToEndOfTypingPair), "<html></html>‸");
|
||||
TS_ASSERT_EQUALS(move("<html><‸body></body></html>", kSelectionMoveToEndOfTypingPair), "<html><body>‸</body></html>");
|
||||
TS_ASSERT_EQUALS(move("<html><body>‸</body></html>", kSelectionMoveToEndOfTypingPair), "<html><body></body>‸</html>");
|
||||
|
||||
TS_ASSERT_EQUALS(move("<ul><li>first‸</li> <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>", kSelectionMoveToBeginOfTypingPair), "<ul><li>‸first</li> <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>");
|
||||
TS_ASSERT_EQUALS(move("<ul><li>first</li>‸ <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>", kSelectionMoveToBeginOfTypingPair), "<ul>‸<li>first</li> <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>");
|
||||
TS_ASSERT_EQUALS(move("<ul><li>first</li> ‸ <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>", kSelectionMoveToBeginOfTypingPair), "<ul>‸<li>first</li> <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>");
|
||||
TS_ASSERT_EQUALS(move("<ul><li>first</li> <li‸> <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>", kSelectionMoveToBeginOfTypingPair), "<ul>‸<li>first</li> <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>");
|
||||
TS_ASSERT_EQUALS(move("<ul><li>first</li> <li>‸ <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>", kSelectionMoveToBeginOfTypingPair), "<ul>‸<li>first</li> <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>");
|
||||
TS_ASSERT_EQUALS(move("<ul><li>first</li> <li> <a> <b>hello</b>‸ <em>world</em></a></li><li>third</li></ul>", kSelectionMoveToBeginOfTypingPair), "<ul><li>first</li> <li> <a> ‸<b>hello</b> <em>world</em></a></li><li>third</li></ul>");
|
||||
TS_ASSERT_EQUALS(move("<ul><li>first</li> <li> <a> <b>hello</b> ‸<em>world</em></a></li><li>third</li></ul>", kSelectionMoveToBeginOfTypingPair), "<ul><li>first</li> <li> <a>‸ <b>hello</b> <em>world</em></a></li><li>third</li></ul>");
|
||||
TS_ASSERT_EQUALS(move("<ul><li>first</li> <li> <a> <b>hello</b> <em>world</em></a>‸</li><li>third</li></ul>", kSelectionMoveToBeginOfTypingPair), "<ul><li>first</li> <li> ‸<a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>");
|
||||
|
||||
TS_ASSERT_EQUALS(move("<ul><li>first‸</li> <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>", kSelectionMoveToEndOfTypingPair), "<ul><li>first</li> <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li>‸</ul>");
|
||||
TS_ASSERT_EQUALS(move("<ul><li>first</li> ‸ <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li></ul>", kSelectionMoveToEndOfTypingPair), "<ul><li>first</li> <li> <a> <b>hello</b> <em>world</em></a></li><li>third</li>‸</ul>");
|
||||
TS_ASSERT_EQUALS(move("<ul><li>first</li> <li> <a> <b>hello</b> ‸<em>world</em></a></li><li>third</li></ul>", kSelectionMoveToEndOfTypingPair), "<ul><li>first</li> <li> <a> <b>hello</b> <em>world</em>‸</a></li><li>third</li></ul>");
|
||||
}
|
||||
|
||||
void test_brace_selection ()
|
||||
{
|
||||
ng::buffer_t buf;
|
||||
@@ -91,4 +143,16 @@ public:
|
||||
TS_ASSERT_EQUALS(to_s(ng::highlight_ranges_for_movement(buf, caret, ng::move(buf, caret, kSelectionMoveRight))), "(empty)");
|
||||
TS_ASSERT_EQUALS(to_s(ng::highlight_ranges_for_movement(buf, caret, ng::move(buf, caret, kSelectionMoveLeft))), "(empty)");
|
||||
}
|
||||
|
||||
void test_tag_highlight_pairs ()
|
||||
{
|
||||
ng::buffer_t buf;
|
||||
buf.insert(0, "<a></a>");
|
||||
|
||||
ng::ranges_t caret = ng::range_t(1);
|
||||
TS_ASSERT_EQUALS(to_s(ng::highlight_ranges_for_movement(buf, caret, ng::move(buf, caret, kSelectionMoveLeft))), "[3-7]");
|
||||
|
||||
caret = ng::range_t(6);
|
||||
TS_ASSERT_EQUALS(to_s(ng::highlight_ranges_for_movement(buf, caret, ng::move(buf, caret, kSelectionMoveRight))), "[0-3]");
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user