add basic support for #lang line-specific editing

Specifically, the #lang line now selects for a
definitions text surrogate, on top of which several
extensions can be built to, say, more easily add keybindings
or change indenting decisions, etc.
This commit is contained in:
Robby Findler 2013-09-06 15:36:06 -05:00
parent 2e49672561
commit 905033e3f8
5 changed files with 152 additions and 53 deletions

View File

@ -101,7 +101,11 @@
module-language-big-defs/ints-interactions-text-mixin
module-language-big-defs/ints-definitions-text-mixin
initialize-prefs-panel
big-defs/ints-label<%>))
big-defs/ints-label<%>
change-lang-surrogate-mixin
default-surrogate%
change-lang-host-mixin))
(define-signature drracket:module-language-tools-cm^
(frame-mixin

View File

@ -118,12 +118,13 @@
(make-extender get-base-interactions-text% 'interactions-text%))
(define (get-base-definitions-text%)
(drracket:module-language:module-language-online-expand-text-mixin
(drracket:module-language-tools:definitions-text-mixin
(drracket:module-language:module-language-big-defs/ints-definitions-text-mixin
(drracket:debug:test-coverage-definitions-text-mixin
(drracket:debug:profile-definitions-text-mixin
(drracket:unit:get-definitions-text%)))))))
(drracket:module-language:change-lang-host-mixin
(drracket:module-language:module-language-online-expand-text-mixin
(drracket:module-language-tools:definitions-text-mixin
(drracket:module-language:module-language-big-defs/ints-definitions-text-mixin
(drracket:debug:test-coverage-definitions-text-mixin
(drracket:debug:profile-definitions-text-mixin
(drracket:unit:get-definitions-text%))))))))
(define-values (extend-definitions-text get-definitions-text)
(make-extender get-base-definitions-text%

View File

@ -1,46 +1,54 @@
#lang racket/unit
(require string-constants
racket/class
racket/list
framework
drracket/private/drsig)
(require string-constants
racket/class
racket/list
framework
racket/gui/base
drracket/private/drsig)
(import [prefix drracket:module-language: drracket:module-language/int^])
(export drracket:modes^)
(define-struct mode (name surrogate repl-submit matches-language))
(define modes (list))
(define (get-modes) modes)
(define (add-mode name surrogate repl-submit matches-language)
(let ([new-mode (make-mode name
surrogate
repl-submit
matches-language)])
(set! modes (cons new-mode modes))
new-mode))
(define (not-a-language-language? l)
(and (not (null? l))
(equal? (last l)
(string-constant no-language-chosen))))
(define (add-initial-modes)
(import)
(export drracket:modes^)
;; must be added first, to make it last in mode list,
;; since predicate matches everything
(add-mode
(string-constant scheme-mode)
(new racket:text-mode%)
(λ (text prompt-position) (racket:text-balanced? text prompt-position))
(λ (l) #t))
(define-struct mode (name surrogate repl-submit matches-language))
(define modes (list))
(add-mode
(string-constant racket-mode)
(new drracket:module-language:default-surrogate%)
(λ (text prompt-position) (racket:text-balanced? text prompt-position))
(λ (l) (member (string-constant module-language-name) l)))
(define (get-modes) modes)
(define (add-mode name surrogate repl-submit matches-language)
(let ([new-mode (make-mode name
surrogate
repl-submit
matches-language)])
(set! modes (cons new-mode modes))
new-mode))
(define (not-a-language-language? l)
(and (not (null? l))
(equal? (last l)
(string-constant no-language-chosen))))
(define (add-initial-modes)
;; must be added first, to make it last in mode list,
;; since predicate matches everything
(add-mode
(string-constant scheme-mode)
(new racket:text-mode%)
(λ (text prompt-position) (racket:text-balanced? text prompt-position))
(λ (l) #t))
(add-mode
(string-constant text-mode)
#f
(λ (text prompt-position) #t)
(λ (l)
(and l
(or (not-a-language-language? l)
(ormap (λ (x) (regexp-match #rx"Algol" x)) l))))))
(add-mode
(string-constant text-mode)
#f
(λ (text prompt-position) #t)
(λ (l)
(and l
(or (not-a-language-language? l)
(ormap (λ (x) (regexp-match #rx"Algol" x)) l))))))

View File

@ -2552,4 +2552,74 @@
[tab (in-list (send frame get-tabs))]
[v (in-value (send (send tab get-defs) get-filename))]
#:when v)
v)))
v))
(define-local-member-name maybe-change-language)
(define change-lang-host<%> (interface () maybe-change-language))
(define change-lang-host-mixin
(mixin ((class->interface text%) mode:host-text<%>) (change-lang-host<%>)
(inherit set-surrogate)
(define current-surrogate-mod #f)
(define current-language-end #f)
;; called by the surrogate-mixin to see if the surrogate needs changing if
;; the mode is not the racket language mode, then this doesn't get called.
(define/public (maybe-change-language start)
(when (or (not current-language-end)
(< start current-language-end))
(update-surrogate)))
(define/private (update-surrogate)
(define defs-port (open-input-text-editor this))
;; need this to count chars, not bytes (in case of non-ASCII
;; in defs before #lang line)
(port-count-lines! defs-port)
(define get-info
(with-handlers ([exn:fail? (λ (x) #f)])
(read-language defs-port (λ () #f))))
(define-values (line col pos) (port-next-location defs-port))
(define new-surrogate-mod
(and get-info
(get-info 'definitions-text-surrogate #f)))
(set! current-language-end pos)
(unless (equal? current-surrogate-mod new-surrogate-mod)
(set! current-surrogate-mod new-surrogate-mod)
(define new-surrogate
(and new-surrogate-mod
(with-handlers ([exn:fail?
(λ (x)
(log-error
(format "Error while loading surrogate; expected to be in ~s\n~a"
new-surrogate-mod
(exn-message x)))
#f)])
(dynamic-require new-surrogate-mod 'surrogate%))))
(set-surrogate (new (if new-surrogate
(change-lang-surrogate-mixin
new-surrogate)
default-surrogate%)))))
(super-new)))
(define change-lang-surrogate-mixin
(mixin (mode:surrogate-text<%>) ()
(define/override (after-insert ths supr start len)
(super after-insert ths supr start len)
(when (is-a? ths change-lang-host<%>)
(send ths maybe-change-language start)))
(define/override (after-delete ths supr start len)
(super after-delete ths supr start len)
(when (is-a? ths change-lang-host<%>)
(send ths maybe-change-language start)))
(super-new)))
(define default-surrogate%
(change-lang-surrogate-mixin
racket:text-mode%)))

View File

@ -570,16 +570,32 @@ has.
@index{modes}
@index{scheme mode}
@index{racket mode}
@index{definitions-text-surrogate}
DrRacket provides support for multiple editor modes. Tools
register modes via
DrRacket provides support for multiple editor modes based on the
@tt{#lang} line at the beginning of the editor. If the
@onscreen{Modes} submenu of the @onscreen{Edit} menu has
the @onscreen{Racket} mode chosen (which is the default if the
Language dialog's ``The Racket Language'' is chosen), then
DrRacket calls the language's @racket[get-info] procedure
(see @racket[read-language] for more about how to set up
a language's @racket[get-info] procedure) with
@racket['definitions-text-surrogate]. This is expected to return
a quoted module path (in the sense of @racket[module-path?]) that
names a module that exports @racket[surrogate%]. It is expected
to be bound to a class implementing the @racket[mode:surrogate-text<%>]
interface. Assuming so, it is used as the surrogate for the definitions
text.
Additionally, plugins can register modes via
@racket[drracket:modes:add-mode]. Each mode is
visible in the @onscreen{Modes} submenu of the @onscreen{Edit}
menu. Initially, DrRacket only supports two modes: Racket
mode and text mode.
DrRacket automatically selects a mode for each open
file based on the file's extension. If the file ends with
file based on the file's extension (and the language chosen
as described above). If the file ends with
@File{.txt}, DrRacket uses text mode. Otherwise, DrRacket
uses Racket mode.