mirror of https://gitlab.com/basile.b/dexed.git
435 lines
12 KiB
Plaintext
435 lines
12 KiB
Plaintext
unit ce_tools;
|
|
|
|
{$I ce_defines.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, LazFileUtils, process, menus, ce_processes,
|
|
ce_common, ce_writableComponent, ce_interfaces, ce_observer, ce_inspectors,
|
|
ce_synmemo;
|
|
|
|
type
|
|
|
|
TCEToolItems = class;
|
|
|
|
TPipeInputKind = (pikNone, pikEditor, pikSelection, pikLine);
|
|
|
|
TCEToolItem = class(TCollectionItem)
|
|
private
|
|
fToolItems: TCEToolItems;
|
|
fNextToolAlias: string;
|
|
fProcess: TCEProcess;
|
|
fExecutable: TCEFilename;
|
|
fWorkingDir: TCEPathname;
|
|
fShowWin: TShowWindowOptions;
|
|
fOpts: TProcessOptions;
|
|
fParameters: TStringList;
|
|
fToolAlias: string;
|
|
fQueryParams: boolean;
|
|
fClearMessages: boolean;
|
|
fEditorToInput: boolean;
|
|
fOutputToNext: boolean;
|
|
fShortcut: TShortcut;
|
|
fMsgs: ICEMessagesDisplay;
|
|
fSymStringExpander: ICESymStringExpander;
|
|
fPipeInputKind: TPipeInputKind;
|
|
procedure setParameters(value: TStringList);
|
|
procedure processOutput(sender: TObject);
|
|
procedure setToolAlias(value: string);
|
|
published
|
|
property toolAlias: string read fToolAlias write setToolAlias;
|
|
property options: TProcessOptions read fOpts write fOpts;
|
|
property executable: TCEFilename read fExecutable write fExecutable;
|
|
property workingDirectory: TCEPathname read fWorkingDir write fWorkingDir;
|
|
property parameters: TStringList read fParameters write setParameters;
|
|
property showWindows: TShowWindowOptions read fShowWin write fShowWin;
|
|
property queryParameters: boolean read fQueryParams write fQueryParams;
|
|
property clearMessages: boolean read fClearMessages write fClearMessages;
|
|
property shortcut: TShortcut read fShortcut write fShortcut;
|
|
property nextToolAlias: string read fNextToolAlias write fNextToolAlias;
|
|
property outputToNext: boolean read fOutputToNext write fOutputToNext;
|
|
property pipeInputKind: TPipeInputKind read fPipeInputKind write fPipeInputKind;
|
|
// TODO-cmaintenance: remove this property from version 3 update 1
|
|
property editorToInput: boolean read fEditorToInput write fEditorToInput stored false;
|
|
public
|
|
constructor create(ACollection: TCollection); override;
|
|
destructor destroy; override;
|
|
procedure assign(Source: TPersistent); override;
|
|
//
|
|
procedure execute(previous: TCEToolItem);
|
|
property process: TCEProcess read fProcess;
|
|
end;
|
|
|
|
TCEToolItems = class(TCollection)
|
|
public
|
|
function findTool(const value: string): TCEToolItem;
|
|
end;
|
|
|
|
TCETools = class(TWritableLfmTextComponent, ICEEditableShortcut, ICEDocumentObserver)
|
|
private
|
|
fTools: TCEToolItems;
|
|
fShctCount: Integer;
|
|
fDoc: TCESynMemo;
|
|
fMenu: TMenuItem;
|
|
function getTool(index: Integer): TCEToolItem;
|
|
procedure setTools(value: TCEToolItems);
|
|
//
|
|
procedure executeToolFromMenu(sender: TObject);
|
|
//
|
|
procedure docNew(document: TCESynMemo);
|
|
procedure docFocused(document: TCESynMemo);
|
|
procedure docChanged(document: TCESynMemo);
|
|
procedure docClosing(document: TCESynMemo);
|
|
//
|
|
function scedWantFirst: boolean;
|
|
function scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean;
|
|
procedure scedSendItem(const category, identifier: string; aShortcut: TShortcut);
|
|
procedure scedSendDone;
|
|
published
|
|
property tools: TCEToolItems read fTools write setTools;
|
|
public
|
|
constructor create(aOwner: TComponent); override;
|
|
destructor destroy; override;
|
|
//
|
|
procedure updateMenu;
|
|
function addTool: TCEToolItem;
|
|
procedure executeTool(tool: TCEToolItem); overload;
|
|
procedure executeTool(index: Integer); overload;
|
|
property tool[index: integer]: TCEToolItem read getTool; default;
|
|
end;
|
|
|
|
//TODO-crefactor: either set the tools as a service of merge the tools collection& tool editor in a single unit.
|
|
|
|
var
|
|
CustomTools: TCETools;
|
|
|
|
implementation
|
|
|
|
uses
|
|
dialogs;
|
|
|
|
const
|
|
toolsFname = 'tools.txt';
|
|
|
|
{$REGION TCEToolItem -----------------------------------------------------------}
|
|
function TCEToolItems.findTool(const value: string): TCEToolItem;
|
|
var
|
|
item: TCollectionItem;
|
|
begin
|
|
for item in self do
|
|
if TCEToolItem(item).toolAlias = value then
|
|
exit(TCEToolItem(item));
|
|
exit(nil);
|
|
end;
|
|
|
|
constructor TCEToolItem.create(ACollection: TCollection);
|
|
begin
|
|
inherited;
|
|
// TODO-cbugfix: tools are init before symstring, even when order of 'uses' is modified (lpr)
|
|
fSymStringExpander:= getSymStringExpander;
|
|
fMsgs := getMessageDisplay;
|
|
fToolItems := TCEToolItems(ACollection);
|
|
fToolAlias := format('<tool %d>', [ID]);
|
|
fParameters := TStringList.create;
|
|
end;
|
|
|
|
destructor TCEToolItem.destroy;
|
|
begin
|
|
fParameters.Free;
|
|
ce_processes.killProcess(fProcess);
|
|
inherited;
|
|
end;
|
|
|
|
procedure TCEToolItem.assign(Source: TPersistent);
|
|
var
|
|
tool: TCEToolItem;
|
|
begin
|
|
// only used to clone a tool: so don't copy everything.
|
|
if Source is TCEToolItem then
|
|
begin
|
|
tool := TCEToolItem(Source);
|
|
//
|
|
toolAlias := tool.toolAlias;
|
|
queryParameters := tool.queryParameters;
|
|
clearMessages := tool.clearMessages;
|
|
options := tool.options;
|
|
executable := tool.executable;
|
|
workingDirectory := tool.workingDirectory;
|
|
editorToInput := tool.editorToInput;
|
|
showWindows := tool.showWindows;
|
|
pipeInputKind := tool.pipeInputKind;
|
|
parameters.Assign(tool.parameters);
|
|
end
|
|
else inherited;
|
|
end;
|
|
|
|
procedure TCEToolItem.setParameters(value: TStringList);
|
|
begin
|
|
fParameters.Assign(value);
|
|
end;
|
|
|
|
procedure TCEToolItem.setToolAlias(value: string);
|
|
var
|
|
i: integer = 0;
|
|
begin
|
|
while fToolItems.findTool(value).isNotNil do
|
|
begin
|
|
value += intToStr(i);
|
|
i += 1;
|
|
end;
|
|
fToolAlias := value;
|
|
end;
|
|
|
|
procedure TCEToolItem.execute(previous: TCEToolItem);
|
|
var
|
|
arg: string;
|
|
prm: string;
|
|
inp: string;
|
|
old: string;
|
|
begin
|
|
ce_processes.killProcess(fProcess);
|
|
//
|
|
if fMsgs = nil then
|
|
fMsgs := getMessageDisplay;
|
|
if fClearMessages then
|
|
fMsgs.clearByContext(amcMisc);
|
|
if fSymStringExpander = nil then
|
|
fSymStringExpander:= getSymStringExpander;
|
|
//
|
|
old := GetCurrentDirUTF8;
|
|
fProcess := TCEProcess.Create(nil);
|
|
fProcess.OnReadData:= @processOutput;
|
|
fProcess.OnTerminate:= @processOutput;
|
|
fProcess.Options := fOpts;
|
|
fProcess.Executable := exeFullName(fSymStringExpander.expand(fExecutable));
|
|
fProcess.ShowWindow := fShowWin;
|
|
fProcess.CurrentDirectory := fSymStringExpander.expand(fWorkingDir);
|
|
for prm in fParameters do if not isStringDisabled(prm) then
|
|
fProcess.Parameters.AddText(fSymStringExpander.expand(prm));
|
|
if fQueryParams then
|
|
begin
|
|
prm := '';
|
|
if InputQuery('Parameters', '', prm) then
|
|
begin
|
|
prm := fSymStringExpander.expand(prm);
|
|
arg := StringReplace(fParameters.Text, '<$1>', prm, [rfReplaceAll]);
|
|
if prm.isNotEmpty and (arg = fParameters.Text) then
|
|
fProcess.Parameters.AddText(prm)
|
|
else
|
|
fProcess.Parameters.Text := arg;
|
|
end;
|
|
end;
|
|
ensureNoPipeIfWait(fProcess);
|
|
//
|
|
if fProcess.Executable.fileExists then
|
|
begin
|
|
fProcess.Execute;
|
|
if previous.isNotNil and previous.outputToNext
|
|
and (poUsePipes in previous.Options) and (poUsePipes in Options) then
|
|
begin
|
|
setLength(inp, previous.process.OutputStack.Size);
|
|
previous.process.OutputStack.Position:=0;
|
|
previous.process.OutputStack.Read(inp[1], inp.length);
|
|
fProcess.Input.Write(inp[1], inp.length);
|
|
fProcess.CloseInput;
|
|
end;
|
|
end;
|
|
//
|
|
SetCurrentDirUTF8(old);
|
|
end;
|
|
|
|
procedure TCEToolItem.processOutput(sender: TObject);
|
|
var
|
|
lst: TStringList;
|
|
str: string;
|
|
nxt: TCEToolItem;
|
|
begin
|
|
if ((not fOutputToNext) or fNextToolAlias.isEmpty) and (poUsePipes in options) then
|
|
begin
|
|
lst := TStringList.Create;
|
|
try
|
|
fProcess.getFullLines(lst);
|
|
for str in lst do
|
|
fMsgs.message(str, nil, amcMisc, amkAuto);
|
|
finally
|
|
lst.Free;
|
|
end;
|
|
end;
|
|
if (not fProcess.Running) and fNextToolAlias.isNotEmpty then
|
|
begin
|
|
nxt := fToolItems.findTool(fNextToolAlias);
|
|
if nxt.isNotNil then nxt.execute(self);
|
|
end;
|
|
end;
|
|
{$ENDREGION --------------------------------------------------------------------}
|
|
|
|
{$REGION Standard Comp/Obj -----------------------------------------------------}
|
|
constructor TCETools.create(aOwner: TComponent);
|
|
var
|
|
fname: string;
|
|
begin
|
|
inherited;
|
|
fTools := TCEToolItems.Create(TCEToolItem);
|
|
fname := getCoeditDocPath + toolsFname;
|
|
if fname.fileExists then loadFromFile(fname);
|
|
//
|
|
EntitiesConnector.addObserver(self);
|
|
end;
|
|
|
|
destructor TCETools.destroy;
|
|
begin
|
|
EntitiesConnector.removeObserver(self);
|
|
//
|
|
ForceDirectoriesUTF8(getCoeditDocPath);
|
|
saveToFile(getCoeditDocPath + toolsFname);
|
|
fTools.Free;
|
|
inherited;
|
|
end;
|
|
{$ENDREGION}
|
|
|
|
{$REGION ICEMainMenuProvider ---------------------------------------------------}
|
|
procedure TCETools.updateMenu;
|
|
var
|
|
mnu: ICEMainMenu = nil;
|
|
itm: TMenuItem;
|
|
colitm: TCEToolItem;
|
|
i: integer;
|
|
begin
|
|
if fMenu.isNil then
|
|
begin
|
|
mnu := getMainMenu;
|
|
if not assigned(mnu) then
|
|
exit;
|
|
fMenu := mnu.mnuAdd;
|
|
fMenu.Caption:='Custom tools';
|
|
end;
|
|
fMenu.Clear;
|
|
for i := 0 to tools.Count-1 do
|
|
begin
|
|
colitm := tool[i];
|
|
itm := TMenuItem.Create(fMenu);
|
|
itm.ShortCut:= colitm.shortcut;
|
|
itm.Caption := colitm.toolAlias;
|
|
itm.tag := ptrInt(colitm);
|
|
itm.onClick := @executeToolFromMenu;
|
|
fMenu.add(itm);
|
|
end;
|
|
end;
|
|
|
|
procedure TCETools.executeToolFromMenu(sender: TObject);
|
|
begin
|
|
executeTool(TCEToolItem(TMenuItem(sender).tag));
|
|
end;
|
|
{$ENDREGION}
|
|
|
|
{$REGION ICEEditableShortcut ---------------------------------------------------}
|
|
function TCETools.scedWantFirst: boolean;
|
|
begin
|
|
result := fTools.Count > 0;
|
|
fShctCount := 0;
|
|
end;
|
|
|
|
function TCETools.scedWantNext(out category, identifier: string; out aShortcut: TShortcut): boolean;
|
|
begin
|
|
category := 'Tools';
|
|
identifier:= tool[fShctCount].toolAlias;
|
|
aShortcut := tool[fShctCount].shortcut;
|
|
//
|
|
fShctCount += 1;
|
|
result := fShctCount < fTools.Count;
|
|
end;
|
|
|
|
procedure TCETools.scedSendItem(const category, identifier: string; aShortcut: TShortcut);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if category <> 'Tools' then exit;
|
|
//
|
|
for i := 0 to tools.Count-1 do if tool[i].toolAlias = identifier then
|
|
begin
|
|
tool[i].shortcut := aShortcut;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
procedure TCETools.scedSendDone;
|
|
begin
|
|
end;
|
|
{$ENDREGION}
|
|
|
|
{$REGION ICEDocumentObserver ---------------------------------------------------}
|
|
procedure TCETools.docNew(document: TCESynMemo);
|
|
begin
|
|
fDoc := document;
|
|
end;
|
|
|
|
procedure TCETools.docFocused(document: TCESynMemo);
|
|
begin
|
|
fDoc := document;
|
|
end;
|
|
|
|
procedure TCETools.docChanged(document: TCESynMemo);
|
|
begin
|
|
end;
|
|
|
|
procedure TCETools.docClosing(document: TCESynMemo);
|
|
begin
|
|
if fDoc <> document then exit;
|
|
fDoc := nil;
|
|
end;
|
|
{$ENDREGION}
|
|
|
|
{$REGION Tools things ----------------------------------------------------------}
|
|
procedure TCETools.setTools(value: TCEToolItems);
|
|
begin
|
|
fTools.Assign(value);
|
|
end;
|
|
|
|
function TCETools.getTool(index: Integer): TCEToolItem;
|
|
begin
|
|
result := TCEToolItem(fTools.Items[index]);
|
|
end;
|
|
|
|
function TCETools.addTool: TCEToolItem;
|
|
begin
|
|
result := TCEToolItem(fTools.Add);
|
|
end;
|
|
|
|
procedure TCETools.executeTool(tool: TCEToolItem);
|
|
var
|
|
txt: string;
|
|
begin
|
|
if tool.isNil then exit;
|
|
//
|
|
tool.execute(nil);
|
|
if tool.editorToInput then tool.pipeInputKind:= pikEditor;
|
|
if (tool.pipeInputKind <> pikNone) and fDoc.isNotNil
|
|
and (poUsePipes in tool.options) and tool.fProcess.Input.isNotNil then
|
|
begin
|
|
case tool.pipeInputKind of
|
|
pikEditor: txt := fDoc.Text;
|
|
pikLine: txt := fDoc.LineText;
|
|
pikSelection: txt := fDoc.SelText;
|
|
end;
|
|
tool.fProcess.Input.Write(txt[1], txt.length);
|
|
tool.fProcess.CloseInput;
|
|
end;
|
|
end;
|
|
|
|
procedure TCETools.executeTool(index: Integer);
|
|
begin
|
|
if index < 0 then exit;
|
|
if index > fTools.Count-1 then exit;
|
|
//
|
|
executeTool(tool[index]);
|
|
end;
|
|
{$ENDREGION}
|
|
|
|
initialization
|
|
RegisterClasses([TCEToolItem, TCETools]);
|
|
CustomTools := TCETools.create(nil);
|
|
finalization
|
|
CustomTools.Free;
|
|
end.
|