1 #include "msg.h"
2 #include "buffer.h"
3 #include "error.h"
4 #include "move.h"
5 #include "search.h"
6 #include "util/ptr-array.h"
7 #include "util/str-util.h"
8 #include "util/xmalloc.h"
9 #include "window.h"
10 
11 static PointerArray file_locations = PTR_ARRAY_INIT;
12 static PointerArray msgs = PTR_ARRAY_INIT;
13 static size_t msg_pos;
14 
file_location_create(const char * filename,unsigned long buffer_id,unsigned long line,unsigned long column)15 FileLocation *file_location_create (
16     const char *filename,
17     unsigned long buffer_id,
18     unsigned long line,
19     unsigned long column
20 ) {
21     FileLocation *loc = xnew0(FileLocation, 1);
22     loc->filename = filename ? xstrdup(filename) : NULL;
23     loc->buffer_id = buffer_id;
24     loc->line = line;
25     loc->column = column;
26     return loc;
27 }
28 
file_location_free(FileLocation * loc)29 void file_location_free(FileLocation *loc)
30 {
31     free(loc->filename);
32     free(loc->pattern);
33     free(loc);
34 }
35 
file_location_equals(const FileLocation * a,const FileLocation * b)36 static bool file_location_equals(const FileLocation *a, const FileLocation *b)
37 {
38     if (!xstreq(a->filename, b->filename)) {
39         return false;
40     }
41     if (a->buffer_id != b->buffer_id) {
42         return false;
43     }
44     if (!xstreq(a->pattern, b->pattern)) {
45         return false;
46     }
47     if (a->line != b->line) {
48         return false;
49     }
50     if (a->column != b->column) {
51         return false;
52     }
53     return true;
54 }
55 
file_location_go(const FileLocation * loc)56 static bool file_location_go(const FileLocation *loc)
57 {
58     Window *w = window;
59     View *v = window_open_buffer(w, loc->filename, true, NULL);
60     bool ok = true;
61 
62     if (!v) {
63         // Failed to open file. Error message should be visible.
64         return false;
65     }
66     if (w->view != v) {
67         set_view(v);
68         // Force centering view to the cursor because file changed
69         v->force_center = true;
70     }
71     if (loc->pattern != NULL) {
72         bool err = false;
73         search_tag(loc->pattern, &err);
74         ok = !err;
75     } else if (loc->line > 0) {
76         move_to_line(v, loc->line);
77         if (loc->column > 0) {
78             move_to_column(v, loc->column);
79         }
80     }
81     return ok;
82 }
83 
file_location_return(const FileLocation * loc)84 static bool file_location_return(const FileLocation *loc)
85 {
86     Window *w = window;
87     Buffer *b = find_buffer_by_id(loc->buffer_id);
88     View *v;
89 
90     if (b != NULL) {
91         v = window_get_view(w, b);
92     } else {
93         if (loc->filename == NULL) {
94             // Can't restore closed buffer that had no filename.
95             // Try again.
96             return false;
97         }
98         v = window_open_buffer(w, loc->filename, true, NULL);
99     }
100     if (v == NULL) {
101         // Open failed. Don't try again.
102         return true;
103     }
104     set_view(v);
105     move_to_line(v, loc->line);
106     move_to_column(v, loc->column);
107     return true;
108 }
109 
push_file_location(FileLocation * loc)110 void push_file_location(FileLocation *loc)
111 {
112     ptr_array_add(&file_locations, loc);
113 }
114 
pop_file_location(void)115 void pop_file_location(void)
116 {
117     bool go = true;
118     while (file_locations.count > 0 && go) {
119         FileLocation *loc = file_locations.ptrs[--file_locations.count];
120         go = !file_location_return(loc);
121         file_location_free(loc);
122     }
123 }
124 
free_message(Message * m)125 static void free_message(Message *m)
126 {
127     free(m->msg);
128     if (m->loc != NULL) {
129         file_location_free(m->loc);
130     }
131     free(m);
132 }
133 
message_equals(const Message * a,const Message * b)134 static bool message_equals(const Message *a, const Message *b)
135 {
136     if (!streq(a->msg, b->msg)) {
137         return false;
138     }
139     if (a->loc == NULL) {
140         return b->loc == NULL;
141     }
142     if (b->loc == NULL) {
143         return false;
144     }
145     return file_location_equals(a->loc, b->loc);
146 }
147 
is_duplicate(const Message * m)148 static bool is_duplicate(const Message *m)
149 {
150     for (size_t i = 0; i < msgs.count; i++) {
151         if (message_equals(m, msgs.ptrs[i])) {
152             return true;
153         }
154     }
155     return false;
156 }
157 
new_message(const char * msg)158 Message *new_message(const char *msg)
159 {
160     Message *m = xnew0(Message, 1);
161     m->msg = xstrdup(msg);
162     return m;
163 }
164 
add_message(Message * m)165 void add_message(Message *m)
166 {
167     if (is_duplicate(m)) {
168         free_message(m);
169     } else {
170         ptr_array_add(&msgs, m);
171     }
172 }
173 
activate_current_message(void)174 void activate_current_message(void)
175 {
176     if (msg_pos == msgs.count) {
177         return;
178     }
179     const Message *m = msgs.ptrs[msg_pos];
180     if (m->loc != NULL && m->loc->filename != NULL) {
181         if (!file_location_go(m->loc)) {
182             // Error message is visible
183             return;
184         }
185     }
186     if (msgs.count == 1) {
187         info_msg("%s", m->msg);
188     } else {
189         info_msg("[%zu/%zu] %s", msg_pos + 1, msgs.count, m->msg);
190     }
191 }
192 
activate_next_message(void)193 void activate_next_message(void)
194 {
195     if (msg_pos + 1 < msgs.count) {
196         msg_pos++;
197     }
198     activate_current_message();
199 }
200 
activate_prev_message(void)201 void activate_prev_message(void)
202 {
203     if (msg_pos > 0) {
204         msg_pos--;
205     }
206     activate_current_message();
207 }
208 
clear_messages(void)209 void clear_messages(void)
210 {
211     ptr_array_free_cb(&msgs, FREE_FUNC(free_message));
212     msg_pos = 0;
213 }
214 
message_count(void)215 size_t message_count(void)
216 {
217     return msgs.count;
218 }
219