| 1023 | } // unnamed namespace |
| 1024 | |
| 1025 | QString KDevelop::extractFormattedTextFromContext(const QString& _formattedMergedText, const QString& text, |
| 1026 | QStringView leftContext, QStringView rightContext, int tabWidth) |
| 1027 | { |
| 1028 | if (leftContext.isEmpty() && rightContext.isEmpty()) { |
| 1029 | return _formattedMergedText; // fast path, nothing to do |
| 1030 | } |
| 1031 | |
| 1032 | QStringView formattedMergedText = _formattedMergedText; |
| 1033 | //Now remove "leftContext" and "rightContext" from the sides |
| 1034 | |
| 1035 | // For double quotes, C-style comments and brackets of each type, ensure that inserted or removed opening characters |
| 1036 | // (or sequences) are matched by inserted or removed closing characters (or sequences) in the formatted version of |
| 1037 | // text. The matching closing entities should be encountered after the opening ones. |
| 1038 | // Tracking/matching only inserted and removed (ignoring untouched) fuzzy characters is important to prevent |
| 1039 | // issues with commented out and (conditionally) disabled by preprocessor code. The assumption is that formatters |
| 1040 | // do not break code and do not insert or remove unmatched quotes, comments or brackets into disabled code. |
| 1041 | |
| 1042 | // For simplicity, only track double quotes (and not comments or brackets) while prefix-matching the two |
| 1043 | // contexts. Brackets opened in the left context can be closed in the right context. Such closing could be |
| 1044 | // validated, but is not, in order to avoid further complicating the implementation. Validating quotes, |
| 1045 | // comments and all bracket types while matching text against its formatted counterpart should be sufficient. |
| 1046 | |
| 1047 | // Fuzzy characters inserted at context-text boundaries are always included into the formatted text fragment. |
| 1048 | // If this greedy inclusion produces a quote, comment or bracket mismatch, unformatted text is returned. |
| 1049 | // We could try to achieve a match by giving some of such inserted fuzzy characters to contexts. But that would |
| 1050 | // substantially complicate the implementation and likely worsen existing formatting. For example, a brace inserted |
| 1051 | // at a context-text boundary affects the surrounding whitespace. If we give the brace to the adjacent context, |
| 1052 | // it won't be part of the final version of formatted text, and therefore our attempts to compute whitespace to be |
| 1053 | // included into formatted text at the affected boundary would probably produce an unsatisfactory result. |
| 1054 | |
| 1055 | const auto handleDoubleQuoteMatchFailure = [] { |
| 1056 | // The formatter inserted or removed a double quote in exactly one of two contexts, which means that it |
| 1057 | // inserted/removed the matching double quote into the formatted text. Therefore, reformatting only |
| 1058 | // the text fragment would produce an unpaired quote and break code. Give up formatting to prevent that. |
| 1059 | DoubleQuoteValidator::printFailureWarning(); |
| 1060 | }; |
| 1061 | |
| 1062 | bool hasUnmatchedDoubleQuote = false; |
| 1063 | |
| 1064 | if (!leftContext.isEmpty()) { |
| 1065 | DoubleQuoteFuzzyMatcher fuzzyMatcher; |
| 1066 | // Inform the matcher that the beginning of the left context is not a context-text boundary. |
| 1067 | fuzzyMatcher.firstNonWhitespaceCharacterMatch(); |
| 1068 | PrefixMatcher prefixMatcher(leftContext.cbegin(), leftContext.cend(), formattedMergedText.cbegin(), |
| 1069 | formattedMergedText.cend(), fuzzyMatcher); |
| 1070 | const auto result = prefixMatcher.match(); |
| 1071 | if (!result.hasMatched) { |
| 1072 | return text; |
| 1073 | } |
| 1074 | |
| 1075 | hasUnmatchedDoubleQuote = fuzzyMatcher.hasUnmatchedDoubleQuote(); |
| 1076 | if (hasUnmatchedDoubleQuote && rightContext.isEmpty()) { |
| 1077 | handleDoubleQuoteMatchFailure(); |
| 1078 | return text; |
| 1079 | } |
| 1080 | |
| 1081 | // include all possible whitespace at the context-text boundary |
| 1082 | auto rMatchEnd = std::make_reverse_iterator(result.matchEnd); |
nothing calls this directly
no test coverage detected