[dev] [edit] Introducing edit, a simple text editor

19 views
Skip to first unread message

Arthur Jacquin

unread,
Sep 27, 2023, 8:14:02 AM9/27/23
to d...@suckless.org
Hello suckless developers,

This is my first time here, I hope I'm doing everything correctly :)

There is a bit of a story leading to this post. About a year ago, I
discovered the kakoune text editor. At that time I was a pretty happy
vim user, but kakoune arguments for its reversed grammar[0] resonated
with me and soon made me want a vim replacement.

Already aware of and enthusiastic about the suckless philosophy, I
started by exploring the text editors referenced there[1]. I was
looking for the combination of a simple yet powerful editing model and
a truly suckless development style. As none satisfied me, I decided to
write my own.

The result is edit[2] (yeah, quite creative on the name).

It uses a kakoune-inspired selection-centric, selection-then-action
model. This makes multi-cursor or column/block editing natural,
without getting in the way for simple editing.

In a typical suckless fashion, edit is written in C99 with no external
dependencies, and is very easy to configure, hack on and extend. The
terminal drawing stuff is done by termbox2.h, a single-header library
discovered on [1].

In about 2.2K lines (plus 2.7K lines for termbox2.h), edit packs quite
a few features, such as syntax highlighting, autocompletion, a line
clipboard, and a search and replace engine supporting regular
expressions and subpatterns and fields reuse.

By now, it's fairly stable. No major change is expected, and I'm
gradually polishing it towards version 1.0. If you're interested in
taking the time to discover edit, the online manual page[3] might be a
good starting point. I'm looking forward some feedback, so feel free
to share your comments ;)

Happy hacking,

Arthur

[0]: https://kakoune.org/why-kakoune/why-kakoune.html
[1]: https://suckless.org/rocks/
[2]: https://jacquin.xyz/edit
[3]: https://jacquin.xyz/edit.1





Daniel Littlewood

unread,
Sep 27, 2023, 9:23:06 AM9/27/23
to dev mail list
Hi Arthur, I tried to build your project, but it failed for me:

termbox2.h:2209:22: error: storage size of ‘sa’ isn’t known
termbox2.h:2345:46: error: ‘struct sigaction’ has no member named ‘sa_handler’
termbox2.h:2345:44: error: invalid use of undefined type ‘struct sigaction’

All the best,
Dan

Arthur Jacquin

unread,
Sep 27, 2023, 9:51:35 AM9/27/23
to dev mail list
> Hi Arthur, I tried to build your project, but it failed for me:
>
> termbox2.h:2209:22: error: storage size of 'sa' isn't known
> termbox2.h:2345:46: error: 'struct sigaction' has no member named
> 'sa_handler'
> termbox2.h:2345:44: error: invalid use of undefined type 'struct
> sigaction'
>
> All the best,
> Dan

termbox2.h is not C99 compliant, yet the -std=c99 compilation flag is
set in the default configuration. On the compilers I tried, it has not
been a problem as the non-C99 parts were ignored, but I shouldn't have
assumed it would always be this way. Sorry for the annoyance.

I removed the -std=c99 flag in the upstream, until I patch termbox2.h
for full C99 compliance. You can either pull the upstream or delete
the flag manually in config.mk.

If deleting the flag is not enough, please let me know. Otherwise,
happy editing ;)

Arthur





Страхиња Радић

unread,
Sep 27, 2023, 11:11:52 AM9/27/23
to dev mail list
On 23/09/27 03:50PM, Arthur Jacquin wrote:
> termbox2.h is not C99 compliant, yet the -std=c99 compilation flag is
> set in the default configuration. On the compilers I tried, it has not
> been a problem as the non-C99 parts were ignored, but I shouldn't have
> assumed it would always be this way. Sorry for the annoyance.

Which parts of termbox2.h are not C99 compliant? What compiler doesn't "ignore"
the non-C99 compliant code when -std=c99 is passed?

I maintain my own simple editor called sled[1], which uses termbox2.h and
passes `-std=c99` as a default flag. I have compiled it using GCC, Clang/LLVM,
tinycc, on (GNU/)Linux and OpenBSD, and I have yet to encounter any errors.

I tried compiling your program using `CC=c99` (script wrapper to GCC) and
passing `-std=c99`, and there are only some warnings about implicitly declared
functions, nothing about `struct sigaction`. In any case, you can simply
include the needed headers prior to including termbox2.h (my program uses
signal.h to redraw after C-Z, so I include it anyway).

[1]: https://strahinja.srht.site/sled/
signature.asc

Ben Green

unread,
Sep 27, 2023, 1:26:14 PM9/27/23
to dev mail list
Hello Dan,


Dan wrote:

termbox2.h:2209:22: error: storage size of ‘sa’ isn’t known
termbox2.h:2345:46: error: ‘struct sigaction’ has no member named ‘sa_handler’
termbox2.h:2345:44: error: invalid use of undefined type ‘struct sigaction’

Seems like termbox2.h expects something in your headers that is not present, what operating system and libc are you using?

Kind regards,

Benjamin.

Arthur Jacquin

unread,
Sep 27, 2023, 3:02:29 PM9/27/23
to dev mail list
The whole C99 compliance thing is not perfectly clear to me, and the
clarification is on my todo list. Let me explain what I understand as
of now. If I'm wrong, correct me.

I use tinycc and gcc for testing, with the `-std=c99` flag. Like you,
I've never had any errors, but I get warnings for implicitely
declared functions (with gcc): strerror_r, cfmakeraw, and fileno.
I believe these functions are not in the C99 standard library, if I
refer to this draft[0], and I would like to avoid including
POSIX-specific headers.

So to my understanding, termbox2.h is C99 compliant, but uses
functions that are not part of the C99 standard library, and that's
why I said (incorrectly) that termbox2.h is not C99 compliant.

What I'm not sure about is how these implicitely declared functions
are treated by the linker. As I intend to rewrite the termbox2.h
parts related to these functions, this question is not crucial, and
until the rewrite is done, deleting the `-std=c99` flag should be
enough.

[0]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf





Страхиња Радић

unread,
Sep 27, 2023, 4:34:41 PM9/27/23
to dev mail list
On closer inspection, termbox2.h does include signal.h itself[1], and
additionally defines _XOPEN_SOURCE[2] and _DEFAULT_SOURCE, so the inclusion of
signal.h can't be escaped.

My testing has shown that when -std=c99 is specified, it is as if that switch
explicitly undefines _DEFAULT_SOURCE/_XOPEN_SOURCE **defined inside the header
file** (this is the weird part). If -D_DEFAULT_SOURCE is given as an argument,
or without -std=c99, when gcc is called directly rather than through the
wrapper, c99, there are no warnings:

$ c99 -E edit.c | grep fileno
if (fstat(fileno(fp), &st) != 0) {
$ gcc -E edit.c | grep fileno
int fileno(FILE *);
int fileno_unlocked(FILE *);
if (fstat(fileno(fp), &st) != 0) {
$ c99 -D_DEFAULT_SOURCE -E edit.c | grep fileno
int fileno(FILE *);
int fileno_unlocked(FILE *);
if (fstat(fileno(fp), &st) != 0) {
$

I use Alpine Linux, thus musl libc. Searching the web yielded the answer[3] on
Stack Overflow, though:

> If you want this to cleanly compile with -std=c99, you must consider the
> inclusion of the _DEFAULT_SOURCE feature test macro

I'm not sure about why you wouldn't want POSIX compliance though (To compile on
Plan9? But it has APE[4]).


[1]: https://github.com/arthur-jacquin/edit/blob/41cd580d80459d1f13ef923c812cf6a858b240f0/termbox2.h#L40
[2]: https://github.com/arthur-jacquin/edit/blob/41cd580d80459d1f13ef923c812cf6a858b240f0/termbox2.h#L29
[3]: https://stackoverflow.com/a/10435860
[4]: https://9p.io/wiki/plan9/Porting_alien_software_to_Plan_9/index.html
signature.asc

Adam Sampson

unread,
Sep 27, 2023, 6:26:32 PM9/27/23
to dev mail list
Страхиња Радић <con...@strahinja.org> writes:

> On closer inspection, termbox2.h does include signal.h itself[1], and
> additionally defines _XOPEN_SOURCE[2] and _DEFAULT_SOURCE, so the
> inclusion of signal.h can't be escaped.

I suspect the problem is that these kinds of macros need to be defined
before *any* of the glibc headers are included (see the
feature_test_macros man page). edit.c includes various other headers
before pulling in termbox2.h, by which time it's too late.

--
Adam Sampson <a...@offog.org> <http://offog.org/>

David Demelier

unread,
Sep 28, 2023, 3:02:49 AM9/28/23
to d...@suckless.org
On Wed, 2023-09-27 at 22:33 +0200, Страхиња Радић wrote:
> On closer inspection, termbox2.h does include signal.h itself[1], and
> additionally defines _XOPEN_SOURCE[2] and _DEFAULT_SOURCE, so the
> inclusion of
> signal.h can't be escaped.
>
> My testing has shown that when -std=c99 is specified, it is as if
> that switch
> explicitly undefines _DEFAULT_SOURCE/_XOPEN_SOURCE **defined inside
> the header
> file** (this is the weird part). If -D_DEFAULT_SOURCE is given as an
> argument,

This,

I've been played several times with -std and
_POSIX_C_SOURCE/_XOPEN_SOURCE and experienced many different behavior
depending on the system and libc. Example, I remember that setting
_XOPEN_SOURCE could hide non-standard functions which can still be
useful as long as you take care of the proper #define.

Not sure if that helps but I eventually stopped adding flags at all and
use just the defaults everywhere. Otherwise I'd be glad to understand
if there is a complete and strict conformance explanation on those
combinations.

--
David



Страхиња Радић

unread,
Sep 28, 2023, 7:35:35 AM9/28/23
to dev mail list
On 23/09/28 09:02AM, David Demelier wrote:
> Not sure if that helps but I eventually stopped adding flags at all and
> use just the defaults everywhere. Otherwise I'd be glad to understand
> if there is a complete and strict conformance explanation on those
> combinations.

As Adam noted, the macros need to be defined before including the headers. More
details can be found in feature_test_macros(7)[1] and libc documentation.


musl libc documentation[2] states (search for "Feature Test Macros Supported by
musl")
> If no feature test macros are defined, musl's headers operate in
> "default features" mode, exposing the equivalent of the `_BSD_SOURCE`
> option below. This corresponds fairly well to what most applications
> unaware of feature test macros expect, and also provides a number of
> more modern features.
>
> Otherwise, if at least one of the below-listed feature test macros is
> defined, they are treated additively, starting from pure ISO C as a
> base. Unless otherwise specified, musl ignores the value of the macro
> and only checks whether it is defined.


Likewise, glibc documentation[3] on feature test macros states:
> Be aware that compiler options also affect included features:
>
> * If you use a strict conformance option, features beyond those from the
> compiler’s language version will be disabled, though feature test macros
> may be used to enable them.
> * Features enabled by compiler options are not overridden by feature test
> macros.


It is worth noting that most suckless programs don't define feature test macros
inside of the source code. Instead, they are usually specified in config.mk as
command line arguments passed to the compiler (thus guaranteed to not suffer
from header inclusion before defining feature test macros). This also gives a
clue on how to use termbox2.h properly.


Also: https://suckless.org/coding_style/
> C Features
>
> * Use C99 without extensions (ISO/IEC 9899:1999).
> * Use POSIX.1-2008:
> - When using gcc define _POSIX_C_SOURCE 200809L.
> - Alternatively define _XOPEN_SOURCE 700.


[1]: https://linux.die.net/man/7/feature_test_macros
[2]: https://musl.libc.org/doc/1.1.24/manual.html
[3]: https://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html#index-_005fDEFAULT_005fSOURCE

signature.asc

Arthur Jacquin

unread,
Oct 22, 2023, 5:13:21 AM10/22/23
to dev mail list
> This looks pretty nice, I also like the fact that bindings
> are easily configurables.
> I have a few patches that I'll propose later after more usage.

Thanks for your nice reply! Can't wait to see your patches ;)

> One thing that I miss there though, as a luser, is editing history.
> To me, undo-ing is very handy. I haven't looked
> at the data structure yet, and I get that this isn't implemented
> by design, but do you recall that would something complex to add?

I understand that some people may miss the undo feature, as it's so
common in others tools. I've given it a lot of thought when designing
edit. As you pointed out, undo-ing is tightly linked to the data
structures, so leaving it aside had major implications on the
developement.

Undo-ing is clearly a non-trivial feature. Most operations are not
injective, meaning one cannot deduce the previous content only from
the new, modified content. Thus implementing undo-ing recquires
storing and managing more content. How much complexity it adds very
much depends on the data structures.

vis[0] (after a quick inspection) seems to have found a good balance
between simplicity and compatibility with features such as undo-ing.
I felt that going the undo path would result in a cheap and
non-innovative vis imitation.

Besides, I found out I was saving so frequently in vim that undo-ing
was most of the time equivalent to reloading. Going without undo-ing
was not a real issue for me.

So I took edit as an opportunity to explore how much complexity can be
stripped off by not implementing the undo feature. There are very few
defined structures. Content is simply stored line by line in a doubly
linked list, as UTF-8 strings:

typedef struct Line { // doubly linked list of lines
struct Line *prev, *next;
int line_nb; // between 1 and nb_lines
int ml, dl; // length in memory, on screen
char *chars; // content (UTF-8, NULL-ended
string)
} Line;

So that's for the the explanation, but to be honest your mail
triggered me to think of undo-ing again. As I don't intend to change
the central data structures, undo-ing won't be as good as in vis, but
I have a few ideas... I'll come back to you if I have some news.

[0]: https://sr.ht/~martanne/vis/





Greg Minshall

unread,
Oct 23, 2023, 9:52:12 AM10/23/23
to dev mail list, Arthur Jacquin
+1 on undo. (but, i'm an emacs user, not likely to ever be part of your
customer base... :)

Quentin Rameau

unread,
Oct 23, 2023, 10:24:41 AM10/23/23
to dev mail list
Hi Arthur,
Well, I really get the idea and the opportunity to have an editor
without editing history, and that could actually be a good way to train
for it too.

I was rather looking for confirmation,
that wasn't really a feature-request.

I think that my most recurrent use-case of undoing is for testing
text replacement, sometimes getting the correct RegEx need a couple
tries for complex ones.

But again, those problem can also be solved with some discipline
and I wouldn't want to trigger a corruption of the current model
if you like how it is already!

Thanks for your consideration though, cheers!
>
>
>
>
>

Sean MacLennan

unread,
Oct 23, 2023, 2:30:29 PM10/23/23
to Arthur Jacquin, dev mail list
On Sun, 22 Oct 2023 11:12:09 +0200
Arthur Jacquin <art...@jacquin.xyz> wrote:

> Undo-ing is clearly a non-trivial feature.

I wrote my own editor (zedit or z) in the late '80s. At that time none
of the editors I was using had undo... so I didn't add it.

It wasn't until I switched to emacs a decade later that I had undo. I
added undo to z. It works 100% of the time in testing... but only
about 80% of the time if doing real heavy editing.

Luckily I only use z for light editing now, so the undo is good enough.

Basically, I agree that undo is hard.

Cheers,
Sean

P.S. I lost the link to the source code for edit. Could you resend it?

Sean MacLennan

unread,
Oct 23, 2023, 8:08:22 PM10/23/23
to Arthur Jacquin, dev mail list
On Tue, 24 Oct 2023 01:26:24 +0200
Arthur Jacquin <art...@jacquin.xyz> wrote:

> I'm interested in having a look at the code too, in case you want to
> share it ;)

No problem. See the README.md for a brief history of zedit. It is
probably too big to be suckless, even in its greatly reduced state.

Most of the cool stuff is gone... mostly because modern OSes don't need
it. It used to have a virtual memory system (saving to swap), extended
memory on DOS, optimized disk writes... the list goes on.

On DOS it had a cool border, the current line was highlighted, and it
dealt with CGA monitors.

It also started out with a gap page buffer scheme which was the rage at
the time. You created a gap in the middle of each page. The idea was
that inserts could use that gap.

However, profiling showed that a lot of cpu was being consumed to deal
with the gap. Most developers spend a LOT more time reading the code
than writing. So zedit is now optimized for reading.

Your usage of lines, although it takes more memory, is probably a good
thing these days. Might be a problem with files in the gigabyte range ;)

https://github.com/smaclennan/zedit

The buffer code is split out in its own buff directory.

Cheers,
Sean

Kyryl Melekhin

unread,
Oct 24, 2023, 7:00:14 AM10/24/23
to dev mail list
Hey guys,

On this topic, I would like to revisit Nextvi. It's been a year since
my last post.
The editor has been getting some lovely quality of life updates while still
adhering to the original philosophy.

Since this is my creation I might be biased, but I still think that Nextvi is
the best suckless editor. This is because it manages to implement everything
without any 3rd party libraries and the shortest number of lines possible.

And of course undo is implemented and works perfectly, and many
other features most editors of this kind lack.

[0] https://github.com/kyx0r/nextvi

Please give it a revisit, and help me get 100 stars on github! I don't
remember the requirement but I think github puts the code that
has over 100 stars into their Arctic deep freeze storage. So it would
be great to have it preserved for future generations in case of a world
disaster.

Kind regards,
Kyryl

Roberto E. Vargas Caballero

unread,
Oct 24, 2023, 9:02:10 AM10/24/23
to dev mail list, Arthur Jacquin
Hi,

On Mon, Oct 23, 2023 at 10:52:11AM -0400, Sean MacLennan wrote:
> Basically, I agree that undo is hard.

Indeed. Ed gets undo by design keeping a history keeping a list of lines
and every modification implies a new list, so you can always undo (at least
once).

Regards,

Страхиња Радић

unread,
Oct 24, 2023, 11:38:54 AM10/24/23
to dev mail list
On 23/10/23 03:11PM, Kyryl Melekhin wrote:
> Since this is my creation I might be biased, but I still think that Nextvi is
> the best suckless editor.

That should be left for others to decide.


> Please give it a revisit, and help me get 100 stars on github!

Social networks should die. Github is one of them.[1]

Say no to Microsoft, Google, Amazon, Disney, Mozilla and other globalist
"Big Tech" corporations.


[1]: https://sfconservancy.org/GiveUpGitHub/
signature.asc

Kyryl Melekhin

unread,
Oct 24, 2023, 11:50:18 AM10/24/23
to dev mail list
Страхиња,
I don't disagree. However, for the moment github is just an alternative
way to do a data backup. Hosting my own git is something I might do in
the future.
Besides, I want people to actually use my software and have some kind
of visibility.

Nobody would know of suckless.org were it not be constantly posted and
talked about
on various social media(s).

Страхиња Радић

unread,
Oct 24, 2023, 12:19:58 PM10/24/23
to dev mail list
On 23/10/24 11:49AM, Kyryl Melekhin wrote:
> Besides, I want people to actually use my software and have some kind
> of visibility.
>
> Nobody would know of suckless.org were it not be constantly posted and
> talked about
> on various social media(s).

This touches up one of the fundamental differences between suckless software
and software that sucks: what drives the creation of software? Many mainstream
software projects have fallen prey to the same disease that gripped show
business and other creative fields: it is exclusively driven by publicity,
catering to crowds and statistical analysis of target audiences, over
everything else. This is something to take note of and avoid.

Programmers shouldn't be held hostage by the "opinion of the crowd" (a social
network defect). Programmers should be motivated only by the desire to create
what is in their own opinion functional, quality software. Maybe it will be
hidden in obscurity, so what? But it won't. If it is useful, those interested
will find a way to discover it and use it.
signature.asc
Reply all
Reply to author
Forward
0 new messages