antares 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +206 -82
- data/lib/antares/compatibility.rb +72 -72
- data/lib/antares/grammar.rb +274 -0
- data/lib/antares/highlighter.rb +93 -13
- data/lib/antares/structure.rb +430 -0
- data/lib/antares/tm_language_lexer.rb +193 -0
- data/lib/antares/tm_language_plist.rb +146 -0
- data/lib/antares/version.rb +1 -1
- data/lib/antares.rb +7 -0
- data/sig/antares.rbs +63 -0
- metadata +26 -2
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rexml/parsers/pullparser"
|
|
4
|
+
require "rexml/text"
|
|
5
|
+
|
|
6
|
+
module Antares
|
|
7
|
+
class TMLanguagePlist
|
|
8
|
+
ELEMENTS = %w[plist dict array key string integer real true false date data].freeze
|
|
9
|
+
PLIST_DOCTYPE = %r{<!DOCTYPE\s+plist\s+PUBLIC\s+
|
|
10
|
+
"-//Apple(?:\s+Computer)?//DTD\s+PLIST\s+1\.0//EN"\s+
|
|
11
|
+
"http://www\.apple\.com/DTDs/PropertyList-1\.0\.dtd"\s*>}ix
|
|
12
|
+
Frame = Struct.new(:name, :attributes, :text, :children, keyword_init: true)
|
|
13
|
+
|
|
14
|
+
def self.parse(source, max_depth:)
|
|
15
|
+
raise GrammarError, "tmLanguage XML entity declarations are unsupported" if source.match?(/<!ENTITY/i)
|
|
16
|
+
declaration = source.match(PLIST_DOCTYPE)
|
|
17
|
+
prefix = source[0...declaration.begin(0)] if declaration
|
|
18
|
+
if prefix&.match?(/\A\s*(?:<\?xml\b.*?\?>\s*)?\z/m)
|
|
19
|
+
source = prefix + source[declaration.end(0)..].to_s
|
|
20
|
+
end
|
|
21
|
+
raise GrammarError, "unsupported tmLanguage XML document type" if source.match?(/<!DOCTYPE/i)
|
|
22
|
+
|
|
23
|
+
new(source, max_depth).parse
|
|
24
|
+
rescue REXML::ParseException, ArgumentError, RangeError => error
|
|
25
|
+
raise GrammarError, "invalid tmLanguage plist: #{error.message.byteslice(0, 256)}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize(source, max_depth)
|
|
29
|
+
@parser = REXML::Parsers::PullParser.new(source)
|
|
30
|
+
@max_depth = max_depth
|
|
31
|
+
@frames = []
|
|
32
|
+
@roots = []
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def parse
|
|
36
|
+
while @parser.has_next?
|
|
37
|
+
event = @parser.pull
|
|
38
|
+
case event.event_type
|
|
39
|
+
when :start_element then start(event[0], event[1])
|
|
40
|
+
when :end_element then finish(event[0])
|
|
41
|
+
when :text then text(event[0], entities: true)
|
|
42
|
+
when :cdata then text(event[0], entities: false)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
raise GrammarError, "plist must contain one plist root" unless @frames.empty? && @roots.length == 1 && @roots.first.first == "plist"
|
|
46
|
+
|
|
47
|
+
@roots.first.last
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def start(name, attributes)
|
|
53
|
+
raise GrammarError, "unsupported plist element #{name.inspect}" unless ELEMENTS.include?(name)
|
|
54
|
+
raise ResourceLimitError, "tmLanguage nesting exceeds #{@max_depth}" if @frames.length >= @max_depth
|
|
55
|
+
allowed_attributes = name == "plist" && (attributes.keys - ["version"]).empty?
|
|
56
|
+
raise GrammarError, "unsupported attributes on plist element #{name.inspect}" unless attributes.empty? || allowed_attributes
|
|
57
|
+
|
|
58
|
+
@frames << Frame.new(name: name, attributes: attributes, text: +"", children: [])
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def text(value, entities:)
|
|
62
|
+
value = decode_entities(value) if entities
|
|
63
|
+
raise GrammarError, "text outside plist root" if @frames.empty? && !value.strip.empty?
|
|
64
|
+
|
|
65
|
+
@frames.last&.text&.concat(value)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def finish(name)
|
|
69
|
+
frame = @frames.pop
|
|
70
|
+
raise GrammarError, "mismatched plist element" unless frame&.name == name
|
|
71
|
+
|
|
72
|
+
value = decode(frame)
|
|
73
|
+
if @frames.empty?
|
|
74
|
+
@roots << [name, value]
|
|
75
|
+
else
|
|
76
|
+
@frames.last.children << [name, value]
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def decode(frame)
|
|
81
|
+
scalar = !%w[plist dict array].include?(frame.name)
|
|
82
|
+
raise GrammarError, "plist container contains text" if !scalar && !frame.text.strip.empty?
|
|
83
|
+
raise GrammarError, "plist scalar contains child elements" if scalar && !frame.children.empty?
|
|
84
|
+
|
|
85
|
+
case frame.name
|
|
86
|
+
when "plist"
|
|
87
|
+
raise GrammarError, "plist root must contain one value" unless frame.children.length == 1
|
|
88
|
+
|
|
89
|
+
frame.children.first.last
|
|
90
|
+
when "dict" then decode_dict(frame.children)
|
|
91
|
+
when "array" then frame.children.map(&:last)
|
|
92
|
+
when "key", "string", "date", "data" then scalar_text(frame)
|
|
93
|
+
when "integer" then integer(scalar_text(frame))
|
|
94
|
+
when "real" then real(scalar_text(frame))
|
|
95
|
+
when "true" then boolean(frame, true)
|
|
96
|
+
when "false" then boolean(frame, false)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def scalar_text(frame)
|
|
101
|
+
frame.text
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def decode_entities(value)
|
|
105
|
+
remainder = value.gsub(/&(?:amp|lt|gt|quot|apos|#\d+|#x[0-9a-f]+);/i, "")
|
|
106
|
+
raise GrammarError, "unsupported plist entity" if remainder.include?("&")
|
|
107
|
+
|
|
108
|
+
REXML::Text.unnormalize(value)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def decode_dict(children)
|
|
112
|
+
raise GrammarError, "plist dict must alternate keys and values" unless children.length.even?
|
|
113
|
+
|
|
114
|
+
result = {}
|
|
115
|
+
children.each_slice(2) do |key, entry|
|
|
116
|
+
raise GrammarError, "plist dict entry is missing a key" unless key.first == "key" && !key.last.empty?
|
|
117
|
+
raise GrammarError, "plist dict entry is missing a value" if entry.first == "key"
|
|
118
|
+
raise GrammarError, "duplicate plist key #{key.last.inspect}" if result.key?(key.last)
|
|
119
|
+
|
|
120
|
+
result[key.last] = entry.last
|
|
121
|
+
end
|
|
122
|
+
result
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def integer(value)
|
|
126
|
+
raise GrammarError, "invalid plist integer" unless value.match?(/\A[+-]?\d{1,64}\z/)
|
|
127
|
+
|
|
128
|
+
Integer(value, 10)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def boolean(frame, value)
|
|
132
|
+
raise GrammarError, "plist boolean must be empty" unless frame.text.strip.empty?
|
|
133
|
+
|
|
134
|
+
value
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def real(value)
|
|
138
|
+
number = Float(value)
|
|
139
|
+
raise GrammarError, "invalid plist real" unless number.finite?
|
|
140
|
+
|
|
141
|
+
number
|
|
142
|
+
rescue ArgumentError
|
|
143
|
+
raise GrammarError, "invalid plist real"
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
data/lib/antares/version.rb
CHANGED
data/lib/antares.rb
CHANGED
|
@@ -7,6 +7,7 @@ module Antares
|
|
|
7
7
|
class Error < StandardError; end
|
|
8
8
|
class UnsupportedLexerError < Error; end
|
|
9
9
|
class ResourceLimitError < Error; end
|
|
10
|
+
class GrammarError < Error; end
|
|
10
11
|
|
|
11
12
|
def self.compatible?(lexer)
|
|
12
13
|
klass = lexer.is_a?(Class) ? lexer : lexer.class
|
|
@@ -19,3 +20,9 @@ require_relative "antares/compatibility"
|
|
|
19
20
|
require_relative "antares/lexer_state_snapshot"
|
|
20
21
|
require_relative "antares/lexer_driver"
|
|
21
22
|
require_relative "antares/highlighter"
|
|
23
|
+
require_relative "antares/structure"
|
|
24
|
+
require_relative "antares/grammar"
|
|
25
|
+
|
|
26
|
+
module Antares
|
|
27
|
+
private_constant :TMLanguageLexer, :TMLanguagePlist
|
|
28
|
+
end
|
data/sig/antares.rbs
CHANGED
|
@@ -10,13 +10,39 @@ module Antares
|
|
|
10
10
|
end
|
|
11
11
|
class ResourceLimitError < Error
|
|
12
12
|
end
|
|
13
|
+
class GrammarError < Error
|
|
14
|
+
end
|
|
13
15
|
|
|
14
16
|
type strategy = :auto | :incremental | :window | :full
|
|
15
17
|
type token = [Class, String]
|
|
16
18
|
type row = Array[token]
|
|
19
|
+
type structure_kind = :block | :comment | :string | :region | :token | :line
|
|
20
|
+
|
|
21
|
+
interface _Structure
|
|
22
|
+
def fold_regions: (?Range[Integer]? range) -> Array[Region]
|
|
23
|
+
def brackets: (?Range[Integer]? range) -> Array[Bracket]
|
|
24
|
+
def bracket_at: (Integer line, Integer column) -> Bracket?
|
|
25
|
+
def context_at: (Integer line) -> Array[Region]
|
|
26
|
+
def selection_ranges: (Integer line, Integer column) -> Array[Region]
|
|
27
|
+
def edit: (from_line: Integer, removed: Integer, inserted: Integer) -> self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
interface _StructureProvider
|
|
31
|
+
def new: (lines: ^(Integer) -> String, line_count: ^() -> Integer,
|
|
32
|
+
tokens_for: ^(Integer) -> row, tokens_in: ^(Range[Integer]) -> Array[row],
|
|
33
|
+
stabilize: ^(Integer) -> Integer) -> _Structure
|
|
34
|
+
end
|
|
17
35
|
|
|
18
36
|
def self.compatible?: (untyped lexer) -> (:incremental | :window)
|
|
19
37
|
|
|
38
|
+
module Grammar
|
|
39
|
+
MAX_BYTES: Integer
|
|
40
|
+
MAX_DEPTH: Integer
|
|
41
|
+
MAX_RULES: Integer
|
|
42
|
+
MAX_REGEX_BYTES: Integer
|
|
43
|
+
def self.load_tmlanguage: (String path) -> untyped
|
|
44
|
+
end
|
|
45
|
+
|
|
20
46
|
class LexerStateSnapshot
|
|
21
47
|
attr_reader fingerprint: String
|
|
22
48
|
attr_reader bytesize: Integer
|
|
@@ -43,5 +69,42 @@ module Antares
|
|
|
43
69
|
def edit: (from_line: Integer, removed: Integer, inserted: Integer) -> self
|
|
44
70
|
def advance: (until_line: Integer, ?from_line: Integer?) -> self
|
|
45
71
|
def checkpoint_bytes: () -> Integer
|
|
72
|
+
def structure: () -> _Structure
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class Bracket < Struct[untyped]
|
|
76
|
+
attr_accessor open_line: Integer
|
|
77
|
+
attr_accessor open_column: Integer
|
|
78
|
+
attr_accessor close_line: Integer
|
|
79
|
+
attr_accessor close_column: Integer
|
|
80
|
+
attr_accessor depth: Integer
|
|
81
|
+
attr_accessor kind: String
|
|
82
|
+
def self.new: (?open_line: Integer, ?open_column: Integer, ?close_line: Integer,
|
|
83
|
+
?close_column: Integer, ?depth: Integer, ?kind: String) -> Bracket
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class Region < Struct[untyped]
|
|
87
|
+
attr_accessor start_line: Integer
|
|
88
|
+
attr_accessor end_line: Integer
|
|
89
|
+
attr_accessor kind: structure_kind
|
|
90
|
+
attr_accessor label: String
|
|
91
|
+
attr_accessor start_column: Integer?
|
|
92
|
+
attr_accessor end_column: Integer?
|
|
93
|
+
def self.new: (?start_line: Integer, ?end_line: Integer, ?kind: structure_kind,
|
|
94
|
+
?label: String, ?start_column: Integer?, ?end_column: Integer?) -> Region
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class Structure
|
|
98
|
+
def self.register: (String | Symbol language, _StructureProvider provider) -> _StructureProvider
|
|
99
|
+
def self.unregister: (String | Symbol language) -> untyped
|
|
100
|
+
def initialize: (lines: ^(Integer) -> String, line_count: ^() -> Integer,
|
|
101
|
+
tokens_for: ^(Integer) -> row, tokens_in: ^(Range[Integer]) -> Array[row],
|
|
102
|
+
?stabilize: ^(Integer) -> Integer) -> void
|
|
103
|
+
def fold_regions: (?Range[Integer]? range) -> Array[Region]
|
|
104
|
+
def brackets: (?Range[Integer]? range) -> Array[Bracket]
|
|
105
|
+
def bracket_at: (Integer line, Integer column) -> Bracket?
|
|
106
|
+
def context_at: (Integer line) -> Array[Region]
|
|
107
|
+
def selection_ranges: (Integer line, Integer column) -> Array[Region]
|
|
108
|
+
def edit: (from_line: Integer, removed: Integer, inserted: Integer) -> self
|
|
46
109
|
end
|
|
47
110
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: antares
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yudai Takada
|
|
@@ -23,6 +23,26 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '5.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rexml
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3.2'
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '4'
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '3.2'
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '4'
|
|
26
46
|
email:
|
|
27
47
|
- t.yudai92@gmail.com
|
|
28
48
|
executables: []
|
|
@@ -34,9 +54,13 @@ files:
|
|
|
34
54
|
- README.md
|
|
35
55
|
- lib/antares.rb
|
|
36
56
|
- lib/antares/compatibility.rb
|
|
57
|
+
- lib/antares/grammar.rb
|
|
37
58
|
- lib/antares/highlighter.rb
|
|
38
59
|
- lib/antares/lexer_driver.rb
|
|
39
60
|
- lib/antares/lexer_state_snapshot.rb
|
|
61
|
+
- lib/antares/structure.rb
|
|
62
|
+
- lib/antares/tm_language_lexer.rb
|
|
63
|
+
- lib/antares/tm_language_plist.rb
|
|
40
64
|
- lib/antares/version.rb
|
|
41
65
|
- sig/antares.rbs
|
|
42
66
|
homepage: https://github.com/noxdea/antares
|
|
@@ -61,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
61
85
|
- !ruby/object:Gem::Version
|
|
62
86
|
version: '0'
|
|
63
87
|
requirements: []
|
|
64
|
-
rubygems_version:
|
|
88
|
+
rubygems_version: 3.6.9
|
|
65
89
|
specification_version: 4
|
|
66
90
|
summary: Incremental highlighting for Rouge lexers
|
|
67
91
|
test_files: []
|