/** * Creates the errors that are related to a bad indentation (number of space chars is not ok). * * @param monitor */ private static void createBadIndentForSpacesMessages( IDocument doc, IAnalysisPreferences analysisPrefs, IIndentPrefs indentPrefs, ArrayList<IMessage> ret, List<Tuple3<String, Integer, Boolean>> validsAre, IProgressMonitor monitor) { int tabWidth = indentPrefs.getTabWidth(); // if we're analyzing the spaces, let's mark invalid indents (tabs are not searched for those // because // a tab always marks a full indent). FastStringBuffer buffer = new FastStringBuffer(); for (Tuple3<String, Integer, Boolean> indentation : validsAre) { if (monitor.isCanceled()) { return; } if (!indentation .o3) { // if it does not have more contents (its only whitespaces), let's keep on going! continue; } String indentStr = indentation.o1; if (indentStr.indexOf("\t") != -1) { continue; // the ones that appear in tabs and spaces should not be analyzed here (they'll // have their own error messages). } int lenFound = indentStr.length(); int extraChars = lenFound % tabWidth; if (extraChars != 0) { Integer offset = indentation.o2; int startLine = PySelection.getLineOfOffset(doc, offset) + 1; int startCol = 1; int endCol = startCol + lenFound; buffer.clear(); ret.add( new Message( IAnalysisPreferences.TYPE_INDENTATION_PROBLEM, buffer.append("Bad Indentation (").append(lenFound).append(" spaces)").toString(), startLine, startLine, startCol, endCol, analysisPrefs)); } } }
/** * Analyze the doc for mixed tabs and indents with the wrong number of chars. * * @param monitor * @return a list with the error messages to be shown to the user. */ public static List<IMessage> analyzeDoc( IDocument doc, IAnalysisPreferences analysisPrefs, String moduleName, IIndentPrefs indentPrefs, IProgressMonitor monitor) { ArrayList<IMessage> ret = new ArrayList<IMessage>(); // don't even try to gather indentation errors if they should be ignored. if (analysisPrefs.getSeverityForType(IAnalysisPreferences.TYPE_INDENTATION_PROBLEM) == IMarker.SEVERITY_INFO) { return ret; } List<Tuple3<String, Integer, Boolean>> foundTabs = new ArrayList<Tuple3<String, Integer, Boolean>>(); List<Tuple3<String, Integer, Boolean>> foundSpaces = new ArrayList<Tuple3<String, Integer, Boolean>>(); TabNannyDocIterator it; try { it = new TabNannyDocIterator(doc); } catch (BadLocationException e) { return ret; } while (it.hasNext()) { Tuple3<String, Integer, Boolean> indentation; try { indentation = it.next(); } catch (BadLocationException e) { return ret; } // it can actually be in both (if we have spaces and tabs in the same indent line). if (indentation.o1.indexOf('\t') != -1) { foundTabs.add(indentation); } if (indentation.o1.indexOf(' ') != -1) { foundSpaces.add(indentation); } if (monitor.isCanceled()) { return ret; } } int spacesFoundSize = foundSpaces.size(); int tabsFoundSize = foundTabs.size(); if (spacesFoundSize == 0 && tabsFoundSize == 0) { // nothing to do here... (no indents available) return ret; } // let's discover whether we should mark the tabs found as errors or the spaces found... boolean markTabsAsError; // if we found the same number of indents for tabs and spaces, let's use the user-prefs to // decide what to do if (spacesFoundSize == tabsFoundSize) { // ok, we have both, spaces and tabs... let's see what the user actually wants markTabsAsError = indentPrefs.getUseSpaces(false); } else if (tabsFoundSize > spacesFoundSize) { // let's see what appears more in the file (and mark the other as error). markTabsAsError = false; } else { markTabsAsError = true; } List<Tuple3<String, Integer, Boolean>> errorsAre; List<Tuple3<String, Integer, Boolean>> validsAre; String errorMsg; char errorChar; if (markTabsAsError) { validsAre = foundSpaces; errorsAre = foundTabs; errorMsg = "Mixed Indentation: Tab found"; errorChar = '\t'; createBadIndentForSpacesMessages(doc, analysisPrefs, indentPrefs, ret, validsAre, monitor); } else { validsAre = foundTabs; errorsAre = foundSpaces; errorMsg = "Mixed Indentation: Spaces found"; errorChar = ' '; } createMixedErrorMessages(doc, analysisPrefs, ret, errorsAre, errorMsg, errorChar, monitor); return ret; }
/** * Performs the action with a given PySelection * * @param ps Given PySelection * @return boolean The success or failure of the action */ public int perform(PySelection ps) { // What we'll be replacing the selected text with FastStringBuffer strbuf = new FastStringBuffer(); try { // discover 1st line that starts the block comment int i; int startLineIndex = getStartIndex(ps); int endLineIndex = getEndIndex(ps); if (startLineIndex == -1 || endLineIndex == -1) { if (startLineIndex == -1 && endLineIndex == -1) { return -1; } else if (startLineIndex == -1) { startLineIndex = endLineIndex; } else { endLineIndex = startLineIndex; } } // For each line, uncomment it for (i = startLineIndex; i <= endLineIndex; i++) { boolean addDelim = true; String spacesBefore = ""; String line = ps.getLine(i); int lineLen = line.length(); for (int j = 0; j < lineLen; j++) { char c = line.charAt(j); if (c == '#') { // ok, it starts with # (so, remove the whitespaces before it) if (j > 0) { spacesBefore = line.substring(0, j); } line = line.substring(j); break; } else { if (!Character.isWhitespace(c)) { break; } } } if (line.startsWith("#")) { line = line.substring(1); } // get the chars used in block-comments AbstractBlockCommentAction[] acts = new AbstractBlockCommentAction[] { new PyAddSingleBlockComment(), new PyAddBlockComment() }; HashSet<Character> chars = new HashSet<Character>(); for (int j = 0; j < acts.length; j++) { AbstractBlockCommentAction action = acts[j]; chars.add(action.getColsAndChar().o2); } if (line.length() > 0) { boolean removedChar = false; char lastChar = '\0'; for (int j = 0; j < line.length(); j++) { lastChar = line.charAt(j); if (!chars.contains(lastChar)) { break; } else { removedChar = true; line = line.substring(1); j--; } } if (line.length() == 0 && removedChar) { addDelim = false; } if (removedChar && lastChar == ' ') { line = line.substring(1); } } if (addDelim) { strbuf.append(spacesBefore); strbuf.append(line); String lineDelimiter = ps.getDoc().getLineDelimiter(i); if (lineDelimiter != null) { strbuf.append(lineDelimiter); } } } // Ok, at this point things should be correct, but make sure than on uncomment, // the code goes to a proper indent position (remove spaces we may have added when creating a // block). String string = strbuf.toString(); List<String> lines = StringUtils.splitInLines(string); Tuple<Integer, String> posAndLine = new Tuple<Integer, String>(-1, ""); for (String line : lines) { int firstCharPosition = PySelection.getFirstCharPosition(line); if (firstCharPosition < posAndLine.o1 || posAndLine.o1 < 0) { posAndLine.o1 = firstCharPosition; posAndLine.o2 = line; } } if (posAndLine.o1 > 0) { final String sub = posAndLine.o2.substring(0, posAndLine.o1); if (sub.endsWith( " ")) { // If it ends with a tab, we won't change anything (only spaces are removed -- // which we may have introduced) boolean allEqual = true; for (String line : lines) { if (!line.startsWith(sub)) { allEqual = false; break; } } if (allEqual) { if (sub.startsWith("\t")) { // Tabs based indent: remove any ending spaces (and at this point we know a string // ends with a space) int j; for (j = sub.length() - 1; j >= 0; j--) { char c = sub.charAt(j); if (c != ' ') { j++; break; } } String newSub = sub.substring(0, j); strbuf.clear(); for (String line : lines) { strbuf.append(newSub); strbuf.append(line.substring(sub.length())); } } else { IIndentPrefs indentPrefs; if (targetEditor instanceof PyEdit) { PyEdit pyEdit = (PyEdit) targetEditor; indentPrefs = pyEdit.getIndentPrefs(); } else { indentPrefs = DefaultIndentPrefs.get(); } String indentationString = indentPrefs.getIndentationString(); int subLen = sub.length(); int indentLen = indentationString.length(); int mod = subLen % indentLen; if (mod != 0) { String substring = sub.substring(subLen - mod, subLen); boolean onlyWhitespaces = true; for (int k = 0; k < substring.length(); k++) { if (substring.charAt(k) != ' ') { onlyWhitespaces = false; break; } } if (onlyWhitespaces) { String newSub = sub.substring(0, subLen - mod); strbuf.clear(); for (String line : lines) { strbuf.append(newSub); strbuf.append(line.substring(sub.length())); } } } } } } } // Replace the text with the modified information int startLineOffset = ps.getLineOffset(startLineIndex); int endLineOffset = ps.getEndLineOffset(endLineIndex); String endLineDelimiter = ps.getDoc().getLineDelimiter(endLineIndex); if (endLineDelimiter != null) { endLineOffset += endLineDelimiter.length(); } String str = strbuf.toString(); ps.getDoc().replace(startLineOffset, endLineOffset - startLineOffset, str); return startLineOffset + str.length(); } catch (Exception e) { beep(e); } // In event of problems, return false return -1; }