2
***************************************************************************
4
* This source is free software; you can redistribute it and/or modify *
5
* it under the terms of the GNU General Public License as published by *
6
* the Free Software Foundation; either version 2 of the License, or *
7
* (at your option) any later version. *
9
* This code is distributed in the hope that it will be useful, but *
10
* WITHOUT ANY WARRANTY; without even the implied warranty of *
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12
* General Public License for more details. *
14
* A copy of the GNU General Public License is available on the World *
15
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
16
* obtain it by writing to the Free Software Foundation, *
17
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19
***************************************************************************
21
Author: Mattias Gaertner
33
Classes, SysUtils, Dialogs, Controls, LCLIntf, Clipbrd, LCLType, LResources,
35
IDEDialogs, LazIDEIntf, SrcEditorIntf, IDEHelpIntf,
36
FileProcs, CodeToolManager, CodeCache, SourceLog, BasicCodeTools,
37
EventCodeTool, LinkScanner, PascalParserTool, CodeTree, SourceChanger,
43
{ TCodyClipboardData }
45
TCodyClipboardData = class
48
procedure WriteString(MemStream: TMemoryStream; const s: string);
49
function ReadString(MemStream: TMemoryStream): string;
50
procedure WriteToStream(MemStream: TMemoryStream); virtual; abstract;
51
procedure ReadFromStream(MemStream: TMemoryStream); virtual; abstract;
52
procedure Execute({%H-}SrcEdit: TSourceEditorInterface; {%H-}LogXY: TPoint); virtual;
54
TCodyClipboardFormat = class of TCodyClipboardData;
56
{ TCodyClipboardSrcData }
58
TCodyClipboardSrcData = class(TCodyClipboardData)
60
SourceFilename: string;
63
procedure SetSourcePos(const SrcPos: TCodeXYPosition);
64
procedure WriteToStream(MemStream: TMemoryStream); override;
65
procedure ReadFromStream(MemStream: TMemoryStream); override;
72
FClipboardFormats: TFPList;
73
function GetClipboardFormats(Index: integer): TCodyClipboardFormat;
76
destructor Destroy; override;
78
procedure DecodeLoaded(Sender: TSourceLog; const Filename: string;
79
var Source, DiskEncoding, MemEncoding: string);
82
class function ClipboardFormatId: TClipboardFormat;
83
function CanReadFromClipboard(AClipboard: TClipboard): Boolean;
84
function ReadFromClipboard(AClipboard: TClipboard;
85
SrcEdit: TSourceEditorInterface; LogXY: TPoint; AText: string): boolean;
86
function WriteToClipboard(Data: TCodyClipboardData;
87
AClipboard: TClipboard = nil): Boolean;
88
procedure RegisterClipboardFormat(ccFormat: TCodyClipboardFormat);
89
function FindClipboardFormat(aName: string): TCodyClipboardFormat;
90
function ClipboardFormatCount: integer;
91
property ClipboardFormats[Index: integer]: TCodyClipboardFormat
92
read GetClipboardFormats;
93
procedure SrcEditCopyPaste(SrcEdit: TSourceEditorInterface;
94
var AText: String; var {%H-}AMode: TSemSelectionMode; ALogStartPos: TPoint;
95
var AnAction: TSemCopyPasteAction);
104
cupeMainCodeNotFound, // the file of the unit start was not found
106
cupeCursorNotInCode, // e.g. in front of the keyword 'unit'
110
procedure ExplodeAWithBlockCmd(Sender: TObject);
111
procedure InsertFileAtCursor(Sender: TObject);
112
procedure InsertCallInherited(Sender: TObject);
114
function ParseTilCursor(out Tool: TCodeTool; out CleanPos: integer;
115
out Node: TCodeTreeNode; out ErrorHandled: boolean;
116
JumpToError: boolean; CodePos: PCodeXYPosition = nil): TCUParseError;
117
function ParseUnit(out Tool: TCodeTool; out CleanPos: integer;
118
out Node: TCodeTreeNode; out ErrorHandled: boolean;
119
JumpToError: boolean; CodePos: PCodeXYPosition = nil;
120
TilCursor: boolean = false): TCUParseError;
121
procedure OpenCodyHelp(Path: string);
125
procedure ExplodeAWithBlockCmd(Sender: TObject);
127
procedure ErrorNotInWithVar;
129
IDEMessageDialog(crsCWError,
130
crsCWPleasePlaceTheCursorOfTheSourceEditorOnAWithVariab,
135
SrcEdit: TSourceEditorInterface;
137
// commit changes form source editor to codetools
138
if not LazarusIDE.BeginCodeTools then exit;
139
// check context at cursor
140
SrcEdit:=SourceEditorManagerIntf.ActiveEditor;
141
if SrcEdit=nil then begin
145
if not CodeToolBoss.RemoveWithBlock(SrcEdit.CodeToolsBuffer as TCodeBuffer,
146
SrcEdit.CursorTextXY.X,SrcEdit.CursorTextXY.Y)
148
// syntax error or not in a class
149
if CodeToolBoss.ErrorMessage<>'' then
150
LazarusIDE.DoJumpToCodeToolBossError
157
procedure InsertFileAtCursor(Sender: TObject);
159
OpenDialog: TOpenDialog;
163
SrcEdit: TSourceEditorInterface;
165
SrcEdit:=SourceEditorManagerIntf.ActiveEditor;
166
if SrcEdit=nil then exit;
168
OpenDialog:=TOpenDialog.Create(nil);
171
InitIDEFileDialog(OpenDialog);
172
OpenDialog.Title:=crsCUSelectFileToInsertAtCursor;
173
OpenDialog.Options:=OpenDialog.Options+[ofFileMustExist];
174
Filter:=crsCUPascalPasPpPasPp;
175
Filter:=Format(crsCUAllFiles, [Filter, FileMask, FileMask]);
176
OpenDialog.Filter:=Filter;
177
if not OpenDialog.Execute then exit;
178
Filename:=OpenDialog.FileName;
179
if not FileIsText(Filename) then begin
180
if IDEMessageDialog(crsCUWarning, crsCUTheFileSeemsToBeABinaryProceed,
181
mtConfirmation,[mbOk,mbCancel])<>mrOK then exit;
183
Code:=TCodeBuffer.Create;
184
Code.Filename:=Filename;
185
Code.OnDecodeLoaded:=@Cody.DecodeLoaded;
186
if not Code.LoadFromFile(Filename) then begin
187
IDEMessageDialog(crsCWError, Format(crsCUUnableToLoadFile, [Filename, #13
193
SrcEdit.Selection:=Code.Source;
200
procedure InsertCallInherited(Sender: TObject);
202
procedure ErrorNotInMethod;
204
IDEMessageDialog(crsCWError,
205
crsCUPleasePlaceTheCursorOfTheSourceEditorInAnImplement,
211
Tool: TEventsCodeTool;
213
CursorNode: TCodeTreeNode;
214
ProcNode: TCodeTreeNode;
215
DeclNode: TCodeTreeNode;
217
SrcEdit: TSourceEditorInterface;
219
IndentContextSensitive: Boolean;
220
NewIndent: TFABIndentationPolicy;
227
if (ParseTilCursor(Tool,CleanPos,CursorNode,Handled,true)<>cupeSuccess)
228
and not Handled then begin
232
SrcEdit:=SourceEditorManagerIntf.ActiveEditor;
235
ProcNode:=CursorNode.GetNodeOfType(ctnProcedure);
236
if not Tool.NodeIsMethodBody(ProcNode) then begin
237
debugln(['InsertCallInherited not in a method body']);
240
// search the declaration (the header of the body may be incomplete)
241
DeclNode:=Tool.FindCorrespondingProcNode(ProcNode);
245
NewCode:='inherited '+Tool.ExtractProcHead(DeclNode,
246
[phpWithoutClassName,phpWithParameterNames,phpWithoutParamTypes,
247
phpWithoutSemicolon]);
248
NewCode:=StringReplace(NewCode,';',',',[rfReplaceAll])+';';
249
//debugln(['InsertCallInherited NewCode="',NewCode,'"']);
252
if Tool.NodeIsFunction(DeclNode) then begin
253
if FindFirstNonSpaceCharInLine(Tool.Src,CleanPos)<CleanPos then begin
254
// insert function behind some code
255
// e.g. InheritedValue:=|
259
// store the old result value
260
NewCode:='Result:='+NewCode;
263
NewLine:=true; // procedures always on a separate line
267
if NewLine then begin
270
Indent:=SrcEdit.CursorScreenXY.X-1;
271
IndentContextSensitive:=true;
272
if CodeToolBoss.Indenter.GetIndent(Tool.Src,CleanPos,
273
Tool.Scanner.NestedComments,
274
true,NewIndent,IndentContextSensitive,NewCode)
275
and NewIndent.IndentValid then begin
276
Indent:=NewIndent.Indent;
278
while (FromPos>1) and (Tool.Src[FromPos-1] in [' ',#9]) do
280
NewCode:=GetIndentStr(Indent)+NewCode;
281
//debugln(['InsertCallInherited Indent=',Indent,' Line="',GetLineInSrc(Tool.Src,CleanPos),'"']);
284
NewCode:=CodeToolBoss.SourceChangeCache.BeautifyCodeOptions.BeautifyStatement(
285
NewCode,Indent,[bcfDoNotIndentFirstLine],GetPosInLine(Tool.Src,FromPos));
286
CodeToolBoss.SourceChangeCache.MainScanner:=Tool.Scanner;
287
// move editor cursor in front of insert position
288
NewXY:=Point(GetPosInLine(Tool.Src,FromPos)+1,SrcEdit.CursorTextXY.Y);
289
//debugln(['InsertCallInherited NewXY=',dbgs(NewXY),' FromPos=',Tool.CleanPosToStr(FromPos),' ToPos=',Tool.CleanPosToStr(ToPos)]);
290
if not CodeToolBoss.SourceChangeCache.Replace(Gap,Gap,FromPos,ToPos,NewCode)
292
debugln(['InsertCallInherited CodeToolBoss.SourceChangeCache.Replace failed']);
295
SrcEdit.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('InsertCallInherited'){$ENDIF};
297
SrcEdit.CursorTextXY:=NewXY;
298
if not CodeToolBoss.SourceChangeCache.Apply then begin
299
debugln(['InsertCallInherited CodeToolBoss.SourceChangeCache.Apply failed']);
303
SrcEdit.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('InsertCallInherited'){$ENDIF};
306
on e: Exception do CodeToolBoss.HandleException(e);
309
// syntax error or not in a method
310
if not Handled then begin
311
if CodeToolBoss.ErrorMessage<>'' then
312
LazarusIDE.DoJumpToCodeToolBossError
319
function ParseTilCursor(out Tool: TCodeTool; out CleanPos: integer;
320
out Node: TCodeTreeNode; out ErrorHandled: boolean;
321
JumpToError: boolean; CodePos: PCodeXYPosition): TCUParseError;
323
Result:=ParseUnit(Tool,CleanPos,Node,ErrorHandled,JumpToError,CodePos,true);
326
function ParseUnit(out Tool: TCodeTool; out CleanPos: integer;
327
out Node: TCodeTreeNode; out ErrorHandled: boolean; JumpToError: boolean;
328
CodePos: PCodeXYPosition; TilCursor: boolean): TCUParseError;
330
SrcEdit: TSourceEditorInterface;
331
CursorPos: TCodeXYPosition;
337
if CodePos<>nil then CodePos^:=CleanCodeXYPosition;
338
SrcEdit:=SourceEditorManagerIntf.ActiveEditor;
339
if SrcEdit=nil then begin
340
debugln(['CodyUtils.ParseTilCursor: no source editor']);
341
exit(cupeNoSrcEditor);
343
if not LazarusIDE.BeginCodeTools then exit;
345
CursorPos.Code:=SrcEdit.CodeToolsBuffer as TCodeBuffer;
346
CursorPos.X:=SrcEdit.CursorTextXY.X;
347
CursorPos.Y:=SrcEdit.CursorTextXY.Y;
351
if not CodeToolBoss.InitCurCodeTool(CursorPos.Code) then
352
exit(cupeMainCodeNotFound);
354
Tool:=CodeToolBoss.CurCodeTool;
355
Result:=cupeParseError;
356
//Range:=trTillRange;
358
Tool.BuildTreeAndGetCleanPos(trTillCursor,lsrEnd,CursorPos,CleanPos,
359
[btSetIgnoreErrorPos])
361
Tool.BuildTreeAndGetCleanPos(trTillRange,lsrEnd,CursorPos,CleanPos,[]);
362
Node:=Tool.FindDeepestNodeAtPos(CleanPos,false);
364
exit(cupeCursorNotInCode);
367
on e: Exception do CodeToolBoss.HandleException(e);
370
if (CodeToolBoss.ErrorMessage<>'') and JumpToError then begin
372
LazarusIDE.DoJumpToCodeToolBossError;
377
procedure OpenCodyHelp(Path: string);
381
BasePath:='http://wiki.lazarus.freepascal.org/Cody';
382
OpenURL(BasePath+Path);
385
{ TCodyClipboardSrcData }
387
procedure TCodyClipboardSrcData.SetSourcePos(const SrcPos: TCodeXYPosition);
389
SourceFilename:=SrcPos.Code.Filename;
394
procedure TCodyClipboardSrcData.WriteToStream(MemStream: TMemoryStream);
396
WriteString(MemStream,SourceFilename);
397
WriteLRSInteger(MemStream,SourceY);
398
WriteLRSInteger(MemStream,SourceX);
401
procedure TCodyClipboardSrcData.ReadFromStream(MemStream: TMemoryStream);
403
SourceFilename:=ReadString(MemStream);
404
SourceY:=ReadLRSInteger(MemStream);
405
SourceX:=ReadLRSInteger(MemStream);
408
{ TCodyClipboardData }
410
procedure TCodyClipboardData.WriteString(MemStream: TMemoryStream;
416
if length(s)<255 then begin
418
MemStream.Write(b,1);
420
MemStream.Write(s[1],b);
423
MemStream.Write(b,1);
425
WriteLRSInteger(MemStream,l);
426
MemStream.Write(s[1],l);
430
function TCodyClipboardData.ReadString(MemStream: TMemoryStream): string;
437
if MemStream.Read(b,1)<>1 then exit;
441
MemStream.Read(Result[1],b);
443
l:=ReadLRSInteger(MemStream);
446
MemStream.Read(Result[1],l);
448
//debugln(['TCodyClipboardData.ReadString Result="',Result,'"']);
451
procedure TCodyClipboardData.Execute(SrcEdit: TSourceEditorInterface;
454
raise Exception.Create('not implemented yet: '+ClassName+'.Execute');
459
function TCody.GetClipboardFormats(Index: integer): TCodyClipboardFormat;
461
Result:=TCodyClipboardFormat(FClipboardFormats[Index]);
464
constructor TCody.Create;
466
FClipboardFormats:=TFPList.Create;
469
destructor TCody.Destroy;
471
FreeAndNil(FClipboardFormats);
475
procedure TCody.DecodeLoaded(Sender: TSourceLog; const Filename: string;
476
var Source, DiskEncoding, MemEncoding: string);
478
//debugln(['TCody.DecodeLoaded ',Filename]);
479
if (Sender is TCodeBuffer)
480
and Assigned(CodeToolBoss.SourceCache.OnDecodeLoaded) then
481
CodeToolBoss.SourceCache.OnDecodeLoaded(TCodeBuffer(Sender),Filename,
482
Source,DiskEncoding,MemEncoding);
485
class function TCody.ClipboardFormatId: TClipboardFormat;
487
CodyClipboardMimeType = 'Application/X-Laz-Cody';
489
ID: TClipboardFormat = 0;
492
ID := ClipboardRegisterFormat(CodyClipboardMimeType);
496
function TCody.CanReadFromClipboard(AClipboard: TClipboard): Boolean;
498
Result := AClipboard.HasFormat(ClipboardFormatId);
501
function TCody.ReadFromClipboard(AClipboard: TClipboard;
502
SrcEdit: TSourceEditorInterface; LogXY: TPoint; AText: string): boolean;
504
procedure InvalidStream;
506
raise Exception.Create('The Cody clipboard data is invalid');
510
MemStream: TMemoryStream;
512
aFormat: TCodyClipboardFormat;
513
Data: TCodyClipboardData;
516
if not AClipboard.HasFormat(ClipboardFormatId) then exit;
518
MemStream:=TMemoryStream.Create;
521
Result:=AClipboard.GetFormat(ClipboardFormatId,MemStream);
523
MemStream.Position:=0;
524
if MemStream.Read(ID[0],1)<>1 then
526
if MemStream.Read(ID[1],ord(ID[0]))<>ord(ID[0]) then
528
aFormat:=FindClipboardFormat(ID);
531
Data:=aFormat.Create;
533
Data.ReadFromStream(MemStream);
534
Data.Execute(SrcEdit,LogXY);
541
function TCody.WriteToClipboard(Data: TCodyClipboardData; AClipboard: TClipboard
544
MemStream: TMemoryStream;
547
if AClipboard=nil then AClipboard:=Clipboard;
548
AClipboard.AsText:=Data.AsText;
549
if not AClipboard.HasFormat(CF_TEXT) then
550
raise Exception.Create('Write to clipboard failed');
551
MemStream:=TMemoryStream.Create;
554
MemStream.Write(ID[0],length(ID)+1);
555
Data.WriteToStream(MemStream);
556
MemStream.Position:=0;
557
Result:=AClipboard.AddFormat(ClipboardFormatId,MemStream);
563
procedure TCody.RegisterClipboardFormat(ccFormat: TCodyClipboardFormat);
565
if FindClipboardFormat(ccFormat.ClassName)<>nil then
566
raise Exception.Create('cody clipboard format "'+ccFormat.ClassName+'" is already registered');
567
FClipboardFormats.Add(ccFormat);
570
function TCody.FindClipboardFormat(aName: string): TCodyClipboardFormat;
574
for i:=0 to ClipboardFormatCount-1 do begin
575
Result:=ClipboardFormats[i];
576
if SysUtils.CompareText(Result.ClassName,aName)=0 then exit;
581
function TCody.ClipboardFormatCount: integer;
583
Result:=FClipboardFormats.Count;
586
procedure TCody.SrcEditCopyPaste(SrcEdit: TSourceEditorInterface;
587
var AText: String; var AMode: TSemSelectionMode; ALogStartPos: TPoint;
588
var AnAction: TSemCopyPasteAction);
590
AClipBoard: TClipboard;
592
// ToDo: use the right clipboard
593
AClipBoard:=Clipboard;
595
if not ReadFromClipboard(AClipBoard,SrcEdit,ALogStartPos,AText) then exit;
597
on E: Exception do begin
598
IDEMessageDialog('Error','Unable to paste Cody data.'#13+E.Message,
602
AnAction:=semcaAbort;