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"
|
2014-10-01 03:20:12 +04:00
|
|
|
#include "client.hh"
|
2014-12-23 16:34:21 +03:00
|
|
|
#include "containers.hh"
|
2012-01-23 17:56:43 +04:00
|
|
|
#include "context.hh"
|
2013-03-25 22:58:23 +04:00
|
|
|
#include "file.hh"
|
2015-01-15 16:54:38 +03:00
|
|
|
#include "shared_string.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
|
|
|
|
{
|
|
|
|
|
2015-01-22 16:39:29 +03:00
|
|
|
Buffer::Buffer(String name, Flags flags, BufferLines lines,
|
2013-10-17 21:47:09 +04:00
|
|
|
time_t fs_timestamp)
|
2014-10-30 17:00:42 +03:00
|
|
|
: Scope(GlobalScope::instance()),
|
|
|
|
m_name(flags & Flags::File ? real_path(parse_filename(name)) : std::move(name)),
|
2013-11-08 01:42:51 +04:00
|
|
|
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),
|
2014-10-30 17:00:42 +03:00
|
|
|
m_fs_timestamp(fs_timestamp)
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2012-08-08 21:36:40 +04:00
|
|
|
BufferManager::instance().register_buffer(*this);
|
2014-10-30 17:00:42 +03:00
|
|
|
options().register_watcher(*this);
|
2012-11-23 21:42:07 +04:00
|
|
|
|
2012-11-27 16:39:35 +04:00
|
|
|
if (lines.empty())
|
2015-01-22 16:39:29 +03:00
|
|
|
lines.emplace_back(StringStorage::create("\n"));
|
2012-11-27 16:39:35 +04:00
|
|
|
|
2012-11-23 21:42:07 +04:00
|
|
|
for (auto& line : lines)
|
|
|
|
{
|
2015-01-26 01:36:05 +03:00
|
|
|
kak_assert(not line->length == 0 and line->data()[line->length-1] == '\n');
|
2012-11-23 21:42:07 +04:00
|
|
|
}
|
2015-01-22 16:39:29 +03:00
|
|
|
static_cast<BufferLines&>(m_lines) = std::move(lines);
|
2012-08-14 16:13:10 +04:00
|
|
|
|
2014-05-11 16:20:13 +04:00
|
|
|
m_changes.push_back({ Change::Insert, {0,0}, line_count(), true });
|
2014-05-11 15:20:59 +04:00
|
|
|
|
2013-04-12 21:11:28 +04:00
|
|
|
if (flags & Flags::File)
|
|
|
|
{
|
|
|
|
if (flags & Flags::New)
|
2013-12-11 17:57:10 +04:00
|
|
|
run_hook_in_own_context("BufNew", m_name);
|
2013-04-12 21:11:28 +04:00
|
|
|
else
|
2013-10-17 21:47:09 +04:00
|
|
|
{
|
|
|
|
kak_assert(m_fs_timestamp != InvalidTime);
|
2013-12-11 17:57:10 +04:00
|
|
|
run_hook_in_own_context("BufOpen", m_name);
|
2013-10-17 21:47:09 +04:00
|
|
|
}
|
2013-04-12 21:11:28 +04:00
|
|
|
}
|
2012-06-12 22:27:57 +04:00
|
|
|
|
2013-12-11 17:57:10 +04:00
|
|
|
run_hook_in_own_context("BufCreate", m_name);
|
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;
|
2013-11-13 00:36:42 +04:00
|
|
|
|
2014-10-30 17:00:42 +03:00
|
|
|
for (auto& option : options().flatten_options())
|
2013-11-13 00:36:42 +04:00
|
|
|
on_option_changed(*option);
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2011-10-24 18:23:13 +04:00
|
|
|
Buffer::~Buffer()
|
|
|
|
{
|
2013-12-11 17:57:10 +04:00
|
|
|
run_hook_in_own_context("BufClose", m_name);
|
2012-08-05 22:12:43 +04:00
|
|
|
|
2014-10-30 17:00:42 +03:00
|
|
|
options().unregister_watcher(*this);
|
2012-08-08 21:36:40 +04:00
|
|
|
BufferManager::instance().unregister_buffer(*this);
|
2014-01-11 23:05:09 +04:00
|
|
|
m_values.clear();
|
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;
|
|
|
|
}
|
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
BufferIterator Buffer::iterator_at(ByteCoord coord) const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2013-06-03 20:56:48 +04:00
|
|
|
return is_end(coord) ? end() : BufferIterator(*this, clamp(coord));
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord Buffer::clamp(ByteCoord coord) const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
|
|
|
if (m_lines.empty())
|
2014-05-07 22:51:01 +04:00
|
|
|
return ByteCoord{};
|
2011-09-02 20:51:20 +04:00
|
|
|
|
2013-05-30 16:17:19 +04:00
|
|
|
coord.line = Kakoune::clamp(coord.line, 0_line, line_count() - 1);
|
2013-06-05 20:41:02 +04:00
|
|
|
ByteCount max_col = std::max(0_byte, m_lines[coord.line].length() - 1);
|
2013-05-30 16:17:19 +04:00
|
|
|
coord.column = Kakoune::clamp(coord.column, 0_byte, max_col);
|
|
|
|
return coord;
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord Buffer::offset_coord(ByteCoord coord, CharCount offset)
|
2013-12-15 18:14:52 +04:00
|
|
|
{
|
2015-01-19 22:31:56 +03:00
|
|
|
StringView line = m_lines[coord.line];
|
2013-12-15 18:14:52 +04:00
|
|
|
auto character = std::max(0_char, std::min(line.char_count_to(coord.column) + offset,
|
|
|
|
line.char_length() - 1));
|
|
|
|
return {coord.line, line.byte_count_to(character)};
|
|
|
|
}
|
|
|
|
|
2014-09-09 22:35:54 +04:00
|
|
|
ByteCoordAndTarget Buffer::offset_coord(ByteCoordAndTarget coord, LineCount offset)
|
2013-12-15 18:14:52 +04:00
|
|
|
{
|
2014-09-09 22:35:54 +04:00
|
|
|
auto character = coord.target == -1 ? m_lines[coord.line].char_count_to(coord.column) : coord.target;
|
2013-12-15 18:14:52 +04:00
|
|
|
auto line = Kakoune::clamp(coord.line + offset, 0_line, line_count()-1);
|
2015-01-19 22:31:56 +03:00
|
|
|
StringView content = m_lines[line];
|
2013-12-15 18:14:52 +04:00
|
|
|
|
|
|
|
character = std::max(0_char, std::min(character, content.char_length() - 2));
|
2014-09-09 22:35:54 +04:00
|
|
|
return {line, content.byte_count_to(character), character};
|
2013-12-15 18:14:52 +04:00
|
|
|
}
|
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
String Buffer::string(ByteCoord begin, ByteCoord end) const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
String res;
|
2013-06-28 01:49:34 +04:00
|
|
|
for (auto line = begin.line; line <= end.line and line < line_count(); ++line)
|
2012-03-30 15:37:18 +04:00
|
|
|
{
|
2013-06-28 01:49:34 +04:00
|
|
|
ByteCount start = 0;
|
|
|
|
if (line == begin.line)
|
|
|
|
start = begin.column;
|
|
|
|
ByteCount count = -1;
|
|
|
|
if (line == end.line)
|
|
|
|
count = end.column - start;
|
2014-05-24 20:08:01 +04:00
|
|
|
res += m_lines[line].substr(start, count);
|
2012-03-30 15:37:18 +04:00
|
|
|
}
|
|
|
|
return res;
|
2011-09-02 20:51:20 +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 };
|
|
|
|
|
2014-05-10 19:25:07 +04:00
|
|
|
Type type;
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord coord;
|
2015-01-15 16:54:38 +03:00
|
|
|
SharedString content;
|
2012-08-10 21:12:43 +04:00
|
|
|
|
2015-01-15 16:54:38 +03:00
|
|
|
Modification(Type type, ByteCoord coord, SharedString content)
|
2013-04-26 20:45:24 +04:00
|
|
|
: type(type), coord(coord), content(std::move(content)) {}
|
2012-08-10 21:12:43 +04:00
|
|
|
|
|
|
|
Modification inverse() const
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2014-05-10 19:25:07 +04:00
|
|
|
return {type == Insert ? Erase : Insert, coord, 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
|
|
|
|
2015-01-22 16:39:29 +03:00
|
|
|
void Buffer::reload(BufferLines lines, time_t fs_timestamp)
|
2014-10-24 00:04:05 +04:00
|
|
|
{
|
|
|
|
m_changes.push_back({ Change::Erase, {0,0}, back_coord(), true });
|
|
|
|
|
|
|
|
commit_undo_group();
|
|
|
|
if (not (m_flags & Flags::NoUndo))
|
|
|
|
{
|
|
|
|
for (auto line = line_count()-1; line >= 0; --line)
|
|
|
|
m_current_undo_group.emplace_back(
|
|
|
|
Modification::Erase, line, m_lines[line]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lines.empty())
|
2015-01-22 16:39:29 +03:00
|
|
|
lines.emplace_back(StringStorage::create("\n"));
|
2014-10-24 00:04:05 +04:00
|
|
|
|
|
|
|
for (auto& line : lines)
|
|
|
|
{
|
2015-01-26 01:36:05 +03:00
|
|
|
kak_assert(not line->length == 0 and line->data()[line->length-1] == '\n');
|
2014-10-24 00:04:05 +04:00
|
|
|
if (not (m_flags & Flags::NoUndo))
|
|
|
|
m_current_undo_group.emplace_back(
|
|
|
|
Modification::Insert, line_count()-1, m_lines.back());
|
|
|
|
}
|
2015-01-22 16:39:29 +03:00
|
|
|
static_cast<BufferLines&>(m_lines) = std::move(lines);
|
|
|
|
|
2014-10-24 00:04:05 +04:00
|
|
|
commit_undo_group();
|
2014-11-01 22:35:27 +03:00
|
|
|
|
|
|
|
m_last_save_undo_index = m_history_cursor - m_history.begin();
|
2014-10-24 00:04:05 +04:00
|
|
|
m_fs_timestamp = fs_timestamp;
|
|
|
|
|
|
|
|
m_changes.push_back({ Change::Insert, {0,0}, back_coord(), true });
|
|
|
|
}
|
2013-04-25 16:03:55 +04:00
|
|
|
|
|
|
|
void Buffer::commit_undo_group()
|
|
|
|
{
|
|
|
|
if (m_flags & Flags::NoUndo)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (m_current_undo_group.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_history.erase(m_history_cursor, m_history.end());
|
|
|
|
|
|
|
|
m_history.push_back(std::move(m_current_undo_group));
|
|
|
|
m_current_undo_group.clear();
|
|
|
|
m_history_cursor = m_history.end();
|
|
|
|
|
|
|
|
if (m_history.size() < m_last_save_undo_index)
|
|
|
|
m_last_save_undo_index = -1;
|
|
|
|
}
|
|
|
|
|
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
|
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)
|
|
|
|
{
|
2015-01-19 22:31:56 +03:00
|
|
|
kak_assert(line->strview().length() > 0);
|
|
|
|
kak_assert(line->strview().back() == '\n');
|
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
|
|
|
}
|
|
|
|
|
2014-10-01 03:20:12 +04:00
|
|
|
ByteCoord Buffer::do_insert(ByteCoord pos, StringView content)
|
2012-02-23 01:54:25 +04:00
|
|
|
{
|
2013-05-22 21:21:59 +04:00
|
|
|
kak_assert(is_valid(pos));
|
2013-03-18 22:39:32 +04:00
|
|
|
|
|
|
|
if (content.empty())
|
2013-06-06 21:39:53 +04:00
|
|
|
return pos;
|
2013-03-18 22:39:32 +04:00
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord begin;
|
|
|
|
ByteCoord end;
|
2014-05-11 16:20:13 +04:00
|
|
|
bool at_end = false;
|
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-05-22 21:21:59 +04:00
|
|
|
if (is_end(pos))
|
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')
|
|
|
|
{
|
2015-01-19 22:31:56 +03:00
|
|
|
m_lines.push_back(StringStorage::create(content.substr(start, i + 1 - start)));
|
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 != content.length())
|
2015-01-19 22:31:56 +03:00
|
|
|
m_lines.push_back(StringStorage::create(content.substr(start)));
|
2012-04-04 17:56:19 +04:00
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
begin = pos.column == 0 ? pos : ByteCoord{ pos.line + 1, 0 };
|
2014-05-24 20:08:01 +04:00
|
|
|
end = ByteCoord{ line_count(), 0 };
|
2014-05-11 16:20:13 +04:00
|
|
|
at_end = true;
|
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
|
|
|
{
|
2014-10-03 16:39:13 +04:00
|
|
|
StringView prefix = m_lines[pos.line].substr(0, pos.column);
|
|
|
|
StringView suffix = m_lines[pos.line].substr(pos.column);
|
2012-03-30 15:37:18 +04:00
|
|
|
|
2015-01-19 22:31:56 +03:00
|
|
|
LineList 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')
|
|
|
|
{
|
2014-10-03 16:39:13 +04:00
|
|
|
StringView line_content = content.substr(start, i + 1 - start);
|
2012-03-30 15:37:18 +04:00
|
|
|
if (start == 0)
|
2015-01-19 22:31:56 +03:00
|
|
|
new_lines.emplace_back(StringStorage::create(prefix + line_content));
|
2012-03-30 15:37:18 +04:00
|
|
|
else
|
2015-01-19 22:31:56 +03:00
|
|
|
new_lines.push_back(StringStorage::create(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)
|
2015-01-19 22:31:56 +03:00
|
|
|
new_lines.emplace_back(StringStorage::create(prefix + content + suffix));
|
2012-08-10 16:22:57 +04:00
|
|
|
else if (start != content.length() or not suffix.empty())
|
2015-01-19 22:31:56 +03:00
|
|
|
new_lines.emplace_back(StringStorage::create(content.substr(start) + suffix));
|
2013-03-18 22:39:32 +04:00
|
|
|
|
2013-05-22 21:21:59 +04:00
|
|
|
LineCount last_line = pos.line + new_lines.size() - 1;
|
2013-03-18 22:39:32 +04:00
|
|
|
|
2013-05-22 21:21:59 +04:00
|
|
|
auto line_it = m_lines.begin() + (int)pos.line;
|
2013-03-18 22:39:32 +04:00
|
|
|
*line_it = std::move(*new_lines.begin());
|
2014-01-07 01:04:09 +04:00
|
|
|
|
2013-03-18 22:39:32 +04:00
|
|
|
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
|
|
|
|
2013-05-22 21:21:59 +04:00
|
|
|
begin = pos;
|
2014-05-07 22:51:01 +04:00
|
|
|
end = ByteCoord{ 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
|
|
|
|
2014-05-11 16:20:13 +04:00
|
|
|
m_changes.push_back({ Change::Insert, begin, end, at_end });
|
2013-06-06 21:39:53 +04:00
|
|
|
return begin;
|
2012-03-30 15:37:18 +04:00
|
|
|
}
|
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord Buffer::do_erase(ByteCoord begin, ByteCoord end)
|
2012-03-30 15:37:18 +04:00
|
|
|
{
|
2013-05-22 21:21:59 +04:00
|
|
|
kak_assert(is_valid(begin));
|
|
|
|
kak_assert(is_valid(end));
|
2014-06-26 22:01:39 +04:00
|
|
|
StringView prefix = m_lines[begin.line].substr(0, begin.column);
|
|
|
|
StringView suffix = m_lines[end.line].substr(end.column);
|
2014-05-24 20:08:01 +04:00
|
|
|
String new_line = prefix + suffix;
|
2012-03-30 15:37:18 +04:00
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord next;
|
2012-08-24 01:56:35 +04:00
|
|
|
if (new_line.length() != 0)
|
2013-03-15 17:15:29 +04:00
|
|
|
{
|
2013-05-22 21:21:59 +04:00
|
|
|
m_lines.erase(m_lines.begin() + (int)begin.line, m_lines.begin() + (int)end.line);
|
2015-01-19 22:31:56 +03:00
|
|
|
m_lines.get_storage(begin.line) = StringStorage::create(new_line);
|
2013-06-06 21:39:53 +04:00
|
|
|
next = begin;
|
2013-03-15 17:15:29 +04:00
|
|
|
}
|
|
|
|
else
|
2013-06-06 21:39:53 +04:00
|
|
|
{
|
2013-05-22 21:21:59 +04:00
|
|
|
m_lines.erase(m_lines.begin() + (int)begin.line, m_lines.begin() + (int)end.line + 1);
|
2014-05-07 22:51:01 +04:00
|
|
|
next = is_end(begin) ? end_coord() : ByteCoord{begin.line, 0};
|
2013-06-06 21:39:53 +04:00
|
|
|
}
|
2012-03-30 15:37:18 +04:00
|
|
|
|
2014-05-11 16:20:13 +04:00
|
|
|
m_changes.push_back({ Change::Erase, begin, end, is_end(begin) });
|
2013-06-06 21:39:53 +04:00
|
|
|
return next;
|
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
|
|
|
{
|
2014-10-01 03:20:12 +04:00
|
|
|
StringView content = modification.content;
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord coord = modification.coord;
|
2012-03-30 15:37:18 +04:00
|
|
|
|
2013-05-22 21:21:59 +04:00
|
|
|
kak_assert(is_valid(coord));
|
2013-06-06 22:02:20 +04:00
|
|
|
// in modifications, end coords should be {line_count(), 0}
|
2014-10-29 00:55:08 +03:00
|
|
|
kak_assert((m_lines.empty() and coord == ByteCoord{0,0} ) or
|
|
|
|
coord != ByteCoord(line_count()-1, m_lines.back().length()));
|
|
|
|
|
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-05-22 21:21:59 +04:00
|
|
|
do_insert(coord, 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();
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord end = advance(coord, count);
|
2013-05-23 16:17:25 +04:00
|
|
|
kak_assert(string(coord, end) == content);
|
2013-05-22 21:21:59 +04:00
|
|
|
do_erase(coord, 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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-03 16:39:13 +04:00
|
|
|
BufferIterator Buffer::insert(const BufferIterator& pos, StringView content)
|
2012-08-10 21:12:43 +04:00
|
|
|
{
|
2013-06-06 21:39:53 +04:00
|
|
|
kak_assert(is_valid(pos.coord()));
|
2012-08-10 21:12:43 +04:00
|
|
|
if (content.empty())
|
2013-06-06 21:39:53 +04:00
|
|
|
return pos;
|
2012-08-14 16:13:10 +04:00
|
|
|
|
2015-01-15 16:54:38 +03:00
|
|
|
SharedString real_content;
|
2013-06-06 21:39:53 +04:00
|
|
|
if (pos == end() and content.back() != '\n')
|
2015-01-15 16:54:38 +03:00
|
|
|
real_content = intern(content + "\n");
|
2014-10-03 16:39:13 +04:00
|
|
|
else
|
2015-01-15 16:54:38 +03:00
|
|
|
real_content = intern(content);
|
2012-08-14 16:13:10 +04:00
|
|
|
|
2013-06-06 22:02:20 +04:00
|
|
|
// for undo and redo purpose it is better to use one past last line rather
|
|
|
|
// than one past last char coord.
|
2014-05-07 22:51:01 +04:00
|
|
|
auto coord = pos == end() ? ByteCoord{line_count()} : pos.coord();
|
2012-11-21 16:43:10 +04:00
|
|
|
if (not (m_flags & Flags::NoUndo))
|
2014-10-03 16:39:13 +04:00
|
|
|
m_current_undo_group.emplace_back(Modification::Insert, coord, real_content);
|
2014-12-19 02:13:45 +03:00
|
|
|
return {*this, do_insert(pos.coord(), real_content)};
|
2012-08-10 21:12:43 +04:00
|
|
|
}
|
|
|
|
|
2013-06-06 21:39:53 +04:00
|
|
|
BufferIterator Buffer::erase(BufferIterator begin, BufferIterator end)
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2013-06-06 21:39:53 +04:00
|
|
|
// do not erase last \n except if we erase from the start of a line
|
|
|
|
if (end == this->end() and (begin.coord().column != 0 or begin == this->begin()))
|
|
|
|
--end;
|
2012-08-14 16:13:10 +04:00
|
|
|
|
2012-08-10 21:12:43 +04:00
|
|
|
if (begin == end)
|
2013-06-06 21:39:53 +04:00
|
|
|
return begin;
|
2012-03-13 01:31:27 +04:00
|
|
|
|
2012-11-21 16:43:10 +04:00
|
|
|
if (not (m_flags & Flags::NoUndo))
|
2013-06-06 21:39:53 +04:00
|
|
|
m_current_undo_group.emplace_back(Modification::Erase, begin.coord(),
|
2015-01-15 16:54:38 +03:00
|
|
|
intern(string(begin.coord(), end.coord())));
|
2014-12-19 02:13:45 +03:00
|
|
|
return {*this, do_erase(begin.coord(), end.coord())};
|
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_last_save_undo_index = history_cursor_index;
|
2013-10-17 21:47:09 +04:00
|
|
|
m_fs_timestamp = get_fs_timestamp(m_name);
|
2011-10-05 18:21:24 +04:00
|
|
|
}
|
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord Buffer::advance(ByteCoord coord, ByteCount count) const
|
2013-05-22 20:59:55 +04:00
|
|
|
{
|
2014-05-24 20:08:01 +04:00
|
|
|
if (count > 0)
|
|
|
|
{
|
|
|
|
auto line = coord.line;
|
|
|
|
count += coord.column;
|
|
|
|
while (count >= m_lines[line].length())
|
|
|
|
{
|
|
|
|
count -= m_lines[line++].length();
|
|
|
|
if (line == line_count())
|
|
|
|
return end_coord();
|
|
|
|
}
|
|
|
|
return { line, count };
|
|
|
|
}
|
|
|
|
else if (count < 0)
|
|
|
|
{
|
|
|
|
auto line = coord.line;
|
|
|
|
count += coord.column;
|
|
|
|
while (count < 0)
|
|
|
|
{
|
|
|
|
count += m_lines[--line].length();
|
|
|
|
if (line < 0)
|
|
|
|
return {0, 0};
|
|
|
|
}
|
|
|
|
return { line, count };
|
|
|
|
}
|
|
|
|
return coord;
|
2013-05-22 20:59:55 +04:00
|
|
|
}
|
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord Buffer::char_next(ByteCoord coord) const
|
2013-06-03 20:56:48 +04:00
|
|
|
{
|
|
|
|
if (coord.column < m_lines[coord.line].length() - 1)
|
|
|
|
{
|
2014-10-03 16:39:13 +04:00
|
|
|
auto line = m_lines[coord.line];
|
2014-10-05 13:20:50 +04:00
|
|
|
coord.column += utf8::codepoint_size(line[coord.column]);
|
2013-12-12 17:45:14 +04:00
|
|
|
// Handle invalid utf-8
|
|
|
|
if (coord.column >= line.length())
|
|
|
|
{
|
|
|
|
++coord.line;
|
|
|
|
coord.column = 0;
|
|
|
|
}
|
2013-06-03 20:56:48 +04:00
|
|
|
}
|
|
|
|
else if (coord.line == m_lines.size() - 1)
|
|
|
|
coord.column = m_lines.back().length();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++coord.line;
|
|
|
|
coord.column = 0;
|
|
|
|
}
|
|
|
|
return coord;
|
|
|
|
}
|
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord Buffer::char_prev(ByteCoord coord) const
|
2013-06-03 20:56:48 +04:00
|
|
|
{
|
|
|
|
kak_assert(is_valid(coord));
|
|
|
|
if (is_end(coord))
|
|
|
|
return coord = {(int)m_lines.size()-1, m_lines.back().length() - 1};
|
|
|
|
else if (coord.column == 0)
|
|
|
|
{
|
|
|
|
if (coord.line > 0)
|
2013-06-04 16:11:55 +04:00
|
|
|
coord.column = m_lines[--coord.line].length() - 1;
|
2013-06-03 20:56:48 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-10-03 16:39:13 +04:00
|
|
|
auto line = m_lines[coord.line];
|
2014-07-03 00:14:01 +04:00
|
|
|
coord.column = (int)(utf8::character_start(line.begin() + (int)coord.column - 1, line.begin()) - line.begin());
|
2013-06-03 20:56:48 +04:00
|
|
|
}
|
|
|
|
return coord;
|
|
|
|
}
|
|
|
|
|
2013-10-15 21:51:31 +04:00
|
|
|
time_t Buffer::fs_timestamp() const
|
|
|
|
{
|
|
|
|
kak_assert(m_flags & Flags::File);
|
|
|
|
return m_fs_timestamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Buffer::set_fs_timestamp(time_t ts)
|
|
|
|
{
|
|
|
|
kak_assert(m_flags & Flags::File);
|
|
|
|
m_fs_timestamp = ts;
|
|
|
|
}
|
|
|
|
|
2013-11-13 00:36:42 +04:00
|
|
|
void Buffer::on_option_changed(const Option& option)
|
|
|
|
{
|
2013-12-11 17:57:10 +04:00
|
|
|
run_hook_in_own_context("BufSetOption",
|
|
|
|
option.name() + "=" + option.get_as_string());
|
|
|
|
}
|
|
|
|
|
2014-10-20 22:18:38 +04:00
|
|
|
void Buffer::run_hook_in_own_context(const String& hook_name, StringView param)
|
2013-12-11 17:57:10 +04:00
|
|
|
{
|
2014-12-19 02:12:58 +03:00
|
|
|
InputHandler hook_handler({ *this, Selection{} }, Context::Flags::Transient);
|
2014-10-30 17:00:42 +03:00
|
|
|
hooks().run_hook(hook_name, param, hook_handler.context());
|
2013-11-13 00:36:42 +04:00
|
|
|
}
|
2013-12-11 17:57:10 +04:00
|
|
|
|
2014-05-07 22:51:01 +04:00
|
|
|
ByteCoord Buffer::last_modification_coord() const
|
2014-04-08 02:39:12 +04:00
|
|
|
{
|
|
|
|
if (m_history.empty())
|
|
|
|
return {};
|
|
|
|
return m_history.back().back().coord;
|
|
|
|
}
|
|
|
|
|
2014-09-22 22:19:34 +04:00
|
|
|
String Buffer::debug_description() const
|
|
|
|
{
|
|
|
|
String res = display_name() + "\n";
|
|
|
|
|
|
|
|
res += " Flags: ";
|
|
|
|
if (m_flags & Flags::File)
|
|
|
|
res += "File (" + name() + ") ";
|
|
|
|
if (m_flags & Flags::New)
|
|
|
|
res += "New ";
|
|
|
|
if (m_flags & Flags::Fifo)
|
|
|
|
res += "Fifo ";
|
|
|
|
if (m_flags & Flags::NoUndo)
|
|
|
|
res += "NoUndo ";
|
|
|
|
res += "\n";
|
|
|
|
|
|
|
|
size_t content_size = 0;
|
|
|
|
for (auto& line : m_lines)
|
2015-01-19 22:31:56 +03:00
|
|
|
content_size += (int)line->strview().length();
|
2014-09-22 22:19:34 +04:00
|
|
|
|
|
|
|
size_t additional_size = 0;
|
|
|
|
for (auto& undo_group : m_history)
|
|
|
|
additional_size += undo_group.size() * sizeof(Modification);
|
|
|
|
additional_size += m_changes.size() * sizeof(Change);
|
|
|
|
|
|
|
|
res += " Used mem: content=" + to_string(content_size) +
|
|
|
|
" additional=" + to_string(additional_size) + "\n";
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|