Tuesday, December 16

More Emacs Help

As well as keying the help provided by Google in Emacs to the major-mode, it's possible to use it with multiple libraries as well. For instance, the following snippet lets me look up documentation for one library (OGRE) by pressing 'o' and for another by pressing 'a' , rather like the Slime selector, discussed previously. It's a huseful way of pulling together doxygen API documentation when working on clonk-loads of libraries, as is all to typical of C++ or Java development.


(defun search-site-url (keyword &optional site inurl lucky)
"Do a Google search for KEYWORD. Restrict to SITE and INURL, if specified.
Jump to best match (I Feel Lucky) if LUCKY set.
"
;; (message (list keyword site inurl lucky))
(concat "http://www.google.com/"
(format "search?q=%s" (url-hexify-string keyword))
(if site (format "+site:%s" (url-hexify-string site)))
(if inurl (format "+inurl:%s" (url-hexify-string inurl)))
(if lucky "&btnI")))


(defun help-selector ()
(interactive)
(message "Select [%s]: "
(apply #'string (mapcar #'car help-selector-methods)))
(let* ((ch (save-window-excursion
(select-window (minibuffer-window))
(read-char)))
(params (find ch help-selector-methods :key #'car)))
(browse-url (apply #'search-site-url (thing-at-point 'symbol) (list (third params) (fourth params) (fifth params))))))

(defvar help-selector-methods '(( ?a "Assimp"
"assimp.sourceforge.net" "/lib_html/" t)
( ?o "Ogre"
"www.ogre3d.org" "/docs/api/html/" t)
( ?w "WxWidgets"
"docs.wxwidgets.org" "/stable/" t)))

(global-set-key [(shift f1)] 'help-selector)

Thursday, August 21

Lambda of the Daleks


As promised, the Daleks appear. They have been loaded dynamically from an md2 whilst the framework was running. Sometimes I find myself fighting my C++ reflexes. There's no need to bring the framework down in order to change it. It still seems like magic when I edit a function definition, compile it and *bam* the change appears in the game window...






...one day, all software will be developed this way. Onwards..

Sunday, August 17

And now for a bit of swank..


Now my testbed talks happily to emacs via slime. That's the "exploratory" part of "exploratory game developement.". Next up: Daleks...

And now for something completley different.


You guessed it - another testbed. This time, in 100% Common Lisp (SBCL), just for comparison.

Wednesday, August 13

More testbed


I've done some more playing around with that testbed, integrating ECL and G3Ds Shape API. It works ok, apart from a few wrinkles. It might even be the beginnings of a nice educational app for learning 3d transforms and the like. The main improvement is that the REPL gives you error feedback rather than crashing the app when you give it a bad form. A distinct must for interactivity...

Tuesday, July 22

What next?


Embedding ECLS in a 3D Engine - G3D - proved remarkably easy. The question is - now what. What should live on the Lisp side and what should live on the C++ side? Geometry for C++? AI for Lisp..what about collisions, then? Hmm..

Wednesday, July 16

Using Google for Context Sensitive Help in Emacs


Here's a hack that combines three of the most useful things in the known Universe: Google, Emacs and Firefox. Emacs 22.x has a standard method for invoking a browser: the browse URL function. To customise it use:


M-x customize-group browse-url
We are interested in the settings: Browse Url Browser Function which should be set to
browse-url-firefox
Then there is Browse Url Firefox Program which should be set to the full path of wherever firefox lives on your machine, and Browse Url Firefox New Window Is Tab which should be on to prevent multiple firefoxen cluttering up your windows when you try this.


Now that you have customized everything, you drop a crafted function into your .emacs and bind it to a key:



;; -- INTEGRATED HELP
(require 'url)
(defun search-site-url (site url keyword)
(concat "http://www.google.com/"
(format "search?q=%s+site:%s+inurl:%s&btnI"
(url-hexify-string keyword)
(url-hexify-string site)
(url-hexify-string url))))

(defun wxhelp ()
"Open a window showing the wxWidgets documentation for the word under the point"
(interactive)
(browse-url (search-site-url "docs.wxwidgets.org" "2\\\\.8\\\\.6"
(thing-at-point 'symbol))))

(global-set-key "\C-h\C-w" 'wxhelp)



Now, if you type Control H Control W while the cursor is over a symbol, Emacs will look it up via Google. For instance, if your cursor is over wxApp, by the magic of "I'm feeling Lucky", Google will find http://docs.wxwidgets.org/2.8.6/wx_wxapp.html which is the manual page for wxApp



Obviously the interesting function here is

site-search-url
which munges up an URL to feed to google to make it search a specific site for a keyword, filtering URLs that do not contain a certian string. The highly escaped \\\\ is to ensure that 2\.8\.6 appears in the final URL so that Google treats . as a literal.



The great utility of this hack is its universiality. It will work for any language or library with a sanely organised reference web site. Language specific lookup functions based on sites like cppreference.com are left as an exercise for the reader...

Friday, March 14

Emacs Selectors

One of the nicer things that comes with slime is the slime-selector: it's just a little buffer switching thing, that lets you press r to switch to the repl buffer, d to switch to the debugger buffer, s to switch to the scratch buffer and so forth. Bound to a function key, it's an extraordinarily convenient way of switching to a specific buffer.



Of course, it's geared to SLIME and Lisp, but when coding C++ on the day job I often find myself switching between code, the debugger, shell and compilation buffer, so without further ado, here is the C equivalent, which is easily hacked up to include other buffers, too: actually, I'm beginning to think something like this functionality should be in emacs by default, as it eliminates a lot of keyboard gymnastics.







;; slime selector clone for c ----------------------------------

(defvar c-selector-methods nil)

(defun c-selector ()
(interactive)
(message "Select [%s]: "
(apply #'string (mapcar #'car c-selector-methods)))
(let* ((ch (save-window-excursion
(select-window (minibuffer-window))
(read-char)))
(method (find ch c-selector-methods :key #'car)))
(cond ((null method)
(message (format "No method for charachter: %s" ch))
(ding)
(sleep-for 1)
(discard-input)
(c-selector))
(t
(funcall (third method))))))


(defmacro def-c-selector-method (key description &rest body)
`(setq c-selector-methods
(sort* (cons (list ,key ,description
(lambda ()
(let ((buffer (progn ,@body)))
(cond ((get-buffer buffer)
(switch-to-buffer buffer))
(t
(message "No such buffer")
(ding))))))
(remove* ,key c-selector-methods :key #'car))
#'< :key #'car)))

(def-c-selector-method ?d "GDB debugger buffer"
gud-comint-buffer)

(def-c-selector-method ?g "Grep buffer"
grep-last-buffer)

(def-c-selector-method ?l "Buffer List"
(let ((result (get-buffer "*buffer-selection*")))
(if result
result
((bs-show)
(get-buffer "*buffer-selection*")))))

(def-c-selector-method ?c "Compilation buffer"
(let ((result (get-buffer "*compilation*")))
(if result
result
(compilation-find-buffer))))

(def-c-selector-method ?s "Shell buffer"
(get-buffer "*shell*"))

Friday, February 22

ASDF for the slightly confused

Well, you have a shiny new lisp compiler/interpreter/environment on your machine, and like an eager and smart newbie you want to do something with it. Right there and then many lisp newbies go SPLAT and end up as a bug on a windscreen, later to be scraped off and fed to Guido's pet snake. So here is ASDF for confused newbies (a very different proposition from a dummy, who should go back to reading bright yellow paperbacks and programming Java in the enterprise shop window).


ASDF is about 500 lines of lisp source, and a dot asd file is the lisp equivalent of the ubqutious make; you can't do anything in a serious modern lisp environment without it, so lets take you through it step by step. An asd file describes a system that contains modules and components. Components are usually individual files. Modules are collections of files, usually in subdirectories. Let's look at an example dot asd file: this one is for cowl - a simple opengl gui written by William Robinson who is working on Cityscape, a promising
game found at this address.


(defsystem #:cowl-ftgl
:depends-on (#:cffi)
:description "FTGL Common Lisp wrapper for Cowl."
:components ((:module "src"
:components
((:file "cowl-ftgl")))))


The defsystem form then defines a system with one component which is a module called src which in turn contains one component in a file called cowl-ftgl, which will be a lisp file, naturally: since it's in a module called src, it will be loaded in the src directory below the directory that holds the dot asd file. Sometimes it is necessary to wrap the defsystem form in it's own package using defpackage and a throwaway package name. We do not do that here because we do not define any symbols that we would need to refer to externally, but we would need to if we were defining new component classes or methods to be used in the system, examples of which will follow later in the article.


Components, modules and systems are defined as CLOS objects in asdf.lisp, so of course they can be operated upon with generic functions, and there are quite a few that come with asdf. The key one is named with Arc-like gnomicness: "oos" - which I imagine is short for "Operate on System". It takes at least two parameters: the operation to perform and the system to perform it on.


The operation you are most likely to perfom is load-op: this loads and compiles a system, and the form is simply:


(asdf:oos 'asdf:load-op 'system-name)

Other operations are compile-op, and test-op: the latter will only have meaning if the author of your system has provided tests, as some packages do.


ASDF will search for systems using the list of functions referred to by *system-definition-search-functions* used to search for .asd files in the filesystem. By default this contains only one function, (sysdef-central-registry-search) which is perfectly adequate for file systems with symbolic links. The modus operandi on such system is to have a share/common-lisp/systems directory populated with symbolic links to dot asd files which may be downloaded and untarred in any part of the fs tree to which the user has access. Then the (sysdef-central-registry-search) scans the list of directories referred to by *central-registry* for dot asd files containing the system which it needs to operate on.



To put it another way, procedurally this translates to:


  1. Download your asdf package

  2. Unzip - say to ~/thinngy/cl-blab

  3. Note that ~/thinngy/cl-blab/cl-blab.asd is the system.

  4. Create a ~/share/common-lisp/systems directory for systems
  5. cd ~/share/common-lisp/system
  6. ln -s ~/thinggy/cl-blab/cl-blab.asd cl-blab.asd
  7. now fire up your lisp and enter..

    ;; for systems that don't do (require 'asdf)
    (load #"/path/to/asdf.lisp")

    ;; the / on the end of the path is important, don't forget it
    (push #"/home/me/share/common-lisp/systems/" asdf:*central-registry*)

    (asdf:oos 'asdf:load-op 'cl-blab)

For Windows the situation is sligtly more complicated: either you write your own search function *or*, more practically apply a patch that lets you use shortcuts in place of symlinks.



You'd be right to point out that this is on the laborious side, but the purpose of this article is to demonstrate how asdf *works*. There are two packages for automating lisp package and asdf system installation: asdf-install and clbuild, which I encourage newbies to investigate, in the usual sadistic exercise for the reader.



Why go through all this palaver with asdf? Well the beauty of asdf is that systems, components and modules are in fact CLOS objects, so we can do tricks like this:




(cffi:load-foreign-library
(make-pathname
:name "cftgl"
:type "so"
:directory (directory-namestring (asdf:component-pathname
(asdf:find-system :cowl-ftgl)))))

..which loads "clftgl.so" which is found in the same directory as the dot asd file containing the system definition. Highly useful when distributing a C shared lib that wraps a C++ library so that it can be called into from Lisp.


The fact that source files are component, lets us write specialist methods for different kinds of source file such as c-source-file, java-source-file, html-file (all defined in asdf.lisp) allows asdf to work as a make replacement, operating on non-lisp source, thus:



(defmethod output-files ((op compile-op) (c c-source-file))
(list (make-pathname :type "o" :defaults
(component-pathname c))))

(defmethod perform ((op compile-op) (c c-source-file))
(unless
(= 0 (run-shell-command "/usr/bin/gcc -fPIC -o ~S -c ~S"
(unix-name (car (output-files op c)))
(unix-name (component-pathname c))))
(error 'operation-error :operation op :component c)))

(defmethod perform ((operation load-op) (c c-source-file))
t)

There is an extended example of this kind of thing where asdf is extended to work with a fortran to lisp converter here.

To conclude: like many things in Lisp, asdf is prickly for newbies and takes time to get to know and use well. However, when you do you can do things with it for which you'd normally need another build system, independent of your language. As Brucio would observe: Lisp does not have this limitation and Common Lispers laugh at things like ant.

Friday, December 28

Hippie Expand and Autocompletion in Emacs

The Emacs way of doing auto - completion is known as Hippie Expand which is a function that uses a variety of methods to try and expand an incomplete word near the point. Why it's called Hippie Expand I have no idea. The name is a bit misleading; for a long time I thought it was related to zippy or pinhead. Perhaps it's something to do with Emacs being used and written by hippies, who knows?


The nice thing about hippie expand is that like many things in Emacs, it can be customized and expanded, via the make-hippie-expand function. For instance, here is the hippie-expand function I use for c-mode



(make-hippie-expand-function
'(try-expand-dabbrev-visible
try-expand-dabbrev-from-kill
try-expand-dabbrev-all-buffers
try-complete-file-name-partially
try-complete-file-name)))))

This function first tries to expand the thing found at the point as a dynamic abbreviation, which is based on a scan of existing text for words or symbols with the same beginning as the one at the point. For instance, if "catalogue" is in the buffer, and you expand "cat", then "catalogue" will be one of the expansions offered, which is useful if "catalogue" is a variable name you use a lot.


The hippie expand function is bound to a key (I use M-/) and each press of that key will cycle through the possible completions for you. It's not Intellisense, but it does cut down on typing if you use descriptive variable names.


It's also fairly trivial to write your own hippie-expand function. Here is an example of a hippie expand function which does completion based on a list of current tags - which are sometimes more useful than dabbrevs.



;; This is a simple function to return the point at the beginning of the symbol to be completed
(defun he-tag-beg ()
(let ((p
(save-excursion
(backward-word 1)
(point))))
p))

;; The actual expansion function
(defun try-expand-tag (old)
;; old is true if we have already attempted an expansion
(unless old
;; he-init-string is used to capture the string we are trying to complete
(he-init-string (he-tag-beg) (point))
;; he-expand list is the list of possible expansions
(setq he-expand-list (sort
(all-completions he-search-string 'tags-complete-tag) 'string-lessp)))
;; now we go through the list, looking for an expansion that isn't in the table of previously
;; tried expansions
(while (and he-expand-list
(he-string-member (car he-expand-list) he-tried-table))
(setq he-expand-list (cdr he-expand-list)))
;; if we didn't have any expansions left, reset the expansion list
(if (null he-expand-list)
(progn
(when old (he-reset-string))
())
;; otherwise offer the expansion at the head of the list
(he-substitute-string (car he-expand-list))
;; and put that expansion into the tried expansions list
(setq he-expand-list (cdr he-expand-list))
t))
;; done, now we just use it as a clause in our make-hippie-expand-function (as above)


Hippie-expand isn't the only auto-completion gizmo I work with in Emacs; there is also slime-complete-symbol which does symbol completion for my while I am working in SLIME. This can be neatly integrated into hippie-expand, like so..


;; hippie expand slime symbol
(defun he-slime-symbol-beg ()
(let ((p
(slime-symbol-start-pos)))
p))

(defun try-expand-slime-symbol (old)
(unless old
(he-init-string (he-slime-symbol-beg) (point))
(setq he-expand-list (sort
(car (slime-contextual-completions (slime-symbol-start-pos) (slime-symbol-end-pos))) 'string-lessp)))
(while (and he-expand-list
(he-string-member (car he-expand-list) he-tried-table))
(setq he-expand-list (cdr he-expand-list)))
(if (null he-expand-list)
(progn
(when old (he-reset-string))
())
(he-substitute-string (car he-expand-list))
(setq he-expand-list (cdr he-expand-list))
t))

EDIT: With newer (CVS and 3.0) versions of SLIME the api for symbol completion
has changed and the hippie-expand function for slime symbol completion is..


;; hippie expand slime symbol
(defun he-slime-symbol-beg ()
(let ((p
(slime-symbol-start-pos)))
p))

(defun try-expand-slime-symbol (old)
(unless old
(he-init-string (he-slime-symbol-beg) (point))
(setq he-expand-list (sort
(car (slime-simple-completions
(buffer-substring-no-properties (slime-symbol-start-pos) (slime-symbol-end-pos))))
'string-lessp)))
(while (and he-expand-list
(he-string-member (car he-expand-list) he-tried-table))
(setq he-expand-list (cdr he-expand-list)))
(if (null he-expand-list)
(progn
(when old (he-reset-string))
())
(he-substitute-string (car he-expand-list))
(setq he-expand-list (cdr he-expand-list))
t))

Thursday, December 20

Final Results of Informal Programming Language Comparison.

A recent post on programming.reddit.com about profanity in comments was highly amusing if you are childish enough to be amused by such things - I am, anyway - set me to thinking about the Google Code search thing and whether it could be used in any profitable way to compare programming languages. I quicky hit upon the hypothesis that programmers content with their language would be more likely to type comments along the lines of "this rocks" and ones more disaffected might type comments along the lines of "this sucks".


Hence, all that was required was to write a bit of code to tabutlate the search results by language and a clear winner would emerge. It did, and it wasn't the one I was expecting.


My final choice of metric was the ratio of incidence of the word "sucks" and the word "rocks" scaled by the frequency of a netural control word such as "okay" (and even so, poor old FORTH did not get a lookin as it got zero hits on any of these). I call this the suckage to rockage ration and henceforth regard it as the gold standard of programming metrics. Without further ado, here are the charts and the code.




Controversy is roughly the distance between suckage and rockage - high if the language arouses strong feelings, low if it's boring, staple stuff.




C!? I really didn't expect this. It might be due to the inability of the search to separate out C and C++. It's almost certianly due to a low suckage rather than a high rockage.



The code, which is Public Domain, if anyone is inclined to tinker.



(asdf:oos 'asdf:load-op 'drakma)
(asdf:oos 'asdf:load-op 's-xml)
(asdf:oos 'asdf:load-op 'clot)

(defpackage :code-index (:use :cl :clot :drakma :s-xml))

(in-package :code-index)

(defun code-search (regexp &key language license file package (output-type :sxml))
(let ((url (concatenate 'string
"http://google.com/codesearch/feeds/search?q="
(when language (concatenate 'string "lang:" language "+"))
(when license (concatenate 'string "license:" license "+"))
(when file (concatenate 'string "file:" file "+"))
(when package (concatenate 'string "package:" package "+"))
regexp)))
(multiple-value-bind (body-or-stream status-code headers uri stream must-close reason-phrase)
(drakma::http-request url
:force-binary t)
(declare (ignore headers must-close stream reason-phrase))
(format t "Request for ~A~%" uri)
(format t "Status code ~A~%" status-code)
(parse-xml-string (flexi-streams:octets-to-string body-or-stream :external-format (flexi-streams:make-external-format :utf-8)) ))))

;; google returns a malformed string every time - wallies!
;;(code-search "sucks" :language "pascal")

(defun code-search-hit-count (results)
(parse-integer (cadr (nth 5 results))))

(defun calculate-index-for-language (lang)
(format t "~&For Langauge : ~A~&" lang)
(let* ((control-index
(code-search-hit-count (code-search "okay" :language lang)))
(suckage-index
(/ (code-search-hit-count (code-search "sucks" :language lang))
control-index))
(rockage-index
(/ (code-search-hit-count (code-search "rocks" :language lang))
control-index)))
(format t "Suckage index ~D~&" suckage-index)
(format t "Rockage index ~D~&" rockage-index)
(format t "Controversy index ~F~&" (sqrt (+ (* rockage-index rockage-index) (* suckage-index suckage-index))))
;; ;; admittedly it's a bust if no one ever says that language Y sucks, but how probable is that ;-)
;; ;; but forth managed it...
(format t "Suckage/Rockage ratio ~D~&" (/ rockage-index suckage-index))
(list control-index suckage-index rockage-index
(sqrt (+ (* rockage-index rockage-index) (* suckage-index suckage-index)))
(/ rockage-index suckage-index))))


(defun compare-languages ()
(let* ((language-list '("c" "ruby" "perl" "pascal" "erlang" "javascript" "java" "smalltalk" "python" "lisp" "haskell" "ocaml"))
(language-results (mapcar #'calculate-index-for-language language-list))
(control-list (append (list "Frequency" "brown") (mapcar #'(lambda (x) (nth 0 x)) language-results)))
(suckage-list (append (list "Suckage" "red") (mapcar #'(lambda (x) (nth 1 x)) language-results)))
(rockage-list (append (list "Rockage" "green") (mapcar #'(lambda (x) (nth 2 x)) language-results)))
(controversy-list (append (list "Controversy" "blue") (mapcar #'(lambda (x) (nth 3 x)) language-results)))
(suckage/rockage-list (append (list "Rockage to Suckage ratio" "yellow") (mapcar #'(lambda (x) (nth 4 x)) language-results))))
(cl-gd:with-image* (640 480)
(fill-image 0 0 :color "white")
(plot-bar-chart (list suckage-list rockage-list controversy-list) :x-axis-labels language-list :bar-width .8 :vgrid t)
(cl-gd:write-image-to-file
(make-pathname :defaults clot-system:*base-directory* :name "languages" :type "png") :if-exists :supersede))
(cl-gd:with-image* (640 480)
(fill-image 0 0 :color "white")
(plot-bar-chart (list suckage/rockage-list) :x-axis-labels language-list :bar-width .8 :vgrid t)
(cl-gd:write-image-to-file
(make-pathname :defaults clot-system:*base-directory* :name "suckage-to-rockage" :type "png") :if-exists :supersede))))

Programming Language Comparison


First cut.

Tuesday, November 27

Devhelp Inform Designers Manual

Another devhelp file courtesy of pyhtmlhelp. This time it's the Inform Designers manual.. Yes, I'm writing interactive fiction, again..

Sunday, October 14

Change-class extensibility

I made an interesting minor discovery today: change-class is extensible in much the way
initialize-instance is...




CL-USER> (defclass test-class () ((a-slot :initarg :a-slot-value :initform 0)))
#
CL-USER> (defclass derived-class (test-class) ())
#
CL-USER> (defmethod update-instance-for-different-class :after ((old test-class) (new derived-class) &key fixup-information)
(format t "~A " fixup-information))
# DERIVED-CLASS) {A97F421}>
CL-USER> (make-instance 'test-class :a-slot-value 27)
#
CL-USER> (defparameter *test-instance* (make-instance 'test-class :a-slot-value 27))
*TEST-INSTANCE*
CL-USER> (change-class *test-instance* 'derived-class :fixup-information "Hello World")
Hello World
#
CL-USER>


I'm not sure I actually want to use this, but it's nice to know it's there..

Wednesday, August 29

Automating Visual Studio

Visual Studio is both a curse and a boon. A boon because it is a very good IDE for debugging. A curse because it's restricted to a single platform and tends to lock out third-party editors, such as Emacs, jEdit, SlickEdit, or whatever it is you use.


Most decent editors expect to be able to compile a file via a shell command, capture that commands output, and then scan the output for errors, enabling you to jump to the exact locaton. This can be via make, scons, and the regexp can be modified for different compilers.


However the 'Export Makefile' command dissapeared from Visual Studio with version six. The functionality to compile individual files from the command line has gone, leaving the non-Microsoft editor user with a dilemma. Whether to change their editing habits for the sake of a peaceful life with the GUI, or forsake the ability to compile individual files - whole projects can still be built via the DevEnv command.


However there is a nice Python hack that lets you get at the compiling functionality. You need to use the Python Win32 COM extensions to access Visual Stduio automation from Python. Micheal Graz created this script and tightly integrated it with Vim. I've taken out the vim-specific parts, and added only one Emacs specific part (in dte_get_file) to invoke Emacs when "getting" the current file from Visual Studio. The compilation output now goes to the command line rather than a vim quickfix buffer, so it should be possible to adapt this to your needs.



import os, sys, re, time, pywintypes, win32com.client

vsWindowKindTaskList = '{4A9B7E51-AA16-11D0-A8C5-00A0C921A4D2}'
vsWindowKindFindResults1 = '{0F887920-C2B6-11D2-9375-0080C747D9A0}'
vsWindowKindFindResults2 = '{0F887921-C2B6-11D2-9375-0080C747D9A0}'
vsWindowKindOutput = '{34E76E81-EE4A-11D0-AE2E-00A0C90FFFC3}'

vsBuildStateNotStarted = 1 # Build has not yet been started.
vsBuildStateInProgress = 2 # Build is currently in progress.
vsBuildStateDone = 3 # Build has been completed

#----------------------------------------------------------------------

def dte_compile_file ():
dte = _get_dte()
if not dte: return
try:
dte.ExecuteCommand ('Build.Compile')
except Exception, e:
_dte_exception (e)
return
# ExecuteCommand is not synchronous so we have to wait
while dte.Solution.SolutionBuild.BuildState == vsBuildStateInProgress:
time.sleep (0.1)
dte_output ('output')
_status_msg ('Compile file complete')

#----------------------------------------------------------------------

def dte_build_solution():
dte = _get_dte()
if not dte: return
if dte.CSharpProjects.Count:
dte.Documents.CloseAll()
_dte_raise ()
_dte_output_activate ()
try:
dte.Solution.SolutionBuild.Build (1)
# Build is not synchronous so we have to wait
while dte.Solution.SolutionBuild.BuildState != vsBuildStateDone:
time.sleep (0.25)
except Exception, e:
_dte_exception (e)
return
dte_output ('output')
_status_msg ('Build solution complete')


#----------------------------------------------------------------------

def dte_output (window_kind):
if window_kind == 'find_results_1':
window_name = 'Find Results 1'
window_id = vsWindowKindFindResults1
elif window_kind == 'find_results_2':
window_name = 'Find Results 2'
window_id = vsWindowKindFindResults2
elif window_kind == 'output':
window_name = 'Output'
window_id = vsWindowKindOutput
else:
_msg ('Error: unrecognized window (%s)' % window_kind)
return
dte = _get_dte()
if not dte:
print ">> Failed to get dte."
return
if window_id == vsWindowKindOutput:
owp = dte.Windows.Item(window_id).Object.OutputWindowPanes.Item('Build')
sel = owp.TextDocument.Selection
else:
sel = dte.Windows.Item(window_id).Selection
sel.SelectAll()
_status_msg ('VS %s' % window_name)
print sel.Text
sel.Collapse()


#----------------------------------------------------------------------

def dte_get_file ():
dte = _get_dte()
if not dte: return
doc = dte.ActiveDocument
if not doc:
_status_msg ('No VS file!')
return
pt = doc.Selection.ActivePoint
file = os.path.join (doc.Path, doc.Name)
os.system("emacsclientw -n +%d:%d %s " % (pt.Line, pt.DisplayColumn, file))

#----------------------------------------------------------------------

def dte_put_file (filename, line_num, col_num):
if not filename:
return
dte = _get_dte()
if not dte: return
io = dte.ItemOperations
rc = io.OpenFile (os.path.abspath (filename))
sel = dte.ActiveDocument.Selection
sel.MoveToLineAndOffset (line_num, col_num)
_dte_raise ()

#----------------------------------------------------------------------

def _get_dte ():
try:
return win32com.client.GetActiveObject ('VisualStudio.DTE')
except pywintypes.com_error:
_msg ('Cannot access VisualStudio. Not running?')
return None

#----------------------------------------------------------------------

_wsh = None
def _get_wsh ():
global _wsh
if not _wsh:
try:
_wsh = win32com.client.Dispatch ('WScript.Shell')
except pywintypes.com_error:
_msg ('Cannot access WScript.Shell')
return _wsh

#----------------------------------------------------------------------


#----------------------------------------------------------------------

def _dte_raise ():
dte = _get_dte()
if not dte: return
try:
dte.MainWindow.Activate ()
_get_wsh().AppActivate (dte.MainWindow.Caption)
except:
pass

#----------------------------------------------------------------------

def _dte_output_activate ():
dte = _get_dte()
if not dte: return
dte.Windows.Item(vsWindowKindOutput).Activate()

#----------------------------------------------------------------------

def _dte_set_autoload ():
dte = _get_dte()
if not dte: return
p = dte.Properties ('Environment', 'Documents')
p.Item('DetectFileChangesOutsideIDE').Value = 1
p.Item('AutoloadExternalChanges').Value = 1


#----------------------------------------------------------------------

def _dte_exception (e):
if isinstance (e, pywintypes.com_error):
try:
msg = e[2][2]
except:
msg = None
else:
msg = e
if not msg:
msg = 'Encountered unknown exception'
_status_msg ('ERROR %s' % msg)

#----------------------------------------------------------------------

def _status_msg (msg):
try:
caption = _get_dte().MainWindow.Caption.split()[0]
except:
caption = None
if caption:
msg = msg + ' (' + caption + ')'
_msg (msg)

#----------------------------------------------------------------------

def _msg (msg):
print ">> " + msg


#----------------------------------------------------------------------
# eg vsgo.py dte_compile_file
# vsgo.py dte_put_file GridToolPanelComponent.cpp 10 5
# vsgo.py dte_get_file
# vsgo.py dte_build_solution

def main ():
prog = os.path.basename(sys.argv[0])
if len(sys.argv) == 1:
print 'echo "ERROR: not enough args to %s"' % prog
return

fcn_name = sys.argv[1]
if not globals().has_key(fcn_name):
print 'echo "ERROR: no such fcn %s in %s"' % (fcn_name, prog)
return

fcn = globals()[fcn_name]
try:
apply(fcn, sys.argv[2:])

except TypeError, e:
print 'echo "ERROR in %s: %s"' % (prog, str(e))
return

if __name__ == '__main__': main()

Tuesday, June 12

Lisp Array Setter Syntax

Not the most inituituve in the world



(defmethod (setf setter-name) (value (self class-type) x y z)
(setf (row-major-aref (array-of self) (* x ... blah... ))))


(setf (setter-name self) x-index y-index z-index value)

The thing to remember is that the value always comes first.

Saturday, June 9

wxWidgets Documentation in Devhelp format

This post is what it says on the tin. I converted the wxWidgets help from htb to devhelp using this handy help converter. Here is the result. It needs to live somewhere where devhelp can find it: usually /usr/share/devhelp/books or ~/.devhelp/books, although the latter did not work for me


Why did I want devhelp and not HTB. Partly because devhelp integrates much more nicely with Gnome, but also because it made it trivial to get context - sensitive help out of Emacs with this little gem:




(defun devhelp-word-at-point ()
"runs devhelp"
(interactive)
(setq w (current-word))
(start-process-shell-command "devhelp" nil "devhelp" "-s" w))

Monday, June 4

Save Slime And Die

The best way to work with a Lisp is to leave it running all the time and hack on it bit by bit, building up your program bit by bit, doing mini-tests and off-the-cuff code as you go along and shaping it..


Except if you are like me, you might be working on a laptop with a limited battery life and have a strict time limit to your lisp hacking. It's bothered me that when working with SLIME the only way to save a core was to start a lisp in a terminal, load up your systems and your files, then dump and use that with C-u M-x slime, which lets you enter a command line to start your lisp.


It annoys me because if you are like me, your code doesn't necessarily capture the actual state of your core: it also contains half a dozen test variables and functions that you entered in the repl and are useful to have hang around. So I tried to find a way to dump and restore core from within slime. I came up with the following function



(defun save-slime-and-die (core-file-name)
;; close all
(mapcar #'(lambda (x) (swank::close-connection x)) swank::*connections*)
#+sb-thread
(dolist (thread (remove (swank::current-thread) (swank::all-threads)))
(swank::kill-thread thread))
(sleep 1)
(save-lisp-and-die core-file-name))

It's obviously for sbcl, and the gotcha is that it has to be executed in the *inferior-lisp* buffer, but it works..C-u M-x sbcl --core test.core brings back core dumped with (save-slime-and-die #P"test.core")

Thursday, May 3

CPP Evil

Possibly the most incomprehensible line of code I have ever written.


#define ALLOCATE_ENGINE_OBJECT(type) reinterpret_cast<Engine##type##Object*>(\
world.allocate<Engine##type##Object>()->init_object(\
type##Pool.allocate<Engine##type##Data>()))

Sunday, March 4

Unrealscript mode for Emacs

Edit: I've since abandoned this mode and moved to a simpler (but less buggy) version not based on cc-mode that lives here


;;; unrealscript-mode.el --- unrealscript mode derived from cc-mode

;; Author: 2007 John Connors
;; Maintainer: John Connors <johnc at yagc dot co dot uk>
;; Created: March 2007
;; Version: See cc-mode.el
;; Keywords: unrealscript cc-mode languages oop

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:


;; Note: The interface used in this file requires CC Mode 5.30 or
;; later.

;;; Code:

(require 'cc-mode)

;; These are only required at compile time to get the sources for the
;; language constants. (The cc-fonts require and the font-lock
;; related constants could additionally be put inside an
;; (eval-after-load "font-lock" ...) but then some trickery is
;; necessary to get them compiled.)
(eval-when-compile
(require 'cc-langs)
(require 'cc-fonts))

(eval-and-compile
;; Make our mode known to the language constant system. Use Java
;; mode as the fallback for the constants we don't change here.
;; This needs to be done also at compile time since the language
;; constants are evaluated then.
(c-add-language 'unrealscript-mode 'java-mode))

;; unrealscript has no boolean but a string and a vector type.
(c-lang-defconst c-primitive-type-kwds
unrealscript
(append
'("bool" "name" "object" "actor" "string" "vector")
(delete "boolean"
;; Use append to not be destructive on the
;; return value below.
(append
;; Due to the fallback to Java, we need not give
;; a language to `c-lang-const'.
(c-lang-const c-primitive-type-kwds)
nil))))

(c-lang-defconst c-type-modifier-keywds
unrealscript
(append
'("config" "deprecated" "edfindable" "editconstarray"
"editinline" "export" "noexport" "globalconfig"
"localized" "const" "editconst" "input" "travel"
"skip" "export")
(c-lang-const c-type-modifier-keywds)
nil))


;; "Keywords introducing extra declaration specifiers in the region
;; between the header and the body \(i.e. the \"K&R-region\") in
;; declarations."

(c-lang-defconst c-postfix-decl-spec-kwds
unrealscript
(append
'("abstract" "native" "nativereplication" "nousercreate" "within"
"perobjectconfig" "transient" "noexport" "dependson" "exportstructs"
"cacheexempt" "hidedropdown" "parseconfig" "dontcollapsecategories"
"collapsecategories" "hidecategories" "showcategories" "placeable"
"notplaceable" "instanced")
(c-lang-const c-postfix-decl-spec-kwds)
nil))

;; "Keywords that may be followed by a comma separated list of type
;; identifiers, where each optionally can be prefixed by keywords. (Can
;; also be used for the special case when the list can contain only one
;; element.)"
(c-lang-defconst c-type-list-kwds
unrealscript
(cons "within"
(c-lang-const c-type-list-kwds)))

;; Function declarations begin with "function" in this language.
;; There's currently no special keyword list for that in CC Mode, but
;; treating it as a modifier works fairly well.
(c-lang-defconst c-modifier-kwds
unrealscript
(cons
"function"
(c-lang-const c-modifier-kwds)))


(c-lang-defconst c-block-decls-with-vars
unrealscript
(cons
"state"
(c-lang-const c-other-block-decl-kwds)))

;; "Keywords that may be followed by a parenthesis expression that doesn't
;; contain type identifiers."
(c-lang-defconst c-paren-nontype-kwds
unrealscript
(append
'("state" "var")
(c-lang-const c-paren-nontype-kwds)
nil))


;; "Keywords that may be followed by a parenthesis expression containing
;; type identifiers separated by arbitrary tokens."
(c-lang-defconst c-paren-type-kwds
unrealscript
(append
'("config" "dependson")
(c-lang-const c-paren-type-kwds)))

;; ;; No cpp in this language, but there's still a "#exec" directive to
;; ;; fontify. (The definitions for the extra keywords above are enough
;; ;; to incorporate them into the fontification regexps for types and
;; ;; keywords, so no additional font-lock patterns are required.)
;; (c-lang-defconst c-cpp-matchers
;; unrealscript
;; ;; Use the eval form for `font-lock-keywords' to be able to use
;; ;; the `c-preprocessor-face-name' variable that maps to a
;; ;; suitable face depending on the (X)Emacs version.
;; '(eval . (list "^\\s *\\(#exec\\)\\>\\(.*\\)"
;; (list 1 c-preprocessor-face-name)
;; '(2 font-lock-string-face))))

(defcustom unrealscript-font-lock-extra-types nil
"*List of extra types (aside from the type keywords) to recognize in Unrealscript mode.
Each list item should be a regexp matching a single identifier."
)

(defconst unrealscript-font-lock-keywords-1
(c-lang-const c-matchers-1 unrealscript)
"Minimal highlighting for UNREALSCRIPT mode.")

(defconst unrealscript-font-lock-keywords-2
(c-lang-const c-matchers-2 unrealscript)
"Fast normal highlighting for UNREALSCRIPT mode.")

(defconst unrealscript-font-lock-keywords-3
(c-lang-const c-matchers-3 unrealscript)
"Accurate normal highlighting for UNREALSCRIPT mode.")

(defvar unrealscript-font-lock-keywords unrealscript-font-lock-keywords-3
"Default expressions to highlight in UNREALSCRIPT mode.")

(defvar unrealscript-mode-syntax-table nil
"Syntax table used in unrealscript-mode buffers.")
(or unrealscript-mode-syntax-table
(setq unrealscript-mode-syntax-table
(funcall (c-lang-const c-make-mode-syntax-table unrealscript))))

(defvar unrealscript-mode-abbrev-table nil
"Abbreviation table used in unrealscript-mode buffers.")

(c-define-abbrev-table 'unrealscript-mode-abbrev-table
;; Keywords that if they occur first on a line might alter the
;; syntactic context, and which therefore should trig reindentation
;; when they are completed.
'(("else" "else" c-electric-continued-statement 0)
("while" "while" c-electric-continued-statement 0)))

(defvar unrealscript-mode-map (let ((map (c-make-inherited-keymap)))
;; Add bindings which are only useful for UNREALSCRIPT
map)
"Keymap used in unrealscript-mode buffers.")

(easy-menu-define unrealscript-menu unrealscript-mode-map "UNREALSCRIPT Mode Commands"
;; Can use `unrealscript' as the language for `c-mode-menu'
;; since its definition covers any language. In
;; this case the language is used to adapt to the
;; nonexistence of a cpp pass and thus removing some
;; irrelevant menu alternatives.
(cons "UNREALSCRIPT" (c-lang-const c-mode-menu unrealscript)))

;;;###Autoload
(add-to-list 'auto-mode-alist '("\\.uc\\'" . unrealscript-mode))

;;;###autoload
(defun unrealscript-mode ()
"Major mode for editing UNREALSCRIPT UnrealScript is a
Java-like object-orientated programming (OOP) language created by
Epic Games for programming in-game content for the UnrealEngine.

The hook `
c-mode-common-hook' is run with no args at mode
initialization, then `
unrealscript-mode-hook'.

Key bindings:
\\{unrealscript-mode-map}"

(interactive)
(kill-all-local-variables)
(c-initialize-cc-mode t)
(set-syntax-table unrealscript-mode-syntax-table)
(setq major-mode 'unrealscript-mode
mode-name "UnrealScript"
local-abbrev-table unrealscript-mode-abbrev-table
abbrev-mode t)
(use-local-map c-mode-map)
;; `c-init-language-vars' is a macro that is expanded at compile
;; time to a large `setq' with all the language variables and their
;; customized values for our language.
(c-init-language-vars unrealscript-mode)
;; `c-common-init' initializes most of the components of a CC Mode
;; buffer, including setup of the mode menu, font-lock, etc.
;; There's also a lower level routine `c-basic-common-init' that
;; only makes the necessary initialization to get the syntactic
;; analysis and similar things working.
(c-common-init 'unrealscript-mode)
(easy-menu-add unrealscript-menu)
(run-hooks 'c-mode-common-hook)
(run-hooks 'unrealscript-mode-hook)
(setq font-lock-keywords-case-fold-search t)
(c-update-modeline))


(provide 'unrealscript-mode)