lazarus/components/chmhelp/packages/idehelp/chmlangref.pas
2023-07-07 17:30:03 +02:00

242 lines
7.5 KiB
ObjectPascal

unit ChmLangRef;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, chmreader, chmsitemap,
Dialogs, LazHelpIntf, HelpIntfs,
FileUtil, LazFileUtils, LazStringUtils, LazUTF8,
IDEHelpIntf, MacroIntf;
const
sFPCLangRef = 'FPC Language Reference';
type
{ TLangRefHelpDatabase }
TLangRefHelpDatabase = class(THelpDatabase)
private
FCHMSearchPath: string;
FKeywordNodes: TList;
FKeyWordsList: TStringListUTF8Fast;
FRTLIndex: TStringList;
procedure ClearKeywordNodes;
procedure LoadChmIndex(const Path, ChmFileName: string;
IndexStrings: TStrings; const Filter: string = '');
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
procedure LoadKeywordList(const Path: string);
function GetNodesForKeyword(const HelpKeyword: string;
var ListOfNodes: THelpNodeQueryList; var ErrMsg: string
): TShowHelpResult; override;
function ShowHelp(Query: THelpQuery; {%H-}BaseNode, NewNode: THelpNode;
{%H-}QueryItem: THelpQueryItem;
var ErrMsg: string): TShowHelpResult; override;
property CHMSearchPath: string read FCHMSearchPath write FCHMSearchPath;
end;
procedure RegisterLangRefHelpDatabase;
var
LangRefHelpDatabase: TLangRefHelpDatabase = nil;
implementation
procedure RegisterLangRefHelpDatabase;
begin
if not Assigned(LangRefHelpDatabase) then
LangRefHelpDatabase := TLangRefHelpDatabase(HelpDatabases.CreateHelpDatabase(sFPCLangRef,
TLangRefHelpDatabase, true));
end;
{ TLangRefHelpDatabase }
procedure TLangRefHelpDatabase.ClearKeywordNodes;
var
i: Integer;
begin
for i := 0 to FKeywordNodes.Count - 1 do
TObject(FKeywordNodes[i]).Free;
FKeywordNodes.Clear;
end;
constructor TLangRefHelpDatabase.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
FKeywordNodes := TList.Create;
FKeyWordsList := TStringListUTF8Fast.Create;
FKeyWordsList.CaseSensitive := False;
FRTLIndex := TStringList.Create;
FRTLIndex.CaseSensitive := False;
end;
destructor TLangRefHelpDatabase.Destroy;
begin
ClearKeywordNodes;
FKeywordNodes.Free;
FKeyWordsList.Free;
FRTLIndex.Free;
inherited Destroy;
end;
procedure TLangRefHelpDatabase.LoadKeywordList(const Path: string);
begin
FRTLIndex.Clear; // Path has been changed
LoadChmIndex(Path, 'ref.chm', FKeyWordsList);
end;
procedure TLangRefHelpDatabase.LoadChmIndex(const Path, ChmFileName: string;
IndexStrings: TStrings; const Filter: string = '');
var
chm: TChmFileList;
fchm: TChmReader;
SM: TChmSiteMap;
X, Y: Integer;
s: string;
Filename: String;
SMItem: TChmSiteMapItem;
begin
fCHMSearchPath := Path;
if fCHMSearchPath = '' then
begin
fCHMSearchPath := '$(LazarusDir)/docs/chm;$(LazarusDir)/docs/html';
IDEMacros.SubstituteMacros(fCHMSearchPath);
fCHMSearchPath := MinimizeSearchPath(GetForcedPathDelims(fCHMSearchPath));
end;
Filename:=SearchFileInPath(ChmFileName,'',fCHMSearchPath,';',[]);
IndexStrings.Clear;
if (Filename<>'') then
begin
chm := TChmFileList.Create(Utf8ToSys(Filename));
try
if chm.Count = 0 then Exit;
fchm := chm.Chm[0];
SM := fChm.GetIndexSitemap;
if SM <> nil then
begin
for X := 0 to SM.Items.Count - 1 do
begin
SMItem:=SM.Items.Item[X];
s := SMItem.Name;
if SMItem.Children.Count = 0 then
begin
if (SMItem.Local<>'')
and ((Filter = '') or (Pos(Filter, SMItem.Local) > 0)) then
IndexStrings.Add(s + '=' + SMItem.Local)
end else begin
with SMItem.Children do
for Y := 0 to Count - 1 do
begin
if (Item[Y].Local<>'')
and ((Filter = '') or (Pos(Filter, Item[Y].Local) > 0)) then
IndexStrings.Add(s + '=' + Item[Y].Local)
end;
end;
for Y:=0 to SMItem.SubItemcount-1 do begin
if (SMItem.SubItem[Y].Local<>'')
and ((Filter = '') or (Pos(Filter, SMItem.SubItem[Y].Local) > 0)) then
IndexStrings.Add(s + '=' + SMItem.SubItem[Y].Local)
end;
end;
SM.Free;
end;
fchm.Free;
finally
chm.Free;
end;
end;
end;
function TLangRefHelpDatabase.GetNodesForKeyword(const HelpKeyword: string;
var ListOfNodes: THelpNodeQueryList; var ErrMsg: string): TShowHelpResult;
var
KeyWord, s: String;
i, n: Integer;
KeywordNode: THelpNode;
begin
Result := shrHelpNotFound;
if (csDesigning in ComponentState) then Exit;
if (FPCKeyWordHelpPrefix<>'')
and (LeftStr(HelpKeyword,length(FPCKeyWordHelpPrefix))=FPCKeyWordHelpPrefix) then
begin
if FKeyWordsList.Count = 0 then LoadKeywordList(fCHMSearchPath);
if FKeyWordsList.Count = 0 then
begin
Result := shrDatabaseNotFound;
ErrMsg := Format('ref.chm not found. Please put ref.chm help file in '+ LineEnding
+ '%s' + LineEnding
+'or set the path to it with "HelpFilesPath" in '
+' Environment Options -> Help -> Help Options ->' + LineEnding
+'under Viewers - CHM Help Viewer', [fCHMSearchPath]);
Exit;
end;
// HelpKeyword starts with KeywordPrefix
KeyWord := Copy(HelpKeyword, Length(FPCKeyWordHelpPrefix) + 1, Length(HelpKeyword));
ClearKeywordNodes;
n := 0;
for i := 0 to FKeyWordsList.Count - 1 do
begin
if SameText(FKeyWordsList.Names[i], KeyWord) then
begin
Inc(n);
KeywordNode := THelpNode.CreateURL(Self,KeyWord,'ref.chm://' + FKeyWordsList.ValueFromIndex[i]);
KeywordNode.Title := Format('Pascal keyword "%s"', [KeyWord]);
if n > 1 then
KeywordNode.Title := KeywordNode.Title + ' (' + IntToStr(n) + ')';
FKeywordNodes.Add(KeywordNode);
CreateNodeQueryListAndAdd(KeywordNode,nil,ListOfNodes,true);
Result := shrSuccess;
end;
end;
if (Result = shrSuccess) and (SameText(KeyWord, 'for') or SameText(KeyWord, 'in')) then
begin { for => +forin, in => +forin }
i := FKeyWordsList.IndexOfName('forin');
if i < 0 then Exit;
KeywordNode := THelpNode.CreateURL(Self,KeyWord,'ref.chm://' + FKeyWordsList.ValueFromIndex[i]);
KeywordNode.Title := Format('Pascal keyword "%s"', ['for..in']);
FKeywordNodes.Add(KeywordNode);
CreateNodeQueryListAndAdd(KeywordNode, nil, ListOfNodes, True);
end;
if Result <> shrSuccess then
begin
{ it can be predefined procedure/function from RTL }
if FRTLIndex.Count = 0 then
LoadChmIndex(FCHMSearchPath, 'rtl.chm', FRTLIndex, 'system/');
for i := 0 to FRTLIndex.Count - 1 do
begin
s := FRTLIndex.Names[i];
if LazStartsText(KeyWord, s) and
((Length(s) = Length(KeyWord)) or (s[Length(KeyWord) + 1] = ' ')) then
begin
KeywordNode := THelpNode.CreateURL(Self,KeyWord,'rtl.chm://' + FRTLIndex.ValueFromIndex[i]);
KeywordNode.Title := Format('RTL - Free Pascal Run Time Library: "%s"', [KeyWord]);
FKeywordNodes.Add(KeywordNode);
CreateNodeQueryListAndAdd(KeywordNode, nil, ListOfNodes, True);
Exit(shrSuccess); // only first match
end;
end;
end;
end;
end;
function TLangRefHelpDatabase.ShowHelp(Query: THelpQuery; BaseNode,
NewNode: THelpNode; QueryItem: THelpQueryItem; var ErrMsg: string
): TShowHelpResult;
var
Viewer: THelpViewer;
begin
Result:=shrHelpNotFound;
if not (Query is THelpQueryKeyword) then exit;
Result := FindViewer('text/html', ErrMsg, Viewer);
if Result <> shrSuccess then Exit;
Result := Viewer.ShowNode(NewNode, ErrMsg);
end;
end.