Wiki Home

IntelliSense Custom Scripts

(Updated: 2010.02.06 05:15:56 PM)
Namespace: VFP
Please post interesting/useful/fun custom scripts you've written for Intellisense ... one of Category 3 Star Topics.

Visual FoxPro's IDE is extensible au max. Take Intellisense, for example.
Contents:
Background and Example:
Code Templates
Create an IF..ENDIF structure
Produce a DO CASE.. OTHERWISE statement
Informative header
Create function or procedure body in your prg
Enhanced ability to create functions or procedures in your prg
Wait window shortcuts
Generate a SQL UPDATE statement
Create a LOCAL declaration
Commenting
Place a Comment noting date and time changed and by whom
Place a Comment noting date changed and by whom
Place a TODO comment (for Task list) including Date Placed
Code Generation
Generate Error Reporting Code
Check the parameters passed to a function
Create Excel Automation template
Produce a skeleton HTML TABLE
Intellisense Metadata Batch Updaters
Program to Add All VCX classes to Intellisense
Create a call to object Factory function
Extract all PROC/FUNC/PARAM statements into Intellisense
Extract all PROC/FUNC/PARAM statements into Intellisense including Inline Parameters
Show #DEFINEs in drop-down list
Show form parameters with DO FORM formname WITH
Drop down list of open tables
Correct typos
Table Of Contents
  • Example -- MC -> MODIFY COMMAND
  • DOC -> Produce a DO CASE.. OTHERWISE statement
  • HTTAB -> Produce a skeleton HTML TABLE
  • GENUP -> Generate a SQL UPDATE statement
  • LOCALS -> Create a LOCAL declaration
  • EXCEL -> Create Excel Automation template
  • CHG -> Place a Comment noting date changed and by whom
  • IFE -> Create an IF..ENDIF structure
  • ERR -> Generate Error Reporting Code
  • PCHK -> Check the parameters passed to a function
  • MULTI - Program to Add All VCX classes to Intellisense
  • CCD -> Place a Comment noting date and time changed and by whom
  • FACT -> Create a call to object Factory function
  • SEL -> In the command window, drop down a list of all open tables.
  • TODO -> Place a TODO comment (for Task list) including Date Placed ( changed [2006.07.11] - edyshor)
  • UDF - Program to Extract all PROC/FUNC/PARAM statements from a PRG into Intellisense
  • UDF2 - Enhanced Program to Extract all PROC/FUNC/PARAM statements from a PRG into Intellisense including Inline Parameters
  • DEFLIST -> Show #DEFINEs in drop-down list
  • FRMPARAMS -> Show form parameters with DO FORM formname WITH
  • CLIP - Expand CT to _ClipText
  • Typos - Automatically correct typos
  • Header information - Create a header for your files
  • Function / Procedure bodies - Create a function / procedure body in your prg
  • Function / Procedure creation -> Create function/proc in your code , with additional features
  • 'Wait window' shortcut - use wwn for "wait window nowait"
  • SelectAll - Generate a SELECT statement that enumerates all the field names
  • IntelliSenseCustomScripts_TableFieldList - Table and Field listing in a Program or Method window
  • How-to Create a Custom Script
    (so examples don't need to repeat this information)
    1. Open the Intellisense manager via the menu (Tools | IntelliSense Manger) or programmatically with DO FOXCODE.
    2. Select the Custom tab
    3. Enter the short cut text (such as, DC, in the out-of-the box example)
    4. Click Add
    5. Click Script
    6. Foxcode that is shown below goes in this memo field. Note that it's *just* a memo, it's not a code window.

    For examples and references other than this page, see the help topics "Customizing IntelliSense Using Foxcode" and "Foxcode Object Reference"
    TAKE NOTE!
    Particularly for those using Megafox and the InLineGetLocVars script.

    In VFP8 the values to return from a script linked in via CustomDefScripts are opposite.
    1) Where in VFP7 you RETURN, you now RETURN .F. in VFP8
    2) Where in VFP7 you RETURN .F. you now RETURN in VFP8
    THEREFORE all script examples that work in VFP7 need to be changed for VFP8.
    If you don't do that, then you'll break other parts of IntelliSense, like the expansion of keywords.
    -- Paul Maskens

    Background and Example:

    You can add a record with a "macro" shortcut that include scripts (in memo fields of this table) that will allow you to do things like:

    (you type: MC) VFP expands that to MODIFY COMMAND
    And the difference between this and CEE (besides being native) is... ?
    There is little difference. It is now native and part of the Intellisense table. There is a 'console' to manage this table that makes it easy to handle, besides of coure being able to BROWSE it and edit from the command box. As soon as we study the table's capabilities a little more we'll come up with more ideas and samples that might show some other differences.

    Code Templates


    Create an IF..ENDIF structure

    This is a simple one called IFE to create an IF..ENDIF structure
    LPARAMETER oFoxCode
    
    * 0 – Command Window
    * 1 – Program
    * 8 – Menu Snippet
    * 10 – Code Snippet
    * 12 – Stored Procedure
    IF oFoxcode.Location # 1 and oFoxcode.Location # 10
       RETURN "IFE"
    ENDIF
    
    oFoxcode.valuetype = "V"
    
    LOCAL lcWhat, lcRetval
    lcWhat = INPUTBOX("Enter IF condition","VFP","")
    
    text to lcRetVal textmerge noshow
    if < < lcWhat > >
      ~
    else
    
    endif && < < lcWhat > >
    endtext
    
    RETURN lcRetVal
    
    -- Alex Feldstein

    Produce a DO CASE.. OTHERWISE statement


    Fill in the following table fields:
    Type = "U"
    abbrev = "DOC"
    command = {}
    

    In Data Field:
    LPARAMETERS oFoxcode
    
    * 0 – Command Window
    * 1 – Program
    * 8 – Menu Snippet
    * 10 – Code Snippet
    * 12 – Stored Procedure
    IF oFoxcode.Location # 1 and oFoxcode.Location # 10
       RETURN "DOC"
    ENDIF
    
    oFoxcode.valuetype = "V"
    
    LOCAL lnCases, llOWise, lcRetval
    lnCases = VAL(INPUTBOX("Enter number of CASEs","VFP","2"))
    IF lnCases < 1
      lnCases = 2   && default
    ENDIF
    
    llOWise = MESSAGEBOX("Do you want an OTHERWISE?",4,"VFP") = 6
    
    SET TEXT ON NOSHOW
    SET TEXTMERGE TO MEMVAR lcRetVal
    
    \DO CASE
    \CASE ~
    FOR n = 1 TO lnCases - 1
    \CASE
    ENDFOR
    
    IF llOWise
    \OTHERWISE
    ENDIF
    \ENDCASE
    
    SET TEXTMERGE TO
    SET TEXTMERGE OFF
    
    RETURN lcRetVal
    

    and it places the cursor at the beginning in the first CASE ready for you to type. You could even have a dialog first that asks you how many CASE and if you want an OTHERWISE!

    RETURN oFoxCode.Abbrev instead of RETURN "DOC" avoids the need for hard coding Geoff Franklin

    Informative header

    create a header for your files
    Type = U
    abbrev = hdr
    cmd = {}
    Data
    LPARAMETERS oFoxcode
    LOCAL lcPurpose
    
    IF oFoxcode.Location = 0
       RETURN "header"
    ENDIF
    
    oFoxcode.valuetype = "V"
    
    TEXT TO lcHeader TEXTMERGE NOSHOW
    **********************************************************************
    * Program....: < < wtitle(wontop()) > >
    * Version....:
    * Author.....: YourName here
    * Date.......: < < dmy(date()) > >, < < time() > >
    * Notice.....: Copyright © < < TRANSFORM(Year(date()) ) > >, CompanyNameHere.
    *              All Rights Reserved.
    * Compiler...: < < version() > >
    * Abstract...:
    * Changes....: < < Getenv("USERNAME") > >, Created < < dmy(date()) > > / < < time() > >
    * Parameters.:
    * Called by..:
    * Purpose....: ~
    **********************************************************************
    ENDTEXT
    
    RETURN lcHeader

    Create function or procedure body in your prg

    to get function bodies filled in for you in prg files
    you need two records for this:
    record 1:
    Type="S"
    abbrev="document"
    cmd = {}
    Data
    LPARAMETER oFoxCode
    LOCAL lcParam, lcParamDescript, lcType, lnI, lcName, lcDescription, lnParameters, lcRetVal
    
    lcParam = ""
    lcParamDescript = ""
    
    * Run everywhere but not in the command window
    IF oFoxCode.Location = 0
       return "FUN"
    ENDIF
    
    * we want to return a value
    *
    oFoxcode.valuetype = "V"
    
    * read the user field in the record that called this script.
    * what do you want to create, a FUN or a PRO.
    *
    IF oFoxCode.user = "F"
       lcType = "FUNCTION"
    ELSE
       lcType = "PROCEDURE"
    ENDIF
    
    * get the name
    *
    lcName  = inputbox(lcType+" Name:", _screen.caption)
    
    * and some descriptive description
    *
    lcDescription = inputbox(lcType+" Description:",_screen.caption)
    
    * read the number of parameters
    *
    lnParameters = val( inputbox(lcType+" Number of parameters:",_screen.caption,"1") )
    
    * some code to execute when there are 1 or more parameters
    *
    IF lnParameters > 0
    
       FOR lnI = 1 to lnParameters
          * fill the lcParam variable
          *
          lcParam = lcParam+inputbox("Parameter "+alltrim(str(lnI))+" Name",_screen.caption)+", "
    
          * some descriptive text for the parameter would be nice for documenting purposes.
          *
          lcparamDescript = lcparamDescript+"Parameter "+alltrim(str(lnI))+":  "+;
          inputbox("Parameter "+alltrim(str(lnI))+" Description: ", _screen.caption) +chr(13) +chr(10) +"* "
       ENDFOR
    
       * strip off the last ", " from the parameter list
       lcParam = substr(lcParam,1, len(lcParam)-2)
    ENDIF
    
    * determine the type of return value
    *lcParamType =
    * make a block of text to return
    *
    TEXT TO lcRetVal TEXTMERGE NOSHOW
    *******************************************
    * < < lcType > > < < lcName > >( < < lcparam > > )
    * Date       :   < < DATE() > >, < < time() > >
    * author     :   < < GETENV("USERNAME") > >
    * description:  < < lcDescription > >
    ****** PARAMETERS **************
    * Parameters :  < < alltrim( str(lnParameters)) > >
    * < < lcParamDescript > >
    *******************************************
     < < lcType > > < < lcName > > ( < < lcParam > > )
       ~code goes here~
    
    END < < substr(lctype,1,4) > >
    ENDTEXT
    
    RETURN lcRetVal


    The second record
    Type = U
    abbrev = fun
    cmd = {document}
    user = F
    data
    LPARAMETER oFoxCode
    


    For procedures you simply make another record
    same as above
    make the abbrev = pro
    user = P
    typing "pro" in your prgfile will give you a full PROCEDURE body then.

    Enhanced ability to create functions or procedures in your prg

    Like the previous example, the following script allows you to create functions/procedures by prompting you for names and parameters. It also allows you to specify a return value and tries to initialize it based on the second letter of the return variable you specify.

    As with the previous example, you'll need to create two records in foxcode:
    Record 1:
    Type="S"
    abbrev="document"
    cmd = {}
    Data
    LPARAMETER oFoxCode
    oFoxCode.valuetype = "V"
    
    LOCAL lcParam, lcParamDescript, lcType, lnI, lcName, lcDescription, lnParameters, lcRetVal
    LOCAL lnAt, lcTemp, lcThisParmName, lcThisParmDescribe
    LOCAL lcRetVar, lcLOCALRetVarString, lcLocalRetVarInitString, lcRETURNstring
    
    * Run everywhere but not in the command window
    IF oFoxCode.Location = 0
    	RETURN "FUN"
    ENDIF
    
    
    * Read the user field in the record that called this script.
    * what do you want to create, a FUN or a PRO.
    *
    IF oFoxCode.USER = "F"
    	lcType = "FUNCTION"
    ELSE
    	lcType = "PROCEDURE"
    ENDIF
    
    
    lcParam = ""
    lcParamDescript = ""
    
    
    * get the name
    *
    lcName = INPUTBOX(lcType+" Name:", _SCREEN.CAPTION)
    
    * and some descriptive description
    lcDescription = INPUTBOX(lcType+" Description:",_SCREEN.CAPTION)
    
    * read the number of parameters
    lnParameters = VAL( INPUTBOX(lcType+" Number of parameters:",_SCREEN.CAPTION,"1") )
    
    
    * some code to execute when there are 1 or more parameters
    *
    IF lnParameters > 0
    
    	FOR lnI = 1 TO lnParameters
    
    		lcTemp =	INPUTBOX("Parameter "+ALLTRIM(STR(lnI))+" Name [, Description]",_SCREEN.CAPTION)
    		lnAt = AT(",",lcTemp)
    		IF lnAt > 0
    			lcThisParmName= LEFT(lcTemp,lnAt-1)
    			lcThisParmDescribe = SUBSTR(lcTemp,lnAt+1)
    		ELSE
    			lcThisParmName= ALLTRIM(lcTemp)
    			lcThisParmDescribe = ""
    		ENDIF
    
    		lcParam = lcParam+lcThisParmName+", "
    
    		lcParamDescript = lcParamDescript+CHR(9) + "Parameter "+ALLTRIM(STR(lnI))+": "+ lcThisParmName + " - "  ;
    			+ lcThisParmDescribe +CHR(13) +CHR(10) +"* "
    
    	ENDFOR
    
    * strip off the last ", " from the parameter list
    	lcParam = SUBSTR(lcParam,1, LEN(lcParam)-2)
    ENDIF && lnParameters > 0
    
    lcRetVar = INPUTBOX("RETURN varname:",_SCREEN.CAPTION)
    lcLOCALRetVarString = IIF(LEN(RTRIM(lcRetVar))>0,"LOCAL " + lcRetVar,"")
    
    
    lcLocalRetVarInitString = ""
    DO CASE
    CASE LEN(lcRetVar) >=2 AND UPPER(SUBSTR(lcRetVar,2,1)) = "C"
    	lcLocalRetVarInitString = lcRetVar + [ = "" ]
    CASE LEN(lcRetVar) >=2 AND UPPER(SUBSTR(lcRetVar,2,1)) = "D"
    	lcLocalRetVarInitString = lcRetVar + [ = {} ]
    CASE LEN(lcRetVar) >=2 AND UPPER(SUBSTR(lcRetVar,2,1)) = "T"
    	lcLocalRetVarInitString = lcRetVar + [ = {:} ]
    CASE LEN(lcRetVar) >=2 AND UPPER(SUBSTR(lcRetVar,2,1)) = "N"
    	lcLocalRetVarInitString = lcRetVar + [ = 0 ]
    CASE LEN(lcRetVar) >=2 AND UPPER(SUBSTR(lcRetVar,2,1)) = "Y"
    	lcLocalRetVarInitString = lcRetVar + [ = $0.00 ]
    CASE LEN(lcRetVar) >=2 AND UPPER(SUBSTR(lcRetVar,2,1)) = "L"
    	lcLocalRetVarInitString = lcRetVar + [ = .F. ]
    ENDCASE
    
    IF LEN(lcLocalRetVarInitString) > 0
    	lcLocalRetVarInitString = lcLocalRetVarInitString + [ ] + CHR(38) + CHR(38) + [ Initialize]
    ENDIF  && LEN(lcLocalRetVarInitString) > 0
    
    
    lcRETURNstring = IIF(LEN(RTRIM(lcRetVar))>0,"RETURN " + lcRetVar,"RETURN .T.")
    
    
    *
    TEXT TO lcRetVal TEXTMERGE NOSHOW
    
    * Note:  < <  > > brackets include spaces to avoid wiki parsing
    
    < < lcType > > < < lcName > >
    *********************************************************************************************************************************
    * Date : < < DATE() > >, < < time() > >
    * Author : < < GETENV("USERNAME") > >
    * Description: < < lcDescription > >
    * Parameters : < < alltrim(str(lnParameters)) > >
    * < < lcParamDescript > >
    *********************************************************************************************************************************
    
    LPARAMETERS < < lcParam > >
    
    < < lcLOCALRetVarString > >
    < < lcLocalRetVarInitString > >
    
    ~
    
    < < lcRETURNstring > >
    *********************************************************************************************************************************
    END< < substr(lctype,1,4) > >  < < CHR(38) > >< < CHR(38) > > < < lcName > >
    
    ENDTEXT
    
    RETURN lcRetVal
    


    If you're copying and pasting the above code, remember to get rid of the extra spaces between the brackets, which were inserted
    to prevent wiki parsing.

    The second record will call the first record you just created, when you type the letters fun while editing a .prg:
    Type = U
    abbrev = fun
    cmd = {document}
    user = F
    data
    LPARAMETER oFoxCode
    


    For procedures, simply create yet another record, very similar to the one above:

    Type = U
    abbrev = pro
    cmd = {document}
    user = F
    data
    LPARAMETER oFoxCode
    


    Dave Martin

    Wait window shortcuts

    For a wait window nowait
    type = U
    abbrev = wwn
    cmd = {}

    LPARAMETERS oFoxcode
    
    oFoxcode.valuetype = "V"
    
    TEXT TO myvar TEXTMERGE NOSHOW
    WAIT WINDOW NOWAIT "~"
    ENDTEXT
    
    RETURN myvar
    


    Boudewijn Lutgerink

    Generate a SQL UPDATE statement


    You can also do this kind of code generation to pump out SQL code:
    Fill in the following table fields:
    Type = "U"
    abbrev = "genup"
    command = {}
    

    In Data Field:
    ***********************************
    LPARAMETERS oFoxcode
    
    #DEFINE CRLF chr(13) + chr(10)
    
    
    IF oFoxcode.Location # 1
       RETURN "FC"
    ENDIF
    
    oFoxcode.valuetype = "V"
    cUpdateStr = "Update " + alias() +" set ;" + CRLF
    nfldcount =afields(afldlist)
    cfldlist = ""
    
    for idx = 1 to nfldcount
       cfldlist = cfldlist + afldlist[idx,1] + ;
         " = m." + afldlist[idx,1] + iif(idx != nfldcount, "; " + CRLF,"")
    endfor
    
    RETURN cUpdateStr + cfldlist
    ***************************

    This will take the current open table and create a SQL update command using memvars and insert it into the current cursor position in the editor.

    -- Harold Chattaway

    Create a LOCAL declaration


    Creates the 'LOCAL' declaration by scanning a codeblock for variables named using the Hungarian Notation convention.
    -- Rhodri C Evans

    *!*	Intaller - this will install the script into the intellisense table:
    DO Add2IntelliSense WITH "locals", "", Locals(), "U"
    DO Add2IntelliSense WITH "locals6", "", Locals(), "U"
    PROCEDURE Add2IntelliSense( lcAbbr, lcLong, tcCode, tcType )
    	LOCAL cTAlias, lpdwReserved,lpdwType, z
    	cTAlias = SYS(2015)
    	USE (_FOXCODE) ALIAS (cTAlias) AGAIN SHARED
    	SELECT (cTAlias)
    	LOCATE FOR UPPER(Abbrev) = UPPER(lcAbbr)
    	IF EMPTY(tcCode)
    		IF EOF() THEN
    			INSERT INTO (cTAlias) (TYPE, Abbrev, expanded, cmd, CASE, SAVE, TIMESTAMP) ;
    				VALUES ('U',UPPER(lcAbbr), lcLong,'', 'U', .F., DATETIME())
    		ELSE
    			UPDATE (cTAlias) SET TYPE = 'U',;
    				expanded=lcLong,;
    				cmd = '',;
    				CASE = 'U',;
    				SAVE = .F., ;
    				TIMESTAMP = DATETIME() ;
    				WHERE UPPER(Abbrev) = UPPER(lcAbbr)
    		ENDIF
    	ELSE
    		IF EOF() THEN
    			INSERT INTO (cTAlias) (TYPE, Abbrev, expanded, cmd, DATA, CASE, SAVE, TIMESTAMP) ;
    				VALUES (tcType,UPPER(lcAbbr), lcLong,'{}',tcCode, 'M', tcType#"S", DATETIME())
    		ELSE
    			UPDATE (cTAlias) SET TYPE = tcType,;
    				expanded=lcLong,;
    				cmd = '{}',;
    				DATA = tcCode, ;
    				CASE = 'M',;
    				SAVE = tcType#"S", ;
    				TIMESTAMP = DATETIME() ;
    				WHERE UPPER(Abbrev) = UPPER(lcAbbr)
    		ENDIF
    	ENDIF
    	USE IN (SELECT(cTAlias))
    ENDPROC
    *!*	End Installer
    
    
    
    PROCEDURE Locals
    LOCAL lcCode
    TEXT TO lcCode noshow
    *!* Troy Murphy troy@solutionsoft.cc
    *!*	Summary:
    *!*	Designed for those that use the Hungarian Notation convention in variable naming.
    *!*	Creates the 'LOCAL' declaration by scanning a codeblock.
    *!*	Considers single character variables and variables that use 'l' in hungarian notation as local variables.
    *!*	Also honors other scoping declarations (local private public).
    *!*	Constrains itself to current code block (special thanks to Greg Reichert).
    *!*	It will create the type (as) syntax which will is especially useful on object types since that will give you the intellisense to your own classes.
    *!*	Runs in VFP8 and above.
    *!*
    *!*	To Use:
    *!*	Write the code as normal using hungarian notation for variables (lcMyVar, loMyObj).
    *!*	The first two characters are key.
    *!*	When you want the local declaration, place the mouse pointer at the line you wish the declaration to go
    *!*	   and type 'locals' or 'locals6' (no quotes) followed by a space to insert the LOCAL declaration.
    *!*	Please feel free to improve or change behavior to suit your likng.
    *!*	Of course you should type locals before the variables are initially initialized.
    *!*	This is a simple script, but I find it very productive.
    
    
    lparameter oFoxCode
    * Create the 'LOCAL' declaration in a code window.
    * Requires hungarian notation for variable types.  Only supports local types (l).
    * Be careful on code that contains multiple procedure files....
    * Troy Murphy - 6/21/05 modified to make object types match definition for intellisense.
    * Troy Murphy - 6/23/05
    *	will now treat single character variables as locals.
    *	Support for 'store .. to ..'
    *	Support for 'for' and 'for each'
    *	Support for arrays (dimension statements of la.. arrays are declared [1])
    * Troy Murphy - 6/24/05
    *	Will now support 'into array' and 'scatter name'
    *	Defaults to sorted (ascending)
    *	Will now ignore variables already declared in other 'local' directives.
    * Greg Reichert - 8/30/2006
    *   Now supports to exclude variable currently declared as Private, Public, Dimension, and Parameter statements.
    *   Only see variables in current Procedure block.
    *   IF LOCALS6 is typed, the Local statement is VFP 6.0 compatible (No AS statement prefixed).
    * Greg Reichert - 8/31/2006
    *	Fixed LOCAL ARRAY declaration
    *	Fixed when in non-procedure block.  search ends at first Procedure or Define class block is encountered.
    * Troy Murphy - 8/31/2006
    *	Support for inline array functions (finds the array parameter and localizes that if necessary).
    *	Support for 'DO FORM' - will honor 'linked' and 'to' keywords.
    *	De-Commisioned the 'LOCAL ARRAY Declaration' since arrays appear as: "local loMyArray[1]" which will also initialize the array.
    *   Removed the ignoring of lines with 'DIMENSION' statements to support the local array convention.
    * Rhodri Evans - 1/23/2009
    *	Support for statements that are split across multiple lines.
    *	Check no duplicates in output.
    *	Support for TEXT TO ...
    *	Support for 'DO FORM' WITH clause
    *	Additional Array functions
    * SYS(2030,1)	&& Uncomment this line to allow debugging.
    local i, lcCurrentLocals as string, lcExpression as string, lcInLineCommentString as string, lcObjClassLib as string, lcObjType as string, lcParse as string, lcResult as string, lcSourceLine as string, lcSuffix as string, lcText as string, lcTmp as string, lcVar as string, lcVartype as string, llSorted as Boolean, lnCnt as number, lnHandle as number, lnLoop as number, lnResult as number, lnResultDim as number, lnSelEnd as number, lnSelStart as number, lnWords as number, loObjTypes as 'collection'
    local laText[1], laResult[1], laParameters[1], laEnv[25], llLoop as Boolean, lnDup as Number, lnLoopStart as Number
    llSorted=.F.	&& Do you want the locals list sorted?
    lcResult="LOCAL "
    set library to (home()+"FoxTools.fll") additive
    lnHandle=_wontop()
    if vartype(lnHandle)='N'
    	lcInLineCommentString=replicate(chr(28),2)
    	lnResult=_EdGetEnv(lnHandle,@laEnv)
    	lnSelStart=0
    	lnSelEnd=laEnv[2]
    	lcText=_EdGetStr(lnHandle,lnSelStart,lnSelEnd-1)
    	lcResult="LOCAL "
    	lnResultDim=0
    	lnCnt=alines(laText,lcText)
    	lcCurrentLocals=''
    	loObjTypes=createobject('collection')
    
    	*2.00.00.00913-4-----------------------------------
    	*  Locate current position, scan to beginning of procedure
    	*--------------------------------------------------
    
    	LOCAL llAsVersion6
    	llAsVersion6 = .F. 	
    	for lnLoop=1 to alen(laText,1)
    		* Add SourceLine to previous if continuation line
    		lcSourceLine=IIF(llLoop,lcSourceLine,'')+ALLTRIM(chrtran(laText[lnLoop],chr(9)+chr(10)+chr(13),""))
    		IF RIGHT(lcSourceLine,1)=';'
    			* statement split across multiple lines	
    			lcSourceLine = STUFF(lcSourceLine,LEN(lcSourceLine),1,' ') && remove continuation mark
    			lnLoopStart = lnLoop
    			llLoop = .T.
    			LOOP
    		ELSE
    			* Statement finishes on this line
    			IF !llLoop
    				lnLoopStart = lnLoop
    			ENDIF
    			llLoop = .F.
    		ENDIF
    		IF LEFT(ALLTRIM(UPPER(lcSourceLine)),6)=="LOCALS"
    			llAsVersion6 = (ALLTRIM(UPPER(lcSourceLine))=="LOCALS6")		&& make compatible with VFP 6.0
    			*--------------------------------------------------
    			*  find the beginning of the block
    			*--------------------------------------------------
    			FOR lnLoop=lnLoop TO 1 STEP -1
    				* Add SourceLine to previous if continuation line
    				lcSourceLine=IIF(llLoop,lcSourceLine,'')+ALLTRIM(chrtran(laText[lnLoop],chr(9)+chr(10)+chr(13),""))
    				IF RIGHT(lcSourceLine,1)=';'
    					* statement split across multiple lines	
    					lcSourceLine = STUFF(lcSourceLine,LEN(lcSourceLine),1,' ') && remove continuation mark
    					lnLoopStart = lnLoop
    					llLoop = .T.
    					LOOP
    				ELSE
    					* Statement finishes on this line
    					IF !llLoop
    						lnLoopStart = lnLoop
    					ENDIF
    					llLoop = .F.
    				ENDIF
    				lcSourceLine = IIF(LOWER(LEFT(lcSourceLine,7))=="hidden ",ALLTRIM(SUBSTR(lcSourceLine,8)), lcSourceLine)
    				lcSourceLine = IIF(LOWER(LEFT(lcSourceLine,10))=="protected ",ALLTRIM(SUBSTR(lcSourceLine,11)), lcSourceLine)
    				
    				DO CASE
    				CASE LOWER(LEFT(lcSourceLine,10))=="procedure "
    					EXIT
    				CASE LOWER(LEFT(lcSourceLine,9))=="function "
    					EXIT
    				ENDCASE
    			NEXT
    			
    			EXIT
    		ENDIF
    	NEXT
    
    	*--------------------------------------------------
    	*  remember the start end end positions
    	*--------------------------------------------------
    	LOCAL lnStartLoop, lnEndLoop
    	lnStartLoop = MAX(1,lnLoopStart)
    	lnEndLoop	= alen(laText,1)
    		
    	* Find the existing local declarations
    	for lnLoop=lnStartLoop to alen(laText,1)
    		* Add SourceLine to previous if continuation line
    		lcSourceLine=IIF(llLoop,lcSourceLine,'')+ALLTRIM(chrtran(laText[lnLoop],chr(9)+chr(10)+chr(13),""))
    		IF RIGHT(lcSourceLine,1)=';'
    			* statement split across multiple lines	
    			lcSourceLine = STUFF(lcSourceLine,LEN(lcSourceLine),1,' ') && remove continuation mark
    			llLoop = .T.
    			LOOP
    		ELSE
    			* Statement finishes on this line
    			llLoop = .F.
    		ENDIF
    		lcSourceLine = IIF(LOWER(LEFT(lcSourceLine,7))=="hidden ",ALLTRIM(SUBSTR(lcSourceLine,8)), lcSourceLine)
    		lcSourceLine = IIF(LOWER(LEFT(lcSourceLine,10))=="protected ",ALLTRIM(SUBSTR(lcSourceLine,11)), lcSourceLine)
    
    		*--------------------------------------------------
    		*  GLR 8-29-2006 : add Private, Public, and Dimension to the list of possible declaration statements
    		*--------------------------------------------------
    		*-- GLR 8/31/2006 : added 'Define' to list
    		*-- THM 8/31/2006 : removed 'dimens' from list.
    		if INLIST(left(lower(lcSourceLine),6),'local ','privat','public', 'parame', 'lparam', ;
    											'proced', 'functi', 'endpro', 'endfun', 'define')
    			DO CASE
    			*--------------------------------------------------
    			*  standard declarations
    			*--------------------------------------------------
    			CASE left(lower(lcSourceLine),6)=='local '
    				lcSourceLine=substr(lcSourceLine,6)
    			CASE left(lower(lcSourceLine),6)=='public'
    				lcSourceLine=substr(lcSourceLine,6)
    			CASE left(lower(lcSourceLine),8)=='private '
    				lcSourceLine=substr(lcSourceLine,8)
    			CASE left(lower(lcSourceLine),10)=='dimension '
    				lcSourceLine=substr(lcSourceLine,10)
    			CASE left(lower(lcSourceLine),10)=='parameter '
    				lcSourceLine=substr(lcSourceLine,10)
    			CASE left(lower(lcSourceLine),11)=='lparameter '
    				lcSourceLine=substr(lcSourceLine,11)
    
    			*--------------------------------------------------
    			*  if the start is th top of the page, then look for start of procedure or class
    			*--------------------------------------------------
    			*-- GLR 8/31/2006 : Will stop search when new block in encountered.
    			CASE left(lower(lcSourceLine),10)=='procedure ' and lnStartLoop==1
     				lnEndLoop = lnLoop
     				EXIT
    			CASE left(lower(lcSourceLine), 9)=='function ' and lnStartLoop==1
     				lnEndLoop = lnLoop
     				EXIT
    			CASE left(lower(lcSourceLine),13)=='define class ' and lnStartLoop==1
     				lnEndLoop = lnLoop
     				EXIT
    
    			*--------------------------------------------------
    			*  if start procedure, get arguments
    			*--------------------------------------------------
    			CASE left(lower(lcSourceLine),10)=='procedure '
    				IF "("$lcSourceLine
    	 				lcSourceLine=STRTRAN(substr(lcSourceLine, AT("(",lcSourceLine)+1),")","") 				
    				ELSE
    					LOOP
    				ENDIF
    			CASE left(lower(lcSourceLine),9)=='function '
     				IF "("$lcSourceLine
    	 				lcSourceLine=STRTRAN(substr(lcSourceLine, AT("(",lcSourceLine)+1),")","") 				
     				ELSE
     					LOOP
     				ENDIF
     			
     			*--------------------------------------------------
     			*  if End of block, remember and exit loop
     			*--------------------------------------------------
     			CASE left(lower(lcSourceLine),7)=='endproc'
     				*-- end of procedure
     				lnEndLoop = lnLoop
     				EXIT
     			CASE left(lower(lcSourceLine),7)=='endfunc'
     				*-- end of function
     				lnEndLoop = lnLoop
     				EXIT
    			ENDCASE 			
    			
    			*--------------------------------------------------
    			*  Parse declarations
    			*--------------------------------------------------
    			for i = 1 to getwordcount(lcSourceLine,',')
    				lcTmp=alltrim(getwordnum(lcSourceLine,i,','))
    				if ' as '$lower(lcTmp)
    					lcTmp=alltrim(left(lcTmp,at(' ',lcTmp)))
    				endif
    				if '['$lcTmp
    					lcTmp=alltrim(left(lcTmp,at('[',lcTmp)-1))
    				endif
    				if '('$lcTmp
    					lcTmp=alltrim(left(lcTmp,at('(',lcTmp)-1))
    				endif
    				if !lower(alltrim(lcTmp)+',')$lower(alltrim(lcCurrentLocals)+',')
    					lcCurrentLocals = lcCurrentLocals + lcTmp +','
    				endif
    			endfor
    		endif
    	endfor
    	
    	*--------------------------------------------------
    	*  using start and end points
    	*--------------------------------------------------
    	for lnLoop=lnStartLoop to lnEndLoop
    		* Add SourceLine to previous if continuation line
    		lcSourceLine=IIF(llLoop,lcSourceLine,'')+ALLTRIM(chrtran(laText[lnLoop],chr(9)+chr(10)+chr(13),""))
    		IF RIGHT(lcSourceLine,1)=';'
    			* statement split across multiple lines	
    			lcSourceLine = STUFF(lcSourceLine,LEN(lcSourceLine),1,' ') && remove continuation mark
    			llLoop = .T.
    			LOOP
    		ELSE
    			* Statement finishes on this line
    			llLoop = .F.
    		ENDIF
    		lcParse=''
    		do case
    			case left(lower(lcSourceLine),6)='store ' and ' to '$lower(lcSourceLine)
    				lcParse=substr(lcSourceLine,rat([ to ],lower(lcSourceLine))+4)
    			case  left(lower(lcSourceLine),10)='dimension '
    				lcParse=strtran(lcSourceLine,'dimension ','',1,1,3)
    				if [(]$lcParse
    					lcParse=left(lcParse,at('(',lcParse)-1)+'[1]'
    				else
    					if "["$lcParse
    						lcParse=left(lcParse,at('[',lcParse)-1)+'[1]'
    					endif
    				endif
    			case 'select '$lower(lcSourceLine) and ' into array '$lower(lcSourceLine)
    				lcParse=substr(lcSourceLine,rat(' into array ',lower(lcSourceLine))+12)
    				if lcInLineCommentString $ lcParse
    					lcParse=left(lcParse,at(lcInLineCommentString,lcParse))
    				endif
    				lcParse=lcParse+'[1]'
    			case lower(left(lcSourceLine,8))='scatter' and ' name '$lower(lcSourceLine)
    				lcParse=strextract(lcSourceLine,' name ',' ',1,1+2)
    			case lower(left(lcSourceLine,9))='for each '
    				lcParse=strextract(lcSourceLine,'for each ',' in ',1,3)
    			case lower(left(lcSourceLine,4))='for '
    				lcParse=strextract(lcSourceLine,'for ','=',1,3)
    			CASE LEFT(LOWER(lcSourceLine),8)='text to '
    				lcParse=GETWORDNUM(lcSourceLine,3)
    			case [=]$lcSourceLine
    				lcParse=getwordnum(lcSourceLine,1,"=")
    		endcase
    		if !empty(lcParse)
    			if lcInLineCommentString$lcParse
    				lcParse=left(lcParse,at(lcInLineCommentString,lcParse))
    			endif
    			lnWords=getwordcount(lcParse)
    			if lnWords=1 and !inlist(left(lcParse,1),"'",'"','*')
    				lcExpression=alltrim(substr(lcSourceLine,at("=",lcSourceLine)+1))
    				lcVar=getwordnum(lcParse,1)
    				if ascan(laResult,lcVar,-1,-1,-1,7)=0 and atc(".",lcVar)=0 and (atc("[",lcVar)=0 or left(lower(lcVar),2)='la') and (upper(left(lcVar,1))="L" or len(alltrim(lcVar))=1)
    					lcTmp=lcVar	&& Search for array declarations
    					if '['$lcTmp
    						lcTmp=alltrim(left(lcTmp,at('[',lcTmp)-1))
    					endif
    					if '('$lcTmp
    						lcTmp=alltrim(left(lcTmp,at('(',lcTmp)-1))
    					ENDIF
    					if !lower(alltrim(lcTmp)+',')$lower(alltrim(lcCurrentLocals)+',')
    						IF VARTYPE(laResult[ALEN(laResult,1)])='C'
    							DIMENSION laResult[ALEN(laResult,1)+1]
    						ENDIF
    						laResult[ALEN(laResult,1)]=lcVar
    						LOCAL llCreateObject
    						if inlist(lower(left(lcExpression,9)),'newobject','createobj')
    							llCreateObject = (lower(left(lcExpression,9))=='createobj')
    							lcExpression=alltrim(strextract(lcExpression,[(]))
    							if right(lcExpression,1)=[)]
    								lcExpression=left(lcExpression,len(lcExpression)-1)
    							endif
    							lcObjType=getwordnum(lcExpression,1,[,])
    							lcObjClassLib=getwordnum(lcExpression,2,[,])
    							if empty(lcObjClassLib) OR llCreateObject
    								loObjTypes.add(lcObjType,lcVar)
    							else
    								loObjTypes.add(lcObjType+[ OF ]+lcObjClassLib,lcVar)
    							endif
    						endif
    					endif
    				endif
    			endif
    		ENDIF
    		
    		*-- THM 08/31/2006 - Support for inline array functions.		
    		*Support for array functions - lines may contain more than one of these....
    		*-- RCE 01/23/2009 - respect Current Locals
    		LOCAL lcArrayParse, lcArrayLine, llContinue, lcArrayFunctions, lcLine, i
    		lcArrayFunctions='aclass,acopy,adatabases,adbobjects,adir,adel,adlls,adockstate,aelement,aerror,aevents,afields,afont,agetclass,agetfileversion,ains,ainstance,alanguage,alen,alines,amembers,amouseobj,anetresources,aprinters,aprocinfo,ascan,aselobj,asessions,asort,'+'asqlhandles,astackinfo,asubscript,ataginfo,aused,avcxclasses'
    		lcArrayParse=''
    		lcArrayLine=CHRTRAN(lcSourceLine,' ','')
    		if lcInLineCommentString$lcArrayLine
    			lcArrayLine=left(lcArrayLine,at(lcInLineCommentString,lcArrayLine))
    		ENDIF
    		FOR i = 1 TO GETWORDCOUNT(lcArrayFunctions,',')
    			lcArrayFunc=GETWORDNUM(lcArrayFunctions,i,',')
    			IF lcArrayFunc+'('$LOWER(lcArrayLine)
    				lcLine=SUBSTR(lcArrayLine,AT(lcArrayFunc+'(',LOWER(lcArrayLine)))
    				lcArrayName=STREXTRACT(lcLine,'(',IIF(AT(',',lcLine)=0 or AT(',',lcLine)>AT(')',lcLine),')',','))
    				IF !EMPTY(lcArrayName) AND !lower(alltrim(lcArrayName)+',')$lower(alltrim(lcCurrentLocals)+',')
    					IF VARTYPE(laResult[ALEN(laResult,1)])='C'
    						DIMENSION laResult[ALEN(laResult,1)+1]
    					ENDIF
    					laResult[ALEN(laResult,1)]=lcArrayName+'[1]'
    				ENDIF
    			ENDIF
    		ENDFOR
    		
    		*-- THM 08/31/06 - Support for 'DO FORM'
    		*-- RCE 01/23/09 - Support for WITH clause; respect Current Locals
    		if left(lower(lcSourceLine),8)='do form '
    			lcSourceLine=STRTRAN(lcSourceLine,[ noread],[],-1,-1,1)
    			lcSourceLine=STRTRAN(lcSourceLine,[ noshow],[],-1,-1,1)
    			IF ' to '$lower(lcSourceLine)
    				lcTmp=substr(lcSourceLine,rat([ to ],lower(lcSourceLine))+4)
    				if !lower(alltrim(lcTmp)+',')$lower(alltrim(lcCurrentLocals)+',')
    					IF VARTYPE(laResult[ALEN(laResult,1)])='C'
    						DIMENSION laResult[ALEN(laResult,1)+1]
    					ENDIF
    					laResult[ALEN(laResult,1)]=lcTmp
    				ENDIF
    			ENDIF
    			IF ' linked '$LOWER(lcSourceLine)
    				lcTmp=ALLTRIM(STREXTRACT(LOWER(lcSourceLine),' name ',' linked '))
    				if !lower(alltrim(lcTmp)+',')$lower(alltrim(lcCurrentLocals)+',')
    					IF VARTYPE(laResult[ALEN(laResult,1)])='C'
    						DIMENSION laResult[ALEN(laResult,1)+1]
    					ENDIF
    					laResult[ALEN(laResult,1)]=SUBSTR(lcSourceLine,AT(LOWER(lcTmp),LOWER(lcSourceLine)),LEN(lcTmp))+IIF(llAsVersion6,'',[ AS Form])
    				ENDIF
    			ENDIF
    			IF ' with '$LOWER(lcSourceLine)
    				lcParse=ALLTRIM(STREXTRACT(lcSourceLine,[ with ],[ to ],1,1+2))
    				FOR i = 1 TO GETWORDCOUNT(lcParse,[,])
    					lcTmp=ALLTRIM(GETWORDNUM(lcParse,i,[,]))
    					IF ISALPHA(lcTmp) AND !lower(alltrim(lcTmp)+',')$lower(alltrim(lcCurrentLocals)+',') ;
    							AND EMPTY(CHRTRAN(LOWER(lcTmp),'abcdefghijklmnopqrstuvwyxz1234567890[]',''))
    						
    						IF VARTYPE(laResult[ALEN(laResult,1)])='C'
    							DIMENSION laResult[ALEN(laResult,1)+1]
    						ENDIF
    						laResult[ALEN(laResult,1)]=lcTmp						
    					ENDIF
    				ENDFOR
    			ENDIF
    		ENDIF
    	ENDFOR
    	
    	if vartype(laResult[1])="C"
    		if llSorted
    			=asort(laResult,-1,-1,-1,1)
    		ENDIF
    		for lnLoop=1 to alen(laResult,1)
    			*-- RCE 01/23/09 - No duplicates
    			IF lnLoop>1
    				lnDup = ASCAN(laResult,laResult[lnLoop],1,lnLoop-1,1,1+4)
    				IF lnDup > 0 AND LOWER(GETWORDNUM(laResult[lnLoop],1)) = LOWER(GETWORDNUM(laResult[lnDup],1))
    					LOOP
    				ENDIF
    			ENDIF
    			IF "."$laResult[lnLoop]
    				* Properties
    				LOOP
    			ENDIF
    			lcVartype=upper(substr(laResult[lnLoop],2,1))
    			lcSuffix = ""
    			do case
    				CASE llAsVersion6			&& no AS clause is added
    				CASE [ AS ]$UPPER(laResult[lnLoop])	&& AS clause alredy present
    				case lcVartype="N"
    					lcSuffix = " AS Number"
    				case lcVartype="I"
    					lcSuffix = " AS Integer"
    				case lcVartype="O"
    					try
    						if empty(loObjTypes(laResult[lnLoop]))
    							lcSuffix = " AS Object"
    						else
    							lcSuffix = " AS "+loObjTypes(laResult[lnLoop])
    						endif
    					catch
    						lcSuffix = " AS Object"
    					endtry
    				case lcVartype="L"
    					lcSuffix = " AS Boolean"
    				case lcVartype="C"
    					lcSuffix = " AS String"
    				case lcVartype="U"
    					lcSuffix = " AS Variant"
    				case lcVartype="D"
    					lcSuffix = " AS Date"
    				case lcVartype="T"
    					lcSuffix = " AS DateTime"
    				case lcVartype="Y"
    					lcSuffix = " AS Currency"
    				otherwise
    					lcSuffix = ""
    			ENDCASE
    			if lnLoop>1
    				lcResult=lcResult+", "
    			ENDIF
    			lcResult=lcResult+laResult[lnLoop]+lcSuffix
    		endfor
    	else
    		lcResult=""
    	endif
    	oFoxCode.valuetype="V"
    endif
    return lcResult
    ENDTEXT
    RETURN lcCode
    ENDPROC


    Commenting


    Place a Comment noting date and time changed and by whom


    Like Dan Jurden's example, the following script inserts a comment in the format I like to use with the big difference that I fill in the date and time. Also, I often comment a line of code that I'm about to change. If I type the shortcut (CCD, in my case) at the beginning of a line of code, it will be commented. Next thing I'll do is write a command that copies the line, since I'm often modifying the line.

    LPARAMETERS oFoxcode
    
    IF oFoxcode.Location = 0
       RETURN "CCD"
    ENDIF
    
    oFoxcode.valuetype = "V"
    
    LOCAL myVar
    TEXT TO myVar TEXTMERGE NOSHOW
    *!* < < DATETIME() > > nf 2: ~
    *!*
    ENDTEXT
    
    RETURN myvar
    


    Note. Do not included the extra spaces around the angle brackets in the DATETIME() call.

    -- Nancy Folsom

    Place a Comment noting date changed and by whom


    I have added two new custom commands to my Intellisense table: COM and CHG.
    COM expands to "* DJ "
    CHG expands to "* Changed by DJ on "

    This can be accomplished by adding a custom entry on the Custom tab and entering something like this in the script window (foxcode.data):
    LPARAMETER oFoxCode
    local lcRetVal, lcDate
    
    oFoxcode.valuetype = "V"
    lcDate = dtos(date())
    lcDate = left(lcDate, 4) + '/' + substr(lcDate,5,2) + '/' + right(lcDate,2)
    lcRetVal = "* DJ " + lcDate + " "
    
    return lcRetVal
    -- Dan Jurden

    Place a TODO comment (for Task list) including Date Placed


    Since tasks are associated with a file (PRG) and line number, I find having comments in my code that I can parse to helpful in addition to using the tasks list. So, my "ToDo" script does the following:
    LPARAMETERS oFoxcode
    
    IF oFoxcode.Location = 0
       RETURN "TODO"
    ENDIF
    
    oFoxcode.valuetype = "V"
    
    LOCAL myVar
    KEYBOARD "{ALT+F2}"    && <- i think you want it here
    TEXT TO myVar TEXTMERGE NOSHOW
    KEYBOARD "{ALT+F2}"
    *!* < < DATE() > > nf TODO: ~
    ENDTEXT
    
    RETURN myvar
    -- Nancy Folsom

    Code Generation


    Generate Error Reporting Code


    I use this entry to facilitate genereating error messages:
    Command: ERR
    Script:
    LPARAMETER oFoxCode
    LOCAL lsAns as String
    oFoxCode.ValueType = 'V'
    
    lsAns = INPUTBOX("Enter Error Message")
    
    IF EMPTY(lsAns)
      RETURN ""
    ELSE
      lsAns = 'MESSAGEBOX("ERROR: ' + lsAns + '", 16, "Error")' + CHR(13) + "~"
      RETURN lsAns
    ENDIF
    

    Check the parameters passed to a function



    And this one to check parameters to a function
    Command: pchk
    Script:
    LPARAMETER oFoxCode
    
    oFoxCode.ValueType = 'V'
    
    lcAns = INPUTBOX("Parameter To Check", "Parameter Check")
    IF EMPTY(lcAns)
      RETURN .F.
    ENDIF
    lcType = INPUTBOX("Proper Type", "Parameter Check")
    IF EMPTY(lcType)
      RETURN .F.
    ENDIF
    
    
    lcRet = "IF VARTYPE(" + lcAns + ") != '" + lcType + "'"
    
    IF lcType == 'C'
      lcRet = lcRet + " OR EMPTY(" + lcAns + ")"
    ENDIF
    
    lcRet = lcRet + Chr(13)
    lcRet = lcRet + '    MESSAGEBOX(' +;
       '"ERROR: Invalid Parameter Type for parameter ' + lcAns + '" + CHR(13) +;' + Chr(13) +;
       '               "    Expected Type: ' + lcType + '" + CHR(13) + ;' + CHR(13) + ;
       '               "    Type Received: " + VarType(' + lcAns + ') + CHR(13) ;' + CHR(13) +;
       '               , 16, "Error")'
    lcRet = lcRet + Chr(13)
    lcRet = lcRet + "    RETURN .F."
    lcRet = lcRet + Chr(13)
    lcRet = lcRet + "ENDIF"
    lcRet = lcRet + Chr(13)
    
    RETURN lcRet
    
    -- Louis Zelus

    Create Excel Automation template


    Here's one to create an Excel Automation template to get your code started
    Fill in the following table fields:
    Type = "U"
    abbrev = "excel"
    command = {}
    

    In Data Field:
    ***********************************
    LPARAMETERS oFoxcode
    
    * 0 – Command Window
    * 1 – Program
    * 8 – Menu Snippet
    * 10 – Code Snippet
    * 12 – Stored Procedure
    
    oFoxcode.valuetype = "V"
    
    IF oFoxcode.Location = 0
      * if in command window
       RETURN [loExcel = CREATEOBJECT("Excel.Application")]
    ENDIF
    
    IF oFoxcode.Location # 1 and oFoxcode.Location # 10
      * if not in a PRG or a Method
       RETURN "Excel"
    ENDIF
    
    #define CRLF CHR(13)+CHR(10)
    LOCAL lcRetVal
    
    lcRetVal = ;
    [LOCAL loExcel, loWorkBook, loRange] + CRLF + ;
    [loExcel = CREATEOBJECT("Excel.Application")] + CRLF + ;
    [loExcel.SheetsInNewWorkbook = 1] + CRLF + ;
    [loExcel.DisplayAlerts = .F.] + CRLF + ;
    [loWorkBook = loExcel.Workbooks.Add()] + CRLF + ;
    [loExcel.Visible = .t.] + CRLF + ;
    [*loRange = loExcel.Range("A1:A5")] + CRLF + ;
    [*] + CRLF + ;
    [*loWorkBook.SaveAs(cFilename)] + CRLF + ;
    [*loExcel.Quit]
    
    RETURN lcRetVal
    


    -- Alex Feldstein

    Produce a skeleton HTML TABLE


    or you could run a script to create an HTML TABLE (if you do web code) that has a dialog asking you how many rows and columns, and whatever other properties you can dream of that creates something like:
    * (brackets include spaces to avoid wiki parsing) 
    
    Response.Write([< TABLE WIDTH="100%" align="LEFT" >] + CRLF)
    Response.Write([< TR >] + CRLF)
    Response.Write([< TD >] + CRLF)
    Response.Write([< /TD >] + CRLF)
    Response.Write([< TD >] + CRLF)
    Response.Write([< /TD >] + CRLF)
    Response.Write([< /TR >] + CRLF)
    Response.Write([< /TABLE >] + CRLF)
    

    and other cool time-savers.
    -- Alex Feldstein

    Intellisense Metadata Batch Updaters


    Program to Add All VCX classes to Intellisense


    Does any one else find it annoying that you can only add classes one at a time via the Intellisense manager? (No? Oh well, just me then.)
    This code will add all the classes in all the class libraries in the current project. -- Rhodri CEvans
    NB This isn't a custom script, it's a stand alone program! (Hey, I didn't put it on this page somebody moved it here.)

    I refactored this page from a jumble of issues in the various Intellisense topics. So, if it doesn't really belong under intellisense, and I can see the point, what would be a better way to organize it? Feel free. *g* -- Nancy Folsom

    * add the Microsoft [[Visual FoxPro]] 7.0 Type Library (manually, why isn't it in there anyway?)
    Local o As VisualFoxpro.IFoxPrjFile
    Use (_Foxcode) Alias Foxcode Shared In 0
    * loop thru project's files
    For Each o In _vfp.Projects(1).Files
      If Lower(Justext(o.Name))='vcx'
        * class library
        Use (Alltrim(o.Name)) Alias _vcx In 0
        * get classes
        Select 'T' As Type, Padr(objname+[ OF "]+Alltrim(o.Name)+["],100) As Data, ;
          .T. As Save, Datetime() As Timestamp ;
          FROM _vcx ;
          INTO Cursor Query ;
          WHERE Empty(Parent) And !Empty(Class) And !Deleted()
        Use In _vcx
        Scan
          Select Foxcode
          * check we haven't already got it
          Locate For Data = Query.Data
          If !Found()
            Insert Into Foxcode (Type, Data, Save, Timestamp) ;
              VALUES (Query.Type, Query.Data, Query.Save, Query.Timestamp)
          Endif
          Select Query
        Endscan
      Endif
    Endfor
    Use In Foxcode

    Create a call to object Factory function


    If you use a data-driven Factory() function to create your objects, you can add that function to the IntelliSense Manager and a script that lists the available classes. The script might look something like:

    LPARAMETER oFoxCode
    
    oFoxCode.Valuetype = "L"
    
    Select FacName, FacDesc from Factory where ISense = .t. into array oFoxCode.Items
    Use in Factory
    
    Return AllTrim(oFoxcode.Expanded)+'("~")'
    

    When you type Factory(, you'll be presented with a list of class names. Once selected that name will automatically be surrounded with quotes. You could do something similar with various Strategy types as well. -- Joel Leach
    In VS.Net, a 'TODO comment automatically shows up in the task list. If you want to do something similar in VFP, add a "TODO" command, replace with "*TODO:" (or whatever you want), and use the following simple script:
    LPARAMETER oFoxCode
    
    oFoxCode.ValueType="V"
    KEYBOARD "{ALT+F2}"
    
    Return AllTrim(oFoxCode.Expanded)
    

    Of course, you could always just press Alt + F2. :o) -- Joel Leach

    Extract all PROC/FUNC/PARAM statements into Intellisense


    This is another stand alone program. This will read a specified prg file and extract the procedure and parameter information to the IntelliSense foxcode table. NB It doesn't work on prg's without a procedure / function statment.
    LPARAMETERS tcPrg
    LOCAL laProcInfo, lcPrgStr, laLines, lnCount, lcLine, lcParams, llContinuation
    DIMENSION laProcInfo(1), laLines(1)
    IF VARTYPE(tcPrg)#'C' OR !FILE(tcPrg)
      RETURN
    ENDIF
    USE (_FOXCODE) SHARED ALIAS foxcode
    APROCINFO(laProcInfo,tcPrg)
    lcPrgStr = STRTRAN(FILETOSTR(tcPrg),CHR(9))
    ALINES(laLines,lcPrgStr)
    lnCount = ALEN(laProcInfo,1)
    FOR i = 1 TO lnCount
      IF laProcInfo(i,3)='Procedure' AND !'.'$laProcInfo(i,1)
        lcParams = ''
        llContinuation = .F.
        * Stand alone procedure, not part of a class
        FOR j = laProcInfo[i,2]+1 TO IIF(i!=lnCount,laProcInfo[i+1,2]-1,ALEN(laLines,1))
          lcLine = ALLTRIM(laLines(j))
          * remove inline comments
          IF '&'+'&'$lcLine
            lcLine = RTRIM(LEFT(lcLine,AT('&'+'&',lcLine)-1))
          ENDIF
          DO CASE
          CASE LEFT(lcLine,1)='*' OR EMPTY(lcLine)
            * comment line
            LOOP
          CASE 'param'$LOWER(lcLine)
            * line contains 'param'
            lcParams = SUBSTR(lcLine,AT(' ',lcLine)+1) && remove parameter keyword
          CASE llContinuation
            * continuation
            lcParams = lcParams + lcLine
          OTHERWISE
            * other code reached - parameters are always before other code, so exit loop
            EXIT
          ENDCASE
          IF RIGHT(lcLine,1)#';'
            * no continuation mark - exit loop
            EXIT
          ELSE
            * strip out continuation mark
            lcParams = SUBSTR(lcParams,1,LEN(lcParams)-1)
            * set continuation flag
            llContinuation = .T.
          ENDIF
        ENDFOR
        LOCATE FOR ALLTRIM(expanded) == ALLTRIM(laProcInfo(i,1)) AND type = 'F'
        IF FOUND()
          REPLACE expanded WITH laProcInfo(i,1), tip WITH lcParams, timestamp WITH DATETIME()
        ELSE
          INSERT INTO foxcode (type,expanded,tip,case,timestamp) VALUES ('F',laProcInfo(i,1),lcParams,'M',DATETIME())
        ENDIF
      ENDIF
    ENDFOR
    USE IN foxcode
    
    -- Rhodri CEvans

    Extract all PROC/FUNC/PARAM statements into Intellisense including Inline Parameters


    With gratitude to Mr. Evans for his elegant and well written solution above, I have enhanced it to handle in-line parameters like
    FUNCTION FuncName(tcParam1, tnParam2, tlParam3)

    LPARAMETERS tcPrg
    LOCAL laProcInfo, lcPrgStr, laLines, lnCount, lcLine, lcParams, llContinuation
    DIMENSION laProcInfo(1), laLines(1)
    IF VARTYPE(tcPrg)#'C' OR !FILE(tcPrg)
      RETURN
    ENDIF
    USE (_FOXCODE) SHARED ALIAS FOXCODE
    APROCINFO(laProcInfo,tcPrg)
    lcPrgStr = STRTRAN(FILETOSTR(tcPrg),CHR(9))
    ALINES(laLines,lcPrgStr)
    lnCount = ALEN(laProcInfo,1)
    FOR i = 1 TO lnCount
      IF laProcInfo(i,3)='Procedure' AND !'.'$laProcInfo(i,1)
        lcParams = ''
        llContinuation = .F.
        * Stand alone procedure, not part of a class
        lcLine = ALLTRIM(laLines[laProcinfo[i,2]])
        lcProcName = ALLTRIM(laProcInfo[i,1])
        IF '(' $ lcLine && In line parameters.
          lcParams = SUBSTR(lcLine,AT('(',lcLine) + 1) && Get all text after the open parens.
          IF RIGHT(lcParams,1) = ')' && Strip off the close parens, if present.
            lcParams = LEFT(lcParams,LEN(lcParams) - 1)
          ENDIF
          IF RIGHT(lcParams,1) $ ';' && Oops, we have a continuation line
            * strip out continuation mark
            lcParams = SUBSTR(lcParams,1,LEN(lcParams) - 1)
            lcLine = ALLTRIM(laLines[laProcInfo[i,2] + 1]) && Get the continuation line.
            lcParams = lcParams + lcLine
            IF ')' $ lcParams && Strip of close parens, if present.
              lcParams = LEFT(lcParams,LEN(lcParams) - 1)
            ENDIF
            * Now, if you have additional continuation lines of parameters you'll have to modify this code here. And you may want to
            * rethink your career choice.
          ENDIF
    
        ELSE
    
          FOR j = laProcInfo[i,2] + 1 TO IIF(i!=lnCount,laProcInfo[i + 1,2]-1,ALEN(laLines,1))
            lcLine = ALLTRIM(laLines(j))
            * remove inline comments
            IF '&'+'&'$lcLine
              lcLine = RTRIM(LEFT(lcLine,AT('&'+'&',lcLine)-1))
            ENDIF
            DO CASE
              CASE LEFT(lcLine,1)='*' OR EMPTY(lcLine)
                * comment line
                LOOP
              CASE 'param'$LOWER(lcLine)
                * line contains 'param'
                lcParams = SUBSTR(lcLine,AT(' ',lcLine)+1) && remove parameter keyword
              CASE llContinuation
                * continuation
                lcParams = lcParams + lcLine
              OTHERWISE
                * other code reached - parameters are always before other code, so exit loop
                EXIT
            ENDCASE
            IF RIGHT(lcLine,1)#';'
              * no continuation mark - exit loop
              EXIT
            ELSE
              * strip out continuation mark
              lcParams = SUBSTR(lcParams,1,LEN(lcParams)-1)
              * set continuation flag
              llContinuation = .T.
            ENDIF
          ENDFOR
        ENDIF
    
        LOCATE FOR ALLTRIM(expanded) == ALLTRIM(laProcInfo(i,1)) AND TYPE = 'F'
        IF FOUND()
          REPLACE expanded WITH lcProcName, tip WITH lcParams, TIMESTAMP WITH DATETIME()
        ELSE
          INSERT INTO FOXCODE (TYPE,expanded,tip,CASE,TIMESTAMP) VALUES ('F',lcProcName,lcParams,'M',DATETIME())
        ENDIF
      ENDIF
    ENDFOR
    USE IN FOXCODE
    
    
    -- Ray Kirk

    Show #DEFINEs in drop-down list


    Here's one you might find useful. Like a good little programmer, I use defined constants in my programs. But, there are so many, I constantly (pun intended) have to look them up in the header file. The prg and script below will put the contents of a header file in a drop-down list.

    First of all, save the code below to Deflist.prg somewhere in your path. This program parses the header file into an IntelliSense compatible array.
    * Converts list of defined constants into an array
    Lparameters lcHeaderFile, laConstantArray
    Local lnFileHandle, lcLine, lnArraySize, lcConstant, lcValue, lnValueStart, lcComment
    
    lnArraySize = 1
    Dimension laConstantArray[lnArraySize,2]
    laConstantArray = ""
    
    * Open header file
    lnFileHandle = Fopen(FullPath(lcHeaderFile))
    If lnFileHandle < 0
      MessageBox("Cannot open header file "+FullPath(lcHeaderFile),16,"Error")
      Return laConstantArray
    EndIf
    Fseek(lnFileHandle,0,0) && go to top of file
    DO while not Feof(lnFileHandle)
      lcLine = Alltrim(Fgets(lnFileHandle,8192))
      * Add #DEFINEs to array
      If Upper(lcLine) = "#DEFINE"
        Dimension laConstantArray[lnArraySize,2]
        lcConstant = Alltrim(Strtran(lcLine, "#DEFINE", "",1,1,1)) && remove #DEFINE
        lcConstant = Alltrim(Strtran(lcConstant,Chr(9)," ")) && convert TABs to SPACEs
        lnValueStart = At(" ",lcConstant)
        lcValue = Alltrim(Substr(lcConstant,lnValueStart))
        lcConstant = Alltrim(Left(lcConstant,lnValueStart))
        laConstantArray[lnArraySize,1] = lcConstant
        laConstantArray[lnArraySize,2] = lcValue
        lnArraySize = lnArraySize + 1
      EndIf
      * Add comments to array. Customize to your own preference.
      * If you don't want all comments, start comment with a custom string (ie. *DEFLIST) and change code below
      If Upper(lcLine) = "*"
        Dimension laConstantArray[lnArraySize,2]
        lcComment = Alltrim(Strtran(lcLine, "*", "",1,1,1)) && remove asterisks
        lcComment = "--- " + lcComment + " ---"
        laConstantArray[lnArraySize,1] = lcComment
        laConstantArray[lnArraySize,2] = ""
        lnArraySize = lnArraySize + 1
      EndIf
    EndDo
    Fclose(lnFileHandle)
    
    Return @laConstantArray
    

    Next, add the following code to a script in the IntelliSense Manager. Add the entry as a Function, because a Command will not fire in the middle of a statement.
    * Set up as Function in IntelliSense.  Command will not work in the middle of a statement.
    LPARAMETER oFoxCode
    
    oFoxCode.Valuetype = "L"  && drop-down list
    oFoxCode.ItemSort = .f.   && do not sort
    
    * Could not pass oFoxCode.Items by reference, so setting up temporary array
    Dimension laArray[1,2]
    DefList(Home()+"Foxpro.h",@laArray) && populate array with header info
    
    * Resize oFoxCode.Items and copy contents from temporary array
    Dimension oFoxCode.Items[ALen(laArray,1),2]
    =ACopy(laArray,oFoxCode.Items,1,-1,1)
    
    Return "~"  && removes function name
    

    The script above references the standard foxpro header file. You can set up scripts to multiple header files by simply changing the first parameter to Deflist(). For example, I type DL( for my application header and FDL( for Foxpro.h. Enjoy. -- Joel Leach

    Show form parameters with DO FORM formname WITH


    This code will bring back a form's parameters when you type DO FORM whatever WITH .... as long as the form is not in use anywhere.
    In Intellisense Manager, Custom tab create a new script called "frmForm" and put the code below in the script.
    Next, on the General tab, click browse and find the entry for "Do Form". Type {frmForm} (including curly brackets) into the Cmd field.
    -- Rhodri CEvans
    LPARAMETER oFoxcode
    IF FILE(_CODESENSE)
      LOCAL eRetVal, loFoxCodeLoader
      SET PROCEDURE TO (_CODESENSE) ADDITIVE
      loFoxCodeLoader = CreateObject("FoxCodeLoader")
      eRetVal = loFoxCodeLoader.Start(m.oFoxCode)
      loFoxCodeLoader = NULL
      IF ATC(_CODESENSE,SET("PROC"))#0
        RELEASE PROCEDURE (_CODESENSE)
      ENDIF
      RETURN m.eRetVal
    ENDIF
    
    DEFINE CLASS FoxCodeLoader as FoxCodeScript
    
    PROCEDURE Main()
      IF 'WITH'$UPPER(this.oFoxCode.FullLine)
        LOCAL lcPath, lcTip
        lcPath = GetWordNum(this.cLine,3)
        Do CASE
        Case Left(lcPath,1)=["]
          * double quotes
          lcPath = Substr(this.cLine,At(["],this.cLine)+1,At(["],this.cLine,2)-At(["],this.cLine)-1)
        Case Left(lcPath,1)=[']
          * single quotes
          lcPath = Substr(this.cLine,At(['],this.cLine)+1,At(['],this.cLine,2)-At(['],this.cLine)-1)
        Case Left(lcPath,1)="["
          * square brackets
          lcPath = Substr(this.cLine,At("[",this.cLine)+1,At("]",this.cLine)-At("[",this.cLine)-1)
        EndCase
        IF Empty(JustExt(lcPath))
          * add extention
          lcPath = lcPath+'.SCX'
        EndIf
        lcTip = this.GetFormParams(lcPath)+' [TO VarName][NOREAD] [NOSHOW]'
        this.DisplayTip(lcTip)
      Else
        THIS.DisplayTip("DO FORM FormName | ? [NAME VarName [LINKED]][WITH cParameterList][TO VarName][NOREAD] [NOSHOW]")
      EndIf
    ENDPROC
    
    Function GetFormParams
      Lparameters tcFname, tcObjName
      Local laLines(1),lnLine, lcRetVal, i, llCont
      * hide errors while we try to use file
      llError = THIS.lHideScriptErrors
      THIS.lHideScriptErrors = .T.
      Select 0
      Use (tcFname) again shared
      THIS.lHideScriptErrors = llError
      IF !Empty(Alias())
        * file used successfully
        If Upper(JustExt(tcFname))='SCX'
          locate for baseclass = 'form' and empty(parent) and !Deleted()
        Else
          Locate for objname = tcObjName and empty(parent) and !Deleted()
        EndIf
        If Found()
          ALines(laLines,methods)
          lnLine = ascan(laLines,'PROCEDURE Init')
          DO Case
          Case lnLine > 0
            lcRetVal = ''
            FOR i = lnLine+1 to ASCAN(laLines,'ENDPROC',lnLine+1)
              * Remove inline comments
              laLines(i) = Iif('&'+'&'$laLines(i),strtran(laLines(i),Substr(laLines(i),Rat('&'+'&',laLines(i)))),laLines(i))
              Do CASE
              Case Empty(laLines(i)) or Left(Alltrim(laLines(i)),1)='*'
                * Empty or comment line
                LOOP
              Case 'param'$Lower(laLines(i)) or llCont
                * PARAMETER statement or continuation line
                IF 'param'$Lower(laLines(i)) and !llCont
                  laLines(i) = strtran(lalines(i),GetWordNum(lalines(i),1))
                EndIf
                lcRetVal = lcRetVal + strtran(Alltrim(laLines(i)),Chr(9))
              EndCase
              IF Right(Alltrim(laLines(i)),1)#';'
                * no continuation mark
                Exit
              Else
                * continuation
                lcRetVal = Left(lcRetVal,Len(lcRetVal)-1)
                llCont = .T.
              EndIf
            EndFor
            use
          Case !Empty(class) and !Empty(classloc)
            * no Init code, check parent class
            Scatter fields like class* memo memvar
            Use
            lcPath = JustPath(tcFname)
            Do Case
            Case '..\'$m.classloc
              Do while Left(m.classloc,3)='..\'
                m.classloc = Substr(m.classloc,4)
                lcPath = Left(lcPath,Rat('\',lcPath)-1)
              EndDo
              m.classloc = lcPath+'\'+m.classloc
            Case !'\'$m.classloc
              m.classloc = lcPath+'\'+m.classloc
            EndCase
            lcRetVal = this.GetFormParams(m.classloc,m.class)
          EndCase
        EndIf
      Else
        lcRetVal = ''
      EndIf
      Return lcRetVal
    EndFunc
    
    ENDDEFINE
    

    Expand CT to _ClipText This sounds like MF->Modify Form, but that is a command and so will only expand if it is the first word.

    IntelliSense Manager, Browse, find Abbrev = 'CustomDefaultScripts' (near bottem), add CT to list in Data field, Save.
    IntelliSense Manager, Custom, Add, Name="ct" Type="Script", Script:
    LPARAMETER oFoxScript
    IF !UPPER(oFoxScript.oFoxcode.UserTyped) == UPPER(oFoxScript.cCustomScriptName)
      RETURN .F.
    ENDIF
    if oFoxScript.nwords = 1
      oFoxScript.ReplaceWord("_ClipText =")
    else
      oFoxScript.ReplaceWord( StrTran( oFoxScript.cLastWord, 'ct' ) + "_ClipText" )
    endif
    RETURN "~"
    

    Save, Replace and give it a shot. "ct ct " should give you "_ClipText = _ClipText " (notice that it figures out if you need the "=" or not.

    Hmm, small issue: "_ClipText = Strtran(ct," doesn't expand the 2nd ct. This does work: "_ClipText = Strtran( ct " so life isn't too bad.

    Note: this may increase the amount of time a SPACE responds, due to # of scripts it will need to traverse, so get a faster machine.

    Thanks to Tracy Pearson for figuring this out for me. - cfk

    Drop down list of open tables

    This will save you tons of time when working in the command window
    Type = U
    Cmd={OpenTables}

    Type = S
    Abbrev = Open Tables
    Data =
    LPARAMETER oFoxCode
    
    LOCAL X, cReturn, nUsed
    
    * Create array of open tables
    nUsed =AUSED(aTables)
    
    IF nUsed = 0
    * Return a value
    oFoxCode.valueType = 'V'
    cReturn = 'USE ~'
    ELSE
    * Tell IntelliSense that you want a dropdown list to appear
    oFoxCode.valueType = 'L'
    * Build the list using oFoxCode's built in properties
    DIMENSION oFoxcode.Items[m.nUsed,2]
    FOR i = 1 TO m.nUsed
    oFoxcode.Items[m.i,1] = aTables[i, 1]
    oFoxcode.Items[m.i,2] = ""
    ENDFOR
    * Return the prefix for the results of the list
    cReturn = "SELECT"
    ENDIF
    * Return either USE or SELECT 
    RETURN cReturn
    



    Correct typos

    Some simple ideas to prevent typos
    Type = U
    Abbrev = retrun
    Expanded = RETURN
    Case = U

    Type = U
    Abbrev = ednif
    Expanded = ENDIF
    Case = U

    Use (_Foxcode) In 0 Shared Again Alias 'UdpFoxCode'
    
    Delete From UdpFoxCode Where Type = 'U' And Lower( ABBREV )  == 'retrun'
    Insert Into UdpFoxCode (Type, ABBREV, Expanded, Case )  Values ( 'U', 'retrun', 'return', 'U' )
    
    Delete From UdpFoxCode Where Type = 'U' And Lower( ABBREV )  == 'ednif'
    Insert Into UdpFoxCode (Type, ABBREV, Expanded, Case )  Values ( 'U', 'ednif', 'endif', 'U' )
    
    Use In Select( 'UdpFoxCode' )
    


    See also: IntelliSense FAQ
    Category IntelliSense Category VFP IDE Category Code Samples