diff --git a/plugins/markdown-actions/markdown-actions.vala b/plugins/markdown-actions/markdown-actions.vala index d7efe7c8bc..d15e0335fa 100644 --- a/plugins/markdown-actions/markdown-actions.vala +++ b/plugins/markdown-actions/markdown-actions.vala @@ -94,25 +94,29 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services if (keyval == Gdk.Key.Return) { char ul_marker; - int ol_number = 1; + int ol_number; + string prefix; string item_text; var line = get_current_line (); - if (parse_unordered_list_item (line, out ul_marker)) { - if (line.length <= 3) { // empty item - delete_empty_item (); + + if (parse_ordered_list_item (line, out ol_number, out item_text, out prefix, null)) { + if (item_text.strip () == "") { + delete_empty_item (prefix); } else { - string to_insert = "\n%c ".printf (ul_marker); + string to_insert = "\n%s%d. ".printf (prefix, ++ol_number); current_source.buffer.insert_at_cursor (to_insert, to_insert.length); + // Check following lines to see if renumbering required + fix_ordered_list_numbering (prefix.length, ol_number); } return true; - } else if (parse_ordered_list_item (line, ref ol_number, out item_text)) { - if (item_text.length == 0) { - delete_empty_item (); - } else { - string to_insert = "\n%d. ".printf (ol_number + 1); + } else if (parse_unordered_list_item (line, out ul_marker, out item_text, out prefix)) { + if (item_text.strip () == "") { + delete_empty_item (prefix); + } else { // ul_marker is not null here + string to_insert = "\n%s%c ".printf (prefix, ul_marker); current_source.buffer.insert_at_cursor (to_insert, to_insert.length); - fix_ordered_list_numbering (); } + return true; } } @@ -120,7 +124,7 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services return false; } - private void delete_empty_item () { + private void delete_empty_item (string prefix = "") { Gtk.TextIter start, end; var current_buffer = current_source.buffer; current_buffer.get_iter_at_offset (out start, current_buffer.cursor_position); @@ -128,35 +132,48 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services end = start; end.forward_to_line_end (); current_buffer.delete (ref start, ref end); - current_buffer.insert_at_cursor ("\n", 1); + var _prefix = prefix; + if (prefix.strip () == "") { + _prefix = ""; + } + current_buffer.insert_at_cursor ("%s".printf (_prefix), _prefix.length); + current_buffer.get_iter_at_offset (out start, current_buffer.cursor_position); } - private void fix_ordered_list_numbering () { + // Starting on the line where a numbered list item was inserted, check if renumbering required + private void fix_ordered_list_numbering (int indent_spaces, int inserted_number) { Gtk.TextIter next; - var count = 1; - var item_text = ""; var current_buffer = current_source.buffer; current_buffer.get_iter_at_offset (out next, current_buffer.cursor_position); - var line = get_current_line (next).strip (); - // Get list item number from current line - parse_ordered_list_item (line, ref count, out item_text); - while ( - next.forward_line () && - parse_ordered_list_item (get_current_line (next).strip (), ref count, out item_text) + int point_offset = 0, count = inserted_number, next_count = 0; + string item_text = "", next_prefix = ""; + // Search for ordered list lines at the same level until level falls below or end of doc + while (next.forward_line () && + parse_ordered_list_item ( + get_current_line (next), + out next_count, + out item_text, + out next_prefix, + out point_offset + ) && + next_prefix.length >= indent_spaces ) { - count++; - var next_mark = current_buffer.create_mark (null, next, true); - var point_offset = line.index_of_char ('.'); - var start = next; - var end = start; - end.forward_chars (point_offset); - - current_buffer.delete (ref start, ref end); - current_buffer.get_iter_at_mark (out next, next_mark); - - var to_insert = "%d".printf (count); - current_buffer.insert (ref next, to_insert, to_insert.length); + // Only update lines at same indent within same block + if (next_prefix.length == indent_spaces) { + count++; + next.forward_chars (indent_spaces); + var next_mark = current_buffer.create_mark (null, next, true); + var start = next; + var end = start; + end.forward_chars (point_offset - indent_spaces); + + current_buffer.delete (ref start, ref end); + current_buffer.get_iter_at_mark (out next, next_mark); + + var to_insert = "%d".printf (count); + current_buffer.insert (ref next, to_insert, to_insert.length); + } } } @@ -172,32 +189,84 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services end = start; end.forward_to_line_end (); - return current_buffer.get_text (start, end, false); + var cl = current_buffer.get_text (start, end, false); + return cl; } - private bool parse_ordered_list_item (string line, ref int current_number, out string item_text) { + private bool parse_ordered_list_item ( + string line, + out int current_number, + out string item_text, + out string prefix, + out int first_point_pos + ) { item_text = ""; - int first_point_character = line.index_of_char ('.'); - if (first_point_character < 0) { - return false; - } + prefix = ""; + current_number = -1; + // Github does not automatically handle sublists in numbered lists so neither do we for now + // We assume the first number point is the only one. + first_point_pos = line.index_of_char ('.'); //TODO Handle ")" Ignored escaped? + + item_text = line.substring (first_point_pos + 1).strip (); - item_text = line.substring (first_point_character + 1).strip (); + var line_start = line.substring (0, first_point_pos); + line_start = line_start.replace ("-", " ").replace ("*", " "); + prefix = line.substring (0, line_start.last_index_of_char (' ') + 1); - var line_start = line.substring (0, first_point_character); if (!int.try_parse (line_start, out current_number)) { return false; } - return true; + + return current_number >= 1; } - private bool parse_unordered_list_item (string line, out char ul_marker) { - if ((line[0] == '*' || line[0] == '-') && line[1] == ' ') { - ul_marker = line[0]; - return true; + private bool parse_unordered_list_item ( + string line, + out char ul_marker, + out string item_text, + out string prefix + ) { + prefix = ""; + item_text = ""; + ul_marker = ' '; + + // Scan line for last unordered list marker + unichar uc = 0; + int index = 0; + uint spaces = 0; + char? last_marker = null; + while (line.get_next_char (ref index, out uc)) { + string buf = " "; + var i = uc.to_utf8 (buf); + if (i != 1) { + break; + } + + var c = buf[0]; + if (c.isspace ()) { + spaces++; + // Only allow one space between or after markers but any number before the first + if (last_marker != null && spaces > 1) { + break; + } + } else if (c == '-' || c == '*') { + spaces = 0; + last_marker = c; + } else { // text item starts here + break; + } } - ul_marker = '\0'; - return false; + + if (last_marker == null) { + return false; + } + + ul_marker = last_marker; + item_text = line.substring (index - 1); + // The item text might contain marker characters so take care to ignore those + prefix = line.substring (0, index - 1); + prefix = prefix.substring (0, prefix.last_index_of_char (ul_marker)); + return true; } private void insert_link () {