Open concretevitamin opened 6 years ago
clang-format.el
just passes buffer-file-name
(which is something like "/ssh:host:/path/to/file"
) to clang-format via the -assume-filename
flag. As clang-format can not make sense of this file name it (presumably) uses its default style. I see two options:
clang-format
on remote host@kljohann Thanks! Both options work for me and will be tremendously helpful. For Option 2, most remote machines I work with have clang-format
installed either in /usr/bin
or /usr/local/bin
, with the former being the more popular.
Looking forward to any updates on this!
Just to clarify: I was only trying to come up with possible ways to implement this. As I do not use clang-format via tramp I do not plan to work on this feature. :) If you come up with a patch the best way forward would be via the mailing list or phabricator.
Being a elisp naif, would love a hint on how to run clang-format on tramp.
I would really like this package to support remote connections with tramp as well.
I've tried to look a bit into it, but my knowledge of elisp is very limited to say the least.
As far as I can tell, the issue is, that clang-format.el calls call-process-region
to actually execute the clang-format process which does not respect/support remote connections.
Instead something like process-file
should be used instead which does some magic to support remote connections based on the value of the default-directory
variable. It seems like other emacs packages which do support remote connections does something like that so it works transparently.
I've found a file in the tramp package called tramp-util.el:
https://github.com/jwiegley/tramp/blob/master/lisp/tramp-util.el
which seems to wrap the standard emacs process handling functions, but I haven't been able to figure out how to use that or copy the relevant parts, again because of my limited elisp knowledge.
I assume this should be fairly simple to implement for someone more experienced in elisp, but unfortunately that's not me (although I'm willing to try and look a bit more into it).
Anything else I can do to get this supported?
Thanks a lot!
The workaround: I created a directory on my local machine matching the tramp path. And copied .clang-format file inside.
For example I downloaded .clang-format into /tmp/
$ sudo -i
# mkdir -p /scp:remote:/home/remote_user/prj/game/Source/Server/src/Core/
# mv /tmp/.clang-format /scp\:remote\:/home/remote_user/prj/game/Source/
# chown chown -R local_user /scp\:remote\:/
# exit
$
Here's how I was able to work around this issue:
.clang-format
file from the remote location into my home directory.--assume-filename
argument with a path referring to a file with the same name in the home directory: (defun my-clang-format-region ()
(interactive)
(let* ((start (if (use-region-p) (region-beginning) (point)))
(end (if (use-region-p) (region-end) (point)))
(assumed-filename (if (file-remote-p buffer-file-name)
(concat (getenv "HOME") "/" (file-name-nondirectory buffer-file-name))
buffer-file-name)))
(clang-format-region (region-beginning) (region-end) clang-format-style assumed-filename)))
(global-set-key '[(control meta tab)] 'my-clang-format-region)
It seems that --assume-filename
can specify a path to a file that doesn't exist. All clang-format seems to care about is the file's extension and directory path; it uses the directory path as a location to look for the .clang-format
file. If it doesn't find the file there, it looks in every ancestor directory starting from that location.
This worked for me with versions 9.0 and 10.0 of the clang-format
executable, and clang-format.el
version 20190824.2216 from melpa.
(Cross-posted as an answer to the original poster's question on StackOverflow.)
(Edited 2020-06-16: fixed the global-set-key function call, which could cause key binding problems if used in the way that I originally posted.)
@richls Cheers, mate. There is a little mistake in your function though, you don't use start
and end
. Also a recursive let*
binding is not necessary and let
suffices. Here is the fixed version:
(defun my-clang-format-region ()
(interactive)
(let ((start (if (use-region-p) (region-beginning) (point)))
(end (if (use-region-p) (region-end) (point)))
(assumed-filename (if (file-remote-p buffer-file-name)
(concat (getenv "HOME") "/" (file-name-nondirectory buffer-file-name))
buffer-file-name)))
(clang-format-region start end clang-format-style assumed-filename)))
Right you are, mate! I also found that my argument to global-set-key was wrong — it should be:
(global-set-key '[(control meta tab)] 'my-clang-format-region)
Currently the function translates any remote name to be located in the home directory,
(if (file-remote-p buffer-file-name)
(concat (getenv "HOME") "/" (file-name-nondirectory buffer-file-name))
buffer-file-name)
i.e. /ssh:jdoe@host:/home/jdoe/baz.cpp
becomes ~/baz.cpp
. This might or might not be what you want.
Sometimes it is more convenient to use instead
(file-local-name buffer-file-name)
which will translate /ssh:jdoe@host:/home/jdoe/baz.cpp
into /home/jdoe/baz.cpp
. However, this is only really useful if the remote directory structure mirrors the local one (including the user name).
Therefore I have come up with this monstrosity which will use /home/jdoe/baz.cpp
if it exists and ~/baz.cpp
if it doesn't.
(if (file-remote-p buffer-file-name)
(let ((local-file (file-local-name buffer-file-name)))
(if (file-readable-p local-file)
local-file
(expand-file-name (file-name-nondirectory buffer-file-name) "~")))
buffer-file-name)
The reason I suggested translating file names to the home directory is that I put a copy of the remote .clang-format
file in my home directory. This way, the local clang process always uses that format file for any .cpp file, no matter what path the .cpp file is in on the remote machine and even if that path doesn't exist locally.
If you need to work on multiple projects with different .clang-format
files, you'll need to find a way of mapping each remote path to its own local .clang-format
file. In my case, the code I work on is scattered across so many subdirectories that it would be impractical to mirror the entire path structure on my local box, but I only ever need one format file. Other situations may require different approaches. It would be a nice compromise if, say, you only needed to mirror paths as far as the directory containing the .clang-format
file, and not below that.
To combine the above with clang-format+-mode
:
;; Fix clang-format (and clang-format+ mode) in tramp mode.
(defun tramp-aware-clang-format (orig-fun start end &optional style assume-file-name)
(unless assume-file-name
(setq assume-file-name
(if (file-remote-p buffer-file-name)
(concat (getenv "HOME") "/" (file-name-nondirectory buffer-file-name))
buffer-file-name)))
(apply orig-fun (list start end style assume-file-name)))
(advice-add 'clang-format-region :around #'tramp-aware-clang-format)
To build on the answer from @richls, you can also change the default value of clang-format-style
to a local file with:
(setq-default clang-format-style "file:<path to local .clang-format>")
clang-format-style
refers to the --style=
option in the clang-format
CLI.
You should also look into "Per-Directory Local Variables" in Emacs if you have multiple different remote projects that do not use the same format.
(This is a cross-post from https://stackoverflow.com/questions/48678302/emacs-tramp-mode-does-not-respect-clang-format-in-the-remote-directory. I couldn't find any maintainer information on elpa so I only traced it here; apologies if this is not the right place.)
I have a git repo with a
.clang-format
and asimple.c
source code. In local mode, namely editing off local disk, it respects the style file. However if I edit using tramp mode the same repo, thenclang-format
will format the source code using some default style file (I don't know where it's picked up), and will NOT honor the style file existing in the same remote directory.Q1: How do I fix this?
Q2: (Maybe it's easier) Where does clang-format in tramp-mode pick up the style file by default?