|
1 | 1 | local M = {} |
2 | 2 |
|
3 | 3 | ---@alias checkmate.TodoItemState "checked" | "unchecked" |
| 4 | +--@alias checkmate.TodoItemState "checked" | "unchecked" |
4 | 5 |
|
5 | 6 | --- @class TodoMarkerInfo |
6 | 7 | --- @field position {row: integer, col: integer} Position of the marker (0-indexed) |
@@ -63,7 +64,8 @@ local FULL_TODO_QUERY = vim.treesitter.query.parse( |
63 | 64 | ]] |
64 | 65 | ) |
65 | 66 |
|
66 | | -local METADATA_PATTERN = "@([%a][%w_%-]*)%(%s*(.-)%s*%)" |
| 67 | +-- match the @tag and then a balanced sequence %b(), which is everything in the outer parentheses |
| 68 | +local METADATA_PATTERN = "@([%a][%w_%-]*)(%b())" |
67 | 69 |
|
68 | 70 | M.list_item_markers = { "-", "+", "*" } |
69 | 71 |
|
@@ -774,27 +776,44 @@ function M.extract_metadata(line, row) |
774 | 776 | by_tag = {}, |
775 | 777 | } |
776 | 778 |
|
| 779 | + ---@param _line string String to search |
| 780 | + ---@param from_byte_pos integer start looking at this byte position |
| 781 | + ---@return string? tag, string? value, integer? start_byte, integer? end_byte |
| 782 | + local function find_metadata(_line, from_byte_pos) |
| 783 | + local s, e, tag, raw = _line:find(METADATA_PATTERN, from_byte_pos) |
| 784 | + if not s or not e then |
| 785 | + return nil |
| 786 | + end |
| 787 | + |
| 788 | + -- raw is "( ... )", so we gotta strip outer parens |
| 789 | + local inner = raw:sub(2, -2) |
| 790 | + |
| 791 | + -- trim whitespace |
| 792 | + local value = inner:match("^%s*(.-)%s*$") |
| 793 | + |
| 794 | + return tag, value, s, e |
| 795 | + end |
| 796 | + |
777 | 797 | -- find all @tag(value) patterns and their positions |
778 | 798 | local byte_pos = 1 |
779 | 799 | while true do |
780 | | - -- will capture tag names that include underscores and hypens |
781 | | - local tag_start_byte, tag_end_byte, tag, value = line:find(METADATA_PATTERN, byte_pos) |
782 | | - if not tag_start_byte or not tag_end_byte then |
| 800 | + local tag, value, start_byte, end_byte = find_metadata(line, byte_pos) |
| 801 | + if not tag or not start_byte or not end_byte then |
783 | 802 | break |
784 | 803 | end |
785 | 804 |
|
786 | 805 | ---@type checkmate.MetadataEntry |
787 | 806 | local entry = { |
788 | 807 | tag = tag, |
789 | | - value = value, |
| 808 | + value = value or "", |
790 | 809 | range = { |
791 | | - start = { row = row, col = tag_start_byte - 1 }, -- 0-indexed column |
| 810 | + start = { row = row, col = start_byte - 1 }, -- 0-indexed column |
792 | 811 | -- For the end col, we need 0 indexed (subtract 1) and since it is end-exclusive we add 1, cancelling out |
793 | 812 | -- end-exclusive means the end col points to the pos after the last char |
794 | | - ["end"] = { row = row, col = tag_end_byte }, |
| 813 | + ["end"] = { row = row, col = end_byte }, |
795 | 814 | }, |
796 | 815 | alias_for = nil, -- Will be set later if it's an alias |
797 | | - position_in_line = tag_start_byte, -- track original position in the line, use byte pos for sorting |
| 816 | + position_in_line = start_byte, -- track original position in the line, use byte pos for sorting |
798 | 817 | } |
799 | 818 |
|
800 | 819 | -- check if this is an alias and map to canonical name |
@@ -827,10 +846,10 @@ function M.extract_metadata(line, row) |
827 | 846 | end |
828 | 847 |
|
829 | 848 | -- move position for next search |
830 | | - byte_pos = tag_end_byte + 1 |
| 849 | + byte_pos = end_byte + 1 |
831 | 850 |
|
832 | 851 | log.debug( |
833 | | - string.format("Metadata found: %s=%s at [%d,%d]-[%d,%d]", tag, value, row, tag_start_byte - 1, row, tag_end_byte), |
| 852 | + string.format("Metadata found: %s=%s at [%d,%d]-[%d,%d]", tag, value, row, start_byte - 1, row, end_byte), |
834 | 853 | { module = "parser" } |
835 | 854 | ) |
836 | 855 | end |
|
0 commit comments