Fix a random crash when vim modelines are used
[anjuta.git] / libanjuta / anjuta-modeline.c
blob7694d74b946b8fb34d43e96a672c6f981dcabed3
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta-modeline.c
4 * Copyright (C) Sébastien Granjoux 2013 <seb.sfo@free.fr>
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 /**
21 * SECTION:anjuta-modeline
22 * @short_description: Parse editor mode line
23 * @see_also:
24 * @stability: Unstable
25 * @include: libanjuta/anjuta-modeline.h
29 #include "anjuta-modeline.h"
31 #include "anjuta-debug.h"
33 #include <glib-object.h>
35 #include <stdlib.h>
36 #include <string.h>
38 /* Types declarations
39 *---------------------------------------------------------------------------*/
41 enum {
42 SET_USE_SPACES = 1 << 0,
43 SET_STATEMENT_INDENTATION = 1 << 1,
44 SET_TAB_SIZE = 1 << 2,
45 CHECK_NEXT = 1 << 4
48 typedef struct {
49 gint settings;
51 gint use_spaces;
52 gint statement_indentation;
53 gint tab_size;
54 } IndentationParams;
57 /* Helpers functions
58 *---------------------------------------------------------------------------*/
60 /* Private functions
61 *---------------------------------------------------------------------------*/
63 static gchar *
64 get_editor_line (IAnjutaEditor *editor, gint line)
66 IAnjutaIterable *start;
67 IAnjutaIterable *end;
68 gchar *content = NULL;
70 if (line < 0)
72 gint last;
74 end = ianjuta_editor_get_end_position(editor, NULL);
75 last = ianjuta_editor_get_line_from_position (editor, end, NULL);
76 line = last + line;
77 g_object_unref (end);
79 if (line > 0)
81 start = ianjuta_editor_get_line_begin_position (editor, line, NULL);
82 end = ianjuta_editor_get_line_end_position (editor, line, NULL);
83 content = ianjuta_editor_get_text (editor, start, end, NULL);
84 g_object_unref (start);
85 g_object_unref (end);
88 return content;
91 static void
92 set_vim_params (IndentationParams *params, const gchar *key, const gchar *value)
94 //DEBUG_PRINT ("Setting indent param: %s = %s", key, value);
95 if ((strcmp (key, "expandtab") == 0) ||
96 (strcmp (key, "et") == 0))
98 params->use_spaces = 1;
99 params->settings |= SET_USE_SPACES;
101 else if ((strcmp (key, "noexpandtab") == 0) ||
102 (strcmp (key, "noet") == 0))
104 params->use_spaces = 0;
105 params->settings |= SET_USE_SPACES;
107 else if ((strcmp (key, "shiftwidth") == 0) ||
108 (strcmp (key, "sw") == 0))
110 params->statement_indentation = atoi (value);
111 params->settings |= SET_STATEMENT_INDENTATION;
113 else if ((strcmp (key, "softtabstop") == 0) ||
114 (strcmp (key, "sts") == 0) ||
115 (strcmp (key, "tabstop") == 0) ||
116 (strcmp (key, "ts") == 0))
118 params->tab_size = atoi (value);
119 params->settings |= SET_TAB_SIZE;
123 static gboolean
124 parse_vim_modeline (IndentationParams *params, const gchar *line, gint linenum)
126 gchar *ptr;
127 const gchar *end;
128 gchar *key;
129 gchar *value;
131 /* Check the first 5 and last 5 lines */
132 if ((linenum < -5) || (linenum == 0) || (linenum > 5))
134 return FALSE;
137 ptr = strstr (line, "vim:");
138 if (ptr == NULL)
140 if ((linenum != -5) && (linenum != 5)) params->settings = CHECK_NEXT;
141 return FALSE;
143 ptr += 4;
144 while (g_ascii_isspace (*ptr)) ptr++;
145 if (strncmp (ptr, "set", 3) != 0)
147 if ((linenum != -5) && (linenum != 5)) params->settings = CHECK_NEXT;
148 return FALSE;
150 ptr += 3;
152 for (end = ptr; *end != '\0'; end++)
154 if ((*end == ':') && (*(end-1) != '\\')) break;
157 while (ptr != end)
159 gchar sep;
161 while (g_ascii_isspace (*ptr)) ptr++;
162 if (ptr == end) break;
164 /* Get key */
165 key = ptr++;
166 value = NULL;
167 while ((ptr != end) && (*ptr != '=') && !g_ascii_isspace(*ptr)) ptr++;
168 sep = *ptr;
169 *ptr = '\0';
171 if (sep == '=')
173 /* Get value */
174 value = ++ptr;
175 while ((ptr != end) && !g_ascii_isspace(*ptr)) ptr++;
176 sep = *ptr;
177 *ptr = '\0';
179 if (sep != '\0') ptr++;
182 set_vim_params (params, key, value);
185 return TRUE;
188 static void
189 set_emacs_params (IndentationParams *params, const gchar *key, const gchar *value)
191 //DEBUG_PRINT ("Setting indent param: %s = %s", key, value);
192 if (strcmp (key, "indent-tabs-mode") == 0)
194 if (strcmp (value, "t") == 0)
196 params->use_spaces = 0;
197 params->settings |= SET_USE_SPACES;
199 else if (strcmp (value, "nil") == 0)
201 params->use_spaces = 1;
202 params->settings |= SET_USE_SPACES;
205 else if ((strcmp (key, "c-basic-offset") == 0) ||
206 (strcmp (key, "indent-offset") == 0))
208 params->statement_indentation = atoi (value);
209 params->settings |= SET_STATEMENT_INDENTATION;
211 else if (strcasecmp (key, "tab-width") == 0)
213 params->tab_size = atoi (value);
214 params->settings |= SET_TAB_SIZE;
218 static gboolean
219 parse_emacs_modeline (IndentationParams *params, gchar *line, gint linenum)
221 gchar *ptr;
222 gchar *end;
223 gchar *key;
224 gchar *value;
226 if (linenum == 1)
228 /* If first line is a shebang, check second line */
229 if ((line[0] == '#') && (line[1] =='!'))
231 params->settings |= CHECK_NEXT;
232 return FALSE;
235 else if (linenum != 2)
237 /* Check only the 2 first lines */
238 return FALSE;
241 ptr = strstr (line, "-*-");
242 if (ptr == NULL) return FALSE;
243 ptr += 3;
244 end = strstr (ptr, "-*-");
245 if (end == NULL) return FALSE;
246 *end = '\0';
248 while (*ptr != '\0')
250 gchar sep;
252 while (g_ascii_isspace (*ptr)) ptr++;
253 if (*ptr == '\0') break;
255 /* Get key */
256 key = ptr++;
257 value = NULL;
258 while ((*ptr != '\0') && (*ptr != ':') && (*ptr != ';')) ptr++;
259 sep = *ptr;
261 end = ptr - 1;
262 while (g_ascii_isspace (*end)) end--;
263 *(end + 1) = '\0';
265 if (sep == ':')
267 /* Get value */
268 ptr++;
269 while (g_ascii_isspace (*ptr)) ptr++;
270 if (*ptr != '\0')
272 value = ptr;
273 while ((*ptr != '\0') && (*ptr != ';')) ptr++;
274 sep = *ptr;
276 end = ptr - 1;
277 while (g_ascii_isspace (*end)) end--;
278 *(end + 1) = '\0';
280 if (sep == ';') ptr++;
284 set_emacs_params (params, key, value);
287 return TRUE;
291 static gboolean
292 set_indentation (IAnjutaEditor *editor, IndentationParams *params)
294 if (params->settings == 0) return FALSE;
296 if (params->settings & SET_USE_SPACES)
297 ianjuta_editor_set_use_spaces (editor, params->use_spaces, NULL);
299 if (params->settings & SET_STATEMENT_INDENTATION)
300 ianjuta_editor_set_indentsize (editor, params->statement_indentation, NULL);
302 if (params->settings & SET_TAB_SIZE)
303 ianjuta_editor_set_tabsize (editor, params->tab_size, NULL);
305 return TRUE;
309 /* Public functions
310 *---------------------------------------------------------------------------*/
314 * anjuta_apply_modeline:
315 * @editor: #IAnjutaEditor object
317 * Check the editor buffer to find a mode line and update the indentation
318 * settings if found.
320 * The mode line is special line used by the text editor to define settings for
321 * the current file, typically indentation. Anjuta currently recognize two kinds
322 * of mode line:
324 * Emacs mode line, on the first or the second line if the first one is a
325 * shebang (#!) with the following format:
326 * -*- key1: value1; key2: value2 -*-
328 * Vim mode line, one the first 5 or the last 5 lines with the following format:
329 * vim:set key1=value1 key2=value2
331 * Returns: %TRUE if a mode line has been found and applied.
333 gboolean
334 anjuta_apply_modeline (IAnjutaEditor *editor)
336 IndentationParams params = {CHECK_NEXT,0,0,0};
337 gint line;
338 gchar *content = NULL;
340 g_return_val_if_fail (IANJUTA_IS_EDITOR (editor), FALSE);
342 /* Check the first lines */
343 for (line = 1; params.settings == CHECK_NEXT; line++)
345 g_free (content);
346 content = get_editor_line (editor, line);
347 if (content == NULL) return FALSE;
349 params.settings = 0;
350 if (parse_vim_modeline (&params, content, line)) break;
351 if (parse_emacs_modeline (&params, content, line)) break;
354 /* Check the last lines */
355 if (params.settings == 0) params.settings = CHECK_NEXT;
356 for (line = -1;params.settings == CHECK_NEXT; line--)
358 g_free (content);
359 content = get_editor_line (editor, line);
360 if (content == NULL) return FALSE;
362 params.settings = 0;
363 if (parse_vim_modeline (&params, content, line)) break;
364 if (parse_emacs_modeline (&params, content, line)) break;
366 g_free (content);
368 /* Set indentation settings */
369 return set_indentation (editor, &params);