summaryrefslogtreecommitdiff
path: root/src/buffer_utils.cc
blob: 12ffae195f667f0041a1d53e0c4a3110f7f4f7b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include "buffer_utils.hh"

#include "buffer_manager.hh"
#include "event_manager.hh"

#include <unistd.h>
#include <sys/select.h>

namespace Kakoune
{

CharCount get_column(const Buffer& buffer,
                     CharCount tabstop, ByteCoord coord)
{
    auto line = buffer[coord.line];
    auto col = 0_char;
    for (auto it = line.begin();
         it != line.end() and coord.column > (int)(it - line.begin());
         it = utf8::next(it, line.end()))
    {
        if (*it == '\t')
            col = (col / tabstop + 1) * tabstop;
        else
            ++col;
    }
    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)
{
    static ValueId s_fifo_watcher_id = ValueId::get_free_id();

    Buffer* buffer = BufferManager::instance().get_buffer_ifp(name);
    if (buffer)
    {
        buffer->flags() |= Buffer::Flags::NoUndo;
        buffer->reload(std::vector<String>({"\n"_str}), 0);
    }
    else
        buffer = new Buffer(std::move(name), Buffer::Flags::Fifo | Buffer::Flags::NoUndo);

    auto watcher_deleter = [buffer](FDWatcher* watcher) {
        kak_assert(buffer->flags() & Buffer::Flags::Fifo);
        close(watcher->fd());
        buffer->run_hook_in_own_context("BufCloseFifo", "");
        buffer->flags() &= ~Buffer::Flags::Fifo;
        watcher->~FDWatcher();
    };

    // capture a non static one to silence a warning.
    ValueId fifo_watcher_id = s_fifo_watcher_id;

    std::unique_ptr<FDWatcher, decltype(watcher_deleter)> watcher(
        new FDWatcher(fd, [buffer, scroll, fifo_watcher_id](FDWatcher& watcher) {
        constexpr size_t buffer_size = 2048;
        // if we read data slower than it arrives in the fifo, limiting the
        // iteration number allows us to go back go back to the event loop and
        // handle other events sources (such as input)
        size_t loops = 16;
        char data[buffer_size];
        const int fifo = watcher.fd();
        timeval tv{ 0, 0 };
        fd_set  rfds;
        ssize_t count = 0;
        do
        {
            count = read(fifo, data, buffer_size);
            auto pos = buffer->end()-1;

            bool prevent_scrolling = pos == buffer->begin() and not scroll;
            if (prevent_scrolling)
                ++pos;

            buffer->insert(pos, String(data, data+count));

            if (count > 0 and prevent_scrolling)
            {
                buffer->erase(buffer->begin(), buffer->begin()+1);
                // in the other case, the buffer will have automatically
                // inserted a \n to guarantee its invariant.
                if (data[count-1] == '\n')
                    buffer->insert(buffer->end(), "\n");
            }

            FD_ZERO(&rfds);
            FD_SET(fifo, &rfds);
        }
        while (--loops and count > 0 and
               select(fifo+1, &rfds, nullptr, nullptr, &tv) == 1);

        if (count <= 0)
            buffer->values().erase(fifo_watcher_id); // will delete this
    }), std::move(watcher_deleter));

    buffer->values()[fifo_watcher_id] = Value(std::move(watcher));
    buffer->flags() = Buffer::Flags::Fifo | Buffer::Flags::NoUndo;

    return buffer;
}

}