|
25 | 25 | import org.sonar.api.batch.sensor.highlighting.TypeOfText; |
26 | 26 | import org.sonar.plugins.python.api.PythonSubscriptionCheck; |
27 | 27 | import org.sonar.plugins.python.api.PythonVisitorContext; |
| 28 | +import org.sonar.plugins.python.api.TokenLocation; |
28 | 29 | import org.sonar.plugins.python.api.tree.ClassDef; |
29 | 30 | import org.sonar.plugins.python.api.tree.FileInput; |
30 | 31 | import org.sonar.plugins.python.api.tree.FunctionDef; |
|
33 | 34 | import org.sonar.plugins.python.api.tree.Tree; |
34 | 35 | import org.sonar.plugins.python.api.tree.Trivia; |
35 | 36 | import org.sonar.python.SubscriptionVisitor; |
36 | | -import org.sonar.plugins.python.api.TokenLocation; |
37 | 37 | import org.sonar.python.api.PythonKeyword; |
38 | 38 | import org.sonar.python.api.PythonTokenType; |
39 | 39 |
|
|
83 | 83 | * Reminder: a docstring is a string literal that occurs as the first statement in a module, |
84 | 84 | * function, class, or method definition. |
85 | 85 | */ |
86 | | -public class PythonHighlighter extends PythonSubscriptionCheck { |
| 86 | +public class PythonHighlighter { |
87 | 87 |
|
88 | | - private NewHighlighting newHighlighting; |
89 | | - private Set<Token> docStringTokens; |
| 88 | + private final Object monitor; |
90 | 89 |
|
91 | | - public PythonHighlighter(SensorContext context, PythonInputFile inputFile) { |
92 | | - docStringTokens = new HashSet<>(); |
93 | | - newHighlighting = context.newHighlighting(); |
94 | | - newHighlighting.onFile(inputFile.wrappedFile()); |
| 90 | + public PythonHighlighter(Object monitor) { |
| 91 | + this.monitor = monitor; |
95 | 92 | } |
96 | 93 |
|
97 | | - @Override |
98 | | - public void scanFile(PythonVisitorContext visitorContext) { |
99 | | - SubscriptionVisitor.analyze(Collections.singletonList(this), visitorContext); |
| 94 | + public PythonHighlighter() { |
| 95 | + this(new Object()); |
100 | 96 | } |
101 | 97 |
|
102 | | - @Override |
103 | | - public void initialize(Context context) { |
104 | | - context.registerSyntaxNodeConsumer(Tree.Kind.FILE_INPUT, ctx -> checkFirstStatement(((FileInput) ctx.syntaxNode()).docstring())); |
105 | | - context.registerSyntaxNodeConsumer(Tree.Kind.FUNCDEF, ctx -> checkFirstStatement(((FunctionDef) ctx.syntaxNode()).docstring())); |
106 | | - context.registerSyntaxNodeConsumer(Tree.Kind.CLASSDEF, ctx -> checkFirstStatement(((ClassDef) ctx.syntaxNode()).docstring())); |
107 | | - context.registerSyntaxNodeConsumer(Tree.Kind.TOKEN, ctx -> visitToken(((Token) ctx.syntaxNode()))); |
| 98 | + public void highlight(SensorContext sensorContext, PythonVisitorContext visitorContext, PythonInputFile inputFile) { |
| 99 | + var check = new PythonHighlighterSubscriptionCheck(sensorContext, inputFile); |
| 100 | + check.scanFile(visitorContext); |
| 101 | + save(check.newHighlighting); |
108 | 102 | } |
109 | 103 |
|
110 | | - private void checkFirstStatement(@Nullable StringLiteral docString) { |
111 | | - if (docString == null) { |
112 | | - return; |
113 | | - } |
114 | | - for (Tree stringElement : docString.children()) { |
115 | | - highlight(stringElement.firstToken(), TypeOfText.STRUCTURED_COMMENT); |
116 | | - docStringTokens.add(stringElement.firstToken()); |
| 104 | + private void save(NewHighlighting newHighlighting) { |
| 105 | + synchronized (monitor) { |
| 106 | + newHighlighting.save(); |
117 | 107 | } |
118 | 108 | } |
119 | 109 |
|
120 | | - private void visitToken(Token token) { |
121 | | - if (token.type().equals(PythonTokenType.NUMBER)) { |
122 | | - highlight(token, TypeOfText.CONSTANT); |
| 110 | + private static class PythonHighlighterSubscriptionCheck extends PythonSubscriptionCheck { |
123 | 111 |
|
124 | | - } else if (token.type() instanceof PythonKeyword) { |
125 | | - highlight(token, TypeOfText.KEYWORD); |
| 112 | + private final NewHighlighting newHighlighting; |
| 113 | + private final Set<Token> docStringTokens; |
126 | 114 |
|
127 | | - } else if (token.type().equals(PythonTokenType.STRING) && !docStringTokens.contains(token)) { |
128 | | - highlight(token, TypeOfText.STRING); |
| 115 | + public PythonHighlighterSubscriptionCheck(SensorContext context, PythonInputFile inputFile) { |
| 116 | + docStringTokens = new HashSet<>(); |
| 117 | + newHighlighting = context.newHighlighting(); |
| 118 | + newHighlighting.onFile(inputFile.wrappedFile()); |
| 119 | + } |
129 | 120 |
|
130 | | - } else if (token.type().equals(IDENTIFIER) && isPython3Keyword(token.value())) { |
131 | | - // async and await are keywords starting python 3.5, however, for compatibility with previous versions, we cannot consider them as real keywords |
132 | | - highlight(token, TypeOfText.KEYWORD); |
| 121 | + @Override |
| 122 | + public void scanFile(PythonVisitorContext visitorContext) { |
| 123 | + SubscriptionVisitor.analyze(Collections.singletonList(this), visitorContext); |
| 124 | + } |
133 | 125 |
|
| 126 | + @Override |
| 127 | + public void initialize(Context context) { |
| 128 | + context.registerSyntaxNodeConsumer(Tree.Kind.FILE_INPUT, ctx -> checkFirstStatement(((FileInput) ctx.syntaxNode()).docstring())); |
| 129 | + context.registerSyntaxNodeConsumer(Tree.Kind.FUNCDEF, ctx -> checkFirstStatement(((FunctionDef) ctx.syntaxNode()).docstring())); |
| 130 | + context.registerSyntaxNodeConsumer(Tree.Kind.CLASSDEF, ctx -> checkFirstStatement(((ClassDef) ctx.syntaxNode()).docstring())); |
| 131 | + context.registerSyntaxNodeConsumer(Tree.Kind.TOKEN, ctx -> visitToken(((Token) ctx.syntaxNode()))); |
134 | 132 | } |
135 | 133 |
|
136 | | - for (Trivia trivia : token.trivia()) { |
137 | | - highlight(trivia.token(), TypeOfText.COMMENT); |
| 134 | + private void checkFirstStatement(@Nullable StringLiteral docString) { |
| 135 | + if (docString == null) { |
| 136 | + return; |
| 137 | + } |
| 138 | + for (Tree stringElement : docString.children()) { |
| 139 | + highlight(stringElement.firstToken(), TypeOfText.STRUCTURED_COMMENT); |
| 140 | + docStringTokens.add(stringElement.firstToken()); |
| 141 | + } |
138 | 142 | } |
139 | | - } |
140 | 143 |
|
141 | | - private static boolean isPython3Keyword(String value) { |
142 | | - return "await".equals(value) || "async".equals(value) || "match".equals(value) || "case".equals(value); |
143 | | - } |
| 144 | + private void visitToken(Token token) { |
| 145 | + if (token.type().equals(PythonTokenType.NUMBER)) { |
| 146 | + highlight(token, TypeOfText.CONSTANT); |
144 | 147 |
|
145 | | - @Override |
146 | | - public void leaveFile() { |
147 | | - newHighlighting.save(); |
148 | | - } |
| 148 | + } else if (token.type() instanceof PythonKeyword) { |
| 149 | + highlight(token, TypeOfText.KEYWORD); |
149 | 150 |
|
150 | | - private void highlight(Token token, TypeOfText typeOfText) { |
151 | | - TokenLocation tokenLocation = new TokenLocation(token); |
152 | | - newHighlighting.highlight(tokenLocation.startLine(), tokenLocation.startLineOffset(), tokenLocation.endLine(), tokenLocation.endLineOffset(), typeOfText); |
| 151 | + } else if (token.type().equals(PythonTokenType.STRING) && !docStringTokens.contains(token)) { |
| 152 | + highlight(token, TypeOfText.STRING); |
| 153 | + |
| 154 | + } else if (token.type().equals(IDENTIFIER) && isPython3Keyword(token.value())) { |
| 155 | + // async and await are keywords starting python 3.5, however, for compatibility with previous versions, we cannot consider them as real keywords |
| 156 | + highlight(token, TypeOfText.KEYWORD); |
| 157 | + |
| 158 | + } |
| 159 | + |
| 160 | + for (Trivia trivia : token.trivia()) { |
| 161 | + highlight(trivia.token(), TypeOfText.COMMENT); |
| 162 | + } |
| 163 | + } |
| 164 | + |
| 165 | + private static boolean isPython3Keyword(String value) { |
| 166 | + return "await".equals(value) || "async".equals(value) || "match".equals(value) || "case".equals(value); |
| 167 | + } |
| 168 | + |
| 169 | + private void highlight(Token token, TypeOfText typeOfText) { |
| 170 | + TokenLocation tokenLocation = new TokenLocation(token); |
| 171 | + newHighlighting.highlight(tokenLocation.startLine(), tokenLocation.startLineOffset(), tokenLocation.endLine(), tokenLocation.endLineOffset(), typeOfText); |
| 172 | + } |
153 | 173 | } |
154 | 174 |
|
155 | 175 | } |
0 commit comments