gloo 6.1.0 → 6.2.1

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,102 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2026 Eric Crane. All rights reserved.
3
+ #
4
+ # Resolves, validates and invokes a function. Shared by the
5
+ # `invoke`/`~>` verb (lib/gloo/verbs/invoke.rb) and inline calls
6
+ # inside any expression (lib/gloo/expr/call.rb), so both go through
7
+ # the same error handling instead of maintaining two copies of it.
8
+ #
9
+
10
+ module Gloo
11
+ module Core
12
+ class Invoker
13
+
14
+ NO_TARGET_ERR = 'Missing function reference!'.freeze
15
+ NOT_FOUND_ERR = 'Object was not found: '.freeze
16
+ NOT_FUNCTION_ERR = 'Not a function: '.freeze
17
+ PARAM_COUNT_ERR = 'Wrong number of parameters for function: '.freeze
18
+
19
+ #
20
+ # Resolve, validate and invoke the function at the given
21
+ # target path, with the given raw (unevaluated) arg tokens.
22
+ # Reports an error and returns nil for any failure - including
23
+ # a failure inside the function itself (see Function#invoke,
24
+ # which leaves `it` alone rather than setting it to an
25
+ # unreliable result when that happens).
26
+ #
27
+ def self.invoke( engine, target, arg_tokens )
28
+ if target.nil? || target.to_s.strip.empty?
29
+ engine.err NO_TARGET_ERR
30
+ return nil
31
+ end
32
+
33
+ func = resolve_function( engine, target )
34
+ return nil unless func
35
+
36
+ args = evaluate_arg_tokens( engine, arg_tokens )
37
+ return nil unless params_count_ok?( engine, func, args )
38
+
39
+ return invoke_function( engine, func, args )
40
+ end
41
+
42
+ #
43
+ # Resolve the function object at the target path. Reports an
44
+ # error and returns nil if the path doesn't resolve to
45
+ # anything, or resolves to an object that isn't a function.
46
+ #
47
+ def self.resolve_function( engine, target )
48
+ pn = Gloo::Core::Pn.new( engine, target )
49
+ func = pn.resolve
50
+
51
+ unless func
52
+ engine.err "#{NOT_FOUND_ERR}#{target}"
53
+ return nil
54
+ end
55
+
56
+ unless func.is_function?
57
+ engine.err "#{NOT_FUNCTION_ERR}#{target}"
58
+ return nil
59
+ end
60
+
61
+ return func
62
+ end
63
+
64
+ #
65
+ # Evaluate a list of raw tokens, each as its own single-token
66
+ # expression (a literal or object reference) - the standalone
67
+ # invoke verb's long-standing per-token semantics, now shared
68
+ # with the inline-call form too.
69
+ #
70
+ def self.evaluate_arg_tokens( engine, tokens )
71
+ return ( tokens || [] ).map do |t|
72
+ Gloo::Expr::Expression.new( engine, [ t ] ).evaluate
73
+ end
74
+ end
75
+
76
+ #
77
+ # Confirm the number of args given matches the number the
78
+ # function declares. Reports an error and returns false if
79
+ # they don't match.
80
+ #
81
+ def self.params_count_ok?( engine, func, args )
82
+ expected = func.params_hash&.keys&.length || 0
83
+ return true if args.count == expected
84
+
85
+ engine.err "#{PARAM_COUNT_ERR}#{func.pn} " \
86
+ "(expected #{expected}, got #{args.count})"
87
+ return false
88
+ end
89
+
90
+ #
91
+ # Invoke the function and return its result.
92
+ #
93
+ def self.invoke_function( engine, func, args )
94
+ engine.log.debug "invoking function: #{func.pn}"
95
+ result = func.invoke( args )
96
+ engine.log.debug "function returned: #{result}"
97
+ return result
98
+ end
99
+
100
+ end
101
+ end
102
+ end
@@ -39,17 +39,58 @@ module Gloo
39
39
  # If additional params were provided, split them out
40
40
  # from the token list.
41
41
  #
42
+ # A trailing (...) is normally the optional-param convention
43
+ # (eg. show "text" (color)) and gets split off here. But a
44
+ # trailing (...) can also be an inline function call that
45
+ # happens to be the last thing in the command (eg.
46
+ # show invoke( functions.add 3 4 )) - that must be left alone
47
+ # so it flows into tokenizing as part of the main command.
48
+ #
42
49
  def split_params( cmd )
43
- params = nil
44
- i = cmd.rindex( '(' )
45
- if i && cmd.strip.end_with?( ')' )
46
- pstr = cmd[ i + 1..-1 ]
47
- params = pstr.strip[ 0..-2 ] if pstr
48
- cmd = cmd[ 0, i].strip
49
- end
50
+ i = matching_open_paren_index( cmd )
51
+ return cmd, nil unless i
52
+ return cmd, nil if inline_call_opener?( cmd, i )
53
+
54
+ pstr = cmd[ i + 1..-1 ]
55
+ params = pstr.strip[ 0..-2 ] if pstr
56
+ cmd = cmd[ 0, i ].strip
50
57
  return cmd, params
51
58
  end
52
59
 
60
+ #
61
+ # Find the index of the '(' that balances the command's final
62
+ # ')', counting depth from the end so an inline call earlier
63
+ # in the command (or nested parens) doesn't confuse the match.
64
+ # Returns nil if the command doesn't end with ')', or the
65
+ # parens aren't balanced.
66
+ #
67
+ def matching_open_paren_index( cmd )
68
+ return nil unless cmd.strip.end_with?( ')' )
69
+
70
+ depth = 0
71
+ ( cmd.length - 1 ).downto( 0 ) do |idx|
72
+ case cmd[ idx ]
73
+ when ')' then depth += 1
74
+ when '('
75
+ depth -= 1
76
+ return idx if depth.zero?
77
+ end
78
+ end
79
+ return nil
80
+ end
81
+
82
+ #
83
+ # Is the '(' at the given index immediately preceded (no
84
+ # space) by an inline-call keyword, eg. invoke( or ~>( ?
85
+ # Shares its keyword list with Gloo::Core::Tokens, which is
86
+ # where such a call actually gets recognized as one token.
87
+ #
88
+ def inline_call_opener?( cmd, paren_index )
89
+ before = cmd[ 0...paren_index ]
90
+ word = before[ /\S*\z/ ]
91
+ return Gloo::Core::Tokens::CALL_OPENERS.include?( word )
92
+ end
93
+
53
94
  #
54
95
  # Parse a command and then run it if it parsed correctly.
55
96
  #
@@ -9,6 +9,15 @@ module Gloo
9
9
  module Core
10
10
  class Tokens
11
11
 
12
+ # Keywords that open an inline function call, eg. invoke( ... )
13
+ # or ~>( ... ). Kept here since Tokens is where they get
14
+ # recognized as a single bounded token; Parser#split_params
15
+ # reuses this same list to avoid mistaking a trailing call for
16
+ # its unrelated trailing-optional-param convention.
17
+ CALL_OPENERS = %w[invoke ~>].freeze
18
+
19
+ QUOTE_CHARS = [ '"', "'" ].freeze
20
+
12
21
  attr_reader :cmd, :tokens
13
22
 
14
23
  # ---------------------------------------------------------------------
@@ -142,8 +151,19 @@ module Gloo
142
151
  #
143
152
  # Create a list of token from the given string.
144
153
  #
154
+ # An inline call (invoke( ... ) / ~>( ... )) is checked for
155
+ # first since it needs to be quote-aware in its own right (a
156
+ # call's args can include a quoted string) - see
157
+ # find_call_range. Falls through to the original quote-then-
158
+ # plain-split handling, unchanged, when there's no call.
159
+ #
145
160
  def tokenize( str )
146
- if str.index( '"' )
161
+ range = find_call_range( str )
162
+ if range
163
+ tokenize( str[ 0...range.first ] ) if range.first.positive?
164
+ @tokens << str[ range ]
165
+ tokenize( str[ range.last + 1..-1 ] ) if range.last + 1 < str.length
166
+ elsif str.index( '"' )
147
167
  i = str.index( '"' )
148
168
  j = str.index( '"', i + 1 )
149
169
  j ||= str.length
@@ -164,6 +184,66 @@ module Gloo
164
184
  end
165
185
  end
166
186
 
187
+ #
188
+ # Find the char range of the first top-level (not inside a
189
+ # quoted string) invoke(...)/~>(...) call in str. Returns nil
190
+ # if there isn't one, or it's never properly closed.
191
+ #
192
+ def find_call_range( str )
193
+ i = 0
194
+ while i < str.length
195
+ ch = str[ i ]
196
+
197
+ if QUOTE_CHARS.include?( ch )
198
+ close = str.index( ch, i + 1 )
199
+ return nil unless close
200
+
201
+ i = close + 1
202
+ next
203
+ end
204
+
205
+ CALL_OPENERS.each do |kw|
206
+ opener = "#{kw}("
207
+ next unless str[ i, opener.length ] == opener
208
+ next unless i.zero? || str[ i - 1 ] =~ /\s/
209
+
210
+ close = find_matching_close_paren( str, i + opener.length - 1 )
211
+ return ( i..close ) if close
212
+ end
213
+
214
+ i += 1
215
+ end
216
+ return nil
217
+ end
218
+
219
+ #
220
+ # Given the index of an open paren, find the index of its
221
+ # balancing close paren - skipping over quoted substrings so
222
+ # a stray paren character inside a quoted arg doesn't throw
223
+ # off the depth count. Returns nil if it's never closed.
224
+ #
225
+ def find_matching_close_paren( str, open_index )
226
+ depth = 0
227
+ i = open_index
228
+ while i < str.length
229
+ ch = str[ i ]
230
+ if QUOTE_CHARS.include?( ch )
231
+ close = str.index( ch, i + 1 )
232
+ return nil unless close
233
+
234
+ i = close + 1
235
+ next
236
+ elsif ch == '('
237
+ depth += 1
238
+ elsif ch == ')'
239
+ depth -= 1
240
+ return i if depth.zero?
241
+ end
242
+ i += 1
243
+ end
244
+ return nil
245
+ end
246
+
167
247
  end
168
248
  end
169
249
  end
@@ -41,11 +41,12 @@ module Gloo
41
41
  # List all verbs.
42
42
  #
43
43
  def cmd_show_verbs( _obj, _context )
44
+ theme = @engine.theme
44
45
  data = "\n"
45
- data << " Verbs (shortcut, name)\n".blue
46
+ data << theme.heading( " Verbs (shortcut, name)\n" )
46
47
  @engine.dictionary.get_verbs.sort_by( &:keyword ).each do |v|
47
- cut = v.keyword_shortcut.ljust( 5, ' ' ).yellow
48
- name = v.keyword.ljust( 20, ' ' ).white
48
+ cut = theme.accent( v.keyword_shortcut.ljust( 5, ' ' ) )
49
+ name = theme.emphasis( v.keyword.ljust( 20, ' ' ) )
49
50
  data << " #{cut} #{name} \n"
50
51
  end
51
52
  @engine.log.show "#{data}\n"
@@ -55,14 +56,15 @@ module Gloo
55
56
  # List all object types.
56
57
  #
57
58
  def cmd_show_objects( _obj, _context )
59
+ theme = @engine.theme
58
60
  data = "\n"
59
- data << " Objects \n".blue
61
+ data << theme.heading( " Objects \n" )
60
62
  @engine.dictionary.get_obj_types.sort_by( &:typename ).each do |o|
61
63
  if o.short_typename != o.typename
62
- short = "(#{o.short_typename})".yellow
63
- name = "#{o.typename.white} #{short}"
64
+ short = theme.accent( "(#{o.short_typename})" )
65
+ name = "#{theme.emphasis( o.typename )} #{short}"
64
66
  else
65
- name = o.typename.white
67
+ name = theme.emphasis( o.typename )
66
68
  end
67
69
  data << " #{name.ljust( 30, ' ' )}\n"
68
70
  end
@@ -76,21 +78,44 @@ module Gloo
76
78
  @engine.settings.show
77
79
  end
78
80
 
81
+ #
82
+ # Show the current theme, how to change it, and a color preview
83
+ # of both palettes - handy for checking how they actually render
84
+ # in whatever terminal you're sitting in.
85
+ #
86
+ def cmd_show_theme( _obj, _context )
87
+ theme = @engine.theme
88
+ settings = @engine.settings
89
+ config_file = File.join( settings.config_path, 'gloo.yml' )
90
+
91
+ data = "\n"
92
+ data << theme.heading( " Theme\n" )
93
+ data << " Current theme: #{theme.emphasis( settings.theme )}\n\n"
94
+ data << theme.muted( " Change it in #{config_file}:\n" )
95
+ data << theme.muted( " theme: dark (or: light)\n\n" )
96
+ data << theme_preview( ' Dark palette', Gloo::App::Theme.new( 'dark' ) )
97
+ data << "\n"
98
+ data << theme_preview( ' Light palette', Gloo::App::Theme.new( 'light' ) )
99
+ @engine.log.show "#{data}\n"
100
+ end
101
+
79
102
  #
80
103
  # List loaded extensions.
81
104
  #
82
105
  def cmd_show_extensions( _obj, _context )
106
+ theme = @engine.theme
83
107
  data = "\n"
84
- data << " Extensions\n".blue
85
- data << " Use `load ext {name}` to load a User Extension, \n" \
108
+ data << theme.heading( " Extensions\n" )
109
+ data << theme.muted(
110
+ " Use `load ext {name}` to load a User Extension, \n" \
86
111
  " then `object {name}` / `verb {name}` / `extension {name}` here to see what it adds. \n" \
87
- " Only loaded extensions are listed below.\n\n".light_black
112
+ " Only loaded extensions are listed below.\n\n" )
88
113
  loaded = @engine.ext_manager.loaded_extensions
89
114
  if loaded.empty?
90
- data << " (none loaded)\n".light_black
115
+ data << theme.muted( " (none loaded)\n" )
91
116
  else
92
117
  loaded.sort.each do |name, _ext|
93
- data << " #{name.white} \n"
118
+ data << " #{theme.emphasis( name )} \n"
94
119
  end
95
120
  end
96
121
  @engine.log.show "#{data}\n"
@@ -100,17 +125,19 @@ module Gloo
100
125
  # List loaded libraries.
101
126
  #
102
127
  def cmd_show_libraries( _obj, _context )
128
+ theme = @engine.theme
103
129
  data = "\n"
104
- data << " Libraries\n".blue
105
- data << " Use `load lib {name}` to load a core library, \n" \
130
+ data << theme.heading( " Libraries\n" )
131
+ data << theme.muted(
132
+ " Use `load lib {name}` to load a core library, \n" \
106
133
  " then `object {name}` / `verb {name}` here to see what it adds. \n" \
107
- " Only loaded libraries are listed below.\n\n".light_black
134
+ " Only loaded libraries are listed below.\n\n" )
108
135
  loaded = @engine.lib_manager.loaded_libraries
109
136
  if loaded.empty?
110
- data << " (none loaded)\n".light_black
137
+ data << theme.muted( " (none loaded)\n" )
111
138
  else
112
139
  loaded.sort.each do |name, _lib|
113
- data << " #{name.white} \n"
140
+ data << " #{theme.emphasis( name )} \n"
114
141
  end
115
142
  end
116
143
  @engine.log.show "#{data}\n"
@@ -120,10 +147,11 @@ module Gloo
120
147
  # List all narrative doc pages (dev/gloo/docs/*.md).
121
148
  #
122
149
  def cmd_show_docs( _obj, _context )
150
+ theme = @engine.theme
123
151
  data = "\n"
124
- data << " Docs\n".blue
152
+ data << theme.heading( " Docs\n" )
125
153
  doc_page_names.each do |name|
126
- data << " #{name.white}\n"
154
+ data << " #{theme.emphasis( name )}\n"
127
155
  end
128
156
  @engine.log.show "#{data}\n"
129
157
  end
@@ -199,6 +227,22 @@ module Gloo
199
227
 
200
228
  private
201
229
 
230
+ #
231
+ # Build a labeled preview block for one theme's palette: one line
232
+ # per role, colored in that role's own color, labeled with the
233
+ # role's name. Padding is applied to the plain label before it's
234
+ # colorized, so the ANSI codes don't throw off the alignment.
235
+ #
236
+ def theme_preview( title, theme )
237
+ lines = "#{theme.heading( title )}\n"
238
+ Gloo::App::Theme::ROLES.each do |role|
239
+ label = role.to_s.ljust( 12 )
240
+ lines << theme.send( role, " #{label}#{role}" )
241
+ lines << "\n"
242
+ end
243
+ return lines
244
+ end
245
+
202
246
  #
203
247
  # Colorize markdown and page it, bracketed with a '---' rule above
204
248
  # and below so the content stands out from surrounding CLI output.
@@ -206,7 +250,7 @@ module Gloo
206
250
  def page_markdown( md )
207
251
  rule = '-' * @engine.platform.cols
208
252
  bracketed = "#{rule}\n#{md.strip}\n#{rule}\n"
209
- @engine.platform.page( Gloo::Docs::MarkdownRenderer.colorize( bracketed ) )
253
+ @engine.platform.page( Gloo::Docs::MarkdownRenderer.colorize( bracketed, @engine.theme ) )
210
254
  end
211
255
 
212
256
  #
@@ -257,6 +301,9 @@ module Gloo
257
301
  name: 'objects', description: 'List all object types', method: 'cmd_show_objects' )
258
302
  add_command_node(
259
303
  name: 'settings', description: 'Show application settings', method: 'cmd_show_settings' )
304
+ add_command_node(
305
+ name: 'theme', description: 'Show the current theme and preview both palettes',
306
+ method: 'cmd_show_theme' )
260
307
  add_command_node(
261
308
  name: 'extensions', description: 'List loaded extensions', method: 'cmd_show_extensions' )
262
309
  add_command_node(
@@ -16,18 +16,18 @@ module Gloo
16
16
 
17
17
  #
18
18
  # Colorize the given markdown text for terminal display.
19
- def self.colorize( text )
19
+ def self.colorize( text, theme )
20
20
  in_code = false
21
21
  lines = text.split( "\n" ).map do |line|
22
22
  if line.start_with?( '```' )
23
23
  in_code = !in_code
24
- next line.light_black
24
+ next theme.muted( line )
25
25
  end
26
- next line.light_black if in_code
27
- next line.sub( /^#\s*/, '' ).blue.bold if line.start_with?( '# ' )
28
- next line.sub( /^##\s*/, '' ).cyan.bold if line.start_with?( '## ' )
29
- next line.cyan if line.start_with?( '### ' ) || line.start_with?( '#### ' )
30
- next line.light_black if line =~ /\A-{3,}\z/
26
+ next theme.muted( line ) if in_code
27
+ next theme.heading( line.sub( /^#\s*/, '' ) ) if line.start_with?( '# ' )
28
+ next theme.subheading( line.sub( /^##\s*/, '' ) ) if line.start_with?( '## ' )
29
+ next theme.subheading( line ) if line.start_with?( '### ' ) || line.start_with?( '#### ' )
30
+ next theme.muted( line ) if line =~ /\A-{3,}\z/
31
31
 
32
32
  line
33
33
  end
@@ -0,0 +1,54 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2026 Eric Crane. All rights reserved.
3
+ #
4
+ # An inline function call inside an expression, eg.
5
+ # invoke( functions.add 3 4 ) or ~>( functions.add 3 4 ).
6
+ #
7
+ # Gloo::Core::Tokens recognizes the whole invoke(...)/~>(...) span
8
+ # as one token; Call takes that single token, splits it back out
9
+ # into a target path and raw arg tokens (reusing Tokens itself, so
10
+ # quoted args and nested calls tokenize the same way they would
11
+ # anywhere else), and hands both to Gloo::Core::Invoker - the same
12
+ # resolve/validate/invoke path the standalone invoke verb uses.
13
+ #
14
+
15
+ module Gloo
16
+ module Expr
17
+ class Call
18
+
19
+ attr_reader :target, :arg_tokens
20
+
21
+ #
22
+ # Does the given token look like an inline call?
23
+ #
24
+ def self.call?( token )
25
+ return false unless token.is_a?( String )
26
+
27
+ return Gloo::Core::Tokens::CALL_OPENERS.any? do |kw|
28
+ token.start_with?( "#{kw}(" ) && token.end_with?( ')' )
29
+ end
30
+ end
31
+
32
+ #
33
+ # Parse the call token into a target path and raw arg tokens.
34
+ #
35
+ def initialize( engine, token )
36
+ @engine = engine
37
+
38
+ inner = token[ token.index( '(' ) + 1..-2 ].to_s
39
+ tokens = Gloo::Core::Tokens.new( inner )
40
+ @target = tokens.first
41
+ @arg_tokens = tokens.params || []
42
+ end
43
+
44
+ #
45
+ # Resolve, validate and invoke - returns the result value, or
46
+ # nil if it failed (an error will already have been reported).
47
+ #
48
+ def value
49
+ return Gloo::Core::Invoker.invoke( @engine, @target, @arg_tokens )
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -47,6 +47,7 @@ module Gloo
47
47
  end
48
48
 
49
49
  return @left.value if @left.is_a? Gloo::Core::Literal
50
+ return @left.value if @left.is_a? Gloo::Expr::Call
50
51
  return resolve_ref @left if @left.is_a? Gloo::Core::Pn
51
52
 
52
53
  return @left
@@ -75,6 +76,7 @@ module Gloo
75
76
  #
76
77
  def evaluate_sym( sym )
77
78
  return sym.value if sym.is_a? Gloo::Core::Literal
79
+ return sym.value if sym.is_a? Gloo::Expr::Call
78
80
  return resolve_ref sym if sym.is_a? Gloo::Core::Pn
79
81
 
80
82
  return sym
@@ -109,6 +111,7 @@ module Gloo
109
111
  return LInteger.new( token ) if LInteger.integer?( token )
110
112
  return LString.new( token ) if LString.string?( token )
111
113
  return LDecimal.new( token ) if LDecimal.decimal?( token )
114
+ return Gloo::Expr::Call.new( @engine, token ) if Gloo::Expr::Call.call?( token )
112
115
 
113
116
  # last chance: an Object reference
114
117
  return Gloo::Core::Pn.new( @engine, token )
@@ -59,31 +59,11 @@ module Gloo
59
59
  :name => KEYWORD,
60
60
  :shortcut => KEYWORD_SHORT,
61
61
  :description => 'A string value. For string interpolation, ' \
62
- 'see the erb object type.',
63
- :messages => [
64
- 'up — Convert the string to uppercase. This message changes the value of the string.',
65
- 'down — Convert the string to lowercase. This message changes the value of the string.',
66
- 'size Get the size of the string. It will have the string size.',
67
- 'count_chars — Count the number of characters in the string. It will have the character count.',
68
- 'count_words — Count the number of words in the string. It will have the word count.',
69
- 'count_lines — Count the number of lines in the string. It will have the line count.',
70
- 'starts_with? ({str}) — Check if the string starts with the given string. A parameter is required: the string to look for at the beginning of this string. It will have a boolean.',
71
- 'ends_with? ({str}) — Check if the string ends with the given string. A parameter is required: the string to look for at the end of this string. It will have a boolean.',
72
- 'substring? ({str}) — Check if the string includes the given sub-string. A parameter is required: the string to look for in this string. It will have a boolean.',
73
- 'format_for_html — Format this string for HTML output. Tabs, spaces and returns are converted to HTML elements. The value of the string is changed.',
74
- 'encode64 — Base64 encode the string. This message changes the value of the string. It will have the encoded string.',
75
- 'decode64 — Decode the string from Base64. This message changes the value of the string. It will have the decoded string.',
76
- 'escape — Escape the string to make it URL safe. This message changes the value of the string. It will have the escaped string.',
77
- 'unescape — Unescape the string (from URL safe format). This message changes the value of the string. It will have the unescaped string.',
78
- 'gen_uuid — Set the value of the string to a newly generated, random UUID. This message changes the value of the string.',
79
- 'gen_alphanumeric ({len}) — Set the value of the string to a newly generated, random alphanumeric string. The {len} parameter is optional; the length is 10 if not specified. This message changes the value of the string.',
80
- 'gen_hex ({len}) — Set the value of the string to a newly generated, random hex string. The {len} parameter is optional; the length is 10 if not specified. This message changes the value of the string.',
81
- 'gen_base64 ({len}) — Set the value of the string to a newly generated, random base64 string. The {len} parameter is optional; the length is 12 if not specified. This message changes the value of the string.',
82
- 'trim — Strip whitespace from the beginning and end of the string. This message changes the value of the string. It will have the trimmed string.',
83
- 'sub ({from}, {to}) — Substitute the first occurrence of {from} with {to}. Both parameters are required. This message changes the value of the string. It will have the result.',
84
- 'gsub ({from}, {to}) — Substitute all occurrences of {from} with {to}. Both parameters are required. This message changes the value of the string. It will have the result.',
85
- 'page — Show the value in a pager (less), for viewing long content a screen at a time.'
86
- ],
62
+ 'see the erb object type. Shares the same messages as the ' \
63
+ 'text object type; the two differ mainly by convention — ' \
64
+ 'string for a single word or line, text for longer, ' \
65
+ 'multi-line blocks.',
66
+ :messages => StringMsgs.message_docs,
87
67
  :examples => <<~EXAMPLES.strip
88
68
  s [can] :
89
69
  msg [string] : Hello World!