mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
# IntelliJ IDEA Community Edition
|
||||
### Building and Running from the IDE
|
||||
To develop IntelliJ IDEA, you can use either IntelliJ IDEA Community Edition or IntelliJ IDEA Ultimate. To build and run the code:
|
||||
* Run **getPlugins.sh** / **getPlugins.bat** from the project root directory to check out additional modules.
|
||||
* Make sure you have the **Groovy** plugin enabled. Parts of IntelliJ IDEA are written in Groovy, and you will get compilation errors if you don't have the plugin enabled.
|
||||
* Make sure you have the **UI Designer** plugin enabled. Most of IntelliJ IDEA's UI is built using the **UI Designer**, and the version you build will not run correctly if you don't have the plugin enabled.
|
||||
* Open the project
|
||||
* Configure a JSDK named "**IDEA jdk**" (case sensitive), pointing to an installation of JDK 1.6
|
||||
* Unless you're running on a Mac with an Apple JDK, add <JDK_HOME>/lib/tools.jar to the set of "**IDEA jdk**" jars.
|
||||
* Use Build | Make Project to build the code.
|
||||
* To run the code, use the provided shared run configuration "**IDEA**".
|
||||
|
||||
You can find other useful information at [http://www.jetbrains.org](http://www.jetbrains.org)
|
||||
@@ -248,6 +248,7 @@ binding.setVariable("commonJvmArgsForTests", {
|
||||
if (debugPort != null) jdwp += ",address=$debugPort"
|
||||
return [
|
||||
"-ea",
|
||||
"-Dio.netty.leakDetectionLevel=PARANOID",
|
||||
"-server",
|
||||
"-Xbootclasspath/p:${projectBuilder.moduleOutput(findModule("boot"))}",
|
||||
"-XX:+HeapDumpOnOutOfMemoryError",
|
||||
|
||||
+114
-3
@@ -15,18 +15,30 @@
|
||||
*/
|
||||
package com.intellij.openapi.roots.ui.configuration;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.CustomShortcutSet;
|
||||
import com.intellij.openapi.project.ProjectBundle;
|
||||
import com.intellij.openapi.roots.SourceFolder;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.roots.IconActionComponent;
|
||||
import com.intellij.util.ui.FormBuilder;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.model.JpsDummyElement;
|
||||
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
|
||||
import org.jetbrains.jps.model.java.JavaResourceRootProperties;
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* @author nik
|
||||
*/
|
||||
public abstract class JavaResourceRootEditHandlerBase extends ModuleSourceRootEditHandler<JpsDummyElement> {
|
||||
protected JavaResourceRootEditHandlerBase(JpsModuleSourceRootType<JpsDummyElement> rootType) {
|
||||
public abstract class JavaResourceRootEditHandlerBase extends ModuleSourceRootEditHandler<JavaResourceRootProperties> {
|
||||
public JavaResourceRootEditHandlerBase(JpsModuleSourceRootType<JavaResourceRootProperties> rootType) {
|
||||
super(rootType);
|
||||
}
|
||||
|
||||
@@ -41,4 +53,103 @@ public abstract class JavaResourceRootEditHandlerBase extends ModuleSourceRootEd
|
||||
public CustomShortcutSet getMarkRootShortcutSet() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Icon getRootIcon(@NotNull JavaResourceRootProperties properties) {
|
||||
return properties.isForGeneratedSources() ? getGeneratedRootIcon() : getRootIcon();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Icon getGeneratedRootIcon() {
|
||||
return getRootIcon();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getPropertiesString(@NotNull JavaResourceRootProperties properties) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
if (properties.isForGeneratedSources()) {
|
||||
buffer.append(" [generated]");
|
||||
}
|
||||
String relativeOutputPath = properties.getRelativeOutputPath();
|
||||
if (!relativeOutputPath.isEmpty()) {
|
||||
buffer.append(" (").append(relativeOutputPath).append(")");
|
||||
}
|
||||
return buffer.length() > 0 ? buffer.toString() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createPropertiesEditor(@NotNull final SourceFolder folder,
|
||||
@NotNull final JComponent parentComponent,
|
||||
@NotNull final ContentRootPanel.ActionCallback callback) {
|
||||
final IconActionComponent iconComponent = new IconActionComponent(AllIcons.Modules.SetPackagePrefix,
|
||||
AllIcons.Modules.SetPackagePrefixRollover,
|
||||
ProjectBundle.message("module.paths.edit.properties.tooltip"),
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
JavaResourceRootProperties properties = folder.getJpsElement().getProperties( JavaModuleSourceRootTypes.RESOURCES);
|
||||
assert properties != null;
|
||||
ResourceRootPropertiesDialog
|
||||
dialog = new ResourceRootPropertiesDialog(parentComponent, properties);
|
||||
if (dialog.showAndGet()) {
|
||||
callback.onSourceRootPropertiesChanged(folder);
|
||||
}
|
||||
}
|
||||
});
|
||||
final JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setOpaque(false);
|
||||
panel.add(iconComponent, BorderLayout.CENTER);
|
||||
panel.add(Box.createHorizontalStrut(3), BorderLayout.EAST);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private static class ResourceRootPropertiesDialog extends DialogWrapper {
|
||||
private final JTextField myRelativeOutputPathField;
|
||||
private final JCheckBox myIsGeneratedCheckBox;
|
||||
private final JPanel myMainPanel;
|
||||
@NotNull private final JavaResourceRootProperties myProperties;
|
||||
|
||||
private ResourceRootPropertiesDialog(@NotNull JComponent parentComponent, @NotNull JavaResourceRootProperties properties) {
|
||||
super(parentComponent, true);
|
||||
myProperties = properties;
|
||||
setTitle(ProjectBundle.message("module.paths.edit.properties.title"));
|
||||
myRelativeOutputPathField = new JTextField();
|
||||
myIsGeneratedCheckBox = new JCheckBox(UIUtil.replaceMnemonicAmpersand("For &generated resources"));
|
||||
myMainPanel = FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent("Relative output &path:", myRelativeOutputPathField)
|
||||
.addComponent(myIsGeneratedCheckBox)
|
||||
.getPanel();
|
||||
myRelativeOutputPathField.setText(myProperties.getRelativeOutputPath());
|
||||
myRelativeOutputPathField.setColumns(25);
|
||||
myIsGeneratedCheckBox.setSelected(myProperties.isForGeneratedSources());
|
||||
init();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myRelativeOutputPathField;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
myProperties.setRelativeOutputPath(normalizePath(myRelativeOutputPathField.getText()));
|
||||
myProperties.setForGeneratedSources(myIsGeneratedCheckBox.isSelected());
|
||||
super.doOKAction();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String normalizePath(String path) {
|
||||
return StringUtil.trimEnd(StringUtil.trimStart(FileUtil.toSystemIndependentName(path.trim()), "/"), "/");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return myMainPanel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
|
||||
import org.jetbrains.jps.model.java.JavaResourceRootProperties;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootProperties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -51,7 +52,8 @@ public class JavaProjectRootsUtil {
|
||||
|
||||
private static boolean isForGeneratedSources(SourceFolder sourceFolder) {
|
||||
JavaSourceRootProperties properties = sourceFolder.getJpsElement().getProperties(JavaModuleSourceRootTypes.SOURCES);
|
||||
return properties != null && properties.isForGeneratedSources();
|
||||
JavaResourceRootProperties resourceProperties = sourceFolder.getJpsElement().getProperties(JavaModuleSourceRootTypes.RESOURCES);
|
||||
return properties != null && properties.isForGeneratedSources() || resourceProperties != null && resourceProperties.isForGeneratedSources();
|
||||
}
|
||||
|
||||
public static boolean isInGeneratedCode(@NotNull VirtualFile file, @NotNull Project project) {
|
||||
|
||||
@@ -241,8 +241,11 @@ public class JavaSdkImpl extends JavaSdk {
|
||||
final FileChooserDescriptor descriptor = new FileChooserDescriptor(baseDescriptor) {
|
||||
@Override
|
||||
public void validateSelectedFiles(VirtualFile[] files) throws Exception {
|
||||
if (files.length > 0 && JrtFileSystem.isModularJdk(files[0].getPath()) && !JrtFileSystem.isSupported()) {
|
||||
throw new Exception(LangBundle.message("jrt.not.available.message"));
|
||||
if (files.length > 0 && !JrtFileSystem.isSupported()) {
|
||||
String path = files[0].getPath();
|
||||
if (JrtFileSystem.isModularJdk(path) || JrtFileSystem.isModularJdk(adjustSelectedSdkHome(path))) {
|
||||
throw new Exception(LangBundle.message("jrt.not.available.message"));
|
||||
}
|
||||
}
|
||||
baseDescriptor.validateSelectedFiles(files);
|
||||
}
|
||||
|
||||
@@ -32,10 +32,7 @@ import org.jetbrains.jps.cmdline.ProjectDescriptor;
|
||||
import org.jetbrains.jps.indices.IgnoredFileIndex;
|
||||
import org.jetbrains.jps.indices.ModuleExcludeIndex;
|
||||
import org.jetbrains.jps.model.JpsModel;
|
||||
import org.jetbrains.jps.model.java.JavaResourceRootType;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootProperties;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType;
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
|
||||
import org.jetbrains.jps.model.java.*;
|
||||
import org.jetbrains.jps.model.module.JpsModule;
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
|
||||
import org.jetbrains.jps.model.module.JpsTypedModuleSourceRoot;
|
||||
@@ -101,10 +98,11 @@ public final class ResourcesTarget extends JVMModuleBuildTarget<ResourceRootDesc
|
||||
}
|
||||
|
||||
JavaResourceRootType resourceType = isTests() ? JavaResourceRootType.TEST_RESOURCE : JavaResourceRootType.RESOURCE;
|
||||
for (JpsModuleSourceRoot root : myModule.getSourceRoots(resourceType)) {
|
||||
for (JpsTypedModuleSourceRoot<JavaResourceRootProperties> root : myModule.getSourceRoots(resourceType)) {
|
||||
if (!isExcludedFromCompilation(excludedRootProviders, root)) {
|
||||
File rootFile = root.getFile();
|
||||
roots.add(new ResourceRootDescriptor(rootFile, this, "", computeRootExcludes(rootFile, index)));
|
||||
String relativeOutputPath = root.getProperties().getRelativeOutputPath();
|
||||
roots.add(new ResourceRootDescriptor(rootFile, this, relativeOutputPath.replace('/', '.'), computeRootExcludes(rootFile, index)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.jps.builders.rebuild
|
||||
|
||||
import org.jetbrains.jps.util.JpsPathUtil
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.jps.model.java.JavaResourceRootType
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService
|
||||
|
||||
/**
|
||||
* @author nik
|
||||
*/
|
||||
@@ -71,4 +77,17 @@ public class ModuleRebuildTest: JpsRebuildTestCase() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun testResourceRootWithRelativeOutputPath() {
|
||||
val m = addModule("m")
|
||||
val res = PathUtil.getParentPath(createFile("res/a.txt", "42"))
|
||||
val url = JpsPathUtil.pathToUrl(res)
|
||||
m.addSourceRoot(url, JavaResourceRootType.RESOURCE, JpsJavaExtensionService.getInstance().createResourceRootProperties("foo", false))
|
||||
rebuild()
|
||||
assertOutput(getAbsolutePath("out/production/m"), fs {
|
||||
dir("foo") {
|
||||
file("a.txt", "42")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.jps.model.java;
|
||||
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.model.ex.JpsElementBase;
|
||||
|
||||
/**
|
||||
* @author nik
|
||||
*/
|
||||
public class JavaResourceRootProperties extends JpsElementBase<JavaResourceRootProperties> {
|
||||
private String myRelativeOutputPath = "";
|
||||
private boolean myForGeneratedSources;
|
||||
|
||||
public JavaResourceRootProperties(@NotNull String relativeOutputPath, boolean forGeneratedSources) {
|
||||
myRelativeOutputPath = relativeOutputPath;
|
||||
myForGeneratedSources = forGeneratedSources;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return relative path to the target directory under the module output directory for resource files from this root
|
||||
*/
|
||||
@NotNull
|
||||
public String getRelativeOutputPath() {
|
||||
return myRelativeOutputPath;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JavaResourceRootProperties createCopy() {
|
||||
return new JavaResourceRootProperties(myRelativeOutputPath, myForGeneratedSources);
|
||||
}
|
||||
|
||||
public boolean isForGeneratedSources() {
|
||||
return myForGeneratedSources;
|
||||
}
|
||||
|
||||
public void setRelativeOutputPath(@NotNull String relativeOutputPath) {
|
||||
if (!Comparing.equal(myRelativeOutputPath, relativeOutputPath)) {
|
||||
myRelativeOutputPath = relativeOutputPath;
|
||||
fireElementChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void setForGeneratedSources(boolean forGeneratedSources) {
|
||||
if (myForGeneratedSources != forGeneratedSources) {
|
||||
myForGeneratedSources = forGeneratedSources;
|
||||
fireElementChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyChanges(@NotNull JavaResourceRootProperties modified) {
|
||||
setRelativeOutputPath(modified.myRelativeOutputPath);
|
||||
setForGeneratedSources(modified.myForGeneratedSources);
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,14 @@
|
||||
package org.jetbrains.jps.model.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.model.JpsDummyElement;
|
||||
import org.jetbrains.jps.model.JpsElementFactory;
|
||||
import org.jetbrains.jps.model.ex.JpsElementTypeWithDummyProperties;
|
||||
import org.jetbrains.jps.model.ex.JpsElementTypeBase;
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
|
||||
|
||||
/**
|
||||
* @author nik
|
||||
*/
|
||||
public class JavaResourceRootType extends JpsElementTypeWithDummyProperties implements JpsModuleSourceRootType<JpsDummyElement> {
|
||||
public class JavaResourceRootType extends JpsElementTypeBase<JavaResourceRootProperties> implements
|
||||
JpsModuleSourceRootType<JavaResourceRootProperties> {
|
||||
public static final JavaResourceRootType RESOURCE = new JavaResourceRootType();
|
||||
public static final JavaResourceRootType TEST_RESOURCE = new JavaResourceRootType();
|
||||
|
||||
@@ -33,7 +32,7 @@ public class JavaResourceRootType extends JpsElementTypeWithDummyProperties impl
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JpsDummyElement createDefaultProperties() {
|
||||
return JpsElementFactory.getInstance().createDummyElement();
|
||||
public JavaResourceRootProperties createDefaultProperties() {
|
||||
return JpsJavaExtensionService.getInstance().createResourceRootProperties("", false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,4 +121,7 @@ public abstract class JpsJavaExtensionService {
|
||||
|
||||
@NotNull
|
||||
public abstract JavaSourceRootProperties createSourceRootProperties(@NotNull String packagePrefix);
|
||||
|
||||
@NotNull
|
||||
public abstract JavaResourceRootProperties createResourceRootProperties(@NotNull String relativeOutputPath, boolean forGeneratedResource);
|
||||
}
|
||||
|
||||
@@ -188,6 +188,12 @@ public class JpsJavaExtensionServiceImpl extends JpsJavaExtensionService {
|
||||
return createSourceRootProperties(packagePrefix, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JavaResourceRootProperties createResourceRootProperties(@NotNull String relativeOutputPath, boolean forGeneratedResource) {
|
||||
return new JavaResourceRootProperties(relativeOutputPath, forGeneratedResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public JpsProductionModuleOutputPackagingElement createProductionModuleOutput(@NotNull JpsModuleReference moduleReference) {
|
||||
|
||||
+30
-4
@@ -19,7 +19,9 @@ import com.intellij.openapi.util.JDOMUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.model.*;
|
||||
import org.jetbrains.jps.model.JpsElementFactory;
|
||||
import org.jetbrains.jps.model.JpsProject;
|
||||
import org.jetbrains.jps.model.JpsUrlList;
|
||||
import org.jetbrains.jps.model.java.*;
|
||||
import org.jetbrains.jps.model.library.JpsOrderRootType;
|
||||
import org.jetbrains.jps.model.module.JpsDependencyElement;
|
||||
@@ -32,7 +34,6 @@ import org.jetbrains.jps.model.serialization.artifact.JpsPackagingElementSeriali
|
||||
import org.jetbrains.jps.model.serialization.java.compiler.*;
|
||||
import org.jetbrains.jps.model.serialization.library.JpsLibraryRootTypeSerializer;
|
||||
import org.jetbrains.jps.model.serialization.module.JpsModuleRootModelSerializer;
|
||||
import org.jetbrains.jps.model.serialization.module.JpsModuleSourceRootDummyPropertiesSerializer;
|
||||
import org.jetbrains.jps.model.serialization.module.JpsModuleSourceRootPropertiesSerializer;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -56,6 +57,7 @@ public class JpsJavaModelSerializerExtension extends JpsModelSerializerExtension
|
||||
private static final String JAVADOC_PATHS_TAG = "javadoc-paths";
|
||||
private static final String MODULE_LANGUAGE_LEVEL_ATTRIBUTE = "LANGUAGE_LEVEL";
|
||||
public static final String ROOT_TAG = "root";
|
||||
private static final String RELATIVE_OUTPUT_PATH_ATTRIBUTE = "relativeOutputPath";
|
||||
private static final String IS_GENERATED_ATTRIBUTE = "generated";
|
||||
public static final JavaSourceRootPropertiesSerializer JAVA_SOURCE_ROOT_PROPERTIES_SERIALIZER =
|
||||
new JavaSourceRootPropertiesSerializer(JavaSourceRootType.SOURCE, JpsModuleRootModelSerializer.JAVA_SOURCE_ROOT_TYPE_ID);
|
||||
@@ -88,8 +90,8 @@ public class JpsJavaModelSerializerExtension extends JpsModelSerializerExtension
|
||||
public List<? extends JpsModuleSourceRootPropertiesSerializer<?>> getModuleSourceRootPropertiesSerializers() {
|
||||
return Arrays.asList(JAVA_SOURCE_ROOT_PROPERTIES_SERIALIZER,
|
||||
new JavaSourceRootPropertiesSerializer(JavaSourceRootType.TEST_SOURCE, JpsModuleRootModelSerializer.JAVA_TEST_ROOT_TYPE_ID),
|
||||
new JpsModuleSourceRootDummyPropertiesSerializer(JavaResourceRootType.RESOURCE, "java-resource"),
|
||||
new JpsModuleSourceRootDummyPropertiesSerializer(JavaResourceRootType.TEST_RESOURCE, "java-test-resource"));
|
||||
new JavaResourceRootPropertiesSerializer(JavaResourceRootType.RESOURCE, "java-resource"),
|
||||
new JavaResourceRootPropertiesSerializer(JavaResourceRootType.TEST_RESOURCE, "java-test-resource"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -328,4 +330,28 @@ public class JpsJavaModelSerializerExtension extends JpsModelSerializerExtension
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class JavaResourceRootPropertiesSerializer extends JpsModuleSourceRootPropertiesSerializer<JavaResourceRootProperties> {
|
||||
private JavaResourceRootPropertiesSerializer(JpsModuleSourceRootType<JavaResourceRootProperties> type, String typeId) {
|
||||
super(type, typeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaResourceRootProperties loadProperties(@NotNull Element sourceRootTag) {
|
||||
String relativeOutputPath = StringUtil.notNullize(sourceRootTag.getAttributeValue(RELATIVE_OUTPUT_PATH_ATTRIBUTE));
|
||||
boolean isGenerated = Boolean.parseBoolean(sourceRootTag.getAttributeValue(IS_GENERATED_ATTRIBUTE));
|
||||
return getService().createResourceRootProperties(relativeOutputPath, isGenerated);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveProperties(@NotNull JavaResourceRootProperties properties, @NotNull Element sourceRootTag) {
|
||||
String relativeOutputPath = properties.getRelativeOutputPath();
|
||||
if (!relativeOutputPath.isEmpty()) {
|
||||
sourceRootTag.setAttribute(RELATIVE_OUTPUT_PATH_ATTRIBUTE, relativeOutputPath);
|
||||
}
|
||||
if (properties.isForGeneratedSources()) {
|
||||
sourceRootTag.setAttribute(IS_GENERATED_ATTRIBUTE, Boolean.TRUE.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/gen" type="java-resource" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/gen-foo" type="java-resource" relativeOutputPath="foo" generated="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/resourceRoots.iml" filepath="$PROJECT_DIR$/resourceRoots.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.4" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
+23
-3
@@ -151,11 +151,31 @@ public class JpsProjectSerializationTest extends JpsSerializationTestCase {
|
||||
assertEquals("UTF-8", configuration.getEncoding(new File(getAbsolutePath("other"))));
|
||||
}
|
||||
|
||||
public void testResourceRoots() {
|
||||
String projectPath = "/jps/model-serialization/testData/resourceRoots/";
|
||||
loadProject(projectPath + "resourceRoots.ipr");
|
||||
JpsModule module = assertOneElement(myProject.getModules());
|
||||
List<JpsModuleSourceRoot> roots = module.getSourceRoots();
|
||||
assertSame(JavaSourceRootType.SOURCE, roots.get(0).getRootType());
|
||||
checkResourceRoot(roots.get(1), false, "");
|
||||
checkResourceRoot(roots.get(2), true, "");
|
||||
checkResourceRoot(roots.get(3), true, "foo");
|
||||
doTestSaveModule(module, projectPath + "resourceRoots.iml");
|
||||
}
|
||||
|
||||
private static void checkResourceRoot(JpsModuleSourceRoot root, boolean forGenerated, String relativeOutput) {
|
||||
assertSame(JavaResourceRootType.RESOURCE, root.getRootType());
|
||||
JavaResourceRootProperties properties = root.getProperties(JavaResourceRootType.RESOURCE);
|
||||
assertNotNull(properties);
|
||||
assertEquals(forGenerated, properties.isForGeneratedSources());
|
||||
assertEquals(relativeOutput, properties.getRelativeOutputPath());
|
||||
}
|
||||
|
||||
public void testSaveProject() {
|
||||
loadProject(SAMPLE_PROJECT_PATH);
|
||||
List<JpsModule> modules = myProject.getModules();
|
||||
doTestSaveModule(modules.get(0), "main.iml");
|
||||
doTestSaveModule(modules.get(1), "util/util.iml");
|
||||
doTestSaveModule(modules.get(0), SAMPLE_PROJECT_PATH + "/main.iml");
|
||||
doTestSaveModule(modules.get(1), SAMPLE_PROJECT_PATH + "/util/util.iml");
|
||||
//tod[nik] remember that test output root wasn't specified and doesn't save it to avoid unnecessary modifications of iml files
|
||||
//doTestSaveModule(modules.get(2), "xxx/xxx.iml");
|
||||
|
||||
@@ -188,7 +208,7 @@ public class JpsProjectSerializationTest extends JpsSerializationTestCase {
|
||||
try {
|
||||
Element actual = JDomSerializationUtil.createComponentElement("NewModuleRootManager");
|
||||
JpsModuleRootModelSerializer.saveRootModel(module, actual);
|
||||
File imlFile = getFileInSampleProject(moduleFilePath);
|
||||
File imlFile = new File(getTestDataFileAbsolutePath(moduleFilePath));
|
||||
Element rootElement = loadModuleRootTag(imlFile);
|
||||
Element expected = JDomSerializationUtil.findComponent(rootElement, "NewModuleRootManager");
|
||||
PlatformTestUtil.assertElementsEqual(expected, actual);
|
||||
|
||||
@@ -40,6 +40,9 @@ public abstract class DiffContentFactory {
|
||||
@NotNull
|
||||
public abstract EmptyContent createEmpty();
|
||||
|
||||
@NotNull
|
||||
public abstract DocumentContent create(@NotNull String text);
|
||||
|
||||
@NotNull
|
||||
public abstract DocumentContent create(@NotNull String text, @Nullable FileType type);
|
||||
|
||||
@@ -58,8 +61,11 @@ public abstract class DiffContentFactory {
|
||||
@NotNull
|
||||
public abstract DiffContent createClipboardContent();
|
||||
|
||||
/**
|
||||
* @param referenceContent used to detect FileType and proper highlighting for clipboard content
|
||||
*/
|
||||
@NotNull
|
||||
public abstract DocumentContent createClipboardContent(@NotNull DocumentContent mainContent);
|
||||
public abstract DocumentContent createClipboardContent(@NotNull DocumentContent referenceContent);
|
||||
|
||||
@NotNull
|
||||
public abstract DiffContent createBinary(@Nullable Project project,
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.ide.CopyPasteManager;
|
||||
@@ -47,6 +46,12 @@ public class DiffContentFactoryImpl extends DiffContentFactory {
|
||||
return new EmptyContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public DocumentContent create(@NotNull String text) {
|
||||
return create(text, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public DocumentContent create(@NotNull String text, @Nullable FileType type) {
|
||||
@@ -97,14 +102,16 @@ public class DiffContentFactoryImpl extends DiffContentFactory {
|
||||
@NotNull
|
||||
public DiffContent createClipboardContent() {
|
||||
String text = CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor);
|
||||
return new DocumentContentImpl(new DocumentImpl(StringUtil.notNullize(text)));
|
||||
Document document = EditorFactory.getInstance().createDocument(StringUtil.convertLineSeparators(StringUtil.notNullize(text)));
|
||||
return new DocumentContentImpl(document); // TODO: show difference in line separators ?
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public DocumentContent createClipboardContent(@NotNull DocumentContent mainContent) {
|
||||
String text = CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor);
|
||||
return new DocumentContentWrapper(mainContent, StringUtil.notNullize(text));
|
||||
Document document = EditorFactory.getInstance().createDocument(StringUtil.convertLineSeparators(StringUtil.notNullize(text)));
|
||||
return new DocumentContentWrapper(document, mainContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -47,7 +47,7 @@ public class DiffRequestFactoryImpl extends DiffRequestFactory {
|
||||
@NotNull
|
||||
public ContentDiffRequest createClipboardVsValue(@NotNull String value) {
|
||||
DiffContent content1 = myContentFactory.createClipboardContent();
|
||||
DiffContent content2 = myContentFactory.create(value, null);
|
||||
DiffContent content2 = myContentFactory.create(value);
|
||||
|
||||
String title1 = DiffBundle.message("diff.content.clipboard.content.title");
|
||||
String title2 = DiffBundle.message("diff.content.selected.value");
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package com.intellij.diff.contents;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -30,9 +29,9 @@ public class DocumentContentWrapper implements DocumentContent {
|
||||
@NotNull private final DocumentContent myBase;
|
||||
@NotNull private final Document myDocument;
|
||||
|
||||
public DocumentContentWrapper(@NotNull DocumentContent base, @NotNull String text) {
|
||||
public DocumentContentWrapper(@NotNull Document document, @NotNull DocumentContent base) {
|
||||
myDocument = document;
|
||||
myBase = base;
|
||||
myDocument = EditorFactory.getInstance().createDocument(text);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -109,9 +109,9 @@ public class ScratchFileServiceImpl extends ScratchFileService implements Persis
|
||||
@Nullable
|
||||
@Override
|
||||
public RootType getRootType(@NotNull VirtualFile file) {
|
||||
VirtualFile parent = file.getParent();
|
||||
if (!(parent instanceof VirtualFileWithId)) return null;
|
||||
RootType result = myIndex.getInfoForFile(parent);
|
||||
VirtualFile directory = file.isDirectory() ? file : file.getParent();
|
||||
if (!(directory instanceof VirtualFileWithId)) return null;
|
||||
RootType result = myIndex.getInfoForFile(directory);
|
||||
return result == NULL_TYPE ? null : result;
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -28,6 +28,10 @@ import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
|
||||
import org.jetbrains.jps.model.java.JavaResourceRootProperties;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootProperties;
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -58,7 +62,7 @@ public class ContentEntryTreeCellRenderer extends NodeRenderer {
|
||||
if (file != null && file.isDirectory()) {
|
||||
final ContentEntry contentEntry = editor.getContentEntry();
|
||||
if (contentEntry != null) {
|
||||
final String prefix = getPrefix(contentEntry, file);
|
||||
final String prefix = getPresentablePrefix(contentEntry, file);
|
||||
if (!prefix.isEmpty()) {
|
||||
append(" (" + prefix + ")", new SimpleTextAttributes(Font.PLAIN, JBColor.GRAY));
|
||||
}
|
||||
@@ -70,10 +74,14 @@ public class ContentEntryTreeCellRenderer extends NodeRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getPrefix(final ContentEntry entry, final VirtualFile file) {
|
||||
private static String getPresentablePrefix(final ContentEntry entry, final VirtualFile file) {
|
||||
for (final SourceFolder sourceFolder : entry.getSourceFolders()) {
|
||||
if (file.equals(sourceFolder.getFile())) {
|
||||
return sourceFolder.getPackagePrefix();
|
||||
JpsModuleSourceRoot element = sourceFolder.getJpsElement();
|
||||
JavaSourceRootProperties properties = element.getProperties(JavaModuleSourceRootTypes.SOURCES);
|
||||
if (properties != null) return properties.getPackagePrefix();
|
||||
JavaResourceRootProperties resourceRootProperties = element.getProperties(JavaModuleSourceRootTypes.RESOURCES);
|
||||
if (resourceRootProperties != null) return resourceRootProperties.getRelativeOutputPath();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -54,5 +54,9 @@ public class RegistryToggleAction extends ToggleAction {
|
||||
@Override
|
||||
public void setSelected(AnActionEvent e, boolean state) {
|
||||
Registry.get(myKey).setValue(state);
|
||||
doWhenDone(e);
|
||||
}
|
||||
|
||||
public void doWhenDone(AnActionEvent e) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ class FilePointerPartNode {
|
||||
for (FilePointerPartNode child : children) {
|
||||
// find the right child (its part should start with ours)
|
||||
int i = child.indexOfFirstDifferentChar(path, index);
|
||||
if (i != index && (i > index+1 || path.charAt(index) != '/')) {
|
||||
if (i != index && (i > index+1 || path.charAt(index) != '/' || index == 0)) {
|
||||
FilePointerPartNode node = child.findPointerOrCreate(path, index, fileAndUrl);
|
||||
if (node.leaf == null) pointersUnder++; // the new node's been created
|
||||
return node;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.ui.mac;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.fileChooser.FileChooser;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
@@ -37,6 +38,7 @@ import com.intellij.util.Consumer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.sun.jna.Callback;
|
||||
import com.sun.jna.Pointer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -59,53 +61,69 @@ public class MacFileChooserDialogImpl implements PathChooserDialog {
|
||||
private final Project myProject;
|
||||
private Consumer<List<VirtualFile>> myCallback;
|
||||
|
||||
private static boolean checkFile(@NotNull ID self, ID url, boolean checkDirectories) {
|
||||
MacFileChooserDialogImpl dialog = ourImplMap.get(self);
|
||||
if (dialog == null) {
|
||||
// Since it has already been removed from the map, the file is likely to be valid if the user was able to select it
|
||||
return true;
|
||||
}
|
||||
|
||||
if (url == null || url.intValue() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ID filename = Foundation.invoke(url, "path");
|
||||
String fileName = Foundation.toStringViaUTF8(filename);
|
||||
if (fileName == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(fileName);
|
||||
return file == null || (!checkDirectories && file.isDirectory()) || dialog.myChooserDescriptor.isFileSelectable(file);
|
||||
}
|
||||
|
||||
private static final Callback SHOULD_ENABLE_CALLBACK = new Callback() {
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public boolean callback(ID self, String selector, ID panel, ID url) {
|
||||
// allow any directory - ability to select nested directories
|
||||
return checkFile(self, url, false);
|
||||
try {
|
||||
return checkFile(self, url, true); // allow any directory - ability to select nested directories
|
||||
}
|
||||
catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static final Callback VALIDATE_URL_CALLBACK = new Callback() {
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public boolean callback(ID self, String selector, ID panel, ID url, ID outError) {
|
||||
if (checkFile(self, url, true)) {
|
||||
return true;
|
||||
try {
|
||||
return checkFile(self, url, false);
|
||||
}
|
||||
|
||||
/*
|
||||
if (!outError.equals(ID.NIL)) {
|
||||
ID error = Foundation.invoke("NSError", "errorWithDomain:code:userInfo:", Foundation.nsString("org.jetbrains"),
|
||||
Foundation.createDict(new String[]{"NSLocalizedDescriptionKey"}, new Object[]{"Not allowed"}));
|
||||
// todo "*outError = error"
|
||||
catch (Exception e) {
|
||||
if (!ID.NIL.equals(outError)) {
|
||||
ID domain = Foundation.nsString(ApplicationNamesInfo.getInstance().getProductName());
|
||||
ID dict = Foundation.createDict(new String[]{"NSLocalizedDescription"}, new Object[]{e.getMessage()});
|
||||
ID error = Foundation.invoke("NSError", "errorWithDomain:code:userInfo:", domain, 100, dict);
|
||||
new Pointer(outError.longValue()).setLong(0, error.longValue());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
private static boolean checkFile(@NotNull ID self, ID url, boolean quickCheck) throws Exception {
|
||||
MacFileChooserDialogImpl impl = ourImplMap.get(self);
|
||||
if (impl == null) {
|
||||
return true; // already removed from the map: the file is likely to be valid since the user was able to select it
|
||||
}
|
||||
|
||||
if (url == null || url.intValue() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ID filename = Foundation.invoke(url, "path");
|
||||
String path = Foundation.toStringViaUTF8(filename);
|
||||
if (path == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(path);
|
||||
if (file == null || quickCheck && file.isDirectory()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!impl.myChooserDescriptor.isFileSelectable(file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!quickCheck) {
|
||||
VirtualFile[] files = {file};
|
||||
impl.myChooserDescriptor.validateSelectedFiles(files);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final Callback OPEN_PANEL_DID_END = new Callback() {
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public void callback(ID self, String selector, ID openPanelDidEnd, ID returnCode, ID contextInfo) {
|
||||
@@ -300,7 +318,6 @@ public class MacFileChooserDialogImpl implements PathChooserDialog {
|
||||
|
||||
@Override
|
||||
public void choose(@Nullable final VirtualFile toSelect, @NotNull final Consumer<List<VirtualFile>> callback) {
|
||||
|
||||
ExtensionsInitializer.initialize();
|
||||
|
||||
myCallback = callback;
|
||||
@@ -356,12 +373,11 @@ public class MacFileChooserDialogImpl implements PathChooserDialog {
|
||||
return Foundation.invoke(id, Foundation.createSelector(selector), args);
|
||||
}
|
||||
|
||||
/** This class is intended to force extensions initialization on EDT thread (IDEA-107271)
|
||||
*/
|
||||
/** This class is intended to force extensions initialization on EDT thread (IDEA-107271) */
|
||||
private static class ExtensionsInitializer {
|
||||
private ExtensionsInitializer() {}
|
||||
private static boolean initialized;
|
||||
private static void initialize () {
|
||||
private static void initialize() {
|
||||
if (initialized) return;
|
||||
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
|
||||
@Override
|
||||
@@ -372,5 +388,4 @@ public class MacFileChooserDialogImpl implements PathChooserDialog {
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -47,10 +47,9 @@ public class Growl {
|
||||
final ID applicationIcon = getApplicationIcon();
|
||||
final ID defaultNotifications = Foundation.fillArray(myDefaultNotification);
|
||||
final ID allNotifications = Foundation.fillArray(myAllNotifications);
|
||||
|
||||
final ID userDict = Foundation.createDict(new String[]{GROWL_APP_NAME, GROWL_APP_ICON, GROWL_DEFAULT_NOTIFICATIONS, GROWL_ALL_NOTIFICATIONS},
|
||||
new Object[]{myProductName, applicationIcon, defaultNotifications, allNotifications});
|
||||
|
||||
final ID userDict = Foundation.createDict(
|
||||
new String[]{GROWL_APP_NAME, GROWL_APP_ICON, GROWL_DEFAULT_NOTIFICATIONS, GROWL_ALL_NOTIFICATIONS},
|
||||
new Object[]{myProductName, applicationIcon, defaultNotifications, allNotifications});
|
||||
final ID center = invoke("NSDistributedNotificationCenter", "defaultCenter");
|
||||
final Object notificationName = Foundation.nsString(GROWL_APPLICATION_REGISTRATION_NOTIFICATION);
|
||||
invoke(center, "postNotificationName:object:userInfo:deliverImmediately:", notificationName, null, userDict, true);
|
||||
@@ -59,12 +58,11 @@ public class Growl {
|
||||
|
||||
public void notifyGrowlOf(final String notification, final String title, final String description) {
|
||||
final ID autoReleasePool = createAutoReleasePool();
|
||||
final ID dict = Foundation.createDict(new String[]{
|
||||
GROWL_NOTIFICATION_NAME, GROWL_NOTIFICATION_TITLE, GROWL_NOTIFICATION_DESCRIPTION, GROWL_APP_NAME},
|
||||
new Object[]{notification, title, description, myProductName});
|
||||
final ID dict = Foundation.createDict(
|
||||
new String[]{GROWL_NOTIFICATION_NAME, GROWL_NOTIFICATION_TITLE, GROWL_NOTIFICATION_DESCRIPTION, GROWL_APP_NAME},
|
||||
new Object[]{notification, title, description, myProductName});
|
||||
final ID center = invoke("NSDistributedNotificationCenter", "defaultCenter");
|
||||
final Object notificationName = Foundation.nsString(GROWL_NOTIFICATION);
|
||||
|
||||
invoke(center, "postNotificationName:object:userInfo:deliverImmediately:", notificationName, null, dict, true);
|
||||
invoke(autoReleasePool, "release");
|
||||
}
|
||||
|
||||
+11
@@ -163,6 +163,17 @@ public class VirtualFilePointerTest extends PlatformLangTestCase {
|
||||
assertSameElements(myVirtualFilePointerManager.getPointersUnder(b, "p2"), p2);
|
||||
}
|
||||
|
||||
public void testUrlsHavingOnlyStartingSlashInCommonAndInvalidUrlBetweenThem() throws Exception {
|
||||
VirtualFilePointer p1 = myVirtualFilePointerManager.create("file:///a/p1", disposable, null);
|
||||
myVirtualFilePointerManager.create("file://invalid/path", disposable, null);
|
||||
VirtualFilePointer p2 = myVirtualFilePointerManager.create("file:///b/p2", disposable, null);
|
||||
final LightVirtualFile root = new LightVirtualFile("/");
|
||||
LightVirtualFile a = createLightFile(root, "a");
|
||||
LightVirtualFile b = createLightFile(root, "b");
|
||||
assertSameElements(myVirtualFilePointerManager.getPointersUnder(a, "p1"), p1);
|
||||
assertSameElements(myVirtualFilePointerManager.getPointersUnder(b, "p2"), p2);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static LightVirtualFile createLightFile(final LightVirtualFile parent, final String name) {
|
||||
return new LightVirtualFile(name) {
|
||||
|
||||
+22
-11
@@ -27,10 +27,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.model.JpsElement;
|
||||
import org.jetbrains.jps.model.JpsElementFactory;
|
||||
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
|
||||
import org.jetbrains.jps.model.java.JavaResourceRootType;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootProperties;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType;
|
||||
import org.jetbrains.jps.model.java.*;
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
|
||||
import org.jetbrains.jps.model.module.JpsTypedModuleSourceRoot;
|
||||
@@ -78,7 +75,14 @@ public class SourceFolderImpl extends ContentFolderBaseImpl implements SourceFol
|
||||
@Override
|
||||
public String getPackagePrefix() {
|
||||
JavaSourceRootProperties properties = getJavaProperties();
|
||||
return properties != null ? properties.getPackagePrefix() : DEFAULT_PACKAGE_PREFIX;
|
||||
if (properties != null) {
|
||||
return properties.getPackagePrefix();
|
||||
}
|
||||
JavaResourceRootProperties resourceJavaProperties = getResourceJavaProperties();
|
||||
if (resourceJavaProperties != null) {
|
||||
return resourceJavaProperties.getRelativeOutputPath().replace('/', '.');
|
||||
}
|
||||
return DEFAULT_PACKAGE_PREFIX;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -86,6 +90,11 @@ public class SourceFolderImpl extends ContentFolderBaseImpl implements SourceFol
|
||||
return myJpsElement.getProperties(JavaModuleSourceRootTypes.SOURCES);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JavaResourceRootProperties getResourceJavaProperties() {
|
||||
return myJpsElement.getProperties(JavaModuleSourceRootTypes.RESOURCES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPackagePrefix(@NotNull String packagePrefix) {
|
||||
JavaSourceRootProperties properties = getJavaProperties();
|
||||
@@ -112,6 +121,12 @@ public class SourceFolderImpl extends ContentFolderBaseImpl implements SourceFol
|
||||
return myJpsElement;
|
||||
}
|
||||
|
||||
private boolean isForGeneratedSources() {
|
||||
JavaSourceRootProperties properties = getJavaProperties();
|
||||
JavaResourceRootProperties resourceJavaProperties = getResourceJavaProperties();
|
||||
return properties != null && properties.isForGeneratedSources() || resourceJavaProperties != null && resourceJavaProperties.isForGeneratedSources();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ContentFolderBaseImpl folder) {
|
||||
if (!(folder instanceof SourceFolderImpl)) return -1;
|
||||
@@ -124,12 +139,8 @@ public class SourceFolderImpl extends ContentFolderBaseImpl implements SourceFol
|
||||
if (i!= 0) return i;
|
||||
i = Boolean.valueOf(isTestSource()).compareTo(sourceFolder.isTestSource());
|
||||
if (i != 0) return i;
|
||||
JavaSourceRootProperties properties1 = getJavaProperties();
|
||||
JavaSourceRootProperties properties2 = sourceFolder.getJavaProperties();
|
||||
if (properties1 != null && properties2 != null) {
|
||||
i = Boolean.valueOf(properties1.isForGeneratedSources()).compareTo(properties2.isForGeneratedSources());
|
||||
if (i != 0) return i;
|
||||
}
|
||||
i = Boolean.valueOf(isForGeneratedSources()).compareTo(sourceFolder.isForGeneratedSources());
|
||||
if (i != 0) return i;
|
||||
//todo[nik] perhaps we should use LinkedSet instead of SortedSet and get rid of this method
|
||||
return myJpsElement.getRootType().getClass().getName().compareTo(sourceFolder.getRootType().getClass().getName());
|
||||
}
|
||||
|
||||
+16
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2015 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.
|
||||
@@ -15,7 +15,11 @@
|
||||
*/
|
||||
package com.intellij.xdebugger.impl.actions;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.RegistryToggleAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
||||
/**
|
||||
* @author Konstantin Bulenkov
|
||||
@@ -24,4 +28,15 @@ public class UseInlineDebuggerAction extends RegistryToggleAction {
|
||||
public UseInlineDebuggerAction() {
|
||||
super("ide.debugger.inline");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWhenDone(AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
if (project != null) {
|
||||
final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (editor != null) {
|
||||
editor.getComponent().repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ This module encapsulates Django semi-public API knowledge, and not very stable b
|
||||
"""
|
||||
from optparse import Option
|
||||
import django
|
||||
from django.apps import registry
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.management import ManagementUtility, get_commands, BaseCommand
|
||||
|
||||
__author__ = 'Ilya.Kazakevich'
|
||||
@@ -21,7 +21,11 @@ def report_data(dumper):
|
||||
"""
|
||||
utility = ManagementUtility()
|
||||
for command_name in get_commands().keys():
|
||||
command = utility.fetch_command(command_name)
|
||||
try:
|
||||
command = utility.fetch_command(command_name)
|
||||
except ImproperlyConfigured:
|
||||
continue # TODO: Log somehow
|
||||
|
||||
assert isinstance(command, BaseCommand)
|
||||
dumper.start_command(command_name=command_name,
|
||||
command_help_text=str(command.usage("").replace("%prog", command_name)),
|
||||
|
||||
@@ -856,8 +856,11 @@ custom.type.mimic.name=Dynamic class based on {0}
|
||||
# Values for command argument value validation
|
||||
commandLine.validation.badCommand=Unknown command
|
||||
commandLine.validation.argMissing=Required argument value is missing
|
||||
commandLine.validation.optArgMissing=Option argument value is missing
|
||||
commandLine.validation.argBadValue=Argument can't have this value
|
||||
commandLine.validation.excessArg=Excess argument value
|
||||
commandLine.validation.badOption=Bad option or option already set
|
||||
commandLine.validation.noArgAllowed=No argument allowed for this option
|
||||
# And for labels
|
||||
commandLine.subText.key.complete=Use {0} to complete selected variant
|
||||
commandLine.subText.key.suggestions=Use {0} to view available values
|
||||
|
||||
@@ -107,8 +107,8 @@ public final class WordWithPosition extends Range<Integer> {
|
||||
|
||||
WordWithPosition position = (WordWithPosition)o;
|
||||
|
||||
if (getFrom() != position.getFrom()) return false;
|
||||
if (getTo() != position.getTo()) return false;
|
||||
if (!getFrom().equals(position.getFrom())) return false;
|
||||
if (!getTo().equals(position.getTo())) return false;
|
||||
if (!myWord.equals(position.myWord)) return false;
|
||||
|
||||
return true;
|
||||
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Chunk and its info pair. Chunk may be null, while chunk info always present.
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class ChunkAndInfo implements Comparable<ChunkAndInfo> {
|
||||
@Nullable
|
||||
private final WordWithPosition myChunk;
|
||||
@NotNull
|
||||
private final ChunkInfo myChunkInfo;
|
||||
|
||||
ChunkAndInfo(@Nullable final WordWithPosition chunk, @NotNull final ChunkInfo chunkInfo) {
|
||||
myChunk = chunk;
|
||||
myChunkInfo = chunkInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return chunk (word). may be null
|
||||
*/
|
||||
@Nullable
|
||||
WordWithPosition getChunk() {
|
||||
return myChunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return chunk info.
|
||||
*/
|
||||
@NotNull
|
||||
ChunkInfo getChunkInfo() {
|
||||
return myChunkInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull final ChunkAndInfo o) {
|
||||
if (myChunk == null && o.myChunk == null) {
|
||||
return 0;
|
||||
}
|
||||
if (myChunk == null) {
|
||||
return 1;
|
||||
}
|
||||
if (o.myChunk == null) {
|
||||
return -1;
|
||||
}
|
||||
return myChunk.getFrom().compareTo(o.myChunk.getFrom());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ChunkAndInfo info = (ChunkAndInfo)o;
|
||||
|
||||
if (myChunk != null ? !myChunk.equals(info.myChunk) : info.myChunk != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myChunk != null ? myChunk.hashCode() : 0;
|
||||
}
|
||||
}
|
||||
-247
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
|
||||
import com.intellij.util.Range;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfacePresenterAdapter;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView;
|
||||
import com.jetbrains.python.suggestionList.SuggestionsBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
// TODO: Test
|
||||
|
||||
/**
|
||||
* Presenter that uses {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver} to parse pack of chunks
|
||||
* to obtain {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo}.
|
||||
* Each chunk may be paired with certain chunk info. Such info tells presenter whether this chunk has info, error, suggestions and so on.
|
||||
* If caret situated far from chunks, then next neariest chunk should be found (see {@link #findNearestChunkAndInfo()}.
|
||||
*
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
* @see com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo
|
||||
*/
|
||||
public final class ChunkDriverBasedPresenter extends CommandInterfacePresenterAdapter {
|
||||
|
||||
@NotNull
|
||||
private final ChunkDriver myChunkDriver;
|
||||
@NotNull
|
||||
private final SortedSet<ChunkAndInfo> myChunkAndInfos = new TreeSet<ChunkAndInfo>();
|
||||
@Nullable
|
||||
private Runnable myExecutor;
|
||||
|
||||
public ChunkDriverBasedPresenter(@NotNull final CommandInterfaceView view,
|
||||
@NotNull final ChunkDriver chunkDriver) {
|
||||
super(view);
|
||||
myChunkDriver = chunkDriver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch() {
|
||||
super.launch();
|
||||
reparseText(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textChanged() {
|
||||
reparseText(false);
|
||||
}
|
||||
|
||||
private void reparseText(final boolean skipSuggestions) {
|
||||
final List<WordWithPosition> chunks = WordWithPosition.splitText(myView.getText());
|
||||
final ParseInfo parseInfo = myChunkDriver.parse(chunks);
|
||||
myExecutor = parseInfo.getExecutor();
|
||||
final List<ChunkInfo> chunkInfos = parseInfo.getChunkInfo();
|
||||
assert chunkInfos.size() >= chunks.size() : "Driver did not return enough chunks";
|
||||
assert !chunkInfos.isEmpty() : "At least one chunk info should exist";
|
||||
myChunkAndInfos.clear();
|
||||
for (int i = 0; i < chunkInfos.size(); i++) {
|
||||
final ChunkInfo chunkInfo = chunkInfos.get(i);
|
||||
final WordWithPosition chunk = chunks.size() > i ? chunks.get(i) : null;
|
||||
myChunkAndInfos.add(new ChunkAndInfo(chunk, chunkInfo));
|
||||
}
|
||||
|
||||
|
||||
// configure Errors And Balloons
|
||||
|
||||
final Collection<WordWithPosition> infoBalloons = new ArrayList<WordWithPosition>();
|
||||
final Collection<WordWithPosition> errorBalloons = new ArrayList<WordWithPosition>();
|
||||
|
||||
|
||||
for (final ChunkAndInfo chunkInfoPair : myChunkAndInfos) {
|
||||
final ChunkInfo chunkInfo = chunkInfoPair.getChunkInfo();
|
||||
Range<Integer> chunk = chunkInfoPair.getChunk();
|
||||
if (chunk == null) {
|
||||
// After the last!
|
||||
chunk = CommandInterfaceView.AFTER_LAST_CHARACTER_RANGE;
|
||||
}
|
||||
final String error = chunkInfo.getError();
|
||||
if (error != null) {
|
||||
errorBalloons.add(new WordWithPosition(error, chunk));
|
||||
}
|
||||
final String info = chunkInfo.getInfoBalloon();
|
||||
if (info != null) {
|
||||
infoBalloons.add(new WordWithPosition(info, chunk));
|
||||
}
|
||||
}
|
||||
|
||||
myView.setInfoAndErrors(infoBalloons, errorBalloons);
|
||||
|
||||
|
||||
if (!skipSuggestions) {
|
||||
configureSuggestion(false);
|
||||
}
|
||||
|
||||
// Configure subtexts
|
||||
final List<Range<Integer>> placesWhereSuggestionAvailable = new ArrayList<Range<Integer>>();
|
||||
for (final ChunkAndInfo chunkAndInfo : myChunkAndInfos) {
|
||||
Range<Integer> chunk = chunkAndInfo.getChunk();
|
||||
// If some place has suggestions, then add it
|
||||
if (chunkAndInfo.getChunkInfo().getSuggestions() != null) {
|
||||
if (chunk == null) {
|
||||
// If there is no such chunk, that means we are after the last character, so use "special case" here
|
||||
//noinspection ReuseOfLocalVariable
|
||||
chunk = CommandInterfaceView.AFTER_LAST_CHARACTER_RANGE;
|
||||
}
|
||||
placesWhereSuggestionAvailable.add(chunk);
|
||||
}
|
||||
}
|
||||
myView.configureSubTexts(parseInfo.getStatusText(), placesWhereSuggestionAvailable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestionRequested() {
|
||||
configureSuggestion(true); // Show or hide
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Displays suggestions if needed.
|
||||
*
|
||||
* @param requestedExplicitly is suggesions where requested by user explicitly or not
|
||||
*/
|
||||
private void configureSuggestion(final boolean requestedExplicitly) {
|
||||
myView.removeSuggestions();
|
||||
final ChunkAndInfo chunkAndInfo = findNearestChunkAndInfo();
|
||||
final ChunkInfo chunkInfo = chunkAndInfo.getChunkInfo();
|
||||
final WordWithPosition chunk = chunkAndInfo.getChunk();
|
||||
|
||||
final SuggestionInfo suggestionInfo = chunkInfo.getSuggestions();
|
||||
if (suggestionInfo == null || (!suggestionInfo.isShowSuggestionsAutomatically() && !requestedExplicitly)) {
|
||||
return;
|
||||
}
|
||||
final List<String> suggestions = new ArrayList<String>(suggestionInfo.getSuggestions());
|
||||
if (chunk != null && !requestedExplicitly) {
|
||||
filterLeaveOnlyMatching(suggestions, chunk.getText());
|
||||
}
|
||||
// TODO: Place to add history
|
||||
if (!suggestions.isEmpty()) {
|
||||
// No need to display empty suggestions
|
||||
myView
|
||||
.displaySuggestions(new SuggestionsBuilder(suggestions), suggestionInfo.isShowAbsolute(), (chunk == null ? null : chunk.getText()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters collection of suggestions leaving only those starts with certain text.
|
||||
* @param suggestions list to filter
|
||||
* @param textToMatch leave only parts that start with this param
|
||||
*/
|
||||
private static void filterLeaveOnlyMatching(@NotNull final Iterable<String> suggestions, @NotNull final String textToMatch) {
|
||||
// TODO: use guava instead?
|
||||
final Iterator<String> iterator = suggestions.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (!iterator.next().startsWith(textToMatch)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the nearest chunk and info to use. It may or may not find chunk, but it should always provide some chunk info.
|
||||
*
|
||||
* @return nearest chunk info and, probably, chunk.
|
||||
*/
|
||||
@NotNull
|
||||
private ChunkAndInfo findNearestChunkAndInfo() {
|
||||
final int caretPosition = myView.getCaretPosition();
|
||||
|
||||
for (final ChunkAndInfo chunkAndInfo : myChunkAndInfos) {
|
||||
final Range<Integer> range = chunkAndInfo.getChunk();
|
||||
if (range != null && range.isWithin(caretPosition)) {
|
||||
return chunkAndInfo;
|
||||
}
|
||||
if (range != null && range.getFrom() > caretPosition) {
|
||||
return new ChunkAndInfo(null, chunkAndInfo.getChunkInfo());
|
||||
}
|
||||
}
|
||||
|
||||
return new ChunkAndInfo(null, myChunkAndInfos.last().getChunkInfo());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void completionRequested(@Nullable final String valueFromSuggestionList) {
|
||||
final ChunkAndInfo chunkAndInfo = findNearestChunkAndInfo();
|
||||
final WordWithPosition chunk = chunkAndInfo.getChunk();
|
||||
|
||||
if (valueFromSuggestionList != null) {
|
||||
// Just insert it
|
||||
if (chunk != null) { // If caret is on the chunk itself
|
||||
myView.replaceText(chunk.getFrom(), chunk.getTo(), valueFromSuggestionList);
|
||||
}
|
||||
else {
|
||||
myView.insertTextAfterCaret(valueFromSuggestionList);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//User did not provide text no insert, do our best to find one
|
||||
|
||||
final ChunkInfo chunkInfo = chunkAndInfo.getChunkInfo();
|
||||
final SuggestionInfo suggestionInfo = chunkInfo.getSuggestions();
|
||||
if (suggestionInfo == null) {
|
||||
return; // No suggestion available for this chunk
|
||||
}
|
||||
final List<String> suggestions = new ArrayList<String>(suggestionInfo.getSuggestions());
|
||||
if (chunk != null) {
|
||||
filterLeaveOnlyMatching(suggestions, chunk.getText());
|
||||
}
|
||||
if (suggestions.size() == 1) {
|
||||
// Exclusive!
|
||||
if (chunk != null) {
|
||||
myView.replaceText(chunk.getFrom(), chunk.getTo(), suggestions.get(0));
|
||||
}
|
||||
else {
|
||||
myView.insertTextAfterCaret(suggestions.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executionRequested() {
|
||||
if (myExecutor == null) {
|
||||
// TODO: Display error somehow
|
||||
}
|
||||
else {
|
||||
myExecutor.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Information about certain place in text, provided by chunk driver.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class ChunkInfo {
|
||||
@Nullable
|
||||
private final String myInfoBalloon;
|
||||
@Nullable
|
||||
private final String myError;
|
||||
@Nullable
|
||||
private final SuggestionInfo mySuggestions;
|
||||
|
||||
|
||||
public ChunkInfo(@Nullable final String infoBalloon,
|
||||
@Nullable final String error) {
|
||||
this(infoBalloon, error, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param infoBalloon Info balloon to display when caret meets this place (null if display nothing)
|
||||
* @param error Error balloon to display when caret meets this place and underline text as error (null if no error)
|
||||
* @param suggestions list of suggestions available in this place (if any)
|
||||
*/
|
||||
public ChunkInfo(@Nullable final String infoBalloon,
|
||||
@Nullable final String error,
|
||||
@Nullable final SuggestionInfo suggestions) {
|
||||
myInfoBalloon = infoBalloon;
|
||||
myError = error;
|
||||
mySuggestions = suggestions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Info balloon to display when caret meets this place (null if display nothing)
|
||||
*/
|
||||
@Nullable
|
||||
public String getInfoBalloon() {
|
||||
return myInfoBalloon;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Error balloon to display when caret meets this place and underline text as error (null if no error)
|
||||
*/
|
||||
@Nullable
|
||||
public String getError() {
|
||||
return myError;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return list of suggestions available in this place (if any)
|
||||
*/
|
||||
@Nullable
|
||||
public SuggestionInfo getSuggestions() {
|
||||
return mySuggestions;
|
||||
}
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Pack of {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo} for certain chunks and other parsing info.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class ParseInfo {
|
||||
@Nullable
|
||||
private final String myStatusText;
|
||||
@NotNull
|
||||
private final List<ChunkInfo> myChunkInfo = new ArrayList<ChunkInfo>();
|
||||
@Nullable
|
||||
private final Runnable myExecutor;
|
||||
|
||||
/**
|
||||
* @param chunkInfo Chunk info should match chunks in 1-to-1 manner:
|
||||
* If "chunk1 chunk2 chunk3" were provided, you then need to return list where first chunkInfo matches first chunk ets.
|
||||
* And there also should be one more chunkInfo for tail.
|
||||
* @param statusText Status text {@link com.jetbrains.python.commandInterface.CommandInterfaceView view} may display.
|
||||
* @param executor Engine to process command-line execution
|
||||
*/
|
||||
public ParseInfo(@NotNull final Collection<ChunkInfo> chunkInfo,
|
||||
@Nullable final String statusText,
|
||||
@Nullable final Runnable executor) {
|
||||
myStatusText = statusText;
|
||||
myChunkInfo.addAll(chunkInfo);
|
||||
myExecutor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple parse info with out of status text and executor
|
||||
*
|
||||
* @param chunkInfo Chunk info (See {@link #ParseInfo(java.util.Collection, String, Runnable)}
|
||||
* @see #ParseInfo(java.util.Collection, String, Runnable)
|
||||
*/
|
||||
public ParseInfo(@NotNull final Collection<ChunkInfo> chunkInfo) {
|
||||
this(chunkInfo, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Status text {@link com.jetbrains.python.commandInterface.CommandInterfaceView view} may display.
|
||||
*/
|
||||
@Nullable
|
||||
String getStatusText() {
|
||||
return myStatusText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Engine to process command-line execution
|
||||
*/
|
||||
@Nullable
|
||||
Runnable getExecutor() {
|
||||
return myExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Chunk info should match chunks in 1-to-1 manner, and there also should be one more chunkInfo for tail (
|
||||
* see {@link #ParseInfo(java.util.Collection, String, Runnable) ctor} manual)
|
||||
*/
|
||||
@NotNull
|
||||
List<ChunkInfo> getChunkInfo() {
|
||||
return Collections.unmodifiableList(myChunkInfo);
|
||||
}
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link com.jetbrains.python.commandInterface.CommandInterfacePresenter} implementation based on ideas of <strong>chunk</strong>
|
||||
* and {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver}.
|
||||
* This presenter explodes command line into several parts or chunks.
|
||||
* Chunks then passed to {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver driver} and it returns
|
||||
* all information it has about each chunk. Presenter uses this information to display chunks correctly using view.
|
||||
* To use this package, {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver} should be implemented.
|
||||
*
|
||||
* See {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriverBasedPresenter} as entry point
|
||||
*
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
@@ -16,7 +16,6 @@
|
||||
package com.jetbrains.python.commandInterface.command;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineParseResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -59,7 +58,7 @@ public interface Command {
|
||||
* Execute command
|
||||
*
|
||||
* @param module module to execute command against
|
||||
* @param commandLine command's command line
|
||||
* @param parameters command's arguments and options (just like entered by user but splitted by space)
|
||||
*/
|
||||
void execute(@NotNull final Module module, @NotNull final CommandLineParseResult commandLine);
|
||||
void execute(@NotNull final Module module, @NotNull final List<String> parameters);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command option
|
||||
* Command option.
|
||||
* It may have some long names (like --foo) and short (like -f), help text and arguments (if not flag option)
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class Option {
|
||||
@@ -40,17 +42,17 @@ public final class Option {
|
||||
private final String myHelp;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param argumentAndQuantity if option accepts argument, there should be pair of [argument_quantity, its_type_info]
|
||||
* @param help option help
|
||||
* @param shortNames option short names
|
||||
* @param longNames option long names
|
||||
* @param help option help
|
||||
* @param shortNames option short names
|
||||
* @param longNames option long names
|
||||
*/
|
||||
public Option(@Nullable final Pair<Integer, OptionArgumentInfo> argumentAndQuantity,
|
||||
@NotNull final String help,
|
||||
@NotNull final Collection<String> shortNames,
|
||||
@NotNull final Collection<String> longNames) {
|
||||
Preconditions.checkArgument(argumentAndQuantity == null || argumentAndQuantity.first > 0, "Illegal args and quantity: " + argumentAndQuantity);
|
||||
Preconditions
|
||||
.checkArgument(argumentAndQuantity == null || argumentAndQuantity.first > 0, "Illegal args and quantity: " + argumentAndQuantity);
|
||||
myArgumentAndQuantity = argumentAndQuantity;
|
||||
myShortNames.addAll(shortNames);
|
||||
myLongNames.addAll(longNames);
|
||||
@@ -65,6 +67,16 @@ public final class Option {
|
||||
return Collections.unmodifiableList(myLongNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all option names (long and short)
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getAllNames() {
|
||||
final List<String> result = new ArrayList<String>(myLongNames);
|
||||
result.addAll(myShortNames);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Option short names
|
||||
*/
|
||||
@@ -74,8 +86,7 @@ public final class Option {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return if option accepts argument -- pair of [argument_quantity, its_type_info]. Null otherwise.
|
||||
* @return if option accepts argument -- pair of [argument_quantity, its_type_info]. Null otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
public Pair<Integer, OptionArgumentInfo> getArgumentAndQuantity() {
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ import java.util.List;
|
||||
* For options, whose argument is based on certain type.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
* @see com.jetbrains.python.commandInterface.command.OptionArgumentType
|
||||
* @see OptionArgumentType
|
||||
*/
|
||||
public final class OptionTypedArgumentInfo implements OptionArgumentInfo {
|
||||
@NotNull
|
||||
@@ -42,7 +42,7 @@ public final class OptionTypedArgumentInfo implements OptionArgumentInfo {
|
||||
// We only check integer for now
|
||||
if (myType == OptionArgumentType.INTEGER) {
|
||||
try {
|
||||
// We just parse it to get exception
|
||||
// We just getCommandLineInfo it to get exception
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
Integer.parseInt(value);
|
||||
}
|
||||
|
||||
-190
@@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.commandBasedChunkDriver;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver;
|
||||
import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo;
|
||||
import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ParseInfo;
|
||||
import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.SuggestionInfo;
|
||||
import com.jetbrains.python.commandInterface.command.Argument;
|
||||
import com.jetbrains.python.commandInterface.command.ArgumentsInfo;
|
||||
import com.jetbrains.python.commandInterface.command.Command;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineParseResult;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineParser;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePartType;
|
||||
import com.jetbrains.python.commandLineParser.MalformedCommandLineException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Chunk driver that uses pack of commands.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandBasedChunkDriver implements ChunkDriver {
|
||||
@NotNull
|
||||
private final CommandLineParser myCommandLineParser;
|
||||
@NotNull
|
||||
private final Map<String, Command> myCommands = new TreeMap<String, Command>(); // To sort commands by name
|
||||
@NotNull
|
||||
private final Module myModule;
|
||||
|
||||
/**
|
||||
* @param commandLineParser parser to use
|
||||
* @param module module parsing takes place in
|
||||
* @param commands available commands
|
||||
*/
|
||||
public CommandBasedChunkDriver(@NotNull final CommandLineParser commandLineParser,
|
||||
@NotNull final Module module,
|
||||
@NotNull final Collection<? extends Command> commands) {
|
||||
myCommandLineParser = commandLineParser;
|
||||
for (final Command command : commands) {
|
||||
myCommands.put(command.getName(), command);
|
||||
}
|
||||
myModule = module;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ParseInfo parse(@NotNull final List<WordWithPosition> chunks) {
|
||||
// TODO: Refactor to add command first to prevent copy/paste
|
||||
if (chunks.isEmpty()) {
|
||||
return createBadCommandInfo(chunks.size());
|
||||
}
|
||||
|
||||
try {
|
||||
final CommandLineParseResult commandLine = myCommandLineParser.parse(chunks);
|
||||
final Command command = myCommands.get(commandLine.getCommand().getText());
|
||||
if (command == null) {
|
||||
// Bad command inserted
|
||||
return createBadCommandInfo(chunks.size());
|
||||
}
|
||||
|
||||
// Command exists, lets check its arguments
|
||||
|
||||
// TODO: Support options as well
|
||||
|
||||
// First, validate values
|
||||
final ArgumentsInfo commandArgumentsInfo = command.getArgumentsInfo();
|
||||
|
||||
|
||||
final List<ChunkInfo> chunkInfo = new ArrayList<ChunkInfo>();
|
||||
// First chunk iscommand and it seems to be ok
|
||||
chunkInfo.add(new ChunkInfo(null, null, new SuggestionInfo(false, true, myCommands.keySet())));
|
||||
|
||||
|
||||
// Now add balloons, info and suggestions
|
||||
for (int i = 0; i < commandLine.getParts().size(); i++) {
|
||||
final Pair<Boolean, Argument> argumentPair = commandArgumentsInfo.getArgument(i);
|
||||
if (argumentPair == null) { // Excess argument!
|
||||
chunkInfo.add(new ChunkInfo(null, PyBundle.message("commandLine.validation.excessArg")));
|
||||
continue;
|
||||
}
|
||||
final Argument argument = argumentPair.getSecond();
|
||||
final List<String> availableValues = argument.getAvailableValues();
|
||||
final Pair<CommandLinePartType, WordWithPosition> part = commandLine.getParts().get(i);
|
||||
if (part.first != CommandLinePartType.ARGUMENT) {
|
||||
// Only arguments are supported now, so we have nothing to say about this chunk
|
||||
chunkInfo.add(new ChunkInfo(null, null));
|
||||
}
|
||||
final String argumentValue = part.second.getText();
|
||||
String errorMessage = null;
|
||||
if (availableValues != null && !availableValues.contains(argumentValue)) {
|
||||
// Bad value
|
||||
errorMessage = PyBundle.message("commandLine.validation.argBadValue");
|
||||
}
|
||||
// Argument seems to be ok. We suggest values automatically only if value is bad
|
||||
chunkInfo.add(new ChunkInfo(argument.getHelpText(), errorMessage,
|
||||
(availableValues != null ? new SuggestionInfo(errorMessage != null, false, availableValues) : null)));
|
||||
}
|
||||
|
||||
|
||||
final Pair<Boolean, Argument> nextArgumentPair = commandArgumentsInfo.getArgument(commandLine.getParts().size());
|
||||
if (nextArgumentPair != null) {
|
||||
// Next arg exists
|
||||
final Argument nextArgument = nextArgumentPair.getSecond();
|
||||
final List<String> availableValues = nextArgument.getAvailableValues();
|
||||
// Only add error if required
|
||||
final String error = nextArgumentPair.first ? PyBundle.message("commandLine.validation.argMissing") : null;
|
||||
final ChunkInfo lastArgInfo =
|
||||
new ChunkInfo(nextArgument.getHelpText(), error,
|
||||
(availableValues != null ? new SuggestionInfo(false, false, availableValues) : null));
|
||||
chunkInfo.add(lastArgInfo);
|
||||
}
|
||||
else {
|
||||
// Looks like all arguments are satisfied. Adding empty chunk to prevent completion etc.
|
||||
// This is a hack, but with out of it last chunkinfo will always be used, even 200 chars after last place
|
||||
chunkInfo.add(new ChunkInfo(null, null));
|
||||
}
|
||||
|
||||
assert chunkInfo.size() >= chunks.size() : "Contract broken: not enough chunks";
|
||||
|
||||
return new ParseInfo(chunkInfo, command.getHelp(), new MyExecutor(command, commandLine));
|
||||
}
|
||||
catch (final MalformedCommandLineException ignored) {
|
||||
// Junk enetered!
|
||||
return createBadCommandInfo(chunks.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates parse info signaling command is bad or junk
|
||||
*
|
||||
* @param numberOfChunks number of chunks provided by user (we must return chunk info for each chunk + 1, accroding to contract)
|
||||
* @return parse info to return
|
||||
*/
|
||||
@NotNull
|
||||
private ParseInfo createBadCommandInfo(final int numberOfChunks) {
|
||||
final List<ChunkInfo> result = new ArrayList<ChunkInfo>();
|
||||
// We know that first chunk command line, but we can't say anything about outher chunks except they are bad.
|
||||
// How ever, we must say something according to contract (number of infos should be equal or greater than number of chunks)
|
||||
result
|
||||
.add(new ChunkInfo(null, PyBundle.message("commandLine.validation.badCommand"), new SuggestionInfo(true, true, myCommands.keySet())));
|
||||
for (int i = 1; i < numberOfChunks; i++) {
|
||||
result.add(
|
||||
new ChunkInfo(null, PyBundle.message("commandLine.validation.badCommand")));
|
||||
}
|
||||
|
||||
return new ParseInfo(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapter that executes command using {@link Command#execute(com.intellij.openapi.module.Module, com.jetbrains.python.commandLineParser.CommandLineParseResult)}
|
||||
*/
|
||||
private class MyExecutor implements Runnable {
|
||||
@NotNull
|
||||
private final Command myCommand;
|
||||
@NotNull
|
||||
private final CommandLineParseResult myCommandLine;
|
||||
|
||||
MyExecutor(@NotNull final Command command, @NotNull final CommandLineParseResult line) {
|
||||
myCommand = command;
|
||||
myCommandLine = line;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
myCommand.execute(myModule, myCommandLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.commandBasedRangeDriver;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.Range;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.commandInterface.command.Argument;
|
||||
import com.jetbrains.python.commandInterface.command.Command;
|
||||
import com.jetbrains.python.commandInterface.command.Option;
|
||||
import com.jetbrains.python.commandInterface.command.OptionArgumentInfo;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.Executor;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfo;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfoDriver;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.SuggestionInfo;
|
||||
import com.jetbrains.python.commandLineParser.CommandLine;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineParser;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePart;
|
||||
import com.jetbrains.python.commandLineParser.MalformedCommandLineException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Driver that returns pack of range infos for certain command line
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandBasedRangeInfoDriver implements RangeInfoDriver {
|
||||
@NotNull
|
||||
private final Map<String, Command> myCommands = new TreeMap<String, Command>(); // To sort commands by name
|
||||
@NotNull
|
||||
private final Module myModule;
|
||||
@NotNull
|
||||
private final CommandLineParser myCommandLineParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param module module (to be used in execution)
|
||||
* @param commandLineParser parser to parse command lines
|
||||
* @param commands available commands
|
||||
*/
|
||||
public CommandBasedRangeInfoDriver(
|
||||
@NotNull final Module module,
|
||||
@NotNull final CommandLineParser commandLineParser,
|
||||
@NotNull final Collection<? extends Command> commands) {
|
||||
for (final Command command : commands) {
|
||||
myCommands.put(command.getName(), command);
|
||||
}
|
||||
myModule = module;
|
||||
myCommandLineParser = commandLineParser;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Pair<Executor, List<RangeInfo>> getCommandLineInfo(@NotNull final String commandLineText) {
|
||||
// TODO: Copty/paste with exceptiojn
|
||||
if (StringUtil.isEmpty(commandLineText)) {
|
||||
final RangeInfo info = new RangeInfo(null, "", new SuggestionInfo(
|
||||
true, true, myCommands.keySet()
|
||||
), RangeInfo.TERMINATION_RANGE, false
|
||||
);
|
||||
return new Pair<Executor, List<RangeInfo>>(null, Collections.singletonList(info));
|
||||
}
|
||||
final CommandLine commandLine;
|
||||
try {
|
||||
commandLine = myCommandLineParser.parse(commandLineText);
|
||||
}
|
||||
catch (final MalformedCommandLineException ignored) {
|
||||
return new Pair<Executor, List<RangeInfo>>(null, Collections.singletonList(
|
||||
new RangeInfo(null, PyBundle.message("commandLine.validation.badCommand"), new SuggestionInfo(
|
||||
false, true, myCommands.keySet()
|
||||
), new Range<Integer>(0, commandLineText.length()), false)
|
||||
));
|
||||
}
|
||||
|
||||
final Command command = getExistingCommand(commandLine);
|
||||
final List<CommandLinePart> commandLineParts = commandLine.getParts();
|
||||
if (command == null) {
|
||||
// Bad command inserted
|
||||
return createBadCommandInfo(commandLine);
|
||||
}
|
||||
|
||||
final UnusedOptionsCollector unusedOptionsCollector = UnusedOptionsCollector.create(command, commandLineParts);
|
||||
final RangeInfoCollector rangeInfoCollector = RangeInfoCollector.create(command, commandLineParts, unusedOptionsCollector);
|
||||
|
||||
|
||||
|
||||
final SuggestionInfo commandSuggestions = new SuggestionInfo(false, true, myCommands.keySet());
|
||||
// Add command as first range info
|
||||
final List<RangeInfo> rangeInfos =
|
||||
new ArrayList<RangeInfo>(Collections.singletonList(new RangeInfo(null, null, commandSuggestions, commandLine.getCommand(), false)));
|
||||
// Then add collected infos
|
||||
rangeInfos.addAll(rangeInfoCollector.getRangeInfos());
|
||||
|
||||
|
||||
/////// What about "after the caret" ?
|
||||
final Pair<Boolean, Argument> unsatisfiedArgument = rangeInfoCollector.getUnsatisfiedPositionalArgument();
|
||||
final OptionArgumentInfo unsatisfiedOptionArgument = rangeInfoCollector.getUnsatisfiedOptionArgument();
|
||||
|
||||
|
||||
// TODO: Move to collector after test
|
||||
if (unsatisfiedOptionArgument != null) {
|
||||
final List<String> availableValues = unsatisfiedOptionArgument.getAvailableValues();
|
||||
final SuggestionInfo suggestionInfo;
|
||||
if (availableValues != null) {
|
||||
|
||||
suggestionInfo =
|
||||
new SuggestionInfo(false, false, availableValues);
|
||||
}
|
||||
else {
|
||||
suggestionInfo = null;
|
||||
}
|
||||
rangeInfos
|
||||
.add(new RangeInfo(null, PyBundle.message("commandLine.validation.optArgMissing"), suggestionInfo, RangeInfo.TERMINATION_RANGE,
|
||||
false));
|
||||
}
|
||||
else if (unsatisfiedArgument != null) {
|
||||
final boolean required = unsatisfiedArgument.first;
|
||||
final Argument argument = unsatisfiedArgument.second;
|
||||
// Only add error if required
|
||||
final String error = required ? PyBundle.message("commandLine.validation.argMissing") : null;
|
||||
final List<String> availableValues = unusedOptionsCollector.addUnusedOptions(argument.getAvailableValues());
|
||||
final RangeInfo lastArgInfo =
|
||||
new RangeInfo(argument.getHelpText(), error,
|
||||
(availableValues != null ? new SuggestionInfo(false, false, availableValues) : null), RangeInfo.TERMINATION_RANGE,
|
||||
false);
|
||||
rangeInfos.add(lastArgInfo);
|
||||
}
|
||||
else {
|
||||
// Looks like all arguments are satisfied. Adding empty chunk to prevent completion etc.
|
||||
// This is a hack, but with out of it last range info will always be used, even 200 chars after last place
|
||||
rangeInfos.add(new RangeInfo(null, null, rangeInfoCollector.getCurrentSuggestions(false, null), RangeInfo.TERMINATION_RANGE, false));
|
||||
}
|
||||
|
||||
assert rangeInfos.size() >= commandLineParts.size() : "Contract broken: not enough chunks";
|
||||
return Pair.<Executor, List<RangeInfo>>create(new CommandExecutor(command, myModule, commandLine.getPartsAsText()), rangeInfos);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Command getExistingCommand(@NotNull final CommandLine commandLine) {
|
||||
return myCommands.get(commandLine.getCommand().getText());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates range info info signaling command is bad or junk
|
||||
*
|
||||
*
|
||||
* @param commandLine command line passed by user
|
||||
* @return info to return
|
||||
*/
|
||||
@NotNull
|
||||
private Pair<Executor, List<RangeInfo>> createBadCommandInfo(final CommandLine commandLine) {
|
||||
final List<RangeInfo> result = new ArrayList<RangeInfo>();
|
||||
// We know that first chunk command line, but we can't say anything about outher chunks except they are bad.
|
||||
// How ever, we must say something
|
||||
result
|
||||
.add(new RangeInfo(null, PyBundle.message("commandLine.validation.badCommand"), new SuggestionInfo(true, true, myCommands.keySet()),
|
||||
commandLine.getCommand(), false));
|
||||
// Command is unknown, all other parts are junk
|
||||
for (final CommandLinePart part : commandLine.getParts()) {
|
||||
result.add(new RangeInfo(null, "", false, part.getWord()));
|
||||
}
|
||||
|
||||
|
||||
return Pair.create(null, result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds option by its name
|
||||
* @param command current command
|
||||
* @param optionName option name
|
||||
* @return option or null if no option found
|
||||
*/
|
||||
@Nullable
|
||||
static Option findOptionByName(@NotNull final Command command, @NotNull final String optionName) {
|
||||
for (final Option option : command.getOptions()) {
|
||||
for (final String name : option.getAllNames()) {
|
||||
if (name.equals(optionName)) {
|
||||
return option;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.commandBasedRangeDriver;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.python.commandInterface.command.Command;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.Executor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Executes commands according to {@link Executor} contract
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class CommandExecutor implements Executor {
|
||||
@NotNull
|
||||
private final Command myCommand;
|
||||
@NotNull
|
||||
private final Module myModule;
|
||||
@NotNull
|
||||
private final String[] myArguments;
|
||||
|
||||
/**
|
||||
* @param command command to execute
|
||||
* @param module module to execute against
|
||||
* @param argumentsLine all command arguments as testline
|
||||
*/
|
||||
CommandExecutor(@NotNull final Command command, @NotNull final Module module, @NotNull final String argumentsLine) {
|
||||
myCommand = command;
|
||||
myModule = module;
|
||||
myArguments = (StringUtil.isEmpty(argumentsLine) ? ArrayUtil.EMPTY_STRING_ARRAY : argumentsLine.split(" "));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getExecutionDescription() {
|
||||
return myCommand.getHelp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
myCommand.execute(myModule, Arrays.asList(myArguments));
|
||||
}
|
||||
}
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.commandBasedRangeDriver;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.jetbrains.python.commandInterface.command.Argument;
|
||||
import com.jetbrains.python.commandInterface.command.Command;
|
||||
import com.jetbrains.python.commandInterface.command.Option;
|
||||
import com.jetbrains.python.commandInterface.command.OptionArgumentInfo;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfo;
|
||||
import com.jetbrains.python.commandInterface.rangeBasedPresenter.SuggestionInfo;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineArgument;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineOption;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePart;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePartVisitor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Visitor that collections ranges info by visiting options and arguments
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class RangeInfoCollector implements CommandLinePartVisitor {
|
||||
/**
|
||||
* Options already met in command line. It has nothing to do with {@link UnusedOptionsCollector}!
|
||||
*/
|
||||
@NotNull
|
||||
private final Set<String> myUsedOptions = new HashSet<String>();
|
||||
@NotNull
|
||||
private final List<RangeInfo> myRangeInfos = new ArrayList<RangeInfo>();
|
||||
@NotNull
|
||||
private final Command myCommand;
|
||||
|
||||
private int myNumOfProcessedPositionalArguments = 0;
|
||||
private boolean mySkipNextArgument;
|
||||
private Pair<Integer, OptionArgumentInfo> myExpectedOptionArgument;
|
||||
@NotNull
|
||||
private final UnusedOptionsCollector myUnusedOptionsCollector;
|
||||
|
||||
/**
|
||||
* @param command command
|
||||
* @param unusedOptionsCollector instance of unused options collector
|
||||
*/
|
||||
private RangeInfoCollector(@NotNull final Command command,
|
||||
@NotNull final UnusedOptionsCollector unusedOptionsCollector) {
|
||||
myCommand = command;
|
||||
myUnusedOptionsCollector = unusedOptionsCollector;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static RangeInfoCollector create(@NotNull final Command command,
|
||||
@NotNull final Iterable<CommandLinePart> commandLineParts,
|
||||
@NotNull final UnusedOptionsCollector unusedOptionsCollector) {
|
||||
final RangeInfoCollector infoCollector = new RangeInfoCollector(command, unusedOptionsCollector);
|
||||
for (final CommandLinePart part : commandLineParts) {
|
||||
part.accept(infoCollector);
|
||||
}
|
||||
return infoCollector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOption(@NotNull final CommandLineOption option) {
|
||||
if (processOptionArgument(option.getWord())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final Option commandOption = CommandBasedRangeInfoDriver.findOptionByName(myCommand, option.getOptionName());
|
||||
final WordWithPosition attachedArgument = option.getAttachedArgument();
|
||||
if (commandOption == null) {
|
||||
// There is no such option
|
||||
// To Mark attached arg so we skip it in {@link #visitArgument}
|
||||
if (attachedArgument != null) {
|
||||
mySkipNextArgument = true;
|
||||
}
|
||||
// No such option
|
||||
myRangeInfos.add(new RangeInfo(null, PyBundle.message("commandLine.validation.badOption"), getCurrentSuggestions(false, null),
|
||||
option.getWord(), false));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final Pair<Integer, OptionArgumentInfo> argumentAndQuantity = commandOption.getArgumentAndQuantity();
|
||||
|
||||
|
||||
// If option already used, then mark it
|
||||
final String optionAlreadyUsed = Sets.intersection(Sets.newHashSet(commandOption.getAllNames()), myUsedOptions).isEmpty()
|
||||
? null
|
||||
: PyBundle.message("commandLine.validation.badOption");
|
||||
myRangeInfos
|
||||
.add(new RangeInfo(commandOption.getHelp(), optionAlreadyUsed, getCurrentSuggestions(false, null), option.getWord(),
|
||||
argumentAndQuantity != null));
|
||||
|
||||
// remove from existing options
|
||||
myUsedOptions.addAll(commandOption.getAllNames());
|
||||
|
||||
|
||||
if (argumentAndQuantity == null) {
|
||||
if (attachedArgument != null) {
|
||||
myRangeInfos.add(new RangeInfo(null, PyBundle.message("commandLine.validation.noArgAllowed"), false, attachedArgument));
|
||||
mySkipNextArgument = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Some option args required
|
||||
myExpectedOptionArgument = argumentAndQuantity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process option argument (not to be confused with positional!)
|
||||
*
|
||||
* @param currentPart part with argument.
|
||||
* @return false if there should not be any option argument so this method did nothing. True if there should be and it is processed.
|
||||
*/
|
||||
private boolean processOptionArgument(@NotNull final WordWithPosition currentPart) {
|
||||
if (myExpectedOptionArgument == null) {
|
||||
return false;
|
||||
}
|
||||
final OptionArgumentInfo argumentInfo = myExpectedOptionArgument.second;
|
||||
int argsLeft = myExpectedOptionArgument.first;
|
||||
|
||||
final boolean valid = argumentInfo.isValid(currentPart.getText());
|
||||
final List<String> availableValues = argumentInfo.getAvailableValues();
|
||||
final SuggestionInfo suggestions;
|
||||
if (availableValues != null) {
|
||||
suggestions = new SuggestionInfo(false, false, availableValues);
|
||||
}
|
||||
else {
|
||||
suggestions = null;
|
||||
}
|
||||
|
||||
myRangeInfos.add(new RangeInfo(null, (valid ? null : PyBundle.message("commandLine.validation.argBadValue")),
|
||||
suggestions, currentPart, false));
|
||||
|
||||
|
||||
if (--argsLeft == 0) {
|
||||
myExpectedOptionArgument = null;
|
||||
}
|
||||
else {
|
||||
myExpectedOptionArgument = Pair.create(argsLeft, argumentInfo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns suggestions available for current position.
|
||||
*
|
||||
* @param showAutomatically make suggestions displayed automatically
|
||||
* @param argumentPair argument to use. If null, current argument according to internal counter would be used.
|
||||
* @return suggestiom info or null if no suggestion available
|
||||
*/
|
||||
@Nullable
|
||||
SuggestionInfo getCurrentSuggestions(final boolean showAutomatically, @Nullable Pair<Boolean, Argument> argumentPair) {
|
||||
if (argumentPair == null) {
|
||||
argumentPair = myCommand.getArgumentsInfo().getArgument(myNumOfProcessedPositionalArguments);
|
||||
}
|
||||
|
||||
final List<String> suggestions = argumentPair != null ? myUnusedOptionsCollector.addUnusedOptions(
|
||||
argumentPair.second.getAvailableValues()) : myUnusedOptionsCollector.addUnusedOptions(
|
||||
null);
|
||||
if (suggestions == null) {
|
||||
return null;
|
||||
}
|
||||
return new SuggestionInfo(showAutomatically, false, suggestions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitArgument(@NotNull final CommandLineArgument argument) {
|
||||
if (mySkipNextArgument) {
|
||||
// Skip argument, clear flag and do nothing
|
||||
mySkipNextArgument = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (processOptionArgument(argument.getWord())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Pair<Boolean, Argument> argumentPair = myCommand.getArgumentsInfo().getArgument(myNumOfProcessedPositionalArguments++);
|
||||
if (argumentPair == null) {
|
||||
//Exceed!
|
||||
myRangeInfos.add(new RangeInfo(null, PyBundle.message("commandLine.validation.excessArg"), false, argument.getWord()));
|
||||
return;
|
||||
}
|
||||
final Argument commandArgument = argumentPair.second;
|
||||
|
||||
final List<String> argumentAvailableValues = commandArgument.getAvailableValues();
|
||||
final String argumentValue = argument.getWord().getText();
|
||||
String errorMessage = null;
|
||||
if (argumentAvailableValues != null && !argumentAvailableValues.contains(argumentValue)) {
|
||||
// Bad value
|
||||
errorMessage = PyBundle.message("commandLine.validation.argBadValue");
|
||||
}
|
||||
// Argument seems to be ok. We suggest values automatically only if value is bad
|
||||
myRangeInfos.add(new RangeInfo(commandArgument.getHelpText(), errorMessage,
|
||||
getCurrentSuggestions(errorMessage != null, argumentPair),
|
||||
argument.getWord(), false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return calculates range infos for all command line parts (not the command itself!)
|
||||
*/
|
||||
@NotNull
|
||||
Collection<RangeInfo> getRangeInfos() {
|
||||
return Collections.unmodifiableList(myRangeInfos);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Unsatisfied (currently expected) positional argument ([required, arg]) or null if no arg expected
|
||||
*/
|
||||
@Nullable
|
||||
Pair<Boolean, Argument> getUnsatisfiedPositionalArgument() {
|
||||
return myCommand.getArgumentsInfo().getArgument(myNumOfProcessedPositionalArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Unsatisfied (currently expected) option argument or null if no option argument expected
|
||||
*/
|
||||
public OptionArgumentInfo getUnsatisfiedOptionArgument() {
|
||||
if (myExpectedOptionArgument != null) {
|
||||
return myExpectedOptionArgument.second;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.commandBasedRangeDriver;
|
||||
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.python.commandInterface.command.Command;
|
||||
import com.jetbrains.python.commandInterface.command.Option;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineArgument;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineOption;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePart;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePartVisitor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Collections all used and unused options to report unused options then.
|
||||
* It may be used to add unused options to suggestion list {@link #addUnusedOptions(Collection)}
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class UnusedOptionsCollector implements CommandLinePartVisitor {
|
||||
@NotNull
|
||||
private final Set<String> myUnusedOptions = new HashSet<String>();
|
||||
@NotNull
|
||||
private final Command myCommand;
|
||||
|
||||
/**
|
||||
* @param command command to be used
|
||||
* @param commandLineParts command line parts
|
||||
* @return instance
|
||||
*/
|
||||
static UnusedOptionsCollector create(@NotNull final Command command, @NotNull final Iterable<CommandLinePart> commandLineParts) {
|
||||
final UnusedOptionsCollector collector = new UnusedOptionsCollector(command);
|
||||
for (final CommandLinePart part : commandLineParts) {
|
||||
part.accept(collector);
|
||||
}
|
||||
return collector;
|
||||
}
|
||||
|
||||
private UnusedOptionsCollector(@NotNull final Command command) {
|
||||
for (final Option option : command.getOptions()) {
|
||||
myUnusedOptions.addAll(option.getAllNames());
|
||||
}
|
||||
myCommand = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOption(@NotNull final CommandLineOption option) {
|
||||
final Option commandOption = CommandBasedRangeInfoDriver.findOptionByName(myCommand, option.getOptionName());
|
||||
if (commandOption != null) {
|
||||
myUnusedOptions.removeAll(commandOption.getAllNames());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges list of unused options and other values provided as argument.
|
||||
*
|
||||
* @param mainValues values to merge options with. May be null.
|
||||
* @return null of no options and no values provided or merged list of values and options. See method usages for more info
|
||||
*/
|
||||
@Nullable
|
||||
List<String> addUnusedOptions(@Nullable final Collection<String> mainValues) {
|
||||
if (mainValues == null && myUnusedOptions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final List<String> result = new ArrayList<String>(myUnusedOptions);
|
||||
|
||||
if (mainValues != null) {
|
||||
result.addAll(mainValues);
|
||||
}
|
||||
return (result.isEmpty() ? null : result);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visitArgument(@NotNull final CommandLineArgument argument) {
|
||||
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver} implementation based on idea of
|
||||
* {@link com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfoDriver} implementation based on idea of
|
||||
* {@link com.jetbrains.python.commandInterface.command command, option and argument}.
|
||||
*
|
||||
* See {@link com.jetbrains.python.commandInterface.commandBasedChunkDriver.CommandBasedChunkDriver} as entry point.
|
||||
* See {@link com.jetbrains.python.commandInterface.commandBasedRangeDriver.CommandBasedRangeInfoDriver} as entry point.
|
||||
* It parses command line using {@link com.jetbrains.python.commandLineParser.CommandLineParser} and finds matching command and arguments
|
||||
* provided by user
|
||||
*
|
||||
@@ -26,4 +26,4 @@
|
||||
* @see com.jetbrains.python.commandInterface.command.Command
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
|
||||
package com.jetbrains.python.commandInterface.commandBasedRangeDriver;
|
||||
@@ -31,7 +31,7 @@
|
||||
*
|
||||
* <p>
|
||||
* There is also swing-based view implementation in {@link com.jetbrains.python.commandInterface.swingView}
|
||||
* and presenter implementation based on idea of commands with arguments. See {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter}
|
||||
* and presenter implementation based on idea of commands with arguments. See {@link com.jetbrains.python.commandInterface.rangeBasedPresenter}
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.rangeBasedPresenter;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Engine to execute command line
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface Executor {
|
||||
/**
|
||||
* @return information about what to execute (like command name)
|
||||
*/
|
||||
@Nullable
|
||||
String getExecutionDescription();
|
||||
|
||||
/**
|
||||
* Execute command!
|
||||
*/
|
||||
void execute();
|
||||
}
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.rangeBasedPresenter;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.Range;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfacePresenterAdapter;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView;
|
||||
import com.jetbrains.python.suggestionList.SuggestionsBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* Presenter that uses {@link RangeInfoDriver} to obtain {@link RangeInfo info}.
|
||||
* Such info tells presenter whether this text has info, error, suggestions and so on.
|
||||
* If caret situated far from text, then next neariest range should be found (see {@link #findNearestRangeInfo()}.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
* @see RangeInfo
|
||||
*/
|
||||
public final class RangeBasedPresenter extends CommandInterfacePresenterAdapter {
|
||||
@NotNull
|
||||
private final RangeInfoDriver myRangeInfoDriver;
|
||||
@Nullable
|
||||
private Executor myExecutor;
|
||||
private final SortedSet<RangeInfo> myRangeInfos = new TreeSet<RangeInfo>();
|
||||
|
||||
public RangeBasedPresenter(@NotNull final CommandInterfaceView view,
|
||||
@NotNull final RangeInfoDriver rangeInfoDriver) {
|
||||
super(view);
|
||||
myRangeInfoDriver = rangeInfoDriver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch() {
|
||||
super.launch();
|
||||
reparseText(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textChanged() {
|
||||
reparseText(false);
|
||||
}
|
||||
|
||||
private void reparseText(final boolean skipSuggestions) {
|
||||
|
||||
|
||||
final Pair<Executor, List<RangeInfo>> rangeInfoStructure = myRangeInfoDriver.getCommandLineInfo(myView.getText());
|
||||
myExecutor = rangeInfoStructure.first;
|
||||
final List<RangeInfo> rangeInfos = rangeInfoStructure.second;
|
||||
|
||||
assert !rangeInfos.isEmpty() : "At least one chunk info should exist";
|
||||
myRangeInfos.clear();
|
||||
myRangeInfos.addAll(rangeInfos);
|
||||
|
||||
|
||||
// configure Errors And Balloons
|
||||
|
||||
final Collection<WordWithPosition> infoBalloons = new ArrayList<WordWithPosition>();
|
||||
final Collection<WordWithPosition> errorBalloons = new ArrayList<WordWithPosition>();
|
||||
|
||||
|
||||
for (final RangeInfo rangeInfo : rangeInfos) {
|
||||
final String error = rangeInfo.getError();
|
||||
if (error != null) {
|
||||
errorBalloons.add(new WordWithPosition(error, rangeInfo));
|
||||
}
|
||||
final String info = rangeInfo.getInfoBalloon();
|
||||
if (info != null) {
|
||||
infoBalloons.add(new WordWithPosition(info, rangeInfo));
|
||||
}
|
||||
}
|
||||
|
||||
myView.setInfoAndErrors(infoBalloons, errorBalloons);
|
||||
|
||||
|
||||
if (!skipSuggestions) {
|
||||
configureSuggestion(false);
|
||||
}
|
||||
|
||||
// Configure subtexts
|
||||
final List<Range<Integer>> placesWhereSuggestionAvailable = new ArrayList<Range<Integer>>();
|
||||
for (final RangeInfo rangeInfo : rangeInfos) {
|
||||
// If some place has suggestions, then add it
|
||||
if (rangeInfo.getSuggestions() != null) {
|
||||
placesWhereSuggestionAvailable.add(rangeInfo);
|
||||
}
|
||||
}
|
||||
final String statusText = (myExecutor != null ? myExecutor.getExecutionDescription() : null);
|
||||
myView.configureSubTexts(statusText, placesWhereSuggestionAvailable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestionRequested() {
|
||||
configureSuggestion(true); // Show or hide
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Displays suggestions if needed.
|
||||
*
|
||||
* @param requestedExplicitly is suggesions where requested by user explicitly or not
|
||||
*/
|
||||
private void configureSuggestion(final boolean requestedExplicitly) {
|
||||
myView.removeSuggestions();
|
||||
final RangeInfo rangeInfo = findNearestRangeInfo();
|
||||
final String text = getTextByRange(rangeInfo);
|
||||
|
||||
final SuggestionInfo suggestionInfo = rangeInfo.getSuggestions();
|
||||
if (suggestionInfo == null || (!suggestionInfo.isShowSuggestionsAutomatically() && !requestedExplicitly)) {
|
||||
return;
|
||||
}
|
||||
final List<String> suggestions = new ArrayList<String>(suggestionInfo.getSuggestions());
|
||||
if (text != null && !requestedExplicitly) {
|
||||
filterLeaveOnlyMatching(suggestions, text);
|
||||
}
|
||||
// TODO: Place to add history
|
||||
if (!suggestions.isEmpty()) {
|
||||
// No need to display empty suggestions
|
||||
myView
|
||||
.displaySuggestions(new SuggestionsBuilder(suggestions), suggestionInfo.isShowAbsolute(), text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters collection of suggestions leaving only those starts with certain text.
|
||||
*
|
||||
* @param suggestions list to filter
|
||||
* @param textToMatch leave only parts that start with this param
|
||||
*/
|
||||
private static void filterLeaveOnlyMatching(@NotNull final Iterable<String> suggestions, @NotNull final String textToMatch) {
|
||||
// TODO: use guava instead?
|
||||
final Iterator<String> iterator = suggestions.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (!iterator.next().startsWith(textToMatch)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the nearest range to use.
|
||||
*
|
||||
* @return nearest range info
|
||||
*/
|
||||
@NotNull
|
||||
private RangeInfo findNearestRangeInfo() {
|
||||
final int caretPosition = myView.getCaretPosition();
|
||||
|
||||
for (final RangeInfo range : myRangeInfos) {
|
||||
if (range.isWithin(caretPosition)) {
|
||||
return range;
|
||||
}
|
||||
if (range.getFrom() > caretPosition) {
|
||||
return range; // Ranges are sorted, so we are on the next range. Take it, if caret is not within range
|
||||
}
|
||||
}
|
||||
|
||||
return myRangeInfos.last();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void completionRequested(@Nullable final String valueFromSuggestionList) {
|
||||
final RangeInfo rangeInfo = findNearestRangeInfo();
|
||||
final String text = getTextByRange(rangeInfo);
|
||||
|
||||
if (valueFromSuggestionList != null) {
|
||||
// Just insert it
|
||||
if (text != null) { // If caret is on the text itself
|
||||
// TODO: Replace next range, if it has no space before it(--a=12 should be replaced wth arg)
|
||||
myView.replaceText(rangeInfo.getFrom(), rangeInfo.getTo(), valueFromSuggestionList);
|
||||
}
|
||||
else {
|
||||
myView.insertTextAfterCaret(valueFromSuggestionList);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//User did not provide text no insert, do our best to find one
|
||||
|
||||
final SuggestionInfo suggestionInfo = rangeInfo.getSuggestions();
|
||||
if (suggestionInfo == null) {
|
||||
return; // No suggestion available for this chunk
|
||||
}
|
||||
final List<String> suggestions = new ArrayList<String>(suggestionInfo.getSuggestions());
|
||||
if (text != null) {
|
||||
filterLeaveOnlyMatching(suggestions, text);
|
||||
}
|
||||
if (suggestions.size() == 1) {
|
||||
// Exclusive!
|
||||
if (text != null) {
|
||||
// TODO: Replace next range, if it has no space before it(--a=12 should be replaced wth arg)
|
||||
myView.replaceText(rangeInfo.getFrom(), rangeInfo.getTo(), suggestions.get(0));
|
||||
}
|
||||
else {
|
||||
myView.insertTextAfterCaret(suggestions.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for text under the range
|
||||
* @param rangeInfo range
|
||||
* @return text or null if range does not contain any text
|
||||
*/
|
||||
@Nullable
|
||||
private String getTextByRange(@NotNull final RangeInfo rangeInfo) {
|
||||
|
||||
if (rangeInfo.isTerminationRange()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
final String viewText = myView.getText();
|
||||
return viewText.substring(rangeInfo.getFrom(), rangeInfo.getTo());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executionRequested() {
|
||||
if (myExecutor == null) {
|
||||
// TODO: Display error somehow
|
||||
}
|
||||
else {
|
||||
myExecutor.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.rangeBasedPresenter;
|
||||
|
||||
import com.intellij.util.Range;
|
||||
import com.jetbrains.python.commandInterface.CommandInterfaceView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Information about certain place in text, provided by driver.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class RangeInfo extends Range<Integer> implements Comparable<RangeInfo> {
|
||||
|
||||
|
||||
/**
|
||||
* Special "terminator": the last range which is after all other ranges.
|
||||
*/
|
||||
public static final Range<Integer> TERMINATION_RANGE = CommandInterfaceView.AFTER_LAST_CHARACTER_RANGE;
|
||||
|
||||
@Nullable
|
||||
private final String myInfoBalloon;
|
||||
@Nullable
|
||||
private final String myError;
|
||||
@Nullable
|
||||
private final SuggestionInfo mySuggestions;
|
||||
private final boolean myExclusiveBorders;
|
||||
|
||||
|
||||
/**
|
||||
* @param infoBalloon info to bind to this range
|
||||
* @param error error to bind to this range
|
||||
* @param exclusiveBorders true if range has exclusive borders and right border is not part of it. I.e. 3 is part of 1-3 range with out
|
||||
* of exclusive borders, but not part of exclusive range
|
||||
* @param range from and to
|
||||
*/
|
||||
public RangeInfo(@Nullable final String infoBalloon,
|
||||
@Nullable final String error,
|
||||
final boolean exclusiveBorders,
|
||||
@NotNull final Range<Integer> range) {
|
||||
this(infoBalloon, error, null, range, exclusiveBorders);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param infoBalloon Info balloon to display when caret meets this place (null if display nothing)
|
||||
* @param error Error balloon to display when caret meets this place and underline text as error (null if no error)
|
||||
* @param suggestions list of suggestions available in this place (if any)
|
||||
*/
|
||||
public RangeInfo(@Nullable final String infoBalloon,
|
||||
@Nullable final String error,
|
||||
@Nullable final SuggestionInfo suggestions,
|
||||
@NotNull final Range<Integer> range,
|
||||
final boolean exclusiveBorders) {
|
||||
super(range.getFrom(), range.getTo());
|
||||
myInfoBalloon = infoBalloon;
|
||||
myError = error;
|
||||
mySuggestions = suggestions;
|
||||
myExclusiveBorders = exclusiveBorders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Info balloon to display when caret meets this place (null if display nothing)
|
||||
*/
|
||||
@Nullable
|
||||
public String getInfoBalloon() {
|
||||
return myInfoBalloon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Error balloon to display when caret meets this place and underline text as error (null if no error)
|
||||
*/
|
||||
@Nullable
|
||||
public String getError() {
|
||||
return myError;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of suggestions available in this place (if any)
|
||||
*/
|
||||
@Nullable
|
||||
public SuggestionInfo getSuggestions() {
|
||||
return mySuggestions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(RangeInfo o) {
|
||||
return getFrom().compareTo(o.getFrom());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(final Integer object) {
|
||||
if (!super.isWithin(object)) {
|
||||
return false;
|
||||
}
|
||||
if (myExclusiveBorders) {
|
||||
return object < getTo();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isTerminationRange() {
|
||||
// TODO: copy/paste with view
|
||||
return TERMINATION_RANGE.getFrom().equals(getFrom()) && TERMINATION_RANGE.getTo().equals(getTo());
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandInterface.rangeBasedPresenter;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Driver that knows how to getCommandLineInfo pack of chunks into chunk info.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface RangeInfoDriver {
|
||||
/**
|
||||
* Parses command line text into executor and pack of range infos.
|
||||
* There <strong>always</strong> should be at least one range info and the last one is almost always {@link RangeInfo#TERMINATION_RANGE).
|
||||
*
|
||||
* @param commandLineText command line text to parse.
|
||||
* @return pair or executor (the one that knows how to execute command line) and pack of range infos.
|
||||
* <strong>Warning! </strong> :Executor could be null if command can't be executed
|
||||
*/
|
||||
@NotNull
|
||||
Pair<Executor, List<RangeInfo>> getCommandLineInfo(@NotNull String commandLineText);
|
||||
}
|
||||
+10
-1
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
package com.jetbrains.python.commandInterface.rangeBasedPresenter;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -67,4 +67,13 @@ public final class SuggestionInfo {
|
||||
public boolean isShowAbsolute() {
|
||||
return myShowAbsolute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SuggestionInfo{" +
|
||||
"mySuggestions=" + mySuggestions +
|
||||
", myShowSuggestionsAutomatically=" + myShowSuggestionsAutomatically +
|
||||
", myShowAbsolute=" + myShowAbsolute +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link com.jetbrains.python.commandInterface.CommandInterfacePresenter} implementation based on ideas of <strong>range info</strong>
|
||||
* and {@link com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfoDriver}.
|
||||
* This presenter passes command line to driver, and it returns pack of range information.
|
||||
* Each record contains everything presenter needs to know about certain range (i.e. error between 2 and 5 chars).
|
||||
* Special type of range {@link com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfo#TERMINATION_RANGE} exists, that
|
||||
* you may need to check.
|
||||
* <p/>
|
||||
* Presenter uses this information to display chunks correctly using view.
|
||||
* To use this package, {@link com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeInfoDriver} should be implemented.
|
||||
* <p/>
|
||||
* See {@link com.jetbrains.python.commandInterface.rangeBasedPresenter.RangeBasedPresenter} as entry point
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.rangeBasedPresenter;
|
||||
+19
-19
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.jetbrains.python.commandLineParser;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -25,22 +25,22 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command line parse result.
|
||||
* Command line getCommandLineInfo result.
|
||||
* It consists of command itself and its parts.
|
||||
* Each part may be {@link com.jetbrains.python.commandLineParser.CommandLinePartType#ARGUMENT argument} or
|
||||
* {@link com.jetbrains.python.commandLineParser.CommandLinePartType#OPTION option} or something else.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineParseResult {
|
||||
public final class CommandLine {
|
||||
@NotNull
|
||||
private final List<Pair<CommandLinePartType, WordWithPosition>> myParts = new ArrayList<Pair<CommandLinePartType, WordWithPosition>>();
|
||||
private final List<CommandLinePart> myParts = new ArrayList<CommandLinePart>();
|
||||
@NotNull
|
||||
private final WordWithPosition myCommand;
|
||||
|
||||
public CommandLineParseResult(
|
||||
public CommandLine(
|
||||
@NotNull final WordWithPosition command,
|
||||
@NotNull final Collection<Pair<CommandLinePartType, WordWithPosition>> parts) {
|
||||
@NotNull final Collection<CommandLinePart> parts) {
|
||||
myCommand = command;
|
||||
myParts.addAll(parts);
|
||||
}
|
||||
@@ -53,26 +53,26 @@ public final class CommandLineParseResult {
|
||||
return myCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of parts in format [part_type, value].
|
||||
* For example (rm my_folder): [{@link com.jetbrains.python.commandLineParser.CommandLinePartType#ARGUMENT argument}, my_folder]
|
||||
*/
|
||||
@NotNull
|
||||
public List<Pair<CommandLinePartType, WordWithPosition>> getParts() {
|
||||
public List<CommandLinePart> getParts() {
|
||||
return Collections.unmodifiableList(myParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all command line parts with out of part information (just words and positions).
|
||||
* Note tha command itself is not part, only args and options are
|
||||
* @see #getParts()
|
||||
* @return all command parts as text (actually by bindning them back together)
|
||||
*/
|
||||
@NotNull
|
||||
public Collection<WordWithPosition> getPartsNoType() {
|
||||
final Collection<WordWithPosition> result = new ArrayList<WordWithPosition>();
|
||||
for (final Pair<CommandLinePartType, WordWithPosition> part : myParts) {
|
||||
result.add(part.second);
|
||||
public String getPartsAsText() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
int lastPosition = 0;
|
||||
for (final CommandLinePart part : getParts()) {
|
||||
final WordWithPosition partWord = part.getWord();
|
||||
if (lastPosition != partWord.getFrom()) {
|
||||
builder.append(' ');
|
||||
}
|
||||
builder.append(partWord.getText());
|
||||
lastPosition = partWord.getTo();
|
||||
}
|
||||
return result;
|
||||
return StringUtil.trim(builder.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandLineParser;
|
||||
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Positional or option argument.
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineArgument extends CommandLinePart {
|
||||
|
||||
public CommandLineArgument(@NotNull final WordWithPosition argumentValue) {
|
||||
super(argumentValue);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void accept(@NotNull final CommandLinePartVisitor visitor) {
|
||||
visitor.visitArgument(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandLineParser;
|
||||
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Option. Each option has name and may also have attached argument like "--long-option=attached_arg" or "-sATTACHED_ARG"
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class CommandLineOption extends CommandLinePart {
|
||||
|
||||
@NotNull
|
||||
private final String myOptionName;
|
||||
|
||||
@Nullable
|
||||
private final WordWithPosition myAttachedArgument;
|
||||
|
||||
/**
|
||||
* @param option option (text and position)
|
||||
* @param optionName option name (like "--foo")
|
||||
* @param attachedArgument option attached argument (like --foo=ATTACHED_ARG)
|
||||
*/
|
||||
public CommandLineOption(@NotNull final WordWithPosition option,
|
||||
@NotNull final String optionName,
|
||||
@Nullable final WordWithPosition attachedArgument) {
|
||||
super(option);
|
||||
myOptionName = optionName;
|
||||
myAttachedArgument = attachedArgument;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return option name (like "--foo")
|
||||
*/
|
||||
@NotNull
|
||||
public String getOptionName() {
|
||||
return myOptionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return option attached argument (like --foo=ATTACHED_ARG)
|
||||
*/
|
||||
@Nullable
|
||||
public WordWithPosition getAttachedArgument() {
|
||||
return myAttachedArgument;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(@NotNull final CommandLinePartVisitor visitor) {
|
||||
visitor.visitOption(this);
|
||||
}
|
||||
}
|
||||
@@ -15,24 +15,20 @@
|
||||
*/
|
||||
package com.jetbrains.python.commandLineParser;
|
||||
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Engine to parse command line. It understands how options and arguments are coded in certain commandline.
|
||||
* It supportd {@link com.jetbrains.python.WordWithPosition} telling you exactly with part of
|
||||
* command line is command or argument. That helps you to underline or emphisize some parts.
|
||||
* Engine to {@link CommandLine} structure from text.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface CommandLineParser {
|
||||
/**
|
||||
* @param commandLineParts command line splitted into words.
|
||||
*
|
||||
* @param commandLineText command line to parse
|
||||
* @return command line information
|
||||
* @throws MalformedCommandLineException in case of bad commandline
|
||||
*/
|
||||
@NotNull
|
||||
CommandLineParseResult parse(@NotNull List<WordWithPosition> commandLineParts) throws MalformedCommandLineException;
|
||||
CommandLine parse(@NotNull String commandLineText) throws MalformedCommandLineException;
|
||||
}
|
||||
|
||||
+24
-12
@@ -13,27 +13,39 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.commandInterface.chunkDriverBasedPresenter;
|
||||
package com.jetbrains.python.commandLineParser;
|
||||
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Driver that knows how to parse pack of chunks into chunk info.
|
||||
* Part of command line. Known subclasses are {@link CommandLineOption} and {@link CommandLineArgument}
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
* @see CommandLineArgument
|
||||
* @see CommandLineOption
|
||||
*/
|
||||
public interface ChunkDriver {
|
||||
public abstract class CommandLinePart {
|
||||
@NotNull
|
||||
private final WordWithPosition myWord;
|
||||
|
||||
/**
|
||||
* Parses chunks into pack of chunks. There <strong>always</strong> should be chunk+1 chunkInfos (one for the tail like
|
||||
* {@link com.jetbrains.python.commandInterface.CommandInterfaceView#AFTER_LAST_CHARACTER_RANGE}).
|
||||
* So, at least one chunk info should also exist!
|
||||
*
|
||||
* @param chunks chunks (parts of command line)
|
||||
* @return parse info with chunks info. Warning: do not return less chunk infos than chunks provided. That leads to runtime error
|
||||
* @param word word (and its position) this part represents
|
||||
*/
|
||||
protected CommandLinePart(@NotNull final WordWithPosition word) {
|
||||
myWord = word;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return word (and its position) this part represents
|
||||
*/
|
||||
@NotNull
|
||||
ParseInfo parse(@NotNull List<WordWithPosition> chunks);
|
||||
public final WordWithPosition getWord() {
|
||||
return myWord;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param visitor visitor to accept
|
||||
*/
|
||||
public abstract void accept(@NotNull CommandLinePartVisitor visitor);
|
||||
}
|
||||
+11
-15
@@ -15,27 +15,23 @@
|
||||
*/
|
||||
package com.jetbrains.python.commandLineParser;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Types of command line parts.
|
||||
* Visitor to process options and arguments
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
* @see CommandLinePart#accept(CommandLinePartVisitor)
|
||||
*/
|
||||
public enum CommandLinePartType {
|
||||
public interface CommandLinePartVisitor {
|
||||
|
||||
/**
|
||||
* Argument (or positional, or unnamed argument) something that has only value. Like "my_folder" in "rm my_folder"
|
||||
* @param option option to visit
|
||||
*/
|
||||
ARGUMENT,
|
||||
void visitOption(@NotNull CommandLineOption option);
|
||||
|
||||
/**
|
||||
* Option is named but optional parameter. Like "-l" in "ls -l".
|
||||
* @param argument argument to visit
|
||||
*/
|
||||
OPTION,
|
||||
/**
|
||||
* Option argument like --folder-to-delete=/
|
||||
* Here root is option argument
|
||||
*/
|
||||
OPTION_ARGUMENT,
|
||||
/**
|
||||
* Some part of command line that {@link com.jetbrains.python.commandLineParser.CommandLineParser} does not understand
|
||||
*/
|
||||
UNKNOWN
|
||||
void visitArgument(@NotNull CommandLineArgument argument);
|
||||
}
|
||||
@@ -16,38 +16,27 @@
|
||||
package com.jetbrains.python.commandLineParser.optParse;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.jetbrains.python.commandInterface.command.Option;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Supports --long-option-style-with=value
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class LongOptionParser implements OptionParser {
|
||||
@Nullable
|
||||
final class LongOptionParser extends OptionParserRegexBased {
|
||||
@NotNull
|
||||
private static final Pattern LONG_OPT_PATTERN = Pattern.compile("^((--[a-zA-Z0-9-]+)=?)");
|
||||
|
||||
LongOptionParser() {
|
||||
super(LONG_OPT_PATTERN);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Pair<Option, String> findOptionAndValue(@NotNull final List<Option> availableOptions, @NotNull final String textToCheck) {
|
||||
if (!textToCheck.startsWith("--")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (final Option option : availableOptions) {
|
||||
for (final String longName : option.getLongNames()) {
|
||||
if (textToCheck.startsWith(longName)) {
|
||||
final String[] parts = textToCheck.split("=");
|
||||
if (parts.length == 0) {
|
||||
return Pair.create(option, null);
|
||||
}
|
||||
else {
|
||||
return Pair.create(option, parts[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
protected Pair<String, String> getOptionTextAndNameFromMatcher(@NotNull final Matcher matcher) {
|
||||
return Pair.create(matcher.group(1), matcher.group(2));
|
||||
}
|
||||
}
|
||||
|
||||
+36
-87
@@ -16,19 +16,15 @@
|
||||
package com.jetbrains.python.commandLineParser.optParse;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import com.jetbrains.python.WordWithPosition;
|
||||
import com.jetbrains.python.commandInterface.command.Command;
|
||||
import com.jetbrains.python.commandInterface.command.Option;
|
||||
import com.jetbrains.python.commandInterface.command.OptionArgumentInfo;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineParseResult;
|
||||
import com.jetbrains.python.commandLineParser.CommandLineParser;
|
||||
import com.jetbrains.python.commandLineParser.CommandLinePartType;
|
||||
import com.jetbrains.python.commandLineParser.MalformedCommandLineException;
|
||||
import com.jetbrains.python.commandLineParser.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -48,114 +44,67 @@ import java.util.*;
|
||||
public final class OptParseCommandLineParser implements CommandLineParser {
|
||||
/**
|
||||
* Supported option parsers (option types, actually)
|
||||
* Short (-o) and long (--option) strategies are used here
|
||||
*/
|
||||
@NotNull
|
||||
private static final OptionParser[] OPTION_PARSERS = {new LongOptionParser(), new ShortOptionParser()};
|
||||
|
||||
/**
|
||||
* CommandName -> Options
|
||||
*/
|
||||
@NotNull
|
||||
private final Map<String, List<Option>> myCommandOptions = new HashMap<String, List<Option>>();
|
||||
private static final OptionParser[] OPTION_PARSERS = {new ShortOptionParser(), new LongOptionParser()};
|
||||
|
||||
/**
|
||||
* @param commands commands (needed to parse out options)
|
||||
*/
|
||||
public OptParseCommandLineParser(@NotNull final Iterable<? extends Command> commands) {
|
||||
for (final Command command : commands) {
|
||||
myCommandOptions.put(command.getName(), command.getOptions());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandLineParseResult parse(@NotNull final List<WordWithPosition> commandLineParts) throws MalformedCommandLineException {
|
||||
final Deque<WordWithPosition> parts = new ArrayDeque<WordWithPosition>(commandLineParts);
|
||||
public CommandLine parse(@NotNull final String commandLineText) throws MalformedCommandLineException {
|
||||
final Deque<WordWithPosition> parts = new ArrayDeque<WordWithPosition>(WordWithPosition.splitText(commandLineText));
|
||||
if (parts.isEmpty()) {
|
||||
throw new MalformedCommandLineException("No command provided");
|
||||
}
|
||||
final WordWithPosition command = parts.pop();
|
||||
final List<Option> options;
|
||||
if (myCommandOptions.containsKey(command.getText())) {
|
||||
options = myCommandOptions.get(command.getText());
|
||||
}
|
||||
else {
|
||||
options = Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<CommandLinePart> optionsAndArguments = new ArrayList<CommandLinePart>();
|
||||
|
||||
final List<Pair<CommandLinePartType, WordWithPosition>> resultParts = new ArrayList<Pair<CommandLinePartType, WordWithPosition>>();
|
||||
if (isOption(command)) {
|
||||
throw new MalformedCommandLineException("Command can't start with option prefix");
|
||||
}
|
||||
|
||||
int optionArgumentsLeft = 0; // If option has N arguments, then next N arguments belong to this option
|
||||
for (final WordWithPosition part : parts) {
|
||||
if (optionArgumentsLeft > 0) {
|
||||
optionArgumentsLeft--;
|
||||
resultParts.add(Pair.create(CommandLinePartType.OPTION_ARGUMENT, part));
|
||||
continue;
|
||||
}
|
||||
if (isOption(part)) {
|
||||
// This is option!
|
||||
final Pair<Option, String> optionAndValue = findOptionAndValue(options, part.getText());
|
||||
if (optionAndValue != null) {
|
||||
final Option option = optionAndValue.first;
|
||||
final Pair<Integer, OptionArgumentInfo> argumentAndQuantity = option.getArgumentAndQuantity();
|
||||
if (argumentAndQuantity != null) {
|
||||
optionArgumentsLeft = argumentAndQuantity.first;
|
||||
}
|
||||
final String optionArgumentValue = optionAndValue.second;
|
||||
if (optionArgumentValue != null) {
|
||||
// We found argument
|
||||
optionArgumentsLeft--;
|
||||
final String optionArgumentText =
|
||||
part.getText().substring(0, part.getText().length() - optionArgumentValue.length());
|
||||
final WordWithPosition optionPart =
|
||||
new WordWithPosition(optionArgumentText, part.getFrom(), part.getFrom() + optionArgumentText.length());
|
||||
final WordWithPosition valuePart = new WordWithPosition(optionArgumentValue, optionPart.getTo(), part.getTo());
|
||||
resultParts.add(Pair.create(CommandLinePartType.OPTION, optionPart));
|
||||
resultParts.add(Pair.create(CommandLinePartType.OPTION_ARGUMENT, valuePart));
|
||||
}
|
||||
else {
|
||||
resultParts.add(Pair.create(CommandLinePartType.OPTION, part));
|
||||
}
|
||||
final Pair<String, String> optionTextAndName = findOptionTextAndName(part.getText());
|
||||
if (optionTextAndName != null) {
|
||||
final String optionText = optionTextAndName.first;
|
||||
final String optionName = optionTextAndName.second;
|
||||
final WordWithPosition option = new WordWithPosition(optionText, part.getFrom(), part.getFrom() + optionText.length());
|
||||
|
||||
final WordWithPosition optionArgument;
|
||||
if (optionText.length() == part.getText().length()) {
|
||||
optionArgument = null;
|
||||
}
|
||||
else {
|
||||
resultParts.add(Pair.create(CommandLinePartType.UNKNOWN, part));
|
||||
// Looks like we have option argument here.
|
||||
final String argumentText = part.getText().substring(optionText.length());
|
||||
optionArgument = new WordWithPosition(argumentText, option.getTo(), part.getTo());
|
||||
}
|
||||
optionsAndArguments.add(new CommandLineOption(option, optionName, optionArgument)); //Option
|
||||
if (optionArgument != null) {
|
||||
optionsAndArguments.add(new CommandLineArgument(optionArgument)); // And its argument
|
||||
}
|
||||
}
|
||||
else {
|
||||
resultParts.add(Pair.create(CommandLinePartType.ARGUMENT, part));
|
||||
optionsAndArguments.add(new CommandLineArgument(part)); //Not an option. Should be argument
|
||||
}
|
||||
}
|
||||
return new CommandLineParseResult(command, resultParts);
|
||||
return new CommandLine(command, optionsAndArguments);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse out option and its value iterating through the parsers
|
||||
*
|
||||
* @param options all available options
|
||||
* @param text text believed to be an option like "--foo=123"
|
||||
* @return [option->argument_value] pair or null if provided text is not an option. ArgValue may also be null if not provided
|
||||
* @see com.jetbrains.python.commandLineParser.optParse.OptionParser#findOptionAndValue(java.util.List, String)
|
||||
* @param text text believed to be an option like "--foo=123"
|
||||
* @return [option_text, option_name] or null of no such option
|
||||
* @see OptionParser#findOptionTextAndName(String)
|
||||
*/
|
||||
@Nullable
|
||||
private static Pair<Option, String> findOptionAndValue(@NotNull final List<Option> options, @NotNull final String text) {
|
||||
private static Pair<String, String> findOptionTextAndName(@NotNull final String optionText) {
|
||||
for (final OptionParser parser : OPTION_PARSERS) {
|
||||
final Pair<Option, String> optionAndValue = parser.findOptionAndValue(options, text);
|
||||
if (optionAndValue != null) {
|
||||
return optionAndValue;
|
||||
final Pair<String, String> optionTextAndName = parser.findOptionTextAndName(optionText);
|
||||
if (optionTextAndName != null) {
|
||||
return optionTextAndName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command command to check
|
||||
* @return if command is option or not
|
||||
*/
|
||||
private static boolean isOption(@NotNull final WordWithPosition command) {
|
||||
return command.getText().startsWith("-");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,25 +16,19 @@
|
||||
package com.jetbrains.python.commandLineParser.optParse;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.jetbrains.python.commandInterface.command.Option;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Engine that knows how to deal with option of certain style
|
||||
* Engine that knows how to deal with option of certain style (like long and short)
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
interface OptionParser {
|
||||
/**
|
||||
* Checks if some text that looks like and option is really option
|
||||
*
|
||||
* @param availableOptions all available options
|
||||
* @param textToCheck text believed to be an option like "--foo=123"
|
||||
* @return [option->argument_value] pair or null if provided text is not an option. ArgValue may also be null if not provided
|
||||
* @param optionText text to parse (like --foo=bar)
|
||||
* @return null if option can't be parsed. Otherwise pair of [option_text, option_name]. That may match each other in some cases.
|
||||
*/
|
||||
@Nullable
|
||||
Pair<Option, String> findOptionAndValue(@NotNull List<Option> availableOptions, @NotNull String textToCheck);
|
||||
Pair<String, String> findOptionTextAndName(@NotNull String optionText);
|
||||
}
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.jetbrains.python.commandLineParser.optParse;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Parses options using regex and template method
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
abstract class OptionParserRegexBased implements OptionParser {
|
||||
@NotNull
|
||||
private final Pattern myPattern;
|
||||
|
||||
/**
|
||||
* @param pattern regex. If option matches pattern, ({@link Matcher#find() opened matcher}) would be passed to template method
|
||||
* {@link #getOptionTextAndNameFromMatcher(Matcher)}
|
||||
*/
|
||||
protected OptionParserRegexBased(@NotNull final Pattern pattern) {
|
||||
myPattern = pattern;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final Pair<String, String> findOptionTextAndName(@NotNull final String optionText) {
|
||||
final Matcher matcher = myPattern.matcher(optionText);
|
||||
if (!matcher.find()) {
|
||||
return null;
|
||||
}
|
||||
return getOptionTextAndNameFromMatcher(matcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains [option_text, option_name] from matcher
|
||||
*
|
||||
* @param matcher opened (with find() called) matcher
|
||||
* @return pair [option_text, option_name]
|
||||
*/
|
||||
@NotNull
|
||||
protected abstract Pair<String, String> getOptionTextAndNameFromMatcher(@NotNull Matcher matcher);
|
||||
}
|
||||
@@ -16,44 +16,29 @@
|
||||
package com.jetbrains.python.commandLineParser.optParse;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.jetbrains.python.commandInterface.command.Option;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Deals with short-style options like -b or -f
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
final class ShortOptionParser implements OptionParser {
|
||||
final class ShortOptionParser extends OptionParserRegexBased {
|
||||
/**
|
||||
* Short-style option regexp
|
||||
*/
|
||||
private static final Pattern SHORT_OPT_START = Pattern.compile("^(-[a-zA-Z0-9])([^ -])?");
|
||||
private static final Pattern SHORT_OPT_PATTERN = Pattern.compile("^(-[a-zA-Z0-9])([^ -])?");
|
||||
|
||||
@Nullable
|
||||
ShortOptionParser() {
|
||||
super(SHORT_OPT_PATTERN);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Pair<Option, String> findOptionAndValue(@NotNull final List<Option> availableOptions, @NotNull final String textToCheck) {
|
||||
final Matcher matcher = SHORT_OPT_START.matcher(textToCheck);
|
||||
|
||||
if (!matcher.find()) {
|
||||
return null;
|
||||
}
|
||||
final String optionText = matcher.group(1);
|
||||
final String optionValueText = (matcher.groupCount() == 2 ? matcher.group(2) : null);
|
||||
|
||||
|
||||
for (final Option option : availableOptions) {
|
||||
for (final String shortName : option.getShortNames()) {
|
||||
if (optionText.equals(shortName)) {
|
||||
return Pair.create(option, optionValueText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
protected Pair<String, String> getOptionTextAndNameFromMatcher(@NotNull final Matcher matcher) {
|
||||
return Pair.create(matcher.group(1), matcher.group(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,15 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Engine to parse command-line.
|
||||
* Command line consists of command itself, {@link com.jetbrains.python.commandLineParser.CommandLinePartType#ARGUMENT arguments}
|
||||
* and {@link com.jetbrains.python.commandLineParser.CommandLinePartType#OPTION options}.
|
||||
* Use need to split command line into {@link com.jetbrains.python.WordWithPosition chunks}, pass them to
|
||||
* {@link com.jetbrains.python.commandLineParser.CommandLineParser parser} and obtain {@link com.jetbrains.python.commandLineParser.CommandLineParseResult}.
|
||||
* Engine to parse commandline from string to {@link com.jetbrains.python.commandLineParser.CommandLine} structure.
|
||||
*
|
||||
* Command line consists of command itself, {@link com.jetbrains.python.commandLineParser.CommandLineArgument arguments}
|
||||
* and {@link com.jetbrains.python.commandLineParser.CommandLineOption options}.
|
||||
* Use need to pass text to {@link com.jetbrains.python.commandLineParser.CommandLineParser parser} and obtain {@link com.jetbrains.python.commandLineParser.CommandLine}.
|
||||
* <p/>
|
||||
* Not like any other parsers, this package supports {@link com.jetbrains.python.WordWithPosition} telling you exactly with part of
|
||||
* command line is command or argument. That helps you to underline or emphisize some parts.
|
||||
*
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
package com.jetbrains.python.commandLineParser;
|
||||
@@ -99,6 +99,7 @@ public class SuggestionList {
|
||||
}
|
||||
// Fill and select
|
||||
|
||||
int record = 0; // Record to select
|
||||
// Iterate through groups adding suggestions. Odd groups should be marked differently.
|
||||
for (int groupId = 0; groupId < values.size(); groupId++) {
|
||||
final List<Suggestion> suggestions = values.get(groupId);
|
||||
@@ -106,11 +107,12 @@ public class SuggestionList {
|
||||
final Suggestion suggestion = suggestions.get(suggestionId);
|
||||
myListModel.addElement(new SuggestionListElement((groupId % 2) == 0, suggestion));
|
||||
if (suggestion.getText().equals(elementToSelect)) {
|
||||
myList.setSelectedIndex(suggestionId + groupId);
|
||||
myList.setSelectedIndex(record);
|
||||
}
|
||||
record++;
|
||||
}
|
||||
}
|
||||
if ((elementToSelect == null) && (!myListModel.isEmpty())) {
|
||||
if ((myList.getSelectedIndex() ==-1) && (!myListModel.isEmpty())) {
|
||||
myList.setSelectedIndex(0); // Select first element
|
||||
}
|
||||
|
||||
@@ -124,6 +126,7 @@ public class SuggestionList {
|
||||
myListPopUp.addListener(myListener);
|
||||
}
|
||||
myListPopUp.show(displayPoint);
|
||||
myList.ensureIndexIsVisible(myList.getSelectedIndex()); // Scrolls to selected
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user