11

Suppose I'm writing a C program in a file foo.c:

int add_numbers(int x, int y, int z) {
    // Very complex implementation here.
    return x + y + z;
}

I want a command that will insert a corresponding function prototype in foo.h:

int add_numbers(int x, int y, int z);

Are there any existing Emacs solutions for this?

9

UPDATE: I created Semantic Refactor package that completely solves this problem and more. You can look at the demos to see how it works. The remaining text of this answer, after this sentence, is old and put it there just for historical reason.

OLD ANSWER:

You can use senator-copy-tag for accurately copy function signature, then paste back in your source file. senator-copy-tag and Senator commands are available when you enable semantic-mode:

(semantic-mode 1)

Semantic is an Emacs built-in package.

You can combine Semantic Senator with Projectile into a command to insert function prototype into other file (file with the same name but different extension) from anywhere in your project. If there's only one other file, the command inserts immediately into that file; if more than one, you are prompted to select a file; if there's none, you are prompted for the entire files in your project. After you select a file, a prompt offers a list of Semantic tags in the current buffer that you can choose to insert after.

I submitted a PR to Emacs Refactor. Full code if you want to try without waiting for the PR: click here.

Here is a demo (it starts when you see START DEMO at the bottom):

projectile-semantic-insert-function-prototype

You can also use only Senator to copy and function prototype. As long as point is anywhere inside the function signature or function body, run senator-copy-tag, which is bound to C-c , M-w by default, it copies the whole function: both the signature and body. However, you can paste only the signature if you want by running the command senator-yank-tag, which is bound to C-c , C-y by default. Pressing C-y paste the whole function signature along with its body. senator-copy-tag even works with function signature expanded on multiple lines like this:

void 
func(int a,
     int b, 
     int c)
{
    .....
}

Although this approach does not insert directly into buffer with the same name, it is more applicable in other cases. Your use case only works if you have two file in the same directory and with the same name but different extensions. What if the function declaration and function definition must stay in different files with different names?

EDIT2: Here is an example for smart insertion of function prototype using Semantic tags. Currently, you can only insert based on the relative positions ("before" and "after") of top level Semantic tags. I will update to make user to be able to insert in any place where Semantic tags are available, with more positions (i.e. when a tag is a Class, it should offer additional positions: public, projected and private). The demo starts when you see START DEMO at the bottom:

projectile-semantic-insert-function-prototype2

Bonus: If you want to generate a list of empty function definitions in a .cpp file from a header file, use member-functions.el. But soon, I will replace it with Semantic + Projectile.

3

The following command should do it. It passed my tests, and has no external dependencies.

(defun endless/copy-proto-to-header-file ()
  (interactive)
  (save-excursion
    ;; c-mode's `beginning-of-defun' should be robust enough.
    (beginning-of-defun)
    (let ((l (point)))
      (search-forward-regexp " *{")
      (let ((proto (buffer-substring l (match-beginning 0))))
        (ff-find-other-file)
        ;; If other file is already open, we don't want to move point.
        (save-excursion
          (goto-char (point-max))
          ;; Do some more movement here if you want.
          (insert "\n" proto ";"))))))
2

I think the following should work (it works for me):

(defun c-copy-function-signature-to-header ()
  (interactive)
  (save-excursion
    (let ((last-point -1))
      (while (/= (point) last-point)
        (setq last-point (point))
        (sp-backward-up-sexp))
      (kill-ring-save (point) (line-beginning-position))
      (ff-find-other-file)
      (yank)
      ;; clean whitespace between closing paren and opening curly 
      (delete-trailing-whitespace
        (line-beginning-position)
        (line-end-position))
      (insert ";")))

This requires smartparens, although you might be able to hack up your own copy of sp-backward-up-sexp if that is unacceptable.

Also note that this will put the header wherever you left the (point) last in the header. As you didn't specify where you wanted it, I thought this was probably best. Of course, if you want it to append at the end, you can put an (end-of-buffer) before the (yank). Of of course you can add fancier stuff like creating a new line near the point and yanking there, but that's up to your taste.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.