Initial revision
diff --git a/Lib/idlelib/ScriptBinding.py b/Lib/idlelib/ScriptBinding.py
new file mode 100644
index 0000000..aa46c68
--- /dev/null
+++ b/Lib/idlelib/ScriptBinding.py
@@ -0,0 +1,169 @@
+"""Extension to execute code outside the Python shell window.
+
+This adds the following commands (to the Edit menu, until there's a
+separate Python menu):
+
+- Check module (Alt-F5) does a full syntax check of the current module.
+It also runs the tabnanny to catch any inconsistent tabs.
+
+- Import module (F5) is equivalent to either import or reload of the
+current module.  The window must have been saved previously. The
+module is added to sys.modules, and is also added to the __main__
+namespace.  Output goes to the shell window.
+
+- Run module (Control-F5) does the same but executes the module's
+code in the __main__ namespace.
+
+"""
+
+import sys
+import os
+import imp
+import tkMessageBox
+
+indent_message = """Error: Inconsistent indentation detected!
+
+This means that either:
+
+(1) your indentation is outright incorrect (easy to fix), or
+
+(2) your indentation mixes tabs and spaces in a way that depends on \
+how many spaces a tab is worth.
+
+To fix case 2, change all tabs to spaces by using Select All followed \
+by Untabify Region (both in the Edit menu)."""
+
+class ScriptBinding:
+    
+    keydefs = {
+        '<<check-module>>': ['<Alt-F5>', '<Meta-F5>'],
+        '<<import-module>>': ['<F5>'],
+        '<<run-script>>': ['<Control-F5>'],
+    }
+    
+    menudefs = [
+        ('edit', [None,
+                  ('Check module', '<<check-module>>'),
+                  ('Import module', '<<import-module>>'),
+                  ('Run script', '<<run-script>>'),
+                 ]
+        ),
+    ]
+
+    def __init__(self, editwin):
+        self.editwin = editwin
+        # Provide instance variables referenced by Debugger
+        # XXX This should be done differently
+        self.flist = self.editwin.flist
+        self.root = self.flist.root
+
+    def check_module_event(self, event):
+        filename = self.getfilename()
+        if not filename:
+            return
+        if not self.tabnanny(filename):
+            return
+        if not self.checksyntax(filename):
+            return
+
+    def tabnanny(self, filename):
+        import tabnanny
+        import tokenize
+        tabnanny.reset_globals()
+        f = open(filename, 'r')
+        try:
+            tokenize.tokenize(f.readline, tabnanny.tokeneater)
+        except tokenize.TokenError, msg:
+            self.errorbox("Token error",
+                          "Token error:\n%s" % str(msg))
+            return 0
+        except tabnanny.NannyNag, nag:
+            # The error messages from tabnanny are too confusing...
+            self.editwin.gotoline(nag.get_lineno())
+            self.errorbox("Tab/space error", indent_message)
+            return 0
+        return 1
+
+    def checksyntax(self, filename):
+        f = open(filename, 'r')
+        source = f.read()
+        f.close()
+        if '\r' in source:
+            import re
+            source = re.sub(r"\r\n", "\n", source)
+        if source and source[-1] != '\n':
+            source = source + '\n'
+        try:
+            compile(source, filename, "exec")
+        except (SyntaxError, OverflowError), err:
+            try:
+                msg, (errorfilename, lineno, offset, line) = err
+                if not errorfilename:
+                    err.args = msg, (filename, lineno, offset, line)
+                    err.filename = filename
+            except:
+                lineno = None
+                msg = "*** " + str(err)
+            if lineno:
+                self.editwin.gotoline(lineno)
+            self.errorbox("Syntax error",
+                          "There's an error in your program:\n" + msg)
+        return 1
+
+    def import_module_event(self, event):
+        filename = self.getfilename()
+        if not filename:
+            return
+
+        modname, ext = os.path.splitext(os.path.basename(filename))
+        if sys.modules.has_key(modname):
+            mod = sys.modules[modname]
+        else:
+            mod = imp.new_module(modname)
+            sys.modules[modname] = mod
+        mod.__file__ = filename
+        setattr(sys.modules['__main__'], modname, mod)
+
+        dir = os.path.dirname(filename)
+        dir = os.path.normpath(os.path.abspath(dir))
+        if dir not in sys.path:
+            sys.path.insert(0, dir)
+
+        flist = self.editwin.flist
+        shell = flist.open_shell()
+        interp = shell.interp
+        interp.runcode("reload(%s)" % modname)
+
+    def run_script_event(self, event):
+        filename = self.getfilename()
+        if not filename:
+            return
+
+        flist = self.editwin.flist
+        shell = flist.open_shell()
+        interp = shell.interp
+        if (not sys.argv or
+            os.path.basename(sys.argv[0]) != os.path.basename(filename)):
+            # XXX Too often this discards arguments the user just set...
+            sys.argv = [filename]
+        interp.execfile(filename)
+
+    def getfilename(self):
+        # Logic to make sure we have a saved filename
+        # XXX Better logic would offer to save!
+        if not self.editwin.get_saved():
+            self.errorbox("Not saved",
+                          "Please save first!")
+            self.editwin.text.focus_set()
+            return
+        filename = self.editwin.io.filename
+        if not filename:
+            self.errorbox("No file name",
+                          "This window has no file name")
+            return
+        return filename
+
+    def errorbox(self, title, message):
+        # XXX This should really be a function of EditorWindow...
+        tkMessageBox.showerror(title, message, master=self.editwin.text)
+        self.editwin.text.focus_set()