Class: VER::Undo::Record
- Inherits:
-
Struct
- Object
- Struct
- VER::Undo::Record
- Defined in:
- lib/ver/undo.rb
Overview
Every Record is responsible for one change that it can apply or undo. There is only a very limited set of methods for modifications, as some of the destructive String methods in Ruby can have unpredictable results. In order to undo a change, we have to predict what the change will do before it happens to avoid expensive diff algorithms.
A Record has one parent and a number of childs. If there are any childs, one of them is called current, and is the child that was last active, this way we can provide an intuitive way of choosing a child record to apply when a user wants to redo an undone change.
Record has a direct pointer to the data in the tree, since it has to know about nothing else.
Apart from that, Record also knows the time when it was created, this way you can move forward and backward in time.
Revisions only keep the data necessary to undo/redo a change, not the whole data that was modified, that way it can keep overall memory-usage to a minimum.
The applied property indicates whether or not this change has been applied already.
Instance Attribute Summary collapse
-
#applied ⇒ Object
Returns the value of attribute applied.
-
#childs ⇒ Object
Returns the value of attribute childs.
-
#ctime ⇒ Object
Returns the value of attribute ctime.
-
#parent ⇒ Object
Returns the value of attribute parent.
-
#redo_info ⇒ Object
Returns the value of attribute redo_info.
-
#separator ⇒ Object
Returns the value of attribute separator.
-
#undo_info ⇒ Object
Returns the value of attribute undo_info.
-
#widget ⇒ Object
Returns the value of attribute widget.
Instance Method Summary collapse
- #applied? ⇒ Boolean
- #compact! ⇒ Object
- #delete(from, to) ⇒ Object
-
#initialize(widget, parent = nil) ⇒ Record
constructor
A new instance of Record.
- #insert(pos, string) ⇒ Object
- #inspect ⇒ Object
- #next ⇒ Object
- #next=(child) ⇒ Object
- #redo ⇒ Object
- #replace(from, to, string) ⇒ Object
- #undo ⇒ Object
Constructor Details
#initialize(widget, parent = nil) ⇒ Record
Returns a new instance of Record.
146 147 148 149 150 151 152 |
# File 'lib/ver/undo.rb', line 146 def initialize(, parent = nil) self., self.parent = , parent self.ctime = Time.now self.childs = [] self.applied = false self.separator = false end |
Instance Attribute Details
#applied ⇒ Object
Returns the value of attribute applied
143 144 145 |
# File 'lib/ver/undo.rb', line 143 def applied @applied end |
#childs ⇒ Object
Returns the value of attribute childs
143 144 145 |
# File 'lib/ver/undo.rb', line 143 def childs @childs end |
#ctime ⇒ Object
Returns the value of attribute ctime
143 144 145 |
# File 'lib/ver/undo.rb', line 143 def ctime @ctime end |
#parent ⇒ Object
Returns the value of attribute parent
143 144 145 |
# File 'lib/ver/undo.rb', line 143 def parent @parent end |
#redo_info ⇒ Object
Returns the value of attribute redo_info
143 144 145 |
# File 'lib/ver/undo.rb', line 143 def redo_info @redo_info end |
#separator ⇒ Object
Returns the value of attribute separator
143 144 145 |
# File 'lib/ver/undo.rb', line 143 def separator @separator end |
#undo_info ⇒ Object
Returns the value of attribute undo_info
143 144 145 |
# File 'lib/ver/undo.rb', line 143 def undo_info @undo_info end |
#widget ⇒ Object
Returns the value of attribute widget
143 144 145 |
# File 'lib/ver/undo.rb', line 143 def @widget end |
Instance Method Details
#applied? ⇒ Boolean
280 281 282 |
# File 'lib/ver/undo.rb', line 280 def applied? applied end |
#compact! ⇒ Object
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/ver/undo.rb', line 205 def compact! return if separator return unless parent = self.parent pundo_from, pundo_to, pundo_string = parent.undo_info sundo_from, sundo_to, sundo_string = undo_info predo_name, *predo_args = parent.redo_info sredo_name, *sredo_args = redo_info # only compact identical methods return unless predo_name == sredo_name case predo_name when :insert predo_pos, predo_string = predo_args sredo_pos, sredo_string = sredo_args # the records have to be consecutive so they can still be applied by a # single undo/redo consecutive = (predo_pos + predo_string.size) == sredo_pos return parent.compact! unless consecutive redo_string = "#{predo_string}#{sredo_string}" self.redo_info = [:insert, predo_pos, redo_string] undo_string = "#{pundo_string}#{sundo_string}" self.undo_info = [pundo_from, sundo_to, undo_string] when :replace predo_from, predo_to, predo_string = predo_args sredo_from, sredo_to, sredo_string = sredo_args # the records have to be consecutive so they can still be applied by a # single undo/redo consecutive = predo_to == sredo_from return parent.compact! unless consecutive redo_string = "#{predo_string}#{sredo_string}" self.redo_info = [:replace, predo_from, sredo_to, undo_string] undo_string = "#{pundo_string}#{sundo_string}" self.undo_info = [pundo_from, sundo_to, undo_string] when :delete predo_from, predo_to = predo_args sredo_from, sredo_to = sredo_args consecutive = predo_to == sredo_from return parent.compact! unless consecutive self.redo_info = [:delete, predo_from, sredo_to] undo_string = "#{sundo_string}#{pundo_string}" self.undo_info = [pundo_from, sundo_to, undo_string] else return end # the parent of our parent (grandparent) becomes our parent self.parent = grandparent = parent.parent # recurse into a new compact cycle if we have a grandparent if grandparent grandparent.next = self compact! end end |
#delete(from, to) ⇒ Object
178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/ver/undo.rb', line 178 def delete(from, to) from = .index(from) unless from.respond_to?(:to_index) to = .index(to) unless to.respond_to?(:to_index) data = .get(from, to) .execute_only(:delete, from, to) .touch!(*from.upto(to)) self.redo_info = [:delete, from, to] self.undo_info = [from, from, data] self.applied = true end |
#insert(pos, string) ⇒ Object
154 155 156 157 158 159 160 161 162 163 |
# File 'lib/ver/undo.rb', line 154 def insert(pos, string) pos = .index(pos) unless pos.respond_to?(:to_index) .execute_only(:insert, pos, string) .touch!(pos) self.redo_info = [:insert, pos, string] self.undo_info = [pos, pos + string.size, ''] self.applied = true end |
#inspect ⇒ Object
284 285 286 |
# File 'lib/ver/undo.rb', line 284 def inspect "#<Undo::Record sep=%p undo=%p redo=%p>" % [separator, undo_info, redo_info] end |
#next ⇒ Object
276 277 278 |
# File 'lib/ver/undo.rb', line 276 def next childs.first end |
#next=(child) ⇒ Object
272 273 274 |
# File 'lib/ver/undo.rb', line 272 def next=(child) childs.unshift(childs.delete(child) || child) end |
#redo ⇒ Object
200 201 202 203 |
# File 'lib/ver/undo.rb', line 200 def redo return unless redo_info && !applied send(*redo_info) end |
#replace(from, to, string) ⇒ Object
165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/ver/undo.rb', line 165 def replace(from, to, string) from = .index(from) unless from.respond_to?(:to_index) to = .index(to) unless to.respond_to?(:to_index) data = .get(from, to) .execute_only(:replace, from, to, string) .touch!(*from.upto(to)) self.redo_info = [:replace, from, to, string] self.undo_info = [from, from + string.size, data] self.applied = true end |
#undo ⇒ Object
191 192 193 194 195 196 197 198 |
# File 'lib/ver/undo.rb', line 191 def undo return unless undo_info && applied from, to, string = undo_info .execute_only(:replace, from, to, string) self.applied = false end |