mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-144761 diff: do not ignore whitespaces in string literals
This commit is contained in:
committed by
Aleksey Pivovarov
parent
b0f96c60a4
commit
74921ef65b
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.diff.lang;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class JavaDiffIgnoredRangeProvider extends LangDiffIgnoredRangeProvider {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Ignore imports and formatting";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean accepts(@NotNull Project project, @NotNull Language language) {
|
||||
return JavaLanguage.INSTANCE.equals(language);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<TextRange> computeIgnoredRanges(@NotNull Project project, @NotNull CharSequence text, @NotNull Language language) {
|
||||
return ReadAction.compute(() -> {
|
||||
List<TextRange> result = new ArrayList<>();
|
||||
PsiFile psiFile = PsiFileFactory.getInstance(project).createFileFromText("", language, text);
|
||||
|
||||
psiFile.accept(new PsiElementVisitor() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
if (element.getTextLength() == 0) return;
|
||||
|
||||
if (isIgnored(element)) {
|
||||
result.add(element.getTextRange());
|
||||
}
|
||||
else {
|
||||
element.acceptChildren(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isIgnored(@NotNull PsiElement element) {
|
||||
if (element instanceof PsiWhiteSpace) return true;
|
||||
if (element instanceof PsiImportList) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -23,14 +23,17 @@ import com.intellij.diff.util.MergeRange;
|
||||
import com.intellij.diff.util.Range;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.diff.FilesTooBigForDiffException;
|
||||
import com.intellij.util.text.CharSequenceSubSequence;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -39,6 +42,11 @@ import static java.util.Collections.singletonList;
|
||||
public class ComparisonManagerImpl extends ComparisonManager {
|
||||
private static final Logger LOG = Logger.getInstance(ComparisonManagerImpl.class);
|
||||
|
||||
@NotNull
|
||||
public static ComparisonManagerImpl getInstanceImpl() {
|
||||
return (ComparisonManagerImpl)getInstance();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<LineFragment> compareLines(@NotNull CharSequence text1,
|
||||
@@ -433,6 +441,130 @@ public class ComparisonManagerImpl extends ComparisonManager {
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compare two texts by-line and then compare changed fragments by-word
|
||||
*/
|
||||
@NotNull
|
||||
public List<LineFragment> compareLinesWithIgnoredRanges(@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull List<TextRange> ignoredRanges1,
|
||||
@NotNull List<TextRange> ignoredRanges2,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) throws DiffTooBigException {
|
||||
BitSet ignored1 = collectIgnoredRanges(ignoredRanges1);
|
||||
BitSet ignored2 = collectIgnoredRanges(ignoredRanges2);
|
||||
|
||||
List<Line> lines1 = getLines(text1);
|
||||
List<Line> lines2 = getLines(text2);
|
||||
|
||||
List<CharSequence> lineTexts1 = ContainerUtil.map(lines1, line -> line.getNotIgnoredContent(ignored1));
|
||||
List<CharSequence> lineTexts2 = ContainerUtil.map(lines2, line -> line.getNotIgnoredContent(ignored2));
|
||||
|
||||
FairDiffIterable iterable = ByLine.compare(lineTexts1, lineTexts2, ComparisonPolicy.DEFAULT, indicator);
|
||||
List<LineFragment> lineFragments = convertIntoLineFragments(lines1, lines2, iterable);
|
||||
|
||||
if (innerFragments) {
|
||||
lineFragments = createInnerFragments(lineFragments, text1, text2, ComparisonPolicy.DEFAULT, indicator);
|
||||
}
|
||||
|
||||
return ContainerUtil.mapNotNull(lineFragments, fragment -> {
|
||||
return trimIgnoredChanges(fragment, lines1, lines2, ignored1, ignored2);
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static BitSet collectIgnoredRanges(@NotNull List<TextRange> ignoredRanges) {
|
||||
BitSet set = new BitSet();
|
||||
for (TextRange range : ignoredRanges) {
|
||||
set.set(range.getStartOffset(), range.getEndOffset());
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static LineFragment trimIgnoredChanges(@NotNull LineFragment fragment,
|
||||
@NotNull List<Line> lines1,
|
||||
@NotNull List<Line> lines2,
|
||||
@NotNull BitSet ignored1,
|
||||
@NotNull BitSet ignored2) {
|
||||
// trim ignored lines
|
||||
Range range = TrimUtil.trimExpandList(lines1, lines2,
|
||||
fragment.getStartLine1(), fragment.getStartLine2(),
|
||||
fragment.getEndLine1(), fragment.getEndLine2(),
|
||||
(line1, line2) -> areIgnoredEqualLines(line1, line2, ignored1, ignored2),
|
||||
line -> isIgnoredLine(line, ignored1),
|
||||
line -> isIgnoredLine(line, ignored2));
|
||||
|
||||
int startLine1 = range.start1;
|
||||
int startLine2 = range.start2;
|
||||
int endLine1 = range.end1;
|
||||
int endLine2 = range.end2;
|
||||
|
||||
if (startLine1 == endLine1 && startLine2 == endLine2) return null;
|
||||
|
||||
IntPair offsets1 = getOffsets(lines1, startLine1, endLine1);
|
||||
IntPair offsets2 = getOffsets(lines2, startLine2, endLine2);
|
||||
int startOffset1 = offsets1.val1;
|
||||
int endOffset1 = offsets1.val2;
|
||||
int startOffset2 = offsets2.val1;
|
||||
int endOffset2 = offsets2.val2;
|
||||
|
||||
List<DiffFragment> newInner = null;
|
||||
if (fragment.getInnerFragments() != null) {
|
||||
int shift1 = startOffset1 - fragment.getStartOffset1();
|
||||
int shift2 = startOffset2 - fragment.getStartOffset2();
|
||||
int newCount1 = endOffset1 - startOffset1;
|
||||
int newCount2 = endOffset2 - startOffset2;
|
||||
|
||||
newInner = ContainerUtil.mapNotNull(fragment.getInnerFragments(), it -> {
|
||||
// update offsets, as some lines might have been ignored completely
|
||||
int start1 = Math.max(it.getStartOffset1() - shift1, 0);
|
||||
int start2 = Math.max(it.getStartOffset2() - shift2, 0);
|
||||
int end1 = Math.max(Math.min(it.getEndOffset1() - shift1, newCount1), 0);
|
||||
int end2 = Math.max(Math.min(it.getEndOffset2() - shift2, newCount2), 0);
|
||||
|
||||
// trim inner fragments
|
||||
TextRange range1 = trimIgnoredRange(start1, end1, ignored1, startOffset1);
|
||||
TextRange range2 = trimIgnoredRange(start2, end2, ignored2, startOffset2);
|
||||
|
||||
if (range1.isEmpty() && range2.isEmpty()) return null;
|
||||
return new DiffFragmentImpl(range1.getStartOffset(), range1.getEndOffset(),
|
||||
range2.getStartOffset(), range2.getEndOffset());
|
||||
});
|
||||
if (newInner.isEmpty()) return null;
|
||||
}
|
||||
|
||||
return new LineFragmentImpl(startLine1, endLine1, startLine2, endLine2,
|
||||
startOffset1, endOffset1, startOffset2, endOffset2,
|
||||
newInner);
|
||||
}
|
||||
|
||||
private static boolean isIgnoredLine(@NotNull Line line, @NotNull BitSet ignored) {
|
||||
return trimIgnoredRange(line.getOffset1(), line.getOffset2(), ignored, 0).isEmpty();
|
||||
}
|
||||
|
||||
private static boolean areIgnoredEqualLines(@NotNull Line line1, @NotNull Line line2,
|
||||
@NotNull BitSet ignored1, @NotNull BitSet ignored2) {
|
||||
int start1 = line1.getOffset1();
|
||||
int end1 = line1.getOffset2();
|
||||
int start2 = line2.getOffset1();
|
||||
int end2 = line2.getOffset2();
|
||||
|
||||
Range range = TrimUtil.trimExpandText(line1.getOriginalText(), line2.getOriginalText(),
|
||||
start1, start2, end1, end2,
|
||||
ignored1, ignored2);
|
||||
return range.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
private static TextRange trimIgnoredRange(int start, int end, @NotNull BitSet ignored, int offset) {
|
||||
IntPair intPair = TrimUtil.trim(offset + start, offset + end, ignored);
|
||||
return new TextRange(intPair.val1 - offset, intPair.val2 - offset);
|
||||
}
|
||||
|
||||
|
||||
private static class Line {
|
||||
@NotNull private final CharSequence myChars;
|
||||
private final int myOffset1;
|
||||
@@ -458,5 +590,20 @@ public class ComparisonManagerImpl extends ComparisonManager {
|
||||
public CharSequence getContent() {
|
||||
return new CharSequenceSubSequence(myChars, myOffset1, myOffset2);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CharSequence getNotIgnoredContent(@NotNull BitSet ignored) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = myOffset1; i < myOffset2; i++) {
|
||||
if (ignored.get(i)) continue;
|
||||
sb.append(myChars.charAt(i));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CharSequence getOriginalText() {
|
||||
return myChars;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.diff.util.IntPair
|
||||
import com.intellij.diff.util.MergeRange
|
||||
import com.intellij.diff.util.Range
|
||||
import com.intellij.openapi.util.text.StringUtil.isWhiteSpace
|
||||
import java.util.*
|
||||
|
||||
fun isPunctuation(c: Char): Boolean {
|
||||
if (c == '_') return false
|
||||
@@ -42,6 +43,11 @@ fun trim(text: CharSequence, start: Int, end: Int): IntPair {
|
||||
{ index -> isWhiteSpace(text[index]) })
|
||||
}
|
||||
|
||||
fun trim(start: Int, end: Int, ignored: BitSet): IntPair {
|
||||
return trim(start, end,
|
||||
{ index -> ignored[index] })
|
||||
}
|
||||
|
||||
fun trimStart(text: CharSequence, start: Int, end: Int): Int {
|
||||
return trimStart(start, end,
|
||||
{ index -> isWhiteSpace(text[index]) })
|
||||
@@ -156,12 +162,25 @@ fun expandWhitespacesBackward(text1: CharSequence, text2: CharSequence, text3: C
|
||||
}
|
||||
|
||||
|
||||
fun expandIW(text1: CharSequence, text2: CharSequence,
|
||||
start1: Int, start2: Int, end1: Int, end2: Int): Range {
|
||||
fun <T> trimExpandList(text1: List<T>, text2: List<T>,
|
||||
start1: Int, start2: Int, end1: Int, end2: Int,
|
||||
equals: (T, T) -> Boolean,
|
||||
ignored1: (T) -> Boolean,
|
||||
ignored2: (T) -> Boolean): Range {
|
||||
return trimExpand(start1, start2, end1, end2,
|
||||
{ index1, index2 -> equals(text1[index1], text2[index2]) },
|
||||
{ index -> ignored1(text1[index]) },
|
||||
{ index -> ignored2(text2[index]) })
|
||||
}
|
||||
|
||||
fun trimExpandText(text1: CharSequence, text2: CharSequence,
|
||||
start1: Int, start2: Int, end1: Int, end2: Int,
|
||||
ignored1: BitSet,
|
||||
ignored2: BitSet): Range {
|
||||
return trimExpand(start1, start2, end1, end2,
|
||||
{ index1, index2 -> text1[index1] == text2[index2] },
|
||||
{ index -> isWhiteSpace(text1[index]) },
|
||||
{ index -> isWhiteSpace(text2[index]) })
|
||||
{ index -> ignored1[index] },
|
||||
{ index -> ignored2[index] })
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.diff.lang;
|
||||
|
||||
import com.intellij.diff.contents.DiffContent;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DiffIgnoredRangeProvider {
|
||||
ExtensionPointName<DiffIgnoredRangeProvider> EP_NAME =
|
||||
ExtensionPointName.create("com.intellij.diff.lang.DiffIgnoredRangeProvider");
|
||||
|
||||
@NotNull
|
||||
String getDescription();
|
||||
|
||||
boolean accepts(@Nullable Project project, @NotNull DiffContent content);
|
||||
|
||||
@NotNull
|
||||
List<TextRange> getIgnoredRanges(@Nullable Project project, @NotNull CharSequence text, @NotNull DiffContent content);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.diff.lang;
|
||||
|
||||
import com.intellij.diff.contents.DiffContent;
|
||||
import com.intellij.diff.contents.DocumentContent;
|
||||
import com.intellij.diff.util.DiffUserDataKeys;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.LanguageFileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.LanguageSubstitutors;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class LangDiffIgnoredRangeProvider implements DiffIgnoredRangeProvider {
|
||||
protected abstract boolean accepts(@NotNull Project project, @NotNull Language language);
|
||||
|
||||
@NotNull
|
||||
protected abstract List<TextRange> computeIgnoredRanges(@NotNull Project project, @NotNull CharSequence text, @NotNull Language language);
|
||||
|
||||
@Override
|
||||
public final boolean accepts(@Nullable Project project, @NotNull DiffContent content) {
|
||||
if (project == null) return false;
|
||||
Language language = getLanguage(project, content);
|
||||
if (language == null) return false;
|
||||
return accepts(project, language);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<TextRange> getIgnoredRanges(@Nullable Project project, @NotNull CharSequence text, @NotNull DiffContent content) {
|
||||
assert project != null;
|
||||
Language language = getLanguage(project, content);
|
||||
assert language != null;
|
||||
|
||||
return computeIgnoredRanges(project, text, language);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Language getLanguage(@NotNull Project project, @NotNull DiffContent content) {
|
||||
Language language = content.getUserData(DiffUserDataKeys.LANGUAGE);
|
||||
if (language != null) return language;
|
||||
|
||||
FileType type = content.getContentType();
|
||||
if (type instanceof LanguageFileType) language = ((LanguageFileType)type).getLanguage();
|
||||
|
||||
if (language != null && content instanceof DocumentContent) {
|
||||
VirtualFile highlightFile = ((DocumentContent)content).getHighlightFile();
|
||||
if (highlightFile != null) language = LanguageSubstitutors.INSTANCE.substituteLanguage(language, highlightFile, project);
|
||||
}
|
||||
|
||||
return language;
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase {
|
||||
myEditorSettingsAction = new SetEditorSettingsAction(getTextSettings(), getEditors());
|
||||
myEditorSettingsAction.applyDefaults();
|
||||
|
||||
myTextDiffProvider = DiffUtil.createNoIgnoreTextDiffProvider(getRequest(), getTextSettings(), this::rediff);
|
||||
myTextDiffProvider = DiffUtil.createNoIgnoreTextDiffProvider(getProject(), getRequest(), getTextSettings(), this::rediff);
|
||||
|
||||
new MyOpenInEditorWithMouseAction().install(getEditors());
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
|
||||
|
||||
myModifierProvider = new ModifierProvider();
|
||||
|
||||
myTextDiffProvider = DiffUtil.createTextDiffProvider(getRequest(), getTextSettings(), this::rediff);
|
||||
myTextDiffProvider = DiffUtil.createTextDiffProvider(getProject(), getRequest(), getTextSettings(), this::rediff);
|
||||
|
||||
DiffUtil.registerAction(new ReplaceSelectedChangesAction(Side.LEFT, true), myPanel);
|
||||
DiffUtil.registerAction(new AppendSelectedChangesAction(Side.LEFT, true), myPanel);
|
||||
|
||||
@@ -56,7 +56,7 @@ public class SimpleOnesideDiffViewer extends OnesideTextDiffViewer {
|
||||
public SimpleOnesideDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
|
||||
super(context, (ContentDiffRequest)request);
|
||||
|
||||
myTextDiffProvider = DiffUtil.createTextDiffProvider(getRequest(), getTextSettings(), this::rediff);
|
||||
myTextDiffProvider = DiffUtil.createTextDiffProvider(getProject(), getRequest(), getTextSettings(), this::rediff);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,7 +22,8 @@ public enum IgnorePolicy {
|
||||
DEFAULT("Do not ignore"),
|
||||
TRIM_WHITESPACES("Trim whitespaces"),
|
||||
IGNORE_WHITESPACES("Ignore whitespaces"),
|
||||
IGNORE_WHITESPACES_CHUNKS("Ignore whitespaces and empty lines");
|
||||
IGNORE_WHITESPACES_CHUNKS("Ignore whitespaces and empty lines"),
|
||||
FORMATTING("Ignore formatting");
|
||||
|
||||
@NotNull private final String myText;
|
||||
|
||||
@@ -46,6 +47,8 @@ public enum IgnorePolicy {
|
||||
return ComparisonPolicy.IGNORE_WHITESPACES;
|
||||
case IGNORE_WHITESPACES_CHUNKS:
|
||||
return ComparisonPolicy.IGNORE_WHITESPACES;
|
||||
case FORMATTING:
|
||||
return ComparisonPolicy.DEFAULT;
|
||||
default:
|
||||
throw new IllegalArgumentException(this.name());
|
||||
}
|
||||
|
||||
@@ -336,6 +336,9 @@ public class TextDiffViewerUtil {
|
||||
if (value == IgnorePolicy.IGNORE_WHITESPACES_CHUNKS) {
|
||||
return list(IgnorePolicy.IGNORE_WHITESPACES);
|
||||
}
|
||||
if (value == IgnorePolicy.FORMATTING) {
|
||||
return list(IgnorePolicy.TRIM_WHITESPACES);
|
||||
}
|
||||
return list(IgnorePolicy.DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
+9
-20
@@ -25,15 +25,14 @@ import com.intellij.diff.util.DiffUserDataKeysEx;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.diff.tools.util.base.HighlightPolicy.*;
|
||||
import static com.intellij.diff.tools.util.base.IgnorePolicy.*;
|
||||
|
||||
public class SimpleTextDiffProvider extends TextDiffProviderBase implements TwosideTextDiffProvider {
|
||||
private static final DiffUserDataKeysEx.DiffComputer DEFAULT_COMPUTER = (text1, text2, policy, innerChanges, indicator) -> {
|
||||
public class SimpleTextDiffProvider extends TwosideTextDiffProviderBase implements TwosideTextDiffProvider {
|
||||
static final DiffUserDataKeysEx.DiffComputer DEFAULT_COMPUTER = (text1, text2, policy, innerChanges, indicator) -> {
|
||||
if (innerChanges) {
|
||||
return ComparisonManager.getInstance().compareLinesInner(text1, text2, policy, indicator);
|
||||
}
|
||||
@@ -67,25 +66,15 @@ public class SimpleTextDiffProvider extends TextDiffProviderBase implements Twos
|
||||
myDiffComputer = diffComputer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
@Override
|
||||
public List<LineFragment> compare(@NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull ProgressIndicator indicator) {
|
||||
IgnorePolicy ignorePolicy = getIgnorePolicy();
|
||||
HighlightPolicy highlightPolicy = getHighlightPolicy();
|
||||
|
||||
if (!highlightPolicy.isShouldCompare()) return null;
|
||||
|
||||
protected List<LineFragment> doCompare(@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull IgnorePolicy ignorePolicy,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) {
|
||||
ComparisonPolicy policy = ignorePolicy.getComparisonPolicy();
|
||||
boolean innerFragments = highlightPolicy.isFineFragments();
|
||||
boolean squashFragments = highlightPolicy.isShouldSquash();
|
||||
boolean trimFragments = ignorePolicy.isShouldTrimChunks();
|
||||
|
||||
indicator.checkCanceled();
|
||||
List<LineFragment> fragments = myDiffComputer.compute(text1, text2, policy, innerFragments, indicator);
|
||||
|
||||
indicator.checkCanceled();
|
||||
return ComparisonManager.getInstance().processBlocks(fragments, text1, text2,
|
||||
policy, squashFragments, trimFragments);
|
||||
return myDiffComputer.compute(text1, text2, policy, innerFragments, indicator);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.diff.tools.util.text;
|
||||
|
||||
import com.intellij.diff.comparison.ComparisonManagerImpl;
|
||||
import com.intellij.diff.comparison.ComparisonPolicy;
|
||||
import com.intellij.diff.contents.DiffContent;
|
||||
import com.intellij.diff.fragments.LineFragment;
|
||||
import com.intellij.diff.lang.DiffIgnoredRangeProvider;
|
||||
import com.intellij.diff.requests.ContentDiffRequest;
|
||||
import com.intellij.diff.tools.util.base.HighlightPolicy;
|
||||
import com.intellij.diff.tools.util.base.IgnorePolicy;
|
||||
import com.intellij.diff.tools.util.base.TextDiffSettingsHolder.TextDiffSettings;
|
||||
import com.intellij.diff.util.Side;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.diff.tools.util.base.HighlightPolicy.*;
|
||||
import static com.intellij.diff.tools.util.base.IgnorePolicy.*;
|
||||
|
||||
public class SmartTextDiffProvider extends TwosideTextDiffProviderBase implements TwosideTextDiffProvider {
|
||||
private static final IgnorePolicy[] IGNORE_POLICIES = {DEFAULT, TRIM_WHITESPACES, IGNORE_WHITESPACES, IGNORE_WHITESPACES_CHUNKS, FORMATTING};
|
||||
private static final HighlightPolicy[] HIGHLIGHT_POLICIES = {BY_LINE, BY_WORD, BY_WORD_SPLIT, DO_NOT_HIGHLIGHT};
|
||||
|
||||
@Nullable private final Project myProject;
|
||||
@NotNull private final DiffContent myContent1;
|
||||
@NotNull private final DiffContent myContent2;
|
||||
@NotNull private final DiffIgnoredRangeProvider myProvider;
|
||||
|
||||
@Nullable
|
||||
public static TwosideTextDiffProvider create(@Nullable Project project,
|
||||
@NotNull ContentDiffRequest request,
|
||||
@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff) {
|
||||
if (!Registry.is("diff.smart.ignore.enabled")) return null;
|
||||
DiffContent content1 = Side.LEFT.select(request.getContents());
|
||||
DiffContent content2 = Side.RIGHT.select(request.getContents());
|
||||
DiffIgnoredRangeProvider ignoredRangeProvider = getIgnoredRangeProvider(project, content1, content2);
|
||||
if (ignoredRangeProvider == null) return null;
|
||||
return new SmartTextDiffProvider(project, content1, content2, settings, rediff, ignoredRangeProvider);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static TwosideTextDiffProvider.NoIgnore createNoIgnore(@Nullable Project project,
|
||||
@NotNull ContentDiffRequest request,
|
||||
@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff) {
|
||||
if (!Registry.is("diff.smart.ignore.enabled")) return null;
|
||||
DiffContent content1 = Side.LEFT.select(request.getContents());
|
||||
DiffContent content2 = Side.RIGHT.select(request.getContents());
|
||||
DiffIgnoredRangeProvider ignoredRangeProvider = getIgnoredRangeProvider(project, content1, content2);
|
||||
if (ignoredRangeProvider == null) return null;
|
||||
return new SmartTextDiffProvider.NoIgnore(project, content1, content2, settings, rediff, ignoredRangeProvider);
|
||||
}
|
||||
|
||||
private SmartTextDiffProvider(@Nullable Project project,
|
||||
@NotNull DiffContent content1,
|
||||
@NotNull DiffContent content2,
|
||||
@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff,
|
||||
@NotNull DiffIgnoredRangeProvider ignoredRangeProvider) {
|
||||
this(project, content1, content2, settings, rediff, ignoredRangeProvider, IGNORE_POLICIES, HIGHLIGHT_POLICIES);
|
||||
}
|
||||
|
||||
private SmartTextDiffProvider(@Nullable Project project,
|
||||
@NotNull DiffContent content1,
|
||||
@NotNull DiffContent content2,
|
||||
@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff,
|
||||
@NotNull DiffIgnoredRangeProvider ignoredRangeProvider,
|
||||
@NotNull IgnorePolicy[] ignorePolicies,
|
||||
@NotNull HighlightPolicy[] highlightPolicies) {
|
||||
super(settings, rediff, ignorePolicies, highlightPolicies);
|
||||
myProject = project;
|
||||
myContent1 = content1;
|
||||
myContent2 = content2;
|
||||
myProvider = ignoredRangeProvider;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getText(@NotNull IgnorePolicy option) {
|
||||
if (option == FORMATTING) return myProvider.getDescription();
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<LineFragment> doCompare(@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull IgnorePolicy ignorePolicy,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) {
|
||||
if (ignorePolicy == FORMATTING) {
|
||||
return compareIgnoreFormatting(text1, text2, innerFragments, indicator);
|
||||
}
|
||||
else {
|
||||
ComparisonPolicy policy = ignorePolicy.getComparisonPolicy();
|
||||
return SimpleTextDiffProvider.DEFAULT_COMPUTER.compute(text1, text2, policy, innerFragments, indicator);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<LineFragment> compareIgnoreFormatting(@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator) {
|
||||
List<TextRange> ranges1 = myProvider.getIgnoredRanges(myProject, text1, myContent1);
|
||||
List<TextRange> ranges2 = myProvider.getIgnoredRanges(myProject, text2, myContent2);
|
||||
|
||||
ComparisonManagerImpl comparisonManager = ComparisonManagerImpl.getInstanceImpl();
|
||||
return comparisonManager.compareLinesWithIgnoredRanges(text1, text2, ranges1, ranges2, innerFragments, indicator);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static DiffIgnoredRangeProvider getIgnoredRangeProvider(@Nullable Project project,
|
||||
@NotNull DiffContent content1,
|
||||
@NotNull DiffContent content2) {
|
||||
for (DiffIgnoredRangeProvider provider : DiffIgnoredRangeProvider.EP_NAME.getExtensions()) {
|
||||
if (provider.accepts(project, content1) &&
|
||||
provider.accepts(project, content2)) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static class NoIgnore extends SmartTextDiffProvider implements TwosideTextDiffProvider.NoIgnore {
|
||||
private NoIgnore(@Nullable Project project,
|
||||
@NotNull DiffContent content1,
|
||||
@NotNull DiffContent content2,
|
||||
@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff,
|
||||
@NotNull DiffIgnoredRangeProvider ignoredRangeProvider) {
|
||||
super(project, content1, content2, settings, rediff, ignoredRangeProvider,
|
||||
IGNORE_POLICIES, ArrayUtil.remove(HIGHLIGHT_POLICIES, DO_NOT_HIGHLIGHT));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<LineFragment> compare(@NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull ProgressIndicator indicator) {
|
||||
//noinspection ConstantConditions
|
||||
return super.compare(text1, text2, indicator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,11 @@ import com.intellij.diff.tools.util.base.TextDiffViewerUtil.IgnorePolicySettingA
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.Separator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.openapi.util.text.StringUtil.notNullize;
|
||||
import static com.intellij.util.containers.ContainerUtil.list;
|
||||
|
||||
public class TextDiffProviderBase implements TextDiffProvider {
|
||||
@@ -36,8 +38,8 @@ public class TextDiffProviderBase implements TextDiffProvider {
|
||||
@NotNull Runnable rediff,
|
||||
@NotNull IgnorePolicy[] ignorePolicies,
|
||||
@NotNull HighlightPolicy[] highlightPolicies) {
|
||||
myIgnorePolicySettingAction = new IgnorePolicySettingAction(settings, rediff, ignorePolicies);
|
||||
myHighlightPolicySettingAction = new HighlightPolicySettingAction(settings, rediff, highlightPolicies);
|
||||
myIgnorePolicySettingAction = new MyIgnorePolicySettingAction(settings, rediff, ignorePolicies);
|
||||
myHighlightPolicySettingAction = new MyHighlightPolicySettingAction(settings, rediff, highlightPolicies);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -69,4 +71,44 @@ public class TextDiffProviderBase implements TextDiffProvider {
|
||||
public boolean isHighlightingDisabled() {
|
||||
return myHighlightPolicySettingAction.getValue() == HighlightPolicy.DO_NOT_HIGHLIGHT;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
protected String getText(@NotNull IgnorePolicy option) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected String getText(@NotNull HighlightPolicy option) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private class MyIgnorePolicySettingAction extends IgnorePolicySettingAction {
|
||||
public MyIgnorePolicySettingAction(@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff,
|
||||
@NotNull IgnorePolicy[] ignorePolicies) {
|
||||
super(settings, rediff, ignorePolicies);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getText(@NotNull IgnorePolicy option) {
|
||||
return notNullize(TextDiffProviderBase.this.getText(option), super.getText(option));
|
||||
}
|
||||
}
|
||||
|
||||
private class MyHighlightPolicySettingAction extends HighlightPolicySettingAction {
|
||||
public MyHighlightPolicySettingAction(@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff,
|
||||
@NotNull HighlightPolicy[] highlightPolicies) {
|
||||
super(settings, rediff, highlightPolicies);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getText(@NotNull HighlightPolicy option) {
|
||||
return notNullize(TextDiffProviderBase.this.getText(option), super.getText(option));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.diff.tools.util.text;
|
||||
|
||||
import com.intellij.diff.comparison.ComparisonManager;
|
||||
import com.intellij.diff.comparison.ComparisonPolicy;
|
||||
import com.intellij.diff.fragments.LineFragment;
|
||||
import com.intellij.diff.tools.util.base.HighlightPolicy;
|
||||
import com.intellij.diff.tools.util.base.IgnorePolicy;
|
||||
import com.intellij.diff.tools.util.base.TextDiffSettingsHolder.TextDiffSettings;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
abstract class TwosideTextDiffProviderBase extends TextDiffProviderBase implements TwosideTextDiffProvider {
|
||||
protected TwosideTextDiffProviderBase(@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff,
|
||||
@NotNull IgnorePolicy[] ignorePolicies,
|
||||
@NotNull HighlightPolicy[] highlightPolicies) {
|
||||
super(settings, rediff, ignorePolicies, highlightPolicies);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<LineFragment> compare(@NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull ProgressIndicator indicator) {
|
||||
IgnorePolicy ignorePolicy = getIgnorePolicy();
|
||||
HighlightPolicy highlightPolicy = getHighlightPolicy();
|
||||
|
||||
if (!highlightPolicy.isShouldCompare()) return null;
|
||||
|
||||
ComparisonPolicy policy = ignorePolicy.getComparisonPolicy();
|
||||
boolean innerFragments = highlightPolicy.isFineFragments();
|
||||
boolean squashFragments = highlightPolicy.isShouldSquash();
|
||||
boolean trimFragments = ignorePolicy.isShouldTrimChunks();
|
||||
|
||||
indicator.checkCanceled();
|
||||
List<LineFragment> fragments = doCompare(text1, text2, ignorePolicy, innerFragments, indicator);
|
||||
|
||||
indicator.checkCanceled();
|
||||
return ComparisonManager.getInstance().processBlocks(fragments, text1, text2,
|
||||
policy, squashFragments, trimFragments);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract List<LineFragment> doCompare(@NotNull CharSequence text1,
|
||||
@NotNull CharSequence text2,
|
||||
@NotNull IgnorePolicy ignorePolicy,
|
||||
boolean innerFragments,
|
||||
@NotNull ProgressIndicator indicator);
|
||||
}
|
||||
@@ -36,10 +36,7 @@ import com.intellij.diff.requests.ContentDiffRequest;
|
||||
import com.intellij.diff.requests.DiffRequest;
|
||||
import com.intellij.diff.tools.util.base.TextDiffSettingsHolder.TextDiffSettings;
|
||||
import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
|
||||
import com.intellij.diff.tools.util.text.LineOffsets;
|
||||
import com.intellij.diff.tools.util.text.MergeInnerDifferences;
|
||||
import com.intellij.diff.tools.util.text.SimpleTextDiffProvider;
|
||||
import com.intellij.diff.tools.util.text.TwosideTextDiffProvider;
|
||||
import com.intellij.diff.tools.util.text.*;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.Disposable;
|
||||
@@ -600,24 +597,30 @@ public class DiffUtil {
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public static TwosideTextDiffProvider createTextDiffProvider(@NotNull ContentDiffRequest request,
|
||||
public static TwosideTextDiffProvider createTextDiffProvider(@Nullable Project project,
|
||||
@NotNull ContentDiffRequest request,
|
||||
@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff) {
|
||||
DiffUserDataKeysEx.DiffComputer diffComputer = request.getUserData(DiffUserDataKeysEx.CUSTOM_DIFF_COMPUTER);
|
||||
if (diffComputer != null) {
|
||||
return new SimpleTextDiffProvider(settings, rediff, diffComputer);
|
||||
}
|
||||
if (diffComputer != null) return new SimpleTextDiffProvider(settings, rediff, diffComputer);
|
||||
|
||||
TwosideTextDiffProvider smartProvider = SmartTextDiffProvider.create(project, request, settings, rediff);
|
||||
if (smartProvider != null) return smartProvider;
|
||||
|
||||
return new SimpleTextDiffProvider(settings, rediff);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static TwosideTextDiffProvider.NoIgnore createNoIgnoreTextDiffProvider(@NotNull ContentDiffRequest request,
|
||||
public static TwosideTextDiffProvider.NoIgnore createNoIgnoreTextDiffProvider(@Nullable Project project,
|
||||
@NotNull ContentDiffRequest request,
|
||||
@NotNull TextDiffSettings settings,
|
||||
@NotNull Runnable rediff) {
|
||||
DiffUserDataKeysEx.DiffComputer diffComputer = request.getUserData(DiffUserDataKeysEx.CUSTOM_DIFF_COMPUTER);
|
||||
if (diffComputer != null) {
|
||||
return new SimpleTextDiffProvider.NoIgnore(settings, rediff, diffComputer);
|
||||
}
|
||||
if (diffComputer != null) return new SimpleTextDiffProvider.NoIgnore(settings, rediff, diffComputer);
|
||||
|
||||
TwosideTextDiffProvider.NoIgnore smartProvider = SmartTextDiffProvider.createNoIgnore(project, request, settings, rediff);
|
||||
if (smartProvider != null) return smartProvider;
|
||||
|
||||
return new SimpleTextDiffProvider.NoIgnore(settings, rediff);
|
||||
}
|
||||
|
||||
|
||||
@@ -993,6 +993,8 @@
|
||||
<automaticRenamerFactory implementation="com.intellij.refactoring.rename.RelatedFilesRenamerFactory"/>
|
||||
<refactoring.moveHandler implementation="com.intellij.refactoring.move.moveFilesOrDirectories.MoveRelatedFilesHandler"
|
||||
order="before moveFileOrDir"/>
|
||||
|
||||
<diff.lang.DiffIgnoredRangeProvider implementation="com.intellij.diff.lang.JavaDiffIgnoredRangeProvider"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
|
||||
@@ -254,6 +254,7 @@
|
||||
<extensionPoint qualifiedName="com.intellij.diff.DiffTool" interface="com.intellij.diff.DiffTool"/>
|
||||
<extensionPoint qualifiedName="com.intellij.diff.merge.MergeTool" interface="com.intellij.diff.merge.MergeTool"/>
|
||||
<extensionPoint qualifiedName="com.intellij.diff.DiffExtension" interface="com.intellij.diff.DiffExtension"/>
|
||||
<extensionPoint qualifiedName="com.intellij.diff.lang.DiffIgnoredRangeProvider" interface="com.intellij.diff.lang.DiffIgnoredRangeProvider"/>
|
||||
<extensionPoint name="testStatusListener" interface="com.intellij.execution.testframework.TestStatusListener"/>
|
||||
|
||||
<extensionPoint name="runConfigurationBeforeRunProviderDelegate" interface="com.intellij.execution.impl.RunConfigurationBeforeRunProviderDelegate"/>
|
||||
|
||||
@@ -531,6 +531,8 @@ diff.merge.resolve.conflict.action.use.greedy.approach.description=Use greedy he
|
||||
diff.enable.psi.highlighting=true
|
||||
diff.enable.psi.highlighting.description=Enable advanced highlighting and code navigation in VCS content in diff viewers.
|
||||
diff.pass.rich.editor.context=false
|
||||
diff.smart.ignore.enabled=true
|
||||
diff.smart.ignore.enabled.description=Enable syntax-aware ignore option in text diff viewer
|
||||
|
||||
search.everywhere.toolwindows=true
|
||||
search.everywhere.files=true
|
||||
|
||||
Reference in New Issue
Block a user