/*********************************************************************** Find and do. Applies one of several options to a found string. Mar 12, 2022 - fix Count option again. Thanks to Carlo Hogeveen for the report. Jan 18, 2021 - fix to Count option. Thanks to Tom Collins for noting that it was broken. Additional work to try and make it work better in all cases. June 14 2016 - lower case was not doing anything - fix typo. March 16, 2012 SEM - added delete/cut/copy of just the found text Feb 06 2010 SEM - added Upper/Lower options. April 5, 2004 SEM - Don't allow Find&Do to run if potential file changing options are selected and the editor is in Browse Mode. Thanks to Carlo Hogeveen for the report. March 25, 2004 SEM Changed after-applying-macro logic to move right one character. Previously, we moved right the size of the found string. But what if the found string was deleted? February 2004 SEM - in the mlRepeatFind(), change the code to call the lRepeatFind() if no lines were deleted. Previously, lRepeatFind() was only called if the number of lines did not change. I'm not sure which is correct. July 2003 SEM - Implement speed-up involving 'G'lobal option. Thanks to Bruce Riggins for the tip. June 2003 SEM - Remove 'v' option if found. Remove 'a' option if any of the 'all files' options were selected. Thanks to Jose Adriano Baltieri for the fix. February 7, 2003 - SEM: Add Find & Keep File option. Will unload files where the find string is *not* found. Add Find & Quit File option. Will unload files where the find string *is* found. Add Find & Apply Macro. Allows an arbitrary macro to be run against each found string. Add Find & Apply Key. Allows an arbitrary key to be pressed against each found string. Key could be assigned to a keyboard macro, for instance. 12/12/2002 - SEM: Use EmptyBuffer(id) to empty clipboard 07/23/02 - SEM: If delete or cut was specified, and the target was found on the first line, the 2nd line is skipped. The problem was with RepeatFind. An old work-around did not handle the first line. 06/22/00 - SEM: Whoops! previous change was broken. 08/24/98 - SEM: if no prompting is NOT specified, will now prompt for confirmation. 07/29/98 - SEM: increase the find string maxlen to 200 characters. notes: "&Delete Line" skip = false: cursor to the beginning of next line "Cu&t Append Line" skip = false: cursor to the beginning of next line "Delete found text" skip = false "Cut Append found text" skip = false "Copy Append Line" skip = true "&Copy Append found text" skip = true "Cou&nt" skip = true "&UPPER case" skip = true "&lower case" skip = true "Apply &Macro by Name" skip = true (to be safe) "&Apply Key" skip = true (to be safe) "Move to Column" skip = true (to be safe) "&Keep File" NA "&Quit File" NA ***********************************************************************/ // order is important, specifically, we rely on multi-file options being last constant dofDELLINE = 1, dofCUTAPPEND_LINE, dofCOPYAPPEND_LINE, dofDIVIDE1, dofDEL_TEXT, dofCUTAPPEND_TEXT, dofCOPYAPPEND_TEXT, dofDIVIDE2, dofCOUNT, dofMACRO, dofKEYMACRO, dofUPPER, dofLOWER, dofMOVE_TO_COLUMN, dofALLFILES, dofKEEP, dofQUIT menu FindAndDoMenu() History Title = "After Find, Do" width = 17 "&Delete Line" "Cu&t Append Line" "Co&py Append Line" "Found Text", , Divide "&Erase found text" "Cut &Append found text" "Copy Append &found text" "", , Divide "Cou&nt" "Apply &Macro by Name" "Apply Ke&y" "&UPPER case" "&lower case" "Move to &Column" "All Files", , Divide "&Keep File" "&Quit File" end integer buffer_id // to determine if the first find in current file integer proc NumFilesAndSystemKludge() return (NumFiles() + (BufferType() <> _NORMAL_)) end integer proc get_skip(integer choice) integer skip = FALSE if choice in dofCOPYAPPEND_TEXT, dofCOPYAPPEND_LINE, dofCOUNT, dofUPPER, dofLOWER, dofMACRO, dofKEYMACRO, dofMOVE_TO_COLUMN skip = TRUE endif return (skip) end integer proc clear_clipboard(integer choice) integer n if choice in dofCOPYAPPEND_LINE, dofCOPYAPPEND_TEXT, dofCUTAPPEND_LINE, dofCUTAPPEND_TEXT n = YesNo("Clear clipboard before appending?") if n == 1 EmptyBuffer(Query(ClipboardId)) endif elseif choice == 0 return (FALSE) endif return (TRUE) end /************************************************************************** Use n and num_lines to determine if a line was deleted, which invokes special case code. **************************************************************************/ integer proc mlRepeatFind(string find_st, string find_option_st0, integer n, integer num_lines, integer skip) string find_option_st[12] // if no lines where deleted, just repeat the find // if n <= num_lines // return (lRepeatFind()) // endif if n - num_lines == MAXINT endif find_option_st = find_option_st0 if GetBufferId() <> buffer_id buffer_id = GetBufferId() endif if skip find_option_st = find_option_st + "+" endif return (lFind(find_st, find_option_st)) end /************************************************************************* March 25, 2004 SEM I'm not sure where to move the cursor after a macro has been executed. Some choices are: Do nothing, assume the macro positions it correctly If the number of lines has changed, move to the beginning of the current line. If the number of lines has not changed: Move right 1. Move right the length of the found string. *************************************************************************/ proc handleCursorPosition(integer num_lines, integer cur_line, integer cur_pos) if num_lines == NumLines() // no lines deleted if CurrLine() <= cur_line GotoLine(cur_line) if CurrPos() == cur_pos Right() endif endif elseif CurrLine() else // line was deleted BegLine() endif end proc ApplyDo(integer choice, string macro_name, integer macro_key, integer found_len, integer mv_to_col) integer num_lines = NumLines() integer cur_line = CurrLine() integer cur_pos = CurrPos() if found_len // just to keep the compiler happy - we don't use it for now endif if choice in dofUPPER, dofLOWER, dofDEL_TEXT, dofCUTAPPEND_TEXT, dofCOPYAPPEND_TEXT, dofMOVE_TO_COLUMN PushBlock() MarkFoundText() endif case choice when dofDELLINE DelLine() BegLine() when dofCUTAPPEND_LINE, dofCOPYAPPEND_LINE PushBlock() MarkLine() MarkLine() if choice == dofCUTAPPEND_LINE CutAppend() else Copyappend() endif PopBlock() BegLine() if choice == dofCOPYAPPEND_LINE EndLine() endif when dofMACRO ExecMacro(macro_name) handleCursorPosition(num_lines, cur_line, cur_pos) when dofKEYMACRO PressKey(macro_key) handleCursorPosition(num_lines, cur_line, cur_pos) if macro_key== Right() endif //当应用键刚刚将找到列间隔符往右移到下一个高亮列之后,自动右移一个列位置,以免下次重复找到同一个列间隔符 when dofUPPER Upper() when dofLOWER Lower() when dofDEL_TEXT DelBlock() when dofCUTAPPEND_TEXT CutAppend() when dofCOPYAPPEND_TEXT Copyappend() when dofMOVE_TO_COLUMN GotoBlockBegin() while CurrCol() < mv_to_col InsertText(' ', _INSERT_) endwhile while CurrCol() > mv_to_col if not Left() break endif if not CurrChar() in _AT_EOL_, _BEYOND_EOL_, Asc(' ') break endif DelChar() endwhile GotoBlockEnd() endcase if choice in dofUPPER, dofLOWER, dofDEL_TEXT, dofCUTAPPEND_TEXT, dofCOPYAPPEND_TEXT, dofMOVE_TO_COLUMN PopBlock() endif end // troublesome options: //b //c //l //v integer proc is_allowed(string s, integer choice) if Pos('B', s) return (Warn("B)ack option not supported in Find&Do")) endif if Pos('C', s) if choice > dofALLFILES return (Warn("C)urrent line only option set, not compatible with All files")) endif endif if Pos('L', s) if not isCursorInBlock() return (Warn("L (block/select) option set, but cursor not in a block")) endif if choice > dofALLFILES return (Warn("L (block/select) option set, not compatible with All files")) endif endif if Pos('V', s) return (Warn("V)iew option not supported in Find&Do")) endif return (TRUE) end /************************************************************************** keep: Remove any files where find_st is _not_ found. not keep: Remove any files where find_st _is_ found. Notes: QuitFile() causes the previous file (ring-wise) to load. **************************************************************************/ proc findAndKeepOrQuit(string find_st, string find_option_st, integer keep) integer found, old_sound, hook_state, removed = 0, n = NumFilesAndSystemKludge() PushPosition() old_sound = Set(beep, off) hook_state = SetHookState(OFF) do n times PushPosition() found = lFind(find_st, find_option_st) PopPosition() if (keep and not found) or (not keep and found) if not QuitFile() break endif removed = removed + 1 endif NextFile() enddo SetHookState(hook_state) Set(beep, old_sound) // restore position after turning hooks on, so _ON_CHANGING_FILES_ will run PopPosition() // and force hooks to fire, in case original file was quit if FileExists(CurrFilename()) EditFile(QuotePath(CurrFilename())) endif Message(removed, " file(s) unloaded") end proc main() integer choice, curr_id, count, n, old_sound, hook_state, check_buff, this_id, prompted, macro_key, reply_key, found_len, mv_to_col, skip, p string find_st[255] = '', find_option_st[12] = '', macro_name[_MAXPATH_] = '', move_to_column[10] = '' buffer_id = 0 // we need initialize this global var each time mv_to_col = 0 if Ask("Search for:", find_st, _FIND_HISTORY_) and Ask("Options [BGLIWNX] (Back Global Local Ignore-case Words No-promp reg-eXp):", find_option_st, _FIND_OPTIONS_HISTORY_) count = 0 if not is_allowed(Upper(find_option_st), 0) return () endif prompted = Pos('N', Upper(find_option_st)) == 0 curr_id = GetBufferId() choice = FindAndDoMenu() if choice == 0 return () endif if choice > dofALLFILES if Pos('A', Upper(find_option_st)) find_option_st = DelStr(find_option_st, Pos('A', Upper(find_option_st)), 1) endif endif if not is_allowed(Upper(find_option_st), choice) return () endif if not clear_clipboard(choice) return () endif case choice when 0 return () when dofCUTAPPEND_LINE, dofCOPYAPPEND_LINE // cut/copy to buffer EmptyBuffer(Query(ClipBoardId)) when dofMACRO repeat if not Ask("Macro to run on each found string:",macro_name) or Trim(macro_name) == "" return () endif until isMacroLoaded(GetToken(macro_name, " ", 1)) or LoadMacro(GetToken(macro_name, " ", 1)) when dofKEYMACRO Message("Press the Key you want to apply on each found string, to cancel") macro_key = GetKey() if macro_key == return () endif when dofKEEP findAndKeepOrQuit(find_st, find_option_st, TRUE) return () when dofQUIT findAndKeepOrQuit(find_st, find_option_st, FALSE) return () when dofMOVE_TO_COLUMN if not Ask("Column to move to:", move_to_column) return () endif if not isDigit(move_to_column) Warn("column must be numeric") return () endif mv_to_col = Val(move_to_column) when dofCOUNT prompted = FALSE skip = TRUE endcase PushPosition() old_sound = Set(beep, off) hook_state = SetHookState(OFF) if lFind(find_st, find_option_st) p = Pos('G', Upper(find_option_st)) if p > 0 find_option_st = DelStr(find_option_st, p, 1) endif curr_id = GetBufferId() check_buff = FALSE repeat found_len = Length(GetFoundText()) this_id = GetBufferId() if check_buff and curr_id == this_id break endif if BrowseMode() and (choice in dofDELLINE, dofCUTAPPEND_LINE, dofMACRO, dofKEYMACRO, dofMOVE_TO_COLUMN) if MsgBoxEx("Buffer is in Browse Mode", "The current operation cannot be performed because the current buffer is in Browse Mode", "[&Skip current buffer];[&Halt operation]") == 1 EndFile() else break endif endif if curr_id <> this_id check_buff = TRUE endif count = count + 1 n = NumLines() if not prompted ApplyDo(choice, macro_name, macro_key, found_len, mv_to_col) skip = get_skip(choice) else ScrollToCenter() UpdateDisplay() HiLiteFoundText() Message("L ", CurrLine(), " ", SplitPath(CurrFilename(), _NAME_|_EXT_), " Apply (Yes/No/Only/Rest/Quit)") skip = false repeat reply_key = GetKey() until reply_key in , , , , , , , , , , case reply_key when , ApplyDo(choice, macro_name, macro_key, found_len, mv_to_col) when , // just skip this one, go on to next skip = true when , ApplyDo(choice, macro_name, macro_key, found_len, mv_to_col) break when , ApplyDo(choice, macro_name, macro_key, found_len, mv_to_col) prompted = FALSE when , , break endcase endif until not mlRepeatFind(find_st, find_option_st, n, NumLines(), skip) endif SetHookState(hook_state) Set(beep, old_sound) // restore position after turning hooks on, so _ON_CHANGING_FILES_ will run PopPosition() Message(count, " occurrence(s) found") endif end