1# -*- coding: utf-8 -*- 2# 3# completion.py - commander 4# 5# Copyright (C) 2010 - Jesse van den Kieboom 6# 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program; if not, write to the Free Software 19# Foundation, Inc., 51 Franklin Street, Fifth Floor, 20# Boston, MA 02110-1301, USA. 21 22import commander.commands as commands 23import bisect 24import sys 25import os 26import re 27 28from xml.sax import saxutils 29 30__all__ = ['command', 'filename'] 31 32def _common_prefix_part(first, second): 33 length = min(len(first), len(second)) 34 35 for i in range(0, length): 36 if first[i] != second[i]: 37 return first[:i] 38 39 return first[:length] 40 41def common_prefix(args, sep=None): 42 # A common prefix can be something like 43 # first: some-thing 44 # second: sho-tar 45 # res: s-t 46 args = list(args) 47 48 if not args: 49 return '' 50 51 if len(args) == 1: 52 return str(args[0]) 53 54 first = str(args[0]) 55 second = str(args[1]) 56 57 if not sep: 58 ret = _common_prefix_part(first, second) 59 else: 60 first = first.split(sep) 61 second = second.split(sep) 62 ret = [] 63 64 for i in range(0, min(len(first), len(second))): 65 ret.append(_common_prefix_part(first[i], second[i])) 66 67 ret = sep.join(ret) 68 69 del args[0] 70 args[0] = ret 71 72 return common_prefix(args, sep) 73 74def _expand_commands(cmds): 75 if not cmds: 76 cmds.extend(commands.Commands().modules()) 77 return 78 79 old = list(cmds) 80 del cmds[:] 81 82 # Expand 'commands' to all the respective subcommands 83 84 for cmd in old: 85 for c in cmd.commands(): 86 bisect.insort(cmds, c) 87 88def _filter_command(cmd, subs): 89 parts = cmd.name.split('-') 90 91 if len(subs) > len(parts): 92 return False 93 94 for i in range(0, len(subs)): 95 if not parts[i].startswith(subs[i]): 96 return False 97 98 return True 99 100def _filter_commands(cmds, subs): 101 # See what parts of cmds still match the parts in subs 102 idx = bisect.bisect_left(cmds, subs[0]) 103 ret = [] 104 105 while idx < len(cmds): 106 if not cmds[idx].name.startswith(subs[0]): 107 break 108 109 if _filter_command(cmds[idx], subs): 110 ret.append(cmds[idx]) 111 112 idx += 1 113 114 return ret 115 116def single_command(words, idx): 117 ret = command(words, idx) 118 119 if not ret: 120 return None 121 122 ret[0] = list(filter(lambda x: x.method, ret[0])) 123 124 if not ret[0]: 125 return None 126 127 return ret[0][0] 128 129def command(words, idx): 130 s = words[idx].strip() 131 132 parts = s.split('.') 133 cmds = [] 134 135 for i in parts: 136 # Expand all the parents to their child commands 137 _expand_commands(cmds) 138 139 if not cmds: 140 return None 141 142 subs = i.split('-') 143 cmds = _filter_commands(cmds, subs) 144 145 if not cmds: 146 return None 147 148 if not cmds: 149 return None 150 151 if len(parts) == 1: 152 completed = common_prefix(cmds) 153 else: 154 completed = '.'.join(parts[0:-1]) + '.' + common_prefix(cmds, '-') 155 156 return [cmds, completed] 157 158def _file_color(path): 159 if os.path.isdir(path): 160 format = '<span color="#799ec6">%s</span>' 161 else: 162 format = '%s' 163 164 return format % (saxutils.escape(os.path.basename(path)),) 165 166def _sort_nicely(l): 167 convert = lambda text: int(text) if text.isdigit() else text 168 alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] 169 170 l.sort(key=alphanum_key) 171 172def filename(words, idx, view): 173 prefix = os.path.dirname(words[idx]) 174 partial = os.path.expanduser(words[idx]) 175 176 doc = view.get_buffer() 177 178 if not doc.is_untitled(): 179 root = os.path.dirname(doc.get_file().get_location().get_path()) 180 else: 181 root = os.path.expanduser('~/') 182 183 if not os.path.isabs(partial): 184 partial = os.path.join(root, partial) 185 186 dirname = os.path.dirname(partial) 187 188 try: 189 files = os.listdir(dirname) 190 except OSError: 191 return None 192 193 base = os.path.basename(partial) 194 ret = [] 195 real = [] 196 197 for f in files: 198 if f.startswith(base) and (base or not f.startswith('.')): 199 real.append(os.path.join(dirname, f)) 200 ret.append(os.path.join(prefix, f)) 201 202 _sort_nicely(real) 203 204 if len(ret) == 1: 205 if os.path.isdir(real[0]): 206 after = '/' 207 else: 208 after = ' ' 209 210 return ret, ret[0], after 211 else: 212 return list(map(lambda x: _file_color(x), real), common_prefix(ret)) 213 214def words(ret): 215 def decorator(words, idx): 216 rr = list(filter(lambda x: x.startswith(words[idx]), ret)) 217 return rr, common_prefix(rr) 218 219 return decorator 220 221# ex:ts=4:et 222