Tutorials

This chapter contains the following sections:

Defining Stack Routines

These examples show you what can be done in a language that supports typed variables and untyped container variables. The following example code shows how to define a set of stack routines in Slick-C® that support any type of element:

void stacknew(typeless &stack)

{

    stack._makeempty();  // Destroy current contents of stack.

    stack[0]=0;          // Make an array and use first element as top count.

}

void stackpush(typeless &stack, typeless &value)

{

    stack[++stack[0]]=value;

}

typeless stackpop(typeless &stack)

{

    if (stack[0]<=0) return('');

    // Make a copy of the element.

    result=stack[stack[0]--];

    // Free space allocated by value and delete array element. _deleteel is a

    // built-in method which operates on arrays and hash tables.

    stack._deleteel(stack[0]+1);  

    return(result);

}

defmain()

{

    // The above routines can handle variables of any type, including

    // string constants.

 

    struct RECORD {

       int i;

       _str s;

    };

    // You can't make a limit on the number of elements in an array.

    // We will add support for initially allocating a specific number of elements.

    RECORD arecord[];

    arecord[0].i=4;arecord[0].s="element 0";

    RECORD symboltable:[];        // Declare a hash table/associative array.

    symboltable:["name1"].i=1;symboltable:["name1"].s="element 0";

    stacknew(stack);

    stackpush(stack,arecord);     // Push an array onto the stack.

 

    stackpush(stack,symboltable); // Push a hash table/associative array onto

                                  // the same stack.

 

    stackpush(stack,"string");    // Push a string constant onto the same stack.

}

The following example shows how a container variable can access structure members as an array:

/*

    Read lines of a file which contains tab-delimited data into an array of 

    structures. Each line represents an array structure element.

 

    The tab-delimited data on each line represents fields in the structure. 

    We will assume the file contains valid data for filling this structure.

*/

int ReadTable(_str filename,typeless (&table)[])

{

    // Use an editor buffer to open and cache the file. Data is read 

    // in blocks from the file only. We don't need this much power, but 

    // Slick-C needs a few more non-editor file I/O functions.

    status=_open_temp_view(filename,temp_view_id,orig_view_id);

 

    if (status)  return(status);

    top();up();  // Place cursor on line 0 before first line of file.

    for (j=0;;++j) {

       if (down())  break;

       get_line(line);

       if (line:=="") continue;

       rest=line;

       p= &table[j]; // Make p point to this structure element.

       // Here we access structure members as an array of elements.

       p->[0]="";

       // Note that loop supports fields which are strings of length 0.

       for (i=0;;++i) {

          if (rest:=="" && i)	break;

 

          // Parse is similar to REXX. We were unable to come up with a

          // satisfactory function syntax so with went with a REXX-style syntax.

          // Place text up to but not including tab character into value variable.

          // Place tab character and rest of data in rest variable.

          parse rest with value "\t" +0 rest;

          if (substr(rest,1,1):=="\t") {

              rest=substr(rest,2);

          }

          p->[i]=value;

       }

 

    }

    _delete_temp_view(temp_view_id);

    activate_view(orig_view_id);

    return(0);

}

struct TABLE_ENTRY {

    _str name;

    int value;

};

// defmain is the main entry pointer for a Slick-C batch/script macro.

defmain()

{

    TABLE_ENTRY table[];

    // Table file should exist.

    // NOTE: (TABLE_ENTRY []) is type compatible with (typeless []).

    status=ReadTable("table",table);

    if (status) {

       _message_box("Failed to read table file");

       return(1);

    }

    _message_box("First record:  name=":+table[0].name:+" value=":+table[0].value);

}  

Searching for a String Within a Current Function

This macro can be used with many languages. It searches the current procedure or function for a specified string, with specified options. Use this macro in cases where references do not work, such as searching for a partial identifier name.

Several useful aspects of this macro, aspects that can be reused in other macros, are that it prompts the user for a string, it selects the current procedure, and it performs a search within the selection.

See the following sections:

Creating the Macro

Complete the following steps:

  1. Enter the macro code below into a file called procsearch.e.

  2. To load the module, from the main menu, select Macro → Load Module.

  3. Bind the command proc_search to a key. To use the macro, press the appropriate key.

  4. In the Search string text box, enter the text to search for, and in the Options text box, enter the search options (see Command Line Search Options).

Contents of procsearch.e:

#include 'slick.sh'

 

_command int proc_search(...) name_info(','VSARG2_READ_ONLY|

                                 VSARG2_REQUIRES_EDITORCTL|

                                 VSARG2_MARK)

{

    // Save the original cursor position to restore later.

    typeless original_position;

    save_pos(original_position);

 

    // Prompt the user for a search string, and search options.

    _str result = show('-modal _textbox_form',

                    'Search Function',  // Dialog box caption.

                    TB_RETRIEVE_INIT,   // Flags.

                    '',                 // Use default text box width.

                    '',                 // Help item.

                    '',                 // Button list.

                    'procsearch',       // Retrieve name.

                    'Search string:',   // First prompt.

                    'Options:ixcs');    // Second prompt and default.

    if ( result=='' ) {

       // If the user clicked the Cancel button, just return.

       return(COMMAND_CANCELLED_RC);

    }

 

    // The results from the text boxes.

    _str search_string=_param1;

    _str search_options=_param2;

 

    int status=select_proc(); // Select the current proc.

    if ( status ) {

       // In rare cases select_proc can fail if a procedure is too complex.

       // If select_proc failed, show an error messages, return the cursor to the

       // original position, and return.

       _message_box(nls("select_proc failed"));

       restore_pos(original_position);

       message(get_message(status)); 

       return(status); 

    }

    lock_selection();   // Lock the selection.

 

    begin_select();     // Move the cursor to the beginning of the selection.

 

    status=find(search_string,'m':+search_options);  // Find the text that the

                                                     // user specified using the

                                                     // options specified. We 

                                                     // prepend the 'm' option

                                                     // since we know we are

                                                     // searching in a selection.

 

    if ( status ) {

       // If the search string was not found, deselect and return the cursor to

       // the original position. 

       deselect();

       restore_pos(original_position);

    }

 

    // Just return the status. This will leave the proc selected so that

    // find_next works.

    return(status);

}

Analyzing the Macro

The save_pos() call at the beginning of the macro saves the current cursor position information. This function places the cursor in its original position if necessary.

The show() function launches a dialog box. In this case, the show() function launches a general purpose dialog box named _text box_form. The dialog box _text box_form prompts the user for one or more strings. After the first argument, the remaining arguments to show() pass to the on_create dialog box. In this case, there are several arguments.

The second argument to show() is the caption for the on_create dialog box.

The next argument is a set of flags. In this case, the only flag specified is TB_RETRIEVE_INIT. The TB_RETRIEVE_INIT flag tells the dialog box to initialize itself by retrieving the last values filled in for this dialog box.

Use the next three arguments to specify text box width, help, and a button list. These particular arguments are unused in this example, which is why they are shown here as ''.

The retrieve name is a unique name used to retrieve the values that were previously filled in for this dialog box. Any remaining arguments are interpreted as prompts for the user. Default values can be given by specifying the prompt as prompt:defaultvalue. The first prompt is the search string, and the second is for search options. The options have default ixcs, meaning case-insensitive, and exclude comments and strings. See the following section for a list of command line search options.

After the call to show, verify that the result is ''. If so, then the user clicked the Cancel button, so we return. Otherwise, SlickEdit® must obtain the values that the user provides. These values are returned in global variables _param1.._param N. In this case, our search string is returned in _param1, and the search options are in _param2. These are saved in local variables.

SlickEdit calls select_proc to select the current function. If select_proc returns a non-zero status, then it failed, so it is returned. In rare cases, select_proc can fail if a function is too long, or has preprocessing that keeps it from correctly identifying the end of the function.

Next, lock_selection() is called, and then begin_select() is called to move to the beginning of the selection.

Now, we can call find() with the search string and the search options from the user. Insert m at the beginning of the options string to specify search only in the selection.

Finally, check the status from find. If the string is not found, clear the function and restore the original cursor position.

Command Line Search Options

Command line search options include the characters listed in the table below.

Option

Description

+

(Default) Forward search.

-

Reverse search.

<

(Default) Place cursor at beginning of string found.

>

Place cursor after end of string found.

E

(Default) Case-sensitive search.

I

Case-insensitive search.

M

Search within visible mark.

H

Find text in hidden lines.

R

Search for SlickEdit® regular expression.

U

Interpret string as a UNIX regular expression.

B

Interpret string as a Brief regular expression.

N

(Default) Do not interpret search string as a regular search string.

@

No error message.

W

Limits search to words such as variable names.

,

Delimiter to separate ambiguous options.

Reading and Modifying Buffers

Slick-C® includes the Slick-C API. The API covers many actions normally performed in a code editor, including navigating and modifying buffers.

Topics in this section:

Functions for Reading and Modifying Buffers

The table below contains functions for reading and modifying buffers. This table focuses on one particular category of the API, those functions that allow you to programmatically traverse and modify buffers. These powerful functions enable you to take tasks that you can do manually, and create a macro to perform the same tasks in seconds.

Function

Action

_str cur_word( int & start_col [, _str from_cursor ])

Gets the current word at cursor.

int delete_line()

Deletes the current line.

void _delete_text( int len )

Delete len bytes starting from the cursor position.

void get_line( _str & line )

Retrieves current line.

_str get_text([int count [,int seek_pos ]])

Gets a stream of text starting at current line.

void keyin( _str string )

Inserts string of characters as if typed from the keyboard.

void insert_line( _str line )

Inserts line after current line.

void _insert_text( _str string )

Inserts string at cursor position.

void replace_line( _str line )

Replaces current line.

Common Functions for Navigating Buffers

The table below contains functions that can be used for navigating buffers.

Function

Action

int up( [int num ] )

Moves cursor up num lines, or one line if no value passed in.

int down( [int num ] )

Moves cursor down num lines, or one line if no value passed in.

void left()

Moves cursor one position to the left.

void right()

Moves cursor one position to the right.

void top()

Places cursor at first line and first column of buffer.

void bottom()

Places cursor at end of last line of buffer.

void _begin_line()

Places cursor at the beginning of the current line.

void _end_line()

Places cursor after the end of the current line.

Escape Backslashes Example

Escape backslashes if, for every slash in a directory name, you actually need two for the compiler to handle the directory name or string properly.

Example:

_command escape_slash(){

    _str myLine;

    get_line(myLine); // Set string szLine to the current line.

    myLine = stranslate(myLine, "\\\\", "\\"); // Replace slash with double

                                               // slashes.

    replace_line(myLine); // Replace the line in the buffer.

}

The above command accepts the following line of code:

myDirectory = "C:\Data\Corporate\Internal";

and replaces it with:

myDirectory = "C:\\Data\\Corporate\\Internal";

Comment Out Debug Print Lines Example

Print or debug statements can be used to debug. These statements need to have supporting comments or they must be deleted. The following example shows a simple function that loops through your entire file. It contains supporting comments for all of the lines that have a printf statement:

_command comment_printf(){

    _str curLine;

    top(); // Go to top of buffer

    up();  // Get to the top line

    while ( !down() ) {                    // Loop until end of file.

       get_line( curLine );                // Get the current line.

       if( pos( "printf", curLine ) ){     // Search for a printf.

 

          _begin_line();                   // If printf exists, move cursor to the

                                           // first column.

 

          _insert_text( // );              // Add a comment.

       }

    }

}

The function uses many of the buffer modifications and navigation macros. It loops line-by-line through the file, checks for a string, and adds a comment when necessary. Modify this macro to meet your needs. For example, if you want the lines deleted instead of commented, replace the _insert_text() call with delete_line(). Also, check to see if the comment characters already exist before you add the comment text. Instead of calling _begin_line, call begin_line_text_toggle'this places the cursor at the first non-space character of a line. Next, check if you are in a comment by calling _in_comment().

Working with Existing Macros

Every time you select a menu, click a button, or enter a key, a Slick-C® macro is called to perform an action. More than half of the code in SlickEdit products is written in Slick-C and this source is provided to you when you install, so you can tweak the product or use the Slick-C source as an example to help write your own macros. By default, the Slick-C source is located in the macros subdirectory of your SlickEdit® installation folder.

To make a macro change, or to recycle existing code, you need to know how to find a name to a particular command and how to find its location in the source code. These examples will walk you through the steps:

Example: Turning on Line Numbers for All Files

SlickEdit® includes a line number toggle option to turn line numbers on and off for each edit window. This option is located on the View menu (Display → Line Numbers). By default, all files are displayed without line numbers. When you enable them, they are enabled throughout sessions until you disable them. SlickEdit also provides an option to enable line numbers on a language-specific basis (Window → SlickEdit Preferences → Languages → [Language Category] → [Language] → General).

To automatically turn on line numbers for all files that are opened or created in SlickEdit regardless of the language, you will need to write a macro, as outlined in the subsequent sections:

Find the Command Definition

You need to find the command that is associated with Display → Line Numbers in order to view its source code, so that you can obtain the function you®ll be using in your new macro.

To determine the command that is associated with Display → Line Numbers:

  1. Close any open files.

  2. From the main menu, select Macro → Menus. The dialog box contains a list of all menus. To view the main menu, select _mdi_menu and click Open. The Menu Editor dialog is displayed.

  3. Navigate to Display → Line Numbers. When you select Line Numbers, certain fields in the dialog box are populated. The Command field is populated with the Slick-C® command that is invoked when this menu item is selected. In this case, the command is view-line-numbers-toggle. Every time that you click Display → Line Numbers from the main menu, view-line-numbers-toggle is called.

    To view the source code for the view-line-numbers-toggle command:

  4. From the main menu, click Macro → Go to Slick-C Definition.

  5. Start typing view, and select view_line_numbers_toggle() from the drop-down list, then click OK.

  6. By viewing the source, it is a simple "if on then off, else on" algorithm, using bitwise logic. Note that you will need to use p_LCBufFlags|=VSLCBUFFLAG_LINENUMBERS in your new macro to enable the display of line numbers.

Create the New Macro
  1. Create a new empty file named DisplayAllLines.e.

  2. Copy and paste or type the following code into the file:

#include "slick.sh" 

 

void _buffer_add_ViewLineNumbers()

{

    p_LCBufFlags|=VSLCBUFFLAG_LINENUMBERS;

    p_line_numbers_len = _default_option(VSOPTION_LINE_NUMBERS_LEN);

}

Any Slick-C macro that starts with _buffer_add_ is called when a new edit window is displayed. To enable the numbers for every file, use the logic from Step 5 above.

Load the Macro

The new macro needs to be loaded. To load the macro, from the main menu, select Macro → Load Module → DisplayAllLines.e.

If the macro was loaded properly, the message Modules loaded is displayed in the SlickEdit® message line. If an error message is displayed, the macro did not load and the change did not take effect. Correct the error and load the macro again.

Results

Now every new file opened has line numbers. If any files were left open at the beginning, close and reopen them and they will all have line numbers.

To remove the functionality that turns on line numbers for all files, you need to unload DisplayAllLines.e: From the main menu select Macro > Unload Module. Select DisplayAllLines.ex from the list and click OK. The list shows a .ex extension on the module instead of a .e because you are actually compiling the source file into a binary file (.ex) and loading it, not the actual source code.

Example: Counting Lines of Code

The number of lines of code in your workspace, projects, or files is often used to measure and analyze performance, and can be determined by using a macro.

This example describes a macro, linecount.e, that loops through all projects in the current workspace and all files within each project in the current workspace, and then displays a report in a new editor window.

You can obtain linecount.e from the SlickEdit Web site at www.slickedit.com in the Slick-C® Documentation section. Line numbers referenced in the subsections below:

Gather Workspace, Project, and File Information

Get a list of all projects and files in the workspace. _GetWorkspaceFiles() (Line 88) gets the list of all projects in a workspace and places the list in a temporary buffer. The loop following (Lines 93-95), parses through the buffer and stores the information in a temporary array for later reporting. This array, defined in Line 67, is a three-dimensional array to store multiple projects, and multiple files per project.

Loop through each project, starting at Line 98, and fill the array with all file names for each project. GetProjectFiles() does this by placing the list in a temporary buffer. Grab the names from the buffer and put them in the array (Lines 109-124).

Loop and Count

For each project, open up a temporary buffer for each file in the project. Think of it as an invisible buffer where you can move the cursor programmatically to check whether it is in a comment.

  • _open_temp_view (Line 139) opens it.

  • up() and top() (Line 158) places the cursor at the top to start.

  • down() (Line 161) will move the cursor down one line at a time.

Loop through the file to read one line at a time, as mentioned above (Lines 161-202). This validates whether the current line is in a comment (Line 171), and if not, it increments the counter. If the current line is in a comment, the next step is to jump to the end of the comment or comment block (Line 168). Another check is made to see if the current line is in a comment and count it if it is not a comment.

Create the Report

All of the information is now stored in an array, so the next task is to generate a report and loop thru the array to display the results. This is done in Lines 220-263.

The displayResultsInBuffer flag can be changed to false to only display the total lines in the entire workspace.

Now that you understand the macro, the next steps are to load and run it.

Load the Macro

To load linecount.e, be sure to save it to your local hard drive, then from the main menu, click Macros → Load Module. Find linecount.e and click Open.

Run the Macro

You can now run the macro. There are several ways to run macros: from the command line, through a menu item, or by using a keyboard shortcut.

To run the macro from the command line:

  1. Open the command line by pressing Esc or by clicking in the message line area.

  2. Type linecount and press Enter.

To associate the macro with a menu item:

  1. Select Macro → Menus, then select menu on which you want to add the macro. For example, to add the macro to the right-click context menu, select _ext_menu_default.

  2. Click Open.

  3. In the Menu Editor dialog, click Insert to add a new menu item.

  4. Type a new Caption, set the Command to linecount. Use the Up and Down buttons to move the new item to the desired location in the list. Type "Menu Editor dialog box" in the Help Index (Help → Index) for more information about using the Menu Editor.

To associate the macro with a key or key sequence:

  1. From the main menu, click Window → SlickEdit Preferences → Keyboard → Key Bindings.

  2. Find a key sequence that is not used®do not bind keys that are bound. To determine if a key or key sequence is already in use, place the focus in the Search by key sequence field and press the key/key sequence you want to check. For example, press Enter and the table will be filtered to show all commands bound to the Enter key.

  3. After determining the key or key sequence you want to use for the new binding, close the Options dialog.

  4. From the main menu, click Macro → List Macros.

  5. Select linecount, then click Bind to Key. The Key Bindings option screen is displayed with linecount selected.

  6. Click Add and when the Bind Key dialog appears, type the key sequence to bind.

  7. Click Bind, then OK.