diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArchiveAntCopyInstructionCreator.java b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArchiveAntCopyInstructionCreator.java index 8343e9f86b52..1857676997c0 100644 --- a/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArchiveAntCopyInstructionCreator.java +++ b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArchiveAntCopyInstructionCreator.java @@ -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); + } } diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/DirectoryAntCopyInstructionCreator.java b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/DirectoryAntCopyInstructionCreator.java index ad9c41d49398..0d8d08c4a41a 100644 --- a/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/DirectoryAntCopyInstructionCreator.java +++ b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/DirectoryAntCopyInstructionCreator.java @@ -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; + } } diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarsBuilder.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarsBuilder.java index 7e1a218a04a1..10b387d762ec 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarsBuilder.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarsBuilder.java @@ -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 writtenPaths = new THashSet(); for (Pair 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 nestedJar : jar.getPackedJars()) { @@ -178,9 +185,35 @@ public class JarsBuilder { } } + private void extractFileAndAddToJar(JarOutputStream jarOutputStream, VirtualFile sourceFile, String relativePath, THashSet 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 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 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 { diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactUtil.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactUtil.java index 3d246a41c078..da39774c3698 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactUtil.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactUtil.java @@ -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> 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); } diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerUtil.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerUtil.java new file mode 100644 index 000000000000..f512c68e7a0b --- /dev/null +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerUtil.java @@ -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)))); + } +} diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompilerInstance.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompilerInstance.java index f2746de16b45..46c1ed0bab25 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompilerInstance.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompilerInstance.java @@ -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> changedItems, final Set processedItems, - final Set writtenPaths, final Set deletedJars) { + final @NotNull Set writtenPaths, final Set deletedJars) { final boolean testMode = ApplicationManager.getApplication().isUnitTestMode(); final DeploymentUtil deploymentUtil = DeploymentUtil.getInstance(); @@ -188,20 +186,26 @@ public class ArtifactsCompilerInstance extends CompilerInstance 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 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 artifacts = myContext.getUserData(ArtifactsCompiler.AFFECTED_ARTIFACTS); if (artifacts != null) { diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryCopyPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryCopyPackagingElement.java index 3721180f6a32..77ad2d567313 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryCopyPackagingElement.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryCopyPackagingElement.java @@ -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 diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryElementType.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryElementType.java new file mode 100644 index 000000000000..34be7a6e00bf --- /dev/null +++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryElementType.java @@ -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 { + 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 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 list = new ArrayList(); + 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(); + } +} diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryPackagingElement.java new file mode 100644 index 000000000000..32ccc9396a19 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryPackagingElement.java @@ -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 { + 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 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; + } +} diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileCopyPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileCopyPackagingElement.java index 8835d077beec..6513c765e76e 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileCopyPackagingElement.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileCopyPackagingElement.java @@ -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 diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/PackagingElementFactoryImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/PackagingElementFactoryImpl.java index 1d527a257ddc..be6c0b32993f 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/elements/PackagingElementFactoryImpl.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/PackagingElementFactoryImpl.java @@ -51,11 +51,12 @@ public class PackagingElementFactoryImpl extends PackagingElementFactory { public static final PackagingElementType ARCHIVE_ELEMENT_TYPE = new ArchiveElementType(); public static final PackagingElementType FILE_COPY_ELEMENT_TYPE = new FileCopyElementType(); public static final PackagingElementType DIRECTORY_COPY_ELEMENT_TYPE = new DirectoryCopyElementType(); + public static final PackagingElementType EXTRACTED_DIRECTORY_ELEMENT_TYPE = new ExtractedDirectoryElementType(); public static final PackagingElementType> 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) { diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/ExtractedDirectoryPresentation.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ExtractedDirectoryPresentation.java new file mode 100644 index 000000000000..3649f0cc7ec8 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ExtractedDirectoryPresentation.java @@ -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; + } +} \ No newline at end of file diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/FileCopyPresentation.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/FileCopyPresentation.java index 853f72c87b9d..a48648ddf913 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/ui/FileCopyPresentation.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/FileCopyPresentation.java @@ -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); } diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Include.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Include.java index b0a5cff3c0ba..73352a24d983 100644 --- a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Include.java +++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Include.java @@ -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("name", name)}); + super("include", pair("name", name)); } } diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PatternSet.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PatternSet.java index eee85abbd7ad..6a1d2375e6bb 100644 --- a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PatternSet.java +++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PatternSet.java @@ -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("id", id)}); + super("patternset", pair("id", id)); } } diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Unzip.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Unzip.java new file mode 100644 index 000000000000..8e3a4e7d2369 --- /dev/null +++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Unzip.java @@ -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)); + } +} diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/ZipFileSet.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/ZipFileSet.java index 50991595783b..9970a9d004d5 100644 --- a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/ZipFileSet.java +++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/ZipFileSet.java @@ -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; diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/AntCopyInstructionCreator.java b/java/compiler/openapi/src/com/intellij/packaging/elements/AntCopyInstructionCreator.java index 33dccf956950..0c20bb61c4b8 100644 --- a/java/compiler/openapi/src/com/intellij/packaging/elements/AntCopyInstructionCreator.java +++ b/java/compiler/openapi/src/com/intellij/packaging/elements/AntCopyInstructionCreator.java @@ -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); } diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementFactory.java b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementFactory.java index 88364671f4da..2cbad503ce6a 100644 --- a/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementFactory.java +++ b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementFactory.java @@ -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); diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementWeights.java b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementWeights.java index e36ac1229052..f193e9be3490 100644 --- a/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementWeights.java +++ b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementWeights.java @@ -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; diff --git a/platform/icons/src/nodes/copyOfFolder.png b/platform/icons/src/nodes/copyOfFolder.png index b4c66afd4e86..eaa813b50df2 100644 Binary files a/platform/icons/src/nodes/copyOfFolder.png and b/platform/icons/src/nodes/copyOfFolder.png differ diff --git a/platform/icons/src/nodes/extractedFolder.png b/platform/icons/src/nodes/extractedFolder.png new file mode 100644 index 000000000000..6526ba4f8db2 Binary files /dev/null and b/platform/icons/src/nodes/extractedFolder.png differ diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java index 6ca3e920e21f..b9f571c79a71 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java @@ -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); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java index 96f723a291d6..012b2e419daf 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java @@ -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 { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/IndentedCommenter.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/IndentedCommenter.java new file mode 100644 index 000000000000..5273acf6314d --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/IndentedCommenter.java @@ -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(); + +} diff --git a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java index 719aa3daf73e..aae8a0edaa25 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java @@ -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); } diff --git a/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java b/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java index 2449862dfd81..3d064a03baee 100644 --- a/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java +++ b/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java @@ -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; } diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/InspectionProjectProfileManager.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/InspectionProjectProfileManager.java index e7483e56ede1..6ee071f2a6e3 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/InspectionProjectProfileManager.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/InspectionProjectProfileManager.java @@ -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; diff --git a/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java b/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java index 9037bf52a414..d2697cb7331a 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java @@ -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); diff --git a/platform/platform-api/src/com/intellij/openapi/wm/StatusBarWidget.java b/platform/platform-api/src/com/intellij/openapi/wm/StatusBarWidget.java index 46e8a86b12e0..4a8528808d5c 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/StatusBarWidget.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/StatusBarWidget.java @@ -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); diff --git a/platform/platform-impl/src/com/intellij/diagnostic/IdeMessagePanel.java b/platform/platform-impl/src/com/intellij/diagnostic/IdeMessagePanel.java index cdbe815d8a61..46e40f5d4a9f 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/IdeMessagePanel.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/IdeMessagePanel.java @@ -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; } diff --git a/platform/platform-impl/src/com/intellij/notification/impl/IdeNotificationArea.java b/platform/platform-impl/src/com/intellij/notification/impl/IdeNotificationArea.java index 3760b008a439..ed51be8b0f56 100644 --- a/platform/platform-impl/src/com/intellij/notification/impl/IdeNotificationArea.java +++ b/platform/platform-impl/src/com/intellij/notification/impl/IdeNotificationArea.java @@ -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; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java index f3ac15c6c37b..7728292bd43e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java @@ -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); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java index 88d897c9d3e7..3f309e1987d9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java @@ -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. - *

- * 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 diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java index a12865d77d70..11763b6fb1ce 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java @@ -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 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 [start; end) 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 'x' coordinate that corresponds to the given 'start' offset + * @param currentColumn logical column that corresponds to the given 'start' offset + * @return target offset that belongs to the [start; end) range and points to the target logical + * column if any; -1 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,77 @@ 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; + 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)); + 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 +401,10 @@ public class EditorUtil { if (tabSize <= 0) { tabSize = 1; } + return nextTabStop(x, editor, tabSize); + } + public static int nextTabStop(int x, Editor editor, int tabSize) { tabSize *= getSpaceWidth(Font.PLAIN, editor); int nTabs = x / tabSize; @@ -371,6 +447,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 [start; end) interval * at the given editor. @@ -390,10 +481,10 @@ public class EditorUtil { * from [1; tab size] (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()); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java index 6f4d4290843a..2870ab265133 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java @@ -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; + while (true) { + LogicalPosition logical = myEditor.visualToLogicalPosition(new VisualPosition(visualLine, 0)); + if (logical.line == logicalLine) { + height += myEditor.getLineHeight(); + visualLine++; + } + else { + break; } } + return new VerticalInfo(y, height); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/DefaultEditorTextRepresentationHelper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/DefaultEditorTextRepresentationHelper.java index ca7ef098ee32..a866ba0daa4f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/DefaultEditorTextRepresentationHelper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/DefaultEditorTextRepresentationHelper.java @@ -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); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 122d1f28fcca..5bda19660b94 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -440,7 +440,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } @NotNull - public FoldingModel getFoldingModel() { + public FoldingModelEx getFoldingModel() { return myFoldingModel; } @@ -902,7 +902,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 +983,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 +1054,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; } @@ -1319,6 +1318,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } void paint(Graphics g) { + //TODO den remove + System.out.printf("EditorImpl.paint(): (%d; %d) - [%d; %d]%n", g.getClipBounds().x, g.getClipBounds().y, g.getClipBounds().width, g.getClipBounds().height); startOptimizedScrolling(); if (myCursorUpdater != null) { @@ -2667,10 +2668,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 +2685,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) { @@ -4844,7 +4839,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); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorTextRepresentationHelper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorTextRepresentationHelper.java index da31f72de73f..6e391667b15e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorTextRepresentationHelper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorTextRepresentationHelper.java @@ -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. *

@@ -37,16 +39,28 @@ public interface EditorTextRepresentationHelper { * @param x 'x' 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 'x' 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 'x' 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 ([start; end)) starting + * at the given 'x' offset from visual line start using given font type. + *

+ * Note: target region is allows to contain line feeds, the width is calculated as a difference between 'x' + * 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 'x' 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); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java index 8dc8a3acfb08..3dd07cde9a4b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java @@ -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; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/DefaultSoftWrapApplianceManager.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/DefaultSoftWrapApplianceManager.java index 876d7bd3ea71..9b7a7fdd8ee2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/DefaultSoftWrapApplianceManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/DefaultSoftWrapApplianceManager.java @@ -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: *

@@ -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) {
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java
index 99cd59788f5f..cc965b8ff252 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java
@@ -18,14 +18,11 @@ package com.intellij.openapi.editor.impl.softwrap;
 import com.intellij.openapi.editor.*;
 import com.intellij.openapi.editor.ex.EditorEx;
 import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
-import com.intellij.openapi.editor.impl.IterationState;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.util.text.CharArrayUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.awt.*;
-import java.nio.CharBuffer;
 import java.util.List;
 
 /**
@@ -37,171 +34,44 @@ import java.util.List;
  */
 public class SoftWrapDataMapper {
 
-  private static final VisualPosition  DUMMY_VISUAL  = new VisualPosition(Integer.MAX_VALUE, Integer.MAX_VALUE);
-
-  private final CharBuffer myCharBuffer = CharBuffer.allocate(1);
-
   private final EditorTextRepresentationHelper myTextRepresentationHelper;
   private final EditorEx                       myEditor;
   private final SoftWrapsStorage               myStorage;
-  private final SoftWrapPainter                myPainter;
-  private final FontTypeProvider               myFontTypeProvider;
+  //private final FontTypeProvider               myFontTypeProvider;
+
+  //public SoftWrapDataMapper(EditorEx editor,
+  //                          SoftWrapsStorage storage,
+  //                          EditorTextRepresentationHelper textRepresentationHelper)
+  //{
+  //  this(editor, storage, textRepresentationHelper, new IterationStateFontTypeProvider(editor));
+  //}
 
   public SoftWrapDataMapper(EditorEx editor,
                             SoftWrapsStorage storage,
-                            SoftWrapPainter painter,
-                            EditorTextRepresentationHelper textRepresentationHelper)
-  {
-    this(editor, storage, painter, textRepresentationHelper, new IterationStateFontTypeProvider(editor));
-  }
-
-  SoftWrapDataMapper(EditorEx editor,
-                            SoftWrapsStorage storage,
-                            SoftWrapPainter painter,
-                            EditorTextRepresentationHelper textRepresentationHelper,
-                            FontTypeProvider fontTypeProvider)
+                            EditorTextRepresentationHelper textRepresentationHelper/*,
+                            FontTypeProvider fontTypeProvider*/)
   {
     myEditor = editor;
     myStorage = storage;
-    myPainter = painter;
     myTextRepresentationHelper = textRepresentationHelper;
-    myFontTypeProvider = fontTypeProvider;
+    //myFontTypeProvider = fontTypeProvider;
   }
 
   @NotNull
-  public LogicalPosition adjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual) {
-    try {
-      return doAdjustLogicalPosition(defaultLogical, visual);
-    }
-    finally {
-      myFontTypeProvider.cleanup();
-    }
+  public LogicalPosition visualToLogical(@NotNull VisualPosition visual) {
+    return toLogical(new VisualPositionBasedStrategy(visual));
   }
 
-  @SuppressWarnings({"AssignmentToForLoopParameter"})
   @NotNull
-  private LogicalPosition doAdjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual) {
-    Document document = myEditor.getDocument();
-    int maxOffset = document.getLineEndOffset(Math.min(defaultLogical.line, document.getLineCount() - 1));
+  public LogicalPosition offsetToLogicalPosition(int offset) {
+    OffsetBasedStrategy strategy = new OffsetBasedStrategy(myTextRepresentationHelper, myEditor.getDocument(), offset);
+    return toLogical(strategy);
+  }
 
-    // This index points to registered soft wrap that is guaranteed to be located after the target visual line.
-    int endIndex = myStorage.getSoftWrapIndex(maxOffset + 1);
-    if (endIndex < 0) {
-      endIndex = -endIndex - 1;
-    }
-
-    int softWrapLinesBeforeCurrentLogicalLine = 0;
-    int softWrapLinesOnCurrentLogicalLine = 0;
-    int lastSoftWrapLogicalLine = -1;
-
-    FoldingModel foldingModel = myEditor.getFoldingModel();
-    int i = 0;
-    List softWraps = myStorage.getSoftWraps();
-    int max = Math.min(softWraps.size(), endIndex);
-    for (; i < max; i++) {
-      TextChange softWrap = softWraps.get(i);
-      if (!isVisible(softWrap)) {
-        continue;
-      }
-
-      int currentSoftWrapLineFeeds = StringUtil.countNewLines(softWrap.getText());
-      int softWrapLine = document.getLineNumber(softWrap.getStart());
-      int visualLineBeforeSoftWrapAppliance = myEditor.logicalToVisualPosition(new LogicalPosition(softWrapLine, 0)).line
-                                              + softWrapLinesBeforeCurrentLogicalLine + softWrapLinesOnCurrentLogicalLine;
-      if (visualLineBeforeSoftWrapAppliance > visual.line) {
-        softWrapLinesBeforeCurrentLogicalLine += softWrapLinesOnCurrentLogicalLine;
-        int logicalLine = defaultLogical.line - softWrapLinesBeforeCurrentLogicalLine;
-        return new LogicalPosition(
-          logicalLine, defaultLogical.column, softWrapLinesBeforeCurrentLogicalLine, 0, 0,
-          getFoldedLinesBefore(document.getLineStartOffset(logicalLine)),
-          visual.column - defaultLogical.column
-        );
-      }
-
-      if (lastSoftWrapLogicalLine >= 0 && lastSoftWrapLogicalLine != softWrapLine) {
-        softWrapLinesBeforeCurrentLogicalLine += softWrapLinesOnCurrentLogicalLine;
-        softWrapLinesOnCurrentLogicalLine = 0;
-      }
-      lastSoftWrapLogicalLine = softWrapLine;
-
-      int visualLineAfterSoftWrapAppliance = visualLineBeforeSoftWrapAppliance + currentSoftWrapLineFeeds;
-      if (visualLineAfterSoftWrapAppliance < visual.line) {
-        softWrapLinesOnCurrentLogicalLine += currentSoftWrapLineFeeds;
-        continue;
-      }
-
-      int startLineOffset = document.getLineStartOffset(softWrapLine);
-      int endLineOffset = document.getLineEndOffset(softWrapLine);
-      FoldRegion region = foldingModel.getCollapsedRegionAtOffset(endLineOffset);
-      while (region != null) {
-        int line = document.getLineNumber(region.getEndOffset());
-        endLineOffset = document.getLineEndOffset(line);
-        region = foldingModel.getCollapsedRegionAtOffset(endLineOffset);
-      }
-      CharSequence documentText = document.getCharsSequence();
-
-      // If we're here that means that current soft wrap affects logical line that is matched to the given visual line.
-      // We iterate from the logical line start then in order to calculate resulting logical position.
-      Context context = new Context(
-        visual, softWrapLine, softWrapLinesBeforeCurrentLogicalLine, softWrapLinesOnCurrentLogicalLine,
-        visualLineBeforeSoftWrapAppliance, getFoldedLinesBefore(startLineOffset)
-      );
-      myFontTypeProvider.init(startLineOffset);
-
-      for (int j = startLineOffset; j < endLineOffset; j++) {
-
-        // Process soft wrap at the current offset if any.
-        TextChange softWrapToProcess = myStorage.getSoftWrap(j);
-        if (softWrapToProcess != null && isVisible(softWrapToProcess)) {
-          context.beforeSoftWrap();
-          if (j >= softWrap.getStart()) {
-            CharSequence softWrapText = softWrapToProcess.getText();
-            for (int k = 0; k < softWrapText.length(); k++) {
-              LogicalPosition result = context.onSoftWrapSymbol(softWrapText.charAt(k));
-              if (result != null) {
-                return result;
-              }
-            }
-          }
-          context.afterSoftWrap();
-        }
-
-        context.fontType = myFontTypeProvider.getFontType(j);
-
-        FoldRegion foldRegion = foldingModel.getCollapsedRegionAtOffset(j);
-        if (foldRegion != null) {
-          LogicalPosition result = context.onCollapsedFolding(foldRegion);
-          if (result != null) {
-            return result;
-          }
-          j = foldRegion.getEndOffset();
-        }
-
-        // Process document symbol.
-        LogicalPosition result = context.onNonSoftWrapSymbol(documentText.charAt(j));
-        if (result != null) {
-          return result;
-        }
-      }
-
-      // If we are here that means that target visual position is located at virtual space after the line end.
-      context.logicalColumn += visual.column - context.visualColumn;
-      return context.build();     
-    }
-
-    // If we are here that means that there is no soft wrap on a logical line that corresponds to the target visual line.
-    softWrapLinesBeforeCurrentLogicalLine += softWrapLinesOnCurrentLogicalLine;
-    int logicalLine = defaultLogical.line - softWrapLinesBeforeCurrentLogicalLine;
-    // There is a possible case that we can't count on given default logical position - e.g. if given visual position line
-    // is more than total document lines count.
-    if (logicalLine < 0) {
-      logicalLine = Math.min(lastSoftWrapLogicalLine + 1, document.getLineCount() - 1);
-    }
-    int foldedLines = getFoldedLinesBefore(document.getLineStartOffset(logicalLine));
-    int foldingColumnDiff = visual.column - defaultLogical.column;
-    return new LogicalPosition(
-      logicalLine, defaultLogical.column, softWrapLinesBeforeCurrentLogicalLine, 0, 0, foldedLines, foldingColumnDiff
-    );
+  @NotNull
+  private LogicalPosition toLogical(LogicalPositionCalculatorStrategy strategy) {
+    LogicalPositionCalculator calculator = new LogicalPositionCalculator(strategy);
+    return calculator.calculate();
   }
 
   @NotNull
@@ -263,175 +133,6 @@ public class SoftWrapDataMapper {
     return new VisualPosition(visual.line + lineDiff, columnToUse);
   }
 
-  public LogicalPosition offsetToLogicalPosition(int offset) {
-    try {
-      return doOffsetToLogicalPosition(offset);
-    }
-    finally {
-      myFontTypeProvider.cleanup();
-    }
-  }
-
-  @SuppressWarnings({"AssignmentToForLoopParameter"})
-  private LogicalPosition doOffsetToLogicalPosition(int offset) {
-    FoldingModel foldingModel = myEditor.getFoldingModel();
-    Document document = myEditor.getDocument();
-    CharSequence text = document.getCharsSequence();
-    int line = document.getLineNumber(offset);
-    int lineStartOffset = document.getLineStartOffset(line);
-    FoldRegion region = foldingModel.getCollapsedRegionAtOffset(lineStartOffset);
-    while (region != null && region.getStartOffset() != lineStartOffset) {
-      line = document.getLineNumber(region.getStartOffset());
-      lineStartOffset = document.getLineStartOffset(line);
-      region = foldingModel.getCollapsedRegionAtOffset(lineStartOffset);
-    }
-
-    Context context = new Context(line, getSoftWrapIntroducedLinesBefore(lineStartOffset), getFoldedLinesBefore(lineStartOffset));
-    myFontTypeProvider.init(lineStartOffset);
-    context.fontType = myFontTypeProvider.getFontType(lineStartOffset);
-    for (int i = lineStartOffset; i <= offset; i++) {
-      TextChangeImpl softWrap = myStorage.getSoftWrap(i);
-      if (softWrap != null) {
-        context.beforeSoftWrap();
-        CharSequence softWrapText = softWrap.getText();
-        for (int k = 0; k < softWrapText.length(); k++) {
-          context.onSoftWrapSymbol(softWrapText.charAt(k));
-        }
-        context.afterSoftWrap();
-      }
-
-      if (i == offset) {
-        // We want to count soft wrap that is registered at target offset if any but not exceeding document symbols.
-        break;
-      }
-
-      region = foldingModel.getCollapsedRegionAtOffset(i);
-      if (region != null) {
-        processFoldRegion(context, region, offset);
-        if (offset <= region.getEndOffset()) {
-          break;
-        }
-        i = region.getEndOffset();
-      }
-      context.fontType = myFontTypeProvider.getFontType(i);
-      context.onNonSoftWrapSymbol(text.charAt(i));
-    }
-    return new LogicalPosition(
-      context.logicalLine,
-      context.logicalColumn,
-      context.softWrapLinesBefore,
-      context.targetSoftWrapLines,
-      context.softWrapColumnDiff,
-      getFoldedLinesBefore(offset),
-      context.foldingColumnDiff
-    );
-  }
-
-  /**
-   * Processes given collapsed fold region assuming that we need to stop at a target offset.
-   * 

- * Processing result is updated state of the given context object. - * - * @param context processing data holder - * @param region collapsed fold region to process - * @param offset target stop offset - */ - @SuppressWarnings({"AssignmentToForLoopParameter"}) - private void processFoldRegion(Context context, FoldRegion region, int offset) { - CharSequence text = myEditor.getDocument().getCharsSequence(); - int max = Math.min(offset, region.getEndOffset()); - boolean multilineFolding = false; - for (int i = region.getStartOffset(); i < max;) { - int lineFeedOffset = CharArrayUtil.shiftForwardUntil(text, i, "\n"); - if (lineFeedOffset < max) { - context.softWrapLinesBefore += context.targetSoftWrapLines; - context.targetSoftWrapLines = 0; - context.softWrapColumnDiff = 0; - context.foldedLines++; - context.logicalColumn = 0; - context.foldingColumnDiff = context.visualColumn; - context.logicalLine++; - i = lineFeedOffset + 1; - multilineFolding = true; - } - else { - if (multilineFolding) { - context.logicalColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, i, max, 0); - context.foldingColumnDiff = context.visualColumn - context.logicalColumn; - break; - } - else { - int foldedColumns = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, region.getStartOffset(), max, context.x); - context.logicalColumn += foldedColumns; - context.foldingColumnDiff -= foldedColumns; - if (offset >= region.getEndOffset()) { - context.foldingColumnDiff += region.getPlaceholderText().length(); - } - return; - } - } - } - - if (offset >= region.getEndOffset()) { - int foldPlaceholderColumns = region.getPlaceholderText().length(); - context.visualColumn += foldPlaceholderColumns; - context.foldingColumnDiff += foldPlaceholderColumns; - context.x += foldPlaceholderColumns * myTextRepresentationHelper.charWidth(' ', context.x, Font.PLAIN); - } - } - - /** - * Allows to answer how many soft wrap-introduced visual lines are located before the given offset. - * - * @param offset target offset - * @return number of soft wrap-introduced visual lines are located before the given offset - */ - private int getSoftWrapIntroducedLinesBefore(int offset) { - int result = 0; - List softWraps = myStorage.getSoftWraps(); - - // Calculate number of soft wrap-introduced lines before the line that holds target offset. - int index = myStorage.getSoftWrapIndex(offset); - if (index < 0) { - index = -index - 1; - } - int max = Math.min(index, softWraps.size()); - for (int j = 0; j < max; j++) { - TextChange softWrap = softWraps.get(j); - if (isVisible(softWrap)) { - result += StringUtil.countNewLines(softWrap.getText()); - } - } - return result; - } - - /** - * Allows to answer how many folded lines are located before the logical line that contains given offset. - * - * @param offset target offset - * @return number of folded lines are located before the logical line that contains given offset. - */ - private int getFoldedLinesBefore(int offset) { - Document document = myEditor.getDocument(); - int line = document.getLineNumber(offset); - int lineStartOffset = document.getLineStartOffset(line); - int result = 0; - for (FoldRegion foldRegion : myEditor.getFoldingModel().getAllFoldRegions()) { - if (foldRegion.getStartOffset() >= lineStartOffset) { - break; - } - - if (foldRegion.isExpanded() || !foldRegion.isValid()) { - continue; - } - - int foldingStartLine = document.getLineNumber(foldRegion.getStartOffset()); - int foldingEndLine = document.getLineNumber(foldRegion.getEndOffset()); - result += Math.min(line, foldingEndLine) - foldingStartLine; - } - return result; - } - private boolean isVisible(TextChange softWrap) { FoldingModel foldingModel = myEditor.getFoldingModel(); int start = softWrap.getStart(); @@ -441,219 +142,345 @@ public class SoftWrapDataMapper { return !foldingModel.isOffsetCollapsed(start) || !foldingModel.isOffsetCollapsed(start - 1); } - private int toVisualColumnSymbolsNumber(char c, int x) { - myCharBuffer.clear(); - myCharBuffer.put(c); - myCharBuffer.flip(); - return myTextRepresentationHelper.toVisualColumnSymbolsNumber(myCharBuffer, 0, 1, x); - } - - private class Context { - - public final VisualPosition targetVisualPosition; - public final int visualLineBeforeSoftWrapAppliance; - public final int softWrapLinesOnCurrentLineBeforeTargetSoftWrap; + private static class Context implements Cloneable { public int logicalLine; - public int visualLine; - public int softWrapLinesBefore; - public int targetSoftWrapLines; - public int softWrapColumnDiff; public int logicalColumn; + public int visualLine; public int visualColumn; + public int offset; + public int softWrapLinesBefore; + public int softWrapLinesCurrent; + public int softWrapColumnDiff; public int foldedLines; public int foldingColumnDiff; public int x; - public int fontType; - Context(int logicalLine, int softWrapLinesBefore, int foldedLines) { - this(DUMMY_VISUAL, logicalLine, softWrapLinesBefore, 0, 0, foldedLines); + @NotNull + public LogicalPosition build() { + return new LogicalPosition( + logicalLine, logicalColumn, softWrapLinesBefore, softWrapLinesCurrent, softWrapColumnDiff, foldedLines, foldingColumnDiff + ); } - Context(VisualPosition targetVisualPosition, int logicalLine, int softWrapLinesBefore, - int softWrapLinesOnCurrentLineBeforeTargetSoftWrap, int visualLineBeforeSoftWrapAppliance, int foldedLines) - { - this.targetVisualPosition = targetVisualPosition; - this.softWrapLinesBefore = softWrapLinesBefore; - this.softWrapLinesOnCurrentLineBeforeTargetSoftWrap = softWrapLinesOnCurrentLineBeforeTargetSoftWrap; - this.visualLineBeforeSoftWrapAppliance = visualLineBeforeSoftWrapAppliance; - this.foldedLines = foldedLines; - this.logicalLine = logicalLine; - visualLine = visualLineBeforeSoftWrapAppliance + targetSoftWrapLines; + @Override + protected Context clone() { + Context result = new Context(); + result.logicalLine = logicalLine; + result.logicalColumn = logicalColumn; + result.visualLine = visualLine; + result.visualColumn = visualColumn; + result.offset = offset; + result.softWrapLinesBefore = softWrapLinesBefore; + result.softWrapLinesCurrent = softWrapLinesCurrent; + result.softWrapColumnDiff = softWrapColumnDiff; + result.foldedLines = foldedLines; + result.foldingColumnDiff = foldingColumnDiff; + result.x = x; + return result; } - /** - * Updates current context within the soft wrap symbol. - * - * @param c soft wrap symbol to process - * @return logical position that matches target visual position if given symbol processing makes it possible to calculate it; - * null otherwise - */ - @Nullable - public LogicalPosition onSoftWrapSymbol(char c) { - // Process line feed inside soft wrap. - if (c == '\n') { - if (targetVisualPosition.line == visualLineBeforeSoftWrapAppliance + targetSoftWrapLines) { - softWrapColumnDiff = targetVisualPosition.column - logicalColumn; - return build(); + private void onNewLine() { + softWrapLinesBefore += softWrapLinesCurrent; + softWrapLinesCurrent = 0; + softWrapColumnDiff = 0; + foldingColumnDiff = 0; + } + } + + private class LogicalPositionCalculator { + + public final LogicalPositionCalculatorStrategy strategy; + + public Context context = new Context(); + + LogicalPositionCalculator(LogicalPositionCalculatorStrategy strategy) { + this.strategy = strategy; + } + + @NotNull + public LogicalPosition calculate() { + FoldingProvider foldRegions = new FoldingProvider(); + SoftWrapsProvider softWraps = new SoftWrapsProvider(); + + FoldRegion foldRegion = foldRegions.get(); + TextChange softWrap = softWraps.get(); + + LogicalPosition result = null; + while (true) { + if (foldRegion == null && softWrap == null || strategy.exceeds(context)) { + return strategy.build(context); } - else { - x = 0; - softWrapColumnDiff = -logicalColumn; - targetSoftWrapLines++; - visualLine++; - visualColumn = 0; - return null; - } - } - softWrapColumnDiff++; - - // Just update information about tracked symbols number if current visual line is too low. - if (targetVisualPosition.line > visualLineBeforeSoftWrapAppliance + targetSoftWrapLines) { - visualColumn += toVisualColumnSymbolsNumber(c, x); - x += myTextRepresentationHelper.charWidth(c, x, fontType); - return null; - } - - // There is a possible case that, for example, target visual column is zero and it points to the soft-wrapped line, - // i.e. soft wrap are. We shouldn't count symbols then. Hence, we perform this preliminary examination with eager - // return if necessary. - if (targetVisualPosition.column <= visualColumn) { - return build(); - } - - // Process non-line feed inside soft wrap. - visualColumn++; // Don't expect tabulation to be used inside soft wrap text. - x += myTextRepresentationHelper.charWidth(c, x, fontType); - - if (targetVisualPosition.column <= visualColumn) { - return build(); - } - else { - return null; - } - } - - public void beforeSoftWrap() { - x = 0; - } - - public void afterSoftWrap() { - x += myPainter.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP); - visualColumn++; - softWrapColumnDiff++; - } - - @SuppressWarnings({"AssignmentToForLoopParameter"}) - @Nullable - public LogicalPosition onCollapsedFolding(FoldRegion region) { - int visualFoldingPlaceholderWidth = region.getPlaceholderText().length(); // Assuming that no tabs are used as placeholder - - // Process situation when target visual position points to collapsed folding placeholder. - if (visualLine == targetVisualPosition.line && visualColumn + visualFoldingPlaceholderWidth > targetVisualPosition.column) { - return build(); - } - - // If control flow reaches this point that means that we should process whole folded region and update current object state. - CharSequence text = myEditor.getDocument().getCharsSequence(); - boolean multiline = false; - for (int i = region.getStartOffset(); i < region.getEndOffset();) { - int lineFeedOffset = CharArrayUtil.shiftForwardUntil(text, i, "\n"); - // Process multiline folded text. - if (lineFeedOffset < region.getEndOffset()) { - logicalLine++; - foldedLines++; - logicalColumn = 0; - softWrapLinesBefore += targetSoftWrapLines; - targetSoftWrapLines = 0; - softWrapColumnDiff = 0; - i = lineFeedOffset + 1; - multiline = true; - } - else { - if (multiline) { - logicalColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, i, region.getEndOffset(), 0); + if (foldRegion != null && softWrap != null) { + if (softWrap.getStart() <= foldRegion.getStartOffset()) { + result = process(softWrap); + softWrap = softWraps.get(); } else { - logicalColumn += myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, i, region.getEndOffset(), x); + result = process(foldRegion); + foldRegion = foldRegions.get(); } - foldingColumnDiff = visualColumn + visualFoldingPlaceholderWidth - logicalColumn - softWrapColumnDiff; - i = region.getEndOffset(); + } + else { + if (foldRegion != null) { + result = process(foldRegion); + foldRegion = foldRegions.get(); + } + if (softWrap != null) { + result = process(softWrap); + softWrap = softWraps.get(); + } + } + if (result != null) { + return result; } } - - visualColumn += visualFoldingPlaceholderWidth; - x += visualFoldingPlaceholderWidth * myTextRepresentationHelper.charWidth(' ', x, Font.PLAIN); - if (visualLine == targetVisualPosition.line && visualColumn == targetVisualPosition.column) { - return build(); - } - return null; } - /** - * Updates current context within the non-soft wrap symbol. - * - * @param c soft wrap symbol to process - * @return logical position that matches target visual position if given symbol processing makes it possible to calculate it; - * null otherwise - */ @Nullable - public LogicalPosition onNonSoftWrapSymbol(char c) { - // Don't expect line feed symbol to be delivered to this method in assumption that we process only one logical line here. - if (c == '\n') { - x = 0; - assert false; + private LogicalPosition process(@NotNull FoldRegion region) { + if (region.getStartOffset() > context.offset) { + Context newContext = advanceToOffset(region.getStartOffset()); + if (strategy.exceeds(newContext)) { + return strategy.build(context); + } + context = newContext; + } + + Document document = myEditor.getDocument(); + CharSequence text = document.getCharsSequence(); + int foldingStartLine = document.getLineNumber(region.getStartOffset()); + + Context afterFolding = context.clone(); + afterFolding.logicalLine += document.getLineNumber(region.getEndOffset()) - foldingStartLine; + int visualColumnInc = region.getPlaceholderText().length(); // Assuming that no tabulations are used at placeholder. + afterFolding.visualColumn += visualColumnInc; + + int i = CharArrayUtil.shiftBackwardUntil(text, region.getEndOffset() - 1, "\n"); + // Process multi-line folding. + if (i >= region.getStartOffset()) { + int width = myTextRepresentationHelper.textWidth(text, i + 1, region.getEndOffset(), 0); + afterFolding.logicalColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(width); + afterFolding.x = width; + afterFolding.softWrapLinesBefore += afterFolding.softWrapLinesCurrent; + afterFolding.softWrapLinesCurrent = 0; + afterFolding.softWrapColumnDiff = 0; + afterFolding.foldedLines += document.getLineNumber(region.getEndOffset()) - foldingStartLine; + afterFolding.foldingColumnDiff = afterFolding.visualColumn - afterFolding.logicalColumn; + } + // Process single-line folding + else { + int width = myTextRepresentationHelper.textWidth(text, region.getStartOffset(), region.getEndOffset(), context.x); + int logicalColumnInc = myTextRepresentationHelper.toVisualColumnSymbolsNumber(width); + afterFolding.logicalColumn += logicalColumnInc; + afterFolding.x += width; + afterFolding.foldingColumnDiff += visualColumnInc - logicalColumnInc; + } + afterFolding.offset = region.getEndOffset(); + + if (!strategy.exceeds(afterFolding)) { + context = afterFolding; return null; } - // Just update information about tracked symbols number if current visual line is too low. - if (targetVisualPosition.line > visualLine) { - int columnsForSymbol = toVisualColumnSymbolsNumber(c, x); - visualColumn += columnsForSymbol; - logicalColumn += columnsForSymbol; - x += myTextRepresentationHelper.charWidth(c, x, fontType); - return null; - } + return strategy.build(context, region); + } - // There is a possible case that, for example, target visual column is zero. We shouldn't count symbols then. - // Hence, we perform this preliminary examination with eager return if necessary. - if (targetVisualPosition.column <= visualColumn) { - return build(); + @Nullable + private LogicalPosition process(@NotNull TextChange softWrap) { + Context newContext = advanceToOffset(softWrap.getStart()); + if (strategy.exceeds(newContext)) { + return strategy.build(context); } + Document document = myEditor.getDocument(); + int lastUsedLogicalLine = document.getLineNumber(context.offset); + context = newContext; - int columnsForSymbol = toVisualColumnSymbolsNumber(c, x); - int diffInColumns = targetVisualPosition.column - visualColumn; - int incrementToUse = columnsForSymbol; - if (columnsForSymbol >= diffInColumns) { - incrementToUse = Math.min(columnsForSymbol, diffInColumns); - } - visualColumn += incrementToUse; - logicalColumn += incrementToUse; - x += myTextRepresentationHelper.charWidth(c, x, fontType); - - if (targetVisualPosition.column <= visualColumn) { - return build(); + // Create context that points to the soft wrap end visual position. + Context afterSoftWrap = context.clone(); + int lineFeeds = StringUtil.countNewLines(softWrap.getText()); + afterSoftWrap.visualLine += lineFeeds; + afterSoftWrap.visualColumn = myEditor.getSoftWrapModel().getSoftWrapIndentWidthInColumns(softWrap); + afterSoftWrap.x = myEditor.getSoftWrapModel().getSoftWrapIndentWidthInPixels(softWrap); + if (lastUsedLogicalLine == context.logicalLine) { + afterSoftWrap.softWrapLinesCurrent += lineFeeds; } else { + afterSoftWrap.softWrapLinesBefore += context.softWrapLinesCurrent; + afterSoftWrap.softWrapLinesCurrent = lineFeeds; + } + afterSoftWrap.softWrapColumnDiff = afterSoftWrap.visualColumn - afterSoftWrap.logicalColumn; + afterSoftWrap.foldingColumnDiff = 0; + + if (!strategy.exceeds(afterSoftWrap)) { + context = afterSoftWrap; return null; } + return strategy.build(context, softWrap); } - private LogicalPosition build() { - return build(foldingColumnDiff); + private Context advanceToOffset(int newOffset) { + Context result = context.clone(); + if (result.offset == newOffset) { + return result; + } + + Document document = myEditor.getDocument(); + CharSequence text = document.getCharsSequence(); + int lastUsedLogicalLine = document.getLineNumber(context.offset); + int currentLogicalLine = document.getLineNumber(newOffset); + + // Update state to the offset that corresponds to the same logical line that was used last time. + if (currentLogicalLine == lastUsedLogicalLine) { + int width = myTextRepresentationHelper.textWidth(text, result.offset, newOffset, result.x); + int columnDiff = myTextRepresentationHelper.toVisualColumnSymbolsNumber(width); + result.x += width; + result.logicalColumn += columnDiff; + result.visualColumn += columnDiff; + } + // Update state to the start of the folding that doesn't belong to the same logical line that was used last time. + else { + int lineDiff = currentLogicalLine - lastUsedLogicalLine; + result.logicalLine += lineDiff; + result.visualLine += lineDiff; + int startLineOffset = document.getLineStartOffset(currentLogicalLine); + result.x = myTextRepresentationHelper.textWidth(text, startLineOffset, newOffset, result.x); + result.visualColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(result.x); + result.logicalColumn = result.visualColumn; + result.onNewLine(); + } + result.offset = newOffset; + return result; + } + } + + private interface LogicalPositionCalculatorStrategy { + boolean exceeds(Context context); + @NotNull LogicalPosition build(Context context); + @NotNull LogicalPosition build(Context context, FoldRegion region); + @NotNull LogicalPosition build(Context context, TextChange softWrap); + } + + private static class VisualPositionBasedStrategy implements LogicalPositionCalculatorStrategy { + + private final VisualPosition myTargetVisual; + + VisualPositionBasedStrategy(VisualPosition visual) { + myTargetVisual = visual; } - private LogicalPosition build(int foldingColumnDiff) { - return new LogicalPosition( - logicalLine, - logicalColumn, - softWrapLinesBefore, - softWrapLinesOnCurrentLineBeforeTargetSoftWrap + targetSoftWrapLines, - softWrapColumnDiff, - foldedLines, - foldingColumnDiff - ); + @Override + public boolean exceeds(Context context) { + return context.visualLine > myTargetVisual.line + || (context.visualLine == myTargetVisual.line && context.visualColumn > myTargetVisual.column); + } + + @NotNull + @Override + public LogicalPosition build(Context context) { + if (context.visualLine == myTargetVisual.line) { + context.logicalColumn += myTargetVisual.column - context.visualColumn; + return context.build(); + } + context.logicalLine += myTargetVisual.line - context.visualLine; + context.logicalColumn = myTargetVisual.column; + context.onNewLine(); + return context.build(); + } + + @NotNull + @Override + public LogicalPosition build(Context context, FoldRegion region) { + // We just point to the logical position of folding region start if visual position points to collapsed fold region placeholder. + return context.build(); + } + + @NotNull + @Override + public LogicalPosition build(Context context, TextChange softWrap) { + if (myTargetVisual.line == context.visualLine) { + context.softWrapColumnDiff = myTargetVisual.column - context.logicalColumn - context.foldingColumnDiff; + } + else { + context.foldingColumnDiff = 0; + context.softWrapLinesCurrent += myTargetVisual.line - context.visualLine; + context.softWrapColumnDiff = myTargetVisual.column - context.logicalColumn; + } + return context.build(); + } + } + + private static class OffsetBasedStrategy implements LogicalPositionCalculatorStrategy { + + private final EditorTextRepresentationHelper myRepresentationHelper; + private final Document myDocument; + + private final int myOffset; + + OffsetBasedStrategy(EditorTextRepresentationHelper representationHelper, Document document, int offset) { + myRepresentationHelper = representationHelper; + myDocument = document; + myOffset = offset; + } + + @Override + public boolean exceeds(Context context) { + return context.offset > myOffset; + } + + @NotNull + @Override + public LogicalPosition build(Context context) { + int targetLogicalLine = myDocument.getLineNumber(myOffset); + if (targetLogicalLine == context.logicalLine) { + context.logicalColumn + += myRepresentationHelper.toVisualColumnSymbolsNumber(myDocument.getCharsSequence(), context.offset, myOffset, context.x); + return context.build(); + } + context.logicalLine = targetLogicalLine; + int i = CharArrayUtil.shiftBackwardUntil(myDocument.getCharsSequence(), myOffset - 1, "\n"); + if (i >= context.offset) { + context.logicalColumn = myRepresentationHelper.toVisualColumnSymbolsNumber(myDocument.getCharsSequence(), i + 1, myOffset, 0); + } + else { + context.logicalColumn + = myRepresentationHelper.toVisualColumnSymbolsNumber(myDocument.getCharsSequence(), context.offset, myOffset, context.x); + } + context.onNewLine(); + return context.build(); + } + + @NotNull + @Override + public LogicalPosition build(Context context, FoldRegion region) { + // We want to return logical position that corresponds to the visual start of the given folding region. + int startLine = myDocument.getLineNumber(region.getStartOffset()); + int endLine = myDocument.getLineNumber(myOffset); + int lineFeeds = endLine - startLine; + + if (lineFeeds > 0) { + context.logicalLine += lineFeeds; + context.foldedLines += lineFeeds; + context.onNewLine(); + int i = CharArrayUtil.shiftBackwardUntil(myDocument.getCharsSequence(), myOffset - 1, "\n"); + context.logicalColumn = myRepresentationHelper.toVisualColumnSymbolsNumber(myDocument.getCharsSequence(), i + 1, myOffset, 0); + context.foldingColumnDiff = context.visualColumn - context.logicalColumn; + } + else { + int logicalColumns + = myRepresentationHelper.toVisualColumnSymbolsNumber(myDocument.getCharsSequence(), region.getStartOffset(), myOffset, context.x); + context.logicalColumn += logicalColumns; + context.foldingColumnDiff -= logicalColumns; + } + return context.build(); + } + + @NotNull + @Override + public LogicalPosition build(Context context, TextChange softWrap) { + assert false; // Don't expect soft wrap do affect offset-based mapping request. + return new LogicalPosition(0, 0); } } @@ -662,41 +489,89 @@ public class SoftWrapDataMapper { *

* It's primary purpose is to relief unit testing. */ - interface FontTypeProvider { - void init(int start); - int getFontType(int offset); - void cleanup(); + //interface FontTypeProvider { + // void init(int start); + // int getFontType(int offset); + // void cleanup(); + //} + + //private static class IterationStateFontTypeProvider implements FontTypeProvider { + // + // private final EditorEx myEditor; + // + // private IterationState myState; + // private int myFontType; + // + // private IterationStateFontTypeProvider(EditorEx editor) { + // myEditor = editor; + // } + // + // @Override + // public void init(int start) { + // myState = new IterationState(myEditor, start, false); + // myFontType = myState.getMergedAttributes().getFontType(); + // } + // + // @Override + // public int getFontType(int offset) { + // if (offset >= myState.getEndOffset()) { + // myState.advance(); + // myFontType = myState.getMergedAttributes().getFontType(); + // } + // return myFontType; + // } + // + // @Override + // public void cleanup() { + // myState = null; + // } + //} + + private class SoftWrapsProvider { + + private final List mySoftWraps; + private int myIndex; + + SoftWrapsProvider() { + mySoftWraps = myStorage.getSoftWraps(); + } + + @Nullable + public TextChange get() { + if (myIndex < 0 || myIndex >= mySoftWraps.size()) { + return null; + } + TextChange result = mySoftWraps.get(myIndex++); + if (isVisible(result)) { + return result; + } + else { + return get(); + } + } } - private static class IterationStateFontTypeProvider implements FontTypeProvider { + private class FoldingProvider { - private final EditorEx myEditor; + private final FoldRegion[] myFoldRegions; + private int myIndex; - private IterationState myState; - private int myFontType; - - private IterationStateFontTypeProvider(EditorEx editor) { - myEditor = editor; + FoldingProvider() { + myFoldRegions = myEditor.getFoldingModel().fetchTopLevel(); } - @Override - public void init(int start) { - myState = new IterationState(myEditor, start, false); - myFontType = myState.getMergedAttributes().getFontType(); - } - - @Override - public int getFontType(int offset) { - if (offset >= myState.getEndOffset()) { - myState.advance(); - myFontType = myState.getMergedAttributes().getFontType(); + @Nullable + public FoldRegion get() { + if (myFoldRegions == null || myIndex < 0 || myIndex >= myFoldRegions.length) { + return null; + } + FoldRegion result = myFoldRegions[myIndex++]; + if (result.isExpanded()) { + return get(); + } + else { + return result; } - return myFontType; - } - - @Override - public void cleanup() { - myState = null; } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java index ae59c2a7beff..e1440cf46d22 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java @@ -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; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java index 7de4893f367a..5035c39d2b00 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EncodingPanel.java @@ -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; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java index 55506b23d6dc..64d085a2ca1c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java @@ -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 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 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 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; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java index 5c3f01c4c1a6..cfa169289027 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InfoAndProgressPanel.java @@ -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; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InsertOverwritePanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InsertOverwritePanel.java index 1489302b1356..a5aa101cfd54 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InsertOverwritePanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/InsertOverwritePanel.java @@ -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; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java index 14a87704e865..2bfba9d2f5a4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java @@ -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; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java index 0deea4849100..01bcf900f2f0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java @@ -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; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToggleReadOnlyAttributePanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToggleReadOnlyAttributePanel.java index 6e905a35fc5f..8e8ad69ac2d4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToggleReadOnlyAttributePanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToggleReadOnlyAttributePanel.java @@ -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; } diff --git a/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java index b65c3c88755c..39b69460cdaa 100644 --- a/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java +++ b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java @@ -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], \n" + - " data[2], data[3], \n" + - " data[4], data[5], \n" + - " data[6], data[7], \n" + - " data[8], data[9], \n" + - " data[10], data[11], \n" + - " data[12], data[13]); \n" + + " bar(data[0], \n" + + " data[1] \n" + + " data[2] \n" + + " data[3], \n" + + " data[4], \n" + + " data[5] \n" + + " 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() { + // } + //} } diff --git a/platform/platform-resources-en/src/messages/UIBundle.properties b/platform/platform-resources-en/src/messages/UIBundle.properties index bff0b3397edc..deef01709675 100644 --- a/platform/platform-resources-en/src/messages/UIBundle.properties +++ b/platform/platform-resources-en/src/messages/UIBundle.properties @@ -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. diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java index 7e46e475477d..7c7f932a8785 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java @@ -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 { diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java b/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java index 889584fa9745..cb7863226a23 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java @@ -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; diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/junit-noframes.xsl b/platform/testRunner/src/com/intellij/execution/testframework/export/junit-noframes.xsl new file mode 100644 index 000000000000..f0ab96416c70 --- /dev/null +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/junit-noframes.xsl @@ -0,0 +1,467 @@ + + + + + + +Unit Test Results. + + + + + + <xsl:value-of select="$TITLE"/> + + + + + + + + + + +


+ + + +
+ + + +
+ + + + + + + + + + + + + + + +

Packages

+ Note: package statistics are not computed recursively, they only sum up all of its testsuites numbers. + + + + + + + + + + + + + + + + + Failure + Error + + + + + + + + + + + +
+ + + +
+
+ + + + + + + + + + + + +

Package

+ + + + + + +
+ Back to top +

+

+ + + + + + + + +

TestCase

+ + + + + + + + + + +
+ +

+ + Back to top + + + + +

Summary

+ + + + + + + + + + + + + + + + + Failure + Error + + + + + + + + + +
TestsFailuresErrorsSuccess rateTime
+ + + + + + + +
+ + + + +
+ Note: failures are anticipated and checked for with assertions while errors are unanticipated. +
+
+ + + + cur = TestCases['.'] = new Array(); + + + cur[''] = ''; + + + + + +

+ + + + + +
Designed for use with JUnit and Ant.
+
+
+ + + + Name + Tests + Errors + Failures + Time(s) + + + + + + + Name + Tests + Errors + Failures + Time(s) + Time Stamp + Host + + + + + + + Name + Status + Type + Time(s) + + + + + + + + + + + Failure + Error + + + + + + + + + + + + + + + + + + + + + + + Error + + + + + + Failure + + + + Error + + + + Success + + + + + + + + + + + + + + + + + + + + + + + + N/A + + + + + + +

+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platform/testRunner/testRunner.iml b/platform/testRunner/testRunner.iml index 01430add824a..5c2b923541d0 100644 --- a/platform/testRunner/testRunner.iml +++ b/platform/testRunner/testRunner.iml @@ -11,7 +11,6 @@ - diff --git a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java index 69ab692e7607..24dcca73eddc 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java @@ -510,7 +510,7 @@ public class FileUtil { return parentFile.exists() && parentFile.isDirectory() || parentFile.mkdirs(); } } - return false; + return true; } public static boolean createIfDoesntExist(File file) { diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index fe4299e6e766..119a720c0803 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -1236,6 +1236,20 @@ public class StringUtil { return i; } + /** + * Allows to answer if target symbol is contained at given char sequence at [start; end) 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 true if given symbol is contained at the target range of the given char sequence; + * false 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()); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/IncomingChangesIndicator.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/IncomingChangesIndicator.java index c6d728b745b7..26cd7b3980eb 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/IncomingChangesIndicator.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/IncomingChangesIndicator.java @@ -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; } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/resources/IOResourceInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/resources/IOResourceInspection.java index 8dfa5c619ffa..e7a1520bcba0 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/resources/IOResourceInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/resources/IOResourceInspection.java @@ -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); } diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index e4fffdff6da5..4f69c496b5c5 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -207,6 +207,11 @@ public class GitVcs extends AbstractVcs { * 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 { */ @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 { */ @Override protected void deactivate() { + isActivated = false; GitBranchConfigurations.getInstance(myProject).deactivate(); if (myRootTracker != null) { myRootTracker.dispose(); @@ -768,4 +775,11 @@ public class GitVcs extends AbstractVcs { 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; + } } diff --git a/plugins/git4idea/src/git4idea/checkin/GitPushActiveBranchesDialog.java b/plugins/git4idea/src/git4idea/checkin/GitPushActiveBranchesDialog.java index f6176c9878ac..a92a8336da29 100644 --- a/plugins/git4idea/src/git4idea/checkin/GitPushActiveBranchesDialog.java +++ b/plugins/git4idea/src/git4idea/checkin/GitPushActiveBranchesDialog.java @@ -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) { diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurations.java b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurations.java index 35cdcb832a12..ef2bc44d9d85 100644 --- a/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurations.java +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurations.java @@ -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 cs = new ArrayList(myConfigurations.size()); for (GitBranchConfiguration ci : myConfigurations.values()) { @@ -363,9 +389,54 @@ public class GitBranchConfigurations implements PersistentStateComponent
- + @@ -10,107 +10,150 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + + + - + - + + + + + + - + - - - - - - - - - - + - + - + + + + + + + + + + + + + + + + + + + + - + - + - + - + + + + + + - + + - + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/git4idea/src/git4idea/config/GitVcsPanel.java b/plugins/git4idea/src/git4idea/config/GitVcsPanel.java index 128a0d4cb5be..8e5bbf8e5b57 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsPanel.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsPanel.java @@ -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()); } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgChangesetStatus.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgChangesetStatus.java index 4cf12657c2db..41f52870824c 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgChangesetStatus.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgChangesetStatus.java @@ -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; } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCurrentBranchStatus.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCurrentBranchStatus.java index dd3e6c612568..e787d7604ada 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCurrentBranchStatus.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCurrentBranchStatus.java @@ -62,7 +62,7 @@ public class HgCurrentBranchStatus extends JLabel implements CustomStatusBarWidg return "HgCurrentBranchStatus"; } - public WidgetPresentation getPresentation(@NotNull Type type) { + public WidgetPresentation getPresentation(@NotNull PlatformType type) { return null; }