1 /* ide-xml-completion-values.c
2 *
3 * Copyright 2017 Sebastien Lafargue <slafargue@gnome.org>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21 #include <dazzle.h>
22
23 #include "ide-xml-completion-values.h"
24 #include "ide-xml-position.h"
25
26 typedef struct
27 {
28 IdeXmlRngDefine *define;
29 gchar *values;
30 gchar *prefix;
31
32 guint is_initial_state : 1;
33 } MatchingState;
34
35 static GPtrArray * process_matching_state (MatchingState *state,
36 IdeXmlRngDefine *define);
37
38 static ValueMatchItem *
value_match_item_new(const gchar * value)39 value_match_item_new (const gchar *value)
40 {
41 ValueMatchItem *item;
42
43 g_assert (!dzl_str_empty0 (value));
44
45 item = g_slice_new0 (ValueMatchItem);
46 item->name = g_strdup (value);
47
48 return item;
49 }
50
51 static void
value_match_item_free(gpointer data)52 value_match_item_free (gpointer data)
53 {
54 ValueMatchItem *item = (ValueMatchItem *)data;
55
56 g_clear_pointer (&item->name, g_free);
57 g_slice_free (ValueMatchItem, item);
58 }
59
60 static GPtrArray *
match_values_new(void)61 match_values_new (void)
62 {
63 GPtrArray *ar;
64
65 ar = g_ptr_array_new_with_free_func ((GDestroyNotify)value_match_item_free);
66 return ar;
67 }
68
69 static void
match_values_add(GPtrArray * to_values,GPtrArray * from_values)70 match_values_add (GPtrArray *to_values,
71 GPtrArray *from_values)
72 {
73 ValueMatchItem *to_item;
74 ValueMatchItem *from_item;
75
76 g_assert (to_values != NULL);
77
78 if (from_values == NULL)
79 return;
80
81 for (gint i = 0; i < from_values->len; ++i)
82 {
83 from_item = g_ptr_array_index (from_values, i);
84 to_item = value_match_item_new (from_item->name);
85 g_ptr_array_add (to_values, to_item);
86 }
87 }
88
89 static MatchingState *
matching_state_new(IdeXmlRngDefine * define,const gchar * values,const gchar * prefix)90 matching_state_new (IdeXmlRngDefine *define,
91 const gchar *values,
92 const gchar *prefix)
93 {
94 MatchingState *state;
95
96 g_assert (define != NULL);
97
98 state = g_slice_new0 (MatchingState);
99
100 state->define = define;
101 state->values = g_strdup (values);
102 state->prefix = g_strdup (prefix);
103
104 state->is_initial_state = FALSE;
105
106 return state;
107 }
108
109 static void
matching_state_free(MatchingState * state)110 matching_state_free (MatchingState *state)
111 {
112 g_clear_pointer (&state->values, g_free);
113 g_clear_pointer (&state->prefix, g_free);
114 g_slice_free (MatchingState, state);
115 }
116
117 static GPtrArray *
process_value(MatchingState * state)118 process_value (MatchingState *state)
119 {
120 GPtrArray *match_values = NULL;
121 ValueMatchItem *item;
122 const gchar *value;
123
124 g_assert (state->define->type == IDE_XML_RNG_DEFINE_VALUE);
125
126 value = (gchar *)state->define->name;
127
128 if (!dzl_str_empty0 (value) &&
129 (dzl_str_empty0 (state->prefix) || g_str_has_prefix (value, state->prefix)))
130 {
131 match_values = match_values_new ();
132 item = value_match_item_new (value);
133 g_ptr_array_add (match_values, item);
134 }
135
136 return match_values;
137 }
138
139 static GPtrArray *
process_choice(MatchingState * state)140 process_choice (MatchingState *state)
141 {
142 GPtrArray *match_values = NULL;
143 IdeXmlRngDefine *defines;
144
145 g_assert (state->define->type == IDE_XML_RNG_DEFINE_CHOICE);
146
147 if (NULL != (defines = state->define->content))
148 {
149 match_values = match_values_new ();
150 while (defines != NULL)
151 {
152 g_autoptr (GPtrArray) match = NULL;
153
154 if (NULL != (match = process_matching_state (state, defines)))
155 {
156 /* TODO: use move */
157 match_values_add (match_values, match);
158 }
159
160 defines = defines->next;
161 }
162 }
163
164 return match_values;
165 }
166
167 static GPtrArray *
process_group(MatchingState * state)168 process_group (MatchingState *state)
169 {
170 GPtrArray *match_values = NULL;
171 IdeXmlRngDefine *defines;
172
173 g_assert (state->define->type == IDE_XML_RNG_DEFINE_GROUP ||
174 state->define->type == IDE_XML_RNG_DEFINE_ATTRIBUTE ||
175 state->define->type == IDE_XML_RNG_DEFINE_ZEROORMORE ||
176 state->define->type == IDE_XML_RNG_DEFINE_ONEORMORE ||
177 state->define->type == IDE_XML_RNG_DEFINE_OPTIONAL);
178
179 if (NULL != (defines = state->define->content))
180 {
181 while (defines != NULL)
182 {
183 g_autoptr (GPtrArray) match = NULL;
184
185 match_values = match_values_new ();
186 if (NULL != (match = process_matching_state (state, defines)))
187 {
188 match_values_add (match_values, match);
189 }
190
191 defines = defines->next;
192 }
193 }
194
195 return match_values;
196 }
197
198 static GPtrArray *
process_matching_state(MatchingState * state,IdeXmlRngDefine * define)199 process_matching_state (MatchingState *state,
200 IdeXmlRngDefine *define)
201 {
202 IdeXmlRngDefine *old_define;
203 IdeXmlRngDefineType type;
204 GPtrArray *match_values = NULL;
205
206 g_assert (state != NULL);
207 g_assert (define != NULL);
208
209 old_define = state->define;
210 state->define = define;
211
212 if (state->is_initial_state)
213 {
214 state->is_initial_state = FALSE;
215 type = IDE_XML_RNG_DEFINE_GROUP;
216 }
217 else
218 type = define->type;
219
220 switch (type)
221 {
222 case IDE_XML_RNG_DEFINE_VALUE:
223 match_values = process_value (state);
224 break;
225
226 case IDE_XML_RNG_DEFINE_ATTRIBUTE:
227 case IDE_XML_RNG_DEFINE_ATTRIBUTES_GROUP:
228 case IDE_XML_RNG_DEFINE_NOOP:
229 case IDE_XML_RNG_DEFINE_NOTALLOWED:
230 case IDE_XML_RNG_DEFINE_TEXT:
231 case IDE_XML_RNG_DEFINE_DATATYPE:
232 case IDE_XML_RNG_DEFINE_EMPTY:
233 case IDE_XML_RNG_DEFINE_ELEMENT:
234 case IDE_XML_RNG_DEFINE_START:
235 case IDE_XML_RNG_DEFINE_PARAM:
236 case IDE_XML_RNG_DEFINE_EXCEPT:
237 case IDE_XML_RNG_DEFINE_LIST:
238 match_values = NULL;
239 break;
240
241 case IDE_XML_RNG_DEFINE_DEFINE:
242 case IDE_XML_RNG_DEFINE_REF:
243 case IDE_XML_RNG_DEFINE_PARENTREF:
244 case IDE_XML_RNG_DEFINE_EXTERNALREF:
245 match_values = process_matching_state (state, define->content);
246 break;
247
248 case IDE_XML_RNG_DEFINE_INTERLEAVE:
249 case IDE_XML_RNG_DEFINE_GROUP:
250 case IDE_XML_RNG_DEFINE_ZEROORMORE:
251 case IDE_XML_RNG_DEFINE_ONEORMORE:
252 case IDE_XML_RNG_DEFINE_OPTIONAL:
253 match_values = process_group (state);
254 break;
255
256 case IDE_XML_RNG_DEFINE_CHOICE:
257 match_values = process_choice (state);
258 break;
259
260 default:
261 g_assert_not_reached ();
262 }
263
264 state->define = old_define;
265
266 return match_values;
267 }
268
269 static MatchingState *
create_initial_matching_state(IdeXmlRngDefine * define,const gchar * values,const gchar * prefix)270 create_initial_matching_state (IdeXmlRngDefine *define,
271 const gchar *values,
272 const gchar *prefix)
273 {
274 MatchingState *state;
275
276 g_assert (define != NULL);
277
278 state = matching_state_new (define, values, prefix);
279 state->is_initial_state = TRUE;
280
281 return state;
282 }
283
284 /* Return an array of ValueMatchItem */
285 GPtrArray *
ide_xml_completion_values_get_matches(IdeXmlRngDefine * define,const gchar * values,const gchar * prefix)286 ide_xml_completion_values_get_matches (IdeXmlRngDefine *define,
287 const gchar *values,
288 const gchar *prefix)
289 {
290 MatchingState *initial_state;
291 GPtrArray *match_values = NULL;
292
293 g_return_val_if_fail (define != NULL, NULL);
294
295 if (define->content != NULL)
296 {
297 initial_state = create_initial_matching_state (define, values, prefix);
298
299 initial_state->is_initial_state = TRUE;
300 match_values = process_matching_state (initial_state, define);
301 matching_state_free (initial_state);
302 }
303
304 return match_values;
305 }
306