1
/* ide-source-view-movements.c
3
* Copyright (C) 2015 Christian Hergert <christian@hergert.me>
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#define G_LOG_DOMAIN "ide-source-view"
21
#include "ide-debug.h"
22
#include "ide-enums.h"
23
#include "ide-internal.h"
24
#include "ide-source-iter.h"
25
#include "ide-source-view-movements.h"
26
#include "ide-vim-iter.h"
28
#define ANCHOR_BEGIN "SELECTION_ANCHOR_BEGIN"
29
#define ANCHOR_END "SELECTION_ANCHOR_END"
31
#define TRACE_ITER(iter) \
32
IDE_TRACE_MSG("%d:%d", gtk_text_iter_get_line(iter), \
33
gtk_text_iter_get_line_offset(iter))
38
/* The target_offset contains the ideal character line_offset. This can
39
* sometimes be further forward than designed when the line does not have
40
* enough characters to get back to the original position. -1 indicates
44
IdeSourceViewMovement type; /* Type of movement */
45
GtkTextIter insert; /* Current insert cursor location */
46
GtkTextIter selection; /* Current selection cursor location */
47
gint count; /* Repeat count for movement */
48
gunichar modifier; /* For forward/backward char search */
49
guint extend_selection : 1; /* If selection should be extended */
50
guint exclusive : 1; /* See ":help exclusive" in vim */
51
guint ignore_select : 1; /* Don't update selection after movement */
52
guint ignore_target_offset : 1; /* Don't propagate new line offset */
53
guint ignore_scroll_to_insert : 1; /* Don't scroll to insert mark */
61
} MatchingBracketState;
64
is_single_line_selection (const GtkTextIter *begin,
65
const GtkTextIter *end)
67
if (gtk_text_iter_compare (begin, end) < 0)
68
return ((gtk_text_iter_get_line_offset (begin) == 0) &&
69
(gtk_text_iter_get_line_offset (end) == 0) &&
70
((gtk_text_iter_get_line (begin) + 1) ==
71
gtk_text_iter_get_line (end)));
73
return ((gtk_text_iter_get_line_offset (begin) == 0) &&
74
(gtk_text_iter_get_line_offset (end) == 0) &&
75
((gtk_text_iter_get_line (end) + 1) ==
76
gtk_text_iter_get_line (begin)));
80
is_single_char_selection (const GtkTextIter *begin,
81
const GtkTextIter *end)
89
if (gtk_text_iter_forward_char (&tmp) && gtk_text_iter_equal (&tmp, end))
93
if (gtk_text_iter_forward_char (&tmp) && gtk_text_iter_equal (&tmp, begin))
100
text_iter_forward_to_nonspace_captive (GtkTextIter *iter)
102
while (!gtk_text_iter_ends_line (iter) && g_unichar_isspace (gtk_text_iter_get_char (iter)))
103
if (!gtk_text_iter_forward_char (iter))
106
return !g_unichar_isspace (gtk_text_iter_get_char (iter));
110
select_range (Movement *mv,
111
GtkTextIter *insert_iter,
112
GtkTextIter *selection_iter)
114
GtkTextBuffer *buffer;
116
GtkTextMark *selection;
120
g_assert (insert_iter);
121
g_assert (selection_iter);
123
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
124
insert = gtk_text_buffer_get_insert (buffer);
125
selection = gtk_text_buffer_get_selection_bound (buffer);
127
mv->ignore_select = TRUE;
130
* If the caller is requesting that we select a single character, we will
131
* keep the iter before that character. This more closely matches the visual
134
insert_off = gtk_text_iter_get_offset (insert_iter);
135
selection_off = gtk_text_iter_get_offset (selection_iter);
136
if ((insert_off - selection_off) == 1)
137
gtk_text_iter_order (insert_iter, selection_iter);
139
gtk_text_buffer_move_mark (buffer, insert, insert_iter);
140
gtk_text_buffer_move_mark (buffer, selection, selection_iter);
144
ensure_anchor_selected (Movement *mv)
146
GtkTextBuffer *buffer;
147
GtkTextMark *selection_mark;
148
GtkTextMark *insert_mark;
149
GtkTextIter anchor_begin;
150
GtkTextIter anchor_end;
151
GtkTextIter insert_iter;
152
GtkTextIter selection_iter;
153
GtkTextMark *selection_anchor_begin;
154
GtkTextMark *selection_anchor_end;
156
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
158
selection_anchor_begin = gtk_text_buffer_get_mark (buffer, ANCHOR_BEGIN);
159
selection_anchor_end = gtk_text_buffer_get_mark (buffer, ANCHOR_END);
161
if (!selection_anchor_begin || !selection_anchor_end)
164
gtk_text_buffer_get_iter_at_mark (buffer, &anchor_begin, selection_anchor_begin);
165
gtk_text_buffer_get_iter_at_mark (buffer, &anchor_end, selection_anchor_end);
167
insert_mark = gtk_text_buffer_get_insert (buffer);
168
gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, insert_mark);
170
selection_mark = gtk_text_buffer_get_selection_bound (buffer);
171
gtk_text_buffer_get_iter_at_mark (buffer, &selection_iter, selection_mark);
173
if ((gtk_text_iter_compare (&selection_iter, &anchor_end) < 0) &&
174
(gtk_text_iter_compare (&insert_iter, &anchor_end) < 0))
176
if (gtk_text_iter_compare (&insert_iter, &selection_iter) < 0)
177
select_range (mv, &insert_iter, &anchor_end);
179
select_range (mv, &anchor_end, &selection_iter);
181
else if ((gtk_text_iter_compare (&selection_iter, &anchor_begin) > 0) &&
182
(gtk_text_iter_compare (&insert_iter, &anchor_begin) > 0))
184
if (gtk_text_iter_compare (&insert_iter, &selection_iter) < 0)
185
select_range (mv, &anchor_begin, &selection_iter);
187
select_range (mv, &insert_iter, &anchor_begin);
192
text_iter_forward_to_empty_line (GtkTextIter *iter,
195
if (!gtk_text_iter_forward_char (iter))
198
while (gtk_text_iter_compare (iter, bounds) < 0)
200
if (gtk_text_iter_starts_line (iter) && gtk_text_iter_ends_line (iter))
202
if (!gtk_text_iter_forward_char (iter))
210
ide_source_view_movements_get_selection (Movement *mv)
212
GtkTextBuffer *buffer;
216
g_assert (IDE_IS_SOURCE_VIEW (mv->self));
218
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
220
mark = gtk_text_buffer_get_insert (buffer);
221
gtk_text_buffer_get_iter_at_mark (buffer, &mv->insert, mark);
223
mark = gtk_text_buffer_get_selection_bound (buffer);
224
gtk_text_buffer_get_iter_at_mark (buffer, &mv->selection, mark);
228
ide_source_view_movements_select_range (Movement *mv)
230
GtkTextBuffer *buffer;
234
g_assert (IDE_IS_SOURCE_VIEW (mv->self));
236
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
238
if (mv->extend_selection)
239
gtk_text_buffer_select_range (buffer, &mv->insert, &mv->selection);
241
gtk_text_buffer_select_range (buffer, &mv->insert, &mv->insert);
243
mark = gtk_text_buffer_get_insert (buffer);
244
gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (mv->self), mark);
248
ide_source_view_movements_nth_char (Movement *mv)
250
gtk_text_iter_set_line_offset (&mv->insert, 0);
252
for (; mv->count > 0; mv->count--)
254
if (gtk_text_iter_ends_line (&mv->insert))
256
gtk_text_iter_forward_char (&mv->insert);
260
gtk_text_iter_forward_char (&mv->insert);
264
ide_source_view_movements_previous_char (Movement *mv)
266
mv->count = MAX (1, mv->count);
268
for (; mv->count; mv->count--)
270
if (gtk_text_iter_starts_line (&mv->insert))
272
gtk_text_iter_backward_char (&mv->insert);
276
gtk_text_iter_forward_char (&mv->insert);
280
ide_source_view_movements_next_char (Movement *mv)
282
mv->count = MAX (1, mv->count);
284
for (; mv->count; mv->count--)
286
if (gtk_text_iter_ends_line (&mv->insert))
288
gtk_text_iter_forward_char (&mv->insert);
291
if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
292
gtk_text_iter_forward_char (&mv->insert);
296
ide_source_view_movements_first_char (Movement *mv)
298
gtk_text_iter_set_line_offset (&mv->insert, 0);
302
ide_source_view_movements_first_nonspace_char (Movement *mv)
306
if (gtk_text_iter_get_line_offset (&mv->insert) != 0)
307
gtk_text_iter_set_line_offset (&mv->insert, 0);
309
while (!gtk_text_iter_ends_line (&mv->insert) &&
310
(ch = gtk_text_iter_get_char (&mv->insert)) &&
311
g_unichar_isspace (ch))
312
gtk_text_iter_forward_char (&mv->insert);
314
if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
315
gtk_text_iter_forward_char (&mv->insert);
319
ide_source_view_movements_line_chars (Movement *mv)
321
GtkTextIter orig = mv->insert;
324
* Selects the current position up to the first nonspace character.
325
* If the cursor is at the line start, we will select the newline.
326
* If only whitespace exists, we will select line offset of 0.
329
if (gtk_text_iter_starts_line (&mv->insert))
331
gtk_text_iter_backward_char (&mv->insert);
337
gtk_text_iter_set_line_offset (&mv->insert, 0);
339
while (!gtk_text_iter_ends_line (&mv->insert) &&
340
(ch = gtk_text_iter_get_char (&mv->insert)) &&
341
g_unichar_isspace (ch))
342
gtk_text_iter_forward_char (&mv->insert);
344
if (gtk_text_iter_ends_line (&mv->insert) ||
345
(gtk_text_iter_compare (&orig, &mv->insert) <= 0))
346
gtk_text_iter_set_line_offset (&mv->insert, 0);
350
gtk_text_iter_forward_char (&mv->insert);
354
ide_source_view_movements_line_end (Movement *mv)
356
if (!gtk_text_iter_ends_line (&mv->insert))
357
gtk_text_iter_forward_to_line_end (&mv->insert);
360
gtk_text_iter_forward_char (&mv->insert);
364
ide_source_view_movements_middle_char (Movement *mv)
366
GtkTextView *text_view = GTK_TEXT_VIEW (mv->self);
373
gtk_text_view_get_iter_location (text_view, &mv->insert, &rect);
374
window = gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT);
376
width = gdk_window_get_width (window);
380
chars_in_line = width / rect.width;
381
if (chars_in_line == 0)
384
gtk_text_iter_set_line_offset (&mv->insert, 0);
386
for (line_offset = chars_in_line / 2; line_offset; line_offset--)
387
if (!gtk_text_iter_forward_char (&mv->insert))
391
if (!gtk_text_iter_ends_line (&mv->insert))
392
gtk_text_iter_forward_char (&mv->insert);
396
ide_source_view_movements_last_char (Movement *mv)
398
if (!gtk_text_iter_ends_line (&mv->insert))
400
gtk_text_iter_forward_to_line_end (&mv->insert);
401
if (mv->exclusive && !gtk_text_iter_starts_line (&mv->insert))
402
gtk_text_iter_backward_char (&mv->insert);
407
ide_source_view_movements_first_line (Movement *mv)
409
gtk_text_iter_set_line (&mv->insert, mv->count);
410
gtk_text_iter_set_line_offset (&mv->insert, 0);
414
ide_source_view_movements_nth_line (Movement *mv)
416
GtkTextBuffer *buffer;
418
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
421
gtk_text_buffer_get_end_iter (buffer, &mv->insert);
423
gtk_text_iter_set_line (&mv->insert, mv->count - 1);
425
gtk_text_iter_set_line_offset (&mv->insert, 0);
427
while (!gtk_text_iter_ends_line (&mv->insert) &&
428
g_unichar_isspace (gtk_text_iter_get_char (&mv->insert)))
429
if (!gtk_text_iter_forward_char (&mv->insert))
434
ide_source_view_movements_last_line (Movement *mv)
436
GtkTextBuffer *buffer;
438
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
439
gtk_text_buffer_get_end_iter (buffer, &mv->insert);
440
gtk_text_iter_set_line_offset (&mv->insert, 0);
446
line = gtk_text_iter_get_line (&mv->insert) - mv->count;
447
gtk_text_iter_set_line (&mv->insert, MAX (0, line));
452
ide_source_view_movements_next_line (Movement *mv)
454
GtkTextBuffer *buffer;
455
gboolean has_selection;
459
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
461
/* check for linewise */
462
has_selection = !gtk_text_iter_equal (&mv->insert, &mv->selection) || !mv->exclusive;
464
line = gtk_text_iter_get_line (&mv->insert);
466
if ((*mv->target_offset) > 0)
467
offset = *mv->target_offset;
470
* If we have a whole line selected (from say `V`), then we need to swap
471
* the cursor and selection. This feels to me like a slight bit of a hack.
472
* There may be cause to actually have a selection mode and know the type
473
* of selection (line vs individual characters).
475
if (is_single_line_selection (&mv->insert, &mv->selection))
479
if (gtk_text_iter_compare (&mv->insert, &mv->selection) < 0)
480
gtk_text_iter_order (&mv->selection, &mv->insert);
482
target_line = gtk_text_iter_get_line (&mv->insert) + 1;
483
gtk_text_iter_set_line (&mv->insert, target_line);
485
if (target_line != gtk_text_iter_get_line (&mv->insert))
488
select_range (mv, &mv->insert, &mv->selection);
489
ensure_anchor_selected (mv);
493
if (is_single_char_selection (&mv->insert, &mv->selection))
495
if (gtk_text_iter_compare (&mv->insert, &mv->selection) < 0)
496
*mv->target_offset = ++offset;
499
gtk_text_buffer_get_iter_at_line (buffer, &mv->insert, line + 1);
500
if ((line + 1) == gtk_text_iter_get_line (&mv->insert))
502
for (; offset; offset--)
503
if (!gtk_text_iter_ends_line (&mv->insert))
504
if (!gtk_text_iter_forward_char (&mv->insert))
508
select_range (mv, &mv->insert, &mv->selection);
509
ensure_anchor_selected (mv);
512
gtk_text_buffer_select_range (buffer, &mv->insert, &mv->insert);
517
gtk_text_buffer_get_end_iter (buffer, &mv->insert);
520
select_range (mv, &mv->insert, &mv->selection);
521
ensure_anchor_selected (mv);
524
gtk_text_buffer_select_range (buffer, &mv->insert, &mv->insert);
527
/* make sure selection/insert are up to date */
528
if (!gtk_text_buffer_get_has_selection (buffer))
529
mv->selection = mv->insert;
533
ide_source_view_movements_previous_line (Movement *mv)
535
GtkTextBuffer *buffer;
536
gboolean has_selection;
540
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
542
/* check for linewise */
543
has_selection = !gtk_text_iter_equal (&mv->insert, &mv->selection) || !mv->exclusive;
545
line = gtk_text_iter_get_line (&mv->insert);
547
if ((*mv->target_offset) > 0)
548
offset = *mv->target_offset;
554
* If we have a whole line selected (from say `V`), then we need to swap the cursor and
555
* selection. This feels to me like a slight bit of a hack. There may be cause to actually have
556
* a selection mode and know the type of selection (line vs individual characters).
558
if (is_single_line_selection (&mv->insert, &mv->selection))
560
if (gtk_text_iter_compare (&mv->insert, &mv->selection) > 0)
561
gtk_text_iter_order (&mv->insert, &mv->selection);
562
gtk_text_iter_set_line (&mv->insert, gtk_text_iter_get_line (&mv->insert) - 1);
563
select_range (mv, &mv->insert, &mv->selection);
564
ensure_anchor_selected (mv);
568
if (is_single_char_selection (&mv->insert, &mv->selection))
570
if (gtk_text_iter_compare (&mv->insert, &mv->selection) > 0)
574
*mv->target_offset = offset;
578
gtk_text_buffer_get_iter_at_line (buffer, &mv->insert, line - 1);
579
if ((line - 1) == gtk_text_iter_get_line (&mv->insert))
581
for (; offset; offset--)
582
if (!gtk_text_iter_ends_line (&mv->insert))
583
if (!gtk_text_iter_forward_char (&mv->insert))
588
if (gtk_text_iter_equal (&mv->insert, &mv->selection))
589
gtk_text_iter_backward_char (&mv->insert);
590
select_range (mv, &mv->insert, &mv->selection);
591
ensure_anchor_selected (mv);
594
gtk_text_buffer_select_range (buffer, &mv->insert, &mv->insert);
597
/* make sure selection/insert are up to date */
598
if (!gtk_text_buffer_get_has_selection (buffer))
599
mv->selection = mv->insert;
603
ide_source_view_movements_screen_top (Movement *mv)
605
GtkTextView *text_view = (GtkTextView *)mv->self;
608
ide_source_view_get_visible_rect (mv->self, &rect);
609
gtk_text_view_get_iter_at_location (text_view, &mv->insert, rect.x, rect.y);
610
gtk_text_iter_set_line_offset (&mv->insert, 0);
614
ide_source_view_movements_screen_middle (Movement *mv)
616
GtkTextView *text_view = (GtkTextView *)mv->self;
619
ide_source_view_get_visible_rect (mv->self, &rect);
620
gtk_text_view_get_iter_at_location (text_view, &mv->insert, rect.x, rect.y + (rect.height / 2));
621
gtk_text_iter_set_line_offset (&mv->insert, 0);
625
ide_source_view_movements_screen_bottom (Movement *mv)
627
GtkTextView *text_view = (GtkTextView *)mv->self;
630
ide_source_view_get_visible_rect (mv->self, &rect);
631
gtk_text_view_get_iter_at_location (text_view, &mv->insert, rect.x, rect.y + rect.height - 1);
632
gtk_text_iter_set_line_offset (&mv->insert, 0);
636
ide_source_view_movements_scroll_by_lines (Movement *mv,
639
GtkTextView *text_view = (GtkTextView *)mv->self;
641
GtkTextBuffer *buffer;
652
vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (mv->self));
653
buffer = gtk_text_view_get_buffer (text_view);
655
gtk_text_buffer_get_bounds (buffer, &begin, &end);
659
if (gtk_text_iter_get_line (&end) == gtk_text_iter_get_line (&mv->insert))
664
if (gtk_text_iter_get_line (&begin) == gtk_text_iter_get_line (&mv->insert))
668
g_assert_not_reached ();
670
gtk_text_view_get_iter_location (text_view, &mv->insert, &rect);
672
amount = lines * rect.height;
674
value = gtk_adjustment_get_value (vadj);
675
upper = gtk_adjustment_get_upper (vadj);
676
gtk_adjustment_set_value (vadj, CLAMP (value + amount, 0, upper));
678
mv->ignore_scroll_to_insert = TRUE;
679
ide_source_view_place_cursor_onscreen (mv->self);
683
ide_source_view_movements_scroll (Movement *mv)
685
GtkTextBuffer *buffer;
687
gint count = MAX (1, mv->count);
689
if (mv->type == IDE_SOURCE_VIEW_MOVEMENT_SCREEN_DOWN)
692
ide_source_view_movements_scroll_by_lines (mv, count);
694
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
695
mark = gtk_text_buffer_get_insert (buffer);
696
gtk_text_buffer_get_iter_at_mark (buffer, &mv->insert, mark);
697
ide_source_view_move_mark_onscreen (mv->self, mark);
701
ide_source_view_movements_move_page (Movement *mv)
703
GtkTextView *text_view = (GtkTextView *)mv->self;
704
GtkTextBuffer *buffer;
707
GtkTextIter iter_top;
708
GtkTextIter iter_bottom;
709
GtkTextIter scroll_iter;
715
gtk_text_view_get_visible_rect (text_view, &rect);
716
gtk_text_view_get_iter_at_location (text_view, &iter_top, rect.x, rect.y);
717
gtk_text_view_get_iter_at_location (text_view, &iter_bottom,
719
rect.y + rect.height);
721
buffer = gtk_text_view_get_buffer (text_view);
723
line_top = gtk_text_iter_get_line (&iter_top);
724
line_bottom = gtk_text_iter_get_line (&iter_bottom);
726
half_page = MAX (1, (line_bottom - line_top) / 2);
727
scrolloff = MIN (ide_source_view_get_scroll_offset (mv->self), half_page);
729
switch ((int)mv->type)
731
case IDE_SOURCE_VIEW_MOVEMENT_HALF_PAGE_UP:
732
ide_source_view_movements_scroll_by_lines (mv, -half_page);
733
gtk_text_iter_backward_lines (&mv->insert, half_page);
736
case IDE_SOURCE_VIEW_MOVEMENT_HALF_PAGE_DOWN:
737
ide_source_view_movements_scroll_by_lines (mv, half_page);
738
gtk_text_iter_forward_lines (&mv->insert, half_page);
741
case IDE_SOURCE_VIEW_MOVEMENT_PAGE_UP:
742
gtk_text_buffer_get_iter_at_line (buffer, &mv->insert, MAX (0, line_top - scrolloff));
743
text_iter_forward_to_nonspace_captive (&mv->insert);
744
ide_source_view_movements_select_range (mv);
746
mark = _ide_source_view_get_scroll_mark (mv->self);
747
gtk_text_buffer_get_iter_at_line (buffer, &scroll_iter, line_top);
748
gtk_text_buffer_move_mark (buffer, mark, &scroll_iter);
749
gtk_text_view_scroll_to_mark (text_view, mark, 0.0, TRUE, 1.0, 1.0);
751
mv->ignore_select = TRUE;
752
mv->ignore_scroll_to_insert = TRUE;
755
case IDE_SOURCE_VIEW_MOVEMENT_PAGE_DOWN:
756
gtk_text_buffer_get_iter_at_line (buffer, &mv->insert, line_bottom + scrolloff);
757
text_iter_forward_to_nonspace_captive (&mv->insert);
758
ide_source_view_movements_select_range (mv);
760
mark = _ide_source_view_get_scroll_mark (mv->self);
761
gtk_text_buffer_get_iter_at_line (buffer, &scroll_iter, line_bottom);
762
gtk_text_buffer_move_mark (buffer, mark, &scroll_iter);
763
gtk_text_view_scroll_to_mark (text_view, mark, 0.0, TRUE, 1.0, 0.0);
765
mv->ignore_select = TRUE;
766
mv->ignore_scroll_to_insert = TRUE;
770
g_assert_not_reached();
775
bracket_predicate (gunichar ch,
778
MatchingBracketState *state = user_data;
780
if (ch == state->jump_from)
782
else if (ch == state->jump_to)
785
return (state->depth == 0);
789
ide_source_view_movements_match_special (Movement *mv)
791
MatchingBracketState state;
793
gboolean is_forward = FALSE;
799
state.jump_from = gtk_text_iter_get_char (&mv->insert);
801
switch (state.jump_from)
838
ret = gtk_text_iter_forward_find_char (&mv->insert, bracket_predicate, &state, NULL);
840
ret = gtk_text_iter_backward_find_char (&mv->insert, bracket_predicate, &state, NULL);
844
else if (!mv->exclusive)
845
gtk_text_iter_forward_char (&mv->insert);
849
ide_source_view_movements_scroll_center (Movement *mv)
851
GtkTextView *text_view = (GtkTextView *)mv->self;
853
GtkTextBuffer *buffer;
855
buffer = gtk_text_view_get_buffer (text_view);
856
insert = gtk_text_buffer_get_insert (buffer);
858
switch ((int)mv->type)
860
case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_BOTTOM:
861
ide_source_view_scroll_to_mark (mv->self, insert, 0.0, TRUE, 1.0, 1.0, TRUE);
864
case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_TOP:
865
ide_source_view_scroll_to_mark (mv->self, insert, 0.0, TRUE, 1.0, 0.0, TRUE);
868
case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_CENTER:
869
ide_source_view_scroll_to_mark (mv->self, insert, 0.0, TRUE, 1.0, 0.5, TRUE);
878
ide_source_view_movements_next_word_end (Movement *mv)
884
_ide_vim_iter_forward_word_end (&mv->insert);
886
/* prefer an empty line before word */
887
text_iter_forward_to_empty_line (©, &mv->insert);
888
if (gtk_text_iter_compare (©, &mv->insert) < 0)
891
if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
892
gtk_text_iter_forward_char (&mv->insert);
896
ide_source_view_movements_next_full_word_end (Movement *mv)
902
_ide_vim_iter_forward_WORD_end (&mv->insert);
904
/* prefer an empty line before word */
905
text_iter_forward_to_empty_line (©, &mv->insert);
906
if (gtk_text_iter_compare (©, &mv->insert) < 0)
909
if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
910
gtk_text_iter_forward_char (&mv->insert);
914
ide_source_view_movements_next_word_start (Movement *mv)
920
_ide_vim_iter_forward_word_start (&mv->insert);
922
/* prefer an empty line before word */
923
text_iter_forward_to_empty_line (©, &mv->insert);
924
if (gtk_text_iter_compare (©, &mv->insert) < 0)
927
if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
928
gtk_text_iter_forward_char (&mv->insert);
932
ide_source_view_movements_next_full_word_start (Movement *mv)
938
_ide_vim_iter_forward_WORD_start (&mv->insert);
940
/* prefer an empty line before word */
941
text_iter_forward_to_empty_line (©, &mv->insert);
942
if (gtk_text_iter_compare (©, &mv->insert) < 0)
945
if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
946
gtk_text_iter_forward_char (&mv->insert);
950
ide_source_view_movements_previous_word_start (Movement *mv)
956
_ide_source_iter_backward_visible_word_start (&mv->insert);
959
* Vim treats an empty line as a word.
961
if (gtk_text_iter_backward_char (©))
962
if (gtk_text_iter_get_char (©) == '\n')
965
if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
966
gtk_text_iter_forward_char (&mv->insert);
970
ide_source_view_movements_previous_full_word_start (Movement *mv)
976
_ide_source_iter_backward_full_word_start (&mv->insert);
979
* Vim treats an empty line as a word.
981
if (gtk_text_iter_backward_char (©))
982
if (gtk_text_iter_get_char (©) == '\n')
985
if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
986
gtk_text_iter_forward_char (&mv->insert);
990
ide_source_view_movements_previous_word_end (Movement *mv)
996
_ide_vim_iter_backward_word_end (&mv->insert);
999
* Vim treats an empty line as a word.
1001
while ((gtk_text_iter_compare (©, &mv->insert) > 0) &&
1002
gtk_text_iter_backward_char (©))
1004
if (gtk_text_iter_starts_line (©) &&
1005
gtk_text_iter_ends_line (©))
1009
if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
1010
gtk_text_iter_forward_char (&mv->insert);
1014
ide_source_view_movements_previous_full_word_end (Movement *mv)
1020
_ide_vim_iter_backward_WORD_end (&mv->insert);
1023
* Vim treats an empty line as a word.
1025
while ((gtk_text_iter_compare (©, &mv->insert) > 0) &&
1026
gtk_text_iter_backward_char (©))
1028
if (gtk_text_iter_starts_line (©) &&
1029
gtk_text_iter_ends_line (©))
1033
if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
1034
gtk_text_iter_forward_char (&mv->insert);
1038
ide_source_view_movements_paragraph_start (Movement *mv)
1040
_ide_vim_iter_backward_paragraph_start (&mv->insert);
1044
while (g_unichar_isspace (gtk_text_iter_get_char (&mv->insert)))
1046
if (!gtk_text_iter_forward_char (&mv->insert))
1053
ide_source_view_movements_paragraph_end (Movement *mv)
1055
_ide_vim_iter_forward_paragraph_end (&mv->insert);
1059
gboolean adjust = FALSE;
1061
while (g_unichar_isspace (gtk_text_iter_get_char (&mv->insert)))
1064
if (!gtk_text_iter_backward_char (&mv->insert))
1069
gtk_text_iter_forward_char (&mv->insert);
1074
ide_source_view_movements_sentence_start (Movement *mv)
1076
_ide_vim_iter_backward_sentence_start (&mv->insert);
1080
ide_source_view_movements_sentence_end (Movement *mv)
1082
_ide_vim_iter_forward_sentence_end (&mv->insert);
1086
ide_source_view_movements_line_percentage (Movement *mv)
1088
GtkTextBuffer *buffer;
1092
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
1093
gtk_text_buffer_get_end_iter (buffer, &end);
1094
end_line = gtk_text_iter_get_line (&end);
1098
gtk_text_iter_set_line (&mv->insert, 0);
1104
mv->count = MAX (1, mv->count);
1105
line = (float)end_line * (mv->count / 100.0);
1106
gtk_text_iter_set_line (&mv->insert, line);
1111
ide_source_view_movements_first_nonspace_char (mv);
1115
ide_source_view_movements_previous_unmatched (Movement *mv,
1124
g_assert (opposite);
1132
if (!gtk_text_iter_backward_char (&mv->insert))
1138
ch = gtk_text_iter_get_char (&mv->insert);
1142
else if (ch == opposite)
1148
gtk_text_iter_forward_char (&mv->insert);
1154
g_assert_not_reached ();
1158
ide_source_view_movements_next_unmatched (Movement *mv,
1167
g_assert (opposite);
1175
if (!gtk_text_iter_forward_char (&mv->insert))
1181
ch = gtk_text_iter_get_char (&mv->insert);
1185
else if (ch == opposite)
1191
gtk_text_iter_forward_char (&mv->insert);
1197
g_assert_not_reached ();
1201
find_match (gunichar ch,
1204
Movement *mv = data;
1206
return (mv->modifier == ch);
1210
ide_source_view_movements_next_match_modifier (Movement *mv)
1215
bounds = insert = mv->insert;
1216
gtk_text_iter_forward_to_line_end (&bounds);
1218
if (gtk_text_iter_forward_find_char (&insert, find_match, mv, &bounds))
1221
gtk_text_iter_forward_char (&insert);
1222
mv->insert = insert;
1227
ide_source_view_movements_previous_match_modifier (Movement *mv)
1232
bounds = insert = mv->insert;
1233
gtk_text_iter_set_line_offset (&bounds, 0);
1235
if (gtk_text_iter_backward_find_char (&insert, find_match, mv, &bounds))
1238
gtk_text_iter_forward_char (&insert);
1239
mv->insert = insert;
1244
_ide_source_view_apply_movement (IdeSourceView *self,
1245
IdeSourceViewMovement movement,
1246
gboolean extend_selection,
1250
gint *target_offset)
1252
Movement mv = { 0 };
1253
GtkTextBuffer *buffer;
1254
GtkTextMark *insert;
1257
g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
1259
#ifdef IDE_ENABLE_TRACE
1261
GEnumValue *enum_value;
1262
GEnumClass *enum_class;
1264
enum_class = g_type_class_ref (IDE_TYPE_SOURCE_VIEW_MOVEMENT);
1265
enum_value = g_enum_get_value (enum_class, movement);
1266
IDE_TRACE_MSG ("movement(%s, extend_selection=%s, exclusive=%s, count=%u)",
1267
enum_value->value_nick,
1268
extend_selection ? "YES" : "NO",
1269
exclusive ? "YES" : "NO",
1271
g_type_class_unref (enum_class);
1275
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
1276
insert = gtk_text_buffer_get_insert (buffer);
1279
mv.target_offset = target_offset;
1281
mv.extend_selection = extend_selection;
1282
mv.exclusive = exclusive;
1284
mv.ignore_select = FALSE;
1285
mv.ignore_target_offset = FALSE;
1286
mv.modifier = modifier;
1288
ide_source_view_movements_get_selection (&mv);
1292
case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_OFFSET:
1293
gtk_text_iter_backward_chars (&mv.insert, MAX (1, mv.count));
1296
case IDE_SOURCE_VIEW_MOVEMENT_NEXT_OFFSET:
1297
gtk_text_iter_forward_chars (&mv.insert, MAX (1, mv.count));
1300
case IDE_SOURCE_VIEW_MOVEMENT_NTH_CHAR:
1301
ide_source_view_movements_nth_char (&mv);
1304
case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_CHAR:
1305
ide_source_view_movements_previous_char (&mv);
1308
case IDE_SOURCE_VIEW_MOVEMENT_NEXT_CHAR:
1309
ide_source_view_movements_next_char (&mv);
1312
case IDE_SOURCE_VIEW_MOVEMENT_FIRST_CHAR:
1313
ide_source_view_movements_first_char (&mv);
1316
case IDE_SOURCE_VIEW_MOVEMENT_FIRST_NONSPACE_CHAR:
1317
ide_source_view_movements_first_nonspace_char (&mv);
1320
case IDE_SOURCE_VIEW_MOVEMENT_MIDDLE_CHAR:
1321
ide_source_view_movements_middle_char (&mv);
1324
case IDE_SOURCE_VIEW_MOVEMENT_LAST_CHAR:
1325
ide_source_view_movements_last_char (&mv);
1328
case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_FULL_WORD_START:
1329
for (i = MAX (1, mv.count); i > 0; i--)
1330
ide_source_view_movements_previous_full_word_start (&mv);
1333
case IDE_SOURCE_VIEW_MOVEMENT_NEXT_FULL_WORD_START:
1334
for (i = MAX (1, mv.count); i > 0; i--)
1335
ide_source_view_movements_next_full_word_start (&mv);
1338
case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_FULL_WORD_END:
1339
for (i = MAX (1, mv.count); i > 0; i--)
1340
ide_source_view_movements_previous_full_word_end (&mv);
1343
case IDE_SOURCE_VIEW_MOVEMENT_NEXT_FULL_WORD_END:
1344
for (i = MAX (1, mv.count); i > 0; i--)
1345
ide_source_view_movements_next_full_word_end (&mv);
1348
case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_WORD_START:
1349
for (i = MAX (1, mv.count); i > 0; i--)
1350
ide_source_view_movements_previous_word_start (&mv);
1353
case IDE_SOURCE_VIEW_MOVEMENT_NEXT_WORD_START:
1354
for (i = MAX (1, mv.count); i > 0; i--)
1355
ide_source_view_movements_next_word_start (&mv);
1358
case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_WORD_END:
1359
for (i = MAX (1, mv.count); i > 0; i--)
1360
ide_source_view_movements_previous_word_end (&mv);
1363
case IDE_SOURCE_VIEW_MOVEMENT_NEXT_WORD_END:
1364
for (i = MAX (1, mv.count); i > 0; i--)
1365
ide_source_view_movements_next_word_end (&mv);
1368
case IDE_SOURCE_VIEW_MOVEMENT_SENTENCE_START:
1369
for (i = MAX (1, mv.count); i > 0; i--)
1370
ide_source_view_movements_sentence_start (&mv);
1373
case IDE_SOURCE_VIEW_MOVEMENT_SENTENCE_END:
1374
for (i = MAX (1, mv.count); i > 0; i--)
1375
ide_source_view_movements_sentence_end (&mv);
1378
case IDE_SOURCE_VIEW_MOVEMENT_PARAGRAPH_START:
1379
for (i = MAX (1, mv.count); i > 0; i--)
1381
mv.exclusive = exclusive && i == 1;
1382
ide_source_view_movements_paragraph_start (&mv);
1386
case IDE_SOURCE_VIEW_MOVEMENT_PARAGRAPH_END:
1387
for (i = MAX (1, mv.count); i > 0; i--)
1389
mv.exclusive = exclusive && i == 1;
1390
ide_source_view_movements_paragraph_end (&mv);
1394
case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_LINE:
1395
mv.ignore_target_offset = TRUE;
1396
mv.ignore_select = TRUE;
1397
for (i = MAX (1, mv.count); i > 0; i--)
1398
ide_source_view_movements_previous_line (&mv);
1401
case IDE_SOURCE_VIEW_MOVEMENT_NEXT_LINE:
1402
mv.ignore_target_offset = TRUE;
1403
mv.ignore_select = TRUE;
1404
for (i = MAX (1, mv.count); i > 0; i--)
1405
ide_source_view_movements_next_line (&mv);
1408
case IDE_SOURCE_VIEW_MOVEMENT_FIRST_LINE:
1409
ide_source_view_movements_first_line (&mv);
1412
case IDE_SOURCE_VIEW_MOVEMENT_NTH_LINE:
1413
ide_source_view_movements_nth_line (&mv);
1416
case IDE_SOURCE_VIEW_MOVEMENT_LAST_LINE:
1417
ide_source_view_movements_last_line (&mv);
1420
case IDE_SOURCE_VIEW_MOVEMENT_LINE_PERCENTAGE:
1421
ide_source_view_movements_line_percentage (&mv);
1424
case IDE_SOURCE_VIEW_MOVEMENT_LINE_CHARS:
1425
ide_source_view_movements_line_chars (&mv);
1428
case IDE_SOURCE_VIEW_MOVEMENT_LINE_END:
1429
ide_source_view_movements_line_end (&mv);
1432
case IDE_SOURCE_VIEW_MOVEMENT_HALF_PAGE_UP:
1433
case IDE_SOURCE_VIEW_MOVEMENT_HALF_PAGE_DOWN:
1434
case IDE_SOURCE_VIEW_MOVEMENT_PAGE_UP:
1435
case IDE_SOURCE_VIEW_MOVEMENT_PAGE_DOWN:
1436
for (i = MAX (1, mv.count); i > 0; i--)
1437
ide_source_view_movements_move_page (&mv);
1440
case IDE_SOURCE_VIEW_MOVEMENT_SCREEN_DOWN:
1441
case IDE_SOURCE_VIEW_MOVEMENT_SCREEN_UP:
1442
for (i = MAX (1, mv.count); i > 0; i--)
1443
ide_source_view_movements_scroll (&mv);
1446
case IDE_SOURCE_VIEW_MOVEMENT_SCREEN_TOP:
1447
ide_source_view_movements_screen_top (&mv);
1450
case IDE_SOURCE_VIEW_MOVEMENT_SCREEN_MIDDLE:
1451
ide_source_view_movements_screen_middle (&mv);
1454
case IDE_SOURCE_VIEW_MOVEMENT_SCREEN_BOTTOM:
1455
ide_source_view_movements_screen_bottom (&mv);
1458
case IDE_SOURCE_VIEW_MOVEMENT_MATCH_SPECIAL:
1459
ide_source_view_movements_match_special (&mv);
1462
case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_TOP:
1463
case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_CENTER:
1464
case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_BOTTOM:
1465
ide_source_view_movements_scroll_center (&mv);
1468
case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_UNMATCHED_BRACE:
1469
for (i = MAX (1, mv.count); i > 0; i--)
1470
ide_source_view_movements_previous_unmatched (&mv, '{', '}');
1473
case IDE_SOURCE_VIEW_MOVEMENT_NEXT_UNMATCHED_BRACE:
1474
for (i = MAX (1, mv.count); i > 0; i--)
1475
ide_source_view_movements_next_unmatched (&mv, '}', '{');
1478
case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_UNMATCHED_PAREN:
1479
for (i = MAX (1, mv.count); i > 0; i--)
1480
ide_source_view_movements_previous_unmatched (&mv, '(', ')');
1483
case IDE_SOURCE_VIEW_MOVEMENT_NEXT_UNMATCHED_PAREN:
1484
for (i = MAX (1, mv.count); i > 0; i--)
1485
ide_source_view_movements_next_unmatched (&mv, ')', '(');
1488
case IDE_SOURCE_VIEW_MOVEMENT_NEXT_MATCH_MODIFIER:
1489
for (i = MAX (1, mv.count); i > 0; i--)
1490
ide_source_view_movements_next_match_modifier (&mv);
1493
case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_MATCH_MODIFIER:
1494
for (i = MAX (1, mv.count); i > 0; i--)
1495
ide_source_view_movements_previous_match_modifier (&mv);
1499
g_return_if_reached ();
1502
if (!mv.ignore_select)
1503
ide_source_view_movements_select_range (&mv);
1505
if (!mv.ignore_target_offset)
1506
*target_offset = gtk_text_iter_get_line_offset (&mv.insert);
1508
if (!mv.ignore_scroll_to_insert)
1509
ide_source_view_scroll_mark_onscreen (self, insert, TRUE, 0.5, 0.5);