I've i开发者_运维知识库nherited some code where the author had an aversion to semicolons. Is it possible to fix all the mlint messages in one go (at least all the ones with an automatic fix), rather than having to click each one and press ALT+ENTER?
NOTE: This answer uses the function MLINT, which is no longer recommended in newer versions of MATLAB. The newer function CHECKCODE is preferred, and the code below will still work by simply replacing the call to MLINT with a call to this newer function.
I don't know of a way in general to automatically fix code based on MLINT messages. However, in your specific case there is an automated way for you to add semicolons to lines that throw an MLINT warning.
First, let's start with this sample script junk.m
:
a = 1
b = 2;
c = 'a'
d = [1 2 3]
e = 'hello';
The first, third, and fourth lines will give you the MLINT warning message "Terminate statement with semicolon to suppress output (within a script).". Using the functional form of MLINT, we can find the lines in the file where this warning occurs. Then, we can read all the lines of code from the file, add a semicolon to the ends of the lines where the warning occurs, and write the lines of code back to the file. Here's the code to do so:
%# Find the lines where a given mlint warning occurs:
fileName = 'junk.m';
mlintID = 'NOPTS'; %# The ID of the warning
mlintData = mlint(fileName,'-id'); %# Run mlint on the file
index = strcmp({mlintData.id},mlintID); %# Find occurrences of the warnings...
lineNumbers = [mlintData(index).line]; %# ... and their line numbers
%# Read the lines of code from the file:
fid = fopen(fileName,'rt');
linesOfCode = textscan(fid,'%s','Delimiter',char(10)); %# Read each line
fclose(fid);
%# Modify the lines of code:
linesOfCode = linesOfCode{1}; %# Remove the outer cell array encapsulation
linesOfCode(lineNumbers) = strcat(linesOfCode(lineNumbers),';'); %# Add ';'
%# Write the lines of code back to the file:
fid = fopen(fileName,'wt');
fprintf(fid,'%s\n',linesOfCode{1:end-1}); %# Write all but the last line
fprintf(fid,'%s',linesOfCode{end}); %# Write the last line
fclose(fid);
And now the file junk.m
should have semicolons at the end of every line. If you want, you can put the above code in a function so that you can easily run it on every file of your inherited code.
In order to solve this problem in a general fashion for all available autofix actions, we must resort to horribly undocumented java methods. The implementation of mlint
(and now checkcode
) uses mlintmex
(a builtin; not a mexfile as the name suggests), which simply returns the text output from the linter. No autofixes are exposed; even line and column numbers are emitted as plain text. It seems to be the same as the output from the mlint binary within Matlab's installation ($(matlabroot)/bin/$(arch)/mlint
)
So we must fall back to the java implementation used by the editor itself. Beware: here follows terribly undocumented code for R2013a.
%// Get the java component for the active matlab editor
ed = matlab.desktop.editor.getActive().JavaEditor.getTextComponent();
%// Get the java representation of all mlint messages
msgs = com.mathworks.widgets.text.mcode.MLint.getMessages(ed.getText(),ed.getFilename())
%// Loop through all messages and apply the autofix, if it exits
%// Iterate backwards to try to prevent changing the location of subsequent
%// fixes... but two nearby fixes could still mess each other up.
for i = msgs.size-1:-1:0
if msgs.get(i).hasAutoFix()
com.mathworks.widgets.text.mcode.analyzer.CodeAnalyzerUtils.applyAutoFixes(ed,msgs.get(i).getAutoFixChanges);
end
end
EDIT: AHA! You can get the mlint binary to return the fixes with the -fix
flag... and this applies to the builtin checkcode
, too! Still undocumented (as far as I know), but likely much more robust than the above:
>> checkcode(matlab.desktop.editor.getActiveFilename(),'-fix')
L 2 (C 3): Terminate statement with semicolon to suppress output (in functions). (CAN FIX)
----FIX MESSAGE <Add a semicolon.>
----CHANGE MESSAGE L 2 (C 13); L 2 (C 12): <;>
L 30 (C 52-53): Input argument 'in' might be unused. If this is OK, consider replacing it by ~. (CAN FIX)
----FIX MESSAGE <Replace name by ~.>
----CHANGE MESSAGE L 30 (C 52); L 30 (C 53): <~>
When assigning to a structure, this also reveals the purpose of the new fix
field that @High Performance Mark notes in his comment on @gnovice's answer; it appears to be 1 when there is a fix available, 2 when the message is the FIX MESSAGE
above, and 4 when the message is the CHANGE MESSAGE
.
Here's a quick and dirty Matlab function that returns a 'fixed' string given the path to a m-file. No error checking, etc, and it doesn't save the file back as I don't promise that it'll work. You could also use the matlab.desktop.editor
public (!) API to get the active document (getActive
) and use the getter and setter on the Text
property to modify the document in place without saving it.
function str = applyAutoFixes(filepath)
msgs = checkcode(filepath,'-fix');
fid = fopen(filepath,'rt');
iiLine = 1;
lines = cell(0);
line = fgets(fid);
while ischar(line)
lines{iiLine} = line;
iiLine = iiLine+1;
line = fgets(fid);
end
fclose(fid);
pos = [0 cumsum(cellfun('length',lines))];
str = [lines{:}];
fixes = msgs([msgs.fix] == 4);
%// Iterate backwards to try to prevent changing the indexing of 'str'
%// Note that two changes could still conflict with eachother. You could check
%// for this, or iteratively run mlint and fix one problem at a time.
for fix = fliplr(fixes(:)')
%'// fix.column is a 2x2 - not sure what the second column is used for
change_start = pos(fix.line(1)) + fix.column(1,1);
change_end = pos(fix.line(2)) + fix.column(2,1);
if change_start >= change_end
%// Seems to be an insertion
str = [str(1:change_start) fix.message str(change_start+1:end)];
else
%// Seems to be a replacement
str = [str(1:change_start-1) fix.message str(change_end+1:end)];
end
end
I know this is an old post but I just needed this recently and improved a bit on the original code so if anybody else needs it here it is. It looks for missing ";" in functions, not just in regular scripts, maintains the whitespace in code and writes only files that have something changed.
function [] = add_semicolon(fileName)
%# Find the lines where a given mlint warning occurs:
mlintIDinScript = 'NOPTS'; %# The ID of the warning
mlintIDinFunction = 'NOPRT';
mlintData = mlint(fileName,'-id'); %# Run mlint on the file
index = strcmp({mlintData.id},mlintIDinScript) | strcmp({mlintData.id},mlintIDinFunction); %# Find occurrences of the warnings...
lineNumbers = [mlintData(index).line]; %# ... and their line numbers
if isempty(lineNumbers)
return;
end;
%# Read the lines of code from the file:
fid = fopen(fileName,'rt');
%linesOfCode = textscan(fid,'%s', 'Whitespace', '\n\r'); %# Read each line
lineNo = 0;
tline = fgetl(fid);
while ischar(tline)
lineNo = lineNo + 1;
linesOfCode{lineNo} = tline;
tline = fgetl(fid);
end
fclose(fid);
%# Modify the lines of code:
%linesOfCode = linesOfCode{1}; %# Remove the outer cell array encapsulation
linesOfCode(lineNumbers) = strcat(linesOfCode(lineNumbers),';'); %# Add ';'
%# Write the lines of code back to the file:
fim = fopen(fileName,'wt');
fprintf(fim,'%s\n',linesOfCode{1:end-1}); %# Write all but the last line
fprintf(fim,'%s',linesOfCode{end}); %# Write the last line
fclose(fim);
精彩评论