Skip to content

Instantly share code, notes, and snippets.

@skt041959
Last active November 11, 2021 12:26
Show Gist options
  • Save skt041959/688a768223bb89a7c3da0fe8a34cc705 to your computer and use it in GitHub Desktop.
Save skt041959/688a768223bb89a7c3da0fe8a34cc705 to your computer and use it in GitHub Desktop.
import vim
import re
import json
import asyncio
import logging
try:
from patiencediff import PatienceSequenceMatcher as SequenceMatcher
except ImportError:
from difflib import SequenceMatcher
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.debug('session loaded')
LUA_CODE = """
_G.fold_fromsearch = function(winid)
vim.api.nvim_win_call(winid, function()
vim.wo.foldmethod = 'manual'
vim.cmd([[normal! zE]])
vim.wo.foldminlines = 0
vim.wo.foldlevel = 0
vim.wo.foldtext = "(v:folddashes.'').((v:foldend)-(v:foldstart)+(1))"
local lastline = 0
for _,l in ipairs(vim.b.searchfold_matches) do
if l - lastline > 1 then
vim.cmd( tostring(lastline+1) .. ',' .. tostring(l-1) .. 'fold' )
end
lastline = l
end
if vim.api.nvim_buf_line_count(0) - lastline > 1 then
vim.cmd( tostring(lastline+1) .. ',$' .. 'fold' )
end
end)
end
"""
vim.exec_lua(LUA_CODE)
def logdiff_loglevel(level):
logger.setLevel(logging.getLevelName(level))
async def async_searchfold(bufnr, winid, filename, pattern):
command = ['rg', '-z', '--json', pattern, filename]
vim.command('redraw', async_=True)
vim.api.notify(' '.join(command), 2, {}, async_=True)
logger.debug(command)
rg_proc = await asyncio.create_subprocess_exec(
*command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
# rg_proc = subprocess.run(
# command,
# universal_newlines=True,
# encoding="utf8",
# check=False,
# capture_output=True,
# timeout=60,
# )
match_lnums = []
match_content = []
rg_stdout, rg_stderr = await rg_proc.communicate()
logger.debug('communicate %d', rg_proc.pid)
if rg_proc.returncode != 0:
return
for line in rg_stdout.splitlines(False):
value = json.loads(line)
if value['type'] == 'match':
data = value['data']
match_lnums.append(data['line_number'])
match_content.append(data['lines']['text'])
vim.async_call(lambda: vim.api.buf_set_var(bufnr, 'searchfold_matches', match_lnums))
# vim.async_call(lambda: vim.api.buf_set_var(bufnr, 'searchfold_content', match_content))
vim.async_call(lambda: vim.lua.fold_fromsearch(winid))
return bufnr, match_lnums, match_content
LogDiffSigns = {
'LogDiffDelete': {
'text': ' ',
'linehl': 'DiffDelete',
},
'LogDiffAdd': {
'text': ' ',
'linehl': 'DiffAdd',
},
'LogDiffChange': {
'text': ' ',
'linehl': 'DiffChange',
},
}
SearchTasks = {}
def searchfold(*args):
logger.debug(args)
SearchTasks[args[0]] = asyncio.create_task(async_searchfold(*args))
logdiff_ignore_pattern_string = '|'.join(
[
r'^.*?\]',
r'(cpu|real|mem) ?[:=] ?[m0-9:\.]+',
r'(cpu|real|epoch) time ?[:=] ?[0-9:\.]+',
r'\d{1,2}:\d{2}:\d{2}\.\d{1,3}',
]
)
async def async_logdiff_mark(bufnr1, bufnr2, ignore_pattern=None):
if ignore_pattern is not None:
logdiff_ignore = re.compile(ignore_pattern)
else:
logdiff_ignore = re.compile(logdiff_ignore_pattern_string, re.IGNORECASE)
results = await asyncio.gather(*SearchTasks.values())
matches = {result[0]: {'lnums': result[1], 'content': result[2]} for result in results}
logger.debug(results)
lines1 = [logdiff_ignore.sub('###', e) for e in matches[bufnr1]['content']]
lines2 = [logdiff_ignore.sub('###', e) for e in matches[bufnr2]['content']]
lnums1 = matches[bufnr1]['lnums']
lnums2 = matches[bufnr2]['lnums']
deleted = []
inserted = []
changed1 = []
changed2 = []
for group in SequenceMatcher(None, lines1, lines2).get_grouped_opcodes(0):
for tag, i1, i2, j1, j2 in group:
if tag == 'replace':
changed1.extend(range(i1, i2))
changed2.extend(range(j1, j2))
elif tag == 'delete':
deleted.extend(range(i1, i2))
elif tag == 'insert':
inserted.extend(range(j1, j2))
for name, define in LogDiffSigns.items():
vim.funcs.sign_define(name, define, async_=True)
for nr in changed1:
vim.funcs.sign_place(
0, 'LogDiff', 'LogDiffChange', bufnr1, {'lnum': lnums1[nr]}, async_=True
)
for nr in changed2:
vim.funcs.sign_place(
0, 'LogDiff', 'LogDiffChange', bufnr2, {'lnum': lnums2[nr]}, async_=True
)
for nr in deleted:
vim.funcs.sign_place(
0, 'LogDiff', 'LogDiffDelete', bufnr1, {'lnum': lnums1[nr]}, async_=True
)
for nr in inserted:
vim.funcs.sign_place( #
0, 'LogDiff', 'LogDiffAdd', bufnr2, {'lnum': lnums2[nr]}, async_=True
)
vim.command('redraw', async_=True)
vim.api.notify(logdiff_ignore_pattern_string, 2, {}, async_=True)
def logdiff_mark(bufnr1, bufnr2, ignore_pattern=None):
logger.debug('create diff task')
asyncio.create_task(async_logdiff_mark(bufnr1, bufnr2, ignore_pattern))
function! LogFold() abort
if empty(s:logdiff_mark)
runtime autoload/yarp.vim
let s:logdiff_mark = yarp#py3('logdiff_mark')
endif
call s:logdiff_mark.notify('searchfold',
\ bufnr(), win_getid(), bufname(), g:searchfold_pattern)
setlocal nowrap
endfunction
function! LogFoldAndDiff(...) abort
call call('SetLogPattern', a:000)
windo setlocal nowrap | checktime %
noautocmd windo call LogFold()
if len(tabpagebuflist()) < 2
sign unplace * group=LogDiff
return
endif
if empty(s:logdiff_mark)
runtime autoload/yarp.vim
let s:logdiff_mark = yarp#py3('logdiff_mark')
endif
let [bufnr1, bufnr2 ; _] = tabpagebuflist()
call s:logdiff_mark.notify('logdiff_mark', bufnr1, bufnr2)
endfunction
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment