Improve find_file_next and next-error
* no longer scan directory in `file_file_next` if pattern lacks wildcards * patterns without wildcards can have subdirectory * `next-error`: if file is not found in the current buffer directory, try and locate it in subdirectories
This commit is contained in:
parent
4722d0ceff
commit
9079f85034
7 changed files with 99 additions and 55 deletions
|
|
@ -375,6 +375,7 @@ int fbf_render_init(const char *font_path)
|
|||
fprintf(stderr, "Could not load font '%s'\n", filename);
|
||||
}
|
||||
}
|
||||
find_file_close(&ffs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -144,12 +144,13 @@ static int match_error(EditBuffer *b, int start_offset, ShellError *dest);
|
|||
|
||||
static void set_error_offset(EditBuffer *b, int offset)
|
||||
{
|
||||
pstrcpy(error_state.buffer, sizeof(error_state.buffer), b ? b->name : "");
|
||||
error_state.offset = offset - 1;
|
||||
error_state.line_num = error_state.col_num = -1;
|
||||
*error_state.filename = '\0';
|
||||
error_state.msg_offset = offset;
|
||||
*error_state.message = '\0';
|
||||
ShellError *sep = &error_state;
|
||||
pstrcpy(sep->buffer, sizeof(sep->buffer), b ? b->name : "");
|
||||
sep->offset = offset - 1;
|
||||
sep->line_num = sep->col_num = -1;
|
||||
*sep->filename = '\0';
|
||||
sep->msg_offset = offset;
|
||||
*sep->message = '\0';
|
||||
}
|
||||
|
||||
static QEProperty *shell_add_cwd(EditBuffer *b, int offset, const char *cwd, int force) {
|
||||
|
|
@ -3960,7 +3961,6 @@ static void do_compile(EditState *s, const char *cmd)
|
|||
static int match_error(EditBuffer *b, int start_offset, ShellError *dest)
|
||||
{
|
||||
char filename[MAX_FILENAME_SIZE];
|
||||
char fullpath[MAX_FILENAME_SIZE];
|
||||
buf_t fname[1];
|
||||
int line_num, col_num, len;
|
||||
int offset = start_offset;
|
||||
|
|
@ -4015,19 +4015,15 @@ static int match_error(EditBuffer *b, int start_offset, ShellError *dest)
|
|||
c = eb_nextc(b, offset, &offset);
|
||||
}
|
||||
if (dest) {
|
||||
canonicalize_absolute_buffer_path(b, start_offset,
|
||||
fullpath, countof(fullpath),
|
||||
filename);
|
||||
if (line_num != dest->line_num
|
||||
|| col_num != dest->col_num
|
||||
|| !strequal(fullpath, dest->filename)) {
|
||||
|| !strequal(filename, dest->filename)) {
|
||||
dest->offset = start_offset;
|
||||
dest->msg_offset = offset;
|
||||
dest->line_num = line_num;
|
||||
dest->col_num = col_num;
|
||||
pstrcpy(dest->filename, countof(dest->filename), fullpath);
|
||||
len = eb_fgets(b, dest->message, countof(dest->message),
|
||||
offset, &offset);
|
||||
pstrcpy(dest->filename, countof(dest->filename), filename);
|
||||
len = eb_fgets(b, dest->message, countof(dest->message), offset, &offset);
|
||||
dest->message[len] = '\0'; /* strip the trailing newline if any */
|
||||
return 2; // new error
|
||||
}
|
||||
|
|
@ -4037,7 +4033,9 @@ static int match_error(EditBuffer *b, int start_offset, ShellError *dest)
|
|||
|
||||
static void do_next_error(EditState *s, int arg, int dir)
|
||||
{
|
||||
char fullname[MAX_FILENAME_SIZE];
|
||||
QEmacsState *qs = s->qs;
|
||||
ShellError *sep = &error_state;
|
||||
EditState *e;
|
||||
EditBuffer *b;
|
||||
int offset;
|
||||
|
|
@ -4058,7 +4056,7 @@ static void do_next_error(EditState *s, int arg, int dir)
|
|||
set_error_offset(s->b, s->offset);
|
||||
}
|
||||
|
||||
if ((b = qe_find_buffer_name(qs, error_state.buffer)) == NULL) {
|
||||
if ((b = qe_find_buffer_name(qs, sep->buffer)) == NULL) {
|
||||
if (s->b->flags & BF_ERROR) {
|
||||
b = s->b;
|
||||
} else {
|
||||
|
|
@ -4080,7 +4078,7 @@ static void do_next_error(EditState *s, int arg, int dir)
|
|||
}
|
||||
|
||||
/* find next/prev error */
|
||||
offset = error_state.offset;
|
||||
offset = sep->offset;
|
||||
|
||||
/* CG: should use higher level parsing */
|
||||
for (;;) {
|
||||
|
|
@ -4097,22 +4095,32 @@ static void do_next_error(EditState *s, int arg, int dir)
|
|||
}
|
||||
offset = eb_prev_line(b, offset);
|
||||
}
|
||||
if (match_error(b, offset, &error_state) > 1) {
|
||||
if (match_error(b, offset, sep) > 1) {
|
||||
// found a new error
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
canonicalize_absolute_buffer_path(b, sep->offset, fullname, countof(fullname), sep->filename);
|
||||
if ((stat(fullname, &sb) < 0 || !S_ISREG(sb.st_mode))
|
||||
&& !is_abs_path(sep->filename) && *sep->filename != '~') {
|
||||
// try and locate filename in a subdirectory up to 2 levels down
|
||||
char path[MAX_FILENAME_SIZE];
|
||||
FindFileState *ffs;
|
||||
get_default_path(b, offset, path, countof(path));
|
||||
ffs = find_file_open(path, sep->filename, FF_NODIR | 2);
|
||||
find_file_next(ffs, fullname, sizeof(fullname));
|
||||
find_file_close(&ffs);
|
||||
}
|
||||
|
||||
/* check file and go to the error */
|
||||
if (stat(error_state.filename, &sb) < 0) {
|
||||
// XXX: should prompt for fullpath as a fallback
|
||||
put_error(qs->active_window, "Cannot find error in '%s': %s",
|
||||
error_state.filename, strerror(errno));
|
||||
if (stat(fullname, &sb) < 0) {
|
||||
put_error(s, "Cannot locate file '%s': %s",
|
||||
fullname, strerror(errno));
|
||||
return;
|
||||
} else
|
||||
if (!S_ISREG(sb.st_mode)) {
|
||||
put_error(qs->active_window, "Cannot find error in '%s': %s",
|
||||
error_state.filename, "not a regular file");
|
||||
put_error(s, "Cannot load '%s': not a regular file", fullname);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -4134,15 +4142,15 @@ static void do_next_error(EditState *s, int arg, int dir)
|
|||
if (e) {
|
||||
/* update error window to show the error at top */
|
||||
e->interactive = 0;
|
||||
e->offset_top = e->offset = error_state.offset;
|
||||
e->offset_top = e->offset = sep->offset;
|
||||
if (qs->shell_buffer_read_only)
|
||||
b->flags |= BF_READONLY;
|
||||
}
|
||||
|
||||
/* set and update active window */
|
||||
qs->active_window = s;
|
||||
do_find_file(s, error_state.filename, 0);
|
||||
do_goto_line(s, error_state.line_num, error_state.col_num);
|
||||
do_find_file(s, fullname, 0);
|
||||
do_goto_line(s, sep->line_num, sep->col_num);
|
||||
do_center_cursor(s, 1);
|
||||
|
||||
#if 0 // conflicts with muscle memory (chqrlie)
|
||||
|
|
@ -4151,7 +4159,7 @@ static void do_next_error(EditState *s, int arg, int dir)
|
|||
qe_register_transient_binding(qs, "previous-error", "M-p");
|
||||
}
|
||||
#endif
|
||||
put_status(s, "=> %s", error_state.message);
|
||||
put_status(s, "=> %s", sep->message);
|
||||
}
|
||||
|
||||
static int match_digits(const char32_t *buf, int n, char32_t sep) {
|
||||
|
|
|
|||
13
qe-manual.md
13
qe-manual.md
|
|
@ -2038,6 +2038,13 @@ Start a directory enumeration.
|
|||
* argument `pattern` a file pattern using `?` and `*` with the classic
|
||||
semantics used by unix shells
|
||||
|
||||
* argument `flags` a combination of options:
|
||||
- `FF_PATH`: the `path` argument is a list of directories
|
||||
- `FF_NODIR`: do not match directory names
|
||||
- `FF_NOXXDIR`: do not match `.` or `..`
|
||||
- `FF_ONLYDIR`: do not match non directories
|
||||
- 0 ... 15: maximum subdirectory depth for recursive matching
|
||||
|
||||
Return a pointer to an opaque FindFileState structure.
|
||||
|
||||
### `int from_hex(int c);`
|
||||
|
|
@ -2461,17 +2468,17 @@ and insertion sort for small chunks. The GNU lib C on linux also
|
|||
has a function `qsort_r()` with similar semantics but a different
|
||||
calling convention.
|
||||
|
||||
### `int qe_shell_match(const char *string, const char *pattern);`
|
||||
### `int qe_shell_match(const char *str, const char *pattern);`
|
||||
|
||||
Test whether a filename or pathname matches a shell-style pattern
|
||||
|
||||
* argument `string` a valid pointer to a string representing a
|
||||
* argument `str` a valid pointer to a string representing a
|
||||
filename
|
||||
|
||||
* argument `pattern` a valid pointer to a file pattern using `?`
|
||||
and `*` with the classic semantics used by unix shells
|
||||
|
||||
Return non zero if `string` matches `pattern`.
|
||||
Return non zero if `str` matches `pattern`.
|
||||
|
||||
Note: this function is a simplified version of POSIX function
|
||||
`fnmatch` defined in header `<fnmatch.h>`
|
||||
|
|
|
|||
2
qe.c
2
qe.c
|
|
@ -9078,7 +9078,7 @@ void qe_kill_buffer(QEmacsState *qs, EditBuffer *b)
|
|||
}
|
||||
|
||||
/* return TRUE if absolute path. works for files and URLs */
|
||||
static int is_abs_path(const char *path)
|
||||
int is_abs_path(const char *path)
|
||||
{
|
||||
size_t prefix;
|
||||
|
||||
|
|
|
|||
1
qe.h
1
qe.h
|
|
@ -134,6 +134,7 @@ struct CompleteState {
|
|||
char current[MAX_FILENAME_SIZE];
|
||||
};
|
||||
|
||||
int is_abs_path(const char *path);
|
||||
void canonicalize_absolute_path(EditState *s, char *buf, int buf_size, const char *path1);
|
||||
void canonicalize_absolute_buffer_path(EditBuffer *b, int offset,
|
||||
char *buf, int buf_size,
|
||||
|
|
|
|||
74
util.c
74
util.c
|
|
@ -35,14 +35,14 @@
|
|||
#include "config.h" /* for CONFIG_WIN32 */
|
||||
#include "util.h"
|
||||
|
||||
int qe_shell_match(const char *string, const char *pattern) {
|
||||
int qe_shell_match(const char *str, const char *pattern) {
|
||||
/*@API utils
|
||||
Test whether a filename or pathname matches a shell-style pattern
|
||||
@argument `string` a valid pointer to a string representing a
|
||||
@argument `str` a valid pointer to a string representing a
|
||||
filename
|
||||
@argument `pattern` a valid pointer to a file pattern using `?`
|
||||
and `*` with the classic semantics used by unix shells
|
||||
@return non zero if `string` matches `pattern`.
|
||||
@return non zero if `str` matches `pattern`.
|
||||
@note this function is a simplified version of POSIX function
|
||||
`fnmatch` defined in header `<fnmatch.h>`
|
||||
*/
|
||||
|
|
@ -51,22 +51,22 @@ int qe_shell_match(const char *string, const char *pattern) {
|
|||
if (c == '*') {
|
||||
if (*pattern == '\0')
|
||||
return 1;
|
||||
while (*string) {
|
||||
if (qe_shell_match(string, pattern))
|
||||
while (*str) {
|
||||
if (qe_shell_match(str, pattern))
|
||||
return 1;
|
||||
string++;
|
||||
str++;
|
||||
}
|
||||
return 0;
|
||||
} else
|
||||
if (c == '?') {
|
||||
if (*string++ == '\0')
|
||||
if (*str++ == '\0')
|
||||
return 0;
|
||||
} else
|
||||
if (c != *string++) {
|
||||
if (c != *str++) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return *string == '\0';
|
||||
return *str == '\0';
|
||||
}
|
||||
|
||||
#define MAX_FILENAME_SIZE 1024 /* Size for a filename buffer */
|
||||
|
|
@ -78,6 +78,7 @@ struct FindFileState {
|
|||
const char *bufptr;
|
||||
int flags;
|
||||
int depth;
|
||||
size_t dirlen;
|
||||
DIR *dir;
|
||||
DIR *parent_dir[FF_DEPTH];
|
||||
size_t parent_len[FF_DEPTH];
|
||||
|
|
@ -89,19 +90,26 @@ FindFileState *find_file_open(const char *path, const char *pattern, int flags)
|
|||
@argument `path` the initial directory for the enumeration.
|
||||
@argument `pattern` a file pattern using `?` and `*` with the classic
|
||||
semantics used by unix shells
|
||||
@argument `flags` a combination of options:
|
||||
- `FF_PATH`: the `path` argument is a list of directories
|
||||
- `FF_NODIR`: do not match directory names
|
||||
- `FF_NOXXDIR`: do not match `.` or `..`
|
||||
- `FF_ONLYDIR`: do not match non directories
|
||||
- 0 ... 15: maximum subdirectory depth for recursive matching
|
||||
@return a pointer to an opaque FindFileState structure.
|
||||
*/
|
||||
// XXX: should check if pattern has wildcards
|
||||
FindFileState *s;
|
||||
|
||||
s = qe_mallocz(FindFileState);
|
||||
if (!s)
|
||||
return NULL;
|
||||
pstrcpy(s->path, sizeof(s->path), path);
|
||||
pstrcpy(s->pattern, sizeof(s->pattern), pattern);
|
||||
s->bufptr = s->path;
|
||||
s->dir = NULL;
|
||||
s->flags = flags;
|
||||
if (s) {
|
||||
pstrcpy(s->path, sizeof(s->path), path);
|
||||
pstrcpy(s->pattern, sizeof(s->pattern), pattern);
|
||||
s->bufptr = s->path;
|
||||
s->dir = NULL;
|
||||
s->flags = flags;
|
||||
if (!s->pattern[strcspn(s->pattern, "*?")])
|
||||
s->flags |= FF_NOPAT;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +121,7 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) {
|
|||
array in bytes.
|
||||
@return `0` if there is a match, `-1` if no more files match the pattern.
|
||||
*/
|
||||
// XXX: should match wildcards in directory names
|
||||
struct stat st;
|
||||
struct dirent *dirent;
|
||||
const char *p;
|
||||
|
||||
|
|
@ -127,7 +135,8 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) {
|
|||
s->depth--;
|
||||
s->dir = s->parent_dir[s->depth];
|
||||
s->parent_dir[s->depth] = NULL;
|
||||
s->dirpath[s->parent_len[s->depth]] = '\0';
|
||||
s->dirlen = s->parent_len[s->depth];
|
||||
s->dirpath[s->dirlen] = '\0';
|
||||
continue;
|
||||
}
|
||||
p = s->bufptr;
|
||||
|
|
@ -137,10 +146,25 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) {
|
|||
p += strcspn(p, ":");
|
||||
else
|
||||
p += strlen(p);
|
||||
pstrncpy(s->dirpath, sizeof(s->dirpath), s->bufptr, p - s->bufptr);
|
||||
pstrncpy(s->dirpath, countof(s->dirpath), s->bufptr, p - s->bufptr);
|
||||
if (*p == ':')
|
||||
p++;
|
||||
s->bufptr = p;
|
||||
new_dir:
|
||||
s->dirlen = strlen(s->dirpath);
|
||||
if (s->flags & FF_NOPAT) {
|
||||
makepath(s->dirpath, countof(s->dirpath), s->dirpath, s->pattern);
|
||||
if (!stat(s->dirpath, &st)) {
|
||||
if ((S_ISDIR(st.st_mode) && !(s->flags & FF_NODIR))
|
||||
|| (S_ISREG(st.st_mode) && !(s->flags & FF_ONLYDIR))) {
|
||||
pstrcpy(filename, filename_size_max, s->dirpath);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
s->dirpath[s->dirlen] = '\0';
|
||||
if (!(s->flags & FF_DEPTH))
|
||||
continue;
|
||||
}
|
||||
s->dir = opendir(s->dirpath);
|
||||
} else {
|
||||
int isdir = 0;
|
||||
|
|
@ -160,11 +184,11 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) {
|
|||
} else {
|
||||
if (s->depth < (s->flags & FF_DEPTH)) {
|
||||
s->parent_dir[s->depth] = s->dir;
|
||||
s->parent_len[s->depth] = strlen(s->dirpath);
|
||||
s->parent_len[s->depth] = s->dirlen;
|
||||
s->depth++;
|
||||
makepath(s->dirpath, sizeof(s->dirpath), s->dirpath, dirent->d_name);
|
||||
s->dir = opendir(s->dirpath);
|
||||
continue;
|
||||
s->dir = NULL;
|
||||
makepath(s->dirpath, countof(s->dirpath), s->dirpath, dirent->d_name);
|
||||
goto new_dir;
|
||||
}
|
||||
}
|
||||
if (s->flags & FF_NODIR)
|
||||
|
|
@ -173,6 +197,8 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) {
|
|||
if (s->flags & FF_ONLYDIR)
|
||||
continue;
|
||||
}
|
||||
if (s->flags & FF_NOPAT)
|
||||
continue;
|
||||
if (qe_shell_match(dirent->d_name, s->pattern)) {
|
||||
makepath(filename, filename_size_max,
|
||||
s->dirpath, dirent->d_name);
|
||||
|
|
|
|||
1
util.h
1
util.h
|
|
@ -53,6 +53,7 @@ typedef struct FindFileState FindFileState;
|
|||
#define FF_NODIR 0x020 /* only match regular files */
|
||||
#define FF_NOXXDIR 0x040 /* do not match . or .. */
|
||||
#define FF_ONLYDIR 0x080 /* do not match non directories */
|
||||
#define FF_NOPAT 0x100 /* do not use shell matching */
|
||||
#define FF_DEPTH 0x00f /* max recursion depth */
|
||||
|
||||
FindFileState *find_file_open(const char *path, const char *pattern, int flags);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue