Hacker News new | ask | show | jobs
by db48x 308 days ago
This is a common misunderstanding about buffers. Buffers are the base primitive for text manipulation for a good reason; it’s a deliberate design decision not a handicap or a straitjacket. Think of a buffer as a string with a cursor in it, where it is always easy to manipulate the text at the cursor. (It’s implemented using a gap buffer <https://en.wikipedia.org/wiki/Gap_buffer>, but you don’t need to know that.)

The operations you can do on a buffer include not just the usual string primitives like insertion and deletion of text, but _any_ editing command that you could use as a user as well. For example, if you want to move the cursor to the beginning of the line, call `beginning-of-line`. This is exactly the same function that is bound to `C-a`. Want to move to the next line? That’s `next-line`, which is bound to the `down` key by default. Want to drop the mark at the cursor position? Call `set-mark`. Want to search for something? `re-search-forward`. Now that you’ve found what you were looking for, you want to remove it from the buffer? Call `kill-region`, same as if you had typed `C-w`.

And commands build on other, more primitive, commands to give you complex behaviors. There are commands for creating, parsing, and sending emails, making HTTP requests and parsing HTTP responses, creating and parsing JSON, XML or HTML, etc, etc. You can create some JSON in the current buffer, send it off to the server via HTTP, and when the response comes back it swaps you over to a new buffer with the response. Or you can ask it to parse the response and just give you the body, etc. Basically any command you could interactively use as an Emacs user will work as part of a new Elisp program simply by virtue of the fact that it works on the current buffer. And the new Elisp programs you write can be used as commands by the user, and as parts of the user’s next Elisp program.

And switching buffers it not slow; all the buffers are in memory at the same time and the “current” buffer is just a pointer. The only quirk is that the pointer is stored in a variable instead of being passed as an argument to every single command you run. And that’s a good thing, because you would soon get tired of passing the current buffer to every single function you call!

1 comments

I hadn't even realized how much I'd gotten used to emacs' way of thinking about buffers until I tried automating Google Docs.

... What a nightmare. Everything is an object. A header is an object. A paragraph is an object. There's a three-deep hierarchy to start talking about the current document. Yes, it's nice and namespaced and in that sense is well-defined, but the "hello world" of inserting text at the current cursor position is an absolute nightmare. And if you want to insert something more complex, like a couple headers and a paragraph and maybe a table? Get ready to build a factory function that creates a bunch of stuff and glues it together like you're writing a UI in Java AWT. And then it's not even in the doc yet, you have to go find your cursor, find the doc element the cursor is in, and only then can you insert content via a reparenting operation.

In emacs, you could do all of that with repeated "insert" calls, nearly literally touching the same keys you'd use to do it in the editor. It's an almost 1-to-1 metaphor mapping. If you know how to write text in emacs you know half of what you need to script emacs.