Refactoring of code and update of zsh plugin for fedit script
This commit is contained in:
		
							
								
								
									
										145
									
								
								fedit.py
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								fedit.py
									
									
									
									
									
								
							| @@ -14,27 +14,31 @@ import shutil | |||||||
| import subprocess | import subprocess | ||||||
|  |  | ||||||
| # ========== Constants ========== | # ========== Constants ========== | ||||||
| # Paths | # ----- Paths ----- | ||||||
| BOOT_DIR = "/boot" | BOOT_DIR = "/boot" | ||||||
| ETC_DIR = "/etc" | ETC_DIR = "/etc" | ||||||
|  |  | ||||||
| # Exit Codes | # ----- Exit Codes ----- | ||||||
| E_NOEDITORFOUND = 2 | E_NOEDITORFOUND = 2 | ||||||
| E_NOFILESELECTED = 3 | E_NOFILESELECTED = 3 | ||||||
|  |  | ||||||
| # Commands | # ----- Commands ----- | ||||||
| FIND_CMD = "/usr/bin/fd" | FIND_CMD = "/usr/bin/fd" | ||||||
| FIND_OPTS = ["--hidden", "--print0", "--type", "f", "--no-ignore-vcs"] | FIND_OPTS = ["--hidden", "--print0", "--type", "f"] | ||||||
| FZF_CMD = "/usr/bin/fzf" | EXTRA_FIND_OPTS = {"no_ignore_vcs": "--no-ignore", "no_ignore": "--no-ignore-vcs"} | ||||||
| FZF_OPTS = ["--read0", "--select-1", "--exit-0", "--print0"] |  | ||||||
| LOCATE_CMD = "/usr/bin/locate" | LOCATE_CMD = "/usr/bin/locate" | ||||||
| LOCATE_OPTS = ["--all", "--ignore-case", "--null"] | LOCATE_OPTS = ["--all", "--ignore-case", "--null"] | ||||||
|  |  | ||||||
|  | FZF_CMD = "/usr/bin/fzf" | ||||||
|  | FZF_OPTS = ["--read0", "--select-1", "--exit-0", "--print0"] | ||||||
|  |  | ||||||
|  | # ----- Misc. ----- | ||||||
| LOCALE = "utf-8" | LOCALE = "utf-8" | ||||||
|  |  | ||||||
|  |  | ||||||
| # ========== Functions ========== | # ========== Functions ========== | ||||||
| def select_editor(editor_override=None): | def select_editor(override=None): | ||||||
|     """Return a possible canonical path to an editor. |     """Return a possible canonical path to an editor. | ||||||
|     Select an editor from one of: |     Select an editor from one of: | ||||||
|     * -e, --editor |     * -e, --editor | ||||||
| @@ -45,15 +49,15 @@ def select_editor(editor_override=None): | |||||||
|  |  | ||||||
|     If an editor cannot be resolved, then an Error is raised instead. |     If an editor cannot be resolved, then an Error is raised instead. | ||||||
|  |  | ||||||
|     :param editor_override: argument to override an editor |     :param override: argument to override an editor | ||||||
|     :returns: path to one of these editors |     :returns: path to one of these editors | ||||||
|     :rtype: str |     :rtype: str | ||||||
|     :raises: FileNotFoundError if an editor could not be resolved |     :raises: FileNotFoundError if an editor could not be resolved | ||||||
|     """ |     """ | ||||||
|     editor = None |     editor = None | ||||||
|  |  | ||||||
|     if editor_override is not None: |     if override is not None: | ||||||
|         editor = shutil.which(editor_override) |         editor = shutil.which(override) | ||||||
|     elif "EDITOR" in os.environ: |     elif "EDITOR" in os.environ: | ||||||
|         editor = shutil.which(os.environ.get("EDITOR")) |         editor = shutil.which(os.environ.get("EDITOR")) | ||||||
|     elif shutil.which("vim") is not None: |     elif shutil.which("vim") is not None: | ||||||
| @@ -69,12 +73,16 @@ def gen_editor_cmd(filename): | |||||||
|     """Generate a command line to run for editing a file based on |     """Generate a command line to run for editing a file based on | ||||||
|     permissions. |     permissions. | ||||||
|  |  | ||||||
|  |     This command does not pass extra options to the editor, hence | ||||||
|  |     there are no arguments to pass for options. | ||||||
|  |  | ||||||
|     :param filename: name of file to edit |     :param filename: name of file to edit | ||||||
|     :type filename: str or path-like object |     :type filename: str or path-like object | ||||||
|     :returns: command to execute to edit file |     :returns: command to execute to edit file | ||||||
|     :rtype: list |     :rtype: list | ||||||
|     """ |     """ | ||||||
|     # possible for a race condition to occur here |     # Possible for a race condition to occur here | ||||||
|  |     # What happens if the file or its metadata changes? | ||||||
|     if os.access(filename, os.W_OK): |     if os.access(filename, os.W_OK): | ||||||
|         return [editor, filename] |         return [editor, filename] | ||||||
|     else: |     else: | ||||||
| @@ -90,21 +98,24 @@ def run_fzf(files): | |||||||
|     :rtype: str |     :rtype: str | ||||||
|     """ |     """ | ||||||
|     selected_file = subprocess.run( |     selected_file = subprocess.run( | ||||||
|         [FZF_CMD] + FZF_OPTS, input=files, stdout=subprocess.PIPE |         [FZF_CMD, *FZF_OPTS], input=files, stdout=subprocess.PIPE | ||||||
|     ).stdout |     ).stdout | ||||||
|  |  | ||||||
|     return selected_file.decode(LOCALE).strip("\x00") |     return selected_file.decode(LOCALE).strip("\x00") | ||||||
|  |  | ||||||
|  |  | ||||||
| def find_files(directory=None): | def find_files(opts, directory=None): | ||||||
|     """Use a find-based program to locate files, then pass to fzf. |     """Use a find-based program to locate files, then pass to fzf. | ||||||
|  |  | ||||||
|  |     :param opts: options to pass to the find program | ||||||
|  |     :type opts: list of str | ||||||
|     :param directory: directory to search for files |     :param directory: directory to search for files | ||||||
|     :type directory: str |     :type directory: str | ||||||
|     :returns: path of user-selected file |     :returns: path of user-selected file | ||||||
|     :rtype: bytes |     :rtype: bytes | ||||||
|     """ |     """ | ||||||
|     cmd = [FIND_CMD] + FIND_OPTS |     cmd = [FIND_CMD, *opts] | ||||||
|  |  | ||||||
|     if directory is not None: |     if directory is not None: | ||||||
|         cmd.extend(["--", ".", directory]) |         cmd.extend(["--", ".", directory]) | ||||||
|  |  | ||||||
| @@ -126,51 +137,73 @@ def locate_files(patterns): | |||||||
|  |  | ||||||
|  |  | ||||||
| # ========== Main Script ========== | # ========== Main Script ========== | ||||||
| parser = argparse.ArgumentParser() | if __name__ == "__main__": | ||||||
| parser.add_argument( |     parser = argparse.ArgumentParser() | ||||||
|     "-b", |     parser.add_argument( | ||||||
|     "--boot", |         "-b", | ||||||
|     action="store_const", |         "--boot", | ||||||
|     const=BOOT_DIR, |         action="store_const", | ||||||
|     dest="dir", |         const=BOOT_DIR, | ||||||
|     help="edit a file in /boot", |         dest="dir", | ||||||
| ) |         help="edit a file in /boot", | ||||||
| parser.add_argument( |     ) | ||||||
|     "-d", "--dir", dest="dir", type=str, help="edit a file in a given directory" |     parser.add_argument( | ||||||
| ) |         "-d", "--dir", dest="dir", type=str, help="edit a file in a given directory" | ||||||
| parser.add_argument( |     ) | ||||||
|     "-E", |     parser.add_argument( | ||||||
|     "--etc", |         "-E", | ||||||
|     action="store_const", |         "--etc", | ||||||
|     const=ETC_DIR, |         action="store_const", | ||||||
|     dest="dir", |         const=ETC_DIR, | ||||||
|     help="edit a file in /etc", |         dest="dir", | ||||||
| ) |         help="edit a file in /etc", | ||||||
| parser.add_argument("-e", "--editor", help="use a given editor") |     ) | ||||||
| parser.add_argument("patterns", type=str, nargs="*", help="patterns to pass to locate") |     parser.add_argument( | ||||||
|  |         "-I", | ||||||
|  |         "--no-ignore", | ||||||
|  |         action="append_const", | ||||||
|  |         const="--no-ignore", | ||||||
|  |         dest="extra_find_opts", | ||||||
|  |         help="do not respect .(git|fd)ignore files", | ||||||
|  |     ) | ||||||
|  |     parser.add_argument( | ||||||
|  |         "-i", | ||||||
|  |         "--no-ignore-vcs", | ||||||
|  |         action="append_const", | ||||||
|  |         const="--no-ignore-vcs", | ||||||
|  |         dest="extra_find_opts", | ||||||
|  |         help="do not respect .gitignore files", | ||||||
|  |     ) | ||||||
|  |     parser.add_argument("-e", "--editor", help="use a given editor") | ||||||
|  |     parser.add_argument( | ||||||
|  |         "patterns", type=str, nargs="*", help="patterns to pass to locate" | ||||||
|  |     ) | ||||||
|  |  | ||||||
| args = parser.parse_args() |     args = parser.parse_args() | ||||||
|  |  | ||||||
| final_find_cmd = [FIND_CMD] + FIND_OPTS |     user_opts = [] if args.extra_find_opts is None else args.extra_find_opts | ||||||
| editor = "" |     user_opts.extend(FIND_OPTS) | ||||||
|  |  | ||||||
| try: |     editor = "" | ||||||
|     editor = select_editor(args.editor) |  | ||||||
| except FileNotFoundError as e: |  | ||||||
|     print(e) |  | ||||||
|     exit(E_NOEDITORFOUND) |  | ||||||
|  |  | ||||||
| # If patterns were passed, use locate |     try: | ||||||
| # Otherwise check for -d and use fd |         editor = select_editor(args.editor) | ||||||
| if not args.patterns == []: |     except FileNotFoundError as e: | ||||||
|     files = locate_files(args.patterns) |         print(e) | ||||||
| else: |         exit(E_NOEDITORFOUND) | ||||||
|     files = find_files(args.dir) |  | ||||||
|  |  | ||||||
| selected_file = run_fzf(files) |     # If patterns were passed, use locate | ||||||
|  |     # Otherwise check for -d and use fd | ||||||
|  |     files = ( | ||||||
|  |         find_files(user_opts, args.dir) | ||||||
|  |         if not args.patterns | ||||||
|  |         else locate_files(args.patterns) | ||||||
|  |     ) | ||||||
|  |  | ||||||
| if not selected_file == "": |     selected_file = run_fzf(files) | ||||||
|     cmd = gen_editor_cmd(selected_file) |  | ||||||
|     subprocess.run(cmd) |     if selected_file != "": | ||||||
| else: |         cmd = gen_editor_cmd(selected_file) | ||||||
|     exit(E_NOFILESELECTED) |         subprocess.run(cmd) | ||||||
|  |     else: | ||||||
|  |         exit(E_NOFILESELECTED) | ||||||
|   | |||||||
| @@ -7,6 +7,8 @@ arguments=( | |||||||
|   {-d,--dir}'[edit a file in a given directory]' |   {-d,--dir}'[edit a file in a given directory]' | ||||||
|   {-E,--etc}'[edit a file in /etc]' |   {-E,--etc}'[edit a file in /etc]' | ||||||
|   {-e,--editor}'[use a given editor]' |   {-e,--editor}'[use a given editor]' | ||||||
|  |   {-I,--no-ignore}'[do not respect  .(git|fd)ignore files]' | ||||||
|  |   {-i,--no-ignore-vcs}'[do not respect .gitignore files]' | ||||||
|   '*:filename:_files' |   '*:filename:_files' | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user