有没有一种方法可以一次性修复所有 MATLAB mlint 信息?

11

我继承了一些代码,但作者不喜欢使用分号。是否可能一次性修复所有 mlint 消息(至少自动修复的消息),而不必每次单击并按 ALT+ENTER?


3
请注意,我很感谢现有的答案,但希望找到一个更普遍适用的解决方案。 - Dennis Jaheruddin
最近他们用checkcode替换了mlint。你也可以使用它。 - NKN
3个回答

8

注意: 本答案使用的函数MLINT在MATLAB的新版本中不再推荐使用。更好的选择是CHECKCODE函数,只需将对MLINT的调用替换为对这个新函数的调用即可。


我不知道有没有一般性的方法可以根据MLINT的信息自动修复代码。但是,在你的特定情况下,有一种自动化的方法可以让你添加分号到引发MLINT警告的行上。

首先,我们从这个样例脚本junk.m开始:

a = 1
b = 2;
c = 'a'
d = [1 2 3]
e = 'hello';

第一、三和四行会给你MLINT警告信息“在脚本中,用分号终止语句以抑制输出。”。使用MLINT的函数形式,我们可以找到文件中出现此警告的行。然后,我们可以从文件中读取所有代码行,在警告出现的行末添加分号,然后将代码行写回文件。以下是执行此操作的代码:
%# 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);

现在文件junk.m的每一行末尾都应该有分号。如果需要,您可以将上述代码放入一个函数中,以便可以轻松地在继承的所有文件中运行它。


1
如果您在调用 mlint 时将“-struct”替换为“-id”,则返回的结构包含一个名为“id”的附加字段,该字段是 mlint 消息的短标识符。与消息的完整文本相比,这可能更容易匹配。该结构还包含一个名为“fix”的字段,在我的测试文件中所有警告都为0,我还没有弄清楚它的作用 - 它没有记录在文档中。 - High Performance Mark
@高性能标记:'fix'字段必须是一个较新的功能,因为它在MATLAB R2009a中没有出现。 - gnovice
@HighPerformanceMark,我发现如何使fix字段功能正常:将-fix标志传递给mlint/checkcode!有关详细信息,请参见我的答案 - mbauman

7
为了以一般方式解决所有可用自动修复操作的问题,我们必须诉诸于毫无文档的 Java 方法。实现 mlint(现在是 checkcode)使用 mlintmex(内置函数;名称暗示它是 mex 文件),它只是从 linter 返回 text 输出。没有暴露自动修复;即使行号和列号也被作为纯文本输出。它似乎与 Matlab 安装中的 mlint 二进制文件的输出相同($(matlabroot)/bin/$(arch)/mlint)。
因此,我们必须退回到编辑器本身使用的 Java 实现。注意:以下是针对 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

编辑: 哎呀! 你可以使用-fix标志让mlint二进制文件返回修复结果...并且这也适用于内置的checkcode。虽然这仍未被记录(据我所知),但可能比上面提到的更加健壮:

>> 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):   <~>

当分配给一个结构时,这也揭示了新的fix字段的目的,正如@High Performance Mark@gnovice答案中所指出的那样;当有修复可用时,它似乎为1,当消息是上面的FIX MESSAGE时为2,当消息是CHANGE MESSAGE时为4。
这是一个快速而简单的Matlab函数,可以根据m文件路径返回一个“fixed”字符串。没有错误检查等,它也不会保存文件,因为我不能保证它能工作。您还可以使用matlab.desktop.editor公共(!)API获取活动文档(getActive),并在原地修改文档的getter和setter的Text属性,而不保存它。
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

太棒了,第一个看起来正是我想要的!在赏金到期之前没有机会测试它,但除非有人能超越它,否则它就是属于你的了。 - Dennis Jaheruddin
2
我将Java方法添加到快捷方式中,找到了一个漂亮的工具图标,目前运行顺畅。非常好的解决方案。 - Oleg
@OlegKomarov:小心,同一行上的两个更改可能会相互干扰。我已经反转了迭代顺序,这似乎可以解决大多数情况。但更加健壮的方法是迭代运行 mlint 并逐个修复消息。 - mbauman
我只是想知道这个能否导出到其他的IDE。我喜欢在vim中编辑,可能有一种方法可以做到。不幸的是,我不是一个“vimscript”编辑器,如果有人改进matlab mlint集成脚本以实现类似的功能,那就太好了。如果没有人这样做,也许有一天我会尝试,这个话题将会是很有帮助的。 - Werner
@Werner:是的,您绝对可以调用Mathworks提供的mlint二进制文件并自行解析输出。不过更有趣的是尝试直接链接到动态库libmwmlint.{dll,so,dylib}。快速查看符号让我想知道是否可能(MLINT::mlint_file(char const*), MLINT::mlint_text(char const*, char const*, unsigned int),最有趣/令人望而生畏的是:MLINT::set_output_fun(void (*)(char const*, void*), void*))。 - mbauman
这就是为什么我认为我不是做这个的合适人选,哈哈哈,我不知道在编译库中跟踪函数是否可能,但谁知道我是否足够好去做它呢x)。 - Werner

6

我知道这是一个旧帖子,但最近我需要它,并改进了原始代码,所以如果其他人需要,这里就有了。它查找函数中缺少的";",不仅限于常规脚本,在代码中保留空格,并仅写入有更改的文件。

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);

1
谢谢,欢迎来到SO。即使是旧问题也值得添加。 - Richie Cotton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接