Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65c32347b5 | ||
|
|
b742a191ee | ||
|
|
9229952cd9 | ||
|
|
389cfdd583 | ||
|
|
3f1e851152 | ||
|
|
6b1fe63294 | ||
|
|
821fbb3566 | ||
|
|
3670564712 | ||
|
|
ced402ad5d | ||
|
|
5f8d072b86 | ||
|
|
f84f285db3 | ||
|
|
44194c000d | ||
|
|
c61bd07bb5 | ||
|
|
3df0a0f796 | ||
|
|
f16dfc92b2 | ||
|
|
96faf18d96 | ||
|
|
6393210a31 | ||
|
|
7151847d1f | ||
|
|
d5ffbd86f3 | ||
|
|
ab8b90b187 | ||
|
|
8d42c49b77 | ||
|
|
0e24296f67 | ||
|
|
7f87dc49f8 | ||
|
|
cc4177b096 |
Generated
+3
-3
@@ -3915,9 +3915,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
|
||||
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -11,6 +11,8 @@ on:
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v2
|
||||
|
||||
Vendored
+2
-2
@@ -41,8 +41,8 @@
|
||||
"editor.formatOnSave": true,
|
||||
"files.insertFinalNewline": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true,
|
||||
"source.organizeImports": true
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "explicit"
|
||||
},
|
||||
},
|
||||
"eslint.format.enable": true,
|
||||
|
||||
@@ -1,5 +1,34 @@
|
||||
# C/C++ for Visual Studio Code Changelog
|
||||
|
||||
## Version 1.19.2: January 22, 2024
|
||||
### Enhancements
|
||||
* Implement progressive population of IntelliSense results. [#7759](https://github.com/microsoft/vscode-cpptools/issues/7759)
|
||||
* Improve memory efficiency by using token parsing in the 'Add #include' feature. [#11515](https://github.com/microsoft/vscode-cpptools/issues/11515)
|
||||
* Improve the types supported for the 'Add #include' code action.
|
||||
* Various performance improvements.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix shell escaping for `cppbuild` task command line arguments. [#11422](https://github.com/microsoft/vscode-cpptools/issues/11422)
|
||||
* Fix IntelliSense not updating when a `#include` is added from a refactor command. [#11549](https://github.com/microsoft/vscode-cpptools/issues/11549)
|
||||
* Fix formatting not working in headers after using 'Extract to Function'. [#11729](https://github.com/microsoft/vscode-cpptools/issues/11729)
|
||||
* Fix document symbol requests not checking for cancellation. [#11750](https://github.com/microsoft/vscode-cpptools/issues/11750)
|
||||
* Fix code analysis results getting cleared after there's a configuration update. [#11790](https://github.com/microsoft/vscode-cpptools/issues/11790)
|
||||
* Fix an exception getting thrown if IntelliSense is disabled but a configuration provider is registered. [#11795](https://github.com/microsoft/vscode-cpptools/issues/11795)
|
||||
* Fix German code analysis translations. [PR #11845](https://github.com/microsoft/vscode-cpptools/pull/11845)
|
||||
* Thank you for the contribution [@Sir2B (Tobias Obermayer)](https://github.com/Sir2B)
|
||||
* Fix an EACCES error when using include wildcards with system includes. [#11833](https://github.com/microsoft/vscode-cpptools/issues/11833)
|
||||
* Fix IntelliSense passes occurring while a user is typing, not honoring the `C_Cpp.intelliSenseUpdateDelay` setting.
|
||||
* Fix a call hierarchy bug leading to use of header-only TU's unnecessarily.
|
||||
* Fix a bug that could lead to missing TU source file candidates.
|
||||
* Fix a "random" IntelliSense crash during completion.
|
||||
* Fix a crash if access to `/dev/urandom` is restricted.
|
||||
* Lots of other minor fixes.
|
||||
|
||||
## Version 1.19.1: November 21, 2023
|
||||
### Bug Fixes
|
||||
* Fix `Add '#include'` code actions for Mac frameworks. [#11579](https://github.com/microsoft/vscode-cpptools/issues/11579)
|
||||
* Fix snippet and include completion. [#11715](https://github.com/microsoft/vscode-cpptools/issues/11715), [#11720](https://github.com/microsoft/vscode-cpptools/issues/11720)
|
||||
|
||||
## Version 1.19.0: November 16, 2023
|
||||
### Bug Fixes
|
||||
* Fix IntelliSense bug with type deduction using concepts. [#8132](https://github.com/microsoft/vscode-cpptools/issues/8132)
|
||||
|
||||
+186
-146
@@ -1172,6 +1172,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
array-timsort 1.0.3 - MIT
|
||||
https://github.com/kaelzhang/node-array-timsort
|
||||
|
||||
Copyright (c) 2015 Marco Ziccardi
|
||||
|
||||
The MIT License
|
||||
|
||||
@@ -1788,7 +1789,7 @@ THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
follow-redirects 1.15.3 - MIT
|
||||
follow-redirects 1.15.4 - MIT
|
||||
https://github.com/follow-redirects/follow-redirects
|
||||
|
||||
Copyright 2014-present Olivier Lalonde <[email protected]> , James Talmage <[email protected]> , Ruben Verborgh
|
||||
@@ -3005,7 +3006,7 @@ The notices below are from non-npm sources.
|
||||
- LLDB (https://lldb.llvm.org/)
|
||||
- LLVM (http://llvm.org/)
|
||||
- MI Debug Engine (https://github.com/Microsoft/MIEngine)
|
||||
- Mono (https://github.com/mono/mono)
|
||||
- musl (https://git.musl-libc.org/cgit/musl)
|
||||
- SQLite (https://www.sqlite.org/)
|
||||
Includes:functions (from fossil) (https://fossil-scm.org)
|
||||
- SoftFloat (http://www.jhauser.us/arithmetic/SoftFloat.html)
|
||||
@@ -3646,165 +3647,204 @@ THE SOFTWARE.
|
||||
=========================================
|
||||
END OF MI Debug Engine NOTICES AND INFORMATION
|
||||
|
||||
%% Mono NOTICES AND INFORMATION BEGIN HERE
|
||||
%% musl NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
The Mono project may include other third party copyrights and notices, as listed in the
|
||||
copyright.txt file included with the Mono project.
|
||||
musl as a whole is licensed under the following standard MIT license:
|
||||
|
||||
The MIT License (MIT)
|
||||
----------------------------------------------------------------------
|
||||
Copyright © 2005-2020 Rich Felker, et al.
|
||||
|
||||
Copyright (c) .NET Foundation and its contributors. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Authors/contributors include:
|
||||
|
||||
The following code is linked with the final Mono runtime, the libmono
|
||||
embeddable runtime:
|
||||
A. Wilcox
|
||||
Ada Worcester
|
||||
Alex Dowad
|
||||
Alex Suykov
|
||||
Alexander Monakov
|
||||
Andre McCurdy
|
||||
Andrew Kelley
|
||||
Anthony G. Basile
|
||||
Aric Belsito
|
||||
Arvid Picciani
|
||||
Bartosz Brachaczek
|
||||
Benjamin Peterson
|
||||
Bobby Bingham
|
||||
Boris Brezillon
|
||||
Brent Cook
|
||||
Chris Spiegel
|
||||
Clément Vasseur
|
||||
Daniel Micay
|
||||
Daniel Sabogal
|
||||
Daurnimator
|
||||
David Carlier
|
||||
David Edelsohn
|
||||
Denys Vlasenko
|
||||
Dmitry Ivanov
|
||||
Dmitry V. Levin
|
||||
Drew DeVault
|
||||
Emil Renner Berthing
|
||||
Fangrui Song
|
||||
Felix Fietkau
|
||||
Felix Janda
|
||||
Gianluca Anzolin
|
||||
Hauke Mehrtens
|
||||
He X
|
||||
Hiltjo Posthuma
|
||||
Isaac Dunham
|
||||
Jaydeep Patil
|
||||
Jens Gustedt
|
||||
Jeremy Huntwork
|
||||
Jo-Philipp Wich
|
||||
Joakim Sindholt
|
||||
John Spencer
|
||||
Julien Ramseier
|
||||
Justin Cormack
|
||||
Kaarle Ritvanen
|
||||
Khem Raj
|
||||
Kylie McClain
|
||||
Leah Neukirchen
|
||||
Luca Barbato
|
||||
Luka Perkov
|
||||
M Farkas-Dyck (Strake)
|
||||
Mahesh Bodapati
|
||||
Markus Wichmann
|
||||
Masanori Ogino
|
||||
Michael Clark
|
||||
Michael Forney
|
||||
Mikhail Kremnyov
|
||||
Natanael Copa
|
||||
Nicholas J. Kain
|
||||
orc
|
||||
Pascal Cuoq
|
||||
Patrick Oppenlander
|
||||
Petr Hosek
|
||||
Petr Skocik
|
||||
Pierre Carrier
|
||||
Reini Urban
|
||||
Rich Felker
|
||||
Richard Pennington
|
||||
Ryan Fairfax
|
||||
Samuel Holland
|
||||
Segev Finer
|
||||
Shiz
|
||||
sin
|
||||
Solar Designer
|
||||
Stefan Kristiansson
|
||||
Stefan O'Rear
|
||||
Szabolcs Nagy
|
||||
Timo Teräs
|
||||
Trutz Behn
|
||||
Valentin Ochs
|
||||
Will Dietz
|
||||
William Haddon
|
||||
William Pitcock
|
||||
|
||||
* support/minizip: BSD license (modified).
|
||||
* Copyright (C) 1998-2005 Gilles Vollant
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
Portions of this software are derived from third-party works licensed
|
||||
under terms compatible with the above MIT license:
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
The TRE regular expression implementation (src/regex/reg* and
|
||||
src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed
|
||||
under a 2-clause BSD license (license text in the source files). The
|
||||
included version has been heavily modified by Rich Felker in 2012, in
|
||||
the interests of size, simplicity, and namespace cleanliness.
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
Much of the math library code (src/math/* and src/complex/*) is
|
||||
Copyright © 1993,2004 Sun Microsystems or
|
||||
Copyright © 2003-2011 David Schultz or
|
||||
Copyright © 2003-2009 Steven G. Kargl or
|
||||
Copyright © 2003-2009 Bruce D. Evans or
|
||||
Copyright © 2008 Stephen L. Moshier or
|
||||
Copyright © 2017-2018 Arm Limited
|
||||
and labelled as such in comments in the individual source files. All
|
||||
have been licensed under extremely permissive terms.
|
||||
|
||||
* mono/utils/freebsd-dwarf.h.
|
||||
* Copyright (c) 2007 John Birrell ([email protected])
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
*notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
*notice, this list of conditions and the following disclaimer in the
|
||||
*documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED.IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008
|
||||
The Android Open Source Project and is licensed under a two-clause BSD
|
||||
license. It was taken from Bionic libc, used on Android.
|
||||
|
||||
* mono/utils/freebsd-elf_common.h, freebsd-elf64.h freebsd-elf32.h: BSD license.
|
||||
* Copyright (c) 1998 John D. Polstra.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
The AArch64 memcpy and memset code (src/string/aarch64/*) are
|
||||
Copyright © 1999-2019, Arm Limited.
|
||||
|
||||
* mono/utils/bsearch.c: BSD license.
|
||||
* Taken from NetBSD with some minor modifications.
|
||||
*
|
||||
* Copyright (c) 1990, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
The implementation of DES for crypt (src/crypt/crypt_des.c) is
|
||||
Copyright © 1994 David Burren. It is licensed under a BSD license.
|
||||
|
||||
The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was
|
||||
originally written by Solar Designer and placed into the public
|
||||
domain. The code also comes with a fallback permissive license for use
|
||||
in jurisdictions that may not recognize the public domain.
|
||||
|
||||
The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011
|
||||
Valentin Ochs and is licensed under an MIT-style license.
|
||||
|
||||
The x86_64 port was written by Nicholas J. Kain and is licensed under
|
||||
the standard MIT terms.
|
||||
|
||||
The mips and microblaze ports were originally written by Richard
|
||||
Pennington for use in the ellcc project. The original code was adapted
|
||||
by Rich Felker for build system and code conventions during upstream
|
||||
integration. It is licensed under the standard MIT terms.
|
||||
|
||||
The mips64 port was contributed by Imagination Technologies and is
|
||||
licensed under the standard MIT terms.
|
||||
|
||||
The powerpc port was also originally written by Richard Pennington,
|
||||
and later supplemented and integrated by John Spencer. It is licensed
|
||||
under the standard MIT terms.
|
||||
|
||||
All other files which have no copyright comments are original works
|
||||
produced specifically for use as part of this library, written either
|
||||
by Rich Felker, the main author of the library, or by one or more
|
||||
contibutors listed above. Details on authorship of individual files
|
||||
can be found in the git version control history of the project. The
|
||||
omission of copyright and license comments in each file is in the
|
||||
interest of source tree size.
|
||||
|
||||
In addition, permission is hereby granted for all public header files
|
||||
(include/* and arch/*/bits/*) and crt files intended to be linked into
|
||||
applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit
|
||||
the copyright notice and permission notice otherwise required by the
|
||||
license, and to use these files without any requirement of
|
||||
attribution. These files include substantial contributions from:
|
||||
|
||||
Bobby Bingham
|
||||
John Spencer
|
||||
Nicholas J. Kain
|
||||
Rich Felker
|
||||
Richard Pennington
|
||||
Stefan Kristiansson
|
||||
Szabolcs Nagy
|
||||
|
||||
all of whom have explicitly granted such permission.
|
||||
|
||||
This file previously contained text expressing a belief that most of
|
||||
the files covered by the above exception were sufficiently trivial not
|
||||
to be subject to copyright, resulting in confusion over whether it
|
||||
negated the permissions granted in the license. In the spirit of
|
||||
permissive licensing, and of not having licensing issues being an
|
||||
obstacle to adoption, that text has been removed.
|
||||
|
||||
* mono/io-layer/wapi_glob.h, wapi_glob.c: BSD license
|
||||
* Copyright (c) 1989, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Guido van Rossum.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
=========================================
|
||||
END OF Mono NOTICES AND INFORMATION
|
||||
END OF musl NOTICES AND INFORMATION
|
||||
|
||||
%% libuv NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"c_cpp.command.toggleDimInactiveRegions.title": "Farbgebung für inaktive Regionen umschalten",
|
||||
"c_cpp.command.resetDatabase.title": "IntelliSense-Datenbank zurücksetzen",
|
||||
"c_cpp.command.takeSurvey.title": "An Umfrage teilnehmen",
|
||||
"c_cpp.command.restartIntelliSenseForFile.title": "IntelliSense für Active File neu starten",
|
||||
"c_cpp.command.restartIntelliSenseForFile.title": "IntelliSense für \"Aktive Datei\" neu starten",
|
||||
"c_cpp.command.logDiagnostics.title": "Diagnose protokollieren",
|
||||
"c_cpp.command.referencesViewGroupByType.title": "Nach Verweistyp gruppieren",
|
||||
"c_cpp.command.referencesViewUngroupByType.title": "Gruppierung nach Verweistyp aufheben",
|
||||
@@ -37,10 +37,10 @@
|
||||
"c_cpp.command.GoToNextDirectiveInGroup.title": "Zur nächsten Präprozessoranweisung in bedingter Gruppe wechseln",
|
||||
"c_cpp.command.GoToPrevDirectiveInGroup.title": "Zur vorherigen Präprozessoranweisung in bedingter Gruppe wechseln",
|
||||
"c_cpp.command.CreateDeclarationOrDefinition.title": "Deklaration/Definition erstellen",
|
||||
"c_cpp.command.RunCodeAnalysisOnActiveFile.title": "Code Analysis auf \"Aktive Dateien\" ausführen",
|
||||
"c_cpp.command.RunCodeAnalysisOnOpenFiles.title": "Code Analysis auf \"Dateien öffnen\" ausführen",
|
||||
"c_cpp.command.RunCodeAnalysisOnAllFiles.title": "Code Analysis auf \"Alle Dateien\" ausführen",
|
||||
"c_cpp.command.RemoveAllCodeAnalysisProblems.title": "Alle Code Analysis Probleme löschen",
|
||||
"c_cpp.command.RunCodeAnalysisOnActiveFile.title": "Code Analyse auf \"Aktive Datei\" ausführen",
|
||||
"c_cpp.command.RunCodeAnalysisOnOpenFiles.title": "Code Analyse auf \"Offene Dateien\" ausführen",
|
||||
"c_cpp.command.RunCodeAnalysisOnAllFiles.title": "Code Analyse auf \"Alle Dateien\" ausführen",
|
||||
"c_cpp.command.RemoveAllCodeAnalysisProblems.title": "Alle Code Analyse Probleme löschen",
|
||||
"c_cpp.command.BuildAndDebugFile.title": "C/C++-Datei debuggen",
|
||||
"c_cpp.command.BuildAndRunFile.title": "C/C++-Datei ausführen",
|
||||
"c_cpp.command.AddDebugConfiguration.title": "Debugkonfiguration hinzufügen",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "cpptools",
|
||||
"displayName": "C/C++",
|
||||
"description": "C/C++ IntelliSense, debugging, and code browsing.",
|
||||
"version": "1.19.0-main",
|
||||
"version": "1.19.2-main",
|
||||
"publisher": "ms-vscode",
|
||||
"icon": "LanguageCCPP_color_128x.png",
|
||||
"readme": "README.md",
|
||||
@@ -5811,17 +5811,17 @@
|
||||
},
|
||||
"configurationDefaults": {
|
||||
"[cpp]": {
|
||||
"editor.wordBasedSuggestions": false,
|
||||
"editor.wordBasedSuggestions": "off",
|
||||
"editor.suggest.insertMode": "replace",
|
||||
"editor.semanticHighlighting.enabled": true
|
||||
},
|
||||
"[cuda-cpp]": {
|
||||
"editor.wordBasedSuggestions": false,
|
||||
"editor.wordBasedSuggestions": "off",
|
||||
"editor.suggest.insertMode": "replace",
|
||||
"editor.semanticHighlighting.enabled": true
|
||||
},
|
||||
"[c]": {
|
||||
"editor.wordBasedSuggestions": false,
|
||||
"editor.wordBasedSuggestions": "off",
|
||||
"editor.suggest.insertMode": "replace",
|
||||
"editor.semanticHighlighting.enabled": true
|
||||
}
|
||||
|
||||
@@ -470,9 +470,9 @@
|
||||
]
|
||||
},
|
||||
"c_cpp.configuration.autocomplete.markdownDescription": {
|
||||
"message": "Controls the auto-completion provider. If `disabled` and you want word-based completion, you will also need to set `\"[cpp]\": {\"editor.wordBasedSuggestions\": true}` (and similarly for `c` and `cuda-cpp` languages).",
|
||||
"message": "Controls the auto-completion provider. If `disabled` and you want word-based completion, you will also need to set `\"[cpp]\": {\"editor.wordBasedSuggestions\": <value>}` (and similarly for `c` and `cuda-cpp` languages).",
|
||||
"comment": [
|
||||
"Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered."
|
||||
"Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered -- except for text \"value\", which is a placeholder that should be translated."
|
||||
]
|
||||
},
|
||||
"c_cpp.configuration.autocomplete.default.description": "Uses the active IntelliSense engine.",
|
||||
|
||||
@@ -1162,16 +1162,16 @@ abstract class DefaultConfigurationProvider implements IConfigurationAssetProvid
|
||||
|
||||
class WindowsConfigurationProvider extends DefaultConfigurationProvider {
|
||||
private executable: string = "a.exe";
|
||||
private pipeProgram: string = "<" + localize("path.to.pipe.program", "full path to pipe program such as {0}", "plink.exe").replace(/\"/g, "\\\"") + ">";
|
||||
private pipeProgram: string = "<" + localize("path.to.pipe.program", "full path to pipe program such as {0}", "plink.exe").replace(/"/g, '\\"') + ">";
|
||||
private MIMode: string = 'gdb';
|
||||
private setupCommandsBlock: string = `"setupCommands": [
|
||||
{
|
||||
"description": "${localize("enable.pretty.printing", "Enable pretty-printing for {0}", "gdb").replace(/\"/g, "\\\"")}",
|
||||
"description": "${localize("enable.pretty.printing", "Enable pretty-printing for {0}", "gdb").replace(/"/g, '\\"')}",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"description": "${localize("enable.intel.disassembly.flavor", "Set Disassembly Flavor to {0}", "Intel").replace(/\"/g, "\\\"")}",
|
||||
"description": "${localize("enable.intel.disassembly.flavor", "Set Disassembly Flavor to {0}", "Intel").replace(/"/g, '\\"')}",
|
||||
"text": "-gdb-set disassembly-flavor intel",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
@@ -1205,12 +1205,12 @@ class LinuxConfigurationProvider extends DefaultConfigurationProvider {
|
||||
private MIMode: string = 'gdb';
|
||||
private setupCommandsBlock: string = `"setupCommands": [
|
||||
{
|
||||
"description": "${localize("enable.pretty.printing", "Enable pretty-printing for {0}", "gdb").replace(/\"/g, "\\\"")}",
|
||||
"description": "${localize("enable.pretty.printing", "Enable pretty-printing for {0}", "gdb").replace(/"/g, '\\"')}",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"description": "${localize("enable.intel.disassembly.flavor", "Set Disassembly Flavor to {0}", "Intel").replace(/\"/g, "\\\"")}",
|
||||
"description": "${localize("enable.intel.disassembly.flavor", "Set Disassembly Flavor to {0}", "Intel").replace(/"/g, '\\"')}",
|
||||
"text": "-gdb-set disassembly-flavor intel",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ function createLaunchString(name: string, type: string, executable: string): str
|
||||
return `"name": "${name}",
|
||||
"type": "${type}",
|
||||
"request": "launch",
|
||||
"program": "${localize("enter.program.name", "enter program name, for example {0}", "$\{workspaceFolder\}" + "/" + executable).replace(/\"/g, "\\\"")}",
|
||||
"program": "${localize("enter.program.name", "enter program name, for example {0}", "$\{workspaceFolder\}" + "/" + executable).replace(/"/g, '\\"')}",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "$\{fileDirname\}",
|
||||
@@ -106,7 +106,7 @@ function createAttachString(name: string, type: string, executable: string): str
|
||||
"name": "${name}",
|
||||
"type": "${type}",
|
||||
"request": "attach",{0}
|
||||
`, [type === "cppdbg" ? `${os.EOL}"program": "${localize("enter.program.name", "enter program name, for example {0}", "$\{workspaceFolder\}" + "/" + executable).replace(/\"/g, "\\\"")}",` : ""]);
|
||||
`, [type === "cppdbg" ? `${os.EOL}"program": "${localize("enter.program.name", "enter program name, for example {0}", "$\{workspaceFolder\}" + "/" + executable).replace(/"/g, '\\"')}",` : ""]);
|
||||
}
|
||||
|
||||
function createRemoteAttachString(name: string, type: string, executable: string): string {
|
||||
@@ -114,7 +114,7 @@ function createRemoteAttachString(name: string, type: string, executable: string
|
||||
"name": "${name}",
|
||||
"type": "${type}",
|
||||
"request": "attach",
|
||||
"program": "${localize("enter.program.name", "enter program name, for example {0}", "$\{workspaceFolder\}" + "/" + executable).replace(/\"/g, "\\\"")}",
|
||||
"program": "${localize("enter.program.name", "enter program name, for example {0}", "$\{workspaceFolder\}" + "/" + executable).replace(/"/g, '\\"')}",
|
||||
"processId": "$\{command:pickRemoteProcess\}"
|
||||
`;
|
||||
}
|
||||
@@ -158,7 +158,7 @@ abstract class Configuration implements IConfiguration {
|
||||
export class MIConfigurations extends Configuration {
|
||||
|
||||
public GetLaunchConfiguration(): IConfigurationSnippet {
|
||||
const name: string = `(${this.MIMode}) ${localize("launch.string", "Launch").replace(/\"/g, "\\\"")}`;
|
||||
const name: string = `(${this.MIMode}) ${localize("launch.string", "Launch").replace(/"/g, '\\"')}`;
|
||||
|
||||
const body: string = formatString(`{
|
||||
\t${indentJsonString(createLaunchString(name, this.miDebugger, this.executable))},
|
||||
@@ -168,7 +168,7 @@ export class MIConfigurations extends Configuration {
|
||||
|
||||
return {
|
||||
"label": configPrefix + name,
|
||||
"description": localize("launch.with", "Launch with {0}.", this.MIMode).replace(/\"/g, "\\\""),
|
||||
"description": localize("launch.with", "Launch with {0}.", this.MIMode).replace(/"/g, '\\"'),
|
||||
"bodyText": body.trim(),
|
||||
"isInitialConfiguration": true,
|
||||
"debuggerType": DebuggerType.cppdbg
|
||||
@@ -176,7 +176,7 @@ export class MIConfigurations extends Configuration {
|
||||
}
|
||||
|
||||
public GetAttachConfiguration(): IConfigurationSnippet {
|
||||
const name: string = `(${this.MIMode}) ${localize("attach.string", "Attach").replace(/\"/g, "\\\"")}`;
|
||||
const name: string = `(${this.MIMode}) ${localize("attach.string", "Attach").replace(/"/g, '\\"')}`;
|
||||
|
||||
const body: string = formatString(`{
|
||||
\t${indentJsonString(createAttachString(name, this.miDebugger, this.executable))}
|
||||
@@ -186,7 +186,7 @@ export class MIConfigurations extends Configuration {
|
||||
|
||||
return {
|
||||
"label": configPrefix + name,
|
||||
"description": localize("attach.with", "Attach with {0}.", this.MIMode).replace(/\"/g, "\\\""),
|
||||
"description": localize("attach.with", "Attach with {0}.", this.MIMode).replace(/"/g, '\\"'),
|
||||
"bodyText": body.trim(),
|
||||
"debuggerType": DebuggerType.cppdbg
|
||||
};
|
||||
@@ -197,7 +197,7 @@ export class MIConfigurations extends Configuration {
|
||||
export class PipeTransportConfigurations extends Configuration {
|
||||
|
||||
public GetLaunchConfiguration(): IConfigurationSnippet {
|
||||
const name: string = `(${this.MIMode}) ${localize("pipe.launch", "Pipe Launch").replace(/\"/g, "\\\"")}`;
|
||||
const name: string = `(${this.MIMode}) ${localize("pipe.launch", "Pipe Launch").replace(/"/g, '\\"')}`;
|
||||
|
||||
const body: string = formatString(`
|
||||
{
|
||||
@@ -208,7 +208,7 @@ export class PipeTransportConfigurations extends Configuration {
|
||||
|
||||
return {
|
||||
"label": configPrefix + name,
|
||||
"description": localize("pipe.launch.with", "Pipe Launch with {0}.", this.MIMode).replace(/\"/g, "\\\""),
|
||||
"description": localize("pipe.launch.with", "Pipe Launch with {0}.", this.MIMode).replace(/"/g, '\\"'),
|
||||
"bodyText": body.trim(),
|
||||
"debuggerType": DebuggerType.cppdbg
|
||||
};
|
||||
@@ -216,7 +216,7 @@ export class PipeTransportConfigurations extends Configuration {
|
||||
}
|
||||
|
||||
public GetAttachConfiguration(): IConfigurationSnippet {
|
||||
const name: string = `(${this.MIMode}) ${localize("pipe.attach", "Pipe Attach").replace(/\"/g, "\\\"")}`;
|
||||
const name: string = `(${this.MIMode}) ${localize("pipe.attach", "Pipe Attach").replace(/"/g, '\\"')}`;
|
||||
|
||||
const body: string = formatString(`
|
||||
{
|
||||
@@ -226,7 +226,7 @@ export class PipeTransportConfigurations extends Configuration {
|
||||
}`, [this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]);
|
||||
return {
|
||||
"label": configPrefix + name,
|
||||
"description": localize("pipe.attach.with", "Pipe Attach with {0}.", this.MIMode).replace(/\"/g, "\\\""),
|
||||
"description": localize("pipe.attach.with", "Pipe Attach with {0}.", this.MIMode).replace(/"/g, '\\"'),
|
||||
"bodyText": body.trim(),
|
||||
"debuggerType": DebuggerType.cppdbg
|
||||
};
|
||||
@@ -237,7 +237,7 @@ export class PipeTransportConfigurations extends Configuration {
|
||||
export class WindowsConfigurations extends Configuration {
|
||||
|
||||
public GetLaunchConfiguration(): IConfigurationSnippet {
|
||||
const name: string = `(Windows) ${localize("launch.string", "Launch").replace(/\"/g, "\\\"")}`;
|
||||
const name: string = `(Windows) ${localize("launch.string", "Launch").replace(/"/g, '\\"')}`;
|
||||
|
||||
const body: string = `
|
||||
{
|
||||
@@ -246,7 +246,7 @@ export class WindowsConfigurations extends Configuration {
|
||||
|
||||
return {
|
||||
"label": configPrefix + name,
|
||||
"description": localize("launch.with.vs.debugger", "Launch with the Visual Studio C/C++ debugger.").replace(/\"/g, "\\\""),
|
||||
"description": localize("launch.with.vs.debugger", "Launch with the Visual Studio C/C++ debugger.").replace(/"/g, '\\"'),
|
||||
"bodyText": body.trim(),
|
||||
"isInitialConfiguration": true,
|
||||
"debuggerType": DebuggerType.cppvsdbg
|
||||
@@ -255,7 +255,7 @@ export class WindowsConfigurations extends Configuration {
|
||||
}
|
||||
|
||||
public GetAttachConfiguration(): IConfigurationSnippet {
|
||||
const name: string = `(Windows) ${localize("attach.string", "Attach").replace(/\"/g, "\\\"")}`;
|
||||
const name: string = `(Windows) ${localize("attach.string", "Attach").replace(/"/g, '\\"')}`;
|
||||
|
||||
const body: string = `
|
||||
{
|
||||
@@ -264,7 +264,7 @@ export class WindowsConfigurations extends Configuration {
|
||||
|
||||
return {
|
||||
"label": configPrefix + name,
|
||||
"description": localize("attach.with.vs.debugger", "Attach to a process with the Visual Studio C/C++ debugger.").replace(/\"/g, "\\\""),
|
||||
"description": localize("attach.with.vs.debugger", "Attach to a process with the Visual Studio C/C++ debugger.").replace(/"/g, '\\"'),
|
||||
"bodyText": body.trim(),
|
||||
"debuggerType": DebuggerType.cppvsdbg
|
||||
};
|
||||
@@ -277,7 +277,7 @@ export class WSLConfigurations extends Configuration {
|
||||
public bashPipeProgram = process.arch === 'ia32' ? "${env:windir}\\\\sysnative\\\\bash.exe" : "${env:windir}\\\\system32\\\\bash.exe";
|
||||
|
||||
public GetLaunchConfiguration(): IConfigurationSnippet {
|
||||
const name: string = `(${this.MIMode}) ${localize("bash.on.windows.launch", "Bash on Windows Launch").replace(/\"/g, "\\\"")}`;
|
||||
const name: string = `(${this.MIMode}) ${localize("bash.on.windows.launch", "Bash on Windows Launch").replace(/"/g, '\\"')}`;
|
||||
|
||||
const body: string = formatString(`
|
||||
{
|
||||
@@ -287,14 +287,14 @@ export class WSLConfigurations extends Configuration {
|
||||
|
||||
return {
|
||||
"label": configPrefix + name,
|
||||
"description": localize("launch.bash.windows", "Launch in Bash on Windows using {0}.", this.MIMode).replace(/\"/g, "\\\""),
|
||||
"description": localize("launch.bash.windows", "Launch in Bash on Windows using {0}.", this.MIMode).replace(/"/g, '\\"'),
|
||||
"bodyText": body.trim(),
|
||||
"debuggerType": DebuggerType.cppdbg
|
||||
};
|
||||
}
|
||||
|
||||
public GetAttachConfiguration(): IConfigurationSnippet {
|
||||
const name: string = `(${this.MIMode}) ${localize("bash.on.windows.attach", "Bash on Windows Attach").replace(/\"/g, "\\\"")}`;
|
||||
const name: string = `(${this.MIMode}) ${localize("bash.on.windows.attach", "Bash on Windows Attach").replace(/"/g, '\\"')}`;
|
||||
|
||||
const body: string = formatString(`
|
||||
{
|
||||
@@ -304,7 +304,7 @@ export class WSLConfigurations extends Configuration {
|
||||
|
||||
return {
|
||||
"label": configPrefix + name,
|
||||
"description": localize("remote.attach.bash.windows", "Attach to a remote process running in Bash on Windows using {0}.", this.MIMode).replace(/\"/g, "\\\""),
|
||||
"description": localize("remote.attach.bash.windows", "Attach to a remote process running in Bash on Windows using {0}.", this.MIMode).replace(/"/g, '\\"'),
|
||||
"bodyText": body.trim(),
|
||||
"debuggerType": DebuggerType.cppdbg
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ export class ArchitectureReplacer {
|
||||
public static checkAndReplaceWSLPipeProgram(pipeProgramStr: string, expectedArch: ArchType): string | undefined {
|
||||
let replacedPipeProgram: string | undefined;
|
||||
const winDir: string | undefined = process.env.WINDIR ? process.env.WINDIR.toLowerCase() : undefined;
|
||||
const winDirAltDirSep: string | undefined = process.env.WINDIR ? process.env.WINDIR.replace('\\', '/').toLowerCase() : undefined;
|
||||
const winDirAltDirSep: string | undefined = process.env.WINDIR ? process.env.WINDIR.replace(/\\/g, '/').toLowerCase() : undefined;
|
||||
const winDirEnv: string = "${env:windir}";
|
||||
|
||||
if (winDir && winDirAltDirSep && (pipeProgramStr.indexOf(winDir) === 0 || pipeProgramStr.indexOf(winDirAltDirSep) === 0 || pipeProgramStr.indexOf(winDirEnv) === 0)) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import * as vscode from 'vscode';
|
||||
import { Position, Range, RequestType, TextDocumentIdentifier } from 'vscode-languageclient';
|
||||
import * as Telemetry from '../../telemetry';
|
||||
import { DefaultClient, workspaceReferences } from '../client';
|
||||
import { processDelayedDidOpen } from '../extension';
|
||||
import { CancellationSender } from '../references';
|
||||
import { makeVscodeRange } from '../utils';
|
||||
|
||||
@@ -104,7 +103,7 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider {
|
||||
}
|
||||
|
||||
public async prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.CallHierarchyItem | undefined> {
|
||||
await this.client.enqueue(() => processDelayedDidOpen(document));
|
||||
await this.client.ready;
|
||||
|
||||
workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest);
|
||||
workspaceReferences.clearViews();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
import * as vscode from 'vscode';
|
||||
import { Client, DefaultClient, GetDocumentSymbolRequest, GetDocumentSymbolRequestParams, GetDocumentSymbolResult, LocalizeDocumentSymbol, SymbolScope } from '../client';
|
||||
import { clients, processDelayedDidOpen } from '../extension';
|
||||
import { clients } from '../extension';
|
||||
import { getLocalizedString, getLocalizedSymbolScope } from '../localization';
|
||||
import { makeVscodeRange } from '../utils';
|
||||
|
||||
@@ -57,7 +57,7 @@ export class DocumentSymbolProvider implements vscode.DocumentSymbolProvider {
|
||||
const client: Client = clients.getClientFor(document.uri);
|
||||
if (client instanceof DefaultClient) {
|
||||
const defaultClient: DefaultClient = <DefaultClient>client;
|
||||
await client.enqueue(() => processDelayedDidOpen(document));
|
||||
await client.ready;
|
||||
const params: GetDocumentSymbolRequestParams = {
|
||||
uri: document.uri.toString()
|
||||
};
|
||||
|
||||
@@ -3,28 +3,65 @@
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
import * as vscode from 'vscode';
|
||||
import { ManualPromise } from '../../Utility/Async/manualPromise';
|
||||
import { CppFoldingRange, DefaultClient, FoldingRangeKind, GetFoldingRangesParams, GetFoldingRangesRequest, GetFoldingRangesResult } from '../client';
|
||||
import { processDelayedDidOpen } from '../extension';
|
||||
import { CppSettings } from '../settings';
|
||||
|
||||
interface FoldingRangeRequestInfo {
|
||||
promise: ManualPromise<vscode.FoldingRange[] | undefined> | undefined;
|
||||
}
|
||||
|
||||
export class FoldingRangeProvider implements vscode.FoldingRangeProvider {
|
||||
private client: DefaultClient;
|
||||
public onDidChangeFoldingRangesEvent = new vscode.EventEmitter<void>();
|
||||
public onDidChangeFoldingRanges?: vscode.Event<void>;
|
||||
|
||||
// Mitigate an issue where VS Code sends us an inordinate number of requests
|
||||
// for the same file without waiting for the prior request to complete or cancelling them.
|
||||
private pendingRequests: Map<string, FoldingRangeRequestInfo> = new Map<string, FoldingRangeRequestInfo>();
|
||||
|
||||
constructor(client: DefaultClient) {
|
||||
this.client = client;
|
||||
this.onDidChangeFoldingRanges = this.onDidChangeFoldingRangesEvent.event;
|
||||
}
|
||||
async provideFoldingRanges(document: vscode.TextDocument, context: vscode.FoldingContext, token: vscode.CancellationToken): Promise<vscode.FoldingRange[] | undefined> {
|
||||
await this.client.ready;
|
||||
const settings: CppSettings = new CppSettings();
|
||||
if (!settings.codeFolding) {
|
||||
return [];
|
||||
}
|
||||
const params: GetFoldingRangesParams = {
|
||||
uri: document.uri.toString()
|
||||
};
|
||||
|
||||
await this.client.enqueue(() => processDelayedDidOpen(document));
|
||||
const pendingRequest: FoldingRangeRequestInfo | undefined = this.pendingRequests.get(document.uri.toString());
|
||||
if (pendingRequest !== undefined) {
|
||||
if (pendingRequest.promise === undefined) {
|
||||
pendingRequest.promise = new ManualPromise<vscode.FoldingRange[] | undefined>();
|
||||
}
|
||||
console.log("Redundant folding ranges request received for: " + document.uri.toString());
|
||||
return pendingRequest.promise;
|
||||
}
|
||||
const foldingRangeRequestInfo: FoldingRangeRequestInfo = {
|
||||
promise: undefined
|
||||
};
|
||||
this.pendingRequests.set(document.uri.toString(), foldingRangeRequestInfo);
|
||||
|
||||
const promise: Promise<vscode.FoldingRange[] | undefined> = this.requestRanges(document.uri.toString(), token);
|
||||
await promise;
|
||||
this.pendingRequests.delete(document.uri.toString());
|
||||
if (foldingRangeRequestInfo.promise !== undefined) {
|
||||
promise.then(() => {
|
||||
foldingRangeRequestInfo.promise?.resolve(promise);
|
||||
}, () => {
|
||||
foldingRangeRequestInfo.promise?.reject(new vscode.CancellationError());
|
||||
});
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
private async requestRanges(uri: string, token: vscode.CancellationToken): Promise<vscode.FoldingRange[] | undefined>
|
||||
{
|
||||
const params: GetFoldingRangesParams = {
|
||||
uri
|
||||
};
|
||||
|
||||
const response: GetFoldingRangesResult = await this.client.languageClient.sendRequest(GetFoldingRangesRequest, params, token);
|
||||
if (token.isCancellationRequested || response.ranges === undefined) {
|
||||
@@ -55,6 +92,14 @@ export class FoldingRangeProvider implements vscode.FoldingRangeProvider {
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
// Consider all pending requests as being outdated. Cancel them all.
|
||||
const oldPendingRequests: Map<string, FoldingRangeRequestInfo> = this.pendingRequests;
|
||||
this.pendingRequests = new Map<string, FoldingRangeRequestInfo>();
|
||||
this.onDidChangeFoldingRangesEvent.fire();
|
||||
oldPendingRequests.forEach((value: FoldingRangeRequestInfo | undefined, _key: string) => {
|
||||
if (value !== undefined && value.promise !== undefined) {
|
||||
value.promise.reject(new vscode.CancellationError());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,22 +3,19 @@
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
import * as vscode from 'vscode';
|
||||
import { Position, RequestType } from 'vscode-languageclient';
|
||||
import { DefaultClient, openFileVersions } from '../client';
|
||||
import { processDelayedDidOpen } from '../extension';
|
||||
import { ManualPromise } from '../../Utility/Async/manualPromise';
|
||||
import { CppSettings } from '../settings';
|
||||
|
||||
interface GetInlayHintsParams {
|
||||
uri: string;
|
||||
interface FileData
|
||||
{
|
||||
version: number;
|
||||
promise: ManualPromise<vscode.InlayHint[]>;
|
||||
inlayHints: vscode.InlayHint[];
|
||||
}
|
||||
|
||||
enum InlayHintKind {
|
||||
Type = 0,
|
||||
Parameter = 1,
|
||||
}
|
||||
|
||||
interface CppInlayHint {
|
||||
position: Position;
|
||||
export interface CppInlayHint {
|
||||
line: number;
|
||||
character: number;
|
||||
label: string;
|
||||
inlayHintKind: InlayHintKind;
|
||||
isValueRef: boolean;
|
||||
@@ -28,81 +25,131 @@ interface CppInlayHint {
|
||||
identifierLength: number;
|
||||
}
|
||||
|
||||
interface GetInlayHintsResult {
|
||||
fileVersion: number;
|
||||
inlayHints: CppInlayHint[];
|
||||
enum InlayHintKind {
|
||||
Type = 0,
|
||||
Parameter = 1,
|
||||
}
|
||||
|
||||
type InlayHintsCacheEntry = {
|
||||
FileVersion: number;
|
||||
TypeHints: CppInlayHint[];
|
||||
ParameterHints: CppInlayHint[];
|
||||
};
|
||||
|
||||
const GetInlayHintsRequest: RequestType<GetInlayHintsParams, GetInlayHintsResult, void> =
|
||||
new RequestType<GetInlayHintsParams, GetInlayHintsResult, void>('cpptools/getInlayHints');
|
||||
|
||||
export class InlayHintsProvider implements vscode.InlayHintsProvider {
|
||||
private client: DefaultClient;
|
||||
public onDidChangeInlayHintsEvent = new vscode.EventEmitter<void>();
|
||||
public onDidChangeInlayHints?: vscode.Event<void>;
|
||||
private cache: Map<string, InlayHintsCacheEntry> = new Map<string, InlayHintsCacheEntry>();
|
||||
public onDidChangeInlayHints?: vscode.Event<void> = this.onDidChangeInlayHintsEvent.event;
|
||||
private allFileData: Map<string, FileData> = new Map<string, FileData>();
|
||||
|
||||
constructor(client: DefaultClient) {
|
||||
this.client = client;
|
||||
this.onDidChangeInlayHints = this.onDidChangeInlayHintsEvent.event;
|
||||
}
|
||||
|
||||
public async provideInlayHints(document: vscode.TextDocument, range: vscode.Range,
|
||||
token: vscode.CancellationToken): Promise<vscode.InlayHint[] | undefined> {
|
||||
await this.client.enqueue(() => processDelayedDidOpen(document));
|
||||
const uriString: string = document.uri.toString();
|
||||
|
||||
// Get results from cache if available.
|
||||
let cacheEntry: InlayHintsCacheEntry | undefined = this.cache.get(uriString);
|
||||
if (cacheEntry?.FileVersion === document.version) {
|
||||
return this.buildVSCodeHints(document.uri, cacheEntry);
|
||||
public async provideInlayHints(document: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise<vscode.InlayHint[]> {
|
||||
const uri: vscode.Uri = document.uri;
|
||||
const uriString: string = uri.toString();
|
||||
let fileData: FileData | undefined = this.allFileData.get(uriString);
|
||||
if (fileData) {
|
||||
if (fileData.promise.isCompleted) {
|
||||
// Make sure file hasn't been changed since the last set of results.
|
||||
// If a complete promise is present, there should also be a cache.
|
||||
if (fileData.version === document.version) {
|
||||
return fileData.promise;
|
||||
}
|
||||
} else {
|
||||
// A new request requires a new ManualPromise, as each promise returned needs
|
||||
// to be associated with the cancellation token provided at the time.
|
||||
fileData.promise.reject(new vscode.CancellationError());
|
||||
}
|
||||
}
|
||||
|
||||
// Get new results from the language server
|
||||
const params: GetInlayHintsParams = { uri: uriString };
|
||||
const inlayHintsResult: GetInlayHintsResult = await this.client.languageClient.sendRequest(GetInlayHintsRequest, params, token);
|
||||
if (token.isCancellationRequested || inlayHintsResult.inlayHints === undefined || inlayHintsResult.fileVersion !== openFileVersions.get(uriString)) {
|
||||
throw new vscode.CancellationError();
|
||||
fileData = {
|
||||
version: document.version,
|
||||
promise: new ManualPromise<vscode.InlayHint[]>(),
|
||||
inlayHints: []
|
||||
};
|
||||
this.allFileData.set(uriString, fileData);
|
||||
|
||||
// Capture a local variable instead of referring to the member variable directly,
|
||||
// to avoid race conditions where the member variable is changed before the
|
||||
// cancallation token is triggered.
|
||||
const currentPromise = fileData.promise;
|
||||
token.onCancellationRequested(() => {
|
||||
const fileData: FileData | undefined = this.allFileData.get(uriString);
|
||||
if (fileData && currentPromise === fileData.promise) {
|
||||
this.allFileData.delete(uriString);
|
||||
currentPromise.reject(new vscode.CancellationError());
|
||||
}
|
||||
});
|
||||
return currentPromise;
|
||||
}
|
||||
|
||||
public deliverInlayHints(uriString: string, cppInlayHints: CppInlayHint[], startNewSet: boolean): void {
|
||||
if (!startNewSet && cppInlayHints.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cacheEntry = this.createCacheEntry(inlayHintsResult);
|
||||
this.cache.set(uriString, cacheEntry);
|
||||
return this.buildVSCodeHints(document.uri, cacheEntry);
|
||||
const editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.find(e => e.document.uri.toString() === uriString);
|
||||
if (!editor) {
|
||||
this.allFileData.get(uriString)?.promise.resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
// Use a lambda to remove ambiguity about whether fileData may be undefined.
|
||||
const [fileData, wasNewPromiseCreated]: [FileData, boolean] = (() => {
|
||||
let fileData = this.allFileData.get(uriString);
|
||||
let newPromiseCreated = false;
|
||||
if (!fileData) {
|
||||
fileData = {
|
||||
version: editor.document.version,
|
||||
promise: new ManualPromise<vscode.InlayHint[]>(),
|
||||
inlayHints: []
|
||||
};
|
||||
newPromiseCreated = true;
|
||||
this.allFileData.set(uriString, fileData);
|
||||
} else {
|
||||
if (!fileData.promise.isPending) {
|
||||
fileData.promise.reject(new vscode.CancellationError());
|
||||
fileData.promise = new ManualPromise<vscode.InlayHint[]>();
|
||||
newPromiseCreated = true;
|
||||
}
|
||||
if (fileData.version !== editor.document.version) {
|
||||
fileData.version = editor.document.version;
|
||||
fileData.inlayHints = [];
|
||||
}
|
||||
}
|
||||
return [fileData, newPromiseCreated];
|
||||
})();
|
||||
if (startNewSet) {
|
||||
fileData.inlayHints = [];
|
||||
}
|
||||
|
||||
public invalidateFile(uri: string): void {
|
||||
this.cache.delete(uri);
|
||||
this.onDidChangeInlayHintsEvent.fire();
|
||||
}
|
||||
const typeHints: CppInlayHint[] = cppInlayHints.filter(h => h.inlayHintKind === InlayHintKind.Type);
|
||||
const paramHints: CppInlayHint[] = cppInlayHints.filter(h => h.inlayHintKind === InlayHintKind.Parameter);
|
||||
|
||||
private buildVSCodeHints(uri: vscode.Uri, cacheEntry: InlayHintsCacheEntry): vscode.InlayHint[] {
|
||||
let result: vscode.InlayHint[] = [];
|
||||
const settings: CppSettings = new CppSettings(uri);
|
||||
const settings: CppSettings = new CppSettings(vscode.Uri.parse(uriString));
|
||||
if (settings.inlayHintsAutoDeclarationTypes) {
|
||||
const resolvedTypeHints: vscode.InlayHint[] = this.resolveTypeHints(uri, cacheEntry.TypeHints);
|
||||
result = result.concat(resolvedTypeHints);
|
||||
const resolvedTypeHints: vscode.InlayHint[] = this.resolveTypeHints(settings, typeHints);
|
||||
Array.prototype.push.apply(fileData.inlayHints, resolvedTypeHints);
|
||||
}
|
||||
if (settings.inlayHintsParameterNames || settings.inlayHintsReferenceOperator) {
|
||||
const resolvedParameterHints: vscode.InlayHint[] = this.resolveParameterHints(uri, cacheEntry.ParameterHints);
|
||||
result = result.concat(resolvedParameterHints);
|
||||
const resolvedParameterHints: vscode.InlayHint[] = this.resolveParameterHints(settings, paramHints);
|
||||
Array.prototype.push.apply(fileData.inlayHints, resolvedParameterHints);
|
||||
}
|
||||
|
||||
fileData?.promise.resolve(fileData.inlayHints);
|
||||
if (wasNewPromiseCreated) {
|
||||
this.onDidChangeInlayHintsEvent.fire();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private resolveTypeHints(uri: vscode.Uri, hints: CppInlayHint[]): vscode.InlayHint[] {
|
||||
public removeFile(uriString: string): void {
|
||||
const fileData: FileData | undefined = this.allFileData.get(uriString);
|
||||
if (!fileData) {
|
||||
return;
|
||||
}
|
||||
if (fileData.promise.isPending) {
|
||||
fileData.promise.reject(new vscode.CancellationError());
|
||||
}
|
||||
this.allFileData.delete(uriString);
|
||||
}
|
||||
|
||||
private resolveTypeHints(settings: CppSettings, hints: CppInlayHint[]): vscode.InlayHint[] {
|
||||
const resolvedHints: vscode.InlayHint[] = [];
|
||||
const settings: CppSettings = new CppSettings(uri);
|
||||
for (const hint of hints) {
|
||||
const showOnLeft: boolean = settings.inlayHintsAutoDeclarationTypesShowOnLeft && hint.identifierLength > 0;
|
||||
const inlayHint: vscode.InlayHint = new vscode.InlayHint(
|
||||
new vscode.Position(hint.position.line, hint.position.character +
|
||||
new vscode.Position(hint.line, hint.character +
|
||||
(showOnLeft ? 0 : hint.identifierLength)),
|
||||
showOnLeft ? hint.label : ": " + hint.label,
|
||||
vscode.InlayHintKind.Type);
|
||||
@@ -113,9 +160,8 @@ export class InlayHintsProvider implements vscode.InlayHintsProvider {
|
||||
return resolvedHints;
|
||||
}
|
||||
|
||||
private resolveParameterHints(uri: vscode.Uri, hints: CppInlayHint[]): vscode.InlayHint[] {
|
||||
private resolveParameterHints(settings: CppSettings, hints: CppInlayHint[]): vscode.InlayHint[] {
|
||||
const resolvedHints: vscode.InlayHint[] = [];
|
||||
const settings: CppSettings = new CppSettings(uri);
|
||||
for (const hint of hints) {
|
||||
// Build parameter label based on settings.
|
||||
let paramHintLabel: string = "";
|
||||
@@ -144,7 +190,7 @@ export class InlayHintsProvider implements vscode.InlayHintsProvider {
|
||||
}
|
||||
|
||||
const inlayHint: vscode.InlayHint = new vscode.InlayHint(
|
||||
new vscode.Position(hint.position.line, hint.position.character),
|
||||
new vscode.Position(hint.line, hint.character),
|
||||
refOperatorString + paramHintLabel + ":",
|
||||
vscode.InlayHintKind.Parameter);
|
||||
inlayHint.paddingRight = true;
|
||||
@@ -152,15 +198,4 @@ export class InlayHintsProvider implements vscode.InlayHintsProvider {
|
||||
}
|
||||
return resolvedHints;
|
||||
}
|
||||
|
||||
private createCacheEntry(inlayHintsResults: GetInlayHintsResult): InlayHintsCacheEntry {
|
||||
const typeHints: CppInlayHint[] = inlayHintsResults.inlayHints.filter(h => h.inlayHintKind === InlayHintKind.Type);
|
||||
const paramHints: CppInlayHint[] = inlayHintsResults.inlayHints.filter(h => h.inlayHintKind === InlayHintKind.Parameter);
|
||||
const cacheEntry: InlayHintsCacheEntry = {
|
||||
FileVersion: inlayHintsResults.fileVersion,
|
||||
TypeHints: typeHints,
|
||||
ParameterHints: paramHints
|
||||
};
|
||||
return cacheEntry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,55 +3,127 @@
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
import * as vscode from 'vscode';
|
||||
import { DefaultClient, GetSemanticTokensParams, GetSemanticTokensRequest, GetSemanticTokensResult, openFileVersions, semanticTokensLegend } from '../client';
|
||||
import { processDelayedDidOpen } from '../extension';
|
||||
import { ManualPromise } from '../../Utility/Async/manualPromise';
|
||||
|
||||
interface FileData
|
||||
{
|
||||
version: number;
|
||||
promise: ManualPromise<vscode.SemanticTokens>;
|
||||
tokenBuilder: vscode.SemanticTokensBuilder;
|
||||
}
|
||||
|
||||
export interface SemanticToken {
|
||||
line: number;
|
||||
character: number;
|
||||
length: number;
|
||||
type: number;
|
||||
modifiers?: number;
|
||||
}
|
||||
|
||||
export class SemanticTokensProvider implements vscode.DocumentSemanticTokensProvider {
|
||||
private client: DefaultClient;
|
||||
public onDidChangeSemanticTokensEvent = new vscode.EventEmitter<void>();
|
||||
public onDidChangeSemanticTokens?: vscode.Event<void>;
|
||||
private tokenCaches: Map<string, [number, vscode.SemanticTokens]> = new Map<string, [number, vscode.SemanticTokens]>();
|
||||
|
||||
constructor(client: DefaultClient) {
|
||||
this.client = client;
|
||||
this.onDidChangeSemanticTokens = this.onDidChangeSemanticTokensEvent.event;
|
||||
}
|
||||
public onDidChangeSemanticTokens?: vscode.Event<void> = this.onDidChangeSemanticTokensEvent.event;
|
||||
private allFileData: Map<string, FileData> = new Map<string, FileData>();
|
||||
|
||||
public async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.SemanticTokens> {
|
||||
const editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.find(e => e.document === document);
|
||||
const uri: vscode.Uri = document.uri;
|
||||
const uriString: string = uri.toString();
|
||||
let fileData: FileData | undefined = this.allFileData.get(uriString);
|
||||
if (fileData) {
|
||||
if (fileData.promise.isCompleted) {
|
||||
// Make sure file hasn't been changed since the last set of results.
|
||||
// If a complete promise is present, there should also be a cache.
|
||||
if (fileData.version === document.version) {
|
||||
return fileData.promise;
|
||||
}
|
||||
} else {
|
||||
// A new request requires a new ManualPromise, as each promise returned needs
|
||||
// to be associated with the cancellation token provided at the time.
|
||||
fileData.promise.reject(new vscode.CancellationError());
|
||||
}
|
||||
}
|
||||
fileData = {
|
||||
version: document.version,
|
||||
promise: new ManualPromise<vscode.SemanticTokens>(),
|
||||
tokenBuilder: new vscode.SemanticTokensBuilder()
|
||||
};
|
||||
this.allFileData.set(uriString, fileData);
|
||||
|
||||
// Capture a local variable instead of referring to the member variable directly,
|
||||
// to avoid race conditions where the member variable is changed before the
|
||||
// cancallation token is triggered.
|
||||
const currentPromise = fileData.promise;
|
||||
token.onCancellationRequested(() => {
|
||||
const fileData: FileData | undefined = this.allFileData.get(uriString);
|
||||
if (fileData && currentPromise === fileData.promise) {
|
||||
this.allFileData.delete(uriString);
|
||||
currentPromise.reject(new vscode.CancellationError());
|
||||
}
|
||||
});
|
||||
|
||||
return currentPromise;
|
||||
}
|
||||
|
||||
public deliverTokens(uriString: string, semanticTokens: SemanticToken[], startNewSet: boolean): void {
|
||||
if (!startNewSet && semanticTokens.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.find(e => e.document.uri.toString() === uriString);
|
||||
if (!editor) {
|
||||
// Don't provide document semantic tokens for files that aren't visible,
|
||||
// which prevents launching a lot of IntelliSense processes from a find/replace.
|
||||
const builder: vscode.SemanticTokensBuilder = new vscode.SemanticTokensBuilder();
|
||||
const tokens: vscode.SemanticTokens = builder.build();
|
||||
return tokens;
|
||||
this.allFileData.get(uriString)?.promise.resolve(tokens);
|
||||
return;
|
||||
}
|
||||
await this.client.enqueue(() => processDelayedDidOpen(document));
|
||||
|
||||
const uriString: string = document.uri.toString();
|
||||
// First check the semantic token cache to see if we already have results for that file and version
|
||||
const cache: [number, vscode.SemanticTokens] | undefined = this.tokenCaches.get(uriString);
|
||||
if (cache && cache[0] === document.version) {
|
||||
return cache[1];
|
||||
// Use a lambda to remove ambiguity about whether fileData may be undefined.
|
||||
const [fileData, wasNewPromiseCreated]: [FileData, boolean] = (() => {
|
||||
let fileData = this.allFileData.get(uriString);
|
||||
let newPromiseCreated = false;
|
||||
if (!fileData) {
|
||||
fileData = {
|
||||
version: editor.document.version,
|
||||
promise: new ManualPromise<vscode.SemanticTokens>(),
|
||||
tokenBuilder: new vscode.SemanticTokensBuilder()
|
||||
};
|
||||
newPromiseCreated = true;
|
||||
this.allFileData.set(uriString, fileData);
|
||||
} else {
|
||||
if (!fileData.promise.isPending) {
|
||||
fileData.promise.reject(new vscode.CancellationError());
|
||||
fileData.promise = new ManualPromise<vscode.SemanticTokens>();
|
||||
newPromiseCreated = true;
|
||||
}
|
||||
if (fileData.version !== editor.document.version) {
|
||||
fileData.version = editor.document.version;
|
||||
fileData.tokenBuilder = new vscode.SemanticTokensBuilder();
|
||||
}
|
||||
}
|
||||
return [fileData, newPromiseCreated];
|
||||
})();
|
||||
if (startNewSet) {
|
||||
fileData.tokenBuilder = new vscode.SemanticTokensBuilder();
|
||||
}
|
||||
const params: GetSemanticTokensParams = {
|
||||
uri: uriString
|
||||
};
|
||||
const tokensResult: GetSemanticTokensResult = await this.client.languageClient.sendRequest(GetSemanticTokensRequest, params, token);
|
||||
if (token.isCancellationRequested || tokensResult.tokens === undefined || tokensResult.fileVersion !== openFileVersions.get(uriString)) {
|
||||
throw new vscode.CancellationError();
|
||||
}
|
||||
const builder: vscode.SemanticTokensBuilder = new vscode.SemanticTokensBuilder(semanticTokensLegend);
|
||||
tokensResult.tokens.forEach((semanticToken) => {
|
||||
builder.push(semanticToken.line, semanticToken.character, semanticToken.length, semanticToken.type, semanticToken.modifiers);
|
||||
|
||||
semanticTokens.forEach((semanticToken) => {
|
||||
fileData.tokenBuilder.push(semanticToken.line, semanticToken.character, semanticToken.length, semanticToken.type, semanticToken.modifiers);
|
||||
});
|
||||
const tokens: vscode.SemanticTokens = builder.build();
|
||||
this.tokenCaches.set(uriString, [tokensResult.fileVersion, tokens]);
|
||||
return tokens;
|
||||
|
||||
fileData?.promise.resolve(fileData.tokenBuilder.build());
|
||||
if (wasNewPromiseCreated) {
|
||||
this.onDidChangeSemanticTokensEvent.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public invalidateFile(uri: string): void {
|
||||
this.tokenCaches.delete(uri);
|
||||
this.onDidChangeSemanticTokensEvent.fire();
|
||||
public removeFile(uriString: string): void {
|
||||
const fileData: FileData | undefined = this.allFileData.get(uriString);
|
||||
if (!fileData) {
|
||||
return;
|
||||
}
|
||||
if (fileData.promise.isPending) {
|
||||
fileData.promise.reject(new vscode.CancellationError());
|
||||
}
|
||||
this.allFileData.delete(uriString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ import { DocumentRangeFormattingEditProvider } from './Providers/documentRangeFo
|
||||
import { DocumentSymbolProvider } from './Providers/documentSymbolProvider';
|
||||
import { FindAllReferencesProvider } from './Providers/findAllReferencesProvider';
|
||||
import { FoldingRangeProvider } from './Providers/foldingRangeProvider';
|
||||
import { InlayHintsProvider } from './Providers/inlayHintProvider';
|
||||
import { CppInlayHint, InlayHintsProvider } from './Providers/inlayHintProvider';
|
||||
import { OnTypeFormattingEditProvider } from './Providers/onTypeFormattingEditProvider';
|
||||
import { RenameProvider } from './Providers/renameProvider';
|
||||
import { SemanticTokensProvider } from './Providers/semanticTokensProvider';
|
||||
import { SemanticToken, SemanticTokensProvider } from './Providers/semanticTokensProvider';
|
||||
import { WorkspaceSymbolProvider } from './Providers/workspaceSymbolProvider';
|
||||
// End provider imports
|
||||
|
||||
@@ -53,7 +53,7 @@ import {
|
||||
import { Location, TextEdit, WorkspaceEdit } from './commonTypes';
|
||||
import * as configs from './configurations';
|
||||
import { DataBinding } from './dataBinding';
|
||||
import { CppSourceStr, clients, configPrefix, updateLanguageConfigurations } from './extension';
|
||||
import { CppSourceStr, clients, configPrefix, updateLanguageConfigurations, watchForCrashes } from './extension';
|
||||
import { LocalizeStringParams, getLocaleId, getLocalizedString } from './localization';
|
||||
import { PersistentFolderState, PersistentWorkspaceState } from './persistentState';
|
||||
import { createProtocolFilter } from './protocolFilter';
|
||||
@@ -61,7 +61,7 @@ import * as refs from './references';
|
||||
import { CppSettings, OtherSettings, SettingsParams, WorkspaceFolderSettingsParams, getEditorConfigSettings } from './settings';
|
||||
import { SettingsTracker } from './settingsTracker';
|
||||
import { ConfigurationType, LanguageStatusUI, getUI } from './ui';
|
||||
import { handleChangedFromCppToC, makeVscodeLocation, makeVscodeRange } from './utils';
|
||||
import { handleChangedFromCppToC, makeLspRange, makeVscodeLocation, makeVscodeRange } from './utils';
|
||||
import minimatch = require("minimatch");
|
||||
|
||||
function deepCopy(obj: any) {
|
||||
@@ -137,34 +137,6 @@ function showMessageWindow(params: ShowMessageWindowParams): void {
|
||||
}
|
||||
}
|
||||
|
||||
function publishIntelliSenseDiagnostics(params: PublishIntelliSenseDiagnosticsParams): void {
|
||||
if (!diagnosticsCollectionIntelliSense) {
|
||||
diagnosticsCollectionIntelliSense = vscode.languages.createDiagnosticCollection(configPrefix + "IntelliSense");
|
||||
}
|
||||
|
||||
// Convert from our Diagnostic objects to vscode Diagnostic objects
|
||||
const diagnosticsIntelliSense: vscode.Diagnostic[] = [];
|
||||
params.diagnostics.forEach((d) => {
|
||||
const message: string = getLocalizedString(d.localizeStringParams);
|
||||
const diagnostic: vscode.Diagnostic = new vscode.Diagnostic(makeVscodeRange(d.range), message, d.severity);
|
||||
diagnostic.code = d.code;
|
||||
diagnostic.source = CppSourceStr;
|
||||
if (d.relatedInformation) {
|
||||
diagnostic.relatedInformation = [];
|
||||
for (const info of d.relatedInformation) {
|
||||
diagnostic.relatedInformation.push(new vscode.DiagnosticRelatedInformation(makeVscodeLocation(info.location), info.message));
|
||||
}
|
||||
}
|
||||
|
||||
diagnosticsIntelliSense.push(diagnostic);
|
||||
});
|
||||
|
||||
const realUri: vscode.Uri = vscode.Uri.parse(params.uri);
|
||||
diagnosticsCollectionIntelliSense.set(realUri, diagnosticsIntelliSense);
|
||||
|
||||
clients.timeTelemetryCollector.setUpdateRangeTime(realUri);
|
||||
}
|
||||
|
||||
function publishRefactorDiagnostics(params: PublishRefactorDiagnosticsParams): void {
|
||||
if (!diagnosticsCollectionRefactor) {
|
||||
diagnosticsCollectionRefactor = vscode.languages.createDiagnosticCollection(configPrefix + "Refactor");
|
||||
@@ -251,12 +223,6 @@ interface DecorationRangesPair {
|
||||
ranges: vscode.Range[];
|
||||
}
|
||||
|
||||
interface InactiveRegionParams {
|
||||
uri: string;
|
||||
fileVersion: number;
|
||||
regions: InputRegion[];
|
||||
}
|
||||
|
||||
interface InternalSourceFileConfiguration extends SourceFileConfiguration {
|
||||
compilerArgsLegacy?: string[];
|
||||
}
|
||||
@@ -322,11 +288,6 @@ interface RefactorDiagnostic {
|
||||
relatedInformation?: RefactorDiagnosticRelatedInformation[];
|
||||
}
|
||||
|
||||
interface PublishIntelliSenseDiagnosticsParams {
|
||||
uri: string;
|
||||
diagnostics: IntelliSenseDiagnostic[];
|
||||
}
|
||||
|
||||
interface PublishRefactorDiagnosticsParams {
|
||||
uri: string;
|
||||
diagnostics: RefactorDiagnostic[];
|
||||
@@ -420,21 +381,18 @@ export interface GetFoldingRangesResult {
|
||||
ranges: CppFoldingRange[];
|
||||
}
|
||||
|
||||
export interface GetSemanticTokensParams {
|
||||
export interface IntelliSenseResult {
|
||||
uri: string;
|
||||
}
|
||||
|
||||
interface SemanticToken {
|
||||
line: number;
|
||||
character: number;
|
||||
length: number;
|
||||
type: number;
|
||||
modifiers?: number;
|
||||
}
|
||||
|
||||
export interface GetSemanticTokensResult {
|
||||
fileVersion: number;
|
||||
tokens: SemanticToken[];
|
||||
diagnostics: IntelliSenseDiagnostic[];
|
||||
inactiveRegions: InputRegion[];
|
||||
semanticTokens: SemanticToken[];
|
||||
inlayHints: CppInlayHint[];
|
||||
clearExistingDiagnostics: boolean;
|
||||
clearExistingInactiveRegions: boolean;
|
||||
clearExistingSemanticTokens: boolean;
|
||||
clearExistingInlayHint: boolean;
|
||||
isCompletePass: boolean;
|
||||
}
|
||||
|
||||
enum SemanticTokenTypes {
|
||||
@@ -512,7 +470,7 @@ export interface DoxygenCodeActionCommandArguments {
|
||||
}
|
||||
|
||||
interface SetTemporaryTextDocumentLanguageParams {
|
||||
path: string;
|
||||
uri: string;
|
||||
isC: boolean;
|
||||
isCuda: boolean;
|
||||
}
|
||||
@@ -532,10 +490,6 @@ interface FinishedRequestCustomConfigParams {
|
||||
uri: string;
|
||||
}
|
||||
|
||||
interface IntervalTimerParams {
|
||||
freeMemory: number;
|
||||
}
|
||||
|
||||
export interface TextDocumentWillSaveParams {
|
||||
textDocument: TextDocumentIdentifier;
|
||||
reason: vscode.TextDocumentSaveReason;
|
||||
@@ -551,7 +505,6 @@ interface CppInitializationParams {
|
||||
cacheStoragePath: string;
|
||||
workspaceStoragePath: string;
|
||||
databaseStoragePath: string;
|
||||
freeMemory: number;
|
||||
vcpkgRoot: string;
|
||||
intelliSenseCacheDisabled: boolean;
|
||||
caseSensitiveFileSupport: boolean;
|
||||
@@ -566,8 +519,24 @@ interface TagParseStatus {
|
||||
isPaused: boolean;
|
||||
}
|
||||
|
||||
interface DidChangeVisibleTextEditorsParams {
|
||||
activeUri?: string;
|
||||
activeSelection?: Range;
|
||||
visibleRanges?: { [uri: string]: Range[] };
|
||||
}
|
||||
|
||||
interface DidChangeTextEditorVisibleRangesParams {
|
||||
uri: string;
|
||||
visibleRanges: Range[];
|
||||
}
|
||||
|
||||
interface DidChangeActiveEditorParams {
|
||||
uri?: string;
|
||||
selection?: Range;
|
||||
}
|
||||
|
||||
// Requests
|
||||
const InitializationRequest: RequestType<CppInitializationParams, void, void> = new RequestType<CppInitializationParams, void, void>('cpptools/initialize');
|
||||
const InitializationRequest: RequestType<CppInitializationParams, string, void> = new RequestType<CppInitializationParams, string, void>('cpptools/initialize');
|
||||
const QueryCompilerDefaultsRequest: RequestType<QueryDefaultCompilerParams, configs.CompilerDefaults, void> = new RequestType<QueryDefaultCompilerParams, configs.CompilerDefaults, void>('cpptools/queryCompilerDefaults');
|
||||
const QueryTranslationUnitSourceRequest: RequestType<QueryTranslationUnitSourceParams, QueryTranslationUnitSourceResult, void> = new RequestType<QueryTranslationUnitSourceParams, QueryTranslationUnitSourceResult, void>('cpptools/queryTranslationUnitSource');
|
||||
const SwitchHeaderSourceRequest: RequestType<SwitchHeaderSourceParams, string, void> = new RequestType<SwitchHeaderSourceParams, string, void>('cpptools/didSwitchHeaderSource');
|
||||
@@ -575,7 +544,6 @@ const GetDiagnosticsRequest: RequestType<void, GetDiagnosticsResult, void> = new
|
||||
export const GetDocumentSymbolRequest: RequestType<GetDocumentSymbolRequestParams, GetDocumentSymbolResult, void> = new RequestType<GetDocumentSymbolRequestParams, GetDocumentSymbolResult, void>('cpptools/getDocumentSymbols');
|
||||
export const GetSymbolInfoRequest: RequestType<WorkspaceSymbolParams, LocalizeSymbolInformation[], void> = new RequestType<WorkspaceSymbolParams, LocalizeSymbolInformation[], void>('cpptools/getWorkspaceSymbols');
|
||||
export const GetFoldingRangesRequest: RequestType<GetFoldingRangesParams, GetFoldingRangesResult, void> = new RequestType<GetFoldingRangesParams, GetFoldingRangesResult, void>('cpptools/getFoldingRanges');
|
||||
export const GetSemanticTokensRequest: RequestType<GetSemanticTokensParams, GetSemanticTokensResult, void> = new RequestType<GetSemanticTokensParams, GetSemanticTokensResult, void>('cpptools/getSemanticTokens');
|
||||
export const FormatDocumentRequest: RequestType<FormatParams, FormatResult, void> = new RequestType<FormatParams, FormatResult, void>('cpptools/formatDocument');
|
||||
export const FormatRangeRequest: RequestType<FormatParams, FormatResult, void> = new RequestType<FormatParams, FormatResult, void>('cpptools/formatRange');
|
||||
export const FormatOnTypeRequest: RequestType<FormatParams, FormatResult, void> = new RequestType<FormatParams, FormatResult, void>('cpptools/formatOnType');
|
||||
@@ -584,6 +552,7 @@ const ExtractToFunctionRequest: RequestType<ExtractToFunctionParams, WorkspaceEd
|
||||
const GoToDirectiveInGroupRequest: RequestType<GoToDirectiveInGroupParams, Position | undefined, void> = new RequestType<GoToDirectiveInGroupParams, Position | undefined, void>('cpptools/goToDirectiveInGroup');
|
||||
const GenerateDoxygenCommentRequest: RequestType<GenerateDoxygenCommentParams, GenerateDoxygenCommentResult | undefined, void> = new RequestType<GenerateDoxygenCommentParams, GenerateDoxygenCommentResult, void>('cpptools/generateDoxygenComment');
|
||||
const ChangeCppPropertiesRequest: RequestType<CppPropertiesParams, void, void> = new RequestType<CppPropertiesParams, void, void>('cpptools/didChangeCppProperties');
|
||||
const DirectlyReferencedHeadersRequest: RequestType<void, string[], void> = new RequestType<void, string[], void>('cpptools/directlyReferencedHeaders');
|
||||
|
||||
// Notifications to the server
|
||||
const DidOpenNotification: NotificationType<DidOpenTextDocumentParams> = new NotificationType<DidOpenTextDocumentParams>('textDocument/didOpen');
|
||||
@@ -593,12 +562,12 @@ const FileDeletedNotification: NotificationType<FileChangedParams> = new Notific
|
||||
const ResetDatabaseNotification: NotificationType<void> = new NotificationType<void>('cpptools/resetDatabase');
|
||||
const PauseParsingNotification: NotificationType<void> = new NotificationType<void>('cpptools/pauseParsing');
|
||||
const ResumeParsingNotification: NotificationType<void> = new NotificationType<void>('cpptools/resumeParsing');
|
||||
const ActiveDocumentChangeNotification: NotificationType<TextDocumentIdentifier> = new NotificationType<TextDocumentIdentifier>('cpptools/activeDocumentChange');
|
||||
const DidChangeActiveEditorNotification: NotificationType<DidChangeActiveEditorParams> = new NotificationType<DidChangeActiveEditorParams>('cpptools/didChangeActiveEditor');
|
||||
const RestartIntelliSenseForFileNotification: NotificationType<TextDocumentIdentifier> = new NotificationType<TextDocumentIdentifier>('cpptools/restartIntelliSenseForFile');
|
||||
const TextEditorSelectionChangeNotification: NotificationType<Range> = new NotificationType<Range>('cpptools/textEditorSelectionChange');
|
||||
const DidChangeTextEditorSelectionNotification: NotificationType<Range> = new NotificationType<Range>('cpptools/didChangeTextEditorSelection');
|
||||
const ChangeCompileCommandsNotification: NotificationType<FileChangedParams> = new NotificationType<FileChangedParams>('cpptools/didChangeCompileCommands');
|
||||
const ChangeSelectedSettingNotification: NotificationType<FolderSelectedSettingParams> = new NotificationType<FolderSelectedSettingParams>('cpptools/didChangeSelectedSetting');
|
||||
const IntervalTimerNotification: NotificationType<IntervalTimerParams> = new NotificationType<IntervalTimerParams>('cpptools/onIntervalTimer');
|
||||
const IntervalTimerNotification: NotificationType<void> = new NotificationType<void>('cpptools/onIntervalTimer');
|
||||
const CustomConfigurationNotification: NotificationType<CustomConfigurationParams> = new NotificationType<CustomConfigurationParams>('cpptools/didChangeCustomConfiguration');
|
||||
const CustomBrowseConfigurationNotification: NotificationType<CustomBrowseConfigurationParams> = new NotificationType<CustomBrowseConfigurationParams>('cpptools/didChangeCustomBrowseConfiguration');
|
||||
const ClearCustomConfigurationsNotification: NotificationType<WorkspaceFolderParams> = new NotificationType<WorkspaceFolderParams>('cpptools/clearCustomConfigurations');
|
||||
@@ -607,6 +576,8 @@ const PreviewReferencesNotification: NotificationType<void> = new NotificationTy
|
||||
const RescanFolderNotification: NotificationType<void> = new NotificationType<void>('cpptools/rescanFolder');
|
||||
const FinishedRequestCustomConfig: NotificationType<FinishedRequestCustomConfigParams> = new NotificationType<FinishedRequestCustomConfigParams>('cpptools/finishedRequestCustomConfig');
|
||||
const DidChangeSettingsNotification: NotificationType<SettingsParams> = new NotificationType<SettingsParams>('cpptools/didChangeSettings');
|
||||
const DidChangeVisibleTextEditorsNotification: NotificationType<DidChangeVisibleTextEditorsParams> = new NotificationType<DidChangeVisibleTextEditorsParams>('cpptools/didChangeVisibleTextEditors');
|
||||
const DidChangeTextEditorVisibleRangesNotification: NotificationType<DidChangeTextEditorVisibleRangesParams> = new NotificationType<DidChangeTextEditorVisibleRangesParams>('cpptools/didChangeTextEditorVisibleRanges');
|
||||
|
||||
const CodeAnalysisNotification: NotificationType<CodeAnalysisParams> = new NotificationType<CodeAnalysisParams>('cpptools/runCodeAnalysis');
|
||||
const PauseCodeAnalysisNotification: NotificationType<void> = new NotificationType<void>('cpptools/pauseCodeAnalysis');
|
||||
@@ -622,24 +593,21 @@ const ReportTagParseStatusNotification: NotificationType<TagParseStatus> = new N
|
||||
const ReportStatusNotification: NotificationType<ReportStatusNotificationBody> = new NotificationType<ReportStatusNotificationBody>('cpptools/reportStatus');
|
||||
const DebugProtocolNotification: NotificationType<DebugProtocolParams> = new NotificationType<DebugProtocolParams>('cpptools/debugProtocol');
|
||||
const DebugLogNotification: NotificationType<LocalizeStringParams> = new NotificationType<LocalizeStringParams>('cpptools/debugLog');
|
||||
const InactiveRegionNotification: NotificationType<InactiveRegionParams> = new NotificationType<InactiveRegionParams>('cpptools/inactiveRegions');
|
||||
const CompileCommandsPathsNotification: NotificationType<CompileCommandsPaths> = new NotificationType<CompileCommandsPaths>('cpptools/compileCommandsPaths');
|
||||
const ReferencesNotification: NotificationType<refs.ReferencesResult> = new NotificationType<refs.ReferencesResult>('cpptools/references');
|
||||
const ReportReferencesProgressNotification: NotificationType<refs.ReportReferencesProgressNotification> = new NotificationType<refs.ReportReferencesProgressNotification>('cpptools/reportReferencesProgress');
|
||||
const RequestCustomConfig: NotificationType<string> = new NotificationType<string>('cpptools/requestCustomConfig');
|
||||
const PublishIntelliSenseDiagnosticsNotification: NotificationType<PublishIntelliSenseDiagnosticsParams> = new NotificationType<PublishIntelliSenseDiagnosticsParams>('cpptools/publishIntelliSenseDiagnostics');
|
||||
const PublishRefactorDiagnosticsNotification: NotificationType<PublishRefactorDiagnosticsParams> = new NotificationType<PublishRefactorDiagnosticsParams>('cpptools/publishRefactorDiagnostics');
|
||||
const ShowMessageWindowNotification: NotificationType<ShowMessageWindowParams> = new NotificationType<ShowMessageWindowParams>('cpptools/showMessageWindow');
|
||||
const ShowWarningNotification: NotificationType<ShowWarningParams> = new NotificationType<ShowWarningParams>('cpptools/showWarning');
|
||||
const ReportTextDocumentLanguage: NotificationType<string> = new NotificationType<string>('cpptools/reportTextDocumentLanguage');
|
||||
const SemanticTokensChanged: NotificationType<string> = new NotificationType<string>('cpptools/semanticTokensChanged');
|
||||
const InlayHintsChanged: NotificationType<string> = new NotificationType<string>('cpptools/inlayHintsChanged');
|
||||
const IntelliSenseSetupNotification: NotificationType<IntelliSenseSetup> = new NotificationType<IntelliSenseSetup>('cpptools/IntelliSenseSetup');
|
||||
const SetTemporaryTextDocumentLanguageNotification: NotificationType<SetTemporaryTextDocumentLanguageParams> = new NotificationType<SetTemporaryTextDocumentLanguageParams>('cpptools/setTemporaryTextDocumentLanguage');
|
||||
const ReportCodeAnalysisProcessedNotification: NotificationType<number> = new NotificationType<number>('cpptools/reportCodeAnalysisProcessed');
|
||||
const ReportCodeAnalysisTotalNotification: NotificationType<number> = new NotificationType<number>('cpptools/reportCodeAnalysisTotal');
|
||||
const DoxygenCommentGeneratedNotification: NotificationType<GenerateDoxygenCommentResult> = new NotificationType<GenerateDoxygenCommentResult>('cpptools/insertDoxygenComment');
|
||||
const CanceledReferencesNotification: NotificationType<void> = new NotificationType<void>('cpptools/canceledReferences');
|
||||
const IntelliSenseResultNotification: NotificationType<IntelliSenseResult> = new NotificationType<IntelliSenseResult>('cpptools/intelliSenseResult');
|
||||
|
||||
let failureMessageShown: boolean = false;
|
||||
|
||||
@@ -744,11 +712,12 @@ export interface Client {
|
||||
RootUri?: vscode.Uri;
|
||||
RootFolder?: vscode.WorkspaceFolder;
|
||||
Name: string;
|
||||
TrackedDocuments: Set<vscode.TextDocument>;
|
||||
TrackedDocuments: Map<string, vscode.TextDocument>;
|
||||
onDidChangeSettings(event: vscode.ConfigurationChangeEvent): Promise<Record<string, string>>;
|
||||
onDidOpenTextDocument(document: vscode.TextDocument): void;
|
||||
onDidCloseTextDocument(document: vscode.TextDocument): void;
|
||||
onDidChangeVisibleTextEditor(editor: vscode.TextEditor): void;
|
||||
onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]): Promise<void>;
|
||||
onDidChangeTextEditorVisibleRanges(uri: vscode.Uri): Promise<void>;
|
||||
onDidChangeTextDocument(textDocumentChangeEvent: vscode.TextDocumentChangeEvent): void;
|
||||
onRegisterCustomConfigurationProvider(provider: CustomConfigurationProvider1): Thenable<void>;
|
||||
updateCustomConfigurations(requestingProvider?: CustomConfigurationProvider1): Thenable<void>;
|
||||
@@ -764,10 +733,11 @@ export interface Client {
|
||||
getVcpkgEnabled(): Thenable<boolean>;
|
||||
getCurrentCompilerPathAndArgs(): Thenable<util.CompilerPathAndArgs | undefined>;
|
||||
getKnownCompilers(): Thenable<configs.KnownCompiler[] | undefined>;
|
||||
takeOwnership(document: vscode.TextDocument): Promise<void>;
|
||||
takeOwnership(document: vscode.TextDocument): void;
|
||||
sendDidOpen(document: vscode.TextDocument): Promise<void>;
|
||||
requestSwitchHeaderSource(rootUri: vscode.Uri, fileName: string): Thenable<string>;
|
||||
activeDocumentChanged(document: vscode.TextDocument): Promise<void>;
|
||||
updateActiveDocumentTextOptions(): void;
|
||||
didChangeActiveEditor(editor?: vscode.TextEditor, selection?: Range): Promise<void>;
|
||||
restartIntelliSenseForFile(document: vscode.TextDocument): Promise<void>;
|
||||
activate(): void;
|
||||
selectionChanged(selection: Range): void;
|
||||
@@ -808,6 +778,7 @@ export interface Client {
|
||||
getShowConfigureIntelliSenseButton(): boolean;
|
||||
setShowConfigureIntelliSenseButton(show: boolean): void;
|
||||
addTrustedCompiler(path: string): Promise<void>;
|
||||
getDirectlyReferencedHeaders(): Promise<string[]>;
|
||||
}
|
||||
|
||||
export function createClient(workspaceFolder?: vscode.WorkspaceFolder): Client {
|
||||
@@ -834,7 +805,7 @@ export class DefaultClient implements Client {
|
||||
private rootFolder?: vscode.WorkspaceFolder;
|
||||
private rootRealPath: string;
|
||||
private workspaceStoragePath: string;
|
||||
private trackedDocuments = new Set<vscode.TextDocument>();
|
||||
private trackedDocuments = new Map<string, vscode.TextDocument>();
|
||||
private isSupported: boolean = true;
|
||||
private inactiveRegionsDecorations = new Map<string, DecorationRangesPair>();
|
||||
private settingsTracker: SettingsTracker;
|
||||
@@ -901,7 +872,7 @@ export class DefaultClient implements Client {
|
||||
public get Name(): string {
|
||||
return this.getName(this.rootFolder);
|
||||
}
|
||||
public get TrackedDocuments(): Set<vscode.TextDocument> {
|
||||
public get TrackedDocuments(): Map<string, vscode.TextDocument> {
|
||||
return this.trackedDocuments;
|
||||
}
|
||||
public get IsTagParsing(): boolean {
|
||||
@@ -1262,7 +1233,7 @@ export class DefaultClient implements Client {
|
||||
// e.g. prevents empty c_cpp_properties.json from generation.
|
||||
this.registerFileWatcher();
|
||||
initializedClientCount = 0;
|
||||
this.inlayHintsProvider = new InlayHintsProvider(this);
|
||||
this.inlayHintsProvider = new InlayHintsProvider();
|
||||
|
||||
this.disposables.push(vscode.languages.registerInlayHintsProvider(util.documentSelector, this.inlayHintsProvider));
|
||||
this.disposables.push(vscode.languages.registerRenameProvider(util.documentSelector, new RenameProvider(this)));
|
||||
@@ -1283,12 +1254,14 @@ export class DefaultClient implements Client {
|
||||
|
||||
const settings: CppSettings = new CppSettings();
|
||||
if (settings.enhancedColorization && semanticTokensLegend) {
|
||||
this.semanticTokensProvider = new SemanticTokensProvider(this);
|
||||
this.semanticTokensProvider = new SemanticTokensProvider();
|
||||
this.semanticTokensProviderDisposable = vscode.languages.registerDocumentSemanticTokensProvider(util.documentSelector, this.semanticTokensProvider, semanticTokensLegend);
|
||||
}
|
||||
|
||||
// Listen for messages from the language server.
|
||||
this.registerNotifications();
|
||||
}
|
||||
|
||||
// update all client configurations
|
||||
this.configuration.setupConfigurations();
|
||||
initializedClientCount++;
|
||||
@@ -1539,7 +1512,6 @@ export class DefaultClient implements Client {
|
||||
databaseStoragePath: databaseStoragePath,
|
||||
workspaceStoragePath: this.workspaceStoragePath,
|
||||
cacheStoragePath: cacheStoragePath,
|
||||
freeMemory: Math.floor(os.freemem() / 1048576),
|
||||
vcpkgRoot: util.getVcpkgRoot(),
|
||||
intelliSenseCacheDisabled: intelliSenseCacheDisabled,
|
||||
caseSensitiveFileSupport: workspaceSettings.caseSensitiveFileSupport,
|
||||
@@ -1602,7 +1574,7 @@ export class DefaultClient implements Client {
|
||||
// Move initialization to a separate message, so we can see log output from it.
|
||||
// A request is used in order to wait for completion and ensure that no subsequent
|
||||
// higher priority message may be processed before the Initialization request.
|
||||
await languageClient.sendRequest(InitializationRequest, cppInitializationParams);
|
||||
watchForCrashes(await languageClient.sendRequest(InitializationRequest, cppInitializationParams));
|
||||
}
|
||||
|
||||
public async sendDidChangeSettings(): Promise<void> {
|
||||
@@ -1639,7 +1611,7 @@ export class DefaultClient implements Client {
|
||||
const settings: CppSettings = new CppSettings();
|
||||
if (changedSettings.enhancedColorization) {
|
||||
if (settings.enhancedColorization && semanticTokensLegend) {
|
||||
this.semanticTokensProvider = new SemanticTokensProvider(this);
|
||||
this.semanticTokensProvider = new SemanticTokensProvider();
|
||||
this.semanticTokensProviderDisposable = vscode.languages.registerDocumentSemanticTokensProvider(util.documentSelector, this.semanticTokensProvider, semanticTokensLegend);
|
||||
} else if (this.semanticTokensProviderDisposable) {
|
||||
this.semanticTokensProviderDisposable.dispose();
|
||||
@@ -1670,15 +1642,65 @@ export class DefaultClient implements Client {
|
||||
return changedSettings;
|
||||
}
|
||||
|
||||
public onDidChangeVisibleTextEditor(editor: vscode.TextEditor): void {
|
||||
const settings: CppSettings = new CppSettings(this.RootUri);
|
||||
if (settings.dimInactiveRegions) {
|
||||
// Apply text decorations to inactive regions
|
||||
const valuePair: DecorationRangesPair | undefined = this.inactiveRegionsDecorations.get(editor.document.uri.toString());
|
||||
if (valuePair) {
|
||||
editor.setDecorations(valuePair.decoration, valuePair.ranges); // VSCode clears the decorations when the text editor becomes invisible
|
||||
private prepareVisibleRanges(editors: readonly vscode.TextEditor[]): { [uri: string]: Range[] } {
|
||||
const visibleRanges: { [uri: string]: Range[] } = {};
|
||||
editors.forEach(editor => {
|
||||
// Use a map, to account for multiple editors for the same file.
|
||||
// First, we just concat all ranges for the same file.
|
||||
const uri: string = editor.document.uri.toString();
|
||||
if (!visibleRanges[uri]) {
|
||||
visibleRanges[uri] = [];
|
||||
}
|
||||
visibleRanges[uri] = visibleRanges[uri].concat(editor.visibleRanges.map(makeLspRange));
|
||||
});
|
||||
|
||||
// We may need to merge visible ranges, if there are multiple editors for the same file,
|
||||
// and some of the ranges overlap.
|
||||
Object.keys(visibleRanges).forEach(uri => {
|
||||
visibleRanges[uri] = util.mergeOverlappingRanges(visibleRanges[uri]);
|
||||
});
|
||||
|
||||
return visibleRanges;
|
||||
}
|
||||
|
||||
// Handles changes to visible files/ranges, changes to current selection/position,
|
||||
// and changes to the active text editor. Should only be called on the primary client.
|
||||
public async onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]): Promise<void> {
|
||||
const params: DidChangeVisibleTextEditorsParams = {
|
||||
visibleRanges: this.prepareVisibleRanges(editors)
|
||||
};
|
||||
if (vscode.window.activeTextEditor) {
|
||||
if (util.isCpp(vscode.window.activeTextEditor.document)) {
|
||||
params.activeUri = vscode.window.activeTextEditor.document.uri.toString();
|
||||
params.activeSelection = makeLspRange(vscode.window.activeTextEditor.selection);
|
||||
}
|
||||
}
|
||||
|
||||
await this.languageClient.sendNotification(DidChangeVisibleTextEditorsNotification, params);
|
||||
}
|
||||
|
||||
public async onDidChangeTextEditorVisibleRanges(uri: vscode.Uri): Promise<void> {
|
||||
// VS Code will notify us of a particular editor, but same file may be open in
|
||||
// multiple editors, so we coalesc those visible ranges.
|
||||
const editors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(editor => editor.document.uri === uri);
|
||||
|
||||
let visibleRanges: Range[] = [];
|
||||
if (editors.length === 1) {
|
||||
visibleRanges = editors[0].visibleRanges.map(makeLspRange);
|
||||
} else {
|
||||
editors.forEach(editor => {
|
||||
// Use a map, to account for multiple editors for the same file.
|
||||
// First, we just concat all ranges for the same file.
|
||||
visibleRanges = visibleRanges.concat(editor.visibleRanges.map(makeLspRange));
|
||||
});
|
||||
}
|
||||
|
||||
const params: DidChangeTextEditorVisibleRangesParams = {
|
||||
uri: uri.toString(),
|
||||
visibleRanges
|
||||
};
|
||||
|
||||
await this.languageClient.sendNotification(DidChangeTextEditorVisibleRangesNotification, params);
|
||||
}
|
||||
|
||||
public onDidChangeTextDocument(textDocumentChangeEvent: vscode.TextDocumentChangeEvent): void {
|
||||
@@ -1710,10 +1732,14 @@ export class DefaultClient implements Client {
|
||||
public onDidCloseTextDocument(document: vscode.TextDocument): void {
|
||||
const uri: string = document.uri.toString();
|
||||
if (this.semanticTokensProvider) {
|
||||
this.semanticTokensProvider.invalidateFile(uri);
|
||||
this.semanticTokensProvider.removeFile(uri);
|
||||
}
|
||||
if (this.inlayHintsProvider) {
|
||||
this.inlayHintsProvider.invalidateFile(uri);
|
||||
this.inlayHintsProvider.removeFile(uri);
|
||||
}
|
||||
this.inactiveRegionsDecorations.delete(uri);
|
||||
if (diagnosticsCollectionIntelliSense) {
|
||||
diagnosticsCollectionIntelliSense.delete(document.uri);
|
||||
}
|
||||
openFileVersions.delete(uri);
|
||||
}
|
||||
@@ -1790,11 +1816,9 @@ export class DefaultClient implements Client {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.clearCustomConfigurations();
|
||||
await Promise.all([
|
||||
this.clearCustomConfigurations(),
|
||||
this.handleRemoveAllCodeAnalysisProblems()]);
|
||||
await Promise.all([
|
||||
...[...this.trackedDocuments].map(document => this.provideCustomConfiguration(document.uri, undefined, true))
|
||||
...[...this.trackedDocuments].map(([_uri, document]) => this.provideCustomConfiguration(document.uri, undefined, true))
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -2120,14 +2144,11 @@ export class DefaultClient implements Client {
|
||||
* that it knows about the file, as well as adding it to this client's set of
|
||||
* tracked documents.
|
||||
*/
|
||||
public async takeOwnership(document: vscode.TextDocument): Promise<void> {
|
||||
this.trackedDocuments.add(document);
|
||||
this.updateActiveDocumentTextOptions();
|
||||
// in case the client is recreated, wait for the isStarted to finish.
|
||||
await DefaultClient.isStarted;
|
||||
return this.sendDidOpen(document);
|
||||
public takeOwnership(document: vscode.TextDocument): void {
|
||||
this.trackedDocuments.set(document.uri.toString(), document);
|
||||
}
|
||||
|
||||
// Only used in crash recovery. Otherwise, VS Code sends didOpen directly to native process (through the protocolFilter).
|
||||
public async sendDidOpen(document: vscode.TextDocument): Promise<void> {
|
||||
const params: DidOpenTextDocumentParams = {
|
||||
textDocument: {
|
||||
@@ -2137,9 +2158,15 @@ export class DefaultClient implements Client {
|
||||
text: document.getText()
|
||||
}
|
||||
};
|
||||
await this.ready;
|
||||
await this.languageClient.sendNotification(DidOpenNotification, params);
|
||||
}
|
||||
|
||||
public async getDirectlyReferencedHeaders(): Promise<string[]> {
|
||||
await this.ready;
|
||||
return this.languageClient.sendRequest(DirectlyReferencedHeadersRequest, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* a Promise that can be awaited to know when it's ok to proceed.
|
||||
*
|
||||
@@ -2256,7 +2283,6 @@ export class DefaultClient implements Client {
|
||||
this.languageClient.onNotification(LogTelemetryNotification, logTelemetry);
|
||||
this.languageClient.onNotification(ReportStatusNotification, (e) => void this.updateStatus(e));
|
||||
this.languageClient.onNotification(ReportTagParseStatusNotification, (e) => this.updateTagParseStatus(e));
|
||||
this.languageClient.onNotification(InactiveRegionNotification, (e) => this.updateInactiveRegions(e));
|
||||
this.languageClient.onNotification(CompileCommandsPathsNotification, (e) => void this.promptCompileCommands(e));
|
||||
this.languageClient.onNotification(ReferencesNotification, (e) => this.processReferencesPreview(e));
|
||||
this.languageClient.onNotification(ReportReferencesProgressNotification, (e) => this.handleReferencesProgress(e));
|
||||
@@ -2267,14 +2293,12 @@ export class DefaultClient implements Client {
|
||||
void defaultClient.handleRequestCustomConfig(requestFile);
|
||||
}
|
||||
});
|
||||
this.languageClient.onNotification(PublishIntelliSenseDiagnosticsNotification, publishIntelliSenseDiagnostics);
|
||||
this.languageClient.onNotification(IntelliSenseResultNotification, (e) => this.handleIntelliSenseResult(e));
|
||||
this.languageClient.onNotification(PublishRefactorDiagnosticsNotification, publishRefactorDiagnostics);
|
||||
RegisterCodeAnalysisNotifications(this.languageClient);
|
||||
this.languageClient.onNotification(ShowMessageWindowNotification, showMessageWindow);
|
||||
this.languageClient.onNotification(ShowWarningNotification, showWarning);
|
||||
this.languageClient.onNotification(ReportTextDocumentLanguage, (e) => this.setTextDocumentLanguage(e));
|
||||
this.languageClient.onNotification(SemanticTokensChanged, (e) => this.semanticTokensProvider?.invalidateFile(e));
|
||||
this.languageClient.onNotification(InlayHintsChanged, (e) => this.inlayHintsProvider?.invalidateFile(e));
|
||||
this.languageClient.onNotification(IntelliSenseSetupNotification, (e) => this.logIntelliSenseSetupTime(e));
|
||||
this.languageClient.onNotification(SetTemporaryTextDocumentLanguageNotification, (e) => void this.setTemporaryTextDocumentLanguage(e));
|
||||
this.languageClient.onNotification(ReportCodeAnalysisProcessedNotification, (e) => this.updateCodeAnalysisProcessed(e));
|
||||
@@ -2283,6 +2307,60 @@ export class DefaultClient implements Client {
|
||||
this.languageClient.onNotification(CanceledReferencesNotification, this.serverCanceledReferences);
|
||||
}
|
||||
|
||||
private handleIntelliSenseResult(intelliseSenseResult: IntelliSenseResult): void {
|
||||
const fileVersion: number | undefined = openFileVersions.get(intelliseSenseResult.uri);
|
||||
if (fileVersion !== undefined && fileVersion !== intelliseSenseResult.fileVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.semanticTokensProvider) {
|
||||
this.semanticTokensProvider.deliverTokens(intelliseSenseResult.uri, intelliseSenseResult.semanticTokens, intelliseSenseResult.clearExistingSemanticTokens);
|
||||
}
|
||||
if (this.inlayHintsProvider) {
|
||||
this.inlayHintsProvider.deliverInlayHints(intelliseSenseResult.uri, intelliseSenseResult.inlayHints, intelliseSenseResult.clearExistingInlayHint);
|
||||
}
|
||||
|
||||
this.updateInactiveRegions(intelliseSenseResult.uri, intelliseSenseResult.inactiveRegions, intelliseSenseResult.clearExistingInactiveRegions, intelliseSenseResult.isCompletePass);
|
||||
this.updateSquiggles(intelliseSenseResult.uri, intelliseSenseResult.diagnostics, intelliseSenseResult.clearExistingDiagnostics);
|
||||
}
|
||||
|
||||
private updateSquiggles(uriString: string, diagnostics: IntelliSenseDiagnostic[], startNewSet: boolean): void {
|
||||
|
||||
if (!diagnosticsCollectionIntelliSense) {
|
||||
diagnosticsCollectionIntelliSense = vscode.languages.createDiagnosticCollection(configPrefix + "IntelliSense");
|
||||
}
|
||||
|
||||
// Convert from our Diagnostic objects to vscode Diagnostic objects
|
||||
|
||||
const diagnosticsIntelliSense: vscode.Diagnostic[] = [];
|
||||
diagnostics.forEach((d) => {
|
||||
const message: string = getLocalizedString(d.localizeStringParams);
|
||||
const diagnostic: vscode.Diagnostic = new vscode.Diagnostic(makeVscodeRange(d.range), message, d.severity);
|
||||
diagnostic.code = d.code;
|
||||
diagnostic.source = CppSourceStr;
|
||||
if (d.relatedInformation) {
|
||||
diagnostic.relatedInformation = [];
|
||||
for (const info of d.relatedInformation) {
|
||||
diagnostic.relatedInformation.push(new vscode.DiagnosticRelatedInformation(makeVscodeLocation(info.location), info.message));
|
||||
}
|
||||
}
|
||||
|
||||
diagnosticsIntelliSense.push(diagnostic);
|
||||
});
|
||||
|
||||
const realUri: vscode.Uri = vscode.Uri.parse(uriString);
|
||||
if (!startNewSet) {
|
||||
const existingDiagnostics: readonly vscode.Diagnostic[] | undefined = diagnosticsCollectionIntelliSense.get(realUri);
|
||||
if (existingDiagnostics) {
|
||||
// Note: The spread operator puts every element on the stack, so it should be avoided for large arrays.
|
||||
Array.prototype.push.apply(diagnosticsIntelliSense, existingDiagnostics as any[]);
|
||||
}
|
||||
}
|
||||
diagnosticsCollectionIntelliSense.set(realUri, diagnosticsIntelliSense);
|
||||
|
||||
clients.timeTelemetryCollector.setUpdateRangeTime(realUri);
|
||||
}
|
||||
|
||||
private setTextDocumentLanguage(languageStr: string): void {
|
||||
const cppSettings: CppSettings = new CppSettings();
|
||||
if (cppSettings.autoAddFileAssociations) {
|
||||
@@ -2295,7 +2373,9 @@ export class DefaultClient implements Client {
|
||||
|
||||
private async setTemporaryTextDocumentLanguage(params: SetTemporaryTextDocumentLanguageParams): Promise<void> {
|
||||
const languageId: string = params.isC ? "c" : params.isCuda ? "cuda-cpp" : "cpp";
|
||||
const document: vscode.TextDocument = await vscode.workspace.openTextDocument(params.path);
|
||||
const uri: vscode.Uri = vscode.Uri.parse(params.uri);
|
||||
const client: Client = clients.getClientFor(uri);
|
||||
const document: vscode.TextDocument | undefined = client.TrackedDocuments.get(params.uri);
|
||||
if (!!document && document.languageId !== languageId) {
|
||||
if (document.languageId === "cpp" && languageId === "c") {
|
||||
handleChangedFromCppToC(document);
|
||||
@@ -2504,44 +2584,49 @@ export class DefaultClient implements Client {
|
||||
this.model.isParsingWorkspacePaused.Value = tagParseStatus.isPaused;
|
||||
}
|
||||
|
||||
private updateInactiveRegions(params: InactiveRegionParams): void {
|
||||
const settings: CppSettings = new CppSettings(this.RootUri);
|
||||
const opacity: number | undefined = settings.inactiveRegionOpacity;
|
||||
if (opacity !== null && opacity !== undefined) {
|
||||
const decoration: vscode.TextEditorDecorationType = vscode.window.createTextEditorDecorationType({
|
||||
opacity: opacity.toString(),
|
||||
backgroundColor: settings.inactiveRegionBackgroundColor,
|
||||
color: settings.inactiveRegionForegroundColor,
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.OpenOpen
|
||||
});
|
||||
// We must convert to vscode.Ranges in order to make use of the API's
|
||||
const ranges: vscode.Range[] = params.regions.map(element => new vscode.Range(element.startLine, 0, element.endLine, 0));
|
||||
// Find entry for cached file and act accordingly
|
||||
const valuePair: DecorationRangesPair | undefined = this.inactiveRegionsDecorations.get(params.uri);
|
||||
if (valuePair) {
|
||||
// Disposing of and resetting the decoration will undo previously applied text decorations
|
||||
valuePair.decoration.dispose();
|
||||
valuePair.decoration = decoration;
|
||||
// As vscode.TextEditor.setDecorations only applies to visible editors, we must cache the range for when another editor becomes visible
|
||||
valuePair.ranges = ranges;
|
||||
} else { // The entry does not exist. Make a new one
|
||||
const toInsert: DecorationRangesPair = {
|
||||
decoration: decoration,
|
||||
ranges: ranges
|
||||
};
|
||||
this.inactiveRegionsDecorations.set(params.uri, toInsert);
|
||||
}
|
||||
if (settings.dimInactiveRegions && params.fileVersion === openFileVersions.get(params.uri)) {
|
||||
// Apply the decorations to all *visible* text editors
|
||||
const editors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => e.document.uri.toString() === params.uri);
|
||||
for (const e of editors) {
|
||||
e.setDecorations(decoration, ranges);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.codeFoldingProvider) {
|
||||
private updateInactiveRegions(uriString: string, inactiveRegions: InputRegion[], startNewSet: boolean, updateFoldingRanges: boolean): void {
|
||||
if (this.codeFoldingProvider && updateFoldingRanges) {
|
||||
this.codeFoldingProvider.refresh();
|
||||
}
|
||||
|
||||
const client: Client = clients.getClientFor(vscode.Uri.parse(uriString));
|
||||
if (!(client instanceof DefaultClient) || (!startNewSet && inactiveRegions.length === 0)) {
|
||||
return;
|
||||
}
|
||||
const settings: CppSettings = new CppSettings(client.RootUri);
|
||||
const dimInactiveRegions: boolean = settings.dimInactiveRegions;
|
||||
let currentSet: DecorationRangesPair | undefined = this.inactiveRegionsDecorations.get(uriString);
|
||||
if (startNewSet || !dimInactiveRegions) {
|
||||
if (currentSet) {
|
||||
currentSet.decoration.dispose();
|
||||
this.inactiveRegionsDecorations.delete(uriString);
|
||||
}
|
||||
if (!dimInactiveRegions) {
|
||||
return;
|
||||
}
|
||||
currentSet = undefined;
|
||||
}
|
||||
if (currentSet === undefined) {
|
||||
const opacity: number | undefined = settings.inactiveRegionOpacity;
|
||||
currentSet = {
|
||||
decoration: vscode.window.createTextEditorDecorationType({
|
||||
opacity: (opacity === undefined) ? "0.55" : opacity.toString(),
|
||||
backgroundColor: settings.inactiveRegionBackgroundColor,
|
||||
color: settings.inactiveRegionForegroundColor,
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.OpenOpen
|
||||
}),
|
||||
ranges: []
|
||||
};
|
||||
this.inactiveRegionsDecorations.set(uriString, currentSet);
|
||||
}
|
||||
|
||||
Array.prototype.push.apply(currentSet.ranges, inactiveRegions.map(element => new vscode.Range(element.startLine, 0, element.endLine, 0)));
|
||||
|
||||
// Apply the decorations to all *visible* text editors
|
||||
const editors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => e.document.uri.toString() === uriString);
|
||||
for (const e of editors) {
|
||||
e.setDecorations(currentSet.decoration, currentSet.ranges);
|
||||
}
|
||||
}
|
||||
|
||||
public logIntelliSenseSetupTime(notification: IntelliSenseSetup): void {
|
||||
@@ -2637,7 +2722,7 @@ export class DefaultClient implements Client {
|
||||
return results;
|
||||
}
|
||||
|
||||
private updateActiveDocumentTextOptions(): void {
|
||||
public updateActiveDocumentTextOptions(): void {
|
||||
const editor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
|
||||
if (editor && util.isCpp(editor.document)) {
|
||||
void SessionState.buildAndDebugIsSourceFile.set(util.isCppOrCFile(editor.document.uri));
|
||||
@@ -2670,13 +2755,27 @@ export class DefaultClient implements Client {
|
||||
/**
|
||||
* notifications to the language server
|
||||
*/
|
||||
public async activeDocumentChanged(document: vscode.TextDocument): Promise<void> {
|
||||
this.updateActiveDocumentTextOptions();
|
||||
if (!util.isCpp(document)) {
|
||||
public async didChangeActiveEditor(editor?: vscode.TextEditor): Promise<void> {
|
||||
// For now, we ignore deactivation events.
|
||||
// VS will refresh IntelliSense on activation, as a catch-all for file changes in
|
||||
// other applications. But VS Code will deactivate the document when focus is moved
|
||||
// to another control, such as the Output window. So, to avoid costly updates, we
|
||||
// only trigger that update when focus moves from one C++ document to another.
|
||||
// Fortunately, VS Code generates file-change notifications for all files
|
||||
// in the workspace, so we should trigger appropriate updates for most changes
|
||||
// made in other applications.
|
||||
if (!editor || !util.isCpp(editor.document)) {
|
||||
return;
|
||||
}
|
||||
await this.ready;
|
||||
return this.languageClient.sendNotification(ActiveDocumentChangeNotification, this.languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document)).catch(logAndReturn.undefined);
|
||||
|
||||
this.updateActiveDocumentTextOptions();
|
||||
|
||||
const params: DidChangeActiveEditorParams = {
|
||||
uri: editor?.document?.uri.toString(),
|
||||
selection: editor ? makeLspRange(editor.selection) : undefined
|
||||
};
|
||||
|
||||
return this.languageClient.sendNotification(DidChangeActiveEditorNotification, params).catch(logAndReturn.undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2696,8 +2795,7 @@ export class DefaultClient implements Client {
|
||||
}
|
||||
|
||||
public async selectionChanged(selection: Range): Promise<void> {
|
||||
await this.ready;
|
||||
return this.languageClient.sendNotification(TextEditorSelectionChangeNotification, selection);
|
||||
return this.languageClient.sendNotification(DidChangeTextEditorSelectionNotification, selection);
|
||||
}
|
||||
|
||||
public async resetDatabase(): Promise<void> {
|
||||
@@ -3531,7 +3629,6 @@ export class DefaultClient implements Client {
|
||||
const isReplace: boolean = !range.isEmpty && isSourceFile;
|
||||
lineOffset += nextLineOffset;
|
||||
nextLineOffset = (edit.newText.match(/\n/g) ?? []).length;
|
||||
let rangeStartLine: number = range.start.line + lineOffset;
|
||||
|
||||
// Find the editType.
|
||||
if (isReplace) {
|
||||
@@ -3553,6 +3650,8 @@ export class DefaultClient implements Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
const formatRangeStartLine: number = range.start.line + lineOffset;
|
||||
let rangeStartLine: number = formatRangeStartLine;
|
||||
let rangeStartCharacter: number = 0;
|
||||
if (edit.newText.startsWith("\r\n\r\n")) {
|
||||
rangeStartCharacter = 4;
|
||||
@@ -3567,15 +3666,15 @@ export class DefaultClient implements Client {
|
||||
rangeStartCharacter = 1;
|
||||
rangeStartLine += 1;
|
||||
}
|
||||
const newRange: vscode.Range = new vscode.Range(
|
||||
new vscode.Position(rangeStartLine + (nextLineOffset < 0 ? nextLineOffset : 0), range.start.character),
|
||||
const newFormatRange: vscode.Range = new vscode.Range(
|
||||
new vscode.Position(formatRangeStartLine + (nextLineOffset < 0 ? nextLineOffset : 0), range.start.character),
|
||||
new vscode.Position(rangeStartLine + (nextLineOffset < 0 ? 0 : nextLineOffset),
|
||||
isReplace ? range.end.character :
|
||||
range.end.character + edit.newText.length - rangeStartCharacter));
|
||||
if (isSourceFile) {
|
||||
sourceFormatUriAndRanges.push({uri, range: newRange});
|
||||
sourceFormatUriAndRanges.push({uri, range: newFormatRange});
|
||||
} else {
|
||||
headerFormatUriAndRanges.push({uri, range: newRange});
|
||||
headerFormatUriAndRanges.push({uri, range: newFormatRange});
|
||||
}
|
||||
if (isReplace || !isSourceFile) {
|
||||
// Handle additional declaration lines added before the new function call.
|
||||
@@ -3701,10 +3800,7 @@ export class DefaultClient implements Client {
|
||||
// These events can be discarded until the language client is ready.
|
||||
// Don't queue them up with this.notifyWhenLanguageClientReady calls.
|
||||
if (this.innerLanguageClient !== undefined && this.configuration !== undefined) {
|
||||
const params: IntervalTimerParams = {
|
||||
freeMemory: Math.floor(os.freemem() / 1048576)
|
||||
};
|
||||
void this.languageClient.sendNotification(IntervalTimerNotification, params).catch(logAndReturn.undefined);
|
||||
void this.languageClient.sendNotification(IntervalTimerNotification).catch(logAndReturn.undefined);
|
||||
this.configuration.checkCppProperties();
|
||||
this.configuration.checkCompileCommands();
|
||||
}
|
||||
@@ -3825,11 +3921,12 @@ class NullClient implements Client {
|
||||
RootRealPath: string = "/";
|
||||
RootUri?: vscode.Uri = vscode.Uri.file("/");
|
||||
Name: string = "(empty)";
|
||||
TrackedDocuments = new Set<vscode.TextDocument>();
|
||||
TrackedDocuments = new Map<string, vscode.TextDocument>();
|
||||
async onDidChangeSettings(event: vscode.ConfigurationChangeEvent): Promise<Record<string, string>> { return {}; }
|
||||
onDidOpenTextDocument(document: vscode.TextDocument): void { }
|
||||
onDidCloseTextDocument(document: vscode.TextDocument): void { }
|
||||
onDidChangeVisibleTextEditor(editor: vscode.TextEditor): void { }
|
||||
onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]): Promise<void> { return Promise.resolve(); }
|
||||
onDidChangeTextEditorVisibleRanges(uri: vscode.Uri): Promise<void> { return Promise.resolve(); }
|
||||
onDidChangeTextDocument(textDocumentChangeEvent: vscode.TextDocumentChangeEvent): void { }
|
||||
onRegisterCustomConfigurationProvider(provider: CustomConfigurationProvider1): Thenable<void> { return Promise.resolve(); }
|
||||
updateCustomConfigurations(requestingProvider?: CustomConfigurationProvider1): Thenable<void> { return Promise.resolve(); }
|
||||
@@ -3845,10 +3942,11 @@ class NullClient implements Client {
|
||||
getVcpkgEnabled(): Thenable<boolean> { return Promise.resolve(false); }
|
||||
getCurrentCompilerPathAndArgs(): Thenable<util.CompilerPathAndArgs | undefined> { return Promise.resolve(undefined); }
|
||||
getKnownCompilers(): Thenable<configs.KnownCompiler[] | undefined> { return Promise.resolve([]); }
|
||||
takeOwnership(document: vscode.TextDocument): Promise<void> { return Promise.resolve(); }
|
||||
takeOwnership(document: vscode.TextDocument): void { }
|
||||
sendDidOpen(document: vscode.TextDocument): Promise<void> { return Promise.resolve(); }
|
||||
requestSwitchHeaderSource(rootUri: vscode.Uri, fileName: string): Thenable<string> { return Promise.resolve(""); }
|
||||
activeDocumentChanged(document: vscode.TextDocument): Promise<void> { return Promise.resolve(); }
|
||||
updateActiveDocumentTextOptions(): void { }
|
||||
didChangeActiveEditor(editor?: vscode.TextEditor): Promise<void> { return Promise.resolve(); }
|
||||
restartIntelliSenseForFile(document: vscode.TextDocument): Promise<void> { return Promise.resolve(); }
|
||||
activate(): void { }
|
||||
selectionChanged(selection: Range): void { }
|
||||
@@ -3892,4 +3990,5 @@ class NullClient implements Client {
|
||||
getShowConfigureIntelliSenseButton(): boolean { return false; }
|
||||
setShowConfigureIntelliSenseButton(show: boolean): void { }
|
||||
addTrustedCompiler(path: string): Promise<void> { return Promise.resolve(); }
|
||||
getDirectlyReferencedHeaders(): Promise<string[]> { return Promise.resolve([]); }
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { logAndReturn } from '../Utility/Async/returns';
|
||||
import * as util from '../common';
|
||||
import * as telemetry from '../telemetry';
|
||||
import * as cpptools from './client';
|
||||
@@ -67,12 +66,13 @@ export class ClientCollection {
|
||||
this.disposables.push(vscode.workspace.onDidChangeWorkspaceFolders(e => this.onDidChangeWorkspaceFolders(e)));
|
||||
}
|
||||
|
||||
public async activeDocumentChanged(document: vscode.TextDocument): Promise<void> {
|
||||
this.activeDocument = document;
|
||||
const activeClient: cpptools.Client = this.getClientFor(document.uri);
|
||||
public async didChangeActiveEditor(editor?: vscode.TextEditor): Promise<void> {
|
||||
this.activeDocument = editor?.document;
|
||||
|
||||
// Notify the active client that the document has changed.
|
||||
await activeClient.activeDocumentChanged(document);
|
||||
// If there is no active document, switch to the default client.
|
||||
const activeClient: cpptools.Client = !editor ? this.defaultClient : this.getClientFor(editor.document.uri);
|
||||
await activeClient.didChangeActiveEditor(editor);
|
||||
|
||||
// If the active client changed, resume the new client and tell the currently active client to deactivate.
|
||||
if (activeClient !== this.activeClient) {
|
||||
@@ -121,7 +121,10 @@ export class ClientCollection {
|
||||
const client: cpptools.Client = pair[1];
|
||||
|
||||
const newClient: cpptools.Client = this.createClient(client.RootFolder, true);
|
||||
client.TrackedDocuments.forEach(document => void this.transferOwnership(document, client).catch(logAndReturn.undefined));
|
||||
for (const document of client.TrackedDocuments.values()) {
|
||||
this.transferOwnership(document, client);
|
||||
await newClient.sendDidOpen(document);
|
||||
}
|
||||
|
||||
if (this.activeClient === client) {
|
||||
// It cannot be undefined. If there is an active document, we activate it later.
|
||||
@@ -137,9 +140,12 @@ export class ClientCollection {
|
||||
|
||||
if (this.activeDocument) {
|
||||
this.activeClient = this.getClientFor(this.activeDocument.uri);
|
||||
await this.activeClient.activeDocumentChanged(this.activeDocument);
|
||||
this.activeClient.updateActiveDocumentTextOptions();
|
||||
this.activeClient.activate();
|
||||
await this.activeClient.didChangeActiveEditor(vscode.window.activeTextEditor);
|
||||
}
|
||||
const cppEditors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => util.isCpp(e.document));
|
||||
await this.defaultClient.onDidChangeVisibleTextEditors(cppEditors);
|
||||
}
|
||||
|
||||
private async onDidChangeWorkspaceFolders(e?: vscode.WorkspaceFoldersChangeEvent): Promise<void> {
|
||||
@@ -157,8 +163,7 @@ export class ClientCollection {
|
||||
this.languageClients.delete(path); // Do this first so that we don't iterate on it during the ownership transfer process.
|
||||
|
||||
// Transfer ownership of the client's documents to another client.
|
||||
// (this includes calling textDocument/didOpen on the new client so that the server knows it's open too)
|
||||
client.TrackedDocuments.forEach(document => void this.transferOwnership(document, client).catch(logAndReturn.undefined));
|
||||
client.TrackedDocuments.forEach(document => this.transferOwnership(document, client));
|
||||
|
||||
if (this.activeClient === client) {
|
||||
this.activeClient.deactivate();
|
||||
@@ -198,17 +203,17 @@ export class ClientCollection {
|
||||
// Redundant deactivate should be OK.
|
||||
this.activeClient.deactivate();
|
||||
this.activeClient = newActiveClient;
|
||||
await this.activeClient.activeDocumentChanged(this.activeDocument);
|
||||
this.activeClient.updateActiveDocumentTextOptions();
|
||||
this.activeClient.activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async transferOwnership(document: vscode.TextDocument, oldOwner: cpptools.Client): Promise<void> {
|
||||
private transferOwnership(document: vscode.TextDocument, oldOwner: cpptools.Client): void {
|
||||
const newOwner: cpptools.Client = this.getClientFor(document.uri);
|
||||
if (newOwner !== oldOwner) {
|
||||
return newOwner.takeOwnership(document);
|
||||
newOwner.takeOwnership(document);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -806,7 +806,7 @@ export class CppProperties {
|
||||
if (isGlobPattern) {
|
||||
// fastGlob silently strips non-found paths. Limit that behavior to dynamic paths only.
|
||||
const matches: string[] = fastGlob.isDynamicPattern(normalized) ?
|
||||
fastGlob.sync(normalized, { onlyDirectories: true, cwd }) : [res];
|
||||
fastGlob.sync(normalized, { onlyDirectories: true, cwd, suppressErrors: true, deep: 15 }) : [res];
|
||||
resolvedGlob.push(...matches.map(s => s + suffix));
|
||||
} else {
|
||||
resolvedGlob.push(normalized + suffix);
|
||||
@@ -1536,9 +1536,15 @@ export class CppProperties {
|
||||
|
||||
// Check if config name is unique.
|
||||
errors.name = this.isConfigNameUnique(config.name);
|
||||
|
||||
let resolvedCompilerPath: string | undefined;
|
||||
// Validate compilerPath
|
||||
let resolvedCompilerPath: string | undefined = this.resolvePath(config.compilerPath);
|
||||
if (config.compilerPath) {
|
||||
resolvedCompilerPath = which.sync(config.compilerPath, { nothrow: true }) ?? undefined;
|
||||
}
|
||||
|
||||
if (resolvedCompilerPath === undefined) {
|
||||
resolvedCompilerPath = this.resolvePath(config.compilerPath);
|
||||
}
|
||||
const settings: CppSettings = new CppSettings(this.rootUri);
|
||||
const compilerPathAndArgs: util.CompilerPathAndArgs = util.extractCompilerPathAndArgs(!!settings.legacyCompilerArgsBehavior, resolvedCompilerPath);
|
||||
if (resolvedCompilerPath
|
||||
@@ -1993,7 +1999,7 @@ export class CppProperties {
|
||||
|
||||
// Escape the path string for literal use in a regular expression
|
||||
// Need to escape any quotes to match the original text
|
||||
let escapedPath: string = curPath.replace(/\"/g, '\\\"');
|
||||
let escapedPath: string = curPath.replace(/"/g, '\\"');
|
||||
escapedPath = escapedPath.replace(/[-\"\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
|
||||
// Create a pattern to search for the path with either a quote or semicolon immediately before and after,
|
||||
|
||||
@@ -170,11 +170,9 @@ export class CppBuildTaskProvider implements TaskProvider {
|
||||
const compilerPathBase: string = path.basename(compilerPath);
|
||||
const isCl: boolean = compilerPathBase.toLowerCase() === "cl.exe";
|
||||
const isClang: boolean = !isCl && compilerPathBase.toLowerCase().includes("clang");
|
||||
// Double-quote the command if it is not already double-quoted.
|
||||
// Double-quote the command if needed.
|
||||
let resolvedcompilerPath: string = isCl ? compilerPathBase : compilerPath;
|
||||
if (resolvedcompilerPath && !resolvedcompilerPath.startsWith("\"") && resolvedcompilerPath.includes(" ")) {
|
||||
resolvedcompilerPath = "\"" + resolvedcompilerPath + "\"";
|
||||
}
|
||||
resolvedcompilerPath = util.quoteArgument(resolvedcompilerPath);
|
||||
|
||||
if (!definition) {
|
||||
const isWindows: boolean = os.platform() === 'win32';
|
||||
@@ -209,7 +207,7 @@ export class CppBuildTaskProvider implements TaskProvider {
|
||||
const task: CppBuildTask = new Task(definition, scope, definition.label, ext.CppSourceStr,
|
||||
new CustomExecution(async (resolvedDefinition: TaskDefinition): Promise<Pseudoterminal> =>
|
||||
// When the task is executed, this callback will run. Here, we setup for running the task.
|
||||
new CustomBuildTaskTerminal(resolvedcompilerPath, resolvedDefinition.args, resolvedDefinition.options, {taskUsesActiveFile, insertStd: isClang && os.platform() === 'darwin'})
|
||||
new CustomBuildTaskTerminal(resolvedcompilerPath, resolvedDefinition.args, resolvedDefinition.options, { taskUsesActiveFile, insertStd: isClang && os.platform() === 'darwin' })
|
||||
), isCl ? '$msCompile' : '$gcc');
|
||||
|
||||
task.group = TaskGroup.Build;
|
||||
@@ -390,7 +388,7 @@ class CustomBuildTaskTerminal implements Pseudoterminal {
|
||||
util.createDirIfNotExistsSync(exePath);
|
||||
|
||||
this.args.forEach((value, index) => {
|
||||
value = util.normalizeArg(util.resolveVariables(value));
|
||||
value = util.quoteArgument(util.resolveVariables(value));
|
||||
activeCommand = activeCommand + " " + value;
|
||||
this.args[index] = value;
|
||||
});
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { CustomConfigurationProvider, SourceFileConfigurationItem, Version, WorkspaceBrowseConfiguration } from 'vscode-cpptools';
|
||||
import * as ext from './extension';
|
||||
import { CppSettings } from './settings';
|
||||
|
||||
/**
|
||||
@@ -158,7 +157,8 @@ export class CustomConfigurationProviderCollection {
|
||||
}
|
||||
|
||||
public add(provider: CustomConfigurationProvider, version: Version): boolean {
|
||||
if (new CppSettings(ext.getActiveClient().RootUri).intelliSenseEngine === "disabled") {
|
||||
const settings: CppSettings = new CppSettings((vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) ? vscode.workspace.workspaceFolders[0]?.uri : undefined);
|
||||
if (settings.intelliSenseEngine === "disabled") {
|
||||
console.warn("Language service is disabled. Provider will not be registered.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import * as vscode from 'vscode';
|
||||
import { Range } from 'vscode-languageclient';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { TargetPopulation } from 'vscode-tas-client';
|
||||
import * as which from 'which';
|
||||
import { logAndReturn } from '../Utility/Async/returns';
|
||||
import * as util from '../common';
|
||||
import { PlatformInformation } from '../platform';
|
||||
@@ -28,7 +29,7 @@ import { PersistentState } from './persistentState';
|
||||
import { NodeType, TreeNode } from './referencesModel';
|
||||
import { CppSettings } from './settings';
|
||||
import { LanguageStatusUI, getUI } from './ui';
|
||||
import { makeCpptoolsRange, rangeEquals, shouldChangeFromCToCpp, showInstallCompilerWalkthrough } from './utils';
|
||||
import { makeLspRange, rangeEquals, showInstallCompilerWalkthrough } from './utils';
|
||||
|
||||
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
|
||||
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
|
||||
@@ -37,7 +38,7 @@ export const configPrefix: string = "C/C++: ";
|
||||
|
||||
let prevCrashFile: string;
|
||||
export let clients: ClientCollection;
|
||||
let activeDocument: string;
|
||||
let activeDocument: vscode.TextDocument | undefined;
|
||||
let ui: LanguageStatusUI;
|
||||
const disposables: vscode.Disposable[] = [];
|
||||
const commandDisposables: vscode.Disposable[] = [];
|
||||
@@ -167,10 +168,11 @@ export async function activate(): Promise<void> {
|
||||
});
|
||||
|
||||
disposables.push(vscode.workspace.onDidChangeConfiguration(onDidChangeSettings));
|
||||
disposables.push(vscode.window.onDidChangeActiveTextEditor(onDidChangeActiveTextEditor));
|
||||
ui.activeDocumentChanged(); // Handle already active documents (for non-cpp files that we don't register didOpen).
|
||||
disposables.push(vscode.window.onDidChangeTextEditorSelection(onDidChangeTextEditorSelection));
|
||||
disposables.push(vscode.window.onDidChangeVisibleTextEditors(onDidChangeVisibleTextEditors));
|
||||
disposables.push(vscode.window.onDidChangeTextEditorVisibleRanges((e) => clients.ActiveClient.enqueue(async () => onDidChangeTextEditorVisibleRanges(e))));
|
||||
disposables.push(vscode.window.onDidChangeActiveTextEditor((e) => clients.ActiveClient.enqueue(async () => onDidChangeActiveTextEditor(e))));
|
||||
ui.didChangeActiveEditor(); // Handle already active documents (for non-cpp files that we don't register didOpen).
|
||||
disposables.push(vscode.window.onDidChangeTextEditorSelection((e) => clients.ActiveClient.enqueue(async () => onDidChangeTextEditorSelection(e))));
|
||||
disposables.push(vscode.window.onDidChangeVisibleTextEditors((e) => clients.ActiveClient.enqueue(async () => onDidChangeVisibleTextEditors(e))));
|
||||
|
||||
updateLanguageConfigurations();
|
||||
|
||||
@@ -244,6 +246,7 @@ export async function activate(): Promise<void> {
|
||||
const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
|
||||
if (activeEditor) {
|
||||
clients.timeTelemetryCollector.setFirstFile(activeEditor.document.uri);
|
||||
activeDocument = activeEditor.document;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +281,13 @@ async function onDidChangeSettings(event: vscode.ConfigurationChangeEvent): Prom
|
||||
|
||||
let noActiveEditorTimeout: NodeJS.Timeout | undefined;
|
||||
|
||||
export function onDidChangeActiveTextEditor(editor?: vscode.TextEditor): void {
|
||||
async function onDidChangeTextEditorVisibleRanges(event: vscode.TextEditorVisibleRangesChangeEvent): Promise<void> {
|
||||
if (util.isCpp(event.textEditor.document)) {
|
||||
await clients.getDefaultClient().onDidChangeTextEditorVisibleRanges(event.textEditor.document.uri);
|
||||
}
|
||||
}
|
||||
|
||||
function onDidChangeActiveTextEditor(editor?: vscode.TextEditor): void {
|
||||
/* need to notify the affected client(s) */
|
||||
console.assert(clients !== undefined, "client should be available before active editor is changed");
|
||||
if (clients === undefined) {
|
||||
@@ -289,93 +298,42 @@ export function onDidChangeActiveTextEditor(editor?: vscode.TextEditor): void {
|
||||
clearTimeout(noActiveEditorTimeout);
|
||||
noActiveEditorTimeout = undefined;
|
||||
}
|
||||
|
||||
if (!editor) {
|
||||
// When switching between documents, VS Code is setting the active editor to undefined
|
||||
// temporarily, so this prevents the C++-related status bar items from flickering off/on.
|
||||
noActiveEditorTimeout = setTimeout(() => {
|
||||
activeDocument = "";
|
||||
ui.activeDocumentChanged();
|
||||
activeDocument = undefined;
|
||||
ui.didChangeActiveEditor();
|
||||
noActiveEditorTimeout = undefined;
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
if (util.isCppOrRelated(editor.document)) {
|
||||
// This is required for the UI to update correctly.
|
||||
void clients.activeDocumentChanged(editor.document).catch(logAndReturn.undefined);
|
||||
if (util.isCpp(editor.document)) {
|
||||
activeDocument = editor.document.uri.toString();
|
||||
clients.ActiveClient.selectionChanged(makeCpptoolsRange(editor.selection));
|
||||
} else {
|
||||
activeDocument = "";
|
||||
}
|
||||
void clients.didChangeActiveEditor(undefined).catch(logAndReturn.undefined);
|
||||
} else {
|
||||
activeDocument = "";
|
||||
ui.didChangeActiveEditor();
|
||||
if (util.isCppOrRelated(editor.document)) {
|
||||
if (util.isCpp(editor.document)) {
|
||||
activeDocument = editor.document;
|
||||
void clients.didChangeActiveEditor(editor).catch(logAndReturn.undefined);
|
||||
} else {
|
||||
activeDocument = undefined;
|
||||
void clients.didChangeActiveEditor(undefined).catch(logAndReturn.undefined);
|
||||
}
|
||||
//clients.ActiveClient.selectionChanged(makeLspRange(editor.selection));
|
||||
} else {
|
||||
activeDocument = undefined;
|
||||
}
|
||||
}
|
||||
getUI().activeDocumentChanged();
|
||||
}
|
||||
|
||||
function onDidChangeTextEditorSelection(event: vscode.TextEditorSelectionChangeEvent): void {
|
||||
/* need to notify the affected client(s) */
|
||||
if (!event.textEditor || !vscode.window.activeTextEditor || event.textEditor.document.uri !== vscode.window.activeTextEditor.document.uri ||
|
||||
!util.isCpp(event.textEditor.document)) {
|
||||
if (!util.isCpp(event.textEditor.document)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeDocument !== event.textEditor.document.uri.toString()) {
|
||||
// For some unknown reason we don't reliably get onDidChangeActiveTextEditor callbacks.
|
||||
activeDocument = event.textEditor.document.uri.toString();
|
||||
void clients.activeDocumentChanged(event.textEditor.document).catch(logAndReturn.undefined);
|
||||
ui.activeDocumentChanged();
|
||||
}
|
||||
clients.ActiveClient.selectionChanged(makeCpptoolsRange(event.selections[0]));
|
||||
clients.ActiveClient.selectionChanged(makeLspRange(event.selections[0]));
|
||||
}
|
||||
|
||||
export async function processDelayedDidOpen(document: vscode.TextDocument): Promise<boolean> {
|
||||
const client: Client = clients.getClientFor(document.uri);
|
||||
if (client) {
|
||||
// Log warm start.
|
||||
if (clients.checkOwnership(client, document)) {
|
||||
if (!client.isInitialized()) {
|
||||
// This can randomly get hit when adding/removing workspace folders.
|
||||
await client.ready;
|
||||
}
|
||||
// Do not call await between TrackedDocuments.has() and TrackedDocuments.add(),
|
||||
// to avoid sending redundant didOpen notifications.
|
||||
if (!client.TrackedDocuments.has(document)) {
|
||||
// If not yet tracked, process as a newly opened file. (didOpen is sent to server in client.takeOwnership()).
|
||||
client.TrackedDocuments.add(document);
|
||||
clients.timeTelemetryCollector.setDidOpenTime(document.uri);
|
||||
// Work around vscode treating ".C" or ".H" as c, by adding this file name to file associations as cpp
|
||||
if (document.languageId === "c" && shouldChangeFromCToCpp(document)) {
|
||||
const baseFileName: string = path.basename(document.fileName);
|
||||
const mappingString: string = baseFileName + "@" + document.fileName;
|
||||
client.addFileAssociations(mappingString, "cpp");
|
||||
client.sendDidChangeSettings();
|
||||
document = await vscode.languages.setTextDocumentLanguage(document, "cpp");
|
||||
}
|
||||
await client.provideCustomConfiguration(document.uri, undefined);
|
||||
// client.takeOwnership() will call client.TrackedDocuments.add() again, but that's ok. It's a Set.
|
||||
client.onDidOpenTextDocument(document);
|
||||
await client.takeOwnership(document);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]): void {
|
||||
// Process delayed didOpen for any visible editors we haven't seen before
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
editors.forEach(async (editor) => {
|
||||
if (util.isCpp(editor.document)) {
|
||||
const client: Client = clients.getClientFor(editor.document.uri);
|
||||
await client.enqueue(() => processDelayedDidOpen(editor.document));
|
||||
client.onDidChangeVisibleTextEditor(editor);
|
||||
}
|
||||
});
|
||||
async function onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]): Promise<void> {
|
||||
const cppEditors: vscode.TextEditor[] = editors.filter(e => util.isCpp(e.document));
|
||||
await clients.getDefaultClient().onDidChangeVisibleTextEditors(cppEditors);
|
||||
}
|
||||
|
||||
function onInterval(): void {
|
||||
@@ -671,7 +629,7 @@ async function onGoToPrevDirectiveInGroup(): Promise<void> {
|
||||
}
|
||||
|
||||
async function onRunCodeAnalysisOnActiveFile(): Promise<void> {
|
||||
if (activeDocument !== "") {
|
||||
if (activeDocument) {
|
||||
await vscode.commands.executeCommand("workbench.action.files.saveAll");
|
||||
return getActiveClient().handleRunCodeAnalysisOnActiveFile();
|
||||
}
|
||||
@@ -965,8 +923,8 @@ function reportMacCrashes(): void {
|
||||
const crashObject: Record<string, string> = {};
|
||||
if (err?.code) {
|
||||
// If the directory isn't there, we have a problem...
|
||||
crashObject["fs.stat: err.code"] = err.code;
|
||||
telemetry.logLanguageServerEvent("MacCrash", crashObject, undefined);
|
||||
crashObject["errCode"] = err.code;
|
||||
telemetry.logLanguageServerEvent("MacCrash", crashObject);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1002,17 +960,67 @@ function reportMacCrashes(): void {
|
||||
}
|
||||
}
|
||||
|
||||
let previousMacCrashData: string;
|
||||
let previousMacCrashCount: number = 0;
|
||||
export function watchForCrashes(crashDirectory: string): void {
|
||||
if (process.platform !== "win32") {
|
||||
prevCrashFile = "";
|
||||
fs.stat(crashDirectory, (err) => {
|
||||
const crashObject: Record<string, string> = {};
|
||||
if (err?.code) {
|
||||
// If the directory isn't there, we have a problem...
|
||||
crashObject["errCode"] = err.code;
|
||||
telemetry.logLanguageServerEvent("CppCrash", crashObject);
|
||||
return;
|
||||
}
|
||||
|
||||
function logMacCrashTelemetry(data: string): void {
|
||||
// vscode.workspace.createFileSystemWatcher only works in workspace folders.
|
||||
try {
|
||||
fs.watch(crashDirectory, (event, filename) => {
|
||||
if (event !== "rename") {
|
||||
return;
|
||||
}
|
||||
if (!filename || filename === prevCrashFile) {
|
||||
return;
|
||||
}
|
||||
prevCrashFile = filename;
|
||||
if (!filename.startsWith("cpptools")) {
|
||||
return;
|
||||
}
|
||||
// Wait 5 seconds to allow time for the crash log to finish being written.
|
||||
setTimeout(() => {
|
||||
fs.readFile(path.resolve(crashDirectory, filename), 'utf8', (err, data) => {
|
||||
void handleCrashFileRead(crashDirectory, filename, err, data);
|
||||
});
|
||||
}, 5000);
|
||||
});
|
||||
} catch (e) {
|
||||
// The file watcher limit is hit (may not be possible on Mac, but just in case).
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let previousCrashData: string;
|
||||
let previousCrashCount: number = 0;
|
||||
|
||||
function logCrashTelemetry(data: string, type: string, offsetData?: string): void {
|
||||
const crashObject: Record<string, string> = {};
|
||||
const crashCountObject: Record<string, number> = {};
|
||||
crashObject.CrashingThreadCallStack = data;
|
||||
previousMacCrashCount = data === previousMacCrashData ? previousMacCrashCount + 1 : 0;
|
||||
previousMacCrashData = data;
|
||||
crashCountObject.CrashCount = previousMacCrashCount;
|
||||
telemetry.logLanguageServerEvent("MacCrash", crashObject, crashCountObject);
|
||||
if (offsetData !== undefined) {
|
||||
crashObject.CrashingThreadCallStackOffsets = offsetData;
|
||||
}
|
||||
previousCrashCount = data === previousCrashData ? previousCrashCount + 1 : 0;
|
||||
previousCrashData = data;
|
||||
crashCountObject.CrashCount = previousCrashCount + 1;
|
||||
telemetry.logLanguageServerEvent(type, crashObject, crashCountObject);
|
||||
}
|
||||
|
||||
function logMacCrashTelemetry(data: string): void {
|
||||
logCrashTelemetry(data, "MacCrash");
|
||||
}
|
||||
|
||||
function logCppCrashTelemetry(data: string, offsetData?: string): void {
|
||||
logCrashTelemetry(data, "CppCrash", offsetData);
|
||||
}
|
||||
|
||||
function handleMacCrashFileRead(err: NodeJS.ErrnoException | undefined | null, data: string): void {
|
||||
@@ -1105,6 +1113,93 @@ function handleMacCrashFileRead(err: NodeJS.ErrnoException | undefined | null, d
|
||||
logMacCrashTelemetry(data);
|
||||
}
|
||||
|
||||
async function handleCrashFileRead(crashDirectory: string, crashFile: string, err: NodeJS.ErrnoException | undefined | null, data: string): Promise<void> {
|
||||
if (err) {
|
||||
if (err.code === "ENOENT") {
|
||||
return; // ignore known issue
|
||||
}
|
||||
return logCppCrashTelemetry("readFile: " + err.code);
|
||||
}
|
||||
|
||||
const lines: string[] = data.split("\n");
|
||||
let addressData: string = ".\n.";
|
||||
data = crashFile + "\n";
|
||||
const filtPath: string | null = which.sync("c++filt", { nothrow: true });
|
||||
const isMac: boolean = process.platform === "darwin";
|
||||
const startStr: string = isMac ? " _" : "(";
|
||||
const offsetStr: string = isMac ? " + " : "+0x";
|
||||
const endOffsetStr: string = isMac ? " " : ")";
|
||||
const dotStr: string = "…";
|
||||
data += lines[0]; // signal type
|
||||
for (let lineNum: number = 2; lineNum < lines.length - 3; ++lineNum) { // skip first/last lines
|
||||
if (lineNum > 1) {
|
||||
data += "\n";
|
||||
addressData += "\n";
|
||||
}
|
||||
const line: string = lines[lineNum];
|
||||
const startPos: number = line.indexOf(startStr);
|
||||
if (startPos === -1 || line[startPos + 1] === "+") {
|
||||
data += dotStr;
|
||||
const startAddressPos: number = line.indexOf("0x");
|
||||
const endAddressPos: number = line.indexOf(endOffsetStr, startAddressPos + 2);
|
||||
if (startAddressPos === -1 || endAddressPos === -1 || startAddressPos >= endAddressPos) {
|
||||
addressData += "Unexpected offset";
|
||||
} else {
|
||||
addressData += line.substring(startAddressPos, endAddressPos);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const offsetPos: number = line.indexOf(offsetStr, startPos + startStr.length);
|
||||
if (offsetPos === -1) {
|
||||
data += "Missing offsetStr";
|
||||
continue; // unexpected
|
||||
}
|
||||
const startPos2: number = startPos + 1;
|
||||
let funcStr: string = line.substring(startPos2, offsetPos);
|
||||
if (filtPath) {
|
||||
const ret: util.ProcessReturnType | undefined = await util.spawnChildProcess(filtPath, [funcStr], undefined, true).catch(logAndReturn.undefined);
|
||||
if (ret !== undefined) {
|
||||
funcStr = ret.output;
|
||||
funcStr = funcStr.replace(/std::(?:__1|__cxx11)/g, "std"); // simplify std namespaces.
|
||||
funcStr = funcStr.replace(/std::basic_/g, "std::");
|
||||
funcStr = funcStr.replace(/ >/g, ">");
|
||||
funcStr = funcStr.replace(/, std::(?:allocator|char_traits)<char>/g, "");
|
||||
funcStr = funcStr.replace(/<char>/g, "");
|
||||
funcStr = funcStr.replace(/, std::allocator<std::string>/g, "");
|
||||
}
|
||||
}
|
||||
data += funcStr + offsetStr;
|
||||
const offsetPos2: number = offsetPos + offsetStr.length;
|
||||
if (isMac) {
|
||||
data += line.substring(offsetPos2);
|
||||
const startAddressPos: number = line.indexOf("0x");
|
||||
if (startAddressPos === -1 || startAddressPos >= startPos) {
|
||||
// unexpected
|
||||
data += "<Missing 0x>";
|
||||
continue;
|
||||
}
|
||||
addressData += `${line.substring(startAddressPos, startPos)}`;
|
||||
} else {
|
||||
const endPos: number = line.indexOf(")", offsetPos2);
|
||||
if (endPos === -1) {
|
||||
data += "<Missing )>";
|
||||
continue; // unexpected
|
||||
}
|
||||
data += line.substring(offsetPos2, endPos);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.length > 8192) { // The API has an 8k limit.
|
||||
data = data.substring(0, 8191) + "…";
|
||||
}
|
||||
|
||||
console.log(`Crash call stack:\n${data}`);
|
||||
logCppCrashTelemetry(data, addressData);
|
||||
|
||||
await util.deleteFile(path.resolve(crashDirectory, crashFile)).catch(logAndReturn.undefined);
|
||||
void util.deleteDirectory(crashDirectory).catch(logAndReturn.undefined);
|
||||
}
|
||||
|
||||
export function deactivate(): Thenable<void> {
|
||||
clients.timeTelemetryCollector.clear();
|
||||
console.log("deactivating extension");
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import { Middleware } from 'vscode-languageclient';
|
||||
import * as util from '../common';
|
||||
import { Client } from './client';
|
||||
import { clients, onDidChangeActiveTextEditor, processDelayedDidOpen } from './extension';
|
||||
import { clients } from './extension';
|
||||
import { shouldChangeFromCToCpp } from './utils';
|
||||
|
||||
let anyFileOpened: boolean = false;
|
||||
|
||||
export function createProtocolFilter(): Middleware {
|
||||
// Disabling lint for invoke handlers
|
||||
@@ -18,29 +22,42 @@ export function createProtocolFilter(): Middleware {
|
||||
const invoke4 = (a: any, b: any, c: any, d: any, next: (a: any, b: any, c: any, d: any) => any): any => clients.ActiveClient.enqueue(() => next(a, b, c, d));
|
||||
|
||||
return {
|
||||
didOpen: async (document, _sendMessage) => {
|
||||
if (util.isCpp(document)) {
|
||||
util.setWorkspaceIsCpp();
|
||||
didOpen: async (document, sendMessage) => clients.ActiveClient.enqueue(async () => {
|
||||
if (!util.isCpp(document)) {
|
||||
return;
|
||||
}
|
||||
const editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.find(e => e.document === document);
|
||||
if (editor) {
|
||||
// If the file was visible editor when we were activated, we will not get a call to
|
||||
// onDidChangeVisibleTextEditors, so immediately open any file that is visible when we receive didOpen.
|
||||
// Otherwise, we defer opening the file until it's actually visible.
|
||||
await clients.ActiveClient.enqueue(() => processDelayedDidOpen(document));
|
||||
if (editor && editor === vscode.window.activeTextEditor) {
|
||||
onDidChangeActiveTextEditor(editor);
|
||||
util.setWorkspaceIsCpp();
|
||||
const client: Client = clients.getClientFor(document.uri);
|
||||
if (clients.checkOwnership(client, document)) {
|
||||
const uriString: string = document.uri.toString();
|
||||
if (!client.TrackedDocuments.has(uriString)) {
|
||||
client.TrackedDocuments.set(uriString, document);
|
||||
// Work around vscode treating ".C" or ".H" as c, by adding this file name to file associations as cpp
|
||||
if (document.languageId === "c" && shouldChangeFromCToCpp(document)) {
|
||||
const baseFileName: string = path.basename(document.fileName);
|
||||
const mappingString: string = baseFileName + "@" + document.fileName;
|
||||
client.addFileAssociations(mappingString, "cpp");
|
||||
client.sendDidChangeSettings();
|
||||
document = await vscode.languages.setTextDocumentLanguage(document, "cpp");
|
||||
}
|
||||
await client.provideCustomConfiguration(document.uri, undefined);
|
||||
// client.takeOwnership() will call client.TrackedDocuments.add() again, but that's ok. It's a Set.
|
||||
client.onDidOpenTextDocument(document);
|
||||
client.takeOwnership(document);
|
||||
await sendMessage(document);
|
||||
|
||||
// For a file already open when we activate, sometimes we don't get any notifications about visible
|
||||
// or active text editors, visible ranges, or text selection. As a workaround, we trigger
|
||||
// onDidChangeVisibleTextEditors here, only for the first file opened.
|
||||
if (!anyFileOpened)
|
||||
{
|
||||
anyFileOpened = true;
|
||||
const cppEditors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => util.isCpp(e.document));
|
||||
await client.onDidChangeVisibleTextEditors(cppEditors);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// NO-OP
|
||||
// If the file is not opened into an editor (such as in response for a control-hover),
|
||||
// we do not actually load a translation unit for it. When we receive a didOpen, the file
|
||||
// may not yet be visible. So, we defer creation of the translation until we receive a
|
||||
// call to onDidChangeVisibleTextEditors(), in extension.ts. A file is only loaded when
|
||||
// it is actually opened in the editor (not in response to control-hover, which sends a
|
||||
// didOpen), and first becomes visible.
|
||||
}
|
||||
},
|
||||
}),
|
||||
didChange: async (textDocumentChangeEvent, sendMessage) => clients.ActiveClient.enqueue(async () => {
|
||||
const me: Client = clients.getClientFor(textDocumentChangeEvent.document.uri);
|
||||
me.onDidChangeTextDocument(textDocumentChangeEvent);
|
||||
@@ -52,7 +69,7 @@ export function createProtocolFilter(): Middleware {
|
||||
// Don't use awaitUntilLanguageClientReady.
|
||||
// Otherwise, the message can be delayed too long.
|
||||
const me: Client = clients.getClientFor(event.document.uri);
|
||||
if (me.TrackedDocuments.has(event.document)) {
|
||||
if (me.TrackedDocuments.has(event.document.uri.toString())) {
|
||||
return sendMessage(event);
|
||||
}
|
||||
return [];
|
||||
@@ -60,9 +77,10 @@ export function createProtocolFilter(): Middleware {
|
||||
didSave: invoke1,
|
||||
didClose: async (document, sendMessage) => clients.ActiveClient.enqueue(async () => {
|
||||
const me: Client = clients.getClientFor(document.uri);
|
||||
if (me.TrackedDocuments.has(document)) {
|
||||
const uriString: string = document.uri.toString();
|
||||
if (me.TrackedDocuments.has(uriString)) {
|
||||
me.onDidCloseTextDocument(document);
|
||||
me.TrackedDocuments.delete(document);
|
||||
me.TrackedDocuments.delete(uriString);
|
||||
await sendMessage(document);
|
||||
}
|
||||
}),
|
||||
@@ -70,7 +88,7 @@ export function createProtocolFilter(): Middleware {
|
||||
resolveCompletionItem: invoke2,
|
||||
provideHover: async (document, position, token, next: (document: any, position: any, token: any) => any) => clients.ActiveClient.enqueue(async () => {
|
||||
const me: Client = clients.getClientFor(document.uri);
|
||||
if (me.TrackedDocuments.has(document)) {
|
||||
if (me.TrackedDocuments.has(document.uri.toString())) {
|
||||
return next(document, position, token);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -368,6 +368,10 @@ export class ReferencesManager {
|
||||
this.referencesProgressBarStartTime = Date.now();
|
||||
this.clearViews();
|
||||
|
||||
if (this.referencesDelayProgress) {
|
||||
clearInterval(this.referencesDelayProgress);
|
||||
}
|
||||
|
||||
this.referencesDelayProgress = setInterval(() => {
|
||||
const progressTitle: string = referencesCommandModeToString(this.client.ReferencesCommandMode);
|
||||
this.referencesProgressOptions = { location: vscode.ProgressLocation.Notification, title: progressTitle, cancellable: true };
|
||||
|
||||
@@ -10,6 +10,7 @@ import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
import { quote } from 'shell-quote';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as which from 'which';
|
||||
@@ -278,7 +279,7 @@ export class CppSettings extends Settings {
|
||||
let clangVersion: string;
|
||||
try {
|
||||
const exePath: string = getExtensionFilePath(`./LLVM/bin/${clangName}`);
|
||||
const output: string[] = execSync(`${exePath} --version`).toString().split(" ");
|
||||
const output: string[] = execSync(quote([exePath, '--version'])).toString().split(" ");
|
||||
if (output.length < 3 || output[0] !== clangStr || output[1] !== "version" || !semver.valid(output[2])) {
|
||||
if (output.length === 3) {
|
||||
return path;
|
||||
|
||||
@@ -495,7 +495,7 @@ export class LanguageStatusUI {
|
||||
}
|
||||
}
|
||||
|
||||
public activeDocumentChanged(): void {
|
||||
public didChangeActiveEditor(): void {
|
||||
const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
|
||||
if (!activeEditor) {
|
||||
this.ShowConfiguration = false;
|
||||
|
||||
@@ -10,7 +10,7 @@ import { SessionState } from '../sessionState';
|
||||
import { Location, TextEdit } from './commonTypes';
|
||||
import { CppSettings } from './settings';
|
||||
|
||||
export function makeCpptoolsRange(vscRange: vscode.Range): Range {
|
||||
export function makeLspRange(vscRange: vscode.Range): Range {
|
||||
return {
|
||||
start: { line: vscRange.start.line, character: vscRange.start.character },
|
||||
end: { line: vscRange.end.line, character: vscRange.end.character }
|
||||
|
||||
@@ -12,7 +12,7 @@ import { isIdentifierPart, isIdentifierStart } from './characterCodes';
|
||||
|
||||
/** simple dynamic tagged literal implementation */
|
||||
export function taggedLiteral(templateString: string, templateVars: Record<string, any>): string {
|
||||
return safeEval(`\`${templateString.replace('\\', '\\\\').replace(/`/, '\`')}\`;`, templateVars) as string;
|
||||
return safeEval(`\`${templateString.replace(/\\/g, '\\\\').replace(/`/g, '\`')}\`;`, templateVars) as string;
|
||||
}
|
||||
|
||||
function parseTaggedLiteral(templateString: string) {
|
||||
|
||||
+112
-26
@@ -11,7 +11,7 @@ import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as tmp from 'tmp';
|
||||
import * as vscode from 'vscode';
|
||||
import { DocumentFilter } from 'vscode-languageclient';
|
||||
import { DocumentFilter, Range } from 'vscode-languageclient';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { TargetPopulation } from 'vscode-tas-client';
|
||||
import * as which from "which";
|
||||
@@ -677,6 +677,22 @@ export function deleteFile(filePath: string): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteDirectory(directoryPath: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (fs.existsSync(directoryPath)) {
|
||||
fs.rmdir(directoryPath, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getReadmeMessage(): string {
|
||||
const readmePath: string = getExtensionFilePath("README.md");
|
||||
const readmeMessage: string = localize("refer.read.me", "Please refer to {0} for troubleshooting information. Issues can be created at {1}", readmePath, "https://github.com/Microsoft/vscode-cpptools/issues");
|
||||
@@ -736,14 +752,16 @@ export interface ProcessReturnType {
|
||||
output: string;
|
||||
}
|
||||
|
||||
export async function spawnChildProcess(program: string, args: string[] = [], continueOn?: string, cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
|
||||
export async function spawnChildProcess(program: string, args: string[] = [], continueOn?: string, skipLogging?: boolean, cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
|
||||
// Do not use CppSettings to avoid circular require()
|
||||
const settings: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", null);
|
||||
const loggingLevel: string | undefined = settings.get<string>("loggingLevel");
|
||||
if (loggingLevel === "Information" || loggingLevel === "Debug") {
|
||||
getOutputChannelLogger().appendLine(`$ ${program} ${args.join(' ')}`);
|
||||
if (skipLogging === undefined || !skipLogging) {
|
||||
const settings: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", null);
|
||||
const loggingLevel: string | undefined = settings.get<string>("loggingLevel");
|
||||
if (loggingLevel === "Information" || loggingLevel === "Debug") {
|
||||
getOutputChannelLogger().appendLine(`$ ${program} ${args.join(' ')}`);
|
||||
}
|
||||
}
|
||||
const programOutput: ProcessOutput = await spawnChildProcessImpl(program, args, continueOn, cancellationToken);
|
||||
const programOutput: ProcessOutput = await spawnChildProcessImpl(program, args, continueOn, skipLogging, cancellationToken);
|
||||
const exitCode: number | NodeJS.Signals | undefined = programOutput.exitCode;
|
||||
if (programOutput.exitCode) {
|
||||
return { succeeded: false, exitCode, output: programOutput.stderr || programOutput.stdout || localize('process.exited', 'Process exited with code {0}', exitCode) };
|
||||
@@ -765,12 +783,12 @@ interface ProcessOutput {
|
||||
stderr: string;
|
||||
}
|
||||
|
||||
async function spawnChildProcessImpl(program: string, args: string[], continueOn?: string, cancellationToken?: vscode.CancellationToken): Promise<ProcessOutput> {
|
||||
async function spawnChildProcessImpl(program: string, args: string[], continueOn?: string, skipLogging?: boolean, cancellationToken?: vscode.CancellationToken): Promise<ProcessOutput> {
|
||||
const result = new ManualPromise<ProcessOutput>();
|
||||
|
||||
// Do not use CppSettings to avoid circular require()
|
||||
const settings: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", null);
|
||||
const loggingLevel: string | undefined = settings.get<string>("loggingLevel");
|
||||
const loggingLevel: string | undefined = (skipLogging === undefined || !skipLogging) ? settings.get<string>("loggingLevel") : "None";
|
||||
|
||||
let proc: child_process.ChildProcess;
|
||||
if (await isExecutable(program)) {
|
||||
@@ -1360,25 +1378,55 @@ export function sequentialResolve<T>(items: T[], promiseBuilder: (item: T) => Pr
|
||||
}, Promise.resolve());
|
||||
}
|
||||
|
||||
export function normalizeArg(arg: string): string {
|
||||
arg = arg.trim();
|
||||
// Check if the arg is enclosed in backtick,
|
||||
// or includes unescaped double-quotes (or single-quotes on Windows),
|
||||
// or includes unescaped single-quotes on mac and linux.
|
||||
if (/^`.*`$/g.test(arg) || /.*[^\\]".*/g.test(arg) ||
|
||||
(process.platform.includes("win") && /.*[^\\]'.*/g.test(arg)) ||
|
||||
(!process.platform.includes("win") && /.*[^\\]'.*/g.test(arg))) {
|
||||
return arg;
|
||||
export function quoteArgument(argument: string): string {
|
||||
// Return the argument as is if it's empty
|
||||
if (!argument) {
|
||||
return argument;
|
||||
}
|
||||
// The special character double-quote is already escaped in the arg.
|
||||
const unescapedSpaces: string | undefined = arg.split('').find((char, index) => index > 0 && char === " " && arg[index - 1] !== "\\");
|
||||
if (!unescapedSpaces && !process.platform.includes("win")) {
|
||||
return arg;
|
||||
} else if (arg.includes(" ")) {
|
||||
arg = arg.replace(/\\\s/g, " ");
|
||||
return "\"" + arg + "\"";
|
||||
|
||||
if (os.platform() === "win32") {
|
||||
// Windows-style quoting logic
|
||||
if (!/[\s\t\n\v\"\\&%^]/.test(argument)) {
|
||||
return argument;
|
||||
}
|
||||
|
||||
let quotedArgument = '"';
|
||||
let backslashCount = 0;
|
||||
|
||||
for (const char of argument) {
|
||||
if (char === '\\') {
|
||||
backslashCount++;
|
||||
} else {
|
||||
if (char === '"') {
|
||||
quotedArgument += '\\'.repeat(backslashCount * 2 + 1);
|
||||
} else {
|
||||
quotedArgument += '\\'.repeat(backslashCount);
|
||||
}
|
||||
quotedArgument += char;
|
||||
backslashCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
quotedArgument += '\\'.repeat(backslashCount * 2);
|
||||
quotedArgument += '"';
|
||||
return quotedArgument;
|
||||
} else {
|
||||
return arg;
|
||||
// Unix-style quoting logic
|
||||
if (!/[\s\t\n\v\"'\\$`|;&(){}<>*?!\[\]~^#%]/.test(argument)) {
|
||||
return argument;
|
||||
}
|
||||
|
||||
let quotedArgument = "'";
|
||||
for (const c of argument) {
|
||||
if (c === "'") {
|
||||
quotedArgument += "'\\''";
|
||||
} else {
|
||||
quotedArgument += c;
|
||||
}
|
||||
}
|
||||
|
||||
quotedArgument += "'";
|
||||
return quotedArgument;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1546,3 +1594,41 @@ export function getNumericLoggingLevel(loggingLevel: string | undefined): number
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeOverlappingRanges(ranges: Range[]): Range[] {
|
||||
// Fix any reversed ranges. Not sure if this is needed, but ensures the input is sanitized.
|
||||
const mergedRanges: Range[] = ranges.map(range => {
|
||||
if (range.start.line > range.end.line || (range.start.line === range.end.line && range.start.character > range.end.character)) {
|
||||
return Range.create(range.end, range.start);
|
||||
}
|
||||
return range;
|
||||
});
|
||||
|
||||
// Merge overlapping ranges.
|
||||
mergedRanges.sort((a, b) => a.start.line - b.start.line || a.start.character - b.start.character);
|
||||
let lastMergedIndex = 0; // Index to keep track of the last merged range
|
||||
for (let currentIndex = 0; currentIndex < ranges.length; currentIndex++) {
|
||||
const currentRange = ranges[currentIndex]; // No need for a shallow copy, since we're not modifying the ranges we haven't read yet.
|
||||
let nextIndex = currentIndex + 1;
|
||||
while (nextIndex < ranges.length) {
|
||||
const nextRange = ranges[nextIndex];
|
||||
// Check for non-overlapping ranges first
|
||||
if (nextRange.start.line > currentRange.end.line ||
|
||||
(nextRange.start.line === currentRange.end.line && nextRange.start.character > currentRange.end.character)) {
|
||||
break;
|
||||
}
|
||||
// Otherwise, merge the overlapping ranges
|
||||
currentRange.end = {
|
||||
line: Math.max(currentRange.end.line, nextRange.end.line),
|
||||
character: Math.max(currentRange.end.character, nextRange.end.character)
|
||||
};
|
||||
nextIndex++;
|
||||
}
|
||||
// Overwrite the array in-place
|
||||
mergedRanges[lastMergedIndex] = currentRange;
|
||||
lastMergedIndex++;
|
||||
currentIndex = nextIndex - 1; // Skip the merged ranges
|
||||
}
|
||||
mergedRanges.length = lastMergedIndex;
|
||||
return mergedRanges;
|
||||
}
|
||||
|
||||
@@ -131,4 +131,8 @@ export class CppTools implements CppToolsTestApi {
|
||||
public getTestHook(): CppToolsTestHook {
|
||||
return test.getTestHook();
|
||||
}
|
||||
|
||||
public async getDirectlyReferencedHeaders(): Promise<string[]> {
|
||||
return LanguageServer.getActiveClient().getDirectlyReferencedHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import * as assert from "assert";
|
||||
import { suite } from 'mocha';
|
||||
import * as os from "os";
|
||||
import { delimiter } from 'path';
|
||||
import { escapeForSquiggles, normalizeArg, resolveVariables } from "../../../../src/common";
|
||||
import { escapeForSquiggles, quoteArgument, resolveVariables } from "../../../../src/common";
|
||||
|
||||
suite("resolveVariables", () => {
|
||||
const success: string = "success";
|
||||
@@ -219,39 +219,36 @@ suite("resolveVariables", () => {
|
||||
testEscapeForSquigglesScenario("\"\\\\\"", "\"\\\\\\\\\""); // quoted string containing escaped backslash
|
||||
});
|
||||
|
||||
test("normalizeArgs:", () => {
|
||||
const testNormalizeArgsScenario: any = (input: string, expectedOutput: string) => {
|
||||
const result: string = normalizeArg(input);
|
||||
test("quoteArgument:", () => {
|
||||
const testQuoteArgumentScenario: any = (input: string, expectedOutput: string) => {
|
||||
const result: string = quoteArgument(input);
|
||||
if (result !== expectedOutput) {
|
||||
throw new Error(`normalizeArgs failure: for \"${input}\", \"${result}\" !== \"${expectedOutput}\"`);
|
||||
throw new Error(`quoteArgument failure: for \"${input}\", \"${result}\" !== \"${expectedOutput}\"`);
|
||||
}
|
||||
};
|
||||
/*
|
||||
this is how the args from tasks.json will be sent to the chilprocess.spawn:
|
||||
"args":[
|
||||
"-DTEST1=TEST1 TEST1", // "-DTEST1=TEST1 TEST1"
|
||||
"-DTEST2=\"TEST2 TEST2\"", // -DTEST2="TEST2 TEST2"
|
||||
"-DTEST3=\\\"TEST3 TEST3\\\"", // "-DTEST3=\"TEST3 TEST3\""
|
||||
"-DTEST4=TEST4\\ TEST4", // "-DTEST4=TEST4 TEST4"
|
||||
"-DTEST5='TEST5 TEST5'", // -DTEST5='TEST5 TEST5'
|
||||
"-DTEST6=TEST6\\ TEST6 Test6", // "-DTEST6=TEST6 TEST6 Test6"
|
||||
]
|
||||
*/
|
||||
testNormalizeArgsScenario("-DTEST1=TEST1 TEST1", "\"-DTEST1=TEST1 TEST1\"");
|
||||
testNormalizeArgsScenario("-DTEST2=\"TEST2 TEST2\"", "-DTEST2=\"TEST2 TEST2\"");
|
||||
testNormalizeArgsScenario("-DTEST3=\\\"TEST3 TEST3\\\"", "\"-DTEST3=\\\"TEST3 TEST3\\\"\"");
|
||||
if (process.platform.includes("win")) {
|
||||
testNormalizeArgsScenario("-DTEST4=TEST4\\ TEST4", "\"-DTEST4=TEST4 TEST4\"");
|
||||
testNormalizeArgsScenario("-DTEST5=\'TEST5 TEST5\'", "-DTEST5=\'TEST5 TEST5\'");
|
||||
} else {
|
||||
testNormalizeArgsScenario("-DTEST4=TEST4\\ TEST4", "-DTEST4=TEST4\\ TEST4");
|
||||
testNormalizeArgsScenario("-DTEST5='TEST5 TEST5'", "-DTEST5='TEST5 TEST5'");
|
||||
}
|
||||
testNormalizeArgsScenario("-DTEST6=TEST6\\ TEST6 Test6", "\"-DTEST6=TEST6 TEST6 Test6\"");
|
||||
/*
|
||||
this is how the args from tasks.json will be sent to the chilprocess.spawn:
|
||||
"args":[
|
||||
"-DTEST1=TEST1 TEST1", // "-DTEST1=TEST1 TEST1"
|
||||
"-DTEST2=\"TEST2 TEST2\"", // -DTEST2="TEST2 TEST2"
|
||||
"-DTEST3=\\\"TEST3 TEST3\\\"", // "-DTEST3=\"TEST3 TEST3\""
|
||||
"-DTEST4=TEST4\\ TEST4", // "-DTEST4=TEST4 TEST4"
|
||||
"-DTEST5='TEST5 TEST5'", // -DTEST5='TEST5 TEST5'
|
||||
"-DTEST6=TEST6\\ TEST6 Test6", // "-DTEST6=TEST6 TEST6 Test6"
|
||||
]
|
||||
*/
|
||||
testQuoteArgumentScenario("-DTEST1=TEST1 TEST1", "\"-DTEST1=TEST1 TEST1\"");
|
||||
testQuoteArgumentScenario("-DTEST2=\"TEST2 TEST2\"", "\"-DTEST2=\"TEST2 TEST2\"\"");
|
||||
testQuoteArgumentScenario("-DTEST3=\\\"TEST3 TEST3\\\"", "\"-DTEST3=\\\"TEST3 TEST3\\\"\"");
|
||||
testQuoteArgumentScenario("-DTEST4=TEST4\\ TEST4", "\"-DTEST4=TEST4 TEST4\"");
|
||||
testQuoteArgumentScenario("-DTEST5=\'TEST5 TEST5\'", "-DTEST5=\'TEST5 TEST5\'");
|
||||
testQuoteArgumentScenario("-DTEST4=TEST4\\ TEST4", "-DTEST4=TEST4\\ TEST4");
|
||||
testQuoteArgumentScenario("-DTEST5='TEST5 TEST5'", "-DTEST5='TEST5 TEST5'");
|
||||
testQuoteArgumentScenario("-DTEST6=TEST6\\ TEST6 Test6", "\"-DTEST6=TEST6 TEST6 Test6\"");
|
||||
});
|
||||
|
||||
interface ResolveTestFlowEnvironment {
|
||||
withEnvironment(additionalEnvironment: {[key: string]: string | string[]}): ResolveTestFlowAssert;
|
||||
withEnvironment(additionalEnvironment: { [key: string]: string | string[] }): ResolveTestFlowAssert;
|
||||
shouldLookupSymbol(key: string): void;
|
||||
}
|
||||
interface ResolveTestFlowAssert {
|
||||
@@ -260,9 +257,9 @@ suite("resolveVariables", () => {
|
||||
|
||||
function resolveVariablesWithInput(input: string): ResolveTestFlowEnvironment {
|
||||
return {
|
||||
withEnvironment: (additionalEnvironment: {[key: string]: string | string[]}) => inputAndEnvironment(input, additionalEnvironment),
|
||||
withEnvironment: (additionalEnvironment: { [key: string]: string | string[] }) => inputAndEnvironment(input, additionalEnvironment),
|
||||
shouldLookupSymbol: (symbol: string) => {
|
||||
const environment: {[key: string]: string | string[]} = {};
|
||||
const environment: { [key: string]: string | string[] } = {};
|
||||
environment[symbol] = success;
|
||||
return inputAndEnvironment(input, environment)
|
||||
.shouldResolveTo(success);
|
||||
@@ -270,7 +267,7 @@ suite("resolveVariables", () => {
|
||||
};
|
||||
}
|
||||
|
||||
function inputAndEnvironment(input: string, additionalEnvironment: {[key: string]: string | string[]}): ResolveTestFlowAssert {
|
||||
function inputAndEnvironment(input: string, additionalEnvironment: { [key: string]: string | string[] }): ResolveTestFlowAssert {
|
||||
return {
|
||||
shouldResolveTo: (expected: string) => {
|
||||
const actual: string = resolveVariables(input, additionalEnvironment);
|
||||
|
||||
@@ -321,7 +321,7 @@ class SettingsApp {
|
||||
private showErrorWithInfo(elementID: string, errorInfo: string): void {
|
||||
this.showElement(elementID, errorInfo ? true : false);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
document.getElementById(elementID)!.innerHTML = errorInfo ? errorInfo : "";
|
||||
document.getElementById(elementID)!.textContent = errorInfo ? errorInfo : "";
|
||||
}
|
||||
|
||||
private updateConfigSelection(message: any): void {
|
||||
|
||||
+3
-3
@@ -2272,9 +2272,9 @@ flush-write-stream@^1.0.2:
|
||||
readable-stream "^2.3.6"
|
||||
|
||||
follow-redirects@^1.15.0:
|
||||
version "1.15.3"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
|
||||
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
|
||||
version "1.15.4"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf"
|
||||
integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==
|
||||
|
||||
for-each@^0.3.3:
|
||||
version "0.3.3"
|
||||
|
||||
Reference in New Issue
Block a user