#include "buffer.hh" #include "assert.hh" #include "buffer_manager.hh" #include "buffer_utils.hh" #include "client.hh" #include "context.hh" #include "diff.hh" #include "file.hh" #include "flags.hh" #include "option_types.hh" #include "ranges.hh" #include "shared_string.hh" #include "unit_tests.hh" #include "utils.hh" #include "window.hh" #include namespace Kakoune { struct ParsedLines { BufferLines lines; ByteOrderMark bom = ByteOrderMark::None; EolFormat eolformat = EolFormat::Lf; }; static ParsedLines parse_lines(StringView data) { ParsedLines res; const char* pos = data.begin(); if (data.substr(0, 3_byte) == "\xEF\xBB\xBF") { res.bom = ByteOrderMark::Utf8; pos = data.begin() + 3; } bool has_crlf = false, has_lf = false; for (auto it = pos; it != data.end(); ++it) { if (*it == '\n') ((it != pos and *(it-1) == '\r') ? has_crlf : has_lf) = true; } const bool crlf = has_crlf and not has_lf; res.eolformat = crlf ? EolFormat::Crlf : EolFormat::Lf; while (pos < data.end()) { const char* eol = std::find(pos, data.end(), '\n'); res.lines.emplace_back(StringData::create({{pos, eol - (crlf and eol != data.end() ? 1 : 0)}, "\n"})); pos = eol + 1; } return res; } static void apply_options(OptionManager& options, const ParsedLines& parsed_lines) { options.get_local_option("eolformat").set(parsed_lines.eolformat); options.get_local_option("BOM").set(parsed_lines.bom); } Buffer::HistoryNode::HistoryNode(HistoryId parent) : parent{parent}, timepoint{Clock::now()} {} Buffer::Buffer(String name, Flags flags, StringView data, timespec fs_timestamp) : Scope{GlobalScope::instance()}, m_name{(flags & Flags::File) ? real_path(parse_filename(name)) : std::move(name)}, m_display_name{(flags & Flags::File) ? compact_path(m_name) : m_name}, m_flags{flags | Flags::NoUndo}, m_history{{HistoryId::Invalid}}, m_history_id{HistoryId::First}, m_last_save_history_id{HistoryId::First}, m_fs_timestamp{fs_timestamp.tv_sec, fs_timestamp.tv_nsec} { ParsedLines parsed_lines = parse_lines(data); if (parsed_lines.lines.empty()) parsed_lines.lines.emplace_back(StringData::create({"\n"})); #ifdef KAK_DEBUG for (auto& line : parsed_lines.lines) kak_assert(not (line->length == 0) and line->data()[line->length-1] == '\n'); #endif static_cast(m_lines) = std::move(parsed_lines.lines); m_changes.push_back({ Change::Insert, {0,0}, line_count() }); apply_options(options(), parsed_lines); // now we may begin to record undo data if (not (flags & Flags::NoUndo)) m_flags &= ~Flags::NoUndo; } void Buffer::on_registered() { // Ignore debug buffer, as it can be created in many // corner cases (including while destroying the BufferManager // if a BufClose hooks triggers writing to it). if (m_flags & Flags::Debug) return; options().register_watcher(*this); if (m_flags & Buffer::Flags::NoHooks) { on_option_changed(options()["readonly"]); return; } run_hook_in_own_context("BufCreate", m_name); if (m_flags & Flags::File) { if (m_flags & Buffer::Flags::New) run_hook_in_own_context("BufNewFile", m_name); else { kak_assert(m_fs_timestamp != InvalidTime); run_hook_in_own_context("BufOpenFile", m_name); } } for (auto& option : options().flatten_options() | transform(&std::unique_ptr