Class: Todoloo::Parser

Inherits:
Parslet::Parser
  • Object
show all
Defined in:
lib/todoloo/parser.rb

Overview

Constant Summary collapse

TASK_TYPES =
%w[TODO NOTE FIXME HBD HACK XXX]
MIN_INDENTATION_BEYOND_TYPE_START =
1

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.rule(name, *args, **kwargs, &block) ⇒ Object

Extension to automatically define ‘?` predicate rules



28
29
30
31
32
33
34
35
# File 'lib/todoloo/parser.rb', line 28

def self.rule(name, *args, **kwargs, &block)
  if name.to_s.end_with?("?")
    super
  else
    super # Define the original rule
    super("#{name}?") { public_send(name).maybe }
  end
end

Instance Method Details

#column_offset_of_capture(name, context) ⇒ Object

Calculates zero-based column offset for the given capture. Use only within dynamic blocks



115
116
117
118
119
120
121
122
123
124
125
# File 'lib/todoloo/parser.rb', line 115

def column_offset_of_capture(name, context)
  case name
  when :type
    # FIXME: Not sure yet why this is even necessary...
    _, col_index = context.captures[:type][:type].line_and_column
  else
    _, col_index = context.captures[name].line_and_column
  end

  col_index - 1
end

#comment_continuationObject



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/todoloo/parser.rb', line 88

def comment_continuation
  dynamic do |_source, context|
    parser = match('[\n]')

    comment_start = column_offset_of_capture(:comment_start, context)

    # Match any non comment content before the comment starts
    parser = ignore_code_text(parser, comment_start)

    # Consume the characther that the comment opened with, like a `#` or `//`
    parser >>= str(context.captures[:comment_start]).ignore

    parser >>= (spacing? >> task_type).absent?

    required_indentation = column_offset_of_capture(:type, context) - comment_start - context.captures[:comment_start].length + MIN_INDENTATION_BEYOND_TYPE_START

    parser = indentation(parser, required_indentation)

    # parser >>= task_type.absent?

    parser >>= description_text
    parser
  end
end

#ignore_code_text(parser, count) ⇒ Object



127
128
129
130
131
132
133
# File 'lib/todoloo/parser.rb', line 127

def ignore_code_text(parser, count)
  if count.positive?
    parser >> code_text.repeat(count, count).ignore
  else
    parser
  end
end

#indentation(parser, count) ⇒ Object



135
136
137
138
139
# File 'lib/todoloo/parser.rb', line 135

def indentation(parser, count)
  return parser unless count.positive?

  parser >> space.repeat(count, count)
end

#parse_and_transform(text, path: "") ⇒ Object

Returns a list of tasks



8
9
10
11
12
13
14
15
16
17
18
# File 'lib/todoloo/parser.rb', line 8

def parse_and_transform(text, path: "")
  tree = parse(text)

  pp tree if Todoloo.debug?

  Transformer.new.apply(tree).map do |hash|
    line, column = hash.fetch(:type).line_and_column

    Task.from_hash(hash.merge(path: path, line: line, column: column))
  end
end

#safe_parse(text) ⇒ Object



20
21
22
23
24
25
# File 'lib/todoloo/parser.rb', line 20

def safe_parse(text)
  parse(text)
rescue Parslet::ParseFailed => e
  puts e.parse_failure_cause.ascii_tree
  nil
end