Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replaces MATLAB's smart indent functionality with self written code #75. #76

Merged
merged 5 commits into from
Oct 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions +MBeautifier/MIndenter.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
classdef MIndenter < handle
% Performs code indenting. Should not be used directly but only by
% MBeautify.

properties (Constant)
Delimiters = {' ', '\f', '\n', '\r', '\t', '\v', ...
','};
KeywordsIncrease = {'function', 'classdef', 'properties', ...
'methods', 'if', 'for', 'parfor', 'switch', 'try', 'while'};
KeywordsSandwich = {'else', 'elseif', 'case', 'otherwise', ...
'catch'};
KeywordsDecrease = {'end', 'end;'};
end

properties (Access = private)
Configuration;
end

methods
function obj = MIndenter(configuration)
% Creates a new formatter using the passed configuration.
obj.Configuration = configuration;
end

function indentedSource = performIndenting(obj, source)
% indentation strategy:
% allfunctions (default) = AllFunctionIndent
% nestedfunctions = MixedFunctionIndent (MATLAB default)
% noindent = ClassicFunctionIndent
strategy = obj.Configuration.specialRule('Indentation_Strategy').Value;

% determine indent string
indentationCharacter = obj.Configuration.specialRule('IndentationCharacter').Value;
indentationCount = obj.Configuration.specialRule('IndentationCount').ValueAsDouble;
if strcmpi(indentationCharacter, 'white-space')
indent = ' ';
for i = 2:indentationCount
indent = [' ', indent];
end
elseif strcmpi(indentationCharacter, 'tab')
indent = '\t';
else
warning('MBeautifier:IllegalSetting:IndentationCharacter', 'MBeautifier: The indentation character must be set to "white-space" or "tab". MBeautifier using MATLAB defaults.');
indent = ' ';
end

% TODO
%makeBlankLinesEmpty = Configuration.specialRule('Indentation_TrimBlankLines').ValueAsDouble;

% currently in continuation mode (line before ended with ...)?
continuationMode = 0;
% layer of indentatino (next line)
layerNext = 0;
% this stack keeps track of the keywords
stack = {};

% start indenting
lines = splitlines(source);
for linect = 1:numel(lines)
% layer of indentation (current line)
layer = layerNext;

% remove existing indentation and whitespace
lines{linect} = strtrim(lines{linect});

% split line in words
words = split(lines{linect}, obj.Delimiters);

% ignore empty lines and comments
if (~isempty(lines{linect}) && (lines{linect}(1) ~= '%'))
% find keywords and adjust indent
for wordct = 1:numel(words)
% detect end of line comments
if (strcmp(words{wordct}, '%'))
break;
end

% look for keywords that increase indent
if (sum(strcmp(words{wordct}, obj.KeywordsIncrease)))
layerNext = layerNext + 1;
% push keyword onto stack
stack = [stack, words{wordct}];

% correction for function keywords according to
% configuration
if (strcmp(stack{end}, 'function'))
switch lower(strategy)
case 'nestedfunctions'
if (numel(stack) == 1)
% top level function
layerNext = layerNext - 1;
else
if (strcmp(stack{end-1}, 'function'))
% nested function
layer = layer + 1;
layerNext = layerNext + 1;
else
% class method
% do nothing
end
end
case 'noindent'
layerNext = layerNext - 1;
otherwise
end
end

% correction for switch
if (strcmp(stack{end}, 'switch'))
layerNext = layerNext + 1;
end
end

% look for sandwich keywords
if (sum(strcmp(words{wordct}, obj.KeywordsSandwich)))
if (wordct == 1)
% at the beginning, decrease only current indent
layer = layer - 1;
end
end

% look for end that decreases the indent
if (sum(strcmp(words{wordct}, obj.KeywordsDecrease)))
if (wordct == 1)
% end at the beginning decreases indent of this line
layer = layer - 1;
end
% inline end may alter the indent of the next line
layerNext = layerNext - 1;

% correction for function keywords according to
% configuration
if (strcmp(stack{end}, 'function'))
switch lower(strategy)
case 'nestedfunctions'
if (numel(stack) == 1)
% top level function
layerNext = layerNext + 1;
else
if (strcmp(stack{end-1}, 'function'))
% nested function
layer = layer - 1;
else
% class method
% do nothing
end
end
case 'noindent'
if (wordct == 1)
% end at the beginning decreases indent of this line
layer = layer + 1;
end
layerNext = layerNext + 1;
otherwise
% do nothing
end
end

% correction for switch
if (strcmp(stack{end}, 'switch'))
if (wordct == 1)
% end at the beginning decreases indent of this line
layer = layer - 1;
end
layerNext = layerNext - 1;
end

% pop keyword
stack(end) = [];
end
end

% look for continuation lines
if (strcmp(words{end}, '...'))
if (~continuationMode)
continuationMode = 1;
layerNext = layerNext + 1;
end
else
if (continuationMode)
continuationMode = 0;
layerNext = layerNext - 1;
end
end
end

% add correct indentation
for ict = 1:layer
lines{linect} = [indent, lines{linect}];
end
end
complete = join(lines, newline);
indentedSource = complete{1};
end
end
end
32 changes: 32 additions & 0 deletions MBeautify.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,38 @@
%% Public API

methods (Static = true)

function formatFileNoEditor(file, outFile)
% Formats the file specified in the first argument. If the
% second argument is also specified, the formatted source is
% saved to this file. The input and the output file can be the
% same, in which case the format operation is carried out
% in-place.
%
if ~exist(file, 'file')
return;
end

text = fileread(file);

% Format the code
configuration = MBeautify.getConfiguration();
formatter = MBeautifier.MFormatter(configuration);
text = formatter.performFormatting(text);

% Indent the code
indenter = MBeautifier.MIndenter(configuration);
text = indenter.performIndenting(text);

if (nargin == 1)
outFile = file;
end

% write formatted text to file
fid = fopen(outFile, 'wt');
fprintf(fid, '%s', text);
fclose(fid);
end

function formatFile(file, outFile)
% Formats the file specified in the first argument. The file is opened in the Matlab Editor. If the second
Expand Down
8 changes: 4 additions & 4 deletions resources/settings/MBeautyConfigurationRules.xml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
<ValueTo>:</ValueTo>
</OperatorPaddingRule>
</OperatorPadding>

<KeyworPadding>
<KeyworPaddingRule>
<Keyword>case</Keyword>
Expand Down Expand Up @@ -174,7 +174,7 @@
<RightPadding>1</RightPadding>
</KeyworPaddingRule>
</KeyworPadding>

<SpecialRules>
<SpecialRule>
<Key>MaximalNewLines</Key>
Expand Down Expand Up @@ -238,7 +238,7 @@
</SpecialRule>
<SpecialRule>
<Key>Indentation_Strategy</Key>
<Value>AllFunctions</Value>
<Value>NestedFunctions</Value>
</SpecialRule>
</SpecialRules>
</MBeautifyRuleConfiguration>
</MBeautifyRuleConfiguration>