Documenting (help section of) an m-file

by

Guzman (view profile)

 

This script makes a template for documenting m-files.

DocFun.m
function DocFun(fileName, authorName, authorEMail)

%DOCFUN inserts a template to document a function
%
% The first line of the function must be the definition header (no blank
% lines expected). Currently DocFun does not support multi-line headers.
%
% SYNTAX
%    DocFun(fileName)
%
% INPUT
%        fileName: a string containing the name of the function. It must be an
%                  m-file, though it can be passed without the .m extension.
%      authorName: (opt.) string containing the author's name
%     authorEMail: (opt.) string containing the author's email
%
% OUTPUT
%
% EXAMPLES
%     DocFun('DocFun')
%     DocFun('DocFun', 'Guzmn Daz')
%     DocFun('DocFun', 'Guzmn Daz', 'guzman_at_uniovi.es')
%     DocFun('DocFun.m')
%
% M-FILES required: none
%
% MAT-FILES required: none
%
%
% Author:       Guzmn Daz
% email:        guzman_at_uniovi.es
% Matlab ver.:  7 (R2010a)
% Date:         20-Apr-2013
% Update:       

%-------------------------



%% Check input
% m-file
if length(fileName) >2
    if strcmpi(fileName(end-1:end), '.m')
        fileName = fileName(1:end-2);           % remove .m extension
    end
end
% Does it exist?
if ~exist([fileName, '.m'], 'file')
    error(['File ', fileName, '.m not found']);
end


%% Prepare files
tmpFile = tempname;

fidTmp = fopen(tmpFile, 'w');
fid    = fopen([fileName, '.m'], 'rt');


%% Obtain args
% Read file
nLineIn = 1;                                    % initialize line counter
clear('lineIn')                                 % clear line content storage
while fid > 0
    lineIn{nLineIn, 1} = fgetl(fid);            % read all file
    if ~ischar(lineIn{nLineIn, 1})
        break                                   % when eof, exit
    end
    nLineIn = nLineIn + 1;
end
fclose(fid);                                    % close m-file

% First line must be header
if isempty(findstr(lineIn{1}, 'function'))
    error('First line of the function must be a function definition header')
end

% Argins
cAux = regexp(lineIn{1}, '[()]', 'split');
if length(cAux) > 1                             % argin found
    cArgin = regexp(cAux{2}, ',', 'split');
else
    cArgin = [];
end

% Argouts
if findstr(lineIn{1}, '=')                % argout found
    cAux = lineIn{1}(1:findstr(lineIn{1}, '=')-1);
    cAux = regexp(cAux, ']', 'split');
    if length(cAux) > 1                   % argouts
        cAux = regexp(cAux{1}, '[', 'split');
        cArgout = regexp(cAux{2}, ',', 'split');
    else
        cAux = regexp(cAux{1}, ' ', 'split');
        cArgout{1} = cAux{2};
    end
else
    cArgout = [];
end

% Syntax
cAux = regexp(lineIn{1}, 'function', 'split');
cSyntax = strtrim(cAux{2});


%% Write temporary file
% Header
fprintf(fidTmp,[lineIn{1}, '\n']);
fprintf(fidTmp, '\n');

% Help
fprintf(fidTmp,['%%', upper(fileName), '\n']);
fprintf(fidTmp, '%%\n');
fprintf(fidTmp, '%% SYNTAX\n');
fprintf(fidTmp,['%%    ', strtrim(cSyntax), '\n']);
fprintf(fidTmp, '%%\n');
fprintf(fidTmp, '%% INPUT\n');
if ~isempty(cArgin)
    maxLength = max(cellfun('length', cArgin)); % max length of all cell elements
    for i = 1:length(cArgin)
        fprintf(fidTmp, ['%%    ', repmat(' ', 1, maxLength-length(strtrim(cArgin{i}))), strtrim(cArgin{i}), ': \n']);
    end
end
fprintf(fidTmp, '%%\n');

fprintf(fidTmp, '%% OUTPUT\n');
if ~isempty(cArgout)
    maxLength = max(cellfun('length', cArgout)); % max length of all cell elements
    for i = 1:length(cArgout)
        fprintf(fidTmp, ['%%    ', repmat(' ', 1, maxLength-length(strtrim(cArgout{i}))), strtrim(cArgout{i}), ': \n']);
    end
end
fprintf(fidTmp, '%%\n');

fprintf(fidTmp, '%% EXAMPLES\n');
fprintf(fidTmp, '%%\n');

fprintf(fidTmp, '%% M-FILES required: ');
calledFiles = mydepfun(fileName, true);
if length(calledFiles) > 1
    fprintf(fidTmp, '\n');
    for i = 1:length(calledFiles)
        [~, oneCalledFile.name, ~] = fileparts(calledFiles{i});
        if ~strcmp(oneCalledFile.name, fileName)
                fprintf(fidTmp, ['%%    ', oneCalledFile.name, '.m\n']);
        end
    end
else
    fprintf(fidTmp, 'none\n');
end
fprintf(fidTmp, '%%\n');

fprintf(fidTmp, '%% MAT-FILES required: none\n');

fprintf(fidTmp, '%%\n');
fprintf(fidTmp, '%%\n');
if exist('authorName', 'var')
   fprintf(fidTmp, ['%% Author:       ', authorName, '\n']);
else
   fprintf(fidTmp, '%% Author:       \n');
end
if exist('authorEMail', 'var')
   fprintf(fidTmp, ['%% email:        ', authorEMail, '\n']);
else
   fprintf(fidTmp, '%% email:        \n');
end
fprintf(fidTmp,['%% Matlab ver.:  ', version,'\n']);
fprintf(fidTmp,['%% Date:         ', date,'\n']);
fprintf(fidTmp, '%% Update:       \n\n');
fprintf(fidTmp, ['%%', repmat('-', 1, length(lineIn{1})), '\n']);
fprintf(fidTmp, '\n');

% Rest of code
warning off
fprintf(fidTmp, '%s\n', lineIn{2:end});
warning on

% Close all files
fclose('all');


%% Manage files
% Make a backup copy
copyfile([fileName, '.m'], [fileName, '.bak'])

% Replace old file with temporary
copyfile(tmpFile, [fileName, '.m'])

% Delete temporary file
delete(tmpFile)









%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Malcolm Wood's 
% http://www.mathworks.com/matlabcentral/fileexchange/10702-exporttozip-identifies-the-dependencies-of-an-m-file-and-creates-a-zip-file
function filelist = mydepfun(fn,recursive)
%MYDEPFUN - Variation on depfun which skips toolbox files
%
% filelist = mydepfun(fn)
% filelist = mydepfun(fn,recursive)
%
% Returns a list of files which are required by the specified
% function, omitting any which are inside $matlabroot/toolbox. 
%
% "fn" is a string specifying a filename in any form that can be
%   identified by the built-in function "which".
% "recursive" is a logical scalar; if false, only the files called
%   directly by the specified function are returned.  If true, *all*
%   those files are scanned to, and any required by those, and so on.
%
% "filelist" is a cell array of fully qualified file name strings,
%   including the specified file.
%
% e.g.
%     filelist = mydepfun('myfunction')
%     filelist = mydepfun('C:\files\myfunction.m',true) 

% Copyright 2006-2010 The MathWorks, Inc.

if ~ischar(fn)
    error('First argument must be a string');
end

foundfile = which(fn);
if isempty(foundfile)
    error('File not found: %s',fn);
end

% Scan this file
filelist = i_scan(foundfile);

% If "recursive" is supplied and true, scan files on which this one depends.
if nargin>1 && recursive
    % Create a list of files which we have still to scan.
    toscan = filelist;
    toscan = toscan(2:end); % first entry is always the same file again
    % Now scan files until we have none left to scan
    while numel(toscan)>0
        % Scan the first file on the list
        newlist = i_scan(toscan{1});
        newlist = newlist(2:end); % first entry is always the same file again
        toscan(1) = []; % remove the file we've just scanned
        % Find out which files are not already on the list.  Take advantage of
        % the fact that "which" and "depfun" return the correct capitalisation
        % of file names, even on Windows, making it safe to use "ismember"
        % (which is case-sensitive).
        reallynew = ~ismember(newlist,filelist);
        newlist = newlist(reallynew);
        % If they're not already in the file list, we'll need to scan them too.
        % (Conversely, if they ARE in the file list, we've either scanned them
        %  already, or they're currently on the toscan list)
        toscan = unique( [ toscan ; newlist ] );
        filelist = unique( [ filelist ; newlist ] );
    end
end

%%%%%%%%%%%%%%%%%%%%%
% Returns the non-toolbox files which the specified one calls.
% The specified file is always first in the returned list.
function list = i_scan(f)

func = i_function_name(f);

list = depfun(func,'-toponly','-quiet');

toolboxroot = fullfile(matlabroot,'toolbox');

intoolbox = strncmpi(list,toolboxroot,numel(toolboxroot));

list = list(~intoolbox);

%%%%%%%%%%%%%%%%%%%%%%%%
function func = i_function_name(f)
% Identifies the function name for the specified file,
% including the class name where appropriate.  Does not
% work for UDD classes, e.g. @rtw/@rtw

[dirname,funcname] = fileparts(f);
[ignore,dirname] = fileparts(dirname);

if ~isempty(dirname) && dirname(1)=='@'
    func = [ dirname '/' funcname ];
else
    func = funcname;
end







Contact us