Syntax Highlighting
Although I do enjoy Emacs' font-lock mode, it does leave something
to be desired after seeing the very fine-grained syntax highlighting
of Visual Studio 7. In graphics heavy code such as the code I write,
a lot of array lookups are done in tight loops. Visual Studio
highlights operators, such as the '[' and ']' operators, which makes
array heavy code much more readable. Emacs does not, by default.
Solution: Write new font-lock rules for c++-mode. I haven't
included all of the syntax here, but this is what works for me without
looking too fruity:
(font-lock-add-keywords 'c++-mode
'(
;; Currently support for []|&!.+=-/%*,()<>{}
("\\(\\[\\|\\]\\|[|!\\.\\+\\=\\&]\\|-\\|\\/\\|\\%\\|\\*\\|,\\|(\\|)\\|>\\
|<\\|{\\|}\\)" 1 font-lock-operator-face )
<BR>
; End of c++ statement
("\\(;\\)" 1 font-lock-end-statement )
))
Apologies to regex pros. I get the job done, but I'm not great.
You will also want to define font-lock-operator-face and
font-lock-end-statement:
(make-face 'font-lock-operator-face)
(make-face 'font-lock-end-statement)
(setq font-lock-operator-face 'font-lock-operator-face)
(setq font-lock-end-statement 'font-lock-end-statement)
There are multiple ways to assign a face. You will want to do it
in a way that is similar to anything you already have customized.
Status compared to Visual Studio: Emacs is a perfectly
acceptable alternative. If you turn off precaching of syntax
highlighting, it's a bit slower to load a file. Jumping around a
60,000 line c++ source tree on my 1.2ghz Athlon is still easily within
acceptable ranges.
"Go To Definition"
Visual Studio 7 lets you right click on a component of a statement
(usually a function, variable or typedef) and go to it's definition.
This handy feature lets you jump around a source tree in a snappy
fashion. Emacs has a similar feature out of the box. You need to
build a tags table, which is essentially a precached list of all of
the definitions. Go to the root of your source tree, and type:
etags `find -iname "*.cpp"` `find -iname "*.h"`
This produces a file called 'TAGS' in the current working directory.
Next, in Emacs, type M-x visit-tags-table and select the TAGS
file.
In true Emacs fashion, the best way of navigating the tags is
through the bindings. M-. will find a tag. M-* brings your cursor
back to the place it was at before the most recent tag find. (Yes,
this works recursively!) C-TAB completes the current keyword from the
tags table database.
Status compared to Visual Studio: Not quite as smart or
accurate as the Visual C++ tag finder. I find myself having to revert
to M-x grep from time to time, or M-x occur (try it). On the bright
side, there is no latency: the precaching in Emacs makes for one of
those rare situations where Emacs is actually faster than Visual
Studio in it's default state. I'm also a huge fan of the ability to
push and pop my navigation points through the source tree. Mixed bag.
I'd choose Emacs over Visual Studio if I had to take one
implementation over the other, overall.
Compilation
Getting Visual Studio 7 projects to compile through Emacs is one of
the thorniest issues addressed here. If you used Visual C++ 6.0, you
know that used to be able to export Makefiles. Well, in its infinite
wisdom, Microsoft has removed this ability from Visual Studio 7.
Instead, you must invoke the IDE from the command line and get it to
build from the .sln file.
If you were to bring up a shell (with devenv.exe and .com in your
$PATH), you could type "devenv.com foo.sln /build Debug /out
output.log" and have it compile your project from the command line.
Caveat - if you don't specify devenv.com, you get devenv.exe, which,
for seemingly no good reason, sends it's output somewhere else than
stdout, which ensures that you won't see it unless you're running
cmd.exe. Always specify devenv.com when building from the command
line.
In the simplest case of directory structures, all you need to do to
compile a Visual Studio 7 project in Emacs is to set the default
compile command:
(setq compile-command "devenv.com foo.sln /build Debug")
The usual C-` to step through the compiler errors works just fine with
the compile buffer's output.
Honestly, I maintain a multi-directory project with some
complications, so I call a perl script which actually goes ahead and
calls devenv with the proper build configuration. I don't mind
keeping that logic out of Emacs.
Compiling a Single File
One thing I hate about Visual Studio is what happens when I make a
small change in a file, and the entire project's dependancies are
checked before a single file is compiled. If you're like me, and you
have a ton of dependancies in a big project, it can take upwards of
around a minute to compile your single line change. Unfortunately,
this minute can become two or three minutes if you muck your changes
up and need to recompile. I'd rather compile the file by itself to
make sure all of the changes are done and final before calling in the
dependancy checker and the linker with the above step. In Visual
Studio, I could hit ctrl-F7, but that's the grace an IDE gives
you.
We can shell out and run Visual Studio's cl.exe (assuming it's in
your path). But, with what arguments and environment? Good question.
You need to set the INCLUDE environment variable for the single
file compilation environment. When loading my project, I run the
following command:
(setenv "INCLUDE" "C:\\Program Files\\Microsoft Visual
Studio .NET\\Vc7\\include;C:\\Program Files\\Microsoft Visual
Studio .NET\\Vc7\\atlmfc\\include;C:\\Program Files\\Microsoft
Visual Studio
.NET\\Vc7\\PlatformSDK\\include\\prerelease;C:\\Program
Files\\Microsoft Visual Studio
.NET\\Vc7\\PlatformSDK\\include;C:\\Program Files\\Microsoft
Visual Studio .NET\\FrameworkSDK\\include;")
Your mileage will vary. Don't copy this directly, but
instead follow the directions above for aquiring your own INCLUDE
environment variable set. Compile your project in Visual Studio, and
check out the generated buildlog.htm. It will list off the
environment the program was compiled in. Grab the contents of the
INCLUDE line, and use it in Emacs, remembering to escape the
slashes.
Next, we need to address the cl.exe syntax. We want to build the
source file similar to the already-built obj files so it will drop in
nicely when it's linked. Open the buildlog.htm back up. Near the
bottom, there will be talk of options put into a temp file and then
passed to cl.exe. We want these in emacs. Therefore, set this
variable, which we'll use in a second. Mine looks similar to
this:
(setq c++-compile-current-file "cl.exe /Od /D \"WIN32\"
/D \"_DEBUG\" /D \"_WINDOWS\" /D \"_WIN32\" /D \"QUAKE3\" /FD /EHsc
/MDd /YX\"stdafx.h\" /Fp\".\\Debug/foo.pch\" /Fo\".\\Debug/\"
/Fd\".\\Debug/\" /FR\".\\Debug/\" /W3 /c /ZI ")
Given that we have defined c++-compile-current-file, the
following LISP function compiles against it:
(defun c++-compile-current ()
(interactive)
(let ((compile-command
(concat c++-compile-current-file (buffer-file-name))))
(save-buffer)
(recompile)
))
It'd be cool if I had a Perl script to generate the required LISP
based on the contents of a buildlog.htm. I haven't done that yet,
though.
Status compared to Visual Studio: A bit of a bitch to get
working, but once it works, you're there. Compiling a single file
isn't recommended for small projects, as it's a bit of an effort to
get working, but you wouldn't need the speed gains on a small project
anyway. Considering Visual Studio does this out of the box, there is
no contest as to which I'd prefer. But, let's face it, you're taking
a chunk of logic usually reserved for IDEs, and making it execute
through Emacs. Not bad, considering.
Starting the Debug Executable
After you build a binary, you're going to want to run it, right? I
simply execute a shell script which sets up the current working
directory properly, and runs the debug executable. Consider the
following:
(defun shell-run-debug ()
(interactive)
(shell-command "sh rundebug.sh"))
You may also need to set the environment properly here.
Status compared to Visual Studio: Visual Studio 7 rearranges
your windows, sometimes needlessly, when you enter debug mode. In
this case, you are waiting for a crash before you are taken into the
Visual Studio debugger. In practice, the executable starts up
slightly faster. I am annoyed less, but ymmv. Both get the job
done.
Class Browser
Visual Studio has the class/file/definition browser. Emacs has the
speedbar. It's installed by default. Fire up a file in your project
once you have your tags loaded (see above for tags tips), and type
M-x speedbar.
I haven't had much experience with the speedbar, but it seems
pretty usable so far. But then, I don't care for Visual Studio's
class browser 90% of the time. I have it off.
Status compared to Visual Studio: The Visual Studio browser
seems to be a bit more snappy. I don't appreciate having to double
click a file to open it, or the flickery nature of the highlight in
speedbar. This may be fixable - like I said, I'm not a big speedbar
user in the first place. Speedbar gets points for it's use in all
different languages. I still prefer Visual Studio's functionality
here. Best interface in town.
Indentation and Code Style Compatibility
There is a small army of coders who dislike some aspect of then
default indentation style in Emacs' C/C++ modes. I myself, have been
hassled by respectable programmers for using it.
Braces cause a double indentation:~the braces are indented once, and then whatever is past the braces is indented again. This is
not a bug, or an oversight - it is the GNU coding style at work. In
addition, Visual Studio displays the tabs generated by Emacs by
default incorrectly. If you are working with people who have not
discovered The One True Editor, you will want to convert your tabs to
spaces. In addition, I will throw in a line that shows how to reduce
the number of spaces per tab stop in case your code project requires
it, and a line that automatically places your cursor at the required
indent level instead of the far left of your buffer where you will be
needing to press TAB anyhow.
(setq-default tab-width 2) ; Tab width set to 2 spaces
(setq-default indent-tabs-mode nil) ; Indentation cannot insert tabs
(add-hook 'c++-mode-hook '(lambda () (local-set-key "\C-m" 'c-context-line-break)))
(add-hook 'c++-mode-hook '(lambda () (c-set-offset 'substatement-open '0)))
Status compared to Visual Studio: Emacs does precisely what
I want here. I didn't even mention anything about electric braces or
anything of that nature, since the object of this document is to help
with the jealousy Emacs users feel against Visual Studio 7. Taking
Emacs beyond Visual Studio 7's featureset is an exercise left to the
reader.
Project-Centric Functionality
The concept of projects and solutions in Visual Studio is central
to it's operation. Emacs is not an IDE, but it does play one on TV.
I can make Emacs do enough to satisfy my requirements. There is a
session management tool for Emacs out there, but I
don't really need that. I take a more simple route, of setting a few
states within Emacs whenever I approach working on a project. Observe
the following code which opens a source file in one of the projects,
fires up the speedbar, starts a tags table definition and sets up the
environment for compiling a single file in the project:
(defun project-foo ()
(interactive)
(save-excursion
(find-file "c:\\foo\\bar.cpp")
(find-file "c:\\foo\\bar.h")
(visit-tags-table "c:\\foo\\TAGS")
(setq compile-command "perl compile.pl")
(setq c++-compile-current-file "cl.exe /Od ")
(setenv "INCLUDE" "c:\\foo\\bar\\include;")
(speedbar)
)
)
Status compared to Visual Studio: Emacs does what I want it
to. Visual Studio wins because it's an IDE that works for a single
compiler, and there is a ton of raw functionality behind the idea of
projects and solutions.
Scrolling
By default in Emacs, when you scroll, you are given what amounts to
half a page down when your cursor reaches the end of the screen. If
you prefer the smooth scrolling of Visual Studio, you can get it:
(setq scroll-step 1)
(setq scroll-conservatively 50)
Sadly, in Emacs, your cursor always has to be in the screen, to my
knowledge. In Visual Studio, you can mouse wheel scroll away from the
current editing position, and when you get back to it, your cursor
will be where you left it. Emacs waits until your cursor is at the
edge of the screen, then it reluctantly drags it along. You are given
a choice, however. The following line will make the cursor start
scrolling right away:
(setq scroll-preserve-screen-position nil)
Status compared to Visual Studio: Overall, I like the
scrolling features in Emacs more, despite the gripe I have above. I
like the idea of scroll-margin which allows me to "look
before I leap".
What I Haven't Touched On
Visual Studio 7 still excels over Emacs in a number of ways I haven't
touched on here. One of the biggest issues in the list is debugging.
The idea of stepping through the code, making changes and linking into
the running code (a feature that has been there since Visual Studio 6)
is something that is best done from the IDE.
Visual Studio also has the Intellisense feature. I am told that
this feature can be compared to the functionality of the speedbar,
coupled with Emacs packages EIEIO and the Semantic Bovinator. I am
okay with the way things are currently, but will definitely be
checking these packages out once I become familiar with my current
working environment. You can click here to jump
ahead of me and learn how this works for yourself, already.
MSDN help. The ability to jump around the MSDN help is better in
Visual Studio. I don't use MSDN help in my day to day life. I would
be interested to hear what others are doing.
"Reverse" integration, because it's not out there (yet?). There is
a plugin for Visual Studio that lets you use Emacs in lieu of the
in-IDE editor. You click on a file in the IDE, and Emacs pops up
instead of the Visual Studio 6 editor. Here is a link
to VisEmacs. It's got nothing to do with Visual Studio 7. The last
release was in 2000. And yes, I have tried to see if I could get it
to work, and I fell short of success.
Visual Source Safe. I use CVS.
Additional reading based on editorial comments posted to this article: