openMSX
Completer.cc
Go to the documentation of this file.
1 #include "Completer.hh"
2 #include "Interpreter.hh"
3 #include "InterpreterOutput.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 "view.hh"
14 #include "xrange.hh"
15 
16 using std::vector;
17 using std::string;
18 using std::string_view;
19 
20 namespace openmsx {
21 
22 Completer::Completer(string_view name)
23  : theName(std::string(name)) // TODO take std::string parameter instead and move()
24 {
25 }
26 
27 static bool formatHelper(const vector<string_view>& input, size_t columnLimit,
28  vector<string>& result)
29 {
30  size_t column = 0;
31  auto it = begin(input);
32  do {
33  size_t maxcolumn = column;
34  for (size_t i = 0; (i < result.size()) && (it != end(input));
35  ++i, ++it) {
36  auto curSize = utf8::unchecked::size(result[i]);
37  strAppend(result[i], spaces(column - curSize), *it);
38  maxcolumn = std::max(maxcolumn,
39  utf8::unchecked::size(result[i]));
40  if (maxcolumn > columnLimit) return false;
41  }
42  column = maxcolumn + 2;
43  } while (it != end(input));
44  return true;
45 }
46 
47 static vector<string> format(const vector<string_view>& input, size_t columnLimit)
48 {
49  vector<string> result;
50  for (auto lines : xrange(1u, input.size())) {
51  result.assign(lines, string());
52  if (formatHelper(input, columnLimit, result)) {
53  return result;
54  }
55  }
56  append(result, input);
57  return result;
58 }
59 
60 vector<string> Completer::formatListInColumns(const vector<string_view>& input)
61 {
62  return format(input, output->getOutputColumns() - 1);
63 }
64 
65 bool Completer::equalHead(string_view s1, string_view s2, bool caseSensitive)
66 {
67  if (s2.size() < s1.size()) return false;
68  if (caseSensitive) {
69  return memcmp(s1.data(), s2.data(), s1.size()) == 0;
70  } else {
71  return strncasecmp(s1.data(), s2.data(), s1.size()) == 0;
72  }
73 }
74 
75 bool Completer::completeImpl(string& str, vector<string_view> matches,
76  bool caseSensitive)
77 {
78  assert(ranges::all_of(matches, [&](auto& m) {
79  return equalHead(str, m, caseSensitive);
80  }));
81 
82  if (matches.empty()) {
83  // no matching values
84  return false;
85  }
86  if (matches.size() == 1) {
87  // only one match
88  str = matches.front();
89  return true;
90  }
91 
92  // Sort and remove duplicates.
93  // For efficiency it's best if the list doesn't contain duplicates to
94  // start with. Though sometimes this is hard to avoid. E.g. when doing
95  // filename completion + some extra allowed strings and one of these
96  // extra strings is the same as one of the filenames.
97  ranges::sort(matches);
98  matches.erase(ranges::unique(matches), end(matches));
99 
100  bool expanded = false;
101  while (true) {
102  auto it = begin(matches);
103  if (str.size() == it->size()) {
104  // match is as long as first word
105  goto out; // TODO rewrite this
106  }
107  // expand with one char and check all strings
108  auto b = begin(*it);
109  auto e = b + str.size();
110  utf8::unchecked::next(e); // one more utf8 char
111  string_view string2(&*b, e - b);
112  for (; it != end(matches); ++it) {
113  if (!equalHead(string2, *it, caseSensitive)) {
114  goto out; // TODO rewrite this
115  }
116  }
117  // no conflict found
118  str = string2;
119  expanded = true;
120  }
121  out:
122  if (!expanded && output) {
123  // print all possibilities
124  for (auto& line : formatListInColumns(matches)) {
125  output->output(line);
126  }
127  }
128  return false;
129 }
130 
131 void Completer::completeFileName(vector<string>& tokens,
132  const FileContext& context)
133 {
134  completeFileNameImpl(tokens, context, vector<string_view>());
135 }
136 
137 void Completer::completeFileNameImpl(vector<string>& tokens,
138  const FileContext& context,
139  vector<string_view> matches)
140 {
141  string& filename = tokens.back();
144  string_view dirname1 = FileOperations::getDirName(filename);
145 
146  vector<string> paths;
148  paths.emplace_back();
149  } else {
150  paths = context.getPaths();
151  }
152 
153  vector<string> filenames;
154  for (auto& p : paths) {
155  auto pLen = p.size();
156  if (!p.empty() && (p.back() != '/')) ++pLen;
157  auto fileAction = [&](const string& path) {
158  const auto& nm = FileOperations::getConventionalPath(
159  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 
181 void Completer::checkNumArgs(span<const TclObject> tokens, unsigned exactly, const char* errMessage) const
182 {
183  checkNumArgs(tokens, exactly, Prefix{exactly - 1}, errMessage);
184 }
185 
186 void Completer::checkNumArgs(span<const TclObject> tokens, AtLeast atLeast, const char* errMessage) const
187 {
188  checkNumArgs(tokens, atLeast, Prefix{atLeast.min - 1}, errMessage);
189 }
190 
191 void Completer::checkNumArgs(span<const TclObject> tokens, Between between, const char* errMessage) const
192 {
193  checkNumArgs(tokens, between, Prefix{between.min - 1}, errMessage);
194 }
195 
196 void Completer::checkNumArgs(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 
202 void Completer::checkNumArgs(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 
208 void Completer::checkNumArgs(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:139
static std::vector< std::string > formatListInColumns(const std::vector< std::string_view > &input)
Definition: Completer.cc:60
void checkNumArgs(span< const TclObject > tokens, unsigned exactly, const char *errMessage) const
Definition: Completer.cc:181
Completer(std::string_view name)
Definition: Completer.cc:22
virtual Interpreter & getInterpreter() const =0
virtual unsigned getOutputColumns() const =0
virtual void output(std::string_view text)=0
void wrongNumArgs(unsigned argc, span< const TclObject > tokens, const char *message)
Definition: Interpreter.cc:458
Definition: span.hh:126
constexpr index_type size() const noexcept
Definition: span.hh:296
void append(Result &)
Definition: stl.hh:340
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:287
std::unique_ptr< Context > context
Definition: GLContext.cc:9
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 & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
bool isAbsolutePath(string_view path)
Checks whether it's a absolute path or not.
const std::string & expandCurrentDirFromDrive(const std::string &path)
Get the current directory of the specified drive Linux: return the given string unchanged.
string join(string_view part1, string_view part2)
Join two paths.
This file implemented 3 utility functions:
Definition: Autofire.cc:5
constexpr const char *const filename
bool foreach_file_and_directory(std::string path, FileAction fileAction, DirAction dirAction)
auto unique(ForwardRange &&range)
Definition: ranges.hh:137
bool all_of(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:119
void sort(RandomAccessRange &&range)
Definition: ranges.hh:35
size_t size(std::string_view utf8)
uint32_t next(octet_iterator &it)
strCatImpl::ConcatSpaces spaces(size_t n)
Definition: strCat.hh:700
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:669
constexpr auto xrange(T e)
Definition: xrange.hh:155
constexpr auto begin(const zstring_view &x)
Definition: zstring_view.hh:82
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:83