1. handle escape character;
  2. handle items filtering
    based on entered letters;
  3. redraw UI when the filter
    changes;
  4. TODO
    handle selection changes via arrows, hjkl;
  5. TODO
    handle selection after Enter has been pressed;
		
	
			
		
			
				
	
	
		
			265 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			265 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import functools
 | |
| import configparser
 | |
| import collections
 | |
| import asyncio
 | |
| import threading
 | |
| import re
 | |
| import inspect
 | |
| import pathlib
 | |
| import logging
 | |
| import fnmatch
 | |
| import vim
 | |
| 
 | |
| from typing import (
 | |
| 	Optional,
 | |
| 	ClassVar,
 | |
| 	Self,
 | |
| 	Any,
 | |
| 	Callable,
 | |
| )
 | |
| 
 | |
| logger = logging.getLogger(__name__)
 | |
| 
 | |
| from .utils import Vim
 | |
| 
 | |
| # logging.basicConfig(level=logging.WARNING)
 | |
| 
 | |
| 
 | |
| MODULE_NAME = 'online_fxreader_pr34_vim'
 | |
| 
 | |
| 
 | |
| def f1():
 | |
| 	t1 = vim.current.window
 | |
| 	t2 = t1.width
 | |
| 	vim.command('vnew')
 | |
| 	t3 = t2 // 3
 | |
| 	vim.command('vertical resize %d' % t3)
 | |
| 	vim.current.window = t1
 | |
| 
 | |
| 
 | |
| def f2():
 | |
| 	context = {k: vim.options['splitright'] for k in ['splitright']}
 | |
| 	try:
 | |
| 		current_window = vim.current.window
 | |
| 		vim.options['splitright'] = True
 | |
| 		vim.command('vnew')
 | |
| 		vim.command('r! tmux show-buffer')
 | |
| 		vim.current.window = current_window
 | |
| 	finally:
 | |
| 		for k, v in context.items():
 | |
| 			vim.options[k] = v
 | |
| 
 | |
| 
 | |
| def f5_1(pattern, flags, info):
 | |
| 	import subprocess
 | |
| 	import io
 | |
| 	import re
 | |
| 	import tempfile
 | |
| 	import traceback
 | |
| 	import logging
 | |
| 
 | |
| 	# print([pattern, flags, info])
 | |
| 	completed_process = None
 | |
| 
 | |
| 	options = dict(
 | |
| 		recursive=False,
 | |
| 		ext=[],
 | |
| 	)
 | |
| 
 | |
| 	# print('fuck')
 | |
| 	if b'r' in flags:
 | |
| 		while True:
 | |
| 			ext_m = re.compile(r'^.([^\,]+),(.*)$').match(pattern)
 | |
| 
 | |
| 			if pattern[:3] in [r'\r,']:
 | |
| 				options['recursive'] = True
 | |
| 				pattern = pattern[3:]
 | |
| 			elif not ext_m is None:
 | |
| 				options['ext'].append(ext_m[1])
 | |
| 				pattern = ext_m[2]
 | |
| 			else:
 | |
| 				break
 | |
| 
 | |
| 	print(
 | |
| 		[
 | |
| 			flags,
 | |
| 			pattern,
 | |
| 			options,
 | |
| 		]
 | |
| 	)
 | |
| 
 | |
| 	try:
 | |
| 		git_cmd = [
 | |
| 			'git',
 | |
| 			'grep',
 | |
| 			'-n',
 | |
| 		]
 | |
| 
 | |
| 		if options['recursive']:
 | |
| 			git_cmd.append('--recurse-submodules')
 | |
| 
 | |
| 		git_cmd.extend(['-P', pattern])
 | |
| 
 | |
| 		if len(options['ext']) > 0:
 | |
| 			git_cmd.extend(['--', *['**/*%s' % o for o in options['ext']]])
 | |
| 
 | |
| 		completed_process = subprocess.run(
 | |
| 			git_cmd,
 | |
| 			capture_output=True,
 | |
| 		)
 | |
| 		assert completed_process.returncode == 0 or (
 | |
| 			completed_process.stdout == b''
 | |
| 			# completed_process.stdout == b'' and
 | |
| 			# completed_process.stderr == b''
 | |
| 		)
 | |
| 		t1 = completed_process.stdout
 | |
| 	except:
 | |
| 		logging.error(
 | |
| 			''.join(
 | |
| 				[
 | |
| 					traceback.format_exc(),
 | |
| 					getattr(completed_process, 'stdout', b'').decode('utf-8'),
 | |
| 					getattr(completed_process, 'stderr', b'').decode('utf-8'),
 | |
| 				]
 | |
| 			)
 | |
| 		)
 | |
| 		t1 = b''
 | |
| 
 | |
| 	def watch(data):
 | |
| 		with tempfile.NamedTemporaryFile(suffix='.txt') as f:
 | |
| 			with io.open(f.name, 'wb') as f2:
 | |
| 				f2.write(data)
 | |
| 			vim.command('!less %s' % f.name)
 | |
| 
 | |
| 	# watch(t1)
 | |
| 
 | |
| 	t2 = []
 | |
| 	for o in t1.splitlines():
 | |
| 		try:
 | |
| 			# watch(o.encode('utf-8'))
 | |
| 			t3 = o.decode('utf-8')
 | |
| 			t4 = re.compile(r'^([^\:\=]+)[\:\=](\d+)[\:\=](.*)$').match(t3)
 | |
| 			if not t4 is None:
 | |
| 				t2.append(
 | |
| 					dict(
 | |
| 						name=t4[3].strip(),
 | |
| 						filename=t4[1],
 | |
| 						cmd=t4[2],
 | |
| 					)
 | |
| 				)
 | |
| 		except:
 | |
| 			pass
 | |
| 	# print(t2)
 | |
| 
 | |
| 	# return [{'name': 'blah', 'filename': 'docker-compose.yml', 'cmd': '23'}]
 | |
| 	return t2
 | |
| 
 | |
| 
 | |
| class EditorConfigModeline:
 | |
| 	_instance: ClassVar[Optional['EditorConfigModeline']] = None
 | |
| 
 | |
| 	def __init__(self) -> None:
 | |
| 		self.configs: dict[
 | |
| 			pathlib.Path,
 | |
| 			dict[str, str],
 | |
| 		] = dict()
 | |
| 
 | |
| 		Vim.run_command(r"""
 | |
| augroup EditorConfigModeline
 | |
|   autocmd!
 | |
|   autocmd BufEnter * python3 import online_fxreader_pr34_vim.main; online_fxreader_pr34_vim.main.EditorConfigModeline.singleton().on_buffer()
 | |
| augroup END
 | |
| 		""")
 | |
| 
 | |
| 	@classmethod
 | |
| 	def singleton(cls) -> Self:
 | |
| 		if cls._instance is None:
 | |
| 			cls._instance = cls()
 | |
| 
 | |
| 		return cls._instance
 | |
| 
 | |
| 	def load_config(self) -> Optional[dict[str, str]]:
 | |
| 		cwd = pathlib.Path.cwd()
 | |
| 
 | |
| 		if not cwd in self.configs:
 | |
| 			config_path = cwd / '.editorconfig'
 | |
| 
 | |
| 			if not config_path.exists():
 | |
| 				return None
 | |
| 
 | |
| 			parser = configparser.ConfigParser()
 | |
| 			parser.optionxform = str  # keep case
 | |
| 			try:
 | |
| 				parser.read(str(config_path))
 | |
| 			except:
 | |
| 				logger.exception('')
 | |
| 				return None
 | |
| 
 | |
| 			config: dict[str, str] = dict()
 | |
| 
 | |
| 			for section in parser.sections():
 | |
| 				logger.info(dict(section=section))
 | |
| 
 | |
| 				if len(section) > 0:
 | |
| 					# pattern = section[1:-1]
 | |
| 					pattern = section
 | |
| 					if not parser[section].get('vim_modeline') is None:
 | |
| 						config[pattern] = parser[section].get('vim_modeline')
 | |
| 						self.validate_modeline(config[pattern])
 | |
| 
 | |
| 			self.configs[cwd] = config
 | |
| 
 | |
| 		return self.configs[cwd]
 | |
| 
 | |
| 	@classmethod
 | |
| 	def validate_modeline(cls, modeline: str) -> None:
 | |
| 		pattern = re.compile(r'^set(\s+(noet|sts|ts|et|ai|ci|noai|noci|sw)(\=\w)?)+$')
 | |
| 		assert pattern.match(modeline), 'invalid modeline %s' % modeline
 | |
| 
 | |
| 	@classmethod
 | |
| 	def find_entry(
 | |
| 		cls,
 | |
| 		file_path: pathlib.Path,
 | |
| 		config: Optional[dict[str, str]] = None,
 | |
| 	) -> Optional[str]:
 | |
| 		if config is None:
 | |
| 			return None
 | |
| 
 | |
| 		project_root = pathlib.Path.cwd()
 | |
| 
 | |
| 		if file_path.is_relative_to(project_root):
 | |
| 			rel_path = file_path.relative_to(pathlib.Path.cwd())
 | |
| 		else:
 | |
| 			rel_path = file_path
 | |
| 
 | |
| 		for pattern, modeline in config.items():
 | |
| 			if fnmatch.fnmatch(str(rel_path), pattern):
 | |
| 				return modeline
 | |
| 
 | |
| 		return None
 | |
| 
 | |
| 	def on_buffer(self) -> None:
 | |
| 		config = self.load_config()
 | |
| 
 | |
| 		logger.info(dict(config=config))
 | |
| 
 | |
| 		buf_name = vim.current.buffer.name
 | |
| 		file_path = pathlib.Path(buf_name).resolve()
 | |
| 
 | |
| 		entry = self.find_entry(file_path, config=config)
 | |
| 
 | |
| 		logger.info(dict(modeline=entry))
 | |
| 
 | |
| 		vim.command('silent! {}'.format(entry))
 | |
| 
 | |
| 		# vim.command("echo '{}'".format('applied %s' % entry))
 | |
| 
 | |
| 		# raise NotImplementedError
 | |
| 
 | |
| 
 | |
| # EditorConfigModeline.singleton()
 | |
| 
 | |
| 
 | |
| def init():
 | |
| 	EditorConfigModeline.singleton()
 |