2010-07-14 9 views
11

J'ai hérité d'un code où l'auteur avait une aversion pour les points-virgules. Est-il possible de corriger tous les messages mlint en une fois (au moins tous ceux avec un correctif automatique), plutôt que d'avoir à cliquer sur chacun d'entre eux et d'appuyer sur ALT + ENTRER?Y at-il un moyen de résoudre tous les messages MATLAB mlint à la fois?

+3

Notez que j'apprécie les réponses existantes, mais j'espère trouver une solution plus généralement applicable. –

+0

Ils ont récemment remplacé mlint avec checkcode. Vous pouvez l'utiliser aussi bien. – NKN

Répondre

8

REMARQUE: Cette réponse utilise la fonction MLINT, qui est plus recommandé dans les nouvelles versions de Matlab. La nouvelle fonction CHECKCODE est préférée, et le code ci-dessous fonctionnera toujours en remplaçant simplement l'appel à MLINT par un appel à cette nouvelle fonction.


Je ne sais pas d'une manière en général pour fixer automatiquement le code basé sur MLINT messages. Toutefois, dans votre cas spécifique, il existe un moyen automatisé d'ajouter des points-virgules aux lignes qui émettent un avertissement MLINT.

Tout d'abord, commençons par ce script exemple junk.m:

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

Les premier, troisième et quatrième lignes vous donnera le message d'avertissement MLINT « Mettre fin à la déclaration des demicolonne pour supprimer la sortie (dans un script). ". En utilisant la forme fonctionnelle de MLINT, nous pouvons trouver les lignes dans le fichier où cet avertissement se produit. Ensuite, nous pouvons lire toutes les lignes de code à partir du fichier, ajouter un point-virgule aux extrémités des lignes où l'avertissement se produit et écrire les lignes de code dans le fichier. Voici le code pour le faire:

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

Et maintenant le fichier junk.m doit avoir des points-virgules à la fin de chaque ligne. Si vous le souhaitez, vous pouvez placer le code ci-dessus dans une fonction afin de pouvoir l'exécuter facilement sur tous les fichiers de votre code hérité.

+1

Si vous remplacez «-struct» par «-id» dans votre appel à mlint, la structure renvoyée contient un champ supplémentaire appelé «id» qui est un identifiant court pour le message mlint. Cela pourrait être plus facile à comparer que le texte intégral du message. La structure contient également un champ appelé 'fix' qui vaut 0 pour tous les avertissements dans mon fichier de test et je n'ai pas encore compris de quoi il s'agit - il n'est pas documenté. –

+0

@High Performance Mark: Le champ '' fix'' doit être une nouvelle fonctionnalité, car il n'apparaît pas dans MATLAB R2009a. – gnovice

+0

oui, je cours 2010a ici. –

6

Je sais que c'est un vieux message mais j'ai juste besoin de cela récemment et amélioré un peu sur le code original, donc si quelqu'un d'autre a besoin ici, c'est. Il cherche manquant ";" dans les fonctions, pas seulement dans les scripts ordinaires, conserve les espaces dans le code et écrit uniquement les fichiers qui ont quelque chose de changé.

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

Merci, et bienvenue à SO. Même les anciennes questions valent la peine d'être ajoutées. –

7

Afin de résoudre ce problème d'une façon générale pour toutes les actions de correction automatique disponibles, nous devons recourir à des méthodes java horriblement en situation irrégulière. La mise en œuvre de mlint (et maintenant checkcode) utilise mlintmex (un builtin, pas un mexfile comme son nom l'indique), qui renvoie simplement le texte sortie du linter. Aucun autofixes sont exposés; même les numéros de ligne et de colonne sont émis en texte brut. Il semble que ce soit la même chose que la sortie du binaire mlint dans l'installation de Matlab ($(matlabroot)/bin/$(arch)/mlint)

Nous devons donc revenir à l'implémentation Java utilisée par l'éditeur lui-même. Attention: voici un code terriblement non documenté pour 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! Vous pouvez obtenir le binary mlint pour renvoyer les correctifs avec le drapeau -fix ... et ceci s'applique également au checkcode intégré!Toujours sans-papiers (pour autant que je sache), mais probablement beaucoup plus robuste que ce qui précède:

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

Lors de l'attribution à une structure, cela révèle aussi le but du nouveau champ fix que @High Performance Mark notes dans son commentaire sur @gnovice de answer; il semble être 1 lorsqu'il y a un correctif disponible, 2 lorsque le message est le FIX MESSAGE ci-dessus, et 4 lorsque le message est le CHANGE MESSAGE.

Voici une fonction Matlab rapide et sale qui renvoie une chaîne 'fixe' en fonction du chemin vers un fichier m. Aucune vérification d'erreur, etc, et il ne sauvegarde pas le fichier car je ne promets pas que ça va fonctionner. Vous pouvez également utiliser l'API publique matlab.desktop.editor (!) Pour obtenir le document actif (getActive) et utiliser le getter et le setter sur la propriété Text pour modifier le document en place sans l'enregistrer.

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 
+0

Incroyable, le premier semble être exactement ce que je cherchais! Je n'aurai pas l'occasion de le tester avant l'expiration de la prime, mais à moins que quelqu'un ne puisse le faire, il viendra à votre rencontre. –

+2

J'ai ajouté l'appraoch de Java à un raccourci, j'ai trouvé une belle icône d'outil et ça marche bien jusqu'à maintenant. Excellente solution – Oleg

+0

@OlegKomarov: Attention, deux changements sur la même ligne peuvent gâcher l'un l'autre. J'ai inversé l'ordre d'itération, ce qui semble résoudre la plupart des cas. Plus robuste, cependant, serait d'exécuter itérativement mlint et de corriger un message à la fois. –