OXYGENHUB
◆ main openmsx / src / commands / Completer.hh
4.6 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef COMPLETER_HH
2
#define COMPLETER_HH
3
4
#include "CompletionCandidate.hh"
5
6
#include <algorithm>
7
#include <concepts>
8
#include <ranges>
9
#include <span>
10
#include <string>
11
#include <string_view>
12
#include <vector>
13
14
namespace openmsx {
15
16
class FileContext;
17
class Interpreter;
18
class InterpreterOutput;
19
class TclObject;
20
21
class Completer
22
{
23
public:
24
	Completer(const Completer&) = delete;
25
	Completer(Completer&&) = delete;
26
	Completer& operator=(const Completer&) = delete;
27
	Completer& operator=(Completer&&) = delete;
28
29
	[[nodiscard]] const std::string& getName() const { return theName; }
30
31
	/** Print help for this command.
32
	  */
33
	[[nodiscard]] virtual std::string help(std::span<const TclObject> tokens) const = 0;
34
35
	/** Attempt tab completion for this command.
36
	  * @param tokens Tokenized command line;
37
	  *     tokens[0] is the command itself.
38
	  *     The last token is incomplete, this method tries to complete it.
39
	  */
40
	virtual void tabCompletion(std::vector<std::string>& tokens) const = 0;
41
42
	[[nodiscard]] virtual Interpreter& getInterpreter() const = 0;
43
44
	template<std::ranges::forward_range Range>
45
	static void completeString(std::vector<std::string>& tokens,
46
	                           Range&& possibleValues,
47
	                           bool caseSensitive = true);
48
	template<std::ranges::forward_range Range = std::vector<CompletionCandidate>>
49
	static void completeFileName(std::vector<std::string>& tokens,
50
	                             const FileContext& context,
51
	                             const Range& extra = {});
52
53
	static std::vector<std::string> formatListInColumns(
54
		std::span<const std::string_view> input);
55
56
	// helper functions to check the number of arguments
57
	struct AtLeast { unsigned min; };
58
	struct Between { unsigned min; unsigned max; };
59
	struct Prefix { unsigned n; }; // how many items from 'tokens' to show in error
60
	void checkNumArgs(std::span<const TclObject> tokens, unsigned exactly, const char* errMessage) const;
61
	void checkNumArgs(std::span<const TclObject> tokens, AtLeast atLeast,  const char* errMessage) const;
62
	void checkNumArgs(std::span<const TclObject> tokens, Between between,  const char* errMessage) const;
63
	void checkNumArgs(std::span<const TclObject> tokens, unsigned exactly, Prefix prefix, const char* errMessage) const;
64
	void checkNumArgs(std::span<const TclObject> tokens, AtLeast atLeast,  Prefix prefix, const char* errMessage) const;
65
	void checkNumArgs(std::span<const TclObject> tokens, Between between,  Prefix prefix, const char* errMessage) const;
66
67
	// should only be called by CommandConsole
68
	static void setOutput(InterpreterOutput* output_) { output = output_; }
69
70
protected:
71
	template<typename String>
72
		requires(!std::same_as<Completer, std::remove_cvref_t<String>>) // don't block copy-constructor
73
	explicit Completer(String&& name_)
74
		: theName(std::forward<String>(name_))
75
	{
76
	}
77
78
	~Completer() = default;
79
80
private:
81
	static bool equalHead(std::string_view s, const CompletionCandidate& c);
82
	template<std::ranges::forward_range Range>
83
	static std::vector<CompletionCandidate> filter(
84
		std::string_view str, Range&& range, bool caseSensitive = true);
85
	static void completeImpl(std::vector<std::string>& tokens, std::vector<CompletionCandidate> matches);
86
	static void completeFileNameImpl(std::vector<std::string>& tokens,
87
	                                 const FileContext& context,
88
	                                 std::vector<CompletionCandidate> matches);
89
90
	const std::string theName;
91
	static inline InterpreterOutput* output = nullptr;
92
};
93
94
template<std::ranges::forward_range Range>
95
std::vector<CompletionCandidate> Completer::filter(
96
	std::string_view str, Range&& range, bool caseSensitive)
97
{
98
	struct MakeCompletionCandidate {
99
		CompletionCandidate operator()(std::string_view s, bool caseSensitive) const {
100
			return {.text = std::string(s), .caseSensitive = caseSensitive};
101
		}
102
		CompletionCandidate operator()(CompletionCandidate c, bool /*caseSensitive*/) const {
103
			return c;
104
		}
105
	} make;
106
107
	std::vector<CompletionCandidate> result;
108
	for (auto&& value : range) {
109
		CompletionCandidate c = make(std::forward<decltype(value)>(value), caseSensitive);
110
		if (equalHead(str, c)) {
111
			result.push_back(std::move(c));
112
		}
113
	}
114
	return result;
115
}
116
117
template<std::ranges::forward_range Range>
118
void Completer::completeString(
119
	std::vector<std::string>& tokens,
120
	Range&& possibleValues,
121
	bool caseSensitive)
122
{
123
	completeImpl(tokens,
124
	             filter(tokens.back(), std::forward<Range>(possibleValues), caseSensitive));
125
}
126
127
template<std::ranges::forward_range Range>
128
void Completer::completeFileName(
129
	std::vector<std::string>& tokens,
130
	const FileContext& context,
131
	const Range& extra)
132
{
133
	completeFileNameImpl(tokens, context, filter(tokens.back(), extra));
134
}
135
136
} // namespace openmsx
137
138
#endif