diff --git a/plugins/org.python.pydev.core/META-INF/MANIFEST.MF b/plugins/org.python.pydev.core/META-INF/MANIFEST.MF index e3cebb0a31..284629f1db 100644 --- a/plugins/org.python.pydev.core/META-INF/MANIFEST.MF +++ b/plugins/org.python.pydev.core/META-INF/MANIFEST.MF @@ -40,6 +40,7 @@ Export-Package: org.python.copiedfromeclipsesrc, org.python.pydev.core.preferences, org.python.pydev.core.proposals, org.python.pydev.core.structure, + org.python.pydev.core.wrap_paragraph, org.python.pydev.json.eclipsesource, org.python.pydev.refactoring.core.base, org.python.pydev.shared_ui.search.replace diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/IPyEdit.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/IPyEdit.java index 584ecc1eca..41f0d410a5 100644 --- a/plugins/org.python.pydev.core/src/org/python/pydev/core/IPyEdit.java +++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/IPyEdit.java @@ -45,4 +45,6 @@ public interface IPyEdit extends IParserObserver, IBaseEditor, IPyFormatStdProvi /* PyParser */ Object getParser(); + int getPrintMarginColums(); + } diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/wrap_paragraph/AssistWrapParagraph.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/wrap_paragraph/AssistWrapParagraph.java new file mode 100644 index 0000000000..c9e0eb9d87 --- /dev/null +++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/wrap_paragraph/AssistWrapParagraph.java @@ -0,0 +1,77 @@ +package org.python.pydev.core.wrap_paragraph; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITypedRegion; +import org.eclipse.text.edits.ReplaceEdit; +import org.python.pydev.core.IAssistProps; +import org.python.pydev.core.IPyEdit; +import org.python.pydev.core.IPythonNature; +import org.python.pydev.core.MisconfigurationException; +import org.python.pydev.core.docutils.ParsingUtils; +import org.python.pydev.core.docutils.PySelection; +import org.python.pydev.core.partition.PyPartitionScanner; +import org.python.pydev.core.proposals.CompletionProposalFactory; +import org.python.pydev.shared_core.code_completion.ICompletionProposalHandle; +import org.python.pydev.shared_core.code_completion.IPyCompletionProposal; +import org.python.pydev.shared_core.image.IImageCache; +import org.python.pydev.shared_core.image.IImageHandle; +import org.python.pydev.shared_core.image.UIConstants; +import org.python.pydev.shared_core.partitioner.FastPartitioner; + +public class AssistWrapParagraph implements IAssistProps { + + private Paragrapher paragrapher; + + @Override + public List getProps(PySelection ps, IImageCache imageCache, File f, + IPythonNature nature, IPyEdit edit, int offset) throws BadLocationException, MisconfigurationException { + ReplaceEdit replaceEdit = paragrapher.getReplaceEdit(); + + String replacementString = replaceEdit.getText(); + int replacementOffset = replaceEdit.getOffset(); + int replacementLength = replaceEdit.getLength(); + int cursorPosition = replaceEdit.getText().length(); + String displayString = "Wrap text"; + Object contextInformation = null; + String additionalProposalInfo = null; + int priority = IPyCompletionProposal.PRIORITY_DEFAULT; + + IImageHandle image = imageCache != null ? imageCache.get(UIConstants.COMPLETION_TEMPLATE) : null; + + ICompletionProposalHandle proposal = CompletionProposalFactory.get().createPyCompletionProposal( + replacementString, replacementOffset, + replacementLength, cursorPosition, image, displayString, + contextInformation, additionalProposalInfo, priority); + List ret = new ArrayList<>(); + ret.add(proposal); + + return ret; + } + + @Override + public boolean isValid(PySelection ps, String sel, IPyEdit edit, int offset) { + IDocument doc = ps.getDoc(); + ITypedRegion partition = ((FastPartitioner) PyPartitionScanner.checkPartitionScanner(doc)).getPartition(offset); + + // Only valid for strings or comments. + if (!ParsingUtils.isStringContentType(partition.getType()) + && !ParsingUtils.isCommentContentType(partition.getType())) { + return false; + } + + int noCols = edit.getPrintMarginColums(); + paragrapher = new Paragrapher(ps, noCols); + String errorMsg = paragrapher.getValidErrorInPos(); + if (errorMsg == null) { + return true; + } + + return false; + } + +} diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/wrap_paragraph/Paragrapher.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/wrap_paragraph/Paragrapher.java new file mode 100644 index 0000000000..233dc97289 --- /dev/null +++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/wrap_paragraph/Paragrapher.java @@ -0,0 +1,211 @@ +package org.python.pydev.core.wrap_paragraph; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.text.edits.ReplaceEdit; +import org.python.pydev.core.docutils.PySelection; +import org.python.pydev.shared_core.string.TextSelectionUtils; + +public class Paragrapher { + private PySelection selection; + private IDocument document; + + private int offset; + public int currentLineNo; + + private String currentLine; + + public String leadingString; + public String mainText; + + public int offsetOfOriginalParagraph; + public int lengthOfOriginalParagraph; + + private int numberOfLinesInDocument; + public final String docDelimiter; + private int noCols; + private List paragraph; + + private static Pattern pattern = Pattern + .compile("(\\s*#\\s*|\\s*\"\"\"\\s*|\\s*'''\\s*|\\s*\"\\s*|\\s*'\\s*|\\s*)"); + + public Paragrapher(PySelection selection, int noCols) { + this.noCols = noCols; + this.selection = selection; + this.document = selection.getDoc(); + + this.docDelimiter = TextSelectionUtils.getDelimiter(document); + this.offset = selection.getAbsoluteCursorOffset(); + this.currentLineNo = selection.getLineOfOffset(offset); + + this.currentLine = selection.getLine(currentLineNo); + + Matcher matcher = pattern.matcher(currentLine); + if (matcher.find()) { + this.leadingString = currentLine.substring(0, matcher.end()); + this.mainText = currentLine.substring(matcher.end()); + } + + this.offsetOfOriginalParagraph = 0; + this.lengthOfOriginalParagraph = 0; + + this.numberOfLinesInDocument = document.getNumberOfLines(); + } + + private String[] splitLine(String line) { + Matcher matcher = pattern.matcher(line); + if (matcher.find()) { + String leadingString = line.substring(0, matcher.end()); + String mainText = line.substring(matcher.end()); + return new String[] { leadingString, mainText }; + } + return new String[] { "", line }; // Fallback if no match + } + + public String getCurrentLine() { + this.currentLine = selection.getLine(currentLineNo); + this.mainText = splitLine(currentLine)[1]; + return this.mainText; + } + + public boolean previousLineIsInParagraph() { + if (currentLineNo == 0) { + return false; + } + + String previousLine = selection.getLine(currentLineNo - 1); + String[] previousLineParts = splitLine(previousLine); + String leadingStringOfPreviousLine = previousLineParts[0]; + String mainTextOfPreviousLine = previousLineParts[1]; + + if (mainTextOfPreviousLine.trim().isEmpty() || !leadingStringOfPreviousLine.equals(leadingString)) { + String line = selection.getLine(currentLineNo); + int lineEndsAt; + try { + lineEndsAt = selection.getEndLineOffset(currentLineNo); + } catch (BadLocationException e) { + return false; + } + offsetOfOriginalParagraph = lineEndsAt - line.length(); + return false; + } else { + return true; + } + } + + public boolean nextLineIsInParagraph() { + if (currentLineNo + 1 == numberOfLinesInDocument) { + return false; + } + + String nextLine = selection.getLine(currentLineNo + 1); + String[] nextLineParts = splitLine(nextLine); + String leadingStringOfNextLine = nextLineParts[0]; + String mainTextOfNextLine = nextLineParts[1]; + + if (mainTextOfNextLine.trim().isEmpty() || !leadingStringOfNextLine.equals(leadingString)) { + try { + lengthOfOriginalParagraph = selection.getEndLineOffset(currentLineNo) - offsetOfOriginalParagraph; + } catch (BadLocationException e) { + return false; + } + return false; + } else { + return true; + } + } + + public String getValidErrorInPos() { + // Start building a list of lines of text in paragraph + paragraph = new ArrayList<>(); + paragraph.add(this.getCurrentLine()); + + // Check if it's a docstring (""" or ' or ") + boolean isDocstring = this.leadingString.contains("\"\"\"") || + this.leadingString.contains("'") || + this.leadingString.contains("\""); + + if (isDocstring) { + return "Cannot rewrap docstrings"; + } + + if (paragraph.get(0).trim().isEmpty()) { + return "Currect selection is empty"; + } + + // Don't wrap empty lines or docstrings + int startingLineNo = this.currentLineNo; + + // Add the lines before the line containing the selection + while (this.previousLineIsInParagraph()) { + this.currentLineNo--; + paragraph.add(0, this.getCurrentLine()); + } + + // Add the lines after the line containing the selection + this.currentLineNo = startingLineNo; + while (this.nextLineIsInParagraph()) { + this.currentLineNo++; + paragraph.add(this.getCurrentLine()); + } + + if (paragraph.size() == 1 && paragraph.get(0).length() < noCols - this.leadingString.length()) { + return "Current selection cannot be wrapped"; + } + return null; + } + + public ReplaceEdit getReplaceEdit() { + // Rewrap the paragraph + List wrappedParagraph = new ArrayList<>(); + + for (String line : paragraph) { + wrappedParagraph.add(line.trim() + " "); + } + + String fullParagraph = String.join("", wrappedParagraph); + List rewrappedParagraph = wrapText(fullParagraph, noCols - this.leadingString.length()); + + // Add line terminators + List finalParagraph = new ArrayList<>(); + for (String line : rewrappedParagraph) { + finalParagraph.add(this.leadingString + line + this.docDelimiter); + } + + // Adjust the last line to remove the delimiter + if (!finalParagraph.isEmpty()) { + String lastLine = finalParagraph.get(finalParagraph.size() - 1); + lastLine = lastLine.replace(this.docDelimiter, ""); + finalParagraph.set(finalParagraph.size() - 1, lastLine); + } + + // Replace the original paragraph + String newText = String.join("", finalParagraph); + ReplaceEdit replaceEdit = new ReplaceEdit(this.offsetOfOriginalParagraph, this.lengthOfOriginalParagraph, + newText); + return replaceEdit; + } + + public static List wrapText(String text, int width) { + // Mimic Python's textwrap functionality + List lines = new ArrayList<>(); + int start = 0; + while (start < text.length()) { + int end = Math.min(text.length(), start + width); + if (end < text.length() && !Character.isWhitespace(text.charAt(end))) { + while (end > start && !Character.isWhitespace(text.charAt(end))) { + end--; + } + } + lines.add(text.substring(start, end).trim()); + start = end; + } + return lines; + } + +} diff --git a/plugins/org.python.pydev.jython/jysrc/pyedit_wrap_paragraph.py b/plugins/org.python.pydev.jython/jysrc/pyedit_wrap_paragraph.py deleted file mode 100644 index 76d5339e50..0000000000 --- a/plugins/org.python.pydev.jython/jysrc/pyedit_wrap_paragraph.py +++ /dev/null @@ -1,292 +0,0 @@ -""" Wrap Paragraph by Don Taylor. - -A Pydev script for rewrapping the current paragraph to fit inside the print -margin preference in Eclipse (defaults to 80 columns). A paragraph is a block -of lines with a common leading string such as '# ' or a number of spaces. The -lines in the newly wrapped paragraph will all have the same leading string as -the original paragraph. - -Usage: Position cursor inside paragraph to be rewrapped and hit , w - -Caveats: Embedded tabs are always replaced by single spaces. - Does not wrap if the cursor is within the first line of a docstring. - Wrap Paragraph makes simple assumptions about paragraphs. Check your - results, will undo the last rewrap. - -Note: Activates with 'w' by default. Edit the constants ACTIVATION_STRING - and WAIT_FOR_ENTER near the end of this file if this does not suit your - needs. - -Version: 0.1.1 - alpha - -Date: May 2006 - -License: Available under the same conditions as PyDev. See PyDev license for - details: http://pydev.sourceforge.net - -Support: Contact the author for bug reports/feature requests via the Pydev - users list (or use the source). - -History: 20 May 2006 - Initial release. - 21 May 2006 - Changed no of columns wrapped from 80 to the Eclipse - setting for the print margin preference. - -""" -#=============================================================================== -# The following is a copy of textwrap.py from the CPython 2.4 standard library -# - slightly modified for Jython 2.1 compatibility. Included here directly -# instead of as an imported module so that the Wrap Paragraph Jython Pydev -# extension can consist of a single file. The extension code starts at around -# line 400. -#=============================================================================== -"""Text wrapping and filling. -""" - -# Copyright (C) 1999-2001 Gregory P. Ward. -# Copyright (C) 2002, 2003 Python Software Foundation. -# Written by Greg Ward - -__revision__ = "$Id$" - - -#=============================================================================== -# Pydev Extensions in Jython code protocol -#=============================================================================== -if False: - from org.python.pydev.editor import PyEdit #@UnresolvedImport - cmd = 'command string' - editor = PyEdit - systemGlobals = {} - -#---------------------------- REQUIRED LOCALS----------------------------------- -# interface: String indicating which command will be executed As this script -# will be watching the PyEdit (that is the actual editor in Pydev), and this -# script will be listening to it, this string can indicate any of the methods of -# org.python.pydev.editor.IPyEditListener -assert cmd is not None - -# interface: PyEdit object: this is the actual editor that we will act upon -assert editor is not None - -if cmd == 'onCreateActions': -#----------------------------------Paragrapher---------------------------------- - Paragrapher = systemGlobals.get('Paragrapher') - if Paragrapher is None: - class Paragrapher: - ''' Provides tools to process a paragraph of text in the Pydev editor. - - ''' - def __init__(self, editor): - self.selection = editor.createPySelection() - self.document = editor.getDocument() - - self.offset = self.selection.getAbsoluteCursorOffset() - self.currentLineNo = self.selection.getLineOfOffset(self.offset) - - self.docDelimiter = self.selection.getDelimiter(self.document) - self.currentLine = self.selection.getLine(self.currentLineNo) - - self.pattern = r'''(\s*#\s*|\s*"""\s*|''' \ - + r"""\s*'''\s*|""" \ - + r'''\s*"\s*|''' \ - + r"""\s*'\s*|\s*)""" - - import re - self.compiledRe = re.compile(self.pattern) - - self.leadingString, self.mainText = \ - self._splitLine(self.currentLine) - - self.offsetOfOriginalParagraph = 0 - self.lengthOfOriginalParagraph = 0 - - self.numberOfLinesInDocument = self.document.getNumberOfLines() - - def _splitLine(self, line): - ''' _splitLine(string: line) -> (string: leadingString,\ - string: mainText) - - Split the line into two parts - a leading string and the remaining - text. - ''' - - matched = self.compiledRe.match(line) - leadingString = line[0:matched.end()] - mainText = line[matched.end():] - - return (leadingString, mainText) - - def getCurrentLine(self): - ''' getCurrentLine() -> string - - Return the main part of the text of the current line as a string. - ''' - - self.currentLine = self.selection.getLine(self.currentLineNo) - self.mainText = self._splitLine(self.currentLine)[1] - return self.mainText - - def previousLineIsInParagraph(self): - ''' previousLineIsInParagraph() -> bool ''' - - previousLine = self.selection.getLine(self.currentLineNo - 1) - leadingStringOfPreviousLine, mainTextOfPreviousLine = \ - self._splitLine(previousLine) - - if (self.currentLineNo == 0) | \ - (mainTextOfPreviousLine.strip() == "") | \ - (leadingStringOfPreviousLine != self.leadingString): # diff para [1] - line = self.selection.getLine(self.currentLineNo) - lineEndsAt = self.selection.getEndLineOffset(self.currentLineNo) - self.offsetOfOriginalParagraph = lineEndsAt - len(line) - return False - else: - return True # same para - - # [1] The current line is the first line of a paragraph. Calculate - # starting offset of the first character of the original paragraph. - - def nextLineIsInParagraph(self): - ''' nextLineIsInParagraph() -> bool ''' - - nextLine = self.selection.getLine(self.currentLineNo + 1) - leadingStringOfNextLine, mainTextOfNextLine = \ - self._splitLine(nextLine) - - if (self.currentLineNo + 1 == self.numberOfLinesInDocument) | \ - (mainTextOfNextLine.strip() == "") | \ - (leadingStringOfNextLine != self.leadingString): # diff para [1] - self.lengthOfOriginalParagraph = \ - self.selection.getEndLineOffset(self.currentLineNo) - \ - self.offsetOfOriginalParagraph - return False - else: - return True # same para - - # [1] The current line is the last line of a paragraph. Calculate - # the length of the original paragraph. - systemGlobals['Paragrapher'] = Paragrapher - -#------------------------------end of Paragrapher------------------------------- - - WrapParagraph = systemGlobals.get('WrapParagraph') - if WrapParagraph is None: - Action = editor.getActionClass() #from org.eclipse.jface.action import Action #@UnresolvedImport - from java.lang import Runnable #@UnresolvedImport - - class WrapParagraph(Action): - ''' Rewrap the text of the current paragraph. - - WrapParagraph searches for the beginning and end of the paragraph that - contains the selection, rewraps it to fit into 79 character lines and - replaces the original paragraph with the newly wrapped paragraph. - - The current paragraph is the text surrounding the current selection - point. - - Only one paragraph at a time is wrapped. - - A paragraph is a consecutive block of lines whose alphanumeric text all - begins at the same column position. Any constant leading string will be - retained in the newly wrapped paragraph. This handles indented - paragraphs and # comment blocks, and avoids wrapping indented code - examples - but not code samples that are not indented. - - The first, or only, line of a docstring is handled as a special case and - is not wrapped at all. - - ''' - - def __init__(self, editor): - self.editor = editor - - - def displayStatusMessage(self): - self.editor.setMessage(False, "Cannot rewrap docstrings") - - - class RunInUi(Runnable): - '''Helper class that implements a Runnable (just so that we - can pass it to the Java side). It simply calls some callable. - ''' - - def __init__(self, c): - self.callable = c - def run(self): - self.callable() - - - def run(self): - editor = self.editor - p = Paragrapher(editor) - - # Start building a list of lines of text in paragraph - paragraph = [p.getCurrentLine()] - - isDocstring = (p.leadingString.find('"""') != -1) | \ - (p.leadingString.find("'") != -1) | \ - (p.leadingString.find('"') != -1) - if isDocstring: - editor.asyncExec(self.RunInUi(self.displayStatusMessage)) - - # Don't wrap empty lines or docstrings. - if ((paragraph[0].strip() != "") & (not isDocstring)): - startingLineNo = p.currentLineNo - # Add the lines before the line containing the selection. - while p.previousLineIsInParagraph(): - p.currentLineNo -= 1 - paragraph.insert(0, p.getCurrentLine()) - - # Add the lines after the line containing the selection. - p.currentLineNo = startingLineNo - while p.nextLineIsInParagraph(): - p.currentLineNo += 1 - paragraph.append(p.getCurrentLine()) - - # paragraph now contains all of the lines so rewrap it [1]. - noCols = editor.getPrintMarginColums() - paragraph = [line.rstrip() + " " for line in paragraph] - - import textwrap - paragraph = textwrap.wrap("".join(paragraph), \ - width=noCols - len(p.leadingString), \ - expand_tabs=False, \ - ) - # Add line terminators. - paragraph = map((lambda aLine: p.leadingString + aLine + \ - p.docDelimiter), paragraph) - paragraph[-1] = paragraph[-1].replace(p.docDelimiter, "") # [2] - - # Replace original paragraph. - p.document.replace(p.offsetOfOriginalParagraph, \ - p.lengthOfOriginalParagraph, \ - "".join(paragraph)) - # and we are done. - - - # [1] paragraph now contains all of the lines of the paragraph to be - # rewrapped and the lines have all been stripped of their leading - # strings. - # - # Rewrap the paragraph allowing space to insert the leading strings back - # in again after the wrapping is done. But first we need to make sure - # that there is at least one space at the end of each line otherwise the - # wrap routine will combine the last word of one line with the first - # word of the next line. We cannot just add a space as this will be - # kept if there is one there already so strip off any trailing white - # space first and add back just a single space character. - # - # [2] Add line terminators to the end of every line in paragraph except - # the last line otherwise the new paragraph will have an extra line - # terminator at the end. - - systemGlobals['WrapParagraph'] = WrapParagraph - - # Change these constants if the default does not suit your needs - ACTIVATION_STRING = 'w' - WAIT_FOR_ENTER = False - - # Register the extension as an ActionListener. - editor.addOfflineActionListener(ACTIVATION_STRING, WrapParagraph(editor), \ - 'Wrap paragraph', \ - WAIT_FOR_ENTER) diff --git a/plugins/org.python.pydev/src/org/python/pydev/editor/PyEdit.java b/plugins/org.python.pydev/src/org/python/pydev/editor/PyEdit.java index 9c9919f361..c5809369f1 100644 --- a/plugins/org.python.pydev/src/org/python/pydev/editor/PyEdit.java +++ b/plugins/org.python.pydev/src/org/python/pydev/editor/PyEdit.java @@ -123,6 +123,7 @@ import org.python.pydev.editor.actions.PyOpenAction; import org.python.pydev.editor.actions.PyOrganizeImports; import org.python.pydev.editor.actions.PyPeerLinker; +import org.python.pydev.editor.actions.PyWrapParagraphAction; import org.python.pydev.editor.codecompletion.proposals.PyCompletionProposal; import org.python.pydev.editor.codefolding.CodeFoldingSetter; import org.python.pydev.editor.codefolding.PyEditProjection; @@ -1161,7 +1162,9 @@ protected void createActions() { setAction(ITextEditorActionConstants.MOVE_LINE_DOWN, action); } + addOfflineActionListener("w", new PyWrapParagraphAction(this), "Wrap paragraph", false); notifier.notifyOnCreateActions(resources); + onCreateActions.call(this); } catch (Throwable e) { Log.log(e); diff --git a/plugins/org.python.pydev/src/org/python/pydev/editor/actions/PyWrapParagraphAction.java b/plugins/org.python.pydev/src/org/python/pydev/editor/actions/PyWrapParagraphAction.java new file mode 100644 index 0000000000..281143143c --- /dev/null +++ b/plugins/org.python.pydev/src/org/python/pydev/editor/actions/PyWrapParagraphAction.java @@ -0,0 +1,44 @@ +package org.python.pydev.editor.actions; + +import java.util.List; + +import org.eclipse.jface.action.Action; +import org.eclipse.text.edits.ReplaceEdit; +import org.python.pydev.core.docutils.PySelection; +import org.python.pydev.core.log.Log; +import org.python.pydev.core.wrap_paragraph.Paragrapher; +import org.python.pydev.editor.IOfflineActionWithParameters; +import org.python.pydev.editor.PyEdit; + +public class PyWrapParagraphAction extends Action implements IOfflineActionWithParameters { + + protected List parameters; + protected PyEdit edit; + + public PyWrapParagraphAction(PyEdit edit) { + this.edit = edit; + } + + @Override + public void setParameters(List parameters) { + this.parameters = parameters; + } + + @Override + public void run() { + try { + PySelection ps = new PySelection(edit); + Paragrapher p = new Paragrapher(ps, edit.getPrintMarginColums()); + String errorMsg = p.getValidErrorInPos(); + if (errorMsg == null) { + ReplaceEdit replaceEdit = p.getReplaceEdit(); + replaceEdit.apply(ps.getDoc()); + } else { + edit.setMessage(false, errorMsg); + } + } catch (Exception e) { + Log.log(e); + } + } + +} diff --git a/plugins/org.python.pydev/tests/org/python/pydev/parser/PyParserEditorIntegrationTest.java b/plugins/org.python.pydev/tests/org/python/pydev/parser/PyParserEditorIntegrationTest.java index d5501c75a3..c5d05fa12f 100644 --- a/plugins/org.python.pydev/tests/org/python/pydev/parser/PyParserEditorIntegrationTest.java +++ b/plugins/org.python.pydev/tests/org/python/pydev/parser/PyParserEditorIntegrationTest.java @@ -187,6 +187,11 @@ public boolean isCythonFile() { public ICoreTextSelection getTextSelection() { throw new RuntimeException("Not implemented"); } + + @Override + public int getPrintMarginColums() { + throw new RuntimeException("Not implemented"); + } } public static void main(String[] args) {