Code covered by the BSD License  

Highlights from
BetterHelp

image thumbnail

BetterHelp

by

 

03 May 2013 (Updated )

Improves Matlab's help functionality for functions that do not have their own help files.

myHelpUtils.helpParts
classdef helpParts < handle
    %HELPPARTS parses help command into its various parts, including:
    % - The See Also section
    % - The note following the See Also section
    % - Overloaded methods
    % - Examples
    % - Links to published demos
    %
    % Modified to allow custom part sections. See also help2html
    
    % Copyright 2009 The MathWorks, Inc.
    properties (Access=private)
        allParts = []; % array of helpUtils.atomicHelpParts
        otherParts = cell(0);
    end
    
    properties (GetAccess = private, Constant)
        Raw               = 0; % Unclassified text
        SeeAlso           = 1; % See Also section.
        Note              = 2; % Note section under See Also section
        OverloadedMethods = 3; % Overloaded Methods
        Example           = 4; % Examples
        Demo              = 5; % Demos
        Other             = 6; % Other sections 
        Invalid           = -1; % invalid help part
        
        % Title patterns
        seeAlsoPattern = 'See also';
        notePattern  = 'note';
        overloadedMethodsPattern = 'Overloaded methods:';
        demoPattern = 'Published output in the Help browser';
    end
    
    
    methods
        function this = helpParts(fullHelpText,otherHeaders)
            %Constructor takes help comments and extracts specific parts
            
            if ~ischar(fullHelpText)
                error(message('MATLAB:helpUtils:helpParts:InvalidInput'));
            end
            
            this.allParts = myHelpUtils.atomicHelpPart('', fullHelpText, myHelpUtils.helpParts.Raw);
            
            % First look for the see also section.
            found = this.addHelpPart(myHelpUtils.helpParts.seeAlsoPattern);
            
            % Only look for a note if there is a see also!
            if found
                this.addHelpPart(myHelpUtils.helpParts.notePattern);
            end
            
            % Add overloaded methods help part (if any)
            this.addHelpPart(myHelpUtils.helpParts.overloadedMethodsPattern);
            
            % Add demo help part (if any).
            this.addHelpPart(myHelpUtils.helpParts.demoPattern);

            % Extract examples (if any)
            this.addExampleParts;
            
            % Extract other parts (if any)
            otherHeaders = otherHeaders(end:-1:1);
            for ii=1:numel(otherHeaders)
                this.addOtherParts(otherHeaders{ii});
            end
        end
        
        
        function allHelpText = getFullHelpText(this)
            % GETFULLHELPTEXT - returns full help stored by all the help parts
            allHelpText = '';
            for i = 1:length(this.allParts)
                allHelpText = [allHelpText this.allParts(i).getTitle this.allParts(i).getText]; %#ok<AGROW>
            end
        end
        
        function atomicHelpPart = getPart(this, partName)
            % GETPART - retrieves specific help part if found in MATLAB file
            enumType = getEnumeration(partName);
            
            if enumType ~= myHelpUtils.helpParts.Invalid
                atomicHelpPart = this.allParts([this.allParts.enumType] == enumType);
            else
                if any(strcmp(partName,this.otherParts))
                    atomicHelpPart = this.allParts(...
                        strcmp(partName,{this.allParts.title}));
                else
                    atomicHelpPart = myHelpUtils.atomicHelpPart;
                    atomicHelpPart(end) = [];
                end
            end
        end
    end
    
    methods (Access = private)
        function found = addHelpPart(this, nonLocalizedName)
            % ADDHELPPART - appends help part regardless of the language the part name is in
            
            localizedName = xlate(nonLocalizedName);
            
            found = this.searchAndAppendHelpPart(regexptranslate('escape', localizedName), nonLocalizedName);
            
            if ~found && ~strcmp(nonLocalizedName, localizedName)
                found = this.searchAndAppendHelpPart(nonLocalizedName);
            end
        end
        
        function found = searchAndAppendHelpPart(this, titlePattern, nonLocalizedName)
            % SEARCHANDAPPENDHELPPART - helper method that searches and appends a help
            % part if found. The algorithm is as follows:
            % 1. Take the last part
            % 2. Extract the part titled TITLEPATTERN from this last part
            % 3. Replace the previous part with the newly extracted
            %    part.
            % 4. Save remainder as the last part.
            found = false;
            parts = regexpi(this.allParts(end).getText, ['(.*)(' titlePattern ':?)(.*)'], 'tokens', 'once');
            if ~isempty(parts)
                whiteLine = regexp(parts{3}, '\n\s*\n', 'once');
                if isempty(whiteLine)
                    rawRemainderHelpPart = [];
                else
                    rawRemainderHelpPart = myHelpUtils.atomicHelpPart('',  parts{3}(whiteLine:end), myHelpUtils.helpParts.Raw);
                    parts{3} = parts{3}(1:whiteLine-1);
                end
                
                if nargin > 2
                    % title pattern is localized
                    enumType = getEnumeration(nonLocalizedName);
                else
                    enumType = getEnumeration(titlePattern);
                end
                
                newAtomicHelpPart = myHelpUtils.atomicHelpPart(parts{2}, parts{3}, enumType);
                
                this.allParts(end).replaceText(parts{1});
                this.allParts = [this.allParts, newAtomicHelpPart, rawRemainderHelpPart];
                found = true;
            end
        end
        
        function addExampleParts(this)
            % ADDEXAMPLEPARTS - helper method that extracts examples from
            % the raw help preceding the "See Also" section.
            
            rawHelpPart = this.allParts(1);
            
            [examples, raw] = regexpi(rawHelpPart.getText, ...
                '^(?<indent> *)(?<header>(Examples?):?.*)(?<body>(\k<indent> +.*| *)\n*)*',...
                'names', 'lineanchors', 'dotexceptnewline', 'split');
            
            if ~isempty(examples)
                newParts(length(examples)*2) = myHelpUtils.atomicHelpPart;
                
                for i = 1:length(examples)
                    % Need to concatenate spaces preceding the example header.
                    rawWithIndent = [raw{i} examples(i).indent];
                    
                    newParts(2*i-1) = myHelpUtils.atomicHelpPart('', rawWithIndent, myHelpUtils.helpParts.Raw);
                    
                    newParts(2*i) = myHelpUtils.atomicHelpPart(examples(i).header, examples(i).body, myHelpUtils.helpParts.Example);
                end
                
                lastRawPart = myHelpUtils.atomicHelpPart('', raw{end}, myHelpUtils.helpParts.Raw);
                
                this.allParts = [newParts, lastRawPart, this.allParts(2:end)];
            end
        end
        function addOtherParts(this,head)
            % ADDOTHERPARTS - helper method that extracts sections
            % specified by head from the raw help preceding the "See Also"
            % section.
            
            rawHelpPart = this.allParts(1);
            
            [examples, raw] = regexpi(rawHelpPart.getText, ...
                ['\n[ \t]*\n',...
                '(?<indent>[ \t]*)',...
                '(?<header>',head,'):[ \t]*\n',...
                '(?<body>(\k<indent>[ \t]+.*\n?)*)$'],...
                'names', 'lineanchors', 'dotexceptnewline', 'split');
            
            if ~isempty(examples)
                newParts(length(examples)*2) = myHelpUtils.atomicHelpPart;
                
                for i = 1:length(examples)
                    % Need to concatenate spaces preceding the example header.
                    rawWithIndent = [raw{i} examples(i).indent];
                    
                    newParts(2*i-1) = myHelpUtils.atomicHelpPart('', rawWithIndent, myHelpUtils.helpParts.Raw);
                    
                    newParts(2*i) = myHelpUtils.atomicHelpPart(examples(i).header, examples(i).body, myHelpUtils.helpParts.Other);
                end
                
                lastRawPart = myHelpUtils.atomicHelpPart('', raw{end}, myHelpUtils.helpParts.Raw);
                
                this.allParts = [newParts, lastRawPart, this.allParts(2:end)];
                this.otherParts = [this.otherParts,head];
            end
        end
    end
end

%% ------------------------
function enumType = getEnumeration(partName)
    % GETENUMERATION - returns enumeration corresponding to input part name
    switch lower(partName)
        case {'seealso', 'see also'}
            enumType = myHelpUtils.helpParts.SeeAlso;
        case 'note'
            enumType = myHelpUtils.helpParts.Note;
        case 'raw'
            enumType = myHelpUtils.helpParts.Raw;
        case {'overloaded methods:', 'overloaded'}
            enumType = myHelpUtils.helpParts.OverloadedMethods;
        case {'example', 'examples'}
            enumType = myHelpUtils.helpParts.Example;
        case {'published output in the help browser', 'demo'}
            enumType = myHelpUtils.helpParts.Demo;
        otherwise
            % partName is invalid.
            enumType = myHelpUtils.helpParts.Invalid;
    end
end

Contact us