mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Disable formatter ranges in formatter off/on tags [CR-IC-2916]
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
import java.util.*;
|
||||
|
||||
public class CollectionTest {
|
||||
/**
|
||||
* Normal JavaDoc, can
|
||||
* be
|
||||
* formatted.
|
||||
* @param args
|
||||
* Arguments.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
int size;
|
||||
HashSet collection = new HashSet();
|
||||
@@ -22,4 +29,34 @@ public class CollectionTest {
|
||||
System.out.print(iterator.next() + " ");
|
||||
}
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
/**
|
||||
* And please don't touch this:
|
||||
* @param x
|
||||
* These are my nice comments.
|
||||
* @param y
|
||||
* And yet another one.
|
||||
*/
|
||||
public void doSomething(String x, String y) {
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
/**
|
||||
* It's OK to format this comment.
|
||||
* @param z
|
||||
* Parameter Z.
|
||||
*/
|
||||
public void doSomethingElse(String z) {}
|
||||
|
||||
// @formatter:off
|
||||
/**
|
||||
* This comment must be preserved too.
|
||||
* @param i
|
||||
* Parameter I.
|
||||
* @param j
|
||||
* Parameter J.
|
||||
*/
|
||||
public void evenMore(int i, int j) {}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import java.util.*;
|
||||
|
||||
public class CollectionTest {
|
||||
/**
|
||||
* Normal JavaDoc, can
|
||||
* be
|
||||
* formatted.
|
||||
*
|
||||
* @param args Arguments.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
int size;
|
||||
HashSet collection = new HashSet();
|
||||
@@ -22,4 +29,35 @@ public class CollectionTest {
|
||||
System.out.print(iterator.next() + " ");
|
||||
}
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
/**
|
||||
* And please don't touch this:
|
||||
* @param x
|
||||
* These are my nice comments.
|
||||
* @param y
|
||||
* And yet another one.
|
||||
*/
|
||||
public void doSomething(String x, String y) {
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
/**
|
||||
* It's OK to format this comment.
|
||||
*
|
||||
* @param z Parameter Z.
|
||||
*/
|
||||
public void doSomethingElse(String z) {
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
/**
|
||||
* This comment must be preserved too.
|
||||
* @param i
|
||||
* Parameter I.
|
||||
* @param j
|
||||
* Parameter J.
|
||||
*/
|
||||
public void evenMore(int i, int j) {}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.formatting;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiRecursiveElementVisitor;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Rustam Vishnyakov
|
||||
*/
|
||||
public class FormatterTagHandler {
|
||||
|
||||
public enum FormatterTag {ON, OFF, NONE}
|
||||
private final CodeStyleSettings mySettings;
|
||||
|
||||
public FormatterTagHandler(CodeStyleSettings settings) {
|
||||
mySettings = settings;
|
||||
}
|
||||
|
||||
public FormatterTag getFormatterTag(Block block) {
|
||||
if (mySettings.FORMATTER_TAGS_ENABLED &&
|
||||
!StringUtil.isEmpty(mySettings.FORMATTER_ON_TAG) &&
|
||||
!StringUtil.isEmpty(mySettings.FORMATTER_OFF_TAG) &&
|
||||
block instanceof ASTBlock) {
|
||||
ASTNode node = ((ASTBlock)block).getNode();
|
||||
PsiElement element = node.getPsi();
|
||||
if (element != null && element instanceof PsiComment) {
|
||||
return getFormatterTag((PsiComment)element);
|
||||
}
|
||||
}
|
||||
return FormatterTag.NONE;
|
||||
}
|
||||
|
||||
private FormatterTag getFormatterTag(@NotNull PsiComment comment) {
|
||||
CharSequence nodeChars = comment.getNode().getChars();
|
||||
if (mySettings.FORMATTER_TAGS_ACCEPT_REGEXP) {
|
||||
Pattern onPattern = mySettings.getFormatterOnPattern();
|
||||
Pattern offPattern = mySettings.getFormatterOffPattern();
|
||||
if (onPattern != null && onPattern.matcher(nodeChars).find()) return FormatterTag.ON;
|
||||
if (offPattern != null && offPattern.matcher(nodeChars).find()) return FormatterTag.OFF;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < nodeChars.length(); i++) {
|
||||
if (isFormatterTagAt(nodeChars, i, mySettings.FORMATTER_ON_TAG)) return FormatterTag.ON;
|
||||
if (isFormatterTagAt(nodeChars, i, mySettings.FORMATTER_OFF_TAG)) return FormatterTag.OFF;
|
||||
}
|
||||
}
|
||||
return FormatterTag.NONE;
|
||||
}
|
||||
|
||||
private static boolean isFormatterTagAt(@NotNull CharSequence s, int pos, @NotNull String tagName) {
|
||||
if (!tagName.isEmpty() && tagName.charAt(0) == s.charAt(pos)) {
|
||||
int end = pos + tagName.length();
|
||||
if (end <= s.length()) {
|
||||
return StringUtil.equalsIgnoreCase(s.subSequence(pos, end), tagName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<TextRange> getEnabledRanges(ASTNode rootNode, TextRange initialRange) {
|
||||
EnabledRangesCollector collector = new EnabledRangesCollector(initialRange);
|
||||
rootNode.getPsi().accept(collector);
|
||||
return collector.getRanges();
|
||||
}
|
||||
|
||||
private class EnabledRangesCollector extends PsiRecursiveElementVisitor {
|
||||
private final List<FormatterTagInfo> myTagInfoList = new ArrayList<FormatterTagInfo>();
|
||||
private final TextRange myInitialRange;
|
||||
|
||||
private EnabledRangesCollector(TextRange initialRange) {
|
||||
myInitialRange = initialRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitComment(PsiComment comment) {
|
||||
FormatterTag tag = getFormatterTag(comment);
|
||||
//noinspection EnumSwitchStatementWhichMissesCases
|
||||
switch (tag) {
|
||||
case OFF:
|
||||
myTagInfoList.add(new FormatterTagInfo(comment.getTextRange().getEndOffset(), FormatterTag.OFF));
|
||||
break;
|
||||
case ON:
|
||||
myTagInfoList.add(new FormatterTagInfo(comment.getTextRange().getEndOffset(), FormatterTag.ON));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private List<TextRange> getRanges() {
|
||||
List<TextRange> enabledRanges = new ArrayList<TextRange>();
|
||||
Collections.sort(myTagInfoList, new Comparator<FormatterTagInfo>() {
|
||||
@Override
|
||||
public int compare(FormatterTagInfo tagInfo1,
|
||||
FormatterTagInfo tagInfo2) {
|
||||
return tagInfo1.offset - tagInfo2.offset;
|
||||
}
|
||||
});
|
||||
|
||||
int start = myInitialRange.getStartOffset();
|
||||
boolean formatterEnabled = true;
|
||||
for (FormatterTagInfo tagInfo: myTagInfoList) {
|
||||
if (tagInfo.tag == FormatterTag.OFF && formatterEnabled) {
|
||||
if (tagInfo.offset > start) {
|
||||
TextRange range = new TextRange(start, tagInfo.offset);
|
||||
enabledRanges.add(range);
|
||||
}
|
||||
formatterEnabled = false;
|
||||
}
|
||||
else if (tagInfo.tag == FormatterTag.ON && !formatterEnabled) {
|
||||
start = Math.max(tagInfo.offset, myInitialRange.getStartOffset());
|
||||
if (start >= myInitialRange.getEndOffset()) break;
|
||||
formatterEnabled = true;
|
||||
}
|
||||
}
|
||||
if (start < myInitialRange.getEndOffset()) {
|
||||
enabledRanges.add(new TextRange(start, myInitialRange.getEndOffset()));
|
||||
}
|
||||
return enabledRanges;
|
||||
}
|
||||
|
||||
private class FormatterTagInfo {
|
||||
public int offset;
|
||||
public FormatterTag tag;
|
||||
|
||||
private FormatterTagInfo(int offset, FormatterTag tag) {
|
||||
this.offset = offset;
|
||||
this.tag = tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,8 @@
|
||||
package com.intellij.formatting;
|
||||
|
||||
import com.intellij.diagnostic.LogMessageEx;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
@@ -38,7 +34,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Allows to build {@link AbstractBlockWrapper formatting block wrappers} for the target {@link Block formatting blocks}.
|
||||
@@ -54,13 +49,11 @@ class InitialInfoBuilder {
|
||||
private final int myPositionOfInterest;
|
||||
@NotNull
|
||||
private final FormattingProgressCallback myProgressCallback;
|
||||
private final FormatterTagHandler myFormatterTagHandler;
|
||||
|
||||
private final CommonCodeStyleSettings.IndentOptions myOptions;
|
||||
private final CodeStyleSettings mySettings;
|
||||
|
||||
private final Stack<State> myStates = new Stack<State>();
|
||||
|
||||
private enum FormatterTag {ON, OFF, NONE}
|
||||
|
||||
private WhiteSpace myCurrentWhiteSpace;
|
||||
private CompositeBlockWrapper myRootBlockWrapper;
|
||||
private LeafBlockWrapper myPreviousBlock;
|
||||
@@ -86,7 +79,7 @@ class InitialInfoBuilder {
|
||||
myOptions = options;
|
||||
myPositionOfInterest = positionOfInterest;
|
||||
myReadOnlyMode = false;
|
||||
mySettings = settings;
|
||||
myFormatterTagHandler = new FormatterTagHandler(settings);
|
||||
}
|
||||
|
||||
public static InitialInfoBuilder prepareToBuildBlocksSequentially(Block root,
|
||||
@@ -297,7 +290,7 @@ class InitialInfoBuilder {
|
||||
info.arrangeParentTextRange();
|
||||
}
|
||||
|
||||
switch (getFormatterTag(rootBlock)) {
|
||||
switch (myFormatterTagHandler.getFormatterTag(rootBlock)) {
|
||||
case ON:
|
||||
myReadOnlyMode = false;
|
||||
break;
|
||||
@@ -416,42 +409,6 @@ class InitialInfoBuilder {
|
||||
LogMessageEx.error(LOG, messageBuffer.toString(), buffer.toString());
|
||||
}
|
||||
|
||||
private FormatterTag getFormatterTag(Block block) {
|
||||
if (mySettings.FORMATTER_TAGS_ENABLED &&
|
||||
!StringUtil.isEmpty(mySettings.FORMATTER_ON_TAG) &&
|
||||
!StringUtil.isEmpty(mySettings.FORMATTER_OFF_TAG) &&
|
||||
block instanceof ASTBlock) {
|
||||
ASTNode node = ((ASTBlock)block).getNode();
|
||||
PsiElement element = node.getPsi();
|
||||
if (element != null && element instanceof PsiComment) {
|
||||
CharSequence nodeChars = node.getChars();
|
||||
if (mySettings.FORMATTER_TAGS_ACCEPT_REGEXP) {
|
||||
Pattern onPattern = mySettings.getFormatterOnPattern();
|
||||
Pattern offPattern = mySettings.getFormatterOffPattern();
|
||||
if (onPattern != null && onPattern.matcher(nodeChars).find()) return FormatterTag.ON;
|
||||
if (offPattern != null && offPattern.matcher(nodeChars).find()) return FormatterTag.OFF;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < nodeChars.length(); i++) {
|
||||
if (isFormatterTagAt(nodeChars, i, mySettings.FORMATTER_ON_TAG)) return FormatterTag.ON;
|
||||
if (isFormatterTagAt(nodeChars, i, mySettings.FORMATTER_OFF_TAG)) return FormatterTag.OFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return FormatterTag.NONE;
|
||||
}
|
||||
|
||||
private static boolean isFormatterTagAt(@NotNull CharSequence s, int pos, @NotNull String tagName) {
|
||||
if (!tagName.isEmpty() && tagName.charAt(0) == s.charAt(pos)) {
|
||||
int end = pos + tagName.length();
|
||||
if (end <= s.length()) {
|
||||
return StringUtil.equalsIgnoreCase(s.subSequence(pos, end), tagName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We want to wrap {@link Block code blocks} sequentially, hence, need to store a processing state and continue from the point
|
||||
* where we stopped the processing last time.
|
||||
|
||||
+25
-3
@@ -79,9 +79,11 @@ public class CodeFormatterFacade {
|
||||
= new Key<Boolean>("WRAP_LONG_LINE_DURING_FORMATTING_IN_PROGRESS_KEY");
|
||||
|
||||
private final CodeStyleSettings mySettings;
|
||||
private final FormatterTagHandler myTagHandler;
|
||||
|
||||
public CodeFormatterFacade(CodeStyleSettings settings) {
|
||||
mySettings = settings;
|
||||
myTagHandler = new FormatterTagHandler(settings);
|
||||
}
|
||||
|
||||
public ASTNode processElement(ASTNode element) {
|
||||
@@ -253,7 +255,7 @@ public class CodeFormatterFacade {
|
||||
}
|
||||
}
|
||||
|
||||
private static TextRange preprocess(@NotNull final ASTNode node, @NotNull TextRange range) {
|
||||
private TextRange preprocess(@NotNull final ASTNode node, @NotNull TextRange range) {
|
||||
TextRange result = range;
|
||||
PsiElement psi = node.getPsi();
|
||||
if (!psi.isValid()) {
|
||||
@@ -324,13 +326,33 @@ public class CodeFormatterFacade {
|
||||
}
|
||||
}
|
||||
|
||||
for(PreFormatProcessor processor: Extensions.getExtensions(PreFormatProcessor.EP_NAME)) {
|
||||
result = processor.process(node, result);
|
||||
if (!mySettings.FORMATTER_TAGS_ENABLED) {
|
||||
for(PreFormatProcessor processor: Extensions.getExtensions(PreFormatProcessor.EP_NAME)) {
|
||||
result = processor.process(node, result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = preprocessEnabledRanges(node, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private TextRange preprocessEnabledRanges(@NotNull final ASTNode node, @NotNull TextRange range) {
|
||||
TextRange result = TextRange.create(range.getStartOffset(), range.getEndOffset());
|
||||
List<TextRange> enabledRanges = myTagHandler.getEnabledRanges(node, result);
|
||||
int delta = 0;
|
||||
for (TextRange enabledRange : enabledRanges) {
|
||||
enabledRange = enabledRange.shiftRight(delta);
|
||||
for (PreFormatProcessor processor : Extensions.getExtensions(PreFormatProcessor.EP_NAME)) {
|
||||
TextRange processedRange = processor.process(node, enabledRange);
|
||||
delta += processedRange.getLength() - enabledRange.getLength();
|
||||
}
|
||||
}
|
||||
result = result.grown(delta);
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Collection<PsiLanguageInjectionHost> collectInjectionHosts(@NotNull PsiFile file, @NotNull TextRange range) {
|
||||
Stack<PsiElement> toProcess = new Stack<PsiElement>();
|
||||
|
||||
Reference in New Issue
Block a user