I use GoodNotes (iOS, macOS) for taking hand-written notes
on my iPad. It has a variety of paper templates built-in—ruled,
grid, Cornell, and so on—but I wanted to give dot grid paper a try.
Rather than go out and buy a ream of it, I created several PDFs
for 1/4" (quarter inch) dot grid paper that I can load into GoodNotes
as templates (U.S. Letter size).
The result is on GitHub. The repository also contains the
Python script and LaTeX package (.sty file) I used to generate them.
The script generates individual LaTeX files (which in turn use the
LaTeX package) for each of several specifications:
The default stylesheet for HTML in most browsers is rather
unattractive, so you may want to use a custom stylesheet by either
linking to an external stylesheet or including a <style> block in
the exported HTML itself.
Linking to an External Stylesheet
The easiest way to customize the style of previewed and exported HTML
output is to link to an existing CSS stylesheet. Markdown Mode has a
customizable variable markdown-css-paths, which is a list of
stylesheets to link to. For each path or URL in markdown-css-paths,
a line like the following will be included in the HTML output:
The most obvious way to use this is to link to an existing stylesheet.
Noting that the variable is a list, one can specify a single CSS path
on the local filesystem like so:
As another example, the Marked 2 previewer contains several
built-in stylesheets that users can choose between. Conveniently,
these stylesheets are stored on the filesystem as CSS files inside the
application package and you can link to them locally. To use the
“Lopash” style when previewing from markdown-mode, you can set the
markdown-css-paths list as follows:
If you don’t have a stylesheet already, there are many popular
Markdown stylesheets available online. For example, here are three
such repositories on GitHub:
As an alternative (or in addition to) linking to an external
stylesheet, you can also include inline style information in output
files by using the markdown-xhtml-header-content variable. This
variable specifies a string to add to the <head> block of the
resulting HTML output.
For example, to remove the underline from links, but underline links
when hovering, you can add the following <style> block to
markdown-xhtml-header-content:
Markdown Mode for Emacs includes support for imenu, an
interface for quickly navigating to different sections within a
buffer. To try imenu with Markdown Mode, simply run M-x
imenu-add-menubar-index. An “Index” menu will appear in the menubar.
Clicking a heading moves the point to that heading. Alternatively,
when invoking M-x imenu using the keyboard Emacs will present you
with a list of headings in the minibuffer.
Markdown Mode adds a “.” to the top of each sub-menu. Clicking this
dot takes you to the parent section. Otherwise, there is no way to
jump directly to headings that are not “leaf nodes.”
To automatically load imenu when markdown-mode is loaded, you can
add the following to your .emacs or init.el file:
The first line asks Emacs to run the imenu-add-menubar-index
function each time markdown-mode is loaded. The second line asks
imenu to keep the index up to date when files are modified, as
sections may be added or removed.
Another useful imenu-based tool is imenu-list, a third-party
package which shows the current buffer’s imenu entries in a popup
buffer. You can install it from MELPA. I use use-package
for loading packages in my init file, so I configure it like this:
(use-package imenu-list:ensure t:bind(("C-'" . imenu-list-smart-toggle)):config(setq imenu-list-focus-after-activation t imenu-list-auto-resize nil))
Then, when pressing C-‘, a window appears on the right side
showing the heading hierarchy in the *Ilist* buffer. Pressing
C-’ again hides the window.
Of course, there are several keybindings in the *Ilist* buffer for
navigating between sections:
RET or mouse click - visit heading at point
SPC - visit heading, but keep focus on Ilist buffer
TAB or f - fold/unfold subtree
n and p - next/previous line
g - refresh entries
q - quit window and disable imenu-list-minor-mode
Unlike the imenu Index menu, the *Ilist* buffer is updated
automatically when Emacs is idle.
Blink Shell is a unique and powerful shell app for iOS for connecting to remote hosts. Although it is perhaps not quite as polished as Prompt, my other favorite iOS SSH client, it has several features that Prompt does not.
When you first start Blink, there are no buttons or menus, as one expects from an iOS app. Instead, you are greeted with a cryptic blink> prompt in a black terminal with tiny white text.
This might be a bit disconcerting, but after a brief moment of panic, you will probably start to experiment with some commands. If you type an invalid command, Blink will inform you that you can type “help” to see a list of commands:
Blink: v7.0.1. Jul 11 2017
Available commands:
mosh: mosh client.
ssh: ssh client.
ssh-copy-id: Copy an identity to the server.
config: Configure Blink. Add keys, hosts, themes, etc...
help: Prints this.
exit: Close this shell.
Available gestures and keyboard shortcuts:
two fingers tap or cmd+t: New shell.
two fingers swipe down or cmd+w: Close shell.
one finger swipe left/right or cmd+shift+[/]: Switch between shells.
cmd+alt+N: Switch to shell number N.
cmd+o: Switch to other screen (Airplay mode).
cmd+shift+o: Move current shell to other screen (Airplay mode).
cmd+,: Open config.
pinch: Change font size.
If you have a Smart or Bluetooth keyboard attached—and you probably should to get the most out of Blink—then instead of typing a command you might try holding ⌘ to see a list of keyboard shortcuts.
You can see that pressing ⌘, is an alternative to typing config to enter the app settings. Note that this is the same keyboard shortcut for opening the preferences window in most macOS applications. You can also do normal shell things like Control+L to clear, Control+D to log out, and so on.
Configuring and Connecting to Remote Hosts
Blink works with both SSH and Mosh, but before connecting to a remote host you should first import your SSH login keys and set the default username for SSH connections.
You can issue the ssh command with the usual flags, such as ssh -l <username> <hostname>, but you’ll want to configure frequently-accessed hosts for convenience. Each host is given an alias, say <host> instead of <hostname>, so that once configured you can simply type ssh <host> or mosh <host> to connect with the pre-configured hostname, username, port, etc.
SSH
For basic SSH access to a host, you’ll need provide the following:
Name for remote host entry. Note that this the name you’ll use to connect (e.g., ssh <name>) and it is case sensitive.
IP address or hostname.
Port used to access SSH (if different port 22).
Your username.
Either your password or an SSH key to use.
Mosh: Mobile Shell
Most readers probably use SSH already but may not have heard of Mosh, which is much newer. Mosh is designed to provide robust remote connections with roaming when your IP changes (e.g., switching from WiFi to cellular) and intermittent connectivity. Mosh piggy-backs on top of SSH, so if you already have remote SSH access you only need to install a user-level mosh-server binary to enable Mosh access.
If you have installed Mosh on the remote host, then you may also need to provide the following:
Path to mosh-server binary (e.g., /usr/local/bin/mosh-server).1
Optional UDP port (e.g., if you need to use a custom port to get through a firewall). By default, Mosh uses the first available UDP port between 60001 and 60999.
Optional command to run on startup (e.g., screen -d -R or tmux attach -t default).
If you use Homebrew on macOS, installing Mosh is as simple as this:
brew install mobile-shell
Customizing Modifier Keys: Caps Lock as Control
One of Blink’s most powerful features is the ability to customize the keyboard modifier keys. In fact, the reason I found Blink to begin with is it’s ability to use Caps Lock as a Control key (although there is an important caveat, discussed below).
If you set Caps Lock to be a Control key, there is a very important caveat: Caps Lock may be in an unexpected state when you leave and re-enter blink. This is due to the limitations of iOS development. There is a GitHub issue devoted to this problem if you’d like to read more about the technical details.
To summarize the issue, you can think in terms of two distinct Caps Lock states: one global in scope and one local. There is the iOS system-wide Caps Lock state and then the local state within the Blink app. If you are using Caps Lock as the control key, each time you press Caps Lock the system state is toggled but the Blink Caps Lock state remains off. The issue is that when you leave the Blink app, other apps honor the system state, which could be either on or off depending on whether you’ve pressed Caps Lock an even or odd number of times during your Blink session.
If you have an Apple wireless keyboard with a Caps Lock light, the light follows the system state every time you use Caps Lock in Blink, so this can at least help you track the state. Overall, I’ve found that the annoyance of this issue is small in comparison to the convenience of using Caps Lock as Control in Emacs and for other shell commands.
Another caveat is the small delay between when Caps Lock is pressed and when it is registered. When I quickly type control sequences as fast as I typically do—expecting no delay—the Control key part is not recognized. For example, to send ^C you’ll need to press Caps Lock and hold it for a fraction of a second longer than you typically might before pressing C. I don’t mean to overstate this issue, but I find that I do have to be more deliberate about entering control sequences.
Customization
There is a Blink Shell Theme Gallery with a huge selection of themes which you can access in Settings > Appearance > Add a new theme.
In the screenshot, I’m enjoying the Fira Code font—the same font I use in Emacs, yes, a monospaced font with programming ligatures—with the Arthur theme for Blink.
The keyboard shortcuts are also customizable. This includes the keys for switching to the previous and next shells, creating a new shell, closing a shell, and configuring Blink.
This is the “Server” setting, which I found to be confusing at first. This field is not asking for a hostname (again), it is provided in case your mosh-server binary is not in your default PATH. ↩
If you’re looking at the source, you probably also want to see Nicolas Holzschuch’s branch, where he has added several common Unix commands for file management (ls, cp, rm, mkdir, etc.), text processing (cat, grep, wc), file transfer (curl, scp, sftp), and so on. ↩
Ten years ago, I added an Atom feed to this website. Before
that, there was an RSS feed—version 1—and for a while I had an RSS 2
feed. But in more recent years I disabled RSS and have used the Atom
feed exclusively.
The reality for Atom is that, not only is it difficult to write valid
XML by hand, but people also have trouble writing programs to generate
valid XML. Once a feed is available, it needs to be parsed. Even
parsing valid Atom feeds is no simple task, but the implementation
challenges mean that there are also a bunch of broken Atom feeds in
the wild that feed reader writers have to deal with.
Enter JSON Feed, written by two individuals with a lot of
collective feed-processing experience, with the stated goal of being
“simpler to read and write” and “less prone to bugs.” It has been
adopted widely already, both by publishers and feed readers.
Emacs allows one to specify values for variables inside files
themselves. For example, you can specify which mode Emacs should
use to edit a particular file by setting a special mode variable.
You can specify such file local variables at either the beginning or
end of a file.
Perhaps you have seen lines like the following at the beginning of
scripts. This particular line tells Emacs that you’d like to open
this file using cperl-mode:
#!/usr/bin/perl -*- mode: cperl -*-
The mode variable is special; it’s not an actual variable name in
Emacs. Another special variable is coding, which specifies the
character coding system for this file (e.g., utf-8 or latin-1). A
third special variable is eval, which specifies a Lisp expression to
evaluate. Multiple eval declarations can be given in the same file.
Among the special variables, mode is the most special of all and so
the mode: declaration can even be omitted:
#!/usr/bin/perl -*-cperl-*-
File variable definitions should appear in a comment, and the comment
syntax used by Markdown Mode is the same as for HTML comments:
<!-- comment -->. So, to specify a local variable at the beginning
of a file you could add the following to the first line (which would
result in Emacs loading the file in gfm-mode instead of, say,
markdown-mode):
<!-- -*- mode: gfm -*- -->
To specify multiple variables, separate them by semicolons:
<!-- -*- mode: markdown; coding: utf-8 -*- -->
Alternatively, you can insert a local variable block at the end of a
file. Such a block opens with a Local Variables: declaration and
closes with End:, like so:
<!-- Local Variables: --><!-- markdown-enable-math: t --><!-- End: -->
It’s not necessary that each line is a self-contained comment, so the
following also works and it is a personal preference which form you
use:
One useful scenario for using file local variables with Markdown files
include toggling special modes, like setting markdown-enable-math in
the previous example. If you mostly have math mode disabled (so that
$ is not a special character), but sometimes want to enable it,
using a file-local variable as above is a great way to handle this
case.
Other example uses are setting the fill-column in a particular file,
or declaring that spaces should be used for indentation instead of
tabs:
Apple’s Swift programming language is most widely used for iOS and macOS development in Xcode, however, it can also be used in a manner more like a traditional Unix shell script or even compiled into an executable using the command line. Below, I give some examples of these alternative uses.
For scripting, like other shell scripts, the first line of your script’s .swift file should be a “shebang”, which points to the swift executable. Following that, you can write Swift code as usual:
#!/usr/bin/swift
func fibonacci(_ n: Int) -> Int {
if n <= 2 {
return 1
} else {
return fibonacci(n - 1) + fibonacci(n - 2)
}
}
print(fibonacci(10))
You can make the script executable and run it as you would any other shell script:
% chmod +x fibonacci.swift
% ./fibonacci.swift
55
Printing the 10th integer in the Fibonacci sequence is hard coded above, but what if you want to print other values? We can modify the script to take a single command line argument, the index of the Fibonacci number you want to print. Replace the last line above with the following:
let arg = CommandLine.arguments[1] // Store first argument.
if let n = Int(arg) { // If it converts to Int,
print(fibonacci(n)) // print fibonacci(n).
} else { // Otherwise,
print("Usage: fibonacci <n>") // print usage and exit.
}
As with modern scripting languages such as Python and Ruby, you can also run Swift interactively (with Xcode 6.1 and later). The interactive command-line interface is called the Swift Read Eval Print Loop (REPL):
% swift
Welcome to Apple Swift version 3.1 (swiftlang-802.0.48 clang-802.0.48). Type :help for assistance.
1> 1 + 2
$R0: Int = 3
2> print("hello, world")
hello, world
Finally, you can also compile Swift code and run it as a binary:
Markdown Mode is a major mode for editing Markdown-formatted
text files in Emacs. On the surface, it provides syntax
highlighting (“font lock” in Emacs parlance) to provide visual cues
when editing files and it defines commands and keyboard shortcuts for
inserting certain markup. For example, surrounding a phrase in double
asterisks (**) in Markdown makes the phrase appear in bold when
converted to HTML or another format. The corresponding key sequence
in Markdown Mode for inserting bold text, as you probably know if
you’ve read this far, is C-c C-s s.
The documentation for Markdown Mode is now quite lengthy, so
certain features go unnoticed even by long-time users. Two of these
features involve ways to switch types of markup (e.g., from bold to
italics). As an example, suppose I typed **really scary** and wish
I had written it instead using italics as *really scary*. There
are at least two ways in Markdown Mode to quickly fix the mistake.
Using the kill ring: Move the point anywhere in the bold span and
press C-c C-k to execute markdown-kill-thing-at-point.
This removes the entire bold span and adds the contents (“really
scary” without the asterisks) to the kill ring. Then press
C-c C-s e to insert an empty italic span and yank the text
with C-y.
Using markup toggling: Markdown Mode allows you to “toggle”
certain markup, including, bold, italics, and inline code. The second
method is to move the point to the bold text and repeat the insertion
key sequence (C-c C-s s) to remove (toggle) the asterisks.
Then select the text “really scary” (i.e., place it in the active
region) and press C-c C-s e to add the italic markup.
In this case, because there are multiple words, you have to activate
and set the region (the default is for insertion commands to apply to
the single word at the point if there is no active region, or insert
empty markup otherwise). This means the first approach is a little
faster in this case, but there are cases where both approaches are
useful. Both methods also apply to other forms of markup such as
bold, italics, and even links.
Extra Credit: Suppose I have *really scary* in the buffer now
but I wish it was _really scary_ instead. Both will render in HTML
as italics, but you might have a preference for the latter in plain
text. You can use the Markdown Mode cycling commands to switch
between underscores and asterisks in bold and italic phrases by
pressing C-c C-= or C-c C– (markdown-demote
and markdown-promote).
I use a clipboard manager called Copied that syncs previously
copied text across all my devices. Short of having an OS X version of
Drafts, this is a very efficient way to get text from a Mac to an
iOS device. However, when I’m working in Emacs, my clipboard history
quickly becomes cluttered because every bit of text I “kill” in Emacs
gets “copied” to the system pasteboard and then synchronized to all of
my devices.
Indeed, every time I select a sentence or paragraph and kill it with
C-w (kill-region), that text gets added to the clipboard
history. Worse still, every time I press M-DEL
(backward-kill-word) to kill the previous word, that word also gets
added to my history. I use these commands a lot, so even with a
history of 100 previous items, the important items in my history are
quickly buried under a heap of words and phrases that I have killed in
Emacs during the normal process of writing and editing text.
This is mitigated to some extent by the fact that Copied allows
one to quickly filter the history by just typing a search string in
the main window. However, I discovered today that Emacs has a
minor mode called delete-selection-mode and when this mode is
active, the region is replaced when a character is inserted or deleted
(e.g., with backspace). The practical implication of this is that
one can remove the active region by pressing DEL rather
than C-w:
By default, text insertion occurs normally even if the mark is
active–for example, typing a inserts the character ‘a’, then
deactivates the mark. If you enable Delete Selection mode, a minor
mode, then inserting text while the mark is active causes the text
in the region to be deleted first. To toggle Delete Selection mode
on or off, type M-x delete-selection-mode.
Also see DeleteSelectionMode on the EmacsWiki and in the
Emacs FAQ. To summarize, if one uses DEL instead of
C-w to remove text in the active region, it won’t end up in the kill
ring or clipboard history. Somehow this slipped by me, but
delete-selection-mode is enabled by default in Emacs 24 and 25.
Replacing M-DEL with an alternative that deletes without adding to
the kill ring is less obvious. Without adding a custom function, a
long version would be C-SPC M-b DEL. This sequence activates the
mark1, moves the point backward by one word, and deletes the
active region. If you find yourself using this often, it might be
better to define a backward-delete-word function to your init.el
(source):
(defun backward-delete-word (arg)"Delete characters backward until encountering the beginning of a word.With argument ARG, do this that many times."(interactive "p")(delete-region (point)(progn(backward-word arg)(point))))
I moved the default backward-kill-word binding to C-M-DEL and set
M-DEL to this new function:
Suggested snippets in TextExpander 5.0 and 5.1 are stored in plain
text and synchronize to the cloud and other devices in kind. This may
be unexpected given the current wording of the “Privacy Details”
section of the help file (below). This may seem harmless, but if not
configured carefully to exclude certain apps (e.g., Terminal, iTerm 2,
Emacs) then TextExpander may unintentionally save passwords you type
as suggested snippets, which then are stored in plain text and will
propagate to the cloud and other devices. I contacted Smile
support about this, since I thought it must be a bug or an oversight
given what I read in the documentation. They kindly clarified the
behavior and indicated that they will consider updating the
documentation or adding an FAQ entry to address these points. Read
more below for additional background, suggested configuration changes,
and details on how to remove any such sensitive information that may
have been stored in Dropbox as a result.
Background and Details
TextExpander from Smile is a Mac (and iOS) application
that allows one to define short abbreviations for longer words and
phrases. A TextExpander “snippet” consists of an abbreviation,
content (the full-text associated with the abbreviation), and rules
about how and when the abbreviation can be used. TextExpander then
watches what you type and when an abbreviation is noticed it
automatically replaces the abbreviation with the content, provided
that the rules are satisfied.
This is only a basic description the program. More advanced snippets
can include date and time arithmetic, scripts, fill-in forms with
drop-down menus and optional sections, etc. Because of this,
TextExpander is one of a core set of utilities that I install on every
Mac I work on (and every iOS device).
Snippet Suggestion
In TextExpander 5.0, released in May 2015, Smile added a new feature
called “Suggestions” whereby TextExpander will automatically suggest
new snippets based on things you frequently type. This raises obvious
privacy and security concerns, which are addressed as follows in the
TextExpander 5.1 documentation:
Privacy Details
TextExpander observes your keystrokes, as well as the contents of
the pasteboard when you Paste using ⌘V.
TextExpander will track how many times you repeat the same group of
characters and create a new suggestion after a certain amount of
repetition. However, it does not save the tracking of what you type
so the tracking is lost with each TextExpander restart. Therefore,
frequent restarting of TextExpander won’t result in many
suggestions. None of what you type is saved by TextExpander except
for the snippets listed in the Suggested snippets group.
“Tracking” does not mean TextExpander keeps a list of the actual
characters you type. Instead, it keeps an encoded record (called a
“hash”) of that group of characters, similar to the way a password
is securely stored so that no one reading it knows what it is. You
might type “yourpetsname” but what TextExpander sees and records is
“1739405847385.”
After reading this, I was fully reassured about any potential security
risks. If TextExpander only keeps an encoded record of what I type
and that record doesn’t persist across restarts, then I assumed it
only kept the information in memory and that nothing would be stored
in the cloud.
Insecure Text Input and Snippet Suggestion
There are measures in place to prevent TextExpander from storing
potentially sensitive text, like passwords. However, on my system it
still managed to occasionally suggest a passwords as new snippets.
I admit to being slightly terrified every time a notification popped
up to suggest that I add one of my important, paranoia-grade passwords
as a snippet, but at least they weren’t being stored in plain text
anywhere, or so I thought.
To understand why TextExpander was able to see my passwords in the
first place, it helps to know a little more about how text input in a
modern OS X application works. Developers can flag certain text
fields for secure text input (e.g., password fields that don’t display
what you type). The operating system won’t allow TextExpander to see
what you type in those fields and therefore it could not feasibly
suggest snippets based on that input. Applications such as 1Password
and Safari (for properly written web forms) have no problem properly
flagging secure text fields.
In other cases, it’s nearly impossible for OS X to know when secure
text is being requested. Consider a terminal emulator like Terminal
or iTerm 2, or a programmable text editor like Emacs. These
applications can run arbitrary, cross-platform code that is not
specifically written for OS X and hence does not flag secure text
entry in the way that OS X recognizes. Consider logging in to a
remote server via SSH on the command line. When you type your
password in the terminal, unless your terminal emulator is in the list
of excluded apps, TextExpander will take note that you typed a certain
string, one that happens to be your password. If you do that often
enough and you have snippet suggestion turned on, then your password
becomes a suggested snippet. Therefore, one has to be careful to
exclude all applications where passwords and other sensitive
information might be entered but not marked as secure text. Primary
examples are Terminal, iTerm 2, Emacs, and so on.
Suggested Snippet Storage and Synchronization
It turns out that suggestions are stored in plain text in an XML file
in the TextExpander settings bundle (a directory called
Settings.textexpandersettings). The file is named
group_<uuid>.xml, where <uuid> is a long string of numbers and
letters, and is associated with the “Suggested Snippets” group.
Furthermore, if you have sync enabled (via Dropbox, iCloud, etc.) then
these suggestions also sync in plain text. Because I have Dropbox
sync enabled, the group_<uuid>.xml file is stored on Dropbox. As an
example, in the Settings.textexpandersettings directory in my
Dropbox account, I found the following entry in an XML file:
Indeed, rather than REDACTED the value of the plainText key was
actually one my most important passwords–all 47-characters in full,
plain text glory.
In retrospect, I should have been much more vigilant in many ways. I
should have excluded any apps that might result in insecure text being
added as a snippet. The terminal entry weakness wasn’t obvious to me
at first though. It also wasn’t obvious to me that the suggested
snippets were being synchronized across machines because there tend to
be a large number of suggestions to filter through and, since I type
similar things on multiple machines, some similarities across machines
are to be expected.
While I still think the documentation on suggested snippets and
privacy is incomplete and potentially misleading, here is a more
thorough description of the behavior I received from Smile support:
The help text you cited refers to the tracking of keystrokes up to
the point at which a suggestion is made. Once a suggestion is made,
yes, it is stored in the settings in plain text. TextExpander
remembers the suggestions it’s found for later, so as to not keep
suggesting them again. The Help text may need clarification, but in
essence it is exactly true. The log of all keystrokes (tracking) is
discarded and only suggestions are kept.
Clean Up and Prevention
If this happened to you as well and you want to clean things up,
try the following:
Disable snippet suggestion in the TextExpander for any apps that
might not flag all secure text entry by adding them to the list of
excluded apps, as discussed above.
Note the name of the XML file in your settings bundle corresponding
to the “Suggested Snippets” group. You’ll need to find this file
on the Dropbox website later. This step can be tricky since Finder
hides the contents of the setting bundle by default.
One option is to control-click the bundle in finder and click
“Show package contents”. Then preview the group_<uuid>.xml
files until you find the one containing <key>name</key> followed
by <string>Suggested Snippets</string>.
Another option, if you’re comfortable on the command line, is to
cd to the Settings.textexpandersettings directory and grep
for "Suggested Snippets" to find the right file. For example:
cd ~/Dropbox/TextExpander/Settings.textexpandersettings
grep "Suggested Snippets" group*.xml
Turn off snippet suggestions in the TextExpander preferences
and delete the Suggested Snippets group.
The previous step should delete the corresponding XML file, but
you’ll also want to remove the Dropbox version history for the
file. On the Dropbox website, navigate to the settings bundle and
click the trashcan icon to show deleted files. Command-click (or
right click) the deleted XML file and click “Permanently delete”.