lazarus/components/ideintf/ideexterntoolintf.pas
mattias fa26364e98 IDE: further migration external tools
git-svn-id: trunk@42584 -
2013-09-04 09:15:41 +00:00

2437 lines
71 KiB
ObjectPascal

{ Copyright (C) 2006
*****************************************************************************
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Author: Mattias Gaertner
}
unit IDEExternToolIntf;
{$mode objfpc}{$H+}
{$IFDEF EnableNewExtTools}
interface
uses
Classes, SysUtils, typinfo, contnrs, UTF8Process, AvgLvlTree,
LazMethodList, LazLogger, LazFileUtils, LazFileCache, Menus, LCLProc;
const
SubToolFPC = 'FPC';
SubToolFPCPriority = 100;
SubToolFPCLinker = 'FPCLinker';
SubToolFPCRes = 'FPCRes';
SubToolMake = 'make';
SubToolMakePriority = 1000; // higher than FPC
SubToolDefault = 'External Tool';
SubToolDefaultPriority = 0;
type
TETShareStringEvent = procedure(var s: string) of object;
TMessageLineUrgency = (
mluNone,
mluProgress, // time and statistics about the run
mluDebug, // extreme verbosity, only useful for tool authors
mluVerbose3, // all infos
mluVerbose2, // almost all infos
mluVerbose, // extra infos
mluHint, // tool found something unusual
mluNote, // maybe wrong or unnecessary
mluWarning, // probably something is wrong
mluImportant, // message has no urgency level, but should be shown
mluError, // tool could not finish, some tools can still continue
mluFatal, // critical error in input, tool had to abort
mluPanic // bug in tool
);
TMessageLineUrgencies = set of TMessageLineUrgency;
const
MessageLineUrgencyNames: array[TMessageLineUrgency] of string = (
'?',
'Progress',
'Debug',
'Verbose',
'Verbose',
'Verbose',
'Hint',
'Note',
'Warning',
'Misc',
'Error',
'Fatal',
'Panic'
);
type
TMessageLines = class;
TAbstractExternalTool = class;
TMessageLineFlag = (
mlfLeftToken, // position is about left token, otherwise right token
mlfFixed, // reason for the messages was resolved, e.g. quick fixed
mlfHiddenByIDEDirective,
mlfHiddenByIDEDirectiveValid,
mlfFileSearched // file was searched, FullFilename valid
);
TMessageLineFlags = set of TMessageLineFlag;
{ TMessageLine }
TMessageLine = packed class
private
// pointers
FLines: TMessageLines; // owner
FMsg: string; // fixed/improved message
FFilename: string;
FOriginalLine: string;
FSubTool: string;
FTranslatedMsg: string; // translated message
fAttributes: TStrings;
// native types
FSubType: PtrUInt;
FChangeStamp: int64;
// special types
FOutputIndex: integer;
FColumn: integer;
FIndex: integer; // 0-based, position in Lines.Items
FLine: integer;
FMsgID: integer;
FUrgency: TMessageLineUrgency;
FFlags: TMessageLineFlags;
function GetAttribute(const Identifier: string): string;
procedure SetAttribute(const Identifier: string; const AValue: string);
procedure SetColumn(const AValue: integer);
procedure SetFilename(AValue: string);
procedure SetFlags(AValue: TMessageLineFlags);
procedure SetLine(const AValue: integer);
procedure SetMsg(AValue: string);
procedure SetMsgID(AValue: integer);
procedure SetSubTool(AValue: string);
procedure SetSubType(AValue: PtrUInt);
procedure SetTranslatedMsg(AValue: string);
procedure SetUrgency(AValue: TMessageLineUrgency);
procedure SortedSrcPosBind;
procedure SortedSrcPosUnbind;
protected
procedure SetLines(AValue: TMessageLines);
public
constructor Create; virtual;
destructor Destroy; override;
procedure Assign(Source: TMessageLine);
function Equals(Obj: TObject): boolean; override;
procedure Clear;
function GetShortFilename: string; inline;
function GetRelativeFilename: string;
function GetFullFilename: string; inline;
procedure ShareStrings(const ShareStringEvent: TETShareStringEvent); virtual;
procedure SetSourcePosition(NewFilename: string; NewLine, NewColumn: integer);
procedure IncreaseChangeStamp;
procedure MarkFixed;
function HasSourcePosition: boolean;
procedure GetAttributes(List: TStrings);
public
property Index: integer read FIndex; // index in Lines (Note: Lines can have more or less lines than the raw output)
property Urgency: TMessageLineUrgency read FUrgency write SetUrgency;
property SubTool: string read FSubTool write SetSubTool; // e.g. FPC, make, linker, windres
property SubType: PtrUInt read FSubType write SetSubType; // depends on SubTool
property Msg: string read FMsg write SetMsg; // improved message without filename, line, column
property MsgID: integer read FMsgID write SetMsgID; // message id (depends on parser, e.g. fpc writes them with -vq, MsgID<>0 if valid)
property TranslatedMsg: string read FTranslatedMsg write SetTranslatedMsg; // translated Msg
property Filename: string read FFilename write SetFilename; // full file name, relative if not found or not yet searched
property Line: integer read FLine write SetLine; // valid if >0
property Column: integer read FColumn write SetColumn; // valid if >0
property Flags: TMessageLineFlags read FFlags write SetFlags;
property Attribute[const Identifier: string]: string read GetAttribute write SetAttribute; default; // arbitrary attributes
property ChangeStamp: int64 read FChangeStamp;
property OutputIndex: integer read FOutputIndex; // index in raw Output, there can be only one message per output line
property Lines: TMessageLines read FLines write SetLines;
property OriginalLine: string read FOriginalLine;
end;
TMessageLineClass = class of TMessageLine;
{ TMessageLineEnumerator }
TMessageLineEnumerator = class
private
FFilename: string;
FMaxLine: integer;
FMinLine: integer;
protected
FTree: TAvgLvlTree;
FCurrent: TAvgLvlTreeNode;
function GetCurrent: TMessageLine; inline;
public
constructor Create(Tree: TAvgLvlTree; const aFilename: string;
aMinLine, aMaxLine: integer);
function GetEnumerator: TMessageLineEnumerator;
function MoveNext: boolean;
property Current: TMessageLine read GetCurrent;
property Filename: string read FFilename;
property MinLine: integer read FMinLine;
property MaxLine: integer read FMaxLine;
end;
{ TMessageLines }
TETMarksFixedEvent = procedure(ListOfTMessageLine: TFPList) of object;
TMessageLines = class
private
FChangeStamp: int64;
FCritSec: TRTLCriticalSection;
FBaseDirectory: string;
fItems: TFPList; // list of TMessageLine
FMessageLineClass: TMessageLineClass;
FOnMarksFixed: TETMarksFixedEvent;
FOwner: TObject;
FSortedForSrcPos: TAvgLvlTree; // tree of TMessageLine sorted for Filename, Line, Column, OutputIndex, Index
FUpdateSortedSrcPos: boolean;
fChangedHandler: TMethodList;
fMarkedFixed: TAvgLvlTree; // list of TMessageLine
function GetItems(Index: integer): TMessageLine;
procedure SetBaseDirectory(const AValue: string);
procedure LineChanged(Line: TMessageLine);
public
UrgencyCounts: array[TMessageLineUrgency] of integer;
constructor Create(aOwner: TObject; aMsgLineClass: TMessageLineClass);
destructor Destroy; override;
property Owner: TObject read FOwner;
procedure EnterCriticalSection; virtual; // always use before access
procedure LeaveCriticalSection; virtual;
function Count: integer; inline;
procedure Clear;
property Items[Index: integer]: TMessageLine read GetItems; default;
property BaseDirectory: string read FBaseDirectory write SetBaseDirectory; // always trimmed and with trailing /
function CreateLine(OutputIndex: integer): TMessageLine; // create, but do not yet add it
procedure Add(MsgLine: TMessageLine);
procedure Remove(MsgLine: TMessageLine);
procedure Delete(MsgLine: TMessageLine);
procedure MarkFixed(MsgLine: TMessageLine); // (main thread) request to add mlfFixed, will be applied in ApplyFixedMarks
procedure ApplyFixedMarks; virtual; // (main thread) apply mlfFixed to all messages added via MarkFixed
procedure FetchAll(SrcLines: TMessageLines);
procedure SourceLinesInserted(Filename: string; Line, InsertedCount: integer);
procedure SourceLinesDeleted(Filename: string; FirstLine, DeletedCount: integer);
property MessageLineClass: TMessageLineClass read FMessageLineClass;
property OnMarksFixed: TETMarksFixedEvent read FOnMarksFixed write FOnMarksFixed;
property ChangeStamp: int64 read FChangeStamp;
procedure IncreaseChangeStamp; inline;
function IndexOfOutputIndex(OutputIndex: integer): integer;
function EnumerateFile(aFilename: string;
MinLine: integer = 0; MaxLine: integer = High(integer)): TMessageLineEnumerator;
property UpdateSortedSrcPos: boolean // disable this while updating many Filename,Line,Col without changes the order
read FUpdateSortedSrcPos write FUpdateSortedSrcPos;
procedure AddChangedHandler(const OnLineChanged: TNotifyEvent;
AsFirst: boolean = false);
procedure RemoveChangedHandler(const OnLineChanged: TNotifyEvent);
procedure ConsistencyCheck;
end;
{ TExtToolParser
Read the output of a tool, for example the output of the Free Pascal compiler.
It does not filter. Some parsers can work together, for example make and fpc.
Usage: Tool.AddParsers('fpc');
}
TExtToolParser = class(TComponent)
private
FNeedSynchronize: boolean;
FTool: TAbstractExternalTool;
public
destructor Destroy; override; // (main thread)
procedure Init; virtual; // called after macros resolved, before starting thread (main thread)
procedure InitReading; virtual; // called if process started, before first line (worker thread)
procedure Done; virtual; // called after process stopped (worker thread)
procedure ReadLine(Line: string; OutputIndex: integer; var Handled: boolean); virtual; abstract; // (worker thread)
function CreateMsgLine(OutputIndex: integer): TMessageLine; // (worker thread)
procedure AddMsgLine(MsgLine: TMessageLine); // (worker thread)
property Tool: TAbstractExternalTool read FTool;// set when added to a tool
property NeedSynchronize: boolean read FNeedSynchronize write FNeedSynchronize;
procedure ImproveMessages({%H-}aSynchronized: boolean); virtual; // (Synchronized=true->main, else worker thread) called after parsers added lines to Tool.WorkerMessages, Tool is in Critical section
procedure ConsistencyCheck; virtual;
class function IsSubTool(const SubTool: string): boolean; virtual;
class function GetMsgExample({%H-}SubTool: string; {%H-}MsgID: integer): string; virtual;
class function GetMsgHint({%H-}SubTool: string; {%H-}MsgID: integer): string; virtual;
class function GetMsgParser(Msg: TMessageLine; ParserClass: TClass): TExtToolParser;
class function DefaultSubTool: string; virtual; abstract;
class function Priority: integer; virtual; // higher comes first
end;
TExtToolParserClass = class of TExtToolParser;
{ TFPCParser - standard parser for Free Pascal messages, implemented by IDE }
TFPCParser = class(TExtToolParser)
public
class function GetFPCParser(Msg: TMessageLine): TFPCParser;
function GetFPCMsgIDPattern(MsgID: integer): string; virtual; abstract;
class function GetFPCMsgPattern(Msg: TMessageLine): string; virtual; abstract;
class function GetFPCMsgValue1(Msg: TMessageLine): string; virtual; abstract;
class function GetFPCMsgValues(Msg: TMessageLine; out Value1, Value2: string): boolean; virtual; abstract;
end;
{ TMakeParser - standard parser for 'make' messages, implemented by IDE }
TMakeParser = class(TExtToolParser)
end;
{ TDefaultParser - simple parser for simple text output, no filtering }
TDefaultParser = class(TExtToolParser)
public
procedure ReadLine(Line: string; OutputIndex: integer; var Handled: boolean
); override;
class function DefaultSubTool: string; override;
class function Priority: integer; override;
end;
const
DefaultETViewMinUrgency = mluHint;
type
{ TExtToolView }
TExtToolView = class(TComponent)
private
FCaption: string;
FExitStatus: integer;
FLines: TMessageLines;
FMinUrgency: TMessageLineUrgency;
FOnChanged: TNotifyEvent;
FPendingLines: TMessageLines;
FPendingProgressLine: TMessageLine;
FProgressLine: TMessageLine;
FRunning: boolean;
FSummaryMsg: string;
FTool: TAbstractExternalTool;
protected
FLastWorkerMessageCount: integer;
FMessageLineClass: TMessageLineClass;
procedure CreateLines; virtual;
procedure FetchAllPending; virtual; // (main thread)
procedure ToolExited; virtual;
procedure QueueAsyncOnChanged; virtual; abstract; // (worker thread)
procedure RemoveAsyncOnChanged; virtual; abstract; // (main or worker thread)
public
constructor Create(AOwner: TComponent); override; // (main thread)
destructor Destroy; override; // (main thread)
procedure ProcessNewMessages({%H-}AThread: TThread); virtual; // (worker thread, Tool is in Critical section)
procedure ClearLines; // (main thread)
function ApplyPending: boolean; virtual; // true if something changed (main thread)
procedure InputClosed; virtual; // called by Tool when source closed (main thread)
function LineFits(Line: TMessageLine): boolean; virtual; // called by ProcessNewMessages (worker thread)
procedure EnterCriticalSection; virtual;
procedure LeaveCriticalSection; virtual;
procedure ConsistencyCheck; virtual;
public
property Running: boolean read FRunning write FRunning;
property SummaryMsg: string read FSummaryMsg write FSummaryMsg;
property Tool: TAbstractExternalTool read FTool;
property Caption: string read FCaption write FCaption;
property OnChanged: TNotifyEvent read FOnChanged write FOnChanged; // called in main thread
property ExitStatus: integer read FExitStatus write FExitStatus;
property MinUrgency: TMessageLineUrgency read FMinUrgency write FMinUrgency default DefaultETViewMinUrgency; // hide messages below this
property MessageLineClass: TMessageLineClass read FMessageLineClass;
function HasFinished: boolean; virtual; // not running, no pending messages
public
// needs critical section
property PendingLines: TMessageLines read FPendingLines write FPendingLines;
property PendingProgressLine: TMessageLine read FPendingProgressLine write FPendingProgressLine;
property LastWorkerMessageCount: integer read FLastWorkerMessageCount;
public
// only main thread
property Lines: TMessageLines read FLines;
property ProgressLine: TMessageLine read FProgressLine; // valid if ProgressLine.Msg<>''
end;
TExtToolViewClass = class of TExtToolView;
TExternalToolStage = (
etsInit,
etsWaitingForStart, // waiting for a process slot
etsStarting, // creating the process
etsRunning,
etsWaitingForStop,
etsStopped,
etsDestroying
);
TExternalToolStages = set of TExternalToolStage;
TExternalToolNewOutputEvent = procedure(Sender: TObject;
FirstNewMsgLine: integer) of object;
TExternalToolHandler = (
ethNewOutput,
ethStopped,
ethAllViewsUpdated
);
TIDEExternalTools = class;
TExternalToolGroup = class;
{ TAbstractExternalTool
access needs Tool.Enter/LeaveCriticalSection }
TAbstractExternalTool = class(TComponent)
private
FData: TObject;
FEnvironmentOverrides: TStrings;
FEstimatedLoad: int64;
FExitStatus: integer;
FFreeData: boolean;
FGroup: TExternalToolGroup;
FResolveMacrosOnExecute: boolean;
FThread: TThread;
FWorkerDirectory: string;
FWorkerMessages: TMessageLines;
FParsers: TFPList; // list of TExtToolParser
FTitle: string;
FTools: TIDEExternalTools;
FViews: TFPList; // list of TExtToolView
function GetCmdLineParams: string;
function GetParserCount: integer;
function GetParsers(Index: integer): TExtToolParser;
function GetViews(Index: integer): TExtToolView;
procedure SetCmdLineParams(aParams: string);
procedure SetEnvironmentOverrides(AValue: TStrings);
procedure SetGroup(AValue: TExternalToolGroup);
procedure SetTitle(const AValue: string);
procedure AddHandler(HandlerType: TExternalToolHandler;
const AMethod: TMethod; AsFirst: boolean = true);
procedure RemoveHandler(HandlerType: TExternalToolHandler;
const AMethod: TMethod);
protected
FErrorMessage: string;
FExitCode: integer;
FTerminated: boolean;
FHandlers: array[TExternalToolHandler] of TMethodList;
FStage: TExternalToolStage;
FWorkerOutput: TStrings;
FProcess: TProcessUTF8;
FWorkerMessagesClass: TMessageLineClass;
procedure DoCallNotifyHandler(HandlerType: TExternalToolHandler);
function GetExecuteAfter(Index: integer): TAbstractExternalTool; virtual; abstract;
function GetExecuteBefore(Index: integer): TAbstractExternalTool; virtual; abstract;
procedure DoExecute; virtual; abstract; // starts thread, returns immediately
procedure Notification(AComponent: TComponent; Operation: TOperation);
override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure EnterCriticalSection; virtual; // always use before access
procedure LeaveCriticalSection; virtual;
property Thread: TThread read FThread write FThread;
procedure ConsistencyCheck; virtual;
property Title: string read FTitle write SetTitle;
property Data: TObject read FData write FData;
property FreeData: boolean read FFreeData write FFreeData;
property Tools: TIDEExternalTools read FTools;
property Group: TExternalToolGroup read FGroup write SetGroup;
property EstimatedLoad: int64 read FEstimatedLoad write FEstimatedLoad; // used for deciding which tool to run next
// handlers
procedure RemoveAllHandlersOfObject(AnObject: TObject);
procedure AddHandlerOnNewOutput(const OnNewOutput: TExternalToolNewOutputEvent;
AsFirst: boolean = true); // called in main thread
procedure RemoveHandlerOnNewOutput(const OnNewOutput: TExternalToolNewOutputEvent);
procedure AddHandlerOnStopped(const OnStopped: TNotifyEvent;
AsFirst: boolean = true); // called in main thread
procedure RemoveHandlerOnStopped(const OnStopped: TNotifyEvent);
procedure AddHandlerOnAllViewsUpdated(const OnViewsUpdated: TNotifyEvent;
AsFirst: boolean = true); // called in main thread
procedure RemoveHandlerOnAllViewsUpdated(const OnViewsUpdated: TNotifyEvent);
// process
property Process: TProcessUTF8 read FProcess;
property EnvironmentOverrides: TStrings read FEnvironmentOverrides
write SetEnvironmentOverrides; // if not empty, then this and IDE's environemnt will be merged and replace Process.Environment
property CmdLineParams: string read GetCmdLineParams write SetCmdLineParams;
function ResolveMacros: boolean; virtual; abstract; // resolve macros in Process.Executable, Process.CurrentDirectory, Process.Params, Process.Environment on Execute
property ResolveMacrosOnExecute: boolean read FResolveMacrosOnExecute
write FResolveMacrosOnExecute;
property Stage: TExternalToolStage read FStage;
procedure Execute; virtual; abstract;
procedure Terminate; virtual; abstract;
procedure WaitForExit; virtual; abstract;
property Terminated: boolean read FTerminated;
property ExitStatus: integer read FExitStatus write FExitStatus;
property ErrorMessage: string read FErrorMessage write FErrorMessage; // error executing tool
// output
property WorkerOutput: TStrings read FWorkerOutput; // the raw output
property WorkerDirectory: string read FWorkerDirectory write FWorkerDirectory; // changed by parsers, initialized from Process.CurrentDirectory
property WorkerMessages: TMessageLines read FWorkerMessages; // created by parsers
property WorkerMessagesClass: TMessageLineClass read FWorkerMessagesClass;
// parsers
property ParserCount: integer read GetParserCount;
property Parsers[Index: integer]: TExtToolParser read GetParsers; // sorted for Priority
procedure AddParsers(const SubTool: string); // will be freed on Destroy
function AddParser(ParserClass: TExtToolParserClass): TExtToolParser; // will be freed on Destroy
procedure DeleteParser(Parser: TExtToolParser); // disconnect and free
procedure RemoveParser(Parser: TExtToolParser); // disconnect without free
function IndexOfParser(Parser: TExtToolParser): integer;
procedure ClearParsers(Delete: boolean = true);
function FindParser(aParserClass: TExtToolParserClass): TExtToolParser;
// viewers
function ViewCount: integer;
property Views[Index: integer]: TExtToolView read GetViews;
function AddView(View: TExtToolView): integer; // (main thread) will *not* be freed on destroy
procedure DeleteView(View: TExtToolView); // (main thread) disconnect and free
procedure RemoveView(View: TExtToolView); // (main thread) disconnect without free
function IndexOfView(View: TExtToolView): integer;
procedure ClearViews(Delete: boolean = false); // (main thread)
function FindUnfinishedView: TExtToolView;
// dependencies
procedure AddExecuteBefore(Tool: TAbstractExternalTool); virtual; abstract;
function IsExecutedBefore(Tool: TAbstractExternalTool): Boolean; virtual; abstract;// search recursively
procedure RemoveExecuteBefore(Tool: TAbstractExternalTool); virtual; abstract;
function ExecuteBeforeCount: integer; virtual; abstract;
property ExecuteBefore[Index: integer]: TAbstractExternalTool read GetExecuteBefore;
function ExecuteAfterCount: integer; virtual; abstract;
property ExecuteAfter[Index: integer]: TAbstractExternalTool read GetExecuteAfter;
end;
{ TExternalToolGroup }
TExternalToolGroup = class(TComponent)
private
FAbortIfOneFails: boolean;
FItems: TFPList; // list of TAbstractExternalTool
function GetItems(Index: integer): TAbstractExternalTool;
procedure InternalRemove(Tool: TAbstractExternalTool); virtual;
procedure InternallAdd(Tool: TAbstractExternalTool); virtual;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Clear; virtual;
function Count: integer;
property Items[Index: integer]: TAbstractExternalTool read GetItems; default;
property AbortIfOneFails: boolean read FAbortIfOneFails write FAbortIfOneFails;
procedure ToolExited(Tool: TAbstractExternalTool); virtual;
end;
{ TIDEExternalTools }
TIDEExternalTools = class(TComponent)
private
function GetItems(Index: integer): TAbstractExternalTool; inline;
protected
fItems: TFPList; // list of TAbstractExternalTool
function GetParsers(Index: integer): TExtToolParserClass; virtual; abstract; // (main thread)
public
constructor Create(aOwner: TComponent); override;
destructor Destroy; override;
function Count: integer; inline;
procedure TerminateAll; virtual; abstract;
procedure Clear; virtual; abstract; // terminate + free all tools
property Items[Index: integer]: TAbstractExternalTool read GetItems; default;
function Add(Title: string): TAbstractExternalTool; virtual; abstract;
procedure ConsistencyCheck; virtual;
procedure EnterCriticalSection; virtual; abstract;
procedure LeaveCriticalSection; virtual; abstract;
// parsers
procedure RegisterParser(Parser: TExtToolParserClass); virtual; abstract; // (main thread)
procedure UnregisterParser(Parser: TExtToolParserClass); virtual; abstract; // (main thread)
function FindParser(const SubTool: string): TExtToolParserClass; virtual; abstract; // (main thread)
function ParserCount: integer; virtual; abstract; // (main thread)
property Parsers[Index: integer]: TExtToolParserClass read GetParsers; // (main thread)
function GetMsgExample(SubTool: string; MsgID: integer): string; virtual; // (main thread)
function GetMsgHint(SubTool: string; MsgID: integer): string; virtual; // (main thread)
function GetMsgTool(Msg: TMessageLine): TAbstractExternalTool; virtual; abstract;
end;
var
ExternalToolList: TIDEExternalTools = nil; // will be set by the IDE
type
{ TIDEExternalToolOptions }
TETMacroFunction = function(var aValue: string): boolean of object;
TIDEExternalToolOptions = class
private
fCmdLineParams: string;
FCustomMacroFunction: TETMacroFunction;
FEnvironmentOverrides: TStringList;
FExecutable: string;
FQuiet: boolean;
FResolveMacros: boolean;
FScanners: TStrings;
fTitle: string;
fWorkingDirectory: string;
procedure SetEnvironmentOverrides(AValue: TStringList);
procedure SetScanners(AValue: TStrings);
public
constructor Create;
destructor Destroy; override;
procedure Assign(Source: TIDEExternalToolOptions);
function Equals(Obj: TObject): boolean; override;
procedure Clear;
property Title: string read fTitle write fTitle;
property Executable: string read FExecutable write FExecutable;
property Filename: string read FExecutable write FExecutable; deprecated;
property CmdLineParams: string read fCmdLineParams write fCmdLineParams;
property WorkingDirectory: string read fWorkingDirectory write fWorkingDirectory;
property EnvironmentOverrides: TStringList read FEnvironmentOverrides write SetEnvironmentOverrides;
property Scanners: TStrings read FScanners write SetScanners;
property ResolveMacros: boolean read FResolveMacros write FResolveMacros default true;
property CustomMacroFunction: TETMacroFunction read FCustomMacroFunction write FCustomMacroFunction;
property Quiet: boolean read FQuiet write FQuiet; // no user dialogs about errors
end;
type
TRunExternalTool = function(Tool: TIDEExternalToolOptions): boolean of object;
var
RunExternalTool: TRunExternalTool = nil;// set by the IDE
function CompareMsgLinesSrcPos(MsgLine1, MsgLine2: Pointer): integer;
function dbgs(u: TMessageLineUrgency): string; overload;
function dbgs(f: TMessageLineFlag): string; overload;
function dbgs(Flags: TMessageLineFlags): string; overload;
function dbgs(s: TExternalToolStage): string; overload;
implementation
function CompareMsgLinesSrcPos(MsgLine1, MsgLine2: Pointer): integer;
var
Line1: TMessageLine absolute MsgLine1;
Line2: TMessageLine absolute MsgLine2;
begin
Result:=CompareFilenames(Line1.Filename,Line2.Filename);
if Result<>0 then exit;
if Line1.Line<Line2.Line then exit(-1)
else if Line1.Line>Line2.Line then exit(1);
if Line1.Column<Line2.Column then exit(-1)
else if Line1.Column>Line2.Column then exit(1);
if Line1.OutputIndex<Line2.OutputIndex then exit(-1)
else if Line1.OutputIndex>Line2.OutputIndex then exit(1);
if Line1.Index<Line2.Index then exit(-1)
else if Line1.Index>Line2.Index then exit(1);
Result:=0;
end;
function dbgs(u: TMessageLineUrgency): string;
begin
Result:='';
WriteStr(Result,u);
end;
function dbgs(f: TMessageLineFlag): string;
begin
Result:='';
WriteStr(Result,f);
end;
function dbgs(Flags: TMessageLineFlags): string;
var
f: TMessageLineFlag;
begin
Result:='';
for f in Flags do begin
if Result<>'' then Result+=',';
Result+=dbgs(f);
end;
Result:='['+Result+']';
end;
function dbgs(s: TExternalToolStage): string;
begin
Result:='';
WriteStr(Result,s);
end;
{ TFPCParser }
class function TFPCParser.GetFPCParser(Msg: TMessageLine): TFPCParser;
begin
Result:=TFPCParser(GetMsgParser(Msg,TFPCParser));
end;
{ TIDEExternalToolOptions }
procedure TIDEExternalToolOptions.SetEnvironmentOverrides(AValue: TStringList);
begin
if FEnvironmentOverrides.Equals(AValue) then Exit;
FEnvironmentOverrides.Assign(AValue);
end;
procedure TIDEExternalToolOptions.SetScanners(AValue: TStrings);
begin
if FScanners.Equals(AValue) then Exit;
FScanners.Assign(AValue);
end;
constructor TIDEExternalToolOptions.Create;
begin
ResolveMacros:=true;
FEnvironmentOverrides:=TStringList.Create;
FScanners:=TStringList.Create;
end;
destructor TIDEExternalToolOptions.Destroy;
begin
FreeAndNil(FEnvironmentOverrides);
FreeAndNil(FScanners);
inherited Destroy;
end;
procedure TIDEExternalToolOptions.Assign(Source: TIDEExternalToolOptions);
begin
Title:=Source.Title;
Executable:=Source.Executable;
CmdLineParams:=Source.CmdLineParams;
WorkingDirectory:=Source.WorkingDirectory;
EnvironmentOverrides:=Source.EnvironmentOverrides;
Scanners:=Source.Scanners;
ResolveMacros:=Source.ResolveMacros;
CustomMacroFunction:=Source.CustomMacroFunction;
Quiet:=Source.Quiet;
end;
function TIDEExternalToolOptions.Equals(Obj: TObject): boolean;
var
Source: TIDEExternalToolOptions;
begin
if Obj=Self then exit(true);
if Obj is TIDEExternalToolOptions then begin
Source:=TIDEExternalToolOptions(Obj);
Result:=(Title=Source.Title)
and (Executable=Source.Executable)
and (CmdLineParams=Source.CmdLineParams)
and (WorkingDirectory=Source.WorkingDirectory)
and EnvironmentOverrides.Equals(Source.EnvironmentOverrides)
and Scanners.Equals(Source.Scanners)
and (ResolveMacros=Source.ResolveMacros)
and CompareMethods(TMethod(CustomMacroFunction),TMethod(Source.CustomMacroFunction))
and (Quiet=Source.Quiet);
end else
Result:=inherited Equals(Obj);
end;
procedure TIDEExternalToolOptions.Clear;
begin
fCmdLineParams:='';
FCustomMacroFunction:=nil;
FEnvironmentOverrides.Clear;
FExecutable:='';
FResolveMacros:=true;
FScanners.Clear;
fTitle:='';
fWorkingDirectory:='';
FQuiet:=false;
end;
{ TExternalToolGroup }
function TExternalToolGroup.GetItems(Index: integer): TAbstractExternalTool;
begin
Result:=TAbstractExternalTool(FItems[Index]);
end;
constructor TExternalToolGroup.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FItems:=TFPList.Create;
FAbortIfOneFails:=true;
end;
destructor TExternalToolGroup.Destroy;
begin
Clear;
FreeAndNil(FItems);
inherited Destroy;
end;
procedure TExternalToolGroup.Clear;
var
i: Integer;
begin
for i:=Count-1 downto 0 do
Items[i].Group:=nil;
end;
function TExternalToolGroup.Count: integer;
begin
Result:=FItems.Count;
end;
procedure TExternalToolGroup.InternalRemove(Tool: TAbstractExternalTool);
begin
FItems.Remove(Tool);
if FItems.Count>0 then exit;
if csDestroying in ComponentState then exit;
Free;
end;
procedure TExternalToolGroup.InternallAdd(Tool: TAbstractExternalTool);
begin
if FItems.IndexOf(Tool)>=0 then
raise Exception.Create('already in group');
FItems.Add(Tool);
end;
procedure TExternalToolGroup.ToolExited(Tool: TAbstractExternalTool);
var
i: Integer;
begin
//debugln(['TExternalToolGroup.ToolExited START ',Tool.Title,' Error=',Tool.ErrorMessage,' AbortIfOneFails=',AbortIfOneFails]);
if (Tool.ErrorMessage<>'') and AbortIfOneFails then begin
for i:=Count-1 downto 0 do
Items[i].Terminate;
end;
end;
{ TDefaultParser }
procedure TDefaultParser.ReadLine(Line: string; OutputIndex: integer;
var Handled: boolean);
var
MsgLine: TMessageLine;
begin
Handled:=true;
//debugln(['TDefaultParser.ReadLine ',Line]);
MsgLine:=CreateMsgLine(OutputIndex);
MsgLine.Msg:=Line;
MsgLine.Urgency:=mluImportant;
AddMsgLine(MsgLine);
end;
class function TDefaultParser.DefaultSubTool: string;
begin
Result:=SubToolDefault;
end;
class function TDefaultParser.Priority: integer;
begin
Result:=SubToolDefaultPriority;
end;
{ TMessageLineEnumerator }
function TMessageLineEnumerator.GetCurrent: TMessageLine;
begin
Result:=TMessageLine(FCurrent.Data);
end;
constructor TMessageLineEnumerator.Create(Tree: TAvgLvlTree;
const aFilename: string; aMinLine, aMaxLine: integer);
begin
FTree:=Tree;
FFilename:=aFilename;
fMinLine:=aMinLine;
FMaxLine:=aMaxLine;
end;
function TMessageLineEnumerator.GetEnumerator: TMessageLineEnumerator;
begin
Result:=Self;
end;
function TMessageLineEnumerator.MoveNext: boolean;
var
Line: TMessageLine;
CmpLine: TMessageLine;
begin
Result:=false;
if FCurrent=nil then begin
CmpLine:=TMessageLine.Create;
try
CmpLine.Filename:=FFilename;
CmpLine.Line:=MinLine;
CmpLine.Column:=Low(Integer);
FCurrent:=FTree.FindNearest(CmpLine);
if FCurrent=nil then exit;
if FTree.Compare(FCurrent.Data,CmpLine)<0 then
FCurrent:=FCurrent.Successor;
finally
CmpLine.Free;
end;
end else begin
FCurrent:=FCurrent.Successor;
end;
if FCurrent=nil then
exit;
Line:=Current;
if CompareFilenames(Line.Filename,FFilename)<>0 then exit;
if Line.Line>MaxLine then exit;
Result:=true;
end;
{ TAbstractExternalTool }
function TAbstractExternalTool.GetParserCount: integer;
begin
Result:=FParsers.Count;
end;
function TAbstractExternalTool.GetCmdLineParams: string;
begin
Result:=MergeCmdLineParams(Process.Parameters);
end;
function TAbstractExternalTool.GetParsers(Index: integer): TExtToolParser;
begin
Result:=TExtToolParser(FParsers[Index]);
end;
function TAbstractExternalTool.GetViews(Index: integer): TExtToolView;
begin
Result:=TExtToolView(FViews[Index]);
end;
procedure TAbstractExternalTool.SetCmdLineParams(aParams: string);
var
sl: TStringList;
begin
sl:=TStringList.Create;
try
SplitCmdLineParams(aParams,sl);
Process.Parameters:=sl;
finally
sl.Free;
end;
end;
procedure TAbstractExternalTool.SetEnvironmentOverrides(AValue: TStrings);
begin
if (FEnvironmentOverrides=AValue)
or (FEnvironmentOverrides.Equals(AValue)) then Exit;
FEnvironmentOverrides.Assign(AValue);
end;
procedure TAbstractExternalTool.SetGroup(AValue: TExternalToolGroup);
begin
if FGroup=AValue then Exit;
if Group<>nil then
Group.InternalRemove(Self);
FGroup:=AValue;
if Group<>nil then
Group.InternallAdd(Self);
end;
procedure TAbstractExternalTool.SetTitle(const AValue: string);
begin
if FTitle=AValue then exit;
FTitle:=AValue;
end;
procedure TAbstractExternalTool.AddHandler(HandlerType: TExternalToolHandler;
const AMethod: TMethod; AsFirst: boolean);
begin
if FHandlers[HandlerType]=nil then
FHandlers[HandlerType]:=TMethodList.Create;
FHandlers[HandlerType].Add(AMethod,not AsFirst);
end;
procedure TAbstractExternalTool.RemoveHandler(
HandlerType: TExternalToolHandler; const AMethod: TMethod);
begin
FHandlers[HandlerType].Remove(AMethod);
end;
procedure TAbstractExternalTool.DoCallNotifyHandler(
HandlerType: TExternalToolHandler);
begin
FHandlers[HandlerType].CallNotifyEvents(Self);
end;
procedure TAbstractExternalTool.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if Operation=opRemove then begin
EnterCriticalSection;
try
if FViews<>nil then
FViews.Remove(AComponent);
finally
LeaveCriticalSection;
end;
end;
end;
constructor TAbstractExternalTool.Create(AOwner: TComponent);
begin
if AOwner is TIDEExternalTools then
FTools:=TIDEExternalTools(AOwner);
inherited Create(AOwner);
if FWorkerMessagesClass=nil then
FWorkerMessagesClass:=TMessageLine;
FWorkerMessages:=TMessageLines.Create(Self,FWorkerMessagesClass);
FParsers:=TFPList.Create;
FViews:=TFPList.Create;
FStage:=etsInit;
FEstimatedLoad:=1;
FEnvironmentOverrides:=TStringList.Create;
end;
destructor TAbstractExternalTool.Destroy;
var
h: TExternalToolHandler;
begin
EnterCriticalSection;
try
if FreeData then FreeAndNil(FData);
ClearParsers;
ClearViews;
Group:=nil;
for h:=low(FHandlers) to high(FHandlers) do
FreeAndNil(FHandlers[h]);
FWorkerMessages.Clear;
FreeAndNil(FParsers);
FreeAndNil(FViews);
FreeAndNil(FEnvironmentOverrides);
inherited Destroy;
finally
LeaveCriticalsection;
end;
FreeAndNil(FWorkerMessages);
end;
procedure TAbstractExternalTool.EnterCriticalSection;
begin
FWorkerMessages.EnterCriticalSection;
end;
procedure TAbstractExternalTool.LeaveCriticalSection;
begin
FWorkerMessages.LeaveCriticalSection;
end;
procedure TAbstractExternalTool.ConsistencyCheck;
var
i: Integer;
begin
EnterCriticalSection;
try
for i:=0 to ParserCount-1 do
Parsers[i].ConsistencyCheck;
finally
LeaveCriticalSection;
end;
end;
procedure TAbstractExternalTool.RemoveAllHandlersOfObject(AnObject: TObject);
var
HandlerType: TExternalToolHandler;
begin
for HandlerType:=Low(HandlerType) to High(HandlerType) do
FHandlers[HandlerType].RemoveAllMethodsOfObject(AnObject);
end;
procedure TAbstractExternalTool.AddHandlerOnStopped(
const OnStopped: TNotifyEvent; AsFirst: boolean);
begin
AddHandler(ethStopped,TMethod(OnStopped),AsFirst);
end;
procedure TAbstractExternalTool.RemoveHandlerOnStopped(
const OnStopped: TNotifyEvent);
begin
RemoveHandler(ethStopped,TMethod(OnStopped));
end;
procedure TAbstractExternalTool.AddHandlerOnAllViewsUpdated(
const OnViewsUpdated: TNotifyEvent; AsFirst: boolean);
begin
AddHandler(ethAllViewsUpdated,TMethod(OnViewsUpdated),AsFirst);
end;
procedure TAbstractExternalTool.RemoveHandlerOnAllViewsUpdated(
const OnViewsUpdated: TNotifyEvent);
begin
RemoveHandler(ethAllViewsUpdated,TMethod(OnViewsUpdated));
end;
procedure TAbstractExternalTool.AddHandlerOnNewOutput(
const OnNewOutput: TExternalToolNewOutputEvent; AsFirst: boolean);
begin
AddHandler(ethNewOutput,TMethod(OnNewOutput),AsFirst);
end;
procedure TAbstractExternalTool.RemoveHandlerOnNewOutput(
const OnNewOutput: TExternalToolNewOutputEvent);
begin
RemoveHandler(ethNewOutput,TMethod(OnNewOutput));
end;
procedure TAbstractExternalTool.AddParsers(const SubTool: string);
var
ParserClass: TExtToolParserClass;
i: Integer;
Found: Boolean;
begin
Found:=false;
for i:=0 to ExternalToolList.ParserCount-1 do begin
ParserClass:=ExternalToolList.Parsers[i];
if not ParserClass.IsSubTool(SubTool) then continue;
Found:=true;
AddParser(ParserClass);
end;
if not Found then
raise Exception.Create('unable to find parser for tool "'+SubTool+'"');
end;
function TAbstractExternalTool.AddParser(ParserClass: TExtToolParserClass
): TExtToolParser;
var
i: Integer;
begin
for i:=0 to ParserCount-1 do begin
Result:=Parsers[i];
if Result.ClassType=ParserClass then
exit;
end;
Result:=ParserClass.Create(nil);
i:=0;
while (i<FParsers.Count) and (Parsers[i].Priority>=ParserClass.Priority) do
inc(i);
FParsers.Insert(i,Result);
Result.FTool:=Self;
end;
procedure TAbstractExternalTool.RemoveParser(Parser: TExtToolParser);
begin
FParsers.Remove(Parser);
if Parser.Tool<>Self then exit;
Parser.FTool:=nil;
end;
function TAbstractExternalTool.IndexOfParser(Parser: TExtToolParser): integer;
begin
Result:=FParsers.IndexOf(Parser);
end;
procedure TAbstractExternalTool.DeleteParser(Parser: TExtToolParser);
begin
Parser.Free;
end;
procedure TAbstractExternalTool.ClearParsers(Delete: boolean);
begin
while ParserCount>0 do
if Delete then
DeleteParser(Parsers[ParserCount-1])
else
RemoveParser(Parsers[ParserCount-1]);
end;
function TAbstractExternalTool.FindParser(aParserClass: TExtToolParserClass
): TExtToolParser;
var
i: Integer;
begin
for i:=0 to ParserCount-1 do begin
Result:=Parsers[i];
if Result.InheritsFrom(aParserClass) then exit;
end;
Result:=nil;
end;
function TAbstractExternalTool.ViewCount: integer;
begin
Result:=FViews.Count;
end;
function TAbstractExternalTool.AddView(View: TExtToolView): integer;
begin
View.EnterCriticalSection;
try
if View.Tool<>nil then
raise Exception.Create('');
Result:=FViews.Add(View);
FreeNotification(View);
View.FTool:=Self;
View.Lines.BaseDirectory:=WorkerDirectory;
finally
View.LeaveCriticalSection;
end;
end;
procedure TAbstractExternalTool.RemoveView(View: TExtToolView);
begin
View.EnterCriticalSection;
try
View.fTool:=nil;
FViews.Remove(View);
finally
View.LeaveCriticalSection;
end;
end;
function TAbstractExternalTool.IndexOfView(View: TExtToolView): integer;
begin
Result:=FViews.IndexOf(View);
end;
procedure TAbstractExternalTool.DeleteView(View: TExtToolView);
begin
EnterCriticalSection;
try
RemoveView(View);
View.Free;
finally
LeaveCriticalSection;
end;
end;
procedure TAbstractExternalTool.ClearViews(Delete: boolean);
begin
EnterCriticalSection;
try
while ViewCount>0 do
if Delete then
DeleteView(Views[ViewCount-1])
else
RemoveView(Views[ViewCount-1]);
finally
LeaveCriticalSection;
end;
end;
function TAbstractExternalTool.FindUnfinishedView: TExtToolView;
var
i: Integer;
begin
for i:=0 to ViewCount-1 do begin
Result:=Views[i];
if not Result.HasFinished then exit;
end;
Result:=nil;
end;
{ TExtToolParser }
destructor TExtToolParser.Destroy;
begin
if Tool<>nil then
Tool.RemoveParser(Self);
inherited Destroy;
end;
procedure TExtToolParser.Init;
begin
end;
procedure TExtToolParser.InitReading;
begin
end;
procedure TExtToolParser.Done;
begin
end;
function TExtToolParser.CreateMsgLine(OutputIndex: integer): TMessageLine;
begin
Result:=Tool.WorkerMessages.CreateLine(OutputIndex);
if OutputIndex>=0 then
Result.Msg:=Tool.WorkerOutput[OutputIndex]; // use raw output as default msg
end;
procedure TExtToolParser.AddMsgLine(MsgLine: TMessageLine);
begin
Tool.WorkerMessages.Add(MsgLine);
end;
procedure TExtToolParser.ImproveMessages(aSynchronized: boolean);
begin
end;
procedure TExtToolParser.ConsistencyCheck;
begin
end;
class function TExtToolParser.IsSubTool(const SubTool: string): boolean;
begin
Result:=CompareText(DefaultSubTool,SubTool)=0;
end;
class function TExtToolParser.GetMsgExample(SubTool: string; MsgID: integer
): string;
begin
Result:='';
end;
class function TExtToolParser.GetMsgHint(SubTool: string; MsgID: integer
): string;
begin
Result:='';
end;
class function TExtToolParser.GetMsgParser(Msg: TMessageLine;
ParserClass: TClass): TExtToolParser;
var
aTool: TAbstractExternalTool;
begin
Result:=nil;
if ExternalToolList=nil then exit;
aTool:=ExternalToolList.GetMsgTool(Msg);
if aTool=nil then exit;
Result:=aTool.FindParser(TExtToolParserClass(ParserClass));
end;
class function TExtToolParser.Priority: integer;
begin
Result:=0;
end;
{ TIDEExternalTools }
// inline
function TIDEExternalTools.GetItems(Index: integer): TAbstractExternalTool;
begin
Result:=TAbstractExternalTool(fItems[Index]);
end;
// inline
function TIDEExternalTools.Count: integer;
begin
Result:=fItems.Count;
end;
constructor TIDEExternalTools.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
fItems:=TFPList.Create;
end;
destructor TIDEExternalTools.Destroy;
begin
inherited Destroy;
FreeAndNil(fItems);
if ExternalToolList=Self then
ExternalToolList:=nil;
end;
procedure TIDEExternalTools.ConsistencyCheck;
var
i: Integer;
begin
for i:=0 to Count-1 do
Items[i].ConsistencyCheck;
end;
function TIDEExternalTools.GetMsgExample(SubTool: string; MsgID: integer
): string;
var
Parser: TExtToolParserClass;
i: Integer;
begin
Result:='';
for i:=0 to ParserCount-1 do begin
Parser:=Parsers[i];
Result:=Parser.GetMsgExample(SubTool,MsgID);
if Result<>'' then exit;
end;
end;
function TIDEExternalTools.GetMsgHint(SubTool: string; MsgID: integer): string;
var
Parser: TExtToolParserClass;
i: Integer;
begin
Result:='';
for i:=0 to ParserCount-1 do begin
Parser:=Parsers[i];
Result:=Parser.GetMsgHint(SubTool,MsgID);
if Result<>'' then exit;
end;
end;
{ TMessageLines }
function TMessageLines.GetItems(Index: integer): TMessageLine;
begin
Result:=TMessageLine(fItems[Index]);
end;
procedure TMessageLines.SetBaseDirectory(const AValue: string);
var
NewValue: String;
begin
NewValue:=CleanAndExpandDirectory(AValue);
if FBaseDirectory=NewValue then exit;
FBaseDirectory:=NewValue;
IncreaseChangeStamp;
end;
procedure TMessageLines.LineChanged(Line: TMessageLine);
begin
IncreaseChangeStamp;
if fChangedHandler<>nil then
fChangedHandler.CallNotifyEvents(Line);
end;
constructor TMessageLines.Create(aOwner: TObject;
aMsgLineClass: TMessageLineClass);
begin
FOwner:=aOwner;
InitCriticalSection(FCritSec);
FMessageLineClass:=aMsgLineClass;
fItems:=TFPList.Create;
FSortedForSrcPos:=TAvgLvlTree.Create(@CompareMsgLinesSrcPos);
FUpdateSortedSrcPos:=true;
fChangedHandler:=TMethodList.Create;
end;
destructor TMessageLines.Destroy;
begin
EnterCriticalSection;
try
Clear;
FreeAndNil(FSortedForSrcPos);
FreeAndNil(fItems);
inherited Destroy;
FreeAndNil(fChangedHandler);
finally
LeaveCriticalsection;
end;
DoneCriticalsection(FCritSec);
end;
procedure TMessageLines.EnterCriticalSection;
begin
System.EnterCriticalsection(FCritSec);
end;
procedure TMessageLines.LeaveCriticalSection;
begin
System.LeaveCriticalsection(FCritSec);
end;
function TMessageLines.Count: integer;
begin
Result:=fItems.Count;
end;
procedure TMessageLines.Clear;
var
i: Integer;
c: TMessageLineUrgency;
begin
if fItems.Count=0 then exit;
FreeAndNil(fMarkedFixed);
for i:=0 to fItems.Count-1 do
TObject(fItems[i]).Free;
fItems.Clear;
FSortedForSrcPos.Clear;
for c:=low(UrgencyCounts) to high(UrgencyCounts) do UrgencyCounts[c]:=0;
IncreaseChangeStamp;
end;
function TMessageLines.CreateLine(OutputIndex: integer): TMessageLine;
begin
Result:=MessageLineClass.Create;
Result.FIndex:=-1;
Result.FOutputIndex:=OutputIndex;
end;
procedure TMessageLines.Add(MsgLine: TMessageLine);
begin
if MsgLine.Index>=0 then
raise Exception.Create('TMessageLines.Add already added');
MsgLine.FLines:=Self;
MsgLine.FIndex:=fItems.Add(MsgLine);
FSortedForSrcPos.Add(MsgLine);
inc(UrgencyCounts[MsgLine.Urgency]);
LineChanged(MsgLine);
end;
procedure TMessageLines.Remove(MsgLine: TMessageLine);
var
i: Integer;
begin
if MsgLine.FLines<>Self then
raise Exception.Create('');
FSortedForSrcPos.Remove(MsgLine);
fItems.Delete(MsgLine.Index);
for i:=MsgLine.Index to Count-1 do
Items[i].FIndex:=i;
MsgLine.FLines:=nil;
dec(UrgencyCounts[MsgLine.Urgency]);
IncreaseChangeStamp;
end;
procedure TMessageLines.Delete(MsgLine: TMessageLine);
begin
Remove(MsgLine);
MsgLine.Free;
end;
procedure TMessageLines.MarkFixed(MsgLine: TMessageLine);
begin
//debugln(['TMessageLines.MarkFixed ',MsgLine.Msg,' ',MsgLine.Line,',',MsgLine.Column]);
if fMarkedFixed=nil then
fMarkedFixed:=TAvgLvlTree.Create;
if fMarkedFixed.Find(MsgLine)=nil then
fMarkedFixed.Add(MsgLine);
end;
procedure TMessageLines.ApplyFixedMarks;
var
Node: TAvgLvlTreeNode;
Msg: TMessageLine;
List: TFPList;
begin
//debugln(['TMessageLines.ApplyFixedMarks ']);
if fMarkedFixed=nil then exit;
List:=TFPList.Create;
try
for Node in fMarkedFixed do begin
Msg:=TMessageLine(Node.Data);
if mlfFixed in Msg.Flags then Continue;
Msg.Flags:=Msg.Flags+[mlfFixed];
List.Add(Msg);
end;
if List.Count=0 then exit;
if Assigned(OnMarksFixed) then
OnMarksFixed(List);
finally
FreeAndNil(fMarkedFixed);
List.Free;
end;
end;
procedure TMessageLines.FetchAll(SrcLines: TMessageLines);
var
i: Integer;
u: TMessageLineUrgency;
MsgLine: TMessageLine;
begin
if (SrcLines=nil) or (SrcLines=Self) or (SrcLines.Count=0) then exit;
SrcLines.FSortedForSrcPos.Clear;
for u:=low(TMessageLineUrgency) to high(TMessageLineUrgency) do
UrgencyCounts[u]:=0;
for i:=0 to SrcLines.Count-1 do begin
MsgLine:=SrcLines[i];
//debugln(['TMessageLines.FetchAll ',MsgLine.Msg]);
MsgLine.FLines:=Self;
MsgLine.FIndex:=fItems.Add(MsgLine);
FSortedForSrcPos.Add(MsgLine);
inc(UrgencyCounts[MsgLine.Urgency]);
LineChanged(MsgLine);
end;
SrcLines.fItems.Clear;
IncreaseChangeStamp;
end;
procedure TMessageLines.SourceLinesInserted(Filename: string; Line,
InsertedCount: integer);
// adjust Line numbers in all messages
var
CmpLine: TMessageLine;
Node: TAvgLvlTreeNode;
MsgLine: TMessageLine;
begin
if (Filename='') or (Count<=0) then exit;
CmpLine:=TMessageLine.Create;
try
CmpLine.Filename:=Filename;
CmpLine.Line:=Line;
Node:=FSortedForSrcPos.FindNearest(CmpLine);
if Node=nil then exit;
Node:=FSortedForSrcPos.FindLeftMost(CmpLine);
// Note: if no exact node was found, Node can be one too less or too high
if FSortedForSrcPos.Compare(Node,CmpLine)<0 then
Node:=FSortedForSrcPos.FindSuccessor(Node);
// adjust line numbers behind (in same source)
CmpLine.Line:=High(integer);
while (Node<>nil) and (FSortedForSrcPos.Compare(Node,CmpLine)<0) do begin
MsgLine:=TMessageLine(Node.Data);
inc(MsgLine.FLine,InsertedCount);
LineChanged(MsgLine);
Node:=FSortedForSrcPos.FindSuccessor(Node);
end;
finally
CmpLine.Free;
end;
end;
procedure TMessageLines.SourceLinesDeleted(Filename: string; FirstLine,
DeletedCount: integer);
// adjust Line numbers in all messages and mark lines in range as deleted
var
CmpLine: TMessageLine;
Node: TAvgLvlTreeNode;
MsgLine: TMessageLine;
begin
if (Filename='') or (Count<=0) then exit;
CmpLine:=TMessageLine.Create;
try
CmpLine.Filename:=Filename;
CmpLine.Line:=FirstLine;
Node:=FSortedForSrcPos.FindNearest(CmpLine);
if Node=nil then exit;
Node:=FSortedForSrcPos.FindLeftMost(CmpLine);
// Note: if no exact node was found, Node can be one too less or too high
if FSortedForSrcPos.Compare(Node,CmpLine)<0 then
Node:=FSortedForSrcPos.FindSuccessor(Node);
// mark lines as deleted
CmpLine.Line:=FirstLine+DeletedCount;
while (Node<>nil) and (FSortedForSrcPos.Compare(Node,CmpLine)<0) do begin
MsgLine:=TMessageLine(Node.Data);
MsgLine.Flags:=MsgLine.Flags+[mlfFixed];
Node:=FSortedForSrcPos.FindSuccessor(Node);
end;
// adjust line numbers behind (in same source)
CmpLine.Line:=High(integer);
while (Node<>nil) and (FSortedForSrcPos.Compare(Node,CmpLine)<0) do begin
MsgLine:=TMessageLine(Node.Data);
dec(MsgLine.FLine,DeletedCount);
LineChanged(MsgLine);
Node:=FSortedForSrcPos.FindSuccessor(Node);
end;
finally
CmpLine.Free;
end;
end;
procedure TMessageLines.IncreaseChangeStamp;
begin
LUIncreaseChangeStamp64(FChangeStamp);
end;
function TMessageLines.IndexOfOutputIndex(OutputIndex: integer): integer;
var
l: Integer;
r: Integer;
CurOutputIndex: Integer;
begin
l:=0;
r:=Count-1;
while (l<=r) do begin
Result:=(l+r) div 2;
CurOutputIndex:=Items[Result].OutputIndex;
if CurOutputIndex>OutputIndex then
r:=Result-1
else if CurOutputIndex<OutputIndex then
l:=Result+1
else
exit;
end;
Result:=-1;
end;
function TMessageLines.EnumerateFile(aFilename: string; MinLine: integer;
MaxLine: integer): TMessageLineEnumerator;
begin
Result:=TMessageLineEnumerator.Create(FSortedForSrcPos,aFilename,MinLine,MaxLine);
end;
procedure TMessageLines.AddChangedHandler(const OnLineChanged: TNotifyEvent;
AsFirst: boolean);
begin
fChangedHandler.Add(TMethod(OnLineChanged),not AsFirst);
end;
procedure TMessageLines.RemoveChangedHandler(const OnLineChanged: TNotifyEvent);
begin
fChangedHandler.Remove(TMethod(OnLineChanged));
end;
procedure TMessageLines.ConsistencyCheck;
begin
FSortedForSrcPos.ConsistencyCheck;
end;
{ TMessageLine }
//inline
function TMessageLine.GetShortFilename: string;
begin
Result:=ExtractFileName(Filename);
end;
//inline
function TMessageLine.GetFullFilename: string;
begin
Result:=Filename;
end;
function TMessageLine.GetAttribute(const Identifier: string): string;
begin
if fAttributes=nil then
Result:=''
else
Result:=fAttributes.Values[Identifier];
end;
procedure TMessageLine.SetAttribute(const Identifier: string;
const AValue: string);
begin
if GetAttribute(Identifier)=AValue then exit;
if fAttributes=nil then
fAttributes:=TStringList.Create;
fAttributes.Values[Identifier]:=AValue;
IncreaseChangeStamp;
end;
procedure TMessageLine.SetColumn(const AValue: integer);
begin
if FColumn=AValue then exit;
SortedSrcPosUnbind;
FColumn:=AValue;
SortedSrcPosBind;
IncreaseChangeStamp;
end;
procedure TMessageLine.SetFilename(AValue: string);
begin
AValue:=TrimFilename(AValue);
if not FilenameIsAbsolute(AValue) then begin
if (FLines<>nil)
and (FLines.BaseDirectory<>'') then
AValue:=AppendPathDelim(FLines.BaseDirectory)+AValue;
end;
if FFilename=AValue then exit;
SortedSrcPosUnbind;
FFilename:=AValue;
SortedSrcPosBind;
IncreaseChangeStamp;
end;
procedure TMessageLine.SetFlags(AValue: TMessageLineFlags);
begin
if FFlags=AValue then Exit;
FFlags:=AValue;
IncreaseChangeStamp;
end;
procedure TMessageLine.SetLine(const AValue: integer);
begin
if FLine=AValue then exit;
SortedSrcPosUnbind;
FLine:=AValue;
SortedSrcPosBind;
IncreaseChangeStamp;
end;
procedure TMessageLine.SortedSrcPosBind;
begin
if (Index>=0) and Lines.UpdateSortedSrcPos then
Lines.FSortedForSrcPos.Add(Self);
end;
procedure TMessageLine.SortedSrcPosUnbind;
begin
if (Index>=0) and Lines.UpdateSortedSrcPos then
Lines.FSortedForSrcPos.Remove(Self);
end;
procedure TMessageLine.SetLines(AValue: TMessageLines);
begin
if FLines=AValue then exit;
if FLines<>nil then begin
SortedSrcPosUnbind;
FLines.Remove(Self);
end;
FLines:=AValue;
if FLines<>nil then begin
FLines.Add(Self);
if (not FilenameIsAbsolute(FFilename))
and (Lines.BaseDirectory<>'') then
FFilename:=AppendPathDelim(Lines.BaseDirectory)+FFilename;
SortedSrcPosBind;
end else
FLines:=nil;
end;
procedure TMessageLine.SetMsg(AValue: string);
begin
if FMsg=AValue then Exit;
FMsg:=AValue;
IncreaseChangeStamp;
end;
procedure TMessageLine.SetMsgID(AValue: integer);
begin
if FMsgID=AValue then Exit;
FMsgID:=AValue;
IncreaseChangeStamp;
end;
procedure TMessageLine.SetSubTool(AValue: string);
begin
if FSubTool=AValue then Exit;
FSubTool:=AValue;
IncreaseChangeStamp;
end;
procedure TMessageLine.SetSubType(AValue: PtrUInt);
begin
if FSubType=AValue then Exit;
FSubType:=AValue;
IncreaseChangeStamp;
end;
procedure TMessageLine.SetTranslatedMsg(AValue: string);
begin
if FTranslatedMsg=AValue then Exit;
FTranslatedMsg:=AValue;
IncreaseChangeStamp;
end;
procedure TMessageLine.SetUrgency(AValue: TMessageLineUrgency);
begin
if FUrgency=AValue then Exit;
if Index>=0 then
dec(Lines.UrgencyCounts[Urgency]);
FUrgency:=AValue;
if Index>=0 then
inc(Lines.UrgencyCounts[Urgency]);
IncreaseChangeStamp;
end;
constructor TMessageLine.Create;
begin
inherited Create;
FIndex:=-1;
FOutputIndex:=-1;
IncreaseChangeStamp;
end;
destructor TMessageLine.Destroy;
begin
FreeAndNil(fAttributes);
inherited Destroy;
end;
procedure TMessageLine.Assign(Source: TMessageLine);
begin
if Source.fAttributes<>nil then begin
if fAttributes=nil then
fAttributes:=TStringList.Create;
fAttributes.Assign(Source.fAttributes);
end else begin
FreeAndNil(fAttributes);
end;
Urgency:=Source.Urgency;
Column:=Source.Column;
Filename:=Source.Filename;
Flags:=Source.Flags;
Line:=Source.Line;
Msg:=Source.Msg;
MsgID:=Source.MsgID;
fOutputIndex:=Source.OutputIndex;
SubTool:=Source.SubTool;
SubType:=Source.SubType;
TranslatedMsg:=Source.TranslatedMsg;
IncreaseChangeStamp;
end;
function TMessageLine.Equals(Obj: TObject): boolean;
var
Source: TMessageLine;
begin
if Obj is TMessageLine then begin
Source:=TMessageLine(Obj);
Result:=false;
if Source.fAttributes<>nil then begin
if fAttributes=nil then exit;
if not fAttributes.Equals(Source.fAttributes) then exit;
end else begin
if (fAttributes<>nil) and (fAttributes.Count>0) then
exit;
end;
Result:=(Urgency=Source.Urgency)
and (Column=Source.Column)
and (Filename=Source.Filename)
and (Flags=Source.Flags)
and (Line=Source.Line)
and (Msg=Source.Msg)
and (MsgID=Source.MsgID)
and (fOutputIndex=Source.OutputIndex)
and (SubTool=Source.SubTool)
and (SubType=Source.SubType)
and (TranslatedMsg=Source.TranslatedMsg);
end else
Result:=inherited Equals(Obj);
end;
procedure TMessageLine.Clear;
begin
SubTool:='';
Msg:='';
TranslatedMsg:='';
Filename:='';
FreeAndNil(fAttributes);
end;
function TMessageLine.GetRelativeFilename: string;
begin
Result:=FFilename;
if (Lines<>nil) and (Lines.BaseDirectory<>'') then
Result:=CreateRelativePath(Result,FLines.BaseDirectory);
end;
procedure TMessageLine.ShareStrings(const ShareStringEvent: TETShareStringEvent
);
var
i: Integer;
s: String;
begin
ShareStringEvent(FFilename);
ShareStringEvent(FMsg);
ShareStringEvent(FTranslatedMsg);
ShareStringEvent(FSubTool);
if fAttributes<>nil then begin
for i:=0 to fAttributes.Count-1 do begin
s:=fAttributes[i];
ShareStringEvent(s);
fAttributes[i]:=s;
end;
end;
end;
procedure TMessageLine.SetSourcePosition(NewFilename: string; NewLine,
NewColumn: integer);
begin
NewFilename:=TrimFilename(NewFilename);
if (FFilename=NewFilename) and (NewLine=Line) and (NewColumn=Column) then exit;
SortedSrcPosUnbind;
FFilename:=NewFilename;
FLine:=NewLine;
FColumn:=NewColumn;
SortedSrcPosBind;
IncreaseChangeStamp;
end;
procedure TMessageLine.IncreaseChangeStamp;
begin
if Lines<>nil then begin
Lines.LineChanged(Self);
FChangeStamp:=Lines.ChangeStamp;
end else
LUIncreaseChangeStamp64(FChangeStamp);
end;
procedure TMessageLine.MarkFixed;
begin
Lines.MarkFixed(Self);
end;
function TMessageLine.HasSourcePosition: boolean;
begin
Result:=(Line>0) and (Column>0) and (GetFullFilename<>'');
end;
procedure TMessageLine.GetAttributes(List: TStrings);
begin
List.Assign(fAttributes);
List.Values['Urgency']:=MessageLineUrgencyNames[Urgency];
List.Values['SubTool']:=SubTool;
List.Values['SubType']:=IntToStr(SubType);
List.Values['File']:=Filename;
List.Values['Line']:=IntToStr(Line);
List.Values['Col']:=IntToStr(Column);
List.Values['Msg']:=Msg;
List.Values['MsgID']:=IntToStr(MsgID);
List.Values['OriginalLine']:=OriginalLine;
end;
{ TExtToolView }
procedure TExtToolView.FetchAllPending;
begin
Lines.FetchAll(PendingLines);
end;
procedure TExtToolView.ToolExited;
begin
end;
procedure TExtToolView.CreateLines;
begin
FLines:=TMessageLines.Create(Self, FMessageLineClass);
FProgressLine:=FMessageLineClass.Create;
FPendingLines:=TMessageLines.Create(Self, FMessageLineClass);
FPendingProgressLine:=FMessageLineClass.Create;
end;
constructor TExtToolView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
if FMessageLineClass=nil then
FMessageLineClass:=TMessageLine;
CreateLines;
FRunning:=true;
FMinUrgency:=DefaultETViewMinUrgency;
FLastWorkerMessageCount:=-1;
end;
destructor TExtToolView.Destroy;
begin
// wait for other threads to finish their access
EnterCriticalSection;
try
RemoveAsyncOnChanged;
ClearLines;
FreeAndNil(FProgressLine);
FreeAndNil(FPendingLines);
FreeAndNil(FPendingProgressLine);
inherited Destroy;
finally
LeaveCriticalSection;
end;
FreeAndNil(FLines);
end;
procedure TExtToolView.ProcessNewMessages(AThread: TThread);
{ Called by TExternalTool.AddOutputLines
Tool is in Critical section
}
var
i: Integer;
SrcMsg: TMessageLine;
NewMsg: TMessageLine;
Changed: Boolean;
NewProgressLine: TMessageLine;
begin
if csDestroying in ComponentState then exit;
Changed:=false;
EnterCriticalSection; // Beware: Tool is already in critical section
try
if (FPendingLines=nil) or (FPendingProgressLine=nil) then exit;
//DebugLn(['TExtToolView.ProcessNewMessages START From=',FirstMsgLine,' To=',Tool.WorkerMessages.Count-1]);
NewProgressLine:=nil;
for i:=FLastWorkerMessageCount+1 to Tool.WorkerMessages.Count-1 do begin
SrcMsg:=Tool.WorkerMessages[i];
//debugln(['TExtToolView.ProcessNewMessages Msg="',SrcMsg.Msg,'" Fits=',LineFits(SrcMsg)]);
if LineFits(SrcMsg) then begin
NewProgressLine:=nil;
end else begin
NewProgressLine:=SrcMsg;
continue;
end;
Changed:=true;
NewMsg:=PendingLines.CreateLine(-1);
NewMsg.Assign(SrcMsg);
//debugln(['TExtToolView.ProcessNewMessages NewMsg=',Lines.Count,'="',NewMsg.Msg,'"']);
PendingLines.Add(NewMsg);
end;
FLastWorkerMessageCount:=Tool.WorkerMessages.Count-1;
if (NewProgressLine<>nil) and Running then begin
Changed:=true;
PendingProgressLine.Assign(NewProgressLine);
end
else if PendingProgressLine.Msg<>'' then begin
Changed:=true;
PendingProgressLine.Msg:='';
end;
//debugln(['TExtToolView.ProcessNewMessages END Changed=',Changed,' Progress="',ProgressLine.Msg,'"']);
finally
LeaveCriticalSection;
end;
if Changed and Assigned(OnChanged) then begin
// wake up main thread
QueueAsyncOnChanged;
end;
end;
procedure TExtToolView.ClearLines;
var
i: Integer;
begin
EnterCriticalSection;
try
FLastWorkerMessageCount:=-1;
if Lines<>nil then
Lines.Clear;
if ProgressLine<>nil then
ProgressLine.Clear;
if PendingLines<>nil then begin
for i:=0 to PendingLines.Count-1 do
TObject(PendingLines[i]).Free;
PendingLines.Clear;
end;
if PendingProgressLine<>nil then
PendingProgressLine.Clear;
finally
LeaveCriticalSection;
end;
end;
function TExtToolView.ApplyPending: boolean;
// returns true if something changed
begin
Result:=false;
if csDestroying in ComponentState then exit;
EnterCriticalSection;
try
if csDestroying in ComponentState then exit;
if PendingLines.Count>0 then begin
FetchAllPending;
Result:=true;
end;
if not ProgressLine.Equals(PendingProgressLine) then begin
ProgressLine.Assign(PendingProgressLine);
Result:=true;
end;
finally
LeaveCriticalSection;
end;
end;
procedure TExtToolView.InputClosed;
begin
if csDestroying in ComponentState then exit;
if not Running then begin
raise Exception.Create('TExtToolView.InputClosed already closed: '+Caption);
end;
FRunning:=false;
EnterCriticalSection;
try
if csDestroying in ComponentState then exit;
if PendingProgressLine.Msg<>'' then
PendingProgressLine.Clear;
finally
LeaveCriticalSection;
end;
if Tool<>nil then
ExitStatus:=Tool.ExitStatus;
ToolExited;
if Assigned(OnChanged) then begin
RemoveAsyncOnChanged;
OnChanged(Self);
end;
end;
function TExtToolView.LineFits(Line: TMessageLine): boolean;
begin
Result:=(Line.Msg<>'') and (Line.Urgency>=MinUrgency);
end;
procedure TExtToolView.EnterCriticalSection;
begin
FLines.EnterCriticalSection;
end;
procedure TExtToolView.LeaveCriticalSection;
begin
FLines.LeaveCriticalSection;
end;
procedure TExtToolView.ConsistencyCheck;
begin
EnterCriticalSection;
try
FLines.ConsistencyCheck;
FPendingLines.ConsistencyCheck;
finally
LeaveCriticalSection;
end;
end;
function TExtToolView.HasFinished: boolean;
begin
Result:=false;
EnterCriticalSection;
try
if Running then exit;
if (Tool<>nil) and (Tool.Stage<>etsStopped) then exit;
if PendingLines.Count>0 then exit;
Result:=true;
finally
LeaveCriticalSection;
end;
end;
end.
{$ELSE EnableNewExtTools}
interface
uses
Classes, SysUtils, LCLType, LazConfigStorage, Forms, Controls, BaseIDEIntf;
{ The xml format version:
When the format changes (new values, changed formats) we can distinguish old
files and are able to convert them.
}
const
ExternalToolOptionsVersion = '2';
type
TIDEExternalToolOptions = class;
{ TIDEScanMessageLine }
TIDEScanMessageLine = class(TPersistent)
private
FCaller: TObject;
FLine: string;
FLineNumber: integer;
FTool: TIDEExternalToolOptions;
FWorkingDirectory: string;
procedure SetLine(const AValue: string);
procedure SetWorkingDirectory(const AValue: string);
protected
procedure SetTool(const AValue: TIDEExternalToolOptions);
procedure SetLineNumber(const NewLineNumber: integer);
procedure LineChanged(const OldValue: string); virtual; abstract;
procedure WorkingDirectoryChanged(const OldValue: string); virtual; abstract;
public
constructor Create(TheCaller: TObject = nil; TheTool: TIDEExternalToolOptions = nil);
property Caller: TObject read FCaller;
property Line: string read FLine write SetLine;
property WorkingDirectory: string read FWorkingDirectory write SetWorkingDirectory;
property LineNumber: integer read FLineNumber;
property Tool: TIDEExternalToolOptions read FTool;
end;
TOnIDEExtToolParseLine = procedure(Sender: TObject;
Line: TIDEScanMessageLine) of object;
{
TIDEExternalToolOptions - the storage object for a single external tool
}
TIDEExternalToolOptions = class(TPersistent)
private
fCmdLineParams: string;
FEnvironmentOverrides: TStringList;
fFilename: string;
FHideMainForm: boolean;
FOnParseLine: TOnIDEExtToolParseLine;
FScanners: TStrings;
FScanOutput: boolean;
fScanOutputForFPCMessages: boolean;
fScanOutputForMakeMessages: boolean;
FShowAllOutput: boolean;
fTitle: string;
fWorkingDirectory: string;
procedure SetScanners(const AValue: TStrings);
procedure SetScanOutput(const AValue: boolean);
procedure SetShowAllOutput(const AValue: boolean);
public
constructor Create;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure Clear; virtual;
function NeedsOutputFilter: boolean;
function Load(Config: TConfigStorage): TModalResult; virtual;
function Save(Config: TConfigStorage): TModalResult; virtual;
function ShortDescription: string;
procedure AssignEnvironmentTo(Strings: TStrings);
procedure ParseLine(Sender: TObject; Line: TIDEScanMessageLine); virtual;
property CmdLineParams: string read fCmdLineParams write fCmdLineParams;
property Filename: string read fFilename write fFilename;
property Title: string read fTitle write fTitle;
property WorkingDirectory: string
read fWorkingDirectory write fWorkingDirectory;
property EnvironmentOverrides: TStringList read FEnvironmentOverrides;
property ScanOutputForFPCMessages: boolean
read fScanOutputForFPCMessages write fScanOutputForFPCMessages;
property ScanOutputForMakeMessages: boolean
read fScanOutputForMakeMessages write fScanOutputForMakeMessages;
property ScanOutput: boolean read FScanOutput write SetScanOutput;
property ShowAllOutput: boolean read FShowAllOutput write SetShowAllOutput;
property OnParseLine: TOnIDEExtToolParseLine read FOnParseLine write FOnParseLine;
property Scanners: TStrings read FScanners write SetScanners;
property HideMainForm: boolean read FHideMainForm write FHideMainForm default true;
end;
type
TRunExternalTool = function (Tool: TIDEExternalToolOptions): TModalResult of object;
var
RunExternalTool: TRunExternalTool = nil;// set by the IDE
implementation
{ TIDEExternalToolOptions }
procedure TIDEExternalToolOptions.SetScanOutput(const AValue: boolean);
begin
if FScanOutput=AValue then exit;
FScanOutput:=AValue;
end;
procedure TIDEExternalToolOptions.SetScanners(const AValue: TStrings);
begin
if FScanners=AValue then exit;
FScanners.Assign(AValue);
end;
procedure TIDEExternalToolOptions.SetShowAllOutput(const AValue: boolean);
begin
if FShowAllOutput=AValue then exit;
FShowAllOutput:=AValue;
end;
procedure TIDEExternalToolOptions.Assign(Source: TPersistent);
var
Src: TIDEExternalToolOptions;
begin
if Source=Self then exit;
if Source is TIDEExternalToolOptions then begin
Src:=TIDEExternalToolOptions(Source);
fTitle:=Src.fTitle;
fFilename:=Src.fFilename;
fCmdLineParams:=Src.fCmdLineParams;
fWorkingDirectory:=Src.fWorkingDirectory;
fScanOutputForFPCMessages:=Src.fScanOutputForFPCMessages;
fScanOutputForMakeMessages:=Src.fScanOutputForMakeMessages;
FScanOutput:=Src.FScanOutput;
FShowAllOutput:=Src.FShowAllOutput;
FScanners.Assign(Src.FScanners);
FHideMainForm:=Src.HideMainForm;
end else
inherited Assign(Source);
end;
constructor TIDEExternalToolOptions.Create;
begin
inherited Create;
FEnvironmentOverrides:=TStringList.Create;
FScanners:=TStringList.Create;
FHideMainForm:=true;
Clear;
end;
destructor TIDEExternalToolOptions.Destroy;
begin
FreeAndNil(FEnvironmentOverrides);
FreeAndNil(FScanners);
inherited Destroy;
end;
procedure TIDEExternalToolOptions.Clear;
begin
fTitle:='';
fFilename:='';
fCmdLineParams:='';
fWorkingDirectory:='';
fScanOutputForFPCMessages:=false;
fScanOutputForMakeMessages:=false;
FScanOutput:=false;
FShowAllOutput:=false;
FHideMainForm:=true;
FEnvironmentOverrides.Clear;
FScanners.Clear;
end;
function TIDEExternalToolOptions.Load(Config: TConfigStorage): TModalResult;
begin
Clear;
fTitle:=Config.GetValue('Title/Value','');
fFilename:=Config.GetValue('Filename/Value','');
fCmdLineParams:=Config.GetValue('CmdLineParams/Value','');
fWorkingDirectory:=Config.GetValue('WorkingDirectory/Value','');
fScanOutputForFPCMessages:=Config.GetValue(
'ScanOutputForFPCMessages/Value',false);
fScanOutputForMakeMessages:=Config.GetValue(
'ScanOutputForMakeMessages/Value',false);
FShowAllOutput:=Config.GetValue('ShowAllOutput/Value',false);
FHideMainForm:=Config.GetValue('HideMainForm/Value',true);
Config.GetValue('EnvironmentOverrides/',FEnvironmentOverrides);
Config.GetValue('Scanners/',FScanners);
Result:=mrOk;
end;
function TIDEExternalToolOptions.Save(Config: TConfigStorage): TModalResult;
begin
Config.SetValue('Format/Version',ExternalToolOptionsVersion);
Config.SetDeleteValue('Title/Value',fTitle,'');
Config.SetDeleteValue('Filename/Value',fFilename,'');
Config.SetDeleteValue('CmdLineParams/Value',fCmdLineParams,'');
Config.SetDeleteValue('WorkingDirectory/Value',fWorkingDirectory,'');
Config.SetDeleteValue(
'ScanOutputForFPCMessages/Value',fScanOutputForFPCMessages,
false);
Config.SetDeleteValue(
'ScanOutputForMakeMessages/Value',fScanOutputForMakeMessages,
false);
Config.SetDeleteValue('ShowAllOutput/Value',FShowAllOutput,false);
Config.SetDeleteValue('HideMainForm/Value',FHideMainForm,true);
Config.SetValue('EnvironmentOverrides/',FEnvironmentOverrides);
Config.SetValue('Scanners/',FScanners);
Result:=mrOk;
end;
function TIDEExternalToolOptions.ShortDescription: string;
begin
Result:=Title;
end;
procedure TIDEExternalToolOptions.AssignEnvironmentTo(Strings: TStrings);
begin
BaseIDEIntf.AssignEnvironmentTo(Strings,EnvironmentOverrides);
end;
procedure TIDEExternalToolOptions.ParseLine(Sender: TObject;
Line: TIDEScanMessageLine);
begin
if Assigned(OnParseLine) then
OnParseLine(Sender,Line);
end;
function TIDEExternalToolOptions.NeedsOutputFilter: boolean;
begin
Result:=ScanOutput
or ScanOutputForFPCMessages or ScanOutputForMakeMessages
or ShowAllOutput
or ((FScanners<>nil) and (FScanners.Count>0));
end;
{ TIDEScanMessageLine }
procedure TIDEScanMessageLine.SetLine(const AValue: string);
var
OldLine: String;
begin
if FLine=AValue then exit;
OldLine:=FLine;
FLine:=AValue;
LineChanged(OldLine);
end;
procedure TIDEScanMessageLine.SetWorkingDirectory(const AValue: string);
var
OldDir: String;
begin
if FWorkingDirectory=AValue then exit;
OldDir:=FWorkingDirectory;
FWorkingDirectory:=AValue;
WorkingDirectoryChanged(OldDir);
end;
procedure TIDEScanMessageLine.SetTool(const AValue: TIDEExternalToolOptions);
begin
FTool:=AValue;
end;
procedure TIDEScanMessageLine.SetLineNumber(const NewLineNumber: integer);
begin
FLineNumber:=NewLineNumber;
end;
constructor TIDEScanMessageLine.Create(TheCaller: TObject;
TheTool: TIDEExternalToolOptions);
begin
FCaller:=TheCaller;
FTool:=TheTool;
end;
end.
{$ENDIF}