Merge 81c0b333e3c9709b59d2b9d30024ae064a48f6c7 on remote branch
Change-Id: I4e8e9bd05831f486e26391af43734212386be57e
diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
new file mode 100644
index 0000000..f75004e
--- /dev/null
+++ b/.github/workflows/build_and_test.yml
@@ -0,0 +1,38 @@
+name: Build and Test
+
+on:
+ push:
+ paths-ignore:
+ - '**.md'
+ pull_request:
+ paths-ignore:
+ - '**.md'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ # test against latest update of each major Java version, as well as specific updates of LTS versions:
+ java: [ 11, 13 ]
+ name: Build ktfmt on Java ${{ matrix.java }}
+ steps:
+ - uses: actions/checkout@v1
+ with:
+ submodules: recursive
+ - name: Set up JDK ${{ matrix.java }}
+ uses: actions/setup-java@v1
+ with:
+ java-version: ${{ matrix.java }}
+ - name: Build ktfmt
+ run: mvn -B install --file pom.xml
+ - name: Build ktfmt_idea_plugin
+ run: |
+ pushd ktfmt_idea_plugin
+ ./gradlew build
+ popd
+ - name: Build the Online Formatter
+ run: |
+ pushd online_formatter
+ ./gradlew build
+ popd
diff --git a/.github/workflows/publish_artifacts_on_release.yaml b/.github/workflows/publish_artifacts_on_release.yaml
new file mode 100644
index 0000000..85ecb96
--- /dev/null
+++ b/.github/workflows/publish_artifacts_on_release.yaml
@@ -0,0 +1,70 @@
+# When a new release is created, publish artifacts to Maven Central / JetBrains Marketplace / etc..
+#
+# About secrets used here:
+# 1. OSSRH_USERNAME, OSSRH_TOKEN: token obtained from https://oss.sonatype.org/
+# 2. OSSRH_GPG_SECRET_KEY - private key for signing Maven artifacts
+# 3. OSSRH_GPG_SECRET_KEY_PASSWORD - password for unlocking OSSRH_GPG_SECRET_KEY
+# 4. JETBRAINS_MARKETPLACE_TOKEN - token obtained from https://plugins.jetbrains.com/author/me/tokens
+
+name: Publish package to Maven Central and JetBrains Marketplace
+on:
+ release:
+ types: [created]
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up Maven Central Repository
+ uses: actions/setup-java@v1
+ with:
+ java-version: 11
+ server-id: ossrh
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_PASSWORD
+ - id: install-secret-key
+ name: Install gpg secret key
+ run: |
+ cat <(echo -e "${{ secrets.OSSRH_GPG_SECRET_KEY }}") | gpg --batch --import
+ gpg --list-secret-keys --keyid-format LONG
+ - id: publish-to-central
+ name: Publish to Maven Central Repository
+ env:
+ MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
+ MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
+ run: |
+ mvn \
+ --no-transfer-progress \
+ --batch-mode \
+ -Dgpg.passphrase=${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} \
+ -Prelease clean deploy
+ - name: Publish IntelliJ plugin to JetBrains Marketplace
+ run: |
+ pushd ktfmt_idea_plugin
+ ./gradlew publishPlugin --stacktrace
+ popd
+ env:
+ JETBRAINS_MARKETPLACE_TOKEN: ${{ secrets.JETBRAINS_MARKETPLACE_TOKEN }}
+ - uses: actions/setup-node@v2
+ - name: Deploy website
+ run: |
+ KTFMT_TMP_DIR=$(mktemp -d)
+
+ pushd website
+ npm install .
+ KTFMT_WEBSITE_OUTPUT_DIR="$KTFMT_TMP_DIR" gulp build-website
+ popd
+
+ git fetch
+ git checkout gh-pages
+ git config user.email ktfmt@facebook.com
+ git config user.name ktfmt
+
+ rm -rf *
+ cp -R "$KTFMT_TMP_DIR"/* .
+
+ git add .
+ git commit -m "Publish website"
+ git push --force
+
+ rm -rf "$KTFMT_TMP_DIR"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b11d424
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+.gradle/
+ktfmt_idea_plugin/build/
+.idea/
+*.ims
+*.iml
+
+.classpath
+.project
+.factorypath
+.settings/
+.apt_generated/
+
+target/
+
+bin/
+out/
+
+.DS_Store
+classes/
+
+online_formatter/build/
+online_formatter/out.yml
+
+website/node_modules
+release-ktfmt-website/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..ded40a0
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "vendor/google-java-format"]
+ path = vendor/google-java-format
+ url = https://github.com/google/google-java-format.git
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..5294b93
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// 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.
+//
+
+// README:
+// - Before expanding the visiblity of any of the targets in this file,
+// please add a test that covers the new dependencies in the TEST_MAPPING file
+// of this directory. We cannot guarantee that ExoPlayer dependents will not
+// be accidentally broken if it is not covered by presubmit.
+// - The names in this files follow the pattern exoplayer-dep_name[-module_name]
+// where dep_name identifies the client, and module_name disambiguates the
+// module for cases where necessary (example: The same client depends
+// separately on two exoplayer modules).
+
+package {
+ default_applicable_licenses: ["external_ktfmt_license"],
+}
+
+
+license {
+ name: "external_ktfmt_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "LICENSE",
+ ],
+}
+
+java_binary_host {
+ name: "ktfmt",
+ manifest: "MANIFEST.mf",
+ srcs: ["core/src/main/**/*.kt"],
+ static_libs: [
+ "kotlin-stdlib-jdk7",
+ "kotlin-compiler-embeddable",
+ "google_java_format",
+ "guava",
+ "trove-prebuilt",
+ "jna-prebuilt",
+ ],
+}
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..4bd525a
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to make participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies within all project spaces, and it also applies when
+an individual is representing the project or its community in public spaces.
+Examples of representing a project or community include using an official
+project e-mail address, posting via an official social media account, or acting
+as an appointed representative at an online or offline event. Representation of
+a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at <opensource-conduct@fb.com>. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..75522ca
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,33 @@
+# Contributing to ktfmt
+We want to make contributing to this project as easy and transparent as
+possible.
+
+**If you plan a significant change, please first start a discussion as a GitHub Issue.**
+
+## Pull Requests
+We actively welcome your pull requests.
+
+1. Fork the repo and create your branch from `main`.
+2. If you've added code that should be tested, add tests.
+3. If you've changed APIs, update the documentation.
+4. Ensure the test suite passes.
+5. Make sure your code lints.
+6. If you haven't already, complete the Contributor License Agreement ("CLA").
+
+## Contributor License Agreement ("CLA")
+In order to accept your pull request, we need you to submit a CLA. You only need
+to do this once to work on any of Facebook's open source projects.
+
+Complete your CLA here: <https://code.facebook.com/cla>
+
+## Issues
+We use GitHub issues to track public bugs. Please ensure your description is
+clear and has sufficient instructions to be able to reproduce the issue.
+
+Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
+disclosure of security bugs. In those cases, please go through the process
+outlined on that page and do not file a public issue.
+
+## License
+By contributing to ktfmt, you agree that your contributions will be licensed
+under the LICENSE file in the root directory of this source tree.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..872bf6a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,206 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+=============================================================================
+
+This product bundles parts of google-java-format 1.7, which is available under an
+Apache license.
diff --git a/MANIFEST.mf b/MANIFEST.mf
new file mode 100644
index 0000000..a6f54fb
--- /dev/null
+++ b/MANIFEST.mf
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Main-Class: com.facebook.ktfmt.cli.Main
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..23da49e
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,13 @@
+name: "ktfmt"
+description:
+ "A formatter for Kotlin code."
+
+third_party {
+ url {
+ type: GIT
+ value: "https://github.com/facebookincubator/ktfmt.git"
+ }
+ version: "v0.39"
+ last_upgrade_date { year: 2022 month: 7 day: 25 }
+ license_type: NOTICE
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..92e2205
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+jdemeulenaere@google.com
+nicomazz@google.com
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..71b7c8b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,124 @@
+[![](https://github.com/facebookincubator/ktfmt/workflows/build/badge.svg)](https://github.com/facebookincubator/ktfmt/actions?query=workflow%3Abuild)
+
+# ktfmt
+
+`ktfmt` is a program that pretty-prints (formats) Kotlin code, based on [google-java-format](https://github.com/google/google-java-format).
+
+**Note** that `ktfmt` still has some rough edges which we're constantly working on fixing.
+
+The minimum supported runtime version is JDK 11, released September 2018.
+
+## Demo
+
+|Before Formatting| Formatted by `ktfmt`|
+| ---- | ---- |
+| ![Original](docs/images/before.png) | ![ktfmt](docs/images/ktfmt.png) |
+
+For comparison, the same code formatted by [`ktlint`](https://github.com/pinterest/ktlint) and IntelliJ:
+
+| Formatted by `ktlint`|Formatted by IntelliJ|
+| ------ | --------|
+| ![ktlint](docs/images/ktlint.png) | ![IntelliJ](docs/images/intellij.png) |
+
+## Using the formatter
+
+### IntelliJ, Android Studio, and other JetBrains IDEs
+
+A
+[ktfmt IntelliJ plugin](https://plugins.jetbrains.com/plugin/14912-ktfmt)
+is available from the plugin repository. To install it, go to your IDE's
+settings and select the `Plugins` category. Click the `Marketplace` tab, search
+for the `ktfmt` plugin, and click the `Install` button.
+
+The plugin will be disabled by default. To enable it in the current project, go
+to `File→Settings...→ktfmt Settings` (or `IntelliJ
+IDEA→Preferences...→Editor→ktfmt Settings` on macOS) and
+check the `Enable ktfmt` checkbox. (A notification will be
+presented when you first open a project offering to do this for you.)
+
+To enable it by default in new projects, use `File→New Project Settings→Preferences for new Projects→Editor→ktfmt Settings`.
+
+When enabled, it will replace the normal `Reformat Code` action, which can be
+triggered from the `Code` menu or with the Ctrl-Alt-L (by default) keyboard
+shortcut.
+
+To configure IntelliJ to approximate ktfmt's formatting rules during code editing,
+you can edit your project's
+[`.editorconfig` file](https://www.jetbrains.com/help/idea/configuring-code-style.html#editorconfig)
+to include the Kotlin section from one of the files inside
+[`docs/editorconfig`](docs/editorconfig).
+Not all of ktfmt's rules can be represented as IntelliJ editor settings, so you will still
+need to run ktfmt. Alternately, that file can be used as a reference to manually change
+the project's code style settings.
+
+### from the command-line
+
+[Download the formatter](https://github.com/facebookincubator/ktfmt/releases)
+and run it with:
+
+```
+java -jar /path/to/ktfmt-<VERSION>-jar-with-dependencies.jar [--dropbox-style] [files...]
+```
+
+`--dropbox-style` makes `ktfmt` use a block indent of 4 spaces instead of 2. See below for details.
+
+***Note:*** *There is no configurability as to the formatter's algorithm for
+formatting (apart from `--dropbox-style`). This is a deliberate design decision to unify our code
+formatting on a single format.*
+
+### using Gradle
+
+A [Gradle plugin (ktfmt-gradle)](https://github.com/cortinico/ktfmt-gradle) is available on the Gradle Plugin Portal. To set it up, just follow the instructions in the [How-to-use section](https://github.com/cortinico/ktfmt-gradle#how-to-use-).
+
+Alternatively, you can use [Spotless](https://github.com/diffplug/spotless) with the [ktfmt Gradle plugin](https://github.com/diffplug/spotless/tree/main/plugin-gradle#ktfmt).
+
+### using Maven
+
+Consider using [Spotless](https://github.com/diffplug/spotless) with the [ktfmt Maven plugin](https://github.com/diffplug/spotless/tree/main/plugin-maven#ktfmt).
+
+## FAQ
+
+### `ktfmt` vs `ktlint` vs IntelliJ
+
+`ktfmt` uses google-java-format's underlying engine, and as such, many items on [google-java-format's FAQ](https://github.com/google/google-java-format/wiki/FAQ) apply to `ktfmt` as well.
+
+In particular,
+1. `ktfmt` ignores most existing formatting. It respects existing newlines in some places, but in general, its output is deterministic and is independent of the input code.
+2. `ktfmt` exposes no configuration options that govern formatting behavior. See https://github.com/google/google-java-format/wiki/FAQ#i-just-need-to-configure-it-a-bit-differently-how for the rationale.
+
+These two properties make `ktfmt` a good fit in large Kotlin code bases, where consistency is very important.
+
+We created `ktfmt` because `ktlint` and IntelliJ sometime fail to produce nice-looking code that fits in 100 columns, as can be seen in the [Demo](README.md#Demo) section.
+
+### `ktfmt` uses a 2-space indent; why not 4? any way to change that?
+
+Two reasons -
+1. Many of our projects use a mixture of Kotlin and Java, and we found the back-and-forth in styles to be distracting.
+2. From a pragmatic standpoint, the formatting engine behind google-java-format uses more whitespace and newlines than other formatters. Using an indentation of 4 spaces quickly reaches the maximal column width.
+
+However, we do offer an escape-hatch for projects that absolutely cannot make the move to `ktfmt` because of 2-space: the `--dropbox-style` flag changes block indents to 4-space.
+
+## Developer's Guide
+
+### Setup
+
+* Open `pom.xml` in IntelliJ. Choose "Open as a Project"
+* The IntelliJ project will unfortunately be broken on import. To fix,
+ * Turn off ErrorProne by removing the compiler parameters in IntelliJ at the bottom of "Settings -> Build, Execution, Deployment -> Compiler -> Java Compiler" (see https://github.com/google/google-java-format/issues/417)
+
+### Development
+
+* Currently, we mainly develop by adding tests to `FormatterTest.kt`.
+
+### Building on the Command Line
+
+* Run `mvn install`
+* Run `java -jar core/target/ktfmt-<VERSION>-jar-with-dependencies.jar`
+
+### Releasing
+
+See [RELEASING.md](RELEASING.md).
+
+## License
+
+Apache License 2.0
diff --git a/RELEASING.md b/RELEASING.md
new file mode 100644
index 0000000..64d5745
--- /dev/null
+++ b/RELEASING.md
@@ -0,0 +1,10 @@
+# Releasing a New `ktfmt` Version
+
+1. Run `./bump_versions.sh $OLD_VERSION $NEW_VERSION` and commit the changes.
+2. Create a new Release in GitHub. A GitHub Action is automatically triggered and builds and publishes the artifacts to
+ 1. Maven
+ 2. IntelliJ Plugin marketplace
+3. TODO: also automate website generation (https://facebookincubator.github.io/ktfmt/) and the AWS Lambda that powers it. For now, you must clone the repo locally, and manually run some steps.
+ 1. pushd online_formatter; ./build_and_deploy.sh; popd
+ 1. Credentials should be configured using https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#credentials
+ 2. Follow instructions in website/README.md
diff --git a/core/pom.xml b/core/pom.xml
new file mode 100644
index 0000000..c7e8df4
--- /dev/null
+++ b/core/pom.xml
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ktfmt</artifactId>
+
+ <name>Ktfmt</name>
+ <description>A program that reformats Kotlin source code to comply with the common community standard for Kotlin code conventions.</description>
+
+ <parent>
+ <groupId>com.facebook</groupId>
+ <artifactId>ktfmt-parent</artifactId>
+ <version>0.39</version>
+ </parent>
+
+ <properties>
+ <dokka.version>0.10.1</dokka.version>
+ <kotlin.version>1.6.10</kotlin.version>
+ <kotlin.compiler.incremental>true</kotlin.compiler.incremental>
+ <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
+ <main.class>com.facebook.ktfmt.cli.Main</main.class>
+ </properties>
+
+ <pluginRepositories>
+ <pluginRepository>
+ <id>jcenter</id>
+ <name>JCenter</name>
+ <url>https://jcenter.bintray.com/</url>
+ </pluginRepository>
+ </pluginRepositories>
+
+ <build>
+ <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
+ <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-maven-plugin</artifactId>
+ <version>${kotlin.version}</version>
+
+ <executions>
+ <execution>
+ <id>compile</id>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ <configuration>
+ <sourceDirs>
+ <sourceDir>${project.basedir}/src/main/java</sourceDir>
+ </sourceDirs>
+ </configuration>
+ </execution>
+
+ <execution>
+ <id>test-compile</id>
+ <goals>
+ <goal>test-compile</goal>
+ </goals>
+ <configuration>
+ <sourceDirs>
+ <sourceDir>${project.basedir}/src/test/java</sourceDir>
+ </sourceDirs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.5.1</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ <executions>
+ <!-- Replacing default-compile as it is treated specially by maven -->
+ <execution>
+ <id>default-compile</id>
+ <phase>none</phase>
+ </execution>
+ <!-- Replacing default-testCompile as it is treated specially by maven -->
+ <execution>
+ <id>default-testCompile</id>
+ <phase>none</phase>
+ </execution>
+ <execution>
+ <id>java-compile</id>
+ <phase>compile</phase>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>java-test-compile</id>
+ <phase>test-compile</phase>
+ <goals>
+ <goal>testCompile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <archive>
+ <manifest>
+ <addClasspath>true</addClasspath>
+ <mainClass>${main.class}</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.6</version>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>${main.class}</mainClass>
+ </manifest>
+ </archive>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>29.0-jre</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-stdlib-jdk7</artifactId>
+ <version>${kotlin.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-test</artifactId>
+ <version>${kotlin.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-compiler-embeddable</artifactId>
+ <version>${kotlin.version}</version>
+ </dependency>
+ <!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>4.2.2</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.googlejavaformat</groupId>
+ <artifactId>google-java-format</artifactId>
+ <version>1.8</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.13.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.truth</groupId>
+ <artifactId>truth</artifactId>
+ <version>1.0</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>release</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.sonatype.plugins</groupId>
+ <artifactId>nexus-staging-maven-plugin</artifactId>
+ <version>1.6.7</version>
+ <extensions>true</extensions>
+ <configuration>
+ <serverId>ossrh</serverId>
+ <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+ <autoReleaseAfterClose>true</autoReleaseAfterClose>
+ </configuration>
+ </plugin>
+
+ <!-- Source JAR -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.2.1</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- Javadoc JAR -->
+ <!-- Dokka, Kotlin's javadoc, doesn't work on JDK 10+, so we're creating an empty javadoc jar instead. -->
+ <!-- (https://github.com/Kotlin/dokka/issues/294) -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ <configuration>
+ <classifier>javadoc</classifier>
+ <classesDirectory>${project.basedir}/non/existing/dir</classesDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- Sign artifacts -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.6</version>
+ <executions>
+ <execution>
+ <id>sign-artifacts</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ <configuration>
+ <!-- Honor -Dgpg.passphrase=... on the command line. -->
+ <gpgArguments>
+ <arg>--pinentry-mode</arg>
+ <arg>loopback</arg>
+ </gpgArguments>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+ <distributionManagement>
+ <snapshotRepository>
+ <id>ossrh</id>
+ <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+ </snapshotRepository>
+ </distributionManagement>
+
+</project>
diff --git a/core/src/main/java/com/facebook/ktfmt/cli/Main.kt b/core/src/main/java/com/facebook/ktfmt/cli/Main.kt
new file mode 100644
index 0000000..4c965bb
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/cli/Main.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.cli
+
+import com.facebook.ktfmt.format.Formatter
+import com.facebook.ktfmt.format.ParseError
+import com.google.googlejavaformat.FormattingError
+import java.io.BufferedReader
+import java.io.File
+import java.io.IOException
+import java.io.InputStream
+import java.io.InputStreamReader
+import java.io.PrintStream
+import java.util.concurrent.atomic.AtomicInteger
+import kotlin.system.exitProcess
+
+class Main(
+ private val input: InputStream,
+ private val out: PrintStream,
+ private val err: PrintStream,
+ args: Array<String>
+) {
+ companion object {
+ @JvmStatic
+ fun main(args: Array<String>) {
+ exitProcess(Main(System.`in`, System.out, System.err, args).run())
+ }
+
+ /**
+ * expandArgsToFileNames expands 'args' to a list of .kt files to format.
+ *
+ * Most commonly, 'args' is either a list of .kt files, or a name of a directory whose contents
+ * the user wants to format.
+ */
+ fun expandArgsToFileNames(args: List<String>): List<File> {
+ if (args.size == 1 && File(args[0]).isFile) {
+ return listOf(File(args[0]))
+ }
+ val result = mutableListOf<File>()
+ for (arg in args) {
+ if (arg == "-") {
+ error(
+ "Error: '-', which causes ktfmt to read from stdin, should not be mixed with file name")
+ }
+ result.addAll(
+ File(arg).walkTopDown().filter {
+ it.isFile && (it.extension == "kt" || it.extension == "kts")
+ })
+ }
+ return result
+ }
+ }
+
+ private val parsedArgs: ParsedArgs = ParsedArgs.processArgs(err, args)
+
+ fun run(): Int {
+ if (parsedArgs.fileNames.isEmpty()) {
+ err.println(
+ "Usage: ktfmt [--dropbox-style | --google-style | --kotlinlang-style] [--dry-run] [--set-exit-if-changed] File1.kt File2.kt ...")
+ err.println("Or: ktfmt @file")
+ return 1
+ }
+
+ if (parsedArgs.fileNames.size == 1 && parsedArgs.fileNames[0] == "-") {
+ return try {
+ val alreadyFormatted = format(null)
+ if (!alreadyFormatted && parsedArgs.setExitIfChanged) 1 else 0
+ } catch (e: Exception) {
+ 1
+ }
+ }
+
+ val files: List<File>
+ try {
+ files = expandArgsToFileNames(parsedArgs.fileNames)
+ } catch (e: java.lang.IllegalStateException) {
+ err.println(e.message)
+ return 1
+ }
+
+ if (files.isEmpty()) {
+ err.println("Error: no .kt files found")
+ return 1
+ }
+
+ val retval = AtomicInteger(0)
+ files.parallelStream().forEach {
+ try {
+ if (!format(it) && parsedArgs.setExitIfChanged) {
+ retval.set(1)
+ }
+ } catch (e: Exception) {
+ retval.set(1)
+ }
+ }
+ return retval.get()
+ }
+
+ /**
+ * Handles the logic for formatting and flags.
+ *
+ * If dry run mode is active, this simply prints the name of the [source] (file path or `<stdin>`)
+ * to [out]. Otherwise, this will run the appropriate formatting as normal.
+ *
+ * @param file The file to format. If null, the code is read from <stdin>.
+ * @return true iff input is valid and already formatted.
+ */
+ private fun format(file: File?): Boolean {
+ val fileName = file?.toString() ?: "<stdin>"
+ try {
+ val code = file?.readText() ?: BufferedReader(InputStreamReader(input)).readText()
+ val formattedCode = Formatter.format(parsedArgs.formattingOptions, code)
+ val alreadyFormatted = code == formattedCode
+
+ // stdin
+ if (file == null) {
+ if (parsedArgs.dryRun) {
+ if (!alreadyFormatted) {
+ out.println("<stdin>")
+ }
+ } else {
+ out.print(formattedCode)
+ }
+ return alreadyFormatted
+ }
+
+ if (parsedArgs.dryRun) {
+ if (!alreadyFormatted) {
+ out.println(fileName)
+ }
+ } else {
+ // TODO(T111284144): Add tests
+ if (!alreadyFormatted) {
+ file.writeText(formattedCode)
+ }
+ err.println("Done formatting $fileName")
+ }
+
+ return alreadyFormatted
+ } catch (e: IOException) {
+ err.println("Error formatting $fileName: ${e.message}; skipping.")
+ throw e
+ } catch (e: ParseError) {
+ handleParseError(fileName, e)
+ throw e
+ } catch (e: FormattingError) {
+ for (diagnostic in e.diagnostics()) {
+ System.err.println("$fileName:$diagnostic")
+ }
+ e.printStackTrace(err)
+ throw e
+ }
+ }
+
+ private fun handleParseError(fileName: String, e: ParseError) {
+ err.println("$fileName:${e.message}")
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt b/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt
new file mode 100644
index 0000000..503cb89
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.cli
+
+import com.facebook.ktfmt.format.Formatter
+import com.facebook.ktfmt.format.FormattingOptions
+import java.io.File
+import java.io.PrintStream
+
+/** ParsedArgs holds the arguments passed to ktfmt on the command-line, after parsing. */
+data class ParsedArgs(
+ val fileNames: List<String>,
+ val formattingOptions: FormattingOptions,
+ /**
+ * Run the formatter without writing changes to any files. This will print the path of any files
+ * that would be changed if the formatter is run normally.
+ */
+ val dryRun: Boolean,
+
+ /** Return exit code 1 if any formatting changes are detected. */
+ val setExitIfChanged: Boolean,
+) {
+ companion object {
+
+ fun processArgs(err: PrintStream, args: Array<String>): ParsedArgs {
+ if (args.size == 1 && args[0].startsWith("@")) {
+ return parseOptions(err, File(args[0].substring(1)).readLines().toTypedArray())
+ } else {
+ return parseOptions(err, args)
+ }
+ }
+
+ /** parseOptions parses command-line arguments passed to ktfmt. */
+ fun parseOptions(err: PrintStream, args: Array<String>): ParsedArgs {
+ val fileNames = mutableListOf<String>()
+ var formattingOptions = FormattingOptions()
+ var dryRun = false
+ var setExitIfChanged = false
+
+ for (arg in args) {
+ when {
+ arg == "--dropbox-style" -> formattingOptions = Formatter.DROPBOX_FORMAT
+ arg == "--google-style" -> formattingOptions = Formatter.GOOGLE_FORMAT
+ arg == "--kotlinlang-style" -> formattingOptions = Formatter.KOTLINLANG_FORMAT
+ arg == "--dry-run" || arg == "-n" -> dryRun = true
+ arg == "--set-exit-if-changed" -> setExitIfChanged = true
+ arg.startsWith("--") -> err.println("Unexpected option: $arg")
+ arg.startsWith("@") -> err.println("Unexpected option: $arg")
+ else -> fileNames.add(arg)
+ }
+ }
+ return ParsedArgs(fileNames, formattingOptions, dryRun, setExitIfChanged)
+ }
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/debughelpers/OpsDebug.kt b/core/src/main/java/com/facebook/ktfmt/debughelpers/OpsDebug.kt
new file mode 100644
index 0000000..11f5ba4
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/debughelpers/OpsDebug.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.debughelpers
+
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.Range
+import com.google.googlejavaformat.CloseOp
+import com.google.googlejavaformat.CommentsHelper
+import com.google.googlejavaformat.Doc
+import com.google.googlejavaformat.Input
+import com.google.googlejavaformat.Op
+import com.google.googlejavaformat.OpenOp
+import com.google.googlejavaformat.OpsBuilder
+import com.google.googlejavaformat.Output
+import java.util.regex.Pattern
+
+/** A regex to extract the indent size from OpenOp.toString(). */
+private val OPENOP_STRING_FORM_REGEX = Pattern.compile("""OpenOp\{plusIndent=Const\{n=(\d+)}}""")
+
+/**
+ * printOps prints a list of Ops in a hierarchical way.
+ *
+ * It's useful when debugging incorrect newlines.
+ */
+fun printOps(ops: ImmutableList<Op>) {
+ println("Ops: ")
+ var indent = 0
+ for (op in ops) {
+ val line =
+ when (op) {
+ is OpenOp -> {
+ val matcher = OPENOP_STRING_FORM_REGEX.matcher(op.toString())
+ if (matcher.matches()) {
+ val opIndent = matcher.group(1)
+ "[ " + if (opIndent != "0") opIndent else ""
+ } else {
+ "[ $op"
+ }
+ }
+ is CloseOp -> "]"
+ is Doc.Token -> {
+ var result: String? = ""
+ val output =
+ object : Output() {
+ override fun indent(indent: Int) = Unit
+
+ override fun blankLine(k: Int, wanted: OpsBuilder.BlankLineWanted?) = Unit
+
+ override fun markForPartialFormat(start: Input.Token?, end: Input.Token?) = Unit
+
+ override fun getCommentsHelper(): CommentsHelper {
+ throw Throwable()
+ }
+
+ override fun append(text: String?, range: Range<Int>?) {
+ result = text
+ }
+ }
+ op.write(output)
+ """"$result""""
+ }
+ else -> {
+ val result = op.toString()
+ if (result == "Space{}") "\" \"" else result
+ }
+ }
+ if (op is CloseOp) {
+ indent--
+ }
+ repeat(2 * indent) { print(" ") }
+ if (op is OpenOp) {
+ indent++
+ }
+ println(line)
+ }
+ println()
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/debughelpers/PrintAstVisitor.kt b/core/src/main/java/com/facebook/ktfmt/debughelpers/PrintAstVisitor.kt
new file mode 100644
index 0000000..489deba
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/debughelpers/PrintAstVisitor.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.debughelpers
+
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
+
+class PrintAstVisitor : KtTreeVisitorVoid() {
+ private var depth = 0
+
+ override fun visitElement(element: PsiElement) {
+ print(" ".repeat(depth))
+ println("${element.javaClass.simpleName} ${element.text?.replace("\n", "\\n")}")
+ depth++
+ super.visitElement(element)
+ depth--
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/Formatter.kt b/core/src/main/java/com/facebook/ktfmt/format/Formatter.kt
new file mode 100644
index 0000000..1fa90f7
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/Formatter.kt
@@ -0,0 +1,185 @@
+/*
+ * 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.facebook.ktfmt.debughelpers.printOps
+import com.facebook.ktfmt.format.FormattingOptions.Style.DROPBOX
+import com.facebook.ktfmt.format.FormattingOptions.Style.GOOGLE
+import com.facebook.ktfmt.format.RedundantElementRemover.dropRedundantElements
+import com.facebook.ktfmt.format.WhitespaceTombstones.indexOfWhitespaceTombstone
+import com.facebook.ktfmt.kdoc.Escaping
+import com.facebook.ktfmt.kdoc.KDocCommentsHelper
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.Range
+import com.google.googlejavaformat.Doc
+import com.google.googlejavaformat.DocBuilder
+import com.google.googlejavaformat.Newlines
+import com.google.googlejavaformat.OpsBuilder
+import com.google.googlejavaformat.java.FormatterException
+import com.google.googlejavaformat.java.JavaOutput
+import org.jetbrains.kotlin.com.intellij.openapi.util.text.StringUtil
+import org.jetbrains.kotlin.com.intellij.openapi.util.text.StringUtilRt
+import org.jetbrains.kotlin.com.intellij.psi.PsiComment
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.com.intellij.psi.PsiElementVisitor
+import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace
+import org.jetbrains.kotlin.psi.KtImportDirective
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+object Formatter {
+
+ @JvmField
+ val GOOGLE_FORMAT = FormattingOptions(style = GOOGLE, blockIndent = 2, continuationIndent = 2)
+
+ /** A format that attempts to reflect https://kotlinlang.org/docs/coding-conventions.html. */
+ @JvmField
+ val KOTLINLANG_FORMAT = FormattingOptions(style = GOOGLE, blockIndent = 4, continuationIndent = 4)
+
+ @JvmField
+ val DROPBOX_FORMAT = FormattingOptions(style = DROPBOX, blockIndent = 4, continuationIndent = 4)
+
+ private val MINIMUM_KOTLIN_VERSION = KotlinVersion(1, 4)
+
+ /**
+ * format formats the Kotlin code given in 'code' and returns it as a string. This method is
+ * accessed through Reflection.
+ */
+ @JvmStatic
+ @Throws(FormatterException::class, ParseError::class)
+ fun format(code: String): String = format(FormattingOptions(), code)
+
+ /**
+ * format formats the Kotlin code given in 'code' with 'removeUnusedImports' and returns it as a
+ * string. This method is accessed through Reflection.
+ */
+ @JvmStatic
+ @Throws(FormatterException::class, ParseError::class)
+ fun format(code: String, removeUnusedImports: Boolean): String =
+ format(FormattingOptions(removeUnusedImports = removeUnusedImports), code)
+
+ /**
+ * format formats the Kotlin code given in 'code' with the 'maxWidth' and returns it as a string.
+ */
+ @JvmStatic
+ @Throws(FormatterException::class, ParseError::class)
+ fun format(options: FormattingOptions, code: String): String {
+ val (shebang, kotlinCode) =
+ if (code.startsWith("#!")) {
+ code.split("\n".toRegex(), limit = 2)
+ } else {
+ listOf("", code)
+ }
+ checkEscapeSequences(kotlinCode)
+
+ val lfCode = StringUtilRt.convertLineSeparators(kotlinCode)
+ val sortedImports = sortedAndDistinctImports(lfCode)
+ val pretty = prettyPrint(sortedImports, options, "\n")
+ val noRedundantElements =
+ try {
+ dropRedundantElements(pretty, options)
+ } catch (e: ParseError) {
+ throw IllegalStateException("Failed to re-parse code after pretty printing:\n $pretty", e)
+ }
+ val prettyCode =
+ prettyPrint(noRedundantElements, options, Newlines.guessLineSeparator(kotlinCode)!!)
+ return if (shebang.isNotEmpty()) shebang + "\n" + prettyCode else prettyCode
+ }
+
+ /** prettyPrint reflows 'code' using google-java-format's engine. */
+ private fun prettyPrint(code: String, options: FormattingOptions, lineSeparator: String): String {
+ val file = Parser.parse(code)
+ val kotlinInput = KotlinInput(code, file)
+ val javaOutput =
+ JavaOutput(lineSeparator, kotlinInput, KDocCommentsHelper(lineSeparator, options.maxWidth))
+ val builder = OpsBuilder(kotlinInput, javaOutput)
+ file.accept(createAstVisitor(options, builder))
+ builder.sync(kotlinInput.text.length)
+ builder.drain()
+ val ops = builder.build()
+ if (options.debuggingPrintOpsAfterFormatting) {
+ printOps(ops)
+ }
+ val doc = DocBuilder().withOps(ops).build()
+ doc.computeBreaks(javaOutput.commentsHelper, options.maxWidth, Doc.State(+0, 0))
+ doc.write(javaOutput)
+ javaOutput.flush()
+
+ val tokenRangeSet =
+ kotlinInput.characterRangesToTokenRanges(ImmutableList.of(Range.closedOpen(0, code.length)))
+ return WhitespaceTombstones.replaceTombstoneWithTrailingWhitespace(
+ JavaOutput.applyReplacements(code, javaOutput.getFormatReplacements(tokenRangeSet)))
+ }
+
+ private fun createAstVisitor(options: FormattingOptions, builder: OpsBuilder): PsiElementVisitor {
+ if (KotlinVersion.CURRENT < MINIMUM_KOTLIN_VERSION) {
+ throw RuntimeException("Unsupported runtime Kotlin version: " + KotlinVersion.CURRENT)
+ }
+ return KotlinInputAstVisitor(options, builder)
+ }
+
+ private fun checkEscapeSequences(code: String) {
+ var index = code.indexOfWhitespaceTombstone()
+ if (index == -1) {
+ index = Escaping.indexOfCommentEscapeSequences(code)
+ }
+ if (index != -1) {
+ throw ParseError(
+ "ktfmt does not support code which contains one of {\\u0003, \\u0004, \\u0005} character" +
+ "; escape it",
+ StringUtil.offsetToLineColumn(code, index))
+ }
+ }
+
+ private fun sortedAndDistinctImports(code: String): String {
+ val file = Parser.parse(code)
+
+ val importList = file.importList ?: return code
+ if (importList.imports.isEmpty()) {
+ return code
+ }
+
+ val commentList = mutableListOf<PsiElement>()
+ // Find non-import elements; comments are moved, in order, to the top of the import list. Other
+ // non-import elements throw a ParseError.
+ var element = importList.firstChild
+ while (element != null) {
+ if (element is PsiComment) {
+ commentList.add(element)
+ } else if (element !is KtImportDirective && element !is PsiWhiteSpace) {
+ throw ParseError(
+ "Imports not contiguous: " + element.text,
+ StringUtil.offsetToLineColumn(code, element.startOffset))
+ }
+ element = element.nextSibling
+ }
+ fun canonicalText(importDirective: KtImportDirective) =
+ importDirective.importedFqName?.asString() +
+ " " +
+ importDirective.alias?.text?.replace("`", "") +
+ " " +
+ if (importDirective.isAllUnder) "*" else ""
+
+ val sortedImports = importList.imports.sortedBy(::canonicalText).distinctBy(::canonicalText)
+ val importsWithComments = commentList + sortedImports
+
+ return code.replaceRange(
+ importList.startOffset,
+ importList.endOffset,
+ importsWithComments.joinToString(separator = "\n") { imprt -> imprt.text } + "\n")
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/FormattingOptions.kt b/core/src/main/java/com/facebook/ktfmt/format/FormattingOptions.kt
new file mode 100644
index 0000000..791cde3
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/FormattingOptions.kt
@@ -0,0 +1,68 @@
+/*
+ * 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
+
+data class FormattingOptions(
+ val style: Style = Style.FACEBOOK,
+
+ /** ktfmt breaks lines longer than maxWidth. */
+ val maxWidth: Int = DEFAULT_MAX_WIDTH,
+
+ /**
+ * blockIndent is the size of the indent used when a new block is opened, in spaces.
+ *
+ * For example,
+ * ```
+ * fun f() {
+ * //
+ * }
+ * ```
+ */
+ val blockIndent: Int = 2,
+
+ /**
+ * continuationIndent is the size of the indent used when a line is broken because it's too
+ * long, in spaces.
+ *
+ * For example,
+ * ```
+ * val foo = bar(
+ * 1)
+ * ```
+ */
+ val continuationIndent: Int = 4,
+
+ /** Whether ktfmt should remove imports that are not used. */
+ val removeUnusedImports: Boolean = true,
+
+ /**
+ * Print the Ops generated by KotlinInputAstVisitor to help reason about formatting (i.e.,
+ * newline) decisions
+ */
+ val debuggingPrintOpsAfterFormatting: Boolean = false
+) {
+
+ companion object {
+ const val DEFAULT_MAX_WIDTH: Int = 100
+ }
+
+ enum class Style {
+ FACEBOOK,
+ DROPBOX,
+ GOOGLE
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/KotlinInput.kt b/core/src/main/java/com/facebook/ktfmt/format/KotlinInput.kt
new file mode 100644
index 0000000..52b01a4
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/KotlinInput.kt
@@ -0,0 +1,231 @@
+/*
+ * 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.DiscreteDomain
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.ImmutableMap
+import com.google.common.collect.ImmutableRangeMap
+import com.google.common.collect.Iterables.getLast
+import com.google.common.collect.Range
+import com.google.common.collect.RangeSet
+import com.google.common.collect.TreeRangeSet
+import com.google.googlejavaformat.Input
+import com.google.googlejavaformat.Newlines
+import com.google.googlejavaformat.java.FormatterException
+import com.google.googlejavaformat.java.JavaOutput
+import java.util.LinkedHashMap
+import org.jetbrains.kotlin.com.intellij.openapi.util.text.StringUtil
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtFile
+
+// TODO: share the code with JavaInput instead of copy-pasting here.
+/**
+ * KotlinInput is for Kotlin what JavaInput is for Java.
+ *
+ * <p>KotlinInput is duplicating most of JavaInput's code, but uses the Kotlin compiler as a lexer
+ * instead of Javac. This is required because some valid Kotlin programs are not valid Java
+ * programs, e.g., "a..b".
+ *
+ * <p>See javadoc for JavaInput.
+ */
+class KotlinInput(private val text: String, file: KtFile) : Input() {
+ private val tokens: ImmutableList<Token> // The Tokens for this input.
+ private val positionToColumnMap: ImmutableMap<Int, Int> // Map Tok position to column.
+ private val positionTokenMap: ImmutableRangeMap<Int, Token> // Map position to Token.
+ private var kN = 0 // The number of numbered toks (tokens or comments), excluding the EOF.
+ private val kToToken: Array<Token?>
+
+ init {
+ setLines(ImmutableList.copyOf(Newlines.lineIterator(text)))
+ val toks = buildToks(file, text)
+ positionToColumnMap = makePositionToColumnMap(toks)
+ tokens = buildTokens(toks)
+ val tokenLocations = ImmutableRangeMap.builder<Int, Token>()
+ for (token in tokens) {
+ val end = JavaOutput.endTok(token)
+ var upper = end.position
+ if (end.text.isNotEmpty()) {
+ upper += end.length() - 1
+ }
+ tokenLocations.put(Range.closed(JavaOutput.startTok(token).position, upper), token)
+ }
+ positionTokenMap = tokenLocations.build()
+
+ // adjust kN for EOF
+ kToToken = arrayOfNulls(kN + 1)
+ for (token in tokens) {
+ for (tok in token.toksBefore) {
+ if (tok.index < 0) {
+ continue
+ }
+ kToToken[tok.index] = token
+ }
+ kToToken[token.tok.index] = token
+ for (tok in token.toksAfter) {
+ if (tok.index < 0) {
+ continue
+ }
+ kToToken[tok.index] = token
+ }
+ }
+ }
+
+ @Throws(FormatterException::class)
+ fun characterRangesToTokenRanges(characterRanges: Collection<Range<Int>>): RangeSet<Int> {
+ val tokenRangeSet = TreeRangeSet.create<Int>()
+ for (characterRange0 in characterRanges) {
+ val characterRange = characterRange0.canonical(DiscreteDomain.integers())
+ tokenRangeSet.add(
+ characterRangeToTokenRange(
+ characterRange.lowerEndpoint(),
+ characterRange.upperEndpoint() - characterRange.lowerEndpoint()))
+ }
+ return tokenRangeSet
+ }
+
+ /**
+ * Convert from an offset and length flag pair to a token range.
+ *
+ * @param offset the `0`-based offset in characters
+ * @param length the length in characters
+ * @return the `0`-based [Range] of tokens
+ * @throws FormatterException
+ */
+ @Throws(FormatterException::class)
+ internal fun characterRangeToTokenRange(offset: Int, length: Int): Range<Int> {
+ val requiredLength = offset + length
+ if (requiredLength > text.length) {
+ throw FormatterException(
+ String.format(
+ "error: invalid length %d, offset + length (%d) is outside the file",
+ length,
+ requiredLength))
+ }
+ val expandedLength =
+ when {
+ length < 0 -> return EMPTY_RANGE
+ length == 0 -> 1 // 0 stands for "format the line under the cursor"
+ else -> length
+ }
+ val enclosed =
+ getPositionTokenMap()
+ .subRangeMap(Range.closedOpen(offset, offset + expandedLength))
+ .asMapOfRanges()
+ .values
+ return if (enclosed.isEmpty()) {
+ EMPTY_RANGE
+ } else
+ Range.closedOpen(
+ enclosed.iterator().next().tok.index, getLast(enclosed).getTok().getIndex() + 1)
+ }
+
+ private fun makePositionToColumnMap(toks: List<KotlinTok>): ImmutableMap<Int, Int> {
+ val builder = LinkedHashMap<Int, Int>()
+ for (tok in toks) {
+ builder.put(tok.position, tok.column)
+ }
+ return ImmutableMap.copyOf(builder)
+ }
+
+ private fun buildToks(file: KtFile, fileText: String): ImmutableList<KotlinTok> {
+ val tokenizer = Tokenizer(fileText, file)
+ file.accept(tokenizer)
+ val toks = tokenizer.toks
+ toks.add(KotlinTok(tokenizer.index, "", "", fileText.length, 0, true, KtTokens.EOF))
+ kN = tokenizer.index
+ computeRanges(toks)
+ return ImmutableList.copyOf(toks)
+ }
+
+ private fun buildTokens(toks: List<KotlinTok>): ImmutableList<Token> {
+ val tokens = ImmutableList.builder<Token>()
+ var k = 0
+ val kN = toks.size
+
+ // Remaining non-tokens before the token go here.
+ var toksBefore: ImmutableList.Builder<KotlinTok> = ImmutableList.builder()
+
+ OUTERMOST@ while (k < kN) {
+ while (!toks[k].isToken) {
+ val tok = toks[k++]
+ toksBefore.add(tok)
+ if (isParamComment(tok)) {
+ while (toks[k].isNewline) {
+ // drop newlines after parameter comments
+ k++
+ }
+ }
+ }
+ val tok = toks[k++]
+
+ // Non-tokens starting on the same line go here too.
+ val toksAfter = ImmutableList.builder<KotlinTok>()
+ OUTER@ while (k < kN && !toks[k].isToken) {
+ // Don't attach inline comments to certain leading tokens, e.g. for `f(/*flag1=*/true).
+ //
+ // Attaching inline comments to the right token is hard, and this barely
+ // scratches the surface. But it's enough to do a better job with parameter
+ // name comments.
+ //
+ // TODO(cushon): find a better strategy.
+ if (toks[k].isSlashStarComment && (tok.text == "(" || tok.text == "<" || tok.text == "."))
+ break@OUTER
+ if (toks[k].isJavadocComment && tok.text == ";") break@OUTER
+ if (isParamComment(toks[k])) {
+ tokens.add(KotlinToken(toksBefore.build(), tok, toksAfter.build()))
+ toksBefore = ImmutableList.builder<KotlinTok>().add(toks[k++])
+ // drop newlines after parameter comments
+ while (toks[k].isNewline) {
+ k++
+ }
+ continue@OUTERMOST
+ }
+ val nonTokenAfter = toks[k++]
+ toksAfter.add(nonTokenAfter)
+ if (Newlines.containsBreaks(nonTokenAfter.text)) {
+ break
+ }
+ }
+ tokens.add(KotlinToken(toksBefore.build(), tok, toksAfter.build()))
+ toksBefore = ImmutableList.builder()
+ }
+ return tokens.build()
+ }
+
+ private fun isParamComment(tok: Tok): Boolean {
+ return tok.isSlashStarComment && tok.text.matches("/\\*[A-Za-z0-9\\s_\\-]+=\\s*\\*/".toRegex())
+ }
+
+ override fun getkN(): Int = kN
+
+ override fun getToken(k: Int): Token? = kToToken[k]
+
+ override fun getTokens(): ImmutableList<out Token> = tokens
+
+ override fun getPositionTokenMap(): ImmutableRangeMap<Int, out Token> = positionTokenMap
+
+ override fun getPositionToColumnMap(): ImmutableMap<Int, Int> = positionToColumnMap
+
+ override fun getText(): String = text
+
+ override fun getLineNumber(inputPosition: Int) =
+ StringUtil.offsetToLineColumn(text, inputPosition).line + 1
+
+ override fun getColumnNumber(inputPosition: Int) =
+ StringUtil.offsetToLineColumn(text, inputPosition).column
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt b/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt
new file mode 100644
index 0000000..358fe50
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt
@@ -0,0 +1,2447 @@
+/*
+ * 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.base.Throwables
+import com.google.common.collect.ImmutableList
+import com.google.googlejavaformat.Doc
+import com.google.googlejavaformat.FormattingError
+import com.google.googlejavaformat.Indent
+import com.google.googlejavaformat.Indent.Const.ZERO
+import com.google.googlejavaformat.OpsBuilder
+import com.google.googlejavaformat.Output
+import com.google.googlejavaformat.Output.BreakTag
+import java.util.ArrayDeque
+import java.util.Optional
+import org.jetbrains.kotlin.com.intellij.psi.PsiComment
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace
+import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtAnnotatedExpression
+import org.jetbrains.kotlin.psi.KtAnnotation
+import org.jetbrains.kotlin.psi.KtAnnotationEntry
+import org.jetbrains.kotlin.psi.KtAnnotationUseSiteTarget
+import org.jetbrains.kotlin.psi.KtArrayAccessExpression
+import org.jetbrains.kotlin.psi.KtBinaryExpression
+import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
+import org.jetbrains.kotlin.psi.KtBlockExpression
+import org.jetbrains.kotlin.psi.KtBreakExpression
+import org.jetbrains.kotlin.psi.KtCallExpression
+import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
+import org.jetbrains.kotlin.psi.KtCatchClause
+import org.jetbrains.kotlin.psi.KtClass
+import org.jetbrains.kotlin.psi.KtClassInitializer
+import org.jetbrains.kotlin.psi.KtClassLiteralExpression
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
+import org.jetbrains.kotlin.psi.KtConstantExpression
+import org.jetbrains.kotlin.psi.KtConstructorDelegationCall
+import org.jetbrains.kotlin.psi.KtContainerNode
+import org.jetbrains.kotlin.psi.KtContinueExpression
+import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
+import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
+import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry
+import org.jetbrains.kotlin.psi.KtDoWhileExpression
+import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
+import org.jetbrains.kotlin.psi.KtDynamicType
+import org.jetbrains.kotlin.psi.KtElement
+import org.jetbrains.kotlin.psi.KtEnumEntry
+import org.jetbrains.kotlin.psi.KtExpression
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtFileAnnotationList
+import org.jetbrains.kotlin.psi.KtFinallySection
+import org.jetbrains.kotlin.psi.KtForExpression
+import org.jetbrains.kotlin.psi.KtFunctionType
+import org.jetbrains.kotlin.psi.KtIfExpression
+import org.jetbrains.kotlin.psi.KtImportDirective
+import org.jetbrains.kotlin.psi.KtImportList
+import org.jetbrains.kotlin.psi.KtIsExpression
+import org.jetbrains.kotlin.psi.KtLabelReferenceExpression
+import org.jetbrains.kotlin.psi.KtLabeledExpression
+import org.jetbrains.kotlin.psi.KtLambdaArgument
+import org.jetbrains.kotlin.psi.KtLambdaExpression
+import org.jetbrains.kotlin.psi.KtModifierList
+import org.jetbrains.kotlin.psi.KtNamedFunction
+import org.jetbrains.kotlin.psi.KtNullableType
+import org.jetbrains.kotlin.psi.KtPackageDirective
+import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.KtParameterList
+import org.jetbrains.kotlin.psi.KtParenthesizedExpression
+import org.jetbrains.kotlin.psi.KtPostfixExpression
+import org.jetbrains.kotlin.psi.KtPrefixExpression
+import org.jetbrains.kotlin.psi.KtPrimaryConstructor
+import org.jetbrains.kotlin.psi.KtProjectionKind
+import org.jetbrains.kotlin.psi.KtProperty
+import org.jetbrains.kotlin.psi.KtPropertyAccessor
+import org.jetbrains.kotlin.psi.KtPropertyDelegate
+import org.jetbrains.kotlin.psi.KtQualifiedExpression
+import org.jetbrains.kotlin.psi.KtReferenceExpression
+import org.jetbrains.kotlin.psi.KtReturnExpression
+import org.jetbrains.kotlin.psi.KtScript
+import org.jetbrains.kotlin.psi.KtSecondaryConstructor
+import org.jetbrains.kotlin.psi.KtSimpleNameExpression
+import org.jetbrains.kotlin.psi.KtStringTemplateExpression
+import org.jetbrains.kotlin.psi.KtSuperExpression
+import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry
+import org.jetbrains.kotlin.psi.KtSuperTypeList
+import org.jetbrains.kotlin.psi.KtThisExpression
+import org.jetbrains.kotlin.psi.KtThrowExpression
+import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
+import org.jetbrains.kotlin.psi.KtTryExpression
+import org.jetbrains.kotlin.psi.KtTypeAlias
+import org.jetbrains.kotlin.psi.KtTypeArgumentList
+import org.jetbrains.kotlin.psi.KtTypeConstraint
+import org.jetbrains.kotlin.psi.KtTypeConstraintList
+import org.jetbrains.kotlin.psi.KtTypeParameter
+import org.jetbrains.kotlin.psi.KtTypeParameterList
+import org.jetbrains.kotlin.psi.KtTypeProjection
+import org.jetbrains.kotlin.psi.KtTypeReference
+import org.jetbrains.kotlin.psi.KtUserType
+import org.jetbrains.kotlin.psi.KtValueArgument
+import org.jetbrains.kotlin.psi.KtValueArgumentList
+import org.jetbrains.kotlin.psi.KtWhenConditionInRange
+import org.jetbrains.kotlin.psi.KtWhenConditionIsPattern
+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.startOffset
+import org.jetbrains.kotlin.psi.psiUtil.startsWithComment
+
+/** An AST visitor that builds a stream of {@link Op}s to format. */
+class KotlinInputAstVisitor(
+ private val options: FormattingOptions,
+ private val builder: OpsBuilder
+) : KtTreeVisitorVoid() {
+
+ private val isGoogleStyle = options.style == FormattingOptions.Style.GOOGLE
+
+ /** Standard indentation for a block */
+ private val blockIndent: Indent.Const = Indent.Const.make(options.blockIndent, 1)
+
+ /**
+ * Standard indentation for a long expression or function call, it is different than block
+ * indentation on purpose
+ */
+ private val expressionBreakIndent: Indent.Const = Indent.Const.make(options.continuationIndent, 1)
+
+ private val blockPlusExpressionBreakIndent: Indent.Const =
+ Indent.Const.make(options.blockIndent + options.continuationIndent, 1)
+
+ private val doubleExpressionBreakIndent: Indent.Const =
+ Indent.Const.make(options.continuationIndent, 2)
+
+ private val expressionBreakNegativeIndent: Indent.Const =
+ Indent.Const.make(-options.continuationIndent, 1)
+
+ /** A record of whether we have visited into an expression. */
+ private val inExpression = ArrayDeque(ImmutableList.of(false))
+
+ /** Tracks whether we are handling an import directive */
+ private var inImport = false
+
+ /** Example: `fun foo(n: Int) { println(n) }` */
+ override fun visitNamedFunction(function: KtNamedFunction) {
+ builder.sync(function)
+ builder.block(ZERO) {
+ visitFunctionLikeExpression(
+ function.modifierList,
+ "fun",
+ function.typeParameterList,
+ function.receiverTypeReference,
+ function.nameIdentifier?.text,
+ true,
+ function.valueParameterList,
+ function.typeConstraintList,
+ function.bodyBlockExpression,
+ function.bodyExpression,
+ function.typeReference,
+ function.bodyBlockExpression?.lBrace != null)
+ }
+ }
+
+ /** Example `Int`, `(String)` or `() -> Int` */
+ override fun visitTypeReference(typeReference: KtTypeReference) {
+ builder.sync(typeReference)
+ // Normally we'd visit the children nodes through accessors on 'typeReference', and we wouldn't
+ // loop over children.
+ // But, in this case the modifier list can either be inside the parenthesis:
+ // ... (@Composable (x) -> Unit)
+ // or outside of them:
+ // ... @Composable ((x) -> Unit)
+ val modifierList = typeReference.modifierList
+ val typeElement = typeReference.typeElement
+ for (child in typeReference.node.children()) {
+ when {
+ child.psi == modifierList -> visit(modifierList)
+ child.psi == typeElement -> visit(typeElement)
+ child.elementType == KtTokens.LPAR -> builder.token("(")
+ child.elementType == KtTokens.RPAR -> builder.token(")")
+ }
+ }
+ }
+
+ override fun visitDynamicType(type: KtDynamicType) {
+ builder.token("dynamic")
+ }
+
+ /** Example: `String?` or `((Int) -> Unit)?` */
+ override fun visitNullableType(nullableType: KtNullableType) {
+ builder.sync(nullableType)
+ val innerType = nullableType.innerType
+ val addParenthesis = innerType is KtFunctionType
+ if (addParenthesis) {
+ builder.token("(")
+ }
+ visit(nullableType.modifierList)
+ visit(innerType)
+ if (addParenthesis) {
+ builder.token(")")
+ }
+ builder.token("?")
+ }
+
+ /** Example: `String` or `List<Int>`, */
+ override fun visitUserType(type: KtUserType) {
+ builder.sync(type)
+
+ if (type.qualifier != null) {
+ visit(type.qualifier)
+ builder.token(".")
+ }
+ visit(type.referenceExpression)
+ val typeArgumentList = type.typeArgumentList
+ if (typeArgumentList != null) {
+ builder.block(expressionBreakIndent) { visit(typeArgumentList) }
+ }
+ }
+
+ /** Example `<Int, String>` in `List<Int, String>` */
+ override fun visitTypeArgumentList(typeArgumentList: KtTypeArgumentList) {
+ builder.sync(typeArgumentList)
+ builder.block(ZERO) {
+ builder.token("<")
+ builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+ builder.block(ZERO) {
+ emitParameterLikeList(
+ typeArgumentList.arguments, typeArgumentList.trailingComma != null, wrapInBlock = true)
+ }
+ }
+ builder.token(">")
+ }
+
+ override fun visitTypeProjection(typeProjection: KtTypeProjection) {
+ builder.sync(typeProjection)
+ val typeReference = typeProjection.typeReference
+ when (typeProjection.projectionKind) {
+ KtProjectionKind.IN -> {
+ builder.token("in")
+ builder.space()
+ visit(typeReference)
+ }
+ KtProjectionKind.OUT -> {
+ builder.token("out")
+ builder.space()
+ visit(typeReference)
+ }
+ KtProjectionKind.STAR -> builder.token("*")
+ KtProjectionKind.NONE -> visit(typeReference)
+ }
+ }
+
+ /**
+ * @param keyword e.g., "fun" or "class".
+ * @param typeOrDelegationCall for functions, the return typeOrDelegationCall; for classes, the
+ * list of supertypes.
+ */
+ private fun visitFunctionLikeExpression(
+ modifierList: KtModifierList?,
+ keyword: String,
+ typeParameters: KtTypeParameterList?,
+ receiverTypeReference: KtTypeReference?,
+ name: String?,
+ emitParenthesis: Boolean,
+ parameterList: KtParameterList?,
+ typeConstraintList: KtTypeConstraintList?,
+ bodyBlockExpression: KtBlockExpression?,
+ nonBlockBodyExpressions: KtExpression?,
+ typeOrDelegationCall: KtElement?,
+ emitBraces: Boolean
+ ) {
+ builder.block(ZERO) {
+ if (modifierList != null) {
+ visitModifierList(modifierList)
+ }
+ builder.token(keyword)
+ if (typeParameters != null) {
+ builder.space()
+ builder.block(ZERO) { visit(typeParameters) }
+ }
+
+ if (name != null || receiverTypeReference != null) {
+ builder.space()
+ }
+ builder.block(ZERO) {
+ if (receiverTypeReference != null) {
+ visit(receiverTypeReference)
+ builder.breakOp(Doc.FillMode.INDEPENDENT, "", expressionBreakIndent)
+ builder.token(".")
+ }
+ if (name != null) {
+ builder.token(name)
+ }
+ }
+ if (emitParenthesis) {
+ builder.token("(")
+ }
+ var paramBlockNeedsClosing = false
+ builder.block(ZERO) {
+ if (parameterList != null && parameterList.parameters.isNotEmpty()) {
+ paramBlockNeedsClosing = true
+ builder.open(expressionBreakIndent)
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+ visit(parameterList)
+ }
+ if (emitParenthesis) {
+ if (parameterList != null && parameterList.parameters.isNotEmpty()) {
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+ }
+ builder.token(")")
+ } else {
+ if (paramBlockNeedsClosing) {
+ builder.close()
+ }
+ }
+ if (typeOrDelegationCall != null) {
+ builder.block(ZERO) {
+ if (typeOrDelegationCall is KtConstructorDelegationCall) {
+ builder.space()
+ }
+ builder.token(":")
+ if (parameterList?.parameters.isNullOrEmpty()) {
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+ builder.block(expressionBreakIndent) { visit(typeOrDelegationCall) }
+ } else {
+ builder.space()
+ builder.block(expressionBreakNegativeIndent) { visit(typeOrDelegationCall) }
+ }
+ }
+ }
+ }
+ if (paramBlockNeedsClosing) {
+ builder.close()
+ }
+ if (typeConstraintList != null) {
+ builder.space()
+ visit(typeConstraintList)
+ }
+ if (bodyBlockExpression != null) {
+ builder.space()
+ visitBlockBody(bodyBlockExpression, emitBraces)
+ } else if (nonBlockBodyExpressions != null) {
+ builder.space()
+ builder.block(ZERO) {
+ builder.token("=")
+ if (isLambdaOrScopingFunction(nonBlockBodyExpressions)) {
+ visitLambdaOrScopingFunction(nonBlockBodyExpressions)
+ } else {
+ builder.block(expressionBreakIndent) {
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
+ builder.block(ZERO) { visit(nonBlockBodyExpressions) }
+ }
+ }
+ }
+ }
+ builder.guessToken(";")
+ }
+ if (name != null) {
+ builder.forcedBreak()
+ }
+ }
+
+ private fun genSym(): Output.BreakTag {
+ return Output.BreakTag()
+ }
+
+ private fun visitBlockBody(bodyBlockExpression: PsiElement, emitBraces: Boolean) {
+ if (emitBraces) {
+ builder.token("{", Doc.Token.RealOrImaginary.REAL, blockIndent, Optional.of(blockIndent))
+ }
+ val statements = bodyBlockExpression.children
+ if (statements.isNotEmpty()) {
+ builder.block(blockIndent) {
+ builder.forcedBreak()
+ builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
+ visitStatements(statements)
+ }
+ builder.forcedBreak()
+ builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO)
+ }
+ if (emitBraces) {
+ builder.token("}", blockIndent)
+ }
+ }
+
+ private fun visitStatement(statement: PsiElement) {
+ builder.block(ZERO) { visit(statement) }
+ builder.guessToken(";")
+ }
+
+ private fun visitStatements(statements: Array<PsiElement>) {
+ var first = true
+ builder.guessToken(";")
+ for (statement in statements) {
+ builder.forcedBreak()
+ if (!first) {
+ builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
+ }
+ first = false
+ visitStatement(statement)
+ }
+ }
+
+ override fun visitProperty(property: KtProperty) {
+ builder.sync(property)
+ builder.block(ZERO) {
+ declareOne(
+ kind = DeclarationKind.FIELD,
+ modifiers = property.modifierList,
+ valOrVarKeyword = property.valOrVarKeyword.text,
+ typeParameters = property.typeParameterList,
+ receiver = property.receiverTypeReference,
+ name = property.nameIdentifier?.text,
+ type = property.typeReference,
+ typeConstraintList = property.typeConstraintList,
+ delegate = property.delegate,
+ initializer = property.initializer,
+ accessors = property.accessors)
+ }
+ builder.guessToken(";")
+ if (property.parent !is KtWhenExpression) {
+ builder.forcedBreak()
+ }
+ }
+
+ /**
+ * Example: "com.facebook.bla.bla" in imports or "a.b.c.d" in expressions.
+ *
+ * There's a few cases that are different. We deal with imports by keeping them on the same line.
+ * For regular chained expressions we go the left most descendant so we can start indentation only
+ * before the first break (a `.` or `?.`), and keep the seem indentation for this chain of calls.
+ */
+ override fun visitQualifiedExpression(expression: KtQualifiedExpression) {
+ builder.sync(expression)
+ val receiver = expression.receiverExpression
+ when {
+ inImport -> {
+ visit(receiver)
+ val selectorExpression = expression.selectorExpression
+ if (selectorExpression != null) {
+ builder.token(".")
+ visit(selectorExpression)
+ }
+ }
+ receiver is KtWhenExpression || receiver is KtStringTemplateExpression -> {
+ builder.block(ZERO) {
+ visit(receiver)
+ builder.token(expression.operationSign.value)
+ visit(expression.selectorExpression)
+ }
+ }
+ else -> {
+ emitQualifiedExpression(expression)
+ }
+ }
+ }
+
+ /** Extra data to help [emitQualifiedExpression] know when to open and close a group */
+ private class GroupingInfo {
+ var groupOpenCount = 0
+ var shouldCloseGroup = false
+ }
+
+ /**
+ * Handles a chain of qualified expressions, i.e. `a[5].b!!.c()[4].f()`
+ *
+ * This is by far the most complicated part of this formatter. We start by breaking the expression
+ * to the steps it is executed in (which are in the opposite order of how the syntax tree is
+ * built).
+ *
+ * We then calculate information to know which parts need to be groups, and finally go part by
+ * part, emitting it to the [builder] while closing and opening groups.
+ */
+ private fun emitQualifiedExpression(expression: KtExpression) {
+ val parts = breakIntoParts(expression)
+ // whether we want to make a lambda look like a block, this make Kotlin DSLs look as expected
+ val useBlockLikeLambdaStyle = parts.last().isLambda() && parts.count { it.isLambda() } == 1
+ val groupingInfos = computeGroupingInfo(parts, useBlockLikeLambdaStyle)
+ builder.block(expressionBreakIndent) {
+ val nameTag = genSym() // allows adjusting arguments indentation if a break will be made
+ for ((index, ktExpression) in parts.withIndex()) {
+ if (ktExpression is KtQualifiedExpression) {
+ builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO, Optional.of(nameTag))
+ }
+ repeat(groupingInfos[index].groupOpenCount) { builder.open(ZERO) }
+ when (ktExpression) {
+ is KtQualifiedExpression -> {
+ builder.token(ktExpression.operationSign.value)
+ val selectorExpression = ktExpression.selectorExpression
+ if (selectorExpression !is KtCallExpression) {
+ // selector is a simple field access
+ visit(selectorExpression)
+ if (groupingInfos[index].shouldCloseGroup) {
+ builder.close()
+ }
+ } else {
+ // selector is a function call, we may close a group after its name
+ // emit `doIt` from `doIt(1, 2) { it }`
+ visit(selectorExpression.calleeExpression)
+ // close groups according to instructions
+ if (groupingInfos[index].shouldCloseGroup) {
+ builder.close()
+ }
+ // close group due to last lambda to allow block-like style in `as.forEach { ... }`
+ val isTrailingLambda = useBlockLikeLambdaStyle && index == parts.size - 1
+ if (isTrailingLambda) {
+ builder.close()
+ }
+ val argsIndentElse = if (index == parts.size - 1) ZERO else expressionBreakIndent
+ val lambdaIndentElse = if (isTrailingLambda) expressionBreakNegativeIndent else ZERO
+
+ // emit `(1, 2) { it }` from `doIt(1, 2) { it }`
+ visitCallElement(
+ null,
+ selectorExpression.typeArgumentList,
+ selectorExpression.valueArgumentList,
+ selectorExpression.lambdaArguments,
+ argumentsIndent = Indent.If.make(nameTag, expressionBreakIndent, argsIndentElse),
+ lambdaIndent = Indent.If.make(nameTag, ZERO, lambdaIndentElse),
+ )
+ }
+ }
+ is KtArrayAccessExpression -> {
+ visitArrayAccessBrackets(ktExpression)
+ builder.close()
+ }
+ is KtPostfixExpression -> {
+ builder.token(ktExpression.operationReference.text)
+ builder.close()
+ }
+ else -> {
+ check(index == 0)
+ visit(ktExpression)
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Decomposes a qualified expression into parts, so `rainbow.red.orange.yellow` becomes `[rainbow,
+ * rainbow.red, rainbow.red.orange, rainbow.orange.yellow]`
+ */
+ private fun breakIntoParts(expression: KtExpression): List<KtExpression> {
+ val parts = ArrayDeque<KtExpression>()
+
+ // use an ArrayDeque and add elements to the beginning so the innermost expression comes first
+ // foo.bar.yay -> [yay, bar.yay, foo.bar.yay]
+
+ var node: KtExpression? = expression
+ while (node != null) {
+ parts.addFirst(node)
+ node =
+ when (node) {
+ is KtQualifiedExpression -> node.receiverExpression
+ is KtArrayAccessExpression -> node.arrayExpression
+ is KtPostfixExpression -> node.baseExpression
+ else -> null
+ }
+ }
+
+ return parts.toList()
+ }
+
+ /**
+ * Generates the [GroupingInfo] array to go with an array of [KtQualifiedExpression] parts
+ *
+ * For example, the expression `a.b[2].c.d()` is made of four expressions:
+ * 1. [KtQualifiedExpression] `a.b[2].c . d()` (this will be `parts[4]`)
+ * 1. [KtQualifiedExpression] `a.b[2] . c` (this will be `parts[3]`)
+ * 2. [KtArrayAccessExpression] `a.b [2]` (this will be `parts[2]`)
+ * 3. [KtQualifiedExpression] `a . b` (this will be `parts[1]`)
+ * 4. [KtSimpleNameExpression] `a` (this will be `parts[0]`)
+ *
+ * Once in parts, these are in the reverse order. To render the array correct we need to make sure
+ * `b` and [2] are in a group so we avoid splitting them. To do so we need to open a group for `b`
+ * (that will be done in part 2), and always close a group for an array.
+ *
+ * Here is the same expression, with justified braces marking the groupings it will get:
+ * ```
+ * a . b [2] . c . d ()
+ * {a . b} --> Grouping `a.b` because it can be a package name or simple field access so we add 1
+ * to the number of groups to open at groupingInfos[0], and mark to close a group at
+ * groupingInfos[1]
+ * {a . b [2]} --> Grouping `a.b` with `[2]`, since otherwise we may break inside the brackets
+ * instead of preferring breaks before dots. So we open a group at [0], but since
+ * we always close a group after brackets, we don't store that information.
+ * {c . d} --> another group to attach the first function name to the fields before it
+ * this time we don't start the group in the beginning, and use
+ * lastIndexToOpen to track the spot after the last time we stopped
+ * grouping.
+ * ```
+ * The final expression with groupings:
+ * ```
+ * {{a.b}[2]}.{c.d}()
+ * ```
+ */
+ private fun computeGroupingInfo(
+ parts: List<KtExpression>,
+ useBlockLikeLambdaStyle: Boolean
+ ): List<GroupingInfo> {
+ val groupingInfos = List(parts.size) { GroupingInfo() }
+ var lastIndexToOpen = 0
+ for ((index, part) in parts.withIndex()) {
+ when (part) {
+ is KtQualifiedExpression -> {
+ val receiverExpression = part.receiverExpression
+ val previous =
+ (receiverExpression as? KtQualifiedExpression)?.selectorExpression
+ ?: receiverExpression
+ val current = checkNotNull(part.selectorExpression)
+ if (lastIndexToOpen == 0 &&
+ shouldGroupPartWithPrevious(parts, part, index, previous, current)) {
+ // this and the previous items should be grouped for better style
+ // we add another group to open in index 0
+ groupingInfos[0].groupOpenCount++
+ // we don't always close a group when emitting this node, so we need this flag to
+ // mark if we need to close a group
+ groupingInfos[index].shouldCloseGroup = true
+ } else {
+ // use this index in to open future groups for arrays and postfixes
+ // we will also stop grouping field access to the beginning of the expression
+ lastIndexToOpen = index
+ }
+ }
+ is KtArrayAccessExpression,
+ is KtPostfixExpression -> {
+ // we group these with the last item with a name, and we always close them
+ groupingInfos[lastIndexToOpen].groupOpenCount++
+ }
+ }
+ }
+ if (useBlockLikeLambdaStyle) {
+ // a trailing lambda adds a group that we stop before emitting the lambda
+ groupingInfos[0].groupOpenCount++
+ }
+ return groupingInfos
+ }
+
+ /** Decide whether a [KtQualifiedExpression] part should be grouped with the previous part */
+ private fun shouldGroupPartWithPrevious(
+ parts: List<KtExpression>,
+ part: KtExpression,
+ index: Int,
+ previous: KtExpression,
+ current: KtExpression
+ ): Boolean {
+ // this is the second, and the first is short, avoid `.` "hanging in air"
+ if (index == 1 && previous.text.length < options.continuationIndent) {
+ return true
+ }
+ // the previous part is `this` or `super`
+ if (previous is KtSuperExpression || previous is KtThisExpression) {
+ return true
+ }
+ // this and the previous part are a package name, type name, or property
+ if (previous is KtSimpleNameExpression &&
+ current is KtSimpleNameExpression &&
+ part is KtDotQualifiedExpression) {
+ return true
+ }
+ // this is `Foo` in `com.facebook.Foo`, so everything before it is a package name
+ if (current.text.first().isUpperCase() &&
+ current is KtSimpleNameExpression &&
+ part is KtDotQualifiedExpression) {
+ return true
+ }
+ // this is the `foo()` in `com.facebook.Foo.foo()` or in `Foo.foo()`
+ if (current is KtCallExpression &&
+ (previous !is KtCallExpression) &&
+ previous.text?.firstOrNull()?.isUpperCase() == true) {
+ return true
+ }
+ // this is an invocation and the last item, and the previous it not, i.e. `a.b.c()`
+ // keeping it grouped and splitting the arguments makes `a.b(...)` feel like `aab()`
+ return current is KtCallExpression &&
+ previous !is KtCallExpression &&
+ index == parts.indices.last
+ }
+
+ /** Returns true if the expression represents an invocation that is also a lambda */
+ private fun KtExpression.isLambda(): Boolean {
+ return extractCallExpression(this)?.lambdaArguments?.isNotEmpty() ?: false
+ }
+
+ /**
+ * emitQualifiedExpression formats call expressions that are either part of a qualified
+ * expression, or standing alone. This method makes it easier to handle both cases uniformly.
+ */
+ private fun extractCallExpression(expression: KtExpression): KtCallExpression? {
+ val ktExpression = (expression as? KtQualifiedExpression)?.selectorExpression ?: expression
+ return ktExpression as? KtCallExpression
+ }
+
+ override fun visitCallExpression(callExpression: KtCallExpression) {
+ builder.sync(callExpression)
+ with(callExpression) {
+ visitCallElement(
+ calleeExpression,
+ typeArgumentList,
+ valueArgumentList,
+ lambdaArguments,
+ lambdaIndent = ZERO)
+ }
+ }
+
+ /** Examples `foo<T>(a, b)`, `foo(a)`, `boo()`, `super(a)` */
+ private fun visitCallElement(
+ callee: KtExpression?,
+ typeArgumentList: KtTypeArgumentList?,
+ argumentList: KtValueArgumentList?,
+ lambdaArguments: List<KtLambdaArgument>,
+ argumentsIndent: Indent = expressionBreakIndent,
+ lambdaIndent: Indent = ZERO
+ ) {
+ builder.block(ZERO) {
+ visit(callee)
+ val arguments = argumentList?.arguments.orEmpty()
+ builder.block(argumentsIndent) { visit(typeArgumentList) }
+ builder.block(argumentsIndent) {
+ if (argumentList != null) {
+ builder.token("(")
+ }
+ if (arguments.isNotEmpty()) {
+ if (isGoogleStyle) {
+ visit(argumentList)
+ val first = arguments.first()
+ if (arguments.size != 1 ||
+ first?.isNamed() != false ||
+ first.getArgumentExpression() !is KtLambdaExpression) {
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+ }
+ } else {
+ builder.block(ZERO) { visit(argumentList) }
+ }
+ }
+ if (argumentList != null) {
+ builder.token(")")
+ }
+ }
+ val hasTrailingComma = argumentList?.trailingComma != null
+ if (lambdaArguments.isNotEmpty()) {
+ builder.space()
+ builder.block(lambdaIndent) {
+ lambdaArguments.forEach {
+ visitArgumentInternal(it, forceBreakLambdaBody = hasTrailingComma)
+ }
+ }
+ }
+ }
+ }
+
+ /** Example (`1, "hi"`) in a function call */
+ override fun visitValueArgumentList(list: KtValueArgumentList) {
+ builder.sync(list)
+ val arguments = list.arguments
+ val isSingleUnnamedLambda =
+ arguments.size == 1 &&
+ arguments.first().getArgumentExpression() is KtLambdaExpression &&
+ arguments.first().getArgumentName() == null
+ if (isSingleUnnamedLambda) {
+ builder.block(expressionBreakNegativeIndent) {
+ visit(arguments.first())
+ if (list.trailingComma != null) {
+ builder.token(",")
+ }
+ }
+ } else {
+ // Break before args.
+ builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+ emitParameterLikeList(
+ list.arguments, list.trailingComma != null, wrapInBlock = !isGoogleStyle)
+ }
+ }
+
+ /** Example `{ 1 + 1 }` (as lambda) or `{ (x, y) -> x + y }` */
+ override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
+ visitLambdaExpressionInternal(lambdaExpression, brokeBeforeBrace = null, forceBreakBody = false)
+ }
+
+ /**
+ * 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:
+ * ```
+ * fun foo() =
+ * coroutineScope { x ->
+ * bar()
+ * car()
+ * }
+ * ```
+ * @param forceBreakBody if true, forces the lambda to be multi-line. Useful for call expressions
+ * where it would look weird for the lambda to be on one-line. For example, here we avoid
+ * one-lining `{ x = 0 }` since the parameters have a trailing comma:
+ * ```
+ * foo.bar(
+ * trailingComma,
+ * ) {
+ * x = 0
+ * }
+ * ```
+ */
+ private fun visitLambdaExpressionInternal(
+ lambdaExpression: KtLambdaExpression,
+ brokeBeforeBrace: BreakTag?,
+ forceBreakBody: Boolean,
+ ) {
+ builder.sync(lambdaExpression)
+
+ val valueParams = lambdaExpression.valueParameters
+ val hasParams = valueParams.isNotEmpty()
+ val statements = (lambdaExpression.bodyExpression ?: fail()).children
+ val hasStatements = statements.isNotEmpty()
+ val hasArrow = lambdaExpression.functionLiteral.arrow != null
+
+ fun ifBrokeBeforeBrace(onTrue: Indent, onFalse: Indent): Indent {
+ if (brokeBeforeBrace == null) return onFalse
+ return Indent.If.make(brokeBeforeBrace, onTrue, onFalse)
+ }
+
+ /**
+ * Enable correct formatting of the `fun foo() = scope {` syntax.
+ *
+ * We can't denote the lambda (+ scope function) as a block, since (for multiline lambdas) the
+ * rectangle rule would force the entire lambda onto a lower line. Instead, we conditionally
+ * indent all the interior levels of the lambda based on whether we had to break before the
+ * opening brace (or scope function). This mimics the look of a block when the break is taken.
+ *
+ * These conditional indents should not be used inside interior blocks, since that would apply
+ * the condition twice.
+ */
+ val bracePlusBlockIndent = ifBrokeBeforeBrace(blockPlusExpressionBreakIndent, blockIndent)
+ val bracePlusExpressionIndent =
+ ifBrokeBeforeBrace(doubleExpressionBreakIndent, expressionBreakIndent)
+ val bracePlusZeroIndent = ifBrokeBeforeBrace(expressionBreakIndent, ZERO)
+
+ builder.token("{")
+
+ if (hasParams || hasArrow) {
+ builder.space()
+ builder.block(bracePlusExpressionIndent) {
+ forEachCommaSeparated(valueParams) { it.accept(this) }
+ }
+ builder.block(bracePlusBlockIndent) {
+ if (lambdaExpression.functionLiteral.valueParameterList?.trailingComma != null) {
+ builder.token(",")
+ builder.forcedBreak()
+ } else if (hasParams) {
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
+ }
+ builder.token("->")
+ }
+ builder.breakOp(Doc.FillMode.UNIFIED, "", bracePlusZeroIndent)
+ }
+
+ if (forceBreakBody) {
+ builder.forcedBreak()
+ }
+
+ if (hasStatements) {
+ builder.breakOp(Doc.FillMode.UNIFIED, " ", bracePlusBlockIndent)
+ builder.block(bracePlusBlockIndent) {
+ builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO)
+ if (statements.size == 1 &&
+ statements.first() !is KtReturnExpression &&
+ lambdaExpression.bodyExpression?.startsWithComment() != true) {
+ visitStatement(statements[0])
+ } else {
+ visitStatements(statements)
+ }
+ }
+ }
+
+ 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.token("}", blockIndent)
+ }
+ }
+
+ /** Example `this` or `this@Foo` */
+ override fun visitThisExpression(expression: KtThisExpression) {
+ builder.sync(expression)
+ builder.token("this")
+ visit(expression.getTargetLabel())
+ }
+
+ /** Example `Foo` or `@Foo` */
+ override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
+ builder.sync(expression)
+ when (expression) {
+ is KtLabelReferenceExpression -> {
+ if (expression.text[0] == '@') {
+ builder.token("@")
+ builder.token(expression.getIdentifier()?.text ?: fail())
+ } else {
+ builder.token(expression.getIdentifier()?.text ?: fail())
+ builder.token("@")
+ }
+ }
+ else -> {
+ if (expression.text.isNotEmpty()) {
+ builder.token(expression.text)
+ }
+ }
+ }
+ }
+
+ /** e.g., `a: Int, b: Int, c: Int` in `fun foo(a: Int, b: Int, c: Int) { ... }`. */
+ override fun visitParameterList(list: KtParameterList) {
+ emitParameterLikeList(list.parameters, list.trailingComma != null, wrapInBlock = false)
+ }
+
+ /**
+ * Emit a list of elements that look like function parameters or arguments, e.g., `a, b, c` in
+ * `foo(a, b, c)`
+ */
+ private fun <T : PsiElement> emitParameterLikeList(
+ list: List<T>?,
+ hasTrailingComma: Boolean,
+ wrapInBlock: Boolean
+ ) {
+ if (list.isNullOrEmpty()) {
+ return
+ }
+
+ forEachCommaSeparated(list, hasTrailingComma, wrapInBlock, trailingBreak = isGoogleStyle) {
+ visit(it)
+ }
+ if (hasTrailingComma) {
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+ }
+ }
+
+ /**
+ * Call `function` for each element in `list`, with comma (,) tokens inbetween.
+ *
+ * Example:
+ * ```
+ * a, b, c, 3, 4, 5
+ * ```
+ *
+ * Either the entire list fits in one line, or each element is put on its own line:
+ * ```
+ * a,
+ * b,
+ * c,
+ * 3,
+ * 4,
+ * 5
+ * ```
+ *
+ * @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.
+ *
+ * Example:
+ * ```
+ * a,
+ * b,
+ * ```
+ */
+ private fun <T> forEachCommaSeparated(
+ list: Iterable<T>,
+ hasTrailingComma: Boolean = false,
+ wrapInBlock: Boolean = true,
+ trailingBreak: Boolean = false,
+ function: (T) -> Unit
+ ) {
+ if (hasTrailingComma) {
+ builder.block(ZERO) {
+ builder.forcedBreak()
+ for (value in list) {
+ function(value)
+ builder.token(",")
+ builder.forcedBreak()
+ }
+ }
+ return
+ }
+
+ builder.block(ZERO, isEnabled = wrapInBlock) {
+ var first = true
+ builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+ for (value in list) {
+ if (!first) {
+ builder.token(",")
+ builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+ }
+ first = false
+
+ function(value)
+ }
+ }
+ if (trailingBreak) {
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+ }
+ }
+
+ /** Example `a` in `foo(a)`, or `*a`, or `limit = 50` */
+ override fun visitArgument(argument: KtValueArgument) {
+ visitArgumentInternal(argument, forceBreakLambdaBody = false)
+ }
+
+ /**
+ * The internal version of [visitArgument].
+ *
+ * @param forceBreakLambdaBody if true (and [argument] is of type [KtLambdaExpression]), forces
+ * the lambda to be multi-line. See documentation of [visitLambdaExpressionInternal] for an
+ * example.
+ */
+ private fun visitArgumentInternal(
+ argument: KtValueArgument,
+ forceBreakLambdaBody: Boolean,
+ ) {
+ builder.sync(argument)
+ val hasArgName = argument.getArgumentName() != null
+ val isLambda = argument.getArgumentExpression() is KtLambdaExpression
+ builder.block(ZERO) {
+ if (hasArgName) {
+ visit(argument.getArgumentName())
+ builder.space()
+ builder.token("=")
+ if (isLambda) {
+ builder.space()
+ }
+ }
+ builder.block(if (hasArgName && !isLambda) expressionBreakIndent else ZERO) {
+ if (hasArgName && !isLambda) {
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
+ }
+ if (argument.isSpread) {
+ builder.token("*")
+ }
+ if (isLambda) {
+ visitLambdaExpressionInternal(
+ argument.getArgumentExpression() as KtLambdaExpression,
+ brokeBeforeBrace = null,
+ forceBreakBody = forceBreakLambdaBody,
+ )
+ } else {
+ visit(argument.getArgumentExpression())
+ }
+ }
+ }
+ }
+
+ override fun visitReferenceExpression(expression: KtReferenceExpression) {
+ builder.sync(expression)
+ builder.token(expression.text)
+ }
+
+ override fun visitReturnExpression(expression: KtReturnExpression) {
+ builder.sync(expression)
+ builder.token("return")
+ visit(expression.getTargetLabel())
+ val returnedExpression = expression.returnedExpression
+ if (returnedExpression != null) {
+ builder.space()
+ visit(returnedExpression)
+ }
+ builder.guessToken(";")
+ }
+
+ /**
+ * For example `a + b`, `a + b + c` or `a..b`
+ *
+ * The extra handling here drills to the left most expression and handles it for long chains of
+ * binary expressions that are formatted not accordingly to the associative values That is, we
+ * want to think of `a + b + c` as `(a + b) + c`, whereas the AST parses it as `a + (b + c)`
+ */
+ override fun visitBinaryExpression(expression: KtBinaryExpression) {
+ builder.sync(expression)
+ val op = expression.operationToken
+
+ if (KtTokens.ALL_ASSIGNMENTS.contains(op) && isLambdaOrScopingFunction(expression.right)) {
+ // Assignments are statements in Kotlin; we don't have to worry about compound assignment.
+ visit(expression.left)
+ builder.space()
+ builder.token(expression.operationReference.text)
+ visitLambdaOrScopingFunction(expression.right)
+ return
+ }
+
+ val parts =
+ ArrayDeque<KtBinaryExpression>().apply {
+ var current: KtExpression? = expression
+ while (current is KtBinaryExpression && current.operationToken == op) {
+ addFirst(current)
+ current = current.left
+ }
+ }
+
+ val leftMostExpression = parts.first()
+ visit(leftMostExpression.left)
+ for (leftExpression in parts) {
+ when (leftExpression.operationToken) {
+ KtTokens.RANGE -> {}
+ KtTokens.ELVIS -> builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+ else -> builder.space()
+ }
+ builder.token(leftExpression.operationReference.text)
+ val isFirst = leftExpression === leftMostExpression
+ if (isFirst) {
+ builder.open(expressionBreakIndent)
+ }
+ when (leftExpression.operationToken) {
+ KtTokens.RANGE -> {}
+ KtTokens.ELVIS -> builder.space()
+ else -> builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+ }
+ visit(leftExpression.right)
+ }
+ builder.close()
+ }
+
+ override fun visitPostfixExpression(expression: KtPostfixExpression) {
+ builder.sync(expression)
+ builder.block(ZERO) {
+ val baseExpression = expression.baseExpression
+ val operator = expression.operationReference.text
+
+ visit(baseExpression)
+ if (baseExpression is KtPostfixExpression &&
+ baseExpression.operationReference.text.last() == operator.first()) {
+ builder.space()
+ }
+ builder.token(operator)
+ }
+ }
+
+ override fun visitPrefixExpression(expression: KtPrefixExpression) {
+ builder.sync(expression)
+ builder.block(ZERO) {
+ val baseExpression = expression.baseExpression
+ val operator = expression.operationReference.text
+
+ builder.token(operator)
+ if (baseExpression is KtPrefixExpression &&
+ operator.last() == baseExpression.operationReference.text.first()) {
+ builder.space()
+ }
+ visit(baseExpression)
+ }
+ }
+
+ override fun visitLabeledExpression(expression: KtLabeledExpression) {
+ builder.sync(expression)
+ visit(expression.labelQualifier)
+ if (expression.baseExpression !is KtLambdaExpression) {
+ builder.space()
+ }
+ visit(expression.baseExpression)
+ }
+
+ internal enum class DeclarationKind {
+ FIELD,
+ PARAMETER
+ }
+
+ /**
+ * Declare one variable or variable-like thing.
+ *
+ * Examples:
+ * - `var a: Int = 5`
+ * - `a: Int`
+ * - `private val b:
+ */
+ private fun declareOne(
+ kind: DeclarationKind,
+ modifiers: KtModifierList?,
+ valOrVarKeyword: String?,
+ typeParameters: KtTypeParameterList? = null,
+ receiver: KtTypeReference? = null,
+ name: String?,
+ type: KtTypeReference?,
+ typeConstraintList: KtTypeConstraintList? = null,
+ initializer: KtExpression?,
+ delegate: KtPropertyDelegate? = null,
+ accessors: List<KtPropertyAccessor>? = null
+ ): Int {
+ val verticalAnnotationBreak = genSym()
+
+ val isField = kind == DeclarationKind.FIELD
+
+ if (isField) {
+ builder.blankLineWanted(OpsBuilder.BlankLineWanted.conditional(verticalAnnotationBreak))
+ }
+
+ visit(modifiers)
+ builder.block(ZERO) {
+ builder.block(ZERO) {
+ if (valOrVarKeyword != null) {
+ builder.token(valOrVarKeyword)
+ builder.space()
+ }
+
+ if (typeParameters != null) {
+ visit(typeParameters)
+ builder.space()
+ }
+
+ // conditionally indent the name and initializer +4 if the type spans
+ // multiple lines
+ if (name != null) {
+ if (receiver != null) {
+ visit(receiver)
+ 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)
+ }
+ 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)) {
+ 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) }
+ }
+ }
+ 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) {
+ for (accessor in accessors) {
+ builder.forcedBreak()
+ // The semicolon must come after the newline, or the output code will not parse.
+ builder.guessToken(";")
+
+ builder.block(ZERO) {
+ visitFunctionLikeExpression(
+ accessor.modifierList,
+ accessor.namePlaceholder.text,
+ null,
+ null,
+ null,
+ accessor.bodyExpression != null || accessor.bodyBlockExpression != null,
+ accessor.parameterList,
+ null,
+ accessor.bodyBlockExpression,
+ accessor.bodyExpression,
+ accessor.returnTypeReference,
+ accessor.bodyBlockExpression?.lBrace != null)
+ }
+ }
+ }
+ }
+
+ builder.guessToken(";")
+
+ if (isField) {
+ builder.blankLineWanted(OpsBuilder.BlankLineWanted.conditional(verticalAnnotationBreak))
+ }
+
+ return 0
+ }
+
+ /**
+ * Returns whether an expression is a lambda or initializer expression in which case we will want
+ * to avoid indenting the lambda block
+ *
+ * Examples:
+ * 1. '... = { ... }' is a lambda expression
+ * 2. '... = Runnable { ... }' is considered a scoping function
+ * 3. '... = scope { ... }' '... = apply { ... }' is a scoping function
+ *
+ * but not:
+ * 1. '... = foo() { ... }' due to the empty parenthesis
+ * 2. '... = Runnable @Annotation { ... }' due to the annotation
+ */
+ private fun isLambdaOrScopingFunction(expression: KtExpression?): Boolean {
+ if (expression is KtLambdaExpression) {
+ return true
+ }
+ if (expression is KtCallExpression &&
+ expression.valueArgumentList?.leftParenthesis == null &&
+ expression.lambdaArguments.isNotEmpty() &&
+ expression.typeArgumentList?.arguments.isNullOrEmpty() &&
+ expression.lambdaArguments.first().getArgumentExpression() is KtLambdaExpression) {
+ return true
+ }
+ return false
+ }
+
+ /** See [isLambdaOrScopingFunction] for examples. */
+ private fun visitLambdaOrScopingFunction(expr: PsiElement?) {
+ val breakToExpr = genSym()
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent, Optional.of(breakToExpr))
+
+ val lambdaExpression =
+ when (expr) {
+ is KtLambdaExpression -> expr
+ is KtCallExpression -> {
+ visit(expr.calleeExpression)
+ builder.space()
+ expr.lambdaArguments[0].getLambdaExpression() ?: fail()
+ }
+ else -> throw AssertionError(expr)
+ }
+
+ visitLambdaExpressionInternal(
+ lambdaExpression,
+ brokeBeforeBrace = breakToExpr,
+ forceBreakBody = false,
+ )
+ }
+
+ override fun visitClassOrObject(classOrObject: KtClassOrObject) {
+ builder.sync(classOrObject)
+ val modifierList = classOrObject.modifierList
+ builder.block(ZERO) {
+ if (modifierList != null) {
+ visitModifierList(modifierList)
+ }
+ val declarationKeyword = classOrObject.getDeclarationKeyword()
+ if (declarationKeyword != null) {
+ builder.token(declarationKeyword.text ?: fail())
+ }
+ val name = classOrObject.nameIdentifier
+ if (name != null) {
+ builder.space()
+ builder.token(name.text)
+ visit(classOrObject.typeParameterList)
+ }
+ visit(classOrObject.primaryConstructor)
+ val superTypes = classOrObject.getSuperTypeList()
+ if (superTypes != null) {
+ builder.space()
+ builder.block(ZERO) {
+ builder.token(":")
+ builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
+ visit(superTypes)
+ }
+ }
+ builder.space()
+ val typeConstraintList = classOrObject.typeConstraintList
+ if (typeConstraintList != null) {
+ if (superTypes?.entries?.lastOrNull() is KtDelegatedSuperTypeEntry) {
+ builder.forcedBreak()
+ }
+ visit(typeConstraintList)
+ builder.space()
+ }
+ val body = classOrObject.body
+ if (classOrObject.hasModifier(KtTokens.ENUM_KEYWORD)) {
+ visitEnumBody(classOrObject as KtClass)
+ } else if (body != null) {
+ visitBlockBody(body, true)
+ }
+ }
+ if (classOrObject.nameIdentifier != null) {
+ builder.forcedBreak()
+ }
+ }
+
+ /** Example `{ RED, GREEN; fun foo() { ... } }` for an enum class */
+ private fun visitEnumBody(enumClass: KtClass) {
+ val body = enumClass.body
+ if (body == null) {
+ return
+ }
+ builder.token("{", Doc.Token.RealOrImaginary.REAL, blockIndent, Optional.of(blockIndent))
+ builder.open(ZERO)
+ builder.block(blockIndent) {
+ builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+ val (enumEntries, nonEnumEntryStatements) = body.children.partition { it is KtEnumEntry }
+ builder.forcedBreak()
+ visitEnumEntries(enumEntries)
+
+ if (nonEnumEntryStatements.isNotEmpty()) {
+ builder.forcedBreak()
+ builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
+ visitStatements(nonEnumEntryStatements.toTypedArray())
+ }
+ }
+ builder.forcedBreak()
+ builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO)
+ builder.token("}", blockIndent)
+ builder.close()
+ }
+
+ /** Example `RED, GREEN, BLUE,` in an enum class, or `RED, GREEN;` */
+ private fun visitEnumEntries(enumEntries: List<PsiElement>) {
+ builder.block(ZERO) {
+ builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+ for (value in enumEntries) {
+ visit(value)
+ if (builder.peekToken() == Optional.of(",")) {
+ builder.token(",")
+ builder.forcedBreak()
+ }
+ }
+ }
+ builder.guessToken(";")
+ }
+
+ override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor) {
+ builder.sync(constructor)
+ builder.block(ZERO) {
+ if (constructor.hasConstructorKeyword()) {
+ builder.open(ZERO)
+ builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+ visit(constructor.modifierList)
+ builder.token("constructor")
+ }
+
+ builder.block(ZERO) {
+ builder.token("(")
+ builder.block(expressionBreakIndent) {
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+ visit(constructor.valueParameterList)
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+ if (constructor.hasConstructorKeyword()) {
+ builder.close()
+ }
+ }
+ builder.token(")")
+ }
+ }
+ }
+
+ /** Example `private constructor(n: Int) : this(4, 5) { ... }` inside a class's body */
+ override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor) {
+ val delegationCall = constructor.getDelegationCall()
+ val bodyExpression = constructor.bodyExpression
+
+ builder.sync(constructor)
+
+ visitFunctionLikeExpression(
+ constructor.modifierList,
+ "constructor",
+ null,
+ null,
+ null,
+ true,
+ constructor.valueParameterList,
+ null,
+ bodyExpression,
+ null,
+ if (!delegationCall.isImplicit) delegationCall else null,
+ true)
+ }
+
+ override fun visitConstructorDelegationCall(call: KtConstructorDelegationCall) {
+ // Work around a misfeature in kotlin-compiler: call.calleeExpression.accept doesn't call
+ // visitReferenceExpression, but calls visitElement instead.
+ builder.block(ZERO) {
+ builder.token(if (call.isCallToThis) "this" else "super")
+ visitCallElement(
+ null,
+ call.typeArgumentList,
+ call.valueArgumentList,
+ call.lambdaArguments,
+ lambdaIndent = ZERO)
+ }
+ }
+
+ override fun visitClassInitializer(initializer: KtClassInitializer) {
+ builder.sync(initializer)
+ builder.token("init")
+ builder.space()
+ visit(initializer.body)
+ }
+
+ override fun visitConstantExpression(expression: KtConstantExpression) {
+ builder.sync(expression)
+ builder.token(expression.text)
+ }
+
+ /** Example `(1 + 1)` */
+ override fun visitParenthesizedExpression(expression: KtParenthesizedExpression) {
+ builder.sync(expression)
+ builder.token("(")
+ visit(expression.expression)
+ builder.token(")")
+ }
+
+ override fun visitPackageDirective(directive: KtPackageDirective) {
+ builder.sync(directive)
+ if (directive.packageKeyword == null) {
+ return
+ }
+ builder.token("package")
+ builder.space()
+ var first = true
+ for (packageName in directive.packageNames) {
+ if (first) {
+ first = false
+ } else {
+ builder.token(".")
+ }
+ builder.token(packageName.getIdentifier()?.text ?: packageName.getReferencedName())
+ }
+
+ builder.guessToken(";")
+ builder.forcedBreak()
+ }
+
+ /** Example `import com.foo.A; import com.bar.B` */
+ override fun visitImportList(importList: KtImportList) {
+ builder.sync(importList)
+ importList.imports.forEach { visit(it) }
+ }
+
+ /** Example `import com.foo.A` */
+ override fun visitImportDirective(directive: KtImportDirective) {
+ builder.sync(directive)
+ builder.token("import")
+ builder.space()
+
+ val importedReference = directive.importedReference
+ if (importedReference != null) {
+ inImport = true
+ visit(importedReference)
+ inImport = false
+ }
+ if (directive.isAllUnder) {
+ builder.token(".")
+ builder.token("*")
+ }
+
+ // Possible alias.
+ val alias = directive.alias?.nameIdentifier
+ if (alias != null) {
+ builder.space()
+ builder.token("as")
+ builder.space()
+ builder.token(alias.text ?: fail())
+ }
+
+ // Force a newline afterwards.
+ builder.guessToken(";")
+ builder.forcedBreak()
+ }
+
+ /** For example `@Magic private final` */
+ override fun visitModifierList(list: KtModifierList) {
+ builder.sync(list)
+ var onlyAnnotationsSoFar = true
+
+ for (child in list.node.children()) {
+ val psi = child.psi
+ if (psi is PsiWhiteSpace) {
+ continue
+ }
+
+ if (child.elementType is KtModifierKeywordToken) {
+ onlyAnnotationsSoFar = false
+ builder.token(child.text)
+ } else {
+ visit(psi)
+ }
+
+ if (onlyAnnotationsSoFar) {
+ builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+ } else {
+ builder.space()
+ }
+ }
+ }
+
+ /**
+ * Example:
+ * ```
+ * @SuppressLint("MagicNumber")
+ * print(10)
+ * ```
+ *
+ * in
+ *
+ * ```
+ * fun f() {
+ * @SuppressLint("MagicNumber")
+ * print(10)
+ * }
+ * ```
+ */
+ override fun visitAnnotatedExpression(expression: KtAnnotatedExpression) {
+ builder.sync(expression)
+ builder.block(ZERO) {
+ val baseExpression = expression.baseExpression
+
+ builder.block(ZERO) {
+ val annotationEntries = expression.annotationEntries
+ for (annotationEntry in annotationEntries) {
+ if (annotationEntry !== annotationEntries.first()) {
+ builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+ }
+ visit(annotationEntry)
+ }
+ }
+
+ // Binary expressions in a block have a different meaning according to their formatting.
+ // If there in the line above, they refer to the entire expression, if they're in the same
+ // line then only to the first operand of the operator.
+ // We force a break to avoid such semantic changes
+ when {
+ (baseExpression is KtBinaryExpression || baseExpression is KtBinaryExpressionWithTypeRHS) &&
+ expression.parent is KtBlockExpression -> builder.forcedBreak()
+ baseExpression is KtLambdaExpression -> builder.space()
+ else -> builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+ }
+
+ visit(expression.baseExpression)
+ }
+ }
+
+ /**
+ * For example, @field:[Inject Named("WEB_VIEW")]
+ *
+ * A KtAnnotation is used only to group multiple annotations with the same use-site-target. It
+ * only appears in a modifier list since annotated expressions do not have use-site-targets.
+ */
+ override fun visitAnnotation(annotation: KtAnnotation) {
+ builder.sync(annotation)
+ builder.block(ZERO) {
+ builder.token("@")
+ val useSiteTarget = annotation.useSiteTarget
+ if (useSiteTarget != null) {
+ visit(useSiteTarget)
+ builder.token(":")
+ }
+ builder.block(expressionBreakIndent) {
+ builder.token("[")
+
+ builder.block(ZERO) {
+ var first = true
+ builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+ for (value in annotation.entries) {
+ if (!first) {
+ builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+ }
+ first = false
+
+ visit(value)
+ }
+ }
+ }
+ builder.token("]")
+ }
+ builder.forcedBreak()
+ }
+
+ /** For example, 'field' in @field:[Inject Named("WEB_VIEW")] */
+ override fun visitAnnotationUseSiteTarget(
+ annotationTarget: KtAnnotationUseSiteTarget,
+ data: Void?
+ ): Void? {
+ builder.token(annotationTarget.getAnnotationUseSiteTarget().renderName)
+ return null
+ }
+
+ /** For example `@Magic` or `@Fred(1, 5)` */
+ override fun visitAnnotationEntry(annotationEntry: KtAnnotationEntry) {
+ builder.sync(annotationEntry)
+ if (annotationEntry.atSymbol != null) {
+ builder.token("@")
+ }
+ val useSiteTarget = annotationEntry.useSiteTarget
+ if (useSiteTarget != null && useSiteTarget.parent == annotationEntry) {
+ visit(useSiteTarget)
+ builder.token(":")
+ }
+ visitCallElement(
+ annotationEntry.calleeExpression,
+ null, // Type-arguments are included in the annotation's callee expression.
+ annotationEntry.valueArgumentList,
+ listOf())
+ }
+
+ override fun visitFileAnnotationList(
+ fileAnnotationList: KtFileAnnotationList,
+ data: Void?
+ ): Void? {
+ for (child in fileAnnotationList.node.children()) {
+ if (child is PsiElement) {
+ continue
+ }
+ visit(child.psi)
+ builder.forcedBreak()
+ }
+
+ return null
+ }
+
+ override fun visitSuperTypeList(list: KtSuperTypeList) {
+ builder.sync(list)
+ builder.block(expressionBreakIndent) { forEachCommaSeparated(list.entries) { visit(it) } }
+ }
+
+ override fun visitSuperTypeCallEntry(call: KtSuperTypeCallEntry) {
+ builder.sync(call)
+ visitCallElement(call.calleeExpression, null, call.valueArgumentList, call.lambdaArguments)
+ }
+
+ /**
+ * Example `Collection<Int> by list` in `class MyList(list: List<Int>) : Collection<Int> by list`
+ */
+ override fun visitDelegatedSuperTypeEntry(specifier: KtDelegatedSuperTypeEntry) {
+ builder.sync(specifier)
+ visit(specifier.typeReference)
+ builder.space()
+ builder.token("by")
+ builder.space()
+ visit(specifier.delegateExpression)
+ }
+
+ override fun visitWhenExpression(expression: KtWhenExpression) {
+ builder.sync(expression)
+ builder.block(ZERO) {
+ emitKeywordWithCondition("when", expression.subjectExpression)
+
+ builder.space()
+ builder.token("{", Doc.Token.RealOrImaginary.REAL, blockIndent, Optional.of(blockIndent))
+
+ expression.entries.forEach { whenEntry ->
+ builder.block(blockIndent) {
+ builder.forcedBreak()
+ if (whenEntry.isElse) {
+ builder.token("else")
+ } else {
+ builder.block(ZERO) {
+ val conditions = whenEntry.conditions
+ for ((index, condition) in conditions.withIndex()) {
+ visit(condition)
+ builder.guessToken(",")
+ if (index != conditions.lastIndex) {
+ builder.forcedBreak()
+ }
+ }
+ }
+ }
+ val whenExpression = whenEntry.expression
+ builder.space()
+ builder.token("->")
+ if (whenExpression is KtBlockExpression) {
+ builder.space()
+ visit(whenExpression)
+ } else {
+ builder.block(expressionBreakIndent) {
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
+ visit(whenExpression)
+ }
+ }
+ builder.guessToken(";")
+ }
+ builder.forcedBreak()
+ }
+ builder.token("}")
+ }
+ }
+
+ override fun visitBlockExpression(expression: KtBlockExpression) {
+ builder.sync(expression)
+ visitBlockBody(expression, true)
+ }
+
+ override fun visitWhenConditionWithExpression(condition: KtWhenConditionWithExpression) {
+ builder.sync(condition)
+ visit(condition.expression)
+ }
+
+ override fun visitWhenConditionIsPattern(condition: KtWhenConditionIsPattern) {
+ builder.sync(condition)
+ builder.token(if (condition.isNegated) "!is" else "is")
+ builder.space()
+ visit(condition.typeReference)
+ }
+
+ /** Example `in 1..2` as part of a when expression */
+ override fun visitWhenConditionInRange(condition: KtWhenConditionInRange) {
+ builder.sync(condition)
+ // TODO: replace with 'condition.isNegated' once https://youtrack.jetbrains.com/issue/KT-34395
+ // is fixed.
+ val isNegated = condition.firstChild?.node?.findChildByType(KtTokens.NOT_IN) != null
+ builder.token(if (isNegated) "!in" else "in")
+ builder.space()
+ visit(condition.rangeExpression)
+ }
+
+ override fun visitIfExpression(expression: KtIfExpression) {
+ builder.sync(expression)
+ builder.block(ZERO) {
+ emitKeywordWithCondition("if", expression.condition)
+
+ if (expression.then is KtBlockExpression) {
+ builder.space()
+ builder.block(ZERO) { visit(expression.then) }
+ } else {
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+ builder.block(expressionBreakIndent) { visit(expression.then) }
+ }
+
+ if (expression.elseKeyword != null) {
+ if (expression.then is KtBlockExpression) {
+ builder.space()
+ } else {
+ builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+ }
+
+ builder.block(ZERO) {
+ builder.token("else")
+ if (expression.`else` is KtBlockExpression || expression.`else` is KtIfExpression) {
+ builder.space()
+ builder.block(ZERO) { visit(expression.`else`) }
+ } else {
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+ builder.block(expressionBreakIndent) { visit(expression.`else`) }
+ }
+ }
+ }
+ }
+ }
+
+ /** Example `a[3]`, `b["a", 5]` or `a.b.c[4]` */
+ override fun visitArrayAccessExpression(expression: KtArrayAccessExpression) {
+ builder.sync(expression)
+ if (expression.arrayExpression is KtQualifiedExpression) {
+ emitQualifiedExpression(expression)
+ } else {
+ visit(expression.arrayExpression)
+ visitArrayAccessBrackets(expression)
+ }
+ }
+
+ /**
+ * Example `[3]` in `a[3]` or `a[3].b` Separated since it needs to be used from a top level array
+ * expression (`a[3]`) and from within a qualified chain (`a[3].b)
+ */
+ private fun visitArrayAccessBrackets(expression: KtArrayAccessExpression) {
+ builder.block(ZERO) {
+ builder.token("[")
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+ builder.block(expressionBreakIndent) {
+ emitParameterLikeList(
+ expression.indexExpressions, expression.trailingComma != null, wrapInBlock = true)
+ }
+ }
+ builder.token("]")
+ }
+
+ /** Example `val (a, b: Int) = Pair(1, 2)` */
+ override fun visitDestructuringDeclaration(destructuringDeclaration: KtDestructuringDeclaration) {
+ builder.sync(destructuringDeclaration)
+ val valOrVarKeyword = destructuringDeclaration.valOrVarKeyword
+ if (valOrVarKeyword != null) {
+ builder.token(valOrVarKeyword.text)
+ builder.space()
+ }
+ val hasTrailingComma = destructuringDeclaration.trailingComma != null
+ builder.block(ZERO) {
+ builder.token("(")
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+ builder.block(expressionBreakIndent) {
+ emitParameterLikeList(
+ destructuringDeclaration.entries, hasTrailingComma, wrapInBlock = true)
+ }
+ }
+ builder.token(")")
+ val initializer = destructuringDeclaration.initializer
+ if (initializer != null) {
+ builder.space()
+ builder.token("=")
+ if (hasTrailingComma) {
+ builder.space()
+ } else {
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+ }
+ builder.block(expressionBreakIndent, !hasTrailingComma) { visit(initializer) }
+ }
+ }
+
+ /** Example `a: String` which is part of `(a: String, b: String)` */
+ override fun visitDestructuringDeclarationEntry(
+ multiDeclarationEntry: KtDestructuringDeclarationEntry
+ ) {
+ builder.sync(multiDeclarationEntry)
+ declareOne(
+ initializer = null,
+ kind = DeclarationKind.PARAMETER,
+ modifiers = multiDeclarationEntry.modifierList,
+ name = multiDeclarationEntry.nameIdentifier?.text ?: fail(),
+ type = multiDeclarationEntry.typeReference,
+ valOrVarKeyword = null,
+ )
+ }
+
+ /** Example `"Hello $world!"` or `"""Hello world!"""` */
+ override fun visitStringTemplateExpression(expression: KtStringTemplateExpression) {
+ builder.sync(expression)
+ builder.token(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone(expression.text))
+ }
+
+ /** Example `super` in `super.doIt(5)` or `super<Foo>` in `super<Foo>.doIt(5)` */
+ override fun visitSuperExpression(expression: KtSuperExpression) {
+ builder.sync(expression)
+ builder.token("super")
+ val superTypeQualifier = expression.superTypeQualifier
+ if (superTypeQualifier != null) {
+ builder.token("<")
+ visit(superTypeQualifier)
+ builder.token(">")
+ }
+ visit(expression.labelQualifier)
+ }
+
+ /** Example `<T, S>` */
+ override fun visitTypeParameterList(list: KtTypeParameterList) {
+ builder.sync(list)
+ builder.block(ZERO) {
+ builder.token("<")
+ val parameters = list.parameters
+ if (parameters.isNotEmpty()) {
+ // Break before args.
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+ builder.block(expressionBreakIndent) {
+ emitParameterLikeList(list.parameters, list.trailingComma != null, wrapInBlock = true)
+ }
+ }
+ builder.token(">")
+ }
+ }
+
+ override fun visitTypeParameter(parameter: KtTypeParameter) {
+ builder.sync(parameter)
+ visit(parameter.modifierList)
+ builder.token(parameter.nameIdentifier?.text ?: "")
+ val extendsBound = parameter.extendsBound
+ if (extendsBound != null) {
+ builder.space()
+ builder.token(":")
+ builder.space()
+ visit(extendsBound)
+ }
+ }
+
+ /** Example `where T : View, T : Listener` */
+ override fun visitTypeConstraintList(list: KtTypeConstraintList) {
+ builder.token("where")
+ builder.space()
+ builder.sync(list)
+ forEachCommaSeparated(list.constraints) { visit(it) }
+ }
+
+ /** Example `T : Foo` */
+ override fun visitTypeConstraint(constraint: KtTypeConstraint) {
+ builder.sync(constraint)
+ // TODO(nreid260): What about annotations on the type reference? `where @A T : Int`
+ visit(constraint.subjectTypeParameterName)
+ builder.space()
+ builder.token(":")
+ builder.space()
+ visit(constraint.boundTypeReference)
+ }
+
+ /** Example `for (i in items) { ... }` */
+ override fun visitForExpression(expression: KtForExpression) {
+ builder.sync(expression)
+ builder.block(ZERO) {
+ builder.token("for")
+ builder.space()
+ builder.token("(")
+ visit(expression.loopParameter)
+ builder.space()
+ builder.token("in")
+ builder.block(ZERO) {
+ builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
+ builder.block(expressionBreakIndent) { visit(expression.loopRange) }
+ }
+ builder.token(")")
+ builder.space()
+ visit(expression.body)
+ }
+ }
+
+ /** Example `while (a < b) { ... }` */
+ override fun visitWhileExpression(expression: KtWhileExpression) {
+ builder.sync(expression)
+ emitKeywordWithCondition("while", expression.condition)
+ builder.space()
+ visit(expression.body)
+ }
+
+ /** Example `do { ... } while (a < b)` */
+ override fun visitDoWhileExpression(expression: KtDoWhileExpression) {
+ builder.sync(expression)
+ builder.token("do")
+ builder.space()
+ if (expression.body != null) {
+ visit(expression.body)
+ builder.space()
+ }
+ emitKeywordWithCondition("while", expression.condition)
+ }
+
+ /** Example `break` or `break@foo` in a loop */
+ override fun visitBreakExpression(expression: KtBreakExpression) {
+ builder.sync(expression)
+ builder.token("break")
+ visit(expression.labelQualifier)
+ }
+
+ /** Example `continue` or `continue@foo` in a loop */
+ override fun visitContinueExpression(expression: KtContinueExpression) {
+ builder.sync(expression)
+ builder.token("continue")
+ visit(expression.labelQualifier)
+ }
+
+ /** Example `f: String`, or `private val n: Int` or `(a: Int, b: String)` (in for-loops) */
+ override fun visitParameter(parameter: KtParameter) {
+ builder.sync(parameter)
+ builder.block(ZERO) {
+ val destructuringDeclaration = parameter.destructuringDeclaration
+ val typeReference = parameter.typeReference
+ if (destructuringDeclaration != null) {
+ builder.block(ZERO) {
+ visit(destructuringDeclaration)
+ if (typeReference != null) {
+ builder.token(":")
+ builder.space()
+ visit(typeReference)
+ }
+ }
+ } else {
+ declareOne(
+ kind = DeclarationKind.PARAMETER,
+ modifiers = parameter.modifierList,
+ valOrVarKeyword = parameter.valOrVarKeyword?.text,
+ name = parameter.nameIdentifier?.text,
+ type = typeReference,
+ initializer = parameter.defaultValue)
+ }
+ }
+ }
+
+ /** Example `String::isNullOrEmpty` */
+ override fun visitCallableReferenceExpression(expression: KtCallableReferenceExpression) {
+ builder.sync(expression)
+ visit(expression.receiverExpression)
+
+ // For some reason, expression.receiverExpression doesn't contain the question-mark token in
+ // case of a nullable type, e.g., in String?::isNullOrEmpty.
+ // Instead, KtCallableReferenceExpression exposes a method that looks for the QUEST token in
+ // its children.
+ if (expression.hasQuestionMarks) {
+ builder.token("?")
+ }
+
+ builder.block(expressionBreakIndent) {
+ builder.token("::")
+ builder.breakOp(Doc.FillMode.INDEPENDENT, "", ZERO)
+ visit(expression.callableReference)
+ }
+ }
+
+ override fun visitClassLiteralExpression(expression: KtClassLiteralExpression) {
+ builder.sync(expression)
+ val receiverExpression = expression.receiverExpression
+ if (receiverExpression is KtCallExpression) {
+ visitCallElement(
+ receiverExpression.calleeExpression,
+ receiverExpression.typeArgumentList,
+ receiverExpression.valueArgumentList,
+ receiverExpression.lambdaArguments)
+ } else {
+ visit(receiverExpression)
+ }
+ builder.token("::")
+ builder.token("class")
+ }
+
+ override fun visitFunctionType(type: KtFunctionType) {
+ builder.sync(type)
+ val receiver = type.receiver
+ if (receiver != null) {
+ visit(receiver)
+ builder.token(".")
+ }
+ builder.block(expressionBreakIndent) {
+ builder.token("(")
+ visit(type.parameterList)
+ }
+ builder.token(")")
+ builder.space()
+ builder.token("->")
+ builder.space()
+ builder.block(expressionBreakIndent) { visit(type.returnTypeReference) }
+ }
+
+ /** Example `a is Int` or `b !is Int` */
+ override fun visitIsExpression(expression: KtIsExpression) {
+ builder.sync(expression)
+ val openGroupBeforeLeft = expression.leftHandSide !is KtQualifiedExpression
+ if (openGroupBeforeLeft) builder.open(ZERO)
+ visit(expression.leftHandSide)
+ if (!openGroupBeforeLeft) builder.open(ZERO)
+ val parent = expression.parent
+ if (parent is KtValueArgument ||
+ parent is KtParenthesizedExpression ||
+ parent is KtContainerNode) {
+ builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
+ } else {
+ builder.space()
+ }
+ visit(expression.operationReference)
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+ builder.block(expressionBreakIndent) { visit(expression.typeReference) }
+ builder.close()
+ }
+
+ /** Example `a as Int` or `a as? Int` */
+ override fun visitBinaryWithTypeRHSExpression(expression: KtBinaryExpressionWithTypeRHS) {
+ builder.sync(expression)
+ val openGroupBeforeLeft = expression.left !is KtQualifiedExpression
+ if (openGroupBeforeLeft) builder.open(ZERO)
+ visit(expression.left)
+ if (!openGroupBeforeLeft) builder.open(ZERO)
+ builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
+ visit(expression.operationReference)
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+ builder.block(expressionBreakIndent) { visit(expression.right) }
+ builder.close()
+ }
+
+ /**
+ * Example:
+ * ```
+ * fun f() {
+ * val a: Array<Int> = [1, 2, 3]
+ * }
+ * ```
+ */
+ override fun visitCollectionLiteralExpression(expression: KtCollectionLiteralExpression) {
+ builder.sync(expression)
+ builder.block(ZERO) {
+ builder.token("[")
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+ builder.block(expressionBreakIndent) {
+ emitParameterLikeList(
+ expression.getInnerExpressions(), expression.trailingComma != null, wrapInBlock = true)
+ }
+ }
+ builder.token("]")
+ }
+
+ override fun visitTryExpression(expression: KtTryExpression) {
+ builder.sync(expression)
+ builder.token("try")
+ builder.space()
+ visit(expression.tryBlock)
+ for (catchClause in expression.catchClauses) {
+ visit(catchClause)
+ }
+ visit(expression.finallyBlock)
+ }
+
+ override fun visitCatchSection(catchClause: KtCatchClause) {
+ builder.sync(catchClause)
+ builder.space()
+ builder.token("catch")
+ builder.space()
+ builder.block(ZERO) {
+ builder.token("(")
+ builder.block(expressionBreakIndent) {
+ builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+ visit(catchClause.catchParameter)
+ builder.guessToken(",")
+ }
+ }
+ builder.token(")")
+ builder.space()
+ visit(catchClause.catchBody)
+ }
+
+ override fun visitFinallySection(finallySection: KtFinallySection) {
+ builder.sync(finallySection)
+ builder.space()
+ builder.token("finally")
+ builder.space()
+ visit(finallySection.finalExpression)
+ }
+
+ override fun visitThrowExpression(expression: KtThrowExpression) {
+ builder.sync(expression)
+ builder.token("throw")
+ builder.space()
+ visit(expression.thrownExpression)
+ }
+
+ /** Example `RED(0xFF0000)` in an enum class */
+ override fun visitEnumEntry(enumEntry: KtEnumEntry) {
+ builder.sync(enumEntry)
+ builder.block(ZERO) {
+ visit(enumEntry.modifierList)
+ builder.token(enumEntry.nameIdentifier?.text ?: fail())
+ enumEntry.initializerList?.initializers?.forEach { visit(it) }
+ val body = enumEntry.body
+ if (body != null) {
+ builder.space()
+ visitBlockBody(body, true)
+ }
+ }
+ }
+
+ /** Example `private typealias TextChangedListener = (string: String) -> Unit` */
+ override fun visitTypeAlias(typeAlias: KtTypeAlias) {
+ builder.sync(typeAlias)
+ builder.block(ZERO) {
+ visit(typeAlias.modifierList)
+ builder.token("typealias")
+ builder.space()
+ builder.token(typeAlias.nameIdentifier?.text ?: fail())
+ visit(typeAlias.typeParameterList)
+
+ builder.space()
+ builder.token("=")
+ builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+ builder.block(expressionBreakIndent) {
+ visit(typeAlias.getTypeReference())
+ visit(typeAlias.typeConstraintList)
+ builder.guessToken(";")
+ }
+ builder.forcedBreak()
+ }
+ }
+
+ /**
+ * visitElement is called for almost all types of AST nodes. We use it to keep track of whether
+ * we're currently inside an expression or not.
+ *
+ * @throws FormattingError
+ */
+ override fun visitElement(element: PsiElement) {
+ inExpression.addLast(element is KtExpression || inExpression.peekLast())
+ val previous = builder.depth()
+ try {
+ super.visitElement(element)
+ } catch (e: FormattingError) {
+ throw e
+ } catch (t: Throwable) {
+ throw FormattingError(builder.diagnostic(Throwables.getStackTraceAsString(t)))
+ } finally {
+ inExpression.removeLast()
+ }
+ builder.checkClosed(previous)
+ }
+
+ override fun visitKtFile(file: KtFile) {
+ markForPartialFormat()
+ var importListEmpty = false
+ var isFirst = true
+ for (child in file.children) {
+ if (child.text.isBlank()) {
+ importListEmpty = child is KtImportList
+ continue
+ }
+ if (!isFirst && child !is PsiComment && (child !is KtScript || !importListEmpty)) {
+ builder.blankLineWanted(OpsBuilder.BlankLineWanted.YES)
+ }
+ visit(child)
+ isFirst = false
+ }
+ markForPartialFormat()
+ }
+
+ override fun visitScript(script: KtScript) {
+ markForPartialFormat()
+ var lastChildHadBlankLineBefore = false
+ var first = true
+ for (child in script.blockExpression.children) {
+ if (child.text.isBlank()) {
+ continue
+ }
+ builder.forcedBreak()
+ val childGetsBlankLineBefore = child !is KtProperty
+ if (first) {
+ builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
+ } else if (child !is PsiComment &&
+ (childGetsBlankLineBefore || lastChildHadBlankLineBefore)) {
+ builder.blankLineWanted(OpsBuilder.BlankLineWanted.YES)
+ }
+ visit(child)
+ builder.guessToken(";")
+ lastChildHadBlankLineBefore = childGetsBlankLineBefore
+ first = false
+ }
+ markForPartialFormat()
+ }
+
+ private fun inExpression(): Boolean {
+ return inExpression.peekLast()
+ }
+
+ /**
+ * markForPartialFormat is used to delineate the smallest areas of code that must be formatted
+ * together.
+ *
+ * When only parts of the code are being formatted, the requested area is expanded until it's
+ * covered by an area marked by this method.
+ */
+ private fun markForPartialFormat() {
+ if (!inExpression()) {
+ builder.markForPartialFormat()
+ }
+ }
+
+ /**
+ * Emit a [Doc.Token].
+ *
+ * @param token the [String] to wrap in a [Doc.Token]
+ * @param plusIndentCommentsBefore extra block for comments before this token
+ */
+ private fun OpsBuilder.token(token: String, plusIndentCommentsBefore: Indent = ZERO) {
+ token(
+ token,
+ Doc.Token.RealOrImaginary.REAL,
+ plusIndentCommentsBefore,
+ /* breakAndIndentTrailingComment */ Optional.empty())
+ }
+
+ /**
+ * Opens a new level, emits into it and closes it.
+ *
+ * This is a helper method to make it easier to keep track of [OpsBuilder.open] and
+ * [OpsBuilder.close] calls
+ *
+ * @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
+ ) {
+ if (isEnabled) {
+ open(plusIndent)
+ }
+ block()
+ if (isEnabled) {
+ close()
+ }
+ }
+
+ /** Helper method to sync the current offset to match any element in the AST */
+ private fun OpsBuilder.sync(psiElement: PsiElement) {
+ sync(psiElement.startOffset)
+ }
+
+ /**
+ * Throws a formatting error
+ *
+ * This is used as `expr ?: fail()` to avoid using the !! operator and provide better error
+ * messages.
+ */
+ private fun fail(message: String = "Unexpected"): Nothing {
+ throw FormattingError(builder.diagnostic(message))
+ }
+
+ /** Helper function to improve readability */
+ private fun visit(element: PsiElement?) {
+ element?.accept(this)
+ }
+
+ /** Emits a key word followed by a condition, e.g. `if (b)` or `while (c < d )` */
+ private fun emitKeywordWithCondition(keyword: String, condition: KtExpression?) {
+ if (condition == null) {
+ builder.token(keyword)
+ return
+ }
+
+ builder.block(ZERO) {
+ builder.token(keyword)
+ builder.space()
+ builder.token("(")
+ if (isGoogleStyle) {
+ builder.block(expressionBreakIndent) {
+ builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+ visit(condition)
+ builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+ }
+ } else {
+ builder.block(ZERO) { visit(condition) }
+ }
+ }
+ builder.token(")")
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/KotlinTok.kt b/core/src/main/java/com/facebook/ktfmt/format/KotlinTok.kt
new file mode 100644
index 0000000..d03a7e4
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/KotlinTok.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.base.MoreObjects
+import com.google.googlejavaformat.Input
+import com.google.googlejavaformat.Newlines
+import org.jetbrains.kotlin.lexer.KtToken
+
+class KotlinTok(
+ private val index: Int,
+ private val originalText: String,
+ private val text: String,
+ private val position: Int,
+ private val columnI: Int,
+ val isToken: Boolean,
+ private val kind: KtToken
+) : Input.Tok {
+
+ override fun getIndex(): Int = index
+
+ override fun getText(): String = text
+
+ override fun getOriginalText(): String = originalText
+
+ override fun length(): Int = originalText.length
+
+ override fun getPosition(): Int = position
+
+ override fun getColumn(): Int = columnI
+
+ override fun isNewline(): Boolean = Newlines.isNewline(text)
+
+ override fun isSlashSlashComment(): Boolean = text.startsWith("//")
+
+ override fun isSlashStarComment(): Boolean = text.startsWith("/*")
+
+ override fun isJavadocComment(): Boolean = text.startsWith("/**") && text.length > 4
+
+ override fun isComment(): Boolean = isSlashSlashComment || isSlashStarComment
+
+ fun kind(): KtToken = kind
+
+ override fun toString(): String {
+ return MoreObjects.toStringHelper(this)
+ .add("index", index)
+ .add("text", text)
+ .add("position", position)
+ .add("columnI", columnI)
+ .add("isToken", isToken)
+ .toString()
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/KotlinToken.kt b/core/src/main/java/com/facebook/ktfmt/format/KotlinToken.kt
new file mode 100644
index 0000000..eacc431
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/KotlinToken.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.base.MoreObjects
+import com.google.common.collect.ImmutableList
+import com.google.googlejavaformat.Input
+
+class KotlinToken(
+ private val toksBefore: ImmutableList<KotlinTok>,
+ private val kotlinTok: KotlinTok,
+ private val toksAfter: ImmutableList<KotlinTok>
+) : Input.Token {
+
+ override fun getTok(): KotlinTok = kotlinTok
+
+ override fun getToksBefore(): ImmutableList<out Input.Tok> = toksBefore
+
+ override fun getToksAfter(): ImmutableList<out Input.Tok> = toksAfter
+
+ override fun toString(): String {
+ return MoreObjects.toStringHelper(this)
+ .add("tok", kotlinTok)
+ .add("toksBefore", toksBefore)
+ .add("toksAfter", toksAfter)
+ .toString()
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/ParseError.kt b/core/src/main/java/com/facebook/ktfmt/format/ParseError.kt
new file mode 100644
index 0000000..216dfb6
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/ParseError.kt
@@ -0,0 +1,23 @@
+/*
+ * 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 org.jetbrains.kotlin.com.intellij.openapi.util.text.LineColumn
+
+class ParseError(val errorDescription: String, val lineColumn: LineColumn) :
+ IllegalArgumentException(
+ "${lineColumn.line + 1}:${lineColumn.column + 1}: error: $errorDescription")
diff --git a/core/src/main/java/com/facebook/ktfmt/format/Parser.kt b/core/src/main/java/com/facebook/ktfmt/format/Parser.kt
new file mode 100644
index 0000000..d8e4791
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/Parser.kt
@@ -0,0 +1,74 @@
+/*
+ * 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 org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
+import org.jetbrains.kotlin.cli.common.messages.MessageRenderer.PLAIN_RELATIVE_PATHS
+import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
+import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer
+import org.jetbrains.kotlin.com.intellij.openapi.util.text.StringUtil
+import org.jetbrains.kotlin.com.intellij.psi.PsiErrorElement
+import org.jetbrains.kotlin.com.intellij.psi.PsiManager
+import org.jetbrains.kotlin.com.intellij.testFramework.LightVirtualFile
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.idea.KotlinFileType
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+/** Parser parses a Kotlin file given as a string and returns its parse tree. */
+object Parser {
+
+ /**
+ * The environment used to open a KtFile
+ *
+ * We allocate it once, and ignore the Diposable. This causes a memory leak, but it was also
+ * causing a worse memory leak before when we created new ones and disposed them. This leak comes
+ * from [KotlinCoreEnvironment.createForProduction]:
+ * https://github.com/JetBrains/kotlin/blob/master/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt#L544
+ */
+ val env: KotlinCoreEnvironment
+
+ init {
+ // To hide annoying warning on Windows
+ System.setProperty("idea.use.native.fs.for.win", "false")
+ val disposable = Disposer.newDisposable()
+ val configuration = CompilerConfiguration()
+ configuration.put(
+ CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY,
+ PrintingMessageCollector(System.err, PLAIN_RELATIVE_PATHS, false))
+ env =
+ KotlinCoreEnvironment.createForProduction(
+ disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
+ }
+
+ fun parse(code: String): KtFile {
+ val virtualFile = LightVirtualFile("temp.kts", KotlinFileType.INSTANCE, code)
+ val ktFile = PsiManager.getInstance(env.project).findFile(virtualFile) as KtFile
+ ktFile.collectDescendantsOfType<PsiErrorElement>().let {
+ if (it.isNotEmpty()) throwParseError(code, it[0])
+ }
+ return ktFile
+ }
+
+ private fun throwParseError(fileContents: String, error: PsiErrorElement): Nothing {
+ throw ParseError(
+ error.errorDescription, StringUtil.offsetToLineColumn(fileContents, error.startOffset))
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/RedundantElementRemover.kt b/core/src/main/java/com/facebook/ktfmt/format/RedundantElementRemover.kt
new file mode 100644
index 0000000..a073438
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/RedundantElementRemover.kt
@@ -0,0 +1,73 @@
+/*
+ * 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 org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocImpl
+import org.jetbrains.kotlin.psi.KtImportList
+import org.jetbrains.kotlin.psi.KtPackageDirective
+import org.jetbrains.kotlin.psi.KtReferenceExpression
+import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+/** Removes elements that are not needed in the code, such as semicolons and unused imports. */
+object RedundantElementRemover {
+ /** Remove extra semicolons and unused imports, if enabled in the [options] */
+ fun dropRedundantElements(code: String, options: FormattingOptions): String {
+ val file = Parser.parse(code)
+ val redundantImportDetector = RedundantImportDetector(enabled = options.removeUnusedImports)
+ val redundantSemicolonDetector = RedundantSemicolonDetector()
+
+ file.accept(
+ object : KtTreeVisitorVoid() {
+ override fun visitElement(element: PsiElement) {
+ if (element is KDocImpl) {
+ redundantImportDetector.takeKdoc(element)
+ } else {
+ redundantSemicolonDetector.takeElement(element) { super.visitElement(element) }
+ }
+ }
+
+ override fun visitPackageDirective(directive: KtPackageDirective) {
+ redundantImportDetector.takePackageDirective(directive) {
+ super.visitPackageDirective(directive)
+ }
+ }
+
+ override fun visitImportList(importList: KtImportList) {
+ redundantImportDetector.takeImportList(importList) { super.visitImportList(importList) }
+ }
+
+ override fun visitReferenceExpression(expression: KtReferenceExpression) {
+ redundantImportDetector.takeReferenceExpression(expression)
+ super.visitReferenceExpression(expression)
+ }
+ })
+
+ val result = StringBuilder(code)
+ val elementsToRemove =
+ redundantSemicolonDetector.getRedundantSemicolonElements() +
+ redundantImportDetector.getRedundantImportElements()
+
+ for (element in elementsToRemove.sortedByDescending(PsiElement::endOffset)) {
+ result.replace(element.startOffset, element.endOffset, "")
+ }
+
+ return result.toString()
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/RedundantImportDetector.kt b/core/src/main/java/com/facebook/ktfmt/format/RedundantImportDetector.kt
new file mode 100644
index 0000000..7efa893
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/RedundantImportDetector.kt
@@ -0,0 +1,179 @@
+/*
+ * 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 org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocImpl
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtImportDirective
+import org.jetbrains.kotlin.psi.KtImportList
+import org.jetbrains.kotlin.psi.KtPackageDirective
+import org.jetbrains.kotlin.psi.KtReferenceExpression
+import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
+
+internal class RedundantImportDetector(val enabled: Boolean) {
+ companion object {
+ private val OPERATORS =
+ setOf(
+ // Unary prefix operators
+ "unaryPlus",
+ "unaryMinus",
+ "not",
+ // Increments and decrements
+ "inc",
+ "dec",
+ // Arithmetic operators
+ "plus",
+ "minus",
+ "times",
+ "div",
+ "rem",
+ "mod", // deprecated
+ "rangeTo",
+ // 'In' operator
+ "contains",
+ // Indexed access operator
+ "get",
+ "set",
+ // Invoke operator
+ "invoke",
+ // Augmented assignments
+ "plusAssign",
+ "minusAssign",
+ "timesAssign",
+ "divAssign",
+ "remAssign",
+ "modAssign", // deprecated
+ // Equality and inequality operators
+ "equals",
+ // Comparison operators
+ "compareTo",
+ // Iterator operators
+ "iterator",
+ "next",
+ "hasNext",
+ // Bitwise operators
+ "and",
+ "or",
+ // Property delegation operators
+ "getValue",
+ "setValue",
+ "provideDelegate")
+
+ private val COMPONENT_OPERATOR_REGEX = Regex("component\\d+")
+
+ private val KDOC_TAG_SKIP_FIRST_REFERENCE_REGEX = Regex("^@(param|property) (.+)")
+ }
+
+ private var thisPackage: FqName? = null
+
+ private val usedReferences = OPERATORS.toMutableSet()
+
+ private lateinit var importCleanUpCandidates: Set<KtImportDirective>
+
+ private var isPackageElement = false
+ private var isImportElement = false
+
+ fun takePackageDirective(directive: KtPackageDirective, superBlock: () -> Unit) {
+ if (!enabled) {
+ return superBlock.invoke()
+ }
+
+ thisPackage = directive.fqName
+
+ isPackageElement = true
+ superBlock.invoke()
+ isPackageElement = false
+ }
+
+ fun takeImportList(importList: KtImportList, superBlock: () -> Unit) {
+ if (!enabled) {
+ return superBlock.invoke()
+ }
+
+ importCleanUpCandidates =
+ importList.imports
+ .filter { import ->
+ import.isValidImport &&
+ !import.isAllUnder &&
+ import.identifier != null &&
+ requireNotNull(import.identifier) !in OPERATORS &&
+ !COMPONENT_OPERATOR_REGEX.matches(import.identifier.orEmpty())
+ }
+ .toSet()
+
+ isImportElement = true
+ superBlock.invoke()
+ isImportElement = false
+ }
+
+ fun takeKdoc(kdoc: KDocImpl) {
+ kdoc.getChildrenOfType<KDocSection>().forEach { kdocSection ->
+ val tagLinks =
+ kdocSection.getChildrenOfType<KDocTag>().flatMap { tag ->
+ val tagLinks = tag.getChildrenOfType<KDocLink>().toList()
+ when {
+ KDOC_TAG_SKIP_FIRST_REFERENCE_REGEX.matches(tag.text) -> tagLinks.drop(1)
+ else -> tagLinks
+ }
+ }
+
+ val links = kdocSection.getChildrenOfType<KDocLink>() + tagLinks
+
+ val references =
+ links.flatMap { link ->
+ link.getChildrenOfType<KDocName>().mapNotNull {
+ it.getQualifiedName().firstOrNull()?.trim('[', ']')
+ }
+ }
+
+ usedReferences += references
+ }
+ }
+
+ fun takeReferenceExpression(expression: KtReferenceExpression) {
+ if (!enabled) return
+
+ if (!isPackageElement && !isImportElement && expression.children.isEmpty()) {
+ usedReferences += expression.text.trim('`')
+ }
+ }
+
+ fun getRedundantImportElements(): List<PsiElement> {
+ if (!enabled) return emptyList()
+
+ val redundantImports = mutableListOf<PsiElement>()
+
+ // Collect unused imports
+ for (import in importCleanUpCandidates) {
+ val isUnused = import.aliasName !in usedReferences && import.identifier !in usedReferences
+ val isFromSamePackage = import.importedFqName?.parent() == thisPackage && import.alias == null
+ if (isUnused || isFromSamePackage) {
+ redundantImports += import
+ }
+ }
+
+ return redundantImports
+ }
+
+ private inline val KtImportDirective.identifier: String?
+ get() = importPath?.importedName?.identifier?.trim('`')
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/RedundantSemicolonDetector.kt b/core/src/main/java/com/facebook/ktfmt/format/RedundantSemicolonDetector.kt
new file mode 100644
index 0000000..3f6601b
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/RedundantSemicolonDetector.kt
@@ -0,0 +1,83 @@
+/*
+ * 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 org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtContainerNodeForControlStructureBody
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtEnumEntry
+import org.jetbrains.kotlin.psi.KtIfExpression
+import org.jetbrains.kotlin.psi.KtLambdaExpression
+import org.jetbrains.kotlin.psi.KtStringTemplateEntry
+import org.jetbrains.kotlin.psi.KtStringTemplateExpression
+import org.jetbrains.kotlin.psi.KtWhileExpression
+import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
+import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments
+import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
+import org.jetbrains.kotlin.psi.psiUtil.siblings
+
+internal class RedundantSemicolonDetector {
+ private val extraSemicolons = mutableListOf<PsiElement>()
+
+ fun getRedundantSemicolonElements(): List<PsiElement> = extraSemicolons
+
+ /** returns **true** if this element was an extra comma, **false** otherwise. */
+ fun takeElement(element: PsiElement, superBlock: () -> Unit) {
+ if (isExtraSemicolon(element)) {
+ extraSemicolons += element
+ } else {
+ superBlock.invoke()
+ }
+ }
+
+ private fun isExtraSemicolon(element: PsiElement): Boolean {
+ if (element.text != ";") {
+ return false
+ }
+
+ val parent = element.parent
+ if (parent is KtStringTemplateExpression || parent is KtStringTemplateEntry) {
+ return false
+ }
+ if (parent is KtEnumEntry &&
+ parent.siblings(forward = true, withItself = false).any { it is KtDeclaration }) {
+ return false
+ }
+
+ val prevLeaf = element.prevLeaf(false)
+ val prevConcreteSibling = element.getPrevSiblingIgnoringWhitespaceAndComments()
+ if ((prevConcreteSibling is KtIfExpression || prevConcreteSibling is KtWhileExpression) &&
+ prevLeaf is KtContainerNodeForControlStructureBody &&
+ prevLeaf.text.isEmpty()) {
+ return false
+ }
+
+ val nextConcreteSibling = element.getNextSiblingIgnoringWhitespaceAndComments()
+ if (nextConcreteSibling is KtLambdaExpression) {
+ /**
+ * Example: `val x = foo(0) ; { dead -> lambda }`
+ *
+ * There are a huge number of cases here because the trailing lambda syntax is so flexible.
+ * Therefore, we just assume that all semicolons followed by lambdas are meaningful. The cases
+ * where they could be removed are too rare to justify the risk of changing behaviour.
+ */
+ return false
+ }
+
+ return true
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/Tokenizer.kt b/core/src/main/java/com/facebook/ktfmt/format/Tokenizer.kt
new file mode 100644
index 0000000..ababba0
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/Tokenizer.kt
@@ -0,0 +1,111 @@
+/*
+ * 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 java.util.regex.Pattern
+import org.jetbrains.kotlin.com.intellij.psi.PsiComment
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace
+import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtStringTemplateExpression
+import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+/**
+ * Tokenizer traverses a Kotlin parse tree (which blessedly contains whitespaces and comments,
+ * unlike Javac) and constructs a list of 'Tok's.
+ *
+ * <p>The google-java-format infra expects newline Toks to be separate from maximal-whitespace Toks,
+ * but Kotlin emits them together. So, we split them using Java's \R regex matcher. We don't use
+ * 'split' et al. because we want Toks for the newlines themselves.
+ */
+class Tokenizer(private val fileText: String, val file: KtFile) : KtTreeVisitorVoid() {
+
+ companion object {
+ private val WHITESPACE_NEWLINE_REGEX: Pattern = Pattern.compile("\\R|( )+")
+ }
+
+ val toks = mutableListOf<KotlinTok>()
+ var index = 0
+
+ override fun visitElement(element: PsiElement) {
+ val startIndex = element.startOffset
+ when (element) {
+ is PsiComment -> {
+ toks.add(
+ KotlinTok(
+ index,
+ fileText.substring(startIndex, element.endOffset),
+ element.text,
+ startIndex,
+ 0,
+ false,
+ KtTokens.EOF))
+ index++
+ return
+ }
+ is KtStringTemplateExpression -> {
+ toks.add(
+ KotlinTok(
+ index,
+ WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone(
+ fileText.substring(startIndex, element.endOffset)),
+ element.text,
+ startIndex,
+ 0,
+ true,
+ KtTokens.EOF))
+ index++
+ return
+ }
+ is LeafPsiElement -> {
+ val elementText = element.text
+ val endIndex = element.endOffset
+ if (element is PsiWhiteSpace) {
+ val matcher = WHITESPACE_NEWLINE_REGEX.matcher(elementText)
+ while (matcher.find()) {
+ val text = matcher.group()
+ toks.add(
+ KotlinTok(
+ -1,
+ fileText.substring(startIndex + matcher.start(), startIndex + matcher.end()),
+ text,
+ startIndex + matcher.start(),
+ 0,
+ false,
+ KtTokens.EOF))
+ }
+ } else {
+ toks.add(
+ KotlinTok(
+ index,
+ fileText.substring(startIndex, endIndex),
+ elementText,
+ startIndex,
+ 0,
+ true,
+ KtTokens.EOF))
+ index++
+ }
+ }
+ }
+ super.visitElement(element)
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/TypeNameClassifier.kt b/core/src/main/java/com/facebook/ktfmt/format/TypeNameClassifier.kt
new file mode 100644
index 0000000..eba3ccc
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/TypeNameClassifier.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * 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.
+ */
+
+// This was copied from https://github.com/google/google-java-format and converted to Kotlin,
+// because the original is package-private.
+
+package com.facebook.ktfmt.format
+
+import com.google.common.base.Verify
+import java.util.Optional
+
+/** Heuristics for classifying qualified names as types. */
+object TypeNameClassifier {
+
+ /** A state machine for classifying qualified names. */
+ private enum class TyParseState(val isSingleUnit: Boolean) {
+
+ /** The start state. */
+ START(false) {
+ override fun next(n: JavaCaseFormat): TyParseState {
+ return when (n) {
+ JavaCaseFormat.UPPERCASE ->
+ // if we see an UpperCamel later, assume this was a class
+ // e.g. com.google.FOO.Bar
+ AMBIGUOUS
+ JavaCaseFormat.LOWER_CAMEL -> REJECT
+ JavaCaseFormat.LOWERCASE ->
+ // could be a package
+ START
+ JavaCaseFormat.UPPER_CAMEL -> TYPE
+ }
+ }
+ },
+
+ /** The current prefix is a type. */
+ TYPE(true) {
+ override fun next(n: JavaCaseFormat): TyParseState {
+ return when (n) {
+ JavaCaseFormat.UPPERCASE,
+ JavaCaseFormat.LOWER_CAMEL,
+ JavaCaseFormat.LOWERCASE -> FIRST_STATIC_MEMBER
+ JavaCaseFormat.UPPER_CAMEL -> TYPE
+ }
+ }
+ },
+
+ /** The current prefix is a type, followed by a single static member access. */
+ FIRST_STATIC_MEMBER(true) {
+ override fun next(n: JavaCaseFormat): TyParseState {
+ return REJECT
+ }
+ },
+
+ /** Anything not represented by one of the other states. */
+ REJECT(false) {
+ override fun next(n: JavaCaseFormat): TyParseState {
+ return REJECT
+ }
+ },
+
+ /** An ambiguous type prefix. */
+ AMBIGUOUS(false) {
+ override fun next(n: JavaCaseFormat): TyParseState {
+ return when (n) {
+ JavaCaseFormat.UPPERCASE -> AMBIGUOUS
+ JavaCaseFormat.LOWER_CAMEL,
+ JavaCaseFormat.LOWERCASE -> REJECT
+ JavaCaseFormat.UPPER_CAMEL -> TYPE
+ }
+ }
+ };
+
+ /** Transition function. */
+ abstract fun next(n: JavaCaseFormat): TyParseState
+ }
+
+ /**
+ * Returns the end index (inclusive) of the longest prefix that matches the naming conventions of
+ * a type or static field access, or -1 if no such prefix was found.
+ *
+ * Examples:
+ *
+ * * ClassName
+ * * ClassName.staticMemberName
+ * * com.google.ClassName.InnerClass.staticMemberName
+ */
+ internal fun typePrefixLength(nameParts: List<String>): Optional<Int> {
+ var state = TyParseState.START
+ var typeLength = Optional.empty<Int>()
+ for (i in nameParts.indices) {
+ state = state.next(JavaCaseFormat.from(nameParts[i]))
+ if (state === TyParseState.REJECT) {
+ break
+ }
+ if (state.isSingleUnit) {
+ typeLength = Optional.of(i)
+ }
+ }
+ return typeLength
+ }
+
+ /** Case formats used in Java identifiers. */
+ enum class JavaCaseFormat {
+ UPPERCASE,
+ LOWERCASE,
+ UPPER_CAMEL,
+ LOWER_CAMEL;
+
+ companion object {
+
+ /** Classifies an identifier's case format. */
+ internal fun from(name: String): JavaCaseFormat {
+ Verify.verify(name.isNotEmpty())
+ var firstUppercase = false
+ var hasUppercase = false
+ var hasLowercase = false
+ var first = true
+ for (char in name) {
+ if (!Character.isAlphabetic(char.code)) {
+ continue
+ }
+ if (first) {
+ firstUppercase = Character.isUpperCase(char)
+ first = false
+ }
+ hasUppercase = hasUppercase or Character.isUpperCase(char)
+ hasLowercase = hasLowercase or Character.isLowerCase(char)
+ }
+ return if (firstUppercase) {
+ if (hasLowercase) UPPER_CAMEL else UPPERCASE
+ } else {
+ if (hasUppercase) LOWER_CAMEL else LOWERCASE
+ }
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/WhitespaceTombstones.kt b/core/src/main/java/com/facebook/ktfmt/format/WhitespaceTombstones.kt
new file mode 100644
index 0000000..816e137
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/WhitespaceTombstones.kt
@@ -0,0 +1,42 @@
+/*
+ * 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 java.util.regex.Pattern
+import java.util.regex.Pattern.MULTILINE
+
+object WhitespaceTombstones {
+ /** See [replaceTrailingWhitespaceWithTombstone]. */
+ const val SPACE_TOMBSTONE = '\u0003'
+
+ fun String.indexOfWhitespaceTombstone() = this.indexOf(SPACE_TOMBSTONE)
+
+ /**
+ * Google-java-format removes trailing spaces when it emits formatted code, which is a problem for
+ * multiline string literals. We trick it by replacing the last trailing space in such cases with
+ * a tombstone, a character that's unlikely to be used in a regular program. After formatting, we
+ * replace it back to a space.
+ */
+ fun replaceTrailingWhitespaceWithTombstone(s: String): String {
+ return Pattern.compile(" ($)", MULTILINE).matcher(s).replaceAll("$SPACE_TOMBSTONE$1")
+ }
+
+ /** See [replaceTrailingWhitespaceWithTombstone]. */
+ fun replaceTombstoneWithTrailingWhitespace(s: String): String {
+ return s.replace(SPACE_TOMBSTONE, ' ')
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/Escaping.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/Escaping.kt
new file mode 100644
index 0000000..85c9938
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/Escaping.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.kdoc
+
+object Escaping {
+
+ private const val SLASH_STAR_ESCAPE = "\u0004\u0005"
+
+ private const val STAR_SLASH_ESCAPE = "\u0005\u0004"
+
+ fun indexOfCommentEscapeSequences(s: String) =
+ s.indexOfAny(listOf(SLASH_STAR_ESCAPE, STAR_SLASH_ESCAPE))
+
+ /**
+ * kotlin-compiler's KDoc lexer doesn't correctly handle nested slash-star comments, so we escape
+ * them into tombstones, format, then unescape.
+ */
+ fun escapeKDoc(s: String): String {
+ val startMarkerIndex = s.indexOf("/*")
+ val endMarkerIndex = s.lastIndexOf("*/")
+
+ if (startMarkerIndex == -1 || endMarkerIndex == -1) {
+ throw RuntimeException("KDoc with no /** and/or */")
+ }
+
+ return s.substring(0, startMarkerIndex + 3) +
+ s.substring(startMarkerIndex + 3, endMarkerIndex)
+ .replace("/*", SLASH_STAR_ESCAPE)
+ .replace("*/", STAR_SLASH_ESCAPE) +
+ s.substring(endMarkerIndex)
+ }
+
+ /**
+ *
+ * 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/KDocCommentsHelper.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocCommentsHelper.kt
new file mode 100644
index 0000000..a63a332
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocCommentsHelper.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * 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.
+ */
+
+/*
+ * This was copied from https://github.com/google/google-java-format and modified extensively to
+ * work for Kotlin formatting
+ */
+
+package com.facebook.ktfmt.kdoc
+
+import com.google.common.base.CharMatcher
+import com.google.common.base.Strings
+import com.google.googlejavaformat.CommentsHelper
+import com.google.googlejavaformat.Input.Tok
+import com.google.googlejavaformat.Newlines
+import com.google.googlejavaformat.java.Formatter
+import java.util.ArrayList
+import java.util.regex.Pattern
+
+/** `KDocCommentsHelper` extends [CommentsHelper] to rewrite KDoc comments. */
+class KDocCommentsHelper(private val lineSeparator: String, private val maxLineLength: Int) :
+ CommentsHelper {
+
+ override fun rewrite(tok: Tok, maxWidth: Int, column0: Int): String {
+ if (!tok.isComment) {
+ return tok.originalText
+ }
+ var text = tok.originalText
+ if (tok.isJavadocComment) {
+ text = KDocFormatter.formatKDoc(text, column0, maxLineLength)
+ }
+ val lines = ArrayList<String>()
+ val it = Newlines.lineIterator(text)
+ while (it.hasNext()) {
+ lines.add(CharMatcher.whitespace().trimTrailingFrom(it.next()))
+ }
+ return if (tok.isSlashSlashComment) {
+ indentLineComments(lines, column0)
+ } else if (javadocShaped(lines)) {
+ indentJavadoc(lines, column0)
+ } else {
+ preserveIndentation(lines, column0)
+ }
+ }
+
+ // For non-javadoc-shaped block comments, shift the entire block to the correct
+ // column, but do not adjust relative indentation.
+ private fun preserveIndentation(lines: List<String>, column0: Int): String {
+ val builder = StringBuilder()
+
+ // find the leftmost non-whitespace character in all trailing lines
+ var startCol = -1
+ for (i in 1 until lines.size) {
+ val lineIdx = CharMatcher.whitespace().negate().indexIn(lines[i])
+ if (lineIdx >= 0 && (startCol == -1 || lineIdx < startCol)) {
+ startCol = lineIdx
+ }
+ }
+
+ // output the first line at the current column
+ builder.append(lines[0])
+
+ // output all trailing lines with plausible indentation
+ for (i in 1 until lines.size) {
+ builder.append(lineSeparator).append(Strings.repeat(" ", column0))
+ // check that startCol is valid index, e.g. for blank lines
+ if (lines[i].length >= startCol) {
+ builder.append(lines[i].substring(startCol))
+ } else {
+ builder.append(lines[i])
+ }
+ }
+ return builder.toString()
+ }
+
+ // Wraps and re-indents line comments.
+ private fun indentLineComments(lines: List<String>, column0: Int): String {
+ val wrappedLines = wrapLineComments(lines, column0)
+ val builder = StringBuilder()
+ builder.append(wrappedLines[0].trim())
+ val indentString = Strings.repeat(" ", column0)
+ for (i in 1 until wrappedLines.size) {
+ builder.append(lineSeparator).append(indentString).append(wrappedLines[i].trim())
+ }
+ return builder.toString()
+ }
+
+ private fun wrapLineComments(lines: List<String>, column0: Int): List<String> {
+ val result = ArrayList<String>()
+ for (originalLine in lines) {
+ var line = originalLine
+ // Add missing leading spaces to line comments: `//foo` -> `// foo`.
+ val matcher = LINE_COMMENT_MISSING_SPACE_PREFIX.matcher(line)
+ if (matcher.find()) {
+ val length = matcher.group(1).length
+ line = Strings.repeat("/", length) + " " + line.substring(length)
+ }
+ if (line.startsWith("// MOE:")) {
+ // don't wrap comments for https://github.com/google/MOE
+ result.add(line)
+ continue
+ }
+ while (line.length + column0 > Formatter.MAX_LINE_LENGTH) {
+ var idx = Formatter.MAX_LINE_LENGTH - column0
+ // only break on whitespace characters, and ignore the leading `// `
+ while (idx >= 2 && !CharMatcher.whitespace().matches(line[idx])) {
+ idx--
+ }
+ if (idx <= 2) {
+ break
+ }
+ result.add(line.substring(0, idx))
+ line = "//" + line.substring(idx)
+ }
+ result.add(line)
+ }
+ return result
+ }
+
+ // Remove leading whitespace (trailing was already removed), and re-indent.
+ // Add a +1 indent before '*', and add the '*' if necessary.
+ private fun indentJavadoc(lines: List<String>, column0: Int): String {
+ val builder = StringBuilder()
+ builder.append(lines[0].trim())
+ val indent = column0 + 1
+ val indentString = Strings.repeat(" ", indent)
+ for (i in 1 until lines.size) {
+ builder.append(lineSeparator).append(indentString)
+ val line = lines[i].trim()
+ if (!line.startsWith("*")) {
+ builder.append("* ")
+ }
+ builder.append(line)
+ }
+ return builder.toString()
+ }
+
+ // Preserve special `//noinspection` and `//$NON-NLS-x$` comments used by IDEs, which cannot
+ // contain leading spaces.
+ private val LINE_COMMENT_MISSING_SPACE_PREFIX =
+ Pattern.compile("^(//+)(?!noinspection|\\\$NON-NLS-\\d+\\$)[^\\s/]")
+
+ // Returns true if the comment looks like javadoc
+ private fun javadocShaped(lines: List<String>): Boolean {
+ val it = lines.iterator()
+ if (!it.hasNext()) {
+ return false
+ }
+ val first = it.next().trim()
+ // if it's actually javadoc, we're done
+ if (first.startsWith("/**")) {
+ return true
+ }
+ // if it's a block comment, check all trailing lines for '*'
+ if (!first.startsWith("/*")) {
+ return false
+ }
+ while (it.hasNext()) {
+ if (!it.next().trim().startsWith("*")) {
+ return false
+ }
+ }
+ return true
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/KDocFormatter.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocFormatter.kt
new file mode 100644
index 0000000..8c0af07
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocFormatter.kt
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * 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.
+ */
+
+/*
+ * This was copied from https://github.com/google/google-java-format and modified extensively to
+ * work for Kotlin formatting
+ */
+
+package com.facebook.ktfmt.kdoc
+
+import com.facebook.ktfmt.kdoc.KDocToken.Type.BEGIN_KDOC
+import com.facebook.ktfmt.kdoc.KDocToken.Type.BLANK_LINE
+import com.facebook.ktfmt.kdoc.KDocToken.Type.CODE
+import com.facebook.ktfmt.kdoc.KDocToken.Type.CODE_BLOCK_MARKER
+import com.facebook.ktfmt.kdoc.KDocToken.Type.CODE_CLOSE_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.CODE_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.END_KDOC
+import com.facebook.ktfmt.kdoc.KDocToken.Type.LIST_ITEM_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.LITERAL
+import com.facebook.ktfmt.kdoc.KDocToken.Type.MARKDOWN_LINK
+import com.facebook.ktfmt.kdoc.KDocToken.Type.PRE_CLOSE_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.PRE_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.TABLE_CLOSE_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.TABLE_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.WHITESPACE
+import java.util.regex.Pattern.compile
+import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType
+import org.jetbrains.kotlin.kdoc.lexer.KDocLexer
+import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
+import org.jetbrains.kotlin.lexer.KtTokens.WHITE_SPACE
+
+/**
+ * Entry point for formatting KDoc.
+ *
+ * This stateless class reads tokens from the stateful lexer and translates them to "requests" and
+ * "writes" to the stateful writer. It also munges tokens into "standardized" forms. Finally, it
+ * performs postprocessing to convert the written KDoc to a one-liner if possible or to leave a
+ * single blank line if it's empty.
+ */
+object KDocFormatter {
+
+ private val ONE_CONTENT_LINE_PATTERN = compile(" */[*][*]\n *[*] (.*)\n *[*]/")
+
+ private val NUMBERED_LIST_PATTERN = "[0-9]+\\.".toRegex()
+
+ /**
+ * Formats the given Javadoc comment, which must start with ∕✱✱ and end with ✱∕. The output will
+ * start and end with the same characters.
+ */
+ fun formatKDoc(input: String, blockIndent: Int, maxLineLength: Int): String {
+ val escapedInput = Escaping.escapeKDoc(input)
+ val kDocLexer = KDocLexer()
+ kDocLexer.start(escapedInput)
+ val tokens = mutableListOf<KDocToken>()
+ var previousType: IElementType? = null
+ while (kDocLexer.tokenType != null) {
+ val tokenType = kDocLexer.tokenType
+ val tokenText =
+ with(kDocLexer.tokenText) {
+ if (previousType == KDocTokens.LEADING_ASTERISK && first() == ' ') substring(1)
+ else this
+ }
+
+ processToken(tokenType, tokens, tokenText, previousType)
+
+ previousType = tokenType
+ kDocLexer.advance()
+ }
+ val result = render(tokens, blockIndent, maxLineLength)
+ return makeSingleLineIfPossible(blockIndent, result, maxLineLength)
+ }
+
+ private fun processToken(
+ tokenType: IElementType?,
+ tokens: MutableList<KDocToken>,
+ tokenText: String,
+ previousType: IElementType?
+ ) {
+ when (tokenType) {
+ KDocTokens.START -> tokens.add(KDocToken(BEGIN_KDOC, tokenText))
+ KDocTokens.END -> tokens.add(KDocToken(END_KDOC, tokenText))
+ KDocTokens.LEADING_ASTERISK -> Unit // Ignore, no need to output anything
+ KDocTokens.TAG_NAME -> tokens.add(KDocToken(TAG, tokenText))
+ KDocTokens.CODE_BLOCK_TEXT -> tokens.add(KDocToken(CODE, tokenText))
+ KDocTokens.MARKDOWN_INLINE_LINK,
+ KDocTokens.MARKDOWN_LINK -> {
+ tokens.add(KDocToken(MARKDOWN_LINK, tokenText))
+ }
+ KDocTokens.MARKDOWN_ESCAPED_CHAR,
+ KDocTokens.TEXT -> {
+ var first = true
+ for (word in tokenizeKdocText(tokenText)) {
+ if (word.first().isWhitespace()) {
+ tokens.add(KDocToken(WHITESPACE, " "))
+ continue
+ }
+ if (first) {
+ if (word == "-" || word == "*" || word.matches(NUMBERED_LIST_PATTERN)) {
+ tokens.add(KDocToken(LIST_ITEM_OPEN_TAG, ""))
+ }
+ first = false
+ }
+ // If the KDoc is malformed (e.g. unclosed code block) KDocLexer doesn't report an
+ // END_KDOC properly. We want to recover in such cases
+ if (word == "*/") {
+ tokens.add(KDocToken(END_KDOC, word))
+ } else if (word.startsWith("```")) {
+ tokens.add(KDocToken(CODE_BLOCK_MARKER, word))
+ } else {
+ tokens.add(KDocToken(LITERAL, word))
+ }
+ }
+ }
+ WHITE_SPACE -> {
+ if (previousType == KDocTokens.LEADING_ASTERISK || tokenText.count { it == '\n' } >= 2) {
+ tokens.add(KDocToken(BLANK_LINE, ""))
+ } else {
+ tokens.add(KDocToken(WHITESPACE, " "))
+ }
+ }
+ else -> throw RuntimeException("Unexpected: $tokenType")
+ }
+ }
+ private fun render(input: List<KDocToken>, blockIndent: Int, maxLineLength: Int): String {
+ val output = KDocWriter(blockIndent, maxLineLength)
+ for (token in input) {
+ when (token.type) {
+ BEGIN_KDOC -> output.writeBeginJavadoc()
+ END_KDOC -> {
+ output.writeEndJavadoc()
+ return Escaping.unescapeKDoc(output.toString())
+ }
+ LIST_ITEM_OPEN_TAG -> output.writeListItemOpen(token)
+ PRE_OPEN_TAG -> output.writePreOpen(token)
+ PRE_CLOSE_TAG -> output.writePreClose(token)
+ CODE_OPEN_TAG -> output.writeCodeOpen(token)
+ CODE_CLOSE_TAG -> output.writeCodeClose(token)
+ TABLE_OPEN_TAG -> output.writeTableOpen(token)
+ TABLE_CLOSE_TAG -> output.writeTableClose(token)
+ TAG -> output.writeTag(token)
+ CODE -> output.writeCodeLine(token)
+ CODE_BLOCK_MARKER -> output.writeExplicitCodeBlockMarker(token)
+ BLANK_LINE -> output.requestBlankLine()
+ WHITESPACE -> output.requestWhitespace()
+ LITERAL -> output.writeLiteral(token)
+ MARKDOWN_LINK -> output.writeMarkdownLink(token)
+ else -> throw AssertionError(token.type)
+ }
+ }
+ throw AssertionError()
+ }
+
+ /**
+ * Returns the given string or a one-line version of it (e.g., "∕✱✱ Tests for foos. ✱∕") if it
+ * fits on one line.
+ */
+ private fun makeSingleLineIfPossible(
+ blockIndent: Int,
+ input: String,
+ maxLineLength: Int
+ ): String {
+ val oneLinerContentLength = maxLineLength - "/** */".length - blockIndent
+ val matcher = ONE_CONTENT_LINE_PATTERN.matcher(input)
+ if (matcher.matches() && matcher.group(1).isEmpty()) {
+ return "/** */"
+ } else if (matcher.matches() && matcher.group(1).length <= oneLinerContentLength) {
+ return "/** " + matcher.group(1) + " */"
+ }
+ return input
+ }
+
+ /**
+ * tokenizeKdocText splits 's' by whitespace, and returns both whitespace and non-whitespace
+ * parts.
+ *
+ * Multiple adjacent whitespace characters are collapsed into one. Trailing and leading spaces are
+ * included in the result.
+ *
+ * Example: `" one two three "` becomes `[" ", "one", " ", "two", " ", "three", " "]`. See tests
+ * for more examples.
+ */
+ fun tokenizeKdocText(s: String) = sequence {
+ if (s.isEmpty()) {
+ return@sequence
+ }
+ var mark = 0
+ var inWhitespace = s[0].isWhitespace()
+ for (i in 1..s.lastIndex) {
+ if (inWhitespace == s[i].isWhitespace()) {
+ continue
+ }
+ val result = if (inWhitespace) " " else s.substring(mark, i)
+ inWhitespace = s[i].isWhitespace()
+ mark = i
+ yield(result)
+ }
+ yield(if (inWhitespace) " " else s.substring(mark, s.length))
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/KDocToken.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocToken.kt
new file mode 100644
index 0000000..51801c8
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocToken.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * 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.
+ */
+
+/*
+ * This was copied from https://github.com/google/google-java-format and modified extensively to
+ * work for Kotlin formatting
+ */
+
+package com.facebook.ktfmt.kdoc
+
+/**
+ * KDoc token. Our idea of what constitutes a token is often larger or smaller than what you'd
+ * naturally expect. The decision is usually pragmatic rather than theoretical. Most of the details
+ * are in [KDocFormatter].
+ */
+internal class KDocToken(val type: Type, val value: String) {
+ /**
+ * KDoc token type.
+ *
+ * The general idea is that every token that requires special handling (extra line breaks,
+ * indentation, forcing or forbidding whitespace) from [KDocWriter] gets its own type. But I
+ * haven't been super careful about it, so I'd imagine that we could merge or remove some of these
+ * if we wanted. (For example, PARAGRAPH_CLOSE_TAG and LIST_ITEM_CLOSE_TAG could share a common
+ * IGNORABLE token type. But their corresponding OPEN tags exist, so I've kept the CLOSE tags.)
+ *
+ * Note, though, that tokens of the same type may still have been handled differently by [ ] when
+ * it created them. For example, LITERAL is used for both plain text and inline tags, even though
+ * the two affect the lexer's state differently.
+ */
+ internal enum class Type {
+ /** ∕✱✱ */
+ BEGIN_KDOC,
+ /** ✱∕ */
+ END_KDOC,
+ LIST_ITEM_OPEN_TAG,
+ HEADER_OPEN_TAG,
+ PARAGRAPH_OPEN_TAG,
+ PRE_OPEN_TAG,
+ PRE_CLOSE_TAG,
+ CODE_OPEN_TAG,
+ CODE_CLOSE_TAG,
+ TABLE_OPEN_TAG,
+ TABLE_CLOSE_TAG,
+ /** Things such as `@param` or `@see` */
+ TAG,
+ /** Code between two markers of three backticks */
+ CODE,
+ /** three backticks */
+ CODE_BLOCK_MARKER,
+ /** A link in brackets such as [KDocToken] */
+ MARKDOWN_LINK,
+ BLANK_LINE,
+ /**
+ * Whitespace that is not in a `<pre>` or `<table>` section. Whitespace includes leading
+ * newlines, asterisks, and tabs and spaces. In the output, it is translated to newlines (with
+ * leading spaces and asterisks) or spaces.
+ */
+ WHITESPACE,
+ /**
+ * Anything else: `foo`, `<b>`, `{ foo}` etc. [KDocFormatter] sometimes creates adjacent literal
+ * tokens, which it then merges into a single, larger literal token before returning its output.
+ *
+ * This also includes whitespace in a `<pre>` or `<table>` section. We preserve user formatting
+ * in these sections, including arbitrary numbers of spaces. By treating such whitespace as a
+ * literal, we can merge it with adjacent literals, preventing us from autowrapping inside these
+ * sections -- and doing so naively, to boot. The wrapped line would have no indentation after
+ * "* " or, possibly worse, it might begin with an arbitrary amount of whitespace that didn't
+ * fit on the previous line. Of course, by doing this, we're potentially creating lines of more
+ * than 100 characters. But it seems fair to call in the humans to resolve such problems.
+ */
+ LITERAL
+ }
+
+ fun length(): Int = value.length
+
+ override fun toString(): String = "KDocToken{$type: \"$value\"}"
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/KDocWriter.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocWriter.kt
new file mode 100644
index 0000000..8d7db5d
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocWriter.kt
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * 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.
+ */
+
+/*
+ * This was copied from https://github.com/google/google-java-format and modified extensively to
+ * work for Kotlin formatting
+ */
+
+package com.facebook.ktfmt.kdoc
+
+import com.facebook.ktfmt.kdoc.KDocToken.Type.CODE_BLOCK_MARKER
+import com.facebook.ktfmt.kdoc.KDocToken.Type.HEADER_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.LIST_ITEM_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.PARAGRAPH_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocWriter.RequestedWhitespace.BLANK_LINE
+import com.facebook.ktfmt.kdoc.KDocWriter.RequestedWhitespace.CONDITIONAL_WHITESPACE
+import com.facebook.ktfmt.kdoc.KDocWriter.RequestedWhitespace.NEWLINE
+import com.facebook.ktfmt.kdoc.KDocWriter.RequestedWhitespace.NONE
+import com.facebook.ktfmt.kdoc.KDocWriter.RequestedWhitespace.WHITESPACE
+import com.google.common.base.Strings
+import com.google.common.collect.Ordering
+import com.google.common.collect.Sets.immutableEnumSet
+
+/**
+ * Stateful object that accepts "requests" and "writes," producing formatted Javadoc.
+ *
+ * Our Javadoc formatter doesn't ever generate a parse tree, only a stream of tokens, so the writer
+ * must compute and store the answer to questions like "How many levels of nested HTML list are we
+ * inside?"
+ */
+internal class KDocWriter(blockIndentCount: Int, private val maxLineLength: Int) {
+
+ /**
+ * Tokens that are always pinned to the following token. For example, `<p>` in `<p>Foo bar` (never
+ * `<p> Foo bar` or `<p>\nFoo bar`).
+ *
+ * This is not the only kind of "pinning" that we do: See also the joining of LITERAL tokens done
+ * by the lexer. The special pinning here is necessary because these tokens are not of type
+ * LITERAL (because they require other special handling).
+ */
+ private val START_OF_LINE_TOKENS =
+ immutableEnumSet(LIST_ITEM_OPEN_TAG, PARAGRAPH_OPEN_TAG, HEADER_OPEN_TAG)
+
+ private val output = StringBuilder()
+ private val blockIndent = Strings.repeat(" ", blockIndentCount + 1)
+
+ private var remainingOnLine: Int = 0
+ private var atStartOfLine: Boolean = false
+ private var inCodeBlock: Boolean = false
+ private var requestedWhitespace = NONE
+
+ /**
+ * Requests whitespace between the previously written token and the next written token. The
+ * request may be honored, or it may be overridden by a request for "more significant" whitespace,
+ * like a newline.
+ */
+ fun requestWhitespace() {
+ requestWhitespace(WHITESPACE)
+ }
+
+ fun writeBeginJavadoc() {
+ /*
+ * JavaCommentsHelper will make sure this is indented right. But it seems sensible enough that,
+ * if our input starts with ∕✱✱, so too does our output.
+ */
+ appendTrackingLength("/**")
+ }
+
+ fun writeEndJavadoc() {
+ requestCloseCodeBlockMarker()
+ output.append("\n")
+ appendTrackingLength(blockIndent)
+ appendTrackingLength("*/")
+ }
+
+ fun writeListItemOpen(token: KDocToken) {
+ requestCloseCodeBlockMarker()
+ requestNewline()
+ writeToken(token)
+ }
+
+ fun writePreOpen(token: KDocToken) {
+ requestBlankLine()
+
+ writeToken(token)
+ }
+
+ fun writePreClose(token: KDocToken) {
+ writeToken(token)
+
+ requestBlankLine()
+ }
+
+ fun writeCodeOpen(token: KDocToken) {
+ writeToken(token)
+ }
+
+ fun writeCodeClose(token: KDocToken) {
+ writeToken(token)
+ }
+
+ fun writeTableOpen(token: KDocToken) {
+ requestBlankLine()
+
+ writeToken(token)
+ }
+
+ fun writeTableClose(token: KDocToken) {
+ writeToken(token)
+
+ requestBlankLine()
+ }
+
+ fun writeTag(token: KDocToken) {
+ requestNewline()
+ writeToken(token)
+ }
+
+ fun writeCodeLine(token: KDocToken) {
+ requestOpenCodeBlockMarker()
+ requestNewline()
+ if (token.value.isNotEmpty()) {
+ writeToken(token)
+ }
+ }
+
+ /** Adds a code block marker if we are not in a code block currently */
+ private fun requestCloseCodeBlockMarker() {
+ if (inCodeBlock) {
+ this.requestedWhitespace = NEWLINE
+ writeExplicitCodeBlockMarker(KDocToken(CODE_BLOCK_MARKER, "```"))
+ }
+ }
+
+ /** Adds a code block marker if we are in a code block currently */
+ private fun requestOpenCodeBlockMarker() {
+ if (!inCodeBlock) {
+ this.requestedWhitespace = NEWLINE
+ writeExplicitCodeBlockMarker(KDocToken(CODE_BLOCK_MARKER, "```"))
+ }
+ }
+
+ fun writeExplicitCodeBlockMarker(token: KDocToken) {
+ requestNewline()
+ writeToken(token)
+ requestNewline()
+ inCodeBlock = !inCodeBlock
+ }
+
+ fun writeLiteral(token: KDocToken) {
+ requestCloseCodeBlockMarker()
+
+ writeToken(token)
+ }
+
+ fun writeMarkdownLink(token: KDocToken) {
+ writeToken(token)
+ }
+
+ override fun toString(): String {
+ return output.toString()
+ }
+
+ fun requestBlankLine() {
+ requestWhitespace(BLANK_LINE)
+ }
+
+ fun requestNewline() {
+ requestWhitespace(NEWLINE)
+ }
+
+ private fun requestWhitespace(requestedWhitespace: RequestedWhitespace) {
+ this.requestedWhitespace =
+ Ordering.natural<Comparable<*>>().max(requestedWhitespace, this.requestedWhitespace)
+ }
+
+ /**
+ * The kind of whitespace that has been requested between the previous and next tokens. The order
+ * of the values is significant: It goes from lowest priority to highest. For example, if the
+ * previous token requests [.BLANK_LINE] after it but the next token requests only [ ][.NEWLINE]
+ * before it, we insert [.BLANK_LINE].
+ */
+ internal enum class RequestedWhitespace {
+ NONE,
+
+ /**
+ * Add one space, only if the next token seems like a word In contrast, punctuation like a dot
+ * does need a space before it.
+ */
+ CONDITIONAL_WHITESPACE,
+
+ /** Add one space, e.g. " " */
+ WHITESPACE,
+
+ /** Break to the next line */
+ NEWLINE,
+
+ /** Add a whole blank line between the two lines of content */
+ BLANK_LINE,
+ }
+
+ private fun writeToken(token: KDocToken) {
+ if (requestedWhitespace == BLANK_LINE) {
+ writeBlankLine()
+ requestedWhitespace = NONE
+ } else if (requestedWhitespace == NEWLINE) {
+ writeNewline()
+ requestedWhitespace = NONE
+ }
+
+ val needWhitespace =
+ when (requestedWhitespace) {
+ WHITESPACE -> true
+ CONDITIONAL_WHITESPACE -> token.value.first().isLetterOrDigit()
+ else -> false
+ }
+ /*
+ * Write a newline if necessary to respect the line limit. (But if we're at the beginning of the
+ * line, a newline won't help. Or it might help but only by separating "<p>veryverylongword,"
+ * which goes against our style.)
+ */
+ if (!atStartOfLine && token.length() + (if (needWhitespace) 1 else 0) > remainingOnLine) {
+ writeNewline()
+ }
+ if (!atStartOfLine && needWhitespace) {
+ appendTrackingLength(" ")
+ }
+
+ appendTrackingLength(token.value)
+ requestedWhitespace = NONE
+
+ if (!START_OF_LINE_TOKENS.contains(token.type)) {
+ atStartOfLine = false
+ }
+ }
+
+ private fun writeBlankLine() {
+ output.append("\n")
+ appendTrackingLength(blockIndent)
+ appendTrackingLength("*")
+ writeNewline()
+ }
+
+ private fun writeNewline() {
+ output.append("\n")
+ remainingOnLine = maxLineLength
+ appendTrackingLength(blockIndent)
+ appendTrackingLength("* ")
+ atStartOfLine = true
+ }
+
+ /*
+ * TODO(cpovirk): We really want the number of "characters," not chars. Figure out what the
+ * right way of measuring that is (grapheme count (with BreakIterator?)? sum of widths of all
+ * graphemes? I don't think that our style guide is specific about this.). Moreover, I am
+ * probably brushing other problems with surrogates, etc. under the table. Hopefully I mostly
+ * get away with it by joining all non-space, non-tab characters together.
+ *
+ * Possibly the "width" question has no right answer:
+ * http://denisbider.blogspot.com/2015/09/when-monospace-fonts-arent-unicode.html
+ */
+ private fun appendTrackingLength(str: String) {
+ output.append(str)
+ remainingOnLine -= str.length
+ }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/NestingCounter.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/NestingCounter.kt
new file mode 100644
index 0000000..e744089
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/NestingCounter.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * 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.
+ */
+
+/*
+ * This was copied from https://github.com/google/google-java-format and modified extensively to
+ * work for Kotlin formatting
+ */
+
+package com.facebook.ktfmt.kdoc
+
+/** Mutable integer for tracking the level of nesting. */
+internal class NestingCounter {
+ private var value: Int = 0
+
+ val isPositive: Boolean
+ get() = value > 0
+
+ fun value(): Int {
+ return value
+ }
+
+ fun increment() {
+ value++
+ }
+
+ fun incrementIfPositive() {
+ if (value > 0) {
+ value++
+ }
+ }
+
+ fun decrementIfPositive() {
+ if (value > 0) {
+ value--
+ }
+ }
+
+ fun reset() {
+ value = 0
+ }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt b/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt
new file mode 100644
index 0000000..c02aca6
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt
@@ -0,0 +1,432 @@
+/*
+ * 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.cli
+
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.PrintStream
+import java.lang.IllegalStateException
+import java.nio.file.Files
+import java.util.concurrent.ForkJoinPool
+import kotlin.io.path.createTempDirectory
+import org.junit.After
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Suppress("FunctionNaming")
+@RunWith(JUnit4::class)
+class MainTest {
+
+ private val root = createTempDirectory().toFile()
+
+ private val emptyInput = "".byteInputStream()
+ private val out = ByteArrayOutputStream()
+ private val err = ByteArrayOutputStream()
+
+ @After
+ fun tearDown() {
+ root.deleteRecursively()
+ }
+
+ /**
+ * Scenario: someone _really_ wants to format this file, regardless of its extension. When a
+ * single argument file is given, it is used as is without filtering by extension.
+ */
+ @Test
+ fun `expandArgsToFileNames - single file arg is used as is`() {
+ val fooBar = root.resolve("foo.bar")
+ fooBar.writeText("hi")
+ assertThat(Main.expandArgsToFileNames(listOf(fooBar.toString()))).containsExactly(fooBar)
+ }
+
+ @Test
+ fun `expandArgsToFileNames - single arg which is not a file is not returned`() {
+ val fooBar = root.resolve("foo.bar")
+ assertThat(Main.expandArgsToFileNames(listOf(fooBar.toString()))).isEmpty()
+ }
+
+ @Test
+ fun `expandArgsToFileNames - single arg which is a directory is resolved to its recursively contained kt files`() {
+ val dir = root.resolve("dir")
+ dir.mkdirs()
+ val foo = dir.resolve("foo.kt")
+ foo.writeText("")
+ val bar = dir.resolve("bar.kt")
+ bar.writeText("")
+ assertThat(Main.expandArgsToFileNames(listOf(dir.toString()))).containsExactly(foo, bar)
+ }
+
+ @Test
+ fun `expandArgsToFileNames - multiple directory args are resolved to their recursively contained kt files`() {
+ val dir1 = root.resolve("dir1")
+ dir1.mkdirs()
+ val foo1 = dir1.resolve("foo1.kt")
+ foo1.writeText("")
+ val bar1 = dir1.resolve("bar1.kt")
+ bar1.writeText("")
+
+ val dir2 = root.resolve("dir2")
+ dir1.mkdirs()
+ val foo2 = dir1.resolve("foo2.kt")
+ foo2.writeText("")
+ val bar2 = dir1.resolve("bar2.kt")
+ bar2.writeText("")
+
+ assertThat(Main.expandArgsToFileNames(listOf(dir1.toString(), dir2.toString())))
+ .containsExactly(foo1, bar1, foo2, bar2)
+ }
+
+ @Test
+ fun `expandArgsToFileNames - a dash is an error`() {
+ try {
+ Main.expandArgsToFileNames(listOf(root.resolve("foo.bar").toString(), File("-").toString()))
+ fail("expected exception, but nothing was thrown")
+ } catch (e: IllegalStateException) {
+ assertThat(e.message).contains("Error")
+ }
+ }
+
+ @Test
+ fun `Using '-' as the filename formats an InputStream`() {
+ val code = "fun f1 ( ) : Int = 0"
+ Main(code.byteInputStream(), PrintStream(out), PrintStream(err), arrayOf("-")).run()
+
+ val expected = "fun f1(): Int = 0\n"
+ assertThat(out.toString("UTF-8")).isEqualTo(expected)
+ }
+
+ @Test
+ fun `Parsing errors are reported (stdin)`() {
+ val code = "fun f1 ( "
+ val returnValue =
+ Main(code.byteInputStream(), PrintStream(out), PrintStream(err), arrayOf("-")).run()
+
+ assertThat(returnValue).isEqualTo(1)
+ assertThat(err.toString("UTF-8")).startsWith("<stdin>:1:14: error: ")
+ }
+
+ @Test
+ fun `Parsing errors are reported (file)`() {
+ val fooBar = root.resolve("foo.kt")
+ fooBar.writeText("fun f1 ( ")
+ val returnValue =
+ Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf(fooBar.toString())).run()
+
+ assertThat(returnValue).isEqualTo(1)
+ assertThat(err.toString("UTF-8")).contains("foo.kt:1:14: error: ")
+ }
+
+ @Test
+ fun `all files in args are processed, even if one of them has an error`() {
+ val file1 = root.resolve("file1.kt")
+ val file2Broken = root.resolve("file2.kt")
+ val file3 = root.resolve("file3.kt")
+ file1.writeText("fun f1 () ")
+ file2Broken.writeText("fun f1 ( ")
+ file3.writeText("fun f1 () ")
+
+ // Make Main() process files serially.
+ val forkJoinPool = ForkJoinPool(1)
+
+ val returnValue: Int =
+ forkJoinPool
+ .submit<Int> {
+ Main(
+ emptyInput,
+ PrintStream(out),
+ PrintStream(err),
+ arrayOf(file1.toString(), file2Broken.toString(), file3.toString()))
+ .run()
+ }
+ .get()
+
+ assertThat(returnValue).isEqualTo(1)
+ assertThat(err.toString("UTF-8")).contains("Done formatting $file1")
+ assertThat(err.toString("UTF-8")).contains("file2.kt:1:14: error: ")
+ assertThat(err.toString("UTF-8")).contains("Done formatting $file3")
+ }
+
+ @Test
+ fun `file is not modified if it is already formatted`() {
+ val code = """fun f() = println("hello, world")""" + "\n"
+ val formattedFile = root.resolve("formatted_file.kt")
+ formattedFile.writeText(code)
+ val formattedFilePath = formattedFile.toPath()
+
+ val lastModifiedTimeBeforeRunningFormatter =
+ Files.getLastModifiedTime(formattedFilePath).toMillis()
+ Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf(formattedFile.toString())).run()
+ val lastModifiedTimeAfterRunningFormatter =
+ Files.getLastModifiedTime(formattedFilePath).toMillis()
+
+ assertThat(lastModifiedTimeBeforeRunningFormatter)
+ .isEqualTo(lastModifiedTimeAfterRunningFormatter)
+ }
+
+ @Test
+ fun `file is modified if it is not formatted`() {
+ val code = """fun f() = println( "hello, world")""" + "\n"
+ val unformattedFile = root.resolve("unformatted_file.kt")
+ unformattedFile.writeText(code)
+ val unformattedFilePath = unformattedFile.toPath()
+
+ val lastModifiedTimeBeforeRunningFormatter =
+ Files.getLastModifiedTime(unformattedFilePath).toMillis()
+ // The test may run under 1ms, and we need to make sure the new file timestamp will be different
+ Thread.sleep(100)
+ Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf(unformattedFile.toString())).run()
+ val lastModifiedTimeAfterRunningFormatter =
+ Files.getLastModifiedTime(unformattedFilePath).toMillis()
+
+ assertThat(lastModifiedTimeBeforeRunningFormatter)
+ .isLessThan(lastModifiedTimeAfterRunningFormatter)
+ }
+
+ @Test
+ fun `dropbox-style is passed to formatter (file)`() {
+ val code =
+ """fun f() {
+ for (child in
+ node.next.next.next.next.next.next.next.next.next.next.next.next.next.next.data()) {
+ println(child)
+ }
+}
+"""
+ val fooBar = root.resolve("foo.kt")
+ fooBar.writeText(code)
+
+ Main(
+ emptyInput,
+ PrintStream(out),
+ PrintStream(err),
+ arrayOf("--dropbox-style", fooBar.toString()))
+ .run()
+
+ assertThat(fooBar.readText()).isEqualTo(code)
+ }
+
+ @Test
+ fun `dropbox-style is passed to formatter (stdin)`() {
+ val code =
+ """fun f() {
+ |for (child in
+ |node.next.next.next.next.next.next.next.next.next.next.next.next.next.next.data()) {
+ |println(child)
+ |}
+ |}
+ |""".trimMargin()
+ val formatted =
+ """fun f() {
+ | for (child in
+ | node.next.next.next.next.next.next.next.next.next.next.next.next.next.next.data()) {
+ | println(child)
+ | }
+ |}
+ |""".trimMargin()
+ Main(
+ code.byteInputStream(),
+ PrintStream(out),
+ PrintStream(err),
+ arrayOf("--dropbox-style", "-"))
+ .run()
+
+ assertThat(out.toString("UTF-8")).isEqualTo(formatted)
+ }
+
+ @Test
+ fun `expandArgsToFileNames - resolves 'kt' and 'kts' filenames only (recursively)`() {
+ val f1 = root.resolve("1.kt")
+ val f2 = root.resolve("2.kt")
+ val f3 = root.resolve("3")
+ val f4 = root.resolve("4.dummyext")
+ val f5 = root.resolve("5.kts")
+
+ val dir = root.resolve("foo")
+ dir.mkdirs()
+ val f6 = root.resolve("foo/1.kt")
+ val f7 = root.resolve("foo/2.kts")
+ val f8 = root.resolve("foo/3.dummyext")
+ val files = listOf(f1, f2, f3, f4, f5, f6, f7, f8)
+ for (f in files) {
+ f.createNewFile()
+ }
+ assertThat(Main.expandArgsToFileNames(files.map { it.toString() }))
+ .containsExactly(f1, f2, f5, f6, f7)
+ }
+
+ @Test
+ fun `formatting from stdin prints formatted code to stdout regardless of whether it was already formatted`() {
+ val expected = """fun f() = println("hello, world")""" + "\n"
+
+ Main(
+ """fun f ( ) = println("hello, world")""".byteInputStream(),
+ PrintStream(out),
+ PrintStream(err),
+ arrayOf("-"))
+ .run()
+ assertThat(out.toString("UTF-8")).isEqualTo(expected)
+
+ out.reset()
+
+ Main(
+ """fun f () = println("hello, world")""".byteInputStream(),
+ PrintStream(out),
+ PrintStream(err),
+ arrayOf("-"))
+ .run()
+ assertThat(out.toString("UTF-8")).isEqualTo(expected)
+ }
+
+ @Test
+ fun `--dry-run prints filename and does not change file`() {
+ val code = """fun f () = println( "hello, world" )"""
+ val file = root.resolve("foo.kt")
+ file.writeText(code)
+
+ Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf("--dry-run", file.toString()))
+ .run()
+
+ assertThat(file.readText()).isEqualTo(code)
+ assertThat(out.toString("UTF-8")).contains(file.toString())
+ }
+
+ @Test
+ fun `--dry-run prints 'stdin' and does not reformat code from stdin`() {
+ val code = """fun f () = println( "hello, world" )"""
+
+ Main(code.byteInputStream(), PrintStream(out), PrintStream(err), arrayOf("--dry-run", "-"))
+ .run()
+
+ assertThat(out.toString("UTF-8")).doesNotContain("hello, world")
+ assertThat(out.toString("UTF-8")).isEqualTo("<stdin>\n")
+ }
+
+ @Test
+ fun `--dry-run prints nothing when there are no changes needed (file)`() {
+ val code = """fun f() = println("hello, world")\n"""
+ val file = root.resolve("foo.kt")
+ file.writeText(code)
+
+ Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf("--dry-run", file.toString()))
+ .run()
+
+ assertThat(out.toString("UTF-8")).isEmpty()
+ }
+
+ @Test
+ fun `--dry-run prints nothing when there are no changes needed (stdin)`() {
+ val code = """fun f() = println("hello, world")\n"""
+
+ Main(code.byteInputStream(), PrintStream(out), PrintStream(err), arrayOf("--dry-run", "-"))
+ .run()
+
+ assertThat(out.toString("UTF-8")).isEmpty()
+ }
+
+ @Test
+ fun `Exit code is 0 when there are changes (file)`() {
+ val code = """fun f () = println( "hello, world" )"""
+ val file = root.resolve("foo.kt")
+ file.writeText(code)
+
+ val exitCode =
+ Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf(file.toString())).run()
+
+ assertThat(exitCode).isEqualTo(0)
+ }
+
+ @Test
+ fun `Exits with 0 when there are changes (stdin)`() {
+ val code = """fun f () = println( "hello, world" )"""
+
+ val exitCode =
+ Main(code.byteInputStream(), PrintStream(out), PrintStream(err), arrayOf("-")).run()
+
+ assertThat(exitCode).isEqualTo(0)
+ }
+
+ @Test
+ fun `Exit code is 1 when there are changes and --set-exit-if-changed is set (file)`() {
+ val code = """fun f () = println( "hello, world" )"""
+ val file = root.resolve("foo.kt")
+ file.writeText(code)
+
+ val exitCode =
+ Main(
+ emptyInput,
+ PrintStream(out),
+ PrintStream(err),
+ arrayOf("--set-exit-if-changed", file.toString()))
+ .run()
+
+ assertThat(exitCode).isEqualTo(1)
+ }
+
+ @Test
+ fun `Exit code is 1 when there are changes and --set-exit-if-changed is set (stdin)`() {
+ val code = """fun f () = println( "hello, world" )"""
+
+ val exitCode =
+ Main(
+ code.byteInputStream(),
+ PrintStream(out),
+ PrintStream(err),
+ arrayOf("--set-exit-if-changed", "-"))
+ .run()
+
+ assertThat(exitCode).isEqualTo(1)
+ }
+
+ @Test
+ fun `--set-exit-if-changed and --dry-run changes nothing, prints filenames, and exits with 1 (file)`() {
+ val code = """fun f () = println( "hello, world" )"""
+ val file = root.resolve("foo.kt")
+ file.writeText(code)
+
+ val exitCode =
+ Main(
+ emptyInput,
+ PrintStream(out),
+ PrintStream(err),
+ arrayOf("--dry-run", "--set-exit-if-changed", file.toString()))
+ .run()
+
+ assertThat(file.readText()).isEqualTo(code)
+ assertThat(out.toString("UTF-8")).contains(file.toString())
+ assertThat(exitCode).isEqualTo(1)
+ }
+
+ @Test
+ fun `--set-exit-if-changed and --dry-run changes nothing, prints filenames, and exits with 1 (stdin)`() {
+ val code = """fun f () = println( "hello, world" )"""
+
+ val exitCode =
+ Main(
+ code.byteInputStream(),
+ PrintStream(out),
+ PrintStream(err),
+ arrayOf("--dry-run", "--set-exit-if-changed", "-"))
+ .run()
+
+ assertThat(out.toString("UTF-8")).doesNotContain("hello, world")
+ assertThat(out.toString("UTF-8")).isEqualTo("<stdin>\n")
+ assertThat(exitCode).isEqualTo(1)
+ }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt b/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt
new file mode 100644
index 0000000..40f3646
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt
@@ -0,0 +1,156 @@
+/*
+ * 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.cli
+
+import com.facebook.ktfmt.format.Formatter
+import com.facebook.ktfmt.format.FormattingOptions
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayOutputStream
+import java.io.FileNotFoundException
+import java.io.PrintStream
+import kotlin.io.path.createTempDirectory
+import kotlin.test.assertFailsWith
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Suppress("FunctionNaming")
+@RunWith(JUnit4::class)
+class ParsedArgsTest {
+
+ private val root = createTempDirectory().toFile()
+
+ @After
+ fun tearDown() {
+ root.deleteRecursively()
+ }
+
+ @Test
+ fun `files to format are returned and unknown flags are reported`() {
+ val out = ByteArrayOutputStream()
+
+ val (fileNames, _) = ParsedArgs.parseOptions(PrintStream(out), arrayOf("foo.kt", "--unknown"))
+
+ assertThat(fileNames).containsExactly("foo.kt")
+ assertThat(out.toString()).isEqualTo("Unexpected option: --unknown\n")
+ }
+
+ @Test
+ fun `files to format are returned and flags starting with @ are reported`() {
+ val out = ByteArrayOutputStream()
+
+ val (fileNames, _) = ParsedArgs.parseOptions(PrintStream(out), arrayOf("foo.kt", "@unknown"))
+
+ assertThat(fileNames).containsExactly("foo.kt")
+ assertThat(out.toString()).isEqualTo("Unexpected option: @unknown\n")
+ }
+
+ @Test
+ fun `parseOptions uses default values when args are empty`() {
+ val out = ByteArrayOutputStream()
+
+ val parsed = ParsedArgs.parseOptions(PrintStream(out), arrayOf("foo.kt"))
+
+ val formattingOptions = parsed.formattingOptions
+ assertThat(formattingOptions.style).isEqualTo(FormattingOptions.Style.FACEBOOK)
+ assertThat(formattingOptions.maxWidth).isEqualTo(100)
+ assertThat(formattingOptions.blockIndent).isEqualTo(2)
+ assertThat(formattingOptions.continuationIndent).isEqualTo(4)
+ assertThat(formattingOptions.removeUnusedImports).isTrue()
+ assertThat(formattingOptions.debuggingPrintOpsAfterFormatting).isFalse()
+
+ assertThat(parsed.dryRun).isFalse()
+ assertThat(parsed.setExitIfChanged).isFalse()
+ }
+
+ @Test
+ fun `parseOptions recognizes --dropbox-style and rejects unknown flags`() {
+ val out = ByteArrayOutputStream()
+
+ val (fileNames, formattingOptions) =
+ ParsedArgs.parseOptions(PrintStream(out), arrayOf("--dropbox-style", "foo.kt", "--unknown"))
+
+ assertThat(fileNames).containsExactly("foo.kt")
+ assertThat(formattingOptions.blockIndent).isEqualTo(4)
+ assertThat(formattingOptions.continuationIndent).isEqualTo(4)
+ assertThat(out.toString()).isEqualTo("Unexpected option: --unknown\n")
+ }
+
+ @Test
+ fun `parseOptions recognizes --google-style`() {
+ val out = ByteArrayOutputStream()
+
+ val (_, formattingOptions) =
+ ParsedArgs.parseOptions(PrintStream(out), arrayOf("--google-style", "foo.kt"))
+
+ assertThat(formattingOptions).isEqualTo(Formatter.GOOGLE_FORMAT)
+ }
+
+ @Test
+ fun `parseOptions recognizes --dry-run`() {
+ val out = ByteArrayOutputStream()
+
+ val parsed = ParsedArgs.parseOptions(PrintStream(out), arrayOf("--dry-run", "foo.kt"))
+
+ assertThat(parsed.dryRun).isTrue()
+ }
+
+ @Test
+ fun `parseOptions recognizes -n as --dry-run`() {
+ val out = ByteArrayOutputStream()
+
+ val parsed = ParsedArgs.parseOptions(PrintStream(out), arrayOf("-n", "foo.kt"))
+
+ assertThat(parsed.dryRun).isTrue()
+ }
+
+ @Test
+ fun `parseOptions recognizes --set-exit-if-changed`() {
+ val out = ByteArrayOutputStream()
+
+ val parsed =
+ ParsedArgs.parseOptions(PrintStream(out), arrayOf("--set-exit-if-changed", "foo.kt"))
+
+ assertThat(parsed.setExitIfChanged).isTrue()
+ }
+
+ @Test
+ fun `processArgs use the @file option with non existing file`() {
+ val out = ByteArrayOutputStream()
+
+ val e =
+ assertFailsWith<FileNotFoundException> {
+ ParsedArgs.processArgs(PrintStream(out), arrayOf("@non-existing-file"))
+ }
+ assertThat(e.message).contains("non-existing-file (No such file or directory)")
+ }
+
+ @Test
+ fun `processArgs use the @file option with file containing arguments`() {
+ val out = ByteArrayOutputStream()
+ val file = root.resolve("existing-file")
+ file.writeText("--google-style\n--dry-run\n--set-exit-if-changed\nFile1.kt\nFile2.kt\n")
+
+ val parsed = ParsedArgs.processArgs(PrintStream(out), arrayOf("@" + file.absolutePath))
+
+ assertThat(parsed.formattingOptions).isEqualTo(Formatter.GOOGLE_FORMAT)
+ assertThat(parsed.dryRun).isTrue()
+ assertThat(parsed.setExitIfChanged).isTrue()
+ assertThat(parsed.fileNames).containsExactlyElementsIn(listOf("File1.kt", "File2.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
new file mode 100644
index 0000000..2096f50
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/format/FormatterTest.kt
@@ -0,0 +1,5906 @@
+/*
+ * 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.facebook.ktfmt.testutil.assertFormatted
+import com.facebook.ktfmt.testutil.assertThatFormatting
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Suppress("FunctionNaming")
+@RunWith(JUnit4::class)
+class FormatterTest {
+
+ @Test
+ fun `support script (kts) files`() =
+ assertFormatted(
+ """
+ |package foo
+ |
+ |import java.io.File
+ |
+ |val one: String
+ |
+ |val two: String
+ |
+ |fun f() {
+ | println("asd")
+ |}
+ |
+ |println("Called with args:")
+ |
+ |args.forEach { println(File + "-") }
+ |""".trimMargin())
+
+ @Test
+ fun `support script (kts) files with a shebang`() =
+ assertFormatted(
+ """
+ |#!/usr/bin/env kscript
+ |package foo
+ |
+ |println("Called")
+ |""".trimMargin())
+
+ @Test
+ fun `call chains`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |fun f() {
+ | // Static method calls are attached to the class name.
+ | ImmutableList.newBuilder()
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .build()
+ |
+ | // Multiple call expressions --> each on its own line.
+ | ImmutableList()
+ | .newBuilder()
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .build()
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `line breaks in function arguments`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |fun f() {
+ | computeBreaks(
+ | javaOutput.commentsHelper,
+ | maxWidth,
+ | Doc.State(+0, 0))
+ | computeBreaks(
+ | output.commentsHelper, maxWidth, State(0))
+ | doc.computeBreaks(
+ | javaOutput.commentsHelper,
+ | maxWidth,
+ | Doc.State(+0, 0))
+ | doc.computeBreaks(
+ | output.commentsHelper, maxWidth, State(0))
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `parameters and return type in function definitions`() =
+ assertFormatted(
+ """
+ |----------------------------------------
+ |fun format(
+ | code: String,
+ | maxWidth: Int =
+ | DEFAULT_MAX_WIDTH_VERY_LONG
+ |): String {
+ | val a = 0
+ |}
+ |
+ |fun print(
+ | code: String,
+ | maxWidth: Int =
+ | DEFAULT_MAX_WIDTH_VERY_LONG
+ |) {
+ | val a = 0
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `kitchen sink of tests`() {
+ // Don't add more tests here
+ val code =
+ """
+ |fun
+ |f (
+ |a : Int
+ | , b: Double , c:String) { var result = 0
+ | val aVeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongVar = 43
+ | foo.bar.zed.accept(
+ |
+ | )
+ |
+ | foo(
+ |
+ | )
+ |
+ | foo.bar.zed.accept(
+ | DoSomething.bar()
+ | )
+ |
+ | bar(
+ | ImmutableList.newBuilder().add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).build())
+ |
+ |
+ | ImmutableList.newBuilder().add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).build()
+ | }
+ |""".trimMargin()
+
+ val expected =
+ """
+ |fun f(a: Int, b: Double, c: String) {
+ | var result = 0
+ | val aVeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongVar =
+ | 43
+ | foo.bar.zed.accept()
+ |
+ | foo()
+ |
+ | foo.bar.zed.accept(DoSomething.bar())
+ |
+ | bar(
+ | ImmutableList.newBuilder()
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .build())
+ |
+ | ImmutableList.newBuilder()
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .build()
+ |}
+ |""".trimMargin()
+
+ assertThatFormatting(code).isEqualTo(expected)
+ // Don't add more tests here
+ }
+
+ @Test
+ fun `spacing around variable declarations`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | var x: Int = 4
+ | val y = 0
+ |}
+ |""".trimMargin())
+
+ @Test fun `class without a body nor properties`() = assertFormatted("class Foo\n")
+
+ @Test fun `interface without a body nor properties`() = assertFormatted("interface Foo\n")
+
+ @Test fun `preserve empty primary constructor`() = assertFormatted("class Foo()\n")
+
+ @Test
+ fun `simple fun interface`() =
+ assertFormatted(
+ """fun interface MyRunnable {
+ | fun runIt()
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle complex fun interface without body`() =
+ assertFormatted("public fun interface Function<T : List<*>> : (Int, T?) -> T?\n")
+
+ @Test
+ fun `class without a body, with explicit ctor params`() =
+ assertFormatted("class Foo(a: Int, var b: Double, val c: String)\n")
+
+ @Test
+ fun `class with a body and explicit ctor params`() =
+ assertFormatted(
+ """
+ |class Foo(a: Int, var b: Double, val c: String) {
+ | val x = 2
+ | fun method() {}
+ | class Bar
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `properties and fields with modifiers`() =
+ assertFormatted(
+ """
+ |class Foo(public val p1: Int, private val p2: Int, open val p3: Int, final val p4: Int) {
+ | private var f1 = 0
+ | public var f2 = 0
+ | open var f3 = 0
+ | final var f4 = 0
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `properties with multiple modifiers`() =
+ assertFormatted(
+ """
+ |class Foo(public open inner val p1: Int) {
+ | public open inner var f2 = 0
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `spaces around binary operations`() =
+ assertFormatted(
+ """
+ |fun foo() {
+ | a = 5
+ | x + 1
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `breaking long binary operations`() =
+ assertFormatted(
+ """
+ |--------------------
+ |fun foo() {
+ | val finalWidth =
+ | value1 +
+ | value2 +
+ | (value3 +
+ | value4 +
+ | value5) +
+ | foo(v) +
+ | (1 + 2) +
+ | function(
+ | value7,
+ | value8) +
+ | value9
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `prioritize according to associativity`() =
+ assertFormatted(
+ """
+ |--------------------------------------
+ |fun foo() {
+ | return expression1 != expression2 ||
+ | expression2 != expression1
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `once a binary expression is broken, split on every line`() =
+ assertFormatted(
+ """
+ |--------------------------------------
+ |fun foo() {
+ | val sentence =
+ | "The" +
+ | "quick" +
+ | ("brown" + "fox") +
+ | "jumps" +
+ | "over" +
+ | "the" +
+ | "lazy" +
+ | "dog"
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `long binary expressions with ranges in the middle`() =
+ assertFormatted(
+ """
+ |--------------------------------------
+ |fun foo() {
+ | val sentence =
+ | "The" +
+ | "quick" +
+ | ("brown".."fox") +
+ | "jumps" +
+ | "over" +
+ | "the".."lazy" + "dog"
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `assignment expressions with scoping functions are block-like`() =
+ assertFormatted(
+ """
+ |---------------------------
+ |fun f() {
+ | name.sub = scope { x ->
+ | //
+ | }
+ | name.sub += scope { x ->
+ | //
+ | }
+ | name.sub -= scope { x ->
+ | //
+ | }
+ | name.sub *= scope { x ->
+ | //
+ | }
+ | name.sub /= scope { x ->
+ | //
+ | }
+ | name.sub %= scope { x ->
+ | //
+ | }
+ |}
+ |
+ |fun h() {
+ | long.name.sub =
+ | scope { x ->
+ | //
+ | }
+ | long.name.sub +=
+ | scope { x ->
+ | //
+ | }
+ | long.name.sub -=
+ | scope { x ->
+ | //
+ | }
+ | long.name.sub *=
+ | scope { x ->
+ | //
+ | }
+ | long.name.sub /=
+ | scope { x ->
+ | //
+ | }
+ | long.name.sub %=
+ | scope { x ->
+ | //
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `don't keep adding newlines between these two comments when they're at end of file`() =
+ assertFormatted(
+ """
+ |package foo
+ |// a
+ |
+ |/* Another comment */
+ |""".trimMargin())
+
+ @Test
+ fun `properties with accessors`() =
+ assertFormatted(
+ """
+ |class Foo {
+ | var x: Int
+ | get() = field
+ | var y: Boolean
+ | get() = x.equals(123)
+ | set(value) {
+ | field = value
+ | }
+ | var z: Boolean
+ | get() {
+ | x.equals(123)
+ | }
+ | var zz = false
+ | private set
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `properties with accessors and semicolons on same line`() {
+ val code =
+ """
+ |class Foo {
+ | var x = false; private set
+ | internal val a by lazy { 5 }; internal get
+ | var foo: Int; get() = 6; set(x) {};
+ |}
+ |""".trimMargin()
+
+ val expected =
+ """
+ |class Foo {
+ | var x = false
+ | private set
+ | internal val a by lazy { 5 }
+ | internal get
+ | var foo: Int
+ | get() = 6
+ | set(x) {}
+ |}
+ |""".trimMargin()
+
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `a property with a too long name being broken on multiple lines`() =
+ assertFormatted(
+ """
+ |--------------------
+ |class Foo {
+ | val thisIsALongName:
+ | String =
+ | "Hello there this is long"
+ | get() = field
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `multi-character unary and binary operators such as ==`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | 3 == 4
+ | true && false
+ | a++
+ | a === b
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `package names stay in one line`() {
+ val code =
+ """
+ | package com .example. subexample
+ |
+ |fun f() = 1
+ |""".trimMargin()
+ val expected =
+ """
+ |package com.example.subexample
+ |
+ |fun f() = 1
+ |""".trimMargin()
+
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `handle package name and imports with escapes and spaces`() =
+ assertFormatted(
+ """
+ |package com.`fun times`.`with package names`
+ |
+ |import `nothing stops`.`us`.`from doing this`
+ |
+ |fun f() = `from doing this`()
+ |""".trimMargin())
+
+ @Test
+ fun `safe dot operator expression`() =
+ assertFormatted("""
+ |fun f() {
+ | node?.name
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `safe dot operator expression with normal`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | node?.name.hello
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `safe dot operator expression chain in expression function`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |fun f(number: Int) =
+ | Something.doStuff(number)?.size
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `avoid breaking suspected package names`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun f() {
+ | com.facebook.Foo
+ | .format()
+ | org.facebook.Foo
+ | .format()
+ | java.lang.stuff.Foo
+ | .format()
+ | javax.lang.stuff.Foo
+ | .format()
+ | kotlin.lang.Foo
+ | .format()
+ | foo.facebook.Foo
+ | .format()
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `an assortment of tests for emitQualifiedExpression`() =
+ assertFormatted(
+ """
+ |-------------------------------------
+ |fun f() {
+ | // Regression test: https://github.com/facebookincubator/ktfmt/issues/56
+ | kjsdfglkjdfgkjdfkgjhkerjghkdfj
+ | ?.methodName1()
+ |
+ | // a series of field accesses followed by a single call expression
+ | // is kept together.
+ | abcdefghijkl.abcdefghijkl
+ | ?.methodName2()
+ |
+ | // Similar to above.
+ | abcdefghijkl.abcdefghijkl
+ | ?.methodName3
+ | ?.abcdefghijkl()
+ |
+ | // Multiple call expressions cause each part of the expression
+ | // to be placed on its own line.
+ | abcdefghijkl
+ | ?.abcdefghijkl
+ | ?.methodName4()
+ | ?.abcdefghijkl()
+ |
+ | // Don't break first call expression if it fits.
+ | foIt(something.something.happens())
+ | .thenReturn(result)
+ |
+ | // Break after `longerThanFour(` because it's longer than 4 chars
+ | longerThanFour(
+ | something.something
+ | .happens())
+ | .thenReturn(result)
+ |
+ | // Similarly to above, when part of qualified expression.
+ | foo.longerThanFour(
+ | something.something
+ | .happens())
+ | .thenReturn(result)
+ |
+ | // Keep 'super' attached to the method name
+ | super.abcdefghijkl
+ | .methodName4()
+ | .abcdefghijkl()
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `an assortment of tests for emitQualifiedExpression with lambdas`() =
+ assertFormatted(
+ """
+ |----------------------------------------------------------------------------
+ |fun f() {
+ | val items =
+ | items.toMutableList.apply {
+ | //
+ | foo
+ | }
+ |
+ | val items =
+ | items.toMutableList().apply {
+ | //
+ | foo
+ | }
+ |
+ | // All dereferences are on one line (because they fit), even though
+ | // the apply() at the end requires a line break.
+ | val items =
+ | items.toMutableList.sdfkjsdf.sdfjksdflk.sdlfkjsldfj.apply {
+ | //
+ | foo
+ | }
+ |
+ | // All method calls are on one line (because they fit), even though
+ | // the apply() at the end requires a line break.
+ | val items =
+ | items.toMutableList().sdfkjsdf().sdfjksdflk().sdlfkjsldfj().apply {
+ | //
+ | foo
+ | }
+ |
+ | // All method calls with lambdas could fit, but we avoid a block like syntax
+ | // and break to one call per line
+ | val items =
+ | items
+ | .map { it + 1 }
+ | .filter { it > 0 }
+ | .apply {
+ | //
+ | foo
+ | }
+ |
+ | // the lambda is indented properly with the break before it
+ | val items =
+ | items.fieldName.sdfkjsdf.sdfjksdflk.sdlfkjsldfj.sdfjksdflk.sdlfkjsldfj
+ | .sdlfkjsldfj
+ | .apply {
+ | //
+ | foo
+ | }
+ | items.fieldName.sdfkjsdf.sdfjksdflk.sdlfkjsldfj.sdfjksdflk.sdlfkjsldfj
+ | .apply {
+ | //
+ | foo
+ | }
+ |
+ | // When there are multiple method calls, and they don't fit on one
+ | // line, put each on a new line.
+ | val items =
+ | items
+ | .toMutableList()
+ | .sdfkjsdf()
+ | .sdfjksdflk()
+ | .sdlfkjsldfj()
+ | .sdfjksdflk()
+ | .sdlfkjsldfj()
+ | .apply {
+ | //
+ | foo
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `when two lambdas are in a chain, avoid block syntax`() =
+ assertFormatted(
+ """
+ |class Foo : Bar() {
+ | fun doIt() {
+ | fruit.onlyBananas().forEach { banana ->
+ | val seeds = banana.find { it.type == SEED }
+ | println(seeds)
+ | }
+ |
+ | fruit
+ | .filter { isBanana(it, Bananas.types) }
+ | .forEach { banana ->
+ | val seeds = banana.find { it.type == SEED }
+ | println(seeds)
+ | }
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `don't one-line lambdas following parameter breaks`() =
+ assertFormatted(
+ """
+ |------------------------------------------------------------------------
+ |class Foo : Bar() {
+ | fun doIt() {
+ | // don't break in lambda, no parameter breaks found
+ | fruit.forEach { eat(it) }
+ |
+ | // break in the lambda because the closing paren gets attached
+ | // to the last argument
+ | fruit.forEach(
+ | someVeryLongParameterNameThatWillCauseABreak,
+ | evenWithoutATrailingCommaOnTheParameterListSoLetsSeeIt) {
+ | eat(it)
+ | }
+ |
+ | // break in the lambda
+ | fruit.forEach(
+ | fromTheVine = true,
+ | ) {
+ | eat(it)
+ | }
+ |
+ | // don't break in the inner lambda, as nesting doesn't respect outer levels
+ | fruit.forEach(
+ | fromTheVine = true,
+ | ) {
+ | fruit.forEach { eat(it) }
+ | }
+ |
+ | // don't break in the lambda, as breaks don't propagate
+ | fruit
+ | .onlyBananas(
+ | fromTheVine = true,
+ | )
+ | .forEach { eat(it) }
+ |
+ | // don't break in the inner lambda, as breaks don't propagate to parameters
+ | fruit.onlyBananas(
+ | fromTheVine = true,
+ | processThem = { eat(it) },
+ | ) {
+ | eat(it)
+ | }
+ |
+ | // don't break in the inner lambda, as breaks don't propagate to the body
+ | fruit.onlyBananas(
+ | fromTheVine = true,
+ | ) {
+ | val anon = { eat(it) }
+ | }
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `indent parameters after a break when there's a lambda afterwards`() =
+ assertFormatted(
+ """
+ |---------------------------
+ |class C {
+ | fun method() {
+ | Foo.FooBar(
+ | param1, param2)
+ | .apply {
+ | //
+ | foo
+ | }
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `no break between multi-line strings and their selectors`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |val STRING =
+ | ""${'"'}
+ | |foo
+ | |""${'"'}.trimMargin()
+ |
+ |// This is a bug (line is longer than limit)
+ |// that we don't know how to avoid, for now.
+ |val STRING =
+ | ""${'"'}
+ | |foo
+ | |----------------------------------""${'"'}.trimMargin()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `import list`() {
+ val code =
+ """
+ | import com .example.common.reality. FooBar
+ | import com .example.common.reality. FooBar2 as foosBars
+ | import com .example.common.reality. *
+ | import foo.bar // Test
+ | import abc.def /*
+ | test */
+ |
+ |val x = FooBar.def { foosBars(bar) }
+ |""".trimMargin()
+ val expected =
+ """
+ |import abc.def /*
+ | test */
+ |import com.example.common.reality.*
+ |import com.example.common.reality.FooBar
+ |import com.example.common.reality.FooBar2 as foosBars
+ |import foo.bar // Test
+ |
+ |val x = FooBar.def { foosBars(bar) }
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `imports with trailing comments and expressions`() {
+ val code =
+ """
+ |import com.example.zab // test
+ |import com.example.foo ; val x = Sample(foo, zab)
+ |""".trimMargin()
+
+ val expected =
+ """
+ |import com.example.foo
+ |import com.example.zab // test
+ |
+ |val x = Sample(foo, zab)
+ |""".trimMargin()
+
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `backticks are ignored in import sort order`() =
+ assertFormatted(
+ """
+ |import com.example.`if`
+ |import com.example.we
+ |import com.example.`when`
+ |import com.example.wow
+ |
+ |val x = `if` { we.`when`(wow) }
+ |""".trimMargin())
+
+ @Test
+ fun `backticks are ignored in import sort order ('as' directory)`() =
+ assertFormatted(
+ """
+ |import com.example.a as `if`
+ |import com.example.a as we
+ |import com.example.a as `when`
+ |import com.example.a as wow
+ |
+ |val x = `if` { we.`when`(wow) }
+ |""".trimMargin())
+
+ @Test
+ fun `imports are deduplicated`() {
+ val code =
+ """
+ |import com.example.b.*
+ |import com.example.b
+ |import com.example.a as `if`
+ |import com.example.a as we
+ |import com.example.a as `when`
+ |import com.example.a as wow
+ |import com.example.a as `when`
+ |
+ |val x = `if` { we.`when`(wow) } ?: b
+ |""".trimMargin()
+ val expected =
+ """
+ |import com.example.a as `if`
+ |import com.example.a as we
+ |import com.example.a as `when`
+ |import com.example.a as wow
+ |import com.example.b
+ |import com.example.b.*
+ |
+ |val x = `if` { we.`when`(wow) } ?: b
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `unused imports are removed`() {
+ val code =
+ """
+ |import com.unused.Sample
+ |import com.used.FooBarBaz as Baz
+ |import com.used.bar // test
+ |import com.used.`class`
+ |import com.used.a.*
+ |import com.used.b as `if`
+ |import com.used.b as we
+ |import com.unused.a as `when`
+ |import com.unused.a as wow
+ |
+ |fun test(input: we) {
+ | Baz(`class`)
+ | `if` { bar }
+ | val x = unused()
+ |}
+ |""".trimMargin()
+ val expected =
+ """
+ |import com.used.FooBarBaz as Baz
+ |import com.used.a.*
+ |import com.used.b as `if`
+ |import com.used.b as we
+ |import com.used.bar // test
+ |import com.used.`class`
+ |
+ |fun test(input: we) {
+ | Baz(`class`)
+ | `if` { bar }
+ | val x = unused()
+ |}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `imports from the same package are removed`() {
+ val code =
+ """
+ |package com.example
+ |
+ |import com.example.Sample
+ |import com.example.Sample.CONSTANT
+ |import com.example.a.foo
+ |
+ |fun test() {
+ | foo(CONSTANT, Sample())
+ |}
+ |""".trimMargin()
+ val expected =
+ """
+ |package com.example
+ |
+ |import com.example.Sample.CONSTANT
+ |import com.example.a.foo
+ |
+ |fun test() {
+ | foo(CONSTANT, Sample())
+ |}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `keep import elements only mentioned in kdoc`() {
+ val code =
+ """
+ |package com.example.kdoc
+ |
+ |import com.example.Bar
+ |import com.example.Example
+ |import com.example.Foo
+ |import com.example.JavaDocLink
+ |import com.example.Param
+ |import com.example.R
+ |import com.example.ReturnedValue
+ |import com.example.Sample
+ |import com.example.unused
+ |import com.example.exception.AnException
+ |import com.example.kdoc.Doc
+ |
+ |/**
+ | * [Foo] is something only mentioned here, just like [R.layout.test] and [Doc].
+ | *
+ | * Old {@link JavaDocLink} that gets removed.
+ | *
+ | * @throws AnException
+ | * @exception Sample.SampleException
+ | * @param unused [Param]
+ | * @property JavaDocLink [Param]
+ | * @return [Unit] as [ReturnedValue]
+ | * @sample Example
+ | * @see Bar for more info
+ | * @throws AnException
+ | */
+ |class Dummy
+ |""".trimMargin()
+ val expected =
+ """
+ |package com.example.kdoc
+ |
+ |import com.example.Bar
+ |import com.example.Example
+ |import com.example.Foo
+ |import com.example.Param
+ |import com.example.R
+ |import com.example.ReturnedValue
+ |import com.example.Sample
+ |import com.example.exception.AnException
+ |
+ |/**
+ | * [Foo] is something only mentioned here, just like [R.layout.test] and [Doc].
+ | *
+ | * Old {@link JavaDocLink} that gets removed.
+ | *
+ | * @throws AnException
+ | * @exception Sample.SampleException
+ | * @param unused [Param]
+ | * @property JavaDocLink [Param]
+ | * @return [Unit] as [ReturnedValue]
+ | * @sample Example
+ | * @see Bar for more info
+ | * @throws AnException
+ | */
+ |class Dummy
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `keep component imports`() =
+ assertFormatted(
+ """
+ |import com.example.component1
+ |import com.example.component10
+ |import com.example.component120
+ |import com.example.component2
+ |import com.example.component3
+ |import com.example.component4
+ |import com.example.component5
+ |""".trimMargin())
+
+ @Test
+ fun `keep operator imports`() =
+ assertFormatted(
+ """
+ |import com.example.and
+ |import com.example.compareTo
+ |import com.example.contains
+ |import com.example.dec
+ |import com.example.div
+ |import com.example.divAssign
+ |import com.example.equals
+ |import com.example.get
+ |import com.example.getValue
+ |import com.example.hasNext
+ |import com.example.inc
+ |import com.example.invoke
+ |import com.example.iterator
+ |import com.example.minus
+ |import com.example.minusAssign
+ |import com.example.mod
+ |import com.example.modAssign
+ |import com.example.next
+ |import com.example.not
+ |import com.example.or
+ |import com.example.plus
+ |import com.example.plusAssign
+ |import com.example.provideDelegate
+ |import com.example.rangeTo
+ |import com.example.rem
+ |import com.example.remAssign
+ |import com.example.set
+ |import com.example.setValue
+ |import com.example.times
+ |import com.example.timesAssign
+ |import com.example.unaryMinus
+ |import com.example.unaryPlus
+ |""".trimMargin())
+
+ @Test
+ fun `keep unused imports when formatting options has feature turned off`() {
+ val code =
+ """
+ |import com.unused.FooBarBaz as Baz
+ |import com.unused.Sample
+ |import com.unused.a as `when`
+ |import com.unused.a as wow
+ |import com.unused.a.*
+ |import com.unused.b as `if`
+ |import com.unused.b as we
+ |import com.unused.bar // test
+ |import com.unused.`class`
+ |""".trimMargin()
+
+ assertThatFormatting(code)
+ .withOptions(FormattingOptions(removeUnusedImports = false))
+ .isEqualTo(code)
+ }
+
+ @Test
+ fun `comments between imports are moved above import list`() {
+ val code =
+ """
+ |package com.facebook.ktfmt
+ |
+ |/* leading comment */
+ |import com.example.abc
+ |/* internal comment 1 */
+ |import com.example.bcd
+ |// internal comment 2
+ |import com.example.Sample
+ |// trailing comment
+ |
+ |val x = Sample(abc, bcd)
+ |""".trimMargin()
+ val expected =
+ """
+ |package com.facebook.ktfmt
+ |
+ |/* leading comment */
+ |/* internal comment 1 */
+ |// internal comment 2
+ |import com.example.Sample
+ |import com.example.abc
+ |import com.example.bcd
+ |
+ |// trailing comment
+ |
+ |val x = Sample(abc, bcd)
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `no redundant newlines when there are no imports`() =
+ assertFormatted(
+ """
+ |package foo123
+ |
+ |/*
+ |bar
+ |*/
+ |""".trimMargin())
+
+ @Test
+ fun `basic annotations`() =
+ assertFormatted(
+ """
+ |@Fancy
+ |class Foo {
+ | @Fancy
+ | fun baz(@Fancy foo: Int) {
+ | @Fancy val a = 1 + foo
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `function calls with multiple arguments`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | foo(1, 2, 3)
+ |
+ | foo(
+ | 123456789012345678901234567890,
+ | 123456789012345678901234567890,
+ | 123456789012345678901234567890)
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `function calls with multiple named arguments`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | foo(1, b = 2, c = 3)
+ |
+ | foo(
+ | 123456789012345678901234567890,
+ | b = 23456789012345678901234567890,
+ | c = 3456789012345678901234567890)
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `named arguments indent their value expression`() =
+ assertFormatted(
+ """
+ |fun f() =
+ | Bar(
+ | tokens =
+ | mutableListOf<Token>().apply {
+ | // Printing
+ | print()
+ | },
+ | duration = duration)
+ |""".trimMargin())
+
+ @Test
+ fun `Arguments are blocks`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |override fun visitProperty(property: KtProperty) {
+ | builder.sync(property)
+ | builder.block(ZERO) {
+ | declareOne(
+ | kind = DeclarationKind.FIELD,
+ | modifiers = property.modifierList,
+ | valOrVarKeyword =
+ | property.valOrVarKeyword.text,
+ | typeParameters =
+ | property.typeParameterList,
+ | receiver = property.receiverTypeReference,
+ | name = property.nameIdentifier?.text,
+ | type = property.typeReference,
+ | typeConstraintList =
+ | property.typeConstraintList,
+ | delegate = property.delegate,
+ | initializer = property.initializer)
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `anonymous function`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | setListener(
+ | fun(number: Int) {
+ | println(number)
+ | })
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `anonymous function with receiver`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | setListener(
+ | fun View.() {
+ | println(this)
+ | })
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `when() with a subject expression`() =
+ assertFormatted(
+ """
+ |fun f(x: Int) {
+ | when (x) {
+ | 1 -> print(1)
+ | 2 -> print(2)
+ | 3 ->
+ | // Comment
+ | print(3)
+ | else -> {
+ | print("else")
+ | }
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `when() expression with complex predicates`() =
+ assertFormatted(
+ """
+ |fun f(x: Int) {
+ | when {
+ | x == 1 || x == 2 -> print(1)
+ | x == 3 && x != 4 -> print(2)
+ | else -> {
+ | print(3)
+ | }
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `when() expression with several conditions`() =
+ assertFormatted(
+ """
+ |fun f(x: Int) {
+ | when {
+ | 0,
+ | 1 -> print(1)
+ | else -> print(0)
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `when() expression with is and in`() =
+ assertFormatted(
+ """
+ |fun f(x: Int) {
+ | when (x) {
+ | is String -> print(1)
+ | !is String -> print(2)
+ | in 1..3 -> print()
+ | in a..b -> print()
+ | in a..3 -> print()
+ | in 1..b -> print()
+ | !in 1..b -> print()
+ | else -> print(3)
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `when() expression with enum values`() =
+ assertFormatted(
+ """
+ |fun f(x: Color) {
+ | when (x) {
+ | is Color.Red -> print(1)
+ | is Color.Green -> print(2)
+ | else -> print(3)
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `when() expression with generic matcher and exhaustive`() =
+ assertFormatted(
+ """
+ |fun f(x: Result) {
+ | when (x) {
+ | is Success<*> -> print(1)
+ | is Failure -> print(2)
+ | }.exhaustive
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `when() expression with multiline condition`() =
+ assertFormatted(
+ """
+ |--------------------------
+ |fun foo() {
+ | when (expressions1 +
+ | expression2 +
+ | expression3) {
+ | 1 -> print(1)
+ | 2 -> print(2)
+ | }
+ |
+ | when (foo(
+ | expressions1 &&
+ | expression2 &&
+ | expression3)) {
+ | 1 -> print(1)
+ | 2 -> print(2)
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `lambda assigned to variable does not break before brace`() =
+ assertFormatted(
+ """
+ |fun doIt() {
+ | val lambda = {
+ | doItOnce()
+ | doItTwice()
+ | }
+ |}
+ |
+ |fun foo() = {
+ | doItOnce()
+ | doItTwice()
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `when() expression storing in local variable`() =
+ assertFormatted(
+ """
+ |fun f(x: Result) {
+ | when (val y = x.improved()) {
+ | is Success<*> -> print(y)
+ | is Failure -> print(2)
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `line breaks inside when expressions and conditions`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | return Text.create(c)
+ | .onTouch {
+ | when (it.motionEvent.action) {
+ | ACTION_DOWN ->
+ | Toast.makeText(it.view.context, "Down!", Toast.LENGTH_SHORT, blablabla).show()
+ | ACTION_UP -> Toast.makeText(it.view.context, "Up!", Toast.LENGTH_SHORT).show()
+ | ACTION_DOWN ->
+ | Toast.makeText(
+ | it.view.context, "Down!", Toast.LENGTH_SHORT, blablabla, blablabl, blabla)
+ | .show()
+ | }
+ | }
+ | .build()
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `function return types`() =
+ assertFormatted(
+ """
+ |fun f1(): Int = 0
+ |
+ |fun f2(): Int {}
+ |""".trimMargin())
+
+ @Test
+ fun `multi line function without a block body`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |fun longFunctionNoBlock():
+ | Int =
+ | 1234567 + 1234567
+ |
+ |fun shortFun(): Int =
+ | 1234567 + 1234567
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `return type doesn't fit in one line`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |interface X {
+ | fun f(
+ | arg1: Arg1Type,
+ | arg2: Arg2Type
+ | ): Map<String, Map<String, Double>>? {
+ | //
+ | }
+ |
+ | fun functionWithGenericReturnType(
+ | arg1: Arg1Type,
+ | arg2: Arg2Type
+ | ): Map<String, Map<String, Double>>? {
+ | //
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `list of superclasses`() =
+ assertFormatted(
+ """
+ |class Derived2 : Super1, Super2 {}
+ |
+ |class Derived1 : Super1, Super2
+ |
+ |class Derived3(a: Int) : Super1(a)
+ |
+ |class Derived4 : Super1()
+ |
+ |class Derived5 : Super3<Int>()
+ |""".trimMargin())
+
+ @Test
+ fun `list of superclasses over multiple lines`() =
+ assertFormatted(
+ """
+ |--------------------
+ |class Derived2 :
+ | Super1,
+ | Super2 {}
+ |
+ |class Derived1 :
+ | Super1, Super2
+ |
+ |class Derived3(
+ | a: Int
+ |) : Super1(a)
+ |
+ |class Derived4 :
+ | Super1()
+ |
+ |class Derived5 :
+ | Super3<Int>()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `annotations with parameters`() =
+ assertFormatted("""
+ |@AnnWithArrayValue(1, 2, 3) class C
+ |""".trimMargin())
+
+ @Test
+ fun `method modifiers`() =
+ assertFormatted("""
+ |override internal fun f() {}
+ |""".trimMargin())
+
+ @Test
+ fun `class modifiers`() =
+ assertFormatted(
+ """
+ |abstract class Foo
+ |
+ |inner class Foo
+ |
+ |final class Foo
+ |
+ |open class Foo
+ |""".trimMargin())
+
+ @Test
+ fun `kdoc comments`() {
+ val code =
+ """
+ |/**
+ | * foo
+ | */ class F {
+ |
+ | }
+ |""".trimMargin()
+ val expected = """
+ |/** foo */
+ |class F {}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `nested kdoc comments`() {
+ val code =
+ """
+ |/**
+ | * foo /* bla */
+ | */ class F {
+ |
+ | }
+ |""".trimMargin()
+ val expected = """
+ |/** foo /* bla */ */
+ |class F {}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `nested kdoc inside code block`() =
+ assertFormatted(
+ """
+ |/**
+ | * ```
+ | * edit -> { /* open edit screen */ }
+ | * ```
+ | */
+ |fun foo() {}
+ |""".trimMargin())
+
+ @Test
+ fun `formatting kdoc doesn't add p HTML tags`() =
+ assertFormatted(
+ """
+ |/**
+ | * Bla bla bla bla
+ | *
+ | * This is an inferred paragraph, and as you can see, we don't add a p tag to it, even though bla
+ | * bla.
+ | *
+ | * <p>On the other hand, we respect existing tags, and don't remove them.
+ | */
+ |""".trimMargin())
+
+ @Test
+ fun `formatting kdoc preserves lists`() =
+ assertFormatted(
+ """
+ |/**
+ | * Here are some fruit I like:
+ | * - Banana
+ | * - Apple
+ | *
+ | * This is another paragraph
+ | */
+ |""".trimMargin())
+
+ @Test
+ fun `formatting kdoc lists with line wraps breaks and merges correctly`() {
+ val code =
+ """
+ |/**
+ | * Here are some fruit I like:
+ | * - Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana
+ | * - Apple Apple Apple Apple
+ | * Apple Apple
+ | *
+ | * This is another paragraph
+ | */
+ |""".trimMargin()
+ val expected =
+ """
+ |/**
+ | * Here are some fruit I like:
+ | * - Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana
+ | * Banana Banana Banana Banana Banana
+ | * - Apple Apple Apple Apple Apple Apple
+ | *
+ | * This is another paragraph
+ | */
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `too many spaces on list continuation mean it's a code block, so mark it accordingly`() {
+ val code =
+ """
+ |/**
+ | * Here are some fruit I like:
+ | * - Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana
+ | * Banana Banana Banana Banana Banana
+ | */
+ |""".trimMargin()
+ val expected =
+ """
+ |/**
+ | * Here are some fruit I like:
+ | * - Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana
+ | * ```
+ | * Banana Banana Banana Banana Banana
+ | * ```
+ | */
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `add explicit code markers around indented code`() {
+ val code =
+ """
+ |/**
+ | * This is a code example:
+ | *
+ | * this_is_code()
+ | *
+ | * This is not code again
+ | */
+ |""".trimMargin()
+ val expected =
+ """
+ |/**
+ | * This is a code example:
+ | * ```
+ | * this_is_code()
+ | * ```
+ | * This is not code again
+ | */
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `formatting kdoc preserves lists of asterisks`() =
+ assertFormatted(
+ """
+ |/**
+ | * Here are some fruit I like:
+ | * * Banana
+ | * * Apple
+ | *
+ | * This is another paragraph
+ | */
+ |""".trimMargin())
+
+ @Test
+ fun `formatting kdoc preserves numbered`() =
+ assertFormatted(
+ """
+ |/**
+ | * Here are some fruit I like:
+ | * 1. Banana
+ | * 2. Apple
+ | *
+ | * This is another paragraph
+ | */
+ |""".trimMargin())
+
+ @Test
+ fun `formatting kdoc with markdown errors`() =
+ assertFormatted(
+ """
+ |/** \[ */
+ |fun markdownError() = Unit
+ |""".trimMargin())
+
+ @Test
+ fun `return statement with value`() =
+ assertFormatted(
+ """
+ |fun random(): Int {
+ | return 4
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `return statement without value`() =
+ assertFormatted(
+ """
+ |fun print(b: Boolean) {
+ | print(b)
+ | return
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `return expression without value`() =
+ assertFormatted(
+ """
+ |fun print(b: Boolean?) {
+ | print(b ?: return)
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `if statement without else`() =
+ assertFormatted(
+ """
+ |fun maybePrint(b: Boolean) {
+ | if (b) {
+ | println(b)
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `if statement with else`() =
+ assertFormatted(
+ """
+ |fun maybePrint(b: Boolean) {
+ | if (b) {
+ | println(2)
+ | } else {
+ | println(1)
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `if expression with else`() =
+ assertFormatted(
+ """
+ |fun maybePrint(b: Boolean) {
+ | println(if (b) 1 else 2)
+ | println(
+ | if (b) {
+ | val a = 1 + 1
+ | 2 * a
+ | } else 2)
+ | return if (b) 1 else 2
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `if expression with break before else`() =
+ assertFormatted(
+ """
+ |------------------------------
+ |fun compute(b: Boolean) {
+ | val c =
+ | if (a + b < 20) a + b
+ | else a
+ | return if (a + b < 20) a + b
+ | else c
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `if expression with break before expressions`() =
+ assertFormatted(
+ """
+ |--------------------------
+ |fun compute(b: Boolean) {
+ | val c =
+ | if (a + b < 20)
+ | a + b
+ | else if (a < 20) a
+ | else
+ | a + b + b + 1000
+ | return if (a + b < 20)
+ | a + b
+ | else c
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `blocky expressions in if-else`() =
+ assertFormatted(
+ """
+ |fun numbers() {
+ | if (true)
+ | do {
+ | eat("is")
+ | matches += type()
+ | } while (eat(","))
+ | else
+ | while (1 < 2) {
+ | println("Everything is okay")
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `if expression with multiline condition`() =
+ assertFormatted(
+ """
+ |----------------------------
+ |fun foo() {
+ | if (expressions1 &&
+ | expression2 &&
+ | expression3) {
+ | bar()
+ | }
+ |
+ | if (foo(
+ | expressions1 &&
+ | expression2 &&
+ | expression3)) {
+ | bar()
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `assignment expression on multiple lines`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |fun f() {
+ | var myVariable = 5
+ | myVariable =
+ | function1(4, 60, 8) + function2(57, 39, 20)
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `A program that tickled a bug in KotlinInput`() =
+ assertFormatted("""
+ |val x = 2
+ |""".trimMargin())
+
+ @Test
+ fun `a few variations of constructors`() =
+ assertFormatted(
+ """
+ |class Foo constructor(number: Int) {}
+ |
+ |class Foo2 private constructor(number: Int) {}
+ |
+ |class Foo3 @Inject constructor(number: Int) {}
+ |
+ |class Foo4 @Inject private constructor(number: Int) {}
+ |
+ |class Foo5
+ |@Inject
+ |private constructor(
+ | number: Int,
+ | number2: Int,
+ | number3: Int,
+ | number4: Int,
+ | number5: Int,
+ | number6: Int
+ |) {}
+ |""".trimMargin())
+
+ @Test
+ fun `a primary constructor without a class body `() =
+ assertFormatted(
+ """
+ |-------------------------
+ |data class Foo(
+ | val number: Int = 0
+ |)
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `a secondary constructor without a body`() =
+ assertFormatted(
+ """
+ |---------------------------
+ |data class Foo {
+ | constructor(
+ | val number: Int = 0
+ | )
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `a secondary constructor with a body breaks before closing parenthesis`() =
+ assertFormatted(
+ """
+ |---------------------------
+ |data class Foo {
+ | constructor(
+ | val number: Int = 0
+ | ) {}
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `a constructor with many arguments over breaking to next line`() =
+ assertFormatted(
+ """
+ |data class Foo(
+ | val number: Int,
+ | val name: String,
+ | val age: Int,
+ | val title: String,
+ | val offspring2: List<Foo>
+ |) {}
+ |""".trimMargin())
+
+ @Test
+ fun `a constructor with keyword and many arguments over breaking to next line`() =
+ assertFormatted(
+ """
+ |data class Foo
+ |constructor(
+ | val name: String,
+ | val age: Int,
+ | val title: String,
+ | val offspring: List<Foo>,
+ | val foo: String
+ |) {}
+ |""".trimMargin())
+
+ @Test
+ fun `a constructor with many arguments over multiple lines`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |data class Foo
+ |constructor(
+ | val number: Int,
+ | val name: String,
+ | val age: Int,
+ | val title: String,
+ | val offspring: List<Foo>
+ |) {}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle secondary constructors`() =
+ assertFormatted(
+ """
+ |class Foo private constructor(number: Int) {
+ | private constructor(n: Float) : this(1)
+ | private constructor(n: Double) : this(1) {
+ | println("built")
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `a secondary constructor with many arguments over multiple lines`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |data class Foo {
+ | constructor(
+ | val number: Int,
+ | val name: String,
+ | val age: Int,
+ | val title: String,
+ | val offspring: List<Foo>
+ | )
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `a secondary constructor with many arguments passed to delegate`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |data class Foo {
+ | constructor(
+ | val number: Int,
+ | val name: String,
+ | val age: Int,
+ | val title: String,
+ | val offspring: List<Foo>
+ | ) : this(
+ | number,
+ | name,
+ | age,
+ | title,
+ | offspring,
+ | offspring)
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `a secondary constructor with no arguments passed to delegate`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |data class Foo {
+ | constructor() :
+ | this(
+ | Foo.createSpeciallyDesignedParameter(),
+ | Foo.createSpeciallyDesignedParameter(),
+ | )
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `secondary constructor with param list that fits in one line, with delegate`() =
+ assertFormatted(
+ """
+ |class C {
+ | constructor(
+ | context: Context?,
+ | attrs: AttributeSet?,
+ | defStyleAttr: Int,
+ | defStyleRes: Int
+ | ) : super(context, attrs, defStyleAttr, defStyleRes) {
+ | init(attrs)
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle calling super constructor in secondary constructor`() =
+ assertFormatted(
+ """
+ |class Foo : Bar {
+ | internal constructor(number: Int) : super(number) {}
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle super statement with with type argument`() =
+ assertFormatted(
+ """
+ |class Foo : Bar(), FooBar {
+ | override fun doIt() {
+ | super<FooBar>.doIt()
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle super statement with with label argument`() =
+ assertFormatted(
+ """
+ |class Foo : Bar(), FooBar {
+ | override fun doIt() {
+ | foo.doThat {
+ | super<FooBar>@Foo.doIt()
+ |
+ | // this one is actually generics on the call expression, not super
+ | super@Foo<FooBar>.doIt()
+ | }
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `primary constructor without parameters with a KDoc`() =
+ assertFormatted(
+ """
+ |class Class
+ |/** A comment */
+ |constructor() {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle objects`() = assertFormatted("""
+ |object Foo(n: Int) {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle object expression`() =
+ assertFormatted(
+ """
+ |fun f(): Any {
+ | return object : Adapter() {}
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle object expression in parenthesis`() =
+ assertFormatted(
+ """
+ |fun f(): Any {
+ | return (object : Adapter() {})
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle array indexing operator`() =
+ assertFormatted(
+ """
+ |fun f(a: Magic) {
+ | a[3]
+ | b[3, 4]
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `keep array indexing grouped with expression is possible`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun f(a: Magic) {
+ | foo.bar()
+ | .foobar[1, 2, 3]
+ | foo.bar()
+ | .foobar[
+ | 1,
+ | 2,
+ | 3,
+ | 4,
+ | 5]
+ | foo.bar()
+ | .foobar[1, 2, 3]
+ | .barfoo[3, 2, 1]
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `mixed chains`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun f(a: Magic) {
+ | foo.bar()
+ | .foobar[1, 2, 3]
+ | foo.bar()
+ | .foobar[
+ | 1,
+ | 2,
+ | 3,
+ | 4,
+ | 5]
+ | foo.bar()
+ | .foobar[1, 2, 3]
+ | .barfoo[3, 2, 1]
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle destructuring declaration`() =
+ assertFormatted(
+ """
+ |-----------------------------------------------
+ |fun f() {
+ | val (a, b: Int) = listOf(1, 2)
+ | val (asd, asd, asd, asd, asd, asd, asd) =
+ | foo.bar(asdasd, asdasd)
+ |
+ | val (accountType, accountId) =
+ | oneTwoThreeFourFiveSixSeven(
+ | foo, bar, zed, boo)
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+ @Test
+ fun `chains with derferences and array indexing`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun f() {
+ | foo.bam()
+ | .uber!![0, 1, 2]
+ | .boom()[1, 3, 5]
+ | .lah
+ | .doo { it }
+ | .feep[1]
+ | as Boo
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `block like syntax after dereferences and indexing with short lines`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun f() {
+ | foo.bam()
+ | .uber!![0, 1, 2]
+ | .forEach {
+ | println(it)
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `block like syntax after dereferences and indexing with long lines`() =
+ assertFormatted(
+ """
+ |----------------------------------
+ |fun f() {
+ | foo.uber!![0, 1, 2].forEach {
+ | println(it)
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `try to keep type names together`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun f() {
+ | com.facebook.foo.Foo(
+ | 1, 2)
+ | com.facebook.foo
+ | .Foo(1, 2)
+ | .andAlsoThis()
+ | com.facebook.Foo.foo(
+ | 1, 2)
+ | com.facebook
+ | .foobarFoo
+ | .foo(1, 2)
+ | foo.invoke(
+ | foo, bar, bar)
+ | foo.invoke(foo, bar)
+ | .invoke()
+ | FooFoo.foooooooo()
+ | .foooooooo()
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `avoid breaking brackets and keep them with array name`() =
+ assertFormatted(
+ """
+ |-------------------------------------------------------------------------
+ |fun f() {
+ | val a =
+ | invokeIt(context.packageName)
+ | .getInternalMutablePackageInfo(context.packageName)
+ | .someItems[0]
+ | .getInternalMutablePackageInfo(context.packageName)
+ | .someItems[0]
+ | .doIt()
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `keep function call with type name even if array expression is next`() =
+ assertFormatted(
+ """
+ |class f {
+ | private val somePropertyWithBackingOne
+ | get() =
+ | _somePropertyWithBackingOne
+ | ?: Classname.getStuff<SomePropertyRelatedClassProvider>(requireContext())[
+ | somePropertiesProvider, somePropertyCallbacks]
+ | .also { _somePropertyWithBackingOne = it }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `array access in middle of chain and end of it behaves similarly`() =
+ assertFormatted(
+ """
+ |--------------------------------------
+ |fun f() {
+ | if (aaaaa == null ||
+ | aaaaa.bbbbb[0] == null ||
+ | aaaaa.bbbbb[0].cc == null ||
+ | aaaaa.bbbbb[0].dddd == null) {
+ | println()
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle qmark for nullalble types`() =
+ assertFormatted(
+ """
+ |fun doItWithNullReturns(a: String, b: String): Int? {
+ | return 5
+ |}
+ |
+ |fun doItWithNulls(a: String, b: String?) {}
+ |""".trimMargin())
+
+ @Test
+ fun `nullable function type`() =
+ assertFormatted("""
+ |var listener: ((Boolean) -> Unit)? = null
+ |""".trimMargin())
+
+ @Test
+ fun `redundant parenthesis in function types`() =
+ assertFormatted(
+ """
+ |val a: (Int) = 7
+ |
+ |var listener: ((Boolean) -> Unit) = foo
+ |""".trimMargin())
+
+ @Test
+ fun `handle string literals`() =
+ assertFormatted(
+ """
+ |fun doIt(world: String) {
+ | println("Hello world!")
+ | println("Hello! ${'$'}world")
+ | println("Hello! ${'$'}{"wor" + "ld"}")
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle multiline string literals`() =
+ assertFormatted(
+ """
+ |fun doIt(world: String) {
+ | println(${"\"".repeat(3)}Hello
+ | world!${"\"".repeat(3)})
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `Trailing whitespaces are preserved in multiline strings`() {
+ val code =
+ listOf(
+ "fun doIt(world: String) {",
+ " println(\"\"\"This line has trailing whitespace ",
+ " world!\"\"\")",
+ " println(\"\"\"This line has trailing whitespace \$s ",
+ " world!\"\"\")",
+ " println(\"\"\"This line has trailing whitespace \${s} ",
+ " world!\"\"\")",
+ " println(\"\"\"This line has trailing whitespace \$ ",
+ " world!\"\"\")",
+ "}",
+ "")
+ .joinToString("\n")
+ assertThatFormatting(code).allowTrailingWhitespace().isEqualTo(code)
+ }
+
+ @Test
+ fun `Consecutive line breaks in multiline strings are preserved`() =
+ assertFormatted(
+ """
+ |val x = ""${'"'}
+ |
+ |
+ |
+ |Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+ |""${'"'}
+ |""".trimMargin())
+
+ @Test
+ fun `Trailing spaces in a comment are not preserved`() {
+ val before =
+ listOf("// trailing spaces in a comment are not preserved ", "").joinToString("\n")
+ val after = listOf("// trailing spaces in a comment are not preserved", "").joinToString("\n")
+ assertThatFormatting(before).allowTrailingWhitespace().isEqualTo(after)
+ }
+
+ @Test
+ fun `Code with tombstones is not supported`() {
+ val code = """
+ |fun good() {
+ | // ${'\u0003'}
+ |}
+ |""".trimMargin()
+ try {
+ Formatter.format(code)
+ fail()
+ } catch (e: ParseError) {
+ assertThat(e.errorDescription).contains("\\u0003")
+ assertThat(e.lineColumn.line).isEqualTo(1)
+ assertThat(e.lineColumn.column).isEqualTo(5)
+ }
+ }
+
+ @Test
+ fun `handle some basic generics scenarios`() =
+ assertFormatted(
+ """
+ |fun <T> doIt(a: List<T>): List<Int>? {
+ | val b: List<Int> = convert<Int>(listOf(5, 4))
+ | return b
+ |}
+ |
+ |class Foo<T>
+ |""".trimMargin())
+
+ @Test
+ fun `handle for loops`() =
+ assertFormatted(
+ """
+ |fun f(a: List<Int>) {
+ | for (i in a.indices) {
+ | println(i)
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle for loops with long dot chains`() =
+ assertFormatted(
+ """
+ |-----------------------------------
+ |fun f(a: Node<Int>) {
+ | for (child in node.next.data()) {
+ | println(child)
+ | }
+ | for (child in
+ | node.next.next.data()) {
+ | println(child)
+ | }
+ | for (child in
+ | node.next.next.next.next
+ | .data()) {
+ | println(child)
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `when two lambdas following a call, indent the lambda properly`() =
+ assertFormatted(
+ """
+ |----------------------------
+ |fun f() {
+ | doIt()
+ | .apply {
+ | number =
+ | computeNumber1()
+ | }
+ | .apply {
+ | number = 2 * number
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `when two lambdas following a field, indent the lambda properly`() =
+ assertFormatted(
+ """
+ |----------------------------
+ |fun f() {
+ | field
+ | .apply {
+ | number =
+ | computeNumber1()
+ | }
+ | .apply {
+ | number = 2 * number
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `break after 'four' (even though it's 4 chars long) because there's a lambda afterwards`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | four
+ | .let {
+ | //
+ | foo()
+ | }
+ | .methodCall()
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `keep last expression in qualified indented`() =
+ assertFormatted(
+ """
+ |----------------------------
+ |fun f() {
+ | Stuff()
+ | .doIt(
+ | Foo.doIt()
+ | .doThat())
+ | .doIt(
+ | Foo.doIt()
+ | .doThat())
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `properly place lambda arguments into blocks`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun f() {
+ | foo {
+ | red.orange.yellow()
+ | }
+ |
+ | foo.bar {
+ | red.orange.yellow()
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `properly handle one statement lambda with comment`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun f() {
+ | foo {
+ | // this is a comment
+ | red.orange.yellow()
+ | }
+ | foo {
+ | /* this is also a comment */
+ | red.orange.yellow()
+ | }
+ | foo.bar {
+ | // this is a comment
+ | red.orange.yellow()
+ | }
+ | foo.bar() {
+ | // this is a comment
+ | red.orange.yellow()
+ | }
+ | foo.bar {
+ | /* this is also a comment */
+ | red.orange.yellow()
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `properly handle one statement lambda with comment after body statements`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun f() {
+ | foo {
+ | red.orange.yellow()
+ | // this is a comment
+ | }
+ | foo {
+ | red.orange.yellow()
+ | /* this is also a comment */
+ | }
+ | foo.bar {
+ | red.orange.yellow()
+ | // this is a comment
+ | }
+ | foo.bar() {
+ | red.orange.yellow()
+ | // this is a comment
+ | }
+ | foo.bar {
+ | red.orange.yellow()
+ | /* this is also a comment */
+ | }
+ | red.orange.yellow()
+ | // this is a comment
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `try to keep expression in the same line until the first lambda`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |fun f() {
+ | foo.bar.bar?.let {
+ | a()
+ | }
+ | foo.bar.bar?.let {
+ | action()
+ | action2()
+ | }
+ | foo.bar.bar.bar.bar
+ | ?.let { a() }
+ | foo.bar.bar.bar.bar
+ | ?.let {
+ | action()
+ | action2()
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `different indentation in chained calls`() =
+ assertFormatted(
+ """
+ |---------------------------
+ |fun f() {
+ | fooDdoIt(
+ | foo1, foo2, foo3)
+ | foo.doIt(
+ | foo1, foo2, foo3)
+ | foo.doIt(
+ | foo1, foo2, foo3)
+ | .doThat()
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `always add a conditional break for a lambda which is not last`() =
+ assertFormatted(
+ """
+ |--------------------
+ |fun f() {
+ | foofoo
+ | .doIt {
+ | doStuff()
+ | }
+ | .doIt {
+ | doStuff()
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `keep parenthesis and braces together when there's only one lambda argument`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | doIt({})
+ | doIt({ it + it })
+ | doIt({
+ | val a = it
+ | a + a
+ | })
+ | doIt(functor = { it + it })
+ | doIt(
+ | functor = {
+ | val a = it
+ | a + a
+ | })
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `Qualified type`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | var plusFour: Indent.Const
+ | var x: Map.Entry<String, Integer>
+ | var x: List<String>.Iterator
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle destructuring declaration in for loop`() =
+ assertFormatted(
+ """
+ |fun f(a: List<Pair<Int, Int>>) {
+ | for ((x, y: Int) in a) {}
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle function references`() =
+ assertFormatted(
+ """
+ |--------------------------------
+ |fun f(a: List<Int>) {
+ | a.forEach(::println)
+ | a.map(Int::toString)
+ | a.map(String?::isNullOrEmpty)
+ | a.map(
+ | SuperLongClassName?::
+ | functionName)
+ | val f =
+ | SuperLongClassName::
+ | functionName
+ | val g =
+ | invoke(a, b)::functionName
+ | val h =
+ | invoke(a, b, c)::
+ | functionName
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle escaped identifier`() =
+ assertFormatted(
+ """
+ |import foo as `foo foo`
+ |import org.mockito.Mockito.`when` as `yay yay`
+ |
+ |fun `spaces in functions`() {
+ | val `when` = NEVER
+ | val (`do not`, `ever write`) = SERIOUSLY
+ | val `a a`: Int
+ | `yay yay`(`foo foo`)
+ |}
+ |
+ |class `more spaces`
+ |""".trimMargin())
+
+ @Test
+ fun `handle annotations with arguments`() =
+ assertFormatted(
+ """
+ |@Px fun f(): Int = 5
+ |
+ |@Dimenstion(unit = DP) fun g(): Int = 5
+ |
+ |@RunWith(MagicRunner::class)
+ |@Px
+ |class Test {
+ | //
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `no newlines after annotations if entire expr fits in one line`() =
+ assertFormatted(
+ """
+ |-----------------------------------------------
+ |@Px @Px fun f(): Int = 5
+ |
+ |@Px
+ |@Px
+ |@Px
+ |@Px
+ |@Px
+ |@Px
+ |@Px
+ |@Px
+ |fun f(): Int = 5
+ |
+ |@Px
+ |@Px
+ |fun f(): Int {
+ | return 5
+ |}
+ |
+ |@Dimenstion(unit = DP) @Px fun g(): Int = 5
+ |
+ |@Dimenstion(unit = DP)
+ |@Px
+ |fun g(): Int {
+ | return 5
+ |}
+ |
+ |@RunWith @Px class Test
+ |
+ |@RunWith(MagicRunner::class) @Px class Test
+ |
+ |@RunWith @Px class Test {}
+ |
+ |@RunWith(MagicRunner::class) @Px class Test {}
+ |
+ |@RunWith(MagicRunner::class)
+ |@Px
+ |@Px
+ |class Test {}
+ |
+ |@RunWith(MagicRunner::class)
+ |@Px
+ |class Test {
+ | //
+ |}
+ |
+ |fun f() {
+ | if (@Stuff(Magic::class) isGood()) {
+ | println("")
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `no newlines after annotations on properties if entire expression fits in one line`() =
+ assertFormatted(
+ """
+ |--------------------------------------------
+ |@Suppress("UnsafeCast")
+ |val ClassA.methodA
+ | get() = foo as Bar
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `when annotations cause line breaks, and constant has no type dont break before value`() =
+ assertFormatted(
+ """
+ |----------------------------------------------------------
+ |object Foo {
+ | @LongLongLongLongAnnotation
+ | @LongLongLongLongLongAnnotation
+ | private val ROW_HEIGHT = 72
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `annotations in literal function types`() =
+ assertFormatted(
+ """
+ |val callback: (@Anno List<@JvmSuppressWildcards String>) -> Unit = foo
+ |""".trimMargin())
+
+ @Test
+ fun `annotations on type parameters`() =
+ assertFormatted(
+ """
+ |class Foo<@Anno out @Anno T, @Anno in @Anno U> {
+ | inline fun <@Anno reified @Anno X, @Anno reified @Anno Y> bar() {}
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `annotations on type constraints`() =
+ assertFormatted(
+ """
+ |class Foo<T : @Anno Kip, U> where U : @Anno Kip, U : @Anno Qux {
+ | fun <T : @Anno Kip, U> bar() where U : @Anno Kip, U : @Anno Qux {}
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `annotations on type arguments`() =
+ assertFormatted("""
+ |fun foo(x: Foo<in @Anno Int>) {}
+ |""".trimMargin())
+
+ @Test
+ fun `annotations on destructuring declaration elements`() =
+ assertFormatted("""
+ |val x = { (@Anno x, @Anno y) -> x }
+ |""".trimMargin())
+
+ @Test
+ fun `annotations on exceptions`() =
+ assertFormatted(
+ """
+ |fun doIt() {
+ | try {
+ | doItAgain()
+ | } catch (@Nullable e: Exception) {
+ | //
+ | } catch (@Suppress("GeneralException") e: Exception) {}
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `Unary prefix expressions`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | !a
+ | -4
+ | val x = -foo()
+ | +4
+ | ++a
+ | --a
+ |
+ | + +a
+ | +-a
+ | +!a
+ | -+a
+ | - -a
+ | -!a
+ | !+a
+ | !a
+ | ! !a
+ |
+ | + ++a
+ | +--a
+ | -++a
+ | - --a
+ | !++a
+ | !--a
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `Unary postfix expressions`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | a!!
+ | a++
+ | a--
+ |
+ | a--!!
+ | a++!!
+ |
+ | a!! !!
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle wildcard generics`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | val l: List<*>
+ | val p: Pair<*, *>
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle covariant and contravariant type arguments`() =
+ assertFormatted("""
+ |val p: Pair<in T, out S>
+ |""".trimMargin())
+
+ @Test
+ fun `handle covariant and contravariant type parameters`() =
+ assertFormatted("""
+ |class Foo<in T, out S>
+ |""".trimMargin())
+
+ @Test
+ fun `handle bounds for type parameters`() =
+ assertFormatted("""
+ |class Foo<in T : List<*>, out S : Any?>
+ |""".trimMargin())
+
+ @Test
+ fun `handle compound generic bounds on classes`() =
+ assertFormatted(
+ """
+ |class Foo<T>(n: Int) where T : Bar, T : FooBar {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle compound generic bounds on functions`() =
+ assertFormatted(
+ """
+ |fun <T> foo(n: Int) where T : Bar, T : FooBar {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle compound generic bounds on properties`() =
+ assertFormatted(
+ """
+ |val <T> List<T>.twiceSum: Int where T : Int
+ | get() {
+ | return 2 * sum()
+ | }
+ |""".trimMargin())
+
+ @Test
+ fun `handle compound generic bounds on class with delegate`() =
+ assertFormatted(
+ """
+ |class Foo<T>() : Bar by bar
+ |where T : Qux
+ |""".trimMargin())
+
+ @Test
+ fun `explicit type on property getter`() =
+ assertFormatted(
+ """
+ |class Foo {
+ | val silly: Int
+ | get(): Int = 1
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle method calls with lambda arg only`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | val a = g { 1 + 1 }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle method calls value args and a lambda arg`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | val a = g(1, 2) { 1 + 1 }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle top level constants`() =
+ assertFormatted(
+ """
+ |-----------------------------
+ |val a = 5
+ |
+ |const val b = "a"
+ |
+ |val a = 5
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle lambda arg with named arguments`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | val b = { x: Int, y: Int -> x + y }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `avoid newline before lambda argument if it is named`() =
+ assertFormatted(
+ """
+ |private fun f(items: List<Int>) {
+ | doSomethingCool(
+ | items,
+ | lambdaArgument = {
+ | step1()
+ | step2()
+ | }) { it.doIt() }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle labeled this pointer`() =
+ assertFormatted(
+ """
+ |class Foo {
+ | fun f() {
+ | g { println(this@Foo) }
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle extension and operator functions`() =
+ assertFormatted("""
+ |operator fun Point.component1() = x
+ |""".trimMargin())
+
+ @Test
+ fun `handle extension methods with very long names`() =
+ assertFormatted(
+ """
+ |------------------------------------------
+ |fun LongReceiverNameThatRequiresBreaking
+ | .doIt() {}
+ |
+ |fun LongButNotTooLong.doIt(
+ | n: Int,
+ | f: Float
+ |) {}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle extension properties`() =
+ assertFormatted(
+ """
+ |val Int.isPrime: Boolean
+ | get() = runMillerRabinPrimality(this)
+ |""".trimMargin())
+
+ @Test
+ fun `generic extension property`() =
+ assertFormatted("""
+ |val <T> List<T>.twiceSize = 2 * size()
+ |""".trimMargin())
+
+ @Test
+ fun `handle file annotations`() =
+ assertFormatted(
+ """
+ |@file:JvmName("DifferentName")
+ |
+ |package com.somecompany.example
+ |
+ |import com.somecompany.example2
+ |
+ |class Foo {
+ | val a = example2("and 1")
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle init block`() =
+ assertFormatted(
+ """
+ |class Foo {
+ | init {
+ | println("Init!")
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle interface delegation`() =
+ assertFormatted(
+ """
+ |class MyList(impl: List<Int>) : Collection<Int> by impl
+ |""".trimMargin())
+
+ @Test
+ fun `handle property delegation`() =
+ assertFormatted("""
+ |val a by lazy { 1 + 1 }
+ |""".trimMargin())
+
+ @Test
+ fun `handle property delegation with type and breaks`() =
+ assertFormatted(
+ """
+ |---------------------------------
+ |val importantValue: Int by lazy {
+ | 1 + 1
+ |}
+ |
+ |val importantValue: Int by lazy {
+ | val b = 1 + 1
+ | b + b
+ |}
+ |
+ |val importantValueLonger:
+ | Int by lazy { 1 + 1 }
+ |
+ |val importantValue: Int by
+ | doIt(1 + 1)
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle multi-annotations with use-site targets`() =
+ assertFormatted(
+ """
+ |class Something {
+ | @field:[Inject Named("WEB_VIEW")]
+ | internal lateinit var httpClient: OkHttpClient
+ |
+ | @field:[Inject Named("WEB_VIEW")]
+ | var httpClient: OkHttpClient
+ |
+ | @Px
+ | @field:[Inject Named("WEB_VIEW")]
+ | var httpClient: OkHttpClient
+ |}
+ |
+ """.trimMargin())
+
+ @Test
+ fun `handle parameters with annoations with parameters`() =
+ assertFormatted(
+ """
+ |class Something {
+ | fun doIt(@Magic(withHat = true) foo: Foo) {
+ | println(foo)
+ | }
+ |}
+ |
+ """.trimMargin())
+
+ @Test
+ fun `handle lambda types`() =
+ assertFormatted(
+ """
+ |val listener1: (Boolean) -> Unit = { b -> !b }
+ |
+ |val listener2: () -> Unit = {}
+ |
+ |val listener3: (Int, Double) -> Int = { a, b -> a }
+ |
+ |val listener4: Int.(Int, Boolean) -> Unit
+ |""".trimMargin())
+
+ @Test
+ fun `handle unicode in string literals`() =
+ assertFormatted("""
+ |val a = "\uD83D\uDC4D"
+ |""".trimMargin())
+
+ @Test
+ fun `handle casting`() =
+ assertFormatted(
+ """
+ |fun castIt(o: Object) {
+ | println(o is Double)
+ | println(o !is Double)
+ | doIt(o as Int)
+ | doIt(o as? Int)
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle casting with breaks`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun castIt(
+ | something: Any
+ |) {
+ | doIt(
+ | something
+ | as List<*>)
+ | doIt(
+ | something
+ | is List<*>)
+ | println(
+ | something
+ | is
+ | List<String>)
+ | doIt(
+ | something
+ | as
+ | List<String>)
+ | println(
+ | something
+ | is
+ | PairList<
+ | String,
+ | Int>)
+ | doIt(
+ | something
+ | as
+ | PairList<
+ | String,
+ | Int>)
+ | println(
+ | a is Int &&
+ | b is String)
+ | l.b?.s?.sOrNull() is
+ | SomethingLongEnough
+ |}
+ |
+ |val a =
+ | l.sOrNull() is
+ | SomethingLongEnough
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle collection literals in annotations`() =
+ assertFormatted(
+ """
+ |@Foo(a = [1, 2])
+ |fun doIt(o: Object) {
+ | //
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle try, catch and finally`() =
+ assertFormatted(
+ """
+ |fun foo() {
+ | try {
+ | bar()
+ | } catch (e: Exception) {
+ | throw e
+ | } finally {
+ | println("finally")
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle infix methods`() =
+ assertFormatted(
+ """
+ |fun numbers() {
+ | (0 until 100).size
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle while loops`() =
+ assertFormatted(
+ """
+ |fun numbers() {
+ | while (1 < 2) {
+ | println("Everything is okay")
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle do while loops`() =
+ assertFormatted(
+ """
+ |fun numbers() {
+ | do {
+ | println("Everything is okay")
+ | } while (1 < 2)
+ |
+ | do while (1 < 2)
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle break and continue`() =
+ assertFormatted(
+ """
+ |fun numbers() {
+ | while (1 < 2) {
+ | if (true) {
+ | break
+ | }
+ | if (false) {
+ | continue
+ | }
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle all kinds of labels and jumps`() =
+ assertFormatted(
+ """
+ |fun f(a: List<Int>) {
+ | a.map {
+ | myloop@ for (i in a) {
+ | if (true) {
+ | break@myloop
+ | } else if (false) {
+ | continue@myloop
+ | } else {
+ | a.map `inner map`@{
+ | return@`inner map`
+ | }
+ | }
+ | }
+ | return@map 2 * it
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `don't crash on top level statements with semicolons`() {
+ val code =
+ """
+ |val x = { 0 };
+ |
+ |foo({ 0 });
+ |
+ |foo { 0 };
+ |
+ |val fill = 0;
+ |""".trimMargin()
+ val expected =
+ """
+ |val x = { 0 }
+ |
+ |foo({ 0 })
+ |
+ |foo { 0 }
+ |
+ |val fill = 0
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `preserve semicolons in enums`() {
+ val code =
+ """
+ |enum class SemiColonIsNotRequired {
+ | TRUE, FALSE;
+ |}
+ |
+ |enum class SemiColonIsRequired {
+ | ONE, TWO;
+ |
+ | fun isOne(): Boolean = this == ONE
+ |}
+ |""".trimMargin()
+ val expected =
+ """
+ |enum class SemiColonIsNotRequired {
+ | TRUE,
+ | FALSE
+ |}
+ |
+ |enum class SemiColonIsRequired {
+ | ONE,
+ | TWO;
+ |
+ | fun isOne(): Boolean = this == ONE
+ |}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `preserve semicolons in comments and strings`() {
+ val code =
+ """
+ |fun f() {
+ | val x = ";"
+ | val x = ""${'"'} don't touch ; in raw strings ""${'"'}
+ |}
+ |
+ |// Don't touch ; inside comments.
+ |
+ |/** Don't touch ; inside comments. */
+ |""".trimMargin()
+ val expected =
+ """
+ |fun f() {
+ | val x = ";"
+ | val x = ""${'"'} don't touch ; in raw strings ""${'"'}
+ |}
+ |
+ |// Don't touch ; inside comments.
+ |
+ |/** Don't touch ; inside comments. */
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `preserve semicolons in empty if-s and while-s`() {
+ val code =
+ """
+ |fun f() {
+ | while (true);
+ | while (true) /** a */ ;
+ |
+ | if (true);
+ | if (true) /** a */ ;
+ |
+ | if (true)
+ | else
+ | ;
+ |}
+ |""".trimMargin()
+ val expected =
+ """
+ |fun f() {
+ | while (true) ;
+ | while (true)
+ | /** a */
+ | ;
+ |
+ | if (true) ;
+ | if (true)
+ | /** a */
+ | ;
+ |
+ | if (true) else ;
+ |}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `preserve semicolons between calls and dead lambdas`() {
+ val code =
+ """
+ |fun f() {
+ | foo(0); { dead -> lambda }
+ |
+ | foo(0) ; { dead -> lambda }
+ |
+ | foo(0) /** a */ ; /** b */ { dead -> lambda }
+ |
+ | foo(0) { trailing -> lambda }; { dead -> lambda }
+ |
+ | foo { trailing -> lambda }; { dead -> lambda }
+ |
+ | val x = foo(); { dead -> lambda }
+ |
+ | val x = bar() && foo(); { dead -> lambda }
+ |
+ | // `z` has a property and a method both named `bar`
+ | val x = z.bar; { dead -> lambda }
+ |
+ | // `this` has a property and a method both named `bar`
+ | val x = bar; { dead -> lambda }
+ |
+ | // Literally any callable expression is dangerous
+ | val x = (if (cond) x::foo else x::bar); { dead -> lambda }
+ |}
+ |""".trimMargin()
+ val expected =
+ """
+ |fun f() {
+ | foo(0);
+ | { dead -> lambda }
+ |
+ | foo(0);
+ | { dead -> lambda }
+ |
+ | foo(0)
+ | /** a */
+ | ;
+ | /** b */
+ | { dead -> lambda }
+ |
+ | foo(0) { trailing -> lambda };
+ | { dead -> lambda }
+ |
+ | foo { trailing -> lambda };
+ | { dead -> lambda }
+ |
+ | val x = foo();
+ | { dead -> lambda }
+ |
+ | val x = bar() && foo();
+ | { dead -> lambda }
+ |
+ | // `z` has a property and a method both named `bar`
+ | val x = z.bar;
+ | { dead -> lambda }
+ |
+ | // `this` has a property and a method both named `bar`
+ | val x = bar;
+ | { dead -> lambda }
+ |
+ | // Literally any callable expression is dangerous
+ | val x = (if (cond) x::foo else x::bar);
+ | { dead -> lambda }
+ |}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `drop redundant semicolons`() {
+ val code =
+ """
+ |package org.examples;
+ |import org.examples.wow.MuchWow;
+ |import org.examples.wow.ManyAmaze
+ |
+ |typealias Int2 = Int;
+ |
+ |fun f() {
+ | val a = 3;
+ | val x = 5 ; val y = ManyAmaze();
+ | myThingMap.forEach { val (key, value) = it; println("mapped ${"$"}MuchWow") }
+ | when {
+ | true -> "1"; false -> "0"
+ | }
+ | someLongVariableName.let {
+ | someReallyLongFunctionNameThatMakesThisNotFitInOneLineWithTheAboveVariable();
+ | }
+ |} ;
+ |
+ |""".trimMargin()
+ val expected =
+ """
+ |package org.examples
+ |
+ |import org.examples.wow.ManyAmaze
+ |import org.examples.wow.MuchWow
+ |
+ |typealias Int2 = Int
+ |
+ |fun f() {
+ | val a = 3
+ | val x = 5
+ | val y = ManyAmaze()
+ | myThingMap.forEach {
+ | val (key, value) = it
+ | println("mapped ${"$"}MuchWow")
+ | }
+ | when {
+ | true -> "1"
+ | false -> "0"
+ | }
+ | someLongVariableName.let {
+ | someReallyLongFunctionNameThatMakesThisNotFitInOneLineWithTheAboveVariable()
+ | }
+ |}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `pretty-print after dropping redundant semicolons`() {
+ val code = """
+ |fun f() {
+ | val veryLongName = 5;
+ |}
+ |""".trimMargin()
+ val expected =
+ """
+ |fun f() {
+ | val veryLongName = 5
+ |}
+ |""".trimMargin()
+ assertThatFormatting(code).withOptions(FormattingOptions(maxWidth = 22)).isEqualTo(expected)
+ }
+
+ @Test
+ fun `handle no parenthesis in lambda calls`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | a { println("a") }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle multi statement lambdas`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | a {
+ | println("a")
+ | println("b")
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle multi line one statement lambda`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |fun f() {
+ | a {
+ | println(foo.bar.boom)
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `statements are wrapped in blocks`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | builder.block {
+ | getArgumentName().accept
+ | return
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `properly break fully qualified nested user types`() =
+ assertFormatted(
+ """
+ |-------------------------------------------------------
+ |val complicated:
+ | com.example.interesting.SomeType<
+ | com.example.interesting.SomeType<Int, Nothing>,
+ | com.example.interesting.SomeType<
+ | com.example.interesting.SomeType<
+ | Int, Nothing>,
+ | Nothing>> =
+ | DUMMY
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle multi-line lambdas within lambdas and calling chains`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | builder.block(ZERO) {
+ | builder.token("when")
+ | expression1.let { subjectExp ->
+ | builder.token(")")
+ | return
+ | }
+ | }
+ | builder.block(ZERO) {
+ | expression2.subjectExpression.let { subjectExp ->
+ | builder.token(")")
+ | return
+ | }
+ | }
+ | builder.block(ZERO) {
+ | expression2.subjectExpression
+ | .let { subjectExp ->
+ | builder.token(")")
+ | return
+ | }
+ | .sum
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle multi line lambdas with explicit args`() =
+ assertFormatted(
+ """
+ |--------------------
+ |fun f() {
+ | a { (x, y) ->
+ | x + y
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle lambda with destructuring and type`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | g { (a, b): List<Int> -> a }
+ | g { (a, b): List<Int>, (c, d): List<Int> -> a }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle parenthesis in lambda calls for now`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | a() { println("a") }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle chaining of calls with lambdas`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | bobby
+ | .map { x -> x * x }
+ | .map { x -> x * x }
+ | ?.map { x ->
+ | val y = x * x
+ | y
+ | }
+ | .sum
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle break of lambda args per line with indentation`() =
+ assertFormatted(
+ """
+ |-----------
+ |fun f() {
+ | a() {
+ | arg1,
+ | arg2,
+ | x ->
+ | doIt()
+ | doIt()
+ | }
+ | a() {
+ | arg1,
+ | arg2,
+ | arg3
+ | ->
+ | doIt()
+ | doIt()
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle trailing comma in lambda`() =
+ assertFormatted(
+ """
+ |-----------
+ |fun f() {
+ | a() {
+ | arg1,
+ | arg2,
+ | x,
+ | ->
+ | doIt()
+ | doIt()
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `break before Elvis operator`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |fun f() {
+ | someObject
+ | .someMethodReturningCollection()
+ | .map { it.someProperty }
+ | .find { it.contains(someSearchValue) }
+ | ?: someDefaultValue
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle comments in the middle of calling chain`() =
+ assertFormatted(
+ """
+ |---------------------------
+ |fun f() {
+ | someObject
+ | .letsDoIt()
+ | // this is a comment
+ | .doItOnce()
+ | // this is a comment
+ | .doItTwice()
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle reified types`() =
+ assertFormatted(
+ """
+ |inline fun <reified T> foo(t: T) {
+ | println(t)
+ |}
+ |
+ |inline fun <reified in T> foo2(t: T) {
+ | println(t)
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle suspended types`() =
+ assertFormatted(
+ """
+ |private val reader: suspend (Key) -> Output?
+ |
+ |private val delete: (suspend (Key) -> Unit)? = null
+ |
+ |inline fun <R> foo(noinline block: suspend () -> R): suspend () -> R
+ |
+ |inline fun <R> bar(noinline block: (suspend () -> R)?): (suspend () -> R)?
+ |""".trimMargin())
+
+ @Test
+ fun `handle simple enum classes`() =
+ assertFormatted(
+ """
+ |enum class BetterBoolean {
+ | TRUE,
+ | FALSE,
+ | FILE_NOT_FOUND,
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle enum class with functions`() =
+ assertFormatted(
+ """
+ |enum class BetterBoolean {
+ | TRUE,
+ | FALSE,
+ | FILE_NOT_FOUND;
+ | fun isGood(): Boolean {
+ | return true
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle enum with annotations`() =
+ assertFormatted(
+ """
+ |enum class BetterBoolean {
+ | @True TRUE,
+ | @False @WhatIsTruth FALSE,
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle enum constructor calls`() =
+ assertFormatted(
+ """
+ |enum class BetterBoolean(val name: String, val value: Boolean = true) {
+ | TRUE("true"),
+ | FALSE("false", false),
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle enum entries with body`() =
+ assertFormatted(
+ """
+ |enum class Animal(canWalk: Boolean = true) {
+ | DOG {
+ | fun speak() = "woof"
+ | },
+ | FISH(false) {},
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle empty enum`() =
+ assertFormatted("""
+ |enum class YTho {
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `expect enum class`() =
+ assertFormatted("""
+ |expect enum class ExpectedEnum
+ |""".trimMargin())
+
+ @Test
+ fun `enum without trailing comma`() =
+ assertFormatted(
+ """
+ |enum class Highlander {
+ | ONE
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `enum comma and semicolon`() {
+ assertThatFormatting(
+ """
+ |enum class Highlander {
+ | ONE,;
+ |}
+ |""".trimMargin())
+ .isEqualTo(
+ """
+ |enum class Highlander {
+ | ONE,
+ |}
+ |""".trimMargin())
+ }
+
+ @Test
+ fun `semicolon is placed on next line when there's a trailing comma in an enum declaration`() =
+ assertFormatted(
+ """
+ |enum class Highlander {
+ | ONE,
+ | TWO,
+ | ;
+ |
+ | fun f() {}
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle varargs and spread operator`() =
+ assertFormatted(
+ """
+ |fun foo(vararg args: String) {
+ | foo2(*args)
+ | foo3(options = *args)
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle typealias`() =
+ assertFormatted(
+ """
+ |----------------------------------------------
+ |private typealias TextChangedListener =
+ | (string: String) -> Unit
+ |
+ |typealias PairPair<X, Y> = Pair<Pair<X, Y>, X>
+ |
+ |class Foo
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle the 'dynamic' type`() =
+ assertFormatted(
+ """
+ |fun x(): dynamic = "x"
+ |
+ |val dyn: dynamic = 1
+ |""".trimMargin())
+
+ @Test
+ fun `handle class expression with generics`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | println(Array<String>::class.java)
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `ParseError contains correct line and column numbers`() {
+ val code =
+ """
+ |// Foo
+ |fun good() {
+ | //
+ |}
+ |
+ |fn (
+ |""".trimMargin()
+ try {
+ Formatter.format(code)
+ fail()
+ } catch (e: ParseError) {
+ assertThat(e.lineColumn.line).isEqualTo(6)
+ assertThat(e.lineColumn.column).isEqualTo(0)
+ assertThat(e.errorDescription).contains("Expecting an expression")
+ }
+ }
+
+ @Test
+ fun `fail() reports line+column number`() {
+ val code =
+ """
+ |// Foo
+ |fun good() {
+ | return@ 5
+ |}
+ |""".trimMargin()
+ try {
+ Formatter.format(code)
+ fail()
+ } catch (e: ParseError) {
+ assertThat(e.lineColumn.line).isEqualTo(2)
+ assertThat(e.lineColumn.column).isEqualTo(8)
+ }
+ }
+
+ @Test
+ fun `annotations on class, fun, parameters and literals`() =
+ assertFormatted(
+ """
+ |@Fancy
+ |class Foo {
+ | @Fancy
+ | fun baz(@Fancy foo: Int): Int {
+ | return (@Fancy 1)
+ | }
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `annotations on function types`() =
+ assertFormatted(
+ """
+ |fun foo(bar: @StringRes Int) {}
+ |
+ |fun foo(error: @Composable ((x) -> Unit)) {}
+ |
+ |fun foo(error: (@Composable (x) -> Unit)) {}
+ |
+ |fun foo(
+ | error:
+ | @field:[Inject Named("WEB_VIEW")]
+ | ((x) -> Unit)
+ |) {}
+ |
+ |fun foo(
+ | error:
+ | (@field:[Inject Named("WEB_VIEW")]
+ | (x) -> Unit)
+ |) {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle annotations with use-site targets`() =
+ assertFormatted(
+ """
+ |class FooTest {
+ | @get:Rule val exceptionRule: ExpectedException = ExpectedException.none()
+ |
+ | @set:Magic(name = "Jane") var field: String
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `handle annotations mixed with keywords since we cannot reorder them for now`() =
+ assertFormatted(
+ """
+ |public @Magic final class Foo
+ |
+ |public @Magic(1) final class Foo
+ |
+ |@Magic(1) public final class Foo
+ |""".trimMargin())
+
+ @Test
+ fun `handle annotations more`() =
+ assertFormatted(
+ """
+ |-------------------------------------------------
+ |@Anno1
+ |@Anno2(param = Param1::class)
+ |@Anno3
+ |@Anno4(param = Param2::class)
+ |class MyClass {}
+ |
+ |fun f() {
+ | @Suppress("MagicNumber") add(10)
+ |
+ | @Annotation // test a comment after annotations
+ | return 5
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `annotated expressions`() =
+ assertFormatted(
+ """
+ |------------------------------------------------
+ |fun f() {
+ | @Suppress("MagicNumber") add(10) && add(20)
+ |
+ | @Suppress("MagicNumber")
+ | add(10) && add(20)
+ |
+ | @Anno1 @Anno2(param = Param1::class)
+ | add(10) && add(20)
+ |
+ | @Anno1
+ | @Anno2(param = Param1::class)
+ | @Anno3
+ | @Anno4(param = Param2::class)
+ | add(10) && add(20)
+ |
+ | @Anno1
+ | @Anno2(param = Param1::class)
+ | @Anno3
+ | @Anno4(param = Param2::class)
+ | add(10) && add(20)
+ |
+ | @Suppress("MagicNumber") add(10) &&
+ | add(20) &&
+ | add(30)
+ |
+ | add(@Suppress("MagicNumber") 10) &&
+ | add(20) &&
+ | add(30)
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `annotated function declarations`() =
+ assertFormatted(
+ """
+ |@Anno
+ |fun f() {
+ | add(10)
+ |}
+ |
+ |@Anno(param = 1)
+ |fun f() {
+ | add(10)
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `annotated class declarations`() =
+ assertFormatted(
+ """
+ |@Anno class F
+ |
+ |@Anno(param = 1) class F
+ |
+ |@Anno(P)
+ |// Foo
+ |@Anno("param")
+ |class F
+ |""".trimMargin())
+
+ @Test
+ fun `handle type arguments in annotations`() =
+ assertFormatted(
+ """
+ |@TypeParceler<UUID, UUIDParceler>() class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle one line KDoc`() =
+ assertFormatted(
+ """
+ |/** Hi, I am a one line kdoc */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle KDoc with Link`() =
+ assertFormatted(
+ """
+ |/** This links to [AnotherClass] */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle KDoc with paragraphs`() =
+ assertFormatted(
+ """
+ |/**
+ | * Hi, I am a two paragraphs kdoc
+ | *
+ | * There's a space line to preserve between them
+ | */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle KDoc with blocks`() =
+ assertFormatted(
+ """
+ |/**
+ | * Hi, I am a two paragraphs kdoc
+ | *
+ | * @param param1 this is param1
+ | * @param[param2] this is param2
+ | */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle KDoc with code examples`() =
+ assertFormatted(
+ """
+ |/**
+ | * This is how you write a simple hello world in Kotlin:
+ | *
+ | * ```
+ | * fun main(args: Array<String>) {
+ | * println("Hello World!")
+ | * }
+ | * ```
+ | *
+ | * Amazing ah?
+ | * ```
+ | * fun `code can be with a blank line above it` () {}
+ | * ```
+ | * Or after it!
+ | */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle KDoc with tagged code examples`() =
+ assertFormatted(
+ """
+ |/**
+ | * ```kotlin
+ | * fun main(args: Array<String>) {
+ | * println("Hello World!")
+ | * }
+ | * ```
+ | */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle stray code markers in lines and produce stable output`() {
+ val code =
+ """
+ |/**
+ | * Look! code: ``` aaa
+ | * fun f() = Unit
+ | * foo
+ | * ```
+ | */
+ |class MyClass {}
+ |""".trimMargin()
+ assertFormatted(Formatter.format(code))
+ }
+
+ @Test
+ fun `code block with triple backtick`() {
+ val code =
+ """
+ |/**
+ | * Look! code:
+ | * ```
+ | * aaa ``` wow
+ | * ```
+ | */
+ |class MyClass {}
+ |""".trimMargin()
+ val expected =
+ """
+ |/**
+ | * Look! code:
+ | * ```
+ | * aaa ``` wow
+ | * ```
+ | */
+ |class MyClass {}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `when code closer in mid of line, produce stable output`() {
+ val code =
+ """
+ |/**
+ | * Look! code: ``` aaa
+ | * fun f() = Unit
+ | * foo ``` wow
+ | */
+ |class MyClass {}
+ |""".trimMargin()
+ assertFormatted(Formatter.format(code))
+ }
+
+ @Test
+ fun `handle KDoc with link reference`() =
+ assertFormatted(
+ """
+ |/** Doc line with a reference to [AnotherClass] in the middle of a sentence */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle KDoc with links one after another`() =
+ assertFormatted(
+ """
+ |/** Here are some links [AnotherClass] [AnotherClass2] */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `don't add spaces after links in Kdoc`() =
+ assertFormatted(
+ """
+ |/** Here are some links [AnotherClass][AnotherClass2]hello */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `don't remove spaces after links in Kdoc`() =
+ assertFormatted(
+ """
+ |/** Please see [onNext] (which has more details) */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `link anchor in KDoc are preserved`() =
+ assertFormatted(
+ """
+ |/** [link anchor](the URL for the link anchor goes here) */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `don't add spaces between links in KDoc (because they're actually references)`() =
+ assertFormatted(
+ """
+ |/** Here are some links [AnotherClass][AnotherClass2] */
+ |class MyClass {}
+ |
+ |/** The final produced value may have [size][ByteString.size] < [bufferSize]. */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `collapse spaces after links in KDoc`() {
+ val code =
+ """
+ |/** Here are some links [Class1], [Class2] [Class3]. hello */
+ |class MyClass {}
+ |""".trimMargin()
+ val expected =
+ """
+ |/** Here are some links [Class1], [Class2] [Class3]. hello */
+ |class MyClass {}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `collapse newlines after links in KDoc`() {
+ val code =
+ """
+ |/**
+ | * Here are some links [Class1]
+ | * [Class2]
+ | */
+ |class MyClass {}
+ |""".trimMargin()
+ val expected =
+ """
+ |/** Here are some links [Class1] [Class2] */
+ |class MyClass {}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `do not crash because of malformed KDocs and produce stable output`() {
+ val code = """
+ |/** Surprise ``` */
+ |class MyClass {}
+ |""".trimMargin()
+ assertFormatted(Formatter.format(code))
+ }
+
+ @Test
+ fun `Respect spacing of text after link`() =
+ assertFormatted(
+ """
+ |/** Enjoy this link [linkstuff]. */
+ |class MyClass {}
+ |
+ |/** There are many [FooObject]s. */
+ |class MyClass {}
+ |""".trimMargin())
+
+ @Test
+ fun `handle KDoc with multiple separated param tags, breaking and merging lines and missing asterisk`() {
+ val code =
+ """
+ |/**
+ | * Trims leading whitespace characters followed by [marginPrefix] from every line of a source string and removes
+ | * the first and the last lines if they are blank (notice difference blank vs empty).
+ |
+ | * Doesn't affect a line if it doesn't contain [marginPrefix] except the first and the last blank lines.
+ | *
+ | * Doesn't preserve the original line endings.
+ | *
+ | * @param marginPrefix non-blank string, which is used as a margin delimiter. Default is `|` (pipe character).
+ | *
+ | * @sample samples.text.Strings.trimMargin
+ | * @see trimIndent
+ | * @see kotlin.text.isWhitespace
+ | */
+ |class ThisWasCopiedFromTheTrimMarginMethod {}
+ |""".trimMargin()
+ val expected =
+ """
+ |/**
+ | * Trims leading whitespace characters followed by [marginPrefix] from every line of a source string
+ | * and removes the first and the last lines if they are blank (notice difference blank vs empty).
+ | *
+ | * Doesn't affect a line if it doesn't contain [marginPrefix] except the first and the last blank
+ | * lines.
+ | *
+ | * Doesn't preserve the original line endings.
+ | *
+ | * @param marginPrefix non-blank string, which is used as a margin delimiter. Default is `|` (pipe
+ | * character).
+ | *
+ | * @sample samples.text.Strings.trimMargin
+ | * @see trimIndent
+ | * @see kotlin.text.isWhitespace
+ | */
+ |class ThisWasCopiedFromTheTrimMarginMethod {}
+ |""".trimMargin()
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+
+ @Test
+ fun `KDoc is reflowed`() {
+ val code =
+ """
+ |/** Lorem ipsum dolor sit amet, consectetur */
+ |class MyClass {}
+ |""".trimMargin()
+ val expected =
+ """
+ |/**
+ | * Lorem ipsum dolor sit amet,
+ | * consectetur
+ | */
+ |class MyClass {}
+ |""".trimMargin()
+ assertThatFormatting(code).withOptions(FormattingOptions(maxWidth = 33)).isEqualTo(expected)
+ }
+
+ @Test
+ fun `sanity - block and continuation indents are 4`() {
+ val code =
+ """
+ |fun f() {
+ | for (child in
+ | node.next.next.next.next
+ | .data()) {
+ | println(child)
+ | }
+ |}
+ |""".trimMargin()
+ assertThatFormatting(code)
+ .withOptions(FormattingOptions(maxWidth = 35, blockIndent = 4, continuationIndent = 4))
+ .isEqualTo(code)
+ }
+
+ @Test
+ fun `comment after a block is stable and does not add space lines`() =
+ assertFormatted(
+ """
+ |fun doIt() {}
+ |
+ |/* this is the first comment */
+ |""".trimMargin())
+
+ @Test
+ fun `preserve LF, CRLF and CR line endings`() {
+ val lines = listOf("fun main() {", " println(\"test\")", "}")
+ for (ending in listOf("\n", "\r\n", "\r")) {
+ val code = lines.joinToString(ending) + ending
+ assertThatFormatting(code).isEqualTo(code)
+ }
+ }
+
+ @Test
+ fun `handle trailing commas (constructors)`() =
+ assertFormatted(
+ """
+ |--------------------
+ |class Foo(
+ | a: Int,
+ |)
+ |
+ |class Foo(
+ | a: Int,
+ | b: Int,
+ |)
+ |
+ |class Foo(
+ | a: Int,
+ | b: Int
+ |)
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle trailing commas (explicit constructors)`() =
+ assertFormatted(
+ """
+ |------------------------
+ |class Foo
+ |constructor(
+ | a: Int,
+ |)
+ |
+ |class Foo
+ |constructor(
+ | a: Int,
+ | b: Int,
+ |)
+ |
+ |class Foo
+ |constructor(
+ | a: Int,
+ | b: Int
+ |)
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle trailing commas (secondary constructors)`() =
+ assertFormatted(
+ """
+ |------------------------
+ |class Foo {
+ | constructor(
+ | a: Int,
+ | )
+ |}
+ |
+ |class Foo {
+ | constructor(
+ | a: Int,
+ | b: Int,
+ | )
+ |}
+ |
+ |class Foo {
+ | constructor(
+ | a: Int,
+ | b: Int
+ | )
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle trailing commas (function definitions)`() =
+ assertFormatted(
+ """
+ |------------------------
+ |fun <
+ | T,
+ |> foo() {}
+ |
+ |fun <
+ | T,
+ | S,
+ |> foo() {}
+ |
+ |fun foo(
+ | a: Int,
+ |) {}
+ |
+ |fun foo(
+ | a: Int,
+ | b: Int
+ |) {}
+ |
+ |fun foo(
+ | a: Int,
+ | b: Int,
+ |) {}
+ |
+ |fun foo(
+ | a: Int,
+ | b: Int,
+ | c: Int,
+ |) {}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle trailing commas (function calls)`() =
+ assertFormatted(
+ """
+ |------------------------
+ |fun main() {
+ | foo(
+ | 3,
+ | )
+ |
+ | foo<Int>(
+ | 3,
+ | )
+ |
+ | foo<
+ | Int,
+ | >(
+ | 3,
+ | )
+ |
+ | foo<Int>(
+ | "asdf", "asdf")
+ |
+ | foo<
+ | Int,
+ | >(
+ | "asd",
+ | "asd",
+ | )
+ |
+ | foo<
+ | Int,
+ | Boolean,
+ | >(
+ | 3,
+ | )
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle trailing commas (proprties)`() =
+ assertFormatted(
+ """
+ |--------------------------
+ |val foo: String
+ | set(
+ | value,
+ | ) {}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle trailing commas (higher-order functions)`() =
+ assertFormatted(
+ """
+ |--------------------------
+ |fun foo(
+ | x:
+ | (
+ | Int,
+ | ) -> Unit
+ |) {}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle trailing commas (after lambda arg)`() =
+ assertFormatted(
+ """
+ |--------------------------
+ |fun foo() {
+ | foo({ it },)
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle trailing commas (other)`() =
+ assertFormatted(
+ """
+ |--------------------------
+ |fun main() {
+ | val (
+ | x: Int,
+ | ) = foo()
+ | val (
+ | x: Int,
+ | y: Int,
+ | ) = foo()
+ |
+ | val (
+ | x: Int,
+ | ) = foo(
+ | blablablablFoobar,
+ | alskdjasld)
+ |
+ | val (
+ | x: Int,
+ | y: Int,
+ | ) = foo(
+ | blablablablFoobar,
+ | asldkalsd)
+ |
+ | a[
+ | 0,
+ | ] = 43
+ | a[
+ | 0,
+ | 1,
+ | ] = 43
+ |
+ | [
+ | 0,
+ | ]
+ | [
+ | 0,
+ | 1,
+ | ]
+ |
+ | when (foo) {
+ | 'x', -> 43
+ | 'x',
+ | 'y', -> 43
+ | 'x',
+ | 'y',
+ | 'z',
+ | 'w',
+ | 'a',
+ | 'b', -> 43
+ | }
+ |
+ | try {
+ | //
+ | } catch (e: Error,) {
+ | //
+ | }
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `assignment of a scoping function`() =
+ assertFormatted(
+ """
+ |----------------------------
+ |val foo = coroutineScope {
+ | foo()
+ | //
+ |}
+ |
+ |fun foo() = coroutineScope {
+ | foo()
+ | //
+ |}
+ |
+ |fun foo() = use { x ->
+ | foo()
+ | //
+ |}
+ |
+ |fun foo() =
+ | coroutineScope { x ->
+ | foo()
+ | //
+ | }
+ |
+ |fun foo() =
+ | Runnable @Px {
+ | foo()
+ | //
+ | }
+ |
+ |fun longName() =
+ | coroutineScope {
+ | foo()
+ | //
+ | }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `top level properties with other types preserve newline spacing`() {
+ assertFormatted(
+ """
+ |---------------------------------
+ |fun something() {
+ | println("hi")
+ |}
+ |
+ |const val SOME_CONST = 1
+ |val SOME_STR = "hi"
+ |// Single comment
+ |val SOME_INT = 1
+ |
+ |// Intentional space above single comment
+ |val SOME_INT2 = 1
+ |
+ |val FOO = 2
+ |const val BAR = 3
+ |
+ |fun baz() = 1
+ |
+ |val d = 1
+ |
+ |class Bar {}
+ |
+ |val e = 1
+ |/** Doc block */
+ |val f = 1
+ |
+ |/** Intent. space above doc */
+ |val g = 1
+ |
+ |data class Qux(val foo: String)
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ assertThatFormatting(
+ """
+ |import com.example.foo
+ |import com.example.bar
+ |const val SOME_CONST = foo.a
+ |val SOME_STR = bar.a
+ |""".trimMargin())
+ .isEqualTo(
+ """
+ |import com.example.bar
+ |import com.example.foo
+ |
+ |const val SOME_CONST = foo.a
+ |val SOME_STR = bar.a
+ |""".trimMargin())
+ }
+
+ @Test
+ fun `first line is never empty`() =
+ assertThatFormatting("""
+ |
+ |fun f() {}
+ |""".trimMargin())
+ .isEqualTo("""
+ |fun f() {}
+ |""".trimMargin())
+
+ @Test
+ fun `at most one newline between any adjacent top-level elements`() =
+ assertThatFormatting(
+ """
+ |import com.Bar
+ |
+ |
+ |import com.Foo
+ |
+ |
+ |fun f() {}
+ |
+ |
+ |fun f() {}
+ |
+ |
+ |class C {}
+ |
+ |
+ |class C {}
+ |
+ |
+ |val x = Foo()
+ |
+ |
+ |val x = Bar()
+ |""".trimMargin())
+ .isEqualTo(
+ """
+ |import com.Bar
+ |import com.Foo
+ |
+ |fun f() {}
+ |
+ |fun f() {}
+ |
+ |class C {}
+ |
+ |class C {}
+ |
+ |val x = Foo()
+ |
+ |val x = Bar()
+ |""".trimMargin())
+
+ @Test
+ fun `at least one newline between any adjacent top-level elements, unless it's a property`() =
+ assertThatFormatting(
+ """
+ |import com.Bar
+ |import com.Foo
+ |fun f() {}
+ |fun f() {}
+ |class C {}
+ |class C {}
+ |val x = Foo()
+ |val x = Bar()
+ |""".trimMargin())
+ .isEqualTo(
+ """
+ |import com.Bar
+ |import com.Foo
+ |
+ |fun f() {}
+ |
+ |fun f() {}
+ |
+ |class C {}
+ |
+ |class C {}
+ |
+ |val x = Foo()
+ |val x = Bar()
+ |""".trimMargin())
+
+ @Test
+ fun `handle array of annotations with field prefix`() {
+ val code: String =
+ """
+ |class MyClass {
+ | @field:[JvmStatic Volatile]
+ | var myVar: String? = null
+ |}
+ |
+ """.trimMargin()
+ assertThatFormatting(code).isEqualTo(code)
+ }
+
+ @Test
+ fun `handle array of annotations without field prefix`() {
+ val code: String =
+ """
+ |class MyClass {
+ | @[JvmStatic Volatile]
+ | var myVar: String? = null
+ |}
+ |
+ """.trimMargin()
+ assertThatFormatting(code).isEqualTo(code)
+ }
+
+ // Regression test against https://github.com/facebookincubator/ktfmt/issues/243
+ @Test
+ fun `regression test against Issue 243`() {
+ val code =
+ """
+ |class Foo {
+ | companion object {
+ | var instance: Foo? = null
+ |
+ | fun getInstance() {
+ | return instance ?: synchronized(Foo::class) {
+ | Foo().also { instance = it }
+ | }
+ | }
+ | }
+ |}
+ |""".trimMargin()
+
+ // Don't throw.
+ Formatter.format(code)
+ }
+
+ @Test
+ fun `lambda with required arrow`() =
+ assertFormatted(
+ """
+ |val a = { x: Int -> }
+ |val b = { x: Int -> 0 }
+ |val c = { x: Int ->
+ | val y = 0
+ | y
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `lambda with optional arrow`() =
+ assertFormatted(
+ """
+ |val a = { -> }
+ |val b = { -> 0 }
+ |val c = { ->
+ | val y = 0
+ | y
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `lambda missing optional arrow`() =
+ assertFormatted(
+ """
+ |val a = {}
+ |val b = { 0 }
+ |val c = {
+ | val y = 0
+ | y
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `chaining - many dereferences`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, fit on one line`() =
+ assertFormatted(
+ """
+ |---------------------------------------------------------------------------
+ |rainbow.red.orange.yellow.green.blue.indigo.violet.cyan.magenta.key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one invocation at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .build()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one invocation at end, fit on one line`() =
+ assertFormatted(
+ """
+ |---------------------------------------------------------------------------
+ |rainbow.red.orange.yellow.green.blue.indigo.violet.cyan.magenta.key.build()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, two invocations at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .build()
+ | .shine()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one invocation in the middle`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .shine()
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, two invocations in the middle`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .shine()
+ | .bright()
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one lambda at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .build { it.appear }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one short lambda at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .z { it }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one multiline lambda at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .z {
+ | it
+ | it
+ | }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one short lambda in the middle`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .z { it }
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one multiline lambda in the middle`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .z {
+ | it
+ | it
+ | }
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one multiline lambda in the middle, remainder could fit on one line`() =
+ assertFormatted(
+ """
+ |-----------------------------------------------------------------------------------------
+ |rainbow.red.orange.yellow.green.blue
+ | .z {
+ | it
+ | it
+ | }
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one multiline lambda and two invocations in the middle, remainder could fit on one line`() =
+ assertFormatted(
+ """
+ |-----------------------------------------------------------------------------------------
+ |rainbow.red.orange.yellow.green.blue
+ | .z {
+ | it
+ | it
+ | }
+ | .shine()
+ | .bright()
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one lambda and invocation at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .z { it }
+ | .shine()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one multiline lambda and invocation at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .z {
+ | it
+ | it
+ | }
+ | .shine()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one invocation and lambda at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .shine()
+ | .z { it }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, one short lambda and invocation in the middle`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .z { it }
+ | .shine()
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, invocation and one short lambda in the middle`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .shine()
+ | .z { it }
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, starting with this`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |this.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, starting with this, one invocation at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |this.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .build()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, starting with super`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |super.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, starting with super, one invocation at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |super.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .build()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, starting with short variable`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |z123.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, starting with short variable, one invocation at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |z123.red.orange.yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .build()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, starting with short variable and lambda, invocation at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |z12.z { it }
+ | .red
+ | .orange
+ | .yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .shine()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, starting with this and lambda, invocation at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |this.z { it }
+ | .red
+ | .orange
+ | .yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ | .shine()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many invocations`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.a().b().c()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many invocations, with multiline lambda at end`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.a().b().c().zz {
+ | it
+ | it
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many dereferences, starting type name`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |com.sky.Rainbow.red
+ | .orange
+ | .yellow
+ | .green
+ | .blue
+ | .indigo
+ | .violet
+ | .cyan
+ | .magenta
+ | .key
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many invocations, starting with short variable, lambda at end`() =
+ assertFormatted(
+ """
+ |-------------
+ |z12.shine()
+ | .bright()
+ | .z { it }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - start with invocation, lambda at end`() =
+ assertFormatted(
+ """
+ |---------------------
+ |getRainbow(
+ | aa, bb, cc)
+ | .z { it }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - many invocations, start with lambda`() =
+ assertFormatted(
+ """
+ |---------------------
+ |z { it }
+ | .shine()
+ | .bright()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining - start with type name, end with invocation`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |com.sky.Rainbow
+ | .colorFactory
+ | .build()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline lambda`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.z {
+ | it
+ | it
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline lambda with trailing dereferences`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow
+ | .z {
+ | it
+ | it
+ | }
+ | .red
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline lambda with long name`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow
+ | .someLongLambdaName {
+ | it
+ | it
+ | }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline lambda with long name and trailing dereferences`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow
+ | .someLongLambdaName {
+ | it
+ | it
+ | }
+ | .red
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline lambda with prefix`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.z {
+ | it
+ | it
+ |}
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline lambda with prefix, forced to next line`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .longLambdaName {
+ | it
+ | it
+ | }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline lambda with prefix, forced to next line with another expression`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .key
+ | .longLambdaName {
+ | it
+ | it
+ | }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline arguments`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.shine(
+ | infrared,
+ | ultraviolet,
+ |)
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline arguments with trailing dereferences`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow
+ | .shine(
+ | infrared,
+ | ultraviolet,
+ | )
+ | .red
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline arguments, forced to next line`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .shine(
+ | infrared,
+ | ultraviolet,
+ | )
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline arguments, forced to next line with another expression`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .key
+ | .shine(
+ | infrared,
+ | ultraviolet,
+ | )
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline arguments, forced to next line with another expression, with trailing dereferences`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow.red.orange.yellow
+ | .key
+ | .shine(
+ | infrared,
+ | ultraviolet,
+ | )
+ | .red
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline arguments, with trailing invocation`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow
+ | .shine(
+ | infrared,
+ | ultraviolet,
+ | )
+ | .bright()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline arguments, with trailing lambda`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |rainbow
+ | .shine(
+ | infrared,
+ | ultraviolet,
+ | )
+ | .z { it }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline arguments, prefixed with super, with trailing invocation`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |super.shine(
+ | infrared,
+ | ultraviolet,
+ | )
+ | .bright()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - multiline arguments, starting with short variable, with trailing invocation`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |z12.shine(
+ | infrared,
+ | ultraviolet,
+ | )
+ | .bright()
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - start with multiline arguments`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |getRainbow(
+ | infrared,
+ | ultraviolet,
+ |)
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `chaining (indentation) - start with multiline arguments, with trailing invocation`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |getRainbow(
+ | infrared,
+ | ultraviolet,
+ | )
+ | .z { it }
+ |""".trimMargin(),
+ deduceMaxWidth = true)
+
+ @Test
+ fun `annotations for expressions`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | var b
+ | @Suppress("UNCHECKED_CAST") b = f(1) as Int
+ | @Suppress("UNCHECKED_CAST")
+ | b = f(1) as Int
+ |
+ | @Suppress("UNCHECKED_CAST") b = f(1) to 5
+ | @Suppress("UNCHECKED_CAST")
+ | b = f(1) to 5
+ |
+ | @Suppress("UNCHECKED_CAST") f(1) as Int + 5
+ | @Suppress("UNCHECKED_CAST")
+ | f(1) as Int + 5
+ |
+ | @Anno1 /* comment */ @Anno2 f(1) as Int
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `annotations for expressions 2`() {
+ val code =
+ """
+ |fun f() {
+ | @Suppress("UNCHECKED_CAST") f(1 + f(1) as Int)
+ | @Suppress("UNCHECKED_CAST")
+ | f(1 + f(1) as Int)
+ |}
+ |""".trimMargin()
+
+ val expected =
+ """
+ |fun f() {
+ | @Suppress("UNCHECKED_CAST") f(1 + f(1) as Int)
+ | @Suppress("UNCHECKED_CAST") f(1 + f(1) as Int)
+ |}
+ |""".trimMargin()
+
+ assertThatFormatting(code).isEqualTo(expected)
+ }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/format/GoogleStyleFormatterKtTest.kt b/core/src/test/java/com/facebook/ktfmt/format/GoogleStyleFormatterKtTest.kt
new file mode 100644
index 0000000..61b6b47
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/format/GoogleStyleFormatterKtTest.kt
@@ -0,0 +1,1024 @@
+/*
+ * 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.facebook.ktfmt.testutil.assertFormatted
+import com.facebook.ktfmt.testutil.assertThatFormatting
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Suppress("FunctionNaming")
+@RunWith(JUnit4::class)
+class GoogleStyleFormatterKtTest {
+
+ @Test
+ fun `kitchen sink of tests`() {
+ // Don't add more tests here
+ val code =
+ """
+ |fun
+ |f (
+ |a : Int
+ | , b: Double , c:String) { var result = 0
+ | val aVeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongVar = 43
+ | foo.bar.zed.accept(
+ |
+ | )
+ |
+ | foo(
+ |
+ | )
+ |
+ | foo.bar.zed.accept(
+ | DoSomething.bar()
+ | )
+ |
+ | bar(
+ | ImmutableList.newBuilder().add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).build())
+ |
+ |
+ | ImmutableList.newBuilder().add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).build()
+ | }
+ |""".trimMargin()
+
+ val expected =
+ """
+ |fun f(a: Int, b: Double, c: String) {
+ | var result = 0
+ | val aVeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongVar =
+ | 43
+ | foo.bar.zed.accept()
+ |
+ | foo()
+ |
+ | foo.bar.zed.accept(DoSomething.bar())
+ |
+ | bar(
+ | ImmutableList.newBuilder()
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .build()
+ | )
+ |
+ | ImmutableList.newBuilder()
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .add(1)
+ | .build()
+ |}
+ |""".trimMargin()
+
+ assertThatFormatting(code).withOptions(Formatter.GOOGLE_FORMAT).isEqualTo(expected)
+ // Don't add more tests here
+ }
+
+ @Test
+ fun `class params are placed each in their own line`() =
+ assertFormatted(
+ """
+ |-----------------------------------------
+ |class Foo(
+ | a: Int,
+ | var b: Double,
+ | val c: String
+ |) {
+ | //
+ |}
+ |
+ |class Foo(
+ | a: Int,
+ | var b: Double,
+ | val c: String
+ |)
+ |
+ |class Foo(
+ | a: Int,
+ | var b: Int,
+ | val c: Int
+ |) {
+ | //
+ |}
+ |
+ |class Bi(
+ | a: Int,
+ | var b: Int,
+ | val c: Int
+ |) {
+ | //
+ |}
+ |
+ |class C(a: Int, var b: Int, val c: Int) {
+ | //
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `function params are placed each in their own line`() =
+ assertFormatted(
+ """
+ |-----------------------------------------
+ |fun foo12(
+ | a: Int,
+ | var b: Double,
+ | val c: String
+ |) {
+ | //
+ |}
+ |
+ |fun foo12(
+ | a: Int,
+ | var b: Double,
+ | val c: String
+ |)
+ |
+ |fun foo12(
+ | a: Int,
+ | var b: Double,
+ | val c: String
+ |) = 5
+ |
+ |fun foo12(
+ | a: Int,
+ | var b: Int,
+ | val c: Int
+ |) {
+ | //
+ |}
+ |
+ |fun bi12(
+ | a: Int,
+ | var b: Int,
+ | val c: Int
+ |) {
+ | //
+ |}
+ |
+ |fun c12(a: Int, var b: Int, val c: Int) {
+ | //
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `return type doesn't fit in one line`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |interface X {
+ | fun f(
+ | arg1: Arg1Type,
+ | arg2: Arg2Type
+ | ): Map<String, Map<String, Double>>? {
+ | //
+ | }
+ |
+ | fun functionWithGenericReturnType(
+ | arg1: Arg1Type,
+ | arg2: Arg2Type
+ | ): Map<String, Map<String, Double>>? {
+ | //
+ | }
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `indent parameters after a break when there's a lambda afterwards`() =
+ assertFormatted(
+ """
+ |---------------------------
+ |class C {
+ | fun method() {
+ | Foo.FooBar(
+ | param1,
+ | param2
+ | )
+ | .apply {
+ | //
+ | foo
+ | }
+ | }
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `don't one-line lambdas following parameter breaks`() =
+ assertFormatted(
+ """
+ |------------------------------------------------------------------------
+ |class Foo : Bar() {
+ | fun doIt() {
+ | // don't break in lambda, no parameter breaks found
+ | fruit.forEach { eat(it) }
+ |
+ | // don't break in lambda, because we only detect parameter breaks
+ | // with trailing commas
+ | fruit.forEach(
+ | someVeryLongParameterNameThatWillCauseABreak,
+ | evenWithoutATrailingCommaOnTheParameterListSoLetsSeeIt
+ | ) { eat(it) }
+ |
+ | // break in the lambda
+ | fruit.forEach(
+ | fromTheVine = true,
+ | ) {
+ | eat(it)
+ | }
+ |
+ | // don't break in the inner lambda, as nesting doesn't respect outer levels
+ | fruit.forEach(
+ | fromTheVine = true,
+ | ) {
+ | fruit.forEach { eat(it) }
+ | }
+ |
+ | // don't break in the lambda, as breaks don't propagate
+ | fruit
+ | .onlyBananas(
+ | fromTheVine = true,
+ | )
+ | .forEach { eat(it) }
+ |
+ | // don't break in the inner lambda, as breaks don't propagate to parameters
+ | fruit.onlyBananas(
+ | fromTheVine = true,
+ | processThem = { eat(it) },
+ | ) {
+ | eat(it)
+ | }
+ |
+ | // don't break in the inner lambda, as breaks don't propagate to the body
+ | fruit.onlyBananas(
+ | fromTheVine = true,
+ | ) {
+ | val anon = { eat(it) }
+ | }
+ | }
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `function calls with multiple arguments`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | foo(1, 2, 3)
+ |
+ | foo(
+ | 123456789012345678901234567890,
+ | 123456789012345678901234567890,
+ | 123456789012345678901234567890
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT)
+
+ @Test
+ fun `line breaks inside when expressions and conditions`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | return Text.create(c)
+ | .onTouch {
+ | when (it.motionEvent.action) {
+ | ACTION_DOWN ->
+ | Toast.makeText(it.view.context, "Down!", Toast.LENGTH_SHORT, blablabla).show()
+ | ACTION_UP -> Toast.makeText(it.view.context, "Up!", Toast.LENGTH_SHORT).show()
+ | ACTION_DOWN ->
+ | Toast.makeText(
+ | it.view.context,
+ | "Down!",
+ | Toast.LENGTH_SHORT,
+ | blablabla,
+ | blablabl,
+ | blablabl,
+ | blablabl,
+ | blabla
+ | )
+ | .show()
+ | }
+ | }
+ | .build()
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ )
+
+ @Test
+ fun `anonymous function`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | setListener(
+ | fun(number: Int) {
+ | println(number)
+ | }
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ )
+
+ @Test
+ fun `avoid newline before lambda argument if it is named`() =
+ assertFormatted(
+ """
+ |private fun f(items: List<Int>) {
+ | doSomethingCool(
+ | items,
+ | lambdaArgument = {
+ | step1()
+ | step2()
+ | }
+ | ) { it.doIt() }
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ )
+
+ @Test
+ fun `anonymous function with receiver`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | setListener(
+ | fun View.() {
+ | println(this)
+ | }
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ )
+
+ @Test
+ fun `function calls with multiple named arguments`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | foo(1, b = 2, c = 3)
+ |
+ | foo(
+ | 123456789012345678901234567890,
+ | b = 23456789012345678901234567890,
+ | c = 3456789012345678901234567890
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT)
+
+ @Test
+ fun `Arguments are blocks`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |override fun visitProperty(property: KtProperty) {
+ | builder.sync(property)
+ | builder.block(ZERO) {
+ | declareOne(
+ | kind = DeclarationKind.FIELD,
+ | modifiers = property.modifierList,
+ | valOrVarKeyword =
+ | property.valOrVarKeyword.text,
+ | typeParametersBlaBla =
+ | property.typeParameterList,
+ | receiver = property.receiverTypeReference,
+ | name = property.nameIdentifier?.text,
+ | type = property.typeReference,
+ | typeConstraintList =
+ | property.typeConstraintList,
+ | delegate = property.delegate,
+ | initializer = property.initializer
+ | )
+ | }
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `keep last expression in qualified indented`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun f() {
+ | Stuff()
+ | .doIt(
+ | Foo.doIt()
+ | .doThat()
+ | )
+ | .doIt(
+ | Foo.doIt()
+ | .doThat()
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `if expression with else`() =
+ assertFormatted(
+ """
+ |fun maybePrint(b: Boolean) {
+ | println(if (b) 1 else 2)
+ | println(
+ | if (b) {
+ | val a = 1 + 1
+ | 2 * a
+ | } else 2
+ | )
+ | return if (b) 1 else 2
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT)
+
+ @Test
+ fun `named arguments indent their value expression`() =
+ assertFormatted(
+ """
+ |fun f() =
+ | Bar(
+ | tokens =
+ | mutableListOf<Token>().apply {
+ | // Printing
+ | print()
+ | },
+ | duration = duration
+ | )
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT)
+
+ @Test
+ fun `breaking long binary operations`() =
+ assertFormatted(
+ """
+ |--------------------
+ |fun foo() {
+ | val finalWidth =
+ | value1 +
+ | value2 +
+ | (value3 +
+ | value4 +
+ | value5) +
+ | foo(v) +
+ | (1 + 2) +
+ | function(
+ | value7,
+ | value8
+ | ) +
+ | value9
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle casting with breaks`() =
+ assertFormatted(
+ """
+ |-------------------
+ |fun castIt(
+ | something: Any
+ |) {
+ | doIt(
+ | something
+ | as List<*>
+ | )
+ | doIt(
+ | something
+ | is List<*>
+ | )
+ | println(
+ | something
+ | is
+ | List<String>
+ | )
+ | doIt(
+ | something
+ | as
+ | List<String>
+ | )
+ | println(
+ | something
+ | is
+ | PairList<
+ | String, Int
+ | >
+ | )
+ | doIt(
+ | something
+ | as
+ | PairList<
+ | String, Int
+ | >
+ | )
+ | println(
+ | a is Int &&
+ | b is String
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `line breaks in function arguments`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |fun f() {
+ | computeBreaks(
+ | javaOutput.commentsHelper,
+ | maxWidth,
+ | Doc.State(+0, 0)
+ | )
+ | computeBreaks(
+ | output.commentsHelper,
+ | maxWidth,
+ | State(0)
+ | )
+ | doc.computeBreaks(
+ | javaOutput.commentsHelper,
+ | maxWidth,
+ | Doc.State(+0, 0)
+ | )
+ | doc.computeBreaks(
+ | output.commentsHelper,
+ | maxWidth,
+ | State(0)
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ // TODO: there's a bug here - the last case shouldn't break after 'foo'.
+ @Test
+ fun `different indentation in chained calls`() =
+ assertFormatted(
+ """
+ |----------------------
+ |fun f() {
+ | fooDdoIt(
+ | foo1,
+ | foo2,
+ | foo3
+ | )
+ | foo.doIt(
+ | foo1,
+ | foo2,
+ | foo3
+ | )
+ | foo
+ | .doIt(
+ | foo1,
+ | foo2,
+ | foo3
+ | )
+ | .doThat()
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `a secondary constructor with many arguments passed to delegate`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |data class Foo {
+ | constructor(
+ | val number: Int,
+ | val name: String,
+ | val age: Int,
+ | val title: String,
+ | val offspring: List<Foo>
+ | ) : this(
+ | number,
+ | name,
+ | age,
+ | title,
+ | offspring,
+ | offspring
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `a secondary constructor with no arguments passed to delegate`() =
+ assertFormatted(
+ """
+ |--------------------------------------------------
+ |data class Foo {
+ | constructor() :
+ | this(
+ | Foo.createSpeciallyDesignedParameter(),
+ | Foo.createSpeciallyDesignedParameter(),
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle trailing commas (function calls)`() =
+ assertFormatted(
+ """
+ |------------------------
+ |fun main() {
+ | foo(
+ | 3,
+ | )
+ |
+ | foo<Int>(
+ | 3,
+ | )
+ |
+ | foo<
+ | Int,
+ | >(
+ | 3,
+ | )
+ |
+ | foo<Int>(
+ | "asdf",
+ | "asdf"
+ | )
+ |
+ | foo<
+ | Int,
+ | >(
+ | "asd",
+ | "asd",
+ | )
+ |
+ | foo<
+ | Int,
+ | Boolean,
+ | >(
+ | 3,
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `an assortment of tests for emitQualifiedExpression`() =
+ assertFormatted(
+ """
+ |--------------------------------------
+ |fun f() {
+ | // Regression test: https://github.com/facebookincubator/ktfmt/issues/56
+ | kjsdfglkjdfgkjdfkgjhkerjghkdfj
+ | ?.methodName1()
+ |
+ | // a series of field accesses followed by a single call expression
+ | // is kept together.
+ | abcdefghijkl.abcdefghijkl
+ | ?.methodName2()
+ |
+ | // Similar to above.
+ | abcdefghijkl.abcdefghijkl
+ | ?.methodName3
+ | ?.abcdefghijkl()
+ |
+ | // Multiple call expressions cause each part of the expression
+ | // to be placed on its own line.
+ | abcdefghijkl
+ | ?.abcdefghijkl
+ | ?.methodName4()
+ | ?.abcdefghijkl()
+ |
+ | // Don't break first call expression if it fits.
+ | foIt(something.something.happens())
+ | .thenReturn(result)
+ |
+ | // Break after `longerThanFour(` because it's longer than 4 chars
+ | longerThanFour(
+ | something.somethingBlaBla
+ | .happens()
+ | )
+ | .thenReturn(result)
+ |
+ | // Similarly to above, when part of qualified expression.
+ | foo
+ | .longerThanFour(
+ | something.somethingBlaBla
+ | .happens()
+ | )
+ | .thenReturn(result)
+ |
+ | // Keep 'super' attached to the method name
+ | super.abcdefghijkl
+ | .methodName4()
+ | .abcdefghijkl()
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `keep parenthesis and braces together when there's only one lambda argument`() =
+ assertFormatted(
+ """
+ |fun f() {
+ | doIt({})
+ | doIt({ it + it })
+ | doIt({
+ | val a = it
+ | a + a
+ | })
+ | doIt(functor = { it + it })
+ | doIt(
+ | functor = {
+ | val a = it
+ | a + a
+ | }
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT)
+
+ @Test
+ fun `chained calls that don't fit in one line`() =
+ assertFormatted(
+ """
+ |---------------------------
+ |fun f() {
+ | foo(
+ | println("a"),
+ | println("b")
+ | )
+ | .bar(
+ | println("b"),
+ | println("b")
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `lambda assigned to variable does not break before brace`() =
+ assertFormatted(
+ """
+ |fun doIt() {
+ | val lambda = {
+ | doItOnce()
+ | doItTwice()
+ | }
+ |}
+ |
+ |fun foo() = {
+ | doItOnce()
+ | doItTwice()
+ |}
+ |""".trimMargin())
+
+ @Test
+ fun `if expression with multiline condition`() =
+ assertFormatted(
+ """
+ |----------------------------
+ |fun foo() {
+ | if (
+ | expressions1 &&
+ | expression2 &&
+ | expression3
+ | ) {
+ | bar()
+ | }
+ |
+ | if (
+ | foo(
+ | expressions1 &&
+ | expression2 &&
+ | expression3
+ | )
+ | ) {
+ | bar()
+ | }
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `if expression with condition that exactly fits to line`() =
+ assertFormatted(
+ """
+ |-------------------------
+ |fun foo() {
+ | if (
+ | e1 && e2 && e3 = e4
+ | ) {
+ | bar()
+ | }
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `when() expression with multiline condition`() =
+ assertFormatted(
+ """
+ |-----------------------
+ |fun foo() {
+ | when (
+ | expressions1 +
+ | expression2 +
+ | expression3
+ | ) {
+ | 1 -> print(1)
+ | 2 -> print(2)
+ | }
+ |
+ | when (
+ | foo(
+ | expressions1 &&
+ | expression2 &&
+ | expression3
+ | )
+ | ) {
+ | 1 -> print(1)
+ | 2 -> print(2)
+ | }
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `when expression with condition that exactly fits to line`() =
+ assertFormatted(
+ """
+ |---------------------------
+ |fun foo() {
+ | when (
+ | e1 && e2 && e3 = e4
+ | ) {
+ | 1 -> print(1)
+ | 2 -> print(2)
+ | }
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `while expression with multiline condition`() =
+ assertFormatted(
+ """
+ |----------------------------
+ |fun foo() {
+ | while (
+ | expressions1 &&
+ | expression2 &&
+ | expression3
+ | ) {
+ | bar()
+ | }
+ |
+ | while (
+ | foo(
+ | expressions1 &&
+ | expression2 &&
+ | expression3
+ | )
+ | ) {
+ | bar()
+ | }
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `while expression with condition that exactly fits to line`() =
+ assertFormatted(
+ """
+ |----------------------------
+ |fun foo() {
+ | while (
+ | e1 && e2 && e3 = e4
+ | ) {
+ | bar()
+ | }
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `handle destructuring declaration`() =
+ assertFormatted(
+ """
+ |-------------------------------------------
+ |fun f() {
+ | val (a, b: Int) = listOf(1, 2)
+ | val (asd, asd, asd, asd, asd, asd, asd) =
+ | foo.bar(asdasd, asdasd)
+ |
+ | val (accountType, accountId) =
+ | oneTwoThreeFourFiveSixSeven(
+ | foo,
+ | bar,
+ | zed,
+ | boo
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `trailing break argument list`() =
+ assertFormatted(
+ """
+ |-------------------
+ |fun method() {
+ | Foo.FooBar(
+ | longParameter
+ | )
+ | Foo.FooBar(
+ | param1,
+ | param2
+ | )
+ |}
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `trailing break chains`() =
+ assertFormatted(
+ """
+ |-------------
+ |bar(
+ | FooOpClass
+ | .doOp(1)
+ | .doOp(2)
+ |)
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+
+ @Test
+ fun `wrapping for long function types`() =
+ assertFormatted(
+ """
+ |------------------------
+ |var listener:
+ | (
+ | a: String,
+ | b: String,
+ | c: String,
+ | d: String
+ | ) -> Unit
+ |""".trimMargin(),
+ formattingOptions = Formatter.GOOGLE_FORMAT,
+ deduceMaxWidth = true)
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/format/KotlinInputTest.kt b/core/src/test/java/com/facebook/ktfmt/format/KotlinInputTest.kt
new file mode 100644
index 0000000..d965f05
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/format/KotlinInputTest.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.truth.Truth.assertThat
+import org.junit.Test
+
+class KotlinInputTest {
+ @Test
+ fun `Comments are toks not tokens`() {
+ val code = "/** foo */ class F {}"
+ val input = KotlinInput(code, Parser.parse(code))
+ assertThat(input.getTokens().map { it.tok.text })
+ .containsExactly("class", "F", "{", "}", "")
+ .inOrder()
+ assertThat(input.getTokens()[0].toksBefore.map { it.text })
+ .containsExactly("/** foo */", " ")
+ .inOrder()
+ }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/format/TokenizerTest.kt b/core/src/test/java/com/facebook/ktfmt/format/TokenizerTest.kt
new file mode 100644
index 0000000..3f8fe71
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/format/TokenizerTest.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class TokenizerTest {
+ @Test
+ fun `PsiWhiteSpace are split to newlines and maximal-length whitespaces`() {
+ val code =
+ listOf(
+ "val a = ", //
+ "", //
+ " ", //
+ " 15")
+ .joinToString("\n")
+
+ val file = Parser.parse(code)
+ val tokenizer = Tokenizer(code, file)
+ file.accept(tokenizer)
+
+ assertThat(tokenizer.toks.map { it.originalText })
+ .containsExactly("val", " ", "a", " ", "=", " ", "\n", "\n", " ", "\n", " ", "15")
+ .inOrder()
+ }
+
+ @Test
+ fun `Strings are returns as a single token`() {
+ val code =
+ listOf(
+ "val a=\"\"\"",
+ " ",
+ " ",
+ " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do ",
+ " Lorem",
+ " ",
+ " ",
+ " \"\"\"",
+ "val b=\"lorem ipsum\"",
+ " ",
+ " ")
+ .joinToString("\n")
+
+ val file = Parser.parse(code)
+ val tokenizer = Tokenizer(code, file)
+ file.accept(tokenizer)
+
+ assertThat(tokenizer.toks.map { it.originalText })
+ .containsExactly(
+ "val",
+ " ",
+ "a",
+ "=",
+ listOf(
+ "\"\"\"",
+ " ${WhitespaceTombstones.SPACE_TOMBSTONE}",
+ " ${WhitespaceTombstones.SPACE_TOMBSTONE}",
+ " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do${WhitespaceTombstones.SPACE_TOMBSTONE}",
+ " Lorem",
+ " ${WhitespaceTombstones.SPACE_TOMBSTONE}",
+ " ${WhitespaceTombstones.SPACE_TOMBSTONE}",
+ " \"\"\"")
+ .joinToString("\n"),
+ "\n",
+ "val",
+ " ",
+ "b",
+ "=",
+ "\"lorem ipsum\"",
+ "\n",
+ " ",
+ "\n",
+ " ")
+ .inOrder()
+ }
+
+ @Test
+ fun `Token index is advanced after a string token`() {
+ val code = """
+ |val b="a"
+ |val a=5
+ |""".trimMargin().trimMargin()
+
+ val file = Parser.parse(code)
+ val tokenizer = Tokenizer(code, file)
+ file.accept(tokenizer)
+
+ assertThat(tokenizer.toks.map { it.originalText })
+ .containsExactly("val", " ", "b", "=", "\"a\"", "\n", "val", " ", "a", "=", "5")
+ .inOrder()
+ assertThat(tokenizer.toks.map { it.index })
+ .containsExactly(0, -1, 1, 2, 3, -1, 4, -1, 5, 6, 7)
+ .inOrder()
+ }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/format/WhitespaceTombstonesTest.kt b/core/src/test/java/com/facebook/ktfmt/format/WhitespaceTombstonesTest.kt
new file mode 100644
index 0000000..79ce6ce
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/format/WhitespaceTombstonesTest.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class WhitespaceTombstonesTest {
+
+ @Test
+ fun testReplaceTrailingWhitespaceWithTombstone() {
+ assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone("")).isEqualTo("")
+
+ assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone(" sdfl"))
+ .isEqualTo(" sdfl")
+ assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone(" sdfl "))
+ .isEqualTo(" sdfl${WhitespaceTombstones.SPACE_TOMBSTONE}")
+ assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone(" sdfl "))
+ .isEqualTo(" sdfl ${WhitespaceTombstones.SPACE_TOMBSTONE}")
+
+ assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone(" sdfl \n skdjfh"))
+ .isEqualTo(" sdfl ${WhitespaceTombstones.SPACE_TOMBSTONE}\n skdjfh")
+ assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone(" sdfl \n skdjfh "))
+ .isEqualTo(
+ " sdfl ${WhitespaceTombstones.SPACE_TOMBSTONE}\n skdjfh${WhitespaceTombstones.SPACE_TOMBSTONE}")
+ assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone(" sdfl \n\n skdjfh "))
+ .isEqualTo(
+ " sdfl ${WhitespaceTombstones.SPACE_TOMBSTONE}\n\n skdjfh${WhitespaceTombstones.SPACE_TOMBSTONE}")
+ }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/kdoc/EscapingTest.kt b/core/src/test/java/com/facebook/ktfmt/kdoc/EscapingTest.kt
new file mode 100644
index 0000000..688f612
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/kdoc/EscapingTest.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.kdoc
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class EscapingTest {
+
+ @Test
+ fun `escaping kdoc`() {
+ assertThat(Escaping.escapeKDoc("/** foo */")).isEqualTo("/** foo */")
+ assertThat(Escaping.escapeKDoc("/*** foo */")).isEqualTo("/*** foo */")
+ assertThat(Escaping.escapeKDoc("/** * foo */")).isEqualTo("/** * foo */")
+ assertThat(Escaping.escapeKDoc("/** /* foo */")).isEqualTo("/** \u0004\u0005 foo */")
+ assertThat(Escaping.escapeKDoc("/** /* foo */ */"))
+ .isEqualTo("/** \u0004\u0005 foo \u0005\u0004 */")
+
+ assertThat(Escaping.escapeKDoc("/* /* foo */ */"))
+ .isEqualTo("/* \u0004\u0005 foo \u0005\u0004 */")
+ }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/kdoc/KDocFormatterTest.kt b/core/src/test/java/com/facebook/ktfmt/kdoc/KDocFormatterTest.kt
new file mode 100644
index 0000000..4efdf42
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/kdoc/KDocFormatterTest.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.kdoc
+
+import com.facebook.ktfmt.kdoc.KDocFormatter.tokenizeKdocText
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class KDocFormatterTest {
+ @Test
+ fun testTokenizeKdocText() {
+ assertThat(tokenizeKdocText(" one two three ").asIterable())
+ .containsExactly(" ", "one", " ", "two", " ", "three", " ")
+ .inOrder()
+ assertThat(tokenizeKdocText("one two three ").asIterable())
+ .containsExactly("one", " ", "two", " ", "three", " ")
+ .inOrder()
+ assertThat(tokenizeKdocText("one two three").asIterable())
+ .containsExactly("one", " ", "two", " ", "three")
+ .inOrder()
+ assertThat(tokenizeKdocText("onetwothree").asIterable())
+ .containsExactly("onetwothree")
+ .inOrder()
+ assertThat(tokenizeKdocText("").asIterable()).isEmpty()
+ }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/testutil/KtfmtTruth.kt b/core/src/test/java/com/facebook/ktfmt/testutil/KtfmtTruth.kt
new file mode 100644
index 0000000..3a2a620
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/testutil/KtfmtTruth.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.testutil
+
+import com.facebook.ktfmt.debughelpers.PrintAstVisitor
+import com.facebook.ktfmt.format.Formatter
+import com.facebook.ktfmt.format.FormattingOptions
+import com.facebook.ktfmt.format.Parser
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Truth
+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:
+ * ```
+ * --------------------
+ * // 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
+ */
+fun assertFormatted(
+ code: String,
+ formattingOptions: FormattingOptions = FormattingOptions(),
+ deduceMaxWidth: Boolean = false
+) {
+ val first = code.lines().first()
+ var deducedCode = code
+ var maxWidth = FormattingOptions.DEFAULT_MAX_WIDTH
+ val isFirstLineAMaxWidthMarker = first.all { it == '-' }
+ if (deduceMaxWidth) {
+ if (!isFirstLineAMaxWidthMarker) {
+ throw RuntimeException(
+ "deduceMaxWidth is false, please remove the first dashes only line from the code (i.e. ---)")
+ }
+ deducedCode = code.substring(code.indexOf('\n') + 1)
+ maxWidth = first.length
+ } else {
+ if (isFirstLineAMaxWidthMarker) {
+ throw RuntimeException(
+ "When deduceMaxWidth is true the first line need to be all dashes only (i.e. ---)")
+ }
+ }
+ assertThatFormatting(deducedCode)
+ .withOptions(formattingOptions.copy(maxWidth = maxWidth))
+ .isEqualTo(deducedCode)
+}
+
+fun assertThatFormatting(code: String): FormattedCodeSubject {
+ fun codes(): Subject.Factory<FormattedCodeSubject, String> {
+ return Subject.Factory { metadata, subject -> FormattedCodeSubject(metadata, subject) }
+ }
+ return Truth.assertAbout(codes()).that(code)
+}
+
+class FormattedCodeSubject(metadata: FailureMetadata, private val code: String) :
+ Subject(metadata, code) {
+ private var options: FormattingOptions = FormattingOptions()
+ private var allowTrailingWhitespace = false
+
+ fun withOptions(options: FormattingOptions): FormattedCodeSubject {
+ this.options = options
+ return this
+ }
+
+ fun allowTrailingWhitespace(): FormattedCodeSubject {
+ this.allowTrailingWhitespace = true
+ return this
+ }
+
+ fun isEqualTo(expectedFormatting: String) {
+ if (!allowTrailingWhitespace && expectedFormatting.lines().any { it.endsWith(" ") }) {
+ throw RuntimeException(
+ "Expected code contains trailing whitespace, which the formatter usually doesn't output:\n" +
+ expectedFormatting
+ .lines()
+ .map { if (it.endsWith(" ")) "[$it]" else it }
+ .joinToString("\n"))
+ }
+ val actualFormatting: String
+ try {
+ actualFormatting = Formatter.format(options, code)
+ if (actualFormatting != expectedFormatting) {
+ reportError(code)
+ println("# Output: ")
+ println("#".repeat(20))
+ println(actualFormatting)
+ println("# Expected: ")
+ println("#".repeat(20))
+ println(expectedFormatting)
+ println("#".repeat(20))
+ println(
+ "Need more information about the break operations?" +
+ "Run test with assertion with \"FormattingOptions(debuggingPrintOpsAfterFormatting = true)\"")
+ }
+ } catch (e: Error) {
+ reportError(code)
+ throw e
+ }
+ Assert.assertEquals(expectedFormatting, actualFormatting)
+ }
+
+ private fun reportError(code: String) {
+ val file = Parser.parse(code)
+ println("# Parse tree of input: ")
+ println("#".repeat(20))
+ file.accept(PrintAstVisitor())
+ println()
+ println("# Input: ")
+ println("#".repeat(20))
+ println(code)
+ println()
+ }
+}
diff --git a/docs/editorconfig/.editorconfig-default b/docs/editorconfig/.editorconfig-default
new file mode 100644
index 0000000..22d8db2
--- /dev/null
+++ b/docs/editorconfig/.editorconfig-default
@@ -0,0 +1,93 @@
+# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
+# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
+# and making sure your editor is set to read settings from .editorconfig files.
+#
+# It includes editor-specific config options for IntelliJ IDEA.
+#
+# If any option is wrong, PR are welcome
+
+[{*.kt,*.kts}]
+indent_style = space
+insert_final_newline = true
+max_line_length = 100
+indent_size = 2
+ij_continuation_indent_size = 4
+ij_java_names_count_to_use_import_on_demand = 9999
+ij_kotlin_align_in_columns_case_branch = false
+ij_kotlin_align_multiline_binary_operation = false
+ij_kotlin_align_multiline_extends_list = false
+ij_kotlin_align_multiline_method_parentheses = false
+ij_kotlin_align_multiline_parameters = true
+ij_kotlin_align_multiline_parameters_in_calls = false
+ij_kotlin_allow_trailing_comma = true
+ij_kotlin_allow_trailing_comma_on_call_site = true
+ij_kotlin_assignment_wrap = normal
+ij_kotlin_blank_lines_after_class_header = 0
+ij_kotlin_blank_lines_around_block_when_branches = 0
+ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
+ij_kotlin_block_comment_at_first_column = true
+ij_kotlin_call_parameters_new_line_after_left_paren = true
+ij_kotlin_call_parameters_right_paren_on_new_line = false
+ij_kotlin_call_parameters_wrap = on_every_item
+ij_kotlin_catch_on_new_line = false
+ij_kotlin_class_annotation_wrap = split_into_lines
+ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
+ij_kotlin_continuation_indent_for_chained_calls = true
+ij_kotlin_continuation_indent_for_expression_bodies = true
+ij_kotlin_continuation_indent_in_argument_lists = true
+ij_kotlin_continuation_indent_in_elvis = false
+ij_kotlin_continuation_indent_in_if_conditions = false
+ij_kotlin_continuation_indent_in_parameter_lists = false
+ij_kotlin_continuation_indent_in_supertype_lists = false
+ij_kotlin_else_on_new_line = false
+ij_kotlin_enum_constants_wrap = off
+ij_kotlin_extends_list_wrap = normal
+ij_kotlin_field_annotation_wrap = split_into_lines
+ij_kotlin_finally_on_new_line = false
+ij_kotlin_if_rparen_on_new_line = false
+ij_kotlin_import_nested_classes = false
+ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
+ij_kotlin_keep_blank_lines_before_right_brace = 2
+ij_kotlin_keep_blank_lines_in_code = 2
+ij_kotlin_keep_blank_lines_in_declarations = 2
+ij_kotlin_keep_first_column_comment = true
+ij_kotlin_keep_indents_on_empty_lines = false
+ij_kotlin_keep_line_breaks = true
+ij_kotlin_lbrace_on_next_line = false
+ij_kotlin_line_comment_add_space = false
+ij_kotlin_line_comment_at_first_column = true
+ij_kotlin_method_annotation_wrap = split_into_lines
+ij_kotlin_method_call_chain_wrap = normal
+ij_kotlin_method_parameters_new_line_after_left_paren = true
+ij_kotlin_method_parameters_right_paren_on_new_line = true
+ij_kotlin_method_parameters_wrap = on_every_item
+ij_kotlin_name_count_to_use_star_import = 9999
+ij_kotlin_name_count_to_use_star_import_for_members = 9999
+ij_kotlin_parameter_annotation_wrap = off
+ij_kotlin_space_after_comma = true
+ij_kotlin_space_after_extend_colon = true
+ij_kotlin_space_after_type_colon = true
+ij_kotlin_space_before_catch_parentheses = true
+ij_kotlin_space_before_comma = false
+ij_kotlin_space_before_extend_colon = true
+ij_kotlin_space_before_for_parentheses = true
+ij_kotlin_space_before_if_parentheses = true
+ij_kotlin_space_before_lambda_arrow = true
+ij_kotlin_space_before_type_colon = false
+ij_kotlin_space_before_when_parentheses = true
+ij_kotlin_space_before_while_parentheses = true
+ij_kotlin_spaces_around_additive_operators = true
+ij_kotlin_spaces_around_assignment_operators = true
+ij_kotlin_spaces_around_equality_operators = true
+ij_kotlin_spaces_around_function_type_arrow = true
+ij_kotlin_spaces_around_logical_operators = true
+ij_kotlin_spaces_around_multiplicative_operators = true
+ij_kotlin_spaces_around_range = false
+ij_kotlin_spaces_around_relational_operators = true
+ij_kotlin_spaces_around_unary_operator = false
+ij_kotlin_spaces_around_when_arrow = true
+ij_kotlin_variable_annotation_wrap = off
+ij_kotlin_while_on_new_line = false
+ij_kotlin_wrap_elvis_expressions = 1
+ij_kotlin_wrap_expression_body_functions = 1
+ij_kotlin_wrap_first_method_in_call_chain = false
diff --git a/docs/editorconfig/.editorconfig-dropbox b/docs/editorconfig/.editorconfig-dropbox
new file mode 100644
index 0000000..bc214f6
--- /dev/null
+++ b/docs/editorconfig/.editorconfig-dropbox
@@ -0,0 +1,94 @@
+# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
+# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
+# and making sure your editor is set to read settings from .editorconfig files.
+#
+# It includes editor-specific config options for IntelliJ IDEA.
+#
+# If any option is wrong, PR are welcome
+
+[{*.kt,*.kts}]
+indent_style = space
+insert_final_newline = true
+max_line_length = 100
+indent_size = 4
+ij_continuation_indent_size = 8
+ij_java_names_count_to_use_import_on_demand = 9999
+ij_kotlin_align_in_columns_case_branch = false
+ij_kotlin_align_multiline_binary_operation = false
+ij_kotlin_align_multiline_extends_list = false
+ij_kotlin_align_multiline_method_parentheses = false
+ij_kotlin_align_multiline_parameters = true
+ij_kotlin_align_multiline_parameters_in_calls = false
+ij_kotlin_allow_trailing_comma = true
+ij_kotlin_allow_trailing_comma_on_call_site = true
+ij_kotlin_assignment_wrap = normal
+ij_kotlin_blank_lines_after_class_header = 0
+ij_kotlin_blank_lines_around_block_when_branches = 0
+ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
+ij_kotlin_block_comment_at_first_column = true
+ij_kotlin_call_parameters_new_line_after_left_paren = true
+ij_kotlin_call_parameters_right_paren_on_new_line = false
+ij_kotlin_call_parameters_wrap = on_every_item
+ij_kotlin_catch_on_new_line = false
+ij_kotlin_class_annotation_wrap = split_into_lines
+ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
+ij_kotlin_continuation_indent_for_chained_calls = true
+ij_kotlin_continuation_indent_for_expression_bodies = true
+ij_kotlin_continuation_indent_in_argument_lists = true
+ij_kotlin_continuation_indent_in_elvis = false
+ij_kotlin_continuation_indent_in_if_conditions = false
+ij_kotlin_continuation_indent_in_parameter_lists = false
+ij_kotlin_continuation_indent_in_supertype_lists = false
+ij_kotlin_else_on_new_line = false
+ij_kotlin_enum_constants_wrap = off
+ij_kotlin_extends_list_wrap = normal
+ij_kotlin_field_annotation_wrap = split_into_lines
+ij_kotlin_finally_on_new_line = false
+ij_kotlin_if_rparen_on_new_line = false
+ij_kotlin_import_nested_classes = false
+ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
+ij_kotlin_keep_blank_lines_before_right_brace = 2
+ij_kotlin_keep_blank_lines_in_code = 2
+ij_kotlin_keep_blank_lines_in_declarations = 2
+ij_kotlin_keep_first_column_comment = true
+ij_kotlin_keep_indents_on_empty_lines = false
+ij_kotlin_keep_line_breaks = true
+ij_kotlin_lbrace_on_next_line = false
+ij_kotlin_line_comment_add_space = false
+ij_kotlin_line_comment_at_first_column = true
+ij_kotlin_method_annotation_wrap = split_into_lines
+ij_kotlin_method_call_chain_wrap = normal
+ij_kotlin_method_parameters_new_line_after_left_paren = true
+ij_kotlin_method_parameters_right_paren_on_new_line = true
+ij_kotlin_method_parameters_wrap = on_every_item
+ij_kotlin_name_count_to_use_star_import = 9999
+ij_kotlin_name_count_to_use_star_import_for_members = 9999
+ij_java_names_count_to_use_import_on_demand = 9999
+ij_kotlin_parameter_annotation_wrap = off
+ij_kotlin_space_after_comma = true
+ij_kotlin_space_after_extend_colon = true
+ij_kotlin_space_after_type_colon = true
+ij_kotlin_space_before_catch_parentheses = true
+ij_kotlin_space_before_comma = false
+ij_kotlin_space_before_extend_colon = true
+ij_kotlin_space_before_for_parentheses = true
+ij_kotlin_space_before_if_parentheses = true
+ij_kotlin_space_before_lambda_arrow = true
+ij_kotlin_space_before_type_colon = false
+ij_kotlin_space_before_when_parentheses = true
+ij_kotlin_space_before_while_parentheses = true
+ij_kotlin_spaces_around_additive_operators = true
+ij_kotlin_spaces_around_assignment_operators = true
+ij_kotlin_spaces_around_equality_operators = true
+ij_kotlin_spaces_around_function_type_arrow = true
+ij_kotlin_spaces_around_logical_operators = true
+ij_kotlin_spaces_around_multiplicative_operators = true
+ij_kotlin_spaces_around_range = false
+ij_kotlin_spaces_around_relational_operators = true
+ij_kotlin_spaces_around_unary_operator = false
+ij_kotlin_spaces_around_when_arrow = true
+ij_kotlin_variable_annotation_wrap = off
+ij_kotlin_while_on_new_line = false
+ij_kotlin_wrap_elvis_expressions = 1
+ij_kotlin_wrap_expression_body_functions = 1
+ij_kotlin_wrap_first_method_in_call_chain = false
diff --git a/docs/editorconfig/.editorconfig-google b/docs/editorconfig/.editorconfig-google
new file mode 100644
index 0000000..9e62d0d
--- /dev/null
+++ b/docs/editorconfig/.editorconfig-google
@@ -0,0 +1,93 @@
+# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
+# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
+# and making sure your editor is set to read settings from .editorconfig files.
+#
+# It includes editor-specific config options for IntelliJ IDEA.
+#
+# If any option is wrong, PR are welcome
+
+[{*.kt,*.kts}]
+indent_style = space
+insert_final_newline = true
+max_line_length = 100
+indent_size = 2
+ij_continuation_indent_size = 2
+ij_java_names_count_to_use_import_on_demand = 9999
+ij_kotlin_align_in_columns_case_branch = false
+ij_kotlin_align_multiline_binary_operation = false
+ij_kotlin_align_multiline_extends_list = false
+ij_kotlin_align_multiline_method_parentheses = false
+ij_kotlin_align_multiline_parameters = true
+ij_kotlin_align_multiline_parameters_in_calls = false
+ij_kotlin_allow_trailing_comma = true
+ij_kotlin_allow_trailing_comma_on_call_site = true
+ij_kotlin_assignment_wrap = normal
+ij_kotlin_blank_lines_after_class_header = 0
+ij_kotlin_blank_lines_around_block_when_branches = 0
+ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
+ij_kotlin_block_comment_at_first_column = true
+ij_kotlin_call_parameters_new_line_after_left_paren = true
+ij_kotlin_call_parameters_right_paren_on_new_line = false
+ij_kotlin_call_parameters_wrap = on_every_item
+ij_kotlin_catch_on_new_line = false
+ij_kotlin_class_annotation_wrap = split_into_lines
+ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
+ij_kotlin_continuation_indent_for_chained_calls = true
+ij_kotlin_continuation_indent_for_expression_bodies = true
+ij_kotlin_continuation_indent_in_argument_lists = true
+ij_kotlin_continuation_indent_in_elvis = false
+ij_kotlin_continuation_indent_in_if_conditions = false
+ij_kotlin_continuation_indent_in_parameter_lists = false
+ij_kotlin_continuation_indent_in_supertype_lists = false
+ij_kotlin_else_on_new_line = false
+ij_kotlin_enum_constants_wrap = off
+ij_kotlin_extends_list_wrap = normal
+ij_kotlin_field_annotation_wrap = split_into_lines
+ij_kotlin_finally_on_new_line = false
+ij_kotlin_if_rparen_on_new_line = false
+ij_kotlin_import_nested_classes = false
+ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
+ij_kotlin_keep_blank_lines_before_right_brace = 2
+ij_kotlin_keep_blank_lines_in_code = 2
+ij_kotlin_keep_blank_lines_in_declarations = 2
+ij_kotlin_keep_first_column_comment = true
+ij_kotlin_keep_indents_on_empty_lines = false
+ij_kotlin_keep_line_breaks = true
+ij_kotlin_lbrace_on_next_line = false
+ij_kotlin_line_comment_add_space = false
+ij_kotlin_line_comment_at_first_column = true
+ij_kotlin_method_annotation_wrap = split_into_lines
+ij_kotlin_method_call_chain_wrap = normal
+ij_kotlin_method_parameters_new_line_after_left_paren = true
+ij_kotlin_method_parameters_right_paren_on_new_line = true
+ij_kotlin_method_parameters_wrap = on_every_item
+ij_kotlin_name_count_to_use_star_import = 9999
+ij_kotlin_name_count_to_use_star_import_for_members = 9999
+ij_kotlin_parameter_annotation_wrap = off
+ij_kotlin_space_after_comma = true
+ij_kotlin_space_after_extend_colon = true
+ij_kotlin_space_after_type_colon = true
+ij_kotlin_space_before_catch_parentheses = true
+ij_kotlin_space_before_comma = false
+ij_kotlin_space_before_extend_colon = true
+ij_kotlin_space_before_for_parentheses = true
+ij_kotlin_space_before_if_parentheses = true
+ij_kotlin_space_before_lambda_arrow = true
+ij_kotlin_space_before_type_colon = false
+ij_kotlin_space_before_when_parentheses = true
+ij_kotlin_space_before_while_parentheses = true
+ij_kotlin_spaces_around_additive_operators = true
+ij_kotlin_spaces_around_assignment_operators = true
+ij_kotlin_spaces_around_equality_operators = true
+ij_kotlin_spaces_around_function_type_arrow = true
+ij_kotlin_spaces_around_logical_operators = true
+ij_kotlin_spaces_around_multiplicative_operators = true
+ij_kotlin_spaces_around_range = false
+ij_kotlin_spaces_around_relational_operators = true
+ij_kotlin_spaces_around_unary_operator = false
+ij_kotlin_spaces_around_when_arrow = true
+ij_kotlin_variable_annotation_wrap = off
+ij_kotlin_while_on_new_line = false
+ij_kotlin_wrap_elvis_expressions = 1
+ij_kotlin_wrap_expression_body_functions = 1
+ij_kotlin_wrap_first_method_in_call_chain = false
diff --git a/docs/editorconfig/.editorconfig-kotlinlang b/docs/editorconfig/.editorconfig-kotlinlang
new file mode 100644
index 0000000..2f55867
--- /dev/null
+++ b/docs/editorconfig/.editorconfig-kotlinlang
@@ -0,0 +1,93 @@
+# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
+# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
+# and making sure your editor is set to read settings from .editorconfig files.
+#
+# It includes editor-specific config options for IntelliJ IDEA.
+#
+# If any option is wrong, PR are welcome
+
+[{*.kt,*.kts}]
+indent_style = space
+insert_final_newline = true
+max_line_length = 100
+indent_size = 4
+ij_continuation_indent_size = 4
+ij_java_names_count_to_use_import_on_demand = 9999
+ij_kotlin_align_in_columns_case_branch = false
+ij_kotlin_align_multiline_binary_operation = false
+ij_kotlin_align_multiline_extends_list = false
+ij_kotlin_align_multiline_method_parentheses = false
+ij_kotlin_align_multiline_parameters = true
+ij_kotlin_align_multiline_parameters_in_calls = false
+ij_kotlin_allow_trailing_comma = true
+ij_kotlin_allow_trailing_comma_on_call_site = true
+ij_kotlin_assignment_wrap = normal
+ij_kotlin_blank_lines_after_class_header = 0
+ij_kotlin_blank_lines_around_block_when_branches = 0
+ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
+ij_kotlin_block_comment_at_first_column = true
+ij_kotlin_call_parameters_new_line_after_left_paren = true
+ij_kotlin_call_parameters_right_paren_on_new_line = false
+ij_kotlin_call_parameters_wrap = on_every_item
+ij_kotlin_catch_on_new_line = false
+ij_kotlin_class_annotation_wrap = split_into_lines
+ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
+ij_kotlin_continuation_indent_for_chained_calls = true
+ij_kotlin_continuation_indent_for_expression_bodies = true
+ij_kotlin_continuation_indent_in_argument_lists = true
+ij_kotlin_continuation_indent_in_elvis = false
+ij_kotlin_continuation_indent_in_if_conditions = false
+ij_kotlin_continuation_indent_in_parameter_lists = false
+ij_kotlin_continuation_indent_in_supertype_lists = false
+ij_kotlin_else_on_new_line = false
+ij_kotlin_enum_constants_wrap = off
+ij_kotlin_extends_list_wrap = normal
+ij_kotlin_field_annotation_wrap = split_into_lines
+ij_kotlin_finally_on_new_line = false
+ij_kotlin_if_rparen_on_new_line = false
+ij_kotlin_import_nested_classes = false
+ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
+ij_kotlin_keep_blank_lines_before_right_brace = 2
+ij_kotlin_keep_blank_lines_in_code = 2
+ij_kotlin_keep_blank_lines_in_declarations = 2
+ij_kotlin_keep_first_column_comment = true
+ij_kotlin_keep_indents_on_empty_lines = false
+ij_kotlin_keep_line_breaks = true
+ij_kotlin_lbrace_on_next_line = false
+ij_kotlin_line_comment_add_space = false
+ij_kotlin_line_comment_at_first_column = true
+ij_kotlin_method_annotation_wrap = split_into_lines
+ij_kotlin_method_call_chain_wrap = normal
+ij_kotlin_method_parameters_new_line_after_left_paren = true
+ij_kotlin_method_parameters_right_paren_on_new_line = true
+ij_kotlin_method_parameters_wrap = on_every_item
+ij_kotlin_name_count_to_use_star_import = 9999
+ij_kotlin_name_count_to_use_star_import_for_members = 9999
+ij_kotlin_parameter_annotation_wrap = off
+ij_kotlin_space_after_comma = true
+ij_kotlin_space_after_extend_colon = true
+ij_kotlin_space_after_type_colon = true
+ij_kotlin_space_before_catch_parentheses = true
+ij_kotlin_space_before_comma = false
+ij_kotlin_space_before_extend_colon = true
+ij_kotlin_space_before_for_parentheses = true
+ij_kotlin_space_before_if_parentheses = true
+ij_kotlin_space_before_lambda_arrow = true
+ij_kotlin_space_before_type_colon = false
+ij_kotlin_space_before_when_parentheses = true
+ij_kotlin_space_before_while_parentheses = true
+ij_kotlin_spaces_around_additive_operators = true
+ij_kotlin_spaces_around_assignment_operators = true
+ij_kotlin_spaces_around_equality_operators = true
+ij_kotlin_spaces_around_function_type_arrow = true
+ij_kotlin_spaces_around_logical_operators = true
+ij_kotlin_spaces_around_multiplicative_operators = true
+ij_kotlin_spaces_around_range = false
+ij_kotlin_spaces_around_relational_operators = true
+ij_kotlin_spaces_around_unary_operator = false
+ij_kotlin_spaces_around_when_arrow = true
+ij_kotlin_variable_annotation_wrap = off
+ij_kotlin_while_on_new_line = false
+ij_kotlin_wrap_elvis_expressions = 1
+ij_kotlin_wrap_expression_body_functions = 1
+ij_kotlin_wrap_first_method_in_call_chain = false
diff --git a/docs/images/before.png b/docs/images/before.png
new file mode 100644
index 0000000..c545a44
--- /dev/null
+++ b/docs/images/before.png
Binary files differ
diff --git a/docs/images/intellij.png b/docs/images/intellij.png
new file mode 100644
index 0000000..c2ac597
--- /dev/null
+++ b/docs/images/intellij.png
Binary files differ
diff --git a/docs/images/ktfmt.png b/docs/images/ktfmt.png
new file mode 100644
index 0000000..79ed3f7
--- /dev/null
+++ b/docs/images/ktfmt.png
Binary files differ
diff --git a/docs/images/ktlint.png b/docs/images/ktlint.png
new file mode 100644
index 0000000..9c70b83
--- /dev/null
+++ b/docs/images/ktlint.png
Binary files differ
diff --git a/generate_includes_file.py b/generate_includes_file.py
new file mode 100755
index 0000000..be9ebff
--- /dev/null
+++ b/generate_includes_file.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+
+#
+# Copyright 2022, The Android Open Source Project
+#
+# 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.
+#
+"""Script to generate an include file that can be passed to ktfmt.py.
+
+The include file is generated from one or more folders containing one or more
+Kotlin files. The generated include file will exclude all Kotlin files that
+currently don't follow ktfmt format. This way, we can easily start formatting
+all new files in a project/directory.
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ 'Generate an include file that can be passed to ktfmt.py')
+ parser.add_argument(
+ '--output', '-o', required=True, help='The output include file.')
+ parser.add_argument(
+ 'files',
+ nargs='*',
+ help='The files or directories that should be included.')
+ args = parser.parse_args()
+
+ # Add a line preprended with '+' for included files/folders.
+ with open(args.output, 'w+') as out:
+ includes = args.files
+ includes.sort()
+ for include in includes:
+ out.write('+' + include + '\n')
+
+ # Retrieve all Kotlin files.
+ kt_files = []
+ for file in args.files:
+ if os.path.isdir(file):
+ for root, dirs, files in os.walk(file):
+ for f in files:
+ if is_kotlin_file(f):
+ kt_files += [os.path.join(root, f)]
+
+ if is_kotlin_file(file):
+ kt_files += [file]
+
+ # Check all files with ktfmt.
+ ktfmt_args = ['--kotlinlang-style', '--set-exit-if-changed', '--dry-run'
+ ] + kt_files
+ dir = os.path.dirname(__file__)
+ ktfmt_jar = os.path.normpath(
+ os.path.join(dir,
+ '../../prebuilts/build-tools/common/framework/ktfmt.jar'))
+
+ ktlint_env = os.environ.copy()
+ ktlint_env['JAVA_CMD'] = 'java'
+ try:
+ process = subprocess.Popen(
+ ['java', '-jar', ktfmt_jar] + ktfmt_args,
+ stdout=subprocess.PIPE,
+ env=ktlint_env)
+
+ # Add a line prepended with '-' for all files that are not correctly.
+ # formatted.
+ stdout, _ = process.communicate()
+ incorrect_files = stdout.decode('utf-8').splitlines()
+ incorrect_files.sort()
+ for file in incorrect_files:
+ out.write('-' + file + '\n')
+ except OSError as e:
+ print('Error running ktfmt')
+ sys.exit(1)
+
+
+def is_kotlin_file(name):
+ return name.endswith('.kt') or name.endswith('.kts')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ktfmt.py b/ktfmt.py
new file mode 100755
index 0000000..6f115f7
--- /dev/null
+++ b/ktfmt.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+
+#
+# Copyright 2022, The Android Open Source Project
+#
+# 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.
+#
+"""Script to format or check Kotlin files."""
+
+import argparse
+import os
+import subprocess
+import sys
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ 'Format Kotlin files or check that they are correctly formatted.')
+ parser.add_argument(
+ '--check',
+ '-c',
+ action='store_true',
+ default=False,
+ help='Perform a format check instead of formatting.')
+ parser.add_argument(
+ '--includes_file',
+ '-i',
+ default='',
+ help='The file containing the Kotlin files and directories that should be included/excluded, generated using generate_includes_file.py.'
+ )
+ parser.add_argument(
+ 'files',
+ nargs='*',
+ help='The files to format or check. If --include_file is specified, only the files at their intersection will be formatted/checked.'
+ )
+ args = parser.parse_args()
+
+ ktfmt_args = ['--kotlinlang-style']
+
+ check = args.check
+ if check:
+ ktfmt_args += ['--set-exit-if-changed', '--dry-run']
+
+ kt_files = []
+ for file in args.files:
+ if os.path.isdir(file):
+ for root, dirs, files in os.walk(file):
+ for f in files:
+ if is_kotlin_file(f):
+ kt_files += [os.path.join(root, f)]
+
+ if is_kotlin_file(file):
+ kt_files += [file]
+
+ # Only format/check files from the includes list.
+ includes_file = args.includes_file
+ if kt_files and includes_file:
+ f = open(includes_file, 'r')
+
+ lines = f.read().splitlines()
+ included = [line[1:] for line in lines if line.startswith('+')]
+ included_files = set()
+ included_dirs = []
+ for line in included:
+ if is_kotlin_file(line):
+ included_files.add(line)
+ else:
+ included_dirs += [line]
+
+ excluded_files = [line[1:] for line in lines if line.startswith('-')]
+
+ kt_files = [
+ kt_file for kt_file in kt_files if kt_file not in excluded_files and
+ (kt_file in included_files or is_included(kt_file, included_dirs))
+ ]
+
+ # No need to start ktfmt if there are no files to check/format.
+ if not kt_files:
+ sys.exit(0)
+
+ ktfmt_args += kt_files
+
+ dir = os.path.normpath(os.path.dirname(__file__))
+ ktfmt_jar = os.path.join(
+ dir, '../../prebuilts/build-tools/common/framework/ktfmt.jar')
+
+ ktlint_env = os.environ.copy()
+ ktlint_env['JAVA_CMD'] = 'java'
+ try:
+ process = subprocess.Popen(
+ ['java', '-jar', ktfmt_jar] + ktfmt_args,
+ stdout=subprocess.PIPE,
+ env=ktlint_env)
+ stdout, _ = process.communicate()
+ code = process.returncode
+ if check and code != 0:
+ print(
+ '**********************************************************************'
+ )
+ print(
+ 'Some Kotlin files are not properly formatted. Run the following command to format them:\n\n'
+ )
+ script_path = os.path.normpath(__file__)
+ incorrect_files = stdout.decode('utf-8').splitlines()
+ print('$ ' + script_path + ' ' + ' '.join(incorrect_files) + '\n')
+ print(
+ '**********************************************************************'
+ )
+ sys.exit(code)
+ else:
+ sys.exit(0)
+ except OSError as e:
+ print('Error running ktfmt')
+ sys.exit(1)
+
+
+def is_kotlin_file(name):
+ return name.endswith('.kt') or name.endswith('.kts')
+
+
+def is_included(file, dirs):
+ for dir in dirs:
+ if file.startswith(dir):
+ return True
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ktfmt_idea_plugin/DEVELOPING.md b/ktfmt_idea_plugin/DEVELOPING.md
new file mode 100644
index 0000000..de64dcb
--- /dev/null
+++ b/ktfmt_idea_plugin/DEVELOPING.md
@@ -0,0 +1,24 @@
+## Meaning of Versions
+
+Versions are defined in `build.gradle.kts`.
+
+`intellij.version` is used for -
+1. Building the plugin, so the source code needs to be compatible with it.
+2. Determining the _maximum_ supported version. This means that it needs to be updated every time a new IntelliJ _Branch_ version is released. For example, 2021.1, 2021.2, etc.
+
+`tasks.patchPluginXml.sinceBuild` is used for -
+1. Determinig the _minimum_ supported version, so the code must not reference classes not introduced yet.
+
+
+## Testing
+
+* Use Gradle to run the plugin.
+* _Don't_ use IntelliJ to run the plugin.
+
+```
+ktfmt/ktfmt_idea_plugin $ ./gradlew runIde
+```
+
+Point `intellij.alternativeIdePath` in `build.gradle.kts` to a local installation of IntelliJ / Android Studio to test the plugin with it. Don't forget to enable the plugin. Easiest way: Cmd-shift-A, type 'ktfmt', look for Preferences.
+
+Another way: change `intellij.version` and comment out `alternativeIdePath`. This will run the plugin on whatever version you put - it'll be downloaded through Gradle.
diff --git a/ktfmt_idea_plugin/README.md b/ktfmt_idea_plugin/README.md
new file mode 100644
index 0000000..2e44397
--- /dev/null
+++ b/ktfmt_idea_plugin/README.md
@@ -0,0 +1,9 @@
+## Tested with:
+
+Android Studio 4.1.1 (build 201)
+IntelliJ 2020.2 (build 202)
+IntelliJ 2020.3 (build 203)
+
+## Doesn't work with:
+
+Android Studio 4.0 (build 193) (requires large changes to the plugin code)
diff --git a/ktfmt_idea_plugin/build.gradle.kts b/ktfmt_idea_plugin/build.gradle.kts
new file mode 100644
index 0000000..63c7879
--- /dev/null
+++ b/ktfmt_idea_plugin/build.gradle.kts
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+plugins {
+ id("org.jetbrains.intellij") version "0.7.2"
+ java
+ id("com.diffplug.spotless") version "5.10.2"
+}
+
+val ktfmtVersion = rootProject.file("../version.txt").readText().trim()
+val pluginVersion = "1.1"
+
+group = "com.facebook"
+
+version = "$pluginVersion.$ktfmtVersion"
+
+repositories {
+ mavenCentral()
+ mavenLocal()
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+}
+
+dependencies {
+ implementation("com.facebook", "ktfmt", ktfmtVersion)
+ implementation("com.google.googlejavaformat", "google-java-format", "1.8")
+}
+
+// See https://github.com/JetBrains/gradle-intellij-plugin/
+intellij {
+ // Version with which to build (and run; unless alternativeIdePath is specified)
+ version = "2020.3"
+ // To run on a different IDE, uncomment and specify a path.
+ // alternativeIdePath = "/Applications/Android Studio.app"
+}
+
+tasks {
+ patchPluginXml {
+ sinceBuild("201")
+ untilBuild("")
+ }
+ publishPlugin { token(System.getenv("JETBRAINS_MARKETPLACE_TOKEN")) }
+ runPluginVerifier { ideVersions(listOf("211.6432.7")) }
+}
+
+spotless { java { googleJavaFormat() } }
diff --git a/ktfmt_idea_plugin/gradle.properties b/ktfmt_idea_plugin/gradle.properties
new file mode 100644
index 0000000..29e08e8
--- /dev/null
+++ b/ktfmt_idea_plugin/gradle.properties
@@ -0,0 +1 @@
+kotlin.code.style=official
\ No newline at end of file
diff --git a/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.jar b/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..62d4c05
--- /dev/null
+++ b/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.properties b/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..622ab64
--- /dev/null
+++ b/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/ktfmt_idea_plugin/gradlew b/ktfmt_idea_plugin/gradlew
new file mode 100755
index 0000000..fbd7c51
--- /dev/null
+++ b/ktfmt_idea_plugin/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+# https://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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/ktfmt_idea_plugin/gradlew.bat b/ktfmt_idea_plugin/gradlew.bat
new file mode 100644
index 0000000..a9f778a
--- /dev/null
+++ b/ktfmt_idea_plugin/gradlew.bat
@@ -0,0 +1,104 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/ktfmt_idea_plugin/settings.gradle.kts b/ktfmt_idea_plugin/settings.gradle.kts
new file mode 100644
index 0000000..8deb7fc
--- /dev/null
+++ b/ktfmt_idea_plugin/settings.gradle.kts
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+rootProject.name = "ktfmt_idea_plugin"
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/CodeStyleManagerDecorator.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/CodeStyleManagerDecorator.java
new file mode 100644
index 0000000..61de704
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/CodeStyleManagerDecorator.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.intellij;
+
+import com.intellij.formatting.FormattingMode;
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.ChangedRangesInfo;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.psi.codeStyle.DocCommentSettings;
+import com.intellij.psi.codeStyle.FormattingModeAwareIndentAdjuster;
+import com.intellij.psi.codeStyle.Indent;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.ThrowableRunnable;
+import java.util.Collection;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Decorates the {@link CodeStyleManager} abstract class by delegating to a concrete implementation
+ * instance (likely IJ's default instance).
+ */
+@SuppressWarnings("deprecation")
+class CodeStyleManagerDecorator extends CodeStyleManager
+ implements FormattingModeAwareIndentAdjuster {
+
+ private final CodeStyleManager delegate;
+
+ CodeStyleManagerDecorator(CodeStyleManager delegate) {
+ this.delegate = delegate;
+ }
+
+ CodeStyleManager getDelegate() {
+ return delegate;
+ }
+
+ @Override
+ public Project getProject() {
+ return delegate.getProject();
+ }
+
+ @Override
+ public PsiElement reformat(PsiElement element) throws IncorrectOperationException {
+ return delegate.reformat(element);
+ }
+
+ @Override
+ public PsiElement reformat(PsiElement element, boolean canChangeWhiteSpacesOnly)
+ throws IncorrectOperationException {
+ return delegate.reformat(element, canChangeWhiteSpacesOnly);
+ }
+
+ @Override
+ public PsiElement reformatRange(PsiElement element, int startOffset, int endOffset)
+ throws IncorrectOperationException {
+ return delegate.reformatRange(element, startOffset, endOffset);
+ }
+
+ @Override
+ public PsiElement reformatRange(
+ PsiElement element, int startOffset, int endOffset, boolean canChangeWhiteSpacesOnly)
+ throws IncorrectOperationException {
+ return delegate.reformatRange(element, startOffset, endOffset, canChangeWhiteSpacesOnly);
+ }
+
+ @Override
+ public void reformatText(PsiFile file, int startOffset, int endOffset)
+ throws IncorrectOperationException {
+ delegate.reformatText(file, startOffset, endOffset);
+ }
+
+ @Override
+ public void reformatText(PsiFile file, Collection<TextRange> ranges)
+ throws IncorrectOperationException {
+ delegate.reformatText(file, ranges);
+ }
+
+ @Override
+ public void reformatTextWithContext(PsiFile psiFile, ChangedRangesInfo changedRangesInfo)
+ throws IncorrectOperationException {
+ delegate.reformatTextWithContext(psiFile, changedRangesInfo);
+ }
+
+ @Override
+ public void reformatTextWithContext(PsiFile file, Collection<TextRange> ranges)
+ throws IncorrectOperationException {
+ delegate.reformatTextWithContext(file, ranges);
+ }
+
+ @Override
+ public void adjustLineIndent(PsiFile file, TextRange rangeToAdjust)
+ throws IncorrectOperationException {
+ delegate.adjustLineIndent(file, rangeToAdjust);
+ }
+
+ @Override
+ public int adjustLineIndent(PsiFile file, int offset) throws IncorrectOperationException {
+ return delegate.adjustLineIndent(file, offset);
+ }
+
+ @Override
+ public int adjustLineIndent(Document document, int offset) {
+ return delegate.adjustLineIndent(document, offset);
+ }
+
+ public void scheduleIndentAdjustment(Document document, int offset) {
+ delegate.scheduleIndentAdjustment(document, offset);
+ }
+
+ @Override
+ public boolean isLineToBeIndented(PsiFile file, int offset) {
+ return delegate.isLineToBeIndented(file, offset);
+ }
+
+ @Override
+ @Nullable
+ public String getLineIndent(PsiFile file, int offset) {
+ return delegate.getLineIndent(file, offset);
+ }
+
+ @Override
+ @Nullable
+ public String getLineIndent(PsiFile file, int offset, FormattingMode mode) {
+ return delegate.getLineIndent(file, offset, mode);
+ }
+
+ @Override
+ @Nullable
+ public String getLineIndent(Document document, int offset) {
+ return delegate.getLineIndent(document, offset);
+ }
+
+ @Override
+ public Indent getIndent(String text, FileType fileType) {
+ return delegate.getIndent(text, fileType);
+ }
+
+ @Override
+ public String fillIndent(Indent indent, FileType fileType) {
+ return delegate.fillIndent(indent, fileType);
+ }
+
+ @Override
+ public Indent zeroIndent() {
+ return delegate.zeroIndent();
+ }
+
+ @Override
+ public void reformatNewlyAddedElement(ASTNode block, ASTNode addedElement)
+ throws IncorrectOperationException {
+ delegate.reformatNewlyAddedElement(block, addedElement);
+ }
+
+ @Override
+ public boolean isSequentialProcessingAllowed() {
+ return delegate.isSequentialProcessingAllowed();
+ }
+
+ @Override
+ public void performActionWithFormatterDisabled(Runnable r) {
+ delegate.performActionWithFormatterDisabled(r);
+ }
+
+ @Override
+ public <T extends Throwable> void performActionWithFormatterDisabled(ThrowableRunnable<T> r)
+ throws T {
+ delegate.performActionWithFormatterDisabled(r);
+ }
+
+ @Override
+ public <T> T performActionWithFormatterDisabled(Computable<T> r) {
+ return delegate.performActionWithFormatterDisabled(r);
+ }
+
+ @Override
+ public int getSpacing(PsiFile file, int offset) {
+ return delegate.getSpacing(file, offset);
+ }
+
+ @Override
+ public int getMinLineFeeds(PsiFile file, int offset) {
+ return delegate.getMinLineFeeds(file, offset);
+ }
+
+ @Override
+ public void runWithDocCommentFormattingDisabled(PsiFile file, Runnable runnable) {
+ delegate.runWithDocCommentFormattingDisabled(file, runnable);
+ }
+
+ @Override
+ public DocCommentSettings getDocCommentSettings(PsiFile file) {
+ return delegate.getDocCommentSettings(file);
+ }
+
+ // From FormattingModeAwareIndentAdjuster
+
+ /** Uses same fallback as {@link CodeStyleManager#getCurrentFormattingMode}. */
+ @Override
+ public FormattingMode getCurrentFormattingMode() {
+ if (delegate instanceof FormattingModeAwareIndentAdjuster) {
+ return ((FormattingModeAwareIndentAdjuster) delegate).getCurrentFormattingMode();
+ }
+ return FormattingMode.REFORMAT;
+ }
+
+ @Override
+ public int adjustLineIndent(final Document document, final int offset, FormattingMode mode)
+ throws IncorrectOperationException {
+ if (delegate instanceof FormattingModeAwareIndentAdjuster) {
+ return ((FormattingModeAwareIndentAdjuster) delegate)
+ .adjustLineIndent(document, offset, mode);
+ }
+ return offset;
+ }
+
+ @Override
+ public void scheduleReformatWhenSettingsComputed(PsiFile file) {
+ delegate.scheduleReformatWhenSettingsComputed(file);
+ }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/FormatterUtil.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/FormatterUtil.java
new file mode 100644
index 0000000..c67f114
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/FormatterUtil.java
@@ -0,0 +1,51 @@
+/*
+ * 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.intellij;
+
+import static com.facebook.ktfmt.format.Formatter.format;
+
+import com.facebook.ktfmt.format.ParseError;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import com.google.googlejavaformat.java.FormatterException;
+import com.intellij.openapi.util.TextRange;
+import java.util.Map;
+
+final class FormatterUtil {
+
+ private FormatterUtil() {}
+
+ /**
+ * Formats 'code' using ktfmt.
+ *
+ * @return formatted code
+ */
+ static Map<TextRange, String> getReplacements(UiFormatterStyle uiFormatterStyle, String code) {
+ try {
+ return ImmutableMap.of(TextRange.allOf(code), formatCode(uiFormatterStyle, code));
+ } catch (FormatterException | ParseError e) {
+ return ImmutableMap.of();
+ }
+ }
+
+ @VisibleForTesting
+ static String formatCode(UiFormatterStyle uiFormatterStyle, String code)
+ throws FormatterException {
+
+ return format(uiFormatterStyle.getFormattingOptions(), code);
+ }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/InitialConfigurationProjectManagerListener.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/InitialConfigurationProjectManagerListener.java
new file mode 100644
index 0000000..2bc9775
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/InitialConfigurationProjectManagerListener.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.intellij;
+
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationDisplayType;
+import com.intellij.notification.NotificationGroup;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManagerListener;
+import org.jetbrains.annotations.NotNull;
+
+final class InitialConfigurationProjectManagerListener implements ProjectManagerListener {
+
+ private static final String NOTIFICATION_TITLE = "Enable ktfmt";
+ private static final NotificationGroup NOTIFICATION_GROUP =
+ new NotificationGroup(NOTIFICATION_TITLE, NotificationDisplayType.STICKY_BALLOON, true);
+
+ @Override
+ public void projectOpened(@NotNull Project project) {
+
+ KtfmtSettings settings = KtfmtSettings.getInstance(project);
+
+ if (settings.isUninitialized()) {
+ settings.setEnabled(false);
+ displayNewUserNotification(project, settings);
+ }
+ }
+
+ private void displayNewUserNotification(Project project, KtfmtSettings settings) {
+ Notification notification =
+ new Notification(
+ NOTIFICATION_GROUP.getDisplayId(),
+ NOTIFICATION_TITLE,
+ "The ktfmt plugin is disabled by default. "
+ + "<a href=\"enable\">Enable for this project</a>.",
+ NotificationType.INFORMATION,
+ (n, e) -> {
+ settings.setEnabled(true);
+ n.expire();
+ });
+ notification.notify(project);
+ }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtCodeStyleManager.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtCodeStyleManager.java
new file mode 100644
index 0000000..d2184ea
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtCodeStyleManager.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * 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.intellij;
+
+import static java.util.Comparator.comparing;
+
+import com.google.common.collect.ImmutableList;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.psi.impl.CheckUtil;
+import com.intellij.util.IncorrectOperationException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.kotlin.idea.KotlinFileType;
+
+/**
+ * A {@link CodeStyleManager} implementation which formats .kt files with ktfmt. Formatting of all
+ * other types of files is delegated to IJ's default implementation.
+ */
+class KtfmtCodeStyleManager extends CodeStyleManagerDecorator {
+
+ public KtfmtCodeStyleManager(@NotNull CodeStyleManager original) {
+ super(original);
+ }
+
+ @Override
+ public void reformatText(PsiFile file, int startOffset, int endOffset)
+ throws IncorrectOperationException {
+ if (overrideFormatterForFile(file)) {
+ formatInternal(file, ImmutableList.of(new TextRange(startOffset, endOffset)));
+ } else {
+ super.reformatText(file, startOffset, endOffset);
+ }
+ }
+
+ @Override
+ public void reformatText(PsiFile file, Collection<TextRange> ranges)
+ throws IncorrectOperationException {
+ if (overrideFormatterForFile(file)) {
+ formatInternal(file, ranges);
+ } else {
+ super.reformatText(file, ranges);
+ }
+ }
+
+ @Override
+ public void reformatTextWithContext(PsiFile file, Collection<TextRange> ranges) {
+ if (overrideFormatterForFile(file)) {
+ formatInternal(file, ranges);
+ } else {
+ super.reformatTextWithContext(file, ranges);
+ }
+ }
+
+ @Override
+ public PsiElement reformatRange(
+ PsiElement element, int startOffset, int endOffset, boolean canChangeWhiteSpacesOnly) {
+ // Only handle elements that are PsiFile for now -- otherwise we need to search
+ // for some
+ // element within the file at new locations given the original startOffset and
+ // endOffsets
+ // to serve as the return value.
+ PsiFile file = element instanceof PsiFile ? (PsiFile) element : null;
+ if (file != null && canChangeWhiteSpacesOnly && overrideFormatterForFile(file)) {
+ formatInternal(file, ImmutableList.of(new TextRange(startOffset, endOffset)));
+ return file;
+ } else {
+ return super.reformatRange(element, startOffset, endOffset, canChangeWhiteSpacesOnly);
+ }
+ }
+
+ /** Return whether or not this formatter can handle formatting the given file. */
+ private boolean overrideFormatterForFile(PsiFile file) {
+ return KotlinFileType.INSTANCE.getName().equals(file.getFileType().getName())
+ && KtfmtSettings.getInstance(getProject()).isEnabled();
+ }
+
+ private void formatInternal(PsiFile file, Collection<TextRange> ranges) {
+ ApplicationManager.getApplication().assertWriteAccessAllowed();
+ PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject());
+ documentManager.commitAllDocuments();
+ CheckUtil.checkWritable(file);
+
+ Document document = documentManager.getDocument(file);
+
+ if (document == null) {
+ return;
+ }
+ // If there are postponed PSI changes (e.g., during a refactoring), just abort.
+ // If we apply them now, then the incoming text ranges may no longer be valid.
+ if (documentManager.isDocumentBlockedByPsi(document)) {
+ return;
+ }
+
+ format(document, ranges);
+ }
+
+ /**
+ * Format the ranges of the given document.
+ *
+ * <p>Overriding methods will need to modify the document with the result of the external
+ * formatter (usually using {@link #performReplacements(Document, Map)}.
+ */
+ private void format(Document document, Collection<TextRange> ranges) {
+ UiFormatterStyle uiFormatterStyle =
+ KtfmtSettings.getInstance(getProject()).getUiFormatterStyle();
+
+ performReplacements(
+ document, FormatterUtil.getReplacements(uiFormatterStyle, document.getText()));
+ }
+
+ private void performReplacements(
+ final Document document, final Map<TextRange, String> replacements) {
+
+ if (replacements.isEmpty()) {
+ return;
+ }
+
+ TreeMap<TextRange, String> sorted = new TreeMap<>(comparing(TextRange::getStartOffset));
+ sorted.putAll(replacements);
+ WriteCommandAction.runWriteCommandAction(
+ getProject(),
+ () -> {
+ for (Entry<TextRange, String> entry : sorted.descendingMap().entrySet()) {
+ document.replaceString(
+ entry.getKey().getStartOffset(), entry.getKey().getEndOffset(), entry.getValue());
+ }
+ PsiDocumentManager.getInstance(getProject()).commitDocument(document);
+ });
+ }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.form b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.form
new file mode 100644
index 0000000..e515398
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.form
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.facebook.ktfmt.intellij.KtfmtConfigurable">
+ <grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="4a87f" class="javax.swing.JCheckBox" binding="enable" default-binding="true">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Enable ktfmt"/>
+ </properties>
+ </component>
+ <vspacer id="19e83">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ <component id="c93e1" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Code style"/>
+ </properties>
+ </component>
+ <component id="31761" class="javax.swing.JComboBox" binding="styleComboBox" custom-create="true">
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="1" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.java
new file mode 100644
index 0000000..ac82917
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.intellij;
+
+import com.facebook.ktfmt.intellij.KtfmtSettings.EnabledState;
+import com.intellij.openapi.options.BaseConfigurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SearchableConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.intellij.uiDesigner.core.GridLayoutManager;
+import com.intellij.uiDesigner.core.Spacer;
+import java.awt.Insets;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class KtfmtConfigurable extends BaseConfigurable implements SearchableConfigurable {
+
+ private final Project project;
+ private JPanel panel;
+ private JCheckBox enable;
+ private JComboBox styleComboBox;
+
+ public KtfmtConfigurable(Project project) {
+ this.project = project;
+ }
+
+ @NotNull
+ @Override
+ public String getId() {
+ return "ktfmt.settings";
+ }
+
+ @Nullable
+ @Override
+ public Runnable enableSearch(String option) {
+ return null;
+ }
+
+ @Nls
+ @Override
+ public String getDisplayName() {
+ return "ktfmt Settings";
+ }
+
+ @Nullable
+ @Override
+ public String getHelpTopic() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public JComponent createComponent() {
+ return panel;
+ }
+
+ @Override
+ public void apply() throws ConfigurationException {
+ KtfmtSettings settings = KtfmtSettings.getInstance(project);
+ settings.setEnabled(enable.isSelected() ? EnabledState.ENABLED : getDisabledState());
+ settings.setUiFormatterStyle(((UiFormatterStyle) styleComboBox.getSelectedItem()));
+ }
+
+ private EnabledState getDisabledState() {
+ // The default settings (inherited by new projects) are either 'enabled' or
+ // 'show notification'. There's no way to default new projects to disabled. If
+ // someone wants
+ // that, we can add another checkbox, I suppose.
+ return project.isDefault() ? EnabledState.UNKNOWN : EnabledState.DISABLED;
+ }
+
+ @Override
+ public void reset() {
+ KtfmtSettings settings = KtfmtSettings.getInstance(project);
+ enable.setSelected(settings.isEnabled());
+ styleComboBox.setSelectedItem(settings.getUiFormatterStyle());
+ }
+
+ @Override
+ public boolean isModified() {
+ KtfmtSettings settings = KtfmtSettings.getInstance(project);
+ return enable.isSelected() != settings.isEnabled()
+ || !styleComboBox.getSelectedItem().equals(settings.getUiFormatterStyle());
+ }
+
+ @Override
+ public void disposeUIResources() {}
+
+ private void createUIComponents() {
+ styleComboBox = new ComboBox<>(UiFormatterStyle.values());
+ }
+
+ {
+ // GUI initializer generated by IntelliJ IDEA GUI Designer
+ // >>> IMPORTANT!! <<<
+ // DO NOT EDIT OR ADD ANY CODE HERE!
+ $$$setupUI$$$();
+ }
+
+ /**
+ * Method generated by IntelliJ IDEA GUI Designer >>> IMPORTANT!! <<< DO NOT edit this method OR
+ * call it in your code!
+ *
+ * @noinspection ALL
+ */
+ private void $$$setupUI$$$() {
+ createUIComponents();
+ panel = new JPanel();
+ panel.setLayout(new GridLayoutManager(3, 2, new Insets(0, 0, 0, 0), -1, -1));
+ enable = new JCheckBox();
+ enable.setText("Enable ktfmt");
+ panel.add(
+ enable,
+ new GridConstraints(
+ 0,
+ 0,
+ 1,
+ 2,
+ GridConstraints.ANCHOR_WEST,
+ GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED,
+ null,
+ null,
+ null,
+ 0,
+ false));
+ final Spacer spacer1 = new Spacer();
+ panel.add(
+ spacer1,
+ new GridConstraints(
+ 2,
+ 0,
+ 1,
+ 2,
+ GridConstraints.ANCHOR_CENTER,
+ GridConstraints.FILL_VERTICAL,
+ 1,
+ GridConstraints.SIZEPOLICY_WANT_GROW,
+ null,
+ null,
+ null,
+ 0,
+ false));
+ final JLabel label1 = new JLabel();
+ label1.setText("Code style");
+ panel.add(
+ label1,
+ new GridConstraints(
+ 1,
+ 0,
+ 1,
+ 1,
+ GridConstraints.ANCHOR_WEST,
+ GridConstraints.FILL_NONE,
+ GridConstraints.SIZEPOLICY_FIXED,
+ GridConstraints.SIZEPOLICY_FIXED,
+ null,
+ null,
+ null,
+ 0,
+ false));
+ panel.add(
+ styleComboBox,
+ new GridConstraints(
+ 1,
+ 1,
+ 1,
+ 1,
+ GridConstraints.ANCHOR_WEST,
+ GridConstraints.FILL_HORIZONTAL,
+ GridConstraints.SIZEPOLICY_CAN_GROW,
+ GridConstraints.SIZEPOLICY_FIXED,
+ null,
+ null,
+ null,
+ 1,
+ false));
+ }
+
+ /** @noinspection ALL */
+ public JComponent $$$getRootComponent$$$() {
+ return panel;
+ }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtInstaller.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtInstaller.java
new file mode 100644
index 0000000..ab414e8
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtInstaller.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.intellij;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import com.intellij.ide.plugins.PluginManagerCore;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManagerListener;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.serviceContainer.ComponentManagerImpl;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * A component that replaces the default IntelliJ {@link CodeStyleManager} with one that formats via
+ * ktfmt.
+ */
+final class KtfmtInstaller implements ProjectManagerListener {
+
+ @Override
+ public void projectOpened(@NotNull Project project) {
+ installFormatter(project);
+ }
+
+ private static void installFormatter(Project project) {
+ CodeStyleManager currentManager = CodeStyleManager.getInstance(project);
+
+ if (currentManager instanceof KtfmtCodeStyleManager) {
+ currentManager = ((KtfmtCodeStyleManager) currentManager).getDelegate();
+ }
+
+ setManager(project, new KtfmtCodeStyleManager(currentManager));
+ }
+
+ private static void setManager(Project project, CodeStyleManager newManager) {
+ ComponentManagerImpl platformComponentManager = (ComponentManagerImpl) project;
+ IdeaPluginDescriptor plugin =
+ PluginManagerCore.getPlugin(PluginId.getId("com.facebook.ktfmt_idea_plugin"));
+ checkState(plugin != null, "Couldn't locate our own PluginDescriptor.");
+ platformComponentManager.registerServiceInstance(CodeStyleManager.class, newManager, plugin);
+ }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtSettings.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtSettings.java
new file mode 100644
index 0000000..076e633
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtSettings.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.intellij;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.Nullable;
+
+@State(
+ name = "KtfmtSettings",
+ storages = {@Storage("ktfmt.xml")})
+class KtfmtSettings implements PersistentStateComponent<KtfmtSettings.State> {
+
+ private State state = new State();
+
+ static KtfmtSettings getInstance(Project project) {
+ return ServiceManager.getService(project, KtfmtSettings.class);
+ }
+
+ @Nullable
+ @Override
+ public State getState() {
+ return state;
+ }
+
+ @Override
+ public void loadState(State state) {
+ this.state = state;
+ }
+
+ boolean isEnabled() {
+ return state.enabled.equals(EnabledState.ENABLED);
+ }
+
+ void setEnabled(boolean enabled) {
+ setEnabled(enabled ? EnabledState.ENABLED : EnabledState.DISABLED);
+ }
+
+ void setEnabled(EnabledState enabled) {
+ state.enabled = enabled;
+ }
+
+ boolean isUninitialized() {
+ return state.enabled.equals(EnabledState.UNKNOWN);
+ }
+
+ UiFormatterStyle getUiFormatterStyle() {
+ return state.uiFormatterStyle;
+ }
+
+ void setUiFormatterStyle(UiFormatterStyle uiFormatterStyle) {
+ state.uiFormatterStyle = uiFormatterStyle;
+ }
+
+ enum EnabledState {
+ UNKNOWN,
+ ENABLED,
+ DISABLED;
+ }
+
+ static class State {
+
+ private EnabledState enabled = EnabledState.UNKNOWN;
+ public UiFormatterStyle uiFormatterStyle = UiFormatterStyle.DEFAULT;
+
+ // enabled used to be a boolean so we use bean property methods for backwards
+ // compatibility
+ public void setEnabled(@Nullable String enabledStr) {
+ if (enabledStr == null) {
+ enabled = EnabledState.UNKNOWN;
+ } else if (Boolean.valueOf(enabledStr)) {
+ enabled = EnabledState.ENABLED;
+ } else {
+ enabled = EnabledState.DISABLED;
+ }
+ }
+
+ public String getEnabled() {
+ switch (enabled) {
+ case ENABLED:
+ return "true";
+ case DISABLED:
+ return "false";
+ default:
+ return null;
+ }
+ }
+ }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/UiFormatterStyle.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/UiFormatterStyle.java
new file mode 100644
index 0000000..8147397
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/UiFormatterStyle.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.intellij;
+
+import static com.facebook.ktfmt.format.Formatter.DROPBOX_FORMAT;
+import static com.facebook.ktfmt.format.Formatter.GOOGLE_FORMAT;
+import static com.facebook.ktfmt.format.Formatter.KOTLINLANG_FORMAT;
+
+import com.facebook.ktfmt.format.FormattingOptions;
+
+/** Configuration options for the formatting style. */
+enum UiFormatterStyle {
+ DEFAULT("Default", new FormattingOptions()),
+ DROPBOX("Dropbox", DROPBOX_FORMAT),
+ GOOGLE("Google (internal)", GOOGLE_FORMAT),
+ KOTLINLANG("Kotlinlang", KOTLINLANG_FORMAT);
+
+ private final String description;
+ private final FormattingOptions formattingOptions;
+
+ UiFormatterStyle(String description, FormattingOptions formattingOptions) {
+ this.description = description;
+ this.formattingOptions = formattingOptions;
+ }
+
+ FormattingOptions getFormattingOptions() {
+ return formattingOptions;
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+}
diff --git a/ktfmt_idea_plugin/src/main/resources/META-INF/plugin.xml b/ktfmt_idea_plugin/src/main/resources/META-INF/plugin.xml
new file mode 100644
index 0000000..bf371f9
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/resources/META-INF/plugin.xml
@@ -0,0 +1,27 @@
+<idea-plugin url="https://github.com/facebookincubator/ktfmt/tree/main/ktfmt_idea_plugin">
+ <id>com.facebook.ktfmt_idea_plugin</id>
+ <name>ktfmt</name>
+ <vendor url="https://github.com/facebookincubator/ktfmt">Facebook</vendor>
+
+ <description>ktfmt is a program that reformats Kotlin source code to comply with the common community standard for
+ Kotlin code conventions.
+ </description>
+
+ <depends>com.intellij.modules.platform</depends>
+
+ <applicationListeners>
+ <listener class="com.facebook.ktfmt.intellij.InitialConfigurationProjectManagerListener"
+ topic="com.intellij.openapi.project.ProjectManagerListener"/>
+ <listener class="com.facebook.ktfmt.intellij.KtfmtInstaller"
+ topic="com.intellij.openapi.project.ProjectManagerListener"/>
+ </applicationListeners>
+
+ <extensions defaultExtensionNs="com.intellij">
+ <projectConfigurable instance="com.facebook.ktfmt.intellij.KtfmtConfigurable"
+ id="com.facebook.ktfmt_idea_plugin.settings"
+ displayName="ktfmt Settings"
+ parentId="editor"/>
+ <projectService serviceImplementation="com.facebook.ktfmt.intellij.KtfmtSettings"/>
+ </extensions>
+
+</idea-plugin>
diff --git a/ktfmt_idea_plugin/src/test/java/com/facebook/ktfmt/intellij/FormatterUtilTest.java b/ktfmt_idea_plugin/src/test/java/com/facebook/ktfmt/intellij/FormatterUtilTest.java
new file mode 100644
index 0000000..8b6b642
--- /dev/null
+++ b/ktfmt_idea_plugin/src/test/java/com/facebook/ktfmt/intellij/FormatterUtilTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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.intellij;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class FormatterUtilTest {
+ @Test
+ public void getReplacements() throws Exception {
+ String code = "val a = 5";
+ String expected = "val a = 5\n";
+ String actual = FormatterUtil.formatCode(UiFormatterStyle.DEFAULT, code);
+
+ assertEquals(expected, actual);
+ }
+}
diff --git a/online_formatter/README.md b/online_formatter/README.md
new file mode 100644
index 0000000..139e582
--- /dev/null
+++ b/online_formatter/README.md
@@ -0,0 +1,15 @@
+# AWS Lambda to format Kotlin code using ktfmt
+
+## Build
+
+```
+./gradlew build
+```
+
+## Deploy
+
+```
+./build_and_deploy.sh
+```
+
+The script creates two jars, one with the `com.facebook.ktfmt.onlineformatter` package, and the other with all of its dependencies (including ktfmt itself). This makes deploying just the Lambda faster.
diff --git a/online_formatter/build.gradle.kts b/online_formatter/build.gradle.kts
new file mode 100644
index 0000000..140c338
--- /dev/null
+++ b/online_formatter/build.gradle.kts
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins { kotlin("jvm") version "1.5.0" }
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+}
+
+val ktfmtVersion = rootProject.file("../version.txt").readText().trim()
+
+dependencies {
+ implementation("com.facebook:ktfmt:$ktfmtVersion")
+ implementation(platform("software.amazon.awssdk:bom:2.10.73"))
+ implementation("software.amazon.awssdk:lambda")
+ implementation("com.amazonaws:aws-lambda-java-core:1.2.1")
+ implementation("com.amazonaws:aws-lambda-java-events:2.2.9")
+ implementation("com.google.code.gson:gson:2.8.6")
+ testImplementation(kotlin("test-junit"))
+}
+
+tasks {
+ test { useJUnit() }
+
+ withType<KotlinCompile>() { kotlinOptions.jvmTarget = "11" }
+
+ val packageFat by
+ creating(Zip::class) {
+ from(compileKotlin)
+ from(processResources)
+ into("lib") { from(configurations.runtimeClasspath) }
+ dirMode = 0b111101101 // 0755
+ fileMode = 0b111101101 // 0755
+ }
+
+ val packageLibs by
+ creating(Zip::class) {
+ into("java/lib") { from(configurations.runtimeClasspath) }
+ dirMode = 0b111101101 // 0755
+ fileMode = 0b111101101 // 0755
+ }
+
+ val packageSkinny by
+ creating(Zip::class) {
+ from(compileKotlin)
+ from(processResources)
+ }
+
+ build { dependsOn(packageSkinny) }
+}
diff --git a/online_formatter/build_and_deploy.sh b/online_formatter/build_and_deploy.sh
new file mode 100755
index 0000000..c0ec4b0
--- /dev/null
+++ b/online_formatter/build_and_deploy.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# 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.
+
+set -eo pipefail
+./gradlew -q packageLibs
+mv build/distributions/lambda.zip build/lambda-lib.zip
+
+./gradlew build
+
+ARTIFACT_BUCKET=ktfmt-online-formatter-lambda
+TEMPLATE=format.yaml
+aws cloudformation package --template-file $TEMPLATE --s3-bucket $ARTIFACT_BUCKET --output-template-file out.yml
+aws cloudformation deploy --template-file out.yml --stack-name ktfmt --capabilities CAPABILITY_NAMED_IAM --region=us-east-2
diff --git a/online_formatter/format.yaml b/online_formatter/format.yaml
new file mode 100644
index 0000000..bb504fb
--- /dev/null
+++ b/online_formatter/format.yaml
@@ -0,0 +1,29 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Transform: 'AWS::Serverless-2016-10-31'
+Description: An AWS Serverless Specification template describing your function.
+Resources:
+ format:
+ Type: 'AWS::Serverless::Function'
+ Properties:
+ CodeUri: build/distributions/lambda.zip
+ Handler: 'com.facebook.ktfmt.onlineformatter.Handler::handleRequest'
+ Runtime: java11
+ Description: ''
+ MemorySize: 1024
+ Timeout: 15
+ Policies:
+ - AWSLambdaBasicExecutionRole
+ - AWSLambda_ReadOnlyAccess
+ - AWSXrayWriteOnlyAccess
+ - AWSLambdaVPCAccessExecutionRole
+ Tracing: Active
+ Layers:
+ - !Ref libs
+ libs:
+ Type: AWS::Serverless::LayerVersion
+ Properties:
+ LayerName: ktfmt-lib
+ Description: Dependencies for ktfmt
+ ContentUri: build/lambda-lib.zip
+ CompatibleRuntimes:
+ - java11
diff --git a/online_formatter/gradle.properties b/online_formatter/gradle.properties
new file mode 100644
index 0000000..7fc6f1f
--- /dev/null
+++ b/online_formatter/gradle.properties
@@ -0,0 +1 @@
+kotlin.code.style=official
diff --git a/online_formatter/gradle/wrapper/gradle-wrapper.jar b/online_formatter/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
--- /dev/null
+++ b/online_formatter/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/online_formatter/gradle/wrapper/gradle-wrapper.properties b/online_formatter/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..be52383
--- /dev/null
+++ b/online_formatter/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/online_formatter/gradlew b/online_formatter/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/online_formatter/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+# https://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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/online_formatter/gradlew.bat b/online_formatter/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/online_formatter/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/online_formatter/settings.gradle.kts b/online_formatter/settings.gradle.kts
new file mode 100644
index 0000000..67cc482
--- /dev/null
+++ b/online_formatter/settings.gradle.kts
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+rootProject.name = "lambda"
diff --git a/online_formatter/src/main/kotlin/main.kt b/online_formatter/src/main/kotlin/main.kt
new file mode 100644
index 0000000..22611fe
--- /dev/null
+++ b/online_formatter/src/main/kotlin/main.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.onlineformatter
+
+import com.amazonaws.services.lambda.runtime.Context
+import com.amazonaws.services.lambda.runtime.RequestHandler
+import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent
+import com.facebook.ktfmt.cli.ParsedArgs
+import com.facebook.ktfmt.format.Formatter
+import com.google.gson.Gson
+import java.io.ByteArrayOutputStream
+import java.io.PrintStream
+
+class Handler : RequestHandler<APIGatewayProxyRequestEvent, String> {
+ init {
+ // Warm up.
+ gson.toJson(Formatter.format(Formatter.KOTLINLANG_FORMAT, "/* foo */ fun foo() {}"))
+ }
+
+ override fun handleRequest(event: APIGatewayProxyRequestEvent, context: Context?): String {
+ return gson.toJson(
+ try {
+ val request = gson.fromJson(event.body, Request::class.java)
+ val parsingErrors = ByteArrayOutputStream()
+ val style = request.style
+ val parsedArgs =
+ ParsedArgs.parseOptions(
+ PrintStream(parsingErrors), if (style == null) arrayOf() else arrayOf(style))
+ Response(
+ Formatter.format(
+ parsedArgs.formattingOptions.copy(maxWidth = request.maxWidth ?: 100),
+ request.source ?: ""),
+ parsingErrors.toString().ifEmpty { null })
+ } catch (e: Exception) {
+ Response(null, e.message)
+ })
+ }
+
+ companion object {
+ val gson = Gson()
+ }
+}
+
+class Request {
+ var source: String? = ""
+ var style: String? = ""
+ var maxWidth: Int? = 100
+}
+
+data class Response(val source: String?, val err: String?)
diff --git a/online_formatter/src/test/kotlin/HandlerTest.kt b/online_formatter/src/test/kotlin/HandlerTest.kt
new file mode 100644
index 0000000..ce7a141
--- /dev/null
+++ b/online_formatter/src/test/kotlin/HandlerTest.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.onlineformatter
+
+import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent
+import kotlin.test.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class HandlerTest {
+
+ @Test
+ fun dontCrash_success() {
+ val event = APIGatewayProxyRequestEvent()
+ event.body = """{"source": "fun foo ( ) { }","style": "--kotlinlang-style"}"""
+ val response = Handler().handleRequest(event, null)
+ assertEquals("""{"source":"fun foo() {}\n"}""", response)
+ }
+
+ @Test
+ fun dontCrash_malformedRequestJson() {
+ val event = APIGatewayProxyRequestEvent()
+ event.body = """{"source": "fun foo ( ) { }"style": "--kotlinlang-style"}"""
+ val response = Handler().handleRequest(event, null)
+ assertEquals(
+ """{"err":"com.google.gson.stream.MalformedJsonException: """ +
+ """Unterminated object at line 1 column 30 path ${'$'}.source"}""",
+ response)
+ }
+
+ @Test
+ fun dontCrash_emptyRequestJson() {
+ val event = APIGatewayProxyRequestEvent()
+ event.body = ""
+ val response = Handler().handleRequest(event, null)
+ assertEquals("{}", response)
+ }
+
+ @Test
+ fun dontCrash_malformedSourceCode() {
+ val event = APIGatewayProxyRequestEvent()
+ event.body = """{"source": "fun foo ( { }","style": "--kotlinlang-style"}"""
+ val response = Handler().handleRequest(event, null)
+ assertEquals("""{"err":"1:10: error: Expecting \u0027)\u0027"}""", response)
+ }
+
+ @Test
+ fun dontCrash_allNullRequest() {
+ val event = APIGatewayProxyRequestEvent()
+ event.body = """{}"""
+ val response = Handler().handleRequest(event, null)
+ assertEquals("""{"source":"\n"}""", response)
+ }
+
+ @Test
+ fun dontCrash_malformedStyle() {
+ val event = APIGatewayProxyRequestEvent()
+ event.body = """{"source": "fun foo ( ) { }","style": "blabla"}"""
+ val response = Handler().handleRequest(event, null)
+ assertEquals("""{"source":"fun foo() {}\n"}""", response)
+ }
+
+ @Test
+ fun dontCrash_noStyle() {
+ val event = APIGatewayProxyRequestEvent()
+ event.body = """{"source": "fun foo ( ) { }"}"""
+ val response = Handler().handleRequest(event, null)
+ assertEquals("""{"source":"fun foo() {}\n"}""", response)
+ }
+
+ @Test
+ fun dontCrash_negativeMaxWidth() {
+ val event = APIGatewayProxyRequestEvent()
+ event.body = """{"source": "fun foo ( ) { }", "maxWidth": -100}"""
+ val response = Handler().handleRequest(event, null)
+ assertEquals("""{"source":"fun foo() {}\n"}""", response)
+ }
+
+ /** maxWidth == null is treated as if it were 100 */
+ @Test
+ fun dontCrash_nullMaxWidth() {
+ val event = APIGatewayProxyRequestEvent()
+ event.body = """{"source": "fun foo ( ) { }", "maxWidth": null}"""
+ val response = Handler().handleRequest(event, null)
+ assertEquals("""{"source":"fun foo() {}\n"}""", response)
+ }
+
+ @Test
+ fun dontCrash_nonIntegerMaxWidth() {
+ val event = APIGatewayProxyRequestEvent()
+ event.body = """{"source": "fun foo ( ) { }", "maxWidth": "bla"}"""
+ val response = Handler().handleRequest(event, null)
+ assertEquals(
+ """{"err":"java.lang.NumberFormatException: For input string: \"bla\""}""", response)
+ }
+
+ @Test
+ fun dontCrash_coerceMaxWidthToInt() {
+ val event = APIGatewayProxyRequestEvent()
+ event.body = """{"source": "fun foo ( ) { }", "maxWidth": "100"}"""
+ val response = Handler().handleRequest(event, null)
+ assertEquals("""{"source":"fun foo() {}\n"}""", response)
+ }
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..b8a40db
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.facebook</groupId>
+ <artifactId>ktfmt-parent</artifactId>
+ <version>0.39</version>
+ <packaging>pom</packaging>
+
+ <name>Ktfmt Parent</name>
+ <description>A program that reformats Kotlin source code to comply with the common community standard for Kotlin code conventions.</description>
+ <url>https://github.com/facebookincubator/ktfmt</url>
+ <inceptionYear>2019</inceptionYear>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ </license>
+ </licenses>
+ <developers>
+ <developer>
+ <name>Facebook</name>
+ </developer>
+ </developers>
+ <scm>
+ <connection>scm:git:https://github.com/facebookincubator/ktfmt.git</connection>
+ <developerConnection>scm:git:git@github.com:facebookincubator/ktfmt.git</developerConnection>
+ <url>https://github.com/facebookincubator/ktfmt.git</url>
+ </scm>
+
+ <modules>
+ <module>core</module>
+ </modules>
+
+ <distributionManagement>
+ <snapshotRepository>
+ <id>ossrh</id>
+ <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+ </snapshotRepository>
+ </distributionManagement>
+
+ <profiles>
+ <profile>
+ <id>release</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.sonatype.plugins</groupId>
+ <artifactId>nexus-staging-maven-plugin</artifactId>
+ <version>1.6.7</version>
+ <extensions>true</extensions>
+ <configuration>
+ <serverId>ossrh</serverId>
+ <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+ <autoReleaseAfterClose>true</autoReleaseAfterClose>
+ </configuration>
+ </plugin>
+
+ <!-- Sign artifacts -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.6</version>
+ <executions>
+ <execution>
+ <id>sign-artifacts</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ <configuration>
+ <!-- Honor -Dgpg.passphrase=... on the command line. -->
+ <gpgArguments>
+ <arg>--pinentry-mode</arg>
+ <arg>loopback</arg>
+ </gpgArguments>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+
+</project>
diff --git a/version.txt b/version.txt
new file mode 100644
index 0000000..751b1ea
--- /dev/null
+++ b/version.txt
@@ -0,0 +1 @@
+0.39
diff --git a/website/README.md b/website/README.md
new file mode 100644
index 0000000..14ba602
--- /dev/null
+++ b/website/README.md
@@ -0,0 +1,19 @@
+# A demo website for ktfmt
+
+## Run
+
+```
+npm install .
+npm run simpleserver
+```
+
+## Build for deployment
+
+```
+npm install .
+KTFMT_TMP_DIR=$(mktemp -d)
+KTFMT_WEBSITE_OUTPUT_DIR=$KTFMT_TMP_DIR gulp build-website
+
+# Clean up:
+rm -rf "$KTFMT_TMP_DIR"
+```
diff --git a/website/all.css b/website/all.css
new file mode 100644
index 0000000..9a15931
--- /dev/null
+++ b/website/all.css
@@ -0,0 +1,310 @@
+/**
+ * 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.
+ */
+
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+body {
+ padding: 54px 0 40px 0;
+ overflow-y: scroll;
+}
+
+body.home {
+ padding-top: 0;
+}
+
+body,
+input,
+button,
+select,
+textarea,
+.navbar-search .search-query {
+ font: 400 14px/1.4em "Segoe UI", "Open Sans", Calibri, Candara, Arial, sans-serif;
+}
+
+.navbar .nav {
+ float: left;
+ margin-right: 0;
+}
+
+a {
+ color: #0066cc;
+ cursor: pointer;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ letter-spacing: -0.01em;
+ margin: 0;
+ line-height: normal;
+}
+
+/***** Bootstrap Cosmo Overrides *****/
+h1, h2 {
+ font-family: "Segoe UI Light", "Segoe UI", "Open Sans", Calibri, Candara, Arial, sans-serif;
+ font-weight: 300;
+}
+h1 {
+ font-size: 72px;
+ letter-spacing: -0.02em;
+}
+
+.hero-unit h1 {
+ font-size: 48px;
+}
+
+h2 {
+ font-size: 26px;
+}
+
+h3 {
+ font-size: 26px;
+}
+
+h4 {
+ font-size: 16px;
+}
+
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small {
+ color: #999;
+}
+
+.alert-heading,
+.alert h1,
+.alert h2,
+.alert h3,
+.alert h4,
+.alert h5,
+.alert h6 {
+ color: inherit;
+}
+
+.alert-info {
+ color: #3a87ad;
+ background-color: #d9edf7;
+}
+
+.navbar-inverse .navbar-inner {
+ background-color: #68217A;
+ -webkit-filter: none;
+ filter: none;
+}
+
+.navbar-inverse.home .navbar-inner {
+ background-color: transparent;
+}
+
+.navbar-inverse .btn-navbar {
+ background: transparent;
+ margin-top: 14px;
+}
+
+.navbar-inverse .btn-navbar:hover,
+.navbar-inverse .btn-navbar:focus,
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active,
+.navbar-inverse .btn-navbar.disabled,
+.navbar-inverse .btn-navbar[disabled] {
+ background: #442359;
+}
+
+.btn-primary {
+ background-color: #0072C6;
+}
+
+.home .hero-unit {
+ margin-top: -54px;
+ position: relative;
+ z-index: 100;
+}
+
+.hero-unit {
+ font-weight: normal;
+}
+
+.hero-unit h1 {
+ margin: 0 0 6px;
+}
+
+.nav-tabs > li > a {
+ color: #999;
+}
+
+.nav-tabs > li > a:hover {
+ color: #555;
+}
+
+.nav-tabs > .active > a,
+.nav-tabs > .active > a:hover,
+.nav-tabs > .active > a:focus {
+ color: #0072C6;
+}
+
+/***** General *****/
+
+body > section > .container {
+ padding-top: 12px;
+}
+
+.masthead {
+ background-color: #0072C6;
+ color: white;
+}
+
+.masthead .hero-unit {
+ padding: 30px 0 0;
+ background: none;
+}
+
+.navbar.home {
+ position: relative;
+ z-index: 500;
+}
+
+.navbar .nav>li>a {
+ text-shadow: none;
+ padding-top: 18px;
+ font-size: 14px;
+ text-transform: uppercase;
+}
+
+.navbar-inverse .nav-collapse .nav>li>a {
+ color: white;
+ padding-left: 0;
+}
+
+.navbar-inverse .nav>li>a.nav-item:focus, .navbar-inverse .nav>li>a.nav-item:hover {
+ background-color: rgba(0,0,0,.12);
+}
+
+.navbar-inverse .nav .active>a.nav-item, .navbar-inverse .nav .active>a.nav-item:hover, .navbar-inverse .nav .active>a.nav-item:focus {
+ color:#fff;
+ background-color: rgba(0,0,0,.24);
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+.navbar .logo {
+ padding: 16px 12px 0 0px;
+ height: 35px;
+ display: block;
+ float: left;
+ color: #fff;
+ font-size: 24px;
+ letter-spacing: -1px;
+}
+
+.navbar .logo a {
+ color: #fff;
+ font-size: 24px;
+ letter-spacing: -1px;
+}
+
+.navbar-fixed-top {
+ z-index: 500;
+}
+
+.flgroup:after {
+ content: "";
+ display: block;
+ visibility: hidden;
+ height: 0;
+ clear: both;
+}
+
+/* Controls */
+
+/* Media Queries */
+@media (min-width: 1200px) {
+ h1, h2 {
+ letter-spacing: -0.04em;
+ }
+
+ .hero-unit h1 {
+ font-size: 72px;
+ }
+
+ h2 {
+ font-size: 36px;
+ }
+
+}
+
+#gh-link {
+ display: none;
+ position: fixed;
+ top: 0;
+ right: 0;
+ border: 0;
+ margin:0;
+ z-index: 1000;
+}
+@media (min-width: 980px) {
+ #gh-link {
+ display: block;
+ }
+}
+
+@media (min-width: 980px) {
+ .navbar .nav {
+ float: right;
+ }
+
+ .navbar-inverse .nav-collapse .nav>li>a {
+ padding-left: 15px;
+ }
+}
+
+@media (min-width: 768px) and (max-width: 979px) {
+ h1 {
+ letter-spacing: -0.02em;
+ }
+}
+
+@media (max-width: 979px) {
+ body {
+ padding: inherit;
+ }
+
+ .navbar-fixed-top, .navbar-fixed-bottom, .navbar-static-top {
+ margin-right: inherit;
+ margin-left: inherit;
+ margin-bottom: 0;
+ }
+
+ .navbar-fixed-top .navbar-inner {
+ padding: 0 20px;
+ }
+
+ .navbar .container {
+ width: 724px;
+ }
+}
+
+@media (max-width: 767px) {
+ .navbar .container {
+ width: 100%;
+ }
+}
+
+@media (max-width: 480px) {
+ .navbar .logo a {
+ display: none;
+ }
+}
diff --git a/website/gulpfile.js b/website/gulpfile.js
new file mode 100644
index 0000000..dd08969
--- /dev/null
+++ b/website/gulpfile.js
@@ -0,0 +1,167 @@
+/**
+ * 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.
+ */
+
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+const gulp = require("gulp");
+const es = require("event-stream");
+const path = require("path");
+const fs = require("fs");
+const os = require('os');
+const rimraf = require("rimraf");
+const cp = require("child_process");
+const CleanCSS = require("clean-css");
+const uncss = require("uncss");
+
+const VERSION = fs.readFileSync("../version.txt", "utf-8").trim();
+
+allow_deploy_to_github = process.env.KTFMT_WEBSITE_ALLOW_DEPLOY_TO_GITHUB == '1';
+outdir = process.env.KTFMT_WEBSITE_OUTPUT_DIR || path.join(__dirname, '../release-ktfmt-website');
+console.log('Using output dir: ' + outdir)
+
+// --- website
+const cleanWebsiteTask = function (cb) {
+ rimraf(outdir, { maxBusyTries: 1 }, cb);
+};
+const buildWebsiteTask = function () {
+ function replaceWithRelativeResource(dataPath, contents, regex, callback) {
+ return contents.replace(regex, function (_, m0) {
+ var filePath = path.join(path.dirname(dataPath), m0);
+ return callback(m0, fs.readFileSync(filePath));
+ });
+ }
+
+ var waiting = 0;
+ var done = false;
+
+ return es
+ .merge(
+ gulp
+ .src(["**/*"], {
+ dot: true,
+ ignore: [
+ "package.json",
+ "package-lock.json",
+ "node_modules/**/*",
+ "gulpfile.js",
+ '.DS_Store',
+ ],
+ })
+ .pipe(
+ es.through(
+ function (data) {
+ if (!data.contents || !/\.(html)$/.test(data.path)) {
+ return this.emit("data", data);
+ }
+
+ var contents = data.contents.toString();
+ contents = contents.replace(/{{version}}/g, VERSION);
+ contents = contents.replace(
+ /{{year}}/g,
+ new Date().getFullYear()
+ );
+
+ var allCSS = "";
+ var tmpcontents = replaceWithRelativeResource(
+ data.path,
+ contents,
+ /<link data-inline="yes-please" href="([^"]+)".*/g,
+ function (m0, fileContents) {
+ allCSS += fileContents.toString("utf8");
+ return "";
+ }
+ );
+ tmpcontents = tmpcontents.replace(/<script.*/g, "");
+ tmpcontents = tmpcontents.replace(/<link.*/g, "");
+
+ waiting++;
+ uncss(
+ tmpcontents,
+ { raw: allCSS },
+ function (err, output) {
+ waiting--;
+
+ if (!err) {
+ output = new CleanCSS().minify(output).styles;
+ var isFirst = true;
+ contents = contents.replace(
+ /<link data-inline="yes-please" href="([^"]+)".*/g,
+ function (_, m0) {
+ if (isFirst) {
+ isFirst = false;
+ return "<style>" + output + "</style>";
+ }
+ return "";
+ }
+ );
+ }
+
+ // Inline javascript
+ contents = replaceWithRelativeResource(
+ data.path,
+ contents,
+ /<script data-inline="yes-please" src="([^"]+)".*/g,
+ function (m0, fileContents) {
+ return (
+ "<script>" + fileContents.toString("utf8") + "</script>"
+ );
+ }
+ );
+
+ data.contents = Buffer.from(
+ contents.split(/\r\n|\r|\n/).join("\n")
+ );
+ this.emit("data", data);
+
+ if (done && waiting === 0) {
+ this.emit("end");
+ }
+ }.bind(this)
+ );
+ },
+ function () {
+ done = true;
+ if (waiting === 0) {
+ this.emit("end");
+ }
+ }
+ )
+ )
+ .pipe(gulp.dest(outdir))
+ )
+ .pipe(
+ es.through(
+ function (data) {
+ this.emit("data", data);
+ },
+ function () {
+ // temporarily create package.json so that npm install doesn't bark
+ fs.writeFileSync(path.join(outdir, 'package.json'), '{}');
+ fs.writeFileSync(path.join(outdir, '.nojekyll'), '');
+ cp.execSync('npm install monaco-editor@0.23', {
+ cwd: outdir
+ });
+ rimraf.sync(path.join(outdir, 'node_modules/monaco-editor/dev'));
+ rimraf.sync(path.join(outdir, 'node_modules/monaco-editor/esm'));
+ fs.unlinkSync(path.join(outdir, 'package.json'));
+
+ this.emit("end");
+ }
+ )
+ );
+}
+buildWebsiteSeries = gulp.series(cleanWebsiteTask, buildWebsiteTask);
+gulp.task("build-website", buildWebsiteSeries);
diff --git a/website/img/oss_logo.png b/website/img/oss_logo.png
new file mode 100644
index 0000000..226c942
--- /dev/null
+++ b/website/img/oss_logo.png
Binary files differ
diff --git a/website/index.html b/website/index.html
new file mode 100644
index 0000000..7572e39
--- /dev/null
+++ b/website/index.html
@@ -0,0 +1,308 @@
+<!-- Forked from https://microsoft.github.io/monaco-editor/ -->
+
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta name="viewport" content="width=device-width" />
+
+ <title>ktfmt - the Kotlin code formatter</title>
+
+ <link data-inline="yes-please" href="./lib/bootstrap-cosmo.css" rel="stylesheet"/>
+ <link data-inline="yes-please" href="./lib/bootstrap-responsive.min.css" rel="stylesheet"/>
+ <link data-inline="yes-please" href="./all.css" rel="stylesheet" />
+ <link data-inline="yes-please" href="./index/index.css" rel="stylesheet" />
+
+ <link data-name="vs/editor/editor.main"
+ rel="stylesheet"
+ href="node_modules/monaco-editor/min/vs/editor/editor.main.css"
+ />
+ </head>
+
+ <body>
+ <a id="gh-link" href="https://github.com/facebookincubator/ktfmt"
+ ><img
+ loading="lazy"
+ width="149"
+ height="149"
+ src="https://github.blog/wp-content/uploads/2008/12/forkme_right_white_ffffff.png?resize=149%2C149"
+ class="attachment-full size-full"
+ alt="Fork me on GitHub"
+ data-recalc-dims="1"
+ /></a>
+ <nav class="navbar navbar-inverse navbar-fixed-top">
+ <div class="navbar-inner">
+ <div class="container">
+ <div class="logo">
+ ktfmt
+ </div>
+ <div class="logo">
+ <a href="index.html">- the Kotlin code formatter</a>
+ </div>
+ <!-- collapse button for smaller screens -->
+ <button
+ type="button"
+ class="btn btn-navbar"
+ data-toggle="collapse"
+ data-target=".nav-collapse"
+ >
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+
+ <!-- navbar title -->
+ <div class="nav-collapse collapse">
+ <ul class="nav">
+ <li>
+ <a
+ class="nav-item"
+ href="https://github.com/facebookincubator/ktfmt"
+ >GitHub</a
+ >
+ </li>
+ <li>
+ <a
+ class="nav-item"
+ href="https://kotlinlang.slack.com/archives/C01GZCU0QNB"
+ >Slack</a
+ >
+ </li>
+ <li>
+ <a
+ class="nav-item"
+ href="https://search.maven.org/artifact/com.facebook/ktfmt/{{version}}/jar"
+ >Maven Central</a
+ >
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </nav>
+ <section class="try">
+ <div class="container">
+ <h3>About</h3>
+ <div class="row">
+ <div class="span12">
+ <br />
+ <p>
+ <code>ktfmt</code> is a program that pretty-prints (formats)
+ Kotlin code.
+ </p>
+ <p>
+ It always produces the same result, regardless of how the code
+ looks initially, freeing developers to focus on essence.
+ </p>
+ <p>
+ It is by design non-customizable in order to promote consistency.
+ </p>
+ </div>
+ </div>
+ <hr />
+ <h3>Usage</h3>
+ <div class="row">
+ <div class="span12">
+ <br />
+ <ul class="nav nav-tabs" id="usage_tab">
+ <li class="active">
+ <a href="#usage_intellij" data-toggle="tab"
+ >IntelliJ / Android Studio</a
+ >
+ </li>
+ <li>
+ <a href="#usage_gradle" data-toggle="tab">Gradle</a>
+ </li>
+ <li><a href="#usage_maven" data-toggle="tab">Maven</a></li>
+ <li><a href="#usage_cli" data-toggle="tab">Command line</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" id="usage_intellij">
+ <p>
+ A ktfmt plugin that augments the Reformat Code action (<code>⌥⌘L</code>) is available from JetBrains Marketplace.
+ </p>
+ <a class="btn btn-info" href="https://plugins.jetbrains.com/plugin/14912-ktfmt" target="_blank">Install</a>
+ </div>
+ <div class="tab-pane" id="usage_gradle">
+ <p>
+ <u><b>Spotless</b></u> (<a
+ href="https://github.com/diffplug/spotless/tree/main/plugin-gradle#ktfmt"
+ >details</a
+ >):
+ </p>
+
+ <pre>
+# build.gradle.kts
+plugins { id("com.diffplug.spotless") }
+
+// version and style are optional
+spotless { kotlin { ktfmt('{{version}}').kotlinlangStyle() } }
+</pre
+ >
+
+ <p>or,</p>
+ <p>
+ <u><b>ktfmt-gradle</b></u> (<a
+ href="https://github.com/cortinico/ktfmt-gradle#how-to-use-"
+ >details</a
+ >):
+ </p>
+ <pre>
+# build.gradle.kts
+plugins { id("com.ncorti.ktfmt.gradle") }
+
+ktfmt { kotlinLangStyle() }
+</pre
+ >
+ </div>
+ <div class="tab-pane" id="usage_maven">
+ <p>
+ <u><b>Spotless</b></u> (<a
+ href="https://github.com/diffplug/spotless/tree/main/plugin-maven#ktfmt"
+ >details</a
+ >):
+ </p>
+
+ <pre>
+# pom.xml
+<configuration>
+ <kotlin>
+ <ktfmt>
+ <version>{{version}}</version>
+ </ktfmt>
+ </kotlin>
+</configuration>
+</pre
+ >
+ </div>
+ <div class="tab-pane" id="usage_cli">
+ <p>
+ Download the jar from Maven Central and invoke it using
+ <code>java</code>:
+ </p>
+ <pre>
+$ wget https://repo1.maven.org/maven2/com/facebook/ktfmt/{{version}}/ktfmt-{{version}}-jar-with-dependencies.jar
+$ java -jar ktfmt-{{version}}-jar-with-dependencies.jar [--kotlinlang-style] [files...]
+</pre
+ >
+ </div>
+ </div>
+ </div>
+ </div>
+ <hr />
+ <h3>Try It Online</h3>
+ <small>Running v{{version}}</small>
+ <div class="editor row">
+ <div class="span12">
+ <div class="row">
+ <form id="editorForm" name="editorForm">
+ <div class="span3">
+ <label class="control-label">Style</label>
+ <select class="style-picker input-medium">
+ <option>kotlinlang</option>
+ <option>Java-like</option>
+ </select>
+ </div>
+ <div class="span3">
+ <label class="control-label">Column Limit</label>
+ <input
+ class="input-small column-limit-picker"
+ type="number"
+ min="50"
+ max="140"
+ class="form-control"
+ id="column_limit"
+ value="100"
+ required
+ />
+ </div>
+ <div class="span4 pull-right">
+ <button class="btn btn-primary pull-right" type="submit">
+ Format
+ </button>
+ <p
+ id="error-message"
+ class="text-error"
+ style="display: none"
+ >
+ Donec ullamcorper nulla non metus auctor fringilla.
+ </p>
+ </div>
+ </form>
+ </div>
+ <div class="editor-frame">
+ <div class="loading editor" style="display: none">
+ <div class="progress progress-striped active">
+ <div class="bar"></div>
+ </div>
+ </div>
+ <div id="editor"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ <footer class="container">
+ <hr />
+ <p class="text-center">
+ [
+ <a
+ target="_blank"
+ rel="noopener noreferrer"
+ href="https://opensource.facebook.com/legal/privacy/"
+ >Privacy</a
+ >
+ ] [
+ <a
+ target="_blank"
+ rel="noopener noreferrer"
+ href="https://opensource.facebook.com/legal/terms/"
+ >Terms</a
+ >
+ ] [
+ <a
+ target="_blank"
+ rel="noopener noreferrer"
+ href="https://opensource.facebook.com/legal/data-policy/"
+ >Data Policy</a
+ >
+ ] [
+ <a
+ target="_blank"
+ rel="noopener noreferrer"
+ href="https://opensource.facebook.com/legal/cookie-policy/"
+ >Cookie Policy</a
+ >
+ ]
+ </p>
+ <p class="text-center">
+ <a href="https://opensource.facebook.com/" title="Facebook Open Source">
+ <img
+ src="img/oss_logo.png"
+ alt="Facebook Open Source"
+ style="max-height: 50px; margin-bottom: 12px"
+ />
+ </a>
+ <br />
+ <small>© {{year}} Facebook, Inc. Based on <a href="https://microsoft.github.io/monaco-editor">https://microsoft.github.io/monaco-editor</a></small>
+ </p>
+ </footer>
+
+ <script
+ src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"
+ integrity="sha256-wS9gmOZBqsqWxgIVgA8Y9WcQOa7PgSIX+rPA0VL2rbQ="
+ crossorigin="anonymous"
+ ></script>
+ <script
+ src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.0/bootstrap.min.js"
+ integrity="sha256-u+l2mGjpmGK/mFgUncmMcFKdMijvV+J3odlDJZSNUu8="
+ crossorigin="anonymous"
+ ></script>
+
+ <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
+ <script src="node_modules/monaco-editor/min/vs/editor/editor.main.nls.js"></script>
+ <script src="node_modules/monaco-editor/min/vs/editor/editor.main.js"></script>
+ <script data-inline="yes-please" src="./index/index.js"></script>
+ </body>
+</html>
diff --git a/website/index/index.css b/website/index/index.css
new file mode 100644
index 0000000..1135b17
--- /dev/null
+++ b/website/index/index.css
@@ -0,0 +1,274 @@
+/**
+ * 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.
+ */
+
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+.try .drops {
+ margin: 0;
+ list-style: none;
+ float: right;
+}
+
+.try .drops li {
+ float: left;
+ width: auto;
+ height: auto;
+ text-indent: 0;
+ font-size: 26px;
+ line-height: normal;
+ margin: 0 12px;
+ cursor: pointer;
+}
+
+.try .drops li a:hover,
+.try .drops li a:focus {
+ text-decoration: none;
+}
+
+.try .drops li h4 {
+ color: #999;
+}
+
+.try .drops li.active h4,
+.try .drops li.active:hover h4 {
+ color: #0072C6;
+}
+
+.try .drops li:hover h4 {
+ color: rgba(0, 114, 198, .5);
+}
+
+.try .editor.row {
+ padding: 18px 0
+}
+
+.try .row h4 {
+ padding-bottom: 6px;
+}
+
+.try .tile {
+ position: relative;
+ height: 72px;
+ border: 1px solid #ddd;
+ text-align: right;
+ overflow: hidden;
+ cursor: pointer;
+
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+
+ -webkit-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.try .tile:hover {
+ -webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25);
+ -moz-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25);
+ box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25);
+}
+
+.try .tile h4 {
+ margin: 0;
+ position: absolute;
+ right: 12px;
+ bottom: 12px;
+ z-index: 1;
+}
+
+.try .tile .glyph {
+ position: absolute;
+ left: 12px;
+ bottom: -6px;
+ background: url('../img/cloud.png') no-repeat;
+ background-size: 80px 43px;
+ height: 43px;
+ width: 80px;
+
+ opacity: .5;
+ transition: opacity .5s ease, bottom .5s ease;
+ -webkit-transition: opacity .5s ease, bottom .5s ease;
+}
+
+.try .tile:hover .glyph {
+ opacity: 1;
+ bottom: -2px;
+}
+
+.try .editor.row h4 small {
+ color: #555;
+}
+
+.try .editor.row .control-label {
+ display: inline-block;
+ position: relative;
+ top: -4px;
+ margin-right: 4px;
+}
+
+.try .editor.row .monaco-editor .find-widget input[type="text"] {
+ margin-bottom: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ -webkit-transition: none;
+ -moz-transition: none;
+ -o-transition: none;
+ transition: none;
+}
+
+.try .editor.row .monaco-editor .find-widget .monaco-checkbox .label {
+ min-height: 20px;
+ min-width: 20px;
+}
+
+.try .editor.row .monaco-editor .find-widget .close {
+ float: none;
+ opacity: 1;
+}
+
+.try .editor .editor-frame {
+ position: relative;
+}
+
+.try .editor .editor-frame #editor,
+.try .editor .editor-frame #diff-editor {
+ height: 400px;
+ margin-bottom: 10px;
+ border: 1px solid #eee;
+}
+
+.try .editor .editor-frame .loading {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ background-color: rgba(255, 255, 255, .5);
+ margin: 0 auto;
+ display: none;
+ z-index: 100;
+}
+
+.try .editor .editor-frame .progress {
+ width: 50%;
+ margin: 15% auto 0;
+}
+
+.try .editor .editor-frame .progress .bar {
+ width: 100%;
+ background-color: #4bb1cf;
+}
+
+.try .editor .editor-frame #editor .alert,
+.try .editor .editor-frame #diff-editor .alert {
+ margin: 18% auto 0;
+ width: 30%;
+ text-align: center;
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #eed3d7;
+}
+
+.try .editor #editor .monaco-editor .row,
+.try .editor #diff-editor .monaco-editor .row {
+ margin-left: 0;
+}
+
+.try .editor .vs.monaco-editor .context-view.monaco-menu-container a {
+ color: #646465;
+}
+
+.try .editor .vs-dark.monaco-editor .context-view.monaco-menu-container a {
+ color: #BBB;
+}
+
+@media (min-width: 1200px) {
+ .try .editor .row {
+ margin-left: 0px;
+ }
+}
+
+@media (max-width: 767px) {
+ .try .container {
+ margin: 0 24px;
+ }
+
+ .try .tile h4 {
+ right: 6px;
+ }
+
+ .try .editor > .span9 .row .span4 {
+ float: left;
+ width: 50%;
+ }
+
+ .try .editor h4 {
+ margin-top: 0;
+ }
+}
+
+@media (max-width: 520px) {
+ .try .editor > .span3 p,
+ .try .editor > .span3 h4 {
+ display: none;
+ }
+}
+
+@media (max-width: 320px) {
+ .try .editor > .span9 {
+ display: none;
+ }
+}
+
+/* ------- BEGIN bootstrap fixes for the editor ------- */
+
+.monaco-editor .container:before, .monaco-editor .row:before {
+ content: "";
+ display: inherit;
+}
+
+.monaco-editor .container:after, .monaco-editor .row:after {
+ clear: inherit;
+}
+
+.monaco-editor .container {
+ width: auto;
+ margin: inherit;
+ padding: inherit;
+}
+
+.monaco-editor .close {
+ float: none;
+ font-size: inherit;
+ font-weight: inherit;
+ line-height: inherit;
+ color: inherit;
+ text-shadow: inherit;
+ opacity: inherit;
+ filter: inherit;
+}
+
+.monaco-editor .row {
+ margin: inherit;
+}
+
+.monaco-editor .invisible {
+ visibility: visible;
+}
+
+/* ------- END bootstrap fixes for the editor ------- */
diff --git a/website/index/index.js b/website/index/index.js
new file mode 100644
index 0000000..55c5c8f
--- /dev/null
+++ b/website/index/index.js
@@ -0,0 +1,144 @@
+/**
+ * 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.
+ */
+
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+"use strict";
+
+var editor = null;
+var style = "--kotlinlang-style";
+var columnLimit = 100;
+var options = null;
+
+require.config({ paths: { vs: "node_modules/monaco-editor/min/vs" } });
+
+$(document).ready(function () {
+ require(["vs/editor/editor.main"], function () {
+ editor = monaco.editor.create(document.getElementById("editor"), {
+ value: [
+ "/**",
+ " * Formats the given Javadoc comment, which must start with /** and end with */. The output will",
+ " * start and end with the same characters.",
+ " */",
+ "class Handler : RequestHandler<APIGatewayProxyRequestEvent, String> {",
+ " override fun handleRequest(event: APIGatewayProxyRequestEvent, context: Context?): String {",
+ " for ((i, item) in items.withIndex()) {",
+ " if (needDot) {",
+ " // fillMode is bla bla",
+ " val fillMode =",
+ " if (unconsumedPrefixes.isNotEmpty() && i <= unconsumedPrefixes.peekFirst()) {",
+ " prefixFillMode /* bla bla */",
+ " } else {",
+ " Doc.FillMode.UNIFIED",
+ " }",
+ ' builder.breakOp(fillMode, "", ZERO, Optional.of(nameTag))',
+ " builder.token((item as KtQualifiedExpression).operationSign.value)",
+ " }",
+ " emitSelectorUpToParenthesis(item)",
+ " if (unconsumedPrefixes.isNotEmpty() && i == unconsumedPrefixes.peekFirst()) {",
+ " builder.close()",
+ " unconsumedPrefixes.removeFirst()",
+ " }",
+ " if (i == items.size - 1 && hasTrailingLambda) {",
+ " builder.close()",
+ " }",
+ " val argsIndent =",
+ " Indent.If.make(",
+ " nameTag,",
+ " expressionBreakIndent,",
+ " if (trailingDereferences) expressionBreakIndent else ZERO",
+ " )",
+ " }",
+ " }",
+ "}",
+ ].join("\n"),
+ language: "kotlin",
+ rulers: [columnLimit],
+ scrollBeyondLastLine: false,
+ });
+ });
+ options = editor.getOptions();
+ window.onresize = function () {
+ editor.layout();
+ };
+
+ $(".style-picker").change(function () {
+ changeStyle(this.selectedIndex);
+ });
+
+ $(".column-limit-picker").change(function () {
+ columnLimit = parseInt(this.value);
+ editor.updateOptions({'rulers': [columnLimit]});
+ reformatEditor();
+ });
+
+ $("#editorForm").submit(function (event) {
+ event.preventDefault();
+ reformatEditor();
+ });
+});
+
+function changeStyle(newStyle) {
+ style = newStyle === 0 ? "--kotlinlang-style" : undefined;
+ reformatEditor();
+}
+
+function reformatEditor() {
+ disableDemoUi();
+ $.ajax({
+ type: "POST",
+ url: "https://8uj6xa47qa.execute-api.us-east-2.amazonaws.com/ktfmt",
+ contentType: "application/json",
+ data: JSON.stringify({
+ source: editor.getValue(),
+ maxWidth: columnLimit,
+ style: style,
+ }),
+ dataType: "json",
+ error: function (xhr, errorType, errorThrown) {
+ showError(errorThrown);
+ },
+ })
+ .done(function (data) {
+ if (data.err) {
+ showError(data.err);
+ } else {
+ $("#error-message").hide();
+ editor.setValue(data.source);
+ }
+ })
+ .always(function () {
+ enableDemoUi();
+ });
+}
+
+function disableDemoUi() {
+ $('.loading.editor').show();
+ $("#editorForm :input").prop("disabled", true);
+ editor.updateOptions({ readOnly: true, language: 'text' });
+}
+
+function enableDemoUi() {
+ $("#editorForm :input").prop("disabled", false);
+ editor.updateOptions({ readOnly: false });
+ $('.loading.editor').fadeOut({ duration: 300 });
+}
+
+function showError(error) {
+ var errorMessage = $("#error-message");
+ errorMessage.text("Error: " + error);
+ errorMessage.show();
+}
diff --git a/website/lib/bootstrap-cosmo.css b/website/lib/bootstrap-cosmo.css
new file mode 100644
index 0000000..4ee78a2
--- /dev/null
+++ b/website/lib/bootstrap-cosmo.css
@@ -0,0 +1,6794 @@
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+/*!
+ * Bootstrap v2.3.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+.clearfix {
+ *zoom: 1;
+}
+
+.clearfix:before,
+.clearfix:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.clearfix:after {
+ clear: both;
+}
+
+.hide-text {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+.input-block-level {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+nav,
+section {
+ display: block;
+}
+
+audio,
+canvas,
+video {
+ display: inline-block;
+ *display: inline;
+ *zoom: 1;
+}
+
+audio:not([controls]) {
+ display: none;
+}
+
+html {
+ font-size: 100%;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+
+a:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+a:hover,
+a:active {
+ outline: 0;
+}
+
+sub,
+sup {
+ position: relative;
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+img {
+ width: auto\9;
+ height: auto;
+ max-width: 100%;
+ vertical-align: middle;
+ border: 0;
+ -ms-interpolation-mode: bicubic;
+}
+
+#map_canvas img,
+.google-maps img {
+ max-width: none;
+}
+
+button,
+input,
+select,
+textarea {
+ margin: 0;
+ font-size: 100%;
+ vertical-align: middle;
+}
+
+button,
+input {
+ *overflow: visible;
+ line-height: normal;
+}
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ cursor: pointer;
+ -webkit-appearance: button;
+}
+
+label,
+select,
+button,
+input[type="button"],
+input[type="reset"],
+input[type="submit"],
+input[type="radio"],
+input[type="checkbox"] {
+ cursor: pointer;
+}
+
+input[type="search"] {
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ -webkit-appearance: textfield;
+}
+
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button {
+ -webkit-appearance: none;
+}
+
+textarea {
+ overflow: auto;
+ vertical-align: top;
+}
+
+@media print {
+ * {
+ color: #000 !important;
+ text-shadow: none !important;
+ background: transparent !important;
+ box-shadow: none !important;
+ }
+ a,
+ a:visited {
+ text-decoration: underline;
+ }
+ a[href]:after {
+ content: " (" attr(href) ")";
+ }
+ abbr[title]:after {
+ content: " (" attr(title) ")";
+ }
+ .ir a:after,
+ a[href^="javascript:"]:after,
+ a[href^="#"]:after {
+ content: "";
+ }
+ pre,
+ blockquote {
+ border: 1px solid #999;
+ page-break-inside: avoid;
+ }
+ thead {
+ display: table-header-group;
+ }
+ tr,
+ img {
+ page-break-inside: avoid;
+ }
+ img {
+ max-width: 100% !important;
+ }
+ @page {
+ margin: 0.5cm;
+ }
+ p,
+ h2,
+ h3 {
+ orphans: 3;
+ widows: 3;
+ }
+ h2,
+ h3 {
+ page-break-after: avoid;
+ }
+}
+
+body {
+ margin: 0;
+ line-height: 20px;
+ color: #555555;
+ background-color: #ffffff;
+}
+
+a {
+ color: #007fff;
+ text-decoration: none;
+}
+
+a:hover,
+a:focus {
+ color: #0066cc;
+ text-decoration: underline;
+}
+
+.img-rounded {
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.img-polaroid {
+ padding: 4px;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.img-circle {
+ -webkit-border-radius: 500px;
+ -moz-border-radius: 500px;
+ border-radius: 500px;
+}
+
+.row {
+ margin-left: -20px;
+ *zoom: 1;
+}
+
+.row:before,
+.row:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.row:after {
+ clear: both;
+}
+
+[class*="span"] {
+ float: left;
+ min-height: 1px;
+ margin-left: 20px;
+}
+
+.container,
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+ width: 940px;
+}
+
+.span12 {
+ width: 940px;
+}
+
+.span11 {
+ width: 860px;
+}
+
+.span10 {
+ width: 780px;
+}
+
+.span9 {
+ width: 700px;
+}
+
+.span8 {
+ width: 620px;
+}
+
+.span7 {
+ width: 540px;
+}
+
+.span6 {
+ width: 460px;
+}
+
+.span5 {
+ width: 380px;
+}
+
+.span4 {
+ width: 300px;
+}
+
+.span3 {
+ width: 220px;
+}
+
+.span2 {
+ width: 140px;
+}
+
+.span1 {
+ width: 60px;
+}
+
+.offset12 {
+ margin-left: 980px;
+}
+
+.offset11 {
+ margin-left: 900px;
+}
+
+.offset10 {
+ margin-left: 820px;
+}
+
+.offset9 {
+ margin-left: 740px;
+}
+
+.offset8 {
+ margin-left: 660px;
+}
+
+.offset7 {
+ margin-left: 580px;
+}
+
+.offset6 {
+ margin-left: 500px;
+}
+
+.offset5 {
+ margin-left: 420px;
+}
+
+.offset4 {
+ margin-left: 340px;
+}
+
+.offset3 {
+ margin-left: 260px;
+}
+
+.offset2 {
+ margin-left: 180px;
+}
+
+.offset1 {
+ margin-left: 100px;
+}
+
+.row-fluid {
+ width: 100%;
+ *zoom: 1;
+}
+
+.row-fluid:before,
+.row-fluid:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.row-fluid:after {
+ clear: both;
+}
+
+.row-fluid [class*="span"] {
+ display: block;
+ float: left;
+ width: 100%;
+ min-height: 30px;
+ margin-left: 2.127659574468085%;
+ *margin-left: 2.074468085106383%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+}
+
+.row-fluid .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 2.127659574468085%;
+}
+
+.row-fluid .span12 {
+ width: 100%;
+ *width: 99.94680851063829%;
+}
+
+.row-fluid .span11 {
+ width: 91.48936170212765%;
+ *width: 91.43617021276594%;
+}
+
+.row-fluid .span10 {
+ width: 82.97872340425532%;
+ *width: 82.92553191489361%;
+}
+
+.row-fluid .span9 {
+ width: 74.46808510638297%;
+ *width: 74.41489361702126%;
+}
+
+.row-fluid .span8 {
+ width: 65.95744680851064%;
+ *width: 65.90425531914893%;
+}
+
+.row-fluid .span7 {
+ width: 57.44680851063829%;
+ *width: 57.39361702127659%;
+}
+
+.row-fluid .span6 {
+ width: 48.93617021276595%;
+ *width: 48.88297872340425%;
+}
+
+.row-fluid .span5 {
+ width: 40.42553191489362%;
+ *width: 40.37234042553192%;
+}
+
+.row-fluid .span4 {
+ width: 31.914893617021278%;
+ *width: 31.861702127659576%;
+}
+
+.row-fluid .span3 {
+ width: 23.404255319148934%;
+ *width: 23.351063829787233%;
+}
+
+.row-fluid .span2 {
+ width: 14.893617021276595%;
+ *width: 14.840425531914894%;
+}
+
+.row-fluid .span1 {
+ width: 6.382978723404255%;
+ *width: 6.329787234042553%;
+}
+
+.row-fluid .offset12 {
+ margin-left: 104.25531914893617%;
+ *margin-left: 104.14893617021275%;
+}
+
+.row-fluid .offset12:first-child {
+ margin-left: 102.12765957446808%;
+ *margin-left: 102.02127659574467%;
+}
+
+.row-fluid .offset11 {
+ margin-left: 95.74468085106382%;
+ *margin-left: 95.6382978723404%;
+}
+
+.row-fluid .offset11:first-child {
+ margin-left: 93.61702127659574%;
+ *margin-left: 93.51063829787232%;
+}
+
+.row-fluid .offset10 {
+ margin-left: 87.23404255319149%;
+ *margin-left: 87.12765957446807%;
+}
+
+.row-fluid .offset10:first-child {
+ margin-left: 85.1063829787234%;
+ *margin-left: 84.99999999999999%;
+}
+
+.row-fluid .offset9 {
+ margin-left: 78.72340425531914%;
+ *margin-left: 78.61702127659572%;
+}
+
+.row-fluid .offset9:first-child {
+ margin-left: 76.59574468085106%;
+ *margin-left: 76.48936170212764%;
+}
+
+.row-fluid .offset8 {
+ margin-left: 70.2127659574468%;
+ *margin-left: 70.10638297872339%;
+}
+
+.row-fluid .offset8:first-child {
+ margin-left: 68.08510638297872%;
+ *margin-left: 67.9787234042553%;
+}
+
+.row-fluid .offset7 {
+ margin-left: 61.70212765957446%;
+ *margin-left: 61.59574468085106%;
+}
+
+.row-fluid .offset7:first-child {
+ margin-left: 59.574468085106375%;
+ *margin-left: 59.46808510638297%;
+}
+
+.row-fluid .offset6 {
+ margin-left: 53.191489361702125%;
+ *margin-left: 53.085106382978715%;
+}
+
+.row-fluid .offset6:first-child {
+ margin-left: 51.063829787234035%;
+ *margin-left: 50.95744680851063%;
+}
+
+.row-fluid .offset5 {
+ margin-left: 44.68085106382979%;
+ *margin-left: 44.57446808510638%;
+}
+
+.row-fluid .offset5:first-child {
+ margin-left: 42.5531914893617%;
+ *margin-left: 42.4468085106383%;
+}
+
+.row-fluid .offset4 {
+ margin-left: 36.170212765957444%;
+ *margin-left: 36.06382978723405%;
+}
+
+.row-fluid .offset4:first-child {
+ margin-left: 34.04255319148936%;
+ *margin-left: 33.93617021276596%;
+}
+
+.row-fluid .offset3 {
+ margin-left: 27.659574468085104%;
+ *margin-left: 27.5531914893617%;
+}
+
+.row-fluid .offset3:first-child {
+ margin-left: 25.53191489361702%;
+ *margin-left: 25.425531914893618%;
+}
+
+.row-fluid .offset2 {
+ margin-left: 19.148936170212764%;
+ *margin-left: 19.04255319148936%;
+}
+
+.row-fluid .offset2:first-child {
+ margin-left: 17.02127659574468%;
+ *margin-left: 16.914893617021278%;
+}
+
+.row-fluid .offset1 {
+ margin-left: 10.638297872340425%;
+ *margin-left: 10.53191489361702%;
+}
+
+.row-fluid .offset1:first-child {
+ margin-left: 8.51063829787234%;
+ *margin-left: 8.404255319148938%;
+}
+
+[class*="span"].hide,
+.row-fluid [class*="span"].hide {
+ display: none;
+}
+
+[class*="span"].pull-right,
+.row-fluid [class*="span"].pull-right {
+ float: right;
+}
+
+.container {
+ margin-right: auto;
+ margin-left: auto;
+ *zoom: 1;
+}
+
+.container:before,
+.container:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.container:after {
+ clear: both;
+}
+
+.container-fluid {
+ padding-right: 20px;
+ padding-left: 20px;
+ *zoom: 1;
+}
+
+.container-fluid:before,
+.container-fluid:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.container-fluid:after {
+ clear: both;
+}
+
+p {
+ margin: 0 0 10px;
+}
+
+.lead {
+ margin-bottom: 20px;
+ font-size: 21px;
+ font-weight: 200;
+ line-height: 30px;
+}
+
+small {
+ font-size: 85%;
+}
+
+strong {
+ font-weight: bold;
+}
+
+em {
+ font-style: italic;
+}
+
+cite {
+ font-style: normal;
+}
+
+.muted {
+ color: #dfdfdf;
+}
+
+a.muted:hover,
+a.muted:focus {
+ color: #c6c6c6;
+}
+
+.text-warning {
+ color: #ffffff;
+}
+
+a.text-warning:hover,
+a.text-warning:focus {
+ color: #e6e6e6;
+}
+
+.text-error {
+ color: #ffffff;
+}
+
+a.text-error:hover,
+a.text-error:focus {
+ color: #e6e6e6;
+}
+
+.text-info {
+ color: #ffffff;
+}
+
+a.text-info:hover,
+a.text-info:focus {
+ color: #e6e6e6;
+}
+
+.text-success {
+ color: #ffffff;
+}
+
+a.text-success:hover,
+a.text-success:focus {
+ color: #e6e6e6;
+}
+
+.text-left {
+ text-align: left;
+}
+
+.text-right {
+ text-align: right;
+}
+
+.text-center {
+ text-align: center;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ margin: 10px 0;
+ font-family: inherit;
+ font-weight: 300;
+ line-height: 20px;
+ color: #080808;
+ text-rendering: optimizelegibility;
+}
+
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small {
+ font-weight: normal;
+ line-height: 1;
+ color: #dfdfdf;
+}
+
+h1,
+h2,
+h3 {
+ line-height: 40px;
+}
+
+h1 {
+ font-size: 38.5px;
+}
+
+h2 {
+ font-size: 31.5px;
+}
+
+h3 {
+ font-size: 24.5px;
+}
+
+h4 {
+ font-size: 17.5px;
+}
+
+h5 {
+ font-size: 14px;
+}
+
+h6 {
+ font-size: 11.9px;
+}
+
+h1 small {
+ font-size: 24.5px;
+}
+
+h2 small {
+ font-size: 17.5px;
+}
+
+h3 small {
+ font-size: 14px;
+}
+
+h4 small {
+ font-size: 14px;
+}
+
+.page-header {
+ padding-bottom: 9px;
+ margin: 20px 0 30px;
+ border-bottom: 1px solid #eeeeee;
+}
+
+ul,
+ol {
+ padding: 0;
+ margin: 0 0 10px 25px;
+}
+
+ul ul,
+ul ol,
+ol ol,
+ol ul {
+ margin-bottom: 0;
+}
+
+li {
+ line-height: 20px;
+}
+
+ul.unstyled,
+ol.unstyled {
+ margin-left: 0;
+ list-style: none;
+}
+
+ul.inline,
+ol.inline {
+ margin-left: 0;
+ list-style: none;
+}
+
+ul.inline > li,
+ol.inline > li {
+ display: inline-block;
+ *display: inline;
+ padding-right: 5px;
+ padding-left: 5px;
+ *zoom: 1;
+}
+
+dl {
+ margin-bottom: 20px;
+}
+
+dt,
+dd {
+ line-height: 20px;
+}
+
+dt {
+ font-weight: bold;
+}
+
+dd {
+ margin-left: 10px;
+}
+
+.dl-horizontal {
+ *zoom: 1;
+}
+
+.dl-horizontal:before,
+.dl-horizontal:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.dl-horizontal:after {
+ clear: both;
+}
+
+.dl-horizontal dt {
+ float: left;
+ width: 160px;
+ overflow: hidden;
+ clear: left;
+ text-align: right;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.dl-horizontal dd {
+ margin-left: 180px;
+}
+
+hr {
+ margin: 20px 0;
+ border: 0;
+ border-top: 1px solid #eeeeee;
+ border-bottom: 1px solid #ffffff;
+}
+
+abbr[title],
+abbr[data-original-title] {
+ cursor: help;
+ border-bottom: 1px dotted #dfdfdf;
+}
+
+abbr.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
+}
+
+blockquote {
+ padding: 0 0 0 15px;
+ margin: 0 0 20px;
+ border-left: 5px solid #eeeeee;
+}
+
+blockquote p {
+ margin-bottom: 0;
+ font-size: 17.5px;
+ font-weight: 300;
+ line-height: 1.25;
+}
+
+blockquote small {
+ display: block;
+ line-height: 20px;
+ color: #dfdfdf;
+}
+
+blockquote small:before {
+ content: '\2014 \00A0';
+}
+
+blockquote.pull-right {
+ float: right;
+ padding-right: 15px;
+ padding-left: 0;
+ border-right: 5px solid #eeeeee;
+ border-left: 0;
+}
+
+blockquote.pull-right p,
+blockquote.pull-right small {
+ text-align: right;
+}
+
+blockquote.pull-right small:before {
+ content: '';
+}
+
+blockquote.pull-right small:after {
+ content: '\00A0 \2014';
+}
+
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+ content: "";
+}
+
+address {
+ display: block;
+ margin-bottom: 20px;
+ font-style: normal;
+ line-height: 20px;
+}
+
+code,
+pre {
+ padding: 0 3px 2px;
+ font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
+ font-size: 12px;
+ /* color: #999999; */
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+code {
+ padding: 2px 4px;
+ color: #d14;
+ white-space: nowrap;
+ background-color: #f7f7f9;
+ border: 1px solid #e1e1e8;
+}
+
+pre {
+ display: block;
+ padding: 9.5px;
+ margin: 0 0 10px;
+ font-size: 13px;
+ line-height: 20px;
+ word-break: break-all;
+ word-wrap: break-word;
+ white-space: pre;
+ white-space: pre-wrap;
+ background-color: #f5f5f5;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+pre.prettyprint {
+ margin-bottom: 20px;
+}
+
+pre code {
+ padding: 0;
+ color: inherit;
+ white-space: pre;
+ white-space: pre-wrap;
+ background-color: transparent;
+ border: 0;
+}
+
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
+}
+
+form {
+ margin: 0 0 20px;
+}
+
+fieldset {
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: 40px;
+ color: #999999;
+ border: 0;
+ border-bottom: 1px solid #e5e5e5;
+}
+
+legend small {
+ font-size: 15px;
+ color: #dfdfdf;
+}
+
+label,
+input,
+button,
+select,
+textarea {
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 20px;
+}
+
+input,
+button,
+select,
+textarea {
+ font-family: "Open Sans", Calibri, Candara, Arial, sans-serif;
+}
+
+label {
+ display: block;
+ margin-bottom: 5px;
+}
+
+select,
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+ display: inline-block;
+ height: 20px;
+ padding: 4px 6px;
+ margin-bottom: 10px;
+ font-size: 14px;
+ line-height: 20px;
+ color: #bbbbbb;
+ vertical-align: middle;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+input,
+textarea,
+.uneditable-input {
+ width: 206px;
+}
+
+textarea {
+ height: auto;
+}
+
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+ background-color: #ffffff;
+ border: 1px solid #bbbbbb;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
+ -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
+ -o-transition: border linear 0.2s, box-shadow linear 0.2s;
+ transition: border linear 0.2s, box-shadow linear 0.2s;
+}
+
+textarea:focus,
+input[type="text"]:focus,
+input[type="password"]:focus,
+input[type="datetime"]:focus,
+input[type="datetime-local"]:focus,
+input[type="date"]:focus,
+input[type="month"]:focus,
+input[type="time"]:focus,
+input[type="week"]:focus,
+input[type="number"]:focus,
+input[type="email"]:focus,
+input[type="url"]:focus,
+input[type="search"]:focus,
+input[type="tel"]:focus,
+input[type="color"]:focus,
+.uneditable-input:focus {
+ border-color: rgba(82, 168, 236, 0.8);
+ outline: 0;
+ outline: thin dotted \9;
+ /* IE6-9 */
+
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+}
+
+input[type="radio"],
+input[type="checkbox"] {
+ margin: 4px 0 0;
+ margin-top: 1px \9;
+ *margin-top: 0;
+ line-height: normal;
+}
+
+input[type="file"],
+input[type="image"],
+input[type="submit"],
+input[type="reset"],
+input[type="button"],
+input[type="radio"],
+input[type="checkbox"] {
+ width: auto;
+}
+
+select,
+input[type="file"] {
+ height: 30px;
+ /* In IE7, the height of the select element cannot be changed by height, only font-size */
+
+ *margin-top: 4px;
+ /* For IE7, add top margin to align select with labels */
+
+ line-height: 30px;
+}
+
+select {
+ width: 220px;
+ background-color: #ffffff;
+ border: 1px solid #bbbbbb;
+}
+
+select[multiple],
+select[size] {
+ height: auto;
+}
+
+select:focus,
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+.uneditable-input,
+.uneditable-textarea {
+ color: #dfdfdf;
+ cursor: not-allowed;
+ background-color: #fcfcfc;
+ border-color: #bbbbbb;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+}
+
+.uneditable-input {
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.uneditable-textarea {
+ width: auto;
+ height: auto;
+}
+
+input:-moz-placeholder,
+textarea:-moz-placeholder {
+ color: #bbbbbb;
+}
+
+input:-ms-input-placeholder,
+textarea:-ms-input-placeholder {
+ color: #bbbbbb;
+}
+
+input::-webkit-input-placeholder,
+textarea::-webkit-input-placeholder {
+ color: #bbbbbb;
+}
+
+.radio,
+.checkbox {
+ min-height: 20px;
+ padding-left: 20px;
+}
+
+.radio input[type="radio"],
+.checkbox input[type="checkbox"] {
+ float: left;
+ margin-left: -20px;
+}
+
+.controls > .radio:first-child,
+.controls > .checkbox:first-child {
+ padding-top: 5px;
+}
+
+.radio.inline,
+.checkbox.inline {
+ display: inline-block;
+ padding-top: 5px;
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+
+.radio.inline + .radio.inline,
+.checkbox.inline + .checkbox.inline {
+ margin-left: 10px;
+}
+
+.input-mini {
+ width: 60px;
+}
+
+.input-small {
+ width: 90px;
+}
+
+.input-medium {
+ width: 150px;
+}
+
+.input-large {
+ width: 210px;
+}
+
+.input-xlarge {
+ width: 270px;
+}
+
+.input-xxlarge {
+ width: 530px;
+}
+
+input[class*="span"],
+select[class*="span"],
+textarea[class*="span"],
+.uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"] {
+ float: none;
+ margin-left: 0;
+}
+
+.input-append input[class*="span"],
+.input-append .uneditable-input[class*="span"],
+.input-prepend input[class*="span"],
+.input-prepend .uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"],
+.row-fluid .input-prepend [class*="span"],
+.row-fluid .input-append [class*="span"] {
+ display: inline-block;
+}
+
+input,
+textarea,
+.uneditable-input {
+ margin-left: 0;
+}
+
+.controls-row [class*="span"] + [class*="span"] {
+ margin-left: 20px;
+}
+
+input.span12,
+textarea.span12,
+.uneditable-input.span12 {
+ width: 926px;
+}
+
+input.span11,
+textarea.span11,
+.uneditable-input.span11 {
+ width: 846px;
+}
+
+input.span10,
+textarea.span10,
+.uneditable-input.span10 {
+ width: 766px;
+}
+
+input.span9,
+textarea.span9,
+.uneditable-input.span9 {
+ width: 686px;
+}
+
+input.span8,
+textarea.span8,
+.uneditable-input.span8 {
+ width: 606px;
+}
+
+input.span7,
+textarea.span7,
+.uneditable-input.span7 {
+ width: 526px;
+}
+
+input.span6,
+textarea.span6,
+.uneditable-input.span6 {
+ width: 446px;
+}
+
+input.span5,
+textarea.span5,
+.uneditable-input.span5 {
+ width: 366px;
+}
+
+input.span4,
+textarea.span4,
+.uneditable-input.span4 {
+ width: 286px;
+}
+
+input.span3,
+textarea.span3,
+.uneditable-input.span3 {
+ width: 206px;
+}
+
+input.span2,
+textarea.span2,
+.uneditable-input.span2 {
+ width: 126px;
+}
+
+input.span1,
+textarea.span1,
+.uneditable-input.span1 {
+ width: 46px;
+}
+
+.controls-row {
+ *zoom: 1;
+}
+
+.controls-row:before,
+.controls-row:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.controls-row:after {
+ clear: both;
+}
+
+.controls-row [class*="span"],
+.row-fluid .controls-row [class*="span"] {
+ float: left;
+}
+
+.controls-row .checkbox[class*="span"],
+.controls-row .radio[class*="span"] {
+ padding-top: 5px;
+}
+
+input[disabled],
+select[disabled],
+textarea[disabled],
+input[readonly],
+select[readonly],
+textarea[readonly] {
+ cursor: not-allowed;
+ background-color: #eeeeee;
+}
+
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"][readonly],
+input[type="checkbox"][readonly] {
+ background-color: transparent;
+}
+
+.control-group.warning .control-label,
+.control-group.warning .help-block,
+.control-group.warning .help-inline {
+ color: #ffffff;
+}
+
+.control-group.warning .checkbox,
+.control-group.warning .radio,
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+ color: #ffffff;
+}
+
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+ border-color: #ffffff;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.warning input:focus,
+.control-group.warning select:focus,
+.control-group.warning textarea:focus {
+ border-color: #e6e6e6;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+}
+
+.control-group.warning .input-prepend .add-on,
+.control-group.warning .input-append .add-on {
+ color: #ffffff;
+ background-color: #ff7518;
+ border-color: #ffffff;
+}
+
+.control-group.error .control-label,
+.control-group.error .help-block,
+.control-group.error .help-inline {
+ color: #ffffff;
+}
+
+.control-group.error .checkbox,
+.control-group.error .radio,
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+ color: #ffffff;
+}
+
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+ border-color: #ffffff;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.error input:focus,
+.control-group.error select:focus,
+.control-group.error textarea:focus {
+ border-color: #e6e6e6;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+}
+
+.control-group.error .input-prepend .add-on,
+.control-group.error .input-append .add-on {
+ color: #ffffff;
+ background-color: #ff0039;
+ border-color: #ffffff;
+}
+
+.control-group.success .control-label,
+.control-group.success .help-block,
+.control-group.success .help-inline {
+ color: #ffffff;
+}
+
+.control-group.success .checkbox,
+.control-group.success .radio,
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+ color: #ffffff;
+}
+
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+ border-color: #ffffff;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.success input:focus,
+.control-group.success select:focus,
+.control-group.success textarea:focus {
+ border-color: #e6e6e6;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+}
+
+.control-group.success .input-prepend .add-on,
+.control-group.success .input-append .add-on {
+ color: #ffffff;
+ background-color: #3fb618;
+ border-color: #ffffff;
+}
+
+.control-group.info .control-label,
+.control-group.info .help-block,
+.control-group.info .help-inline {
+ color: #ffffff;
+}
+
+.control-group.info .checkbox,
+.control-group.info .radio,
+.control-group.info input,
+.control-group.info select,
+.control-group.info textarea {
+ color: #ffffff;
+}
+
+.control-group.info input,
+.control-group.info select,
+.control-group.info textarea {
+ border-color: #ffffff;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.info input:focus,
+.control-group.info select:focus,
+.control-group.info textarea:focus {
+ border-color: #e6e6e6;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+}
+
+.control-group.info .input-prepend .add-on,
+.control-group.info .input-append .add-on {
+ color: #ffffff;
+ background-color: #9954bb;
+ border-color: #ffffff;
+}
+
+input:focus:invalid,
+textarea:focus:invalid,
+select:focus:invalid {
+ color: #b94a48;
+ border-color: #ee5f5b;
+}
+
+input:focus:invalid:focus,
+textarea:focus:invalid:focus,
+select:focus:invalid:focus {
+ border-color: #e9322d;
+ -webkit-box-shadow: 0 0 6px #f8b9b7;
+ -moz-box-shadow: 0 0 6px #f8b9b7;
+ box-shadow: 0 0 6px #f8b9b7;
+}
+
+.form-actions {
+ padding: 19px 20px 20px;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #e5e5e5;
+ *zoom: 1;
+}
+
+.form-actions:before,
+.form-actions:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.form-actions:after {
+ clear: both;
+}
+
+.help-block,
+.help-inline {
+ color: #7b7b7b;
+}
+
+.help-block {
+ display: block;
+ margin-bottom: 10px;
+}
+
+.help-inline {
+ display: inline-block;
+ *display: inline;
+ padding-left: 5px;
+ vertical-align: middle;
+ *zoom: 1;
+}
+
+.input-append,
+.input-prepend {
+ display: inline-block;
+ margin-bottom: 10px;
+ font-size: 0;
+ white-space: nowrap;
+ vertical-align: middle;
+}
+
+.input-append input,
+.input-prepend input,
+.input-append select,
+.input-prepend select,
+.input-append .uneditable-input,
+.input-prepend .uneditable-input,
+.input-append .dropdown-menu,
+.input-prepend .dropdown-menu,
+.input-append .popover,
+.input-prepend .popover {
+ font-size: 14px;
+}
+
+.input-append input,
+.input-prepend input,
+.input-append select,
+.input-prepend select,
+.input-append .uneditable-input,
+.input-prepend .uneditable-input {
+ position: relative;
+ margin-bottom: 0;
+ *margin-left: 0;
+ vertical-align: top;
+ -webkit-border-radius: 0 0 0px 0;
+ -moz-border-radius: 0 0 0px 0;
+ border-radius: 0 0 0px 0;
+}
+
+.input-append input:focus,
+.input-prepend input:focus,
+.input-append select:focus,
+.input-prepend select:focus,
+.input-append .uneditable-input:focus,
+.input-prepend .uneditable-input:focus {
+ z-index: 2;
+}
+
+.input-append .add-on,
+.input-prepend .add-on {
+ display: inline-block;
+ width: auto;
+ height: 20px;
+ min-width: 16px;
+ padding: 4px 5px;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 20px;
+ text-align: center;
+ text-shadow: 0 1px 0 #ffffff;
+ background-color: #eeeeee;
+ border: 1px solid #ccc;
+}
+
+.input-append .add-on,
+.input-prepend .add-on,
+.input-append .btn,
+.input-prepend .btn,
+.input-append .btn-group > .dropdown-toggle,
+.input-prepend .btn-group > .dropdown-toggle {
+ vertical-align: top;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.input-append .active,
+.input-prepend .active {
+ background-color: #96ed7a;
+ border-color: #3fb618;
+}
+
+.input-prepend .add-on,
+.input-prepend .btn {
+ margin-right: -1px;
+}
+
+.input-prepend .add-on:first-child,
+.input-prepend .btn:first-child {
+ -webkit-border-radius: 0 0 0 0px;
+ -moz-border-radius: 0 0 0 0px;
+ border-radius: 0 0 0 0px;
+}
+
+.input-append input,
+.input-append select,
+.input-append .uneditable-input {
+ -webkit-border-radius: 0 0 0 0px;
+ -moz-border-radius: 0 0 0 0px;
+ border-radius: 0 0 0 0px;
+}
+
+.input-append input + .btn-group .btn:last-child,
+.input-append select + .btn-group .btn:last-child,
+.input-append .uneditable-input + .btn-group .btn:last-child {
+ -webkit-border-radius: 0 0 0px 0;
+ -moz-border-radius: 0 0 0px 0;
+ border-radius: 0 0 0px 0;
+}
+
+.input-append .add-on,
+.input-append .btn,
+.input-append .btn-group {
+ margin-left: -1px;
+}
+
+.input-append .add-on:last-child,
+.input-append .btn:last-child,
+.input-append .btn-group:last-child > .dropdown-toggle {
+ -webkit-border-radius: 0 0 0px 0;
+ -moz-border-radius: 0 0 0px 0;
+ border-radius: 0 0 0px 0;
+}
+
+.input-prepend.input-append input,
+.input-prepend.input-append select,
+.input-prepend.input-append .uneditable-input {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.input-prepend.input-append input + .btn-group .btn,
+.input-prepend.input-append select + .btn-group .btn,
+.input-prepend.input-append .uneditable-input + .btn-group .btn {
+ -webkit-border-radius: 0 0 0px 0;
+ -moz-border-radius: 0 0 0px 0;
+ border-radius: 0 0 0px 0;
+}
+
+.input-prepend.input-append .add-on:first-child,
+.input-prepend.input-append .btn:first-child {
+ margin-right: -1px;
+ -webkit-border-radius: 0 0 0 0px;
+ -moz-border-radius: 0 0 0 0px;
+ border-radius: 0 0 0 0px;
+}
+
+.input-prepend.input-append .add-on:last-child,
+.input-prepend.input-append .btn:last-child {
+ margin-left: -1px;
+ -webkit-border-radius: 0 0 0px 0;
+ -moz-border-radius: 0 0 0px 0;
+ border-radius: 0 0 0px 0;
+}
+
+.input-prepend.input-append .btn-group:first-child {
+ margin-left: 0;
+}
+
+input.search-query {
+ padding-right: 14px;
+ padding-right: 4px \9;
+ padding-left: 14px;
+ padding-left: 4px \9;
+ /* IE7-8 doesn't have border-radius, so don't indent the padding */
+
+ margin-bottom: 0;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+
+/* Allow for input prepend/append in search forms */
+
+.form-search .input-append .search-query,
+.form-search .input-prepend .search-query {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.form-search .input-append .search-query {
+ -webkit-border-radius: 14px 0 0 14px;
+ -moz-border-radius: 14px 0 0 14px;
+ border-radius: 14px 0 0 14px;
+}
+
+.form-search .input-append .btn {
+ -webkit-border-radius: 0 14px 14px 0;
+ -moz-border-radius: 0 14px 14px 0;
+ border-radius: 0 14px 14px 0;
+}
+
+.form-search .input-prepend .search-query {
+ -webkit-border-radius: 0 14px 14px 0;
+ -moz-border-radius: 0 14px 14px 0;
+ border-radius: 0 14px 14px 0;
+}
+
+.form-search .input-prepend .btn {
+ -webkit-border-radius: 14px 0 0 14px;
+ -moz-border-radius: 14px 0 0 14px;
+ border-radius: 14px 0 0 14px;
+}
+
+.form-search input,
+.form-inline input,
+.form-horizontal input,
+.form-search textarea,
+.form-inline textarea,
+.form-horizontal textarea,
+.form-search select,
+.form-inline select,
+.form-horizontal select,
+.form-search .help-inline,
+.form-inline .help-inline,
+.form-horizontal .help-inline,
+.form-search .uneditable-input,
+.form-inline .uneditable-input,
+.form-horizontal .uneditable-input,
+.form-search .input-prepend,
+.form-inline .input-prepend,
+.form-horizontal .input-prepend,
+.form-search .input-append,
+.form-inline .input-append,
+.form-horizontal .input-append {
+ display: inline-block;
+ *display: inline;
+ margin-bottom: 0;
+ vertical-align: middle;
+ *zoom: 1;
+}
+
+.form-search .hide,
+.form-inline .hide,
+.form-horizontal .hide {
+ display: none;
+}
+
+.form-search label,
+.form-inline label,
+.form-search .btn-group,
+.form-inline .btn-group {
+ display: inline-block;
+}
+
+.form-search .input-append,
+.form-inline .input-append,
+.form-search .input-prepend,
+.form-inline .input-prepend {
+ margin-bottom: 0;
+}
+
+.form-search .radio,
+.form-search .checkbox,
+.form-inline .radio,
+.form-inline .checkbox {
+ padding-left: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+
+.form-search .radio input[type="radio"],
+.form-search .checkbox input[type="checkbox"],
+.form-inline .radio input[type="radio"],
+.form-inline .checkbox input[type="checkbox"] {
+ float: left;
+ margin-right: 3px;
+ margin-left: 0;
+}
+
+.control-group {
+ margin-bottom: 10px;
+}
+
+legend + .control-group {
+ margin-top: 20px;
+ -webkit-margin-top-collapse: separate;
+}
+
+.form-horizontal .control-group {
+ margin-bottom: 20px;
+ *zoom: 1;
+}
+
+.form-horizontal .control-group:before,
+.form-horizontal .control-group:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.form-horizontal .control-group:after {
+ clear: both;
+}
+
+.form-horizontal .control-label {
+ float: left;
+ width: 160px;
+ padding-top: 5px;
+ text-align: right;
+}
+
+.form-horizontal .controls {
+ *display: inline-block;
+ *padding-left: 20px;
+ margin-left: 180px;
+ *margin-left: 0;
+}
+
+.form-horizontal .controls:first-child {
+ *padding-left: 180px;
+}
+
+.form-horizontal .help-block {
+ margin-bottom: 0;
+}
+
+.form-horizontal input + .help-block,
+.form-horizontal select + .help-block,
+.form-horizontal textarea + .help-block,
+.form-horizontal .uneditable-input + .help-block,
+.form-horizontal .input-prepend + .help-block,
+.form-horizontal .input-append + .help-block {
+ margin-top: 10px;
+}
+
+.form-horizontal .form-actions {
+ padding-left: 180px;
+}
+
+table {
+ max-width: 100%;
+ background-color: transparent;
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+.table {
+ width: 100%;
+ margin-bottom: 20px;
+}
+
+.table th,
+.table td {
+ padding: 8px;
+ line-height: 20px;
+ text-align: left;
+ vertical-align: top;
+ border-top: 1px solid #dddddd;
+}
+
+.table th {
+ font-weight: bold;
+}
+
+.table thead th {
+ vertical-align: bottom;
+}
+
+.table caption + thead tr:first-child th,
+.table caption + thead tr:first-child td,
+.table colgroup + thead tr:first-child th,
+.table colgroup + thead tr:first-child td,
+.table thead:first-child tr:first-child th,
+.table thead:first-child tr:first-child td {
+ border-top: 0;
+}
+
+.table tbody + tbody {
+ border-top: 2px solid #dddddd;
+}
+
+.table .table {
+ background-color: #ffffff;
+}
+
+.table-condensed th,
+.table-condensed td {
+ padding: 4px 5px;
+}
+
+.table-bordered {
+ border: 1px solid #dddddd;
+ border-collapse: separate;
+ *border-collapse: collapse;
+ border-left: 0;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.table-bordered th,
+.table-bordered td {
+ border-left: 1px solid #dddddd;
+}
+
+.table-bordered caption + thead tr:first-child th,
+.table-bordered caption + tbody tr:first-child th,
+.table-bordered caption + tbody tr:first-child td,
+.table-bordered colgroup + thead tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child td,
+.table-bordered thead:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child td {
+ border-top: 0;
+}
+
+.table-bordered thead:first-child tr:first-child > th:first-child,
+.table-bordered tbody:first-child tr:first-child > td:first-child,
+.table-bordered tbody:first-child tr:first-child > th:first-child {
+ -webkit-border-top-left-radius: 0;
+ border-top-left-radius: 0;
+ -moz-border-radius-topleft: 0;
+}
+
+.table-bordered thead:first-child tr:first-child > th:last-child,
+.table-bordered tbody:first-child tr:first-child > td:last-child,
+.table-bordered tbody:first-child tr:first-child > th:last-child {
+ -webkit-border-top-right-radius: 0;
+ border-top-right-radius: 0;
+ -moz-border-radius-topright: 0;
+}
+
+.table-bordered thead:last-child tr:last-child > th:first-child,
+.table-bordered tbody:last-child tr:last-child > td:first-child,
+.table-bordered tbody:last-child tr:last-child > th:first-child,
+.table-bordered tfoot:last-child tr:last-child > td:first-child,
+.table-bordered tfoot:last-child tr:last-child > th:first-child {
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-border-radius-bottomleft: 0;
+}
+
+.table-bordered thead:last-child tr:last-child > th:last-child,
+.table-bordered tbody:last-child tr:last-child > td:last-child,
+.table-bordered tbody:last-child tr:last-child > th:last-child,
+.table-bordered tfoot:last-child tr:last-child > td:last-child,
+.table-bordered tfoot:last-child tr:last-child > th:last-child {
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-bottomright: 0;
+}
+
+.table-bordered tfoot + tbody:last-child tr:last-child td:first-child {
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-border-radius-bottomleft: 0;
+}
+
+.table-bordered tfoot + tbody:last-child tr:last-child td:last-child {
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-bottomright: 0;
+}
+
+.table-bordered caption + thead tr:first-child th:first-child,
+.table-bordered caption + tbody tr:first-child td:first-child,
+.table-bordered colgroup + thead tr:first-child th:first-child,
+.table-bordered colgroup + tbody tr:first-child td:first-child {
+ -webkit-border-top-left-radius: 0;
+ border-top-left-radius: 0;
+ -moz-border-radius-topleft: 0;
+}
+
+.table-bordered caption + thead tr:first-child th:last-child,
+.table-bordered caption + tbody tr:first-child td:last-child,
+.table-bordered colgroup + thead tr:first-child th:last-child,
+.table-bordered colgroup + tbody tr:first-child td:last-child {
+ -webkit-border-top-right-radius: 0;
+ border-top-right-radius: 0;
+ -moz-border-radius-topright: 0;
+}
+
+.table-striped tbody > tr:nth-child(odd) > td,
+.table-striped tbody > tr:nth-child(odd) > th {
+ background-color: #f9f9f9;
+}
+
+.table-hover tbody tr:hover > td,
+.table-hover tbody tr:hover > th {
+ background-color: #e8f8fd;
+}
+
+table td[class*="span"],
+table th[class*="span"],
+.row-fluid table td[class*="span"],
+.row-fluid table th[class*="span"] {
+ display: table-cell;
+ float: none;
+ margin-left: 0;
+}
+
+.table td.span1,
+.table th.span1 {
+ float: none;
+ width: 44px;
+ margin-left: 0;
+}
+
+.table td.span2,
+.table th.span2 {
+ float: none;
+ width: 124px;
+ margin-left: 0;
+}
+
+.table td.span3,
+.table th.span3 {
+ float: none;
+ width: 204px;
+ margin-left: 0;
+}
+
+.table td.span4,
+.table th.span4 {
+ float: none;
+ width: 284px;
+ margin-left: 0;
+}
+
+.table td.span5,
+.table th.span5 {
+ float: none;
+ width: 364px;
+ margin-left: 0;
+}
+
+.table td.span6,
+.table th.span6 {
+ float: none;
+ width: 444px;
+ margin-left: 0;
+}
+
+.table td.span7,
+.table th.span7 {
+ float: none;
+ width: 524px;
+ margin-left: 0;
+}
+
+.table td.span8,
+.table th.span8 {
+ float: none;
+ width: 604px;
+ margin-left: 0;
+}
+
+.table td.span9,
+.table th.span9 {
+ float: none;
+ width: 684px;
+ margin-left: 0;
+}
+
+.table td.span10,
+.table th.span10 {
+ float: none;
+ width: 764px;
+ margin-left: 0;
+}
+
+.table td.span11,
+.table th.span11 {
+ float: none;
+ width: 844px;
+ margin-left: 0;
+}
+
+.table td.span12,
+.table th.span12 {
+ float: none;
+ width: 924px;
+ margin-left: 0;
+}
+
+.table tbody tr.success > td {
+ background-color: #3fb618;
+}
+
+.table tbody tr.error > td {
+ background-color: #ff0039;
+}
+
+.table tbody tr.warning > td {
+ background-color: #ff7518;
+}
+
+.table tbody tr.info > td {
+ background-color: #9954bb;
+}
+
+.table-hover tbody tr.success:hover > td {
+ background-color: #379f15;
+}
+
+.table-hover tbody tr.error:hover > td {
+ background-color: #e60033;
+}
+
+.table-hover tbody tr.warning:hover > td {
+ background-color: #fe6600;
+}
+
+.table-hover tbody tr.info:hover > td {
+ background-color: #8d46b0;
+}
+
+/* White icons with optional class, or on hover/focus/active states of certain elements */
+
+.icon-white,
+.nav-pills > .active > a > [class^="icon-"],
+.nav-pills > .active > a > [class*=" icon-"],
+.nav-list > .active > a > [class^="icon-"],
+.nav-list > .active > a > [class*=" icon-"],
+.navbar-inverse .nav > .active > a > [class^="icon-"],
+.navbar-inverse .nav > .active > a > [class*=" icon-"],
+.dropdown-menu > li > a:hover > [class^="icon-"],
+.dropdown-menu > li > a:focus > [class^="icon-"],
+.dropdown-menu > li > a:hover > [class*=" icon-"],
+.dropdown-menu > li > a:focus > [class*=" icon-"],
+.dropdown-menu > .active > a > [class^="icon-"],
+.dropdown-menu > .active > a > [class*=" icon-"],
+.dropdown-submenu:hover > a > [class^="icon-"],
+.dropdown-submenu:focus > a > [class^="icon-"],
+.dropdown-submenu:hover > a > [class*=" icon-"],
+.dropdown-submenu:focus > a > [class*=" icon-"] {
+ background-image: url("../img/glyphicons-halflings-white.png");
+}
+
+.icon-glass {
+ background-position: 0 0;
+}
+
+.icon-music {
+ background-position: -24px 0;
+}
+
+.icon-search {
+ background-position: -48px 0;
+}
+
+.icon-envelope {
+ background-position: -72px 0;
+}
+
+.icon-heart {
+ background-position: -96px 0;
+}
+
+.icon-star {
+ background-position: -120px 0;
+}
+
+.icon-star-empty {
+ background-position: -144px 0;
+}
+
+.icon-user {
+ background-position: -168px 0;
+}
+
+.icon-film {
+ background-position: -192px 0;
+}
+
+.icon-th-large {
+ background-position: -216px 0;
+}
+
+.icon-th {
+ background-position: -240px 0;
+}
+
+.icon-th-list {
+ background-position: -264px 0;
+}
+
+.icon-ok {
+ background-position: -288px 0;
+}
+
+.icon-remove {
+ background-position: -312px 0;
+}
+
+.icon-zoom-in {
+ background-position: -336px 0;
+}
+
+.icon-zoom-out {
+ background-position: -360px 0;
+}
+
+.icon-off {
+ background-position: -384px 0;
+}
+
+.icon-signal {
+ background-position: -408px 0;
+}
+
+.icon-cog {
+ background-position: -432px 0;
+}
+
+.icon-trash {
+ background-position: -456px 0;
+}
+
+.icon-home {
+ background-position: 0 -24px;
+}
+
+.icon-file {
+ background-position: -24px -24px;
+}
+
+.icon-time {
+ background-position: -48px -24px;
+}
+
+.icon-road {
+ background-position: -72px -24px;
+}
+
+.icon-download-alt {
+ background-position: -96px -24px;
+}
+
+.icon-download {
+ background-position: -120px -24px;
+}
+
+.icon-upload {
+ background-position: -144px -24px;
+}
+
+.icon-inbox {
+ background-position: -168px -24px;
+}
+
+.icon-play-circle {
+ background-position: -192px -24px;
+}
+
+.icon-repeat {
+ background-position: -216px -24px;
+}
+
+.icon-refresh {
+ background-position: -240px -24px;
+}
+
+.icon-list-alt {
+ background-position: -264px -24px;
+}
+
+.icon-lock {
+ background-position: -287px -24px;
+}
+
+.icon-flag {
+ background-position: -312px -24px;
+}
+
+.icon-headphones {
+ background-position: -336px -24px;
+}
+
+.icon-volume-off {
+ background-position: -360px -24px;
+}
+
+.icon-volume-down {
+ background-position: -384px -24px;
+}
+
+.icon-volume-up {
+ background-position: -408px -24px;
+}
+
+.icon-qrcode {
+ background-position: -432px -24px;
+}
+
+.icon-barcode {
+ background-position: -456px -24px;
+}
+
+.icon-tag {
+ background-position: 0 -48px;
+}
+
+.icon-tags {
+ background-position: -25px -48px;
+}
+
+.icon-book {
+ background-position: -48px -48px;
+}
+
+.icon-bookmark {
+ background-position: -72px -48px;
+}
+
+.icon-print {
+ background-position: -96px -48px;
+}
+
+.icon-camera {
+ background-position: -120px -48px;
+}
+
+.icon-font {
+ background-position: -144px -48px;
+}
+
+.icon-bold {
+ background-position: -167px -48px;
+}
+
+.icon-italic {
+ background-position: -192px -48px;
+}
+
+.icon-text-height {
+ background-position: -216px -48px;
+}
+
+.icon-text-width {
+ background-position: -240px -48px;
+}
+
+.icon-align-left {
+ background-position: -264px -48px;
+}
+
+.icon-align-center {
+ background-position: -288px -48px;
+}
+
+.icon-align-right {
+ background-position: -312px -48px;
+}
+
+.icon-align-justify {
+ background-position: -336px -48px;
+}
+
+.icon-list {
+ background-position: -360px -48px;
+}
+
+.icon-indent-left {
+ background-position: -384px -48px;
+}
+
+.icon-indent-right {
+ background-position: -408px -48px;
+}
+
+.icon-facetime-video {
+ background-position: -432px -48px;
+}
+
+.icon-picture {
+ background-position: -456px -48px;
+}
+
+.icon-pencil {
+ background-position: 0 -72px;
+}
+
+.icon-map-marker {
+ background-position: -24px -72px;
+}
+
+.icon-adjust {
+ background-position: -48px -72px;
+}
+
+.icon-tint {
+ background-position: -72px -72px;
+}
+
+.icon-edit {
+ background-position: -96px -72px;
+}
+
+.icon-share {
+ background-position: -120px -72px;
+}
+
+.icon-check {
+ background-position: -144px -72px;
+}
+
+.icon-move {
+ background-position: -168px -72px;
+}
+
+.icon-step-backward {
+ background-position: -192px -72px;
+}
+
+.icon-fast-backward {
+ background-position: -216px -72px;
+}
+
+.icon-backward {
+ background-position: -240px -72px;
+}
+
+.icon-play {
+ background-position: -264px -72px;
+}
+
+.icon-pause {
+ background-position: -288px -72px;
+}
+
+.icon-stop {
+ background-position: -312px -72px;
+}
+
+.icon-forward {
+ background-position: -336px -72px;
+}
+
+.icon-fast-forward {
+ background-position: -360px -72px;
+}
+
+.icon-step-forward {
+ background-position: -384px -72px;
+}
+
+.icon-eject {
+ background-position: -408px -72px;
+}
+
+.icon-chevron-left {
+ background-position: -432px -72px;
+}
+
+.icon-chevron-right {
+ background-position: -456px -72px;
+}
+
+.icon-plus-sign {
+ background-position: 0 -96px;
+}
+
+.icon-minus-sign {
+ background-position: -24px -96px;
+}
+
+.icon-remove-sign {
+ background-position: -48px -96px;
+}
+
+.icon-ok-sign {
+ background-position: -72px -96px;
+}
+
+.icon-question-sign {
+ background-position: -96px -96px;
+}
+
+.icon-info-sign {
+ background-position: -120px -96px;
+}
+
+.icon-screenshot {
+ background-position: -144px -96px;
+}
+
+.icon-remove-circle {
+ background-position: -168px -96px;
+}
+
+.icon-ok-circle {
+ background-position: -192px -96px;
+}
+
+.icon-ban-circle {
+ background-position: -216px -96px;
+}
+
+.icon-arrow-left {
+ background-position: -240px -96px;
+}
+
+.icon-arrow-right {
+ background-position: -264px -96px;
+}
+
+.icon-arrow-up {
+ background-position: -289px -96px;
+}
+
+.icon-arrow-down {
+ background-position: -312px -96px;
+}
+
+.icon-share-alt {
+ background-position: -336px -96px;
+}
+
+.icon-resize-full {
+ background-position: -360px -96px;
+}
+
+.icon-resize-small {
+ background-position: -384px -96px;
+}
+
+.icon-plus {
+ background-position: -408px -96px;
+}
+
+.icon-minus {
+ background-position: -433px -96px;
+}
+
+.icon-asterisk {
+ background-position: -456px -96px;
+}
+
+.icon-exclamation-sign {
+ background-position: 0 -120px;
+}
+
+.icon-gift {
+ background-position: -24px -120px;
+}
+
+.icon-leaf {
+ background-position: -48px -120px;
+}
+
+.icon-fire {
+ background-position: -72px -120px;
+}
+
+.icon-eye-open {
+ background-position: -96px -120px;
+}
+
+.icon-eye-close {
+ background-position: -120px -120px;
+}
+
+.icon-warning-sign {
+ background-position: -144px -120px;
+}
+
+.icon-plane {
+ background-position: -168px -120px;
+}
+
+.icon-calendar {
+ background-position: -192px -120px;
+}
+
+.icon-random {
+ width: 16px;
+ background-position: -216px -120px;
+}
+
+.icon-comment {
+ background-position: -240px -120px;
+}
+
+.icon-magnet {
+ background-position: -264px -120px;
+}
+
+.icon-chevron-up {
+ background-position: -288px -120px;
+}
+
+.icon-chevron-down {
+ background-position: -313px -119px;
+}
+
+.icon-retweet {
+ background-position: -336px -120px;
+}
+
+.icon-shopping-cart {
+ background-position: -360px -120px;
+}
+
+.icon-folder-close {
+ width: 16px;
+ background-position: -384px -120px;
+}
+
+.icon-folder-open {
+ width: 16px;
+ background-position: -408px -120px;
+}
+
+.icon-resize-vertical {
+ background-position: -432px -119px;
+}
+
+.icon-resize-horizontal {
+ background-position: -456px -118px;
+}
+
+.icon-hdd {
+ background-position: 0 -144px;
+}
+
+.icon-bullhorn {
+ background-position: -24px -144px;
+}
+
+.icon-bell {
+ background-position: -48px -144px;
+}
+
+.icon-certificate {
+ background-position: -72px -144px;
+}
+
+.icon-thumbs-up {
+ background-position: -96px -144px;
+}
+
+.icon-thumbs-down {
+ background-position: -120px -144px;
+}
+
+.icon-hand-right {
+ background-position: -144px -144px;
+}
+
+.icon-hand-left {
+ background-position: -168px -144px;
+}
+
+.icon-hand-up {
+ background-position: -192px -144px;
+}
+
+.icon-hand-down {
+ background-position: -216px -144px;
+}
+
+.icon-circle-arrow-right {
+ background-position: -240px -144px;
+}
+
+.icon-circle-arrow-left {
+ background-position: -264px -144px;
+}
+
+.icon-circle-arrow-up {
+ background-position: -288px -144px;
+}
+
+.icon-circle-arrow-down {
+ background-position: -312px -144px;
+}
+
+.icon-globe {
+ background-position: -336px -144px;
+}
+
+.icon-wrench {
+ background-position: -360px -144px;
+}
+
+.icon-tasks {
+ background-position: -384px -144px;
+}
+
+.icon-filter {
+ background-position: -408px -144px;
+}
+
+.icon-briefcase {
+ background-position: -432px -144px;
+}
+
+.icon-fullscreen {
+ background-position: -456px -144px;
+}
+
+.dropup,
+.dropdown {
+ position: relative;
+}
+
+.dropdown-toggle {
+ *margin-bottom: -3px;
+}
+
+.dropdown-toggle:active,
+.open .dropdown-toggle {
+ outline: 0;
+}
+
+.caret {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ vertical-align: top;
+ border-top: 4px solid #000000;
+ border-right: 4px solid transparent;
+ border-left: 4px solid transparent;
+ content: "";
+}
+
+.dropdown .caret {
+ margin-top: 8px;
+ margin-left: 2px;
+}
+
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ list-style: none;
+ background-color: #ffffff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ *border-right-width: 2px;
+ *border-bottom-width: 2px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+
+.dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+
+.dropdown-menu .divider {
+ *width: 100%;
+ height: 1px;
+ margin: 9px 1px;
+ *margin: -5px 0 5px;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ border-bottom: 1px solid #ffffff;
+}
+
+.dropdown-menu > li > a {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: 20px;
+ color: #999999;
+ white-space: nowrap;
+}
+
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus,
+.dropdown-submenu:hover > a,
+.dropdown-submenu:focus > a {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: #007af5;
+ background-image: -moz-linear-gradient(top, #007fff, #0072e6);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#007fff), to(#0072e6));
+ background-image: -webkit-linear-gradient(top, #007fff, #0072e6);
+ background-image: -o-linear-gradient(top, #007fff, #0072e6);
+ background-image: linear-gradient(to bottom, #007fff, #0072e6);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff007fff', endColorstr='#ff0072e6', GradientType=0);
+}
+
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: #007af5;
+ background-image: -moz-linear-gradient(top, #007fff, #0072e6);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#007fff), to(#0072e6));
+ background-image: -webkit-linear-gradient(top, #007fff, #0072e6);
+ background-image: -o-linear-gradient(top, #007fff, #0072e6);
+ background-image: linear-gradient(to bottom, #007fff, #0072e6);
+ background-repeat: repeat-x;
+ outline: 0;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff007fff', endColorstr='#ff0072e6', GradientType=0);
+}
+
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ color: #dfdfdf;
+}
+
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ text-decoration: none;
+ cursor: default;
+ background-color: transparent;
+ background-image: none;
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.open {
+ *z-index: 1000;
+}
+
+.open > .dropdown-menu {
+ display: block;
+}
+
+.pull-right > .dropdown-menu {
+ right: 0;
+ left: auto;
+}
+
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+ border-top: 0;
+ border-bottom: 4px solid #000000;
+ content: "";
+}
+
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-bottom: 1px;
+}
+
+.dropdown-submenu {
+ position: relative;
+}
+
+.dropdown-submenu > .dropdown-menu {
+ top: 0;
+ left: 100%;
+ margin-top: -6px;
+ margin-left: -1px;
+ -webkit-border-radius: 0 6px 6px 6px;
+ -moz-border-radius: 0 6px 6px 6px;
+ border-radius: 0 6px 6px 6px;
+}
+
+.dropdown-submenu:hover > .dropdown-menu {
+ display: block;
+}
+
+.dropup .dropdown-submenu > .dropdown-menu {
+ top: auto;
+ bottom: 0;
+ margin-top: 0;
+ margin-bottom: -2px;
+ -webkit-border-radius: 5px 5px 5px 0;
+ -moz-border-radius: 5px 5px 5px 0;
+ border-radius: 5px 5px 5px 0;
+}
+
+.dropdown-submenu > a:after {
+ display: block;
+ float: right;
+ width: 0;
+ height: 0;
+ margin-top: 5px;
+ margin-right: -10px;
+ border-color: transparent;
+ border-left-color: #cccccc;
+ border-style: solid;
+ border-width: 5px 0 5px 5px;
+ content: " ";
+}
+
+.dropdown-submenu:hover > a:after {
+ border-left-color: #ffffff;
+}
+
+.dropdown-submenu.pull-left {
+ float: none;
+}
+
+.dropdown-submenu.pull-left > .dropdown-menu {
+ left: -100%;
+ margin-left: 10px;
+ -webkit-border-radius: 6px 0 6px 6px;
+ -moz-border-radius: 6px 0 6px 6px;
+ border-radius: 6px 0 6px 6px;
+}
+
+.dropdown .dropdown-menu .nav-header {
+ padding-right: 20px;
+ padding-left: 20px;
+}
+
+.typeahead {
+ z-index: 1051;
+ margin-top: 2px;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.well {
+ min-height: 20px;
+ padding: 19px;
+ margin-bottom: 20px;
+ background-color: #eeeeee;
+ border: 1px solid #dcdcdc;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+
+.well blockquote {
+ border-color: #ddd;
+ border-color: rgba(0, 0, 0, 0.15);
+}
+
+.well-large {
+ padding: 24px;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.well-small {
+ padding: 9px;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.fade {
+ opacity: 0;
+ -webkit-transition: opacity 0.15s linear;
+ -moz-transition: opacity 0.15s linear;
+ -o-transition: opacity 0.15s linear;
+ transition: opacity 0.15s linear;
+}
+
+.fade.in {
+ opacity: 1;
+}
+
+.collapse {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ -webkit-transition: height 0.35s ease;
+ -moz-transition: height 0.35s ease;
+ -o-transition: height 0.35s ease;
+ transition: height 0.35s ease;
+}
+
+.collapse.in {
+ height: auto;
+}
+
+.close {
+ float: right;
+ font-size: 20px;
+ font-weight: bold;
+ line-height: 20px;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+}
+
+.close:hover,
+.close:focus {
+ color: #000000;
+ text-decoration: none;
+ cursor: pointer;
+ opacity: 0.4;
+ filter: alpha(opacity=40);
+}
+
+button.close {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+}
+
+.btn {
+ display: inline-block;
+ *display: inline;
+ padding: 4px 12px;
+ margin-bottom: 0;
+ *margin-left: .3em;
+ font-size: 14px;
+ line-height: 20px;
+ color: #999999;
+ text-align: center;
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
+ vertical-align: middle;
+ cursor: pointer;
+ background-color: #dfdfdf;
+ *background-color: #c8c8c8;
+ background-image: -moz-linear-gradient(top, #eeeeee, #c8c8c8);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#eeeeee), to(#c8c8c8));
+ background-image: -webkit-linear-gradient(top, #eeeeee, #c8c8c8);
+ background-image: -o-linear-gradient(top, #eeeeee, #c8c8c8);
+ background-image: linear-gradient(to bottom, #eeeeee, #c8c8c8);
+ background-repeat: repeat-x;
+ border: 1px solid #bbbbbb;
+ *border: 0;
+ border-color: #c8c8c8 #c8c8c8 #a2a2a2;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ border-bottom-color: #a2a2a2;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffeeeeee', endColorstr='#ffc8c8c8', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ *zoom: 1;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn:hover,
+.btn:focus,
+.btn:active,
+.btn.active,
+.btn.disabled,
+.btn[disabled] {
+ color: #999999;
+ background-color: #c8c8c8;
+ *background-color: #bbbbbb;
+}
+
+.btn:active,
+.btn.active {
+ background-color: #aeaeae \9;
+}
+
+.btn:first-child {
+ *margin-left: 0;
+}
+
+.btn:hover,
+.btn:focus {
+ color: #999999;
+ text-decoration: none;
+ background-position: 0 -15px;
+ -webkit-transition: background-position 0.1s linear;
+ -moz-transition: background-position 0.1s linear;
+ -o-transition: background-position 0.1s linear;
+ transition: background-position 0.1s linear;
+}
+
+.btn:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+.btn.active,
+.btn:active {
+ background-image: none;
+ outline: 0;
+ -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn.disabled,
+.btn[disabled] {
+ cursor: default;
+ background-image: none;
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.btn-large {
+ padding: 22px 30px;
+ font-size: 17.5px;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-large [class^="icon-"],
+.btn-large [class*=" icon-"] {
+ margin-top: 4px;
+}
+
+.btn-small {
+ padding: 2px 10px;
+ font-size: 11.9px;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-small [class^="icon-"],
+.btn-small [class*=" icon-"] {
+ margin-top: 0;
+}
+
+.btn-mini [class^="icon-"],
+.btn-mini [class*=" icon-"] {
+ margin-top: -1px;
+}
+
+.btn-mini {
+ padding: 2px 6px;
+ font-size: 10.5px;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-block {
+ display: block;
+ width: 100%;
+ padding-right: 0;
+ padding-left: 0;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.btn-block + .btn-block {
+ margin-top: 5px;
+}
+
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
+}
+
+.btn-primary.active,
+.btn-warning.active,
+.btn-danger.active,
+.btn-success.active,
+.btn-info.active,
+.btn-inverse.active {
+ color: rgba(255, 255, 255, 0.75);
+}
+
+.btn-primary {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #0f82f5;
+ *background-color: #0072e6;
+ background-image: -moz-linear-gradient(top, #1a8cff, #0072e6);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1a8cff), to(#0072e6));
+ background-image: -webkit-linear-gradient(top, #1a8cff, #0072e6);
+ background-image: -o-linear-gradient(top, #1a8cff, #0072e6);
+ background-image: linear-gradient(to bottom, #1a8cff, #0072e6);
+ background-repeat: repeat-x;
+ border-color: #0072e6 #0072e6 #004c99;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1a8cff', endColorstr='#ff0072e6', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary:active,
+.btn-primary.active,
+.btn-primary.disabled,
+.btn-primary[disabled] {
+ color: #ffffff;
+ background-color: #0072e6;
+ *background-color: #0066cc;
+}
+
+.btn-primary:active,
+.btn-primary.active {
+ background-color: #0059b3 \9;
+}
+
+.btn-warning {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #fe781e;
+ *background-color: #fe6600;
+ background-image: -moz-linear-gradient(top, #ff8432, #fe6600);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ff8432), to(#fe6600));
+ background-image: -webkit-linear-gradient(top, #ff8432, #fe6600);
+ background-image: -o-linear-gradient(top, #ff8432, #fe6600);
+ background-image: linear-gradient(to bottom, #ff8432, #fe6600);
+ background-repeat: repeat-x;
+ border-color: #fe6600 #fe6600 #b14700;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff8432', endColorstr='#fffe6600', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-warning:hover,
+.btn-warning:focus,
+.btn-warning:active,
+.btn-warning.active,
+.btn-warning.disabled,
+.btn-warning[disabled] {
+ color: #ffffff;
+ background-color: #fe6600;
+ *background-color: #e45c00;
+}
+
+.btn-warning:active,
+.btn-warning.active {
+ background-color: #cb5200 \9;
+}
+
+.btn-danger {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #f50f43;
+ *background-color: #e60033;
+ background-image: -moz-linear-gradient(top, #ff1a4d, #e60033);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ff1a4d), to(#e60033));
+ background-image: -webkit-linear-gradient(top, #ff1a4d, #e60033);
+ background-image: -o-linear-gradient(top, #ff1a4d, #e60033);
+ background-image: linear-gradient(to bottom, #ff1a4d, #e60033);
+ background-repeat: repeat-x;
+ border-color: #e60033 #e60033 #990022;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff1a4d', endColorstr='#ffe60033', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-danger:hover,
+.btn-danger:focus,
+.btn-danger:active,
+.btn-danger.active,
+.btn-danger.disabled,
+.btn-danger[disabled] {
+ color: #ffffff;
+ background-color: #e60033;
+ *background-color: #cc002e;
+}
+
+.btn-danger:active,
+.btn-danger.active {
+ background-color: #b30028 \9;
+}
+
+.btn-success {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #41bb19;
+ *background-color: #379f15;
+ background-image: -moz-linear-gradient(top, #47cd1b, #379f15);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#47cd1b), to(#379f15));
+ background-image: -webkit-linear-gradient(top, #47cd1b, #379f15);
+ background-image: -o-linear-gradient(top, #47cd1b, #379f15);
+ background-image: linear-gradient(to bottom, #47cd1b, #379f15);
+ background-repeat: repeat-x;
+ border-color: #379f15 #379f15 #205c0c;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff47cd1b', endColorstr='#ff379f15', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-success:hover,
+.btn-success:focus,
+.btn-success:active,
+.btn-success.active,
+.btn-success.disabled,
+.btn-success[disabled] {
+ color: #ffffff;
+ background-color: #379f15;
+ *background-color: #2f8912;
+}
+
+.btn-success:active,
+.btn-success.active {
+ background-color: #28720f \9;
+}
+
+.btn-info {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #9b59bb;
+ *background-color: #8d46b0;
+ background-image: -moz-linear-gradient(top, #a466c2, #8d46b0);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#a466c2), to(#8d46b0));
+ background-image: -webkit-linear-gradient(top, #a466c2, #8d46b0);
+ background-image: -o-linear-gradient(top, #a466c2, #8d46b0);
+ background-image: linear-gradient(to bottom, #a466c2, #8d46b0);
+ background-repeat: repeat-x;
+ border-color: #8d46b0 #8d46b0 #613079;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffa466c2', endColorstr='#ff8d46b0', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-info:hover,
+.btn-info:focus,
+.btn-info:active,
+.btn-info.active,
+.btn-info.disabled,
+.btn-info[disabled] {
+ color: #ffffff;
+ background-color: #8d46b0;
+ *background-color: #7e3f9d;
+}
+
+.btn-info:active,
+.btn-info.active {
+ background-color: #6f378b \9;
+}
+
+.btn-inverse {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #080808;
+ *background-color: #000000;
+ background-image: -moz-linear-gradient(top, #0d0d0d, #000000);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0d0d0d), to(#000000));
+ background-image: -webkit-linear-gradient(top, #0d0d0d, #000000);
+ background-image: -o-linear-gradient(top, #0d0d0d, #000000);
+ background-image: linear-gradient(to bottom, #0d0d0d, #000000);
+ background-repeat: repeat-x;
+ border-color: #000000 #000000 #000000;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0d0d0d', endColorstr='#ff000000', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-inverse:hover,
+.btn-inverse:focus,
+.btn-inverse:active,
+.btn-inverse.active,
+.btn-inverse.disabled,
+.btn-inverse[disabled] {
+ color: #ffffff;
+ background-color: #000000;
+ *background-color: #000000;
+}
+
+.btn-inverse:active,
+.btn-inverse.active {
+ background-color: #000000 \9;
+}
+
+button.btn,
+input[type="submit"].btn {
+ *padding-top: 3px;
+ *padding-bottom: 3px;
+}
+
+button.btn::-moz-focus-inner,
+input[type="submit"].btn::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+
+button.btn.btn-large,
+input[type="submit"].btn.btn-large {
+ *padding-top: 7px;
+ *padding-bottom: 7px;
+}
+
+button.btn.btn-small,
+input[type="submit"].btn.btn-small {
+ *padding-top: 3px;
+ *padding-bottom: 3px;
+}
+
+button.btn.btn-mini,
+input[type="submit"].btn.btn-mini {
+ *padding-top: 1px;
+ *padding-bottom: 1px;
+}
+
+.btn-link,
+.btn-link:active,
+.btn-link[disabled] {
+ background-color: transparent;
+ background-image: none;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.btn-link {
+ color: #007fff;
+ cursor: pointer;
+ border-color: transparent;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-link:hover,
+.btn-link:focus {
+ color: #0066cc;
+ text-decoration: underline;
+ background-color: transparent;
+}
+
+.btn-link[disabled]:hover,
+.btn-link[disabled]:focus {
+ color: #999999;
+ text-decoration: none;
+}
+
+.btn-group {
+ position: relative;
+ display: inline-block;
+ *display: inline;
+ *margin-left: .3em;
+ font-size: 0;
+ white-space: nowrap;
+ vertical-align: middle;
+ *zoom: 1;
+}
+
+.btn-group:first-child {
+ *margin-left: 0;
+}
+
+.btn-group + .btn-group {
+ margin-left: 5px;
+}
+
+.btn-toolbar {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ font-size: 0;
+}
+
+.btn-toolbar > .btn + .btn,
+.btn-toolbar > .btn-group + .btn,
+.btn-toolbar > .btn + .btn-group {
+ margin-left: 5px;
+}
+
+.btn-group > .btn {
+ position: relative;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-group > .btn + .btn {
+ margin-left: -1px;
+}
+
+.btn-group > .btn,
+.btn-group > .dropdown-menu,
+.btn-group > .popover {
+ font-size: 14px;
+}
+
+.btn-group > .btn-mini {
+ font-size: 10.5px;
+}
+
+.btn-group > .btn-small {
+ font-size: 11.9px;
+}
+
+.btn-group > .btn-large {
+ font-size: 17.5px;
+}
+
+.btn-group > .btn:first-child {
+ margin-left: 0;
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -webkit-border-top-left-radius: 0;
+ border-top-left-radius: 0;
+ -moz-border-radius-bottomleft: 0;
+ -moz-border-radius-topleft: 0;
+}
+
+.btn-group > .btn:last-child,
+.btn-group > .dropdown-toggle {
+ -webkit-border-top-right-radius: 0;
+ border-top-right-radius: 0;
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-topright: 0;
+ -moz-border-radius-bottomright: 0;
+}
+
+.btn-group > .btn.large:first-child {
+ margin-left: 0;
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -webkit-border-top-left-radius: 0;
+ border-top-left-radius: 0;
+ -moz-border-radius-bottomleft: 0;
+ -moz-border-radius-topleft: 0;
+}
+
+.btn-group > .btn.large:last-child,
+.btn-group > .large.dropdown-toggle {
+ -webkit-border-top-right-radius: 0;
+ border-top-right-radius: 0;
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-topright: 0;
+ -moz-border-radius-bottomright: 0;
+}
+
+.btn-group > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group > .btn:active,
+.btn-group > .btn.active {
+ z-index: 2;
+}
+
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0;
+}
+
+.btn-group > .btn + .dropdown-toggle {
+ *padding-top: 5px;
+ padding-right: 8px;
+ *padding-bottom: 5px;
+ padding-left: 8px;
+ -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-group > .btn-mini + .dropdown-toggle {
+ *padding-top: 2px;
+ padding-right: 5px;
+ *padding-bottom: 2px;
+ padding-left: 5px;
+}
+
+.btn-group > .btn-small + .dropdown-toggle {
+ *padding-top: 5px;
+ *padding-bottom: 4px;
+}
+
+.btn-group > .btn-large + .dropdown-toggle {
+ *padding-top: 7px;
+ padding-right: 12px;
+ *padding-bottom: 7px;
+ padding-left: 12px;
+}
+
+.btn-group.open .dropdown-toggle {
+ background-image: none;
+ -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-group.open .btn.dropdown-toggle {
+ background-color: #c8c8c8;
+}
+
+.btn-group.open .btn-primary.dropdown-toggle {
+ background-color: #0072e6;
+}
+
+.btn-group.open .btn-warning.dropdown-toggle {
+ background-color: #fe6600;
+}
+
+.btn-group.open .btn-danger.dropdown-toggle {
+ background-color: #e60033;
+}
+
+.btn-group.open .btn-success.dropdown-toggle {
+ background-color: #379f15;
+}
+
+.btn-group.open .btn-info.dropdown-toggle {
+ background-color: #8d46b0;
+}
+
+.btn-group.open .btn-inverse.dropdown-toggle {
+ background-color: #000000;
+}
+
+.btn .caret {
+ margin-top: 8px;
+ margin-left: 0;
+}
+
+.btn-large .caret {
+ margin-top: 6px;
+}
+
+.btn-large .caret {
+ border-top-width: 5px;
+ border-right-width: 5px;
+ border-left-width: 5px;
+}
+
+.btn-mini .caret,
+.btn-small .caret {
+ margin-top: 8px;
+}
+
+.dropup .btn-large .caret {
+ border-bottom-width: 5px;
+}
+
+.btn-primary .caret,
+.btn-warning .caret,
+.btn-danger .caret,
+.btn-info .caret,
+.btn-success .caret,
+.btn-inverse .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.btn-group-vertical {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+}
+
+.btn-group-vertical > .btn {
+ display: block;
+ float: none;
+ max-width: 100%;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-group-vertical > .btn + .btn {
+ margin-top: -1px;
+ margin-left: 0;
+}
+
+.btn-group-vertical > .btn:first-child {
+ -webkit-border-radius: 0 0px 0 0;
+ -moz-border-radius: 0 0px 0 0;
+ border-radius: 0 0px 0 0;
+}
+
+.btn-group-vertical > .btn:last-child {
+ -webkit-border-radius: 0 0 0 0px;
+ -moz-border-radius: 0 0 0 0px;
+ border-radius: 0 0 0 0px;
+}
+
+.btn-group-vertical > .btn-large:first-child {
+ -webkit-border-radius: 0 0px 0 0;
+ -moz-border-radius: 0 0px 0 0;
+ border-radius: 0 0px 0 0;
+}
+
+.btn-group-vertical > .btn-large:last-child {
+ -webkit-border-radius: 0 0 0 0px;
+ -moz-border-radius: 0 0 0 0px;
+ border-radius: 0 0 0 0px;
+}
+
+.alert {
+ padding: 8px 35px 8px 14px;
+ margin-bottom: 20px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ background-color: #ff7518;
+ border: 1px solid transparent;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.alert,
+.alert h4 {
+ color: #ffffff;
+}
+
+.alert h4 {
+ margin: 0;
+}
+
+.alert .close {
+ position: relative;
+ top: -2px;
+ right: -21px;
+ line-height: 20px;
+}
+
+.alert-success {
+ color: #ffffff;
+ background-color: #3fb618;
+ border-color: transparent;
+}
+
+.alert-success h4 {
+ color: #ffffff;
+}
+
+.alert-danger,
+.alert-error {
+ color: #ffffff;
+ background-color: #ff0039;
+ border-color: transparent;
+}
+
+.alert-danger h4,
+.alert-error h4 {
+ color: #ffffff;
+}
+
+.alert-info {
+ color: #ffffff;
+ background-color: #9954bb;
+ border-color: transparent;
+}
+
+.alert-info h4 {
+ color: #ffffff;
+}
+
+.alert-block {
+ padding-top: 14px;
+ padding-bottom: 14px;
+}
+
+.alert-block > p,
+.alert-block > ul {
+ margin-bottom: 0;
+}
+
+.alert-block p + p {
+ margin-top: 5px;
+}
+
+.nav {
+ margin-bottom: 20px;
+ margin-left: 0;
+ list-style: none;
+}
+
+.nav > li > a {
+ display: block;
+}
+
+.nav > li > a:hover,
+.nav > li > a:focus {
+ text-decoration: none;
+ background-color: #eeeeee;
+}
+
+.nav > li > a > img {
+ max-width: none;
+}
+
+.nav > .pull-right {
+ float: right;
+}
+
+.nav-header {
+ display: block;
+ padding: 3px 15px;
+ font-size: 11px;
+ font-weight: bold;
+ line-height: 20px;
+ color: #dfdfdf;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ text-transform: uppercase;
+}
+
+.nav li + .nav-header {
+ margin-top: 9px;
+}
+
+.nav-list {
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-bottom: 0;
+}
+
+.nav-list > li > a,
+.nav-list .nav-header {
+ margin-right: -15px;
+ margin-left: -15px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+}
+
+.nav-list > li > a {
+ padding: 3px 15px;
+}
+
+.nav-list > .active > a,
+.nav-list > .active > a:hover,
+.nav-list > .active > a:focus {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
+ background-color: #007fff;
+}
+
+.nav-list [class^="icon-"],
+.nav-list [class*=" icon-"] {
+ margin-right: 2px;
+}
+
+.nav-list .divider {
+ *width: 100%;
+ height: 1px;
+ margin: 9px 1px;
+ *margin: -5px 0 5px;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ border-bottom: 1px solid #ffffff;
+}
+
+.nav-tabs,
+.nav-pills {
+ *zoom: 1;
+}
+
+.nav-tabs:before,
+.nav-pills:before,
+.nav-tabs:after,
+.nav-pills:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.nav-tabs:after,
+.nav-pills:after {
+ clear: both;
+}
+
+.nav-tabs > li,
+.nav-pills > li {
+ float: left;
+}
+
+.nav-tabs > li > a,
+.nav-pills > li > a {
+ padding-right: 12px;
+ padding-left: 12px;
+ margin-right: 2px;
+ line-height: 14px;
+}
+
+.nav-tabs {
+ border-bottom: 1px solid #ddd;
+}
+
+.nav-tabs > li {
+ margin-bottom: -1px;
+}
+
+.nav-tabs > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ line-height: 20px;
+ border: 1px solid transparent;
+ -webkit-border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+
+.nav-tabs > li > a:hover,
+.nav-tabs > li > a:focus {
+ border-color: #eeeeee #eeeeee #dddddd;
+}
+
+.nav-tabs > .active > a,
+.nav-tabs > .active > a:hover,
+.nav-tabs > .active > a:focus {
+ color: #bbbbbb;
+ cursor: default;
+ background-color: #ffffff;
+ border: 1px solid #ddd;
+ border-bottom-color: transparent;
+}
+
+.nav-pills > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ margin-top: 2px;
+ margin-bottom: 2px;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+.nav-pills > .active > a,
+.nav-pills > .active > a:hover,
+.nav-pills > .active > a:focus {
+ color: #ffffff;
+ background-color: #007fff;
+}
+
+.nav-stacked > li {
+ float: none;
+}
+
+.nav-stacked > li > a {
+ margin-right: 0;
+}
+
+.nav-tabs.nav-stacked {
+ border-bottom: 0;
+}
+
+.nav-tabs.nav-stacked > li > a {
+ border: 1px solid #ddd;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.nav-tabs.nav-stacked > li:first-child > a {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.nav-tabs.nav-stacked > li:last-child > a {
+ -webkit-border-bottom-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -moz-border-radius-bottomleft: 4px;
+}
+
+.nav-tabs.nav-stacked > li > a:hover,
+.nav-tabs.nav-stacked > li > a:focus {
+ z-index: 2;
+ border-color: #ddd;
+}
+
+.nav-pills.nav-stacked > li > a {
+ margin-bottom: 3px;
+}
+
+.nav-pills.nav-stacked > li:last-child > a {
+ margin-bottom: 1px;
+}
+
+.nav-tabs .dropdown-menu {
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+}
+
+.nav-pills .dropdown-menu {
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.nav .dropdown-toggle .caret {
+ margin-top: 6px;
+ border-top-color: #007fff;
+ border-bottom-color: #007fff;
+}
+
+.nav .dropdown-toggle:hover .caret,
+.nav .dropdown-toggle:focus .caret {
+ border-top-color: #0066cc;
+ border-bottom-color: #0066cc;
+}
+
+/* move down carets for tabs */
+
+.nav-tabs .dropdown-toggle .caret {
+ margin-top: 8px;
+}
+
+.nav .active .dropdown-toggle .caret {
+ border-top-color: #fff;
+ border-bottom-color: #fff;
+}
+
+.nav-tabs .active .dropdown-toggle .caret {
+ border-top-color: #bbbbbb;
+ border-bottom-color: #bbbbbb;
+}
+
+.nav > .dropdown.active > a:hover,
+.nav > .dropdown.active > a:focus {
+ cursor: pointer;
+}
+
+.nav-tabs .open .dropdown-toggle,
+.nav-pills .open .dropdown-toggle,
+.nav > li.dropdown.open.active > a:hover,
+.nav > li.dropdown.open.active > a:focus {
+ color: #ffffff;
+ background-color: #dfdfdf;
+ border-color: #dfdfdf;
+}
+
+.nav li.dropdown.open .caret,
+.nav li.dropdown.open.active .caret,
+.nav li.dropdown.open a:hover .caret,
+.nav li.dropdown.open a:focus .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+ opacity: 1;
+ filter: alpha(opacity=100);
+}
+
+.tabs-stacked .open > a:hover,
+.tabs-stacked .open > a:focus {
+ border-color: #dfdfdf;
+}
+
+.tabbable {
+ *zoom: 1;
+}
+
+.tabbable:before,
+.tabbable:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.tabbable:after {
+ clear: both;
+}
+
+.tab-content {
+ overflow: auto;
+}
+
+.tabs-below > .nav-tabs,
+.tabs-right > .nav-tabs,
+.tabs-left > .nav-tabs {
+ border-bottom: 0;
+}
+
+.tab-content > .tab-pane,
+.pill-content > .pill-pane {
+ display: none;
+}
+
+.tab-content > .active,
+.pill-content > .active {
+ display: block;
+}
+
+.tabs-below > .nav-tabs {
+ border-top: 1px solid #ddd;
+}
+
+.tabs-below > .nav-tabs > li {
+ margin-top: -1px;
+ margin-bottom: 0;
+}
+
+.tabs-below > .nav-tabs > li > a {
+ -webkit-border-radius: 0 0 4px 4px;
+ -moz-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+}
+
+.tabs-below > .nav-tabs > li > a:hover,
+.tabs-below > .nav-tabs > li > a:focus {
+ border-top-color: #ddd;
+ border-bottom-color: transparent;
+}
+
+.tabs-below > .nav-tabs > .active > a,
+.tabs-below > .nav-tabs > .active > a:hover,
+.tabs-below > .nav-tabs > .active > a:focus {
+ border-color: transparent #ddd #ddd #ddd;
+}
+
+.tabs-left > .nav-tabs > li,
+.tabs-right > .nav-tabs > li {
+ float: none;
+}
+
+.tabs-left > .nav-tabs > li > a,
+.tabs-right > .nav-tabs > li > a {
+ min-width: 74px;
+ margin-right: 0;
+ margin-bottom: 3px;
+}
+
+.tabs-left > .nav-tabs {
+ float: left;
+ margin-right: 19px;
+ border-right: 1px solid #ddd;
+}
+
+.tabs-left > .nav-tabs > li > a {
+ margin-right: -1px;
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
+}
+
+.tabs-left > .nav-tabs > li > a:hover,
+.tabs-left > .nav-tabs > li > a:focus {
+ border-color: #eeeeee #dddddd #eeeeee #eeeeee;
+}
+
+.tabs-left > .nav-tabs .active > a,
+.tabs-left > .nav-tabs .active > a:hover,
+.tabs-left > .nav-tabs .active > a:focus {
+ border-color: #ddd transparent #ddd #ddd;
+ *border-right-color: #ffffff;
+}
+
+.tabs-right > .nav-tabs {
+ float: right;
+ margin-left: 19px;
+ border-left: 1px solid #ddd;
+}
+
+.tabs-right > .nav-tabs > li > a {
+ margin-left: -1px;
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+
+.tabs-right > .nav-tabs > li > a:hover,
+.tabs-right > .nav-tabs > li > a:focus {
+ border-color: #eeeeee #eeeeee #eeeeee #dddddd;
+}
+
+.tabs-right > .nav-tabs .active > a,
+.tabs-right > .nav-tabs .active > a:hover,
+.tabs-right > .nav-tabs .active > a:focus {
+ border-color: #ddd #ddd #ddd transparent;
+ *border-left-color: #ffffff;
+}
+
+.nav > .disabled > a {
+ color: #dfdfdf;
+}
+
+.nav > .disabled > a:hover,
+.nav > .disabled > a:focus {
+ text-decoration: none;
+ cursor: default;
+ background-color: transparent;
+}
+
+.navbar {
+ *position: relative;
+ *z-index: 2;
+ margin-bottom: 20px;
+ overflow: visible;
+}
+
+.navbar-inner {
+ min-height: 50px;
+ padding-right: 20px;
+ padding-left: 20px;
+ background-color: #080808;
+ background-image: -moz-linear-gradient(top, #080808, #080808);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#080808), to(#080808));
+ background-image: -webkit-linear-gradient(top, #080808, #080808);
+ background-image: -o-linear-gradient(top, #080808, #080808);
+ background-image: linear-gradient(to bottom, #080808, #080808);
+ background-repeat: repeat-x;
+ border: 1px solid transparent;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff080808', GradientType=0);
+ *zoom: 1;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+}
+
+.navbar-inner:before,
+.navbar-inner:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.navbar-inner:after {
+ clear: both;
+}
+
+.navbar .container {
+ width: auto;
+}
+
+.nav-collapse.collapse {
+ height: auto;
+ overflow: visible;
+}
+
+.navbar .brand {
+ display: block;
+ float: left;
+ padding: 15px 20px 15px;
+ margin-left: -20px;
+ font-size: 20px;
+ font-weight: 200;
+ color: #ffffff;
+ text-shadow: 0 1px 0 #080808;
+}
+
+.navbar .brand:hover,
+.navbar .brand:focus {
+ text-decoration: none;
+}
+
+.navbar-text {
+ margin-bottom: 0;
+ line-height: 50px;
+ color: #ffffff;
+}
+
+.navbar-link {
+ color: #ffffff;
+}
+
+.navbar-link:hover,
+.navbar-link:focus {
+ color: #bbbbbb;
+}
+
+.navbar .divider-vertical {
+ height: 50px;
+ margin: 0 9px;
+ border-right: 1px solid #080808;
+ border-left: 1px solid #080808;
+}
+
+.navbar .btn,
+.navbar .btn-group {
+ margin-top: 10px;
+}
+
+.navbar .btn-group .btn,
+.navbar .input-prepend .btn,
+.navbar .input-append .btn,
+.navbar .input-prepend .btn-group,
+.navbar .input-append .btn-group {
+ margin-top: 0;
+}
+
+.navbar-form {
+ margin-bottom: 0;
+ *zoom: 1;
+}
+
+.navbar-form:before,
+.navbar-form:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.navbar-form:after {
+ clear: both;
+}
+
+.navbar-form input,
+.navbar-form select,
+.navbar-form .radio,
+.navbar-form .checkbox {
+ margin-top: 10px;
+}
+
+.navbar-form input,
+.navbar-form select,
+.navbar-form .btn {
+ display: inline-block;
+ margin-bottom: 0;
+}
+
+.navbar-form input[type="image"],
+.navbar-form input[type="checkbox"],
+.navbar-form input[type="radio"] {
+ margin-top: 3px;
+}
+
+.navbar-form .input-append,
+.navbar-form .input-prepend {
+ margin-top: 5px;
+ white-space: nowrap;
+}
+
+.navbar-form .input-append input,
+.navbar-form .input-prepend input {
+ margin-top: 0;
+}
+
+.navbar-search {
+ position: relative;
+ float: left;
+ margin-top: 10px;
+ margin-bottom: 0;
+}
+
+.navbar-search .search-query {
+ padding: 4px 14px;
+ margin-bottom: 0;
+ font-family: "Open Sans", Calibri, Candara, Arial, sans-serif;
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 1;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+
+.navbar-static-top {
+ position: static;
+ margin-bottom: 0;
+}
+
+.navbar-static-top .navbar-inner {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+ position: fixed;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+ margin-bottom: 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-static-top .navbar-inner {
+ border-width: 0 0 1px;
+}
+
+.navbar-fixed-bottom .navbar-inner {
+ border-width: 1px 0 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-fixed-bottom .navbar-inner {
+ padding-right: 0;
+ padding-left: 0;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+ width: 940px;
+}
+
+.navbar-fixed-top {
+ top: 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-static-top .navbar-inner {
+ -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
+}
+
+.navbar-fixed-bottom {
+ bottom: 0;
+}
+
+.navbar-fixed-bottom .navbar-inner {
+ -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
+}
+
+.navbar .nav {
+ position: relative;
+ left: 0;
+ display: block;
+ float: left;
+ margin: 0 10px 0 0;
+}
+
+.navbar .nav.pull-right {
+ float: right;
+ margin-right: 0;
+}
+
+.navbar .nav > li {
+ float: left;
+}
+
+.navbar .nav > li > a {
+ float: none;
+ padding: 15px 15px 15px;
+ color: #ffffff;
+ text-decoration: none;
+ text-shadow: 0 1px 0 #080808;
+}
+
+.navbar .nav .dropdown-toggle .caret {
+ margin-top: 8px;
+}
+
+.navbar .nav > li > a:focus,
+.navbar .nav > li > a:hover {
+ color: #bbbbbb;
+ text-decoration: none;
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.navbar .nav > .active > a,
+.navbar .nav > .active > a:hover,
+.navbar .nav > .active > a:focus {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: transparent;
+ -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+ -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+}
+
+.navbar .btn-navbar {
+ display: none;
+ float: right;
+ padding: 7px 10px;
+ margin-right: 5px;
+ margin-left: 5px;
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #000000;
+ *background-color: #000000;
+ background-image: -moz-linear-gradient(top, #000000, #000000);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#000000), to(#000000));
+ background-image: -webkit-linear-gradient(top, #000000, #000000);
+ background-image: -o-linear-gradient(top, #000000, #000000);
+ background-image: linear-gradient(to bottom, #000000, #000000);
+ background-repeat: repeat-x;
+ border-color: #000000 #000000 #000000;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff000000', endColorstr='#ff000000', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+}
+
+.navbar .btn-navbar:hover,
+.navbar .btn-navbar:focus,
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active,
+.navbar .btn-navbar.disabled,
+.navbar .btn-navbar[disabled] {
+ color: #ffffff;
+ background-color: #000000;
+ *background-color: #000000;
+}
+
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active {
+ background-color: #000000 \9;
+}
+
+.navbar .btn-navbar .icon-bar {
+ display: block;
+ width: 18px;
+ height: 2px;
+ background-color: #f5f5f5;
+ -webkit-border-radius: 1px;
+ -moz-border-radius: 1px;
+ border-radius: 1px;
+ -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+ -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.btn-navbar .icon-bar + .icon-bar {
+ margin-top: 3px;
+}
+
+.navbar .nav > li > .dropdown-menu:before {
+ position: absolute;
+ top: -7px;
+ left: 9px;
+ display: inline-block;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid #ccc;
+ border-left: 7px solid transparent;
+ border-bottom-color: rgba(0, 0, 0, 0.2);
+ content: '';
+}
+
+.navbar .nav > li > .dropdown-menu:after {
+ position: absolute;
+ top: -6px;
+ left: 10px;
+ display: inline-block;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid #ffffff;
+ border-left: 6px solid transparent;
+ content: '';
+}
+
+.navbar-fixed-bottom .nav > li > .dropdown-menu:before {
+ top: auto;
+ bottom: -7px;
+ border-top: 7px solid #ccc;
+ border-bottom: 0;
+ border-top-color: rgba(0, 0, 0, 0.2);
+}
+
+.navbar-fixed-bottom .nav > li > .dropdown-menu:after {
+ top: auto;
+ bottom: -6px;
+ border-top: 6px solid #ffffff;
+ border-bottom: 0;
+}
+
+.navbar .nav li.dropdown > a:hover .caret,
+.navbar .nav li.dropdown > a:focus .caret {
+ border-top-color: #bbbbbb;
+ border-bottom-color: #bbbbbb;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle,
+.navbar .nav li.dropdown.active > .dropdown-toggle,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle {
+ color: #ffffff;
+ background-color: transparent;
+}
+
+.navbar .nav li.dropdown > .dropdown-toggle .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.navbar .pull-right > li > .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu:before,
+.navbar .nav > li > .dropdown-menu.pull-right:before {
+ right: 12px;
+ left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu:after,
+.navbar .nav > li > .dropdown-menu.pull-right:after {
+ right: 13px;
+ left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu {
+ right: 100%;
+ left: auto;
+ margin-right: -1px;
+ margin-left: 0;
+ -webkit-border-radius: 6px 0 6px 6px;
+ -moz-border-radius: 6px 0 6px 6px;
+ border-radius: 6px 0 6px 6px;
+}
+
+.navbar-inverse .navbar-inner {
+ background-color: #007fff;
+ background-image: -moz-linear-gradient(top, #007fff, #007fff);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#007fff), to(#007fff));
+ background-image: -webkit-linear-gradient(top, #007fff, #007fff);
+ background-image: -o-linear-gradient(top, #007fff, #007fff);
+ background-image: linear-gradient(to bottom, #007fff, #007fff);
+ background-repeat: repeat-x;
+ border-color: transparent;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff007fff', endColorstr='#ff007fff', GradientType=0);
+}
+
+.navbar-inverse .brand,
+.navbar-inverse .nav > li > a {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.navbar-inverse .brand:hover,
+.navbar-inverse .nav > li > a:hover,
+.navbar-inverse .brand:focus,
+.navbar-inverse .nav > li > a:focus {
+ color: #ffffff;
+}
+
+.navbar-inverse .brand {
+ color: #ffffff;
+}
+
+.navbar-inverse .navbar-text {
+ color: #ffffff;
+}
+
+.navbar-inverse .nav > li > a:focus,
+.navbar-inverse .nav > li > a:hover {
+ color: #ffffff;
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.navbar-inverse .nav .active > a,
+.navbar-inverse .nav .active > a:hover,
+.navbar-inverse .nav .active > a:focus {
+ color: #ffffff;
+ background-color: #007fff;
+}
+
+.navbar-inverse .navbar-link {
+ color: #ffffff;
+}
+
+.navbar-inverse .navbar-link:hover,
+.navbar-inverse .navbar-link:focus {
+ color: #ffffff;
+}
+
+.navbar-inverse .divider-vertical {
+ border-right-color: #007fff;
+ border-left-color: #007fff;
+}
+
+.navbar-inverse .nav li.dropdown.open > .dropdown-toggle,
+.navbar-inverse .nav li.dropdown.active > .dropdown-toggle,
+.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle {
+ color: #ffffff;
+ background-color: #007fff;
+}
+
+.navbar-inverse .nav li.dropdown > a:hover .caret,
+.navbar-inverse .nav li.dropdown > a:focus .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.navbar-inverse .navbar-search .search-query {
+ color: #ffffff;
+ background-color: #80bfff;
+ border-color: #007fff;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ -webkit-transition: none;
+ -moz-transition: none;
+ -o-transition: none;
+ transition: none;
+}
+
+.navbar-inverse .navbar-search .search-query:-moz-placeholder {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-search .search-query:-ms-input-placeholder {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-search .search-query:focus,
+.navbar-inverse .navbar-search .search-query.focused {
+ padding: 5px 15px;
+ color: #999999;
+ text-shadow: 0 1px 0 #ffffff;
+ background-color: #ffffff;
+ border: 0;
+ outline: 0;
+ -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+}
+
+.navbar-inverse .btn-navbar {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #0072e6;
+ *background-color: #0072e6;
+ background-image: -moz-linear-gradient(top, #0072e6, #0072e6);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0072e6), to(#0072e6));
+ background-image: -webkit-linear-gradient(top, #0072e6, #0072e6);
+ background-image: -o-linear-gradient(top, #0072e6, #0072e6);
+ background-image: linear-gradient(to bottom, #0072e6, #0072e6);
+ background-repeat: repeat-x;
+ border-color: #0072e6 #0072e6 #004c99;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0072e6', endColorstr='#ff0072e6', GradientType=0);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.navbar-inverse .btn-navbar:hover,
+.navbar-inverse .btn-navbar:focus,
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active,
+.navbar-inverse .btn-navbar.disabled,
+.navbar-inverse .btn-navbar[disabled] {
+ color: #ffffff;
+ background-color: #0072e6;
+ *background-color: #0066cc;
+}
+
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active {
+ background-color: #0059b3 \9;
+}
+
+.breadcrumb {
+ padding: 8px 15px;
+ margin: 0 0 20px;
+ list-style: none;
+ background-color: #f5f5f5;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.breadcrumb > li {
+ display: inline-block;
+ *display: inline;
+ text-shadow: 0 1px 0 #ffffff;
+ *zoom: 1;
+}
+
+.breadcrumb > li > .divider {
+ padding: 0 5px;
+ color: #ccc;
+}
+
+.breadcrumb > .active {
+ color: #dfdfdf;
+}
+
+.pagination {
+ margin: 20px 0;
+}
+
+.pagination ul {
+ display: inline-block;
+ *display: inline;
+ margin-bottom: 0;
+ margin-left: 0;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ *zoom: 1;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.pagination ul > li {
+ display: inline;
+}
+
+.pagination ul > li > a,
+.pagination ul > li > span {
+ float: left;
+ padding: 4px 12px;
+ line-height: 20px;
+ text-decoration: none;
+ background-color: #dfdfdf;
+ border: 1px solid transparent;
+ border-left-width: 0;
+}
+
+.pagination ul > li > a:hover,
+.pagination ul > li > a:focus,
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+ background-color: #007fff;
+}
+
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+ color: #dfdfdf;
+ cursor: default;
+}
+
+.pagination ul > .disabled > span,
+.pagination ul > .disabled > a,
+.pagination ul > .disabled > a:hover,
+.pagination ul > .disabled > a:focus {
+ color: #dfdfdf;
+ cursor: default;
+ background-color: transparent;
+}
+
+.pagination ul > li:first-child > a,
+.pagination ul > li:first-child > span {
+ border-left-width: 1px;
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -webkit-border-top-left-radius: 0;
+ border-top-left-radius: 0;
+ -moz-border-radius-bottomleft: 0;
+ -moz-border-radius-topleft: 0;
+}
+
+.pagination ul > li:last-child > a,
+.pagination ul > li:last-child > span {
+ -webkit-border-top-right-radius: 0;
+ border-top-right-radius: 0;
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-topright: 0;
+ -moz-border-radius-bottomright: 0;
+}
+
+.pagination-centered {
+ text-align: center;
+}
+
+.pagination-right {
+ text-align: right;
+}
+
+.pagination-large ul > li > a,
+.pagination-large ul > li > span {
+ padding: 22px 30px;
+ font-size: 17.5px;
+}
+
+.pagination-large ul > li:first-child > a,
+.pagination-large ul > li:first-child > span {
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -webkit-border-top-left-radius: 0;
+ border-top-left-radius: 0;
+ -moz-border-radius-bottomleft: 0;
+ -moz-border-radius-topleft: 0;
+}
+
+.pagination-large ul > li:last-child > a,
+.pagination-large ul > li:last-child > span {
+ -webkit-border-top-right-radius: 0;
+ border-top-right-radius: 0;
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-topright: 0;
+ -moz-border-radius-bottomright: 0;
+}
+
+.pagination-mini ul > li:first-child > a,
+.pagination-small ul > li:first-child > a,
+.pagination-mini ul > li:first-child > span,
+.pagination-small ul > li:first-child > span {
+ -webkit-border-bottom-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -webkit-border-top-left-radius: 0;
+ border-top-left-radius: 0;
+ -moz-border-radius-bottomleft: 0;
+ -moz-border-radius-topleft: 0;
+}
+
+.pagination-mini ul > li:last-child > a,
+.pagination-small ul > li:last-child > a,
+.pagination-mini ul > li:last-child > span,
+.pagination-small ul > li:last-child > span {
+ -webkit-border-top-right-radius: 0;
+ border-top-right-radius: 0;
+ -webkit-border-bottom-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -moz-border-radius-topright: 0;
+ -moz-border-radius-bottomright: 0;
+}
+
+.pagination-small ul > li > a,
+.pagination-small ul > li > span {
+ padding: 2px 10px;
+ font-size: 11.9px;
+}
+
+.pagination-mini ul > li > a,
+.pagination-mini ul > li > span {
+ padding: 2px 6px;
+ font-size: 10.5px;
+}
+
+.pager {
+ margin: 20px 0;
+ text-align: center;
+ list-style: none;
+ *zoom: 1;
+}
+
+.pager:before,
+.pager:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.pager:after {
+ clear: both;
+}
+
+.pager li {
+ display: inline;
+}
+
+.pager li > a,
+.pager li > span {
+ display: inline-block;
+ padding: 5px 14px;
+ background-color: #fff;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+
+.pager li > a:hover,
+.pager li > a:focus {
+ text-decoration: none;
+ background-color: #f5f5f5;
+}
+
+.pager .next > a,
+.pager .next > span {
+ float: right;
+}
+
+.pager .previous > a,
+.pager .previous > span {
+ float: left;
+}
+
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+ color: #dfdfdf;
+ cursor: default;
+ background-color: #fff;
+}
+
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+ background-color: #000000;
+}
+
+.modal-backdrop.fade {
+ opacity: 0;
+}
+
+.modal-backdrop,
+.modal-backdrop.fade.in {
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+
+.modal {
+ position: fixed;
+ top: 10%;
+ left: 50%;
+ z-index: 1050;
+ width: 560px;
+ margin-left: -280px;
+ background-color: #ffffff;
+ border: 1px solid #999;
+ border: 1px solid rgba(0, 0, 0, 0.3);
+ *border: 1px solid #999;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ outline: none;
+ -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding-box;
+ background-clip: padding-box;
+}
+
+.modal.fade {
+ top: -25%;
+ -webkit-transition: opacity 0.3s linear, top 0.3s ease-out;
+ -moz-transition: opacity 0.3s linear, top 0.3s ease-out;
+ -o-transition: opacity 0.3s linear, top 0.3s ease-out;
+ transition: opacity 0.3s linear, top 0.3s ease-out;
+}
+
+.modal.fade.in {
+ top: 10%;
+}
+
+.modal-header {
+ padding: 9px 15px;
+ border-bottom: 1px solid #eee;
+}
+
+.modal-header .close {
+ margin-top: 2px;
+}
+
+.modal-header h3 {
+ margin: 0;
+ line-height: 30px;
+}
+
+.modal-body {
+ position: relative;
+ max-height: 400px;
+ padding: 15px;
+ overflow-y: auto;
+}
+
+.modal-form {
+ margin-bottom: 0;
+}
+
+.modal-footer {
+ padding: 14px 15px 15px;
+ margin-bottom: 0;
+ text-align: right;
+ background-color: #f5f5f5;
+ border-top: 1px solid #ddd;
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+ *zoom: 1;
+ -webkit-box-shadow: inset 0 1px 0 #ffffff;
+ -moz-box-shadow: inset 0 1px 0 #ffffff;
+ box-shadow: inset 0 1px 0 #ffffff;
+}
+
+.modal-footer:before,
+.modal-footer:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.modal-footer:after {
+ clear: both;
+}
+
+.modal-footer .btn + .btn {
+ margin-bottom: 0;
+ margin-left: 5px;
+}
+
+.modal-footer .btn-group .btn + .btn {
+ margin-left: -1px;
+}
+
+.modal-footer .btn-block + .btn-block {
+ margin-left: 0;
+}
+
+.tooltip {
+ position: absolute;
+ z-index: 1030;
+ display: block;
+ font-size: 11px;
+ line-height: 1.4;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ visibility: visible;
+}
+
+.tooltip.in {
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+
+.tooltip.top {
+ padding: 5px 0;
+ margin-top: -3px;
+}
+
+.tooltip.right {
+ padding: 0 5px;
+ margin-left: 3px;
+}
+
+.tooltip.bottom {
+ padding: 5px 0;
+ margin-top: 3px;
+}
+
+.tooltip.left {
+ padding: 0 5px;
+ margin-left: -3px;
+}
+
+.tooltip-inner {
+ max-width: 200px;
+ padding: 8px;
+ color: #ffffff;
+ text-align: center;
+ text-decoration: none;
+ background-color: #000000;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.tooltip-arrow {
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+
+.tooltip.top .tooltip-arrow {
+ bottom: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-top-color: #000000;
+ border-width: 5px 5px 0;
+}
+
+.tooltip.right .tooltip-arrow {
+ top: 50%;
+ left: 0;
+ margin-top: -5px;
+ border-right-color: #000000;
+ border-width: 5px 5px 5px 0;
+}
+
+.tooltip.left .tooltip-arrow {
+ top: 50%;
+ right: 0;
+ margin-top: -5px;
+ border-left-color: #000000;
+ border-width: 5px 0 5px 5px;
+}
+
+.tooltip.bottom .tooltip-arrow {
+ top: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-bottom-color: #000000;
+ border-width: 0 5px 5px;
+}
+
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1010;
+ display: none;
+ max-width: 276px;
+ padding: 1px;
+ text-align: left;
+ white-space: normal;
+ background-color: #ff7518;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+
+.popover.top {
+ margin-top: -10px;
+}
+
+.popover.right {
+ margin-left: 10px;
+}
+
+.popover.bottom {
+ margin-top: 10px;
+}
+
+.popover.left {
+ margin-left: -10px;
+}
+
+.popover-title {
+ padding: 8px 14px;
+ margin: 0;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 18px;
+ background-color: #ff7518;
+ border-bottom: 1px solid #fe6600;
+ -webkit-border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+ border-radius: 5px 5px 0 0;
+}
+
+.popover-title:empty {
+ display: none;
+}
+
+.popover-content {
+ padding: 9px 14px;
+}
+
+.popover .arrow,
+.popover .arrow:after {
+ position: absolute;
+ display: block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+
+.popover .arrow {
+ border-width: 16px;
+}
+
+.popover .arrow:after {
+ border-width: 15px;
+ content: "";
+}
+
+.popover.top .arrow {
+ bottom: -16px;
+ left: 50%;
+ margin-left: -16px;
+ border-top-color: #999;
+ border-top-color: transparent;
+ border-bottom-width: 0;
+}
+
+.popover.top .arrow:after {
+ bottom: 1px;
+ margin-left: -15px;
+ border-top-color: #ff7518;
+ border-bottom-width: 0;
+}
+
+.popover.right .arrow {
+ top: 50%;
+ left: -16px;
+ margin-top: -16px;
+ border-right-color: #999;
+ border-right-color: transparent;
+ border-left-width: 0;
+}
+
+.popover.right .arrow:after {
+ bottom: -15px;
+ left: 1px;
+ border-right-color: #ff7518;
+ border-left-width: 0;
+}
+
+.popover.bottom .arrow {
+ top: -16px;
+ left: 50%;
+ margin-left: -16px;
+ border-bottom-color: #999;
+ border-bottom-color: transparent;
+ border-top-width: 0;
+}
+
+.popover.bottom .arrow:after {
+ top: 1px;
+ margin-left: -15px;
+ border-bottom-color: #ff7518;
+ border-top-width: 0;
+}
+
+.popover.left .arrow {
+ top: 50%;
+ right: -16px;
+ margin-top: -16px;
+ border-left-color: #999;
+ border-left-color: transparent;
+ border-right-width: 0;
+}
+
+.popover.left .arrow:after {
+ right: 1px;
+ bottom: -15px;
+ border-left-color: #ff7518;
+ border-right-width: 0;
+}
+
+.thumbnails {
+ margin-left: -20px;
+ list-style: none;
+ *zoom: 1;
+}
+
+.thumbnails:before,
+.thumbnails:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.thumbnails:after {
+ clear: both;
+}
+
+.row-fluid .thumbnails {
+ margin-left: 0;
+}
+
+.thumbnails > li {
+ float: left;
+ margin-bottom: 20px;
+ margin-left: 20px;
+}
+
+.thumbnail {
+ display: block;
+ padding: 4px;
+ line-height: 20px;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ -webkit-transition: all 0.2s ease-in-out;
+ -moz-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+}
+
+a.thumbnail:hover,
+a.thumbnail:focus {
+ border-color: #007fff;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+ -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+ box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+}
+
+.thumbnail > img {
+ display: block;
+ max-width: 100%;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.thumbnail .caption {
+ padding: 9px;
+ color: #bbbbbb;
+}
+
+.media,
+.media-body {
+ overflow: hidden;
+ *overflow: visible;
+ zoom: 1;
+}
+
+.media,
+.media .media {
+ margin-top: 15px;
+}
+
+.media:first-child {
+ margin-top: 0;
+}
+
+.media-object {
+ display: block;
+}
+
+.media-heading {
+ margin: 0 0 5px;
+}
+
+.media > .pull-left {
+ margin-right: 10px;
+}
+
+.media > .pull-right {
+ margin-left: 10px;
+}
+
+.media-list {
+ margin-left: 0;
+ list-style: none;
+}
+
+.label,
+.badge {
+ display: inline-block;
+ padding: 2px 4px;
+ font-size: 11.844px;
+ font-weight: bold;
+ line-height: 14px;
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ white-space: nowrap;
+ vertical-align: baseline;
+ background-color: #dfdfdf;
+}
+
+.label {
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+.badge {
+ padding-right: 9px;
+ padding-left: 9px;
+ -webkit-border-radius: 9px;
+ -moz-border-radius: 9px;
+ border-radius: 9px;
+}
+
+.label:empty,
+.badge:empty {
+ display: none;
+}
+
+a.label:hover,
+a.label:focus,
+a.badge:hover,
+a.badge:focus {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.label-important,
+.badge-important {
+ background-color: #ffffff;
+}
+
+.label-important[href],
+.badge-important[href] {
+ background-color: #e6e6e6;
+}
+
+.label-warning,
+.badge-warning {
+ background-color: #ff7518;
+}
+
+.label-warning[href],
+.badge-warning[href] {
+ background-color: #e45c00;
+}
+
+.label-success,
+.badge-success {
+ background-color: #ffffff;
+}
+
+.label-success[href],
+.badge-success[href] {
+ background-color: #e6e6e6;
+}
+
+.label-info,
+.badge-info {
+ background-color: #ffffff;
+}
+
+.label-info[href],
+.badge-info[href] {
+ background-color: #e6e6e6;
+}
+
+.label-inverse,
+.badge-inverse {
+ background-color: #999999;
+}
+
+.label-inverse[href],
+.badge-inverse[href] {
+ background-color: #808080;
+}
+
+.btn .label,
+.btn .badge {
+ position: relative;
+ top: -1px;
+}
+
+.btn-mini .label,
+.btn-mini .badge {
+ top: 0;
+}
+
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-moz-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-ms-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-o-keyframes progress-bar-stripes {
+ from {
+ background-position: 0 0;
+ }
+ to {
+ background-position: 40px 0;
+ }
+}
+
+@keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+.progress {
+ height: 20px;
+ margin-bottom: 20px;
+ overflow: hidden;
+ background-color: #f7f7f7;
+ background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
+ background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
+ background-repeat: repeat-x;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+.progress .bar {
+ float: left;
+ width: 0;
+ height: 100%;
+ font-size: 12px;
+ color: #ffffff;
+ text-align: center;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #0e90d2;
+ background-image: -moz-linear-gradient(top, #149bdf, #0480be);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
+ background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
+ background-image: -o-linear-gradient(top, #149bdf, #0480be);
+ background-image: linear-gradient(to bottom, #149bdf, #0480be);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ -webkit-transition: width 0.6s ease;
+ -moz-transition: width 0.6s ease;
+ -o-transition: width 0.6s ease;
+ transition: width 0.6s ease;
+}
+
+.progress .bar + .bar {
+ -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+}
+
+.progress-striped .bar {
+ background-color: #149bdf;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ -webkit-background-size: 40px 40px;
+ -moz-background-size: 40px 40px;
+ -o-background-size: 40px 40px;
+ background-size: 40px 40px;
+}
+
+.progress.active .bar {
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ -moz-animation: progress-bar-stripes 2s linear infinite;
+ -ms-animation: progress-bar-stripes 2s linear infinite;
+ -o-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite;
+}
+
+.progress-danger .bar,
+.progress .bar-danger {
+ background-color: #dd514c;
+ background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));
+ background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);
+}
+
+.progress-danger.progress-striped .bar,
+.progress-striped .bar-danger {
+ background-color: #ee5f5b;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-success .bar,
+.progress .bar-success {
+ background-color: #5eb95e;
+ background-image: -moz-linear-gradient(top, #62c462, #57a957);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));
+ background-image: -webkit-linear-gradient(top, #62c462, #57a957);
+ background-image: -o-linear-gradient(top, #62c462, #57a957);
+ background-image: linear-gradient(to bottom, #62c462, #57a957);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);
+}
+
+.progress-success.progress-striped .bar,
+.progress-striped .bar-success {
+ background-color: #62c462;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-info .bar,
+.progress .bar-info {
+ background-color: #4bb1cf;
+ background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));
+ background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);
+}
+
+.progress-info.progress-striped .bar,
+.progress-striped .bar-info {
+ background-color: #5bc0de;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-warning .bar,
+.progress .bar-warning {
+ background-color: #ff9046;
+ background-image: -moz-linear-gradient(top, #ffa365, #ff7518);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffa365), to(#ff7518));
+ background-image: -webkit-linear-gradient(top, #ffa365, #ff7518);
+ background-image: -o-linear-gradient(top, #ffa365, #ff7518);
+ background-image: linear-gradient(to bottom, #ffa365, #ff7518);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffa365', endColorstr='#ffff7518', GradientType=0);
+}
+
+.progress-warning.progress-striped .bar,
+.progress-striped .bar-warning {
+ background-color: #ffa365;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.accordion {
+ margin-bottom: 20px;
+}
+
+.accordion-group {
+ margin-bottom: 2px;
+ border: 1px solid #e5e5e5;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.accordion-heading {
+ border-bottom: 0;
+}
+
+.accordion-heading .accordion-toggle {
+ display: block;
+ padding: 8px 15px;
+}
+
+.accordion-toggle {
+ cursor: pointer;
+}
+
+.accordion-inner {
+ padding: 9px 15px;
+ border-top: 1px solid #e5e5e5;
+}
+
+.carousel {
+ position: relative;
+ margin-bottom: 20px;
+ line-height: 1;
+}
+
+.carousel-inner {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+}
+
+.carousel-inner > .item {
+ position: relative;
+ display: none;
+ -webkit-transition: 0.6s ease-in-out left;
+ -moz-transition: 0.6s ease-in-out left;
+ -o-transition: 0.6s ease-in-out left;
+ transition: 0.6s ease-in-out left;
+}
+
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+ display: block;
+ line-height: 1;
+}
+
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ display: block;
+}
+
+.carousel-inner > .active {
+ left: 0;
+}
+
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ position: absolute;
+ top: 0;
+ width: 100%;
+}
+
+.carousel-inner > .next {
+ left: 100%;
+}
+
+.carousel-inner > .prev {
+ left: -100%;
+}
+
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
+ left: 0;
+}
+
+.carousel-inner > .active.left {
+ left: -100%;
+}
+
+.carousel-inner > .active.right {
+ left: 100%;
+}
+
+.carousel-control {
+ position: absolute;
+ top: 40%;
+ left: 15px;
+ width: 40px;
+ height: 40px;
+ margin-top: -20px;
+ font-size: 60px;
+ font-weight: 100;
+ line-height: 30px;
+ color: #ffffff;
+ text-align: center;
+ background: #080808;
+ border: 3px solid #ffffff;
+ -webkit-border-radius: 23px;
+ -moz-border-radius: 23px;
+ border-radius: 23px;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+
+.carousel-control.right {
+ right: 15px;
+ left: auto;
+}
+
+.carousel-control:hover,
+.carousel-control:focus {
+ color: #ffffff;
+ text-decoration: none;
+ opacity: 0.9;
+ filter: alpha(opacity=90);
+}
+
+.carousel-indicators {
+ position: absolute;
+ top: 15px;
+ right: 15px;
+ z-index: 5;
+ margin: 0;
+ list-style: none;
+}
+
+.carousel-indicators li {
+ display: block;
+ float: left;
+ width: 10px;
+ height: 10px;
+ margin-left: 5px;
+ text-indent: -999px;
+ background-color: #ccc;
+ background-color: rgba(255, 255, 255, 0.25);
+ border-radius: 5px;
+}
+
+.carousel-indicators .active {
+ background-color: #fff;
+}
+
+.carousel-caption {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 15px;
+ background: #999999;
+ background: rgba(0, 0, 0, 0.75);
+}
+
+.carousel-caption h4,
+.carousel-caption p {
+ line-height: 20px;
+ color: #ffffff;
+}
+
+.carousel-caption h4 {
+ margin: 0 0 5px;
+}
+
+.carousel-caption p {
+ margin-bottom: 0;
+}
+
+.hero-unit {
+ padding: 60px;
+ margin-bottom: 30px;
+ font-size: 18px;
+ font-weight: 200;
+ line-height: 30px;
+ color: inherit;
+ background-color: #eeeeee;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.hero-unit h1 {
+ margin-bottom: 0;
+ font-size: 60px;
+ line-height: 1;
+ letter-spacing: -1px;
+ color: inherit;
+}
+
+.hero-unit li {
+ line-height: 30px;
+}
+
+.pull-right {
+ float: right;
+}
+
+.pull-left {
+ float: left;
+}
+
+.hide {
+ display: none;
+}
+
+.show {
+ display: block;
+}
+
+.invisible {
+ visibility: hidden;
+}
+
+.affix {
+ position: fixed;
+}
+
+body {
+ font-weight: 300;
+}
+
+h1 {
+ font-size: 50px;
+}
+
+h2,
+h3 {
+ font-size: 26px;
+}
+
+h4 {
+ font-size: 14px;
+}
+
+h5,
+h6 {
+ font-size: 11px;
+}
+
+blockquote {
+ padding: 10px 15px;
+ background-color: #eeeeee;
+ border-left-color: #bbbbbb;
+}
+
+blockquote.pull-right {
+ padding: 10px 15px;
+ border-right-color: #bbbbbb;
+}
+
+blockquote small {
+ color: #bbbbbb;
+}
+
+.muted {
+ color: #bbbbbb;
+}
+
+.text-warning {
+ color: #ff7518;
+}
+
+a.text-warning:hover {
+ color: #e45c00;
+}
+
+.text-error {
+ color: #ff0039;
+}
+
+a.text-error:hover {
+ color: #cc002e;
+}
+
+.text-info {
+ color: #9954bb;
+}
+
+a.text-info:hover {
+ color: #7e3f9d;
+}
+
+.text-success {
+ color: #3fb618;
+}
+
+a.text-success:hover {
+ color: #2f8912;
+}
+
+.navbar .navbar-inner {
+ background-image: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.navbar .brand:hover {
+ color: #bbbbbb;
+}
+
+.navbar .nav > .active > a,
+.navbar .nav > .active > a:hover,
+.navbar .nav > .active > a:focus {
+ background-color: rgba(0, 0, 0, 0.05);
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle,
+.navbar .nav li.dropdown.active > .dropdown-toggle,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle {
+ color: #ffffff;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle:hover,
+.navbar .nav li.dropdown.active > .dropdown-toggle:hover,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle:hover {
+ color: #eeeeee;
+}
+
+.navbar .navbar-search .search-query {
+ line-height: normal;
+}
+
+.navbar-inverse .brand,
+.navbar-inverse .nav > li > a {
+ text-shadow: none;
+}
+
+.navbar-inverse .brand:hover,
+.navbar-inverse .nav > .active > a,
+.navbar-inverse .nav > .active > a:hover,
+.navbar-inverse .nav > .active > a:focus {
+ color: #ffffff;
+ background-color: rgba(0, 0, 0, 0.05);
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.navbar-inverse .navbar-search .search-query {
+ color: #080808;
+}
+
+div.subnav {
+ margin: 0 1px;
+ background: #dfdfdf none;
+ border: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+div.subnav .nav {
+ background-color: transparent;
+}
+
+div.subnav .nav > li > a {
+ border-color: transparent;
+}
+
+div.subnav .nav > .active > a,
+div.subnav .nav > .active > a:hover {
+ color: #ffffff;
+ background-color: #000000;
+ border-color: transparent;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+div.subnav-fixed {
+ top: 51px;
+ margin: 0;
+}
+
+.nav .open .dropdown-toggle,
+.nav > li.dropdown.open.active > a:hover {
+ color: #007fff;
+}
+
+.nav-tabs > li > a {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.nav-tabs.nav-stacked > li > a:hover {
+ color: #ffffff;
+ background-color: #007fff;
+}
+
+.nav-tabs.nav-stacked > .active > a,
+.nav-tabs.nav-stacked > .active > a:hover {
+ color: #bbbbbb;
+ background-color: #ffffff;
+}
+
+.nav-tabs.nav-stacked > li:first-child > a,
+.nav-tabs.nav-stacked > li:last-child > a {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.tabs-below > .nav-tabs > li > a,
+.tabs-left > .nav-tabs > li > a,
+.tabs-right > .nav-tabs > li > a {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.nav-pills > li > a {
+ color: #000000;
+ background-color: #dfdfdf;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.nav-pills > li > a:hover {
+ color: #ffffff;
+ background-color: #000000;
+}
+
+.nav-pills > .disabled > a,
+.nav-pills > .disabled > a:hover {
+ color: #999999;
+ background-color: #eeeeee;
+}
+
+.nav-list > li > a {
+ color: #080808;
+}
+
+.nav-list > li > a:hover {
+ color: #ffffff;
+ text-shadow: none;
+ background-color: #007fff;
+}
+
+.nav-list .nav-header {
+ color: #080808;
+}
+
+.nav-list .divider {
+ background-color: #bbbbbb;
+ border-bottom: none;
+}
+
+.pagination ul {
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.pagination ul > li > a,
+.pagination ul > li > span {
+ margin-right: 6px;
+ color: #080808;
+}
+
+.pagination ul > li > a:hover,
+.pagination ul > li > span:hover {
+ color: #ffffff;
+ background-color: #080808;
+}
+
+.pagination ul > li:last-child > a,
+.pagination ul > li:last-child > span {
+ margin-right: 0;
+}
+
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+ color: #ffffff;
+}
+
+.pagination ul > .disabled > span,
+.pagination ul > .disabled > a,
+.pagination ul > .disabled > a:hover {
+ color: #999999;
+ background-color: #eeeeee;
+}
+
+.pager li > a,
+.pager li > span {
+ color: #080808;
+ background-color: #dfdfdf;
+ border: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.pager li > a:hover,
+.pager li > span:hover {
+ color: #ffffff;
+ background-color: #080808;
+}
+
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > span {
+ color: #999999;
+ background-color: #eeeeee;
+}
+
+.breadcrumb {
+ background-color: #dfdfdf;
+}
+
+.breadcrumb li {
+ text-shadow: none;
+}
+
+.breadcrumb .divider,
+.breadcrumb .active {
+ color: #080808;
+ text-shadow: none;
+}
+
+.btn {
+ padding: 5px 12px;
+ text-shadow: none;
+ background-image: none;
+ border: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.btn.disabled {
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-large {
+ padding: 22px 30px;
+}
+
+.btn-small {
+ padding: 2px 10px;
+}
+
+.btn-mini {
+ padding: 2px 6px;
+}
+
+.btn-group > .btn:first-child,
+.btn-group > .btn:last-child,
+.btn-group > .dropdown-toggle {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-group > .btn + .dropdown-toggle {
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.table tbody tr.success td {
+ color: #ffffff;
+}
+
+.table tbody tr.error td {
+ color: #ffffff;
+}
+
+.table tbody tr.info td {
+ color: #ffffff;
+}
+
+.table-bordered {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.table-bordered thead:first-child tr:first-child th:first-child,
+.table-bordered tbody:first-child tr:first-child td:first-child {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.table-bordered thead:last-child tr:last-child th:first-child,
+.table-bordered tbody:last-child tr:last-child td:first-child,
+.table-bordered tfoot:last-child tr:last-child td:first-child {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+select,
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"] {
+ color: #080808;
+}
+
+.control-group.warning .control-label,
+.control-group.warning .help-block,
+.control-group.warning .help-inline {
+ color: #ff7518;
+}
+
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+ color: #080808;
+ border-color: #ff7518;
+}
+
+.control-group.error .control-label,
+.control-group.error .help-block,
+.control-group.error .help-inline {
+ color: #ff0039;
+}
+
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+ color: #080808;
+ border-color: #ff0039;
+}
+
+.control-group.success .control-label,
+.control-group.success .help-block,
+.control-group.success .help-inline {
+ color: #3fb618;
+}
+
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+ color: #080808;
+ border-color: #3fb618;
+}
+
+legend {
+ color: #080808;
+ border-bottom: none;
+}
+
+.form-actions {
+ background-color: #eeeeee;
+ border-top: none;
+}
+
+.dropdown-menu {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.alert {
+ text-shadow: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.alert-heading,
+.alert h1,
+.alert h2,
+.alert h3,
+.alert h4,
+.alert h5,
+.alert h6 {
+ color: #ffffff;
+}
+
+.label {
+ min-width: 80px;
+ min-height: 80px;
+ font-weight: 300;
+ text-shadow: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.label-success {
+ background-color: #3fb618;
+}
+
+.label-important {
+ background-color: #ff0039;
+}
+
+.label-info {
+ background-color: #9954bb;
+}
+
+.label-inverse {
+ background-color: #000000;
+}
+
+.badge {
+ font-weight: 300;
+ text-shadow: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.badge-success {
+ background-color: #3fb618;
+}
+
+.badge-important {
+ background-color: #ff0039;
+}
+
+.badge-info {
+ background-color: #9954bb;
+}
+
+.badge-inverse {
+ background-color: #000000;
+}
+
+.hero-unit {
+ border: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.well {
+ border: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+[class^="icon-"],
+[class*=" icon-"] {
+ margin: 0 2px;
+ vertical-align: -2px;
+}
+
+a.thumbnail {
+ background-color: #dfdfdf;
+}
+
+a.thumbnail:hover {
+ background-color: #bbbbbb;
+ border-color: transparent;
+}
+
+.progress {
+ height: 6px;
+ background-color: #eeeeee;
+ background-image: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.progress .bar {
+ background-color: #007fff;
+/* background-image: none;*/
+}
+
+.progress-info {
+ background-color: #9954bb;
+}
+
+.progress-success {
+ background-color: #3fb618;
+}
+
+.progress-warning {
+ background-color: #ff7518;
+}
+
+.progress-danger {
+ background-color: #ff0039;
+}
+
+.modal {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.modal-header {
+ border-bottom: none;
+}
+
+.modal-footer {
+ background-color: transparent;
+ border-top: none;
+}
+
+.popover {
+ color: #ffffff;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.popover-title {
+ color: #ffffff;
+ border-bottom: none;
+}
+
+.pull-right {
+ float: right;
+}
+
+.pull-left {
+ float: left;
+}
+
+.hide {
+ display: none;
+}
+
+.show {
+ display: block;
+}
+
+.invisible {
+ visibility: hidden;
+}
+
+.affix {
+ position: fixed;
+}
diff --git a/website/lib/bootstrap-responsive.min.css b/website/lib/bootstrap-responsive.min.css
new file mode 100644
index 0000000..557b492
--- /dev/null
+++ b/website/lib/bootstrap-responsive.min.css
@@ -0,0 +1,11 @@
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+/*!
+ * Bootstrap Responsive v2.3.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
diff --git a/website/package-lock.json b/website/package-lock.json
new file mode 100644
index 0000000..c178ada
--- /dev/null
+++ b/website/package-lock.json
@@ -0,0 +1,9269 @@
+{
+ "name": "website",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "devDependencies": {
+ "clean-css": "^5.1.1",
+ "event-stream": "4.0.1",
+ "gulp": "^4.0.2",
+ "monaco-editor": "^0.23.0",
+ "rimraf": "^3.0.2",
+ "uncss": "^0.17.3",
+ "yaserver": "^0.3.0"
+ }
+ },
+ "node_modules/abab": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
+ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
+ "dev": true
+ },
+ "node_modules/acorn": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+ "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-globals": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
+ "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^6.0.1",
+ "acorn-walk": "^6.0.1"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
+ "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
+ "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-wrap": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ansi-gray": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
+ "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=",
+ "dev": true,
+ "dependencies": {
+ "ansi-wrap": "0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ansi-wrap": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
+ "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "dependencies": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ }
+ },
+ "node_modules/anymatch/node_modules/normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "dependencies": {
+ "remove-trailing-separator": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/append-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz",
+ "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=",
+ "dev": true,
+ "dependencies": {
+ "buffer-equal": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/archy": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
+ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
+ "dev": true
+ },
+ "node_modules/arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-filter": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz",
+ "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=",
+ "dev": true,
+ "dependencies": {
+ "make-iterator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-map": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz",
+ "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=",
+ "dev": true,
+ "dependencies": {
+ "make-iterator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-each": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
+ "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
+ "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
+ "dev": true
+ },
+ "node_modules/array-initial": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz",
+ "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=",
+ "dev": true,
+ "dependencies": {
+ "array-slice": "^1.0.0",
+ "is-number": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-initial/node_modules/is-number": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-last": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz",
+ "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-last/node_modules/is-number": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-slice": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
+ "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-sort": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz",
+ "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==",
+ "dev": true,
+ "dependencies": {
+ "default-compare": "^1.0.0",
+ "get-value": "^2.0.6",
+ "kind-of": "^5.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/asn1": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "node_modules/assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/async-done": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz",
+ "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.2",
+ "process-nextick-args": "^2.0.0",
+ "stream-exhaust": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/async-each": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+ "dev": true
+ },
+ "node_modules/async-limiter": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+ "dev": true
+ },
+ "node_modules/async-settle": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz",
+ "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=",
+ "dev": true,
+ "dependencies": {
+ "async-done": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
+ "node_modules/atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true,
+ "bin": {
+ "atob": "bin/atob.js"
+ },
+ "engines": {
+ "node": ">= 4.5.0"
+ }
+ },
+ "node_modules/aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/aws4": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+ "dev": true
+ },
+ "node_modules/bach": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz",
+ "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=",
+ "dev": true,
+ "dependencies": {
+ "arr-filter": "^1.1.1",
+ "arr-flatten": "^1.0.1",
+ "arr-map": "^2.0.0",
+ "array-each": "^1.0.0",
+ "array-initial": "^1.0.0",
+ "array-last": "^1.1.1",
+ "async-done": "^1.2.2",
+ "async-settle": "^1.0.0",
+ "now-and-later": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "node_modules/base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "dependencies": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/base/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dev": true,
+ "dependencies": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "dependencies": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/browser-process-hrtime": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
+ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
+ "dev": true
+ },
+ "node_modules/buffer-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz",
+ "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "node_modules/cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "dependencies": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true
+ },
+ "node_modules/chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "deprecated": "Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.",
+ "dev": true,
+ "dependencies": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ },
+ "optionalDependencies": {
+ "fsevents": "^1.2.7"
+ }
+ },
+ "node_modules/class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "dependencies": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/clean-css": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.1.2.tgz",
+ "integrity": "sha512-QcaGg9OuMo+0Ds933yLOY+gHPWbxhxqF0HDexmToPf8pczvmvZGYzd+QqWp9/mkucAOKViI+dSFOqoZIvXbeBw==",
+ "dev": true,
+ "dependencies": {
+ "source-map": "~0.6.0"
+ },
+ "engines": {
+ "node": ">= 10.0"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wrap-ansi": "^2.0.0"
+ }
+ },
+ "node_modules/clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/clone-buffer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
+ "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/clone-stats": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
+ "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=",
+ "dev": true
+ },
+ "node_modules/cloneable-readable": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz",
+ "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "process-nextick-args": "^2.0.0",
+ "readable-stream": "^2.3.5"
+ }
+ },
+ "node_modules/code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/collection-map": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz",
+ "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=",
+ "dev": true,
+ "dependencies": {
+ "arr-map": "^2.0.2",
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "dev": true,
+ "dependencies": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "dev": true,
+ "bin": {
+ "color-support": "bin.js"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "node_modules/concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
+ "engines": [
+ "node >= 0.8"
+ ],
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+ "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "node_modules/copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/copy-props": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz",
+ "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==",
+ "dev": true,
+ "dependencies": {
+ "each-props": "^1.3.2",
+ "is-plain-object": "^5.0.0"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cssom": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+ "dev": true
+ },
+ "node_modules/cssstyle": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz",
+ "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
+ "dev": true,
+ "dependencies": {
+ "cssom": "0.3.x"
+ }
+ },
+ "node_modules/d": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+ "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+ "dev": true,
+ "dependencies": {
+ "es5-ext": "^0.10.50",
+ "type": "^1.0.1"
+ }
+ },
+ "node_modules/dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/data-urls": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
+ "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
+ "dev": true,
+ "dependencies": {
+ "abab": "^2.0.0",
+ "whatwg-mimetype": "^2.2.0",
+ "whatwg-url": "^7.0.0"
+ }
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "node_modules/default-compare": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz",
+ "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^5.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/default-resolution": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz",
+ "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "dependencies": {
+ "object-keys": "^1.0.12"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/detect-file": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/domexception": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
+ "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
+ "dev": true,
+ "dependencies": {
+ "webidl-conversions": "^4.0.2"
+ }
+ },
+ "node_modules/duplexer": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+ "dev": true
+ },
+ "node_modules/duplexify": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+ "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "node_modules/each-props": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz",
+ "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.1",
+ "object.defaults": "^1.1.0"
+ }
+ },
+ "node_modules/each-props/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dev": true,
+ "dependencies": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es5-ext": {
+ "version": "0.10.53",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+ "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+ "dev": true,
+ "dependencies": {
+ "es6-iterator": "~2.0.3",
+ "es6-symbol": "~3.1.3",
+ "next-tick": "~1.0.0"
+ }
+ },
+ "node_modules/es6-iterator": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+ "dev": true,
+ "dependencies": {
+ "d": "1",
+ "es5-ext": "^0.10.35",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "node_modules/es6-symbol": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+ "dev": true,
+ "dependencies": {
+ "d": "^1.0.1",
+ "ext": "^1.1.2"
+ }
+ },
+ "node_modules/es6-weak-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
+ "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
+ "dev": true,
+ "dependencies": {
+ "d": "1",
+ "es5-ext": "^0.10.46",
+ "es6-iterator": "^2.0.3",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+ "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+ "dev": true,
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^4.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/event-stream": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz",
+ "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==",
+ "dev": true,
+ "dependencies": {
+ "duplexer": "^0.1.1",
+ "from": "^0.1.7",
+ "map-stream": "0.0.7",
+ "pause-stream": "^0.0.11",
+ "split": "^1.0.1",
+ "stream-combiner": "^0.2.2",
+ "through": "^2.3.8"
+ }
+ },
+ "node_modules/expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "dev": true,
+ "dependencies": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-tilde": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+ "dev": true,
+ "dependencies": {
+ "homedir-polyfill": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ext": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+ "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+ "dev": true,
+ "dependencies": {
+ "type": "^2.0.0"
+ }
+ },
+ "node_modules/ext/node_modules/type": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz",
+ "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==",
+ "dev": true
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "dependencies": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ]
+ },
+ "node_modules/fancy-log": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
+ "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==",
+ "dev": true,
+ "dependencies": {
+ "ansi-gray": "^0.1.1",
+ "color-support": "^1.1.3",
+ "parse-node-version": "^1.0.0",
+ "time-stamp": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "dependencies": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/findup-sync": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
+ "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
+ "dev": true,
+ "dependencies": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "micromatch": "^3.0.4",
+ "resolve-dir": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/fined": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz",
+ "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==",
+ "dev": true,
+ "dependencies": {
+ "expand-tilde": "^2.0.2",
+ "is-plain-object": "^2.0.3",
+ "object.defaults": "^1.1.0",
+ "object.pick": "^1.2.0",
+ "parse-filepath": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/fined/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/flagged-respawn": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
+ "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/flush-write-stream": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+ "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.3.6"
+ }
+ },
+ "node_modules/for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/for-own": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
+ "dev": true,
+ "dependencies": {
+ "for-in": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "dev": true,
+ "dependencies": {
+ "map-cache": "^0.2.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/from": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
+ "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
+ "dev": true
+ },
+ "node_modules/fs-mkdirp-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
+ "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.11",
+ "through2": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1"
+ },
+ "engines": {
+ "node": ">= 4.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "node_modules/get-caller-file": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+ "dev": true
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+ "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ }
+ },
+ "node_modules/glob-parent/node_modules/is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/glob-stream": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
+ "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=",
+ "dev": true,
+ "dependencies": {
+ "extend": "^3.0.0",
+ "glob": "^7.1.1",
+ "glob-parent": "^3.1.0",
+ "is-negated-glob": "^1.0.0",
+ "ordered-read-streams": "^1.0.0",
+ "pumpify": "^1.3.5",
+ "readable-stream": "^2.1.5",
+ "remove-trailing-separator": "^1.0.1",
+ "to-absolute-glob": "^2.0.0",
+ "unique-stream": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/glob-watcher": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz",
+ "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==",
+ "dev": true,
+ "dependencies": {
+ "anymatch": "^2.0.0",
+ "async-done": "^1.2.0",
+ "chokidar": "^2.0.0",
+ "is-negated-glob": "^1.0.0",
+ "just-debounce": "^1.0.0",
+ "normalize-path": "^3.0.0",
+ "object.defaults": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/global-modules": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+ "dev": true,
+ "dependencies": {
+ "global-prefix": "^1.0.1",
+ "is-windows": "^1.0.1",
+ "resolve-dir": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/global-prefix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
+ "dev": true,
+ "dependencies": {
+ "expand-tilde": "^2.0.2",
+ "homedir-polyfill": "^1.0.1",
+ "ini": "^1.3.4",
+ "is-windows": "^1.0.1",
+ "which": "^1.2.14"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/glogg": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz",
+ "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==",
+ "dev": true,
+ "dependencies": {
+ "sparkles": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
+ "dev": true
+ },
+ "node_modules/gulp": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz",
+ "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==",
+ "dev": true,
+ "dependencies": {
+ "glob-watcher": "^5.0.3",
+ "gulp-cli": "^2.2.0",
+ "undertaker": "^1.2.1",
+ "vinyl-fs": "^3.0.0"
+ },
+ "bin": {
+ "gulp": "bin/gulp.js"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/gulp-cli": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz",
+ "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-colors": "^1.0.1",
+ "archy": "^1.0.0",
+ "array-sort": "^1.0.0",
+ "color-support": "^1.1.3",
+ "concat-stream": "^1.6.0",
+ "copy-props": "^2.0.1",
+ "fancy-log": "^1.3.2",
+ "gulplog": "^1.0.0",
+ "interpret": "^1.4.0",
+ "isobject": "^3.0.1",
+ "liftoff": "^3.1.0",
+ "matchdep": "^2.0.0",
+ "mute-stdout": "^1.0.0",
+ "pretty-hrtime": "^1.0.0",
+ "replace-homedir": "^1.0.0",
+ "semver-greatest-satisfied-range": "^1.1.0",
+ "v8flags": "^3.2.0",
+ "yargs": "^7.1.0"
+ },
+ "bin": {
+ "gulp": "bin/gulp.js"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/gulplog": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
+ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=",
+ "dev": true,
+ "dependencies": {
+ "glogg": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "deprecated": "this library is no longer supported",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "dev": true,
+ "dependencies": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values/node_modules/kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/homedir-polyfill": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
+ "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
+ "dev": true,
+ "dependencies": {
+ "parse-passwd": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/hosted-git-info": {
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+ "dev": true
+ },
+ "node_modules/html-encoding-sniffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
+ "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
+ "dev": true,
+ "dependencies": {
+ "whatwg-encoding": "^1.0.1"
+ }
+ },
+ "node_modules/html-tags": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-1.2.0.tgz",
+ "integrity": "sha1-x43mW1Zjqll5id0rerSSANfk25g=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ },
+ "engines": {
+ "node": ">=0.8",
+ "npm": ">=1.3.7"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/indexes-of": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+ "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+ "dev": true
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "node_modules/interpret": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/invert-kv": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-absolute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
+ "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
+ "dev": true,
+ "dependencies": {
+ "is-relative": "^1.0.0",
+ "is-windows": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-absolute-url": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
+ "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "node_modules/is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "node_modules/is-core-module": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
+ "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-descriptor/node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "dev": true,
+ "dependencies": {
+ "number-is-nan": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-html": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-html/-/is-html-1.1.0.tgz",
+ "integrity": "sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ=",
+ "dev": true,
+ "dependencies": {
+ "html-tags": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-negated-glob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
+ "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-relative": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
+ "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
+ "dev": true,
+ "dependencies": {
+ "is-unc-path": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "node_modules/is-unc-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
+ "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
+ "dev": true,
+ "dependencies": {
+ "unc-path-regex": "^0.1.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+ "dev": true
+ },
+ "node_modules/is-valid-glob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz",
+ "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
+ },
+ "node_modules/jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true
+ },
+ "node_modules/jsdom": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz",
+ "integrity": "sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==",
+ "dev": true,
+ "dependencies": {
+ "abab": "^2.0.0",
+ "acorn": "^6.0.4",
+ "acorn-globals": "^4.3.0",
+ "array-equal": "^1.0.0",
+ "cssom": "^0.3.4",
+ "cssstyle": "^1.1.1",
+ "data-urls": "^1.1.0",
+ "domexception": "^1.0.1",
+ "escodegen": "^1.11.0",
+ "html-encoding-sniffer": "^1.0.2",
+ "nwsapi": "^2.1.3",
+ "parse5": "5.1.0",
+ "pn": "^1.1.0",
+ "request": "^2.88.0",
+ "request-promise-native": "^1.0.5",
+ "saxes": "^3.1.9",
+ "symbol-tree": "^3.2.2",
+ "tough-cookie": "^2.5.0",
+ "w3c-hr-time": "^1.0.1",
+ "w3c-xmlserializer": "^1.1.2",
+ "webidl-conversions": "^4.0.2",
+ "whatwg-encoding": "^1.0.5",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^7.0.0",
+ "ws": "^6.1.2",
+ "xml-name-validator": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true
+ },
+ "node_modules/jsprim": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "dependencies": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "node_modules/just-debounce": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz",
+ "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==",
+ "dev": true
+ },
+ "node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/last-run": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz",
+ "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=",
+ "dev": true,
+ "dependencies": {
+ "default-resolution": "^2.0.0",
+ "es6-weak-map": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/lazystream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
+ "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
+ "dev": true,
+ "dependencies": {
+ "readable-stream": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.6.3"
+ }
+ },
+ "node_modules/lcid": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+ "dev": true,
+ "dependencies": {
+ "invert-kv": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/lead": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz",
+ "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=",
+ "dev": true,
+ "dependencies": {
+ "flush-write-stream": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/liftoff": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz",
+ "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==",
+ "dev": true,
+ "dependencies": {
+ "extend": "^3.0.0",
+ "findup-sync": "^3.0.0",
+ "fined": "^1.0.1",
+ "flagged-respawn": "^1.0.0",
+ "is-plain-object": "^2.0.4",
+ "object.map": "^1.0.0",
+ "rechoir": "^0.6.2",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/liftoff/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/load-json-file": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "strip-bom": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/lodash.sortby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+ "dev": true
+ },
+ "node_modules/make-iterator": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
+ "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/make-iterator/node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/map-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
+ "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=",
+ "dev": true
+ },
+ "node_modules/map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "dev": true,
+ "dependencies": {
+ "object-visit": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/matchdep": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz",
+ "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=",
+ "dev": true,
+ "dependencies": {
+ "findup-sync": "^2.0.0",
+ "micromatch": "^3.0.4",
+ "resolve": "^1.4.0",
+ "stack-trace": "0.0.10"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/matchdep/node_modules/findup-sync": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
+ "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
+ "dev": true,
+ "dependencies": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^3.1.0",
+ "micromatch": "^3.0.4",
+ "resolve-dir": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/matchdep/node_modules/is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "dependencies": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "dependencies": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.46.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz",
+ "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.29",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz",
+ "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": "1.46.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dev": true,
+ "dependencies": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mixin-deep/node_modules/is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mixin-deep/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/monaco-editor": {
+ "version": "0.23.0",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.23.0.tgz",
+ "integrity": "sha512-q+CP5zMR/aFiMTE9QlIavGyGicKnG2v/H8qVvybLzeFsARM8f6G9fL0sMST2tyVYCwDKkGamZUI6647A0jR/Lg==",
+ "dev": true
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "node_modules/mute-stdout": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz",
+ "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/nan": {
+ "version": "2.14.2",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
+ "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "dependencies": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanomatch/node_modules/extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "dependencies": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanomatch/node_modules/is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanomatch/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanomatch/node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/next-tick": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
+ "dev": true
+ },
+ "node_modules/normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "dependencies": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/now-and-later": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz",
+ "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.2"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nwsapi": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
+ "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
+ "dev": true
+ },
+ "node_modules/oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "dev": true,
+ "dependencies": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.defaults": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
+ "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=",
+ "dev": true,
+ "dependencies": {
+ "array-each": "^1.0.1",
+ "array-slice": "^1.0.0",
+ "for-own": "^1.0.0",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
+ "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=",
+ "dev": true,
+ "dependencies": {
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.reduce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz",
+ "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=",
+ "dev": true,
+ "dependencies": {
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/ordered-read-streams": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
+ "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=",
+ "dev": true,
+ "dependencies": {
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "node_modules/os-locale": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+ "dev": true,
+ "dependencies": {
+ "lcid": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/parse-filepath": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
+ "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=",
+ "dev": true,
+ "dependencies": {
+ "is-absolute": "^1.0.0",
+ "map-cache": "^0.2.0",
+ "path-root": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "dependencies": {
+ "error-ex": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/parse-node-version": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+ "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/parse-passwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+ "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
+ "dev": true
+ },
+ "node_modules/pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+ "dev": true
+ },
+ "node_modules/path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "dependencies": {
+ "pinkie-promise": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/path-root": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
+ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=",
+ "dev": true,
+ "dependencies": {
+ "path-root-regex": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-root-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
+ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pause-stream": {
+ "version": "0.0.11",
+ "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
+ "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
+ "dev": true,
+ "dependencies": {
+ "through": "~2.3"
+ }
+ },
+ "node_modules/performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true
+ },
+ "node_modules/picocolors": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+ "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+ "dev": true
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "dependencies": {
+ "pinkie": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pn": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
+ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
+ "dev": true
+ },
+ "node_modules/posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "7.0.39",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+ "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+ "dev": true,
+ "dependencies": {
+ "picocolors": "^0.2.1",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
+ "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+ "dev": true,
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/pretty-hrtime": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
+ "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "node_modules/psl": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+ "dev": true
+ },
+ "node_modules/pump": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/pumpify": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+ "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+ "dev": true,
+ "dependencies": {
+ "duplexify": "^3.6.0",
+ "inherits": "^2.0.3",
+ "pump": "^2.0.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "dev": true,
+ "dependencies": {
+ "load-json-file": "^1.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^1.0.0",
+ "read-pkg": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/rechoir": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+ "dev": true,
+ "dependencies": {
+ "resolve": "^1.1.6"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/regex-not/node_modules/extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "dependencies": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/regex-not/node_modules/is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/regex-not/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/remove-bom-buffer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz",
+ "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5",
+ "is-utf8": "^0.2.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/remove-bom-stream": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz",
+ "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=",
+ "dev": true,
+ "dependencies": {
+ "remove-bom-buffer": "^3.0.0",
+ "safe-buffer": "^5.1.0",
+ "through2": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+ "dev": true
+ },
+ "node_modules/repeat-element": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+ "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/replace-ext": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz",
+ "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/replace-homedir": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz",
+ "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=",
+ "dev": true,
+ "dependencies": {
+ "homedir-polyfill": "^1.0.1",
+ "is-absolute": "^1.0.0",
+ "remove-trailing-separator": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
+ "dev": true,
+ "dependencies": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/request-promise-core": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+ "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.19"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "peerDependencies": {
+ "request": "^2.34"
+ }
+ },
+ "node_modules/request-promise-native": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
+ "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
+ "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142",
+ "dev": true,
+ "dependencies": {
+ "request-promise-core": "1.1.4",
+ "stealthy-require": "^1.1.1",
+ "tough-cookie": "^2.3.3"
+ },
+ "engines": {
+ "node": ">=0.12.0"
+ },
+ "peerDependencies": {
+ "request": "^2.34"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-main-filename": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+ "dev": true
+ },
+ "node_modules/resolve": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-dir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
+ "dev": true,
+ "dependencies": {
+ "expand-tilde": "^2.0.0",
+ "global-modules": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-options": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz",
+ "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=",
+ "dev": true,
+ "dependencies": {
+ "value-or-function": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+ "deprecated": "https://github.com/lydell/resolve-url#deprecated",
+ "dev": true
+ },
+ "node_modules/ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "node_modules/safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "dev": true,
+ "dependencies": {
+ "ret": "~0.1.10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "node_modules/saxes": {
+ "version": "3.1.11",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz",
+ "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==",
+ "dev": true,
+ "dependencies": {
+ "xmlchars": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/semver-greatest-satisfied-range": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz",
+ "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=",
+ "dev": true,
+ "dependencies": {
+ "sver-compat": "^1.5.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
+ },
+ "node_modules/set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/set-value/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "dependencies": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-node/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-util/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-resolve": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "dev": true,
+ "dependencies": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "node_modules/source-map-url": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+ "dev": true
+ },
+ "node_modules/sparkles": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz",
+ "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "dev": true,
+ "dependencies": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-license-ids": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz",
+ "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==",
+ "dev": true
+ },
+ "node_modules/split": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+ "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+ "dev": true,
+ "dependencies": {
+ "through": "2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/split-string/node_modules/extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "dependencies": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/split-string/node_modules/is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/split-string/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sshpk": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+ "dev": true,
+ "dependencies": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ },
+ "bin": {
+ "sshpk-conv": "bin/sshpk-conv",
+ "sshpk-sign": "bin/sshpk-sign",
+ "sshpk-verify": "bin/sshpk-verify"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stealthy-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stream-combiner": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
+ "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=",
+ "dev": true,
+ "dependencies": {
+ "duplexer": "~0.1.1",
+ "through": "~2.3.4"
+ }
+ },
+ "node_modules/stream-exhaust": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz",
+ "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==",
+ "dev": true
+ },
+ "node_modules/stream-shift": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+ "dev": true
+ },
+ "node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "dev": true,
+ "dependencies": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "dev": true,
+ "dependencies": {
+ "is-utf8": "^0.2.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sver-compat": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz",
+ "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=",
+ "dev": true,
+ "dependencies": {
+ "es6-iterator": "^2.0.1",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "node_modules/through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "dependencies": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "node_modules/through2-filter": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
+ "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
+ "dev": true,
+ "dependencies": {
+ "through2": "~2.0.0",
+ "xtend": "~4.0.0"
+ }
+ },
+ "node_modules/time-stamp": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
+ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-absolute-glob": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
+ "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=",
+ "dev": true,
+ "dependencies": {
+ "is-absolute": "^1.0.0",
+ "is-negated-glob": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-object-path/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex/node_modules/extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "dependencies": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex/node_modules/is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-through": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz",
+ "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=",
+ "dev": true,
+ "dependencies": {
+ "through2": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "dependencies": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+ "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true
+ },
+ "node_modules/type": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+ "dev": true
+ },
+ "node_modules/type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "node_modules/unc-path-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
+ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/uncss": {
+ "version": "0.17.3",
+ "resolved": "https://registry.npmjs.org/uncss/-/uncss-0.17.3.tgz",
+ "integrity": "sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^2.20.0",
+ "glob": "^7.1.4",
+ "is-absolute-url": "^3.0.1",
+ "is-html": "^1.1.0",
+ "jsdom": "^14.1.0",
+ "lodash": "^4.17.15",
+ "postcss": "^7.0.17",
+ "postcss-selector-parser": "6.0.2",
+ "request": "^2.88.0"
+ },
+ "bin": {
+ "uncss": "bin/uncss"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/undertaker": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz",
+ "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==",
+ "dev": true,
+ "dependencies": {
+ "arr-flatten": "^1.0.1",
+ "arr-map": "^2.0.0",
+ "bach": "^1.0.0",
+ "collection-map": "^1.0.0",
+ "es6-weak-map": "^2.0.1",
+ "fast-levenshtein": "^1.0.0",
+ "last-run": "^1.1.0",
+ "object.defaults": "^1.0.0",
+ "object.reduce": "^1.0.0",
+ "undertaker-registry": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/undertaker-registry": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz",
+ "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/undertaker/node_modules/fast-levenshtein": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz",
+ "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=",
+ "dev": true
+ },
+ "node_modules/union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "dependencies": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/uniq": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+ "dev": true
+ },
+ "node_modules/unique-stream": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
+ "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
+ "dev": true,
+ "dependencies": {
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "through2-filter": "^3.0.0"
+ }
+ },
+ "node_modules/unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "dev": true,
+ "dependencies": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "dev": true,
+ "dependencies": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "dependencies": {
+ "isarray": "1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/upath": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4",
+ "yarn": "*"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+ "deprecated": "Please see https://github.com/lydell/urix#deprecated",
+ "dev": true
+ },
+ "node_modules/use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "node_modules/uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true,
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
+ "node_modules/v8flags": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
+ "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==",
+ "dev": true,
+ "dependencies": {
+ "homedir-polyfill": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "dependencies": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "node_modules/value-or-function": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz",
+ "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "node_modules/vinyl": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz",
+ "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==",
+ "dev": true,
+ "dependencies": {
+ "clone": "^2.1.1",
+ "clone-buffer": "^1.0.0",
+ "clone-stats": "^1.0.0",
+ "cloneable-readable": "^1.0.0",
+ "remove-trailing-separator": "^1.0.1",
+ "replace-ext": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/vinyl-fs": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz",
+ "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==",
+ "dev": true,
+ "dependencies": {
+ "fs-mkdirp-stream": "^1.0.0",
+ "glob-stream": "^6.1.0",
+ "graceful-fs": "^4.0.0",
+ "is-valid-glob": "^1.0.0",
+ "lazystream": "^1.0.0",
+ "lead": "^1.0.0",
+ "object.assign": "^4.0.4",
+ "pumpify": "^1.3.5",
+ "readable-stream": "^2.3.3",
+ "remove-bom-buffer": "^3.0.0",
+ "remove-bom-stream": "^1.2.0",
+ "resolve-options": "^1.1.0",
+ "through2": "^2.0.0",
+ "to-through": "^2.0.0",
+ "value-or-function": "^3.0.0",
+ "vinyl": "^2.0.0",
+ "vinyl-sourcemap": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/vinyl-sourcemap": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz",
+ "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=",
+ "dev": true,
+ "dependencies": {
+ "append-buffer": "^1.0.2",
+ "convert-source-map": "^1.5.0",
+ "graceful-fs": "^4.1.6",
+ "normalize-path": "^2.1.1",
+ "now-and-later": "^2.0.0",
+ "remove-bom-buffer": "^3.0.0",
+ "vinyl": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/vinyl-sourcemap/node_modules/normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "dependencies": {
+ "remove-trailing-separator": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/w3c-hr-time": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
+ "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
+ "dev": true,
+ "dependencies": {
+ "browser-process-hrtime": "^1.0.0"
+ }
+ },
+ "node_modules/w3c-xmlserializer": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz",
+ "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==",
+ "dev": true,
+ "dependencies": {
+ "domexception": "^1.0.1",
+ "webidl-conversions": "^4.0.2",
+ "xml-name-validator": "^3.0.0"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+ "dev": true
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+ "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+ "dev": true,
+ "dependencies": {
+ "iconv-lite": "0.4.24"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+ "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+ "dev": true
+ },
+ "node_modules/whatwg-url": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+ "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+ "dev": true,
+ "dependencies": {
+ "lodash.sortby": "^4.7.0",
+ "tr46": "^1.0.1",
+ "webidl-conversions": "^4.0.2"
+ }
+ },
+ "node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/which-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
+ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
+ "dev": true
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "node_modules/ws": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
+ "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
+ "dev": true,
+ "dependencies": {
+ "async-limiter": "~1.0.0"
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
+ "dev": true
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
+ "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==",
+ "dev": true
+ },
+ "node_modules/yargs": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz",
+ "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^3.0.0",
+ "cliui": "^3.2.0",
+ "decamelize": "^1.1.1",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^1.4.0",
+ "read-pkg-up": "^1.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^1.0.2",
+ "which-module": "^1.0.0",
+ "y18n": "^3.2.1",
+ "yargs-parser": "5.0.0-security.0"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "5.0.0-security.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz",
+ "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^3.0.0",
+ "object.assign": "^4.1.0"
+ }
+ },
+ "node_modules/yaserver": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/yaserver/-/yaserver-0.3.0.tgz",
+ "integrity": "sha512-q7O/gz6B46jA0QZptLxPDBWwojnbpxXwjHGHwWKghsPQBNDcTwOmJ2wTbFAoU6me1M/Z995eATE5THYmGzGE6A==",
+ "dev": true,
+ "bin": {
+ "yaserver": "bin/yaserver"
+ }
+ }
+ },
+ "dependencies": {
+ "abab": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
+ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
+ "dev": true
+ },
+ "acorn": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+ "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+ "dev": true
+ },
+ "acorn-globals": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
+ "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==",
+ "dev": true,
+ "requires": {
+ "acorn": "^6.0.1",
+ "acorn-walk": "^6.0.1"
+ }
+ },
+ "acorn-walk": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
+ "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-colors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
+ "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "^0.1.0"
+ }
+ },
+ "ansi-gray": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
+ "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-wrap": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
+ "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=",
+ "dev": true
+ },
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ }
+ }
+ },
+ "append-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz",
+ "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=",
+ "dev": true,
+ "requires": {
+ "buffer-equal": "^1.0.0"
+ }
+ },
+ "archy": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
+ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
+ "dev": true
+ },
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true
+ },
+ "arr-filter": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz",
+ "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=",
+ "dev": true,
+ "requires": {
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true
+ },
+ "arr-map": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz",
+ "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=",
+ "dev": true,
+ "requires": {
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "dev": true
+ },
+ "array-each": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
+ "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=",
+ "dev": true
+ },
+ "array-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
+ "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
+ "dev": true
+ },
+ "array-initial": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz",
+ "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=",
+ "dev": true,
+ "requires": {
+ "array-slice": "^1.0.0",
+ "is-number": "^4.0.0"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+ "dev": true
+ }
+ }
+ },
+ "array-last": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz",
+ "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==",
+ "dev": true,
+ "requires": {
+ "is-number": "^4.0.0"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+ "dev": true
+ }
+ }
+ },
+ "array-slice": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
+ "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
+ "dev": true
+ },
+ "array-sort": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz",
+ "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==",
+ "dev": true,
+ "requires": {
+ "default-compare": "^1.0.0",
+ "get-value": "^2.0.6",
+ "kind-of": "^5.0.2"
+ }
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "dev": true
+ },
+ "asn1": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "dev": true
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "dev": true
+ },
+ "async-done": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz",
+ "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.2",
+ "process-nextick-args": "^2.0.0",
+ "stream-exhaust": "^1.0.1"
+ }
+ },
+ "async-each": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+ "dev": true
+ },
+ "async-limiter": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+ "dev": true
+ },
+ "async-settle": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz",
+ "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=",
+ "dev": true,
+ "requires": {
+ "async-done": "^1.2.2"
+ }
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+ "dev": true
+ },
+ "bach": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz",
+ "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=",
+ "dev": true,
+ "requires": {
+ "arr-filter": "^1.1.1",
+ "arr-flatten": "^1.0.1",
+ "arr-map": "^2.0.0",
+ "array-each": "^1.0.0",
+ "array-initial": "^1.0.0",
+ "array-last": "^1.1.1",
+ "async-done": "^1.2.2",
+ "async-settle": "^1.0.0",
+ "now-and-later": "^2.0.0"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ }
+ }
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dev": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true
+ },
+ "bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "browser-process-hrtime": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
+ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
+ "dev": true
+ },
+ "buffer-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz",
+ "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=",
+ "dev": true
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ }
+ },
+ "camelcase": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
+ "dev": true
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true
+ },
+ "chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ }
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ }
+ }
+ },
+ "clean-css": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.1.2.tgz",
+ "integrity": "sha512-QcaGg9OuMo+0Ds933yLOY+gHPWbxhxqF0HDexmToPf8pczvmvZGYzd+QqWp9/mkucAOKViI+dSFOqoZIvXbeBw==",
+ "dev": true,
+ "requires": {
+ "source-map": "~0.6.0"
+ }
+ },
+ "cliui": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wrap-ansi": "^2.0.0"
+ }
+ },
+ "clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+ "dev": true
+ },
+ "clone-buffer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
+ "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=",
+ "dev": true
+ },
+ "clone-stats": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
+ "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=",
+ "dev": true
+ },
+ "cloneable-readable": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz",
+ "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "process-nextick-args": "^2.0.0",
+ "readable-stream": "^2.3.5"
+ }
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+ "dev": true
+ },
+ "collection-map": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz",
+ "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=",
+ "dev": true,
+ "requires": {
+ "arr-map": "^2.0.2",
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "dev": true,
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "convert-source-map": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+ "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+ "dev": true
+ },
+ "copy-props": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz",
+ "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==",
+ "dev": true,
+ "requires": {
+ "each-props": "^1.3.2",
+ "is-plain-object": "^5.0.0"
+ }
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true
+ },
+ "cssom": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+ "dev": true
+ },
+ "cssstyle": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz",
+ "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
+ "dev": true,
+ "requires": {
+ "cssom": "0.3.x"
+ }
+ },
+ "d": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+ "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+ "dev": true,
+ "requires": {
+ "es5-ext": "^0.10.50",
+ "type": "^1.0.1"
+ }
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "data-urls": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
+ "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.0",
+ "whatwg-mimetype": "^2.2.0",
+ "whatwg-url": "^7.0.0"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "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=",
+ "dev": true
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "default-compare": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz",
+ "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^5.0.2"
+ }
+ },
+ "default-resolution": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz",
+ "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true
+ },
+ "detect-file": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
+ "dev": true
+ },
+ "domexception": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
+ "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
+ "dev": true,
+ "requires": {
+ "webidl-conversions": "^4.0.2"
+ }
+ },
+ "duplexer": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+ "dev": true
+ },
+ "duplexify": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+ "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "each-props": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz",
+ "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.1",
+ "object.defaults": "^1.1.0"
+ },
+ "dependencies": {
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ }
+ }
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dev": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es5-ext": {
+ "version": "0.10.53",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+ "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+ "dev": true,
+ "requires": {
+ "es6-iterator": "~2.0.3",
+ "es6-symbol": "~3.1.3",
+ "next-tick": "~1.0.0"
+ }
+ },
+ "es6-iterator": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+ "dev": true,
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.35",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "es6-symbol": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+ "dev": true,
+ "requires": {
+ "d": "^1.0.1",
+ "ext": "^1.1.2"
+ }
+ },
+ "es6-weak-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
+ "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
+ "dev": true,
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.46",
+ "es6-iterator": "^2.0.3",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "escodegen": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+ "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+ "dev": true,
+ "requires": {
+ "esprima": "^4.0.1",
+ "estraverse": "^4.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1",
+ "source-map": "~0.6.1"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "event-stream": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz",
+ "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==",
+ "dev": true,
+ "requires": {
+ "duplexer": "^0.1.1",
+ "from": "^0.1.7",
+ "map-stream": "0.0.7",
+ "pause-stream": "^0.0.11",
+ "split": "^1.0.1",
+ "stream-combiner": "^0.2.2",
+ "through": "^2.3.8"
+ }
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ }
+ }
+ },
+ "expand-tilde": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.1"
+ }
+ },
+ "ext": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+ "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+ "dev": true,
+ "requires": {
+ "type": "^2.0.0"
+ },
+ "dependencies": {
+ "type": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz",
+ "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==",
+ "dev": true
+ }
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ }
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true
+ },
+ "fancy-log": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
+ "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==",
+ "dev": true,
+ "requires": {
+ "ansi-gray": "^0.1.1",
+ "color-support": "^1.1.3",
+ "parse-node-version": "^1.0.0",
+ "time-stamp": "^1.0.0"
+ }
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true,
+ "optional": true
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ }
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "requires": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "findup-sync": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
+ "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
+ "dev": true,
+ "requires": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "micromatch": "^3.0.4",
+ "resolve-dir": "^1.0.1"
+ }
+ },
+ "fined": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz",
+ "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.2",
+ "is-plain-object": "^2.0.3",
+ "object.defaults": "^1.1.0",
+ "object.pick": "^1.2.0",
+ "parse-filepath": "^1.0.1"
+ },
+ "dependencies": {
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ }
+ }
+ },
+ "flagged-respawn": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
+ "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==",
+ "dev": true
+ },
+ "flush-write-stream": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+ "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.3.6"
+ }
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+ "dev": true
+ },
+ "for-own": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.1"
+ }
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true
+ },
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "dev": true,
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "from": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
+ "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
+ "dev": true
+ },
+ "fs-mkdirp-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
+ "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "through2": "^2.0.3"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1"
+ }
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "get-caller-file": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+ "dev": true
+ },
+ "get-intrinsic": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+ "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+ "dev": true
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "glob-stream": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
+ "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=",
+ "dev": true,
+ "requires": {
+ "extend": "^3.0.0",
+ "glob": "^7.1.1",
+ "glob-parent": "^3.1.0",
+ "is-negated-glob": "^1.0.0",
+ "ordered-read-streams": "^1.0.0",
+ "pumpify": "^1.3.5",
+ "readable-stream": "^2.1.5",
+ "remove-trailing-separator": "^1.0.1",
+ "to-absolute-glob": "^2.0.0",
+ "unique-stream": "^2.0.2"
+ }
+ },
+ "glob-watcher": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz",
+ "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-done": "^1.2.0",
+ "chokidar": "^2.0.0",
+ "is-negated-glob": "^1.0.0",
+ "just-debounce": "^1.0.0",
+ "normalize-path": "^3.0.0",
+ "object.defaults": "^1.1.0"
+ }
+ },
+ "global-modules": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+ "dev": true,
+ "requires": {
+ "global-prefix": "^1.0.1",
+ "is-windows": "^1.0.1",
+ "resolve-dir": "^1.0.0"
+ }
+ },
+ "global-prefix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.2",
+ "homedir-polyfill": "^1.0.1",
+ "ini": "^1.3.4",
+ "is-windows": "^1.0.1",
+ "which": "^1.2.14"
+ }
+ },
+ "glogg": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz",
+ "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==",
+ "dev": true,
+ "requires": {
+ "sparkles": "^1.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
+ "dev": true
+ },
+ "gulp": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz",
+ "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==",
+ "dev": true,
+ "requires": {
+ "glob-watcher": "^5.0.3",
+ "gulp-cli": "^2.2.0",
+ "undertaker": "^1.2.1",
+ "vinyl-fs": "^3.0.0"
+ }
+ },
+ "gulp-cli": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz",
+ "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^1.0.1",
+ "archy": "^1.0.0",
+ "array-sort": "^1.0.0",
+ "color-support": "^1.1.3",
+ "concat-stream": "^1.6.0",
+ "copy-props": "^2.0.1",
+ "fancy-log": "^1.3.2",
+ "gulplog": "^1.0.0",
+ "interpret": "^1.4.0",
+ "isobject": "^3.0.1",
+ "liftoff": "^3.1.0",
+ "matchdep": "^2.0.0",
+ "mute-stdout": "^1.0.0",
+ "pretty-hrtime": "^1.0.0",
+ "replace-homedir": "^1.0.0",
+ "semver-greatest-satisfied-range": "^1.1.0",
+ "v8flags": "^3.2.0",
+ "yargs": "^7.1.0"
+ }
+ },
+ "gulplog": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
+ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=",
+ "dev": true,
+ "requires": {
+ "glogg": "^1.0.0"
+ }
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true
+ },
+ "har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
+ "dev": true
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "homedir-polyfill": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
+ "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
+ "dev": true,
+ "requires": {
+ "parse-passwd": "^1.0.0"
+ }
+ },
+ "hosted-git-info": {
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+ "dev": true
+ },
+ "html-encoding-sniffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
+ "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
+ "dev": true,
+ "requires": {
+ "whatwg-encoding": "^1.0.1"
+ }
+ },
+ "html-tags": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-1.2.0.tgz",
+ "integrity": "sha1-x43mW1Zjqll5id0rerSSANfk25g=",
+ "dev": true
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "indexes-of": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+ "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "interpret": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+ "dev": true
+ },
+ "invert-kv": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
+ "dev": true
+ },
+ "is-absolute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
+ "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
+ "dev": true,
+ "requires": {
+ "is-relative": "^1.0.0",
+ "is-windows": "^1.0.1"
+ }
+ },
+ "is-absolute-url": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
+ "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==",
+ "dev": true
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ }
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^1.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-core-module": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
+ "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ }
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-html": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-html/-/is-html-1.1.0.tgz",
+ "integrity": "sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ=",
+ "dev": true,
+ "requires": {
+ "html-tags": "^1.0.0"
+ }
+ },
+ "is-negated-glob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
+ "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=",
+ "dev": true
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "dev": true
+ },
+ "is-relative": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
+ "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
+ "dev": true,
+ "requires": {
+ "is-unc-path": "^1.0.0"
+ }
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "is-unc-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
+ "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
+ "dev": true,
+ "requires": {
+ "unc-path-regex": "^0.1.2"
+ }
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+ "dev": true
+ },
+ "is-valid-glob": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz",
+ "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=",
+ "dev": true
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true
+ },
+ "jsdom": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz",
+ "integrity": "sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.0",
+ "acorn": "^6.0.4",
+ "acorn-globals": "^4.3.0",
+ "array-equal": "^1.0.0",
+ "cssom": "^0.3.4",
+ "cssstyle": "^1.1.1",
+ "data-urls": "^1.1.0",
+ "domexception": "^1.0.1",
+ "escodegen": "^1.11.0",
+ "html-encoding-sniffer": "^1.0.2",
+ "nwsapi": "^2.1.3",
+ "parse5": "5.1.0",
+ "pn": "^1.1.0",
+ "request": "^2.88.0",
+ "request-promise-native": "^1.0.5",
+ "saxes": "^3.1.9",
+ "symbol-tree": "^3.2.2",
+ "tough-cookie": "^2.5.0",
+ "w3c-hr-time": "^1.0.1",
+ "w3c-xmlserializer": "^1.1.2",
+ "webidl-conversions": "^4.0.2",
+ "whatwg-encoding": "^1.0.5",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^7.0.0",
+ "ws": "^6.1.2",
+ "xml-name-validator": "^3.0.0"
+ }
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "just-debounce": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz",
+ "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ },
+ "last-run": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz",
+ "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=",
+ "dev": true,
+ "requires": {
+ "default-resolution": "^2.0.0",
+ "es6-weak-map": "^2.0.1"
+ }
+ },
+ "lazystream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
+ "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.5"
+ }
+ },
+ "lcid": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+ "dev": true,
+ "requires": {
+ "invert-kv": "^1.0.0"
+ }
+ },
+ "lead": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz",
+ "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=",
+ "dev": true,
+ "requires": {
+ "flush-write-stream": "^1.0.2"
+ }
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "liftoff": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz",
+ "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==",
+ "dev": true,
+ "requires": {
+ "extend": "^3.0.0",
+ "findup-sync": "^3.0.0",
+ "fined": "^1.0.1",
+ "flagged-respawn": "^1.0.0",
+ "is-plain-object": "^2.0.4",
+ "object.map": "^1.0.0",
+ "rechoir": "^0.6.2",
+ "resolve": "^1.1.7"
+ },
+ "dependencies": {
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ }
+ }
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "strip-bom": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "lodash.sortby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+ "dev": true
+ },
+ "make-iterator": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
+ "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ }
+ }
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+ "dev": true
+ },
+ "map-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
+ "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=",
+ "dev": true
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "dev": true,
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "matchdep": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz",
+ "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=",
+ "dev": true,
+ "requires": {
+ "findup-sync": "^2.0.0",
+ "micromatch": "^3.0.4",
+ "resolve": "^1.4.0",
+ "stack-trace": "0.0.10"
+ },
+ "dependencies": {
+ "findup-sync": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
+ "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
+ "dev": true,
+ "requires": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^3.1.0",
+ "micromatch": "^3.0.4",
+ "resolve-dir": "^1.0.1"
+ }
+ },
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ }
+ }
+ },
+ "mime-db": {
+ "version": "1.46.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz",
+ "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.29",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz",
+ "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.46.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ }
+ }
+ },
+ "monaco-editor": {
+ "version": "0.23.0",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.23.0.tgz",
+ "integrity": "sha512-q+CP5zMR/aFiMTE9QlIavGyGicKnG2v/H8qVvybLzeFsARM8f6G9fL0sMST2tyVYCwDKkGamZUI6647A0jR/Lg==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "mute-stdout": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz",
+ "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==",
+ "dev": true
+ },
+ "nan": {
+ "version": "2.14.2",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
+ "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
+ "dev": true,
+ "optional": true
+ },
+ "nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ }
+ }
+ },
+ "next-tick": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
+ "dev": true
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "now-and-later": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz",
+ "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.2"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true
+ },
+ "nwsapi": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
+ "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
+ "dev": true
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "dev": true,
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.assign": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "object.defaults": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
+ "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=",
+ "dev": true,
+ "requires": {
+ "array-each": "^1.0.1",
+ "array-slice": "^1.0.0",
+ "for-own": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
+ "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=",
+ "dev": true,
+ "requires": {
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "object.reduce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz",
+ "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=",
+ "dev": true,
+ "requires": {
+ "for-own": "^1.0.0",
+ "make-iterator": "^1.0.0"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ }
+ },
+ "ordered-read-streams": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
+ "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "os-locale": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+ "dev": true,
+ "requires": {
+ "lcid": "^1.0.0"
+ }
+ },
+ "parse-filepath": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
+ "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=",
+ "dev": true,
+ "requires": {
+ "is-absolute": "^1.0.0",
+ "map-cache": "^0.2.0",
+ "path-root": "^0.1.1"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "parse-node-version": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+ "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
+ "dev": true
+ },
+ "parse-passwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+ "dev": true
+ },
+ "parse5": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+ "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
+ "dev": true
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+ "dev": true
+ },
+ "path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "requires": {
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "path-root": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
+ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=",
+ "dev": true,
+ "requires": {
+ "path-root-regex": "^0.1.0"
+ }
+ },
+ "path-root-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
+ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=",
+ "dev": true
+ },
+ "path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "pause-stream": {
+ "version": "0.0.11",
+ "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
+ "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
+ "dev": true,
+ "requires": {
+ "through": "~2.3"
+ }
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true
+ },
+ "picocolors": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+ "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+ "dev": true
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "pn": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
+ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
+ "dev": true
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "dev": true
+ },
+ "postcss": {
+ "version": "7.0.39",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+ "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+ "dev": true,
+ "requires": {
+ "picocolors": "^0.2.1",
+ "source-map": "^0.6.1"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
+ "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ }
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "pretty-hrtime": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
+ "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+ "dev": true
+ },
+ "pump": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "pumpify": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+ "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+ "dev": true,
+ "requires": {
+ "duplexify": "^3.6.0",
+ "inherits": "^2.0.3",
+ "pump": "^2.0.0"
+ }
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "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==",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^1.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^1.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "dev": true,
+ "requires": {
+ "find-up": "^1.0.0",
+ "read-pkg": "^1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "rechoir": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+ "dev": true,
+ "requires": {
+ "resolve": "^1.1.6"
+ }
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ }
+ }
+ },
+ "remove-bom-buffer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz",
+ "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5",
+ "is-utf8": "^0.2.1"
+ }
+ },
+ "remove-bom-stream": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz",
+ "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=",
+ "dev": true,
+ "requires": {
+ "remove-bom-buffer": "^3.0.0",
+ "safe-buffer": "^5.1.0",
+ "through2": "^2.0.3"
+ }
+ },
+ "remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+ "dev": true
+ },
+ "repeat-element": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+ "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "replace-ext": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz",
+ "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==",
+ "dev": true
+ },
+ "replace-homedir": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz",
+ "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.1",
+ "is-absolute": "^1.0.0",
+ "remove-trailing-separator": "^1.1.0"
+ }
+ },
+ "request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ }
+ },
+ "request-promise-core": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+ "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.19"
+ }
+ },
+ "request-promise-native": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
+ "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
+ "dev": true,
+ "requires": {
+ "request-promise-core": "1.1.4",
+ "stealthy-require": "^1.1.1",
+ "tough-cookie": "^2.3.3"
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-dir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.0",
+ "global-modules": "^1.0.0"
+ }
+ },
+ "resolve-options": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz",
+ "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=",
+ "dev": true,
+ "requires": {
+ "value-or-function": "^3.0.0"
+ }
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+ "dev": true
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "dev": true,
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "saxes": {
+ "version": "3.1.11",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz",
+ "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==",
+ "dev": true,
+ "requires": {
+ "xmlchars": "^2.1.1"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "semver-greatest-satisfied-range": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz",
+ "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=",
+ "dev": true,
+ "requires": {
+ "sver-compat": "^1.5.0"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
+ },
+ "set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "dependencies": {
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ }
+ }
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.2.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-resolve": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+ "dev": true
+ },
+ "sparkles": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz",
+ "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz",
+ "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==",
+ "dev": true
+ },
+ "split": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+ "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+ "dev": true,
+ "requires": {
+ "through": "2"
+ }
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ }
+ }
+ },
+ "sshpk": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+ "dev": true,
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ }
+ },
+ "stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
+ "dev": true
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ }
+ }
+ },
+ "stealthy-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
+ "dev": true
+ },
+ "stream-combiner": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
+ "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=",
+ "dev": true,
+ "requires": {
+ "duplexer": "~0.1.1",
+ "through": "~2.3.4"
+ }
+ },
+ "stream-exhaust": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz",
+ "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==",
+ "dev": true
+ },
+ "stream-shift": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+ "dev": true
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "dev": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "dev": true,
+ "requires": {
+ "is-utf8": "^0.2.0"
+ }
+ },
+ "sver-compat": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz",
+ "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=",
+ "dev": true,
+ "requires": {
+ "es6-iterator": "^2.0.1",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "through2-filter": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
+ "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
+ "dev": true,
+ "requires": {
+ "through2": "~2.0.0",
+ "xtend": "~4.0.0"
+ }
+ },
+ "time-stamp": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
+ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=",
+ "dev": true
+ },
+ "to-absolute-glob": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
+ "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=",
+ "dev": true,
+ "requires": {
+ "is-absolute": "^1.0.0",
+ "is-negated-glob": "^1.0.0"
+ }
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ }
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "to-through": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz",
+ "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=",
+ "dev": true,
+ "requires": {
+ "through2": "^2.0.3"
+ }
+ },
+ "tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "tr46": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+ "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true
+ },
+ "type": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "unc-path-regex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
+ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=",
+ "dev": true
+ },
+ "uncss": {
+ "version": "0.17.3",
+ "resolved": "https://registry.npmjs.org/uncss/-/uncss-0.17.3.tgz",
+ "integrity": "sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.20.0",
+ "glob": "^7.1.4",
+ "is-absolute-url": "^3.0.1",
+ "is-html": "^1.1.0",
+ "jsdom": "^14.1.0",
+ "lodash": "^4.17.15",
+ "postcss": "^7.0.17",
+ "postcss-selector-parser": "6.0.2",
+ "request": "^2.88.0"
+ }
+ },
+ "undertaker": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz",
+ "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.0.1",
+ "arr-map": "^2.0.0",
+ "bach": "^1.0.0",
+ "collection-map": "^1.0.0",
+ "es6-weak-map": "^2.0.1",
+ "fast-levenshtein": "^1.0.0",
+ "last-run": "^1.1.0",
+ "object.defaults": "^1.0.0",
+ "object.reduce": "^1.0.0",
+ "undertaker-registry": "^1.0.0"
+ },
+ "dependencies": {
+ "fast-levenshtein": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz",
+ "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=",
+ "dev": true
+ }
+ }
+ },
+ "undertaker-registry": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz",
+ "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=",
+ "dev": true
+ },
+ "union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ }
+ },
+ "uniq": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+ "dev": true
+ },
+ "unique-stream": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
+ "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
+ "dev": true,
+ "requires": {
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "through2-filter": "^3.0.0"
+ }
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "dev": true,
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+ "dev": true
+ }
+ }
+ },
+ "upath": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+ "dev": true
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+ "dev": true
+ },
+ "use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ },
+ "v8flags": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
+ "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.1"
+ }
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "value-or-function": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz",
+ "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=",
+ "dev": true
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "vinyl": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz",
+ "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==",
+ "dev": true,
+ "requires": {
+ "clone": "^2.1.1",
+ "clone-buffer": "^1.0.0",
+ "clone-stats": "^1.0.0",
+ "cloneable-readable": "^1.0.0",
+ "remove-trailing-separator": "^1.0.1",
+ "replace-ext": "^1.0.0"
+ }
+ },
+ "vinyl-fs": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz",
+ "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==",
+ "dev": true,
+ "requires": {
+ "fs-mkdirp-stream": "^1.0.0",
+ "glob-stream": "^6.1.0",
+ "graceful-fs": "^4.0.0",
+ "is-valid-glob": "^1.0.0",
+ "lazystream": "^1.0.0",
+ "lead": "^1.0.0",
+ "object.assign": "^4.0.4",
+ "pumpify": "^1.3.5",
+ "readable-stream": "^2.3.3",
+ "remove-bom-buffer": "^3.0.0",
+ "remove-bom-stream": "^1.2.0",
+ "resolve-options": "^1.1.0",
+ "through2": "^2.0.0",
+ "to-through": "^2.0.0",
+ "value-or-function": "^3.0.0",
+ "vinyl": "^2.0.0",
+ "vinyl-sourcemap": "^1.1.0"
+ }
+ },
+ "vinyl-sourcemap": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz",
+ "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=",
+ "dev": true,
+ "requires": {
+ "append-buffer": "^1.0.2",
+ "convert-source-map": "^1.5.0",
+ "graceful-fs": "^4.1.6",
+ "normalize-path": "^2.1.1",
+ "now-and-later": "^2.0.0",
+ "remove-bom-buffer": "^3.0.0",
+ "vinyl": "^2.0.0"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ }
+ }
+ },
+ "w3c-hr-time": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
+ "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
+ "dev": true,
+ "requires": {
+ "browser-process-hrtime": "^1.0.0"
+ }
+ },
+ "w3c-xmlserializer": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz",
+ "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==",
+ "dev": true,
+ "requires": {
+ "domexception": "^1.0.1",
+ "webidl-conversions": "^4.0.2",
+ "xml-name-validator": "^3.0.0"
+ }
+ },
+ "webidl-conversions": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+ "dev": true
+ },
+ "whatwg-encoding": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+ "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+ "dev": true,
+ "requires": {
+ "iconv-lite": "0.4.24"
+ }
+ },
+ "whatwg-mimetype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+ "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+ "dev": true
+ },
+ "whatwg-url": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+ "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+ "dev": true,
+ "requires": {
+ "lodash.sortby": "^4.7.0",
+ "tr46": "^1.0.1",
+ "webidl-conversions": "^4.0.2"
+ }
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
+ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
+ "dev": true
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "ws": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
+ "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0"
+ }
+ },
+ "xml-name-validator": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
+ "dev": true
+ },
+ "xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true
+ },
+ "xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "dev": true
+ },
+ "y18n": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
+ "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz",
+ "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^3.0.0",
+ "cliui": "^3.2.0",
+ "decamelize": "^1.1.1",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^1.4.0",
+ "read-pkg-up": "^1.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^1.0.2",
+ "which-module": "^1.0.0",
+ "y18n": "^3.2.1",
+ "yargs-parser": "5.0.0-security.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "5.0.0-security.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz",
+ "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^3.0.0",
+ "object.assign": "^4.1.0"
+ }
+ },
+ "yaserver": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/yaserver/-/yaserver-0.3.0.tgz",
+ "integrity": "sha512-q7O/gz6B46jA0QZptLxPDBWwojnbpxXwjHGHwWKghsPQBNDcTwOmJ2wTbFAoU6me1M/Z995eATE5THYmGzGE6A==",
+ "dev": true
+ }
+ }
+}
diff --git a/website/package.json b/website/package.json
new file mode 100644
index 0000000..5ef380f
--- /dev/null
+++ b/website/package.json
@@ -0,0 +1,15 @@
+{
+ "scripts": {
+ "prettier": "prettier --write .",
+ "simpleserver": "yaserver --root ./ --port 8888"
+ },
+ "devDependencies": {
+ "clean-css": "^5.1.1",
+ "event-stream": "4.0.1",
+ "gulp": "^4.0.2",
+ "monaco-editor": "^0.23.0",
+ "rimraf": "^3.0.2",
+ "uncss": "^0.17.3",
+ "yaserver": "^0.3.0"
+ }
+}