markly 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/bin/markly +94 -0
  3. data/ext/markly/arena.c +103 -0
  4. data/ext/markly/autolink.c +425 -0
  5. data/ext/markly/autolink.h +8 -0
  6. data/ext/markly/blocks.c +1585 -0
  7. data/ext/markly/buffer.c +278 -0
  8. data/ext/markly/buffer.h +116 -0
  9. data/ext/markly/case_fold_switch.inc +4327 -0
  10. data/ext/markly/chunk.h +135 -0
  11. data/ext/markly/cmark-gfm-core-extensions.h +54 -0
  12. data/ext/markly/cmark-gfm-extension_api.h +736 -0
  13. data/ext/markly/cmark-gfm-extensions_export.h +42 -0
  14. data/ext/markly/cmark-gfm.h +817 -0
  15. data/ext/markly/cmark-gfm_export.h +42 -0
  16. data/ext/markly/cmark-gfm_version.h +7 -0
  17. data/ext/markly/cmark.c +55 -0
  18. data/ext/markly/cmark_ctype.c +44 -0
  19. data/ext/markly/cmark_ctype.h +33 -0
  20. data/ext/markly/commonmark.c +519 -0
  21. data/ext/markly/config.h +76 -0
  22. data/ext/markly/core-extensions.c +27 -0
  23. data/ext/markly/entities.inc +2138 -0
  24. data/ext/markly/ext_scanners.c +1159 -0
  25. data/ext/markly/ext_scanners.h +24 -0
  26. data/ext/markly/extconf.rb +7 -0
  27. data/ext/markly/footnotes.c +40 -0
  28. data/ext/markly/footnotes.h +25 -0
  29. data/ext/markly/houdini.h +57 -0
  30. data/ext/markly/houdini_href_e.c +100 -0
  31. data/ext/markly/houdini_html_e.c +66 -0
  32. data/ext/markly/houdini_html_u.c +149 -0
  33. data/ext/markly/html.c +465 -0
  34. data/ext/markly/html.h +27 -0
  35. data/ext/markly/inlines.c +1633 -0
  36. data/ext/markly/inlines.h +29 -0
  37. data/ext/markly/iterator.c +159 -0
  38. data/ext/markly/iterator.h +26 -0
  39. data/ext/markly/latex.c +466 -0
  40. data/ext/markly/linked_list.c +37 -0
  41. data/ext/markly/man.c +278 -0
  42. data/ext/markly/map.c +122 -0
  43. data/ext/markly/map.h +41 -0
  44. data/ext/markly/markly.c +1226 -0
  45. data/ext/markly/markly.h +16 -0
  46. data/ext/markly/node.c +979 -0
  47. data/ext/markly/node.h +118 -0
  48. data/ext/markly/parser.h +58 -0
  49. data/ext/markly/plaintext.c +235 -0
  50. data/ext/markly/plugin.c +36 -0
  51. data/ext/markly/plugin.h +34 -0
  52. data/ext/markly/references.c +42 -0
  53. data/ext/markly/references.h +26 -0
  54. data/ext/markly/registry.c +63 -0
  55. data/ext/markly/registry.h +24 -0
  56. data/ext/markly/render.c +205 -0
  57. data/ext/markly/render.h +62 -0
  58. data/ext/markly/scanners.c +20382 -0
  59. data/ext/markly/scanners.h +62 -0
  60. data/ext/markly/scanners.re +326 -0
  61. data/ext/markly/strikethrough.c +167 -0
  62. data/ext/markly/strikethrough.h +9 -0
  63. data/ext/markly/syntax_extension.c +149 -0
  64. data/ext/markly/syntax_extension.h +34 -0
  65. data/ext/markly/table.c +803 -0
  66. data/ext/markly/table.h +12 -0
  67. data/ext/markly/tagfilter.c +60 -0
  68. data/ext/markly/tagfilter.h +8 -0
  69. data/ext/markly/tasklist.c +156 -0
  70. data/ext/markly/tasklist.h +8 -0
  71. data/ext/markly/utf8.c +317 -0
  72. data/ext/markly/utf8.h +35 -0
  73. data/ext/markly/xml.c +181 -0
  74. data/lib/markly.rb +43 -0
  75. data/lib/markly/flags.rb +37 -0
  76. data/lib/markly/markly.so +0 -0
  77. data/lib/markly/node.rb +70 -0
  78. data/lib/markly/node/inspect.rb +59 -0
  79. data/lib/markly/renderer.rb +133 -0
  80. data/lib/markly/renderer/html_renderer.rb +252 -0
  81. data/lib/markly/version.rb +5 -0
  82. metadata +211 -0
@@ -0,0 +1,35 @@
1
+ #ifndef CMARK_UTF8_H
2
+ #define CMARK_UTF8_H
3
+
4
+ #include <stdint.h>
5
+ #include "buffer.h"
6
+
7
+ #ifdef __cplusplus
8
+ extern "C" {
9
+ #endif
10
+
11
+ CMARK_GFM_EXPORT
12
+ void cmark_utf8proc_case_fold(cmark_strbuf *dest, const uint8_t *str,
13
+ bufsize_t len);
14
+
15
+ CMARK_GFM_EXPORT
16
+ void cmark_utf8proc_encode_char(int32_t uc, cmark_strbuf *buf);
17
+
18
+ CMARK_GFM_EXPORT
19
+ int cmark_utf8proc_iterate(const uint8_t *str, bufsize_t str_len, int32_t *dst);
20
+
21
+ CMARK_GFM_EXPORT
22
+ void cmark_utf8proc_check(cmark_strbuf *dest, const uint8_t *line,
23
+ bufsize_t size);
24
+
25
+ CMARK_GFM_EXPORT
26
+ int cmark_utf8proc_is_space(int32_t uc);
27
+
28
+ CMARK_GFM_EXPORT
29
+ int cmark_utf8proc_is_punctuation(int32_t uc);
30
+
31
+ #ifdef __cplusplus
32
+ }
33
+ #endif
34
+
35
+ #endif
@@ -0,0 +1,181 @@
1
+ #include <stdlib.h>
2
+ #include <stdio.h>
3
+ #include <string.h>
4
+ #include <assert.h>
5
+
6
+ #include "config.h"
7
+ #include "cmark-gfm.h"
8
+ #include "node.h"
9
+ #include "buffer.h"
10
+ #include "houdini.h"
11
+ #include "syntax_extension.h"
12
+
13
+ #define BUFFER_SIZE 100
14
+
15
+ // Functions to convert cmark_nodes to XML strings.
16
+
17
+ static void escape_xml(cmark_strbuf *dest, const unsigned char *source,
18
+ bufsize_t length) {
19
+ houdini_escape_html0(dest, source, length, 0);
20
+ }
21
+
22
+ struct render_state {
23
+ cmark_strbuf *xml;
24
+ int indent;
25
+ };
26
+
27
+ static CMARK_INLINE void indent(struct render_state *state) {
28
+ int i;
29
+ for (i = 0; i < state->indent; i++) {
30
+ cmark_strbuf_putc(state->xml, ' ');
31
+ }
32
+ }
33
+
34
+ static int S_render_node(cmark_node *node, cmark_event_type ev_type,
35
+ struct render_state *state, int options) {
36
+ cmark_strbuf *xml = state->xml;
37
+ bool literal = false;
38
+ cmark_delim_type delim;
39
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
40
+ char buffer[BUFFER_SIZE];
41
+
42
+ if (entering) {
43
+ indent(state);
44
+ cmark_strbuf_putc(xml, '<');
45
+ cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
46
+
47
+ if (options & CMARK_OPT_SOURCEPOS && node->start_line != 0) {
48
+ snprintf(buffer, BUFFER_SIZE, " sourcepos=\"%d:%d-%d:%d\"",
49
+ node->start_line, node->start_column, node->end_line,
50
+ node->end_column);
51
+ cmark_strbuf_puts(xml, buffer);
52
+ }
53
+
54
+ if (node->extension && node->extension->xml_attr_func) {
55
+ const char* r = node->extension->xml_attr_func(node->extension, node);
56
+ if (r != NULL)
57
+ cmark_strbuf_puts(xml, r);
58
+ }
59
+
60
+ literal = false;
61
+
62
+ switch (node->type) {
63
+ case CMARK_NODE_DOCUMENT:
64
+ cmark_strbuf_puts(xml, " xmlns=\"http://commonmark.org/xml/1.0\"");
65
+ break;
66
+ case CMARK_NODE_TEXT:
67
+ case CMARK_NODE_CODE:
68
+ case CMARK_NODE_HTML_BLOCK:
69
+ case CMARK_NODE_HTML_INLINE:
70
+ cmark_strbuf_puts(xml, " xml:space=\"preserve\">");
71
+ escape_xml(xml, node->as.literal.data, node->as.literal.len);
72
+ cmark_strbuf_puts(xml, "</");
73
+ cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
74
+ literal = true;
75
+ break;
76
+ case CMARK_NODE_LIST:
77
+ switch (cmark_node_get_list_type(node)) {
78
+ case CMARK_ORDERED_LIST:
79
+ cmark_strbuf_puts(xml, " type=\"ordered\"");
80
+ snprintf(buffer, BUFFER_SIZE, " start=\"%d\"",
81
+ cmark_node_get_list_start(node));
82
+ cmark_strbuf_puts(xml, buffer);
83
+ delim = cmark_node_get_list_delim(node);
84
+ if (delim == CMARK_PAREN_DELIM) {
85
+ cmark_strbuf_puts(xml, " delim=\"paren\"");
86
+ } else if (delim == CMARK_PERIOD_DELIM) {
87
+ cmark_strbuf_puts(xml, " delim=\"period\"");
88
+ }
89
+ break;
90
+ case CMARK_BULLET_LIST:
91
+ cmark_strbuf_puts(xml, " type=\"bullet\"");
92
+ break;
93
+ default:
94
+ break;
95
+ }
96
+ snprintf(buffer, BUFFER_SIZE, " tight=\"%s\"",
97
+ (cmark_node_get_list_tight(node) ? "true" : "false"));
98
+ cmark_strbuf_puts(xml, buffer);
99
+ break;
100
+ case CMARK_NODE_HEADING:
101
+ snprintf(buffer, BUFFER_SIZE, " level=\"%d\"", node->as.heading.level);
102
+ cmark_strbuf_puts(xml, buffer);
103
+ break;
104
+ case CMARK_NODE_CODE_BLOCK:
105
+ if (node->as.code.info.len > 0) {
106
+ cmark_strbuf_puts(xml, " info=\"");
107
+ escape_xml(xml, node->as.code.info.data, node->as.code.info.len);
108
+ cmark_strbuf_putc(xml, '"');
109
+ }
110
+ cmark_strbuf_puts(xml, " xml:space=\"preserve\">");
111
+ escape_xml(xml, node->as.code.literal.data, node->as.code.literal.len);
112
+ cmark_strbuf_puts(xml, "</");
113
+ cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
114
+ literal = true;
115
+ break;
116
+ case CMARK_NODE_CUSTOM_BLOCK:
117
+ case CMARK_NODE_CUSTOM_INLINE:
118
+ cmark_strbuf_puts(xml, " on_enter=\"");
119
+ escape_xml(xml, node->as.custom.on_enter.data,
120
+ node->as.custom.on_enter.len);
121
+ cmark_strbuf_putc(xml, '"');
122
+ cmark_strbuf_puts(xml, " on_exit=\"");
123
+ escape_xml(xml, node->as.custom.on_exit.data,
124
+ node->as.custom.on_exit.len);
125
+ cmark_strbuf_putc(xml, '"');
126
+ break;
127
+ case CMARK_NODE_LINK:
128
+ case CMARK_NODE_IMAGE:
129
+ cmark_strbuf_puts(xml, " destination=\"");
130
+ escape_xml(xml, node->as.link.url.data, node->as.link.url.len);
131
+ cmark_strbuf_putc(xml, '"');
132
+ cmark_strbuf_puts(xml, " title=\"");
133
+ escape_xml(xml, node->as.link.title.data, node->as.link.title.len);
134
+ cmark_strbuf_putc(xml, '"');
135
+ break;
136
+ default:
137
+ break;
138
+ }
139
+ if (node->first_child) {
140
+ state->indent += 2;
141
+ } else if (!literal) {
142
+ cmark_strbuf_puts(xml, " /");
143
+ }
144
+ cmark_strbuf_puts(xml, ">\n");
145
+
146
+ } else if (node->first_child) {
147
+ state->indent -= 2;
148
+ indent(state);
149
+ cmark_strbuf_puts(xml, "</");
150
+ cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
151
+ cmark_strbuf_puts(xml, ">\n");
152
+ }
153
+
154
+ return 1;
155
+ }
156
+
157
+ char *cmark_render_xml(cmark_node *root, int options) {
158
+ return cmark_render_xml_with_mem(root, options, cmark_node_mem(root));
159
+ }
160
+
161
+ char *cmark_render_xml_with_mem(cmark_node *root, int options, cmark_mem *mem) {
162
+ char *result;
163
+ cmark_strbuf xml = CMARK_BUF_INIT(mem);
164
+ cmark_event_type ev_type;
165
+ cmark_node *cur;
166
+ struct render_state state = {&xml, 0};
167
+
168
+ cmark_iter *iter = cmark_iter_new(root);
169
+
170
+ cmark_strbuf_puts(state.xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
171
+ cmark_strbuf_puts(state.xml,
172
+ "<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n");
173
+ while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
174
+ cur = cmark_iter_get_node(iter);
175
+ S_render_node(cur, ev_type, &state, options);
176
+ }
177
+ result = (char *)cmark_strbuf_detach(&xml);
178
+
179
+ cmark_iter_free(iter);
180
+ return result;
181
+ }
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # The compiled library.
5
+ require_relative 'markly/markly'
6
+
7
+ require_relative 'markly/flags'
8
+ require_relative 'markly/node'
9
+ require_relative 'markly/renderer'
10
+ require_relative 'markly/renderer/html_renderer'
11
+ require_relative 'markly/version'
12
+
13
+ module Markly
14
+ # Public: Parses a Markdown string into a `document` node.
15
+ #
16
+ # string - {String} to be parsed
17
+ # option - A {Symbol} or {Array of Symbol}s indicating the parse options
18
+ # extensions - An {Array of Symbol}s indicating the extensions to use
19
+ #
20
+ # Returns the `parser` node.
21
+ def self.parse(text, flags: DEFAULT, extensions: nil)
22
+ parser = Parser.new(flags)
23
+
24
+ extensions&.each do |extension|
25
+ parser.enable(extension)
26
+ end
27
+
28
+ return parser.parse(text.encode(Encoding::UTF_8))
29
+ end
30
+
31
+ # Public: Parses a Markdown string into an HTML string.
32
+ #
33
+ # text - A {String} of text
34
+ # option - Either a {Symbol} or {Array of Symbol}s indicating the render options
35
+ # extensions - An {Array of Symbol}s indicating the extensions to use
36
+ #
37
+ # Returns a {String} of converted HTML.
38
+ def self.render_html(text, flags: DEFAULT, parse_flags: flags, render_flags: flags, extensions: [])
39
+ root = self.parse(text, flags: parse_flags, extensions: extensions)
40
+
41
+ return root.to_html(flags: render_flags, extensions: extensions)
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Markly
4
+ DEFAULT = 0
5
+ VALIDATE_UTF8 = 1 << 9
6
+ SMART = 1 << 10
7
+ LIBERAL_HTML_TAG = 1 << 12
8
+ FOOTNOTES = 1 << 13
9
+ STRIKETHROUGH_DOUBLE_TILDE = 1 << 14
10
+ UNSAFE = 1 << 17
11
+
12
+ PARSE_FLAGS = {
13
+ validate_utf8: VALIDATE_UTF8,
14
+ smart_quotes: SMART,
15
+ liberal_html_tags: LIBERAL_HTML_TAG,
16
+ footnotes: FOOTNOTES,
17
+ strikethrough_double_tilde: STRIKETHROUGH_DOUBLE_TILDE,
18
+ unsafe: UNSAFE,
19
+ }
20
+
21
+ SOURCE_POSITION = 1 << 1
22
+ HARD_BREAKS = 1 << 2
23
+ NO_BREAKS = 1 << 4
24
+ GITHUB_PRE_LANG = 1 << 11
25
+ TABLE_PREFER_STYLE_ATTRIBUTES = 1 << 15
26
+ FULL_INFO_STRING = 1 << 16
27
+
28
+ RENDER_FLAGS = {
29
+ source_position: SOURCE_POSITION,
30
+ hard_breaks: HARD_BREAKS,
31
+ no_breaks: NO_BREAKS,
32
+ pre_lang: GITHUB_PRE_LANG,
33
+ table_prefer_style_attributes: TABLE_PREFER_STYLE_ATTRIBUTES,
34
+ full_info_string: FULL_INFO_STRING,
35
+ unsafe: UNSAFE,
36
+ }
37
+ end
Binary file
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'node/inspect'
4
+
5
+ module Markly
6
+ class Node
7
+ include Enumerable
8
+ include Inspect
9
+
10
+ # Public: An iterator that "walks the tree," descending into children recursively.
11
+ #
12
+ # blk - A {Proc} representing the action to take for each child
13
+ def walk(&block)
14
+ return enum_for(:walk) unless block_given?
15
+
16
+ yield self
17
+ each do |child|
18
+ child.walk(&block)
19
+ end
20
+ end
21
+
22
+ # Public: Convert the node to an HTML string.
23
+ #
24
+ # options - A {Symbol} or {Array of Symbol}s indicating the render options
25
+ # extensions - An {Array of Symbol}s indicating the extensions to use
26
+ #
27
+ # Returns a {String}.
28
+ def to_html(flags: DEFAULT, extensions: [])
29
+ _render_html(flags, extensions).force_encoding('utf-8')
30
+ end
31
+
32
+ # Public: Convert the node to a CommonMark string.
33
+ #
34
+ # options - A {Symbol} or {Array of Symbol}s indicating the render options
35
+ # width - Column to wrap the output at
36
+ #
37
+ # Returns a {String}.
38
+ def to_commonmark(flags: DEFAULT, width: 120)
39
+ _render_commonmark(flags, width).force_encoding('utf-8')
40
+ end
41
+
42
+ # Public: Convert the node to a plain text string.
43
+ #
44
+ # options - A {Symbol} or {Array of Symbol}s indicating the render options
45
+ # width - Column to wrap the output at
46
+ #
47
+ # Returns a {String}.
48
+ def to_plaintext(flags: DEFAULT, width: 120)
49
+ _render_plaintext(flags, width).force_encoding('utf-8')
50
+ end
51
+
52
+ # Public: Iterate over the children (if any) of the current pointer.
53
+ def each
54
+ return enum_for(:each) unless block_given?
55
+
56
+ child = first_child
57
+ while child
58
+ nextchild = child.next
59
+ yield child
60
+ child = nextchild
61
+ end
62
+ end
63
+
64
+ # Deprecated: Please use `each` instead
65
+ def each_child(&block)
66
+ warn '[DEPRECATION] `each_child` is deprecated. Please use `each` instead.'
67
+ each(&block)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pp'
4
+
5
+ module Markly
6
+ class Node
7
+ module Inspect
8
+ PP_INDENT_SIZE = 2
9
+
10
+ def inspect
11
+ PP.pp(self, +'', Float::INFINITY)
12
+ end
13
+
14
+ # @param printer [PrettyPrint] pp
15
+ def pretty_print(printer)
16
+ printer.group(PP_INDENT_SIZE, "#<#{self.class}(#{type}):", '>') do
17
+ printer.breakable
18
+
19
+ attrs = %i[
20
+ source_position
21
+ string_content
22
+ url
23
+ title
24
+ header_level
25
+ list_type
26
+ list_start
27
+ list_tight
28
+ fence_info
29
+ ].map do |name|
30
+ begin
31
+ [name, __send__(name)]
32
+ rescue Error
33
+ nil
34
+ end
35
+ end.compact
36
+
37
+ printer.seplist(attrs) do |name, value|
38
+ printer.text "#{name}="
39
+ printer.pp value
40
+ end
41
+
42
+ if first_child
43
+ printer.breakable
44
+ printer.group(PP_INDENT_SIZE) do
45
+ children = []
46
+ node = first_child
47
+ while node
48
+ children << node
49
+ node = node.next
50
+ end
51
+ printer.text 'children='
52
+ printer.pp children
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require 'stringio'
5
+
6
+ module Markly
7
+ class Renderer
8
+ attr_accessor :in_tight, :warnings, :in_plain
9
+ def initialize(flags: DEFAULT, extensions: [])
10
+ @flags = flags
11
+ @stream = StringIO.new(+'')
12
+ @need_blocksep = false
13
+ @warnings = Set.new
14
+ @in_tight = false
15
+ @in_plain = false
16
+ @tagfilter = extensions.include?(:tagfilter)
17
+ end
18
+
19
+ def out(*args)
20
+ args.each do |arg|
21
+ if arg == :children
22
+ @node.each { |child| out(child) }
23
+ elsif arg.is_a?(Array)
24
+ arg.each { |x| render(x) }
25
+ elsif arg.is_a?(Node)
26
+ render(arg)
27
+ else
28
+ @stream.write(arg)
29
+ end
30
+ end
31
+ end
32
+
33
+ def render(node)
34
+ @node = node
35
+ if node.type == :document
36
+ document(node)
37
+ @stream.string
38
+ elsif @in_plain && node.type != :text && node.type != :softbreak
39
+ node.each { |child| render(child) }
40
+ else
41
+ begin
42
+ send(node.type, node)
43
+ rescue NoMethodError => e
44
+ @warnings.add("WARNING: #{node.type} not implemented.")
45
+ raise e
46
+ end
47
+ end
48
+ end
49
+
50
+ def document(_node)
51
+ out(:children)
52
+ end
53
+
54
+ def code_block(node)
55
+ code_block(node)
56
+ end
57
+
58
+ def reference_def(_node); end
59
+
60
+ def cr
61
+ return if @stream.string.empty? || @stream.string[-1] == "\n"
62
+
63
+ out("\n")
64
+ end
65
+
66
+ def blocksep
67
+ out("\n")
68
+ end
69
+
70
+ def containersep
71
+ cr unless @in_tight
72
+ end
73
+
74
+ def block
75
+ cr
76
+ yield
77
+ cr
78
+ end
79
+
80
+ def container(starter, ender)
81
+ out(starter)
82
+ yield
83
+ out(ender)
84
+ end
85
+
86
+ def plain
87
+ old_in_plain = @in_plain
88
+ @in_plain = true
89
+ yield
90
+ @in_plain = old_in_plain
91
+ end
92
+
93
+ private
94
+
95
+ def escape_href(str)
96
+ @node.html_escape_href(str)
97
+ end
98
+
99
+ def escape_html(str)
100
+ @node.html_escape_html(str)
101
+ end
102
+
103
+ def tagfilter(str)
104
+ if @tagfilter
105
+ str.gsub(
106
+ %r{
107
+ <
108
+ (
109
+ title|textarea|style|xmp|iframe|
110
+ noembed|noframes|script|plaintext
111
+ )
112
+ (?=\s|>|/>)
113
+ }xi,
114
+ '&lt;\1'
115
+ )
116
+ else
117
+ str
118
+ end
119
+ end
120
+
121
+ def source_position(node)
122
+ return '' unless flag_enabled?(SOURCE_POSITION)
123
+
124
+ s = node.source_position
125
+ " data-sourcepos=\"#{s[:start_line]}:#{s[:start_column]}-" \
126
+ "#{s[:end_line]}:#{s[:end_column]}\""
127
+ end
128
+
129
+ def flag_enabled?(flag)
130
+ (@flags & flag) != 0
131
+ end
132
+ end
133
+ end