Hacker News new | past | comments | ask | show | jobs | submit login

> "I want to extend my tools with code, not configuration."

You can do that with Visual Studio too with Visual Studio Extensions. You can also manage extensions (including those from other users) with a built-in package manager, just like in Emacs.

Here's a Visual Studio Extensions tutorial if you're interested:

https://channel9.msdn.com/Shows/Visual-Studio-Toolbox/Buildi...




Yes, but writing a Visual Studio extension (or Eclipse, etc.) is much higher friction than Emacs. In emacs, I can just open a buffer in emacs-lisp-mode, write a quick function, immediately eval it and start using it. The instant feedback means that quick, small customizations are very low cost. It's like the difference between writing a code generator for Java vs using macros in Lisp.


Visual Studio had a pretty nice macro record and playback system that was great for ephemeral, low friction scripts. They removed it because they said nobody used it.

I switched to AutoHotKey and now I have a macro system that works everywhere.


I see. Can you give me an example of a function you wrote in that way?


Here are three of mine:

    (defun tom/insert-sterling-symbol ()
      (interactive)
      (insert "£"))

    (defun tom/set-buffer-modified ()
      (interactive)
      (set-buffer-modified-p 't))

    (defun tom/unix ()
      "Set buffer to unix line endings"
      (interactive)
      (set-buffer-file-coding-system 'unix))
The last one exists only because I could never remember the right command to select Unix-style line endings. So rather than train myself to learn it, I just wrote a small wrapper with an obvious (to me...) name, because it's so easy to do that.

I've written some Visual Studio addins as well... the experience is not really comparable. (Visual Studio VBA macros, when it supported that, were painful too.) emacs makes it a lot easier.


Incidentally, C-x 8 is by default bound to a keymap that includes a lot of glyphs not often found on keyboards. You can insert £ with C-x 8 L, which might be more convenient than M-x tom/insert-sterling-symbol RET.


Not OP, but a very quick and simple example of something I put in my .emacs very long time ago:

  (defconst trc-comment-keywords "\\<\\(FIXME\\|TODO\\|BUG\\|HACK\\|NOTE\\|WARNING\\|ERROR\\)")

  ;; Install the word coloring
  (defun add-comment-keywords ()
    (font-lock-add-keywords nil
                            `((,trc-comment-keywords 1 font-lock-warning-face t))))

  (add-hook 'find-file-hooks 'add-comment-keywords t)
  (set-face-underline 'font-lock-warning-face "yellow") ; Just make sure we'll see it
This defines a function #'add-comment-keywords and adds it as a hook to the "open file" operation. The function highlights all matches to the regexp in trc-comment-keywords so that they're highlighted in the source code using a modified font-lock-warning-face.

This is an example of something you could just type in and evaluate right there, in your buffer. Later, you can save it in your init file to have it always enabled.

A more complicated example of related code I wrote with some help from the Internet and the Emacs documentation:

  (defun list-comment-notes ()
    "List all TODO/FIXME/HACK, itp. in a new buffer for reference."
      (interactive)
    (save-excursion
      (goto-char (point-min))
      (let ((collected-lines '()))
        (while (re-search-forward trc-comment-keywords nil t)
          ;; collect lines
          (setq collected-lines (cons 
                                 (format "%d: %s" (line-number-at-pos) (grab-current-line))
                                 collected-lines)))
  
        ;; generate a new buffer
        (let ((notes-buffer (generate-new-buffer (concat (buffer-name) "-comment-notes"))))
          (set-buffer notes-buffer)
          ;; dump collected stuff to here.
          (dolist (a-line collected-lines)
            (insert a-line)
            (insert "\n"))))))
What it does is it scans the current buffer for all occurrences of the keywords and lists them in a new buffer (with "-comment-notes" appended to the name). It's a crude function that could use some improvements, but it works well enough and is now just a M-x list-comment-notes away. Or, with the way M-x works, just M-x l-c-n away. Or I could bind it to a key.

The great thing about Emacs is that all you need to extend it is to type the source somewhere. No need for creating projects, build scripts, rebooting your editor, etc. You can evalute and reevaluate the code until it does what you want. Lisp interactivity, which Emacs inherits, is addicting and still unparalleled in other programming languages / environments.


Not OP, but I recently installed the Emacs flavor of the widely used Emmet HTML generation tool, and I found myself wanting to customize some aspects of its behavior so that they better suited our in-house style.

I could've edited the Emmet source code in place, but to do so would've meant losing my customizations next time I upgraded the package. I could have forked it, but that would mean having to manually merge changes into the fork every time a new release comes out.

Instead, after a couple hours of reading the Emmet source and fiddling around in an Emacs Lisp buffer, I ended up with this:

    (defcustom emmet-verticalize-html-attributes nil
      "Whether to verticalize attributes in HTML tags.
    
    \"Verticalize\" here means to separate each pair of attributes by
    newline and enough indentation to line up the first character of
    attribute names. For example, the unverticalized production
    
    <div id=\"foo\" class=\"bar\"></div>
    
    when verticalized becomes
    
    <div id=\"foo\"
         class=\"bar\"></div>"
      :type 'boolean
      :group 'emmet)
    
    (defcustom emmet-verticalize-minimum-attributes 3
      "The minimum number of attributes required for
    verticalization. If a tag has fewer than this many
    attributes. they will not be verticalized, even when
    verticalization is enabled.
    
    Values less than 2 have no effect, since verticalization only has
    an effect on tags with at least two attributes."
      :type 'integer
      :group 'emmet)
    
    (defun emmet-verticalize-html-tag (orig &rest args)
      (let ((html (apply orig args))
            (attribute-count 0))
        (if (null emmet-verticalize-html-attributes)
            html
            (save-match-data
              (with-temp-buffer
                (insert html)
                (html-mode)
                (goto-char (point-min))
                (while (re-search-forward (pcre-to-elisp "^\\<[^\\/]\\S+") nil t)
                  (while (and (not (= (point) (point-max)))
                              (not (looking-at (pcre-to-elisp "\\/?>")))
                              (re-search-forward (pcre-to-elisp "\\w[\\w\\d_-]+\\=\\\".*?\\\"" ) nil t))
                    (setq attribute-count (1+ attribute-count))
                    (and (not (looking-at (pcre-to-elisp "\\/?>")))
                         (newline-and-indent))))
                (indent-region (point-min) (point-max))
                (if (>= attribute-count emmet-verticalize-minimum-attributes)
                    (buffer-substring-no-properties (point-min) (point-max))
                    html))))))
    
    (advice-add 'emmet-make-html-tag :around #'emmet-verticalize-html-tag)
    
    (defcustom emmet-indent-html t
      "Whether or not to indent generated HTML according to the same rules
    used by `html-mode'.
    
    When non-nil, Emmet-generated HTML will be indented via
    `html-mode', with a newline after every closing angle bracket.
    
    When nil, Emmet-generated HTML won't be post-processed in this
    way. (The version of Emmet I'm using, 1.0.8, applies no
    indentation whatsoever.)"
      :type 'boolean
      :group 'emmet)
    
    (defun emmet-indent-html-snippet (html)
      (if (not emmet-indent-html)
          html
          (save-match-data
            (with-temp-buffer
              (insert html)
              (goto-char (point-min))
              (while (and (re-search-forward (pcre-to-elisp "\\>") nil t)
                          (not (eobp)))
                (newline))
              (goto-char (point-min))
              (while (search-forward "\n\n" nil t)
                (replace-match "\n" nil t))
              (html-mode)
              (indent-region (point-min) (point-max))
              (buffer-substring-no-properties (point-min) (point-max))))))
    
    (advice-add 'emmet-make-html-tag :filter-return #'emmet-indent-html-snippet)
...which not only implements the behavior I desire, and does so without modifying the original source or breaking my ability to upgrade Emmet, but also adds customization options to those provided by stock Emmet, so that I can adjust the way my Emmet extensions behave without having to touch the code. (Not that touching the code is any great hardship, but it's nice to be able to adjust the way these extensions behave in the same interface I use to make other minor adjustments to the way Emmet works.)

Shortly after I copied that code into the Emacs buffer I'm using (via Firefox's "It's All Text!" extension) to edit this comment, but before posting, I inadvertently killed Emacs's message buffer, which contains a running log of everything displayed in the editor's echo area. I wasn't prompted for confirmation before killing that buffer, but I felt I should've been. So I opened an Emacs Lisp buffer and wrote this:

    (defun defend-message-buffer (kill-fun &rest args)
      (let* ((buf (car args))
             (buf-name (if (stringp buf)
                           buf
                           (substring-no-properties (buffer-name buf)))))
        (if (string= buf-name "*Messages*")
            (and (yes-or-no-p "Really kill the message buffer? ")
                 (funcall kill-fun buf))
            (funcall kill-fun buf))))
    
    (advice-add 'kill-buffer :around #'defend-message-buffer)
And now I am.


"just like in Emacs."

There are many thing you can do, but it really, really isn't just like emacs.


> "There are many thing you can do, but it really, really isn't just like emacs."

"a built-in package manager, just like in Emacs."

They both have built-in package managers for extensions. I don't think this is a controversial statement.


That is fair - I mis-read "just like emacs" in your original. Apologies.

The extension mechanism really isn't like emacs at all. Package management is a bit more similar (although can you easily add new repositories for nuget?)


> "although can you easily add new repositories for nuget?"

Yes. Here's a guide showing how to create a local Nuget repository:

http://codurance.com/2015/05/04/creating-a-local-nuget-repos...

In the guide you can see this screenshot:

http://codurance.com/assets/img/custom/blog/2015-05-01-creat...

Which shows you one way to set up the package source.

Also worth mentioning Paket, which is a Nuget replacement. In addition to working with the standard Nuget library, Paket can pull dependencies directly from Git repos.

https://fsprojects.github.io/Paket/


cool, thanks.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: