1
1
from abc import ABCMeta , abstractmethod
2
- from typing import Dict , List
2
+ from typing import Dict , List , Tuple
3
3
4
4
from . import violation
5
5
from .violation import Violation
11
11
class Checker (metaclass = ABCMeta ):
12
12
@staticmethod
13
13
@abstractmethod
14
- def check (tree : SyntaxTree , config : ConfigLoader ) -> List [str ]:
14
+ def check (tree : SyntaxTree , config : ConfigLoader ) -> List [Violation ]:
15
15
pass
16
16
17
17
@@ -66,7 +66,7 @@ def _check(node: Node, keyword_style: str) -> List[Violation]:
66
66
continue
67
67
68
68
word : str = token .word
69
- expected : str = KeywordStyleChecker ._get_expected (word , keyword_style )
69
+ expected : str = KeywordStyleChecker .get_expected (word , keyword_style )
70
70
if word != expected :
71
71
params = {'index' : idx , 'actual' : word , 'expected' : expected }
72
72
v = violation .KeywordStyleViolation (leaf , keyword_style , ** params )
@@ -78,7 +78,7 @@ def _check(node: Node, keyword_style: str) -> List[Violation]:
78
78
return violation_list
79
79
80
80
@staticmethod
81
- def _get_expected (keyword : str , keyword_style : str ) -> str :
81
+ def get_expected (keyword : str , keyword_style : str ) -> str :
82
82
expected : str = keyword
83
83
84
84
if keyword_style == 'lower' :
@@ -133,7 +133,7 @@ def _check_position(node: Node, comma_position: str) -> List[Violation]:
133
133
for idx in comma_indexes :
134
134
# If a comma is in brackets, it is appropriate not to break a line at comma.
135
135
# So determines that by counting left- and right- brackets at left-right-side.
136
- is_open_bracket = 0 < (tokens [0 : idx ].count (lb ) - tokens [0 : idx ].count (rb ))
136
+ is_open_bracket = 0 < (tokens [0 :idx ].count (lb ) - tokens [0 :idx ].count (rb ))
137
137
is_close_bracket = 0 < (tokens [idx + 1 :].count (rb ) - tokens [idx + 1 :].count (lb ))
138
138
139
139
if not is_open_bracket or not is_close_bracket :
@@ -309,7 +309,7 @@ def check(tree: SyntaxTree, config: ConfigLoader) -> List[Violation]:
309
309
expected_list = {}
310
310
for k , vs in expected_kvs .items ():
311
311
expected_list [k ] = ' ' .join ([
312
- KeywordStyleChecker ._get_expected (v , keyword_style ) for v in vs ])
312
+ KeywordStyleChecker .get_expected (v , keyword_style ) for v in vs ])
313
313
314
314
result .extend (JoinChecker ._check_context (tree .root , expected_list ))
315
315
@@ -323,13 +323,15 @@ def _check_table_name(node: Node) -> List[Violation]:
323
323
for leaf in node .leaves :
324
324
for idx , token in enumerate (leaf .contents ):
325
325
# ignores except join
326
- if token .kind != Token . KEYWORD or token . word .upper () != 'JOIN' :
326
+ if token .word .upper () != 'JOIN' :
327
327
continue
328
328
329
329
# ignores the token next to 'JOIN' is identifier which may be table.
330
- if idx < len (leaf .contents )- 1 and leaf .contents [idx + 1 ].kind == Token .IDENTIFIER :
330
+ if idx <= len (leaf .contents )- 2 and leaf .contents [idx + 2 ].kind == Token .IDENTIFIER :
331
331
continue
332
332
333
+ # TODO: Checks below
334
+ # TODO: SubQueries will become violation in the future.
333
335
"""
334
336
Ignores the token next to 'Join' is 'Select' (maybe SubQuery)
335
337
Examples:
@@ -344,10 +346,6 @@ def _check_table_name(node: Node) -> List[Violation]:
344
346
Join (Select id From y)
345
347
------
346
348
"""
347
- # TODO: SubQueries will be violation in the future.
348
- if idx < len (leaf .contents )- 2 and \
349
- ('SELECT' in [leaf .contents [idx + 1 ].word .upper (), leaf .contents [idx + 2 ].word .upper ()]):
350
- continue
351
349
352
350
v = violation .JoinTableViolation (leaf , index = idx )
353
351
violation_list .append (v )
@@ -395,89 +393,60 @@ def _check_context(node: Node, expected_list: Dict[str, str]) -> List[Violation]
395
393
return violation_list
396
394
397
395
398
- def _check_join_context (line_num , pos , tokens , token_index ):
399
- """
400
- valid contexts
401
- [left outer join], [inner join] or [cross join]
402
- Args:
403
- line_num:
404
- pos:
405
- tokens:
406
- token_index:
407
-
408
- Returns:
409
-
410
- """
411
- token = tokens [token_index ]
412
-
413
- if token .word .upper () != 'JOIN' :
414
- return []
415
-
416
- # concat join contexts
417
- join_contexts = [token .word ]
418
- for tk in reversed (tokens [:token_index ]):
419
- if tk .kind == Token .WHITESPACE :
420
- continue
421
-
422
- if tk .word .upper () in ['INNER' , 'OUTER' , 'LEFT' , 'RIGHT' , 'CROSS' ]:
423
- join_contexts .insert (0 , tk .word )
424
- else :
425
- break
426
-
427
- join_context_str = ' ' .join (join_contexts )
396
+ class LineChecker (Checker ):
397
+ """Checks violations about lines management.
428
398
429
- if join_context_str .upper () not in ['LEFT OUTER JOIN' , 'INNER JOIN' , 'CROSS JOIN' ]:
430
- return ['(L{}, {}): {}: {}' .format (line_num , pos , msg .MESSAGE_JOIN_CONTEXT , join_context_str )]
399
+ 1. Checks whether two or more blank lines exist.
431
400
432
- return []
401
+ 2. Checks whether breaking line after specified keywords.
402
+ Examples:
403
+ --- Good ---
404
+ Select
405
+ x
406
+ From
407
+ y
408
+ ------------
433
409
434
-
435
- def _check_break_line (line_num , pos , tokens , token_index ):
410
+ ---- NG ----
411
+ Select x From y
412
+ ------------
436
413
"""
437
- break line at 'and', 'or', 'on' (except between A and B)
438
- Args:
439
- line_num:
440
- pos:
441
- tokens:
442
- token_index:
443
-
444
- Returns:
445
414
446
- """
447
- token = tokens [token_index ]
415
+ @staticmethod
416
+ def check (tree : SyntaxTree , config : ConfigLoader ) -> List [Violation ]:
417
+ result : List [Violation ] = []
448
418
449
- if token .word .upper () not in ['AND' , 'OR' , 'ON' ]:
450
- return []
419
+ # 1. Checks whether two or more blank lines exist.
420
+ blank_count , v_list = LineChecker ._check_blank_line (tree .root , blank_count = 0 )
421
+ if blank_count >= 2 :
422
+ v_list
423
+ result .extend ()
451
424
452
- if _is_first_token ( tokens , token_index ):
453
- return []
425
+ # 2. Checks whether breaking line after specified keywords.
426
+ # TODO: Implement
454
427
455
- # if AND, search between context
456
- if token .word .upper () == 'AND' :
457
- for tk in reversed (tokens [:token_index ]):
458
- if tk .word .upper () == 'AND' :
459
- break
460
- if tk .word .upper () == 'BETWEEN' :
461
- return []
428
+ return result
462
429
463
- return ['(L{}, {}): {}: {}' .format (line_num , pos , msg .MESSAGE_BREAK_LINE , token .word )]
430
+ @staticmethod
431
+ def _check_blank_line (node : Node , blank_count : int ) -> Tuple [int , List [Violation ]]:
432
+ violation_list : List [Violation ] = []
464
433
434
+ count = len (node .contents )
465
435
466
- def _is_first_token (tokens , token_index ):
467
- """
468
- Args:
469
- tokens:
470
- token_index:
436
+ is_blank = (count == 0 )
471
437
472
- Returns:
438
+ if count == 1 and node .contents [0 ].kind == Token .WHITESPACE :
439
+ violation_list .append (violation .OnlyWhitespaceViolation (node , index = 0 ))
440
+ is_blank = True
473
441
474
- """
442
+ # If this line is not blank and 2 or more previous lines are blank, stack violation.
443
+ if is_blank :
444
+ blank_count += 1
445
+ elif blank_count >= 2 :
446
+ violation_list .append (violation .MultiBlankLineViolation (node , index = 0 ))
475
447
476
- if token_index == 0 :
477
- return True
448
+ for leaf in node . leaves :
449
+ LineChecker . _check_blank_line ( tree . root , blank_count = 0 ))
478
450
479
- for token in tokens [:token_index ]:
480
- if token .kind != Token .WHITESPACE and token .kind != Token .COMMENT :
481
- return False
482
451
483
- return True
452
+ return blank_count , violation_list
0 commit comments