├── .assembly.xml ├── .detekt.yml ├── .editorconfig ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── ci.yml │ └── nightly.yml ├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── encodings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── libraries │ ├── Maven__com_google_code_findbugs_jsr305_3_0_2.xml │ ├── Maven__com_google_code_gson_gson_2_8_9.xml │ ├── Maven__com_google_errorprone_error_prone_annotations_2_2_0.xml │ ├── Maven__com_google_guava_failureaccess_1_0_1.xml │ ├── Maven__com_google_guava_guava_27_1_jre.xml │ ├── Maven__com_google_guava_listenablefuture_9999_0_empty_to_avoid_conflict_with_guava.xml │ ├── Maven__com_google_j2objc_j2objc_annotations_1_1.xml │ ├── Maven__info_picocli_picocli_4_6_2.xml │ ├── Maven__org_apiguardian_apiguardian_api_1_1_0.xml │ ├── Maven__org_checkerframework_checker_qual_2_5_2.xml │ ├── Maven__org_codehaus_mojo_animal_sniffer_annotations_1_17.xml │ ├── Maven__org_eclipse_lsp4j_org_eclipse_lsp4j_0_12_0.xml │ ├── Maven__org_eclipse_lsp4j_org_eclipse_lsp4j_generator_0_12_0.xml │ ├── Maven__org_eclipse_lsp4j_org_eclipse_lsp4j_jsonrpc_0_12_0.xml │ ├── Maven__org_eclipse_xtend_org_eclipse_xtend_lib_2_24_0.xml │ ├── Maven__org_eclipse_xtend_org_eclipse_xtend_lib_macro_2_24_0.xml │ ├── Maven__org_eclipse_xtext_org_eclipse_xtext_xbase_lib_2_24_0.xml │ ├── Maven__org_fusesource_jansi_jansi_2_4_0.xml │ ├── Maven__org_jetbrains_annotations_13_0.xml │ ├── Maven__org_jetbrains_kotlin_kotlin_stdlib_1_6_0.xml │ ├── Maven__org_jetbrains_kotlin_kotlin_stdlib_common_1_6_0.xml │ ├── Maven__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_6_0.xml │ ├── Maven__org_jetbrains_kotlin_kotlin_stdlib_jdk8_1_6_0.xml │ ├── Maven__org_jetbrains_kotlin_kotlin_test_1_6_0.xml │ ├── Maven__org_jetbrains_kotlin_kotlin_test_junit5_1_6_0.xml │ ├── Maven__org_junit_jupiter_junit_jupiter_5_8_1.xml │ ├── Maven__org_junit_jupiter_junit_jupiter_api_5_6_0.xml │ ├── Maven__org_junit_jupiter_junit_jupiter_engine_5_6_0.xml │ ├── Maven__org_junit_jupiter_junit_jupiter_params_5_8_1.xml │ ├── Maven__org_junit_platform_junit_platform_commons_1_6_0.xml │ ├── Maven__org_junit_platform_junit_platform_engine_1_6_0.xml │ └── Maven__org_opentest4j_opentest4j_1_2_0.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── ACKNOWLEDGMENTS.md ├── LICENSE.md ├── README.md ├── changelog.xml ├── lspcli.iml ├── pom.xml ├── schemas ├── assembly-2.1.0.xsd ├── changes-1.0.0.xsd └── maven-4.0.0.xsd ├── src ├── main │ ├── kotlin │ │ └── org │ │ │ └── bsplines │ │ │ └── lspcli │ │ │ ├── LspCliLauncher.kt │ │ │ ├── client │ │ │ ├── Checker.kt │ │ │ ├── LspCliLanguageClient.kt │ │ │ └── LspCliTextDocumentItem.kt │ │ │ ├── server │ │ │ └── LspCliLanguageServer.kt │ │ │ └── tools │ │ │ ├── FileIo.kt │ │ │ ├── I18n.kt │ │ │ ├── Logging.kt │ │ │ ├── LspCliSettings.kt │ │ │ └── VersionProvider.kt │ └── resources │ │ └── LspCliMessagesBundle.properties └── test │ └── kotlin │ └── org │ └── bsplines │ └── lspcli │ └── tools │ ├── I18nTest.kt │ └── VersionProviderTest.kt └── tools ├── common.py ├── convertChangelog.py ├── createBinaryArchives.py └── inspectWithIntellijIdea.py /.assembly.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | distribution 11 | 12 | tar.gz 13 | 14 | 15 | 16 | ${basedir}/target/appassembler 17 | 18 | 19 | 20 | ${basedir} 21 | 22 | ACKNOWLEDGMENTS.md 23 | changelog.xml 24 | LICENSE.md 25 | README.md 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /.detekt.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | complexity: 8 | LongMethod: 9 | threshold: 75 10 | empty-blocks: 11 | EmptyFunctionBlock: 12 | active: false 13 | naming: 14 | ConstructorParameterNaming: 15 | privateParameterPattern: "_?[a-z][A-Za-z0-9]*" 16 | style: 17 | ReturnCount: 18 | max: 6 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | [*.{kt,kts}] 8 | ij_kotlin_allow_trailing_comma = true 9 | ij_kotlin_allow_trailing_comma_on_call_site = true 10 | indent_size = 2 11 | insert_final_newline = true 12 | max_line_length = 100 13 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | ko_fi: valentjn 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | version: 2 8 | updates: 9 | - package-ecosystem: "maven" 10 | directory: "/" 11 | schedule: 12 | interval: "weekly" 13 | day: "sunday" 14 | open-pull-requests-limit: 10 15 | assignees: 16 | - "valentjn" 17 | labels: 18 | - "pr-dependabot 🤖" 19 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | name: "CI" 8 | 9 | on: 10 | push: 11 | branches: 12 | - "*" 13 | tags: 14 | - "*" 15 | pull_request: 16 | branches: 17 | - "develop" 18 | workflow_dispatch: 19 | 20 | jobs: 21 | build: 22 | name: "CI - Build Job" 23 | runs-on: "${{ matrix.os }}" 24 | 25 | strategy: 26 | matrix: 27 | os: 28 | - "ubuntu-20.04" 29 | - "macos-11.0" 30 | - "windows-2019" 31 | 32 | steps: 33 | - name: "Checkout Repository" 34 | uses: "actions/checkout@v2" 35 | 36 | - name: "Set up Java" 37 | uses: "actions/setup-java@v1" 38 | with: 39 | java-version: "11.0.9" 40 | 41 | - name: "Build lsp-cli" 42 | run: "mvn -B -e verify" 43 | 44 | inspect: 45 | name: "CI - Inspect Job" 46 | runs-on: "ubuntu-20.04" 47 | 48 | steps: 49 | - name: "Checkout Repository" 50 | uses: "actions/checkout@v2" 51 | 52 | - name: "Set up Java" 53 | uses: "actions/setup-java@v1" 54 | with: 55 | java-version: "11.0.9" 56 | 57 | - name: "Set up Python" 58 | uses: "actions/setup-python@v2" 59 | with: 60 | python-version: "3.9.0" 61 | 62 | - name: "Build lsp-cli" 63 | run: "mvn -B -e verify" 64 | 65 | - name: "Set LSP_CLI_IDEA_ARCHIVE_NAME and LSP_CLI_IDEA_DIRECTORY_NAME" 66 | run: "echo -e \"LSP_CLI_IDEA_ARCHIVE_NAME=ideaIC-2021.2.tar.gz\nLSP_CLI_IDEA_DIRECTORY_NAME=idea-IC-212.4746.92\" >> $GITHUB_ENV" 67 | 68 | - name: "Load IntelliJ IDEA from Cache" 69 | uses: "actions/cache@v2" 70 | with: 71 | path: "${{ env.LSP_CLI_IDEA_DIRECTORY_NAME }}" 72 | key: "${{ env.LSP_CLI_IDEA_DIRECTORY_NAME }}-${{ runner.os }}-v1" 73 | 74 | - name: "Download and Extract IntelliJ IDEA If Necessary" 75 | run: "[[ -d ${{ env.LSP_CLI_IDEA_DIRECTORY_NAME }} ]] || { wget https://download.jetbrains.com/idea/${{ env.LSP_CLI_IDEA_ARCHIVE_NAME }} && tar -xzf ${{ env.LSP_CLI_IDEA_ARCHIVE_NAME }}; }" 76 | 77 | - name: "Inspect Code with IntelliJ IDEA" 78 | run: "python tools/inspectWithIntellijIdea.py --idea-path ${{ env.LSP_CLI_IDEA_DIRECTORY_NAME }}" 79 | 80 | validate: 81 | name: "CI - Validate Job" 82 | runs-on: "ubuntu-20.04" 83 | 84 | steps: 85 | - name: "Checkout Repository" 86 | uses: "actions/checkout@v2" 87 | 88 | - name: "Set up Python" 89 | uses: "actions/setup-python@v2" 90 | with: 91 | python-version: "3.9.0" 92 | 93 | - name: "Install Python Dependencies" 94 | run: "python -m pip install --upgrade pip && pip install xmlschema==1.6.4" 95 | 96 | - name: "Validate .assembly.xml" 97 | run: "python -c 'import xmlschema; xmlschema.XMLSchema(\"schemas/assembly-2.1.0.xsd\").validate(\".assembly.xml\")'" 98 | 99 | - name: "Validate changelog.xml" 100 | run: "python -c 'import xmlschema; xmlschema.XMLSchema(\"schemas/changes-1.0.0.xsd\").validate(\"changelog.xml\")'" 101 | 102 | - name: "Validate pom.xml" 103 | run: "python -c 'import xmlschema; xmlschema.XMLSchema(\"schemas/maven-4.0.0.xsd\").validate(\"pom.xml\")'" 104 | 105 | upload_coverage: 106 | name: "CI - Upload Coverage Job" 107 | if: "${{ github.event_name != 'pull_request' }}" 108 | runs-on: "ubuntu-20.04" 109 | 110 | steps: 111 | - name: "Checkout Repository" 112 | uses: "actions/checkout@v2" 113 | 114 | - name: "Set up Java" 115 | uses: "actions/setup-java@v1" 116 | with: 117 | java-version: "11.0.9" 118 | 119 | - name: "Build lsp-cli" 120 | run: "mvn -B -e verify" 121 | 122 | - name: "Upload Coverage Report to Coveralls" 123 | env: 124 | LSP_CLI_COVERALLS_REPOSITORY_TOKEN: "${{ secrets.LSP_CLI_COVERALLS_REPOSITORY_TOKEN }}" 125 | run: "mvn -B -e coveralls:report \"-DrepoToken=$LSP_CLI_COVERALLS_REPOSITORY_TOKEN\"" 126 | 127 | deploy: 128 | name: "CI - Deploy Job" 129 | needs: 130 | - "build" 131 | - "inspect" 132 | - "validate" 133 | - "upload_coverage" 134 | if: "${{ startsWith(github.ref, 'refs/tags/') }}" 135 | runs-on: "ubuntu-20.04" 136 | 137 | steps: 138 | - name: "Checkout Repository" 139 | uses: "actions/checkout@v2" 140 | 141 | - name: "Set up Java" 142 | uses: "actions/setup-java@v1" 143 | with: 144 | java-version: "11.0.9" 145 | 146 | - name: "Set up Python" 147 | uses: "actions/setup-python@v2" 148 | with: 149 | python-version: "3.9.0" 150 | 151 | - name: "Install Python Dependencies" 152 | run: "python -m pip install --upgrade pip && pip install semver==2.13.0" 153 | 154 | - name: "Set LSP_CLI_VERSION" 155 | run: "echo \"LSP_CLI_VERSION=$(python -c \"import re; print(re.search(r'(.*?)', open('pom.xml', 'r').read()).group(1), end='')\")\" >> $GITHUB_ENV" 156 | 157 | - name: "Check LSP_CLI_VERSION" 158 | run: "if [[ -z \"$LSP_CLI_VERSION\" ]]; then echo 'Error: LSP_CLI_VERSION not set!'; (exit 1); fi; echo \"LSP_CLI_VERSION set to '$LSP_CLI_VERSION'\"" 159 | 160 | - name: "Set LSP_CLI_IS_PRERELEASE" 161 | run: "if [[ -z \"$LSP_CLI_VERSION\" ]]; then echo 'Error: LSP_CLI_VERSION not set!'; (exit 1); fi; echo \"LSP_CLI_IS_PRERELEASE=$(python -c \"import semver; print('true' if semver.VersionInfo.parse('$LSP_CLI_VERSION').prerelease is not None else 'false', end='')\")\" >> $GITHUB_ENV" 162 | 163 | - name: "Check LSP_CLI_IS_PRERELEASE" 164 | run: "if [[ -z \"$LSP_CLI_IS_PRERELEASE\" ]]; then echo 'Error: LSP_CLI_IS_PRERELEASE not set!'; (exit 1); fi; echo \"LSP_CLI_IS_PRERELEASE set to '$LSP_CLI_IS_PRERELEASE'\"" 165 | 166 | - name: "Set LSP_CLI_CHANGELOG" 167 | run: "if [ \"$LSP_CLI_IS_PRERELEASE\" = \"false\" ]; then echo \"LSP_CLI_CHANGELOG<> $GITHUB_ENV; python tools/convertChangelog.py --xml-file changelog.xml --version latest >> $GITHUB_ENV; echo \"EOF\" >> $GITHUB_ENV; else echo \"LSP_CLI_CHANGELOG=This is a pre-release. Use at your own risk.\" >> $GITHUB_ENV; fi" 168 | 169 | - name: "Build lsp-cli" 170 | run: "mvn -B -e package" 171 | 172 | - name: "Create Binary Archives" 173 | run: "python tools/createBinaryArchives.py" 174 | 175 | - name: "Create GitHub Release" 176 | uses: "softprops/action-gh-release@v0.1.8" 177 | with: 178 | token: "${{ secrets.LSP_CLI_CREATE_GITHUB_RELEASE_TOKEN }}" 179 | prerelease: "${{ env.LSP_CLI_IS_PRERELEASE }}" 180 | body: "${{ env.LSP_CLI_CHANGELOG }}" 181 | files: "target/lsp-cli-${{ env.LSP_CLI_VERSION }}.tar.gz\ntarget/lsp-cli-${{ env.LSP_CLI_VERSION }}-linux-x64.tar.gz\ntarget/lsp-cli-${{ env.LSP_CLI_VERSION }}-mac-x64.tar.gz\ntarget/lsp-cli-${{ env.LSP_CLI_VERSION }}-windows-x64.zip" 182 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | name: "Nightly" 8 | on: 9 | schedule: 10 | - cron: "0 3 * * *" 11 | workflow_dispatch: 12 | 13 | jobs: 14 | deploy: 15 | name: "Nightly - Deploy Job" 16 | runs-on: "ubuntu-20.04" 17 | 18 | steps: 19 | - name: "Checkout Repository" 20 | uses: "actions/checkout@v2" 21 | 22 | - name: "Set up Java" 23 | uses: "actions/setup-java@v1" 24 | with: 25 | java-version: "11.0.9" 26 | 27 | - name: "Set up Python" 28 | uses: "actions/setup-python@v2" 29 | with: 30 | python-version: "3.9.0" 31 | 32 | - name: "Install Python Dependencies" 33 | run: "python -m pip install --upgrade pip && pip install semver==2.13.0" 34 | 35 | - name: "Set LSP_CLI_VERSION" 36 | run: "echo \"LSP_CLI_VERSION=$(python -c \"import datetime; import re; print('{}.nightly.{}'.format(re.search(r'(.*?)(?:\\\\.develop)?', open('pom.xml', 'r').read()).group(1), datetime.datetime.today().strftime('%Y-%m-%d')), end='')\")\" >> $GITHUB_ENV" 37 | 38 | - name: "Check LSP_CLI_VERSION" 39 | run: "if [[ -z \"$LSP_CLI_VERSION\" ]]; then echo 'Error: LSP_CLI_VERSION not set!'; (exit 1); fi; echo \"LSP_CLI_VERSION set to '$LSP_CLI_VERSION'\"" 40 | 41 | - name: "Bump Version" 42 | run: "python -c \"import re\nfile = open('pom.xml', 'r+'); pom = file.read(); file.seek(0); file.truncate(); file.write(re.sub(r'(.*?)', '${{ env.LSP_CLI_VERSION }}', pom, 1))\"" 43 | 44 | - name: "Build lsp-cli" 45 | run: "mvn -B -e package" 46 | 47 | - name: "Create Binary Archives" 48 | run: "python tools/createBinaryArchives.py" 49 | 50 | - name: "Delete Old Nightly Releases" 51 | uses: "dev-drprasad/delete-older-releases@v0.2.0" 52 | with: 53 | keep_latest: 0 54 | delete_tag_pattern: "nightly" 55 | env: 56 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 57 | 58 | - name: "Update Nightly Tag" 59 | run: "git tag -f nightly && git push -f origin nightly" 60 | 61 | - name: "Create GitHub Release" 62 | uses: "softprops/action-gh-release@v0.1.8" 63 | env: 64 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 65 | with: 66 | tag_name: "nightly" 67 | name: "${{ env.LSP_CLI_VERSION }}" 68 | prerelease: true 69 | body: "This is a nightly build. Use at your own risk." 70 | files: "target/lsp-cli-${{ env.LSP_CLI_VERSION }}.tar.gz\ntarget/lsp-cli-${{ env.LSP_CLI_VERSION }}-linux-x64.tar.gz\ntarget/lsp-cli-${{ env.LSP_CLI_VERSION }}-mac-x64.tar.gz\ntarget/lsp-cli-${{ env.LSP_CLI_VERSION }}-windows-x64.zip" 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | 3 | /.classpath 4 | /.project 5 | 6 | /target/ 7 | 8 | /.vscode/ltex.*.txt 9 | /.vscode/settings.json 10 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 22 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_google_code_findbugs_jsr305_3_0_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_google_code_gson_gson_2_8_9.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_google_errorprone_error_prone_annotations_2_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_google_guava_failureaccess_1_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_google_guava_guava_27_1_jre.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_google_guava_listenablefuture_9999_0_empty_to_avoid_conflict_with_guava.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_google_j2objc_j2objc_annotations_1_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__info_picocli_picocli_4_6_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_checkerframework_checker_qual_2_5_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_codehaus_mojo_animal_sniffer_annotations_1_17.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_eclipse_lsp4j_org_eclipse_lsp4j_0_12_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_eclipse_lsp4j_org_eclipse_lsp4j_generator_0_12_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_eclipse_lsp4j_org_eclipse_lsp4j_jsonrpc_0_12_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_eclipse_xtend_org_eclipse_xtend_lib_2_24_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_eclipse_xtend_org_eclipse_xtend_lib_macro_2_24_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_eclipse_xtext_org_eclipse_xtext_xbase_lib_2_24_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_fusesource_jansi_jansi_2_4_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_jetbrains_annotations_13_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_jetbrains_kotlin_kotlin_stdlib_1_6_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_jetbrains_kotlin_kotlin_stdlib_common_1_6_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_6_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_jetbrains_kotlin_kotlin_stdlib_jdk8_1_6_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_jetbrains_kotlin_kotlin_test_1_6_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_jetbrains_kotlin_kotlin_test_junit5_1_6_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_8_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_8_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Mozilla Public License Version 2.0 2 | 3 | ## 1. Definitions 4 | 5 | **1.1. “Contributor”** 6 | means each individual or legal entity that creates, contributes to 7 | the creation of, or owns Covered Software. 8 | 9 | **1.2. “Contributor Version”** 10 | means the combination of the Contributions of others (if any) used 11 | by a Contributor and that particular Contributor's Contribution. 12 | 13 | **1.3. “Contribution”** 14 | means Covered Software of a particular Contributor. 15 | 16 | **1.4. “Covered Software”** 17 | means Source Code Form to which the initial Contributor has attached 18 | the notice in Exhibit A, the Executable Form of such Source Code 19 | Form, and Modifications of such Source Code Form, in each case 20 | including portions thereof. 21 | 22 | **1.5. “Incompatible With Secondary Licenses”** 23 | means 24 | 25 | * **(a)** that the initial Contributor has attached the notice described 26 | in Exhibit B to the Covered Software; or 27 | * **(b)** that the Covered Software was made available under the terms of 28 | version 1.1 or earlier of the License, but not also under the 29 | terms of a Secondary License. 30 | 31 | **1.6. “Executable Form”** 32 | means any form of the work other than Source Code Form. 33 | 34 | **1.7. “Larger Work”** 35 | means a work that combines Covered Software with other material, in 36 | a separate file or files, that is not Covered Software. 37 | 38 | **1.8. “License”** 39 | means this document. 40 | 41 | **1.9. “Licensable”** 42 | means having the right to grant, to the maximum extent possible, 43 | whether at the time of the initial grant or subsequently, any and 44 | all of the rights conveyed by this License. 45 | 46 | **1.10. “Modifications”** 47 | means any of the following: 48 | 49 | * **(a)** any file in Source Code Form that results from an addition to, 50 | deletion from, or modification of the contents of Covered 51 | Software; or 52 | * **(b)** any new file in Source Code Form that contains any Covered 53 | Software. 54 | 55 | **1.11. “Patent Claims” of a Contributor** 56 | means any patent claim(s), including without limitation, method, 57 | process, and apparatus claims, in any patent Licensable by such 58 | Contributor that would be infringed, but for the grant of the 59 | License, by the making, using, selling, offering for sale, having 60 | made, import, or transfer of either its Contributions or its 61 | Contributor Version. 62 | 63 | **1.12. “Secondary License”** 64 | means either the GNU General Public License, Version 2.0, the GNU 65 | Lesser General Public License, Version 2.1, the GNU Affero General 66 | Public License, Version 3.0, or any later versions of those 67 | licenses. 68 | 69 | **1.13. “Source Code Form”** 70 | means the form of the work preferred for making modifications. 71 | 72 | **1.14. “You” (or “Your”)** 73 | means an individual or a legal entity exercising rights under this 74 | License. For legal entities, “You” includes any entity that 75 | controls, is controlled by, or is under common control with You. For 76 | purposes of this definition, “control” means **(a)** the power, direct 77 | or indirect, to cause the direction or management of such entity, 78 | whether by contract or otherwise, or **(b)** ownership of more than 79 | fifty percent (50%) of the outstanding shares or beneficial 80 | ownership of such entity. 81 | 82 | ## 2. License Grants and Conditions 83 | 84 | ### 2.1. Grants 85 | 86 | Each Contributor hereby grants You a world-wide, royalty-free, 87 | non-exclusive license: 88 | 89 | * **(a)** under intellectual property rights (other than patent or trademark) 90 | Licensable by such Contributor to use, reproduce, make available, 91 | modify, display, perform, distribute, and otherwise exploit its 92 | Contributions, either on an unmodified basis, with Modifications, or 93 | as part of a Larger Work; and 94 | * **(b)** under Patent Claims of such Contributor to make, use, sell, offer 95 | for sale, have made, import, and otherwise transfer either its 96 | Contributions or its Contributor Version. 97 | 98 | ### 2.2. Effective Date 99 | 100 | The licenses granted in Section 2.1 with respect to any Contribution 101 | become effective for each Contribution on the date the Contributor first 102 | distributes such Contribution. 103 | 104 | ### 2.3. Limitations on Grant Scope 105 | 106 | The licenses granted in this Section 2 are the only rights granted under 107 | this License. No additional rights or licenses will be implied from the 108 | distribution or licensing of Covered Software under this License. 109 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 110 | Contributor: 111 | 112 | * **(a)** for any code that a Contributor has removed from Covered Software; 113 | or 114 | * **(b)** for infringements caused by: **(i)** Your and any other third party's 115 | modifications of Covered Software, or **(ii)** the combination of its 116 | Contributions with other software (except as part of its Contributor 117 | Version); or 118 | * **(c)** under Patent Claims infringed by Covered Software in the absence of 119 | its Contributions. 120 | 121 | This License does not grant any rights in the trademarks, service marks, 122 | or logos of any Contributor (except as may be necessary to comply with 123 | the notice requirements in Section 3.4). 124 | 125 | ### 2.4. Subsequent Licenses 126 | 127 | No Contributor makes additional grants as a result of Your choice to 128 | distribute the Covered Software under a subsequent version of this 129 | License (see Section 10.2) or under the terms of a Secondary License (if 130 | permitted under the terms of Section 3.3). 131 | 132 | ### 2.5. Representation 133 | 134 | Each Contributor represents that the Contributor believes its 135 | Contributions are its original creation(s) or it has sufficient rights 136 | to grant the rights to its Contributions conveyed by this License. 137 | 138 | ### 2.6. Fair Use 139 | 140 | This License is not intended to limit any rights You have under 141 | applicable copyright doctrines of fair use, fair dealing, or other 142 | equivalents. 143 | 144 | ### 2.7. Conditions 145 | 146 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 147 | in Section 2.1. 148 | 149 | ## 3. Responsibilities 150 | 151 | ### 3.1. Distribution of Source Form 152 | 153 | All distribution of Covered Software in Source Code Form, including any 154 | Modifications that You create or to which You contribute, must be under 155 | the terms of this License. You must inform recipients that the Source 156 | Code Form of the Covered Software is governed by the terms of this 157 | License, and how they can obtain a copy of this License. You may not 158 | attempt to alter or restrict the recipients' rights in the Source Code 159 | Form. 160 | 161 | ### 3.2. Distribution of Executable Form 162 | 163 | If You distribute Covered Software in Executable Form then: 164 | 165 | * **(a)** such Covered Software must also be made available in Source Code 166 | Form, as described in Section 3.1, and You must inform recipients of 167 | the Executable Form how they can obtain a copy of such Source Code 168 | Form by reasonable means in a timely manner, at a charge no more 169 | than the cost of distribution to the recipient; and 170 | 171 | * **(b)** You may distribute such Executable Form under the terms of this 172 | License, or sublicense it under different terms, provided that the 173 | license for the Executable Form does not attempt to limit or alter 174 | the recipients' rights in the Source Code Form under this License. 175 | 176 | ### 3.3. Distribution of a Larger Work 177 | 178 | You may create and distribute a Larger Work under terms of Your choice, 179 | provided that You also comply with the requirements of this License for 180 | the Covered Software. If the Larger Work is a combination of Covered 181 | Software with a work governed by one or more Secondary Licenses, and the 182 | Covered Software is not Incompatible With Secondary Licenses, this 183 | License permits You to additionally distribute such Covered Software 184 | under the terms of such Secondary License(s), so that the recipient of 185 | the Larger Work may, at their option, further distribute the Covered 186 | Software under the terms of either this License or such Secondary 187 | License(s). 188 | 189 | ### 3.4. Notices 190 | 191 | You may not remove or alter the substance of any license notices 192 | (including copyright notices, patent notices, disclaimers of warranty, 193 | or limitations of liability) contained within the Source Code Form of 194 | the Covered Software, except that You may alter any license notices to 195 | the extent required to remedy known factual inaccuracies. 196 | 197 | ### 3.5. Application of Additional Terms 198 | 199 | You may choose to offer, and to charge a fee for, warranty, support, 200 | indemnity or liability obligations to one or more recipients of Covered 201 | Software. However, You may do so only on Your own behalf, and not on 202 | behalf of any Contributor. You must make it absolutely clear that any 203 | such warranty, support, indemnity, or liability obligation is offered by 204 | You alone, and You hereby agree to indemnify every Contributor for any 205 | liability incurred by such Contributor as a result of warranty, support, 206 | indemnity or liability terms You offer. You may include additional 207 | disclaimers of warranty and limitations of liability specific to any 208 | jurisdiction. 209 | 210 | ## 4. Inability to Comply Due to Statute or Regulation 211 | 212 | If it is impossible for You to comply with any of the terms of this 213 | License with respect to some or all of the Covered Software due to 214 | statute, judicial order, or regulation then You must: **(a)** comply with 215 | the terms of this License to the maximum extent possible; and **(b)** 216 | describe the limitations and the code they affect. Such description must 217 | be placed in a text file included with all distributions of the Covered 218 | Software under this License. Except to the extent prohibited by statute 219 | or regulation, such description must be sufficiently detailed for a 220 | recipient of ordinary skill to be able to understand it. 221 | 222 | ## 5. Termination 223 | 224 | **5.1.** The rights granted under this License will terminate automatically 225 | if You fail to comply with any of its terms. However, if You become 226 | compliant, then the rights granted under this License from a particular 227 | Contributor are reinstated **(a)** provisionally, unless and until such 228 | Contributor explicitly and finally terminates Your grants, and **(b)** on an 229 | ongoing basis, if such Contributor fails to notify You of the 230 | non-compliance by some reasonable means prior to 60 days after You have 231 | come back into compliance. Moreover, Your grants from a particular 232 | Contributor are reinstated on an ongoing basis if such Contributor 233 | notifies You of the non-compliance by some reasonable means, this is the 234 | first time You have received notice of non-compliance with this License 235 | from such Contributor, and You become compliant prior to 30 days after 236 | Your receipt of the notice. 237 | 238 | **5.2.** If You initiate litigation against any entity by asserting a patent 239 | infringement claim (excluding declaratory judgment actions, 240 | counter-claims, and cross-claims) alleging that a Contributor Version 241 | directly or indirectly infringes any patent, then the rights granted to 242 | You by any and all Contributors for the Covered Software under Section 243 | 2.1 of this License shall terminate. 244 | 245 | **5.3.** In the event of termination under Sections 5.1 or 5.2 above, all 246 | end user license agreements (excluding distributors and resellers) which 247 | have been validly granted by You or Your distributors under this License 248 | prior to termination shall survive termination. 249 | 250 | ## 6. Disclaimer of Warranty 251 | 252 | > Covered Software is provided under this License on an “as is” 253 | > basis, without warranty of any kind, either expressed, implied, or 254 | > statutory, including, without limitation, warranties that the 255 | > Covered Software is free of defects, merchantable, fit for a 256 | > particular purpose or non-infringing. The entire risk as to the 257 | > quality and performance of the Covered Software is with You. 258 | > Should any Covered Software prove defective in any respect, You 259 | > (not any Contributor) assume the cost of any necessary servicing, 260 | > repair, or correction. This disclaimer of warranty constitutes an 261 | > essential part of this License. No use of any Covered Software is 262 | > authorized under this License except under this disclaimer. 263 | 264 | ## 7. Limitation of Liability 265 | 266 | > Under no circumstances and under no legal theory, whether tort 267 | > (including negligence), contract, or otherwise, shall any 268 | > Contributor, or anyone who distributes Covered Software as 269 | > permitted above, be liable to You for any direct, indirect, 270 | > special, incidental, or consequential damages of any character 271 | > including, without limitation, damages for lost profits, loss of 272 | > goodwill, work stoppage, computer failure or malfunction, or any 273 | > and all other commercial damages or losses, even if such party 274 | > shall have been informed of the possibility of such damages. This 275 | > limitation of liability shall not apply to liability for death or 276 | > personal injury resulting from such party's negligence to the 277 | > extent applicable law prohibits such limitation. Some 278 | > jurisdictions do not allow the exclusion or limitation of 279 | > incidental or consequential damages, so this exclusion and 280 | > limitation may not apply to You. 281 | 282 | ## 8. Litigation 283 | 284 | Any litigation relating to this License may be brought only in the 285 | courts of a jurisdiction where the defendant maintains its principal 286 | place of business and such litigation shall be governed by laws of that 287 | jurisdiction, without reference to its conflict-of-law provisions. 288 | Nothing in this Section shall prevent a party's ability to bring 289 | cross-claims or counter-claims. 290 | 291 | ## 9. Miscellaneous 292 | 293 | This License represents the complete agreement concerning the subject 294 | matter hereof. If any provision of this License is held to be 295 | unenforceable, such provision shall be reformed only to the extent 296 | necessary to make it enforceable. Any law or regulation which provides 297 | that the language of a contract shall be construed against the drafter 298 | shall not be used to construe this License against a Contributor. 299 | 300 | ## 10. Versions of the License 301 | 302 | ### 10.1. New Versions 303 | 304 | Mozilla Foundation is the license steward. Except as provided in Section 305 | 10.3, no one other than the license steward has the right to modify or 306 | publish new versions of this License. Each version will be given a 307 | distinguishing version number. 308 | 309 | ### 10.2. Effect of New Versions 310 | 311 | You may distribute the Covered Software under the terms of the version 312 | of the License under which You originally received the Covered Software, 313 | or under the terms of any subsequent version published by the license 314 | steward. 315 | 316 | ### 10.3. Modified Versions 317 | 318 | If you create software not governed by this License, and you want to 319 | create a new license for such software, you may create and use a 320 | modified version of this License if you rename the license and remove 321 | any references to the name of the license steward (except to note that 322 | such modified license differs from this License). 323 | 324 | ### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 325 | 326 | If You choose to distribute Source Code Form that is Incompatible With 327 | Secondary Licenses under the terms of this version of the License, the 328 | notice described in Exhibit B of this License must be attached. 329 | 330 | ## Exhibit A - Source Code Form License Notice 331 | 332 | This Source Code Form is subject to the terms of the Mozilla Public 333 | License, v. 2.0. If a copy of the MPL was not distributed with this 334 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 335 | 336 | If it is not possible or desirable to put the notice in a particular 337 | file, then You may include the notice in a location (such as a LICENSE 338 | file in a relevant directory) where a recipient would be likely to look 339 | for such a notice. 340 | 341 | You may add additional accurate notices of copyright ownership. 342 | 343 | ## Exhibit B - “Incompatible With Secondary Licenses” Notice 344 | 345 | This Source Code Form is "Incompatible With Secondary Licenses", as 346 | defined by the Mozilla Public License, v. 2.0. 347 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | # `lsp-cli` — CLI Language Client for LSP Language Servers 10 | 11 | `lsp-cli` implements a language client according to the [Language Server Protocol (LSP)](https://microsoft.github.io/language-server-protocol/) with a command-line interface (CLI). 12 | 13 | Language servers written for the LSP can usually only be used with a language client, which is typically an editor like VS Code. With `lsp-cli`, language servers can also be used on the command line. This allows you to harness the power of language servers for scripting, build pipelines, etc. 14 | 15 | Most [existing language servers](https://microsoft.github.io/language-server-protocol/implementors/servers/) should be supported, but `lsp-cli` was created for [LTEX LS](https://github.com/valentjn/ltex-ls) (a language server for LanguageTool), which is primary use case of `lsp-cli` and drives its development. 16 | 17 | ## Features 18 | 19 | - Printing diagnostics (linting) for each checked file 20 | - Printing code actions for each diagnostic 21 | - Supplying client configuration to the server 22 | - Customization of usage message and default argument values via JSON file (path supplied by environment variable) 23 | 24 | If you'd like support for other LSP features as well, please open a feature request or a pull request. 25 | 26 | ## Known Limitations 27 | 28 | - Client-side requests of diagnostics are currently not supported by the LSP (see [microsoft/language-server-protocol#737](https://github.com/microsoft/language-server-protocol/issues/737) and [current proposal for LSP 3.17](https://github.com/microsoft/vscode-languageserver-node/blob/eba6a7308b21ab94bd412fbfa63e36964b6d82ad/protocol/src/common/proposed.diagnostics.md)). Therefore, `lsp-cli` relies on the server sending a `textDocument/publishDiagnostics` notification for every opened file, even if there are no diagnostics. 29 | 30 | ## Requirements 31 | 32 | - 64-bit Linux, Mac, or Windows operating system; alternatively, an arbitrary operating system with Java installed 33 | 34 | ## Installation 35 | 36 | 1. Download the [latest release](https://github.com/valentjn/lsp-cli/releases/latest) from GitHub. 37 | - It's recommended that you choose the archive corresponding to your platform (these archives are standalone, no Java installation necessary). 38 | - If you choose the platform-independent file `lsp-cli-VERSION.tar.gz`, then you need Java 11 or later on your computer. 39 | 2. Extract the archive to an arbitrary location on your computer. 40 | 41 | ## Startup 42 | 43 | It is recommended to use the startup scripts `bin/lsp-cli` (Linux, Mac) and `bin\lsp-cli.bat` (Windows) to start `lsp-cli`. These scripts are only part of the released versions. The startup scripts can be controlled by the following environment variables: 44 | 45 | - `JAVA_HOME`: Path to the directory of the Java distribution to use (contains `bin`, `lib`, and other subdirectories). If set, this overrides the included Java distribution when using a platform-dependent `lsp-cli` archive. 46 | - `JAVA_OPTS`: Java arguments to be fed to `java` (e.g., `-Xmx1024m`) 47 | 48 | It is also possible to start `lsp-cli` directly without the startup scripts (not recommended). 49 | 50 | ### Command-Line Arguments 51 | 52 | Any command-line arguments supplied to the startup scripts are processed by `lsp-cli` itself. The possible arguments are as follows: 53 | 54 | - `--client-configuration=`: Use the client configuration stored in the JSON file ``. The format is usually nested JSON objects (e.g., `{"latex": {"commands": ...}}`).\ 55 | Only for LTEX LS: A flattened JSON object (`{"latex.commands": ...}`) is also allowed, and setting names may be prefixed by a top level named `ltex` (e.g., `{"ltex.latex.commands": ...}` is accepted as well). 56 | - `-h`, `--help`: Show help message and exit. 57 | - `--hide-commands`: Hide commands in lists of code actions for diagnostics, only show quick fixes. 58 | - `--server-command-line=`: Required. Command line to start the language server, starting with the path of its executable. If you want to supply arguments to the language server, separate them with spaces. If the path of the executable or one of the arguments contain spaces, you can escape them by using `\ ` instead. In `.lsp-cli.json`, this option can either be specified as an array of arguments or as a string with space-separated arguments. 59 | - `--server-working-directory=`: Working directory for `--server-command-line`. If omitted, use the parent directory of `.lsp-cli.json` if given, otherwise use the current working directory. 60 | - `-V`, `--version`: Print version information as JSON to the standard output and exit. The format is a JSON object with `"java"` and `"lsp-cli"` keys and string values. A key may be missing if no information about the corresponding version could be retrieved. 61 | - `--verbose`: Write to standard error output what is being done. 62 | - ` ...`: Required. Paths of files or directories to check. Directories are traversed recursively for supported file types. If `-` is given, standard input will be checked as plain text. 63 | 64 | Instead of using the equals sign `=` to separate option names and values, it is also possible to use one or more spaces. 65 | 66 | ### `.lsp-cli.json` Configuration File 67 | 68 | The appearance of help messages (e.g., `--help`) and the default values of arguments can also be controlled via a special JSON file, which is usually named `.lsp-cli.json`. 69 | 70 | The file is located via checking the environment variable `LSP_CLI_JSON_SETTINGS_PATH`. If this path points to a file, then that file will be used. If it points to a directory, then the file named `.lsp-cli.json` in that directory will be used. The behavior of `lsp-cli` is controlled by the command-line arguments as usual, if `LSP_CLI_JSON_SETTINGS_PATH` is not set or the path it contains does not exist. 71 | 72 | The JSON file is given below in TypeScript. When specifying arguments, always use their full names as strings (e.g., `"--server-working-directory"`). 73 | 74 | ```typescript 75 | interface LspCliJson { 76 | /** 77 | * Program name to use in help and error messages (default: `lsp-cli`). 78 | */ 79 | programName?: string; 80 | 81 | helpMessage?: { 82 | /** 83 | * Description of the program. 84 | */ 85 | description?: string; 86 | 87 | /** 88 | * List of arguments to hide. 89 | * If both `hiddenArguments` and `visibleArguments` are given, `visibleArguments` wins. 90 | */ 91 | hiddenArguments?: string[]; 92 | 93 | /** 94 | * List of arguments to show; hide all other arguments. 95 | * If both `hiddenArguments` and `visibleArguments` are given, `visibleArguments` wins. 96 | */ 97 | visibleArguments?: string[]; 98 | }; 99 | 100 | defaultValues?: { 101 | /** 102 | * Default values for arguments, if they are not specified on the command line. 103 | * The types of the values depend on the arguments (e.g., use `true` for Boolean flags). 104 | */ 105 | [argument: string]: any; 106 | }; 107 | } 108 | ``` 109 | 110 | ### Exit Codes 111 | 112 | - 0: `lsp-cli` exited successfully, and the language server reported no diagnostics for the checked files. 113 | - 1: An exception was thrown during the execution of `lsp-cli`. 114 | - 2: An invalid command-line argument was supplied to `lsp-cli`. 115 | - 3: The language server reported at least one diagnostic for the checked files. 116 | -------------------------------------------------------------------------------- /changelog.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | Changelog 12 | Julian Valentin, lsp-cli Development Community 13 | 14 | 15 | 16 | 17 | Bump Kotlin to 1.6.0 18 | 19 | 20 | Bump picocli to 4.6.2 21 | 22 | 23 | 24 | 25 | Fix language server sometimes not terminated 26 | 27 | 28 | Fix error when running `lsp-cli` from a different directory than `bin/` 29 | 30 | 31 | Fix usage help of command-line arguments 32 | 33 | 34 | 35 | 36 | Rename internal messages bundle for internationalization to avoid name clashes 37 | 38 | 39 | 40 | 41 | Initial release 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /lspcli.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 14 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 4.0.0 11 | org.bsplines 12 | lspcli 13 | 1.0.4-alpha.1.develop 14 | ${project.groupId}:${project.artifactId} 15 | lsp-cli: CLI language client for LSP language servers 16 | https://github.com/valentjn/lsp-cli 17 | 18 | scm:git:git://github.com/valentjn/lsp-cli.git 19 | scm:git:ssh://github.com:valentjn/lsp-cli.git 20 | https://github.com/valentjn/lsp-cli/tree/develop 21 | 22 | 23 | 24 | valentjn 25 | Julian Valentin 26 | 27 | 28 | 29 | 30 | Mozilla Public License, Version 2.0 31 | https://mozilla.org/MPL/2.0/ 32 | 33 | 34 | 35 | 11 36 | 1.6.10 37 | 1.6 38 | ${java.version} 39 | true 40 | org.bsplines.lspcli.LspCliLauncher 41 | UTF-8 42 | 5.8.2 43 | 44 | 45 | 46 | org.jetbrains.kotlin 47 | kotlin-stdlib-jdk8 48 | ${kotlin.version} 49 | 50 | 51 | org.eclipse.lsp4j 52 | org.eclipse.lsp4j 53 | 0.12.0 54 | 55 | 56 | com.google.code.gson 57 | gson 58 | 2.8.9 59 | 60 | 61 | org.fusesource.jansi 62 | jansi 63 | 2.4.0 64 | 65 | 66 | info.picocli 67 | picocli 68 | 4.6.2 69 | 70 | 71 | org.jetbrains.kotlin 72 | kotlin-test-junit5 73 | ${kotlin.version} 74 | test 75 | 76 | 77 | org.junit.jupiter 78 | junit-jupiter 79 | ${junit.jupiter.version} 80 | test 81 | 82 | 83 | 84 | ${project.basedir}/src/main/kotlin 85 | ${project.basedir}/src/test/kotlin 86 | 87 | 88 | org.jetbrains.kotlin 89 | kotlin-maven-plugin 90 | ${kotlin.version} 91 | 92 | 93 | 94 | compile 95 | 96 | compile 97 | 98 | 99 | 100 | 101 | test-compile 102 | 103 | test-compile 104 | 105 | 106 | 107 | 108 | 109 | org.apache.maven.plugins 110 | maven-dependency-plugin 111 | 3.2.0 112 | 113 | 114 | 115 | 116 | properties 117 | 118 | 119 | 120 | 121 | 122 | com.github.ozsie 123 | detekt-maven-plugin 124 | 1.19.1 125 | 126 | true 127 | ${project.basedir}/.detekt.yml 128 | 129 | 130 | 131 | verify 132 | 133 | check 134 | 135 | 136 | 137 | 138 | 139 | org.apache.maven.plugins 140 | maven-surefire-plugin 141 | 2.22.2 142 | 143 | 144 | org.jacoco 145 | jacoco-maven-plugin 146 | 0.8.7 147 | 148 | 149 | prepare-agent 150 | 151 | prepare-agent 152 | 153 | 154 | 155 | check 156 | 157 | report 158 | check 159 | 160 | 161 | 162 | 163 | BUNDLE 164 | 165 | 166 | LINE 167 | COVEREDRATIO 168 | 0% 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | org.apache.maven.plugins 179 | maven-jar-plugin 180 | 3.2.0 181 | 182 | 183 | 184 | true 185 | ${main.class} 186 | 187 | 188 | 189 | 190 | 191 | org.codehaus.mojo 192 | appassembler-maven-plugin 193 | 2.1.0 194 | 195 | 196 | 197 | ${main.class} 198 | lsp-cli 199 | 200 | 201 | true 202 | flat 203 | lib 204 | true 205 | 206 | 207 | 208 | package 209 | 210 | assemble 211 | 212 | 213 | 214 | 215 | 216 | org.apache.maven.plugins 217 | maven-antrun-plugin 218 | 3.0.0 219 | 220 | 221 | ktlint 222 | verify 223 | 224 | 225 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | run 237 | 238 | 239 | 240 | patch-bin-scripts 241 | 242 | 243 | 244 | 245 | if "%JAVACMD%"=="" set JAVACMD=java 246 | NUL 2>&1 253 | if "%ERRORLEVEL%" == "0" goto init 254 | 255 | echo. 256 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 257 | echo. 258 | echo Please set the JAVA_HOME variable in your environment to match the 259 | echo location of your Java installation. 260 | 261 | goto error 262 | 263 | :findJavaFromJavaHome 264 | set JAVA_HOME=%JAVA_HOME:"=% 265 | set JAVACMD=%JAVA_HOME%/bin/java.exe 266 | 267 | if exist "%JAVACMD%" goto init 268 | 269 | echo. 270 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 271 | echo. 272 | echo Please set the JAVA_HOME variable in your environment to match the 273 | echo location of your Java installation. 274 | 275 | goto error 276 | 277 | :init]]> 278 | 279 | 280 | %JAVACMD% %JAVA_OPTS% -classpath %CLASSPATH% 281 | "%JAVACMD%" %JAVA_OPTS% -classpath %CLASSPATH% 282 | 283 | 284 | set ERROR_CODE=%ERRORLEVEL% 285 | set ERROR_CODE=%ERRORLEVEL% 286 | if %ERROR_CODE% EQU 0 set ERROR_CODE=1 287 | 288 | 289 | 290 | package 291 | 292 | run 293 | 294 | 295 | 296 | 297 | 298 | com.pinterest 299 | ktlint 300 | 0.43.2 301 | 302 | 303 | 304 | 305 | org.apache.maven.plugins 306 | maven-assembly-plugin 307 | 3.3.0 308 | 309 | 310 | .assembly.xml 311 | 312 | lsp-cli-${project.version} 313 | false 314 | 315 | 316 | 317 | make-assembly 318 | package 319 | 320 | single 321 | 322 | 323 | 324 | 325 | 326 | org.eluder.coveralls 327 | coveralls-maven-plugin 328 | 4.3.0 329 | 330 | 331 | javax.xml.bind 332 | jaxb-api 333 | 2.3.1 334 | 335 | 336 | 337 | 338 | 339 | 340 | -------------------------------------------------------------------------------- /schemas/changes-1.0.0.xsd: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 1.0.0 24 | 25 | Record every release with their subsequent changes. 26 | 27 | 28 | 29 | 30 | 31 | 1.0.0 32 | 33 | Record every release with their subsequent changes. 34 | 35 | 36 | 37 | 38 | 39 | 1.0.0 40 | 41 | Contains the properties of this document. 42 | 43 | 44 | 45 | 46 | 47 | 1.0.0 48 | 49 | Contains the releases of this project with the actions taken 50 | for each of the releases. 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 1.0.0 59 | 60 | 61 | 62 | 63 | 1.0.0 64 | The list of releases for this project. 65 | 66 | 67 | 68 | 69 | 70 | 71 | 1.0.0 72 | A single release of this project. 73 | 74 | 75 | 76 | 77 | 1.0.0 78 | The list of actions taken for this release. 79 | 80 | 81 | 82 | 83 | 84 | 1.0.0 85 | 86 | The version number associated with this release. 87 | 88 | 89 | 90 | 91 | 92 | 1.0.0 93 | 94 | 95 | <p>The date of this release.</p> 96 | <p>This field can be any String, such as "in SVN" when the version isn't yet released. </p> 97 | 98 | 99 | 100 | 101 | 102 | 103 | 1.0.0 104 | 105 | A short description of this release. 106 | 107 | 108 | 109 | 110 | 111 | 112 | 1.0.0 113 | 114 | A single action done on the project, during this release. 115 | 116 | 117 | 118 | 119 | 120 | 1.0.0 121 | A list of fix issues. 122 | 123 | 124 | 125 | 126 | 1.0.0 127 | A list of contibutors for this issue. 128 | 129 | 130 | 131 | 132 | 133 | 1.0.0 134 | 135 | 136 | <p>Name of developer who committed the change.</p> 137 | <p>This <b>MUST</b> be the name of the developer as described in the developers section of the pom.xml file.</p> 138 | 139 | 140 | 141 | 142 | 143 | 144 | 1.0.0 145 | 146 | Name of the person to be credited for this change. This can be used when a patch is submitted by a non-committer. 147 | 148 | 149 | 150 | 151 | 152 | 1.0.0 153 | 154 | Email of the person to be credited for this change. 155 | 156 | 157 | 158 | 159 | 160 | 1.0.0 161 | 162 | 163 | <p>Id of the issue related to this change. This is the id in your issue tracking system.</p> 164 | <p>The Changes plugin will generate a URL out of this id. The URL is constructed using the value of the issueLinkTemplate parameter.</p> 165 | <p>See the <a href="changes-report.html">changes-report mojo</a> for more details.</p> 166 | 167 | 168 | 169 | 170 | 171 | 172 | 1.0.0 173 | 174 | 175 | Supported action types are the following: 176 | <ul> 177 | <li>add : added functionnality to the project.</li> 178 | <li>fix : bug fix for the project.</li> 179 | <li>update : updated some part of the project.</li> 180 | <li>remove : removed some functionnality from the project.</li> 181 | </ul> 182 | 183 | 184 | 185 | 186 | 187 | 188 | 1.0.0 189 | 190 | 191 | <p>Id of issue tracking system. If empty 'default' value will be use.</p> 192 | <p>The Changes plugin will generate a URL out of this id. The URL is constructed using the value of the issueLinkTemplatePerSystem parameter.</p> 193 | <p>See the <a href="changes-report.html">changes-report mojo</a> for more details.</p> 194 | 195 | 196 | 197 | 198 | 199 | 200 | 1.0.0 201 | fix date 202 | 203 | 204 | 205 | 206 | 207 | 1.0.0 208 | 209 | A fixed issue. 210 | 211 | 212 | 213 | 214 | 1.0.0 215 | 216 | 217 | <p>Id of the issue related to this change. This is the id in your issue tracking system.</p> 218 | <p>The Changes plugin will generate a URL out of this id. The URL is constructed using the value of the issueLinkTemplate parameter.</p> 219 | <p>See the <a href="changes-report.html">changes-report mojo</a> for more details.</p> 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 1.0.0 228 | 229 | Name and Email of the person to be credited for this change. This can be used when a patch is submitted by a non-committer. 230 | 231 | 232 | 233 | 234 | 1.0.0 235 | Name of the person to be credited for this change. 236 | 237 | 238 | 239 | 240 | 1.0.0 241 | Email of the person to be credited for this change. 242 | 243 | 244 | 245 | 246 | 247 | 1.0.0 248 | 249 | 250 | 251 | 252 | 1.0.0 253 | Page Title. 254 | 255 | 256 | 257 | 258 | 1.0.0 259 | Page Author 260 | 261 | 262 | 263 | 264 | 265 | 266 | 1.0.0 267 | 268 | A description of the author page. 269 | 270 | 271 | 272 | 273 | 274 | 1.0.0 275 | 276 | The page author email. 277 | 278 | 279 | 280 | 281 | -------------------------------------------------------------------------------- /src/main/kotlin/org/bsplines/lspcli/LspCliLauncher.kt: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | package org.bsplines.lspcli 9 | 10 | import com.google.gson.JsonElement 11 | import org.bsplines.lspcli.client.Checker 12 | import org.bsplines.lspcli.client.LspCliLanguageClient 13 | import org.bsplines.lspcli.tools.I18n 14 | import org.bsplines.lspcli.tools.Logging 15 | import org.bsplines.lspcli.tools.LspCliSettings 16 | import org.bsplines.lspcli.tools.VersionProvider 17 | import org.fusesource.jansi.AnsiConsole 18 | import picocli.CommandLine 19 | import java.nio.file.Path 20 | import java.util.concurrent.Callable 21 | import java.util.logging.Level 22 | import kotlin.system.exitProcess 23 | 24 | @CommandLine.Command( 25 | name = "lsp-cli", 26 | mixinStandardHelpOptions = true, 27 | showDefaultValues = true, 28 | versionProvider = VersionProvider::class, 29 | description = ["lsp-cli - CLI language client for LSP language servers"] 30 | ) 31 | class LspCliLauncher : Callable { 32 | @CommandLine.Option( 33 | names = ["--server-command-line"], 34 | paramLabel = "", 35 | required = true, 36 | description = [ 37 | "Required. Command line to start the language server, starting with the path of its " 38 | + "executable. If you want to supply arguments to the language server, separate them with " 39 | + "spaces. If the path of the executable or one of the arguments contain spaces, you can " 40 | + "escape them by using '\\ ' instead. In .lsp-cli.json, this option can either be " 41 | + "specified as an array of arguments or as a string with space-separated arguments.", 42 | ] 43 | ) 44 | var serverCommandLineString: String? = null 45 | 46 | var serverCommandLineList: List? = null 47 | 48 | @CommandLine.Option( 49 | names = ["--server-working-directory"], 50 | paramLabel = "", 51 | description = [ 52 | "Working directory for --server-command-line. If omitted, use the parent directory of " 53 | + "`.lsp-cli.json` if given, otherwise use the current working directory.", 54 | ] 55 | ) 56 | var serverWorkingDirPath: Path? = null 57 | 58 | @CommandLine.Option( 59 | names = ["--client-configuration"], 60 | paramLabel = "", 61 | description = [ 62 | "Use the client configuration stored in the JSON file . The format is usually nested " 63 | + "JSON objects (e.g., {\"latex\": {\"commands\": ...}}).", 64 | ] 65 | ) 66 | var clientConfigurationFilePath: Path? = null 67 | 68 | @CommandLine.Option( 69 | names = ["--hide-commands"], 70 | description = [ 71 | "Hide commands in lists of code actions for diagnostics, only show quick fixes.", 72 | ] 73 | ) 74 | var hideCommands: Boolean = false 75 | 76 | @CommandLine.Option( 77 | names = ["--verbose"], 78 | description = [ 79 | "Write to standard error output what is being done.", 80 | ] 81 | ) 82 | var verbose: Boolean = false 83 | 84 | @CommandLine.Parameters( 85 | paramLabel = "", 86 | arity = "1..*", 87 | description = [ 88 | "Paths of files or directories to check. " 89 | + "Directories are traversed recursively for supported file types. " 90 | + "If - is given, standard input will be checked as plain text.", 91 | ] 92 | ) 93 | var inputFilePaths: List = emptyList() 94 | 95 | constructor() 96 | 97 | constructor(parseResult: CommandLine.ParseResult, lspCliSettings: LspCliSettings) { 98 | if (parseResult.hasMatchedOption("--server-command-line")) { 99 | this.serverCommandLineString = parseResult.matchedOptionValue("--server-command-line", null) 100 | } else { 101 | val jsonElement: JsonElement? = 102 | lspCliSettings.getValue("defaultValues", "--server-command-line") 103 | 104 | if (jsonElement?.isJsonArray == true) { 105 | this.serverCommandLineList = jsonElement.asJsonArray.map { it.asString } 106 | } else if (jsonElement != null) { 107 | this.serverCommandLineString = jsonElement.asString 108 | } 109 | } 110 | 111 | this.serverWorkingDirPath = if (parseResult.hasMatchedOption("--server-working-directory")) { 112 | parseResult.matchedOptionValue("--server-working-directory", null) 113 | } else { 114 | val string: String? = lspCliSettings.getValue( 115 | "defaultValues", 116 | "--server-working-directory", 117 | )?.asString 118 | 119 | if (string != null) { 120 | Path.of(string) 121 | } else { 122 | lspCliSettings.settingsFilePath?.toAbsolutePath()?.parent 123 | } 124 | } 125 | 126 | this.clientConfigurationFilePath = if (parseResult.hasMatchedOption("--client-configuration")) { 127 | parseResult.matchedOptionValue("--client-configuration", null) 128 | } else { 129 | lspCliSettings.getValue( 130 | "defaultValues", 131 | "--client-configuration", 132 | )?.asString?.let { Path.of(it) } 133 | } 134 | 135 | this.hideCommands = if (parseResult.hasMatchedOption("--hide-commands")) { 136 | parseResult.matchedOptionValue("--hide-commands", false) 137 | } else { 138 | lspCliSettings.getValue("defaultValues", "--hide-commands")?.asBoolean ?: false 139 | } 140 | 141 | this.verbose = if (parseResult.hasMatchedOption("--verbose")) { 142 | parseResult.matchedOptionValue("--verbose", false) 143 | } else { 144 | lspCliSettings.getValue("defaultValues", "--verbose")?.asBoolean ?: false 145 | } 146 | 147 | if (parseResult.matchedPositionals().isNotEmpty()) { 148 | this.inputFilePaths = parseResult.matchedPositionals()[0].getValue() 149 | } 150 | } 151 | 152 | override fun call(): Int { 153 | if (this.verbose) Logging.setLogLevel(Level.INFO) 154 | 155 | val serverCommandLine: List = ( 156 | this.serverCommandLineList ?: this.serverCommandLineString?.let { 157 | serverCommandLineString: String -> 158 | NON_ESCAPED_SPACE_REGEX.split(serverCommandLineString).map { 159 | ESCAPED_SPACE_REGEX.replace(it, " ") 160 | } 161 | } ?: throw IllegalArgumentException( 162 | I18n.format("requiredArgumentNotSpecified", "--server-command-line"), 163 | ) 164 | ) 165 | 166 | val client = LspCliLanguageClient( 167 | serverCommandLine, 168 | this.serverWorkingDirPath, 169 | this.clientConfigurationFilePath, 170 | ) 171 | 172 | try { 173 | val checker = Checker(client, hideCommands = this.hideCommands) 174 | val numberOfMatches: Int = checker.check(this.inputFilePaths) 175 | return (if (numberOfMatches == 0) 0 else EXIT_CODE_MATCHES_FOUND) 176 | } finally { 177 | client.languageServer.shutdown() 178 | client.languageServerProcess.destroy() 179 | } 180 | } 181 | 182 | companion object { 183 | private const val EXIT_CODE_MATCHES_FOUND = 3 184 | 185 | private val ESCAPED_SPACE_REGEX = Regex("\\\\ ") 186 | private const val NON_ESCAPED_SPACE_REGEX_STRING = "(?) { 192 | AnsiConsole.systemInstall() 193 | 194 | val lspCliSettingsFilePath: Path? = getLspCliSettingsFilePath() 195 | val lspCliSettings = LspCliSettings(lspCliSettingsFilePath) 196 | 197 | val commandLine: CommandLine = createCommandLine(lspCliSettings) 198 | val commandSpec: CommandLine.Model.CommandSpec = commandLine.commandSpec 199 | 200 | try { 201 | val parseResult: CommandLine.ParseResult = commandLine.parseArgs(*arguments) 202 | 203 | if (parseResult.isUsageHelpRequested) { 204 | commandLine.usage(commandLine.out) 205 | exitProcess(commandSpec.exitCodeOnUsageHelp()) 206 | } else if (parseResult.isVersionHelpRequested) { 207 | commandLine.printVersionHelp(commandLine.out) 208 | exitProcess(commandSpec.exitCodeOnVersionHelp()) 209 | } 210 | 211 | val launcher = LspCliLauncher(parseResult, lspCliSettings) 212 | val exitCode: Int = launcher.call() 213 | if (exitCode != 0) exitProcess(exitCode) 214 | } catch (e: CommandLine.ParameterException) { 215 | commandLine.err.println(e.message) 216 | 217 | if (!CommandLine.UnmatchedArgumentException.printSuggestions(e, commandLine.err)) { 218 | e.commandLine.usage(commandLine.err) 219 | } 220 | 221 | exitProcess(commandSpec.exitCodeOnInvalidInput()) 222 | } catch (e: Exception) { 223 | e.printStackTrace(commandLine.err) 224 | exitProcess(commandSpec.exitCodeOnExecutionException()) 225 | } 226 | } 227 | 228 | private fun getLspCliSettingsFilePath(): Path? { 229 | val environmentVariable: String = System.getenv("LSP_CLI_JSON_SETTINGS_PATH") ?: "" 230 | val path: Path? = if (environmentVariable.isNotEmpty()) { 231 | Path.of(environmentVariable) 232 | } else { 233 | val appHome: String = System.getProperty("app.home", "") 234 | if (appHome.isNotEmpty()) Path.of(appHome, "bin") else null 235 | } 236 | 237 | return if (path == null) { 238 | null 239 | } else if (path.toFile().isFile) { 240 | path 241 | } else if (path.toFile().isDirectory) { 242 | val filePath: Path = path.resolve(".lsp-cli.json") 243 | if (filePath.toFile().isFile) filePath else null 244 | } else { 245 | null 246 | } 247 | } 248 | 249 | private fun createCommandLine(lspCliSettings: LspCliSettings): CommandLine { 250 | val defaultValues: Map? = lspCliSettings.getValueAsMap("defaultValues") 251 | val hiddenArguments: List? = 252 | lspCliSettings.getValueAsListOfString("helpMessage", "hiddenArguments") 253 | val visibleArguments: List? = 254 | lspCliSettings.getValueAsListOfString("helpMessage", "visibleArguments") 255 | 256 | val classCommandSpec = CommandLine.Model.CommandSpec.forAnnotatedObject(LspCliLauncher()) 257 | val commandSpec = CommandLine.Model.CommandSpec.create() 258 | 259 | commandSpec.usageMessage(classCommandSpec.usageMessage()) 260 | 261 | val usageMessageDescription: String? = 262 | lspCliSettings.getValue("helpMessage", "description")?.asString 263 | 264 | if (usageMessageDescription != null) { 265 | commandSpec.usageMessage().description(usageMessageDescription) 266 | } 267 | 268 | for (classOptionSpec: CommandLine.Model.OptionSpec in classCommandSpec.options()) { 269 | commandSpec.addOption( 270 | createOptionSpec(classOptionSpec, defaultValues, hiddenArguments, visibleArguments), 271 | ) 272 | } 273 | 274 | for ( 275 | positionalParamSpec: CommandLine.Model.PositionalParamSpec 276 | in classCommandSpec.positionalParameters() 277 | ) { 278 | commandSpec.addPositional(positionalParamSpec) 279 | } 280 | 281 | val commandLine = CommandLine(commandSpec) 282 | commandLine.commandName = lspCliSettings.getValue("programName")?.asString ?: "lsp-cli" 283 | commandLine.isCaseInsensitiveEnumValuesAllowed = true 284 | 285 | return commandLine 286 | } 287 | 288 | private fun createOptionSpec( 289 | classOptionSpec: CommandLine.Model.OptionSpec, 290 | defaultValues: Map?, 291 | hiddenArguments: List?, 292 | visibleArguments: List?, 293 | ): CommandLine.Model.OptionSpec { 294 | val optionSpecBuilder: CommandLine.Model.OptionSpec.Builder = classOptionSpec.toBuilder() 295 | 296 | if (defaultValues != null) { 297 | for (name: String in optionSpecBuilder.names()) { 298 | val defaultValueString: String? = defaultValues[name]?.toString() 299 | if (defaultValueString != null) optionSpecBuilder.defaultValue(defaultValueString) 300 | } 301 | } 302 | 303 | val hidden = if (visibleArguments != null) { 304 | !visibleArguments.contains(classOptionSpec.longestName()) 305 | } else { 306 | hiddenArguments?.contains(classOptionSpec.longestName()) ?: false 307 | } 308 | 309 | optionSpecBuilder.hidden(hidden) 310 | return optionSpecBuilder.build() 311 | } 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /src/main/kotlin/org/bsplines/lspcli/client/Checker.kt: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | package org.bsplines.lspcli.client 9 | 10 | import org.bsplines.lspcli.tools.FileIo 11 | import org.bsplines.lspcli.tools.I18n 12 | import org.bsplines.lspcli.tools.Logging 13 | import org.eclipse.lsp4j.CodeAction 14 | import org.eclipse.lsp4j.CodeActionContext 15 | import org.eclipse.lsp4j.CodeActionParams 16 | import org.eclipse.lsp4j.Command 17 | import org.eclipse.lsp4j.Diagnostic 18 | import org.eclipse.lsp4j.DiagnosticSeverity 19 | import org.eclipse.lsp4j.DidOpenTextDocumentParams 20 | import org.eclipse.lsp4j.Position 21 | import org.eclipse.lsp4j.TextDocumentIdentifier 22 | import org.eclipse.lsp4j.jsonrpc.messages.Either 23 | import org.fusesource.jansi.Ansi 24 | import org.fusesource.jansi.Ansi.Color 25 | import org.fusesource.jansi.AnsiConsole 26 | import java.io.ByteArrayOutputStream 27 | import java.nio.charset.StandardCharsets 28 | import java.nio.file.Files 29 | import java.nio.file.Path 30 | import java.util.stream.Collectors 31 | import kotlin.math.ceil 32 | 33 | class Checker( 34 | val languageClient: LspCliLanguageClient, 35 | val hideCommands: Boolean = false, 36 | ) { 37 | fun check(paths: List): Int { 38 | var numberOfMatches = 0 39 | for (path: Path in paths) numberOfMatches += check(path) 40 | return numberOfMatches 41 | } 42 | 43 | fun check(path: Path): Int { 44 | val text: String 45 | val codeLanguageId: String 46 | 47 | if (path.toString() == "-") { 48 | val outputStream = ByteArrayOutputStream() 49 | val buffer = ByteArray(STANDARD_INPUT_BUFFER_SIZE) 50 | 51 | while (true) { 52 | val length = System.`in`.read(buffer) 53 | if (length == -1) break 54 | outputStream.write(buffer, 0, length) 55 | } 56 | 57 | text = outputStream.toString(StandardCharsets.UTF_8) 58 | codeLanguageId = "plaintext" 59 | } else if (path.toFile().isDirectory) { 60 | return checkDirectory(path) 61 | } else { 62 | text = FileIo.readFileWithException(path) 63 | codeLanguageId = FileIo.getCodeLanguageIdFromPath(path) ?: "plaintext" 64 | } 65 | 66 | return checkFile(path, codeLanguageId, text) 67 | } 68 | 69 | private fun checkDirectory(path: Path): Int { 70 | var numberOfMatches = 0 71 | 72 | for (childPath: Path in Files.walk(path).collect(Collectors.toList())) { 73 | if (childPath.toFile().isFile) { 74 | val curCodeLanguageId: String? = FileIo.getCodeLanguageIdFromPath(childPath) 75 | if (curCodeLanguageId != null) numberOfMatches += check(childPath) 76 | } 77 | } 78 | 79 | return numberOfMatches 80 | } 81 | 82 | private fun checkFile(path: Path, languageId: String, text: String): Int { 83 | val uri: String = path.toUri().toString() 84 | val document = LspCliTextDocumentItem(uri, languageId, 1, text) 85 | Logging.logger.info(I18n.format("checkingFile", path.toString())) 86 | 87 | this.languageClient.languageServer.textDocumentService.didOpen( 88 | DidOpenTextDocumentParams(document), 89 | ) 90 | 91 | Logging.logger.info(I18n.format("waitingForDiagnosticsForFile", path.toString())) 92 | val diagnostics: List 93 | 94 | while (true) { 95 | val curDiagnostics: List? = this.languageClient.diagnosticsMap[uri] 96 | 97 | if (curDiagnostics != null) { 98 | diagnostics = curDiagnostics 99 | break 100 | } 101 | 102 | Thread.sleep(WAIT_FOR_DIAGNOSTIC_MILLISECONDS) 103 | } 104 | 105 | val documentId = TextDocumentIdentifier(uri) 106 | val terminalWidth: Int = run { 107 | val terminalWidth: Int = AnsiConsole.getTerminalWidth() 108 | if (terminalWidth >= 2) terminalWidth else Integer.MAX_VALUE 109 | } 110 | 111 | for (diagnostic: Diagnostic in diagnostics) { 112 | val codeActionTitles = ArrayList() 113 | val codeActionResult: List> = 114 | this.languageClient.languageServer.textDocumentService.codeAction( 115 | CodeActionParams(documentId, diagnostic.range, CodeActionContext(listOf(diagnostic))), 116 | ).get() 117 | 118 | for (entry: Either in codeActionResult) { 119 | val command: Command? = entry.left 120 | val codeAction: CodeAction? = entry.right 121 | 122 | if ((command != null) && !this.hideCommands) { 123 | codeActionTitles.add(command.title) 124 | } else if ((codeAction != null) && ((codeAction.command == null) || !this.hideCommands)) { 125 | codeActionTitles.add(codeAction.title) 126 | } 127 | } 128 | 129 | printDiagnostic(path, document, diagnostic, codeActionTitles, terminalWidth) 130 | } 131 | 132 | return diagnostics.size 133 | } 134 | 135 | companion object { 136 | private val TRAILING_WHITESPACE_REGEX = Regex("[ \t\r\n]+$") 137 | 138 | private const val STANDARD_INPUT_BUFFER_SIZE = 1024 139 | private const val WAIT_FOR_DIAGNOSTIC_MILLISECONDS = 50L 140 | private const val TAB_SIZE = 8 141 | 142 | @Suppress("ComplexMethod") 143 | private fun printDiagnostic( 144 | path: Path, 145 | document: LspCliTextDocumentItem, 146 | diagnostic: Diagnostic, 147 | codeActionTitles: List, 148 | terminalWidth: Int, 149 | ) { 150 | val text: String = document.text 151 | val fromPosition: Position = diagnostic.range.start 152 | val toPosition: Position = diagnostic.range.end 153 | val fromPos: Int = document.convertPosition(fromPosition) 154 | val toPos: Int = document.convertPosition(toPosition) 155 | 156 | val color: Color = when (diagnostic.severity) { 157 | DiagnosticSeverity.Error -> Color.RED 158 | DiagnosticSeverity.Warning -> Color.YELLOW 159 | DiagnosticSeverity.Information -> Color.BLUE 160 | DiagnosticSeverity.Hint -> Color.BLUE 161 | else -> Color.BLUE 162 | } 163 | val typeString: String = when (diagnostic.severity) { 164 | DiagnosticSeverity.Error -> "error" 165 | DiagnosticSeverity.Warning -> "warning" 166 | DiagnosticSeverity.Information -> "info" 167 | DiagnosticSeverity.Hint -> "hint" 168 | else -> "info" 169 | } 170 | 171 | val diagnosticCode: String = diagnostic.code?.get()?.toString() ?: "" 172 | val ansi: Ansi = (Ansi.ansi().bold().a(path.toString()).a(":") 173 | .a(fromPosition.line + 1).a(":").a(fromPosition.character + 1).a(": ") 174 | .fg(color).a(typeString).a(":").reset().bold().a(" ").a(diagnostic.message)) 175 | if (diagnosticCode.isNotEmpty()) ansi.a(" [").a(diagnosticCode).a("]") 176 | ansi.reset() 177 | println(ansi) 178 | 179 | val lineStartPos: Int = document.convertPosition(Position(fromPosition.line, 0)) 180 | val lineEndPos: Int = document.convertPosition(Position(fromPosition.line + 1, 0)) 181 | val line: String = text.substring(lineStartPos, lineEndPos) 182 | 183 | println( 184 | Ansi.ansi().a(line.substring(0, fromPos - lineStartPos)).bold().fg(color) 185 | .a(line.substring(fromPos - lineStartPos, toPos - lineStartPos)).reset() 186 | .a(line.substring(toPos - lineStartPos).replaceFirst(TRAILING_WHITESPACE_REGEX, "")), 187 | ) 188 | 189 | var indentationSize = guessIndentationSize(text, lineStartPos, fromPos, terminalWidth) 190 | 191 | for (codeActionTitle: String in codeActionTitles) { 192 | if (indentationSize + codeActionTitle.length > terminalWidth) indentationSize = 0 193 | } 194 | 195 | val indentation: String = " ".repeat(indentationSize) 196 | 197 | for (codeActionTitle: String in codeActionTitles) { 198 | println(Ansi.ansi().a(indentation).fg(Color.GREEN).a(codeActionTitle).reset()) 199 | } 200 | } 201 | 202 | private fun guessIndentationSize( 203 | text: String, 204 | lineStartPos: Int, 205 | fromPos: Int, 206 | terminalWidth: Int, 207 | ): Int { 208 | var indentationSize = 0 209 | 210 | for (pos in lineStartPos until fromPos) { 211 | if (text[pos] == '\t') { 212 | indentationSize = (ceil((indentationSize + 1.0) / TAB_SIZE) * TAB_SIZE).toInt() 213 | } else { 214 | indentationSize++ 215 | } 216 | 217 | if (indentationSize >= terminalWidth) indentationSize = 0 218 | } 219 | 220 | return indentationSize 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/kotlin/org/bsplines/lspcli/client/LspCliLanguageClient.kt: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | package org.bsplines.lspcli.client 9 | 10 | import com.google.gson.JsonElement 11 | import com.google.gson.JsonObject 12 | import com.google.gson.JsonParser 13 | import org.bsplines.lspcli.server.LspCliLanguageServer 14 | import org.bsplines.lspcli.tools.FileIo 15 | import org.bsplines.lspcli.tools.I18n 16 | import org.bsplines.lspcli.tools.Logging 17 | import org.eclipse.lsp4j.ConfigurationItem 18 | import org.eclipse.lsp4j.ConfigurationParams 19 | import org.eclipse.lsp4j.Diagnostic 20 | import org.eclipse.lsp4j.InitializeParams 21 | import org.eclipse.lsp4j.MessageActionItem 22 | import org.eclipse.lsp4j.MessageParams 23 | import org.eclipse.lsp4j.PublishDiagnosticsParams 24 | import org.eclipse.lsp4j.ShowMessageRequestParams 25 | import org.eclipse.lsp4j.jsonrpc.Launcher 26 | import org.eclipse.lsp4j.launch.LSPLauncher 27 | import org.eclipse.lsp4j.services.LanguageClient 28 | import org.eclipse.lsp4j.services.LanguageServer 29 | import java.nio.file.Path 30 | import java.util.concurrent.CompletableFuture 31 | import java.util.concurrent.ExecutorService 32 | import java.util.concurrent.Executors 33 | 34 | class LspCliLanguageClient( 35 | serverCommandLine: List, 36 | serverWorkingDirPath: Path? = null, 37 | clientConfigurationFilePath: Path? = null, 38 | ) : LanguageClient { 39 | val languageServerProcess: Process = 40 | startLanguageServerProcess(serverCommandLine, serverWorkingDirPath) 41 | val languageServer: LanguageServer = initializeLanguageServer() 42 | val clientConfiguration: JsonObject = if (clientConfigurationFilePath != null) { 43 | JsonParser.parseString(FileIo.readFileWithException(clientConfigurationFilePath)).asJsonObject 44 | } else { 45 | JsonObject() 46 | } 47 | 48 | private val _diagnosticsMap: MutableMap> = HashMap() 49 | val diagnosticsMap: Map> 50 | get() = _diagnosticsMap 51 | 52 | override fun telemetryEvent(params: Any?) { 53 | } 54 | 55 | override fun publishDiagnostics(params: PublishDiagnosticsParams?) { 56 | val uri: String = params?.uri ?: return 57 | val diagnostics: List = params.diagnostics ?: return 58 | this._diagnosticsMap[uri] = diagnostics 59 | } 60 | 61 | override fun showMessage(params: MessageParams?) { 62 | } 63 | 64 | override fun showMessageRequest( 65 | params: ShowMessageRequestParams?, 66 | ): CompletableFuture { 67 | return CompletableFuture.completedFuture(MessageActionItem()) 68 | } 69 | 70 | override fun logMessage(params: MessageParams?) { 71 | } 72 | 73 | override fun configuration( 74 | configurationParams: ConfigurationParams?, 75 | ): CompletableFuture> { 76 | if (configurationParams == null) return CompletableFuture.completedFuture(emptyList()) 77 | val result = ArrayList() 78 | 79 | for (ignored: ConfigurationItem in configurationParams.items) { 80 | result.add(clientConfiguration) 81 | } 82 | 83 | return CompletableFuture.completedFuture(result) 84 | } 85 | 86 | companion object { 87 | private fun startLanguageServerProcess( 88 | serverCommandLine: List, 89 | serverWorkingDirPath: Path? = null, 90 | ): Process { 91 | val absoluteServerCommandLine: MutableList = serverCommandLine.toMutableList() 92 | 93 | if (serverWorkingDirPath != null) { 94 | absoluteServerCommandLine[0] = 95 | serverWorkingDirPath.resolve(absoluteServerCommandLine[0]).toString() 96 | } 97 | 98 | Logging.logger.info( 99 | I18n.format( 100 | "startingLanguageServer", 101 | absoluteServerCommandLine.joinToString(" "), 102 | serverWorkingDirPath?.toFile(), 103 | ), 104 | ) 105 | 106 | val processBuilder: ProcessBuilder = 107 | ProcessBuilder(absoluteServerCommandLine).directory(serverWorkingDirPath?.toFile()) 108 | val process: Process = processBuilder.start() 109 | Runtime.getRuntime().addShutdownHook(Thread(process::destroy)) 110 | 111 | return process 112 | } 113 | } 114 | 115 | private fun initializeLanguageServer(): LanguageServer { 116 | val executorService: ExecutorService = Executors.newSingleThreadScheduledExecutor() 117 | 118 | val launcherBuilder = LSPLauncher.Builder() 119 | launcherBuilder.setLocalService(this) 120 | launcherBuilder.setRemoteInterface(LspCliLanguageServer::class.javaObjectType) 121 | launcherBuilder.setInput(this.languageServerProcess.inputStream) 122 | launcherBuilder.setOutput(this.languageServerProcess.outputStream) 123 | launcherBuilder.setExecutorService(executorService) 124 | 125 | val launcher: Launcher = launcherBuilder.create() 126 | val server: LanguageServer = launcher.remoteProxy 127 | launcher.startListening() 128 | executorService.shutdown() 129 | 130 | Logging.logger.info(I18n.format("initializingLanguageServer")) 131 | server.initialize(InitializeParams()).get() 132 | return server 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/kotlin/org/bsplines/lspcli/client/LspCliTextDocumentItem.kt: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | package org.bsplines.lspcli.client 9 | 10 | import org.eclipse.lsp4j.Position 11 | import org.eclipse.lsp4j.TextDocumentItem 12 | 13 | class LspCliTextDocumentItem( 14 | uri: String, 15 | codeLanguageId: String, 16 | version: Int, 17 | text: String, 18 | ) : TextDocumentItem(uri, codeLanguageId, version, text) { 19 | private val lineStartPosList: MutableList = ArrayList() 20 | 21 | init { 22 | reinitializeLineStartPosList(text) 23 | } 24 | 25 | private fun reinitializeLineStartPosList(text: String) { 26 | this.lineStartPosList.clear() 27 | this.lineStartPosList.add(0) 28 | 29 | var i = 0 30 | 31 | while (i < text.length) { 32 | val c: Char = text[i] 33 | 34 | if (c == '\r') { 35 | if ((i + 1 < text.length) && (text[i + 1] == '\n')) i++ 36 | this.lineStartPosList.add(i + 1) 37 | } else if (c == '\n') { 38 | this.lineStartPosList.add(i + 1) 39 | } 40 | 41 | i++ 42 | } 43 | } 44 | 45 | @Suppress("NestedBlockDepth") 46 | fun convertPosition(position: Position): Int { 47 | val line: Int = position.line 48 | val character: Int = position.character 49 | val text: String = text 50 | 51 | return when { 52 | line < 0 -> 0 53 | line >= this.lineStartPosList.size -> text.length 54 | else -> { 55 | val lineStart: Int = this.lineStartPosList[line] 56 | val nextLineStart: Int = if (line < this.lineStartPosList.size - 1) { 57 | this.lineStartPosList[line + 1] 58 | } else { 59 | text.length 60 | } 61 | val lineLength: Int = nextLineStart - lineStart 62 | 63 | when { 64 | character < 0 -> lineStart 65 | character >= lineLength -> { 66 | var pos: Int = lineStart + lineLength 67 | 68 | if (pos >= 1) { 69 | if (text[pos - 1] == '\r') { 70 | pos-- 71 | } else if (text[pos - 1] == '\n') { 72 | pos-- 73 | if ((pos >= 1) && (text[pos - 1] == '\r')) pos-- 74 | } 75 | } 76 | 77 | pos 78 | } 79 | else -> lineStart + character 80 | } 81 | } 82 | } 83 | } 84 | 85 | fun convertPosition(pos: Int): Position { 86 | var line: Int = this.lineStartPosList.binarySearch(pos) 87 | 88 | if (line < 0) { 89 | val insertionPoint: Int = -line - 1 90 | line = insertionPoint - 1 91 | } 92 | 93 | return Position(line, pos - this.lineStartPosList[line]) 94 | } 95 | 96 | override fun setText(text: String) { 97 | super.setText(text) 98 | reinitializeLineStartPosList(text) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/kotlin/org/bsplines/lspcli/server/LspCliLanguageServer.kt: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | package org.bsplines.lspcli.server 9 | 10 | import org.eclipse.lsp4j.services.LanguageServer 11 | 12 | interface LspCliLanguageServer : LanguageServer 13 | -------------------------------------------------------------------------------- /src/main/kotlin/org/bsplines/lspcli/tools/FileIo.kt: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | package org.bsplines.lspcli.tools 9 | 10 | import java.io.IOException 11 | import java.nio.charset.StandardCharsets 12 | import java.nio.file.Files 13 | import java.nio.file.Path 14 | import java.nio.file.StandardOpenOption 15 | 16 | object FileIo { 17 | fun readFile(filePath: Path): String? { 18 | return try { 19 | readFileWithException(filePath) 20 | } catch (e: IOException) { 21 | Logging.logger.warning(I18n.format("couldNotReadFile", e, filePath.toString())) 22 | null 23 | } 24 | } 25 | 26 | fun readFileWithException(filePath: Path): String { 27 | return String(Files.readAllBytes(filePath), StandardCharsets.UTF_8) 28 | } 29 | 30 | fun writeFile(filePath: Path, text: String) { 31 | try { 32 | writeFileWithException(filePath, text) 33 | } catch (e: IOException) { 34 | Logging.logger.warning(I18n.format("couldNotWriteFile", e, filePath.toString())) 35 | } 36 | } 37 | 38 | fun writeFileWithException(filePath: Path, text: String) { 39 | Files.write( 40 | filePath, 41 | text.toByteArray(StandardCharsets.UTF_8), 42 | StandardOpenOption.CREATE, 43 | StandardOpenOption.TRUNCATE_EXISTING, 44 | StandardOpenOption.WRITE, 45 | StandardOpenOption.SYNC, 46 | ) 47 | } 48 | 49 | @Suppress("ComplexCondition", "ComplexMethod", "LongMethod") 50 | fun getCodeLanguageIdFromPath(path: Path): String? { 51 | val fileName: String = path.fileName.toString() 52 | 53 | return if (fileName.endsWith(".bib")) { 54 | "bibtex" 55 | } else if (fileName.endsWith(".c") 56 | || fileName.endsWith(".h")) { 57 | "c" 58 | } else if (fileName.endsWith(".clj")) { 59 | "clojure" 60 | } else if (fileName.endsWith(".coffee")) { 61 | "coffeescript" 62 | } else if (fileName.endsWith(".cc") 63 | || fileName.endsWith(".cpp") 64 | || fileName.endsWith(".cxx") 65 | || fileName.endsWith(".hh") 66 | || fileName.endsWith(".hpp") 67 | || fileName.endsWith(".inl")) { 68 | "cpp" 69 | } else if (fileName.endsWith(".cs")) { 70 | "csharp" 71 | } else if (fileName.endsWith(".dart")) { 72 | "dart" 73 | } else if (fileName.endsWith(".ex")) { 74 | "elixir" 75 | } else if (fileName.endsWith(".elm")) { 76 | "elm" 77 | } else if (fileName.endsWith(".erl")) { 78 | "erlang" 79 | } else if (fileName.endsWith(".f90")) { 80 | "fortran-modern" 81 | } else if (fileName.endsWith(".fs")) { 82 | "fsharp" 83 | } else if (fileName.endsWith(".go")) { 84 | "go" 85 | } else if (fileName.endsWith(".groovy")) { 86 | "groovy" 87 | } else if (fileName.endsWith(".hs")) { 88 | "haskell" 89 | } else if (fileName.endsWith(".htm") 90 | || fileName.endsWith(".html") 91 | || fileName.endsWith(".xht") 92 | || fileName.endsWith(".xhtml")) { 93 | "html" 94 | } else if (fileName.endsWith(".java")) { 95 | "java" 96 | } else if (fileName.endsWith(".js")) { 97 | "javascript" 98 | } else if (fileName.endsWith(".jl")) { 99 | "julia" 100 | } else if (fileName.endsWith(".kt")) { 101 | "kotlin" 102 | } else if (fileName.endsWith(".tex")) { 103 | "latex" 104 | } else if (fileName.endsWith(".lisp")) { 105 | "lisp" 106 | } else if (fileName.endsWith(".lua")) { 107 | "lua" 108 | } else if (fileName.endsWith(".md")) { 109 | "markdown" 110 | } else if (fileName.endsWith(".m")) { 111 | "matlab" 112 | } else if (fileName.endsWith(".org")) { 113 | "org" 114 | } else if (fileName.endsWith(".pl")) { 115 | "perl" 116 | } else if (fileName.endsWith(".php")) { 117 | "php" 118 | } else if (fileName.endsWith(".txt")) { 119 | "plaintext" 120 | } else if (fileName.endsWith(".ps1")) { 121 | "powershell" 122 | } else if (fileName.endsWith(".pp")) { 123 | "puppet" 124 | } else if (fileName.endsWith(".py")) { 125 | "python" 126 | } else if (fileName.endsWith(".r")) { 127 | "r" 128 | } else if (fileName.endsWith(".rst")) { 129 | "restructuredtext" 130 | } else if (fileName.endsWith(".Rnw") 131 | || fileName.endsWith(".rnw")) { 132 | "rsweave" 133 | } else if (fileName.endsWith(".rb")) { 134 | "ruby" 135 | } else if (fileName.endsWith(".rs")) { 136 | "rust" 137 | } else if (fileName.endsWith(".scala")) { 138 | "scala" 139 | } else if (fileName.endsWith(".sh")) { 140 | "shellscript" 141 | } else if (fileName.endsWith(".sql")) { 142 | "sql" 143 | } else if (fileName.endsWith(".swift")) { 144 | "swift" 145 | } else if (fileName.endsWith(".ts")) { 146 | "typescript" 147 | } else if (fileName.endsWith(".vb")) { 148 | "vb" 149 | } else if (fileName.endsWith(".v")) { 150 | "verilog" 151 | } else { 152 | null 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/kotlin/org/bsplines/lspcli/tools/I18n.kt: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | package org.bsplines.lspcli.tools 9 | 10 | import java.io.PrintWriter 11 | import java.io.StringWriter 12 | import java.text.MessageFormat 13 | import java.util.Locale 14 | import java.util.MissingResourceException 15 | import java.util.ResourceBundle 16 | 17 | object I18n { 18 | private var messages: ResourceBundle? = null 19 | 20 | init { 21 | setDefaultLocale() 22 | } 23 | 24 | @Suppress("SwallowedException") 25 | fun setDefaultLocale() { 26 | try { 27 | setLocale(Locale.getDefault(), false) 28 | } catch (e: MissingResourceException) { 29 | setLocale(Locale.ENGLISH, false) 30 | } 31 | } 32 | 33 | fun setLocale(locale: Locale, log: Boolean = true) { 34 | if (log) Logging.logger.info(format("settingLocale", locale.language)) 35 | messages = ResourceBundle.getBundle("LspCliMessagesBundle", locale) 36 | } 37 | 38 | fun format(key: String, vararg messageArguments: Any?): String { 39 | val messages: ResourceBundle? = messages 40 | 41 | val message: String = if ((messages != null) && messages.containsKey(key)) { 42 | messages.getString(key) 43 | } else { 44 | val builder = StringBuilder() 45 | 46 | if (messages == null) { 47 | builder.append("MessagesBundle is null while trying to get i18n message with key '") 48 | builder.append(key) 49 | builder.append("'") 50 | } else { 51 | builder.append("i18n message with key '") 52 | builder.append(key) 53 | builder.append("' not found") 54 | } 55 | 56 | builder.append(", message arguments: ") 57 | 58 | for (i in messageArguments.indices) { 59 | if (i > 0) builder.append(", ") 60 | builder.append("'{") 61 | builder.append(i.toString()) 62 | builder.append("}'") 63 | } 64 | 65 | builder.toString() 66 | } 67 | 68 | val formatter = MessageFormat("") 69 | formatter.applyPattern(message.replace("'", "''")) 70 | val stringArguments: Array = Array(messageArguments.size) { "" } 71 | 72 | for (i in messageArguments.indices) { 73 | stringArguments[i] = (messageArguments[i]?.toString() ?: "null") 74 | } 75 | 76 | return formatter.format(stringArguments) 77 | } 78 | 79 | fun format(key: String, e: Exception, vararg messageArguments: Any?): String { 80 | val builder = StringBuilder() 81 | builder.append(format(key, messageArguments)) 82 | builder.append(". ") 83 | builder.append(format(e)) 84 | return builder.toString() 85 | } 86 | 87 | fun format(e: Exception): String { 88 | val writer = StringWriter() 89 | writer.write(format("followingExceptionOccurred")) 90 | writer.write("\n") 91 | e.printStackTrace(PrintWriter(writer)) 92 | return writer.toString() 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/kotlin/org/bsplines/lspcli/tools/Logging.kt: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | package org.bsplines.lspcli.tools 9 | 10 | import java.util.logging.ConsoleHandler 11 | import java.util.logging.Level 12 | import java.util.logging.Logger 13 | 14 | object Logging { 15 | val logger: Logger = Logger.getLogger("org.bsplines.lspcli") 16 | private val loggerConsoleHandler = ConsoleHandler() 17 | 18 | init { 19 | logger.useParentHandlers = false 20 | logger.addHandler(loggerConsoleHandler) 21 | setLogLevel(Level.WARNING) 22 | } 23 | 24 | fun setLogLevel(logLevel: Level) { 25 | logger.level = logLevel 26 | loggerConsoleHandler.level = logLevel 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/kotlin/org/bsplines/lspcli/tools/LspCliSettings.kt: -------------------------------------------------------------------------------- 1 | package org.bsplines.lspcli.tools 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.google.gson.JsonParser 6 | import java.nio.file.Path 7 | 8 | class LspCliSettings( 9 | val settingsFilePath: Path? = null, 10 | ) { 11 | val jsonSettings: JsonObject = if (settingsFilePath != null) { 12 | JsonParser.parseString(FileIo.readFileWithException(settingsFilePath)).asJsonObject 13 | } else { 14 | JsonObject() 15 | } 16 | 17 | fun getValue(vararg keys: String): JsonElement? { 18 | var jsonElement: JsonElement = this.jsonSettings 19 | 20 | for (key: String in keys) { 21 | val jsonObject: JsonObject? = if (jsonElement.isJsonObject) jsonElement.asJsonObject else null 22 | 23 | if (jsonObject?.has(key) == true) { 24 | jsonElement = jsonObject.get(key) 25 | } else { 26 | return null 27 | } 28 | } 29 | 30 | return jsonElement 31 | } 32 | 33 | fun getValueAsListOfString(vararg keys: String): List? { 34 | val jsonElement: JsonElement = getValue(*keys) ?: return null 35 | val list = ArrayList() 36 | 37 | for (entryElement: JsonElement in jsonElement.asJsonArray) { 38 | list.add(entryElement.asString) 39 | } 40 | 41 | return list 42 | } 43 | 44 | fun getValueAsMap(vararg keys: String): Map? { 45 | val jsonElement: JsonElement = getValue(*keys) ?: return null 46 | val map = HashMap() 47 | 48 | for ((mapKey: String, mapValueElement: JsonElement) in jsonElement.asJsonObject.entrySet()) { 49 | map[mapKey] = mapValueElement 50 | } 51 | 52 | return map 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/kotlin/org/bsplines/lspcli/tools/VersionProvider.kt: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | package org.bsplines.lspcli.tools 9 | 10 | import com.google.gson.Gson 11 | import com.google.gson.GsonBuilder 12 | import com.google.gson.JsonObject 13 | import org.bsplines.lspcli.LspCliLauncher 14 | import picocli.CommandLine 15 | 16 | class VersionProvider : CommandLine.IVersionProvider { 17 | override fun getVersion(): Array { 18 | val lspcliPackage: Package? = LspCliLauncher::class.java.getPackage() 19 | val jsonObject = JsonObject() 20 | 21 | if (lspcliPackage != null) { 22 | val lspcliVersion: String? = lspcliPackage.implementationVersion 23 | if (lspcliVersion != null) jsonObject.addProperty("lsp-cli", lspcliVersion) 24 | } 25 | 26 | val javaVersion: String? = System.getProperty("java.version") 27 | if (javaVersion != null) jsonObject.addProperty("java", javaVersion) 28 | 29 | val gsonBuilder: Gson = GsonBuilder().setPrettyPrinting().create() 30 | return arrayOf(gsonBuilder.toJson(jsonObject)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/resources/LspCliMessagesBundle.properties: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | checkingFile = Checking '{0}'... 8 | couldNotReadFile = Could not read file '{0}' 9 | couldNotWriteFile = Could not write file '{0}' 10 | followingExceptionOccurred = The following exception occurred: 11 | initializingLanguageServer = Initializing language server... 12 | requiredArgumentNotSpecified = Required argument '{0}' not specified 13 | settingLocale = Setting locale to '{0}' 14 | startingLanguageServer = Starting language server with command line '{0}' in directory '{1}'... 15 | waitingForDiagnosticsForFile = Waiting for diagnostics for file '{0}'... 16 | -------------------------------------------------------------------------------- /src/test/kotlin/org/bsplines/lspcli/tools/I18nTest.kt: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | package org.bsplines.lspcli.tools 9 | 10 | import kotlin.test.Test 11 | import kotlin.test.assertContains 12 | import kotlin.test.assertEquals 13 | 14 | class I18nTest { 15 | @Test 16 | fun testFormat() { 17 | assertEquals("Checking 'test'...", I18n.format("checkingFile", "test")) 18 | 19 | assertEquals( 20 | "i18n message with key 'abc' not found, message arguments: 'def', '42', 'null'", 21 | I18n.format("abc", "def", 42, null), 22 | ) 23 | 24 | assertContains( 25 | I18n.format(NullPointerException("abc")), 26 | Regex( 27 | "The following exception occurred:[\r\n]+java\\.lang\\.NullPointerException: abc[\r\n]+.*", 28 | RegexOption.DOT_MATCHES_ALL, 29 | ), 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/kotlin/org/bsplines/lspcli/tools/VersionProviderTest.kt: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | package org.bsplines.lspcli.tools 9 | 10 | import com.google.gson.JsonElement 11 | import com.google.gson.JsonObject 12 | import com.google.gson.JsonParser 13 | import kotlin.test.Test 14 | import kotlin.test.assertEquals 15 | import kotlin.test.assertTrue 16 | 17 | class VersionProviderTest { 18 | @Test 19 | fun testVersion() { 20 | val versionProvider = VersionProvider() 21 | val version: Array = versionProvider.version 22 | assertEquals(1, version.size) 23 | 24 | val rootJsonElement: JsonElement = JsonParser.parseString(version[0]) 25 | assertTrue(rootJsonElement.isJsonObject) 26 | 27 | val rootJsonObject: JsonObject = rootJsonElement.asJsonObject 28 | assertTrue(rootJsonObject.has("java")) 29 | 30 | val javaJsonElement: JsonElement = rootJsonObject["java"] 31 | assertTrue(javaJsonElement.isJsonPrimitive) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tools/common.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | 9 | import re 10 | import subprocess 11 | from typing import Tuple 12 | 13 | 14 | 15 | def getGitHubOrganizationRepository() -> Tuple[str, str]: 16 | output = subprocess.run(["git", "remote", "get-url", "origin"], 17 | stdout=subprocess.PIPE).stdout.decode() 18 | regexMatch = re.search(r"github.com[:/](.*?)/(.*?)(?:\.git)?$", output) 19 | assert regexMatch is not None, output 20 | organization, repository = regexMatch.group(1), regexMatch.group(2) 21 | return organization, repository 22 | 23 | organization, repository = getGitHubOrganizationRepository() 24 | -------------------------------------------------------------------------------- /tools/convertChangelog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | 9 | import argparse 10 | import datetime 11 | import pathlib 12 | import re 13 | import sys 14 | from typing import Optional, Tuple 15 | import xml.etree.ElementTree as et 16 | import xml.dom.minidom 17 | 18 | sys.path.append(str(pathlib.Path(__file__).parent)) 19 | import common 20 | 21 | 22 | 23 | def parseIssue(issue: str) -> Tuple[str, str, int, str]: 24 | issueMatch = re.search(r"(?:(.*?)/)?(.*?)?#([0-9]+)", issue) 25 | assert issueMatch is not None, issue 26 | issueOrganization = (issueMatch.group(1) if issueMatch.group(1) is not None else 27 | common.organization) 28 | issueRepository = (issueMatch.group(2) if issueMatch.group(2) != "" else common.repository) 29 | issueNumber = int(issueMatch.group(3)) 30 | 31 | if issueOrganization == common.organization: 32 | if issueRepository == common.repository: 33 | normalizedIssue = f"#{issueNumber}" 34 | else: 35 | normalizedIssue = f"{issueRepository}#{issueNumber}" 36 | else: 37 | normalizedIssue = f"{issueOrganization}/{issueRepository}#{issueNumber}" 38 | 39 | return issueOrganization, issueRepository, issueNumber, normalizedIssue 40 | 41 | 42 | 43 | def replaceUnicodeWithXmlEntities(string: str) -> str: 44 | return re.sub(r"[\u007f-\U0010ffff]", (lambda x: f"&#x{ord(x.group()):04x};"), string) 45 | 46 | 47 | 48 | def convertChangelogFromXmlToMarkdown(xmlFilePath: pathlib.Path, 49 | version: Optional[str] = None) -> str: 50 | document = et.parse(xmlFilePath).getroot() 51 | 52 | if version is None: 53 | markdown = """ 60 | 61 | """ 62 | 63 | title = document.findtext("./{http://maven.apache.org/changes/1.0.0}properties" 64 | "/{http://maven.apache.org/changes/1.0.0}title") 65 | markdown += f"# {title}\n" 66 | else: 67 | markdown = "" 68 | 69 | releases = document.findall("./{http://maven.apache.org/changes/1.0.0}body" 70 | "/{http://maven.apache.org/changes/1.0.0}release") 71 | 72 | for release in releases: 73 | curVersion = release.attrib["version"] 74 | if version == "latest": version = curVersion 75 | if (version is not None) and (curVersion != version): continue 76 | 77 | description = release.attrib.get("description", "") 78 | if len(description) > 0: description = f" \u2014 \u201c{description}\u201d" 79 | dateStr = release.attrib["date"] 80 | dateStr = (datetime.datetime.strptime(dateStr, "%Y-%m-%d").strftime("%B %d, %Y") 81 | if dateStr != "upcoming" else dateStr) 82 | if version is None: markdown += f"\n## {curVersion}{description} ({dateStr})\n\n" 83 | 84 | for action in release.findall("./{http://maven.apache.org/changes/1.0.0}action"): 85 | type_ = action.attrib["type"] 86 | typeEmoji = { 87 | "add" : "\u2728", 88 | "fix": "\U0001f41b", 89 | "remove" : "\U0001f5d1", 90 | "update" : "\U0001f527", 91 | }[type_] 92 | typeStr = {"add" : "New", "fix": "Bug fix", "remove" : "Removal", "update" : "Change"}[type_] 93 | 94 | additionalInfo = "" 95 | 96 | if "issue" in action.attrib: 97 | for issue in action.attrib["issue"].split(","): 98 | issueOrganization, issueRepository, issueNumber, issue = parseIssue(issue) 99 | additionalInfo += (" \u2014 " if additionalInfo == "" else ", ") 100 | additionalInfo += (f"[{issue}](https://github.com/{issueOrganization}/" 101 | f"{issueRepository}/issues/{issueNumber})") 102 | 103 | if "due-to" in action.attrib: 104 | for author in action.attrib["due-to"].split(","): 105 | additionalInfo += (" \u2014 " if additionalInfo == "" else ", ") 106 | userMatch = re.search(r"@([^)]+)", author) 107 | if userMatch is not None: author = f"[{author}](https://github.com/{userMatch.group(1)})" 108 | additionalInfo += author 109 | 110 | change = action.text 111 | assert change is not None 112 | change = change.strip() 113 | change = change.replace("LaTeX", "LATEX") 114 | change = re.sub(r"(?EX", change) 115 | 116 | markdown += f"- {typeEmoji} *{typeStr}:* {change}{additionalInfo}\n" 117 | 118 | markdown = replaceUnicodeWithXmlEntities(markdown) 119 | return markdown 120 | 121 | 122 | 123 | def convertReleaseFromMarkdownToXml(body: et.Element, version: str, name: str, dateStr: str, 124 | changes: str) -> et.Element: 125 | attributes = {"version" : version} 126 | if len(name) > 0: attributes["description"] = name 127 | attributes["date"] = (datetime.datetime.strptime(dateStr, "%B %d, %Y").strftime("%Y-%m-%d") 128 | if dateStr != "upcoming" else dateStr) 129 | release = et.SubElement(body, "release", attributes) 130 | 131 | changes = changes.strip() 132 | 133 | for change in changes.split("\n"): 134 | change = re.sub(r"^- ", "", change).strip() 135 | attributes = {} 136 | 137 | if change.startswith("Remove "): 138 | attributes["type"] = "remove" 139 | elif (change.startswith("Add ") or ("support" in change.lower()) 140 | or (change == "Initial release")): 141 | attributes["type"] = "add" 142 | elif (change.startswith("Fix ") 143 | or any(x in change.lower() for x in ["error", "warning", "prevent"])): 144 | attributes["type"] = "fix" 145 | else: 146 | attributes["type"] = "update" 147 | 148 | issues = [] 149 | authors = [] 150 | issueFound = True 151 | 152 | while issueFound: 153 | issueMatch1 = re.search( 154 | r" \((?:fixes|fixes part of|see) \[([^\]]*?#[0-9]+)\]\(.*?\)\)", change) 155 | issueMatch2 = re.search( 156 | r"(?:[;,]| and) (?:fixes |see )?\[([^\]]*?#[0-9]+)\]\(.*?\)", change) 157 | issueMatch3 = re.search( 158 | r" \((?:\[PR |PR \[)(.*?#[0-9]+)\]\([^\]]*?\) by \[([^\]]*?)\]\(.*?\)\)", change) 159 | issueMatch4 = re.search( 160 | r"(?:[;,]| and) \[PR (.*?#[0-9]+)\]\([^\]]*?\) by \[([^\]]*?)\]\(.*?\)", change) 161 | issueFound = False 162 | 163 | for issueMatch in [issueMatch1, issueMatch2, issueMatch3, issueMatch4]: 164 | if issueMatch is not None: 165 | issueFound = True 166 | _, _, _, issue = parseIssue(issueMatch.group(1)) 167 | issues.append(issue) 168 | if len(issueMatch.groups()) > 1: authors.append(issueMatch.group(2)) 169 | change = change[:issueMatch.start()] + change[issueMatch.end():] 170 | 171 | if len(issues) > 0: attributes["issue"] = ",".join(sorted(issues)) 172 | if len(authors) > 0: attributes["due-to"] = ",".join(sorted(authors)) 173 | 174 | change = change.replace("TEX", "TeX").replace("LA", "La") 175 | 176 | assert f"github.com/{common.organization}" not in change, change 177 | assert " -" not in change, change 178 | 179 | action = et.SubElement(release, "action", attributes) 180 | action.text = f"\n {change}\n " 181 | 182 | return release 183 | 184 | 185 | 186 | def convertChangelogFromMarkdownToXml(markdownFilePath: pathlib.Path, 187 | version: Optional[str] = None) -> str: 188 | with open(markdownFilePath, "r") as f: changelog = f.read() 189 | 190 | document = et.Element("document", { 191 | "xmlns" : "http://maven.apache.org/changes/1.0.0", 192 | "xmlns:xsi" : "http://www.w3.org/2001/XMLSchema-instance", 193 | "xsi:schemaLocation" : "http://maven.apache.org/changes/1.0.0 " 194 | "https://maven.apache.org/xsd/changes-1.0.0.xsd" 195 | }) 196 | 197 | properties = et.SubElement(document, "properties") 198 | title = et.SubElement(properties, "title") 199 | title.text = "Changelog" 200 | author = et.SubElement(properties, "author") 201 | author.text = "Julian Valentin, LTeX Development Community" 202 | 203 | body = et.SubElement(document, "body") 204 | 205 | regexMatches = re.findall( 206 | r"\n## ([^ ]+)(?: \u2014 \u201c(.*?)\u201d)? \((.*)\)\n\n((?:.|\n)+?)(?=$|\n## )", changelog) 207 | for regexMatch in regexMatches: 208 | curVersion = regexMatch[0] 209 | if version == "latest": version = curVersion 210 | if (version is not None) and (curVersion != version): continue 211 | release = convertReleaseFromMarkdownToXml(body, *regexMatch) 212 | 213 | if version is not None: 214 | document = release 215 | break 216 | 217 | xmlStr = et.tostring(document, encoding="unicode", xml_declaration=True) 218 | xmlStr = xml.dom.minidom.parseString(xmlStr).toprettyxml(indent=" ") 219 | xmlStr = re.sub(r"^<\?xml version=\"1.0\" \?>\n", 220 | (""" 221 | 228 | """ if version is None else ""), xmlStr) 229 | xmlStr = replaceUnicodeWithXmlEntities(xmlStr) 230 | 231 | return xmlStr 232 | 233 | 234 | 235 | def main() -> None: 236 | parser = argparse.ArgumentParser( 237 | description="Convert changelog from XML to Markdown and vice-versa.") 238 | xmlFileArgument = parser.add_argument("--xml-file", type=pathlib.Path, metavar="PATH", 239 | help="XML file to convert to Markdown") 240 | parser.add_argument("--markdown-file", type=pathlib.Path, metavar="PATH", 241 | help="Markdown file to convert to XML") 242 | parser.add_argument("--output-file", type=pathlib.Path, default=pathlib.Path("-"), metavar="PATH", 243 | help="Output file; '-' is standard output (default)") 244 | parser.add_argument("--version", 245 | help="Version to convert; 'latest' is permitted; all versions are converted if omitted") 246 | arguments = parser.parse_args() 247 | 248 | if arguments.xml_file is not None: 249 | output = convertChangelogFromXmlToMarkdown(arguments.xml_file, arguments.version) 250 | elif arguments.markdown_file is not None: 251 | output = convertChangelogFromMarkdownToXml(arguments.markdown_file, arguments.version) 252 | else: 253 | raise argparse.ArgumentError(xmlFileArgument, 254 | "One of --xml-file or --markdown-file is required") 255 | 256 | if str(arguments.output_file) == "-": 257 | print(output, end="") 258 | else: 259 | with open(arguments.output_file, "w") as f: f.write(output) 260 | 261 | 262 | 263 | if __name__ == "__main__": 264 | main() 265 | -------------------------------------------------------------------------------- /tools/createBinaryArchives.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | 9 | import pathlib 10 | import re 11 | import shutil 12 | import subprocess 13 | import tarfile 14 | import tempfile 15 | import urllib.parse 16 | import urllib.request 17 | import zipfile 18 | 19 | javaVersion = "11.0.12+7" 20 | 21 | 22 | 23 | def createBinaryArchive(platform: str, arch: str) -> None: 24 | print(f"Processing platform/arch '{platform}/{arch}'...") 25 | lspCliVersion = getLspCliVersion() 26 | targetDirPath = pathlib.Path(__file__).parent.parent.joinpath("target") 27 | lspCliArchivePath = pathlib.Path(__file__).parent.parent.joinpath( 28 | targetDirPath, f"lsp-cli-{lspCliVersion}.tar.gz") 29 | 30 | with tempfile.TemporaryDirectory() as tmpDirPathStr: 31 | tmpDirPath = pathlib.Path(tmpDirPathStr) 32 | 33 | print("Extracting lsp-cli archive...") 34 | with tarfile.open(lspCliArchivePath, "r:gz") as tarFile: tarFile.extractall(path=tmpDirPath) 35 | 36 | lspCliDirPath = tmpDirPath.joinpath(f"lsp-cli-{lspCliVersion}") 37 | relativeJavaDirPath = downloadJava(tmpDirPath, lspCliDirPath, platform, arch) 38 | 39 | print("Setting default for JAVA_HOME in startup script...") 40 | 41 | if platform == "windows": 42 | lspCliDirPath.joinpath("bin", "lsp-cli").unlink() 43 | binScriptPath = lspCliDirPath.joinpath("bin", "lsp-cli.bat") 44 | searchPattern = re.compile("^set REPO=.*$", flags=re.MULTILINE) 45 | else: 46 | lspCliDirPath.joinpath("bin", "lsp-cli.bat").unlink() 47 | binScriptPath = lspCliDirPath.joinpath("bin", "lsp-cli") 48 | searchPattern = re.compile("^BASEDIR=.*$", flags=re.MULTILINE) 49 | 50 | with open(binScriptPath, "r") as file: binScript = file.read() 51 | 52 | if platform == "windows": 53 | insertStr = f"\r\nif not defined JAVA_HOME set JAVA_HOME=\"%BASEDIR%\\{relativeJavaDirPath}\"" 54 | else: 55 | insertStr = f"\n[ -z \"$JAVA_HOME\" ] && JAVA_HOME=\"$BASEDIR\"/{relativeJavaDirPath}" 56 | 57 | regexMatch = searchPattern.search(binScript) 58 | assert regexMatch is not None 59 | binScript = binScript[:regexMatch.end()] + insertStr + binScript[regexMatch.end():] 60 | with open(binScriptPath, "w") as file: file.write(binScript) 61 | 62 | lspCliBinaryArchiveFormat = ("zip" if platform == "windows" else "gztar") 63 | lspCliBinaryArchiveExtension = (".zip" if platform == "windows" else ".tar.gz") 64 | lspCliBinaryArchivePath = targetDirPath.joinpath( 65 | f"lsp-cli-{lspCliVersion}-{platform}-{arch}") 66 | print(f"Creating binary archive '{lspCliBinaryArchivePath}{lspCliBinaryArchiveExtension}'...") 67 | shutil.make_archive(str(lspCliBinaryArchivePath), lspCliBinaryArchiveFormat, 68 | root_dir=tmpDirPath) 69 | print("") 70 | 71 | 72 | 73 | def downloadJava(tmpDirPath: pathlib.Path, lspCliDirPath: pathlib.Path, 74 | platform: str, arch: str) -> str: 75 | javaArchiveExtension = (".zip" if platform == "windows" else ".tar.gz") 76 | javaArchiveName = (f"OpenJDK11U-jdk_{arch}_{platform}_hotspot_" 77 | f"{javaVersion.replace('+', '_')}{javaArchiveExtension}") 78 | 79 | javaUrl = ("https://github.com/adoptium/temurin11-binaries/releases/download/" 80 | f"jdk-{urllib.parse.quote_plus(javaVersion)}/{javaArchiveName}") 81 | javaArchivePath = lspCliDirPath.joinpath(javaArchiveName) 82 | print(f"Downloading JDK from '{javaUrl}' to '{javaArchivePath}'...") 83 | urllib.request.urlretrieve(javaUrl, javaArchivePath) 84 | print("Extracting JDK archive...") 85 | 86 | if javaArchiveExtension == ".zip": 87 | with zipfile.ZipFile(javaArchivePath, "r") as zipFile: zipFile.extractall(path=tmpDirPath) 88 | else: 89 | with tarfile.open(javaArchivePath, "r:gz") as tarFile: tarFile.extractall(path=tmpDirPath) 90 | 91 | print("Removing JDK archive...") 92 | javaArchivePath.unlink() 93 | 94 | relativeJavaDirPathString = f"jdk-{javaVersion}" 95 | jdkDirPath = tmpDirPath.joinpath(relativeJavaDirPathString) 96 | jmodsDirPath = (jdkDirPath.joinpath("jmods") if platform == "mac" else 97 | jdkDirPath.joinpath("Contents", "Home", "jmods")) 98 | javaTargetDirPath = lspCliDirPath.joinpath(relativeJavaDirPathString) 99 | 100 | print("Creating Java distribution...") 101 | subprocess.run(["jlink", "--module-path", str(jmodsDirPath), "--add-modules", "java.se", 102 | "--strip-debug", "--no-man-pages", "--no-header-files", "--compress=2", 103 | "--output", str(javaTargetDirPath)]) 104 | 105 | print("Removing JDK directory...") 106 | shutil.rmtree(jdkDirPath) 107 | 108 | return relativeJavaDirPathString 109 | 110 | 111 | 112 | def getLspCliVersion() -> str: 113 | with open("pom.xml", "r") as file: 114 | regexMatch = re.search(r"(.*?)", file.read()) 115 | assert regexMatch is not None 116 | return regexMatch.group(1) 117 | 118 | 119 | 120 | def main() -> None: 121 | createBinaryArchive("linux", "x64") 122 | createBinaryArchive("mac", "x64") 123 | createBinaryArchive("windows", "x64") 124 | 125 | 126 | if __name__ == "__main__": 127 | main() 128 | -------------------------------------------------------------------------------- /tools/inspectWithIntellijIdea.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # Copyright (C) 2021 Julian Valentin, lsp-cli Development Community 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | 9 | import argparse 10 | import pathlib 11 | import subprocess 12 | import sys 13 | import tarfile 14 | import tempfile 15 | import urllib.request 16 | 17 | 18 | 19 | def downloadIdea(tmpDirPath: pathlib.Path) -> pathlib.Path: 20 | print("Downloading IntelliJ IDEA...") 21 | archiveFilePath = tmpDirPath.joinpath("idea.tar.gz") 22 | urllib.request.urlretrieve("https://download.jetbrains.com/idea/ideaIC-2021.2.tar.gz", 23 | archiveFilePath) 24 | 25 | print("Extracting IntelliJ IDEA...") 26 | with tarfile.open(archiveFilePath) as f: f.extractall(tmpDirPath) 27 | archiveFilePath.unlink() 28 | 29 | return tmpDirPath.joinpath("idea-IC-212.4746.92") 30 | 31 | 32 | 33 | def runIdea(ideaDirPath: pathlib.Path, tmpDirPath: pathlib.Path) -> None: 34 | repoDirPath = pathlib.Path(__file__).parent.parent.resolve() 35 | resultsDirPath = tmpDirPath.joinpath("results") 36 | 37 | print("Running IntelliJ IDEA...") 38 | subprocess.run([str(ideaDirPath.joinpath("bin", "idea.sh")), "inspect", str(repoDirPath), 39 | repoDirPath.joinpath(".idea", "inspectionProfiles", "Project_Default.xml"), 40 | str(resultsDirPath), "-v2", "-d", "src"]) 41 | 42 | hasProblems = False 43 | 44 | for childPath in sorted(resultsDirPath.iterdir()): 45 | if childPath.name == ".descriptions.xml": continue 46 | 47 | if not hasProblems: 48 | print("") 49 | print("Found problems with IntelliJ IDEA!") 50 | 51 | hasProblems = True 52 | print("") 53 | print(f"{childPath.name}:") 54 | print("") 55 | with open(childPath, "r") as f: print(f.read()) 56 | 57 | if hasProblems: 58 | sys.exit(1) 59 | else: 60 | print("") 61 | print("No problems found with IntelliJ IDEA.") 62 | 63 | 64 | 65 | def main() -> None: 66 | parser = argparse.ArgumentParser(description="Inspect Code with IntelliJ IDEA.") 67 | parser.add_argument("--idea-path", type=pathlib.Path, 68 | help="Directory to IntelliJ IDEA to use; will be downloaded if omitted") 69 | arguments = parser.parse_args() 70 | 71 | with tempfile.TemporaryDirectory() as tmpDirPathStr: 72 | tmpDirPath = pathlib.Path(tmpDirPathStr) 73 | ideaDirPath = (arguments.idea_path if arguments.idea_path is not None else 74 | downloadIdea(tmpDirPath)) 75 | runIdea(ideaDirPath, tmpDirPath) 76 | 77 | 78 | 79 | if __name__ == "__main__": 80 | main() 81 | --------------------------------------------------------------------------------