ide-source-view.c 244 KB
Newer Older
1 2
/* ide-source-view.c
 *
3
 * Copyright 2015 Christian Hergert <christian@hergert.me>
4
 *
5 6 7 8
 * 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 3 of the License, or
 * (at your option) any later version.
9
 *
10 11 12 13
 * 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.
14 15 16 17 18 19 20
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#define G_LOG_DOMAIN "ide-source-view"

21 22
#include "config.h"

23
#include <cairo-gobject.h>
24
#include <dazzle.h>
25
#include <glib/gi18n.h>
26
#include <stdlib.h>
27

28
#include "ide-context.h"
29 30
#include "ide-debug.h"
#include "ide-enums.h"
31 32 33 34

#include "application/ide-application.h"
#include "buffers/ide-buffer-manager.h"
#include "buffers/ide-buffer.h"
35
#include "buffers/ide-buffer-private.h"
36 37
#include "completion/ide-completion.h"
#include "completion/ide-completion-private.h"
38 39 40 41 42 43 44 45
#include "diagnostics/ide-diagnostic.h"
#include "diagnostics/ide-fixit.h"
#include "diagnostics/ide-source-location.h"
#include "diagnostics/ide-source-range.h"
#include "files/ide-file-settings.h"
#include "files/ide-file.h"
#include "plugins/ide-extension-adapter.h"
#include "plugins/ide-extension-set-adapter.h"
46
#include "rename/ide-rename-provider.h"
47 48 49 50
#include "snippets/ide-snippet-chunk.h"
#include "snippets/ide-snippet-context.h"
#include "snippets/ide-snippet-private.h"
#include "snippets/ide-snippet.h"
51
#include "sourceview/ide-cursor.h"
52
#include "sourceview/ide-indenter.h"
53
#include "sourceview/ide-omni-gutter-renderer.h"
54
#include "sourceview/ide-omni-gutter-renderer-private.h"
55
#include "sourceview/ide-source-iter.h"
56 57 58
#include "sourceview/ide-source-view-capture.h"
#include "sourceview/ide-source-view-mode.h"
#include "sourceview/ide-source-view-movements.h"
59
#include "sourceview/ide-source-view-private.h"
60 61 62
#include "sourceview/ide-source-view.h"
#include "sourceview/ide-text-util.h"
#include "symbols/ide-symbol.h"
63
#include "symbols/ide-symbol-resolver.h"
64 65 66
#include "util/ide-gtk.h"
#include "vcs/ide-vcs.h"
#include "workbench/ide-workbench-private.h"
67
#include "threading/ide-task.h"
68
#include "util/ide-glib.h"
69

70 71
#define INCLUDE_STATEMENTS "^#include[\\s]+[\\\"\\<][^\\s\\\"\\\'\\<\\>[:cntrl:]]+[\\\"\\>]"

72
#define DEFAULT_FONT_DESC "Monospace 11"
73 74
#define ANIMATION_X_GROW 50
#define ANIMATION_Y_GROW 30
75 76
#define SMALL_SCROLL_DURATION_MSEC 100
#define LARGE_SCROLL_DURATION_MSEC 250
77
#define FIXIT_LABEL_LEN_MAX 30
78
#define SCROLL_REPLAY_DELAY 1000
79
#define DEFAULT_OVERSCROLL_NUM_LINES 1
80 81
#define TAG_DEFINITION "action::hover-definition"
#define DEFINITION_HIGHLIGHT_MODIFIER GDK_CONTROL_MASK
82

83 84
#define ALL_ACCELS_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK)

85 86 87 88 89
#define _GDK_RECTANGLE_X2(rect) dzl_cairo_rectangle_x2(rect)
#define _GDK_RECTANGLE_Y2(rect) dzl_cairo_rectangle_y2(rect)
#define _GDK_RECTANGLE_CONTAINS(rect,other) dzl_cairo_rectangle_contains_rectangle(rect,other)
#define _GDK_RECTANGLE_CENTER_X(rect) dzl_cairo_rectangle_center(rect)
#define _GDK_RECTANGLE_CENTER_Y(rect) dzl_cairo_rectangle_middle(rect)
90 91 92
#define TRACE_RECTANGLE(name, rect) \
  IDE_TRACE_MSG ("%s = Rectangle(x=%d, y=%d, width=%d, height=%d)", \
                 name, (rect)->x, (rect)->y, (rect)->width, (rect)->height)
93

94 95 96 97 98
#define SCROLL_X(align) \
  (((align) == IDE_SOURCE_SCROLL_BOTH) || ((align) == IDE_SOURCE_SCROLL_X))
#define SCROLL_Y(align) \
  (((align) == IDE_SOURCE_SCROLL_BOTH) || ((align) == IDE_SOURCE_SCROLL_Y))

99 100
typedef struct
{
101 102 103
  IdeBuffer                   *buffer;
  GtkCssProvider              *css_provider;
  PangoFontDescription        *font_desc;
104
  IdeExtensionAdapter         *indenter_adapter;
105
  IdeSourceViewCapture        *capture;
106
  gchar                       *display_name;
107
  IdeSourceViewMode           *mode;
108
  GtkTextMark                 *scroll_mark;
109
  GQueue                      *selections;
110
  GQueue                      *snippets;
111 112
  DzlAnimation                *hadj_animation;
  DzlAnimation                *vadj_animation;
113
  IdeOmniGutterRenderer       *omni_renderer;
114

115
  IdeCompletion               *completion;
116

117 118
  DzlBindingGroup             *file_setting_bindings;
  DzlSignalGroup              *buffer_signals;
119

120 121
  guint                        change_sequence;

122
  guint                        target_line_column;
123
  GString                     *command_str;
124
  gunichar                     command;
125
  gunichar                     modifier;
126
  gunichar                     search_char;
127
  gint                         count;
128 129
  gunichar                     inner_left;
  gunichar                     inner_right;
130

131
  guint                        scroll_offset;
132 133
  gint                         cached_char_height;
  gint                         cached_char_width;
134

135
  guint                        saved_line;
136
  guint                        saved_line_column;
137
  guint                        saved_selection_line;
138
  guint                        saved_selection_line_column;
139

140
  GdkRGBA                      snippet_area_background_rgba;
141

142
  guint                        font_scale;
143

144 145
  gint                         overscroll_num_lines;

146 147 148
  guint                        delay_size_allocate_chainup;
  GtkAllocation                delay_size_allocation;

149
  IdeSourceLocation           *definition_src_location;
150 151
  GtkTextMark                 *definition_highlight_start_mark;
  GtkTextMark                 *definition_highlight_end_mark;
152

153 154
  GRegex                      *include_regex;

155 156
  IdeCursor                   *cursor;

157 158
  guint                        in_key_press;

159
  guint                        auto_indent : 1;
160
  guint                        completion_blocked : 1;
161
  guint                        highlight_current_line : 1;
162
  guint                        in_replay_macro : 1;
163
  guint                        insert_mark_cleared : 1;
164 165
  guint                        insert_matching_brace : 1;
  guint                        overwrite_braces : 1;
166
  guint                        recording_macro : 1;
167
  guint                        scrolling_to_scroll_mark : 1;
168 169
  guint                        show_grid_lines : 1;
  guint                        snippet_completion : 1;
170
  guint                        waiting_for_capture : 1;
171
  guint                        waiting_for_symbol : 1;
172 173
} IdeSourceViewPrivate;

174 175 176
typedef struct
{
  IdeSourceView    *self;
177 178
  GtkTextMark      *word_start_mark;
  GtkTextMark      *word_end_mark;
179 180
} DefinitionHighlightData;

181 182 183 184 185 186
typedef struct
{
  GPtrArray         *resolvers;
  IdeSourceLocation *location;
} FindReferencesTaskData;

187
G_DEFINE_TYPE_WITH_PRIVATE (IdeSourceView, ide_source_view, GTK_SOURCE_TYPE_VIEW)
188
DZL_DEFINE_COUNTER (instances, "IdeSourceView", "Instances", "Number of IdeSourceView instances")
189 190 191

enum {
  PROP_0,
192
  PROP_COMPLETION_N_ROWS,
193
  PROP_COUNT,
194
  PROP_FILE_SETTINGS,
195 196
  PROP_FONT_NAME,
  PROP_FONT_DESC,
197
  PROP_INDENTER,
198
  PROP_INDENT_STYLE,
199
  PROP_INSERT_MATCHING_BRACE,
200
  PROP_MODE_DISPLAY_NAME,
201
  PROP_OVERWRITE_BRACES,
202
  PROP_SCROLL_OFFSET,
203
  PROP_SHOW_GRID_LINES,
Christian Hergert's avatar
Christian Hergert committed
204
  PROP_SHOW_LINE_CHANGES,
205
  PROP_SHOW_LINE_DIAGNOSTICS,
206
  PROP_OVERSCROLL,
207 208 209 210 211
  LAST_PROP,

  /* These are overridden */
  PROP_AUTO_INDENT,
  PROP_HIGHLIGHT_CURRENT_LINE,
212 213
  PROP_OVERWRITE,
  PROP_SHOW_LINE_NUMBERS,
214 215
};

216
enum {
217
  ACTION,
218
  ADD_CURSOR,
219
  APPEND_TO_COUNT,
220
  AUTO_INDENT,
221
  BEGIN_MACRO,
222
  BEGIN_RENAME,
223
  BEGIN_USER_ACTION,
224
  CAPTURE_MODIFIER,
225
  CLEAR_COUNT,
226
  CLEAR_MODIFIER,
227
  CLEAR_SEARCH,
228
  CLEAR_SELECTION,
229
  CLEAR_SNIPPETS,
230
  CYCLE_COMPLETION,
231
  DOCUMENTATION_REQUESTED,
232
  DECREASE_FONT_SIZE,
233
  DELETE_SELECTION,
234
  DRAW_BUBBLES,
235
  DUPLICATE_ENTIRE_LINE,
236
  END_MACRO,
237
  END_USER_ACTION,
238
  FOCUS_LOCATION,
239
  FORMAT_SELECTION,
240
  FIND_REFERENCES,
241
  GOTO_DEFINITION,
242
  HIDE_COMPLETION,
243
  INCREASE_FONT_SIZE,
244
  INDENT_SELECTION,
245
  INSERT_AT_CURSOR_AND_INDENT,
246
  INSERT_MODIFIER,
247
  JUMP,
248
  MOVEMENT,
249
  MOVE_ERROR,
250
  MOVE_SEARCH,
251
  PASTE_CLIPBOARD_EXTENDED,
252
  POP_SELECTION,
253
  POP_SNIPPET,
254
  PUSH_SELECTION,
255
  PUSH_SNIPPET,
256
  REBUILD_HIGHLIGHT,
257
  REINDENT,
258
  REMOVE_CURSORS,
259
  REPLAY_MACRO,
260
  REQUEST_DOCUMENTATION,
261
  RESET,
262
  RESET_FONT_SIZE,
263
  RESTORE_INSERT_MARK,
264
  SAVE_COMMAND,
265
  SAVE_INSERT_MARK,
266
  SAVE_SEARCH_CHAR,
267
  SELECT_INNER,
268
  SELECT_TAG,
269
  SELECTION_THEATRIC,
270
  SET_MODE,
271
  SET_OVERWRITE,
272
  SET_SEARCH_TEXT,
273
  SORT,
274
  SWAP_SELECTION_BOUNDS,
275 276 277
  LAST_SIGNAL
};

278 279 280 281 282 283 284 285 286 287 288 289
enum {
  FONT_SCALE_XX_SMALL,
  FONT_SCALE_X_SMALL,
  FONT_SCALE_SMALL,
  FONT_SCALE_NORMAL,
  FONT_SCALE_LARGE,
  FONT_SCALE_X_LARGE,
  FONT_SCALE_XX_LARGE,
  FONT_SCALE_XXX_LARGE,
  LAST_FONT_SCALE
};

290 291 292
static GParamSpec *properties [LAST_PROP];
static guint       signals [LAST_SIGNAL];
static gdouble     fontScale [LAST_FONT_SCALE] = {
293 294 295 296
  0.57870, 0.69444, 0.83333,
  1.0,
  1.2, 1.44, 1.728, 2.48832,
};
297

298 299 300 301 302
static void ide_source_view_real_save_insert_mark    (IdeSourceView         *self);
static void ide_source_view_real_restore_insert_mark (IdeSourceView         *self);
static void ide_source_view_real_set_mode            (IdeSourceView         *self,
                                                      const gchar           *name,
                                                      IdeSourceViewModeType  type);
303
static void ide_source_view_save_column              (IdeSourceView         *self);
304 305 306 307
static void ide_source_view_maybe_overwrite          (IdeSourceView         *self,
                                                      GtkTextIter           *iter,
                                                      const gchar           *text,
                                                      gint                   len);
308

309 310 311
static void
find_references_task_data_free (FindReferencesTaskData *data)
{
312 313
  dzl_clear_pointer (&data->resolvers, g_ptr_array_unref);
  dzl_clear_pointer (&data->location, ide_source_location_unref);
314 315 316
  g_slice_free (FindReferencesTaskData, data);
}

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
static void
block_interactive (IdeSourceView *self)
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);

  g_assert (IDE_IS_SOURCE_VIEW (self));

  ide_completion_block_interactive (priv->completion);
}

static void
unblock_interactive (IdeSourceView *self)
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);

  g_assert (IDE_IS_SOURCE_VIEW (self));

  ide_completion_unblock_interactive (priv->completion);
}

337 338 339 340 341 342 343
static void
find_references_task_get_extension (IdeExtensionSetAdapter *set,
                                    PeasPluginInfo         *plugin_info,
                                    PeasExtension          *extension,
                                    gpointer                user_data)
{
  FindReferencesTaskData *data = user_data;
344 345 346 347
  IdeSymbolResolver *resolver = (IdeSymbolResolver *)extension;

  g_assert (data != NULL);
  g_assert (IDE_IS_SYMBOL_RESOLVER (resolver));
348

349
  g_ptr_array_add (data->resolvers, g_object_ref (resolver));
350 351
}

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
static void
definition_highlight_data_free (DefinitionHighlightData *data)
{
  if (data != NULL)
    {
      GtkTextBuffer *buffer;

      buffer = gtk_text_mark_get_buffer (data->word_start_mark);

      gtk_text_buffer_delete_mark (buffer, data->word_start_mark);
      gtk_text_buffer_delete_mark (buffer, data->word_end_mark);

      g_clear_object (&data->self);
      g_clear_object (&data->word_start_mark);
      g_clear_object (&data->word_end_mark);

      g_slice_free (DefinitionHighlightData, data);
    }
}

G_DEFINE_AUTOPTR_CLEANUP_FUNC (DefinitionHighlightData, definition_highlight_data_free)
373

374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
static gboolean
ide_source_view_can_animate (IdeSourceView *self)
{
  GtkSettings *settings;
  GdkScreen *screen;
  gboolean can_animate = FALSE;

  g_assert (IDE_IS_SOURCE_VIEW (self));

  screen = gtk_widget_get_screen (GTK_WIDGET (self));
  settings = gtk_settings_get_for_screen (screen);

  g_object_get (settings, "gtk-enable-animations", &can_animate, NULL);

  return can_animate;
}

391 392
void
_ide_source_view_set_count (IdeSourceView *self,
393
                            gint           count)
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);

  g_return_if_fail (IDE_IS_SOURCE_VIEW (self));

  priv->count = count;
}

void
_ide_source_view_set_modifier (IdeSourceView *self,
                               gunichar       modifier)
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);

  g_return_if_fail (IDE_IS_SOURCE_VIEW (self));

  priv->modifier = modifier;
411 412 413

  if (priv->recording_macro && !priv->in_replay_macro)
    ide_source_view_capture_record_modifier (priv->capture, modifier);
414 415
}

416 417 418 419 420 421 422 423 424 425 426 427 428
static IdeIndenter *
ide_source_view_get_indenter (IdeSourceView *self)
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);

  g_assert (IDE_IS_SOURCE_VIEW (self));

  if (priv->indenter_adapter != NULL)
    return ide_extension_adapter_get_extension (priv->indenter_adapter);

  return NULL;
}

429 430 431 432 433 434 435
static void
ide_source_view_block_handlers (IdeSourceView *self)
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);

  g_assert (IDE_IS_SOURCE_VIEW (self));

436
  dzl_signal_group_block (priv->buffer_signals);
437 438 439 440 441 442 443 444 445
}

static void
ide_source_view_unblock_handlers (IdeSourceView *self)
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);

  g_assert (IDE_IS_SOURCE_VIEW (self));

446
  dzl_signal_group_unblock (priv->buffer_signals);
447 448 449 450 451 452 453 454 455 456 457
}

static void
get_rect_for_iters (GtkTextView       *text_view,
                    const GtkTextIter *iter1,
                    const GtkTextIter *iter2,
                    GdkRectangle      *rect,
                    GtkTextWindowType  window_type)
{
  GdkRectangle area;
  GdkRectangle tmp;
458 459
  GtkTextIter begin;
  GtkTextIter end;
460 461 462
  GtkTextIter iter;

  g_assert (GTK_IS_TEXT_VIEW (text_view));
463 464 465 466 467
  g_assert (iter1 != NULL);
  g_assert (iter2 != NULL);
  g_assert (rect != NULL);
  g_assert (gtk_text_iter_get_buffer (iter1) == gtk_text_iter_get_buffer (iter2));
  g_assert (gtk_text_view_get_buffer (text_view) == gtk_text_iter_get_buffer (iter1));
468

469 470
  begin = *iter1;
  end = *iter2;
471

472
  if (gtk_text_iter_equal (&begin, &end))
473 474 475 476
    {
      gtk_text_view_get_iter_location (text_view, &begin, &area);
      goto finish;
    }
477

478 479
  gtk_text_iter_order (&begin, &end);

480 481 482 483 484 485 486
  if (gtk_text_iter_get_line (&begin) == gtk_text_iter_get_line (&end))
    {
      gtk_text_view_get_iter_location (text_view, &begin, &area);
      gtk_text_view_get_iter_location (text_view, &end, &tmp);
      gdk_rectangle_union (&area, &tmp, &area);
      goto finish;
    }
487

488
  gtk_text_view_get_iter_location (text_view, &begin, &area);
489 490

  iter = begin;
491 492 493

  do
    {
494 495 496 497
      /* skip trailing newline */
      if ((gtk_text_iter_starts_line (&iter) && gtk_text_iter_equal (&iter, &end)))
        break;

498 499 500 501 502 503 504 505 506 507
      gtk_text_view_get_iter_location (text_view, &iter, &tmp);
      gdk_rectangle_union (&area, &tmp, &area);

      gtk_text_iter_forward_to_line_end (&iter);
      gtk_text_view_get_iter_location (text_view, &iter, &tmp);
      gdk_rectangle_union (&area, &tmp, &area);

      if (!gtk_text_iter_forward_char (&iter))
        break;
    }
508
  while (gtk_text_iter_compare (&iter, &end) <= 0);
509

510
finish:
511 512 513 514 515 516
  gtk_text_view_buffer_to_window_coords (text_view, window_type, area.x, area.y, &area.x, &area.y);

  *rect = area;
}

static void
517 518 519
animate_expand (IdeSourceView     *self,
                const GtkTextIter *begin,
                const GtkTextIter *end)
520
{
521
  DzlBoxTheatric *theatric;
522 523 524 525
  GtkAllocation alloc;
  GdkRectangle rect = { 0 };

  g_assert (IDE_IS_SOURCE_VIEW (self));
526 527
  g_assert (begin != NULL);
  g_assert (end != NULL);
528 529 530 531 532

  get_rect_for_iters (GTK_TEXT_VIEW (self), begin, end, &rect, GTK_TEXT_WINDOW_WIDGET);
  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
  rect.height = MIN (rect.height, alloc.height - rect.y);

533
  theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
534 535 536 537 538 539 540 541 542
                           "alpha", 0.3,
                           "background", "#729fcf",
                           "height", rect.height,
                           "target", self,
                           "width", rect.width,
                           "x", rect.x,
                           "y", rect.y,
                           NULL);

543 544
  dzl_object_animate_full (theatric,
                           DZL_ANIMATION_EASE_IN_CUBIC,
545 546 547 548 549 550 551 552 553 554 555 556
                           250,
                           gtk_widget_get_frame_clock (GTK_WIDGET (self)),
                           g_object_unref,
                           theatric,
                           "x", rect.x - ANIMATION_X_GROW,
                           "width", rect.width + (ANIMATION_X_GROW * 2),
                           "y", rect.y - ANIMATION_Y_GROW,
                           "height", rect.height + (ANIMATION_Y_GROW * 2),
                           "alpha", 0.0,
                           NULL);
}

557 558 559 560 561
static void
animate_shrink (IdeSourceView     *self,
                const GtkTextIter *begin,
                const GtkTextIter *end)
{
562
  DzlBoxTheatric *theatric;
563 564
  GtkAllocation alloc;
  GdkRectangle rect = { 0 };
565
  GdkRectangle char_rect = { 0 };
566 567 568
  GtkTextIter copy_begin;
  GtkTextIter copy_end;
  gboolean is_whole_line;
569
  gboolean is_single_line;
570 571 572 573 574

  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (begin);
  g_assert (end);

575
  get_rect_for_iters (GTK_TEXT_VIEW (self), begin, begin, &char_rect, GTK_TEXT_WINDOW_WIDGET);
576 577 578 579 580 581
  get_rect_for_iters (GTK_TEXT_VIEW (self), begin, end, &rect, GTK_TEXT_WINDOW_WIDGET);
  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
  rect.height = MIN (rect.height, alloc.height - rect.y);

  copy_begin = *begin;
  copy_end = *end;
582

583
  gtk_text_iter_order (&copy_begin, &copy_end);
584 585 586 587 588 589

  is_single_line = (gtk_text_iter_get_line (&copy_begin) == gtk_text_iter_get_line (&copy_end));
  is_whole_line = ((gtk_text_iter_get_line (&copy_begin) + 1 ==
                    gtk_text_iter_get_line (&copy_end)) &&
                   (gtk_text_iter_starts_line (&copy_begin) &&
                    gtk_text_iter_starts_line (&copy_end)));
590

591
  theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
592 593 594 595 596 597 598 599 600 601
                           "alpha", 0.3,
                           "background", "#729fcf",
                           "height", rect.height,
                           "target", self,
                           "width", rect.width,
                           "x", rect.x,
                           "y", rect.y,
                           NULL);

  if (is_whole_line)
602 603
    dzl_object_animate_full (theatric,
                             DZL_ANIMATION_EASE_OUT_QUAD,
604 605 606 607 608 609 610 611
                             150,
                             gtk_widget_get_frame_clock (GTK_WIDGET (self)),
                             g_object_unref,
                             theatric,
                             "x", rect.x,
                             "width", rect.width,
                             "y", rect.y,
                             "height", 0,
612
                             "alpha", 0.3,
613
                             NULL);
614
  else if (is_single_line)
615 616
    dzl_object_animate_full (theatric,
                             DZL_ANIMATION_EASE_OUT_QUAD,
617 618 619 620 621 622 623 624
                             150,
                             gtk_widget_get_frame_clock (GTK_WIDGET (self)),
                             g_object_unref,
                             theatric,
                             "x", rect.x,
                             "width", 0,
                             "y", rect.y,
                             "height", rect.height,
625 626 627
                             "alpha", 0.3,
                             NULL);
  else
628 629
    dzl_object_animate_full (theatric,
                             DZL_ANIMATION_EASE_OUT_QUAD,
630
                             150,
631 632 633
                             gtk_widget_get_frame_clock (GTK_WIDGET (self)),
                             g_object_unref,
                             theatric,
634
                             "x", rect.x,
635
                             "width", 0,
636 637
                             "y", rect.y,
                             "height", char_rect.height,
638
                             "alpha", 0.3,
639 640 641
                             NULL);
}

642
void
643 644 645 646 647
ide_source_view_scroll_to_insert (IdeSourceView *self)
{
  GtkTextBuffer *buffer;
  GtkTextMark *mark;

648 649
  IDE_ENTRY;

650 651 652
  g_assert (IDE_IS_SOURCE_VIEW (self));

  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
653
  _ide_buffer_cancel_cursor_restore (IDE_BUFFER (buffer));
654
  mark = gtk_text_buffer_get_insert (buffer);
655
  ide_source_view_scroll_mark_onscreen (self, mark, TRUE, 0.5, 0.5);
656 657

  IDE_EXIT;
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
}

static void
ide_source_view_invalidate_window (IdeSourceView *self)
{
  GdkWindow *window;

  g_assert (IDE_IS_SOURCE_VIEW (self));

  if ((window = gtk_text_view_get_window (GTK_TEXT_VIEW (self), GTK_TEXT_WINDOW_WIDGET)))
    {
      gdk_window_invalidate_rect (window, NULL, TRUE);
      gtk_widget_queue_draw (GTK_WIDGET (self));
    }
}

static gchar *
text_iter_get_line_prefix (const GtkTextIter *iter)
{
  GtkTextIter begin;
  GString *str;

  g_assert (iter);

  gtk_text_iter_assign (&begin, iter);
  gtk_text_iter_set_line_offset (&begin, 0);

  str = g_string_new (NULL);

  if (gtk_text_iter_compare (&begin, iter) != 0)
    {
      do
        {
          gunichar c;

          c = gtk_text_iter_get_char (&begin);

          switch (c)
            {
            case '\t':
            case ' ':
              g_string_append_unichar (str, c);
              break;
            default:
              g_string_append_c (str, ' ');
              break;
            }
        }
      while (gtk_text_iter_forward_char (&begin) &&
             (gtk_text_iter_compare (&begin, iter) < 0));
    }

  return g_string_free (str, FALSE);
}

713
static void
714
ide_source_view_update_auto_indent_override (IdeSourceView *self)
715 716
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
717 718
  GtkSourceLanguage *language;
  const gchar *lang_id = NULL;
719
  IdeIndenter *indenter;
720 721 722

  g_assert (IDE_IS_SOURCE_VIEW (self));

723 724 725 726 727 728 729 730 731 732 733
  /* Update the indenter if necessary */
  if (priv->auto_indent &&
      priv->indenter_adapter != NULL &&
      priv->buffer != NULL &&
      NULL != (language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (priv->buffer))))
    lang_id = gtk_source_language_get_id (language);

  if (priv->indenter_adapter != NULL)
    ide_extension_adapter_set_value (priv->indenter_adapter, lang_id);

  /* Fetch our indenter */
734 735
  indenter = ide_source_view_get_indenter (self);

736 737
  /*
   * Updates our override of auto-indent from the GtkSourceView underneath us.
738 739 740
   * Since we do our own mimicing of GtkSourceView, we always disable it. Also
   * updates our mode which needs to know if we have an indenter to provide
   * different CSS selectors.
741
   */
742 743 744
  gtk_source_view_set_auto_indent (GTK_SOURCE_VIEW (self), FALSE);
  if (priv->mode != NULL)
    ide_source_view_mode_set_has_indenter (priv->mode, !!indenter);
745 746
}

747 748 749 750 751 752 753 754 755
static void
ide_source_view_set_file_settings (IdeSourceView   *self,
                                   IdeFileSettings *file_settings)
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);

  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (IDE_IS_FILE_SETTINGS (file_settings));

756
  if (file_settings != ide_source_view_get_file_settings (self))
757
    {
758
      dzl_binding_group_set_source (priv->file_setting_bindings, file_settings);
759
      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_FILE_SETTINGS]);
760 761 762
    }
}

763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
static void
ide_source_view__file_load_settings_cb (GObject      *object,
                                        GAsyncResult *result,
                                        gpointer      user_data)
{
  g_autoptr(IdeSourceView) self = user_data;
  g_autoptr(IdeFileSettings) file_settings = NULL;
  g_autoptr(GError) error = NULL;
  IdeFile *file = (IdeFile *)object;

  g_assert (IDE_IS_FILE (file));
  g_assert (IDE_IS_SOURCE_VIEW (self));

  file_settings = ide_file_load_settings_finish (file, result, &error);

  if (!file_settings)
    {
      g_message ("%s", error->message);
      return;
    }

784
  ide_source_view_set_file_settings (self, file_settings);
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
}

static void
ide_source_view_reload_file_settings (IdeSourceView *self)
{
  IdeBuffer *buffer;
  IdeFile *file;

  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (IDE_IS_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (self))));

  buffer = IDE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (self)));
  file = ide_buffer_get_file (buffer);

  ide_file_load_settings_async (file,
                                NULL,
                                ide_source_view__file_load_settings_cb,
                                g_object_ref (self));
}

805 806 807
static void
ide_source_view_reload_language (IdeSourceView *self)
{
808
  GtkSourceLanguage *language;
809 810 811 812 813
  GtkTextBuffer *buffer;
  IdeFile *file = NULL;

  g_assert (IDE_IS_SOURCE_VIEW (self));

814
  /*
815
   * Update source language, etc.
816
   */
817 818 819 820 821 822
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
  file = ide_buffer_get_file (IDE_BUFFER (buffer));
  language = ide_file_get_language (file);

  g_assert (IDE_IS_BUFFER (buffer));
  g_assert (IDE_IS_FILE (file));
823
  g_assert (!language || GTK_SOURCE_IS_LANGUAGE (language));
824

825
  gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (buffer), language);
826 827
}

828 829 830 831 832 833 834
static void
ide_source_view__buffer_notify_file_cb (IdeSourceView *self,
                                        GParamSpec    *pspec,
                                        IdeBuffer     *buffer)
{
  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (IDE_IS_BUFFER (buffer));
835

836
  ide_source_view_reload_language (self);
837
  ide_source_view_reload_file_settings (self);
838 839 840 841 842 843 844
}

static void
ide_source_view__buffer_notify_language_cb (IdeSourceView *self,
                                            GParamSpec    *pspec,
                                            IdeBuffer     *buffer)
{
845 846 847 848
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
  GtkSourceLanguage *language;
  const gchar *lang_id = NULL;

849 850
  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (IDE_IS_BUFFER (buffer));
851 852 853 854 855 856 857 858 859 860

  if ((language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (buffer))))
    lang_id = gtk_source_language_get_id (language);

  /*
   * Update the indenter, which is provided by a plugin.
   */
  if (priv->indenter_adapter != NULL)
    ide_extension_adapter_set_value (priv->indenter_adapter, lang_id);
  ide_source_view_update_auto_indent_override (self);
861
  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_INDENTER]);
862 863
}

864 865 866 867 868 869 870
static void
ide_source_view__buffer_notify_style_scheme_cb (IdeSourceView *self,
                                                GParamSpec    *pspec,
                                                IdeBuffer     *buffer)
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
  GtkSourceStyleScheme *scheme = NULL;
871 872
  GtkSourceStyle *snippet_area_style = NULL;
  g_autofree gchar *snippet_background = NULL;
873 874 875 876 877

  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (IDE_IS_BUFFER (buffer));

  scheme = gtk_source_buffer_get_style_scheme (GTK_SOURCE_BUFFER (buffer));
878 879
  if (scheme != NULL)
    snippet_area_style = gtk_source_style_scheme_get_style (scheme, "snippet::area");
880

881
  if (snippet_area_style != NULL)
882 883
    g_object_get (snippet_area_style, "background", &snippet_background, NULL);

884 885
  if (snippet_background == NULL ||
      !gdk_rgba_parse (&priv->snippet_area_background_rgba, snippet_background))
886 887 888 889
    {
      gdk_rgba_parse (&priv->snippet_area_background_rgba, "#204a87");
      priv->snippet_area_background_rgba.alpha = 0.1;
    }
890 891
}

892 893 894 895
static void
ide_source_view__buffer_changed_cb (IdeSourceView *self,
                                    IdeBuffer     *buffer)
{
896 897
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);

898 899 900
  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (IDE_IS_BUFFER (buffer));

901
  priv->change_sequence++;
902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
}

static void
ide_source_view_rebuild_css (IdeSourceView *self)
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);

  g_return_if_fail (IDE_IS_SOURCE_VIEW (self));

  if (!priv->css_provider)
    {
      GtkStyleContext *style_context;

      priv->css_provider = gtk_css_provider_new ();
      style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
      gtk_style_context_add_provider (style_context,
                                      GTK_STYLE_PROVIDER (priv->css_provider),
                                      GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    }

  if (priv->font_desc)
    {
      g_autofree gchar *str = NULL;
      g_autofree gchar *css = NULL;
926 927 928
      const PangoFontDescription *font_desc = priv->font_desc;
      PangoFontDescription *copy = NULL;

929
      if (priv->font_scale != FONT_SCALE_NORMAL)
930
        font_desc = copy = ide_source_view_get_scaled_font_desc (self);
931

932
      str = dzl_pango_font_description_to_css (font_desc);
933
      css = g_strdup_printf ("textview { %s }", str ?: "");
934
      gtk_css_provider_load_from_data (priv->css_provider, css, -1, NULL);
935

936 937 938
      if (priv->omni_renderer != NULL)
        _ide_omni_gutter_renderer_reset_font (priv->omni_renderer);

939 940 941
      if (priv->completion != NULL)
        _ide_completion_set_font_description (priv->completion, font_desc);

942
      dzl_clear_pointer (&copy, pango_font_description_free);
943 944 945
    }
}

946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
static void
ide_source_view_invalidate_range_mark (IdeSourceView *self,
                                       GtkTextMark   *mark_begin,
                                       GtkTextMark   *mark_end)
{
  GtkTextBuffer *buffer;
  GdkRectangle rect;
  GtkTextIter begin;
  GtkTextIter end;
  GdkWindow *window;

  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (GTK_IS_TEXT_MARK (mark_begin));
  g_assert (GTK_IS_TEXT_MARK (mark_end));

  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
  gtk_text_buffer_get_iter_at_mark (buffer, &begin, mark_begin);
  gtk_text_buffer_get_iter_at_mark (buffer, &end, mark_end);

  get_rect_for_iters (GTK_TEXT_VIEW (self), &begin, &end, &rect, GTK_TEXT_WINDOW_TEXT);
  window = gtk_text_view_get_window (GTK_TEXT_VIEW (self), GTK_TEXT_WINDOW_TEXT);
  gdk_window_invalidate_rect (window, &rect, FALSE);
}

static void
971
ide_source_view__buffer_insert_text_cb (IdeSourceView *self,
972 973 974
                                        GtkTextIter   *iter,
                                        gchar         *text,
                                        gint           len,
975
                                        GtkTextBuffer *buffer)
976 977
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
978
  IdeSnippet *snippet;
979 980

  g_assert (IDE_IS_SOURCE_VIEW (self));
981 982
  g_assert (iter != NULL);
  g_assert (text != NULL);
983 984
  g_assert (IDE_IS_BUFFER (buffer));

985
  if (ide_buffer_get_loading (IDE_BUFFER (buffer)))
986
    return;
987

988 989
  gtk_text_buffer_begin_user_action (buffer);

990
  if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
991 992
    {
      ide_source_view_block_handlers (self);
993
      ide_snippet_before_insert_text (snippet, buffer, iter, text, len);
994 995
      ide_source_view_unblock_handlers (self);
    }
996 997 998
}

static void
999
ide_source_view__buffer_insert_text_after_cb (IdeSourceView *self,
1000 1001 1002
                                              GtkTextIter   *iter,
                                              gchar         *text,
                                              gint           len,
1003
                                              GtkTextBuffer *buffer)
1004 1005
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
1006
  IdeSnippet *snippet;
1007
  GtkTextIter insert;
1008 1009

  g_assert (IDE_IS_SOURCE_VIEW (self));
1010 1011
  g_assert (iter != NULL);
  g_assert (text != NULL);
1012 1013
  g_assert (IDE_IS_BUFFER (buffer));

1014
  if (ide_buffer_get_loading (IDE_BUFFER (buffer)))
1015
    return;
1016

1017
  if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
1018 1019 1020 1021 1022
    {
      GtkTextMark *begin;
      GtkTextMark *end;

      ide_source_view_block_handlers (self);
1023
      ide_snippet_after_insert_text (snippet, buffer, iter, text, len);
1024 1025
      ide_source_view_unblock_handlers (self);

1026 1027
      begin = ide_snippet_get_mark_begin (snippet);
      end = ide_snippet_get_mark_end (snippet);
1028 1029
      ide_source_view_invalidate_range_mark (self, begin, end);
    }
1030

1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
  if (priv->in_key_press)
    {
      /*
       * If we are handling the key-press-event, we might have just inserted
       * a character that indicates we should overwrite the next character.
       * However, due to GtkIMContext constraints, we need to allow it to be
       * inserted and then handle it here.
       */
      ide_source_view_maybe_overwrite (self, iter, text, len);
    }

1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
  gtk_text_buffer_get_iter_at_mark (buffer, &insert, gtk_text_buffer_get_insert(buffer));
  if (gtk_text_iter_equal (iter, &insert))
    {
      ide_source_view_block_handlers (self);
      ide_cursor_insert_text (priv->cursor, text, len);
      ide_source_view_unblock_handlers (self);
      gtk_text_buffer_get_iter_at_mark (buffer, iter, gtk_text_buffer_get_insert (buffer));
    }

  gtk_text_buffer_end_user_action (buffer);

1053
  return;
1054 1055 1056
}

static void
1057
ide_source_view__buffer_delete_range_cb (IdeSourceView *self,
1058 1059
                                         GtkTextIter   *begin,
                                         GtkTextIter   *end,
1060
                                         GtkTextBuffer *buffer)
1061 1062
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
1063
  IdeSnippet *snippet;
1064

1065 1066
  IDE_ENTRY;

1067
  g_assert (IDE_IS_SOURCE_VIEW (self));
1068
  g_assert (GTK_IS_TEXT_BUFFER (buffer));
1069

1070
  if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
1071 1072 1073 1074 1075
    {
      GtkTextMark *begin_mark;
      GtkTextMark *end_mark;

      ide_source_view_block_handlers (self);
1076
      ide_snippet_before_delete_range (snippet, buffer, begin, end);
1077 1078
      ide_source_view_unblock_handlers (self);

1079 1080
      begin_mark = ide_snippet_get_mark_begin (snippet);
      end_mark = ide_snippet_get_mark_end (snippet);
1081 1082
      ide_source_view_invalidate_range_mark (self, begin_mark, end_mark);
    }
1083

1084
  IDE_EXIT;
1085 1086 1087
}

static void
1088
ide_source_view__buffer_delete_range_after_cb (IdeSourceView *self,
1089 1090
                                               GtkTextIter   *begin,
                                               GtkTextIter   *end,
1091
                                               GtkTextBuffer *buffer)
1092 1093
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
1094
  IdeSnippet *snippet;
1095

1096 1097
  IDE_ENTRY;

1098
  g_assert (IDE_IS_SOURCE_VIEW (self));
1099
  g_assert (GTK_IS_TEXT_BUFFER (buffer));
1100 1101 1102

  ide_source_view_block_handlers (self);

1103
  if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
1104
    ide_snippet_after_delete_range (snippet, buffer, begin, end);
1105 1106

  ide_source_view_unblock_handlers (self);
1107 1108

  IDE_EXIT;
1109 1110 1111
}

static void
1112
ide_source_view__buffer_mark_set_cb (IdeSourceView *self,
1113 1114
                                     GtkTextIter   *iter,
                                     GtkTextMark   *mark,
1115
                                     GtkTextBuffer *buffer)
1116 1117
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
1118
  IdeSnippet *snippet;
1119
  GtkTextMark *insert;
1120 1121

  g_assert (IDE_IS_SOURCE_VIEW (self));
1122
  g_assert (iter != NULL);
1123
  g_assert (GTK_IS_TEXT_MARK (mark));
1124
  g_assert (GTK_IS_TEXT_BUFFER (buffer));
1125

1126
  insert = gtk_text_buffer_get_insert (buffer);
1127

1128
  if (mark == insert)
1129
    {
1130
      ide_source_view_block_handlers (self);
1131
      while (NULL != (snippet = g_queue_peek_head (priv->snippets)) &&
1132
             !ide_snippet_insert_set (snippet, mark))
1133
        ide_source_view_pop_snippet (self);
1134
      ide_source_view_unblock_handlers (self);
1135
    }
1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153

#ifdef IDE_ENABLE_TRACE
  if (mark == insert || mark == gtk_text_buffer_get_selection_bound (buffer))
      {
        GtkTextIter begin;
        GtkTextIter end;

        if (gtk_text_buffer_get_selection_bounds (buffer, &begin, &end))
          {
            gtk_text_iter_order (&begin, &end);
            IDE_TRACE_MSG ("Selection is now %d:%d to %d:%d",
                           gtk_text_iter_get_line (&begin),
                           gtk_text_iter_get_line_offset (&begin),
                           gtk_text_iter_get_line (&end),
                           gtk_text_iter_get_line_offset (&end));
          }
      }
#endif
1154 1155
}

1156 1157 1158 1159 1160
static void
ide_source_view__buffer_notify_has_selection_cb (IdeSourceView *self,
                                                 GParamSpec    *pspec,
                                                 IdeBuffer     *buffer)
{
1161 1162
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
  gboolean has_selection;
1163 1164
  IdeWorkbench *workbench = ide_widget_get_workbench (GTK_WIDGET (self));

1165 1166 1167
  has_selection = gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (buffer));
  ide_source_view_mode_set_has_selection (priv->mode, has_selection);

1168 1169 1170
  if (workbench == NULL)
    return;

1171
  if (has_selection)
1172 1173 1174 1175 1176
    ide_workbench_set_selection_owner (workbench, G_OBJECT (self));
  else if (ide_workbench_get_selection_owner (workbench) == G_OBJECT (self))
    ide_workbench_set_selection_owner (workbench, NULL);
}

1177 1178 1179 1180
static void
ide_source_view__buffer_line_flags_changed_cb (IdeSourceView *self,
                                               IdeBuffer     *buffer)
{
1181 1182
  IDE_ENTRY;

1183 1184 1185
  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (IDE_IS_BUFFER (buffer));

1186 1187
  gtk_source_gutter_queue_draw (gtk_source_view_get_gutter (GTK_SOURCE_VIEW (self),
                                                            GTK_TEXT_WINDOW_LEFT));
1188 1189

  IDE_EXIT;
1190 1191
}

1192 1193 1194 1195
static void
ide_source_view__buffer_loaded_cb (IdeSourceView *self,
                                   IdeBuffer     *buffer)
{
1196
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
1197
  GtkAdjustment *adj;
1198
  GtkTextMark *insert;
1199
  GtkTextIter iter;
1200

1201 1202
  IDE_ENTRY;

1203 1204 1205
  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (IDE_IS_BUFFER (buffer));

1206 1207
  if (priv->completion_blocked)
    {
1208
      unblock_interactive (self);
1209 1210 1211
      priv->completion_blocked = FALSE;
    }

1212
  insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
1213

1214
  /* Store the line column (visual offset) so movements are correct. */
1215
  gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter, insert);
1216 1217
  priv->target_line_column = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (self),
                                                                &iter);
1218 1219 1220 1221 1222 1223 1224

  /* Only scroll if the user hasn't started an intermediate scroll */
  adj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self));
  if (gtk_adjustment_get_value (adj) == gtk_adjustment_get_lower (adj))
    ide_source_view_scroll_to_mark (self, insert, 0.0, TRUE, 0.5, 0.5, TRUE);

  IDE_EXIT;
1225 1226
}

1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
static void
ide_source_view_set_cursor_from_name (IdeSourceView *self,
                                      const gchar   *cursor_name)
{
  GdkDisplay *display;
  GdkCursor *cursor;
  GdkWindow *window = gtk_text_view_get_window (GTK_TEXT_VIEW (self),
                                                GTK_TEXT_WINDOW_TEXT);

  if (!window)
    return;

  display = gdk_window_get_display (window);
  cursor = gdk_cursor_new_from_name (display, cursor_name);

  gdk_window_set_cursor (window, cursor);
}

static void
ide_source_view_reset_definition_highlight (IdeSourceView *self)
{
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);

1250 1251
  g_assert (IDE_IS_SOURCE_VIEW (self));

1252
  if (priv->definition_src_location)
1253
    dzl_clear_pointer (&priv->definition_src_location, ide_source_location_unref);
1254

1255
  if (priv->buffer != NULL)
1256
    {
1257 1258
      GtkTextIter begin;
      GtkTextIter end;
1259

1260 1261
      gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (priv->buffer), &begin, &end);
      gtk_text_buffer_remove_tag_by_name (GTK_TEXT_BUFFER (priv->buffer), TAG_DEFINITION, &begin, &end);
1262 1263 1264 1265 1266
    }

  ide_source_view_set_cursor_from_name (self, "text");
}

1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
static void
ide_source_view__buffer__notify_can_redo (IdeSourceView *self,
                                          GParamSpec    *pspec,
                                          IdeBuffer     *buffer)
{
  GActionGroup *group;
  gboolean can_redo;

  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (IDE_IS_BUFFER (buffer));

  g_object_get (buffer,
                "can-redo", &can_redo,
                NULL);

  group = gtk_widget_get_action_group (GTK_WIDGET (self), "sourceview");
1283
  dzl_widget_action_group_set_action_enabled (DZL_WIDGET_ACTION_GROUP (group), "redo", can_redo);
1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301
}

static void
ide_source_view__buffer__notify_can_undo (IdeSourceView *self,
                                          GParamSpec    *pspec,
                                          IdeBuffer     *buffer)
{
  GActionGroup *group;
  gboolean can_undo;

  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (IDE_IS_BUFFER (buffer));

  g_object_get (buffer,
                "can-undo", &can_undo,
                NULL);

  group = gtk_widget_get_action_group (GTK_WIDGET (self), "sourceview");
1302
  dzl_widget_action_group_set_action_enabled (DZL_WIDGET_ACTION_GROUP (group), "undo", can_undo);
1303 1304
}

1305
static void
1306 1307
ide_source_view_bind_buffer (IdeSourceView  *self,
                             IdeBuffer      *buffer,
1308
                             DzlSignalGroup *group)
1309
{
1310
  IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
1311
  GtkTextMark *insert;
1312
  GtkTextIter iter;
1313
  IdeContext *context;
1314

1315 1316
  IDE_ENTRY;

1317 1318
  g_assert (IDE_IS_SOURCE_VIEW (self));
  g_assert (IDE_IS_BUFFER (buffer));
1319
  g_assert (DZL_IS_SIGNAL_GROUP (group));
1320 1321

  priv->buffer = buffer;
1322

1323 1324
  ide_source_view_reset_definition_highlight (self);

1325 1326
  ide_buffer_hold (buffer);

1327
  if (ide_buffer_get_loading (buffer))
1328
    {
1329
      block_interactive (self);
1330 1331 1332
      priv->completion_blocked = TRUE;
    }

1333 1334 1335 1336 1337 1338 1339 1340
  context = ide_buffer_get_context (buffer);

  priv->indenter_adapter = ide_extension_adapter_new (context,
                                                      peas_engine_get_default (),
                                                      IDE_TYPE_INDENTER,
                                                      "Indenter-Languages",
                                                      NULL);

1341 1342 1343 1344
  priv->cursor = g_object_new (IDE_TYPE_CURSOR,
                               "ide-source-view", self,
                               NULL);

1345
  /* Create scroll mark used by movements and our scrolling helper */