How can I edit Google Drive text documents using Emacs and mirror my changes back to the Google Doc?
I found a Google command line program, as well as something called gclient, which is part of Emacsspeak, but they seem to require Linux or Windows, and I'm on OSX using Aquamacs. Or maybe I just don't understand how to install them.
Is this doable?
googlecl can be installed from macports. You can then open files with local emacs using emacs server.
Once it is installed, you can follow these commands:
$ google docs list # gets a list of the files
$ google docs get <FILE> /tmp/edit$$.txt # gets the <FILE> to /tmp/edit$$.tmp
$ emacsclient /tmp/edit$$.tmp
$ google docs upload /tmp/edit$$.tmp <FILE>
However, I've found that google docs get doesn't work as well as it should.
There is no need to Fetch, Edit(emacs), and Push the file back.
First install "Google Drive" on your mac.
Then you can edit the file directly. Look under ~/Google\ Drive.
Another option using gdrive (requires helm for completion)
(defvar gdocs-folder-id "<gdrive folder for exported docs>"
"location for storing org to gdocs exported files, use 'gdrive list -t <foldername>' to find the id")
(defun gdoc-export-buffer ()
"Export current buffer as google doc to folder irentified by gdocs-folder-id"
(interactive)
(shell-command
(format "gdrive upload --convert --mimetype text/plain --parent %s --file %s"
gdocs-folder-id buffer-file-name)))
(defun gdoc-import-buffer (doc)
"Import a file in gdocs-folder-id into current buffer"
(interactive
(list
(completing-read "Choose one: "
(split-string
(shell-command-to-string
(format "gdrive list -q \"'%s' in parents\"" gdocs-folder-id)) "\n"))))
(insert (replace-regexp-in-string (string ?\C-m) (string ?\C-j) (shell-command-to-string
(format "gdrive download -s --format txt --id %s" (car (split-string doc " ")))))))
It all seems to work if you open from a terminal, e.g.:
open -a emacs "Google Drive/My Drive/path/to/file.txt"
Saving the file works, but you can't open a new file once you're in emacs, and Google Drive seems to get confused if you try to create a new file from within emacs.
Related
I love eshell's TRAMP integration. With it I can do cd /ssh:foo:/etc to
ssh into a remote machine and visit its /etc/ directory. I can also do
find-file motd to open this file in my local emacs. However, what if I need to use sudo to change the file? I know I can give the
full path, like so:
find-file /sudo:foo:/etc/motd
but is there a way to open the file via TRAMPs sudo support, without having to type the full path?
I managed to came up with the following eshell alias that works for me:
alias sff 'find-file "${pwd}/$1"(:s/ssh/sudo/)'
It should be fairly obvious what it does. It prepends the working directory
path, but with the string ssh replaced by sudo. Thus it only works for
remote files accessed over ssh. I rarely edit files using sudo locally, so
that's not a problem for me. However, we can make it work for local files too, at the cost of complexity:
alias sff 'find-file "${pwd}/$1"(:s,^,/sudo::,:s,::/ssh:,:,)'
That is, prepend /sudo:: (which is how to sudo for local files) and
subsequently replace any ocurrence of ::/ssh: with :. (I would have just removed :/ssh:, but eshell's :s/// construct didn't accept an empty
replacement.)
I found an alternative answer that works very well over at EmacsWiki.
Using that you'd still open the file with find-file as usual, but then
invoke M-x sudo-edit-current-file (shown below) to re-open the file as root
using Tramp. I think this is a very elegant solution, because often I
initially just want to look at a file, then later find that I need to edit it.
Here's the function, in case it disappears from the page above:
(set-default 'tramp-default-proxies-alist (quote ((".*" "\\`root\\'" "/ssh:%h:"))))
(require 'tramp)
(defun sudo-edit-current-file ()
(interactive)
(let ((position (point)))
(find-alternate-file
(if (file-remote-p (buffer-file-name))
(let ((vec (tramp-dissect-file-name (buffer-file-name))))
(tramp-make-tramp-file-name
"sudo"
(tramp-file-name-user vec)
(tramp-file-name-host vec)
(tramp-file-name-localname vec)))
(concat "/sudo:root#localhost:" (buffer-file-name))))
(goto-char position)))
Assume that I have in a certain directory several org-mode files: foo1.org, foo2.org, etc. I would like to have a script (maybe a makefile) that I could invoke something like
$ generate-pdfs
and foo1.pdf, foo2.pdf, etc. will be generated.
I thought that something like emacs --batch --eval <MAGIC> is a good start, but I don't know the magic.
A solution that is solely inside emacs could be of interest as well.
As you said, Emacs has the --batch option to perform operations with Emacs from the shell. In addition to that, you can use the -l flag to load Emacs Lisp code from a file and execute it, and the -f flag to execute a single Lisp function.
Here is a basic example, which exports a single org-mode file to HTML:
emacs myorgfile.org --batch -f org-html-export-to-html --kill
Perhaps you want something more advanced like exporting/publishing a full org-mode project. I do not have sample code for that, but it should not be too complicated.
I also have a sample Makefile I wrote some time ago to export all org-mode files in the directory to HTML (and also copy the HTML files to another directory):
OUT_DIR=/some/output/dir/html
# Using GNU Make-specific functions here
FILES=$(patsubst %.org,$(OUT_DIR)/%.html,$(wildcard *.org))
.PHONY: all clean install-doc
all: install-doc
install-doc: $(OUT_DIR) $(FILES)
$(OUT_DIR):
mkdir -v -p $(OUT_DIR)
%.html: %.org
emacs $< --batch -f org-html-export-to-html--kill
$(OUT_DIR)/%.html: %.html
install -v -m 644 -t $(OUT_DIR) $<
rm $<
clean:
rm *.html
EDIT:
With Org-mode 8 and the new export engine the function for HTML export has changed.
To make the previous examples work with Org 7 or older, replace org-html-export-to-html with org-export-as-html.
I expect to publish (by the end of this week-end) OrgMk, a suite of Makefile and standalone Bash scripts (usable as well under Cygwin) just to do that! Even more: generation of HTML, Ascii, Beamer, etc.
You'll find it on my GitHub account: https://github.com/fniessen/ (where I already have Emacs configuration files, color themes and other stuff such as an Org Babel refcard -- in progress).
Mark a few org files in dired and call this:
(defun dired-org-to-pdf ()
(interactive)
(mapc
(lambda (f)
(with-current-buffer
(find-file-noselect f)
(org-latex-export-to-pdf)))
(dired-get-marked-files)))
If you know what async is, wrap the call as it can take a while.
update:
Here's a version that combines the awesome dired approach with the lame
other one:)
(defun dired-org-to-pdf ()
(interactive)
(let ((files
(if (eq major-mode 'dired-mode)
(dired-get-marked-files)
(let ((default-directory (read-directory-name "dir: ")))
(mapcar #'expand-file-name
(file-expand-wildcards "*.org"))))))
(mapc
(lambda (f)
(with-current-buffer
(find-file-noselect f)
(org-latex-export-to-pdf)))
files)))
With tf.exe diff , I can get a diff.
Can I use this with ediff to visualize the diff in emacs?
I'm under the impression that ediff normally takes 2 or 3 files. I just have the one file, and a diff.
An option you might get to work is to use
M-x ediff-patch-buffer
It will prompt you for the patch file (or buffer if you have it open already), and the buffer to be patched. It then will march you through the differences.
Because the diff shows changes from the repository version to the current version, the patch is wrong direction. I'd write a command that generated the proper diff and use that - if you really want to use a diff.
Personally, I'd probably try to plug some code in to get 'ediff-revision (which I have bound to C-x v -) to get it to work.
Or just write some lisp which follows this pseudo code (since I don't have tf to do actual testing):
(defun ediff-tf-file-with-previous-version (file &optional version)
"DTRT and call ediff with the previous version of this file"
(interactive)
(ediff-files (progn
(unless version
(setq version (<parse-to-get-version> (shell-command (concat "tf.exe properties " file)))))
(shell-command (concat "tf.exe view " file (<munge-itemspec-version> version) " > " file ".older")))
file))
thanks R Berg for the fix
It looks as though someone has written a rudimentary Team Foundation mode, which you can grab from the wiki page here. It doesn't look like it has plugged anything into ediff though.
Here's a script I wrote that does it (I think!):
#!/bin/bash
# quit on error
set -e
# create a temporary file for the patched output
# note that the mkfifo command is optional - it will save disc space
patched_file=/tmp/diff_$RANDOM
mkfifo $patched_file
patch -o $patched_file "$1" "$2" &
vimdiff "$1" $patched_file
rm $patched_file
Is there an easy way to have emacs save current buffer in two locations?
I could in the 'after-save-hook' programmatically copy the current file to a second location, but writing lisp code for that might take some time.
For those that are curious why I want this:
I want the changes I make to my JSP immediately be deployed in tomcat's webapps/myapp directory.
So everytime I save a JSP file I want it saved in both my current version controlled source location as well as in the directory where my Tomcat application is deployed.
I can't use symlinks because I use a windows machine and the destination location is a directory in Linux box that is exported through Samba.
Something like this should work:
(add-hook 'local-write-file-hooks 'my-save-hook)
(defun my-save-hook ()
"write the file in two places"
(let ((orig (buffer-file-name)))
(write-file (concat "/some/other/path" (file-name-nondirectory orig)) nil)
(write-file orig nil)))
For more on local-write-file-hooks see this answer.
Obviously customize the file name created in the first call to 'write-file.
Given the problem you are trying to solve is to deploy changes immediately, I would suggest writing a script (in your case a batch file) that invokes rsync with the appropriate options. You could either run this in the after-save-hook (which is probably overkill) or assign a hotkey to run it for you when you have made a set of changes that you want to test. Something like:
(global-set-key 'f11 (shell-command "c:/dev/deploy_to_test.bat"))
where the script would look like this:
rsync -avz --del c:/dev/mywebapp z:/srv/tomcat/mywebapp
This is probably better than saving the same file in multiple places, as it ensures the deployment directory always matches what you have in your source repository.
Probably a more general solution is a hook similar to this Gist: https://gist.github.com/howardabrams/67d60458858f407a13bd :
(defun ha/folder-action-save-hook ()
"A file save hook that will look for a script in the same directory, called .on-save. It will then execute that script asynchronously."
(let* ((filename (buffer-file-name))
(dir (file-name-directory filename))
(script (concat dir ".on-save"))
(cmd (concat script " " filename)))
(write-file filename nil)
(when (file-exists-p script)
(async-shell-command cmd))))
(add-hook 'local-write-file-hooks 'ha/folder-action-save-hook)
Essentially, on any file save, if the directory where the file is being saved has an executable script called .on-save, it will execute that script with the name of the file being saved.
This allows you to specify an rsync command (or one or more other commands) on a per-directory basis. Granted, this could be expanded to walk up a directory tree looking for this sort of pattern.
Is there a way to modify/tell dired to copy files asynchronously? If you mark multiple files in dired and then use 'C' to copy them, emacs locks up until every file is copied. I instead want this copy to be started, and for me to continue editing as it goes on in the background. Is there a way to get this behaviour?
EDIT: Actually, C calls 'dired-do-copy' in dired-aux, not in dired itself. Sorry for any confusion.
I think emacs is mostly limited to a single thread - so this may not be directly possible through standard dired commands such as 'C' copy.
However, there is a dired command "dired-do-shell-command" which calls out to a shell to do the work in the background. If you select the files you want to copy and then use key '!' (this runs dired-do-shell-command) then type 'cp ? [destination]' (possibly can use 'copy' if you are on windows). I haven't tested this - so see help on "dired-do-shell-command" for full details.
See also the Emacs function dired-do-async-shell-command.
For an even more generic solution see https://github.com/jwiegley/emacs-async with which you also can evaluate arbitrary Emacs Lisp code through call to a separate Emacs process (which of course incurs a bit of extra latency). More specifically regard file operations see the file dired-async.el in this repo.
Also note that there is work on threading in Emacs under the working name Concurrent Emacs but it's not there yet. See http://www.emacswiki.org/emacs/ConcurrentEmacs for details.
I found this answer quite helpful: https://emacs.stackexchange.com/a/13802/10761. Reading that answer shows that you can make it so that dired will copy with the scp method instead of the ssh method (the latter initially encodes the file with gzip and that can be quite slow). The scp method will only copy with the scp program when the file is larger than tramp-copy-size-limit (which is 10240 by default). Using this scp method in conjunction with dired-async-mode is very nice, as it will not only copy quickly with scp, but it will also do it asynchronously and out of your way.
Also, I think this is useful: https://oremacs.com/2016/02/24/dired-rsync/. It provides this snippet of code to use rsync to copy files in dired:
;;;###autoload
(defun ora-dired-rsync (dest)
(interactive
(list
(expand-file-name
(read-file-name
"Rsync to:"
(dired-dwim-target-directory)))))
;; store all selected files into "files" list
(let ((files (dired-get-marked-files
nil current-prefix-arg))
;; the rsync command
(tmtxt/rsync-command
"rsync -arvz --progress "))
;; add all selected file names as arguments
;; to the rsync command
(dolist (file files)
(setq tmtxt/rsync-command
(concat tmtxt/rsync-command
(shell-quote-argument file)
" ")))
;; append the destination
(setq tmtxt/rsync-command
(concat tmtxt/rsync-command
(shell-quote-argument dest)))
;; run the async shell command
(async-shell-command tmtxt/rsync-command "*rsync*")
;; finally, switch to that window
(other-window 1)))
(define-key dired-mode-map "Y" 'ora-dired-rsync)