mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 21:03:12 +00:00
Remove the indentation part of parser_t::test(). Rename it to
detect_errors().
This commit is contained in:
parent
67b1f14a6f
commit
925fe65dd8
5 changed files with 23 additions and 125 deletions
|
@ -497,14 +497,14 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
|
||||||
{
|
{
|
||||||
if (condition && wcslen(condition))
|
if (condition && wcslen(condition))
|
||||||
{
|
{
|
||||||
if (parser.test(condition))
|
if (parser.detect_errors(condition))
|
||||||
{
|
{
|
||||||
append_format(stderr_buffer,
|
append_format(stderr_buffer,
|
||||||
L"%ls: Condition '%ls' contained a syntax error\n",
|
L"%ls: Condition '%ls' contained a syntax error\n",
|
||||||
argv[0],
|
argv[0],
|
||||||
condition);
|
condition);
|
||||||
|
|
||||||
parser.test(condition, NULL, &stderr_buffer, argv[0]);
|
parser.detect_errors(condition, &stderr_buffer, argv[0]);
|
||||||
|
|
||||||
res = true;
|
res = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -585,52 +585,52 @@ static void test_parser()
|
||||||
parser_t parser(PARSER_TYPE_GENERAL, true);
|
parser_t parser(PARSER_TYPE_GENERAL, true);
|
||||||
|
|
||||||
say(L"Testing null input to parser");
|
say(L"Testing null input to parser");
|
||||||
if (!parser.test(NULL))
|
if (!parser.detect_errors(NULL))
|
||||||
{
|
{
|
||||||
err(L"Null input to parser.test undetected");
|
err(L"Null input to parser.detect_errors undetected");
|
||||||
}
|
}
|
||||||
|
|
||||||
say(L"Testing block nesting");
|
say(L"Testing block nesting");
|
||||||
if (!parser.test(L"if; end"))
|
if (!parser.detect_errors(L"if; end"))
|
||||||
{
|
{
|
||||||
err(L"Incomplete if statement undetected");
|
err(L"Incomplete if statement undetected");
|
||||||
}
|
}
|
||||||
if (!parser.test(L"if test; echo"))
|
if (!parser.detect_errors(L"if test; echo"))
|
||||||
{
|
{
|
||||||
err(L"Missing end undetected");
|
err(L"Missing end undetected");
|
||||||
}
|
}
|
||||||
if (!parser.test(L"if test; end; end"))
|
if (!parser.detect_errors(L"if test; end; end"))
|
||||||
{
|
{
|
||||||
err(L"Unbalanced end undetected");
|
err(L"Unbalanced end undetected");
|
||||||
}
|
}
|
||||||
|
|
||||||
say(L"Testing detection of invalid use of builtin commands");
|
say(L"Testing detection of invalid use of builtin commands");
|
||||||
if (!parser.test(L"case foo"))
|
if (!parser.detect_errors(L"case foo"))
|
||||||
{
|
{
|
||||||
err(L"'case' command outside of block context undetected");
|
err(L"'case' command outside of block context undetected");
|
||||||
}
|
}
|
||||||
if (!parser.test(L"switch ggg; if true; case foo;end;end"))
|
if (!parser.detect_errors(L"switch ggg; if true; case foo;end;end"))
|
||||||
{
|
{
|
||||||
err(L"'case' command outside of switch block context undetected");
|
err(L"'case' command outside of switch block context undetected");
|
||||||
}
|
}
|
||||||
if (!parser.test(L"else"))
|
if (!parser.detect_errors(L"else"))
|
||||||
{
|
{
|
||||||
err(L"'else' command outside of conditional block context undetected");
|
err(L"'else' command outside of conditional block context undetected");
|
||||||
}
|
}
|
||||||
if (!parser.test(L"else if"))
|
if (!parser.detect_errors(L"else if"))
|
||||||
{
|
{
|
||||||
err(L"'else if' command outside of conditional block context undetected");
|
err(L"'else if' command outside of conditional block context undetected");
|
||||||
}
|
}
|
||||||
if (!parser.test(L"if false; else if; end"))
|
if (!parser.detect_errors(L"if false; else if; end"))
|
||||||
{
|
{
|
||||||
err(L"'else if' missing command undetected");
|
err(L"'else if' missing command undetected");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parser.test(L"break"))
|
if (!parser.detect_errors(L"break"))
|
||||||
{
|
{
|
||||||
err(L"'break' command outside of loop block context undetected");
|
err(L"'break' command outside of loop block context undetected");
|
||||||
}
|
}
|
||||||
if (!parser.test(L"exec ls|less") || !parser.test(L"echo|return"))
|
if (!parser.detect_errors(L"exec ls|less") || !parser.detect_errors(L"echo|return"))
|
||||||
{
|
{
|
||||||
err(L"Invalid pipe command undetected");
|
err(L"Invalid pipe command undetected");
|
||||||
}
|
}
|
||||||
|
|
103
parser.cpp
103
parser.cpp
|
@ -2771,7 +2771,7 @@ int parser_t::parser_test_argument(const wchar_t *arg, wcstring *out, const wcha
|
||||||
|
|
||||||
// debug( 1, L"%ls -> %ls %ls", arg_cpy, subst, tmp.buff );
|
// debug( 1, L"%ls -> %ls %ls", arg_cpy, subst, tmp.buff );
|
||||||
|
|
||||||
err |= parser_t::test(subst, 0, out, prefix);
|
err |= parser_t::detect_errors(subst, out, prefix);
|
||||||
|
|
||||||
free(subst);
|
free(subst);
|
||||||
free(arg_cpy);
|
free(arg_cpy);
|
||||||
|
@ -2906,12 +2906,9 @@ struct block_info_t
|
||||||
{
|
{
|
||||||
int position; //tokenizer position
|
int position; //tokenizer position
|
||||||
block_type_t type; //type of the block
|
block_type_t type; //type of the block
|
||||||
int indentation; //indentation associated with the block
|
|
||||||
|
|
||||||
bool has_had_case; //if we are a switch, whether we've encountered a case
|
|
||||||
};
|
};
|
||||||
|
|
||||||
parser_test_error_bits_t parser_t::test(const wchar_t *buff, int *block_level, wcstring *out, const wchar_t *prefix)
|
parser_test_error_bits_t parser_t::detect_errors(const wchar_t *buff, wcstring *out, const wchar_t *prefix)
|
||||||
{
|
{
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
|
|
||||||
|
@ -2923,9 +2920,8 @@ parser_test_error_bits_t parser_t::test(const wchar_t *buff, int *block_level, w
|
||||||
int err=0;
|
int err=0;
|
||||||
int unfinished = 0;
|
int unfinished = 0;
|
||||||
|
|
||||||
// These are very nearly stacks, but sometimes we have to inspect non-top elements (e.g. return)
|
// This is very nearly a stack, but sometimes we have to inspect non-top elements (e.g. return)
|
||||||
std::vector<struct block_info_t> block_infos;
|
std::vector<struct block_info_t> block_infos;
|
||||||
int indentation_sum = 0; //sum of indentation in block_infos
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Set to 1 if the current command is inside a pipeline
|
Set to 1 if the current command is inside a pipeline
|
||||||
|
@ -2958,16 +2954,6 @@ parser_test_error_bits_t parser_t::test(const wchar_t *buff, int *block_level, w
|
||||||
|
|
||||||
CHECK(buff, 1);
|
CHECK(buff, 1);
|
||||||
|
|
||||||
if (block_level)
|
|
||||||
{
|
|
||||||
size_t len = wcslen(buff);
|
|
||||||
for (size_t i=0; i<len; i++)
|
|
||||||
{
|
|
||||||
block_level[i] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenizer_t tok(buff, 0);
|
tokenizer_t tok(buff, 0);
|
||||||
|
|
||||||
scoped_push<tokenizer_t*> tokenizer_push(¤t_tokenizer, &tok);
|
scoped_push<tokenizer_t*> tokenizer_push(¤t_tokenizer, &tok);
|
||||||
|
@ -3059,53 +3045,18 @@ parser_test_error_bits_t parser_t::test(const wchar_t *buff, int *block_level, w
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
indentation_sum -= block_infos.back().indentation;
|
|
||||||
block_infos.pop_back();
|
block_infos.pop_back();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Store the block level. This needs to be done
|
|
||||||
_after_ checking for end commands, but _before_
|
|
||||||
checking for block opening commands.
|
|
||||||
*/
|
|
||||||
if (block_level != NULL)
|
|
||||||
{
|
|
||||||
int indentation_adjust = 0;
|
|
||||||
if (command == L"else")
|
|
||||||
{
|
|
||||||
// if or else if goes back
|
|
||||||
indentation_adjust = -1;
|
|
||||||
}
|
|
||||||
else if (command == L"case")
|
|
||||||
{
|
|
||||||
if (! block_infos.empty() && block_infos.back().type == SWITCH)
|
|
||||||
{
|
|
||||||
// mark that we've encountered a case, and increase the indentation
|
|
||||||
// by doing this now, we avoid overly indenting the first case as the user types it
|
|
||||||
if (! block_infos.back().has_had_case)
|
|
||||||
{
|
|
||||||
block_infos.back().has_had_case = true;
|
|
||||||
block_infos.back().indentation += 1;
|
|
||||||
indentation_sum += 1;
|
|
||||||
}
|
|
||||||
// unindent this case
|
|
||||||
indentation_adjust = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
block_level[tok_get_pos(&tok)] = indentation_sum + indentation_adjust;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Handle block commands
|
Handle block commands
|
||||||
*/
|
*/
|
||||||
if (parser_keywords_is_block(command))
|
if (parser_keywords_is_block(command))
|
||||||
{
|
{
|
||||||
struct block_info_t info = {current_tokenizer_pos, parser_get_block_type(command), 1 /* indent */};
|
struct block_info_t info = {current_tokenizer_pos, parser_get_block_type(command)};
|
||||||
block_infos.push_back(info);
|
block_infos.push_back(info);
|
||||||
indentation_sum += info.indentation;
|
|
||||||
tok_next(&tok);
|
tok_next(&tok);
|
||||||
tok_set_pos(&tok, mark);
|
tok_set_pos(&tok, mark);
|
||||||
}
|
}
|
||||||
|
@ -3651,52 +3602,6 @@ parser_test_error_bits_t parser_t::test(const wchar_t *buff, int *block_level, w
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Fill in the unset block_level entries. Until now, only places
|
|
||||||
where the block level _changed_ have been filled out. This fills
|
|
||||||
in the rest.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (block_level)
|
|
||||||
{
|
|
||||||
int last_level = 0;
|
|
||||||
size_t i, len = wcslen(buff);
|
|
||||||
for (i=0; i<len; i++)
|
|
||||||
{
|
|
||||||
if (block_level[i] >= 0)
|
|
||||||
{
|
|
||||||
last_level = block_level[i];
|
|
||||||
/*
|
|
||||||
Make all whitespace before a token have the new
|
|
||||||
level. This avoid using the wrong indentation level
|
|
||||||
if a new line starts with whitespace.
|
|
||||||
*/
|
|
||||||
size_t prev_char_idx = i;
|
|
||||||
while (prev_char_idx--)
|
|
||||||
{
|
|
||||||
if (!wcschr(L" \n\t\r", buff[prev_char_idx]))
|
|
||||||
break;
|
|
||||||
block_level[prev_char_idx] = last_level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
block_level[i] = last_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Make all trailing whitespace have the block level that the
|
|
||||||
validator had at exit. This makes sure a new line is
|
|
||||||
correctly indented even if it is empty.
|
|
||||||
*/
|
|
||||||
int last_indent = block_infos.empty() ? 0 : block_infos.back().indentation;
|
|
||||||
size_t suffix_idx = len;
|
|
||||||
while (suffix_idx--)
|
|
||||||
{
|
|
||||||
if (!wcschr(L" \n\t\r", buff[suffix_idx]))
|
|
||||||
break;
|
|
||||||
block_level[suffix_idx] = last_indent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Calculate exit status
|
Calculate exit status
|
||||||
*/
|
*/
|
||||||
|
|
2
parser.h
2
parser.h
|
@ -487,7 +487,7 @@ public:
|
||||||
\param out if non-null, any errors in the command will be filled out into this buffer
|
\param out if non-null, any errors in the command will be filled out into this buffer
|
||||||
\param prefix the prefix string to prepend to each error message written to the \c out buffer
|
\param prefix the prefix string to prepend to each error message written to the \c out buffer
|
||||||
*/
|
*/
|
||||||
parser_test_error_bits_t test(const wchar_t * buff, int *block_level = NULL, wcstring *out = NULL, const wchar_t *prefix = NULL);
|
parser_test_error_bits_t detect_errors(const wchar_t * buff, wcstring *out = NULL, const wchar_t *prefix = NULL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Test if the specified string can be parsed as an argument list,
|
Test if the specified string can be parsed as an argument list,
|
||||||
|
|
15
reader.cpp
15
reader.cpp
|
@ -519,14 +519,7 @@ wcstring combine_command_and_autosuggestion(const wcstring &cmdline, const wcstr
|
||||||
static void reader_repaint()
|
static void reader_repaint()
|
||||||
{
|
{
|
||||||
// Update the indentation
|
// Update the indentation
|
||||||
if (0)
|
data->indents = parse_util_compute_indents(data->command_line);
|
||||||
{
|
|
||||||
parser_t::principal_parser().test(data->command_line.c_str(), &data->indents[0]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data->indents = parse_util_compute_indents(data->command_line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combine the command and autosuggestion into one string
|
// Combine the command and autosuggestion into one string
|
||||||
wcstring full_line = combine_command_and_autosuggestion(data->command_line, data->autosuggestion);
|
wcstring full_line = combine_command_and_autosuggestion(data->command_line, data->autosuggestion);
|
||||||
|
@ -2479,7 +2472,7 @@ void reader_run_command(parser_t &parser, const wcstring &cmd)
|
||||||
|
|
||||||
int reader_shell_test(const wchar_t *b)
|
int reader_shell_test(const wchar_t *b)
|
||||||
{
|
{
|
||||||
int res = parser_t::principal_parser().test(b);
|
int res = parser_t::principal_parser().detect_errors(b);
|
||||||
|
|
||||||
if (res & PARSER_TEST_ERROR)
|
if (res & PARSER_TEST_ERROR)
|
||||||
{
|
{
|
||||||
|
@ -2499,7 +2492,7 @@ int reader_shell_test(const wchar_t *b)
|
||||||
0);
|
0);
|
||||||
|
|
||||||
|
|
||||||
parser_t::principal_parser().test(b, NULL, &sb, L"fish");
|
parser_t::principal_parser().detect_errors(b, &sb, L"fish");
|
||||||
fwprintf(stderr, L"%ls", sb.c_str());
|
fwprintf(stderr, L"%ls", sb.c_str());
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
@ -3911,7 +3904,7 @@ static int read_ni(int fd, const io_chain_t &io)
|
||||||
}
|
}
|
||||||
|
|
||||||
wcstring sb;
|
wcstring sb;
|
||||||
if (! parser.test(str.c_str(), 0, &sb, L"fish"))
|
if (! parser.detect_errors(str.c_str(), &sb, L"fish"))
|
||||||
{
|
{
|
||||||
parser.eval(str, io, TOP);
|
parser.eval(str, io, TOP);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue