(module keybinding-lang mzscheme
  (require (lib "mred.ss" "mred")
           (lib "string-constant.ss" "string-constants")
           (lib "framework.ss" "framework")
           (lib "class.ss"))
  
  (provide (rename kl-module-begin #%module-begin)
           (all-from-except mzscheme #%module-begin)
           (all-from (lib "framework.ss" "framework"))
           (all-from (lib "mred.ss" "mred"))
           (all-from (lib "class.ss")))
  
  (define-syntax (kl-module-begin stx)
    (syntax-case stx ()
      [(mb a ...) 
       (with-syntax ([#%keymap (datum->syntax-object (syntax mb) '#%keymap)]
                     [#%keybinding (datum->syntax-object (syntax mb) '#%keybinding)]
                     [keybinding (datum->syntax-object (syntax mb) 'keybinding)])
         (syntax (#%plain-module-begin 
                  (define #%keymap (make-object keymap:aug-keymap%))
                  (provide #%keymap)
                  (define (#%keybinding key proc src line col pos)
                    (unless (string? key)
                      (error 'keybinding "expected string as first argument, got ~e (other arg ~e)" key proc))
                    (unless (and (procedure? proc)
                                 (procedure-arity-includes? proc 2))
                      (error 'keybinding "expected procedure of two arguments as second argument, got ~e (other arg ~e)"
                             proc
                             key))
                    (let ([name (if (and line col)
                                    (format "~a:~a.~a" src line col)
                                    (format "~a:~a" src pos))])
                      (send #%keymap add-function name 
                            (λ (x y)
                              (let ([end-edit-sequence
                                     (λ ()
                                       (when (is-a? x editor<%>)
                                         (let loop ()
                                           (when (send x in-edit-sequence?)
                                             (send x end-edit-sequence)
                                             (loop)))))])
                              (with-handlers ([exn:fail? 
                                               (λ (x)
                                                 (end-edit-sequence)
                                                 (message-box (string-constant drscheme)
                                                              (format (string-constant user-defined-keybinding-error) 
                                                                      name
                                                                      (exn-message x))))])
                                (proc x y)
                                (when (is-a? x editor<%>)
                                  (when (send x in-edit-sequence?)
                                    (end-edit-sequence)
                                    (message-box (string-constant drscheme)
                                                 (format (string-constant user-defined-keybinding-error) 
                                                                      name
                                                                      "Editor left in edit-sequence"))))))))
                      (send #%keymap map-function key name)))
                  (define-syntax (keybinding stx)
                    (syntax-case stx ()
                      [(_ key val)
                       (with-syntax ([src (syntax-source stx)]
                                     [line (syntax-line stx)]
                                     [col (syntax-column stx)]
                                     [pos (syntax-position stx)])
                         (syntax (#%keybinding key val 'src 'line 'col 'pos)))]))
                  a ...)))])))
