GoFish Help: Using GoFish to perform Code Replacements
This page documents the Replace Mode features which were added in Version 4.2
GoFish has three powerful code Replace Modes. Each mode provides backups and replace history.
This page explains the use of GoFish for performing code replacements, as well as the risks involved when doing so.
DANGER!! There are many risks associated with tools that replace code in your source files. GoFish attempts to help you understand and manage those risks by (1) performing backups of your files before a
replace is performed and (2) categorizing the code matches (MatchTypes) according to their Risk Level and allowing you to select the Risk Level that you want to allow for Replace operations. Please study this entire page to learn more about his powerful, yet
dangerous feature of GoFish.
(Note: you must enable Replace Mode in the Options form before using it the first time. To enable and configure Replace options, choose the Options button from the main GoFish form and go to the Replace and Backup tabs.)
Here is a screenshot of GoFish in Replace Mode :
After performing a Search, you can enter Replace Mode, and choose either of the modes to perform Replacements on the matched lines.
GoFish has 3 replace modes:
- (1) Replace Text
You can perform mass replace across multiple rows by clicking the checkbox in the Replace column on the desired row(s). Then, enter a string in the Replacement Text field. You will see a preview of what the code lines will be in the Replace Line column
of the grid and in the code preview window. Click the “Replace checked” button to apply the changes.
- (2) Edit Line
You can also edit *any part* of the code line for the current row by using the Replace Line editbox and clicking Replace Line. You will get a confirmation dialog box to proceed.
- (3) Advanced Replace
With Advanced Replace, you select a UDF prg which processes the replacement text. For each checked row, the matched line will be passed into the selected UDF where it can be modified using any FoxPro code that you write in the prg. The return value
from the UDF should be a string containing the new line that you wish to replace the old line. You can even add new lines by include CR and LF characters between lines. *YOU* are in total control of what the replaced line contains.
Sample UDF prg - Parses passed line and returns a modified line.
Example - The above UDF would provide this result:
OpenTable(Param1, Param2, Param3, Param4)
Will replace with this line:
For each Replace mode, you will get a confirmation dialog box to proceed, unless you have disabled the dialog in the Options form:
After applying a Replace operation, you will see a green checkmark on each row indicating that the Replace was performed successfully. No further edits can be done on a row once it has been been used in a Replaced operation (until a new Search is performed)
Backups - GoFish includes a back up system that is executed before each replace operation. It is enabled by default when you enable the Replace feature. You can also provide a custom backup PRG to manage backups according to your own need
and preference. You can also disable the backup feature entirely if you wish to handle you backups another way. Either way, *YOU* are responsible to test the backup feature on your system to make sure it is working correctly before you use GoFish to perform
code replaces! See
GoFish Backup Help for more information.
Logging - Each applied replace operation is recorded in DBF log tables to help you with troubleshooting and reviewing your code changes.
- Master table: GF_Replace_History.dbf (contains 1 row for each replace that is applied. Assigns a unique ID for each replace operation. Corresponds to the folder name in the Backups folder.)
- Detail table: GF_Replace_Detail.dbf (contains one record for each line of code that is affected by the replace. The record contains all the details of the row that were discovered during the initials search phase, such as filename, method name, object name,
class name, line number, etc.)
These tables are stored in the Home(7) folder if you want to access them directly, or you can see a view of the log activity by clicking the History button from the Replace Panel.
Risks - There are many risks associated with tools that replace code in your source files. GoFish attempts to help you understand and manage those risks by categorizing the types of code matches it finds (see the MatchType column of
the search results grid), and allowing you to select the Risk Level that you want to allow for Replace operations in the search results grid. You may choose a Risk Level from 0 (meaning the Replace feature is entirely disabled), Level 1, Level 2, or Level
3. See the chart on the Replace tab of the Options form for more details about what MatchTypes are included in each Risk Level (screenshot below).
Here is a screen shot of the Replace page on the Options form where you can enable Replace Mode and select your Risk Level:
Getting started with replace:
When doing a replace, GoFish only does what it is told do. It relies on you to determine if the result is going to be good code. There are a few things GoFish will not let you touch, like Filename, TimeStamp, and a few more (See the chart for a complete
Step 1: Before performing a Replace operation with GoFish, you should go to the Replace page on the Options form shown above and select the Risk Level that you want to use for the Replace. (The out-of-the-box default is Level 1, “Pretty
Safe”) . You can read more about the types of risks in the Risks areas further down on this page.
Step 2: As you perform a Search, each row in the search results is assigned a Risk Level based on the sensitivity of the MatchType according to the above table.
Step 3: When you enter into Replace Mode on the main form, it will then make a few UI changes to prepare for the Replace operation. Based on your selected Risk Level (on the Replace page of the Options form), each row in the results
grid that falls in or below the selected Risk Level will have a checkbox in the Replace column which allows you to select that row for a code replace operation. You then enter a Replace expression, and click the checkbox for each row that you want to replace.
You will see a Preview of the replaced line in the grid and in the HTML code view at the bottom of the form.
(You can also use the Filter button or TreeView to filter the results while in replace mode)
There are 2 main kinds of risks when doing a code replace with *any* tool:
- One risk is simply where the resulting code is just invalid syntax or references invalid variable names, parameters, or methods. It won’t compile as-is, but it can be fixed to make it right. (Heck, my own hand-written code falls into this category very
- The other risk is a little more serious: You probably know by now that the SCX/VCX/FRX files in VFP are very sensitive in their structure and field contents. If they get goofed up, VFP can even refuse to open the file for editing, much less compile and
run it. These are the bad ones. Make sure you use the Backup feature of GoFish so you can recover your backup copies if this happens to you.
Deep dive into replace risks:
So let’s dig into what happens during a Search and Replace to look at some of the gotcha’s that could happen if you replace something that results in invalid code, invalid property names, or property values.
It is very possible that a user can perform a replace that causes problems with the files after the Replace is completed. For instance, if you affected the PROCEDURE or ENDPROC words in the METHODS column of a SCX/VCX. Or goofed up some property assignment
code in a SCX or VCX Properties column, or caused a good in a PRG file, stuff like that. But that would be the users fault, not GF, because GF just does what he’s told to do. There is no way for GoFish to fully protect you from the risks of what’s possible
For instance, if you did a mass partial replace on “CED” with some other string, well, then, you just killed a bunch of your forms and classes!!! (Cause CED is found inside of PROCEDURE, you see.) One should use a filter and sorting to closely examine
the match results before selecting the ones to Replace. Should be pretty easy and fairly safe if one takes caution. But there is always the real possibility that you break something.
When replacing words like “Quote”, “P_O_Num”, “mach_num”, “custno”, etc., you might break code that needs to be fixed, but you should be able to avoid breaking the SCX/VCX in a way that it simply won’t open in VFP.
Examples of risky code replaces:
Imagine a search for the phrase “category” on a code base which finds these three matches:
||cCategory = “Some Category”
||cCategory = “Some Category”
There are two real issues here:
1. If the use wanted to change the <Property Name> or <Property Def> , then *both* match rows need to be changed to avoid problems. Ideally, if you checked the <Property Def> match, it should be smart enough to process
the corresponding <Property Name> row too, even if the user does not check it. Or, the opposite, if the user checks the <Property Name> row, and fails to check the <Property Def> row, it would need to process the <Property Def> row
too, else there will be a problem.
2. The example above is a specifically chosen example to show a special case where a match occurs on both the <Property Name> and the <Property Value>. So the same line of code has resulted in two match results in the grid; one for the <Property
Name> and another row for the <Property Value>.
Now, it’s important to realize that when performing a Replace, GoFish uses a Regular Expression object to do the work, and the way it is presently used results in every match on that line getting replaced. There is no way to select only one match on that
line to replace. All matches on a line will be replaced.
So let’s say you don’t mess with the <Property Name> and <Property Def> matches discussed in Issue 1, but instead you mark *only* the <Property Value> checkbox (should be a pretty safe replace, right), but, for GR to replace
*only* that part of the match line is a lot more tricky than what currently happens. It current replaces every match on the line, which means, when a match happens on both the <Property Name> and the <Property Value>, it would
replace them both, even though you only checked the <Property Value> one. I could fix this, but it will take some extra coding that I really do not look forward to.
Note: Issue 2 is not an issue at all if the match only occurs in the <Property Value> side.
Other helpful resources: