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.
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Antares
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
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.1.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: 4.0.19
88
+ rubygems_version: 3.6.9
65
89
  specification_version: 4
66
90
  summary: Incremental highlighting for Rouge lexers
67
91
  test_files: []