gpt4 book ai didi

delphi - 如何在已打开的 Delphi IDE 中从另一个应用程序打开 .pas 文件并定位到行#

转载 作者:行者123 更新时间:2023-12-03 14:54:13 30 4
gpt4 key购买 nike

假设我打开了 Delphi IDE,如何打开 .pas在另一个应用程序中选择的文件并在 Delphi IDE 中打开它,并将其定位到特定的行号?

我见过一些编辑工具可以做到这一点。

我不确定它是否只是普通文件打开的一个选项(例如,使用默认文件关联),还是一个命令行选项,或者您需要 DDE 或 COM 或完全不同的东西。

请注意,我不想关闭项目并重新打开新的或假的项目。

此外,我不希望将该文件添加到项目中。我只是想打开它。

例如,当您 <ctrl>-click对于变量或类型,IDE 将打开包含该符号的文件并转到声明该符号的行。这就是我想做的一切——但是来自外部应用程序。 (我不是在寻找符号,只是在寻找一条线。)

我目前使用的是 Delphi XE5,因此我对较新的 Delphi 版本感兴趣,而不是 XE2 之前的版本。

(问题的一部分是,如果 IDE 已打开,如何确保文件在当前 IDE 内的新选项卡中打开,而不是在 IDE 的另一个实例中打开?)

最佳答案

下面的代码(针对 D7)展示了如何通过编译的 IDE 插件 .Dpk 来完成此操作进入 Bpl。它一开始只是一个“概念证明”,但它确实有效。

它包含一个“发送器”应用程序,该应用程序使用 WM_COPYDATA 将文件名、行号和列发送到 .Bpl 文件中托管的接收器。

发送者向接收者发送一个类似的字符串

Filename=d:\aaad7\ota\dskfilesu.pas
Line=8
Col=12
Comment=(* some comment or other*)

注释行是可选的。

在 .Bpl 中,接收者使用 OTA 服务打开请求的文件并定位编辑器插入符,然后插入注释(如果有)。

最棘手的事情是找出如何处理一种特定的复杂情况,即要打开的命名文件具有关联表单的情况。如果是这样,在 D7(以及我假设启用了 float 设计器选项的其他 IDE 版本)中,当 IDE打开 .Pas 文件,它还会打开 .Dfm,并留给其自己的设备,这会将表单编辑器保留在代码编辑器前面。为 .Pas 文件调用 IOTASourceEditor.Show 至少将 IDE 代码编辑器放在 .Dfm 表单前面,但这并不能满足我,因为现在我的好奇心被激起了 - 如何获得 IDE 的表单显示在屏幕之外?

我花了很多时间探索各种死胡同,因为 OTA + NTA 服务似乎没有提供任何方法来显式关闭 IOTAEditor 或其任何后代。最后发现,要做的事情只是获取对表单的引用,然后向其发送 WM_CLOSE(!) - 请参阅代码中的注释。

Fwiw,作为 OTA 的新手,一开始(在我发现 IOTAModules 的工作原理之前),我发现最困难的部分是发现如何获取设置编辑器插入符号所需的 IEditView 界面位置,但像往常一样,这些界面事物,一旦你完全正确地使用了“魔法咒语”,一切都会起作用。

祝你好运!感谢您的精彩挑战!

unit Receiveru;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ToolsAPI;

type
TOTAEditPosnForm = class(TForm)
Memo1: TMemo;
private
FEdLine: Integer;
FEdCol: Integer;
FEditorFileName: String;
FEditorInsert: String;
procedure WMCopyData(var Msg : TWMCopyData); message WM_COPYDATA;
procedure HandleCopyDataString(CopyDataStruct : PCopyDataStruct);
procedure OpenInIDEEditor;
property EditorFileName : String read FEditorFileName write FEditorFileName;
property EdLine : Integer read FEdLine write FEdLine;
property EdCol : Integer read FEdCol write FEdCol;
property EditorInsert : String read FEditorInsert write FEditorInsert;
end;

var
OTAEditPosnForm: TOTAEditPosnForm;

procedure Register;

implementation

{$R *.dfm}

procedure MonitorFiles;
begin
OTAEditPosnForm := TOTAEditPosnForm.Create(Nil);
OTAEditPosnForm.Show;
end;

procedure Register;
begin
MonitorFiles;
end;

procedure TOTAEditPosnForm.OpenInIDEEditor;
var
IServices : IOTAServices;
IActionServices : IOTAActionServices;
IModuleServices : IOTAModuleServices;
IEditorServices : IOTAEditorServices60;
IModule : IOTAModule;
i : Integer;
IEditor : IOTAEditor;
ISourceEditor : IOTASourceEditor;
IFormEditor : IOTAFormEditor;
IComponent : IOTAComponent;
INTAComp : INTAComponent;
AForm : TForm;
IEditView : IOTAEditView;
CursorPos : TOTAEditPos;
IEditWriter : IOTAEditWriter;
CharPos : TOTACharPos;
InsertPos : Longint;
FileName : String;
begin
IServices := BorlandIDEServices as IOTAServices;
Assert(Assigned(IServices), 'IOTAServices not available');

IServices.QueryInterface(IOTAACtionServices, IActionServices);
if IActionServices <> Nil then begin

IServices.QueryInterface(IOTAModuleServices, IModuleServices);
Assert(IModuleServices <> Nil);

// Close all files open in the IDE
IModuleServices.CloseAll;

if IActionServices.OpenFile(EditorFileName) then begin

// At this point, if the named file has an associated .DFM and
// we stopped here, the form designer would be in front of the
// code editor.

IModule := IModuleServices.Modules[0];
// IModule is the one holding our .Pas file and its .Dfm, if any
// So, iterate the IModule's editors until we find the one
// for the .Pas file and then call .Show on it. This will
// bring the code editor in front of the form editor.

ISourceEditor := Nil;

for i := 0 to IModule.ModuleFileCount - 1 do begin
IEditor := IModule.ModuleFileEditors[i];
FileName := IEditor.FileName;
Memo1.Lines.Add(Format('%d %s', [i, FileName]));
if CompareText(ExtractFileExt(IEditor.FileName), '.Pas') = 0 then begin
if ISourceEditor = Nil then begin
IEditor.QueryInterface(IOTASourceEditor, ISourceEditor);
IEditor.Show;
end
end
else begin
// Maybe the editor is a Form Editor. If it is
// close the form (the counterpart to the .Pas, that is}
IEditor.QueryInterface(IOTAFormEditor, IFormEditor);
if IFormEditor <> Nil then begin
IComponent := IFormEditor.GetRootComponent;
IComponent.QueryInterface(INTAComponent, INTAComp);
AForm := TForm(INTAComp.GetComponent);
//AForm.Close; < this does NOT close the on-screen form
// IActionServices.CloseFile(IEditor.FileName); <- neither does this
SendMessage(AForm.Handle, WM_Close, 0, 0); // But this does !
end;
end;
end;

// Next, place the editor caret where we want it ...
IServices.QueryInterface(IOTAEditorServices, IEditorServices);
Assert(IEditorServices <> Nil);

IEditView := IEditorServices.TopView;
Assert(IEditView <> Nil);
CursorPos.Line := edLine;
CursorPos.Col := edCol;
IEditView.SetCursorPos(CursorPos);
// and scroll the IEditView to the caret
IEditView.MoveViewToCursor;

// Finally, insert the comment, if any
if EditorInsert <> '' then begin
Assert(ISourceEditor <> Nil);
IEditView.ConvertPos(True, CursorPos, CharPos);
InsertPos := IEditView.CharPosToPos(CharPos);
IEditWriter := ISourceEditor.CreateUndoableWriter;
Assert(IEditWriter <> Nil, 'IEditWriter');
IEditWriter.CopyTo(InsertPos);
IEditWriter.Insert(PChar(EditorInsert));
IEditWriter := Nil;
end;
end;
end;
end;

procedure TOTAEditPosnForm.HandleCopyDataString(
CopyDataStruct: PCopyDataStruct);
begin
Memo1.Lines.Text := PChar(CopyDataStruct.lpData);

EditorFileName := Memo1.Lines.Values['FileName'];
edLine := StrToInt(Memo1.Lines.Values['Line']);
edCol := StrToInt(Memo1.Lines.Values['Col']);
EditorInsert := Trim(Memo1.Lines.Values['Comment']);
if EditorFileName <> '' then
OpenInIDEEditor;
end;

procedure TOTAEditPosnForm.WMCopyData(var Msg: TWMCopyData);
begin
HandleCopyDataString(Msg.CopyDataStruct);
msg.Result := Length(Memo1.Lines.Text);
end;

initialization

finalization
if Assigned(OTAEditPosnForm) then begin
OTAEditPosnForm.Close;
FreeAndNil(OTAEditPosnForm);
end;
end.

发件人代码:

procedure TSenderMainForm.btnSendClick(Sender: TObject);
begin
SendMemo;
end;

procedure TSenderMainForm.SendData(
CopyDataStruct: TCopyDataStruct);
var
HReceiver : THandle;
Res : integer;
begin
HReceiver := FindWindow(PChar('TOTAEditPosnForm'),PChar('OTAEditPosnForm'));
if HReceiver = 0 then begin
Caption := 'CopyData Receiver NOT found!';
end
else begin
Res := SendMessage(HReceiver, WM_COPYDATA, Integer(Handle), Integer(@CopyDataStruct));
if Res > 0 then
Caption := Format('Received %d characters', [Res]);
end;
end;

procedure TSenderMainForm.SendMemo;
var
MS : TMemoryStream;
CopyDataStruct : TCopyDataStruct;
S : String;
begin
MS := TMemoryStream.Create;
try
S := Memo1.Lines.Text + #0;
MS.Write(S[1], Length(S));
CopyDataStruct.dwData := 1;
CopyDataStruct.cbData := MS.Size;
CopyDataStruct.lpData := MS.Memory;
SendData(CopyDataStruct);
finally
MS.Free;
end;
end;

关于delphi - 如何在已打开的 Delphi IDE 中从另一个应用程序打开 .pas 文件并定位到行#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24690352/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com