fmt 0.3.2 → 0.3.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5cab8d0eef24b2446c187520bd8a8ac2b71cef4149ff6d0370ffd04d67120d1
4
- data.tar.gz: 3e5b6dca4adf86661850769bd7ca0191fe7f9b62b7c7c8612765bb5c5fb54c0b
3
+ metadata.gz: 674b8314e97d214271069e488dfdb736551f431953d260d1766001fdc797fd55
4
+ data.tar.gz: 8b9c601567e80419c9b2bb7414bcb42b22431832c9c1db7039a53b6d1f1cdd34
5
5
  SHA512:
6
- metadata.gz: ceeb6ab3f6d47a8863a35b90addf52135d31905be200c402819a516fa2600bbc0672e03d813555031bee131c865d44a92f3ad431863a3a4efd72ebe7ed3b7cc3
7
- data.tar.gz: cfdbbdb355a735235b7417bc5b96a636c7e4392d8911178fbaa80e16a7127db41d388101cbdb8e14aa15c1562846d83a8546b8137c35cca3ade24aa695e39435
6
+ metadata.gz: 01317ca04d3745516ec6fc842650bf0cce4fc47702f12b025b5d3d203f328f2c92053b3805a72eb9ac134686d3b1d0dfe52aee15f82ab13d8fab4f4b1f7240c9
7
+ data.tar.gz: e08ed93970be78e2fe713d22a2861111d931f63bc6d8f6e402ba1ee25405febd82b343758f201a255f3ae5f9b0d0f8faae2ea2a0d40a2d6a7b33473f6f545e32
data/README.md CHANGED
@@ -1,6 +1,15 @@
1
1
  <p align="center">
2
2
  <a href="http://blog.codinghorror.com/the-best-code-is-no-code-at-all/">
3
- <img alt="Lines of Code" src="https://img.shields.io/badge/loc-1042-47d299.svg" />
3
+ <img alt="Lines of Code" src="https://img.shields.io/badge/loc-1050-47d299.svg" />
4
+ </a>
5
+ <a href="https://rubygems.org/gems/fmt">
6
+ <img alt="GEM Version" src="https://img.shields.io/gem/v/fmt">
7
+ </a>
8
+ <a href="https://rubygems.org/gems/fmt">
9
+ <img alt="GEM Downloads" src="https://img.shields.io/gem/dt/fmt">
10
+ </a>
11
+ <a href="https://github.com/hopsoft/fmt/actions">
12
+ <img alt="Tests" src="https://github.com/hopsoft/fmt/actions/workflows/tests.yml/badge.svg" />
4
13
  </a>
5
14
  <a href="https://github.com/testdouble/standard">
6
15
  <img alt="Ruby Style" src="https://img.shields.io/badge/style-standard-168AFE?logo=ruby&logoColor=FE1616" />
@@ -21,21 +30,21 @@
21
30
 
22
31
  ## Table of Contents
23
32
 
24
- - [Getting Started](#getting-started)
25
- - [Usage](#usage)
26
- - [Macros](#macros)
27
- - [Pipelines](#pipelines)
28
- - [Supported Methods](#supported-methods)
29
- - [Rainbow GEM](#rainbow-gem)
30
- - [Composition](#composition)
31
- - [Embedded Templates](#embedded-templates)
32
- - [Customizing Fmt](#customizing-fmt)
33
- - [Kernel Refinement](#kernel-refinement)
34
- - [`fmt(object, *pipeline)`](#fmtobject-pipeline)
35
- - [`fmt_print(object, *pipeline)`](#fmt_printobject-pipeline)
36
- - [`fmt_puts(object, *pipeline)`](#fmt_putsobject-pipeline)
37
- - [Performance](#performance)
38
- - [Sponsors](#sponsors)
33
+ - [Getting Started](#getting-started)
34
+ - [Usage](#usage)
35
+ - [Macros](#macros)
36
+ - [Pipelines](#pipelines)
37
+ - [Supported Methods](#supported-methods)
38
+ - [Rainbow GEM](#rainbow-gem)
39
+ - [Composition](#composition)
40
+ - [Embedded Templates](#embedded-templates)
41
+ - [Customizing Fmt](#customizing-fmt)
42
+ - [Kernel Refinement](#kernel-refinement)
43
+ - [`fmt(object, *pipeline)`](#fmtobject-pipeline)
44
+ - [`fmt_print(object, *pipeline)`](#fmt_printobject-pipeline)
45
+ - [`fmt_puts(object, *pipeline)`](#fmt_putsobject-pipeline)
46
+ - [Performance](#performance)
47
+ - [Sponsors](#sponsors)
39
48
 
40
49
  <!-- Tocer[finish]: Auto-generated, don't remove. -->
41
50
 
@@ -133,7 +142,7 @@ Templates can include multiple format strings with distinct pipelines:
133
142
  ```ruby
134
143
  template = "Date: %<date>.10s|>magenta -- %{msg}|>titleize|>bold"
135
144
  Fmt(template, date: Time.now, msg: "this is cool")
136
- #=> "Date: \e[35m2024-09-20\e[0m \e[1mThis Is Cool\e[0m"
145
+ #=> "Date: \e[35m2024-09-21\e[0m -- \e[1mThis Is Cool\e[0m"
137
146
  ```
138
147
 
139
148
  #### Embedded Templates
@@ -178,11 +187,11 @@ template = <<~T
178
187
  %{one}|>red {{
179
188
  %{two}|>blue {{
180
189
  %{three}|>green
181
- }}
182
- }}|>bold
190
+ }}|>bold
191
+ }}
183
192
  T
184
193
  Fmt(template, one: "Red", two: "Blue", three: "Green")
185
- #=> "Multiline:\n\e[31mRed\e[0m \e[1m\n \e[34mBlue\e[0m \n \e[32mGreen\e[0m"
194
+ #=> "Multiline:\n\e[31mRed\e[0m \n \e[34mBlue\e[0m \e[1m\n \e[32mGreen\e[0m\n \e[0m\n\n"
186
195
  ```
187
196
 
188
197
  ### Customizing Fmt
@@ -10,7 +10,7 @@ module Fmt
10
10
  # @rbs key: Symbol -- key for embed
11
11
  # @rbs placeholder: String -- placeholder for embed
12
12
  def initialize(urtext = "", key:, placeholder:)
13
- @urtext = urtext.to_s
13
+ @urtext = urtext.to_s.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "?")
14
14
  @key = key
15
15
  @placeholder = placeholder
16
16
  end
@@ -8,7 +8,7 @@ module Fmt
8
8
  # Constructor
9
9
  # @rbs urtext: String -- original source code
10
10
  def initialize(urtext = "")
11
- @urtext = urtext.to_s
11
+ @urtext = urtext.to_s.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "?")
12
12
  end
13
13
 
14
14
  attr_reader :urtext # : String -- original source code
@@ -8,7 +8,7 @@ module Fmt
8
8
  # Constructor
9
9
  # @rbs urtext: String -- original source code
10
10
  def initialize(urtext = "")
11
- @urtext = urtext.to_s
11
+ @urtext = urtext.to_s.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "?")
12
12
  end
13
13
 
14
14
  attr_reader :urtext # : String -- original source code
@@ -5,15 +5,15 @@
5
5
  module Fmt
6
6
  # Parses a template from a string and builds an AST (Abstract Syntax Tree)
7
7
  class TemplateParser < Parser
8
- EMBED_PEEK = %r{(?=#{esc Sigils::EMBED_PREFIX})}o # : Regexp -- detects start of an embed prefix (look ahead)
9
- PIPELINE_HEAD = %r{[#{Sigils::FORMAT_PREFIX}]}o # : Regexp -- detects start of a pipeline (i.e. Ruby format string)
10
- PIPELINE_PEEK = %r{(?=[#{Sigils::FORMAT_PREFIX}][^#{Sigils::FORMAT_PREFIX}]|\z)}o # : Regexp -- detects start of a pipeline (look ahead)
11
- PIPELINE_TAIL = %r{\s|\z}o # : Regexp -- detects end of a pipeline
8
+ EMBED_PEEK = %r{(?=#{esc Sigils::EMBED_PREFIX})}ou # : Regexp -- detects start of an embed prefix (look ahead)
9
+ PIPELINE_PEEK = %r{(?=[#{Sigils::FORMAT_PREFIX}][^#{Sigils::FORMAT_PREFIX}])}ou # : Regexp -- detects start of a pipeline (look ahead)
10
+ PERCENT_LITERAL = %r{[#{Sigils::FORMAT_PREFIX}]{2}}ou # : Regexp -- detects a percent literal
11
+ WHITESPACE = %r{\s}ou # : Regexp -- detects whitespace
12
12
 
13
13
  # Constructor
14
14
  # @rbs urtext: String -- original source code
15
15
  def initialize(urtext = "")
16
- @urtext = urtext.to_s
16
+ @urtext = urtext.to_s.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "?")
17
17
  end
18
18
 
19
19
  attr_reader :urtext # : String -- original source code
@@ -66,48 +66,28 @@ module Fmt
66
66
  # Extracts the next embed with the scanner
67
67
  # @rbs scanner: StringScanner -- scanner to extract from
68
68
  # @rbs return: String? -- extracted embed
69
- # def extract_next_embed(scanner)
70
- # return nil unless scanner.skip_until(EMBED_PEEK)
71
-
72
- # index = scanner.pos
73
- # rindex = index
74
- # stack = 0
75
- # string = scanner.string
76
-
77
- # while rindex < string.length
78
- # char = string.getbyte(rindex)
79
-
80
- # case char
81
- # when Sigils::EMBED_PREFIX.ord then stack += 1
82
- # when Sigils::EMBED_SUFFIX.ord then stack -= 1
83
- # end
84
-
85
- # rindex += 1
86
- # break if stack.zero?
87
- # end
88
-
89
- # stack.zero? ? string[index...rindex] : nil
90
- # end
91
69
  def extract_next_embed(scanner)
92
70
  return nil unless scanner.skip_until(EMBED_PEEK)
93
71
 
94
- string = scanner.string
72
+ head = Sigils::EMBED_PREFIX[0]
73
+ tail = Sigils::EMBED_SUFFIX[0]
95
74
  index = scanner.pos
96
- rindex = index
97
75
  stack = 0
98
76
 
99
- while rindex < string.length
100
- case string[rindex]
101
- in Sigils::EMBED_PREFIX[0] then break if (stack += 1).zero?
102
- in Sigils::EMBED_SUFFIX[0] then break if (stack -= 1).zero?
77
+ until scanner.eos?
78
+ case scanner.getch
79
+ in ^head then stack += 1
80
+ in ^tail then stack -= 1
81
+ in nil then break
103
82
  else # noop
104
83
  end
105
84
 
106
- rindex += 1
85
+ break if stack.zero?
107
86
  end
108
87
 
109
- scanner.pos = rindex
110
- stack.zero? ? string[index...rindex] : nil
88
+ return nil unless stack.zero?
89
+
90
+ scanner.string[index...scanner.pos]
111
91
  end
112
92
 
113
93
  # Extracts embed metadata from the source
@@ -136,12 +116,31 @@ module Fmt
136
116
  # @rbs scanner: StringScanner -- scanner to extract from
137
117
  # @rbs return: String? -- extracted pipeline
138
118
  def extract_next_pipeline(scanner)
139
- return nil unless scanner.skip_until(PIPELINE_HEAD)
119
+ return nil unless scanner.skip_until(PIPELINE_PEEK)
140
120
 
141
121
  index = scanner.pos
142
- rindex = scanner.skip_until(PIPELINE_PEEK) ? scanner.pos : scanner.terminate.pos
143
- pipeline = scanner.string[index - 1...rindex].strip
144
- pipeline.rpartition(PIPELINE_TAIL).first
122
+
123
+ until scanner.eos?
124
+ len = 1
125
+ val = nil
126
+ val = scanner.peek(len += 1) until val&.valid_encoding?
127
+ if val&.match? PERCENT_LITERAL
128
+ scanner.pos += len
129
+ next
130
+ end
131
+
132
+ len = 0
133
+ val = nil
134
+ val = scanner.peek(len += 1) until val&.valid_encoding?
135
+ case [index, scanner.pos, val]
136
+ in [i, pos, Sigils::FORMAT_PREFIX] if i == pos then scanner.pos += len
137
+ in [i, pos, Sigils::FORMAT_PREFIX] if i != pos then break
138
+ in [i, pos, WHITESPACE] if arguments_balanced?(scanner.string[i...pos]) then break
139
+ else scanner.pos += len
140
+ end
141
+ end
142
+
143
+ scanner.string[index...scanner.pos]
145
144
  end
146
145
 
147
146
  # Extracts pipelines from the source
@@ -158,5 +157,13 @@ module Fmt
158
157
 
159
158
  generator.to_a
160
159
  end
160
+
161
+ # Indicates if arguments are balances in the given list of chars
162
+ # @rbs value: String -- value to check
163
+ # @rbs return: bool
164
+ def arguments_balanced?(value)
165
+ return true if value.nil? || value.empty?
166
+ value.count(Sigils::ARGS_PREFIX) == value.count(Sigils::ARGS_SUFFIX)
167
+ end
161
168
  end
162
169
  end
data/lib/fmt/version.rb CHANGED
@@ -3,5 +3,5 @@
3
3
  # rbs_inline: enabled
4
4
 
5
5
  module Fmt
6
- VERSION = "0.3.2"
6
+ VERSION = "0.3.4"
7
7
  end
@@ -5,11 +5,11 @@ module Fmt
5
5
  class TemplateParser < Parser
6
6
  EMBED_PEEK: ::Regexp
7
7
 
8
- PIPELINE_HEAD: ::Regexp
9
-
10
8
  PIPELINE_PEEK: ::Regexp
11
9
 
12
- PIPELINE_TAIL: ::Regexp
10
+ PERCENT_LITERAL: ::Regexp
11
+
12
+ WHITESPACE: ::Regexp
13
13
 
14
14
  # Constructor
15
15
  # @rbs urtext: String -- original source code
@@ -35,9 +35,10 @@ module Fmt
35
35
 
36
36
  private
37
37
 
38
- # stack.zero? ? string[index...rindex] : nil
39
- # end
40
- def extract_next_embed: (untyped scanner) -> untyped
38
+ # Extracts the next embed with the scanner
39
+ # @rbs scanner: StringScanner -- scanner to extract from
40
+ # @rbs return: String? -- extracted embed
41
+ def extract_next_embed: (StringScanner scanner) -> String?
41
42
 
42
43
  # Extracts embed metadata from the source
43
44
  # @rbs return: Array[Hash] -- extracted embeds
@@ -52,5 +53,10 @@ module Fmt
52
53
  # @rbs source: String -- source code to extract pipelines from
53
54
  # @rbs return: Array[String] -- extracted pipelines
54
55
  def extract_pipelines: (String source) -> Array[String]
56
+
57
+ # Indicates if arguments are balances in the given list of chars
58
+ # @rbs value: String -- value to check
59
+ # @rbs return: bool
60
+ def arguments_balanced?: (String value) -> bool
55
61
  end
56
62
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fmt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Hopkins (hopsoft)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-10 00:00:00.000000000 Z
11
+ date: 2024-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ast