mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'traff/prof-advertiser'
This commit is contained in:
@@ -18,5 +18,8 @@
|
||||
<applicationService serviceInterface="com.intellij.openapi.application.IdeUrlTrackingParametersProvider"
|
||||
serviceImplementation="com.intellij.ide.customization.UtmIdeUrlTrackingParametersProvider"
|
||||
overrides="true"/>
|
||||
|
||||
<annotator language="Python" implementationClass="com.jetbrains.python.PyCharmProfessionalAdvertiser"/>
|
||||
<projectService serviceImplementation="com.jetbrains.python.PyCharmProfessionalAdvertiserSettings"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PyCellUtil {
|
||||
|
||||
public static boolean hasCells(@NotNull PsiFile file) {
|
||||
PsiElement el = file.getFirstChild();
|
||||
while (el != null) {
|
||||
if (isBlockCell(el)) {
|
||||
return true;
|
||||
}
|
||||
el = el.getNextSibling();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement getCellStart(@NotNull PsiElement element) {
|
||||
PsiElement el = element;
|
||||
while (el != null && !isBlockCell(el)) {
|
||||
el = PsiTreeUtil.prevLeaf(el);
|
||||
}
|
||||
if (el == null) {
|
||||
return element.getContainingFile().getFirstChild();
|
||||
}
|
||||
return PsiTreeUtil.nextLeaf(el);
|
||||
}
|
||||
|
||||
public static boolean isBlockCell(PsiElement element) {
|
||||
return (element instanceof PsiComment) &&
|
||||
(element.getText().startsWith("# %%") || element.getText().startsWith("#%%") || element.getText().startsWith("# <codecell>")) ||
|
||||
element.getText().startsWith("# In[");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getCodeInCell(@Nullable PsiElement element) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
while (element != null && !isBlockCell(element)) {
|
||||
text.append(element.getText());
|
||||
element = element.getNextSibling();
|
||||
}
|
||||
return StringUtil.trim(text.toString());
|
||||
}
|
||||
|
||||
public static PsiElement findNextCell(PsiElement startElement) {
|
||||
PsiElement el = PsiTreeUtil.nextLeaf(PsiTreeUtil.getDeepestFirst(startElement));
|
||||
while (el != null) {
|
||||
if (isBlockCell(el)) {
|
||||
return el;
|
||||
}
|
||||
el = PsiTreeUtil.nextLeaf(el);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PsiElement findPrevCell(PsiElement startElement) {
|
||||
PsiElement el = PsiTreeUtil.prevLeaf(PsiTreeUtil.getDeepestFirst(startElement));
|
||||
while (el != null) {
|
||||
if (isBlockCell(el)) {
|
||||
return el;
|
||||
}
|
||||
el = PsiTreeUtil.prevLeaf(el);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.lang.annotation.AnnotationHolder;
|
||||
import com.intellij.lang.annotation.Annotator;
|
||||
import com.intellij.notification.NotificationDisplayType;
|
||||
import com.intellij.notification.NotificationGroup;
|
||||
import com.intellij.notification.NotificationListener;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.FileIndexFacade;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyFromImportStatement;
|
||||
import com.jetbrains.python.psi.PyImportElement;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
*/
|
||||
public class PyCharmProfessionalAdvertiser implements Annotator {
|
||||
|
||||
|
||||
@Language("HTML")
|
||||
private static final String NOTIFICATIONS_TEXT =
|
||||
"<a href=\"prof\">PyCharm Professional Edition</a> has special support for it.";
|
||||
|
||||
private static final NotificationGroup BALLOON_NOTIFICATIONS = new NotificationGroup("PyCharm Professional Advertiser",
|
||||
NotificationDisplayType.STICKY_BALLOON, false);
|
||||
@Override
|
||||
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
|
||||
|
||||
if (element instanceof PyFile) {
|
||||
|
||||
final PyFile pyFile = (PyFile)element;
|
||||
final Project project = element.getProject();
|
||||
|
||||
if (getSettings(project).shown) {
|
||||
return;
|
||||
}
|
||||
|
||||
final VirtualFile vFile = pyFile.getVirtualFile();
|
||||
if (vFile != null && FileIndexFacade.getInstance(project).isInLibraryClasses(vFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PyCellUtil.hasCells(pyFile)) {
|
||||
showInspectionAdvertisement(project, "code cells in the editor", "https://www.jetbrains.com/pycharm/features/scientific_tools.html", "codecells");
|
||||
}
|
||||
|
||||
if (containsImport(pyFile, "django")) {
|
||||
showInspectionAdvertisement(project, "the Django Framework", "https://www.jetbrains.com/pycharm/features/web_development.html#django","django");
|
||||
}
|
||||
|
||||
if (containsImport(pyFile, "flask")) {
|
||||
showInspectionAdvertisement(project, "the Flask Framework", null,"flask");
|
||||
}
|
||||
|
||||
if (containsImport(pyFile, "pyramid")) {
|
||||
showInspectionAdvertisement(project, "the Pyramid Framework", null,"pyramid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void showInspectionAdvertisement(@NotNull Project project,
|
||||
@NotNull String message,
|
||||
@Nullable String url,
|
||||
@NotNull String source) {
|
||||
showSingletonNotification(project, "You are using " + message, NOTIFICATIONS_TEXT, NotificationType.INFORMATION,
|
||||
(notification, event) -> {
|
||||
if ("prof".equals(event.getDescription())) {
|
||||
BrowserUtil.browse(
|
||||
(url != null ? url : "https://www.jetbrains.com/pycharm/features/editions_comparison_matrix.html") +
|
||||
"?utm_source=from_product&utm_medium=advertiser&utm_campaign=" +
|
||||
source);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void showSingletonNotification(@NotNull Project project,
|
||||
@NotNull String title,
|
||||
@NotNull String htmlContent,
|
||||
@NotNull NotificationType type,
|
||||
@NotNull NotificationListener listener) {
|
||||
getSettings(project).shown = true;
|
||||
BALLOON_NOTIFICATIONS.createNotification(title, htmlContent, type, (notification, event) -> {
|
||||
try {
|
||||
listener.hyperlinkUpdate(notification, event);
|
||||
}
|
||||
finally {
|
||||
notification.expire();
|
||||
}
|
||||
}).notify(project);
|
||||
}
|
||||
|
||||
private static boolean containsImport(@NotNull PyFile file, String pkg) {
|
||||
for (PyFromImportStatement importStatement : file.getFromImports()) {
|
||||
final QualifiedName name = importStatement.getImportSourceQName();
|
||||
if (name != null && name.toString().toLowerCase().contains(pkg)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (PyImportElement importElement : file.getImportTargets()) {
|
||||
final QualifiedName name = importElement.getImportedQName();
|
||||
if (name != null && name.toString().toLowerCase().contains(pkg)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PyCharmProfessionalAdvertiserSettings getSettings(@NotNull Project project) {
|
||||
return ServiceManager.getService(project, PyCharmProfessionalAdvertiserSettings.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
*/
|
||||
@State(name = "PyCharmProfessionalAdvertiser")
|
||||
public class PyCharmProfessionalAdvertiserSettings implements PersistentStateComponent<PyCharmProfessionalAdvertiserSettings> {
|
||||
public boolean shown = false;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PyCharmProfessionalAdvertiserSettings getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(@NotNull PyCharmProfessionalAdvertiserSettings state) {
|
||||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user