xaizek / libvle (License: GPLv3+) (since 2019-04-21)
Library for building Vim-like applications.
Commit 509036a3ff91e33793fa9ff0c8c6cea28053c372

Vim-like parsing of command-line commands
Author: xaizek
Author date (UTC): 2022-02-07 23:58
Committer name: xaizek
Committer date (UTC): 2022-02-08 13:49
Parent(s): 1f3a0cd26a9833098f2a4a8b6f7eaba6e3ea6db8
Signing key: 99DC5E4DB05F6BE2
Tree: da838b45c0d0d3513d5ca534c902618e664858de
File Lines added Lines deleted
Commands.cpp 159 0
Commands.hpp 78 0
README.md 58 10
utils/utils.c 0 4
File Commands.cpp added (mode: 100644) (index 0000000..87db1e2)
1 // libvle -- library for building Vim-like applications
2 // Copyright (C) 2022 xaizek <xaizek@posteo.net>
3 //
4 // This file is part of libvle.
5 //
6 // libvle is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // libvle is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with libvle. If not, see <https://www.gnu.org/licenses/>.
18
19 #include "Commands.hpp"
20
21 #include <cstring>
22
23 #include <deque>
24 #include <functional>
25 #include <string>
26 #include <utility>
27 #include <vector>
28
29 #include "engine/cmds.h"
30
31 using namespace vle;
32
33 static int completeLine(const char cmd_line[], void *extra_arg);
34 static int completeArgs(int id, const cmd_info_t *cmd_info, int arg_pos,
35 void *extra_arg);
36 static int swapRange(void);
37 static int resolveMark(char mark);
38 static char * expandMacros(const char str[], int for_shell, int *usr1,
39 int *usr2);
40 static char * expandEnvVars(const char *str);
41 static void post(int id);
42 static void selectRange(int id, const cmd_info_t *cmd_info);
43 static int skipAtBeginning(int id, const char *args);
44 static int cmdHandler(const cmd_info_t *cmd_info);
45
46 Commands::Commands()
47 {
48 auto conf = new cmds_conf_t();
49 conf->complete_line = &completeLine;
50 conf->complete_args = &completeArgs;
51 conf->swap_range = &swapRange;
52 conf->resolve_mark = &resolveMark;
53 conf->expand_macros = &expandMacros;
54 conf->expand_envvars = &expandEnvVars;
55 conf->post = &post;
56 conf->select_range = &selectRange;
57 conf->skip_at_beginning = &skipAtBeginning;
58
59 cmds_conf = conf;
60 }
61
62 Commands::~Commands()
63 {
64 delete static_cast<cmds_conf_t *>(cmds_conf);
65 }
66
67 bool
68 Commands::execute(const std::string &cmd)
69 {
70 setup();
71 return (vle_cmds_run(cmd.c_str()) >= 0);
72 }
73
74 void
75 Commands::setup()
76 {
77 auto conf = static_cast<cmds_conf_t *>(cmds_conf);
78 if(conf->inner != nullptr)
79 {
80 vle_cmds_init(/*udf=*/1, conf);
81 return;
82 }
83
84 vle_cmds_init(/*udf=*/1, conf);
85
86 std::vector<cmd_add_t> cmdsInfo;
87 cmdsInfo.reserve(commands.size());
88 for (auto &cmd : commands) {
89 cmd_add_t info = {};
90 info.name = cmd.name.c_str();
91 info.abbr = (cmd.abbr.empty() ? nullptr : cmd.abbr.c_str());
92 info.descr = cmd.descr.c_str();
93 info.user_data = &cmd;
94 info.handler = &cmdHandler;
95 info.min_args = cmd.minArgs;
96 info.max_args = (cmd.maxArgs < 0 ? NOT_DEF : cmd.maxArgs);
97 cmdsInfo.push_back(info);
98 }
99
100 vle_cmds_add(cmdsInfo.data(), cmdsInfo.size());
101 }
102
103 void
104 Commands::addCommand(Command cmd)
105 {
106 commands.push_back(std::move(cmd));
107 }
108
109 static int
110 completeLine(const char cmd_line[], void *extra_arg)
111 { return 0; }
112
113 static int
114 completeArgs(int id, const cmd_info_t *cmd_info, int arg_pos, void *extra_arg)
115 { return 0; }
116
117 static int
118 swapRange(void)
119 { return 1; }
120
121 static int
122 resolveMark(char mark)
123 { return -1; }
124
125 static char *
126 expandMacros(const char str[], int for_shell, int *usr1, int *usr2)
127 { return strdup(str); }
128
129 static char *
130 expandEnvVars(const char *str)
131 { return strdup(str); }
132
133 static void
134 post(int id)
135 { }
136
137 static void
138 selectRange(int id, const cmd_info_t *cmd_info)
139 { }
140
141 static int
142 skipAtBeginning(int id, const char *args)
143 { return -1; }
144
145 // Dispatches handler invocations from the C layer.
146 static int
147 cmdHandler(const cmd_info_t *cmd_info)
148 {
149 std::vector<std::string> args;
150 args.reserve(cmd_info->argc);
151
152 for (int i = 0; i < cmd_info->argc; ++i) {
153 args.push_back(cmd_info->argv[i]);
154 }
155
156 auto cmd = static_cast<const Command *>(cmd_info->user_data);
157 cmd->handler(args);
158 return 0;
159 }
File Commands.hpp added (mode: 100644) (index 0000000..3ede6f2)
1 // libvle -- library for building Vim-like applications
2 // Copyright (C) 2022 xaizek <xaizek@posteo.net>
3 //
4 // This file is part of libvle.
5 //
6 // libvle is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // libvle is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with libvle. If not, see <https://www.gnu.org/licenses/>.
18
19 #ifndef LIBVLE__COMMANDS_HPP__
20 #define LIBVLE__COMMANDS_HPP__
21
22 #include <deque>
23 #include <functional>
24 #include <string>
25 #include <vector>
26
27 namespace vle {
28
29 // A command.
30 struct Command
31 {
32 // Canonical (the most complete) type of handler.
33 using handler_t = void(const std::vector<std::string> &args);
34
35 std::string abbr; // Shortest name (prefix of the longest one).
36 std::string name; // Longest name.
37 std::string descr; // Description of the command.
38
39 int minArgs; // Minimal number of arguments (>= 0).
40 int maxArgs; // Maximum number of arguments or a negative value (means
41 // "unlimited").
42
43 std::function<handler_t> handler; // Handler to be called on match.
44 };
45
46 // Dispatches command-line commands.
47 class Commands
48 {
49 public:
50 // Setups an empty state.
51 Commands();
52 ~Commands();
53
54 Commands(const Commands &rhs) = delete;
55 Commands(Commands &&rhs) = delete;
56 Commands & operator=(const Commands &rhs) = delete;
57 Commands & operator=(Commands &&rhs) = delete;
58
59 public:
60 // Runs a command. Returns `false` on error (from parser or handler).
61 bool execute(const std::string &cmd);
62
63 // Registers a shortcut. Throws `std::invalid_argument` if shortcut is too
64 // long or a duplicate.
65 void addCommand(Command cmd);
66
67 private:
68 // Prepares engine for use of this set of commands.
69 void setup();
70
71 private:
72 std::deque<Command> commands; // List of registered commands.
73 void *cmds_conf; // Settings for the cmds unit.
74 };
75
76 }
77
78 #endif // LIBVLE__COMMANDS_HPP__
File README.md changed (mode: 100644) (index ac0a57f..51ee1c0)
1 **libvle**, _2019_
1 **libvle**, _2019 – 2022_
2 2
3 _This file last updated on 21 April, 2019_
3 _This file last updated on 7 February, 2022_
4 4
5 5 ### Brief Description ### ### Brief Description ###
6 6
 
... ... This is a C++11 library that provides basic classes for implementing Vim-like
8 8 behaviour. Although the interface is in C++, core of the implementation is in behaviour. Although the interface is in C++, core of the implementation is in
9 9 C. C.
10 10
11 VLE stands for Vim-Like Engine.
11 VLE stands for Vim-Like Engine/Experience.
12 12
13 13 #### Goals #### #### Goals ####
14 14
 
... ... over again and instead do it once in a reusable manner.
17 17
18 18 #### Implementation state #### #### Implementation state ####
19 19
20 It was extracted out of [vifm][vifm] for a couple of projects, has minimally
20 It was extracted out of [Vifm] for a couple of projects, has minimally
21 21 necessary functionality and can change quite a bit in the future. The necessary functionality and can change quite a bit in the future. The
22 22 implementations might diverge as there is currently no synchronization process implementations might diverge as there is currently no synchronization process
23 23 in place. in place.
 
... ... The API consists of classes in the `vle` namespace.
41 41
42 42 #### What's available #### #### What's available ####
43 43
44 As noted above, API is extended to accomodate use cases and currently includes
44 As noted above, API is extended to accommodate use cases and currently includes
45 45 only: only:
46 46
47 47 * `Modes` -- handles initialization, deinitialization and switching modes * `Modes` -- handles initialization, deinitialization and switching modes
48 48 * `Mode` -- contains mode configuration * `Mode` -- contains mode configuration
49 49 * `Shortcut` -- shortcuts with optional support of count * `Shortcut` -- shortcuts with optional support of count
50 * `KeyDispatcher` -- input processor
50 * `KeyDispatcher` -- input processor for key shortcuts
51 * `Commands` -- parses and executes a single command-line :command
51 52
52 #### How to use ####
53 #### How to use shortcut implementation ####
53 54
54 1. Create `Mode` objects and populate them with `Shortcut` objects.
55 1. Create `Mode` objects and populate them with instances of `Shortcut`.
55 56 2. Put `Mode` objects into `Modes`. 2. Put `Mode` objects into `Modes`.
56 57 3. Feed input to `KeyDispatcher`, which will trigger shortcut handlers. 3. Feed input to `KeyDispatcher`, which will trigger shortcut handlers.
57 58
58 ### Sample ###
59 #### How to use command implementation ####
60
61 1. Create an instance of `Commands` and populate it with `Command` objects.
62 2. Get :command from somewhere (e.g., user input or file).
63 3. Pass :command to `Commands`, which will trigger appropriate command handler.
64
65 ### Samples ###
66
67 #### Shortcuts example ####
59 68
60 69 Very minimal application: Very minimal application:
61 70
 
... ... main()
105 114 } }
106 115 ``` ```
107 116
108 [vifm]: https://vifm.info/
117 #### Commands example ####
118
119 Very minimal application:
120
121 ```cxx
122 #include <cstdlib>
123
124 #include <iostream>
125 #include <string>
126
127 #include "vle/Commands.hpp"
128
129 int
130 main()
131 {
132 bool quit = false;
133
134 vle::Commands commands;
135 commands.addCommand({ "q", "quit", "exit the app", 0, 0, [&](...) {
136 quit = true;
137 }});
138 commands.addCommand({ "", "echo", "print arguments", 0, -1,
139 [&](const std::vector<std::string> &args) {
140 for (const auto &arg : args) {
141 std::cout << arg << '\n';
142 }
143 }});
144
145 for (std::string s; !quit && (std::cout << " User input: ") &&
146 std::getline(std::cin, s); ) {
147 if (!commands.execute(s)) {
148 std::cout << "Some kind of error has occurred.\n";
149 }
150 }
151
152 return EXIT_SUCCESS;
153 }
154 ```
155
156 [Vifm]: https://vifm.info/
File utils/utils.c changed (mode: 100644) (index 4670233..ff04c0c)
21 21 #include <stddef.h> /* size_t */ #include <stddef.h> /* size_t */
22 22 #include <stdlib.h> /* qsort() */ #include <stdlib.h> /* qsort() */
23 23
24 #define LOG_ERROR_MSG(...)
25
26 24 void void
27 25 expand_squotes_escaping(char s[]) expand_squotes_escaping(char s[])
28 26 { {
 
... ... expand_dquotes_escaping(char s[])
68 66 /* e0 */ "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" /* e0 */ "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"
69 67 /* f0 */ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; /* f0 */ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff";
70 68
71 char *str = s;
72 69 char *p; char *p;
73 70
74 71 p = s; p = s;
 
... ... expand_dquotes_escaping(char s[])
82 79 s++; s++;
83 80 if(*s == '\0') if(*s == '\0')
84 81 { {
85 LOG_ERROR_MSG("Escaped eol in \"%s\"", str);
86 82 break; break;
87 83 } }
88 84 *p++ = table[(int)*s++]; *p++ = table[(int)*s++];
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/libvle

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/libvle

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master