implementation module EdSearchMenu;

//	The commands of the Search menu

import StdClass,StdInt, StdString, StdBool, StdChar,StdArray;
import deltaDialog, deltaIOState, deltaTimer;

import 	EdProgramState, EdDialogs, EdSupport, EdParse, EdFiles, EdLists, EdFileMenu, EdPath,
		EdDrawWindow, EdTextWindow, EdMenuItems, EdInterrupt, EdWindows, EdTextFind, EdText;

(AP2) infixr;
(AP2) f g :== f e1 e2; { (e1,e2) = g };

(THEN2) infixl;
(THEN2) s f :== f e1 e2; { (e1,e2) = s };
     
Lin1ID		:== 1;
Lin2ID		:== 2;
But1ID		:== 3;
But2ID		:== 4;

BIgnID 		:== 51;
BBacID		:== 52;
BWraID		:== 53;
BMatID		:== 54;
EFinID		:== 2;
ERepID		:== 4;
	
ITextID		:== 1;
ISTextID	:== 2;
IFinID		:== 3;
RBMod		:== 4;
RSearchDefinitionID :== 41;
RSearchImplementationID :== 42;
RSearchIdentifiersID :== 43;
BExpID		:== 51;
RBImpID		:== 6;
RImpID		:== 61;
RPathsID	:== 62;
CBVerID		:== 8;
BVerID		:== 82;
BTCancID	:== 11;
BTFindID	:== 12;

//	Device function for the Find... command: The Find dialog

Find :: !ProgState !IO -> ProgIO;
Find prog=:{editor={editwindows,findinfo}} io
	=	OpenModalDialog dialog prog io;
	where {
		dialog= CommandDialog DFindID "Find" [] 9 [st1,et1,st2,et2,boxes,bt1,bt2,bt3,bt4];
		st1	= StaticText 1 Left "Find:";
		et1	= EditText EFinID (YOffset 1 (MM 1.0)) (MM 120.0) 1 findinfo.find;
		st2	= StaticText 3 (Below EFinID) "Replace With:";
		et2	= EditText ERepID (YOffset 3 (MM 1.0)) (MM 120.0) 1 findinfo.replace;
		boxes	= CheckBoxes 5 Left (Rows 2) [
					CheckBox BIgnID "Ignore Case" Able ignore	DFIdle,
					CheckBox BBacID "Backward"    Able backw	DFIdle,
					CheckBox BWraID "Wrap Around" Able wrapa	DFIdle,
					CheckBox BMatID "Match Words" Able matchw	DFIdle
				  ];
		bt1	= DialogButton 6 Left        "Cancel"      Able Cancel;
		bt2	= DialogButton 7 (RightTo 6) "Replace All" able ReplaceAll;
		bt3	= DialogButton 8 (RightTo 7) "Replace"     able Replace;
		bt4	= DialogButton 9 (RightTo 8) "Find"        Able FindFind;
		ignore				= BoolToMark findinfo.ignore;
		backw				= BoolToMark findinfo.backw;
		wrapa				= BoolToMark findinfo.wrapa;
		matchw				= BoolToMark findinfo.matchw;
		(_,front)			= GetFrontWindow editwindows;
		able				= BoolToSelect (front.wstate.wdtype == EditWd);
	};
	
// GetFindDialogInfo :: !DialogInfo -> FindInfo;
GetFindDialogInfo dinfo
	:==	{	find	= GetEditText EFinID dinfo,
			replace	= GetEditText ERepID dinfo,
			ignore	= CheckBoxMarked BIgnID dinfo,
			backw	= CheckBoxMarked BBacID dinfo,
			wrapa	= CheckBoxMarked BWraID dinfo,
			matchw	= CheckBoxMarked BMatID dinfo
		};

FindFind :: !DialogInfo !ProgState !IO -> ProgIO;
FindFind dialog prog=:{editor} io
	= DoFind False FindD prog` (CloseActiveDialog io);
	where {
		prog`	= {prog & editor={editor & findinfo=GetFindDialogInfo dialog}};
	};
	
Replace :: !DialogInfo !ProgState !IO -> ProgIO;
Replace dialog prog=:{editor} io
	= DoFind False find_replace prog` (CloseActiveDialog io);
	where {
	prog`	= {prog & editor={editor & findinfo=GetFindDialogInfo dialog}};
	
	find_replace :: !PartTSel !FindInfo !NrLines !Text -> (!Bool, !ReplacedAll, !PartTSel, !Text);	
	find_replace textpos=:{l1,c1,l2,c2} finfo=:{backw,replace,find} nrlines text
		| not fnd	= (False, EmptyReplaced, textpos, text);
		| not rpl	= (True, replaceda, fsel, text`);
					= (True, replacedb, rsel, text`);
		where {
		lnr | backw	= l1;
					= l2;
		cnr | backw	= c1;
					= c2;
		rsel		= {fsel & c2 = fsel.c1 + size replace};
		replaceda	= {len= 0,org="",repl="",ln_cns=Nil};
		replacedb	= {len= size replace,org=replace,repl=find,ln_cns={Replaced | lnr=fsel.l1,cnr=fsel.c1}:!Nil};
		(fnd,fsel)	= Text_Find nrlines lnr cnr finfo text;
		(rpl,text`)	= Text_Replace fsel.l1 fsel.c1 finfo text; };
	};

ReplaceAll :: !DialogInfo !ProgState !IO -> ProgIO;
ReplaceAll dialog prog=:{editor=ed=:{editwindows}} io
	| fnd	= Edit_UpdateMenuItems prog` io`;
			= (prog4,io1);
	where {
	(prog`,io`)					= DrawTextUpdate frontid ScrollHalfWin wu prog4 io1;
	prog4						= {prog & editor={ed & findinfo=finfo,editwindows=editwindows`}};
	(frontid,front)				= GetFrontWindow editwindows;
	io1							= CloseActiveDialog io;
	(front1,fnd,replaced,wu)	= ReplaceAllInText front (Text_ReplaceAll finfo);
	front`						= {front1 & wstate={front1.wstate & undoinfo=undoinfo}};
	editwindows` | fnd			= SetFrontWindow front` editwindows;
								= editwindows;
	finfo						= GetFindDialogInfo dialog;
	undoinfo					= UndoInfoReplaceAll replaced;
	};
	
UndoInfoReplaceAll :: !ReplacedAll -> UndoInfo;
UndoInfoReplaceAll replaced
	= { EmptyUndo &	replaced	= replaced,
					menu		= {undo = True,action = " Replace All"} };

//	Device function for the Find Next/Find Previous command

FindNext :: !Bool !ProgState !IO -> ProgIO;
FindNext flip prog io = DoFind flip FindD prog io;

//	Device function for the Find Selection command

FindSelection :: !Bool !ProgState !IO -> ProgIO;
FindSelection flip prog=:{editor=ed=:{findinfo,editwindows}} io
	= DoFind flip FindD prog` io;
	where {
	prog`				= {prog & editor={ed & editwindows=editwindows`,findinfo=finfo`}};
	editwindows`		= SetFrontWindow front` editwindows;
	(_,front)			= GetFrontWindow editwindows;
	(front`,clip)		= CopyText front tsel;
	finfo`				= {findinfo & find = Head clip};
	tsel				= front.wtext.WinText.selection.tsel;
	};

//	Device function for the replace and find command

ReplaceAndFind :: !Bool !ProgState !IO -> ProgIO;
ReplaceAndFind flip prog io
	= DoFind flip replace_find prog io;
	where {
	
	replace_find :: !PartTSel !FindInfo !NrLines !Text -> (!Bool, !ReplacedAll, !PartTSel, !Text);	
	replace_find textpos=:{l1,c1} finfo=:{backw,replace,find} nrlines text
		| not rpl	= (False, EmptyReplaced, textpos, text);
		| not fnd	= (False, replaced, rsel, text`);
					= (True, replaced, fsel, text`);
		where {
		l1`	| backw		= rsel.l1;
						= rsel.l2;
		c1`	| backw		= rsel.c1;
						= rsel.c2;
		rsel			= {l1=l1,c1=c1,l2=l1,c2=c1 + size replace};
		replaced		= {len= size replace,org=replace,repl=find,ln_cns={Replaced | lnr=rsel.l1,cnr=rsel.c1}:!Nil};
		(rpl,text`)		= Text_Replace l1 c1 finfo text;
		(fnd,fsel)		= Text_Find nrlines l1` c1` finfo text`; };
	};

// Device function for the Find Error Command

FindError :: !Bool !ProgState !IO -> ProgIO;
FindError flip prog=:{editor={editwindows}} io
	= SetLineTo linenr ErrorWdID errwdold prog io
	  THEN2 openmodule;
	where {
	errwdold					= GetWindow ErrorWdID editwindows;
	linenr	| line_sel && flip	= (lastline + dec textpos.l1) rem lastline;
			| line_sel			= (inc textpos.l1) rem lastline;
								= textpos.l1 rem lastline;
	lastline					= dec (errwdold.wtext.nrlines);
	textpos						= EW_GetTextPosition curline selection;
	oldtsel						= selection.tsel;
	line_sel					= inc oldtsel.l1==oldtsel.l2 && oldtsel.c1==0 && oldtsel.c2==0;
	selection					= errwdold.wtext.WinText.selection;
	curline						= errwdold.wtext.curline;
	
	openmodule :: !ProgState !IO -> ProgIO;
	openmodule prog=:{editor=ed=:{editwindows}} io
		| not_empty	= OpenModule (path) {l1=lnr,c1=0,l2=inc lnr,c2=0} prog io;
					= (prog, Alert io);
		where {
		errwdnew	= GetWindow ErrorWdID editwindows;
		errmsg		= Head clip;
		(_,clip)	= CopyText errwdnew errwdnew.wtext.WinText.selection.tsel;
		(path,lnr)	= ParseErrorMsg errmsg;
		not_empty	= path <> EmptyPathname;
		};
	};
	
// Device function for the Find Identifier Command
	
FindIdentifier :: !ProgState !IO -> ProgIO;
FindIdentifier prog=:{editor={findidinfo}} io
	= OpenFindIdentDialog True findidinfo prog io;
	
OpenFindIdentDialog :: !Bool !FindIdentInfo !ProgState !IO -> ProgIO;
OpenFindIdentDialog full {search_kind,cleanid,imp,verbose,export_} prog io
	= (prog,OpenDialog dialog (CloseDialog DFindIdentID io));
	where {
		dialog | full
			= CommandDialog DFindIdentID "Find Identifier" [] BTFindID [dt1,st1,et1,ri1,ri2,cb1,bt1,bt2];
			= CommandDialog DFindIdentID title [] IFinID [dt1,st1,et1,bt1];
		dt1		= DynamicText ITextID Left (MM 150.0) "";
		st1		= StaticText ISTextID Left "Find:";
		et1		= EditText IFinID (YOffset ISTextID (MM 1.0)) (MM 120.0) 1 cleanid;
		ri1		= RadioButtons RBMod Left (Columns 1) (SearchKindToButtonId search_kind) [
					RadioItem RSearchDefinitionID "Find Definition"			Able		disable,
					RadioItem RSearchImplementationID "Find Implementation"	Able		enable,
					RadioItem RSearchIdentifiersID "Find Identifiers"		Able		disable
				];
		ri2		= RadioButtons RBImpID (RightTo RBMod)  (Columns 1) (SearchTypeToButtonId imp) [
					RadioItem RImpID	"Search In Imported Files"			Able		DFIdle,	
					RadioItem RPathsID	"Search Paths"						Unable		DFIdle ];
		cb1	= CheckBoxes CBVerID Left (Columns 1)[
				CheckBox BVerID "Be Verbose" Able verbm	DFIdle,
				CheckBox BExpID	"Exported Identifiers Only" (case search_kind of {Implementation->Able;_->Unable}) expm DFIdle
				];
		bt1		= DialogButton BTCancID Left	"Close"			Able		(CloseFindId full);
		bt2		= DialogButton BTFindID (XOffset BTCancID (MM 40.0)) "Find" Able FindFindIdent;
		expm	= BoolToMark export_;
		verbm	= BoolToMark verbose;
		title = case search_kind of {
					Definition -> "Find Definition";
					Implementation -> "Find Implementation";
					Identifier->"Find Identifier";
				};

		enable	dinfo dstate = EnableDialogItems [BExpID] dstate;
		disable	dinfo dstate = DisableDialogItems [BExpID] dstate;
	
		CloseFindId :: !Bool !DialogInfo !ProgState !IO -> ProgIO;
		CloseFindId full dinfo prog=:{editor} io
			| full
				= (prog`,io`);
				= (prog,io`);
		where {
			io`		= CloseDialog DFindIdentID io;
			prog`	= {prog & editor={editor & findidinfo=GetFindIdentDialogInfo dinfo}};
		};
	};

	SearchTypeToButtonId :: !SearchType -> Int;
	SearchTypeToButtonId SearchImports = RImpID;
	SearchTypeToButtonId SearchPaths = RPathsID;
	
	SearchKindToButtonId :: !SearchKind -> Int;
	SearchKindToButtonId Definition = RSearchDefinitionID;
	SearchKindToButtonId Implementation = RSearchImplementationID;
	SearchKindToButtonId Identifier = RSearchIdentifiersID;

	
// GetFindIdentDialogInfo :: !DialogInfo -> FindIdentInfo;
GetFindIdentDialogInfo dinfo
	:==	{ 	search_kind	= ButtonIdToSearchKind (GetSelectedRadioItemId RBMod dinfo),
			cleanid	= GetEditText IFinID dinfo,
			imp		= ButtonIdToSearchType (GetSelectedRadioItemId RBImpID dinfo),
			export_	= CheckBoxMarked BExpID dinfo,
			verbose	= CheckBoxMarked BVerID dinfo
		};

ButtonIdToSearchKind :: !Int -> SearchKind;
ButtonIdToSearchKind RSearchDefinitionID = Definition;
ButtonIdToSearchKind RSearchImplementationID = Implementation;
ButtonIdToSearchKind RSearchIdentifiersID = Identifier;
	
ButtonIdToSearchType :: !Int -> SearchType;
ButtonIdToSearchType RImpID	= SearchImports;
ButtonIdToSearchType RPathsID	= SearchPaths;

ChangeMsgString :: !Bool !String !ProgState !IO -> ProgIO;
ChangeMsgString verbose msg prog io
	| verbose	= (prog, ChangeDialog DFindIdentID [ChangeDynamicText ITextID msg] io);
				= (prog, io);
	
FindFindIdent :: !DialogInfo !ProgState !IO -> ProgIO;
FindFindIdent dinfo prog=:{editor} io
	= CloseWindow "" ErrorWdID prog1 io THEN2 FindIdent info;
	where {
		prog1		= {prog & editor={editor & findidinfo=info}};
		info		= GetFindIdentDialogInfo dinfo;
	};
			
// Device function for the Find Definition/Implementation Command
	
FindDefOrImp :: !Bool !ProgState !IO -> ProgIO;
FindDefOrImp def prog=:{editor=ed=:{editwindows,findidinfo}} io
	| is_cleanid	= FindIdent info` prog` io`;
					= (prog,io1);
	where {
	(prog`,io`)			= OpenFindIdentDialog False info` prog4 io2;
	(prog4,io2)			= CloseWindow "" ErrorWdID prog3 io1;
	io1					= CloseDialog DFindIdentID io;
	prog3				= {prog & editor={ed & findidinfo=info`}};
	(_,front)			= GetFrontWindow editwindows;
	cleanid				= Head clip;
	(_,clip)			= CopyText front front.wtext.WinText.selection.tsel;
	info`				= {findidinfo & search_kind = if def Definition Implementation, cleanid = cleanid};
	is_cleanid			= CleanModId cleanid;
	};

//	Device function for the Goto Line command

GotoLine :: !ProgState !IO -> ProgIO;
GotoLine prog=:{editor={editwindows}} io = OpenModalDialog dialog prog io;
	where {
	dialog				= CommandDialog IGotoLID "Goto Line" [] But2ID [stext,etext,cancel,ok];
	stext				= StaticText Lin1ID Center "Goto Line:";
	etext				= EditText Lin2ID (RightTo Lin1ID) (Inch 0.5) 1 (toString (inc textpos.l1));
	cancel				= DialogButton But1ID (Below Lin2ID) "Cancel" Able Cancel;
	ok					= DialogButton But2ID (RightTo But1ID) "OK" Able GotoLineOK;
	(_,front)			= GetFrontWindow editwindows;
	textpos				= EW_GetTextPosition front.wtext.curline front.wtext.WinText.selection;
	};

GotoLineOK :: !DialogInfo !ProgState !IO -> ProgIO;
GotoLineOK dinfo prog=:{editor={editwindows}} io
	=  (prog1,CloseActiveDialog io1);
	where {
	(prog1,io1)		= SetLineTo lnr` frontid front prog io;
	(frontid,front)	= GetFrontWindow editwindows;
	lnr` | lnr < 0	= 0;
					= lnr;
	lnr				= dec (StringToInt lnrstr);
	lnrstr			= GetEditText Lin2ID dinfo;
	};
	
//	Device function for the Goto Cursor command

GotoCursor :: !ProgState !IO -> ProgIO;
GotoCursor prog=:{editor={editwindows}} io
	= DrawSelect True frontid wu prog io;
	where {
	(frontid,front)	= GetFrontWindow editwindows;
	wu				= {EmptyWindowUpdate & oldpos=curpos,oldsel=sel,curpos=curpos,selection=sel};
	sel				= front.wtext.WinText.selection;
	curpos			= front.wtext.cursorpos;	
	};

/* Aux. function: Looks up the definition/implementation of the indicated identifier. */

FindIdent :: !FindIdentInfo !ProgState !IO -> ProgIO;
FindIdent info=:{search_kind,imp} prog=:{editor={project,editwindows,defaults={paths=defs}}} io
	| not (WindowsPresent editwindows)
		= (prog,io);
	| imp==SearchPaths
		= StartIntr DFindIdentID (search (winpath :! modpaths1) search_kind) prog io`;
		{
			(modpaths1, io`)	= accFiles (ReadDirectories defs prjs Nil) io;
			prjs					| PR_ProjectSet project
										= PR_GetPaths project;
										= RemoveFilename winpath:!Nil;
		}
		= StartIntr DFindIdentID (search (GetModuleName winpath :! Nil) search_kind) prog io;
	where {
		(_,front)				= GetFrontWindow editwindows;
		winpath					= front.wstate.pathname;
	
		search :: !(List Pathname) SearchKind !Bool !ProgState !IO -> ProgIO;
		search modpaths Identifier intr prog io
			= SearchIdentifiersInFiles (IsDefPathname winpath) info Nil modpaths intr prog io;
		search modpaths search_kind intr prog io
			= SearchFiles (IsDefPathname winpath) info Nil modpaths intr prog io;
	};

ReadDirectories :: !(List Pathname) !(List Pathname) !(List Pathname) !*Files -> (!List Pathname, !*Files);
ReadDirectories Nil Nil acc disk = (acc, disk);
ReadDirectories Nil prjs acc disk
	= ReadDirectories prjs Nil acc disk;
ReadDirectories (path:!rest) prjs acc disk
	| ok
		= ReadDirectories rest prjs acc` disk`;
		= (acc, disk1);
	{}{
	(ok,disk1)		= FOpenDir path disk;
	(disk2,acc`)	= ReadDirectory disk1 acc;
	(_,disk`)		= FCloseDir disk2;
	};

ReadDirectory :: !*Files !(List Pathname) -> (!*Files, !List Pathname);
ReadDirectory disk acc
	| not ok
		= (disk`,acc);
	| IsDefPathname fn
		= ReadDirectory disk` (fn :! acc);
		= ReadDirectory disk` acc;
	{}{
		(ok,fn,disk`)	= FGetPlainFileName disk;
	};

SearchIdentifiersInFiles :: !Bool !FindIdentInfo !(List Pathname) !(List Pathname) !Bool !ProgState !IO -> ProgIO;
SearchIdentifiersInFiles is_dcl_file info done modnames True prog io
	=	(prog,io);
SearchIdentifiersInFiles is_dcl_file info done Nil intr prog io
	=	ChangeMsgString True "" AP2 StopIntr prog io;
SearchIdentifiersInFiles is_dcl_file info done ("":!rest) intr prog io
	=	SearchIdentifiersInFiles is_dcl_file info done rest intr prog io;
SearchIdentifiersInFiles is_dcl_file info=:{search_kind,verbose,imp,cleanid,export_} done (modname:!rest) intr prog io
	| not is_dcl_file && StringOccurs modname done
		= SearchIdentifiersInFiles False info done rest intr prog io;
	| not is_dcl_file
		= ContIntr (SearchIdentifiersInFiles True info done (modname:!impsa)) AP2 write_found_positions;
		= ContIntr (SearchIdentifiersInFiles False info (modname:!done) impsa) AP2 write_found_positions;
	{}{
		modname`						| is_dcl_file = MakeDefPathname modname; = MakeImpPathname modname;
		(prog1,io2)						= ChangeMsgString verbose ("Searching '" +++ RemovePath modname` +++ "'") prog io;
		((prog2,impsa,found_positions), io3)	= accFiles (SearchIdentifiersInFile True rest cleanid modname` prog1) io2;
		found_in_file					= case found_positions of {PosNil -> False; _ -> True };

		write_found_positions
			| found_in_file
				= WriteFoundPositionsInErrorWindow search_kind modname` found_positions cleanid prog2 io3;
				= (prog2,io3);
	
		SearchIdentifiersInFile :: !Bool !(List Pathname) !String !Pathname !ProgState !Files -> ((!ProgState,!List Modulename,!IdentifierPositionList), Files);
		SearchIdentifiersInFile imp rest cleanid modname prog files
			| IsFullPathname modname
				= FindIdentifiersInFile imp rest cleanid modname prog files;
			| path<>EmptyPathname
				= FindIdentifiersInFile imp rest cleanid path prog1 files1;
				= ((prog1,rest,PosNil), files1);
			{}{
				((prog1,path), files1)	= GetFullPathname modname prog files;
			}
	};

/* Aux. function: Looks up the definition/implementation of the indicated identifier. */

SearchFiles :: !Bool !FindIdentInfo !(List Pathname) !(List Pathname) !Bool !ProgState !IO -> ProgIO;
SearchFiles is_dcl_file info done modnames True prog io
	=	(prog,io);
SearchFiles is_dcl_file info done Nil intr prog io
	=	ChangeMsgString True "Not Found" AP2 StopIntr prog io;
SearchFiles is_dcl_file info done ("":!rest) intr prog io
	=	SearchFiles is_dcl_file info done rest intr prog io;
SearchFiles is_dcl_file info=:{search_kind,verbose,imp,cleanid,export_} done (modname:!rest) intr prog io
	| is_dcl_file && StringOccurs modname done
		= SearchFiles is_dcl_file info done rest intr prog io;
	| not (found_in_file && (case search_kind of {Definition->is_dcl_file;_->not is_dcl_file}))
		= ContIntr (SearchFiles next_file_is_dcl_file info done` rest`) prog2 io2`;
	| not found3
		= ContIntr (SearchFiles next_file_is_dcl_file info done` rest`) prog3 io3;
		= OpenModule modname` tsel prog4 (CloseDialog DFindIdentID io4);
		{
			(prog4,io4)	= StopIntr prog3 io3;
			tsel = case found_positions of {
						(Pos line_n column_n _) -> {l1=line_n, c1=column_n,l2=line_n, c2=column_n + size cleanid}
					};
		}
	{
		modname`						| is_dcl_file = MakeDefPathname modname; = MakeImpPathname modname;
		(prog1,io2)						= ChangeMsgString verbose ("Searching '" +++ RemovePath modname` +++ "'") prog io;
		((prog2, impsa,found_positions), io2`)	= accFiles (SearchFile (imp==SearchImports) rest cleanid modname` prog1) io2;
		found_in_file					= case found_positions of {PosNil -> False; _ -> True };

		(prog3,io3,found3)				= check_exported prog2 io2`;

		check_exported prog io
			| not is_dcl_file && export_ && found_in_file
				= case found_positions of {
					PosNil -> (prog2,io2,False);
					_      -> (prog2,io2,True);
				}
				= (prog,io,True);
			where {
				(prog1,io1)	= ChangeMsgString verbose ("Searching '" +++ (RemovePath modname`) +++ "'") prog io;
				((prog2, _,found_positions),io2) = accFiles (SearchFile False Nil cleanid modname` prog1) io1;
			};

		search_icl_file_next	= found_in_file && is_dcl_file && (case search_kind of {Definition->False;_->True});
		next_file_is_dcl_file	= not search_icl_file_next;
		rest`					| search_icl_file_next
									= modname :! Nil;
								| not is_dcl_file
									= modname :! impsa;
									= impsa;
		done`					| is_dcl_file
									= modname :! done;
									= done;

		SearchFile :: !Bool !(List Pathname) !String !Pathname !ProgState !Files -> ((!ProgState, !List Modulename,!IdentifierPositionList), !Files);
		SearchFile imp rest cleanid modname prog files
			| IsFullPathname modname
				= FindDefinitionInFile imp rest cleanid modname prog files;
			| path<>EmptyPathname
				= FindDefinitionInFile imp rest cleanid path prog1 files1;
				= ((prog1,rest,PosNil),files1);
			{}{
				((prog1,path),files1)	= GetFullPathname modname prog files;
			}
	};

WriteFoundPositionsInErrorWindow :: !SearchKind !Modulename !IdentifierPositionList !String !ProgState !IO -> ProgIO;
WriteFoundPositionsInErrorWindow search_kind modname PosNil id prog io
	= (prog,io);
WriteFoundPositionsInErrorWindow search_kind modname (Pos line_n _ positions) id prog io
	=	WriteFoundPositionsInErrorWindow search_kind modname positions id
		AP2 UpdateInfoWindow ErrorWdID msgtxt prog io;
	{
		msgtxt	= Text_StringsToText (MakeFoundMsg search_kind modname (inc line_n) id :! Nil);
	};

/* Aux. function: sets current line to the line specified and selects it */

SetLineTo :: !Int !EditWdId !EditWindow !ProgState !IO -> ProgIO;
SetLineTo lnr wdid wd=:{wtext={nrlines}} prog=:{editor=ed=:{editwindows}} io
	| lnr < nrlines	= Edit_UpdateMenuItems prog` io`;
					= (prog, Alert io);
	where {
	(prog`,io`)	= DrawSelect True wdid wu prog1 io;
	prog1		= {prog & editor={ed & editwindows=SetWindow wdid wd` editwindows}};
	(wd`,wu)	= SelectLine wd lnr;
	};
	
/* Aux. function: performs the actual search and/or replace */

DoFind :: !Bool !TextReplaceFunction !ProgState !IO -> ProgIO;
DoFind flip find_and_replace prog=:{editor=ed=:{findinfo,editwindows}} io
	| str_empty
		= (prog,io);
	| fnd` || rpl
		= Edit_UpdateMenuItems prog2 io2; {
			io2 			| fnd` = io1; = Alert io1;
			(prog2,io1)		= DrawTextUpdate frontid ScrollHalfWin wu prog1 io;
		}
		= Edit_UpdateMenuItems prog1 (Alert io);
	where {
	prog1						= {prog & editor={ed & editwindows=SetFrontWindow front` editwindows}};
	front`		| rpl || fnd`	= {front1 & wstate = {front1.wstate & undoinfo = undoinfo`}};
								= front1;
	(front1,fnd,rpl,oldpos,wu)	= FindReplaceInText front finfo` find_and_replace;
	(frontid,front)				= GetFrontWindow editwindows;
	undoinfo`					= UndoInfoFindReplace oldpos wu;
	finfo`		| flip			= {findinfo & backw=not findinfo.backw};
								= findinfo;
	str_empty					= size finfo`.find == 0;
	fnd`						= fnd && otherplace;
	otherplace					= wu.oldsel.tsel.l1 <> wu.WindowUpdate.selection.tsel.l1 ||
									wu.oldsel.tsel.c1 <> wu.WindowUpdate.selection.tsel.c1 ||
										wu.oldsel.tsel.l2 <> wu.WindowUpdate.selection.tsel.l2 ||
											wu.oldsel.tsel.c2 <> wu.WindowUpdate.selection.tsel.c2;
	};

/* Determines the undo info after a find/replace */

UndoInfoFindReplace :: !PartTSel !WindowUpdate -> UndoInfo;
UndoInfoFindReplace oldpos {WindowUpdate | added=tsel=:{l1,c1,l2,c2}, removed}
	| c1 <> c2	= { EmptyUndo &	menu	= { undo = True, action = " Replace" },
								added	= tsel,
								removed	= {Removed | EmptyRemoved &
											before	= removed,
											lnr		= l1,
											cnr		= c1 } };
				= { EmptyUndo &	menu		= { undo = True, action = " Find" },
								clipcopied	= { EmptyClipCopied & pos = oldpos } };
								
/* Performes a find starting at the given line and char number  */

FindD :: !PartTSel !FindInfo !NrLines !Text -> (!Bool, !ReplacedAll, !PartTSel, !Text);
FindD textpos=:{l1,c1,l2,c2} finfo=:{backw} nrlines text
	| fnd	= (True, EmptyReplaced, fsel, text);
			= (False, EmptyReplaced, textpos, text);
	where {
	lnr | backw	= l1;
				= l2;
	cnr | backw	= c1;
				= c2;
	(fnd,fsel)	= Text_Find nrlines lnr cnr finfo text;
	};
