libanjuta: Add IAnjutaIndenter interface
[anjuta.git] / plugins / language-support-python / plugin.c
blob7a6169606ff2719ca54fcd1de919ef6965781fc1
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * plugin.c
4 * Copyright (C) Ishan Chattopadhyaya 2009 <ichattopadhyaya@gmail.com>
5 *
6 * plugin.c is free software.
7 *
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
11 * any later version.
13 * plugin.c is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with plugin.c. If not, write to:
20 * The Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301, USA.
25 #include <config.h>
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <libanjuta/anjuta-shell.h>
30 #include <libanjuta/anjuta-debug.h>
31 #include <libanjuta/anjuta-launcher.h>
32 #include <libanjuta/anjuta-preferences.h>
33 #include <libanjuta/interfaces/ianjuta-iterable.h>
34 #include <libanjuta/interfaces/ianjuta-document.h>
35 #include <libanjuta/interfaces/ianjuta-document-manager.h>
36 #include <libanjuta/interfaces/ianjuta-editor.h>
37 #include <libanjuta/interfaces/ianjuta-file.h>
38 #include <libanjuta/interfaces/ianjuta-editor-cell.h>
39 #include <libanjuta/interfaces/ianjuta-editor-language.h>
40 #include <libanjuta/interfaces/ianjuta-editor-selection.h>
41 #include <libanjuta/interfaces/ianjuta-editor-assist.h>
42 #include <libanjuta/interfaces/ianjuta-preferences.h>
43 #include <libanjuta/interfaces/ianjuta-symbol.h>
44 #include <libanjuta/interfaces/ianjuta-language.h>
46 //REMOVE GLADE #include <glade/glade.h>
48 #include "plugin.h"
49 #include "python-utils.h"
50 #include "python-assist.h"
52 /* Pixmaps */
53 #define ANJUTA_PIXMAP_COMPLETE "anjuta-complete"
54 #define ANJUTA_PIXMAP_AUTOCOMPLETE "anjuta-complete-auto"
55 #define ANJUTA_PIXMAP_AUTOINDENT "anjuta-indent-auto"
56 #define ANJUTA_STOCK_SWAP "anjuta-swap"
57 #define ANJUTA_STOCK_COMPLETE "anjuta-complete"
58 #define ANJUTA_STOCK_AUTOCOMPLETE "anjuta-autocomplete"
59 #define ANJUTA_STOCK_AUTOINDENT "anjuta-indent"
61 #define UI_FILE PACKAGE_DATA_DIR"/ui/python-plugin.xml"
62 #define PROPERTIES_FILE_UI PACKAGE_DATA_DIR"/glade/python-plugin-properties.ui"
63 #define ICON_FILE "python_plugin.png"
65 /* Preferences keys */
67 #define PREF_SCHEMA "org.gnome.anjuta.python"
68 #define PREF_INDENT_AUTOMATIC "python-indent-automatic"
69 #define PREF_INDENT_ADAPTIVE "python-indent-adaptive"
70 #define PREF_INDENT_TAB_INDENTS "python-indent-tab-indents"
71 #define PREF_INDENT_STATEMENT_SIZE "python-indent-statement-size"
72 #define PREF_INDENT_BRACE_SIZE "python-indent-brace-size"
74 #define PREF_NO_ROPE_WARNING "python-no-rope-warning"
75 #define PREF_INTERPRETER_PATH "python-interpreter-path"
77 #define TAB_SIZE (ianjuta_editor_get_tabsize (editor, NULL))
79 #define USE_SPACES_FOR_INDENTATION (ianjuta_editor_get_use_spaces (editor, NULL))
81 #define INDENT_SIZE \
82 (plugin->param_statement_indentation >= 0? \
83 plugin->param_statement_indentation : \
84 g_settings_get_int (plugin->settings, PREF_INDENT_STATEMENT_SIZE))
86 #define BRACE_INDENT \
87 (plugin->param_brace_indentation >= 0? \
88 plugin->param_brace_indentation : \
89 g_settings_get_int (plugin->settings, PREF_INDENT_BRACE_SIZE))
91 #define CASE_INDENT (INDENT_SIZE)
92 #define LABEL_INDENT (INDENT_SIZE)
94 static gpointer parent_class;
96 static gboolean
97 iter_is_newline (IAnjutaIterable *iter, gchar ch)
99 if (ch == '\n' || ch == '\r')
100 return TRUE;
101 return FALSE;
104 /* Returns TRUE if iter was moved */
105 static gboolean
106 skip_iter_to_newline_head (IAnjutaIterable *iter, gchar ch)
108 gboolean ret_val = FALSE;
110 if (ch == '\n')
112 /* Possibly at tail */
113 if (ianjuta_iterable_previous (iter, NULL))
115 ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter),
116 0, NULL);
117 if (ch != '\r')
118 /* Already at head, undo iter */
119 ianjuta_iterable_next (iter, NULL);
120 else
121 /* Correctly at head */
122 ret_val = TRUE;
125 return ret_val;
128 /* Returns TRUE if iter was moved */
129 static gboolean
130 skip_iter_to_newline_tail (IAnjutaIterable *iter, gchar ch)
132 gboolean ret_val = FALSE;
134 if (ch == '\r')
136 /* Possibly at head */
137 if (ianjuta_iterable_previous (iter, NULL))
139 ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter),
140 0, NULL);
141 if (ch != '\n')
142 /* Already at tail, undo iter */
143 ianjuta_iterable_next (iter, NULL);
144 else
145 /* Correctly at tail */
146 ret_val = TRUE;
149 return ret_val;
152 /* Jumps to the reverse matching brace of the given brace character */
154 static gint
155 get_line_indentation (IAnjutaEditor *editor, gint line_num)
157 IAnjutaIterable *line_begin, *line_end;
158 gchar *line_string, *idx;
159 gint line_indent = 0;
161 line_begin = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
162 line_end = ianjuta_editor_get_line_end_position (editor, line_num, NULL);
164 DEBUG_PRINT ("%s: line begin = %d, line end = %d", __FUNCTION__,
165 line_begin, line_end);
167 if (ianjuta_iterable_compare (line_begin, line_end, NULL) == 0)
169 g_object_unref (line_begin);
170 g_object_unref (line_end);
171 return 0;
174 line_string = ianjuta_editor_get_text (editor, line_begin, line_end,
175 NULL);
176 g_object_unref (line_begin);
177 g_object_unref (line_end);
179 /* DEBUG_PRINT ("line_string = '%s'", line_string); */
181 if (!line_string)
182 return 0;
184 idx = line_string;
186 /* Find first non-white space */
187 while (*idx != '\0' && isspace (*idx))
189 if (*idx == '\t')
190 line_indent += TAB_SIZE;
191 else
192 line_indent++;
193 idx++; /* Since we are looking for first non-space char, simple
194 * increment of the utf8 chars would do */
196 g_free (line_string);
197 return line_indent;
200 static gchar *
201 get_line_indentation_string (IAnjutaEditor *editor, gint spaces, gint line_indent_spaces)
203 gint i;
204 gchar *indent_string;
206 if ((spaces + line_indent_spaces) <= 0)
207 return NULL;
209 if (USE_SPACES_FOR_INDENTATION)
211 indent_string = g_new0 (gchar, spaces + line_indent_spaces + 1);
212 for (i = 0; i < (spaces + line_indent_spaces); i++)
213 indent_string[i] = ' ';
215 else
217 gint num_tabs = spaces / TAB_SIZE;
218 gint num_spaces = spaces % TAB_SIZE;
219 indent_string = g_new0 (gchar, num_tabs + num_spaces + line_indent_spaces + 1);
221 for (i = 0; i < num_tabs; i++)
222 indent_string[i] = '\t';
223 for (; i < num_tabs + (num_spaces + line_indent_spaces); i++)
224 indent_string[i] = ' ';
226 return indent_string;
229 static void
230 set_indentation_param_emacs (PythonPlugin* plugin, const gchar *param,
231 const gchar *value)
233 //DEBUG_PRINT ("Setting indent param: %s = %s", param, value);
234 if (strcasecmp (param, "indent-tabs-mode") == 0)
236 if (strcasecmp (value, "t") == 0)
238 plugin->param_use_spaces = 0;
239 ianjuta_editor_set_use_spaces (IANJUTA_EDITOR (plugin->current_editor),
240 FALSE, NULL);
242 else if (strcasecmp (value, "nil") == 0)
244 plugin->param_use_spaces = 1;
245 ianjuta_editor_set_use_spaces (IANJUTA_EDITOR (plugin->current_editor),
246 TRUE, NULL);
249 else if (strcasecmp (param, "c-basic-offset") == 0)
251 plugin->param_statement_indentation = atoi (value);
253 else if (strcasecmp (param, "tab-width") == 0)
255 plugin->param_tab_size = atoi (value);
256 ianjuta_editor_set_tabsize (IANJUTA_EDITOR (plugin->current_editor),
257 plugin->param_tab_size, NULL);
261 static void
262 set_indentation_param_vim (PythonPlugin* plugin, const gchar *param,
263 const gchar *value)
265 //DEBUG_PRINT ("Setting indent param: %s = %s", param, value);
266 if (g_str_equal (param, "expandtab") ||
267 g_str_equal (param, "et"))
269 plugin->param_use_spaces = 1;
270 ianjuta_editor_set_use_spaces (IANJUTA_EDITOR (plugin->current_editor),
271 TRUE, NULL);
273 else if (g_str_equal (param, "noexpandtabs") ||
274 g_str_equal (param, "noet"))
276 plugin->param_use_spaces = 0;
277 ianjuta_editor_set_use_spaces (IANJUTA_EDITOR (plugin->current_editor),
278 FALSE, NULL);
280 if (!value)
281 return;
282 else if (g_str_equal (param, "shiftwidth") ||
283 g_str_equal (param, "sw"))
285 plugin->param_statement_indentation = atoi (value);
287 else if (g_str_equal (param, "softtabstop") ||
288 g_str_equal (param, "sts") ||
289 g_str_equal (param, "tabstop") ||
290 g_str_equal (param, "ts"))
292 plugin->param_tab_size = atoi (value);
293 ianjuta_editor_set_tabsize (IANJUTA_EDITOR (plugin->current_editor),
294 plugin->param_tab_size, NULL);
298 static void
299 parse_mode_line_emacs (PythonPlugin *plugin, const gchar *modeline)
301 gchar **strv, **ptr;
303 strv = g_strsplit (modeline, ";", -1);
304 ptr = strv;
305 while (*ptr)
307 gchar **keyval;
308 keyval = g_strsplit (*ptr, ":", 2);
309 if (keyval[0] && keyval[1])
311 g_strstrip (keyval[0]);
312 g_strstrip (keyval[1]);
313 set_indentation_param_emacs (plugin, g_strchug (keyval[0]),
314 g_strchug (keyval[1]));
316 g_strfreev (keyval);
317 ptr++;
319 g_strfreev (strv);
322 static void
323 parse_mode_line_vim (PythonPlugin *plugin, const gchar *modeline)
325 gchar **strv, **ptr;
327 strv = g_strsplit (modeline, " ", -1);
328 ptr = strv;
329 while (*ptr)
331 gchar **keyval;
332 keyval = g_strsplit (*ptr, "=", 2);
333 if (keyval[0])
335 g_strstrip (keyval[0]);
336 if (keyval[1])
338 g_strstrip (keyval[1]);
339 set_indentation_param_vim (plugin, g_strchug (keyval[0]),
340 g_strchug (keyval[1]));
342 else
343 set_indentation_param_vim (plugin, g_strchug (keyval[0]),
344 NULL);
346 g_strfreev (keyval);
347 ptr++;
349 g_strfreev (strv);
352 static gchar *
353 extract_mode_line (const gchar *comment_text, gboolean* vim)
355 /* Search for emacs-like modelines */
356 gchar *begin_modeline, *end_modeline;
357 begin_modeline = strstr (comment_text, "-*-");
358 if (begin_modeline)
360 begin_modeline += 3;
361 end_modeline = strstr (begin_modeline, "-*-");
362 if (end_modeline)
364 *vim = FALSE;
365 return g_strndup (begin_modeline, end_modeline - begin_modeline);
368 /* Search for vim-like modelines */
369 begin_modeline = strstr (comment_text, "vim:set");
370 if (begin_modeline)
372 begin_modeline += 7;
373 end_modeline = strstr (begin_modeline, ":");
374 /* Check for escape characters */
375 while (end_modeline)
377 if (!g_str_equal ((end_modeline - 1), "\\"))
378 break;
379 end_modeline++;
380 end_modeline = strstr (end_modeline, ":");
382 if (end_modeline)
384 gchar* vim_modeline = g_strndup (begin_modeline, end_modeline - begin_modeline);
385 *vim = TRUE;
386 return vim_modeline;
389 return NULL;
392 #define MINI_BUFFER_SIZE 3
394 static void
395 initialize_indentation_params (PythonPlugin *plugin)
397 IAnjutaIterable *iter;
398 GString *comment_text;
399 gboolean comment_begun = FALSE;
400 gboolean line_comment = FALSE;
401 gchar mini_buffer[MINI_BUFFER_SIZE] = {0};
403 /* Initialize indentation parameters */
404 plugin->param_tab_size = -1;
405 plugin->param_statement_indentation = -1;
406 plugin->param_brace_indentation = -1;
407 plugin->param_case_indentation = -1;
408 plugin->param_label_indentation = -1;
409 plugin->param_use_spaces = -1;
411 /* Find the first comment text in the buffer */
412 comment_text = g_string_new (NULL);
413 iter = ianjuta_editor_get_start_position (IANJUTA_EDITOR (plugin->current_editor),
414 NULL);
417 gboolean shift_buffer = TRUE;
418 gint i;
419 gchar ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter),
420 0, NULL);
422 for (i = 0; i < MINI_BUFFER_SIZE - 1; i++)
424 if (mini_buffer[i] == '\0')
426 mini_buffer[i] = ch;
427 shift_buffer = FALSE;
428 break;
431 if (shift_buffer == TRUE)
433 /* Shift buffer and add */
434 for (i = 0; i < MINI_BUFFER_SIZE - 1; i++)
435 mini_buffer [i] = mini_buffer[i+1];
436 mini_buffer[i] = ch;
439 if (!comment_begun && strncmp (mini_buffer, "/*", 2) == 0)
441 comment_begun = TRUE;
442 /* Reset buffer */
443 mini_buffer[0] = mini_buffer[1] = '\0';
445 else if (!comment_begun && strncmp (mini_buffer, "//", 2) == 0)
447 comment_begun = TRUE;
448 line_comment = TRUE;
450 else if (!comment_begun && mini_buffer[1] != '\0')
452 /* The buffer doesn't begin with a comment */
453 break;
455 else if (comment_begun)
457 if ((line_comment && ch == '\n') ||
458 (!line_comment && strncmp (mini_buffer, "*/", 2) == 0))
460 break;
464 if (comment_begun)
465 g_string_append_c (comment_text, ch);
468 while (ianjuta_iterable_next (iter, NULL));
470 /* DEBUG_PRINT ("Comment text: %s", comment_text->str);*/
471 if (comment_text->len > 0)
474 /* First comment found */
475 gboolean vim;
476 gchar *modeline = extract_mode_line (comment_text->str, &vim);
477 if (modeline)
479 if (!vim)
480 parse_mode_line_emacs (plugin, modeline);
481 else
482 parse_mode_line_vim (plugin, modeline);
483 g_free (modeline);
486 g_string_free (comment_text, TRUE);
487 g_object_unref (iter);
490 static gint
491 set_line_indentation (IAnjutaEditor *editor, gint line_num, gint indentation, gint line_indent_spaces)
493 IAnjutaIterable *line_begin, *line_end, *indent_position;
494 IAnjutaIterable *current_pos;
495 gint carat_offset, nchars = 0, nchars_removed = 0;
496 gchar *old_indent_string = NULL, *indent_string = NULL;
498 /* DEBUG_PRINT ("In %s()", __FUNCTION__); */
499 line_begin = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
500 line_end = ianjuta_editor_get_line_end_position (editor, line_num, NULL);
503 DEBUG_PRINT ("line begin = %d, line end = %d, current_pos = %d",
504 line_begin, line_end, current_pos);
506 indent_position = ianjuta_iterable_clone (line_begin, NULL);
508 if (ianjuta_iterable_compare (line_end, line_begin, NULL) > 0)
510 gchar *idx;
511 gchar *line_string = ianjuta_editor_get_text (editor, line_begin,
512 line_end, NULL);
514 //DEBUG_PRINT ("line_string = '%s'", line_string);
515 if (line_string)
517 idx = line_string;
519 /* Find first non-white space */
520 while (*idx != '\0' && isspace (*idx))
522 idx = g_utf8_find_next_char (idx, NULL);
523 ianjuta_iterable_next (indent_position, NULL);
525 g_free (line_string);
528 /* Indent iter defined at this point, Identify how much is current
529 * position is beyound this point. We need to restore it later after
530 * indentation
532 current_pos = ianjuta_editor_get_position (editor, NULL);
533 carat_offset = ianjuta_iterable_diff (indent_position, current_pos, NULL);
534 //DEBUG_PRINT ("carat offset is = %d", carat_offset);
536 /* Set new indentation */
537 if ((indentation + line_indent_spaces) > 0)
539 indent_string = get_line_indentation_string (editor, indentation, line_indent_spaces);
540 nchars = indent_string ? g_utf8_strlen (indent_string, -1) : 0;
542 /* Only indent if there is something to indent with */
543 if (indent_string)
545 /* Get existing indentation */
546 if (ianjuta_iterable_compare (indent_position, line_begin, NULL) > 0)
548 old_indent_string =
549 ianjuta_editor_get_text (editor, line_begin,
550 indent_position, NULL);
552 //DEBUG_PRINT ("old_indent_string = '%s'", old_indent_string);
553 nchars_removed = g_utf8_strlen (old_indent_string, -1);
556 /* Only indent if there was no indentation before or old
557 * indentation string was different from the new indent string
559 if (old_indent_string == NULL ||
560 strcmp (old_indent_string, indent_string) != 0)
562 /* Remove the old indentation string, if there is any */
563 if (old_indent_string)
564 ianjuta_editor_erase (editor, line_begin,
565 indent_position, NULL);
567 /* Insert the new indentation string */
568 ianjuta_editor_insert (editor, line_begin,
569 indent_string, -1, NULL);
574 /* If indentation == 0, we really didn't enter the previous code block,
575 * but we may need to clear existing indentation.
577 if ((indentation + line_indent_spaces) == 0)
579 /* Get existing indentation */
580 if (ianjuta_iterable_compare (indent_position, line_begin, NULL) > 0)
582 old_indent_string =
583 ianjuta_editor_get_text (editor, line_begin,
584 indent_position, NULL);
585 nchars_removed = g_utf8_strlen (old_indent_string, -1);
587 if (old_indent_string)
588 ianjuta_editor_erase (editor, line_begin, indent_position, NULL);
591 /* Restore current position */
592 if (carat_offset >= 0)
594 /* If the cursor was not before the first non-space character in
595 * the line, restore it's position after indentation.
597 gint i;
598 IAnjutaIterable *pos = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
599 for (i = 0; i < nchars + carat_offset; i++)
600 ianjuta_iterable_next (pos, NULL);
601 ianjuta_editor_goto_position (editor, pos, NULL);
602 g_object_unref (pos);
604 else /* cursor_offset < 0 */
606 /* If the cursor was somewhere in the old indentation spaces,
607 * home the cursor to first non-space character in the line (or
608 * end of line if there is no non-space characters in the line.
610 gint i;
611 IAnjutaIterable *pos = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
612 for (i = 0; i < nchars; i++)
613 ianjuta_iterable_next (pos, NULL);
614 ianjuta_editor_goto_position (editor, pos, NULL);
615 g_object_unref (pos);
618 g_object_unref (current_pos);
619 g_object_unref (indent_position);
620 g_object_unref (line_begin);
621 g_object_unref (line_end);
623 g_free (old_indent_string);
624 g_free (indent_string);
625 return nchars;
628 /* incomplete_statement:
629 * 1 == COMPLETE STATEMENT
630 * 0 == INCOMPLETE STATEMENT
631 * -1 == UNKNOWN
634 static gchar*
635 get_current_statement (IAnjutaEditor *editor, gint line_num, gint *found_line_num)
637 gchar point_ch;
638 IAnjutaIterable *iter = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
639 gchar statement[1024];
640 gint counter=0;
644 point_ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter), 0, NULL);
646 if (!ianjuta_iterable_next (iter, NULL) )
647 break;
648 } while (point_ch == ' ' || point_ch == '\t'); // Whitespace
650 if (!ianjuta_iterable_previous (iter, NULL))
651 return "";
655 point_ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter), 0, NULL);
656 statement[counter++] = point_ch;
658 if (!ianjuta_iterable_next (iter, NULL) )
659 break;
660 } while (isalpha(point_ch) || isdigit(point_ch)); // FIXME: Is this UTF-8 compatible?
661 statement[counter-1] = '\0';
663 g_object_unref (iter);
664 return g_strdup_printf("%s", statement);
667 static gchar
668 get_last_char (IAnjutaEditor *editor, gint line_num, gint *found_line_num)
670 gchar point_ch;
671 IAnjutaIterable *iter = ianjuta_editor_get_line_end_position (editor, line_num, NULL);
675 if (ianjuta_iterable_previous (iter, NULL) )
677 point_ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter), 0,
678 NULL);
680 else
681 break;
683 while (point_ch == ' ' || point_ch == '\n' || point_ch == '\r' || point_ch == '\t'); // Whitespace
685 *found_line_num = ianjuta_editor_get_line_from_position (editor, iter, NULL);
686 return point_ch;
689 static gboolean
690 spaces_only (IAnjutaEditor* editor, IAnjutaIterable* begin, IAnjutaIterable* end);
692 static gboolean
693 is_spaces_only (IAnjutaEditor *editor, gint line_num)
695 IAnjutaIterable* begin = ianjuta_editor_get_line_begin_position (editor, line_num, NULL);
696 IAnjutaIterable* end = ianjuta_editor_get_line_end_position (editor, line_num , NULL);
698 if (spaces_only (editor, begin, end))
700 return TRUE;
703 return FALSE;
706 static gint
707 get_line_indentation_base (PythonPlugin *plugin,
708 IAnjutaEditor *editor,
709 gint line_num,
710 gint *incomplete_statement,
711 gint *line_indent_spaces,
712 gboolean *colon_indent)
714 IAnjutaIterable *iter;
715 gchar point_ch;
716 gint line_indent = 0;
717 gint currentline = line_num - 1;
718 gchar *last_line_statement;
720 *incomplete_statement = 0;
721 *line_indent_spaces = 0;
723 if (currentline <= 1)
724 return 0;
726 iter = ianjuta_editor_get_line_end_position (editor, line_num, NULL);
728 point_ch = get_last_char (editor, currentline, &currentline);
729 last_line_statement = get_current_statement (editor, currentline, &currentline);
731 if (!g_strcmp0(last_line_statement, "return") ||
732 !g_strcmp0(last_line_statement, "break") ||
733 !g_strcmp0(last_line_statement, "pass") ||
734 !g_strcmp0(last_line_statement, "raise") ||
735 !g_strcmp0(last_line_statement, "continue") )
737 if (get_line_indentation (editor, currentline)>= INDENT_SIZE)
738 line_indent = get_line_indentation (editor, currentline) - INDENT_SIZE;
740 else if (point_ch == ':')
742 line_indent = get_line_indentation (editor, currentline) + INDENT_SIZE;
744 else
746 gint line = currentline;
747 while (is_spaces_only(editor, line) && line >= 0)
748 line--;
749 line_indent = get_line_indentation (editor, line);
752 return line_indent;
755 static gboolean
756 spaces_only (IAnjutaEditor* editor, IAnjutaIterable* begin, IAnjutaIterable* end)
758 gboolean empty = TRUE;
759 gchar* idx;
760 gchar* text = ianjuta_editor_get_text (editor, begin, end, NULL);
762 if (text == NULL)
763 return TRUE;
766 for (idx = text; *idx != '\0'; idx++)
768 if (!isspace(*idx))
770 empty = FALSE;
771 break;
774 g_free(text);
775 return empty;
778 static gint
779 get_line_auto_indentation (PythonPlugin *plugin, IAnjutaEditor *editor,
780 gint line, gint *line_indent_spaces)
782 IAnjutaIterable *iter;
783 gint line_indent = 0;
784 gint incomplete_statement = -1;
785 gboolean colon_indent = FALSE;
787 g_return_val_if_fail (line > 0, 0);
789 if (line == 1) /* First line */
791 return 0;
793 else
795 IAnjutaIterable* begin = ianjuta_editor_get_line_begin_position (editor, line -1 , NULL);
796 IAnjutaIterable* end = ianjuta_editor_get_line_end_position (editor, line -1 , NULL);
798 if (spaces_only (editor, begin, end))
800 set_line_indentation (editor, line -1, 0, 0);
802 g_object_unref (begin);
803 g_object_unref (end);
806 iter = ianjuta_editor_get_line_begin_position (editor, line, NULL);
808 /* if (is_iter_inside_string (iter))
810 line_indent = get_line_indentation (editor, line - 1);
812 else
814 line_indent = get_line_indentation_base (plugin, editor, line,
815 &incomplete_statement,
816 line_indent_spaces,
817 &colon_indent);
818 /*}*/
820 /* Determine what the first non-white char in the line is */
823 gchar ch;
824 /* Check if we are *inside* comment or string. Begining of comment
825 * or string does not count as inside. If inside, just align with
826 * previous indentation.
828 /*if (is_iter_inside_string (iter))
830 line_indent = get_line_indentation (editor, line - 1);
831 break;
833 ch = ianjuta_editor_cell_get_char (IANJUTA_EDITOR_CELL (iter),
834 0, NULL);
835 if (iter_is_newline (iter, ch))
837 skip_iter_to_newline_tail (iter, ch);
839 /* First levels are excused from incomplete statement indent */
840 if (incomplete_statement == 1 && line_indent > 0)
841 line_indent += INDENT_SIZE;
842 break;
845 else if (!isspace (ch))
847 /* First levels are excused from incomplete statement indent */
848 if (incomplete_statement == 1 && line_indent > 0)
849 line_indent += INDENT_SIZE;
850 break;
854 while (ianjuta_iterable_next (iter, NULL));
855 g_object_unref (iter);
857 return line_indent;
860 static void
861 on_editor_char_inserted_cpp (IAnjutaEditor *editor,
862 IAnjutaIterable *insert_pos,
863 gchar ch,
864 PythonPlugin *plugin)
866 IAnjutaIterable *iter;
867 gboolean should_auto_indent = FALSE;
869 iter = ianjuta_iterable_clone (insert_pos, NULL);
871 /* If autoindent is enabled*/
872 if (g_settings_get_boolean (plugin->settings, PREF_INDENT_AUTOMATIC))
874 if (iter_is_newline (iter, ch))
876 skip_iter_to_newline_head (iter, ch);
877 /* All newline entries means enable indenting */
878 should_auto_indent = TRUE;
881 if (should_auto_indent)
883 gint insert_line;
884 gint line_indent;
885 gint line_indent_spaces;
887 ianjuta_document_begin_undo_action (IANJUTA_DOCUMENT(editor), NULL);
888 initialize_indentation_params (plugin);
890 insert_line = ianjuta_editor_get_lineno (editor, NULL);
891 line_indent = get_line_auto_indentation (plugin, editor, insert_line, &line_indent_spaces);
892 set_line_indentation (editor, insert_line, line_indent, line_indent_spaces);
893 ianjuta_document_end_undo_action (IANJUTA_DOCUMENT(editor), NULL);
898 g_object_unref (iter);
901 static void
902 on_check_finished (AnjutaLauncher* launcher,
903 int child_pid, int exit_status,
904 gulong time, gpointer user_data)
906 PythonPlugin* plugin = ANJUTA_PLUGIN_PYTHON (user_data);
907 if (exit_status != 0)
909 GtkWidget* dialog = gtk_dialog_new_with_buttons (_("Python support warning"),
910 NULL,
911 GTK_DIALOG_MODAL,
912 GTK_STOCK_OK,
913 GTK_RESPONSE_ACCEPT,
914 NULL);
915 GtkWidget* area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
917 GtkWidget* label = gtk_label_new (_("Either python path is wrong or python-rope (http://rope.sf.net) libraries\n"
918 "aren't installed. Both are required for autocompletion in python files.\n"
919 "Please install them and check the python path in the preferences."));
920 GtkWidget* check_button = gtk_check_button_new_with_label (_("Do not show that warning again"));
922 gtk_box_pack_start (GTK_BOX (area), label,
923 TRUE, TRUE, 5);
924 gtk_box_pack_start (GTK_BOX (area), check_button,
925 TRUE, TRUE, 5);
926 gtk_widget_show_all (dialog);
928 gtk_dialog_run (GTK_DIALOG(dialog));
930 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button)))
932 g_settings_get_boolean (plugin->settings,
933 PREF_NO_ROPE_WARNING);
935 gtk_widget_destroy (dialog);
937 g_object_unref (launcher);
940 static void
941 check_support (PythonPlugin *python_plugin)
943 if (!g_settings_get_boolean (python_plugin->settings,
944 PREF_NO_ROPE_WARNING))
946 AnjutaLauncher* launcher = anjuta_launcher_new ();
947 gchar* python_path = g_settings_get_string (python_plugin->settings,
948 PREF_INTERPRETER_PATH);
949 gchar* command = g_strdup_printf ("%s -c \"import rope\"", python_path);
951 g_signal_connect (launcher, "child-exited",
952 G_CALLBACK(on_check_finished), python_plugin);
953 anjuta_launcher_execute (launcher, command, NULL, NULL);
955 g_free (python_path);
956 g_free (command);
961 static void
962 install_support (PythonPlugin *lang_plugin)
964 IAnjutaLanguage* lang_manager =
965 anjuta_shell_get_interface (ANJUTA_PLUGIN (lang_plugin)->shell,
966 IAnjutaLanguage, NULL);
967 IAnjutaSymbolManager* sym_manager =
968 anjuta_shell_get_interface (ANJUTA_PLUGIN (lang_plugin)->shell,
969 IAnjutaSymbolManager,
970 NULL);
971 IAnjutaDocumentManager* docman =
972 anjuta_shell_get_interface (ANJUTA_PLUGIN (lang_plugin)->shell,
973 IAnjutaDocumentManager,
974 NULL);
976 if (!lang_manager || !sym_manager || !docman)
977 return;
979 if (lang_plugin->support_installed)
980 return;
982 lang_plugin->current_language =
983 ianjuta_language_get_name_from_editor (lang_manager,
984 IANJUTA_EDITOR_LANGUAGE (lang_plugin->current_editor), NULL);
986 DEBUG_PRINT("Language support installed for: %s",
987 lang_plugin->current_language);
989 if (lang_plugin->current_language &&
990 (g_str_equal (lang_plugin->current_language, "Python")))
992 g_signal_connect (lang_plugin->current_editor,
993 "char-added",
994 G_CALLBACK (on_editor_char_inserted_cpp),
995 lang_plugin);
997 else
999 return;
1002 initialize_indentation_params (lang_plugin);
1003 /* Disable editor intern auto-indent */
1004 ianjuta_editor_set_auto_indent (IANJUTA_EDITOR(lang_plugin->current_editor),
1005 FALSE, NULL);
1007 if (IANJUTA_IS_EDITOR_ASSIST (lang_plugin->current_editor) )
1009 AnjutaPlugin *plugin;
1010 AnjutaUI *ui;
1011 IAnjutaEditorAssist* iassist;
1013 const gchar *project_root;
1014 gchar *editor_filename;
1015 GValue value = {0,};
1017 check_support (lang_plugin);
1019 plugin = ANJUTA_PLUGIN (lang_plugin);
1020 ui = anjuta_shell_get_ui (plugin->shell, NULL);
1021 iassist = IANJUTA_EDITOR_ASSIST (lang_plugin->current_editor);
1023 g_assert (lang_plugin->assist == NULL);
1025 anjuta_shell_get_value (ANJUTA_PLUGIN (lang_plugin)->shell,
1026 IANJUTA_PROJECT_MANAGER_PROJECT_ROOT_URI, &value, NULL);
1027 project_root = ANJUTA_PLUGIN_PYTHON(plugin)->project_root_directory; //g_value_get_string (&value);
1028 editor_filename = ANJUTA_PLUGIN_PYTHON(plugin)->current_editor_filename;
1030 lang_plugin->assist = python_assist_new (iassist,
1031 sym_manager,
1032 docman,
1033 lang_plugin->settings,
1034 editor_filename,
1035 project_root);
1038 lang_plugin->support_installed = TRUE;
1041 static void
1042 uninstall_support (PythonPlugin *lang_plugin)
1044 if (!lang_plugin->support_installed)
1045 return;
1047 if (lang_plugin->current_language &&
1048 (g_str_equal (lang_plugin->current_language, "Python")))
1050 g_signal_handlers_disconnect_by_func (lang_plugin->current_editor,
1051 G_CALLBACK (on_editor_char_inserted_cpp),
1052 lang_plugin);
1055 if (lang_plugin->assist)
1057 g_object_unref (lang_plugin->assist);
1058 lang_plugin->assist = NULL;
1061 lang_plugin->support_installed = FALSE;
1064 static void
1065 on_editor_language_changed (IAnjutaEditor *editor,
1066 const gchar *new_language,
1067 PythonPlugin *plugin)
1069 uninstall_support (plugin);
1070 install_support (plugin);
1073 static void
1074 on_value_added_current_editor (AnjutaPlugin *plugin, const gchar *name,
1075 const GValue *value, gpointer data)
1077 PythonPlugin *lang_plugin;
1078 IAnjutaDocument* doc = IANJUTA_DOCUMENT(g_value_get_object (value));
1079 lang_plugin = (PythonPlugin*) (plugin);
1082 if (IANJUTA_IS_EDITOR(doc))
1084 lang_plugin->current_editor = G_OBJECT(doc);
1086 else
1088 lang_plugin->current_editor = NULL;
1089 return;
1091 if (lang_plugin->current_editor)
1093 IAnjutaEditor* editor = IANJUTA_EDITOR (lang_plugin->current_editor);
1094 GFile* current_editor_file = ianjuta_file_get_file (IANJUTA_FILE (editor),
1095 NULL);
1097 if (current_editor_file)
1099 lang_plugin->current_editor_filename = g_file_get_path (current_editor_file);
1100 g_object_unref (current_editor_file);
1103 install_support (lang_plugin);
1106 g_signal_connect (lang_plugin->current_editor, "language-changed",
1107 G_CALLBACK (on_editor_language_changed),
1108 plugin);
1111 static void
1112 on_value_removed_current_editor (AnjutaPlugin *plugin, const gchar *name,
1113 gpointer data)
1115 PythonPlugin *lang_plugin;
1116 lang_plugin = (PythonPlugin*) (plugin);
1117 if (lang_plugin->current_editor)
1118 g_signal_handlers_disconnect_by_func (lang_plugin->current_editor,
1119 G_CALLBACK (on_editor_language_changed),
1120 plugin);
1122 uninstall_support (lang_plugin);
1124 g_free (lang_plugin->current_editor_filename);
1125 lang_plugin->current_editor_filename = NULL;
1126 lang_plugin->current_editor = NULL;
1129 static void
1130 on_auto_indent (GtkAction *action, gpointer data)
1132 gint line_start, line_end;
1133 gint insert_line;
1134 gint line_indent;
1135 gboolean has_selection;
1137 PythonPlugin *lang_plugin;
1138 IAnjutaEditor *editor;
1139 lang_plugin = (PythonPlugin*) (data);
1140 editor = IANJUTA_EDITOR (lang_plugin->current_editor);
1142 has_selection = ianjuta_editor_selection_has_selection
1143 (IANJUTA_EDITOR_SELECTION (editor), NULL);
1144 if (has_selection)
1146 IAnjutaIterable *sel_start, *sel_end;
1147 sel_start = ianjuta_editor_selection_get_start (IANJUTA_EDITOR_SELECTION (editor),
1148 NULL);
1149 sel_end = ianjuta_editor_selection_get_end (IANJUTA_EDITOR_SELECTION (editor),
1150 NULL);
1151 line_start = ianjuta_editor_get_line_from_position (editor, sel_start, NULL);
1152 line_end = ianjuta_editor_get_line_from_position (editor, sel_end, NULL);
1153 g_object_unref (sel_start);
1154 g_object_unref (sel_end);
1156 else
1158 line_start = ianjuta_editor_get_lineno (IANJUTA_EDITOR(editor), NULL);
1159 line_end = line_start;
1161 ianjuta_document_begin_undo_action (IANJUTA_DOCUMENT(editor), NULL);
1162 initialize_indentation_params (lang_plugin);
1164 for (insert_line = line_start; insert_line <= line_end; insert_line++)
1166 gint line_indent_spaces = 0;
1167 line_indent = get_line_auto_indentation (lang_plugin, editor,
1168 insert_line,
1169 &line_indent_spaces);
1170 /* DEBUG_PRINT ("Line indent for line %d = %d", insert_line, line_indent); */
1171 set_line_indentation (editor, insert_line, line_indent, line_indent_spaces);
1173 ianjuta_document_end_undo_action (IANJUTA_DOCUMENT(editor), NULL);
1176 static GtkActionEntry actions[] = {
1178 "ActionMenuEdit",
1179 NULL, N_("_Edit"),
1180 NULL, NULL, NULL
1183 "ActionEditAutoindent",
1184 GTK_STOCK_NEW, //ANJUTA_STOCK_AUTOINDENT,
1185 N_("Auto-Indent"), "<control>i",
1186 N_("Auto-indent current line or selection based on indentation settings"),
1187 G_CALLBACK (on_auto_indent)
1191 static void
1192 register_stock_icons (AnjutaPlugin *plugin)
1194 static gboolean registered = FALSE;
1196 if (registered)
1197 return;
1198 registered = TRUE;
1200 /* Register stock icons */
1201 BEGIN_REGISTER_ICON (plugin);
1202 // REGISTER_ICON_FULL (ANJUTA_PIXMAP_SWAP, ANJUTA_STOCK_SWAP);
1203 // REGISTER_ICON_FULL (ANJUTA_PIXMAP_COMPLETE, ANJUTA_STOCK_COMPLETE);
1204 // REGISTER_ICON_FULL (ANJUTA_PIXMAP_AUTOCOMPLETE, ANJUTA_STOCK_AUTOCOMPLETE);
1205 // REGISTER_ICON_FULL (ANJUTA_PIXMAP_AUTOINDENT, ANJUTA_STOCK_AUTOINDENT);
1206 END_REGISTER_ICON;
1209 // CODE from git PLUGIN
1210 static void
1211 on_project_root_added (AnjutaPlugin *plugin, const gchar *name,
1212 const GValue *value, gpointer user_data)
1214 PythonPlugin *python_plugin;
1215 gchar *project_root_uri;
1216 GFile *file;
1218 python_plugin = ANJUTA_PLUGIN_PYTHON (plugin);
1220 g_free (python_plugin->project_root_directory);
1221 project_root_uri = g_value_dup_string (value);
1222 file = g_file_new_for_uri (project_root_uri);
1223 python_plugin->project_root_directory = g_file_get_path (file);
1224 g_object_unref (file);
1225 g_free (project_root_uri);
1228 static void
1229 on_project_root_removed (AnjutaPlugin *plugin, const gchar *name,
1230 gpointer user_data)
1232 AnjutaUI *ui;
1233 PythonPlugin *python_plugin;
1235 python_plugin = ANJUTA_PLUGIN_PYTHON (plugin);
1237 g_free (python_plugin->project_root_directory);
1238 python_plugin->project_root_directory = NULL;
1240 ui = anjuta_shell_get_ui (plugin->shell, NULL);
1243 static gboolean
1244 python_plugin_activate (AnjutaPlugin *plugin)
1246 AnjutaUI *ui;
1248 PythonPlugin *python_plugin;
1249 static gboolean initialized = FALSE;
1251 python_plugin = (PythonPlugin*) plugin;
1253 if (!initialized)
1255 register_stock_icons (plugin);
1259 python_plugin->prefs = anjuta_shell_get_preferences (plugin->shell, NULL);
1261 /* Add all UI actions and merge UI */
1262 ui = anjuta_shell_get_ui (plugin->shell, NULL);
1264 python_plugin->action_group =
1266 anjuta_ui_add_action_group_entries (ui, "ActionGroupPythonAssist",
1267 _("Python Assistance"),
1268 actions,
1269 G_N_ELEMENTS (actions),
1270 GETTEXT_PACKAGE, TRUE,
1271 plugin);
1272 python_plugin->uiid = anjuta_ui_merge (ui, UI_FILE);
1274 initialized = FALSE;
1276 /* Add watches */
1277 python_plugin->project_root_watch_id = anjuta_plugin_add_watch (plugin,
1278 IANJUTA_PROJECT_MANAGER_PROJECT_ROOT_URI,
1279 on_project_root_added,
1280 on_project_root_removed,
1281 NULL);
1283 python_plugin->editor_watch_id = anjuta_plugin_add_watch (plugin,
1284 IANJUTA_DOCUMENT_MANAGER_CURRENT_DOCUMENT,
1285 on_value_added_current_editor,
1286 on_value_removed_current_editor,
1287 NULL);
1288 return TRUE;
1291 static gboolean
1292 python_plugin_deactivate (AnjutaPlugin *plugin)
1295 AnjutaUI *ui;
1296 PythonPlugin *lang_plugin;
1297 lang_plugin = (PythonPlugin*) (plugin);
1298 DEBUG_PRINT ("%s", "PythonPlugin: Dectivating PythonPlugin plugin ...");
1300 anjuta_plugin_remove_watch (plugin,
1301 lang_plugin->editor_watch_id,
1302 TRUE);
1303 anjuta_plugin_remove_watch (plugin,
1304 lang_plugin->project_root_watch_id,
1305 TRUE);
1308 ui = anjuta_shell_get_ui (plugin->shell, NULL);
1309 anjuta_ui_remove_action_group (ui, ANJUTA_PLUGIN_PYTHON(plugin)->action_group);
1310 anjuta_ui_unmerge (ui, ANJUTA_PLUGIN_PYTHON(plugin)->uiid);
1312 DEBUG_PRINT ("%s", "PythonPlugin: Deactivated plugin.");
1314 return TRUE;
1317 static void
1318 python_plugin_finalize (GObject *obj)
1320 /* Finalization codes here */
1321 G_OBJECT_CLASS (parent_class)->finalize (obj);
1324 static void
1325 python_plugin_dispose (GObject *obj)
1327 /* Disposition codes */
1328 PythonPlugin *plugin = (PythonPlugin*)obj;
1330 if (plugin->settings)
1331 g_object_unref (plugin->settings);
1332 plugin->settings = NULL;
1334 G_OBJECT_CLASS (parent_class)->dispose (obj);
1337 static void
1338 python_plugin_instance_init (GObject *obj)
1340 PythonPlugin *plugin = (PythonPlugin*)obj;
1341 plugin->action_group = NULL;
1342 plugin->current_editor = NULL;
1343 plugin->current_language = NULL;
1344 plugin->editor_watch_id = 0;
1345 plugin->uiid = 0;
1346 plugin->assist = NULL;
1347 plugin->settings = g_settings_new (PREF_SCHEMA);
1350 static void
1351 python_plugin_class_init (GObjectClass *klass)
1353 AnjutaPluginClass *plugin_class = ANJUTA_PLUGIN_CLASS (klass);
1355 parent_class = g_type_class_peek_parent (klass);
1357 plugin_class->activate = python_plugin_activate;
1358 plugin_class->deactivate = python_plugin_deactivate;
1359 klass->finalize = python_plugin_finalize;
1360 klass->dispose = python_plugin_dispose;
1364 static void
1365 ipreferences_merge (IAnjutaPreferences* ipref, AnjutaPreferences* prefs,
1366 GError** e)
1368 /* Add preferences */
1369 PythonPlugin* plugin = ANJUTA_PLUGIN_PYTHON (ipref);
1370 gchar *objects[] = {"python_preferences_dialog", NULL};
1371 plugin->bxml = gtk_builder_new();
1372 gtk_builder_add_objects_from_file(plugin->bxml, PROPERTIES_FILE_UI, objects, NULL);
1373 anjuta_preferences_add_from_builder (prefs,
1374 plugin->bxml,
1375 plugin->settings,
1376 "preferences", _("Python"),
1377 ICON_FILE);
1380 static void
1381 ipreferences_unmerge (IAnjutaPreferences* ipref, AnjutaPreferences* prefs,
1382 GError** e)
1384 PythonPlugin* plugin = ANJUTA_PLUGIN_PYTHON (ipref);
1385 anjuta_preferences_remove_page(prefs, _("Python"));
1386 g_object_unref (plugin->bxml);
1389 static void
1390 ipreferences_iface_init (IAnjutaPreferencesIface* iface)
1392 iface->merge = ipreferences_merge;
1393 iface->unmerge = ipreferences_unmerge;
1396 //ANJUTA_PLUGIN_BOILERPLATE (PythonPlugin, python_plugin);
1398 ANJUTA_PLUGIN_BEGIN (PythonPlugin, python_plugin);
1399 ANJUTA_PLUGIN_ADD_INTERFACE(ipreferences, IANJUTA_TYPE_PREFERENCES);
1400 ANJUTA_PLUGIN_END;
1403 ANJUTA_SIMPLE_PLUGIN (PythonPlugin, python_plugin);