[index entries: _snip_ _arrow_ _label_ ] _snips_ and _arrows_ library ============================ Collection: mrflow Files: snips-and-arrows.ss snips-and-arrows-view.ss snips-and-arrows-model.ss This library allows a programmer to display information about terms in a DrScheme text editor and its embedded sub-editors. Textual information about a given term can be displayed using snips inserted in the editor next to the term. Each snip is colored according to a programmer specified type. Several snips can exist for a given term and type. A relation between two terms can be displayed by drawing an arrow between the two terms. Each arrow is colored using a programmer specified color. Arrows do not carry any textual information with them. The library is fundamentally based on terms. It is not possible to display snips or arrows that are not directly related to terms. However the library does not require terms to be represented by MzScheme syntax objects. Terms can be represented by any user-defined data structure, called hereafter _labels_. A label represents exactly one term and the library user is responsible for keeping track of which term is represented by a given label (by, say, keeping a reference to the syntax object in the label's data structure), but a term in an editor can be represented by one or more labels and the user doesn't have to keep track of that. This asymmetry between terms and labels is necessary because macro expansions might duplicate some terms. With each label can be associated a number of snips containing textual information. Each snip has a user-defined type that determines the color of the snip. A label can have several snips for a given type, and snips of different types associated with it. Mouse menus are available to show or hide all snips of a given type for all labels corresponding to a given term. The snips are inserted on the left of the corresponding term, in an order that is determined by the user (see the snip-types-and-colors argument of the init-snips-and-arrows-gui function below for details). The library defines two mixins that augment the text editors with various methods, and two functions that are used to initialize the library. Both functions return one function to register labels with the library and one function to change terms in the editors. A term in an editor will be recognized and colored by the library only if at least one of its corresponding labels has been registered with the library. All terms corresponding to registered labels must, either directly or indirectly through embedded sub-editors, be contained in a single editor called hereafter the top editor. In the case of the DrScheme definitions window and its embedded sub-editors (if any), the top editor will be the definitions window itself. Usage ===== (require (lib "snips-and-arrows.ss" "mrflow")) This will import two mixins: extend-all-editors-mixin and extend-top-editor-mixin and two functions: init-snips-and-arrows-gui and init-snips-and-arrows-gui-for-syntax-objects See the example at the end of this document for a quick how-to. All text% classes that will be used to create editors that will contain, either directly or indirectly through embedded editors, terms to be colored by the library, must be extended using the _extend-all-editors-mixin_ mixin. In the case of the DrScheme definitions window and its embedded sub-editors (if any) this is simply done by giving this mixin as argument to the drscheme:unit:add-to-program-editor-mixin function during phase1 of DrScheme. The text% class that will be used to create the top editor that will contain, either directly or indirectly through embedded sub-editors, all the terms to be colored must be extended using the _extend-top-editor-mixin_ mixin. This must be done even if all the terms to be colored are contained in embedded sub-editors and the top editor doesn't itself directly contain any terms to be colored. The mixin extends that text% class with two methods of no arguments: - _color-registered-labels_, to be called once all labels have been registered (see below) in order to color the corresponding terms, to start the automatic display of arrows as well as to make the snips and arrows related mouse menus available to the user. - _remove-all-snips-and-arrows-and-colors_, to be called to terminate the automatic display of arrows, make the snips and arrows related mouse menus unavailable to the user, remove all inserted snips, and optionally clear all colors (see below the last argument to init-snips-and-arrows-gui and the last optional argument to init-snips-and-arrows-gui-for-syntax-objects for details about this last point). In the case of the DrScheme definitions window, using this mixin is simply done by giving it as argument to the drscheme:get/extend:extend-definitions-text function. The init-snips-and-arrows-gui and init-snips-and-arrows-gui-for-syntax-objects functions are used to initialize the library. Exactly one of them must be called before trying to use the library. The init-snips-and-arrows-gui-for-syntax-objects function is a simplified version of init-snips-and-arrows-gui. The _init-snips-and-arrows-gui_ function has eleven arguments in the following order (with a mnemonic name and a type between parenthesis): 1) the top editor (top-editor: text%), the editor that, either directly or indirectly through embedded sub-editors, contains all terms that are to be colored by the library. 2) a function from label to editor (get-editor-from-label: label -> text%) returning the editor that directly contains the term corresponding to the label. This function must return the same editor for all the labels corresponding to a given term. 3) a function from label to term position (get-mzscheme-position-from-label: label -> non-negative-exact-integer) returning the MzScheme position of the term corresponding to the label in the editor that directly contains it (the editor that would be returned by the get-editor-from-label function described just above). This function must return the same position for all the labels corresponding to a given term. 4) a function from label to term color span (get-span-from-label: label -> non-negative-exact-integer) indicating the number of characters of the term corresponding to the label that should be colored. Coloring of a term starts with the leftmost character of the term and continues towards the right for that number of characters. This applies to all terms for which at least one label has been registered with the library, regardless of whether they are atomic or not, so care must be exercised when computing the span for a term containing sub-terms that also have to be colored. Arrows starting or ending at a given term will be anchored at half the span of the term, so again care must be taken when specifying the span of non-atomic terms. This function must return the same span for all labels corresponding to a given term. 5) a function from label to a list of arrows data (get-arrows-from-label: label -> (listof (list label label string))) indicating what arrows should be drawn when the mouse pointer is placed over the term corresponding to the label. Each sublist in the list represents one arrow, and must contain, in that order, a label corresponding to the starting term for the arrow, a label corresponding to the ending term for the arrow, and a string representing the arrow color (see color-database<%> in the help-desk for a list of color names). 6) a function from label to style delta (get-style-delta-from-label: label -> style-delta%) indicating how the term corresponding to the label should be colored. This function must return the same style delta for all the labels corresponding to a given term. 7) a function from popup menu and a list of labels to void (extend-menu-for-labels: popup-menu% (listof label) -> void) that can be used to add menu-item% objects to the menu that pops up when the user clicks with the right mouse button on a term. All the labels in the list correspond to that term (there might be several labels in the list because of macro expansions). The callbacks for the added menu items must not directly modify the content of any editor that directly contains colored terms but must instead use a function like the first one returned by the call to init-snips-and-arrows-gui (see below for details). 8) a function from snip type (see snip-types-and-colors below) and action symbol to string (get-menu-text-from-snip-type: symbol symbol -> string) that takes as input a snip type represented as a symbol as well as one of the two symbols 'show or 'hide representing a user command and returns a string that will be used as the text for the menu entry that will allow the user to perform said command for the snips of that given type. 9) a function from snip type and label to a list of strings (get-snip-text-from-snip-type-and-label: symbol label -> (listof string)) that returns the content of all the snips of the given type that will be inserted next to the term corresponding to the label when the user uses the snip-related mouse menu entries described just above. 10) a list of snip types and colors (snip-types-and-colors: (listof (cons symbol string))) that describes all possible types of snips and their associated color. Each sub-list in the list must contain two elements: a snip type name represented as a symbol, and a corresponding snip color (see color-database<%> in the help-desk for a list of color names). For a given term, snips of different types will be inserted from left to right on the left of the corresponding term in the order in which their types appear in this list. 11) a boolean (clear-colors-immediately?: (union #t #f)) that indicates whether, once the user starts modifying the content of the editors, the terms colored by the library should be uncolored immediately or should be uncolored only the next time a DrScheme tool is run or the program executed. The init-snips-and-arrows-gui then returns two values: a function to change terms and a function to register a label. The function to change terms has type ((listof (cons label string)) -> void). Each pair in the function's argument consists of a label representing a term to be changed and a string that will be used to replace that term. It is only necessary to give one label per term to be changed. It is an error to give two pairs with labels representing the same term but with different strings. The function to register a label has type (label -> void) and is used to indicate to the library that the corresponding term has to be colored. The same label can safely be registered several times. Unspeakable things involving nasty crawling bugs will happen to you, and your descendants will be cursed to the seventh generation if you dare to call this function after having already called the color-registered-labels method described above. The init-snips-and-arrows-gui-for-syntax-objects function is a simplified (yes, really) version of init-snips-and-arrows-gui that assumes labels are in fact MzScheme syntax objects, and also provides default values for all the menu and snip related functions that simply assume that no snips are used at all. Also, the terms represented by the syntax objects are assumed to be atomic, meaning they will be colored in whole and arrows will be anchored at half the terms' spans. Seemingly unanchored arrows and overlapping colors may result if you try to use this function with non-atomic terms (i.e. terms containing sub-terms). The function has only three required arguments, and five optional arguments. The three required arguments are, in that order, using the mnemonic names from the description of init-snips-and-arrows-gui above, and with updated types in parenthesis: - top-editor (text%) - get-arrows-from-label (syntax-object -> (listof (list syntax-object syntax-object string))) - get-style-delta-from-label (syntax-object -> style-delta%) The five optional arguments are as follows, in that order, with their default value in brackets: - extend-menu-for-labels (popup-menu% (listof syntax-object) -> void) [(lambda (m l) (void)))] - get-menu-text-from-snip-type (symbol symbol -> string) [internal error function] - get-snip-text-from-snip-type-and-label (symbol syntax-object -> (listof string)) [internal error function] - snip-types-and-colors (listof (cons symbol string)) ['()] - clear-colors-immediately? (union #t #f) [#f] The init-snips-and-arrows-gui-for-syntax-objects returns the same two functions as the init-snips-and-arrows-gui function. Example ======= This example assumes that this library is used by a DrScheme tool. The skeleton for the tool would then look like this: (module my-tool ... (require (lib "snips-and-arrows.ss" "mrflow")) ... (define tool@ (unit/sig drscheme:tool-exports^ (import drscheme:tool^) ... (define (phase1) ... (drscheme:unit:add-to-program-editor-mixin extend-all-editors-mixin) ...) ... (drscheme:get/extend:extend-definitions-text extend-top-editor-mixin) ... (drscheme:get/extend:extend-unit-frame (lambda (super%) (class super% (inherit get-definitions-text) (rename [super-clear-annotations clear-annotations]) ; -> void (define/override (clear-annotations) ... (super-clear-annotations) (send (get-definitions-text) remove-all-snips-and-arrows-and-colors) ...) ... (define my-tool-button (instantiate button% ... (callback (lambda (button event) ... (letrec-values ([(user-change-terms register-label-with-gui) ; use init-snips-and-arrows-gui-for-syntax-objects ; if you only deal with syntax objects (init-snips-and-arrows-gui (get-definitions-text) ... ; for extend-menu-for-labels, if necessary - ; basically a callback to library-user code (lambda (popup-menu labels) ... (make-object menu-item% "change stuff" popup-menu ; menu callback (lambda (item event) ... (let ([label-and-new-term-pairs (my-tool-get-stuff-to-change labels)]) ... ; callback to the library from within callback to ; library-user code (user-change-terms label-and-new-term-pairs) ...) ...) ...) ...) ...)]) ... ; call to super's method to clean other tools' annotations (super-clear-annotations) ... (drscheme:eval:expand-program ... (lambda (syntax-object-or-eof iter) (if (eof-object? syntax-object-or-eof) (begin ... (send definitions-text color-registered-labels) ...) (begin ... (my-tool-process-syntax-object ... register-label-with-gui ...) ... (iter))))))))))))))) ...)