Awesome. When I saw all the things it could do in so little code, I assumed a higher level language would be used. But, nope...plain old C with the usual standard libraries.
The parser for the syntax highlighting is super cool. Concise and declarative definition of the syntax (well, the keywords, anyway), in HL_keywords, and then maybe 150 lines of code for the parser. I doubt it would work well for a much more complex language than C (say, JavaScript, or Perl), without a lot more smarts. But, it works really well on the C I looked at. Colors are handled in a cute way, too.
All around, I'm just astounded at how concise this is, while still being entirely readable and comprehensible. This isn't clever/tricky golfing code, this is actually human-readable C code.
Edit: Also, I'm struck by how nice/simple the editorProcessKeypress handling is. Trying to do something like this in JavaScript/HTML5 is crazily more complex and verbose. Seeing something like this reminds me how messy user interaction still is in the browser, compared to old CLI and desktop paradigms.
The syntax highlighter isn't really a parser, per se. It's just a lexer / tokenizer / scanner / what-have-you, which tend to be fairly compact state machines.
As an aside, I agree with your edit. As someone who started programming back in the DOS days, whenever I do Web development, I'm always floored by how much it feels like one step forward and two steps back. So many simple things just aren't simple when it comes to the browser, and even certain protocols on the backend like FastCGI seem way more complicated than they need to be (SCGI seems pretty nice, though I feel it's missing a way to signal out-of-band information to the Web server, the way you'd use stderr in CGI, for example (though perhaps I'm missing something there)).
As much as I love the idea of sending semantic markup over the wire for document transfer, I can't help but wonder if a more terminal-like protocol would be better for application "delivery", the way we used to do with telnet and BBSs in the 90s, with an upgrade for multimedia. But I digress...
FastCGI/SCGI were obsoleted by having app servers simply speak HTTP; the web gateway just needs to function as a simple reverse proxy. HTTP is about as simple as it gets to parse - you can write a passable (not quite production quality, but works) parser in about 10 minutes.
A lot of the difficulty in webapps is because every webapp is inherently a distributed system, which are always hard. Single-page apps with no connection to a server are actually quite simple, but they're also about as commercially viable as DOS programming (i.e. not at all).
A while back I'm sure I saw a HTTP state diagram posted here that showed writing a HTTP parser is anything but simple. I guess if you only have to be a (reverse) proxy you might get away with it.
While you're correct that it's a state diagram processing HTTP semantics and not parsing; parsing a text-based protocol is far from trivial. In fact, the HTTP2 FAQ explicitly mentions [1] that reducing parsing complexity was a motivation for going binary with HTTP2.
I've done perfectly adequate HTTP parsing with this Python 4-liner:
headerText, body = text.split('\r\n\r\n', 1)
headerLines = headerText.split('\r\n')
method, path, protocol = headerLines[1].split(' ')
headers = dict(line.split(':').map(str.strip) for line in lines[1:])
For production use you'd probably want something a bit faster & more robust like Mongrel's HTTP parser (itself only 166 lines of Ragel), which powers several million websites out there:
Speaking HTTP is worse in almost every way to using CGI: harder to implement, loses meta information (suppose you put an application at /app/ on your server... CGI handles it well, HTTP doesn't unless you do some extension since your server will see GET / anyway) and is harder to centrally log too.
I think app servers speak HTTP more because it is kinda convenient for developers to run the local server without setting up the http server.. convenience rather than superiority.
Nah, it's not worse: as nostrademons mentioned, you have your gateway server be a reverse proxy server, e.g. HAProxy, and then you can do whatever you want in there. Serve an application under /app/? No problem. Centrally log? Of course. Harder to implement? Not really, every major language has well-supported HTTP libs. And since you only need to know how HTTP works, rather than needing to understand HTTP and CGI, it's conceptually simpler too, assuming that you're running more than one backend server and thus needed a load balancer anyway.
It is interesting to note that most those HTTP libraries end up looking like CGI to the programmer anyway, even sometimes using X-Whatever headers for additional information, because that's the relevant information to an app server.
I started programming in the same era, and feel the same way about "web apps" being a step backwards. The problem is we're building apps on top of a platform originally intended as a document viewer, with kludge upon kludge piled on...
Off topic, but my biggest takeaway was that I could aspire to be someone who isn't a twenty something, with a family, with superb work life balance, not living in the valley, sitting in a garden without three 27 inch monitors, not using the latest programming language or fad and still writing beautiful code that a lot of the world runs on.
I agree. I LOVE how his post weaves in his normal life, having time for doing things he actually loves outside of work, while still continuing to work on passion projects. It just goes to show you that you don't need to always buy into the cowboy coding hype that is often on display here.
> Let’s say this again, “email client”. The notion of email client itself is gone at this point.
Really? Is it lame to be using an email clients these days?
I still use an email client (Thunderbird) because I can't stand the thought of all my mail sitting forever on Goggle's or whoever's servers. Yes, I know that they could have secretly archived all of my email the moment it was sent or received, and that my privacy is not necessarily enhanced by downloading and storing my mail offline.
That's my main reason, but I can think of many other reasons to prefer email clients to a web interface.
I'm surprised by the sentiment that email clients are passé.
Very cool! Going by the acronym, are you trying to make a Stallman-ish IMAP client that works well offline and only needs an occasional Internet connection to sync?
No, just that it does networking on a different thread. This is my main (but not only) problem with mutt. I'm not opposed to going in the direction you suggest, but think more mosh less stallman.
Gmail's web client also non-optionally auto-wraps text before sending, which is very annoying when you're trying to send a patch or part of a log file.
Only in plain text mode, sadly. If you send an HTML message, a plain text version gets included, too, which isn't auto-wrapped. It's encoded as quoted-printable instead, which keeps lines short enough to be legal for SMTP but can be turned back into the original text by the client on the other end.
That issue basically forced me to switch from plain-text to html emails, and thence to the slippery slope of using italics and the OCD of syntax highlighting my code snippets..
In my defense, I still eschew html as far as possible. Even when I use html the messages are multipart, and the text versions usually are complete. I do this thing when I reply where I open the gmail menu, switch to plain text and then switch back to html. This has the effect of stripping out all styling from the message I am replying to, replacing html block quotes with conventional '>'s, and so on. That makes it easier for me to quote passages and so on.
I think of it as 99%[1] text/html with format=flowed, and if gmail ever added format=flowed to text mode[2] I'd switch back in a heartbeat.
[1] The remaining 1% of the time I'm composing something long enough that it has more in common with publishing than 1-1 communication.
You might be right; I may have misinterpreted. He might be saying, "The notion of email client itself is gone from Nano at this point, everything changed about Nano".
And yet I read, on Hacker News, a number of people writing how they were often saved by the availability of nano on random systems
All the time, but I even use nano all the time on OS X for full on programming, mostly for quick edits or starting things off before I can be bothered opening a full project in Sublime. (Sure, I can 'get around' in vi, but I just don't seem to have the brain for it and nano is 90% fine. I grew up on GWBASIC so anything is an improvement ;-))
That aside, this feels like it could be a kinda cool command line level equivalent of ToDoMVC. Port this simple editor into Go, Rust, and a few similar languages - see what the differences are, etc.
Nice thing about Nano is that they arrow keys always seem to do what they are supposed to, no matter the system, shell, TTY, etc. Vi seems to have issues sometimes, and needs extra config
I always try to remember them by noting that the leftmost key moves left, the rightmost key moves right (not too hard), and that 'K climbs' (using alliteration to remember that). That leaves 'J' for cursor down.
In practice, though, I use the cursor keys. Luckily, they work on the editors/systems I work on nowadays. Side effect is that I am very bad at remembering those mnemonics.
I stole the following from a colleagues `.vimrc` which finally allowed me to learn `hjkl`. Now I forget I have this set, and when I do accidentally use an arrow key it makes me laugh.
The point is, they are no longer in symbolic memory. At this point they are muscle memory. Quick, which muscle do you use to move your right thumb in? Does it matter?
Exactly: muscle memory. In your car, do you move the turn signal lever up or down to indicate a right turn? It actually takes longer to answer this question than to do the action in real life.
Another example: There are several passwords that I can type instantly, but I wouldn't be able to recite them.
h is left because it is on the left of the movement keys, l is right because it is on the right of the movement keys... a fairly convenient correspondence.
Just have to imagine the motion. I remember left/right by literally right clicking & feeling my middle finger be to the right of my index. I got a little mixed up when I switched my mouse to my left hand
I use hjkl consistently in vim and in the shell. Just think about how long it takes you to move your fingers to the cursor keys. You should spend some effort getting used to it, as using hjkl means you can keep your hands in the middle of the keyboard. I have also never had any problems with strain, and I suspect this is part of the reason.
Erm... what? I didn't claim it's more usable (though it is for me). I just
provided mnemonics I saw once which I liked, for a question "which k/j moves
up and which moves down?".
The leftmost one is left, the rightmost one is right. Of the two that remain, the one that looks vaguely like an arrow pointing downward, that one goes down. There's one key remaining, and that one goes up, k?
As everyone says, it doesn't take a lot of practice before you know it well enough that you'll never forget. Unbind the arrow keys, or go play some nethack (no-keypad), or whatever.
I know k is up, because I constantly <ESC> k in bash, bit the point of using hjkl is exactly not to know which one is what. You set your finger on the keyboard in home position, now your muscle memory does the rest.
The poster probably changed his bash to use vi editing mode with set -o vi. In this mode, <ESC>k would put the text of the last command entered back on the prompt.
> Have you seen somewhere where that isn't the case?
I use the dvorak keyboard layout and used to remap (among other things) j and k in vim, since they were not on the home row for me. I stopped doing that after a while though.
If you ever happen to bork your kernel badly enough to prevent it from booting, you'll find a classic VI pretty quickly, and probably be grateful for it.
Well, not really. My system doesn't have vi installed. Vim, of course, any
time of the day. Stripped Vim (vim-tiny, as Debian calls it), sure, just after
installation. But vi -- no, I don't have it installed.
For quite a long time Linux didn't have vi even ported. There were several
clones, like Elvis and nvi (and Vim, of course). Few years ago I learned that
somebody took the effort and actually ported traditional vi to Linux. I doubt
mainstream distributions cared to include it, though. Why should they? They
already have plethora of clones packaged, and those clones typically do much
more than traditional vi.
The lesson from this tale is this: don't call vi what is merely a vi clone. vi
is a name of a quite concrete project.
And as for kernel unable to boot: if it can't boot, then how the heck am
I supposed to run anything under this kernel? Unless you meant kernel can't
run OS from local disk. I know Linux well enough to manage to run my things.
You would be surprised how much could you hack through if you knew how ELF
binaries work.
You are being needlessly pedantic. The point is that typing "vi" into a terminal runs some application that implements a "vi-ish" interface. Whether it is ported vi or vim or vim-tiny has some effect on how nice that interface is to work with, but I would argue that most vim users would be able to get their configuration fixed or whatever
I assure you I am not pedantic needlessly. The claim was "vi has some issues
with arrow keys". Now tell me: which vi clone was elsurudo talking about?
Because those are different projects. And Vim doesn't have a smallest issue
with arrow keys (unless with "compatible" option set, I think, but I didn't
use it for years and I may remember wrong).
This is the kind of magic that got me excited about programming in the first place.
I remember being in high school and one day, after class, a friend of mine mentioned that the best programmer we knew had once written a breakout clone with ANSI "graphics." We found him and he said he didn't have the source any more, but we asked him how he did it. He then proceeded to rewrite the whole thing, in Pascal, in about an hour, this time adding animations. Thanks antirez for bringing back that feeling (and, oh yeah, for redis too ;))
PS. the "Low level terminal handling" is gold here.
I love seeing little projects like this, and I wish I were better at finding time to do them myself. It’s all too easy when you’ve been programming professionally for a long time, often working on the same projects every day for months or years, to lose that sense of wonder at what we can achieve in a few hours with a modest amount of code. Sometimes it’s good to just sit down for a day or two and hack together a kind of program you’ve never written before, whether that’s a text editor or a puzzle game or rendering a pretty 3D fractal landscape or an IRC chat bot. It’s always nice to see someone else who’s created something that way too, so thanks for sharing.
Honestly, this is the most exciting and inspiring project I've seen on HN in years, by far. I love that it's a fully usable terminal-based text editor that has zero dependencies. Just perusing the source code has been extremely interesting and educational for me already, and it's only been 5 minutes. Definitely gonna bookmark this project and hack on it! The TODO list looks like a good start, especially the xterm-based feature.
You can tell it's going to be good when, on a Mac, you download the source and compile it, and a) it compiles b) it doesn't give a single warning. It's nice when portability actually works.
I wrote a nano-inspired TTY-based editor, too, and have used it to do virtually all of my coding work for the last couple of years. There's nothing particularly noteworthy about it, technologically, but it does exactly what I want in exactly the way I want it to, and that was enough to make the time spent feel worth my while.
There's something really satisfying about the exercise of creating one's tools. I think there is a lot of value in projects like this whether anyone else ever uses them or not, as an exercise in software craftsmanship.
Looking at this project, I am inspired to see if I could simplify my editor's codebase. I was never fully convinced that ncurses did enough good to justify its complexity; it might be interesting to adopt this author's approach instead.
When I started programming, the basic digital world already existed for me. I never had the experience of typing in a program from a magazine.
This is one of those odd experiences where you remember that self-hosting[1] can involve far more components than just the compiler, spiraling an unexpectedly deep path into the development stack.
I just used @antirez's 1k LOC kilo editor to edit his 1K LOC editor and it was an oddly pleasurable experience :)
This is the programming equivalent of a food blog post (technical details interspersed with what's happening in the author's life). It's great and I think there should be more of it!
Cool; now embed it into a 150 MB Electon app and you have a state of the art editor (maybe introduce several arbitrary delays in the range 300 ms - 2 sec for the authentic Atom feeling)!
Can anyone explain how to do a full-screen UI using only VT100 codes? If I had to write a small editor, I'd probably reach for something like Termbox for display.
It really is pretty simple. The big benefit of ncurses is if you want to target terminals other than xterm compatible ones, and they are so rare nowadays that you can really just ignore them....
Input is quite a pain though (however, ncurses sometimes get it wrong too), but the same thing applies, you just need to switch to raw mode with tcsetattr and then have some kind of select() loop or something to watch for escape sequences then translate them into interesting key presses.
Mouse events are the same btw: you write out a sequence to turn them on, then the terminal sends them as sequences to stdin.
I wrote a terminal library myself for the D programming language (and a terminal emulator too!), it is kinda ugly code but it isn't hard.
I haven't checked but I kinda doubt it... and I kinda hope not - the windows console API is so much nicer than the unix terminal system it would be a pity to make it an ugly hybrid.
Of course, you can run a terminal emulator inside the windows console and handle those sequences too! If I had to guess, I'd say that's what Microsoft would have implemented.
You could look at the code to see how it's done, but basically, you send a special sequence of characters that update the screen.
The VT100 (and a lot of terminal emulators these days) use what are called ANSI escape sequences. For instance, to move the cursor, the CUP (CUrsor Position) sequence is used, which is "<ESC> [ <row> ; <col> H" where <ESC> is the ESCAPE ASCII character, <row> is the row number (as ASCII, so for row 12, it's the string "12"), <col> is the column number ("40" for column 40) and the rest are the literal characters. So, in hex, it's 1B 5B 31 32 3B 34 30 48 (<ESC>[12;40H).
There are more sequences (move the cursor up, down, left, right, clear various portions of the screen, change colors, etc).
Conversely, the terminal will send ANSI escape sequences representing certain keys, such as "<ESC>[A" for the up arrow key, "<ESC>[D" for the left arrow key and such. It's then just a matter of recognizing these sequences and doing the appropriate thing ("I received <ESC>[A so the user is moving the cursor up. Update my internal structure to represent that, and send to the terminal <ESC>[A so it moves the cursor on the screen up").
I think the whole point of this post was that it's a lot of fun to write code if you know it's not going to be taken seriously or used in the long-term, so what you're asking seems to be the exact opposite of why this was created.
It would be fun to see this project replicated using some other underlying data structures, e.g. the buffer-gap data structure used by emacs.[0] Does the code become shorter/prettier? etc.
What challenges might you have if you wanted to implement something like this inside a game framework like Unity or MonoGame with C#, where it renders everything via DirectX or OpenGL?
The main challenge is that, even now, in the year 2016, there is no objectively good, fast, reliable way of rendering arbitrary text with formatting in a texture. Hell, even just plain text is kind of a pain in the ass.
It's hard enough to just render high quality ASCII text. But when you consider the layout problems if you want to support full unicode, with rtl and combining characters, Indic script, etc it's very difficult.
The nice thing about a framework like Unity is that they do have a textfield object that behaves a lot like a standard text object in the .NET Framework. It has a lot of shortcomings but rendering the text is the easy part.
Unity only supports Unicode BMP. Unity doesn't support surrogates, RTL rendering/input etc. Rendering the text performantly for none western scripts is definitely not the easy part, especially on mobile (where Unity also fails to behave properly).
Proper Unicode and RTL support is something Jonathan Blow recently spent a lot of time getting just right for The Witness. I wonder if he'd ever consider open sourcing that work.
If you were looking to do this and wanted a text library that is more flexible than MonoGame's bitmap fonts, I maintain the QuickFont library for text rendering with OpenTK: https://github.com/opcon/quickfont. It doesn't have very good unicode support though.
Nice work. I clearly remember writing a full screen editor in a few hundred lines of Turbo Pascal back in '86 because I hated using EDLIN to edit .BAT files in DOS... :D
Back in the 90's I took a class in Visual Basic, where one of the assignments was to write a rules-compliant tic-tac-toe game. The teacher apologized for the amount of repetitive code we'd have to write to implement it in Windows forms (or whatever it was called back then). I got it down to one printed page.
When I taught V that was a class assignment (along with create a 4 function calculator). I don't remember any of the programs being that long.
For extra credit for people that had problems I'd allow a resubmission of an assignment if you did the logic in a different way. One of the students did a table that let them look up the current board and say what the new move was.
The GitHub page states: "The project is in alpha stage and was written in just a few hours taking code from my other two projects, load81 and linenoise."
Hello! It was a few hours (probably 6-8) across a total of two weekends (so a total span of 9 days where I did 2 "days" of work, 5 days of pause, 2 days of work).
Judging from the screencast he use around 10 seconds per line. That would be, (10s * 1000 loc)/3600 = 2.7 hours just to type 1000 lines of code. Programming is of course not typing, but a mental activity first and foremost. Let's say he is incredible smart [1] and helped by copy and past, so on average he uses 90 seconds to program one line. That is, 25 hours. Of course it will take a few days to create a 1000 line C program that does anything useful.
And of course I know that "a few hours" is just something one say to look cool. I just don't think it is cool.
>Of course it will take a few days to create a 1000 line C program that does anything useful.
You'd be surprised. Especially if you have done C for 1 or 2 decades, you can write 1000 lines in 5-6 hours easily, even without thinking, especially since those lines are for a very specific, and not surprising domain and functionality. It's not like you need novel thinking or to solve some unique challenges to build a text editor of this level. It reads and saves text files, moves the cursor around, and does some crude syntax highlighting. Big fucking deal.
Most of those lines, of course, are also quite easy to fill given C's verbose nature, e.g implementing a few standard data structures you'll need can easily fill 200 or more lines.
>And of course I know that "a few hours" is just something one say to look cool. I just don't think it is cool.
You keep insisting that he lied, without proof. That's untactful. You also keep acting as if this is some huge feat that nobody can pull off and people can only claim to do for "coolness factor".
"In Oct. of 1978, a month after introduction, Barnaby began coding Wordstar with new features. According to Rubenstein, who carefully tracked Barnaby’s work, it took four months to code Wordstar. This was done in assembler from scratch. Only 10-percent of Wordmaster code was used. That was the text buffering algorithms. In four months Barnaby wrote 137,000 lines of bullet-proof assembly language code. Rubenstein later checked with some friends from IBM who calculated Barnaby’s output as 42-man years."
I think you missed the point: the point is that the correct English usage is "fewer than" when discussing quantifiable items (e.g., lines of code). The title should be "...an editor in fewer than 1000 lines of code."
Thank you for clarifying. I thought for a second it could have been a grammatical nitpick but discounted that since this thread is re: an Antirez post. I personally would be very sad if his writing style became more forced and began to feel like excerpts from The Elements of Style.
The parser for the syntax highlighting is super cool. Concise and declarative definition of the syntax (well, the keywords, anyway), in HL_keywords, and then maybe 150 lines of code for the parser. I doubt it would work well for a much more complex language than C (say, JavaScript, or Perl), without a lot more smarts. But, it works really well on the C I looked at. Colors are handled in a cute way, too.
All around, I'm just astounded at how concise this is, while still being entirely readable and comprehensible. This isn't clever/tricky golfing code, this is actually human-readable C code.
Edit: Also, I'm struck by how nice/simple the editorProcessKeypress handling is. Trying to do something like this in JavaScript/HTML5 is crazily more complex and verbose. Seeing something like this reminds me how messy user interaction still is in the browser, compared to old CLI and desktop paradigms.