message-view: bgo #727634 - Cannot copy build output
[anjuta.git] / plugins / indentation-python-style / python-indentation.c
blobd7cf2b84ee1908ad7dac437b951b6a11171b3701
1 /*
2 * python-indentation.c
4 * Copyright (C) 2011 - Johannes Schmid
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <ctype.h>
21 #include <stdlib.h>
22 #include <ctype.h>
23 #include <libanjuta/anjuta-shell.h>
24 #include <libanjuta/anjuta-debug.h>
25 #include <libanjuta/anjuta-launcher.h>
26 #include <libanjuta/anjuta-preferences.h>
27 #include <libanjuta/anjuta-modeline.h>
28 #include <libanjuta/interfaces/ianjuta-iterable.h>
29 #include <libanjuta/interfaces/ianjuta-document.h>
30 #include <libanjuta/interfaces/ianjuta-document-manager.h>
31 #include <libanjuta/interfaces/ianjuta-editor.h>
32 #include <libanjuta/interfaces/ianjuta-file.h>
33 #include <libanjuta/interfaces/ianjuta-editor-cell.h>
34 #include <libanjuta/interfaces/ianjuta-editor-language.h>
35 #include <libanjuta/interfaces/ianjuta-editor-selection.h>
36 #include <libanjuta/interfaces/ianjuta-editor-assist.h>
37 #include <libanjuta/interfaces/ianjuta-preferences.h>
38 #include <libanjuta/interfaces/ianjuta-symbol.h>
39 #include <libanjuta/interfaces/ianjuta-language.h>
41 #include "python-indentation.h"
43 #define PREF_INDENT_AUTOMATIC "indent-automatic"
44 #define PREF_INDENT_ADAPTIVE "indent-adaptive"
45 #define PREF_INDENT_TAB_INDENTS "indent-tab-indents"
46 #define PREF_INDENT_BRACE_SIZE "indent-brace-size"
48 #define TAB_SIZE (ianjuta_editor_get_tabsize (editor, NULL))
50 #define USE_SPACES_FOR_INDENTATION (ianjuta_editor_get_use_spaces (editor, NULL))
52 #define INDENT_SIZE (ianjuta_editor_get_indentsize (editor, NULL))
54 #define BRACE_INDENT \
55 (plugin->param_brace_indentation >= 0? \
56 plugin->param_brace_indentation : \
57 g_settings_get_int (plugin->settings, PREF_INDENT_BRACE_SIZE))
59 #define CASE_INDENT (INDENT_SIZE)
60 #define LABEL_INDENT (INDENT_SIZE)
62 static gboolean
63 iter_is_newline (IAnjutaIterable *iter, gchar ch)
65 if (ch == '\n' || ch == '\r')
66 return TRUE;
67 return FALSE;
70 /* Returns TRUE if iter was moved */
71 static gboolean
72 skip_iter_to_newline_head (IAnjutaIterable *iter, gchar ch)
74 gboolean ret_val = FALSE;
76 if (ch == '\n')
78 /* Possibly at tail */
79 if (ianjuta_iterable_previous (iter, NULL))
81 ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter),
82 0, NULL);
83 if (ch != '\r')
84 /* Already at head, undo iter */
85 ianjuta_iterable_next (iter, NULL);
86 else
87 /* Correctly at head */
88 ret_val = TRUE;
91 return ret_val;
94 /* Returns TRUE if iter was moved */
95 static gboolean
96 skip_iter_to_newline_tail (IAnjutaIterable *iter, gchar ch)
98 gboolean ret_val = FALSE;
100 if (ch == '\r')
102 /* Possibly at head */
103 if (ianjuta_iterable_previous (iter, NULL))
105 ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter),
106 0, NULL);
107 if (ch != '\n')
108 /* Already at tail, undo iter */
109 ianjuta_iterable_next (iter, NULL);
110 else
111 /* Correctly at tail */
112 ret_val = TRUE;
115 return ret_val;
118 /* Jumps to the reverse matching brace of the given brace character */
120 static gint
121 get_line_indentation (IAnjutaEditor *editor, gint line_num)
123 IAnjutaIterable *line_begin, *line_end;
124 gchar *line_string, *idx;
125 gint line_indent = 0;
127 line_begin = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
128 line_end = ianjuta_editor_get_line_end_position (editor, line_num, NULL);
130 DEBUG_PRINT ("%s: line begin = %d, line end = %d", __FUNCTION__,
131 line_begin, line_end);
133 if (ianjuta_iterable_compare (line_begin, line_end, NULL) == 0)
135 g_object_unref (line_begin);
136 g_object_unref (line_end);
137 return 0;
140 line_string = ianjuta_editor_get_text (editor, line_begin, line_end,
141 NULL);
142 g_object_unref (line_begin);
143 g_object_unref (line_end);
145 /* DEBUG_PRINT ("line_string = '%s'", line_string); */
147 if (!line_string)
148 return 0;
150 idx = line_string;
152 /* Find first non-white space */
153 while (*idx != '\0' && isspace (*idx))
155 if (*idx == '\t')
156 line_indent += TAB_SIZE;
157 else
158 line_indent++;
159 idx++; /* Since we are looking for first non-space char, simple
160 * increment of the utf8 chars would do */
162 g_free (line_string);
163 return line_indent;
166 static gchar *
167 get_line_indentation_string (IAnjutaEditor *editor, gint spaces, gint line_indent_spaces)
169 gint i;
170 gchar *indent_string;
172 if ((spaces + line_indent_spaces) <= 0)
173 return NULL;
175 if (USE_SPACES_FOR_INDENTATION)
177 indent_string = g_new0 (gchar, spaces + line_indent_spaces + 1);
178 for (i = 0; i < (spaces + line_indent_spaces); i++)
179 indent_string[i] = ' ';
181 else
183 gint num_tabs = spaces / TAB_SIZE;
184 gint num_spaces = spaces % TAB_SIZE;
185 indent_string = g_new0 (gchar, num_tabs + num_spaces + line_indent_spaces + 1);
187 for (i = 0; i < num_tabs; i++)
188 indent_string[i] = '\t';
189 for (; i < num_tabs + (num_spaces + line_indent_spaces); i++)
190 indent_string[i] = ' ';
192 return indent_string;
196 void
197 python_indent_init (IndentPythonPlugin* plugin)
200 /* Initialize indentation parameters */
201 plugin->param_brace_indentation = -1;
202 plugin->param_case_indentation = -1;
203 plugin->param_label_indentation = -1;
205 anjuta_apply_modeline (IANJUTA_EDITOR (plugin->current_editor));
208 static gint
209 set_line_indentation (IAnjutaEditor *editor, gint line_num, gint indentation, gint line_indent_spaces)
211 IAnjutaIterable *line_begin, *line_end, *indent_position;
212 IAnjutaIterable *current_pos;
213 gint carat_offset, nchars = 0;
214 gchar *old_indent_string = NULL, *indent_string = NULL;
216 /* DEBUG_PRINT ("In %s()", __FUNCTION__); */
217 line_begin = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
218 line_end = ianjuta_editor_get_line_end_position (editor, line_num, NULL);
221 DEBUG_PRINT ("line begin = %d, line end = %d, current_pos = %d",
222 line_begin, line_end, current_pos);
224 indent_position = ianjuta_iterable_clone (line_begin, NULL);
226 if (ianjuta_iterable_compare (line_end, line_begin, NULL) > 0)
228 gchar *idx;
229 gchar *line_string = ianjuta_editor_get_text (editor, line_begin,
230 line_end, NULL);
232 //DEBUG_PRINT ("line_string = '%s'", line_string);
233 if (line_string)
235 idx = line_string;
237 /* Find first non-white space */
238 while (*idx != '\0' && isspace (*idx))
240 idx = g_utf8_find_next_char (idx, NULL);
241 ianjuta_iterable_next (indent_position, NULL);
243 g_free (line_string);
246 /* Indent iter defined at this point, Identify how much is current
247 * position is beyound this point. We need to restore it later after
248 * indentation
250 current_pos = ianjuta_editor_get_position (editor, NULL);
251 carat_offset = ianjuta_iterable_diff (indent_position, current_pos, NULL);
252 //DEBUG_PRINT ("carat offset is = %d", carat_offset);
254 /* Set new indentation */
255 if ((indentation + line_indent_spaces) > 0)
257 indent_string = get_line_indentation_string (editor, indentation, line_indent_spaces);
258 nchars = indent_string ? g_utf8_strlen (indent_string, -1) : 0;
260 /* Only indent if there is something to indent with */
261 if (indent_string)
263 /* Get existing indentation */
264 if (ianjuta_iterable_compare (indent_position, line_begin, NULL) > 0)
266 old_indent_string =
267 ianjuta_editor_get_text (editor, line_begin,
268 indent_position, NULL);
270 //DEBUG_PRINT ("old_indent_string = '%s'", old_indent_string);
273 /* Only indent if there was no indentation before or old
274 * indentation string was different from the new indent string
276 if (old_indent_string == NULL ||
277 strcmp (old_indent_string, indent_string) != 0)
279 /* Remove the old indentation string, if there is any */
280 if (old_indent_string)
281 ianjuta_editor_erase (editor, line_begin,
282 indent_position, NULL);
284 /* Insert the new indentation string */
285 ianjuta_editor_insert (editor, line_begin,
286 indent_string, -1, NULL);
291 /* If indentation == 0, we really didn't enter the previous code block,
292 * but we may need to clear existing indentation.
294 if ((indentation + line_indent_spaces) == 0)
296 /* Get existing indentation */
297 if (ianjuta_iterable_compare (indent_position, line_begin, NULL) > 0)
299 old_indent_string =
300 ianjuta_editor_get_text (editor, line_begin,
301 indent_position, NULL);
303 if (old_indent_string)
304 ianjuta_editor_erase (editor, line_begin, indent_position, NULL);
307 /* Restore current position */
308 if (carat_offset >= 0)
310 /* If the cursor was not before the first non-space character in
311 * the line, restore it's position after indentation.
313 gint i;
314 IAnjutaIterable *pos = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
315 for (i = 0; i < nchars + carat_offset; i++)
316 ianjuta_iterable_next (pos, NULL);
317 ianjuta_editor_goto_position (editor, pos, NULL);
318 g_object_unref (pos);
320 else /* cursor_offset < 0 */
322 /* If the cursor was somewhere in the old indentation spaces,
323 * home the cursor to first non-space character in the line (or
324 * end of line if there is no non-space characters in the line.
326 gint i;
327 IAnjutaIterable *pos = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
328 for (i = 0; i < nchars; i++)
329 ianjuta_iterable_next (pos, NULL);
330 ianjuta_editor_goto_position (editor, pos, NULL);
331 g_object_unref (pos);
334 g_object_unref (current_pos);
335 g_object_unref (indent_position);
336 g_object_unref (line_begin);
337 g_object_unref (line_end);
339 g_free (old_indent_string);
340 g_free (indent_string);
341 return nchars;
344 /* incomplete_statement:
345 * 1 == COMPLETE STATEMENT
346 * 0 == INCOMPLETE STATEMENT
347 * -1 == UNKNOWN
350 static gchar*
351 get_current_statement (IAnjutaEditor *editor, gint line_num)
353 gchar point_ch;
354 IAnjutaIterable *iter = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
355 GString* statement = g_string_new (NULL);
359 point_ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter), 0, NULL);
361 if (!ianjuta_iterable_next (iter, NULL) )
362 break;
364 while (g_ascii_isspace (point_ch) && point_ch != '\n');
366 if (!ianjuta_iterable_previous (iter, NULL))
368 g_object_unref (iter);
369 g_string_free (statement, TRUE);
370 return g_strdup("");
375 point_ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter), 0, NULL);
376 g_string_append_c (statement, point_ch);
378 if (!ianjuta_iterable_next (iter, NULL) )
379 break;
381 while (g_ascii_isalpha(point_ch) || g_ascii_isdigit(point_ch));
383 g_object_unref (iter);
384 return g_string_free (statement, FALSE);
387 static gchar
388 get_last_char (IAnjutaEditor *editor, gint line_num, gint *found_line_num)
390 gchar point_ch;
391 IAnjutaIterable *iter = ianjuta_editor_get_line_end_position (editor, line_num, NULL);
395 if (ianjuta_iterable_previous (iter, NULL) )
397 point_ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter), 0,
398 NULL);
400 else
401 break;
403 while (point_ch == ' ' || point_ch == '\n' || point_ch == '\r' || point_ch == '\t'); // Whitespace
405 *found_line_num = ianjuta_editor_get_line_from_position (editor, iter, NULL);
406 g_object_unref (iter);
407 return point_ch;
410 static gboolean
411 spaces_only (IAnjutaEditor* editor, IAnjutaIterable* begin, IAnjutaIterable* end);
413 static gboolean
414 is_spaces_only (IAnjutaEditor *editor, gint line_num)
416 IAnjutaIterable* begin = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
417 IAnjutaIterable* end = ianjuta_editor_get_line_end_position (editor, line_num , NULL);
419 if (spaces_only (editor, begin, end))
421 return TRUE;
424 return FALSE;
427 static gint
428 get_line_indentation_base (IndentPythonPlugin *plugin,
429 IAnjutaEditor *editor,
430 gint line_num,
431 gint *incomplete_statement,
432 gint *line_indent_spaces,
433 gboolean *colon_indent)
435 gchar point_ch;
436 gint line_indent = 0;
437 gint currentline = line_num - 1;
438 gchar *statement;
439 gchar *current_statement;
441 *incomplete_statement = 0;
442 *line_indent_spaces = 0;
444 if (currentline <= 1)
445 return 0;
447 point_ch = get_last_char (editor, currentline, &currentline);
448 statement = get_current_statement (editor, currentline);
449 current_statement = get_current_statement (editor, line_num);
451 if (g_str_equal(statement, "return") ||
452 g_str_equal(statement, "break") ||
453 g_str_equal(statement, "pass") ||
454 g_str_equal(statement, "raise") ||
455 g_str_equal(statement, "continue"))
457 if (get_line_indentation (editor, currentline)>= INDENT_SIZE)
458 line_indent = get_line_indentation (editor, currentline) - INDENT_SIZE;
461 else if ((g_str_has_prefix(current_statement, "def") && point_ch != ':') ||
462 g_str_has_prefix(current_statement, "else") ||
463 g_str_has_prefix(current_statement, "elif") ||
464 g_str_has_prefix(current_statement, "except") ||
465 g_str_has_prefix(current_statement, "finally"))
467 if (get_line_indentation (editor, currentline)>= INDENT_SIZE)
468 line_indent = get_line_indentation (editor, currentline) - INDENT_SIZE;
470 else if (point_ch == ':')
472 line_indent = get_line_indentation (editor, currentline) + INDENT_SIZE;
474 else
476 gint line = currentline;
477 while (is_spaces_only(editor, line) && line >= 0)
478 line--;
479 line_indent = get_line_indentation (editor, line);
482 g_free (statement);
483 g_free (current_statement);
485 return line_indent;
488 static gboolean
489 spaces_only (IAnjutaEditor* editor, IAnjutaIterable* begin, IAnjutaIterable* end)
491 gboolean empty = TRUE;
492 gchar* idx;
493 gchar* text = ianjuta_editor_get_text (editor, begin, end, NULL);
495 if (text == NULL)
496 return TRUE;
499 for (idx = text; *idx != '\0'; idx++)
501 if (!isspace(*idx))
503 empty = FALSE;
504 break;
507 g_free(text);
508 return empty;
511 static gint
512 get_line_auto_indentation (IndentPythonPlugin *plugin, IAnjutaEditor *editor,
513 gint line, gint *line_indent_spaces)
515 IAnjutaIterable *iter;
516 gint line_indent = 0;
517 gint incomplete_statement = -1;
518 gboolean colon_indent = FALSE;
520 g_return_val_if_fail (line > 0, 0);
522 if (line == 1) /* First line */
524 return 0;
526 else
528 IAnjutaIterable* begin = ianjuta_editor_get_line_begin_position (editor, line -1 , NULL);
529 IAnjutaIterable* end = ianjuta_editor_get_line_end_position (editor, line -1 , NULL);
531 if (spaces_only (editor, begin, end))
533 set_line_indentation (editor, line -1, 0, 0);
535 g_object_unref (begin);
536 g_object_unref (end);
539 iter = ianjuta_editor_get_line_begin_position (editor, line, NULL);
541 line_indent = get_line_indentation_base (plugin, editor, line,
542 &incomplete_statement,
543 line_indent_spaces,
544 &colon_indent);
546 /* Determine what the first non-white char in the line is */
549 gchar ch;
550 /* Check if we are *inside* comment or string. Begining of comment
551 * or string does not count as inside. If inside, just align with
552 * previous indentation.
554 ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter),
555 0, NULL);
556 if (iter_is_newline (iter, ch))
558 skip_iter_to_newline_tail (iter, ch);
560 /* First levels are excused from incomplete statement indent */
561 if (incomplete_statement == 1 && line_indent > 0)
562 line_indent += INDENT_SIZE;
563 break;
566 else if (!isspace (ch))
568 /* First levels are excused from incomplete statement indent */
569 if (incomplete_statement == 1 && line_indent > 0)
570 line_indent += INDENT_SIZE;
571 break;
575 while (ianjuta_iterable_next (iter, NULL));
576 g_object_unref (iter);
578 return line_indent;
581 void
582 python_indent (IndentPythonPlugin *plugin,
583 IAnjutaEditor *editor,
584 IAnjutaIterable *insert_pos,
585 gchar ch)
587 IAnjutaIterable *iter;
588 gboolean should_auto_indent = FALSE;
590 iter = ianjuta_iterable_clone (insert_pos, NULL);
592 /* If autoindent is enabled*/
593 if (g_settings_get_boolean (plugin->settings, PREF_INDENT_AUTOMATIC))
595 if (iter_is_newline (iter, ch))
597 skip_iter_to_newline_head (iter, ch);
598 /* All newline entries means enable indenting */
599 should_auto_indent = TRUE;
602 if (should_auto_indent)
604 gint insert_line;
605 gint line_indent;
606 gint line_indent_spaces;
608 ianjuta_document_begin_undo_action (IANJUTA_DOCUMENT(editor), NULL);
609 python_indent_init (plugin);
611 insert_line = ianjuta_editor_get_lineno (editor, NULL);
612 line_indent = get_line_auto_indentation (plugin, editor, insert_line, &line_indent_spaces);
613 set_line_indentation (editor, insert_line, line_indent, line_indent_spaces);
614 ianjuta_document_end_undo_action (IANJUTA_DOCUMENT(editor), NULL);
619 g_object_unref (iter);
622 void
623 python_indent_auto (IndentPythonPlugin* lang_plugin,
624 IAnjutaIterable* start,
625 IAnjutaIterable* end)
627 gint line_start, line_end;
628 gint insert_line;
629 gint line_indent;
630 gboolean has_selection;
632 IAnjutaEditor *editor;
633 editor = IANJUTA_EDITOR (lang_plugin->current_editor);
635 if (!start || !end)
637 has_selection = ianjuta_editor_selection_has_selection
638 (IANJUTA_EDITOR_SELECTION (editor), NULL);
639 if (has_selection)
641 IAnjutaIterable *sel_start, *sel_end;
642 sel_start = ianjuta_editor_selection_get_start (IANJUTA_EDITOR_SELECTION (editor),
643 NULL);
644 sel_end = ianjuta_editor_selection_get_end (IANJUTA_EDITOR_SELECTION (editor),
645 NULL);
646 line_start = ianjuta_editor_get_line_from_position (editor, sel_start, NULL);
647 line_end = ianjuta_editor_get_line_from_position (editor, sel_end, NULL);
648 g_object_unref (sel_start);
649 g_object_unref (sel_end);
651 else
653 line_start = ianjuta_editor_get_lineno (IANJUTA_EDITOR(editor), NULL);
654 line_end = line_start;
657 else
659 line_start = ianjuta_editor_get_line_from_position (editor, start, NULL);
660 line_end = ianjuta_editor_get_line_from_position (editor, end, NULL);
662 ianjuta_document_begin_undo_action (IANJUTA_DOCUMENT(editor), NULL);
663 python_indent_init (lang_plugin);
665 for (insert_line = line_start; insert_line <= line_end; insert_line++)
667 gint line_indent_spaces = 0;
668 line_indent = get_line_auto_indentation (lang_plugin, editor,
669 insert_line,
670 &line_indent_spaces);
671 set_line_indentation (editor, insert_line, line_indent, line_indent_spaces);
673 ianjuta_document_end_undo_action (IANJUTA_DOCUMENT(editor), NULL);