mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode merge-base (#22780)
* Revert "Revert "Merge from vscode merge-base (#22769)" (#22779)"
This reverts commit 47a1745180.
* Fix notebook download task
* Remove done call from extensions-ci
This commit is contained in:
@@ -52,7 +52,8 @@
|
||||
".devcontainer.json"
|
||||
],
|
||||
"filenamePatterns": [
|
||||
"**/User/snippets/*.json"
|
||||
"**/User/snippets/*.json",
|
||||
"**/User/profiles/*/snippets/*.json"
|
||||
]
|
||||
}, {
|
||||
"id": "json",
|
||||
@@ -78,6 +79,10 @@
|
||||
"fileMatch": "%APP_SETTINGS_HOME%/settings.json",
|
||||
"url": "vscode://schemas/settings/user"
|
||||
},
|
||||
{
|
||||
"fileMatch": "%APP_SETTINGS_HOME%/profiles/*/settings.json",
|
||||
"url": "vscode://schemas/settings/profile"
|
||||
},
|
||||
{
|
||||
"fileMatch": "%MACHINE_SETTINGS_HOME%/settings.json",
|
||||
"url": "vscode://schemas/settings/machine"
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"customizations": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"codespaces": {
|
||||
"type": "object",
|
||||
"description": "Customizations specific to GitHub Codespaces",
|
||||
"properties": {
|
||||
"repositories": {
|
||||
"type": "object",
|
||||
"description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9-_.]+[.]*\/[a-zA-Z0-9-_*]+[.]*$": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "object",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"additionalProperties": true,
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"actions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"checks": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"contents": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"deployments": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"discussions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"issues": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"packages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pull_requests": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"repository_projects": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"statuses": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"workflows": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "string",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"enum": [
|
||||
"read-all",
|
||||
"write-all"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"codespaces": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "Codespaces-specific configuration.",
|
||||
"deprecated": true,
|
||||
"deprecationMessage": "Use 'customizations/codespaces' instead"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -406,6 +406,7 @@
|
||||
},
|
||||
"customizations": {
|
||||
"type": "object",
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.",
|
||||
"properties": {
|
||||
"vscode": {
|
||||
"type": "object",
|
||||
@@ -429,13 +430,186 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"codespaces": {
|
||||
"type": "object",
|
||||
"description": "Customizations specific to GitHub Codespaces",
|
||||
"properties": {
|
||||
"repositories": {
|
||||
"type": "object",
|
||||
"description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9-_.]+[.]*/[a-zA-Z0-9-_*]+[.]*$": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "object",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"additionalProperties": true,
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"actions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"checks": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"contents": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"deployments": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"discussions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"issues": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"packages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pull_requests": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"repository_projects": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"statuses": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"workflows": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "string",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"enum": [
|
||||
"read-all",
|
||||
"write-all"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations."
|
||||
"additionalProperties": false
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -842,6 +1016,7 @@
|
||||
},
|
||||
"customizations": {
|
||||
"type": "object",
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.",
|
||||
"properties": {
|
||||
"vscode": {
|
||||
"type": "object",
|
||||
@@ -865,13 +1040,186 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"codespaces": {
|
||||
"type": "object",
|
||||
"description": "Customizations specific to GitHub Codespaces",
|
||||
"properties": {
|
||||
"repositories": {
|
||||
"type": "object",
|
||||
"description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9-_.]+[.]*/[a-zA-Z0-9-_*]+[.]*$": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "object",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"additionalProperties": true,
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"actions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"checks": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"contents": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"deployments": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"discussions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"issues": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"packages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pull_requests": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"repository_projects": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"statuses": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"workflows": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "string",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"enum": [
|
||||
"read-all",
|
||||
"write-all"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations."
|
||||
"additionalProperties": false
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -1244,6 +1592,7 @@
|
||||
},
|
||||
"customizations": {
|
||||
"type": "object",
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.",
|
||||
"properties": {
|
||||
"vscode": {
|
||||
"type": "object",
|
||||
@@ -1267,13 +1616,186 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"codespaces": {
|
||||
"type": "object",
|
||||
"description": "Customizations specific to GitHub Codespaces",
|
||||
"properties": {
|
||||
"repositories": {
|
||||
"type": "object",
|
||||
"description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9-_.]+[.]*/[a-zA-Z0-9-_*]+[.]*$": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "object",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"additionalProperties": true,
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"actions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"checks": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"contents": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"deployments": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"discussions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"issues": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"packages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pull_requests": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"repository_projects": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"statuses": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"workflows": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "string",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"enum": [
|
||||
"read-all",
|
||||
"write-all"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations."
|
||||
"additionalProperties": false
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -1620,6 +2142,7 @@
|
||||
},
|
||||
"customizations": {
|
||||
"type": "object",
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.",
|
||||
"properties": {
|
||||
"vscode": {
|
||||
"type": "object",
|
||||
@@ -1643,13 +2166,186 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"codespaces": {
|
||||
"type": "object",
|
||||
"description": "Customizations specific to GitHub Codespaces",
|
||||
"properties": {
|
||||
"repositories": {
|
||||
"type": "object",
|
||||
"description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9-_.]+[.]*/[a-zA-Z0-9-_*]+[.]*$": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "object",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"additionalProperties": true,
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"actions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"checks": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"contents": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"deployments": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"discussions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"issues": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"packages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pull_requests": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"repository_projects": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"statuses": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"workflows": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "string",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"enum": [
|
||||
"read-all",
|
||||
"write-all"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations."
|
||||
"additionalProperties": false
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -1961,6 +2657,7 @@
|
||||
},
|
||||
"customizations": {
|
||||
"type": "object",
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.",
|
||||
"properties": {
|
||||
"vscode": {
|
||||
"type": "object",
|
||||
@@ -1984,13 +2681,186 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"codespaces": {
|
||||
"type": "object",
|
||||
"description": "Customizations specific to GitHub Codespaces",
|
||||
"properties": {
|
||||
"repositories": {
|
||||
"type": "object",
|
||||
"description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9-_.]+[.]*/[a-zA-Z0-9-_*]+[.]*$": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "object",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"additionalProperties": true,
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"actions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"checks": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"contents": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"deployments": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"discussions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"issues": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"packages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pull_requests": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"repository_projects": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"statuses": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"workflows": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "string",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"enum": [
|
||||
"read-all",
|
||||
"write-all"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations."
|
||||
"additionalProperties": false
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -309,6 +309,7 @@
|
||||
},
|
||||
"customizations": {
|
||||
"type": "object",
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.",
|
||||
"properties": {
|
||||
"vscode": {
|
||||
"type": "object",
|
||||
@@ -331,13 +332,183 @@
|
||||
"description": "The port VS Code can use to connect to its backend."
|
||||
}
|
||||
}
|
||||
},
|
||||
"codespaces": {
|
||||
"type": "object",
|
||||
"description": "Customizations specific to GitHub Codespaces",
|
||||
"properties": {
|
||||
"repositories": {
|
||||
"type": "object",
|
||||
"description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9-_.]+[.]*\/[a-zA-Z0-9-_*]+[.]*$": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "object",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"additionalProperties": true,
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"actions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"checks": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"contents": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"deployments": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"discussions": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"issues": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"packages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pages": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"pull_requests": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"repository_projects": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"statuses": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"read",
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"workflows": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"write"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "string",
|
||||
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
|
||||
"enum": [
|
||||
"read-all",
|
||||
"write-all"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations."
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"customizations": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"vscode": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"description": "An array of extensions that should be installed into the container.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$",
|
||||
"errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"$ref": "vscode://schemas/settings/machine",
|
||||
"description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again."
|
||||
},
|
||||
"devPort": {
|
||||
"type": "integer",
|
||||
"description": "The port VS Code can use to connect to its backend."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"description": "An array of extensions that should be installed into the container.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*)((@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)|@prerelease)?$",
|
||||
"errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'."
|
||||
},
|
||||
"deprecated": true,
|
||||
"deprecationMessage": "Use 'customizations/vscode/extensions' instead"
|
||||
},
|
||||
"settings": {
|
||||
"$ref": "vscode://schemas/settings/machine",
|
||||
"description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again.",
|
||||
"deprecated": true,
|
||||
"deprecationMessage": "Use 'customizations/vscode/settings' instead"
|
||||
},
|
||||
"devPort": {
|
||||
"type": "integer",
|
||||
"description": "The port VS Code can use to connect to its backend.",
|
||||
"deprecated": true,
|
||||
"deprecationMessage": "Use 'customizations/vscode/devPort' instead"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { getLocation, JSONPath, parse, visit } from 'jsonc-parser';
|
||||
import { getLocation, JSONPath, parse, visit, Location } from 'jsonc-parser';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { SettingsDocument } from './settingsDocumentHelper';
|
||||
@@ -39,9 +39,11 @@ function registerVariableCompletions(pattern: string): vscode.Disposable {
|
||||
return vscode.languages.registerCompletionItemProvider({ language: 'jsonc', pattern }, {
|
||||
provideCompletionItems(document, position, _token) {
|
||||
const location = getLocation(document.getText(), document.offsetAt(position));
|
||||
if (!location.isAtPropertyKey && location.previousNode && location.previousNode.type === 'string') {
|
||||
const indexOf$ = document.lineAt(position.line).text.lastIndexOf('$', position.character);
|
||||
const startPosition = indexOf$ >= 0 ? new vscode.Position(position.line, indexOf$) : position;
|
||||
if (isCompletingInsidePropertyStringValue(document, location, position)) {
|
||||
let range = document.getWordRangeAtPosition(position, /\$\{[^"\}]*\}?/);
|
||||
if (!range || range.start.isEqual(position) || range.end.isEqual(position) && document.getText(range).endsWith('}')) {
|
||||
range = new vscode.Range(position, position);
|
||||
}
|
||||
|
||||
return [
|
||||
{ label: 'workspaceFolder', detail: localize('workspaceFolder', "The path of the folder opened in VS Code") },
|
||||
@@ -61,7 +63,7 @@ function registerVariableCompletions(pattern: string): vscode.Disposable {
|
||||
{ label: 'extensionInstallFolder', detail: localize('extensionInstallFolder', "The path where an an extension is installed."), param: 'publisher.extension' },
|
||||
].map(variable => ({
|
||||
label: `\${${variable.label}}`,
|
||||
range: new vscode.Range(startPosition, position),
|
||||
range,
|
||||
insertText: variable.param ? new vscode.SnippetString(`\${${variable.label}:`).appendPlaceholder(variable.param).appendText('}') : (`\${${variable.label}}`),
|
||||
detail: variable.detail
|
||||
}));
|
||||
@@ -72,6 +74,18 @@ function registerVariableCompletions(pattern: string): vscode.Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
function isCompletingInsidePropertyStringValue(document: vscode.TextDocument, location: Location, pos: vscode.Position) {
|
||||
if (location.isAtPropertyKey) {
|
||||
return false;
|
||||
}
|
||||
const previousNode = location.previousNode;
|
||||
if (previousNode && previousNode.type === 'string') {
|
||||
const offset = document.offsetAt(pos);
|
||||
return offset > previousNode.offset && offset < previousNode.offset + previousNode.length;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
interface IExtensionsContent {
|
||||
recommendations: string[];
|
||||
}
|
||||
@@ -84,8 +98,8 @@ function registerExtensionsCompletionsInExtensionsDocument(): vscode.Disposable
|
||||
return vscode.languages.registerCompletionItemProvider({ pattern: '**/extensions.json' }, {
|
||||
provideCompletionItems(document, position, _token) {
|
||||
const location = getLocation(document.getText(), document.offsetAt(position));
|
||||
const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
|
||||
if (location.path[0] === 'recommendations') {
|
||||
const range = getReplaceRange(document, location, position);
|
||||
const extensionsContent = <IExtensionsContent>parse(document.getText());
|
||||
return provideInstalledExtensionProposals(extensionsContent && extensionsContent.recommendations || [], '', range, false);
|
||||
}
|
||||
@@ -98,8 +112,8 @@ function registerExtensionsCompletionsInWorkspaceConfigurationDocument(): vscode
|
||||
return vscode.languages.registerCompletionItemProvider({ pattern: '**/*.code-workspace' }, {
|
||||
provideCompletionItems(document, position, _token) {
|
||||
const location = getLocation(document.getText(), document.offsetAt(position));
|
||||
const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
|
||||
if (location.path[0] === 'extensions' && location.path[1] === 'recommendations') {
|
||||
const range = getReplaceRange(document, location, position);
|
||||
const extensionsContent = <IExtensionsContent>parse(document.getText())['extensions'];
|
||||
return provideInstalledExtensionProposals(extensionsContent && extensionsContent.recommendations || [], '', range, false);
|
||||
}
|
||||
@@ -108,6 +122,17 @@ function registerExtensionsCompletionsInWorkspaceConfigurationDocument(): vscode
|
||||
});
|
||||
}
|
||||
|
||||
function getReplaceRange(document: vscode.TextDocument, location: Location, position: vscode.Position) {
|
||||
const node = location.previousNode;
|
||||
if (node) {
|
||||
const nodeStart = document.positionAt(node.offset), nodeEnd = document.positionAt(node.offset + node.length);
|
||||
if (nodeStart.isBeforeOrEqual(position) && nodeEnd.isAfterOrEqual(position)) {
|
||||
return new vscode.Range(nodeStart, nodeEnd);
|
||||
}
|
||||
}
|
||||
return new vscode.Range(position, position);
|
||||
}
|
||||
|
||||
vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', language: 'jsonc' }, {
|
||||
provideDocumentSymbols(document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.SymbolInformation[]> {
|
||||
const result: vscode.SymbolInformation[] = [];
|
||||
@@ -180,28 +205,11 @@ function registerContextKeyCompletions(): vscode.Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidLocation) {
|
||||
if (!isValidLocation || !isCompletingInsidePropertyStringValue(document, location, position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// for JSON everything with quotes is a word
|
||||
const jsonWord = document.getWordRangeAtPosition(position);
|
||||
if (!jsonWord || jsonWord.start.isEqual(position) || jsonWord.end.isEqual(position)) {
|
||||
// we aren't inside a "JSON word" or on its quotes
|
||||
return;
|
||||
}
|
||||
|
||||
let replacing: vscode.Range | undefined;
|
||||
if (jsonWord.end.character - jsonWord.start.character === 2 || document.getWordRangeAtPosition(position, /\s+/)) {
|
||||
// empty json word or on whitespace
|
||||
replacing = new vscode.Range(position, position);
|
||||
} else {
|
||||
replacing = document.getWordRangeAtPosition(position, /[a-zA-Z.]+/);
|
||||
}
|
||||
|
||||
if (!replacing) {
|
||||
return;
|
||||
}
|
||||
const replacing = document.getWordRangeAtPosition(position, /[a-zA-Z.]+/) || new vscode.Range(position, position);
|
||||
const inserting = replacing.with(undefined, position);
|
||||
|
||||
const data = await vscode.commands.executeCommand<ContextKeyInfo[]>('getContextKeyInfo');
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
||||
export function provideInstalledExtensionProposals(existing: string[], additionalText: string, range: vscode.Range, includeBuiltinExtensions: boolean): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
|
||||
export async function provideInstalledExtensionProposals(existing: string[], additionalText: string, range: vscode.Range, includeBuiltinExtensions: boolean): Promise<vscode.CompletionItem[] | vscode.CompletionList> {
|
||||
if (Array.isArray(existing)) {
|
||||
const extensions = includeBuiltinExtensions ? vscode.extensions.all : vscode.extensions.all.filter(e => !(e.id.startsWith('vscode.') || e.id === 'Microsoft.vscode-markdown'));
|
||||
const knownExtensionProposals = extensions.filter(e => existing.indexOf(e.id) === -1);
|
||||
@@ -30,10 +30,10 @@ export function provideInstalledExtensionProposals(existing: string[], additiona
|
||||
return [example];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return [];
|
||||
}
|
||||
|
||||
export function provideWorkspaceTrustExtensionProposals(existing: string[], range: vscode.Range): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
|
||||
export async function provideWorkspaceTrustExtensionProposals(existing: string[], range: vscode.Range): Promise<vscode.CompletionItem[] | vscode.CompletionList> {
|
||||
if (Array.isArray(existing)) {
|
||||
const extensions = vscode.extensions.all.filter(e => e.packageJSON.main);
|
||||
const extensionProposals = extensions.filter(e => existing.indexOf(e.id) === -1);
|
||||
@@ -56,5 +56,5 @@ export function provideWorkspaceTrustExtensionProposals(existing: string[], rang
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -15,32 +15,27 @@ export class SettingsDocument {
|
||||
|
||||
constructor(private document: vscode.TextDocument) { }
|
||||
|
||||
public provideCompletionItems(position: vscode.Position, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
|
||||
public async provideCompletionItems(position: vscode.Position, _token: vscode.CancellationToken): Promise<vscode.CompletionItem[] | vscode.CompletionList> {
|
||||
const location = getLocation(this.document.getText(), this.document.offsetAt(position));
|
||||
const range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
|
||||
|
||||
// window.title
|
||||
if (location.path[0] === 'window.title') {
|
||||
return this.provideWindowTitleCompletionItems(location, range);
|
||||
return this.provideWindowTitleCompletionItems(location, position);
|
||||
}
|
||||
|
||||
// files.association
|
||||
if (location.path[0] === 'files.associations') {
|
||||
return this.provideFilesAssociationsCompletionItems(location, range);
|
||||
return this.provideFilesAssociationsCompletionItems(location, position);
|
||||
}
|
||||
|
||||
// files.exclude, search.exclude
|
||||
if (location.path[0] === 'files.exclude' || location.path[0] === 'search.exclude') {
|
||||
return this.provideExcludeCompletionItems(location, range);
|
||||
return this.provideExcludeCompletionItems(location, position);
|
||||
}
|
||||
|
||||
// files.defaultLanguage
|
||||
if (location.path[0] === 'files.defaultLanguage') {
|
||||
return this.provideLanguageCompletionItems(location, range).then(items => {
|
||||
|
||||
// Add special item '${activeEditorLanguage}'
|
||||
return [this.newSimpleCompletionItem(JSON.stringify('${activeEditorLanguage}'), range, localize('activeEditor', "Use the language of the currently active text editor if any")), ...items];
|
||||
});
|
||||
return this.provideLanguageCompletionItems(location, position);
|
||||
}
|
||||
|
||||
// settingsSync.ignoredExtensions
|
||||
@@ -49,6 +44,7 @@ export class SettingsDocument {
|
||||
try {
|
||||
ignoredExtensions = parse(this.document.getText())['settingsSync.ignoredExtensions'];
|
||||
} catch (e) {/* ignore error */ }
|
||||
const range = this.getReplaceRange(location, position);
|
||||
return provideInstalledExtensionProposals(ignoredExtensions, '', range, true);
|
||||
}
|
||||
|
||||
@@ -58,44 +54,85 @@ export class SettingsDocument {
|
||||
try {
|
||||
alreadyConfigured = Object.keys(parse(this.document.getText())['remote.extensionKind']);
|
||||
} catch (e) {/* ignore error */ }
|
||||
return provideInstalledExtensionProposals(alreadyConfigured, `: [\n\t"ui"\n]`, range, true);
|
||||
const range = this.getReplaceRange(location, position);
|
||||
return provideInstalledExtensionProposals(alreadyConfigured, location.previousNode ? '' : `: [\n\t"ui"\n]`, range, true);
|
||||
}
|
||||
|
||||
// remote.portsAttributes
|
||||
if (location.path[0] === 'remote.portsAttributes' && location.path.length === 2 && location.isAtPropertyKey) {
|
||||
return this.providePortsAttributesCompletionItem(range);
|
||||
return this.providePortsAttributesCompletionItem(this.getReplaceRange(location, position));
|
||||
}
|
||||
|
||||
return this.provideLanguageOverridesCompletionItems(location, position);
|
||||
}
|
||||
|
||||
private provideWindowTitleCompletionItems(_location: Location, range: vscode.Range): vscode.ProviderResult<vscode.CompletionItem[]> {
|
||||
const completions: vscode.CompletionItem[] = [];
|
||||
|
||||
completions.push(this.newSimpleCompletionItem('${activeEditorShort}', range, localize('activeEditorShort', "the file name (e.g. myFile.txt)")));
|
||||
completions.push(this.newSimpleCompletionItem('${activeEditorMedium}', range, localize('activeEditorMedium', "the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)")));
|
||||
completions.push(this.newSimpleCompletionItem('${activeEditorLong}', range, localize('activeEditorLong', "the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt)")));
|
||||
completions.push(this.newSimpleCompletionItem('${activeFolderShort}', range, localize('activeFolderShort', "the name of the folder the file is contained in (e.g. myFileFolder)")));
|
||||
completions.push(this.newSimpleCompletionItem('${activeFolderMedium}', range, localize('activeFolderMedium', "the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder)")));
|
||||
completions.push(this.newSimpleCompletionItem('${activeFolderLong}', range, localize('activeFolderLong', "the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder)")));
|
||||
completions.push(this.newSimpleCompletionItem('${rootName}', range, localize('rootName', "name of the workspace (e.g. myFolder or myWorkspace)")));
|
||||
completions.push(this.newSimpleCompletionItem('${rootPath}', range, localize('rootPath', "file path of the workspace (e.g. /Users/Development/myWorkspace)")));
|
||||
completions.push(this.newSimpleCompletionItem('${folderName}', range, localize('folderName', "name of the workspace folder the file is contained in (e.g. myFolder)")));
|
||||
completions.push(this.newSimpleCompletionItem('${folderPath}', range, localize('folderPath', "file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder)")));
|
||||
completions.push(this.newSimpleCompletionItem('${appName}', range, localize('appName', "e.g. VS Code")));
|
||||
completions.push(this.newSimpleCompletionItem('${remoteName}', range, localize('remoteName', "e.g. SSH")));
|
||||
completions.push(this.newSimpleCompletionItem('${dirty}', range, localize('dirty', "an indicator for when the active editor has unsaved changes")));
|
||||
completions.push(this.newSimpleCompletionItem('${separator}', range, localize('separator', "a conditional separator (' - ') that only shows when surrounded by variables with values")));
|
||||
|
||||
return Promise.resolve(completions);
|
||||
private getReplaceRange(location: Location, position: vscode.Position) {
|
||||
const node = location.previousNode;
|
||||
if (node) {
|
||||
const nodeStart = this.document.positionAt(node.offset), nodeEnd = this.document.positionAt(node.offset + node.length);
|
||||
if (nodeStart.isBeforeOrEqual(position) && nodeEnd.isAfterOrEqual(position)) {
|
||||
return new vscode.Range(nodeStart, nodeEnd);
|
||||
}
|
||||
}
|
||||
return new vscode.Range(position, position);
|
||||
}
|
||||
|
||||
private provideFilesAssociationsCompletionItems(location: Location, range: vscode.Range): vscode.ProviderResult<vscode.CompletionItem[]> {
|
||||
private isCompletingPropertyValue(location: Location, pos: vscode.Position) {
|
||||
if (location.isAtPropertyKey) {
|
||||
return false;
|
||||
}
|
||||
const previousNode = location.previousNode;
|
||||
if (previousNode) {
|
||||
const offset = this.document.offsetAt(pos);
|
||||
return offset >= previousNode.offset && offset <= previousNode.offset + previousNode.length;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async provideWindowTitleCompletionItems(location: Location, pos: vscode.Position): Promise<vscode.CompletionItem[]> {
|
||||
const completions: vscode.CompletionItem[] = [];
|
||||
|
||||
if (!this.isCompletingPropertyValue(location, pos)) {
|
||||
return completions;
|
||||
}
|
||||
|
||||
let range = this.document.getWordRangeAtPosition(pos, /\$\{[^"\}]*\}?/);
|
||||
if (!range || range.start.isEqual(pos) || range.end.isEqual(pos) && this.document.getText(range).endsWith('}')) {
|
||||
range = new vscode.Range(pos, pos);
|
||||
}
|
||||
|
||||
const getText = (variable: string) => {
|
||||
const text = '${' + variable + '}';
|
||||
return location.previousNode ? text : JSON.stringify(text);
|
||||
};
|
||||
|
||||
|
||||
completions.push(this.newSimpleCompletionItem(getText('activeEditorShort'), range, localize('activeEditorShort', "the file name (e.g. myFile.txt)")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('activeEditorMedium'), range, localize('activeEditorMedium', "the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('activeEditorLong'), range, localize('activeEditorLong', "the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt)")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('activeFolderShort'), range, localize('activeFolderShort', "the name of the folder the file is contained in (e.g. myFileFolder)")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('activeFolderMedium'), range, localize('activeFolderMedium', "the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder)")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('activeFolderLong'), range, localize('activeFolderLong', "the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder)")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('rootName'), range, localize('rootName', "name of the workspace (e.g. myFolder or myWorkspace)")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('rootPath'), range, localize('rootPath', "file path of the workspace (e.g. /Users/Development/myWorkspace)")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('folderName'), range, localize('folderName', "name of the workspace folder the file is contained in (e.g. myFolder)")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('folderPath'), range, localize('folderPath', "file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder)")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('appName'), range, localize('appName', "e.g. VS Code")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('remoteName'), range, localize('remoteName', "e.g. SSH")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('dirty'), range, localize('dirty', "an indicator for when the active editor has unsaved changes")));
|
||||
completions.push(this.newSimpleCompletionItem(getText('separator'), range, localize('separator', "a conditional separator (' - ') that only shows when surrounded by variables with values")));
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
private async provideFilesAssociationsCompletionItems(location: Location, position: vscode.Position): Promise<vscode.CompletionItem[]> {
|
||||
const completions: vscode.CompletionItem[] = [];
|
||||
|
||||
if (location.path.length === 2) {
|
||||
// Key
|
||||
if (!location.isAtPropertyKey || location.path[1] === '') {
|
||||
if (location.path[1] === '') {
|
||||
const range = this.getReplaceRange(location, position);
|
||||
|
||||
completions.push(this.newSnippetCompletionItem({
|
||||
label: localize('assocLabelFile', "Files with Extension"),
|
||||
documentation: localize('assocDescriptionFile', "Map all files matching the glob pattern in their filename to the language with the given identifier."),
|
||||
@@ -109,68 +146,68 @@ export class SettingsDocument {
|
||||
snippet: location.isAtPropertyKey ? '"/${1:path to file}/*.${2:extension}": "${3:language}"' : '{ "/${1:path to file}/*.${2:extension}": "${3:language}" }',
|
||||
range
|
||||
}));
|
||||
} else {
|
||||
} else if (this.isCompletingPropertyValue(location, position)) {
|
||||
// Value
|
||||
return this.provideLanguageCompletionItemsForLanguageOverrides(location, range);
|
||||
return this.provideLanguageCompletionItemsForLanguageOverrides(this.getReplaceRange(location, position));
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(completions);
|
||||
return completions;
|
||||
}
|
||||
|
||||
private provideExcludeCompletionItems(location: Location, range: vscode.Range): vscode.ProviderResult<vscode.CompletionItem[]> {
|
||||
private async provideExcludeCompletionItems(location: Location, position: vscode.Position): Promise<vscode.CompletionItem[]> {
|
||||
const completions: vscode.CompletionItem[] = [];
|
||||
|
||||
// Key
|
||||
if (location.path.length === 1) {
|
||||
if (location.path.length === 1 || (location.path.length === 2 && location.path[1] === '')) {
|
||||
const range = this.getReplaceRange(location, position);
|
||||
|
||||
completions.push(this.newSnippetCompletionItem({
|
||||
label: localize('fileLabel', "Files by Extension"),
|
||||
documentation: localize('fileDescription', "Match all files of a specific file extension."),
|
||||
snippet: location.isAtPropertyKey ? '"**/*.${1:extension}": true' : '{ "**/*.${1:extension}": true }',
|
||||
snippet: location.path.length === 2 ? '"**/*.${1:extension}": true' : '{ "**/*.${1:extension}": true }',
|
||||
range
|
||||
}));
|
||||
|
||||
completions.push(this.newSnippetCompletionItem({
|
||||
label: localize('filesLabel', "Files with Multiple Extensions"),
|
||||
documentation: localize('filesDescription', "Match all files with any of the file extensions."),
|
||||
snippet: location.isAtPropertyKey ? '"**/*.{ext1,ext2,ext3}": true' : '{ "**/*.{ext1,ext2,ext3}": true }',
|
||||
snippet: location.path.length === 2 ? '"**/*.{ext1,ext2,ext3}": true' : '{ "**/*.{ext1,ext2,ext3}": true }',
|
||||
range
|
||||
}));
|
||||
|
||||
completions.push(this.newSnippetCompletionItem({
|
||||
label: localize('derivedLabel', "Files with Siblings by Name"),
|
||||
documentation: localize('derivedDescription', "Match files that have siblings with the same name but a different extension."),
|
||||
snippet: location.isAtPropertyKey ? '"**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" }' : '{ "**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" } }',
|
||||
snippet: location.path.length === 2 ? '"**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" }' : '{ "**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" } }',
|
||||
range
|
||||
}));
|
||||
|
||||
completions.push(this.newSnippetCompletionItem({
|
||||
label: localize('topFolderLabel', "Folder by Name (Top Level)"),
|
||||
documentation: localize('topFolderDescription', "Match a top level folder with a specific name."),
|
||||
snippet: location.isAtPropertyKey ? '"${1:name}": true' : '{ "${1:name}": true }',
|
||||
snippet: location.path.length === 2 ? '"${1:name}": true' : '{ "${1:name}": true }',
|
||||
range
|
||||
}));
|
||||
|
||||
completions.push(this.newSnippetCompletionItem({
|
||||
label: localize('topFoldersLabel', "Folders with Multiple Names (Top Level)"),
|
||||
documentation: localize('topFoldersDescription', "Match multiple top level folders."),
|
||||
snippet: location.isAtPropertyKey ? '"{folder1,folder2,folder3}": true' : '{ "{folder1,folder2,folder3}": true }',
|
||||
snippet: location.path.length === 2 ? '"{folder1,folder2,folder3}": true' : '{ "{folder1,folder2,folder3}": true }',
|
||||
range
|
||||
}));
|
||||
|
||||
completions.push(this.newSnippetCompletionItem({
|
||||
label: localize('folderLabel', "Folder by Name (Any Location)"),
|
||||
documentation: localize('folderDescription', "Match a folder with a specific name in any location."),
|
||||
snippet: location.isAtPropertyKey ? '"**/${1:name}": true' : '{ "**/${1:name}": true }',
|
||||
snippet: location.path.length === 2 ? '"**/${1:name}": true' : '{ "**/${1:name}": true }',
|
||||
range
|
||||
}));
|
||||
}
|
||||
|
||||
// Value
|
||||
else {
|
||||
completions.push(this.newSimpleCompletionItem('false', range, localize('falseDescription', "Disable the pattern.")));
|
||||
completions.push(this.newSimpleCompletionItem('true', range, localize('trueDescription', "Enable the pattern.")));
|
||||
|
||||
else if (location.path.length === 2 && this.isCompletingPropertyValue(location, position)) {
|
||||
const range = this.getReplaceRange(location, position);
|
||||
completions.push(this.newSnippetCompletionItem({
|
||||
label: localize('derivedLabel', "Files with Siblings by Name"),
|
||||
documentation: localize('siblingsDescription', "Match files that have siblings with the same name but a different extension."),
|
||||
@@ -179,15 +216,22 @@ export class SettingsDocument {
|
||||
}));
|
||||
}
|
||||
|
||||
return Promise.resolve(completions);
|
||||
return completions;
|
||||
}
|
||||
|
||||
private provideLanguageCompletionItems(_location: Location, range: vscode.Range, formatFunc: (string: string) => string = (l) => JSON.stringify(l)): Thenable<vscode.CompletionItem[]> {
|
||||
return vscode.languages.getLanguages()
|
||||
.then(languages => languages.map(l => this.newSimpleCompletionItem(formatFunc(l), range)));
|
||||
private async provideLanguageCompletionItems(location: Location, position: vscode.Position): Promise<vscode.CompletionItem[]> {
|
||||
if (location.path.length === 1 && this.isCompletingPropertyValue(location, position)) {
|
||||
const range = this.getReplaceRange(location, position);
|
||||
const languages = await vscode.languages.getLanguages();
|
||||
return [
|
||||
this.newSimpleCompletionItem(JSON.stringify('${activeEditorLanguage}'), range, localize('activeEditor', "Use the language of the currently active text editor if any")),
|
||||
...languages.map(l => this.newSimpleCompletionItem(JSON.stringify(l), range))
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private async provideLanguageCompletionItemsForLanguageOverrides(_location: Location, range: vscode.Range): Promise<vscode.CompletionItem[]> {
|
||||
private async provideLanguageCompletionItemsForLanguageOverrides(range: vscode.Range): Promise<vscode.CompletionItem[]> {
|
||||
const languages = await vscode.languages.getLanguages();
|
||||
const completionItems = [];
|
||||
for (const language of languages) {
|
||||
@@ -200,7 +244,7 @@ export class SettingsDocument {
|
||||
}
|
||||
|
||||
private async provideLanguageOverridesCompletionItems(location: Location, position: vscode.Position): Promise<vscode.CompletionItem[]> {
|
||||
if (location.path.length === 1 && location.previousNode && typeof location.previousNode.value === 'string' && location.previousNode.value.startsWith('[')) {
|
||||
if (location.path.length === 1 && location.isAtPropertyKey && location.previousNode && typeof location.previousNode.value === 'string' && location.previousNode.value.startsWith('[')) {
|
||||
const startPosition = this.document.positionAt(location.previousNode.offset + 1);
|
||||
const endPosition = startPosition.translate(undefined, location.previousNode.value.length);
|
||||
const donotSuggestLanguages: string[] = [];
|
||||
|
||||
594
extensions/configuration-editing/src/test/completion.test.ts
Normal file
594
extensions/configuration-editing/src/test/completion.test.ts
Normal file
@@ -0,0 +1,594 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as assert from 'assert';
|
||||
import { promises as fs } from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import 'mocha';
|
||||
|
||||
|
||||
const testFolder = fs.mkdtemp(path.join(os.tmpdir(), 'conf-editing-'));
|
||||
|
||||
suite('Completions in settings.json', () => {
|
||||
const testFile = 'settings.json';
|
||||
|
||||
test('window.title', async () => {
|
||||
{ // inserting after text
|
||||
const content = [
|
||||
'{',
|
||||
' "window.title": "custom|"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "window.title": "custom${activeEditorShort}"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '${activeEditorShort}', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{ // inserting before a variable
|
||||
const content = [
|
||||
'{',
|
||||
' "window.title": "|${activeEditorShort}"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "window.title": "${folderPath}${activeEditorShort}"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '${folderPath}', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{ // inserting after a variable
|
||||
const content = [
|
||||
'{',
|
||||
' "window.title": "${activeEditorShort}|"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "window.title": "${activeEditorShort}${folderPath}"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '${folderPath}', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{ // replacing an variable
|
||||
const content = [
|
||||
'{',
|
||||
' "window.title": "${a|ctiveEditorShort}"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "window.title": "${activeEditorMedium}"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '${activeEditorMedium}', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{ // replacing a partial variable
|
||||
const content = [
|
||||
'{',
|
||||
' "window.title": "${a|"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "window.title": "${dirty}"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '${dirty}', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{ // inserting a literal
|
||||
const content = [
|
||||
'{',
|
||||
' "window.title": |',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "window.title": "${activeEditorMedium}"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '"${activeEditorMedium}"', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{ // no proposals after literal
|
||||
const content = [
|
||||
'{',
|
||||
' "window.title": "${activeEditorShort}" |',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '${activeEditorMedium}', notAvailable: true };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
|
||||
test('files.associations', async () => {
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "files.associations": {',
|
||||
' |',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "files.associations": {',
|
||||
' "*.${1:extension}": "${2:language}"',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: 'Files with Extension', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "files.associations": {',
|
||||
' |',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "files.associations": {',
|
||||
' "/${1:path to file}/*.${2:extension}": "${3:language}"',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: 'Files with Path', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "files.associations": {',
|
||||
' "*.extension": "|bat"',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "files.associations": {',
|
||||
' "*.extension": "json"',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '"json"', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "files.associations": {',
|
||||
' "*.extension": "bat"|',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "files.associations": {',
|
||||
' "*.extension": "json"',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '"json"', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "files.associations": {',
|
||||
' "*.extension": "bat" |',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '"json"', notAvailable: true };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
test('files.exclude', async () => {
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "files.exclude": {',
|
||||
' |',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "files.exclude": {',
|
||||
' "**/*.${1:extension}": true',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: 'Files by Extension', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "files.exclude": {',
|
||||
' "**/*.extension": |true',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "files.exclude": {',
|
||||
' "**/*.extension": { "when": "$(basename).${1:extension}" }',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: 'Files with Siblings by Name', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
test('files.defaultLanguage', async () => {
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "files.defaultLanguage": "json|"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "files.defaultLanguage": "jsonc"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '"jsonc"', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "files.defaultLanguage": |',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "files.defaultLanguage": "jsonc"',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '"jsonc"', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
test('remote.extensionKind', async () => {
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
'\t"remote.extensionKind": {',
|
||||
'\t\t|',
|
||||
'\t}',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: 'vscode.npm' };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
test('remote.portsAttributes', async () => {
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "remote.portsAttributes": {',
|
||||
' |',
|
||||
' }',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '"3000"' };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suite('Completions in extensions.json', () => {
|
||||
const testFile = 'extensions.json';
|
||||
test('change recommendation', async () => {
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "recommendations": [',
|
||||
' "|a.b"',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "recommendations": [',
|
||||
' "ms-vscode.js-debug"',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: 'ms-vscode.js-debug', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
test('add recommendation', async () => {
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "recommendations": [',
|
||||
' |',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "recommendations": [',
|
||||
' "ms-vscode.js-debug"',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: 'ms-vscode.js-debug', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suite('Completions in launch.json', () => {
|
||||
const testFile = 'launch.json';
|
||||
test('variable completions', async () => {
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "version": "0.2.0",',
|
||||
' "configurations": [',
|
||||
' {',
|
||||
' "name": "Run Extension",',
|
||||
' "type": "extensionHost",',
|
||||
' "preLaunchTask": "${|defaultBuildTask}"',
|
||||
' }',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "version": "0.2.0",',
|
||||
' "configurations": [',
|
||||
' {',
|
||||
' "name": "Run Extension",',
|
||||
' "type": "extensionHost",',
|
||||
' "preLaunchTask": "${cwd}"',
|
||||
' }',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '${cwd}', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "version": "0.2.0",',
|
||||
' "configurations": [',
|
||||
' {',
|
||||
' "name": "Run Extension",',
|
||||
' "type": "extensionHost",',
|
||||
' "preLaunchTask": "|${defaultBuildTask}"',
|
||||
' }',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "version": "0.2.0",',
|
||||
' "configurations": [',
|
||||
' {',
|
||||
' "name": "Run Extension",',
|
||||
' "type": "extensionHost",',
|
||||
' "preLaunchTask": "${cwd}${defaultBuildTask}"',
|
||||
' }',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '${cwd}', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "version": "0.2.0",',
|
||||
' "configurations": [',
|
||||
' {',
|
||||
' "name": "Do It",',
|
||||
' "program": "${workspace|"',
|
||||
' }',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "version": "0.2.0",',
|
||||
' "configurations": [',
|
||||
' {',
|
||||
' "name": "Do It",',
|
||||
' "program": "${cwd}"',
|
||||
' }',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '${cwd}', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suite('Completions in tasks.json', () => {
|
||||
const testFile = 'tasks.json';
|
||||
test('variable completions', async () => {
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "version": "0.2.0",',
|
||||
' "tasks": [',
|
||||
' {',
|
||||
' "type": "shell",',
|
||||
' "command": "${|defaultBuildTask}"',
|
||||
' }',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "version": "0.2.0",',
|
||||
' "tasks": [',
|
||||
' {',
|
||||
' "type": "shell",',
|
||||
' "command": "${cwd}"',
|
||||
' }',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '${cwd}', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
{
|
||||
const content = [
|
||||
'{',
|
||||
' "version": "0.2.0",',
|
||||
' "tasks": [',
|
||||
' {',
|
||||
' "type": "shell",',
|
||||
' "command": "${defaultBuildTask}|"',
|
||||
' }',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'{',
|
||||
' "version": "0.2.0",',
|
||||
' "tasks": [',
|
||||
' {',
|
||||
' "type": "shell",',
|
||||
' "command": "${defaultBuildTask}${cwd}"',
|
||||
' }',
|
||||
' ]',
|
||||
'}',
|
||||
].join('\n');
|
||||
const expected = { label: '${cwd}', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suite('Completions in keybindings.json', () => {
|
||||
const testFile = 'keybindings.json';
|
||||
test('context key insertion', async () => {
|
||||
{
|
||||
const content = [
|
||||
'[',
|
||||
' {',
|
||||
' "key": "ctrl+k ctrl+,",',
|
||||
' "command": "editor.jumpToNextFold",',
|
||||
' "when": "|"',
|
||||
' }',
|
||||
']',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'[',
|
||||
' {',
|
||||
' "key": "ctrl+k ctrl+,",',
|
||||
' "command": "editor.jumpToNextFold",',
|
||||
' "when": "resourcePath"',
|
||||
' }',
|
||||
']',
|
||||
].join('\n');
|
||||
const expected = { label: 'resourcePath', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
|
||||
test('context key replace', async () => {
|
||||
{
|
||||
const content = [
|
||||
'[',
|
||||
' {',
|
||||
' "key": "ctrl+k ctrl+,",',
|
||||
' "command": "editor.jumpToNextFold",',
|
||||
' "when": "resou|rcePath"',
|
||||
' }',
|
||||
']',
|
||||
].join('\n');
|
||||
const resultText = [
|
||||
'[',
|
||||
' {',
|
||||
' "key": "ctrl+k ctrl+,",',
|
||||
' "command": "editor.jumpToNextFold",',
|
||||
' "when": "resource"',
|
||||
' }',
|
||||
']',
|
||||
].join('\n');
|
||||
const expected = { label: 'resource', resultText };
|
||||
await testCompletion(testFile, 'jsonc', content, expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
interface ItemDescription {
|
||||
label: string;
|
||||
resultText?: string;
|
||||
notAvailable?: boolean;
|
||||
}
|
||||
|
||||
async function testCompletion(testFileName: string, languageId: string, content: string, expected: ItemDescription) {
|
||||
|
||||
const offset = content.indexOf('|');
|
||||
content = content.substring(0, offset) + content.substring(offset + 1);
|
||||
|
||||
const docUri = vscode.Uri.file(path.join(await testFolder, testFileName));
|
||||
await fs.writeFile(docUri.fsPath, content);
|
||||
|
||||
const editor = await setTestContent(docUri, languageId, content);
|
||||
const position = editor.document.positionAt(offset);
|
||||
|
||||
// Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion
|
||||
const actualCompletions = (await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', docUri, position)) as vscode.CompletionList;
|
||||
|
||||
const matches = actualCompletions.items.filter(completion => {
|
||||
return completion.label === expected.label;
|
||||
});
|
||||
if (expected.notAvailable) {
|
||||
assert.strictEqual(matches.length, 0, `${expected.label} should not existing is results`);
|
||||
} else {
|
||||
assert.strictEqual(matches.length, 1, `${expected.label} should only existing once: Actual: ${actualCompletions.items.map(c => c.label).join(', ')}`);
|
||||
|
||||
if (expected.resultText) {
|
||||
const match = matches[0];
|
||||
if (match.range && match.insertText) {
|
||||
const range = match.range instanceof vscode.Range ? match.range : match.range.replacing;
|
||||
const text = typeof match.insertText === 'string' ? match.insertText : match.insertText.value;
|
||||
|
||||
await editor.edit(eb => eb.replace(range, text));
|
||||
assert.strictEqual(editor.document.getText(), expected.resultText);
|
||||
} else {
|
||||
assert.fail(`Range or insertText missing`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function setTestContent(docUri: vscode.Uri, languageId: string, content: string): Promise<vscode.TextEditor> {
|
||||
const ext = vscode.extensions.getExtension('vscode.configuration-editing')!;
|
||||
await ext.activate();
|
||||
|
||||
const doc = await vscode.workspace.openTextDocument(docUri);
|
||||
await vscode.languages.setTextDocumentLanguage(doc, languageId);
|
||||
const editor = await vscode.window.showTextDocument(doc);
|
||||
|
||||
const fullRange = new vscode.Range(new vscode.Position(0, 0), doc.positionAt(doc.getText().length));
|
||||
await editor.edit(eb => eb.replace(fullRange, content));
|
||||
return editor;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user