SF.net SVN: geany: [1584] trunk
ntrel at users.sourceforge.net
ntrel at xxxxx
Mon May 28 16:07:30 UTC 2007
Revision: 1584
http://svn.sourceforge.net/geany/?rev=1584&view=rev
Author: ntrel
Date: 2007-05-28 09:07:30 -0700 (Mon, 28 May 2007)
Log Message:
-----------
Rename sci_cb.[hc] editor.[hc].
Modified Paths:
--------------
trunk/ChangeLog
trunk/po/POTFILES.in
trunk/src/Makefile.am
trunk/src/callbacks.c
trunk/src/document.c
trunk/src/highlighting.c
trunk/src/keybindings.c
trunk/src/main.c
trunk/src/makefile.win32
trunk/src/search.c
Added Paths:
-----------
trunk/src/editor.c
trunk/src/editor.h
Removed Paths:
-------------
trunk/src/sci_cb.c
trunk/src/sci_cb.h
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2007-05-28 15:28:30 UTC (rev 1583)
+++ trunk/ChangeLog 2007-05-28 16:07:30 UTC (rev 1584)
@@ -4,6 +4,11 @@
Show current project name in window title.
* configure.in, src/Makefile.am:
Force debug mode for a SVN working copy.
+ * src/highlighting.c, src/keybindings.c, src/makefile.win32,
+ src/callbacks.c, src/sci_cb.c, src/sci_cb.h, src/search.c,
+ src/document.c, src/main.c, src/editor.c, src/Makefile.am,
+ src/editor.h, po/POTFILES.in:
+ Rename sci_cb.[hc] editor.[hc].
2007-05-26 Nick Treleaven <nick.treleaven at btinternet.com>
Modified: trunk/po/POTFILES.in
===================================================================
--- trunk/po/POTFILES.in 2007-05-28 15:28:30 UTC (rev 1583)
+++ trunk/po/POTFILES.in 2007-05-28 16:07:30 UTC (rev 1584)
@@ -18,7 +18,7 @@
src/notebook.c
src/prefs.c
src/project.c
-src/sci_cb.c
+src/editor.c
src/sciwrappers.c
src/search.c
src/socket.c
Modified: trunk/src/Makefile.am
===================================================================
--- trunk/src/Makefile.am 2007-05-28 15:28:30 UTC (rev 1583)
+++ trunk/src/Makefile.am 2007-05-28 16:07:30 UTC (rev 1584)
@@ -23,7 +23,7 @@
notebook.c notebook.h \
prefs.c prefs.h \
project.c project.h \
- sci_cb.c sci_cb.h \
+ editor.c editor.h \
sciwrappers.c sciwrappers.h \
search.c search.h \
socket.c socket.h \
Modified: trunk/src/callbacks.c
===================================================================
--- trunk/src/callbacks.c 2007-05-28 15:28:30 UTC (rev 1583)
+++ trunk/src/callbacks.c 2007-05-28 16:07:30 UTC (rev 1584)
@@ -41,7 +41,7 @@
#include "keyfile.h"
#include "document.h"
#include "sciwrappers.h"
-#include "sci_cb.h"
+#include "editor.h"
#include "ui_utils.h"
#include "utils.h"
#include "dialogs.h"
@@ -1249,7 +1249,7 @@
if (idx == -1 || ! doc_list[idx].is_valid)
return;
- sci_cb_find_current_word(doc_list[idx].sci, pos, colour, sizeof colour, NULL);
+ editor_find_current_word(doc_list[idx].sci, pos, colour, sizeof colour, NULL);
dialogs_show_color(colour);
}
@@ -1477,7 +1477,7 @@
verify_click_pos(idx); // make sure that the click_pos is valid
- sci_cb_insert_multiline_comment(idx);
+ editor_insert_multiline_comment(idx);
}
@@ -1829,7 +1829,7 @@
{
gint idx = document_get_cur_idx();
if (idx == -1 || ! doc_list[idx].is_valid) return;
- sci_cb_do_comment(idx, -1, FALSE);
+ editor_do_comment(idx, -1, FALSE);
}
@@ -1839,7 +1839,7 @@
{
gint idx = document_get_cur_idx();
if (idx == -1 || ! doc_list[idx].is_valid) return;
- sci_cb_do_uncomment(idx, -1);
+ editor_do_uncomment(idx, -1);
}
@@ -1850,7 +1850,7 @@
{
gint idx = document_get_cur_idx();
if (idx == -1 || ! doc_list[idx].is_valid) return;
- sci_cb_do_comment_toggle(idx);
+ editor_do_comment_toggle(idx);
}
@@ -2067,7 +2067,7 @@
{ // use the word at current cursor position
gchar word[GEANY_MAX_WORD_LENGTH];
- sci_cb_find_current_word(doc_list[idx].sci, -1, word, sizeof(word), GEANY_WORDCHARS"./");
+ editor_find_current_word(doc_list[idx].sci, -1, word, sizeof(word), GEANY_WORDCHARS"./");
if (word[0] != '\0')
filename = g_strdup(word);
}
Modified: trunk/src/document.c
===================================================================
--- trunk/src/document.c 2007-05-28 15:28:30 UTC (rev 1583)
+++ trunk/src/document.c 2007-05-28 16:07:30 UTC (rev 1584)
@@ -53,7 +53,7 @@
#include "document.h"
#include "support.h"
#include "sciwrappers.h"
-#include "sci_cb.h"
+#include "editor.h"
#include "dialogs.h"
#include "msgwindow.h"
#include "templates.h"
@@ -1571,14 +1571,14 @@
guint n;
const GString *s;
- if (sci != NULL && sci_cb_lexer_get_type_keyword_idx(sci_get_lexer(sci)) == -1)
+ if (sci != NULL && editor_lexer_get_type_keyword_idx(sci_get_lexer(sci)) == -1)
return FALSE;
if (! get_project_typenames(&s))
{ // typenames have not changed
if (s != NULL && sci != NULL)
{
- gint keyword_idx = sci_cb_lexer_get_type_keyword_idx(sci_get_lexer(sci));
+ gint keyword_idx = editor_lexer_get_type_keyword_idx(sci_get_lexer(sci));
sci_set_keywords(sci, keyword_idx, s->str);
if (! delay_colourise)
@@ -1596,7 +1596,7 @@
if (wid)
{
- gint keyword_idx = sci_cb_lexer_get_type_keyword_idx(sci_get_lexer(wid));
+ gint keyword_idx = editor_lexer_get_type_keyword_idx(sci_get_lexer(wid));
if (keyword_idx > 0)
{
@@ -2214,7 +2214,7 @@
n = g_array_index(doc_indexes, gint, i);
sci = doc_list[n].sci;
- if (! recolour || (sci && sci_cb_lexer_get_type_keyword_idx(sci_get_lexer(sci)) == -1))
+ if (! recolour || (sci && editor_lexer_get_type_keyword_idx(sci_get_lexer(sci)) == -1))
{
doc_set[n] = FALSE;
}
Copied: trunk/src/editor.c (from rev 1583, trunk/src/sci_cb.c)
===================================================================
--- trunk/src/editor.c (rev 0)
+++ trunk/src/editor.c 2007-05-28 16:07:30 UTC (rev 1584)
@@ -0,0 +1,2296 @@
+/*
+ * editor.c - this file is part of Geany, a fast and lightweight IDE
+ *
+ * Copyright 2005-2007 Enrico Tröger <enrico.troeger at uvena.de>
+ * Copyright 2006-2007 Nick Treleaven <nick.treleaven at btinternet.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/*
+ * Callbacks for the Scintilla widget (ScintillaObject).
+ * Most important is the sci-notify callback, handled in on_editor_notification().
+ * This includes auto-indentation, comments, auto-completion, calltips, etc.
+ * Also some general Scintilla-related functions.
+ */
+
+#include <ctype.h>
+#include <string.h>
+
+#include "SciLexer.h"
+#include "geany.h"
+
+#include "editor.h"
+#include "document.h"
+#include "sciwrappers.h"
+#include "ui_utils.h"
+#include "utils.h"
+#include "symbols.h"
+
+
+static gchar current_word[GEANY_MAX_WORD_LENGTH]; // holds word under the mouse or keyboard cursor
+
+EditorInfo editor_info = {current_word, -1};
+
+static struct
+{
+ gchar *text;
+ gboolean set;
+ gchar *last_word;
+ guint tag_index;
+} calltip = {NULL, FALSE, NULL, 0};
+
+static gchar indent[100];
+
+
+static void on_new_line_added(ScintillaObject *sci, gint idx);
+static gboolean handle_xml(ScintillaObject *sci, gchar ch, gint idx);
+static void get_indent(ScintillaObject *sci, gint pos, gboolean use_this_line);
+static void auto_multiline(ScintillaObject *sci, gint pos);
+static gboolean is_comment(gint lexer, gint style);
+static void scroll_to_line(ScintillaObject *sci, gint line, gfloat percent_of_view);
+static void auto_close_bracket(ScintillaObject *sci, gint pos, gchar c);
+
+
+// calls the edit popup menu in the editor
+gboolean
+on_editor_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ gint idx = GPOINTER_TO_INT(user_data);
+ editor_info.click_pos = sci_get_position_from_xy(doc_list[idx].sci, event->x, event->y, FALSE);
+
+ if (event->button == 1)
+ {
+ if (GDK_BUTTON_PRESS==event->type && app->pref_editor_disable_dnd)
+ {
+ gint ss = sci_get_selection_start(doc_list[idx].sci);
+ sci_set_selection_end(doc_list[idx].sci, ss);
+ }
+ return utils_check_disk_status(idx, FALSE);
+ }
+
+ if (event->button == 3)
+ {
+ editor_find_current_word(doc_list[idx].sci, editor_info.click_pos,
+ current_word, sizeof current_word, NULL);
+
+ ui_update_popup_goto_items((current_word[0] != '\0') ? TRUE : FALSE);
+ ui_update_popup_copy_items(idx);
+ ui_update_insert_include_item(idx, 0);
+ gtk_menu_popup(GTK_MENU(app->popup_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+typedef struct SCNotification SCNotification;
+
+static void on_margin_click(ScintillaObject *sci, SCNotification *nt)
+{
+ // left click to marker margin marks the line
+ if (nt->margin == 1)
+ {
+ gint line = sci_get_line_from_position(sci, nt->position);
+ gboolean set = sci_is_marker_set_at_line(sci, line, 1);
+
+ //sci_marker_delete_all(doc_list[idx].sci, 1);
+ sci_set_marker_at_line(sci, line, ! set, 1); // toggle the marker
+ }
+ // left click on the folding margin to toggle folding state of current line
+ else if (nt->margin == 2 && app->pref_editor_folding)
+ {
+ gint line = SSM(sci, SCI_LINEFROMPOSITION, nt->position, 0);
+
+ SSM(sci, SCI_TOGGLEFOLD, line, 0);
+ if (app->pref_editor_unfold_all_children &&
+ SSM(sci, SCI_GETLINEVISIBLE, line + 1, 0))
+ { // unfold all children of the current fold point
+ gint last_line = SSM(sci, SCI_GETLASTCHILD, line, -1);
+ gint i;
+
+ for (i = line; i < last_line; i++)
+ {
+ if (! SSM(sci, SCI_GETLINEVISIBLE, i, 0))
+ {
+ SSM(sci, SCI_TOGGLEFOLD, SSM(sci, SCI_GETFOLDPARENT, i, 0), 0);
+ }
+ }
+ }
+ }
+}
+
+
+static void on_update_ui(gint idx, G_GNUC_UNUSED SCNotification *nt)
+{
+ ScintillaObject *sci = doc_list[idx].sci;
+ gint pos = sci_get_current_position(sci);
+
+ // undo / redo menu update
+ ui_update_popup_reundo_items(idx);
+
+ // brace highlighting
+ editor_highlight_braces(sci, pos);
+
+ ui_update_statusbar(idx, pos);
+
+ /* Visible lines are only laid out accurately once [SCN_UPDATEUI] is sent,
+ * so we need to only call sci_scroll_to_line here, because the document
+ * may have line wrapping and folding enabled.
+ * http://scintilla.sourceforge.net/ScintillaDoc.html#LineWrapping */
+ if (doc_list[idx].scroll_percent > 0.0F)
+ {
+ scroll_to_line(sci, -1, doc_list[idx].scroll_percent);
+ doc_list[idx].scroll_percent = -1.0F; // disable further scrolling
+ }
+#if 0
+ /// experimental code for inverting selections
+ {
+ gint i;
+ for (i = SSM(sci, SCI_GETSELECTIONSTART, 0, 0); i < SSM(sci, SCI_GETSELECTIONEND, 0, 0); i++)
+ {
+ // need to get colour from getstyleat(), but how?
+ SSM(sci, SCI_STYLESETFORE, STYLE_DEFAULT, 0);
+ SSM(sci, SCI_STYLESETBACK, STYLE_DEFAULT, 0);
+ }
+
+ sci_get_style_at(sci, pos);
+ }
+#endif
+}
+
+
+static void on_char_added(gint idx, SCNotification *nt)
+{
+ ScintillaObject *sci = doc_list[idx].sci;
+ gint pos = sci_get_current_position(sci);
+
+ switch (nt->ch)
+ {
+ case '\r':
+ { // simple indentation (only for CR format)
+ if (sci_get_eol_mode(sci) == SC_EOL_CR)
+ on_new_line_added(sci, idx);
+ break;
+ }
+ case '\n':
+ { // simple indentation (for CR/LF and LF format)
+ on_new_line_added(sci, idx);
+ break;
+ }
+ case '>':
+ case '/':
+ { // close xml-tags
+ handle_xml(sci, nt->ch, idx);
+ break;
+ }
+ case '(':
+ { // show calltips
+ editor_show_calltip(idx, --pos);
+ break;
+ }
+ case ')':
+ { // hide calltips
+ if (SSM(sci, SCI_CALLTIPACTIVE, 0, 0))
+ {
+ SSM(sci, SCI_CALLTIPCANCEL, 0, 0);
+ }
+ g_free(calltip.text);
+ calltip.text = NULL;
+ calltip.set = FALSE;
+ break;
+ }
+ case '[':
+ case '{':
+ { // Tex auto-closing
+ if (sci_get_lexer(sci) == SCLEX_LATEX)
+ {
+ auto_close_bracket(sci, pos, nt->ch); // Tex auto-closing
+ editor_show_calltip(idx, --pos);
+ }
+ break;
+ }
+ case '}':
+ { // closing bracket handling
+ if (doc_list[idx].use_auto_indention &&
+ app->pref_editor_indention_mode == INDENT_ADVANCED)
+ editor_close_block(idx, pos - 1);
+ break;
+ }
+ default: editor_start_auto_complete(idx, pos, FALSE);
+ }
+}
+
+
+// callback func called by all editors when a signal arises
+void on_editor_notification(GtkWidget *editor, gint scn, gpointer lscn, gpointer user_data)
+{
+ SCNotification *nt;
+ ScintillaObject *sci;
+ gint idx;
+
+ idx = GPOINTER_TO_INT(user_data);
+ sci = doc_list[idx].sci;
+
+ nt = lscn;
+ switch (nt->nmhdr.code)
+ {
+ case SCN_SAVEPOINTLEFT:
+ {
+ doc_list[idx].changed = TRUE;
+ document_set_text_changed(idx);
+ break;
+ }
+ case SCN_SAVEPOINTREACHED:
+ {
+ doc_list[idx].changed = FALSE;
+ document_set_text_changed(idx);
+ break;
+ }
+ case SCN_MODIFYATTEMPTRO:
+ {
+ utils_beep();
+ break;
+ }
+ case SCN_MARGINCLICK:
+ on_margin_click(sci, nt);
+ break;
+
+ case SCN_UPDATEUI:
+ on_update_ui(idx, nt);
+ break;
+
+ case SCN_MODIFIED:
+ {
+ if (nt->modificationType & SC_STARTACTION && ! app->ignore_callback)
+ {
+ // get notified about undo changes
+ document_undo_add(idx, UNDO_SCINTILLA, NULL);
+ }
+ break;
+ }
+ case SCN_CHARADDED:
+ on_char_added(idx, nt);
+ break;
+
+ case SCN_USERLISTSELECTION:
+ {
+ if (nt->listType == 1)
+ {
+ gint pos = SSM(sci, SCI_GETCURRENTPOS, 0, 0);
+ SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) nt->text);
+ }
+ else if (nt->listType == 2)
+ {
+ gint start, pos = SSM(sci, SCI_GETCURRENTPOS, 0, 0);
+ start = pos;
+ while (start > 0 && sci_get_char_at(sci, --start) != '&') ;
+
+ SSM(sci, SCI_INSERTTEXT, pos - 1, (sptr_t) nt->text);
+ }
+ break;
+ }
+ case SCN_AUTOCSELECTION:
+ {
+ // now that autocomplete is finishing, reshow calltips if they were showing
+ if (calltip.set)
+ {
+ gint pos = sci_get_current_position(sci);
+ SSM(sci, SCI_CALLTIPSHOW, pos, (sptr_t) calltip.text);
+ // now autocompletion has been cancelled, so do it manually
+ sci_set_selection_start(sci, nt->lParam);
+ sci_set_selection_end(sci, pos);
+ sci_replace_sel(sci, ""); // clear root of word
+ SSM(sci, SCI_INSERTTEXT, nt->lParam, (sptr_t) nt->text);
+ sci_goto_pos(sci, nt->lParam + strlen(nt->text), FALSE);
+ }
+ break;
+ }
+#ifdef GEANY_DEBUG
+ case SCN_STYLENEEDED:
+ {
+ geany_debug("style");
+ break;
+ }
+#endif
+ case SCN_URIDROPPED:
+ {
+ if (nt->text != NULL)
+ {
+ document_open_file_list(nt->text, -1);
+ }
+ break;
+ }
+ case SCN_CALLTIPCLICK:
+ {
+ if (nt->position > 0)
+ {
+ switch (nt->position)
+ {
+ case 1: // up arrow
+ if (calltip.tag_index > 0)
+ calltip.tag_index--;
+ break;
+
+ case 2: calltip.tag_index++; break; // down arrow
+ }
+ editor_show_calltip(idx, -1);
+ }
+ break;
+ }
+ }
+}
+
+
+static void on_new_line_added(ScintillaObject *sci, gint idx)
+{
+ gint pos = sci_get_current_position(sci);
+
+ // simple indentation
+ if (doc_list[idx].use_auto_indention)
+ {
+ get_indent(sci, pos, FALSE);
+ sci_add_text(sci, indent);
+
+ if (app->pref_editor_indention_mode == INDENT_ADVANCED)
+ {
+ // add extra indentation for Python after colon
+ if (FILETYPE_ID(doc_list[idx].file_type) == GEANY_FILETYPES_PYTHON &&
+ sci_get_char_at(sci, pos - 2) == ':' &&
+ sci_get_style_at(sci, pos - 2) == SCE_P_OPERATOR)
+ {
+ // creates and inserts one tabulator sign or whitespace of the amount of the tab width
+ gchar *text = utils_get_whitespace(app->pref_editor_tab_width, FALSE);
+ sci_add_text(sci, text);
+ g_free(text);
+ }
+ }
+ }
+
+ if (app->pref_editor_auto_complete_constructs)
+ {
+ // " * " auto completion in multiline C/C++/D/Java comments
+ auto_multiline(sci, pos);
+
+ editor_auto_latex(idx, pos);
+ }
+}
+
+
+static gboolean lexer_has_braces(ScintillaObject *sci)
+{
+ gint lexer = SSM(sci, SCI_GETLEXER, 0, 0);
+
+ switch (lexer)
+ {
+ case SCLEX_CPP:
+ case SCLEX_D:
+ case SCLEX_HTML: // for PHP & JS
+ case SCLEX_PASCAL: // for multiline comments?
+ case SCLEX_BASH:
+ case SCLEX_PERL:
+ case SCLEX_TCL:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+// in place indentation of one tab or equivalent spaces
+static void do_indent(gchar *buf, gsize len, guint *idx)
+{
+ guint j = *idx;
+
+ if (app->pref_editor_use_tabs)
+ {
+ if (j < len - 1) // leave room for a \0 terminator.
+ buf[j++] = '\t';
+ }
+ else
+ { // insert as many spaces as a tab would take
+ guint k;
+ for (k = 0; k < (guint) app->pref_editor_tab_width && k < len - 1; k++)
+ buf[j++] = ' ';
+ }
+ *idx = j;
+}
+
+
+/* "use_this_line" to auto-indent only if it is a real new line
+ * and ignore the case of editor_close_block */
+static void get_indent(ScintillaObject *sci, gint pos, gboolean use_this_line)
+{
+ guint i, len, j = 0;
+ gint prev_line;
+ gchar *linebuf;
+
+ prev_line = sci_get_line_from_position(sci, pos);
+
+ if (! use_this_line) prev_line--;
+ len = sci_get_line_length(sci, prev_line);
+ linebuf = sci_get_line(sci, prev_line);
+
+ for (i = 0; i < len && j <= (sizeof(indent) - 1); i++)
+ {
+ if (linebuf[i] == ' ' || linebuf[i] == '\t') // simple indentation
+ indent[j++] = linebuf[i];
+ else if (app->pref_editor_indention_mode != INDENT_ADVANCED)
+ break;
+ else if (use_this_line)
+ break;
+ else // editor_close_block
+ {
+ if (! lexer_has_braces(sci))
+ break;
+
+ if (linebuf[i] == '{')
+ {
+ do_indent(indent, sizeof(indent), &j);
+ break;
+ }
+ else
+ {
+ gint k = len - 1;
+
+ while (k > 0 && isspace(linebuf[k])) k--;
+
+ // if last non-whitespace character is a { increase indentation by a tab
+ // e.g. for (...) {
+ if (linebuf[k] == '{')
+ {
+ do_indent(indent, sizeof(indent), &j);
+ }
+ break;
+ }
+ }
+ }
+ indent[j] = '\0';
+ g_free(linebuf);
+}
+
+
+static void auto_close_bracket(ScintillaObject *sci, gint pos, gchar c)
+{
+ if (! app->pref_editor_auto_complete_constructs || SSM(sci, SCI_GETLEXER, 0, 0) != SCLEX_LATEX)
+ return;
+
+ if (c == '[')
+ {
+ sci_add_text(sci, "]");
+ }
+ else if (c == '{')
+ {
+ sci_add_text(sci, "}");
+ }
+ sci_set_current_position(sci, pos, TRUE);
+}
+
+
+/* Finds a corresponding matching brace to the given pos
+ * (this is taken from Scintilla Editor.cxx,
+ * fit to work with editor_close_block) */
+static gint brace_match(ScintillaObject *sci, gint pos)
+{
+ gchar chBrace = sci_get_char_at(sci, pos);
+ gchar chSeek = utils_brace_opposite(chBrace);
+ gchar chAtPos;
+ gint direction = -1;
+ gint styBrace;
+ gint depth = 1;
+ gint styAtPos;
+
+ styBrace = sci_get_style_at(sci, pos);
+
+ if (utils_is_opening_brace(chBrace))
+ direction = 1;
+
+ pos = pos + direction;
+ while ((pos >= 0) && (pos < sci_get_length(sci)))
+ {
+ chAtPos = sci_get_char_at(sci, pos - 1);
+ styAtPos = sci_get_style_at(sci, pos);
+
+ if ((pos > sci_get_end_styled(sci)) || (styAtPos == styBrace))
+ {
+ if (chAtPos == chBrace)
+ depth++;
+ if (chAtPos == chSeek)
+ depth--;
+ if (depth == 0)
+ return pos;
+ }
+ pos = pos + direction;
+ }
+ return -1;
+}
+
+
+/* Called after typing '}', if pref_editor_indention_mode is INDENT_ADVANCED. */
+void editor_close_block(gint idx, gint pos)
+{
+ gint x = 0, cnt = 0;
+ gint start_brace, line, line_len, eol_char_len;
+ gchar *text, *line_buf;
+ ScintillaObject *sci;
+
+ if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return;
+
+ sci = doc_list[idx].sci;
+
+ if (! lexer_has_braces(sci))
+ return;
+
+ line = sci_get_line_from_position(sci, pos);
+ line_len = sci_get_line_length(sci, line);
+ // set eol_char_len to 0 if on last line, because there is no EOL char
+ eol_char_len = (line == (SSM(sci, SCI_GETLINECOUNT, 0, 0) - 1)) ? 0 :
+ utils_get_eol_char_len(document_find_by_sci(sci));
+
+ // check that the line is empty, to not kill text in the line
+ line_buf = sci_get_line(sci, line);
+ line_buf[line_len - eol_char_len] = '\0';
+ while (x < (line_len - eol_char_len))
+ {
+ if (isspace(line_buf[x])) cnt++;
+ x++;
+ }
+ g_free(line_buf);
+
+ if ((line_len - eol_char_len - 1) != cnt) return;
+
+ start_brace = brace_match(sci, pos); // same as sci_find_bracematch (Document::BraceMatch)?
+
+ if (start_brace >= 0)
+ {
+ gint line_start;
+
+ get_indent(sci, start_brace, TRUE);
+ text = g_strconcat(indent, "}", NULL);
+ line_start = sci_get_position_from_line(sci, line);
+ sci_set_anchor(sci, line_start);
+ SSM(sci, SCI_REPLACESEL, 0, (sptr_t) text);
+ g_free(text);
+ }
+ else
+ if (sci_get_lexer(sci) == SCLEX_HTML || sci_get_lexer(sci) == SCLEX_TCL)
+ { /* For TCL & PHP brace_match doesn't work here (maybe lexer bugs?),
+ * so this is a simple workaround. */
+ gint line_indent = sci_get_line_indentation(sci, line);
+ gint last_indent = sci_get_line_indentation(sci, line - 1);
+
+ if (line_indent < last_indent)
+ return;
+ line_indent -= app->pref_editor_tab_width;
+ line_indent = MAX(0, line_indent);
+ sci_set_line_indentation(sci, line, line_indent);
+ }
+}
+
+
+/* Reads the word at given cursor position and writes it into the given buffer. The buffer will be
+ * NULL terminated in any case, even when the word is truncated because wordlen is too small.
+ * position can be -1, then the current position is used.
+ * wc are the wordchars to use, if NULL, GEANY_WORDCHARS will be used */
+void editor_find_current_word(ScintillaObject *sci, gint pos, gchar *word, size_t wordlen,
+ const gchar *wc)
+{
+ gint line, line_start, startword, endword;
+ gchar *chunk;
+
+ if (pos == -1)
+ pos = sci_get_current_position(sci);
+
+ line = sci_get_line_from_position(sci, pos);
+ line_start = sci_get_position_from_line(sci, line);
+ startword = pos - line_start;
+ endword = pos - line_start;
+
+ word[0] = '\0';
+ chunk = sci_get_line(sci, line);
+
+ if (wc == NULL)
+ wc = GEANY_WORDCHARS;
+
+ while (startword > 0 && strchr(wc, chunk[startword - 1]))
+ startword--;
+ while (chunk[endword] && strchr(wc, chunk[endword]))
+ endword++;
+ if(startword == endword)
+ return;
+
+ chunk[endword] = '\0';
+
+ g_strlcpy(word, chunk + startword, wordlen); //ensure null terminated
+ g_free(chunk);
+}
+
+
+static gint find_previous_brace(ScintillaObject *sci, gint pos)
+{
+ gchar c;
+ gint orig_pos = pos;
+
+ c = SSM(sci, SCI_GETCHARAT, pos, 0);
+ while (pos >= 0 && pos > orig_pos - 300)
+ {
+ c = SSM(sci, SCI_GETCHARAT, pos, 0);
+ pos--;
+ if (utils_is_opening_brace(c)) return pos;
+ }
+ return -1;
+}
+
+
+static gint find_start_bracket(ScintillaObject *sci, gint pos)
+{
+ gchar c;
+ gint brackets = 0;
+ gint orig_pos = pos;
+
+ c = SSM(sci, SCI_GETCHARAT, pos, 0);
+ while (pos > 0 && pos > orig_pos - 300)
+ {
+ c = SSM(sci, SCI_GETCHARAT, pos, 0);
+ if (c == ')') brackets++;
+ else if (c == '(') brackets--;
+ pos--;
+ if (brackets < 0) return pos; // found start bracket
+ }
+ return -1;
+}
+
+
+static gboolean append_calltip(GString *str, const TMTag *tag, filetype_id ft_id)
+{
+ if (! tag->atts.entry.arglist) return FALSE;
+
+ if (tag->atts.entry.var_type)
+ {
+ guint i;
+
+ g_string_append(str, tag->atts.entry.var_type);
+ for (i = 0; i < tag->atts.entry.pointerOrder; i++)
+ {
+ g_string_append_c(str, '*');
+ }
+ g_string_append_c(str, ' ');
+ }
+ if (tag->atts.entry.scope)
+ {
+ const gchar *cosep = symbols_get_context_separator(ft_id);
+
+ g_string_append(str, tag->atts.entry.scope);
+ g_string_append(str, cosep);
+ }
+ g_string_append(str, tag->name);
+ g_string_append_c(str, ' ');
+ g_string_append(str, tag->atts.entry.arglist);
+
+ return TRUE;
+}
+
+
+static gchar *find_calltip(const gchar *word, filetype *ft)
+{
+ const GPtrArray *tags;
+ const gint arg_types = tm_tag_function_t | tm_tag_prototype_t |
+ tm_tag_method_t | tm_tag_macro_with_arg_t;
+ TMTagAttrType *attrs = NULL;
+ TMTag *tag;
+ GString *str = NULL;
+ guint i;
+
+ g_return_val_if_fail(ft && word && *word, NULL);
+
+ tags = tm_workspace_find(word, arg_types | tm_tag_class_t, attrs, FALSE, ft->lang);
+ if (tags->len == 0)
+ return NULL;
+
+ tag = TM_TAG(tags->pdata[0]);
+
+ if (tag->type == tm_tag_class_t && FILETYPE_ID(ft) == GEANY_FILETYPES_D)
+ {
+ // user typed e.g. 'new Classname(' so lookup D constructor Classname::this()
+ tags = tm_workspace_find_scoped("this", tag->name,
+ arg_types, attrs, FALSE, ft->lang, TRUE);
+ if (tags->len == 0)
+ return NULL;
+ }
+
+ // remove tags with no argument list
+ for (i = 0; i < tags->len; i++)
+ {
+ tag = TM_TAG(tags->pdata[i]);
+
+ if (! tag->atts.entry.arglist)
+ tags->pdata[i] = NULL;
+ }
+ tm_tags_prune((GPtrArray *) tags);
+ if (tags->len == 0)
+ return NULL;
+ else
+ { // remove duplicate calltips
+ TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, tm_tag_attr_scope_t,
+ tm_tag_attr_arglist_t, 0};
+
+ tm_tags_sort((GPtrArray *) tags, sort_attr, TRUE);
+ }
+
+ // if the current word has changed since last time, start with the first tag match
+ if (! utils_str_equal(word, calltip.last_word))
+ calltip.tag_index = 0;
+ // cache the current word for next time
+ g_free(calltip.last_word);
+ calltip.last_word = g_strdup(word);
+ calltip.tag_index = MIN(calltip.tag_index, tags->len - 1); // ensure tag_index is in range
+
+ for (i = calltip.tag_index; i < tags->len; i++)
+ {
+ tag = TM_TAG(tags->pdata[i]);
+
+ if (str == NULL)
+ {
+ str = g_string_new(NULL);
+ if (calltip.tag_index > 0)
+ g_string_prepend(str, "\001 "); // up arrow
+ append_calltip(str, tag, FILETYPE_ID(ft));
+ }
+ else // add a down arrow
+ {
+ if (calltip.tag_index > 0) // already have an up arrow
+ g_string_insert_c(str, 1, '\002');
+ else
+ g_string_prepend(str, "\002 ");
+ break;
+ }
+ }
+ if (str)
+ {
+ gchar *result = str->str;
+
+ g_string_free(str, FALSE);
+ return result;
+ }
+ return NULL;
+}
+
+
+// use pos = -1 to search for the previous unmatched open bracket.
+gboolean editor_show_calltip(gint idx, gint pos)
+{
+ gint orig_pos = pos; // the position for the calltip
+ gint lexer;
+ gint style;
+ gchar word[GEANY_MAX_WORD_LENGTH];
+ gchar *str;
+ ScintillaObject *sci;
+
+ if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return FALSE;
+ sci = doc_list[idx].sci;
+
+ lexer = SSM(sci, SCI_GETLEXER, 0, 0);
+
+ if (pos == -1)
+ {
+ // position of '(' is unknown, so go backwards from current position to find it
+ pos = SSM(sci, SCI_GETCURRENTPOS, 0, 0);
+ pos--;
+ orig_pos = pos;
+ pos = (lexer == SCLEX_LATEX) ? find_previous_brace(sci, pos) :
+ find_start_bracket(sci, pos);
+ if (pos == -1) return FALSE;
+ }
+
+ style = SSM(sci, SCI_GETSTYLEAT, pos, 0);
+ if (is_comment(lexer, style))
+ return FALSE;
+ // never show a calltip in a PHP file outside of the <? ?> tags
+ if (lexer == SCLEX_HTML && ! (style >= SCE_HPHP_DEFAULT && style <= SCE_HPHP_OPERATOR))
+ return FALSE;
+
+ word[0] = '\0';
+ editor_find_current_word(sci, pos - 1, word, sizeof word, NULL);
+ if (word[0] == '\0') return FALSE;
+
+ str = find_calltip(word, doc_list[idx].file_type);
+ if (str)
+ {
+ g_free(calltip.text); // free the old calltip
+ calltip.text = str;
+ calltip.set = TRUE;
+ utils_wrap_string(calltip.text, -1);
+ SSM(sci, SCI_CALLTIPSHOW, orig_pos, (sptr_t) calltip.text);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static void show_autocomplete(ScintillaObject *sci, gint rootlen, const gchar *words)
+{
+ // store whether a calltip is showing, so we can reshow it after autocompletion
+ calltip.set = SSM(sci, SCI_CALLTIPACTIVE, 0, 0);
+ SSM(sci, SCI_AUTOCSHOW, rootlen, (sptr_t) words);
+}
+
+
+static gboolean
+autocomplete_html(ScintillaObject *sci, const gchar *root, gsize rootlen)
+{ // HTML entities auto completion
+ guint i, j = 0;
+ GString *words;
+ const gchar **entities = symbols_get_html_entities();
+
+ if (*root != '&' || entities == NULL) return FALSE;
+
+ words = g_string_sized_new(500);
+ for (i = 0; ; i++)
+ {
+ if (entities[i] == NULL) break;
+ else if (entities[i][0] == '#') continue;
+
+ if (! strncmp(entities[i], root, rootlen))
+ {
+ if (j++ > 0) g_string_append_c(words, ' ');
+ g_string_append(words, entities[i]);
+ }
+ }
+ if (words->len > 0) show_autocomplete(sci, rootlen, words->str);
+ g_string_free(words, TRUE);
+ return TRUE;
+}
+
+
+static gboolean
+autocomplete_tags(gint idx, gchar *root, gsize rootlen)
+{ // PHP, LaTeX, C, C++, D and Java tag autocompletion
+ TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
+ const GPtrArray *tags;
+ ScintillaObject *sci;
+
+ if (! DOC_IDX_VALID(idx) || doc_list[idx].file_type == NULL)
+ return FALSE;
+ sci = doc_list[idx].sci;
+
+ tags = tm_workspace_find(root, tm_tag_max_t, attrs, TRUE, doc_list[idx].file_type->lang);
+ if (NULL != tags && tags->len > 0)
+ {
+ GString *words = g_string_sized_new(150);
+ guint j;
+
+ for (j = 0; ((j < tags->len) && (j < GEANY_MAX_AUTOCOMPLETE_WORDS)); ++j)
+ {
+ if (j > 0) g_string_append_c(words, ' ');
+ g_string_append(words, ((TMTag *) tags->pdata[j])->name);
+ }
+ show_autocomplete(sci, rootlen, words->str);
+ g_string_free(words, TRUE);
+ }
+ return TRUE;
+}
+
+
+gboolean editor_start_auto_complete(gint idx, gint pos, gboolean force)
+{
+ gint line, line_start, line_len, line_pos, current, rootlen, startword, lexer, style;
+ gchar *linebuf, *root;
+ ScintillaObject *sci;
+ gboolean ret = FALSE;
+ gchar *wordchars;
+ filetype *ft;
+
+ if ((! app->pref_editor_auto_complete_symbols && ! force) ||
+ ! DOC_IDX_VALID(idx) || doc_list[idx].file_type == NULL)
+ return FALSE;
+
+ sci = doc_list[idx].sci;
+ ft = doc_list[idx].file_type;
+
+ line = sci_get_line_from_position(sci, pos);
+ line_start = sci_get_position_from_line(sci, line);
+ line_len = sci_get_line_length(sci, line);
+ line_pos = pos - line_start - 1;
+ current = pos - line_start;
+ startword = current;
+ lexer = SSM(sci, SCI_GETLEXER, 0, 0);
+ style = SSM(sci, SCI_GETSTYLEAT, pos, 0);
+
+ // don't autocomplete in comments and strings
+ if (is_comment(lexer, style))
+ return FALSE;
+
+ linebuf = sci_get_line(sci, line);
+
+ if (ft->id == GEANY_FILETYPES_LATEX)
+ wordchars = GEANY_WORDCHARS"\\"; // add \ to word chars if we are in a LaTeX file
+ else if (ft->id == GEANY_FILETYPES_HTML || ft->id == GEANY_FILETYPES_PHP)
+ wordchars = GEANY_WORDCHARS"&"; // add & to word chars if we are in a PHP or HTML file
+ else
+ wordchars = GEANY_WORDCHARS;
+
+ // find the start of the current word
+ while ((startword > 0) && (strchr(wordchars, linebuf[startword - 1])))
+ startword--;
+ linebuf[current] = '\0';
+ root = linebuf + startword;
+ rootlen = current - startword;
+
+ // entity autocompletion always in a HTML file, in a PHP file only when we are outside of <? ?>
+ if (ft->id == GEANY_FILETYPES_HTML ||
+ (ft->id == GEANY_FILETYPES_PHP && (style < SCE_HPHP_DEFAULT || style > SCE_HPHP_OPERATOR)))
+ ret = autocomplete_html(sci, root, rootlen);
+ else
+ {
+ // force is set when called by keyboard shortcut, otherwise start at the 4th char
+ if (force || rootlen >= 4)
+ ret = autocomplete_tags(idx, root, rootlen);
+ }
+
+ g_free(linebuf);
+ return ret;
+}
+
+
+void editor_auto_latex(gint idx, gint pos)
+{
+ ScintillaObject *sci;
+
+ if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return;
+ sci = doc_list[idx].sci;
+
+ if (sci_get_char_at(sci, pos - 2) == '}')
+ {
+ gchar *eol, *buf, *construct;
+ gchar env[50];
+ gint line = sci_get_line_from_position(sci, pos - 2);
+ gint line_len = sci_get_line_length(sci, line);
+ gint i, start;
+
+ // get the line
+ buf = sci_get_line(sci, line);
+
+ // get to the first non-blank char (some kind of ltrim())
+ start = 0;
+ //while (isspace(buf[i++])) start++;
+ while (isspace(buf[start])) start++;
+
+ // check for begin
+ if (strncmp(buf + start, "\\begin", 6) == 0)
+ {
+ gchar full_cmd[15];
+ guint j = 0;
+
+ // take also "\begingroup" (or whatever there can be) and append "\endgroup" and so on.
+ i = start + 6;
+ while (i < line_len && buf[i] != '{' && j < (sizeof(full_cmd) - 1))
+ { // copy all between "\begin" and "{" to full_cmd
+ full_cmd[j] = buf[i];
+ i++;
+ j++;
+ }
+ full_cmd[j] = '\0';
+
+ // go through the line and get the environment
+ for (i = start + j; i < line_len; i++)
+ {
+ if (buf[i] == '{')
+ {
+ j = 0;
+ i++;
+ while (buf[i] != '}' && j < (sizeof(env) - 1))
+ { // this could be done in a shorter way, but so it remains readable ;-)
+ env[j] = buf[i];
+ j++;
+ i++;
+ }
+ env[j] = '\0';
+ break;
+ }
+ }
+
+ // get the indention
+ if (doc_list[idx].use_auto_indention) get_indent(sci, pos, TRUE);
+ eol = g_strconcat(utils_get_eol_char(idx), indent, NULL);
+
+ construct = g_strdup_printf("%s\\end%s{%s}", eol, full_cmd, env);
+
+ SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) construct);
+ sci_goto_pos(sci, pos + 1, TRUE);
+ g_free(construct);
+ g_free(eol);
+ }
+ // later there could be some else ifs for other keywords
+
+ g_free(buf);
+ }
+}
+
+
+/* This should use a string with the current word instead of buf, but we will replace this
+ * code with user-defined construct completion. */
+static gboolean complete_constructs(gint idx, gint pos, const gchar *buf, const gchar *space,
+ const gchar *eol)
+{
+ gboolean result;
+ gchar *construct = NULL;
+ gint space_len = strlen(space);
+ ScintillaObject *sci = doc_list[idx].sci;
+
+ // "pattern", buf + x, y -> x + y = 15, because buf is (pos - 15)...(pos) = 15
+ if (! strncmp("if", buf + 13, 2))
+ {
+ if (! isspace(*(buf + 12)))
+ return FALSE;
+
+ construct = g_strdup_printf("()%s{%s%s%s}%s", eol, eol, space, eol, eol);
+
+ SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) construct);
+ sci_goto_pos(sci, pos + 1, TRUE);
+ }
+ else if (! strncmp("else", buf + 11, 4))
+ {
+ if (! isspace(*(buf + 10)))
+ return FALSE;
+
+ construct = g_strdup_printf("%s{%s%s%s}%s", eol, eol, space, eol, eol);
+
+ SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) construct);
+ sci_goto_pos(sci, pos + 3 + space_len + (2 * strlen(indent)), TRUE);
+ }
+ else if (! strncmp("for", buf + 12, 3))
+ {
+ gchar *var;
+ gint contruct_len;
+
+ if (! isspace(*(buf + 11)))
+ return FALSE;
+
+ if (doc_list[idx].file_type->id == GEANY_FILETYPES_PHP)
+ {
+ var = g_strdup("$i");
+ contruct_len = 14;
+ }
+ else
+ {
+ var = g_strdup("i");
+ contruct_len = 12;
+ }
+ construct = g_strdup_printf("(%s%s = 0; %s < ; %s++)%s{%s%s%s}%s",
+ (doc_list[idx].file_type->id == GEANY_FILETYPES_CPP) ? "int " : "",
+ var, var, var, eol, eol, space, eol, eol);
+
+ // add 4 characters because of "int " in C++ mode
+ contruct_len += (doc_list[idx].file_type->id == GEANY_FILETYPES_CPP) ? 4 : 0;
+
+ SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) construct);
+ sci_goto_pos(sci, pos + contruct_len, TRUE);
+ g_free(var);
+ }
+ else if (! strncmp("while", buf + 10, 5))
+ {
+ if (! isspace(*(buf + 9)))
+ return FALSE;
+
+ construct = g_strdup_printf("()%s{%s%s%s}%s", eol, eol, space, eol, eol);
+
+ SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) construct);
+ sci_goto_pos(sci, pos + 1, TRUE);
+ }
+ else if (! strncmp("do", buf + 13, 2))
+ {
+ if (! isspace(*(buf + 12)))
+ return FALSE;
+
+ construct = g_strdup_printf("%s{%s%s%s}%swhile ();%s", eol, eol, space, eol, eol, eol);
+
+ SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) construct);
+ sci_goto_pos(sci, pos + 3 + space_len + (2 * strlen(indent)), TRUE);
+ }
+ else if (! strncmp("try", buf + 12, 3))
+ {
+ if (! isspace(*(buf + 11)))
+ return FALSE;
+
+ construct = g_strdup_printf("%s{%s%s%s}%scatch ()%s{%s%s%s}%s",
+ eol, eol, space, eol, eol, eol, eol, space, eol, eol);
+
+ SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) construct);
+ sci_goto_pos(sci, pos + 3 + space_len + (2 * strlen(indent)), TRUE);
+ }
+ else if (! strncmp("switch", buf + 9, 6))
+ {
+ if (! isspace(*(buf + 8)))
+ return FALSE;
+
+ construct = g_strdup_printf("()%s{%s%scase : break;%s%sdefault: %s}%s",
+ eol, eol, space, eol, space, eol, eol);
+
+ SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) construct);
+ sci_goto_pos(sci, pos + 1, TRUE);
+ }
+ else if (doc_list[idx].file_type->id == GEANY_FILETYPES_FERITE && ! strncmp("iferr", buf + 10, 5))
+ {
+ if (! isspace(*(buf + 9)))
+ return FALSE;
+
+ construct = g_strdup_printf("%s{%s%s%s}%sfix%s{%s%s%s}%s",
+ eol, eol, space, eol, eol, eol, eol, space, eol, eol);
+
+ SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) construct);
+ sci_goto_pos(sci, pos + 3 + space_len + (2 * strlen(indent)), TRUE);
+ }
+ else if (doc_list[idx].file_type->id == GEANY_FILETYPES_FERITE && ! strncmp("monitor", buf + 8, 7))
+ {
+ if (! isspace(*(buf + 7)))
+ return FALSE;
+
+ construct = g_strdup_printf("%s{%s%s%s}%shandle%s{%s%s%s}%s",
+ eol, eol, space, eol, eol, eol, eol, space, eol, eol);
+
+ SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) construct);
+ sci_goto_pos(sci, pos + 3 + space_len + (2 * strlen(indent)), TRUE);
+ }
+ result = (construct != NULL);
+ if (result)
+ {
+ sci_insert_text(sci, pos, " "); // prefix all constructs with a space
+ }
+ g_free(construct);
+ return result;
+}
+
+
+static gboolean at_eol(ScintillaObject *sci, gint pos)
+{
+ gint line = sci_get_line_from_position(sci, pos);
+
+ return (pos == sci_get_line_end_position(sci, line));
+}
+
+
+gboolean editor_auto_forif(gint idx, gint pos)
+{
+ gboolean result;
+ gchar buf[16];
+ gchar *eol;
+ gchar *space;
+ gint lexer, style;
+ gint i;
+ ScintillaObject *sci;
+
+ if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return FALSE;
+
+ // only for C, C++, D, Ferite, Java, JavaScript, Perl and PHP
+ if (doc_list[idx].file_type->id != GEANY_FILETYPES_PHP &&
+ doc_list[idx].file_type->id != GEANY_FILETYPES_C &&
+ doc_list[idx].file_type->id != GEANY_FILETYPES_D &&
+ doc_list[idx].file_type->id != GEANY_FILETYPES_CPP &&
+ doc_list[idx].file_type->id != GEANY_FILETYPES_PERL &&
+ doc_list[idx].file_type->id != GEANY_FILETYPES_JAVA &&
+ doc_list[idx].file_type->id != GEANY_FILETYPES_JS &&
+ doc_list[idx].file_type->id != GEANY_FILETYPES_FERITE)
+ return FALSE;
+
+ sci = doc_list[idx].sci;
+ // return if we are editing an existing line (chars on right of cursor)
+ if (! at_eol(sci, pos))
+ return FALSE;
+
+ lexer = SSM(sci, SCI_GETLEXER, 0, 0);
+ style = SSM(sci, SCI_GETSTYLEAT, pos - 2, 0);
+ // return, if we are in a comment
+ if (is_comment(lexer, style))
+ return FALSE;
+ // never auto complete in a PHP file outside of the <? ?> tags
+ if (lexer == SCLEX_HTML && ! (style >= SCE_HPHP_DEFAULT && style <= SCE_HPHP_OPERATOR))
+ return FALSE;
+
+ sci_get_text_range(sci, pos - 15, pos, buf);
+ if (sizeof(buf) != strlen(buf) + 1)
+ return FALSE; // not enough chars in document
+
+ /* check that the chars before the current word are only whitespace (on this line).
+ * this prevents completion of '} while ' */
+ i = 14; // index before \0 char
+ while (i >= 0 && isalpha(buf[i])) i--; // find pos before keyword
+ while (i >= 0 && buf[i] != '\n' && buf[i] != '\r') // we want to stay in this line('\n' check)
+ {
+ if (! isspace(buf[i]))
+ {
+ return FALSE;
+ }
+ i--;
+ }
+
+ // get the indentation
+ if (doc_list[idx].use_auto_indention) get_indent(sci, pos, TRUE);
+ eol = g_strconcat(utils_get_eol_char(idx), indent, NULL);
+
+ // get the whitespace for additional indentation
+ space = utils_get_whitespace(app->pref_editor_tab_width, FALSE);
+
+ sci_start_undo_action(sci); // needed while we insert a space separately from construct
+ result = complete_constructs(idx, pos, buf, space, eol);
+ sci_end_undo_action(sci);
+
+ utils_free_pointers(eol, space, NULL);
+ return result;
+}
+
+
+void editor_show_macro_list(ScintillaObject *sci)
+{
+ GString *words;
+
+ if (sci == NULL) return;
+
+ words = symbols_get_macro_list();
+ if (words == NULL) return;
+
+ SSM(sci, SCI_USERLISTSHOW, 1, (sptr_t) words->str);
+ g_string_free(words, TRUE);
+}
+
+
+/**
+ * (stolen from anjuta and heavily modified)
+ * This routine will auto complete XML or HTML tags that are still open by closing them
+ * @param ch The character we are dealing with, currently only works with the '>' character
+ * @return True if handled, false otherwise
+ */
+static gboolean handle_xml(ScintillaObject *sci, gchar ch, gint idx)
+{
+ gint lexer = SSM(sci, SCI_GETLEXER, 0, 0);
+ gint pos, min;
+ gchar *str_found, sel[512];
+
+ // If the user has turned us off, quit now.
+ // This may make sense only in certain languages
+ if (! app->pref_editor_auto_close_xml_tags || (lexer != SCLEX_HTML && lexer != SCLEX_XML))
+ return FALSE;
+
+ pos = sci_get_current_position(sci);
+
+ // return if we are in PHP but not in a string or outside of <? ?> tags
+ if (doc_list[idx].file_type->id == GEANY_FILETYPES_PHP)
+ {
+ gint style = sci_get_style_at(sci, pos);
+ if (style != SCE_HPHP_SIMPLESTRING && style != SCE_HPHP_HSTRING &&
+ style <= SCE_HPHP_OPERATOR && style >= SCE_HPHP_DEFAULT)
+ return FALSE;
+ }
+
+ // if ch is /, check for </, else quit
+ if (ch == '/' && sci_get_char_at(sci, pos - 2) != '<')
+ return FALSE;
+
+ // Grab the last 512 characters or so
+ min = pos - (sizeof(sel) - 1);
+ if (min < 0) min = 0;
+
+ if (pos - min < 3)
+ return FALSE; // Smallest tag is 3 characters e.g. <p>
+
+ sci_get_text_range(sci, min, pos, sel);
+ sel[sizeof(sel) - 1] = '\0';
+
+ if (ch == '>' && sel[pos - min - 2] == '/')
+ // User typed something like "<br/>"
+ return FALSE;
+
+ if (ch == '/')
+ str_found = utils_find_open_xml_tag(sel, pos - min, TRUE);
+ else
+ str_found = utils_find_open_xml_tag(sel, pos - min, FALSE);
+
+ // when found string is something like br, img or another short tag, quit
+ if (utils_str_equal(str_found, "br")
+ || utils_str_equal(str_found, "img")
+ || utils_str_equal(str_found, "base")
+ || utils_str_equal(str_found, "basefont") // < or not <
+ || utils_str_equal(str_found, "frame")
+ || utils_str_equal(str_found, "input")
+ || utils_str_equal(str_found, "link")
+ || utils_str_equal(str_found, "area")
+ || utils_str_equal(str_found, "meta"))
+ {
+ return FALSE;
+ }
+
+ if (strlen(str_found) > 0)
+ {
+ gchar *to_insert;
+ if (ch == '/')
+ to_insert = g_strconcat(str_found, ">", NULL);
+ else
+ to_insert = g_strconcat("</", str_found, ">", NULL);
+ sci_start_undo_action(sci);
+ sci_replace_sel(sci, to_insert);
+ if (ch == '>')
+ {
+ SSM(sci, SCI_SETSEL, pos, pos);
+ if (utils_str_equal(str_found, "table")) editor_auto_table(sci, pos);
+ }
+ sci_end_undo_action(sci);
+ g_free(to_insert);
+ g_free(str_found);
+ return TRUE;
+ }
+
+
+ g_free(str_found);
+ return FALSE;
+}
+
+
+void editor_auto_table(ScintillaObject *sci, gint pos)
+{
+ gchar *table;
+ gint indent_pos;
+
+ if (SSM(sci, SCI_GETLEXER, 0, 0) != SCLEX_HTML) return;
+
+ get_indent(sci, pos, TRUE);
+ indent_pos = sci_get_line_indent_position(sci, sci_get_line_from_position(sci, pos));
+ if ((pos - 7) != indent_pos) // 7 == strlen("<table>")
+ {
+ gint i, x;
+ x = strlen(indent);
+ // find the start of the <table tag
+ i = 1;
+ while (i <= pos && sci_get_char_at(sci, pos - i) != '<') i++;
+ // add all non whitespace before the tag to the indent string
+ while ((pos - i) != indent_pos)
+ {
+ indent[x++] = ' ';
+ i++;
+ }
+ indent[x] = '\0';
+ }
+
+ table = g_strconcat("\n", indent, " <tr>\n", indent, " <td>\n", indent, " </td>\n",
+ indent, " </tr>\n", indent, NULL);
+ sci_insert_text(sci, pos, table);
+ g_free(table);
+}
+
+
+static void real_comment_multiline(gint idx, gint line_start, gint last_line)
+{
+ gchar *eol, *str_begin, *str_end;
+ gint line_len;
+
+ if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return;
+
+ eol = utils_get_eol_char(idx);
+ str_begin = g_strdup_printf("%s%s", doc_list[idx].file_type->comment_open, eol);
+ str_end = g_strdup_printf("%s%s", doc_list[idx].file_type->comment_close, eol);
+
+ // insert the comment strings
+ sci_insert_text(doc_list[idx].sci, line_start, str_begin);
+ line_len = sci_get_position_from_line(doc_list[idx].sci, last_line + 2);
+ sci_insert_text(doc_list[idx].sci, line_len, str_end);
+
+ g_free(str_begin);
+ g_free(str_end);
+}
+
+
+static void real_uncomment_multiline(gint idx)
+{
+ // find the beginning of the multi line comment
+ gint pos, line, len, x;
+ gchar *linebuf;
+
+ if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return;
+
+ // remove comment open chars
+ pos = document_find_text(idx, doc_list[idx].file_type->comment_open, 0, TRUE, FALSE);
+ SSM(doc_list[idx].sci, SCI_DELETEBACK, 0, 0);
+
+ // check whether the line is empty and can be deleted
+ line = sci_get_line_from_position(doc_list[idx].sci, pos);
+ len = sci_get_line_length(doc_list[idx].sci, line);
+ linebuf = sci_get_line(doc_list[idx].sci, line);
+ x = 0;
+ while (linebuf[x] != '\0' && isspace(linebuf[x])) x++;
+ if (x == len) SSM(doc_list[idx].sci, SCI_LINEDELETE, 0, 0);
+ g_free(linebuf);
+
+ // remove comment close chars
+ pos = document_find_text(idx, doc_list[idx].file_type->comment_close, 0, FALSE, FALSE);
+ SSM(doc_list[idx].sci, SCI_DELETEBACK, 0, 0);
+
+ // check whether the line is empty and can be deleted
+ line = sci_get_line_from_position(doc_list[idx].sci, pos);
+ len = sci_get_line_length(doc_list[idx].sci, line);
+ linebuf = sci_get_line(doc_list[idx].sci, line);
+ x = 0;
+ while (linebuf[x] != '\0' && isspace(linebuf[x])) x++;
+ if (x == len) SSM(doc_list[idx].sci, SCI_LINEDELETE, 0, 0);
+ g_free(linebuf);
+}
+
+
+void editor_do_uncomment(gint idx, gint line)
+{
+ gint first_line, last_line;
+ gint x, i, line_start, line_len;
+ gint sel_start, sel_end;
+ gsize co_len;
+ gchar sel[256], *co, *cc;
+ gboolean break_loop = FALSE, single_line = FALSE;
+ filetype *ft;
+
+ if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return;
+
+ if (line < 0)
+ { // use selection or current line
+ sel_start = sci_get_selection_start(doc_list[idx].sci);
+ sel_end = sci_get_selection_end(doc_list[idx].sci);
+
+ first_line = sci_get_line_from_position(doc_list[idx].sci, sel_start);
+ // Find the last line with chars selected (not EOL char)
+ last_line = sci_get_line_from_position(doc_list[idx].sci, sel_end - 1);
+ last_line = MAX(first_line, last_line);
+ }
+ else
+ {
+ first_line = last_line = line;
+ sel_start = sel_end = sci_get_position_from_line(doc_list[idx].sci, line);
+ }
+
+ ft = doc_list[idx].file_type;
+
+ // detection of HTML vs PHP code, if non-PHP set filetype to XML
+ line_start = sci_get_position_from_line(doc_list[idx].sci, first_line);
+ if (ft->id == GEANY_FILETYPES_PHP)
+ {
+ if (sci_get_style_at(doc_list[idx].sci, line_start) < 118 ||
+ sci_get_style_at(doc_list[idx].sci, line_start) > 127)
+ ft = filetypes[GEANY_FILETYPES_XML];
+ }
+
+ co = ft->comment_open;
+ cc = ft->comment_close;
+ if (co == NULL) return;
+
+ co_len = strlen(co);
+ if (co_len == 0) return;
+
+ SSM(doc_list[idx].sci, SCI_BEGINUNDOACTION, 0, 0);
+
+ for (i = first_line; (i <= last_line) && (! break_loop); i++)
+ {
+ gint buf_len;
+
+ line_start = sci_get_position_from_line(doc_list[idx].sci, i);
+ line_len = sci_get_line_length(doc_list[idx].sci, i);
+ x = 0;
+
+ buf_len = MIN((gint)sizeof(sel) - 1, line_len - 1);
+ if (buf_len <= 0)
+ continue;
+ sci_get_text_range(doc_list[idx].sci, line_start, line_start + buf_len, sel);
+ sel[buf_len] = '\0';
+
+ while (isspace(sel[x])) x++;
+
+ // to skip blank lines
+ if (x < line_len && sel[x] != '\0')
+ {
+ // use single line comment
+ if (cc == NULL || strlen(cc) == 0)
+ {
+ guint j;
+
+ single_line = TRUE;
+
+ switch (co_len)
+ {
+ case 1: if (sel[x] != co[0]) continue; break;
+ case 2: if (sel[x] != co[0] || sel[x+1] != co[1]) continue; break;
+ case 3: if (sel[x] != co[0] || sel[x+1] != co[1] || sel[x+2] != co[2])
+ continue; break;
+ default: continue;
+ }
+
+ SSM(doc_list[idx].sci, SCI_GOTOPOS, line_start + x + co_len, 0);
+ for (j = 0; j < co_len; j++) SSM(doc_list[idx].sci, SCI_DELETEBACK, 0, 0);
+ }
+ // use multi line comment
+ else
+ {
+ gint style_comment;
+ gint lexer = SSM(doc_list[idx].sci, SCI_GETLEXER, 0, 0);
+
+ // process only lines which are already comments
+ switch (lexer)
+ { // I will list only those lexers which support multi line comments
+ case SCLEX_XML:
+ case SCLEX_HTML:
+ {
+ if (sci_get_style_at(doc_list[idx].sci, line_start) >= 118 &&
+ sci_get_style_at(doc_list[idx].sci, line_start) <= 127)
+ style_comment = SCE_HPHP_COMMENT;
+ else style_comment = SCE_H_COMMENT;
+ break;
+ }
+ case SCLEX_CSS: style_comment = SCE_CSS_COMMENT; break;
+ case SCLEX_SQL: style_comment = SCE_SQL_COMMENT; break;
+ case SCLEX_CAML: style_comment = SCE_CAML_COMMENT; break;
+ case SCLEX_D: style_comment = SCE_D_COMMENT; break;
+ default: style_comment = SCE_C_COMMENT;
+ }
+ if (sci_get_style_at(doc_list[idx].sci, line_start + x) == style_comment)
+ {
+ real_uncomment_multiline(idx);
+ }
+
+ // break because we are already on the last line
+ break_loop = TRUE;
+ break;
+ }
+ }
+ }
+ SSM(doc_list[idx].sci, SCI_ENDUNDOACTION, 0, 0);
+
+ // restore selection if there is one
+ if (sel_start < sel_end)
+ {
+ if (single_line)
+ {
+ sci_set_selection_start(doc_list[idx].sci, sel_start - co_len);
+ sci_set_selection_end(doc_list[idx].sci, sel_end - ((i - first_line) * co_len));
+ }
+ else
+ {
+ gint eol_len = (sci_get_eol_mode(doc_list[idx].sci) == SC_EOL_CRLF) ? 2 : 1;
+ sci_set_selection_start(doc_list[idx].sci, sel_start - co_len - eol_len);
+ sci_set_selection_end(doc_list[idx].sci, sel_end - co_len - eol_len);
+ }
+ }
+}
+
+
+void editor_do_comment_toggle(gint idx)
+{
+ gint first_line, last_line;
+ gint x, i, line_start, line_len;
+ gint sel_start, sel_end, co_len;
+ gint count_commented = 0, count_uncommented = 0;
+ gchar sel[256], *co, *cc;
+ gboolean break_loop = FALSE, single_line = FALSE;
+ gboolean first_line_was_comment = FALSE;
+ filetype *ft;
+
+ if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return;
+
+ sel_start = sci_get_selection_start(doc_list[idx].sci);
+ sel_end = sci_get_selection_end(doc_list[idx].sci);
+
+ ft = doc_list[idx].file_type;
+
+ first_line = sci_get_line_from_position(doc_list[idx].sci,
+ sci_get_selection_start(doc_list[idx].sci));
+ // Find the last line with chars selected (not EOL char)
+ last_line = sci_get_line_from_position(doc_list[idx].sci,
+ sci_get_selection_end(doc_list[idx].sci) - 1);
+ last_line = MAX(first_line, last_line);
+
+ // detection of HTML vs PHP code, if non-PHP set filetype to XML
+ line_start = sci_get_position_from_line(doc_list[idx].sci, first_line);
+ if (ft->id == GEANY_FILETYPES_PHP)
+ {
+ if (sci_get_style_at(doc_list[idx].sci, line_start) < 118 ||
+ sci_get_style_at(doc_list[idx].sci, line_start) > 127)
+ ft = filetypes[GEANY_FILETYPES_XML];
+ }
+
+ co = ft->comment_open;
+ cc = ft->comment_close;
+ if (co == NULL) return;
+
+ co_len = strlen(co);
+ if (co_len == 0) return;
+
+ SSM(doc_list[idx].sci, SCI_BEGINUNDOACTION, 0, 0);
+
+ for (i = first_line; (i <= last_line) && (! break_loop); i++)
+ {
+ gint buf_len;
+
+ line_start = sci_get_position_from_line(doc_list[idx].sci, i);
+ line_len = sci_get_line_length(doc_list[idx].sci, i);
+ x = 0;
+
+ buf_len = MIN((gint)sizeof(sel) - 1, line_len - 1);
+ if (buf_len <= 0)
+ continue;
+ sci_get_text_range(doc_list[idx].sci, line_start, line_start + buf_len, sel);
+ sel[buf_len] = '\0';
+
+ while (isspace(sel[x])) x++;
+
+ // to skip blank lines
+ if (x < line_len && sel[x] != '\0')
+ {
+ // use single line comment
+ if (cc == NULL || strlen(cc) == 0)
+ {
+ gboolean do_continue = FALSE;
+ single_line = TRUE;
+
+ switch (co_len)
+ {
+ case 1:
+ if (sel[x] == co[0])
+ {
+ do_continue = TRUE;
+ editor_do_uncomment(idx, i);
+ count_uncommented++;
+ }
+ break;
+ case 2:
+ if (sel[x] == co[0] && sel[x+1] == co[1])
+ {
+ do_continue = TRUE;
+ editor_do_uncomment(idx, i);
+ count_uncommented++;
+ }
+ break;
+ case 3:
+ if (sel[x] == co[0] && sel[x+1] == co[1] && sel[x+2] == co[2])
+ {
+ do_continue = TRUE;
+ editor_do_uncomment(idx, i);
+ count_uncommented++;
+ }
+ break;
+ default: return;
+ }
+ if (do_continue && i == first_line) first_line_was_comment = TRUE;
+ if (do_continue) continue;
+
+ // we are still here, so the above lines were not already comments, so comment it
+ editor_do_comment(idx, i, FALSE);
+ count_commented++;
+ }
+ // use multi line comment
+ else
+ {
+ gint style_comment;
+ gint lexer = SSM(doc_list[idx].sci, SCI_GETLEXER, 0, 0);
+
+ // skip lines which are already comments
+ switch (lexer)
+ { // I will list only those lexers which support multi line comments
+ case SCLEX_XML:
+ case SCLEX_HTML:
+ {
+ if (sci_get_style_at(doc_list[idx].sci, line_start) >= 118 &&
+ sci_get_style_at(doc_list[idx].sci, line_start) <= 127)
+ style_comment = SCE_HPHP_COMMENT;
+ else style_comment = SCE_H_COMMENT;
+ break;
+ }
+ case SCLEX_CSS: style_comment = SCE_CSS_COMMENT; break;
+ case SCLEX_SQL: style_comment = SCE_SQL_COMMENT; break;
+ case SCLEX_CAML: style_comment = SCE_CAML_COMMENT; break;
+ case SCLEX_D: style_comment = SCE_D_COMMENT; break;
+ default: style_comment = SCE_C_COMMENT;
+ }
+ if (sci_get_style_at(doc_list[idx].sci, line_start + x) == style_comment)
+ {
+ real_uncomment_multiline(idx);
+ count_uncommented++;
+ }
+ else
+ {
+ real_comment_multiline(idx, line_start, last_line);
+ count_commented++;
+ }
+
+ // break because we are already on the last line
+ break_loop = TRUE;
+ break;
+ }
+ }
+ }
+
+ SSM(doc_list[idx].sci, SCI_ENDUNDOACTION, 0, 0);
+
+ // restore selection if there is one
+ if (sel_start < sel_end)
+ {
+ if (single_line)
+ {
+ gint a = (first_line_was_comment) ? - co_len : co_len;
+
+ // don't modify sel_start when the selection starts within indentation
+ get_indent(doc_list[idx].sci, sel_start, TRUE);
+ if ((sel_start - line_start) <= (gint) strlen(indent))
+ a = 0;
+
+ sci_set_selection_start(doc_list[idx].sci, sel_start + a);
+ sci_set_selection_end(doc_list[idx].sci, sel_end +
+ (count_commented * co_len) - (count_uncommented * co_len));
+ }
+ else
+ {
+ gint eol_len = (sci_get_eol_mode(doc_list[idx].sci) == SC_EOL_CRLF) ? 2 : 1;
+ if (count_uncommented > 0)
+ {
+ sci_set_selection_start(doc_list[idx].sci, sel_start - co_len - eol_len);
+ sci_set_selection_end(doc_list[idx].sci, sel_end - co_len - eol_len);
+ }
+ else
+ {
+ sci_set_selection_start(doc_list[idx].sci, sel_start + co_len + eol_len);
+ sci_set_selection_end(doc_list[idx].sci, sel_end + co_len + eol_len);
+ }
+ }
+ }
+ else if (count_uncommented > 0)
+ {
+ gint eol_len = (sci_get_eol_mode(doc_list[idx].sci) == SC_EOL_CRLF) ? 2 : 1;
+ sci_set_current_position(doc_list[idx].sci, sel_start - co_len - eol_len, TRUE);
+ }
+}
+
+
+void editor_do_comment(gint idx, gint line, gboolean allow_empty_lines)
+{
+ gint first_line, last_line;
+ gint x, i, line_start, line_len;
+ gint sel_start, sel_end, co_len;
+ gchar sel[256], *co, *cc;
+ gboolean break_loop = FALSE, single_line = FALSE;
+ filetype *ft;
+
+ if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return;
+
+ if (line < 0)
+ { // use selection or current line
+ sel_start = sci_get_selection_start(doc_list[idx].sci);
+ sel_end = sci_get_selection_end(doc_list[idx].sci);
+
+ first_line = sci_get_line_from_position(doc_list[idx].sci, sel_start);
+ // Find the last line with chars selected (not EOL char)
+ last_line = sci_get_line_from_position(doc_list[idx].sci, sel_end - 1);
+ last_line = MAX(first_line, last_line);
+ }
+ else
+ {
+ first_line = last_line = line;
+ sel_start = sel_end = sci_get_position_from_line(doc_list[idx].sci, line);
+ }
+
+ ft = doc_list[idx].file_type;
+
+ // detection of HTML vs PHP code, if non-PHP set filetype to XML
+ line_start = sci_get_position_from_line(doc_list[idx].sci, first_line);
+ if (ft->id == GEANY_FILETYPES_PHP)
+ {
+ if (sci_get_style_at(doc_list[idx].sci, line_start) < 118 ||
+ sci_get_style_at(doc_list[idx].sci, line_start) > 127)
+ ft = filetypes[GEANY_FILETYPES_XML];
+ }
+
+ co = ft->comment_open;
+ cc = ft->comment_close;
+ if (co == NULL) return;
+
+ co_len = strlen(co);
+ if (co_len == 0) return;
+
+ SSM(doc_list[idx].sci, SCI_BEGINUNDOACTION, 0, 0);
+
+ for (i = first_line; (i <= last_line) && (! break_loop); i++)
+ {
+ gint buf_len;
+
+ line_start = sci_get_position_from_line(doc_list[idx].sci, i);
+ line_len = sci_get_line_length(doc_list[idx].sci, i);
+ x = 0;
+
+ buf_len = MIN((gint)sizeof(sel) - 1, line_len - 1);
+ if (buf_len <= 0)
+ continue;
+ sci_get_text_range(doc_list[idx].sci, line_start, line_start + buf_len, sel);
+ sel[buf_len] = '\0';
+
+ while (isspace(sel[x])) x++;
+
+ // to skip blank lines
+ if (allow_empty_lines || (x < line_len && sel[x] != '\0'))
+ {
+ // use single line comment
+ if (cc == NULL || strlen(cc) == 0)
+ {
+ single_line = TRUE;
+
+ if (ft->comment_use_indent)
+ sci_insert_text(doc_list[idx].sci, line_start + x, co);
+ else
+ sci_insert_text(doc_list[idx].sci, line_start, co);
+ }
+ // use multi line comment
+ else
+ {
+ gint style_comment;
+ gint lexer = SSM(doc_list[idx].sci, SCI_GETLEXER, 0, 0);
+
+ // skip lines which are already comments
+ switch (lexer)
+ { // I will list only those lexers which support multi line comments
+ case SCLEX_XML:
+ case SCLEX_HTML:
+ {
+ if (sci_get_style_at(doc_list[idx].sci, line_start) >= 118 &&
+ sci_get_style_at(doc_list[idx].sci, line_start) <= 127)
+ style_comment = SCE_HPHP_COMMENT;
+ else style_comment = SCE_H_COMMENT;
+ break;
+ }
+ case SCLEX_CSS: style_comment = SCE_CSS_COMMENT; break;
+ case SCLEX_SQL: style_comment = SCE_SQL_COMMENT; break;
+ case SCLEX_CAML: style_comment = SCE_CAML_COMMENT; break;
+ case SCLEX_D: style_comment = SCE_D_COMMENT; break;
+ default: style_comment = SCE_C_COMMENT;
+ }
+ if (sci_get_style_at(doc_list[idx].sci, line_start + x) == style_comment) continue;
+
+ real_comment_multiline(idx, line_start, last_line);
+
+ // break because we are already on the last line
+ break_loop = TRUE;
+ break;
+ }
+ }
+ }
+ SSM(doc_list[idx].sci, SCI_ENDUNDOACTION, 0, 0);
+
+ // restore selection if there is one
+ if (sel_start < sel_end)
+ {
+ if (single_line)
+ {
+ sci_set_selection_start(doc_list[idx].sci, sel_start + co_len);
+ sci_set_selection_end(doc_list[idx].sci, sel_end + ((i - first_line) * co_len));
+ }
+ else
+ {
+ gint eol_len = (sci_get_eol_mode(doc_list[idx].sci) == SC_EOL_CRLF) ? 2 : 1;
+ sci_set_selection_start(doc_list[idx].sci, sel_start + co_len + eol_len);
+ sci_set_selection_end(doc_list[idx].sci, sel_end + co_len + eol_len);
+ }
+ }
+}
+
+
+void editor_highlight_braces(ScintillaObject *sci, gint cur_pos)
+{
+ gint brace_pos = cur_pos - 1;
+ gint end_pos;
+
+ if (! utils_isbrace(sci_get_char_at(sci, brace_pos)))
+ {
+ brace_pos++;
+ if (! utils_isbrace(sci_get_char_at(sci, brace_pos)))
+ {
+ SSM(sci, SCI_BRACEBADLIGHT, -1, 0);
+ return;
+ }
+ }
+ end_pos = SSM(sci, SCI_BRACEMATCH, brace_pos, 0);
+
+ if (end_pos >= 0)
+ SSM(sci, SCI_BRACEHIGHLIGHT, brace_pos, end_pos);
+ else
+ SSM(sci, SCI_BRACEBADLIGHT, brace_pos, 0);
+}
+
+
+static gboolean is_doc_comment_char(gchar c, gint lexer)
+{
+ if (c == '*' && (lexer = SCLEX_HTML || lexer == SCLEX_CPP))
+ return TRUE;
+ else if ((c == '*' || c == '+') && lexer == SCLEX_D)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static void auto_multiline(ScintillaObject *sci, gint pos)
+{
+ gint style = SSM(sci, SCI_GETSTYLEAT, pos - 2, 0);
+ gint lexer = SSM(sci, SCI_GETLEXER, 0, 0);
+ gint i;
+
+ if ((lexer == SCLEX_CPP && (style == SCE_C_COMMENT || style == SCE_C_COMMENTDOC)) ||
+ (lexer == SCLEX_HTML && style == SCE_HPHP_COMMENT) ||
+ (lexer == SCLEX_D && (style == SCE_D_COMMENT ||
+ style == SCE_D_COMMENTDOC ||
+ style == SCE_D_COMMENTNESTED)))
+ {
+ gchar *previous_line = sci_get_line(sci, sci_get_line_from_position(sci, pos - 2));
+ gchar *continuation = "*"; // the type of comment, '*' (C/C++/Java), '+' and the others (D)
+ gchar *whitespace = ""; // to hold whitespace if needed
+ gchar *result;
+ gint len = strlen(previous_line);
+
+ // find and stop at end of multi line comment
+ i = len - 1;
+ while (i >= 0 && isspace(previous_line[i])) i--;
+ if (i >= 1 && is_doc_comment_char(previous_line[i - 1], lexer) && previous_line[i] == '/')
+ {
+ gint cur_line = sci_get_current_line(sci, -1);
+ gint indent_pos = sci_get_line_indent_position(sci, cur_line);
+ gint indent_len = sci_get_col_from_position(sci, indent_pos);
+
+ /* if there is one too many spaces, delete the last space,
+ * to return to the indent used before the multiline comment was started. */
+ if (indent_len % app->pref_editor_tab_width == 1)
+ SSM(sci, SCI_DELETEBACKNOTLINE, 0, 0); // remove whitespace indent
+ g_free(previous_line);
+ return;
+ }
+ // check whether we are on the second line of multi line comment
+ i = 0;
+ while (i < len && isspace(previous_line[i])) i++; // get to start of the line
+
+ if (i + 1 < len &&
+ previous_line[i] == '/' && is_doc_comment_char(previous_line[i + 1], lexer))
+ { // we are on the second line of a multi line comment, so we have to insert white space
+ whitespace = " ";
+ }
+
+ if (style == SCE_D_COMMENTNESTED) continuation = "+"; // for nested comments in D
+
+ result = g_strconcat(whitespace, continuation, " ", NULL);
+ sci_add_text(sci, result);
+ g_free(result);
+
+ g_free(previous_line);
+ }
+}
+
+
+/* Checks whether the given style is a comment or string for the given lexer.
+ * It doesn't handle LEX_HTML, this should be done by the caller.
+ * Returns true if the style is a comment, FALSE otherwise.
+ */
+static gboolean is_comment(gint lexer, gint style)
+{
+ gboolean result = FALSE;
+
+ switch (lexer)
+ {
+ case SCLEX_CPP:
+ case SCLEX_PASCAL:
+ {
+ if (style == SCE_C_COMMENT ||
+ style == SCE_C_COMMENTLINE ||
+ style == SCE_C_COMMENTDOC ||
+ style == SCE_C_COMMENTLINEDOC ||
+ style == SCE_C_CHARACTER ||
+ style == SCE_C_PREPROCESSOR ||
+ style == SCE_C_STRING)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_D:
+ {
+ if (style == SCE_D_COMMENT ||
+ style == SCE_D_COMMENTLINE ||
+ style == SCE_D_COMMENTDOC ||
+ style == SCE_D_COMMENTLINEDOC ||
+ style == SCE_D_COMMENTNESTED ||
+ style == SCE_D_CHARACTER ||
+ style == SCE_D_STRING)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_PYTHON:
+ {
+ if (style == SCE_P_COMMENTLINE ||
+ style == SCE_P_COMMENTBLOCK ||
+ style == SCE_P_STRING)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_F77:
+ {
+ if (style == SCE_F_COMMENT ||
+ style == SCE_F_STRING1 ||
+ style == SCE_F_STRING2)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_PERL:
+ {
+ if (style == SCE_PL_COMMENTLINE ||
+ style == SCE_PL_STRING)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_PROPERTIES:
+ {
+ if (style == SCE_PROPS_COMMENT)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_LATEX:
+ {
+ if (style == SCE_L_COMMENT)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_MAKEFILE:
+ {
+ if (style == SCE_MAKE_COMMENT)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_RUBY:
+ {
+ if (style == SCE_RB_COMMENTLINE ||
+ style == SCE_RB_STRING)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_BASH:
+ {
+ if (style == SCE_SH_COMMENTLINE ||
+ style == SCE_SH_STRING)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_SQL:
+ {
+ if (style == SCE_SQL_COMMENT ||
+ style == SCE_SQL_COMMENTLINE ||
+ style == SCE_SQL_COMMENTDOC ||
+ style == SCE_SQL_STRING)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_TCL:
+ {
+ if (style == SCE_TCL_COMMENT ||
+ style == SCE_TCL_COMMENTLINE ||
+ style == SCE_TCL_IN_QUOTE)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_LUA:
+ {
+ if (style == SCE_LUA_COMMENT ||
+ style == SCE_LUA_COMMENTLINE ||
+ style == SCE_LUA_COMMENTDOC ||
+ style == SCE_LUA_LITERALSTRING ||
+ style == SCE_LUA_CHARACTER ||
+ style == SCE_LUA_STRING)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_HASKELL:
+ {
+ if (style == SCE_HA_COMMENTLINE ||
+ style == SCE_HA_COMMENTBLOCK ||
+ style == SCE_HA_COMMENTBLOCK2 ||
+ style == SCE_HA_COMMENTBLOCK3 ||
+ style == SCE_HA_CHARACTER ||
+ style == SCE_HA_STRING)
+ result = TRUE;
+ break;
+ }
+ case SCLEX_HTML:
+ {
+ if (style == SCE_HPHP_SIMPLESTRING ||
+ style == SCE_HPHP_HSTRING ||
+ style == SCE_HPHP_COMMENTLINE ||
+ style == SCE_HPHP_COMMENT ||
+ style == SCE_H_DOUBLESTRING ||
+ style == SCE_H_SINGLESTRING ||
+ style == SCE_H_CDATA ||
+ style == SCE_H_COMMENT ||
+ style == SCE_H_SGML_DOUBLESTRING ||
+ style == SCE_H_SGML_SIMPLESTRING ||
+ style == SCE_H_SGML_COMMENT)
+ result = TRUE;
+ break;
+ }
+ }
+
+ return result;
+}
+
+
+#if 0
+gboolean editor_lexer_is_c_like(gint lexer)
+{
+ switch (lexer)
+ {
+ case SCLEX_CPP:
+ case SCLEX_D:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+#endif
+
+
+// Returns: -1 if lexer doesn't support type keywords
+gint editor_lexer_get_type_keyword_idx(gint lexer)
+{
+ switch (lexer)
+ {
+ case SCLEX_CPP:
+ case SCLEX_D:
+ return 3;
+
+ default:
+ return -1;
+ }
+}
+
+
+// inserts a three-line comment at one line above current cursor position
+void editor_insert_multiline_comment(gint idx)
+{
+ gchar *text;
+ gint text_len;
+ gint line;
+ gint pos;
+ gboolean have_multiline_comment = FALSE;
+
+ if (doc_list[idx].file_type->comment_close != NULL &&
+ strlen(doc_list[idx].file_type->comment_close) > 0)
+ have_multiline_comment = TRUE;
+
+ // insert three lines one line above of the current position
+ line = sci_get_line_from_position(doc_list[idx].sci, editor_info.click_pos);
+ pos = sci_get_position_from_line(doc_list[idx].sci, line);
+
+ // use the indentation on the current line but only when comment indention is used
+ // and we don't have multi line comment characters
+ if (doc_list[idx].use_auto_indention && ! have_multiline_comment &&
+ doc_list[idx].file_type->comment_use_indent)
+ {
+ get_indent(doc_list[idx].sci, editor_info.click_pos, TRUE);
+ text = g_strdup_printf("%s\n%s\n%s\n", indent, indent, indent);
+ text_len = strlen(text);
+ }
+ else
+ {
+ text = g_strdup("\n\n\n");
+ text_len = 3;
+ }
+ sci_insert_text(doc_list[idx].sci, pos, text);
+ g_free(text);
+
+
+ // select the inserted lines for commenting
+ sci_set_selection_start(doc_list[idx].sci, pos);
+ sci_set_selection_end(doc_list[idx].sci, pos + text_len);
+
+ editor_do_comment(idx, -1, TRUE);
+
+ // set the current position to the start of the first inserted line
+ pos += strlen(doc_list[idx].file_type->comment_open);
+
+ // on multi line comment jump to the next line, otherwise add the length of added indentation
+ if (have_multiline_comment)
+ pos += 1;
+ else
+ pos += strlen(indent);
+
+ sci_set_current_position(doc_list[idx].sci, pos, TRUE);
+ // reset the selection
+ sci_set_anchor(doc_list[idx].sci, pos);
+}
+
+
+/* Scroll the view to make line appear at percent_of_view.
+ * line can be -1 to use the current position. */
+static void scroll_to_line(ScintillaObject *sci, gint line, gfloat percent_of_view)
+{
+ gint vis1, los, delta;
+ GtkWidget *wid = GTK_WIDGET(sci);
+
+ if (! wid->window || ! gdk_window_is_viewable(wid->window))
+ return; // prevent gdk_window_scroll warning
+
+ if (line == -1)
+ line = sci_get_current_line(sci, -1);
+
+ // sci 'visible line' != doc line number because of folding and line wrapping
+ /* calling SCI_VISIBLEFROMDOCLINE for line is more accurate than calling
+ * SCI_DOCLINEFROMVISIBLE for vis1. */
+ line = SSM(sci, SCI_VISIBLEFROMDOCLINE, line, 0);
+ vis1 = SSM(sci, SCI_GETFIRSTVISIBLELINE, 0, 0);
+ los = SSM(sci, SCI_LINESONSCREEN, 0, 0);
+ delta = (line - vis1) - los * percent_of_view;
+ sci_scroll_lines(sci, delta);
+ //sci_scroll_caret(sci); // ensure visible (maybe not needed now)
+}
+
+
+void editor_insert_alternative_whitespace(ScintillaObject *sci)
+{
+ // creates and inserts one tabulator sign or whitespace of the amount of the tab width
+ gchar *text = utils_get_whitespace(app->pref_editor_tab_width, TRUE);
+ sci_add_text(sci, text);
+ g_free(text);
+}
+
+
+void editor_select_word(ScintillaObject *sci)
+{
+ gint pos;
+ gint start;
+ gint end;
+
+ g_return_if_fail(sci != NULL);
+
+ pos = SSM(sci, SCI_GETCURRENTPOS, 0, 0);
+ start = SSM(sci, SCI_WORDSTARTPOSITION, pos, TRUE);
+ end = SSM(sci, SCI_WORDENDPOSITION, pos, TRUE);
+
+ if (start == end) // caret in whitespaces sequence
+ {
+ // look forward but reverse the selection direction,
+ // so the caret end up stay as near as the original position.
+ end = SSM(sci, SCI_WORDENDPOSITION, pos, FALSE);
+ start = SSM(sci, SCI_WORDENDPOSITION, end, TRUE);
+ if (start == end)
+ return;
+ }
+
+ SSM(sci, SCI_SETSEL, start, end);
+}
+
+
Copied: trunk/src/editor.h (from rev 1583, trunk/src/sci_cb.h)
===================================================================
--- trunk/src/editor.h (rev 0)
+++ trunk/src/editor.h 2007-05-28 16:07:30 UTC (rev 1584)
@@ -0,0 +1,93 @@
+/*
+ * editor.h - this file is part of Geany, a fast and lightweight IDE
+ *
+ * Copyright 2005-2007 Enrico Tröger <enrico.troeger at uvena.de>
+ * Copyright 2006-2007 Nick Treleaven <nick.treleaven at btinternet.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef GEANY_SCI_CB_H
+#define GEANY_SCI_CB_H 1
+
+#ifndef PLAT_GTK
+# define PLAT_GTK 1 // needed for ScintillaWidget.h
+#endif
+
+#include "Scintilla.h"
+#include "ScintillaWidget.h"
+
+// Note: Avoid using SSM in files not related to scintilla, use sciwrappers.h instead.
+#define SSM(s, m, w, l) scintilla_send_message(s, m, w, l)
+
+
+typedef struct
+{
+ gchar *current_word; // holds word under the mouse or keyboard cursor
+ gint click_pos; // text position where the mouse was clicked
+} EditorInfo;
+
+extern EditorInfo editor_info;
+
+
+gboolean
+on_editor_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data);
+
+// callback func called by all editors when a signal arises
+void on_editor_notification(GtkWidget* editor, gint scn, gpointer lscn, gpointer user_data);
+
+gboolean editor_start_auto_complete(gint idx, gint pos, gboolean force);
+
+void editor_close_block(gint idx, gint pos);
+
+gboolean editor_auto_forif(gint idx, gint pos);
+
+void editor_auto_latex(gint idx, gint pos);
+
+void editor_show_macro_list(ScintillaObject *sci);
+
+/* Reads the word at given cursor position and writes it into the given buffer. The buffer will be
+ * NULL terminated in any case, even when the word is truncated because wordlen is too small.
+ * position can be -1, then the current position is used.
+ * wc are the wordchars to use, if NULL, GEANY_WORDCHARS will be used */
+void editor_find_current_word(ScintillaObject *sci, gint pos, gchar *word, size_t wordlen,
+ const gchar *wc);
+
+gboolean editor_show_calltip(gint idx, gint pos);
+
+void editor_do_comment_toggle(gint idx);
+
+void editor_do_comment(gint idx, gint line, gboolean allow_empty_lines);
+
+void editor_do_uncomment(gint idx, gint line);
+
+void editor_highlight_braces(ScintillaObject *sci, gint cur_pos);
+
+void editor_auto_table(ScintillaObject *sci, gint pos);
+
+gboolean editor_lexer_is_c_like(gint lexer);
+
+gint editor_lexer_get_type_keyword_idx(gint lexer);
+
+void editor_insert_multiline_comment(gint idx);
+
+void editor_select_word(ScintillaObject *sci);
+
+void editor_insert_alternative_whitespace(ScintillaObject *sci);
+
+#endif
Modified: trunk/src/highlighting.c
===================================================================
--- trunk/src/highlighting.c 2007-05-28 15:28:30 UTC (rev 1583)
+++ trunk/src/highlighting.c 2007-05-28 16:07:30 UTC (rev 1584)
@@ -30,7 +30,7 @@
#include "SciLexer.h"
#include "geany.h"
#include "highlighting.h"
-#include "sci_cb.h"
+#include "editor.h"
#include "utils.h"
#include "filetypes.h"
#include "symbols.h"
Modified: trunk/src/keybindings.c
===================================================================
--- trunk/src/keybindings.c 2007-05-28 15:28:30 UTC (rev 1583)
+++ trunk/src/keybindings.c 2007-05-28 16:07:30 UTC (rev 1584)
@@ -36,7 +36,7 @@
#include "callbacks.h"
#include "prefs.h"
#include "msgwindow.h"
-#include "sci_cb.h"
+#include "editor.h"
#include "sciwrappers.h"
#include "build.h"
#include "tools.h"
@@ -617,7 +617,7 @@
gint pos = sci_get_current_position(sci);
if (app->pref_editor_auto_complete_constructs)
- return sci_cb_auto_forif(idx, pos);
+ return editor_auto_forif(idx, pos);
}
}
return FALSE;
@@ -901,7 +901,7 @@
pos = sci_get_current_position(doc_list[idx].sci);
- sci_cb_find_current_word(doc_list[idx].sci, pos,
+ editor_find_current_word(doc_list[idx].sci, pos,
editor_info.current_word, GEANY_MAX_WORD_LENGTH, NULL);
if (*editor_info.current_word == 0)
@@ -1082,13 +1082,13 @@
on_menu_uncomment_line1_activate(NULL, NULL);
break;
case GEANY_KEYS_EDIT_AUTOCOMPLETE:
- sci_cb_start_auto_complete(idx, sci_get_current_position(doc_list[idx].sci), TRUE);
+ editor_start_auto_complete(idx, sci_get_current_position(doc_list[idx].sci), TRUE);
break;
case GEANY_KEYS_EDIT_CALLTIP:
- sci_cb_show_calltip(idx, -1);
+ editor_show_calltip(idx, -1);
break;
case GEANY_KEYS_EDIT_MACROLIST:
- sci_cb_show_macro_list(doc_list[idx].sci);
+ editor_show_macro_list(doc_list[idx].sci);
break;
case GEANY_KEYS_EDIT_SUPPRESSCOMPLETION:
@@ -1106,10 +1106,10 @@
break;
case GEANY_KEYS_EDIT_SELECTWORD:
- sci_cb_select_word(doc_list[idx].sci);
+ editor_select_word(doc_list[idx].sci);
break;
case GEANY_KEYS_EDIT_INSERTALTWHITESPACE:
- sci_cb_insert_alternative_whitespace(doc_list[idx].sci);
+ editor_insert_alternative_whitespace(doc_list[idx].sci);
break;
case GEANY_KEYS_EDIT_INCREASEINDENT:
on_menu_increase_indent1_activate(NULL, NULL);
Modified: trunk/src/main.c
===================================================================
--- trunk/src/main.c 2007-05-28 15:28:30 UTC (rev 1583)
+++ trunk/src/main.c 2007-05-28 16:07:30 UTC (rev 1584)
@@ -52,7 +52,7 @@
#include "treeviews.h"
#include "notebook.h"
#include "keybindings.h"
-#include "sci_cb.h"
+#include "editor.h"
#include "search.h"
#include "build.h"
#include "highlighting.h"
Modified: trunk/src/makefile.win32
===================================================================
--- trunk/src/makefile.win32 2007-05-28 15:28:30 UTC (rev 1583)
+++ trunk/src/makefile.win32 2007-05-28 16:07:30 UTC (rev 1584)
@@ -46,7 +46,7 @@
OBJS = treeviews.o templates.o encodings.o about.o prefs.o win32.o build.o msgwindow.o dialogs.o \
filetypes.o interface.o main.o support.o callbacks.o utils.o ui_utils.o socket.o \
- highlighting.o sci_cb.o document.o sciwrappers.o keyfile.o keybindings.o search.o notebook.o \
+ highlighting.o editor.o document.o sciwrappers.o keyfile.o keybindings.o search.o notebook.o \
symbols.o tools.o project.o
.c.o:
Deleted: trunk/src/sci_cb.c
===================================================================
--- trunk/src/sci_cb.c 2007-05-28 15:28:30 UTC (rev 1583)
+++ trunk/src/sci_cb.c 2007-05-28 16:07:30 UTC (rev 1584)
@@ -1,2296 +0,0 @@
-/*
- * sci_cb.c - this file is part of Geany, a fast and lightweight IDE
- *
- * Copyright 2005-2007 Enrico Tröger <enrico.troeger at uvena.de>
- * Copyright 2006-2007 Nick Treleaven <nick.treleaven at btinternet.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id$
- */
-
-/*
- * Callbacks for the Scintilla widget (ScintillaObject).
- * Most important is the sci-notify callback, handled in on_editor_notification().
- * This includes auto-indentation, comments, auto-completion, calltips, etc.
- * Also some general Scintilla-related functions.
- */
-
-#include <ctype.h>
-#include <string.h>
-
-#include "SciLexer.h"
-#include "geany.h"
-
-#include "sci_cb.h"
-#include "document.h"
-#include "sciwrappers.h"
-#include "ui_utils.h"
-#include "utils.h"
-#include "symbols.h"
-
-
-static gchar current_word[GEANY_MAX_WORD_LENGTH]; // holds word under the mouse or keyboard cursor
-
-EditorInfo editor_info = {current_word, -1};
-
-static struct
-{
- gchar *text;
- gboolean set;
- gchar *last_word;
- guint tag_index;
-} calltip = {NULL, FALSE, NULL, 0};
-
-static gchar indent[100];
-
-
-static void on_new_line_added(ScintillaObject *sci, gint idx);
-static gboolean handle_xml(ScintillaObject *sci, gchar ch, gint idx);
-static void get_indent(ScintillaObject *sci, gint pos, gboolean use_this_line);
-static void auto_multiline(ScintillaObject *sci, gint pos);
-static gboolean is_comment(gint lexer, gint style);
-static void scroll_to_line(ScintillaObject *sci, gint line, gfloat percent_of_view);
-static void auto_close_bracket(ScintillaObject *sci, gint pos, gchar c);
-
-
-// calls the edit popup menu in the editor
-gboolean
-on_editor_button_press_event (GtkWidget *widget,
- GdkEventButton *event,
- gpointer user_data)
-{
- gint idx = GPOINTER_TO_INT(user_data);
- editor_info.click_pos = sci_get_position_from_xy(doc_list[idx].sci, event->x, event->y, FALSE);
-
- if (event->button == 1)
- {
- if (GDK_BUTTON_PRESS==event->type && app->pref_editor_disable_dnd)
- {
- gint ss = sci_get_selection_start(doc_list[idx].sci);
- sci_set_selection_end(doc_list[idx].sci, ss);
- }
- return utils_check_disk_status(idx, FALSE);
- }
-
- if (event->button == 3)
- {
- sci_cb_find_current_word(doc_list[idx].sci, editor_info.click_pos,
- current_word, sizeof current_word, NULL);
-
- ui_update_popup_goto_items((current_word[0] != '\0') ? TRUE : FALSE);
- ui_update_popup_copy_items(idx);
- ui_update_insert_include_item(idx, 0);
- gtk_menu_popup(GTK_MENU(app->popup_menu), NULL, NULL, NULL, NULL, event->button, event->time);
-
- return TRUE;
- }
- return FALSE;
-}
-
-
-typedef struct SCNotification SCNotification;
-
-static void on_margin_click(ScintillaObject *sci, SCNotification *nt)
-{
- // left click to marker margin marks the line
- if (nt->margin == 1)
- {
- gint line = sci_get_line_from_position(sci, nt->position);
- gboolean set = sci_is_marker_set_at_line(sci, line, 1);
-
- //sci_marker_delete_all(doc_list[idx].sci, 1);
- sci_set_marker_at_line(sci, line, ! set, 1); // toggle the marker
- }
- // left click on the folding margin to toggle folding state of current line
- else if (nt->margin == 2 && app->pref_editor_folding)
- {
- gint line = SSM(sci, SCI_LINEFROMPOSITION, nt->position, 0);
-
- SSM(sci, SCI_TOGGLEFOLD, line, 0);
- if (app->pref_editor_unfold_all_children &&
- SSM(sci, SCI_GETLINEVISIBLE, line + 1, 0))
- { // unfold all children of the current fold point
- gint last_line = SSM(sci, SCI_GETLASTCHILD, line, -1);
- gint i;
-
- for (i = line; i < last_line; i++)
- {
- if (! SSM(sci, SCI_GETLINEVISIBLE, i, 0))
- {
- SSM(sci, SCI_TOGGLEFOLD, SSM(sci, SCI_GETFOLDPARENT, i, 0), 0);
- }
- }
- }
- }
-}
-
-
-static void on_update_ui(gint idx, G_GNUC_UNUSED SCNotification *nt)
-{
- ScintillaObject *sci = doc_list[idx].sci;
- gint pos = sci_get_current_position(sci);
-
- // undo / redo menu update
- ui_update_popup_reundo_items(idx);
-
- // brace highlighting
- sci_cb_highlight_braces(sci, pos);
-
- ui_update_statusbar(idx, pos);
-
- /* Visible lines are only laid out accurately once [SCN_UPDATEUI] is sent,
- * so we need to only call sci_scroll_to_line here, because the document
- * may have line wrapping and folding enabled.
- * http://scintilla.sourceforge.net/ScintillaDoc.html#LineWrapping */
- if (doc_list[idx].scroll_percent > 0.0F)
- {
- scroll_to_line(sci, -1, doc_list[idx].scroll_percent);
- doc_list[idx].scroll_percent = -1.0F; // disable further scrolling
- }
-#if 0
- /// experimental code for inverting selections
- {
- gint i;
- for (i = SSM(sci, SCI_GETSELECTIONSTART, 0, 0); i < SSM(sci, SCI_GETSELECTIONEND, 0, 0); i++)
- {
- // need to get colour from getstyleat(), but how?
- SSM(sci, SCI_STYLESETFORE, STYLE_DEFAULT, 0);
- SSM(sci, SCI_STYLESETBACK, STYLE_DEFAULT, 0);
- }
-
- sci_get_style_at(sci, pos);
- }
-#endif
-}
-
-
-static void on_char_added(gint idx, SCNotification *nt)
-{
- ScintillaObject *sci = doc_list[idx].sci;
- gint pos = sci_get_current_position(sci);
-
- switch (nt->ch)
- {
- case '\r':
- { // simple indentation (only for CR format)
- if (sci_get_eol_mode(sci) == SC_EOL_CR)
- on_new_line_added(sci, idx);
- break;
- }
- case '\n':
- { // simple indentation (for CR/LF and LF format)
- on_new_line_added(sci, idx);
- break;
- }
- case '>':
- case '/':
- { // close xml-tags
- handle_xml(sci, nt->ch, idx);
- break;
- }
- case '(':
- { // show calltips
- sci_cb_show_calltip(idx, --pos);
- break;
- }
- case ')':
- { // hide calltips
- if (SSM(sci, SCI_CALLTIPACTIVE, 0, 0))
- {
- SSM(sci, SCI_CALLTIPCANCEL, 0, 0);
- }
- g_free(calltip.text);
- calltip.text = NULL;
- calltip.set = FALSE;
- break;
- }
- case '[':
- case '{':
- { // Tex auto-closing
- if (sci_get_lexer(sci) == SCLEX_LATEX)
- {
- auto_close_bracket(sci, pos, nt->ch); // Tex auto-closing
- sci_cb_show_calltip(idx, --pos);
- }
- break;
- }
- case '}':
- { // closing bracket handling
- if (doc_list[idx].use_auto_indention &&
- app->pref_editor_indention_mode == INDENT_ADVANCED)
- sci_cb_close_block(idx, pos - 1);
- break;
- }
- default: sci_cb_start_auto_complete(idx, pos, FALSE);
- }
-}
-
-
-// callback func called by all editors when a signal arises
-void on_editor_notification(GtkWidget *editor, gint scn, gpointer lscn, gpointer user_data)
-{
- SCNotification *nt;
- ScintillaObject *sci;
- gint idx;
-
- idx = GPOINTER_TO_INT(user_data);
- sci = doc_list[idx].sci;
-
- nt = lscn;
- switch (nt->nmhdr.code)
- {
- case SCN_SAVEPOINTLEFT:
- {
- doc_list[idx].changed = TRUE;
- document_set_text_changed(idx);
- break;
- }
- case SCN_SAVEPOINTREACHED:
- {
- doc_list[idx].changed = FALSE;
- document_set_text_changed(idx);
- break;
- }
- case SCN_MODIFYATTEMPTRO:
- {
- utils_beep();
- break;
- }
- case SCN_MARGINCLICK:
- on_margin_click(sci, nt);
- break;
-
- case SCN_UPDATEUI:
- on_update_ui(idx, nt);
- break;
-
- case SCN_MODIFIED:
- {
- if (nt->modificationType & SC_STARTACTION && ! app->ignore_callback)
- {
- // get notified about undo changes
- document_undo_add(idx, UNDO_SCINTILLA, NULL);
- }
- break;
- }
- case SCN_CHARADDED:
- on_char_added(idx, nt);
- break;
-
- case SCN_USERLISTSELECTION:
- {
- if (nt->listType == 1)
- {
- gint pos = SSM(sci, SCI_GETCURRENTPOS, 0, 0);
- SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) nt->text);
- }
- else if (nt->listType == 2)
- {
- gint start, pos = SSM(sci, SCI_GETCURRENTPOS, 0, 0);
- start = pos;
- while (start > 0 && sci_get_char_at(sci, --start) != '&') ;
-
- SSM(sci, SCI_INSERTTEXT, pos - 1, (sptr_t) nt->text);
- }
- break;
- }
- case SCN_AUTOCSELECTION:
- {
- // now that autocomplete is finishing, reshow calltips if they were showing
- if (calltip.set)
- {
- gint pos = sci_get_current_position(sci);
- SSM(sci, SCI_CALLTIPSHOW, pos, (sptr_t) calltip.text);
- // now autocompletion has been cancelled, so do it manually
- sci_set_selection_start(sci, nt->lParam);
- sci_set_selection_end(sci, pos);
- sci_replace_sel(sci, ""); // clear root of word
- SSM(sci, SCI_INSERTTEXT, nt->lParam, (sptr_t) nt->text);
- sci_goto_pos(sci, nt->lParam + strlen(nt->text), FALSE);
- }
- break;
- }
-#ifdef GEANY_DEBUG
- case SCN_STYLENEEDED:
- {
- geany_debug("style");
- break;
- }
-#endif
- case SCN_URIDROPPED:
- {
- if (nt->text != NULL)
- {
- document_open_file_list(nt->text, -1);
- }
- break;
- }
- case SCN_CALLTIPCLICK:
- {
- if (nt->position > 0)
- {
- switch (nt->position)
- {
- case 1: // up arrow
- if (calltip.tag_index > 0)
- calltip.tag_index--;
- break;
-
- case 2: calltip.tag_index++; break; // down arrow
- }
- sci_cb_show_calltip(idx, -1);
- }
- break;
- }
- }
-}
-
-
-static void on_new_line_added(ScintillaObject *sci, gint idx)
-{
- gint pos = sci_get_current_position(sci);
-
- // simple indentation
- if (doc_list[idx].use_auto_indention)
- {
- get_indent(sci, pos, FALSE);
- sci_add_text(sci, indent);
-
- if (app->pref_editor_indention_mode == INDENT_ADVANCED)
- {
- // add extra indentation for Python after colon
- if (FILETYPE_ID(doc_list[idx].file_type) == GEANY_FILETYPES_PYTHON &&
- sci_get_char_at(sci, pos - 2) == ':' &&
- sci_get_style_at(sci, pos - 2) == SCE_P_OPERATOR)
- {
- // creates and inserts one tabulator sign or whitespace of the amount of the tab width
- gchar *text = utils_get_whitespace(app->pref_editor_tab_width, FALSE);
- sci_add_text(sci, text);
- g_free(text);
- }
- }
- }
-
- if (app->pref_editor_auto_complete_constructs)
- {
- // " * " auto completion in multiline C/C++/D/Java comments
- auto_multiline(sci, pos);
-
- sci_cb_auto_latex(idx, pos);
- }
-}
-
-
-static gboolean lexer_has_braces(ScintillaObject *sci)
-{
- gint lexer = SSM(sci, SCI_GETLEXER, 0, 0);
-
- switch (lexer)
- {
- case SCLEX_CPP:
- case SCLEX_D:
- case SCLEX_HTML: // for PHP & JS
- case SCLEX_PASCAL: // for multiline comments?
- case SCLEX_BASH:
- case SCLEX_PERL:
- case SCLEX_TCL:
- return TRUE;
- default:
- return FALSE;
- }
-}
-
-
-// in place indentation of one tab or equivalent spaces
-static void do_indent(gchar *buf, gsize len, guint *idx)
-{
- guint j = *idx;
-
- if (app->pref_editor_use_tabs)
- {
- if (j < len - 1) // leave room for a \0 terminator.
- buf[j++] = '\t';
- }
- else
- { // insert as many spaces as a tab would take
- guint k;
- for (k = 0; k < (guint) app->pref_editor_tab_width && k < len - 1; k++)
- buf[j++] = ' ';
- }
- *idx = j;
-}
-
-
-/* "use_this_line" to auto-indent only if it is a real new line
- * and ignore the case of sci_cb_close_block */
-static void get_indent(ScintillaObject *sci, gint pos, gboolean use_this_line)
-{
- guint i, len, j = 0;
- gint prev_line;
- gchar *linebuf;
-
- prev_line = sci_get_line_from_position(sci, pos);
-
- if (! use_this_line) prev_line--;
- len = sci_get_line_length(sci, prev_line);
- linebuf = sci_get_line(sci, prev_line);
-
- for (i = 0; i < len && j <= (sizeof(indent) - 1); i++)
- {
- if (linebuf[i] == ' ' || linebuf[i] == '\t') // simple indentation
- indent[j++] = linebuf[i];
- else if (app->pref_editor_indention_mode != INDENT_ADVANCED)
- break;
- else if (use_this_line)
- break;
- else // sci_cb_close_block
- {
- if (! lexer_has_braces(sci))
- break;
-
- if (linebuf[i] == '{')
- {
- do_indent(indent, sizeof(indent), &j);
- break;
- }
- else
- {
- gint k = len - 1;
-
- while (k > 0 && isspace(linebuf[k])) k--;
-
- // if last non-whitespace character is a { increase indentation by a tab
- // e.g. for (...) {
- if (linebuf[k] == '{')
- {
- do_indent(indent, sizeof(indent), &j);
- }
- break;
- }
- }
- }
- indent[j] = '\0';
- g_free(linebuf);
-}
-
-
-static void auto_close_bracket(ScintillaObject *sci, gint pos, gchar c)
-{
- if (! app->pref_editor_auto_complete_constructs || SSM(sci, SCI_GETLEXER, 0, 0) != SCLEX_LATEX)
- return;
-
- if (c == '[')
- {
- sci_add_text(sci, "]");
- }
- else if (c == '{')
- {
- sci_add_text(sci, "}");
- }
- sci_set_current_position(sci, pos, TRUE);
-}
-
-
-/* Finds a corresponding matching brace to the given pos
- * (this is taken from Scintilla Editor.cxx,
- * fit to work with sci_cb_close_block) */
-static gint brace_match(ScintillaObject *sci, gint pos)
-{
- gchar chBrace = sci_get_char_at(sci, pos);
- gchar chSeek = utils_brace_opposite(chBrace);
- gchar chAtPos;
- gint direction = -1;
- gint styBrace;
- gint depth = 1;
- gint styAtPos;
-
- styBrace = sci_get_style_at(sci, pos);
-
- if (utils_is_opening_brace(chBrace))
- direction = 1;
-
- pos = pos + direction;
- while ((pos >= 0) && (pos < sci_get_length(sci)))
- {
- chAtPos = sci_get_char_at(sci, pos - 1);
- styAtPos = sci_get_style_at(sci, pos);
-
- if ((pos > sci_get_end_styled(sci)) || (styAtPos == styBrace))
- {
- if (chAtPos == chBrace)
- depth++;
- if (chAtPos == chSeek)
- depth--;
- if (depth == 0)
- return pos;
- }
- pos = pos + direction;
- }
- return -1;
-}
-
-
-/* Called after typing '}', if pref_editor_indention_mode is INDENT_ADVANCED. */
-void sci_cb_close_block(gint idx, gint pos)
-{
- gint x = 0, cnt = 0;
- gint start_brace, line, line_len, eol_char_len;
- gchar *text, *line_buf;
- ScintillaObject *sci;
-
- if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return;
-
- sci = doc_list[idx].sci;
-
- if (! lexer_has_braces(sci))
- return;
-
- line = sci_get_line_from_position(sci, pos);
- line_len = sci_get_line_length(sci, line);
- // set eol_char_len to 0 if on last line, because there is no EOL char
- eol_char_len = (line == (SSM(sci, SCI_GETLINECOUNT, 0, 0) - 1)) ? 0 :
- utils_get_eol_char_len(document_find_by_sci(sci));
-
- // check that the line is empty, to not kill text in the line
- line_buf = sci_get_line(sci, line);
- line_buf[line_len - eol_char_len] = '\0';
- while (x < (line_len - eol_char_len))
- {
- if (isspace(line_buf[x])) cnt++;
- x++;
- }
- g_free(line_buf);
-
- if ((line_len - eol_char_len - 1) != cnt) return;
-
- start_brace = brace_match(sci, pos); // same as sci_find_bracematch (Document::BraceMatch)?
-
- if (start_brace >= 0)
- {
- gint line_start;
-
- get_indent(sci, start_brace, TRUE);
- text = g_strconcat(indent, "}", NULL);
- line_start = sci_get_position_from_line(sci, line);
- sci_set_anchor(sci, line_start);
- SSM(sci, SCI_REPLACESEL, 0, (sptr_t) text);
- g_free(text);
- }
- else
- if (sci_get_lexer(sci) == SCLEX_HTML || sci_get_lexer(sci) == SCLEX_TCL)
- { /* For TCL & PHP brace_match doesn't work here (maybe lexer bugs?),
- * so this is a simple workaround. */
- gint line_indent = sci_get_line_indentation(sci, line);
- gint last_indent = sci_get_line_indentation(sci, line - 1);
-
- if (line_indent < last_indent)
- return;
- line_indent -= app->pref_editor_tab_width;
- line_indent = MAX(0, line_indent);
- sci_set_line_indentation(sci, line, line_indent);
- }
-}
-
-
-/* Reads the word at given cursor position and writes it into the given buffer. The buffer will be
- * NULL terminated in any case, even when the word is truncated because wordlen is too small.
- * position can be -1, then the current position is used.
- * wc are the wordchars to use, if NULL, GEANY_WORDCHARS will be used */
-void sci_cb_find_current_word(ScintillaObject *sci, gint pos, gchar *word, size_t wordlen,
- const gchar *wc)
-{
- gint line, line_start, startword, endword;
- gchar *chunk;
-
- if (pos == -1)
- pos = sci_get_current_position(sci);
-
- line = sci_get_line_from_position(sci, pos);
- line_start = sci_get_position_from_line(sci, line);
- startword = pos - line_start;
- endword = pos - line_start;
-
- word[0] = '\0';
- chunk = sci_get_line(sci, line);
-
- if (wc == NULL)
- wc = GEANY_WORDCHARS;
-
- while (startword > 0 && strchr(wc, chunk[startword - 1]))
- startword--;
- while (chunk[endword] && strchr(wc, chunk[endword]))
- endword++;
- if(startword == endword)
- return;
-
- chunk[endword] = '\0';
-
- g_strlcpy(word, chunk + startword, wordlen); //ensure null terminated
- g_free(chunk);
-}
-
-
-static gint find_previous_brace(ScintillaObject *sci, gint pos)
-{
- gchar c;
- gint orig_pos = pos;
-
- c = SSM(sci, SCI_GETCHARAT, pos, 0);
- while (pos >= 0 && pos > orig_pos - 300)
- {
- c = SSM(sci, SCI_GETCHARAT, pos, 0);
- pos--;
- if (utils_is_opening_brace(c)) return pos;
- }
- return -1;
-}
-
-
-static gint find_start_bracket(ScintillaObject *sci, gint pos)
-{
- gchar c;
- gint brackets = 0;
- gint orig_pos = pos;
-
- c = SSM(sci, SCI_GETCHARAT, pos, 0);
- while (pos > 0 && pos > orig_pos - 300)
- {
- c = SSM(sci, SCI_GETCHARAT, pos, 0);
- if (c == ')') brackets++;
- else if (c == '(') brackets--;
- pos--;
- if (brackets < 0) return pos; // found start bracket
- }
- return -1;
-}
-
-
-static gboolean append_calltip(GString *str, const TMTag *tag, filetype_id ft_id)
-{
- if (! tag->atts.entry.arglist) return FALSE;
-
- if (tag->atts.entry.var_type)
- {
- guint i;
-
- g_string_append(str, tag->atts.entry.var_type);
- for (i = 0; i < tag->atts.entry.pointerOrder; i++)
- {
- g_string_append_c(str, '*');
- }
- g_string_append_c(str, ' ');
- }
- if (tag->atts.entry.scope)
- {
- const gchar *cosep = symbols_get_context_separator(ft_id);
-
- g_string_append(str, tag->atts.entry.scope);
- g_string_append(str, cosep);
- }
- g_string_append(str, tag->name);
- g_string_append_c(str, ' ');
- g_string_append(str, tag->atts.entry.arglist);
-
- return TRUE;
-}
-
-
-static gchar *find_calltip(const gchar *word, filetype *ft)
-{
- const GPtrArray *tags;
- const gint arg_types = tm_tag_function_t | tm_tag_prototype_t |
- tm_tag_method_t | tm_tag_macro_with_arg_t;
- TMTagAttrType *attrs = NULL;
- TMTag *tag;
- GString *str = NULL;
- guint i;
-
- g_return_val_if_fail(ft && word && *word, NULL);
-
- tags = tm_workspace_find(word, arg_types | tm_tag_class_t, attrs, FALSE, ft->lang);
- if (tags->len == 0)
- return NULL;
-
- tag = TM_TAG(tags->pdata[0]);
-
- if (tag->type == tm_tag_class_t && FILETYPE_ID(ft) == GEANY_FILETYPES_D)
- {
- // user typed e.g. 'new Classname(' so lookup D constructor Classname::this()
- tags = tm_workspace_find_scoped("this", tag->name,
- arg_types, attrs, FALSE, ft->lang, TRUE);
- if (tags->len == 0)
- return NULL;
- }
-
- // remove tags with no argument list
- for (i = 0; i < tags->len; i++)
- {
- tag = TM_TAG(tags->pdata[i]);
-
- if (! tag->atts.entry.arglist)
- tags->pdata[i] = NULL;
- }
- tm_tags_prune((GPtrArray *) tags);
- if (tags->len == 0)
- return NULL;
- else
- { // remove duplicate calltips
- TMTagAttrType sort_attr[] = {tm_tag_attr_name_t, tm_tag_attr_scope_t,
- tm_tag_attr_arglist_t, 0};
-
- tm_tags_sort((GPtrArray *) tags, sort_attr, TRUE);
- }
-
- // if the current word has changed since last time, start with the first tag match
- if (! utils_str_equal(word, calltip.last_word))
- calltip.tag_index = 0;
- // cache the current word for next time
- g_free(calltip.last_word);
- calltip.last_word = g_strdup(word);
- calltip.tag_index = MIN(calltip.tag_index, tags->len - 1); // ensure tag_index is in range
-
- for (i = calltip.tag_index; i < tags->len; i++)
- {
- tag = TM_TAG(tags->pdata[i]);
-
- if (str == NULL)
- {
- str = g_string_new(NULL);
- if (calltip.tag_index > 0)
- g_string_prepend(str, "\001 "); // up arrow
- append_calltip(str, tag, FILETYPE_ID(ft));
- }
- else // add a down arrow
- {
- if (calltip.tag_index > 0) // already have an up arrow
- g_string_insert_c(str, 1, '\002');
- else
- g_string_prepend(str, "\002 ");
- break;
- }
- }
- if (str)
- {
- gchar *result = str->str;
-
- g_string_free(str, FALSE);
- return result;
- }
- return NULL;
-}
-
-
-// use pos = -1 to search for the previous unmatched open bracket.
-gboolean sci_cb_show_calltip(gint idx, gint pos)
-{
- gint orig_pos = pos; // the position for the calltip
- gint lexer;
- gint style;
- gchar word[GEANY_MAX_WORD_LENGTH];
- gchar *str;
- ScintillaObject *sci;
-
- if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return FALSE;
- sci = doc_list[idx].sci;
-
- lexer = SSM(sci, SCI_GETLEXER, 0, 0);
-
- if (pos == -1)
- {
- // position of '(' is unknown, so go backwards from current position to find it
- pos = SSM(sci, SCI_GETCURRENTPOS, 0, 0);
- pos--;
- orig_pos = pos;
- pos = (lexer == SCLEX_LATEX) ? find_previous_brace(sci, pos) :
- find_start_bracket(sci, pos);
- if (pos == -1) return FALSE;
- }
-
- style = SSM(sci, SCI_GETSTYLEAT, pos, 0);
- if (is_comment(lexer, style))
- return FALSE;
- // never show a calltip in a PHP file outside of the <? ?> tags
- if (lexer == SCLEX_HTML && ! (style >= SCE_HPHP_DEFAULT && style <= SCE_HPHP_OPERATOR))
- return FALSE;
-
- word[0] = '\0';
- sci_cb_find_current_word(sci, pos - 1, word, sizeof word, NULL);
- if (word[0] == '\0') return FALSE;
-
- str = find_calltip(word, doc_list[idx].file_type);
- if (str)
- {
- g_free(calltip.text); // free the old calltip
- calltip.text = str;
- calltip.set = TRUE;
- utils_wrap_string(calltip.text, -1);
- SSM(sci, SCI_CALLTIPSHOW, orig_pos, (sptr_t) calltip.text);
- return TRUE;
- }
- return FALSE;
-}
-
-
-static void show_autocomplete(ScintillaObject *sci, gint rootlen, const gchar *words)
-{
- // store whether a calltip is showing, so we can reshow it after autocompletion
- calltip.set = SSM(sci, SCI_CALLTIPACTIVE, 0, 0);
- SSM(sci, SCI_AUTOCSHOW, rootlen, (sptr_t) words);
-}
-
-
-static gboolean
-autocomplete_html(ScintillaObject *sci, const gchar *root, gsize rootlen)
-{ // HTML entities auto completion
- guint i, j = 0;
- GString *words;
- const gchar **entities = symbols_get_html_entities();
-
- if (*root != '&' || entities == NULL) return FALSE;
-
- words = g_string_sized_new(500);
- for (i = 0; ; i++)
- {
- if (entities[i] == NULL) break;
- else if (entities[i][0] == '#') continue;
-
- if (! strncmp(entities[i], root, rootlen))
- {
- if (j++ > 0) g_string_append_c(words, ' ');
- g_string_append(words, entities[i]);
- }
- }
- if (words->len > 0) show_autocomplete(sci, rootlen, words->str);
- g_string_free(words, TRUE);
- return TRUE;
-}
-
-
-static gboolean
-autocomplete_tags(gint idx, gchar *root, gsize rootlen)
-{ // PHP, LaTeX, C, C++, D and Java tag autocompletion
- TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
- const GPtrArray *tags;
- ScintillaObject *sci;
-
- if (! DOC_IDX_VALID(idx) || doc_list[idx].file_type == NULL)
- return FALSE;
- sci = doc_list[idx].sci;
-
- tags = tm_workspace_find(root, tm_tag_max_t, attrs, TRUE, doc_list[idx].file_type->lang);
- if (NULL != tags && tags->len > 0)
- {
- GString *words = g_string_sized_new(150);
- guint j;
-
- for (j = 0; ((j < tags->len) && (j < GEANY_MAX_AUTOCOMPLETE_WORDS)); ++j)
- {
- if (j > 0) g_string_append_c(words, ' ');
- g_string_append(words, ((TMTag *) tags->pdata[j])->name);
- }
- show_autocomplete(sci, rootlen, words->str);
- g_string_free(words, TRUE);
- }
- return TRUE;
-}
-
-
-gboolean sci_cb_start_auto_complete(gint idx, gint pos, gboolean force)
-{
- gint line, line_start, line_len, line_pos, current, rootlen, startword, lexer, style;
- gchar *linebuf, *root;
- ScintillaObject *sci;
- gboolean ret = FALSE;
- gchar *wordchars;
- filetype *ft;
-
- if ((! app->pref_editor_auto_complete_symbols && ! force) ||
- ! DOC_IDX_VALID(idx) || doc_list[idx].file_type == NULL)
- return FALSE;
-
- sci = doc_list[idx].sci;
- ft = doc_list[idx].file_type;
-
- line = sci_get_line_from_position(sci, pos);
- line_start = sci_get_position_from_line(sci, line);
- line_len = sci_get_line_length(sci, line);
- line_pos = pos - line_start - 1;
- current = pos - line_start;
- startword = current;
- lexer = SSM(sci, SCI_GETLEXER, 0, 0);
- style = SSM(sci, SCI_GETSTYLEAT, pos, 0);
-
- // don't autocomplete in comments and strings
- if (is_comment(lexer, style))
- return FALSE;
-
- linebuf = sci_get_line(sci, line);
-
- if (ft->id == GEANY_FILETYPES_LATEX)
- wordchars = GEANY_WORDCHARS"\\"; // add \ to word chars if we are in a LaTeX file
- else if (ft->id == GEANY_FILETYPES_HTML || ft->id == GEANY_FILETYPES_PHP)
- wordchars = GEANY_WORDCHARS"&"; // add & to word chars if we are in a PHP or HTML file
- else
- wordchars = GEANY_WORDCHARS;
-
- // find the start of the current word
- while ((startword > 0) && (strchr(wordchars, linebuf[startword - 1])))
- startword--;
- linebuf[current] = '\0';
- root = linebuf + startword;
- rootlen = current - startword;
-
- // entity autocompletion always in a HTML file, in a PHP file only when we are outside of <? ?>
- if (ft->id == GEANY_FILETYPES_HTML ||
- (ft->id == GEANY_FILETYPES_PHP && (style < SCE_HPHP_DEFAULT || style > SCE_HPHP_OPERATOR)))
- ret = autocomplete_html(sci, root, rootlen);
- else
- {
- // force is set when called by keyboard shortcut, otherwise start at the 4th char
- if (force || rootlen >= 4)
- ret = autocomplete_tags(idx, root, rootlen);
- }
-
- g_free(linebuf);
- return ret;
-}
-
-
-void sci_cb_auto_latex(gint idx, gint pos)
-{
- ScintillaObject *sci;
-
- if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return;
- sci = doc_list[idx].sci;
-
- if (sci_get_char_at(sci, pos - 2) == '}')
- {
- gchar *eol, *buf, *construct;
- gchar env[50];
- gint line = sci_get_line_from_position(sci, pos - 2);
- gint line_len = sci_get_line_length(sci, line);
- gint i, start;
-
- // get the line
- buf = sci_get_line(sci, line);
-
- // get to the first non-blank char (some kind of ltrim())
- start = 0;
- //while (isspace(buf[i++])) start++;
- while (isspace(buf[start])) start++;
-
- // check for begin
- if (strncmp(buf + start, "\\begin", 6) == 0)
- {
- gchar full_cmd[15];
- guint j = 0;
-
- // take also "\begingroup" (or whatever there can be) and append "\endgroup" and so on.
- i = start + 6;
- while (i < line_len && buf[i] != '{' && j < (sizeof(full_cmd) - 1))
- { // copy all between "\begin" and "{" to full_cmd
- full_cmd[j] = buf[i];
- i++;
- j++;
- }
- full_cmd[j] = '\0';
-
- // go through the line and get the environment
- for (i = start + j; i < line_len; i++)
- {
- if (buf[i] == '{')
- {
- j = 0;
- i++;
@@ Diff output truncated at 100000 characters. @@
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the Commits
mailing list