Merge branch 'svn_18_3'

This commit is contained in:
Konstantin Kolosovsky
2014-04-04 15:19:58 +04:00
25 changed files with 520 additions and 210 deletions
@@ -0,0 +1,68 @@
/*
* Copyright 2000-2014 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.execution.process;
import org.jetbrains.annotations.NotNull;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author Konstantin Kolosovsky.
*/
public class ProcessWrapper extends Process {
@NotNull private final Process myOriginalProcess;
public ProcessWrapper(@NotNull Process originalProcess) {
myOriginalProcess = originalProcess;
}
@Override
public OutputStream getOutputStream() {
return myOriginalProcess.getOutputStream();
}
@Override
public InputStream getInputStream() {
return myOriginalProcess.getInputStream();
}
@Override
public InputStream getErrorStream() {
return myOriginalProcess.getErrorStream();
}
@Override
public int waitFor() throws InterruptedException {
return myOriginalProcess.waitFor();
}
@Override
public int exitValue() {
return myOriginalProcess.exitValue();
}
@Override
public void destroy() {
myOriginalProcess.destroy();
}
@NotNull
public Process getOriginalProcess() {
return myOriginalProcess;
}
}
@@ -20,49 +20,10 @@ import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import java.io.InputStream;
import java.io.OutputStream;
public class RunnerWinProcess extends Process {
private final Process myOriginalProcess;
public class RunnerWinProcess extends ProcessWrapper {
private RunnerWinProcess(@NotNull Process originalProcess) {
myOriginalProcess = originalProcess;
}
@Override
public OutputStream getOutputStream() {
return myOriginalProcess.getOutputStream();
}
@Override
public InputStream getInputStream() {
return myOriginalProcess.getInputStream();
}
@Override
public InputStream getErrorStream() {
return myOriginalProcess.getErrorStream();
}
@Override
public int waitFor() throws InterruptedException {
return myOriginalProcess.waitFor();
}
@Override
public int exitValue() {
return myOriginalProcess.exitValue();
}
@Override
public void destroy() {
myOriginalProcess.destroy();
}
public Process getOriginalProcess() {
return myOriginalProcess;
super(originalProcess);
}
/**
@@ -73,6 +73,7 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
public static final String HTTP_PROXY_PASSWORD = "http-proxy-password";
private Project myProject;
private File myConfigDirectory;
private ISVNAuthenticationProvider myRuntimeCacheProvider;
private PersistentAuthenticationProviderProxy myPersistentAuthenticationProviderProxy;
private SvnConfiguration myConfig;
private static final ThreadLocal<Boolean> ourJustEntered = new ThreadLocal<Boolean>();
@@ -119,6 +120,19 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im
}
});
// This is not the same instance as DefaultSVNAuthenticationManager.myProviders[1], but currently
// DefaultSVNAuthenticationManager.CacheAuthenticationProvider uses only its outer class state - so we utilize necessary logic with
// this new instance.
myRuntimeCacheProvider = createRuntimeAuthenticationProvider();
}
public SVNAuthentication requestFromCache(String kind,
SVNURL url,
String realm,
SVNErrorMessage errorMessage,
SVNAuthentication previousAuth,
boolean authMayBeStored) {
return myRuntimeCacheProvider.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, authMayBeStored);
}
public String getDefaultUsername(String kind, SVNURL url) {
@@ -350,7 +350,7 @@ public class SvnConfiguration implements PersistentStateComponent<SvnConfigurati
});
getInteractiveManager(svnVcs);
// to init
myAuthManager.setAuthenticationProvider(new SvnAuthenticationProvider(svnVcs, myInteractiveProvider, RUNTIME_AUTH_CACHE));
myAuthManager.setAuthenticationProvider(new SvnAuthenticationProvider(svnVcs, myInteractiveProvider, myAuthManager));
myAuthManager.setRuntimeStorage(RUNTIME_AUTH_CACHE);
}
return myAuthManager;
@@ -152,7 +152,7 @@ public class SvnDiffProvider extends DiffProviderEx implements DiffProvider, Dif
svnInfo.getAuthor(), message);
}
catch (VcsException e) {
LOG.debug(e); // most likely the file is unversioned
LOG.info(e); // most likely the file is unversioned
return null;
}
}
@@ -952,6 +952,8 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
return getInfo(new File(path));
}
// TODO: Most likely make getInfo(File) correspond to getInfo(File, SVNRevision.UNDEFINED)
// TODO: And create special method that tries also to resolve HEAD revision - to be used only in special code paths
@Nullable
public SVNInfo getInfo(@NotNull File ioFile) {
SVNInfo result = null;
@@ -19,6 +19,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
/**
@@ -41,7 +42,7 @@ public class InfoCommandRepositoryProvider extends BaseRepositoryProvider {
result = new Repository(myTarget.getURL());
}
else {
SVNInfo info = myVcs.getInfo(myTarget.getFile());
SVNInfo info = myVcs.getInfo(myTarget.getFile(), SVNRevision.UNDEFINED);
result = info != null ? new Repository(info.getRepositoryRootURL()) : null;
}
@@ -15,6 +15,7 @@
*/
package org.jetbrains.idea.svn.checkin;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
@@ -169,7 +170,9 @@ public class IdeaSvnkitBasedAuthenticationCallback implements AuthenticationCall
}
};
WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command);
// Use ModalityState.any() as currently ssh credentials in terminal mode are requested in the thread that reads output and not in
// the thread that started progress
WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command, ModalityState.any());
return new Pair<String, Boolean>(answer.get(), !save.isNull() && save.get());
}
@@ -28,7 +28,7 @@ public class Command {
private File workingDirectory;
@Nullable private File myConfigDir;
@Nullable private LineCommandListener myResultBuilder;
@Nullable private SVNURL myRepositoryUrl;
@Nullable private volatile SVNURL myRepositoryUrl;
@NotNull private SvnTarget myTarget;
@Nullable private ISVNCanceller myCanceller;
@@ -26,16 +26,16 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.util.EventDispatcher;
import com.intellij.util.io.BaseDataReader;
import com.intellij.util.io.BinaryOutputReader;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.SVNCancelException;
import java.io.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -57,7 +57,7 @@ public class CommandExecutor {
private volatile boolean myWasCancelled;
protected final GeneralCommandLine myCommandLine;
protected Process myProcess;
protected OSProcessHandler myHandler;
protected SvnProcessHandler myHandler;
private OutputStreamWriter myProcessWriter;
// TODO: Try to implement commands in a way that they manually indicate if they need full output - to prevent situations
// TODO: when large amount of data needs to be stored instead of just sequential processing.
@@ -157,41 +157,62 @@ public class CommandExecutor {
private void ensureMessageFile() throws SvnBindException {
if (myMessage != null) {
myMessageFile = createTempFile("commit-message", ".txt");
try {
File vcsFolder = new File(PathManager.getSystemPath(), "vcs");
myMessageFile = FileUtil.createTempFile(new File(vcsFolder, "svn"), "commit-message", ".txt");
FileUtil.writeToFile(myMessageFile, myMessage);
myCommandLine.addParameters("-F", myMessageFile.getAbsolutePath());
myCommandLine.addParameters("--config-option", "config:miscellany:log-encoding=" + CharsetToolkit.UTF8);
}
catch (IOException e) {
throw new SvnBindException(e);
}
myCommandLine.addParameters("-F", myMessageFile.getAbsolutePath());
myCommandLine.addParameters("--config-option", "config:miscellany:log-encoding=" + CharsetToolkit.UTF8);
}
}
private void cleanupMessageFile() {
if (myMessageFile != null) {
boolean wasDeleted = FileUtil.delete(myMessageFile);
deleteTempFile(myMessageFile);
}
@NotNull
protected static File getSvnFolder() {
File vcsFolder = new File(PathManager.getSystemPath(), "vcs");
return new File(vcsFolder, "svn");
}
@NotNull
protected static File createTempFile(@NotNull String prefix, @NotNull String extension) throws SvnBindException {
try {
return FileUtil.createTempFile(getSvnFolder(), prefix, extension);
}
catch (IOException e) {
throw new SvnBindException(e);
}
}
protected static void deleteTempFile(@Nullable File file) {
if (file != null) {
boolean wasDeleted = FileUtil.delete(file);
if (!wasDeleted) {
LOG.info("Failed to delete temp commit message file " + myMessageFile.getAbsolutePath());
LOG.info("Failed to delete temp file " + file.getAbsolutePath());
}
}
}
@NotNull
protected OSProcessHandler createProcessHandler() {
return needsBinaryOutput()
? new BinaryOSProcessHandler(myProcess, myCommandLine.getCommandLineString())
: new MyOSProcessHandler(myProcess, myCommandLine.getCommandLineString());
protected SvnProcessHandler createProcessHandler() {
return new SvnProcessHandler(myProcess, myCommandLine.getCommandLineString(), needsUtf8Output(), needsBinaryOutput());
}
private boolean needsBinaryOutput() {
protected boolean needsBinaryOutput() {
return SvnCommandName.cat.equals(myCommand.getName());
}
protected boolean needsUtf8Output() {
return myCommand.getParameters().contains("--xml");
}
@NotNull
protected Process createProcess() throws ExecutionException {
return myCommandLine.createProcess();
@@ -218,9 +239,9 @@ public class CommandExecutor {
return outputAdapter.getOutput();
}
@Nullable
@NotNull
public ByteArrayOutputStream getBinaryOutput() {
return myHandler instanceof BinaryOSProcessHandler ? ((BinaryOSProcessHandler)myHandler).myBinaryOutput : null;
return myHandler.getBinaryOutput();
}
// TODO: Carefully here - do not modify command from threads other than the one started command execution
@@ -444,53 +465,4 @@ public class CommandExecutor {
}
}
}
private class MyOSProcessHandler extends OSProcessHandler {
public MyOSProcessHandler(@NotNull Process process, @Nullable String commandLine) {
super(process, commandLine);
}
@Override
protected Reader createProcessOutReader() {
if (myCommand.getParameters().contains("--xml")) {
return new InputStreamReader(myProcess.getInputStream(), CharsetToolkit.UTF8_CHARSET);
}
return super.createProcessOutReader();
}
}
private static class BinaryOSProcessHandler extends OSProcessHandler {
@NotNull private final ByteArrayOutputStream myBinaryOutput;
public BinaryOSProcessHandler(@NotNull final Process process, @Nullable final String commandLine) {
super(process, commandLine);
myBinaryOutput = new ByteArrayOutputStream();
}
@NotNull
@Override
protected BaseDataReader createOutputDataReader(BaseDataReader.SleepingPolicy sleepingPolicy) {
return new SimpleBinaryOutputReader(myProcess.getInputStream(), sleepingPolicy);
}
private class SimpleBinaryOutputReader extends BinaryOutputReader {
public SimpleBinaryOutputReader(@NotNull InputStream stream, SleepingPolicy sleepingPolicy) {
super(stream, sleepingPolicy);
start();
}
@Override
protected void onBinaryAvailable(@NotNull byte[] data, int size) {
myBinaryOutput.write(data, 0, size);
}
@Override
protected Future<?> executeOnPooledThread(Runnable runnable) {
return BinaryOSProcessHandler.this.executeOnPooledThread(runnable);
}
}
}
}
@@ -53,7 +53,7 @@ public class CommandParametersResolutionModule extends BaseCommandRuntimeModule
InfoCommandRepositoryProvider infoCommandProvider = new InfoCommandRepositoryProvider(myVcs, command.getTarget());
Repository repository = urlMappingProvider.get();
if (repository == null && !SvnCommandName.info.equals(command.getName())) {
if (repository == null && !command.isLocalInfo()) {
repository = infoCommandProvider.get();
}
@@ -17,6 +17,7 @@ package org.jetbrains.idea.svn.commandLine;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
@@ -206,14 +207,20 @@ public class CommandRuntime {
executor = new CommandExecutor(exePath, command);
}
else {
command.put("--force-interactive");
executor = new TerminalExecutor(exePath, command);
// do not explicitly specify "--force-interactive" as it is not supported in svn 1.7 - commands will be interactive by default as
// running under terminal
executor = newTerminalExecutor(command);
((TerminalExecutor)executor).addInteractiveListener(new TerminalSshModule(this, executor));
}
return executor;
}
@NotNull
private TerminalExecutor newTerminalExecutor(@NotNull Command command) {
return SystemInfo.isWindows ? new WinTerminalExecutor(exePath, command) : new TerminalExecutor(exePath, command);
}
private static boolean isLocal(@NotNull Command command) {
return SvnCommandName.version.equals(command.getName()) ||
SvnCommandName.cleanup.equals(command.getName()) ||
@@ -167,7 +167,7 @@ public class CommandUtil {
JAXBContext context = JAXBContext.newInstance(type);
Unmarshaller unmarshaller = context.createUnmarshaller();
return (T) unmarshaller.unmarshal(new StringReader(data));
return (T) unmarshaller.unmarshal(new StringReader(data.trim()));
}
/**
@@ -156,7 +156,7 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient {
}
private static void parseResult(@NotNull final ISVNInfoHandler handler, @Nullable File base, @Nullable String result) throws SVNException {
if (StringUtil.isEmpty(result)) {
if (StringUtil.isEmptyOrSpaces(result)) {
return;
}
@@ -176,7 +176,7 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient {
try {
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), infoHandler[0]);
parser.parse(new ByteArrayInputStream(result.trim().getBytes(CharsetToolkit.UTF8_CHARSET)), infoHandler[0]);
}
catch (SvnExceptionWrapper e) {
LOG.info("info output " + result);
@@ -136,7 +136,7 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI {
final SvnStatusHandler[] svnHandl = new SvnStatusHandler[1];
svnHandl[0] = createStatusHandler(revision, handler, base, infoBase, svnHandl);
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), svnHandl[0]);
parser.parse(new ByteArrayInputStream(result.trim().getBytes(CharsetToolkit.UTF8_CHARSET)), svnHandl[0]);
if (!svnHandl[0].isAnythingReported()) {
if (!SvnUtil.isSvnVersioned(myVcs, path)) {
throw new SVNException(
@@ -0,0 +1,91 @@
/*
* Copyright 2000-2014 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 org.jetbrains.idea.svn.commandLine;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.util.io.BaseDataReader;
import com.intellij.util.io.BinaryOutputReader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.concurrent.Future;
/**
* @author Konstantin Kolosovsky.
*/
public class SvnProcessHandler extends OSProcessHandler {
private final boolean myForceUtf8;
private final boolean myForceBinary;
@NotNull private final ByteArrayOutputStream myBinaryOutput;
public SvnProcessHandler(@NotNull Process process, boolean forceUtf8, boolean forceBinary) {
this(process, null, forceUtf8, forceBinary);
}
public SvnProcessHandler(@NotNull Process process, @Nullable String commandLine, boolean forceUtf8, boolean forceBinary) {
super(process, commandLine);
myForceUtf8 = forceUtf8;
myForceBinary = forceBinary;
myBinaryOutput = new ByteArrayOutputStream();
}
@NotNull
public ByteArrayOutputStream getBinaryOutput() {
return myBinaryOutput;
}
@Override
protected Reader createProcessOutReader() {
if (myForceUtf8) {
return new InputStreamReader(myProcess.getInputStream(), CharsetToolkit.UTF8_CHARSET);
}
return super.createProcessOutReader();
}
@NotNull
@Override
protected BaseDataReader createOutputDataReader(BaseDataReader.SleepingPolicy sleepingPolicy) {
if (myForceBinary) {
return new SimpleBinaryOutputReader(myProcess.getInputStream(), sleepingPolicy);
}
return super.createOutputDataReader(sleepingPolicy);
}
private class SimpleBinaryOutputReader extends BinaryOutputReader {
public SimpleBinaryOutputReader(@NotNull InputStream stream, SleepingPolicy sleepingPolicy) {
super(stream, sleepingPolicy);
start();
}
@Override
protected void onBinaryAvailable(@NotNull byte[] data, int size) {
myBinaryOutput.write(data, 0, size);
}
@Override
protected Future<?> executeOnPooledThread(Runnable runnable) {
return SvnProcessHandler.this.executeOnPooledThread(runnable);
}
}
}
@@ -17,10 +17,7 @@ package org.jetbrains.idea.svn.commandLine;
import com.intellij.execution.CommandLineUtil;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.pty4j.PtyProcess;
import org.jetbrains.annotations.NonNls;
@@ -34,17 +31,6 @@ import java.util.List;
*/
public class TerminalExecutor extends CommandExecutor {
// max available value is 480
// if greater value is provided than the default value of 80 will be assumed
// this could provide unnecessary line breaks and thus could break parsing logic
private static final int TERMINAL_WINDOW_MAX_COLUMNS = 480;
static {
if (SystemInfo.isWindows) {
System.setProperty("win.pty.cols", String.valueOf(TERMINAL_WINDOW_MAX_COLUMNS));
}
}
private final List<InteractiveCommandListener> myInteractiveListeners = ContainerUtil.createLockFreeCopyOnWriteList();
public TerminalExecutor(@NotNull @NonNls String exePath, @NotNull Command command) {
@@ -66,19 +52,28 @@ public class TerminalExecutor extends CommandExecutor {
@NotNull
@Override
protected OSProcessHandler createProcessHandler() {
return new TerminalProcessHandler(myProcess);
protected SvnProcessHandler createProcessHandler() {
return new TerminalProcessHandler(myProcess, needsUtf8Output(), needsBinaryOutput());
}
@NotNull
@Override
protected Process createProcess() throws ExecutionException {
List<String> parameters =
escapeArguments(CommandLineUtil.toCommandLine(myCommandLine.getExePath(), myCommandLine.getParametersList().getList()));
List<String> parameters = escapeArguments(buildParameters());
return createProcess(parameters);
}
@NotNull
protected List<String> buildParameters() {
return CommandLineUtil.toCommandLine(myCommandLine.getExePath(), myCommandLine.getParametersList().getList());
}
@NotNull
protected Process createProcess(@NotNull List<String> parameters) throws ExecutionException {
try {
return PtyProcess
.exec(ArrayUtil.toStringArray(parameters), myCommandLine.getEnvironment(), myCommandLine.getWorkDirectory().getAbsolutePath());
return PtyProcess.exec(ArrayUtil.toStringArray(parameters), myCommandLine.getEnvironment(),
myCommandLine.getWorkDirectory().getAbsolutePath());
}
catch (IOException e) {
throw new ExecutionException(e);
@@ -89,16 +84,11 @@ public class TerminalExecutor extends CommandExecutor {
public void logCommand() {
super.logCommand();
LOG.info("Terminal output " + ((TerminalProcessHandler) myHandler).getTerminalOutput());
LOG.info("Terminal output " + ((TerminalProcessHandler)myHandler).getTerminalOutput());
}
private static List<String> escapeArguments(List<String> collection) {
// TODO: Add additional checks like in java.lang.ProcessImpl constructor
return ContainerUtil.map(collection, new Function<String, String>() {
@Override
public String fun(String s) {
return s.contains(" ") ? "\"" + s + "\"" : s;
}
});
@NotNull
protected List<String> escapeArguments(@NotNull List<String> arguments) {
return arguments;
}
}
@@ -16,11 +16,9 @@
package org.jetbrains.idea.svn.commandLine;
import com.intellij.execution.process.CapturingProcessAdapter;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -32,11 +30,7 @@ import java.util.regex.Matcher;
/**
* @author Konstantin Kolosovsky.
*/
public class TerminalProcessHandler extends OSProcessHandler {
// see http://en.wikipedia.org/wiki/ANSI_escape_code
private static final String NON_CSI_ESCAPE_CODE = "\u001B.[@-_]";
private static final String CSI_ESCAPE_CODE = "\u001B\\[(.*?)[@-~]";
public class TerminalProcessHandler extends SvnProcessHandler {
private final List<InteractiveCommandListener> myInteractiveListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private final CapturingProcessAdapter terminalOutputCapturer = new CapturingProcessAdapter();
@@ -44,8 +38,8 @@ public class TerminalProcessHandler extends OSProcessHandler {
private final StringBuilder outputLine = new StringBuilder();
private final StringBuilder errorLine = new StringBuilder();
public TerminalProcessHandler(@NotNull Process process) {
super(process);
public TerminalProcessHandler(@NotNull Process process, boolean forceUtf8, boolean forceBinary) {
super(process, forceUtf8, forceBinary);
}
public void addInteractiveListener(@NotNull InteractiveCommandListener listener) {
@@ -88,24 +82,13 @@ public class TerminalProcessHandler extends OSProcessHandler {
}
}
private static String filterCombinedText(@NotNull String currentLine) {
// for windows platform output is assumed in format suitable for terminal emulator
// for instance, same text could be returned twice with '\r' symbol in between (so in emulator output we'll still see correct
// text without duplication)
// because of this we manually process '\r' occurrences to get correct output
if (SystemInfo.isWindows) {
currentLine = removeAllBeforeCaretReturn(currentLine);
}
@NotNull
protected String filterCombinedText(@NotNull String currentLine) {
return currentLine;
}
private static String filterText(@NotNull String text) {
if (SystemInfo.isWindows) {
// filter terminal escape codes - they are presented in the output for windows platform
text = text.replaceAll(CSI_ESCAPE_CODE, "").replaceAll(NON_CSI_ESCAPE_CODE, "");
// trim leading '\r' symbols - as they break xml parsing logic
text = StringUtil.trimLeading(text, '\r');
}
@NotNull
protected String filterText(@NotNull String text) {
return text;
}
@@ -121,21 +104,6 @@ public class TerminalProcessHandler extends OSProcessHandler {
}
}
private static String removeAllBeforeCaretReturn(@NotNull String line) {
int caretReturn = line.lastIndexOf("\r");
while (caretReturn >= 0) {
if (caretReturn + 1 < line.length() && line.charAt(caretReturn + 1) != '\n') {
// next symbol is not '\n' - we should not treat text before found caret return symbol
line = line.substring(caretReturn + 1);
break;
}
caretReturn = line.lastIndexOf("\r", caretReturn - 1);
}
return line;
}
private static Key resolveOutputType(@NotNull String line, @NotNull Key outputType) {
Key result = outputType;
@@ -15,6 +15,7 @@
*/
package org.jetbrains.idea.svn.commandLine;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
@@ -111,7 +112,9 @@ public class TerminalSshModule extends LineCommandAdapter implements CommandRunt
}
};
WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command);
// Use ModalityState.any() as currently ssh credentials in terminal mode are requested in the thread that reads output and not in
// the thread that started progress
WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(command, ModalityState.any());
unknownHost = null;
fingerprintAlgorithm = null;
@@ -0,0 +1,162 @@
/*
* Copyright 2000-2014 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 org.jetbrains.idea.svn.commandLine;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.process.ProcessWrapper;
import com.intellij.execution.util.ExecUtil;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.List;
/**
* @author Konstantin Kolosovsky.
*/
public class WinTerminalExecutor extends TerminalExecutor {
// max available value is 480
// if greater value is provided than the default value of 80 will be assumed
// this could provide unnecessary line breaks and thus could break parsing logic
private static final int TERMINAL_WINDOW_MAX_COLUMNS = 480;
static {
// still use isWindows check here not to initialize corresponding property on non-Windows environments
if (SystemInfo.isWindows) {
System.setProperty("win.pty.cols", String.valueOf(TERMINAL_WINDOW_MAX_COLUMNS));
}
}
@Nullable private File myRedirectFile;
@Nullable private FileInputStream myRedirectStream;
public WinTerminalExecutor(@NotNull @NonNls String exePath, @NotNull Command command) {
super(exePath, command);
}
@NotNull
@Override
protected SvnProcessHandler createProcessHandler() {
return new WinTerminalProcessHandler(myProcess, needsUtf8Output(), needsBinaryOutput());
}
@Override
protected void beforeCreateProcess() throws SvnBindException {
super.beforeCreateProcess();
createRedirectFile();
}
private void createRedirectFile() throws SvnBindException {
myRedirectFile = createTempFile("terminal-output", "");
try {
myRedirectStream = new FileInputStream(myRedirectFile);
}
catch (FileNotFoundException e) {
throw new SvnBindException(e);
}
}
@Override
protected void cleanup() {
super.cleanup();
deleteRedirectFile();
}
private void deleteRedirectFile() {
if (myRedirectStream != null) {
try {
myRedirectStream.close();
}
catch (IOException e) {
LOG.info(e);
}
}
deleteTempFile(myRedirectFile);
}
@NotNull
@Override
protected Process createProcess() throws ExecutionException {
checkRedirectFile();
List<String> parameters = escapeArguments(buildParameters());
parameters.add(0, ExecUtil.getWindowsShellName());
parameters.add(1, "/c");
parameters.add(">>");
//noinspection ConstantConditions
parameters.add(quote(myRedirectFile.getAbsolutePath()));
Process process = createProcess(parameters);
return new ProcessWrapper(process) {
@Override
public InputStream getInputStream() {
return myRedirectStream;
}
@Override
public InputStream getErrorStream() {
return getOriginalProcess().getInputStream();
}
};
}
private void checkRedirectFile() {
if (myRedirectFile == null) {
throw new IllegalStateException("No redirect file found");
}
if (myRedirectStream == null) {
throw new IllegalStateException("No redirect stream found");
}
}
/**
* TODO: Identify pty4j quoting requirements for Windows and implement accordingly
*/
@NotNull
@Override
protected List<String> escapeArguments(@NotNull List<String> arguments) {
return ContainerUtil.map(arguments, new Function<String, String>() {
@Override
public String fun(String argument) {
return needQuote(argument) && !isQuoted(argument) ? quote(argument) : argument;
}
});
}
@NotNull
private static String quote(@NotNull String argument) {
return StringUtil.wrapWithDoubleQuote(argument);
}
private static boolean needQuote(@NotNull String argument) {
return argument.contains(" ");
}
private static boolean isQuoted(@NotNull String argument) {
return StringUtil.startsWithChar(argument, '\"') && StringUtil.endsWithChar(argument, '\"');
}
}
@@ -0,0 +1,75 @@
/*
* Copyright 2000-2014 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 org.jetbrains.idea.svn.commandLine;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
/**
* @author Konstantin Kolosovsky.
*/
public class WinTerminalProcessHandler extends TerminalProcessHandler {
// see http://en.wikipedia.org/wiki/ANSI_escape_code
private static final String NON_CSI_ESCAPE_CODE = "\u001B.[@-_]";
private static final String CSI_ESCAPE_CODE = "\u001B\\[(.*?)[@-~]";
public WinTerminalProcessHandler(@NotNull Process process, boolean forceUtf8, boolean forceBinary) {
super(process, forceUtf8, forceBinary);
}
@Override
protected boolean processHasSeparateErrorStream() {
return true;
}
@NotNull
@Override
protected String filterCombinedText(@NotNull String currentLine) {
// for windows platform output is assumed in format suitable for terminal emulator
// for instance, same text could be returned twice with '\r' symbol in between (so in emulator output we'll still see correct
// text without duplication)
// because of this we manually process '\r' occurrences to get correct output
return removeAllBeforeCaretReturn(currentLine);
}
@NotNull
@Override
protected String filterText(@NotNull String text) {
// filter terminal escape codes - they are presented in the output for windows platform
text = text.replaceAll(CSI_ESCAPE_CODE, "").replaceAll(NON_CSI_ESCAPE_CODE, "");
// trim leading '\r' symbols - as they break xml parsing logic
text = StringUtil.trimLeading(text, '\r');
return text;
}
@NotNull
private static String removeAllBeforeCaretReturn(@NotNull String line) {
int caretReturn = line.lastIndexOf("\r");
while (caretReturn >= 0) {
if (caretReturn + 1 < line.length() && line.charAt(caretReturn + 1) != '\n') {
// next symbol is not '\n' - we should not treat text before found caret return symbol
line = line.substring(caretReturn + 1);
break;
}
caretReturn = line.lastIndexOf("\r", caretReturn - 1);
}
return line;
}
}
@@ -2,15 +2,15 @@ package org.jetbrains.idea.svn.content;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.impl.ContentRevisionCache;
import com.intellij.openapi.vfs.CharsetToolkit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.api.BaseSvnClient;
import org.jetbrains.idea.svn.commandLine.*;
import org.jetbrains.idea.svn.commandLine.CommandExecutor;
import org.jetbrains.idea.svn.commandLine.CommandUtil;
import org.jetbrains.idea.svn.commandLine.SvnCommandName;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;
@@ -31,9 +31,7 @@ public class CmdContentClient extends BaseSvnClient implements ContentClient {
CommandUtil.put(parameters, revision);
CommandExecutor command = CommandUtil.execute(myVcs, target, SvnCommandName.cat, parameters, null);
// TODO: currently binary output will be null for terminal mode - use text output in this case
ByteArrayOutputStream output = command.getBinaryOutput();
byte[] bytes = output != null ? output.toByteArray() : CharsetToolkit.getUtf8Bytes(command.getOutput());
byte[] bytes = command.getBinaryOutput().toByteArray();
ContentRevisionCache.checkContentsSize(target.getPathOrUrlString(), bytes.length);
@@ -16,9 +16,9 @@
package org.jetbrains.idea.svn.dialogs;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.util.SystemProperties;
import org.jetbrains.idea.svn.SvnAuthenticationManager;
import org.jetbrains.idea.svn.SvnAuthenticationNotifier;
import org.jetbrains.idea.svn.SvnVcs;
import org.tmatesoft.svn.core.SVNErrorMessage;
@@ -27,7 +27,6 @@ import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationProvider;
import org.tmatesoft.svn.core.auth.SVNAuthentication;
import org.tmatesoft.svn.core.auth.SVNUserNameAuthentication;
import org.tmatesoft.svn.core.internal.wc.ISVNAuthenticationStorage;
import java.util.HashSet;
import java.util.Set;
@@ -36,25 +35,21 @@ import java.util.Set;
* @author alex
*/
public class SvnAuthenticationProvider implements ISVNAuthenticationProvider {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.SvnAuthenticationProvider");
private final Project myProject;
private final SvnAuthenticationNotifier myAuthenticationNotifier;
private final ISVNAuthenticationProvider mySvnInteractiveAuthenticationProvider;
private final ISVNAuthenticationStorage myAuthenticationStorage;
private final SvnAuthenticationManager myAuthenticationManager;
private static final Set<Thread> ourForceInteractive = new HashSet<Thread>();
public SvnAuthenticationProvider(final SvnVcs svnVcs, final ISVNAuthenticationProvider provider,
final ISVNAuthenticationStorage authenticationStorage) {
myAuthenticationStorage = authenticationStorage;
final SvnAuthenticationManager authenticationManager) {
myAuthenticationManager = authenticationManager;
myProject = svnVcs.getProject();
myAuthenticationNotifier = svnVcs.getAuthNotifier();
mySvnInteractiveAuthenticationProvider = provider;
}
private void log(final String s) {
LOG.debug(s);
}
public SVNAuthentication requestClientAuthentication(final String kind,
final SVNURL url,
final String realm,
@@ -73,7 +68,7 @@ public class SvnAuthenticationProvider implements ISVNAuthenticationProvider {
return mySvnInteractiveAuthenticationProvider.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, authMayBeStored);
} else {
if (myAuthenticationNotifier.ensureNotify(obj)) {
return (SVNAuthentication) myAuthenticationStorage.getData(kind, realm);
return myAuthenticationManager.requestFromCache(kind, url, realm, errorMessage, previousAuth, authMayBeStored);
}
}
return null;
@@ -80,7 +80,7 @@ public class SvnAuthenticationTest extends PlatformTestCase {
myAuthenticationManager = new SvnAuthenticationManager(myProject, configFile);
myInteractiveProvider = new SvnTestInteractiveAuthentication(myAuthenticationManager);
myAuthenticationManager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, myInteractiveProvider, SvnConfiguration.RUNTIME_AUTH_CACHE));
myAuthenticationManager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, myInteractiveProvider, myAuthenticationManager));
myAuthenticationManager.setRuntimeStorage(SvnConfiguration.RUNTIME_AUTH_CACHE);
myTestInteraction = new TestInteraction();
@@ -84,7 +84,7 @@ public class SvnAuthenticationTest extends PlatformTestCase {
myAuthenticationManager = new SvnAuthenticationManager(myProject, configFile);
myInteractiveProvider = new SvnTestInteractiveAuthentication(myAuthenticationManager);
myAuthenticationManager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, myInteractiveProvider, SvnConfiguration.RUNTIME_AUTH_CACHE));
myAuthenticationManager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, myInteractiveProvider, myAuthenticationManager));
myAuthenticationManager.setRuntimeStorage(SvnConfiguration.RUNTIME_AUTH_CACHE);
myTestInteraction = new TestInteraction();