在Delphi或是许多其他的IDE中,都拥有代码完成的功能,按下“.”键就能调出某个对象的成员,方法等供选择
这么做极大的方便了程序的编写。如果要自己开发IDE,代码完成的功能是必不可少的
下面这段代码是我的 Rarnu Script 中所使用的,现在公开供各位学习之用。
procedure TFormMain.CodeCompleteExecute(Kind: SynCompletionType;
Sender: TObject; var CurrentInput: string; var x, y: integer;
var CanExecute: boolean);
var
cv: TfsClassVariable;
i, j: integer;
w: string;
p: TBufferCoord;
pg: TCodeSheet;
An: string;
insStr: string;
insFull: string;
coloredStr: string;
cName: string;
begin
pg := TCodeSheet(ppMain.ActivePage);
// set script compile environment
// this setting is to make all class which loaded usable.
// if has not Global Unit
// the script will not work normally.
Script.Clear;
Script.Lines.Text := pg.Memo.Text;
Script.Parent := fsGlobalUnit;
// get the word at cursor position
p := pg.Memo.CaretXY;
p.char := p.char - 2;
w := pg.Memo.GetWordAtRowCol(p);
// find pointed class
cv := Script.FindClass(w);
// if class not found
// it may be a variant object
// use the object name to get class name
// and then try to get class name again
if cv = nil then
begin
cName := GetClassNameByObjectName(w);
cv := Script.FindClass(cName);
end;
if cv = nil then
begin
cName := GetClassTypeByLine;
cv := Script.FindClass(cName);
end;
// clear popup member list
CodeComplete.ItemList.Clear;
CodeComplete.InsertList.Clear;
// get members
// Ancestor is the parent class
// if it is not empty or TObject
// do the loop as well
// for the class RTTI only listed the published
// and special members, not contains parents
// so must loop to top class
// and get all member of them.
if cv <> nil then
begin
while (cv.Ancestor <> EmptyStr) or (cv.Name = 'TObject') do
begin
// get parent class
An := cv.Ancestor;
// loop for members
for i := 0 to cv.MembersCount - 1 do
begin
insStr := EmptyStr;
insFull := EmptyStr;
coloredStr := EmptyStr;
// use regular expression to create colors
// the colors are used to pointer the type of member
// use Members.Typ to get type.
case cv.Members[i].Typ of
fvtClass:
coloredStr := '/color{clblack}/style{+B}';
fvtArray:
coloredStr := '/color{clgreen}';
fvtEnum:
coloredStr := '/color{clHighlight}';
fvtConstructor:
coloredStr := '/color{clred}/style{+B}';
else
coloredStr := '/color{clblack}';
end;
// if not in upon
// check need result or not
// the needed member set to navy
if (coloredStr = '/color{clblack}') and (cv.Members[i].NeedResult) then
coloredStr := '/color{clnavy}';
// if contains params
if cv.Members[i].Count > 0 then
begin
insStr := cv.Members[i].Name;
insFull := cv.Members[i].Name + '(';
for j := 0 to cv.Members[i].Count - 1 do
begin
// add param name and type to member line.
insFull := insFull + cv.Members[i].Params[j].Name + ': ' +
cv.Members[i].Params[j].TypeName;
// if is not the last param
// add comma behind
if j < cv.Members[i].Count - 1 then
begin
insFull := insFull + '; ';
end;
end;
insFull := insFull + ')';
// use colored member line
coloredStr := coloredStr + insFull;
end
else
begin
// if has no param
insStr := cv.Members[i].Name;
coloredStr := coloredStr + insStr;
end;
// if member line not exists
// add it to popup list
if CodeComplete.InsertList.IndexOf(insStr) = -1 then
begin
CodeComplete.InsertList.Add(insStr);
CodeComplete.ItemList.Add(coloredStr);
end;
end;
// find parant class
cv := Script.FindClass(an);
if cv = nil then
Break;
end;
end;
// if popup list is not empty
// allow popup else not allowed
if CodeComplete.ItemList.Count = 0 then
CanExecute := False
else
CanExecute := True;
end;