vim-patch:partial:8.1.0914: code related to findfile() is spread out (#30000)

Problem:    Code related to findfile() is spread out.
Solution:   Put findfile() related code into a new source file. (Yegappan
            Lakshmanan, closes vim/vim#3934)

5fd0f5052f

Keep functions related to wildcard expansion in path.c, as in Vim they
are now spread out among multiple files, which isn't really ideal.
This commit is contained in:
zeertzjq 2024-08-07 09:12:33 +08:00 committed by GitHub
parent 0c99ce0e89
commit 11a6f3c930
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 238 additions and 236 deletions

View File

@ -52,7 +52,9 @@
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
@ -63,6 +65,7 @@
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/option_vars.h"
#include "nvim/os/fs.h"
@ -1503,6 +1506,225 @@ theend:
return file_name;
}
/// Get the file name at the cursor.
/// If Visual mode is active, use the selected text if it's in one line.
/// Returns the name in allocated memory, NULL for failure.
char *grab_file_name(int count, linenr_T *file_lnum)
{
int options = FNAME_MESS | FNAME_EXP | FNAME_REL | FNAME_UNESC;
if (VIsual_active) {
size_t len;
char *ptr;
if (get_visual_text(NULL, &ptr, &len) == FAIL) {
return NULL;
}
// Only recognize ":123" here
if (file_lnum != NULL && ptr[len] == ':' && isdigit((uint8_t)ptr[len + 1])) {
char *p = ptr + len + 1;
*file_lnum = getdigits_int32(&p, false, 0);
}
return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
}
return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
}
/// Return the file name under or after the cursor.
///
/// The 'path' option is searched if the file name is not absolute.
/// The string returned has been alloc'ed and should be freed by the caller.
/// NULL is returned if the file name or file is not found.
///
/// options:
/// FNAME_MESS give error messages
/// FNAME_EXP expand to path
/// FNAME_HYP check for hypertext link
/// FNAME_INCL apply "includeexpr"
char *file_name_at_cursor(int options, int count, linenr_T *file_lnum)
{
return file_name_in_line(get_cursor_line_ptr(),
curwin->w_cursor.col, options, count, curbuf->b_ffname,
file_lnum);
}
/// @param rel_fname file we are searching relative to
/// @param file_lnum line number after the file name
///
/// @return the name of the file under or after ptr[col].
///
/// Otherwise like file_name_at_cursor().
char *file_name_in_line(char *line, int col, int options, int count, char *rel_fname,
linenr_T *file_lnum)
{
// search forward for what could be the start of a file name
char *ptr = line + col;
while (*ptr != NUL && !vim_isfilec((uint8_t)(*ptr))) {
MB_PTR_ADV(ptr);
}
if (*ptr == NUL) { // nothing found
if (options & FNAME_MESS) {
emsg(_("E446: No file name under cursor"));
}
return NULL;
}
size_t len;
bool in_type = true;
bool is_url = false;
// Search backward for first char of the file name.
// Go one char back to ":" before "//", or to the drive letter before ":\" (even if ":"
// is not in 'isfname').
while (ptr > line) {
if ((len = (size_t)(utf_head_off(line, ptr - 1))) > 0) {
ptr -= len + 1;
} else if (vim_isfilec((uint8_t)ptr[-1]) || ((options & FNAME_HYP) && path_is_url(ptr - 1))) {
ptr--;
} else {
break;
}
}
// Search forward for the last char of the file name.
// Also allow ":/" when ':' is not in 'isfname'.
len = path_has_drive_letter(ptr) ? 2 : 0;
while (vim_isfilec((uint8_t)ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
|| ((options & FNAME_HYP) && path_is_url(ptr + len))
|| (is_url && vim_strchr(":?&=", (uint8_t)ptr[len]) != NULL)) {
// After type:// we also include :, ?, & and = as valid characters, so that
// http://google.com:8080?q=this&that=ok works.
if ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z')) {
if (in_type && path_is_url(ptr + len + 1)) {
is_url = true;
}
} else {
in_type = false;
}
if (ptr[len] == '\\' && ptr[len + 1] == ' ') {
// Skip over the "\" in "\ ".
len++;
}
len += (size_t)(utfc_ptr2len(ptr + len));
}
// If there is trailing punctuation, remove it.
// But don't remove "..", could be a directory name.
if (len > 2 && vim_strchr(".,:;!", (uint8_t)ptr[len - 1]) != NULL
&& ptr[len - 2] != '.') {
len--;
}
if (file_lnum != NULL) {
const char *line_english = " line ";
const char *line_transl = _(line_msg);
// Get the number after the file name and a separator character.
// Also accept " line 999" with and without the same translation as
// used in last_set_msg().
char *p = ptr + len;
if (strncmp(p, line_english, strlen(line_english)) == 0) {
p += strlen(line_english);
} else if (strncmp(p, line_transl, strlen(line_transl)) == 0) {
p += strlen(line_transl);
} else {
p = skipwhite(p);
}
if (*p != NUL) {
if (!isdigit((uint8_t)(*p))) {
p++; // skip the separator
}
p = skipwhite(p);
if (isdigit((uint8_t)(*p))) {
*file_lnum = (linenr_T)getdigits_long(&p, false, 0);
}
}
}
return find_file_name_in_path(ptr, len, options, count, rel_fname);
}
static char *eval_includeexpr(const char *const ptr, const size_t len)
{
const sctx_T save_sctx = current_sctx;
set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len);
current_sctx = curbuf->b_p_script_ctx[BV_INEX].script_ctx;
char *res = eval_to_string_safe(curbuf->b_p_inex,
was_set_insecurely(curwin, kOptIncludeexpr, OPT_LOCAL),
true);
set_vim_var_string(VV_FNAME, NULL, 0);
current_sctx = save_sctx;
return res;
}
/// Return the name of the file ptr[len] in 'path'.
/// Otherwise like file_name_at_cursor().
///
/// @param rel_fname file we are searching relative to
char *find_file_name_in_path(char *ptr, size_t len, int options, long count, char *rel_fname)
{
char *file_name;
char *tofree = NULL;
if (len == 0) {
return NULL;
}
if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
tofree = eval_includeexpr(ptr, len);
if (tofree != NULL) {
ptr = tofree;
len = strlen(ptr);
}
}
if (options & FNAME_EXP) {
char *file_to_find = NULL;
char *search_ctx = NULL;
file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
true, rel_fname, &file_to_find, &search_ctx);
// If the file could not be found in a normal way, try applying
// 'includeexpr' (unless done already).
if (file_name == NULL
&& !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
tofree = eval_includeexpr(ptr, len);
if (tofree != NULL) {
ptr = tofree;
len = strlen(ptr);
file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
true, rel_fname, &file_to_find, &search_ctx);
}
}
if (file_name == NULL && (options & FNAME_MESS)) {
char c = ptr[len];
ptr[len] = NUL;
semsg(_("E447: Can't find file \"%s\" in path"), ptr);
ptr[len] = c;
}
// Repeat finding the file "count" times. This matters when it
// appears several times in the path.
while (file_name != NULL && --count > 0) {
xfree(file_name);
file_name = find_file_in_path(ptr, len, options, false, rel_fname,
&file_to_find, &search_ctx);
}
xfree(file_to_find);
vim_findfile_cleanup(search_ctx);
} else {
file_name = xstrnsave(ptr, len);
}
xfree(tofree);
return file_name;
}
void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause, bool pre)
{
static bool recursive = false;

View File

@ -2,6 +2,7 @@
#include <stddef.h> // IWYU pragma: keep
#include "nvim/pos_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
#include "nvim/vim_defs.h" // IWYU pragma: keep
@ -12,6 +13,17 @@ enum {
FINDFILE_BOTH = 2, ///< files and directories
};
/// Values for file_name_in_line()
enum {
FNAME_MESS = 1, ///< give error message
FNAME_EXP = 2, ///< expand to path
FNAME_HYP = 4, ///< check for hypertext link
FNAME_INCL = 8, ///< apply 'includeexpr'
FNAME_REL = 16, ///< ".." and "./" are relative to the (current)
///< file instead of the current directory
FNAME_UNESC = 32, ///< remove backslashes used for escaping
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "file_search.h.generated.h"
#endif

View File

@ -35,6 +35,7 @@
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"

View File

@ -31,6 +31,7 @@
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/file_search.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/garray_defs.h"

View File

@ -8,23 +8,17 @@
#include "auto/config.h"
#include "nvim/ascii_defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/eval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
#include "nvim/os/fs.h"
#include "nvim/os/fs_defs.h"
@ -37,7 +31,6 @@
#include "nvim/regexp_defs.h"
#include "nvim/strings.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
enum {
URL_SLASH = 1, // path_is_url() has found ":/"
@ -1687,87 +1680,6 @@ void simplify_filename(char *filename)
} while (*p != NUL);
}
static char *eval_includeexpr(const char *const ptr, const size_t len)
{
const sctx_T save_sctx = current_sctx;
set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len);
current_sctx = curbuf->b_p_script_ctx[BV_INEX].script_ctx;
char *res = eval_to_string_safe(curbuf->b_p_inex,
was_set_insecurely(curwin, kOptIncludeexpr, OPT_LOCAL),
true);
set_vim_var_string(VV_FNAME, NULL, 0);
current_sctx = save_sctx;
return res;
}
/// Return the name of the file ptr[len] in 'path'.
/// Otherwise like file_name_at_cursor().
///
/// @param rel_fname file we are searching relative to
char *find_file_name_in_path(char *ptr, size_t len, int options, long count, char *rel_fname)
{
char *file_name;
char *tofree = NULL;
if (len == 0) {
return NULL;
}
if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
tofree = eval_includeexpr(ptr, len);
if (tofree != NULL) {
ptr = tofree;
len = strlen(ptr);
}
}
if (options & FNAME_EXP) {
char *file_to_find = NULL;
char *search_ctx = NULL;
file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
true, rel_fname, &file_to_find, &search_ctx);
// If the file could not be found in a normal way, try applying
// 'includeexpr' (unless done already).
if (file_name == NULL
&& !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL) {
tofree = eval_includeexpr(ptr, len);
if (tofree != NULL) {
ptr = tofree;
len = strlen(ptr);
file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
true, rel_fname, &file_to_find, &search_ctx);
}
}
if (file_name == NULL && (options & FNAME_MESS)) {
char c = ptr[len];
ptr[len] = NUL;
semsg(_("E447: Can't find file \"%s\" in path"), ptr);
ptr[len] = c;
}
// Repeat finding the file "count" times. This matters when it
// appears several times in the path.
while (file_name != NULL && --count > 0) {
xfree(file_name);
file_name = find_file_in_path(ptr, len, options, false, rel_fname,
&file_to_find, &search_ctx);
}
xfree(file_to_find);
vim_findfile_cleanup(search_ctx);
} else {
file_name = xstrnsave(ptr, len);
}
xfree(tofree);
return file_name;
}
/// Checks for a Windows drive letter ("C:/") at the start of the path.
///
/// @see https://url.spec.whatwg.org/#start-with-a-windows-drive-letter

View File

@ -26,6 +26,7 @@
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"

View File

@ -6837,142 +6837,6 @@ static void frame_add_height(frame_T *frp, int n)
}
}
// Get the file name at the cursor.
// If Visual mode is active, use the selected text if it's in one line.
// Returns the name in allocated memory, NULL for failure.
char *grab_file_name(int count, linenr_T *file_lnum)
{
int options = FNAME_MESS | FNAME_EXP | FNAME_REL | FNAME_UNESC;
if (VIsual_active) {
size_t len;
char *ptr;
if (get_visual_text(NULL, &ptr, &len) == FAIL) {
return NULL;
}
// Only recognize ":123" here
if (file_lnum != NULL && ptr[len] == ':' && isdigit((uint8_t)ptr[len + 1])) {
char *p = ptr + len + 1;
*file_lnum = getdigits_int32(&p, false, 0);
}
return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
}
return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
}
// Return the file name under or after the cursor.
//
// The 'path' option is searched if the file name is not absolute.
// The string returned has been alloc'ed and should be freed by the caller.
// NULL is returned if the file name or file is not found.
//
// options:
// FNAME_MESS give error messages
// FNAME_EXP expand to path
// FNAME_HYP check for hypertext link
// FNAME_INCL apply "includeexpr"
char *file_name_at_cursor(int options, int count, linenr_T *file_lnum)
{
return file_name_in_line(get_cursor_line_ptr(),
curwin->w_cursor.col, options, count, curbuf->b_ffname,
file_lnum);
}
/// @param rel_fname file we are searching relative to
/// @param file_lnum line number after the file name
///
/// @return the name of the file under or after ptr[col]. Otherwise like file_name_at_cursor().
char *file_name_in_line(char *line, int col, int options, int count, char *rel_fname,
linenr_T *file_lnum)
{
// search forward for what could be the start of a file name
char *ptr = line + col;
while (*ptr != NUL && !vim_isfilec((uint8_t)(*ptr))) {
MB_PTR_ADV(ptr);
}
if (*ptr == NUL) { // nothing found
if (options & FNAME_MESS) {
emsg(_("E446: No file name under cursor"));
}
return NULL;
}
size_t len;
bool in_type = true;
bool is_url = false;
// Search backward for first char of the file name.
// Go one char back to ":" before "//", or to the drive letter before ":\" (even if ":"
// is not in 'isfname').
while (ptr > line) {
if ((len = (size_t)(utf_head_off(line, ptr - 1))) > 0) {
ptr -= len + 1;
} else if (vim_isfilec((uint8_t)ptr[-1]) || ((options & FNAME_HYP) && path_is_url(ptr - 1))) {
ptr--;
} else {
break;
}
}
// Search forward for the last char of the file name.
// Also allow ":/" when ':' is not in 'isfname'.
len = path_has_drive_letter(ptr) ? 2 : 0;
while (vim_isfilec((uint8_t)ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
|| ((options & FNAME_HYP) && path_is_url(ptr + len))
|| (is_url && vim_strchr(":?&=", (uint8_t)ptr[len]) != NULL)) {
// After type:// we also include :, ?, & and = as valid characters, so that
// http://google.com:8080?q=this&that=ok works.
if ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z')) {
if (in_type && path_is_url(ptr + len + 1)) {
is_url = true;
}
} else {
in_type = false;
}
if (ptr[len] == '\\' && ptr[len + 1] == ' ') {
// Skip over the "\" in "\ ".
len++;
}
len += (size_t)(utfc_ptr2len(ptr + len));
}
// If there is trailing punctuation, remove it.
// But don't remove "..", could be a directory name.
if (len > 2 && vim_strchr(".,:;!", (uint8_t)ptr[len - 1]) != NULL
&& ptr[len - 2] != '.') {
len--;
}
if (file_lnum != NULL) {
const char *line_english = " line ";
const char *line_transl = _(line_msg);
// Get the number after the file name and a separator character.
// Also accept " line 999" with and without the same translation as
// used in last_set_msg().
char *p = ptr + len;
if (strncmp(p, line_english, strlen(line_english)) == 0) {
p += strlen(line_english);
} else if (strncmp(p, line_transl, strlen(line_transl)) == 0) {
p += strlen(line_transl);
} else {
p = skipwhite(p);
}
if (*p != NUL) {
if (!isdigit((uint8_t)(*p))) {
p++; // skip the separator
}
p = skipwhite(p);
if (isdigit((uint8_t)(*p))) {
*file_lnum = (linenr_T)getdigits_long(&p, false, 0);
}
}
}
return find_file_name_in_path(ptr, len, options, count, rel_fname);
}
/// Add or remove a status line from window(s), according to the
/// value of 'laststatus'.
///

View File

@ -8,17 +8,6 @@
#include "nvim/option_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
/// Values for file_name_in_line()
enum {
FNAME_MESS = 1, ///< give error message
FNAME_EXP = 2, ///< expand to path
FNAME_HYP = 4, ///< check for hypertext link
FNAME_INCL = 8, ///< apply 'includeexpr'
FNAME_REL = 16, ///< ".." and "./" are relative to the (current)
///< file instead of the current directory
FNAME_UNESC = 32, ///< remove backslashes used for escaping
};
/// arguments for win_split()
enum {
WSP_ROOM = 0x01, ///< require enough room