Initial commit

This commit is contained in:
Maxime Coste 2011-09-02 16:51:20 +00:00
commit 535285d9e6
14 changed files with 1095 additions and 0 deletions

11
GOALS Normal file
View File

@ -0,0 +1,11 @@
* Goals (by priority) :
- edition commands should be as fast, or faster (as in less keystroke) than vi
- consistency
- ability tu run background tasks, for exemple :vimgrep equivalent should run while displaying results
- syntax highlighting
- completion framework
- inotify support
* Non goals :
- No window management, this should be handled by a window manager or screen/tmux,
but the editor should accept multiple heads

3
IDEAS Normal file
View File

@ -0,0 +1,3 @@
* Syntax Highlighting and Folding should be implemented through a 'display filter' concept
A window should have a stack of display filters, that can change the representation of the buffer (adding colors, suppressing lines...)
-> Filters should be used to map window coordinates to buffer coordinates

17
src/Makefile Normal file
View File

@ -0,0 +1,17 @@
sources := $(wildcard *.cc)
objects := $(sources:.cc=.o)
deps := $(addprefix ., $(sources:.cc=.d))
CXXFLAGS += -std=c++0x -g
LDFLAGS += -lncurses -lboost_regex
kak : $(objects)
$(CXX) $(LDFLAGS) $(CXXFLAGS) $(objects) -o $@
include $(deps)
.%.d: %.cc
$(CXX) $(CXXFLAGS) -MM $< -o $@
clean :
rm -f *.o .*.d kak tags

218
src/buffer.cc Normal file
View File

@ -0,0 +1,218 @@
#include "buffer.hh"
#include <cassert>
namespace Kakoune
{
template<typename T>
T clamp(T min, T max, T val)
{
if (val < min)
return min;
if (val > max)
return max;
return val;
}
BufferIterator::BufferIterator(const Buffer& buffer, BufferPos position) : m_buffer(&buffer),
m_position(std::max(0, std::min(position, (BufferPos)buffer.length())))
{
}
BufferIterator& BufferIterator::operator=(const BufferIterator& iterator)
{
m_buffer == iterator.m_buffer;
m_position = iterator.m_position;
}
bool BufferIterator::operator==(const BufferIterator& iterator) const
{
assert(m_buffer == iterator.m_buffer);
return (m_position == iterator.m_position);
}
bool BufferIterator::operator!=(const BufferIterator& iterator) const
{
assert(m_buffer == iterator.m_buffer);
return (m_position != iterator.m_position);
}
bool BufferIterator::operator<(const BufferIterator& iterator) const
{
assert(m_buffer == iterator.m_buffer);
return (m_position < iterator.m_position);
}
bool BufferIterator::operator<=(const BufferIterator& iterator) const
{
assert(m_buffer == iterator.m_buffer);
return (m_position <= iterator.m_position);
}
BufferChar BufferIterator::operator*() const
{
assert(m_buffer);
return m_buffer->at(m_position);
}
BufferSize BufferIterator::operator-(const BufferIterator& iterator) const
{
assert(m_buffer == iterator.m_buffer);
return static_cast<BufferSize>(m_position) -
static_cast<BufferSize>(iterator.m_position);
}
BufferIterator BufferIterator::operator+(BufferSize size) const
{
assert(m_buffer);
return BufferIterator(*m_buffer, m_position + size);
}
BufferIterator BufferIterator::operator-(BufferSize size) const
{
assert(m_buffer);
return BufferIterator(*m_buffer, m_position - size);
}
BufferIterator& BufferIterator::operator+=(BufferSize size)
{
assert(m_buffer);
m_position = std::max(0, std::min((BufferSize)m_position + size,
m_buffer->length()));
return *this;
}
BufferIterator& BufferIterator::operator-=(BufferSize size)
{
assert(m_buffer);
m_position = std::max(0, std::min((BufferSize)m_position - size,
m_buffer->length()));
return *this;
}
BufferIterator& BufferIterator::operator++()
{
return (*this += 1);
}
BufferIterator& BufferIterator::operator--()
{
return (*this -= 1);
}
bool BufferIterator::is_begin() const
{
assert(m_buffer);
return m_position == 0;
}
bool BufferIterator::is_end() const
{
assert(m_buffer);
return m_position == m_buffer->length();
}
Buffer::Buffer(const std::string& name)
: m_name(name)
{
}
void Buffer::erase(const BufferIterator& begin, const BufferIterator& end)
{
m_content.erase(begin.m_position, end - begin);
compute_lines();
}
void Buffer::insert(const BufferIterator& position, const BufferString& string)
{
m_content.insert(position.m_position, string);
compute_lines();
}
BufferIterator Buffer::iterator_at(const LineAndColumn& line_and_column) const
{
if (m_lines.empty())
return begin();
BufferPos line = Kakoune::clamp<int>(0, m_lines.size() - 1, line_and_column.line);
BufferPos column = Kakoune::clamp<int>(0, line_length(line), line_and_column.column);
return BufferIterator(*this, m_lines[line] + column);
}
LineAndColumn Buffer::line_and_column_at(const BufferIterator& iterator) const
{
LineAndColumn result;
if (not m_lines.empty())
{
result.line = line_at(iterator);
result.column = iterator.m_position - m_lines[result.line];
}
return result;
}
BufferPos Buffer::line_at(const BufferIterator& iterator) const
{
for (unsigned i = 0; i < m_lines.size(); ++i)
{
if (m_lines[i] > iterator.m_position)
return i - 1;
}
return m_lines.size() - 1;
}
BufferSize Buffer::line_length(BufferPos line) const
{
assert(not m_lines.empty());
BufferPos end = (line >= m_lines.size() - 1) ?
m_content.size() : m_lines[line + 1] - 1;
return end - m_lines[line];
}
LineAndColumn Buffer::clamp(const LineAndColumn& line_and_column) const
{
if (m_lines.empty())
return LineAndColumn();
LineAndColumn result(line_and_column.line, line_and_column.column);
result.line = Kakoune::clamp<int>(0, m_lines.size() - 1, result.line);
result.column = Kakoune::clamp<int>(0, line_length(result.line), result.column);
return result;
}
void Buffer::compute_lines()
{
m_lines.clear();
m_lines.push_back(0);
for (BufferPos i = 0; i < m_content.size(); ++i)
{
if (m_content[i] == '\n')
m_lines.push_back(i + 1);
}
}
BufferIterator Buffer::begin() const
{
return BufferIterator(*this, 0);
}
BufferIterator Buffer::end() const
{
return BufferIterator(*this, length());
}
BufferSize Buffer::length() const
{
return m_content.size();
}
BufferString Buffer::string(const BufferIterator& begin, const BufferIterator& end) const
{
return m_content.substr(begin.m_position, end - begin);
}
BufferChar Buffer::at(BufferPos position) const
{
return m_content[position];
}
}

109
src/buffer.hh Normal file
View File

@ -0,0 +1,109 @@
#ifndef buffer_hh_INCLUDED
#define buffer_hh_INCLUDED
#include <string>
#include <vector>
namespace Kakoune
{
class Buffer;
typedef int BufferPos;
typedef int BufferSize;
typedef char BufferChar;
typedef std::basic_string<BufferChar> BufferString;
struct LineAndColumn
{
BufferPos line;
BufferPos column;
LineAndColumn(BufferPos line = 0, BufferPos column = 0)
: line(line), column(column) {}
};
class BufferIterator
{
public:
typedef BufferChar value_type;
typedef BufferSize difference_type;
typedef const value_type* pointer;
typedef const value_type& reference;
typedef std::bidirectional_iterator_tag iterator_category;
BufferIterator() : m_buffer(NULL), m_position(0) {}
BufferIterator(const Buffer& buffer, BufferPos position);
BufferIterator& operator=(const BufferIterator& iterator);
bool operator== (const BufferIterator& iterator) const;
bool operator!= (const BufferIterator& iterator) const;
bool operator< (const BufferIterator& iterator) const;
bool operator<= (const BufferIterator& iterator) const;
BufferChar operator* () const;
BufferSize operator- (const BufferIterator& iterator) const;
BufferIterator operator+ (BufferSize size) const;
BufferIterator operator- (BufferSize size) const;
BufferIterator& operator+= (BufferSize size);
BufferIterator& operator-= (BufferSize size);
BufferIterator& operator++ ();
BufferIterator& operator-- ();
bool is_begin() const;
bool is_end() const;
private:
const Buffer* m_buffer;
BufferPos m_position;
friend class Buffer;
};
class Buffer
{
public:
Buffer(const std::string& name);
void erase(const BufferIterator& begin,
const BufferIterator& end);
void insert(const BufferIterator& position,
const BufferString& string);
BufferString string(const BufferIterator& begin,
const BufferIterator& end) const;
BufferIterator begin() const;
BufferIterator end() const;
BufferSize length() const;
BufferIterator iterator_at(const LineAndColumn& line_and_column) const;
LineAndColumn line_and_column_at(const BufferIterator& iterator) const;
LineAndColumn clamp(const LineAndColumn& line_and_column) const;
const std::string& name() const { return m_name; }
const BufferString& content() const { return m_content; }
private:
BufferChar at(BufferPos position) const;
friend class BufferIterator;
std::vector<BufferPos> m_lines;
void compute_lines();
BufferPos line_at(const BufferIterator& iterator) const;
BufferSize line_length(BufferPos line) const;
BufferString m_content;
std::string m_name;
};
}
#endif // buffer_hh_INCLUDED

15
src/display_buffer.cc Normal file
View File

@ -0,0 +1,15 @@
#include "display_buffer.hh"
namespace Kakoune
{
DisplayBuffer::DisplayBuffer()
{
}
LineAndColumn DisplayBuffer::dimensions() const
{
return LineAndColumn();
}
}

55
src/display_buffer.hh Normal file
View File

@ -0,0 +1,55 @@
#ifndef display_buffer_hh_INCLUDED
#define display_buffer_hh_INCLUDED
#include <string>
#include <vector>
#include "buffer.hh"
namespace Kakoune
{
typedef int Color;
typedef int Attribute;
enum Attributes
{
UNDERLINE = 1
};
struct DisplayAtom
{
std::string content;
Color fg_color;
Color bg_color;
Attribute attribute;
DisplayAtom() : fg_color(0), bg_color(0), attribute(0) {}
};
class DisplayBuffer
{
public:
typedef std::vector<DisplayAtom> AtomList;
typedef AtomList::iterator iterator;
typedef AtomList::const_iterator const_iterator;
DisplayBuffer();
LineAndColumn dimensions() const;
void clear() { m_atoms.clear(); }
void append(const DisplayAtom& atom) { m_atoms.push_back(atom); }
iterator begin() { return m_atoms.begin(); }
iterator end() { return m_atoms.end(); }
const_iterator begin() const { return m_atoms.begin(); }
const_iterator end() const { return m_atoms.end(); }
private:
AtomList m_atoms;
};
}
#endif // display_buffer_hh_INCLUDED

57
src/file.cc Normal file
View File

@ -0,0 +1,57 @@
#include "file.hh"
#include "buffer.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstring>
#include <cassert>
namespace Kakoune
{
Buffer* create_buffer_from_file(const std::string& filename)
{
int fd = open(filename.c_str(), O_RDONLY);
if (fd == -1)
throw open_file_error(strerror(errno));
std::string content;
char buf[256];
while (true)
{
ssize_t size = read(fd, buf, 256);
if (size == -1 or size == 0)
break;
content += std::string(buf, size);
}
close(fd);
Buffer* buffer = new Buffer(filename);
buffer->insert(buffer->begin(), content);
return buffer;
}
void write_buffer_to_file(const Buffer& buffer, const std::string& filename)
{
int fd = open(filename.c_str(), O_CREAT | O_WRONLY, 0644);
if (fd == -1)
throw open_file_error(strerror(errno));
const BufferString& content = buffer.content();
ssize_t count = content.length() * sizeof(BufferChar);
const char* ptr = content.c_str();
while (count)
{
ssize_t written = write(fd, ptr, count);
ptr += written;
count -= written;
if (written == -1)
throw write_file_error(strerror(errno));
}
close(fd);
}
}

28
src/file.hh Normal file
View File

@ -0,0 +1,28 @@
#ifndef file_hh_INCLUDED
#define file_hh_INCLUDED
#include <string>
#include <stdexcept>
namespace Kakoune
{
struct open_file_error : public std::runtime_error
{
open_file_error(const std::string& what)
: std::runtime_error(what) {}
};
struct write_file_error : public std::runtime_error
{
write_file_error(const std::string& what)
: std::runtime_error(what) {}
};
class Buffer;
Buffer* create_buffer_from_file(const std::string& filename);
void write_buffer_to_file(const Buffer& buffer, const std::string& filename);
}
#endif // file_hh_INCLUDED

323
src/main.cc Normal file
View File

@ -0,0 +1,323 @@
#include <ncurses.h>
#include "window.hh"
#include "buffer.hh"
#include "file.hh"
#include "regex_selector.hh"
#include <unordered_map>
#include <cassert>
using namespace Kakoune;
void draw_window(Window& window)
{
window.update_display_buffer();
int max_x,max_y;
getmaxyx(stdscr, max_y, max_x);
max_y -= 1;
LineAndColumn position;
for (const DisplayAtom& atom : window.display_buffer())
{
const std::string& content = atom.content;
if (atom.attribute & UNDERLINE)
attron(A_UNDERLINE);
else
attroff(A_UNDERLINE);
size_t pos = 0;
size_t end;
while (true)
{
move(position.line, position.column);
clrtoeol();
end = content.find_first_of('\n', pos);
std::string line = content.substr(pos, end - pos);
addstr(line.c_str());
if (end != std::string::npos)
{
position.line = position.line + 1;
position.column = 0;
pos = end + 1;
if (position.line >= max_y)
break;
}
else
{
position.column += line.length();
break;
}
}
if (position.line >= max_y)
break;
}
while (++position.line < max_y)
{
move(position.line, 0);
clrtoeol();
addch('~');
}
const LineAndColumn& cursor_position = window.cursor_position();
move(cursor_position.line, cursor_position.column);
}
void init_ncurses()
{
initscr();
cbreak();
noecho();
nonl();
intrflush(stdscr, false);
keypad(stdscr, true);
curs_set(2);
}
void deinit_ncurses()
{
endwin();
}
void do_insert(Window& window)
{
std::string inserted;
LineAndColumn pos = window.cursor_position();
while(true)
{
char c = getch();
if (c == 27)
break;
window.insert(std::string() + c);
draw_window(window);
}
}
std::shared_ptr<Window> current_window;
void edit(const std::string& filename)
{
try
{
Buffer* buffer = create_buffer_from_file(filename);
if (buffer)
current_window = std::make_shared<Window>(std::shared_ptr<Buffer>(buffer));
}
catch (open_file_error& what)
{
assert(false);
}
}
void write_buffer(const std::string& filename)
{
try
{
write_buffer_to_file(*current_window->buffer(), filename);
}
catch(open_file_error& what)
{
assert(false);
}
catch(write_file_error& what)
{
assert(false);
}
}
bool quit_requested = false;
void quit(const std::string&)
{
quit_requested = true;
}
std::unordered_map<std::string, std::function<void (const std::string& param)>> cmdmap =
{
{ "e", edit },
{ "edit", edit },
{ "q", quit },
{ "quit", quit },
{ "w", write_buffer },
{ "write", write_buffer },
};
struct prompt_aborted {};
std::string prompt(const std::string& text)
{
int max_x, max_y;
getmaxyx(stdscr, max_y, max_x);
move(max_y-1, 0);
addstr(text.c_str());
clrtoeol();
std::string result;
while(true)
{
char c = getch();
switch (c)
{
case '\r':
return result;
case 7:
move(max_y - 1, text.length() + result.length() - 1);
addch(' ');
result.resize(result.length() - 1);
move(max_y - 1, text.length() + result.length());
refresh;
break;
case 27:
throw prompt_aborted();
default:
result += c;
addch(c);
refresh();
}
}
return result;
}
void print_status(const std::string& status)
{
int x,y;
getmaxyx(stdscr, y, x);
move(y-1, 0);
clrtoeol();
addstr(status.c_str());
}
void do_command()
{
try
{
std::string cmd = prompt(":");
size_t cmd_end = cmd.find_first_of(' ');
std::string cmd_name = cmd.substr(0, cmd_end);
size_t param_start = cmd.find_first_not_of(' ', cmd_end);
std::string param;
if (param_start != std::string::npos)
param = cmd.substr(param_start, cmd.length() - param_start);
if (cmdmap.find(cmd_name) != cmdmap.end())
cmdmap[cmd_name](param);
else
print_status(cmd_name + ": no such command");
}
catch (prompt_aborted&) {}
}
bool is_blank(char c)
{
return c == ' ' or c == '\t' or c == '\n';
}
Selection select_to_next_word(const BufferIterator& cursor)
{
BufferIterator end = cursor;
while (not end.is_end() and not is_blank(*end))
++end;
while (not end.is_end() and is_blank(*end))
++end;
return Selection(cursor, end);
}
Selection select_to_next_word_end(const BufferIterator& cursor)
{
BufferIterator end = cursor;
while (not end.is_end() and is_blank(*end))
++end;
while (not end.is_end() and not is_blank(*end))
++end;
return Selection(cursor, end);
}
Selection select_line(const BufferIterator& cursor)
{
BufferIterator begin = cursor;
while (not begin.is_begin() and *(begin -1) != '\n')
--begin;
BufferIterator end = cursor;
while (not end.is_end() and *end != '\n')
++end;
return Selection(begin, end + 1);
}
void do_search(Window& window)
{
try
{
std::string ex = prompt("/");
window.select(false, RegexSelector(ex));
}
catch (boost::regex_error&) {}
catch (prompt_aborted&) {}
}
std::unordered_map<char, std::function<void (Window& window, int count)>> keymap =
{
{ 'h', [](Window& window, int count) { if (count == 0) count = 1; window.move_cursor(LineAndColumn(0, -count)); window.empty_selections(); } },
{ 'j', [](Window& window, int count) { if (count == 0) count = 1; window.move_cursor(LineAndColumn(count, 0)); window.empty_selections(); } },
{ 'k', [](Window& window, int count) { if (count == 0) count = 1; window.move_cursor(LineAndColumn(-count, 0)); window.empty_selections(); } },
{ 'l', [](Window& window, int count) { if (count == 0) count = 1; window.move_cursor(LineAndColumn(0, count)); window.empty_selections(); } },
{ 'd', [](Window& window, int count) { window.erase(); window.empty_selections(); } },
{ 'c', [](Window& window, int count) { window.erase(); do_insert(window); } },
{ 'i', [](Window& window, int count) { do_insert(window); } },
{ ':', [](Window& window, int count) { do_command(); } },
{ ' ', [](Window& window, int count) { window.empty_selections(); } },
{ 'w', [](Window& window, int count) { do { window.select(false, select_to_next_word); } while(--count > 0); } },
{ 'W', [](Window& window, int count) { do { window.select(true, select_to_next_word); } while(--count > 0); } },
{ 'e', [](Window& window, int count) { do { window.select(false, select_to_next_word_end); } while(--count > 0); } },
{ 'E', [](Window& window, int count) { do { window.select(true, select_to_next_word_end); } while(--count > 0); } },
{ '.', [](Window& window, int count) { do { window.select(false, select_line); } while(--count > 0); } },
{ '/', [](Window& window, int count) { do_search(window); } },
};
int main()
{
init_ncurses();
try
{
auto buffer = std::make_shared<Buffer>("<scratch>");
current_window = std::make_shared<Window>(buffer);
draw_window(*current_window);
int count = 0;
while(not quit_requested)
{
char c = getch();
if (isdigit(c))
count = count * 10 + c - '0';
else
{
if (keymap.find(c) != keymap.end())
{
keymap[c](*current_window, count);
draw_window(*current_window);
}
count = 0;
}
}
deinit_ncurses();
}
catch (std::runtime_error& error)
{
deinit_ncurses();
puts("unhandled exception : ");
puts(error.what());
return -1;
}
return 0;
}

33
src/regex_selector.cc Normal file
View File

@ -0,0 +1,33 @@
#include "regex_selector.hh"
void print_status(const std::string&);
namespace Kakoune
{
RegexSelector::RegexSelector(const std::string& exp)
: m_regex(exp) {}
Selection RegexSelector::operator()(const BufferIterator& cursor) const
{
BufferIterator line_end = cursor + 1;
try
{
while (not line_end.is_end() and *line_end != '\n')
++line_end;
boost::match_results<BufferIterator> matches;
if (boost::regex_search(cursor, line_end, matches, m_regex))
return Selection(matches.begin()->first, matches.begin()->second);
}
catch (boost::regex_error& err)
{
print_status("regex error");
}
return Selection(cursor, cursor);
}
}

25
src/regex_selector.hh Normal file
View File

@ -0,0 +1,25 @@
#ifndef regex_selector_hh_INCLUDED
#define regex_selector_hh_INCLUDED
#include "buffer.hh"
#include "window.hh"
#include <boost/regex.hpp>
namespace Kakoune
{
class RegexSelector
{
public:
RegexSelector(const std::string& exp);
Selection operator()(const BufferIterator& cursor) const;
private:
boost::regex m_regex;
};
}
#endif // regex_selector_hh_INCLUDED

139
src/window.cc Normal file
View File

@ -0,0 +1,139 @@
#include "window.hh"
#include <algorithm>
namespace Kakoune
{
Window::Window(const std::shared_ptr<Buffer> buffer)
: m_buffer(buffer),
m_position(0, 0),
m_cursor(0, 0)
{
}
void Window::erase()
{
if (m_selections.empty())
{
BufferIterator cursor = m_buffer->iterator_at(m_cursor);
m_buffer->erase(cursor, cursor+1);
}
for (auto sel = m_selections.begin(); sel != m_selections.end(); ++sel)
{
m_buffer->erase(sel->begin, sel->end);
sel->end = sel->begin;
}
}
static LineAndColumn measure_string(const Window::String& string)
{
LineAndColumn result(0, 0);
for (size_t i = 0; i < string.length(); ++i)
{
if (string[i] == '\n')
{
++result.line;
result.column = 0;
}
else
++result.column;
}
return result;
}
void Window::insert(const String& string)
{
if (m_selections.empty())
{
m_buffer->insert(m_buffer->iterator_at(m_cursor), string);
move_cursor(measure_string(string));
}
for (auto sel = m_selections.begin(); sel != m_selections.end(); ++sel)
{
m_buffer->insert(sel->begin, string);
sel->begin += string.length();
sel->end += string.length();
}
}
void Window::append(const String& string)
{
if (m_selections.empty())
{
move_cursor(LineAndColumn(0 , 1));
insert(string);
}
for (auto sel = m_selections.begin(); sel != m_selections.end(); ++sel)
{
m_buffer->insert(sel->end, string);
}
}
void Window::empty_selections()
{
m_selections.clear();
}
void Window::select(bool append, const Selector& selector)
{
if (not append or m_selections.empty())
{
empty_selections();
m_selections.push_back(selector(m_buffer->iterator_at(m_cursor)));
}
else
{
for (auto sel = m_selections.begin(); sel != m_selections.end(); ++sel)
{
sel->end = selector(sel->end).end;
}
}
m_cursor = m_buffer->line_and_column_at(m_selections.back().end);
}
void Window::move_cursor(const LineAndColumn& offset)
{
m_cursor = m_buffer->clamp(LineAndColumn(m_cursor.line + offset.line,
m_cursor.column + offset.column));
}
void Window::update_display_buffer()
{
m_display_buffer.clear();
SelectionList sorted_selections = m_selections;
std::sort(sorted_selections.begin(), sorted_selections.end(),
[](const Selection& lhs, const Selection& rhs) { return lhs.begin < rhs.begin; });
BufferIterator current_position = m_buffer->begin();
for (Selection& sel : sorted_selections)
{
if (current_position != sel.begin)
{
DisplayAtom atom;
atom.content = m_buffer->string(current_position, sel.begin);
m_display_buffer.append(atom);
}
if (sel.begin != sel.end)
{
DisplayAtom atom;
atom.content = m_buffer->string(sel.begin, sel.end);
atom.attribute = UNDERLINE;
m_display_buffer.append(atom);
}
current_position = sel.end;
}
if (current_position != m_buffer->end())
{
DisplayAtom atom;
atom.content = m_buffer->string(current_position, m_buffer->end());
m_display_buffer.append(atom);
}
}
}

62
src/window.hh Normal file
View File

@ -0,0 +1,62 @@
#ifndef window_hh_INCLUDED
#define window_hh_INCLUDED
#include <memory>
#include <functional>
#include "buffer.hh"
#include "display_buffer.hh"
namespace Kakoune
{
struct Selection
{
Selection(const BufferIterator& begin, const BufferIterator& end)
: begin(begin), end(end) {}
BufferIterator begin;
BufferIterator end;
};
typedef std::vector<Selection> SelectionList;
class Window
{
public:
typedef BufferString String;
typedef std::function<Selection (const BufferIterator&)> Selector;
Window(const std::shared_ptr<Buffer> buffer);
Window(const Window&) = delete;
void erase();
void insert(const String& string);
void append(const String& string);
const LineAndColumn& position() const { return m_position; }
const LineAndColumn& cursor_position() const { return m_cursor; }
const std::shared_ptr<Buffer>& buffer() const { return m_buffer; }
void move_cursor(const LineAndColumn& offset);
const SelectionList& selections() const { return m_selections; }
void empty_selections();
void select(bool append, const Selector& selector);
const DisplayBuffer& display_buffer() const { return m_display_buffer; }
void update_display_buffer();
private:
std::shared_ptr<Buffer> m_buffer;
LineAndColumn m_position;
LineAndColumn m_cursor;
LineAndColumn m_dimensions;
SelectionList m_selections;
DisplayBuffer m_display_buffer;
};
}
#endif // window_hh_INCLUDED