from kivy.uix.label import Label from kivy.core.window import Window from kivy.graphics import Color,Rectangle import glob import os.path class Command_prompt(Label): def __init__(self,app,**kwargs): # app is used to share information between widgets self.app=app # insert mode self.insert=False # width of a letter self.char_width=9 # cursor position self.cursor=0 # the text, with no color information self.raw_text="" # a one-time message that shows up over the bar self.message="" # array of command with arguments self.argv=[] # display characters from window_offset onwards (for text wrapping) self.window_offset=0 # init Label super(Command_prompt,self).__init__(**kwargs) self.keyboard = Window.request_keyboard(None,self,"text") self.keyboard.bind(on_textinput=self.on_textinput,on_key_down=self.on_key_down) self.draw() def draw(self): # background with self.canvas.before: Color(0,0,0) Rectangle(pos=self.pos,size=self.size) # if message is not empty, draw message instead if self.message!="": # do not wrap self.text=self.message[:min(len(self.message),int(self.width/self.char_width))] self.message="" return # wrap text window_size=int(self.width/self.char_width) if self.cursor>=self.window_offset+window_size: self.window_offset=self.cursor-window_size+1 elif self.cursor0 and self.cursor==len(self.raw_text)) or (n<0 and self.cursor==0): return self.cursor=max(1,min(len(self.raw_text),self.cursor+n)) self.draw() # move cursor to absolute position def move_cursor(self,n): self.cursor=max(1,min(len(self.raw_text),n)) self.draw() # parse text as argv def parse_argv(self): # init self.argv=[""] # return position of cursor cursor=(0,0) # whether inside quotes single_quoted=False double_quoted=False # whether after '\' backslashed=False # start after ':' # proceed one character at a time for i in range(1,len(self.raw_text)): char=self.raw_text[i] # split argument if single_quoted==False and double_quoted==False and backslashed==False and char==' ': # new argv self.argv.append("") # quotes or '\' elif double_quoted==False and backslashed==False and char=='\'': single_quoted=not single_quoted elif single_quoted==False and backslashed==False and char=='"': double_quoted=not double_quoted elif single_quoted==False and backslashed==False and char=='\\': backslashed=True # write character else: self.argv[len(self.argv)-1]+=char # reset backslash backslashed=False # record position of cursor if self.cursor==i+1: cursor=(len(self.argv)-1,len(self.argv[len(self.argv)-1])) return cursor # tab completion def tab_complete(self): # parse argv cursor=self.parse_argv() # write and edit commands if self.argv[0]=="w" or self.argv[0]=="e": # check that cursor is in first argument if cursor[0]==1: # complete filesystem path self.append_text_at_cursor(self.complete_path(self.argv[cursor[0]],cursor[1])) # complete filesystem path def complete_path(self,base,end): paths=glob.glob(glob.escape(base[:end])+"*") print(glob.escape(base[:end])+"*", paths) if len(paths)==0: return "" elif len(paths)==1: # append '/' to directories if os.path.isdir(paths[0]): return paths[0][end:]+"/" else: return paths[0][end:] else: # display in status bar self.app.status_bar.message="" for path in paths: name=os.path.basename(path) # add quotes if needed if " " in name: name="\""+name+"\"" self.app.status_bar.message+=name+" " self.app.status_bar.draw() return os.path.commonprefix(paths)[end:] # run command def run_command(self): # parse command line self.parse_argv() # write if self.argv[0]=="w" or self.argv[0]=="w!" or self.argv[0]=="wq": if len(self.argv)>2: self.message="error: could not write to file: too many arguments -- usage: ':w [path to file]'" return elif len(self.argv)==1: if os.path.isfile(self.app.openfile): self.app.painter.write(self.app.openfile) else: self.message="error: no file is open for editing, specify a path" return else: # check that the file is not a directory if os.path.isdir(self.argv[1]): self.message="error: '"+self.argv[1]+"' is a directory" return # check that file does not already exist elif self.argv[0]!="w!" and self.argv[1]!= self.app.openfile and os.path.isfile(self.argv[1]): self.message="error: '"+self.argv[1]+"' already exists (use ':w!' to overwrite)" return # check that the containing directory exists elif len(os.path.dirname(self.argv[1]))>0 and not os.path.isdir(os.path.dirname(self.argv[1])): self.message="error: could not find directory '"+os.path.dirname(self.argv[1])+"'" return # check permissions elif (len(os.path.dirname(self.argv[1]))>0 and not os.access(os.path.dirname(self.argv[1]),os.W_OK)) or (len(os.path.dirname(self.argv[1]))==0 and not os.access(os.getcwd(),os.W_OK)): self.message="error: permission denied" return else: self.app.painter.write(self.argv[1]) # open file if self.argv[0]=="e": if len(self.argv)>2: self.message="error: could not open file: too many arguments -- usage: ':e '" return elif len(self.argv)==1: self.message="error: could not open file: no argument found -- usage: ':e '" return else: # check that the file is not a directory if os.path.isdir(self.argv[1]): self.message="error: '"+self.argv[1]+"' is a directory" return # check that the file exists elif not os.path.isfile(self.argv[1]): self.message="error: could not find file '"+self.argv[1]+"'" return # check permissions elif not os.access(self.argv[1],os.R_OK): self.message="error: permission denied" return else: # select openfile self.app.openfile=self.argv[1] # read the file self.app.painter.read(self.argv[1]) # quit if self.argv[0]=="q" or self.argv=="wq": self.app.stop()