function hyperHelp = makehelphyper(actionName, pathname, fcnName, helpStr)
%MAKEHELPHYPER Reformat help output so that the content has hyperlinks
%
% ******************************************************************************
% Modifications to add hyperlinks allowing you to open files for editing
% directly from help and directory Contents listings.
%
% John Iversen (john_iversen@post.harvard.edu)
%
% 9/23/05 JRI initial version.
% 9/1/06 JRI new installation instructions; fix bug when
% encountering a directory containing a file of the
% same name. (Thanks to John D'Errico). Change help.m to allow
% linking to class methods.
%
% 1) Added an 'Edit this file' link at the end of every help listing
% for individual functions. Clicking opens the file in the editor.
% 2) Contents pages have an edit hyperlink by each file name.
% 3) Links are provided to edit Contents pages themselves--handy for
% keeping them up to date.
% 4) In case you have a directory containing an m-file of the same name
% add links to both the m-file and directory's Contents file.
% 5) It handles overloaded class methods properly, but only after fixing a
% help.m, which does not provide the class name properly.
%
%
% INSTALLATION
%
% The easy way: place this file, and the accompanying help.m file anywhere in
% your working directory tree that is in front of default matlab directories
% on your search path. These new versions will then be found before the stock
% ones. (I've created a directory called 'overrides' for this purpose).
%
% >> which help %verify it's now picking up your overloaded copies
% .../mymatlabcode/overrides/help.m
%
% >> which makehelphyper
% .../mymatlabcode/overrides/makehelphyper.m
%
% %verify it's working
% >> help help
%
% %you should see the following text at the end of the help listing, with
% 'edit help' being a clickable hyperlink that will open help.m
%
% Edit this file
% edit help
%
% ******************************************************************************
% This is a modified version of the file supplied with matlab (R14SP1) that
% adds hyperlinks for editing files to help listings.
% The original is found in MATLAB701/toolbox/matlab/helptools/private
%
% [my modifications are clearly indicated in the code, search for 'JRI']
%
% ******************************************************************************
%
% Copyright 1984-2004 The MathWorks, Inc.
% $Revision: 1.1.6.7 $ $Date: 2004/08/25 19:09:13 $
itemLoc = which(fcnName);
if (strcmp(itemLoc,'built-in'))
itemLoc = which([fcnName '.m']);
end
% ******************************************************************************
% *** JRI 9/1/06, add variable to preserve full pathname for edit hyperlink
JRI_fullFcnName = fcnName;
% ******************************************************************************
% Isolate the function name in case a full pathname was passed in
[unused fcnName unused unused] = fileparts(fcnName);
% Is this topic a function?
itemIsFunction = length(itemLoc);
% Make "see also" references act as hot links
CR = sprintf('\n');
seeAlso = sprintf('See also');
lengthSeeAlso = length(seeAlso);
xrefStart = findstr(helpStr, sprintf('See also'));
for seeAlsoIndex = 1:length(xrefStart)
nameChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_/.'; % Note : '.' is now valid (for MATLAB class syntax)
delimChars = [ ', ' CR ];
% Determine start and end of "see also" portion of the help output
pieceStr = helpStr(xrefStart(seeAlsoIndex)+lengthSeeAlso : length(helpStr));
periodPos = findstr(pieceStr, '.');
notePos = min([findstr(pieceStr, sprintf('Overloaded')) findstr(lower(pieceStr), sprintf('note:'))]);
crPos = findstr(pieceStr,[CR CR]); % blank line
if isempty(notePos) && isempty(crPos)
xrefEnd = length(helpStr);
trailerStr = '';
elseif ~isempty(notePos)
xrefEnd = xrefStart(seeAlsoIndex)+lengthSeeAlso + notePos(1) - 1;
trailerStr = pieceStr(notePos(1):length(pieceStr));
else
xrefEnd = xrefStart(seeAlsoIndex)+lengthSeeAlso + crPos(1) - 1;
trailerStr = pieceStr(crPos(1):length(pieceStr));
end
% Parse the "See Also" portion of help output to isolate function names.
seealsoStr = '';
word = '';
for chx = xrefStart(seeAlsoIndex)+lengthSeeAlso : xrefEnd
if length(findstr(nameChars, helpStr(chx))) == 1
word = [ word helpStr(chx)];
elseif (length(findstr(delimChars, helpStr(chx))) == 1)
if length(word) > 0
% This word appears to be a function name.
% Make link in corresponding "see also" string.
fname = lower(word);
suffix = '';
if fname(length(fname)) == '.'
% Don't hyperlink the last period of the word.
fname = fname(1:length(fname)-1);
suffix = '.';
end
seealsoStr = [seealsoStr '<a href="matlab:' actionName ' ' fname '">' fname '</a>' suffix];
end
seealsoStr = [seealsoStr helpStr(chx)];
word = '';
else
seealsoStr = [seealsoStr word helpStr(chx)];
word = '';
end
end
% Replace "See Also" section with modified string (with links)
helpStr = [helpStr(1:xrefStart(seeAlsoIndex)+lengthSeeAlso -1) seealsoStr trailerStr];
end
% If there is a list of overloaded methods, make these act as links.
overloadPos = findstr(helpStr, 'Overloaded');
if strcmp(actionName,'doc') == 1
textToFind = ' doc ';
len = 5;
else
textToFind = ' help ';
len = 6;
end
if length(overloadPos) > 0
pieceStr = helpStr(overloadPos(1) : length(helpStr));
% Parse the "Overload methods" section to isolate strings of the form "help DIRNAME/METHOD"
overloadStr = '';
linebrkPos = find(pieceStr == CR);
lineStrt = 1;
for lx = 1 : length(linebrkPos)
lineEnd = linebrkPos(lx);
curLine = pieceStr(lineStrt : lineEnd);
methodStartPos = findstr(curLine, textToFind);
methodEndPos = [length(curLine) - 2];
if (length(methodStartPos) > 0 ) && (length(methodEndPos) > 0 )
linkTag = ['<a href="matlab:' actionName ' ' curLine(methodStartPos(1)+len:methodEndPos(1)+1) '">'];
overloadStr = [overloadStr curLine(1:methodStartPos(1)) linkTag curLine(methodStartPos(1)+1:methodEndPos(1)+1) '</a>' curLine(methodEndPos(1)+2:length(curLine))];
else
overloadStr = [overloadStr curLine];
end
lineStrt = lineEnd + 1;
end
% Replace "Overloaded methods" section with modified string (with links)
helpStr = [helpStr(1:overloadPos(1)-1) overloadStr];
end
% If this topic is not a function description, then it is likely a Contents.m file.
% Scan it for function lists, and modify function names to act as active links.
%*******************************************************************************
% *** JRI 9/1/06 Logic of original function does not work with my modifications
% to add links to edit files.
% (1) Original doesn't correctly handle the case when topic is BOTH a function and a
% directory with Contents.m file
%*******************************************************************************
if (itemIsFunction == 0) || (strcmpi(fcnName, 'Contents')) || (length(findstr(helpStr,'is both a directory and a function'))) || (strcmp(fcnName,'simulink')) || (strcmp(fcnName,'debug'))
fcnPath = '';
%***************************************************************************
% *** JRI 9/1/06 *** (1) don't change fcnName to Contents it it's also a function!
%%% if ~strcmpi(fcnName,'Contents') && ~isempty(fcnName) % ORIGINAL
if ~strcmpi(fcnName,'Contents') && ~isempty(fcnName) && (itemIsFunction == 0),
%***************************************************************************
% We need the pathname to be the name of the dictory, and the
% fcnName to be 'Contents'.
if isempty(pathname)
pathname = fcnName;
else
pathname = [pathname '/' fcnName];
end
fcnName = 'Contents';
end
if strcmpi(fcnName, 'Contents') && ~isempty(pathname)
[fcnPath unused unused] = fileparts(which([pathname filesep fcnName]));
if strncmp(matlabroot, fcnPath, length(matlabroot)) == 1
% if under matlabroot, find the toplevel product directory to
% use for comparison later.
tbxPath = fullfile(matlabroot, 'toolbox');
% get the portion of the path after "toolbox"
postTbxPath = fcnPath(length(tbxPath)+2:end);
% final path for comparison is mlroot/toolbox/<dirname>...
fcnPath = fcnPath(1:length(tbxPath)+findstr(postTbxPath, filesep));
end
end
TAB = sprintf('\t');
helpStr = strrep(helpStr, TAB, ' ');
modHelpStr = '';
linebrkPos = find(helpStr == CR);
lineStrt = 1;
for lx = 1 : length(linebrkPos)
lineEnd = linebrkPos(lx);
curLine = helpStr(lineStrt : lineEnd);
hyphPos = findstr(curLine, ' - ');
if any(hyphPos)
nonblankPos = find(curLine ~= ' ');
if curLine(nonblankPos(1)) == '-'
modHelpStr = [modHelpStr curLine];
else
for i = nonblankPos(1):hyphPos(1)
if (curLine(i) == ' ') || (curLine(i) == ','), break, end;
end
fname = curLine(nonblankPos(1):i-1);
remainder = curLine(i:end);
try
% If there is any help for this name, insert a link for it.
% However, avoid the expensive call to "help" by using "exist"
% to first test for mfiles, builtins, and directories.
fnameType = exist(fname);
if (fnameType == 2) || (fnameType == 7) || (fnameType == 5) || (fnameType == 8) || (any(builtin('helpfunc',fname)))
if ~isempty(fcnPath) && strncmp(fcnPath, which(fname), length(fcnPath)) == 0
% this is likely a shadowed function
modHelpStr = [modHelpStr curLine];
elseif strcmp(fname,'Readme')
modHelpStr = [modHelpStr curLine(1:nonblankPos(1)-1) '<a href="' 'matlab:' actionName ' ' topic '/Readme">' fname '</a>' remainder];
elseif remainder(1) == ','
% Sometimes there are two names separated by a comma.
[fname2, remainder2] = strtok(remainder(3:end));
modHelpStr = [modHelpStr curLine(1:nonblankPos(1)-1) '<a href="' 'matlab:' actionName ' ' fname '">' fname '</a>, ' '<a href="' 'matlab:help ' fname2 '">' fname2 '</a>' remainder2];
else
%%%modHelpStr = [modHelpStr curLine(1:nonblankPos(1)-1) '<a href="' 'matlab:' actionName ' ' fname '">' fname '</a>' remainder]; % ORIGINAL
% *********************************************************************
% *** JRI 9/23/05 --add hyperlink in Contents listing to edit the file
% special case: no edit links for 'help' (root level help)
if ~isempty(pathname),
modHelpStr = [modHelpStr curLine(1:nonblankPos(1)-1) '<a href="' 'matlab:' actionName ' ' fname '">' fname '</a>' ' <a href="matlab:edit ' fname '">[edit]</a>' remainder];
else %root level 'help'
modHelpStr = [modHelpStr curLine(1:nonblankPos(1)-1) '<a href="' 'matlab:' actionName ' ' fname '">' fname '</a>' remainder];
end
% *********************************************************************
end
else
modHelpStr = [modHelpStr curLine];
end
catch
% Just in case an error occurred during the helpfunc call, don't try to
% hyperlink anything.
modHelpStr = [modHelpStr curLine];
end
end
else
modHelpStr = [modHelpStr curLine];
end
lineStrt = lineEnd + 1;
end
helpStr = modHelpStr;
end
%*******************************************************************************
% *** JRI 9/23/05 --for single-function help, add hyperlink to edit the file
% in case of Contents.m, prepend directory name to it
% for root level help ('help'), or help on directories without Contents,
% don't print a link as there's nothing to edit
%
if (itemIsFunction) && ~isempty(pathname),
if (strcmpi(fcnName, 'Contents')),
JRI_linkFcnName = [pathname filesep fcnName];
JRI_fileStr = 'directory''s Contents file';
else
JRI_linkFcnName = JRI_fullFcnName;
JRI_fileStr = 'file';
end
editLinkLine = [CR CR ' Edit this ' JRI_fileStr CR ' <a href="matlab:edit ''' JRI_linkFcnName '''">edit ' JRI_linkFcnName '</a>'];
helpStr = [helpStr editLinkLine];
% if we have function and directory of the same name, also add a link to the
% directory's Contents file (if it has one)
if (length(findstr(helpStr,'is both a directory and a function'))),
JRI_linkFcnName = [pathname filesep 'Contents.m'];
if exist(JRI_linkFcnName,'file'),
editLinkLine = [CR CR ' Edit this directory''s Contents file' CR ' <a href="matlab:edit ''' JRI_linkFcnName '''">edit ' JRI_linkFcnName '</a>'];
helpStr = [helpStr editLinkLine];
end
end
end
%*******************************************************************************
hyperHelp = helpStr;