.PHONY in Makefiles

Chinese version written Oct. 31, 2016
English translated Jan. 31, 2017

Makefile Reviewed

GNU Make is a utility used to manage the building process, i.e., the linkage of compiled files. In it, a target is separated by a colon “:” from its prerequisite; this specifies a dependency. The left-hand-side target’s last modified date is intended to be later than any of the right-hand-side prerequisite’s last modified date. Otherwise, the recipe that follows the dependency declaration will be executed.

For example, I want to cook spaghetti with tomato sauce. If I have bought a new pack of spaghetti today, I shall cook the newly bought spaghetti, and shall not use the spaghetti I left last night in the refrigerator. This way, though the tomato sauce and minced meat need not be cooked again, but the totality of “spaghetti with tomato sauce” is thus newly cooked.

The Keyword .PHONY

That said, if you have seen someone else’s makefile, you would probably see the line

.PHONY: clean

which is used to clean all the already compiled files. Note that the word clean has nothing special, but you have to specify its meaning, like:

clean:
    rm -rf ./*.pdf

Why .PHONY is necessary? I have heard it from a post on Stack Overflow that, while clean is not a real object, but an action, if there is a file named clean on the same directory (probably not a good idea to choose such name), than Make will not execute the recipe of clean, thinking it is up to date. But by declaring it phony, no longer does clean‘s recipe depend on existence actual files, unlike genuine targets. Indeed, the literal meaning of “phony” is “fake”.

But, while the example regarding phony targets given just above assumes that clean has no prerequistes, how is it like when clean does have one or more prerequisites? Indeed, now that a phony target does not even have last modified date (because it is not a file), it is not clear, then, when the recipe is executed, or if it is always executed.

Some Experiments

The First Test

Out of curiosity, I conducted a small experiment on all. Recall that all is executed when make all is run. Consider the makefile

all: `f1` `f2`
	@echo recipe \`\`all\'\' executed.
`f1`:
	@echo process 1:
	touch `f1`
`f2`:
	@echo process 2:
	touch `f2`
clean:
	rm `f1`
	rm `f2`
.PHONY: clean

When neither of f1 nor f2 exists, when I run make, the console output is, of course,

process 1:
touch `f1`
process 2:
touch `f2`
recipe ``all'' executed.

That is, both targets were generated. When I run make again, it is then

make: `all' is up to date.

That is, recipe of all was not run.

The Second Test

To reverse the situation to the beginning, run make clean which deletes both of f1 and f2. Now, I generate an empty file by touch all. And I run make, and f1 and f2 will again be generated as above, and the console output is all the same as above. If make is run for a few more times, it says all is up to date as above.

This is because Make thought all remained to be generated again, for neither of f1 nor f2 exists.

The Third Test

Again run make clean to delete f1 and f2. This time, I run make first to generate them, and run touch all afterwards. Now run make, and notice the console outputs

make: `all' is up to date.

Because the empty file all has shielded the target all, the recipe of all is not executed. Make thought I intended to make the file all in left-hand-side, which is later than right-hand-side, f1 and f2. That is, here all is existent and up to date.

The Fourth Test

Subsequently, I add this line in my makefile:

.PHONY: all

and save the makefile. And I run make again. The console output is, instead,

recipe ``all'' executed.

Indeed recipe of all is executed, as I thought. But f1 and f2 was not generated, as they already exists. Same happens when make is run for several more times. What a reassurance!

Conclusion

In conclusion, as how I understand it, if a target has been declared as prerequisite of the .PHONY target, i.e. a target is phony, then its recipe will always be executed. Of course, when following the recipe, its own prerequisites must exist and up to date, as usual; if they are outdated, they will be generated in advance. In particular, if the phony target has no prerequisite, its recipe will be directly executed.

A phony target, in short, is thought of as something older than everything, as old as time. “File as old as time, true as it can be.”

Setting up Git

Jan. 25, 2017

Here is a memorandum for Git settings I use. I hope that this helps future newcomers to Git find information sometimes hard to look up.

It has not been long since I started to learn to use Git, and many point are simply paraphrased from some Stack Overflow answer. Thus, I encourage and deeply appreciate that the reader shall remind me of every error.

Installing Git

In Mac, Xcode is built-in. Xcode includes Command Line Tools, which in turn includes Git. In case you need to install Xcode, find it in App Store.

However, Xcode takes up a lot of space (10G at least), so if you don’t develop software for Mac, you may well uninstall it, along with relevant libraries. You can still install Command Line Tools for Xcode only. To do this,

$ xcode-select --install

You may also directly install Git using Homebrew:

$ brew install git

To check current version, so that you know whether update is successful:

$ git --version

Beware that Mac user may not be allowed to overwrite the built-in binary that came with Xcode. If you are determined to overwrite,

$ brew link --overwrite git

To make sure this is effective, you may check current location of binary:

$ which git

if the result is /usr/bin, then this git is the built-in one. If it is /usr/local/bin, this git must have been the one you installed.

Basic concepts

It is best that the reader take some time understanding the underlying mechanism of Git, so that from now on we can better describe its operations and grasp their meaning. I suspect many popular conceptions, being simplications, are wrong in a strict sense, and unfortunately, they often bewilder rather than clarify things for the reader.

  • A tree is an aggregate of nodes, each of which contains a pointer of its own child or children, if any.
  • A blob is an opaque binary file that records the the content of a file in the manner Git may read.
  • A tree object is a representation of directory. Each subdirectory of its is represented by another tree that it points to, and each file of its by a blob that it points to.
  • A working tree is the totality of actual source files you are working on. All you see and edit (using Git or not) belongs to the working tree. For convenience, I say that there is a top directory which is the minimal directory that encompasses the working tree.
  • A local repository (local repo) is a subdirectory in the working tree named .git/. Everything Git needs is saved inside the local repo.
  • An index lists files in the working tree Git is aware of. The index is saved in the local repo. We say that Git tracks these indexed files.
  • When you commit, you create a blob called commit object that includes sufficient information to describe the present working tree, along with a unique hash generated according to the author, time stamp, file contents, among other figures.
  • Commits are linked to each other according to respective historical relation, which makes up a commit history.
  • A stash is a commit object not yet committed.
  • When you clone a repo, you you create a copy of it.
  • When you fork a repo, you clone it and modify it, thus generating two diverging versions, and subsequently different histories occur.
  • When you fork you own local repo, you create a branch. Even if the local repo has not branched, we also say there is only one branch called master.
  • A remote repository (remote repo) plays a similar role to local repo, but is store in an Git hosting service, like GitHub. It stores a working tree and commit history.
  • When you push, you download files from the remote repo to to those of local repo.
  • When you pull, you upload files from the local repo to those of remote repo.

Creating local repo

Now let us say that there is a directory called ~/computer_notes. Run cd into it:

$ cd ~/computer_notes

and generate local repo with:

$ git init

Following our example, the local repo will be named ~/computer_notes/.git.

Remember that nothing affects the remote repo unless you push. The implication is that if you have screwed up something and want to start anew from the very beginning, willing to abandon the not-yet-pushed changes in the working tree, you can simply delete the whole /.git and git init again.

Creating remote repo

To create a new remote repo, sign up a Git service provider if you haven’t. No wonder the most popular choice is GitHub. But Bitbucket is also widely used. The difference that concerns average user most is probably that Bitbucket offers unlimited private repositories for free while GitHub does not, but that Bitbucket limits number of users for free to 5 while GitHub does not. You may well sign up both, taking their respective advantage.

The following instructions apply to GitHub, but you get the main idea. In your personal homepage, click the plus sign on the upper right corner, and choose “New repository”.

When you name your remote repo, note that GitHub disallow special characters, and forbids even underscores, but allows hyphen. I deem it a good convention to name local repo with underscore-separated words, and remote repo with the same name with hyphen-separated words. Traditionally repos are named all in lower case. Following the above example, let us call it computer-notes.

GitHub offers two protocals: https and ssh. More on this on a later section. For now, enter the page for the repo, and click the green button “Clone or download”. If the small title reads “Clone with HTTPS”, there is a url that looks like

https://github.com/aminopterin/computer-notes.git

Click “Use SSH”, and you will see something like

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

To set remote url, go back to your terminal emulator. First you have to create an abbreviation of the remote url called origin. If you decide to use SSH, use

$ git remote add origin git@github.com:aminopterin/computer-notes.git

After having created origin, if you have to change url later, use set-url.

$ git remote set-url origin git@github.com:aminopterin/computer-notes.git

To list currently existent remote repo(s):

$ git remote -v

The result will look like:

origin  git@github.com:aminopterin/computer-notes.git (fetch)
origin  git@github.com:aminopterin/computer-notes.git (push)

To see more information in order to trouble-shoot, you may

$ git remote show origin

Configuring Git

Keep in mind that it is the same thing to run, in the terminal,

$ git config --global foo.bar quux

as to append the line

[foo]
    bar = quux

to ~/.gitconfig (the second line should start with a tab). I will use the former form in the below.

Ignoring certain files

It is not in general Git’s purpose to track binary files. Binary files cannot be compared by utility diff, thus cannot be merged or rebased in the normal sense. So you don’t track binary files unless some routines really need it, and so it must be uploaded to the remote repo. For example, a logo of your program that you want those who clone to see. Create a file named .gitignore in the top directory to tell Git to ignore specified files, all files in specified directories, or all files having specified extension. If, for sake of illustration, your .gitignore has these lines,

.DS_Store
*.pdf
sketch/

Then the .DS_Store file in the top directory of working tree, all .pdf files anywhere in the working tree, and all files in the subdirectory called sketch/ in the top directory, will be ignored by Git. You can play around with wildcard *.

Note that, after .gitignore being modified, those files that have been tracked before but are no longer supposed to be tracked now, will however keep being tracked. To stop tracking them, remove all the cache by

$ git rm -r --cached . 

Then add and commit as usual. This also works when, for some reason, a subdirectory is previously ignored, for example because the subdirectory was itself a git working tree, and thus cannot be added for now. Removing caches and adding to index again, solves such an issue.

Specifying text editor for commit message

The default editor for commit message is vi. However, some of the lines in my .vimrc is not compatible with vi (which is normal), and Vim is used anyway with wrong color settings.

To set Vim as the commit message editor, put these in ~/.bashrc:

export VISUAL=vim
export EDITOR="$VISUAL"

You can replace with your favorite editor.

Alternatively, you can specify the editor not through Bash’s setting but through Git’s, with

$ git config --global core.editor "vim"

Color

To color Git’s console output,

$ git config --global color.ui auto

All the various color.* (where * is a wildcard) configurations available with git commands, will then be set. The auto makes sure Git try only those colors the current terminals emulator supports. When a command is used for the first time, relevant color configuration is set.

Generating SSH key and automatic login

HTTPS and SSH differ in several aspects. On one hand, in the presence of a firewall, the HTTPS port is almost always open, while the SSH port is not, being often blocked by network firewalls. On the other hand, an SSH Key is more secure in that, under SSH protocol, the user does not actually login, but under HTTPS he or she has to login.

Under SSH protocol, authentication of SSH Key is easily managed by an ssh-agent, while under HTTPS, the user may also use a credential helper, which I have not tried.

To generate an SSH key for RSA cryptosystem, having 4096 bits, with your email specified,

$ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

You will be asked for the filename with path (by default, ~/.ssh/id_rsa), and a passphrase. Since with an SSH-agent you have only to type the passphrase once, you may well choose a reasonably long (say, some 6-word) phrase. Special characters are not only allowed, but in fact encouraged.

If you have several keys, you may rename the public key (the one with extension .pub) and private key (the one without), but they must share the same name except for extension. Let us rename it rsa_github.pub with mv ~/.ssh/id_rsa ~/.ssh/rsa_github.pub.

Browse your GitHub account, and go to “Personal settings”, and then “SSH and GPG keys”. Click “New SSH key”. Copy and paste the whole content of your public key, or more simply,

$ cat ~/.ssh/rsa_github.pub | pbcopy

and paste it into the box.

To let Keychain remember the key, in ~/.ssh/config append UseKeychain yes after the relevant lines (omitted lines shown as ...)

Host github.com
   User git
   ...
   UseKeychain yes

Hopefully, you will never be asked for passphrase.

Reminding GitHub the correct language

GitHub guesses the language in which most sources in your repo is written in. A comprehensive list is found here. The language is shown besides a round dot in each repo in your own profile page.

If you would like to use an idiosyncratic extension, GitHub may judge it wrongly. For example, GitHub does not recognize .hpp as C++ header. To prevent this, you add a file named .gitattributes in the top directory, with content

*.hpp linguist-language=C++

Rendition of non-ASCII characters

By default, non-ASCII characters, such as Chinese characters, are backslash-escaped according to C-styled character codes. For example, “我” becomes \346\210\221. To show them as verbatim,

$ git config --global core.quotepath false

Writing Prose with Vim

Jan. 21, 2017

This note first discusses the wrapping problem. I observe the pros and cons of both soft and hard wrap.

The present concerns are urged by my decision to use plain text to save my pieces of prose in Markdown. They are in turn edited with Vim, since, in the past four months or so, I have grown gradually accustomed with Vim, and have started to like it. Still, attempt to write and edit prose, rather source code, with Vim, leads to a number of difficulties related to wrapping of text.

Terminology

Before discussing the wrapping problem, I assume the reader has a basic familiarity with Vim. (If you do not, please do yourself a favor and give it a try.) And I now define several terms.

A window is the part of monitor used to display text. If toggled full screen, the window is all of the monitor screen; if :vsp (vertical split) is run, the window is half of the screen. A frame is the present-visible part of the text file.

I say a physical line is an ordered collection of characters delimited by end-of-line (EOL); see another note of mine, “End-of-line Characters”. On the contrary, a apparent line is an ordered collection of characters displayed in a single row in the current window.

I say a plain text file is softly-wrapped if physical lines are allowed to be longer than current window width. And, on the other hand, I say a plain text file is hard-wrapped if such EOL have been inserted that no physical line is longer than current window width.

A compiler (or interpreter) is said to weakly render the EOL if a single EOL in the source is ignored. Meanwhile, a compiler is said to strongly render the EOL if a single EOL in the source is rendered a newline in the binary executive (or visual output). Both a weakly- and strongly-rendering compilers render two consecutive EOL the beginning of new paragraph.

Editing Softly-Wrapped Text with Vim

I just distinguished soft wrap and hard wrap. One will soon find it cumbersome in several aspects to edit soft wrapped text with Vim.

Indeed, by a “line” Vim means a physical line, and in a soft-wrapped piece of prose, a physical line is a paragraph. The result is a file with very long physical lines and blank lines alternating each other.

Navigation

In Vim, j and k stand for navigation in terms of physical lines, while gj and gk the same in terms of apparent lines. Thus the first inconvenience the user may experience is that, in a piece of prose, we normally intend the action of gj and gk, and may still occasionally need j and k. Thus we may map j to gj, and k to gk, and conversely gj to j, and gk to k.

Displaying Lines Not Fully Shown

Another irritation is that, in the very end of the current window, there may be some space shorter than the next physical line; in this case, that physical line is not shown and such space is wasted. This effectively makes my window (already being split) quite small.

A partial workaround is to toggle between displaying the next line as much as possible, and not displaying altogether:

noremap <silent> <leader>\ :call ToggleDisplayLastLine()<cr>

where the function ToggleDisplayLastLine() is implemented as thus:

function! ToggleDisplayLastLine()
    if (&display=='')
        let &display='truncate'
    else
        let &display=''
    endif
endfunction

Scrolling

Moreover, it is awkward to scroll either for half frame (<ctrl>u and <ctrl>d) or a whole frame (<ctrl>f and <ctrl>b).

As for half-frame scrolling, in fact, half of the window does not correspond to many lines, because every physical line now consists of too many apparent lines. Furthermore, a “half frame” now is often much less than half of a window, with some rows wasted in the manner pointed above.

As for whole-frame scrolling, Vim reserves 2 lines, and repeats them both in the previous and the present frame. Now, “scrolling a whole frame” does not really shows the “next frame”, but the next frame with substantial shift, since a paragraph is long.

In both case, it is difficult to visualize the new position after scrolling.

Before long I have made it a habit to always scroll for whole frame, and have mapped:

nnoremap <c-f> <c-f><c-e><c-e>M
nnoremap <c-b> <c-b><c-y><c-y>M

This is easier to visualize, since the previous and the next frame does not overlap, just like pages in a book.

Line Numbers

But a final complain remains unresolved, that is line numbers. Line numbers are crucial in navigating, for example gg20 (jumping to line 20), substitution, for example :5,10s/good/bad/g (replacing “good” with “bad” from line 5 to line 10), or even the decision made before executing 10dd (cutting 10 lines starting from cursor position), when the user takes a look at line numbers and estimates how many lines are to be copied.

The line numbers in Vim, however, is also counted according to physical lines. Since now it is really twice of the paragraph number, such information is not particularly helpful. In contrast, in Microsoft Word apparent line numbers many be shown, and such feature is more convenient.

Considering Hard Wrap

I am not the first one aware of the problem. Many Vim lovers, as I have searched, too proposed their solutions, and some of them, I have tried. The answer vary, but most suggest hard wrap.

The supporters for hard wrap might hold that it is not the source code that matters, but the output. Indeed, the prose written in plain text is sort of source code that has to be interpreted in order to be published, though we don’t normally think this way. I will say more of this below.

The user puts as many words as can be in every physical line before (for example) 74 characters is reached. EOL will be inserted automatically with

set textwidth=74

The reader may want to check formatoptions (see :h fo-table) for detailed settings. The default is tcq (see :h formatoptions). Here t auto-wraps text using textwidth, c does the same for code comments, and q allows formatting of comments with gq.

Unfortunately, when I rearrange material, the physical lines become uneven. Then I cannot estimate the amount of words from a block of text, because end of apparent lines are uneven. But such estimation is important, for such visual cues suggest hint me instantly whether a paragraph is too long, which I should break, or it is too short, which I should combine with adjacent paragraphs.

Re-formatting in Real Time

To make ends of visual lines even, there is a automatically re-formatting command gqap, but it only applies to the current paragraph. Maybe the user can define a shortcut for this, but reformatting still needs to be done when typing, which is a distraction.

More problematically, this doesn’t work for Chinese characters. For them, the command gqap changes nothing. Maybe it is possible, with some Vimscript, to tell Vim what a paragraph in Chinese is, but I don’t know how.

But for the moment let us bypass the point by assuming there is a convenient way to work with hard wrapped prose, and that hard wrapped text is visually satisfying to the user.

Even so, what if I want to copy and paste my prose into somewhere else, like WordPress or Facebook, where EOL is strongly rendered? The paragraphs, once hard wrapped, are not easily converted back to soft wrapped ones.

Luke Maciak recommends

:%norm vipJ

But the result is horrible. Indented code blocks get merged with preceding and following paragraphs (even with a blank line separating each of them), and shorter paragraphs are swallowed too.

Compilation: Four Possibilities

Moreover, there seems to be some controversy about whether EOL is rendered weakly or strongly by Markdown compilers. Though LaTeX compilers always render the EOL weakly, but some Markdown compilers render the EOL weakly, some strongly. This fact, I have deliberately avoid addressing in the above.

Actually, Gruber’s original specification does not resolve parsing issue unambiguously, and GitHub, Pandoc, WordPress, and so on, all give their own implementation. Such disorder motivates CommonMark, the effort to standardize Markdown.

For one thing, Grober hints weak rendition in his spec (same link as above):

The implication of the “one or more consecutive lines of text” rule is that Markdown supports “hard-wrapped” text paragraphs. This differs significantly from most other text-to-HTML formatters […] which translate every line break character in a paragraph into a <br /> tag.

And it is true that GitHub, Pandoc, Stack Overflow, and CommonMark Spec all render EOL weakly.

Unfortunately, WordPress and early GitHub render EOL strongly, showing the concern that available Markdown compilers may still remain inconsistent. For the former, see WordPress’s Markdown quick reference. For the latter, compare this Meta Stack Exchange question and debate there.

Thus there are four cases:

  1. Text hard wrapped, EOL weakly rendered.
  2. Text hard wrapped, EOL strongly rendered.
  3. Text soft wrapped, EOL weakly rendered.
  4. Text soft wrapped, EOL strongly rendered.

Cases (1) and (4) are desired. But (3) also produces the same result. Meanwhile, (2) fixes text width in output, making typesetting inflexible. For the case of PDF, if the text width is less than page width the margin may be wider than conceived, and if it is more than page width each physical line will take up more than one display line.

Notice however that soft wrap is unaffected in both cases (3) and (4). After all, soft wrap avoids single EOL, in which weak and strong rendition differs.

The practice of hard wrap also violates the principle of Markdown somewhat. The original design of Markdown intends that even the source should be easy to read. But hard wrap isn’t something that laypeople (knowing maybe only Microsoft Word) will spontaneously do. It is fair to say hard wrap makes no semantic sense, and is thus artificial.

One user-submitted Wikipedia guideline (retrieved as of Feb. 2017) also raises a similar point against hard wrap. Wiki-markup interpreter also renders EOL weakly, so the guideline is relevant here, although Wiki-markup is different from Markdown. It continues to stress that hard wrap creates a number of difficulties, for example in that they have to be removed before a numbered list or bullet point is added.

“Halfway Wrap” at Every End of Sentence

A compromised way is to insert EOL at every end of sentence; I shall call this halfway wrap. This way, every physical line is a sentence or a clause, which is a meaningful entity. It thus makes sense to go up, go down, cut, or paste an entire line.

But the method of halfway wrap makes ends of apparent lines uneven again, and I have argued above that such estimation plays important role in the creating process.

Still, when writing Markdown, I find it useful to halfway wrap, when I am in the early stage of sketching. Halfway wrap makes it easy to assemble material by cutting or pasting an entire physical line. Afterwards, I remove EOLs between physical lines by visual selecting and J when writing in English, or gJ, in Chinese.

Returning to Soft Wrap

After a long contemplation on the matter, I decide to return to the original habit: I will always halfway-wrap TEX sources, but softly-wrap Markdown sources.

The case for Markdown is different from that of LaTeX. Even though both render EOL weakly, I always see compiled pdf on the facing side, using for example TeXstudio, but view only Markdown source when working on it. Thus I want Markdown sources to visually resemble the output, but that is not necessary as I work on LaTeX sources.

As I see it, the best solution is that Vim show apparent line numbers, rather than physical line numbers. I hope future Vim will add this as a feature. No wonder, this behavior is contrary to Vim’s design—a “line editor”. All of Vim’s commands deal with the file in a line-by-line basis.

But this does not seem to me completely out of the question. To cater both code-writers and prose-writers, there may even be a setting that toggles line number style between them. Or specification on which is used, may be saved in a “modeline”, that is, the first and last few lines of a source file. If in GUI of Vim, like in Microsoft Word, a fairly large, warped arrow appears where a EOL is, hard and soft wrap may be easily distinguished visually, and it will not be confusing to recognize the line number style.

But also arguably, if in a piece of prose the majority of paragraphs are too long to be properly worked with when shown in Vim, these paragraphs are probably too long.

If iTerm’s window fits the screen, as I count it, a split window can show at most 74 halfwidth characters such as ASCII characters, or equivalently, 37 fullwidth characters such as Chinese and Chinese punctuations. The height of window can fit 33 lines if none is wrapped. This amounts to 2442 halfwidth or 1221 fullwidth characters. A paragraph as long as half of the capacity of the present window is probably too long. That is at most 610.5 characters or so. I will probably write paragraph as long as this very seldom (unless I wish to write a parody of In Search of Lost Time, or achieve the conciousness stream in the very last chapter of Ulysses).

That said, it is still unsatisfying that scrolling is not smooth, but a new frame must start with a displayed line of a paragraph (physical line) too. In Microsoft Word or TextEdit for example, this need not be the case, but the first displayed line of in new frame may be any displayed line in any paragraph.

Difference between .bashrc and .bash_profile

Jan. 19, 2017

To understand when .bash_profile or .bashrc are used and differences between them, we introduce several terms. I shall focus on Mac OS, which I use.

In an interactive shell, the user may type commands into it, execute them, and see their output on the screen. A non-interactive shell, in contrast, does not allow the user so. The moment one enters Mac’s native Terminal.app, he or she is using an interactive shell. However, when he runs a shell script, a non-interactive shell is started that runs the script, and then exits when it finishes doing that.

A login shell requires the user to type username and password to log in, but a non-login shell does not. (With definition above, they are interactive.) But Terminal.app automatically logs in when starting up, and thus is a login shell. Of course, the shell that is created by typing the command login, is too a login shell. And the examples of non-login shell include the new Bash instance the user invokes by typing /bin/bash, or another kind of shell, in a terminal.

In Mac, an interactive login shell reads .bash_profile, while an interactive non-login shell reads .bashrc. Default paths of .bash_profile and .bashrc are both the user’s home. On the other hand, a non-interactive shell reads neither.

Actually there are /etc/profile (which Mac has created by default), /etc/bash.bashrc, ~/.bash_login, ~/.profile, BASH_ENV, and ~/.bash_logout, all with subtlety due to historical reasons. But I am not going to figure them out. Instead, the most reliable solution is that the user shall put everything he sets into ~/.bashrc, and make ~/.bash_profile source ~/.bashrc, as I did.

The “Solarized” and other color schemes for Vim

Jan. 7, 2017

This note first explains the basic idea of syntax highlighting in Vim, then describes my experience with several popular color schemes, reminds the reader several caveats, and points out the moral of the story. I will focus only on Vim opened in the terminal emulator iTerm2, rather than GUI version of Vim, for example gVim or MacVim.

Terminology

In Vim, plain text, I say, is parsed with rules according to its file extension. Text are then separated as syntax items, which are strings that match a certain pre-defined pattern. For instance, new is picked out as a c++ keyword that instructs the operating system to find a memory block of a certain size for sake of holding variables, and to report its address. Vim’s syntax files are responsible for parsing. For further information, see :h syntax.

Afterwards, several syntax items are classified into one of the 16 possible syntax groups, all or which is intended to be colored the same. A function from syntax items to syntax groups is said a syntax recognition. For example, in a .cpp file the string new belongs to the syntax group “Statement”. In particular, I say that an item is trivially recognized, if it is mapped to the Normal group, i.e., being ignored.

How it is colored, however, depends on the color scheme. Following above example, new is colored bright yellow in Vim’s “default” color scheme, but grass-green in “Solarized”. For our purpose, all relevant colors may be described in terms of a hex triplet; for example, #ffff00 stands for yellow. In other words, the set consisting of all hex-triplet-represented color is a universal set. Then, I say, a function from syntax groups to hex triplets is a color realization. A realization is specified by a color scheme file with extension .vim.

And furthermore, I say, a hex triplet is shown as a physical color on a computer monitor; for example, #ffff00 is shown as a yellow color on the monitor. I call such map from hex triplets to physical colors to be a display. If a set of displayed physical colors is significantly different from the physical colors envisioned by the creator, they are said to be displayed incorrectly.

If opened in a graphical user interface (GUI), for example gVim or MacVim, all hex triplets are able to be displayed, and we are done.

Meanwhile, in a terminal, I say that each of the 16 syntax group are assigned a hex triplet. What follows is split into two cases.

If termguicolors is set true (i.e. with set termguicolors), I call this a direct assignation. Then variables guifg and guibg are used. We set their values to be hex triplets, specifying the assignation.

Nevertheless, iTerm2 (as well as Mac’s native Terminal.app in recent years) can only display 256 hex triplets. These 256 numbers, I say, are valid hex triplets. A function from hex triplets onto valid triplets now must be applied, which must be an identical function when restricted to the set of valid triplets. I call such function satisfying this a degradation. A realization, then, is a composition of assignation and degradation.

But if termguicolors is set false (i.e. with set notermguicolors), I call this a palette assignation. Then variables ctermfg and ctermbg are used —— in such case, each group are in turn given a nonnegative integer.

At this stage it is further split into two cases, according to whether t_Co (number of colors) is 16 or 256. If 16, such integer ranges from 0 to 15, and is input to a 16-color ANSI palette. If 256, it ranges from 0 to 255, and is input to the 256-color “xterm” palette. Here, I say, a palette is a function from a set of nonnegative integers to hex triplets.

An ANSI palette is specified by the present terminal theme (open preferences to set it), while the “xterm” palette is more or less identical in each terminal emulator, thanks to “xterm” standards. From above, we observe an important fact that in either of 16 or 256 cases, degradation is identical.

In conclusion, let me say that a syntax highlighting is a tuple comprised of a parsing, a recognition, and a realization. Afterwards the highlighting is displayed as physical color.

I noticed it to be very hard to find these pieces of information correct and nicely summarized. I learn these only after having read dozens of Stack Overflow questions and whatever forum posts I searched.

Quest for Color Scheme

Supposing the reader has a vague understanding of these concepts, I can now give an account of my personal quest of alternative Vim color scheme. Until not long ago, as I used iTerm2 and use Vim in terminal, I always chose “Tango Dark” theme (and automatically applying its palette) it provided, and applied the “default” scheme in .vimrc. They looked very great, and I had no intention to change.

Nevertheless, starting from one version of iTerm2 (probably one having been released in Nov. 2016), the physical colors suddenly looked strange. What in the before had been realized as vivid red, became pale purple, and that had been dark blue, now bright as sky-blue.

By then, I had no idea why. Looking back by now, I conclude that it is probably because “Tango Dark”‘s ANSI palette is changed starting in newer versions. But afterwards I learnt that don’t like the physical color under direct assignation, either —— though I should have known that if I had tried the direct assignation, I would have saw the same.

Anyway, I decided to import a third-party scheme. To do so, one possible way, which I did, is to copy the downloaded .vim file that specifies the assignation, and to paste it into ~/.vim/colors/. Alternatively, the whole repo in the GitHub page may be saved in ~/.vim/bundle, so that a plugin manager may find it. With a manager, it is easier to update and remove packages.

The most easy-to-use manager, I think, is Tim Pope’s “Pathogen”. To invoke it, include

execute pathogen#infect()

in .vimrc before the colorscheme line.

Several popular schemes I found were, in order of discovery: (listing GitHub repos)

  • “Solarized” from altercation/vim-colors-solarized
  • “Flattened” from romainl/flattened
  • “Gruvbox” from morhetz/gruvbox
  • “Tomorrow” family of variants from chriskempson/tomorrow-theme
  • “Molokai” from tomasr/molokai

“Solarized” and its Issues

Ethan Schoonover’s popular scheme “Solarized” attempts to realize a collection of painstakingly chosen pastel colors both eye-comforting and readable. The underlying mechanism is unnecessarily complicated, and I did not go to great lengths. But below is how I understand it.

If under palette assignation, “Solarized” requires a suitable ANSI palette to ultimately display correctly. Thus “Solarized” relies on the corresponding “Solarized” theme, and hence corresponding palette. Fortunately, the release of iTerm2 includes the “Solarized” theme. With any other theme, it is to be realized and so displayed incorrectly.

But if under direct assignation, none of the hex triplets being assigned is valid in the case of “Solarized”. As a result, iTerm2 is forced to perform degradation, and its judgement is poor. The result is a bunch of undefined behavior and incorrect realization.

Thus motivated, Romain Lafourcade prepared a simplified version named “Flattened” for sake of robustness. When under direct assignation, it assigns hex triplets same as “Solarized”, but defines degradation function in case true colors are not feasible. And when under palette assignation, it assigns a number within the already standardized 256 xterm palette, ensuring they are all valid, irrespective of the ANSI palette the terminal theme provides.

Similar can be said of all other schemes I listed. For direct assignation, they either simply assign a hex triplets of a valid color, or assign an invalid triplet but specify degradation method themselves. For palette assignation, it directly assigns a valid color in the xterm palette, which must be valid. They all work (though very slightly differently) with either palette or direct assignation.

I suppose it is fair to say that the original “Solarized”, though carefully researched and customized, is ill-designed.

On Highlighting TeX and Markdown

I don’t deem myself having obsessive–compulsive personality disorder, but none of the color schemes is completely satisfactory. In particular, I shall remark on two file formats not strictly being programming languages: LaTeX (.tex) and Markdown (.md). Both, I use a lot—I write in LaTeX reports or notes containing mathematics, and in Markdown personal and creative writings. Unfortunately, their recognition seems to be less well-prepared.

As I observe it, in a .tex file two syntax items are most frequently parsed: the enclosing delimiters (like {}), and backslash-appended commands (like \lambda). For one thing, “Solarized” (or, following it, “Flattened”) recognizes both nontrivially. But “Gruvbox”, “Molokai”, and all of Vim’s native schemes recognize only delimiters nontrivially. And each of “Tomorrow”‘s variants recognizes both trivially.

In addition, among five schemes I listed, only “Gruvbox” recognizes .md nontrivially. It then seems that in general “Gruvbox” best recognizes syntax.

On their Assigned Colors

I find colors that “Gruvbox” assigned is bright and easier to see, but are a bit too close and too identically “warm”. And those of “Solarized” are much more vivid, and bluish-green background may be good for the eyes, but most colors are dim. In particular, the Ignored item in the Normal group (see :h syntax) is difficult to read except with screen set to be the brightest.

Since “Solarized” is the first I tried, intending to fix this, I found the file “solarized.vim” long and cryptic, and not obvious where and how the Normal group is defined. And my attempts to set normal text in brighter by virtue of simply override in .vimrc, all failed.

Afterwards I discovered “Flattened”, which applies easier method to mix colors. In flattened_dark.vim, I chose a brighter grey, and set

hi Normal guifg=#bac3c4

To choose the triplet, one may want to see 256 native colors of the present terminal. Scripts such as mgedmin/show-all-256-colors.py in GitHub Gist is handy.

Lastly, “Solarized” theme also makes console output too dim, as is the case of normal text in Vim. To change it, in ~/.bash_profile, to the line export PS1= which sets you personal prompt, please append \[\033[1;37m\] (escape sequence for pure white in the ANSI palette) at the end. At this point, I am more than satisfied.

Too Long, didn’t Read

In short, copy flattened.vim into ~/.vim/colors/ folder, and in the file, substitute for hi Normal guifg a hex color code of brighter grey. After that, enjoy writing and coding in the lovely Vim for the rest of your life. And, oh, remember the lesson that it is better to design a product with reasonably good quality but reliable, like “Flattened”, rather than one extremely fine-tuned but hardly works, like “Solarized”.