Showing posts with label emacs. Show all posts
Showing posts with label emacs. Show all posts

Saturday, 7 November 2009

Emacs mark-stack

There's no worst feeling than being lost in your code. You know, when you need to look at a bunch of code fragments buried in other files or in the same, very large, file; or having to trace back a bunch of function calls spanning multiple emacs buffers and not knowing where the previous stuff was.
Okey, there are probably a lot of feelings that are worst. But you gotta give me it's annoying.

If only there was a way to push positions to a stack and then come back to them later on! I know about C-@ and C-U C-@, but if you use delete-selection-mode or anything else in which you do a lot of marks that's not an option. Plus, the default key bindings suck and it's only buffer local. So I decided to write my own mark system in emacs lisp.

Before people come rushing down to the comments to tell me there's already a better solution for this problem, take into account that I didn't look much into it. I did this not only to solve the problem but to practice my elisp. Nevertheless, if there are alternatives I would love to hear about them; so comment on, just don't troll =)

If you don't know emacs-lisp there's a guide by Xah Lee I can't recommend enough.

Design


So I want two things: (i) To be able to push and pop marks locally to a buffer and (ii) to be able to pop marks globally. The later means that if I push marks in two different buffers and I do a global pop, the current buffer will change to the one with the latest mark.

To do this, I used two datastructures: A hash table that maps a buffer name to its stack, and a global stack that keeps all the pushed positions.

The first one looks like this:


"marks.el" -> (1622 1042 1283)



((1622 "marks.el") (1042 "marks.el") (194 "*Messages*") (1283 "marks.el"))


In the global stack we keep the name of the buffer so we can move to it and remove the mark from the right stack.

So, onto the code.

Code


First we need a helper function to add the list to the hash if it doesn't exist already or push to the stack on an existing list


(defun add-or-push (key value hash)
(if (gethash key hash)
(progn
(puthash key (cons value (gethash key hash)) hash))
(progn
(puthash key (list value) hash))))


And some functions to clear the local and global stack should things go wrong.


(defun clear-push-mark-for-buffer ()
"Resets the buffer's stack"
(interactive)
(puthash (buffer-name) () local-mark-stack))

(defun clear-global-push-mark ()
"Resets the buffer's stack"
(interactive)
(setq global-mark-stack '())
(maphash (lambda (kk vv) (puthash kk () local-mark-stack)) local-mark-stack)
)


Now, the real code:

For pushing, we just need one function that adds the current possition to both datastructures:


(defun local-push-mark ()
"Pushes a the current point to a stack"
(interactive)
(if (boundp 'local-mark-stack)
(progn
(let (buffer point)
(setq buffer (buffer-name))
(setq point (point))
(add-or-push buffer point local-mark-stack)
(message "Pushed %d on %s" point buffer)
(if (boundp 'global-mark-stack)
(setq global-mark-stack (cons (list point buffer) global-mark-stack))
(setq global-mark-stack (list point buffer)))))
(progn
(setq local-mark-stack (make-hash-table))
(local-push-mark))))


For popping, however, we need a function for the local stack and another for the global stack


(defun local-pop-mark ()
"Pops the a mark from the current buffer's stack"
(interactive)
(let (stack)
(setq stack (gethash (buffer-name) local-mark-stack))
(if (and (boundp 'local-mark-stack) stack)
(progn
(goto-char (pop stack))
(puthash (buffer-name) stack local-mark-stack)
(setq global-mark-stack
(remove (list (point) (buffer-name)) global-mark-stack)))
(message "No marks to pop"))))

(defun global-pop-mark ()
"Pops a mark from any buffer"
(interactive)
(let (pos buffer)
(setq pos (car global-mark-stack))
(setq buffer (nth 1 (car global-mark-stack)))
(setq stack (gethash buffer local-mark-stack))
(if (and (boundp 'global-mark-stack) stack)
(progn
(switch-to-buffer buffer)
(goto-char (pop stack))
(puthash buffer stack local-mark-stack)
(setq global-mark-stack (remove (list pos buffer) global-mark-stack)))
(message "No marks to pop"))))


the only important detail to notice is that the global pop removes both from the local and global stacks. The rest should be self-explanatory.

And we add a statement to provide the marks module


(provide 'marks)


So now, we just need to load the module and add some key bindings:


(require 'marks)
(global-set-key (kbd "C-x ") 'local-push-mark)
(global-set-key (kbd "C-x ") 'local-pop-mark)
(global-set-key (kbd "C-x S-") 'global-pop-mark)


Using it.


If your using my .emacs you just need to ckeck out the code and compile it.

If not just copy everything up to " (provide "marks") " into a file called marks.el and load it in your .emacs.

Enjoy.

Edit: Apparently there's no lisp syntax support for the highlighter I'm using. sorry for that.


Saturday, 28 February 2009

Publishing an emacs buffer

Yes, another emacs post, in row, but I found this so amazingly cool that I couldn't just keep quiet.

I told myself today: "It would be cool if I could convert what is rendered in an emacs buffer to html so I can show it to others". Of course, some people had already beaten me to it.


Making it happen

I used an emacs add-on by Hrvoje Niksic called htmlize. It does all the hard work, and it does it very well.

Still, I wanted a one key solution to publish it. So I just coded these small emacs-lisp functions:


(defun publish-buffer-to (file)
"Converts buffer to html and writes it to file"
(interactive "Ffile: ")
(require 'htmlize)
(save-excursion
(with-current-buffer (htmlize-buffer (current-buffer))
(write-file file)
(kill-buffer (current-buffer))))
(message (concat "current buffer contents published to " file)))

(defun publish-buffer ()
"Converts buffer to html and writes it to ~/public_html/emacs.html"
(interactive)
(publish-buffer-to "~/public_html/emacs.html"))

(snnipets provided by htmlize! =) )

Now all that was left was to provide a simple key shortcut.


(global-set-key [f7] 'publish-buffer)


And thanks to Ruslan Spivak's code I could also add an easy way to create code snippets out of regions.

So now I can easily share my emacs buffers not only on my local server but on my blog =).

Apparently there's another module to do this that might be added to emacs soon, but for now I'm very happy with this solution.

In the future, I'd like to integrate this with tramp and add better file management support so I can seamlessly post to a remote server. I would also like to make it autoreload so it can track live changes, but we'll see.

If you liked this, you can check out my .emacs


Sunday, 22 February 2009

My .emacs

I have been delaying this post for quite a while. I've been waiting until my .emacs file is "complete". Today, I realised it is never going to be complete. So here's a few notes regarding my emacs configuration and how you can use it.

Order

I must confess, a few weeks back my emacs configuration consisted of one HUGE file and a few emacs lisp files scattered in a directory. Also, half of the tools and modes I used were installed in a system wide manner via the OS's packaging system. ...A mess.
I decided to put some order (beauty?) to it. So now my .emacs file is 9 non-comment non-whitespace lines and the rest of the configuration is well distributed several different in appropriately named files and directories. With all tools included it is 535149 LOC.

Mobility (sort of)


I love working in emacs so much that I consider most other editing options a pain. I've even considered implementing emacs-lisp in javascript so I can turn every textarea into a fully functional emacs buffer (this is way down in my list of personal-projects-i-will-do-someday due to its complexity and my lack of free time)[1].

Being a little more realistic, I decided just to put my .emacs in a git repo that I could access everywhere and which included all the things I normally use when developing (or writting in general). This way I could have all my tools everywhere customized the way I want them.
With this approach installing new tools (like JDEE) becomes a little more complicated because they are commonly packaged to be installed in a system wide manner. Also, I cannot use my OS packaging system. But the upside of having everything in one places pays off.

So, why does it say "sort of" in the title? Well, some of the tools (the ones that need non-emacs-lisp components) are compiled for my system (A 64-bit Arch Linux box). So you might have problems with them if you just clone my emacs repo in a different system.
What can go wrong? Mostly there might be problems with JDEE and AucTex. But besides that I don't see much that could go wrong in a unix system.

Goodies

There are not many new and amazing things. So here's a non-exhaustive list of stuff I use every day:
* Color themes
* git support
* JDEE
* Auctex
* ECB
* word-to-emacs (open .doc files in emacs)
* some modes: (java, haskell, erlang, javascript, etc)
* some key-bindings:
* C-x p: go to matching paren
* f9: cross highlight (line and column)
* f11: fullscreen
* f10: remove scrollbar (combined with f11 it makes a great fullscreen experience =) )
* M-/ M-: move line up/down
* f8: make frame on display
* f5: go-to-line
* enhanced clipboard interaction
* in place annotations (I'd like to make this one better)
* automatic backups
* tramp
* everything else that I forgot

What's missing


Currently I am missing:
* Slime (I haven't used Lisp in quite a while, but I'd like to start using it again. Wait for my Clojure exploration)
* Better Python support. The default mode is very good. But I'd love to have more advanced stuff like Ropemacs.
* Bookmarks. A personal project I might add soon.
* Probably some stuff I'm forgetting

Sharing


I very often get asked by firends for my .emacs file (or updates from older ones). So here's a way anyone can go and use my emacs configuration:
Go to github and get a copy of my emacs repo:

> git clone git://github.com/nicolaslara/emacs.git ~/elisp
> ln -s ~/elisp/.emacs ~/.emacs


It doesn't go without a disclaimer: This is, and will always be, an incomplete project. Though it probably is portable, it is made for my personal use and there is no guarantee it will work in different non-linux setups.

Now, I'd love to make this even more portable and add different tools to it. So if anyone have a problem or suggestion I'd love to hear it. I would, of course, like it better if the suggestions come with a link to a fork of the project where the problems are fixed and I can just pull it ;)

Comments


So, Whats the emacs feature you like the most? Which one amazes you the most? anything you couldn't live without? What do you miss from other text editors when working in emacs?

Notes

[1] The folks at Mozilla labs have done a very nice contribution to the online editing world with Bespin. I hope they keep on the good work and turn to project into something emacs-like for the web.