2011-09-02 20:51:20 +04:00
|
|
|
#include "buffer.hh"
|
2011-09-08 04:11:48 +04:00
|
|
|
|
2011-09-09 23:24:18 +04:00
|
|
|
#include "assert.hh"
|
2013-04-09 22:05:40 +04:00
|
|
|
#include "buffer_manager.hh"
|
2012-01-23 17:56:43 +04:00
|
|
|
#include "context.hh"
|
2013-03-25 22:58:23 +04:00
|
|
|
#include "file.hh"
|
2013-04-09 22:05:40 +04:00
|
|
|
#include "utils.hh"
|
|
|
|
#include "window.hh"
|
2011-09-08 04:11:48 +04:00
|
|
|
|
2012-03-06 18:27:03 +04:00
|
|
|
#include <algorithm>
|
|
|
|
|
2011-09-02 20:51:20 +04:00
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
2012-11-23 21:42:07 +04:00
|
|
|
Buffer::Buffer(String name, Flags flags, std::vector<String> lines)
|
2012-11-21 16:43:10 +04:00
|
|
|
: m_name(std::move(name)), m_flags(flags | Flags::NoUndo),
|
2012-11-21 16:37:36 +04:00
|
|
|
m_history(), m_history_cursor(m_history.begin()),
|
2012-04-03 17:39:20 +04:00
|
|
|
m_last_save_undo_index(0),
|
2012-08-15 19:07:53 +04:00
|
|
|
m_timestamp(0),
|
2012-11-22 16:50:29 +04:00
|
|
|
m_hooks(GlobalHooks::instance()),
|
|
|
|
m_options(GlobalOptions::instance())
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2012-08-08 21:36:40 +04:00
|
|
|
BufferManager::instance().register_buffer(*this);
|
2012-11-23 21:42:07 +04:00
|
|
|
|
2013-03-25 22:58:23 +04:00
|
|
|
if (flags & Flags::File)
|
|
|
|
m_name = real_path(m_name);
|
|
|
|
|
2012-11-27 16:39:35 +04:00
|
|
|
if (lines.empty())
|
|
|
|
lines.emplace_back("\n");
|
|
|
|
|
2012-11-23 21:42:07 +04:00
|
|
|
ByteCount pos = 0;
|
|
|
|
m_lines.reserve(lines.size());
|
|
|
|
for (auto& line : lines)
|
|
|
|
{
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(not line.empty() and line.back() == '\n');
|
2012-11-23 21:42:07 +04:00
|
|
|
m_lines.emplace_back(Line{ pos, std::move(line) });
|
|
|
|
pos += m_lines.back().length();
|
|
|
|
}
|
2012-08-14 16:13:10 +04:00
|
|
|
|
2012-08-05 22:12:43 +04:00
|
|
|
Editor editor_for_hooks(*this);
|
|
|
|
Context context(editor_for_hooks);
|
2013-04-12 21:11:28 +04:00
|
|
|
if (flags & Flags::File)
|
|
|
|
{
|
|
|
|
if (flags & Flags::New)
|
|
|
|
m_hooks.run_hook("BufNew", m_name, context);
|
|
|
|
else
|
|
|
|
m_hooks.run_hook("BufOpen", m_name, context);
|
|
|
|
}
|
2012-06-12 22:27:57 +04:00
|
|
|
|
2012-11-22 16:50:29 +04:00
|
|
|
m_hooks.run_hook("BufCreate", m_name, context);
|
2012-09-11 21:03:37 +04:00
|
|
|
|
2012-11-21 16:43:10 +04:00
|
|
|
// now we may begin to record undo data
|
|
|
|
m_flags = flags;
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2011-10-24 18:23:13 +04:00
|
|
|
Buffer::~Buffer()
|
|
|
|
{
|
2013-01-17 16:44:14 +04:00
|
|
|
{
|
|
|
|
Editor hook_editor{*this};
|
|
|
|
Context hook_context{hook_editor};
|
|
|
|
m_hooks.run_hook("BufClose", m_name, hook_context);
|
|
|
|
}
|
2012-08-05 22:12:43 +04:00
|
|
|
|
2012-08-08 21:36:40 +04:00
|
|
|
BufferManager::instance().unregister_buffer(*this);
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(m_change_listeners.empty());
|
2011-10-24 18:23:13 +04:00
|
|
|
}
|
|
|
|
|
2013-03-25 22:58:23 +04:00
|
|
|
String Buffer::display_name() const
|
|
|
|
{
|
|
|
|
if (m_flags & Flags::File)
|
|
|
|
return compact_path(m_name);
|
|
|
|
return m_name;
|
|
|
|
}
|
|
|
|
|
2013-04-22 15:48:18 +04:00
|
|
|
bool Buffer::set_name(String name)
|
|
|
|
{
|
|
|
|
Buffer* other = BufferManager::instance().get_buffer_ifp(name);
|
|
|
|
if (other == nullptr or other == this)
|
|
|
|
{
|
|
|
|
if (m_flags & Flags::File)
|
|
|
|
m_name = real_path(name);
|
|
|
|
else
|
|
|
|
m_name = std::move(name);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-08-15 20:06:59 +04:00
|
|
|
BufferIterator Buffer::iterator_at(const BufferCoord& line_and_column,
|
|
|
|
bool avoid_eol) const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2012-08-15 20:06:59 +04:00
|
|
|
return BufferIterator(*this, clamp(line_and_column, avoid_eol));
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2012-10-11 02:41:48 +04:00
|
|
|
ByteCount Buffer::line_length(LineCount line) const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(line < line_count());
|
2012-10-11 02:41:48 +04:00
|
|
|
ByteCount end = (line < line_count() - 1) ?
|
2013-04-24 15:56:36 +04:00
|
|
|
m_lines[line + 1].start : byte_count();
|
2012-03-30 15:37:18 +04:00
|
|
|
return end - m_lines[line].start;
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2012-08-15 20:06:59 +04:00
|
|
|
BufferCoord Buffer::clamp(const BufferCoord& line_and_column,
|
|
|
|
bool avoid_eol) const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
|
|
|
if (m_lines.empty())
|
2011-09-05 23:06:31 +04:00
|
|
|
return BufferCoord();
|
2011-09-02 20:51:20 +04:00
|
|
|
|
2011-09-05 23:06:31 +04:00
|
|
|
BufferCoord result(line_and_column.line, line_and_column.column);
|
2012-10-02 16:08:09 +04:00
|
|
|
result.line = Kakoune::clamp(result.line, 0_line, line_count() - 1);
|
2012-10-11 02:41:48 +04:00
|
|
|
ByteCount max_col = std::max(0_byte, line_length(result.line) - (avoid_eol ? 2 : 1));
|
|
|
|
result.column = Kakoune::clamp(result.column, 0_byte, max_col);
|
2011-09-02 20:51:20 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:33:52 +04:00
|
|
|
BufferIterator Buffer::iterator_at_line_begin(LineCount line) const
|
2012-08-21 22:52:49 +04:00
|
|
|
{
|
2012-10-31 17:28:03 +04:00
|
|
|
line = Kakoune::clamp(line, 0_line, line_count()-1);
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(line_length(line) > 0);
|
2012-10-31 17:28:03 +04:00
|
|
|
return BufferIterator(*this, { line, 0 });
|
2012-08-21 22:52:49 +04:00
|
|
|
}
|
|
|
|
|
2013-03-13 17:25:22 +04:00
|
|
|
BufferIterator Buffer::iterator_at_line_begin(const BufferIterator& iterator) const
|
2011-11-28 23:31:29 +04:00
|
|
|
{
|
2013-03-13 17:25:22 +04:00
|
|
|
return iterator_at_line_begin(iterator.line());
|
2011-11-28 23:31:29 +04:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:33:52 +04:00
|
|
|
BufferIterator Buffer::iterator_at_line_end(LineCount line) const
|
2012-08-21 22:52:49 +04:00
|
|
|
{
|
2012-10-31 17:28:03 +04:00
|
|
|
line = Kakoune::clamp(line, 0_line, line_count()-1);
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(line_length(line) > 0);
|
2012-08-23 01:33:52 +04:00
|
|
|
return ++BufferIterator(*this, { line, line_length(line) - 1 });
|
2012-08-21 22:52:49 +04:00
|
|
|
}
|
|
|
|
|
2013-03-13 17:25:22 +04:00
|
|
|
BufferIterator Buffer::iterator_at_line_end(const BufferIterator& iterator) const
|
|
|
|
{
|
|
|
|
return iterator_at_line_end(iterator.line());
|
|
|
|
}
|
|
|
|
|
2011-09-02 20:51:20 +04:00
|
|
|
BufferIterator Buffer::begin() const
|
|
|
|
{
|
2012-08-23 01:33:52 +04:00
|
|
|
return BufferIterator(*this, { 0_line, 0 });
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
BufferIterator Buffer::end() const
|
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
if (m_lines.empty())
|
2012-08-23 01:33:52 +04:00
|
|
|
return BufferIterator(*this, { 0_line, 0 });
|
2012-08-24 01:56:35 +04:00
|
|
|
return BufferIterator(*this, { line_count()-1, m_lines.back().length() });
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2013-04-24 15:56:36 +04:00
|
|
|
ByteCount Buffer::byte_count() const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
if (m_lines.empty())
|
|
|
|
return 0;
|
2012-03-30 16:00:40 +04:00
|
|
|
return m_lines.back().start + m_lines.back().length();
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:33:52 +04:00
|
|
|
LineCount Buffer::line_count() const
|
2011-09-22 17:58:35 +04:00
|
|
|
{
|
2012-08-23 01:33:52 +04:00
|
|
|
return LineCount(m_lines.size());
|
2011-09-22 17:58:35 +04:00
|
|
|
}
|
|
|
|
|
2012-03-09 01:23:29 +04:00
|
|
|
String Buffer::string(const BufferIterator& begin, const BufferIterator& end) const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
String res;
|
2012-08-23 01:33:52 +04:00
|
|
|
for (LineCount line = begin.line(); line <= end.line(); ++line)
|
2012-03-30 15:37:18 +04:00
|
|
|
{
|
2012-10-11 02:41:48 +04:00
|
|
|
ByteCount start = 0;
|
2012-03-30 15:37:18 +04:00
|
|
|
if (line == begin.line())
|
|
|
|
start = begin.column();
|
2012-10-11 02:41:48 +04:00
|
|
|
ByteCount count = -1;
|
2012-03-30 15:37:18 +04:00
|
|
|
if (line == end.line())
|
|
|
|
count = end.column() - start;
|
|
|
|
res += m_lines[line].content.substr(start, count);
|
|
|
|
}
|
|
|
|
return res;
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2013-02-20 17:20:16 +04:00
|
|
|
void Buffer::commit_undo_group()
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2012-11-21 16:43:10 +04:00
|
|
|
if (m_flags & Flags::NoUndo)
|
|
|
|
return;
|
|
|
|
|
2012-06-04 18:12:37 +04:00
|
|
|
if (m_current_undo_group.empty())
|
|
|
|
return;
|
|
|
|
|
2013-02-20 17:20:16 +04:00
|
|
|
m_history.erase(m_history_cursor, m_history.end());
|
|
|
|
|
2012-06-28 16:26:01 +04:00
|
|
|
m_history.push_back(std::move(m_current_undo_group));
|
2013-02-20 17:20:16 +04:00
|
|
|
m_current_undo_group.clear();
|
2011-09-06 22:49:32 +04:00
|
|
|
m_history_cursor = m_history.end();
|
|
|
|
|
2013-02-20 17:20:16 +04:00
|
|
|
if (m_history.size() < m_last_save_undo_index)
|
|
|
|
m_last_save_undo_index = -1;
|
2011-09-06 22:49:32 +04:00
|
|
|
}
|
|
|
|
|
2012-08-10 21:12:43 +04:00
|
|
|
// A Modification holds a single atomic modification to Buffer
|
|
|
|
struct Buffer::Modification
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2012-08-10 21:12:43 +04:00
|
|
|
enum Type { Insert, Erase };
|
|
|
|
|
|
|
|
Type type;
|
|
|
|
BufferIterator position;
|
|
|
|
String content;
|
|
|
|
|
|
|
|
Modification(Type type, BufferIterator position, String content)
|
|
|
|
: type(type), position(position), content(std::move(content)) {}
|
|
|
|
|
|
|
|
Modification inverse() const
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2013-02-28 21:51:24 +04:00
|
|
|
Type inverse_type = Insert;
|
2012-08-10 21:12:43 +04:00
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case Insert: inverse_type = Erase; break;
|
|
|
|
case Erase: inverse_type = Insert; break;
|
2013-04-09 22:04:11 +04:00
|
|
|
default: kak_assert(false);
|
2012-08-10 21:12:43 +04:00
|
|
|
}
|
2013-01-23 16:47:45 +04:00
|
|
|
return {inverse_type, position, content};
|
2011-09-06 22:49:32 +04:00
|
|
|
}
|
2012-08-10 21:12:43 +04:00
|
|
|
};
|
2011-09-06 22:49:32 +04:00
|
|
|
|
|
|
|
bool Buffer::undo()
|
|
|
|
{
|
2013-02-20 17:23:52 +04:00
|
|
|
commit_undo_group();
|
|
|
|
|
2011-09-06 22:49:32 +04:00
|
|
|
if (m_history_cursor == m_history.begin())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
--m_history_cursor;
|
|
|
|
|
2011-12-06 22:58:43 +04:00
|
|
|
for (const Modification& modification : reversed(*m_history_cursor))
|
2011-10-18 04:55:45 +04:00
|
|
|
apply_modification(modification.inverse());
|
2012-06-05 17:33:02 +04:00
|
|
|
return true;
|
2011-09-06 22:49:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Buffer::redo()
|
|
|
|
{
|
|
|
|
if (m_history_cursor == m_history.end())
|
|
|
|
return false;
|
|
|
|
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(m_current_undo_group.empty());
|
2013-02-20 17:23:52 +04:00
|
|
|
|
2011-12-06 22:58:43 +04:00
|
|
|
for (const Modification& modification : *m_history_cursor)
|
2011-10-18 04:55:45 +04:00
|
|
|
apply_modification(modification);
|
2011-09-06 22:49:32 +04:00
|
|
|
|
|
|
|
++m_history_cursor;
|
2012-06-05 17:33:02 +04:00
|
|
|
return true;
|
2011-09-06 22:49:32 +04:00
|
|
|
}
|
|
|
|
|
2012-03-30 15:37:18 +04:00
|
|
|
void Buffer::check_invariant() const
|
|
|
|
{
|
2013-02-27 22:02:01 +04:00
|
|
|
#ifdef KAK_DEBUG
|
2012-10-11 02:41:48 +04:00
|
|
|
ByteCount start = 0;
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(not m_lines.empty());
|
2012-03-30 15:37:18 +04:00
|
|
|
for (auto& line : m_lines)
|
|
|
|
{
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(line.start == start);
|
|
|
|
kak_assert(line.length() > 0);
|
|
|
|
kak_assert(line.content.back() == '\n');
|
2012-03-30 16:00:40 +04:00
|
|
|
start += line.length();
|
2012-03-30 15:37:18 +04:00
|
|
|
}
|
2013-02-27 22:02:01 +04:00
|
|
|
#endif
|
2012-03-30 15:37:18 +04:00
|
|
|
}
|
|
|
|
|
2012-08-10 21:12:43 +04:00
|
|
|
void Buffer::do_insert(const BufferIterator& pos, const String& content)
|
2012-02-23 01:54:25 +04:00
|
|
|
{
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(pos.is_valid());
|
2013-03-18 22:39:32 +04:00
|
|
|
|
|
|
|
if (content.empty())
|
|
|
|
return;
|
|
|
|
|
2012-08-15 19:07:53 +04:00
|
|
|
++m_timestamp;
|
2012-10-11 02:41:48 +04:00
|
|
|
ByteCount offset = pos.offset();
|
2012-03-06 18:27:03 +04:00
|
|
|
|
2012-03-30 15:37:18 +04:00
|
|
|
// all following lines advanced by length
|
2012-08-23 01:33:52 +04:00
|
|
|
for (LineCount i = pos.line()+1; i < line_count(); ++i)
|
2012-03-30 15:37:18 +04:00
|
|
|
m_lines[i].start += content.length();
|
2012-02-23 01:54:25 +04:00
|
|
|
|
2012-07-16 23:51:37 +04:00
|
|
|
BufferIterator begin_it;
|
|
|
|
BufferIterator end_it;
|
2013-01-23 21:52:42 +04:00
|
|
|
// if we inserted at the end of the buffer, we have created a new
|
2012-03-30 15:37:18 +04:00
|
|
|
// line without inserting a '\n'
|
2013-01-23 21:52:42 +04:00
|
|
|
if (pos.is_end())
|
2012-02-23 01:54:25 +04:00
|
|
|
{
|
2012-10-11 02:41:48 +04:00
|
|
|
ByteCount start = 0;
|
|
|
|
for (ByteCount i = 0; i < content.length(); ++i)
|
2012-02-23 01:54:25 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
if (content[i] == '\n')
|
|
|
|
{
|
|
|
|
m_lines.push_back({ offset + start, content.substr(start, i + 1 - start) });
|
|
|
|
start = i + 1;
|
|
|
|
}
|
2012-02-23 01:54:25 +04:00
|
|
|
}
|
2012-03-30 15:37:18 +04:00
|
|
|
if (start != content.length())
|
|
|
|
m_lines.push_back({ offset + start, content.substr(start) });
|
2012-04-04 17:56:19 +04:00
|
|
|
|
2013-01-24 17:08:24 +04:00
|
|
|
begin_it = pos.column() == 0 ? pos : BufferIterator{*this, { pos.line() + 1, 0 }};
|
2012-07-16 23:51:37 +04:00
|
|
|
end_it = end();
|
2012-02-23 01:54:25 +04:00
|
|
|
}
|
2012-03-30 15:37:18 +04:00
|
|
|
else
|
2012-02-23 01:54:25 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
String prefix = m_lines[pos.line()].content.substr(0, pos.column());
|
|
|
|
String suffix = m_lines[pos.line()].content.substr(pos.column());
|
|
|
|
|
2013-03-18 22:39:32 +04:00
|
|
|
std::vector<Line> new_lines;
|
2012-02-23 01:54:25 +04:00
|
|
|
|
2012-10-11 02:41:48 +04:00
|
|
|
ByteCount start = 0;
|
|
|
|
for (ByteCount i = 0; i < content.length(); ++i)
|
2012-02-23 01:54:25 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
if (content[i] == '\n')
|
|
|
|
{
|
|
|
|
String line_content = content.substr(start, i + 1 - start);
|
|
|
|
if (start == 0)
|
|
|
|
{
|
|
|
|
line_content = prefix + line_content;
|
2013-03-18 22:39:32 +04:00
|
|
|
new_lines.push_back({ offset + start - prefix.length(),
|
|
|
|
std::move(line_content) });
|
2012-03-30 15:37:18 +04:00
|
|
|
}
|
|
|
|
else
|
2013-03-18 22:39:32 +04:00
|
|
|
new_lines.push_back({ offset + start, std::move(line_content) });
|
2012-03-30 15:37:18 +04:00
|
|
|
start = i + 1;
|
|
|
|
}
|
2012-02-23 01:54:25 +04:00
|
|
|
}
|
2012-03-30 15:37:18 +04:00
|
|
|
if (start == 0)
|
2013-03-18 22:39:32 +04:00
|
|
|
new_lines.push_back({ offset + start - prefix.length(), prefix + content + suffix });
|
2012-08-10 16:22:57 +04:00
|
|
|
else if (start != content.length() or not suffix.empty())
|
2013-03-18 22:39:32 +04:00
|
|
|
new_lines.push_back({ offset + start, content.substr(start) + suffix });
|
|
|
|
|
|
|
|
LineCount last_line = pos.line() + new_lines.size() - 1;
|
|
|
|
|
|
|
|
auto line_it = m_lines.begin() + (int)pos.line();
|
|
|
|
*line_it = std::move(*new_lines.begin());
|
|
|
|
m_lines.insert(line_it+1, std::make_move_iterator(new_lines.begin() + 1),
|
|
|
|
std::make_move_iterator(new_lines.end()));
|
2012-04-04 17:56:19 +04:00
|
|
|
|
2012-07-16 23:51:37 +04:00
|
|
|
begin_it = pos;
|
2013-03-18 22:39:32 +04:00
|
|
|
end_it = BufferIterator{*this, { last_line, m_lines[last_line].length() - suffix.length() }};
|
2012-02-23 01:54:25 +04:00
|
|
|
}
|
2012-03-30 15:37:18 +04:00
|
|
|
|
2012-07-16 23:51:37 +04:00
|
|
|
for (auto listener : m_change_listeners)
|
|
|
|
listener->on_insert(begin_it, end_it);
|
2012-03-30 15:37:18 +04:00
|
|
|
}
|
|
|
|
|
2012-10-08 21:25:17 +04:00
|
|
|
void Buffer::do_erase(const BufferIterator& begin, const BufferIterator& end)
|
2012-03-30 15:37:18 +04:00
|
|
|
{
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(begin.is_valid());
|
|
|
|
kak_assert(end.is_valid());
|
2012-08-15 19:07:53 +04:00
|
|
|
++m_timestamp;
|
2012-10-11 02:41:48 +04:00
|
|
|
const ByteCount length = end - begin;
|
2012-10-08 21:25:17 +04:00
|
|
|
String prefix = m_lines[begin.line()].content.substr(0, begin.column());
|
2012-03-30 15:37:18 +04:00
|
|
|
String suffix = m_lines[end.line()].content.substr(end.column());
|
2012-10-08 21:25:17 +04:00
|
|
|
Line new_line = { m_lines[begin.line()].start, prefix + suffix };
|
2012-03-30 15:37:18 +04:00
|
|
|
|
2012-08-24 01:56:35 +04:00
|
|
|
if (new_line.length() != 0)
|
2013-03-15 17:15:29 +04:00
|
|
|
{
|
|
|
|
m_lines.erase(m_lines.begin() + (int)begin.line(), m_lines.begin() + (int)end.line());
|
|
|
|
m_lines[begin.line()] = std::move(new_line);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_lines.erase(m_lines.begin() + (int)begin.line(), m_lines.begin() + (int)end.line() + 1);
|
2012-03-30 15:37:18 +04:00
|
|
|
|
2012-10-08 21:25:17 +04:00
|
|
|
for (LineCount i = begin.line()+1; i < line_count(); ++i)
|
2012-03-30 15:37:18 +04:00
|
|
|
m_lines[i].start -= length;
|
|
|
|
|
2012-07-16 23:51:37 +04:00
|
|
|
for (auto listener : m_change_listeners)
|
2012-10-08 21:25:17 +04:00
|
|
|
listener->on_erase(begin, end);
|
2012-02-23 01:54:25 +04:00
|
|
|
}
|
|
|
|
|
2011-12-06 22:58:43 +04:00
|
|
|
void Buffer::apply_modification(const Modification& modification)
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
const String& content = modification.content;
|
2013-01-23 17:27:21 +04:00
|
|
|
BufferIterator pos = modification.position;
|
|
|
|
|
|
|
|
// this may happen when a modification applied at the
|
|
|
|
// end of the buffer has been inverted for an undo.
|
2013-01-28 16:45:32 +04:00
|
|
|
if (not pos.is_end() and pos.column() == m_lines[pos.line()].length())
|
2013-01-23 17:27:21 +04:00
|
|
|
pos = { pos.buffer(), { pos.line() + 1, 0 }};
|
2012-03-30 15:37:18 +04:00
|
|
|
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(pos.is_valid());
|
2011-09-06 22:49:32 +04:00
|
|
|
switch (modification.type)
|
|
|
|
{
|
2011-12-06 22:58:43 +04:00
|
|
|
case Modification::Insert:
|
2012-04-04 16:25:42 +04:00
|
|
|
{
|
2013-01-23 21:52:42 +04:00
|
|
|
do_insert(pos, content);
|
2011-09-06 22:49:32 +04:00
|
|
|
break;
|
2012-04-04 16:25:42 +04:00
|
|
|
}
|
2011-12-06 22:58:43 +04:00
|
|
|
case Modification::Erase:
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2012-10-11 03:17:29 +04:00
|
|
|
ByteCount count = content.length();
|
|
|
|
BufferIterator end = pos + count;
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(string(pos, end) == content);
|
2012-10-11 03:17:29 +04:00
|
|
|
do_erase(pos, end);
|
2011-09-06 22:49:32 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(false);
|
2011-09-06 22:49:32 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-10 21:26:17 +04:00
|
|
|
void Buffer::insert(BufferIterator pos, String content)
|
2012-08-10 21:12:43 +04:00
|
|
|
{
|
|
|
|
if (content.empty())
|
|
|
|
return;
|
2012-08-14 16:13:10 +04:00
|
|
|
|
2012-09-10 21:26:17 +04:00
|
|
|
if (pos.is_end() and content.back() != '\n')
|
|
|
|
content += '\n';
|
2012-08-14 16:13:10 +04:00
|
|
|
|
2012-11-21 16:43:10 +04:00
|
|
|
if (not (m_flags & Flags::NoUndo))
|
|
|
|
m_current_undo_group.emplace_back(Modification::Insert, pos, content);
|
|
|
|
do_insert(pos, content);
|
2012-08-10 21:12:43 +04:00
|
|
|
}
|
|
|
|
|
2012-08-14 16:13:10 +04:00
|
|
|
void Buffer::erase(BufferIterator begin, BufferIterator end)
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2012-08-14 16:13:10 +04:00
|
|
|
if (end.is_end() and (begin.column() != 0 or begin.is_begin()))
|
|
|
|
--end;
|
|
|
|
|
2012-08-10 21:12:43 +04:00
|
|
|
if (begin == end)
|
2012-03-13 01:31:27 +04:00
|
|
|
return;
|
|
|
|
|
2012-11-21 16:43:10 +04:00
|
|
|
if (not (m_flags & Flags::NoUndo))
|
|
|
|
m_current_undo_group.emplace_back(Modification::Erase, begin,
|
|
|
|
string(begin, end));
|
2012-10-08 21:25:17 +04:00
|
|
|
do_erase(begin, end);
|
2011-09-06 22:49:32 +04:00
|
|
|
}
|
|
|
|
|
2011-10-05 18:21:24 +04:00
|
|
|
bool Buffer::is_modified() const
|
|
|
|
{
|
2011-11-03 17:44:02 +04:00
|
|
|
size_t history_cursor_index = m_history_cursor - m_history.begin();
|
|
|
|
return m_last_save_undo_index != history_cursor_index
|
2011-10-05 18:21:24 +04:00
|
|
|
or not m_current_undo_group.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Buffer::notify_saved()
|
|
|
|
{
|
2012-12-19 21:56:20 +04:00
|
|
|
if (not m_current_undo_group.empty())
|
2013-02-20 17:20:16 +04:00
|
|
|
commit_undo_group();
|
2012-12-19 21:56:20 +04:00
|
|
|
|
2012-12-28 16:52:19 +04:00
|
|
|
m_flags &= ~Flags::New;
|
2011-11-03 17:44:02 +04:00
|
|
|
size_t history_cursor_index = m_history_cursor - m_history.begin();
|
2012-11-23 16:41:07 +04:00
|
|
|
if (m_last_save_undo_index != history_cursor_index)
|
|
|
|
{
|
|
|
|
++m_timestamp;
|
|
|
|
m_last_save_undo_index = history_cursor_index;
|
|
|
|
}
|
2011-10-05 18:21:24 +04:00
|
|
|
}
|
|
|
|
|
2013-04-23 20:46:18 +04:00
|
|
|
bool Buffer::is_valid(const BufferCoord& c) const
|
|
|
|
{
|
|
|
|
return (c.line < line_count() and c.column < m_lines[c.line].length()) or
|
|
|
|
(c.line == line_count() - 1 and c.column == m_lines.back().length()) or
|
|
|
|
(c.line == line_count() and c.column == 0);
|
|
|
|
}
|
|
|
|
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|