Class: DiffLog

Inherits:
Object show all
Defined in:
lib/xiki/diff_log.rb

Overview

Will store a diff each time a file is saved.

Constant Summary collapse

@@log =
File.expand_path("~/.emacs.d/difflog.notes")
@@temp_path =
"/tmp/saved.txt"

Class Method Summary collapse

Class Method Details

.compare_with_savedObject



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/xiki/diff_log.rb', line 156

def self.compare_with_saved

  if Keys.prefix_u

    buffer_unsaved = View.buffer

    path = View.file

    View.to_buffer "untitled"
    $el.rename_uniquely
    View << File.read(path)

    return $el.ediff_buffers View.buffer, buffer_unsaved

  end

  diff = self.save_diffs :dont_log=>1
  diff = "" if diff.nil?

  View.to_buffer("*diff with saved*")
  View.clear
  Notes.mode

  View.insert diff.count("\n") > 2 ?
    diff :
    "> Note\n- No Differences!\n"
end

.diffs(path = nil) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/xiki/diff_log.rb', line 42

def self.diffs path=nil

  txt = File.open(@@log, 'rb') {|f| f.read}
  txt = txt.sub(/\A- /, '').split(/^- /).reverse.uniq

  path ||= Tree.dir   # Pull from tree if there

  path = Bookmarks[path] if path

  if ! path
    regex = /^\//
  elsif File.file? path   # File
    regex = /^#{Regexp.escape File.dirname path}\/\n  - #{Regexp.escape File.basename path}/
  else   # Dir
    regex = /^#{Regexp.escape path}/
    path = "#{path}/" if path !~ /\/$/
  end

  txt = txt.select{|o| o =~ regex}

  "- @#{txt.join("- @")}"
end

.do_compare_withObject



246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/xiki/diff_log.rb', line 246

def self.do_compare_with
  prefix = Keys.prefix :clear=>1

  # up+ means compare buffers in first two views

  return $el.ediff_buffers( $el.window_buffer($el.nth(0, $el.window_list)), $el.window_buffer($el.nth(1, $el.window_list))) if prefix == :u

  # Save place and grab file at spot

  source_path = Tree.dir_at_spot
  dest_path = Tree.dir :file=>1
  $el.ediff_files source_path, dest_path

end

.enter_from_difflogObject



184
185
186
187
188
189
# File 'lib/xiki/diff_log.rb', line 184

def self.enter_from_difflog
  Location.as_spot
  View.to_after_bar if View.in_bar?
  DiffLog.open
  $el.isearch_backward
end

.enter_newObject

Insert new text added during last save



81
82
83
# File 'lib/xiki/diff_log.rb', line 81

def self.enter_new
  self.enter_old_or_new :new
end

.enter_oldObject



85
86
87
# File 'lib/xiki/diff_log.rb', line 85

def self.enter_old
  self.enter_old_or_new :old
end

.enter_old_or_new(old_or_new) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/xiki/diff_log.rb', line 89

def self.enter_old_or_new old_or_new

  diff = DiffLog.last_diff
  one_line_change = DiffLog.is_one_line_change? diff

  # Show intraline change if changed just one line and not up+

  if ! Keys.up? && one_line_change
    View.<< DiffLog.last_intraline_diff[old_or_new == :old ? 0 : 1] rescue View.beep $!
    return
  end

  # Show lines

  diff.gsub! /^ *[+:-].*\n/, ""   # Only leave red and green lines
  if old_or_new == :old
    diff.gsub! /^ +\|\+.*\n/, ""
    diff.gsub! /^ +\|\-/, ""
  else
    diff.gsub! /^ +\|\-.*\n/, ""
    diff.gsub! /^ +\|\+/, ""
  end
  View << diff

end

.format(raw) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/xiki/diff_log.rb', line 133

def self.format raw
  path, file = raw.match(/--- (.+\/)(.+?)\t/)[1..2]

  # Delete paths at top
  raw.sub!(/.+\n.+\n/, '')

  # Make @@... lines into lines having numbers
  raw.gsub!(/^@@ -(\d+).* \+(\d+).+@@$/) {
    a, b = $1.to_i, $2.to_i
    highest = a > b ? a : b
    "    :#{highest}"
  }

  # Make - and + lines into -| and +| lines
  raw.gsub!(/^\+(.*)/, "      |+\\1")
  raw.gsub!(/^-(.*)/, "      |-\\1")

  # Return with path
  "- #{path}\n" +
  "  - #{file}\n" +
  raw
end

.is_one_line_change?(txt) ⇒ Boolean

Returns:

  • (Boolean)


74
75
76
77
78
# File 'lib/xiki/diff_log.rb', line 74

def self.is_one_line_change? txt
  txt.scan(/^ +\|\-/).length == 1 &&
    txt.scan(/^ +\|\+/).length == 1 &&
    txt.scan(/^ +:/).length
end

.last_diffObject

Insert old text deleted during last save



66
67
68
69
70
71
72
# File 'lib/xiki/diff_log.rb', line 66

def self.last_diff
  $el.with(:save_window_excursion) do
    DiffLog.open
    Search.backward "^-"
    txt = View.txt View.cursor, View.bottom
  end
end

.last_intraline_diff(txt = nil) ⇒ Object

Grabs last diff from difflog, and calculates exactly what changed. Whithin the line. So, it performs a single-line diff

It presumes a single-line change, and a change at only one point on the line.

DiffLog.last_intraline_diff



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/xiki/diff_log.rb', line 269

def self.last_intraline_diff txt=nil
  txt ||= DiffLog.last_diff

  linea = txt[/ +\|\-(.+)/, 1]
  lineb = txt[/ +\|\+(.+)/, 1]

  raise "The last diff in the difflog didn't have a red and a green." if linea.nil? || lineb.nil?

  linea_length, lineb_length = linea.length, lineb.length

  from_start = 0
  while from_start < linea_length
    break if linea[from_start] != lineb[from_start]
    from_start += 1
  end

  from_end = 1
  while from_end < linea_length
    break if from_end > linea_length || from_end > lineb_length || linea[linea_length-from_end] != lineb[lineb_length-from_end]
    from_end += 1
  end

  deltaa = from_end > linea_length ? "" : linea[from_start..(linea_length-from_end)]
  delteab = from_end > lineb_length ? "" : lineb[from_start..(lineb_length-from_end)]

  [deltaa, delteab]
end


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/xiki/diff_log.rb', line 9

def self.menu
  "
  .open/
  .diffs/
  .diffs/$p/
  docs/
    > Summary
    | The difflog tracks diffs of all the changes you make to file
    | (assuming you save using as+file).
    |
    > Keys
    | open+diffs - open the difflog
    | search+diffs - search the difflog from the bottom
  "
end

.openObject

Open file having difflog



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/xiki/diff_log.rb', line 26

def self.open
  View.to_after_bar if View.in_bar?

  # If open, just switch to it and revert
  if View.buffer_open?("difflog.notes")
    View.to_buffer("difflog.notes")
    $el.revert_buffer true, true, true
  else  # Otherwise, open it
    View.open(@@log)
  end
  View.to_bottom
  Line.previous
  Line.to_words
  $el.recenter -4
end

.parse_tree_diffs(txt) ⇒ Object



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
# File 'lib/xiki/diff_log.rb', line 210

def self.parse_tree_diffs txt
  result = [[], []]

  txt = txt.split("\n")
  i, length = 0, txt.length

  while i < length
    line = txt[i]
    #       a_or_d = line[/^[ad]/]
    match = line.match(/^(.)(\d+) (\d+)/)
    raise "Line #{line} unexpected by DiffLog.parse_tree_diffs" if ! match
    a_or_d, line_number, many = match[1..3]
    line_number, many = line_number.to_i, many.to_i

    if a_or_d == "d"
      i += 1
      many.times do |n|
        result[1] << line_number + n
      end
      next
    end

    if a_or_d == "a"
      i += 1
      many.times do |n|
        result[0] << line_number + n
      end
      next
    end

    raise "DiffLog.parse_tree_diffs doesn't know what to do with #{line}"
  end

  result
end

.saveObject

Appends diff to difflog, then saves. Mapped to as_file.



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/xiki/diff_log.rb', line 116

def self.save
  return if View.file_name == "difflog.notes"

  prefix = Keys.prefix :clear=>1

  self.save_diffs

  $el.save_buffer

  if prefix == :u
    sleep(0.3)
    Firefox.reload
  elsif prefix == 9
    Launcher.do_last_launch
  end
end

.save_diffs(options = {}) ⇒ Object

Util function used by public functions



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/xiki/diff_log.rb', line 192

def self.save_diffs options={}
  if options[:patha] && options[:textb]
    patha = options[:patha]
    File.open(@@temp_path, "w") { |f| f << options[:textb] }
  else
    patha = View.file
    $el.write_region nil, nil, @@temp_path
  end

  diff = Console.sync "diff -U 0 \"#{patha}\" \"#{@@temp_path}\""
  return if diff.empty? || diff =~ /: No such file or directory\n/   # Fail quietly if file didn't exist

  diff = self.format(diff) rescue nil
  return diff if diff.nil? || options[:dont_log]

  File.open(@@log, "a") { |f| f << diff } unless diff.count("\n") <= 2
end