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