CodeCompletion.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. unit CodeCompletion;
  2. interface
  3. uses SysUtils, SourceFileUnit, Dialogs, Classes, MainUnit;
  4. const
  5. DefautDesc = '';
  6. CCFVersion: Byte = 2;
  7. // Zero: Cardinal = 0;
  8. type
  9. TOnNeedFile = procedure(const FileName: string; out CCFData: string) of object;
  10. TCompletionItem = class(TObject)
  11. public
  12. Left, Right: string;
  13. Description: string;
  14. Line: Cardinal;
  15. // Not Saved
  16. SourceFileName: string;
  17. procedure ReadFromStream(F: TStream);
  18. procedure WriteToStream(F: TStream);
  19. end;
  20. TCompletionList = class(TStringList)
  21. protected
  22. FIncluded: TStringList;
  23. FOnNeed: TOnNeedFile;
  24. function aGetObject(Index: Integer): TCompletionItem;
  25. procedure aSetObject(Index: Integer; const Value: TCompletionItem);
  26. public
  27. constructor Create; reintroduce;
  28. destructor Destroy; override;
  29. function AddObject(const Symbol: string; Info: TCompletionItem): Integer; reintroduce;
  30. procedure Clear; override;
  31. property Completion[Index: Integer]: TCompletionItem read aGetObject write aSetObject;
  32. procedure AddCompletion(CCFData: TStream); overload;
  33. procedure AddCompletion(const CCFData: string); overload;
  34. // Completion state
  35. property IncludedFiles: TStringList read FIncluded;
  36. // When a file is needed
  37. property OnNeedFile: TOnNeedFile read FOnNeed write FOnNeed;
  38. end;
  39. function ReadString(F: TStream; var S: string): Boolean;
  40. function WriteString(F: TStream; const S: string): Boolean;
  41. function FileToString(const FileName: string): string;
  42. // Remove comments from a C Source
  43. function GetIdent(const S: string; var aPos: Integer): string;
  44. procedure FilterComment(Src, Dst: TStrings);
  45. // Parse a C Source
  46. function MakeCCF_C(const UnitName: string; Src: TSourceTextSourceFile): string;
  47. // Parse an Header Source
  48. function MakeCCF_H(const UnitName: string; Lines: TStrings; FilterTo: TStrings = nil): string;
  49. implementation
  50. uses StrUtils, UtilsDos;
  51. // Routines
  52. function GetIdent(const S: string; var aPos: Integer): string;
  53. var
  54. i, l: Integer;
  55. begin
  56. i := aPos;
  57. l := Length(S);
  58. while (i <= l) and (S[i] = ' ') do
  59. Inc(i);
  60. while (i <= l) and (S[i] in ['a'..'z', 'A'..'Z', '0'..'9', '_', '#']) do
  61. Inc(i);
  62. Result := Copy(S, aPos, i - aPos);
  63. aPos := i;
  64. end;
  65. function ReadString(F: TStream; var S: string): Boolean;
  66. var
  67. B: Byte;
  68. begin
  69. if F.Read(B, 1) = 1 then
  70. begin
  71. Setlength(S, B);
  72. Result := F.Read(S[1], B) = B;
  73. end
  74. else
  75. begin
  76. Result := False;
  77. S := '';
  78. end;
  79. end;
  80. function WriteString(F: TStream; const S: string): Boolean;
  81. var
  82. B: Byte;
  83. begin
  84. B := Length(S);
  85. Result := False;
  86. if F.Write(B, 1) = 1 then
  87. Result := F.Write(S[1], B) = B;
  88. end;
  89. function FileToString(const FileName: string): string;
  90. var
  91. F: TFileStream;
  92. c1, c2: Integer;
  93. begin
  94. Result := '';
  95. try
  96. F := TFileStream.Create(FileName, fmOpenRead);
  97. try
  98. c1 := F.Size;
  99. SetLength(Result, c1);
  100. c2 := F.Read(Result[1], c1);
  101. if c1 <> c2 then
  102. SetLength(Result, c2);
  103. finally
  104. F.Free;
  105. end;
  106. except
  107. end;
  108. end;
  109. procedure FilterComment(Src, Dst: TStrings);
  110. var
  111. S, S2: string;
  112. i, p: Integer;
  113. InComment: Boolean;
  114. begin
  115. InComment := False;
  116. for i := 0 to Src.Count - 1 do
  117. begin
  118. S := Trim(Src[i]);
  119. // Check end of comment
  120. if InComment then
  121. begin
  122. p := Pos('*/', S);
  123. if p = 0 then
  124. begin
  125. Dst.Add('');
  126. Continue;
  127. end
  128. else
  129. begin
  130. InComment := False;
  131. S := Copy(S, p + 2, MaxInt);
  132. end;
  133. end;
  134. // Check line comment
  135. p := Pos('//', S);
  136. if p = 1 then
  137. begin
  138. Dst.Add('');
  139. Continue;
  140. end
  141. else if p <> 0 then
  142. S := Copy(S, 1, p - 1);
  143. // Check start of comment
  144. p := Pos('/*', S);
  145. if p <> 0 then
  146. begin
  147. S2 := S;
  148. S := Copy(S, 1, p - 1);
  149. p := PosEx('*/', S2, p + 2);
  150. InComment := p = 0;
  151. if not InComment then
  152. begin
  153. InComment := False;
  154. S := S + Copy(S2, p + 2, MaxInt);
  155. end;
  156. end;
  157. // Add result
  158. Dst.Add(S);
  159. end;
  160. end;
  161. procedure ParseIncludes(Src, Dst: TStrings);
  162. var
  163. i, p: Integer;
  164. Line: string;
  165. begin
  166. // find line which starts with '#include'
  167. for i := 0 to Src.Count - 1 do
  168. begin
  169. Line := Trim(Src[i]);
  170. if AnsiStartsText('#include', Line) then
  171. begin
  172. p := Pos('"', Line);
  173. if P <> 0 then
  174. begin
  175. Line := Copy(Line, p + 1, MaxInt);
  176. Line := Copy(Line, 1, Pos('"', Line) - 1);
  177. Dst.Add(Trim(Line));
  178. end
  179. else
  180. begin
  181. p := Pos('<', Line);
  182. if P <> 0 then
  183. begin
  184. Line := Copy(Line, p + 1, MaxInt);
  185. Line := Copy(Line, 1, Pos('>', Line) - 1);
  186. Dst.Add(Trim(Line));
  187. end;
  188. end;
  189. end;
  190. end;
  191. end;
  192. function MakeCCF_C(const UnitName: string; Src: TSourceTextSourceFile): string;
  193. var
  194. Dst: TMemoryStream;
  195. // Parse files included in the Source unit
  196. procedure ParseInc;
  197. var
  198. Included: TStringList;
  199. w: Word;
  200. i: Integer;
  201. Source: TStrings;
  202. begin
  203. Source := TStringList.Create;
  204. FilterComment(Src.SourceEditor.Lines, Source);
  205. Included := TStringList.Create;
  206. Included.Sorted := True;
  207. Included.Duplicates := dupIgnore;
  208. ParseIncludes(Source, Included);
  209. w := Included.Count;
  210. // write result to stream
  211. Dst.Write(w, 2);
  212. for i := 0 to Included.Count - 1 do
  213. WriteString(Dst, Included[i]);
  214. Included.Free;
  215. Source.Free;
  216. end;
  217. // Parse Functions
  218. procedure ParseFuncs;
  219. var
  220. Funcs: TSourceFileFunctions;
  221. Func: TSourceFileFunction;
  222. i, p, c, l: Integer;
  223. S: string;
  224. Source: TStrings;
  225. begin
  226. Source := Src.SourceEditor.Lines;
  227. Funcs := Src.GetFunctions;
  228. c := Source.Count;
  229. for i := 0 to Length(Funcs) - 1 do
  230. begin
  231. Func := Funcs[i];
  232. if Func.Name <> '' then
  233. begin
  234. if (Func.ImplementationLine > 0) and (Func.ImplementationLine <= c) then
  235. l := Func.ImplementationLine - 1
  236. else if (Func.PrototypeLine > 0) and (Func.PrototypeLine <= c) then
  237. l := Func.PrototypeLine - 1
  238. else
  239. l := -1;
  240. if l > -1 then
  241. S := Source[l];
  242. if S <> '' then
  243. begin
  244. p := Pos(Func.Name, S);
  245. WriteString(Dst, Func.Name);
  246. Dst.Write(l, 4);
  247. WriteString(Dst, '<c:Teal>func <c:Blue>' + Trim(Copy(S, 1, p - 1)));
  248. WriteString(Dst, Trim(Copy(S, p + Length(Func.Name), MaxInt)));
  249. WriteString(Dst, DefautDesc);
  250. end;
  251. end;
  252. end;
  253. end;
  254. begin
  255. Dst := TMemoryStream.Create;
  256. Dst.Write(CCFVersion, SizeOf(CCFVersion));
  257. WriteString(Dst, UnitName);
  258. ParseInc;
  259. ParseFuncs;
  260. SetString(Result, PChar(Dst.Memory), Dst.Size);
  261. Dst.Free;
  262. end;
  263. function MakeCCF_H(const UnitName: string; Lines: TStrings; FilterTo: TStrings = nil): string;
  264. var
  265. Dst: TMemoryStream;
  266. Source: TStrings;
  267. // Parse files included in the Source unit
  268. procedure ParseInc;
  269. var
  270. Included: TStringList;
  271. w: Word;
  272. i: Integer;
  273. begin
  274. Included := TStringList.Create;
  275. Included.Sorted := True;
  276. Included.Duplicates := dupIgnore;
  277. ParseIncludes(Source, Included);
  278. w := Included.Count;
  279. // write result to stream
  280. Dst.Write(w, 2);
  281. for i := 0 to Included.Count - 1 do
  282. WriteString(Dst, Included[i]);
  283. Included.Free;
  284. end;
  285. procedure ParseHeader;
  286. var
  287. i, p, c: Integer;
  288. S, S2, Ident: string;
  289. begin
  290. c := Source.Count;
  291. i := 0;
  292. while i < c do
  293. begin
  294. S := Source[i];
  295. if AnsiStartsText('#define', S) then
  296. begin
  297. p := 9;
  298. Ident := GetIdent(S, p);
  299. S2 := Trim(Copy(S, p, MaxInt));
  300. if S2 <> '' then
  301. begin
  302. WriteString(Dst, Ident);
  303. Dst.Write(i, 4);
  304. WriteString(Dst, '<c:Green>macro');
  305. WriteString(Dst, S2);
  306. WriteString(Dst, '');
  307. end
  308. end
  309. else
  310. begin
  311. // terminer cette partie du code :)
  312. end;
  313. Inc(i);
  314. end;
  315. end;
  316. begin
  317. Dst := TMemoryStream.Create;
  318. Dst.Write(CCFVersion, SizeOf(CCFVersion));
  319. if Assigned(FilterTo) then
  320. Source := FilterTo
  321. else
  322. Source := TStringList.Create;
  323. Source.Clear;
  324. FilterComment(Lines, Source);
  325. WriteString(Dst, UnitName);
  326. ParseInc;
  327. ParseHeader;
  328. SetString(Result, PChar(Dst.Memory), Dst.Size);
  329. if FilterTo = nil then
  330. Source.Free;
  331. Dst.Free;
  332. end;
  333. { TCompletionList }
  334. constructor TCompletionList.Create;
  335. begin
  336. inherited;
  337. CaseSensitive := True;
  338. Sorted := True;
  339. Duplicates := dupAccept;
  340. FIncluded := TStringList.Create;
  341. FIncluded.Sorted := True;
  342. FIncluded.Duplicates := dupIgnore;
  343. end;
  344. destructor TCompletionList.Destroy;
  345. begin
  346. Clear;
  347. inherited;
  348. end;
  349. function TCompletionList.AddObject(const Symbol: string;
  350. Info: TCompletionItem): Integer;
  351. begin
  352. Result := inherited AddObject(Symbol, Info);
  353. end;
  354. procedure TCompletionList.Clear;
  355. var
  356. i: Integer;
  357. begin
  358. IncludedFiles.Clear;
  359. for i := 0 to Count - 1 do
  360. Objects[i].Free;
  361. inherited;
  362. end;
  363. function TCompletionList.aGetObject(Index: Integer): TCompletionItem;
  364. begin
  365. Result := TCompletionItem(Objects[Index]);
  366. end;
  367. procedure TCompletionList.aSetObject(Index: Integer;
  368. const Value: TCompletionItem);
  369. begin
  370. Objects[Index] := Value;
  371. end;
  372. procedure TCompletionList.AddCompletion(CCFData: TStream);
  373. var
  374. aStream: TStream;
  375. UnitName, S1, S2: string;
  376. i, w: Word;
  377. C: TCompletionItem;
  378. B: Byte;
  379. begin
  380. if (CCFData.Read(B, 1) <> 1) or (B <> CCFVersion) then
  381. Exit; // Wrong Version
  382. if ReadString(CCFData, UnitName) and (IncludedFiles.IndexOf(UnitName) = -1) and (CCFData.Read(w, 2) = 2) then
  383. begin
  384. IncludedFiles.Add(UnitName);
  385. // Check for included files
  386. for i := 1 to w do
  387. begin
  388. if ReadString(CCFData, S1) then
  389. begin
  390. if (IncludedFiles.IndexOf(S1) = -1) and Assigned(OnNeedFile) then
  391. begin
  392. OnNeedFile(S1, S2);
  393. if S2 <> '' then
  394. begin
  395. aStream := TStringStream.Create(S2);
  396. AddCompletion(aStream);
  397. aStream.Free;
  398. end;
  399. end;
  400. end
  401. else
  402. Exit;
  403. end;
  404. // Add completion info
  405. while ReadString(CCFData, S1) do
  406. begin
  407. C := TCompletionItem.Create;
  408. C.ReadFromStream(CCFData);
  409. C.SourceFileName := UnitName;
  410. if S1 <> '' then
  411. AddObject(S1, C)
  412. else
  413. C.Free;
  414. end;
  415. end;
  416. end;
  417. procedure TCompletionList.AddCompletion(const CCFData: string);
  418. var
  419. S: TStringStream;
  420. begin
  421. S := TStringStream.Create(CCFData);
  422. AddCompletion(S);
  423. S.Free;
  424. end;
  425. { TCompletionItem }
  426. procedure TCompletionItem.ReadFromStream(F: TStream);
  427. begin
  428. F.Read(Line, SizeOf(Line));
  429. ReadString(F, Left);
  430. ReadString(F, Right);
  431. ReadString(F, Description);
  432. end;
  433. procedure TCompletionItem.WriteToStream(F: TStream);
  434. begin
  435. F.Write(Line, SizeOf(Line));
  436. WriteString(F, Left);
  437. WriteString(F, Right);
  438. WriteString(F, Description);
  439. end;
  440. end.