Skip to content

Latest commit

 

History

History
951 lines (634 loc) · 36 KB

emacs-macos.org

File metadata and controls

951 lines (634 loc) · 36 KB

[2020-06-05 Fri 13:31]

Article

Because I’ve configured my GNU emacs to automatically run in daemon mode I wish to ‘use’ emacs by running emacsclient exclusively. But emacsclient isn’t an OS-X ‘app’ which means that I can’t double click on a file to open it with emacsclient. It also means that I can’t use the ‘Open With’ menu to edit a file via emacsclient. Here is how to resolve those issues.

Create an emacsclient app

To create the app, I used Platypus. It is a truly great little application that enables one to put a wrapper around a shell script (or, I suppose, an a.out) to make it act like a ‘real’ application.

  1. Create a trivial shell script called ec which calls emacsclient. Make the script executable. I put it in my ~/bin/.
     #!/bin/bash
     # Start emacsclient and return immediately.
    /Applications/Emacs.app/Contents/MacOS/bin/emacsclient -n -c  " $@ "
        
  2. Optional: If you want your app to have the pretty GNU Emacs icon, copy Emacs.app/Contents/Resources/Emacs.icns to the temp dir.
  3. Launch Platypus. In the Platypus UI, do the following:
    App Name              -->  'emacsclient'
    Script Type           -->  'bash'
    Script Path           -->  '~/bin/ec'
    Output                -->  'None'
    Accepts dropped items -->  'checked'
    Everything else       -->  'unchecked'
    
    Optional: Click the gear under the icon and set icon to:  Emacs.icns
        
  4. In Platypus, click on ‘create’.

    This will create a ‘real’ OS-X application named emacsclient.app in your temp dir. Copy this emacsclient.app to /Applications.

You now have a functioning emacsclient OS-X ‘application’.

emacsclient.app and File Extensions

The app created by Platypus worked fine. However, nothing had been done to associate file extensions with the emacsclient.app. Without those associations it would not be possible to, for example, double click a Python file and have emacsclient open it.

It seems like Platypus would offer a mechanism for associating file extensions with the application it generates, but I could not find one. Here’s what I did.

/Applications/emacsclient.app/Contents/Info.plist contains an array of file extensions that emacsclient knows how to open; they are stored under the CFBundleDocumentTypes key. The array was essentially empty.

I decided that I wanted to take the list of supported extensions from the Info.plist under Emacs.app and insert them into the plist for emacsclient.app. But how to do this?

The duti utility looked promising, but it would not compile on El Capitan. It may have just been a matter of updating a version string in the config, but I didn’t feel like investing time in the necessary research.

I believe that I could have used plutil and PlistBuddy to convert both plists to XML and then manually edit them, but seemed tedious and error-prone.

I was surprised to discover that Python includes plistlib in its standard distribution. It made things ridiculously easy.

The following code will extract a key value from a source plist and insert that same key:value into a destination plist, saving the modified destination plist as a new plist.

#!/usr/bin/env python
# cppk.py -- This DOES NOT modify anything in /Applications

import  sys ,  plistlib ,  os

( key ,  src ,  dest ,  new )  =  sys . argv [ 1 :]

source  =  plistlib . readPlist ( src )
destination  =  plistlib . readPlist ( dest )

destination [ key ]  =  source [ key ]

plistlib . writePlist ( destination ,  new )

Example usage:

$  E =/Applications/Emacs.app/Contents/Info.plist
$  EC =/Applications/emacsclient.app/Contents/Info.plist
$ ./cppk.py CFBundleDocumentTypes  $E  $EC ./Info.plist

This creates the desired plist and saves it as ./Info.plist. I then copied ./Info.plist to emacsclient.app/Contents/Info.plist (after making a backup copy).

To force OS-X to pick up the change, I ran lsregister, causing Launch Services to rebuild its database:

 # Valid for OS-X 10.11.2.  If the path is invalid on your version,
 # run 'locate lsregister' to find it.

%  F =/System/Library/Frameworks/CoreServices.framework
%  F +=/Versions/A/Frameworks/LaunchServices.framework
%  F +=/Versions/A/Support/lsregister

%  " $F " -lint -kill -r -domain  local -domain system -domain user

This may take a while to complete.

Conclusion

After this point, emacsclient will appear in the ‘Open With’ menu. Additionally, it is now possible to globally associate emacsclient with file types so that it is the default application used to open them.

I can now double-click on a python file and have emacsclient open it.

I’d like to automate setting the global default application for certain file types (e.g., .py, .txt, etc.), but that will wait for another day.

[2020-06-05 Fri 13:32]

Article

I have used the Emacs for Mac OS-X distribution of Gnu Emacs for years and have been quite pleased with it. Emacs’ server-mode is great in that it makes for very fast load times and it allows sharing buffer contents, kill rings, etc., between windows. However, I found it a little tricky to get it working perfectly on OS-X. These are my notes on how to install and configure it so that server-mode works correctly on OS-X.

Under server-mode, the emacsclient program is used to edit files. I have another note about how to configure emacsclient as an OS-X application. This may be worth reading as well.

Configuration
  1. Install Emacs for Mac OS-X.
  2. Update your path:
    # Emacs.app binaries must come before /usr/bin
    # Using the apple provided emacsclient with a different
    # emacs in server mode is a common source of problems.
    PATH =/Applications/Emacs.app/Contents/MacOS/bin: $PATH
        
  3. Create a script named ec that invokes emacsclient with desired options. Note that this returns immediately; it does not wait for the edit to complete.
     #!/usr/bin/env bash
     # -n    --> Return immediately, don't wait for edit to complete
     # -a "" --> If emacs is not running in server mode, start it
     # -c    --> create a new frame instead of using existing frame
    emacsclient -n -a  "" -c  " $@ "
        
  4. Create a script named ecw that invokes emacsclient with desired options. Note that this command will not exit until the edit is complete.
     #!/usr/bin/env bash
     # -a "" --> If emacs is not running in server mode, start it
     # -c    --> create a new frame instead of using existing frame
    emacsclient -a  "" -c  " $@ "
        
  5. Many programs use the EDITOR and VISUAL environment variables to determine which editor to use. Set these up to use ecw.
    export  EDITOR =ecw
    export  VISUAL =ecw
        

Whenever you run ec or ecw, an emacs daemon in server-mode will be started, if one doesn’t already exist.

This is the simplest emacs setup that I’ve used and it works well.

Lagniappe

While not specific to OS-X, this is germane to server-mode emacs.

I’m used to killing buffers with = C-x k=. When first starting to use server-mode, I started seeing warnings about a client still using the buffer that I was killing. For my (simple) use case, the following allows me to keep using my old muscle-memory to kill the buffer yet avoid the warning:

( add-hook  'server-switch-hook
       ( lambda  ()
         ( when  ( current-local-map )
           ( use-local-map  ( copy-keymap  ( current-local-map ))))
         ( when  server-buffer-clients
           ( local-set-key  ( kbd  "C-x k" )  'server-edit ))))
Alternate Configuration

Before I settled on the above approach, I configured OS-X to automatically start emacs in server-mode at boot time. This does work, but it isn’t as clean as the above. Here’s how to do it:

  1. Create the plist ~/Library/LaunchAgents/org.fsf.gnu.emacs.plist using an editor:
    version= "1.0" >
    
        Label
        org.fsf.gnu.emacs
        ProgramArguments
    
           /Applications/Emacs.app/Contents/MacOS/Emacs
           --daemon
    
        RunAtLoad
    
        KeepAlive
    
        ServiceDescription
        Gnu Emacs Daemon
    
    
        
  2. Install the plist:
    $ launchctl load -w  ~/Library/LaunchAgents/org.fsf.gnu.emacs.plist
        

Note: I made up org.fsf.gnu.emacs. There’s nothing magic about it; it could be any unique value.

Now OS-X will start emacs in daemon mode and keep it running for you. I have had weird things happen with this approach, which is why I converted to the first method presented.

‘Open-with’ and emacsclient

After creating an emacsclient app via Platypus, I was disappointed to notice that the app did not appear in the ‘Open With’ menu when a supported file was control-clicked on.

Here is how I took care of this issue.

/Applications/emacsclient.app/Contents/Info.plist contains an array of file extensions that it knows how to open; they are stored under the CFBundleDocumentTypes key. The array was empty, containing only a *.

I concluded that a suitable array could be copied from Emacs.app and set about trying to insert it into emacsclient’s Info.plist. After a few hours of PlistBuddy and plutil, I was unable to get what I inserted as the array to be properly recognized.

This is why I don’t like binary / non-editable configuration files. There’s no reason that the plist shouldn’t be editable text: YAML, XML, JSON, or whatever. A binary format for configuration files that can only be modified with dedicated tools is insane.

I finally dropped back to Python, having bypassed it initially, assuming that programming be the long road to get it done. I was surprised to see that Python includes plistlib in its standard distribution. It made things ridiculously easy.

The following code grabs the array from Emacs.app, and inserts it into a copy of Info.plist taken from emacsclient.capp. It saves the modified result as Info.plist.new in the current directory.

#!/usr/bin/env python

# Run this script from the command line with the path to the Emacs.app
# Info.plist as the first argument and the path to the emacsclient.app
# Info.plist as the second argument.  'Info.plist.new' will be created
# in current direcotory.

import  sys ,  plistlib ,  os

( pl_from ,  pl_to )  =  sys . argv [ 1 :]

plf  =  plistlib . readPlist ( pl_from )
plt  =  plistlib . readPlist ( pl_to )

plt [ 'CFBundleDocumentTypes' ]  =  plf [ 'CFBundleDocumentTypes' ]

plistlib . writePlist ( plt ,  os . path . basename ( pl_to )  +  '.new' )

When this completes, copy the resulting Info.plist.new to emacsclient.app/Contents/Info.plist (after making a backup copy).

So that OS-X picks up the change, it is necessary to run lsregister, which causes Launch Services to rebuild its database. Here’s how:

 # Valid for OS-X 10.11.2.  If the path is invalid on yours,
 # run 'locate lsregister' to find it.

%  F =/System/Library/Frameworks/CoreServices.framework
%  F +=/Versions/A/Frameworks/LaunchServices.framework
%  F +=/Versions/A/Support/lsregister

%  $F -lint -kill -r -domain  local -domain system -domain user

This may take 15-30 seconds to run. After that, emacsclient will appear in your ‘Open With’ menu the next time you use it; it’s worth the effort.

Build Emacs on macos

brew tap d12frosted/emacs-plus
brew install emacs-plus --with-modern-nuvola-icon --with-xwidgets --without-imagemagick --with-jansson

To use keypad keys as modifier keys

(define-key function-key-map (kbd "<kp-1>") 'event-apply-control-modifier)
(define-key function-key-map (kbd "<kp-2>") 'event-apply-meta-modifier)
(define-key function-key-map (kbd "<kp-3>") 'event-apply-super-modifier)
(define-key function-key-map (kbd "<kp-4>") 'event-apply-shift-modifier)
(define-key function-key-map (kbd "<kp-5>") 'event-apply-hyper-modifier)
(define-key function-key-map (kbd "<kp-6>") 'event-apply-alt-modifier)

[2020-08-18 Tue 23:35]

Article

16 August 2020 Trying out gccemacs on macOS

Been wanting to try Andrea Corallo’s gccemacs. Followed Allen Dang’s handy instructions to build it on macOS.

Though not had much of a chance to play with the new install, here’s what I did to get up and running…

Patch gcc homebrew formula

Edit $(brew –prefix)/Library/Taps/homebrew/homebrew-core/Formula/gcc.rb

$ diff -u $(brew --prefix)/Library/Taps/homebrew/homebrew-core/Formula/gcc.rb $(brew --prefix)/Library/Taps/homebrew/homebrew-core/Formula/gcc.rb.patched
 ---  $(brew --prefix)/Library/Taps/homebrew/homebrew-core/Formula/gcc.rb          2020-08-16 14:12:13.000000000 +0100
 +++  $(brew --prefix)/Library/Taps/homebrew/homebrew-core/Formula/gcc.rb.patched  2020-08-07 09:16:42.000000000 +0100
 @@ -47,7 +47,7 @@
     #  - Go, currently not supported on macOS
     #  - BRIG
     # GCCEMACS
 -     languages = %w[c c++ objc obj-c++ fortran]
 +     languages = %w[c c++ objc obj-c++ fortran  jit ]

     osmajor = `uname -r`.split(".").first
     pkgversion = "Homebrew GCC #{pkg_version} #{build.used_options*" "}".strip
 @@ -67,6 +67,7 @@
       --with-system-zlib
       --with-pkgversion=#{pkgversion}
       --with-bugurl=https://github.com/Homebrew/homebrew-core/issues
 +       --enable-host-shared
     ]

     # Xcode 10 dropped 32-bit support

Install gcc via homebrew

brew install gcc --build-from-source --force

Save configure script

Create configure-gccemacs.sh

 # !/bin/ bash

 set -o nounset
 set -o errexit

 #  Configures Emacs for building native comp support
 #  http://akrl.sdf.org/gccemacs.html

 #  Installing under homebrew's opt, but could be anywhere.
 readonly  GCCEMACS_PREFIX= "$( realpath $(brew --prefix )/opt/gccemacs)"

 readonly  GCC_DIR= "$( realpath $(brew --prefix )/opt/gcc)"
[[ -d $ GCC_DIR ]] ||  {  echo  "${GCC_DIR} not found";  exit 1; }

 readonly  SED_DIR= "$( realpath $(brew --prefix )/opt/gnu-sed)"
[[ -d $ SED_DIR ]] ||  {  echo  "${SED_DIR} not found";  exit 1; }

 readonly  GCC_INCLUDE_DIR=${ GCC_DIR}/include
[[ -d $ GCC_INCLUDE_DIR ]] ||  {  echo  "${GCC_INCLUDE_DIR} not found";  exit 1; }

 readonly  GCC_LIB_DIR=${ GCC_DIR}/lib/gcc/10
[[ -d $ GCC_LIB_DIR ]] ||  {  echo  "${GCC_LIB_DIR} not found";  exit 1; }

 export  PATH= "${SED_DIR}/libexec/gnubin:${PATH}"
 export  CFLAGS= "-I${GCC_INCLUDE_DIR}"
 export  LDFLAGS= "-L${GCC_LIB_DIR} -I${GCC_INCLUDE_DIR}"
 export  DYLD_FALLBACK_LIBRARY_PATH= "${GCC_LIB_DIR}"

 echo  "Environment"
 echo  "-----------"
 echo PATH: $ PATH
 echo CFLAGS: $ CFLAGS
 echo LDFLAGS: $ LDFLAGS
 echo DYLD_FALLBACK_LIBRARY_PATH: $ DYLD_FALLBACK_LIBRARY_PATH
 echo  "-----------"

./autogen.sh

./configure  \
     --prefix= "${GCCEMACS_PREFIX}"  \
     --enable-locallisppath= "${GCCEMACS_PREFIX}/opt/gccemacs/site-lisp"  \
     --with-mailutils  \
     --with-ns  \
     --with-imagemagick  \
     --with-cairo  \
     --with-modules  \
     --with-xml2  \
     --with-gnutls  \
     --with-json  \
     --with-rsvg  \
     --with-nativecomp  \
     --disable-silent-rules  \
     --disable-ns-self-contained  \
     --without-dbus

Make it executable

chmod  +x configure-gccemacs.sh

Clone Emacs source

git clone https://github.com/emacs-mirror/emacs gccemacs

Check out native-comp feature branch

 cd gccemacs
git checkout feature/native-comp

Configure build

../configure-gccemacs.sh

Native lisp compiler found?

Verify native lisp compiler is found:

Does Emacs have native lisp compiler?                   yes

Build

Build will take a while. Put those cores to use. Find out how many you got with:

sysctl hw.logicalcpu
hw.logicalcpu: 4

Ok so run:

make -j4  NATIVE_FAST_BOOT=1
make install

This will take hours and give your fans a good workout.

Symlink Emacs.app/Contents/lisp

Without synlinking, opening Emacs.app from finder fails silently, so symlink as follows. Btw, if you changed $GCCEMACS_PREFIX, use that path prefix instead.

ln -s $( brew --prefix)/opt/gccemacs/share/emacs/28.0.50/lisp nextstep/Emacs.app/Contents/

ps. Without symlink, you won’t see an error unless the binary is executed explicitly from the terminal (ie. nextstep/Emacs.app/Contents/MacOS/Emacs), and then you’d see something like

nextstep/Emacs.app/Contents/MacOS/Emacs
emacs: dlopen(nextstep/Emacs.app/Contents/MacOS/../lisp/emacs-lisp/eln-x86_64-apple-darwin19.4.0-abff6ce99a055711/lisp-mode.eln, 1): image not found

Remove ~/emacs.d

You likely want to start with a clean install, byte-compiling all packages with the latest Emacs version. In any case, rename ~/emacs.d (for backup?) or remove ~/emacs.d.

init.el config

Ensure exec-path and LIBRARY_PATH both point to locations in $GCCEMACS_PREFIX and finally set comp-deferred-compilation. I wrapped mine in exec-path-from-shell, setting early in init.el should be enough.

( use-package  exec-path-from-shell
   :ensure t
   :config
  (exec-path-from-shell-initialize)
  ( if ( and (fboundp 'native-comp-available-p)
           (native-comp-available-p))
      ( progn
        (message  "Native comp is available")
        (add-to-list 'exec-path (expand-file-name  "~/homebrew/opt/gccemacs/bin"))
        (setenv  "LIBRARY_PATH" (concat (getenv  "LIBRARY_PATH")
                                       ( when (getenv  "LIBRARY_PATH")
                                          ":")
                                       (car (file-expand-wildcards
                                             (expand-file-name  "~/homebrew/opt/gcc/lib/gcc/*")))))
         ;;  Only set after LIBRARY_PATH can find gcc libraries.
        ( setq comp-deferred-compilation t))
    (message  "Native comp is *not* available")))

Launch Emacs.app

You’re good to go. Open Emacs.app via finder or shell:

open nextstep/Emacs.app

Deferred compilation logs

After setting comp-deferred-compilation (in init.el config section), .elc files should be asyncronously compiled. Function definition should be updated to native compiled equivalent.

Look out for an **Async-native-compile-log** buffer. Should have content like:

Compiling .emacs.d/elpa/moody-20200514.1946/moody.el...
Compiling .emacs.d/elpa/minions-20200522.1052/minions.el...
Compiling .emacs.d/elpa/persistent-scratch-20190922.1046/persistent-scratch.el...
Compiling .emacs.d/elpa/which-key-20200721.1927/which-key.el...
...

Can also check for .eln files:

find ~/.emacs.d -iname *.eln | wc -l
149

[2020-08-19 Wed 00:23]

Article

The language server protocol was proposed by Microsoft as a way for different editors and development environments to share language analysis backends

This post describes how to configure Emacs, lsp-mode and the palantir python-language-server for improved code intelligence when working on Python projects. (I’m planning a companion post for Emacs, C++ and the cquery language server.)

Goal

Before starting, it is motivating to see what we are working towards.

With a correctly configured setup, Emacs will sport, amongst others, improved completion with interactive documentation, imenu navigation, documentation on hover, and really snazzy find definitions (M-.) and find references.

See the following screenshots for some examples:

/wp-content/uploads/2018/06/Screen-Shot-2018-06-08-at-13.01.32_2018-06-08_14-29-25.png?resize=660%2C481&ssl=1

/wp-content/uploads/2018/06/lsp-python-imenu_2018-06-08_14-34-27.png?resize=660%2C477&ssl=1

/wp-content/uploads/2018/06/lsp-python-docs-on-hover_2018-06-08_14-34-43.png?resize=660%2C477&ssl=1

Pre-requisites on the Python side

Install the python-language-server into the virtual environment, or user environment, that you’re planning to use.

These days, I tend to use pipenv:

  cd my_project
pipenv install python-language-server [all ]

The [all] means that it installs all optional providers, e.g. yapf formatting.

Pre-requisites on the Emacs side

In Emacs, install the required and some optional packages using for example M-x package-install:

  • lsp-mode - the main language server protocol package
  • lsp-ui - UI-related LSP extras, such as the sideline info, docs, flycheck, etc.
  • company-lsp - company-backend for LSP-based code completion.
  • =projectile= or =find-file-in-project= - we use a single function from here to determine the root directory of a project.

Emacs configuration

Add the following to your Emacs init.el, and don’t forget to read the comments.

If you’re not yet using =use-package= now would be a good time to upgrade.

( use-package  lsp-mode
 :ensure  t
 :config

 ;; make sure we have lsp-imenu everywhere we have LSP
 ( require  'lsp-imenu )
 ( add-hook  'lsp-after-open-hook  'lsp-enable-imenu )
 ;; get lsp-python-enable defined
 ;; NB: use either projectile-project-root or ffip-get-project-root-directory
 ;;     or any other function that can be used to find the root directory of a project
 ( lsp-define-stdio-client  lsp-python  "python"
                          #' projectile-project-root
                          ' ( "pyls" ))

 ;; make sure this is activated when python-mode is activated
 ;; lsp-python-enable is created by macro above
 ( add-hook  'python-mode-hook
           ( lambda  ()
             ( lsp-python-enable )))

 ;; lsp extras
 ( use-package  lsp-ui
   :ensure  t
   :config
   ( setq  lsp-ui-sideline-ignore-duplicate  t )
   ( add-hook  'lsp-mode-hook  'lsp-ui-mode ))

 ( use-package  company-lsp
   :config
   ( push  'company-lsp  company-backends ))

 ;; NB: only required if you prefer flake8 instead of the default
 ;; send pyls config via lsp-after-initialize-hook -- harmless for
 ;; other servers due to pyls key, but would prefer only sending this
 ;; when pyls gets initialised (:initialize function in
 ;; lsp-define-stdio-client is invoked too early (before server
 ;; start)) -- cpbotha
 ( defun  lsp-set-cfg  ()
   ( let  (( lsp-cfg  ` ( :pyls  ( :configurationSources  ( "flake8" )))))
     ;; TODO: check lsp--cur-workspace here to decide per server / project
     ( lsp--set-configuration  lsp-cfg )))

 ( add-hook  'lsp-after-initialize-hook  'lsp-set-cfg ))

Putting it all together

Importantly, use =pyenv= or something similar to switch to the relevant virtualenv before opening the first Python file.

When you open the file, the pyls should be automatically started up, and you can edit away with LSP-powered code intelligence.

This often gives better and more detailed results than =elpy=, probably because pyls uses a mix of static and dynamic (introspection-based) analysis.

Furthermore, the handling of LSP servers in Emacs can be unified, giving the same consistent level of support across a whole range of programming languages.

[2020-11-11 Wed 10:51]

Article

Motivation

So a lot of my notes and tasks will require me to reference outlook items, either emails or meetings.

There aren’t any real solutions for Achieving this on MacOS at the moment, and the Windows solutions are not too great.

So I decided to create this unholy abomination. I’m sorry.

Components

So there’s a few moving parts to this, but overall it’s mainly leverging AppleScript, Karabiner and Emacs to provide this functionality.

AppleScript

Let’s start with the MacOS Native automation tools, this is how we’ll get the ID of the Outlook item, which we will need later when searching for it.

There are two AppleScript files, one for inserting a link into the current buffer, and another for using a capture template. Both are almost identical, but one evals some Elisp to ensure we insert at the right point and file.

Insertion
 tell application "Microsoft Outlook"
  set theMessages to selected objects
  repeat with theMessage in theMessages
    set toOpen to id of theMessage
  end repeat
end tell

tell application "Emacs" to activate
do shell script "/usr/local/bin/emacsclient --eval '(with-current-buffer (window-buffer) (org-insert-link nil \"outlook:" & toOpen & "\" (read-string \"Link Name:\")))'"
Capture
 tell application "Microsoft Outlook"
  set theMessages to selected objects
  repeat with theMessage in theMessages
    set toOpen to id of theMessage
  end repeat
end tell

tell application "Emacs" to activate
do shell script "/usr/local/bin/emacsclient \"org-protocol://capture?template=o&url=" & toOpen & "\""
Karabiner

Karabiner provides a very nice way of running a shell script from a keybind, and even supports filtering to the correct window. Thus this keybind will only trigger when Outlook is focused.

Command+L will insert and Command+Shift+L will capture

 {
    "title": "Outlook-Emacs",
    "rules": [
        {
            "description": "Meta-L to copy outlook item to orgmode",
            "manipulators": [
                {
                    "type": "basic",
                    "from": {
                        "key_code": "l",
                        "modifiers": {
                            "mandatory": ["left_command"],
                            "optional": ["caps_lock"]
                        }
                    },
                    "to": [
                        {
                            "shell_command": "osascript ~/Documents/Store_Selected_OutlookItem_As_Orgmode_Link.scpt"
                        }
                    ],
                    "conditions": [
                        {
                            "type": "frontmost_application_if",
                            "bundle_identifiers": ["^com\\.microsoft\\.Outlook$"]
                        }
                    ]
                }
            ]
        },
        {
            "description": "Meta-L to copy outlook item to orgmode",
            "manipulators": [
                {
                    "type": "basic",
                    "from": {
                        "key_code": "l",
                        "modifiers": {
                            "mandatory": ["left_command", "left_shift"],
                            "optional": ["caps_lock"]
                        }
                    },
                    "to": [
                        {
                            "shell_command": "osascript ~/Documents/Capture_Selected_OutlookItem_As_Orgmode_Link.scpt"
                        }
                    ],
                    "conditions": [
                        {
                            "type": "frontmost_application_if",
                            "bundle_identifiers": ["^com\\.microsoft\\.Outlook$"]
                        }
                    ]
                }
            ]
        }
    ]
}
Emacs

There are two parts of this for Emacs, the Capture template, and the custom hyperlink

Custom Link

I just dump this into my startup config, but you could make an ol-outlook.el if you wanted to make it less platform specific.

This relies on MDFind which is the macos spotlight CLI, it will find it, then open it in outlook.

 (require 'ol)

(org-add-link-type "outlook" 'org-outlook-open)

(defun org-outlook-open (id _)
  "Open the outlook item matching that ID"
  (shell-command (format "mdfind \"com_microsoft_outlook_recordID == '%s'\" -0 | xargs -0 open " id)))
Capture template

Ideally this should be customized more for your setup, but this is what I use.

(add-to-list 'org-capture-templates '("o" "Outlook item to capture" entry
          (file+headline "~/Documents/Notes/inbox.org" "Tasks")
          "* TODO [[outlook:%:link][%^{Item name|Email}]]" :clock-in t :clock-resume t))

[2020-11-18 Wed 10:08]

ArticleSynchronizing OSX, Terminal, and Emacs Dark/Light AppearanceComposing Shell, Applescript, and Emacs Eval

By Jeremy Friesen on 2020-11-11

:: # emacs

  # osx   # programming

Throughout the day, I find myself toggling between Light and Dark modes. This evening I wrote up a shell script to keep OS X 📖 , Emacs 📖 , and my Terminal synchronized.

I named the script =dark=. Now, from the command-line, I can type dark to switch between Light and Dark mode.

Below is a copy of that script:

 #!/bin/sh

# This script toggles the Operating System, Terminal, and Emacs
# themes.  It uses the state of the Operating System to determine
# if the terminal and Emacs should be "dark" or "light".

# First, tell OS-x to toggle dark mode.
osascript -e 'tell application "System Events" to tell appearance preferences to set dark mode to not dark mode'

# Second, determine what is the newly set appearance.
appearance=`defaults read -g AppleInterfaceStyle 2>/dev/null`
if [ -z "$appearance" ]
then
  # No value for AppleInterfaceStyle, so the OS has us in light
  # mode, proceed accordingly.
  sh $HOME/.config/base16-shell/scripts/base16-google-light.sh
  editor --eval "(disable-theme 'modus-vivendi)" \
    --eval "(modus-operandi-theme-load)" 1> /dev/null
else
  # AppleInterfaceStyle is set, and that means we're now in "Dark"
  # mode.
  sh $HOME/.config/base16-shell/scripts/base16-google-dark.sh
  editor --eval "(disable-theme 'modus-operandi)" \
    --eval "(modus-vivendi-theme-load)" 1> /dev/null
fi

Nothing fancy.

*Nov 11, 2020 update:*

Emacs Configuration

Later on this evening, I started tinkering to see about Emacs synchronizing with the Dark/Light mode of OS X .

 (defun jnf-dark ()
  "Toggle system-wide Dark or Light setting."
  (interactive)
  (shell-command "osascript -e 'tell application \"System Events\" to tell appearance preferences to set dark mode to not dark mode'")
  (jnf-emacs-theme-by-osx-appearance))


(defun jnf-emacs-theme-by-osx-appearance ()
  "Set theme based on OSX apperance state."
  (if (equal "Dark" (substring
                      (shell-command-to-string
                        "defaults read -g AppleInterfaceStyle") 0 4))
  (progn
    (disable-theme 'modus-operandi)
    (modus-vivendi-theme-load))
  (progn
    (disable-theme 'modus-vivendi)
    (modus-operandi-theme-load))))

;; Load the appropriate Emacs theme based on OSX appearance
(jnf-emacs-theme-by-osx-appearance)

I added this to my one of the files I load for Emacs.

Web Mentions

Others mentions of this post:

Related Posts License(s)

Synchronizing OSX, Terminal, and Emacs Dark/Light Appearance by Jeremy Friesen is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. Based on a work at https://takeonrules.com/2020/11/11/synchronizing-osx-terminal-and-emacs-dark-light-appearance/. Permissions beyond the scope of this license may be available at https://takeonrules.com/more_permissions/.

To gpg working on MAC

Homebrew/homebrew-core#14737 (comment)

brew install pinentry-mac
echo "pinentry-program /usr/local/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
killall gpg-agent

Open a terminal window and cd into a folder under the repository. Then:

git fsck | awk '{print $3}' > tmp.txt
cat tmp.txt | xargs git show > tmp2.txt

Now open tmp2.txt in editor, locate your lost code, and find the commit-id on top of it. Then apply the code:

git stash apply <commit id>

rm tmp.txt tmp2.txt

recover lost stash:

gitk --all $(git fsck --no-reflog | awk '/dangling commit/ {print $3}')