-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathinit.lua
588 lines (562 loc) · 23.4 KB
/
init.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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
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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
-- Copyright 2015-2025 Mitchell. See LICENSE.
--- Two-way file comparison for Textadept.
-- Install this module by copying it into your *~/.textadept/modules/* directory or Textadept's
-- *modules/* directory, and then putting the following in your *~/.textadept/init.lua*:
--
-- ```lua
-- local file_diff = require('file_diff')
-- ```
--
-- ## Compiling
--
-- Releases include binaries, so building this modules should not be necessary. If you want
-- to build manually, use CMake. For example:
--
-- ```bash
-- cmake -S . -B build_dir
-- cmake --build build_dir --target diff
-- cmake --install build_dir
-- ```
--
-- ## Usage
--
-- A sample workflow is this:
-- 1. Start comparing two files via the "Compare Files" submenu in the "Tools" menu.
-- 2. The caret is initially placed in the file on the left.
-- 3. Go to the next change via menu or key binding.
-- 4. Merge the change from the other buffer into the current one (right to left) via menu or
-- key binding.
-- 5. Go to the next change via menu or key binding.
-- 6. Merge the change from the current buffer into the other one (left to right) via menu or
-- key binding.
-- 7. Repeat as necessary.
--
-- Note: merging can be performed wherever the caret is placed when jumping between changes,
-- even if one buffer has a change and the other does not (additions or deletions).
--
-- ## Key Bindings
--
-- Windows and Linux | macOS | Terminal | Command
-- -|-|-|-
-- **Tools**| | |
-- F6 | F6 | None | Compare files...
-- Shift+F6 | ⇧F6 | None | Compare the buffers in two split views
-- Ctrl+F6 | ⌘F6 | None | Stop comparing
-- Ctrl+Alt+. | ^⌘. | None | Goto next difference
-- Ctrl+Alt+, | ^⌘, | None | Goto previous difference
-- Ctrl+Alt+< | ^⌘< | None | Merge left
-- Ctrl+Alt+> | ^⌘> | None | Merge right
-- @module file_diff
local M = {}
--- The marker for line additions.
M.MARK_ADDITION = view.new_marker_number()
--- The marker for line deletions.
M.MARK_DELETION = view.new_marker_number()
--- The marker for line modifications.
M.MARK_MODIFICATION = view.new_marker_number()
--- The indicator number for text added within lines.
M.INDIC_ADDITION = view.new_indic_number()
--- The indicator number for text deleted within lines.
M.INDIC_DELETION = view.new_indic_number()
local MARK_ADDITION = M.MARK_ADDITION
local MARK_DELETION = M.MARK_DELETION
local MARK_MODIFICATION = M.MARK_MODIFICATION
local INDIC_ADDITION = M.INDIC_ADDITION
local INDIC_DELETION = M.INDIC_DELETION
--- The name of the theme color used to mark additions.
-- The default value is 'green'. If your theme does not define that color, set this field to
-- your theme's equivalent.
M.addition_color_name = 'green'
--- The name of the theme color used to mark deletions.
-- The default value is 'red'. If your theme does not define that color, set this field to your
-- theme's equivalent.
M.deletion_color_name = 'red'
--- The name of the theme color used to mark modifications.
-- The default value is 'yellow'. If your theme does not define that color, set this field to
-- your theme's equivalent.
M.modification_color_name = 'yellow'
-- Localizations.
local _L = _L
if not rawget(_L, 'Compare Files') then
-- Dialogs.
_L['Select the first file to compare'] = 'Select the first file to compare'
_L['Select the file to compare to'] = 'Select the file to compare to'
-- Status.
_L['No more differences'] = 'No more differences'
-- Menu.
_L['Compare Files'] = 'Compare _Files'
_L['Compare Files...'] = '_Compare Files...'
_L['Compare This File With...'] = 'Compare This File _With...'
_L['Compare Buffers'] = 'Compare _Buffers'
_L['Next Change'] = '_Next Change'
_L['Previous Change'] = '_Previous Change'
_L['Merge Left'] = 'Merge _Left'
_L['Merge Right'] = 'Merge _Right'
_L['Stop Comparing'] = '_Stop Comparing'
end
local lib = 'file_diff.diff'
if OSX then
lib = lib .. 'osx'
elseif LINUX and io.popen('uname -m'):read() == 'aarch64' then
lib = lib .. 'arm'
end
local diff = require(lib)
local DELETE, INSERT = 0, 1 -- C++: "enum Operation {DELETE, INSERT, EQUAL};"
local view1, view2
--- Clear markers, indicators, and placeholder lines.
-- Used when re-marking changes or finished comparing.
local function clear_marked_changes()
local buffer1 = _VIEWS[view1] and view1.buffer
local buffer2 = _VIEWS[view2] and view2.buffer
for _, mark in ipairs{MARK_ADDITION, MARK_DELETION, MARK_MODIFICATION} do
if buffer1 then buffer1:marker_delete_all(mark) end
if buffer2 then buffer2:marker_delete_all(mark) end
end
for _, indic in ipairs{INDIC_ADDITION, INDIC_DELETION} do
if buffer1 then
buffer1.indicator_current = indic
buffer1:indicator_clear_range(1, buffer1.length)
end
if buffer2 then
buffer2.indicator_current = indic
buffer2:indicator_clear_range(1, buffer2.length)
end
end
if buffer1 then buffer1:annotation_clear_all() end
if buffer2 then buffer2:annotation_clear_all() end
end
local synchronizing = false
--- Synchronize the scroll and line position of the other buffer.
local function synchronize()
synchronizing = true
local line = buffer:line_from_position(buffer.current_pos)
local visible_line = view:visible_from_doc_line(line)
local first_visible_line = view.first_visible_line
local x_offset = view.x_offset
ui.goto_view(view == view1 and view2 or view1)
buffer:goto_line(view:doc_line_from_visible(visible_line))
view.first_visible_line, view.x_offset = first_visible_line, x_offset
ui.goto_view(view == view2 and view1 or view2)
synchronizing = false
end
--- Returns the number of lines contained in the given string.
local function count_lines(text)
local lines = 1
for _ in text:gmatch('\n') do lines = lines + 1 end
return lines
end
--- Mark the differences between the two buffers.
local function mark_changes()
if not _VIEWS[view1] or not _VIEWS[view2] then return end
clear_marked_changes() -- clear previous marks
local buffer1, buffer2 = view1.buffer, view2.buffer
-- Perform the diff.
local diffs = diff(buffer1:get_text(), buffer2:get_text())
-- Parse the diff, marking modified lines and changed text.
local pos1, pos2 = 1, 1
for i = 1, #diffs, 2 do
local op, text = diffs[i], diffs[i + 1]
local text_len = #text
if op == DELETE then
local next_op, next_text = diffs[i + 2], diffs[i + 3]
-- Mark partial lines as modified and full lines as deleted.
local start_line = buffer1:line_from_position(pos1)
local end_line = buffer1:line_from_position(pos1 + text_len)
local mark = MARK_MODIFICATION -- assume partial initially
if next_op ~= INSERT then
-- Deleting full line(s), either from line start to next line(s) start, or from line
-- end to next line(s) end. Adjust `start_line` and `end_line` accordingly for accurate
-- line markers.
if pos1 == buffer1:position_from_line(start_line) and
(pos1 + text_len == buffer1:position_from_line(end_line)) then
mark = MARK_DELETION
end_line = end_line - 1
elseif pos1 == buffer1.line_end_position[start_line] and
(pos1 + text_len == buffer1.line_end_position[end_line]) then
mark = MARK_DELETION
start_line = start_line + 1
end
end
for j = start_line, end_line do buffer1:marker_add(j, mark) end
-- Highlight deletion from partially changed line(s) and mark other line as modified.
if mark == MARK_MODIFICATION then
buffer1.indicator_current = INDIC_DELETION
buffer1:indicator_fill_range(pos1, text_len)
buffer2:marker_add(buffer2:line_from_position(pos2), mark)
end
pos1 = pos1 + text_len
-- Calculate net change in lines and fill in empty space in either buffer with annotations
-- if necessary.
if next_op == INSERT then
-- Deleting line(s) in favor of other lines. If those other lines are more numerous,
-- then fill empty space in this buffer.
local num_lines = count_lines(text)
local next_num_lines = count_lines(next_text)
if num_lines < next_num_lines then
local annotation_lines = next_num_lines - num_lines - 1
local annotation_text = ' ' ..
string.rep('\n', annotation_lines + buffer1.annotation_lines[end_line])
buffer1.annotation_text[end_line] = annotation_text
end
elseif mark == MARK_DELETION then
-- Deleting full line(s) with no replacement, so fill empty space in other buffer.
local offset = not text:find('^\n') and 1 or 0
local line = math.max(buffer2:line_from_position(pos2) - offset, 1)
local annotation_lines = end_line - start_line
local annotation_text = ' ' .. string.rep('\n', annotation_lines)
buffer2.annotation_text[line] = annotation_text
else
-- Deleting partial line(s) with no replacement, so fill empty space in other buffer.
local extra_lines = count_lines(text) - 1
if extra_lines > 0 then
local line = buffer2:line_from_position(pos2)
local annotation_lines = extra_lines - 1
local annotation_text = ' ' .. string.rep('\n', annotation_lines)
buffer2.annotation_text[line] = annotation_text
end
end
elseif op == INSERT then
local prev_op, prev_text = diffs[i - 2], diffs[i - 1]
-- Mark partial lines as modified and full lines as deleted.
local start_line = buffer2:line_from_position(pos2)
local end_line = buffer2:line_from_position(pos2 + text_len)
local mark = MARK_MODIFICATION -- assume partial initially
if prev_op ~= DELETE then
-- Adding full line(s), either from line start to next line(s) start, or from line end
-- to next line(s) end. Adjust `start_line` and `end_line` accordingly for accurate
-- line markers.
if pos2 == buffer2:position_from_line(start_line) and
(pos2 + text_len == buffer2:position_from_line(end_line)) then
mark = MARK_ADDITION
end_line = end_line - 1
elseif pos2 == buffer2.line_end_position[start_line] and
(pos2 + text_len == buffer2.line_end_position[end_line]) then
mark = MARK_ADDITION
start_line = start_line + 1
end
end
for j = start_line, end_line do buffer2:marker_add(j, mark) end
-- Highlight addition from partially changed line(s) and mark other line as modified.
if mark == MARK_MODIFICATION then
buffer2.indicator_current = INDIC_ADDITION
buffer2:indicator_fill_range(pos2, text_len)
buffer1:marker_add(buffer1:line_from_position(pos1), mark)
end
pos2 = pos2 + text_len
-- Calculate net change in lines and fill in empty space in either buffer with annotations
-- if necessary.
if prev_op == DELETE then
-- Adding line(s) in favor of other lines. If those other lines are more numerous,
-- then fill empty space in this buffer.
local num_lines = count_lines(text)
local prev_num_lines = count_lines(prev_text)
if num_lines < prev_num_lines then
local annotation_lines = prev_num_lines - num_lines - 1
local annotation_text = ' ' ..
string.rep('\n', annotation_lines + buffer2.annotation_lines[end_line])
buffer2.annotation_text[end_line] = annotation_text
end
elseif mark == MARK_ADDITION then
-- Adding full line(s) with no replacement, so fill empty space in other buffer.
local offset = not text:find('^\n') and 1 or 0
local line = math.max(buffer1:line_from_position(pos1) - offset, 1)
local annotation_lines = end_line - start_line
local annotation_text = ' ' .. string.rep('\n', annotation_lines)
buffer1.annotation_text[line] = annotation_text
else
-- Adding partial line(s) with no replacement, so fill empty space in other buffer.
local extra_lines = count_lines(text) - 1
if extra_lines > 0 then
local line = buffer1:line_from_position(pos1)
local annotation_lines = extra_lines - 1
local annotation_text = ' ' .. string.rep('\n', annotation_lines)
buffer1.annotation_text[line] = annotation_text
end
end
else
pos1, pos2 = pos1 + text_len, pos2 + text_len
end
end
synchronize()
end
local starting_diff = false
--- Highlight differences between files.
-- @param[opt] file1 String older filename. If `-`, uses the current buffer. If `nil`, the user
-- is prompted for a file.
-- @param[optchain] file2 String newer filename. If `-`, uses the current buffer. If `nil`, the user
-- is prompted for a file.
-- @param[optchain=false] horizontal Split the view horizontally instead of vertically. The
-- default is to compare files side-by-side.
function M.start(file1, file2, horizontal)
file1 = file1 or ui.dialogs.open{
title = _L['Select the first file to compare'],
dir = (buffer.filename or ''):match('^(.+)[/\\]') or lfs.currentdir()
}
if not file1 then return end
file2 = file2 or ui.dialogs.open{
title = string.format('%s %s', _L['Select the file to compare to'], file1:match('[^/\\]+$')),
dir = file1:match('^(.+)[/\\]') or lfs.currentdir()
}
if not file2 then return end
starting_diff = true
if not _VIEWS[view1] or not _VIEWS[view2] and #_VIEWS > 1 then
view1, view2 = _VIEWS[1], _VIEWS[2] -- preserve current split views
end
if _VIEWS[view1] and view ~= view1 then ui.goto_view(view1) end
if file1 ~= '-' then io.open_file(file1) end
view.annotation_visible = view.ANNOTATION_STANDARD -- view1
if not _VIEWS[view1] or not _VIEWS[view2] then
view1, view2 = view:split(not horizontal)
else
ui.goto_view(view2)
end
if file2 ~= '-' then io.open_file(file2) end
view.annotation_visible = view.ANNOTATION_STANDARD -- view2
ui.goto_view(view1)
starting_diff = false
if file1 == '-' or file2 == '-' then mark_changes() end
end
--- Stops comparing.
local function stop()
if not _VIEWS[view1] or not _VIEWS[view2] then return end
clear_marked_changes()
view1, view2 = nil, nil
end
-- Stop comparing when one of the buffer's being compared is switched or closed.
events.connect(events.BUFFER_BEFORE_SWITCH, function() if not starting_diff then stop() end end)
events.connect(events.BUFFER_DELETED, stop)
--- Retrieves a line number's equivalent in the other buffer.
-- @param line Line to get the synchronized equivalent of in the other buffer.
-- @return line
local function get_synchronized_line(line)
local visible_line = view:visible_from_doc_line(line)
local pos = buffer.current_pos
ui.goto_view(view == view1 and view2 or view1)
line = view:doc_line_from_visible(visible_line)
ui.goto_view(view == view2 and view1 or view2)
buffer:set_empty_selection(pos)
return line
end
--- Jumps to the next or previous difference between the two files.
-- `file_diff.start()` must have been called previously.
-- @param[opt=false] next Go to the next previous difference relative to the current line,
-- as opposed to the previous one.
function M.goto_change(next)
if not _VIEWS[view1] or not _VIEWS[view2] then return end
-- Determine the line to start on, keeping in mind the synchronized line numbers may be different.
local line1, line2
local step = next and 1 or -1
if view == view1 then
line1 = buffer:line_from_position(buffer.current_pos) + step
if line1 < 1 then line1 = 1 end
line2 = get_synchronized_line(line1)
else
line2 = buffer:line_from_position(buffer.current_pos) + step
if line2 < 1 then line2 = 1 end
line1 = get_synchronized_line(line2)
end
-- Search for the next change or set of changes, wrapping as necessary.
-- A block of additions, deletions, or modifications should be treated as a single change.
local buffer1, buffer2 = view1.buffer, view2.buffer
local diff_marker = 1 << MARK_ADDITION - 1 | 1 << MARK_DELETION - 1 | 1 << MARK_MODIFICATION - 1
local f = next and buffer.marker_next or buffer.marker_previous
line1 = f(buffer1, line1, diff_marker)
while line1 >= 1 and
(buffer1:marker_get(line1) & diff_marker == buffer1:marker_get(line1 - step) & diff_marker) do
line1 = f(buffer1, line1 + step, diff_marker)
end
line2 = f(buffer2, line2, diff_marker)
while line2 >= 1 and
(buffer2:marker_get(line2) & diff_marker == buffer2:marker_get(line2 - step) & diff_marker) do
line2 = f(buffer2, line2 + step, diff_marker)
end
if line1 < 1 and line2 < 1 then
line1 = f(buffer1, next and 1 or buffer1.line_count, diff_marker)
line2 = f(buffer2, next and 1 or buffer2.line_count, diff_marker)
end
if line1 < 1 and line2 < 1 then
ui.statusbar_text = _L['No more differences']
return
end
-- Determine which change is closer to the current line, keeping in mind the synchronized
-- line numbers may be different. (For example, one buffer may have a block of modifications
-- next while the other buffer has a block of additions next, and those additions logically
-- come first.)
if view == view1 then
if line2 >= 1 then
ui.goto_view(view2)
local visible_line = view:visible_from_doc_line(line2)
ui.goto_view(view1)
local line2_1 = view:doc_line_from_visible(visible_line)
buffer:goto_line(line1 >= 1 and (next and line1 < line2_1 or not next and line1 > line2_1) and
line1 or line2_1)
else
buffer:goto_line(line1)
end
else
if line1 >= 1 then
ui.goto_view(view1)
local visible_line = view:visible_from_doc_line(line1)
ui.goto_view(view2)
local line1_2 = view:doc_line_from_visible(visible_line)
buffer:goto_line(line2 >= 1 and (next and line2 < line1_2 or not next and line2 > line1_2) and
line2 or line1_2)
else
buffer:goto_line(line2)
end
end
view:vertical_center_caret()
end
--- Merges a change from one buffer to another, depending on the change under the caret and the
-- merge direction.
-- @param[opt=false] left Merge from right to left as opposed to left to right.
function M.merge(left)
if not _VIEWS[view1] or not _VIEWS[view2] then return end
local buffer1, buffer2 = view1.buffer, view2.buffer
-- Determine whether or not there is a change to merge.
local start_line = buffer:line_from_position(buffer.current_pos)
local end_line = start_line + 1
local diff_marker = 1 << MARK_ADDITION - 1 | 1 << MARK_DELETION - 1 | 1 << MARK_MODIFICATION - 1
local marker = buffer:marker_get(start_line) & diff_marker
if marker == 0 then
-- Look for additions or deletions from the other buffer, which are offset one line down
-- (side-effect of Scintilla's visible line -> doc line conversions).
local line = get_synchronized_line(start_line) + 1
if (view == view1 and buffer2 or buffer1):marker_get(line) & diff_marker > 0 then
ui.goto_view(view == view1 and view2 or view1)
buffer:set_empty_selection(buffer:position_from_line(line))
M.merge(left)
ui.goto_view(view == view2 and view1 or view2)
buffer:set_empty_selection(buffer:position_from_line(start_line))
end
return
end
-- Determine the bounds of the change target it.
while buffer:marker_get(start_line - 1) & diff_marker == marker do start_line = start_line - 1 end
buffer.target_start = buffer:position_from_line(start_line)
while buffer:marker_get(end_line) & diff_marker == marker do end_line = end_line + 1 end
buffer.target_end = buffer:position_from_line(end_line)
-- Perform the merge, depending on context.
if marker == 1 << MARK_ADDITION - 1 then
if left then
-- Merge addition from right to left.
local line = get_synchronized_line(end_line)
buffer1:insert_text(buffer1:position_from_line(line), buffer2.target_text)
else
-- Merge "deletion" (empty text) from left to right.
buffer2:replace_target('')
end
elseif marker == 1 << MARK_DELETION - 1 then
if left then
-- Merge "addition" (empty text) from right to left.
buffer1:replace_target('')
else
-- Merge deletion from left to right.
local line = get_synchronized_line(end_line)
buffer2:insert_text(buffer2:position_from_line(line), buffer1.target_text)
end
elseif marker == 1 << MARK_MODIFICATION - 1 then
local target_text = buffer.target_text
start_line = get_synchronized_line(start_line)
end_line = get_synchronized_line(end_line)
ui.goto_view(view == view1 and view2 or view1)
buffer.target_start = buffer:position_from_line(start_line)
buffer.target_end = buffer:position_from_line(end_line)
if view == view2 and left or view == view1 and not left then
-- Merge change from opposite view.
target_text = buffer.target_text
ui.goto_view(view == view2 and view1 or view2)
buffer:replace_target(target_text)
else
-- Merge change to opposite view.
buffer:replace_target(target_text)
ui.goto_view(view == view2 and view1 or view2)
end
end
mark_changes() -- refresh
end
-- TODO: connect to these in `start()` and disconnect in `stop()`?
-- Ensure the diff buffers are scrolled in sync.
events.connect(events.UPDATE_UI, function(updated)
if _VIEWS[view1] and _VIEWS[view2] and updated and not synchronizing then
if updated & (view.UPDATE_H_SCROLL | view.UPDATE_V_SCROLL | buffer.UPDATE_SELECTION) > 0 then
synchronize()
end
end
end)
-- Highlight differences as text is typed and deleted.
events.connect(events.MODIFIED, function(position, modification_type)
if not _VIEWS[view1] or not _VIEWS[view2] then return end
if modification_type & (0x01 | 0x02) > 0 then mark_changes() end -- insert text | delete text
end)
events.connect(events.VIEW_NEW, function()
local markers = {
[MARK_ADDITION] = M.addition_color_name, [MARK_DELETION] = M.deletion_color_name,
[MARK_MODIFICATION] = M.modification_color_name
}
for mark, color in pairs(markers) do
view:marker_define(mark, not CURSES and view.MARK_BACKGROUND or view.MARK_FULLRECT)
if view.colors[color] then view.marker_back[mark] = view.colors[color] end
if not CURSES then
view.marker_layer[mark], view.marker_alpha[mark] = view.LAYER_UNDER_TEXT, 0x60
end
end
local indicators = {
[INDIC_ADDITION] = M.addition_color_name, [INDIC_DELETION] = M.deletion_color_name
}
for indic, color in pairs(indicators) do
view.indic_style[indic] = not CURSES and view.INDIC_FULLBOX or view.INDIC_STRAIGHTBOX
if view.colors[color] then view.indic_fore[indic] = view.colors[color] end
if not CURSES then view.indic_alpha[indic], view.indic_under[indic] = 0x60, true end
end
end)
args.register('-d', '--diff', 2, M.start, 'Compares two files')
-- Add a menu and configure key bindings.
-- (Insert 'Compare Files' menu in alphabetical order.)
local m_tools = textadept.menu.menubar['Tools']
local found_area
for i = 1, #m_tools - 1 do
if not found_area and m_tools[i + 1].title == _L['Bookmarks'] then
found_area = true
elseif found_area then
local label = m_tools[i].title or m_tools[i][1]
if 'Compare Files' < label:gsub('^_', '') or m_tools[i][1] == '' then
table.insert(m_tools, i, {
title = _L['Compare Files'], --
{_L['Compare Files...'], M.start}, {
_L['Compare This File With...'],
function() if buffer.filename then M.start(buffer.filename) end end
}, {_L['Compare Buffers'], function() M.start('-', '-') end}, --
{''}, --
{_L['Next Change'], function() M.goto_change(true) end},
{_L['Previous Change'], M.goto_change}, --
{''}, --
{_L['Merge Left'], function() M.merge(true) end}, --
{_L['Merge Right'], M.merge}, --
{''}, --
{_L['Stop Comparing'], stop}
})
break
end
end
end
keys.assign_platform_bindings{
[M.start] = {'f6', 'f6', nil},
[m_tools[_L['Compare Files']][_L['Compare Buffers']][2]] = {'shift+f6', 'shift+f6', nil},
[m_tools[_L['Compare Files']][_L['Stop Comparing']][2]] = {'ctrl+f6', 'cmd+f6', nil},
[m_tools[_L['Compare Files']][_L['Next Change']][2]] = {'ctrl+alt+.', 'ctrl+cmd+.', nil},
[M.goto_change] = {'ctrl+alt+,', 'ctrl+cmd+,', nil},
[m_tools[_L['Compare Files']][_L['Merge Left']][2]] = {'ctrl+alt+<', 'ctrl+cmd+<', nil},
[M.merge] = {'ctrl+alt+>', 'ctrl+cmd+>', nil}
}
return M
-- The function below is a Lua C function.
--- Returns a list of the differences between strings.
-- Each consecutive pair of elements in the returned list represents a "diff". The first element
-- is an integer: 0 for a deletion, 1 for an insertion, and 2 for equality. The second element
-- is the associated diff text.
-- @param text1 String to compare against.
-- @param text2 String to compare.
-- @usage diffs = diff(text1, text2)
-- @usage for i = 1, #diffs, 2 do print(diffs[i], diffs[i + 1]) end
-- @function _G.diff