Version 13 of Integrating Tcl and Emacs on Windows

Updated 2003-03-19 19:45:30

Emacs has the wonderfull "inferior tcl" process which allows us to start Tcl in an Emacs window. Unfortunately, this doesn't work automatically on Windows, Emacs is unable to make the Tcl interpreter start in the "interactive" mode. The problem is that Tcl calls istty() to determine whether tcl_interactive [L1 ] should be set and whether prompts should be displayed. But the Windows version of Emacs can not make istty() return the right value so this doesn't happen and tclsh thinks it's running non-interactive.

One solution is to change the tclsh code, but not everyone can or wants to do that. Another solution is to simulate the command loop in pure Tcl. This works without any changes to Tcl or Emacs, so here is the code that I currently use:

 namespace eval CmdInput {

        # We want to allow shell commands to execute automatically
        # as in normal interactive use.  The "unknown" proc checks
        # that "info script" returns "", before it enables that
        # behaviour, so we subclass "info".  In Tcl 8.4 we could simply
        # call 'info script ""' I believe, but 8.3 and earlier don't do
        # that.
        variable this_script [info script]
        proc info {args} {
                set result [uplevel [concat __org_info $args]]
                set cmd [lindex $args 0]

                variable this_script
                if {"script" == $cmd && "$result" == $this_script} {
                        return ""
                } else {
                        return $result
                }
        }
        rename ::info ::__org_info ;# using a namespace proc here cores
        proc ::info {args} "uplevel \[concat [namespace which info] \$args\]"

        proc loop {} {
                # preparations
                fconfigure stdin  -buffering line
                fconfigure stdout -buffering line
                fconfigure stderr -buffering line
                set ::tcl_interactive 1

                if {[file exists ~/tclshrc.tcl]} {
                        namespace eval :: {uplevel \#0 source ~/tclshrc.tcl}
                }

                # input loop
                while {1} {
                        catch {uplevel \#0 $::tcl_prompt1}
                        flush stdout
                        set cmd {}
                        while {1} {
                                append cmd [gets stdin] "\n"
                                if {[info complete $cmd]} {
                                        break
                                }
                                catch {uplevel \#0 $::tcl_prompt2}
                                flush stdout
                        }
                        history add $cmd
                        catch {uplevel \#0 $cmd} result
                        puts $result
                }
                return ""
        }
 }

 catch {CmdInput::loop} result 
 puts $result
 exit 0

To use this code: Save it to a file called cmd-input.tcl. In Emacs customize the variable tcl-application to read "tclsh83.exe" and the variable tcl-command-switches to include "c:/your-path-here/cmd-input.tcl" (this is with Emacs 21.1, Emacs 20 used other names for the variables, I believe).

I hope this is clear, if not or if you have ideas for improvement, you can always contact me at mailto:[email protected] .


Note: Jim Graham has a similar thing called interp.tcl on his homepage [L2 ].