[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7. Extending

Speedbar can run different types of Major display modes such as Files (see section 3. File Mode), and Buffers (see section 4. Buffer Mode). It can also manage different minor display modes for use with buffers handling specialized data.

These major and minor display modes are handled through an extension system which permits specialized keymaps and menu extensions, in addition to a unique rendering function. You can also specify a wide range of tagging functions. The default uses imenu, but new tagging methods can be easily added. In this chapter, you will learn how to write your own major or minor display modes, and how to create specialized tagging functions.

7.1 Minor Display Modes  How to create a minor display mode.
7.2 Major Display Modes  How to create a major display mode.
7.3 Tagging Extensions  How to create your own tagging methods.
7.4 Creating a display  How to insert buttons and hierarchies.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.1 Minor Display Modes

A minor display mode is a mode useful when using a specific type of buffer. This mode might not be useful for any other kind of data or mode, or may just be more useful that a files or buffers based mode when working with a specialized mode.

Examples that already exist for speedbar include RMAIL, Info, and gdb. These modes display information specific to the major mode shown in the attached frame.

To enable a minor display mode in your favorite Major mode, follow these steps. The string `name' is the name of the major mode being augmented with speedbar.

  1. Create the keymap variable name-speedbar-key-map.

  2. Create a function, named whatever you like, which assigns values into your keymap. Use this command to create the keymap before assigning bindings:

     
        (setq name-speedbar-key-map (speedbar-make-specialized-keymap))
    

    This function creates a special keymap for use in speedbar.

  3. Call your install function, or assign it to a hook like this:

     
    (if (featurep 'speedbar)
        (name-install-speedbar-variables)
      (add-hook 'speedbar-load-hook 'name-install-speedbar-variables))
    

  4. Create an easymenu compatible vector named name-speedbar-menu-items. This will be spliced into speedbar's control menu.

  5. Create a function called name-speedbar-buttons. This function should take one variable, which is the buffer for which it will create buttons. At this time (current-buffer) will point to the uncleared speedbar buffer.

When writing name-speedbar-buttons, the first thing you will want to do is execute a check to see if you need to re-create your display. If it needs to be cleared, you need to erase the speedbar buffer yourself, and start drawing buttons. See section 7.4 Creating a display.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.2 Major Display Modes

Creating a Major Display Mode for speedbar requires authoring a keymap, an easy-menu segment, and writing several functions. These items can be given any name, and are made the same way as in a minor display mode (see section 7.1 Minor Display Modes). Once this is done, these items need to be registered.

Because this setup activity may or may not have speedbar available when it is being loaded, it is necessary to create an install function. This function should create and initialize the keymap, and add your expansions into the customization tables.

When creating the keymap, use the function speedbar-make-specialized-keymap instead of other keymap making functions. This will provide you with the initial bindings needed. Some common speedbar functions you might want to bind are:

speedbar-edit-line
Edit the item on the current line.
speedbar-expand-line
Expand the item under the cursor. With a numeric argument (C-u), flush cached data before expanding.
speedbar-contract-line
Contract the item under the cursor.

These function require that function speedbar-line-directory be correctly overloaded to work.

Next, register your extension like this;

 
  (speedbar-add-expansion-list '("MyExtension"
                                 MyExtension-speedbar-menu-items
                                 MyExtension-speedbar-key-map
                                 MyExtension-speedbar-buttons))

There are no limitations to the names you use.

The first parameter is the string representing your display mode. The second parameter is a variable name containing an easymenu compatible menu definition. This will be stuck in the middle of speedbar's menu. The third parameter is the variable name containing the keymap we discussed earlier. The last parameter is a function which draws buttons for your mode. This function must take two parameters. The directory currently being displayed, and the depth at which you should start rendering buttons. The function will then draw (starting at the current cursor position) any buttons deemed necessary based on the input parameters. See section 7.4 Creating a display.

Next, you need to register function overrides. This may look something like this:

 
(speedbar-add-mode-functions-list
 '("MYEXTENSION"
   (speedbar-item-info . MyExtension-speedbar-item-info)
   (speedbar-line-directory . MyExtension-speedbar-line-directory)))

The first element in the list is the name of you extension. The second is an alist of functions to overload. The function to overload is first, followed by what you want called instead.

For speedbar-line-directory your function should take an optional DEPTH parameter. This is the starting depth for heavily indented lines. If it is not provided, you can derive it like this:

 
(save-match-data
  (if (not depth)
      (progn
        (beginning-of-line)
        (looking-at "^\\([0-9]+\\):")
        (setq depth (string-to-int (match-string 1)))))

where the depth is stored as invisible text at the beginning of each line.

The path returned should be the full path name of the file associated with that line. If the cursor is on a tag, then the file containing that tag should be returned. This is critical for built in file based functions to work (meaning less code for you to write). If your display does not deal in files, you do not need to overload this function.

The function speedbar-item-info, however, is very likely to need overloading. This function takes no parameters and must derive a text summary to display in the minibuffer.

There are several helper functions you can use if you are going to use built in tagging. These functions can be ored since each one returns non-nil if it displays a message. They are:

speedbar-item-info-file-helper
This takes an optional filename parameter. You can derive your own filename, or it will derive it using a (possibly overloaded) function speedbar-line-file. It shows details about a file.
speedbar-item-info-tag-helper
If the current line is a tag, then display information about that tag, such as its parent file, and location.

Your custom function might look like this:

 
(defun MyExtension-item-info ()
  "Display information about the current line."
  (or (speedbar-item-info-tag-helper)
      (message "Interesting detail.")))

Once you have done all this, speedbar will show an entry in the `Displays' menu declaring that your extension is available.

If your major mode is running slowly, you may need to add a set of stealthy update functions.

Variable: speedbar-stealthy-function-list
List of functions to periodically call stealthily. Each function must return nil if interrupted, or t if completed. Stealthy functions which have a single operation should always return t. Functions which take a long time should maintain a state (where they are in their speedbar related calculations) and permit interruption. See speedbar-check-vc as a good example.

Stealthy functions are currently used in files mode. The first activity is to display the main list with minimal decorations though the buttons function. Once that is done, additional work can be done decorating different lines after the fact.

For files mode, VC status and read-only markers can calculated after the first display is up. Because the process of calculating that information can be slow, these functions are interruptable, and will continue processing at a later time out.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.3 Tagging Extensions

It is possible to create new methods for tagging files in speedbar. To do this, you need two basic functions, one function to fetch the tags from a buffer, the other to insert them below the filename.

Function: my-fetch-dynamic-tags file
Parse file for a list of tags. Return the list, or t if there was an error. file is not necessarily available as an buffer when your function is called. Be sure to load it if it is needed with code like this:

 
(save-excursion
  (set-buffer (find-file-noselect file))
  ...
  )

The non-error return value can be anything, as long as it can be inserted by its paired function:

Function: my-insert-tag-list level lst
Insert a list of tags lst started at indentation level level. Creates buttons for each tag, and provides any other display information required.

It is often useful to use speedbar-create-tag-hierarchy on your tag list 7.4 Creating a display.

Once these two functions are written, modify the variable speedbar-dynamic-tags-function-list to include your parser at the beginning, like this:

 
(add-to-list 'speedbar-dynamic-tags-function-list
	     '(my-fetch-dynamic-tags  . my-insert-tag-list))

Variable: speedbar-dynamic-tags-function-list
A list of functions which will return and insert a list of tags. Each element is of the form ( fetch . insert ) where fetch is a function which takes one parameter (the file to tag) and returns a list of tags. The tag list can be of any form as long as the corresponding insert method can handle it. If it returns t, then an error occurred, and the next fetch routine is tried. insert is a function which takes an INDENTation level, and a list of tags to insert. It will then create the speedbar buttons.

If your parser is only good for a few types of files (ie, just texinfo files), make sure that it is either a buffer local modification (set only in texi files), or that the tag generator returns t for unsupported buffers.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.4 Creating a display

Rendering a display in speedbar is completely flexible. When your button function is called, see 7.1 Minor Display Modes, and 7.2 Major Display Modes, you have control to insert anything you want.

The conventions allow almost anything to be inserted, but several helper functions are provided to make it easy to create the standardized buttons.

To understand the built in functions, each `button' in speedbar consists of four important pieces of data. The text to be displayed, token data to be associated with the text, a function to call, and some face to display it in.

When a function is provided, then that text becomes mouse activated, meaning the mouse will highlight the text.

Additionally, for data which can form deep trees, each line is given a depth which indicates how far down the tree it is. This information is stored in invisible text at the beginning of each line, and is used by the navigation commands.

7.4.1 Creating Speedbar Buttons  
7.4.2 Generic Tag Lists  
7.4.3 Expand Buttons  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.4.1 Creating Speedbar Buttons

While it is certainly possible to insert your own text and add decorations to it, it is preferable to use the speedbar button creation API instead.

Function: speedbar-insert-button text face mouse function &optional token prevline
This function inserts one button into the current location. text is the text to insert. face is the face in which it will be displayed. mouse is the face to display over the text when the mouse passes over it. function is called whenever the user clicks on the text.

The optional argument token is extra data to associated with the text. Lastly prevline should be non-nil if you want this line to appear directly after the last button which was created instead of on the next line.

This routine creates a single button in the display, such as a [+] button, or the text that goes after it. It is designed to be called iteratively with indicators on when to add carriage returns between buttons.

To insert a more traditional speedbar line that includes an expansion button followed by clickable text, it is preferable to create a tag-line. Tag lines should be used to represent typical data a user will interact with.

Function: speedbar-make-tag-line exp-button-type exp-button-char exp-button-function exp-button-data tag-button tag-button-function tag-button-data tag-button-face depth

Create a tag line with exp-button-type for the small expansion button. This is the button that expands or contracts a node (if applicable), and exp-button-char the character in it (`+', `-', `?', etc). exp-button-function is the function to call if it's clicked on. Button types are 'bracket, 'angle, 'curly, 'expandtag, 'statictag, or nil. exp-button-data is extra data attached to the text forming the expansion button.

Next, tag-button is the text of the tag. tag-button-function is the function to call if clicked on, and tag-button-data is the data to attach to the text field (such a tag positioning, etc). tag-button-face is a face used for this type of tag.

Lastly, depth shows the depth of expansion.

This function assumes that the cursor is in the speedbar window at the position to insert a new item, and that the new item will end with a CR.

Major display modes that use this function will have a consistant look and feel and reliable image use.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.4.2 Generic Tag Lists

For data stored in a hierarchical list, it is likely possible to have the entire data structure inserted with the speedbar generic list methods.

These insertion functions assume that the data represents 'tags', or data extracted from inside a file, and they are marked with tag markers.

Tags stored in this format can then be manipulated by the tag hierarchy methods described earlier in this manual.

Function: speedbar-insert-generic-list level list expand-fun find-fun

At level, (the current indentation level desired) insert a generic multi-level alist list. Associations with lists get `{+}' tags (to expand into more nodes) and those with positions or other data just get a `>' as the indicator. `{+}' buttons will have the function expand-fun and the token is the cdr list. The token name will have the function find-fun and not token.

Each element of the list can have one of these forms:

(name . marker-or-number)
One tag at this level.
(name (name . marker-or-number) (name . marker-or-number) ... )
One group of tags.
(name marker-or-number (name . marker-or-number) ... )
One Group of tags where the group has a starting position.

When you use speedbar-insert-generic-list, there are some variables you can set buffer-locally to change the behavior. The most obvious is speedbar-tag-hierarchy-method. See section 6.2 Tag Hierarchy Methods.

Variable: speedbar-generic-list-group-expand-button-type
This is the button type used for groups of tags, whether expanded or added in via a hierarchy method. Two good values are 'curly and 'expandtag. Curly is the default button, and 'expandtag is useful if the groups also has a position.

Variable: speedbar-generic-list-tag-button-type
This is the button type used for a single tag. Two good values are nil and 'statictag. nil is the default, and 'statictag has the same width as 'expandtag.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.4.3 Expand Buttons

Once you have made some buttons, you need to hitch them up to do something once clicked on. This is set up with speedbar-make-tag-line which takes two functions. One is for the expand button, and the other for the tag.

The function for the tag can be anything, but the expand button has certain expectations. When that button is has a + in it, that indicates that there is "more" inside. A - indicates that a section can be closed.

The expand function takes the same signature as all speedbar button functions, such as:

 
(defun MY-expand-function (text token indent)
   "Display more information about MY thing.
..."

This function should then use the text parameter to determine what to do. For example:

 
  (cond ((string-match "+" text)	;we have to expand this file
         ;; Expand stuff here
         )
	((string-match "-" text)	;we have to contract this node
         ;; Contract stuff here
         ))

When expanding or contracting, you will need to change the + or - into the opposite symbol. You can do that with

Function: speedbar-change-expand-button-char char
Change the expansion button character to char for the current line.

Our sample would then look like this:

 
  (cond ((string-match "+" text)	;we have to expand this file
         ;; Expand stuff here
	 (speedbar-change-expand-button-char ?-)
         )
	((string-match "-" text)	;we have to contract this node
         ;; Contract stuff here
	 (speedbar-change-expand-button-char ?+)
         ))

To expand text, the speedbar buffer needs to be writable. The initial button insertion function runs while the buffer is writable, but an expand function will need to change that. The best way is to use speedbar-with-writable.

Function: speedbar-with-writable &rest forms
Allow the speedbar buffer to be writable and evaluate forms.

Once the buffer is writable, then you can insert more information using any mechanism you like. Remember that the cursor will be on the expand button just clicked though. As such, the code might look like this:

 
  (cond ((string-match "+" text)	;we have to expand this file
         ;; Expand stuff here
	 (speedbar-change-expand-button-char ?-)
	 (speedbar-with-writable
	   (save-excursion
	     (end-of-line) (forward-char 1)
             ;; Insert goodies here.
           ))         
         )

Contracting a node is a bit easier. You can just use this built in function to do the work:

Function: speedbar-delete-subblock indent
Delete text from point to indentation level indent or greater. Handles end-of-sublist smartly.

Remember that indent is also available as passed into MY-expand-function.

The end result would then look like this:

 
  (cond ((string-match "+" text)	;we have to expand this file
         ;; Expand stuff here
	 (speedbar-change-expand-button-char ?-)
	 (speedbar-with-writable
	   (save-excursion
	     (end-of-line) (forward-char 1)
             ;; Insert goodies here.
           ))         
         )
	((string-match "-" text)	;we have to contract this node
         ;; Contract stuff here
	 (speedbar-change-expand-button-char ?+)
	 (speedbar-delete-subblock indent))
         ))

Expand buttons, unlike other speedbar operations can cause new data to appear in speedbar which would be useful to force onto the screen. You can then recenter using this function:

Function: speedbar-center-buffer-smartly
Recenter a speedbar buffer so the current indentation level is all visible. This assumes that the cursor is on a file, or tag of a file which the user is interested in.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by XEmacs shared group account on December, 19 2009 using texi2html 1.65.