mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
VCS: extension point PatchEP added to allow read/write custom information for each file in patch
This commit is contained in:
@@ -41,7 +41,7 @@ public class PatchCreator {
|
||||
Writer writer = new OutputStreamWriter(new FileOutputStream(filePath));
|
||||
try {
|
||||
String lineSeparator = CodeStyleSettingsManager.getInstance(p).getCurrentSettings().getLineSeparator();
|
||||
UnifiedDiffWriter.write(patches, writer, lineSeparator);
|
||||
UnifiedDiffWriter.write(p, patches, writer, lineSeparator);
|
||||
}
|
||||
finally {
|
||||
writer.close();
|
||||
|
||||
@@ -16,18 +16,15 @@
|
||||
|
||||
package com.intellij.history.integration;
|
||||
|
||||
import com.intellij.openapi.diff.impl.patch.BinaryFilePatch;
|
||||
import com.intellij.openapi.diff.impl.patch.FilePatch;
|
||||
import com.intellij.openapi.diff.impl.patch.PatchReader;
|
||||
import com.intellij.openapi.diff.impl.patch.PatchVirtualFileReader;
|
||||
import com.intellij.openapi.diff.impl.patch.*;
|
||||
import com.intellij.openapi.diff.impl.patch.formove.PatchApplier;
|
||||
import com.intellij.openapi.vcs.ObjectsConvertor;
|
||||
import com.intellij.openapi.vcs.changes.LocalChangeList;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class PatchingTestCase extends IntegrationTestCase {
|
||||
@@ -47,14 +44,9 @@ public abstract class PatchingTestCase extends IntegrationTestCase {
|
||||
}
|
||||
|
||||
protected void applyPatch() throws Exception {
|
||||
List<FilePatch> patches = new ArrayList<FilePatch>();
|
||||
PatchReader reader = PatchVirtualFileReader.create(LocalFileSystem.getInstance().refreshAndFindFileByPath(patchFilePath));
|
||||
|
||||
while (true) {
|
||||
FilePatch p = reader.readNextPatch();
|
||||
if (p == null) break;
|
||||
patches.add(p);
|
||||
}
|
||||
List<FilePatch> patches = ObjectsConvertor.<FilePatch, TextFilePatch>downcast(reader.readAllPatches());
|
||||
|
||||
new PatchApplier<BinaryFilePatch>(myProject, myRoot, patches, (LocalChangeList) null, null).execute();
|
||||
}
|
||||
|
||||
@@ -39,5 +39,6 @@
|
||||
|
||||
<extensionPoint name="vcsPopupProvider"
|
||||
interface="com.intellij.openapi.vcs.actions.VcsQuickListContentProvider"/>
|
||||
<extensionPoint name="patch.extension" interface="com.intellij.openapi.diff.impl.patch.PatchEP" area="IDEA_PROJECT"/>
|
||||
|
||||
</extensionPoints>
|
||||
@@ -19,6 +19,7 @@ import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.text.CharArrayCharSequence;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -36,8 +37,17 @@ public class LineTokenizer {
|
||||
}
|
||||
|
||||
private static String[] tokenize(final CharSequence chars, final boolean includeSeparators, final boolean skipLastEmptyLine) {
|
||||
final List<String> strings = tokenizeIntoList(chars, includeSeparators, skipLastEmptyLine);
|
||||
return strings.isEmpty() ? ArrayUtil.EMPTY_STRING_ARRAY : ArrayUtil.toStringArray(strings);
|
||||
}
|
||||
|
||||
public static List<String> tokenizeIntoList(final CharSequence chars, final boolean includeSeparators) {
|
||||
return tokenizeIntoList(chars, includeSeparators, true);
|
||||
}
|
||||
|
||||
public static List<String> tokenizeIntoList(final CharSequence chars, final boolean includeSeparators, final boolean skipLastEmptyLine) {
|
||||
if (chars == null || chars.length() == 0){
|
||||
return ArrayUtil.EMPTY_STRING_ARRAY;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
LineTokenizer tokenizer = new LineTokenizer(chars);
|
||||
@@ -57,7 +67,7 @@ public class LineTokenizer {
|
||||
|
||||
if (!skipLastEmptyLine && stringEndsWithSeparator(tokenizer)) lines.add("");
|
||||
|
||||
return ArrayUtil.toStringArray(lines);
|
||||
return lines;
|
||||
}
|
||||
|
||||
public static int calcLineCount(final CharSequence chars, final boolean skipLastEmptyLine) {
|
||||
|
||||
@@ -911,6 +911,10 @@ public class StringUtil {
|
||||
return s == null || s.length() == 0;
|
||||
}
|
||||
|
||||
public static boolean isEmpty(final CharSequence cs) {
|
||||
return cs == null || cs.length() == 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String notNullize(final String s) {
|
||||
return notNullize(s, "");
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.openapi.diff.impl.patch;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
* Date: 7/11/11
|
||||
* Time: 11:43 AM
|
||||
*/
|
||||
public interface PatchEP {
|
||||
ExtensionPointName<PatchEP> EP_NAME = ExtensionPointName.create("com.intellij.patch.extension");
|
||||
@NotNull
|
||||
String getName();
|
||||
/**
|
||||
* @param path - before path, if exist, otherwise after path
|
||||
*/
|
||||
@Nullable
|
||||
CharSequence provideContent(@NotNull final String path);
|
||||
/**
|
||||
* @param path - before path, if exist, otherwise after path
|
||||
*/
|
||||
void consumeContent(@NotNull final String path, @NotNull final CharSequence content);
|
||||
}
|
||||
@@ -23,22 +23,25 @@
|
||||
package com.intellij.openapi.diff.impl.patch;
|
||||
|
||||
import com.intellij.openapi.util.text.LineTokenizer;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.changes.TransparentlyFailedValue;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PatchReader {
|
||||
@NonNls public static final String NO_NEWLINE_SIGNATURE = "\\ No newline at end of file";
|
||||
private final List<String> myLines;
|
||||
private final PatchReader.PatchContentParser myPatchContentParser;
|
||||
private final AdditionalInfoParser myAdditionalInfoParser;
|
||||
private List<TextFilePatch> myPatches;
|
||||
|
||||
private enum DiffFormat { CONTEXT, UNIFIED }
|
||||
|
||||
private final String[] myLines;
|
||||
private int myLineIndex = 0;
|
||||
private DiffFormat myDiffFormat = null;
|
||||
@NonNls private static final String CONTEXT_HUNK_PREFIX = "***************";
|
||||
@NonNls private static final String CONTEXT_FILE_PREFIX = "*** ";
|
||||
@NonNls private static final Pattern ourUnifiedHunkStartPattern = Pattern.compile("@@ -(\\d+)(,(\\d+))? \\+(\\d+)(,(\\d+))? @@.*");
|
||||
@@ -46,279 +49,455 @@ public class PatchReader {
|
||||
@NonNls private static final Pattern ourContextAfterHunkStartPattern = Pattern.compile("--- (\\d+),(\\d+) ----");
|
||||
|
||||
public PatchReader(CharSequence patchContent) {
|
||||
myLines = LineTokenizer.tokenize(patchContent, false);
|
||||
myLines = LineTokenizer.tokenizeIntoList(patchContent, false);
|
||||
myAdditionalInfoParser = new AdditionalInfoParser();
|
||||
myPatchContentParser = new PatchContentParser();
|
||||
}
|
||||
|
||||
public List<TextFilePatch> readAllPatches() throws PatchSyntaxException {
|
||||
List<TextFilePatch> result = new ArrayList<TextFilePatch>();
|
||||
while(true) {
|
||||
TextFilePatch patch = readNextPatch();
|
||||
if (patch == null) break;
|
||||
result.add(patch);
|
||||
}
|
||||
return result;
|
||||
parseAllPatches();
|
||||
return myPatches;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TextFilePatch readNextPatch() throws PatchSyntaxException {
|
||||
while (myLineIndex < myLines.length) {
|
||||
String curLine = myLines [myLineIndex];
|
||||
if (curLine.startsWith("--- ") && (myDiffFormat == null || myDiffFormat == DiffFormat.UNIFIED)) {
|
||||
public List<TextFilePatch> getPatches() {
|
||||
return myPatches;
|
||||
}
|
||||
|
||||
public void parseAllPatches() throws PatchSyntaxException {
|
||||
final ListIterator<String> iterator = myLines.listIterator();
|
||||
if (! iterator.hasNext()) {
|
||||
myPatches = Collections.emptyList();
|
||||
return;
|
||||
}
|
||||
|
||||
String next;
|
||||
boolean containsAdditional = false;
|
||||
while (iterator.hasNext()) {
|
||||
next = iterator.next();
|
||||
final boolean containsAdditionalNow = myAdditionalInfoParser.testIsStart(next);
|
||||
if (containsAdditionalNow && containsAdditional) {
|
||||
myAdditionalInfoParser.acceptError(new PatchSyntaxException(iterator.previousIndex(), "Contains additional information without patch itself"));
|
||||
}
|
||||
if (containsAdditionalNow) {
|
||||
containsAdditional = containsAdditionalNow;
|
||||
myAdditionalInfoParser.parse(next, iterator);
|
||||
if (! iterator.hasNext()) {
|
||||
myAdditionalInfoParser.acceptError(new PatchSyntaxException(iterator.previousIndex(), "Contains additional information without patch itself"));
|
||||
break;
|
||||
}
|
||||
next = iterator.next();
|
||||
}
|
||||
|
||||
if (myPatchContentParser.testIsStart(next)) {
|
||||
myPatchContentParser.parse(next, iterator);
|
||||
//iterator.previous(); // to correctly initialize next
|
||||
if (containsAdditional) {
|
||||
final String lastName = myPatchContentParser.getLastName();
|
||||
if (lastName == null) {
|
||||
myAdditionalInfoParser.acceptError(new PatchSyntaxException(iterator.previousIndex(), "Contains additional information without patch itself"));
|
||||
} else {
|
||||
myAdditionalInfoParser.copyToResult(lastName);
|
||||
}
|
||||
}
|
||||
containsAdditional = false;
|
||||
}
|
||||
}
|
||||
myPatches = myPatchContentParser.getResult();
|
||||
}
|
||||
|
||||
public TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> getAdditionalInfo(final Set<String> filterByPaths) {
|
||||
final TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException>
|
||||
value = new TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException>();
|
||||
|
||||
final Map<String, Map<String, CharSequence>> map = myAdditionalInfoParser.getResultMap();
|
||||
final Map<String, Map<String, CharSequence>>newMap = new HashMap<String, Map<String, CharSequence>>();
|
||||
|
||||
for (Map.Entry<String, Map<String, CharSequence>> entry : map.entrySet()) {
|
||||
final Map<String, CharSequence> innerMap = entry.getValue();
|
||||
if (filterByPaths == null || filterByPaths.contains(entry.getKey())) {
|
||||
newMap.put(entry.getKey(), innerMap);
|
||||
}
|
||||
}
|
||||
value.set(newMap);
|
||||
final PatchSyntaxException e = myAdditionalInfoParser.getSyntaxException();
|
||||
if (e != null) {
|
||||
value.fail(e);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static class AdditionalInfoParser implements Parser {
|
||||
// first is path!
|
||||
private final Map<String,Map<String, CharSequence>> myResultMap;
|
||||
private Map<String, CharSequence> myAddMap;
|
||||
private PatchSyntaxException mySyntaxException;
|
||||
|
||||
private AdditionalInfoParser() {
|
||||
myAddMap = new HashMap<String, CharSequence>();
|
||||
myResultMap = new HashMap<String, Map<String, CharSequence>>();
|
||||
}
|
||||
|
||||
public PatchSyntaxException getSyntaxException() {
|
||||
return mySyntaxException;
|
||||
}
|
||||
|
||||
public Map<String, Map<String, CharSequence>> getResultMap() {
|
||||
return myResultMap;
|
||||
}
|
||||
|
||||
public void copyToResult(final String filePath) {
|
||||
if (myAddMap != null && ! myAddMap.isEmpty()) {
|
||||
myResultMap.put(filePath, myAddMap);
|
||||
myAddMap = new HashMap<String, CharSequence>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testIsStart(String start) {
|
||||
if (mySyntaxException != null) return false; // stop on first error
|
||||
return start != null && start.contains(UnifiedDiffWriter.ADDITIONAL_PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(String start, ListIterator<String> iterator) {
|
||||
if (! iterator.hasNext()) {
|
||||
mySyntaxException = new PatchSyntaxException(iterator.previousIndex(), "Empty additional info header");
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
final String header = iterator.next();
|
||||
final int idxHead = header.indexOf(UnifiedDiffWriter.ADD_INFO_HEADER);
|
||||
if (idxHead == -1) {
|
||||
if (myAddMap.isEmpty()) {
|
||||
mySyntaxException = new PatchSyntaxException(iterator.previousIndex(), "Empty additional info header");
|
||||
}
|
||||
iterator.previous();
|
||||
return;
|
||||
}
|
||||
|
||||
final String subsystem = header.substring(idxHead + UnifiedDiffWriter.ADD_INFO_HEADER.length()).trim();
|
||||
if (! iterator.hasNext()) {
|
||||
mySyntaxException = new PatchSyntaxException(iterator.previousIndex(), "Empty '" + subsystem + "' data section");
|
||||
return;
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
myAddMap.put(subsystem, sb);
|
||||
while (iterator.hasNext()) {
|
||||
final String line = iterator.next();
|
||||
if (! line.startsWith(UnifiedDiffWriter.ADD_INFO_LINE_START)) {
|
||||
iterator.previous();
|
||||
break;
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append(StringUtil.unescapeStringCharacters(line.substring(UnifiedDiffWriter.ADD_INFO_LINE_START.length())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void acceptError(PatchSyntaxException e) {
|
||||
mySyntaxException = e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class PatchContentParser implements Parser {
|
||||
private DiffFormat myDiffFormat = null;
|
||||
private final List<TextFilePatch> myPatches;
|
||||
|
||||
private PatchContentParser() {
|
||||
myPatches = new SmartList<TextFilePatch>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testIsStart(String start) {
|
||||
if (start.startsWith("--- ") && (myDiffFormat == null || myDiffFormat == DiffFormat.UNIFIED)) {
|
||||
myDiffFormat = DiffFormat.UNIFIED;
|
||||
return readPatch(curLine);
|
||||
return true;
|
||||
}
|
||||
else if (curLine.startsWith(CONTEXT_FILE_PREFIX) && (myDiffFormat == null || myDiffFormat == DiffFormat.CONTEXT)) {
|
||||
else if (start.startsWith(CONTEXT_FILE_PREFIX) && (myDiffFormat == null || myDiffFormat == DiffFormat.CONTEXT)) {
|
||||
myDiffFormat = DiffFormat.CONTEXT;
|
||||
return readPatch(curLine);
|
||||
return true;
|
||||
}
|
||||
myLineIndex++;
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private TextFilePatch readPatch(String curLine) throws PatchSyntaxException {
|
||||
final TextFilePatch curPatch;
|
||||
curPatch = new TextFilePatch();
|
||||
extractFileName(curLine, curPatch, true);
|
||||
myLineIndex++;
|
||||
curLine = myLines [myLineIndex];
|
||||
String secondNamePrefix = myDiffFormat == DiffFormat.UNIFIED ? "+++ " : "--- ";
|
||||
if (!curLine.startsWith(secondNamePrefix)) {
|
||||
throw new PatchSyntaxException(myLineIndex, "Second file name expected");
|
||||
@Override
|
||||
public void parse(String start, ListIterator<String> iterator) throws PatchSyntaxException {
|
||||
final TextFilePatch patch = readPatch(start, iterator);
|
||||
if (patch != null) {
|
||||
myPatches.add(patch);
|
||||
}
|
||||
}
|
||||
extractFileName(curLine, curPatch, false);
|
||||
myLineIndex++;
|
||||
while(myLineIndex < myLines.length) {
|
||||
PatchHunk hunk;
|
||||
if (myDiffFormat == DiffFormat.UNIFIED) {
|
||||
hunk = readNextHunkUnified();
|
||||
|
||||
public List<TextFilePatch> getResult() throws PatchSyntaxException {
|
||||
return myPatches;
|
||||
}
|
||||
|
||||
private TextFilePatch readPatch(String curLine, ListIterator<String> iterator) throws PatchSyntaxException {
|
||||
final TextFilePatch curPatch = new TextFilePatch();
|
||||
extractFileName(curLine, curPatch, true);
|
||||
|
||||
if (! iterator.hasNext()) throw new PatchSyntaxException(iterator.previousIndex(), "Second file name expected");
|
||||
curLine = iterator.next();
|
||||
String secondNamePrefix = myDiffFormat == DiffFormat.UNIFIED ? "+++ " : "--- ";
|
||||
if (! curLine.startsWith(secondNamePrefix)) {
|
||||
throw new PatchSyntaxException(iterator.previousIndex(), "Second file name expected");
|
||||
}
|
||||
extractFileName(curLine, curPatch, false);
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
PatchHunk hunk;
|
||||
if (myDiffFormat == DiffFormat.UNIFIED) {
|
||||
hunk = readNextHunkUnified(iterator);
|
||||
}
|
||||
else {
|
||||
hunk = readNextHunkContext(iterator);
|
||||
}
|
||||
if (hunk == null) break;
|
||||
curPatch.addHunk(hunk);
|
||||
}
|
||||
return curPatch;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PatchHunk readNextHunkUnified(ListIterator<String> iterator) throws PatchSyntaxException {
|
||||
String curLine = null;
|
||||
int numIncrements = 0;
|
||||
while (iterator.hasNext()) {
|
||||
curLine = iterator.next();
|
||||
++ numIncrements;
|
||||
if (curLine.startsWith("--- ")) {
|
||||
for (int i = 0; i < numIncrements; i++) {
|
||||
iterator.previous();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (curLine.startsWith("@@ ")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! iterator.hasNext()) return null;
|
||||
|
||||
Matcher m = ourUnifiedHunkStartPattern.matcher(curLine);
|
||||
if (!m.matches()) {
|
||||
throw new PatchSyntaxException(iterator.previousIndex(), "Unknown hunk start syntax");
|
||||
}
|
||||
int startLineBefore = Integer.parseInt(m.group(1));
|
||||
final String linesBeforeText = m.group(3);
|
||||
int linesBefore = linesBeforeText == null ? 1 : Integer.parseInt(linesBeforeText);
|
||||
int startLineAfter = Integer.parseInt(m.group(4));
|
||||
final String linesAfterText = m.group(6);
|
||||
int linesAfter = linesAfterText == null ? 1 : Integer.parseInt(linesAfterText);
|
||||
PatchHunk hunk = new PatchHunk(startLineBefore-1, startLineBefore+linesBefore-1, startLineAfter-1, startLineAfter+linesAfter-1);
|
||||
|
||||
PatchLine lastLine = null;
|
||||
while (iterator.hasNext()) {
|
||||
String hunkCurLine = iterator.next();
|
||||
if (lastLine != null && hunkCurLine.startsWith(NO_NEWLINE_SIGNATURE)) {
|
||||
lastLine.setSuppressNewLine(true);
|
||||
continue;
|
||||
}
|
||||
if (hunkCurLine.startsWith("--- ")) {
|
||||
iterator.previous();
|
||||
break;
|
||||
}
|
||||
lastLine = parsePatchLine(hunkCurLine, 1);
|
||||
if (lastLine == null) {
|
||||
iterator.previous();
|
||||
break;
|
||||
}
|
||||
hunk.addLine(lastLine);
|
||||
}
|
||||
return hunk;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLastName() {
|
||||
if (myPatches.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
hunk = readNextHunkContext();
|
||||
final TextFilePatch patch = myPatches.get(myPatches.size() - 1);
|
||||
return patch.getBeforeName() == null ? patch.getAfterName() : patch.getBeforeName();
|
||||
}
|
||||
if (hunk == null) break;
|
||||
curPatch.addHunk(hunk);
|
||||
}
|
||||
return curPatch;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PatchHunk readNextHunkUnified() throws PatchSyntaxException {
|
||||
while(myLineIndex < myLines.length) {
|
||||
String curLine = myLines [myLineIndex];
|
||||
if (curLine.startsWith("--- ") && myLineIndex < myLines.length-1 && myLines [myLineIndex+1].startsWith("+++ ")) {
|
||||
@Nullable
|
||||
private static PatchLine parsePatchLine(final String line, final int prefixLength) {
|
||||
PatchLine.Type type;
|
||||
if (line.startsWith("+")) {
|
||||
type = PatchLine.Type.ADD;
|
||||
}
|
||||
else if (line.startsWith("-")) {
|
||||
type = PatchLine.Type.REMOVE;
|
||||
}
|
||||
else if (line.startsWith(" ")) {
|
||||
type = PatchLine.Type.CONTEXT;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
if (curLine.startsWith("@@ ")) {
|
||||
break;
|
||||
String lineText;
|
||||
if (line.length() < prefixLength) {
|
||||
lineText = "";
|
||||
}
|
||||
myLineIndex++;
|
||||
}
|
||||
if (myLineIndex == myLines.length) {
|
||||
return null;
|
||||
else {
|
||||
lineText = line.substring(prefixLength);
|
||||
}
|
||||
return new PatchLine(type, lineText);
|
||||
}
|
||||
|
||||
Matcher m = ourUnifiedHunkStartPattern.matcher(myLines [myLineIndex]);
|
||||
if (!m.matches()) {
|
||||
throw new PatchSyntaxException(myLineIndex, "Unknown hunk start syntax");
|
||||
}
|
||||
int startLineBefore = Integer.parseInt(m.group(1));
|
||||
final String linesBeforeText = m.group(3);
|
||||
int linesBefore = linesBeforeText == null ? 1 : Integer.parseInt(linesBeforeText);
|
||||
int startLineAfter = Integer.parseInt(m.group(4));
|
||||
final String linesAfterText = m.group(6);
|
||||
int linesAfter = linesAfterText == null ? 1 : Integer.parseInt(linesAfterText);
|
||||
PatchHunk hunk = new PatchHunk(startLineBefore-1, startLineBefore+linesBefore-1, startLineAfter-1, startLineAfter+linesAfter-1);
|
||||
myLineIndex++;
|
||||
PatchLine lastLine = null;
|
||||
while(myLineIndex < myLines.length) {
|
||||
String curLine = myLines [myLineIndex];
|
||||
if (lastLine != null && curLine.startsWith(NO_NEWLINE_SIGNATURE)) {
|
||||
lastLine.setSuppressNewLine(true);
|
||||
myLineIndex++;
|
||||
continue;
|
||||
@Nullable
|
||||
private PatchHunk readNextHunkContext(ListIterator<String> iterator) throws PatchSyntaxException {
|
||||
while (iterator.hasNext()) {
|
||||
String curLine = iterator.next();
|
||||
if (curLine.startsWith(CONTEXT_FILE_PREFIX)) {
|
||||
return null;
|
||||
}
|
||||
if (curLine.startsWith(CONTEXT_HUNK_PREFIX)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (curLine.startsWith("--- ")) {
|
||||
break;
|
||||
}
|
||||
lastLine = parsePatchLine(curLine, 1);
|
||||
if (lastLine == null) {
|
||||
break;
|
||||
}
|
||||
hunk.addLine(lastLine);
|
||||
myLineIndex++;
|
||||
}
|
||||
return hunk;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PatchLine parsePatchLine(final String line, final int prefixLength) {
|
||||
PatchLine.Type type;
|
||||
if (line.startsWith("+")) {
|
||||
type = PatchLine.Type.ADD;
|
||||
}
|
||||
else if (line.startsWith("-")) {
|
||||
type = PatchLine.Type.REMOVE;
|
||||
}
|
||||
else if (line.startsWith(" ")) {
|
||||
type = PatchLine.Type.CONTEXT;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
String lineText;
|
||||
if (line.length() < prefixLength) {
|
||||
lineText = "";
|
||||
}
|
||||
else {
|
||||
lineText = line.substring(prefixLength);
|
||||
}
|
||||
return new PatchLine(type, lineText);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PatchHunk readNextHunkContext() throws PatchSyntaxException {
|
||||
while(myLineIndex < myLines.length) {
|
||||
String curLine = myLines [myLineIndex];
|
||||
if (curLine.startsWith(CONTEXT_FILE_PREFIX)) {
|
||||
if (! iterator.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
if (curLine.startsWith(CONTEXT_HUNK_PREFIX)) {
|
||||
break;
|
||||
Matcher beforeMatcher = ourContextBeforeHunkStartPattern.matcher(iterator.next());
|
||||
if (! beforeMatcher.matches()) {
|
||||
throw new PatchSyntaxException(iterator.previousIndex(), "Unknown before hunk start syntax");
|
||||
}
|
||||
myLineIndex++;
|
||||
}
|
||||
if (myLineIndex == myLines.length) {
|
||||
return null;
|
||||
}
|
||||
myLineIndex++;
|
||||
Matcher beforeMatcher = ourContextBeforeHunkStartPattern.matcher(myLines [myLineIndex]);
|
||||
if (!beforeMatcher.matches()) {
|
||||
throw new PatchSyntaxException(myLineIndex, "Unknown before hunk start syntax");
|
||||
}
|
||||
myLineIndex++;
|
||||
List<String> beforeLines = readContextDiffLines();
|
||||
if (myLineIndex == myLines.length) {
|
||||
throw new PatchSyntaxException(myLineIndex, "Missing after hunk");
|
||||
}
|
||||
Matcher afterMatcher = ourContextAfterHunkStartPattern.matcher(myLines [myLineIndex]);
|
||||
if (!afterMatcher.matches()) {
|
||||
throw new PatchSyntaxException(myLineIndex, "Unknown after hunk start syntax");
|
||||
}
|
||||
myLineIndex++;
|
||||
List<String> afterLines = readContextDiffLines();
|
||||
int startLineBefore = Integer.parseInt(beforeMatcher.group(1));
|
||||
int endLineBefore = Integer.parseInt(beforeMatcher.group(2));
|
||||
int startLineAfter = Integer.parseInt(afterMatcher.group(1));
|
||||
int endLineAfter = Integer.parseInt(afterMatcher.group(2));
|
||||
PatchHunk hunk = new PatchHunk(startLineBefore-1, endLineBefore-1, startLineAfter-1, endLineAfter-1);
|
||||
List<String> beforeLines = readContextDiffLines(iterator);
|
||||
if (! iterator.hasNext()) {
|
||||
throw new PatchSyntaxException(iterator.previousIndex(), "Missing after hunk");
|
||||
}
|
||||
Matcher afterMatcher = ourContextAfterHunkStartPattern.matcher(iterator.next());
|
||||
if (! afterMatcher.matches()) {
|
||||
throw new PatchSyntaxException(iterator.previousIndex(), "Unknown after hunk start syntax");
|
||||
}
|
||||
//if (! iterator.hasNext()) {
|
||||
//throw new PatchSyntaxException(iterator.previousIndex(), "Unexpected patch end");
|
||||
//}
|
||||
List<String> afterLines = readContextDiffLines(iterator);
|
||||
int startLineBefore = Integer.parseInt(beforeMatcher.group(1));
|
||||
int endLineBefore = Integer.parseInt(beforeMatcher.group(2));
|
||||
int startLineAfter = Integer.parseInt(afterMatcher.group(1));
|
||||
int endLineAfter = Integer.parseInt(afterMatcher.group(2));
|
||||
PatchHunk hunk = new PatchHunk(startLineBefore-1, endLineBefore-1, startLineAfter-1, endLineAfter-1);
|
||||
|
||||
int beforeLineIndex = 0;
|
||||
int afterLineIndex = 0;
|
||||
PatchLine lastBeforePatchLine = null;
|
||||
PatchLine lastAfterPatchLine = null;
|
||||
if (beforeLines.size() == 0) {
|
||||
for(String line: afterLines) {
|
||||
hunk.addLine(parsePatchLine(line, 2));
|
||||
int beforeLineIndex = 0;
|
||||
int afterLineIndex = 0;
|
||||
PatchLine lastBeforePatchLine = null;
|
||||
PatchLine lastAfterPatchLine = null;
|
||||
if (beforeLines.size() == 0) {
|
||||
for(String line: afterLines) {
|
||||
hunk.addLine(parsePatchLine(line, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (afterLines.size() == 0) {
|
||||
for(String line: beforeLines) {
|
||||
hunk.addLine(parsePatchLine(line, 2));
|
||||
else if (afterLines.size() == 0) {
|
||||
for(String line: beforeLines) {
|
||||
hunk.addLine(parsePatchLine(line, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
while(beforeLineIndex < beforeLines.size() || afterLineIndex < afterLines.size()) {
|
||||
String beforeLine = beforeLineIndex >= beforeLines.size() ? null : beforeLines.get(beforeLineIndex);
|
||||
String afterLine = afterLineIndex >= afterLines.size() ? null : afterLines.get(afterLineIndex);
|
||||
if (startsWith(beforeLine, NO_NEWLINE_SIGNATURE) && lastBeforePatchLine != null) {
|
||||
lastBeforePatchLine.setSuppressNewLine(true);
|
||||
beforeLineIndex++;
|
||||
}
|
||||
else if (startsWith(afterLine, NO_NEWLINE_SIGNATURE) && lastAfterPatchLine != null) {
|
||||
lastAfterPatchLine.setSuppressNewLine(true);
|
||||
afterLineIndex++;
|
||||
}
|
||||
else if (startsWith(beforeLine, " ") &&
|
||||
(startsWith(afterLine, " ") || afterLine == null /* handle some weird cases with line breaks truncated at EOF */ )) {
|
||||
addContextDiffLine(hunk, beforeLine, PatchLine.Type.CONTEXT);
|
||||
beforeLineIndex++;
|
||||
afterLineIndex++;
|
||||
}
|
||||
else if (startsWith(beforeLine, "-")) {
|
||||
lastBeforePatchLine = addContextDiffLine(hunk, beforeLine, PatchLine.Type.REMOVE);
|
||||
beforeLineIndex++;
|
||||
}
|
||||
else if (startsWith(afterLine, "+")) {
|
||||
lastAfterPatchLine = addContextDiffLine(hunk, afterLine, PatchLine.Type.ADD);
|
||||
afterLineIndex++;
|
||||
}
|
||||
else if (startsWith(beforeLine, "!") && startsWith(afterLine, "!")) {
|
||||
while(beforeLineIndex < beforeLines.size() && beforeLines.get(beforeLineIndex).startsWith("! ")) {
|
||||
lastBeforePatchLine = addContextDiffLine(hunk, beforeLines.get(beforeLineIndex), PatchLine.Type.REMOVE);
|
||||
else {
|
||||
while(beforeLineIndex < beforeLines.size() || afterLineIndex < afterLines.size()) {
|
||||
String beforeLine = beforeLineIndex >= beforeLines.size() ? null : beforeLines.get(beforeLineIndex);
|
||||
String afterLine = afterLineIndex >= afterLines.size() ? null : afterLines.get(afterLineIndex);
|
||||
if (startsWith(beforeLine, NO_NEWLINE_SIGNATURE) && lastBeforePatchLine != null) {
|
||||
lastBeforePatchLine.setSuppressNewLine(true);
|
||||
beforeLineIndex++;
|
||||
}
|
||||
|
||||
while(afterLineIndex < afterLines.size() && afterLines.get(afterLineIndex).startsWith("! ")) {
|
||||
lastAfterPatchLine = addContextDiffLine(hunk, afterLines.get(afterLineIndex), PatchLine.Type.ADD);
|
||||
else if (startsWith(afterLine, NO_NEWLINE_SIGNATURE) && lastAfterPatchLine != null) {
|
||||
lastAfterPatchLine.setSuppressNewLine(true);
|
||||
afterLineIndex++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new PatchSyntaxException(-1, "Unknown line prefix");
|
||||
else if (startsWith(beforeLine, " ") &&
|
||||
(startsWith(afterLine, " ") || afterLine == null /* handle some weird cases with line breaks truncated at EOF */ )) {
|
||||
addContextDiffLine(hunk, beforeLine, PatchLine.Type.CONTEXT);
|
||||
beforeLineIndex++;
|
||||
afterLineIndex++;
|
||||
}
|
||||
else if (startsWith(beforeLine, "-")) {
|
||||
lastBeforePatchLine = addContextDiffLine(hunk, beforeLine, PatchLine.Type.REMOVE);
|
||||
beforeLineIndex++;
|
||||
}
|
||||
else if (startsWith(afterLine, "+")) {
|
||||
lastAfterPatchLine = addContextDiffLine(hunk, afterLine, PatchLine.Type.ADD);
|
||||
afterLineIndex++;
|
||||
}
|
||||
else if (startsWith(beforeLine, "!") && startsWith(afterLine, "!")) {
|
||||
while(beforeLineIndex < beforeLines.size() && beforeLines.get(beforeLineIndex).startsWith("! ")) {
|
||||
lastBeforePatchLine = addContextDiffLine(hunk, beforeLines.get(beforeLineIndex), PatchLine.Type.REMOVE);
|
||||
beforeLineIndex++;
|
||||
}
|
||||
|
||||
while(afterLineIndex < afterLines.size() && afterLines.get(afterLineIndex).startsWith("! ")) {
|
||||
lastAfterPatchLine = addContextDiffLine(hunk, afterLines.get(afterLineIndex), PatchLine.Type.ADD);
|
||||
afterLineIndex++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new PatchSyntaxException(-1, "Unknown line prefix");
|
||||
}
|
||||
}
|
||||
}
|
||||
return hunk;
|
||||
}
|
||||
return hunk;
|
||||
}
|
||||
|
||||
private static boolean startsWith(@Nullable final String line, final String prefix) {
|
||||
return line != null && line.startsWith(prefix);
|
||||
}
|
||||
|
||||
private static PatchLine addContextDiffLine(final PatchHunk hunk, final String line, final PatchLine.Type type) {
|
||||
final PatchLine patchLine = new PatchLine(type, line.length() < 2 ? "" : line.substring(2));
|
||||
hunk.addLine(patchLine);
|
||||
return patchLine;
|
||||
}
|
||||
|
||||
private List<String> readContextDiffLines() {
|
||||
ArrayList<String> result = new ArrayList<String>();
|
||||
while(myLineIndex < myLines.length) {
|
||||
final String line = myLines[myLineIndex];
|
||||
if (!line.startsWith(" ") && !line.startsWith("+ ") && !line.startsWith("- ") && !line.startsWith("! ") &&
|
||||
!line.startsWith(NO_NEWLINE_SIGNATURE)) {
|
||||
break;
|
||||
}
|
||||
result.add(line);
|
||||
myLineIndex++;
|
||||
private static boolean startsWith(@Nullable final String line, final String prefix) {
|
||||
return line != null && line.startsWith(prefix);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void extractFileName(final String curLine, final FilePatch patch, final boolean before) {
|
||||
String fileName = curLine.substring(4);
|
||||
int pos = fileName.indexOf('\t');
|
||||
if (pos < 0) {
|
||||
pos = fileName.indexOf(' ');
|
||||
private static PatchLine addContextDiffLine(final PatchHunk hunk, final String line, final PatchLine.Type type) {
|
||||
final PatchLine patchLine = new PatchLine(type, line.length() < 2 ? "" : line.substring(2));
|
||||
hunk.addLine(patchLine);
|
||||
return patchLine;
|
||||
}
|
||||
if (pos >= 0) {
|
||||
String versionId = fileName.substring(pos).trim();
|
||||
fileName = fileName.substring(0, pos);
|
||||
if (versionId.length() > 0) {
|
||||
if (before) {
|
||||
patch.setBeforeVersionId(versionId);
|
||||
|
||||
private List<String> readContextDiffLines(ListIterator<String> iterator) {
|
||||
ArrayList<String> result = new ArrayList<String>();
|
||||
while (iterator.hasNext()) {
|
||||
final String line = iterator.next();
|
||||
if (!line.startsWith(" ") && !line.startsWith("+ ") && !line.startsWith("- ") && !line.startsWith("! ") &&
|
||||
!line.startsWith(NO_NEWLINE_SIGNATURE)) {
|
||||
iterator.previous();
|
||||
break;
|
||||
}
|
||||
else {
|
||||
patch.setAfterVersionId(versionId);
|
||||
result.add(line);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void extractFileName(final String curLine, final FilePatch patch, final boolean before) {
|
||||
String fileName = curLine.substring(4);
|
||||
int pos = fileName.indexOf('\t');
|
||||
if (pos < 0) {
|
||||
pos = fileName.indexOf(' ');
|
||||
}
|
||||
if (pos >= 0) {
|
||||
String versionId = fileName.substring(pos).trim();
|
||||
fileName = fileName.substring(0, pos);
|
||||
if (versionId.length() > 0) {
|
||||
if (before) {
|
||||
patch.setBeforeVersionId(versionId);
|
||||
}
|
||||
else {
|
||||
patch.setAfterVersionId(versionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (before) {
|
||||
patch.setBeforeName(fileName);
|
||||
}
|
||||
else {
|
||||
patch.setAfterName(fileName);
|
||||
}
|
||||
}
|
||||
if (before) {
|
||||
patch.setBeforeName(fileName);
|
||||
}
|
||||
else {
|
||||
patch.setAfterName(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
private interface Parser {
|
||||
boolean testIsStart(final String start);
|
||||
void parse(final String start, final ListIterator<String> iterator) throws PatchSyntaxException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,25 +22,48 @@
|
||||
*/
|
||||
package com.intellij.openapi.diff.impl.patch;
|
||||
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class UnifiedDiffWriter {
|
||||
@NonNls private static final String INDEX_SIGNATURE = "Index: {0}{1}";
|
||||
@NonNls public static final String ADDITIONAL_PREFIX = "IDEA additional info:";
|
||||
@NonNls public static final String ADD_INFO_HEADER = "Subsystem: ";
|
||||
@NonNls public static final String ADD_INFO_LINE_START = "<+>";
|
||||
private static final String HEADER_SEPARATOR = "===================================================================";
|
||||
|
||||
private UnifiedDiffWriter() {
|
||||
}
|
||||
|
||||
public static void write(Collection<FilePatch> patches, Writer writer, final String lineSeparator) throws IOException {
|
||||
public static void write(Project project, Collection<FilePatch> patches, Writer writer, final String lineSeparator) throws IOException {
|
||||
final PatchEP[] extensions = project == null ? new PatchEP[0] : Extensions.getExtensions(PatchEP.EP_NAME, project);
|
||||
write(patches, writer, lineSeparator, extensions);
|
||||
}
|
||||
|
||||
public static void write(Collection<FilePatch> patches, Writer writer, final String lineSeparator,
|
||||
final PatchEP[] extensions) throws IOException {
|
||||
for(FilePatch filePatch: patches) {
|
||||
if (!(filePatch instanceof TextFilePatch)) continue;
|
||||
TextFilePatch patch = (TextFilePatch) filePatch;
|
||||
writeFileHeading(patch, writer, lineSeparator);
|
||||
final String path = patch.getBeforeName() == null ? patch.getAfterName() : patch.getBeforeName();
|
||||
final Map<String , CharSequence> additionalMap = new HashMap<String, CharSequence>();
|
||||
for (PatchEP extension : extensions) {
|
||||
final CharSequence charSequence = extension.provideContent(path);
|
||||
if (! StringUtil.isEmpty(charSequence)) {
|
||||
additionalMap.put(extension.getName(), charSequence);
|
||||
}
|
||||
}
|
||||
writeFileHeading(patch, writer, lineSeparator, additionalMap);
|
||||
for(PatchHunk hunk: patch.getHunks()) {
|
||||
writeHunkStart(writer, hunk.getStartLineBefore(), hunk.getEndLineBefore(), hunk.getStartLineAfter(), hunk.getEndLineAfter(),
|
||||
lineSeparator);
|
||||
@@ -67,8 +90,26 @@ public class UnifiedDiffWriter {
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeFileHeading(final FilePatch patch, final Writer writer, final String lineSeparator) throws IOException {
|
||||
private static void writeFileHeading(final FilePatch patch,
|
||||
final Writer writer,
|
||||
final String lineSeparator,
|
||||
Map<String, CharSequence> additionalMap) throws IOException {
|
||||
writer.write(MessageFormat.format(INDEX_SIGNATURE, patch.getBeforeName(), lineSeparator));
|
||||
if (additionalMap != null && ! additionalMap.isEmpty()) {
|
||||
writer.write(ADDITIONAL_PREFIX);
|
||||
writer.write(lineSeparator);
|
||||
for (Map.Entry<String, CharSequence> entry : additionalMap.entrySet()) {
|
||||
writer.write(ADD_INFO_HEADER + entry.getKey());
|
||||
writer.write(lineSeparator);
|
||||
final String value = StringUtil.escapeStringCharacters(entry.getValue().toString());
|
||||
final List<String> lines = StringUtil.split(value, "\n");
|
||||
for (String line : lines) {
|
||||
writer.write(ADD_INFO_LINE_START);
|
||||
writer.write(line);
|
||||
writer.write(lineSeparator);
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.write(HEADER_SEPARATOR + lineSeparator);
|
||||
writeRevisionHeading(writer, "---", patch.getBeforeName(), patch.getBeforeVersionId(), lineSeparator);
|
||||
writeRevisionHeading(writer, "+++", patch.getAfterName(), patch.getAfterVersionId(), lineSeparator);
|
||||
@@ -98,4 +139,4 @@ public class UnifiedDiffWriter {
|
||||
writer.write(prefix);
|
||||
writer.write(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.openapi.diff.impl.patch.formove;
|
||||
|
||||
import com.intellij.openapi.diff.impl.patch.PatchEP;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
* Date: 7/12/11
|
||||
* Time: 1:15 PM
|
||||
*/
|
||||
public class TestPatchEP implements PatchEP {
|
||||
private final static String ourName = "com.intellij.openapi.diff.impl.patch.formove.TestPatchEP";
|
||||
private final static String ourContent = "ourContent\nseveral\nlines\twith\u0142\u0001 different symbols";
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return ourName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence provideContent(@NotNull String path) {
|
||||
return ourContent + path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consumeContent(@NotNull String path, @NotNull CharSequence content) {
|
||||
assert (ourContent + path).equals(content.toString());
|
||||
}
|
||||
}
|
||||
+5
@@ -36,4 +36,9 @@ public class TransparentlyFailedValue<T, E extends Exception> {
|
||||
if (this.e != null) throw this.e;
|
||||
return this.t;
|
||||
}
|
||||
|
||||
public void take(final TransparentlyFailedValue<T,E> value) {
|
||||
this.t = value.t;
|
||||
this.e = value.e;
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,19 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ObjectsConvertor {
|
||||
private final static DownCast DOWN_CAST = new DownCast();
|
||||
|
||||
public static class DownCast<Sup, Sub extends Sup> implements Convertor<Sub, Sup> {
|
||||
@Override
|
||||
public Sup convert(Sub o) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
public static <Sup, Sub extends Sup> List<Sup> downcast(List<Sub> list) {
|
||||
return convert(list, (Convertor<Sub, Sup>) DOWN_CAST);
|
||||
}
|
||||
|
||||
public static final Convertor<FilePath, VirtualFile> FILEPATH_TO_VIRTUAL = new Convertor<FilePath, VirtualFile>() {
|
||||
public VirtualFile convert(FilePath fp) {
|
||||
return fp.getVirtualFile();
|
||||
|
||||
+51
-1
@@ -17,16 +17,25 @@ package com.intellij.openapi.vcs.changes.patch;
|
||||
|
||||
import com.intellij.openapi.diff.impl.patch.BinaryFilePatch;
|
||||
import com.intellij.openapi.diff.impl.patch.FilePatch;
|
||||
import com.intellij.openapi.diff.impl.patch.PatchEP;
|
||||
import com.intellij.openapi.diff.impl.patch.PatchSyntaxException;
|
||||
import com.intellij.openapi.diff.impl.patch.formove.PatchApplier;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.vcs.ObjectsConvertor;
|
||||
import com.intellij.openapi.vcs.changes.LocalChangeList;
|
||||
import com.intellij.openapi.vcs.changes.TransparentlyFailedValue;
|
||||
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
@@ -47,7 +56,10 @@ public class ApplyPatchDefaultExecutor implements ApplyPatchExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(MultiMap<VirtualFile, FilePatchInProgress> patchGroups, LocalChangeList localList, String fileName) {
|
||||
public void apply(MultiMap<VirtualFile, FilePatchInProgress> patchGroups,
|
||||
LocalChangeList localList,
|
||||
String fileName,
|
||||
TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
|
||||
final Collection<PatchApplier> appliers = new LinkedList<PatchApplier>();
|
||||
for (VirtualFile base : patchGroups.keySet()) {
|
||||
final PatchApplier patchApplier =
|
||||
@@ -60,5 +72,43 @@ public class ApplyPatchDefaultExecutor implements ApplyPatchExecutor {
|
||||
appliers.add(patchApplier);
|
||||
}
|
||||
PatchApplier.executePatchGroup(appliers);
|
||||
|
||||
applyAdditionalInfo(myProject, additionalInfo);
|
||||
}
|
||||
|
||||
public static void applyAdditionalInfo(final Project project,
|
||||
TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
|
||||
final PatchEP[] extensions = Extensions.getExtensions(PatchEP.EP_NAME, project);
|
||||
if (extensions == null && extensions.length == 0) return;
|
||||
if (additionalInfo != null) {
|
||||
try {
|
||||
final Map<String, Map<String, CharSequence>> map = additionalInfo.get();
|
||||
for (Map.Entry<String, Map<String, CharSequence>> entry : map.entrySet()) {
|
||||
final String path = entry.getKey();
|
||||
final Map<String, CharSequence> innerMap = entry.getValue();
|
||||
|
||||
for (PatchEP extension : extensions) {
|
||||
final CharSequence charSequence = innerMap.get(extension.getName());
|
||||
if (charSequence != null) {
|
||||
extension.consumeContent(path, charSequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (PatchSyntaxException e) {
|
||||
VcsBalloonProblemNotifier
|
||||
.showOverChangesView(project, "Can not apply additional patch info: " + e.getMessage(), MessageType.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> pathsFromGroups(MultiMap<VirtualFile, FilePatchInProgress> patchGroups) {
|
||||
final Set<String> selectedPaths = new HashSet<String>();
|
||||
final Collection<? extends FilePatchInProgress> values = patchGroups.values();
|
||||
for (FilePatchInProgress value : values) {
|
||||
final String path = value.getPatch().getBeforeName() == null ? value.getPatch().getAfterName() : value.getPatch().getBeforeName();
|
||||
selectedPaths.add(path);
|
||||
}
|
||||
return selectedPaths;
|
||||
}
|
||||
}
|
||||
|
||||
+23
-39
@@ -17,7 +17,10 @@ package com.intellij.openapi.vcs.changes.patch;
|
||||
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.diff.impl.patch.*;
|
||||
import com.intellij.openapi.diff.impl.patch.PatchReader;
|
||||
import com.intellij.openapi.diff.impl.patch.PatchSyntaxException;
|
||||
import com.intellij.openapi.diff.impl.patch.PatchVirtualFileReader;
|
||||
import com.intellij.openapi.diff.impl.patch.TextFilePatch;
|
||||
import com.intellij.openapi.fileChooser.FileChooser;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.fileTypes.FileTypes;
|
||||
@@ -86,6 +89,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper {
|
||||
|
||||
private boolean myContainBasedChanges;
|
||||
private JLabel myPatchFileLabel;
|
||||
private PatchReader myReader;
|
||||
|
||||
public ApplyPatchDifferentiatedDialog(final Project project, final ApplyPatchExecutor callback, final List<ApplyPatchExecutor> executors,
|
||||
@NotNull final ApplyPatchMode applyPatchMode, @NotNull final VirtualFile patchFile) {
|
||||
@@ -196,7 +200,9 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper {
|
||||
for (FilePatchInProgress patchInProgress : included) {
|
||||
patchGroups.putValue(patchInProgress.getBase(), patchInProgress);
|
||||
}
|
||||
executor.apply(patchGroups, getSelectedChangeList(), myRecentPathFileChange.get().getVf().getName());
|
||||
final LocalChangeList selected = getSelectedChangeList();
|
||||
executor.apply(patchGroups, selected, myRecentPathFileChange.get().getVf().getName(),
|
||||
myReader == null ? null : myReader.getAdditionalInfo(ApplyPatchDefaultExecutor.pathsFromGroups(patchGroups)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -229,64 +235,42 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper {
|
||||
}
|
||||
final VirtualFile file = filePresentation.getVf();
|
||||
|
||||
final List<TextFilePatch> patches = loadPatches(file);
|
||||
final AutoMatchIterator autoMatchIterator = new AutoMatchIterator(myProject);
|
||||
final List<FilePatchInProgress> matchedPathes = autoMatchIterator.execute(patches);
|
||||
final PatchReader patchReader = loadPatches(file);
|
||||
final List<FilePatchInProgress> matchedPathes = patchReader == null ? Collections.<FilePatchInProgress>emptyList() :
|
||||
new AutoMatchIterator(myProject).execute(patchReader.getPatches());
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
myChangeListChooser.setDefaultName(file.getNameWithoutExtension().replace('_', ' ').trim());
|
||||
myPatches.clear();
|
||||
myPatches.addAll(matchedPathes);
|
||||
|
||||
myReader = patchReader;
|
||||
updateTree(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private List<TextFilePatch> loadPatches(final VirtualFile patchFile) {
|
||||
@Nullable
|
||||
private PatchReader loadPatches(final VirtualFile patchFile) {
|
||||
if (! patchFile.isValid()) {
|
||||
//todo
|
||||
//queueUpdateStatus("Cannot find patch file");
|
||||
return Collections.emptyList();
|
||||
return null;
|
||||
}
|
||||
PatchReader reader;
|
||||
try {
|
||||
reader = PatchVirtualFileReader.create(patchFile);
|
||||
}
|
||||
catch (IOException e) {
|
||||
//todo
|
||||
//queueUpdateStatus(VcsBundle.message("patch.apply.open.error", e.getMessage()));
|
||||
return Collections.emptyList();
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
reader.parseAllPatches();
|
||||
}
|
||||
catch (PatchSyntaxException e) {
|
||||
return null;
|
||||
}
|
||||
final List<TextFilePatch> result = new LinkedList<TextFilePatch>();
|
||||
while(true) {
|
||||
FilePatch patch;
|
||||
try {
|
||||
patch = reader.readNextPatch();
|
||||
}
|
||||
catch (PatchSyntaxException e) {
|
||||
// todo
|
||||
if (e.getLine() >= 0) {
|
||||
//queueUpdateStatus(VcsBundle.message("patch.apply.load.error.line", e.getMessage(), e.getLine()));
|
||||
}
|
||||
else {
|
||||
//queueUpdateStatus(VcsBundle.message("patch.apply.load.error", e.getMessage()));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (patch == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
result.add((TextFilePatch) patch);
|
||||
}
|
||||
if (myPatches.isEmpty()) {
|
||||
// todo
|
||||
//queueUpdateStatus(VcsBundle.message("patch.apply.no.patches.found"));
|
||||
}
|
||||
return result;
|
||||
return reader;
|
||||
}
|
||||
|
||||
private static class FilePresentation {
|
||||
|
||||
+8
-1
@@ -15,10 +15,14 @@
|
||||
*/
|
||||
package com.intellij.openapi.vcs.changes.patch;
|
||||
|
||||
import com.intellij.openapi.diff.impl.patch.PatchSyntaxException;
|
||||
import com.intellij.openapi.vcs.changes.LocalChangeList;
|
||||
import com.intellij.openapi.vcs.changes.TransparentlyFailedValue;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
* Date: 2/25/11
|
||||
@@ -26,5 +30,8 @@ import com.intellij.util.containers.MultiMap;
|
||||
*/
|
||||
public interface ApplyPatchExecutor {
|
||||
String getName();
|
||||
void apply(final MultiMap<VirtualFile, FilePatchInProgress> patchGroups, final LocalChangeList localList, String fileName);
|
||||
void apply(final MultiMap<VirtualFile, FilePatchInProgress> patchGroups,
|
||||
final LocalChangeList localList,
|
||||
String fileName,
|
||||
TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo);
|
||||
}
|
||||
|
||||
+54
-3
@@ -16,26 +16,33 @@
|
||||
package com.intellij.openapi.vcs.changes.patch;
|
||||
|
||||
import com.intellij.openapi.diff.impl.patch.FilePatch;
|
||||
import com.intellij.openapi.diff.impl.patch.PatchEP;
|
||||
import com.intellij.openapi.diff.impl.patch.PatchSyntaxException;
|
||||
import com.intellij.openapi.diff.impl.patch.TextFilePatch;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vcs.AbstractVcsHelper;
|
||||
import com.intellij.openapi.vcs.ObjectsConvertor;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vcs.changes.LocalChangeList;
|
||||
import com.intellij.openapi.vcs.changes.TransparentlyFailedValue;
|
||||
import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager;
|
||||
import com.intellij.openapi.vcs.changes.shelf.ShelvedChangeList;
|
||||
import com.intellij.openapi.vcs.changes.shelf.ShelvedChangesViewManager;
|
||||
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.vcsUtil.VcsCatchingRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
@@ -56,7 +63,10 @@ public class ImportToShelfExecutor implements ApplyPatchExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(final MultiMap<VirtualFile, FilePatchInProgress> patchGroups, LocalChangeList localList, final String fileName) {
|
||||
public void apply(final MultiMap<VirtualFile, FilePatchInProgress> patchGroups,
|
||||
LocalChangeList localList,
|
||||
final String fileName,
|
||||
final TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
|
||||
final VcsCatchingRunnable vcsCatchingRunnable = new VcsCatchingRunnable() {
|
||||
@Override
|
||||
public void runImpl() throws VcsException {
|
||||
@@ -77,9 +87,24 @@ public class ImportToShelfExecutor implements ApplyPatchExecutor {
|
||||
}
|
||||
}));
|
||||
}
|
||||
if (!allPatches.isEmpty()) {
|
||||
if (! allPatches.isEmpty()) {
|
||||
PatchEP[] patchTransitExtensions = null;
|
||||
if (additionalInfo != null) {
|
||||
try {
|
||||
final List<PatchEP> list = new ArrayList<PatchEP>();
|
||||
for (Map.Entry<String, Map<String, CharSequence>> entry : additionalInfo.get().entrySet()) {
|
||||
list.add(new TransitExtension(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
patchTransitExtensions = list.toArray(new PatchEP[list.size()]);
|
||||
}
|
||||
catch (PatchSyntaxException e) {
|
||||
VcsBalloonProblemNotifier
|
||||
.showOverChangesView(myProject, "Can not import additional patch info: " + e.getMessage(), MessageType.ERROR);
|
||||
}
|
||||
}
|
||||
try {
|
||||
final ShelvedChangeList shelvedChangeList = ShelveChangesManager.getInstance(myProject).importFilePatches(fileName, allPatches);
|
||||
final ShelvedChangeList shelvedChangeList = ShelveChangesManager.getInstance(myProject).
|
||||
importFilePatches(fileName, allPatches, patchTransitExtensions);
|
||||
ShelvedChangesViewManager.getInstance(myProject).activateView(shelvedChangeList);
|
||||
}
|
||||
catch (IOException e) {
|
||||
@@ -93,4 +118,30 @@ public class ImportToShelfExecutor implements ApplyPatchExecutor {
|
||||
AbstractVcsHelper.getInstance(myProject).showErrors(vcsCatchingRunnable.get(), IMPORT_TO_SHELF);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TransitExtension implements PatchEP {
|
||||
private final String myName;
|
||||
private final Map<String, CharSequence> myMap;
|
||||
|
||||
private TransitExtension(String name, Map<String, CharSequence> map) {
|
||||
myName = name;
|
||||
myMap = map;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence provideContent(@NotNull String path) {
|
||||
return myMap.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consumeContent(@NotNull String path, @NotNull CharSequence content) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class PatchWriter {
|
||||
Writer writer = new OutputStreamWriter(new FileOutputStream(fileName));
|
||||
try {
|
||||
final String lineSeparator = CodeStyleFacade.getInstance(project).getLineSeparator();
|
||||
UnifiedDiffWriter.write(patches, writer, lineSeparator);
|
||||
UnifiedDiffWriter.write(project, patches, writer, lineSeparator);
|
||||
}
|
||||
finally {
|
||||
writer.close();
|
||||
|
||||
+7
-7
@@ -178,7 +178,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl
|
||||
myFileProcessor.savePathFile(
|
||||
new CompoundShelfFileProcessor.ContentProvider(){
|
||||
public void writeContentTo(final Writer writer) throws IOException {
|
||||
UnifiedDiffWriter.write(patches, writer, "\n");
|
||||
UnifiedDiffWriter.write(myProject, patches, writer, "\n");
|
||||
}
|
||||
},
|
||||
patchPath);
|
||||
@@ -198,13 +198,13 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl
|
||||
return changeList;
|
||||
}
|
||||
|
||||
public ShelvedChangeList importFilePatches(final String fileName, final List<FilePatch> patches) throws IOException {
|
||||
public ShelvedChangeList importFilePatches(final String fileName, final List<FilePatch> patches, final PatchEP[] patchTransitExtensions) throws IOException {
|
||||
try {
|
||||
final File patchPath = getPatchPath(fileName);
|
||||
myFileProcessor.savePathFile(
|
||||
new CompoundShelfFileProcessor.ContentProvider(){
|
||||
public void writeContentTo(final Writer writer) throws IOException {
|
||||
UnifiedDiffWriter.write(patches, writer, "\n");
|
||||
UnifiedDiffWriter.write(patches, writer, "\n", patchTransitExtensions);
|
||||
}
|
||||
},
|
||||
patchPath);
|
||||
@@ -484,12 +484,12 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void writePatchesToFile(final String path, final List<FilePatch> remainingPatches) {
|
||||
private static void writePatchesToFile(final Project project, final String path, final List<FilePatch> remainingPatches) {
|
||||
OutputStreamWriter writer;
|
||||
try {
|
||||
writer = new OutputStreamWriter(new FileOutputStream(path));
|
||||
try {
|
||||
UnifiedDiffWriter.write(remainingPatches, writer, "\n");
|
||||
UnifiedDiffWriter.write(project, remainingPatches, writer, "\n");
|
||||
}
|
||||
finally {
|
||||
writer.close();
|
||||
@@ -514,7 +514,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl
|
||||
new ArrayList<ShelvedBinaryFile>(changeList.getBinaryFiles()));
|
||||
listCopy.DATE = (changeList.DATE == null) ? null : new Date(changeList.DATE.getTime());
|
||||
|
||||
writePatchesToFile(changeList.PATH, remainingPatches);
|
||||
writePatchesToFile(myProject, changeList.PATH, remainingPatches);
|
||||
|
||||
changeList.getBinaryFiles().retainAll(remainingBinaries);
|
||||
changeList.clearLoadedChanges();
|
||||
@@ -569,7 +569,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl
|
||||
for (ShelvedChange change : listCopy.getChanges()) {
|
||||
patches.add(change.loadFilePatch());
|
||||
}
|
||||
writePatchesToFile(listCopy.PATH, patches);
|
||||
writePatchesToFile(myProject, listCopy.PATH, patches);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info(e);
|
||||
|
||||
Reference in New Issue
Block a user