Class: VER::Text::Tag

Inherits:
Struct
  • Object
show all
Defined in:
lib/ver/text/tag.rb

Overview

The first form of annotation in text widgets is a tag. A tag is a textual string that is associated with some of the characters in a text. Tags may contain arbitrary characters, but it is probably best to avoid using the characters “ ” (space), “+” (plus), or “-” (minus): these characters have special meaning in indices, so tags containing them cannot be used as indices. There may be any number of tags associated with characters in a text. Each tag may refer to a single character, a range of characters, or several ranges of characters. An individual character may have any number of tags associated with it.

A priority order is defined among tags, and this order is used in implementing some of the tag-related functions described below. When a tag is defined (by associating it with characters or setting its display options or binding commands to it), it is given a priority higher than any existing tag. The priority order of tags may be redefined using the “pathName tag raise” and “pathName tag lower” widget commands.

Tags serve three purposes in text widgets. First, they control the way information is displayed on the screen. By default, characters are displayed as determined by the -background, -font, and -foreground options for the text widget. However, display options may be associated with individual tags using the “pathName tag configur” widget command. If a character has been tagged, then the display options associated with the tag override the default display style.

Constant Summary

Constants inherited from Struct

Struct::CACHE

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Struct

new

Instance Attribute Details

#bufferObject

Returns the value of attribute buffer

Returns:

  • (Object)

    the current value of buffer



31
32
33
# File 'lib/ver/text/tag.rb', line 31

def buffer
  @buffer
end

#nameObject

Returns the value of attribute name

Returns:

  • (Object)

    the current value of name



31
32
33
# File 'lib/ver/text/tag.rb', line 31

def name
  @name
end

Instance Method Details

#add(*indices) ⇒ Object

FIXME: yes, i know i’m calling ‘tag add` for every line, which makes

things slower, but it seems like there is a bug in the text widget.
So we aggregate the information into a single eval.


35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/ver/text/tag.rb', line 35

def add(*indices)
  code = [%(set win "#{buffer.tk_pathname}")]

  indices.each_slice(2) do |first, last|
    if last
      code << %($win tag add #{name} "#{first}" "#{last}")
    else
      code << %($win tag add #{name} "#{first}")
    end
  end

  Tk.execute_only(Tk::TclString.new(code.join("\n")))
end

#bind(*args, &block) ⇒ Object



49
50
51
# File 'lib/ver/text/tag.rb', line 49

def bind(*args, &block)
  buffer.tag_bind(to_tcl, *args, &block)
end

#cget(option) ⇒ Object



53
54
55
# File 'lib/ver/text/tag.rb', line 53

def cget(option)
  buffer.tag_cget(self, option)
end

#commentObject

Comment all lines that the selection touches. Tries to maintain indent.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/ver/text/tag.rb', line 59

def comment
  comment = buffer.options.comment_line.to_s
  indent = nil
  lines = []

  each_line do |line, fc, tc|
    line_fc, line_tc = "#{line}.#{fc}", "#{line}.#{tc}"

    next if buffer.at_end == line_tc

    lines << line

    next if indent == 0 # can't get lower

    line = buffer.get("#{line}.#{fc}", "#{line}.#{tc}")

    next unless start = line =~ /\S/

    indent ||= start
    indent = start if start < indent
  end

  indent ||= 0

  buffer.undo_record do |record|
    lines.each do |line|
      record.insert("#{line}.#{indent}", comment)
    end
  end
end

#configure(*options) ⇒ Object



90
91
92
# File 'lib/ver/text/tag.rb', line 90

def configure(*options)
  buffer.tag_configure(self, *options)
end

#copyObject



94
95
96
97
# File 'lib/ver/text/tag.rb', line 94

def copy
  chunks = ranges.map{|range| range.get }
  buffer.with_register{|reg| reg.value = chunks.at(1) ? chunks : chunks.first }
end

#deleteObject Also known as: tag_delete



99
100
101
# File 'lib/ver/text/tag.rb', line 99

def delete
  buffer.tag_delete(self)
end

#each_lineObject



108
109
110
111
112
113
114
115
116
117
# File 'lib/ver/text/tag.rb', line 108

def each_line
  return Enumerator.new(self, :each_line) unless block_given?

  each_range do |range|
    fy, fx, ty, tx = *range.first, *range.last
    fy.upto(ty) do |y|
      yield y, fx, tx
    end
  end
end

#each_range(&block) ⇒ Object



104
105
106
# File 'lib/ver/text/tag.rb', line 104

def each_range(&block)
  ranges.each(&block)
end

#encode_rot13!Object



119
120
121
122
123
124
125
# File 'lib/ver/text/tag.rb', line 119

def encode_rot13!
  buffer.undo_record do |record|
    each_range do |range|
      range.encode_rot13!(record)
    end
  end
end

#evaluate!Object

Eval contents of tag and insert them into the buffer.



128
129
130
131
132
133
134
135
136
137
138
# File 'lib/ver/text/tag.rb', line 128

def evaluate!
  file = buffer.filename

  each_range do |range|
    code = range.get

    Methods::Control.stdout_capture_evaluate(code, file, binding) do |res, out|
      range.last.lineend.insert("\n%s%p" % [out, res])
    end
  end
end

#firstObject



140
141
142
# File 'lib/ver/text/tag.rb', line 140

def first
  buffer.index("#{self}.first")
end

#getObject



144
145
146
147
# File 'lib/ver/text/tag.rb', line 144

def get
  values = ranges.map{|range| range.get }
  values.size == 1 ? values.first : values unless values.empty?
end

#indentObject



149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/ver/text/tag.rb', line 149

def indent
  indent_size = buffer.options.shiftwidth
  indent = ' ' * indent_size

  buffer.undo_record do |record|
    each_line do |y, fx, tx|
      tx = fx + indent_size
      next if buffer.get("#{y}.#{fx}", "#{y}.#{tx}").empty?
      record.insert("#{y}.#{fx}", indent)
    end
  end
end

#inspectObject



162
163
164
# File 'lib/ver/text/tag.rb', line 162

def inspect
  "#<VER::Text::Tag %p on %p>" % [name, buffer]
end

#killObject



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/ver/text/tag.rb', line 166

def kill
  indices = []

  chunks = ranges.map{|range|
    indices << range.first << range.last
    range.get
  }

  # A bit of duplication, but if we use copy here we have to iterate the
  # ranges again.
  buffer.with_register do |register|
    register.value = chunks.at(1) ? chunks : chunks.first
  end

  buffer.delete(*indices)
end

#lastObject



183
184
185
# File 'lib/ver/text/tag.rb', line 183

def last
  buffer.index("#{self}.last")
end

#lower(below_this = Tk::None) ⇒ Object Also known as: tag_lower



187
188
189
# File 'lib/ver/text/tag.rb', line 187

def lower(below_this = Tk::None)
  buffer.tag_lower(self, below_this)
end

#lower_case!Object Also known as: downcase!

Convert all characters within the tag to lower-case using String#downcase. Usually only works for alphabetic ASCII characters.



195
196
197
198
199
200
201
# File 'lib/ver/text/tag.rb', line 195

def lower_case!
  buffer.undo_record do |record|
    each_range do |range|
      range.lower_case!(record)
    end
  end
end

#next_range(from_index, to_index = Tk::None) ⇒ Object Also known as: nextrange



204
205
206
# File 'lib/ver/text/tag.rb', line 204

def next_range(from_index, to_index = Tk::None)
  buffer.tag_nextrange(self, from_index, to_index)
end

#pipe!(*cmd) ⇒ Object

Replace contents of tag with stdout output of a command



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/ver/text/tag.rb', line 210

def pipe!(*cmd)
  require 'open3'

  Open3.popen3(*cmd) do |si, so, thread|
    queue = []
    each_range do |range|
      si.write(range.get)
      queue.concat(range.to_a)
    end

    si.close
    output = so.read

    return if queue.empty?

    buffer.undo_record do |record|
      record.delete(*queue)
      record.insert(queue.first, output.chomp)
    end
  end
end

#prev_range(from_index, to_index = Tk::None) ⇒ Object Also known as: prevrange



232
233
234
# File 'lib/ver/text/tag.rb', line 232

def prev_range(from_index, to_index = Tk::None)
  buffer.tag_prevrange(self, from_index, to_index)
end

#raise(above_this = Tk::None) ⇒ Object Also known as: tag_raise



237
238
239
# File 'lib/ver/text/tag.rb', line 237

def raise(above_this = Tk::None)
  buffer.tag_raise(above_this)
end

#rangesObject



242
243
244
# File 'lib/ver/text/tag.rb', line 242

def ranges
  buffer.tag_ranges(self)
end

#remove(index, *indices) ⇒ Object Also known as: tag_remove



246
247
248
# File 'lib/ver/text/tag.rb', line 246

def remove(index, *indices)
  buffer.tag_remove(self, index, *indices)
end

#replace(*args) ⇒ Object



251
252
253
# File 'lib/ver/text/tag.rb', line 251

def replace(*args)
  buffer.range("#{self}.first", "#{self}.last").replace(*args)
end

#to_sObject



255
256
257
# File 'lib/ver/text/tag.rb', line 255

def to_s
  name.to_s
end

#to_tclObject



259
260
261
# File 'lib/ver/text/tag.rb', line 259

def to_tcl
  name.to_tcl
end

#toggle_case!Object

Toggle case within the selection. This only works for alphabetic ASCII characters, no other encodings.



265
266
267
268
269
270
271
# File 'lib/ver/text/tag.rb', line 265

def toggle_case!
  buffer.undo_record do |record|
    each_range do |range|
      range.toggle_case!(record)
    end
  end
end

#uncommentObject



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/ver/text/tag.rb', line 273

def uncomment
  comment = buffer.options.comment_line.to_s
  regex = /#{Regexp.escape(comment)}/

  buffer.undo_record do |record|
    each_line do |y, fx, tx|
      from, to = "#{y}.0 linestart", "#{y}.0 lineend"
      line = buffer.get(from, to)

      if line.sub!(regex, '')
        record.replace(from, to, line)
      end
    end
  end
end

#unindentObject



289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/ver/text/tag.rb', line 289

def unindent
  indent_size = buffer.options.shiftwidth
  indent = ' ' * indent_size
  queue = []

  each_line do |y, fx, tx|
    tx = fx + indent_size
    left, right = "#{y}.#{fx}", "#{y}.#{tx}"
    next unless buffer.get(left, right) == indent
    queue << left << right
  end

  buffer.delete(*queue)
end

#upper_case!Object Also known as: upcase!

Convert all characters within the tag to upper-case using String#upcase. Usually only works for alphabetic ASCII characters.



307
308
309
310
311
312
313
# File 'lib/ver/text/tag.rb', line 307

def upper_case!
  buffer.undo_record do |record|
    each_range do |range|
      range.upper_case!(record)
    end
  end
end

#wrap(width = buffer.prefix_count(80)) ⇒ Object



316
317
318
319
# File 'lib/ver/text/tag.rb', line 316

def wrap(width = buffer.prefix_count(80))
  wrapped = Methods::Control.wrap_lines_of(get, width)
  replace(wrapped.join("\n"))
end