To indent a MATLAB *.m file,
C-x h or Menu -> Edit -> Select All C-M-\ or Menu -> Execute Command indent-region
When using matlab-ts-mode (and not matlab-mode) the MATLAB indent engine:
- How do I shorten long code lines?
The location of the ellipsis line continuation influences the length of code lines. For example, the arguments of a function are aligned, therefore if you want the arguments close to the left, column, put the ellipsis after the opening parenthesis of the function:
longVariableNameForMatrix1 = myFcn(arg1, ... reallyLongArg2);
v.s.
longVariableNameForMatrix1 = myFcn( ... arg1, ... reallyLongArg2);
- How do I speed up a slow indent?
When there are a lot of language elements, for example a really big matrix with many columns and rows, the indent can be very slow. You can wrap these in
%-indent-mode=minimaland%-indent-mode=fullcomment directives. For example:%-indent-mode=minimal mat1 = [ % <lots of rows and columns> ]; %-indent-mode=full
When
%-indent-mode=minimalis active, only the indent-level whitespace on left is modified as needed during indent. No adjustment of whitespaces between the code elements is performed, which results in faster indent.
Simplicity is good
Years ago, I wrote a proprietary c_indent engine. This simple indent engine worked well and was used for many years on large amounts of code, mostly programmatically generated code. It had no options and indented code quickly. Gofmt is another example of a simple indent engine that is very successful. A lesson leaned from these indent engines is that they work well in nearly all cases.
Complexity hurts
I’ve helped with the deployment of clang-format and other indent engines on a large code base. A lesson learned is that the flexibility of clang-format causes inconsistencies. People want different options and that creates conflicts. Another lesson learned is that not-respecting the newlines in code causes negative reactions where the formatted code is less readable. The newlines are put there for a reason and removing or adding them changes the readability and meaning of the code in some (rare) cases. clang-format is also complex and we’ve run into a number of bugs in it over the years. The problem of clang-format making code less-readable also occurs with other indent engines. For example Linus Torvalds vented over “Completely Crazy Rust Format Checking” [1] [2].
The benefit of these complex indent engines is significant and outweighs the drawbacks. Clang-format
has comment directives // clang-format off and // clang-format on which are liberally used to
work-around the drawbacks.
Design
The design of the MATLAB indent engine is based on simplicity. A number of people familiar with the MATLAB language came up with a set of rules for MATLAB indent:
- Respect existing newlines,
- Adjust the indent-level (the whitespace to the left of the code),
- Standardize language element spacing, aligns consecutive statements, aligns matrices and structs, adds missing commas to matrices, etc.
We choose to have no indent options and make sure the indents works well in all cases. This
eliminates the need for comment directives like %#<format-off> and %<format-on>. By having one
indent standard, people reading code from different projects will see consistency. Consistency helps
with understanding and communication.
An important design consideration is to respect what the author wrote by respecting newlines. Adding newlines or combining newlines can make it harder to read the code. Therefore, as a coding guideline, one should not put too much logic on a given line. Line length should be limited 100 when possible. However, going beyond 100 columns is allowed when it improves readability. For example, a matrix of data where each row is slightly longer than 100 columns is easier to read when compared with the same matrix of data formatted to fit within 100 columns.
In this post, Linus Torvalds makes a good point about the problems caused when code is formatted to fit within a column limit (in his case 80): “Excessive line breaks are BAD. They cause real and every-day problems. …”
For performance considerations, we added %-indent-mode=minimal and %-indent-mode=full comment
directives.
- Indent level of 4 spaces, no TAB characters, unicode, LF line-endings (no CRLF)
The MATLAB indent engine will indent to a level of 4 spaces for each scope or conditional. Tab characters are converted to spaces to ensure that code looks the same in all editors. For example, UNIX (POSIX) uses tab stops of 8 whereas Microsoft uses tab stops of 4. Note, the indent engine retains TAB characters within strings because altering them would change answers.
Example:
function a= foo( b,c ) a= b+... c; end
Indents to:
function a = foo(b, c) a = b + ... c; end
MATLAB supports two types of functions, those without a terminating end-statement and “endless” function that have no terminating end-statement. Endless functions without a terminating end-statement should not be indented. Functions with terminating end-statements should be indented. Examples:
function a = myFunctionWithEnd(b) if b > 0 a = 1 else a = 0 end end
function a = myEndlessFunction(b) if b > 0 a = 1 else a = 0 end
When creating new function m-files, an end-statement is recommended and the editor should insert an end-statement when you type the start of a function. Adding a new function to an existing file should conform to the existing end-statement state.
For example, if I create a myNewFunction.m, containing:
function out = myNewFunction(in)
and then type the Enter (RET) key, the end-statement should be automatically added and the cursor point should be placed at the carrot (^) location:
function out = myNewFunction(in) ^ end
The documentation help comment for a function is not indented. Comments for code are indented at the level of the code. The help comment is the comment immediately following the function statement (no blank lines). Example:
function out = foobar(in) % FOOBAR - this is the H1 line of the help % This is additional help info. % This comment is not indented. % this is a comment for the following code out = 2 * in; end
- Recommended line length 100
Code and comments should fit within 100 columns. A line may exceed 100 characters if it improves code readability.
In general, too much logic on a given line hurts readability. Reading the first part of a line should convey the operation being performed by the line. Adding unrelated concepts on a give line hurts readability. Hence the recommendation that lines are not too long and thus the recommendation for limiting length to 100.
However, lines can exceed 100 columns when readability is improved. Consider the following where the row content causes the column width to be 105. Re-flowing would hurt readability.
mat = [ ... <row-1>; <row-2>; ... <row-N>; ]
As another example of long lines, the code below exceeds 100 characters. To re-flow to fit within 100 columns we’d need to split the Simulink block path, which would hurt readability: using lines
set_param( ... 'model/subsystem1/subsystem2/subsystem3/subsystem4/subsystem5/Variable-Fractional-Delay FIR Filter', ... 'Parameter', 'Value')Re-flowing code is not implemented in the indent engine because newlines help with readability and programmatically adding or removing them can adversely effect the readability of code.
- Use 2-space offset for case labels
The code under switch case or otherwise statements is one condition and hence should have the indent level anchored from the switch statement. More specifically switch-statements and if-statements are semantically equivalent and should have the same indent level. For example, the level of complexity of the following two statements is the same which is clear from the level of indent of the doIt function call.
if condition1 == 1 | switch condition1 doIt | case 1 end | doIt | end
- Cells and arrays use indent level of 2 spaces
Indents cells and array with an inner indent level of 2 spaces for the data. Cells and arrays which are structured data. Since there are no “conditionals” within structured data, we treat the nesting in structured data like labels and indent by 2 spaces. Also, data is aligned with the opening parenthesis or bracket. Example:
myCell = { ... {1, 2, 3}, ... { ... [4, 5; ... 6, 7] ... } ... }
- Standardize language elements spacing
- Use a single space after (but not before) a comma.
- Use a single space on each side of binary operators (such as +), except for member operators.
- Do not use any space between member operators and their operands. For example: a.b
- Do not use any space between a unary operator and its operand. For example: -10
- Language keywords should have a space after them. For example: if (cond)
Example 1:
e=a+b * c/ d-1;
Indents to:
e = a + b * c / d - 1;
Example 2:
function out2 =myFcn(in1,in2) if (in1 >1&& in2 >1) || in2> -10 out1 = in1 *in2; else out1 =0; end end
Indents to:
function out2 = myFcn(in1, in2) if (in1 > 1 && in2 > 1) || in2 > -10 out1 = in1 * in2; else out1 = 0; end end
Example 3 (name = value non-positional arguments):
r = myFunction(Name1=3+3 * 4,Name2=7-1 / 2) function result = myFunction(NameValueArgs) arguments NameValueArgs.Name1 NameValueArgs.Name2 end result = NameValueArgs.Name1* NameValueArgs.Name2; end
Indents to:
r = myFunction(Name1 = 3 + 3 * 4, Name2 = 7 - 1 / 2) function result = myFunction(NameValueArgs) arguments NameValueArgs.Name1 NameValueArgs.Name2 end result = NameValueArgs.Name1 * NameValueArgs.Name2; end
- Function call formats
- On a single line:
[result1, result2] = myFunction(arg1, arg2, arg3)
- On multiple lines, aligning subsequent lines after the opening parenthesis:
[result1, result2] = myFunction(arg1, ... arg2, ... arg3)
- On multiple lines, aligning subsequent lines with 4 additional spaces
[result1, result2] = myFunction( ... arg1, arg2, ... arg3)
This is invalid:
[result1, result2] = myFunction(arg1, ... arg2) % arg2 should be aligned with arg1
- On a single line:
- Function definition formats
- On a single line
function [out1, out2] = myFunction(in1, in2)
- Aligned on multiple lines
function ... [ ... out1, ... comment about out1 out2 ... comment about out2 ] ... = myFunction ... ( ... in1, ... comment about in1 in2 ... comment about in2 ) end
- On a single line
- Expressions
When you have long expressions, be consistent in how you break up the lines and minimize use of newlines. Use newlines to help with readability. Place operators at the end of a line, rather than at the beginning of a line.
Operators should never be at the start of a line in an expression which indicates that the expression continues. For example the && is at the end of the 1st line and not the start of the 2nd line:
if (thisOneThing > thisOtherLongLongLongLongLongLongThing && ... aThirdThing == aFourthLongLongLongLongLongLongThing) % code end
You can use extra newlines when it helps with readability. For example, suppose the following conditions are more readable when on separate lines.
if c > 30 && ... d > 40 % code end
Use parentheses to clarify the precedence of “&&” and “||”, even when not strictly necessary because it is common to forget that “&&” has higher precedence than “||”. For example:
if c > 30 || (a > 10 && b > 20) % code end
Do not overuse parentheses. Don’t add them when they are not needed. Overuse of parentheses can clutter code and reduce its readability. Use of parentheses is a indicator that standard operator precedence rules are not in use, for example, “a = b * (c + d)” indicates to the reader that standard operator precedence is not in use.
As a guideline, use the minimum number of parentheses, except for parenthesis to clarify the precedence of “&&” and “||” or more generally, if in doubt about operator precedence, parenthesize.
Examples:
% Good % Bad: too many parentheses if (c > 30 && d > 40) || e if (((c > 30) && (d > 40)) || e) end end
- Align consecutive statements
Example:
rLength =12354 ; rWidth=2; rArea=rLength *rWidth;
Intents to:
rLength = 12354; rWidth = 2; rArea = rLength * rWidth;
For alignment, the statements must be consecutive. For example, the following contains two sections of consecutive statement alignment:
aReallyLongVariable = 10; bShortVar = 5; c = aReallyLongVariable + bShortVar; c2 = c * 2;
- Align consecutive properties alignment
Example:
classdef myClass properties p1 ( 1,:) {mustBeNumeric, mustBeReal} = [0, 0, 0]; longProperty2 (1, 3) {mustBeNumeric, mustBeReal} = [0, 0, 0]; p3(1,2 ) end end
Indents to:
classdef myClass properties p1 (1,:) {mustBeNumeric, mustBeReal} = [0, 0, 0]; longProperty2 (1,3) {mustBeNumeric, mustBeReal} = [0, 0, 0]; p3 (1,2) end end
- Align consecutive arguments
function myFcn(a, longInputVar, c) arguments a{ mustBeNumeric, mustBeReal} longInputVar { mustBeNumeric,mustBeReal ,mustBeFinite} c{mustBeNumeric} end end
Indents to:
function myFcn(a, longInputVar, c) arguments a {mustBeNumeric, mustBeReal} longInputVar {mustBeNumeric, mustBeReal, mustBeFinite} c {mustBeNumeric} end end
- Align consecutive enumeration members
Example:
classdef TrafficLightColors < double enumeration red (1) green(2) yellow(3) end end
Indents to:
classdef TrafficLightColors < double enumeration red (1) green (2) yellow (3) end end
- Align consecutive trailing comments
Trailing comments of consecutive statements in the same scope are aligned.
Example:
s1 =5 ; % Average speed of the first vehicle in miles per hour timeH1=2;% Time in hours traveled of first vehicle in hours distance1= s1 * timeH1; % Distance we've traveled in miles disp(distance1)% Report how far we've gone
Indents to:
s1 = 5; % Average speed of the first vehicle in miles per hour timeH1 = 2; % Time in hours traveled of first vehicle in hours distance1 = s1 * timeH1; % Distance we've traveled in miles disp(distance1) % Report how far we've gone
Example:
if a % comment about a b = 1; % comment about b cLongVariable = [1.01 2.001]; % comment about cLongVariable end
Indents to:
if a % comment about a b = 1; % comment about b cLongVariable = [1.01, 2.001]; % comment about cLongVariable end
- Commas in arrays (matrices and cells)
The MATLAB indent engine will adds missing commas in arrays.
Example:
mat1 = [123456,789 12234.24, 1.2 3,4];
Indents to:
mat1 = [123456, 789, 12234.24, 1.2, 3, 4];
- Align multi-line matrix columns
Example
mat2 = [1234,234 12234.24 1,2 3.41];
Indents to:
mat2 = [1234, 234, 12234.24 1, 2, 3.41];
- Align fields of multi-line structures
Example:
myStruct = struct( ... 'field1', (2+3)*4, ... 'longField2',"foo",... 'f3',2);
Indents to:
myStruct = struct( ... 'field1', (2 + 3) * 4, ... 'longField2', "foo", ... 'f3', 2);
- Function/classdef doc help should be aligned with the function/classdef keyword
Example:
function b= myFunction(a) % help comment % with multiple lines that is displayed when % you type, at the matlab prompt, % >> help myFunction % this is an internal (non-help) comment for the following code b=a *2; end
Indents to:
function b = myFunction(a) % help comment % with multiple lines that is displayed when % you type, at the matlab prompt, % >> help myFunction % this is an internal (non-help) comment for the following code b = a * 2; end
- Indent must be robust to code with syntax errors.
Syntax errors should not cause the indent to cause major changes. For example, syntax errors in a matrix, shouldn’t cause the matrix to be incorrectly indented (formatted).