GNU Readline replace with bestline
This commit is contained in:
parent
e6aed50fe9
commit
7d0f732a57
14
Makefile
14
Makefile
@ -1,6 +1,6 @@
|
||||
MAKEFLAGS="-j $(grep -c ^processor /proc/cpuinfo)"
|
||||
CXXFLAGS:=$(CXXFLAGS) -std=c++20 -Wall -Wextra -Werror=switch -Werror=return-type -Werror=unused-result
|
||||
CPPFLAGS:=$(CPPFLAGS) -Ilib/expected/ -Ilib/ut/ -Ilib/midi/include -Isrc/
|
||||
CPPFLAGS:=$(CPPFLAGS) -Ilib/expected/ -Ilib/ut/ -Ilib/midi/include -Isrc/ -Ilib/bestline/
|
||||
RELEASE_FLAGS=-O3
|
||||
DEBUG_FLAGS=-O0 -ggdb -fsanitize=undefined
|
||||
CXX=g++
|
||||
@ -49,18 +49,22 @@ bin/%.o: src/%.cc src/*.hh
|
||||
@echo "CXX $@"
|
||||
@$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $< -c
|
||||
|
||||
bin/musique: $(Release_Obj) bin/main.o src/*.hh lib/midi/libmidi-alsa.a
|
||||
bin/musique: $(Release_Obj) bin/main.o bin/bestline.o src/*.hh lib/midi/libmidi-alsa.a
|
||||
@echo "CXX $@"
|
||||
@$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/main.o $(LDFLAGS) $(LDLIBS)
|
||||
@$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/bestline.o bin/main.o $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
bin/debug/musique: $(Debug_Obj) bin/debug/main.o src/*.hh
|
||||
bin/debug/musique: $(Debug_Obj) bin/debug/main.o bin/bestline.o src/*.hh
|
||||
@echo "CXX $@"
|
||||
@$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $(Debug_Obj) bin/debug/main.o $(LDFLAGS) $(LDLIBS)
|
||||
@$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $(Debug_Obj) bin/bestline.o bin/debug/main.o $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
bin/debug/%.o: src/%.cc src/*.hh
|
||||
@echo "CXX $@"
|
||||
@$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $< -c
|
||||
|
||||
bin/bestline.o: lib/bestline/bestline.c lib/bestline/bestline.h
|
||||
@echo "CC $@"
|
||||
@$(CC) $< -c -O3 -o $@
|
||||
|
||||
unit-tests: bin/unit-tests
|
||||
./$<
|
||||
|
||||
|
30
lib/bestline/LICENSE
Normal file
30
lib/bestline/LICENSE
Normal file
@ -0,0 +1,30 @@
|
||||
Bestline is released under the 2-clause BSD license.
|
||||
|
||||
Copyright (c) 2018-2021 Justine Tunney <jtunney@gmail.com>
|
||||
Copyright (c) 2010-2016 Salvatore Sanfilippo <antirez@gmail.com>
|
||||
Copyright (c) 2010-2013 Pieter Noordhuis <pcnoordhuis@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
3578
lib/bestline/bestline.c
Normal file
3578
lib/bestline/bestline.c
Normal file
File diff suppressed because it is too large
Load Diff
39
lib/bestline/bestline.h
Normal file
39
lib/bestline/bestline.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct bestlineCompletions {
|
||||
unsigned long len;
|
||||
char **cvec;
|
||||
} bestlineCompletions;
|
||||
|
||||
typedef void(bestlineCompletionCallback)(const char *, bestlineCompletions *);
|
||||
typedef char *(bestlineHintsCallback)(const char *, const char **,
|
||||
const char **);
|
||||
typedef void(bestlineFreeHintsCallback)(void *);
|
||||
typedef unsigned(bestlineXlatCallback)(unsigned);
|
||||
|
||||
void bestlineSetCompletionCallback(bestlineCompletionCallback *);
|
||||
void bestlineSetHintsCallback(bestlineHintsCallback *);
|
||||
void bestlineSetFreeHintsCallback(bestlineFreeHintsCallback *);
|
||||
void bestlineAddCompletion(bestlineCompletions *, const char *);
|
||||
void bestlineSetXlatCallback(bestlineXlatCallback *);
|
||||
|
||||
char *bestline(const char *);
|
||||
char *bestlineRaw(const char *, int, int);
|
||||
char *bestlineWithHistory(const char *, const char *);
|
||||
int bestlineHistoryAdd(const char *);
|
||||
int bestlineHistorySave(const char *);
|
||||
int bestlineHistoryLoad(const char *);
|
||||
void bestlineFreeCompletions(bestlineCompletions *);
|
||||
void bestlineHistoryFree(void);
|
||||
void bestlineClearScreen(int);
|
||||
void bestlineMaskModeEnable(void);
|
||||
void bestlineMaskModeDisable(void);
|
||||
void bestlineDisableRawMode(void);
|
||||
void bestlineFree(void *);
|
||||
|
||||
char bestlineIsSeparator(unsigned);
|
||||
char bestlineNotSeparator(unsigned);
|
||||
char bestlineIsXeparator(unsigned);
|
||||
unsigned bestlineUppercase(unsigned);
|
||||
unsigned bestlineLowercase(unsigned);
|
||||
long bestlineReadCharacter(int, char *, unsigned long);
|
79
lib/bestline/example.c
Normal file
79
lib/bestline/example.c
Normal file
@ -0,0 +1,79 @@
|
||||
#include "bestline.h"
|
||||
|
||||
#ifndef __COSMOPOLITAN__
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// should be ~50kb statically linked
|
||||
// will save history to ~/.foo_history
|
||||
// cc -fno-jump-tables -Os -o foo foo.c bestline.c
|
||||
int main() {
|
||||
char *line;
|
||||
while ((line = bestlineWithHistory("IN> ", "foo"))) {
|
||||
fputs("OUT> ", stdout);
|
||||
fputs(line, stdout);
|
||||
fputs("\n", stdout);
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void completion(const char *buf, bestlineCompletions *lc) {
|
||||
if (buf[0] == 'h') {
|
||||
bestlineAddCompletion(lc,"hello");
|
||||
bestlineAddCompletion(lc,"hello there");
|
||||
}
|
||||
}
|
||||
|
||||
char *hints(const char *buf, const char **ansi1, const char **ansi2) {
|
||||
if (!strcmp(buf,"hello")) {
|
||||
*ansi1 = "\033[35m"; /* magenta foreground */
|
||||
*ansi2 = "\033[39m"; /* reset foreground */
|
||||
return " World";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *line;
|
||||
|
||||
/* Set the completion callback. This will be called every time the
|
||||
* user uses the <tab> key. */
|
||||
bestlineSetCompletionCallback(completion);
|
||||
bestlineSetHintsCallback(hints);
|
||||
|
||||
/* Load history from file. The history file is just a plain text file
|
||||
* where entries are separated by newlines. */
|
||||
bestlineHistoryLoad("history.txt"); /* Load the history at startup */
|
||||
|
||||
/* Now this is the main loop of the typical bestline-based application.
|
||||
* The call to bestline() will block as long as the user types something
|
||||
* and presses enter.
|
||||
*
|
||||
* The typed string is returned as a malloc() allocated string by
|
||||
* bestline, so the user needs to free() it. */
|
||||
|
||||
while((line = bestline("hello> ")) != NULL) {
|
||||
/* Do something with the string. */
|
||||
if (line[0] != '\0' && line[0] != '/') {
|
||||
fputs("echo: '", stdout);
|
||||
fputs(line, stdout);
|
||||
fputs("'\n", stdout);
|
||||
bestlineHistoryAdd(line); /* Add to the history. */
|
||||
bestlineHistorySave("history.txt"); /* Save the history on disk. */
|
||||
} else if (!strncmp(line, "/mask", 5)) {
|
||||
bestlineMaskModeEnable();
|
||||
} else if (!strncmp(line, "/unmask", 7)) {
|
||||
bestlineMaskModeDisable();
|
||||
} else if (line[0] == '/') {
|
||||
fputs("Unreconized command: ", stdout);
|
||||
fputs(line, stdout);
|
||||
fputs("\n", stdout);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
return 0;
|
||||
}
|
38
src/main.cc
38
src/main.cc
@ -7,8 +7,10 @@
|
||||
|
||||
#include <musique.hh>
|
||||
#include <midi.hh>
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
extern "C" {
|
||||
#include <bestline.h>
|
||||
}
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@ -66,6 +68,8 @@ static void trim(std::string_view &s)
|
||||
/// Runs interpreter on given source code
|
||||
struct Runner
|
||||
{
|
||||
static inline Runner *the;
|
||||
|
||||
midi::ALSA alsa;
|
||||
Interpreter interpreter;
|
||||
|
||||
@ -73,6 +77,9 @@ struct Runner
|
||||
Runner(std::string port)
|
||||
: alsa("musique")
|
||||
{
|
||||
assert(the == nullptr, "Only one instance of runner is supported");
|
||||
the = this;
|
||||
|
||||
alsa.init_sequencer();
|
||||
alsa.connect(port);
|
||||
interpreter.midi_connection = &alsa;
|
||||
@ -88,6 +95,11 @@ struct Runner
|
||||
});
|
||||
}
|
||||
|
||||
Runner(Runner const&) = delete;
|
||||
Runner(Runner &&) = delete;
|
||||
Runner& operator=(Runner const&) = delete;
|
||||
Runner& operator=(Runner &&) = delete;
|
||||
|
||||
/// Run given source
|
||||
Result<void> run(std::string_view source, std::string_view filename, bool output = false)
|
||||
{
|
||||
@ -108,6 +120,19 @@ struct Runner
|
||||
/// some of the strings are only views into source
|
||||
std::vector<std::string> eternal_sources;
|
||||
|
||||
void completion(char const* buf, bestlineCompletions *lc)
|
||||
{
|
||||
std::string_view in{buf};
|
||||
|
||||
for (auto scope = Runner::the->interpreter.env.get(); scope != nullptr; scope = scope->parent.get()) {
|
||||
for (auto const& [name, _] : scope->variables) {
|
||||
if (name.starts_with(in)) {
|
||||
bestlineAddCompletion(lc, name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fancy main that supports Result forwarding on error (Try macro)
|
||||
static Result<void> Main(std::span<char const*> args)
|
||||
{
|
||||
@ -189,8 +214,9 @@ static Result<void> Main(std::span<char const*> args)
|
||||
|
||||
if (runnables.empty() || enable_repl) {
|
||||
enable_repl = true;
|
||||
bestlineSetCompletionCallback(completion);
|
||||
for (;;) {
|
||||
char *input_buffer = readline("> ");
|
||||
char *input_buffer = bestlineWithHistory("> ", "musique");
|
||||
if (input_buffer == nullptr) {
|
||||
break;
|
||||
}
|
||||
@ -208,8 +234,6 @@ static Result<void> Main(std::span<char const*> args)
|
||||
continue;
|
||||
}
|
||||
|
||||
add_history(input_buffer);
|
||||
|
||||
if (command.starts_with(':')) {
|
||||
command.remove_prefix(1);
|
||||
if (command == "exit") { break; }
|
||||
@ -234,10 +258,6 @@ static Result<void> Main(std::span<char const*> args)
|
||||
|
||||
int main(int argc, char const** argv)
|
||||
{
|
||||
rl_readline_name = *argv;
|
||||
// Set editing mode to Vim-like
|
||||
rl_editing_mode = 0;
|
||||
|
||||
auto const args = std::span(argv, argc).subspan(1);
|
||||
auto const result = Main(args);
|
||||
if (not result.has_value()) {
|
||||
|
Loading…
Reference in New Issue
Block a user