openMSX
Completer.cc
Go to the documentation of this file.
1#include "Completer.hh"
2#include "Interpreter.hh"
4#include "FileContext.hh"
5#include "FileOperations.hh"
6#include "foreach_file.hh"
7#include "ranges.hh"
8#include "stl.hh"
9#include "strCat.hh"
10#include "stringsp.hh"
11#include "TclObject.hh"
12#include "utf8_unchecked.hh"
13#include "xrange.hh"
14#include <array>
15
16using std::vector;
17using std::string;
18using std::string_view;
19
20namespace openmsx {
21
22static bool formatHelper(std::span<const string_view> input, size_t columnLimit,
23 vector<string>& result)
24{
25 size_t column = 0;
26 auto it = begin(input);
27 do {
28 size_t maxColumn = column;
29 for (size_t i = 0; (i < result.size()) && (it != end(input));
30 ++i, ++it) {
31 auto curSize = utf8::unchecked::size(result[i]);
32 strAppend(result[i], spaces(column - curSize), *it);
33 maxColumn = std::max(maxColumn,
34 utf8::unchecked::size(result[i]));
35 if (maxColumn > columnLimit) return false;
36 }
37 column = maxColumn + 2;
38 } while (it != end(input));
39 return true;
40}
41
42static vector<string> format(std::span<const string_view> input, size_t columnLimit)
43{
44 vector<string> result;
45 for (auto lines : xrange(1u, input.size())) {
46 result.assign(lines, string());
47 if (formatHelper(input, columnLimit, result)) {
48 return result;
49 }
50 }
51 append(result, input);
52 return result;
53}
54
55vector<string> Completer::formatListInColumns(std::span<const string_view> input)
56{
57 return format(input, output->getOutputColumns() - 1);
58}
59
60bool Completer::equalHead(string_view s1, string_view s2, bool caseSensitive)
61{
62 if (s2.size() < s1.size()) return false;
63 if (caseSensitive) {
64 return ranges::equal(s1, subspan(s2, 0, s1.size()));
65 } else {
66 return strncasecmp(s1.data(), s2.data(), s1.size()) == 0;
67 }
68}
69
70bool Completer::completeImpl(string& str, vector<string_view> matches,
71 bool caseSensitive)
72{
73 assert(ranges::all_of(matches, [&](auto& m) {
74 return equalHead(str, m, caseSensitive);
75 }));
76
77 if (matches.empty()) {
78 // no matching values
79 return false;
80 }
81 if (matches.size() == 1) {
82 // only one match
83 str = matches.front();
84 return true;
85 }
86
87 // Sort and remove duplicates.
88 // For efficiency it's best if the list doesn't contain duplicates to
89 // start with. Though sometimes this is hard to avoid. E.g. when doing
90 // filename completion + some extra allowed strings and one of these
91 // extra strings is the same as one of the filenames.
94
95 bool expanded = false;
96 while (true) {
97 auto it = begin(matches);
98 if (str.size() == it->size()) {
99 // match is as long as first word
100 goto out; // TODO rewrite this
101 }
102 // expand with one char and check all strings
103 auto b = begin(*it);
104 auto e = b + str.size();
105 utf8::unchecked::next(e); // one more utf8 char
106 string_view string2(&*b, e - b);
107 for (; it != end(matches); ++it) {
108 if (!equalHead(string2, *it, caseSensitive)) {
109 goto out; // TODO rewrite this
110 }
111 }
112 // no conflict found
113 str = string2;
114 expanded = true;
115 }
116 out:
117 if (!expanded && output) {
118 // print all possibilities
119 for (auto& line : formatListInColumns(matches)) {
120 output->output(line);
121 }
122 }
123 return false;
124}
125
126void Completer::completeFileName(vector<string>& tokens,
127 const FileContext& context)
128{
129 completeFileNameImpl(tokens, context, vector<string_view>());
130}
131
132void Completer::completeFileNameImpl(vector<string>& tokens,
133 const FileContext& context,
134 vector<string_view> matches)
135{
136 string& filename = tokens.back();
137 filename = FileOperations::expandTilde(std::move(filename));
138 filename = FileOperations::expandCurrentDirFromDrive(std::move(filename));
139 string_view dirname1 = FileOperations::getDirName(filename);
140
141 std::span<const string> paths;
142 if (FileOperations::isAbsolutePath(filename)) {
143 static const std::array<std::string, 1> EMPTY = {""};
144 paths = EMPTY;
145 } else {
146 paths = context.getPaths();
147 }
148
149 vector<string> filenames;
150 for (const auto& p : paths) {
151 auto pLen = p.size();
152 if (!p.empty() && (p.back() != '/')) ++pLen;
153 auto fileAction = [&](const string& path) {
155 path.substr(pLen));
156 if (equalHead(filename, nm, true)) {
157 filenames.push_back(nm);
158 }
159 };
160 auto dirAction = [&](string& path) {
161 path += '/';
162 fileAction(path);
163 path.pop_back();
164 };
166 FileOperations::join(p, dirname1),
167 fileAction, dirAction);
168 }
169 append(matches, filenames);
170 bool t = completeImpl(filename, matches, true);
171 if (t && !filename.empty() && (filename.back() != '/')) {
172 // completed filename, start new token
173 tokens.emplace_back();
174 }
175}
176
177void Completer::checkNumArgs(std::span<const TclObject> tokens, unsigned exactly, const char* errMessage) const
178{
179 checkNumArgs(tokens, exactly, Prefix{exactly - 1}, errMessage);
180}
181
182void Completer::checkNumArgs(std::span<const TclObject> tokens, AtLeast atLeast, const char* errMessage) const
183{
184 checkNumArgs(tokens, atLeast, Prefix{atLeast.min - 1}, errMessage);
185}
186
187void Completer::checkNumArgs(std::span<const TclObject> tokens, Between between, const char* errMessage) const
188{
189 checkNumArgs(tokens, between, Prefix{between.min - 1}, errMessage);
190}
191
192void Completer::checkNumArgs(std::span<const TclObject> tokens, unsigned exactly, Prefix prefix, const char* errMessage) const
193{
194 if (tokens.size() == exactly) return;
195 getInterpreter().wrongNumArgs(prefix.n, tokens, errMessage);
196}
197
198void Completer::checkNumArgs(std::span<const TclObject> tokens, AtLeast atLeast, Prefix prefix, const char* errMessage) const
199{
200 if (tokens.size() >= atLeast.min) return;
201 getInterpreter().wrongNumArgs(prefix.n, tokens, errMessage);
202}
203
204void Completer::checkNumArgs(std::span<const TclObject> tokens, Between between, Prefix prefix, const char* errMessage) const
205{
206 if (tokens.size() >= between.min && tokens.size() <= between.max) return;
207 getInterpreter().wrongNumArgs(prefix.n, tokens, errMessage);
208}
209
210} // namespace openmsx
TclObject t
static void completeFileName(std::vector< std::string > &tokens, const FileContext &context, const RANGE &extra)
Definition Completer.hh:147
static std::vector< std::string > formatListInColumns(std::span< const std::string_view > input)
Definition Completer.cc:55
virtual Interpreter & getInterpreter() const =0
void checkNumArgs(std::span< const TclObject > tokens, unsigned exactly, const char *errMessage) const
Definition Completer.cc:177
virtual unsigned getOutputColumns() const =0
virtual void output(std::string_view text)=0
void wrongNumArgs(unsigned argc, std::span< const TclObject > tokens, const char *message)
constexpr double e
Definition Math.hh:21
void append(Result &)
Definition stl.hh:299
std::optional< Context > context
Definition GLContext.cc:10
string expandTilde(string path)
Expand the '~' character to the users home directory.
string_view getDirName(string_view path)
Returns the directory portion of a path.
const std::string & expandCurrentDirFromDrive(const std::string &path)
Get the current directory of the specified drive Linux: return the given string unchanged.
bool isAbsolutePath(string_view path)
Checks whether it's a absolute path or not.
const std::string & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
string join(string_view part1, string_view part2)
Join two paths.
This file implemented 3 utility functions:
Definition Autofire.cc:9
bool matches(const Event &self, const Event &other)
Does this event 'match' the given event.
Definition Event.cc:210
bool foreach_file_and_directory(std::string path, FileAction fileAction, DirAction dirAction)
auto unique(ForwardRange &&range)
Definition ranges.hh:222
bool all_of(InputRange &&range, UnaryPredicate pred)
Definition ranges.hh:186
bool equal(InputRange1 &&range1, InputRange2 &&range2, Pred pred={}, Proj1 proj1={}, Proj2 proj2={})
Definition ranges.hh:368
constexpr void sort(RandomAccessRange &&range)
Definition ranges.hh:49
size_t size(std::string_view utf8)
uint32_t next(octet_iterator &it)
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
Definition ranges.hh:471
strCatImpl::ConcatSpaces spaces(size_t n)
Definition strCat.hh:801
void strAppend(std::string &result, Ts &&...ts)
Definition strCat.hh:752
constexpr auto xrange(T e)
Definition xrange.hh:132
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)