From 905033e3f8b15a4f641c48f2981454f10937db5f Mon Sep 17 00:00:00 2001 From: Robby Findler Date: Fri, 6 Sep 2013 15:36:06 -0500 Subject: [PATCH] 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. --- .../drracket/private/drsig.rkt | 6 +- .../drracket/drracket/private/get-extend.rkt | 13 +-- .../drracket/drracket/private/modes.rkt | 92 ++++++++++--------- .../drracket/private/module-language.rkt | 72 ++++++++++++++- .../drracket/scribblings/tools/tools.scrbl | 22 ++++- 5 files changed, 152 insertions(+), 53 deletions(-) diff --git a/pkgs/drracket-pkgs/drracket-plugin-lib/drracket/private/drsig.rkt b/pkgs/drracket-pkgs/drracket-plugin-lib/drracket/private/drsig.rkt index c60f237301..a55c56a00b 100644 --- a/pkgs/drracket-pkgs/drracket-plugin-lib/drracket/private/drsig.rkt +++ b/pkgs/drracket-pkgs/drracket-plugin-lib/drracket/private/drsig.rkt @@ -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 diff --git a/pkgs/drracket-pkgs/drracket/drracket/private/get-extend.rkt b/pkgs/drracket-pkgs/drracket/drracket/private/get-extend.rkt index 75dd777fd6..4916ce45b4 100644 --- a/pkgs/drracket-pkgs/drracket/drracket/private/get-extend.rkt +++ b/pkgs/drracket-pkgs/drracket/drracket/private/get-extend.rkt @@ -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% diff --git a/pkgs/drracket-pkgs/drracket/drracket/private/modes.rkt b/pkgs/drracket-pkgs/drracket/drracket/private/modes.rkt index 5f5ee86911..ba9d926c88 100644 --- a/pkgs/drracket-pkgs/drracket/drracket/private/modes.rkt +++ b/pkgs/drracket-pkgs/drracket/drracket/private/modes.rkt @@ -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)))))) diff --git a/pkgs/drracket-pkgs/drracket/drracket/private/module-language.rkt b/pkgs/drracket-pkgs/drracket/drracket/private/module-language.rkt index a1d82237f1..02e0572c8a 100644 --- a/pkgs/drracket-pkgs/drracket/drracket/private/module-language.rkt +++ b/pkgs/drracket-pkgs/drracket/drracket/private/module-language.rkt @@ -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%))) diff --git a/pkgs/drracket-pkgs/drracket/scribblings/tools/tools.scrbl b/pkgs/drracket-pkgs/drracket/scribblings/tools/tools.scrbl index 9c94c6207d..c202d71ae3 100644 --- a/pkgs/drracket-pkgs/drracket/scribblings/tools/tools.scrbl +++ b/pkgs/drracket-pkgs/drracket/scribblings/tools/tools.scrbl @@ -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.