2022-08-26 07:15:42 +00:00
|
|
|
"""
|
2022-10-08 15:37:23 +00:00
|
|
|
Readline helper functions for invoke.py.
|
2022-09-27 18:27:55 +00:00
|
|
|
You may import the global singleton `completer` to get access to the
|
|
|
|
completer object itself. This is useful when you want to autocomplete
|
|
|
|
seeds:
|
|
|
|
|
2022-10-08 15:37:23 +00:00
|
|
|
from ldm.invoke.readline import completer
|
2022-09-27 18:27:55 +00:00
|
|
|
completer.add_seed(18247566)
|
|
|
|
completer.add_seed(9281839)
|
2022-08-26 07:15:42 +00:00
|
|
|
"""
|
2022-08-26 02:49:15 +00:00
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import atexit
|
2022-10-08 15:37:23 +00:00
|
|
|
from ldm.invoke.args import Args
|
2022-09-27 18:27:55 +00:00
|
|
|
|
2022-08-26 02:49:15 +00:00
|
|
|
# ---------------readline utilities---------------------
|
|
|
|
try:
|
|
|
|
import readline
|
|
|
|
readline_available = True
|
2022-10-04 18:57:48 +00:00
|
|
|
except (ImportError,ModuleNotFoundError):
|
2022-10-05 02:52:24 +00:00
|
|
|
readline_available = False
|
2022-08-26 02:49:15 +00:00
|
|
|
|
2022-10-08 15:37:23 +00:00
|
|
|
IMG_EXTENSIONS = ('.png','.jpg','.jpeg','.PNG','.JPG','.JPEG','.gif','.GIF')
|
2022-09-27 18:27:55 +00:00
|
|
|
COMMANDS = (
|
|
|
|
'--steps','-s',
|
|
|
|
'--seed','-S',
|
|
|
|
'--iterations','-n',
|
|
|
|
'--width','-W','--height','-H',
|
|
|
|
'--cfg_scale','-C',
|
2022-09-28 23:47:36 +00:00
|
|
|
'--threshold',
|
|
|
|
'--perlin',
|
2022-09-27 18:27:55 +00:00
|
|
|
'--grid','-g',
|
|
|
|
'--individual','-i',
|
|
|
|
'--init_img','-I',
|
|
|
|
'--init_mask','-M',
|
|
|
|
'--init_color',
|
|
|
|
'--strength','-f',
|
|
|
|
'--variants','-v',
|
|
|
|
'--outdir','-o',
|
|
|
|
'--sampler','-A','-m',
|
|
|
|
'--embedding_path',
|
|
|
|
'--device',
|
|
|
|
'--grid','-g',
|
|
|
|
'--gfpgan_strength','-G',
|
|
|
|
'--upscale','-U',
|
|
|
|
'-save_orig','--save_original',
|
|
|
|
'--skip_normalize','-x',
|
|
|
|
'--log_tokenization','-t',
|
2022-10-01 19:21:57 +00:00
|
|
|
'--hires_fix',
|
2022-10-04 18:57:48 +00:00
|
|
|
'!fix','!fetch','!history','!search','!clear',
|
2022-09-27 18:27:55 +00:00
|
|
|
)
|
|
|
|
IMG_PATH_COMMANDS = (
|
2022-09-28 20:47:48 +00:00
|
|
|
'--outdir[=\s]',
|
2022-09-27 18:27:55 +00:00
|
|
|
)
|
|
|
|
IMG_FILE_COMMANDS=(
|
|
|
|
'!fix',
|
|
|
|
'!fetch',
|
2022-09-28 20:47:48 +00:00
|
|
|
'--init_img[=\s]','-I',
|
|
|
|
'--init_mask[=\s]','-M',
|
|
|
|
'--init_color[=\s]',
|
|
|
|
'--embedding_path[=\s]',
|
2022-09-27 18:27:55 +00:00
|
|
|
)
|
|
|
|
path_regexp = '('+'|'.join(IMG_PATH_COMMANDS+IMG_FILE_COMMANDS) + ')\s*\S*$'
|
2022-08-26 07:15:42 +00:00
|
|
|
|
2022-10-04 18:57:48 +00:00
|
|
|
class Completer(object):
|
2022-08-26 07:15:42 +00:00
|
|
|
def __init__(self, options):
|
2022-09-27 18:27:55 +00:00
|
|
|
self.options = sorted(options)
|
|
|
|
self.seeds = set()
|
|
|
|
self.matches = list()
|
|
|
|
self.default_dir = None
|
|
|
|
self.linebuffer = None
|
2022-09-30 18:53:37 +00:00
|
|
|
self.auto_history_active = True
|
2022-08-26 02:49:15 +00:00
|
|
|
return
|
|
|
|
|
2022-08-26 07:15:42 +00:00
|
|
|
def complete(self, text, state):
|
2022-09-27 18:27:55 +00:00
|
|
|
'''
|
2022-10-08 15:37:23 +00:00
|
|
|
Completes invoke command line.
|
2022-09-27 18:27:55 +00:00
|
|
|
BUG: it doesn't correctly complete files that have spaces in the name.
|
|
|
|
'''
|
2022-08-26 02:49:15 +00:00
|
|
|
buffer = readline.get_line_buffer()
|
|
|
|
|
2022-09-27 18:27:55 +00:00
|
|
|
if state == 0:
|
|
|
|
if re.search(path_regexp,buffer):
|
|
|
|
do_shortcut = re.search('^'+'|'.join(IMG_FILE_COMMANDS),buffer)
|
|
|
|
self.matches = self._path_completions(text, state, IMG_EXTENSIONS,shortcut_ok=do_shortcut)
|
2022-08-26 02:49:15 +00:00
|
|
|
|
2022-09-27 18:27:55 +00:00
|
|
|
# looking for a seed
|
|
|
|
elif re.search('(-S\s*|--seed[=\s])\d*$',buffer):
|
|
|
|
self.matches= self._seed_completions(text,state)
|
2022-08-26 02:49:15 +00:00
|
|
|
|
|
|
|
# This is the first time for this text, so build a match list.
|
2022-09-27 18:27:55 +00:00
|
|
|
elif text:
|
2022-08-26 07:15:42 +00:00
|
|
|
self.matches = [
|
|
|
|
s for s in self.options if s and s.startswith(text)
|
|
|
|
]
|
2022-08-26 02:49:15 +00:00
|
|
|
else:
|
|
|
|
self.matches = self.options[:]
|
|
|
|
|
|
|
|
# Return the state'th item from the match list,
|
|
|
|
# if we have that many.
|
|
|
|
try:
|
|
|
|
response = self.matches[state]
|
|
|
|
except IndexError:
|
|
|
|
response = None
|
|
|
|
return response
|
|
|
|
|
2022-09-28 15:48:11 +00:00
|
|
|
def add_history(self,line):
|
2022-09-27 18:27:55 +00:00
|
|
|
'''
|
2022-09-28 15:48:11 +00:00
|
|
|
Pass thru to readline
|
2022-09-27 18:27:55 +00:00
|
|
|
'''
|
2022-09-30 18:53:37 +00:00
|
|
|
if not self.auto_history_active:
|
|
|
|
readline.add_history(line)
|
2022-09-28 15:48:11 +00:00
|
|
|
|
2022-10-04 18:57:48 +00:00
|
|
|
def clear_history(self):
|
|
|
|
'''
|
|
|
|
Pass clear_history() thru to readline
|
|
|
|
'''
|
|
|
|
readline.clear_history()
|
|
|
|
|
|
|
|
def search_history(self,match:str):
|
|
|
|
'''
|
|
|
|
Like show_history() but only shows items that
|
|
|
|
contain the match string.
|
|
|
|
'''
|
|
|
|
self.show_history(match)
|
|
|
|
|
2022-09-28 15:48:11 +00:00
|
|
|
def remove_history_item(self,pos):
|
|
|
|
readline.remove_history_item(pos)
|
2022-09-27 18:27:55 +00:00
|
|
|
|
|
|
|
def add_seed(self, seed):
|
|
|
|
'''
|
|
|
|
Add a seed to the autocomplete list for display when -S is autocompleted.
|
|
|
|
'''
|
|
|
|
if seed is not None:
|
|
|
|
self.seeds.add(str(seed))
|
|
|
|
|
|
|
|
def set_default_dir(self, path):
|
|
|
|
self.default_dir=path
|
|
|
|
|
|
|
|
def get_line(self,index):
|
|
|
|
try:
|
|
|
|
line = self.get_history_item(index)
|
|
|
|
except IndexError:
|
|
|
|
return None
|
|
|
|
return line
|
|
|
|
|
|
|
|
def get_current_history_length(self):
|
|
|
|
return readline.get_current_history_length()
|
|
|
|
|
|
|
|
def get_history_item(self,index):
|
|
|
|
return readline.get_history_item(index)
|
|
|
|
|
2022-10-04 18:57:48 +00:00
|
|
|
def show_history(self,match=None):
|
2022-09-27 18:27:55 +00:00
|
|
|
'''
|
|
|
|
Print the session history using the pydoc pager
|
|
|
|
'''
|
|
|
|
import pydoc
|
|
|
|
lines = list()
|
|
|
|
h_len = self.get_current_history_length()
|
|
|
|
if h_len < 1:
|
|
|
|
print('<empty history>')
|
|
|
|
return
|
|
|
|
|
|
|
|
for i in range(0,h_len):
|
2022-10-04 18:57:48 +00:00
|
|
|
line = self.get_history_item(i+1)
|
|
|
|
if match and match not in line:
|
|
|
|
continue
|
|
|
|
lines.append(f'[{i+1}] {line}')
|
2022-09-27 18:27:55 +00:00
|
|
|
pydoc.pager('\n'.join(lines))
|
|
|
|
|
|
|
|
def set_line(self,line)->None:
|
|
|
|
self.linebuffer = line
|
|
|
|
readline.redisplay()
|
|
|
|
|
|
|
|
def _seed_completions(self, text, state):
|
|
|
|
m = re.search('(-S\s?|--seed[=\s]?)(\d*)',text)
|
|
|
|
if m:
|
|
|
|
switch = m.groups()[0]
|
|
|
|
partial = m.groups()[1]
|
2022-08-26 02:49:15 +00:00
|
|
|
else:
|
2022-09-27 18:27:55 +00:00
|
|
|
switch = ''
|
|
|
|
partial = text
|
2022-08-26 02:49:15 +00:00
|
|
|
|
2022-08-26 07:15:42 +00:00
|
|
|
matches = list()
|
2022-09-27 18:27:55 +00:00
|
|
|
for s in self.seeds:
|
|
|
|
if s.startswith(partial):
|
|
|
|
matches.append(switch+s)
|
|
|
|
matches.sort()
|
|
|
|
return matches
|
2022-08-26 02:49:15 +00:00
|
|
|
|
2022-09-27 18:27:55 +00:00
|
|
|
def _pre_input_hook(self):
|
|
|
|
if self.linebuffer:
|
|
|
|
readline.insert_text(self.linebuffer)
|
|
|
|
readline.redisplay()
|
|
|
|
self.linebuffer = None
|
|
|
|
|
2022-09-28 20:47:48 +00:00
|
|
|
def _path_completions(self, text, state, extensions, shortcut_ok=True):
|
2022-09-27 18:27:55 +00:00
|
|
|
# separate the switch from the partial path
|
|
|
|
match = re.search('^(-\w|--\w+=?)(.*)',text)
|
|
|
|
if match is None:
|
|
|
|
switch = None
|
|
|
|
partial_path = text
|
2022-08-26 02:49:15 +00:00
|
|
|
else:
|
2022-09-27 18:27:55 +00:00
|
|
|
switch,partial_path = match.groups()
|
|
|
|
partial_path = partial_path.lstrip()
|
|
|
|
|
|
|
|
matches = list()
|
|
|
|
path = os.path.expanduser(partial_path)
|
|
|
|
|
|
|
|
if os.path.isdir(path):
|
|
|
|
dir = path
|
|
|
|
elif os.path.dirname(path) != '':
|
2022-08-26 07:15:42 +00:00
|
|
|
dir = os.path.dirname(path)
|
2022-09-27 18:27:55 +00:00
|
|
|
else:
|
|
|
|
dir = ''
|
|
|
|
path= os.path.join(dir,path)
|
2022-08-26 02:49:15 +00:00
|
|
|
|
2022-09-27 18:27:55 +00:00
|
|
|
dir_list = os.listdir(dir or '.')
|
|
|
|
if shortcut_ok and os.path.exists(self.default_dir) and dir=='':
|
|
|
|
dir_list += os.listdir(self.default_dir)
|
2022-08-26 02:49:15 +00:00
|
|
|
|
2022-09-27 18:27:55 +00:00
|
|
|
for node in dir_list:
|
|
|
|
if node.startswith('.') and len(node) > 1:
|
|
|
|
continue
|
|
|
|
full_path = os.path.join(dir, node)
|
|
|
|
|
|
|
|
if not (node.endswith(extensions) or os.path.isdir(full_path)):
|
|
|
|
continue
|
|
|
|
|
|
|
|
if not full_path.startswith(path):
|
|
|
|
continue
|
|
|
|
|
|
|
|
if switch is None:
|
|
|
|
match_path = os.path.join(dir,node)
|
|
|
|
matches.append(match_path+'/' if os.path.isdir(full_path) else match_path)
|
|
|
|
elif os.path.isdir(full_path):
|
|
|
|
matches.append(
|
|
|
|
switch+os.path.join(os.path.dirname(full_path), node) + '/'
|
|
|
|
)
|
|
|
|
elif node.endswith(extensions):
|
|
|
|
matches.append(
|
|
|
|
switch+os.path.join(os.path.dirname(full_path), node)
|
|
|
|
)
|
|
|
|
return matches
|
|
|
|
|
|
|
|
class DummyCompleter(Completer):
|
|
|
|
def __init__(self,options):
|
|
|
|
super().__init__(options)
|
|
|
|
self.history = list()
|
|
|
|
|
2022-09-28 15:48:11 +00:00
|
|
|
def add_history(self,line):
|
2022-09-27 18:27:55 +00:00
|
|
|
self.history.append(line)
|
|
|
|
|
2022-10-04 18:57:48 +00:00
|
|
|
def clear_history(self):
|
|
|
|
self.history = list()
|
|
|
|
|
2022-09-27 18:27:55 +00:00
|
|
|
def get_current_history_length(self):
|
|
|
|
return len(self.history)
|
|
|
|
|
|
|
|
def get_history_item(self,index):
|
|
|
|
return self.history[index-1]
|
|
|
|
|
2022-09-28 15:48:11 +00:00
|
|
|
def remove_history_item(self,index):
|
|
|
|
return self.history.pop(index-1)
|
|
|
|
|
2022-09-27 18:27:55 +00:00
|
|
|
def set_line(self,line):
|
|
|
|
print(f'# {line}')
|
2022-08-26 07:15:42 +00:00
|
|
|
|
2022-09-30 18:53:37 +00:00
|
|
|
def get_completer(opt:Args)->Completer:
|
|
|
|
if readline_available:
|
|
|
|
completer = Completer(COMMANDS)
|
|
|
|
|
|
|
|
readline.set_completer(
|
|
|
|
completer.complete
|
|
|
|
)
|
|
|
|
# pyreadline3 does not have a set_auto_history() method
|
|
|
|
try:
|
|
|
|
readline.set_auto_history(False)
|
|
|
|
completer.auto_history_active = False
|
|
|
|
except:
|
|
|
|
completer.auto_history_active = True
|
|
|
|
readline.set_pre_input_hook(completer._pre_input_hook)
|
|
|
|
readline.set_completer_delims(' ')
|
|
|
|
readline.parse_and_bind('tab: complete')
|
|
|
|
readline.parse_and_bind('set print-completions-horizontally off')
|
|
|
|
readline.parse_and_bind('set page-completions on')
|
|
|
|
readline.parse_and_bind('set skip-completed-text on')
|
|
|
|
readline.parse_and_bind('set show-all-if-ambiguous on')
|
|
|
|
|
2022-10-08 15:37:23 +00:00
|
|
|
histfile = os.path.join(os.path.expanduser(opt.outdir), '.invoke_history')
|
2022-09-30 18:53:37 +00:00
|
|
|
try:
|
|
|
|
readline.read_history_file(histfile)
|
|
|
|
readline.set_history_length(1000)
|
|
|
|
except FileNotFoundError:
|
|
|
|
pass
|
|
|
|
atexit.register(readline.write_history_file, histfile)
|
|
|
|
|
|
|
|
else:
|
|
|
|
completer = DummyCompleter(COMMANDS)
|
|
|
|
return completer
|