Selection: do not use [begin, end) semantics but [first, last]

selections are now defined with inclusive iterators, which means that
Selection(cursor, cursor) is a valid selection of the charateter pointed
by cursor.

On the user interface side, that means that the cursor is now part of
the selection, selectors were adapted to this behavior (and word
selectors are now much more intuitive)
This commit is contained in:
Maxime Coste 2011-09-23 09:17:19 +00:00
parent 5ca901644f
commit 662ba0c904
4 changed files with 89 additions and 51 deletions

View File

@ -16,9 +16,9 @@ Selection RegexSelector::operator()(const BufferIterator& cursor) const
boost::match_results<BufferIterator> matches; boost::match_results<BufferIterator> matches;
if (boost::regex_search(cursor, cursor.buffer().end(), matches, m_regex, boost::match_nosubs)) if (boost::regex_search(cursor, cursor.buffer().end(), matches, m_regex, boost::match_nosubs))
return Selection(matches.begin()->first, matches.begin()->second); return Selection(matches.begin()->first, matches.begin()->second-1);
else if (boost::regex_search(cursor.buffer().begin(), cursor, matches, m_regex, boost::match_nosubs)) else if (boost::regex_search(cursor.buffer().begin(), cursor, matches, m_regex, boost::match_nosubs))
return Selection(matches.begin()->first, matches.begin()->second); return Selection(matches.begin()->first, matches.begin()->second-1);
} }
catch (boost::regex_error& err) catch (boost::regex_error& err)
{ {

View File

@ -33,6 +33,22 @@ static bool is_punctuation(char c)
return not (is_word(c) or is_blank(c)); return not (is_word(c) or is_blank(c));
} }
enum class CharCategories
{
Blank,
Word,
Punctuation,
};
static CharCategories categorize(char c)
{
if (is_word(c))
return CharCategories::Word;
if (is_blank(c))
return CharCategories::Blank;
return CharCategories::Punctuation;
}
template<typename T> template<typename T>
void skip_while(BufferIterator& it, T condition) void skip_while(BufferIterator& it, T condition)
{ {
@ -50,55 +66,72 @@ void skip_while_reverse(BufferIterator& it, T condition)
Selection select_to_next_word(const BufferIterator& cursor) Selection select_to_next_word(const BufferIterator& cursor)
{ {
BufferIterator end = cursor; BufferIterator begin = cursor;
BufferIterator end = cursor+1;
if (is_word(*end)) if (categorize(*begin) != categorize(*end))
skip_while(end, is_word); {
else if (is_punctuation(*end)) ++begin;
++end;
}
if (is_punctuation(*begin))
skip_while(end, is_punctuation); skip_while(end, is_punctuation);
if (is_word(*begin))
skip_while(end, is_word);
skip_while(end, is_blank); skip_while(end, is_blank);
return Selection(cursor, end); return Selection(begin, end-1);
} }
Selection select_to_next_word_end(const BufferIterator& cursor) Selection select_to_next_word_end(const BufferIterator& cursor)
{ {
BufferIterator end = cursor; BufferIterator begin = cursor;
BufferIterator end = cursor+1;
if (categorize(*begin) != categorize(*end))
++begin;
skip_while(end, is_blank); skip_while(end, is_blank);
if (is_word(*end)) if (is_punctuation(*end))
skip_while(end, is_word);
else if (is_punctuation(*end))
skip_while(end, is_punctuation); skip_while(end, is_punctuation);
else if (is_word(*end))
skip_while(end, is_word);
return Selection(cursor, end); return Selection(begin, end-1);
} }
Selection select_to_previous_word(const BufferIterator& cursor) Selection select_to_previous_word(const BufferIterator& cursor)
{ {
BufferIterator end = cursor; BufferIterator begin = cursor;
BufferIterator end = cursor-1;
if (categorize(*begin) != categorize(*end))
--begin;
skip_while_reverse(end, is_blank); skip_while_reverse(end, is_blank);
if (is_word(*end))
skip_while_reverse(end, is_word);
else if (is_punctuation(*end))
skip_while_reverse(end, is_punctuation);
return Selection(cursor, end); if (is_punctuation(*end))
skip_while_reverse(end, is_punctuation);
else if (is_word(*end))
skip_while_reverse(end, is_word);
return Selection(begin, end+1);
} }
Selection select_line(const BufferIterator& cursor) Selection select_line(const BufferIterator& cursor)
{ {
BufferIterator begin = cursor; BufferIterator first = cursor;
while (not begin.is_begin() and *(begin -1) != '\n') while (not first.is_begin() and *(first - 1) != '\n')
--begin; --first;
BufferIterator end = cursor; BufferIterator last = cursor;
while (not end.is_end() and *end != '\n') while (not (last + 1).is_end() and *last != '\n')
++end; ++last;
return Selection(begin, end + 1); return Selection(first, last);
} }
Selection move_select(Window& window, const BufferIterator& cursor, const WindowCoord& offset) Selection move_select(Window& window, const BufferIterator& cursor, const WindowCoord& offset)
@ -136,7 +169,7 @@ Selection select_matching(const BufferIterator& cursor)
if (*it == opening) if (*it == opening)
++level; ++level;
else if (*it == closing and --level == 0) else if (*it == closing and --level == 0)
return Selection(begin, it+1); return Selection(begin, it);
++it; ++it;
} }
@ -151,7 +184,7 @@ Selection select_matching(const BufferIterator& cursor)
if (*it == closing) if (*it == closing)
++level; ++level;
else if (*it == opening and --level == 0) else if (*it == opening and --level == 0)
return Selection(begin, it-1); return Selection(begin, it);
--it; --it;
} }
} }
@ -163,7 +196,7 @@ Selection select_to(const BufferIterator& cursor, char c)
BufferIterator end = cursor + 1; BufferIterator end = cursor + 1;
skip_while(end, [c](char cur) { return not is_eol(cur) and cur != c; }); skip_while(end, [c](char cur) { return not is_eol(cur) and cur != c; });
if (not is_eol(*end)) if (not is_eol(*end))
return Selection(cursor, end); return Selection(cursor, end-1);
return Selection(cursor, cursor); return Selection(cursor, cursor);
} }

View File

@ -7,6 +7,22 @@
namespace Kakoune namespace Kakoune
{ {
BufferIterator Selection::begin() const
{
return std::min(m_first, m_last);
}
BufferIterator Selection::end() const
{
return std::max(m_first, m_last) + 1;
}
void Selection::offset(int offset)
{
m_first += offset;
m_last += offset;
}
Window::Window(Buffer& buffer) Window::Window(Buffer& buffer)
: m_buffer(buffer), : m_buffer(buffer),
m_position(0, 0), m_position(0, 0),
@ -27,12 +43,11 @@ void Window::erase()
for (auto& sel : m_selections) for (auto& sel : m_selections)
{ {
sel.canonicalize();
m_buffer.erase(sel.begin(), sel.end()); m_buffer.erase(sel.begin(), sel.end());
sel = Selection(sel.begin(), sel.begin()); sel = Selection(sel.begin(), sel.begin());
} }
if (not m_selections.empty()) if (not m_selections.empty())
m_cursor = line_and_column_at(m_selections.back().end()); m_cursor = line_and_column_at(m_selections.back().last());
m_buffer.end_undo_group(); m_buffer.end_undo_group();
} }
@ -133,10 +148,10 @@ void Window::select(bool append, const Selector& selector)
{ {
for (auto& sel : m_selections) for (auto& sel : m_selections)
{ {
sel = Selection(sel.begin(), selector(sel.end()).end()); sel = Selection(sel.first(), selector(sel.last()).last());
} }
} }
m_cursor = line_and_column_at(m_selections.back().end()); m_cursor = line_and_column_at(m_selections.back().last());
scroll_to_keep_cursor_visible_ifn(); scroll_to_keep_cursor_visible_ifn();
} }
@ -160,9 +175,6 @@ void Window::update_display_buffer()
SelectionList sorted_selections = m_selections; SelectionList sorted_selections = m_selections;
for (auto& sel : sorted_selections)
sel.canonicalize();
std::sort(sorted_selections.begin(), sorted_selections.end(), std::sort(sorted_selections.begin(), sorted_selections.end(),
[](const Selection& lhs, const Selection& rhs) { return lhs.begin() < rhs.begin(); }); [](const Selection& lhs, const Selection& rhs) { return lhs.begin() < rhs.begin(); });

View File

@ -19,27 +19,20 @@ struct WindowCoord : LineAndColumn<WindowCoord>
struct Selection struct Selection
{ {
Selection(const BufferIterator& begin, const BufferIterator& end) Selection(const BufferIterator& first, const BufferIterator& last)
: m_begin(begin), m_end(end) {} : m_first(first), m_last(last) {}
const BufferIterator& begin() const { return m_begin; } BufferIterator begin() const;
const BufferIterator& end() const { return m_end; } BufferIterator end() const;
void canonicalize() const BufferIterator& first() const { return m_first; }
{ const BufferIterator& last() const { return m_last; }
if (m_end < m_begin)
std::swap(++m_begin, ++m_end);
}
void offset(int offset) void offset(int offset);
{
m_begin += offset;
m_end += offset;
}
private: private:
BufferIterator m_begin; BufferIterator m_first;
BufferIterator m_end; BufferIterator m_last;
}; };
typedef std::vector<Selection> SelectionList; typedef std::vector<Selection> SelectionList;