Merge branch 'master' of git.labs.intellij.net:idea/community

This commit is contained in:
Kirill Likhodedov
2010-08-03 15:57:55 +04:00
103 changed files with 2836 additions and 1259 deletions
+1
View File
@@ -4,6 +4,7 @@
<scope name="IDEA Test Sources" pattern="test:*..*" />
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</component>
<component name="IdProvider" IDEtalkID="88DB5D232B345F18EEFB2825E88EC093" />
<component name="JavadocGenerationManager">
<option name="OUTPUT_DIRECTORY" />
<option name="OPTION_SCOPE" value="protected" />
+10 -10
View File
@@ -127,6 +127,16 @@
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
<option name="USE_RELATIVE_INDENTS" value="false" />
</ADDITIONAL_INDENT_OPTIONS>
<ADDITIONAL_INDENT_OPTIONS fileType="html">
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="8" />
<option name="USE_TAB_CHARACTER" value="false" />
<option name="SMART_TABS" value="false" />
<option name="LABEL_INDENT_SIZE" value="0" />
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
<option name="USE_RELATIVE_INDENTS" value="false" />
</ADDITIONAL_INDENT_OPTIONS>
<ADDITIONAL_INDENT_OPTIONS fileType="java">
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
@@ -147,16 +157,6 @@
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
<option name="USE_RELATIVE_INDENTS" value="false" />
</ADDITIONAL_INDENT_OPTIONS>
<ADDITIONAL_INDENT_OPTIONS fileType="jsp">
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="8" />
<option name="USE_TAB_CHARACTER" value="false" />
<option name="SMART_TABS" value="false" />
<option name="LABEL_INDENT_SIZE" value="0" />
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
<option name="USE_RELATIVE_INDENTS" value="false" />
</ADDITIONAL_INDENT_OPTIONS>
<ADDITIONAL_INDENT_OPTIONS fileType="rb">
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="8" />
@@ -43,11 +43,18 @@ public class ArchiveAntCopyInstructionCreator implements AntCopyInstructionCreat
}
@NotNull
public AntCopyInstructionCreator subFolder(String directoryName) {
public AntCopyInstructionCreator subFolder(@NotNull String directoryName) {
return new ArchiveAntCopyInstructionCreator(myPrefix + "/" + directoryName);
}
public Generator createSubFolderCommand(String directoryName) {
public Generator createSubFolderCommand(@NotNull String directoryName) {
return null;
}
@NotNull
@Override
public Generator createExtractedDirectoryInstruction(@NotNull String jarPath, @NotNull String pathInJar) {
final String pattern = pathInJar.length() == 0 ? null : pathInJar + "**";
return ZipFileSet.createUnpackedSet(jarPath, pathInJar, true, pattern);
}
}
@@ -17,9 +17,7 @@ package com.intellij.compiler.ant.artifacts;
import com.intellij.compiler.ant.Generator;
import com.intellij.compiler.ant.Tag;
import com.intellij.compiler.ant.taskdefs.Copy;
import com.intellij.compiler.ant.taskdefs.FileSet;
import com.intellij.compiler.ant.taskdefs.Mkdir;
import com.intellij.compiler.ant.taskdefs.*;
import com.intellij.packaging.elements.AntCopyInstructionCreator;
import org.jetbrains.annotations.NotNull;
@@ -50,11 +48,23 @@ public class DirectoryAntCopyInstructionCreator implements AntCopyInstructionCre
}
@NotNull
public AntCopyInstructionCreator subFolder(String directoryName) {
public AntCopyInstructionCreator subFolder(@NotNull String directoryName) {
return new DirectoryAntCopyInstructionCreator(myOutputDirectory + "/" + directoryName);
}
public Generator createSubFolderCommand(String directoryName) {
public Generator createSubFolderCommand(@NotNull String directoryName) {
return new Mkdir(myOutputDirectory + "/" + directoryName);
}
@NotNull
@Override
public Generator createExtractedDirectoryInstruction(@NotNull String jarPath, @NotNull String pathInJar) {
final Unzip unzip = new Unzip(jarPath, myOutputDirectory);
if (pathInJar.length() > 0) {
final PatternSet patterns = new PatternSet(null);
patterns.add(new Include(pathInJar + "**"));
unzip.add(patterns);
}
return unzip;
}
}
@@ -25,6 +25,7 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.impl.compiler.ArtifactCompilerUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.graph.CachingSemiGraph;
import com.intellij.util.graph.DFSTBuilder;
@@ -159,8 +160,14 @@ public class JarsBuilder {
try {
final THashSet<String> writtenPaths = new THashSet<String>();
for (Pair<String, VirtualFile> pair : jar.getPackedFiles()) {
File file = VfsUtil.virtualToIoFile(pair.getSecond());
addFileToJar(jarOutputStream, file, pair.getFirst(), writtenPaths);
final VirtualFile sourceFile = pair.getSecond();
if (sourceFile.isInLocalFileSystem()) {
File file = VfsUtil.virtualToIoFile(sourceFile);
addFileToJar(jarOutputStream, file, pair.getFirst(), writtenPaths);
}
else {
extractFileAndAddToJar(jarOutputStream, sourceFile, pair.getFirst(), writtenPaths);
}
}
for (Pair<String, JarInfo> nestedJar : jar.getPackedJars()) {
@@ -178,9 +185,35 @@ public class JarsBuilder {
}
}
private void extractFileAndAddToJar(JarOutputStream jarOutputStream, VirtualFile sourceFile, String relativePath, THashSet<String> writtenPaths)
throws IOException {
relativePath = addParentDirectories(jarOutputStream, writtenPaths, relativePath);
myContext.getProgressIndicator().setText2(relativePath);
if (!writtenPaths.add(relativePath)) return;
final BufferedInputStream input = ArtifactCompilerUtil.getJarEntryInputStream(sourceFile, myContext);
if (input == null) return;
ZipEntry entry = new ZipEntry(relativePath);
entry.setTime(ArtifactCompilerUtil.getJarFile(sourceFile).lastModified());
jarOutputStream.putNextEntry(entry);
FileUtil.copy(input, jarOutputStream);
jarOutputStream.closeEntry();
}
private void addFileToJar(final @NotNull JarOutputStream jarOutputStream, final @NotNull File file, @NotNull String relativePath,
final @NotNull THashSet<String> writtenPaths) throws IOException {
//todo[nik] check file exists?
if (!file.exists()) {
return;
}
relativePath = addParentDirectories(jarOutputStream, writtenPaths, relativePath);
myContext.getProgressIndicator().setText2(relativePath);
ZipUtil.addFileToZip(jarOutputStream, file, relativePath, writtenPaths, myFileFilter);
}
private static String addParentDirectories(JarOutputStream jarOutputStream, THashSet<String> writtenPaths, String relativePath)
throws IOException {
while (relativePath.startsWith("/")) {
relativePath = relativePath.substring(1);
}
@@ -193,9 +226,7 @@ public class JarsBuilder {
}
i = relativePath.indexOf('/', i + 1);
}
myContext.getProgressIndicator().setText2(relativePath);
ZipUtil.addFileToZip(jarOutputStream, file, relativePath, writtenPaths, myFileFilter);
return relativePath;
}
private static void addEntry(final ZipOutputStream output, @NonNls final String relativePath) throws IOException {
@@ -320,6 +320,7 @@ public class ArtifactUtil {
boolean processSubstitutions) {
processPackagingElements(artifact, PackagingElementFactoryImpl.FILE_COPY_ELEMENT_TYPE, processor, context, processSubstitutions);
processPackagingElements(artifact, PackagingElementFactoryImpl.DIRECTORY_COPY_ELEMENT_TYPE, processor, context, processSubstitutions);
processPackagingElements(artifact, PackagingElementFactoryImpl.EXTRACTED_DIRECTORY_ELEMENT_TYPE, processor, context, processSubstitutions);
}
public static Collection<Trinity<Artifact, PackagingElementPath, String>> findContainingArtifactsWithOutputPaths(@NotNull final VirtualFile file, @NotNull Project project) {
@@ -417,8 +418,8 @@ public class ArtifactUtil {
ContainerUtil.addIfNotNull(fileCopyElement.findFile(), result);
}
}
else if (element instanceof DirectoryCopyPackagingElement) {
final VirtualFile sourceRoot = ((DirectoryCopyPackagingElement)element).findFile();
else if (element instanceof DirectoryCopyPackagingElement || element instanceof ExtractedDirectoryPackagingElement) {
final VirtualFile sourceRoot = ((FileOrDirectoryCopyPackagingElement<?>)element).findFile();
if (sourceRoot != null) {
ContainerUtil.addIfNotNull(sourceRoot.findFileByRelativePath(path), result);
}
@@ -0,0 +1,68 @@
/*
* Copyright 2000-2010 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.packaging.impl.compiler;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* @author nik
*/
public class ArtifactCompilerUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.packaging.impl.compiler.ArtifactCompilerUtil");
private ArtifactCompilerUtil() {
}
@Nullable
public static BufferedInputStream getJarEntryInputStream(VirtualFile sourceFile, final CompileContext context) throws IOException {
final String fullPath = sourceFile.getPath();
final int jarEnd = fullPath.indexOf(JarFileSystem.JAR_SEPARATOR);
LOG.assertTrue(jarEnd != -1, fullPath);
String pathInJar = fullPath.substring(jarEnd + JarFileSystem.JAR_SEPARATOR.length());
String jarPath = fullPath.substring(0, jarEnd);
final ZipFile jarFile = new ZipFile(new File(FileUtil.toSystemDependentName(jarPath)));
final ZipEntry entry = jarFile.getEntry(pathInJar);
if (entry == null) {
context.addMessage(CompilerMessageCategory.ERROR, "Cannot extract '" + pathInJar + "' from '" + jarFile.getName() + "': entry not found", null, -1, -1);
return null;
}
return new BufferedInputStream(jarFile.getInputStream(entry)) {
@Override
public void close() throws IOException {
super.close();
jarFile.close();
}
};
}
public static File getJarFile(VirtualFile jarEntry) {
String fullPath = jarEntry.getPath();
return new File(FileUtil.toSystemDependentName(fullPath.substring(fullPath.indexOf(JarFileSystem.JAR_SEPARATOR))));
}
}
@@ -31,7 +31,7 @@ import com.intellij.openapi.deployment.DeploymentUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
@@ -53,9 +53,7 @@ import com.intellij.util.text.CaseInsensitiveStringHashingStrategy;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.*;
import java.util.*;
/**
@@ -164,7 +162,7 @@ public class ArtifactsCompilerInstance extends CompilerInstance<ArtifactBuildTar
private boolean doBuild(final List<Pair<ArtifactCompilerCompileItem, ArtifactPackagingItemOutputState>> changedItems,
final Set<ArtifactCompilerCompileItem> processedItems,
final Set<String> writtenPaths, final Set<String> deletedJars) {
final @NotNull Set<String> writtenPaths, final Set<String> deletedJars) {
final boolean testMode = ApplicationManager.getApplication().isUnitTestMode();
final DeploymentUtil deploymentUtil = DeploymentUtil.getInstance();
@@ -188,20 +186,26 @@ public class ArtifactsCompilerInstance extends CompilerInstance<ArtifactBuildTar
final Ref<IOException> exception = Ref.create(null);
new ReadAction() {
protected void run(final Result result) {
final File fromFile = VfsUtil.virtualToIoFile(sourceItem.getFile());
final VirtualFile sourceFile = sourceItem.getFile();
for (DestinationInfo destination : sourceItem.getDestinations()) {
if (destination instanceof ExplodedDestinationInfo) {
final ExplodedDestinationInfo explodedDestination = (ExplodedDestinationInfo)destination;
File toFile = new File(FileUtil.toSystemDependentName(explodedDestination.getOutputPath()));
if (fromFile.exists()) {
try {
deploymentUtil.copyFile(fromFile, toFile, myContext, writtenPaths, fileFilter);
try {
if (sourceFile.isInLocalFileSystem()) {
final File ioFromFile = VfsUtil.virtualToIoFile(sourceFile);
if (ioFromFile.exists()) {
deploymentUtil.copyFile(ioFromFile, toFile, myContext, writtenPaths, fileFilter);
}
}
catch (IOException e) {
exception.set(e);
return;
else {
extractFile(sourceFile, toFile, writtenPaths, fileFilter);
}
}
catch (IOException e) {
exception.set(e);
return;
}
}
else {
changedJars.add(((JarDestinationInfo)destination).getJarInfo());
@@ -254,6 +258,28 @@ public class ArtifactsCompilerInstance extends CompilerInstance<ArtifactBuildTar
return true;
}
private void extractFile(VirtualFile sourceFile, File toFile, Set<String> writtenPaths, FileFilter fileFilter) throws IOException {
if (!writtenPaths.add(toFile.getPath())) {
return;
}
if (!FileUtil.createParentDirs(toFile)) {
myContext.addMessage(CompilerMessageCategory.ERROR, "Cannot create directory for '" + toFile.getAbsolutePath() + "' file", null, -1, -1);
return;
}
final BufferedInputStream input = ArtifactCompilerUtil.getJarEntryInputStream(sourceFile, myContext);
if (input == null) return;
final BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(toFile));
try {
FileUtil.copy(input, output);
}
finally {
input.close();
output.close();
}
}
private void onBuildStartedOrFinished(final boolean finished) throws Exception {
final Set<Artifact> artifacts = myContext.getUserData(ArtifactsCompiler.AFFECTED_ARTIFACTS);
if (artifacts != null) {
@@ -51,7 +51,7 @@ public class DirectoryCopyPackagingElement extends FileOrDirectoryCopyPackagingE
@NotNull ArtifactAntGenerationContext generationContext,
@NotNull ArtifactType artifactType) {
final String path = generationContext.getSubstitutedPath(myFilePath);
return Collections.singletonList((Generator)creator.createDirectoryContentCopyInstruction(path));
return Collections.singletonList(creator.createDirectoryContentCopyInstruction(path));
}
@Override
@@ -0,0 +1,80 @@
/*
* Copyright 2000-2010 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.packaging.impl.elements;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDialog;
import com.intellij.openapi.fileChooser.FileChooserFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.elements.CompositePackagingElement;
import com.intellij.packaging.elements.PackagingElementType;
import com.intellij.packaging.ui.ArtifactEditorContext;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author nik
*/
public class ExtractedDirectoryElementType extends PackagingElementType<ExtractedDirectoryPackagingElement> {
public static final Icon EXTRACTED_FOLDER_ICON = IconLoader.getIcon("/nodes/extractedFolder.png");
ExtractedDirectoryElementType() {
super("extracted-dir", "Extracted Directory");
}
@Override
public Icon getCreateElementIcon() {
return EXTRACTED_FOLDER_ICON;
}
@Override
public boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact) {
return true;
}
@NotNull
public List<? extends ExtractedDirectoryPackagingElement> chooseAndCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact,
@NotNull CompositePackagingElement<?> parent) {
final FileChooserDescriptor descriptor = new FileChooserDescriptor(false, true, true, false, true, true) {
@Override
public boolean isFileSelectable(VirtualFile file) {
if (file.isInLocalFileSystem() && file.isDirectory()) return false;
return super.isFileSelectable(file);
}
};
final FileChooserDialog chooser = FileChooserFactory.getInstance().createFileChooser(descriptor, context.getProject());
final VirtualFile[] files = chooser.choose(null, context.getProject());
final List<ExtractedDirectoryPackagingElement> list = new ArrayList<ExtractedDirectoryPackagingElement>();
for (VirtualFile file : files) {
final String fullPath = file.getPath();
final int jarEnd = fullPath.indexOf(JarFileSystem.JAR_SEPARATOR);
list.add(new ExtractedDirectoryPackagingElement(fullPath.substring(0, jarEnd), fullPath.substring(jarEnd + 1)));
}
return list;
}
@NotNull
public ExtractedDirectoryPackagingElement createEmpty(@NotNull Project project) {
return new ExtractedDirectoryPackagingElement();
}
}
@@ -0,0 +1,116 @@
/*
* Copyright 2000-2010 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.packaging.impl.elements;
import com.intellij.compiler.ant.Generator;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.artifacts.ArtifactType;
import com.intellij.packaging.elements.*;
import com.intellij.packaging.impl.ui.ExtractedDirectoryPresentation;
import com.intellij.packaging.ui.ArtifactEditorContext;
import com.intellij.packaging.ui.PackagingElementPresentation;
import com.intellij.util.xmlb.annotations.Attribute;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
/**
* @author nik
*/
public class ExtractedDirectoryPackagingElement extends FileOrDirectoryCopyPackagingElement<ExtractedDirectoryPackagingElement> {
private String myPathInJar;
public ExtractedDirectoryPackagingElement() {
super(PackagingElementFactoryImpl.EXTRACTED_DIRECTORY_ELEMENT_TYPE);
}
public ExtractedDirectoryPackagingElement(String jarPath, String pathInJar) {
super(PackagingElementFactoryImpl.EXTRACTED_DIRECTORY_ELEMENT_TYPE, jarPath);
myPathInJar = pathInJar;
if (!StringUtil.startsWithChar(myPathInJar, '/')) {
myPathInJar = "/" + myPathInJar;
}
if (!StringUtil.endsWithChar(myPathInJar, '/')) {
myPathInJar += "/";
}
}
@Override
public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
return new ExtractedDirectoryPresentation(this);
}
@Override
public VirtualFile findFile() {
final VirtualFile jarFile = super.findFile();
if (jarFile == null) return null;
final VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(jarFile);
if ("/".equals(myPathInJar)) return jarRoot;
return jarRoot != null ? jarRoot.findFileByRelativePath(myPathInJar) : null;
}
@Override
public List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext,
@NotNull AntCopyInstructionCreator creator,
@NotNull ArtifactAntGenerationContext generationContext,
@NotNull ArtifactType artifactType) {
final String jarPath = generationContext.getSubstitutedPath(myFilePath);
return Collections.singletonList(creator.createExtractedDirectoryInstruction(jarPath, StringUtil.trimStart(myPathInJar, "/")));
}
@Override
public void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
@NotNull PackagingElementResolvingContext resolvingContext,
@NotNull ArtifactIncrementalCompilerContext compilerContext,
@NotNull ArtifactType artifactType) {
final VirtualFile file = findFile();
if (file != null && file.isValid() && file.isDirectory()) {
creator.addDirectoryCopyInstructions(file);
}
}
@Override
public boolean isEqualTo(@NotNull PackagingElement<?> element) {
return element instanceof ExtractedDirectoryPackagingElement && super.isEqualTo(element)
&& Comparing.equal(myPathInJar, ((ExtractedDirectoryPackagingElement)element).getPathInJar());
}
@Override
public ExtractedDirectoryPackagingElement getState() {
return this;
}
@Override
public void loadState(ExtractedDirectoryPackagingElement state) {
myFilePath = state.getFilePath();
myPathInJar = state.getPathInJar();
}
@Attribute("path-in-jar")
public String getPathInJar() {
return myPathInJar;
}
public void setPathInJar(String pathInJar) {
myPathInJar = pathInJar;
}
}
@@ -58,7 +58,7 @@ public class FileCopyPackagingElement extends FileOrDirectoryCopyPackagingElemen
}
public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
return new FileCopyPresentation(myFilePath, getOutputFileName(), context);
return new FileCopyPresentation(myFilePath, getOutputFileName());
}
@Override
@@ -51,11 +51,12 @@ public class PackagingElementFactoryImpl extends PackagingElementFactory {
public static final PackagingElementType<ArchivePackagingElement> ARCHIVE_ELEMENT_TYPE = new ArchiveElementType();
public static final PackagingElementType<FileCopyPackagingElement> FILE_COPY_ELEMENT_TYPE = new FileCopyElementType();
public static final PackagingElementType<DirectoryCopyPackagingElement> DIRECTORY_COPY_ELEMENT_TYPE = new DirectoryCopyElementType();
public static final PackagingElementType<ExtractedDirectoryPackagingElement> EXTRACTED_DIRECTORY_ELEMENT_TYPE = new ExtractedDirectoryElementType();
public static final PackagingElementType<ArtifactRootElement<?>> ARTIFACT_ROOT_ELEMENT_TYPE = new ArtifactRootElementType();
private static final PackagingElementType[] STANDARD_TYPES = {
DIRECTORY_ELEMENT_TYPE, ARCHIVE_ELEMENT_TYPE,
LibraryElementType.LIBRARY_ELEMENT_TYPE, ModuleOutputElementType.MODULE_OUTPUT_ELEMENT_TYPE,
ArtifactElementType.ARTIFACT_ELEMENT_TYPE, FILE_COPY_ELEMENT_TYPE, DIRECTORY_COPY_ELEMENT_TYPE
ArtifactElementType.ARTIFACT_ELEMENT_TYPE, FILE_COPY_ELEMENT_TYPE, DIRECTORY_COPY_ELEMENT_TYPE, EXTRACTED_DIRECTORY_ELEMENT_TYPE
};
@NotNull
@@ -253,6 +254,13 @@ public class PackagingElementFactoryImpl extends PackagingElementFactory {
return createParentDirectories(relativeOutputPath, new DirectoryCopyPackagingElement(filePath));
}
@Override
@NotNull
public PackagingElement<?> createExtractedDirectoryWithParentDirectories(@NotNull String jarPath, @NotNull String pathInJar,
@NotNull String relativeOutputPath) {
return createParentDirectories(relativeOutputPath, new ExtractedDirectoryPackagingElement(jarPath, pathInJar));
}
@NotNull
@Override
public PackagingElement<?> createFileCopyWithParentDirectories(@NotNull String filePath, @NotNull String relativeOutputPath) {
@@ -0,0 +1,65 @@
/*
* Copyright 2000-2010 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.packaging.impl.ui;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.impl.elements.ExtractedDirectoryElementType;
import com.intellij.packaging.impl.elements.ExtractedDirectoryPackagingElement;
import com.intellij.packaging.ui.PackagingElementPresentation;
import com.intellij.packaging.ui.PackagingElementWeights;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NotNull;
/**
* @author nik
*/
public class ExtractedDirectoryPresentation extends PackagingElementPresentation {
private final String myJarPath;
private final String myPathInJar;
private final VirtualFile myFile;
public ExtractedDirectoryPresentation(ExtractedDirectoryPackagingElement element) {
myFile = element.findFile();
myJarPath = element.getFilePath();
myPathInJar = element.getPathInJar();
}
public String getPresentableName() {
return PathUtil.getFileName(myJarPath) + myPathInJar;
}
public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
presentationData.setIcons(ExtractedDirectoryElementType.EXTRACTED_FOLDER_ICON);
final String parentPath = PathUtil.getParentPath(myJarPath);
if (myFile == null || !myFile.isDirectory()) {
mainAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES;
final VirtualFile parentFile = LocalFileSystem.getInstance().findFileByPath(parentPath);
if (parentFile == null) {
commentAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES;
}
}
presentationData.addText("Unpacked '" + PathUtil.getFileName(myJarPath) + myPathInJar + "'", mainAttributes);
presentationData.addText(" (" + parentPath + ")", commentAttributes);
}
@Override
public int getWeight() {
return PackagingElementWeights.EXTRACTED_DIRECTORY;
}
}
@@ -20,7 +20,6 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.impl.elements.FileCopyElementType;
import com.intellij.packaging.ui.ArtifactEditorContext;
import com.intellij.packaging.ui.PackagingElementPresentation;
import com.intellij.packaging.ui.PackagingElementWeights;
import com.intellij.ui.SimpleTextAttributes;
@@ -33,12 +32,10 @@ import org.jetbrains.annotations.NotNull;
public class FileCopyPresentation extends PackagingElementPresentation {
private final String mySourcePath;
private final String myOutputFileName;
private final ArtifactEditorContext myContext;
private final VirtualFile myFile;
public FileCopyPresentation(String filePath, String outputFileName, ArtifactEditorContext context) {
public FileCopyPresentation(String filePath, String outputFileName) {
myOutputFileName = outputFileName;
myContext = context;
String parentPath;
myFile = LocalFileSystem.getInstance().findFileByPath(filePath);
@@ -65,7 +62,7 @@ public class FileCopyPresentation extends PackagingElementPresentation {
public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
if (myFile != null && !myFile.isDirectory()) {
presentationData.setIcons(myFile != null ? myFile.getIcon() : FileCopyElementType.ICON);
presentationData.setIcons(myFile.getIcon());
presentationData.addText(myOutputFileName, mainAttributes);
presentationData.addText(" (" + mySourcePath + ")", commentAttributes);
}
@@ -17,7 +17,6 @@
package com.intellij.compiler.ant.taskdefs;
import com.intellij.compiler.ant.Tag;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NonNls;
/**
@@ -27,8 +26,7 @@ import org.jetbrains.annotations.NonNls;
public class Include extends Tag {
public Include(@NonNls final String name) {
//noinspection HardCodedStringLiteral
super("include", new Pair[] {new Pair<String, String>("name", name)});
super("include", pair("name", name));
}
}
@@ -17,7 +17,6 @@
package com.intellij.compiler.ant.taskdefs;
import com.intellij.compiler.ant.Tag;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NonNls;
/**
@@ -26,7 +25,6 @@ import org.jetbrains.annotations.NonNls;
*/
public class PatternSet extends Tag{
public PatternSet(@NonNls final String id) {
//noinspection HardCodedStringLiteral
super("patternset", new Pair[] {new Pair<String, String>("id", id)});
super("patternset", pair("id", id));
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2000-2010 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.compiler.ant.taskdefs;
import com.intellij.compiler.ant.Tag;
/**
* @author nik
*/
public class Unzip extends Tag {
public Unzip(String archivePath, String dest) {
super("unzip", pair("src", archivePath), pair("dest", dest));
}
}
@@ -17,9 +17,11 @@
package com.intellij.compiler.ant.taskdefs;
import com.intellij.compiler.ant.Tag;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
@@ -31,12 +33,23 @@ import java.io.File;
public class ZipFileSet extends Tag{
public static final ZipFileSet[] EMPTY_ARRAY = new ZipFileSet[0];
private ZipFileSet(@NonNls String tagName, Pair... tagOptions) {
super(tagName, tagOptions);
}
public ZipFileSet(@NonNls String fileOrDir, @NonNls final String relativePath, boolean isDir) {
super("zipfileset",
pair(isDir ? "dir" : "file", fileOrDir),
pair("prefix", prefix(isDir, relativePath)));
}
public static ZipFileSet createUnpackedSet(@NonNls String zipFilePath, @NotNull String relativePath, final boolean isDir, String pattern) {
return new ZipFileSet("zipfileset",
pair("src", zipFilePath),
pair("prefix", prefix(isDir, relativePath)),
pair("includes", pattern));
}
@Nullable
private static String prefix(final boolean isDir, final String relativePath) {
String path;
@@ -15,8 +15,8 @@
*/
package com.intellij.packaging.elements;
import com.intellij.compiler.ant.Tag;
import com.intellij.compiler.ant.Generator;
import com.intellij.compiler.ant.Tag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -32,8 +32,11 @@ public interface AntCopyInstructionCreator {
Tag createFileCopyInstruction(@NotNull String filePath, String outputFileName);
@NotNull
AntCopyInstructionCreator subFolder(String directoryName);
AntCopyInstructionCreator subFolder(@NotNull String directoryName);
@Nullable
Generator createSubFolderCommand(String directoryName);
Generator createSubFolderCommand(@NotNull String directoryName);
@NotNull
Generator createExtractedDirectoryInstruction(@NotNull String jarPath, @NotNull String pathInJar);
}
@@ -67,6 +67,10 @@ public abstract class PackagingElementFactory {
@NotNull
public abstract PackagingElement<?> createDirectoryCopyWithParentDirectories(@NotNull String filePath, @NotNull String relativeOutputPath);
@NotNull
public abstract PackagingElement<?> createExtractedDirectoryWithParentDirectories(@NotNull String jarPath, @NotNull String pathInJar,
@NotNull String relativeOutputPath);
@NotNull
public abstract PackagingElement<?> createFileCopyWithParentDirectories(@NotNull String filePath, @NotNull String relativeOutputPath,
@Nullable String outputFileName);
@@ -22,6 +22,7 @@ public class PackagingElementWeights {
public static final int ARTIFACT = 100;
public static final int DIRECTORY = 50;
public static final int DIRECTORY_COPY = 40;
public static final int EXTRACTED_DIRECTORY = 39;
public static final int LIBRARY = 30;
public static final int MODULE = 20;
public static final int FACET = 10;
@@ -24,6 +24,7 @@ import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.ILazyParseableElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -43,6 +44,9 @@ public class DeclarationParser {
private static final TokenSet APPEND_TO_METHOD_SET = TokenSet.create(
JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.THROWS_KEYWORD);
private static final String WHITESPACES = "\n\r \t";
private static final String LINE_ENDS = "\n\r";
private DeclarationParser() { }
public static void parseClassBodyWithBraces(final PsiBuilder builder, final boolean isAnnotation, final boolean isEnum) {
@@ -209,6 +213,7 @@ public class DeclarationParser {
}
final PsiBuilder.Marker declaration = builder.mark();
final int declarationStart = builder.getCurrentOffset();
final Pair<PsiBuilder.Marker, Boolean> modListInfo = parseModifierList(builder);
final PsiBuilder.Marker modList = modListInfo.first;
@@ -225,7 +230,7 @@ public class DeclarationParser {
else if (ElementType.CLASS_KEYWORD_BIT_SET.contains(builder.getTokenType())) {
final PsiBuilder.Marker root = parseClassFromKeyword(builder, declaration, false);
if (context == Context.FILE) {
// todo: append following declarations to root
// todo: append following declarations to root (?)
boolean declarationsAfterEnd = false;
while (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.RBRACE) {
@@ -344,7 +349,7 @@ public class DeclarationParser {
if (typeParams != null) {
typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type);
}
return parseFieldOrLocalVariable(builder, declaration, context);
return parseFieldOrLocalVariable(builder, declaration, declarationStart, context);
}
@NotNull
@@ -538,7 +543,7 @@ public class DeclarationParser {
@Nullable
private static PsiBuilder.Marker parseFieldOrLocalVariable(final PsiBuilder builder, final PsiBuilder.Marker declaration,
final Context context) {
final int declarationStart, final Context context) {
final IElementType varType;
if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) {
varType = JavaElementType.FIELD;
@@ -553,46 +558,66 @@ public class DeclarationParser {
}
PsiBuilder.Marker variable = declaration;
boolean openMarker = true;
boolean unclosed = false;
boolean eatSemicolon = true;
boolean expectSemicolon = true;
boolean shouldRollback;
boolean openMarker = true;
while (true) {
shouldRollback = true;
if (!eatBrackets(builder)) {
expectSemicolon = false;
unclosed = true;
}
if (expect(builder, JavaTokenType.EQ)) {
final PsiBuilder.Marker expr = ExpressionParser.parse(builder);
if (expr == null) {
if (expr != null) {
shouldRollback = false;
}
else {
error(builder, JavaErrorMessages.message("expected.expression"));
expectSemicolon = false;
unclosed = true;
break;
}
}
if (builder.getTokenType() == JavaTokenType.COMMA) {
variable.done(varType);
builder.advanceLexer();
variable = builder.mark();
}
else {
if (builder.getTokenType() != JavaTokenType.COMMA) break;
variable.done(varType);
builder.advanceLexer();
if (builder.getTokenType() != JavaTokenType.IDENTIFIER) {
error(builder, JavaErrorMessages.message("expected.identifier"));
unclosed = true;
eatSemicolon = false;
openMarker = false;
break;
}
if (!expect(builder, JavaTokenType.IDENTIFIER)) {
variable.drop();
error(builder, JavaErrorMessages.message("expected.identifier"));
openMarker = false;
eatSemicolon = false;
break;
}
variable = builder.mark();
builder.advanceLexer();
}
if (eatSemicolon) {
if (!expect(builder, JavaTokenType.SEMICOLON) && expectSemicolon) {
if (builder.getTokenType() == JavaTokenType.SEMICOLON && eatSemicolon) {
builder.advanceLexer();
}
else {
// special treatment (see DeclarationParserTest.testMultiLineUnclosed())
if (!builder.eof() && shouldRollback) {
final CharSequence text = builder.getOriginalText();
final int spaceEnd = builder.getCurrentOffset();
final int spaceStart = CharArrayUtil.shiftBackward(text, spaceEnd-1, WHITESPACES);
final int lineStart = CharArrayUtil.shiftBackwardUntil(text, spaceEnd, LINE_ENDS);
if (declarationStart < lineStart && lineStart < spaceStart) {
final int newBufferEnd = CharArrayUtil.shiftForward(text, lineStart, WHITESPACES);
declaration.rollbackTo();
return parse(stoppingBuilder(builder, newBufferEnd), context);
}
}
if (!unclosed) {
error(builder, JavaErrorMessages.message("expected.semicolon"));
}
// todo: special treatment - see DeclarationParserTest.testMultiLineUnclosed()
}
if (openMarker) {
@@ -107,6 +107,25 @@ public class JavaParserUtil {
}
return (braceCount == 0 ? null : tokenType);
}
@Override
public boolean eof() {
return braceCount == 0 || super.eof();
}
};
}
public static PsiBuilder stoppingBuilder(final PsiBuilder builder, final int stopAt) {
return new PsiBuilderAdapter(builder) {
@Override
public IElementType getTokenType() {
return getCurrentOffset() < stopAt ? super.getTokenType() : null;
}
@Override
public boolean eof() {
return getCurrentOffset() < stopAt || super.eof();
}
};
}
@@ -21,7 +21,6 @@ import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.filters.OrFilter;
import com.intellij.psi.impl.compiled.ClsElementImpl;
import com.intellij.psi.impl.source.PsiImmediateClassType;
@@ -39,10 +38,7 @@ import com.intellij.psi.search.SearchScope;
import com.intellij.psi.util.*;
import com.intellij.ui.IconDeferrer;
import com.intellij.ui.RowIcon;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ReflectionCache;
import com.intellij.util.SmartList;
import com.intellij.util.*;
import com.intellij.util.containers.HashMap;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
@@ -864,6 +860,7 @@ public class PsiClassImplUtil {
}
public static boolean isClassEquivalentTo(PsiClass aClass, PsiElement another) {
if (aClass == another) return true;
if (!(another instanceof PsiClass)) return false;
String name1 = aClass.getName();
if (name1 == null) return false;
@@ -900,17 +897,13 @@ public class PsiClassImplUtil {
final PsiFile file1 = aClass.getContainingFile().getOriginalFile();
final PsiFile file2 = another.getContainingFile().getOriginalFile();
if (file1.equals(file2)) {
return true;
}
//see com.intellij.openapi.vcs.changes.PsiChangeTracker
//see com.intellij.psi.impl.PsiFileFactoryImpl#createFileFromText(CharSequence,PsiFile)
final PsiFile original1 = file1.getUserData(PsiFileFactory.ORIGINAL_FILE);
final PsiFile original2 = file2.getUserData(PsiFileFactory.ORIGINAL_FILE);
if (original1 == original2 && original1 != null
|| original1 == file2 || original2 == file1) {
return true;
if (original1 == original2 && original1 != null || original1 == file2 || original2 == file1 || file1 == file2) {
return compareClassSeqNumber(aClass, (PsiClass)another);
}
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(aClass.getProject()).getFileIndex();
@@ -920,6 +913,28 @@ public class PsiClassImplUtil {
(fileIndex.isInSource(vfile2) || fileIndex.isInLibraryClasses(vfile2));
}
private static boolean compareClassSeqNumber(PsiClass aClass, PsiClass another) {
// there may be several classes in one file, they must not be equal
int index1 = getSeqNumber(aClass);
if (index1 == -1) return true;
int index2 = getSeqNumber(another);
return index1 == index2;
}
private static int getSeqNumber(PsiClass aClass) {
// sequence number of this class among its parent' child classes named the same
PsiElement parent = aClass.getParent();
if (parent == null) return -1;
int seqNo = 0;
for (PsiElement child : parent.getChildren()) {
if (child == aClass) return seqNo;
if (child instanceof PsiClass && Comparing.strEqual(aClass.getName(), ((PsiClass)child).getName())) {
seqNo++;
}
}
return -1;
}
private static PsiElement originalElement(PsiClass aClass) {
final PsiElement originalElement = aClass.getOriginalElement();
final PsiCompiledElement compiled = originalElement.getUserData(ClsElementImpl.COMPILED_ELEMENT);
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.impl.PsiSubstitutorImpl;
import com.intellij.psi.impl.search.JavaDirectInheritorsSearcher;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.impl.source.tree.TreeElement;
@@ -167,7 +168,15 @@ public class ClsJavaCodeReferenceElementImpl extends ClsElementImpl implements P
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable((PsiTypeParameterListOwner)element)) {
if (myQualifiedName.equals(parameter.getName())) return parameter;
}
return JavaPsiFacade.getInstance(getProject()).findClass(myQualifiedName, getResolveScope());
return resolveClassPreferringMyJar();
}
private PsiClass resolveClassPreferringMyJar() {
PsiClass[] classes = JavaPsiFacade.getInstance(getProject()).findClasses(myQualifiedName, getResolveScope());
for (PsiClass aClass : classes) {
if (JavaDirectInheritorsSearcher.isFromTheSameJar(aClass, this)) return aClass;
}
return classes.length == 0 ? null : classes[0];
}
public void processVariants(PsiScopeProcessor processor) {
@@ -8,6 +8,8 @@ import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.java.stubs.index.JavaAnonymousClassBaseRefOccurenceIndex;
@@ -19,14 +21,17 @@ import com.intellij.psi.search.searches.AllClassesSearch;
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
import com.intellij.util.Processor;
import com.intellij.util.QueryExecutor;
import com.intellij.util.containers.HashMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* @author max
*/
public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, DirectClassInheritorsSearch.SearchParameters> {
public boolean execute(final DirectClassInheritorsSearch.SearchParameters p, final Processor<PsiClass> consumer) {
final PsiClass aClass = p.getClassToProcess();
final PsiManagerImpl psiManager = (PsiManagerImpl)PsiManager.getInstance(aClass.getProject());
@@ -73,10 +78,22 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
}
});
Map<String, List<PsiClass>> classes = new HashMap<String, List<PsiClass>>();
for (PsiReferenceList referenceList : candidates) {
ProgressManager.checkCanceled();
PsiClass candidate = (PsiClass)referenceList.getParent();
if (!consumer.process(candidate)) return false;
String fqn = candidate.getQualifiedName();
List<PsiClass> list = classes.get(fqn);
if (list == null) {
list = new ArrayList<PsiClass>();
classes.put(fqn, list);
}
list.add(candidate);
}
for (List<PsiClass> sameNamedClasses : classes.values()) {
if (!processSameNamedClasses(consumer, aClass, sameNamedClasses)) return false;
}
if (p.includeAnonymous()) {
@@ -116,4 +133,37 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
return true;
}
private static boolean processSameNamedClasses(Processor<PsiClass> consumer, PsiClass aClass, List<PsiClass> sameNamedClasses) {
// if there is a class from the same jar, prefer it
boolean sameJarClassFound = false;
for (PsiClass sameNamedClass : sameNamedClasses) {
boolean fromSameJar = isFromTheSameJar(sameNamedClass, aClass);
if (fromSameJar) {
sameJarClassFound = true;
if (!consumer.process(sameNamedClass)) return false;
}
}
if (!sameJarClassFound) {
for (PsiClass sameNamedClass : sameNamedClasses) {
if (!consumer.process(sameNamedClass)) return false;
}
}
return true;
}
private static VirtualFile getJarFile(PsiElement candidate) {
VirtualFile file = candidate.getContainingFile().getVirtualFile();
if (file != null && file.getFileSystem() instanceof JarFileSystem) {
return JarFileSystem.getInstance().getVirtualFileForJar(file);
}
return file;
}
public static boolean isFromTheSameJar(PsiElement candidate, PsiElement other) {
VirtualFile c1 = getJarFile(candidate);
VirtualFile c2 = getJarFile(other);
return c1 != null && c1 == c2;
}
}
@@ -27,6 +27,7 @@ import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.impl.source.jsp.JspContextManager;
import com.intellij.psi.impl.source.jsp.jspJava.JspCodeBlock;
import com.intellij.psi.impl.source.tree.*;
import com.intellij.psi.jsp.BaseJspFile;
import com.intellij.psi.jsp.JspElementType;
import com.intellij.psi.jsp.JspFile;
import com.intellij.psi.jsp.JspSpiUtil;
@@ -289,10 +290,10 @@ public class PsiLocalVariableImpl extends CompositePsiElement implements PsiLoca
}
final Set<PsiFile> allIncluded = new THashSet<PsiFile>(10);
final JspFile rootContext = contextManager.getRootContextFile(jspFile);
final BaseJspFile rootContext = contextManager.getRootContextFile(jspFile);
allIncluded.add(rootContext);
JspSpiUtil.visitAllIncludedFilesRecursively(rootContext, new Processor<JspFile>() {
public boolean process(final JspFile file) {
JspSpiUtil.visitAllIncludedFilesRecursively(rootContext, new Processor<BaseJspFile>() {
public boolean process(final BaseJspFile file) {
allIncluded.add(file);
return true;
}
@@ -7,7 +7,7 @@ PsiJavaFile:MultiLineUnclosed.java
PsiKeyword:int('int')
PsiErrorElement:Identifier expected
<empty list>
PsiWhiteSpace(' \n ')
PsiWhiteSpace(' \n ')
PsiField:o
PsiModifierList:
<empty list>
@@ -21,7 +21,6 @@ import com.intellij.lang.java.parser.JavaParsingTestCase;
import com.intellij.pom.java.LanguageLevel;
// todo: fix parser and uncomment tests
public class DeclarationParserTest extends JavaParsingTestCase {
public DeclarationParserTest() {
super("parser-partial/declarations");
@@ -78,7 +77,7 @@ public class DeclarationParserTest extends JavaParsingTestCase {
public void testUnclosedComma() { doParserTest("{ int field, }", false, false); }
public void testUnclosedSemicolon() { doParserTest("{ int field }", false, false); }
public void testMissingInitializerExpression() { doParserTest("{ int field=; }", false, false); }
//public void testMultiLineUnclosed() { doParserTest("{ int \n Object o; }", false, false); }
public void testMultiLineUnclosed() { doParserTest("{ int \n Object o; }", false, false); }
public void testMethodNormal0() { doParserTest("{ void f() {} }", false, false); }
public void testMethodNormal1() { doParserTest("{ void f(); }", false, false); }
@@ -18,6 +18,7 @@ package com.intellij.psi.impl.source.jsp;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.jsp.BaseJspFile;
import com.intellij.psi.jsp.JspFile;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
@@ -32,20 +33,22 @@ public abstract class JspContextManager {
return project.getComponent(JspContextManager.class);
}
public abstract JspFile[] getSuitableContextFiles(@NotNull PsiFile file);
public abstract BaseJspFile[] getSuitableContextFiles(@NotNull PsiFile file);
public abstract void setContextFile(@NotNull PsiFile file, @Nullable JspFile contextFile, final boolean userDefined);
public abstract void setContextFile(@NotNull PsiFile file, @Nullable BaseJspFile contextFile);
public abstract @Nullable JspFile getContextFile(@NotNull PsiFile file);
public abstract @Nullable
BaseJspFile getContextFile(@NotNull PsiFile file);
public abstract @Nullable JspFile getConfiguredContextFile(@NotNull PsiFile file);
public @NotNull JspFile getRootContextFile(@NotNull JspFile file) {
JspFile rootContext = file;
HashSet<JspFile> recursionPreventer = new HashSet<JspFile>();
public @NotNull
BaseJspFile getRootContextFile(@NotNull BaseJspFile file) {
BaseJspFile rootContext = file;
HashSet<BaseJspFile> recursionPreventer = new HashSet<BaseJspFile>();
do {
recursionPreventer.add(rootContext);
JspFile context = getContextFile(rootContext);
BaseJspFile context = getContextFile(rootContext);
if (context == null || recursionPreventer.contains(context)) break;
rootContext = context;
}
@@ -62,14 +62,14 @@ public abstract class JspSpiUtil {
protected abstract int _escapeCharsInJspContext(JspFile file, int offset, String toEscape) throws IncorrectOperationException;
public static void visitAllIncludedFilesRecursively(JspFile jspFile, Processor<JspFile> visitor) {
public static void visitAllIncludedFilesRecursively(BaseJspFile jspFile, Processor<BaseJspFile> visitor) {
final JspSpiUtil util = getJspSpiUtil();
if (util != null) {
util._visitAllIncludedFilesRecursively(jspFile, visitor);
}
}
protected abstract void _visitAllIncludedFilesRecursively(JspFile jspFile, Processor<JspFile> visitor);
protected abstract void _visitAllIncludedFilesRecursively(BaseJspFile jspFile, Processor<BaseJspFile> visitor);
@Nullable
public static PsiElement resolveMethodPropertyReference(@NotNull PsiReference reference, @Nullable PsiClass resolvedClass, boolean readable) {
@@ -919,8 +919,9 @@ public class TypeConversionUtil {
substitutor = getSuperClassSubstitutorInner(superClass, derivedClass, derivedSubstitutor, visited, manager);
}
if (substitutor == null) {
LOG.error("Not inheritor: " + derivedClass + "(" + derivedClass.getClass().getName() + ");" +
" super: " + superClass + "(" + derivedClass.getClass().getName() + ")");
LOG.error(
"Not inheritor: " + derivedClass + "(" + derivedClass.getClass().getName() + "; " + PsiUtil.getVirtualFile(derivedClass) + ");" +
"\n super: " + superClass + "(" + superClass.getClass().getName() + "; " + PsiUtil.getVirtualFile(superClass) + ")");
}
return substitutor;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 727 B

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

@@ -35,16 +35,12 @@ public class FileIncludeInfo {
this.runtimeOnly = runtimeOnly;
}
public FileIncludeInfo(@NotNull String fileName, @NotNull String path, int offset) {
this(fileName, path, offset, false);
}
public FileIncludeInfo(@NotNull String fileName, @NotNull String path) {
this(fileName, path, -1, false);
public FileIncludeInfo(@NotNull String path, int offset) {
this(getFileName(path), path, offset, false);
}
public FileIncludeInfo(@NotNull String path) {
this(getFileName(path), path, -1, false);
this(path, -1);
}
private static String getFileName(String path) {
@@ -18,9 +18,11 @@ package com.intellij.psi.impl.include;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileSystemItem;
import com.intellij.util.Processor;
import org.jetbrains.annotations.Nullable;
/**
@@ -36,6 +38,8 @@ public abstract class FileIncludeManager {
public abstract VirtualFile[] getIncludingFiles(VirtualFile file, boolean compileTimeOnly);
public abstract void processIncludingFiles(PsiFile context, Processor<Pair<VirtualFile, FileIncludeInfo>> processor);
@Nullable
public abstract PsiFileSystemItem resolveFileReference(String text, PsiFile context);
public abstract PsiFileSystemItem resolveFileInclude(FileIncludeInfo info, PsiFile context);
}
@@ -229,6 +229,16 @@ public class PsiTreeUtil {
return result == null ? null : ArrayUtil.toObjectArray(result, aClass);
}
@NotNull public static <T extends PsiElement> List<T> getChildrenOfTypeAsList(@NotNull PsiElement element, @NotNull Class<T> aClass) {
List<T> result = new SmartList<T>();
for(PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()){
if (instanceOf(aClass, child)) {
result.add((T)child);
}
}
return result;
}
private static boolean instanceOf(final Class aClass, final PsiElement child) {
/*
@@ -580,7 +590,8 @@ public class PsiTreeUtil {
while (true);
}
public static PsiElement prevLeaf(PsiElement current){
@Nullable
public static PsiElement prevLeaf(@NotNull PsiElement current){
final PsiElement prevSibling = current.getPrevSibling();
if(prevSibling != null) return lastChild(prevSibling);
final PsiElement parent = current.getParent();
@@ -588,7 +599,8 @@ public class PsiTreeUtil {
return prevLeaf(parent);
}
public static PsiElement nextLeaf(PsiElement current){
@Nullable
public static PsiElement nextLeaf(@NotNull PsiElement current){
final PsiElement nextSibling = current.getNextSibling();
if(nextSibling != null) return firstChild(nextSibling);
final PsiElement parent = current.getParent();
@@ -602,17 +614,20 @@ public class PsiTreeUtil {
return element;
}
public static PsiElement firstChild(final PsiElement element) {
if(element.getFirstChild() != null) return firstChild(element.getFirstChild());
public static PsiElement firstChild(@NotNull final PsiElement element) {
PsiElement child = element.getFirstChild();
if(child != null) return firstChild(child);
return element;
}
public static PsiElement prevLeaf(final PsiErrorElement element, final boolean skipEmptyElements) {
@Nullable
public static PsiElement prevLeaf(@NotNull final PsiErrorElement element, final boolean skipEmptyElements) {
PsiElement prevLeaf = prevLeaf(element);
while (skipEmptyElements && prevLeaf != null && prevLeaf.getTextLength() == 0) prevLeaf = prevLeaf(prevLeaf);
return prevLeaf;
}
@Nullable
public static PsiElement nextLeaf(final PsiErrorElement element, final boolean skipEmptyElements) {
PsiElement nextLeaf = nextLeaf(element);
while (skipEmptyElements && nextLeaf != null && nextLeaf.getTextLength() == 0) nextLeaf = nextLeaf(nextLeaf);
@@ -349,7 +349,14 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler {
else {
space = "";
}
TextRange range = insertNestedComments(chars, startOffset, endOffset, space + commentPrefix + "\n", space + commentSuffix + "\n", commenter);
final StringBuilder nestingPrefix = new StringBuilder(space).append(commentPrefix);
if (!commentPrefix.endsWith("\n")){
nestingPrefix.append("\n");
}
final StringBuilder nestingSuffix = new StringBuilder(space);
nestingSuffix.append(commentSuffix.startsWith("\n") ? commentSuffix.substring(1) : commentSuffix);
nestingSuffix.append("\n");
TextRange range = insertNestedComments(chars, startOffset, endOffset, nestingPrefix.toString(), nestingSuffix.toString(), commenter);
myEditor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset());
//myEditor.getSelectionModel().removeSelection();
LogicalPosition pos = new LogicalPosition(caretPosition.line + 1, caretPosition.column);
@@ -224,7 +224,8 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
}
boolean allLineCommented = true;
boolean commentWithIndent = !CodeStyleSettingsManager.getSettings(myProject).LINE_COMMENT_AT_FIRST_COLUMN;
for (int line = myStartLine; line <= myEndLine; line++) {
Commenter commenter = blockSuitableCommenter != null ? blockSuitableCommenter : findCommenter(line);
if (commenter == null) return;
@@ -245,12 +246,18 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
myCommenters[line - myStartLine] = commenter;
if (!isLineCommented(line, chars, commenter) && (singleline || !isLineEmpty(line))) {
allLineCommented = false;
if (commenter instanceof IndentedCommenter){
final Boolean value = ((IndentedCommenter)commenter).forceIndentedLineComment();
if (value != null){
commentWithIndent = value;
}
}
break;
}
}
if (!allLineCommented) {
if (CodeStyleSettingsManager.getSettings(myProject).LINE_COMMENT_AT_FIRST_COLUMN) {
if (!commentWithIndent) {
doDefaultCommenting(blockSuitableCommenter);
}
else {
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2010 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.codeInsight.generation;
import com.intellij.lang.Commenter;
import org.jetbrains.annotations.Nullable;
/**
* @author oleg
*/
public interface IndentedCommenter extends Commenter {
/**
* Used to override CodeStyleSetings#LINE_COMMENT_AT_FIRST_COLUMN option
* @return true or false to overrride, null to use settings option
*/
@Nullable
Boolean forceIndentedLineComment();
}
@@ -0,0 +1,74 @@
/*
* Copyright 2000-2010 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.ide.actions;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.FileTemplateUtil;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import javax.swing.*;
/**
* @author Dmitry Avdeev
*/
public abstract class CreateFileFromTemplateAction extends CreateFromTemplateAction<PsiFile> {
public CreateFileFromTemplateAction(String text, String description, Icon icon) {
super(text, description, icon);
}
protected PsiFile createFileFromTemplate(String name, FileTemplate template, PsiDirectory dir) {
PsiElement element;
try {
element = FileTemplateUtil
.createFromTemplate(template, name, FileTemplateManager.getInstance().getDefaultProperties(), dir);
final PsiFile psiFile = element.getContainingFile();
final VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile != null) {
FileEditorManager.getInstance(dir.getProject()).openFile(virtualFile, true);
String property = getDefaultTemplateProperty();
if (property != null) {
PropertiesComponent.getInstance(dir.getProject()).setValue(property, template.getName());
}
return psiFile;
}
}
catch (IncorrectOperationException e) {
throw e;
}
catch (Exception e) {
LOG.error(e);
}
return null;
}
@Override
protected PsiFile createFile(String name, String templateName, PsiDirectory dir) {
final FileTemplate template = FileTemplateManager.getInstance().getInternalTemplate(templateName);
return createFileFromTemplate(name, template, dir);
}
}
@@ -17,19 +17,13 @@ package com.intellij.ide.actions;
import com.intellij.CommonBundle;
import com.intellij.ide.IdeView;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.FileTemplateUtil;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -39,37 +33,12 @@ import javax.swing.*;
* @author Eugene.Kudelevsky
*/
public abstract class CreateFromTemplateAction<T extends PsiElement> extends AnAction {
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.CreateFromTemplateAction");
protected static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.CreateFromTemplateAction");
public CreateFromTemplateAction(String text, String description, Icon icon) {
super(text, description, icon);
}
protected static PsiFile createFileFromTemplate(String name, String templateName, PsiDirectory dir) {
final FileTemplate template = FileTemplateManager.getInstance().getInternalTemplate(templateName);
PsiElement element;
try {
element = FileTemplateUtil
.createFromTemplate(template, name, FileTemplateManager.getInstance().getDefaultProperties(), dir);
final PsiFile psiFile = element.getContainingFile();
final VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile != null) {
FileEditorManager.getInstance(dir.getProject()).openFile(virtualFile, true);
return psiFile;
}
}
catch (IncorrectOperationException e) {
throw e;
}
catch (Exception e) {
LOG.error(e);
}
return null;
}
public final void actionPerformed(final AnActionEvent e) {
final DataContext dataContext = e.getDataContext();
@@ -105,22 +74,30 @@ public abstract class CreateFromTemplateAction<T extends PsiElement> extends AnA
});
if (createdElement != null) {
view.selectElement(createdElement);
postProcesss(createdElement, selectedTemplateName.get());
postProcess(createdElement, selectedTemplateName.get());
}
}
protected void postProcesss(T createdElement, String templateName) {
protected void postProcess(T createdElement, String templateName) {
}
@Nullable
protected abstract T createFile(String name, String templateName, PsiDirectory dir);
protected abstract void checkBeforeCreate(String name, String templateName, PsiDirectory dir);
protected void checkBeforeCreate(String name, String templateName, PsiDirectory dir) {
dir.checkCreateFile(name);
}
protected abstract void buildDialog(Project project, PsiDirectory directory, CreateFileFromTemplateDialog.Builder builder);
@Nullable
protected String getDefaultTemplateName(@NotNull PsiDirectory dir) {
String property = getDefaultTemplateProperty();
return property == null ? null : PropertiesComponent.getInstance(dir.getProject()).getValue(property);
}
@Nullable
protected String getDefaultTemplateProperty() {
return null;
}
@@ -0,0 +1,56 @@
/*
* Copyright 2000-2010 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.ide.actions;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.psi.PsiDirectory;
import javax.swing.*;
/**
* @author Dmitry Avdeev
*/
public abstract class TemplateKindProvider {
private final static ExtensionPointName<TemplateKindProvider> EP_NAME =
ExtensionPointName.create("com.intellij.javaee.templateKindProvider");
public static void addAdditionalKinds(AnAction action, PsiDirectory dir, CreateFileFromTemplateDialog.Builder builder) {
String id = ActionManager.getInstance().getId(action);
for (TemplateKindProvider provider : Extensions.getExtensions(EP_NAME)) {
for (Kind kind : provider.getAdditionalKinds(dir)) {
builder.addKind(kind.name, kind.icon, kind.templateName);
}
}
}
public abstract boolean isAvailable(Class<? extends AnAction> actionClass);
public abstract Kind[] getAdditionalKinds(PsiDirectory dir);
public static class Kind {
public final String name;
public final String templateName;
public final Icon icon;
public Kind(String name, String templateName, Icon icon) {
this.name = name;
this.templateName = templateName;
this.icon = icon;
}
}
}
@@ -195,7 +195,7 @@ public class EditorWindow implements EditorEx, UserDataHolderEx {
}
@NotNull
public FoldingModel getFoldingModel() {
public FoldingModelEx getFoldingModel() {
return myFoldingModelWindow;
}
@@ -283,6 +283,11 @@ public class EditorWindow implements EditorEx, UserDataHolderEx {
@NotNull
public LogicalPosition offsetToLogicalPosition(final int offset) {
return offsetToLogicalPosition(offset, true);
}
@NotNull
public LogicalPosition offsetToLogicalPosition(final int offset, boolean softWrapAware) {
assert isValid();
int lineNumber = myDocumentWindow.getLineNumber(offset);
int lineStartOffset = myDocumentWindow.getLineStartOffset(lineNumber);
@@ -465,6 +470,11 @@ public class EditorWindow implements EditorEx, UserDataHolderEx {
@NotNull
public LogicalPosition visualToLogicalPosition(@NotNull final VisualPosition pos) {
return visualToLogicalPosition(pos, true);
}
@NotNull
public LogicalPosition visualToLogicalPosition(@NotNull final VisualPosition pos, boolean softWrapAware) {
assert isValid();
return new LogicalPosition(pos.line, pos.column);
}
@@ -112,7 +112,7 @@ public class TogglePopupHintsPanel implements StatusBarWidget, StatusBarWidget.I
return "InspectionProfile";
}
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return this;
}
@@ -38,6 +38,7 @@ import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -163,7 +163,7 @@ public class FileIncludeIndex extends FileBasedIndexExtension<FileIncludeIndex.K
}
public int getVersion() {
return 0;
return 1;
}
interface Key {
@@ -18,6 +18,8 @@ package com.intellij.psi.impl.include;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* @author Dmitry Avdeev
*/
@@ -26,7 +28,7 @@ class FileIncludeInfoImpl extends FileIncludeInfo {
public final String providerId;
public FileIncludeInfoImpl(@NotNull String path, int offset, boolean runtimeOnly, String providerId) {
super("", path, offset, runtimeOnly);
super(new File(path).getName(), path, offset, runtimeOnly);
this.providerId = providerId;
}
@@ -19,6 +19,7 @@ package com.intellij.psi.impl.include;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileWithId;
@@ -34,12 +35,11 @@ import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.ParameterizedCachedValue;
import com.intellij.psi.util.ParameterizedCachedValueProvider;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Processor;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.MultiMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* @author Dmitry Avdeev
@@ -54,46 +54,68 @@ public class FileIncludeManagerImpl extends FileIncludeManager {
private final IncludeCacheHolder myIncludedHolder = new IncludeCacheHolder("compile time includes", "runtime includes") {
@Override
protected VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly) {
GlobalSearchScope scope = GlobalSearchScope.allScope(myProject);
List<FileIncludeInfoImpl> infoList = FileIncludeIndex.getIncludes(file.getVirtualFile(), scope);
ArrayList<VirtualFile> files = new ArrayList<VirtualFile>();
for (FileIncludeInfoImpl info : infoList) {
if (compileTimeOnly && info.runtimeOnly) {
continue;
}
FileIncludeProvider includeProvider = myProviderMap.get(info.providerId);
if (includeProvider != null) {
PsiFileSystemItem virtualFile = includeProvider.resolveInclude(info, file, myProject);
if (virtualFile != null) {
files.add(virtualFile.getVirtualFile());
protected VirtualFile[] computeFiles(final PsiFile file, final boolean compileTimeOnly) {
final ArrayList<VirtualFile> files = new ArrayList<VirtualFile>();
processIncludes(file, new Processor<FileIncludeInfo>() {
@Override
public boolean process(FileIncludeInfo info) {
if (compileTimeOnly != info.runtimeOnly) {
PsiFileSystemItem virtualFile = resolveFileInclude(info, file);
if (virtualFile != null) {
files.add(virtualFile.getVirtualFile());
}
}
return true;
}
});
return files.toArray(new VirtualFile[files.size()]);
}
};
public void processIncludes(PsiFile file, Processor<FileIncludeInfo> processor) {
GlobalSearchScope scope = GlobalSearchScope.allScope(myProject);
List<FileIncludeInfoImpl> infoList = FileIncludeIndex.getIncludes(file.getVirtualFile(), scope);
for (FileIncludeInfoImpl info : infoList) {
if (!processor.process(info)) {
return;
}
}
}
private final IncludeCacheHolder myIncludingHolder = new IncludeCacheHolder("compile time contexts", "runtime contexts") {
@Override
protected VirtualFile[] computeFiles(PsiFile context, boolean compileTimeOnly) {
final ArrayList<VirtualFile> files = new ArrayList<VirtualFile>();
processIncludingFiles(context, new Processor<Pair<VirtualFile, FileIncludeInfo>>() {
@Override
public boolean process(Pair<VirtualFile, FileIncludeInfo> virtualFileFileIncludeInfoPair) {
files.add(virtualFileFileIncludeInfoPair.first);
return true;
}
});
return VfsUtil.toVirtualFileArray(files);
}
};
private final IncludeCacheHolder myIncludingHolder = new IncludeCacheHolder("compile time contexts", "runtime contexts") {
@Override
protected VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly) {
MultiMap<VirtualFile,FileIncludeInfoImpl> infoList = FileIncludeIndex.getIncludingFileCandidates(file.getName(), GlobalSearchScope.allScope(myProject));
ArrayList<VirtualFile> files = new ArrayList<VirtualFile>();
for (VirtualFile candidate : infoList.keySet()) {
PsiFile psiFile = myPsiManager.findFile(candidate);
if (psiFile == null) continue;
for (FileIncludeInfoImpl info : infoList.get(candidate)) {
FileIncludeProvider includeProvider = myProviderMap.get(info.providerId);
if (includeProvider != null) {
if (file.equals(includeProvider.resolveInclude(info, psiFile, myProject))) {
files.add(candidate);
}
public void processIncludingFiles(PsiFile context, Processor<Pair<VirtualFile, FileIncludeInfo>> processor) {
context = context.getOriginalFile();
VirtualFile contextFile = context.getVirtualFile();
if (contextFile == null) return;
MultiMap<VirtualFile,FileIncludeInfoImpl> infoList = FileIncludeIndex.getIncludingFileCandidates(context.getName(), GlobalSearchScope.allScope(myProject));
for (VirtualFile candidate : infoList.keySet()) {
PsiFile psiFile = myPsiManager.findFile(candidate);
if (psiFile == null || context.equals(psiFile)) continue;
for (FileIncludeInfo info : infoList.get(candidate)) {
PsiFileSystemItem item = resolveFileInclude(info, psiFile);
if (item != null && contextFile.equals(item.getVirtualFile())) {
if (!processor.process(Pair.create(candidate, info))) {
return;
}
}
}
return VfsUtil.toVirtualFileArray(files);
}
};
}
public FileIncludeManagerImpl(Project project, PsiManager psiManager, PsiFileFactory psiFileFactory,
CachedValuesManager cachedValuesManager) {
@@ -120,19 +142,23 @@ public class FileIncludeManagerImpl extends FileIncludeManager {
}
}
@Override
public VirtualFile[] getIncludingFiles(VirtualFile file, boolean compileTimeOnly) {
return myIncludingHolder.getAllFiles(file, compileTimeOnly);
}
@Override
public PsiFileSystemItem resolveFileReference(String text, PsiFile context) {
PsiFileImpl psiFile = (PsiFileImpl)myPsiFileFactory.createFileFromText("dummy.txt", text);
public PsiFileSystemItem resolveFileInclude(FileIncludeInfo info, PsiFile context) {
PsiFileImpl psiFile = (PsiFileImpl)myPsiFileFactory.createFileFromText("dummy.txt", info.path);
psiFile.setOriginalFile(context);
return new FileReferenceSet(psiFile).resolve();
return new FileReferenceSet(psiFile) {
@Override
protected boolean useIncludingFileAsContext() {
return false;
}
}.resolve();
}
private abstract class IncludeCacheHolder {
@@ -140,14 +166,14 @@ public class FileIncludeManagerImpl extends FileIncludeManager {
private final Key<ParameterizedCachedValue<VirtualFile[], PsiFile>> COMPILE_TIME_KEY;
private final Key<ParameterizedCachedValue<VirtualFile[], PsiFile>> RUNTIME_KEY;
private final ParameterizedCachedValueProvider<VirtualFile[], PsiFile> COMPILE_TIME_PROVIDER = new IncludedFilesProvider(false) {
private final ParameterizedCachedValueProvider<VirtualFile[], PsiFile> COMPILE_TIME_PROVIDER = new IncludedFilesProvider(true) {
@Override
protected VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly) {
return IncludeCacheHolder.this.computeFiles(file, compileTimeOnly);
}
};
private final ParameterizedCachedValueProvider<VirtualFile[], PsiFile> RUNTIME_PROVIDER = new IncludedFilesProvider(true) {
private final ParameterizedCachedValueProvider<VirtualFile[], PsiFile> RUNTIME_PROVIDER = new IncludedFilesProvider(false) {
@Override
protected VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly) {
return IncludeCacheHolder.this.computeFiles(file, compileTimeOnly);
@@ -160,36 +186,20 @@ public class FileIncludeManagerImpl extends FileIncludeManager {
}
public VirtualFile[] getAllFiles(VirtualFile file, boolean compileTimeOnly) {
ArrayList<VirtualFile[]> result = new ArrayList<VirtualFile[]>();
Set<VirtualFile> result = new HashSet<VirtualFile>();
getFilesRecursively(file, compileTimeOnly, result);
switch (result.size()) {
case 0:
return VirtualFile.EMPTY_ARRAY;
case 1:
return result.get(0);
default:
int size = 0;
for (VirtualFile[] files : result) {
size+=files.length;
}
VirtualFile[] files = new VirtualFile[size];
int pos = 0;
for (VirtualFile[] virtualFiles : result) {
System.arraycopy(virtualFiles, 0, files, pos, virtualFiles.length);
pos += virtualFiles.length;
}
return files;
}
return result.toArray(new VirtualFile[result.size()]);
}
private void getFilesRecursively(VirtualFile file, boolean compileTimeOnly, List<VirtualFile[]> result) {
private void getFilesRecursively(VirtualFile file, boolean compileTimeOnly, Set<VirtualFile> result) {
if (result.contains(file)) return;
PsiFile psiFile = myPsiManager.findFile(file);
if (psiFile == null) return;
VirtualFile[] includes = compileTimeOnly
? myCachedValuesManager.getParameterizedCachedValue(psiFile, COMPILE_TIME_KEY, COMPILE_TIME_PROVIDER, false, psiFile)
: myCachedValuesManager.getParameterizedCachedValue(psiFile, RUNTIME_KEY, RUNTIME_PROVIDER, false, psiFile);
if (includes.length != 0) {
result.add(includes);
result.addAll(Arrays.asList(includes));
for (VirtualFile include : includes) {
getFilesRecursively(include, compileTimeOnly, result);
}
@@ -17,13 +17,9 @@
package com.intellij.psi.impl.include;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileSystemItem;
import com.intellij.util.indexing.FileContent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Dmitry Avdeev
@@ -39,9 +35,4 @@ public abstract class FileIncludeProvider {
@NotNull
public abstract FileIncludeInfo[] getIncludeInfos(FileContent content);
@Nullable
public PsiFileSystemItem resolveInclude(FileIncludeInfo include, PsiFile context, Project project) {
return FileIncludeManager.getManager(project).resolveFileReference(include.path, context);
}
}
@@ -76,7 +76,7 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF
protected PsiFile myOriginalFile = null;
private final FileViewProvider myViewProvider;
private static final Key<Document> HARD_REFERENCE_TO_DOCUMENT = new Key<Document>("HARD_REFERENCE_TO_DOCUMENT");
private final Object myStubLock = new String("file's stub lock");
private final Object myStubLock = new Object();
private SoftReference<StubTree> myStub;
protected final PsiManagerEx myManager;
private volatile Object myTreeElementPointer; // SoftReference/WeakReference to RepositoryTreeElement when has repository id, RepositoryTreeElement otherwise
@@ -22,15 +22,10 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -46,7 +41,6 @@ public class FileReferenceSet {
private static final FileType[] EMPTY_FILE_TYPES = {};
private static final char SEPARATOR = '/';
private static final String SEPARATOR_STRING = "/";
private static final Key<CachedValue<Collection<PsiFileSystemItem>>> DEFAULT_CONTEXTS_KEY = new Key<CachedValue<Collection<PsiFileSystemItem>>>("default file contexts");
public static final CustomizableReferenceProvider.CustomizationKey<Function<PsiFile, Collection<PsiFileSystemItem>>> DEFAULT_PATH_EVALUATOR_OPTION =
new CustomizableReferenceProvider.CustomizationKey<Function<PsiFile, Collection<PsiFileSystemItem>>>(PsiBundle.message("default.path.evaluator.option"));
public static final Function<PsiFile, Collection<PsiFileSystemItem>> ABSOLUTE_TOP_LEVEL = new Function<PsiFile, Collection<PsiFileSystemItem>>() {
@@ -262,17 +256,7 @@ public class FileReferenceSet {
return getAbsoluteTopLevelDirLocations(file);
}
final CachedValueProvider<Collection<PsiFileSystemItem>> myDefaultContextProvider = new CachedValueProvider<Collection<PsiFileSystemItem>>() {
public Result<Collection<PsiFileSystemItem>> compute() {
final Collection<PsiFileSystemItem> contexts = getContextByFile(file);
return Result.createSingleDependency(contexts,
PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
}
};
final CachedValuesManager cachedValuesManager = CachedValuesManager.getManager(myElement.getProject());
final Collection<PsiFileSystemItem> value =
cachedValuesManager.getCachedValue(file, DEFAULT_CONTEXTS_KEY, myDefaultContextProvider, false);
return value == null ? Collections.<PsiFileSystemItem>emptyList() : value;
return getContextByFile(file);
}
@Nullable
@@ -285,7 +269,7 @@ public class FileReferenceSet {
return file.getOriginalFile();
}
@Nullable
@NotNull
private Collection<PsiFileSystemItem> getContextByFile(@NotNull PsiFile file) {
final PsiElement context = file.getContext();
if (context != null) file = context.getContainingFile();
@@ -70,6 +70,10 @@ public interface StatusBar extends StatusBarInfo {
@Deprecated
void addCustomIndicationComponent(@NotNull JComponent c);
/**
* @deprecated use removeWidget instead
*/
@Deprecated
void removeCustomIndicationComponent(@NotNull JComponent c);
void removeWidget(@NotNull String id);
@@ -33,7 +33,7 @@ import java.awt.event.MouseEvent;
*/
public interface StatusBarWidget extends Disposable {
enum Type {
enum PlatformType {
DEFAULT, MAC
}
@@ -75,7 +75,7 @@ public interface StatusBarWidget extends Disposable {
String ID();
@Nullable
WidgetPresentation getPresentation(@NotNull Type type);
WidgetPresentation getPresentation(@NotNull PlatformType type);
void install(@NotNull final StatusBar statusBar);
@@ -74,7 +74,7 @@ public class IdeMessagePanel extends JPanel implements MessagePoolListener, Cust
return "FatalError";
}
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return null;
}
@@ -50,7 +50,7 @@ public class IdeNotificationArea implements StatusBarWidget, StatusBarWidget.Ico
public IdeNotificationArea() {
}
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return this;
}
@@ -61,6 +61,9 @@ public class DuplicateAction extends EditorAction {
VisualPosition caret = editor.getCaretModel().getVisualPosition();
LogicalPosition lineStart = editor.visualToLogicalPosition(new VisualPosition(caret.line, 0));
LogicalPosition nextLineStart = editor.visualToLogicalPosition(new VisualPosition(caret.line + 1, 0));
if (nextLineStart.line == lineStart.line) {
nextLineStart = new LogicalPosition(lineStart.line+1, 0);
}
int start = editor.logicalPositionToOffset(lineStart);
int end = editor.logicalPositionToOffset(nextLineStart);
@@ -86,4 +89,4 @@ public class DuplicateAction extends EditorAction {
presentation.setText(EditorBundle.message("action.duplicate.line"), true);
}
}
}
}
@@ -21,6 +21,9 @@ import com.intellij.ide.DeleteProvider;
import com.intellij.ide.PasteProvider;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.FoldingModel;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.VisualPosition;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.editor.impl.TextDrawingCallback;
@@ -124,4 +127,13 @@ public interface EditorEx extends Editor {
int calcColumnNumber(int offset, int lineIndex);
TextDrawingCallback getTextDrawingCallback();
@NotNull
@Override
FoldingModelEx getFoldingModel();
@NotNull
LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visiblePos, boolean softWrapAware);
@NotNull LogicalPosition offsetToLogicalPosition(int offset, boolean softWrapAware);
}
@@ -35,26 +35,22 @@ import java.util.List;
public interface SoftWrapModelEx extends SoftWrapModel {
/**
* Asks current model to adjust logical position for the given visual position if necessary.
* <p/>
* Given logical position is allowed to be non-soft wrap aware, i.e. the one calculated as there are no soft wraps at the moment.
* Asks current model to map given visual position to logical.
*
* @param defaultLogical default logical position that corresponds to the given visual position
* @param visual target visual position for which logical position should be adjusted if necessary
* @param visual target visual position for which logical position should be mapped
* @return logical position that corresponds to the given visual position
*/
@NotNull
LogicalPosition adjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual);
LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visual);
/**
* Asks current model to adjust logical position for the given document offset.
* Asks current model to map given document offset to logical position.
*
* @param defaultLogical default logical position that corresponds to the given document offset
* @param offset target editor document offset
* @return logical position for the given editor document offset
*/
@NotNull
LogicalPosition adjustLogicalPosition(LogicalPosition defaultLogical, int offset);
LogicalPosition offsetToLogicalPosition(int offset);
/**
* Asks current model to adjust visual position that corresponds to the given logical position if necessary.
@@ -103,7 +99,15 @@ public interface SoftWrapModelEx extends SoftWrapModel {
* @param drawingType target drawing type
* @return width in pixels required for the painting of the given type
*/
int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType);
int getMinDrawingWidthInPixels(@NotNull SoftWrapDrawingType drawingType);
/**
* Allows to ask for the minimal width in columns required for painting of the given type.
*
* @param drawingType target drawing type
* @return width in columns required for the painting of the given type
*/
int getMinDrawingWidthInColumns(@NotNull SoftWrapDrawingType drawingType);
/**
* Registers given listener within the current model
@@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class EditorUtil {
private EditorUtil() { }
@@ -178,54 +179,69 @@ public class EditorUtil {
*/
public static int calcOffset(Editor editor, CharSequence text, int start, int end, int columnNumber, int tabSize) {
final int maxScanIndex = Math.min(start + columnNumber + 1, end);
if (editor == null) {
return calcSoftWrapUnawareOffset(text, start, maxScanIndex, columnNumber, tabSize);
}
EditorEx editorImpl = (EditorEx)editor;
int offset = start;
IterationState state = new IterationState(editorImpl, offset, false);
int fontType = state.getMergedAttributes().getFontType();
int column = 0;
int x = 0;
int spaceSize = getSpaceWidth(fontType, editorImpl);
SoftWrapModel softWrapModel = editor.getSoftWrapModel();
while (column < columnNumber) {
TextChange softWrap = softWrapModel.getSoftWrap(offset);
if (softWrap != null) {
x = softWrapModel.getSoftWrapIndentWidthInPixels(softWrap);
List<? extends TextChange> softWraps = softWrapModel.getSoftWrapsForRange(start, maxScanIndex);
int startToUse = start;
int x = 0;
AtomicInteger currentColumn = new AtomicInteger();
for (TextChange softWrap : softWraps) {
// There is a possible case that target column points inside soft wrap-introduced virtual space.
if (currentColumn.get() >= columnNumber) {
return startToUse;
}
if (offset >= state.getEndOffset()) {
state.advance();
fontType = state.getMergedAttributes().getFontType();
int result = calcSoftWrapUnawareOffset(editor, text, startToUse, softWrap.getEnd(), columnNumber, tabSize, x, currentColumn);
if (result >= 0) {
return result;
}
char c = offset < end ? text.charAt(offset++) : ' ';
int prevX = x;
if (c == '\t') {
x = nextTabStop(x, editorImpl);
}
else {
x += charWidth(c, fontType, editorImpl);
}
column += columnsNumber(c, x, prevX, spaceSize);
startToUse = softWrap.getStart();
x = softWrapModel.getSoftWrapIndentWidthInPixels(softWrap);
}
//if (column == columnNumber && offset < end && text.charAt(offset) == '\t' && (nextTabStop(x, editorImpl) - x) / spaceSize == 0) {
// offset++;
//}
if (column > columnNumber) offset--;
return offset;
// There is a possible case that target column points inside soft wrap-introduced virtual space.
if (currentColumn.get() >= columnNumber) {
return startToUse;
}
int result = calcSoftWrapUnawareOffset(editor, text, startToUse, end, columnNumber, tabSize, x, currentColumn);
if (result >= 0) {
return result;
}
// We assume that given column points to the virtual space after the line end if control flow reaches this place,
// hence, just return end of line offset then.
return end;
}
private static int calcSoftWrapUnawareOffset(CharSequence text, int start, int end, int columnNumber, int tabSize) {
/**
* Tries to match given logical column to the document offset assuming that it's located at <code>[start; end)</code> region.
*
* @param editor editor that is used to represent target document
* @param text target document text
* @param start start offset to check (inclusive)
* @param end end offset to check (exclusive)
* @param columnNumber target logical column number
* @param tabSize user-defined desired number of columns to use for tabulation symbol representation
* @param x <code>'x'</code> coordinate that corresponds to the given <code>'start'</code> offset
* @param currentColumn logical column that corresponds to the given <code>'start'</code> offset
* @return target offset that belongs to the <code>[start; end)</code> range and points to the target logical
* column if any; <code>-1</code> otherwise
*/
private static int calcSoftWrapUnawareOffset(Editor editor, CharSequence text, int start, int end, int columnNumber, int tabSize, int x,
AtomicInteger currentColumn)
{
// The main problem in a calculation is that target text may contain tabulation symbols and every such symbol may take different
// number of logical columns to represent. E.g. it takes two columns if tab size is four and current column is two; three columns
// if tab size is four and current column is one etc. So, first of all we check if there are tabulation symbols at the target
// text fragment.
boolean useOptimization = true;
boolean hasNonTabs = false;
boolean hasTabs = false;
for (int i = start; i < end; i++) {
if (text.charAt(i) == '\t') {
hasTabs = true;
if (hasNonTabs) {
useOptimization = false;
break;
}
} else {
@@ -233,20 +249,79 @@ public class EditorUtil {
}
}
if (!hasTabs) return Math.min(start + columnNumber, end);
// Perform optimized processing if possible. 'Optimized' here means the processing when we exactly know how many logical
// columns are occupied by tabulation symbols.
if (editor == null || useOptimization) {
if (!hasTabs) {
int result = start + columnNumber - currentColumn.get();
if (result < end) {
return result;
}
else {
currentColumn.addAndGet(end - start);
return -1;
}
}
int shift = 0;
int offset = start;
for (; offset < end && offset + shift < start + columnNumber; offset++) {
if (text.charAt(offset) == '\t') {
shift += getTabLength(offset + shift - start, tabSize) - 1;
// This variable holds number of 'virtual' tab-introduced columns, e.g. there is a possible case that particular tab owns
// three columns, hence, it increases 'shift' by two (3 - 1).
int shift = 0;
int offset = start;
int prevX = x;
for (; offset < end && offset + shift + currentColumn.get() < start + columnNumber; offset++) {
if (text.charAt(offset) == '\t') {
int nextX = nextTabStop(prevX, editor, tabSize);
shift += columnsNumber(nextX - prevX, getSpaceWidth(Font.PLAIN, editor)) - 1;
prevX = nextX;
}
}
int diff = start + columnNumber - offset - shift - currentColumn.get();
if (diff < 0) {
return offset - 1;
}
else if (diff == 0) {
return offset;
}
else {
currentColumn.addAndGet(offset - start + shift);
return -1;
}
}
if (offset + shift > start + columnNumber) {
offset--;
// It means that there are tabulation symbols that can't be explicitly mapped to the occupied logical columns number,
// hence, we need to perform special calculations to get know that.
EditorEx editorImpl = (EditorEx)editor;
int offset = start;
IterationState state = new IterationState(editorImpl, offset, false);
int fontType = state.getMergedAttributes().getFontType();
int column = currentColumn.get();
int spaceSize = getSpaceWidth(fontType, editorImpl);
for (; column < columnNumber && offset < end; offset++) {
if (offset >= state.getEndOffset()) {
state.advance();
fontType = state.getMergedAttributes().getFontType();
}
char c = text.charAt(offset);
if (c == '\t') {
int prevX = x;
x = nextTabStop(x, editorImpl);
column += columnsNumber(x - prevX, spaceSize);
}
else {
x += charWidth(c, fontType, editorImpl);
column++;
}
}
return offset;
if (column == columnNumber) {
return offset;
}
if (column > columnNumber && text.charAt(offset) == '\t') {
return offset - 1;
}
currentColumn.set(column);
return -1;
}
private static int getTabLength(int colNumber, int tabSize) {
@@ -328,7 +403,13 @@ public class EditorUtil {
if (tabSize <= 0) {
tabSize = 1;
}
return nextTabStop(x, editor, tabSize);
}
public static int nextTabStop(int x, Editor editor, int tabSize) {
if (tabSize <= 0) {
return x + getSpaceWidth(Font.PLAIN, editor);
}
tabSize *= getSpaceWidth(Font.PLAIN, editor);
int nTabs = x / tabSize;
@@ -371,6 +452,21 @@ public class EditorUtil {
return result;
}
/**
* Allows to answer how many visual columns are occupied by the given width.
*
* @param width target width
* @param spaceSize width of the single space symbol within the target editor
* @return number of visual columns are occupied by the given width
*/
public static int columnsNumber(int width, int spaceSize) {
int result = width / spaceSize;
if (width % spaceSize > 0) {
result++;
}
return result;
}
/**
* Allows to answer what width in pixels is required to draw fragment of the given char array from <code>[start; end)</code> interval
* at the given editor.
@@ -390,10 +486,10 @@ public class EditorUtil {
* from <code>[1; tab size]</code> (check {@link #nextTabStop(int, Editor)} for more details)
* @return width in pixels required for target text representation
*/
public static int textWidth(@NotNull Editor editor, char[] text, int start, int end, int fontType, int x) {
public static int textWidth(@NotNull Editor editor, CharSequence text, int start, int end, int fontType, int x) {
int result = 0;
for (int i = start; i < end; i++) {
char c = text[i];
char c = text.charAt(i);
if (c != '\t') {
FontInfo font = fontForChar(c, fontType, editor);
result += font.charWidth(c, editor.getContentComponent());
@@ -470,24 +470,27 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
private VerticalInfo createVerticalInfo(LogicalPosition position) {
Document document = myEditor.getDocument();
int line = position.line;
int logicalLine = position.line;
// There is a possible case that active logical line is represented on multiple lines due to soft wraps processing.
// We want to highlight those visual lines as 'active' then, so, we calculate 'y' position for the logical line start
// and height in accordance with the number of occupied visual lines.
int y = myEditor.logicalPositionToXY(myEditor.offsetToLogicalPosition(document.getLineStartOffset(line))).y;
LogicalPosition logicalPosition = myEditor.offsetToLogicalPosition(document.getLineStartOffset(logicalLine));
VisualPosition visualPosition = myEditor.logicalToVisualPosition(logicalPosition);
int y = myEditor.visualPositionToXY(visualPosition).y;
int height = myEditor.getLineHeight();
if (line < document.getLineCount() - 1) {
int nextLineY = myEditor.logicalPositionToXY(myEditor.offsetToLogicalPosition(document.getLineStartOffset(line + 1))).y;
int heightCandidate = nextLineY - y;
// There is a possible case that active line is the one that ends with folding, so, 'y' position
// of its next logical line is the same as the previous. We explicitly check that in order to use non-standard
// line height only in case of visible soft-wrapped line.
if (heightCandidate > height) {
height = heightCandidate;
int visualLine = visualPosition.line + 1;
int lastVisualLine = myEditor.offsetToVisualPosition(document.getTextLength() - 1).line;
for (; visualLine <= lastVisualLine; visualLine++) {
LogicalPosition logical = myEditor.visualToLogicalPosition(new VisualPosition(visualLine, 0));
if (logical.line == logicalLine) {
height += myEditor.getLineHeight();
}
else {
break;
}
}
return new VerticalInfo(y, height);
}
@@ -17,6 +17,9 @@ package com.intellij.openapi.editor.impl;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
/**
* @author Denis Zhdanov
@@ -31,17 +34,17 @@ public class DefaultEditorTextRepresentationHelper implements EditorTextRepresen
}
@Override
public int toVisualColumnSymbolsNumber(CharSequence text, int start, int end, int x) {
public int toVisualColumnSymbolsNumber(@NotNull CharSequence text, int start, int end, int x) {
return EditorUtil.textWidthInColumns(myEditor, text, start, end, x);
}
@Override
public int charWidth(char c, int x, int fontType) {
if (c == '\t') {
return EditorUtil.nextTabStop(x, myEditor) - x;
}
else {
return EditorUtil.charWidth(c, fontType, myEditor);
}
public int toVisualColumnSymbolsNumber(int width) {
return EditorUtil.columnsNumber(width, EditorUtil.getSpaceWidth(Font.PLAIN, myEditor));
}
@Override
public int textWidth(@NotNull CharSequence text, int start, int end, int x) {
return EditorUtil.textWidth(myEditor, text, start, end, Font.PLAIN, x);
}
}
@@ -105,6 +105,7 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public final class EditorImpl extends UserDataHolderBase implements EditorEx, HighlighterClient, Queryable {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.EditorImpl");
@@ -122,7 +123,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
ComplementaryFontsRegistry registry; // load costly font info
}
private final CommandProcessor myCommandProcessor;
private final CommandProcessor myCommandProcessor;
private final MyScrollBar myVerticalScrollBar;
private final CopyOnWriteArrayList<EditorMouseListener> myMouseListeners = ContainerUtil.createEmptyCOWList();
@@ -440,7 +441,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
@NotNull
public FoldingModel getFoldingModel() {
public FoldingModelEx getFoldingModel() {
return myFoldingModel;
}
@@ -902,7 +903,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
// Process 'after soft wrap' sign.
prevX = x;
charWidth = mySoftWrapModel.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP);
charWidth = mySoftWrapModel.getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP);
x += charWidth;
if (x >= px) {
break outer;
@@ -983,16 +984,15 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return offsetToLogicalPosition(offset, true);
}
@NotNull
@Override
public LogicalPosition offsetToLogicalPosition(int offset, boolean softWrapAware) {
if (softWrapAware) {
return mySoftWrapModel.offsetToLogicalPosition(offset);
}
int line = calcLogicalLineNumber(offset, false);
int column = calcColumnNumber(offset, line, false);
LogicalPosition position = new LogicalPosition(line, column);
if (softWrapAware) {
return mySoftWrapModel.adjustLogicalPosition(position, offset);
}
else {
return position;
}
return new LogicalPosition(line, column);
}
@NotNull
@@ -1055,7 +1055,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
if (i >= 0) {
start = i + 1;
}
return new Point(EditorUtil.textWidth(this, softWrapChars, start, column + 1, Font.PLAIN, 0), y);
return new Point(EditorUtil.textWidth(this, softWrap.getText(), start, column + 1, Font.PLAIN, 0), y);
}
break;
}
@@ -1749,7 +1749,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
if (backColor != null && !backColor.equals(defaultBackground) && clip.intersects(position.x, position.y, w, getLineHeight())) {
if (backColor.equals(myLastBackgroundColor) && myLastBackgroundPosition.y == position.y &&
myLastBackgroundPosition.x + myLastBackgroundWidth == position.x) {
myLastBackgroundPosition.x + myLastBackgroundWidth == position.x) {
myLastBackgroundWidth += w;
}
else {
@@ -1794,8 +1794,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
int visibleLineNumber = clip.y / lineHeight;
LogicalPosition logicalPosition = xyToLogicalPosition(new Point(0, clip.y));
int startLineNumber = logicalPosition.line;
// We use AtomicReference here just as a holder for LogicalPosition
// The main idea is that there is a possible case that we need to perform painting starting from soft-wrapped logical line.
// We may want to skip necessary of visual lines then. Hence, we remember logical position that corresponds to the starting
// visual line in order to use it for further processing. As soon as necessary number of visual lines is skipped, logical
// position is expected to be set to null as an indication that no soft wrap-introduced visual lines should be skipped on
// current painting iteration.
AtomicReference<LogicalPosition> logicalPosition = new AtomicReference<LogicalPosition>(xyToLogicalPosition(new Point(0, clip.y)));
int startLineNumber = logicalPosition.get().line;
Point position = new Point(0, visibleLineNumber * lineHeight);
if (startLineNumber == 0 && myPrefixText != null) {
@@ -1850,10 +1856,19 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
else {
FoldRegion collapsedFolderAt = iterationState.getCurrentFold();
if (collapsedFolderAt != null) {
TextChange softWrap = mySoftWrapModel.getSoftWrap(collapsedFolderAt.getStartOffset());
if (softWrap != null && logicalPosition.get() != null) {
position.x = drawStringWithSoftWraps(
g, chars, collapsedFolderAt.getStartOffset(), collapsedFolderAt.getStartOffset(), position, clip, effectColor, effectType,
fontType, currentColor, logicalPosition
);
}
int foldingXStart = position.x;
position.x =
drawStringWithSoftWraps(g, collapsedFolderAt.getPlaceholderText(), position, clip, effectColor, effectType,
fontType, currentColor, logicalPosition);
position.x = drawString(
g, collapsedFolderAt.getPlaceholderText(), position, clip, effectColor, effectType, fontType, currentColor
);
//drawStringWithSoftWraps(g, collapsedFolderAt.getPlaceholderText(), position, clip, effectColor, effectType,
// fontType, currentColor, logicalPosition);
BorderEffect.paintFoldedEffect(g, foldingXStart, position.y, position.x, getLineHeight(), effectColor, effectType);
}
@@ -1963,7 +1978,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
final int lastCount = count - 1;
final Color lastColor = color[lastCount];
if (_data == myLastData && _start == ends[lastCount] && (_color == null || lastColor == null || _color == lastColor)
&& _y == y[lastCount] /* there is a possible case that vertical position is adjusted because of soft wrap */)
&& _y == y[lastCount] /* there is a possible case that vertical position is adjusted because of soft wrap */)
{
ends[lastCount] = _end;
if (lastColor == null) color[lastCount] = _color;
@@ -1996,7 +2011,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private void paintCaretCursor(Graphics g) {
// There is a possible case that visual caret position is changed because of newly added or removed soft wraps.
// We check if that's the case and ask caret model to recalculate visual position if necessary.
myCaretCursor.paint(g);
}
@@ -2046,7 +2061,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
EffectType effectType,
int fontType,
Color fontColor,
LogicalPosition startDrawingLogicalPosition)
AtomicReference<LogicalPosition> startDrawingLogicalPosition)
{
return drawStringWithSoftWraps(g, text.toCharArray(), 0, text.length(), position, clip, effectColor, effectType,
fontType, fontColor, startDrawingLogicalPosition);
@@ -2062,16 +2077,19 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
EffectType effectType,
int fontType,
Color fontColor,
LogicalPosition startDrawingLogicalPosition)
AtomicReference<LogicalPosition> startDrawingLogicalPosition)
{
int startToUse = start;
// There is a possible case that starting logical line is split by soft-wraps and it's part after the split should be drawn.
// We need to skip necessary number of visual lines then.
int softWrapLinesToSkip = startDrawingLogicalPosition.softWrapLinesOnCurrentLogicalLine;
int softWrapLinesToSkip = 0;
if (startDrawingLogicalPosition.get() != null) {
softWrapLinesToSkip = startDrawingLogicalPosition.get().softWrapLinesOnCurrentLogicalLine;
}
TextChange lastSkippedSoftWrap = null;
if (softWrapLinesToSkip > 0) {
List<? extends TextChange> softWraps = getSoftWrapModel().getSoftWrapsForLine(startDrawingLogicalPosition.line);
List<? extends TextChange> softWraps = getSoftWrapModel().getSoftWrapsForLine(startDrawingLogicalPosition.get().line);
for (TextChange softWrap : softWraps) {
softWrapLinesToSkip -= StringUtil.countNewLines(softWrap.getText());
if (softWrapLinesToSkip <= 0) {
@@ -2080,13 +2098,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
break;
}
}
}
}
startToUse = Math.max(startToUse, start);
if (startToUse >= end) {
return position.x;
}
startDrawingLogicalPosition.set(null);
outer:
for (TextChange softWrap : getSoftWrapModel().getSoftWrapsForRange(startToUse, end)) {
@@ -2667,10 +2686,16 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
@NotNull
public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visiblePos) {
assertReadAccess();
if (!myFoldingModel.isFoldingEnabled() && !mySoftWrapModel.isSoftWrappingEnabled()) {
return new LogicalPosition(visiblePos.line, visiblePos.column);
return visualToLogicalPosition(visiblePos, true);
}
@NotNull
public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visiblePos, boolean softWrapAware) {
if (softWrapAware) {
return mySoftWrapModel.visualToLogicalPosition(visiblePos);
}
assertReadAccess();
if (!myFoldingModel.isFoldingEnabled()) return new LogicalPosition(visiblePos.line, visiblePos.column);
int line = visiblePos.line;
int column = visiblePos.column;
@@ -2678,35 +2703,23 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
FoldRegion lastCollapsedBefore = getLastCollapsedBeforePosition(visiblePos);
if (lastCollapsedBefore != null) {
LogicalPosition softWrapAwareLogFoldEnd = offsetToLogicalPosition(lastCollapsedBefore.getEndOffset());
VisualPosition softWrapAwareVisFoldEnd = logicalToVisualPosition(softWrapAwareLogFoldEnd);
if (softWrapAwareVisFoldEnd.line == visiblePos.line) {
if (visiblePos.column == softWrapAwareVisFoldEnd.column) {
return softWrapAwareLogFoldEnd;
}
else if (visiblePos.column > softWrapAwareVisFoldEnd.column) {
int columnToUse = softWrapAwareLogFoldEnd.column + visiblePos.column - softWrapAwareVisFoldEnd.column;
return new LogicalPosition(
softWrapAwareLogFoldEnd.line, columnToUse, softWrapAwareLogFoldEnd.softWrapLinesBeforeCurrentLogicalLine,
softWrapAwareLogFoldEnd.softWrapLinesOnCurrentLogicalLine, visiblePos.column - columnToUse - softWrapAwareLogFoldEnd.foldingColumnDiff,
softWrapAwareLogFoldEnd.foldedLines, softWrapAwareLogFoldEnd.foldingColumnDiff
);
LogicalPosition logFoldEnd = offsetToLogicalPosition(lastCollapsedBefore.getEndOffset(), false);
VisualPosition visFoldEnd = logicalToVisualPosition(logFoldEnd, false);
line = logFoldEnd.line + (visiblePos.line - visFoldEnd.line);
if (visFoldEnd.line == visiblePos.line) {
if (visiblePos.column >= visFoldEnd.column) {
column = logFoldEnd.column + (visiblePos.column - visFoldEnd.column);
}
else {
return offsetToLogicalPosition(lastCollapsedBefore.getStartOffset());
return offsetToLogicalPosition(lastCollapsedBefore.getStartOffset(), false);
}
}
LogicalPosition softWrapUnawareLogFoldEnd = offsetToLogicalPosition(lastCollapsedBefore.getEndOffset(), false);
VisualPosition softWrapUnawareVisFoldEnd = logicalToVisualPosition(softWrapUnawareLogFoldEnd, false);
line = softWrapUnawareLogFoldEnd.line + (visiblePos.line - softWrapUnawareVisFoldEnd.line);
}
if (column < 0) column = 0;
line = Math.min(line, myDocument.getLineCount() - 1);
LogicalPosition softWrapUnawareResult = new LogicalPosition(line, column);
return mySoftWrapModel.adjustLogicalPosition(softWrapUnawareResult, visiblePos);
return new LogicalPosition(line, column);
}
private int calcLogicalLineNumber(int offset) {
@@ -2746,7 +2759,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
int column = EditorUtil.calcColumnNumber(this, text, start, offset, EditorUtil.getTabSize(this));
if (softWrapAware) {
int line = calcLogicalLineNumber(offset, false);
int line = calcLogicalLineNumber(offset, false);
return mySoftWrapModel.adjustLogicalPosition(new LogicalPosition(line, column), offset).column;
}
else {
@@ -2886,22 +2899,22 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
int x = myGutterComponent.convertX(e.getX());
if (x >= myGutterComponent.getLineNumberAreaOffset() &&
x < myGutterComponent.getLineNumberAreaOffset() + myGutterComponent.getLineNumberAreaWidth()) {
x < myGutterComponent.getLineNumberAreaOffset() + myGutterComponent.getLineNumberAreaWidth()) {
return EditorMouseEventArea.LINE_NUMBERS_AREA;
}
if (x >= myGutterComponent.getAnnotationsAreaOffset() &&
x <= myGutterComponent.getAnnotationsAreaOffset() + myGutterComponent.getAnnotationsAreaWidth()) {
x <= myGutterComponent.getAnnotationsAreaOffset() + myGutterComponent.getAnnotationsAreaWidth()) {
return EditorMouseEventArea.ANNOTATIONS_AREA;
}
if (x >= myGutterComponent.getLineMarkerAreaOffset() &&
x < myGutterComponent.getLineMarkerAreaOffset() + myGutterComponent.getLineMarkerAreaWidth()) {
x < myGutterComponent.getLineMarkerAreaOffset() + myGutterComponent.getLineMarkerAreaWidth()) {
return EditorMouseEventArea.LINE_MARKERS_AREA;
}
if (x >= myGutterComponent.getFoldingAreaOffset() &&
x < myGutterComponent.getFoldingAreaOffset() + myGutterComponent.getFoldingAreaWidth()) {
x < myGutterComponent.getFoldingAreaOffset() + myGutterComponent.getFoldingAreaWidth()) {
return EditorMouseEventArea.FOLDING_OUTLINE_AREA;
}
@@ -2993,7 +3006,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
int caretShift = newCaretOffset - mySavedSelectionStart;
if (myMousePressedEvent != null && getMouseEventArea(myMousePressedEvent) != EditorMouseEventArea.EDITING_AREA &&
getMouseEventArea(myMousePressedEvent) != EditorMouseEventArea.LINE_NUMBERS_AREA) {
getMouseEventArea(myMousePressedEvent) != EditorMouseEventArea.LINE_NUMBERS_AREA) {
selectionModel.setSelection(oldSelectionStart, newCaretOffset);
}
else {
@@ -4025,8 +4038,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
public void mouseReleased(MouseEvent e) {
runMouseReleasedCommand(e);
if (!e.isConsumed() && myMousePressedEvent != null && !myMousePressedEvent.isConsumed() &&
Math.abs(e.getX() - myMousePressedEvent.getX()) < EditorUtil.getSpaceWidth(Font.PLAIN, EditorImpl.this) &&
Math.abs(e.getY() - myMousePressedEvent.getY()) < getLineHeight()) {
Math.abs(e.getX() - myMousePressedEvent.getX()) < EditorUtil.getSpaceWidth(Font.PLAIN, EditorImpl.this) &&
Math.abs(e.getY() - myMousePressedEvent.getY()) < getLineHeight()) {
runMouseClickedCommand(e);
}
myMousePressedEvent = null;
@@ -4182,7 +4195,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
myMouseSelectedRegion = myFoldingModel.getFoldingPlaceholderAt(new Point(x, y));
myMousePressedInsideSelection = mySelectionModel.hasSelection() && caretOffset >= mySelectionModel.getSelectionStart() &&
caretOffset <= mySelectionModel.getSelectionEnd();
caretOffset <= mySelectionModel.getSelectionEnd();
if (!myMousePressedInsideSelection && mySelectionModel.hasBlockSelection()) {
int[] starts = mySelectionModel.getBlockSelectionStarts();
@@ -4568,7 +4581,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
final Editor editor = getEditor(source);
if (action == MOVE && !editor.isViewer()) {
if (!FileDocumentManager.getInstance().requestWriting(editor.getDocument(), editor.getProject())) {
return;
return;
}
CommandProcessor.getInstance().executeCommand(((EditorImpl)editor).myProject, new Runnable() {
public void run() {
@@ -4641,7 +4654,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
myMaxWidth = mySize != null ? mySize.width : -1;
}
myOldEndLine = getVisualPositionLine(e.getOffset() + e.getOldLength());
myOldEndLine = offsetToLogicalPosition(e.getOffset() + e.getOldLength()).line;
}
private int getVisualPositionLine(int offset) {
@@ -4672,15 +4685,15 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
myLineWidths.insert(oldEndLine + 1, delta);
}
else if (oldEndLine > newEndLine && !toAddNewLines && newEndLine + 1 < lineWidthSize) {
myLineWidths.remove(newEndLine + 1, Math.min(oldEndLine, lineWidthSize) - newEndLine);
myLineWidths.remove(newEndLine + 1, Math.min(oldEndLine, lineWidthSize) - newEndLine - 1);
}
myIsDirty = true;
}
}
public synchronized void changedUpdate(DocumentEvent e) {
int startLine = e.getOldLength() == 0 ? myOldEndLine : getVisualPositionLine(e.getOffset());
int newEndLine = e.getNewLength() == 0 ? startLine : getVisualPositionLine(e.getOffset() + e.getNewLength());
int startLine = e.getOldLength() == 0 ? myOldEndLine : offsetToLogicalPosition(e.getOffset()).line;
int newEndLine = e.getNewLength() == 0 ? startLine : offsetToLogicalPosition(e.getOffset() + e.getNewLength()).line;
int oldEndLine = myOldEndLine;
update(startLine, newEndLine, oldEndLine);
@@ -4844,7 +4857,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
TextChange softWrap = getSoftWrapModel().getSoftWrap(i);
if (softWrap != null) {
column++; // For 'after soft wrap' drawing.
x = getSoftWrapModel().getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP);
x = getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP);
}
char c = text.charAt(i);
@@ -4882,7 +4895,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
: e.isControlDown() && !e.isMetaDown() && !e.isAltDown() && !e.isShiftDown();
if (changeFontSize) {
setFontSize(myScheme.getEditorFontSize() + e.getWheelRotation());
return;
return;
}
}
@@ -15,6 +15,8 @@
*/
package com.intellij.openapi.editor.impl;
import org.jetbrains.annotations.NotNull;
/**
* Strategy interface for various utility methods used for representing document text at the editor.
* <p/>
@@ -37,16 +39,28 @@ public interface EditorTextRepresentationHelper {
* @param x <code>'x'</code> offset from the visual line start
* @return number of visual columns necessary for the target text sub-sequence representation
*/
int toVisualColumnSymbolsNumber(CharSequence text, int start, int end, int x);
int toVisualColumnSymbolsNumber(@NotNull CharSequence text, int start, int end, int x);
/**
* Allows to retrieve width (in pixels) necessary to represent given symbol at the given <code>'x'</code> offset from
* visual line start using given font type.
* Allows to answer how many visual columns is necessary for representing text of the given width.
*
* @param c target symbol which width should be calculated
* @param x current <code>'x'</code> of the visual line start to use for the target symbol representation
* @param fontType font type to use for representing given symbol
* @return number of pixels necessary for the given symbol representation
* @param width target width
* @return number of visual columns necessary for representation of the text with the given width
*/
int charWidth(char c, int x, int fontType);
int toVisualColumnSymbolsNumber(int width);
/**
* Allows to retrieve width (in pixels) necessary to represent given region (<code>[start; end)</code>) starting
* at the given <code>'x'</code> offset from visual line start using given font type.
* <p/>
* <b>Note:</b> target region is allows to contain line feeds, the width is calculated as a difference between <code>'x'</code>
* coordinates of the last and first symbols.
*
* @param text target text holder
* @param start start offset of the target text sub-sequence (inclusive)
* @param end end offset of the target text sub-sequence (exclusive)
* @param x <code>'x'</code> offset from the visual line start
* @return width in pixels necessary for the target text sub-sequence representation
*/
int textWidth(@NotNull CharSequence text, int start, int end, int x);
}
@@ -43,7 +43,7 @@ import java.util.List;
*/
public class SoftWrapModelImpl implements SoftWrapModelEx {
private final SoftWrapDataMapper myDataAdjuster;
private final SoftWrapDataMapper myDataMapper;
private final SoftWrapsStorage myStorage;
private final SoftWrapPainter myPainter;
private final SoftWrapApplianceManager myApplianceManager;
@@ -60,20 +60,20 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
public SoftWrapModelImpl(@NotNull final EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull SoftWrapPainter painter) {
this(
editor, storage, painter, new DefaultSoftWrapApplianceManager(storage, editor, painter),
new SoftWrapDataMapper(editor, storage, painter, new DefaultEditorTextRepresentationHelper(editor)),
new SoftWrapDataMapper(editor, storage, new DefaultEditorTextRepresentationHelper(editor)),
new SoftWrapDocumentChangeManager(editor, storage)
);
}
public SoftWrapModelImpl(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull SoftWrapPainter painter,
@NotNull SoftWrapApplianceManager applianceManager, @NotNull SoftWrapDataMapper dataAdjuster,
@NotNull SoftWrapApplianceManager applianceManager, @NotNull SoftWrapDataMapper dataMapper,
@NotNull SoftWrapDocumentChangeManager documentChangeManager)
{
myEditor = editor;
myStorage = storage;
myPainter = painter;
myApplianceManager = applianceManager;
myDataAdjuster = dataAdjuster;
myDataMapper = dataMapper;
myDocumentChangeManager = documentChangeManager;
}
@@ -184,25 +184,39 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
}
@Override
public int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType) {
public int getMinDrawingWidthInPixels(@NotNull SoftWrapDrawingType drawingType) {
return myPainter.getMinDrawingWidth(drawingType);
}
@Override
public int getMinDrawingWidthInColumns(@NotNull SoftWrapDrawingType drawingType) {
return myPainter.getMinDrawingWidth(drawingType) > 0 ? 1 : 0;
}
@NotNull
public LogicalPosition adjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual) {
@Override
public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visual) {
if (myActive > 0 || !isSoftWrappingEnabled() || myStorage.isEmpty() || myEditor.getDocument().getTextLength() <= 0) {
return defaultLogical;
return myEditor.visualToLogicalPosition(visual, false);
}
if (defaultLogical.visualPositionAware) {
return defaultLogical;
}
myActive++;
try {
return myDataAdjuster.adjustLogicalPosition(defaultLogical, visual);
return myDataMapper.visualToLogical(visual);
} finally {
myActive--;
}
finally {
}
@NotNull
@Override
public LogicalPosition offsetToLogicalPosition(int offset) {
if (myActive > 0 || !isSoftWrappingEnabled() || myStorage.isEmpty() || myEditor.getDocument().getTextLength() <= 0) {
return myEditor.offsetToLogicalPosition(offset, false);
}
myActive++;
try {
return myDataMapper.offsetToLogicalPosition(offset);
} finally {
myActive--;
}
}
@@ -215,7 +229,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
myActive++;
try {
return myDataAdjuster.offsetToLogicalPosition(offset);
return myDataMapper.offsetToLogicalPosition(offset);
} finally {
myActive--;
}
@@ -229,7 +243,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
myActive++;
try {
return myDataAdjuster.adjustVisualPosition(logical, defaultVisual);
return myDataMapper.adjustVisualPosition(logical, defaultVisual);
}
finally {
myActive--;
@@ -292,7 +306,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
}
if (start < end) {
result += EditorUtil.textWidth(myEditor, chars, start, end, Font.PLAIN, 0);
result += EditorUtil.textWidth(myEditor, softWrap.getText(), start, end, Font.PLAIN, 0);
}
return result;
@@ -29,6 +29,8 @@ import gnu.trove.TIntArrayList;
import gnu.trove.TIntHashSet;
import org.jetbrains.annotations.NotNull;
import java.nio.CharBuffer;
/**
* Default {@link SoftWrapApplianceManager} implementation that is built with the following design guide lines:
* <pre>
@@ -214,7 +216,7 @@ public class DefaultSoftWrapApplianceManager implements SoftWrapApplianceManager
int x = myPainter.getMinDrawingWidth(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
int prevSoftWrapOffset = start;
for (int i = start; i < end; i++) {
int symbolWidth = EditorUtil.textWidth(myEditor, text, i, i + 1, fontType, x);
int symbolWidth = EditorUtil.textWidth(myEditor, CharBuffer.wrap(text), i, i + 1, fontType, x);
if (x + symbolWidth >= myVisibleAreaWidth) {
int offset = calculateSoftWrapOffset(text, i - 1, prevSoftWrapOffset, end);
if (offset >= end || offset <= prevSoftWrapOffset) {
@@ -239,7 +239,7 @@ public class IdeRootPane extends JRootPane implements UISettingsListener {
return c.getClass().getSimpleName();
}
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return null;
}
@@ -64,7 +64,7 @@ public class EncodingPanel implements StatusBarWidget, StatusBarWidget.MultipleT
return "Encoding";
}
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return this;
}
@@ -16,6 +16,7 @@
package com.intellij.openapi.wm.impl.status;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.TaskInfo;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.BalloonHandler;
@@ -42,15 +43,15 @@ import javax.swing.event.HyperlinkListener;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Random;
/**
* User: spLeaner
*/
public class IdeStatusBarImpl extends JComponent implements StatusBarEx {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.status.IdeStatusBarImpl");
private InfoAndProgressPanel myInfoAndProgressPanel;
private enum Position {LEFT, RIGHT, CENTER}
@@ -93,7 +94,7 @@ public class IdeStatusBarImpl extends JComponent implements StatusBarEx {
}
public void addWidget(@NotNull final StatusBarWidget widget) {
addWidget(widget, Position.RIGHT, "before Notifications");
addWidget(widget, Position.RIGHT, "__AUTODETECT__");
}
public void addWidget(@NotNull final StatusBarWidget widget, @NotNull String anchor) {
@@ -139,7 +140,7 @@ public class IdeStatusBarImpl extends JComponent implements StatusBarEx {
}
@Nullable
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return null;
}
@@ -158,19 +159,23 @@ public class IdeStatusBarImpl extends JComponent implements StatusBarEx {
}
public void removeCustomIndicationComponent(@NotNull final JComponent c) {
for (final String key : myWidgetMap.keySet()) {
final WidgetBean bean = myWidgetMap.get(key);
if (bean.component instanceof CustomStatusBarWidget && ((CustomStatusBarWidget)bean.component).getComponent() == c) {
final Set<String> keySet = myWidgetMap.keySet();
final String[] keys = keySet.toArray(new String[keySet.size()]);
for (final String key : keys) {
final WidgetBean value = myWidgetMap.get(key);
if (value.widget instanceof CustomStatusBarWidget && value.component == c) {
removeWidget(key);
myCustomComponentIds.remove(key);
}
}
}
public void dispose() {
for (final String key : myWidgetMap.keySet()) {
final WidgetBean bean = myWidgetMap.get(key);
for (final WidgetBean bean : myWidgetMap.values()) {
Disposer.dispose(bean.widget);
}
myWidgetMap.clear();
}
private void addWidget(@NotNull final StatusBarWidget widget, @NotNull final Position pos, @NotNull String anchor) {
@@ -207,20 +212,29 @@ public class IdeStatusBarImpl extends JComponent implements StatusBarEx {
final JComponent c = widget instanceof CustomStatusBarWidget ? ((CustomStatusBarWidget)widget).getComponent() : wrap(widget);
if (Position.RIGHT == pos && panel.getComponentCount() > 0) {
final List<String> parts = StringUtil.split(anchor, " ");
if (parts.size() < 2) {
throw new IllegalArgumentException(
"anchor should be a relative position ('before' or 'after') and widget ID, like 'after Encoding'");
String wid;
boolean before;
if (!anchor.equals("__AUTODETECT__")) {
final List<String> parts = StringUtil.split(anchor, " ");
if (parts.size() < 2 || !myWidgetMap.keySet().contains(parts.get(1))) {
wid = "Notifications";
before = true;
} else {
wid = parts.get(1);
before = "before".equalsIgnoreCase(parts.get(0));
}
} else {
wid = "Notifications";
before = true;
}
for (final String id : myWidgetMap.keySet()) {
if (id.equalsIgnoreCase(parts.get(1))) {
if (id.equalsIgnoreCase(wid)) {
final WidgetBean bean = myWidgetMap.get(id);
int i = 0;
for (final Component component : myRightPanel.getComponents()) {
if (component == bean.component) {
final String _relative = parts.get(0);
if ("before".equalsIgnoreCase(_relative)) {
if (before) {
panel.add(c, i);
updateBorder(i);
}
@@ -238,8 +252,6 @@ public class IdeStatusBarImpl extends JComponent implements StatusBarEx {
}
}
}
throw new IllegalArgumentException("unable to find widget with id: " + parts.get(1));
}
if (Position.LEFT == pos && panel.getComponentCount() == 0) {
@@ -323,7 +335,7 @@ public class IdeStatusBarImpl extends JComponent implements StatusBarEx {
private static JComponent wrap(@NotNull final StatusBarWidget widget) {
final StatusBarWidget.WidgetPresentation presentation =
widget.getPresentation(SystemInfo.isMac ? StatusBarWidget.Type.MAC : StatusBarWidget.Type.DEFAULT);
widget.getPresentation(SystemInfo.isMac ? StatusBarWidget.PlatformType.MAC : StatusBarWidget.PlatformType.DEFAULT);
assert presentation != null : "Presentation should not be null!";
JComponent wrapper;
@@ -18,15 +18,12 @@ package com.intellij.openapi.wm.impl.status;
import com.intellij.idea.ActionsBundle;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.progress.TaskInfo;
import com.intellij.openapi.progress.impl.ProgressManagerImpl;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.BalloonHandler;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.MultiValuesMap;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.wm.CustomStatusBarWidget;
@@ -52,9 +49,7 @@ import javax.swing.event.HyperlinkListener;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
public class InfoAndProgressPanel extends JPanel implements CustomStatusBarWidget {
private final ProcessPopup myPopup;
@@ -142,7 +137,7 @@ public class InfoAndProgressPanel extends JPanel implements CustomStatusBarWidge
return "InfoAndProgress";
}
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return null;
}
@@ -42,7 +42,7 @@ public class InsertOverwritePanel implements StatusBarWidget, StatusBarWidget.Te
return "InsertOverwrite";
}
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return this;
}
@@ -70,7 +70,7 @@ public class MemoryUsagePanel extends JButton implements CustomStatusBarWidget {
}
@Nullable
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return null;
}
@@ -61,7 +61,7 @@ public class PositionPanel implements StatusBarWidget, StatusBarWidget.TextPrese
return "Position";
}
public WidgetPresentation getPresentation(@NotNull final Type type) {
public WidgetPresentation getPresentation(@NotNull final PlatformType type) {
return this;
}
@@ -30,16 +30,16 @@ public class TextPanel extends JComponent {
private boolean myDecorate = true;
private float myAlignment;
TextPanel() {
protected TextPanel() {
this(null);
}
TextPanel(final boolean decorate) {
protected TextPanel(final boolean decorate) {
this(null);
myDecorate = decorate;
}
TextPanel(@Nullable final String maxPossibleString) {
protected TextPanel(@Nullable final String maxPossibleString) {
myMaxPossibleString = maxPossibleString;
setFont(SystemInfo.isMac ? UIUtil.getLabelFont().deriveFont(11.0f) : UIUtil.getLabelFont());
@@ -93,7 +93,7 @@ public class TextPanel extends JComponent {
final int y = UIUtil.getStringY(s, bounds, g2);
if (SystemInfo.isMac && myDecorate) {
g2.setColor(new Color(215, 215, 215));
g2.drawString(s, x, y+1);
g2.drawString(s, x, y + 1);
}
g2.setColor(getForeground());
@@ -105,7 +105,7 @@ public class TextPanel extends JComponent {
myAlignment = alignment;
}
private static String splitText(final JLabel label, final String text, final int widthLimit){
private static String splitText(final JLabel label, final String text, final int widthLimit) {
final FontMetrics fontMetrics = label.getFontMetrics(label.getFont());
final String[] lines = UIUtil.splitText(text, fontMetrics, widthLimit, ' ');
@@ -130,9 +130,10 @@ public class TextPanel extends JComponent {
return myText;
}
public Dimension getPreferredSize(){
public Dimension getPreferredSize() {
int max = 0;
if (myMaxPossibleString != null) max = getFontMetrics(getFont()).stringWidth(myMaxPossibleString);
String text = getTextForPreferredSize();
if (text != null) max = getFontMetrics(getFont()).stringWidth(text);
if (myPrefSize != null) {
return new Dimension(20 + max, myPrefSize.height);
@@ -140,4 +141,11 @@ public class TextPanel extends JComponent {
return new Dimension(20 + max, getMinimumSize().height);
}
/**
* @return the text that is used to calculate the preferred size
*/
protected String getTextForPreferredSize() {
return myMaxPossibleString;
}
}
@@ -54,7 +54,7 @@ public class ToggleReadOnlyAttributePanel implements StatusBarWidget, StatusBarW
return "ReadOnlyAttribute";
}
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return this;
}
@@ -3,10 +3,13 @@ package com.intellij.openapi.editor.impl.softwrap;
import com.intellij.mock.MockFoldRegion;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.FoldingModelEx;
import com.intellij.openapi.editor.ex.SoftWrapModelEx;
import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import gnu.trove.TIntHashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jmock.Expectations;
import org.jmock.Mockery;
@@ -18,7 +21,6 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -88,7 +90,8 @@ public class SoftWrapDataMapperTest {
private EditorEx myEditor;
private Document myDocument;
private SoftWrapsStorage myStorage;
private FoldingModel myFoldingModel;
private SoftWrapModelEx mySoftWrapModel;
private FoldingModelEx myFoldingModel;
private MockEditorTextRepresentationHelper myRepresentationHelper;
@Before
@@ -100,7 +103,8 @@ public class SoftWrapDataMapperTest {
myEditor = myMockery.mock(EditorEx.class);
myDocument = myMockery.mock(Document.class);
myStorage = new SoftWrapsStorage();
myFoldingModel = myMockery.mock(FoldingModel.class);
mySoftWrapModel = myMockery.mock(SoftWrapModelEx.class);
myFoldingModel = myMockery.mock(FoldingModelEx.class);
final EditorSettings settings = myMockery.mock(EditorSettings.class);
final Project project = myMockery.mock(Project.class);
final SoftWrapPainter painter = myMockery.mock(SoftWrapPainter.class);
@@ -140,6 +144,23 @@ public class SoftWrapDataMapperTest {
allowing(settings).isWhitespacesShown();will(returnValue(true));
allowing(myEditor).getProject();will(returnValue(project));
// Soft wraps.
allowing(myEditor).getSoftWrapModel(); will(returnValue(mySoftWrapModel));
allowing(mySoftWrapModel).getSoftWrapIndentWidthInColumns(with(any(TextChange.class)));
will(new CustomAction("getSoftWrapIndentWidthInColumns") {
@Override
public Object invoke(Invocation invocation) throws Throwable {
return getSoftWrapIndentWidthInColumns((TextChange)invocation.getParameter(0));
}
});
allowing(mySoftWrapModel).getSoftWrapIndentWidthInPixels(with(any(TextChange.class)));
will(new CustomAction("getSoftWrapIndentWidthInPixels") {
@Override
public Object invoke(Invocation invocation) throws Throwable {
return getSoftWrapIndentWidthInPixels((TextChange)invocation.getParameter(0));
}
});
// Folding.
allowing(myEditor).getFoldingModel();will(returnValue(myFoldingModel));
allowing(myFoldingModel).isOffsetCollapsed(with(any(int.class))); will(new CustomAction("isOffsetCollapsed()") {
@@ -155,7 +176,7 @@ public class SoftWrapDataMapperTest {
return getCollapsedFoldRegion((Integer)invocation.getParameter(0));
}
});
allowing(myFoldingModel).getAllFoldRegions(); will(new CustomAction("getAllFoldRegions()") {
allowing(myFoldingModel).fetchTopLevel(); will(new CustomAction("fetchTopLevel()") {
@Override
public Object invoke(Invocation invocation) throws Throwable {
return myFoldRegions.toArray(new FoldRegion[myFoldRegions.size()]);
@@ -188,7 +209,7 @@ public class SoftWrapDataMapperTest {
myRepresentationHelper = new MockEditorTextRepresentationHelper();
myAdjuster = new SoftWrapDataMapper(myEditor, myStorage, painter, myRepresentationHelper, new MockFontTypeProvider());
myAdjuster = new SoftWrapDataMapper(myEditor, myStorage, myRepresentationHelper/*, new MockFontTypeProvider()*/);
}
@After
@@ -196,6 +217,16 @@ public class SoftWrapDataMapperTest {
myMockery.assertIsSatisfied();
}
@Test
public void noSoftWrapsAndFolding() {
String document =
"class Test { \n" +
" public void foo() {} \n" +
" \n" +
"}";
test(document);
}
@Test
public void softWrapHasSymbolBeforeFirstLineFeed() {
String document =
@@ -216,13 +247,13 @@ public class SoftWrapDataMapperTest {
String document =
"public class Test {\n" +
" public void foo(int[] data) {\n" +
" bar(data[0], data[1], <WRAP>\n" +
" </WRAP>data[2], data[3], <WRAP> \n" +
" </WRAP>data[4], data[5], \n" +
" data[6], data[7], \n" +
" data[8], data[9], <WRAP>\n" +
" </WRAP>data[10], data[11], <WRAP> \n" +
" </WRAP>data[12], data[13]); \n" +
" bar(data[0], <WRAP>\n" +
" </WRAP>data[1] <WRAP> \n" +
" </WRAP>data[2] \n" +
" data[3], \n" +
" data[4], <WRAP>\n" +
" </WRAP>data[5] <WRAP> \n" +
" </WRAP>data[6]); \n" +
" }\n" +
" public void bar(int ... i) {\n" +
" }\n" +
@@ -345,6 +376,34 @@ public class SoftWrapDataMapperTest {
test(document);
}
private static int getSoftWrapIndentWidthInColumns(TextChange softWrap) {
int result = 0;
CharSequence text = softWrap.getText();
for (int i = text.length() - 1; i >= 0; i--) {
if (text.charAt(i) == '\n') {
break;
}
result++;
}
if (SOFT_WRAP_DRAWING_WIDTH > 0) {
result++;
}
return result;
}
private int getSoftWrapIndentWidthInPixels(TextChange softWrap) {
int result = 0;
CharSequence text = softWrap.getText();
for (int i = text.length() - 1; i >= 0; i--) {
if (text.charAt(i) == '\n') {
break;
}
result += myRepresentationHelper.textWidth(text, i, i + 1, 0);
}
result += SOFT_WRAP_DRAWING_WIDTH;
return result;
}
private int getLineNumber(int offset) {
int line = 0;
for (TextRange range : myLineRanges) {
@@ -430,10 +489,12 @@ public class SoftWrapDataMapperTest {
for (DataEntry data : myExpectedData) {
// Check logical by visual.
LogicalPosition actualLogicalByVisual = myAdjuster.adjustLogicalPosition(toSoftWrapUnawareLogicalByVisual(data), data.visual);
LogicalPosition actualLogicalByVisual = myAdjuster.visualToLogical(data.visual);
// We don't want to perform the check for logical positions that correspond to the folded space because all of them relate to
// the same logical position of the folding start.
if (!data.foldedSpace && !data.insideTab && !equals(data.logical, actualLogicalByVisual)) {
//TODO den remove
myAdjuster.visualToLogical(data.visual);
throw new AssertionError(
String.format("Detected unmatched logical position by visual (%s). Expected: '%s', actual: '%s'. Calculation was performed "
+ "against soft wrap-unaware logical: '%s'",
@@ -446,6 +507,8 @@ public class SoftWrapDataMapperTest {
// We don't to perform the check for the data that points to soft wrap location here. The reason is that it shares offset
// with the first document symbol after soft wrap, hence, examination always fails.
if (!data.virtualSpace && !data.insideTab && !equals(data.logical, actualLogicalByOffset)) {
//TODO den remove
myAdjuster.offsetToLogicalPosition(data.offset);
throw new AssertionError(
String.format("Detected unmatched logical position by offset. Expected: '%s', actual: '%s'. Calculation was performed "
+ "against offset: '%d' and soft wrap-unaware logical: '%s'",
@@ -635,7 +698,7 @@ public class SoftWrapDataMapperTest {
}
else if (c == '\t') {
int tabWidthInColumns = myRepresentationHelper.toVisualColumnSymbolsNumber(c, x);
x += myRepresentationHelper.charWidth(c, x, Font.PLAIN);
x += MockEditorTextRepresentationHelper.charWidth(c, x);
// There is a possible case that single tabulation symbols is shown in more than one visual column at IntelliJ editor.
// We store data entry only for the first tab column without 'inside tab' flag then.
@@ -652,7 +715,7 @@ public class SoftWrapDataMapperTest {
} else {
logicalColumn++;
offset++;
x += myRepresentationHelper.charWidth(c, x, Font.PLAIN);
x += MockEditorTextRepresentationHelper.charWidth(c, x);
foldingColumnDiff--;
}
return;
@@ -685,7 +748,7 @@ public class SoftWrapDataMapperTest {
visualColumn++;
softWrapColumnDiff++;
softWrapSymbolsOnCurrentVisualLine++;
x += myRepresentationHelper.charWidth(c, x, Font.PLAIN);
x += MockEditorTextRepresentationHelper.charWidth(c, x);
}
return;
}
@@ -706,7 +769,7 @@ public class SoftWrapDataMapperTest {
}
else if (c == '\t') {
int tabWidthInColumns = myRepresentationHelper.toVisualColumnSymbolsNumber(c, x);
x += myRepresentationHelper.charWidth(c, x, Font.PLAIN);
x += MockEditorTextRepresentationHelper.charWidth(c, x);
// There is a possible case that single tabulation symbols is shown in more than one visual column at IntelliJ editor.
// We store data entry only for the first tab column without 'inside tab' flag then.
@@ -726,7 +789,7 @@ public class SoftWrapDataMapperTest {
visualColumn++;
logicalColumn++;
offset++;
x += myRepresentationHelper.charWidth(c, x, Font.PLAIN);
x += MockEditorTextRepresentationHelper.charWidth(c, x);
}
}
@@ -782,10 +845,10 @@ public class SoftWrapDataMapperTest {
}
@Override
public int toVisualColumnSymbolsNumber(CharSequence text, int start, int end, int x) {
public int toVisualColumnSymbolsNumber(@NotNull CharSequence text, int start, int end, int x) {
int result = 0;
for (int i = start; i < end; i++) {
int width = charWidth(text.charAt(i), x, Font.PLAIN);
int width = charWidth(text.charAt(i), x);
result += width / SPACE_SIZE;
if (width % SPACE_SIZE > 0) {
result++;
@@ -796,7 +859,28 @@ public class SoftWrapDataMapperTest {
}
@Override
public int charWidth(char c, int x, int fontType) {
public int toVisualColumnSymbolsNumber(int width) {
int result = width / SPACE_SIZE;
if (width % SPACE_SIZE > 0) {
result++;
}
return result;
}
@Override
public int textWidth(@NotNull CharSequence text, int start, int end, int x) {
int result = 0;
for (int i = start; i < end; i++) {
char c = text.charAt(i);
switch (c) {
case '\n': result = 0; break;
default: result += charWidth(c, result);
}
}
return result;
}
public static int charWidth(char c, int x) {
if (c == '\t') {
int tabWidth = SPACE_SIZE * TAB_SIZE;
int tabsNumber = x / tabWidth;
@@ -808,18 +892,18 @@ public class SoftWrapDataMapperTest {
}
}
private static class MockFontTypeProvider implements SoftWrapDataMapper.FontTypeProvider {
@Override
public void init(int start) {
}
@Override
public int getFontType(int offset) {
return Font.PLAIN;
}
@Override
public void cleanup() {
}
}
//private static class MockFontTypeProvider implements SoftWrapDataMapper.FontTypeProvider {
// @Override
// public void init(int start) {
// }
//
// @Override
// public int getFontType(int offset) {
// return Font.PLAIN;
// }
//
// @Override
// public void cleanup() {
// }
//}
}
@@ -224,11 +224,11 @@ javadoc.external.fetch.error.message=Cannot fetch remote documentation: {0}
searching.for.implementations=Searching For Implementations...
goto.implementation.chooserTitle=<html><body>Choose Implementation of <b>{0}</b> ({1} found)</body></html>
goto.implementation.notFound=Not implementations found
goto.implementation.notFound=No implementations found
goto.test.chooserTitle.test=<html><body>Choose Test for <b>{0}</b> ({1} found)</body></html>
goto.test.chooserTitle.subject=<html><body>Choose Test Subject for <b>{0}</b> ({1} found)</body></html>
goto.test.notFound=Not test subjects found
goto.test.notFound=No test subjects found
incremental.search.tooltip.prefix=Search for:
goto.super.method.chooser.title=Choose super method
@@ -32,14 +32,14 @@ el.lparen.expected=( expected
el.rparen.expected=) expected
el.colon.expected=: expected
el.value.expected=Value expected
el.cannot.resolve.function=Cannot resolve function {0} #loc
el.cannot.resolve.namespace=Cannot resolve namespace {0} #loc
el.cannot.resolve.property=Cannot resolve property or method {0} (dynamic property?) #loc
el.cannot.resolve.function=Cannot resolve function ''{0}'' #loc
el.cannot.resolve.namespace=Cannot resolve namespace ''{0}'' #loc
el.cannot.resolve.property=Cannot resolve property or method ''{0}'' (dynamic property?) #loc
el.mismatched.parameters.count=Different number of formal and actual parameters
el.declare.variable.intention.family=Declare Variable As External Data
el.declare.variable.via.usebean.intention.name=Declare external variable as <jsp:useBean />
el.declare.variable.via.comment.annotation.intention.name=Declare external variable in comment annotation
el.cannot.resolve.variable=Cannot resolve variable {0} #loc
el.cannot.resolve.variable=Cannot resolve variable ''{0}'' #loc
jsf.el.out.of.attribute=JSF EL out of attribute #loc
jsf.method.call.is.nonstd.extension=Method call is nonstandard extension #loc
@@ -79,7 +79,7 @@ status.bar.insert.status.text=Insert
status.bar.overwrite.status.text=Overwrite
popup.hints.panel.click.to.configure.highlighting.tooltip.text=Click to configure highlighting for this file
popup.hints.panel.click.to.configure.profile.text=Click to configure inspection profiles
read.only.attr.panel.double.click.to.toggle.attr.tooltip.text=Double-click to toggle the read-only attribute
read.only.attr.panel.double.click.to.toggle.attr.tooltip.text=Click to toggle the read-only attribute
welcome.screen.get.from.vcs.action.no.vcs.plugins.with.check.out.action.installed.action.name=No VCS plugins with Check-out action installed.
welcome.screen.get.from.vcs.action.checkout.from.list.popup.title=Checkout from
welcome.screen.recent.projects.action.no.recent.projects.to.display.action.name=No recent projects to display.
@@ -45,7 +45,6 @@ import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.util.io.URLUtil;
import org.apache.tools.ant.taskdefs.optional.junit.XMLConstants;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.xml.sax.SAXException;
@@ -258,7 +257,7 @@ public class ExportTestResultsAction extends DumbAwareAction {
else {
Source xslSource;
if (config.getExportFormat() == ExportTestResultsConfiguration.ExportFormat.BundledTemplate) {
URL bundledXsltUrl = XMLConstants.class.getResource("/org/apache/tools/ant/taskdefs/optional/junit/xsl/junit-noframes.xsl");
URL bundledXsltUrl = getClass().getResource("junit-noframes.xsl");
xslSource = new StreamSource(URLUtil.openStream(bundledXsltUrl));
}
else {
@@ -24,7 +24,6 @@ import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Ref;
import com.intellij.util.PairProcessor;
import org.apache.tools.ant.taskdefs.optional.junit.XMLConstants;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
@@ -34,7 +33,18 @@ import java.util.Map;
// this class generates resulting XML compatible to that of XMLJUnitResultFormatter
public class TestResultsXmlFormatter implements XMLConstants {
public class TestResultsXmlFormatter {
// see org.apache.tools.ant.taskdefs.optional.junit.XmlConstants
private static final String TESTSUITES = "testsuites";
private static final String TESTSUITE = "testsuite";
private static final String TESTCASE = "testcase";
private static final String FAILURE = "failure";
private static final String ATTR_NAME = "name";
private static final String ATTR_FAILURES = "failures";
private static final String ATTR_TESTS = "tests";
private static final Logger LOG = Logger.getInstance(TestResultsXmlFormatter.class.getName());
private final RuntimeConfiguration myRuntimeConfiguration;
@@ -0,0 +1,467 @@
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:lxslt="http://xml.apache.org/xslt"
xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils">
<xsl:output method="html" indent="yes" encoding="US-ASCII"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" />
<xsl:decimal-format decimal-separator="." grouping-separator="," />
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<xsl:param name="TITLE">Unit Test Results.</xsl:param>
<!--
Sample stylesheet to be used with Ant JUnitReport output.
It creates a non-framed report that can be useful to send via
e-mail or such.
-->
<xsl:template match="testsuites">
<html>
<head>
<title><xsl:value-of select="$TITLE"/></title>
<style type="text/css">
body {
font:normal 68% verdana,arial,helvetica;
color:#000000;
}
table tr td, table tr th {
font-size: 68%;
}
table.details tr th{
font-weight: bold;
text-align:left;
background:#a6caf0;
}
table.details tr td{
background:#eeeee0;
}
p {
line-height:1.5em;
margin-top:0.5em; margin-bottom:1.0em;
}
h1 {
margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
}
h2 {
margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
}
h3 {
margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
}
h4 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
h5 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
h6 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
.Error {
font-weight:bold; color:red;
}
.Failure {
font-weight:bold; color:purple;
}
.Properties {
text-align:right;
}
</style>
<script type="text/javascript" language="JavaScript">
var TestCases = new Array();
var cur;
<xsl:for-each select="./testsuite">
<xsl:apply-templates select="properties"/>
</xsl:for-each>
</script>
<script type="text/javascript" language="JavaScript"><![CDATA[
function displayProperties (name) {
var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
var doc = win.document;
doc.open();
doc.write("<html><head><title>Properties of " + name + "</title>");
doc.write("<style>")
doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
doc.write("table tr td, table tr th { font-size: 68%; }");
doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
doc.write("</style>");
doc.write("</head><body>");
doc.write("<h3>Properties of " + name + "</h3>");
doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
doc.write("<table class='properties'>");
doc.write("<tr><th>Name</th><th>Value</th></tr>");
for (prop in TestCases[name]) {
doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
}
doc.write("</table>");
doc.write("</body></html>");
doc.close();
win.focus();
}
]]>
</script>
</head>
<body>
<a name="top"></a>
<xsl:call-template name="pageHeader"/>
<!-- Summary part -->
<xsl:call-template name="summary"/>
<hr size="1" width="95%" align="left"/>
<!-- Package List part -->
<xsl:call-template name="packagelist"/>
<hr size="1" width="95%" align="left"/>
<!-- For each package create its part -->
<xsl:call-template name="packages"/>
<hr size="1" width="95%" align="left"/>
<!-- For each class create the part -->
<xsl:call-template name="classes"/>
</body>
</html>
</xsl:template>
<!-- ================================================================== -->
<!-- Write a list of all packages with an hyperlink to the anchor of -->
<!-- of the package name. -->
<!-- ================================================================== -->
<xsl:template name="packagelist">
<h2>Packages</h2>
Note: package statistics are not computed recursively, they only sum up all of its testsuites numbers.
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<xsl:call-template name="testsuite.test.header"/>
<!-- list all packages recursively -->
<xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
<xsl:sort select="@package"/>
<xsl:variable name="testsuites-in-package" select="/testsuites/testsuite[./@package = current()/@package]"/>
<xsl:variable name="testCount" select="sum($testsuites-in-package/@tests)"/>
<xsl:variable name="errorCount" select="sum($testsuites-in-package/@errors)"/>
<xsl:variable name="failureCount" select="sum($testsuites-in-package/@failures)"/>
<xsl:variable name="timeCount" select="sum($testsuites-in-package/@time)"/>
<!-- write a summary for the package -->
<tr valign="top">
<!-- set a nice color depending if there is an error/failure -->
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
<xsl:when test="$errorCount &gt; 0">Error</xsl:when>
</xsl:choose>
</xsl:attribute>
<td><a href="#{@package}"><xsl:value-of select="@package"/></a></td>
<td><xsl:value-of select="$testCount"/></td>
<td><xsl:value-of select="$errorCount"/></td>
<td><xsl:value-of select="$failureCount"/></td>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$timeCount"/>
</xsl:call-template>
</td>
<td><xsl:value-of select="$testsuites-in-package/@timestamp"/></td>
<td><xsl:value-of select="$testsuites-in-package/@hostname"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<!-- ================================================================== -->
<!-- Write a package level report -->
<!-- It creates a table with values from the document: -->
<!-- Name | Tests | Errors | Failures | Time -->
<!-- ================================================================== -->
<xsl:template name="packages">
<!-- create an anchor to this package name -->
<xsl:for-each select="/testsuites/testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
<xsl:sort select="@package"/>
<a name="{@package}"></a>
<h3>Package <xsl:value-of select="@package"/></h3>
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<xsl:call-template name="testsuite.test.header"/>
<!-- match the testsuites of this package -->
<xsl:apply-templates select="/testsuites/testsuite[./@package = current()/@package]" mode="print.test"/>
</table>
<a href="#top">Back to top</a>
<p/>
<p/>
</xsl:for-each>
</xsl:template>
<xsl:template name="classes">
<xsl:for-each select="testsuite">
<xsl:sort select="@name"/>
<!-- create an anchor to this class name -->
<a name="{@name}"></a>
<h3>TestCase <xsl:value-of select="@name"/></h3>
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<xsl:call-template name="testcase.test.header"/>
<!--
test can even not be started at all (failure to load the class)
so report the error directly
-->
<xsl:if test="./error">
<tr class="Error">
<td colspan="4"><xsl:apply-templates select="./error"/></td>
</tr>
</xsl:if>
<xsl:apply-templates select="./testcase" mode="print.test"/>
</table>
<div class="Properties">
<a>
<xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
Properties &#187;
</a>
</div>
<p/>
<a href="#top">Back to top</a>
</xsl:for-each>
</xsl:template>
<xsl:template name="summary">
<h2>Summary</h2>
<xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
<xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
<xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
<xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
<xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<tr valign="top">
<th>Tests</th>
<th>Failures</th>
<th>Errors</th>
<th>Success rate</th>
<th>Time</th>
</tr>
<tr valign="top">
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
<xsl:when test="$errorCount &gt; 0">Error</xsl:when>
</xsl:choose>
</xsl:attribute>
<td><xsl:value-of select="$testCount"/></td>
<td><xsl:value-of select="$failureCount"/></td>
<td><xsl:value-of select="$errorCount"/></td>
<td>
<xsl:call-template name="display-percent">
<xsl:with-param name="value" select="$successRate"/>
</xsl:call-template>
</td>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$timeCount"/>
</xsl:call-template>
</td>
</tr>
</table>
<table border="0" width="95%">
<tr>
<td style="text-align: justify;">
Note: <i>failures</i> are anticipated and checked for with assertions while <i>errors</i> are unanticipated.
</td>
</tr>
</table>
</xsl:template>
<!--
Write properties into a JavaScript data structure.
This is based on the original idea by Erik Hatcher (ehatcher@apache.org)
-->
<xsl:template match="properties">
cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
<xsl:for-each select="property">
<xsl:sort select="@name"/>
cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
</xsl:for-each>
</xsl:template>
<!-- Page HEADER -->
<xsl:template name="pageHeader">
<h1><xsl:value-of select="$TITLE"/></h1>
<table width="100%">
<tr>
<td align="left"></td>
<td align="right">Designed for use with <a href='http://www.junit.org'>JUnit</a> and <a href='http://ant.apache.org/ant'>Ant</a>.</td>
</tr>
</table>
<hr size="1"/>
</xsl:template>
<xsl:template match="testsuite" mode="header">
<tr valign="top">
<th width="80%">Name</th>
<th>Tests</th>
<th>Errors</th>
<th>Failures</th>
<th nowrap="nowrap">Time(s)</th>
</tr>
</xsl:template>
<!-- class header -->
<xsl:template name="testsuite.test.header">
<tr valign="top">
<th width="80%">Name</th>
<th>Tests</th>
<th>Errors</th>
<th>Failures</th>
<th nowrap="nowrap">Time(s)</th>
<th nowrap="nowrap">Time Stamp</th>
<th>Host</th>
</tr>
</xsl:template>
<!-- method header -->
<xsl:template name="testcase.test.header">
<tr valign="top">
<th>Name</th>
<th>Status</th>
<th width="80%">Type</th>
<th nowrap="nowrap">Time(s)</th>
</tr>
</xsl:template>
<!-- class information -->
<xsl:template match="testsuite" mode="print.test">
<tr valign="top">
<!-- set a nice color depending if there is an error/failure -->
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
<xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
</xsl:choose>
</xsl:attribute>
<!-- print testsuite information -->
<td><a href="#{@name}"><xsl:value-of select="@name"/></a></td>
<td><xsl:value-of select="@tests"/></td>
<td><xsl:value-of select="@errors"/></td>
<td><xsl:value-of select="@failures"/></td>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="@time"/>
</xsl:call-template>
</td>
<td><xsl:apply-templates select="@timestamp"/></td>
<td><xsl:apply-templates select="@hostname"/></td>
</tr>
</xsl:template>
<xsl:template match="testcase" mode="print.test">
<tr valign="top">
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="failure | error">Error</xsl:when>
</xsl:choose>
</xsl:attribute>
<td><xsl:value-of select="@name"/></td>
<xsl:choose>
<xsl:when test="failure">
<td>Failure</td>
<td><xsl:apply-templates select="failure"/></td>
</xsl:when>
<xsl:when test="error">
<td>Error</td>
<td><xsl:apply-templates select="error"/></td>
</xsl:when>
<xsl:otherwise>
<td>Success</td>
<td></td>
</xsl:otherwise>
</xsl:choose>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="@time"/>
</xsl:call-template>
</td>
</tr>
</xsl:template>
<xsl:template match="failure">
<xsl:call-template name="display-failures"/>
</xsl:template>
<xsl:template match="error">
<xsl:call-template name="display-failures"/>
</xsl:template>
<!-- Style for the error and failure in the tescase template -->
<xsl:template name="display-failures">
<xsl:choose>
<xsl:when test="not(@message)">N/A</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@message"/>
</xsl:otherwise>
</xsl:choose>
<!-- display the stacktrace -->
<code>
<br/><br/>
<xsl:call-template name="br-replace">
<xsl:with-param name="word" select="."/>
</xsl:call-template>
</code>
<!-- the later is better but might be problematic for non-21" monitors... -->
<!--pre><xsl:value-of select="."/></pre-->
</xsl:template>
<xsl:template name="JS-escape">
<xsl:param name="string"/>
<xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
<xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
<xsl:value-of select="$tmp2"/>
</xsl:template>
<!--
template that will convert a carriage return into a br tag
@param word the text from which to convert CR to BR tag
-->
<xsl:template name="br-replace">
<xsl:param name="word"/>
<xsl:value-of disable-output-escaping="yes" select='stringutils:replace(string($word),"&#xA;","&lt;br/>")'/>
</xsl:template>
<xsl:template name="display-time">
<xsl:param name="value"/>
<xsl:value-of select="format-number($value,'0.000')"/>
</xsl:template>
<xsl:template name="display-percent">
<xsl:param name="value"/>
<xsl:value-of select="format-number($value,'0.00%')"/>
</xsl:template>
</xsl:stylesheet>
-1
View File
@@ -11,7 +11,6 @@
<orderEntry type="module" module-name="lang-impl" />
<orderEntry type="module" module-name="platform-impl" />
<orderEntry type="module" module-name="xdebugger-api" />
<orderEntry type="library" name="Ant" level="project" />
</component>
</module>
@@ -510,7 +510,7 @@ public class FileUtil {
return parentFile.exists() && parentFile.isDirectory() || parentFile.mkdirs();
}
}
return false;
return true;
}
public static boolean createIfDoesntExist(File file) {
@@ -1236,6 +1236,20 @@ public class StringUtil {
return i;
}
/**
* Allows to answer if target symbol is contained at given char sequence at <code>[start; end)</code> interval.
*
* @param s target char sequence to check
* @param start start offset to use within the given char sequence (inclusive)
* @param end end offset to use within the given char sequence (exclusive)
* @param c target symbol to check
* @return <code>true</code> if given symbol is contained at the target range of the given char sequence;
* <code>false</code> otherwise
*/
public static boolean contains(CharSequence s, int start, int end, char c) {
return indexOf(s, c, start, end) >= 0;
}
public static int indexOf(@NotNull CharSequence s, char c) {
return indexOf(s, c, 0, s.length());
}
@@ -163,7 +163,7 @@ public class IncomingChangesIndicator implements ProjectComponent {
return "IncomingChanges";
}
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return this;
}
@@ -174,7 +174,7 @@ public class IOResourceInspection extends ResourceInspection {
public boolean isIOResource(PsiExpression expression){
return TypeUtils.expressionHasTypeOrSubtype(expression,
"java.io.InputStream", "java.io.Writer", "java.io.Reader",
"java.io.RandomAccessFile", "java.io.OutputStream") != null &&
"java.io.RandomAccessFile", "java.io.OutputStream", "java.util.zip.ZipFile") != null &&
!isIgnoredType(expression);
}
+14
View File
@@ -207,6 +207,11 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
* The reference tracker
*/
private GitReferenceTracker myReferenceTracker;
/**
* If true, the vcs was activated
*/
private boolean isActivated;
public static GitVcs getInstance(@NotNull Project project) {
return (GitVcs)ProjectLevelVcsManager.getInstance(project).findVcsByName(NAME);
@@ -496,6 +501,7 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
*/
@Override
protected void activate() {
isActivated = true;
if (!myProject.isDefault() && myRootTracker == null) {
myRootTracker = new GitRootTracker(this, myProject, myRootListeners.getMulticaster());
}
@@ -519,6 +525,7 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
*/
@Override
protected void deactivate() {
isActivated = false;
GitBranchConfigurations.getInstance(myProject).deactivate();
if (myRootTracker != null) {
myRootTracker.dispose();
@@ -768,4 +775,11 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
final VirtualFile vcsRoot = GitUtil.getGitRoot(file);
return GitChangeUtils.getRevisionChanges(project, vcsRoot, revision.getRevisionNumber().asString(), false);
}
/**
* @return true if vcs was activated
*/
public boolean isActivated() {
return isActivated;
}
}
@@ -256,7 +256,7 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
if (seenMerges) {
rootsWithMerges.add(r.root);
}
if (r.remoteCommits > 0 && seenCheckedNode || reorderNeeded) {
if (r.remoteCommits > 0 || reorderNeeded) {
roots.add(r.root);
}
if (reorderNeeded) {
@@ -89,7 +89,7 @@ public class GitBranchConfigurationChangedDialog extends DialogWrapper {
myTable.setModel(new DescriptorTableModel());
myNameTextField.setText(config.getName());
myNewAction = new DialogWrapperExitAction("New Configuration", NEW_CONFIGURATION);
myNameTextField.getDocument().addDocumentListener(new DocumentAdapter() {
final DocumentAdapter l = new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
String text = myNameTextField.getText().trim();
@@ -113,8 +113,9 @@ public class GitBranchConfigurationChangedDialog extends DialogWrapper {
setOKActionEnabled(s == null);
myNewAction.setEnabled(s == null);
}
});
};
myNameTextField.getDocument().addDocumentListener(l);
l.changedUpdate(null);
setOKButtonText("Update");
init();
}
@@ -49,6 +49,7 @@ import git4idea.vfs.GitReferenceListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
/**
@@ -147,6 +148,10 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
* Listener for changes
*/
private final ChangeListAdapter myChangesListener;
/**
* If true, the widget is enabled
*/
private boolean myWidgetEnabled = true;
/**
* The constructor used to dependency injection
@@ -268,8 +273,20 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
}
referencesChanged();
}
if (isWidgetEnabled()) {
installWidget();
}
}
/**
* Install widget
*/
private void installWidget() {
if (!ApplicationManager.getApplication().isHeadlessEnvironment()) {
myWidgetUninstall = GitBranchesWidget.install(myProject, this);
final Runnable r = GitBranchesWidget.install(myProject, this);
synchronized (myStateLock) {
myWidgetUninstall = r;
}
}
}
@@ -279,10 +296,18 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
public void deactivate() {
myVcs.removeGitReferenceListener(myReferenceListener);
myChangeManager.removeChangeListListener(myChangesListener);
if (myWidgetUninstall != null) {
myWidgetUninstall.run();
uninstallWidget();
}
private void uninstallWidget() {
final Runnable r;
synchronized (myStateLock) {
r = myWidgetUninstall;
myWidgetUninstall = null;
}
if (r != null) {
r.run();
}
}
/**
@@ -310,6 +335,7 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
public State getState() {
synchronized (myStateLock) {
State rc = new State();
rc.IS_WIDGET_ENABLED = myWidgetEnabled;
rc.CURRENT = myCurrentConfiguration == null ? null : myCurrentConfiguration.getName();
ArrayList<BranchConfiguration> cs = new ArrayList<BranchConfiguration>(myConfigurations.size());
for (GitBranchConfiguration ci : myConfigurations.values()) {
@@ -363,9 +389,54 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
}
fireCurrentConfigurationChanged();
fireConfigurationsChanged();
if (state.IS_WIDGET_ENABLED != myWidgetEnabled) {
myWidgetEnabled = state.IS_WIDGET_ENABLED;
updateWidgetState();
}
}
}
/**
* Update widget state after update
*/
private void updateWidgetState() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isWidgetEnabled()) {
if (myVcs.isActivated() && myWidgetUninstall == null) {
installWidget();
}
}
else {
uninstallWidget();
}
}
});
}
/**
* @return true if widget is enabled
*/
public boolean isWidgetEnabled() {
synchronized (myStateLock) {
return myWidgetEnabled;
}
}
/**
* Update widget state
*
* @param value true to enable widget
*/
public void setWidgetEnabled(boolean value) {
synchronized (myStateLock) {
myWidgetEnabled = value;
updateWidgetState();
}
}
/**
* @return the candidate remote configurations
*/
@@ -632,18 +703,6 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
return myStateLock;
}
/**
* Check if configuration with specified name already exists
*
* @param name the name to check
* @return true if the configuration exists
*/
boolean hasConfigurationName(String name) {
synchronized (myStateLock) {
return myConfigurations.containsKey(name);
}
}
/**
* Set current configuration
*
@@ -765,6 +824,10 @@ public class GitBranchConfigurations implements PersistentStateComponent<GitBran
* The configuration state
*/
public static class State {
/**
* If true, branches widget is enabled
*/
public boolean IS_WIDGET_ENABLED = true;
/**
* The current configuration
*/
@@ -22,6 +22,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.JBPopupFactory;
@@ -29,6 +30,7 @@ import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager;
import com.intellij.openapi.vfs.VirtualFile;
@@ -36,6 +38,7 @@ import com.intellij.openapi.wm.CustomStatusBarWidget;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.impl.status.TextPanel;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.ui.UIUtil;
import git4idea.GitUtil;
@@ -57,12 +60,15 @@ import java.util.Collection;
/**
* The git branches widget
*/
public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
public class GitBranchesWidget extends TextPanel implements CustomStatusBarWidget {
/**
* The arrows icon
*/
private static final Icon ARROWS_ICON = IconLoader.getIcon("/ide/statusbar_arrows.png");
/**
* The logger
*/
private static final Logger LOG = Logger.getInstance(GitBranchesWidget.class.getName());
/**
* The ID of the widget
*/
@@ -75,6 +81,10 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
* The project
*/
final Project myProject;
/**
* The status bar
*/
private final StatusBar myStatusBar;
/**
* The configurations instance
*/
@@ -83,14 +93,14 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
* The selectable configurations. Null if invalidated or non-initialized
*/
private AnAction[] mySelectableConfigurations;
/**
* The selectable configurations. Null if invalidated or non-initialized
*/
private AnAction[] mySelectableWithChangesConfigurations;
/**
* The candidate remote configurations. Null if invalidated or non-initialized
*/
private AnAction[] myRemoveConfigurations;
/**
* The status bar
*/
private StatusBar myStatusBar;
/**
* If true, the popup is enabled
*/
@@ -103,6 +113,10 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
* The current popup
*/
private ListPopup myPopup;
/**
* The key for the last popup, used to determine if disposed popup is the actually the last pop up.
*/
private Object myPopupKey;
/**
* The default foreground color
*/
@@ -112,17 +126,18 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
/**
* The constructor
*
* @param project the project instance
* @param project the project instance
* @param statusBar the status bar
* @param configurations the configuration settings
*/
public GitBranchesWidget(Project project, GitBranchConfigurations configurations) {
public GitBranchesWidget(Project project, StatusBar statusBar, GitBranchConfigurations configurations) {
myProject = project;
myStatusBar = statusBar;
setBorder(WidgetBorder.INSTANCE);
//setBorder(BorderFactory.createEtchedBorder());
myConfigurations = configurations;
myDefaultForeground = getForeground();
myConfigurationsListener = new MyGitBranchConfigurationsListener();
myConfigurations.addConfigurationListener(myConfigurationsListener);
setIcon(IconLoader.findIcon("/icons/branch.png", getClass()));
Disposer.register(myConfigurations, this);
addMouseListener(new MouseAdapter() {
@Override
@@ -147,8 +162,8 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
public void run() {
StatusBar statusBar = WindowManager.getInstance().getStatusBar(project);
if (statusBar != null) {
final GitBranchesWidget w = new GitBranchesWidget(project, configurations);
statusBar.addWidget(w, "after InsertOverwrite", project);
final GitBranchesWidget w = new GitBranchesWidget(project, statusBar, configurations);
statusBar.addWidget(w, "after " + (SystemInfo.isMac ? "Encoding" : "InsertOverwrite"), project);
widget.set(w);
}
}
@@ -190,7 +205,7 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
* {@inheritDoc}
*/
@Override
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return null;
}
@@ -199,7 +214,6 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
*/
@Override
public void install(@NotNull StatusBar statusBar) {
myStatusBar = statusBar;
}
/**
@@ -219,7 +233,7 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
if (myRemoveConfigurations == null) {
ArrayList<AnAction> rc = new ArrayList<AnAction>();
for (final String c : myConfigurations.getRemotesCandidates()) {
rc.add(new AnAction(c) {
rc.add(new DumbAwareAction(c) {
@Override
public void actionPerformed(AnActionEvent e) {
myConfigurations.startCheckout(null, c, false);
@@ -232,19 +246,28 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
return myRemoveConfigurations;
}
/**
* Show popup is if it is not shown.
*/
void showPopup() {
if (!myPopupEnabled) {
return;
}
if (myPopup != null) {
myPopup.cancel();
}
final DataContext parent = DataManager.getInstance().getDataContext((Component)myStatusBar);
final DataContext dataContext = SimpleDataContext.getSimpleContext(PlatformDataKeys.PROJECT.getName(), myProject, parent);
final Object key = new Object();
myPopupKey = key;
myPopup = JBPopupFactory.getInstance()
.createActionGroupPopup("Checkout", getPopupActionGroup(), dataContext, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, true,
.createActionGroupPopup(null, getPopupActionGroup(), dataContext, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, true,
new Runnable() {
@Override
public void run() {
myPopup = null;
if (key == myPopupKey) {
myPopup = null;
}
}
}, 20);
final Dimension dimension = myPopup.getContent().getPreferredSize();
@@ -253,13 +276,13 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
}
/**
* Ensure that action for checking out configurations are crated
*
* @return get or create selectable configuration group
*/
private AnAction[] getSelectable() {
private AnAction[] ensureSelectableCreated() {
assert myPopupEnabled : "pop should be enabled";
if (mySelectableConfigurations == null) {
ArrayList<AnAction> rc = new ArrayList<AnAction>();
if (mySelectableConfigurations == null || mySelectableWithChangesConfigurations == null) {
GitBranchConfiguration current;
try {
current = myConfigurations.getCurrentConfiguration();
@@ -267,48 +290,63 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
catch (VcsException e) {
LOG.error("Unexpected error at this point", e);
mySelectableConfigurations = new AnAction[0];
mySelectableWithChangesConfigurations = new AnAction[0];
return mySelectableConfigurations;
}
String name = current == null ? "" : current.getName();
for (final String c : myConfigurations.getConfigurationNames()) {
if (name.equals(c)) {
// skip current config
continue;
}
rc.add(new AnAction(c) {
@Override
public void actionPerformed(AnActionEvent e) {
try {
final GitBranchConfiguration toCheckout = myConfigurations.getConfiguration(c);
if (toCheckout == null) {
throw new VcsException("The configuration " + c + " cannot be found.");
}
myConfigurations.startCheckout(toCheckout, null, true);
}
catch (VcsException e1) {
GitUIUtil.showOperationError(myProject, e1, "Unable to load: " + c);
}
}
});
}
mySelectableConfigurations = rc.toArray(new AnAction[rc.size()]);
mySelectableConfigurations = checkoutActions(name, true);
mySelectableWithChangesConfigurations = checkoutActions(name, false);
}
return mySelectableConfigurations;
}
/**
* Checkout actions
*
* @param name the excluded name
* @param quick true, if quick checkout actions
* @return an array of actions for the configurations
*/
private AnAction[] checkoutActions(String name, final boolean quick) {
ArrayList<AnAction> rc = new ArrayList<AnAction>();
for (final String c : myConfigurations.getConfigurationNames()) {
if (name.equals(c)) {
// skip current config
continue;
}
rc.add(new DumbAwareAction(c) {
@Override
public void actionPerformed(AnActionEvent e) {
try {
final GitBranchConfiguration toCheckout = myConfigurations.getConfiguration(c);
if (toCheckout == null) {
throw new VcsException("The configuration " + c + " cannot be found.");
}
myConfigurations.startCheckout(toCheckout, null, quick);
}
catch (VcsException e1) {
GitUIUtil.showOperationError(myProject, e1, "Unable to load: " + c);
}
}
});
}
return rc.toArray(new AnAction[rc.size()]);
}
/**
* @return the action group for popup
*/
ActionGroup getPopupActionGroup() {
if (myPopupActionGroup == null) {
myPopupActionGroup = new DefaultActionGroup(null, false);
myPopupActionGroup.addAction(new AnAction("Manage Configurations ...") {
myPopupActionGroup.addAction(new DumbAwareAction("Manage Configurations ...") {
@Override
public void actionPerformed(AnActionEvent e) {
GitManageConfigurationsDialog.showDialog(myProject, myConfigurations);
}
});
myPopupActionGroup.addAction(new AnAction("Modify Current Configuration ...") {
myPopupActionGroup.addAction(new DumbAwareAction("Modify Current Configuration ...") {
@Override
public void actionPerformed(AnActionEvent e) {
try {
@@ -320,13 +358,14 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
}
}
});
myPopupActionGroup.addAction(new AnAction("New Configuration ...") {
myPopupActionGroup.addAction(new DumbAwareAction("New Configuration ...") {
@Override
public void actionPerformed(AnActionEvent e) {
myConfigurations.startCheckout(null, null, false);
}
});
myPopupActionGroup.add(new MyRemotesActionGroup());
myPopupActionGroup.add(new MySelectableWithChangesActionGroup());
myPopupActionGroup.addSeparator("Branch Configurations");
myPopupActionGroup.add(new MySelectableActionGroup());
}
@@ -339,23 +378,32 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
private void updateLabel() {
cancelPopup();
final GitBranchConfigurations.SpecialStatus status = myConfigurations.getSpecialStatus();
String t;
String text;
myPopupEnabled = false;
Color color = Color.RED;
String tooltip;
switch (status) {
case CHECKOUT_IN_PROGRESS:
t = "Checkout in progress...";
text = "Checkout...";
color = Color.BLUE;
tooltip = "A checkout operation is in progress.";
break;
case MERGING:
t = "Merging...";
text = "Merging...";
tooltip = "Merge is in progress in some vcs roots.";
break;
case REBASING:
t = "Rebasing...";
tooltip = "Rebase is in progress in some vcs roots.";
text = "Rebasing...";
break;
case NON_GIT:
t = "Non-Git project";
tooltip = "No valid vcs roots are configuration for the project.";
text = "Non-Git project";
break;
case SUBMODULES:
t = "Submodules unsupported";
tooltip =
"<html>The submodules are unsupported in the current version.<br/>It is possible to disable widget in Git Vcs settings.</html>";
text = "Submodules unsupported";
break;
case NORMAL:
GitBranchConfiguration current;
@@ -366,18 +414,49 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
current = null;
}
if (current == null) {
t = "Detection in progress";
tooltip = "The branch configurations are not yet detected.";
text = "Detecting...";
}
else {
myPopupEnabled = true;
t = current.getName();
text = current.getName();
tooltip = "<html>The Git branch configuration <b>" + text + "</b> is selected.<br/>Click to select other configuration.</html>";
color = myDefaultForeground;
}
break;
default:
t = "Unknown status: " + status;
tooltip = "Unknown status: " + status;
text = "Unknown status: " + status;
}
setForeground(myPopupEnabled ? myDefaultForeground : Color.RED);
setText(t);
if (!SystemInfo.isMac) {
setForeground(color);
}
setToolTipText(tooltip);
setText(text);
invalidate();
}
@Override
protected void paintComponent(@NotNull final Graphics g) {
super.paintComponent(g);
if (getText() != null && myPopupEnabled) {
final Rectangle r = getBounds();
final Insets insets = getInsets();
ARROWS_ICON
.paintIcon(this, g, r.width - insets.right - ARROWS_ICON.getIconWidth() - 2, r.height / 2 - ARROWS_ICON.getIconHeight() / 2);
}
}
@Override
public Dimension getPreferredSize() {
final Dimension preferredSize = super.getPreferredSize();
return new Dimension(preferredSize.width + ARROWS_ICON.getIconWidth() + 4, preferredSize.height);
}
@Override
protected String getTextForPreferredSize() {
return getText();
}
/**
@@ -406,7 +485,29 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
@NotNull
@Override
public AnAction[] getChildren(@Nullable AnActionEvent e) {
return getSelectable();
return ensureSelectableCreated();
}
}
/**
* Remotes action group
*/
class MySelectableWithChangesActionGroup extends ActionGroup {
/**
* The constructor
*/
public MySelectableWithChangesActionGroup() {
super("Check out with Selected Changes...", true);
}
/**
* {@inheritDoc}
*/
@NotNull
@Override
public AnAction[] getChildren(@Nullable AnActionEvent e) {
ensureSelectableCreated();
return mySelectableWithChangesConfigurations;
}
}
@@ -444,6 +545,7 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
@Override
public void run() {
mySelectableConfigurations = null;
mySelectableWithChangesConfigurations = null;
}
});
}
@@ -475,6 +577,7 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
@Override
public void run() {
mySelectableConfigurations = null;
mySelectableWithChangesConfigurations = null;
updateLabel();
}
});
@@ -498,7 +601,7 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
/**
* Refresh git remotes
*/
class RefreshRemotesAction extends AnAction {
class RefreshRemotesAction extends DumbAwareAction {
/**
* The constructor
*/
@@ -549,5 +652,4 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget {
});
}
}
}
@@ -585,10 +585,7 @@ public class GitCheckoutProcess {
LocalChangeList defaultList = myChangeManager.getDefaultChangeList();
for (ChangeListInfo changeListInfo : changes.CHANGE_LISTS) {
LocalChangeList changeList = lists.get(changeListInfo.NAME);
if (changeList != null) {
myChangeManager.setReadOnly(changeList.getName(), false);
}
else {
if (changeList == null) {
changeList = myChangeManager.addChangeList(changeListInfo.NAME, changeListInfo.COMMENT);
lists.put(changeListInfo.NAME, changeList);
}
@@ -173,7 +173,7 @@ public class GitManageConfigurationsDialog extends DialogWrapper {
"</td></tr></html>");
}
}
boolean isNonCurrent = selected != null || selected != current;
boolean isNonCurrent = selected != null && selected != current;
myDeleteButton.setEnabled(isNonCurrent);
setOKActionEnabled(isNonCurrent && myConfigurations.getSpecialStatus() == GitBranchConfigurations.SpecialStatus.NORMAL);
}
@@ -205,6 +205,7 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
verify();
}
});
verify();
init();
}
@@ -819,8 +820,7 @@ public class GitSwitchBranchesDialog extends DialogWrapper {
@Override
protected void textChanged(DocumentEvent e) {
String s = myTextField.getText();
if (s.length() == 0 &&
(myInvalidValues == null || !myInvalidValues.contains(s)) &&
if ((myInvalidValues == null || !myInvalidValues.contains(s)) &&
(s.length() == 0 || GitBranchNameValidator.INSTANCE.checkInput(s))) {
myTextField.setForeground(myDefaultForeground);
}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="git4idea.config.GitVcsPanel">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
@@ -10,107 +10,150 @@
</properties>
<border type="none"/>
<children>
<component id="b1a9d" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.path.label"/>
<verifyInputWhenFocusTarget value="false"/>
</properties>
</component>
<component id="25af7" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myGitField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<vspacer id="4c83d">
<constraints>
<grid row="5" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="563b2" class="javax.swing.JButton" binding="myTestButton">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="2" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<horizontalAlignment value="0"/>
<label resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.test.label"/>
<text resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.test"/>
</properties>
</component>
<component id="5a909" class="javax.swing.JLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="f5de9"/>
<text resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.ssh.mode"/>
</properties>
</component>
<grid id="97c7d" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="b95e2" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<border type="etched" title="Interface Options">
<font/>
</border>
<children>
<component id="f5de9" class="javax.swing.JComboBox" binding="mySSHExecutableComboBox" default-binding="true">
<component id="26b57" class="javax.swing.JCheckBox" binding="myEnableBranchesWidgetCheckBox" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="true"/>
<text value="Enable &amp;branches widget"/>
<toolTipText value="Enables status widget in status bar"/>
</properties>
</component>
<hspacer id="9eafb">
<hspacer id="a6134">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
</children>
</grid>
<component id="a6d8e" class="javax.swing.JLabel">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="7a626"/>
<text resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.convert.crlf"/>
</properties>
</component>
<grid id="a4472" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="e7668" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<border type="etched" title="File Conversion"/>
<children>
<component id="a6d8e" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="7a626"/>
<text resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.convert.crlf"/>
</properties>
</component>
<hspacer id="9e883">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<vspacer id="8bedb">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="7a626" class="javax.swing.JComboBox" binding="myConvertTextFilesComboBox" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<toolTipText resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.convert.tooltip"/>
</properties>
</component>
<hspacer id="3c6b8">
<component id="a8818" class="javax.swing.JCheckBox" binding="myAskBeforeConversionsCheckBox" default-binding="true">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<grid row="1" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<properties>
<selected value="true"/>
<text resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.convert.ask"/>
<toolTipText resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.convert.ask.tooltip"/>
</properties>
</component>
</children>
</grid>
<component id="a8818" class="javax.swing.JCheckBox" binding="myAskBeforeConversionsCheckBox" default-binding="true">
<grid id="c9e6b" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="4" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="true"/>
<text resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.convert.ask"/>
<toolTipText resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.convert.ask.tooltip"/>
</properties>
</component>
<properties/>
<border type="etched" title="Git"/>
<children>
<component id="b1a9d" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.path.label"/>
<verifyInputWhenFocusTarget value="false"/>
</properties>
</component>
<component id="25af7" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myGitField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="563b2" class="javax.swing.JButton" binding="myTestButton">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="2" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<horizontalAlignment value="0"/>
<label resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.test.label"/>
<text resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.test"/>
</properties>
</component>
<component id="5a909" class="javax.swing.JLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="f5de9"/>
<text resource-bundle="git4idea/i18n/GitBundle" key="git.vcs.config.ssh.mode"/>
</properties>
</component>
<grid id="97c7d" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<hspacer id="9eafb">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<component id="f5de9" class="javax.swing.JComboBox" binding="mySSHExecutableComboBox" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
</grid>
</children>
</grid>
<vspacer id="4c83d">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
</children>
</grid>
</form>
@@ -22,6 +22,7 @@ import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.vcs.VcsException;
import git4idea.GitVcs;
import git4idea.checkout.branches.GitBranchConfigurations;
import git4idea.i18n.GitBundle;
import org.jetbrains.annotations.NotNull;
@@ -57,6 +58,10 @@ public class GitVcsPanel {
* The confirmation checkbox
*/
private JCheckBox myAskBeforeConversionsCheckBox;
/**
* The if selected, the branches widget is enabled in the status bar
*/
private JCheckBox myEnableBranchesWidgetCheckBox;
/**
* The project
*/
@@ -107,6 +112,7 @@ public class GitVcsPanel {
myConvertTextFilesComboBox.setSelectedItem(CRLF_CONVERT_TO_PROJECT);
myGitField.addBrowseFolderListener(GitBundle.getString("find.git.title"), GitBundle.getString("find.git.description"), project,
new FileChooserDescriptor(true, false, false, false, false, false));
myEnableBranchesWidgetCheckBox.setSelected(GitBranchConfigurations.getInstance(myProject).isWidgetEnabled());
}
/**
@@ -148,6 +154,7 @@ public class GitVcsPanel {
mySSHExecutableComboBox.setSelectedItem(settings.isIdeaSsh() ? IDEA_SSH : NATIVE_SSH);
myAskBeforeConversionsCheckBox.setSelected(settings.askBeforeLineSeparatorConversion());
myConvertTextFilesComboBox.setSelectedItem(crlfPolicyItem(settings));
myEnableBranchesWidgetCheckBox.setSelected(GitBranchConfigurations.getInstance(myProject).isWidgetEnabled());
}
/**
@@ -181,7 +188,8 @@ public class GitVcsPanel {
return !settings.getGitExecutable().equals(myGitField.getText()) ||
(settings.isIdeaSsh() != IDEA_SSH.equals(mySSHExecutableComboBox.getSelectedItem())) ||
!crlfPolicyItem(settings).equals(myConvertTextFilesComboBox.getSelectedItem()) ||
settings.askBeforeLineSeparatorConversion() != myAskBeforeConversionsCheckBox.isSelected();
settings.askBeforeLineSeparatorConversion() != myAskBeforeConversionsCheckBox.isSelected() ||
GitBranchConfigurations.getInstance(myProject).isWidgetEnabled() != myEnableBranchesWidgetCheckBox.isSelected();
}
/**
@@ -205,5 +213,6 @@ public class GitVcsPanel {
}
settings.setLineSeparatorsConversion(conversionPolicy);
settings.setAskBeforeLineSeparatorConversion(myAskBeforeConversionsCheckBox.isSelected());
GitBranchConfigurations.getInstance(myProject).setWidgetEnabled(myEnableBranchesWidgetCheckBox.isSelected());
}
}
@@ -53,7 +53,7 @@ public class HgChangesetStatus extends JLabel implements CustomStatusBarWidget {
return "HgChangeSetStatus";
}
public WidgetPresentation getPresentation(@NotNull Type type) {
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return null;
}

Some files were not shown because too many files have changed in this diff Show More