function varargout = pm(fid, varargin)
%pm Project Manager
%
% Warning:
% First run the following code and if that gives you 'Sorry...', then
% do not use 'pm.m'.
% >> pm
% If it says it is OK to use, then run the following command first
% before using pm.m to save your current project. When you save, be
% sure that you make a project file in your project root directory.
% >> pm('save_project')
% Otherwise, it may close all your documents, delete path information
% though all of these will be saved in
% 'temp_pm_backup_<date,time>.mat' in the current directory. In
% case you want to recover all of them, just open it using :
% >> pm('open_project',backup_file_name)
%
% Installation:
% 1. Copy all files to a directory you want. Add it to the MATLAB path.
% 2. For full functionality, give it write permission.
% 3. See startup_sample.m and finish_sample.m for automation.
%
% Features:
% 1. 'pm', which is a very simple and loose project manager, saves only the
% following information for each project that is necessary to switch
% between projects :
% - Current working directory
% - Project specific Path
% - All opened m-files (order is not guaranteed in pm.)
% - Current active m-file.
% - List of run configurations you want to execute sequentially.
%
% 2. 'pm' can auto-run many <a href="matlab: web([docroot '/techdoc/matlab_env/brqxeeu-131.html#brqxeeu-140'])">Run Configurations</a> sequentially. I'm using it
% to unit-test in a very simple form. Caution: This functionality is just
% for my convenience. I don't guarantee anything. And generation of useful
% message in each testing case (or run configuration) is your
% responsibility. This is not intended to provided a product level
% unit-testing suite. If you prefer using separate abc_test.m files,
% use it, not this. (I might add a feature that generates abc_test.m
% file for abc.m file from run configurations. Anyway...)
% 1. Write a testing purpose run configuration for a m-file. You can
% make multiple run configuraitons for each m-file.
% 2. Open the list editor
% >> pm('unit_test_project_edit')
% 3. Select run configurations that you want to execute. It will be
% saved as a test suite for the current project.
% 4. Click 'Save/Run'. Or after saving it, you can use:
% >> pm('unit_test_project')
% 5. This will sequentially execute run configurations in the order
% that you determined in the editor.
%
% Usage examples:
% % The first argument to pm.m is a token of action. The second or
% % later arguments are related to that token. There are twelve tokens
% % and they are chars.
% % 'save_project'
% % 'open_project'
% % 'new_project'
% % 'current_project'
% % 'close_project'
% % 'backup'
% % 'clear_backup'
% % 'startup_pm'
% % 'finish_pm'
% % 'pm_test'
% % 'unit_test_project_edit'
% % 'unit_test_project'
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %% Save project
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pm('save_project') % opens a dialog box to select a project file
% % or saves the current project.
% pm('save_project','') % save current project (or workspace) as a
% % new project (Save as new...)
% pm('save_project','my_pm.mat') % saves the project in 'my_pm.mat'
% % into the current working directory.
% pm('save_project','c:\projects\proj1\my_pm.mat')
% % you can specify the full path and
% % name of the project file.
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %% Open project
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pm('open_project') % opens a dialog box
% pm('open_project','my_pm.mat')
% pm('open_project','c:\projects\proj1\my_pm.mat')
%
% pm('open_project','',0) % the same as pm('open_project')
% pm('open_project','',1) % Paths to opened documents outside the
% % project root directory will be absolute.
% pm('open_project','my_pm.mat',1)
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %% New project
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pm('new_project') % opens a dialog box
% pm('new_project','my_pm.mat') % makes a new project named
% % 'my_pm.mat' in the current directory
% pm('new_project','c:\projects\proj1\my_pm.mat')
%
% pm('new_project','',1) % make a new project but keep the path of
% % the previoius project or previous workspace.
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %% Get the name of the current project
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pm('current_project') % shows the path to the current project.
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %% Close project
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pm('close_project') % closes current project, document, and reset
% % path to system default.
%
% pm('close_project',1) % the same as pm('close_project')
% pm('close_project',0) % closes current project, but keep documents
% % open.
%
% pm('close_project',1,1) % the same as pm('close_project')
% pm('close_project',1,0) % closes current project, close documents,
% % but path is kept.
%
% pm('close_project',0,0) % close project, keep documents, keep path
% pm('close_project',0,1) % close project, but keep documents, reset
% % path
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %% Open the unit-testing-selection editor
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pm('unit_test_project_edit')
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %% Unit test
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pm('unit_test_project')
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %% Clear backup files starting with 'temp_pm_backup_*'
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pm('clear_backup')
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %% Check compatibility (it is a simple test and not guaranteed).
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pm('pm_test')
%
% Useful Shortcuts: <a href="matlab: web([docroot '/techdoc/matlab_env/bq37azl-1.html#bq37azl-6'])">MATLAB shortcuts</a>
% - Open project
% pm('open_project')
% - Save current project
% pm('save_project')
% - Save project as...
% pm('save_project','')
% - Close project
% pm('clost_project')
% - Unit Test Editor
% pm('unit_test_project_edit')
% - Unit Test
% pm('unit_test_project')
% - My project 1
% pm('open_project','C:\projs\pj1\project_myprj_1.mat');
% - My project 2
% % pm('save_project') % Add this if you want to automatically save the current proejct.
% pm('open_project','C:\projs\pj2\project_myprj_2.mat');
% - Clean-up
% clear import; clear variables; close all hidden; clc;
% pm('clear_backup');
%
% Automation on quit or startup MATLAB:
% - You can use 'startup.m' or 'finish.m' to automate the project
% management when you startup or quit MATLAB. Use corresponding
% token: 'startup_pm', 'finish_pm'.
% See <a href="matlab: edit startup_sample">startup_sample.m</a> and <a href="matlab: edit finish_sample.m">finish_sample.m</a>.
% See also <a href="matlab: doc startup.m">startup.m</a>, <a href="matlab: doc finish.m">finish.m</a>.
% - Warning: To use this feature: 1. the directory containing 'pm.m'
% must have write permission, because exit information is saved
% in it. 2. The directory containing 'startup.m' and 'finish.m'
% always must be in search path (during startup MATLAB and
% exiting MATLAB).
%
% Notes:
% - pm.m should be in the path.
% - It does not clear the workspace.
% - It does not save the workspace.
% - You have to save all current documents to open another project.
% - When open a new project, it does not automatically save the
% current project. If you want to do that, use shortcuts. See
% above.
% - Saving project will generate path file like my_pm_pathdef.m
% Keep this in the same directory as the project file (my_pm.mat).
% - paths to the opened m-files that are in the project root folder
% or subdirectories of the project root folder are all relative
% path. So, you can move whole project directory together. But
% paths to other files that is outside of the root project folder
% are absolute paths by default. You can make all paths relative.
% See Usage examples above. <a href="matlab: web([docroot '/toolbox/slvnv/ug/f45716.html#f53374'])">Resolving the document path</a>
% - Related to the previous path issue, all MATLAB path will be saved
% as an absolute path. So if you move your project directory
% somewhere and you've had subdirectories in your path, you need
% to add those subdirectories again after you move your project,
% and save the project.
% - Current working directory is saved as relative path to the
% project root directory IF it is a subdirectory of it, otherwise
% it is saved as an absolute path.
% - 'new_project', 'close_project', 'open_project' do not
% automatically save current project, but they do backup.
%
% Caution:
% 1. pm.m add its directory to path. So you may want to place pm.m in
% some other general purpose tool folder something like 'Matlab
% Central File Exchange' folder.
% 2. Docuemnt order in the editor is not guaranteed to be correctly
% saved, because I couldn't find a service function that returns
% the order of the documents. So, this may not work in other
% MATLAB versions. In this situation, instead, you can arrange
% documents as you want, close MATLAB, open MATLAB, and save
% project again. This is clumsy, but it works because MATLAB
% reload previous opened documents in the order.
% 3. The following builtin editor services are used. They are not
% documented, and highly prone to the version of MATLAB. If these
% services are not supported in your version of MATLAB, pm.m does
% not work at all. pm('pm_test') checks these services except 'closeAll'.
% e=com.mathworks.mlservices.MLEditorServices
% e.closeAll
% e.builtinGetOpenDocumentNames
% e.isDocumentDirty(which('pm.m'))
% e.builtinGetActiveDocument % This service was added very
% % recently (as far as I know,
% % R2007a doesn't have it).
% 4. The following two functions are documented. But they were added
% recently. As far as I know, R2007a supports them, but my old
% MATLAB 7.1 (2005) didn't support it.
% setenv
% getenv
%
% See also:
% <a href="matlab: doc startup.m">startup.m</a>
% <a href="matlab: doc finish.m">finish.m</a>
% <a href="matlab: web([docroot '/techdoc/matlab_env/bq37azl-1.html#bq37azl-6'])">MATLAB shortcuts</a>
% <a href="matlab: web([docroot '/techdoc/matlab_env/brqxeeu-131.html#brqxeeu-140'])">Run Configurations</a>
% <a href="matlab: web([docroot '/toolbox/slvnv/ug/f45716.html#f53374'])">Resolving the document path</a>
%% Pick the function corresponding to the given action token.
% Token name is the sub-function name. The following codes arrange other
% input arguments as input to the sub-function.
if ~exist('fid','var')
OK=pm_test(); % Default action when there is no token.
% if OK
% disp(' ');
% disp('=========================================================');
% help pm;
% disp('WARNING: run the following command first before you use pm.m, ');
% disp(' otherwise it will close all your documents.');
% disp(' >> pm(''save_project'')');
% disp(' ');
% disp('Enjoy~!');
% end
return;
end
fh = str2func(fid);
[varargout{1:nargout}] = fh(varargin{:});
end % function
% Generate testing m-file.
function OK=pm_test()
% This function tests compatibility.
% There should not be any error.
% This test does not test 'closeAll'.
OK=1;
disp(' ');
disp('=========================================================');
disp('Checking editor services...');
try
e=com.mathworks.mlservices.MLEditorServices;
catch
disp('Cannot find Editor service.');
disp('Sorry. You cannot use pm.m');
OK=0;
end
try
e.builtinGetOpenDocumentNames;
catch
disp('Cannot use com.mathworks.mlservices.MLEditorServices.builtinGetOpenDocumentNames');
disp('Sorry. You cannot use pm.m');
OK=0;
end
try
edit(which('pm.m'));
catch
disp('Cannot use ''edit'' function.');
disp('Sorry. You cannot use pm.m');
OK=0;
end
try
e.isDocumentDirty(which('pm.m'));
catch
disp('Cannot use com.mathworks.mlservices.MLEditorServices.isDocumentDirty');
disp('Sorry. You cannot use pm.m');
OK=0;
end
try
e.builtinGetActiveDocument;
catch
disp('Cannot use com.mathworks.mlservices.MLEditorServices.builtinGetActiveDocument');
disp('Sorry. You cannot use pm.m');
OK=0;
end
if ~OK
disp('=========================================================');
disp(' ');
disp('Sorry. You cannot use pm.m');
return;
end
OK2=1;
disp('Checking MATLABDesktop.xml');
f = fullfile(prefdir,'MATLABDesktop.xml');
if ~exist(f,'file')
disp('There is not MATLABDesktop.xml. Document order cannot be saved.');
OK2=0;
else
text = fileread(f);
c = which('pm.m');
if isempty(strfind(text,c))
disp('Document order may not be correctly saved.');
OK2=0;
end
end
disp('Checking run_configurations.xml');
config_list = parseConfigs();
if isnumeric(config_list)
disp('Sorry. You cannot use unit-testig.');
OK2=0;
end
disp('=========================================================');
disp(' ');
if OK2
disp('You system looks compatible.');
else
disp('You system looks compatible although some features are not guaranteed.');
end
end % function
function new_project(pm_filename, keep_path)
if ~exist('keep_path','var') || isempty(keep_path)
keep_path = 0;
end
%% Project filename
clc;
if ~exist('pm_filename','var')
pm_filename = '';
end
[valid,pm_path,pm_name] = my_fileparts(pm_filename,'write','Create a New Project');
if valid==0
disp('Not a valid project filename');
return;
end
clear pm_filename;
backup();
%% 1. Close all documents
editor_handle=com.mathworks.mlservices.MLEditorServices;
myCloseAll(editor_handle);
%% 2. Go to the project root directory.
cd(pm_path);
% edit;
%% 3. Set path
if ~keep_path
restoredefaultpath;
end
addpath(fileparts(mfilename('fullpath')));
%% 4. Save
% Then save the file.
pm('save_project',fullfile(pm_path,[pm_name,'.mat']));
disp(['New project [ ',pm_name,' ] is generated.']);
disp('Use "clear import" to clear only imported java packages.');
disp('Use "clear variables" to clear variables in previous workspace.');
disp('Use "clear all" to clean everything.');
disp('Use "close all" to close all previous figures.');
end % function
function close_project(close_doc, reset_path)
if ~exist('close_doc','var') || isempty(close_doc)
close_doc = 1;
end
if ~exist('reset_path','var') || isempty(reset_path)
reset_path = 1;
end
clc;
backup();
a=getenv('matlab_pm_current_project');
if isempty(a)
disp('No project is currently open.');
disp('If you belive you''re seeing an opened project, save it first.');
return;
end
[pm_path, pm_name] = fileparts(a);
%% 1. Close all documents
if close_doc
editor_handle=com.mathworks.mlservices.MLEditorServices;
myCloseAll(editor_handle);
end
%% 2. Set path
if reset_path
restoredefaultpath;
end
addpath(fileparts(mfilename('fullpath')));
%% 3. Clear the env variable.
setenv('matlab_pm_current_project','');
disp(['Project [ ',pm_name,' ] is closed.']);
disp('Use "clear import" to clear only imported java packages.');
disp('Use "clear variables" to clear variables in previous workspace.');
disp('Use "clear all" to clean everything.');
disp('Use "close all" to close all previous figures.');
end % function
function a=current_project()
a=getenv('matlab_pm_current_project');
if isempty(a)
disp('No project is open.');
else
[pm_path,pm_name]=fileparts(a);
disp(['Project name: ', pm_name]);
disp(['Project file: ', a]);
end
end % function
function save_project(pm_filename,makeAllPathRelative,quiet_save,backup_mode)
if ~exist('backup_mode','var') | isempty(backup_mode)
backup_mode = 0;
end
if ~exist('quiet_save','var') | isempty(quiet_save)
quiet_save = 0;
end
if ~exist('makeAllPathRelative','var') || isempty(makeAllPathRelative)
makeAllPathRelative = 0;
end
current_p=pwd;
%% Project filename
if ~exist('pm_filename','var')
s=getenv('matlab_pm_current_project');
if isempty(s)
pm_filename = '';
else
pm_filename = s;
end
end
[valid,pm_path,pm_name] = my_fileparts(pm_filename,'write','Save Project As...');
if valid==0
disp('Not a valid project filename');
return;
end
clear pm_filename;
if exist(fullfile(pm_path,[pm_name,'.mat']), 'file')
load(fullfile(pm_path,[pm_name,'.mat']));
end
%% Collect project information
% 1. path of the current working directory
tmp_cp = pwd;
if length(pm_path)<length(tmp_cp) && strfind(tmp_cp,pm_path)
a=pm('myMakeRelativePath',pwd,pm_path,[],[],0); % Make relative.
tmp_cp = a{1};
elseif strcmp(pm_path,tmp_cp)
tmp_cp = '.';
end
pm_info.project_current_directory = tmp_cp;
% 2. information of opened m-files
[pm_info.opened_mfiles, pm_info.active_mfile] = getEditorInfo(pm_path, makeAllPathRelative);
% 3. matlab path information
% pm_info.path = path;
cd(pm_path);
savepath([pm_name,'_pathdef.m']);
cd(pm_info.project_current_directory);
% 4. Add current project root path information for future use...
pm_info.last_project_root_directory = pm_path;
%% Save
% Then save the file.
save(fullfile(pm_path,[pm_name,'.mat']), 'pm_info');
if ~backup_mode
setenv('matlab_pm_current_project',fullfile(pm_path,[pm_name,'.mat']) );
end
if ~quiet_save
disp(['Project [ ',pm_name,' ] is saved at: ']);
disp([' ',fullfile(pm_path,[pm_name,'.mat'])]);
disp([' ',fullfile(pm_path,[pm_name,'_pathdef.m'])]);
end
cd(current_p);
end %function
function open_project(pm_filename,quiet_open,open_documents,startup_mode)
if ~exist('startup_mode','var') | isempty(startup_mode)
startup_mode = 0;
end
if ~exist('quiet_open','var') | isempty(quiet_open)
quiet_open = 0;
end
if ~exist('open_documents','var') | isempty(open_documents)
open_documents = 1;
end
%% Project filename
if ~exist('pm_filename','var')
pm_filename = '';
end
[valid,pm_path,pm_name] = my_fileparts(pm_filename,'read','Open Project');
if valid==0
disp('Not a valid project filename');
return;
end
clc;
clear pm_filename;
if ~startup_mode ; backup(quiet_open); end
if strcmp(getenv('matlab_pm_current_project'),fullfile(pm_path,[pm_name,'.mat']) )
disp(['Project [ ',pm_name,' ] is already open.']);
disp(['If you believe you open different project, then close current project first.']);
return;
end
%% Close all m-files before opening a project.
editor_handle=com.mathworks.mlservices.MLEditorServices;
if open_documents
myCloseAll(editor_handle);
end
setenv('matlab_pm_current_project','') % Clear this first for safety.
%% Load project information
cd(pm_path);
load([pm_name,'.mat']);
%% Configure project
% 1. Open m-files for the project
cd(pm_path);
if open_documents
applyEditorInfo(pm_info.opened_mfiles, pm_info.active_mfile)
end
% 2. Set path for the project
% path(pm_info.path);
cd(pm_path);
if exist([pm_name,'_pathdef.m'],'file')
if isfield(pm_info,'last_project_root_directory')
last_project_root_directory = pm_info.last_project_root_directory;
else
last_project_root_directory = pm_path;
end
if ~isempty(last_project_root_directory) && ...
~strcmp(last_project_root_directory, pm_path) % Adjust path
disp('============================================');
disp('Detected project root directory change.');
disp('Modifying path information');
disp('============================================');
path_str = eval([pm_name,'_pathdef']);
% while 1
% a=strfind(path_str,last_project_root_directory);
% if isempty(a); break; end
% path_str = [path_str(1:a(1)-1), pm_path,path_str(a(1)+length(last_project_root_directory):end)];
% end
path_str = strrep(str,last_project_root_directory,pm_path);
path(path_str);
else
path( eval([pm_name,'_pathdef']) );
end
else
warning(['There must be [ ', pm_name,'_pathdef.m ] file in the same directory as project file']);
disp('By default, it uses the path of the previous project.');
disp('If you want to use system default, use the command "restoredefaultpath".');
end
% 3. Go to the last working directory
cd(pm_info.project_current_directory);
if ~quiet_open
disp(['Project [ ',pm_name,' ] is loaded.']);
disp('Use "clear import" to clear only imported java packages.');
disp('Use "clear variables" to clear variables in previous workspace.');
disp('Use "clear all" to clean everything.');
disp('Use "close all" to close all previous figures.');
end
setenv('matlab_pm_current_project',fullfile(pm_path,[pm_name,'.mat']) );
end %function
function pm_filename=backup(quiet_backup)
if ~exist('quiet_backup','var') | isempty(quiet_backup)
quiet_backup = 0;
end
p=getenv('matlab_pm_current_project'); % save current project name
d=getenv('matlab_pm_startup_directory');
c=clock;
pm_datetime = ['temp_pm_backup_', ...
num2str(floor(c(1))),'y_',num2str(floor(c(2))),'m_',num2str(floor(c(3))),'d_', ...
num2str(floor(c(4))),'h_',num2str(floor(c(5))),'m_',num2str(floor(c(6))),'s.mat'];
if ~quiet_backup
disp('Project backup is in progress ...');
disp([' backup id: ',pm_datetime(1:end-4)]);
end
if isempty(p)
if isempty(d)
pm_filename = fullfile(pwd,pm_datetime);
else
pm_filename = fullfile(d,pm_datetime);
end
else
[pm_path,pm_name]=fileparts(p);
pm_filename = fullfile(pm_path,[pm_name,'_',pm_datetime]);
end
backup_mode = 1;
save_project(pm_filename,'',quiet_backup,backup_mode);
setenv('matlab_pm_current_project',p);
if ~quiet_backup
disp('Backup is done.');
disp(' ');
end
end % function
function clear_backup(quiet_clear)
if ~exist('quiet_clear','var') | isempty(quiet_clear)
quiet_clear = 0; % so it is verbose.
end
disp('Deleting project backup files ...');
a=input('Do you want to continue (y/n)?','s');
if ~strcmp(a,'y')
disp('Canceled');
return;
end
p=getenv('matlab_pm_current_project');
d=getenv('matlab_pm_startup_directory');
delete(fullfile(p,'*temp_pm_backup_*'));
delete(fullfile(d,'*temp_pm_backup_*'));
delete('*temp_pm_backup_*'); % delete all temp files in the current directory.
if ~quiet_clear
disp('Done.');
end
end % function
function startup_pm()
setenv('matlab_pm_startup_directory',pwd);
pack_path = fileparts(which('pm.m'));
setenv('matlab_pm_package_directory',pack_path);
current_path = pwd;
cd(pack_path);
quiet_open = 1;
if exist('matlab_pm_exit_info.mat','file')
load matlab_pm_exit_info;
%last_project
%pause;
if exist(last_project,'file')
if ~exist('save_success','var') || isempty(save_success) || save_success==0
open_documents = 1;
elseif save_success==1
if pm('check_startup_filelist',last_project);
open_documents = 0; % exploit MATLAB's internal mechanism. With this '0', it'll keep previously opened files open.
else
open_documents = 1;
end
else
error('What''s going on??? #424234');
end
save_success=0;
save matlab_pm_exit_info last_project save_success;
startup_mode = 1;
pm('open_project',last_project,quiet_open,open_documents,startup_mode);
if ~isempty(strfind(last_project,'temp_pm_backup_'))
setenv('matlab_pm_current_project','');
else
[pm_path,pm_name] = fileparts(last_project);
disp(['Project [ ',pm_name,' ] is loaded.']);
disp('Use "clear import" to clear only imported java packages.');
disp('Use "clear variables" to clear variables in previous workspace.');
disp('Use "clear all" to clean everything.');
disp('Use "close all" to close all previous figures.');
end
else
cd(current_path);
disp(['Cannot locate project file: ',last_project]);
disp('The last project is not successfully loaded.');
end
else
cd(current_path);
disp('Cannot locate ''matlab_pm_exit_info.mat''.');
disp('The last project is not successfully loaded.');
end
quiet_clear =1;
%clear_backup(quiet_clear);
end % function
function finish_pm()
p=getenv('matlab_pm_current_project');
d=getenv('matlab_pm_package_directory');
%clear_backup();
if ~isempty(p)
pm('save_project');
last_project = p;
else
last_project = pm('backup');
end
a=pwd;
if ~isempty(d)
cd(d);
else
k=fileparts(which('pm.m'));
if ~isempty(k)
cd(k);
end
end
save_success=1;
save matlab_pm_exit_info last_project save_success;
cd(a);
end % function
function yes = isSaved()
p=getenv('matlab_pm_current_project');
if isempty(p)
yes=0;
end
[pm_path,pm_name] = fileparts(p);
%% Get old project information
load(p);
pm_info_old = pm_info;
a=pwd;
cd(pm_path);
old_path=eval([p_name,'_pathdef']);
cd(a);
clear pm_info;
%% Gather new project information
makeAllPathRelative = 0; % Need to check if old file neams are relative or absolute paths.
od = pm_info_old.opened_mfiles;
for i=1:length(od)
if strcmp(od{i}(1:2),'..')
makeAllPathRelative = 1;
break;
end
end
pm_info_new.project_current_directory = pwd;
[pm_info_new.opened_mfiles, pm_info_new.active_mfile] = pm('getEditorInfo',pm_path,makeAllPathRelative);
new_path = path;
%% Current working directory comparison
if ~strcmp(pm_info_old.project_current_directory, pm_info_new.project_current_directory)
yes=0;
return;
end
%% Compare opened documents
od = pm_info_old.opened_mfiles;
nd = pm_info_new.opened_mfiles;
for i=1:min(length(od),length(nd))
if ~strcmp(od{i},nd{i})
yes=0;
return;
end
end
%% Compare active docuemnt
if ~strcmp(pm_info_old.active_mfile{1}, pm_info_new.active_mfile{1})
yes=0;
return;
end
%% Compare path
if ~strcmp(old_path, new_path)
yes=0;
return;
end
%% Everything is OK.
yes=1;
end % function
function unit_test_project_edit()
pm_filename=getenv('matlab_pm_current_project');
if isempty(pm_filename)
disp('No project is open.');
return;
end
load(pm_filename);
pm_info.filename = pm_filename;
pm_ut(pm_info);
end % function
function unit_test_project()
pm_filename=getenv('matlab_pm_current_project');
if isempty(pm_filename)
disp('No project is open.');
return;
end
load(pm_filename);
if ~isfield(pm_info, 'test_suite') | isempty(pm_info.test_suite)
disp('No run configuration is selected for batch run.');
return;
end
all_configs = parseConfigs();
if isnumeric(all_configs)
disp('No run configuration in your MATLAB.');
return;
end
% exclude non-existent one.
test_suite = pm_info.test_suite;
for tsi = length(test_suite):-1:1
t = test_suite(tsi);
ok=0;
for cci=1:length(all_configs)
c = all_configs(cci);
% TODO: this portion is version sensitive.
if strcmp(t.id, c.id) %strcmp(t.name,c.name) && strcmp(t.file,c.file) && strcmp(t.id, c.id)
ok=1;
test_suite(tsi) = c; % Copy the most recent configuration.
break;
end
end
if ~ok
test_suite(tsi)=[];
end
end
handles.test_suite = test_suite;
[pm_path,pm_name]=fileparts(pm_filename);
runTestSuite(test_suite,pm_path,pm_name);
end % function
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%
%%%%% Utility functions for pm.m
%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [valid,pm_path,pm_name,ext,versn] = my_fileparts(pm_filename,read_or_write,title_str)
valid = 1;
%% If the pm_filename is empty, get one from the user.
if isempty(pm_filename)
if strcmp(read_or_write,'read')
[FileName,PathName] = uigetfile('*.mat',title_str);
elseif strcmp(read_or_write,'write')
[FileName,PathName] = uiputfile('*.mat',title_str);
end
if FileName==0
valid=0; pm_path=''; pm_name=''; ext=''; versn='';
return;
end
pm_filename = fullfile(PathName,FileName);
end
%% Check the integrity of the pm_filename
[pm_path,pm_name] = fileparts(pm_filename); % extract path and name. (pm_path can be empty and relative)
pm_filename = fullfile(pm_path,[pm_name,'.mat']); % filename with path and .mat extension
if ~exist(pm_filename,'file')
if strcmp(read_or_write,'read') % If we are reading from a project file, and if there is not the file, it is error.
error(['The file, ',pm_filename,', does not exist']);
elseif strcmp(read_or_write,'write')
save(pm_filename,'pm_filename'); % If not, make one.
end
end
%% Get the full path.
% We need full path, not the relative path. Therefore, we need the
% following steps.
if ~isempty(pm_path)
cd(pm_path); % move to the pm_path if it is not the current directory.
end
pm_filename = which([pm_name,'.mat']); % Get the full-path filename.
[pm_path,pm_name,ext,versn] = fileparts(pm_filename); % Extract path and name.
end %function
function myCloseAll(e)
% e is the handle to the editor.
% get the list of opened files.
files = char(e.builtinGetOpenDocumentNames);
% If any document is not saved, generate error.
dirty = 0;
for i=1:size(files,1)
dirty = dirty + e.isDocumentDirty(files(i,:));
if ~exist(files(i,:),'file')
dirty = dirty + 1;
end
end
if dirty > 0
error('Failed to open/save the project. Save m-files first.');
end
e.closeAll;
end % function
function [opened_mfiles, active_mfile] = getEditorInfo(pm_path, makeAllPathRelative)
editor_handle=com.mathworks.mlservices.MLEditorServices;
opened_mfiles = myMakeRelativePath(char(editor_handle.builtinGetOpenDocumentNames),pm_path,[],[],makeAllPathRelative);
active_mfile = myMakeRelativePath(char(editor_handle.builtinGetActiveDocument),pm_path,[],[],makeAllPathRelative);
end % function
function applyEditorInfo(opened_mfiles, active_mfile)
for i=1:length(opened_mfiles)
if exist(opened_mfiles{i},'file')
edit(opened_mfiles{i});
else
warning(['No such file : ', opened_mfiles{i}]);
end
end
% Open active file.
if exist(active_mfile{1}, 'file')
edit(active_mfile{1});
else
warning(['No such file : ', active_mfile{1}]);
end
end % function
function cl = myMakeRelativePath(cl,rp,prefix,ie,makeAllPathRelative)
% This function generates relative path relative to a given root path.
% cl: char file list
% rp: root path
% ie: indices of excluded paths for modification.
% For example, if a file is in 'C:\a\b\c\m.m' and the root path is 'C:\a',
% then it returns 'b\c\m.m'.
%
% change char to cell
if ischar(cl)
k={};
for i=1:size(cl,1)
c = cl(i,:);
for j=length(c):-1:1 % chomp
if isspace(c(j))
c(j)=[];
else
break;
end
end
k{i} = c;
end
cl=k;
cl = myGetCorrectOrder(cl);
end
if ~exist('prefix','var')
prefix='';
end
if ~exist('ie','var')
ie=[];
end
%% Lower the case??
% Here Windows system is assumed. For other systems, change accordingly.
% rp = lower(rp);
rp(rp=='/')='\'; % set the fileseparator to Windows system.
if rp(end)=='\' % remove the fileseparator at the end, if any.
rp(end)=[];
end
im=[]; % indices of modified paths.
rl = length(rp);
for i=1:length(cl)
if any(i==ie)
continue;
end
c = cl{i};
% c=lower(c);
c(c=='/')='\';
if length(c)>rl && strcmp(c(1:rl),rp) % If the path includes root path, then it will be changed to relative path.
cl{i} = [prefix,cl{i}(rl+2:end)];
im=[im,i];
end
end
% do recursively.
ie = [ie,im];
prefix = ['..',filesep,prefix];
[rp,t] = fileparts(rp);
if isempty(t)
return;
end
if makeAllPathRelative
cl = myMakeRelativePath(cl,rp,prefix,ie,makeAllPathRelative);
end
end %function
function cl = myGetCorrectOrder(cl)
%% Hack
% This function is totally a hack, because there is no documented or
% exposed information about this functionality. So it can be highly
% prone to the MATLAB version and future policy. Also it does not
% guarantee the functionality. (FIXIT if it is available in the future.)
f = fullfile(prefdir,'MATLABDesktop.xml');
if ~exist(f,'file')
return;
end
text = fileread(f);
ind=1:length(cl);
for i=1:length(cl)
a=strfind(text,cl{i});
if ~isempty(a) && a(1)>0
ind(i) = a(1);
else
if i==1
ind(i) = -1000;
else
ind(i) = ind(i-1)+1;
end
end
end
[s,o]=sort(ind);
cl=cl(o);
end % function
function config_list = parseConfigs()
% This is also hack.
f = fullfile(prefdir,'run_configurations.m');
if ~exist(f,'file')
config_list = -1;
return;
end
text = fileread(f);
token = '%% @name';
pos = strfind(text,token);
pos(end+1) = length(text)+1;
for i=1:length(pos)-1
% TODO: This portion is sensitive to matlab version.
% Edit accordingly in the future.
% extract information.
% First run configuration.
str = text(pos(i):pos(i+1)-1);
% Extract name
[a,b,c,m] = regexp(str, '%% @name.+','dotexceptnewline');
if m{1}(end)==char(13)
m{1}(end)=[];
end
name = m{1}(10:end);
% or
% name = regexp(str,'(?<=(%% @name\s+))\w+','match');
% Extract filename
[a,b,c,m] = regexp(str, '% @associatedFile.+','dotexceptnewline');
if m{1}(end)==char(13)
m{1}(end)=[];
end
file = m{1}(20:end);
% Extract code portion. b_end_id+3 is the start index of real
% configuration.
[a,b_end_id,c,m] = regexp(str, '% @uniqueId.+','dotexceptnewline');
if m{1}(end)==char(13)
m{1}(end)=[];
end
if ~isempty(regexp(str, '@uniqueId'))
id = m{1}(14:end);
else
config_list=-1;
end
config_list(i).name = name;
config_list(i).file = file;
config_list(i).id = id;
config_list(i).codes= str(b_end_id+4:end); %% Just pick portion that you can see in the run-configuration-editor.
end
end %function
function runTestSuite(test_suite,pm_path,pm_name)
%% Start of testing.
disp(' ');
disp('================================================================');
disp('================================================================');
disp(['Start of testing project [ ',pm_name,' ]']);
disp(['Project Path: ',pm_path]);
for tsi = 1:length(test_suite)
%% Temporary file genearation for each 'run configuration'
t = test_suite(tsi);
fname = fullfile(pm_path,[pm_name,'_test.m']);
fh=fopen(fname,'w');
if fh==-1
disp('Cannot open/write to temporary file for testing: ');
disp([' ',fname]);
return;
end
fprintf(fh,'%s',t.codes);
fclose(fh);
clear([pm_name,'_test'])
try
rehash
catch ME
try
rehash toolbox;
catch ME1
rethrow(ME1);
end
end
%%
disp('==========================');
disp(['Running configuration name: ', t.name]);
disp([' of the file: ', t.file]);
try
%% Run it
disp('<< Output of this configuration ... >>');
ts_handle = str2func([pm_name,'_test']);
ts_handle();
disp('<< Success >>');
catch ME
evaluation_offset = 1;
%% Catch the error.
e = ME;
%% Remove unnecessary stack information
for ti=length(e.stack)-1:-1:1
if strcmp(e.stack(ti).name,'runTestSuite') && strcmp(e.stack(ti+1).name,'unit_test_project')
break;
end
end
stack = e.stack(1:ti-evaluation_offset);
%% Show error information
disp(['>>Stack information:']);
for si=1:length(stack)-1
% Make a link to the position
cs = ['', ...
'Error in ==> <a href="matlab: com.mathworks.mlservices.MLEditorServices.openDocumentToLine(''', ...
stack(si).file, ''',' num2str(stack(si).line),');">',stack(si).name,' at ',num2str(stack(si).line),'</a>', ...
''];
% Show it
disp(' ');
disp(cs);
S=evalc(['dbtype ''',stack(si).file,''' ',num2str(stack(si).line)]);
S(S==char(10))=[]; S(S==char(13))=[]; % Remove newlines.
disp(S);
end
% Finally show the 'run configuration' information.
cs = ['', ...
'Error in run configuration ==> <a href="matlab: com.mathworks.mlservices.MLEditorServices.openDocumentToLine(''', ...
t.file, ''',' num2str(1),');">',t.name,' at ',num2str(stack(end).line),'</a>', ...
''];
disp(' ');
disp(cs);
S=evalc(['dbtype ''',pm_name,'_test.m'' ',num2str(stack(end).line)]);
S(S==char(10))=[]; S(S==char(13))=[]; % Remove newlines.
disp(S);
end
end
disp('==========================');
disp('End of Testing');
a=pwd;
cd(pm_path);
delete([pm_name,'_test*.m']);
cd(a);
end % function
function ok=check_startup_filelist(last_project)
load(last_project);
lp_om = pm_info.opened_mfiles;
cp_om = pm('getEditorInfo',fileparts(last_project), 0);
count = 0;
for i=1:length(lp_om)
if any(ismember(cp_om,lp_om(i)))
count = count+1;
end
end
if count/length(lp_om) >= 0.8
ok=1;
else
ok=0;
disp('More than 20% of opened documents does not match with the saved information.');
disp('There might have been multiple instances of MATLAB running and');
disp('the last instance might have closed unexpectedly.');
disp('Reloading the last properly saved project file.');
end
end
% % %
% % % function pos = strLinePos(str, newLine)
% % % %% This function returns line positions
% % % % This function can be used to divide str using 'newLine' as a delimiter.
% % % % You can use any string in 'newLine'.
% % % if isempty(str) % if there is no string, return empty result.
% % % pos=[];
% % % end
% % %
% % % if ~exist('newLine','var') | isempty(newLine)
% % % f = fullfile(prefdir,'history.m');
% % % if ~exist(f,'file')
% % % warning('Cannot determine system specific newLine characters... Using Windows one...');
% % % newLine = [char(13), char(10)];
% % % else
% % % tmp = fileread(f);
% % % t=strfind(tmp,char(10));
% % % if tmp(t(1)-1) == char(13)
% % % newLine = [char(13), char(10)]; %% NOTE: see Terminator and follow links for more information.
% % % end
% % % end
% % % end
% % %
% % % NLpos = strfind(str,newLine);
% % % sp = [1,NLpos+numel(newLine)]';
% % % ep = [NLpos-1, length(str)]';
% % % pos = [sp,ep];
% % % end %function
% % %
% % %
% % %
% % %
% % %
% % %
% % %
% % %
% % %
% % %
% % % % % % % or find the error position from each file.
% % % % % % % str = fileread(stack(si).file);
% % % % % % % linePos = strLinePos(str);
% % % % % % % err_pos = linePos(stack(si).line,:);
% % % % % % % ec=str(err_pos(1):err_pos(2)); disp([ec]);
% % % % % %
% % % % % % % linePos = strLinePos(codes);
% % % % % % % err_pos = linePos(stack(end).line,:);
% % % % % % % ec=codes(err_pos(1):err_pos(2));
% % % % % % % s=num2str(stack(end).line-1);
% % % % % % % ec = [s,repmat(' ',1,6-length(s))
% % % % % % % disp([ec]);
% % % % % %
% % % % % % % % Check newLine character (Windows vs other OS)
% % % % % % % f = fullfile(prefdir,'run_configurations.m');
% % % % % % % if ~exist(f,'file')
% % % % % % % config_list = -1;
% % % % % % % return;
% % % % % % % end
% % % % % % % tmp = fileread(f);
% % % % % % % t=strfind(tmp,char(10));
% % % % % % % if tmp(t(3)-1) == char(13)
% % % % % % % newLine = [char(13), char(10)]; %% NOTE: see Terminator and follow links for more information.
% % % % % % % end
% % % % % %