Using company completion hook to conform to coding style

Customization question

Hi everyone,

in the GLib/GTK based C programs I’m working on the style guides usually demands a space to be inserted between the function and the opening parenthesis, f.e. g_free (some_variable);

Can anyone give me some pointers how to achieve such a thing?

What I’ve found out so far is that I can set company-completion-finish-hook to point to a custom function.

I’ve used a dummy function to play around with, but I’m not quite sure how to continue:

(defun company-finish-hook (&rest args)
  (message "args: %s" args))
(setq company-completion-finished-hook #'company-finish-hook)

Example

If I complete f.e. g_object_ref(Obj) I can see args: (g_object_ref(Obj)) being printed to my messages buffer.

My idea was to use this hook (if it is the right one for the job) to do some string manipulation and insert a whitespace before the opening parenthesis, but I’m not sure if this is feasible (and if so, how to go about it).

Without knowing too much about company I would imagine that this could be achieved in two different ways:

  • Altering the list completion candidates
  • Manipulating the text after completion finishes

Any advice would be appreciated!

PS: Loving Doom Emacs so far :)

EDIT: Or maybe there’s a different package that can be leveraged for this kind of job?!

System information


Loading data dump...
2 Likes

Hey @devrtz! The Docstring of company-completion-finish-hook states:

Documentation
Hook run when company successfully completes.

The hook is called with the selected candidate as an argument.

If you intend to use it to post-process candidates from a specific
backend, consider using the post-completion command instead.

Looks to me like you would have to see if the company-backend you are using has a post-completion argument that you can customize. If not, you can always create your own backend copy pasting the code of the backend and adding the post-completion argument to do as you want. Which backend does this use?

This seems like an XY problem. I believe the proper way to solve this is configuring a formatter, which will accomplish this more robustly because it doesn’t depend on you always using company to insert functions (and it’s syntax-aware). Unfortunately, I’m not too familiar with this, but I think you’d want to look into whether the upstream projects publish config files for formatters like clang-format, and then integrate that with Doom via the :editor format module. Maybe someone else has more info there?

2 Likes

Hey @danilevy1212, thanks for the pointer!

I’m using company-capf and indeed it has a company--capf-post-completion function.

In your reply you wrote post-completion argument though which leaves me in a bit of a confused state about what needs changing ;)

(defun company--capf-post-completion (arg)
  (let* ((res company-capf--current-completion-data)
         (exit-function (plist-get (nthcdr 4 res) :exit-function))
         (table (nth 3 res)))
    (if exit-function
        ;; We can more or less know when the user is done with completion,
        ;; so we do something different than `completion--done'.
        (funcall exit-function arg
                 ;; FIXME: Should probably use an additional heuristic:
                 ;; completion-at-point doesn't know when the user picked a
                 ;; particular candidate explicitly (it only checks whether
                 ;; further completions exist). Whereas company user can press
                 ;; RET (or use implicit completion with company-tng).
                 (if (= (car (completion-boundaries arg table nil ""))
                        (length arg))
                     'sole
                   'finished)))))

Poking the code it seems like arg is only used with the exit-function and company-caps--current-completion-data is a global holding the completion data.

Enabling tracing of the function (thanks to SPChf) shows that it get’s called like so:

1 -> (company--capf-post-completion #("g_object_ref(Obj)" 0 1 (lsp-completion-item #4=#s(hash-table size 12 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("filterText" #("g_object_ref" 0 12 (match-data (0 7 0 1 1 2 2 3 3 4 4 5 5 6 6 7))) "insertText" "g_object_ref(${1:Obj})" "insertTextFormat" 2 "kind" 1 "label" "g_object_ref(Obj)" "score" 0.8436345458030701 "sortText" #1="40a80791g_object_ref" "textEdit" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("newText" "g_object_ref(${1:Obj})" "range" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("end" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 9 "line" 649)) "start" #s(hash-table size 2 test equal rehash-size 1.5 rehash-threshold 0.8125 data ("character" 2 "line" 649)))))) "_emacsStartPoint" 19531)) lsp-sort-text #1# lsp-completion-start-point 19531 lsp-completion-markers #3=(19531 #<marker (moves after insertion) at 19548 in calls-srtp-utils.c>) lsp-completion-prefix #2="g_objec" face (completions-first-difference)) 1 17 (lsp-completion-item #4# lsp-sort-text #1# lsp-completion-start-point 19531 lsp-completion-markers #3# lsp-completion-prefix #2#)))
1 <- company--capf-post-completion: nil

Wow, quite a lot of information.

I’m (currently) at a bit of a loss at which parts need changing.

  • Redefining company--capf-post-completion (something something advice?)?
  • Changing something about the args?

I’m not super familiar with how company-backends really works, so I am going of documentation and source. For what I can see, you can’t change the arguments (the args argument) that is feed into the company backend. However, you can change the way it’s processed If it it has a function you can advice, like you pointed out :slight_smile: .

In the documentation for company-backends it says:

post-completion: Called after a completion candidate has been inserted
into the buffer.  The second argument is the candidate.  Can be used to
modify it, e.g. to expand a snippet.

So I am guessing you could add :after advice to company--capf-post-completion function and return the modified candidate. It seems pretty complicated though, you would have to detect the parenthesis and insert the space there, and I’m not sure if it would work.

I would take @liam 's approach, the formatter seems like a simpler approach. Not a C programmer so I don’t know if you could reuse an already existing tool, I’m guessing you could.

1 Like

I found for clang-formatter you can use SpaceBeforeParens (SpaceBeforeParensStyle) clang-format 3.5 rule! I think it’s what you wnat @devrtz! This is the default formatter for c/c++ in :editor format module. Seems like you would just need to install the dependency and configure the .clang-format file :slight_smile:

2 Likes

Hi @liam

you might be right about the nature of the problem ;)

I’m pretty novice when it comes to customizing my emacs (apart from changing some variables) and my lisp-fu is certainly lacking aswell (I worked through David S. Touretsky’s book on Common Lisp, but that about covers my lisp experience).

However I’m not afraid to getting my hands dirty and thank you for the pointers toward configuring a formatter :)

As a matter of fact I’ve just come across clang-format configurator which will make this super nice for the projects I’m working on :)

1 Like

@danilevy1212 seems you beat me to it! :)

Once I figured out all the necessary steps I will post them and mark as the solution.

EDIT: While drafting a post I realized there wasn’t really much more to it then the advice given already, so instead I’ll just mark an existing post as solution.

Thanks a lot @liam and @danilevy1212 for your advice!!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.