~jamii/focus

ref: 6ca339cc4a715b8a5bac73b4345a1f5c07c6ddfc focus/lib/focus/buffer_searcher.zig -rw-r--r-- 6.2 KiB
6ca339ccJamie Brandon Bugfixes 1 year, 5 months ago
                                                                                
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
const focus = @import("../focus.zig");
usingnamespace focus.common;
const meta = focus.meta;
const App = focus.App;
const Buffer = focus.Buffer;
const Editor = focus.Editor;
const SingleLineEditor = focus.SingleLineEditor;
const Selector = focus.Selector;
const Window = focus.Window;
const style = focus.style;

pub const BufferSearcher = struct {
    app: *App,
    target_editor: *Editor,
    preview_editor: *Editor,
    input: SingleLineEditor,
    selector: Selector,
    // The position of the currently selected item, or the start pos if nothing was selected yet.
    // This just helps preserve position in the editor when jumping in and out of the buffer searcher.
    selection_pos: usize,

    pub fn init(app: *App, target_editor: *Editor) *BufferSearcher {
        const preview_editor = Editor.init(app, target_editor.buffer, false, false);
        preview_editor.getMainCursor().* = target_editor.getMainCursor().*;
        const input = SingleLineEditor.init(app, app.last_search_filter);
        input.editor.goRealLineStart(input.editor.getMainCursor());
        input.editor.setMark();
        input.editor.goRealLineEnd(input.editor.getMainCursor());
        const selector = Selector.init(app);
        const selection_pos = target_editor.getMainCursor().head.pos;
        var self = app.allocator.create(BufferSearcher) catch oom();
        self.* = BufferSearcher{
            .app = app,
            .target_editor = target_editor,
            .preview_editor = preview_editor,
            .input = input,
            .selector = selector,
            .selection_pos = selection_pos,
        };
        return self;
    }

    pub fn deinit(self: *BufferSearcher) void {
        self.selector.deinit();
        self.input.deinit();
        self.preview_editor.deinit();
        // dont own target_editor
        self.app.allocator.destroy(self);
    }

    pub fn frame(self: *BufferSearcher, window: *Window, rect: Rect, events: []const c.SDL_Event) void {
        const layout = window.layoutSearcherWithPreview(rect);

        // run input frame
        self.input.frame(window, layout.input, events);

        // search buffer
        // TODO default to first result after target
        const filter = self.input.getText();
        var results = ArrayList([]const u8).init(self.app.frame_allocator);
        var result_poss = ArrayList(usize).init(self.app.frame_allocator);
        {
            const max_line_string = format(self.app.frame_allocator, "{}", .{self.preview_editor.buffer.countLines()});
            if (filter.len > 0) {
                var pos: usize = 0;
                var i: usize = 0;
                while (self.preview_editor.buffer.searchForwards(pos, filter)) |found_pos| {
                    const start = self.preview_editor.buffer.getLineStart(found_pos);
                    const end = self.preview_editor.buffer.getLineEnd(found_pos + filter.len);
                    const selection = self.preview_editor.buffer.tree.copy(self.app.frame_allocator, start, end);
                    assert(selection[0] != '\n' and selection[selection.len - 1] != '\n');

                    var result = ArrayList(u8).init(self.app.frame_allocator);
                    const line = self.preview_editor.buffer.getLineColForPos(found_pos)[0];
                    const line_string = format(self.app.frame_allocator, "{}", .{line});
                    result.appendSlice(line_string) catch oom();
                    result.appendNTimes(' ', max_line_string.len - line_string.len + 1) catch oom();
                    result.appendSlice(selection) catch oom();

                    results.append(result.toOwnedSlice()) catch oom();
                    result_poss.append(found_pos) catch oom();

                    pos = found_pos + filter.len;
                    i += 1;
                }
            }
        }

        // update selection
        self.selector.selected = max(result_poss.items.len, 1) - 1;
        for (result_poss.items) |result_pos, i| {
            if (self.selection_pos < result_pos + filter.len) {
                self.selector.selected = i;
                break;
            }
        }

        // run selector frame
        const old_selected = self.selector.selected;
        const action = self.selector.frame(window, layout.selector, events, results.items);
        if (self.selector.selected != old_selected and self.selector.selected < result_poss.items.len) {
            self.selection_pos = result_poss.items[self.selector.selected];
        }
        switch (action) {
            .None, .SelectRaw => {},
            .SelectOne, .SelectAll => {
                self.updateEditor(self.target_editor, action, result_poss.items, filter);
                self.target_editor.top_pixel = self.preview_editor.top_pixel;
                window.popView();
            },
        }

        // set cached search text
        self.app.allocator.free(self.app.last_search_filter);
        self.app.last_search_filter = self.app.dupe(self.input.getText());

        // update preview
        self.updateEditor(self.preview_editor, action, result_poss.items, filter);

        // run preview frame
        self.preview_editor.frame(window, layout.preview, &[0]c.SDL_Event{});
    }

    fn updateEditor(self: *BufferSearcher, editor: *Editor, action: Selector.Action, result_poss: []usize, filter: []const u8) void {
        // TODO centre view on main cursor
        editor.collapseCursors();
        editor.setMark();
        switch (action) {
            .None, .SelectRaw, .SelectOne => {
                if (result_poss.len != 0) {
                    const pos = result_poss[self.selector.selected];
                    var cursor = editor.getMainCursor();
                    editor.goPos(cursor, pos + filter.len);
                    editor.updatePos(&cursor.tail, pos);
                    editor.last_center_pos = pos;
                }
            },
            .SelectAll => {
                for (result_poss) |pos, i| {
                    var cursor = if (i == 0) editor.getMainCursor() else editor.newCursor();
                    editor.goPos(cursor, pos + filter.len);
                    editor.updatePos(&cursor.tail, pos);
                    editor.last_center_pos = pos;
                }
            },
        }
    }
};