diff --git a/plugins/org.python.pydev/src/org/python/pydev/editor/correctionassist/docstrings/AssistDocString.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/AssistDocString.java similarity index 99% rename from plugins/org.python.pydev/src/org/python/pydev/editor/correctionassist/docstrings/AssistDocString.java rename to plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/AssistDocString.java index d73422b2a7..d02e3cee21 100644 --- a/plugins/org.python.pydev/src/org/python/pydev/editor/correctionassist/docstrings/AssistDocString.java +++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/AssistDocString.java @@ -9,7 +9,7 @@ * * @author Fabio Zadrozny */ -package org.python.pydev.editor.correctionassist.docstrings; +package org.python.pydev.core.docstrings; import java.io.File; import java.util.ArrayList; @@ -28,7 +28,6 @@ import org.python.pydev.core.IPyEdit; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.autoedit.DefaultIndentPrefs; -import org.python.pydev.core.docstrings.DocstringPreferences; import org.python.pydev.core.docutils.PySelection; import org.python.pydev.core.docutils.PySelection.DocstringInfo; import org.python.pydev.core.proposals.CompletionProposalFactory; diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/AssistDocstringProposalCore.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/AssistDocstringProposalCore.java new file mode 100644 index 0000000000..3ee2110c0c --- /dev/null +++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/AssistDocstringProposalCore.java @@ -0,0 +1,134 @@ +package org.python.pydev.core.docstrings; + +import java.util.List; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.text.edits.ReplaceEdit; +import org.python.pydev.core.docutils.PySelection; +import org.python.pydev.core.docutils.PySelection.DocstringInfo; +import org.python.pydev.shared_core.log.Log; +import org.python.pydev.shared_core.string.FastStringBuffer; + +public class AssistDocstringProposalCore { + + private final String initial; + private final String delimiter; + private final String docStringMarker; + private final String delimiterAndIndent; + private final String preferredDocstringStyle2; + private final boolean inFunctionLine; + private final DocstringInfo finalDocstringFromFunction; + private final String indentation; + private final FastStringBuffer buf; + private final List params; + + public String fReplacementString; + public int fReplacementOffset; + public int fReplacementLength; + public int fCursorPosition; + + public AssistDocstringProposalCore(String initial, String delimiter, String docStringMarker, + String delimiterAndIndent, String preferredDocstringStyle2, boolean inFunctionLine, + DocstringInfo finalDocstringFromFunction, String indentation, FastStringBuffer buf, List params, + String replacementString, int replacementOffset, + int replacementLength, int cursorPosition) { + this.initial = initial; + this.delimiter = delimiter; + this.docStringMarker = docStringMarker; + this.delimiterAndIndent = delimiterAndIndent; + this.preferredDocstringStyle2 = preferredDocstringStyle2; + this.inFunctionLine = inFunctionLine; + this.finalDocstringFromFunction = finalDocstringFromFunction; + this.indentation = indentation; + this.buf = buf; + this.params = params; + + this.fReplacementString = replacementString; + this.fReplacementOffset = replacementOffset; + this.fReplacementLength = replacementLength; + this.fCursorPosition = cursorPosition; + } + + public ReplaceEdit createTextEdit(IDocument document) { + if (inFunctionLine) { + // Let's check if this function already has a docstring (if it does, update the current docstring + // instead of creating a new one). + String updatedDocstring = null; + if (finalDocstringFromFunction != null) { + updatedDocstring = AssistDocString.updatedDocstring(finalDocstringFromFunction.string, params, + delimiter, + initial + indentation, + preferredDocstringStyle2); + } + if (updatedDocstring != null) { + fReplacementOffset = finalDocstringFromFunction.startLiteralOffset; + fReplacementLength = finalDocstringFromFunction.endLiteralOffset + - finalDocstringFromFunction.startLiteralOffset; + int initialLen = buf.length(); + buf.clear(); + fCursorPosition -= initialLen - buf.length(); + buf.append(updatedDocstring); + } else { + // Create the docstrings + for (String paramName : params) { + if (!PySelection.isIdentifier(paramName)) { + continue; + } + if (this.preferredDocstringStyle2.equals("G")) { + buf.append(delimiterAndIndent).append(indentation).append(paramName).append(":"); + } else { + buf.append(delimiterAndIndent).append(preferredDocstringStyle2).append("param ") + .append(paramName) + .append(":"); + } + if (DocstringPreferences.getTypeTagShouldBeGenerated(paramName)) { + buf.append(delimiterAndIndent).append(preferredDocstringStyle2).append("type ") + .append(paramName) + .append(":"); + } + } + } + + } else { + // It's a class declaration - do nothing. + } + if (finalDocstringFromFunction == null) { + buf.append(delimiterAndIndent).append(docStringMarker); + } + + String comp = buf.toString(); + this.fReplacementString = comp; + + //remove the next line if it is a pass... + if (finalDocstringFromFunction == null) { + PySelection ps = new PySelection(document, fReplacementOffset); + int iNextLine = ps.getCursorLine() + 1; + String nextLine = ps.getLine(iNextLine); + if (nextLine.trim().equals("pass")) { + try { + IRegion lineInformation = document.getLineInformation(iNextLine); + int offset = lineInformation.getOffset(); + + int length = -1; + + if (document.getNumberOfLines() > iNextLine) { + int nextLineOffset = document.getLineInformation(iNextLine + 1).getOffset(); + length = nextLineOffset - offset; + } else { + length = lineInformation.getLength(); + } + + if (length > -1) { + int endOffset = offset + nextLine.length(); + fReplacementLength = endOffset - fReplacementOffset; + } + } catch (Exception e) { + Log.log(e); + } + } + } + ReplaceEdit replaceEdit = new ReplaceEdit(fReplacementOffset, fReplacementLength, fReplacementString); + return replaceEdit; + } +} diff --git a/plugins/org.python.pydev.shared_core/src/org/python/pydev/shared_core/cache/FIFOCache.java b/plugins/org.python.pydev.shared_core/src/org/python/pydev/shared_core/cache/FIFOCache.java new file mode 100644 index 0000000000..b7f63f0926 --- /dev/null +++ b/plugins/org.python.pydev.shared_core/src/org/python/pydev/shared_core/cache/FIFOCache.java @@ -0,0 +1,24 @@ +package org.python.pydev.shared_core.cache; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class FIFOCache extends LinkedHashMap { + + private static final long serialVersionUID = 1L; + + private final int maxSize; + + public FIFOCache(int maxSize) { + super(maxSize + 1, 1.0f, false); // The difference from lru to fifo is the `false` in the constructor. + if (maxSize <= 0) { + throw new AssertionError("Max size must be > 0."); + } + this.maxSize = maxSize; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > maxSize; + } +} \ No newline at end of file diff --git a/plugins/org.python.pydev.shared_core/src/org/python/pydev/shared_core/cache/LRUMap.java b/plugins/org.python.pydev.shared_core/src/org/python/pydev/shared_core/cache/LRUMap.java index 82f6335599..f52a53b798 100644 --- a/plugins/org.python.pydev.shared_core/src/org/python/pydev/shared_core/cache/LRUMap.java +++ b/plugins/org.python.pydev.shared_core/src/org/python/pydev/shared_core/cache/LRUMap.java @@ -20,7 +20,7 @@ public final class LRUMap extends LinkedHashMap { private int maxSize; public LRUMap(int maxSize) { - super(maxSize < 8 ? maxSize : 8); //initial capacity = max size or 8 if max size is big. + super(maxSize + 1); if (maxSize <= 0) { throw new AssertionError("Max size must be > 0."); } @@ -29,7 +29,7 @@ public LRUMap(int maxSize) { // This method is called just after a new entry has been added @Override - public boolean removeEldestEntry(Map.Entry eldest) { + public boolean removeEldestEntry(Map.Entry eldest) { return size() > this.maxSize; } diff --git a/plugins/org.python.pydev/src/org/python/pydev/editor/correctionassist/PythonCorrectionProcessor.java b/plugins/org.python.pydev/src/org/python/pydev/editor/correctionassist/PythonCorrectionProcessor.java index ae16a155d0..1db01f7ddb 100644 --- a/plugins/org.python.pydev/src/org/python/pydev/editor/correctionassist/PythonCorrectionProcessor.java +++ b/plugins/org.python.pydev/src/org/python/pydev/editor/correctionassist/PythonCorrectionProcessor.java @@ -34,10 +34,10 @@ import org.python.pydev.core.IPySyntaxHighlightingAndCodeCompletionEditor; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.MisconfigurationException; +import org.python.pydev.core.docstrings.AssistDocString; import org.python.pydev.core.docutils.PySelection; import org.python.pydev.core.log.Log; import org.python.pydev.editor.PyEdit; -import org.python.pydev.editor.correctionassist.docstrings.AssistDocString; import org.python.pydev.editor.correctionassist.heuristics.AssistAssign; import org.python.pydev.editor.correctionassist.heuristics.AssistFString; import org.python.pydev.editor.correctionassist.heuristics.AssistImport; diff --git a/plugins/org.python.pydev/src/org/python/pydev/editor/correctionassist/docstrings/AssistDocstringCompletionProposal.java b/plugins/org.python.pydev/src/org/python/pydev/editor/correctionassist/docstrings/AssistDocstringCompletionProposal.java index 07860bcf7e..3338aa85ab 100644 --- a/plugins/org.python.pydev/src/org/python/pydev/editor/correctionassist/docstrings/AssistDocstringCompletionProposal.java +++ b/plugins/org.python.pydev/src/org/python/pydev/editor/correctionassist/docstrings/AssistDocstringCompletionProposal.java @@ -2,27 +2,21 @@ import java.util.List; +import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.IContextInformation; -import org.python.pydev.core.docstrings.DocstringPreferences; -import org.python.pydev.core.docutils.PySelection; +import org.eclipse.text.edits.MalformedTreeException; +import org.eclipse.text.edits.ReplaceEdit; +import org.python.pydev.core.docstrings.AssistDocstringProposalCore; import org.python.pydev.core.docutils.PySelection.DocstringInfo; import org.python.pydev.editor.codecompletion.proposals.PyCompletionProposal; import org.python.pydev.shared_core.image.IImageHandle; +import org.python.pydev.shared_core.log.Log; import org.python.pydev.shared_core.string.FastStringBuffer; public class AssistDocstringCompletionProposal extends PyCompletionProposal { - private final String initial; - private final String delimiter; - private final String docStringMarker; - private final String delimiterAndIndent; - private final String preferredDocstringStyle2; - private final boolean inFunctionLine; - private final DocstringInfo finalDocstringFromFunction; - private final String indentation; - private final FastStringBuffer buf; - private final List params; + private final AssistDocstringProposalCore core; public AssistDocstringCompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, IImageHandle image, String displayString, @@ -33,78 +27,25 @@ public AssistDocstringCompletionProposal(String replacementString, int replaceme List params) { super(replacementString, replacementOffset, replacementLength, cursorPosition, image, displayString, contextInformation, additionalProposalInfo, priority, compareContext); - this.initial = initial; - this.delimiter = delimiter; - this.docStringMarker = docStringMarker; - this.delimiterAndIndent = delimiterAndIndent; - this.preferredDocstringStyle2 = preferredDocstringStyle2; - this.inFunctionLine = inFunctionLine; - this.finalDocstringFromFunction = finalDocstringFromFunction; - this.indentation = indentation; - this.buf = buf; - this.params = params; + core = new AssistDocstringProposalCore(initial, delimiter, docStringMarker, delimiterAndIndent, + preferredDocstringStyle2, inFunctionLine, finalDocstringFromFunction, indentation, buf, params, + replacementString, replacementOffset, replacementLength, cursorPosition); } @Override public void apply(IDocument document) { - if (inFunctionLine) { - // Let's check if this function already has a docstring (if it does, update the current docstring - // instead of creating a new one). - String updatedDocstring = null; - if (finalDocstringFromFunction != null) { - updatedDocstring = AssistDocString.updatedDocstring(finalDocstringFromFunction.string, params, - delimiter, - initial + indentation, - preferredDocstringStyle2); - } - if (updatedDocstring != null) { - fReplacementOffset = finalDocstringFromFunction.startLiteralOffset; - fReplacementLength = finalDocstringFromFunction.endLiteralOffset - - finalDocstringFromFunction.startLiteralOffset; - int initialLen = buf.length(); - buf.clear(); - fCursorPosition -= initialLen - buf.length(); - buf.append(updatedDocstring); - } else { - // Create the docstrings - for (String paramName : params) { - if (!PySelection.isIdentifier(paramName)) { - continue; - } - if (this.preferredDocstringStyle2.equals("G")) { - buf.append(delimiterAndIndent).append(indentation).append(paramName).append(":"); - } else { - buf.append(delimiterAndIndent).append(preferredDocstringStyle2).append("param ") - .append(paramName) - .append(":"); - } - if (DocstringPreferences.getTypeTagShouldBeGenerated(paramName)) { - buf.append(delimiterAndIndent).append(preferredDocstringStyle2).append("type ") - .append(paramName) - .append(":"); - } - } - } - - } else { - // It's a class declaration - do nothing. - } - if (finalDocstringFromFunction == null) { - buf.append(delimiterAndIndent).append(docStringMarker); - } - - String comp = buf.toString(); - this.fReplacementString = comp; - - //remove the next line if it is a pass... - if (finalDocstringFromFunction == null) { - PySelection ps = new PySelection(document, fReplacementOffset); - int iNextLine = ps.getCursorLine() + 1; - String nextLine = ps.getLine(iNextLine); - if (nextLine.trim().equals("pass")) { - ps.deleteLine(iNextLine); - } + ReplaceEdit replaceEdit = core.createTextEdit(document); + + // We need to update some of our fields for the proper selection afterwards. + this.fReplacementString = core.fReplacementString; + this.fReplacementOffset = core.fReplacementOffset; + this.fReplacementLength = core.fReplacementLength; + this.fCursorPosition = core.fCursorPosition; + + try { + replaceEdit.apply(document); + } catch (MalformedTreeException | BadLocationException e) { + Log.log(e); } - super.apply(document); } } \ No newline at end of file diff --git a/plugins/org.python.pydev/tests/org/python/pydev/editor/correctionassist/AssistDocStringTest.java b/plugins/org.python.pydev/tests/org/python/pydev/editor/correctionassist/AssistDocStringTest.java index af524eae47..c1ec36c6a6 100644 --- a/plugins/org.python.pydev/tests/org/python/pydev/editor/correctionassist/AssistDocStringTest.java +++ b/plugins/org.python.pydev/tests/org/python/pydev/editor/correctionassist/AssistDocStringTest.java @@ -12,11 +12,11 @@ import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; +import org.python.pydev.core.docstrings.AssistDocString; import org.python.pydev.core.docstrings.DocstringPreferences; import org.python.pydev.core.docutils.PySelection; import org.python.pydev.core.proposals.CompletionProposalFactory; import org.python.pydev.editor.codecompletion.proposals.DefaultCompletionProposalFactory; -import org.python.pydev.editor.correctionassist.docstrings.AssistDocString; import org.python.pydev.shared_core.code_completion.ICompletionProposalHandle; import org.python.pydev.shared_core.string.StringUtils; import org.python.pydev.shared_core.string.TextSelectionUtils;