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 
15 using std::vector;
16 using std::string;
17 using std::string_view;
18 
19 namespace openmsx {
20 
21 Completer::Completer(string_view name)
22  : theName(std::string(name)) // TODO take std::string parameter instead and move()
23 {
24 }
25 
26 static bool formatHelper(const vector<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 
46 static vector<string> format(const vector<string_view>& input, size_t columnLimit)
47 {
48  vector<string> result;
49  for (size_t lines = 1; lines < input.size(); ++lines) {
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 
59 vector<string> Completer::formatListInColumns(const vector<string_view>& input)
60 {
61  return format(input, output->getOutputColumns() - 1);
62 }
63 
64 bool Completer::equalHead(string_view s1, string_view s2, bool caseSensitive)
65 {
66  if (s2.size() < s1.size()) return false;
67  if (caseSensitive) {
68  return memcmp(s1.data(), s2.data(), s1.size()) == 0;
69  } else {
70  return strncasecmp(s1.data(), s2.data(), s1.size()) == 0;
71  }
72 }
73 
74 bool 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.
96  ranges::sort(matches);
97  matches.erase(ranges::unique(matches), end(matches));
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(&*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 (auto& line : formatListInColumns(matches)) {
124  output->output(line);
125  }
126  }
127  return false;
128 }
129 
130 void Completer::completeFileName(vector<string>& tokens,
131  const FileContext& context)
132 {
133  completeFileNameImpl(tokens, context, vector<string_view>());
134 }
135 
136 void Completer::completeFileNameImpl(vector<string>& tokens,
137  const FileContext& context,
138  vector<string_view> matches)
139 {
140  string& filename = tokens.back();
143  string_view dirname1 = FileOperations::getDirName(filename);
144 
145  vector<string> paths;
147  paths.emplace_back();
148  } else {
149  paths = context.getPaths();
150  }
151 
152  vector<string> filenames;
153  for (auto& p : paths) {
154  auto fileAction = [&](const string& path) {
155  const auto& nm = FileOperations::getConventionalPath(path);
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 
177 void Completer::checkNumArgs(span<const TclObject> tokens, unsigned exactly, const char* errMessage) const
178 {
179  checkNumArgs(tokens, exactly, Prefix{exactly - 1}, errMessage);
180 }
181 
182 void Completer::checkNumArgs(span<const TclObject> tokens, AtLeast atLeast, const char* errMessage) const
183 {
184  checkNumArgs(tokens, atLeast, Prefix{atLeast.min - 1}, errMessage);
185 }
186 
187 void Completer::checkNumArgs(span<const TclObject> tokens, Between between, const char* errMessage) const
188 {
189  checkNumArgs(tokens, between, Prefix{between.min - 1}, errMessage);
190 }
191 
192 void Completer::checkNumArgs(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 
198 void Completer::checkNumArgs(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 
204 void Completer::checkNumArgs(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
openmsx::Completer::checkNumArgs
void checkNumArgs(span< const TclObject > tokens, unsigned exactly, const char *errMessage) const
Definition: Completer.cc:177
openmsx::Interpreter::wrongNumArgs
void wrongNumArgs(unsigned argc, span< const TclObject > tokens, const char *message)
Definition: Interpreter.cc:452
InterpreterOutput.hh
openmsx::Completer::Between
Definition: Completer.hh:55
openmsx::Completer::completeFileName
static void completeFileName(std::vector< std::string > &tokens, const FileContext &context, const RANGE &extra)
Definition: Completer.hh:139
openmsx::Completer::Completer
Completer(std::string_view name)
Definition: Completer.cc:21
ranges::sort
void sort(RandomAccessRange &&range)
Definition: ranges.hh:35
utf8::unchecked::next
uint32_t next(octet_iterator &it)
Definition: utf8_unchecked.hh:64
TclObject.hh
utf8::unchecked::size
size_t size(std::string_view utf8)
Definition: utf8_unchecked.hh:227
openmsx::FileOperations::isAbsolutePath
bool isAbsolutePath(string_view path)
Checks whether it's a absolute path or not.
Definition: FileOperations.cc:491
openmsx::InterpreterOutput::output
virtual void output(std::string_view text)=0
openmsx::InterpreterOutput::getOutputColumns
virtual unsigned getOutputColumns() const =0
openmsx::Completer::Prefix::n
unsigned n
Definition: Completer.hh:56
t
TclObject t
Definition: TclObject_test.cc:264
ranges.hh
Interpreter.hh
ranges::all_of
bool all_of(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:119
strAppend
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:644
openmsx::FileOperations::join
string join(string_view part1, string_view part2)
Join two paths.
Definition: FileOperations.cc:423
openmsx::FileContext
Definition: FileContext.hh:10
span
Definition: span.hh:126
openmsx::Completer::AtLeast
Definition: Completer.hh:54
openmsx::FileOperations::getDirName
string_view getDirName(string_view path)
Returns the directory portion of a path.
Definition: FileOperations.cc:398
utf8_unchecked.hh
openmsx::FileOperations::expandTilde
string expandTilde(string_view path)
Expand the '~' character to the users home directory.
Definition: FileOperations.cc:195
openmsx::Completer::Between::max
unsigned max
Definition: Completer.hh:55
openmsx::filename
constexpr const char *const filename
Definition: FirmwareSwitch.cc:10
openmsx::Completer::Between::min
unsigned min
Definition: Completer.hh:55
openmsx::Completer::AtLeast::min
unsigned min
Definition: Completer.hh:54
openmsx::FileOperations::expandCurrentDirFromDrive
string expandCurrentDirFromDrive(string_view path)
Get the current directory of the specified drive Linux: just return an empty string.
Definition: FileOperations.cc:599
view.hh
ranges::unique
auto unique(ForwardRange &&range)
Definition: ranges.hh:137
FileContext.hh
spaces
strCatImpl::ConcatSpaces spaces(size_t n)
Definition: strCat.hh:675
FileOperations.hh
span::size
constexpr index_type size() const noexcept
Definition: span.hh:296
gl::context
std::unique_ptr< Context > context
Definition: GLContext.cc:9
openmsx::foreach_file_and_directory
bool foreach_file_and_directory(std::string path, FileAction fileAction, DirAction dirAction)
Definition: foreach_file.hh:156
strCat.hh
foreach_file.hh
openmsx::FileOperations::getConventionalPath
const std::string & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
Definition: FileOperations.hh:147
detail::append
void append(Result &)
Definition: stl.hh:335
stringsp.hh
gl::max
vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:292
openmsx::Completer::getInterpreter
virtual Interpreter & getInterpreter() const =0
stl.hh
openmsx::Completer::formatListInColumns
static std::vector< std::string > formatListInColumns(const std::vector< std::string_view > &input)
Definition: Completer.cc:59
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5
Completer.hh
openmsx::Completer::Prefix
Definition: Completer.hh:56