IntelliSense in Visual FoxPro 7
IntelliSense in Visual FoxPro 7
Applies to:
Visual FoxPro 7.0
Summary
Learn how IntelliSense is implemented in Visual FoxPro 7.0 and how to take advantage of its potential. IntelliSense™ was first introduced in Microsoft® Visual Basic 5.0™ and has been widely
implemented as an essential developer productivity enhancement in many languages. The implementation in Visual FoxPro 7.0 is unique among them in that it features an open architecture that
allows developers to both customize and extend the built‐in features.
Contents
Introduction
Basic Features of IntelliSense
Quick Info in Editing Windows
Quick Info in the Command Window
Member Lists
Configuring Intellisense
The FoxCode Table
Creating and Using Scripts
Introduction
The term "IntelliSense" refers to the functionality that, when a developer is working in a code editing window, provides auto‐completion of commands and context sensitive, as‐you‐type, help
with syntax. This functionality has long been available in other Microsoft developer tools, but has been conspicuously absent from Visual FoxPro. IntelliSense was finally introduced in to
Visual FoxPro with the release of Version 7.0
The implementation of IntelliSense in Visual FoxPro 7.0 provides all of the standard features, and some new ones. However, because the implementation is data driven it is possible to both alter
the default behavior and to create entirely new behaviors. This paper explains how IntelliSense has been implemented in Visual FoxPro 7.0 and illustrates how developers can take advantage of
its open architecture to make their lives easier, and increase their productivity.
Figure 1 shows how, when writing code, Visual FoxPro 7.0 recognizes partial commands and highlights them using the settings defined for syntax coloring in the 'Editor' tab of the Options
dialog. There is nothing new in this but if, as soon as the code is highlighted, the space key is pressed, Visual FoxPro 7.0 completes the command and displays any associated "Quick Info" in a
tooltip ﴾Figure 2﴿ .
Quick Info comes in several forms, depending on the context. In Figure 2 we can see that it is telling us that the next thing expected is a 'ParameterList'. However, this is not the only format for
Quick Info which is also used to pop up context sensitive extracts from the help file ﴾Figure 3﴿.
The next line of code is a declaration and, after entering the key word and pressing the space key, the associated help text for the command appears. Notice that in this particular case we could
not use auto‐complete for the command because the first four characters ﴾"LOCA"﴿ are actually assigned to the LOCATE command.
The next type of Quick Info that is available is a "Values" list ﴾Figure 4﴿ which is generated automatically for commands which have multiple options.
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 1/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
Notice the additional "Value Tip" that is displayed as the list contents are scrolled. A variant of the basic value list is shown in figure 5 where the available options for a SET command are
displayed, and the value tip is used to indicate which option is currently set. Similar lists are available for settings that accept True/False values.
All of the examples so far are triggered when the space key is pressed, but this is not the only way to trigger IntelliSense. When entering a function, typing the left parenthesis key triggers a
Quick Info "smart" tip ﴾Figure 6﴿.
Notice that although all of the parameters for the StrTran() function are displayed in the Quick Info tip, only the second one ﴾cSearchFor﴿ is highlighted in bold. It is no coincidence that this is the
parameter that is about to be entered in the code window. The IntelliSense engine tracks the commas that separate the parameters to synchronize the highlight in the Tip Window with the
current position in the code window.
Figure 7: Most Recently Used ﴾MRU﴿ List – available in command window only
As the name implies, MRU lists contain the names, and fully qualified path, of the items of the appropriate type that were most recently opened. Separate MRU lists are maintained for the file
types specified by the USE, MODIFY, OPEN, REPORT, LABEL and DO commands. Notice, by the way, that the MRU list always includes the fully qualified path and file name. If the next command
entered into the command window were simply "USE fixlist", the entry that gets added to the MRU would still be "D:\VFP70\Run\fixlist.dbf".
The number of entries kept in these lists is static, which means that once the defined number of entries has been reached, the oldest entry is dropped each time a new items has to be added.
The limit is set on the "View" tab of the Options Dialog and also controls the number of projects that are retained on the File pad of the main system menu.
A variant of the standard MRU list which lists fields in the currently open table is available when using the REPLACE command from the command window ﴾Figure 8﴿
Instead of displaying a list of files, the IntelliSense engine builds a list of the fields in the currently selected table, and includes the data type and size information in the value tip for each field.
Note: The same field list is triggered by typing the period when referencing any currently open table in the command window using the <alias>.<field> syntax.
The second type of special list which is available only in the command window displays all variables that are currently defined, and are in scope, when the shortcut "m." is entered, followed by a
space ﴾Figure 9﴿.
In this list, the Value Tip is used to display the contents of the variable. Object references are shown as either "(Object)" or ".NULL." depending on whether the object is actually instantiated or
not.
Member Lists
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 2/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
Member Lists
Member lists are available in both editing and command windows for objects which have been instantiated and are currently in scope ﴾Figure 10﴿.
The list includes all the properties, events and methods ﴾PEM﴿ of an object, and each type of entry has its own distinctive icon. The Value Tip of a member list displays the comment that is
associated with PEM ﴾and even display the comments for PEMs created in user defined classes﴿.
Member lists are evaluated dynamically so that selecting a contained object will display the members of that object and so on. Figure 11 shows the members list for the child of child of a
contained object in the oDM object.
This would, in itself be useful, but IntelliSense can do much more thanks to another new feature introduced in Visual FoxPro 7.0, "Design Time Strong Typing". Visual FoxPro has always been a
"weakly typed" language. This means that there is no requirement to declare variables explicitly, and that variables, once defined can change their type as needed. This is an enormously
powerful, and flexible approach which has significant benefits for a data centric development tool. However, as always, there are drawbacks. One is that the system can never 'know' what a
variable is being used for, and therefore can neither assumptions about it, nor use it as a source of information.
Visual FoxPro 7.0 now allows the use of strong typing in certain places, at design time only, in order to support IntelliSense. It is implemented by using the new AS clause which can be applied
to classes, parameters or variables to indicate to the IntelliSense engine how a reference is to be handled. There are a number of possible ways of using it.
First it can be used to define objects and variables as instances of other Visual FoxPro classes. These can be native base classes as shown in Figure 12 where the local variable loCombo is defined
as a combobox and then, in the code the reference is used inside a WITH…ENDWITH construct to access the members list directly. When coding is finished, a global search and replace removes
"loCombo" leaving just the ".". Since Strong Typing is only enforced at Design time the declaration can be left in place in the code without causing any problems at run time.
The same technique can be used with custom classes ﴾Figure 13﴿. Note that in order to build member lists for classes they must either have been registered with IntelliSense ﴾see below﴿ or be
specified using the OF <class library> clause as illustrated.
However, IntelliSense is not limited to Visual FoxPro classes. Any class, or application, that exposes a Type Library can be accessed by the IntelliSense engine to build a Member list. ﴾Figure 14﴿.
This provides an alternative to using the Object Browser to discover how to access and control OLE Server applications, COM components and ActiveX controls.
Strong typing also allows for defining the return value and parameter data types for Functions and Procedures in class definitions. In this situation the members list display all of the available
data types and objects that have been defined to IntelliSense ﴾Figure 15﴿
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 3/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
This information is automatically displayed by IntelliSense as Quick Info whenever the resulting method is accessed ﴾Figure 16﴿. Notice that is doesn't matter to IntelliSense whether the class
library is defined visually, or in a program file.
Configuring Intellisense
There are two ways of configuring IntelliSense in Visual FoxPro 7.0.
The first is by setting the contents of a new property on the VFP application object named "EditorOptions". The default setting is "LQKT" although there actually are five features that can be
controlled through this property, as follows:
Hyperlinks ﴾"K or k"﴿. Determines how links are activated. Setting to "k" requires only a single mouse click, while "K" requires that CTRL+Click is used ‐ which is the default. If neither is
specified, hyperlinks are treated as normal text in editing or command windows.
Word Drag 'n' Drop ﴾"W"﴿. When enabled, text can only be dragged to a position immediately following a space. Prevents text being ﴾inadvertently﴿ inserted into existing text when using
drag and drop in an editor or the command window. This behavior is disabled by default.
Designer Value Tips ﴾"T"﴿. This controls whether the items displayed in lists show their associated 'tool tips'. By default, they are shown as you scroll through member lists.
List Members ﴾"L or l"﴿. This controls whether, and when, the object member lists are displayed. By default, lists are automatic ﴾"L"﴿ though you may prefer to use the lower case "l" option
to suppress the automatic display, but still have the list pop up when you press CTRL+J ﴾or select 'List Members' from the "Edit" pad on the main FoxPro menu﴿.
Quick Info ﴾"Q or q"﴿. This controls whether, and when, the various types of Quick Info is displayed. By default, the display is automatic ﴾"Q"﴿ though you may prefer to use the lower case
"q" option to suppress the automatic display, but still have the Quick Info available when you press CTRL+I ﴾or select 'Quick Info' from the "Edit" pad on the main FoxPro menu﴿.
In order to enable all features of IntelliSense ﴾including the controlling text drag and drop﴿ simply enter the following at the command window:
_VFP.EditorOptions = "LQKTW"
The second way is to use the new 'IntelliSense Manager' form which is accessible from the Tools pad of the main system menu. This form has four pages and not only gives access to the same
configuration details described above, but also to other, IntelliSense related functionality through the FoxCode table that drives IntelliSense.
The first page ﴾Figure 17﴿ provides an interactive way to set the _VFP.EditorOptions property values for Member Lists and Quick Info. It also defines how IntelliSense should handle the case used
for Commands, Functions and defines a default value to be used whenever there is no specific instruction. The checkbox determines whether the settings defined are applied to native Visual
FoxPro commands and functions only, or whether they should be applied to user‐defined commands and functions also. Buttons activate the Tips window and browse the FoxCode table that
stores the information used by IntelliSense.
The second "Types" page ﴾Figure 18﴿ controls the entries that are to generate the list of types that are displayed when the AS clause is used. Visual FoxPro 7.0 ships with a 'core' set that includes
all of the Visual FoxPro base classes, and all standard data
types, whether they are actually available in Visual FoxPro or not. Clearing the check box prevents an item from being displayed in type lists.
The list of available types can be extended using the buttons on this page.
Edit opens a browse window that displays the entry for the currently selected item from the FoxCode table. Mainly used to amend the display name for an item
Type Libraries opens a dialog that lists registered COM servers, ActiveX controls or both and allows them to be added to the Type List ﴾Figure 19﴿
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 4/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
Classes opens the standard Visual FoxPro visual class library dialog and allows custom classes to be added to the IntelliSense Type List ﴾Figure 20﴿.
Note that classes that have defined in program files can be included in the Type List, but the appropriate record must be added to the FoxCode table manually
Web Services invokes the Web Service Registration wizard and adds the specified web service to the Type List ﴾Figure 21﴿
The third "Custom" page ﴾Figure 22﴿, provides a filterable view of that can be used to add, edit or delete custom entries in the FoxCode table.
The next section of this paper deals with the FoxCode table in detail.
The final "Advanced" page ﴾Figure 23﴿ gives access to two additional items, Additional Properties and Cleanup. The additional properties are covered in detail in the next section of this paper.
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 5/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
The cleanup button brings forward a maintenance dialog for the FoxCode table and Most Recently Used ﴾MRU﴿ lists ﴾Figure 24﴿.
Type C ﴾1﴿ Identifier which defines how the record should be processed:
Data M ﴾ 4﴿ The content for this record may include List values, code, script text etc
X = No replacement applied
Note: The value specified in the 'Version' record defines the default to be used for any record that does not have its own
setting.
Save L ﴾1﴿ Flag to indicate whether record is preserved during updates to the FoxCode table ﴾False for VFP items﴿
Source M ﴾4﴿ Source for record content. ﴾VFP items use "Reserved"﴿
The Advanced page ﴾Figure 24﴿ of the IntelliSense Manager form includes options to restore and clean up the FoxCode table. If the table gets damaged, or an entry is inadvertently deleted or
changed, the table can be restored to its original state. The TimeStamp, UniqueID and Save fields are checked when the FoxCode table is packed updated or restored to determine the origin of
the data and whether it may be overwritten. By default the native Visual FoxPro entries have both a time stamp and a unique ID, but their Save field is set to False so that they can be overwritten.
User defined entries, on the other hand, do not have ﴾or need﴿ either a unique ID or a time stamp. Providing that their Save field is set to True, they will not be overwritten or deleted by changes
to the table.
The various record types, together with an example of using each, are detailed below:
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 6/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
This Command Type defines an auto‐complete entry that is triggered by the space character and that is, by default, executed by calling the "Default Script" ﴾this is defined in the Data field of the
record with Type = S and an empty Abbrev field﴿. All of the basic VFP commands use this methodology to concatenate the content of the Expanded field to the abbreviated form of the
command.
The other, more complex, commands invoke a generic command handler script ﴾{cmdhandler}﴿ by specifying it in their Cmd field. This script replaces the content of the Abbrev field with the
content of the Expanded field rather than concatenating them. This methodology can easily be used to create "custom commands" that explicitly associate Quick Info ﴾from the Tip field﴿ or a
Members List ﴾from the Data Field﴿ by defining an abbreviation and including a call to the command handler script.
Example: The default behavior of the ON command auto‐completion is to display a list of all the options for the "ON" command, that includes all the print and menu options. However, in
general coding, the only ON command that is used often is ON KEY LABEL. To create an auto‐complete command for the string "OKL" that expands to "ON KEY LABEL " when followed by a
space, add a record to the FoxCode table as follows:
Example: To create an auto‐complete entry for a user‐defined function named OpenFile﴾﴿ triggered by typing "OPF(" add a record to the FoxCode table as follows:
This defines the function as having three parameters. As commas are added to the typed text, the highlighted tip is synchronized automatically.
Example: To assign the default color picker to a custom property name simply add a record to the FoxCode table, substituting your own property name ﴾note that the leading "." is required in
the Abbrev field﴿ as appropriate:
The use of scripts is covered later in this paper in more detail. However, for now suffice it to say that a script may be written directly into the Data field of a record and executed by setting the
Cmd field to contain empty braces ﴾"{}"﴿. To add a script that displays the GetFile() dialog and returns the selected file name for a custom property named ".cSourceFile", add a record to the
foxcode table as follows:
P .cSourceFile {} M .T.
LPARAMETER oFoxCode
LOCAL lcTxt
oFoxcode.valuetype = "V"
lcTxt = ['] + GETFILE() + [']
RETURN lcTxt
Whenever a property named cSourceFile is assigned a value, the GetFile﴾﴿ dialog will be automatically displayed. ﴾Remember, IntelliSense only operates in the development environment, nothing
happens at run time﴿.
Example: To add the Microsoft TreeView control as an option in the Type List, add a record to the FoxCode table as follows﴾
Note that the Data field must contain the correct GUID which is obtainable from the registry. However, the easiest way to create a record of this type is to use the 'Type Libraries' option on the
second page of the IntelliSense Manager and then edit the default name ﴾if necessary﴿ using the editor on the third page.
For visual classes, the 'Classes' option on the second page of the IntelliSense Manager can be used. However for programmatically defined classes the necessary record must be added manually.
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 7/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
Example: To create an expansion of the 'cright' shortcut, add a record to the Foxcode table as follows:
User Type records can also call scripts. The Cmd field is used to indicate where the script is located. A pair of empty braces means that the script is embedded in the current record, while
specifying a name indicates that the script is defined in a Script Record elsewhere in the table. The following variation on the shortcut uses an embedded script to achieve exactly the same result:
U cright {} X .T.
LPARAMETER oFoxCode
LOCAL lcTxt
oFoxcode.valuetype = "V"
lcTxt = "© 2002 Microsoft Corporation"
RETURN lcTxt
Example: The following script displays the Getfile﴾﴿ dialog when invoked and returns, to the calling source, whatever was returned from that dialog. All that is required is to add a record to the
FoxCode table with the name of the script in the Abbrev field and the necessary code in the Data field, as follows:
S ChooseFile .T.
LPARAMETER oFoxCode
LOCAL lcTxt
oFoxcode.valuetype = "V"
lcTxt = ['] + GETFILE() + [']
RETURN lcTxt
The custom extension type is used to identify records that IntelliSense does not process automatically. There are only two such records that are shipped with Visual FoxPro 7.0.
The first, whose Abbrev field contains "CustomPEMs" stores the settings for the advanced configuration properties in its Data field. These properties are accessible through the 'Properties' button
on the Advanced page of the IntelliSense Manager.
The second, whose Abbrev field contains "CustomDefaultScripts" stores the names of any user defined scripts that are invoked by the hooks coded into the default script when the advanced
property named "lAllowCustomDefScripts" is set to True.
A parameter statement. Scripts need to be able to accept a single parameter, which is a reference to the FoxCode object. The properties of the FoxCode object are documented fully
documented in the help file and are also available on MSDN.
Define the type of return that the script generates. This is done by setting the ValueType property of the FoxCode object. This property is used to determine how the result of running the
code in the script is to be interpreted and there are three possible values as shown in Table 2:
V Value: Action depends upon the script – may be used to replace the triggering text or add to it
T Tip: Displays the contents of the FoxCode.ValueTip property as a Quick Info Tip
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 8/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
Check the Location from which the script has been invoked. This is done by checking the Location property of the FoxCode object. Clearly not all actions are appropriate to all situations
and this allows us to bypass the script unless we are in the correct editing window. The values generated for the different editing window types are listed in Table 3:
0 Command Window
1 Program
8 Menu Snippet
10 Code Snippet
12 Stored Procedure
Note: These values, together with much other information about the currently active window, are obtained by calling the FoxTools _EdGetEnv﴾﴿ function. This is not functionality
that is specific to IntelliSense.
The remainder of the script is basically standard FoxPro code, that manipulates the properties of the FoxCode Object and carries out whatever tasks are necessary. The following examples
illustrate the construction and use of Scripts.
In order, to create the script to generate this block of text we need to add a record to the FoxCode table and set it up as illustrated in the following table:
Field Contents
Type U
Abbrev hdr
Cmd {}
Data
LPARAMETERS toFoxCode
IF toFoxcode.Location <1
RETURN toFoxCode.UserTyped
ENDIF
toFoxcode.valuetype = "V"
LOCAL lcTxt, lcName, lcComment
STORE "" TO lcTxt, lcName, lcComment
#DEFINE CRLF CHR(13)+CHR(10)
lcName = WONTOP()
lcVersion = "Visual FoxPro" + VERSION(4)
lcComment = INPUTBOX( 'Comment for the header:' )
TEXT TO lcTxt NOSHOW
***********************************************************************
* Program....: <<UPPER(lcName)>>
* Author.....: Andy Kramek
* Date.......: <<DMY(DATE())>>
* Notice.....: Copyright (c) <<TRANSFORM( YEAR(DATE()))>> Tightline Computers Inc
* Compiler...: <<lcVersion>>
* Purpose....: <<lcComment>>
***********************************************************************
~
ENDTEXT
RETURN TEXTMERGE(lcTxt)
Although very simple, this script illustrates how an IntelliSense script is really constructed in two parts. First we have the IntelliSense specific setup in which we establish a parameter to receive a
reference to the FoxCode object:
LPARAMETERS toFoxCode
*** This script returns a Value
toFoxcode.valuetype = "V"
*** Do nothing unless we are in a program editing window
IF toFoxcode.Location # 1
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 9/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
RETURN toFoxCode.UserTyped
ENDIF
Then we define how the IntelliSense engine is to treat the return value; there are three possibilities:
V ﴾Value﴿Treat whatever is returned from this script as a value. By default values simply replace the shortcut that triggered the script
L ﴾List﴿Use the contents of the Items collection as the source for a list. The script must ensure that the collection is properly dimensioned and populated
T ﴾QuickTip﴿Display the contents of the ValueTip property as a 'Quick Info' tip. The script must manage the content of the property
Finally we check the Location property of the FoxCode object. The location is stored as a numeric value derived from a FoxTools function ﴾EdGetEnv()﴿ that returns an array containing various
items of information about the current editor session. The window type ﴾element 25﴿ is specified as being:
0 = Command Window
1 = Program
8 = Menu Snippet
10 = Code Snippet
12 = Stored Procedure
Since this particular script is really only applicable to a Program file, we can limit its scope to location = 1. Note that when called from any other locations we simply return the contents of
another FoxCode object property, "UserTyped", which, as the name implies contains whatever it was that the user actually typed in to trigger the script. The net result of typing 'hdr ' in any
location except a program, is therefore, the text string 'hdr '.
The second part of the script is standard Visual FoxPro code that actually builds the header text as a string. It is worth noting that this simple script contains a mixture of old and new Visual
FoxPro functions.
*** Standard FoxPro Code from here on
*** Define and initialize local variables
LOCAL lcTxt, lcName, lcComment
STORE "" TO lcTxt, lcName, lcComment
*** Get the window name (if one has been defined)
lcName = WONTOP()
*** Get the VFP Version Number
lcVersion = "Visual FoxPro " + VERSION( 4 )
*** Get the description
lcComment = INPUTBOX( 'Comment for the header:' )
TEXT TO lcTxt NOSHOW
***********************************************************************
* Program....: <<UPPER(lcName)>>
* Author.....: Andy Kramek
* Date.......: <<DMY(DATE())>>
* Notice.....: Copyright (c) <<TRANSFORM( YEAR(DATE()))>> Tightline Computers Inc
* Compiler...: <<lcVersion>>
* Purpose....: <<lcComment>>
***********************************************************************
~
ENDTEXT
RETURN TEXTMERGE(lcTxt)
After defining, and initializing, the necessary local variables, we begin populating them by getting the name of the current window using the old WONTOP() function. Next we retrieve the VFP
Version number, using option 4 of the VERSION() function, and finally we use the brand new INPUTBOX() function to get a comment for the header from the developer.
The block of text that we want to insert is then built as a string using the newly extended TEXT…ENDTEXT construct ﴾which now allows us to store the text directly as a variable﴿. Notice the
inclusion of the tilde character on the last line of the header to ensure that the cursor is positioned correctly. Finally the RETURN statement uses yet another new function, TEXTMERGE() to
evaluate the variable and return the resulting text as the replacement for the defined abbreviation.
The following example shows how we can define a simple list of this sort that allows us to create customized shortcuts for native VFP commands that we use often.
Here a user‐defined shortcut ﴾"CSPB"﴿ displays, as a list, two pre‐defined options which can be used to complete the CursorSetProp buffering command ﴾we do not normally use any of the other
modes﴿. To create this ﴾and similar﴿ shortcuts all that is needed is to add a record to the FoxCode table with the following characteristics:
Field Content
Type U
Abbrev CSPB
Expanded CURSORSETPROP﴾
Cmd {CmdHandler}
The CmdHandler script's default behavior is that it reads the entries from the 'data' field and displays them as a list. The text in the 'abbrev' field is replaced with the contents of the 'expanded'
field to which the selected item from the list is appended.
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 10/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
However, this is not the only way in which we can create a static list. As we indicated in the discussion of the header script above, the FoxCode object exposes an Items collection that is used to
generate lists when the ValueType property is set to "L". We can, therefore, generate a list by creating a script which directly populates the Items collection. This has a distinct advantage over the
CmdHandler approach because the Items collection actually has two columns. The first is used as the prompt displayed in the list and the second is used to create the "Value Tips" that are seen
in most of the native lists.
The next example populates the Items collection with a list of three possible references to be used when creating a local variable to refer to an object. Figure EG03 shows the list, which is
triggered by typing 'obj' followed by a space.
In this example we are again using the 'expanded' field to replace the text used to trigger the script and the list to complete the assignment statement. The necessary FoxCode record looks like
this:
Field Content
Type U
Abbrev Obj
Expanded loRef =
Cmd {}
Data
LPARAMETER toFoxCode
WITH toFoxCode
.ValueType = "L"
DIMENSION .Items[3,2]
.Items[1,1] = "This"
.Items[1,2] = "Current Object"
.Items[2,1] = "ThisForm"
.Items[2,2] = "Current Form"
.Items[3,1] = "This.Parent"
.Items[3,2] = "Immediate Parent Container"
RETURN ALLTRIM( .Expanded )
ENDWITH
The following example uses a generic script to build a list of files of the specified type in the current directory and any immediate sub‐directories. The idea is that it is then called by other
shortcuts that define the specific file type required at the time. Why bother when we already have MRU lists? Well in order for a file to appear in an MRU you must have used it at least once, and
it must have been used within whatever number of files you are using as the MRU file limit. Figure EG04 shows the result of typing the 'mop' ﴾for "Modify Program"﴿ shortcut at the command
line.
Figure EG04: Dynamic list of PRG files in current and first level sub directory
The first thing that we need to do is to create a shortcut entry in the FoxCode table. This is a simple User record of the type we have already met. The only difference here is that instead of
calling the script embedded in the Data field, we are now naming a generic script ﴾"ShoFile"﴿ in the Cmd field. The record to add looks like this:
Field Content
Type U
Abbrev Mop
Cmd {shofile}
The actual script is a little more complex than we have seen so far because we need to manipulate the way in which the FoxCode object is handled. In order to do this we have to instantiate a
custom sub‐class of the FoxCodeScript class ﴾which is defined in FoxCode.prg﴿. The first part of the script deals with this as follows:
LPARAMETER toFoxcode
IF FILE(_CODESENSE)
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 11/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
IF FILE(_CODESENSE)
LOCAL luRetVal, loFoxCodeLoader
*** Ensure we have the root class definition
SET PROCEDURE TO (_CODESENSE) ADDITIVE
*** Create an instance of our custom sub class
loFoxCodeLoader = CreateObject("FoxCodeLoader")
*** Call it's Start() method and pass the FoxCode object
luRetVal = loFoxCodeLoader.Start( toFoxCode )
*** Clean up nicely here
loFoxCodeLoader = NULL
IF ATC(_CODESENSE,SET("PROC"))#0
RELEASE PROCEDURE (_CODESENSE)
ENDIF
*** Return whatever we got back
RETURN luRetVal
ENDIF
Notice that this code assumes that the new system variable, _CodeSense, is pointing to a program that includes the class definition for the "FoxCodeScript" object. The second part of this script
﴾below﴿ defines the custom subclass of the which is instantiated here and passed the reference to the FoxCode object. The rest of this part of the script merely ensures that the object is released
cleanly and no references are left dangling. This the standard preamble for creating a custom script.
The second part of the script is where we define the sub class to carry out the specific tasks we need. The FoxCodeScript class defines a custom property named "oFoxCode" which holds the
passed in reference to the FoxCode object and a template method named "Main()" which is called from the Start() method when the object is instantiated. It is here that we place our specific
handling code:
DEFINE CLASS FoxCodeLoader as FoxCodeScript
PROCEDURE Main()
LOCAL lcMenu, lcKey
lcMenu = THIS.oFoxcode.MenuItem
IF EMPTY( lcMenu )
*** Nothing selected, so display list
lcKey = UPPER( THIS.oFoxcode.UserTyped )
*** What sort of files do we want
DO CASE
CASE INLIST( lcKey, "MOP","DOP" )
lcFiles = '*.prg'
CASE INLIST( lcKey, "MOF", "DOF" )
lcFiles = '*.scx'
CASE INLIST( lcKey, "MOR", "DOR" )
lcFiles = '*.frx'
OTHERWISE
lcFiles = ""
ENDCASE
*** Populate the Items Array for display
This.GetItemList( lcFiles )
*** Return the Expanded item
RETURN This.AdjustCase()
The important thing to realize here is that we will actually call this script TWICE. The first time it is called is when the IntelliSense engine reacts to our defined shortcut ﴾in this case 'mop'﴿. The
script is called explicitly from the record in the FoxCode table and passed a reference to the FoxCode object as usual. Of course, at this point all we have done is to type 'mop' so the MenuItem
property on the FoxCode object will be empty and the code defined at the top of the Main() method executes.
As you can see this checks the UserTyped property on the FoxCode object to determine what shortcut was typed, sets the appropriate file type and calls the custom GetItemList() method. This
method is responsible for running an ADIR﴾﴿ to retrieve the file names and copying the list to the Items collection on the FoxCode Object. It then sets the ValueType property to "L" ﴾to display
the list﴿ and, crucially, sets the ItemScript property to point back to this self‐same ShoFile script.
The ItemScript property is used by the IntelliSense engine to determine what to do when the user selects an item from a list. The return value from this pass through the script will be the usual
result of calling a "User" type record which is to replace the shortcut with the content of the Expanded field ﴾in this case "Modify Command"﴿. In addition, the list generated by GetItemList() will be
displayed. Since the Itemscript property now points to the ShoFile script, when a selection is made from the list we get the second pass through the script but, now the FoxCode Object's
MenuItem property will have a value and so it is the second part of the Main() method which gets executed:
ELSE
*** Return the Selected item
This.oFoxCode.ValueType = "V"
RETURN lcMenu
ENDIF
ENDPROC
All this does is to re‐set the ValueType property to "V" and pass whatever was selected from the list ﴾now in the lcMenu variable﴿ back as the return value to complete the operation.
The code in the GetItemList() method is, with the exception of the very end which deals with the setting up the FoxCode object for the second pass, standard Visual FoxPro code to retrieve a list
of files from the current, and first level subdirectories:
PROCEDURE GetItemList( tcKey )
LOCAL ARRAY laFiles[1,2], laDirs[1], laJunk[1]
LOCAL lcRootDir, lnDirs, lnDCnt, lnFiles, lcNewDir, lnFound, lcCurDir, lnFCnt, lcFile
*** Save Root Directory
lcRootDir = FULLPATH( CURDIR())
*** Get sub‐directories
lnDirs = ADIR( laDirs, '*.' , 'D' )
lnFiles = 0
*** And Process them
FOR lnDCnt = 1 TO lnDirs
*** Return to root and re‐set
CD ( lcRootDir )
DIMENSION laJunk[1]
lcNewDir = UPPER( ALLTRIM( laDirs[ lnDCnt, 1 ] ))
IF lcNewDir = ".."
*** Don't go up the tree
LOOP
ENDIF
CD ( lcNewDir )
lnFound = ADIR( laJunk, lcFiles )
lcCurDir = FULLPATH( CURDIR())
*** If we didn't get any just go on
IF lnFound < 1
LOOP
ENDIF
*** Now process the files
FOR lnFCnt = 1 TO lnFound
lcFile = lcCurDir + laJunk[ lnFCnt, 1 ]
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 12/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
lcFile = lcCurDir + laJunk[ lnFCnt, 1 ]
lnFiles = ALEN( laFiles, 1 ) + 1
IF NOT EMPTY( laFiles[1] )
DIMENSION laFiles[ lnFiles, 2 ]
ELSE
lnFiles = 1
ENDIF
laFiles[ lnFiles, 1 ] = lcFile
NEXT
NEXT
*** Change back to original directory
CD (lcRootDir)
*** If we got something, display the list
IF lnFiles > 0
At this point we have a list of any files of the required type in the array "laJunk". Now we need to set up the FoxCode object to display the list and re‐call this script on selection:
THIS.oFoxcode.ValueType = "L"
THIS.oFoxcode.ItemScript = "ShoFile"
*** Copy items to temporary array
DIMENSION THIS.oFoxcode.Items[lnFiles ,2]
ACOPY(laFiles,THIS.oFoxcode.Items)
ENDIF
ENDPROC
ENDDEFINE
This is not really as complex as it looks. The entire ShoFile script record is given here:
Field Content
Type S
Abbrev Shofile
Cmd {}
Data
LPARAMETER toFoxcode
IF FILE(_CODESENSE)
LOCAL luRetVal, loFoxCodeLoader
*** Ensure we have the root class definition
SET PROCEDURE TO (_CODESENSE) ADDITIVE
*** Create an instance of our custom sub class
loFoxCodeLoader = CreateObject("FoxCodeLoader")
*** Call it's Start() method and pass the FoxCode object
luRetVal = loFoxCodeLoader.Start( toFoxCode )
*** Clean up nicely here
loFoxCodeLoader = NULL
IF ATC(_CODESENSE,SET("PROC"))#0
RELEASE PROCEDURE (_CODESENSE)
ENDIF
*** Return whatever we got back
RETURN luRetVal
ENDIF
DEFINE CLASS FoxCodeLoader as FoxCodeScript
PROCEDURE Main()
LOCAL lcMenu, lcKey
lcMenu = THIS.oFoxcode.MenuItem
IF EMPTY( lcMenu )
*** Nothing selected, so display list
lcKey = UPPER( THIS.oFoxcode.UserTyped )
*** What sort of files do we want
DO CASE
CASE INLIST( lcKey, "MOP","DOP" )
lcFiles = '*.prg'
CASE INLIST( lcKey, "MOF", "DOF" )
lcFiles = '*.scx'
CASE INLIST( lcKey, "MOR", "DOR" )
lcFiles = '*.frx'
OTHERWISE
lcFiles = ""
ENDCASE
*** Populate the Items Array for display
This.GetItemList( lcFiles )
*** Return the Expanded item
RETURN This.AdjustCase()
ELSE
*** Return the Selected item
This.oFoxCode.ValueType = "V"
RETURN lcMenu
ENDIF
ENDPROC
PROCEDURE GetItemList( tcKey )
LOCAL ARRAY laFiles[1,2], laDirs[1], laJunk[1]
LOCAL lcRootDir, lnDirs, lnDCnt, lnFiles, lcNewDir
LOCAL lnFound, lcCurDir, lnFCnt, lcFile
*** Save Root Directory
lcRootDir = FULLPATH( CURDIR())
*** Get sub‐directories
lnDirs = ADIR( laDirs, '*.' , 'D' )
lnFiles = 0
*** And Process them
FOR lnDCnt = 1 TO lnDirs
*** Return to root and re‐set
CD ( lcRootDir )
DIMENSION laJunk[1]
lcNewDir = UPPER( ALLTRIM( laDirs[ lnDCnt, 1 ] ))
IF lcNewDir = ".."
*** Don't go up the tree
LOOP
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 13/14
1/12/2016 IntelliSense in Visual FoxPro 7.0
LOOP
ENDIF
CD ( lcNewDir )
lnFound = ADIR( laJunk, lcFiles )
lcCurDir = FULLPATH( CURDIR())
*** If we didn't get any just go on
IF lnFound < 1
LOOP
ENDIF
*** Now process the files
FOR lnFCnt = 1 TO lnFound
lcFile = lcCurDir + laJunk[ lnFCnt, 1 ]
lnFiles = ALEN( laFiles, 1 ) + 1
IF NOT EMPTY( laFiles[1] )
DIMENSION laFiles[ lnFiles, 2 ]
ELSE
lnFiles = 1
ENDIF
laFiles[ lnFiles, 1 ] = lcFile
NEXT
NEXT
*** Change back to original directory
CD (lcRootDir)
*** If we got something, display the list
IF lnFiles > 0
THIS.oFoxcode.ValueType = "L"
THIS.oFoxcode.ItemScript = "ShoFile"
*** Copy items to temporary array
DIMENSION THIS.oFoxcode.Items[lnFiles ,2]
ACOPY(laFiles,THIS.oFoxcode.Items)
ENDIF
ENDPROC
ENDDEFINE
The script as given includes handling for Programs ﴾mop & dop﴿, Forms ﴾mof and dof﴿ and Reports ﴾mor and dor﴿. It is, of course, a trivial task to add additional types to this script and that is left
as an exercise for the reader.
© 2016 Microsoft
https://msdn.microsoft.com/enus/library/ms917475(d=printer).aspx 14/14