classdef MFormatter < handle
% Performs the actual code formatting. Should not be used directly but only by MBeautify.
properties (Access = private)
Configuration;
AllOperators;
DirectiveDirector;
StringMemory;
% Properties used during the formatting
BlockCommentDepth;
IsInBlockComment;
MatrixIndexingOperatorPadding;
CellArrayIndexingOperatorPadding;
end
properties (Access = private, Constant)
TokenStruct = MBeautifier.MFormatter.getTokenStruct();
end
methods
function obj = MFormatter(configuration)
% Creates a new formatter using the passed configuration.
obj.Configuration = configuration;
obj.MatrixIndexingOperatorPadding = configuration.specialRule('MatrixIndexing_ArithmeticOperatorPadding').ValueAsDouble;
obj.CellArrayIndexingOperatorPadding = configuration.specialRule('CellArrayIndexing_ArithmeticOperatorPadding').ValueAsDouble;
% Init run-time members
obj.StringMemory = [];
obj.BlockCommentDepth = 0;
obj.IsInBlockComment = false;
obj.AllOperators = configuration.operatorCharacters();
end
function formattedSource = performFormatting(obj, source)
% Performs formatting on the specified source.
obj.BlockCommentDepth = 0;
obj.IsInBlockComment = false;
obj.DirectiveDirector = MBeautifier.DirectiveDirector();
nMaximalNewLines = obj.Configuration.specialRule('MaximalNewLines').ValueAsDouble;
nSectionPrecedingNewlines = obj.Configuration.specialRule('SectionPrecedingNewlineCount').ValueAsDouble;
formatSectionPrecedingNewlines = nSectionPrecedingNewlines >= 0;
nSectionTrailingNewlines = obj.Configuration.specialRule('SectionTrailingNewlineCount').ValueAsDouble;
formatSectionTrailingNewlines = nSectionTrailingNewlines >= 0;
newLine = MBeautifier.Constants.NewLine;
contTokenStruct = MBeautifier.MFormatter.TokenStruct.ContinueToken;
textArray = regexp(source, newLine, 'split');
replacedTextArray = {};
isInContinousLine = 0;
containerDepth = 0;
contLineArray = cell(0, 2);
isSectionSeparator = false;
isFormattingOff = false;
nNewLinesFound = 0;
for j = 1:numel(textArray)
line = textArray{j};
%% Check for directives if ...
% ... NOT in continous line
% ... NOT in block comment
if ~isInContinousLine && ~obj.IsInBlockComment
directiveChange = obj.DirectiveDirector.updateFromLine(line);
if ~isequal(directiveChange.Type, MBeautifier.DirectiveChangeType.NONE)
switch (lower(directiveChange.DirectiveName))
case 'format'
if isequal(directiveChange.Type, MBeautifier.DirectiveChangeType.REMOVED)
isFormattingOff = false;
elseif isequal(directiveChange.Type, MBeautifier.DirectiveChangeType.ADDED) || isequal(directiveChange.Type, MBeautifier.DirectiveChangeType.CHANGED)
isFormattingOff = numel(directiveChange.Directive.Values) > 0 && strcmpi(directiveChange.Directive.Values{1}, 'off');
end
otherwise
% Ignore
end
end
end
if isFormattingOff
replacedTextArray = [replacedTextArray, line, MBeautifier.Constants.NewLine];
continue
end
%% Process the maximal new-line count
if isempty(strtrim(line))
nNewLinesFound = nNewLinesFound + 1;
if nNewLinesFound > nMaximalNewLines || ...
(formatSectionTrailingNewlines && isSectionSeparator && nNewLinesFound > nSectionTrailingNewlines)
continue;
end
replacedTextArray = [replacedTextArray, MBeautifier.Constants.NewLine];
continue;
else
if isSectionSeparator && formatSectionTrailingNewlines && (nNewLinesFound - nSectionTrailingNewlines < 0)
for i = 1:abs(nNewLinesFound-nSectionTrailingNewlines)
replacedTextArray = [replacedTextArray, MBeautifier.Constants.NewLine];
end
end
nNewLinesFound = 0;
end
%% Determine the position where the line shall be splitted into code and comment
[actCode, actComment, splittingPos, isSectionSeparator] = obj.findComment(line);
if isSectionSeparator && formatSectionPrecedingNewlines
replacedTextArray = MBeautifier.MFormatter.handleTrailingEmptyLines(replacedTextArray, nSectionPrecedingNewlines);
end
%% Check for line continousment (...)
% Continous lines have to be converted into one single code line to perform replacement on it
% The continousment characters have to be replaced by tokens and the comments of the lines must be stored
% After replacement, the continuosment has to be re-created along with the comments.
trimmedCode = strtrim(actCode);
if ~numel(trimmedCode)
actCodeFinal = '';
else
containerDepth = containerDepth + obj.calculateContainerDepthDeltaOfLine(trimmedCode);
% Auto append "..." to the lines of continuous containers
if containerDepth && ~(numel(trimmedCode) >= 3 && strcmp(trimmedCode(end-2:end), '...'))
if strcmp(trimmedCode(end), ',') || strcmp(trimmedCode(end), ';')
actCode = [trimmedCode, ' ...'];
else
actCode = [actCode, '; ...'];
end
end
trimmedCode = strtrim(actCode);
% Line ends with "..."
if (numel(trimmedCode) >= 3 && strcmp(trimmedCode(end-2:end), '...')) ...
|| (isequal(splittingPos, 1) && isInContinousLine)
isInContinousLine = true;
contLineArray{end+1, 1} = actCode;
contLineArray{end, 2} = actComment;
% Step to next line
continue;
else
% End of cont line
if isInContinousLine
isInContinousLine = false;
contLineArray{end+1, 1} = actCode;
contLineArray{end, 2} = actComment;
% Build the line for replacement
replacedLines = '';
for iLine = 1:size(contLineArray, 1) - 1
tempRow = strtrim(contLineArray{iLine, 1});
tempRow = [tempRow(1:end-3), [' ', contTokenStruct.Token, ' ']];
tempRow = regexprep(tempRow, ['\s+', contTokenStruct.Token, '\s+'], [' ', contTokenStruct.Token, ' ']);
replacedLines = [replacedLines, tempRow];
end
replacedLines = [replacedLines, actCode];
% Replace
actCodeFinal = obj.performReplacements(replacedLines);
评论0