xaizek / libvle (License: GPLv3+) (since 2019-04-21)
Library for building Vim-like applications.
File Mode Size
compat/ 040000
engine/ 040000
utils/ 040000
Commands.cpp 100644 3,995B
Commands.hpp 100644 2,337B
KeyDispatcher.cpp 100644 1,526B
KeyDispatcher.hpp 100644 1,800B
Mode.cpp 100644 2,317B
Mode.hpp 100644 3,181B
Modes.cpp 100644 3,316B
Modes.hpp 100644 1,989B
README.md 100644 4,309B
keys.cpp 100644 1,423B
keys.hpp 100644 1,353B
xmake.lua 100644 210B

/README.md

libvle, 2019 – 2022

This file last updated on 8 February, 2022

Brief Description

This is a C++11 library that provides basic classes for implementing Vim-like behaviour. Although the interface is in C++, core of the implementation is in C.

VLE stands for Vim-Like Engine/Experience.

Goals

The obvious purpose is to avoid reimplementing Vim-like functionality over and over again and instead do it once in a reusable manner.

Implementation state

It was extracted out of Vifm for a couple of projects, has minimally necessary functionality and can change quite a bit in the future. The implementations might diverge as there is currently no synchronization process in place.

Throw-in library state

There is no build system and nothing is getting built for the client. To use it clone the repository (possibly as a submodule) and handle the building with the build system that's used by the main project. Compile C++ files with C++11 enabled.

Alternatively one can use xmake to consume submodule as a subproject (example assumes it's stored under libs/):

add_includedirs("libs")
includes("libs/*/xmake.lua")

Prerequisites

  • C++11 capable compiler

Structure

API

The API consists of classes in the vle namespace.

What's available

As noted above, API is extended to accommodate use cases and currently includes only:

  • Modes -- handles initialization, deinitialization and switching modes
  • Mode -- contains mode configuration
  • Shortcut -- shortcuts with optional support of count
  • KeyDispatcher -- input processor for key shortcuts
  • Commands -- parses and executes a single command-line :command

How to use shortcut implementation

  1. Create Mode objects and populate them with instances of Shortcut.
  2. Put Mode objects into Modes.
  3. Feed input to KeyDispatcher, which will trigger shortcut handlers.

How to use command implementation

  1. Create an instance of Commands and populate it with Command objects.
  2. Get :command from somewhere (e.g., user input or file).
  3. Pass :command to Commands, which will trigger appropriate command handler.

Samples

Shortcuts example

Very minimal application:

#include <cstdlib>

#include <iostream>
#include <string>

#include "vle/KeyDispatcher.hpp"
#include "vle/Mode.hpp"
#include "vle/Modes.hpp"

int
main()
{
    vle::Mode normalMode("normal");
    normalMode.setUsesCount(true);
    normalMode.addShortcut({ L"gg", [&]() {
        std::wcout << L"go to the top\n";
    }, "go to the top" });
    normalMode.addShortcut({ L"j", [&](int i) {
        std::wcout << L"go down " << (i == -1 ? 1 : i) << L" item(s)\n";
    }, "go down" });

    std::vector<vle::Mode> allModes;
    allModes.emplace_back(std::move(normalMode));

    vle::Modes modes;
    modes.setModes(std::move(allModes));
    modes.switchTo("normal");

    vle::KeyDispatcher dispatcher;

    for (std::wstring ws; std::wcout << L"  User input: ",
                          std::getline(std::wcin, ws); ) {
        for (wchar_t wc : ws) {
            std::wcout << L"  > Sending in: " << wc << L'\n';
            dispatcher.dispatch(wc);
            std::wcout << L"  > Buffered: " << dispatcher.getPendingInput()
                       << L'\n';
        }
        std::wcout << L"\n";
    }

    return EXIT_SUCCESS;
}

Commands example

Very minimal application:

#include <cstdlib>

#include <iostream>
#include <string>

#include "vle/Commands.hpp"

int
main()
{
    bool quit = false;

    vle::Commands commands;
    commands.addCommand({ "q", "quit", "exit the app", 0, 0, [&](...) {
        quit = true;
    }});
    commands.addCommand({ "", "echo", "print arguments", 0, -1,
                        [&](const std::vector<std::string> &args) {
        for (const auto &arg : args) {
            std::cout << arg << '\n';
        }
    }});

    for (std::string s; !quit && (std::cout << "  User input: ") &&
                        std::getline(std::cin, s); ) {
        if (!commands.execute(s)) {
            std::cout << "Some kind of error has occurred.\n";
        }
    }

    return EXIT_SUCCESS;
}
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