17

I would like to use the SE flavor of markdown in my emacs. The default Markdown mode has some features (backticks and indentation mark code, # makes a header and > also changes the font) but I would also like to have:

  • * to make a list item, including indentation.
  • [foo](http://example.com) to show up as foo and open a browser to http://example.com when clicked.

Ideally, I would like this to be shown in my emacs as rendered Markdown. For example, if I were to write:

## Header

* item 1
* item 2

         while true; do echo foo; done

End of list and more code

    while true; do echo bar; done

 See [here](http://example.com) for details

I'd like it to be rendered like this in emacs itself:


Header

  • item 1
  • item 2

    while true; do echo foo; done
    

End of list and more code

while true; do echo bar; done

See here for details


Can this be achieved and, if so, how?

terdon
  • 745
  • 6
  • 21
  • About the code block being rendered: doesn't Markdown mode highlight code blocks for you already? What were you looking for there? – Malabarba Oct 06 '14 at 21:27
  • Also, do you want this to be editable, like a WYSIWYG Markdown editor? Or is it enough to display the "compiled" output in a separate buffer? The latter is rather simple, the former is a coding behemoth. – Malabarba Oct 06 '14 at 21:32
  • @Malabarba, yes, the code is fine. What I would like is i) auto indented lists ii) url handling. Either through `xdg-open` or even just simply displaying the target address when clicked on but the link text when not: "Click [here](http://example.com)" to become `Click [here](http://example.com)` when clicked. Just a quick way to seamlessly include URLs in my text documents. – terdon Oct 06 '14 at 21:55
  • OK, then both of these can be achieved with font-lock-add-keyword. I'll try to write something up tomorrow, if nobody beats me to it. – Malabarba Oct 06 '14 at 22:03

2 Answers2

19

**EDIT: ** Since this writing, it seems that part of the features have been directly implemented in markdown-mode. Check out this comment, and the links therein.


Configuration

There are two approaches you can take.

  1. You can write a command that compiles the markdown code (using a shell command) and displays the html in a buffer.
  2. You can make some customizations a-la org-mode to make the buffer look like rendered markdown.

I explain here how to implement number 2. Simply copy all of the code below to you init file.

Add the font-lock rules

This variable controls how you want lists to look. It adds some space to indent the list, and uses a pretty bullet-point (if your font can display it).

(defvar endless/bullet-appearance
  (propertize (if (char-displayable-p ?•) "  •" "  *")
              'face 'markdown-list-face)
  "String to be displayed as the bullet of markdown list items.")

This is the command that actually adds the rules. There is one for lists and one for links.

(require 'rx)
(defvar endless/markdown-link-regexp
    "\\[\\(?1:[^]]+\\)]\\(?:(\\(?2:[^)]+\\))\\|\\[\\(?3:[^]]+\\)]\\)"
  "Regexp matching a markdown link.")

(font-lock-add-keywords
 'markdown-mode
 '(("^ *\\(\\*\\|\\+\\|-\\|\\) "
    1 `(face nil display ,endless/bullet-appearance) prepend)
   (endless/markdown-link-regexp
    1 '(face nil display "") prepend))
 'append)

Make the link editable

Because we’re using the display property to hide part of the link, we need to tell font-lock that it should erase that property whenever you delete part of the link (that way we can still edit it).

(add-hook 'markdown-mode-hook #'endless/markdown-font-lock)

(defun endless/markdown-font-lock ()
  "Configure aggressive font-locking of `markdown-mode'."
  (define-key markdown-mode-map "\C-c\C-l" #'endless/markdown-insert-link)
  (add-to-list (make-local-variable 'font-lock-extra-managed-props) 'display))

We can also define a command to edit it easily, bound to C-c C-l, like in org-mode.

(defun endless/markdown-insert-link ()
  "Insert or edit link at point."
  (interactive)
  (if (or (looking-at endless/markdown-link-regexp)
          (and (ignore-errors (backward-up-list) t)
               (or (looking-at endless/markdown-link-regexp)
                   (and (forward-sexp -1)
                        (looking-at endless/markdown-link-regexp)))))
      (let ((data (endless/ask-for-link
                   (match-string-no-properties 1) 
                   (or (match-string-no-properties 2)
                       (match-string-no-properties 3)))))
        (if (match-string-no-properties 2)
            (replace-match (cdr data) :fixedcase :literal nil 2)
          (replace-match (cdr data) :fixedcase :literal nil 3))
        (replace-match (car data) :fixedcase :literal nil 1))
    (let ((data (endless/ask-for-link)))
      (insert "[" (car data) "](" (cdr data) ")"))))

(defun endless/ask-for-link (&optional name link)
  (cons (read-string "Text of the link: " name)
        (read-string "URL of the link: " link)))

(Optional) Configure some faces

That should be enough for the points you requested. If you want your buffer to look even more like SE markdown, call

M-x customize-group RET markdown-faces

and change what you see fit. I did some configuring myself, and here’s what I got.

(custom-set-faces
 '(markdown-header-face-1 ((t (:inherit markdown-header-face :height 2.0))))
 '(markdown-header-face-2 ((t (:inherit markdown-header-face :height 1.7))))
 '(markdown-header-face-3 ((t (:inherit markdown-header-face :height 1.4))))
 '(markdown-header-face-4 ((t (:inherit markdown-header-face :height 1.1))))
 '(markdown-inline-code-face ((t (:inherit font-lock-constant-face :background "gainsboro"))))
 '(markdown-link-face ((t (:inherit link))))
 '(markdown-pre-face ((t (:inherit font-lock-constant-face :background "gainsboro")))))

Results

Here’s what you’ll get after the first 2 set of configurations:
enter image description here

Here’s what you’ll get after configuring faces as well. It’s arguable whether this looks better, I’ll personally stick with the one above.
enter image description here

Malabarba
  • 22,878
  • 6
  • 78
  • 163
  • Wow! That's pretty near perfect, thanks. This answers everything I asked for, but for extra brownie points, is there a way to make the link more interactive? I mean, an easy way to extract the URL. Would it be possible to toggle between `[foo]` and `[foo](http://bar.com)` by clicking? I'm not too bothered on the details but would like a way of easily displaying the URL if desired. At the moment, I have to delete one of the brackets for it to appear. I can live with that but I'd be interested in a better way. – terdon Oct 07 '14 at 12:45
  • @terdon Done!.. – Malabarba Oct 07 '14 at 14:01
  • I am going to combine this with [Edit with Emacs](https://github.com/stsquad/emacs_chrome), and then I will take over the Tri-State Area! – nispio Oct 07 '14 at 14:08
  • @Malabarba there seems to be an error there. I get `"Unknown rx form `group-n'"`. Output of `--debug-init` [here](http://pastebin.com/ewKMtpAx). Any ideas? – terdon Oct 07 '14 at 14:28
  • @terdon It's possible group-n is only defined in 24.4, I'll try to check. You can try replacing each of the `group-n X` with just `group`. That _might_ still work. – Malabarba Oct 07 '14 at 15:31
  • No, that let `emacs` load with no error and `C-c C-l` brings up the expected dialogs and correctly inserts `[foo](bar)` but links are back to being displayed as `[foo](bar)` instead of `[foo]`. This is very minor though, you've already provided me with [a version](http://emacs.stackexchange.com/revisions/761/1) that answers my Q perfectly. Cheers! – terdon Oct 07 '14 at 16:15
  • @terdon The current version should work. I just replaced the call to (rx) with an actual regexp. – Malabarba Oct 07 '14 at 16:27
  • Thanks, but that one behaves in the same was with respect to links and also breaks the `*` rendering. I guess I'll just have to upgrade my emacs from 23.4.1 :) – terdon Oct 07 '14 at 16:34
  • @terdon make sure you restart emacs when testing these snippets. I tested it on 24.3 and had no problem, which means you might be having another problem. – Malabarba Oct 07 '14 at 16:37
  • Yes, I did restart but I had also forgotten to load markdown mode (blush) so disregard my last comment :) I've now added the right hooks for it to load automatically. – terdon Oct 07 '14 at 16:43
  • Thanks for these modifications, @Malabarba. Based in part on what you did here, I have added markup hiding to the [development version](https://github.com/jrblevin/markdown-mode) of [Markdown mode](http://jblevins.org/projects/markdown-mode/). Markup for bold, italics, links, etc. can be hidden (and toggled on and off). Also, there are nice bullet characters for lists, with nesting. Variable height headings have been around for a while now, too. – Jason Blevins Jun 13 '17 at 05:17
5

A good place to start would be markdown-mode.el which can be downloaded from here.

This mode does not offer org-mode style beautification, but it does offer syntax highlighting and a bevy of customize options.

In order to get this style beautification, someone would need to write an extension to markdown-mode.el implementing font-faces.

  • Most of org-mode.el's faces are defined in org-faces.el.

  • Additionally you will want to take a look at how org-mode visually replaces text with characters. That code is in org-entities.el. This is the code that replaces the latex \pm with ±.

nixeagle
  • 2,407
  • 3
  • 17
  • 14
  • Thanks, I'm using markdown mode at the moment, as I mention in my answer. I would like to know how to implement the features that are missing and also how to have emacs display them as rendered. Alternatively, I'd like to know that it's impossible to display them rendered if that's the case. – terdon Oct 06 '14 at 19:16
  • It is possible to do so, org-mode does syntax beautification like that. Give me a few moments to find the code. I have done similar coding for another major-mode. I did not realize that you already knew about markdown.el. – nixeagle Oct 06 '14 at 19:19
  • I have edited the answer to provide a way to start doing what you want. When I get home, I might be able to bang up some code that does exactly what you want. As it is, org-mode modifies titles to hide the `*` denoting levels and changes the size of the title based on where it is in the document. It also is able to pretty-format links. – nixeagle Oct 06 '14 at 19:33