{ This file is part of the Free Pascal Integrated Development Environment Copyright (c) 1998 by Berczi Gabor Code Complete routines See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} unit FPCodCmp; { CodeComplete } interface uses Objects,Drivers,Dialogs, WEditor,WUtils,WViews; type PCodeCompleteWordList = ^TCodeCompleteWordList; TCodeCompleteWordList = object(TTextCollection) end; PCodeCompleteDialog = ^TCodeCompleteDialog; TCodeCompleteDialog = object(TCenterDialog) constructor Init; function Execute: Word; virtual; procedure HandleEvent(var Event: TEvent); virtual; private CodeCompleteLB : PAdvancedListBox; RB : PRadioButtons; CB : PCheckBoxes; MinInputL,InputL : PEditorInputLine; procedure Add; procedure Edit; procedure Delete; end; function FPCompleteCodeWord(const WordS: string; var Text: string): boolean; procedure InitCodeComplete; function LoadCodeComplete(var S: TStream): boolean; procedure AddStandardUnitsToCodeComplete; procedure AddAvailableUnitsToCodeComplete(OnlyStandard : boolean); function StoreCodeComplete(var S: TStream): boolean; procedure DoneCodeComplete; const CodeCompleteWords : PCodeCompleteWordList = nil; type TCodeCompleteCase = (ccc_unchanged, ccc_lower, ccc_upper, ccc_mixed); const CodeCompleteCase : TCodeCompleteCase = ccc_unchanged; UnitsCodeCompleteWords : PCodeCompleteWordList = nil; procedure RegisterCodeComplete; implementation uses App,Views,MsgBox,Validate, FVConsts, systems, BrowCol, FPSwitch, FPCompil, FPVars, FPSymbol, FPConst,FPViews; {$ifndef NOOBJREG} const RCodeCompleteWordList: TStreamRec = ( ObjType: 14401; VmtLink: Ofs(TypeOf(TCodeCompleteWordList)^); Load: @TCodeCompleteWordList.Load; Store: @TCodeCompleteWordList.Store ); {$endif} {$ifdef useresstrings} resourcestring {$else} const {$endif} { CodeComplete dialog } dialog_codecomplete = 'CodeComplete'; label_codecomplete_keywords = '~K~eywords'; dialog_codecomplete_add = 'Add new keyword'; label_codecomplete_add_keyword = 'Keyword'; dialog_codecomplete_edit = 'Edit keyword'; label_codecomplete_edit_keyword = 'Keyword'; msg_codecomplete_alreadyinlist = '"%s" is already in the list'; { standard button texts } button_OK = 'O~K~'; button_Cancel = 'Cancel'; button_New = '~N~ew'; button_Edit = '~E~dit'; button_Delete = '~D~elete'; function FPCompleteCodeWord(const WordS: string; var Text: string): boolean; var OK: boolean; CIndex, Index, i : sw_integer; St, UpWordS : string; begin if ShowOnlyUnique then UpWordS:=UpCaseStr(WordS); OK:=Assigned(CodeCompleteWords); if OK then begin Text:=CodeCompleteWords^.Lookup(WordS,CIndex); OK:=(CIndex<>-1) and (length(Text)<>length(WordS)); Index:=-1; if OK and ShowOnlyUnique and (CIndexUpcase(Text[Length(UpWordS)+1]) then} begin Text:=''; FPCompleteCodeWord:=false; exit; (* end else { only give the common part } begin i:=Length(UpWordS)+1; while (i<=length(st)) and (i<=length(text)) and (UpCase(st[i])=Upcase(Text[i])) do inc(i); SetLength(Text,i-1); *) end; end; end; end; if (ShowOnlyUnique or not OK) and Assigned(UnitsCodeCompleteWords) then begin Text:=UnitsCodeCompleteWords^.Lookup(WordS,Index); OK:=(Index<>-1) and (length(Text)<>length(WordS)); if ShowOnlyUnique and (IndexUpcase(Text[Length(UpWordS)+1]) then} begin Text:=''; FPCompleteCodeWord:=false; exit; (* end else { only give the common part } begin i:=Length(UpWordS)+1; while (i<=length(st)) and (i<=length(text)) and (UpCase(st[i])=Upcase(Text[i])) do inc(i); SetLength(Text,i-1); *) end; end; end; end; if ShowOnlyUnique and (Index<>-1) and (CIndex<>-1) then begin {St:=PString(CodeCompleteWords^.At(CIndex+1))^; Was wrong, CIndex+1 could be above count => collection.error generated RTE 213 if UpCase(st[Length(UpWordS)+1])<>Upcase(Text[Length(UpWordS)+1]) then} begin Text:=''; FPCompleteCodeWord:=false; exit; (* end else { only give the common part } begin i:=Length(UpWordS)+1; while (i<=length(st)) and (i<=length(text)) and (UpCase(st[i])=Upcase(Text[i])) do inc(i); SetLength(Text,i-1); *) end; end; if OK=false then Text:='' else case CodeCompleteCase of ccc_upper : Text:=UpcaseStr(Text); ccc_lower : Text:=LowcaseStr(Text); ccc_mixed : Text:=UpCase(Text[1])+LowCaseStr(Copy(Text,2,High(Text))); end; FPCompleteCodeWord:=OK; end; procedure InitCodeComplete; var I:integer; S: string; begin if Assigned(CodeCompleteWords) then Dispose(CodeCompleteWords, Done); New(CodeCompleteWords, Init(10,10)); for I:=0 to GetReservedWordCount-1 do begin S:=LowCaseStr(GetReservedWord(I)); if length(S)>=CodeCompleteMinLen then CodeCompleteWords^.Insert(NewStr(S)); end; { there should be also a user front-end for customizing CodeComplete ! any volunteers to implement? ;) - Gabor } end; procedure AddAvailableUnitsToCodeComplete(OnlyStandard : boolean); var I : sw_integer; Overflow: boolean; Level : longint; UpStandardUnits : string; procedure InsertInS(P: PSymbol); procedure InsertItemsInS(P: PSymbolCollection); var I: Sw_integer; begin for I:=0 to P^.Count-1 do InsertInS(P^.At(I)); end; Var st : string; CIndex : sw_integer; begin Inc(level); if UnitsCodeCompleteWords^.Count=MaxCollectionSize then begin Overflow:=true; Exit; end; st:=P^.GetName; if Length(st)>=CodeCompleteMinLen then if not ((level=1) and OnlyStandard and (st=UpCaseStr(CodeCompleteUnitName))) then begin st:=Lowcasestr(st); UnitsCodeCompleteWords^.LookUp(st,CIndex); if CIndex<>-1 then UnitsCodeCompleteWords^.Insert(NewStr(st)); end; { this is wrong because it inserted args or locals of proc in the globals list !! PM} if (P^.Items<>nil) and (level=1) and ((not OnlyStandard or (Pos(P^.GetName+',',UpStandardUnits)>0) or { don't exclude system unit ... } (Pos('SYS',P^.GetName)>0))) then InsertItemsInS(P^.Items); Dec(level); end; begin if OnlyStandard then UpStandardunits:=UpCaseStr(StandardUnits)+','; if IsSymbolInfoAvailable then begin if Assigned(UnitsCodeCompleteWords) then begin Dispose(UnitsCodeCompleteWords,done); UnitsCodeCompleteWords:=nil; end; New(UnitsCodeCompleteWords, Init(10,10)); level:=0; Overflow:=false; BrowCol.Modules^.ForEach(@InsertInS); { if Overflow then WarningBox(msg_toomanysymbolscantdisplayall,nil); } end; end; procedure AddStandardUnitsToCodeComplete; var HiddenSource : PSourceWindow; R : TRect; StoreBrowserSwitchesConfig : string; begin Desktop^.GetExtent(R); New(HiddenSource,init(R,'*')); HiddenSource^.NoNameCount:=0; HiddenSource^.UpdateTitle; HiddenSource^.Hide; CompilingHiddenFile:=HiddenSource; { compile a dummy file to get symbol info } with HiddenSource^.Editor^ do begin FileName:=CodeCompleteUnitName+'.pp'; Addline('unit '+CodeCompleteUnitName+';'); Addline('interface'); if StandardUnits<>'' then begin AddLine('uses'); Addline(StandardUnits); Addline(' ;'); end; Addline('implementation'); Addline('end.'); SetModified(true); // SaveFile; end; StoreBrowserSwitchesConfig:=BrowserSwitches^.GetCurrSelParam; BrowserSwitches^.ReadItemsCfg('+'); DoCompile(cCompile); BrowserSwitches^.SetCurrSelParam(StoreBrowserSwitchesConfig); AddAvailableUnitsToCodeComplete(true); { Now add the interface declarations to the Code Complete list } CompilingHiddenFile:=nil; Dispose(HiddenSource,Done); end; function LoadCodeComplete(var S: TStream): boolean; var C: PCodeCompleteWordList; OK: boolean; NewCodeCompleteMinLen : byte; NewUseStandardUnitsInCodeComplete, NewUseAllUnitsInCodeComplete, NewShowOnlyUnique : boolean; NewCodeCompleteCase : TCodeCompleteCase; StPtr : PString; begin New(C, Load(S)); OK:=Assigned(C) and (S.Status=stOk); if OK then begin if Assigned(CodeCompleteWords) then Dispose(CodeCompleteWords, Done); CodeCompleteWords:=C; S.Read(NewCodeCompleteCase,Sizeof(TCodeCompleteCase)); OK:=(S.Status=stOk); if OK then CodeCompleteCase:=NewCodeCompleteCase; { Old version of Code complete, also OK PM } if not OK or (S.getPos=S.getSize) then begin LoadCodeComplete:=OK; exit; end; if S.Status=stOK then S.Read(NewUseStandardUnitsInCodeComplete,Sizeof(UseStandardUnitsInCodeComplete)); if S.Status=stOK then UseStandardUnitsInCodeComplete:=NewUseStandardUnitsInCodeComplete; if S.Status=stOK then S.Read(NewUseAllUnitsInCodeComplete,Sizeof(UseAllUnitsInCodeComplete)); if S.Status=stOK then UseAllUnitsInCodeComplete:=NewUseAllUnitsInCodeComplete; if S.Status=stOK then S.Read(NewShowOnlyUnique,Sizeof(ShowOnlyUnique)); if S.Status=stOK then ShowOnlyUnique:=NewShowOnlyUnique; if S.Status=stOK then S.Read(NewCodeCompleteMinLen,Sizeof(CodeCompleteMinLen)); if S.Status=stOK then CodeCompleteMinLen:=NewCodeCompleteMinLen; if S.Status=stOK then StPtr:=S.ReadStr else StPtr:=nil; if (S.Status=stOK) then StandardUnits:=GetStr(StPtr); if assigned(StPtr) then FreeMem(StPtr,Length(StandardUnits)+1); OK:=S.Status=stOK; end else if Assigned(C) then Dispose(C, Done); LoadCodeComplete:=OK; end; function StoreCodeComplete(var S: TStream): boolean; var OK: boolean; begin OK:=Assigned(CodeCompleteWords); if OK then begin CodeCompleteWords^.Store(S); S.Write(CodeCompleteCase,Sizeof(TCodeCompleteCase)); { New fields added } S.Write(UseStandardUnitsInCodeComplete,Sizeof(UseStandardUnitsInCodeComplete)); S.Write(UseAllUnitsInCodeComplete,Sizeof(UseAllUnitsInCodeComplete)); S.Write(ShowOnlyUnique,Sizeof(ShowOnlyUnique)); S.Write(CodeCompleteMinLen,Sizeof(CodeCompleteMinLen)); S.WriteStr(@StandardUnits); OK:=OK and (S.Status=stOK); end; StoreCodeComplete:=OK; end; procedure DoneCodeComplete; begin if Assigned(CodeCompleteWords) then begin Dispose(CodeCompleteWords, Done); CodeCompleteWords:=nil; end; if Assigned(UnitsCodeCompleteWords) then begin Dispose(UnitsCodeCompleteWords,done); UnitsCodeCompleteWords:=nil; end; end; constructor TCodeCompleteDialog.Init; var R,R2,R3: TRect; Items: PSItem; SB: PScrollBar; begin R.Assign(0,0,50,22); inherited Init(R,dialog_codecomplete); HelpCtx:=hcCodeCompleteOptions; { name list dialog } GetExtent(R); R.Grow(-3,-2); Inc(R.A.Y); R3.Copy(R); Dec(R.B.X,12); Dec(R.B.Y,7); R2.Copy(R); R2.Move(1,0); R2.A.X:=R2.B.X-1; New(SB, Init(R2)); Insert(SB); New(CodeCompleteLB, Init(R,1,SB)); Insert(CodeCompleteLB); R2.Copy(R); R2.Move(0,-1); R2.B.Y:=R2.A.Y+1; Dec(R2.A.X); Insert(New(PLabel, Init(R2, label_codecomplete_keywords, CodeCompleteLB))); { Case choice } R.Copy(R3); Dec(R.B.Y,2); R.A.Y:=R.B.Y-4; Inc(R.A.X); R.B.X:=R.A.X+15; Items:=NewSItem('Unc~h~anged', NewSItem('~L~ower', NewSItem('~U~pper', NewSItem('~M~ixed',nil)))); RB:=New(PRadioButtons,Init(R,Items)); RB^.SetData(ord(CodeCompleteCase)); R2.Copy(R); R2.Move(0,-1); R2.B.Y:=R2.A.Y+1; Dec(R2.A.X); Insert(New(PLabel, Init(R2, 'Case handling', RB))); Insert(RB); { Mininum length inputline } R.Copy(R3); R.A.Y:=R.B.Y-7;R.B.Y:=R.A.Y+1; Dec(R.B.X); R.A.X:=R.B.X -5; New(MinInputL, Init(R,5)); MinInputL^.SetValidator(New(PRangeValidator, Init(1,255))); Insert(MinInputL); R2.Copy(R); R2.A.X:=20;Dec(R2.B.X,5); Insert(New(PLabel, Init(R2, 'Min. length', MinInputL))); { Standard/all units booleans } Items:=nil; Items:=NewSItem('Add standard units', Items); Items:=NewSItem('Add all units', Items); Items:=NewSItem('Show only unique', Items); R.Copy(R3); R.A.Y:=R.B.Y-5;R.B.Y:=R.A.Y+3; Inc(R.A.X,18); Dec(R.B.X); New(CB, Init(R, Items)); Insert(CB); R2.Copy(R); R2.Move(0,-1); R2.B.Y:=R2.A.Y+1; Dec(R2.A.X); Insert(New(PLabel, Init(R2, 'Unit handling', CB))); R2.Copy(R); R2.Move(0,-1); R2.B.Y:=R2.A.Y+1; If ShowOnlyUnique then CB^.Press(0); If UseAllUnitsInCodeComplete then CB^.Press(1); If UseStandardUnitsInCodeComplete then CB^.Press(2); { Standard unit name boolean } R.Copy(R3); R.A.Y:=R.B.Y-1; Inc(R.A.X); Dec(R.B.X); New(InputL,Init(R,255)); Insert(InputL); InputL^.SetValidator(New(PFilterValidator,Init(NumberChars+AlphaChars+[',']))); R2.Copy(R); R2.Move(0,-1); R2.B.Y:=R2.A.Y+1; Dec(R2.A.X);R2.B.X:=R2.A.X+25; Insert(New(PLabel, Init(R2, '~S~tandard unit list', InputL))); R.Copy(R3); R.A.X:=R.B.X-10; R.B.Y:=R.A.Y+2; Insert(New(PButton, Init(R, button_OK, cmOK, bfNormal))); R.Move(0,2); Insert(New(PButton, Init(R, button_Edit, cmEditItem, bfDefault))); R.Move(0,2); Insert(New(PButton, Init(R, button_New, cmAddItem, bfNormal))); R.Move(0,2); Insert(New(PButton, Init(R, button_Delete, cmDeleteItem, bfNormal))); R.Move(0,2); Insert(New(PButton, Init(R, button_Cancel, cmCancel, bfNormal))); SelectNext(false); end; procedure TCodeCompleteDialog.HandleEvent(var Event: TEvent); var DontClear: boolean; begin case Event.What of evKeyDown : begin DontClear:=false; case Event.KeyCode of kbIns : Message(@Self,evCommand,cmAddItem,nil); kbDel : Message(@Self,evCommand,cmDeleteItem,nil); else DontClear:=true; end; if DontClear=false then ClearEvent(Event); end; evBroadcast : case Event.Command of cmListItemSelected : if Event.InfoPtr=pointer(CodeCompleteLB) then Message(@Self,evCommand,cmEditItem,nil); end; evCommand : begin DontClear:=false; case Event.Command of cmAddItem : Add; cmDeleteItem : Delete; cmEditItem : Edit; else DontClear:=true; end; if DontClear=false then ClearEvent(Event); end; end; inherited HandleEvent(Event); end; function TCodeCompleteDialog.Execute: Word; var R: word; C: PCodeCompleteWordList; NewVal, I: integer; NewValStr : string; begin New(C, Init(10,20)); if Assigned(CodeCompleteWords) then for I:=0 to CodeCompleteWords^.Count-1 do C^.Insert(NewStr(GetStr(CodeCompleteWords^.At(I)))); CodeCompleteLB^.NewList(C); InputL^.SetData(StandardUnits); NewValStr:=IntToStr(CodeCompleteMinLen); MinInputL^.SetData(NewValStr); R:=inherited Execute; if R=cmOK then begin if Assigned(CodeCompleteWords) then Dispose(CodeCompleteWords, Done); CodeCompleteWords:=C; CodeCompleteCase:=TCodeCompleteCase(RB^.Value); MinInputL^.GetData(NewValStr); NewVal:=StrToInt(NewValStr); if (NewVal>0) and (NewVal<>CodeCompleteMinLen) then begin CodeCompleteMinLen:=NewVal; InitCodeComplete; end; ShowOnlyUnique:=CB^.Mark(0); UseAllUnitsInCodeComplete:=CB^.Mark(1); UseStandardUnitsInCodeComplete:=CB^.Mark(2); if UseStandardUnitsInCodeComplete and (not UseAllUnitsInCodeComplete or not assigned(UnitsCodeCompleteWords)) and ((StandardUnits<>GetStr(InputL^.Data)) or not assigned(UnitsCodeCompleteWords)) then begin InputL^.GetData(StandardUnits); AddStandardUnitsToCodeComplete; end else InputL^.GetData(StandardUnits); end else Dispose(C, Done); Execute:=R; end; procedure TCodeCompleteDialog.Add; var IC: boolean; S: string; P: PString; Cmd: word; CanExit: boolean; I: sw_integer; begin IC:=CodeCompleteLB^.Range=0; if IC=false then S:=GetStr(CodeCompleteLB^.List^.At(CodeCompleteLB^.Focused)) else S:=''; repeat Cmd:=InputBox(dialog_codecomplete_add,label_codecomplete_add_keyword,S,255); CanExit:=Cmd<>cmOK; if CanExit=false then begin CanExit:=PCodeCompleteWordList(CodeCompleteLB^.List)^.Search(@S,I)=false; if CanExit=false then begin ClearFormatParams; AddFormatParamStr(S); ErrorBox(msg_codecomplete_alreadyinlist,@FormatParams); end; end; until CanExit; if Cmd=cmOK then begin P:=NewStr(S); with CodeCompleteLB^ do begin List^.Insert(P); SetRange(List^.Count); SetFocusedItem(P); end; ReDraw; end; end; procedure TCodeCompleteDialog.Edit; var S: string; I,T: sw_integer; Cmd: word; CanExit: boolean; P: PString; begin if CodeCompleteLB^.Range=0 then Exit; I:=CodeCompleteLB^.Focused; S:=GetStr(CodeCompleteLB^.List^.At(I)); repeat Cmd:=InputBox(dialog_codecomplete_edit,label_codecomplete_edit_keyword,S,255); CanExit:=Cmd<>cmOK; if CanExit=false then begin CanExit:=PCodeCompleteWordList(CodeCompleteLB^.List)^.Search(@S,T)=false; CanExit:=CanExit or (T=I); if CanExit=false then begin ClearFormatParams; AddFormatParamStr(S); ErrorBox(msg_codecomplete_alreadyinlist,@FormatParams); end; end; until CanExit; if Cmd=cmOK then begin P:=NewStr(S); with CodeCompleteLB^ do begin List^.AtFree(I); List^.Insert(P); SetFocusedItem(P); end; ReDraw; end; end; procedure TCodeCompleteDialog.Delete; begin if CodeCompleteLB^.Range=0 then Exit; CodeCompleteLB^.List^.AtFree(CodeCompleteLB^.Focused); CodeCompleteLB^.SetRange(CodeCompleteLB^.List^.Count); ReDraw; end; procedure RegisterCodeComplete; begin {$ifndef NOOBJREG} RegisterType(RCodeCompleteWordList); {$endif} end; END.