src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleFixer.py

branch
eric7
changeset 10437
2f70ca07f0af
parent 10069
435cc5875135
child 10439
21c28b0f9e41
equal deleted inserted replaced
10436:f6881d10e995 10437:2f70ca07f0af
266 266
267 def saveFile(self, encoding): 267 def saveFile(self, encoding):
268 """ 268 """
269 Public method to save the modified file. 269 Public method to save the modified file.
270 270
271 @param encoding encoding of the source file (string) 271 @param encoding encoding of the source file
272 @return error message on failure (tuple of str) 272 @type str
273 @return error message on failure
274 @rtype tuple of (str, [str])
273 """ 275 """
274 if not self.__modified: 276 if not self.__modified:
275 # no need to write 277 # no need to write
276 return None 278 return None
277 279
303 305
304 def __codeMatch(self, code): 306 def __codeMatch(self, code):
305 """ 307 """
306 Private method to check, if the code should be fixed. 308 Private method to check, if the code should be fixed.
307 309
308 @param code to check (string) 310 @param code to check
309 @return flag indicating it should be fixed (boolean) 311 @type str
312 @return flag indicating it should be fixed
313 @rtype bool
310 """ 314 """
311 315
312 def mutualStartswith(a, b): 316 def mutualStartswith(a, b):
313 """ 317 """
314 Local helper method to compare the beginnings of two strings 318 Local helper method to compare the beginnings of two strings
315 against each other. 319 against each other.
316 320
317 @return flag indicating that one string starts with the other 321 @return flag indicating that one string starts with the other
318 (boolean) 322 @rtype bool
319 """ 323 """
320 return b.startswith(a) or a.startswith(b) 324 return b.startswith(a) or a.startswith(b)
321 325
322 if self.__noFixCodes and any( 326 if self.__noFixCodes and any(
323 mutualStartswith(code.lower(), noFixCode.lower()) 327 mutualStartswith(code.lower(), noFixCode.lower())
365 def finalize(self): 369 def finalize(self):
366 """ 370 """
367 Public method to apply all deferred fixes. 371 Public method to apply all deferred fixes.
368 372
369 @return dictionary containing the fix results 373 @return dictionary containing the fix results
374 @rtype dict
370 """ 375 """
371 results = {} 376 results = {}
372 377
373 # step 1: do fixes operating on logical lines first 378 # step 1: do fixes operating on logical lines first
374 for id_, code, line, pos in self.__stackLogical: 379 for id_, code, line, pos in self.__stackLogical:
390 395
391 def __getID(self): 396 def __getID(self):
392 """ 397 """
393 Private method to get the ID for a deferred fix. 398 Private method to get the ID for a deferred fix.
394 399
395 @return ID for a deferred fix (integer) 400 @return ID for a deferred fix
401 @rtype int
396 """ 402 """
397 self.__lastID += 1 403 self.__lastID += 1
398 return self.__lastID 404 return self.__lastID
399 405
400 def __findLogical(self): 406 def __findLogical(self):
402 Private method to extract the index of all the starts and ends of 408 Private method to extract the index of all the starts and ends of
403 lines. 409 lines.
404 410
405 @return tuple containing two lists of integer with start and end tuples 411 @return tuple containing two lists of integer with start and end tuples
406 of lines 412 of lines
413 @rtype tuple of ([int, int], [int, int])
407 """ 414 """
408 logical_start = [] 415 logical_start = []
409 logical_end = [] 416 logical_end = []
410 last_newline = True 417 last_newline = True
411 sio = StringIO("".join(self.__source)) 418 sio = StringIO("".join(self.__source))
436 def __getLogical(self, line, pos): 443 def __getLogical(self, line, pos):
437 """ 444 """
438 Private method to get the logical line corresponding to the given 445 Private method to get the logical line corresponding to the given
439 position. 446 position.
440 447
441 @param line line number of the issue (integer) 448 @param line line number of the issue
442 @param pos position inside line (integer) 449 @type int
450 @param pos position inside line
451 @type int
443 @return tuple of a tuple of two integers giving the start of the 452 @return tuple of a tuple of two integers giving the start of the
444 logical line, another tuple of two integers giving the end 453 logical line, another tuple of two integers giving the end
445 of the logical line and a list of strings with the original 454 of the logical line and a list of strings with the original
446 source lines 455 source lines
456 @rtype tuple of ((int, int), (int, int), [str])
447 """ 457 """
448 try: 458 try:
449 (logical_start, logical_end) = self.__findLogical() 459 (logical_start, logical_end) = self.__findLogical()
450 except (SyntaxError, tokenize.TokenError): 460 except (SyntaxError, tokenize.TokenError):
451 return None 461 return None
467 477
468 def __getIndentWord(self): 478 def __getIndentWord(self):
469 """ 479 """
470 Private method to determine the indentation type. 480 Private method to determine the indentation type.
471 481
472 @return string to be used for an indentation (string) 482 @return string to be used for an indentation
483 @rtype str
473 """ 484 """
474 sio = StringIO("".join(self.__source)) 485 sio = StringIO("".join(self.__source))
475 indentWord = " " # default in case of failure 486 indentWord = " " # default in case of failure
476 with contextlib.suppress(SyntaxError, tokenize.TokenError): 487 with contextlib.suppress(SyntaxError, tokenize.TokenError):
477 for token in tokenize.generate_tokens(sio.readline): 488 for token in tokenize.generate_tokens(sio.readline):
482 493
483 def __getIndent(self, line): 494 def __getIndent(self, line):
484 """ 495 """
485 Private method to get the indentation string. 496 Private method to get the indentation string.
486 497
487 @param line line to determine the indentation string from (string) 498 @param line line to determine the indentation string from
488 @return indentation string (string) 499 @type str
500 @return indentation string
501 @rtype str
489 """ 502 """
490 return line.replace(line.lstrip(), "") 503 return line.replace(line.lstrip(), "")
491 504
492 def __multilineStringLines(self): 505 def __multilineStringLines(self):
493 """ 506 """
494 Private method to determine the line numbers that are within multi line 507 Private method to determine the line numbers that are within multi line
495 strings and these which are part of a documentation string. 508 strings and these which are part of a documentation string.
496 509
497 @return tuple of a set of line numbers belonging to a multi line 510 @return tuple of a set of line numbers belonging to a multi line
498 string and a set of line numbers belonging to a multi line 511 string and a set of line numbers belonging to a multi line
499 documentation string (tuple of two set of integer) 512 documentation string
513 @rtype tuple of (set of int, set of int)
500 """ 514 """
501 if self.__multiLineNumbers is None: 515 if self.__multiLineNumbers is None:
502 source = "".join(self.__source) 516 source = "".join(self.__source)
503 sio = StringIO(source) 517 sio = StringIO(source)
504 self.__multiLineNumbers = set() 518 self.__multiLineNumbers = set()
524 """ 538 """
525 Private method to fix a badly indented line. 539 Private method to fix a badly indented line.
526 540
527 This is done by adding or removing from its initial indent only. 541 This is done by adding or removing from its initial indent only.
528 542
529 @param line line number of the issue (integer) 543 @param line line number of the issue
530 @param pos position inside line (integer) 544 @type int
545 @param pos position inside line
546 @type int
531 @param logical logical line structure 547 @param logical logical line structure
532 @return flag indicating a change was done (boolean) 548 @type str
549 @return flag indicating a change was done
550 @rtype bool
533 @exception ValueError raised to indicate a bad 'logical' parameter 551 @exception ValueError raised to indicate a bad 'logical' parameter
534 """ 552 """
535 if not logical: 553 if not logical:
536 raise ValueError("Bad value for 'logical' parameter.") 554 raise ValueError("Bad value for 'logical' parameter.")
537 555
568 586
569 def __fixWhitespace(self, line, offset, replacement): 587 def __fixWhitespace(self, line, offset, replacement):
570 """ 588 """
571 Private method to correct whitespace at the given offset. 589 Private method to correct whitespace at the given offset.
572 590
573 @param line line to be corrected (string) 591 @param line line to be corrected
574 @param offset offset within line (integer) 592 @type str
575 @param replacement replacement string (string) 593 @param offset offset within line
594 @type int
595 @param replacement replacement string
596 @type str
576 @return corrected line 597 @return corrected line
598 @rtype str
577 """ 599 """
578 left = line[:offset].rstrip(" \t") 600 left = line[:offset].rstrip(" \t")
579 right = line[offset:].lstrip(" \t") 601 right = line[offset:].lstrip(" \t")
580 if right.startswith("#"): 602 if right.startswith("#"):
581 return line 603 return line
2101 def __init__(self, sourceLines): 2123 def __init__(self, sourceLines):
2102 """ 2124 """
2103 Constructor 2125 Constructor
2104 2126
2105 @param sourceLines list of source lines including eol marker 2127 @param sourceLines list of source lines including eol marker
2106 (list of string) 2128 @type list of str
2107 """ 2129 """
2108 # Raw file lines. 2130 # Raw file lines.
2109 self.raw = sourceLines 2131 self.raw = sourceLines
2110 self.after = [] 2132 self.after = []
2111 2133
2124 2146
2125 def run(self): 2147 def run(self):
2126 """ 2148 """
2127 Public method to run the re-indenter. 2149 Public method to run the re-indenter.
2128 2150
2129 @return flag indicating that a change was done (boolean) 2151 @return flag indicating that a change was done
2152 @rtype bool
2130 """ 2153 """
2131 try: 2154 try:
2132 stats = self.__genStats(tokenize.generate_tokens(self.getline)) 2155 stats = self.__genStats(tokenize.generate_tokens(self.getline))
2133 except (SyntaxError, tokenize.TokenError): 2156 except (SyntaxError, tokenize.TokenError):
2134 return False 2157 return False
2202 2225
2203 def fixedLine(self, line): 2226 def fixedLine(self, line):
2204 """ 2227 """
2205 Public method to get a fixed line. 2228 Public method to get a fixed line.
2206 2229
2207 @param line number of the line to retrieve (integer) 2230 @param line number of the line to retrieve
2208 @return fixed line (string) 2231 @type int
2232 @return fixed line
2233 @rtype str
2209 """ 2234 """
2210 if line < len(self.after): 2235 if line < len(self.after):
2211 return self.after[line] 2236 return self.after[line]
2212 2237
2213 return "" 2238 return ""
2214 2239
2215 def getline(self): 2240 def getline(self):
2216 """ 2241 """
2217 Public method to get a line of text for tokenize. 2242 Public method to get a line of text for tokenize.
2218 2243
2219 @return line of text (string) 2244 @return line of text
2245 @rtype str
2220 """ 2246 """
2221 if self.index >= len(self.lines): 2247 if self.index >= len(self.lines):
2222 line = "" 2248 line = ""
2223 else: 2249 else:
2224 line = self.lines[self.index] 2250 line = self.lines[self.index]
2228 def __genStats(self, tokens): 2254 def __genStats(self, tokens):
2229 """ 2255 """
2230 Private method to generate the re-indent statistics. 2256 Private method to generate the re-indent statistics.
2231 2257
2232 @param tokens tokens generator (tokenize._tokenize) 2258 @param tokens tokens generator (tokenize._tokenize)
2259 @type function
2233 @return reference to the generated statistics 2260 @return reference to the generated statistics
2261 @rtype dict
2234 """ 2262 """
2235 find_stmt = True # next token begins a fresh stmt? 2263 find_stmt = True # next token begins a fresh stmt?
2236 level = 0 # current indent level 2264 level = 0 # current indent level
2237 stats = [] 2265 stats = []
2238 2266
2276 2304
2277 def __getlspace(self, line): 2305 def __getlspace(self, line):
2278 """ 2306 """
2279 Private method to count number of leading blanks. 2307 Private method to count number of leading blanks.
2280 2308
2281 @param line line to check (string) 2309 @param line line to check
2282 @return number of leading blanks (integer) 2310 @type str
2311 @return number of leading blanks
2312 @rtype int
2283 """ 2313 """
2284 i = 0 2314 i = 0
2285 n = len(line) 2315 n = len(line)
2286 while i < n and line[i] == " ": 2316 while i < n and line[i] == " ":
2287 i += 1 2317 i += 1
2309 def __init__(self, physical_lines): 2339 def __init__(self, physical_lines):
2310 """ 2340 """
2311 Constructor 2341 Constructor
2312 2342
2313 @param physical_lines list of physical lines to operate on 2343 @param physical_lines list of physical lines to operate on
2314 (list of strings) 2344 @type list of str
2315 """ 2345 """
2316 self.lines = physical_lines 2346 self.lines = physical_lines
2317 self.tokens = [] 2347 self.tokens = []
2318 self.rel_indent = None 2348 self.rel_indent = None
2319 sio = StringIO("".join(physical_lines)) 2349 sio = StringIO("".join(physical_lines))
2328 def __buildTokensLogical(self, tokens): 2358 def __buildTokensLogical(self, tokens):
2329 """ 2359 """
2330 Private method to build a logical line from a list of tokens. 2360 Private method to build a logical line from a list of tokens.
2331 2361
2332 @param tokens list of tokens as generated by tokenize.generate_tokens 2362 @param tokens list of tokens as generated by tokenize.generate_tokens
2333 @return logical line (string) 2363 @type list of Token
2364 @return logical line
2365 @rtype str
2334 """ 2366 """
2335 # from pycodestyle.py with minor modifications 2367 # from pycodestyle.py with minor modifications
2336 logical = [] 2368 logical = []
2337 previous = None 2369 previous = None
2338 for t in tokens: 2370 for t in tokens:
2360 """ 2392 """
2361 Public method to replicate logic in pycodestyle.py, to know what level 2393 Public method to replicate logic in pycodestyle.py, to know what level
2362 to indent things to. 2394 to indent things to.
2363 2395
2364 @return list of lists, where each list represents valid indent levels 2396 @return list of lists, where each list represents valid indent levels
2365 for the line in question, relative from the initial indent. However, 2397 for the line in question, relative from the initial indent. However,
2366 the first entry is the indent level which was expected. 2398 the first entry is the indent level which was expected.
2399 @rtype list of list
2367 """ 2400 """
2368 # What follows is an adjusted version of 2401 # What follows is an adjusted version of
2369 # pycodestyle.py:continuation_line_indentation. All of the comments 2402 # pycodestyle.py:continuation_line_indentation. All of the comments
2370 # have been stripped and the 'yield' statements replaced with 'pass'. 2403 # have been stripped and the 'yield' statements replaced with 'pass'.
2371 if not self.tokens: 2404 if not self.tokens:
2537 isDocString=False, 2570 isDocString=False,
2538 ): 2571 ):
2539 """ 2572 """
2540 Constructor 2573 Constructor
2541 2574
2542 @param curLine text to work on (string) 2575 @param curLine text to work on
2543 @param prevLine line before the text to work on (string) 2576 @type str
2544 @param nextLine line after the text to work on (string) 2577 @param prevLine line before the text to work on
2545 @param maxLength maximum allowed line length (integer) 2578 @type str
2546 @param eol eond-of-line marker (string) 2579 @param nextLine line after the text to work on
2547 @param indentWord string used for indentation (string) 2580 @type str
2581 @param maxLength maximum allowed line length
2582 @type int
2583 @param eol eond-of-line marker
2584 @type str
2585 @param indentWord string used for indentation
2586 @type str
2548 @param isDocString flag indicating that the line belongs to 2587 @param isDocString flag indicating that the line belongs to
2549 a documentation string (boolean) 2588 a documentation string
2589 @type bool
2550 """ 2590 """
2551 self.__text = curLine 2591 self.__text = curLine
2552 self.__prevText = prevLine 2592 self.__prevText = prevLine
2553 self.__nextText = nextLine 2593 self.__nextText = nextLine
2554 self.__maxLength = maxLength 2594 self.__maxLength = maxLength
2559 def shorten(self): 2599 def shorten(self):
2560 """ 2600 """
2561 Public method to shorten the line wrapped by the class instance. 2601 Public method to shorten the line wrapped by the class instance.
2562 2602
2563 @return tuple of a flag indicating successful shortening, the 2603 @return tuple of a flag indicating successful shortening, the
2564 shortened line and the changed next line (boolean, string, string) 2604 shortened line and the changed next line
2605 @rtype tuple of (bool, str, str)
2565 """ 2606 """
2566 # 1. check for comment 2607 # 1. check for comment
2567 if self.__text.lstrip().startswith("#"): 2608 if self.__text.lstrip().startswith("#"):
2568 lastComment = True 2609 lastComment = True
2569 if self.__nextText.lstrip().startswith("#"): 2610 if self.__nextText.lstrip().startswith("#"):
2720 def __shortenComment(self, isLast): 2761 def __shortenComment(self, isLast):
2721 """ 2762 """
2722 Private method to shorten a comment line. 2763 Private method to shorten a comment line.
2723 2764
2724 @param isLast flag indicating, that the line is the last comment line 2765 @param isLast flag indicating, that the line is the last comment line
2725 (boolean) 2766 @type bool
2726 @return shortened comment line (string) 2767 @return shortened comment line
2768 @rtype str
2727 """ 2769 """
2728 if len(self.__text) <= self.__maxLength: 2770 if len(self.__text) <= self.__maxLength:
2729 return self.__text 2771 return self.__text
2730 2772
2731 newText = self.__text.rstrip() 2773 newText = self.__text.rstrip()
2757 def __breakMultiline(self): 2799 def __breakMultiline(self):
2758 """ 2800 """
2759 Private method to break multi line strings. 2801 Private method to break multi line strings.
2760 2802
2761 @return tuple of the shortened line and the changed next line 2803 @return tuple of the shortened line and the changed next line
2762 (string, string) 2804 @rtype tuple of (str, str)
2763 """ 2805 """
2764 indentation = self.__getIndent(self.__text) 2806 indentation = self.__getIndent(self.__text)
2765 2807
2766 # Handle special case. 2808 # Handle special case.
2767 for symbol in "([{": 2809 for symbol in "([{":
2819 def __isProbablyInsideStringOrComment(self, line, index): 2861 def __isProbablyInsideStringOrComment(self, line, index):
2820 """ 2862 """
2821 Private method to check, if the given string might be inside a string 2863 Private method to check, if the given string might be inside a string
2822 or comment. 2864 or comment.
2823 2865
2824 @param line line to check (string) 2866 @param line line to check
2825 @param index position inside line to check (integer) 2867 @type str
2868 @param index position inside line to check
2869 @type int
2826 @return flag indicating the possibility of being inside a string 2870 @return flag indicating the possibility of being inside a string
2827 or comment 2871 or comment
2872 @rtype bool
2828 """ 2873 """
2829 # Check against being in a string. 2874 # Check against being in a string.
2830 for quote in ['"', "'"]: 2875 for quote in ['"', "'"]:
2831 pos = line.find(quote) 2876 pos = line.find(quote)
2832 if pos != -1 and pos <= index: 2877 if pos != -1 and pos <= index:
2842 def __shortenLine(self, tokens, source, indent): 2887 def __shortenLine(self, tokens, source, indent):
2843 """ 2888 """
2844 Private method to shorten a line of code at an operator. 2889 Private method to shorten a line of code at an operator.
2845 2890
2846 @param tokens tokens of the line as generated by tokenize 2891 @param tokens tokens of the line as generated by tokenize
2847 (list of token) 2892 @type list of Token
2848 @param source code string to work at (string) 2893 @param source code string to work at
2849 @param indent indentation string of the code line (string) 2894 @type str
2850 @return list of candidates (list of string) 2895 @param indent indentation string of the code line
2896 @type str
2897 @return list of candidates
2898 @rtype list of str
2851 """ 2899 """
2852 candidates = [] 2900 candidates = []
2853 2901
2854 for tkn in tokens: 2902 for tkn in tokens:
2855 tokenType = tkn[0] 2903 tokenType = tkn[0]
2909 def __normalizeMultiline(self, text): 2957 def __normalizeMultiline(self, text):
2910 """ 2958 """
2911 Private method to remove multiline-related code that will cause syntax 2959 Private method to remove multiline-related code that will cause syntax
2912 error. 2960 error.
2913 2961
2914 @param text code line to work on (string) 2962 @param text code line to work on
2915 @return normalized code line (string) 2963 @type str
2964 @return normalized code line
2965 @rtype str
2916 """ 2966 """
2917 for quote in "'\"": 2967 for quote in "'\"":
2918 dictPattern = r"^{q}[^{q}]*{q} *: *".format(q=quote) 2968 dictPattern = r"^{q}[^{q}]*{q} *: *".format(q=quote)
2919 if re.match(dictPattern, text): 2969 if re.match(dictPattern, text):
2920 if not text.strip().endswith("}"): 2970 if not text.strip().endswith("}"):
2931 2981
2932 def __lineShorteningRank(self, candidate): 2982 def __lineShorteningRank(self, candidate):
2933 """ 2983 """
2934 Private method to rank a candidate. 2984 Private method to rank a candidate.
2935 2985
2936 @param candidate candidate line to rank (string) 2986 @param candidate candidate line to rank
2937 @return rank of the candidate (integer) 2987 @type str
2988 @return rank of the candidate
2989 @rtype int
2938 """ 2990 """
2939 rank = 0 2991 rank = 0
2940 if candidate.strip(): 2992 if candidate.strip():
2941 if candidate == self.__text: 2993 if candidate == self.__text:
2942 # give the original a disadvantage 2994 # give the original a disadvantage
2994 def __countUnbalancedBrackets(self, line): 3046 def __countUnbalancedBrackets(self, line):
2995 """ 3047 """
2996 Private method to determine the number of unmatched open/close 3048 Private method to determine the number of unmatched open/close
2997 brackets. 3049 brackets.
2998 3050
2999 @param line line to work at (string) 3051 @param line line to work at
3000 @return number of unmatched open/close brackets (integer) 3052 @type str
3053 @return number of unmatched open/close brackets
3054 @rtype int
3001 """ 3055 """
3002 count = 0 3056 count = 0
3003 for opening, closing in ["()", "[]", "{}"]: 3057 for opening, closing in ["()", "[]", "{}"]:
3004 # __IGNORE_WARNING_M613__ 3058 # __IGNORE_WARNING_M613__
3005 count += abs(line.count(opening) - line.count(closing)) 3059 count += abs(line.count(opening) - line.count(closing))
3008 3062
3009 def __getIndent(self, line): 3063 def __getIndent(self, line):
3010 """ 3064 """
3011 Private method to get the indentation string. 3065 Private method to get the indentation string.
3012 3066
3013 @param line line to determine the indentation string from (string) 3067 @param line line to determine the indentation string from
3014 @return indentation string (string) 3068 @type str
3069 @return indentation string
3070 @rtype str
3015 """ 3071 """
3016 # copied from CodeStyleFixer 3072 # copied from CodeStyleFixer
3017 return line.replace(line.lstrip(), "") 3073 return line.replace(line.lstrip(), "")
3018 3074
3019 def __checkSyntax(self, code): 3075 def __checkSyntax(self, code):
3020 """ 3076 """
3021 Private method to check the syntax of the given code fragment. 3077 Private method to check the syntax of the given code fragment.
3022 3078
3023 @param code code fragment to check (string) 3079 @param code code fragment to check
3024 @return flag indicating syntax is ok (boolean) 3080 @type str
3081 @return flag indicating syntax is ok
3082 @rtype bool
3025 """ 3083 """
3026 code = code.replace("\r\n", "\n").replace("\r", "\n") 3084 code = code.replace("\r\n", "\n").replace("\r", "\n")
3027 try: 3085 try:
3028 return compile(code, "<string>", "exec") 3086 return compile(code, "<string>", "exec")
3029 except (SyntaxError, TypeError, UnicodeDecodeError): 3087 except (SyntaxError, TypeError, UnicodeDecodeError):

eric ide

mercurial