content: package org.netbeans.modules.python.source;
  
  import java.awt.BorderLayout;
  import java.awt.Color;
  import java.awt.Dimension;
  import java.awt.event.ActionEvent;
  import java.beans.PropertyChangeEvent;
  import java.beans.PropertyChangeListener;
  import java.io.IOException;
  import java.nio.file.Paths;
  import java.util.regex.Pattern;
  import javax.swing.JButton;
  import javax.swing.JComponent;
  import javax.swing.UIManager;
  import javax.swing.text.Document;
  import javax.swing.text.JTextComponent;
  import org.apache.commons.lang3.math.NumberUtils;
  import org.apache.commons.math3.util.Precision;
  import org.netbeans.modules.csl.spi.GsfUtilities;
  import org.netbeans.modules.python.PythonUtility;
  import org.netbeans.spi.editor.SideBarFactory;
  import org.openide.awt.CloseButtonFactory;
  import org.openide.filesystems.FileObject;
  import org.openide.loaders.DataObject;
  import org.openide.loaders.DataObjectNotFoundException;
  import org.openide.util.Exceptions;
  import org.openide.util.RequestProcessor;
  
  /**
   *
   * @author albilu
   */
  public class PythonPylintScoreBar extends javax.swing.JPanel {
  
      private boolean enabled;
      private static final String PYLINT_SIDEBAR_PROP = "pylintSideBar";
      FileObject fileForDocument;
      static Pattern PYLINT_SCORE = Pattern.compile("Your code has been rated at (.*) \\(previous run: (.*), (.*)\\)|Your code has been rated at (.*)");
      DataObject find;
      static RequestProcessor RP = new RequestProcessor(PythonPylintScoreBar.class.getName(), 5);
  
      /**
       * Creates new form NewJPanel
       */
      public PythonPylintScoreBar(final JTextComponent target) {
          //initComponents();
          Document document = target.getDocument();
          String mimeType = (String) document.getProperty("mimeType"); // NOI18N
          if (!mimeType.equals(PythonUtility.PYTHON_MIME_TYPE)) {
              return;
          }
  
          fileForDocument = GsfUtilities.findFileObject(document);
          try {
              find = DataObject.find(fileForDocument);
          } catch (DataObjectNotFoundException ex) {
              Exceptions.printStackTrace(ex);
          }
  
          boolean on = false;
  
          if (on) {
              showScorePanel(true);
          } else {
              updatePreferredSize();
          }
          target.putClientProperty(PYLINT_SIDEBAR_PROP, this);
  
      }
      PropertyChangeListener pcl = new PropertyChangeListener() {
          @Override
          public void propertyChange(PropertyChangeEvent pce) {
              if (pce.getPropertyName().equals(DataObject.PROP_MODIFIED)) {
                  setScore(fileForDocument);
              }
          }
      };
  
      /**
       * This method is called from within the constructor to initialize the form.
       * WARNING: Do NOT modify this code. The content of this method is always
       * regenerated by the Form Editor.
       */
      @SuppressWarnings("unchecked")
      // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
      private void initComponents() {
  
          pylintScorePanel = new javax.swing.JPanel();
          nameLabel = new javax.swing.JLabel();
          pylintProgressBar = new javax.swing.JProgressBar();
          previousLabel = new javax.swing.JLabel();
  
          setLayout(new java.awt.BorderLayout());
  
          org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(PythonPylintScoreBar.class, "PythonPylintScoreBar.nameLabel.text")); // NOI18N
          nameLabel.setToolTipText(org.openide.util.NbBundle.getMessage(PythonPylintScoreBar.class, "PythonPylintScoreBar.nameLabel.toolTipText")); // NOI18N
          nameLabel.setPreferredSize(new java.awt.Dimension(102, 19));
  
          pylintProgressBar.setBackground(new Color(180, 50, 50));
          pylintProgressBar.setFont(new java.awt.Font("Cantarell", 1, 15)); // NOI18N
          pylintProgressBar.setPreferredSize(new java.awt.Dimension(146, 23));
          pylintProgressBar.setString(org.openide.util.NbBundle.getMessage(PythonPylintScoreBar.class, "PythonPylintScoreBar.pylintProgressBar.string")); // NOI18N
          pylintProgressBar.setStringPainted(true);
          pylintProgressBar.addChangeListener(new javax.swing.event.ChangeListener() {
              public void stateChanged(javax.swing.event.ChangeEvent evt) {
                  pylintProgressBarStateChanged(evt);
              }
          });
  
          previousLabel.setFont(new java.awt.Font("Cantarell", 1, 15)); // NOI18N
          org.openide.awt.Mnemonics.setLocalizedText(previousLabel, org.openide.util.NbBundle.getMessage(PythonPylintScoreBar.class, "PythonPylintScoreBar.previousLabel.text")); // NOI18N
  
          javax.swing.GroupLayout pylintScorePanelLayout = new javax.swing.GroupLayout(pylintScorePanel);
          pylintScorePanel.setLayout(pylintScorePanelLayout);
          pylintScorePanelLayout.setHorizontalGroup(
              pylintScorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addGroup(pylintScorePanelLayout.createSequentialGroup()
                  .addContainerGap()
                  .addComponent(nameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 102, javax.swing.GroupLayout.PREFERRED_SIZE)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(pylintProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(previousLabel)
                  .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
          );
          pylintScorePanelLayout.setVerticalGroup(
              pylintScorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addGroup(pylintScorePanelLayout.createSequentialGroup()
                  .addGap(0, 0, 0)
                  .addGroup(pylintScorePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                      .addComponent(nameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                      .addComponent(pylintProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                      .addComponent(previousLabel))
                  .addGap(0, 0, 0))
          );
  
          pylintScorePanelLayout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {nameLabel, pylintProgressBar});
  
          add(pylintScorePanel, java.awt.BorderLayout.CENTER);
      }// </editor-fold>//GEN-END:initComponents
  
      private void pylintProgressBarStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_pylintProgressBarStateChanged
          int percentComplete = pylintProgressBar.getValue();
          if (percentComplete <= 33.33) {
              pylintProgressBar.setForeground(new Color(180, 50, 50));
          } else if (percentComplete > 33.33 && percentComplete <= 66.66) {
              pylintProgressBar.setForeground(UIManager.getDefaults().getColor("nb.warningForeground"));
          } else {
              pylintProgressBar.setForeground(new Color(30, 180, 30));
          }
      }//GEN-LAST:event_pylintProgressBarStateChanged
  
      // Variables declaration - do not modify//GEN-BEGIN:variables
      private javax.swing.JLabel nameLabel;
      private javax.swing.JLabel previousLabel;
      private javax.swing.JProgressBar pylintProgressBar;
      private javax.swing.JPanel pylintScorePanel;
      // End of variables declaration//GEN-END:variables
  
      public static PythonPylintScoreBar getSideBar(JTextComponent target) {
          return (PythonPylintScoreBar) target.getClientProperty(PYLINT_SIDEBAR_PROP);
      }
  
      public void showScorePanel(boolean on) {
          if (on == enabled) {
              return;
          }
          this.enabled = on;
          if (on) {
              initComponents();
              JButton closeButton = CloseButtonFactory.createBigCloseButton();
              add(closeButton, BorderLayout.EAST);
              closeButton.addActionListener((ActionEvent e) -> {
                  showScorePanel(false);
                  find.removePropertyChangeListener(pcl);
              });
              setScore(fileForDocument);
              find.addPropertyChangeListener(pcl);
          } else {
              removeAll();
          }
  
          updatePreferredSize();
          revalidate();
          repaint();
      }
  
      private void updatePreferredSize() {
          if (enabled) {
              // Recompute
              setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
              setPreferredSize(null);
              Dimension preferred = getPreferredSize();
              setPreferredSize(preferred);
          } else {
              setPreferredSize(new Dimension(0, 0));
              setMaximumSize(new Dimension(0, 0));
          }
          revalidate();
      }
  
      private void setScore(FileObject fileForDocument) {
          RP.post(() -> {
              try {
                  String[] cmd = {PythonUtility.getLspPythonExe(), "-m", "pylint",
                      Paths.get(fileForDocument.getPath()).toString()};
                  String commandOutput = PythonUtility.getCommandOutput(cmd, null);
                  PYLINT_SCORE.matcher(commandOutput).results().forEach((match) -> {
                      String score = match.group(1);
                      String previous = match.group(2);
                      String delta = match.group(3);
                      String onlyScore = match.group(4);
                      if (onlyScore != null) {
                          double percentage = Precision.round(getPercentage(onlyScore), 2);
                          pylintProgressBar.setValue((int) Math.round(percentage));
                          pylintProgressBar.setString(String.format("%s %s", Double.toString(percentage), "%"));
                      } else {
                          double percentage = Precision.round(getPercentage(score), 2);
                          pylintProgressBar.setValue((int) Math.round(percentage));
                          pylintProgressBar.setString(String.format("%s %s", Double.toString(percentage), "%"));
                          previousLabel.setText(String.format("(previous run: %s, %s)", previous, delta));
  
                      }
  
                  });
              } catch (IOException ex) {
                  Exceptions.printStackTrace(ex);
              }
          });
      }
  
      private double getPercentage(String onlyScore) {
          String[] split = onlyScore.split("/");
          double perc = NumberUtils.toDouble(split[0]) / 10 * 100;
          return perc;
      }
  
      public static final class Factory implements SideBarFactory {
  
          @Override
          public JComponent createSideBar(JTextComponent target) {
              return new PythonPylintScoreBar(target);
          }
      }
  }
  