2005-11-22 12:26:26 +00:00
|
|
|
/*
|
2007-05-28 16:07:30 +00:00
|
|
|
* editor.c - this file is part of Geany, a fast and lightweight IDE
|
2005-11-22 12:26:26 +00:00
|
|
|
*
|
2007-01-14 17:36:42 +00:00
|
|
|
* Copyright 2005-2007 Enrico Tröger <enrico.troeger@uvena.de>
|
2007-01-06 15:03:53 +00:00
|
|
|
* Copyright 2006-2007 Nick Treleaven <nick.treleaven@btinternet.com>
|
2005-11-22 12:26:26 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2005-11-30 23:43:56 +00:00
|
|
|
* $Id$
|
2005-11-22 12:26:26 +00:00
|
|
|
*/
|
|
|
|
|
2007-02-24 11:41:56 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-11-22 12:26:26 +00:00
|
|
|
|
|
|
|
#include <ctype.h>
|
2005-12-29 19:50:50 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
2006-07-04 21:31:08 +00:00
|
|
|
#include "SciLexer.h"
|
2005-12-29 19:50:50 +00:00
|
|
|
#include "geany.h"
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2007-05-28 16:07:30 +00:00
|
|
|
#include "editor.h"
|
2005-12-29 19:50:50 +00:00
|
|
|
#include "document.h"
|
2007-08-15 15:37:21 +00:00
|
|
|
#include "filetypes.h"
|
2005-12-29 19:50:50 +00:00
|
|
|
#include "sciwrappers.h"
|
2006-09-05 14:24:47 +00:00
|
|
|
#include "ui_utils.h"
|
2005-12-29 19:50:50 +00:00
|
|
|
#include "utils.h"
|
2006-11-08 11:42:05 +00:00
|
|
|
#include "symbols.h"
|
2006-10-21 11:16:54 +00:00
|
|
|
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2006-09-04 15:57:46 +00:00
|
|
|
static gchar current_word[GEANY_MAX_WORD_LENGTH]; // holds word under the mouse or keyboard cursor
|
|
|
|
|
2007-05-29 16:30:54 +00:00
|
|
|
// Initialised in keyfile.c.
|
|
|
|
EditorPrefs editor_prefs;
|
|
|
|
|
2006-09-04 15:57:46 +00:00
|
|
|
EditorInfo editor_info = {current_word, -1};
|
|
|
|
|
2006-08-15 21:00:43 +00:00
|
|
|
static struct
|
|
|
|
{
|
|
|
|
gchar *text;
|
|
|
|
gboolean set;
|
2007-04-13 11:42:25 +00:00
|
|
|
gchar *last_word;
|
|
|
|
guint tag_index;
|
|
|
|
} calltip = {NULL, FALSE, NULL, 0};
|
2006-08-15 21:00:43 +00:00
|
|
|
|
2005-11-22 12:26:26 +00:00
|
|
|
static gchar indent[100];
|
|
|
|
|
|
|
|
|
2006-08-03 14:08:10 +00:00
|
|
|
static void on_new_line_added(ScintillaObject *sci, gint idx);
|
2006-12-03 23:05:29 +00:00
|
|
|
static gboolean handle_xml(ScintillaObject *sci, gchar ch, gint idx);
|
2006-12-08 18:13:22 +00:00
|
|
|
static void get_indent(ScintillaObject *sci, gint pos, gboolean use_this_line);
|
|
|
|
static void auto_multiline(ScintillaObject *sci, gint pos);
|
2006-12-15 17:09:05 +00:00
|
|
|
static gboolean is_comment(gint lexer, gint style);
|
2007-02-21 12:11:28 +00:00
|
|
|
static void auto_close_bracket(ScintillaObject *sci, gint pos, gchar c);
|
2006-08-03 14:08:10 +00:00
|
|
|
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2006-09-04 15:57:46 +00:00
|
|
|
// 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)
|
|
|
|
{
|
2007-05-29 16:30:54 +00:00
|
|
|
if (GDK_BUTTON_PRESS == event->type && editor_prefs.disable_dnd)
|
2007-01-13 20:50:36 +00:00
|
|
|
{
|
|
|
|
gint ss = sci_get_selection_start(doc_list[idx].sci);
|
|
|
|
sci_set_selection_end(doc_list[idx].sci, ss);
|
|
|
|
}
|
2006-12-18 13:04:18 +00:00
|
|
|
return utils_check_disk_status(idx, FALSE);
|
2006-09-04 15:57:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (event->button == 3)
|
|
|
|
{
|
2007-05-28 16:07:30 +00:00
|
|
|
editor_find_current_word(doc_list[idx].sci, editor_info.click_pos,
|
2007-01-24 19:20:12 +00:00
|
|
|
current_word, sizeof current_word, NULL);
|
2006-09-04 15:57:46 +00:00
|
|
|
|
2006-09-05 14:24:47 +00:00
|
|
|
ui_update_popup_goto_items((current_word[0] != '\0') ? TRUE : FALSE);
|
|
|
|
ui_update_popup_copy_items(idx);
|
|
|
|
ui_update_insert_include_item(idx, 0);
|
2006-09-04 15:57:46 +00:00
|
|
|
gtk_menu_popup(GTK_MENU(app->popup_menu), NULL, NULL, NULL, NULL, event->button, event->time);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-26 15:31:50 +00:00
|
|
|
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
|
2007-05-29 16:30:54 +00:00
|
|
|
else if (nt->margin == 2 && editor_prefs.folding)
|
2007-03-26 15:31:50 +00:00
|
|
|
{
|
|
|
|
gint line = SSM(sci, SCI_LINEFROMPOSITION, nt->position, 0);
|
|
|
|
|
|
|
|
SSM(sci, SCI_TOGGLEFOLD, line, 0);
|
2007-05-29 16:30:54 +00:00
|
|
|
if (editor_prefs.unfold_all_children &&
|
2007-03-26 15:31:50 +00:00
|
|
|
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
|
2007-05-28 16:07:30 +00:00
|
|
|
editor_highlight_braces(sci, pos);
|
2007-03-26 15:31:50 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2007-07-17 16:27:49 +00:00
|
|
|
editor_scroll_to_line(sci, -1, doc_list[idx].scroll_percent);
|
2007-03-26 15:31:50 +00:00
|
|
|
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
|
2007-05-28 16:07:30 +00:00
|
|
|
editor_show_calltip(idx, --pos);
|
2007-03-26 15:31:50 +00:00
|
|
|
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
|
2007-05-28 16:07:30 +00:00
|
|
|
editor_show_calltip(idx, --pos);
|
2007-03-26 15:31:50 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '}':
|
|
|
|
{ // closing bracket handling
|
2007-08-29 15:14:07 +00:00
|
|
|
if (doc_list[idx].auto_indent)
|
2007-05-28 16:07:30 +00:00
|
|
|
editor_close_block(idx, pos - 1);
|
2007-03-26 15:31:50 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-05-28 16:07:30 +00:00
|
|
|
default: editor_start_auto_complete(idx, pos, FALSE);
|
2007-03-26 15:31:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-03 14:08:10 +00:00
|
|
|
// callback func called by all editors when a signal arises
|
2006-04-27 18:00:00 +00:00
|
|
|
void on_editor_notification(GtkWidget *editor, gint scn, gpointer lscn, gpointer user_data)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2007-03-26 15:31:50 +00:00
|
|
|
SCNotification *nt;
|
2005-11-22 12:26:26 +00:00
|
|
|
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:
|
|
|
|
{
|
2006-02-10 20:43:30 +00:00
|
|
|
utils_beep();
|
2005-11-22 12:26:26 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SCN_MARGINCLICK:
|
2007-03-26 15:31:50 +00:00
|
|
|
on_margin_click(sci, nt);
|
2005-11-22 12:26:26 +00:00
|
|
|
break;
|
2006-04-30 15:13:13 +00:00
|
|
|
|
2007-03-26 15:31:50 +00:00
|
|
|
case SCN_UPDATEUI:
|
|
|
|
on_update_ui(idx, nt);
|
2005-11-22 12:26:26 +00:00
|
|
|
break;
|
2007-03-26 15:31:50 +00:00
|
|
|
|
2006-10-10 16:02:41 +00:00
|
|
|
case SCN_MODIFIED:
|
2006-09-06 16:09:08 +00:00
|
|
|
{
|
2007-01-20 23:54:28 +00:00
|
|
|
if (nt->modificationType & SC_STARTACTION && ! app->ignore_callback)
|
2006-09-06 16:09:08 +00:00
|
|
|
{
|
2006-10-10 16:02:41 +00:00
|
|
|
// get notified about undo changes
|
2006-09-06 16:09:08 +00:00
|
|
|
document_undo_add(idx, UNDO_SCINTILLA, NULL);
|
|
|
|
}
|
2005-11-22 12:26:26 +00:00
|
|
|
break;
|
|
|
|
}
|
2006-10-10 16:02:41 +00:00
|
|
|
case SCN_CHARADDED:
|
2007-03-26 15:31:50 +00:00
|
|
|
on_char_added(idx, nt);
|
2005-11-22 12:26:26 +00:00
|
|
|
break;
|
2007-03-26 15:31:50 +00:00
|
|
|
|
2005-11-22 12:26:26 +00:00
|
|
|
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;
|
2007-01-09 16:53:27 +00:00
|
|
|
while (start > 0 && sci_get_char_at(sci, --start) != '&') ;
|
2005-11-22 12:26:26 +00:00
|
|
|
|
|
|
|
SSM(sci, SCI_INSERTTEXT, pos - 1, (sptr_t) nt->text);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2006-08-15 21:00:43 +00:00
|
|
|
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;
|
|
|
|
}
|
2006-11-06 21:02:03 +00:00
|
|
|
#ifdef GEANY_DEBUG
|
|
|
|
case SCN_STYLENEEDED:
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
|
|
|
geany_debug("style");
|
|
|
|
break;
|
|
|
|
}
|
2006-11-06 21:02:03 +00:00
|
|
|
#endif
|
|
|
|
case SCN_URIDROPPED:
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2006-11-15 23:12:13 +00:00
|
|
|
if (nt->text != NULL)
|
2006-04-27 18:00:00 +00:00
|
|
|
{
|
2006-11-17 12:19:31 +00:00
|
|
|
document_open_file_list(nt->text, -1);
|
2006-04-27 18:00:00 +00:00
|
|
|
}
|
2006-02-26 18:19:28 +00:00
|
|
|
break;
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
2007-04-13 11:42:25 +00:00
|
|
|
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
|
|
|
|
}
|
2007-05-28 16:07:30 +00:00
|
|
|
editor_show_calltip(idx, -1);
|
2007-04-13 11:42:25 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-01-09 17:56:50 +00:00
|
|
|
|
2007-05-29 16:30:54 +00:00
|
|
|
/* Returns a string containing width chars of whitespace according to the
|
|
|
|
* setting editor_prefs.use_tabs filled with simple space characters or with the right number
|
|
|
|
* of tab characters. (Result is filled with tabs *and* spaces if width isn't a multiple of
|
|
|
|
* editor_prefs.tab_width).
|
|
|
|
* If alternative is set to TRUE, it uses the opposite of editor_prefs.use_tabs. */
|
|
|
|
static gchar *
|
|
|
|
get_whitespace(gint width, gboolean alternative)
|
|
|
|
{
|
|
|
|
gchar *str;
|
|
|
|
gboolean use_tabs;
|
|
|
|
|
|
|
|
g_return_val_if_fail(width > 0, NULL);
|
|
|
|
|
|
|
|
use_tabs = (alternative) ? ! editor_prefs.use_tabs : editor_prefs.use_tabs;
|
|
|
|
|
|
|
|
if (use_tabs)
|
|
|
|
{ // first fill text with tabluators and fill the rest with spaces
|
|
|
|
gint tabs = width / editor_prefs.tab_width;
|
|
|
|
gint spaces = width % editor_prefs.tab_width;
|
|
|
|
gint len = tabs + spaces;
|
|
|
|
|
|
|
|
str = g_malloc(len + 1);
|
|
|
|
|
|
|
|
memset(str, '\t', tabs);
|
|
|
|
memset(str + tabs, ' ', spaces);
|
|
|
|
str[len] = '\0';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
str = g_strnfill(width, ' ');
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-03 14:08:10 +00:00
|
|
|
static void on_new_line_added(ScintillaObject *sci, gint idx)
|
|
|
|
{
|
|
|
|
gint pos = sci_get_current_position(sci);
|
|
|
|
|
|
|
|
// simple indentation
|
2007-06-12 15:16:17 +00:00
|
|
|
if (doc_list[idx].auto_indent)
|
2006-08-03 14:08:10 +00:00
|
|
|
{
|
2006-12-08 18:13:22 +00:00
|
|
|
get_indent(sci, pos, FALSE);
|
2006-08-03 14:08:10 +00:00
|
|
|
sci_add_text(sci, indent);
|
2006-10-30 12:58:58 +00:00
|
|
|
|
2007-08-29 15:14:07 +00:00
|
|
|
if (editor_prefs.indent_mode > INDENT_BASIC)
|
2007-03-08 18:02:08 +00:00
|
|
|
{
|
|
|
|
// add extra indentation for Python after colon
|
2007-05-08 17:45:54 +00:00
|
|
|
if (FILETYPE_ID(doc_list[idx].file_type) == GEANY_FILETYPES_PYTHON &&
|
2007-03-08 18:02:08 +00:00
|
|
|
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
|
2007-05-29 16:30:54 +00:00
|
|
|
gchar *text = get_whitespace(editor_prefs.tab_width, FALSE);
|
2007-03-08 18:02:08 +00:00
|
|
|
sci_add_text(sci, text);
|
|
|
|
g_free(text);
|
|
|
|
}
|
2006-10-30 12:58:58 +00:00
|
|
|
}
|
2007-02-25 17:27:46 +00:00
|
|
|
}
|
|
|
|
|
2007-05-29 16:30:54 +00:00
|
|
|
if (editor_prefs.auto_complete_constructs)
|
2007-03-09 14:06:20 +00:00
|
|
|
{
|
|
|
|
// " * " auto completion in multiline C/C++/D/Java comments
|
|
|
|
auto_multiline(sci, pos);
|
|
|
|
|
2007-05-28 16:07:30 +00:00
|
|
|
editor_auto_latex(idx, pos);
|
2007-03-09 14:06:20 +00:00
|
|
|
}
|
2006-08-03 14:08:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-28 12:11:37 +00:00
|
|
|
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:
|
2007-05-05 16:51:41 +00:00
|
|
|
case SCLEX_PERL:
|
|
|
|
case SCLEX_TCL:
|
2007-03-28 12:11:37 +00:00
|
|
|
return TRUE;
|
|
|
|
default:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// in place indentation of one tab or equivalent spaces
|
2007-03-30 10:38:13 +00:00
|
|
|
static void do_indent(gchar *buf, gsize len, guint *idx)
|
2007-03-28 12:11:37 +00:00
|
|
|
{
|
2007-03-30 10:38:13 +00:00
|
|
|
guint j = *idx;
|
2007-03-28 12:11:37 +00:00
|
|
|
|
2007-05-29 16:30:54 +00:00
|
|
|
if (editor_prefs.use_tabs)
|
2007-03-28 12:11:37 +00:00
|
|
|
{
|
|
|
|
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;
|
2007-05-29 16:30:54 +00:00
|
|
|
for (k = 0; k < (guint) editor_prefs.tab_width && k < len - 1; k++)
|
2007-03-28 12:11:37 +00:00
|
|
|
buf[j++] = ' ';
|
|
|
|
}
|
2007-03-30 10:38:13 +00:00
|
|
|
*idx = j;
|
2007-03-28 12:11:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* "use_this_line" to auto-indent only if it is a real new line
|
2007-05-28 16:07:30 +00:00
|
|
|
* and ignore the case of editor_close_block */
|
2006-12-08 18:13:22 +00:00
|
|
|
static void get_indent(ScintillaObject *sci, gint pos, gboolean use_this_line)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2007-03-09 12:18:42 +00:00
|
|
|
guint i, len, j = 0;
|
|
|
|
gint prev_line;
|
2006-01-16 18:28:50 +00:00
|
|
|
gchar *linebuf;
|
|
|
|
|
2005-11-22 12:26:26 +00:00
|
|
|
prev_line = sci_get_line_from_position(sci, pos);
|
|
|
|
|
|
|
|
if (! use_this_line) prev_line--;
|
|
|
|
len = sci_get_line_length(sci, prev_line);
|
2006-10-02 15:22:29 +00:00
|
|
|
linebuf = sci_get_line(sci, prev_line);
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2007-03-08 18:02:08 +00:00
|
|
|
for (i = 0; i < len && j <= (sizeof(indent) - 1); i++)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2007-03-28 12:11:37 +00:00
|
|
|
if (linebuf[i] == ' ' || linebuf[i] == '\t') // simple indentation
|
2007-03-08 18:02:08 +00:00
|
|
|
indent[j++] = linebuf[i];
|
2007-08-29 15:14:07 +00:00
|
|
|
else if (editor_prefs.indent_mode <= INDENT_BASIC)
|
2007-03-28 12:11:37 +00:00
|
|
|
break;
|
|
|
|
else if (use_this_line)
|
|
|
|
break;
|
2007-05-28 16:07:30 +00:00
|
|
|
else // editor_close_block
|
2006-01-16 18:28:50 +00:00
|
|
|
{
|
2007-03-28 12:11:37 +00:00
|
|
|
if (! lexer_has_braces(sci))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (linebuf[i] == '{')
|
2007-01-14 17:09:17 +00:00
|
|
|
{
|
2007-03-28 12:11:37 +00:00
|
|
|
do_indent(indent, sizeof(indent), &j);
|
|
|
|
break;
|
2007-01-14 17:09:17 +00:00
|
|
|
}
|
|
|
|
else
|
2007-03-28 12:11:37 +00:00
|
|
|
{
|
|
|
|
gint k = len - 1;
|
2007-03-08 18:02:08 +00:00
|
|
|
|
2007-03-28 12:11:37 +00:00
|
|
|
while (k > 0 && isspace(linebuf[k])) k--;
|
2007-03-08 18:02:08 +00:00
|
|
|
|
2007-03-28 12:11:37 +00:00
|
|
|
// if last non-whitespace character is a { increase indentation by a tab
|
|
|
|
// e.g. for (...) {
|
|
|
|
if (linebuf[k] == '{')
|
2007-01-14 17:09:17 +00:00
|
|
|
{
|
2007-03-28 12:11:37 +00:00
|
|
|
do_indent(indent, sizeof(indent), &j);
|
2007-01-14 17:09:17 +00:00
|
|
|
}
|
2007-03-28 12:11:37 +00:00
|
|
|
break;
|
2007-01-14 17:09:17 +00:00
|
|
|
}
|
2006-01-16 18:28:50 +00:00
|
|
|
}
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
indent[j] = '\0';
|
2006-01-16 18:28:50 +00:00
|
|
|
g_free(linebuf);
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-02-21 12:11:28 +00:00
|
|
|
static void auto_close_bracket(ScintillaObject *sci, gint pos, gchar c)
|
2005-12-13 22:06:12 +00:00
|
|
|
{
|
2007-05-29 16:30:54 +00:00
|
|
|
if (! editor_prefs.auto_complete_constructs || SSM(sci, SCI_GETLEXER, 0, 0) != SCLEX_LATEX)
|
2007-02-21 12:11:28 +00:00
|
|
|
return;
|
2005-12-13 22:06:12 +00:00
|
|
|
|
|
|
|
if (c == '[')
|
|
|
|
{
|
|
|
|
sci_add_text(sci, "]");
|
|
|
|
}
|
|
|
|
else if (c == '{')
|
|
|
|
{
|
|
|
|
sci_add_text(sci, "}");
|
|
|
|
}
|
2007-01-27 18:45:47 +00:00
|
|
|
sci_set_current_position(sci, pos, TRUE);
|
2005-12-13 22:06:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-20 20:39:59 +00:00
|
|
|
/* Finds a corresponding matching brace to the given pos
|
|
|
|
* (this is taken from Scintilla Editor.cxx,
|
2007-05-28 16:07:30 +00:00
|
|
|
* fit to work with editor_close_block) */
|
2006-08-20 20:39:59 +00:00
|
|
|
static gint brace_match(ScintillaObject *sci, gint pos)
|
|
|
|
{
|
|
|
|
gchar chBrace = sci_get_char_at(sci, pos);
|
|
|
|
gchar chSeek = utils_brace_opposite(chBrace);
|
2006-11-06 21:02:03 +00:00
|
|
|
gchar chAtPos;
|
|
|
|
gint direction = -1;
|
|
|
|
gint styBrace;
|
|
|
|
gint depth = 1;
|
|
|
|
gint styAtPos;
|
2006-08-20 20:39:59 +00:00
|
|
|
|
|
|
|
styBrace = sci_get_style_at(sci, pos);
|
|
|
|
|
2007-08-23 11:34:06 +00:00
|
|
|
if (utils_is_opening_brace(chBrace, editor_prefs.brace_match_ltgt))
|
2006-08-20 20:39:59 +00:00
|
|
|
direction = 1;
|
|
|
|
|
|
|
|
pos = pos + direction;
|
|
|
|
while ((pos >= 0) && (pos < sci_get_length(sci)))
|
|
|
|
{
|
2006-11-06 21:02:03 +00:00
|
|
|
chAtPos = sci_get_char_at(sci, pos - 1);
|
|
|
|
styAtPos = sci_get_style_at(sci, pos);
|
2006-08-20 20:39:59 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2006-11-06 21:02:03 +00:00
|
|
|
return -1;
|
2006-08-20 20:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-29 15:14:07 +00:00
|
|
|
/* Called after typing '}'. */
|
2007-05-28 16:07:30 +00:00
|
|
|
void editor_close_block(gint idx, gint pos)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2005-11-30 23:43:56 +00:00
|
|
|
gint x = 0, cnt = 0;
|
2007-08-29 15:14:07 +00:00
|
|
|
gint line, line_len, eol_char_len;
|
2006-03-17 02:25:39 +00:00
|
|
|
gchar *text, *line_buf;
|
2006-09-01 17:07:49 +00:00
|
|
|
ScintillaObject *sci;
|
2007-08-29 15:14:07 +00:00
|
|
|
gint line_indent, last_indent;
|
2006-09-01 17:07:49 +00:00
|
|
|
|
2007-08-29 15:14:07 +00:00
|
|
|
if (editor_prefs.indent_mode < INDENT_CURRENTCHARS)
|
|
|
|
return;
|
|
|
|
if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL)
|
|
|
|
return;
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2006-09-01 17:07:49 +00:00
|
|
|
sci = doc_list[idx].sci;
|
|
|
|
|
2007-03-28 12:11:37 +00:00
|
|
|
if (! lexer_has_braces(sci))
|
2006-12-15 17:09:05 +00:00
|
|
|
return;
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2006-09-01 17:07:49 +00:00
|
|
|
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));
|
|
|
|
|
2005-11-22 12:26:26 +00:00
|
|
|
// check that the line is empty, to not kill text in the line
|
2006-10-02 15:22:29 +00:00
|
|
|
line_buf = sci_get_line(sci, line);
|
2005-11-22 12:26:26 +00:00
|
|
|
line_buf[line_len - eol_char_len] = '\0';
|
2006-01-17 17:38:44 +00:00
|
|
|
while (x < (line_len - eol_char_len))
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
|
|
|
if (isspace(line_buf[x])) cnt++;
|
|
|
|
x++;
|
|
|
|
}
|
2006-03-17 02:25:39 +00:00
|
|
|
g_free(line_buf);
|
2006-04-30 15:13:13 +00:00
|
|
|
|
2007-08-29 15:14:07 +00:00
|
|
|
if ((line_len - eol_char_len - 1) != cnt)
|
|
|
|
return;
|
2007-03-29 11:12:48 +00:00
|
|
|
|
2007-08-29 15:14:07 +00:00
|
|
|
if (editor_prefs.indent_mode == INDENT_MATCHBRACES)
|
2006-11-05 21:20:22 +00:00
|
|
|
{
|
2007-08-29 15:14:07 +00:00
|
|
|
gint start_brace = brace_match(sci, pos);
|
2007-05-16 12:51:29 +00:00
|
|
|
|
2007-08-29 15:14:07 +00:00
|
|
|
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);
|
2007-05-16 12:51:29 +00:00
|
|
|
return;
|
2007-08-29 15:14:07 +00:00
|
|
|
}
|
|
|
|
// fall through - unmatched brace (possibly because of TCL, PHP lexer bugs)
|
2007-05-16 12:51:29 +00:00
|
|
|
}
|
2007-08-29 15:14:07 +00:00
|
|
|
|
|
|
|
// INDENT_CURRENTCHARS
|
|
|
|
line_indent = sci_get_line_indentation(sci, line);
|
|
|
|
last_indent = sci_get_line_indentation(sci, line - 1);
|
|
|
|
|
|
|
|
if (line_indent < last_indent)
|
|
|
|
return;
|
|
|
|
line_indent -= editor_prefs.tab_width;
|
|
|
|
line_indent = MAX(0, line_indent);
|
|
|
|
sci_set_line_indentation(sci, line, line_indent);
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-22 16:52:42 +00:00
|
|
|
/* 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.
|
2007-01-24 19:20:12 +00:00
|
|
|
* position can be -1, then the current position is used.
|
|
|
|
* wc are the wordchars to use, if NULL, GEANY_WORDCHARS will be used */
|
2007-05-28 16:07:30 +00:00
|
|
|
void editor_find_current_word(ScintillaObject *sci, gint pos, gchar *word, size_t wordlen,
|
2007-01-24 19:20:12 +00:00
|
|
|
const gchar *wc)
|
2006-08-20 20:39:59 +00:00
|
|
|
{
|
2006-10-22 16:52:42 +00:00
|
|
|
gint line, line_start, startword, endword;
|
2006-10-02 15:22:29 +00:00
|
|
|
gchar *chunk;
|
2006-08-20 20:39:59 +00:00
|
|
|
|
2006-10-22 16:52:42 +00:00
|
|
|
if (pos == -1)
|
|
|
|
pos = sci_get_current_position(sci);
|
2006-11-05 21:20:22 +00:00
|
|
|
|
2006-10-22 16:52:42 +00:00
|
|
|
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;
|
|
|
|
|
2006-08-20 20:39:59 +00:00
|
|
|
word[0] = '\0';
|
2006-10-02 15:22:29 +00:00
|
|
|
chunk = sci_get_line(sci, line);
|
2006-08-20 20:39:59 +00:00
|
|
|
|
2007-01-24 19:20:12 +00:00
|
|
|
if (wc == NULL)
|
|
|
|
wc = GEANY_WORDCHARS;
|
|
|
|
|
|
|
|
while (startword > 0 && strchr(wc, chunk[startword - 1]))
|
2006-08-20 20:39:59 +00:00
|
|
|
startword--;
|
2007-01-24 19:20:12 +00:00
|
|
|
while (chunk[endword] && strchr(wc, chunk[endword]))
|
2006-08-20 20:39:59 +00:00
|
|
|
endword++;
|
|
|
|
if(startword == endword)
|
|
|
|
return;
|
|
|
|
|
|
|
|
chunk[endword] = '\0';
|
|
|
|
|
2007-08-24 11:48:37 +00:00
|
|
|
g_strlcpy(word, chunk + startword, wordlen); // ensure null terminated
|
2006-08-20 20:39:59 +00:00
|
|
|
g_free(chunk);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-27 14:23:46 +00:00
|
|
|
static gint find_previous_brace(ScintillaObject *sci, gint pos)
|
|
|
|
{
|
|
|
|
gchar c;
|
|
|
|
gint orig_pos = pos;
|
2006-12-15 17:09:05 +00:00
|
|
|
|
2006-10-27 14:23:46 +00:00
|
|
|
c = SSM(sci, SCI_GETCHARAT, pos, 0);
|
|
|
|
while (pos >= 0 && pos > orig_pos - 300)
|
|
|
|
{
|
|
|
|
c = SSM(sci, SCI_GETCHARAT, pos, 0);
|
|
|
|
pos--;
|
2007-08-23 11:34:06 +00:00
|
|
|
if (utils_is_opening_brace(c, editor_prefs.brace_match_ltgt))
|
|
|
|
return pos;
|
2006-10-27 14:23:46 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-13 11:42:25 +00:00
|
|
|
static gboolean append_calltip(GString *str, const TMTag *tag, filetype_id ft_id)
|
2007-01-03 12:41:10 +00:00
|
|
|
{
|
2007-04-13 11:42:25 +00:00
|
|
|
if (! tag->atts.entry.arglist) return FALSE;
|
2007-01-03 12:41:10 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2007-01-12 12:33:17 +00:00
|
|
|
const gchar *cosep = symbols_get_context_separator(ft_id);
|
|
|
|
|
2007-01-03 12:41:10 +00:00
|
|
|
g_string_append(str, tag->atts.entry.scope);
|
2007-01-12 12:33:17 +00:00
|
|
|
g_string_append(str, cosep);
|
2007-01-03 12:41:10 +00:00
|
|
|
}
|
|
|
|
g_string_append(str, tag->name);
|
|
|
|
g_string_append_c(str, ' ');
|
|
|
|
g_string_append(str, tag->atts.entry.arglist);
|
|
|
|
|
2007-04-13 11:42:25 +00:00
|
|
|
return TRUE;
|
2007-01-03 12:41:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-24 12:07:35 +00:00
|
|
|
static gchar *find_calltip(const gchar *word, filetype *ft)
|
|
|
|
{
|
|
|
|
const GPtrArray *tags;
|
2007-04-15 11:46:25 +00:00
|
|
|
const gint arg_types = tm_tag_function_t | tm_tag_prototype_t |
|
|
|
|
tm_tag_method_t | tm_tag_macro_with_arg_t;
|
2007-04-13 11:42:25 +00:00
|
|
|
TMTagAttrType *attrs = NULL;
|
|
|
|
TMTag *tag;
|
|
|
|
GString *str = NULL;
|
|
|
|
guint i;
|
2006-12-24 12:07:35 +00:00
|
|
|
|
|
|
|
g_return_val_if_fail(ft && word && *word, NULL);
|
|
|
|
|
2007-04-15 11:46:25 +00:00
|
|
|
tags = tm_workspace_find(word, arg_types | tm_tag_class_t, attrs, FALSE, ft->lang);
|
2006-12-26 15:49:35 +00:00
|
|
|
if (tags->len == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
tag = TM_TAG(tags->pdata[0]);
|
2007-04-13 11:42:25 +00:00
|
|
|
|
|
|
|
if (tag->type == tm_tag_class_t && FILETYPE_ID(ft) == GEANY_FILETYPES_D)
|
2006-12-24 12:07:35 +00:00
|
|
|
{
|
2007-04-13 11:42:25 +00:00
|
|
|
// user typed e.g. 'new Classname(' so lookup D constructor Classname::this()
|
|
|
|
tags = tm_workspace_find_scoped("this", tag->name,
|
2007-04-15 11:46:25 +00:00
|
|
|
arg_types, attrs, FALSE, ft->lang, TRUE);
|
2007-04-13 11:42:25 +00:00
|
|
|
if (tags->len == 0)
|
|
|
|
return NULL;
|
2006-12-24 12:07:35 +00:00
|
|
|
}
|
2007-04-13 11:42:25 +00:00
|
|
|
|
|
|
|
// remove tags with no argument list
|
|
|
|
for (i = 0; i < tags->len; i++)
|
2006-12-26 15:49:35 +00:00
|
|
|
{
|
2007-04-13 11:42:25 +00:00
|
|
|
tag = TM_TAG(tags->pdata[i]);
|
2006-12-26 15:49:35 +00:00
|
|
|
|
2007-04-13 11:42:25 +00:00
|
|
|
if (! tag->atts.entry.arglist)
|
|
|
|
tags->pdata[i] = NULL;
|
|
|
|
}
|
|
|
|
tm_tags_prune((GPtrArray *) tags);
|
|
|
|
if (tags->len == 0)
|
|
|
|
return NULL;
|
2007-04-24 11:02:26 +00:00
|
|
|
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);
|
|
|
|
}
|
2007-04-13 11:42:25 +00:00
|
|
|
|
|
|
|
// 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)
|
2006-12-26 15:49:35 +00:00
|
|
|
{
|
2007-04-13 11:42:25 +00:00
|
|
|
str = g_string_new(NULL);
|
|
|
|
if (calltip.tag_index > 0)
|
|
|
|
g_string_prepend(str, "\001 "); // up arrow
|
|
|
|
append_calltip(str, tag, FILETYPE_ID(ft));
|
2006-12-26 15:49:35 +00:00
|
|
|
}
|
2007-04-13 11:42:25 +00:00
|
|
|
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;
|
2006-12-26 15:49:35 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
2006-12-24 12:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-04-13 11:42:25 +00:00
|
|
|
// use pos = -1 to search for the previous unmatched open bracket.
|
2007-05-28 16:07:30 +00:00
|
|
|
gboolean editor_show_calltip(gint idx, gint pos)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2006-09-01 17:07:49 +00:00
|
|
|
gint orig_pos = pos; // the position for the calltip
|
2006-06-24 18:43:56 +00:00
|
|
|
gint lexer;
|
2005-11-22 12:26:26 +00:00
|
|
|
gint style;
|
2006-06-18 20:21:19 +00:00
|
|
|
gchar word[GEANY_MAX_WORD_LENGTH];
|
2006-12-24 12:07:35 +00:00
|
|
|
gchar *str;
|
2006-09-01 17:07:49 +00:00
|
|
|
ScintillaObject *sci;
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2006-09-01 17:07:49 +00:00
|
|
|
if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return FALSE;
|
|
|
|
sci = doc_list[idx].sci;
|
2006-06-29 18:22:11 +00:00
|
|
|
|
2006-06-24 18:43:56 +00:00
|
|
|
lexer = SSM(sci, SCI_GETLEXER, 0, 0);
|
2006-06-29 18:22:11 +00:00
|
|
|
|
2005-11-22 12:26:26 +00:00
|
|
|
if (pos == -1)
|
2006-06-29 18:22:11 +00:00
|
|
|
{
|
2006-10-27 14:23:46 +00:00
|
|
|
// position of '(' is unknown, so go backwards from current position to find it
|
2005-11-22 12:26:26 +00:00
|
|
|
pos = SSM(sci, SCI_GETCURRENTPOS, 0, 0);
|
2006-10-27 14:23:46 +00:00
|
|
|
pos--;
|
2006-08-03 13:27:30 +00:00
|
|
|
orig_pos = pos;
|
2006-10-27 14:23:46 +00:00
|
|
|
pos = (lexer == SCLEX_LATEX) ? find_previous_brace(sci, pos) :
|
|
|
|
find_start_bracket(sci, pos);
|
|
|
|
if (pos == -1) return FALSE;
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
|
2007-08-30 15:11:33 +00:00
|
|
|
style = SSM(sci, SCI_GETSTYLEAT, pos - 1, 0); // the style 1 before the brace (which may be highlighted)
|
2006-12-15 17:09:05 +00:00
|
|
|
if (is_comment(lexer, style))
|
|
|
|
return FALSE;
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2006-10-27 14:23:46 +00:00
|
|
|
word[0] = '\0';
|
2007-05-28 16:07:30 +00:00
|
|
|
editor_find_current_word(sci, pos - 1, word, sizeof word, NULL);
|
2005-11-22 12:26:26 +00:00
|
|
|
if (word[0] == '\0') return FALSE;
|
|
|
|
|
2006-12-24 12:07:35 +00:00
|
|
|
str = find_calltip(word, doc_list[idx].file_type);
|
|
|
|
if (str)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2006-08-15 21:00:43 +00:00
|
|
|
g_free(calltip.text); // free the old calltip
|
2006-12-24 12:07:35 +00:00
|
|
|
calltip.text = str;
|
2006-08-15 21:00:43 +00:00
|
|
|
calltip.set = TRUE;
|
|
|
|
utils_wrap_string(calltip.text, -1);
|
|
|
|
SSM(sci, SCI_CALLTIPSHOW, orig_pos, (sptr_t) calltip.text);
|
2006-12-24 12:07:35 +00:00
|
|
|
return TRUE;
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
2006-12-24 12:07:35 +00:00
|
|
|
return FALSE;
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-08-15 21:00:43 +00:00
|
|
|
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;
|
2007-03-26 12:18:51 +00:00
|
|
|
const gchar **entities = symbols_get_html_entities();
|
2006-08-15 21:00:43 +00:00
|
|
|
|
2006-12-15 17:09:05 +00:00
|
|
|
if (*root != '&' || entities == NULL) return FALSE;
|
2006-08-15 21:00:43 +00:00
|
|
|
|
|
|
|
words = g_string_sized_new(500);
|
|
|
|
for (i = 0; ; i++)
|
|
|
|
{
|
2006-12-15 17:09:05 +00:00
|
|
|
if (entities[i] == NULL) break;
|
|
|
|
else if (entities[i][0] == '#') continue;
|
2006-08-15 21:00:43 +00:00
|
|
|
|
2006-12-15 17:09:05 +00:00
|
|
|
if (! strncmp(entities[i], root, rootlen))
|
2006-08-15 21:00:43 +00:00
|
|
|
{
|
|
|
|
if (j++ > 0) g_string_append_c(words, ' ');
|
2006-12-15 17:09:05 +00:00
|
|
|
g_string_append(words, entities[i]);
|
2006-08-15 21:00:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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)
|
2006-12-15 17:09:05 +00:00
|
|
|
{ // PHP, LaTeX, C, C++, D and Java tag autocompletion
|
2006-08-15 21:00:43 +00:00
|
|
|
TMTagAttrType attrs[] = { tm_tag_attr_name_t, 0 };
|
|
|
|
const GPtrArray *tags;
|
|
|
|
ScintillaObject *sci;
|
|
|
|
|
2006-12-04 16:09:57 +00:00
|
|
|
if (! DOC_IDX_VALID(idx) || doc_list[idx].file_type == NULL)
|
2006-08-15 21:00:43 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-05-28 16:07:30 +00:00
|
|
|
gboolean editor_start_auto_complete(gint idx, gint pos, gboolean force)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2006-06-27 20:43:04 +00:00
|
|
|
gint line, line_start, line_len, line_pos, current, rootlen, startword, lexer, style;
|
|
|
|
gchar *linebuf, *root;
|
2006-08-15 21:00:43 +00:00
|
|
|
ScintillaObject *sci;
|
|
|
|
gboolean ret = FALSE;
|
2006-12-04 16:09:57 +00:00
|
|
|
gchar *wordchars;
|
2006-12-15 17:09:05 +00:00
|
|
|
filetype *ft;
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2007-05-29 16:30:54 +00:00
|
|
|
if ((! editor_prefs.auto_complete_symbols && ! force) ||
|
2007-01-26 10:43:25 +00:00
|
|
|
! DOC_IDX_VALID(idx) || doc_list[idx].file_type == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
2006-08-15 21:00:43 +00:00
|
|
|
sci = doc_list[idx].sci;
|
2006-12-15 17:09:05 +00:00
|
|
|
ft = doc_list[idx].file_type;
|
2006-06-24 18:43:56 +00:00
|
|
|
|
|
|
|
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;
|
2006-06-27 20:43:04 +00:00
|
|
|
startword = current;
|
|
|
|
lexer = SSM(sci, SCI_GETLEXER, 0, 0);
|
2006-06-24 18:43:56 +00:00
|
|
|
style = SSM(sci, SCI_GETSTYLEAT, pos, 0);
|
|
|
|
|
2006-12-15 17:09:05 +00:00
|
|
|
// don't autocomplete in comments and strings
|
2007-08-30 15:11:33 +00:00
|
|
|
if (!force && is_comment(lexer, style))
|
2006-12-15 17:09:05 +00:00
|
|
|
return FALSE;
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2006-10-02 15:22:29 +00:00
|
|
|
linebuf = sci_get_line(sci, line);
|
2006-06-05 22:04:50 +00:00
|
|
|
|
2006-12-15 17:09:05 +00:00
|
|
|
if (ft->id == GEANY_FILETYPES_LATEX)
|
2006-12-04 16:09:57 +00:00
|
|
|
wordchars = GEANY_WORDCHARS"\\"; // add \ to word chars if we are in a LaTeX file
|
2006-12-15 17:09:05 +00:00
|
|
|
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
|
2006-12-04 16:09:57 +00:00
|
|
|
else
|
|
|
|
wordchars = GEANY_WORDCHARS;
|
|
|
|
|
2006-06-05 22:04:50 +00:00
|
|
|
// find the start of the current word
|
2006-12-04 16:09:57 +00:00
|
|
|
while ((startword > 0) && (strchr(wordchars, linebuf[startword - 1])))
|
2005-11-22 12:26:26 +00:00
|
|
|
startword--;
|
|
|
|
linebuf[current] = '\0';
|
|
|
|
root = linebuf + startword;
|
|
|
|
rootlen = current - startword;
|
|
|
|
|
2006-09-27 12:48:13 +00:00
|
|
|
// entity autocompletion always in a HTML file, in a PHP file only when we are outside of <? ?>
|
2006-12-15 17:09:05 +00:00
|
|
|
if (ft->id == GEANY_FILETYPES_HTML ||
|
|
|
|
(ft->id == GEANY_FILETYPES_PHP && (style < SCE_HPHP_DEFAULT || style > SCE_HPHP_OPERATOR)))
|
2006-08-15 21:00:43 +00:00
|
|
|
ret = autocomplete_html(sci, root, rootlen);
|
2005-11-22 12:26:26 +00:00
|
|
|
else
|
2006-08-15 21:00:43 +00:00
|
|
|
{
|
|
|
|
// force is set when called by keyboard shortcut, otherwise start at the 4th char
|
|
|
|
if (force || rootlen >= 4)
|
|
|
|
ret = autocomplete_tags(idx, root, rootlen);
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
2006-08-15 21:00:43 +00:00
|
|
|
|
2006-06-27 20:43:04 +00:00
|
|
|
g_free(linebuf);
|
2006-08-15 21:00:43 +00:00
|
|
|
return ret;
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-05-28 16:07:30 +00:00
|
|
|
void editor_auto_latex(gint idx, gint pos)
|
2006-03-10 00:49:19 +00:00
|
|
|
{
|
2006-09-01 17:07:49 +00:00
|
|
|
ScintillaObject *sci;
|
|
|
|
|
|
|
|
if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_type == NULL) return;
|
|
|
|
sci = doc_list[idx].sci;
|
|
|
|
|
2006-03-10 00:49:19 +00:00
|
|
|
if (sci_get_char_at(sci, pos - 2) == '}')
|
|
|
|
{
|
|
|
|
gchar *eol, *buf, *construct;
|
2006-08-29 16:11:55 +00:00
|
|
|
gchar env[50];
|
2006-03-10 00:49:19 +00:00
|
|
|
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
|
2006-10-02 15:22:29 +00:00
|
|
|
buf = sci_get_line(sci, line);
|
2006-03-10 00:49:19 +00:00
|
|
|
|
|
|
|
// get to the first non-blank char (some kind of ltrim())
|
2006-08-29 16:11:55 +00:00
|
|
|
start = 0;
|
|
|
|
//while (isspace(buf[i++])) start++;
|
|
|
|
while (isspace(buf[start])) start++;
|
2006-03-10 00:49:19 +00:00
|
|
|
|
|
|
|
// check for begin
|
|
|
|
if (strncmp(buf + start, "\\begin", 6) == 0)
|
|
|
|
{
|
2006-08-29 16:11:55 +00:00
|
|
|
gchar full_cmd[15];
|
2006-09-02 20:30:35 +00:00
|
|
|
guint j = 0;
|
2006-08-29 16:11:55 +00:00
|
|
|
|
|
|
|
// take also "\begingroup" (or whatever there can be) and append "\endgroup" and so on.
|
|
|
|
i = start + 6;
|
2006-10-02 15:22:29 +00:00
|
|
|
while (i < line_len && buf[i] != '{' && j < (sizeof(full_cmd) - 1))
|
2006-08-29 16:11:55 +00:00
|
|
|
{ // 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++)
|
2006-03-10 00:49:19 +00:00
|
|
|
{
|
|
|
|
if (buf[i] == '{')
|
|
|
|
{
|
2006-08-29 16:11:55 +00:00
|
|
|
j = 0;
|
2006-03-10 00:49:19 +00:00
|
|
|
i++;
|
2006-08-29 16:11:55 +00:00
|
|
|
while (buf[i] != '}' && j < (sizeof(env) - 1))
|
|
|
|
{ // this could be done in a shorter way, but so it remains readable ;-)
|
2006-03-10 00:49:19 +00:00
|
|
|
env[j] = buf[i];
|
|
|
|
j++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
env[j] = '\0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-12 15:16:17 +00:00
|
|
|
// get the indentation
|
|
|
|
if (doc_list[idx].auto_indent) get_indent(sci, pos, TRUE);
|
2006-03-10 00:49:19 +00:00
|
|
|
eol = g_strconcat(utils_get_eol_char(idx), indent, NULL);
|
|
|
|
|
2006-08-29 16:11:55 +00:00
|
|
|
construct = g_strdup_printf("%s\\end%s{%s}", eol, full_cmd, env);
|
2006-03-10 00:49:19 +00:00
|
|
|
|
|
|
|
SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) construct);
|
|
|
|
sci_goto_pos(sci, pos + 1, TRUE);
|
|
|
|
g_free(construct);
|
|
|
|
g_free(eol);
|
|
|
|
}
|
2006-10-02 15:22:29 +00:00
|
|
|
// later there could be some else ifs for other keywords
|
|
|
|
|
|
|
|
g_free(buf);
|
2006-03-10 00:49:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
static gchar *ac_find_completion_by_name(const gchar *type, const gchar *name)
|
2007-02-16 16:38:23 +00:00
|
|
|
{
|
2007-06-17 17:56:48 +00:00
|
|
|
gchar *result = NULL;
|
|
|
|
GHashTable *tmp;
|
2006-06-03 15:05:41 +00:00
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
g_return_val_if_fail(type != NULL && name != NULL, NULL);
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
tmp = g_hash_table_lookup(editor_prefs.auto_completions, type);
|
|
|
|
if (tmp != NULL)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2007-06-17 17:56:48 +00:00
|
|
|
result = g_hash_table_lookup(tmp, name);
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
2007-06-17 17:56:48 +00:00
|
|
|
// whether nothing is set for the current filetype(tmp is NULL) or
|
|
|
|
// the particular completion for this filetype is not set (result is NULL)
|
|
|
|
if (tmp == NULL || result == NULL)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2007-06-17 17:56:48 +00:00
|
|
|
tmp = g_hash_table_lookup(editor_prefs.auto_completions, "Default");
|
|
|
|
if (tmp != NULL)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2007-06-17 17:56:48 +00:00
|
|
|
result = g_hash_table_lookup(tmp, name);
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
}
|
2007-06-17 17:56:48 +00:00
|
|
|
// if result is still NULL here, no completion could be found
|
2006-06-03 15:05:41 +00:00
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
// result is owned by the hash table and will be freed when the table will destroyed
|
|
|
|
return g_strdup(result);
|
|
|
|
}
|
2006-06-03 15:05:41 +00:00
|
|
|
|
2006-04-27 18:00:00 +00:00
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
/* This is very ugly but passing the pattern to ac_replace_specials() doesn't work because it is
|
|
|
|
* modified when replacing a completion but the foreach function still passes the old pointer
|
|
|
|
* to ac_replace_specials, so we use a global pointer outside of ac_replace_specials and
|
|
|
|
* ac_complete_constructs. Any hints to improve this are welcome. */
|
|
|
|
static gchar *global_pattern = NULL;
|
2006-06-03 15:05:41 +00:00
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
void ac_replace_specials(gpointer key, gpointer value, gpointer user_data)
|
|
|
|
{
|
|
|
|
gchar *needle;
|
2006-05-05 16:04:15 +00:00
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
if (key == NULL || value == NULL)
|
|
|
|
return;
|
2006-06-03 15:05:41 +00:00
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
needle = g_strconcat("%", (gchar*) key, "%", NULL);
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
global_pattern = utils_str_replace(global_pattern, needle, (gchar*) value);
|
|
|
|
g_free(needle);
|
|
|
|
}
|
2006-10-08 15:52:31 +00:00
|
|
|
|
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
static gboolean ac_complete_constructs(gint idx, gint pos, const gchar *word)
|
|
|
|
{
|
|
|
|
gchar *str;
|
|
|
|
gchar *pattern;
|
|
|
|
gchar *lindent;
|
|
|
|
gchar *whitespace;
|
|
|
|
gint step, str_len;
|
2007-07-28 10:27:42 +00:00
|
|
|
gint ft_id = FILETYPE_ID(doc_list[idx].file_type);
|
2007-06-17 17:56:48 +00:00
|
|
|
GHashTable *specials;
|
|
|
|
ScintillaObject *sci = doc_list[idx].sci;
|
2006-06-03 15:05:41 +00:00
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
str = g_strdup(word);
|
|
|
|
g_strstrip(str);
|
2006-10-10 16:27:31 +00:00
|
|
|
|
2007-07-28 10:27:42 +00:00
|
|
|
pattern = ac_find_completion_by_name(filetypes[ft_id]->name, str);
|
2007-06-17 17:56:48 +00:00
|
|
|
if (pattern == NULL || pattern[0] == '\0')
|
2007-04-06 10:49:51 +00:00
|
|
|
{
|
2007-06-17 17:56:48 +00:00
|
|
|
utils_free_pointers(str, pattern, NULL); // free pattern in case it is ""
|
|
|
|
return FALSE;
|
2007-04-06 10:49:51 +00:00
|
|
|
}
|
2007-06-17 17:56:48 +00:00
|
|
|
|
|
|
|
get_indent(sci, pos, TRUE);
|
|
|
|
lindent = g_strconcat(utils_get_eol_char(idx), indent, NULL);
|
|
|
|
whitespace = get_whitespace(editor_prefs.tab_width, FALSE);
|
|
|
|
|
|
|
|
// remove the typed word, it will be added again by the used auto completion
|
|
|
|
// (not really necessary but this makes the auto completion more flexible,
|
|
|
|
// e.g. with a completion like hi=hello, so typing "hi<TAB>" will result in "hello")
|
|
|
|
str_len = strlen(str);
|
|
|
|
sci_set_selection_start(sci, pos - str_len);
|
|
|
|
sci_set_selection_end(sci, pos);
|
|
|
|
sci_replace_sel(sci, "");
|
|
|
|
pos -= str_len; // pos has changed while deleting
|
|
|
|
|
|
|
|
// replace 'special' completions
|
|
|
|
specials = g_hash_table_lookup(editor_prefs.auto_completions, "Special");
|
|
|
|
if (specials != NULL)
|
|
|
|
{
|
|
|
|
// ugly hack using global_pattern
|
|
|
|
global_pattern = pattern;
|
|
|
|
g_hash_table_foreach(specials, ac_replace_specials, NULL);
|
|
|
|
pattern = global_pattern;
|
|
|
|
}
|
|
|
|
|
|
|
|
// replace line breaks and whitespaces
|
|
|
|
pattern = utils_str_replace(pattern, "\n", "%newline%"); // to avoid endless replacing of \n
|
|
|
|
pattern = utils_str_replace(pattern, "%newline%", lindent);
|
|
|
|
|
|
|
|
pattern = utils_str_replace(pattern, "\t", "%ws%"); // to avoid endless replacing of \t
|
|
|
|
pattern = utils_str_replace(pattern, "%ws%", whitespace);
|
|
|
|
|
|
|
|
// find the %cursor% pos (has to be done after all other operations)
|
|
|
|
step = utils_strpos(pattern, "%cursor%");
|
|
|
|
if (step != -1)
|
|
|
|
pattern = utils_str_replace(pattern, "%cursor%", "");
|
|
|
|
|
|
|
|
// finally insert the text and set the cursor
|
|
|
|
SSM(sci, SCI_INSERTTEXT, pos, (sptr_t) pattern);
|
|
|
|
if (step != -1)
|
|
|
|
sci_goto_pos(sci, pos + step, TRUE);
|
|
|
|
else
|
|
|
|
sci_goto_pos(sci, pos + strlen(pattern), TRUE);
|
|
|
|
|
|
|
|
utils_free_pointers(pattern, whitespace, lindent, str, NULL);
|
|
|
|
return TRUE;
|
2007-03-27 15:48:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean at_eol(ScintillaObject *sci, gint pos)
|
|
|
|
{
|
|
|
|
gint line = sci_get_line_from_position(sci, pos);
|
2007-09-12 12:08:07 +00:00
|
|
|
gchar c;
|
|
|
|
|
|
|
|
// skip any trailing spaces
|
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
c = sci_get_char_at(sci, pos);
|
|
|
|
if (c == ' ' || c == '\t')
|
|
|
|
pos++;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
2007-01-14 17:09:17 +00:00
|
|
|
|
2007-03-27 15:48:16 +00:00
|
|
|
return (pos == sci_get_line_end_position(sci, line));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-07-28 10:27:42 +00:00
|
|
|
gboolean editor_auto_complete(gint idx, gint pos)
|
2007-03-27 15:48:16 +00:00
|
|
|
{
|
2007-06-18 13:22:15 +00:00
|
|
|
gboolean result = FALSE;
|
2007-03-27 15:48:16 +00:00
|
|
|
gint lexer, style;
|
|
|
|
ScintillaObject *sci;
|
|
|
|
|
2007-07-28 10:27:42 +00:00
|
|
|
if (! DOC_IDX_VALID(idx))
|
2007-03-27 15:48:16 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
sci = doc_list[idx].sci;
|
|
|
|
// return if we are editing an existing line (chars on right of cursor)
|
2007-09-12 12:08:07 +00:00
|
|
|
if (! editor_prefs.auto_complete_whilst_editing && ! at_eol(sci, pos))
|
2007-03-27 15:48:16 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
lexer = SSM(sci, SCI_GETLEXER, 0, 0);
|
|
|
|
style = SSM(sci, SCI_GETSTYLEAT, pos - 2, 0);
|
|
|
|
|
2007-07-28 10:27:42 +00:00
|
|
|
editor_find_current_word(sci, pos, current_word, sizeof current_word, NULL);
|
2007-03-27 15:48:16 +00:00
|
|
|
|
2007-06-18 13:22:15 +00:00
|
|
|
// prevent completion of "for "
|
|
|
|
if (! isspace(sci_get_char_at(sci, pos - 1))) // pos points to the line end char so use pos -1
|
|
|
|
{
|
|
|
|
sci_start_undo_action(sci); // needed because we insert a space separately from construct
|
2007-07-28 10:27:42 +00:00
|
|
|
result = ac_complete_constructs(idx, pos, current_word);
|
2007-06-18 13:22:15 +00:00
|
|
|
sci_end_undo_action(sci);
|
|
|
|
}
|
2007-04-06 10:49:51 +00:00
|
|
|
|
2007-03-27 15:48:16 +00:00
|
|
|
return result;
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-05-28 16:07:30 +00:00
|
|
|
void editor_show_macro_list(ScintillaObject *sci)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2006-06-24 18:43:56 +00:00
|
|
|
GString *words;
|
|
|
|
|
|
|
|
if (sci == NULL) return;
|
2006-06-29 18:22:11 +00:00
|
|
|
|
2006-11-08 11:42:05 +00:00
|
|
|
words = symbols_get_macro_list();
|
2007-03-24 12:10:43 +00:00
|
|
|
if (words == NULL) return;
|
|
|
|
|
2005-11-22 12:26:26 +00:00
|
|
|
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
|
2006-12-03 22:33:46 +00:00
|
|
|
* @param ch The character we are dealing with, currently only works with the '>' character
|
2005-11-22 12:26:26 +00:00
|
|
|
* @return True if handled, false otherwise
|
|
|
|
*/
|
2006-12-03 23:05:29 +00:00
|
|
|
static gboolean handle_xml(ScintillaObject *sci, gchar ch, gint idx)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
|
|
|
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
|
2007-05-29 16:30:54 +00:00
|
|
|
if (! editor_prefs.auto_close_xml_tags || (lexer != SCLEX_HTML && lexer != SCLEX_XML))
|
2005-11-22 12:26:26 +00:00
|
|
|
return FALSE;
|
|
|
|
|
2006-12-03 23:05:29 +00:00
|
|
|
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;
|
|
|
|
}
|
2005-11-22 12:26:26 +00:00
|
|
|
|
|
|
|
// 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)
|
2006-12-03 22:33:46 +00:00
|
|
|
return FALSE; // Smallest tag is 3 characters e.g. <p>
|
2005-11-22 12:26:26 +00:00
|
|
|
|
|
|
|
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
|
2006-12-07 16:09:45 +00:00
|
|
|
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"))
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
|
|
|
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);
|
2007-05-28 16:07:30 +00:00
|
|
|
if (utils_str_equal(str_found, "table")) editor_auto_table(sci, pos);
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
sci_end_undo_action(sci);
|
|
|
|
g_free(to_insert);
|
|
|
|
g_free(str_found);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
g_free(str_found);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-05-28 16:07:30 +00:00
|
|
|
void editor_auto_table(ScintillaObject *sci, gint pos)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
|
|
|
gchar *table;
|
2006-08-15 15:53:18 +00:00
|
|
|
gint indent_pos;
|
2005-11-22 12:26:26 +00:00
|
|
|
|
|
|
|
if (SSM(sci, SCI_GETLEXER, 0, 0) != SCLEX_HTML) return;
|
|
|
|
|
2006-12-08 18:13:22 +00:00
|
|
|
get_indent(sci, pos, TRUE);
|
2006-08-15 15:53:18 +00:00
|
|
|
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;
|
2007-01-09 16:53:27 +00:00
|
|
|
while (i <= pos && sci_get_char_at(sci, pos - i) != '<') i++;
|
2006-08-15 15:53:18 +00:00
|
|
|
// 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);
|
2005-11-22 12:26:26 +00:00
|
|
|
sci_insert_text(sci, pos, table);
|
|
|
|
g_free(table);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-10 11:47:26 +00:00
|
|
|
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
|
2007-07-04 17:08:53 +00:00
|
|
|
pos = document_find_text(idx, doc_list[idx].file_type->comment_open, 0, TRUE, FALSE, NULL);
|
2006-09-10 11:47:26 +00:00
|
|
|
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);
|
2006-10-02 15:22:29 +00:00
|
|
|
linebuf = sci_get_line(doc_list[idx].sci, line);
|
2006-09-10 11:47:26 +00:00
|
|
|
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
|
2007-07-04 17:08:53 +00:00
|
|
|
pos = document_find_text(idx, doc_list[idx].file_type->comment_close, 0, FALSE, FALSE, NULL);
|
2006-09-10 11:47:26 +00:00
|
|
|
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);
|
2006-10-02 15:22:29 +00:00
|
|
|
linebuf = sci_get_line(doc_list[idx].sci, line);
|
2006-09-10 11:47:26 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-07-05 18:19:56 +00:00
|
|
|
/* set toggle to TRUE if the caller is the toggle function, FALSE otherwise
|
|
|
|
* returns the amount of uncommented single comment lines, in case of multi line uncomment
|
|
|
|
* it returns just 1 */
|
|
|
|
gint editor_do_uncomment(gint idx, gint line, gboolean toggle)
|
2006-07-31 09:39:33 +00:00
|
|
|
{
|
2006-08-20 15:47:18 +00:00
|
|
|
gint first_line, last_line;
|
2006-07-31 09:39:33 +00:00
|
|
|
gint x, i, line_start, line_len;
|
2006-08-20 20:39:59 +00:00
|
|
|
gint sel_start, sel_end;
|
2007-07-05 18:19:56 +00:00
|
|
|
gint count = 0;
|
2006-08-20 20:39:59 +00:00
|
|
|
gsize co_len;
|
2006-09-10 11:47:26 +00:00
|
|
|
gchar sel[256], *co, *cc;
|
2006-08-20 15:47:18 +00:00
|
|
|
gboolean break_loop = FALSE, single_line = FALSE;
|
2006-07-31 09:39:33 +00:00
|
|
|
filetype *ft;
|
|
|
|
|
2007-07-05 18:19:56 +00:00
|
|
|
if (! DOC_IDX_VALID(idx) || doc_list[idx].file_type == NULL)
|
|
|
|
return 0;
|
2006-07-31 09:39:33 +00:00
|
|
|
|
2006-09-10 11:47:26 +00:00
|
|
|
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);
|
2006-08-20 15:47:18 +00:00
|
|
|
|
2006-09-10 11:47:26 +00:00
|
|
|
first_line = sci_get_line_from_position(doc_list[idx].sci, sel_start);
|
|
|
|
// Find the last line with chars selected (not EOL char)
|
2007-07-16 15:42:12 +00:00
|
|
|
last_line = sci_get_line_from_position(doc_list[idx].sci,
|
|
|
|
sel_end - utils_get_eol_char_len(idx));
|
2006-09-10 11:47:26 +00:00
|
|
|
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);
|
|
|
|
}
|
2006-07-31 09:39:33 +00:00
|
|
|
|
2006-09-10 11:47:26 +00:00
|
|
|
ft = doc_list[idx].file_type;
|
2006-07-31 09:39:33 +00:00
|
|
|
|
2006-09-10 11:47:26 +00:00
|
|
|
// detection of HTML vs PHP code, if non-PHP set filetype to XML
|
2006-07-31 09:39:33 +00:00
|
|
|
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;
|
2007-07-05 18:19:56 +00:00
|
|
|
if (co == NULL)
|
|
|
|
return 0;
|
2006-07-31 09:39:33 +00:00
|
|
|
|
2006-08-20 15:47:18 +00:00
|
|
|
co_len = strlen(co);
|
2007-07-05 18:19:56 +00:00
|
|
|
if (co_len == 0)
|
|
|
|
return 0;
|
2006-08-20 15:47:18 +00:00
|
|
|
|
2006-07-31 09:39:33 +00:00
|
|
|
SSM(doc_list[idx].sci, SCI_BEGINUNDOACTION, 0, 0);
|
|
|
|
|
|
|
|
for (i = first_line; (i <= last_line) && (! break_loop); i++)
|
|
|
|
{
|
2007-01-09 16:53:27 +00:00
|
|
|
gint buf_len;
|
|
|
|
|
2006-07-31 09:39:33 +00:00
|
|
|
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;
|
|
|
|
|
2007-01-09 16:53:27 +00:00
|
|
|
buf_len = MIN((gint)sizeof(sel) - 1, line_len - 1);
|
|
|
|
if (buf_len <= 0)
|
2007-01-11 11:59:40 +00:00
|
|
|
continue;
|
2007-01-09 16:53:27 +00:00
|
|
|
sci_get_text_range(doc_list[idx].sci, line_start, line_start + buf_len, sel);
|
|
|
|
sel[buf_len] = '\0';
|
2006-07-31 09:39:33 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2007-07-07 17:12:50 +00:00
|
|
|
gsize tm_len = strlen(GEANY_TOGGLE_MARK);
|
2006-07-31 09:39:33 +00:00
|
|
|
|
2006-08-20 15:47:18 +00:00
|
|
|
single_line = TRUE;
|
|
|
|
|
2007-07-05 18:19:56 +00:00
|
|
|
if (toggle)
|
2006-07-31 09:39:33 +00:00
|
|
|
{
|
2007-07-07 17:12:50 +00:00
|
|
|
if (strncmp(sel + x, co, co_len) != 0 ||
|
|
|
|
strncmp(sel + x + co_len, GEANY_TOGGLE_MARK, tm_len) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
co_len += tm_len;
|
2007-07-05 18:19:56 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-07-07 17:12:50 +00:00
|
|
|
if (strncmp(sel + x, co, co_len) != 0)
|
|
|
|
continue;
|
2006-07-31 09:39:33 +00:00
|
|
|
}
|
|
|
|
|
2007-07-17 08:09:15 +00:00
|
|
|
SSM(doc_list[idx].sci, SCI_SETSEL, line_start + x, line_start + x + co_len);
|
|
|
|
sci_replace_sel(doc_list[idx].sci, "");
|
2007-07-05 18:19:56 +00:00
|
|
|
count++;
|
2006-07-31 09:39:33 +00:00
|
|
|
}
|
|
|
|
// 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;
|
2006-12-15 17:09:05 +00:00
|
|
|
case SCLEX_D: style_comment = SCE_D_COMMENT; break;
|
2006-07-31 09:39:33 +00:00
|
|
|
default: style_comment = SCE_C_COMMENT;
|
|
|
|
}
|
|
|
|
if (sci_get_style_at(doc_list[idx].sci, line_start + x) == style_comment)
|
|
|
|
{
|
2006-09-10 11:47:26 +00:00
|
|
|
real_uncomment_multiline(idx);
|
2007-07-05 18:19:56 +00:00
|
|
|
count = 1;
|
2006-07-31 09:39:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// break because we are already on the last line
|
|
|
|
break_loop = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SSM(doc_list[idx].sci, SCI_ENDUNDOACTION, 0, 0);
|
2006-08-20 15:47:18 +00:00
|
|
|
|
2006-09-10 11:47:26 +00:00
|
|
|
// restore selection if there is one
|
2007-07-05 18:19:56 +00:00
|
|
|
// but don't touch the selection if caller is editor_do_comment_toggle
|
|
|
|
if (! toggle && sel_start < sel_end)
|
2006-08-20 15:47:18 +00:00
|
|
|
{
|
2006-09-10 11:47:26 +00:00
|
|
|
if (single_line)
|
|
|
|
{
|
|
|
|
sci_set_selection_start(doc_list[idx].sci, sel_start - co_len);
|
2007-07-05 18:19:56 +00:00
|
|
|
sci_set_selection_end(doc_list[idx].sci, sel_end - (count * co_len));
|
2006-09-10 11:47:26 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-07-05 18:19:56 +00:00
|
|
|
gint eol_len = utils_get_eol_char_len(idx);
|
2006-09-10 11:47:26 +00:00
|
|
|
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);
|
|
|
|
}
|
2006-08-20 15:47:18 +00:00
|
|
|
}
|
2007-07-05 18:19:56 +00:00
|
|
|
|
|
|
|
return count;
|
2006-07-31 09:39:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-05-28 16:07:30 +00:00
|
|
|
void editor_do_comment_toggle(gint idx)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2006-08-20 15:47:18 +00:00
|
|
|
gint first_line, last_line;
|
2007-07-07 17:12:50 +00:00
|
|
|
gint x, i, line_start, line_len, first_line_start;
|
|
|
|
gint sel_start, sel_end;
|
2006-09-10 11:47:26 +00:00
|
|
|
gint count_commented = 0, count_uncommented = 0;
|
|
|
|
gchar sel[256], *co, *cc;
|
2006-08-20 15:47:18 +00:00
|
|
|
gboolean break_loop = FALSE, single_line = FALSE;
|
2006-09-10 11:47:26 +00:00
|
|
|
gboolean first_line_was_comment = FALSE;
|
2007-07-07 17:12:50 +00:00
|
|
|
gsize co_len;
|
|
|
|
gsize tm_len = strlen(GEANY_TOGGLE_MARK);
|
2006-06-24 18:43:56 +00:00
|
|
|
filetype *ft;
|
|
|
|
|
2007-07-05 18:19:56 +00:00
|
|
|
if (! DOC_IDX_VALID(idx) || doc_list[idx].file_type == NULL)
|
|
|
|
return;
|
2006-06-24 18:43:56 +00:00
|
|
|
|
2006-08-20 15:47:18 +00:00
|
|
|
sel_start = sci_get_selection_start(doc_list[idx].sci);
|
|
|
|
sel_end = sci_get_selection_end(doc_list[idx].sci);
|
|
|
|
|
2006-06-24 18:43:56 +00:00
|
|
|
ft = doc_list[idx].file_type;
|
2006-04-27 18:00:00 +00:00
|
|
|
|
|
|
|
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,
|
2007-07-16 15:42:12 +00:00
|
|
|
sci_get_selection_end(doc_list[idx].sci) - utils_get_eol_char_len(idx));
|
2006-04-27 18:00:00 +00:00
|
|
|
last_line = MAX(first_line, last_line);
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2006-09-10 11:47:26 +00:00
|
|
|
// detection of HTML vs PHP code, if non-PHP set filetype to XML
|
2007-07-07 17:12:50 +00:00
|
|
|
first_line_start = sci_get_position_from_line(doc_list[idx].sci, first_line);
|
2006-04-27 18:00:00 +00:00
|
|
|
if (ft->id == GEANY_FILETYPES_PHP)
|
|
|
|
{
|
2007-07-07 17:12:50 +00:00
|
|
|
if (sci_get_style_at(doc_list[idx].sci, first_line_start) < 118 ||
|
|
|
|
sci_get_style_at(doc_list[idx].sci, first_line_start) > 127)
|
2006-04-27 18:00:00 +00:00
|
|
|
ft = filetypes[GEANY_FILETYPES_XML];
|
|
|
|
}
|
2006-04-30 15:13:13 +00:00
|
|
|
|
2006-04-27 18:00:00 +00:00
|
|
|
co = ft->comment_open;
|
|
|
|
cc = ft->comment_close;
|
2007-07-05 18:19:56 +00:00
|
|
|
if (co == NULL)
|
|
|
|
return;
|
2006-05-20 16:47:07 +00:00
|
|
|
|
2006-08-20 15:47:18 +00:00
|
|
|
co_len = strlen(co);
|
2007-07-05 18:19:56 +00:00
|
|
|
if (co_len == 0)
|
|
|
|
return;
|
2006-08-20 15:47:18 +00:00
|
|
|
|
2007-01-11 11:59:40 +00:00
|
|
|
SSM(doc_list[idx].sci, SCI_BEGINUNDOACTION, 0, 0);
|
|
|
|
|
2006-02-10 20:43:30 +00:00
|
|
|
for (i = first_line; (i <= last_line) && (! break_loop); i++)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2007-01-09 16:53:27 +00:00
|
|
|
gint buf_len;
|
|
|
|
|
2005-11-22 12:26:26 +00:00
|
|
|
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;
|
|
|
|
|
2007-01-09 16:53:27 +00:00
|
|
|
buf_len = MIN((gint)sizeof(sel) - 1, line_len - 1);
|
2007-07-07 17:12:50 +00:00
|
|
|
if (buf_len < 0)
|
2007-01-11 11:59:40 +00:00
|
|
|
continue;
|
2007-01-09 16:53:27 +00:00
|
|
|
sci_get_text_range(doc_list[idx].sci, line_start, line_start + buf_len, sel);
|
|
|
|
sel[buf_len] = '\0';
|
2005-11-22 12:26:26 +00:00
|
|
|
|
|
|
|
while (isspace(sel[x])) x++;
|
|
|
|
|
2007-07-07 17:12:50 +00:00
|
|
|
// use single line comment
|
|
|
|
if (cc == NULL || strlen(cc) == 0)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2007-07-07 17:12:50 +00:00
|
|
|
gboolean do_continue = FALSE;
|
|
|
|
single_line = TRUE;
|
|
|
|
|
|
|
|
if (strncmp(sel + x, co, co_len) == 0 &&
|
|
|
|
strncmp(sel + x + co_len, GEANY_TOGGLE_MARK, tm_len) == 0)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2007-07-07 17:12:50 +00:00
|
|
|
do_continue = TRUE;
|
|
|
|
}
|
2006-09-10 11:47:26 +00:00
|
|
|
|
2007-07-07 17:12:50 +00:00
|
|
|
if (do_continue && i == first_line)
|
|
|
|
first_line_was_comment = TRUE;
|
2006-09-10 11:47:26 +00:00
|
|
|
|
2007-07-07 17:12:50 +00:00
|
|
|
if (do_continue)
|
2006-09-10 11:47:26 +00:00
|
|
|
{
|
2007-07-07 17:12:50 +00:00
|
|
|
count_uncommented += editor_do_uncomment(idx, i, TRUE);
|
|
|
|
continue;
|
|
|
|
}
|
2006-09-10 11:47:26 +00:00
|
|
|
|
2007-07-07 17:12:50 +00:00
|
|
|
// we are still here, so the above lines were not already comments, so comment it
|
|
|
|
editor_do_comment(idx, i, TRUE, TRUE);
|
|
|
|
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:
|
2006-09-10 11:47:26 +00:00
|
|
|
{
|
2007-07-07 17:12:50 +00:00
|
|
|
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;
|
2006-09-10 11:47:26 +00:00
|
|
|
}
|
2007-07-07 17:12:50 +00:00
|
|
|
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++;
|
2006-09-10 11:47:26 +00:00
|
|
|
}
|
2007-07-07 17:12:50 +00:00
|
|
|
|
|
|
|
// break because we are already on the last line
|
|
|
|
break_loop = TRUE;
|
|
|
|
break;
|
2006-09-10 11:47:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-11 11:59:40 +00:00
|
|
|
SSM(doc_list[idx].sci, SCI_ENDUNDOACTION, 0, 0);
|
|
|
|
|
2007-07-07 17:12:50 +00:00
|
|
|
co_len += tm_len;
|
|
|
|
|
2006-09-10 11:47:26 +00:00
|
|
|
// 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
|
2006-12-08 18:13:22 +00:00
|
|
|
get_indent(doc_list[idx].sci, sel_start, TRUE);
|
2007-07-07 17:12:50 +00:00
|
|
|
if ((sel_start - first_line_start) <= (gint) strlen(indent))
|
2006-09-10 11:47:26 +00:00
|
|
|
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
|
|
|
|
{
|
2007-07-05 18:19:56 +00:00
|
|
|
gint eol_len = utils_get_eol_char_len(idx);
|
2006-09-10 11:47:26 +00:00
|
|
|
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);
|
|
|
|
}
|
2006-09-26 16:29:44 +00:00
|
|
|
else
|
2006-09-10 11:47:26 +00:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2007-07-07 17:12:50 +00:00
|
|
|
sci_set_current_position(doc_list[idx].sci, sel_start - co_len, TRUE);
|
2006-09-10 11:47:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-07-05 18:19:56 +00:00
|
|
|
/* set toggle to TRUE if the caller is the toggle function, FALSE otherwise */
|
|
|
|
void editor_do_comment(gint idx, gint line, gboolean allow_empty_lines, gboolean toggle)
|
2006-09-10 11:47:26 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2007-07-05 18:19:56 +00:00
|
|
|
if (! DOC_IDX_VALID(idx) || doc_list[idx].file_type == NULL) return;
|
2006-09-10 11:47:26 +00:00
|
|
|
|
|
|
|
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)
|
2007-07-16 15:42:12 +00:00
|
|
|
last_line = sci_get_line_from_position(doc_list[idx].sci,
|
|
|
|
sel_end - utils_get_eol_char_len(idx));
|
2006-09-10 11:47:26 +00:00
|
|
|
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;
|
2007-07-05 18:19:56 +00:00
|
|
|
if (co == NULL)
|
|
|
|
return;
|
2006-09-10 11:47:26 +00:00
|
|
|
|
|
|
|
co_len = strlen(co);
|
2007-07-05 18:19:56 +00:00
|
|
|
if (co_len == 0)
|
|
|
|
return;
|
2006-09-10 11:47:26 +00:00
|
|
|
|
|
|
|
SSM(doc_list[idx].sci, SCI_BEGINUNDOACTION, 0, 0);
|
|
|
|
|
|
|
|
for (i = first_line; (i <= last_line) && (! break_loop); i++)
|
|
|
|
{
|
2007-01-09 16:53:27 +00:00
|
|
|
gint buf_len;
|
|
|
|
|
2006-09-10 11:47:26 +00:00
|
|
|
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;
|
|
|
|
|
2007-01-09 16:53:27 +00:00
|
|
|
buf_len = MIN((gint)sizeof(sel) - 1, line_len - 1);
|
2007-07-07 17:12:50 +00:00
|
|
|
if (buf_len < 0)
|
2007-01-11 11:59:40 +00:00
|
|
|
continue;
|
2007-01-09 16:53:27 +00:00
|
|
|
sci_get_text_range(doc_list[idx].sci, line_start, line_start + buf_len, sel);
|
|
|
|
sel[buf_len] = '\0';
|
2006-09-10 11:47:26 +00:00
|
|
|
|
|
|
|
while (isspace(sel[x])) x++;
|
|
|
|
|
|
|
|
// to skip blank lines
|
2007-01-06 15:03:53 +00:00
|
|
|
if (allow_empty_lines || (x < line_len && sel[x] != '\0'))
|
2006-09-10 11:47:26 +00:00
|
|
|
{
|
|
|
|
// use single line comment
|
|
|
|
if (cc == NULL || strlen(cc) == 0)
|
|
|
|
{
|
2007-07-05 18:19:56 +00:00
|
|
|
gint start = line_start;
|
2006-08-20 15:47:18 +00:00
|
|
|
single_line = TRUE;
|
2006-09-10 11:47:26 +00:00
|
|
|
|
2006-04-27 18:00:00 +00:00
|
|
|
if (ft->comment_use_indent)
|
2007-07-05 18:19:56 +00:00
|
|
|
start = line_start + x;
|
|
|
|
|
|
|
|
if (toggle)
|
|
|
|
{
|
2007-07-07 17:12:50 +00:00
|
|
|
gchar *text = g_strconcat(co, GEANY_TOGGLE_MARK, NULL);
|
2007-07-05 18:19:56 +00:00
|
|
|
sci_insert_text(doc_list[idx].sci, start, text);
|
|
|
|
g_free(text);
|
|
|
|
}
|
2006-04-27 18:00:00 +00:00
|
|
|
else
|
2007-07-05 18:19:56 +00:00
|
|
|
sci_insert_text(doc_list[idx].sci, start, co);
|
2006-04-27 18:00:00 +00:00
|
|
|
}
|
|
|
|
// 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;
|
2006-02-10 20:43:30 +00:00
|
|
|
}
|
2006-04-27 18:00:00 +00:00
|
|
|
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;
|
2006-12-15 17:09:05 +00:00
|
|
|
case SCLEX_D: style_comment = SCE_D_COMMENT; break;
|
2006-04-27 18:00:00 +00:00
|
|
|
default: style_comment = SCE_C_COMMENT;
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
2006-04-27 18:00:00 +00:00
|
|
|
if (sci_get_style_at(doc_list[idx].sci, line_start + x) == style_comment) continue;
|
|
|
|
|
2006-09-10 11:47:26 +00:00
|
|
|
real_comment_multiline(idx, line_start, last_line);
|
2006-04-30 15:13:13 +00:00
|
|
|
|
2006-04-27 18:00:00 +00:00
|
|
|
// break because we are already on the last line
|
|
|
|
break_loop = TRUE;
|
|
|
|
break;
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SSM(doc_list[idx].sci, SCI_ENDUNDOACTION, 0, 0);
|
2006-08-20 15:47:18 +00:00
|
|
|
|
2006-09-10 11:47:26 +00:00
|
|
|
// restore selection if there is one
|
2007-07-05 18:19:56 +00:00
|
|
|
// but don't touch the selection if caller is editor_do_comment_toggle
|
|
|
|
if (! toggle && sel_start < sel_end)
|
2006-08-20 15:47:18 +00:00
|
|
|
{
|
2006-09-10 11:47:26 +00:00
|
|
|
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
|
|
|
|
{
|
2007-07-05 18:19:56 +00:00
|
|
|
gint eol_len = utils_get_eol_char_len(idx);
|
2006-09-10 11:47:26 +00:00
|
|
|
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);
|
|
|
|
}
|
2006-08-20 15:47:18 +00:00
|
|
|
}
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-05-28 16:07:30 +00:00
|
|
|
void editor_highlight_braces(ScintillaObject *sci, gint cur_pos)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2006-12-24 12:07:35 +00:00
|
|
|
gint brace_pos = cur_pos - 1;
|
|
|
|
gint end_pos;
|
|
|
|
|
2007-08-23 11:34:06 +00:00
|
|
|
if (! utils_isbrace(sci_get_char_at(sci, brace_pos), editor_prefs.brace_match_ltgt))
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2006-12-24 12:07:35 +00:00
|
|
|
brace_pos++;
|
2007-08-23 11:34:06 +00:00
|
|
|
if (! utils_isbrace(sci_get_char_at(sci, brace_pos), editor_prefs.brace_match_ltgt))
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2006-12-24 12:07:35 +00:00
|
|
|
SSM(sci, SCI_BRACEBADLIGHT, -1, 0);
|
|
|
|
return;
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
}
|
2006-12-24 12:07:35 +00:00
|
|
|
end_pos = SSM(sci, SCI_BRACEMATCH, brace_pos, 0);
|
|
|
|
|
|
|
|
if (end_pos >= 0)
|
|
|
|
SSM(sci, SCI_BRACEHIGHLIGHT, brace_pos, end_pos);
|
2005-11-22 12:26:26 +00:00
|
|
|
else
|
2006-12-24 12:07:35 +00:00
|
|
|
SSM(sci, SCI_BRACEBADLIGHT, brace_pos, 0);
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
2006-12-08 18:13:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
static gboolean is_doc_comment_char(gchar c, gint lexer)
|
|
|
|
{
|
2007-07-17 08:09:15 +00:00
|
|
|
if (c == '*' && (lexer == SCLEX_HTML || lexer == SCLEX_CPP))
|
2006-12-08 18:13:22 +00:00
|
|
|
return TRUE;
|
|
|
|
else if ((c == '*' || c == '+') && lexer == SCLEX_D)
|
|
|
|
return TRUE;
|
|
|
|
else
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void auto_multiline(ScintillaObject *sci, gint pos)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
|
|
|
gint style = SSM(sci, SCI_GETSTYLEAT, pos - 2, 0);
|
|
|
|
gint lexer = SSM(sci, SCI_GETLEXER, 0, 0);
|
2006-12-08 18:13:22 +00:00
|
|
|
gint i;
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2006-12-07 16:09:45 +00:00
|
|
|
if ((lexer == SCLEX_CPP && (style == SCE_C_COMMENT || style == SCE_C_COMMENTDOC)) ||
|
2006-12-08 18:13:22 +00:00
|
|
|
(lexer == SCLEX_HTML && style == SCE_HPHP_COMMENT) ||
|
|
|
|
(lexer == SCLEX_D && (style == SCE_D_COMMENT ||
|
|
|
|
style == SCE_D_COMMENTDOC ||
|
|
|
|
style == SCE_D_COMMENTNESTED)))
|
2006-01-09 17:56:50 +00:00
|
|
|
{
|
2006-12-08 18:13:22 +00:00
|
|
|
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;
|
2006-12-14 15:49:10 +00:00
|
|
|
gint len = strlen(previous_line);
|
2006-12-08 18:13:22 +00:00
|
|
|
|
|
|
|
// find and stop at end of multi line comment
|
2006-12-14 15:49:10 +00:00
|
|
|
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] == '/')
|
|
|
|
{
|
2007-09-25 16:44:33 +00:00
|
|
|
gint cur_line = sci_get_current_line(sci);
|
2006-12-15 11:57:48 +00:00
|
|
|
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. */
|
2007-05-29 16:30:54 +00:00
|
|
|
if (indent_len % editor_prefs.tab_width == 1)
|
2006-12-15 11:57:48 +00:00
|
|
|
SSM(sci, SCI_DELETEBACKNOTLINE, 0, 0); // remove whitespace indent
|
2006-12-14 15:49:10 +00:00
|
|
|
g_free(previous_line);
|
|
|
|
return;
|
|
|
|
}
|
2006-12-08 18:13:22 +00:00
|
|
|
// check whether we are on the second line of multi line comment
|
|
|
|
i = 0;
|
2006-12-14 15:49:10 +00:00
|
|
|
while (i < len && isspace(previous_line[i])) i++; // get to start of the line
|
2006-12-08 18:13:22 +00:00
|
|
|
|
2006-12-14 15:49:10 +00:00
|
|
|
if (i + 1 < len &&
|
|
|
|
previous_line[i] == '/' && is_doc_comment_char(previous_line[i + 1], lexer))
|
2006-12-08 18:13:22 +00:00
|
|
|
{ // we are on the second line of a multi line comment, so we have to insert white space
|
|
|
|
whitespace = " ";
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
|
2006-12-08 18:13:22 +00:00
|
|
|
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);
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2006-12-08 18:13:22 +00:00
|
|
|
g_free(previous_line);
|
|
|
|
}
|
|
|
|
}
|
2006-12-15 12:52:52 +00:00
|
|
|
|
|
|
|
|
2006-12-15 17:09:05 +00:00
|
|
|
/* 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 ||
|
2006-12-16 17:18:53 +00:00
|
|
|
style == SCE_C_COMMENTLINEDOC ||
|
2006-12-15 17:09:05 +00:00
|
|
|
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;
|
|
|
|
}
|
2007-01-13 20:16:09 +00:00
|
|
|
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;
|
|
|
|
}
|
2007-05-06 14:05:43 +00:00
|
|
|
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;
|
|
|
|
}
|
2007-06-22 17:34:31 +00:00
|
|
|
case SCLEX_FREEBASIC:
|
|
|
|
{
|
|
|
|
if (style == SCE_B_COMMENT ||
|
|
|
|
style == SCE_B_STRING)
|
|
|
|
result = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
2006-12-15 17:09:05 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-16 17:18:53 +00:00
|
|
|
#if 0
|
2007-05-28 16:07:30 +00:00
|
|
|
gboolean editor_lexer_is_c_like(gint lexer)
|
2006-12-15 12:52:52 +00:00
|
|
|
{
|
|
|
|
switch (lexer)
|
|
|
|
{
|
|
|
|
case SCLEX_CPP:
|
|
|
|
case SCLEX_D:
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
2006-12-16 17:18:53 +00:00
|
|
|
#endif
|
|
|
|
|
2006-12-15 12:52:52 +00:00
|
|
|
|
2006-12-16 17:18:53 +00:00
|
|
|
// Returns: -1 if lexer doesn't support type keywords
|
2007-05-28 16:07:30 +00:00
|
|
|
gint editor_lexer_get_type_keyword_idx(gint lexer)
|
2006-12-16 17:18:53 +00:00
|
|
|
{
|
|
|
|
switch (lexer)
|
|
|
|
{
|
|
|
|
case SCLEX_CPP:
|
|
|
|
case SCLEX_D:
|
|
|
|
return 3;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2007-01-06 15:03:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
// inserts a three-line comment at one line above current cursor position
|
2007-05-28 16:07:30 +00:00
|
|
|
void editor_insert_multiline_comment(gint idx)
|
2007-01-06 15:03:53 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2007-06-12 15:16:17 +00:00
|
|
|
// use the indent on the current line but only when comment indentation is used
|
2007-01-06 15:03:53 +00:00
|
|
|
// and we don't have multi line comment characters
|
2007-06-12 15:16:17 +00:00
|
|
|
if (doc_list[idx].auto_indent && ! have_multiline_comment &&
|
2007-01-06 15:03:53 +00:00
|
|
|
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);
|
|
|
|
|
2007-07-05 18:19:56 +00:00
|
|
|
editor_do_comment(idx, -1, TRUE, FALSE);
|
2007-01-06 15:03:53 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2007-01-27 18:45:47 +00:00
|
|
|
sci_set_current_position(doc_list[idx].sci, pos, TRUE);
|
2007-01-06 15:03:53 +00:00
|
|
|
// reset the selection
|
|
|
|
sci_set_anchor(doc_list[idx].sci, pos);
|
|
|
|
}
|
2007-01-31 15:53:11 +00:00
|
|
|
|
|
|
|
|
2007-07-18 12:16:07 +00:00
|
|
|
/* Note: If the editor is pending a redraw, set document::scroll_percent instead.
|
|
|
|
* Scroll the view to make line appear at percent_of_view.
|
2007-01-31 15:53:11 +00:00
|
|
|
* line can be -1 to use the current position. */
|
2007-07-17 16:27:49 +00:00
|
|
|
void editor_scroll_to_line(ScintillaObject *sci, gint line, gfloat percent_of_view)
|
2007-01-31 15:53:11 +00:00
|
|
|
{
|
|
|
|
gint vis1, los, delta;
|
|
|
|
GtkWidget *wid = GTK_WIDGET(sci);
|
|
|
|
|
|
|
|
if (! wid->window || ! gdk_window_is_viewable(wid->window))
|
2007-02-08 16:22:23 +00:00
|
|
|
return; // prevent gdk_window_scroll warning
|
2007-01-31 15:53:11 +00:00
|
|
|
|
|
|
|
if (line == -1)
|
2007-09-25 16:44:33 +00:00
|
|
|
line = sci_get_current_line(sci);
|
2007-01-31 15:53:11 +00:00
|
|
|
|
|
|
|
// 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);
|
2007-07-12 17:16:44 +00:00
|
|
|
sci_scroll_caret(sci); // needed for horizontal scrolling
|
2007-01-31 15:53:11 +00:00
|
|
|
}
|
|
|
|
|
2007-03-22 15:51:39 +00:00
|
|
|
|
2007-05-28 16:07:30 +00:00
|
|
|
void editor_insert_alternative_whitespace(ScintillaObject *sci)
|
2007-03-25 20:51:45 +00:00
|
|
|
{
|
|
|
|
// creates and inserts one tabulator sign or whitespace of the amount of the tab width
|
2007-05-29 16:30:54 +00:00
|
|
|
gchar *text = get_whitespace(editor_prefs.tab_width, TRUE);
|
2007-03-25 20:51:45 +00:00
|
|
|
sci_add_text(sci, text);
|
|
|
|
g_free(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-05-28 16:07:30 +00:00
|
|
|
void editor_select_word(ScintillaObject *sci)
|
2007-03-22 15:51:39 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-07-07 15:12:13 +00:00
|
|
|
SSM(sci, SCI_SETSEL, start, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-24 11:25:41 +00:00
|
|
|
/* extra_line is for selecting the cursor line or anchor line at the bottom of a selection,
|
|
|
|
* when those lines have no selection. */
|
|
|
|
void editor_select_lines(ScintillaObject *sci, gboolean extra_line)
|
2007-07-07 15:12:13 +00:00
|
|
|
{
|
2007-09-22 11:11:24 +00:00
|
|
|
gint start, end, line;
|
2007-07-07 15:12:13 +00:00
|
|
|
|
|
|
|
g_return_if_fail(sci != NULL);
|
|
|
|
|
2007-08-10 11:29:37 +00:00
|
|
|
start = sci_get_selection_start(sci);
|
|
|
|
end = sci_get_selection_end(sci);
|
2007-07-07 15:12:13 +00:00
|
|
|
|
2007-09-24 11:25:41 +00:00
|
|
|
// check if whole lines are already selected
|
|
|
|
if (! extra_line && start != end &&
|
2007-09-22 11:11:24 +00:00
|
|
|
sci_get_col_from_position(sci, start) == 0 &&
|
|
|
|
sci_get_col_from_position(sci, end) == 0)
|
2007-09-24 11:25:41 +00:00
|
|
|
return;
|
2007-09-22 11:11:24 +00:00
|
|
|
|
|
|
|
line = sci_get_line_from_position(sci, start);
|
|
|
|
start = sci_get_position_from_line(sci, line);
|
|
|
|
|
|
|
|
line = sci_get_line_from_position(sci, end);
|
|
|
|
end = sci_get_position_from_line(sci, line + 1);
|
|
|
|
|
2007-03-22 15:51:39 +00:00
|
|
|
SSM(sci, SCI_SETSEL, start, end);
|
|
|
|
}
|
2007-03-27 15:48:16 +00:00
|
|
|
|
|
|
|
|
2007-07-06 10:16:51 +00:00
|
|
|
/* find the start or end of a paragraph by searching all lines in direction (UP or DOWN)
|
|
|
|
* starting at the given line and return the found line or return -1 if called on an empty line */
|
|
|
|
static gint find_paragraph_stop(ScintillaObject *sci, gint line, gint direction)
|
|
|
|
{
|
|
|
|
gboolean found_end = FALSE;
|
|
|
|
gint step;
|
|
|
|
gchar *line_buf, *x;
|
|
|
|
|
|
|
|
// first check current line and return -1 if it is empty to skip creating of a selection
|
|
|
|
line_buf = x = sci_get_line(sci, line);
|
|
|
|
while (isspace(*x))
|
|
|
|
x++;
|
|
|
|
if (*x == '\0')
|
|
|
|
{
|
|
|
|
g_free(line_buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (direction == UP)
|
|
|
|
step = -1;
|
|
|
|
else
|
|
|
|
step = 1;
|
|
|
|
|
|
|
|
while (! found_end)
|
|
|
|
{
|
|
|
|
line += step;
|
|
|
|
|
|
|
|
// sci_get_line checks for sanity of the given line, sci_get_line always return a string
|
|
|
|
// containing at least '\0' so no need to check for NULL
|
|
|
|
line_buf = x = sci_get_line(sci, line);
|
|
|
|
|
|
|
|
// check whether after skipping all whitespace we are at end of line and if so, assume
|
|
|
|
// this line as end of paragraph
|
|
|
|
while (isspace(*x))
|
|
|
|
x++;
|
|
|
|
if (*x == '\0')
|
|
|
|
{
|
|
|
|
found_end = TRUE;
|
|
|
|
if (line == -1)
|
|
|
|
// called on the first line but there is no previous line so return line 0
|
|
|
|
line = 0;
|
|
|
|
}
|
|
|
|
g_free(line_buf);
|
|
|
|
}
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void editor_select_paragraph(ScintillaObject *sci)
|
|
|
|
{
|
|
|
|
gint pos_start, pos_end, line_start, line_found;
|
|
|
|
|
|
|
|
g_return_if_fail(sci != NULL);
|
|
|
|
|
|
|
|
line_start = SSM(sci, SCI_LINEFROMPOSITION, SSM(sci, SCI_GETCURRENTPOS, 0, 0), 0);
|
|
|
|
|
|
|
|
line_found = find_paragraph_stop(sci, line_start, UP);
|
|
|
|
if (line_found == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// find_paragraph_stop returns the emtpy line(previous to the real start of the paragraph),
|
|
|
|
// so use the next line for selection start
|
|
|
|
if (line_found > 0)
|
|
|
|
line_found++;
|
|
|
|
|
|
|
|
pos_start = SSM(sci, SCI_POSITIONFROMLINE, line_found, 0);
|
|
|
|
|
|
|
|
line_found = find_paragraph_stop(sci, line_start, DOWN);
|
|
|
|
pos_end = SSM(sci, SCI_POSITIONFROMLINE, line_found, 0);
|
|
|
|
|
|
|
|
SSM(sci, SCI_SETSEL, pos_start, pos_end);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-07-16 15:42:12 +00:00
|
|
|
// simple auto indentation to indent the current line with the same indent as the previous one
|
|
|
|
void editor_auto_line_indentation(gint idx, gint pos)
|
|
|
|
{
|
|
|
|
gint i, first_line, last_line;
|
2007-07-17 08:09:15 +00:00
|
|
|
gint first_sel_start, first_sel_end, sel_start = 0, sel_end = 0;
|
2007-07-16 15:42:12 +00:00
|
|
|
|
|
|
|
g_return_if_fail(DOC_IDX_VALID(idx));
|
|
|
|
|
|
|
|
first_sel_start = sci_get_selection_start(doc_list[idx].sci);
|
|
|
|
first_sel_end = sci_get_selection_end(doc_list[idx].sci);
|
|
|
|
|
|
|
|
first_line = sci_get_line_from_position(doc_list[idx].sci, first_sel_start);
|
|
|
|
// Find the last line with chars selected (not EOL char)
|
|
|
|
last_line = sci_get_line_from_position(doc_list[idx].sci,
|
|
|
|
first_sel_end - utils_get_eol_char_len(idx));
|
|
|
|
last_line = MAX(first_line, last_line);
|
|
|
|
|
|
|
|
if (pos == -1)
|
|
|
|
pos = first_sel_start;
|
|
|
|
|
|
|
|
// get previous line and use it for get_indent to use that line
|
|
|
|
// (otherwise it would fail on a line only containing "{" in advanced indentation mode)
|
|
|
|
get_indent(doc_list[idx].sci,
|
|
|
|
sci_get_position_from_line(doc_list[idx].sci, first_line - 1), TRUE);
|
|
|
|
SSM(doc_list[idx].sci, SCI_BEGINUNDOACTION, 0, 0);
|
|
|
|
|
|
|
|
for (i = first_line; i <= last_line; i++)
|
|
|
|
{
|
|
|
|
// skip the first line or if the indentation of the previous and current line are equal
|
|
|
|
if (i == 0 ||
|
|
|
|
SSM(doc_list[idx].sci, SCI_GETLINEINDENTATION, i - 1, 0) ==
|
|
|
|
SSM(doc_list[idx].sci, SCI_GETLINEINDENTATION, i, 0))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sel_start = SSM(doc_list[idx].sci, SCI_POSITIONFROMLINE, i, 0);
|
|
|
|
sel_end = SSM(doc_list[idx].sci, SCI_GETLINEINDENTPOSITION, i, 0);
|
|
|
|
if (sel_start < sel_end)
|
|
|
|
{
|
|
|
|
SSM(doc_list[idx].sci, SCI_SETSEL, sel_start, sel_end);
|
2007-07-17 08:09:15 +00:00
|
|
|
sci_replace_sel(doc_list[idx].sci, "");
|
2007-07-16 15:42:12 +00:00
|
|
|
}
|
|
|
|
sci_insert_text(doc_list[idx].sci, sel_start, indent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set cursor position if there was no selection
|
|
|
|
/// TODO implement selection handling if there was a selection
|
|
|
|
if (first_sel_start == first_sel_end)
|
|
|
|
sci_set_current_position(doc_list[idx].sci,
|
|
|
|
pos - (sel_end - sel_start) + strlen(indent), FALSE);
|
|
|
|
|
|
|
|
SSM(doc_list[idx].sci, SCI_ENDUNDOACTION, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// increase / decrease current line or selection by one space
|
|
|
|
void editor_indentation_by_one_space(gint idx, gint pos, gboolean decrease)
|
|
|
|
{
|
|
|
|
gint i, first_line, last_line, line_start, indentation_end, count = 0;
|
|
|
|
gint sel_start, sel_end, first_line_offset = 0;
|
|
|
|
|
|
|
|
g_return_if_fail(DOC_IDX_VALID(idx));
|
|
|
|
|
|
|
|
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 - utils_get_eol_char_len(idx));
|
|
|
|
last_line = MAX(first_line, last_line);
|
|
|
|
|
|
|
|
if (pos == -1)
|
|
|
|
pos = sel_start;
|
|
|
|
|
|
|
|
SSM(doc_list[idx].sci, SCI_BEGINUNDOACTION, 0, 0);
|
|
|
|
|
|
|
|
for (i = first_line; i <= last_line; i++)
|
|
|
|
{
|
|
|
|
indentation_end = SSM(doc_list[idx].sci, SCI_GETLINEINDENTPOSITION, i, 0);
|
|
|
|
if (decrease)
|
|
|
|
{
|
|
|
|
line_start = SSM(doc_list[idx].sci, SCI_POSITIONFROMLINE, i, 0);
|
|
|
|
// searching backwards for a space to remove
|
|
|
|
while (sci_get_char_at(doc_list[idx].sci, indentation_end) != ' ' &&
|
|
|
|
indentation_end > line_start)
|
|
|
|
indentation_end--;
|
|
|
|
|
|
|
|
if (sci_get_char_at(doc_list[idx].sci, indentation_end) == ' ')
|
|
|
|
{
|
2007-07-17 08:09:15 +00:00
|
|
|
SSM(doc_list[idx].sci, SCI_SETSEL, indentation_end, indentation_end + 1);
|
|
|
|
sci_replace_sel(doc_list[idx].sci, "");
|
2007-07-16 15:42:12 +00:00
|
|
|
count--;
|
|
|
|
if (i == first_line)
|
|
|
|
first_line_offset = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sci_insert_text(doc_list[idx].sci, indentation_end, " ");
|
|
|
|
count++;
|
|
|
|
if (i == first_line)
|
|
|
|
first_line_offset = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set cursor position
|
|
|
|
if (sel_start < sel_end)
|
|
|
|
{
|
2007-07-17 08:09:15 +00:00
|
|
|
gint start = sel_start + first_line_offset;
|
|
|
|
if (first_line_offset < 0)
|
|
|
|
start = MAX(sel_start + first_line_offset,
|
|
|
|
SSM(doc_list[idx].sci, SCI_POSITIONFROMLINE, first_line, 0));
|
|
|
|
|
|
|
|
sci_set_selection_start(doc_list[idx].sci, start);
|
2007-07-16 15:42:12 +00:00
|
|
|
sci_set_selection_end(doc_list[idx].sci, sel_end + count);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sci_set_current_position(doc_list[idx].sci, pos + count, FALSE);
|
|
|
|
|
|
|
|
SSM(doc_list[idx].sci, SCI_ENDUNDOACTION, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-06-17 17:56:48 +00:00
|
|
|
void editor_finalize()
|
|
|
|
{
|
|
|
|
g_hash_table_destroy(editor_prefs.auto_completions);
|
|
|
|
|
|
|
|
scintilla_release_resources();
|
|
|
|
}
|