diff --git a/util/formattinghelpers.cpp b/util/formattinghelpers.cpp index fd3d14ff27..dd2f990f76 100644 --- a/util/formattinghelpers.cpp +++ b/util/formattinghelpers.cpp @@ -1,230 +1,192 @@ /* This file is part of KDevelop * Copyright 2011 David Nolden This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "formattinghelpers.h" #include #include #include namespace KDevelop { ///Matches the given prefix to the given text, ignoring all whitespace ///Returns -1 if mismatched, else the position in @p text where the @p prefix match ends int matchPrefixIgnoringWhitespace(QString text, QString prefix, QString fuzzyCharacters) { int prefixPos = 0; int textPos = 0; while (prefixPos < prefix.length() && textPos < text.length()) { skipWhiteSpace: while (prefixPos < prefix.length() && prefix[prefixPos].isSpace()) ++prefixPos; while (textPos < text.length() && text[textPos].isSpace()) ++textPos; if(prefixPos == prefix.length() || textPos == text.length()) break; if(prefix[prefixPos] != text[textPos]) { bool skippedFuzzy = false; while( prefixPos < prefix.length() && fuzzyCharacters.indexOf(prefix[prefixPos]) != -1 ) { ++prefixPos; skippedFuzzy = true; } while( textPos < text.length() && fuzzyCharacters.indexOf(text[textPos]) != -1 ) { ++textPos; skippedFuzzy = true; } if( skippedFuzzy ) goto skipWhiteSpace; return -1; } ++prefixPos; ++textPos; } return textPos; } static QString reverse( const QString& str ) { QString ret; for(int a = str.length()-1; a >= 0; --a) ret.append(str[a]); return ret; } // Returns the text start position with all whitespace that is redundant in the given context skipped -int skipRedundantWhiteSpace( QString context, QString text ) +int skipRedundantWhiteSpace( QString context, QString text, int tabWidth ) { if( context.isEmpty() || !context[context.size()-1].isSpace() || text.isEmpty() || !text[0].isSpace() ) return 0; int textPosition = 0; // Extract trailing whitespace in the context int contextPosition = context.size()-1; while( contextPosition > 0 && context[contextPosition-1].isSpace() ) --contextPosition; int textWhitespaceEnd = 0; while(textWhitespaceEnd < text.size() && text[textWhitespaceEnd].isSpace()) ++textWhitespaceEnd; QString contextWhiteSpace = context.mid(contextPosition); contextPosition = 0; QString textWhiteSpace = text.left(textWhitespaceEnd); // Step 1: Remove redundant newlines while(contextWhiteSpace.contains('\n') && textWhiteSpace.contains('\n')) { int contextOffset = contextWhiteSpace.indexOf('\n')+1; int textOffset = textWhiteSpace.indexOf('\n')+1; contextPosition += contextOffset; contextWhiteSpace.remove(0, contextOffset); textPosition += textOffset; textWhiteSpace.remove(0, textOffset); } int contextOffset = 0; int textOffset = 0; // Skip redundant ordinary whitespace while( contextOffset < contextWhiteSpace.size() && textOffset < textWhiteSpace.size() && contextWhiteSpace[contextOffset].isSpace() && contextWhiteSpace[contextOffset] != '\n' && textWhiteSpace[textOffset].isSpace() && textWhiteSpace[textOffset] != '\n' ) { + bool contextWasTab = contextWhiteSpace[contextOffset] == ' '; + bool textWasTab = textWhiteSpace[contextOffset] == ' '; ++contextOffset; ++textOffset; + if( contextWasTab != textWasTab ) + { + // Problem: We have a mismatch of tabs and/or ordinary whitespaces + if( contextWasTab ) + { + for( int s = 1; s < tabWidth; ++s ) + if( textOffset < textWhiteSpace.size() && textWhiteSpace[textOffset] == ' ' ) + ++textOffset; + }else if( textWasTab ) + { + for( int s = 1; s < tabWidth; ++s ) + if( contextOffset < contextWhiteSpace.size() && contextWhiteSpace[contextOffset] == ' ' ) + ++contextOffset; + } + } } return textPosition+textOffset; } -std::pair skipRedundantWhiteSpaceB( QString context, QString text ) -{ - if( context.isEmpty() || !context[context.size()-1].isSpace() || text.isEmpty() || !text[0].isSpace() ) - return std::make_pair(0, 0); - - int textPosition = 0; - - // Extract trailing whitespace in the context - int contextPosition = context.size()-1; - while( contextPosition > 0 && context[contextPosition-1].isSpace() ) - --contextPosition; - - - int textWhitespaceEnd = 0; - while(textWhitespaceEnd < text.size() && text[textWhitespaceEnd].isSpace()) - ++textWhitespaceEnd; - - QString contextWhiteSpace = context.mid(contextPosition); - contextPosition = 0; - QString textWhiteSpace = text.left(textWhitespaceEnd); - - // Step 1: Remove redundant newlines - while(contextWhiteSpace.contains('\n') && textWhiteSpace.contains('\n')) - { - int contextOffset = contextWhiteSpace.indexOf('\n')+1; - int textOffset = textWhiteSpace.indexOf('\n')+1; - - contextPosition += contextOffset; - contextWhiteSpace.remove(0, contextOffset); - - textPosition += textOffset; - textWhiteSpace.remove(0, textOffset); - } - - while(textWhiteSpace.contains('\n')) - { - // There are remaining newlines which are not redundant, no more matching required. - return std::make_pair(textPosition, contextPosition); - } - - while(contextWhiteSpace.contains('\n')) - { - // There are too many newlines in the context. To make everything correct, we would have - // to remove those newlines, however we're not editing that area, thus there is nothing we can do. - return std::make_pair(textPosition, contextPosition); - } - - // Step 2: Remove redundant whitespace - - if( textWhiteSpace.size() > contextWhiteSpace.size() ) - return std::make_pair(textPosition + contextWhiteSpace.size(), contextPosition + contextWhiteSpace.size()); // Skip the context white space - else - return std::make_pair(textPosition + textWhiteSpace.size(), contextPosition + textWhiteSpace.size()); -} - -QString extractFormattedTextFromContext( const QString& _formattedMergedText, const QString& /*originalMergedText*/, const QString& text, const QString& leftContext, const QString& rightContext, const QString& fuzzyCharacters) +QString extractFormattedTextFromContext( const QString& _formattedMergedText, const QString& text, const QString& leftContext, const QString& rightContext, int tabWidth, const QString& fuzzyCharacters) { QString formattedMergedText = _formattedMergedText; //Now remove "leftContext" and "rightContext" from the sides if(!leftContext.isEmpty()) { int endOfLeftContext = matchPrefixIgnoringWhitespace( formattedMergedText, leftContext, QString() ); if(endOfLeftContext == -1) { // Try 2: Ignore the fuzzy characters while matching endOfLeftContext = matchPrefixIgnoringWhitespace( formattedMergedText, leftContext, fuzzyCharacters ); if(endOfLeftContext == -1) { kWarning() << "problem matching the left context"; return text; } } int startOfWhiteSpace = endOfLeftContext; // Include all leading whitespace while(startOfWhiteSpace > 0 && formattedMergedText[startOfWhiteSpace-1].isSpace()) --startOfWhiteSpace; formattedMergedText = formattedMergedText.mid(startOfWhiteSpace); - int skip = skipRedundantWhiteSpace( leftContext, formattedMergedText ); + int skip = skipRedundantWhiteSpace( leftContext, formattedMergedText, tabWidth ); formattedMergedText = formattedMergedText.mid(skip); } if(!rightContext.isEmpty()) { //Add a whitespace behind the text for matching, so that we definitely capture all trailing whitespace int endOfText = matchPrefixIgnoringWhitespace( formattedMergedText, text+" ", QString() ); if(endOfText == -1) { // Try 2: Ignore the fuzzy characters while matching endOfText = matchPrefixIgnoringWhitespace( formattedMergedText, text+" ", fuzzyCharacters ); if(endOfText == -1) { kWarning() << "problem matching the text while formatting"; return text; } } formattedMergedText = formattedMergedText.left(endOfText); - int skip = skipRedundantWhiteSpace( reverse(rightContext), reverse(formattedMergedText) ); + int skip = skipRedundantWhiteSpace( reverse(rightContext), reverse(formattedMergedText), tabWidth ); formattedMergedText = formattedMergedText.left(formattedMergedText.size() - skip); } return formattedMergedText; } } diff --git a/util/formattinghelpers.h b/util/formattinghelpers.h index 05f548a9ce..d82d421573 100644 --- a/util/formattinghelpers.h +++ b/util/formattinghelpers.h @@ -1,46 +1,47 @@ /* This file is part of KDevelop * Copyright 2011 David Nolden This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FORMATTINGHELPERS_H #define FORMATTINGHELPERS_H #include "utilexport.h" #include class QString; namespace KDevelop { /** * Helps extracting a re-formatted version of a text fragment, within a specific left and right context. * The re-formatting must be an operation which only changes whitespace, and keeps whitespace boundaries * between identifiers intact. If this is not the case, the original text is returned. * * @param formattedMergedText The re-formatted merged text: format(leftContext + text + rightContext) * @param originalMergedText The original merged text: (leftContext + text + rightContext) * @param text The text fragment of which the re-formatted version will be returned * @param leftContext The left context of the text fragment * @param rightContext The right context of the text fragment + * @param tabWidth The width of one tab, required while matching tabs vs. spaces * @param fuzzyCharacters Characters which are ignored in case of mismatches * * @return The re-formatted version of @p text * */ -KDEVPLATFORMUTIL_EXPORT QString extractFormattedTextFromContext(const QString& formattedMergedText, const QString& originalMergedText, const QString& text, const QString& leftContext, const QString& rightContext, const QString& fuzzyCharacters = "{}()/*/"); +KDEVPLATFORMUTIL_EXPORT QString extractFormattedTextFromContext(const QString& formattedMergedText, const QString& text, const QString& leftContext, const QString& rightContext, int tabWidth = 4, const QString& fuzzyCharacters = "{}()/*/"); } #endif // FORMATTINGHELPERS_H