lazarus/ide/helpfpcmessages.pas

1098 lines
30 KiB
ObjectPascal

{
/***************************************************************************
helpfpcmessages.pas
-------------------
***************************************************************************/
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
* *
***************************************************************************
Author: Mattias Gaertner
Abstract:
Help items for FPC messages.
}
unit HelpFPCMessages;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, fgl,
// LCL
LCLIntf, Dialogs, Forms, Controls, StdCtrls, ExtCtrls, Graphics,
ButtonPanel, LazHelpHTML,
// LazUtils
LazConfigStorage, LazFileUtils, LazFileCache, LazUTF8, LazLoggerBase,
// CodeTools
FileProcs, CodeToolsFPCMsgs, CodeToolManager, CodeCache, DefineTemplates,
// IdeIntf
BaseIDEIntf, MacroIntf, HelpIntfs, IDEHelpIntf, IDEMsgIntf,
IDEExternToolIntf, LazHelpIntf, IDEDialogs, TextTools,
// IDE
LazarusIDEStrConsts, EnvironmentOpts;
const
lihcFPCMessages = 'Free Pascal Compiler messages';
lihFPCMessagesURL = 'http://wiki.lazarus.freepascal.org/';
lihFPCMessagesInternalURL = 'file://Build_messages#FreePascal_Compiler_messages';
type
{ TMessageHelpAddition }
TMessageHelpAddition = class
public
Name: string;
URL: string;
RegEx: string;
IDs: string; // comma separated
procedure Assign(Source: TMessageHelpAddition);
function IsEqual(Source: TMessageHelpAddition): boolean;
function Fits(ID: integer; Msg: string): boolean;
end;
TBaseMessageHelpAdditions = specialize TFPGObjectList<TMessageHelpAddition>;
{ TMessageHelpAdditions }
TMessageHelpAdditions = class(TBaseMessageHelpAdditions)
public
function FindWithName(Name: string): TMessageHelpAddition;
function IsEqual(Source: TMessageHelpAdditions): boolean;
procedure Clone(Source: TMessageHelpAdditions);
procedure LoadFromConfig(Cfg: TConfigStorage);
procedure SaveToConfig(Cfg: TConfigStorage);
procedure LoadFromFile(Filename: string);
procedure SaveToFile(Filename: string);
end;
{ TFPCMessagesHelpDatabase }
TFPCMessagesHelpDatabase = class(THTMLHelpDatabase)
private
fAdditions: TMessageHelpAdditions;
FAdditionsChangeStep: integer;
FAdditionsFile: string;
FDefaultAdditionsFile: string;
FFoundAddition: TMessageHelpAddition;
FDefaultNode: THelpNode;
FFoundComment: string;
FLastMessage: string;
FLoadedAdditionsFilename: string;
FMsgFile: TFPCMsgFile;
FMsgFileChangeStep: integer;
FMsgFilename: string;
function GetAdditions(Index: integer): TMessageHelpAddition;
procedure SetAdditionsFile(AValue: string);
procedure SetFoundComment(const AValue: string);
procedure SetLastMessage(const AValue: string);
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
function GetNodesForMessage(const AMessage: string; MessageParts: TStrings;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; override;
function ShowHelp(Query: THelpQuery; BaseNode, NewNode: THelpNode;
QueryItem: THelpQueryItem;
var ErrMsg: string): TShowHelpResult; override;
procedure Load(Storage: TConfigStorage); override;
procedure Save(Storage: TConfigStorage); override;
property DefaultNode: THelpNode read FDefaultNode;
property LastMessage: string read FLastMessage write SetLastMessage;
property FoundComment: string read FFoundComment write SetFoundComment;
property FoundAddition: TMessageHelpAddition read FFoundAddition;
// the FPC message file
function GetMsgFile: TFPCMsgFile;
property MsgFile: TFPCMsgFile read FMsgFile;
property MsgFilename: string read FMsgFilename;
property MsgFileChangeStep: integer read FMsgFileChangeStep;
// additional help for messages (they add an URL to the FPC comments)
function AdditionsCount: integer;
property Additions[Index: integer]: TMessageHelpAddition read GetAdditions;
property AdditionsChangeStep: integer read FAdditionsChangeStep;
property DefaultAdditionsFile: string read FDefaultAdditionsFile;
property LoadedAdditionsFilename: string read FLoadedAdditionsFilename;
procedure ClearAdditions;
procedure LoadAdditions;
procedure SaveAdditions;
function GetAdditionsFilename: string;
published
property AdditionsFile: string read FAdditionsFile write SetAdditionsFile;
end;
{ TEditIDEMsgHelpDialog }
TEditIDEMsgHelpDialog = class(TForm)
AddButton: TButton;
AdditionFitsMsgLabel: TLabel;
AdditionsFileEdit: TEdit;
AdditionsFileLabel: TLabel;
ButtonPanel1: TButtonPanel;
CurGroupBox: TGroupBox;
CurMsgGroupBox: TGroupBox;
CurMsgMemo: TMemo;
DeleteButton: TButton;
FPCMsgFileValueLabel: TLabel;
FPCMsgFileLabel: TLabel;
GlobalOptionsGroupBox: TGroupBox;
NameEdit: TEdit;
NameLabel: TLabel;
OnlyFPCMsgIDsLabel: TLabel;
OnlyFPCMsgIDsEdit: TEdit;
OnlyRegExEdit: TEdit;
OnlyRegExLabel: TLabel;
Splitter1: TSplitter;
AllGroupBox: TGroupBox;
TestURLButton: TButton;
URLEdit: TEdit;
URLLabel: TLabel;
AllListBox: TListBox;
procedure AddButtonClick(Sender: TObject);
procedure AllListBoxSelectionChange(Sender: TObject; {%H-}User: boolean);
procedure ButtonPanel1Click(Sender: TObject);
procedure ButtonPanel1OKButtonClick(Sender: TObject);
procedure DeleteButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure NameEditChange(Sender: TObject);
procedure OnlyFPCMsgIDsEditChange(Sender: TObject);
procedure OnlyRegExEditChange(Sender: TObject);
procedure TestURLButtonClick(Sender: TObject);
procedure URLEditChange(Sender: TObject);
private
fDefaultValue: string;
procedure FillAdditionsList;
procedure UpdateCurAddition;
procedure UpdateCurMessage;
procedure UpdateAdditionsFitsMsg;
function IsIDListValid(IDs: string): boolean;
function IsRegexValid(re: string): boolean;
function IsURLValid(URL: string): boolean;
public
Additions: TMessageHelpAdditions;
CurAddition: TMessageHelpAddition;
CurMsg: string;
CurFPCId: integer;
end;
var
FPCMsgHelpDB: TFPCMessagesHelpDatabase = nil;
function ShowMessageHelpEditor: TModalResult;
procedure CreateFPCMessagesHelpDB;
implementation
{$R *.lfm}
function ShowMessageHelpEditor: TModalResult;
var
Editor: TEditIDEMsgHelpDialog;
begin
Editor:=TEditIDEMsgHelpDialog.Create(nil);
try
Result:=Editor.ShowModal;
finally
Editor.Free;
end;
end;
procedure CreateFPCMessagesHelpDB;
var
StartNode: THelpNode;
begin
FPCMessagesHelpDB:=HelpDatabases.CreateHelpDatabase(lihcFPCMessages,
TFPCMessagesHelpDatabase,true);
FPCMsgHelpDB:=FPCMessagesHelpDB as TFPCMessagesHelpDatabase;
FPCMsgHelpDB.DefaultBaseURL:=lihFPCMessagesURL;
// HTML nodes
StartNode:=THelpNode.CreateURLID(FPCMsgHelpDB, lisFreePascalCompilerMessages,
lihFPCMessagesInternalURL,lihcFPCMessages);
FPCMsgHelpDB.TOCNode:=THelpNode.Create(FPCMsgHelpDB,StartNode);// once as TOC
FPCMsgHelpDB.RegisterItemWithNode(StartNode);// and once as normal page
end;
{ TMessageHelpAdditions }
function TMessageHelpAdditions.FindWithName(Name: string): TMessageHelpAddition;
var
i: Integer;
begin
for i:=0 to Count-1 do begin
Result:=Items[i];
if SysUtils.CompareText(Result.Name,Name)=0 then exit;
end;
Result:=nil;
end;
function TMessageHelpAdditions.IsEqual(Source: TMessageHelpAdditions): boolean;
var
i: Integer;
begin
Result:=false;
if Source=nil then exit;
if Source=Self then exit(true);
if Count<>Source.Count then exit;
for i:=0 to Count-1 do
if not Items[i].IsEqual(Source[i]) then exit;
Result:=true;
end;
procedure TMessageHelpAdditions.Clone(Source: TMessageHelpAdditions);
var
i: Integer;
Item: TMessageHelpAddition;
begin
Clear;
for i:=0 to Source.Count-1 do begin
Item:=TMessageHelpAddition.Create;
Item.Assign(Source[i]);
Add(Item);
end;
end;
procedure TMessageHelpAdditions.LoadFromConfig(Cfg: TConfigStorage);
var
Cnt: Integer;
i: Integer;
Item: TMessageHelpAddition;
SubPath: String;
begin
Clear;
Cfg.AppendBasePath('Additions');
try
Cnt:=Cfg.GetValue('Count',0);
for i:=1 to Cnt do begin
Item:=TMessageHelpAddition.Create;
SubPath:='Item'+IntToStr(i)+'/';
Item.Name:=Cfg.GetValue(SubPath+'Name','');
if Item.Name='' then begin
Item.Free;
end else begin
Add(Item);
Item.IDs:=cfg.GetValue(SubPath+'IDs','');
Item.RegEx:=cfg.GetValue(SubPath+'RegEx','');
Item.URL:=cfg.GetValue(SubPath+'URL','');
end;
end;
finally
Cfg.UndoAppendBasePath;
end;
end;
procedure TMessageHelpAdditions.SaveToConfig(Cfg: TConfigStorage);
var
Cnt: Integer;
i: Integer;
Item: TMessageHelpAddition;
SubPath: String;
begin
Cfg.AppendBasePath('Additions');
try
Cnt:=0;
for i:=0 to Count-1 do begin
Item:=Items[i];
if Item.Name='' then continue;
inc(Cnt);
SubPath:='Item'+IntToStr(Cnt)+'/';
Cfg.SetDeleteValue(SubPath+'Name',Item.Name,'');
cfg.SetDeleteValue(SubPath+'IDs',Item.IDs,'');
cfg.SetDeleteValue(SubPath+'RegEx',Item.RegEx,'');
cfg.SetDeleteValue(SubPath+'URL',Item.URL,'');
end;
Cfg.SetDeleteValue('Count',Cnt,0);
finally
Cfg.UndoAppendBasePath;
end;
end;
procedure TMessageHelpAdditions.LoadFromFile(Filename: string);
var
Cfg: TConfigStorage;
begin
try
Cfg:=GetIDEConfigStorage(Filename,true);
try
LoadFromConfig(Cfg);
finally
Cfg.Free;
end;
except
on E: Exception do begin
debugln(['TMessageHelpAdditions.LoadFromFile unable to load file "'+Filename+'": '+E.Message]);
end;
end;
end;
procedure TMessageHelpAdditions.SaveToFile(Filename: string);
var
Cfg: TConfigStorage;
begin
try
Cfg:=GetIDEConfigStorage(Filename,false);
try
SaveToConfig(Cfg);
Cfg.WriteToDisk;
finally
Cfg.Free;
end;
except
on E: Exception do begin
debugln(['TMessageHelpAdditions.SaveToFile unable to save file "'+Filename+'": '+E.Message]);
end;
end;
end;
{ TMessageHelpAddition }
procedure TMessageHelpAddition.Assign(Source: TMessageHelpAddition);
begin
Name:=Source.Name;
IDs:=Source.IDs;
RegEx:=Source.RegEx;
URL:=Source.URL;
end;
function TMessageHelpAddition.IsEqual(Source: TMessageHelpAddition): boolean;
begin
Result:=(Name=Source.Name)
and (IDs=Source.IDs)
and (RegEx=Source.RegEx)
and (URL=Source.URL);
end;
function TMessageHelpAddition.Fits(ID: integer; Msg: string): boolean;
var
CurID: Integer;
p: PChar;
begin
Result:=false;
if Msg='' then exit;
if RegEx<>'' then begin
try
Result:=REMatches(Msg,RegEx,'I');
except
end;
if not Result then exit;
end;
if IDs<>'' then begin
Result:=false;
p:=PChar(IDs);
CurID:=0;
while p^<>#0 do begin
case p^ of
',':
if (CurID>0) and (CurID=ID) then begin
Result:=true;
break;
end;
'0'..'9':
begin
CurID:=CurID*10+ord(p^)-ord('0');
if CurID>100000 then exit;
end;
else exit;
end;
inc(p);
end;
if (CurID>0) and (CurID=ID) then Result:=true;
if not Result then exit;
end;
end;
{ TEditIDEMsgHelpDialog }
procedure TEditIDEMsgHelpDialog.FormCreate(Sender: TObject);
var
s: String;
begin
fDefaultValue:=lisDefaultPlaceholder;
Caption:=lisEditAdditionalHelpForMessages;
GlobalOptionsGroupBox.Caption:=lisGlobalSettings;
FPCMsgFileLabel.Caption:=lisFPCMessageFile2;
AdditionsFileLabel.Caption:=lisConfigFileOfAdditions;
CurMsgGroupBox.Caption:=lisSelectedMessageInMessagesWindow;
AllGroupBox.Caption:=lisAdditions;
AddButton.Caption:=lisCreateNewAddition;
NameLabel.Caption:=lisCodeToolsDefsName;
OnlyFPCMsgIDsLabel.Caption:=lisOnlyMessagesWithTheseFPCIDsCommaSeparated;
OnlyRegExLabel.Caption:=lisOnlyMessagesFittingThisRegularExpression;
s:=(FPCMessagesHelpDB as THTMLHelpDatabase).GetEffectiveBaseURL;
URLLabel.Caption:=Format(lisURLOnWikiTheBaseUrlIs, [s]);
TestURLButton.Caption:=lisTestURL;
DeleteButton.Caption:=lisDeleteThisAddition;
ButtonPanel1.OKButton.OnClick:=@ButtonPanel1OKButtonClick;
// global options
FPCMsgFileValueLabel.Caption:=EnvironmentOptions.GetParsedCompilerMessagesFilename;
AdditionsFileEdit.Text:=FPCMsgHelpDB.AdditionsFile;
// fetch selected message
UpdateCurMessage;
// list of additions
FPCMsgHelpDB.LoadAdditions;
Additions:=TMessageHelpAdditions.Create;
Additions.Clone(FPCMsgHelpDB.fAdditions);
FillAdditionsList;
// current addition
if AllListBox.Items.Count>0 then
AllListBox.ItemIndex:=0;
UpdateCurAddition;
end;
procedure TEditIDEMsgHelpDialog.ButtonPanel1Click(Sender: TObject);
begin
end;
procedure TEditIDEMsgHelpDialog.AllListBoxSelectionChange(Sender: TObject;
User: boolean);
begin
UpdateCurAddition;
end;
procedure TEditIDEMsgHelpDialog.AddButtonClick(Sender: TObject);
var
i: Integer;
Prefix: String;
NewName: String;
Item: TMessageHelpAddition;
begin
if CurFPCId>=0 then
Prefix:='Msg'+IntToStr(CurFPCId)+'_'
else
Prefix:='Msg';
i:=1;
repeat
NewName:=Prefix+IntToStr(i);
if Additions.FindWithName(NewName)=nil then break;
inc(i);
until false;
Item:=TMessageHelpAddition.Create;
Item.Name:=NewName;
if CurFPCId>=0 then
Item.IDs:=IntToStr(CurFPCId);
Additions.Add(Item);
FillAdditionsList;
AllListBox.ItemIndex:=AllListBox.Items.IndexOf(Item.Name);
UpdateCurAddition;
end;
procedure TEditIDEMsgHelpDialog.ButtonPanel1OKButtonClick(Sender: TObject);
var
Filename: TCaption;
HasChanged: Boolean;
begin
HasChanged:=false;
Filename:=EnvironmentOptions.GetParsedCompilerMessagesFilename;
if (Filename=fDefaultValue) then
Filename:='';
Filename:=AdditionsFileEdit.Text;
if (Filename=fDefaultValue)
or (Filename=FPCMsgHelpDB.FDefaultAdditionsFile) then
Filename:='';
if FPCMsgHelpDB.AdditionsFile<>Filename then begin
FPCMsgHelpDB.AdditionsFile:=Filename;
HasChanged:=true;
end;
if HasChanged then begin
// ToDo: save changes
ShowMessage('Saving global options is not yet supported');
end;
if not Additions.IsEqual(FPCMsgHelpDB.fAdditions) then
begin
FPCMsgHelpDB.fAdditions.Clone(Additions);
FPCMsgHelpDB.SaveAdditions;
end;
end;
procedure TEditIDEMsgHelpDialog.DeleteButtonClick(Sender: TObject);
var
i: LongInt;
NewIndex: Integer;
begin
if CurAddition=nil then exit;
if IDEMessageDialog(lisDelete2,
Format(lisDeleteAddition, [CurAddition.Name]), mtConfirmation, [mbYes, mbNo]
)<>mrYes
then exit;
NewIndex:=AllListBox.ItemIndex;
i:=Additions.IndexOf(CurAddition);
CurAddition:=nil;
if i>=0 then
Additions.Delete(i);
FillAdditionsList;
if NewIndex<0 then NewIndex:=0;
if NewIndex>=AllListBox.Items.Count then dec(NewIndex);
AllListBox.ItemIndex:=NewIndex;
UpdateCurAddition;
end;
procedure TEditIDEMsgHelpDialog.FormDestroy(Sender: TObject);
begin
FreeAndNil(Additions);
end;
procedure TEditIDEMsgHelpDialog.NameEditChange(Sender: TObject);
var
NewName: TCaption;
ConflictAddition: TMessageHelpAddition;
begin
NewName:=NameEdit.Text;
ConflictAddition:=Additions.FindWithName(NewName);
if (NewName='') or (CurAddition=nil)
or ((ConflictAddition<>nil) and (Additions.FindWithName(NewName)<>CurAddition))
then begin
// invalid name
NameLabel.Font.Color:=clRed;
end else begin
NameLabel.Font.Color:=clDefault;
CurAddition.Name:=NewName;
AllListBox.Items[AllListBox.ItemIndex]:=NewName;
end;
end;
procedure TEditIDEMsgHelpDialog.OnlyFPCMsgIDsEditChange(Sender: TObject);
var
NewIDs: TCaption;
begin
NewIDs:=OnlyFPCMsgIDsEdit.Text;
if (CurAddition=nil) or (not IsIDListValid(NewIDs)) then begin
OnlyFPCMsgIDsLabel.Font.Color:=clRed;
end else begin
OnlyFPCMsgIDsLabel.Font.Color:=clDefault;
CurAddition.IDs:=NewIDs;
UpdateAdditionsFitsMsg;
end;
end;
procedure TEditIDEMsgHelpDialog.OnlyRegExEditChange(Sender: TObject);
var
NewRE: TCaption;
begin
NewRE:=OnlyRegExEdit.Text;
if (CurAddition=nil) or (not IsRegexValid(NewRE)) then begin
OnlyRegExLabel.Font.Color:=clRed;
end else begin
OnlyRegExLabel.Font.Color:=clDefault;
CurAddition.RegEx:=NewRE;
UpdateAdditionsFitsMsg;
end;
end;
procedure TEditIDEMsgHelpDialog.TestURLButtonClick(Sender: TObject);
var
URL: String;
begin
if (CurAddition=nil) or (CurAddition.URL='') then exit;
URL:=FPCMsgHelpDB.GetEffectiveBaseURL+CurAddition.URL;
OpenURL(URL);
end;
procedure TEditIDEMsgHelpDialog.URLEditChange(Sender: TObject);
var
NewURL: TCaption;
begin
NewURL:=URLEdit.Text;
if (CurAddition=nil) or (not IsURLValid(NewURL)) then begin
URLLabel.Font.Color:=clRed;
end else begin
URLLabel.Font.Color:=clDefault;
CurAddition.URL:=NewURL;
end;
end;
procedure TEditIDEMsgHelpDialog.FillAdditionsList;
var
sl: TStringListUTF8Fast;
i: Integer;
begin
sl:=TStringListUTF8Fast.Create;
try
for i:=0 to Additions.Count-1 do
sl.Add(Additions[i].Name);
sl.Sort;
AllListBox.Items.Assign(sl);
finally
sl.Free;
end;
end;
procedure TEditIDEMsgHelpDialog.UpdateCurAddition;
var
i: Integer;
begin
i:=AllListBox.ItemIndex;
if i>=0 then
CurAddition:=Additions.FindWithName(AllListBox.Items[i])
else
CurAddition:=nil;
if CurAddition=nil then begin
CurGroupBox.Caption:=lisNoneSelected;
CurGroupBox.Enabled:=false;
NameEdit.Text:='';
OnlyFPCMsgIDsEdit.Text:='';
OnlyRegExEdit.Text:='';
URLEdit.Text:='';
for i:=0 to CurGroupBox.ControlCount-1 do
CurGroupBox.Controls[i].Enabled:=false;
NameLabel.Font.Color:=clDefault;
OnlyFPCMsgIDsEdit.Font.Color:=clDefault;
OnlyRegExEdit.Font.Color:=clDefault;
URLEdit.Font.Color:=clDefault;
end else begin
CurGroupBox.Caption:=lisSelectedAddition;
CurGroupBox.Enabled:=true;
NameEdit.Text:=CurAddition.Name;
NameLabel.Font.Color:=clDefault;
OnlyFPCMsgIDsEdit.Text:=CurAddition.IDs;
if not IsIDListValid(CurAddition.IDs) then
OnlyFPCMsgIDsLabel.Font.Color:=clRed
else
OnlyFPCMsgIDsLabel.Font.Color:=clDefault;
OnlyRegExEdit.Text:=CurAddition.RegEx;
if not IsRegexValid(CurAddition.RegEx) then
OnlyRegExLabel.Font.Color:=clRed
else
OnlyRegExLabel.Font.Color:=clDefault;
URLEdit.Text:=CurAddition.URL;
if not IsURLValid(CurAddition.URL) then
URLLabel.Font.Color:=clRed
else
URLLabel.Font.Color:=clDefault;
for i:=0 to CurGroupBox.ControlCount-1 do
CurGroupBox.Controls[i].Enabled:=true;
end;
UpdateAdditionsFitsMsg;
end;
procedure TEditIDEMsgHelpDialog.UpdateCurMessage;
var
Line: TMessageLine;
sl: TStringList;
MsgFile: TFPCMsgFile;
FPCMsg: TFPCMsgItem;
begin
CurMsg:='';
CurFPCId:=-1;
Line:=IDEMessagesWindow.GetSelectedLine;
if Line=nil then begin
CurMsgMemo.Text:=lisNoMessageSelected;
CurMsgMemo.Enabled:=false;
end else begin
CurMsg:=Line.Msg;
sl:=TStringList.Create;
try
sl.Add('Msg='+Line.Msg);
sl.Add('MsgID='+IntToStr(Line.MsgID));
MsgFile:=FPCMsgHelpDB.GetMsgFile;
if MsgFile<>nil then begin
FPCMsg:=nil;
if Line.MsgID>0 then
FPCMsg:=MsgFile.FindWithID(Line.MsgID);
if FPCMsg=nil then
FPCMsg:=MsgFile.FindWithMessage(Line.Msg);
if FPCMsg<>nil then begin
CurFPCId:=FPCMsg.ID;
sl.Add('FPC Msg='+FPCMsg.GetName);
end;
end;
CurMsgMemo.Text:=sl.Text;
finally
sl.Free;
end;
CurMsgMemo.Enabled:=true;
end;
end;
procedure TEditIDEMsgHelpDialog.UpdateAdditionsFitsMsg;
begin
if (CurAddition=nil) or (CurMsg='') then
AdditionFitsMsgLabel.Visible:=false
else begin
AdditionFitsMsgLabel.Visible:=true;
if CurAddition.Fits(CurFPCId,CurMsg) then begin
AdditionFitsMsgLabel.Caption:=lisAdditionFitsTheCurrentMessage;
end else begin
AdditionFitsMsgLabel.Caption:=lisAdditionDoesNotFitTheCurrentMessage;
end;
end;
end;
function TEditIDEMsgHelpDialog.IsIDListValid(IDs: string): boolean;
// comma separated decimal numbers
var
p: PChar;
id: Integer;
begin
if IDs='' then exit(true);
Result:=false;
p:=PChar(IDs);
id:=0;
while p^<>#0 do begin
case p^ of
',': id:=0;
'0'..'9':
begin
id:=id*10+ord(p^)-ord('0');
if id>100000 then begin
debugln(['TEditIDEMsgHelpDialog.IsIDListValid id too big ',id]);
exit;
end;
end;
else
debugln(['TEditIDEMsgHelpDialog.IsIDListValid invalid character ',ord(p^),'=',dbgstr(p[0])]);
exit;
end;
inc(p);
end;
Result:=true;
end;
function TEditIDEMsgHelpDialog.IsRegexValid(re: string): boolean;
begin
if re='' then exit(true);
Result:=false;
try
REMatches('',re,'I');
Result:=true;
except
on E: Exception do begin
debugln(['TEditIDEMsgHelpDialog.IsRegexValid inalid Re="',re,'": ',E.Message]);
end;
end;
end;
function TEditIDEMsgHelpDialog.IsURLValid(URL: string): boolean;
var
i: Integer;
begin
Result:=false;
if URL='' then exit;
for i:=1 to length(URL) do begin
if URL[i] in [#0..#32] then exit;
end;
Result:=true;
end;
{ TFPCMessagesHelpDatabase }
procedure TFPCMessagesHelpDatabase.SetFoundComment(const AValue: string);
begin
if FFoundComment=AValue then exit;
FFoundComment:=AValue;
end;
function TFPCMessagesHelpDatabase.GetAdditions(Index: integer
): TMessageHelpAddition;
begin
Result:=fAdditions[Index];
end;
procedure TFPCMessagesHelpDatabase.SetAdditionsFile(AValue: string);
begin
if FAdditionsFile=AValue then Exit;
FAdditionsFile:=AValue;
FAdditionsChangeStep:=CTInvalidChangeStamp;
FLoadedAdditionsFilename:='';
end;
procedure TFPCMessagesHelpDatabase.SetLastMessage(const AValue: string);
begin
if FLastMessage=AValue then exit;
FLastMessage:=AValue;
end;
constructor TFPCMessagesHelpDatabase.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
FDefaultAdditionsFile:='$(LazarusDir)/docs/additionalmsghelp.xml';
fAdditions:=TMessageHelpAdditions.Create;
FAdditionsChangeStep:=CTInvalidChangeStamp;
FMsgFileChangeStep:=CTInvalidChangeStamp;
FDefaultNode:=THelpNode.CreateURL(Self, lisFPCMessagesAppendix,
'http://lazarus-ccr.sourceforge.net/fpcdoc/user/userap3.html#x81-168000C');
end;
destructor TFPCMessagesHelpDatabase.Destroy;
begin
FreeAndNil(fAdditions);
FreeAndNil(FDefaultNode);
FreeAndNil(FMsgFile);
inherited Destroy;
end;
function TFPCMessagesHelpDatabase.GetNodesForMessage(const AMessage: string;
MessageParts: TStrings; var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult;
var
MsgItem: TFPCMsgItem;
i: Integer;
FPCID: Integer;
MsgId: Integer;
begin
FFoundAddition:=nil;
FFoundComment:='';
FPCID:=-1;
Result:=inherited GetNodesForMessage(AMessage, MessageParts, ListOfNodes,
ErrMsg);
if (ListOfNodes<>nil) and (ListOfNodes.Count>0) then exit;
LastMessage:=AMessage;
// search message in FPC message file
GetMsgFile;
MsgItem:=nil;
if MsgFile<>nil then begin
MsgId:=StrToIntDef(MessageParts.Values['MsgId'],0);
if MsgId>0 then
MsgItem:=MsgFile.FindWithID(MsgId);
if MsgItem=nil then
MsgItem:=MsgFile.FindWithMessage(AMessage);
if MsgItem<>nil then begin
FoundComment:=MsgItem.GetTrimmedComment(true,true);
FPCID:=MsgItem.ID;
end;
end;
// search message in additions
LoadAdditions;
FFoundAddition:=nil;
for i:=0 to AdditionsCount-1 do begin
if Additions[i].Fits(FPCID,AMessage) then begin
FFoundAddition:=Additions[i];
break;
end;
end;
if (FoundComment<>'') or (FoundAddition<>nil) then begin
Result:=shrSuccess;
CreateNodeQueryListAndAdd(DefaultNode,nil,ListOfNodes,true);
//DebugLn('TFPCMessagesHelpDatabase.GetNodesForMessage ',FoundComment);
end;
end;
function TFPCMessagesHelpDatabase.ShowHelp(Query: THelpQuery; BaseNode,
NewNode: THelpNode; QueryItem: THelpQueryItem; var ErrMsg: string
): TShowHelpResult;
var
URL: String;
begin
Result:=shrHelpNotFound;
if NewNode<>DefaultNode then begin
Result:=inherited ShowHelp(Query, BaseNode, NewNode, QueryItem, ErrMsg);
end else begin
URL:='';
if (FoundAddition<>nil) and (FoundAddition.URL<>'') then
URL:=GetEffectiveBaseURL+FoundAddition.URL;
if FoundComment<>'' then begin
if URL='' then begin
IDEMessageDialog(lisHFMHelpForFreePascalCompilerMessage, FoundComment,
mtInformation,[mbOk]);
end else begin
if IDEQuestionDialog(lisHFMHelpForFreePascalCompilerMessage,
Format(lisThereAreAdditionalNotesForThisMessageOn,
[FoundComment+LineEnding+LineEnding, LineEnding+URL]),
mtInformation, [mrYes, lisOpenURL,
mrClose, lisClose]) = mrYes
then begin
if not OpenURL(URL) then
exit(shrViewerError);
end;
end;
end else if URL<>'' then begin
if not OpenURL(URL) then
exit(shrViewerError);
end;
Result:=shrSuccess;
end;
end;
procedure TFPCMessagesHelpDatabase.Load(Storage: TConfigStorage);
begin
inherited Load(Storage);
AdditionsFile:=Storage.GetValue('Additions/Filename','');
end;
procedure TFPCMessagesHelpDatabase.Save(Storage: TConfigStorage);
begin
inherited Save(Storage);
Storage.SetDeleteValue('Additions/Filename',AdditionsFile,'');
end;
function TFPCMessagesHelpDatabase.GetMsgFile: TFPCMsgFile;
var
Filename: String;
FPCSrcDir: String;
Code: TCodeBuffer;
AltFilename: String;
UnitSet: TFPCUnitSetCache;
CfgCache: TPCTargetConfigCache;
begin
Result:=nil;
Filename:=EnvironmentOptions.GetParsedCompilerMessagesFilename;
if Filename='' then
FileName:='errore.msg';
if not FilenameIsAbsolute(Filename) then
begin
// try in FPC sources and Compiler directory
UnitSet:=CodeToolBoss.GetUnitSetForDirectory('');
if UnitSet=nil then exit;
// try in FPC sources
FPCSrcDir:=UnitSet.FPCSourceDirectory;
if (FPCSrcDir<>'') then begin
AltFilename:=TrimFilename(AppendPathDelim(FPCSrcDir)
+GetForcedPathDelims('compiler/msg/')+Filename);
if FileExistsCached(AltFilename) then
Filename:=AltFilename;
end;
if not FilenameIsAbsolute(Filename) then
begin
// try in compiler path
CfgCache:=UnitSet.GetConfigCache(true);
if CfgCache<>nil then begin
// try in back end compiler path
if FilenameIsAbsolute(CfgCache.RealCompiler) then
begin
AltFilename:=AppendPathDelim(ExtractFilePath(CfgCache.RealCompiler))
+'msg'+PathDelim+Filename;
if FileExistsCached(AltFilename) then
Filename:=AltFilename;
end;
// try in front end compiler path
if (not FilenameIsAbsolute(Filename))
and FilenameIsAbsolute(CfgCache.Compiler) then
begin
AltFilename:=AppendPathDelim(ExtractFilePath(CfgCache.Compiler))
+'msg'+PathDelim+Filename;
if FileExistsCached(AltFilename) then
Filename:=AltFilename;
end;
end;
end;
if not FilenameIsAbsolute(Filename) then exit;
end;
Code:=CodeToolBoss.LoadFile(Filename,true,false);
if Code=nil then exit;
// load MsgFile
if (Filename<>MsgFilename) or (Code.ChangeStep<>MsgFileChangeStep) then begin
fMsgFilename:=Filename;
if FMsgFile=nil then
FMsgFile:=TFPCMsgFile.Create;
FMsgFileChangeStep:=Code.ChangeStep;
try
MsgFile.LoadFromText(Code.Source);
except
on E: Exception do begin
debugln(['TFPCMessagesHelpDatabase failed to parse "'+MsgFilename+'": '+E.Message]);
exit;
end;
end;
end;
Result:=MsgFile;
end;
function TFPCMessagesHelpDatabase.AdditionsCount: integer;
begin
Result:=fAdditions.Count;
end;
procedure TFPCMessagesHelpDatabase.ClearAdditions;
begin
fAdditions.Clear;
FLoadedAdditionsFilename:='';
FAdditionsChangeStep:=CTInvalidChangeStamp;
FFoundAddition:=nil;
end;
procedure TFPCMessagesHelpDatabase.LoadAdditions;
var
Filename: String;
Code: TCodeBuffer;
begin
Filename:=GetAdditionsFilename;
if FLoadedAdditionsFilename<>Filename then
FAdditionsChangeStep:=CTInvalidChangeStamp;
Code:=CodeToolBoss.LoadFile(Filename,true,false);
if Code<>nil then begin
if Code.ChangeStep=AdditionsChangeStep then exit;
fAdditionsChangeStep:=Code.ChangeStep;
end else
fAdditionsChangeStep:=CTInvalidChangeStamp;
ClearAdditions;
fAdditions.LoadFromFile(Filename);
FLoadedAdditionsFilename:=Filename;
end;
procedure TFPCMessagesHelpDatabase.SaveAdditions;
var
Code: TCodeBuffer;
Filename: String;
begin
Filename:=GetAdditionsFilename;
fAdditions.SaveToFile(Filename);
Code:=CodeToolBoss.LoadFile(Filename,true,false);
if Code<>nil then
fAdditionsChangeStep:=Code.ChangeStep;
FLoadedAdditionsFilename:=Filename;
end;
function TFPCMessagesHelpDatabase.GetAdditionsFilename: string;
var
LazDir: String;
begin
Result:=AdditionsFile;
IDEMacros.SubstituteMacros(Result);
if Result='' then begin
Result:=GetForcedPathDelims(FDefaultAdditionsFile);
IDEMacros.SubstituteMacros(Result);
end;
Result:=TrimFilename(Result);
if not FilenameIsAbsolute(Result) then begin
LazDir:=EnvironmentOptions.GetParsedLazarusDirectory;
Result:=TrimFilename(AppendPathDelim(LazDir)+Result);
end;
end;
end.