Functions

A function can be called from the macro language. Slick-CŪ has five kinds of functions: procedures, commands, class methods, library functions, and built-ins. These are described in the following sections:

Defining a Procedure

Procedures and functions are the basic building blocks for most modern, imperative languages. Slick-CŪ procedures cannot be bound to keys. A procedure name must be a valid Slick-C identifier (same as C identifier). Use the following syntax to define a procedure:

      
    [static] [TypeName] id(TypeName1 [&] id1, TypeName2 [&] id2, ...]) 
    {
    statement1; 
    statement2; 
    ... 
    }
    
    

TypeName specifies the return type of the function. For more information, see Types. If the return type is not specified, the function will return typeless. When the void type is used, a value cannot be specified to the return statement. The return statement is used to specify the result of the function call and exit the function.

The optional static keyword is used to limit the scope of a procedure to the module in which it is defined. By default, procedures are global and can be accessed by any module. Procedures are called by specifying the name followed by comma delimited arguments, if any, in parentheses.

      
    [ result =] id( expr1, expr2, ... );
    
    

In the above example, expr1 matches the type of id1 and expr2 matches the type of id2, etc.

Example:

int increment(int x)
{
   return x+1;
}
boolean proc(int &p1, _str p2, _str (&list)[], int (*pfn)(int))
{
   return(true)
}
void defmain()
{
   p1 := 0;
   p2 = "Hello world";
   if ( proc(p1, p2, auto list, increment) ) {
      // ...
   }
}

Note

The list and p1 parameters are call by reference parameters. Like C++, list parameter requires parentheses around the & reference operator and the name, because the [] operator would otherwise be processed first. The pfn parameter is a pointer to a function.

Argument Declarations

The syntax for an argument declaration is the same as for declaring a variable, except that the static keyword cannot be used. An ampersand (&) before the id declares a call by reference parameter. Call by reference array and hash table parameters require parentheses around the & and id.

The last argument in the declaration list may be an ellipsis to indicate that the function accepts more arguments of any type. Use the arg function to access these optional arguments.

TypeName specifies the return type of the function. For more information, see Types. If the return type is not specified, the function will return typeless. When the void type is used, a value cannot be specified to the return statement. The return statement is used to specify the result of the function call and exit the function.

The optional static keyword is used to limit the scope of a procedure to the module in which it is defined. By default, procedures are global and can be accessed by any module. Procedures are called by specifying the name followed by comma delimited arguments, if any, in parentheses.

Example:

boolean proc(int &p1,_str p2,_str (&list)[],int (*&pfn)(int))
{
    return(true)
}

Note

The list, p1, and pfn parameters are call by reference parameters. Like C++, the list parameter requires parentheses around the & reference operator and the name, because the [] operator would otherwise be processed first. This avoids deviating much from C++ syntax. The command pfn is a reference to a pointer to a function.

Procedures can have up to 15 arguments defined. The procedure can be called with more arguments than defined by the procedure declaration. These extra arguments and the arguments defined in the procedure declaration can be retrieved by the arg function. Calling the arg function with no parameters returns the number of parameters with which the function was called. The minimum number of arguments with which the procedure may be called is defined by the procedure heading. A parameter of type var specifies a typeless variable passed by reference.

Default Arguments

Defining arguments with default values instead of using the arg function makes your code more understandable. The assignment operator has special meaning in an argument declaration. It defines a default value for an argument. The default value is used if the caller does not specify the parameter. Default arguments must always be specified in the function definition. Unlike C++, default arguments in prototypes do not have an effect on the compiled code.

Example:

static int proc2()
{
    return("before");
}
int proc(_str p1=proc2():+"after",int p2=2)
{
    return(p1+p2);
}
defmain()
{
    proc();           // Use defaults ("beforeafter" ,2).
    proc("param1");   // Use the second default value. 
    proc("param1",3); // Specify both values.
    proc(,3);         // This is not allowed.
}

Defining a Command

The _command primitive is used to define a new command with argument completion. A command can be invoked by typing its name on the SlickEditŪ command line, selecting it from a menu item definition, pressing a key, calling it in a Slick-CŪ function, or typing its name followed by arguments in parentheses in a Slick-C expression. Command procedures always have global scope and can be bound to a key with the Key Bindings option screen (Window → SlickEdit Preferences → Keyboard → Key Bindings).

The syntax for defining a command is:

      
    _command [TypeName | void] name1[,name2 [,name3... ]( [ArgDecl1, ArgDecl2, ...] ) 
    [name_info(const_exp)] 
    {
    statements
    }
    
    

TypeName specifies the return type of the command (see Types). If TypeName or void is not specified, the return type is typeless. When the void type is used, a value cannot be specified to the return statement. The return statement is used to specify the result of the function call and exit the function.

The syntax for ArgDecls is the same as for declaring a variable, except that the static keyword may not be used. In addition, an & before the id declares a call by references parameter. Call by reference array and hash table parameters require parentheses around the & and the id. However, all typed or named arguments must have a default value.

The last argument in the declaration list can be an ellipsis to indicate that the function accepts more arguments of any type. Use the arg function to access these optional arguments.

The name of a command may be a valid Slick-C identifier, or a string constant of a length of one, such as "/". SlickEdit uses the slash to define a search command.

Example:

// Allow command in read only mode.
// Use ellipsis because this accesses arguments.
_command int goto_line(...) name_info(','VSARG2_READ_ONLY|VSARG2_REQUIRES_EDITORCTL)
{ 
    param=arg(1);
    if (param=="" || ! isinteger(param)) {
       message('Please specify line number');
       return(1);
    }
    p_line=param;
    return(0);
}
_commmand void mycommand(_str filename="") name_info(FILE_ARG)
{
    if (filename=="") {
       _message_box("No filename specified");
    }
    message("filename="filename);
}

Commands receive unnamed command line arguments by calling the arg function. When a command is invoked from the command line, the expression arg(1) contains the rest of the command line after the name with leading spaces removed. For example, invoking the edit command e file1file2 calls the e command with file1 file2 in arg(1). The parse built-in is an excellent function for parsing a command line string (see the Help system for more information on parsing). When another macro calls a command, more than one argument string can be passed. Calling the arg function with no parameters returns the number of parameters with which the command or procedure was called.

name_info Attributes

The optional name_info expression is used to specify command argument completion rules and restricts when the command may be executed.

const_exp is a single constant expression. A comma (,) character in the string indicates the end of an argument.

The first argument in const_exp indicates the type of word arguments the command accepts and is used for argument completion purposes. For a list of already defined argument types, look in the slick.sh file for constants that end in _ARG. const_exp may contain one or more of the _ARG constants. Separate each _ARG constant with a space. An asterisk (*) character may be appended to the end of a completion constant to indicate that one or more of the arguments may be entered. The second argument (after the quoted comma) specifies when the command should be or disabled. One or more of the flags in the table below can be specified and ORed together with the bitwise OR (|) operator.

Flag

Description

VSARG2_CMDLINE

Command supports the command line. VSARG2_CMDLINE allows a fundamental mode key binding to be inherited by the command line.

VSARG2_MARK

ON_SELECT event should pass control on to this command and not deselect text first. Ignored if command does not require an editor control.

VSARG2_QUOTE

Indicates that this command must be quoted when called during macro recording. Needed only if command name is an invalid identifier or keyword.

VSARG2_LASTKEY

Command requires last_event value to be set when called during macro recording.

VSARG2_MACRO

This is a recorded macro command. Used for completion.

VSARG2_TEXT_BOX

Command supports any text box control. VSARG2_TEXT_BOX allows a fundamental mode key binding to be inherited by a text box.

VSARG2_NOEXIT_SCROLL

Do not exit scroll caused by using scroll bars. Ignored if command does not require an editor control.

VSARG2_EDITORCTL

Command allowed in editor control. VSARG2_EDITORCTL allows a fundamental mode. Key binding to be inherited by a non-MDI editor control.

VSARG2_NOUNDOS

Do not automatically call _undo('s'). Require macro to call _undo('s') to start a new level of undo.

VSARG2_READ_ONLY

Command allowed when editor control is in strict read only mode. Ignored if command does not require an editor control

VSARG2_ICON

Command allowed when editor control window is iconized. Ignored if command does not require an editor control.

VSARG2_REQUIRES_EDITORCTL

Command requires an editor control.

VSARG2_REQUIRES_MDI_EDITORCTL

Command requires MDI editor control.

VSARG2_REQUIRES_AB_SELECTION

Command requires selection in active buffer.

VSARG2_REQUIRES_BLOCK_SELECTION

Command requires block/column selection in any buffer.

VSARG2_REQUIRES_CLIPBOARD

Command requires editorctl clipboard.

VSARG2_REQUIRES_FILEMAN_MODE

Command requires active buffer to be in fileman mode.

VSARG2_REQUIRES_TAGGING

Command requires <ext>_proc_search/find-tag support.

VSARG2_REQUIRES_SELECTION

Command requires a selection in any buffer.

VSARG2_REQUIRES_MDI

Command requires MDI interface maybe because it opens a new file or uses _mdi object. Commands with this attribute are removed from pop-up menus in which the MDI interface is not available (editor control OEMs).

Example:

#include "slick.sh"
// This command supports completion where the first argument
// is a filename and the second argument is an environment variable.
_command test1(...) name_info(FILE_ARG" "ENV_ARG)
{
    parse arg(1) with file_name env_name;
    message("file_name="file_name" env_name="env_name);
}
// This command is enabled only when the target is an editor control
// which has a selection.
_command void gui_enumerate() 
       name_info(','VSARG2_REQUIRES_EDITORCTL|VSARG2_REQUIRES_AB_SELECTION)
{
    ...
}
// This commmand supports completion on multiple filenames.
_command e,edit(...) name_info(FILE_ARG'*,'VSARG2_CMDLINE|VSARG2_REQUIRES_MDI)
{
    ...

The edit command allows any number of file name arguments to be given. When the user is presented with a selection list of file names, many files may be selected with the spacebar key. If an asterisk (*) is appended to the end of a completion constant, that command must support a space-delimited list of strings. Double quotes are placed around arguments with embedded spaces.

The value of const_exp may be retrieved by the built-in function name_info.

OnUpdate Functions

A Slick-CŪ command can have a corresponding _OnUpdate_commandname function. This function is used to provide more precise control over the enabling and disabling of a command than the name_info command can provide.

Example:

int _OnUpdate_linehex(CMDUI &cmdui,int target_wid,_str  command)
{
    if ( !target_wid || !target_wid._isEditorCtl()) {
       return(MF_GRAYED);
    }
    if (p_UTF8) {
       return(MF_UNCHECKED|MF_GRAYED);
    }
    if (p_hex_mode==2) {
       return(MF_CHECKED|MF_ENABLED);
    }
    return(MF_UNCHECKED|MF_ENABLED);
}

Class Methods

Slick-CŪ classes can contain methods which implement the class behaviors. Slick-C supports static class methods. These methods may be called without having an instance of the class available. Like Java, all other Slick-C class methods are virtual. Unlike Java and C++, Slick-C class methods do not support overloading. A class method may have up to 14 arguments. Like Java and C++, the first argument is hidden and contains the class instance (this) for virtual methods.

Example:

namespace outer;
 
interface IShape {
   double area();
   void draw();
};
class Rectangle : IShape {
   int m_w=0;
   int m_h=0;
   double area() {
      return m_w*m_h;
   }
   void draw() {
      // Draw box.
   }
};
class Circle : IShape {
   int m_r=0;
   double area() {
      return m_r*m_r*3.1459;
   }
   void draw() {
      // Draw round thing.
   }
};
class Factory {
   static IShape makeShape(int x, int y, _str type, ...)
   {
      switch ( type ) {
      case "Rectangle":
         // ...
      case "Circle":
         // ...
      }
      return null;
   }
};
namespace default;
void draw_car()
{
   body := outer.Factory.makeShape(  0, 10, "Rectangle", 40, 10); 
   cab  := outer.Factory.makeShape(10, 10, "Rectangle", 20, 10);
   axl1 := outer.Factory.makeShape( 5, 5, "Circle", 5);
   axl2 := outer.Factory.makeShape(30, 5, "Circle", 5);
 
   outer.IShape car[];
   car[car._length()] = body;
   car[car._length()] = cab;
   car[car._length()] = axl1;
   car[car._length()] = axl2;
   double area = 0.0;
   foreach ( auto s in car ) {
      area += s.area();
   }
   foreach ( s in car ) {
      s.draw();
   }
}

Function Prototypes

Function prototypes provide the compiler with type information about a function without providing any code. Slick-CŪ reduces the need for prototypes by performing some argument checks at link time. When the linker finds an uninitialized variable error, it recommends that you add a function prototype to your source so the compiler can find your error. You might need a function prototype if you want to use the function address in an expression. Prototypes are not allowed for event functions.

The syntax for defining a function prototype is identical to defining a function except that a semicolon (;) is placed after the closing parentheses of the parameter list. Unlike C++, default arguments in prototypes have no effect on the compiled code. No code or name_info is given.

The need for function prototypes is also mitigated in Slick-C because of the #import directive which allows the compiler to import declarations from another Slick-C module. This is more convenient than C++, where you need to put declarations in a header file to support calling functions across modules. It is also more convenient that Java, because #import gets declarations directly from the source code, so the imported module does not need to be compiled to be imported. This simplifies compiling modules with circular dependencies.

Example:

    int proc(_str s,_str list[]);         // Function prototype.
    int (*pfn)(_str s,_str list[])=proc;  // Pointer to function.
    _command void command1(...);          // Function prototype.
    _command void command1(...) {         // Must have ... here to match prototype.
                                          // Use arg function here to get or set
                                          // arguments.
    }

Library Functions

A library function is a function that was implemented in a dynamically loaded library and was not written in the Slick-CŪ language. A library function must follow Slick-C calling conventions and be registered with the interpreter. Prototypes for library functions should use the extern keyword to indicate that they are implemented outside of Slick-C code.

Built-in Functions

A built-in function is a function that was implemented in the interpreter and was not written in the Slick-CŪ language.

Finding Functions

There are over 1200 documented functions and 200 properties. There are two ways to find the function that you seek. First, you can use the menu item Help → Macro Functions by Category, which displays smaller lists of these functions by category. Second, you can view source code for existing commands. If you do not know the name of the command but you do know the key that invokes the command, use the what_is command or Help → What Is Key to find the name of the command that is executed. Then, use the find_proc command or Macro → Find Slick-C Proc to display the macro source code.

Differences Between Commands, Built-ins, and Defs

  • A command definition looks like a procedure that starts with the _command primitive, and has an optional name_info construct after the arguments. Built-ins are not defined.

  • Commands always have global or namespace scope. Built-ins always have global scope. Procedures can have static (module), global scope, or namespace scope.

  • Commands can be bound to keys. Built-ins and procedures cannot.

  • Commands can be invoked from the command line or the execute function. Built-ins and procedures cannot.

  • A command may be given the same name as a built-in. However, this limits how the command may be called within a macro (use the execute function). None of the commands have the same name as a built-in so you can call any command just like any other function.

  • Only commands may be given non-alphanumeric single character names such as +, =, !, @, #, $, etc. However, this limits how the command can be called within a macro (place the command in quotes or use the execute function).

There are several differences between defining a procedure and defining a command with the _command primitive:

  • The scope of a procedure can be limited to a module.

  • Command functions are invoked by typing the name on the SlickEditŪ command line, from a menu item definition, by using the execute function, or by typing the command name followed by arguments in parentheses in a Slick-CŪ expression. Procedures can only be called by the latter method and cannot be bound to keys.

  • A procedure name must be a valid Slick-C identifier (same as C identifier). The name of a command can be a string constant containing a single character such as "/" (SlickEdit uses the slash to define a search command).

defmain: Writing Slick-CŪ Batch Files

A batch macro contains a special function named defmain. Slick-C batch files have the extension .e. Batch macros can be invoked by typing the name (extension not required) followed by arguments on the SlickEditŪ command line, quoting the name in a macro, or by using the execute function. If the batch macro needs to be recompiled, the Slick-C translator is invoked before the batch macro is executed. Do not use the load command to load a batch program, because defmain is not invoked and an error will result. If you load a batch program that you do not want, use the unload command to unload it. When a batch program is executed, the defmain procedure is called after the procedure definit is called. For more information, see Module Initializations.

The syntax of the defmain function is:

        
    [TypeName | void] defmain()
    {
    statement
    statement
    ...
    }
      
      

TypeName specifies the return type of the function. If TypeName or void is not specified, the return type is typeless. When the void type is used, a value cannot be specified to the return statement. The return value of defmain is placed in the predefined rc global variable.

Note

The execute function only supports returning an int type. Check the global rc variable for other types.

The arg function is used to retrieve the command line arguments passed to the defmain procedure. All of the command line arguments will be in arg(1). Use the parse statement to easily parse multiple space delimited arguments.

The following example displays the arguments given to the macro on the SlickEdit message line. If you define a procedure in a batch program, use the static keyword to conserve memory. SlickEdit stores the names of global procedures and variables in a names table.

defmain()
{
    messageNwait("Arguments given: "arg(1));
    parse arg(1) with word1 word2 .;
    messageNwait("word1="word1" word2="word2);
    return(0);
}

Extending the editor with a batch macro has the advantage of conserving memory and reducing the size of the state file. Also, batch macros can be easily shared between multiple users. The editor keeps the batch macro loaded only while it is executing. External batch macro names and arguments are not supported by completion. To provide completion, you must define a command with the _command primitive and have it call the external batch program. If you name the command the same name as the batch program (without the extension), use the xcom command to bypass internal command searching. There are two ways to invoke a Slick-C batch macro:

  • Type the name of the module followed by arguments on the SlickEdit command line.

  • Type vs -p program at the shell prompt, where program is the name of the batch program and vs is the name of the editor. Alternatively, you may use the -r option to have SlickEdit remain resident after the batch program completes.

For the above methods, SlickEdit invokes the translator to compile the source code file if the source code file exists and its date is later than the date of the .ex file.