#include <algorithm>
#include "linespec.h"
#include "cli/cli-decode.h"
+#include "gdbsupport/gdb_tilde_expand.h"
/* FIXME: This is needed because of lookup_cmd_1 (). We should be
calling a hook instead so we eliminate the CLI dependency. */
if (p[strlen (p) - 1] == '~')
continue;
+ /* Readline appends a trailing '/' if the completion is a
+ directory. If this completion request originated from outside
+ readline (e.g. GDB's 'complete' command), then we append the
+ trailing '/' ourselves now. */
+ if (!tracker.from_readline ())
+ {
+ std::string expanded = gdb_tilde_expand (p_rl.get ());
+ struct stat finfo;
+ const bool isdir = (stat (expanded.c_str (), &finfo) == 0
+ && S_ISDIR (finfo.st_mode));
+ if (isdir)
+ p_rl.reset (concat (p_rl.get (), "/", nullptr));
+ }
+
tracker.add_completion
(make_completion_match_str (std::move (p_rl), text, word));
}
/* See completer.h. */
-completion_tracker::completion_tracker ()
+completion_tracker::completion_tracker (bool from_readline)
+ : m_from_readline (from_readline)
{
discard_completions ();
}
completion_result
complete (const char *line, char const **word, int *quote_char)
{
- completion_tracker tracker_handle_brkchars;
- completion_tracker tracker_handle_completions;
+ completion_tracker tracker_handle_brkchars (false);
+ completion_tracker tracker_handle_completions (false);
completion_tracker *tracker;
/* The WORD should be set to the end of word to complete. We initialize
/* New completion starting. Get rid of the previous tracker and
start afresh. */
delete current_completion.tracker;
- current_completion.tracker = new completion_tracker ();
+ current_completion.tracker = new completion_tracker (true);
completion_tracker &tracker = *current_completion.tracker;
if (end == 0 || !current_completion.tracker->use_custom_word_point ())
{
delete current_completion.tracker;
- current_completion.tracker = new completion_tracker ();
+ current_completion.tracker = new completion_tracker (true);
complete_line (*current_completion.tracker, text,
rl_line_buffer, rl_point);
--- /dev/null
+# Copyright 2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Tests for filename completion. Create a directory tree on the test
+# machine and try completing filenames within the tree.
+
+load_lib completion-support.exp
+
+# Setup a directory tree in which completion tests can be run. The
+# structure is:
+#
+# root/ [ DIRECTORY ]
+# aaa/ [ DIRECTORY ]
+# bb1/ [ DIRECTORY ]
+# bb2/ [ DIRECTORY ]
+# cc1/ [ DIRECTORY ]
+# cc2 [ FILE ]
+proc setup_directory_tree {} {
+ set root [standard_output_file "root"]
+
+ remote_exec host "mkdir -p ${root}"
+ remote_exec host "mkdir -p ${root}/aaa"
+ remote_exec host "mkdir -p ${root}/bb1"
+ remote_exec host "mkdir -p ${root}/bb2"
+ remote_exec host "mkdir -p ${root}/cc1"
+ remote_exec host "touch ${root}/cc2"
+
+ return $root
+}
+
+# Run filename completetion tests. ROOT is the base directory as
+# returned from setup_directory_tree, though, if ROOT is a
+# sub-directory of the user's home directory ROOT might have been
+# modified to replace the $HOME prefix with a single "~" character.
+proc run_tests { root } {
+ test_gdb_complete_none "file ${root}/xx" \
+ "expand a non-existent filename"
+
+ test_gdb_complete_unique "file ${root}/a" \
+ "file ${root}/aaa/" "" false \
+ "expand a unique filename"
+
+ test_gdb_complete_multiple "file ${root}/" \
+ "b" "b" {
+ "bb1/"
+ "bb2/"
+ } "" "" false \
+ "expand multiple directory names"
+
+ test_gdb_complete_multiple "file ${root}/" \
+ "c" "c" {
+ "cc1/"
+ "cc2"
+ } "" "" false \
+ "expand mixed directory and file names"
+}
+
+gdb_start
+
+set root [setup_directory_tree]
+
+run_tests $root
+
+# This test relies on using the $HOME directory. We could make this
+# work for remote hosts, but right now, this isn't supported.
+if {![is_remote host]} {
+
+ # The users home directory.
+ set home $::env(HOME)
+
+ # Check if ROOT is within the $HOME directory. If it is then we can
+ # rerun the tests, but replacing the $HOME part with "~".
+ if { [string compare -length [string length $home] $root $home] == 0 } {
+ # Convert the $HOME prefix in to ~.
+ set tilde_root "~[string range $root [string length $home] end]"
+
+ with_test_prefix "with tilde" {
+ # And rerun the tests.
+ run_tests $tilde_root
+ }
+ }
+}
append completion_list_re [string_to_regexp $c]
append completion_list_re $ws
}
- append completion_list_re $ws
return $completion_list_re
}
# Test that completing LINE with TAB completes to nothing.
-proc test_gdb_complete_tab_none { line } {
+proc test_gdb_complete_tab_none { line { testname "" } } {
set line_re [string_to_regexp $line]
- set test "tab complete \"$line\""
+ if { $testname eq "" } {
+ set testname "tab complete \"$line\""
+ } else {
+ set testname "tab complete: $testname"
+ }
send_gdb "$line\t"
- gdb_test_multiple "" "$test" {
+ gdb_test_multiple "" "$testname" {
-re "^$line_re$completion::bell_re$" {
- pass "$test"
+ pass $gdb_test_name
}
-re "$line_re\[^ \]+ $" {
- fail "$test"
+ fail $gdb_test_name
}
}
- clear_input_line $test
+ clear_input_line $testname
}
# Test that completing INPUT_LINE with TAB completes to
# appended after EXPECTED_OUTPUT. Normally that's a whitespace, but
# in some cases it's some other character, like a colon.
-proc test_gdb_complete_tab_unique { input_line complete_line_re append_char_re } {
+proc test_gdb_complete_tab_unique { input_line complete_line_re append_char_re {testname ""} } {
- set test "tab complete \"$input_line\""
+ if { $testname eq "" } {
+ set testname "tab complete \"$input_line\""
+ } else {
+ set testname "tab complete: $testname"
+ }
send_gdb "$input_line\t"
set res 1
- gdb_test_multiple "" "$test" {
+ gdb_test_multiple "" "$testname" {
-re "^$complete_line_re$append_char_re$" {
- pass "$test"
+ pass $gdb_test_name
}
timeout {
- fail "$test (timeout)"
+ fail "$gdb_test_name (timeout)"
set res -1
}
}
- clear_input_line $test
+ clear_input_line $testname
return $res
}
# to hit the max-completions limit.
proc test_gdb_complete_tab_multiple { input_line add_completed_line \
- completion_list {max_completions 0}} {
+ completion_list {max_completions false} \
+ {testname ""} } {
global gdb_prompt
set input_line_re [string_to_regexp $input_line]
"\\*\\*\\* List may be truncated, max-completions reached\\. \\*\\*\\*"
}
- set test "tab complete \"$input_line\""
+ if { $testname eq "" } {
+ set testname "tab complete \"$input_line\""
+ } else {
+ set testname "tab complete: $testname"
+ }
send_gdb "$input_line\t"
- gdb_test_multiple "" "$test (first tab)" {
+ gdb_test_multiple "" "$testname (first tab)" {
-re "^${input_line_re}${completion::bell_re}$add_completed_line_re$" {
send_gdb "\t"
# If we auto-completed to an ambiguous prefix, we need an
} else {
set maybe_bell ""
}
- gdb_test_multiple "" "$test (second tab)" {
+ gdb_test_multiple "" "$testname (second tab)" {
-re "^${maybe_bell}\r\n$expected_re\r\n$gdb_prompt " {
- gdb_test_multiple "" "$test (second tab)" {
+ gdb_test_multiple "" "$testname (second tab)" {
-re "^$input_line_re$add_completed_line_re$" {
- pass "$test"
+ pass $gdb_test_name
}
}
}
-re "${maybe_bell}\r\n.+\r\n$gdb_prompt $" {
- fail "$test"
+ fail $gdb_test_name
}
}
}
}
- clear_input_line $test
+ clear_input_line $testname
}
# Test that completing LINE with the complete command completes to
# nothing.
-proc test_gdb_complete_cmd_none { line } {
- gdb_test_no_output "complete $line" "cmd complete \"$line\""
+proc test_gdb_complete_cmd_none { line { testname "" } } {
+ if { $testname eq "" } {
+ set testname "cmd complete \"$line\""
+ } else {
+ set testname "cmd complete: $testname"
+ }
+ gdb_test_no_output "complete $line" $testname
}
# Test that completing LINE with the complete command completes to
# COMPLETE_LINE_RE.
# Returns 1 if the test passed, 0 if it failed, -1 if it timed out.
-proc test_gdb_complete_cmd_unique { input_line complete_line_re } {
+proc test_gdb_complete_cmd_unique { input_line complete_line_re {testname ""} } {
global gdb_prompt
set res 0
set cmd "complete $input_line"
set cmd_re [string_to_regexp $cmd]
- set test "cmd complete \"$input_line\""
- gdb_test_multiple $cmd $test {
+ if { $testname eq "" } {
+ set testname "cmd complete \"$input_line\""
+ } else {
+ set testname "cmd complete: $testname"
+ }
+ gdb_test_multiple $cmd $testname {
-re "^$cmd_re\r\n$complete_line_re\r\n$gdb_prompt $" {
- pass $test
+ pass $gdb_test_name
set res 1
}
-re "$gdb_prompt $" {
- fail "$test"
+ fail $gdb_test_name
}
timeout {
- fail "$test (timeout)"
+ fail "$gdb_test_name (timeout)"
set res -1
}
}
# MAX_COMPLETIONS then we expect the completion to hit the
# max-completions limit.
-proc test_gdb_complete_cmd_multiple { cmd_prefix completion_word completion_list {start_quote_char ""} {end_quote_char ""} {max_completions 0}} {
+proc test_gdb_complete_cmd_multiple { cmd_prefix completion_word completion_list {start_quote_char ""} {end_quote_char ""} {max_completions false} {testname ""} } {
global gdb_prompt
set expected_re [make_cmd_completion_list_re $cmd_prefix $completion_list $start_quote_char $end_quote_char]
}
set cmd_re [string_to_regexp "complete $cmd_prefix$completion_word"]
- set test "cmd complete \"$cmd_prefix$completion_word\""
- gdb_test_multiple "complete $cmd_prefix$completion_word" $test {
+ if { $testname eq "" } {
+ set testname "cmd complete \"$cmd_prefix$completion_word\""
+ } else {
+ set testname "cmd complete: $testname"
+ }
+ gdb_test_multiple "complete $cmd_prefix$completion_word" $testname {
-re "^$cmd_re\r\n$expected_re$gdb_prompt $" {
- pass $test
+ pass $gdb_test_name
}
-re "$gdb_prompt $" {
- fail "$test"
+ fail $gdb_test_name
}
}
}
# Test that completing LINE completes to nothing.
-proc test_gdb_complete_none { input_line } {
+proc test_gdb_complete_none { input_line { testname "" } } {
if { [readline_is_used] } {
- test_gdb_complete_tab_none $input_line
+ test_gdb_complete_tab_none $input_line $testname
}
- test_gdb_complete_cmd_none $input_line
+ test_gdb_complete_cmd_none $input_line $testname
}
# Test that completing INPUT_LINE completes to COMPLETE_LINE_RE.
# of a regular expression (as COMPLETE_LINE_RE). See
# test_gdb_complete_unique below.
-proc test_gdb_complete_unique_re { input_line complete_line_re {append_char " "} {max_completions 0}} {
+proc test_gdb_complete_unique_re { input_line complete_line_re {append_char " "} {max_completions false} {testname ""} } {
set append_char_re [string_to_regexp $append_char]
# Trim COMPLETE LINE, for the case we're completing a command with leading
# speeds up testing if necessary.
set test_result [test_gdb_complete_cmd_unique $input_line\
- $expected_output_re]
+ $expected_output_re $testname]
if { $test_result != 1 } {
return $test_result
}
if { [readline_is_used] } {
set test_result [test_gdb_complete_tab_unique $input_line \
- $complete_line_re $append_char_re]
+ $complete_line_re $append_char_re $testname]
}
return $test_result
}
# Like TEST_GDB_COMPLETE_UNIQUE_RE, but COMPLETE_LINE is a string, not
# a regular expression.
-proc test_gdb_complete_unique { input_line complete_line {append_char " "} {max_completions 0}} {
+proc test_gdb_complete_unique { input_line complete_line {append_char " "} {max_completions false} {testname ""} } {
set complete_line_re [string_to_regexp $complete_line]
- test_gdb_complete_unique_re $input_line $complete_line_re $append_char $max_completions
+ test_gdb_complete_unique_re $input_line $complete_line_re $append_char $max_completions $testname
}
# Test that completing "CMD_PREFIX + COMPLETION_WORD" adds
proc test_gdb_complete_multiple {
cmd_prefix completion_word add_completed_line completion_list
- {start_quote_char ""} {end_quote_char ""} {max_completions 0}
+ {start_quote_char ""} {end_quote_char ""} {max_completions false}
+ {testname ""}
} {
if { [readline_is_used] } {
- test_gdb_complete_tab_multiple "$cmd_prefix$completion_word" $add_completed_line $completion_list $max_completions
+ test_gdb_complete_tab_multiple "$cmd_prefix$completion_word" $add_completed_line $completion_list $max_completions $testname
}
- test_gdb_complete_cmd_multiple $cmd_prefix $completion_word $completion_list $start_quote_char $end_quote_char $max_completions
+ test_gdb_complete_cmd_multiple $cmd_prefix $completion_word $completion_list $start_quote_char $end_quote_char $max_completions $testname
}
# Test that all the substring prefixes of INPUT from [0..START) to