Tuesday, September 11, 2012

Disabling Auto-Indent in Emacs

I have to edit a C++ file at work whose indentation is so screwed up that any punctuation key I type ends up changing the indentation of the line I'm working on to something that is way out of line with the text right around it. For sanity's sake I have to M-x set-variable c-syntactic-indentation nil when I'm working on that file. The c-syntactic-indentation variable does not start out buffer-local, meaning that for the sake of the one stupid file, I have turned off the very useful feature of syntactic indentation for every file I have open in that session. So, if you are ever going to set it temporarily, make sure you have this line in your .emacs file:

  (make-variable-buffer-local 'c-syntactic-indentation)
 
While you're at it, don't you want a shortcut for the set-variable? I amused myself greatly this morning as I tried to think what shortcut I would assign this action to. The obvious mnemonics I polled with C-h k were taken: I have C-c TAB set to indent-region, C-c ; to comment-region, and C-c { set to a function to insert a skeleton class definition.  What to do?  I finally hit upon C-c (, and lo-and-behold I had already set that keystroke to toggle c-syntactic-indentation, sometime in the distant past! Here's the code:

  (global-set-key "\C-c("
                  '(lambda ()
                     (interactive)
                     (setq c-syntactic-indentation (not c-syntactic-indentation))
                     (message "c-syntactic-indentation set to %s"
                              (if c-syntactic-indentation "t" "nil"))))

Friday, June 1, 2012

Emacs: Scroll Other Window Up

It often happens that I have two related files open in one emacs frame, and I want to scroll around comparing them.  Of course to scroll back and forth in a single buffer, C-v scrolls you down, and M-v scrolls you up. (In a perverse bit of emacs jargon, C-v's function is called "scroll-up", since the text moves "up" relative to the fixed window -- even though every human wanting to look lower in the file thinks and says "scroll down, scroll down" -- and M-v's function is called "scroll-down".)

To keep two files synchronized as you peruse them, the power tool to use is M-x ediff-buffers, which sets up an interactive diff session to step through the diffs. Within ediff, a simple lowercase "v" scrolls lower in both files at once, and uppercase "V" scrolls higher. Don't forget M-x ediff-revision as a handy tool for comparing a version-controlled file to its latest revision, or for comparing two different revisions interactively.

Ediff is great, but sometimes it is too heavy-handed for the eyeballing I'm trying to do. For one thing, if one of the files contains a large section that is missing in the other, scrolling both files at once means striding through the section in one window while inching down a line at a time in the file that lacks it. Also, navigating ediff can be tedious if there are a great number of changes or reordered chunks, and once your diffs no longer line up sensibly, ediff's highlighting is simply a nuisance.

So there are the old standbys C-v and M-v, plus there is the handy M-C-v, which scrolls lower in the other window. That is, if I have a.txt and b.txt open in a single frame, and the cursor is in a.txt, then M-C-v will show a lower chunk of b.txt, to keep up with C-v in a.txt. Until today, whenever I wanted to move higher in b.txt, I didn't know of a single keystroke to do that, so I always just switched windows and used M-v. I could do C-u - M-C-v, but I never liked that.

Turns out there is a keystroke to scroll higher in b.txt without switching windows: M-C-S-v -- same keys as scrolling lower, plus the shift key. Now, M-C-v is a pretty ergonomic companion to C-v. I lean the base of my left hand on the Ctrl key, hit V with my left index finger, and keep my thumb poised over the Alt key to choose which window to scroll. The shift key just does not fit in with this scheme. With some effort, I can hold it with my left pinkie, but it's not comfortable. So my new solution is to tie that command to C-c M-C-v. True, it's not a single keystroke, but unlike the C-u solution above, it's all in the left hand, and close together without being cramped. The .emacs line is:

  (global-set-key "\C-c\M-\C-v" `scroll-other-window-down)

Yes, the command to go higher in the file -- to scroll up as we humans say -- is "scroll-other-window-down".

Thursday, January 12, 2012

gcc error: invalid use of member (did you forget the `&' ?)

Every time I have gotten the above compilation error, it has nothing to do with a missing ampersand. Instead, it is missing function-call parentheses:

  if (you_go && use->yourMember) { // WRONG!
    this->isInvalid();
  }

  if (i_go && use->myMember()) { // Oh yeah, the parens.
    continue;
  }

So don't use your member invalidly.

Friday, March 11, 2011

Don't Avoid No-ops

A form of spurious case analysis that seems to come up a lot in the code I have to work on right now falls under the category of "avoiding a no-op". Well, don't add cruft to your code just to avoid a no-op.

Here's the one that I see hundreds of:
  if (ptr != NULL) {
    delete ptr;
  }
Instead of:
  delete ptr;
But the line that caused me to post this today was this:
  if (!isalnum(str[i]) && (str[i] != '_')) str[i] = '_';
Why complicate the if condition just to make sure and not overwrite an underscore with an underscore? In reading through this code I paused to consider when an underscore could occur, and whether something special had to happen, only to read a little further and see that someone was guarding against a no-op. Good lord, just perform the no-op so I can read this more easily:
  if (!isalnum(str[i])) str[i] = '_';

Thursday, January 27, 2011

Filename Expansion in Tcl

Stupid, stupid, Tcl. If you want to run a shell command with a glob filename, like *.h, you can't just do:
exec ls -l *.h

You have to explicitly tell Tcl to expand the glob, because it will pass *.h to the shell literally. But you also can't do:
exec ls -l [glob *.h]

because it will pass the entire list to the shell literally (thanks, Tcl, that's useful). You get lucky and it works if there is exactly one file matched by the glob. Otherwise ls will complain that there's no file called "a.h b.h c.h".

So what do you do? Unfortunately, if you pull up the man page for exec on the internet, it tells you a way to do this that is only syntactically valid for Tcl 8.5 or greater. For those of us living in the past, the demonic incantation you must utter is:
eval [list exec ls -l] [glob *.h]

Don't use Tcl unless you have to.

Wednesday, January 26, 2011

C++ Static No Longer Deprecated

Wow, a long-held piece of geek trivia is no longer true.

This Stack Overflow article points out that in a recent draft of the upcoming C++0x revision to the C++ standard, file-scope static declarations are no longer deprecated. For, oh, 20 years, it's been one of those finer language points that you can whip out to show your sophistication and look down on the ignorant. But between the August 2010 draft (pdf) and the November 2010 draft (pdf), the section deprecating static has been struck!

If you're unfamiliar with the issue, it's that anonymous namespaces provide a more general solution to the multiply-defined symbol problem. Instead of:


static int num = 0;
static int read_num() { return num; }


the preferred C++ way to limit visibility of global objects to file scope was:


namespace {

int num = 0;
int read_num() { return num; }

} // anonymous namespace


The rationale is that you can put other symbols you wish to hide -- like class definitions -- in such a namespace, but static doesn't help you with those. As the most general solution to the problem, namespace wins and static loses. But I suppose that 20 years of failing to break people of the static habit led to a recent change of heart.

Suits me. I had been consistently using anonymous namespaces for quite a while, both to be modern and especially since it does frequently come up that I want a type that is only used in one file. But static declarations are better for self-documenting the code, so I am happy to welcome them back.

Only problem is, I need a new piece of C++ trivia to show off with.

Wednesday, November 17, 2010

Emacs: Copy Environment Variable from Shell

Sometimes I get annoyed when my emacs session has different environment variable settings than some shell buffer I have running in the session. The most painful is when a Makefile depends on environment settings that I don't have in my .profile. Command-name completion in a shell buffer can also be painful if you have changed your path. And of course I'd like gdb to start up with the correct environment every time -- you can set the variables inside gdb, but that gets old on those days when gdb itself crashes again and again.

I kept pasting export FOO=bar into the *scratch* buffer and editing it into (setenv "FOO" "bar") and eval'ing that. After the 1000th time of doing that, I decided to automate it. Turns out emacs already has an interactive function for copying an environment variable from the shell, but you have to type in the variable name. I decided to write a little function to look for the last export, or the last echo $FOO, and copy that variable:

(defun engisneering-shell-copy-env-var ()
  (interactive)
  (let* ((expat "\\(export +\\([^=\n]+\\)=\\(.+\\)\\)")
         (echpat "\\(echo +\\$\\(.+\\)\n\\(.+\\)\\)")
         (cshpat "\\(setenv +\\([^ \n]+\\) +\\(.+\\)\\)")
         (patt (concat shell-prompt-pattern
                       "\\(" expat "\\|" echpat "\\|" cshpat "\\)")))
    (save-excursion
      (if (re-search-backward patt)
          (let* ((m (or (and (match-beginning 2) 3)
                        (and (match-beginning 5) 6) 9))
                 (var (buffer-substring (match-beginning m) (match-end m)))
                 (oldval (or (getenv var) " ")))
            (shell-copy-environment-variable var)
            (setq val (getenv var))
            (message "Old %s=%s; New %s=%s" var oldval var val))))))

Run this function inside your shell buffer, and it will search backwards for the last environment variable action and bring that variable setting into the emacs session. It's also nice because it gives you a message in the minibuffer showing the change. Add a local-set-key -- I like C-c C-v -- in your shell-mode-hook, and you're good to go.