1
1
mirror of https://github.com/mawww/kakoune.git synced 2024-11-15 18:15:53 +03:00

Add support for reading from stdin/writing to stdout in filter mode

Note that kakoune still needs to read the whole buffer first, only
once stdin is closed can it execute the keys.
This commit is contained in:
Maxime Coste 2014-08-15 13:21:54 +01:00
parent eff32aa1a1
commit 8d4531d419
5 changed files with 125 additions and 87 deletions

View File

@ -1,5 +1,6 @@
#include "buffer_utils.hh"
#include "buffer_manager.hh"
#include "event_manager.hh"
#include <sys/select.h>
@ -24,6 +25,60 @@ CharCount get_column(const Buffer& buffer,
return col;
}
Buffer* create_buffer_from_data(StringView data, StringView name,
Buffer::Flags flags, time_t fs_timestamp)
{
bool bom = false, crlf = false;
const char* pos = data.begin();
if (data.length() >= 3 and
data[0] == '\xEF' and data[1] == '\xBB' and data[2] == '\xBF')
{
bom = true;
pos = data.begin() + 3;
}
std::vector<String> lines;
while (pos < data.end())
{
const char* line_end = pos;
while (line_end < data.end() and *line_end != '\r' and *line_end != '\n')
++line_end;
// this should happen only when opening a file which has no
// end of line as last character.
if (line_end == data.end())
{
lines.emplace_back(pos, line_end);
lines.back() += '\n';
break;
}
lines.emplace_back(pos, line_end + 1);
lines.back().back() = '\n';
if (line_end+1 != data.end() and *line_end == '\r' and *(line_end+1) == '\n')
{
crlf = true;
pos = line_end + 2;
}
else
pos = line_end + 1;
}
Buffer* buffer = BufferManager::instance().get_buffer_ifp(name);
if (buffer)
buffer->reload(std::move(lines), fs_timestamp);
else
buffer = new Buffer{name, flags, std::move(lines), fs_timestamp};
OptionManager& options = buffer->options();
options.get_local_option("eolformat").set<String>(crlf ? "crlf" : "lf");
options.get_local_option("BOM").set<String>(bom ? "utf-8" : "no");
return buffer;
}
Buffer* create_fifo_buffer(String name, int fd, bool scroll)
{
Buffer* buffer = new Buffer(std::move(name), Buffer::Flags::Fifo | Buffer::Flags::NoUndo);

View File

@ -29,6 +29,10 @@ CharCount get_column(const Buffer& buffer,
Buffer* create_fifo_buffer(String name, int fd, bool scroll = false);
Buffer* create_buffer_from_data(StringView data, StringView name,
Buffer::Flags flags,
time_t fs_timestamp = InvalidTime);
}
#endif // buffer_utils_hh_INCLUDED

View File

@ -3,6 +3,7 @@
#include "assert.hh"
#include "buffer.hh"
#include "buffer_manager.hh"
#include "buffer_utils.hh"
#include "completion.hh"
#include "debug.hh"
#include "unicode.hh"
@ -95,6 +96,21 @@ String compact_path(StringView filename)
return filename.str();
}
String read_fd(int fd)
{
String content;
char buf[256];
while (true)
{
ssize_t size = read(fd, buf, 256);
if (size == -1 or size == 0)
break;
content += String(buf, buf + size);
}
return content;
}
String read_file(StringView filename)
{
int fd = open(parse_filename(filename).c_str(), O_RDONLY);
@ -107,17 +123,7 @@ String read_file(StringView filename)
}
auto close_fd = on_scope_end([fd]{ close(fd); });
String content;
char buf[256];
while (true)
{
ssize_t size = read(fd, buf, 256);
if (size == -1 or size == 0)
break;
content += String(buf, buf + size);
}
return content;
return read_fd(fd);
}
Buffer* create_buffer_from_file(String filename)
@ -132,69 +138,20 @@ Buffer* create_buffer_from_file(String filename)
throw file_access_error(filename, strerror(errno));
}
auto close_fd = on_scope_end([&]{ close(fd); });
struct stat st;
fstat(fd, &st);
if (S_ISDIR(st.st_mode))
{
close(fd);
throw file_access_error(filename, "is a directory");
}
const char* data = (const char*)mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
auto cleanup = on_scope_end([&]{ munmap((void*)data, st.st_size); close(fd); });
auto unmap = on_scope_end([&]{ munmap((void*)data, st.st_size); });
const char* pos = data;
bool crlf = false;
bool bom = false;
if (st.st_size >= 3 and
data[0] == '\xEF' and data[1] == '\xBB' and data[2] == '\xBF')
{
bom = true;
pos = data + 3;
}
std::vector<String> lines;
const char* end = data + st.st_size;
while (pos < end)
{
const char* line_end = pos;
while (line_end < end and *line_end != '\r' and *line_end != '\n')
++line_end;
// this should happen only when opening a file which has no
// end of line as last character.
if (line_end == end)
{
lines.emplace_back(pos, line_end);
lines.back() += '\n';
break;
}
lines.emplace_back(pos, line_end + 1);
lines.back().back() = '\n';
if (line_end+1 != end and *line_end == '\r' and *(line_end+1) == '\n')
{
crlf = true;
pos = line_end + 2;
}
else
pos = line_end + 1;
}
Buffer* buffer = BufferManager::instance().get_buffer_ifp(filename);
if (buffer)
buffer->reload(std::move(lines), st.st_mtime);
else
buffer = new Buffer{filename, Buffer::Flags::File,
std::move(lines), st.st_mtime};
OptionManager& options = buffer->options();
options.get_local_option("eolformat").set<String>(crlf ? "crlf" : "lf");
options.get_local_option("BOM").set<String>(bom ? "utf-8" : "no");
return buffer;
return create_buffer_from_data({data, data + st.st_size}, filename, Buffer::Flags::File, st.st_mtime);
}
static void write(int fd, StringView data, StringView filename)
static void write(int fd, StringView data)
{
const char* ptr = data.data();
ssize_t count = (int)data.length();
@ -206,14 +163,12 @@ static void write(int fd, StringView data, StringView filename)
count -= written;
if (written == -1)
throw file_access_error(filename, strerror(errno));
throw file_access_error("fd: " + to_string(fd), strerror(errno));
}
}
void write_buffer_to_file(Buffer& buffer, StringView filename)
void write_buffer_to_fd(Buffer& buffer, int fd)
{
buffer.run_hook_in_own_context("BufWritePre", buffer.name());
const String& eolformat = buffer.options()["eolformat"].get<String>();
StringView eoldata;
if (eolformat == "crlf")
@ -221,25 +176,31 @@ void write_buffer_to_file(Buffer& buffer, StringView filename)
else
eoldata = "\n";
if (buffer.options()["BOM"].get<String>() == "utf-8")
::write(fd, "\xEF\xBB\xBF", 3);
for (LineCount i = 0; i < buffer.line_count(); ++i)
{
int fd = open(parse_filename(filename).c_str(),
O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1)
throw file_access_error(filename, strerror(errno));
auto close_fd = on_scope_end([fd]{ close(fd); });
if (buffer.options()["BOM"].get<String>() == "utf-8")
::write(fd, "\xEF\xBB\xBF", 3);
for (LineCount i = 0; i < buffer.line_count(); ++i)
{
// end of lines are written according to eolformat but always
// stored as \n
StringView linedata = buffer[i];
write(fd, linedata.substr(0, linedata.length()-1), filename);
write(fd, eoldata, filename);
}
// end of lines are written according to eolformat but always
// stored as \n
StringView linedata = buffer[i];
write(fd, linedata.substr(0, linedata.length()-1));
write(fd, eoldata);
}
}
void write_buffer_to_file(Buffer& buffer, StringView filename)
{
buffer.run_hook_in_own_context("BufWritePre", buffer.name());
int fd = open(parse_filename(filename).c_str(),
O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1)
throw file_access_error(filename, strerror(errno));
auto close_fd = on_scope_end([fd]{ close(fd); });
write_buffer_to_fd(buffer, fd);
if ((buffer.flags() & Buffer::Flags::File) and
real_path(filename) == real_path(buffer.name()))
buffer.notify_saved();

View File

@ -28,9 +28,14 @@ String parse_filename(StringView filename);
String real_path(StringView filename);
String compact_path(StringView filename);
String read_fd(int fd);
String read_file(StringView filename);
Buffer* create_buffer_from_file(String filename);
void write_buffer_to_file(Buffer& buffer, StringView filename);
void write_buffer_to_fd(Buffer& buffer, int fd);
String find_file(StringView filename, memoryview<String> paths);
time_t get_fs_timestamp(StringView filename);

View File

@ -411,6 +411,19 @@ int run_filter(StringView keystr, memoryview<StringView> files)
buffer_manager.delete_buffer(*buffer);
}
if (not isatty(0))
{
Buffer* buffer = create_buffer_from_data(read_fd(0), "*stdin*", Buffer::Flags::None);
InputHandler input_handler{{ *buffer, Selection{} }};
for (auto& key : keys)
input_handler.handle_key(key);
write_buffer_to_fd(*buffer, 1);
buffer_manager.delete_buffer(*buffer);
}
buffer_manager.clear_buffer_trash();
return 0;
}