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