public static void show() {
    if (shownInSession) {
      return;
    }
    shownInSession = true;
    if (SharedCorePlugin.inTestMode()) {
      return;
    }
    String hide = System.getProperty("pydev.funding.hide");
    if (hide != null && (hide.equals("1") || hide.equals("true"))) {
      return;
    }
    IPreferenceStore preferenceStore = PydevPrefs.getPreferenceStore();
    boolean shownOnce = preferenceStore.getBoolean(PYDEV_FUNDING_SHOWN);
    if (!shownOnce) {
      boolean runNowIfInUiThread = false;
      RunInUiThread.async(
          new Runnable() {

            @Override
            public void run() {
              Display disp = Display.getCurrent();
              Shell shell = new Shell(disp);
              DialogNotifier notifier = new DialogNotifier(shell);
              notifier.open();
            }
          },
          runNowIfInUiThread);
    }
  }
  public void checkAllNow() {
    // Add all to be tracked
    Map<IInterpreterManager, Map<String, IInterpreterInfo>> addedToTrack = job.addAllToTrack();

    // remove from the preferences any ignore the user had set previously
    Set<Entry<IInterpreterManager, Map<String, IInterpreterInfo>>> entrySet =
        addedToTrack.entrySet();
    IPreferenceStore preferences = PydevPrefs.getPreferences();
    for (Entry<IInterpreterManager, Map<String, IInterpreterInfo>> entry : entrySet) {
      Set<Entry<String, IInterpreterInfo>> entrySet2 = entry.getValue().entrySet();
      for (Entry<String, IInterpreterInfo> entry2 : entrySet2) {
        String key = SynchSystemModulesManager.createKeyForInfo(entry2.getValue());
        preferences.setValue(key, "");
      }
    }
    if (preferences instanceof IPersistentPreferenceStore) {
      IPersistentPreferenceStore iPersistentPreferenceStore =
          (IPersistentPreferenceStore) preferences;
      try {
        iPersistentPreferenceStore.save();
      } catch (IOException e) {
        Log.log(e);
      }
    }

    // schedule changes to be executed.
    job.scheduleLater(0);
  }
  /**
   * To be called when we start the plugin.
   *
   * <p>Should be called only once (when we'll make a full check for the current integrity of the
   * information) Later on, we'll start to check if things change in the PYTHONPATH based on changes
   * in the filesystem.
   */
  public void start() {

    boolean scheduleInitially = false;
    boolean reCheckOnFilesystemChanges =
        InterpreterGeneralPreferencesPage.getReCheckOnFilesystemChanges();

    IInterpreterManager[] managers = PydevPlugin.getAllInterpreterManagers();
    for (IInterpreterManager iInterpreterManager : managers) {
      if (iInterpreterManager != null) {
        IInterpreterInfo[] interpreterInfos = iInterpreterManager.getInterpreterInfos();
        if (reCheckOnFilesystemChanges) {
          this.registerInterpreterManager(iInterpreterManager, interpreterInfos);
        }
        scheduleInitially =
            scheduleInitially || (interpreterInfos != null && interpreterInfos.length > 0);
      }
    }

    int timeout = 1000 * 30; // Default is waiting 30 seconds after startup

    IPreferenceStore preferences = PydevPrefs.getPreferences();
    boolean alreadyChecked =
        preferences.getBoolean(
            "INTERPRETERS_CHECKED_ONCE"); // Now we add builtin indexing on our checks (so, force it
                                          // at least once).
    boolean force = false;
    if (!alreadyChecked) {
      preferences.setValue(
          "INTERPRETERS_CHECKED_ONCE",
          true); // Now we add builtin indexing on our checks (so, force it at least once).
      force = true;
      timeout = 1000 * 7; // In this case, wait only 7 seconds after startup
    }

    if (force || InterpreterGeneralPreferencesPage.getCheckConsistentOnStartup()) {
      if (scheduleInitially) {
        // Only do the initial schedule if there's something to be tracked (otherwise, wait for some
        // interpreter
        // to be configured and work only on deltas already).

        // The initial job will do a full check on what's available and if it's synched with the
        // filesystem.
        job.addAllToTrack();
        job.scheduleLater(timeout); // Wait a minute before starting our sync process.
      }
    }
  }
  /**
   * Adds a set of arguments used to wrap executed file with unittest runner.
   *
   * @param actualRun in an actual run we'll start the xml-rpc server.
   * @param coverageRun whether we should add the flags to do a coverage run.
   */
  private void addUnittestArgs(List<String> cmdArgs, boolean actualRun, boolean coverageRun)
      throws CoreException {
    if (isUnittest()) {

      // The tests are either written to a configuration file or passed as a parameter.
      String configurationFile =
          this.configuration.getAttribute(Constants.ATTR_UNITTEST_CONFIGURATION_FILE, "");
      if (configurationFile.length() > 0) {
        cmdArgs.add("--config_file");
        if (actualRun) {
          // We should write the contents to a temporary file (because it may be too long, so,
          // always write
          // to a file and read from it later on).
          File tempFile = PydevPlugin.getDefault().getTempFile("custom_pydev_unittest_launch_");
          try {
            OutputStream fileOutputStream = new FileOutputStream(tempFile);
            try {
              try {
                fileOutputStream.write(configurationFile.getBytes());
              } catch (IOException e) {
                throw new CoreException(
                    PydevPlugin.makeStatus(IStatus.ERROR, "Error writing to: " + tempFile, e));
              }
            } finally {
              fileOutputStream.close();
            }
          } catch (Exception e) {
            if (e instanceof CoreException) {
              throw (CoreException) e;
            }
            throw new CoreException(
                PydevPlugin.makeStatus(IStatus.ERROR, "Error writing to: " + tempFile, e));
          }
          cmdArgs.add(tempFile.toString());
        } else {
          cmdArgs.add(configurationFile);
        }
      } else {
        String tests = this.configuration.getAttribute(Constants.ATTR_UNITTEST_TESTS, "");
        if (tests.length() > 0) {
          cmdArgs.add("--tests");
          cmdArgs.add(tests);
        }
      }

      if (PyUnitPrefsPage2.getUsePyUnitView()) {
        // If we want to use the PyUnitView, we need to get the port used so that the python side
        // can connect.
        cmdArgs.add("--port");
        if (actualRun) {
          cmdArgs.add(String.valueOf(getPyUnitServer().getPort()));
        } else {
          cmdArgs.add("0");
        }
      }

      if (coverageRun) {
        cmdArgs.add("--coverage_output_dir");
        cmdArgs.add(PyCoverage.getCoverageDirLocation().getAbsolutePath());

        cmdArgs.add("--coverage_include");
        cmdArgs.add(PyCodeCoverageView.getChosenDir().getLocation().toOSString());

        if (actualRun) {
          IPreferenceStore prefs = PydevPrefs.getPreferenceStore();
          int testRunner = prefs.getInt(PyUnitPrefsPage2.TEST_RUNNER);

          switch (testRunner) {
            case PyUnitPrefsPage2.TEST_RUNNER_NOSE:
              RunInUiThread.async(
                  new Runnable() {

                    public void run() {
                      PyDialogHelpers.openWarningWithIgnoreToggle(
                          "Notes for coverage with the nose test runner.",
                          "Note1: When using the coverage with the nose test runner, "
                              + "please don't pass any specific parameter related to "
                              + "the run in the arguments, as that's already handled by PyDev "
                              + "(i.e.: don't use the builtin cover plugin from nose).\n"
                              + "\n"
                              + "Note2: It's currently not possible to use coverage with the multi-process "
                              + "plugin in nose.",
                          "KEY_COVERAGE_WITH_NOSE_TEST_RUNNER");
                    }
                  });

              break;
            case PyUnitPrefsPage2.TEST_RUNNER_PY_TEST:
              RunInUiThread.async(
                  new Runnable() {

                    public void run() {
                      PyDialogHelpers.openCritical(
                          "PyUnit coverage not compatible with the Py.test test runner.",
                          "Currently the PyDev PyUnit integration is not able to provide coverage "
                              + "info using the py.test test runner (please enter a "
                              + "feature request if you'd like that added)\n"
                              + "\n"
                              + "Note: the run will be continued anyways (without gathering coverage info).");
                    }
                  });
              break;
          }
        }
      }

      // Last thing: nose parameters or parameters the user configured.
      for (String s :
          parseStringIntoList(
              PyUnitPrefsPage2.getTestRunnerParameters(this.configuration, this.project))) {
        cmdArgs.add(s);
      }
    }
  }
  /**
   * Sets defaults.
   *
   * @throws InvalidRunException
   * @throws MisconfigurationException
   */
  @SuppressWarnings("unchecked")
  public PythonRunnerConfig(
      ILaunchConfiguration conf, String mode, String run, boolean makeArgumentsVariableSubstitution)
      throws CoreException, InvalidRunException, MisconfigurationException {
    // 1st thing, see if this is a valid run.
    project = getProjectFromConfiguration(conf);

    if (project == null) { // Ok, we could not find it out
      throw Log.log("Could not get project for configuration: " + conf);
    }

    // We need the project to find out the default interpreter from the InterpreterManager.
    IPythonNature pythonNature = PythonNature.getPythonNature(project);
    if (pythonNature == null) {
      CoreException e = Log.log("No python nature for project: " + project.getName());
      throw e;
    }

    // now, go on configuring other things
    this.configuration = conf;
    this.run = run;
    isDebug = mode.equals(ILaunchManager.DEBUG_MODE);
    isInteractive = mode.equals("interactive");

    resource = getLocation(conf, pythonNature);
    arguments = getArguments(conf, makeArgumentsVariableSubstitution);
    IPath workingPath = getWorkingDirectory(conf, pythonNature);
    workingDirectory = workingPath == null ? null : workingPath.toFile();
    acceptTimeout = PydevPrefs.getPreferences().getInt(PydevEditorPrefs.CONNECT_TIMEOUT);

    interpreterLocation =
        getInterpreterLocation(conf, pythonNature, this.getRelatedInterpreterManager());
    interpreter = getInterpreter(interpreterLocation, conf, pythonNature);

    // make the environment
    ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
    envp = launchManager.getEnvironment(conf);
    IInterpreterManager manager;
    if (isJython()) {
      manager = PydevPlugin.getJythonInterpreterManager();
    } else if (isIronpython()) {
      manager = PydevPlugin.getIronpythonInterpreterManager();
    } else {
      manager = PydevPlugin.getPythonInterpreterManager();
    }

    boolean win32 = PlatformUtils.isWindowsPlatform();

    if (envp == null) {
      // ok, the user has done nothing to the environment, just get all the default environment
      // which has the pythonpath in it
      envp = SimpleRunner.getEnvironment(pythonNature, interpreterLocation, manager);

    } else {
      // ok, the user has done something to configure it, so, just add the pythonpath to the
      // current env (if he still didn't do so)
      Map envMap = conf.getAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, (Map) null);

      String pythonpath =
          SimpleRunner.makePythonPathEnvString(pythonNature, interpreterLocation, manager);
      updateVar(pythonNature, manager, win32, envMap, "PYTHONPATH", pythonpath);
      if (isJython()) {
        // Also update the classpath env variable.
        updateVar(pythonNature, manager, win32, envMap, "CLASSPATH", pythonpath);
        // And the jythonpath env variable
        updateVar(pythonNature, manager, win32, envMap, "JYTHONPATH", pythonpath);

      } else if (isIronpython()) {
        // Also update the ironpythonpath env variable.
        updateVar(pythonNature, manager, win32, envMap, "IRONPYTHONPATH", pythonpath);
      }

      // And we also must get the environment variables specified in the interpreter manager.
      envp = interpreterLocation.updateEnv(envp, envMap.keySet());
    }

    boolean hasDjangoNature = project.hasNature(PythonNature.DJANGO_NATURE_ID);

    String settingsModule = null;
    Map<String, String> variableSubstitution = null;
    final String djangoSettingsKey = "DJANGO_SETTINGS_MODULE";
    String djangoSettingsEnvEntry = null;
    try {
      variableSubstitution = pythonNature.getPythonPathNature().getVariableSubstitution();
      settingsModule = variableSubstitution.get(djangoSettingsKey);
      if (settingsModule != null) {
        if (settingsModule.trim().length() > 0) {
          djangoSettingsEnvEntry = djangoSettingsKey + "=" + settingsModule.trim();
        }
      }
    } catch (Exception e1) {
      Log.log(e1);
    }
    if (djangoSettingsEnvEntry == null && hasDjangoNature) {
      // Default if not specified (only add it if the nature is there).
      djangoSettingsEnvEntry = djangoSettingsKey + "=" + project.getName() + ".settings";
    }

    // Note: set flag even if not debugging as the user may use remote-debugging later on.
    boolean geventSupport =
        DebugPrefsPage.getGeventDebugging()
            && pythonNature.getInterpreterType() == IPythonNature.INTERPRETER_TYPE_PYTHON;

    // Now, set the pythonpathUsed according to what's in the environment.
    String p = "";
    for (int i = 0; i < envp.length; i++) {
      String s = envp[i];
      Tuple<String, String> tup = StringUtils.splitOnFirst(s, '=');
      String var = tup.o1;
      if (win32) {
        // On windows it doesn't matter, always consider uppercase.
        var = var.toUpperCase();
      }

      if (var.equals("PYTHONPATH")) {
        p = tup.o2;

      } else if (var.equals(djangoSettingsKey)) {
        // Update it.
        if (djangoSettingsEnvEntry != null) {
          envp[i] = djangoSettingsEnvEntry;
          djangoSettingsEnvEntry = null;
        }
      }

      if (geventSupport) {
        if (var.equals("GEVENT_SUPPORT")) {
          // Flag already set in the environment
          geventSupport = false;
        }
      }
    }

    // Still not added, let's do that now.
    if (djangoSettingsEnvEntry != null) {
      envp = StringUtils.addString(envp, djangoSettingsEnvEntry);
    }
    if (geventSupport) {
      envp = StringUtils.addString(envp, "GEVENT_SUPPORT=True");
    }
    this.pythonpathUsed = p;
  }
 /** @return */
 public static boolean isFoldingEnabled() {
   return PydevPrefs.getPreferences().getBoolean(PyDevCodeFoldingPrefPage.USE_CODE_FOLDING);
 }
 public static boolean getReCheckOnFilesystemChanges() {
   return PydevPrefs.getPreferences().getBoolean(UPDATE_INTERPRETER_INFO_ON_FILESYSTEM_CHANGES);
 }
 public static boolean getCheckConsistentOnStartup() {
   return PydevPrefs.getPreferences().getBoolean(CHECK_CONSISTENT_ON_STARTUP);
 }
    @Override
    protected IStatus run(IProgressMonitor monitor) {
      boolean selectingElementsInDialog = fSynchManager.getSelectingElementsInDialog();
      if (selectingElementsInDialog) {
        // No point in starting a process if the user already has a dialog related to this process
        // open.
        if (SynchSystemModulesManager.DEBUG) {
          System.out.println("Dialog already showing: rescheduling new check for later.");
        }
        this.scheduleLater(20000);
        return Status.OK_STATUS;
      }
      if (SynchSystemModulesManager.DEBUG) {
        System.out.println("Running SynchJob!");
      }

      if (monitor == null) {
        monitor = new NullProgressMonitor();
      }
      ManagerInfoToUpdate managerToNameToInfo;
      synchronized (fManagerToNameToInfoLock) {
        if (this.fManagerToNameToInfo == null || this.fManagerToNameToInfo.size() == 0) {
          return Status.OK_STATUS; // nothing to do if there's nothing there...
        }
        managerToNameToInfo = new ManagerInfoToUpdate(this.fManagerToNameToInfo);
        this.fManagerToNameToInfo = null;
      }

      long initialTime = System.currentTimeMillis();
      ThreadPriorityHelper priorityHelper = new ThreadPriorityHelper(this.getThread());
      priorityHelper.setMinPriority();

      try {
        final DataAndImageTreeNode root = new DataAndImageTreeNode(null, null, null);
        if (monitor.isCanceled()) {
          return Status.OK_STATUS;
        }

        fSynchManager.updateStructures(
            monitor, root, managerToNameToInfo, new CreateInterpreterInfoCallback());
        long delta = System.currentTimeMillis() - initialTime;
        if (SynchSystemModulesManager.DEBUG) {
          System.out.println(
              "Time to check polling for changes in interpreters: " + delta / 1000.0 + " secs.");
        }

        List<TreeNode> initialSelection = new ArrayList<>(0);
        if (root.hasChildren()) {
          initialSelection =
              fSynchManager.createInitialSelectionForDialogConsideringPreviouslyIgnored(
                  root, PydevPrefs.getPreferences());
        }

        if (root.hasChildren() && initialSelection.size() > 0) {
          if (SynchSystemModulesManager.DEBUG) {
            System.out.println("Changes found in PYTHONPATH.");
          }
          fSynchManager.asyncSelectAndScheduleElementsToChangePythonpath(
              root, managerToNameToInfo, initialSelection);
        } else {
          if (SynchSystemModulesManager.DEBUG) {
            System.out.println("PYTHONPATH remained the same.");
          }
          fSynchManager.synchronizeManagerToNameToInfoPythonpath(
              monitor, managerToNameToInfo, null);
        }

      } finally {
        // As jobs are from a thread pool, restore the priority afterwards
        priorityHelper.restoreInitialPriority();
      }
      return Status.OK_STATUS;
    }
  /**
   * Note: This apply is not directly called (it's called through {@link
   * CtxInsensitiveImportComplProposal#apply(ITextViewer, char, int, int)})
   *
   * <p>This is the point where the completion is written. It has to be written and if some import
   * is also available it should be inserted at this point.
   *
   * <p>We have to be careful to only add an import if that's really needed (e.g.: there's no other
   * import that equals the import that should be added).
   *
   * <p>Also, we have to check if this import should actually be grouped with another import that
   * already exists. (and it could be a multi-line import)
   */
  public void apply(IDocument document, char trigger, int stateMask, int offset) {
    if (this.indentString == null) {
      throw new RuntimeException("Indent string not set (not called with a PyEdit as viewer?)");
    }

    if (!triggerCharAppliesCurrentCompletion(trigger, document, offset)) {
      newForcedOffset = offset + 1; // +1 because that's the len of the trigger
      return;
    }

    try {
      PySelection selection = new PySelection(document);
      int lineToAddImport = -1;
      ImportHandleInfo groupInto = null;
      ImportHandleInfo realImportHandleInfo = null;

      boolean groupImports = ImportsPreferencesPage.getGroupImports();

      LineStartingScope previousLineThatStartsScope = null;
      PySelection ps = null;
      if (this.addLocalImport) {
        ps = new PySelection(document, offset);
        int startLineIndex = ps.getStartLineIndex();
        if (startLineIndex == 0) {
          this.addLocalImport = false;
        } else {
          previousLineThatStartsScope =
              ps.getPreviousLineThatStartsScope(
                  PySelection.INDENT_TOKENS,
                  startLineIndex - 1,
                  PySelection.getFirstCharPosition(ps.getCursorLineContents()));
          if (previousLineThatStartsScope == null) {
            // note that if we have no previous scope, it means we're actually on the global scope,
            // so,
            // proceed as usual...
            this.addLocalImport = false;
          }
        }
      }

      if (realImportRep.length() > 0 && !this.addLocalImport) {

        // Workaround for:
        // https://sourceforge.net/tracker/?func=detail&aid=2697165&group_id=85796&atid=577329
        // when importing from __future__ import with_statement, we actually want to add a 'with'
        // token, not
        // with_statement token.
        boolean isWithStatement = realImportRep.equals("from __future__ import with_statement");
        if (isWithStatement) {
          this.fReplacementString = "with";
        }

        if (groupImports) {
          try {
            realImportHandleInfo = new ImportHandleInfo(realImportRep);
            PyImportsHandling importsHandling = new PyImportsHandling(document);
            for (ImportHandle handle : importsHandling) {
              if (handle.contains(realImportHandleInfo)) {
                lineToAddImport =
                    -2; // signal that there's no need to find a line available to add the import
                break;

              } else if (groupInto == null && realImportHandleInfo.getFromImportStr() != null) {
                List<ImportHandleInfo> handleImportInfo = handle.getImportInfo();

                for (ImportHandleInfo importHandleInfo : handleImportInfo) {

                  if (realImportHandleInfo
                      .getFromImportStr()
                      .equals(importHandleInfo.getFromImportStr())) {
                    List<String> commentsForImports = importHandleInfo.getCommentsForImports();
                    if (commentsForImports.size() > 0
                        && commentsForImports.get(commentsForImports.size() - 1).length() == 0) {
                      groupInto = importHandleInfo;
                      break;
                    }
                  }
                }
              }
            }
          } catch (ImportNotRecognizedException e1) {
            Log.log(e1); // that should not happen at this point
          }
        }

        if (lineToAddImport == -1) {
          boolean isFutureImport = PySelection.isFutureImportLine(this.realImportRep);
          lineToAddImport = selection.getLineAvailableForImport(isFutureImport);
        }
      } else {
        lineToAddImport = -1;
      }
      String delimiter = PyAction.getDelimiter(document);

      appliedWithTrigger = trigger == '.' || trigger == '(';
      String appendForTrigger = "";
      if (appliedWithTrigger) {
        if (trigger == '(') {
          appendForTrigger = "()";

        } else if (trigger == '.') {
          appendForTrigger = ".";
        }
      }

      // if the trigger is ')', just let it apply regularly -- so, ')' will only be added if it's
      // already in the completion.

      // first do the completion
      if (fReplacementString.length() > 0) {
        int dif = offset - fReplacementOffset;
        document.replace(offset - dif, dif + this.fLen, fReplacementString + appendForTrigger);
      }
      if (this.addLocalImport) {
        // All the code below is because we don't want to work with a generated AST (as it may not
        // be there),
        // so, we go to the previous scope, find out the valid indent inside it and then got
        // backwards
        // from the position we're in now to find the closer location to where we're now where we're
        // actually able to add the import.
        try {
          int iLineStartingScope;
          if (previousLineThatStartsScope != null) {
            iLineStartingScope = previousLineThatStartsScope.iLineStartingScope;

            // Go to a non-empty line from the line we have and the line we're currently in.
            int iLine = iLineStartingScope + 1;
            String line = ps.getLine(iLine);
            int startLineIndex = ps.getStartLineIndex();
            while (iLine < startLineIndex && (line.startsWith("#") || line.trim().length() == 0)) {
              iLine++;
              line = ps.getLine(iLine);
            }
            if (iLine >= startLineIndex) {
              // Sanity check!
              iLine = startLineIndex;
              line = ps.getLine(iLine);
            }
            int firstCharPos = PySelection.getFirstCharPosition(line);
            // Ok, all good so far, now, this would add the line to the beginning of
            // the element (after the if statement, def, etc.), let's try to put
            // it closer to where we're now (but still in a valid position).
            int j = startLineIndex;
            while (j >= 0) {
              String line2 = ps.getLine(j);
              if (PySelection.getFirstCharPosition(line2) == firstCharPos) {
                iLine = j;
                break;
              }
              if (j == iLineStartingScope) {
                break;
              }
              j--;
            }

            String indent = line.substring(0, firstCharPos);
            String strToAdd = indent + realImportRep + delimiter;
            ps.addLine(
                strToAdd, iLine - 1); // Will add it just after the line passed as a parameter.
            importLen = strToAdd.length();
            return;
          }
        } catch (Exception e) {
          Log.log(e); // Something went wrong, add it as global (i.e.: BUG)
        }
      }

      if (groupInto != null && realImportHandleInfo != null) {
        // let's try to group it
        final int maxCols;
        if (SharedCorePlugin.inTestMode()) {
          maxCols = 80;
        } else {
          IPreferenceStore chainedPrefStore = PydevPrefs.getChainedPrefStore();
          maxCols =
              chainedPrefStore.getInt(
                  AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLUMN);
        }

        int endLine = groupInto.getEndLine();
        IRegion lineInformation = document.getLineInformation(endLine);
        String strToAdd = ", " + realImportHandleInfo.getImportedStr().get(0);

        String line = PySelection.getLine(document, endLine);
        if (line.length() + strToAdd.length() > maxCols) {
          if (line.indexOf('#') == -1) {
            // no comments: just add it in the next line
            int len = line.length();
            if (line.trim().endsWith(")")) {
              len = line.indexOf(")");
              strToAdd =
                  "," + delimiter + indentString + realImportHandleInfo.getImportedStr().get(0);
            } else {
              strToAdd =
                  ",\\" + delimiter + indentString + realImportHandleInfo.getImportedStr().get(0);
            }

            int end = lineInformation.getOffset() + len;
            importLen = strToAdd.length();
            document.replace(end, 0, strToAdd);
            return;
          }

        } else {
          // regular addition (it won't pass the number of columns expected).
          line = PySelection.getLineWithoutCommentsOrLiterals(line);
          int len = line.length();
          if (line.trim().endsWith(")")) {
            len = line.indexOf(")");
          }

          int end = lineInformation.getOffset() + len;
          importLen = strToAdd.length();
          document.replace(end, 0, strToAdd);
          return;
        }
      }

      // if we got here, it hasn't been added in a grouped way, so, let's add it in a new import
      if (lineToAddImport >= 0 && lineToAddImport <= document.getNumberOfLines()) {
        IRegion lineInformation = document.getLineInformation(lineToAddImport);
        String strToAdd = realImportRep + delimiter;
        importLen = strToAdd.length();
        document.replace(lineInformation.getOffset(), 0, strToAdd);
        return;
      }

    } catch (BadLocationException x) {
      Log.log(x);
    }
  }