Adding New Language Support to Multi-Edit
Multi-Edit ships with support for many of the most popular programming
languages, C/C++, Pascal, Modula-2, DBase and many more but with a little work
unsupported languages can easily be added. This document describes all of the
language features Multi-Edit supports and how to go about adding these
features for an unsupported language.
** If any user has developed support for a previously
unsupported language and would like it to become a supported language, send
us a copy of your language macro and support files, ie *.TPT etc and we will
have it added to a future version of Multi-Edit. Send all such requests to Multi-Edit Development, care of VP Dan
Hughes **
As Multi-Edit has evolved over the years, the features users want to change
most frequently have been redesigned so that most of the changes could be made
without resorting to writing macros and usually consists of filling in entries
in dialogs. This idea has been applied to the language support also but with
the vast differences between programming languages there are still some areas
where macros are required to provide the needed support.
A number of different language features are provided and can be added
independent of each other. This means that when adding support for a new
language all of these features do not need to be implemented to reap the
benefit of a particular feature. Thus the new language support can be
added and tested in stages or not even support some features.
The following list of language features are provided in Multi-Edit and are
shown from the easiest to implement to the hardest. Not all of these features
will be covered in detail here since they are covered fairly well in the
online help and the manual.
- Color syntax and keyword highlighting
- Code commenting and uncommenting
- Templates
- Compiler support and error processing
- Construct matching
- Smart indenting
- Function tagging
- Properties
The way Multi-Edit supports different languages is through the filename
extension. When a file with a specific extension is loaded, a check is done
to see if that extension has been defined and if it has the language features
associated with it are initialized. The Filename Extension Setup dialog is
where the extension specific information is setup and the language associated
with that extension is specified. It is also where the templates and
compiler entries are associated with a given extension.
The language specific information is mostly defined in the Language Setup
dialog accessed from the Tools main or the context menus. Template are
created and changed in the Edit Template dialog accessed from the Tools menu
as well.
Color syntax and keyword highlighting
The ability to highlight keywords and syntax with color is a very powerful
feature. This and the Code commenting feature is the easiest to add. All that
is required to add this feature is to create a new language record by doing
the following:
- From the main menu select Tools | Customize... | Languages...
- Select the Insert button and type in the name of the new language.
This will bring up the Language Setup dialog for the new language and the fields
can then be filled in with the appropriate data. See the online help for more
information about what each field should contain.
When the new language record has been defined it must be associated with a
set of file extensions. This is done as follows:
- From the main menu select Tools | Customize... | Filename extension...
- Select a defined extension and hit the Edit button or
Select the Insert button and type a new set of file extensions
- Select the ... button by Language and Select the newly defined language
from the list.
Code commenting and uncommenting
This feature is automatically added as soon as the comment fields are filled
out in the Language Setup dialog. See the online help for more information.
Templates
The new template system that is provided with Multi-Edit allows adding
abbreviations for common code fragments and language constructs that fully
expand when the space key is pressed. These templates are stored as *.TPT
files under the /MEW/CONFIG subdirectory. A specific language template can
be defined and associated with an extension just like a language record. This
is done as follows:
-
From the main menu select Tools | Customize... | Filename extensions...
- Select the '...' button beside the Template field
- Select a defined template and click the Select button or
select the Insert button and define a new language template.
There can be only one main language template associated with a set of
extensions, but by adding template set names separated by ;'s in the Addon
template field, it is possible to allow additional templates to be added to the
extension. This is how the Windows API template set would be added.
Also, while in the Extension setup dialog be sure to check the Auto template
expansion option to enable template expansion for the specified extension.
To edit or add a new template do the following:
- From the main menu select Tools | Edit templates
This will open the Edit Template dialog for the language specified in the
extension setup for the currently loaded window. This is where new templates
are added and older templates are modified. The fields in this dialog allow
each template to be modified with specifications as to what will be inserted
and when it will be expanded. See the online help for more details about all
of the fields in the template dialog.
Compiler support and error processing
Compiler support and error processing is setup through the Filename Extension
Setup dialog by selecting the Compiler/Program setup button. This will bring up
a list of all compiler entries for the specified extension. New entries
can easily be added by selecting the Insert button and filling in the dialog.
See the online help for more details.
Adding error processing requires a little more work but it is still fairly
straight forward. This involves creating a compiler type and specifying a set
of regular expressions that will match the lines with errors in the captured
compiler output. To do this, select the '...' button by the Program
type field in the Compiler/Program Setup dialog mentioned above. This is also
covered more thoroughly in the online help.
Construct matching
Construct matching provides the ability to start with the cursor on a opening
or closing construct, ie (), {} or begin end, and find (optionally
highlighting) the matching construct. This feature can be implemented in two
ways, with a macro that does the searching or through the new general purpose
LangDoMatch routine.
The macro method requires that a macro be written to locate and match all
supported constructs. This is the older method and will not be discussed in
detail here.
The new method of adding construct matching is to define a set of global
variables that define the patterns to match and then using the general purpose
LangDoMatch macro to do the matching. To implement this the following steps
that must be done.
-
Setup the special pattern global variable.
-
Write a special _xGetMatchPat( ) macro.
-
Write a xMatch macro which calls the LangDoMatch macro passing the
correct parameters depending upon the language Properties.
-
Setup the Init and Match macro fields in the Language Setup dialog to
point to the correct macros.
To setup the special pattern global variables currently requires an xInit macro
be written where x is the language prefix, usually the first three letters of
the language support macro filename. Eventually, we hope to move this into a
dialog and save them in a DB file so that it could be more easily updated.
There are three global variables that must be setup and are shown below using
C as the example. See the CInit macro in C.S for more details.
-
!CMatchExtra - Characters used to specify the start and end of words
This global string variable consists of three parts starting with the \x7F
character shown below as %
%B=str where str is a list of all of the characters that can precede a
construct word.
%E=str where str is a list of all of the characters that can come after a
construct word.
%D=char
where char is a character that is used to separate multiple construct
patterns. The default is to use the space character as the delimiter but
should be changed to something else for languages such as Ada and Visual
Basic that use double word constructs.
-
!CMatchBegPat - Beginning construct patterns to match
This global string variable consists of a series of records using \x7F as
the delimiter character, shown below as %, and each record can consists of
up to seven fields.
%Str1%Str2... where Str# is the character string of a beginning construct, ie IF or (
- %F= Flag where Flag is an optional bit flag used to control how the match routine
will work for matching the current construct. See Language.sh for the
_mfc_Xxxx flag values.
_mfc_StrOnly - This flags when set will cause the matching routine to
exactly match the construct ignoring the characters
before and after it.
_mfc_SkipMid - This flag when set changes the meaning of the %M=
strings to cause them to be skipped when searching for
a match. The %I= field should be used to specify
patterns to ignore when a middle pattern is also
required. The %I= field is available in Mew80b+
releases.
_mfc_ContMatch - This flag when set will cause matching to continue
when the %C= expression is found after a %B= or %E=
string was located. When this flag is reset matching
will end if the %C= expression is not found after a
%B= or %E= string was located.
%B= Str1 Str2 ... where Str# is a %D=char delimited, before and after, list of beginning
construct patterns, ie IF or (
%M= Str1 Str2 ... where Str# is a %D=char delimited, before and after, list of middle
construct patterns, ie ELSE or blank for ( matching. If the %F= flag has
the _mfc_SkipMid bit set then the match routine will cause a found string
matching one of the %M= strings to be skipped.
%E= Str1 Str2 ...
where Str# is a %D=char delimited, before and after, list of ending
construct patterns, ie ENDIF or )
%X=XStr
where XStr is a UNIX style regular expression that will match any of the
B, M or E string, ie (IF)|(ELSE)|(ENDIF) or [\(\)]
%I=IStr
where IStr is an optional %D=char delimited, before and after, list of
construct patterns to ignore when doing a match. See VBasic.s or Ada.s
for an example of how this would be used. (Available in Mew80b+ releases)
%C=CStr
where CStr is an optional UNIX style regular expression to search for
after a %B= or %E= match was found. What happens when a match is found is
determined by the value of the _mfc_ContMatch bit of the %F= flag. See
VBasic.s for an example of how this would be used.
%
The ending delimiter is required
-
!CMatchEndPat - Ending construct patterns to match
This global string variable serves the same functions as the !CMatchBegPat
except that it is used to specify ending patterns.
%Str1%Str2...
where Str# is the character string of a ending construct, ie ENDIF or )
%F= Flag
where Flag is an optional bit flag used to control how the match routine
will work for matching the current construct. Used exactly as described
above.
%B= Str1 Str2 ...
where Str# is a %D=char delimited, before and after, list of ending
construct patterns, ie ENDIF or ).
%M= Str1 Str2 ...
where Str# is an optional %D=char delimited, before and after, list of
middle construct patterns, ie ELSE or blank for ( matching. If the %F=
flag has the _mfc_SkipMid bit set then the match routine will cause a
found string matching one of the %M= strings to be skipped.
%E= Str1 Str2 ...
where Str# is a space delimited, before and after, list of ending
construct patterns, ie IF or (
%X=XStr
where XStr is a UNIX style regular expression that will match any of the
B or E string, ie (IF)|(ENDIF) or [\(\)]
%I=IStr
where IStr is an optional %D=char delimited, before and after, list of
construct patterns to ignore when doing a match. See VBasic.s or Ada.s
for an example of how this would be used. (Available in Mew80b+ releases)
%C=CStr
where CStr is an optional UNIX style regular expression to search for
after a %B= or %E= match was found. What happens when a match is found is
determined by the value of the _mfc_ContMatch bit of the %F= flag. See
VBasic.s for an example of how this would be used.
%
The ending delimiter is required
Before we go into more detail, lets explain how the matching feature works.
When the match routine is run either from a key, toolbar or menu, the Match
macro looks in the language record specified by the current file's extension
for the macro in the Match field. If an entry is found, the specified macro
is executed. This is usually a macro that calls the LangDoMatch routine with
the language prefix specified /LP= and possibly some other parameters to
specify if highlighting is to be done or not.
The first thing that LangDoMatch does is call a special macro for the
specified language called _xGetMatchPat, x being the language prefix passed as
the /LP= parameter. This macro is to provide special character processing for
the specific language. This can be as simple as setting Return_Str to "" and
returning (ie. no special processing), or checking if the current character is
one of the special characters and returning the character surrounded by
the \x7F delimiters characters, or searching for special characters on the
current line and then returning the delimited character. This macro is usually
used to process single character and possible double characters (such as comment
characters /*) but can also be used to reposition the cursor on another word
that is supported. See Fortran.s or Ada.s for an example of the word
repositioning feature. The main LangDoMatch will then process whole words if
the _xGetMatchPat returns "" or the special character can not be matched.
If the _xGetMatchPat returns a pattern then the LangDoMatch routine searches
the special MatchBegPat global variable for a beginning pattern match. If
one is found, it will parse out the fields of that record. It will then use
the specified regular expression to do a forward search. When a match is
found, the found string is compared to the begin, middle and end patterns of
the specified record. What happens next depends upon which string the found
string is found in. If it is found in the begin string a match count is
incremented by one, since a nested construct is found and the search is
continued. If the end string contains the found pattern then the count is
decremented by one and will exit the search loop when it reaches 0, ie the
ending construct was found. If the middle string contains the found string
then the search is repeated unless the count is at 1, ie still inside the
first construct and thus a middle construct match.
If the original pattern was not found in the MatchBegPat global variable then
the MatchEndPat global is "search" and the same process as above happens except
for searching backwards for matching patterns.
If the _xGetMatchPat returns "" or the pattern can not be matched, the word the
cursor is sitting on is read using the begin and end word string to delimit
the word. The above process is then repeated with the word instead of a
character pattern. Thus if the new language is to only support words then the
_xGetMatchpat can always return "".
When a matching construct is found it will be highlighted if the appropriate
parameters are passed. See the LangDoMatch macro in Language.S for more
details.
After the xInit, _xGetMatchPat and xMatch macros are written, the xInit macro
needs to be specified in the Init macro field and the xMatch macro in the
Match field of the Language Setup dialog.
Smart indenting
Smart indenting is the ability to position the cursor in the correct column
to continue typing code after the Enter key is pressed. Since this feature
is very language dependent a macro must be written which, when called by the
CR system macro, should check the context of the cursor and reposition the
cursor on the next line in the correct position.
We have developed two different type routines that should handle most languages
and they can be seen in the C.s and Pascal.S files. The C.s macro, CIndent,
checks the line ending characters of previous lines to determine the indent
level of the next line where the Pascal.s macro, PasIndent, checks the first
word on previous lines to do likewise.
To implement the indent macro for the new language, determine which type of
routine the new language is most like and start with a copy of one of the
supplied macros and change it to implement the specific cases.
When you have the macro written and tested it is installed by doing the
following:
-
From the main menu select Tools | Customize... | Filename extension...
-
Select the specific extensions and hit the Edit button.
-
Set Indent style to Smart.
-
Exit dialog by hitting OK
-
From the main menu select Tools | Customize... | Languages...
-
Select the new language entry and hit the Edit button.
-
Enter the macro name in the Indent macros field, ie C^CIndent
Note: Select Indent style "Auto" if a language indent macro has not been
written. This will cause the cursor to line up with the first word on the
previous line.
Function tagging
This feature requires a macro be written to scan the source file for the function
declaration and/or variable names and writes them to a tag file in a specific format.
The MeTags macro must then be patched to support the new language. This will
not be covered in this document since this is rather involved. We hope to
eventually make this easier to add in the future.
Properties
The properties feature is not really a separate feature in itself but is used
to support some of the other features. The Set Properties button in the
Language Setup dialog, when pressed, will run the macro specified in the Config
macro field. This macro should display a dialog that presents the user with
configuration options that are supported in the new language support. Examples
of this can be seen in the C and Pascal support where the indent style and
auto highlighting of closing ')' can be enabled or changed.
If the new language is to support properties then a set of macros needs to be
written to support them. These macros are described below using C as the
example. Replace the leading C with the first three characters from the name
of the new language macro file and make the needed changes for the specific
properties.
void CSetProperties(
str GStr = Parse_Str( "/GSTR=", MParm_Str ),
str Parameter = MParm_Str
)
/******************************************************************************
Function: Set C and CMAC specific properties. Should only be run by the
LangSetProperties macro.
Entry : str GStr - Name of global string containing properties (/GSTR=)
str ParmStr - Misc parameters
/L=str - Language name to show on title bar
Exit : None
Note:
This macro displays a dialog of all of the available properties that can
be set for the specified language. This macro should save the changed
properties data in the global variable specified by GStr before exiting so
that the Language Setup dialog can update the db record for the specified
language.
********************************************************************( ldh )***/
int CGetProperties( struct tCProperties rCP )
/******************************************************************************
Function: Get the specific language properties for the current file.
Entry : struct rCP - A structure to fill with the properties.
Exit : int
True - Properties fill from Db
False - Properties set to internal defaults
Note:
This macro is called by all the other macros when they need to query a
specific property. A structure should be define that contains entries for
all of the supported properties.
********************************************************************( ldh )***/
void CIndentTmp(
str Set = Parse_Str( "/S=", MParm_Str ),
str Name = Parse_Str( "/N=", MParm_Str )
)
/******************************************************************************
Function: Insert the indent style template Name from the template set Set.
Entry : str Set - The template set
str Name - The template name
Exit : None
Note:
This macro would only be written and used if the new language allows
templates to adjust indent style based upon a property setting. When this
is used the template must be setup to call this macro to determine the
indent style and the name of a template that will expand to the selected
indent style. See C.TPT for examples.
********************************************************************( ldh )***/
** If any user has developed support for a previously
unsupported language and would like it to become a supported language, send
us a copy of your language macro and support files, ie *.TPT etc and we will
have it added to a future version of Multi-Edit. Send all such requests to Multi-Edit Development, care of VP Dan
Hughes **
|