fpc/packages/ide/fpcodcmp.pas
Michaël Van Canneyt 98ea5cddda * PChar -> PAnsichar
2023-07-15 18:22:37 +02:00

668 lines
19 KiB
ObjectPascal

{
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 }
{$ifdef cpullvm}
{$modeswitch nestedprocvars}
{$endif}
{$H-}
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 (CIndex<CodeCompleteWords^.Count-1) then
begin
St:=PString(CodeCompleteWords^.At(CIndex+1))^;
if (UpCaseStr(Copy(St,1,length(WordS)))=UpWordS) then
begin
{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;
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 (Index<UnitsCodeCompleteWords^.Count-1) then
begin
St:=PString(UnitsCodeCompleteWords^.At(Index+1))^;
if UpCaseStr(Copy(St,1,length(WordS)))=UpWordS then
begin
{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;
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(TCallbackProcParam(@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.