diff options
author | Jordan Demeulenaere <jdemeulenaere@google.com> | 2023-02-23 16:44:36 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-02-23 16:44:36 +0000 |
commit | a9fa90b3c3eeb707216323ca430d227f15b93d4f (patch) | |
tree | eb485078f187c7dde2061192b197bbdded5f971a | |
parent | f2ef27b6a0dd6c027e814ab4af4ee21f88746fed (diff) | |
parent | c431a78e6c3dff3faea337fb2fa03900cdf148c2 (diff) | |
download | ktfmt-a9fa90b3c3eeb707216323ca430d227f15b93d4f.tar.gz |
Merge tag 'v0.43' into aosp/master am: 732c9702b2 am: c431a78e6c
Original change: https://android-review.googlesource.com/c/platform/external/ktfmt/+/2453026
Change-Id: I2249c74960adb718a8520bd4b0dd52b06163f8f1
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
21 files changed, 764 insertions, 164 deletions
diff --git a/core/pom.xml b/core/pom.xml index 765c8f1..c5e5119 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -11,7 +11,7 @@ <parent> <groupId>com.facebook</groupId> <artifactId>ktfmt-parent</artifactId> - <version>0.42</version> + <version>0.43</version> </parent> <properties> @@ -103,7 +103,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> - <version>2.6</version> + <version>3.3.0</version> <configuration> <archive> <manifest> @@ -116,7 +116,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> - <version>2.6</version> + <version>3.3.0</version> <executions> <execution> <id>make-assembly</id> diff --git a/core/src/main/java/com/facebook/ktfmt/cli/Main.kt b/core/src/main/java/com/facebook/ktfmt/cli/Main.kt index 0a08944..980897d 100644 --- a/core/src/main/java/com/facebook/ktfmt/cli/Main.kt +++ b/core/src/main/java/com/facebook/ktfmt/cli/Main.kt @@ -70,7 +70,7 @@ class Main( fun run(): Int { if (parsedArgs.fileNames.isEmpty()) { err.println( - "Usage: ktfmt [--dropbox-style | --google-style | --kotlinlang-style] [--dry-run] [--set-exit-if-changed] [--stdin-name=<name>] File1.kt File2.kt ...") + "Usage: ktfmt [--dropbox-style | --google-style | --kotlinlang-style] [--dry-run] [--set-exit-if-changed] [--stdin-name=<name>] [--do-not-remove-unused-imports] File1.kt File2.kt ...") err.println("Or: ktfmt @file") return 1 } diff --git a/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt b/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt index 4c66efd..2c84972 100644 --- a/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt +++ b/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt @@ -52,6 +52,7 @@ data class ParsedArgs( var formattingOptions = FormattingOptions() var dryRun = false var setExitIfChanged = false + var removeUnusedImports = true var stdinName: String? = null for (arg in args) { @@ -61,6 +62,7 @@ data class ParsedArgs( arg == "--kotlinlang-style" -> formattingOptions = Formatter.KOTLINLANG_FORMAT arg == "--dry-run" || arg == "-n" -> dryRun = true arg == "--set-exit-if-changed" -> setExitIfChanged = true + arg == "--do-not-remove-unused-imports" -> removeUnusedImports = false arg.startsWith("--stdin-name") -> stdinName = parseKeyValueArg(err, "--stdin-name", arg) arg.startsWith("--") -> err.println("Unexpected option: $arg") arg.startsWith("@") -> err.println("Unexpected option: $arg") @@ -68,7 +70,13 @@ data class ParsedArgs( } } - return ParsedArgs(fileNames, formattingOptions, dryRun, setExitIfChanged, stdinName) + return ParsedArgs( + fileNames, + formattingOptions.copy(removeUnusedImports = removeUnusedImports), + dryRun, + setExitIfChanged, + stdinName, + ) } private fun parseKeyValueArg(err: PrintStream, key: String, arg: String): String? { diff --git a/core/src/main/java/com/facebook/ktfmt/format/FenceCommentsOp.kt b/core/src/main/java/com/facebook/ktfmt/format/FenceCommentsOp.kt new file mode 100644 index 0000000..721bd05 --- /dev/null +++ b/core/src/main/java/com/facebook/ktfmt/format/FenceCommentsOp.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.facebook.ktfmt.format + +import com.google.common.collect.ImmutableList +import com.google.googlejavaformat.DocBuilder +import com.google.googlejavaformat.Op + +/** + * A dummy [Op] that prevents comments from being moved ahead of it, into parent [Level]s. + * + * If a comment is the first thing in a [Level], [OpBuilder] moves it outside of that level during + * [Doc] building. It does so recursively, until the comment is not the first element of the level. + * This behaviour can be very confusing, where comments seem to absorb or ignore expected + * indentation. + */ +object FenceCommentsOp : Op { + val AS_LIST = ImmutableList.of<Op>(FenceCommentsOp) + + override fun add(builder: DocBuilder) { + // Do nothing. This Op simply needs to be in the OpsBuilder. + } +} diff --git a/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt b/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt index 898b70e..433ae1a 100644 --- a/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt +++ b/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt @@ -121,6 +121,7 @@ import org.jetbrains.kotlin.psi.KtWhenConditionWithExpression import org.jetbrains.kotlin.psi.KtWhenExpression import org.jetbrains.kotlin.psi.KtWhileExpression import org.jetbrains.kotlin.psi.psiUtil.children +import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespace import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startsWithComment @@ -277,7 +278,7 @@ class KotlinInputAstVisitor( /** * @param keyword e.g., "fun" or "class". * @param typeOrDelegationCall for functions, the return typeOrDelegationCall; for classes, the - * list of supertypes. + * list of supertypes. */ private fun visitFunctionLikeExpression( modifierList: KtModifierList?, @@ -627,6 +628,7 @@ class KotlinInputAstVisitor( * lastIndexToOpen to track the spot after the last time we stopped * grouping. * ``` + * * The final expression with groupings: * ``` * {{a.b}[2]}.{c.d}() @@ -794,7 +796,7 @@ class KotlinInputAstVisitor( * Example (`1, "hi"`) in a function call * * @return a [BreakTag] which can tell you if a break was taken, but only when the list doesn't - * terminate in a negative closing indent. See [visitEachCommaSeparated] for examples. + * terminate in a negative closing indent. See [visitEachCommaSeparated] for examples. */ private fun visitValueArgumentListInternal(list: KtValueArgumentList): BreakTag? { builder.sync(list) @@ -844,9 +846,9 @@ class KotlinInputAstVisitor( * The internal version of [visitLambdaExpression]. * * @param brokeBeforeBrace used for tracking if a break was taken right before the lambda - * expression. Useful for scoping functions where we want good looking indentation. For example, - * here we have correct indentation before `bar()` and `car()` because we can detect the break - * after the equals: + * expression. Useful for scoping functions where we want good looking indentation. For example, + * here we have correct indentation before `bar()` and `car()` because we can detect the break + * after the equals: * ``` * fun foo() = * coroutineScope { x -> @@ -922,12 +924,9 @@ class KotlinInputAstVisitor( if (hasParams || hasArrow || hasStatements) { // If we had to break in the body, ensure there is a break before the closing brace builder.breakOp(Doc.FillMode.UNIFIED, " ", bracePlusZeroIndent) - builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO) } builder.block(bracePlusZeroIndent) { - // If there are closing comments, make sure they and the brace are indented together - // The comments will indent themselves, so consume the previous break as a blank line - builder.breakOp(Doc.FillMode.INDEPENDENT, "", ZERO) + builder.fenceComments() builder.token("}", blockIndent) } } @@ -993,7 +992,7 @@ class KotlinInputAstVisitor( * ``` * * @param hasTrailingComma if true, each element is placed on its own line (even if they could've - * fit in a single line), and a trailing comma is emitted. + * fit in a single line), and a trailing comma is emitted. * * Example: * ``` @@ -1002,17 +1001,17 @@ class KotlinInputAstVisitor( * ``` * * @param wrapInBlock if true, place all the elements in a block. When there's no [leadingBreak], - * this will be negatively indented. Note that the [prefix] and [postfix] aren't included in the - * block. + * this will be negatively indented. Note that the [prefix] and [postfix] aren't included in the + * block. * @param leadingBreak if true, break before the first element. * @param prefix if provided, emit this before the first element. * @param postfix if provided, emit this after the last element (or trailing comma). * @param breakAfterPrefix if true, emit a break after [prefix], but before the start of the - * block. + * block. * @param breakBeforePostfix if true, place a break after the last element. Redundant when - * [hasTrailingComma] is true. + * [hasTrailingComma] is true. * @return a [BreakTag] which can tell you if a break was taken, but only when the list doesn't - * terminate in a negative closing indent. + * terminate in a negative closing indent. * * Example 1, this returns a BreakTag which tells you a break wasn't taken: * ``` @@ -1094,11 +1093,8 @@ class KotlinInputAstVisitor( if (postfix != null) { if (breakAfterLastElement) { - // Indent trailing comments to the same depth as list items. We really have to fight - // googlejavaformat here for some reason. - builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO) builder.block(expressionBreakNegativeIndent) { - builder.breakOp(breakType, "", ZERO) + builder.fenceComments() builder.token(postfix, expressionBreakIndent) } } else { @@ -1321,54 +1317,55 @@ class KotlinInputAstVisitor( builder.token(".") } builder.token(name) - builder.op("") } } - if (name != null) { - builder.open(expressionBreakIndent) // open block for named values - } - // For example `: String` in `val thisIsALongName: String` or `fun f(): String` - if (type != null) { - if (name != null) { - builder.token(":") - builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO) + builder.block(expressionBreakIndent, isEnabled = name != null) { + // For example `: String` in `val thisIsALongName: String` or `fun f(): String` + if (type != null) { + if (name != null) { + builder.token(":") + builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO) + } + visit(type) } - visit(type) } - } - - // For example `where T : Int` in a generic method - if (typeConstraintList != null) { - builder.space() - visit(typeConstraintList) - builder.space() - } - // for example `by lazy { compute() }` - if (delegate != null) { - builder.space() - builder.token("by") - if (isLambdaOrScopingFunction(delegate.expression)) { + // For example `where T : Int` in a generic method + if (typeConstraintList != null) { + builder.space() + visit(typeConstraintList) builder.space() - visit(delegate) - } else { - builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent) - builder.block(expressionBreakIndent) { visit(delegate) } } - } else if (initializer != null) { - builder.space() - builder.token("=") - if (isLambdaOrScopingFunction(initializer)) { - visitLambdaOrScopingFunction(initializer) - } else { - builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent) - builder.block(expressionBreakIndent) { visit(initializer) } + + // for example `by lazy { compute() }` + if (delegate != null) { + builder.space() + builder.token("by") + if (isLambdaOrScopingFunction(delegate.expression)) { + builder.space() + visit(delegate) + } else { + builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent) + builder.block(expressionBreakIndent) { + builder.fenceComments() + visit(delegate) + } + } + } else if (initializer != null) { + builder.space() + builder.token("=") + if (isLambdaOrScopingFunction(initializer)) { + visitLambdaOrScopingFunction(initializer) + } else { + builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent) + builder.block(expressionBreakIndent) { + builder.fenceComments() + visit(initializer) + } + } } } - if (name != null) { - builder.close() // close block for named values - } // for example `private set` or `get = 2 * field` if (accessors?.isNotEmpty() == true) { builder.block(blockIndent) { @@ -1419,6 +1416,10 @@ class KotlinInputAstVisitor( * 2. '... = Runnable @Annotation { ... }' due to the annotation */ private fun isLambdaOrScopingFunction(expression: KtExpression?): Boolean { + if (expression == null) return false + if (expression.getPrevSiblingIgnoringWhitespace() is PsiComment) { + return false // Leading comments cause weird indentation. + } if (expression is KtLambdaExpression) { return true } @@ -2479,11 +2480,7 @@ class KotlinInputAstVisitor( * @param plusIndent the block level to pass to the block * @param block a code block to be run in this block level */ - private inline fun OpsBuilder.block( - plusIndent: Indent, - isEnabled: Boolean = true, - block: () -> Unit - ) { + private fun OpsBuilder.block(plusIndent: Indent, isEnabled: Boolean = true, block: () -> Unit) { if (isEnabled) { open(plusIndent) } @@ -2498,6 +2495,11 @@ class KotlinInputAstVisitor( sync(psiElement.startOffset) } + /** Prevent susequent comments from being moved ahead of this point, into parent [Level]s. */ + private fun OpsBuilder.fenceComments() { + addAll(FenceCommentsOp.AS_LIST) + } + /** * Throws a formatting error * diff --git a/core/src/main/java/com/facebook/ktfmt/format/TypeNameClassifier.kt b/core/src/main/java/com/facebook/ktfmt/format/TypeNameClassifier.kt index eba3ccc..76fac3e 100644 --- a/core/src/main/java/com/facebook/ktfmt/format/TypeNameClassifier.kt +++ b/core/src/main/java/com/facebook/ktfmt/format/TypeNameClassifier.kt @@ -90,7 +90,6 @@ object TypeNameClassifier { * a type or static field access, or -1 if no such prefix was found. * * Examples: - * * * ClassName * * ClassName.staticMemberName * * com.google.ClassName.InnerClass.staticMemberName diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/Escaping.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/Escaping.kt index 85c9938..d3d3c10 100644 --- a/core/src/main/java/com/facebook/ktfmt/kdoc/Escaping.kt +++ b/core/src/main/java/com/facebook/ktfmt/kdoc/Escaping.kt @@ -44,10 +44,7 @@ object Escaping { s.substring(endMarkerIndex) } - /** - * - * See [escapeKDoc]. - */ + /** See [escapeKDoc]. */ fun unescapeKDoc(s: String): String = s.replace(SLASH_STAR_ESCAPE, "/*").replace(STAR_SLASH_ESCAPE, "*/") } diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/KDocFormattingOptions.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocFormattingOptions.kt index bfe80ea..0d50d47 100644 --- a/core/src/main/java/com/facebook/ktfmt/kdoc/KDocFormattingOptions.kt +++ b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocFormattingOptions.kt @@ -60,7 +60,8 @@ class KDocFormattingOptions( /** * How many spaces to use for hanging indents in numbered lists and after block tags. Using 4 or - * more here will result in subsequent lines being interpreted as block formatted. + * more here will result in subsequent lines being interpreted as block formatted by IntelliJ (but + * not Dokka). */ var hangingIndent: Int = 2 diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/Paragraph.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/Paragraph.kt index 93e98e5..6d905e5 100644 --- a/core/src/main/java/com/facebook/ktfmt/kdoc/Paragraph.kt +++ b/core/src/main/java/com/facebook/ktfmt/kdoc/Paragraph.kt @@ -101,10 +101,6 @@ class Paragraph(private val task: FormattingTask) { return content.isEmpty() } - private fun hasClosingPre(): Boolean { - return content.contains("</pre>", ignoreCase = false) || next?.hasClosingPre() ?: false - } - fun cleanup() { val original = text @@ -164,11 +160,23 @@ class Paragraph(private val task: FormattingTask) { } private fun convertMarkup(s: String): String { - if (s.none { it == '<' || it == '&' || it == '{' }) return s + // Whether the tag starts with a capital letter and needs to be cleaned, e.g. `@See` -> `@see`. + // (isKDocTag only allows the first letter to be capitalized.) + val convertKDocTag = s.isKDocTag() && s[1].isUpperCase() + + if (!convertKDocTag && s.none { it == '<' || it == '&' || it == '{' }) { + return s + } val sb = StringBuilder(s.length) var i = 0 val n = s.length + + if (convertKDocTag) { + sb.append('@').append(s[1].lowercaseChar()) + i += 2 + } + var code = false var brackets = 0 while (i < n) { @@ -335,8 +343,12 @@ class Paragraph(private val task: FormattingTask) { return reflow(words, lineWidth, hangingIndentSize) } - fun reflow(words: List<String>, lineWidth: Int, hangingIndentSize: Int): List<String> { - if (options.alternate || !options.optimal || hanging && hangingIndentSize > 0) { + private fun reflow(words: List<String>, lineWidth: Int, hangingIndentSize: Int): List<String> { + if (options.alternate || + !options.optimal || + hanging && hangingIndentSize > 0 || + // An unbreakable long word may make other lines shorter and won't look good + words.any { it.length > lineWidth }) { // Switch to greedy if explicitly turned on, and for hanging indent // paragraphs, since the current implementation doesn't have support // for a different maximum length on the first line from the rest @@ -405,7 +417,8 @@ class Paragraph(private val task: FormattingTask) { word.startsWith("```") || word.isDirectiveMarker() || word.startsWith("@") || // interpreted as a tag - word.isTodo()) { + word.isTodo() || + word.startsWith(">")) { return false } @@ -419,7 +432,14 @@ class Paragraph(private val task: FormattingTask) { return true } - private fun computeWords(): List<String> { + /** + * Split [text] up into individual "words"; in the case where some words are not allowed to span + * lines, it will combine these into single word. For example, if we have a sentence which ends + * with a number, e.g. "the sum is 5.", we want to make sure "5." is never placed at the beginning + * of a new line (which would turn it into a list item), so for this we'll compute the word list + * "the", "sum", "is 5.". + */ + fun computeWords(): List<String> { val words = text.split(Regex("\\s+")).filter { it.isNotBlank() }.map { it.trim() } if (words.size == 1) { return words @@ -438,39 +458,53 @@ class Paragraph(private val task: FormattingTask) { val combined = ArrayList<String>(words.size) - // If this paragraph is a list item or a quoted line, merge the first word - // with this item such that we never split them apart. - var start = 0 - var first = words[start++] - if (quoted || hanging && !text.isKDocTag()) { - first = first + " " + words[start++] - } + var from = 0 + val end = words.size + while (from < end) { + val start = + if (from == 0 && (quoted || hanging && !text.isKDocTag())) { + from + 2 + } else { + from + 1 + } + var to = words.size + for (i in start until words.size) { + val next = words[i] + if (next.startsWith("[") && !next.startsWith("[[")) { + // find end + var j = -1 + for (k in i until words.size) { + if (']' in words[k]) { + j = k + break + } + } + if (j != -1) { + // combine everything in the string; we can't break link text + if (start == from + 1 && canBreakAt(words[start])) { + combined.add(words[from]) + from = start + } + // Maybe not break; what if the next word isn't okay? + to = j + 1 + if (to == words.size || canBreakAt(words[to])) { + break + } + } // else: unterminated [, ignore + } else if (canBreakAt(next)) { + to = i + break + } + } - combined.add(first) - var prev = first - var insideSquareBrackets = words[start - 1].startsWith("[") - for (i in start until words.size) { - val word = words[i] - - // We also cannot break up a URL text across lines, which will alter the - // rendering of the docs. - if (prev.startsWith("[")) insideSquareBrackets = true - if (prev.contains("]")) insideSquareBrackets = false - - // Can we start a new line with this without interpreting it in a special - // way? - if (!canBreakAt(word) || insideSquareBrackets) { - // Combine with previous word with a single space; the line breaking - // algorithm won't know that it's more than one word. - val joined = "$prev $word" - combined.removeLast() - combined.add(joined) - prev = joined - } else { - combined.add(word) - prev = word + if (to == from + 1) { + combined.add(words[from]) + } else if (to > from) { + combined.add(words.subList(from, to).joinToString(" ")) } + from = to } + return combined } @@ -503,7 +537,7 @@ class Paragraph(private val task: FormattingTask) { } fun search(pi0: Int, pj0: Int, pi1: Int, pj1: Int) { - val stack = java.util.ArrayDeque<Quadruple>() + val stack = ArrayDeque<Quadruple>() stack.add(Quadruple(pi0, pj0, pi1, pj1)) while (stack.isNotEmpty()) { diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/ParagraphListBuilder.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/ParagraphListBuilder.kt index cb0891e..928a786 100644 --- a/core/src/main/java/com/facebook/ktfmt/kdoc/ParagraphListBuilder.kt +++ b/core/src/main/java/com/facebook/ktfmt/kdoc/ParagraphListBuilder.kt @@ -48,6 +48,7 @@ class ParagraphListBuilder( private fun closeParagraph(): Paragraph { val text = paragraph.text when { + paragraph.preformatted -> {} text.isKDocTag() -> { paragraph.doc = true paragraph.hanging = true @@ -142,14 +143,15 @@ class ParagraphListBuilder( newParagraph() var j = i var foundClose = false - var customize = true + var allowCustomize = true while (j < lines.size) { val l = lines[j] val lineWithIndentation = lineContent(l) if (lineWithIndentation.contains("```") && lineWithIndentation.trimStart().startsWith("```")) { - // Don't convert <pre> tags if we already have nested ``` content; that will lead to trouble - customize = false + // Don't convert <pre> tags if we already have nested ``` content; that will lead to + // trouble + allowCustomize = false } val done = (includeStart || j > i) && until(lineWithIndentation) if (!includeEnd && done) { @@ -175,7 +177,7 @@ class ParagraphListBuilder( if (!foundClose && expectClose) { // Just add a single line as preformatted and then treat the rest in the // normal way - customize = false + allowCustomize = false j = lines.size } @@ -185,7 +187,7 @@ class ParagraphListBuilder( appendText(lineWithIndentation) paragraph.preformatted = true paragraph.allowEmpty = true - if (customize) { + if (allowCustomize) { customize(index, paragraph) } newParagraph() @@ -254,8 +256,11 @@ class ParagraphListBuilder( newParagraph(i - 1).block = true appendText(lineWithoutIndentation) newParagraph(i).block = true - } else if (lineWithoutIndentation.startsWith( - "#")) { // not isHeader() because <h> is handled separately + } else if (lineWithoutIndentation.startsWith("#") + // "## X" is a header, "##X" is not + && + lineWithoutIndentation.firstOrNull { it != '#' }?.equals(' ') == + true) { // not isHeader() because <h> is handled separately // ## Header newParagraph(i - 1).block = true appendText(lineWithoutIndentation) @@ -496,17 +501,28 @@ class ParagraphListBuilder( return ParagraphList(paragraphs) } - private fun addPlainText(i: Int, text: String, braceBalance: Int = 0): Int { - val s = - if (options.convertMarkup && - (text.startsWith("<p>", true) || text.startsWith("<p/>", true))) { - paragraph.separate = true - text.substring(text.indexOf('>') + 1).trim() - } else { - text - } - .let { if (options.collapseSpaces) it.collapseSpaces() else it } + private fun convertPrefix(text: String): String { + return if (options.convertMarkup && + (text.startsWith("<p>", true) || text.startsWith("<p/>", true))) { + paragraph.separate = true + text.substring(text.indexOf('>') + 1).trim() + } else { + text + } + } + private fun convertSuffix(trimmedPrefix: String): String { + return if (options.convertMarkup && + (trimmedPrefix.endsWith("<p/>", true) || (trimmedPrefix.endsWith("</p>", true)))) { + trimmedPrefix.substring(0, trimmedPrefix.length - 4).trimEnd().removeSuffix("*").trimEnd() + } else { + trimmedPrefix + } + } + + private fun addPlainText(i: Int, text: String, braceBalance: Int = 0): Int { + val trimmed = convertSuffix(convertPrefix(text)) + val s = trimmed.let { if (options.collapseSpaces) it.collapseSpaces() else it } appendText(s) appendText(" ") diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/Utilities.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/Utilities.kt index e034bbe..597a285 100644 --- a/core/src/main/java/com/facebook/ktfmt/kdoc/Utilities.kt +++ b/core/src/main/java/com/facebook/ktfmt/kdoc/Utilities.kt @@ -114,17 +114,21 @@ fun String.isLine(minCount: Int = 3): Boolean { fun String.isKDocTag(): Boolean { // Not using a hardcoded list here since tags can change over time - if (startsWith("@")) { + if (startsWith("@") && length > 1) { for (i in 1 until length) { val c = this[i] if (c.isWhitespace()) { return i > 2 } else if (!c.isLetter() || !c.isLowerCase()) { - if (c == '[' && startsWith("@param")) { + if (c == '[' && (startsWith("@param") || startsWith("@property"))) { // @param is allowed to use brackets -- see // https://kotlinlang.org/docs/kotlin-doc.html#param-name // Example: @param[foo] The description of foo return true + } else if (i == 1 && c.isLetter() && c.isUpperCase()) { + // Allow capitalized tgs, such as @See -- this is normally a typo; convertMarkup + // should also fix these. + return true } return false } diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/format.md b/core/src/main/java/com/facebook/ktfmt/kdoc/format.md new file mode 100644 index 0000000..0f33bb7 --- /dev/null +++ b/core/src/main/java/com/facebook/ktfmt/kdoc/format.md @@ -0,0 +1 @@ +kdoc-formatter --greedy --max-line-width=100 --max-comment-width=100 . diff --git a/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt b/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt index 37cbf57..9f0919b 100644 --- a/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt +++ b/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt @@ -108,6 +108,33 @@ class ParsedArgsTest { } @Test + fun `parseOptions defaults to removing imports`() { + val (parsed, _) = parseTestOptions("foo.kt") + assertThat(parsed.formattingOptions.removeUnusedImports).isTrue() + } + + @Test + fun `parseOptions recognizes --do-not-remove-unused-imports to removing imports`() { + val (parsed, _) = parseTestOptions("--do-not-remove-unused-imports", "foo.kt") + assertThat(parsed.formattingOptions.removeUnusedImports).isFalse() + } + + @Test + fun `parseOptions handles dropbox style and --do-not-remove-unused-imports`() { + val (parsed, _) = + parseTestOptions("--do-not-remove-unused-imports", "--dropbox-style", "foo.kt") + assertThat(parsed.formattingOptions.removeUnusedImports).isFalse() + assertThat(parsed.formattingOptions.style).isEqualTo(FormattingOptions.Style.DROPBOX) + } + + @Test + fun `parseOptions handles google style and --do-not-remove-unused-imports`() { + val (parsed, _) = parseTestOptions("--do-not-remove-unused-imports", "--google-style", "foo.kt") + assertThat(parsed.formattingOptions.removeUnusedImports).isFalse() + assertThat(parsed.formattingOptions.style).isEqualTo(FormattingOptions.Style.GOOGLE) + } + + @Test fun `parseOptions --stdin-name`() { val (parsed, _) = parseTestOptions("--stdin-name=my/foo.kt") assertThat(parsed.stdinName).isEqualTo("my/foo.kt") diff --git a/core/src/test/java/com/facebook/ktfmt/format/FormatterTest.kt b/core/src/test/java/com/facebook/ktfmt/format/FormatterTest.kt index 88b16fe..ae9d05e 100644 --- a/core/src/test/java/com/facebook/ktfmt/format/FormatterTest.kt +++ b/core/src/test/java/com/facebook/ktfmt/format/FormatterTest.kt @@ -446,6 +446,58 @@ class FormatterTest { .trimMargin()) @Test + fun `properties with line comment above initializer`() = + assertFormatted( + """ + |class Foo { + | var x: Int = + | // Comment + | 0 + | + | var y: Int = + | // Comment + | scope { + | 0 // + | } + | + | var z: Int = + | // Comment + | if (cond) { + | 0 + | } else { + | 1 + | } + |} + |""" + .trimMargin()) + + @Test + fun `properties with line comment above delegate`() = + assertFormatted( + """ + |class Foo { + | var x: Int by + | // Comment + | 0 + | + | var y: Int by + | // Comment + | scope { + | 0 // + | } + | + | var z: Int by + | // Comment + | if (cond) { + | 0 + | } else { + | 1 + | } + |} + |""" + .trimMargin()) + + @Test fun `properties with accessors`() = assertFormatted( """ diff --git a/core/src/test/java/com/facebook/ktfmt/kdoc/KDocFormatterTest.kt b/core/src/test/java/com/facebook/ktfmt/kdoc/KDocFormatterTest.kt index bb1b156..af13a25 100644 --- a/core/src/test/java/com/facebook/ktfmt/kdoc/KDocFormatterTest.kt +++ b/core/src/test/java/com/facebook/ktfmt/kdoc/KDocFormatterTest.kt @@ -120,10 +120,10 @@ class KDocFormatterTest { KDocFormattingOptions(72), """ /** - * Returns whether lint should check all warnings, including - * those off by default, or null if not configured in - * this configuration. This is a really really really - * long sentence which needs to be broken up. And + * Returns whether lint should check all warnings, including those + * off by default, or null if not configured in this configuration. + * This is a really really really long sentence which needs to be + * broken up. And * ThisIsALongSentenceWhichCannotBeBrokenUpAndMustBeIncludedAsAWholeWithoutNewlinesInTheMiddle. * * This is a separate section which should be flowed together with @@ -3522,9 +3522,9 @@ class KDocFormatterTest { * # Design * The splash screen icon uses the same specifications as * [Adaptive Icons](https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive) - * . This means that the icon needs to fit within a circle - * whose diameter is 2/3 the size of the icon. The actual - * values don't really matter if you use a vector icon. + * . This means that the icon needs to fit within a circle whose + * diameter is 2/3 the size of the icon. The actual values don't + * really matter if you use a vector icon. * * ## Specs * - With icon background (`Theme.SplashScreen.IconBackground`) @@ -4579,6 +4579,356 @@ class KDocFormatterTest { verifyDokka = false) } + @Test + fun testOpenRange() { + // https://github.com/tnorbye/kdoc-formatter/issues/84 + val source = + """ + /** + * This is a line that has the length such that this [link gets + * broken across lines]() which is not valid. + * + * Input is a float in range + * [0, 1) where 0 is fully settled and 1 is dismissed. Will be continue to be called after the user's finger has lifted. Will not be called for if not dismissible. + */ + """ + .trimIndent() + checkFormatter( + source, + KDocFormattingOptions(maxLineWidth = 72), + """ + /** + * This is a line that has the length such that this + * [link gets broken across lines]() which is not valid. + * + * Input is a float in range [0, 1) where 0 is fully settled and 1 is + * dismissed. Will be continue to be called after the user's finger has + * lifted. Will not be called for if not dismissible. + */ + """ + .trimIndent(), + indent = "") + } + + @Test + fun testPropertiesWithBrackets() { + val source = + // From AOSP + // tools/base/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/cxx/prefab/PackageModel.kt + """ + /** + * The Android abi.json schema. + * + * @property[abi] The ABI name of the described library. These names match the tag field for + * [com.android.build.gradle.internal.core.Abi]. + * @property[api] The minimum OS version supported by the library. i.e. the + * library's `minSdkVersion`. + * @property[ndk] The major version of the NDK that this library was built with. + * @property[stl] The STL that this library was built with. + * @property[static] If true then the library is .a, if false then .so. + */ + """ + .trimIndent() + checkFormatter( + source, + KDocFormattingOptions(maxLineWidth = 72), + """ + /** + * The Android abi.json schema. + * + * @property[abi] The ABI name of the described library. These names + * match the tag field for + * [com.android.build.gradle.internal.core.Abi]. + * @property[api] The minimum OS version supported by the library. i.e. + * the library's `minSdkVersion`. + * @property[ndk] The major version of the NDK that this library was + * built with. + * @property[stl] The STL that this library was built with. + * @property[static] If true then the library is .a, if false then .so. + */ + """ + .trimIndent(), + indent = "") + } + + @Test + fun testHandingIndent() { + val source = + """ + /** + * @param count this is how many you + * can fit in a [Bag] + * @param weight how heavy this would + * be in [Grams] + */ + """ + checkFormatter( + source, + KDocFormattingOptions(maxLineWidth = 72), + """ + /** + * @param count this is how many you can fit in a [Bag] + * @param weight how heavy this would be in [Grams] + */ + """ + .trimIndent(), + indent = "") + } + + @Test + fun testMarkupAcrossLines() { + val source = + """ + /** + * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter + * has changed. + * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link + * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes + * respectively. + */ + """ + .trimIndent() + checkFormatter( + source, + KDocFormattingOptions(maxLineWidth = 72), + """ + /** + * Broadcast Action: Indicates the Bluetooth scan mode of the local + * Adapter has changed. + * + * Always contains the extra fields [EXTRA_SCAN_MODE] and + * [EXTRA_PREVIOUS_SCAN_MODE] containing the new and old scan modes + * respectively. + */ + """ + .trimIndent(), + indent = "") + } + + @Test + fun testReferences() { + val source = + """ + /** + * Construct a rectangle from its left and top edges as well as its width and height. + * @param offset Offset to represent the top and left parameters of the Rect + * @param size Size to determine the width and height of this [Rect]. + * @return Rect with [Rect.left] and [Rect.top] configured to [Offset.x] and [Offset.y] as + * [Rect.right] and [Rect.bottom] to [Offset.x] + [Size.width] and [Offset.y] + [Size.height] + * respectively + */ + """ + .trimIndent() + checkFormatter( + source, + KDocFormattingOptions(maxLineWidth = 72), + """ + /** + * Construct a rectangle from its left and top edges as well as its + * width and height. + * + * @param offset Offset to represent the top and left parameters of the + * Rect + * @param size Size to determine the width and height of this [Rect]. + * @return Rect with [Rect.left] and [Rect.top] configured to [Offset.x] + * and [Offset.y] as [Rect.right] and [Rect.bottom] to + * [Offset.x] + [Size.width] and [Offset.y] + [Size.height] + * respectively + */ + """ + .trimIndent(), + indent = "") + } + + @Test + fun testDecapitalizeKdocTags() { + val source = + """ + /** + * Represents a component that handles scroll events, so that other components in the hierarchy + * can adjust their behaviour. + * @See [provideScrollContainerInfo] and [consumeScrollContainerInfo] + */ + """ + .trimIndent() + checkFormatter( + source, + KDocFormattingOptions(maxLineWidth = 72).apply { convertMarkup = true }, + """ + /** + * Represents a component that handles scroll events, so that other + * components in the hierarchy can adjust their behaviour. + * + * @see [provideScrollContainerInfo] and [consumeScrollContainerInfo] + */ + """ + .trimIndent(), + indent = "") + } + + @Test + fun testLineBreak() { + // Makes sure a scenario where we used to put "0." at the beginning of a new line. + // From AOSP's + // frameworks/support/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlWrapper.kt + val source = + """ + /** + * Updates z order index for [SurfaceControlWrapper]. Note that the z order for a + * surface is relative to other surfaces that are siblings of this surface. + * Behavior of siblings with the same z order is undefined. + * + * Z orders can range from Integer.MIN_VALUE to Integer.MAX_VALUE. Default z order + * index is 0. [SurfaceControlWrapper] instances are positioned back-to-front. That is + * lower z order values are rendered below other [SurfaceControlWrapper] instances with + * higher z order values. + * + * @param surfaceControl surface control to set the z order of. + * + * @param zOrder desired layer z order to set the surfaceControl. + */ + """ + .trimIndent() + checkFormatter( + source, + KDocFormattingOptions(maxLineWidth = 72), + """ + /** + * Updates z order index for [SurfaceControlWrapper]. Note that + * the z order for a surface is relative to other surfaces that + * are siblings of this surface. Behavior of siblings with the + * same z order is undefined. + * + * Z orders can range from Integer.MIN_VALUE + * to Integer.MAX_VALUE. Default z order index + * is 0. [SurfaceControlWrapper] instances are positioned + * back-to-front. That is lower z order values are rendered + * below other [SurfaceControlWrapper] instances with higher z + * order values. + * + * @param surfaceControl surface control to set the z order of. + * @param zOrder desired layer z order to set the + * surfaceControl. + */ + """ + .trimIndent(), + indent = " ") + } + + @Test + fun testDocTagsInsidePreformatted() { + // Makes sure we don't treat markup inside preformatted text as potential + // doc tags (with the fix to make us flexible recognize @See as a doctag + // it revealed we were also looking inside preformatted text and started + // treating annotations like @Retention as a doc tag.) + val source = + """ + /** + * Denotes that the annotated element of integer type, represents + * a logical type and that its value should be one of the explicitly + * named constants. If the IntDef#flag() attribute is set to true, + * multiple constants can be combined. + * + * Example: + * ``` + * @Retention(SOURCE) + * @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS}) + * public @interface NavigationMode {} + * public static final int NAVIGATION_MODE_STANDARD = 0; + * public static final int NAVIGATION_MODE_LIST = 1; + * public static final int NAVIGATION_MODE_TABS = 2; + * ... + * public abstract void setNavigationMode(@NavigationMode int mode); + * + * @NavigationMode + * public abstract int getNavigationMode(); + * ``` + * + * For a flag, set the flag attribute: + * ``` + * @IntDef( + * flag = true, + * value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} + * ) + * ``` + * + * @see LongDef + */ + + """ + .trimIndent() + checkFormatter( + source, + KDocFormattingOptions(maxLineWidth = 72), + """ + /** + * Denotes that the annotated element of integer type, represents a + * logical type and that its value should be one of the explicitly named + * constants. If the IntDef#flag() attribute is set to true, multiple + * constants can be combined. + * + * Example: + * ``` + * @Retention(SOURCE) + * @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS}) + * public @interface NavigationMode {} + * public static final int NAVIGATION_MODE_STANDARD = 0; + * public static final int NAVIGATION_MODE_LIST = 1; + * public static final int NAVIGATION_MODE_TABS = 2; + * ... + * public abstract void setNavigationMode(@NavigationMode int mode); + * + * @NavigationMode + * public abstract int getNavigationMode(); + * ``` + * + * For a flag, set the flag attribute: + * ``` + * @IntDef( + * flag = true, + * value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} + * ) + * ``` + * + * @see LongDef + */ + """ + .trimIndent(), + indent = "") + } + + @Test + fun testConvertMarkup2() { + // Bug where the markup conversion around <p></p> wasn't working correctly + // From AOSP's + // frameworks/support/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothAdapter.kt + val source = + """ + /** + * Fundamentally, this is your starting point for all + * Bluetooth actions. * </p> + * <p>This class is thread safe.</p> + * + * @hide + */ + """ + .trimIndent() + checkFormatter( + source, + KDocFormattingOptions(maxLineWidth = 72), + """ + /** + * Fundamentally, this is your starting point for all Bluetooth actions. + * + * This class is thread safe. + * + * @hide + */ + """ + .trimIndent(), + indent = "") + } + /** * Test utility method: from a source kdoc, derive an "equivalent" kdoc (same punctuation, * whitespace, capitalization and length of words) with words from Lorem Ipsum. Useful to create diff --git a/core/src/test/java/com/facebook/ktfmt/kdoc/UtilitiesTest.kt b/core/src/test/java/com/facebook/ktfmt/kdoc/UtilitiesTest.kt index e5ad1a7..0bdc705 100644 --- a/core/src/test/java/com/facebook/ktfmt/kdoc/UtilitiesTest.kt +++ b/core/src/test/java/com/facebook/ktfmt/kdoc/UtilitiesTest.kt @@ -100,6 +100,77 @@ class UtilitiesTest { assertThat(" \t@param\t foo bar.".getParamName()).isEqualTo("foo") assertThat("@param[foo]".getParamName()).isEqualTo("foo") assertThat("@param [foo]".getParamName()).isEqualTo("foo") - assertThat("@param ".getParamName()).isEqualTo(null) + assertThat("@param ".getParamName()).isNull() + } + + @Test + fun testComputeWords() { + fun List<String>.describe(): String { + return "listOf(${this.joinToString(", ") { "\"$it\"" }})" + } + fun check(text: String, expected: List<String>, customizeParagraph: (Paragraph) -> Unit = {}) { + val task = FormattingTask(KDocFormattingOptions(12), "/** $text */", "") + val paragraph = Paragraph(task) + paragraph.content.append(text) + customizeParagraph(paragraph) + val words = paragraph.computeWords() + + assertThat(words.describe()).isEqualTo(expected.describe()) + } + check("Foo", listOf("Foo")) + check("Foo Bar Baz", listOf("Foo", "Bar", "Baz")) + check("Foo Bar Baz", listOf("Foo Bar", "Baz")) { it.quoted = true } + check("Foo Bar Baz", listOf("Foo Bar", "Baz")) { it.hanging = true } + check("1. Foo", listOf("1.", "Foo")) + // "1." can't start a word; if it ends up at the beginning of a line it becomes + // a numbered element. + check("Foo 1.", listOf("Foo 1.")) + check("Foo bar [Link Text] foo bar.", listOf("Foo", "bar", "[Link Text]", "foo", "bar.")) + check("Interval [0, 1) foo bar.", listOf("Interval [0, 1)", "foo", "bar.")) + + // ">" cannot start a word; it would become quoted text + check("if >= 3", listOf("if >=", "3")) + check("if >= 3.", listOf("if >= 3.")) + + check( + "SDK version - [`Partial(Mode.UseIfAvailable)`](Partial) on API 24+", + listOf("SDK", "version - [`Partial(Mode.UseIfAvailable)`](Partial)", "on", "API", "24+")) + + check( + "Z orders can range from Integer.MIN_VALUE to Integer.MAX_VALUE. Default z order " + + " index is 0. [SurfaceControlWrapper] instances are positioned back-to-front.", + listOf( + "Z", + "orders", + "can", + "range", + "from", + "Integer.MIN_VALUE", + "to", + "Integer.MAX_VALUE.", + "Default", + "z", + "order", + "index", + "is 0. [SurfaceControlWrapper]", + "instances", + "are", + "positioned", + "back-to-front.")) + check( + "Equates to `cmd package compile -f -m speed <package>` on API 24+.", + listOf( + "Equates", + "to", + "`cmd", + "package", + "compile", + "-f", + "-m", + "speed", + "<package>`", + "on", + "API", + "24+.")) } } diff --git a/core/src/test/java/com/facebook/ktfmt/testutil/KtfmtTruth.kt b/core/src/test/java/com/facebook/ktfmt/testutil/KtfmtTruth.kt index 3a2a620..de67121 100644 --- a/core/src/test/java/com/facebook/ktfmt/testutil/KtfmtTruth.kt +++ b/core/src/test/java/com/facebook/ktfmt/testutil/KtfmtTruth.kt @@ -29,14 +29,15 @@ import org.junit.Assert * Verifies the given code passes through formatting, and stays the same at the end * * @param code a code string that continas an optional first line made of "---" in the case - * [deduceMaxWidth] is true. For example: + * [deduceMaxWidth] is true. For example: * ``` * -------------------- * // exactly 20 `-` above * fun f() * ``` + * * @param deduceMaxWidth if this is true the code string should start with a line of "-----" in the - * beginning to indicate the max width to format by + * beginning to indicate the max width to format by */ fun assertFormatted( code: String, @@ -5,7 +5,7 @@ <groupId>com.facebook</groupId> <artifactId>ktfmt-parent</artifactId> - <version>0.42</version> + <version>0.43</version> <packaging>pom</packaging> <name>Ktfmt Parent</name> diff --git a/version.txt b/version.txt index f4bb45b..68f3790 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.42 +0.43 diff --git a/website/index.html b/website/index.html index 7572e39..151769b 100644 --- a/website/index.html +++ b/website/index.html @@ -134,7 +134,7 @@ <pre> # build.gradle.kts plugins { id("com.diffplug.spotless") } - + // version and style are optional spotless { kotlin { ktfmt('{{version}}').kotlinlangStyle() } } </pre @@ -285,7 +285,7 @@ $ java -jar ktfmt-{{version}}-jar-with-dependencies.jar [--kotlinlang-style] [fi /> </a> <br /> - <small>© {{year}} Facebook, Inc. Based on <a href="https://microsoft.github.io/monaco-editor">https://microsoft.github.io/monaco-editor</a></small> + <small>Copyright © Meta Platforms, Inc. Based on <a href="https://microsoft.github.io/monaco-editor">https://microsoft.github.io/monaco-editor</a></small> </p> </footer> diff --git a/website/package-lock.json b/website/package-lock.json index d28214b..43d1698 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -946,9 +946,9 @@ } }, "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true, "engines": { "node": ">=0.10" @@ -2750,9 +2750,9 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -3466,9 +3466,9 @@ } }, "node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true, "engines": { "node": ">=0.6" @@ -5867,9 +5867,9 @@ "dev": true }, "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true }, "deep-is": { @@ -7340,9 +7340,9 @@ } }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -7910,9 +7910,9 @@ "dev": true }, "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true }, "read-pkg": { |