(module choose-paren mzscheme
(require (lib "class.ss")
(lib "framework.ss" "framework"))
(provide choose-paren
get-contextual-open-cmd)
(define (get-contextual-open-cmd editor default-cmd)
(cond
[(preferences:get 'framework:fixup-open-parens)
(case (choose-paren editor)
[(#\()
'Open]
[(#\[)
'Open-Square]
[(#\{)
'Open-Square])]
[else default-cmd]))
(define (choose-paren text)
(let* ([pos (send text get-start-position)]
[real-char #\[]
[change-to (λ (i c)
(set! real-char c))]
[start-pos (send text get-start-position)]
[end-pos (send text get-end-position)]
[letrec-like-forms (preferences:get 'framework:square-bracket:letrec)])
(send text begin-edit-sequence #f #f)
(send text insert "[" start-pos 'same #f)
(when (eq? (send text classify-position pos) 'parenthesis)
(let* ([before-whitespace-pos (send text skip-whitespace pos 'backward #t)]
[keyword/distance (find-keyword-and-distance before-whitespace-pos text)])
(cond
[(and keyword/distance
(member keyword/distance
(preferences:get 'framework:square-bracket:cond/offset)))
(void)]
[(and keyword/distance
(member (car keyword/distance)
(preferences:get 'framework:square-bracket:local)))
(unless (= (cadr keyword/distance) 0)
(change-to 7 #\())]
[else
(let* ([backward-match (send text backward-match before-whitespace-pos 0)]
[b-m-char (and (number? backward-match) (send text get-character backward-match))])
(cond
[backward-match
(let* ([before-whitespace-pos2 (send text skip-whitespace backward-match 'backward #t)]
[backward-match2 (send text backward-match before-whitespace-pos2 0)])
(cond
[(member b-m-char '(#\( #\[ #\{))
(change-to 1 b-m-char)]
[else
(change-to 2 #\()]))]
[(not (zero? before-whitespace-pos))
(let ([b-w-p-char (send text get-character (- before-whitespace-pos 1))])
(cond
[(equal? b-w-p-char #\()
(let* ([second-before-whitespace-pos (send text skip-whitespace
(- before-whitespace-pos 1)
'backward
#t)]
[second-backwards-match (send text backward-match
second-before-whitespace-pos
0)])
(cond
[(not second-backwards-match)
(change-to 3 #\()]
[(and (beginning-of-sequence? text second-backwards-match)
(ormap (λ (x) (text-between-equal? x
text
second-backwards-match
second-before-whitespace-pos))
letrec-like-forms))
(void)]
[else
(let* ([second-before-whitespace-pos2 (send text skip-whitespace
second-backwards-match
'backward
#t)]
[second-backwards-match2 (send text backward-match
second-before-whitespace-pos2
0)])
(cond
[(and second-backwards-match2
(eq? (send text classify-position second-backwards-match)
'symbol)
(member "let" letrec-like-forms)
(text-between-equal? "let"
text
second-backwards-match2
second-before-whitespace-pos2))
(void)]
[else
(change-to 4 #\()]))]))]
[else
(change-to 5 #\()]))]
[else
(change-to 6 #\()]))])))
(send text delete pos (+ pos 1) #f)
(send text end-edit-sequence)
real-char))
(define (find-keyword-and-distance before-whitespace-pos text)
(let loop ([pos before-whitespace-pos]
[n 0])
(let ([backward-match (send text backward-match pos 0)])
(cond
[backward-match
(let ([before-whitespace-pos (send text skip-whitespace backward-match 'backward #t)])
(loop before-whitespace-pos
(+ n 1)))]
[else
(let* ([afterwards (send text get-forward-sexp pos)]
[keyword
(and afterwards
(send text get-text pos afterwards))])
(and keyword
(list keyword (- n 1))))]))))
(define (beginning-of-sequence? text start)
(let ([before-space (send text skip-whitespace start 'backward #t)])
(cond
[(zero? before-space) #t]
[else
(equal? (send text get-character (- before-space 1))
#\()])))
(define (text-between-equal? str text start end)
(and (= (string-length str) (- end start))
(let loop ([i (string-length str)])
(cond
[(= i 0) #t]
[else
(and (char=? (string-ref str (- i 1))
(send text get-character (+ i start -1)))
(loop (- i 1)))])))))