Added new features for copying and pasting
This commit is contained in:
parent
2c28ed8eab
commit
38b9f23d36
|
@ -0,0 +1,11 @@
|
|||
This is
|
||||
a test
|
||||
to see
|
||||
how well
|
||||
things will
|
||||
This is
|
||||
a test
|
||||
work when
|
||||
I start
|
||||
using the
|
||||
new features
|
311
chalk
311
chalk
|
@ -22,6 +22,7 @@ import readline
|
|||
###########################################################
|
||||
|
||||
content = []
|
||||
paste_buffer = []
|
||||
file_changed = False
|
||||
filepath = ''
|
||||
filename = ''
|
||||
|
@ -45,13 +46,14 @@ class c:
|
|||
b_blue = '\033[1;34m'
|
||||
white = '\033[1;37m'
|
||||
end = '\033[0m'
|
||||
bold = '\033[1m'
|
||||
|
||||
|
||||
###########################################################
|
||||
# Functions
|
||||
###########################################################
|
||||
|
||||
|
||||
### #################################
|
||||
### Utilities and helpers #################################
|
||||
|
||||
def input_editable(prompt, prefill=''):
|
||||
|
@ -68,27 +70,44 @@ def validate_path(path):
|
|||
path_body = '/'.join(path_body_list)
|
||||
is_path = os.path.isdir(path_body)
|
||||
if not is_path:
|
||||
print("Invalid filepath")
|
||||
os.exit(2)
|
||||
return False
|
||||
return True
|
||||
|
||||
def check_file_writable(fnm):
|
||||
if os.path.exists(fnm):
|
||||
if os.path.isfile(fnm): # is it a file or a dir?
|
||||
return os.access(fnm, os.W_OK)
|
||||
else:
|
||||
return False
|
||||
pdir = os.path.dirname(fnm)
|
||||
if not pdir: pdir = '.'
|
||||
return os.access(pdir, os.W_OK)
|
||||
|
||||
|
||||
def print_help():
|
||||
helptext = [
|
||||
"",
|
||||
"",
|
||||
"{}Commands are entered as the only entry for their row:{}".format(c.yellow, c.end),
|
||||
" {}.{} - Finish writing/exit, will prompt for save".format(c.b_green, c.end),
|
||||
" {}!?{} - Print this help message".format(c.b_green, c.end),
|
||||
" {}!d{} - Display the contents of the file".format(c.b_green, c.end),
|
||||
" {}!v{} - View lines, prompt will request a range of lines".format(c.b_green, c.end),
|
||||
" {}!#{} - Rewrite a line, # is replaced by line number e.g. !27".format(c.b_green, c.end),
|
||||
" {}!x{} - Delete line(s), prompt will request line range".format(c.b_green, c.end),
|
||||
" {}!i{} - Insert line(s), prompt will request start point and quantity".format(c.b_green, c.end),
|
||||
" {}!g{} - Print the ruler/guide".format(c.b_green, c.end),
|
||||
" {}!c{} - Copy line(s) to the paste buffer, prompt will request line range".format(c.b_green, c.end), # TODO
|
||||
" {}!p{} - Paste the contents of the paste buffer, prompt will request location".format(c.b_green, c.end), # TODO
|
||||
" {}!b{} - Buffer view (print the paste buffer to the screen)".format(c.b_green, c.end), # TODO
|
||||
"",
|
||||
" {}!?{} - Print this help message".format(c.b_green, c.end),
|
||||
" {}!g{} - Print the ruler/guide".format(c.b_green, c.end),
|
||||
"",
|
||||
" {}!d{} - Display the whole file".format(c.b_green, c.end),
|
||||
" {}!v{} - View range of lines (will request line range)".format(c.b_green, c.end),
|
||||
"",
|
||||
" {}!#{} - Edit a line (i.e. !27)".format(c.b_green, c.end),
|
||||
" {}!i{} - Insert empty line(s) (will request location/count)".format(c.b_green, c.end),
|
||||
" {}!x{} - Cut/copy line(s) (will request line range)".format(c.b_green, c.end),
|
||||
"",
|
||||
" {}!c{} - Copy to the paste buffer (will request line range)".format(c.b_green, c.end), # TODO
|
||||
" {}!p{} - Paste from the paste buffer (will request destination)".format(c.b_green, c.end), # TODO
|
||||
" {}!b{} - Buffer view (print the paste buffer)".format(c.b_green, c.end), # TODO
|
||||
"",
|
||||
" {}!s{} - Save changes to the document".format(c.b_green, c.end),
|
||||
" {}!a{} - Save as a new file (will request file location)".format(c.b_green, c.end),
|
||||
" {}.{} - Finish writing/exit (will prompt for save)".format(c.b_green, c.end),
|
||||
"",
|
||||
"{}- - -{}".format(c.yellow, c.end),
|
||||
""
|
||||
]
|
||||
for x in helptext:
|
||||
|
@ -129,13 +148,110 @@ def build_contents_from_file(path):
|
|||
filepath = os.path.abspath(path)
|
||||
filename = path.split('/')[-1]
|
||||
try:
|
||||
validate_path(filepath)
|
||||
valid_path = validate_path(filepath)
|
||||
if not valid_path:
|
||||
print('Invalid file path')
|
||||
os.exit(2)
|
||||
|
||||
with open(os.path.abspath(filepath), 'r') as f:
|
||||
content = f.read().split('\n')
|
||||
if content[-1] == '':
|
||||
content.pop()
|
||||
except FileNotFoundError:
|
||||
content = []
|
||||
except PermissionError:
|
||||
print('You do not have permission to read {}'.format(path))
|
||||
os.exit(2)
|
||||
|
||||
|
||||
# Yes No queries the user with a yes no question and returns
|
||||
# True on yes and False on no
|
||||
def yes_no(question):
|
||||
confirmation = ''
|
||||
while confirmation not in ['y','yes','n','no']:
|
||||
confirmation = input(question)
|
||||
confirmation = confirmation.lower()
|
||||
return True if confirmation in ['y', 'yes'] else False
|
||||
|
||||
|
||||
#### ###########################
|
||||
##### Input and Command Routers ###########################
|
||||
|
||||
# Chalk is the entry point into the editor, contains the
|
||||
# input loop and does some routing
|
||||
def chalk(path):
|
||||
global filename
|
||||
global file_changed
|
||||
|
||||
build_contents_from_file(path)
|
||||
|
||||
print_banner(filename)
|
||||
print_ruler()
|
||||
|
||||
while True:
|
||||
ln = input('{:6} {}>{} '.format(len(content), c.yellow, c.end))
|
||||
if ln == '.':
|
||||
# End the editing session (quit)
|
||||
# Will query for save if the file has been changed
|
||||
quit()
|
||||
elif re.match(r'^\!\d+$',ln):
|
||||
# Edit a previous line
|
||||
edit_line(ln)
|
||||
elif len(ln) == 2 and ln[0] == '!':
|
||||
# Route a command
|
||||
command_router(ln)
|
||||
else:
|
||||
# Add new content
|
||||
content.append(ln)
|
||||
file_changed = True
|
||||
|
||||
# Command router takes a command line and routes it to its
|
||||
# command function
|
||||
def command_router(ln):
|
||||
if ln == '!?':
|
||||
print_help()
|
||||
elif ln == '!g':
|
||||
print_ruler()
|
||||
elif ln == '!d':
|
||||
display_file()
|
||||
elif ln == '!v':
|
||||
view_rows()
|
||||
elif ln == '!i':
|
||||
insert_lines()
|
||||
elif ln == '!x':
|
||||
cut_lines()
|
||||
elif ln == '!c':
|
||||
copy_rows()
|
||||
elif ln == '!p':
|
||||
paste_from_buffer()
|
||||
elif ln == '!b':
|
||||
view_paste_buffer()
|
||||
elif ln == '!s':
|
||||
save_changes()
|
||||
elif ln == '!a':
|
||||
save_as()
|
||||
else:
|
||||
print('{:9}{}Unknown command: {}{}'.format(' ', c.red, ln, c.end))
|
||||
|
||||
|
||||
##### ###################################
|
||||
##### Command Functions ###################################
|
||||
|
||||
# Edit line edits an existing line or prints an error
|
||||
# if the line was unable to be edited
|
||||
def edit_line(ln):
|
||||
global content
|
||||
global file_changed
|
||||
try:
|
||||
row = int(ln[1:])
|
||||
newln = input_editable('{:6} {}>{} '.format(row, c.b_blue, c.end), content[row])
|
||||
if newln == '!c':
|
||||
print('{:8} Cancelled...'.format(' '))
|
||||
else:
|
||||
content[row] = newln
|
||||
file_changed = True
|
||||
except:
|
||||
print('{}{:8} Invalid entry!{}'.format(c.b_red, ' ', c.end))
|
||||
|
||||
|
||||
# Save changes saves changes to a file
|
||||
|
@ -156,88 +272,39 @@ def save_changes():
|
|||
except PermissionError:
|
||||
print('{} You do not have permission to write to this file.{}'.format(c.red, c.end))
|
||||
return False
|
||||
except:
|
||||
print('{} Error while writing to file: {}{}'.format(c.red, e, c.end))
|
||||
return False
|
||||
|
||||
|
||||
# Yes No queries the user with a yes no question and returns
|
||||
# True on yes and False on no
|
||||
def yes_no(question):
|
||||
confirmation = ''
|
||||
while confirmation not in ['y','yes','n','no']:
|
||||
confirmation = input(question)
|
||||
confirmation = confirmation.lower()
|
||||
return True if confirmation in ['y', 'yes'] else False
|
||||
|
||||
|
||||
|
||||
##### Input and Command Routers ###########################
|
||||
|
||||
# Chalk is the entry point into the editor, contains the
|
||||
# input loop and does some routing
|
||||
def chalk(path):
|
||||
# Save as will switch the save location to
|
||||
# a new file path and save the content buffer
|
||||
# to that new path. It will validate the path
|
||||
# beforehand
|
||||
def save_as():
|
||||
global filepath
|
||||
global filename
|
||||
global file_changed
|
||||
|
||||
build_contents_from_file(path)
|
||||
print('{:9}{}Enter the new save path (can be relative):{}'.format(' ', c.cyan, c.end))
|
||||
path = input('{:6} {}>{} '.format(' ', c.green, c.end))
|
||||
|
||||
print_banner(filename)
|
||||
print_ruler()
|
||||
continue_save_as = yes_no('{:9}{}Are you sure you want to save as {}? (y/n){} '.format(' ', c.cyan, path, c.end))
|
||||
if not continue_save_as:
|
||||
print('{:9}Save canceled.'.format(' '))
|
||||
|
||||
while True:
|
||||
ln = input('{:6} {}>{} '.format(len(content), c.yellow, c.end))
|
||||
if ln == '.':
|
||||
# End the editing session (quit)
|
||||
# Will query for save if the file has been changed
|
||||
quit()
|
||||
elif len(ln) == 2 and ln[0] == '!':
|
||||
# Route a command
|
||||
command_router(ln)
|
||||
elif re.match(r'^\!\d+$',ln):
|
||||
# Edit a previous line
|
||||
edit_line(ln)
|
||||
else:
|
||||
# Add new content
|
||||
content.append(ln)
|
||||
file_changed = True
|
||||
fp = os.path.abspath(path)
|
||||
fn = path.split('/')[-1]
|
||||
valid = check_file_writable(fp)
|
||||
if not valid:
|
||||
print('{:9}{}The path is invalid, save cancelled{}'.format(' ', c.red, c.end))
|
||||
|
||||
# Command router takes a command line and routes it to its
|
||||
# command function
|
||||
def command_router(ln):
|
||||
if ln == '!?':
|
||||
print_help()
|
||||
elif ln == '!g':
|
||||
print_ruler()
|
||||
elif ln == '!d':
|
||||
display_file()
|
||||
elif ln == '!x':
|
||||
delete_lines()
|
||||
elif ln == '!i':
|
||||
insert_lines()
|
||||
elif ln == '!v':
|
||||
view_rows()
|
||||
else:
|
||||
print('{:9}{}Unknown command: {}{}'.format(' ', c.red, ln, c.end))
|
||||
filepath = fp
|
||||
filename = fn
|
||||
file_changed = True
|
||||
save_changes()
|
||||
|
||||
|
||||
|
||||
##### Command Functions ###################################
|
||||
|
||||
# Edit line edits an existing line or prints an error
|
||||
# if the line was unable to be edited
|
||||
def edit_line(ln):
|
||||
global content
|
||||
global file_changed
|
||||
try:
|
||||
row = int(ln[1:])
|
||||
newln = input_editable('{:6} {}>{} '.format(row, c.b_blue, c.end), content[row])
|
||||
if newln == '!c':
|
||||
print('{:8} Cancelled...'.format(' '))
|
||||
else:
|
||||
content[row] = newln
|
||||
file_changed = True
|
||||
except:
|
||||
print('{}{:8} Invalid entry!{}'.format(c.b_red, ' ', c.end))
|
||||
|
||||
|
||||
# Quit will quit the program, but first will ask to save if there
|
||||
# are any unsaved changes
|
||||
def quit():
|
||||
|
@ -265,9 +332,10 @@ def display_file():
|
|||
print(' - - -\n')
|
||||
|
||||
|
||||
def delete_lines():
|
||||
def cut_lines():
|
||||
global content
|
||||
global file_changed
|
||||
global paste_buffer
|
||||
|
||||
print('{:9}{}Enter the line number you want to start deleting at:{}'.format(' ', c.cyan, c.end))
|
||||
beg = input('{:6} {}>{} '.format(' ', c.b_red, c.end))
|
||||
|
@ -288,7 +356,7 @@ def delete_lines():
|
|||
print('{}{:9}Invalid entry{}'.format(c.red, ' ', c.end))
|
||||
return
|
||||
|
||||
# TODO: Add content to copy buffer
|
||||
paste_buffer = content[beg:end]
|
||||
|
||||
if end == len(content):
|
||||
content = content[:beg]
|
||||
|
@ -313,8 +381,6 @@ def insert_lines():
|
|||
if not continue_insert:
|
||||
print('{:9}Insertion canceled.'.format(' '))
|
||||
|
||||
|
||||
print('\n{:8} {}Enter the line number that you want the new line(s) to\n{:8} appear after.To insert multiple lines enter the starting\n{:8} point and the number of lines separated by a space.\n{:8}To cancel, enter -1{}'.format(' ', c.cyan,' ', ' ', ' ', c.end))
|
||||
try:
|
||||
beg = int(beg)
|
||||
count = int(count)
|
||||
|
@ -357,6 +423,67 @@ def view_rows():
|
|||
print('{}{:8} Invalid entry{}'.format(c.red, ' ', c.end))
|
||||
|
||||
|
||||
def copy_rows():
|
||||
global paste_buffer
|
||||
|
||||
print('{:9}{}Enter the line number you want to start copying from:{}'.format(' ', c.cyan, c.end))
|
||||
start = input('{:6} {}>{} '.format(' ', c.yellow, c.end))
|
||||
|
||||
print('{:9}{}Enter the last line you want to copy ($ for end of file):{}'.format(' ', c.cyan, c.end))
|
||||
finish = input('{:6} {}>{} '.format(' ', c.yellow, c.end))
|
||||
if finish == '$':
|
||||
finish = len(content) - 1
|
||||
|
||||
try:
|
||||
beg = int(start)
|
||||
end = int(finish)
|
||||
|
||||
if beg > end or beg < 0 or end > len(content) - 1:
|
||||
print('{}{:9}Invalid entry x{}'.format(c.red, ' ', c.end))
|
||||
return
|
||||
else:
|
||||
paste_buffer = content[beg:end + 1]
|
||||
|
||||
except:
|
||||
print('{}{:8} Invalid entry{}'.format(c.red, ' ', c.end))
|
||||
|
||||
|
||||
def paste_from_buffer():
|
||||
global content
|
||||
global file_changed
|
||||
|
||||
print('{:9}{}Enter a line number. The pasted data will be inserted {}before{} the given line:{}'.format(' ', c.cyan, '\033[4m', '\033[24m', c.end))
|
||||
beg = input('{:6} {}>{} '.format(' ', c.b_green, c.end))
|
||||
|
||||
continue_paste = yes_no('{:9}{}Are you sure you want to paste from the paste buffer before line {}? (y/n){} '.format(' ', c.cyan, beg, c.end))
|
||||
if not continue_paste:
|
||||
print('{:9}Paste canceled.'.format(' '))
|
||||
|
||||
try:
|
||||
beg = int(beg)
|
||||
|
||||
if beg < 0 or beg > len(content):
|
||||
print('{}{:8} Invalid entry{}'.format(c.red, ' ', c.end))
|
||||
return
|
||||
|
||||
for row in paste_buffer[::-1]:
|
||||
content.insert(beg,row)
|
||||
file_changed = True
|
||||
except:
|
||||
print('{}{:8} Invalid entry{}'.format(c.red, ' ', c.end))
|
||||
|
||||
|
||||
def view_paste_buffer():
|
||||
print('')
|
||||
if len(paste_buffer):
|
||||
for num, ln in enumerate(paste_buffer):
|
||||
print('{:6} - {}{}{}'.format('pb', c.blue, ln, c.end))
|
||||
else:
|
||||
print('{:6} - {}{}{}'.format('pb', c.blue, 'The paste buffer is currently empty', c.end))
|
||||
print('')
|
||||
|
||||
|
||||
|
||||
|
||||
###########################################################
|
||||
# Init
|
||||
|
|
Loading…
Reference in New Issue