Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ namespace Scratch {
public const string ACTION_COLLAPSE_ALL_FOLDERS = "action-collapse-all-folders";
public const string ACTION_ORDER_FOLDERS = "action-order-folders";
public const string ACTION_GO_TO = "action-go-to";
public const string ACTION_GO_TO_MATCHING = "action-go-to-matching";
public const string ACTION_SORT_LINES = "action-sort-lines";
public const string ACTION_NEW_TAB = "action-new-tab";
public const string ACTION_NEW_FROM_CLIPBOARD = "action-new-from-clipboard";
Expand Down Expand Up @@ -149,6 +150,7 @@ namespace Scratch {
{ ACTION_TOGGLE_SHOW_FIND, action_toggle_show_find, null, "false" },
{ ACTION_TEMPLATES, action_templates },
{ ACTION_GO_TO, action_go_to },
{ ACTION_GO_TO_MATCHING, action_go_to_matching },
{ ACTION_SORT_LINES, action_sort_lines },
{ ACTION_NEW_TAB, action_new_tab },
{ ACTION_NEW_FROM_CLIPBOARD, action_new_tab_from_clipboard },
Expand Down Expand Up @@ -214,6 +216,7 @@ namespace Scratch {
action_accelerators.set (ACTION_SAVE, "<Control>s");
action_accelerators.set (ACTION_SAVE_AS, "<Control><shift>s");
action_accelerators.set (ACTION_GO_TO, "<Control>i");
action_accelerators.set (ACTION_GO_TO_MATCHING, "<Control><Shift>i");
action_accelerators.set (ACTION_SORT_LINES, "F5");
action_accelerators.set (ACTION_NEW_TAB, "<Control>n");
action_accelerators.set (ACTION_DUPLICATE_TAB, "<Control><Shift>k" );
Expand Down Expand Up @@ -1371,6 +1374,15 @@ namespace Scratch {
toolbar.format_bar.line_menubutton.active = true;
}

private void action_go_to_matching () {
var doc = document_view.current_document;
if (doc == null) {
return;
} else {
doc.source_view.goto_matching ();
}
}

private void action_templates () {
plugins.plugin_iface.template_manager.show_window (this);
}
Expand Down
21 changes: 20 additions & 1 deletion src/Services/CommentToggler.vala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public class Scratch.CommentToggler {
private enum CommentType {
NONE,
LINE,
BLOCK
BLOCK,
EMPTY
}

private static CommentType get_comment_tags_for_lang (Gtk.SourceLanguage lang,
Expand Down Expand Up @@ -73,6 +74,20 @@ public class Scratch.CommentToggler {
return type != CommentType.NONE;
}

public static bool line_is_commented_or_empty (
Gtk.SourceBuffer buffer,
int line_index,
Gtk.SourceLanguage lang) {

bool commented = false;
Gtk.TextIter start_iter, end_iter;
buffer.get_iter_at_line_index (out start_iter, line_index, 0);
buffer.get_iter_at_line_index (out end_iter, line_index, int.MAX); //Returns end of line iter
return lines_already_commented (
buffer, start_iter, end_iter, 1, lang
) != CommentType.NONE;
}

// Returns whether or not all lines within a region are already commented.
// This is to detect whether to toggle comments on or off. If all lines are commented, then we want to remove
// those comments. If only some are commented, then the user likely selected a chunk of code that already contained
Expand All @@ -86,6 +101,10 @@ public class Scratch.CommentToggler {
string start_tag, end_tag;
var type = get_comment_tags_for_lang (lang, CommentType.BLOCK, out start_tag, out end_tag);
var selection = buffer.get_slice (start, end, true);
if (selection.length == 0) {
return CommentType.EMPTY;
}

if (type == CommentType.BLOCK) {
var regex_string = """^\s*(?:%s)+[\s\S]*(?:%s)+$""";
regex_string = regex_string.printf (Regex.escape_string (start_tag), Regex.escape_string (end_tag));
Expand Down
145 changes: 145 additions & 0 deletions src/Widgets/SourceView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,13 @@ namespace Scratch.Widgets {
}

menu.add (comment_item);

if (true) {//TODO Check preceding char is bracket
var match_item = new Gtk.MenuItem.with_label (_("Goto Matching Bracket")) {
action_name = MainWindow.ACTION_PREFIX + MainWindow.ACTION_GO_TO_MATCHING
};
menu.add (match_item);
}
}

menu.show_all ();
Expand Down Expand Up @@ -704,5 +711,143 @@ namespace Scratch.Widgets {
return Source.REMOVE;
});
}

private const string OPEN_BRACKETS = "{([";
private const string CLOSE_BRACKETS = "})]";
private bool is_open_bracket (unichar c, out unichar matching) {
var index = OPEN_BRACKETS.index_of_char (c);
if (index >= 0) {
matching = CLOSE_BRACKETS[index];
return true;
}

return false;
}

private bool is_close_bracket (unichar c, out unichar matching) {
var index = CLOSE_BRACKETS.index_of_char (c);
if (index >= 0) {
matching = OPEN_BRACKETS[index];
return true;
}

return false;
}

private uint get_indent_spaces (Gtk.TextIter t) {
var indent_iter = t.copy ();
if (indent_iter.backward_line ()) {
indent_iter.forward_line ();
};
uint spaces = 0;
while (indent_iter.forward_char () && indent_iter.get_char ().isspace ()) {
spaces++;
}

return spaces;
}

// Return index of next uncommented, not empty line
private bool skip_commented_lines (out int new_index, int start_index, int step = 1) {
new_index = start_index;
while (CommentToggler.line_is_commented_or_empty (
(Gtk.SourceBuffer)buffer, new_index, language
)) {
new_index += step;
}

return new_index != start_index;
}

public void goto_matching () {
uint start_indent = 0, end_indent = 0, same = 0;
int start_line = -1, end_line = -1, new_line = -1;
unichar c;
bool found = false;
var insert_mark = buffer.get_mark ("insert");
Gtk.TextIter insert_iter, start_iter, end_iter;
buffer.get_iter_at_mark (out insert_iter, insert_mark);
start_line = insert_iter.get_line ();
if (skip_commented_lines (out new_line, start_line)) {
return;
}

start_line++; // Change from index to visible number
insert_iter.backward_char ();
var insert_char = insert_iter.get_char ();
var end = insert_iter.copy ();
unichar matching;
if (is_open_bracket (insert_char, out matching)) {
start_indent = get_indent_spaces (insert_iter);
end_line = buffer.get_line_count ();
while (end.forward_char ()) {
if (end.starts_line () && skip_commented_lines (out new_line, end.get_line ())) {
end.set_line (new_line);
continue;
}
c = end.get_char ();

if (is_open_bracket (c, out matching)) {
same++;
} else if (is_close_bracket (c, out matching)) {
if (same > 0) {
same--;
} else {
end_indent = get_indent_spaces (end);
end_line = end.get_line () + 1;
warning ("end line %i", end_line);
end.forward_char ();
buffer.place_cursor (end);
scroll_to_iter (end, 0.1, false, 0.0, 0.0);
found = true;
break;
}
}
}
} else if (is_close_bracket (insert_char, out matching)) {
start_indent = get_indent_spaces (insert_iter);
end_line = 0;
while (end.backward_char ()) {
if (end.ends_line () && skip_commented_lines (out new_line, end.get_line (), -1)) {
end.set_line (new_line);
end.forward_to_line_end ();
continue;
}
c = end.get_char ();
if (is_close_bracket (c, out matching)) {
same++;
} else if (is_open_bracket (c, out matching)) {
if (same > 0) {
same--;
} else {
end_indent = get_indent_spaces (end);
end_line = end.get_line () + 1;
end.forward_char ();
buffer.place_cursor (end);
scroll_to_iter (end, 0.1, false, 0.0, 0.0);
found = true;
break;
}
}
}
} else {
return;
}

if (start_indent != end_indent || !found) {
var min_line = int.min (start_line, end_line);
var max_line = int.max (start_line, end_line);
var parent_window = get_toplevel () as Gtk.Window;
var dialog = new Granite.MessageDialog (
found ? _("Matching bracket has incorrect indent") : _("No matching bracket found"),
_("You may have omitted a required bracket or inserted an extra bracket between lines %i and %i").printf (min_line, max_line),
new ThemedIcon ("dialog-warning"),
Gtk.ButtonsType.CLOSE
);
dialog.transient_for = parent_window;
dialog.response.connect (dialog.destroy);
dialog.present ();
}
}
}
}