├── .config └── dotnet-tools.json ├── .editorconfig ├── .gitignore ├── .gitmodules ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── GitVersion.yml ├── LICENSE ├── OSSREADME.json ├── Publish-LanguageServer.ps1 ├── README.md ├── azure-pipelines-development.yml ├── azure-pipelines-release.yml ├── azure-pipelines-template.yml ├── build └── semver.py ├── docs ├── BUILDING.md ├── architecture │ ├── diagrams.pptx │ ├── images │ │ ├── lsp-layers.png │ │ └── server-layers.png │ └── overview.md └── images │ ├── associate-language-with-file-extension.jpg │ ├── change-language-mode.jpg │ └── extension-in-action.gif ├── images └── icon.png ├── language-configuration.json ├── package-lock.json ├── package.json ├── snippets └── msbuild-project.json ├── src ├── dotnet.ts ├── extension.ts ├── internal-commands.ts └── notifications.ts ├── syntaxes ├── msbuild.expression.json └── msbuild.json ├── tsconfig.json ├── tslint.json └── webpack.config.js /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "gitversion.tool": { 6 | "version": "6.3.0", 7 | "commands": [ 8 | "dotnet-gitversion" 9 | ], 10 | "rollForward": false 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = false 9 | insert_final_newline = true 10 | 11 | [*.yml] 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Mac 2 | .DS_Store 3 | 4 | ## Extension 5 | out/ 6 | dist/ 7 | 8 | # Language server build 9 | language-server/ 10 | 11 | ## NodeJS 12 | node_modules/ 13 | 14 | ## Visual Studio 15 | 16 | # User-specific files 17 | *.suo 18 | *.user 19 | *.userosscache 20 | *.sln.docstates 21 | 22 | # User-specific files (MonoDevelop/Xamarin Studio) 23 | *.userprefs 24 | 25 | # Build results 26 | [Dd]ebug/ 27 | [Dd]ebugPublic/ 28 | [Rr]elease/ 29 | [Rr]eleases/ 30 | x64/ 31 | x86/ 32 | bld/ 33 | [Bb]in/ 34 | [Oo]bj/ 35 | [Ll]og/ 36 | 37 | # Visual Studio 2015 cache/options directory 38 | .vs/ 39 | # Uncomment if you have tasks that create the project's static files in wwwroot 40 | #wwwroot/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUNIT 47 | *.VisualState.xml 48 | TestResult.xml 49 | 50 | # Build Results of an ATL Project 51 | [Dd]ebugPS/ 52 | [Rr]eleasePS/ 53 | dlldata.c 54 | 55 | # Benchmark Results 56 | BenchmarkDotNet.Artifacts/ 57 | 58 | # .NET Core 59 | project.lock.json 60 | project.fragment.lock.json 61 | artifacts/ 62 | **/Properties/launchSettings.json 63 | 64 | *_i.c 65 | *_p.c 66 | *_i.h 67 | *.ilk 68 | *.meta 69 | *.obj 70 | *.pch 71 | *.pdb 72 | *.pgc 73 | *.pgd 74 | *.rsp 75 | *.sbr 76 | *.tlb 77 | *.tli 78 | *.tlh 79 | *.tmp 80 | *.tmp_proj 81 | *.log 82 | *.vspscc 83 | *.vssscc 84 | .builds 85 | *.pidb 86 | *.svclog 87 | *.scc 88 | 89 | # Chutzpah Test files 90 | _Chutzpah* 91 | 92 | # Visual C++ cache files 93 | ipch/ 94 | *.aps 95 | *.ncb 96 | *.opendb 97 | *.opensdf 98 | *.sdf 99 | *.cachefile 100 | *.VC.db 101 | *.VC.VC.opendb 102 | 103 | # Visual Studio profiler 104 | *.psess 105 | *.vsp 106 | *.vspx 107 | *.sap 108 | 109 | # TFS 2012 Local Workspace 110 | $tf/ 111 | 112 | # Guidance Automation Toolkit 113 | *.gpState 114 | 115 | # ReSharper is a .NET coding add-in 116 | _ReSharper*/ 117 | *.[Rr]e[Ss]harper 118 | *.DotSettings.user 119 | 120 | # JustCode is a .NET coding add-in 121 | .JustCode 122 | 123 | # TeamCity is a build add-in 124 | _TeamCity* 125 | 126 | # DotCover is a Code Coverage Tool 127 | *.dotCover 128 | 129 | # AxoCover is a Code Coverage Tool 130 | .axoCover/* 131 | !.axoCover/settings.json 132 | 133 | # Visual Studio code coverage results 134 | *.coverage 135 | *.coveragexml 136 | 137 | # NCrunch 138 | _NCrunch_* 139 | .*crunch*.local.xml 140 | nCrunchTemp_* 141 | 142 | # MightyMoose 143 | *.mm.* 144 | AutoTest.Net/ 145 | 146 | # Web workbench (sass) 147 | .sass-cache/ 148 | 149 | # Installshield output folder 150 | [Ee]xpress/ 151 | 152 | # DocProject is a documentation generator add-in 153 | DocProject/buildhelp/ 154 | DocProject/Help/*.HxT 155 | DocProject/Help/*.HxC 156 | DocProject/Help/*.hhc 157 | DocProject/Help/*.hhk 158 | DocProject/Help/*.hhp 159 | DocProject/Help/Html2 160 | DocProject/Help/html 161 | 162 | # Click-Once directory 163 | publish/ 164 | 165 | # Publish Web Output 166 | *.[Pp]ublish.xml 167 | *.azurePubxml 168 | # Note: Comment the next line if you want to checkin your web deploy settings, 169 | # but database connection strings (with potential passwords) will be unencrypted 170 | *.pubxml 171 | *.publishproj 172 | 173 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 174 | # checkin your Azure Web App publish settings, but sensitive information contained 175 | # in these scripts will be unencrypted 176 | PublishScripts/ 177 | 178 | # The packages folder can be ignored because of Package Restore 179 | **/packages/* 180 | # except build/, which is used as an MSBuild target. 181 | !**/packages/build/ 182 | # Uncomment if necessary however generally it will be regenerated when needed 183 | #!**/packages/repositories.config 184 | # NuGet v3's project.json files produces more ignorable files 185 | *.nuget.props 186 | *.nuget.targets 187 | 188 | # Microsoft Azure Build Output 189 | csx/ 190 | *.build.csdef 191 | 192 | # Microsoft Azure Emulator 193 | ecf/ 194 | rcf/ 195 | 196 | # Windows Store app package directories and files 197 | AppPackages/ 198 | BundleArtifacts/ 199 | Package.StoreAssociation.xml 200 | _pkginfo.txt 201 | *.appx 202 | 203 | # Visual Studio cache files 204 | # files ending in .cache can be ignored 205 | *.[Cc]ache 206 | # but keep track of directories ending in .cache 207 | !*.[Cc]ache/ 208 | 209 | # Others 210 | ClientBin/ 211 | ~$* 212 | *~ 213 | *.dbmdl 214 | *.dbproj.schemaview 215 | *.jfm 216 | *.pfx 217 | *.publishsettings 218 | orleans.codegen.cs 219 | 220 | # Since there are multiple workflows, uncomment next line to ignore bower_components 221 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 222 | #bower_components/ 223 | 224 | # RIA/Silverlight projects 225 | Generated_Code/ 226 | 227 | # Backup & report files from converting an old project file 228 | # to a newer Visual Studio version. Backup files are not needed, 229 | # because we have git ;-) 230 | _UpgradeReport_Files/ 231 | Backup*/ 232 | UpgradeLog*.XML 233 | UpgradeLog*.htm 234 | 235 | # SQL Server files 236 | *.mdf 237 | *.ldf 238 | *.ndf 239 | 240 | # Business Intelligence projects 241 | *.rdl.data 242 | *.bim.layout 243 | *.bim_*.settings 244 | 245 | # Microsoft Fakes 246 | FakesAssemblies/ 247 | 248 | # GhostDoc plugin setting file 249 | *.GhostDoc.xml 250 | 251 | # Node.js Tools for Visual Studio 252 | .ntvs_analysis.dat 253 | node_modules/ 254 | 255 | # Typescript v1 declaration files 256 | typings/ 257 | 258 | # Visual Studio 6 build log 259 | *.plg 260 | 261 | # Visual Studio 6 workspace options file 262 | *.opt 263 | 264 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 265 | *.vbw 266 | 267 | # Visual Studio LightSwitch build output 268 | **/*.HTMLClient/GeneratedArtifacts 269 | **/*.DesktopClient/GeneratedArtifacts 270 | **/*.DesktopClient/ModelManifest.xml 271 | **/*.Server/GeneratedArtifacts 272 | **/*.Server/ModelManifest.xml 273 | _Pvt_Extensions 274 | 275 | # Paket dependency manager 276 | .paket/paket.exe 277 | paket-files/ 278 | 279 | # FAKE - F# Make 280 | .fake/ 281 | 282 | # JetBrains Rider 283 | .idea/ 284 | *.sln.iml 285 | 286 | # CodeRush 287 | .cr/ 288 | 289 | # Python Tools for Visual Studio (PTVS) 290 | __pycache__/ 291 | *.pyc 292 | 293 | # Cake - Uncomment if you are using it 294 | # tools/** 295 | # !tools/packages.config 296 | 297 | # Tabs Studio 298 | *.tss 299 | 300 | # Telerik's JustMock configuration file 301 | *.jmconfig 302 | 303 | # BizTalk build output 304 | *.btp.cs 305 | *.btm.cs 306 | *.odx.cs 307 | *.xsd.cs 308 | 309 | # VS Code 310 | .vscode/* 311 | !.vscode/settings.json 312 | !.vscode/tasks.json 313 | !.vscode/launch.json 314 | !.vscode/extensions.json 315 | 316 | # Published extension packages 317 | *.vsix 318 | 319 | # Don't ignore library packages 320 | !lib/packages/ 321 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/server"] 2 | path = lib/server 3 | url = https://github.com/tintoy/msbuild-project-tools-server.git 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"], 10 | "smartStep": true, 11 | "sourceMaps": true, 12 | "outFiles": [ "${workspaceRoot}/dist/**/*.js" ], 13 | "preLaunchTask": "prepare-dev" 14 | }, 15 | { 16 | "name": "Attach to LSP process", 17 | "type": "coreclr", 18 | "request": "attach", 19 | "processId": "${command:pickProcess}" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.exclude": { 3 | "out": true, 4 | "dist": true 5 | }, 6 | "yaml.schemas": { 7 | "https://raw.githubusercontent.com/microsoft/azure-pipelines-vscode/master/service-schema.json": "file:///d%3A/Development/github/tintoy/msbuild-project-tools-vscode/azure-pipelines-template.yml" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${fileBasename}: the current opened file's basename 5 | // ${fileDirname}: the current opened file's dirname 6 | // ${fileExtname}: the current opened file's extension 7 | // ${cwd}: the current working directory of the spawned process 8 | 9 | { 10 | "version": "2.0.0", 11 | "tasks": [ 12 | { 13 | "label": "prepare-dev", 14 | "type": "shell", 15 | "command": "npm", 16 | "args": [ 17 | "run", 18 | "build-dev", 19 | "--loglevel", 20 | "silent" 21 | ], 22 | "isBackground": true, 23 | "problemMatcher": "$tsc-watch", 24 | "group": { 25 | "kind": "build", 26 | "isDefault": false 27 | }, 28 | "dependsOn": [ 29 | "Publish-LS-dev" 30 | ], 31 | "dependsOrder": "sequence" 32 | }, 33 | { 34 | "label": "Publish-LS-dev", 35 | "type": "shell", 36 | "command": "powershell", 37 | "args": [ 38 | "${workspaceRoot}/Publish-LanguageServer.ps1", 39 | "--dev" 40 | ] 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | .vs/** 4 | language-server/MSBuildProjectTools.*.xml 5 | out/** 6 | data/** 7 | docs/** 8 | lib/** 9 | test/** 10 | src/** 11 | **/*.map 12 | .gitignore 13 | .editorconfig 14 | .gitmodules 15 | tsconfig.json 16 | tslint.json 17 | *.ps1 18 | *.sh 19 | **/*.exe 20 | **/*.pdb 21 | webpack.config.js 22 | node_modules/** 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | # v0.6.6 4 | 5 | * Correctly handle VSCode/LSP completion behaviour where the last character typed is implicitly part of the current selection, if it triggered completion (tintoy/msbuild-project-tools-vscode#93). 6 | 7 | # v0.6.5 8 | 9 | * Extension now more aggressively forces local user runtime and picks up the latest runtime version available (including previews). This makes it possible to use the extension with preview versions of .NET/MSBuild (tintoy/msbuild-project-tools-vscode#152). 10 | * Language server now validates `dotnet` host process exit code and can provide more diagnostic information in some scenarios, such as when the SDK configured in `global.json` is not available (tintoy/msbuild-project-tools-server#106). 11 | 12 | # v0.6.4 13 | 14 | * Update intellisense help content for Content items and CopyToXXXDirectory global item metadata (tintoy/msbuild-project-tools-vscode#148). 15 | 16 | # v0.6.3 17 | 18 | * Improve handling of concurrent loads for MSBuild sub-projects (fixes tintoy/msbuild-project-tools-server#100). 19 | 20 | # v0.6.0 21 | 22 | * Extension startup time has been improved. 23 | * The MSBuild language server now runs on .NET 8. 24 | * Isolated runtime for the language server is back, so users no longer need to have a specific version of .NET to be installed to be able to use the extension. 25 | 26 | # v0.5.3 27 | 28 | * Improve file-access behaviour for project.assets.json (tintoy/msbuild-project-tools-server#82). 29 | 30 | # v0.5.2 31 | 32 | * Revert usage of the `vscode-dotnet-runtime` extension (tintoy/msbuild-project-tools-vscode#89 - isolated runtimes are problematic if you want to use global SDKs). 33 | 34 | # v0.5.1 35 | 36 | * Temporary roll-back from v0.5.0 to v0.4.5 (tintoy/msbuild-project-tools-vscode#137). 37 | 38 | # v0.5.0 39 | 40 | * The .NET runtime, required to run the language server, is now acquired using the `vscode-dotnet-runtime` extension as a dependency (so you don't need to have that specific version of .NET on your machine installed to use the extension) 41 | * All completion are now shown by default 42 | * Basic integration with `redhat.vscode-xml` extension is now provided, so you can use its features for `msbuild` language 43 | * Language server, packed with the extension, is now built in release mode 44 | * Extension bundle size has been significantly optimized 45 | * Dependencies have been updaded to address known security vulnerabilities 46 | 47 | # v0.4.9 48 | 49 | * Wait for redirected STDOUT/STDERR streams to complete when launching dotnet host process (tintoy/msbuild-project-tools-vscode#105, tintoy/msbuild-project-tools-server#28). 50 | Thanks, @tillig! 51 | 52 | # v0.4.8 53 | 54 | * Selectively enable COREHOST_TRACE when launching dotnet executable to probe .NET SDKs (tintoy/msbuild-project-tools-vscode#105, tintoy/msbuild-project-tools-server#28). 55 | 56 | # v0.4.7 57 | 58 | * Enable logging from .NET / MSBuild-engine discovery logic during language-server startup (tintoy/msbuild-project-tools-server#28). 59 | 60 | # v0.4.6 61 | 62 | * Simplify logic for detecting .NET host version (tintoy/msbuild-project-tools-vscode#99). 63 | 64 | # v0.4.5 65 | 66 | * Improve parsing of output from `dotnet --info` (tintoy/msbuild-project-tools-vscode#98). 67 | 68 | # v0.4.4 69 | 70 | * Always roll forward to the latest (stable) installed version of the runtime (tintoy/msbuild-project-tools-vscode#90). 71 | * Mark extension as a workspace extension to enable correct behaviour in remote scenarios (tintoy/msbuild-project-tools-vscode#99). 72 | 73 | # 0.4.3 74 | 75 | * Fix ArgumentNullException from NuGet client library when requesting package version completions (tintoy/msbuild-project-tools-vscode#91). 76 | 77 | # 0.4.0 78 | 79 | * Support for manually ignoring configured package sources using the `msbuildProjectTools.nuget.ignorePackageSources` extension setting (tintoy/msbuild-project-tools-server#24). 80 | * Support for automatically ignoring configured package sources when the v3 service index indicates that they don't support the NuGet completion API (tintoy/msbuild-project-tools-server#24). 81 | 82 | ## 0.3.16 83 | 84 | * Add support for additional wel-known metadata of ProjectReference items (tintoy/msbuild-project-tools-server#26). 85 | 86 | ## 0.3.15 87 | 88 | * Improve error handling when project assets file was not found while updating NuGet package references for a project (tintoy/msbuild-project-tools-server#24). 89 | 90 | ## 0.3.14 91 | 92 | * Correctly handle preview versions of the .NET SDK when discovering MSBuild instances (tintoy/msbuild-project-tools-vscode#74). 93 | 94 | ## 0.3.13 95 | 96 | * Improve detection logic for .NET host version during extension startup (tintoy/msbuild-project-tools-vscode#73). 97 | 98 | ## 0.3.12 99 | 100 | * Remove legacy ("classic") completion provider. 101 | * Fix MSBuild-dependent tests that break in CI when the 5.0 SDK is also installed (tintoy/msbuild-project-tools-server#20). 102 | * Upgrade language server to target .NET 5.0 (tintoy/msbuild-project-tools-server#22). 103 | 104 | ## 0.3.11 105 | 106 | * Upgrade the language service to use the .NET Core 3.1 runtime (tintoy/msbuild-project-tools-server#20). 107 | 108 | ## 0.3.10 109 | 110 | * Always use the MSBuild engine from the newest version of the .NET Core SDK (tintoy/msbuild-project-tools-server#19). 111 | 112 | ## v0.3.8 113 | 114 | * Completions now correctly replace trigger characters, if any (tintoy/msbuild-project-tools-vscode#67). 115 | 116 | ## v0.3.7 117 | 118 | * Explicitly watch parent process for termination (tintoy/msbuild-project-tools-vscode#53). 119 | 120 | ## v0.3.6 121 | 122 | * Update MSBuild engine packages to v16.5.0 (tintoy/msbuild-project-tools-vscode#66). 123 | 124 | ## v0.3.4 125 | 126 | * Add IntelliSense for `GenerateDocumentationFile` property (tintoy/msbuild-project-tools-vscode#60). 127 | 128 | ## v0.3.3 129 | 130 | * Use v16.4.0 of the MSBuild engine (tintoy/msbuild-project-tools-vscode#59). 131 | 132 | ## v0.3.2 133 | 134 | * Improved error reporting when language service cannot be started (tintoy/msbuild-project-tools-server#17). 135 | 136 | ## v0.3.1 137 | 138 | * Language service now targets .NET Core 3.0 (tintoy/msbuild-project-tools-server#17). 139 | 140 | ## v0.2.55 141 | 142 | * Improve calculation logic for MSBuild ToolsVersion (tintoy/msbuild-project-tools-server#16). 143 | 144 | ## v0.2.54 145 | 146 | * Use tab-character in completion text (tintoy/msbuild-project-tools-server#13). 147 | 148 | ## v0.2.53 149 | 150 | * Upgrade MSBuild packages to v15.9.20 (tintoy/msbuild-project-tools-server#14). 151 | 152 | ## v0.2.52 153 | 154 | * Add UserSecretsId to well-known properties (tintoy/msbuild-project-tools-vscode#48). 155 | 156 | ## v0.2.51 157 | 158 | * Use correct MSBuild SDK folder for .NET Core 3.0 and newer (tintoy/msbuild-project-tools-vscode#46). 159 | 160 | ## v0.2.50 161 | 162 | * Enable per-workspace override of `MSBuildExtensionsPath` and `MSBuildExtensionsPath32` (tintoy/msbuild-project-tools-vscode#35). 163 | 164 | ## v0.2.49 165 | 166 | * Prevent "dotnet --info" hanging when its output is larger than the process STDOUT buffer (tintoy/msbuild-project-tools-vscode#42). 167 | 168 | ## v0.2.47 169 | 170 | * Improvements to logging during startup (tintoy/msbuild-project-tools-vscode#42). 171 | 172 | ## v0.2.46 173 | 174 | * Log configured package sources when initialising a project document (tintoy/msbuild-project-tools-vscode#44). 175 | 176 | ## v0.2.45 177 | 178 | * Handle localised output from `dotnet --info` (tintoy/msbuild-project-tools-vscode#43). 179 | 180 | ## v0.2.44 181 | 182 | * Fix bug in parsing of extension settings. 183 | 184 | ## v0.2.43 185 | 186 | * Optionally provide suggestions for packages from local (file-based) package sources (tintoy/msbuild-project-tools-server#9). 187 | 188 | ## v0.2.42 189 | 190 | * Initial support for flattened (path-based) extension settings (tintoy/msbuild-project-tools-server#7). 191 | * Start removing file-system hyperlinks from hover tooltips, since VS Code no longer renders them correctly. 192 | 193 | ## v0.2.41 194 | 195 | * Use latest stable version of `NuGet.Configuration` to add support for encrypted credentials in `NuGet.config` (tintoy/msbuild-project-tools-vscode#39). 196 | 197 | ## v0.2.39 198 | 199 | * Further improvements to log output (especially for project-load failures; exceptions from invalid project XML are only logged when configured log level is Debug or Verbose). 200 | 201 | ## v0.2.38 202 | 203 | * Reduce size of VSIX package (tintoy/msbuild-project-tools-server#37). 204 | * Improve log output (especially for project-load failures). 205 | 206 | ## v0.2.37 207 | 208 | * Support overriding of MSBuild SDKs path via environment variable (tintoy/msbuild-project-tools-server#5). 209 | 210 | ## v0.2.36 211 | 212 | * Bug-fix: ArgumentException (parameter name: itemType) when requesting completions on root `Project` element (tintoy/msbuild-project-tools-server#5). 213 | 214 | ## v0.2.35 215 | 216 | * Produce cleaner stack-traces using Demystifier. 217 | 218 | ## v0.2.34 219 | 220 | * Display help and documentation links for well-known MSBuild XML elements (tintoy/msbuild-project-tools-server#5). 221 | 222 | ## v0.2.33 223 | 224 | * Correctly handle parsing of MSBuild expressions where the root expression is an unquoted string (i.e. composite expression including one or more string-literal text sequences). 225 | 226 | ## v0.2.32 227 | 228 | * Expression support is no longer experimental! 229 | 230 | ## v0.2.31 231 | 232 | * Ensure package Ids and version appear before other completion types in `PackageReference` elements / attributes. 233 | 234 | ## v0.2.30 235 | 236 | * Add completion for `IsPackable` property. 237 | 238 | ## v0.2.29 239 | 240 | * Bug-fix: Language server process fails to terminate correctly on Linux (tintoy/msbuild-project-tools-vscode#36). 241 | 242 | ## v0.2.28 243 | 244 | * Add completion for `LangVersion` property. 245 | * Improve metadata completions for `Content` items. 246 | * Wait for Exit notification before terminating server process (tintoy/msbuild-project-tools-vscode#36). 247 | 248 | ## v0.2.27 249 | 250 | * LSP library's logging now uses configured logging level. 251 | 252 | ## v0.2.26 253 | 254 | * Implement completion for XML comments. 255 | 256 | ## v0.2.25 257 | 258 | * Implement completion for top-level `` element. 259 | 260 | ## v0.2.24 261 | 262 | * Make ASP.NET core snippets version-specific by @doggy8088 (tintoy/msbuild-project-tools-vscode#32). 263 | * Implement default value(s) for well-known property completions (tintoy/msbuild-project-tools-vscode#31). 264 | 265 | ## v0.2.23 266 | 267 | * Use latest version of OmniSharp LSP libraries (improves stability and diagnostic capabilities). 268 | 269 | ## v0.2.22 270 | 271 | * Improve MSBuild snippets by @doggy8088 (tintoy/msbuild-project-tools-vscode#30). 272 | 273 | ## v0.2.21 274 | 275 | * Add MSBuild snippets by @doggy8088 (tintoy/msbuild-project-tools-vscode#28). 276 | 277 | ## v0.2.20 278 | 279 | * Log errors encountered while warming up NuGet client as Verbose instead of Error (tintoy/msbuild-project-tools-server#2). 280 | 281 | ## v0.2.19 282 | 283 | * Bug-fix: Completions don't always work correctly in .props files (tintoy/msbuild-project-tools-vscode#27). 284 | * Use latest OmniSharp LSP packages. 285 | 286 | ## v0.2.18 287 | 288 | * Use v15.5.x of MSBuild packages (tintoy/msbuild-project-tools-server#1). 289 | 290 | ## v0.2.17 291 | 292 | * Add completions for item elements. 293 | * Split out language server from VS Code extension. 294 | * Never auto-show output window on messages from language server (tintoy/msbuild-project-tools-vscode#25). 295 | 296 | ## v0.2.16 297 | 298 | * Bug-fix: language server does not correctly report server capabilities when first initialised (tintoy/msbuild-project-tools-vscode#22). 299 | 300 | ## v0.2.15 301 | 302 | * Add support for passing language service configuration in `InitializeParams.InitializationOptions` (tintoy/msbuild-project-tools-vscode#17). 303 | 304 | ## v0.2.14 305 | 306 | * Offer element completions, when appropriate, in whitespace or element text (tintoy/msbuild-project-tools-vscode#15). 307 | * Improve completion behaviour. 308 | * Improve performance of element and attribute completions for tasks in `Target` elements. 309 | 310 | ## v0.2.13 311 | 312 | * Bug-fix: attribute completions are erroneously offered when creating a new element under an `ItemGroup` element (tintoy/msbuild-project-tools-vscode#21). 313 | 314 | ## v0.2.12 315 | 316 | * Simplify extension / language-service configuration schema. 317 | The extension will automatically upgrade settings in the legacy format (i.e. ones without `'schemaVersion': 1`), but now ignores the old `msbuildProjectFileTools` configuration section. 318 | * Bug-fix: completions for item metadata expressions being offered when only completions for item group expressions should be offered. 319 | * Bug-fix: `NullReferenceException` when listing completions for item group expressions. 320 | * Bug-fix: restore missing hover tooltip for SDK-style project import. 321 | * Bug-fix: metadata names in unused item groups are always named "Identity". 322 | 323 | ## v0.2.11 324 | 325 | * Diagnostics indicating invalid project contents or XML now have a range covering the whole element or attribute (where possible). 326 | 327 | ## v0.2.10 328 | 329 | * Bug-fix: Extension won't load, after changes for tintoy/msbuild-project-tools-vscode#18, if no configuration was specified (restore configuration defaults). 330 | 331 | ## v0.2.9 332 | 333 | * Add command (`NuGet: toggle pre-release`) to toggle NuGet pre-release packages and package versions on / off (tintoy/msbuild-project-tools-vscode#18). 334 | 335 | ## v0.2.8 336 | 337 | * _Experimental:_ Add completions for task elements based on task types declared in the project. 338 | * _Experimental:_ Add completions for task attributes based on task types declared in the project. 339 | * More testing on MacOS and Linux. 340 | * _Experimental:_ Parsing of MSBuild item transform expressions. 341 | * _Experimental:_ Add experimental feature flag (`empty-completion-lists`) to enable returning empty completion lists rather than null 342 | Fixes tintoy/msbuild-project-tools-vscode#17. 343 | We can't do this by default because our extension depends on VSCode's behaviour when null is returned vs an empty completion list (when null is returned, no completion list is displayed; when an empty completion list is returned, purely-textual completions are displayed based on current file contents). 344 | This behaviour is mainly to support clients other than VSCode (e.g. aCute). 345 | 346 | ## v0.2.7 347 | 348 | * Add setting to control which types of objects from the current projects are included when offering completions. 349 | * _Experimental:_ Add completions for qualified and unqualified item metadata expressions (`%(XXX.YYY)` and `%(YYY)`). 350 | 351 | ## v0.2.6 352 | 353 | * Bug-fix: attribute completions should be available on elements that don't currently have any attributes. 354 | * Bug-fix: go-to-definition should also work for regular-style project imports (not just SDK-style imports). 355 | * _Experimental:_ Add completions for MSBuild property and item expressions (`$(XXX)` and `@(XXX)`). 356 | 357 | ## v0.2.4 358 | 359 | * Bug-fix: missing completions for top-level elements (e.g. ``, ``, ``). 360 | * Improve help for well-known items and their metadata. 361 | * Bug-fix for tintoy/msbuild-project-tools-vscode#11 (should not fail on non-standard file extension). 362 | 363 | ## v0.2.3 364 | 365 | * Add help for well-known elements, attributes, properties, and item types from `MSBuild.*.xsd` to improve completions and tooltips-on-hover. 366 | * Improve completions for attributes that refer to target names. 367 | 368 | ## v0.2.2 369 | 370 | * Add completions for attributes that refer to target names. 371 | 372 | ## v0.2.1 373 | 374 | * Add completions for top-level elements (e.g. ``, ``, ``). 375 | * Add completions for property elements (both common and locally-defined). 376 | * Improve language-service internals (more consistently accurate comprehension of project contents). 377 | 378 | ## v0.2.0 379 | 380 | * Improved completions: 381 | * Add completions for `PackageReference` and `DotNetCliToolReference`. 382 | * Add completions for common item attributes. 383 | * Add completions for property `Condition` elements. 384 | * Support for logging to [Seq](https://getseq.net/). 385 | Only useful if you're hacking on the language service itself. 386 | 387 | ## v0.1.12 388 | 389 | * Sort package versions in descending order for classic completion provider, too. 390 | If you prefer the old behaviour, you can set `msbuildProjectTools.nuget.newestVersionsFirst` to `false`. 391 | 392 | ## v0.1.11 393 | 394 | * Sort package versions in descending order. 395 | If you prefer the old behaviour, you can set `msbuildProjectTools.nuget.newestVersionsFirst` to `false`. 396 | 397 | ## v0.1.10 398 | 399 | * Improve tooltip content when hovering on MSBuild XML. 400 | * Enable jumping from PackageReference element to package on NuGet.org. 401 | 402 | ## v0.1.9 403 | 404 | * Add specific hover tooltip for Condition attributes. 405 | 406 | ## v0.1.8 407 | 408 | * Add basic syntax-highlighting for expressions in MSBuild projects (currently only supported in attribute values). 409 | * Improve delay on first completion of PackageReference by asynchronously warming up the NuGet client. 410 | 411 | ## v0.1.7 412 | 413 | * Add configuration setting to disable tooltip-on-hover. 414 | * Add configuration setting to control logging verbosity. 415 | 416 | ## v0.1.6 417 | 418 | * Actually enable the language server by default (sorry about that). 419 | 420 | ## v0.1.5 421 | 422 | * Language server is now enabled by default. 423 | * Improve calculation of line / column offsets. 424 | 425 | ## v0.1.4 426 | 427 | * Provide intellisense for regular-style and SDK-style imports whose conditions evaluate to false. 428 | * Respect the user's nominated version of the .NET Core tooling to use when loading master projects (equivalent to running `dotnet --version` in the solution directory, this respects `global.json` if present). 429 | 430 | ## v0.1.3 431 | 432 | * Provide intellisense for items whose conditions evaluate to `false`. 433 | * Show information about conditions on hover for items and properties. 434 | 435 | ## v0.1.2 436 | 437 | * Handle `Import` elements that give rise to multiple imported projects (this already worked correctly for SDK-style imports). 438 | * Initial support for master and sub projects. 439 | 440 | ## v0.1.1 441 | 442 | * Use a patched version of `Microsoft.Language.Xml` that behaves correctly in non-windows environments (issues with CR vs CRLF line-endings). 443 | * Improve tooltips on hover. 444 | 445 | ## v0.1.0 446 | 447 | * Fix handling of non-windows line endings. 448 | 449 | ## v0.1.0-rc1 450 | 451 | * Fix cross-platform path handling. 452 | 453 | ## v0.1.0-beta2 454 | 455 | * More informative tooltips on hover 456 | * Handle multiple items originating from a single item group element in the XML. 457 | * Improved error handling 458 | 459 | ## v0.1.0-beta1 460 | 461 | * Implement go-to-definition for project-style and SDK-style imports. 462 | * Detect .NET Core version on startup, and fall back to classic completion provider if >= 2.0.0 is not available. 463 | 464 | ## v0.1.0-alpha2 465 | 466 | * Add configuration property (`msbuildProjectFileTools.languageService.enable`) to switch between MSBuild language engine and classic completion provider. 467 | 468 | ## v0.1.0-alpha1 469 | 470 | * The extension now uses a language server based on Microsoft's Language Server Protocol. 471 | * Tooltips for MSBuild objects in the project file. 472 | * Support for any configured (remote) package source. 473 | We're using the new NuGet client libraries so it should understand global, solution-local, and project-local package sources. 474 | * Highly-improved handling of project files with broken or invalid XML (thanks to `Microsoft.Language.Xml`). 475 | 476 | ## v0.0.2 477 | 478 | * Resolve the URL for the NuGet v3 AutoComplete API at extension startup. 479 | 480 | ## v0.0.1 481 | 482 | * Initial release. 483 | -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | workflow: GitHubFlow/v1 2 | 3 | mode: ContinuousDelivery 4 | label: '{BranchName}' 5 | tag-prefix: '[vV]' 6 | increment: Inherit 7 | prevent-increment: 8 | of-merged-branch: false 9 | when-branch-merged: false 10 | when-current-commit-tagged: true 11 | 12 | semantic-version-format: Strict 13 | assembly-versioning-scheme: MajorMinorPatch 14 | assembly-file-versioning-scheme: MajorMinorPatchTag 15 | 16 | update-build-number: true 17 | 18 | strategies: 19 | - Fallback 20 | - ConfiguredNextVersion 21 | - MergeMessage 22 | - TaggedCommit 23 | - TrackReleaseBranches 24 | - VersionInBranchName 25 | 26 | branches: 27 | main: 28 | mode: ContinuousDeployment 29 | label: '' 30 | increment: Patch 31 | prevent-increment: 32 | of-merged-branch: true 33 | track-merge-target: false 34 | track-merge-message: true 35 | regex: ^master$|^main$ 36 | source-branches: [] 37 | is-source-branch-for: [] 38 | tracks-release-branches: false 39 | is-release-branch: false 40 | is-main-branch: true 41 | pre-release-weight: 55000 42 | 43 | release: 44 | mode: ContinuousDeployment 45 | label: '' 46 | increment: Patch 47 | prevent-increment: 48 | of-merged-branch: false 49 | when-branch-merged: false 50 | when-current-commit-tagged: false 51 | track-merge-target: false 52 | track-merge-message: true 53 | regex: ^release\/(?.+) 54 | source-branches: 55 | - main 56 | is-source-branch-for: [] 57 | tracks-release-branches: false 58 | is-release-branch: true 59 | is-main-branch: false 60 | pre-release-weight: 30000 61 | 62 | preview: 63 | mode: ContinuousDelivery 64 | label: preview 65 | increment: Patch 66 | prevent-increment: 67 | of-merged-branch: true 68 | when-branch-merged: false 69 | when-current-commit-tagged: false 70 | track-merge-target: false 71 | track-merge-message: true 72 | regex: ^preview\/(?.+) 73 | source-branches: 74 | - main 75 | is-source-branch-for: [] 76 | tracks-release-branches: false 77 | is-release-branch: true 78 | is-main-branch: false 79 | pre-release-weight: 30000 80 | 81 | feature: 82 | mode: ContinuousDelivery 83 | label: '{BranchName}' 84 | increment: Inherit 85 | prevent-increment: 86 | when-current-commit-tagged: false 87 | track-merge-message: true 88 | regex: ^feature/(?.+) 89 | source-branches: 90 | - main 91 | - release 92 | is-source-branch-for: [] 93 | is-main-branch: false 94 | pre-release-weight: 30000 95 | 96 | pull-request: 97 | mode: ContinuousDelivery 98 | label: PullRequest{Number} 99 | increment: Inherit 100 | prevent-increment: 101 | of-merged-branch: true 102 | when-current-commit-tagged: false 103 | track-merge-message: true 104 | regex: ^(pull-requests|pull|pr)[\/-](?\d*) 105 | source-branches: 106 | - main 107 | - release 108 | - feature 109 | is-source-branch-for: [] 110 | pre-release-weight: 30000 111 | 112 | unknown: 113 | mode: ManualDeployment 114 | label: '{BranchName}' 115 | increment: Inherit 116 | prevent-increment: 117 | when-current-commit-tagged: false 118 | track-merge-message: false 119 | regex: (?.+) 120 | source-branches: 121 | - main 122 | - release 123 | - feature 124 | - pull-request 125 | is-source-branch-for: [] 126 | is-main-branch: false 127 | 128 | ignore: 129 | sha: [] 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Adam Friedman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OSSREADME.json: -------------------------------------------------------------------------------- 1 | // ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: 2 | 3 | [{ 4 | "name": "atom/language-xml", 5 | "version": "0.0.0", 6 | "license": "MIT", 7 | "repositoryURL": "https://github.com/atom/language-xml", 8 | "description": "The file 'syntaxes/msbuild.json' was derived from the file 'syntaxes/xml.json' in the VSCode XML extension (https://github.com/Microsoft/vscode/blob/master/extensions/xml), which derived it from the Atom XML package (https://github.com/atom/language-xml), which was originally converted from the TextMate bundle https://github.com/textmate/xml.tmbundle." 9 | },{ 10 | "name": "Microsoft/msbuild", 11 | "version": "0.0.0", 12 | "license": "MIT", 13 | "repositoryURL": "https://github.com/Microsoft/msbuild", 14 | "description": "Documentation / help content for well-known MSBuild elements, attributes, properties, and item types was derived from https://github.com/Microsoft/msbuild/blob/master/src/MSBuild/Microsoft.Build.CommonTypes.xsd, as well as https://github.com/MicrosoftDocs/visualstudio-docs/tree/master/docs/msbuild." 15 | }] 16 | -------------------------------------------------------------------------------- /Publish-LanguageServer.ps1: -------------------------------------------------------------------------------- 1 | $dotnet = Get-Command 'dotnet' 2 | 3 | $latestTag = git describe --tags --abbrev=0 4 | 5 | if ($latestTag -notmatch '^(v)?\d+\.\d+\.\d+(-.*)?$') { 6 | Write-Host "Latest tag doesn't follow semantic version format. The format is [v]major.minor.patch[-suffix]" 7 | exit 8 | } 9 | 10 | if ($latestTag.StartsWith('v')) { 11 | $latestTag = $latestTag.Substring(1) 12 | } 13 | 14 | $parts = $latestTag.Split('-') 15 | 16 | $versionPrefix = $parts[0] 17 | 18 | if ($parts.Count -gt 1) { 19 | $versionSuffix = $parts[1] 20 | } 21 | 22 | if ($args -contains '--dev') { 23 | $buildConfiguration = 'Debug' 24 | if ($versionSuffix -eq '') { 25 | $versionSuffix = "$versionSuffix-" 26 | } 27 | $versionSuffix = $versionSuffix + 'dev' 28 | } else { 29 | $buildConfiguration = 'Release' 30 | } 31 | 32 | $latestTagFull = git describe --tags 33 | $latestTagShort = git describe --tags --abbrev=0 34 | 35 | if ($latestTagFull -ceq $latestTagShort) { 36 | $numberOfCommits = 0 37 | } else { 38 | $diff = $latestTagFull -replace $latestTagShort, "" 39 | $numberOfCommits = $diff.Split('-')[1] 40 | } 41 | 42 | $fileVersion = "$versionPrefix.$numberOfCommits" 43 | 44 | Write-Output "Chosen build configuration: $buildConfiguration" 45 | 46 | $serverRoot = Join-Path $PSScriptRoot 'lib\server' 47 | $publishRoot = Join-Path $PSScriptRoot '' 48 | 49 | & $dotnet restore "$serverRoot\MSBuildProjectTools.sln" 50 | & $dotnet publish "$serverRoot\src\LanguageServer\LanguageServer.csproj" -o "$publishRoot\language-server" /p:VersionPrefix="$versionPrefix" /p:VersionSuffix="$versionSuffix" /p:FileVersion="$fileVersion" -c $buildConfiguration 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MSBuild project file tools 2 | 3 | An extension for VS Code that provides intellisense for MSBuild project files, including auto-complete for `` elements. 4 | 5 | ![The extension in action](docs/images/extension-in-action.gif) 6 | 7 | The language service used by this extension can be found here: [tintoy/msbuild-project-tools-server](https://github.com/tintoy/msbuild-project-tools-server/) 8 | 9 | ## Usage 10 | 11 | * Completions for `PackageReference` and `DotNetCliToolReference`. 12 | * Completions for `Include` and `Version` attributes of these items (invoke the `NuGet: toggle pre-release` command to include / exclude pre-release packages / package versions). 13 | * Completions to create these elements. 14 | * Completions for common top-level elements (e.g. `PropertyGroup`, `ItemGroup`, `Target`). 15 | * Completions for import elements. 16 | * Completions for property elements. 17 | * Completions for item elements. 18 | * Completions for common item attributes. 19 | * Completions for common property `Condition` attributes. 20 | * Hover the mouse over imports, targets, items, properties, and conditions to see information about them. 21 | * Document symbols are supported for imports, targets, items, and properties. 22 | * Go-to-definition is implemented for both SDK-style and regular project imports. 23 | * Basic syntax highlighting of MSBuild expressions in attribute values. 24 | To see this highlighting, change the editor language from `XML` to `MSBuild`. 25 | * Completions for `$()` property, `@()` item, and `%()` item metadata expressions. 26 | To enable these completions, ensure that the editor language is `MSBuild` (not `XML`). 27 | * Completions for task elements and attributes based on metadata for tasks declared in the project and its imported projects. 28 | To enable these completions, add the string `Tasks` to the `msbuildProjectTools.language.completionsFromProject` setting. 29 | * Useful snippets for common elements of MSBuild project XML (added by @doggy8088). 30 | To use these snippets, ensure that the editor language is `MSBuild` (not `XML`). 31 | 32 | ### File extensions 33 | 34 | By default, the language service is automatically available for files with the following extensions: 35 | 36 | * `*.*proj` 37 | * `.targets` 38 | * `.props` 39 | * `.tasks` 40 | 41 | If you want to use it with additional file types: 42 | 43 | 1. Open the desired file. 44 | 2. Change the language mode by pressing `Ctrl-Shift-P` (or `Cmd-Shift-P` on Mac) and choosing "Change Language Mode". 45 | ![associate language with file extension](docs/images/change-language-mode.jpg) 46 | 3. Choose "configure file association". 47 | ![associate language with file extension](docs/images/associate-language-with-file-extension.jpg) 48 | 4. Choose language `MSBuild`. 49 | 50 | ## Installation 51 | 52 | You can install this extension from the [VS marketplace](https://marketplace.visualstudio.com/items?itemName=tintoy.msbuild-project-tools), [Open VSX](https://open-vsx.org/extension/tintoy/msbuild-project-tools) or simply [download](https://github.com/tintoy/msbuild-project-tools-vscode/releases/latest) the VSIX package for the latest release and install it by choosing "Install from VSIX" from the menu on the top right of the extensions panel. 53 | 54 | ## Building from source 55 | 56 | See [BUILDING.md](docs/BUILDING.md). 57 | 58 | ## Design 59 | 60 | See [architectural overview](docs/architecture/overview.md) for details (this is a work-in-progress; if you have questions, feel free to create an issue). 61 | 62 | ## Limitations 63 | 64 | * The VS Code extension API has changed somewhat since this extension was first created, and some features (such as hyperlinks to files in hover tooltips) are only partially functional at present (broken by VS Code API / behaviour changes). 65 | _This is still being worked on :-)_ 66 | * Limited intellisense is available for dynamic `PropertyGroup` / `ItemGroup` declarations (i.e. those appearing inside a `Target` element); these are only evaluated when the project is built and so very little information about them is available to us when statically evaluating the project (see [tintoy/msbuild-project-tools-server#5](https://github.com/tintoy/msbuild-project-tools-server/issues/5#issuecomment-383352512) for details). 67 | * Support for task completions is experimental; if you find a problem with it, please [create an issue](https://github.com/tintoy/msbuild-project-tools-vscode/issues/new). 68 | * If you open more than one project at a time (or navigate to imported projects), subsequent projects will be loaded into the same MSBuild project collection as the first project. Once you have closed the last project file, the next project file you open will become the master project. The master project will become selectable in a later release. 69 | 70 | ## Questions / bug reports 71 | 72 | If you have questions, feedback, feature requests, or would like to report a bug, please feel free to reach out by creating an issue. When reporting a bug, please try to include as much information as possible about what you were doing at the time, what you expected to happen, and what actually happened. 73 | 74 | If you're interested in collaborating that'd be great, too :-) 75 | -------------------------------------------------------------------------------- /azure-pipelines-development.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | branches: 3 | include: 4 | - master 5 | 6 | pr: 7 | branches: 8 | include: 9 | - master 10 | 11 | variables: 12 | - name: buildConfiguration 13 | value: Release 14 | 15 | stages: 16 | - template: ./azure-pipelines-template.yml 17 | parameters: 18 | buildConfiguration: ${{ variables.buildConfiguration }} 19 | publish: false 20 | -------------------------------------------------------------------------------- /azure-pipelines-release.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | - name: buildConfiguration 3 | value: Release 4 | - group: vs-marketplace-publisher 5 | 6 | trigger: 7 | include: 8 | - master 9 | - release/* 10 | - preview/* 11 | 12 | stages: 13 | - template: ./azure-pipelines-template.yml 14 | parameters: 15 | buildConfiguration: ${{ variables.buildConfiguration }} 16 | publish: true 17 | vsMarketplacePublisherToken: ${{ variables.VSMarketPlacePublisherToken }} 18 | -------------------------------------------------------------------------------- /azure-pipelines-template.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: buildConfiguration 3 | default: Release 4 | - name: publish 5 | type: boolean 6 | default: "false" 7 | - name: vsMarketplacePublisherToken 8 | type: string 9 | default: "" 10 | 11 | stages: 12 | - stage: Versioning 13 | displayName: "Determine build version" 14 | 15 | jobs: 16 | - job: BuildVersion 17 | 18 | steps: 19 | - task: UseDotNet@2 20 | displayName: "Use .NET Core SDK from global.json" 21 | inputs: 22 | packageType: sdk 23 | useGlobalJson: true 24 | 25 | - task: Bash@3 26 | displayName: "Install GitVersion" 27 | 28 | inputs: 29 | targetType: inline 30 | script: | 31 | echo "PATH=$PATH" 32 | dotnet tool restore --tool-manifest $(Build.SourcesDirectory)/.config/dotnet-tools.json 33 | 34 | - task: PythonScript@0 35 | name: SemVer 36 | displayName: "Capture SemVer properties" 37 | 38 | inputs: 39 | scriptSource: filePath 40 | scriptPath: $(Build.SourcesDirectory)/build/semver.py 41 | arguments: $(Build.SourcesDirectory) 42 | 43 | - stage: Build 44 | displayName: Build 45 | 46 | dependsOn: Versioning 47 | 48 | pool: 49 | vmImage: ubuntu-latest 50 | 51 | jobs: 52 | - job: Build 53 | displayName: Build extension and language server 54 | 55 | variables: 56 | ExtensionVersion: $[stageDependencies.Versioning.BuildVersion.outputs['SemVer.MajorMinorPatch']] 57 | VersionPrefix: $[stageDependencies.Versioning.BuildVersion.outputs['SemVer.VersionPrefix']] 58 | VersionSuffix: $[stageDependencies.Versioning.BuildVersion.outputs['SemVer.VersionSuffix']] 59 | IsPreRelease: $[stageDependencies.Versioning.BuildVersion.outputs['SemVer.IsPreRelease']] 60 | 61 | steps: 62 | - task: Npm@1 63 | displayName: "Install npx" 64 | inputs: 65 | command: custom 66 | customCommand: "install nx" 67 | verbose: false 68 | 69 | - task: Npm@1 70 | displayName: "Update extension package version" 71 | 72 | inputs: 73 | command: custom 74 | customCommand: 'version "$(ExtensionVersion)" --git-tag-version false' 75 | 76 | - task: Npm@1 77 | displayName: "npm install" 78 | 79 | inputs: 80 | command: ci 81 | workingDir: $(Build.SourcesDirectory) 82 | 83 | # TODO: Rather than build language server as part of extension CI, check the last commit in history for repo @ ./lib/server that is associated with a GitHub release and download the appropriate language server artifact from there. 84 | 85 | - task: UseDotNet@2 86 | displayName: "Use .NET Core SDK from global.json" 87 | inputs: 88 | packageType: sdk 89 | useGlobalJson: true 90 | 91 | - task: DotNetCoreCLI@2 92 | displayName: "Install local tools (.NET Core)" 93 | inputs: 94 | command: custom 95 | custom: "tool install" 96 | 97 | - task: DotNetCoreCLI@2 98 | displayName: "Restore packages" 99 | 100 | inputs: 101 | command: "restore" 102 | projects: "./lib/server/MSBuildProjectTools.sln" 103 | restoreArguments: '/p:VersionPrefix="$(VersionPrefix)" /p:VersionSuffix="$(VersionSuffix)"' 104 | 105 | - task: DotNetCoreCLI@2 106 | displayName: "Build solution" 107 | 108 | inputs: 109 | command: "build" 110 | projects: "./lib/server/MSBuildProjectTools.sln" 111 | arguments: '--configuration "$(buildConfiguration)" /p:VersionPrefix="$(VersionPrefix)" /p:VersionSuffix="$(VersionSuffix)"' 112 | 113 | - task: DotNetCoreCLI@2 114 | displayName: "Run tests" 115 | 116 | inputs: 117 | command: "test" 118 | projects: "./lib/server/test/LanguageServer.Engine.Tests/LanguageServer.Engine.Tests.csproj" 119 | arguments: '--configuration "$(buildConfiguration)" /p:VersionPrefix="$(VersionPrefix)" /p:VersionSuffix="$(VersionSuffix)"' 120 | 121 | - task: DotNetCoreCLI@2 122 | displayName: "Publish language server" 123 | 124 | inputs: 125 | command: "publish" 126 | publishWebProjects: false 127 | projects: "./lib/server/src/LanguageServer/LanguageServer.csproj" 128 | arguments: '--configuration "$(buildConfiguration)" -o "./language-server" /p:VersionPrefix="$(VersionPrefix)" /p:VersionSuffix="$(VersionSuffix)"' 129 | modifyOutputPath: false 130 | zipAfterPublish: false 131 | 132 | - task: Bash@3 133 | displayName: "Build VS Code extension package (pre-release)" 134 | 135 | condition: eq( variables.IsPreRelease, 'true' ) 136 | 137 | inputs: 138 | targetType: inline 139 | script: npx vsce package --pre-release -o "$(Build.ArtifactStagingDirectory)/msbuild-project-tools-vscode-$(ExtensionVersion).vsix" 140 | 141 | - task: Bash@3 142 | displayName: "Build VS Code extension package" 143 | 144 | condition: ne( variables.IsPreRelease, 'true' ) 145 | 146 | inputs: 147 | targetType: inline 148 | script: npx vsce package -o "$(Build.ArtifactStagingDirectory)/msbuild-project-tools-vscode-$(ExtensionVersion).vsix" 149 | 150 | - task: PublishBuildArtifacts@1 151 | displayName: "Publish extension package" 152 | 153 | inputs: 154 | PathtoPublish: "$(Build.ArtifactStagingDirectory)/msbuild-project-tools-vscode-$(ExtensionVersion).vsix" 155 | ArtifactName: "vscode-extension" 156 | publishLocation: "Container" 157 | 158 | - task: GitHubRelease@1 159 | displayName: "Create GitHub release from tag" 160 | 161 | condition: and( and( ${{ parameters.publish }}, succeeded() ), contains( variables['Build.SourceBranch'], 'refs/tags/v' ) ) 162 | 163 | inputs: 164 | gitHubConnection: "github.com_tintoy" 165 | repositoryName: "$(Build.Repository.Name)" 166 | action: "create" 167 | target: "$(Build.SourceVersion)" 168 | tagSource: "gitTag" 169 | tagPattern: '^v\d+\.\d+.\d+(-[A-Za-z0-9%\.]+)?$' 170 | releaseNotesFilePath: $(Build.SourcesDirectory)/CHANGELOG.md 171 | addChangeLog: false 172 | assets: "$(Build.ArtifactStagingDirectory)/*.vsix" 173 | assetUploadMode: replace 174 | 175 | - stage: Publish 176 | displayName: Publish 177 | 178 | dependsOn: 179 | - Versioning 180 | - Build 181 | 182 | condition: and( ${{ parameters.publish }}, succeeded() ) 183 | 184 | pool: 185 | vmImage: ubuntu-latest 186 | 187 | jobs: 188 | - deployment: Publish 189 | displayName: Publish extension package to VS Marketplace 190 | 191 | environment: VS Marketplace 192 | 193 | # Artifacts are automatically downloaded for deployment jobs. 194 | 195 | variables: 196 | ExtensionVersion: $[stageDependencies.Versioning.BuildVersion.outputs['SemVer.MajorMinorPatch']] 197 | IsPreRelease: $[stageDependencies.Versioning.BuildVersion.outputs['SemVer.IsPreRelease']] 198 | 199 | strategy: 200 | runOnce: 201 | deploy: 202 | steps: 203 | - task: Npm@1 204 | displayName: "Install npx" 205 | inputs: 206 | command: custom 207 | customCommand: "install nx" 208 | verbose: false 209 | 210 | - task: Npm@1 211 | displayName: "Install vsce" 212 | inputs: 213 | command: custom 214 | customCommand: "install vsce" 215 | verbose: false 216 | 217 | - task: Bash@3 218 | displayName: "Publish to VS Marketplace (pre-release)" 219 | 220 | condition: eq( variables.IsPreRelease, 'true' ) 221 | 222 | env: 223 | VSCE_PAT: ${{ parameters.vsMarketplacePublisherToken }} 224 | 225 | inputs: 226 | targetType: inline 227 | script: | 228 | VS_EXTENSION_PACKAGE_DIR="$(Pipeline.Workspace)/vscode-extension" 229 | VS_EXTENSION_PACKAGE_FILE=$VS_EXTENSION_PACKAGE_DIR/msbuild-project-tools-vscode-$(ExtensionVersion).vsix 230 | 231 | npx vsce publish --pre-release --packagePath $VS_EXTENSION_PACKAGE_FILE 232 | 233 | - task: Bash@3 234 | displayName: "Publish to VS Marketplace" 235 | 236 | condition: ne( variables.IsPreRelease, 'true' ) 237 | 238 | env: 239 | VSCE_PAT: ${{ parameters.vsMarketplacePublisherToken }} 240 | 241 | inputs: 242 | targetType: inline 243 | script: | 244 | VS_EXTENSION_PACKAGE_DIR="$(Pipeline.Workspace)/vscode-extension" 245 | VS_EXTENSION_PACKAGE_FILE=$VS_EXTENSION_PACKAGE_DIR/msbuild-project-tools-vscode-$(ExtensionVersion).vsix 246 | 247 | npx vsce publish --packagePath $VS_EXTENSION_PACKAGE_FILE 248 | -------------------------------------------------------------------------------- /build/semver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This script is used to process semantic versioning 4 | 5 | import json 6 | import os 7 | import subprocess 8 | import sys 9 | 10 | from shutil import which 11 | 12 | def log(level: str, message: str) -> None: 13 | for message_line in message.splitlines(): 14 | print(f"##[{level}]{message_line}") 15 | 16 | def log_info(message: str): 17 | log('info', message) 18 | 19 | def log_debug(message: str): 20 | log('debug', message) 21 | 22 | def log_error(message: str): 23 | log('error', message) 24 | 25 | def set_variable(name: str, value: str, is_output: bool = False, debug: bool = False) -> None: 26 | variable_definition = f"variable={name}" 27 | 28 | if is_output: 29 | variable_definition += ";isOutput=true" 30 | 31 | print(f"##vso[task.setvariable {variable_definition}]{value}") 32 | 33 | if debug: 34 | log_info(f"SetVariable: '{name}' = '{value}'") 35 | 36 | def get_build_number() -> str | None: 37 | return os.getenv('BUILD_BUILDNUMBER') 38 | 39 | def set_build_number(build_number: str) -> None: 40 | print(f"##vso[build.updatebuildnumber]{build_number}") 41 | 42 | def run_gitversion(target_directory: str) -> dict: 43 | dotnet_executable = which("dotnet") 44 | gitversion_commandline = f"{dotnet_executable} gitversion {target_directory}" 45 | log_debug(f"Launch process: '{gitversion_commandline}'") 46 | 47 | (exitCode, output) = subprocess.getstatusoutput(gitversion_commandline) 48 | 49 | if exitCode == 0: 50 | log_debug("Process STDOUT:") 51 | log_debug(output) 52 | 53 | gitversion_variables: dict = json.loads(output) 54 | 55 | return { 56 | variable_name: (variable_value or '') 57 | for (variable_name, variable_value) in gitversion_variables.items() 58 | } 59 | else: 60 | log_error("Process STDOUT:") 61 | log_error(output) 62 | 63 | raise Exception(f"'dotnet gitversion': process exit command ({exitCode}) does not indicate success.") 64 | 65 | if __name__ == "__main__": 66 | args: list[str] = sys.argv 67 | if len(args) != 2: 68 | print(f"Usage: {args[0]}") 69 | 70 | exit(2) 71 | 72 | print(f"PATH = '{os.getenv("PATH")}'") 73 | 74 | target_dir = args[1] 75 | gitversion_variables = run_gitversion(args[1]) 76 | 77 | print( 78 | json.dumps(gitversion_variables, indent=2, sort_keys=True) 79 | ) 80 | 81 | # Odd-numbered minor versions are considered pre-release (this is due to long-standing limitations in the VS extension gallery's handling of package versions). 82 | minor_version = int(gitversion_variables["Minor"]) 83 | if minor_version % 2 == 1: 84 | gitversion_variables["IsPreRelease"] = 'true' 85 | else: 86 | gitversion_variables["IsPreRelease"] = 'false' 87 | 88 | version_prefix = gitversion_variables["MajorMinorPatch"] 89 | 90 | build_metadata = gitversion_variables.get("BuildMetaData") 91 | if build_metadata: 92 | version_prefix = f"{version_prefix}.{build_metadata}" 93 | 94 | version_suffix = gitversion_variables.get('PreReleaseTag') 95 | 96 | gitversion_variables["VersionPrefix"] = version_prefix 97 | gitversion_variables["VersionSuffix"] = version_suffix 98 | 99 | for variable_name in gitversion_variables.keys(): 100 | variable_value = gitversion_variables[variable_name] 101 | set_variable(variable_name, variable_value, is_output=True) 102 | 103 | semver = gitversion_variables["SemVer"] 104 | branch_name = gitversion_variables["EscapedBranchName"] 105 | commits_since_source_version = int( 106 | gitversion_variables.get("CommitsSinceVersionSource", "0") 107 | ) 108 | 109 | set_build_number(f"{semver}+{branch_name}.{commits_since_source_version}") 110 | -------------------------------------------------------------------------------- /docs/BUILDING.md: -------------------------------------------------------------------------------- 1 | # Building MSBuild Project Tools for VS Code 2 | 3 | You'll need: 4 | 5 | 1. .NET 6.0.0 or newer 6 | 2. NodeJS 7 | 3. VSCE 8 | `npm install -g @vscode/vsce` 9 | 4. Powershell (already there by default on Windows) 10 | 11 | Don't forget to update LSP submodule after pulling the repo: 12 | 13 | 1. `git submodule init` 14 | 2. `git submodule update` 15 | 16 | To build: 17 | 18 | 1. `npm install` 19 | 2. `powershell Publish-LanguageServer.ps1 --dev` 20 | 21 | To debug: 22 | 23 | 1. Open VS Code, and hit F5. Both LSP and extension client will be built automatically. A new instance of VS Code will be opened and a debug session will be activated 24 | 25 | To create a VSIX package: 26 | 27 | 1. `npm run build-language-server` 28 | 2. `vsce package` 29 | 30 | ## Publishing to VS Gallery 31 | 32 | To publish the extension to the VS Gallery, push a tag named `vXXX`, where `XXX` is a SemVer-compliant version number. 33 | If the version includes a pre-release tag, it will be published as a pre-release version in the gallery. 34 | -------------------------------------------------------------------------------- /docs/architecture/diagrams.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tintoy/msbuild-project-tools-vscode/22bab3371e8feab9ab52c43fe58d5b43e58a1e6f/docs/architecture/diagrams.pptx -------------------------------------------------------------------------------- /docs/architecture/images/lsp-layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tintoy/msbuild-project-tools-vscode/22bab3371e8feab9ab52c43fe58d5b43e58a1e6f/docs/architecture/images/lsp-layers.png -------------------------------------------------------------------------------- /docs/architecture/images/server-layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tintoy/msbuild-project-tools-vscode/22bab3371e8feab9ab52c43fe58d5b43e58a1e6f/docs/architecture/images/server-layers.png -------------------------------------------------------------------------------- /docs/architecture/overview.md: -------------------------------------------------------------------------------- 1 | # Architectural overview 2 | 3 | Most of the functionality provided by MSBuild project tools is implemented by an LSP-compliant language service. 4 | 5 | _Note: this documentation is a work-in-progress; if you have questions, feel free to create an issue._ 6 | 7 | # Language Server Protocol 8 | 9 | The extension for VS Code extension communicates with this service using [vscode-languageclient](https://www.npmjs.com/package/vscode-languageclient) over STDIN/STDOUT. 10 | 11 | ![LSP - layers](images/lsp-layers.png) 12 | 13 | # Server 14 | 15 | The server is implemented in several layers. 16 | 17 | * **Protocol** 18 | * We use OmniSharp's LSP and JSON-RPC implementation. 19 | * Everything is tied together by the [LanguageServer](https://github.com/OmniSharp/csharp-language-server-protocol/blob/4bc2a15e43593e6e459e9b0784cb85fadcf89c34/src/Lsp/LanguageServer.cs#L22). 20 | * **Server** 21 | * We use Autofac for dependency-injection and Serilog for logging (including a custom logger that forwards log entries to the LSP client, e.g. VS Code, for diplay). 22 | * A [Workspace](../../src/LanguageServer.Engine/Documents/Workspace.cs) handles all projects for a given base directory. 23 | * Most of the state for each open MSBuild project is held by a [ProjectDocument](../../src/LanguageServer.Engine/Documents/ProjectDocument.cs) (see [MasterProjectDocument](../../src/LanguageServer.Engine/Documents/MasterProjectDocument.cs) and [SubProjectDocument](../../src/LanguageServer.Engine/Documents/SubProjectDocument.cs) for further details). 24 | * Document state is protected by an `AsyncReaderWriterLock` (it is the caller's responsibility to call `XXXLock` / `XXXLockAsync` as required). 25 | * **Handlers** 26 | * The [DocumentSyncHandler](../../src/LanguageServer.Engine/Handlers/DocumentSyncHandler.cs) handles synchronisation of document state with the client. We expect a `textDocument/didOpen` notification as the trigger to open and load a project. 27 | * The [CompletionHandler](../../src/LanguageServer.Engine/Handlers/CompletionHandler.cs) calls multiple [completion providers](../../src/LanguageServer.Engine/CompletionProviders) in parallel. 28 | * If all completion providers return `null`, then the `CompletionHandler` will return null to the caller (indicating no completions are available). 29 | * If only _some_ completion providers return `null`, then the `CompletionHandler` will ignore them and return non-`null` completions. 30 | * If any provider indicates that their completion list is incomplete, then the composite completion list will be marked as incomplete. 31 | 32 | ![server layers](images/server-layers.png) 33 | 34 | # Syntax and semantic models 35 | 36 | * At the lowest level, we have `Microsoft.Language.Xml` and `Microsoft.Build.Construction`. 37 | * Above that we have `MSBuildProjectTools.SemanticModel.Xml`, `MSBuildProjectTools.SemanticModel.MSBuild`, and `Microsoft.Build.Evaluation`. 38 | 39 | The semantic models build on the syntax models to create a more high-level API for evaluating project contents. 40 | -------------------------------------------------------------------------------- /docs/images/associate-language-with-file-extension.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tintoy/msbuild-project-tools-vscode/22bab3371e8feab9ab52c43fe58d5b43e58a1e6f/docs/images/associate-language-with-file-extension.jpg -------------------------------------------------------------------------------- /docs/images/change-language-mode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tintoy/msbuild-project-tools-vscode/22bab3371e8feab9ab52c43fe58d5b43e58a1e6f/docs/images/change-language-mode.jpg -------------------------------------------------------------------------------- /docs/images/extension-in-action.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tintoy/msbuild-project-tools-vscode/22bab3371e8feab9ab52c43fe58d5b43e58a1e6f/docs/images/extension-in-action.gif -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tintoy/msbuild-project-tools-vscode/22bab3371e8feab9ab52c43fe58d5b43e58a1e6f/images/icon.png -------------------------------------------------------------------------------- /language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "", 4 | "blockComment": [""] 5 | }, 6 | "brackets": [ 7 | ["<", ">"], 8 | ["(", ")"], 9 | ["$(", ")"], 10 | ["@(", ")"], 11 | ["%(", ")"] 12 | ], 13 | "autoClosingPairs": [ 14 | ["<", ">"], 15 | ["'", "'"], 16 | ["\"", "\""] 17 | ], 18 | "surroundingPairs": [ 19 | ["<", ">"], 20 | ["'", "'"], 21 | ["\"", "\""] 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "msbuild-project-tools", 3 | "displayName": "MSBuild project tools", 4 | "description": "Tools for working with MSBuild project files (such as auto-complete for package Ids / versions).", 5 | "version": "0.6.4", 6 | "settingsSchemaVersion": 1, 7 | "publisher": "tintoy", 8 | "license": "MIT", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/tintoy/msbuild-project-tools-vscode.git" 12 | }, 13 | "icon": "images/icon.png", 14 | "engines": { 15 | "vscode": "^1.82.0" 16 | }, 17 | "categories": [ 18 | "Programming Languages", 19 | "Other" 20 | ], 21 | "keywords": [ 22 | "msbuild", 23 | "dotnet", 24 | "nuget", 25 | "packagereference", 26 | "C#" 27 | ], 28 | "extensionKind": [ 29 | "workspace" 30 | ], 31 | "activationEvents": [ 32 | "workspaceContains:NuGet.config", 33 | "workspaceContains:global.json", 34 | "workspaceContains:**/*.*proj", 35 | "workspaceContains:**/*.props", 36 | "workspaceContains:**/*.targets", 37 | "onLanguage:xml" 38 | ], 39 | "main": "./dist/extension", 40 | "contributes": { 41 | "commands": [], 42 | "configuration": { 43 | "title": "MSBuild project tools", 44 | "type": "object", 45 | "properties": { 46 | "msbuildProjectTools.logging.level": { 47 | "title": "Language service logging level.", 48 | "type": "string", 49 | "enum": [ 50 | "Error", 51 | "Warning", 52 | "Information", 53 | "Debug", 54 | "Verbose" 55 | ], 56 | "default": "Information", 57 | "description": "The logging level for the MSBuild language service." 58 | }, 59 | "msbuildProjectTools.logging.file": { 60 | "title": "Language service log file.", 61 | "type": "string", 62 | "default": null, 63 | "description": "If specified, the language service will log to this file." 64 | }, 65 | "msbuildProjectTools.logging.seq.level": { 66 | "type": "string", 67 | "enum": [ 68 | "Error", 69 | "Warning", 70 | "Information", 71 | "Debug", 72 | "Verbose" 73 | ], 74 | "default": "Verbose", 75 | "description": "The minimum log level for Seq." 76 | }, 77 | "msbuildProjectTools.logging.seq.url": { 78 | "type": "string", 79 | "default": null, 80 | "description": "The URL of the Seq server (Seq logging will be enabled if this is specified)." 81 | }, 82 | "msbuildProjectTools.logging.seq.apiKey": { 83 | "type": "string", 84 | "default": null, 85 | "description": "The API key (if any) to use when authenticating to Seq." 86 | }, 87 | "msbuildProjectTools.logging.trace": { 88 | "type": "boolean", 89 | "default": false, 90 | "description": "Trace all communication between VSCode and the MSBuild language service?" 91 | }, 92 | "msbuildProjectTools.language.disable.hover": { 93 | "title": "Disable tooltips on hover?", 94 | "type": "boolean", 95 | "default": false, 96 | "description": "Disable tooltips when hovering on XML in MSBuild project files?" 97 | }, 98 | "msbuildProjectTools.msbuild.extensionsPath": { 99 | "type": "string", 100 | "default": null, 101 | "description": "Override the MSBuildExtensionsPath property." 102 | }, 103 | "msbuildProjectTools.msbuild.extensionsPath32": { 104 | "type": "string", 105 | "default": null, 106 | "description": "Override the MSBuildExtensionsPath32 property." 107 | }, 108 | "msbuildProjectTools.msbuild.globalProperties": { 109 | "type": "object", 110 | "default": {}, 111 | "description": "Override the default MSBuild properties used when a project is first loaded." 112 | }, 113 | "msbuildProjectTools.experimentalFeatures": { 114 | "type": "array", 115 | "items": { 116 | "type": "string", 117 | "enum": [ 118 | "empty-completion-lists" 119 | ] 120 | }, 121 | "default": [], 122 | "description": "The names of experimental features (if any) to enable. If you're not sure what this is, you probably don't need it." 123 | }, 124 | "msbuildProjectTools.nuget.newestVersionsFirst": { 125 | "type": "boolean", 126 | "default": true, 127 | "description": "Sort package versions in descending order (i.e. newest versions first)? Set this to false to revert to the old behaviour (VSCode decides how to sort the completion list)." 128 | }, 129 | "msbuildProjectTools.nuget.includePreRelease": { 130 | "type": "boolean", 131 | "default": false, 132 | "description": "Include suggestions for pre-release packages and package versions." 133 | }, 134 | "msbuildProjectTools.nuget.includeLocalSources": { 135 | "type": "boolean", 136 | "default": false, 137 | "description": "Include suggestions for packages from local (file-based) package sources. You'll need to close and reopen your project file for this setting to take effect." 138 | }, 139 | "msbuildProjectTools.nuget.ignorePackageSources": { 140 | "type": "array", 141 | "items": { 142 | "type": "string" 143 | }, 144 | "default": [], 145 | "description": "The names/URIs of configured NuGet package sources that should be ignored (i.e. not be used) by MSBuild Project Tools." 146 | }, 147 | "msbuildProjectTools.nuget.disablePreFetch": { 148 | "type": "boolean", 149 | "default": false, 150 | "description": "Don't automatically initialise the NuGet API client when opening a project (this is done to make the first PackageReference completion faster)?" 151 | } 152 | } 153 | }, 154 | "snippets": [ 155 | { 156 | "language": "msbuild", 157 | "path": "./snippets/msbuild-project.json" 158 | } 159 | ], 160 | "grammars": [ 161 | { 162 | "scopeName": "text.xml.msbuild.expression", 163 | "injectTo": [ 164 | "text.xml.msbuild" 165 | ], 166 | "path": "./syntaxes/msbuild.expression.json" 167 | }, 168 | { 169 | "language": "msbuild", 170 | "scopeName": "text.xml.msbuild", 171 | "path": "./syntaxes/msbuild.json" 172 | } 173 | ], 174 | "languages": [ 175 | { 176 | "id": "msbuild", 177 | "aliases": [ 178 | "MSBuild" 179 | ], 180 | "extensions": [ 181 | "csproj", 182 | "fsproj", 183 | "props", 184 | "targets", 185 | "msbuild" 186 | ], 187 | "configuration": "./language-configuration.json" 188 | } 189 | ], 190 | "xmlLanguageParticipants": [ 191 | { 192 | "languageId": "msbuild" 193 | } 194 | ] 195 | }, 196 | "scripts": { 197 | "vscode:prepublish": "npm run build-production", 198 | "build-language-server": "powershell ./Publish-LanguageServer.ps1", 199 | "build-dev": "webpack --mode development", 200 | "build-production": "webpack --mode production" 201 | }, 202 | "extensionDependencies": [ 203 | "ms-dotnettools.vscode-dotnet-runtime" 204 | ], 205 | "devDependencies": { 206 | "@types/node": "^18.15.0", 207 | "@types/object-path": "^0.11.1", 208 | "@types/semver": "^7.5.8", 209 | "@types/vscode": "^1.82.0", 210 | "@types/which": "^3.0.3", 211 | "@vscode/vsce": "2.22.0", 212 | "ts-loader": "^9.5.0", 213 | "tslint": "^5.20.0", 214 | "typescript": "^4.1.6", 215 | "webpack": "^5.94.0", 216 | "webpack-cli": "^5.1.4" 217 | }, 218 | "dependencies": { 219 | "semver": "^7.6.0", 220 | "vscode-languageclient": "^9.0.1", 221 | "which": "^4.0.0" 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /snippets/msbuild-project.json: -------------------------------------------------------------------------------- 1 | { 2 | "ASP.NET Core 2.0 Metapackage": { 3 | "prefix": "aspnetcore20", 4 | "body": [ 5 | "", 6 | " ", 7 | "", 8 | "" 9 | ], 10 | "description": "Load Metapackage for ASP.NET Core" 11 | }, 12 | "ASP.NET Core 2.0 Full Packages": { 13 | "prefix": "aspnetcore20-full", 14 | "body": [ 15 | "", 16 | " ", 17 | " ", 18 | " ", 19 | " ", 20 | " ", 21 | " ", 22 | " ", 23 | " ", 24 | " ", 25 | " ", 26 | " ", 27 | " ", 28 | " ", 29 | " ", 30 | " ", 31 | " ", 32 | " ", 33 | " ", 34 | " ", 35 | " ", 36 | " ", 37 | " ", 38 | " ", 39 | " ", 40 | " ", 41 | " ", 42 | " ", 43 | " ", 44 | " ", 45 | " ", 46 | " ", 47 | " ", 48 | " ", 49 | " ", 50 | " ", 51 | " ", 52 | " ", 53 | " ", 54 | " ", 55 | " ", 56 | " ", 57 | " ", 58 | " ", 59 | " ", 60 | " ", 61 | " ", 62 | " ", 63 | " ", 64 | " ", 65 | " ", 66 | " ", 67 | " ", 68 | " ", 69 | " ", 70 | " ", 71 | " ", 72 | " ", 73 | " ", 74 | " ", 75 | " ", 76 | " ", 77 | " ", 78 | " ", 79 | " ", 80 | " ", 81 | " ", 82 | " ", 83 | " ", 84 | " ", 85 | " ", 86 | " ", 87 | " ", 88 | " ", 89 | " ", 90 | " ", 91 | " ", 92 | " ", 93 | " ", 94 | " ", 95 | " ", 96 | " ", 97 | " ", 98 | " ", 99 | " ", 100 | " ", 101 | " ", 102 | " ", 103 | " ", 104 | " ", 105 | " ", 106 | " ", 107 | " ", 108 | " ", 109 | " ", 110 | " ", 111 | " ", 112 | " ", 113 | " ", 114 | " ", 115 | " ", 116 | " ", 117 | " ", 118 | " ", 119 | " ", 120 | " ", 121 | " ", 122 | " ", 123 | " ", 124 | " ", 125 | " ", 126 | " ", 127 | " ", 128 | " ", 129 | " ", 130 | " ", 131 | " ", 132 | " ", 133 | " ", 134 | " ", 135 | " ", 136 | " ", 137 | " ", 138 | " ", 139 | " ", 140 | " ", 141 | " ", 142 | " ", 143 | " ", 144 | " ", 145 | " ", 146 | " ", 147 | " ", 148 | " ", 149 | " ", 150 | " ", 151 | " ", 152 | "", 153 | "" 154 | ], 155 | "description": "Load Metapackage for ASP.NET Core" 156 | }, 157 | "Microsoft.DotNet.Watcher.Tools": { 158 | "prefix": "dotnet-watcher-tools", 159 | "body": [ 160 | "", 161 | " ", 162 | "", 163 | "" 164 | ], 165 | "description": "Load DotNetCliToolReference for Microsoft.DotNet.Watcher.Tools" 166 | }, 167 | "Set to IsTransformWebConfigDisabled": { 168 | "prefix": "iis-deployment", 169 | "body": [ 170 | "", 171 | " true", 172 | "", 173 | "" 174 | ], 175 | "description": "Enable IIS Deployment Settings" 176 | }, 177 | "NuGet Properties": { 178 | "prefix": "nuget-properties", 179 | "body": [ 180 | "", 181 | " ${1:PackageId}", 182 | " ${1}", 183 | " ${2:1.0.0}", 184 | " ${3:Your Name}", 185 | " ${4:MyCompany Inc}", 186 | " ${5:Product Name}", 187 | " ${6:Package Description}", 188 | " ${7:© MyCompany Corporation. All rights reserved.}", 189 | 190 | " ${8:http://Your.License.Url}", 191 | " ${9:http://Your.Project.Url}", 192 | " ${10:http://Your.Icon.Url}", 193 | " ${11:space-separated tag list}", 194 | " ${12:http://Your.ReleaseNotes.Url}", 195 | " ${13|false,true|}", 196 | 197 | " ${14:https://github.com/user/repo}", 198 | " ${15:git}", 199 | 200 | " ${16|true,false|}", 201 | " ${17|true,false|}", 202 | " ${18|false,true|}", 203 | "", 204 | "" 205 | ], 206 | "description": "MSBuild NuGet Properties" 207 | }, 208 | "Multi-Targeting PropertyGroup in .NET Standard": { 209 | "prefix": "multi-targeting-propertygroup", 210 | "body": [ 211 | "", 212 | " $0", 213 | "", 214 | "" 215 | ], 216 | "description": "Multi-Targeting PropertyGroup in .NET Standard" 217 | }, 218 | "Multi-Targeting ItemGroup in .NET Standard": { 219 | "prefix": "multi-targeting-itemgroup", 220 | "body": [ 221 | "", 222 | " $0", 223 | "", 224 | "" 225 | ], 226 | "description": "Multi-Targeting ItemGroup in .NET Standard" 227 | }, 228 | "SourceLink: Source Code On Demand": { 229 | "prefix": "sourcelink", 230 | "body": [ 231 | "", 232 | " ", 233 | "", 234 | "" 235 | ], 236 | "description": "Source Link your Portable PDB files to allow source code to be downloaded on demand" 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/dotnet.ts: -------------------------------------------------------------------------------- 1 | import { exec } from 'child_process'; 2 | import * as vscode from 'vscode'; 3 | import semver = require('semver'); 4 | import which = require('which'); 5 | 6 | const requiredDotnetRuntimeVersion = '8.0'; 7 | 8 | export type RuntimeDiscoveryResult = 9 | | { success: true, dotnetExecutablePath: string, canBeUsedForRunningLanguageServer: boolean } 10 | | { success: false, failure: RuntimeDiscoveryFailure }; 11 | 12 | export const enum RuntimeDiscoveryFailure { DotnetNotFoundInPath, ErrorWhileGettingRuntimesList }; 13 | 14 | export async function discoverUserRuntime() : Promise { 15 | const dotnetHostPath = await which('dotnet', { nothrow: true }); 16 | 17 | if (dotnetHostPath === null) { 18 | return { success: false, failure: RuntimeDiscoveryFailure.DotnetNotFoundInPath }; 19 | } 20 | 21 | const dotnetRuntimeListOutput = await new Promise((resolve) => { 22 | exec(`"${dotnetHostPath}" --list-runtimes`, (error, stdOut, stdErr) => { 23 | return (error || stdErr) ? resolve(null) : resolve(stdOut); 24 | }); 25 | }); 26 | 27 | if (dotnetRuntimeListOutput === null) { 28 | return { success: false, failure: RuntimeDiscoveryFailure.ErrorWhileGettingRuntimesList }; 29 | } 30 | 31 | const runtimeStringLines = dotnetRuntimeListOutput.match(/[^\r\n]+/g); 32 | const runtimes = runtimeStringLines.map(line => { 33 | const match = line.match(/^(.+?)\s+([\d.]+(?:-[\w.]+)?)\s+\[(.+?)\]$/); 34 | if (match) { 35 | return { 36 | type: match[1], 37 | version: match[2], 38 | path: match[3] 39 | }; 40 | } 41 | }).filter(Boolean); 42 | 43 | // We will force .NET to use the latest runtime available for running language server 44 | // since MSBuild might need to load assemblies of newer versions. 45 | // Therefore we pick runtimes of the same major version or higher 46 | const requiredMajorVersion = semver.major(`${requiredDotnetRuntimeVersion}.0`); 47 | const hasCompatibleRuntime = runtimes.filter(r => r.type === "Microsoft.NETCore.App" && semver.major(r.version) >= requiredMajorVersion).length > 0; 48 | 49 | return { 50 | success: true, 51 | dotnetExecutablePath: dotnetHostPath, 52 | canBeUsedForRunningLanguageServer: hasCompatibleRuntime 53 | }; 54 | } 55 | 56 | interface DotnetAcquireResult { 57 | dotnetPath: string; 58 | } 59 | 60 | export async function acquireIsolatedRuntime(extensionId: string) : Promise { 61 | const dotnetAcquireArgs = { version: requiredDotnetRuntimeVersion, requestingExtensionId: extensionId }; 62 | let status = await vscode.commands.executeCommand('dotnet.acquireStatus', dotnetAcquireArgs); 63 | if (status === undefined) { 64 | await vscode.commands.executeCommand('dotnet.showAcquisitionLog'); 65 | status = await vscode.commands.executeCommand('dotnet.acquire', dotnetAcquireArgs); 66 | } 67 | 68 | if (!status?.dotnetPath) { 69 | return null; 70 | } 71 | 72 | return status.dotnetPath; 73 | } 74 | 75 | export async function acquireDependencies(dotnetExecutablePath : string, dotnetAppPath: string) : Promise { 76 | await vscode.commands.executeCommand('dotnet.ensureDotnetDependencies', { command: dotnetExecutablePath, arguments: [dotnetAppPath] }); 77 | } 78 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { realpathSync } from 'fs'; 4 | import * as vscode from 'vscode'; 5 | import { LanguageClientOptions, ErrorAction, CloseAction, RevealOutputChannelOn } from 'vscode-languageclient'; 6 | import { LanguageClient, ServerOptions } from 'vscode-languageclient/lib/node/main'; 7 | import { Trace } from 'vscode-jsonrpc/lib/node/main'; 8 | 9 | import * as dotnet from './dotnet'; 10 | import { handleBusyNotifications } from './notifications'; 11 | import { registerInternalCommands } from './internal-commands'; 12 | 13 | let languageClient: LanguageClient; 14 | let statusBarItem: vscode.StatusBarItem; 15 | let outputChannel: vscode.OutputChannel; 16 | 17 | const languageServerEnvironment = Object.assign({}, process.env); 18 | 19 | const projectDocumentSelector: vscode.DocumentSelector = [ 20 | { language: 'xml', pattern: '**/*.*proj' }, 21 | { language: 'xml', pattern: '**/*.props' }, 22 | { language: 'xml', pattern: '**/*.targets' }, 23 | { language: 'xml', pattern: '**/*.tasks' }, 24 | { language: 'msbuild', pattern: '**/*.*' } 25 | ]; 26 | 27 | /** 28 | * Called when the extension is activated. 29 | * 30 | * @param context The extension context. 31 | */ 32 | export async function activate(context: vscode.ExtensionContext): Promise { 33 | outputChannel = vscode.window.createOutputChannel('MSBuild Project Tools'); 34 | 35 | const progressOptions: vscode.ProgressOptions = { 36 | location: vscode.ProgressLocation.Window 37 | }; 38 | await vscode.window.withProgress(progressOptions, async progress => { 39 | progress.report({ 40 | message: 'Initialising MSBuild project tools...' 41 | }); 42 | 43 | const hostRuntimeDiscoveryResult = await dotnet.discoverUserRuntime(); 44 | 45 | if (!hostRuntimeDiscoveryResult.success) { 46 | const failureResult = hostRuntimeDiscoveryResult as { failure: dotnet.RuntimeDiscoveryFailure }; 47 | switch (failureResult.failure) { 48 | case dotnet.RuntimeDiscoveryFailure.DotnetNotFoundInPath: 49 | outputChannel.appendLine('"dotnet" command was not found in the PATH. Please make sure "dotnet" is available from the PATH and reload extension since it is required for it to work'); 50 | vscode.window.showErrorMessage('"dotnet" was not found in the PATH (see the output window for details).'); 51 | break; 52 | case dotnet.RuntimeDiscoveryFailure.ErrorWhileGettingRuntimesList: 53 | outputChannel.appendLine('Error occured while trying to execute "dotnet --list-runtimes" command'); 54 | vscode.window.showErrorMessage('Error occured while trying to invoke "dotnet" command (see the output window for details).'); 55 | break; 56 | } 57 | return; 58 | } 59 | 60 | await createLanguageClient(context, hostRuntimeDiscoveryResult); 61 | 62 | context.subscriptions.push( 63 | handleExpressionAutoClose() 64 | ); 65 | 66 | registerInternalCommands(context); 67 | }); 68 | 69 | context.subscriptions.push( 70 | vscode.workspace.onDidChangeConfiguration(async args => { 71 | if (languageClient && args.affectsConfiguration('msbuildProjectTools.logging.trace')) { 72 | const trace = vscode.workspace.getConfiguration('msbuildProjectTools.logging').trace ? Trace.Verbose : Trace.Off; 73 | await languageClient.setTrace(trace); 74 | } 75 | }) 76 | ); 77 | } 78 | 79 | /** 80 | * Called when the extension is deactivated. 81 | */ 82 | export async function deactivate(): Promise { 83 | await languageClient.stop(); 84 | } 85 | 86 | /** 87 | * Create the MSBuild language client. 88 | * 89 | * @param context The current extension context. 90 | * @returns A promise that resolves to the language client. 91 | */ 92 | async function createLanguageClient(context: vscode.ExtensionContext, dotnetOnHost: { dotnetExecutablePath: string, canBeUsedForRunningLanguageServer: boolean }): Promise { 93 | statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 50); 94 | context.subscriptions.push(statusBarItem); 95 | 96 | statusBarItem.text = '$(check) MSBuild Project'; 97 | statusBarItem.tooltip = 'MSBuild Project Tools'; 98 | statusBarItem.hide(); 99 | 100 | outputChannel.appendLine('Starting MSBuild language service...'); 101 | 102 | const clientOptions: LanguageClientOptions = { 103 | synchronize: { 104 | configurationSection: 'msbuildProjectTools' 105 | }, 106 | diagnosticCollectionName: 'MSBuild Project', 107 | errorHandler: { 108 | error: (error, message, count) => { 109 | if (count > 2) // Don't be annoying 110 | return { action: ErrorAction.Shutdown }; 111 | 112 | console.log(message); 113 | console.log(error); 114 | 115 | if (message) 116 | outputChannel.appendLine(`The MSBuild language server encountered an unexpected error: ${message}\n\n${error}`); 117 | else 118 | outputChannel.appendLine(`The MSBuild language server encountered an unexpected error.\n\n${error}`); 119 | 120 | return { action: ErrorAction.Continue }; 121 | }, 122 | closed: () => { return { action: CloseAction.DoNotRestart } } 123 | }, 124 | initializationFailedHandler(error: Error) : boolean { 125 | console.log(error); 126 | 127 | outputChannel.appendLine(`Failed to initialise the MSBuild language server.\n\n${error}`); 128 | vscode.window.showErrorMessage(`Failed to initialise MSBuild language server.\n\n${error}`); 129 | 130 | return false; // Don't attempt to restart the language server. 131 | }, 132 | revealOutputChannelOn: RevealOutputChannelOn.Never 133 | }; 134 | 135 | const loggingConfig = vscode.workspace.getConfiguration('msbuildProjectTools.logging'); 136 | 137 | const seqLoggingSettings = loggingConfig.seq; 138 | if (seqLoggingSettings?.url) { 139 | languageServerEnvironment['MSBUILD_PROJECT_TOOLS_SEQ_URL'] = seqLoggingSettings.url; 140 | languageServerEnvironment['MSBUILD_PROJECT_TOOLS_SEQ_API_KEY'] = seqLoggingSettings.apiKey; 141 | } 142 | 143 | if (loggingConfig.file) { 144 | languageServerEnvironment['MSBUILD_PROJECT_TOOLS_LOG_FILE'] = loggingConfig.file; 145 | } 146 | 147 | if (loggingConfig.level === 'Verbose') { 148 | languageServerEnvironment['MSBUILD_PROJECT_TOOLS_VERBOSE_LOGGING'] = '1'; 149 | } 150 | 151 | const serverAssembly = context.asAbsolutePath('language-server/MSBuildProjectTools.LanguageServer.Host.dll'); 152 | let dotnetForLanguageServer = dotnetOnHost.dotnetExecutablePath; 153 | 154 | if (!dotnetOnHost.canBeUsedForRunningLanguageServer) { 155 | const isolatedDotnet = await dotnet.acquireIsolatedRuntime(context.extension.id); 156 | 157 | if (isolatedDotnet === null) { 158 | const baseErrorMessage = 'Cannot enable the MSBuild language service: unable to acquire isolated .NET runtime'; 159 | outputChannel.appendLine(baseErrorMessage + ". See '.NET Runtime' channel for more info"); 160 | await vscode.window.showErrorMessage(baseErrorMessage); 161 | return; 162 | } 163 | 164 | await dotnet.acquireDependencies(isolatedDotnet, serverAssembly); 165 | 166 | dotnetForLanguageServer = isolatedDotnet; 167 | outputChannel.appendLine("Using isolated .NET runtime"); 168 | } else { 169 | languageServerEnvironment['DOTNET_ROLL_FORWARD'] = 'LatestMajor'; 170 | languageServerEnvironment['DOTNET_ROLL_FORWARD_TO_PRERELEASE'] = '1'; 171 | 172 | outputChannel.appendLine("Using .NET runtime from the host"); 173 | } 174 | 175 | languageServerEnvironment['DOTNET_HOST_PATH'] = realpathSync(dotnetOnHost.dotnetExecutablePath); 176 | 177 | const serverOptions: ServerOptions = { 178 | command: dotnetForLanguageServer, 179 | args: [serverAssembly], 180 | options: { 181 | env: languageServerEnvironment 182 | } 183 | }; 184 | 185 | languageClient = new LanguageClient('MSBuild Language Service', serverOptions, clientOptions); 186 | const trace = loggingConfig.trace ? Trace.Verbose : Trace.Off; 187 | await languageClient.setTrace(trace); 188 | 189 | try { 190 | await languageClient.start(); 191 | handleBusyNotifications(languageClient, statusBarItem); 192 | outputChannel.appendLine('MSBuild language service is running.'); 193 | } 194 | catch (startFailed) { 195 | outputChannel.appendLine(`Failed to start MSBuild language service.\n\n${startFailed}`); 196 | return; 197 | } 198 | } 199 | 200 | /** 201 | * Handle document-change events to automatically insert a closing parenthesis for common MSBuild expressions. 202 | */ 203 | function handleExpressionAutoClose(): vscode.Disposable { 204 | return vscode.workspace.onDidChangeTextDocument(async args => { 205 | if (!vscode.languages.match(projectDocumentSelector, args.document)) 206 | return; 207 | 208 | if (args.contentChanges.length !== 1) 209 | return; // Completion doesn't make sense with multiple cursors. 210 | 211 | const contentChange = args.contentChanges[0]; 212 | if (isOriginPosition(contentChange.range.start)) 213 | return; // We're at the start of the document; no previous character to check. 214 | 215 | if (contentChange.text === '(') { 216 | // Select the previous character and the one they just typed. 217 | const range = contentChange.range.with( 218 | contentChange.range.start.translate(0, -1), 219 | contentChange.range.end.translate(0, 1) 220 | ); 221 | 222 | const openExpression = args.document.getText(range); 223 | switch (openExpression) { 224 | case '$(': // Eval open 225 | case '@(': // Item group open 226 | case '%(': // Item metadata open 227 | { 228 | break; 229 | } 230 | default: 231 | { 232 | return; 233 | } 234 | } 235 | 236 | // Replace open expression with a closed one. 237 | const closedExpression = openExpression + ')'; 238 | await vscode.window.activeTextEditor.edit( 239 | edit => edit.replace(range, closedExpression) 240 | ); 241 | 242 | // Move between the parentheses and trigger completion. 243 | await vscode.commands.executeCommand('msbuildProjectTools.internal.moveAndSuggest', 244 | 'left', // moveTo 245 | 'character', // moveBy 246 | 1 // moveCount 247 | ); 248 | } 249 | }); 250 | } 251 | 252 | /** 253 | * Determine whether the specified {@link vscode.Position} represents the origin position. 254 | * 255 | * @param position The {@link vscode.Position} to examine. 256 | */ 257 | function isOriginPosition(position: vscode.Position): boolean { 258 | return position.line === 0 && position.character === 0; 259 | } 260 | -------------------------------------------------------------------------------- /src/internal-commands.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | /** 4 | * Register the extension's internal commands. 5 | * 6 | * @param context The current extension context. 7 | */ 8 | export function registerInternalCommands(context: vscode.ExtensionContext): void 9 | { 10 | context.subscriptions.push( 11 | vscode.commands.registerTextEditorCommand('msbuildProjectTools.internal.moveAndSuggest', moveAndSuggest) 12 | ); 13 | } 14 | 15 | /** 16 | * Move the cursor and trigger completion. 17 | * 18 | * @param editor The text editor where the command was invoked. 19 | * @param edit The text editor's edit facility. 20 | * @param moveTo The logical direction in which to move (e.g. 'left', 'right', 'up', 'down', etc). 21 | * @param moveBy The unit to move by (e.g. 'line', 'wrappedLine', 'character', 'halfLine'). 22 | * @param moveCount The number of units to move by. 23 | */ 24 | async function moveAndSuggest(editor: vscode.TextEditor, edit: vscode.TextEditorEdit, moveTo: string, moveBy: string, moveCount: number): Promise { 25 | if (!moveTo || !moveBy || !moveCount) 26 | return; 27 | 28 | // Move. 29 | await vscode.commands.executeCommand('cursorMove', { 30 | value: moveCount, 31 | to: moveTo, 32 | by: moveBy 33 | }); 34 | 35 | // Trigger completion. 36 | await vscode.commands.executeCommand('editor.action.triggerSuggest'); 37 | } 38 | -------------------------------------------------------------------------------- /src/notifications.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { NotificationType } from 'vscode-jsonrpc'; 3 | import { LanguageClient } from 'vscode-languageclient/lib/node/main'; 4 | 5 | /** 6 | * Represents a custom notification indicating that the MSBuild language service is or is not busy. 7 | */ 8 | export interface BusyNotification { 9 | /** Is the language service busy? */ 10 | isBusy: boolean; 11 | 12 | /** If the language service is busy, a short message describing why. */ 13 | message: string; 14 | } 15 | 16 | /** 17 | * Well-known notification types. 18 | */ 19 | export namespace NotificationTypes { 20 | /** The {@link BusyNotification} type. */ 21 | export const busy = new NotificationType('msbuild/busy'); 22 | } 23 | 24 | /** 25 | * Configure the language client to handle "language service is busy" notifications. 26 | * 27 | * @param languageClient The MSBuild language client. 28 | */ 29 | export function handleBusyNotifications(languageClient: LanguageClient, statusBarItem: vscode.StatusBarItem): void { 30 | languageClient.onNotification(NotificationTypes.busy, notification => { 31 | if (notification.isBusy) { 32 | statusBarItem.text = '$(watch) MSBuild Project'; 33 | statusBarItem.tooltip = 'MSBuild Project Tools: ' + notification.message; 34 | statusBarItem.show(); 35 | } else { 36 | statusBarItem.text = '$(check) MSBuild Project'; 37 | statusBarItem.tooltip = 'MSBuild Project Tools'; 38 | statusBarItem.hide(); 39 | } 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /syntaxes/msbuild.expression.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "injectionSelector": "text.xml.msbuild", 4 | "name": "MSBuildExpression", 5 | "scopeName": "text.xml.msbuild.expression", 6 | "patterns": [{ 7 | "include": "#singleQuotedString" 8 | }, 9 | { 10 | "include": "#expressions" 11 | }, 12 | { 13 | "include": "#itemSeparator" 14 | } 15 | ], 16 | "repository": { 17 | "operators": { 18 | "patterns": [{ 19 | "match": "(==)", 20 | "captures": { 21 | "1": { 22 | "name": "keyword.operator" 23 | } 24 | } 25 | }, 26 | { 27 | "match": "(!=)", 28 | "captures": { 29 | "1": { 30 | "name": "keyword.operator" 31 | } 32 | } 33 | } 34 | ] 35 | }, 36 | "expressions": { 37 | "patterns": [{ 38 | "comment": "Property expression", 39 | "match": "(\\$\\()([\\w_]+)(\\))", 40 | "captures": { 41 | "1": { 42 | "name": "keyword.operator" 43 | }, 44 | "2": { 45 | "name": "variable.name" 46 | }, 47 | "3": { 48 | "name": "keyword.operator" 49 | } 50 | } 51 | }, 52 | { 53 | "comment": "Item group transform expression", 54 | "match": "(@\\()([\\w_]+)(->)(')([^']+)(')(\\))", 55 | "captures": { 56 | "1": { 57 | "name": "keyword.operator" 58 | }, 59 | "2": { 60 | "name": "variable.name" 61 | }, 62 | "3": { 63 | "name": "keyword.operator" 64 | }, 65 | "4": { 66 | "name": "markup.quote" 67 | }, 68 | "5": { 69 | "patterns": [{ 70 | "include": "#expressions" 71 | }, 72 | { 73 | "include": "#directItemMetadataExpression" 74 | } 75 | ] 76 | }, 77 | "6": { 78 | "name": "markup.quote" 79 | }, 80 | "7": { 81 | "name": "keyword.operator" 82 | } 83 | } 84 | }, 85 | { 86 | "comment": "Item group expression", 87 | "match": "(@\\()([\\w_]+)(\\))", 88 | "captures": { 89 | "1": { 90 | "name": "keyword.operator" 91 | }, 92 | "2": { 93 | "name": "variable.name" 94 | }, 95 | "3": { 96 | "name": "keyword.operator" 97 | } 98 | } 99 | }, 100 | { 101 | "comment": "Item group metadata expression", 102 | "match": "(%\\()([\\w_]+)(\\.)([\\w_]+)(\\))", 103 | "captures": { 104 | "1": { 105 | "name": "keyword.operator" 106 | }, 107 | "2": { 108 | "name": "variable.name" 109 | }, 110 | "3": { 111 | "name": "keyword.operator" 112 | }, 113 | "4": { 114 | "name": "storage" 115 | }, 116 | "5": { 117 | "name": "keyword.operator" 118 | } 119 | } 120 | } 121 | ] 122 | }, 123 | "itemSeparator": { 124 | "patterns": [{ 125 | "match": "(;)", 126 | "captures": { 127 | "1": { 128 | "name": "keyword.operator" 129 | } 130 | } 131 | }] 132 | }, 133 | "directItemMetadataExpression": { 134 | "patterns": [{ 135 | "comment": "Direct item metadata expression", 136 | "match": "(%\\()([\\w_]+)(\\))", 137 | "captures": { 138 | "1": { 139 | "name": "keyword.operator" 140 | }, 141 | "2": { 142 | "name": "variable.name" 143 | }, 144 | "3": { 145 | "name": "keyword.operator" 146 | } 147 | } 148 | }] 149 | }, 150 | "singleQuotedString": { 151 | "patterns": [{ 152 | "match": "(')([^']*)(')", 153 | "captures": { 154 | "1": { 155 | "name": "markup.quote" 156 | }, 157 | "2": { 158 | "name": "string.quoted.single", 159 | "patterns": [{ 160 | "include": "#operators" 161 | }, 162 | { 163 | "include": "#expressions" 164 | } 165 | ] 166 | }, 167 | "3": { 168 | "name": "markup.quote" 169 | } 170 | } 171 | }] 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /syntaxes/msbuild.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "version": "https://github.com/tintoy/msbuild-project-tools-vscode/blob/980a97501456a61a0741575403ed1c5b90a0c545/syntaxes/msbuild.json", 4 | "scopeName": "text.xml.msbuild", 5 | "name": "MSBuild", 6 | "patterns": [ 7 | { 8 | "begin": "(<\\?)\\s*([-_a-zA-Z0-9]+)", 9 | "captures": { 10 | "1": { 11 | "name": "punctuation.definition.tag.xml" 12 | }, 13 | "2": { 14 | "name": "entity.name.tag.xml" 15 | } 16 | }, 17 | "end": "(\\?>)", 18 | "name": "meta.tag.preprocessor.xml", 19 | "patterns": [ 20 | { 21 | "match": " ([a-zA-Z-]+)", 22 | "name": "entity.other.attribute-name.xml" 23 | }, 24 | { 25 | "include": "#doublequotedString" 26 | }, 27 | { 28 | "include": "#singlequotedString" 29 | } 30 | ] 31 | }, 32 | { 33 | "begin": "()", 46 | "name": "meta.tag.sgml.doctype.xml", 47 | "patterns": [ 48 | { 49 | "include": "#internalSubset" 50 | } 51 | ] 52 | }, 53 | { 54 | "include": "#comments" 55 | }, 56 | { 57 | "begin": "(<)((?:([-_a-zA-Z0-9]+)(:))?([-_a-zA-Z0-9:]+))(?=(\\s[^>]*)?>)", 58 | "beginCaptures": { 59 | "1": { 60 | "name": "punctuation.definition.tag.xml" 61 | }, 62 | "2": { 63 | "name": "entity.name.tag.xml" 64 | }, 65 | "3": { 66 | "name": "entity.name.tag.namespace.xml" 67 | }, 68 | "4": { 69 | "name": "punctuation.separator.namespace.xml" 70 | }, 71 | "5": { 72 | "name": "entity.name.tag.localname.xml" 73 | } 74 | }, 75 | "end": "(>)()", 76 | "endCaptures": { 77 | "1": { 78 | "name": "punctuation.definition.tag.xml" 79 | }, 80 | "2": { 81 | "name": "punctuation.definition.tag.xml" 82 | }, 83 | "3": { 84 | "name": "entity.name.tag.xml" 85 | }, 86 | "4": { 87 | "name": "entity.name.tag.namespace.xml" 88 | }, 89 | "5": { 90 | "name": "punctuation.separator.namespace.xml" 91 | }, 92 | "6": { 93 | "name": "entity.name.tag.localname.xml" 94 | }, 95 | "7": { 96 | "name": "punctuation.definition.tag.xml" 97 | } 98 | }, 99 | "name": "meta.tag.no-content.xml", 100 | "patterns": [ 101 | { 102 | "include": "#tagStuff" 103 | } 104 | ] 105 | }, 106 | { 107 | "begin": "()", 126 | "name": "meta.tag.xml", 127 | "patterns": [ 128 | { 129 | "include": "#tagStuff" 130 | } 131 | ] 132 | }, 133 | { 134 | "include": "#entity" 135 | }, 136 | { 137 | "include": "#bare-ampersand" 138 | }, 139 | { 140 | "begin": "<%@", 141 | "beginCaptures": { 142 | "0": { 143 | "name": "punctuation.section.embedded.begin.xml" 144 | } 145 | }, 146 | "end": "%>", 147 | "endCaptures": { 148 | "0": { 149 | "name": "punctuation.section.embedded.end.xml" 150 | } 151 | }, 152 | "name": "source.java-props.embedded.xml", 153 | "patterns": [ 154 | { 155 | "match": "page|include|taglib", 156 | "name": "keyword.other.page-props.xml" 157 | } 158 | ] 159 | }, 160 | { 161 | "begin": "<%[!=]?(?!--)", 162 | "beginCaptures": { 163 | "0": { 164 | "name": "punctuation.section.embedded.begin.xml" 165 | } 166 | }, 167 | "end": "(?!--)%>", 168 | "endCaptures": { 169 | "0": { 170 | "name": "punctuation.section.embedded.end.xml" 171 | } 172 | }, 173 | "name": "source.java.embedded.xml", 174 | "patterns": [ 175 | { 176 | "include": "source.java" 177 | } 178 | ] 179 | }, 180 | { 181 | "begin": "", 188 | "endCaptures": { 189 | "0": { 190 | "name": "punctuation.definition.string.end.xml" 191 | } 192 | }, 193 | "name": "string.unquoted.cdata.xml" 194 | } 195 | ], 196 | "repository": { 197 | "EntityDecl": { 198 | "begin": "()", 217 | "patterns": [ 218 | { 219 | "include": "#doublequotedString" 220 | }, 221 | { 222 | "include": "#singlequotedString" 223 | } 224 | ] 225 | }, 226 | "bare-ampersand": { 227 | "match": "&", 228 | "name": "invalid.illegal.bad-ampersand.xml", 229 | "patterns": [] 230 | }, 231 | "doublequotedString": { 232 | "begin": "\"", 233 | "beginCaptures": { 234 | "0": { 235 | "name": "punctuation.definition.string.begin.xml" 236 | } 237 | }, 238 | "end": "\"", 239 | "endCaptures": { 240 | "0": { 241 | "name": "punctuation.definition.string.end.xml" 242 | } 243 | }, 244 | "name": "string.quoted.double.xml", 245 | "patterns": [ 246 | { 247 | "include": "#entity" 248 | }, 249 | { 250 | "include": "#bare-ampersand" 251 | } 252 | ] 253 | }, 254 | "entity": { 255 | "captures": { 256 | "1": { 257 | "name": "punctuation.definition.constant.xml" 258 | }, 259 | "3": { 260 | "name": "punctuation.definition.constant.xml" 261 | } 262 | }, 263 | "match": "(&)([:a-zA-Z_][:a-zA-Z0-9_.-]*|#[0-9]+|#x[0-9a-fA-F]+)(;)", 264 | "name": "constant.character.entity.xml", 265 | "patterns": [] 266 | }, 267 | "internalSubset": { 268 | "begin": "(\\[)", 269 | "captures": { 270 | "1": { 271 | "name": "punctuation.definition.constant.xml" 272 | } 273 | }, 274 | "end": "(\\])", 275 | "name": "meta.internalsubset.xml", 276 | "patterns": [ 277 | { 278 | "include": "#EntityDecl" 279 | }, 280 | { 281 | "include": "#parameterEntity" 282 | }, 283 | { 284 | "include": "#comments" 285 | } 286 | ] 287 | }, 288 | "parameterEntity": { 289 | "captures": { 290 | "1": { 291 | "name": "punctuation.definition.constant.xml" 292 | }, 293 | "3": { 294 | "name": "punctuation.definition.constant.xml" 295 | } 296 | }, 297 | "match": "(%)([:a-zA-Z_][:a-zA-Z0-9_.-]*)(;)", 298 | "name": "constant.character.parameter-entity.xml", 299 | "patterns": [] 300 | }, 301 | "singlequotedString": { 302 | "begin": "'", 303 | "beginCaptures": { 304 | "0": { 305 | "name": "punctuation.definition.string.begin.xml" 306 | } 307 | }, 308 | "end": "'", 309 | "endCaptures": { 310 | "0": { 311 | "name": "punctuation.definition.string.end.xml" 312 | } 313 | }, 314 | "name": "string.quoted.single.xml", 315 | "patterns": [ 316 | { 317 | "include": "#entity" 318 | }, 319 | { 320 | "include": "#bare-ampersand" 321 | } 322 | ] 323 | }, 324 | "tagStuff": { 325 | "patterns": [ 326 | { 327 | "captures": { 328 | "1": { 329 | "name": "entity.other.attribute-name.namespace.xml" 330 | }, 331 | "2": { 332 | "name": "entity.other.attribute-name.xml" 333 | }, 334 | "3": { 335 | "name": "punctuation.separator.namespace.xml" 336 | }, 337 | "4": { 338 | "name": "entity.other.attribute-name.localname.xml" 339 | } 340 | }, 341 | "match": "(?:^|\\s+)(?:([-\\w.]+)((:)))?([-\\w.:]+)\\s*=" 342 | }, 343 | { 344 | "include": "#doublequotedString" 345 | }, 346 | { 347 | "include": "#singlequotedString" 348 | } 349 | ] 350 | }, 351 | "comments": { 352 | "begin": "<[!%]--", 353 | "captures": { 354 | "0": { 355 | "name": "punctuation.definition.comment.xml" 356 | } 357 | }, 358 | "end": "--%?>", 359 | "name": "comment.block.xml", 360 | "patterns": [] 361 | } 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2017", 5 | "noImplicitAny": false, 6 | "outDir": "out", 7 | "lib": [ 8 | "es2017" 9 | ], 10 | "sourceMap": true, 11 | }, 12 | "exclude": [ 13 | "node_modules", 14 | ".vscode-test" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "member-access": true, 4 | "member-ordering": [ 5 | false 6 | ], 7 | "no-any": false, 8 | "no-inferrable-types": true, 9 | "no-internal-module": true, 10 | "no-var-requires": false, 11 | "typedef": [ 12 | true, 13 | "call-signature", 14 | "parameter", 15 | "property-declaration" 16 | ], 17 | "typedef-whitespace": [ 18 | true, 19 | { 20 | "call-signature": "nospace", 21 | "index-signature": "nospace", 22 | "parameter": "nospace", 23 | "property-declaration": "nospace", 24 | "variable-declaration": "nospace" 25 | }, 26 | { 27 | "call-signature": "onespace", 28 | "index-signature": "onespace", 29 | "parameter": "onespace", 30 | "property-declaration": "onespace", 31 | "variable-declaration": "onespace" 32 | } 33 | ], 34 | "ban": false, 35 | "forin": true, 36 | "label-position": true, 37 | "no-arg": true, 38 | "no-bitwise": false, 39 | "no-conditional-assignment": false, 40 | "no-console": [ 41 | false 42 | ], 43 | "no-construct": true, 44 | "no-debugger": false, 45 | "no-duplicate-variable": true, 46 | "no-empty": false, 47 | "no-eval": true, 48 | "no-shadowed-variable": true, 49 | "no-string-literal": false, 50 | "no-switch-case-fall-through": true, 51 | "no-unused-expression": true, 52 | "no-var-keyword": true, 53 | "radix": true, 54 | "triple-equals": true, 55 | "eofline": true, 56 | "indent": [ 57 | true, 58 | "spaces" 59 | ], 60 | "no-require-imports": false, 61 | "align": [ 62 | true 63 | ], 64 | "class-name": true, 65 | "interface-name": [ 66 | true, 67 | "never-prefix" 68 | ], 69 | "no-consecutive-blank-lines": true, 70 | "one-line": [ 71 | true 72 | ], 73 | "quotemark": [ 74 | true, 75 | "single", 76 | "avoid-escape" 77 | ], 78 | "semicolon": [ 79 | true, 80 | "always" 81 | ], 82 | "variable-name": [ 83 | true, 84 | "ban-keywords", 85 | "check-format", 86 | "allow-leading-underscore" 87 | ], 88 | "whitespace": [ 89 | true 90 | ] 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/extension.ts', 5 | target: 'node', 6 | output: { 7 | path: path.resolve(__dirname, 'dist'), 8 | filename: 'extension.js', 9 | libraryTarget: 'commonjs2', 10 | devtoolModuleFilenameTemplate: '../[resource-path]' 11 | }, 12 | devtool: 'source-map', 13 | externals: { 14 | vscode: 'commonjs vscode' 15 | }, 16 | resolve: { 17 | extensions: ['.ts', '.js'], 18 | conditionNames: ['import', 'require'], 19 | mainFields: ['module', 'main'] 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.ts$/, 25 | exclude: /node_modules/, 26 | use: [ 27 | { 28 | loader: 'ts-loader' 29 | } 30 | ] 31 | } 32 | ] 33 | } 34 | }; 35 | --------------------------------------------------------------------------------