/** * 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); } }