Remapping Input-Source-Switching Key

Feb. 7, 2017

Having discussed the problem of wrapping, I shall share several configurations and practices that, I find, make it easier to write prose with Vim.

The most used key in Vim, without doubt, is the Escape key, which returns to the normal mode.

And for a Chinese user, like me, the second most used function is also clear: switching input source (IS) between a Chinese-IS and English-IS. Thus the user is likely to find it cumbersome to switch mode in Vim in such need.

Indeed, to type one or more Chinese characters, he has to hit i (insert mode), switch to Chinese IS, type, switch back to English-IS, and hit escape. Such toil, unfortunately, nearly cancels the efficiency gained by Vim’s modal design.

It seems best to set both Escape key and the IS-switching key (or key combination) to be something close to the home row (the resting positiong where F and J are). Though Sierra (by now the latest version of Mac OS) allows the user to toggle between two ISs by Caps Lock (see System Preferences > Keyboard > Input Sources), I have already mapped Caps Lock as Escape (which is also possible only as late as Sierra came out). See: System Preferences > Keyboard > Keyboard > Modifier keys). And even if I set Caps Lock as the switch, still I have to set another key near to the home row to be Escape, but Sierra only allows me to set either Shift, Option, or Command as Escape, and each possibility is impractical.

Fortunately, I found Karabiner Elements, which makes it possible to re-map keys. Karabiner Elements works by forcing the physical key X‘s signal to be interpreted as key Y‘s function.

Its precursor is Karabiner, which has been out of order in Sierra. But the team produced a version called Karabiner Elements for Sierra. Here is the latest compiled image.

This is my setting. Physical Caps Lock now performs the function of Escape as before, and Escape that of `/~, and `/~ that of Tab, and Tab that of F5 (or any seldom used function key; F5 in Sierra dims the keyboard lighting), and F5 that of Caps Lock.

Then I set F5 to switch to the previously-used input source. I am proud of my setting. Vim users for non-Latin family language will save considerable effort with this!

Displaying Fullwidth Characters in Terminal Vim

Feb. 7, 2017

The following settings, as I see, makes iTerm2 properly and pleasingly display ASCII characters (hereafter ASCII), several extended Latin charactes (hereafter ELC), and both traditional and simplified Chinese characters and related symbols such as punctuation marks (hereafter CC). In the following, halfwidth means the width of ASCII, and fullwidth twice as wide. In iTerm2, a fullwidth is almost a square.

Tick those boxes concerning display:

  • Use thin strokes for anti-aliased text: “Always”
  • Use HFS+ Unicode normolization (better fidelity but slower)
  • Use a different font for non-ASCII text
  • Anti-aliased [for ASCII text]
  • Anti-aliased [for non-ASCII text]

Anti-aliasing makes strokes smoother, but too thick, thus we tell iTerm2 to use thin strokes, mitigating this effect.

I set these fonts:

  • 14pt Monaco as default ASCII font
  • 15pt PT Mono for non-ASCII text

The choice of non-ASCII font needs justification. It makes ASCII shown in 14pt Monaco, ELC in 15pt PT Mono, and CC in 15pt PingFang SC Regular.

Why is it so? In fact, I guess it is because, whenever a specified font does not cover the present character, Mac applies to it a similar styled font. So, if it is not specified that non-ASCII text shall be shown in another font, by default Mac will show CC in ST Heiti.

Previously I set non-ASCII font to be PingFang SC, since I like it. But I soon find some ELCs too wide, wider than halfwidth. After some trial and error, I discovered that PT Mono applied to ELCs looks good, and furthermore it leaves CC applied to PingFang SC, and all is well. How did it take me so long to discover this, all these months tolerating eye-strainingly thick Sans serif fonts!

I also recommend to tick those boxes to introduce more variety in font.

  • Draw bold text in bold font
  • Draw bold text in bright color
  • Italic text allowed

As for Vim, stick to the default set ambiwidth=single, and do not set it double.

In the beginning I heard it said somewhere that CC expects fullwidth, and thus requires set ambiwidth=double to be displayed correctly (see :help ambiwidth). But I this is not the case. Either Vim or MacVim running in either Terminal.app or iTerm2 already displays CC correctly with ambiwidth=single. (I suppose same is true for Japanese and Korean, but I know neither, and haven’t tried.)

In fact, when previously I set it double, I find the following ELCs displayed in fullwidth also: ß and ü used in German, and ligature Œ and œ, and Norwegian vowel ø, and some special symbols like Euro , section sign §, and pilcrow .

The fact may be related to the observation said above that some CC fonts renders some ELCs wider than halfwidth. I guess that, in these fonts, ELCs just mentioned will be better shown in fullwidth than in halfwidth.

Useful Git Commands

Feb. 6, 2017

Here is a slowly growing summary of Git commands I find helpful, with short explanations following them, addressed to the dear reader in second person. The reader may want to read “Setting up Git” first for relevant background.

I claim no intention to make it a complete table, even in the most rudimentary sense. What I have supposed to be useful, I summary and explain here, and what I have not, I do not. Again, I appreciate anyone to correct my misunderstandings, if any.

I use Mac OS (presently Sierra) on MacBook Air. I will then focus on Mac instructions, but Unix-like users shall find no difficulty in finding their counterpart. From now on, by convention, commands meant to be run in a Bash shell is prefixed with a $ for clarity, as goes the tradition. If you don’t know what that is, just open the Terminal.app, copy and paste whatever I quoted (without $), and hit the Return key.

Git commands require the current working tree set to be either the top directory or any subdirectory of it, except those inside .git/. There are workarounds if you really don’t want to do this, but I guess it is safest just to always go (by cd) into the top directory. This will be assume true hereafter.

Before we continue, keep in mind that Git comes with a detailed manual. If you have any question of fetch, for example, open Git man pages by:

$ git help fetch

Or equivalently,

$ git fetch --help

Saving and uploading: from local to remote

We may visualize git actions as interaction between the working tree, the index, and the local and remote commit history. Thus, add, commit, and push, among others, transfer data from local to remote. In this section, we study these “outward” instructions .

Comparing

Before committing, it is helpful to see what new work has been done. To show modification not added to the index, that is, differences between working tree and the files as was indexed:

$ git diff

If new work has been added to the index, you can still compare the files as was index with latest commit in the local repo,

$ git diff --staged

Instead, to compare a specific file in the working tree with the committed version of it in the local repo, say README.md,

$ git diff <commit_pointer> README.md

where <commit_pointer> is a pointer to the latest commit.

Navigating diff result

After any of these, you will see a new page that summarizes modified lines, and what is changed.

Navigation commands are identical to less, and similar to Vim. Hit j, e or ^e to forward one line (where ^ stands for Control key). Hit k, y, or ^y to backward one line. Hit f or ^f to forward one window. Hit b or ^b to backward one window. Hit d or ^d to forward one-half window. Hit u or ^u to backward one-half window. Hit q or :q to quit.

Updating the index

If there are new files created or old files deleted, run this to update the index according to the present working tree:

$ git add -A

The following does almost the same, except in that it ignores files in a newly created directory:

$ git add .

To view a list of all files saved in the working tree now and being tracked,

$ git ls-tree -r master --name-only

That is, list recursively name of files only (without commit hash) in the master branch (or any other branch specifed).

Committing

Before you commit, you may want to see a short summary of what files are changed and deleted, with

$ git status

To commit, use git commit. However a commit message is strongly recommended, which reminds you as well as other people what you have done. For example,

$ git commit -m "Rename files using new convention"

Summarize your work concisely, within less than some 70 characters. If you cannot do this, you should have probably split your work into two or more commits. It is the tradition that, to save space, verbs in base form are used, and no period is there in the end.

You can modify your commit message even after you commit, with

$ git commit --amend

Now, an editor opens, showing the commit message in the beginning, where you may revise it. This file is saved as .git/COMMIT_EDITMSG.

I find it useful to edit here because you don’t have to backslash-escape special characters, which may occur in verbatim expressions. You can also add more explanatory lines below, separated from the title with a blank line. The status is shown again as commented lines to recapitulate their difference for you.

Viewing Commit Log

Afterwards, you can view the commit log with

$ git log

The result shows commit IDs, authors, time stamps, and commit messages.

To make the log more concise and informative,

$ git log --all --decorate --oneline --graph

The names of option are pretty explanatory. Mnemonic: “a dog”.

Identifying preceding commits

The pointer to current commit in question is named HEAD. To retrieved its ancestors, two operators could be used. HEAD^2 means going up two levels of node, pointing to the youngest of its grandparents of the head. HEAD~2 means, within its several parents, counting twice the youngest elder siblings. The two operators may be composed, for example HEAD^3~2.

Pushing

To push all commits from the local repo to the master branch in the remote repo (origin) whenever they have not been updated,

$ git push origin master

In the first time you push, you may want to create a “upstream tracking reference” to your remote repo by -u, so that, in the future when you push or pull, you do not need options by default.

$ git push -u origin master

Say yes, if, the first time you push, you are asked that whether you should consider the RSA host key for GitHub’s IP address as a safe one.

If you have messed up something, and Git is unable to figure out their ancestral relation and thus refuses to push, you may try with the flag -f or --force:

$ git push origin master -f

This forces every commit in question in the local repo to overwrite its counterpart in the remote repo, and may cause the remote repo to lose data, so think twice before using it.

Adding tags

To add a tag to the latest commit (even when you have modified the working tree after commiting), use

$ git tag -a "v1.0" -m "Compile successful"

Replace the version number and tag message with yours; same for below.

To list simply all tag names,

$ git tag

To see what (long) commit hash a tag points to, run

$ git rev-list -1 "v1.0"

A git push don’t automatically push a tag. You have to

$ git push origin "v1.0"

Updating or regressing the working tree: from remote to local

We may also transfer data from remote to local, by virtue of clone, fetch, pull, checkout, and reset, among others.

Downloading from remote repo

To download everything from the remote repo into an empty directory (which must be empty, otherwise there would be merge conflicts), that is to say clone, we shall cd into it and

$ git clone git@github.com:aminopterin/computer-notes.git .

(The remote url is taken as that of this repo for example.) Replace . to the path you want to clone into. If . is omitted, the entire repo is cloned to be a new subdirectory in the current directory.

On the other hand, to do the same (download from remote repo) with an existent working tree, that is to say fetch, we may use

$ git fetch origin master

(Or any remote branch instead of master.) This keeps creates a local copy of a remote branch in the local repo (the .git directory) only, while not affecting the working tree, nor the head. Interestingly, only when we checkout, do we automatically merge the remote branch, having being fetched but not yet in the working tree, with the local branch where the working tree is.

Meanwhile, a pull not only fetches all remote branches, as just describe in the above paragraph, but automatically tries to merge all conflicts into the current pointed branch.

$ git pull origin master

(Or any remote branch instead of master.) I feel that it is clearer to fetch and merge explicitly than just to pull, unless the situation is simple enough.

Creating or forking a new branch

A branch is a duplication of the working tree, so that the new branch based on it keeps being revised and developed afterwards. Thus, modifications happen in parallel, not only along the original branch, but the new one too. If the branch being based on is other people’s work on GitHub for example, we also say it (the old branch) is being forked. To create a new branch <name_of_new_branch>, basing on the current working tree,

$ git branch <name_of_new_branch>

Shifting the head

But the command branch only does not change the head. To check out an existent branch <another_branch> already created with git branch,

$ git checkout -b <another_branch>

In general, local files will be overwritten, making the working tree match <another_branch>.

I suppose that, after modifying in the working tree, we had better commit them before checking out another branch, otherwise it is not obvious what will happen. If I make some change in (say) a test branch and, without committing, check out back the master branch, then the working tree will not restore to the state of the latest commit of master branch, and there will be no error message.

To make sure whether the head is pointed to the intended branch, use

$ git branch

It will return a list of branches, with the current one highlighted (in my case colored green, and prefixed with a asterisk).

Reversing the working tree

One way of reversing the working tree to a previous commit (together with the index and the head) is reset, namely

$ git reset HEAD^2 --hard

Alternatively, the option --mixed (which is the default option) only reverses the index and the head, and --soft only reverses the head.

Beside reset, revert is another way to restore the working tree, but unlike reset, it creates a new commit. For example, with

$ git revert <commit_pointer>

a new commit which is the previous commit being pointed, is now created. Note that the history is not rewritten in this case.

Managing branches

In addition, we may also modify commit objects and the connection between them, changing the history and relation between branches.

Merging branches

$ git merge <merged_branch>

If there are conflicts, there will be an error message that tells you that. Git essentially overwrites the conflicting part of the file in the working tree.

<<<<<<<
(The text excerpts according to the chief branch being merged into.)
|||||||
(The text excerpts according to the common ancestor version.)
=======
(The text excerpts according to the feature or development branch merging into the chief branch being considered.)
>>>>>>>

The user is asked to replace lines between <<<<<<< and >>>>>>> (inclusive) with what he wants. After all conflicts are resolved, add the changes and merge again.

Rewriting history

Sometimes we might wish to rewrite the history, so that the last 5 commits (say) become one. One way to do this is

$ git reset --hard HEAD~5
$ git merge --squash HEAD@{1}

The first command resets the head to the commit just before the last 5 commits, and the second command combines these commits. Here, HEAD@{1} is the position of the head just before the previous command. The command reset was already discussed above. After these commands, commit.

Alternatively (slight difference exists which I do not elaborate here),

$ git reset --soft HEAD~5

Then commit those squashed changes. The commit message will be helpfully prepopulated with the commit messages of all the squashed commits.

Then commit those squashed changes. The commit message will be helpfully prepopulated with the commit messages of all the squashed commits.

It is even more interesting to “replay” a series of modification onto elsewhere. The command rebase applies respective changes of each commit from the current commit (where HEAD is) on the specified branch, by creating corresponding new commits.

$ git rebase <branch_being_merged_into>

Alternatively, we may choose one commit of a certain branch only, and apply the change from the branching node up to itself, on the current commit. That is, doing the same as rebase only on certain commits, instead of all of them of that branch.

$ git cherry-pick <commit_being_picked>

More Information

  • The Git Documentation This is the documentation on the official homepage of Git. Same material may be found in man pages that are included in the Git package itself, to quote the site.
  • Richard E. Silverman (2013). Git Pocket Guide. Sebastopol, CA: O’Reilly Media. Guides for Git are abundant. This is a readable short guide that may both be read from cover to cover, and looked up as reference.
  • Git—The Simple Guide A table of for the most common Git commands, and very brief explanations for them. On the site, there is a downloadable PDF version.
  • Stack Overflow Stack Overflow is still the most likely place you end up with if you google your problem, but, since everyone can submit, you should take their advice with a grain of salt.