2020import re
2121import sys
2222
23+ try :
24+ import Levenshtein
25+ levenshtein_supported = True
26+ except ImportError :
27+ levenshtein_supported = False
28+
2329class NotesIndex :
2430
2531 def __init__ (self ):
@@ -33,7 +39,7 @@ def __init__(self):
3339 self .list_keywords (self .keyword_filter )
3440 else :
3541 matches = self .search_index (keywords )
36- print '\n ' .join (sorted (matches ))
42+ print self . encode ( '\n ' .join (sorted (matches ) ))
3743
3844 def parse_args (self ):
3945 ''' Parse the command line arguments. '''
@@ -64,6 +70,8 @@ def parse_args(self):
6470 sys .exit (0 )
6571 else :
6672 assert False , "Unhandled option"
73+ if self .keyword_filter is not None :
74+ self .keyword_filter = self .decode (self .keyword_filter )
6775 # Canonicalize pathnames, check validity.
6876 self .database_file = self .munge_path (self .database_file )
6977 self .user_directory = self .munge_path (self .user_directory )
@@ -147,27 +155,37 @@ def search_index(self, keywords):
147155 matches &= set (filenames )
148156 return list (matches ) if matches else []
149157
150- def list_keywords (self , substring , limit = 100 ):
158+ def list_keywords (self , substring , limit = 25 ):
151159 ''' Print all (matching) keywords to standard output. '''
152- i = 0
153- for kw in self .index ['keywords' ]:
160+ decorated = []
161+ for kw , filenames in self .index ['keywords' ]. iteritems () :
154162 if substring in kw .lower ():
155- print kw
156- if i < limit :
157- i += 1
163+ if levenshtein_supported :
164+ decorated .append ((Levenshtein .distance (kw .lower (), substring ), - len (filenames ), kw ))
158165 else :
159- break
166+ decorated .append ((- len (filenames ), kw ))
167+ decorated .sort ()
168+ selection = [d [- 1 ] for d in decorated [:limit ]]
169+ print self .encode ('\n ' .join (selection ))
160170
161171 def tokenize (self , text ):
162172 ''' Tokenize a string into a list of normalized, unique keywords. '''
163173 words = set ()
164- text = text .decode (self . character_encoding , 'ignore' )
165- for word in re .findall (r'\w+' , text . lower () , re .UNICODE ):
174+ text = self .decode (text ). lower ( )
175+ for word in re .findall (r'\w+' , text , re .UNICODE ):
166176 word = word .strip ()
167177 if word != '' and not word .isspace ():
168178 words .add (word )
169179 return words
170180
181+ def encode (self , text ):
182+ ''' Encode a string in the user's preferred character encoding. '''
183+ return text .encode (self .character_encoding , 'ignore' )
184+
185+ def decode (self , text ):
186+ ''' Decode a string in the user's preferred character encoding. '''
187+ return text .decode (self .character_encoding , 'ignore' )
188+
171189 def munge_path (self , path ):
172190 ''' Canonicalize user-defined path, making it absolute. '''
173191 return os .path .abspath (os .path .expanduser (path ))
0 commit comments