From fca39c3434656143651611c029f69356658df818 Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Fri, 26 May 2017 18:33:51 -0400 Subject: [PATCH] moved ctags project file detection logic from edit shell script into xedit binary. --- TODO.md | 22 +++++++++--------- docs/edit.1 | 4 ++-- docs/edit.1.md | 28 +++++++++++------------ docs/xedit.1 | 2 +- docs/xedit.1.md | 2 +- edit | 42 +---------------------------------- inc/edit.h | 5 +++++ lib/utils.c | 46 +++++++++++++++++++++++++++++++++++++- xedit.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++- 9 files changed, 139 insertions(+), 71 deletions(-) diff --git a/TODO.md b/TODO.md index 94990bb..000d7ce 100644 --- a/TODO.md +++ b/TODO.md @@ -2,25 +2,21 @@ Up Next: +* rename to tide +* Change PageUp/Dn to move cursor by screenfuls +* Add Ctrl+Shift+A shortcut to select all * ctrl+d with no selection does word selection instead of context -* get rid of edit wrapper script * move by words is inconsistent. Example: var infoId = 'readerinfo'+reader.id; -* rename to tide - -* Add a way to CD using a builtin -* Ctrl+PageUp/Dn to move cursor by screenfuls -* Ctrl+\ Shortcut to warp cursor to middle of current screen. -* Find shortcut should select previous word if current char is newline -* diagnostic messages can stack up if deselected and not resolved -* highlight all matches of search term +* Add a way to CD using a builtin (buffers will track original dir) * Ctrl+Shift+Enter copies indent of wrong line -* jump to previous or next line with less indent * Shift+Insert should insert primary selection The Future: +* highlight all matches of search term * Make Fn keys execute nth command in the tags buffers +* jump to previous or next line with less indent * use transaction ids to only mark buffer dirty when it really is * refactor selection handling to buf.c to prepare for multiple selections. * 100% coverage with unit and unit-integration tests @@ -33,6 +29,12 @@ The Future: * implement command diffing logic to optimize the undo/redo log * Status line should omit characters from beginning of path to make file path fit +Maybe think about addressing these later: + +* Find shortcut should select previous word if current char is newline +* Ctrl+\ Shortcut to warp cursor to middle of current screen. +* diagnostic messages can stack up if deselected and not resolved + # Auxillary Programs * Visual diff tool diff --git a/docs/edit.1 b/docs/edit.1 index 9c17e58..6fd2d67 100644 --- a/docs/edit.1 +++ b/docs/edit.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "EDIT" "1" "March 2017" "" "" +.TH "EDIT" "1" "May 2017" "" "" . .SH "NAME" \fBedit\fR \- convenience script for launching xedit(1) with a proper environment @@ -10,7 +10,7 @@ \fBedit\fR [\fIfile\fR\.\.\.] . .SH "DESCRIPTION" -This script acts as a wrapper around xedit(1)\. It is responsible for setting up the environment variables and loading the rc file xedit(1) before launching a new instance of xedit(1) for each file provided on the command line\. If no files are provided, xedit(1) will be launched to edit a scratch buffer\. +This script acts as a wrapper around xedit(1)\. It is responsible for setting up the environment variables and loading the rc file before launching a new instance of xedit(1) to edit the given files\. If no files are provided a new instance of xedit(1) will be launched with an empty edit buffer\. . .SH "FILES" . diff --git a/docs/edit.1.md b/docs/edit.1.md index abec4e7..f14df3a 100644 --- a/docs/edit.1.md +++ b/docs/edit.1.md @@ -7,39 +7,39 @@ ## DESCRIPTION This script acts as a wrapper around xedit(1). It is responsible for setting up -the environment variables and loading the rc file xedit(1) before launching a -new instance of xedit(1) for each file provided on the command line. If no files -are provided, xedit(1) will be launched to edit a scratch buffer. +the environment variables and loading the rc file before launching a +new instance of xedit(1) to edit the given files. If no files are provided a new +instance of xedit(1) will be launched with an empty edit buffer. ## FILES * `$HOME/.config/edit/editrc`: - Shell script loaded in current environment to make shell functions and + Shell script loaded in current environment to make shell functions and environment variables available to xedit(1) ## ENVIRONMENT - + * `BASH_ENV`: - Set to same value as $EDITRCFILE so that the file is loaded as a bash script + Set to same value as $EDITRCFILE so that the file is loaded as a bash script in the event that the user shell is bash(1) * `DISPLAY`: - This variable is used to determine if we are running in an X11 environment. - If $DISPLAY is not set then the contents of the $EDITOR variable is used to - determine what editor to launch in lieu of xedit(1). If $EDITOR is not set + This variable is used to determine if we are running in an X11 environment. + If $DISPLAY is not set then the contents of the $EDITOR variable is used to + determine what editor to launch in lieu of xedit(1). If $EDITOR is not set then vim(1) is launched instead. - + * `EDITRCFILE`: - Contains the path of the shell script which is loaded before xedit(1) to - setup the environment and define shell functions which can be called during + Contains the path of the shell script which is loaded before xedit(1) to + setup the environment and define shell functions which can be called during an editing session. * `EDITOR`: - Used as a fallback for when not running in an X11 system (xedit(1) is of + Used as a fallback for when not running in an X11 system (xedit(1) is of course X11 only). * `PATH`: - The $PATH variable is modified in order to add $HOME/config/edit/tools/ to + The $PATH variable is modified in order to add $HOME/config/edit/tools/ to the path. This folder is a standard location in which user scripts and tools can be placed so they can be used from within xedit(1) without cluttering up the normal system path. diff --git a/docs/xedit.1 b/docs/xedit.1 index 32ae155..bc2cd56 100644 --- a/docs/xedit.1 +++ b/docs/xedit.1 @@ -7,7 +7,7 @@ \fBxedit\fR \- a text editor inspired by acme(1) from Plan 9 and Inferno . .SH "SYNOPSIS" -\fBxedit\fR [\fIfile\fR] +\fBxedit\fR [\fIfile\fR\.\.\.] . .SH "DESCRIPTION" \fBxedit\fR is a text editor inspired by the Acme editor from the Plan 9 and Inferno operating systems\. Unlike Acme, \fBxedit\fR is a single window, single file editor\. Instead of baking a window manager into the editor, this job is relegated to an X11 window manager\. It is recommended that \fBxedit\fR be used with a tiling window manager such as dwm(1) or spectrwm(1)\. These window managers will make dealing with multiple windows much easier and more efficient\. diff --git a/docs/xedit.1.md b/docs/xedit.1.md index 65c2007..e24c0cd 100644 --- a/docs/xedit.1.md +++ b/docs/xedit.1.md @@ -2,7 +2,7 @@ ## SYNOPSIS -`xedit` [_file_] +`xedit` [_file_...] ## DESCRIPTION diff --git a/edit b/edit index 2ce5ba5..42ae479 100755 --- a/edit +++ b/edit @@ -1,41 +1,5 @@ #!/bin/sh -# exits with error message -die() { - printf "error: %s\n" "$@" - exit 1 -} - -# search for a ctags file recursively up the directory tree from the file to be -# opened. If one is found, change the working directory of the new process to -# the containing folder and launch xedit with the path of the file relative to -# the new working directory. -edit_relative_ctags(){ - origdir="$PWD" - case "$1" in - /*) origpath="$1" ;; # absoulte path given - *) origpath="$PWD/$1" ;; # relative path given - esac - path="${origpath##*/}" - - # try to cd to the directory containing the file and attempt to walk the - # tree backwars until we find a tags file. if one is found, open the file - # from that directory. - cd "${origpath%/*}" || die "could not open file: '$1'" - dir="$PWD" - while [ "$dir" != "" ]; do - if [ -f "$dir/tags" ]; then - cd "$dir" && exec nohup xedit "$path" > /dev/null 2>&1 - else - path="${dir##*/}/$path" - dir="${dir%/*}" - fi - done - - # file is not part of a project. open it as-is - cd "$origdir" && exec nohup xedit "$1" > /dev/null 2>&1 -} - # Add the editing tools directory to your PATH var so its contents may be used # while editing. export PATH="$HOME/.config/edit/tools:$PATH" @@ -57,10 +21,6 @@ if [ -z "$DISPLAY" ]; then else "$EDITOR" "$@" fi -elif [ 0 -eq $# ]; then - (nohup xedit > /dev/null 2>&1) & else - for f in "$@"; do - edit_relative_ctags "$f" & - done + (nohup xedit "$@" > /dev/null 2>&1) & fi diff --git a/inc/edit.h b/inc/edit.h index 8aabb01..af2e8ba 100644 --- a/inc/edit.h +++ b/inc/edit.h @@ -14,6 +14,11 @@ char* stringdup(const char* str); char* fdgets(int fd); char* chomp(char* in); uint64_t modtime(char* path); +char* getcurrdir(void); +char* dirname(char* path); +bool try_chdir(char* fpath); +char* strconcat(char* dest, ...); +bool file_exists(char* path); /* Buffer management functions *****************************************************************************/ diff --git a/lib/utils.c b/lib/utils.c index 3d74cbb..ceebd3e 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -40,7 +40,8 @@ FMap mmap_readwrite(char* path, size_t sz) { int fd = open(path, O_CREAT|O_RDWR, 0644); if (fd >= 0) { ftruncate(fd, sz); - void* buf = mmap(NULL, pagealign(sz), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (buf != MAP_FAILED) { + void* buf = mmap(NULL, pagealign(sz), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (buf != MAP_FAILED) { file.buf = buf; file.len = sz; } @@ -104,3 +105,46 @@ uint64_t modtime(char* path) { return 0u; return (uint64_t)status.st_mtime; } + +char* getcurrdir(void) { + size_t size = 4096; + char *buf = NULL, *ptr = NULL; + for (; ptr == NULL; size *= 2) { + buf = realloc(buf, size); + ptr = getcwd(buf, size); + if (ptr == NULL && errno != ERANGE) + die("Failed to retrieve current directory"); + } + return buf; +} + +char* dirname(char* path) { + path = stringdup(path); + char* end = strrchr(path, '/'); + if (!end) return NULL; + *end = '\0'; + return path; +} + +bool try_chdir(char* fpath) { + char* dir = dirname(fpath); + bool success = (dir && *dir && chdir(dir) >= 0); + free(dir); + return success; +} + +char* strconcat(char* dest, ...) { + va_list args; + char* curr = dest; + va_start(args, dest); + for (char* s; (s = va_arg(args, char*));) + while (*s) *(curr++) = *(s++); + va_end(args); + *curr = '\0'; + return dest; +} + +bool file_exists(char* path) { + struct stat st; + return (stat(path, &st) < 0); +} diff --git a/xedit.c b/xedit.c index 5d659a6..e91798a 100644 --- a/xedit.c +++ b/xedit.c @@ -5,6 +5,7 @@ #include #include #include +#include #define INCLUDE_DEFS #include "config.h" @@ -526,6 +527,54 @@ void onshutdown(void) { quit(); } +void edit_relative(char* path) { + char *currdir = NULL, *currpath = NULL, *relpath = NULL; + char* origdir = getcurrdir(); + + /* search for a ctags index file indicating the project root */ + if (try_chdir(path)) { + currdir = getcurrdir(); + currpath = calloc(strlen(currdir) + strlen("/tags") + 1, 1); + relpath = calloc(strlen(currdir) + strlen("/tags") + 1, 1); + while (true) { + /* figure out the current path to check */ + strconcat(currpath, currdir, "/tags", 0); + if (file_exists(currpath)) { + /* move up a dir */ + char* end = strrchr(currdir,'/'); + if (!end) break; + char* temp = stringdup(relpath); + strconcat(relpath, end, temp, 0); + free(temp); + *end = '\0'; + } else { + break; + } + } + } + + /* cd to the project directory or the original working directory and open + the file relative to the new working directory */ + if (currdir && *currdir) { + char* fname = strrchr(path, '/')+1; + if (*relpath) + strconcat(currpath, relpath+1, "/", fname, 0); + else + strconcat(currpath, fname, 0); + chdir(currdir); + view_init(win_view(EDIT), currpath, ondiagmsg); + } else { + chdir(origdir); + view_init(win_view(EDIT), path, ondiagmsg); + } + + /* cleanup */ + free(currdir); + free(currpath); + free(relpath); + free(origdir); +} + #ifndef TEST int main(int argc, char** argv) { /* setup the shell */ @@ -536,7 +585,15 @@ int main(int argc, char** argv) { char* tags = getenv("EDITTAGS"); win_settext(TAGS, (tags ? tags : DefaultTags)); win_setruler(RulePosition); - view_init(win_view(EDIT), (argc > 1 ? argv[1] : NULL), ondiagmsg); + /* open the first file in this instance */ + if (argc > 1) + edit_relative(argv[1]); + /* spawn a new instance for each remaining file */ + for (int i = 2; i < argc; i++) { + OpenCmd[1] = argv[i]; + cmdrun(OpenCmd, NULL); + } + /* now create the window and start the event loop */ win_setkeys(Bindings); win_loop(); return 0; -- 2.52.0