Skip to content

Instantly share code, notes, and snippets.

@molsonkiko
Last active September 1, 2023 21:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save molsonkiko/408493626c31695e13a0a5270eea9c94 to your computer and use it in GitHub Desktop.
Save molsonkiko/408493626c31695e13a0a5270eea9c94 to your computer and use it in GitHub Desktop.
Merge files by line (Notepad++ PythonScript script)
# -*- coding: utf-8 -*-
# reference: https://community.notepad-plus-plus.org/topic/24872/is-there-any-alternative-to-merge-files-in-one-plugin/7?_=1693515221904
# installation guide: https://community.notepad-plus-plus.org/topic/23039/faq-desk-how-to-install-and-run-a-script-in-pythonscript
from __future__ import print_function
import os
import re
from Npp import *
__version__ = '0.2.0'
EOLS = ('\r\n', '\r', '\n')
def getCurrentEol():
return EOLS[editor.getEOLMode()]
def main():
fnames = [x[0] for x in notepad.getFiles()]
setfnames = set(fnames)
fnames_str = '\r\n'.join(fnames)
while True:
fnames_chosen_str = notepad.prompt("remove the names of any files in this list that you don't want to merge", 'merge files together', fnames_str)
if fnames_chosen_str is None:
return
fnames_chosen = [f for f in fnames_chosen_str.split('\r\n') if f]
bad = False
for fname in fnames_chosen:
if fname not in setfnames:
bad = True
notepad.messageBox(
'Filename %s was not a currently open file' % fname,
'nonexistent filename',
MESSAGEBOXFLAGS.ICONEXCLAMATION
)
break
if not bad:
break
lines_fname_map = {}
for fname in fnames_chosen:
notepad.open(fname)
text = editor.getText()
eol = getCurrentEol()
lines = text.split(eol)
lines_fname_map[fname] = lines
maxlen = max(len(x) for x in lines_fname_map.values())
merged = []
collisions = {}
for ii in range(maxlen):
line_filled_by = None
for fname, lines in lines_fname_map.items():
len_ = len(lines)
if ii >= len_:
continue
line = lines[ii]
if line:
if line_filled_by:
if ii not in collisions:
collisions[ii] = {line_filled_by: merged[ii]}
collisions[ii][fname] = line
else:
line_filled_by = fname
merged.append(line)
n_collisions = len(collisions)
many_collisions = n_collisions > 10
short_fnames = {}
short_fname_values = set()
for fname in fnames_chosen:
fname_parts = fname.split(os.sep)
first_part_idx = len(fname_parts) - 1
short_fname = '\\'.join(fname_parts[first_part_idx:])
while first_part_idx >= 0 and short_fname in short_fname_values:
first_part_idx -= 1
short_fname = '\\'.join(fname_parts[first_part_idx:])
short_fname_values.add(short_fname)
short_fnames[fname] = short_fname
for coll_ix, (ii, collision) in enumerate(collisions.items()):
collisions_text_list = []
collision_lines = list(collision.values())
for fname, line in collision.items():
existing_choice = merged[ii]
collisions_text_list.append('[ %s ] %r : %r' % (
'X' if line == existing_choice else '',
short_fnames[fname],
line
))
collisions_text = '\r\n'.join(collisions_text_list)
result = notepad.prompt('Collision at line %d. Put an X in the box of the file whose line you want to keep, then hit OK or Cancel to skip this line' % ii, 'merge collisions', collisions_text)
stop_processing = False
if result:
for jj, result_line in enumerate(result.split('\r\n')):
if re.search(r'^\s*\[\s*\S\s*\]', result_line):
line_to_use = collision_lines[jj]
merged[ii] = line_to_use
break
else:
stop_processing = True
remaining_colisions = n_collisions - coll_ix - 1
if coll_ix % 5 == 0 and remaining_colisions >= 5:
mb_result = notepad.messageBox(
'There are %d unresolved collisions left. Stop processing them now?' % remaining_colisions,
'Many collisions left',
MESSAGEBOXFLAGS.ICONQUESTION | MESSAGEBOXFLAGS.YESNO
)
if mb_result == MESSAGEBOXFLAGS.RESULTYES:
break
notepad.new()
editor.setText('\r\n'.join(merged))
notepad.messageBox('This file contains the merger of the selected files', 'merger complete')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment