Merge from vscode 9bc92b48d945144abb405b9e8df05e18accb9148

This commit is contained in:
ADS Merger
2020-02-19 03:11:35 +00:00
parent 98584d32a7
commit 1e308639e5
253 changed files with 6414 additions and 2296 deletions
+1 -1
View File
@@ -193,7 +193,7 @@
"typemoq", "typemoq",
"sinon", "sinon",
"vs/nls", "vs/nls",
"azdata", "azdata",
"**/{vs,sql}/base/common/**", "**/{vs,sql}/base/common/**",
"**/{vs,sql}/base/test/common/**", "**/{vs,sql}/base/test/common/**",
"**/{vs,sql}/base/parts/*/common/**", "**/{vs,sql}/base/parts/*/common/**",
-2
View File
@@ -16,8 +16,6 @@
"request": "attach", "request": "attach",
"name": "Attach to Extension Host", "name": "Attach to Extension Host",
"port": 5870, "port": 5870,
"timeout": 30000,
"restart": true,
"outFiles": [ "outFiles": [
"${workspaceFolder}/out/**/*.js" "${workspaceFolder}/out/**/*.js"
] ]
-14
View File
@@ -31,20 +31,6 @@
} }
} }
}, },
{
"type": "npm",
"script": "strict-function-types-watch",
"label": "TS - Strict Function Types",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"problemMatcher": {
"base": "$tsc-watch",
"owner": "typescript-function-types",
"applyTo": "allDocuments"
}
},
{ {
"type": "npm", "type": "npm",
"script": "strict-vscode-watch", "script": "strict-vscode-watch",
@@ -207,7 +207,7 @@ steps:
"toolVersion": "1.0" "toolVersion": "1.0"
} }
] ]
SessionTimeout: 120 SessionTimeout: 60
displayName: Notarization displayName: Notarization
- script: | - script: |
+120 -83
View File
@@ -467,57 +467,61 @@
], ],
"menus": { "menus": {
"commandPalette": [ "commandPalette": [
{
"command": "git.setLogLevel",
"when": "config.git.enabled && !git.missing"
},
{ {
"command": "git.clone", "command": "git.clone",
"when": "config.git.enabled" "when": "config.git.enabled && !git.missing"
}, },
{ {
"command": "git.init", "command": "git.init",
"when": "config.git.enabled" "when": "config.git.enabled && !git.missing"
}, },
{ {
"command": "git.openRepository", "command": "git.openRepository",
"when": "config.git.enabled" "when": "config.git.enabled && !git.missing"
}, },
{ {
"command": "git.close", "command": "git.close",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.refresh", "command": "git.refresh",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.openFile", "command": "git.openFile",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.openHEADFile", "command": "git.openHEADFile",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.openChange", "command": "git.openChange",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stage", "command": "git.stage",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stageAll", "command": "git.stageAll",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stageAllTracked", "command": "git.stageAllTracked",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stageAllUntracked", "command": "git.stageAllUntracked",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stageSelectedRanges", "command": "git.stageSelectedRanges",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stageChange", "command": "git.stageChange",
@@ -525,7 +529,7 @@
}, },
{ {
"command": "git.revertSelectedRanges", "command": "git.revertSelectedRanges",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.revertChange", "command": "git.revertChange",
@@ -537,51 +541,63 @@
}, },
{ {
"command": "git.unstage", "command": "git.unstage",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.unstageAll", "command": "git.unstageAll",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.unstageSelectedRanges", "command": "git.unstageSelectedRanges",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.clean", "command": "git.clean",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.cleanAll", "command": "git.cleanAll",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.cleanAllTracked",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.cleanAllUntracked",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.commit", "command": "git.commit",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.commitStaged", "command": "git.commitStaged",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.commitEmpty",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.commitStagedSigned", "command": "git.commitStagedSigned",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.commitStagedAmend", "command": "git.commitStagedAmend",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.commitAll", "command": "git.commitAll",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.commitAllSigned", "command": "git.commitAllSigned",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.commitAllAmend", "command": "git.commitAllAmend",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.restoreCommitTemplate", "command": "git.restoreCommitTemplate",
@@ -593,111 +609,111 @@
}, },
{ {
"command": "git.undoCommit", "command": "git.undoCommit",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.checkout", "command": "git.checkout",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.branch", "command": "git.branch",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.branchFrom", "command": "git.branchFrom",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.deleteBranch", "command": "git.deleteBranch",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.renameBranch", "command": "git.renameBranch",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.pull", "command": "git.pull",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.pullFrom", "command": "git.pullFrom",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.pullRebase", "command": "git.pullRebase",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.pullFrom", "command": "git.pullFrom",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.merge", "command": "git.merge",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.createTag", "command": "git.createTag",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.deleteTag", "command": "git.deleteTag",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.fetch", "command": "git.fetch",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.fetchPrune", "command": "git.fetchPrune",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.fetchAll", "command": "git.fetchAll",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.push", "command": "git.push",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.pushForce", "command": "git.pushForce",
"when": "config.git.enabled && config.git.allowForcePush && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && config.git.allowForcePush && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.pushTo", "command": "git.pushTo",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.pushToForce", "command": "git.pushToForce",
"when": "config.git.enabled && config.git.allowForcePush && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && config.git.allowForcePush && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.pushWithTags", "command": "git.pushWithTags",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.pushWithTagsForce", "command": "git.pushWithTagsForce",
"when": "config.git.enabled && config.git.allowForcePush && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && config.git.allowForcePush && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.addRemote", "command": "git.addRemote",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.removeRemote", "command": "git.removeRemote",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.sync", "command": "git.sync",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.syncRebase", "command": "git.syncRebase",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.publish", "command": "git.publish",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.showOutput", "command": "git.showOutput",
@@ -705,35 +721,35 @@
}, },
{ {
"command": "git.ignore", "command": "git.ignore",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stashIncludeUntracked", "command": "git.stashIncludeUntracked",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stash", "command": "git.stash",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stashPop", "command": "git.stashPop",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stashPopLatest", "command": "git.stashPopLatest",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stashApply", "command": "git.stashApply",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stashApplyLatest", "command": "git.stashApplyLatest",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.stashDrop", "command": "git.stashDrop",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
}, },
{ {
"command": "git.timeline.openDiff", "command": "git.timeline.openDiff",
@@ -749,11 +765,6 @@
} }
], ],
"scm/title": [ "scm/title": [
{
"command": "git.init",
"group": "navigation",
"when": "config.git.enabled && !scmProvider && gitOpenRepositoryCount == 0 && workspaceFolderCount != 0"
},
{ {
"command": "git.commit", "command": "git.commit",
"group": "navigation", "group": "navigation",
@@ -1227,76 +1238,76 @@
{ {
"command": "git.openFile", "command": "git.openFile",
"group": "navigation", "group": "navigation",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
}, },
{ {
"command": "git.openChange", "command": "git.openChange",
"group": "navigation", "group": "navigation",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme == file" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme == file"
}, },
{ {
"command": "git.stageSelectedRanges", "command": "git.stageSelectedRanges",
"group": "2_git@1", "group": "2_git@1",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
}, },
{ {
"command": "git.unstageSelectedRanges", "command": "git.unstageSelectedRanges",
"group": "2_git@2", "group": "2_git@2",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
}, },
{ {
"command": "git.revertSelectedRanges", "command": "git.revertSelectedRanges",
"group": "2_git@3", "group": "2_git@3",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
} }
], ],
"editor/context": [ "editor/context": [
{ {
"command": "git.stageSelectedRanges", "command": "git.stageSelectedRanges",
"group": "2_git@1", "group": "2_git@1",
"when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
}, },
{ {
"command": "git.unstageSelectedRanges", "command": "git.unstageSelectedRanges",
"group": "2_git@2", "group": "2_git@2",
"when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
}, },
{ {
"command": "git.revertSelectedRanges", "command": "git.revertSelectedRanges",
"group": "2_git@3", "group": "2_git@3",
"when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" "when": "isInDiffRightEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
} }
], ],
"scm/change/title": [ "scm/change/title": [
{ {
"command": "git.stageChange", "command": "git.stageChange",
"when": "originalResourceScheme == git" "when": "config.git.enabled && !git.missing && originalResourceScheme == git"
}, },
{ {
"command": "git.revertChange", "command": "git.revertChange",
"when": "originalResourceScheme == git" "when": "config.git.enabled && !git.missing && originalResourceScheme == git"
} }
], ],
"timeline/item/context": [ "timeline/item/context": [
{ {
"command": "git.timeline.openDiff", "command": "git.timeline.openDiff",
"group": "inline", "group": "inline",
"when": "timelineItem =~ /git:file\\b/" "when": "config.git.enabled && !git.missing && timelineItem =~ /git:file\\b/"
}, },
{ {
"command": "git.timeline.openDiff", "command": "git.timeline.openDiff",
"group": "1_timeline", "group": "1_timeline",
"when": "timelineItem =~ /git:file\\b/" "when": "config.git.enabled && !git.missing && timelineItem =~ /git:file\\b/"
}, },
{ {
"command": "git.timeline.copyCommitId", "command": "git.timeline.copyCommitId",
"group": "2_timeline@1", "group": "2_timeline@1",
"when": "timelineItem =~ /git:file:commit\\b/" "when": "config.git.enabled && !git.missing && timelineItem =~ /git:file:commit\\b/"
}, },
{ {
"command": "git.timeline.copyCommitMessage", "command": "git.timeline.copyCommitMessage",
"group": "2_timeline@2", "group": "2_timeline@2",
"when": "timelineItem =~ /git:file:commit\\b/" "when": "config.git.enabled && !git.missing && timelineItem =~ /git:file:commit\\b/"
} }
] ]
}, },
@@ -1811,7 +1822,34 @@
72 72
] ]
} }
} },
"viewsWelcome": [
{
"view": "workbench.scm",
"contents": "%view.workbench.scm.disabled%",
"when": "!config.git.enabled"
},
{
"view": "workbench.scm",
"contents": "%view.workbench.scm.missing%",
"when": "config.git.enabled && git.missing"
},
{
"view": "workbench.scm",
"contents": "%view.workbench.scm.empty%",
"when": "config.git.enabled && !git.missing && workbenchState == empty"
},
{
"view": "workbench.scm",
"contents": "%view.workbench.scm.folder%",
"when": "config.git.enabled && !git.missing && workbenchState == folder"
},
{
"view": "workbench.scm",
"contents": "%view.workbench.scm.workspace%",
"when": "config.git.enabled && !git.missing && workbenchState == workspace"
}
]
}, },
"dependencies": { "dependencies": {
"byline": "^5.0.0", "byline": "^5.0.0",
@@ -1829,11 +1867,10 @@
"@types/file-type": "^5.2.1", "@types/file-type": "^5.2.1",
"@types/mocha": "2.2.43", "@types/mocha": "2.2.43",
"@types/node": "^12.11.7", "@types/node": "^12.11.7",
"@types/vscode": "^1.42",
"@types/which": "^1.0.28", "@types/which": "^1.0.28",
"mocha": "^3.2.0", "mocha": "^3.2.0",
"mocha-junit-reporter": "^1.23.3", "mocha-junit-reporter": "^1.23.3",
"mocha-multi-reporters": "^1.1.7", "mocha-multi-reporters": "^1.1.7",
"vscode-test": "^1.3.0" "vscode": "^1.1.36"
} }
} }
+6 -1
View File
@@ -149,5 +149,10 @@
"colors.untracked": "Color for untracked resources.", "colors.untracked": "Color for untracked resources.",
"colors.ignored": "Color for ignored resources.", "colors.ignored": "Color for ignored resources.",
"colors.conflict": "Color for resources with conflicts.", "colors.conflict": "Color for resources with conflicts.",
"colors.submodule": "Color for submodule resources." "colors.submodule": "Color for submodule resources.",
"view.workbench.scm.missing": "A valid git installation was not detected, more details can be found in the [git output](command:git.showOutput).\nPlease [install git](https://git-scm.com/), or learn more about how to use Git and source control in VS Code in [our docs](https://aka.ms/vscode-scm).\nIf you're using a different version control system, you can [search the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22) for additional extensions.",
"view.workbench.scm.disabled": "If you would like to use git features, please enable git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.empty": "In order to use git features, you can open a folder containing a git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone from URL](command:git.clone)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.folder": "The folder currently open doesn't have a git repository.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm)."
} }
+1
View File
@@ -44,6 +44,7 @@ export interface Commit {
readonly authorDate?: Date; readonly authorDate?: Date;
readonly authorName?: string; readonly authorName?: string;
readonly authorEmail?: string; readonly authorEmail?: string;
readonly commitDate?: Date;
} }
export interface Submodule { export interface Submodule {
+12 -9
View File
@@ -333,7 +333,7 @@ function sanitizePath(path: string): string {
return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`); return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`);
} }
const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%P%n%B'; const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%B';
export class Git { export class Git {
@@ -525,6 +525,7 @@ export interface Commit {
authorDate?: Date; authorDate?: Date;
authorName?: string; authorName?: string;
authorEmail?: string; authorEmail?: string;
commitDate?: Date;
} }
export class GitStatusParser { export class GitStatusParser {
@@ -655,15 +656,16 @@ export function parseGitmodules(raw: string): Submodule[] {
return result; return result;
} }
const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm; const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm;
export function parseGitCommits(data: string): Commit[] { export function parseGitCommits(data: string): Commit[] {
let commits: Commit[] = []; let commits: Commit[] = [];
let ref; let ref;
let name; let authorName;
let email; let authorEmail;
let date; let authorDate;
let commitDate;
let parents; let parents;
let message; let message;
let match; let match;
@@ -674,7 +676,7 @@ export function parseGitCommits(data: string): Commit[] {
break; break;
} }
[, ref, name, email, date, parents, message] = match; [, ref, authorName, authorEmail, authorDate, commitDate, parents, message] = match;
if (message[message.length - 1] === '\n') { if (message[message.length - 1] === '\n') {
message = message.substr(0, message.length - 1); message = message.substr(0, message.length - 1);
@@ -685,9 +687,10 @@ export function parseGitCommits(data: string): Commit[] {
hash: ` ${ref}`.substr(1), hash: ` ${ref}`.substr(1),
message: ` ${message}`.substr(1), message: ` ${message}`.substr(1),
parents: parents ? parents.split(' ') : [], parents: parents ? parents.split(' ') : [],
authorDate: new Date(Number(date) * 1000), authorDate: new Date(Number(authorDate) * 1000),
authorName: ` ${name}`.substr(1), authorName: ` ${authorName}`.substr(1),
authorEmail: ` ${email}`.substr(1) authorEmail: ` ${authorEmail}`.substr(1),
commitDate: new Date(Number(commitDate) * 1000),
}); });
} while (true); } while (true);
+2 -1
View File
@@ -178,7 +178,8 @@ export async function activate(context: ExtensionContext): Promise<GitExtension>
// console.warn(err.message); // console.warn(err.message);
// outputChannel.appendLine(err.message); // outputChannel.appendLine(err.message);
// warnAboutMissingGit(); commands.executeCommand('setContext', 'git.missing', true);
// warnAboutMissingGit(); {{SQL CARBON EDIT}} turn-off Git missing prompt
return new GitExtensionImpl(); return new GitExtensionImpl();
} }
+6
View File
@@ -192,6 +192,7 @@ suite('git', () => {
John Doe John Doe
john.doe@mail.com john.doe@mail.com
1580811030 1580811030
1580811031
8e5a374372b8393906c7e380dbb09349c5385554 8e5a374372b8393906c7e380dbb09349c5385554
This is a commit message.\x00`; This is a commit message.\x00`;
@@ -202,6 +203,7 @@ This is a commit message.\x00`;
authorDate: new Date(1580811030000), authorDate: new Date(1580811030000),
authorName: 'John Doe', authorName: 'John Doe',
authorEmail: 'john.doe@mail.com', authorEmail: 'john.doe@mail.com',
commitDate: new Date(1580811031000),
}]); }]);
}); });
@@ -210,6 +212,7 @@ This is a commit message.\x00`;
John Doe John Doe
john.doe@mail.com john.doe@mail.com
1580811030 1580811030
1580811031
8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217 8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217
This is a commit message.\x00`; This is a commit message.\x00`;
@@ -220,6 +223,7 @@ This is a commit message.\x00`;
authorDate: new Date(1580811030000), authorDate: new Date(1580811030000),
authorName: 'John Doe', authorName: 'John Doe',
authorEmail: 'john.doe@mail.com', authorEmail: 'john.doe@mail.com',
commitDate: new Date(1580811031000),
}]); }]);
}); });
@@ -228,6 +232,7 @@ This is a commit message.\x00`;
John Doe John Doe
john.doe@mail.com john.doe@mail.com
1580811030 1580811030
1580811031
This is a commit message.\x00`; This is a commit message.\x00`;
@@ -238,6 +243,7 @@ This is a commit message.\x00`;
authorDate: new Date(1580811030000), authorDate: new Date(1580811030000),
authorName: 'John Doe', authorName: 'John Doe',
authorEmail: 'john.doe@mail.com', authorEmail: 'john.doe@mail.com',
commitDate: new Date(1580811031000),
}]); }]);
}); });
}); });
+436 -21
View File
@@ -31,11 +31,6 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a"
integrity sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA== integrity sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA==
"@types/vscode@^1.42":
version "1.42.0"
resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.42.0.tgz#0ad891a9487e91e34be7c56985058a179031eb76"
integrity sha512-ds6TceMsh77Fs0Mq0Vap6Y72JbGWB8Bay4DrnJlf5d9ui2RSe1wis13oQm+XhguOeH1HUfLGzaDAoupTUtgabw==
"@types/which@^1.0.28": "@types/which@^1.0.28":
version "1.0.28" version "1.0.28"
resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6" resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6"
@@ -48,6 +43,16 @@ agent-base@4, agent-base@^4.3.0:
dependencies: dependencies:
es6-promisify "^5.0.0" es6-promisify "^5.0.0"
ajv@^6.5.5:
version "6.11.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9"
integrity sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ansi-regex@^3.0.0: ansi-regex@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
@@ -62,11 +67,45 @@ applicationinsights@1.0.8:
diagnostic-channel-publishers "0.2.1" diagnostic-channel-publishers "0.2.1"
zone.js "0.7.6" zone.js "0.7.6"
asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
dependencies:
safer-buffer "~2.1.0"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
aws4@^1.8.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
balanced-match@^1.0.0: balanced-match@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
dependencies:
tweetnacl "^0.14.3"
brace-expansion@^1.1.7: brace-expansion@^1.1.7:
version "1.1.8" version "1.1.8"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
@@ -80,16 +119,43 @@ browser-stdout@1.3.0:
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8=
browser-stdout@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
byline@^5.0.0: byline@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1"
integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
charenc@~0.0.1: charenc@~0.0.1:
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
commander@2.15.1:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
commander@2.9.0: commander@2.9.0:
version "2.9.0" version "2.9.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
@@ -102,11 +168,23 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
core-util-is@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
crypt@~0.0.1: crypt@~0.0.1:
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
dependencies:
assert-plus "^1.0.0"
dayjs@1.8.19: dayjs@1.8.19:
version "1.8.19" version "1.8.19"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.19.tgz#5117dc390d8f8e586d53891dbff3fa308f51abfe" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.19.tgz#5117dc390d8f8e586d53891dbff3fa308f51abfe"
@@ -140,6 +218,11 @@ debug@^3.1.0:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
diagnostic-channel-publishers@0.2.1: diagnostic-channel-publishers@0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
@@ -157,6 +240,19 @@ diff@3.2.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k= integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k=
diff@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
dependencies:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
es6-promise@^4.0.3: es6-promise@^4.0.3:
version "4.2.8" version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
@@ -174,16 +270,62 @@ escape-string-regexp@1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
extsprintf@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
extsprintf@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
fast-deep-equal@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
file-type@^7.2.0: file-type@^7.2.0:
version "7.2.0" version "7.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.2.0.tgz#113cfed52e1d6959ab80248906e2f25a8cdccb74" resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.2.0.tgz#113cfed52e1d6959ab80248906e2f25a8cdccb74"
integrity sha1-ETz+1S4daVmrgCSJBuLyWozcy3Q= integrity sha1-ETz+1S4daVmrgCSJBuLyWozcy3Q=
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
fs.realpath@^1.0.0: fs.realpath@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
dependencies:
assert-plus "^1.0.0"
glob@7.1.1: glob@7.1.1:
version "7.1.1" version "7.1.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
@@ -196,7 +338,19 @@ glob@7.1.1:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^7.1.3: glob@7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.2:
version "7.1.6" version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -213,16 +367,39 @@ glob@^7.1.3:
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
growl@1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
growl@1.9.2: growl@1.9.2:
version "1.9.2" version "1.9.2"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
har-validator@~5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
dependencies:
ajv "^6.5.5"
har-schema "^2.0.0"
has-flag@^1.0.0: has-flag@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
he@1.1.1: he@1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
@@ -236,7 +413,16 @@ http-proxy-agent@^2.1.0:
agent-base "4" agent-base "4"
debug "3.1.0" debug "3.1.0"
https-proxy-agent@^2.2.4: http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
https-proxy-agent@^2.2.1:
version "2.2.4" version "2.2.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
@@ -269,21 +455,61 @@ is-buffer@~1.1.1:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
isexe@^2.0.0: isexe@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
jschardet@2.1.1: jschardet@2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184"
integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q==
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json3@3.3.2: json3@3.3.2:
version "3.3.2" version "3.3.2"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.2.3"
verror "1.10.0"
lodash._baseassign@^3.0.0: lodash._baseassign@^3.0.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
@@ -354,7 +580,19 @@ md5@^2.1.0:
crypt "~0.0.1" crypt "~0.0.1"
is-buffer "~1.1.1" is-buffer "~1.1.1"
minimatch@^3.0.2, minimatch@^3.0.4: mime-db@1.43.0:
version "1.43.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==
mime-types@^2.1.12, mime-types@~2.1.19:
version "2.1.26"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06"
integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==
dependencies:
mime-db "1.43.0"
minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@@ -410,6 +648,23 @@ mocha@^3.2.0:
mkdirp "0.5.1" mkdirp "0.5.1"
supports-color "3.1.2" supports-color "3.1.2"
mocha@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==
dependencies:
browser-stdout "1.3.1"
commander "2.15.1"
debug "3.1.0"
diff "3.5.0"
escape-string-regexp "1.0.5"
glob "7.1.2"
growl "1.10.5"
he "1.1.1"
minimatch "3.0.4"
mkdirp "0.5.1"
supports-color "5.4.0"
ms@2.0.0: ms@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -420,6 +675,11 @@ ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
once@^1.3.0: once@^1.3.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -432,14 +692,68 @@ path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
rimraf@^2.6.3: performance-now@^2.1.0:
version "2.7.1" version "2.1.0"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
dependencies:
glob "^7.1.3"
"safer-buffer@>= 2.1.2 < 3": psl@^1.1.28:
version "1.7.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c"
integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==
punycode@^2.1.0, punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
querystringify@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
request@^2.88.0:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
caseless "~0.12.0"
combined-stream "~1.0.6"
extend "~3.0.2"
forever-agent "~0.6.1"
form-data "~2.3.2"
har-validator "~5.1.3"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.19"
oauth-sign "~0.9.0"
performance-now "^2.1.0"
qs "~6.5.2"
safe-buffer "^5.1.2"
tough-cookie "~2.5.0"
tunnel-agent "^0.6.0"
uuid "^3.3.2"
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
version "5.2.0"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -449,6 +763,39 @@ semver@^5.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
semver@^5.4.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
source-map-support@^0.5.0:
version "0.5.16"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map@^0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
sshpk@^1.7.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
bcrypt-pbkdf "^1.0.0"
dashdash "^1.12.0"
ecc-jsbn "~0.1.1"
getpass "^0.1.1"
jsbn "~0.1.0"
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
strip-ansi@^4.0.0: strip-ansi@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
@@ -463,6 +810,62 @@ supports-color@3.1.2:
dependencies: dependencies:
has-flag "^1.0.0" has-flag "^1.0.0"
supports-color@5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==
dependencies:
has-flag "^3.0.0"
tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
dependencies:
psl "^1.1.28"
punycode "^2.1.1"
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
dependencies:
safe-buffer "^5.0.1"
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
uri-js@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
dependencies:
punycode "^2.1.0"
url-parse@^1.4.4:
version "1.4.7"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
uuid@^3.3.2:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
dependencies:
assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
vscode-extension-telemetry@0.1.1: vscode-extension-telemetry@0.1.1:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b"
@@ -475,20 +878,32 @@ vscode-nls@^4.0.0:
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002"
integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw==
vscode-test@^1.3.0: vscode-test@^0.4.1:
version "1.3.0" version "0.4.3"
resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-1.3.0.tgz#3310ab385d9b887b4c82e8f52be1030e7cf9493d" resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8"
integrity sha512-LddukcBiSU2FVTDr3c1D8lwkiOvwlJdDL2hqVbn6gIz+rpTqUCkMZSKYm94Y1v0WXlHSDQBsXyY+tchWQgGVsw== integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w==
dependencies: dependencies:
http-proxy-agent "^2.1.0" http-proxy-agent "^2.1.0"
https-proxy-agent "^2.2.4" https-proxy-agent "^2.2.1"
rimraf "^2.6.3"
vscode-uri@^2.0.0: vscode-uri@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542"
integrity sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw== integrity sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw==
vscode@^1.1.36:
version "1.1.36"
resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6"
integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ==
dependencies:
glob "^7.1.2"
mocha "^5.2.0"
request "^2.88.0"
semver "^5.4.1"
source-map-support "^0.5.0"
url-parse "^1.4.4"
vscode-test "^0.4.1"
which@^1.3.0: which@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
@@ -76,29 +76,29 @@ let telemetryReporter: TelemetryReporter | undefined;
export function activate(context: ExtensionContext) { export function activate(context: ExtensionContext) {
let toDispose = context.subscriptions; const toDispose = context.subscriptions;
let rangeFormatting: Disposable | undefined = undefined; let rangeFormatting: Disposable | undefined = undefined;
let packageInfo = getPackageInfo(context); const packageInfo = getPackageInfo(context);
telemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); telemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
let serverMain = readJSONFile(context.asAbsolutePath('./server/package.json')).main; const serverMain = readJSONFile(context.asAbsolutePath('./server/package.json')).main;
let serverModule = context.asAbsolutePath(path.join('server', serverMain)); const serverModule = context.asAbsolutePath(path.join('server', serverMain));
// The debug options for the server // The debug options for the server
let debugOptions = { execArgv: ['--nolazy', '--inspect=' + (9000 + Math.round(Math.random() * 10000))] }; const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (9000 + Math.round(Math.random() * 10000))] };
// If the extension is launch in debug mode the debug server options are use // If the extension is launch in debug mode the debug server options are use
// Otherwise the run options are used // Otherwise the run options are used
let serverOptions: ServerOptions = { const serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc }, run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
}; };
let documentSelector = ['json', 'jsonc']; const documentSelector = ['json', 'jsonc'];
let schemaResolutionErrorStatusBarItem = window.createStatusBarItem({ const schemaResolutionErrorStatusBarItem = window.createStatusBarItem({
id: 'status.json.resolveError', id: 'status.json.resolveError',
name: localize('json.resolveError', "JSON: Schema Resolution Error"), name: localize('json.resolveError', "JSON: Schema Resolution Error"),
alignment: StatusBarAlignment.Right, alignment: StatusBarAlignment.Right,
@@ -109,10 +109,10 @@ export function activate(context: ExtensionContext) {
schemaResolutionErrorStatusBarItem.text = '$(alert)'; schemaResolutionErrorStatusBarItem.text = '$(alert)';
toDispose.push(schemaResolutionErrorStatusBarItem); toDispose.push(schemaResolutionErrorStatusBarItem);
let fileSchemaErrors = new Map<string, string>(); const fileSchemaErrors = new Map<string, string>();
// Options to control the language client // Options to control the language client
let clientOptions: LanguageClientOptions = { const clientOptions: LanguageClientOptions = {
// Register the server for json documents // Register the server for json documents
documentSelector, documentSelector,
initializationOptions: { initializationOptions: {
@@ -172,17 +172,17 @@ export function activate(context: ExtensionContext) {
}; };
// Create the language client and start the client. // Create the language client and start the client.
let client = new LanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), serverOptions, clientOptions); const client = new LanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), serverOptions, clientOptions);
client.registerProposedFeatures(); client.registerProposedFeatures();
let disposable = client.start(); const disposable = client.start();
toDispose.push(disposable); toDispose.push(disposable);
client.onReady().then(() => { client.onReady().then(() => {
const schemaDocuments: { [uri: string]: boolean } = {}; const schemaDocuments: { [uri: string]: boolean } = {};
// handle content request // handle content request
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
let uri = Uri.parse(uriPath); const uri = Uri.parse(uriPath);
if (uri.scheme !== 'http' && uri.scheme !== 'https') { if (uri.scheme !== 'http' && uri.scheme !== 'https') {
return workspace.openTextDocument(uri).then(doc => { return workspace.openTextDocument(uri).then(doc => {
schemaDocuments[uri.toString()] = true; schemaDocuments[uri.toString()] = true;
@@ -212,7 +212,7 @@ export function activate(context: ExtensionContext) {
} }
}); });
let handleContentChange = (uriString: string) => { const handleContentChange = (uriString: string) => {
if (schemaDocuments[uriString]) { if (schemaDocuments[uriString]) {
client.sendNotification(SchemaContentChangeNotification.type, uriString); client.sendNotification(SchemaContentChangeNotification.type, uriString);
return true; return true;
@@ -220,7 +220,7 @@ export function activate(context: ExtensionContext) {
return false; return false;
}; };
let handleActiveEditorChange = (activeEditor?: TextEditor) => { const handleActiveEditorChange = (activeEditor?: TextEditor) => {
if (!activeEditor) { if (!activeEditor) {
return; return;
} }
@@ -244,7 +244,7 @@ export function activate(context: ExtensionContext) {
})); }));
toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange)); toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange));
let handleRetryResolveSchemaCommand = () => { const handleRetryResolveSchemaCommand = () => {
if (window.activeTextEditor) { if (window.activeTextEditor) {
schemaResolutionErrorStatusBarItem.text = '$(watch)'; schemaResolutionErrorStatusBarItem.text = '$(watch)';
const activeDocUri = window.activeTextEditor.document.uri.toString(); const activeDocUri = window.activeTextEditor.document.uri.toString();
@@ -282,7 +282,7 @@ export function activate(context: ExtensionContext) {
}); });
let languageConfiguration: LanguageConfiguration = { const languageConfiguration: LanguageConfiguration = {
wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/, wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/,
indentationRules: { indentationRules: {
increaseIndentPattern: /({+(?=([^"]*"[^"]*")*[^"}]*$))|(\[+(?=([^"]*"[^"]*")*[^"\]]*$))/, increaseIndentPattern: /({+(?=([^"]*"[^"]*")*[^"}]*$))|(\[+(?=([^"]*"[^"]*")*[^"\]]*$))/,
@@ -300,7 +300,7 @@ export function activate(context: ExtensionContext) {
} else if (formatEnabled && !rangeFormatting) { } else if (formatEnabled && !rangeFormatting) {
rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, { rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, {
provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult<TextEdit[]> { provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult<TextEdit[]> {
let params: DocumentRangeFormattingParams = { const params: DocumentRangeFormattingParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
range: client.code2ProtocolConverter.asRange(range), range: client.code2ProtocolConverter.asRange(range),
options: client.code2ProtocolConverter.asFormattingOptions(options) options: client.code2ProtocolConverter.asFormattingOptions(options)
@@ -325,11 +325,11 @@ export function deactivate(): Promise<any> {
} }
function getSchemaAssociation(_context: ExtensionContext): ISchemaAssociations { function getSchemaAssociation(_context: ExtensionContext): ISchemaAssociations {
let associations: ISchemaAssociations = {}; const associations: ISchemaAssociations = {};
extensions.all.forEach(extension => { extensions.all.forEach(extension => {
let packageJSON = extension.packageJSON; const packageJSON = extension.packageJSON;
if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation) { if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation) {
let jsonValidation = packageJSON.contributes.jsonValidation; const jsonValidation = packageJSON.contributes.jsonValidation;
if (Array.isArray(jsonValidation)) { if (Array.isArray(jsonValidation)) {
jsonValidation.forEach(jv => { jsonValidation.forEach(jv => {
let { fileMatch, url } = jv; let { fileMatch, url } = jv;
@@ -359,11 +359,11 @@ function getSchemaAssociation(_context: ExtensionContext): ISchemaAssociations {
} }
function getSettings(): Settings { function getSettings(): Settings {
let httpSettings = workspace.getConfiguration('http'); const httpSettings = workspace.getConfiguration('http');
let resultLimit: number = Math.trunc(Math.max(0, Number(workspace.getConfiguration().get('json.maxItemsComputed')))) || 5000; const resultLimit: number = Math.trunc(Math.max(0, Number(workspace.getConfiguration().get('json.maxItemsComputed')))) || 5000;
let settings: Settings = { const settings: Settings = {
http: { http: {
proxy: httpSettings.get('proxy'), proxy: httpSettings.get('proxy'),
proxyStrictSSL: httpSettings.get('proxyStrictSSL') proxyStrictSSL: httpSettings.get('proxyStrictSSL')
@@ -373,10 +373,18 @@ function getSettings(): Settings {
resultLimit resultLimit
} }
}; };
let schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null); const schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null);
let collectSchemaSettings = (schemaSettings: JSONSchemaSettings[], rootPath?: string, fileMatchPrefix?: string) => { const collectSchemaSettings = (schemaSettings: JSONSchemaSettings[], folderUri?: Uri, isMultiRoot?: boolean) => {
for (let setting of schemaSettings) {
let url = getSchemaId(setting, rootPath); let fileMatchPrefix = undefined;
if (folderUri && isMultiRoot) {
fileMatchPrefix = folderUri.toString();
if (fileMatchPrefix[fileMatchPrefix.length - 1] === '/') {
fileMatchPrefix = fileMatchPrefix.substr(0, fileMatchPrefix.length - 1);
}
}
for (const setting of schemaSettings) {
const url = getSchemaId(setting, folderUri);
if (!url) { if (!url) {
continue; continue;
} }
@@ -385,69 +393,78 @@ function getSettings(): Settings {
schemaSetting = schemaSettingsById[url] = { url, fileMatch: [] }; schemaSetting = schemaSettingsById[url] = { url, fileMatch: [] };
settings.json!.schemas!.push(schemaSetting); settings.json!.schemas!.push(schemaSetting);
} }
let fileMatches = setting.fileMatch; const fileMatches = setting.fileMatch;
let resultingFileMatches = schemaSetting.fileMatch!;
if (Array.isArray(fileMatches)) { if (Array.isArray(fileMatches)) {
if (fileMatchPrefix) { const resultingFileMatches = schemaSetting.fileMatch || [];
for (let fileMatch of fileMatches) { schemaSetting.fileMatch = resultingFileMatches;
if (fileMatch[0] === '/') { const addMatch = (pattern: string) => { // filter duplicates
resultingFileMatches.push(fileMatchPrefix + fileMatch); if (resultingFileMatches.indexOf(pattern) === -1) {
resultingFileMatches.push(fileMatchPrefix + '/*' + fileMatch); resultingFileMatches.push(pattern);
} else { }
resultingFileMatches.push(fileMatchPrefix + '/' + fileMatch); };
resultingFileMatches.push(fileMatchPrefix + '/*/' + fileMatch); for (const fileMatch of fileMatches) {
} if (fileMatchPrefix) {
if (fileMatch[0] === '/') {
addMatch(fileMatchPrefix + fileMatch);
addMatch(fileMatchPrefix + '/*' + fileMatch);
} else {
addMatch(fileMatchPrefix + '/' + fileMatch);
addMatch(fileMatchPrefix + '/*/' + fileMatch);
}
} else {
addMatch(fileMatch);
} }
} else {
resultingFileMatches.push(...fileMatches);
} }
} }
if (setting.schema) { if (setting.schema && !schemaSetting.schema) {
schemaSetting.schema = setting.schema; schemaSetting.schema = setting.schema;
} }
} }
}; };
const folders = workspace.workspaceFolders;
// merge global and folder settings. Qualify all file matches with the folder path. // merge global and folder settings. Qualify all file matches with the folder path.
let globalSettings = workspace.getConfiguration('json', null).get<JSONSchemaSettings[]>('schemas'); const globalSettings = workspace.getConfiguration('json', null).get<JSONSchemaSettings[]>('schemas');
if (Array.isArray(globalSettings)) { if (Array.isArray(globalSettings)) {
collectSchemaSettings(globalSettings, workspace.rootPath); if (!folders) {
collectSchemaSettings(globalSettings);
}
} }
let folders = workspace.workspaceFolders;
if (folders) { if (folders) {
for (let folder of folders) { const isMultiRoot = folders.length > 1;
let folderUri = folder.uri; for (const folder of folders) {
const folderUri = folder.uri;
let schemaConfigInfo = workspace.getConfiguration('json', folderUri).inspect<JSONSchemaSettings[]>('schemas'); const schemaConfigInfo = workspace.getConfiguration('json', folderUri).inspect<JSONSchemaSettings[]>('schemas');
let folderSchemas = schemaConfigInfo!.workspaceFolderValue; const folderSchemas = schemaConfigInfo!.workspaceFolderValue;
if (Array.isArray(folderSchemas)) { if (Array.isArray(folderSchemas)) {
let folderPath = folderUri.toString(); collectSchemaSettings(folderSchemas, folderUri, isMultiRoot);
if (folderPath[folderPath.length - 1] === '/') {
folderPath = folderPath.substr(0, folderPath.length - 1);
}
collectSchemaSettings(folderSchemas, folderUri.fsPath, folderPath);
} }
if (Array.isArray(globalSettings)) {
collectSchemaSettings(globalSettings, folderUri, isMultiRoot);
}
} }
} }
return settings; return settings;
} }
function getSchemaId(schema: JSONSchemaSettings, rootPath?: string) { function getSchemaId(schema: JSONSchemaSettings, folderUri?: Uri) {
let url = schema.url; let url = schema.url;
if (!url) { if (!url) {
if (schema.schema) { if (schema.schema) {
url = schema.schema.id || `vscode://schemas/custom/${encodeURIComponent(hash(schema.schema).toString(16))}`; url = schema.schema.id || `vscode://schemas/custom/${encodeURIComponent(hash(schema.schema).toString(16))}`;
} }
} else if (rootPath && (url[0] === '.' || url[0] === '/')) { } else if (folderUri && (url[0] === '.' || url[0] === '/')) {
url = Uri.file(path.normalize(path.join(rootPath, url))).toString(); url = folderUri.with({ path: path.posix.join(folderUri.path, url) }).toString();
} }
return url; return url;
} }
function getPackageInfo(context: ExtensionContext): IPackageInfo | undefined { function getPackageInfo(context: ExtensionContext): IPackageInfo | undefined {
let extensionPackage = readJSONFile(context.asAbsolutePath('./package.json')); const extensionPackage = readJSONFile(context.asAbsolutePath('./package.json'));
if (extensionPackage) { if (extensionPackage) {
return { return {
name: extensionPackage.name, name: extensionPackage.name,
@@ -1,7 +1,7 @@
{ {
"name": "vscode-json-languageserver", "name": "vscode-json-languageserver",
"description": "JSON language server", "description": "JSON language server",
"version": "1.2.2", "version": "1.2.3",
"author": "Microsoft Corporation", "author": "Microsoft Corporation",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
+26 -2
View File
@@ -21,6 +21,7 @@ interface IToken {
accessToken?: string; // When unable to refresh due to network problems, the access token becomes undefined accessToken?: string; // When unable to refresh due to network problems, the access token becomes undefined
expiresIn?: string; // How long access token is valid, in seconds expiresIn?: string; // How long access token is valid, in seconds
expiresAt?: number; // UNIX epoch time at which token will expire
refreshToken: string; refreshToken: string;
accountName: string; accountName: string;
@@ -183,12 +184,33 @@ export class AzureActiveDirectoryService {
private convertToSession(token: IToken): vscode.AuthenticationSession { private convertToSession(token: IToken): vscode.AuthenticationSession {
return { return {
id: token.sessionId, id: token.sessionId,
accessToken: () => !token.accessToken ? Promise.reject('Unavailable due to network problems') : Promise.resolve(token.accessToken), accessToken: () => this.resolveAccessToken(token),
accountName: token.accountName, accountName: token.accountName,
scopes: token.scope.split(' ') scopes: token.scope.split(' ')
}; };
} }
private async resolveAccessToken(token: IToken): Promise<string> {
if (token.accessToken && (!token.expiresAt || token.expiresAt > Date.now())) {
Logger.info('Token available from cache');
return Promise.resolve(token.accessToken);
}
try {
Logger.info('Token expired or unavailable, trying refresh');
const refreshedToken = await this.refreshToken(token.refreshToken, token.scope);
if (refreshedToken.accessToken) {
Promise.resolve(token.accessToken);
} else {
throw new Error();
}
} catch (e) {
throw new Error('Unavailable due to network problems');
}
throw new Error('Unavailable due to network problems');
}
private getTokenClaims(accessToken: string): ITokenClaims { private getTokenClaims(accessToken: string): ITokenClaims {
try { try {
return JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64').toString()); return JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64').toString());
@@ -252,9 +274,9 @@ export class AzureActiveDirectoryService {
res.writeHead(302, { Location: '/' }); res.writeHead(302, { Location: '/' });
res.end(); res.end();
} catch (err) { } catch (err) {
Logger.error(err.message);
res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` }); res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` });
res.end(); res.end();
throw new Error(err.message);
} }
} catch (e) { } catch (e) {
Logger.error(e.message); Logger.error(e.message);
@@ -263,6 +285,7 @@ export class AzureActiveDirectoryService {
if (e.message === 'Error listening to server' || e.message === 'Closed' || e.message === 'Timeout waiting for port') { if (e.message === 'Error listening to server' || e.message === 'Closed' || e.message === 'Timeout waiting for port') {
await this.loginWithoutLocalServer(scope); await this.loginWithoutLocalServer(scope);
} }
throw new Error(e.message);
} finally { } finally {
setTimeout(() => { setTimeout(() => {
server.close(); server.close();
@@ -374,6 +397,7 @@ export class AzureActiveDirectoryService {
const claims = this.getTokenClaims(json.access_token); const claims = this.getTokenClaims(json.access_token);
return { return {
expiresIn: json.expires_in, expiresIn: json.expires_in,
expiresAt: Date.now() + json.expires_in * 1000,
accessToken: json.access_token, accessToken: json.access_token,
refreshToken: json.refresh_token, refreshToken: json.refresh_token,
scope, scope,
@@ -22,7 +22,6 @@ export async function activate(_: vscode.ExtensionContext) {
await loginService.login(scopes.sort().join(' ')); await loginService.login(scopes.sort().join(' '));
return loginService.sessions[0]!; return loginService.sessions[0]!;
} catch (e) { } catch (e) {
vscode.window.showErrorMessage(`Logging in failed: ${e}`);
throw e; throw e;
} }
}, },
+1 -1
View File
@@ -50,7 +50,7 @@ function code() {
export VSCODE_LOGS= export VSCODE_LOGS=
# Launch Code # Launch Code
exec "$CODE" . "$@" exec "$CODE" . --no-sandbox "$@"
} }
function code-wsl() function code-wsl()
+4 -1
View File
@@ -89,7 +89,7 @@ app.once('ready', function () {
traceOptions: args['trace-options'] || 'record-until-full,enable-sampling' traceOptions: args['trace-options'] || 'record-until-full,enable-sampling'
}; };
contentTracing.startRecording(traceOptions, () => onReady()); contentTracing.startRecording(traceOptions).finally(() => onReady());
} else { } else {
onReady(); onReady();
} }
@@ -169,6 +169,9 @@ function configureCommandlineSwitchesSync(cliArgs) {
app.commandLine.appendSwitch('js-flags', jsFlags); app.commandLine.appendSwitch('js-flags', jsFlags);
} }
// TODO@Ben TODO@Deepak Electron 7 workaround for https://github.com/microsoft/vscode/issues/88873
app.commandLine.appendSwitch('disable-features', 'LayoutNG');
return argvConfig; return argvConfig;
} }
@@ -39,7 +39,7 @@ export class MainThreadQueryEditor extends Disposable implements MainThreadQuery
public $connect(fileUri: string, connectionId: string): Thenable<void> { public $connect(fileUri: string, connectionId: string): Thenable<void> {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
let editors = this._editorService.visibleControls.filter(resource => { let editors = this._editorService.visibleControls.filter(resource => {
return !!resource && resource.input.getResource().toString() === fileUri; return !!resource && resource.input.resource.toString() === fileUri;
}); });
let editor = editors && editors.length > 0 ? editors[0] : undefined; let editor = editors && editors.length > 0 ? editors[0] : undefined;
let options: IConnectionCompletionOptions = { let options: IConnectionCompletionOptions = {
@@ -76,7 +76,7 @@ export class MainThreadQueryEditor extends Disposable implements MainThreadQuery
public $connectWithProfile(fileUri: string, connection: azdata.connection.ConnectionProfile): Thenable<void> { public $connectWithProfile(fileUri: string, connection: azdata.connection.ConnectionProfile): Thenable<void> {
return new Promise<void>(async (resolve, reject) => { return new Promise<void>(async (resolve, reject) => {
let editors = this._editorService.visibleControls.filter(resource => { let editors = this._editorService.visibleControls.filter(resource => {
return !!resource && resource.input.getResource().toString() === fileUri; return !!resource && resource.input.resource.toString() === fileUri;
}); });
let editor = editors && editors.length > 0 ? editors[0] : undefined; let editor = editors && editors.length > 0 ? editors[0] : undefined;
@@ -97,7 +97,7 @@ export class MainThreadQueryEditor extends Disposable implements MainThreadQuery
} }
public $runQuery(fileUri: string, runCurrentQuery: boolean = true): void { public $runQuery(fileUri: string, runCurrentQuery: boolean = true): void {
let filteredEditors = this._editorService.visibleControls.filter(editor => editor.input.getResource().toString() === fileUri); let filteredEditors = this._editorService.visibleControls.filter(editor => editor.input.resource.toString() === fileUri);
if (filteredEditors && filteredEditors.length > 0) { if (filteredEditors && filteredEditors.length > 0) {
let editor = filteredEditors[0]; let editor = filteredEditors[0];
if (editor instanceof QueryEditor) { if (editor instanceof QueryEditor) {
@@ -119,7 +119,7 @@ export class MainThreadQueryEditor extends Disposable implements MainThreadQuery
public $createQueryTab(fileUri: string, title: string, componentId: string): void { public $createQueryTab(fileUri: string, title: string, componentId: string): void {
let editors = this._editorService.visibleControls.filter(resource => { let editors = this._editorService.visibleControls.filter(resource => {
return !!resource && resource.input.getResource().toString() === fileUri; return !!resource && resource.input.resource.toString() === fileUri;
}); });
let editor = editors && editors.length > 0 ? editors[0] : undefined; let editor = editors && editors.length > 0 ? editors[0] : undefined;
@@ -114,7 +114,7 @@ export class EditDataInput extends EditorInput implements IConnectableInput {
public save(): Promise<IEditorInput | undefined> { return Promise.resolve(undefined); } public save(): Promise<IEditorInput | undefined> { return Promise.resolve(undefined); }
public getTypeId(): string { return EditDataInput.ID; } public getTypeId(): string { return EditDataInput.ID; }
public setBootstrappedTrue(): void { this._hasBootstrapped = true; } public setBootstrappedTrue(): void { this._hasBootstrapped = true; }
public getResource(): URI { return this._uri; } public get resource(): URI { return this._uri; }
public supportsSplitEditor(): boolean { return false; } public supportsSplitEditor(): boolean { return false; }
public setupComplete() { this._setup = true; } public setupComplete() { this._setup = true; }
public get queryString(): string { public get queryString(): string {
@@ -5,6 +5,7 @@
import { EditorInput } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor';
import { Emitter } from 'vs/base/common/event'; import { Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
/** /**
* Input for the EditDataResultsEditor. This input helps with logic for the viewing and editing of * Input for the EditDataResultsEditor. This input helps with logic for the viewing and editing of
@@ -102,4 +103,8 @@ export class EditDataResultsInput extends EditorInput {
get uri(): string { get uri(): string {
return unescape(this._uri); return unescape(this._uri);
} }
get resource(): URI | undefined {
return undefined;
}
} }
@@ -46,8 +46,8 @@ export class DashboardInput extends EditorInput {
// vscode has a comment that Mode's will eventually be removed (not sure the state of this comment) // vscode has a comment that Mode's will eventually be removed (not sure the state of this comment)
// so this might be able to be undone when that happens // so this might be able to be undone when that happens
if (!model.getModel(this.getResource())) { if (!model.getModel(this.resource)) {
model.createModel('', modeService.create('dashboard'), this.getResource()); model.createModel('', modeService.create('dashboard'), this.resource);
} }
this._initializedPromise = _connectionService.connectIfNotConnected(_connectionProfile, 'dashboard').then( this._initializedPromise = _connectionService.connectIfNotConnected(_connectionProfile, 'dashboard').then(
u => { u => {
@@ -72,7 +72,7 @@ export class DashboardInput extends EditorInput {
return DashboardInput.ID; return DashboardInput.ID;
} }
public getResource(): URI { public get resource(): URI {
return URI.from({ return URI.from({
scheme: 'dashboard', scheme: 'dashboard',
path: 'dashboard' path: 'dashboard'
@@ -287,4 +287,8 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
super.dispose(); super.dispose();
this._profilerService.disconnectSession(this.id); this._profilerService.disconnectSession(this.id);
} }
get resource(): URI | undefined {
return undefined;
}
} }
@@ -89,11 +89,11 @@ export class ModelViewInput extends EditorInput {
return this._title; return this._title;
} }
public getResource(): URI { public get resource(): URI | undefined {
if (this._options.resourceName) { if (this._options.resourceName) {
return URI.from({ scheme: ModelViewInput.Scheme, path: this._options.resourceName }); return URI.from({ scheme: ModelViewInput.Scheme, path: this._options.resourceName });
} }
return super.getResource(); return undefined;
} }
public get container(): HTMLElement { public get container(): HTMLElement {
@@ -196,7 +196,7 @@ export class QueryTextEditor extends BaseTextEditor {
this.refreshEditorConfiguration(); this.refreshEditorConfiguration();
} }
private refreshEditorConfiguration(configuration = this.textResourceConfigurationService.getValue<IEditorConfiguration>(this.input.getResource())): void { private refreshEditorConfiguration(configuration = this.textResourceConfigurationService.getValue<IEditorConfiguration>(this.input.resource)): void {
if (!this.getControl()) { if (!this.getControl()) {
return; return;
} }
+1 -1
View File
@@ -76,7 +76,7 @@ export function getCurrentGlobalConnection(objectExplorerService: IObjectExplore
let activeInput = workbenchEditorService.activeEditor; let activeInput = workbenchEditorService.activeEditor;
if (activeInput) { if (activeInput) {
connection = connectionManagementService.getConnectionProfile(activeInput.getResource().toString()); connection = connectionManagementService.getConnectionProfile(activeInput.resource.toString());
} }
return connection; return connection;
@@ -166,7 +166,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab
} }
// Getters for private properties // Getters for private properties
public get uri(): string { return this.getResource()!.toString(true); } public get uri(): string { return this.resource!.toString(true); }
public get text(): EditorInput { return this._text; } public get text(): EditorInput { return this._text; }
public get results(): QueryResultsInput { return this._results; } public get results(): QueryResultsInput { return this._results; }
// Description is shown beside the tab name in the combobox of open editors // Description is shown beside the tab name in the combobox of open editors
@@ -191,7 +191,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab
// Forwarding resource functions to the inline sql file editor // Forwarding resource functions to the inline sql file editor
public isDirty(): boolean { return this._text.isDirty(); } public isDirty(): boolean { return this._text.isDirty(); }
public getResource(): URI | undefined { return this._text.getResource(); } public get resource(): URI | undefined { return this._text.resource; }
public matchInputInstanceType(inputType: any): boolean { public matchInputInstanceType(inputType: any): boolean {
return (this._text instanceof inputType); return (this._text instanceof inputType);
@@ -12,6 +12,7 @@ import { QueryPlanState } from 'sql/workbench/common/editor/query/queryPlanState
import { MessagePanelState } from 'sql/workbench/common/editor/query/messagePanelState'; import { MessagePanelState } from 'sql/workbench/common/editor/query/messagePanelState';
import { GridPanelState } from 'sql/workbench/common/editor/query/gridPanelState'; import { GridPanelState } from 'sql/workbench/common/editor/query/gridPanelState';
import { QueryModelViewState } from 'sql/workbench/common/editor/query/modelViewState'; import { QueryModelViewState } from 'sql/workbench/common/editor/query/modelViewState';
import { URI } from 'vs/base/common/uri';
export class ResultsViewState { export class ResultsViewState {
public readonly gridPanelState: GridPanelState = new GridPanelState(); public readonly gridPanelState: GridPanelState = new GridPanelState();
@@ -89,4 +90,8 @@ export class QueryResultsInput extends EditorInput {
get uri(): string { get uri(): string {
return this._uri; return this._uri;
} }
get resource(): URI | undefined {
return undefined;
}
} }
@@ -198,7 +198,7 @@ export class CommandLineWorkbenchContribution implements IWorkbenchContribution,
// If an open and connectable query editor exists for the given URI, attach it to the connection profile // If an open and connectable query editor exists for the given URI, attach it to the connection profile
private async processFile(uriString: string, profile: IConnectionProfile, warnOnConnectFailure: boolean): Promise<void> { private async processFile(uriString: string, profile: IConnectionProfile, warnOnConnectFailure: boolean): Promise<void> {
let activeEditor = this._editorService.editors.filter(v => v.getResource().toString() === uriString).pop(); let activeEditor = this._editorService.editors.filter(v => v.resource.toString() === uriString).pop();
if (activeEditor instanceof QueryEditorInput && activeEditor.state.connected) { if (activeEditor instanceof QueryEditorInput && activeEditor.state.connected) {
let options: IConnectionCompletionOptions = { let options: IConnectionCompletionOptions = {
params: { connectionType: ConnectionType.editor, runQueryOnCompletion: RunQueryOnConnectionMode.none, input: activeEditor }, params: { connectionType: ConnectionType.editor, runQueryOnCompletion: RunQueryOnConnectionMode.none, input: activeEditor },
@@ -49,12 +49,12 @@ export class EditorReplacementContribution implements IWorkbenchContribution {
} }
if (!language) { // in the case the input doesn't have a preferred mode set we will attempt to guess the mode from the file path if (!language) { // in the case the input doesn't have a preferred mode set we will attempt to guess the mode from the file path
language = this.modeService.getModeIdByFilepathOrFirstLine(editor.getResource()); language = this.modeService.getModeIdByFilepathOrFirstLine(editor.resource);
} }
if (!language) { if (!language) {
// just use the extension // just use the extension
language = path.extname(editor.getResource().toString()).slice(1); // remove the . language = path.extname(editor.resource.toString()).slice(1); // remove the .
} }
if (!language) { if (!language) {
@@ -211,7 +211,7 @@ export abstract class NotebookInput extends EditorInput {
private _notebookFindModel: NotebookFindModel; private _notebookFindModel: NotebookFindModel;
constructor(private _title: string, constructor(private _title: string,
private resource: URI, private _resource: URI,
private _textInput: TextInput, private _textInput: TextInput,
@ITextModelService private textModelService: ITextModelService, @ITextModelService private textModelService: ITextModelService,
@IInstantiationService private instantiationService: IInstantiationService, @IInstantiationService private instantiationService: IInstantiationService,
@@ -219,7 +219,6 @@ export abstract class NotebookInput extends EditorInput {
@IExtensionService private extensionService: IExtensionService @IExtensionService private extensionService: IExtensionService
) { ) {
super(); super();
this.resource = resource;
this._standardKernels = []; this._standardKernels = [];
this._providersLoaded = this.assignProviders(); this._providersLoaded = this.assignProviders();
this._notebookEditorOpenedTimestamp = Date.now(); this._notebookEditorOpenedTimestamp = Date.now();
@@ -299,8 +298,8 @@ export abstract class NotebookInput extends EditorInput {
private async setTrustForNewEditor(newInput: IEditorInput | undefined): Promise<void> { private async setTrustForNewEditor(newInput: IEditorInput | undefined): Promise<void> {
let isTrusted = this._model.getNotebookModel().trustedMode; let isTrusted = this._model.getNotebookModel().trustedMode;
if (isTrusted && newInput && newInput.getResource() !== this.getResource()) { if (isTrusted && newInput && newInput.resource !== this.resource) {
await this.notebookService.serializeNotebookStateChange(newInput.getResource(), NotebookChangeType.Saved, undefined, true); await this.notebookService.serializeNotebookStateChange(newInput.resource, NotebookChangeType.Saved, undefined, true);
} }
} }
@@ -337,8 +336,8 @@ export abstract class NotebookInput extends EditorInput {
public abstract getTypeId(): string; public abstract getTypeId(): string;
getResource(): URI { get resource(): URI {
return this.resource; return this._resource;
} }
public get untitledEditorModel(): IUntitledTextEditorModel { public get untitledEditorModel(): IUntitledTextEditorModel {
@@ -260,7 +260,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
let editors = this.editorService.visibleControls; let editors = this.editorService.visibleControls;
for (let editor of editors) { for (let editor of editors) {
if (editor && editor.input.getResource() === this._notebookParams.input.notebookUri) { if (editor && editor.input.resource === this._notebookParams.input.notebookUri) {
await editor.group.closeEditor(this._notebookParams.input as NotebookInput, { preserveFocus: true }); // sketchy await editor.group.closeEditor(this._notebookParams.input as NotebookInput, { preserveFocus: true }); // sketchy
break; break;
} }
@@ -23,9 +23,9 @@ export class NotebookEditorInputAssociation implements ILanguageAssociation {
convertInput(activeEditor: IEditorInput): NotebookInput { convertInput(activeEditor: IEditorInput): NotebookInput {
if (activeEditor instanceof FileEditorInput) { if (activeEditor instanceof FileEditorInput) {
return this.instantiationService.createInstance(FileNotebookInput, activeEditor.getName(), activeEditor.getResource(), activeEditor); return this.instantiationService.createInstance(FileNotebookInput, activeEditor.getName(), activeEditor.resource, activeEditor);
} else if (activeEditor instanceof UntitledTextEditorInput) { } else if (activeEditor instanceof UntitledTextEditorInput) {
return this.instantiationService.createInstance(UntitledNotebookInput, activeEditor.getName(), activeEditor.getResource(), activeEditor); return this.instantiationService.createInstance(UntitledNotebookInput, activeEditor.getName(), activeEditor.resource, activeEditor);
} else { } else {
return undefined; return undefined;
} }
@@ -48,7 +48,7 @@ export class FileNoteBookEditorInputFactory implements IEditorInputFactory {
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): FileNotebookInput | undefined { deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): FileNotebookInput | undefined {
const factory = editorInputFactoryRegistry.getEditorInputFactory(FILE_EDITOR_INPUT_ID); const factory = editorInputFactoryRegistry.getEditorInputFactory(FILE_EDITOR_INPUT_ID);
const fileEditorInput = factory.deserialize(instantiationService, serializedEditorInput) as FileEditorInput; const fileEditorInput = factory.deserialize(instantiationService, serializedEditorInput) as FileEditorInput;
return instantiationService.createInstance(FileNotebookInput, fileEditorInput.getName(), fileEditorInput.getResource(), fileEditorInput); return instantiationService.createInstance(FileNotebookInput, fileEditorInput.getName(), fileEditorInput.resource, fileEditorInput);
} }
canSerialize(): boolean { // we can always serialize notebooks canSerialize(): boolean { // we can always serialize notebooks
@@ -68,7 +68,7 @@ export class UntitledNoteBookEditorInputFactory implements IEditorInputFactory {
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledNotebookInput | undefined { deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledNotebookInput | undefined {
const factory = editorInputFactoryRegistry.getEditorInputFactory(UntitledTextEditorInput.ID); const factory = editorInputFactoryRegistry.getEditorInputFactory(UntitledTextEditorInput.ID);
const untitledEditorInput = factory.deserialize(instantiationService, serializedEditorInput) as UntitledTextEditorInput; const untitledEditorInput = factory.deserialize(instantiationService, serializedEditorInput) as UntitledTextEditorInput;
return instantiationService.createInstance(UntitledNotebookInput, untitledEditorInput.getName(), untitledEditorInput.getResource(), untitledEditorInput); return instantiationService.createInstance(UntitledNotebookInput, untitledEditorInput.getName(), untitledEditorInput.resource, untitledEditorInput);
} }
canSerialize(): boolean { // we can always serialize notebooks canSerialize(): boolean { // we can always serialize notebooks
@@ -115,7 +115,7 @@ suite('Notebook Input', function (): void {
assert.strictEqual(untitledNotebookInput.untitledEditorModel, testModel); assert.strictEqual(untitledNotebookInput.untitledEditorModel, testModel);
// getResource // getResource
assert.strictEqual(untitledNotebookInput.getResource(), untitledUri); assert.strictEqual(untitledNotebookInput.resource, untitledUri);
// Standard kernels // Standard kernels
let testKernels = [{ let testKernels = [{
@@ -96,12 +96,12 @@ export class SqlFlavorStatusbarItem extends Disposable implements IWorkbenchCont
} }
private _onEditorClosed(event: IEditorCloseEvent): void { private _onEditorClosed(event: IEditorCloseEvent): void {
let uri = event.editor.getResource().toString(); let uri = event.editor.resource.toString();
if (uri && uri in this._sqlStatusEditors) { if (uri && uri in this._sqlStatusEditors) {
// If active editor is being closed, hide the query status. // If active editor is being closed, hide the query status.
let activeEditor = this.editorService.activeControl; let activeEditor = this.editorService.activeControl;
if (activeEditor) { if (activeEditor) {
let currentUri = activeEditor.input.getResource().toString(); let currentUri = activeEditor.input.resource.toString();
if (uri === currentUri) { if (uri === currentUri) {
this.hide(); this.hide();
} }
@@ -114,7 +114,7 @@ export class SqlFlavorStatusbarItem extends Disposable implements IWorkbenchCont
private _onEditorsChanged(): void { private _onEditorsChanged(): void {
let activeEditor = this.editorService.activeControl; let activeEditor = this.editorService.activeControl;
if (activeEditor) { if (activeEditor) {
let uri = activeEditor.input.getResource().toString(); let uri = activeEditor.input.resource.toString();
// Show active editor's language flavor status // Show active editor's language flavor status
if (uri) { if (uri) {
@@ -145,7 +145,7 @@ export class SqlFlavorStatusbarItem extends Disposable implements IWorkbenchCont
private _showStatus(uri: string): void { private _showStatus(uri: string): void {
let activeEditor = this.editorService.activeControl; let activeEditor = this.editorService.activeControl;
if (activeEditor) { if (activeEditor) {
let currentUri = activeEditor.input.getResource().toString(); let currentUri = activeEditor.input.resource.toString();
if (uri === currentUri) { if (uri === currentUri) {
let flavor: SqlProviderEntry = this._sqlStatusEditors[uri]; let flavor: SqlProviderEntry = this._sqlStatusEditors[uri];
if (flavor) { if (flavor) {
@@ -186,7 +186,7 @@ export class ChangeFlavorAction extends Action {
public run(): Promise<any> { public run(): Promise<any> {
let activeEditor = this._editorService.activeControl; let activeEditor = this._editorService.activeControl;
let currentUri = activeEditor?.input.getResource().toString(); let currentUri = activeEditor?.input.resource.toString();
if (this._connectionManagementService.isConnected(currentUri)) { if (this._connectionManagementService.isConnected(currentUri)) {
let currentProvider = this._connectionManagementService.getProviderIdFromUri(currentUri); let currentProvider = this._connectionManagementService.getProviderIdFromUri(currentUri);
return this._showMessage(Severity.Info, nls.localize('alreadyConnected', return this._showMessage(Severity.Info, nls.localize('alreadyConnected',
@@ -226,4 +226,3 @@ export class ChangeFlavorAction extends Action {
return Promise.resolve(undefined); return Promise.resolve(undefined);
} }
} }
@@ -105,7 +105,7 @@ export class QueryEditor extends BaseEditor {
this.queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService); this.queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService);
// Clear view state for deleted files // Clear view state for deleted files
this._register(fileService.onFileChanges(e => this.onFilesChanged(e))); this._register(fileService.onDidFilesChange(e => this.onFilesChanged(e)));
} }
private onFilesChanged(e: FileChangesEvent): void { private onFilesChanged(e: FileChangesEvent): void {
@@ -317,7 +317,7 @@ export class QueryEditor extends BaseEditor {
this.inputDisposables.add(this.input.state.onChange(c => this.updateState(c))); this.inputDisposables.add(this.input.state.onChange(c => this.updateState(c)));
this.updateState({ connectingChange: true, connectedChange: true, executingChange: true, resultsVisibleChange: true, sqlCmdModeChanged: true }); this.updateState({ connectingChange: true, connectedChange: true, executingChange: true, resultsVisibleChange: true, sqlCmdModeChanged: true });
const editorViewState = this.loadTextEditorViewState(this.input.getResource()); const editorViewState = this.loadTextEditorViewState(this.input.resource);
if (editorViewState && editorViewState.resultsHeight && this.splitview.length > 1) { if (editorViewState && editorViewState.resultsHeight && this.splitview.length > 1) {
this.splitview.resizeView(1, editorViewState.resultsHeight); this.splitview.resizeView(1, editorViewState.resultsHeight);
@@ -331,7 +331,7 @@ export class QueryEditor extends BaseEditor {
// Otherwise we save the view state to restore it later // Otherwise we save the view state to restore it later
else if (!input.isDisposed()) { else if (!input.isDisposed()) {
this.saveTextEditorViewState(input.getResource()); this.saveTextEditorViewState(input.resource);
} }
} }
@@ -34,7 +34,7 @@ export class QueryEditorLanguageAssociation implements ILanguageAssociation {
@IEditorService private readonly editorService: IEditorService) { } @IEditorService private readonly editorService: IEditorService) { }
convertInput(activeEditor: IEditorInput): QueryEditorInput | undefined { convertInput(activeEditor: IEditorInput): QueryEditorInput | undefined {
const queryResultsInput = this.instantiationService.createInstance(QueryResultsInput, activeEditor.getResource().toString(true)); const queryResultsInput = this.instantiationService.createInstance(QueryResultsInput, activeEditor.resource.toString(true));
let queryEditorInput: QueryEditorInput; let queryEditorInput: QueryEditorInput;
if (activeEditor instanceof FileEditorInput) { if (activeEditor instanceof FileEditorInput) {
queryEditorInput = this.instantiationService.createInstance(FileQueryEditorInput, '', activeEditor, queryResultsInput); queryEditorInput = this.instantiationService.createInstance(FileQueryEditorInput, '', activeEditor, queryResultsInput);
@@ -81,8 +81,8 @@ export class FileQueryEditorInputFactory implements IEditorInputFactory {
const factory = editorInputFactoryRegistry.getEditorInputFactory(FILE_EDITOR_INPUT_ID); const factory = editorInputFactoryRegistry.getEditorInputFactory(FILE_EDITOR_INPUT_ID);
const fileEditorInput = factory.deserialize(instantiationService, serializedEditorInput) as FileEditorInput; const fileEditorInput = factory.deserialize(instantiationService, serializedEditorInput) as FileEditorInput;
// only successfully deserilize the file if the resource actually exists // only successfully deserilize the file if the resource actually exists
if (this.fileService.exists(fileEditorInput.getResource())) { if (this.fileService.exists(fileEditorInput.resource)) {
const queryResultsInput = instantiationService.createInstance(QueryResultsInput, fileEditorInput.getResource().toString()); const queryResultsInput = instantiationService.createInstance(QueryResultsInput, fileEditorInput.resource.toString());
return instantiationService.createInstance(FileQueryEditorInput, '', fileEditorInput, queryResultsInput); return instantiationService.createInstance(FileQueryEditorInput, '', fileEditorInput, queryResultsInput);
} else { } else {
fileEditorInput.dispose(); fileEditorInput.dispose();
@@ -110,7 +110,7 @@ export class UntitledQueryEditorInputFactory implements IEditorInputFactory {
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledQueryEditorInput | undefined { deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledQueryEditorInput | undefined {
const factory = editorInputFactoryRegistry.getEditorInputFactory(UntitledTextEditorInput.ID); const factory = editorInputFactoryRegistry.getEditorInputFactory(UntitledTextEditorInput.ID);
const untitledEditorInput = factory.deserialize(instantiationService, serializedEditorInput) as UntitledTextEditorInput; const untitledEditorInput = factory.deserialize(instantiationService, serializedEditorInput) as UntitledTextEditorInput;
const queryResultsInput = instantiationService.createInstance(QueryResultsInput, untitledEditorInput.getResource().toString()); const queryResultsInput = instantiationService.createInstance(QueryResultsInput, untitledEditorInput.resource.toString());
return instantiationService.createInstance(UntitledQueryEditorInput, '', untitledEditorInput, queryResultsInput); return instantiationService.createInstance(UntitledQueryEditorInput, '', untitledEditorInput, queryResultsInput);
} }
@@ -17,7 +17,7 @@ export class QueryPlanConverter implements ILanguageAssociation {
constructor(@IInstantiationService private instantiationService: IInstantiationService) { } constructor(@IInstantiationService private instantiationService: IInstantiationService) { }
convertInput(activeEditor: IEditorInput): QueryPlanInput { convertInput(activeEditor: IEditorInput): QueryPlanInput {
return this.instantiationService.createInstance(QueryPlanInput, activeEditor.getResource()); return this.instantiationService.createInstance(QueryPlanInput, activeEditor.resource);
} }
createBase(activeEditor: QueryPlanInput): IEditorInput { createBase(activeEditor: QueryPlanInput): IEditorInput {
@@ -83,4 +83,8 @@ export class QueryPlanInput extends EditorInput {
public get uniqueSelector(): string { public get uniqueSelector(): string {
return this._uniqueSelector; return this._uniqueSelector;
} }
get resource(): URI | undefined {
return undefined;
}
} }
+1
View File
@@ -11,6 +11,7 @@
"strictBindCallApply": true, "strictBindCallApply": true,
"strictNullChecks": false, "strictNullChecks": false,
"strictPropertyInitialization": false, "strictPropertyInitialization": false,
"strict": false,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
+9 -9
View File
@@ -251,14 +251,14 @@ export interface IGlobOptions {
} }
interface ParsedStringPattern { interface ParsedStringPattern {
(path: string, basename: string): string | null | Promise<string | null> /* the matching pattern */; (path: string, basename?: string): string | null | Promise<string | null> /* the matching pattern */;
basenames?: string[]; basenames?: string[];
patterns?: string[]; patterns?: string[];
allBasenames?: string[]; allBasenames?: string[];
allPaths?: string[]; allPaths?: string[];
} }
interface ParsedExpressionPattern { interface ParsedExpressionPattern {
(path: string, basename: string, name?: string, hasSibling?: (name: string) => boolean | Promise<boolean>): string | null | Promise<string | null> /* the matching pattern */; (path: string, basename?: string, name?: string, hasSibling?: (name: string) => boolean | Promise<boolean>): string | null | Promise<string | null> /* the matching pattern */;
requiresSiblings?: boolean; requiresSiblings?: boolean;
allBasenames?: string[]; allBasenames?: string[];
allPaths?: string[]; allPaths?: string[];
@@ -374,7 +374,7 @@ function trivia3(pattern: string, options: IGlobOptions): ParsedStringPattern {
if (n === 1) { if (n === 1) {
return <ParsedStringPattern>parsedPatterns[0]; return <ParsedStringPattern>parsedPatterns[0];
} }
const parsedPattern: ParsedStringPattern = function (path: string, basename: string) { const parsedPattern: ParsedStringPattern = function (path: string, basename?: string) {
for (let i = 0, n = parsedPatterns.length; i < n; i++) { for (let i = 0, n = parsedPatterns.length; i < n; i++) {
if ((<ParsedStringPattern>parsedPatterns[i])(path, basename)) { if ((<ParsedStringPattern>parsedPatterns[i])(path, basename)) {
return pattern; return pattern;
@@ -409,7 +409,7 @@ function trivia4and5(path: string, pattern: string, matchPathEnds: boolean): Par
function toRegExp(pattern: string): ParsedStringPattern { function toRegExp(pattern: string): ParsedStringPattern {
try { try {
const regExp = new RegExp(`^${parseRegExp(pattern)}$`); const regExp = new RegExp(`^${parseRegExp(pattern)}$`);
return function (path: string, basename: string) { return function (path: string) {
regExp.lastIndex = 0; // reset RegExp to its initial state to reuse it! regExp.lastIndex = 0; // reset RegExp to its initial state to reuse it!
return typeof path === 'string' && regExp.test(path) ? pattern : null; return typeof path === 'string' && regExp.test(path) ? pattern : null;
}; };
@@ -457,7 +457,7 @@ export function parse(arg1: string | IExpression | IRelativePattern, options: IG
if (parsedPattern === NULL) { if (parsedPattern === NULL) {
return FALSE; return FALSE;
} }
const resultPattern: ParsedPattern & { allBasenames?: string[]; allPaths?: string[]; } = function (path: string, basename: string) { const resultPattern: ParsedPattern & { allBasenames?: string[]; allPaths?: string[]; } = function (path: string, basename?: string) {
return !!parsedPattern(path, basename); return !!parsedPattern(path, basename);
}; };
if (parsedPattern.allBasenames) { if (parsedPattern.allBasenames) {
@@ -540,7 +540,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse
return <ParsedStringPattern>parsedPatterns[0]; return <ParsedStringPattern>parsedPatterns[0];
} }
const resultExpression: ParsedStringPattern = function (path: string, basename: string) { const resultExpression: ParsedStringPattern = function (path: string, basename?: string) {
for (let i = 0, n = parsedPatterns.length; i < n; i++) { for (let i = 0, n = parsedPatterns.length; i < n; i++) {
// Pattern matches path // Pattern matches path
const result = (<ParsedStringPattern>parsedPatterns[i])(path, basename); const result = (<ParsedStringPattern>parsedPatterns[i])(path, basename);
@@ -565,7 +565,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse
return resultExpression; return resultExpression;
} }
const resultExpression: ParsedStringPattern = function (path: string, basename: string, hasSibling?: (name: string) => boolean | Promise<boolean>) { const resultExpression: ParsedStringPattern = function (path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise<boolean>) {
let name: string | undefined = undefined; let name: string | undefined = undefined;
for (let i = 0, n = parsedPatterns.length; i < n; i++) { for (let i = 0, n = parsedPatterns.length; i < n; i++) {
@@ -620,12 +620,12 @@ function parseExpressionPattern(pattern: string, value: boolean | SiblingClause,
if (value) { if (value) {
const when = (<SiblingClause>value).when; const when = (<SiblingClause>value).when;
if (typeof when === 'string') { if (typeof when === 'string') {
const result: ParsedExpressionPattern = (path: string, basename: string, name: string, hasSibling: (name: string) => boolean | Promise<boolean>) => { const result: ParsedExpressionPattern = (path: string, basename?: string, name?: string, hasSibling?: (name: string) => boolean | Promise<boolean>) => {
if (!hasSibling || !parsedPattern(path, basename)) { if (!hasSibling || !parsedPattern(path, basename)) {
return null; return null;
} }
const clausePattern = when.replace('$(basename)', name); const clausePattern = when.replace('$(basename)', name!);
const matched = hasSibling(clausePattern); const matched = hasSibling(clausePattern);
return isThenable(matched) ? return isThenable(matched) ?
matched.then(m => m ? pattern : null) : matched.then(m => m ? pattern : null) :
@@ -3,6 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { memoize } from 'vs/base/common/decorators';
export interface ILink { export interface ILink {
readonly label: string; readonly label: string;
readonly href: string; readonly href: string;
@@ -10,7 +12,16 @@ export interface ILink {
} }
export type LinkedTextNode = string | ILink; export type LinkedTextNode = string | ILink;
export type LinkedText = LinkedTextNode[];
export class LinkedText {
constructor(readonly nodes: LinkedTextNode[]) { }
@memoize
toString(): string {
return this.nodes.map(node => typeof node === 'string' ? node : node.label).join('');
}
}
const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: "([^"]+)")?\)/gi; const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: "([^"]+)")?\)/gi;
@@ -40,5 +51,5 @@ export function parseLinkedText(text: string): LinkedText {
result.push(text.substring(index)); result.push(text.substring(index));
} }
return result; return new LinkedText(result);
} }
@@ -4,50 +4,50 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as assert from 'assert'; import * as assert from 'assert';
import { parseLinkedText } from 'vs/base/browser/linkedText'; import { parseLinkedText } from 'vs/base/common/linkedText';
suite('LinkedText', () => { suite('LinkedText', () => {
test('parses correctly', () => { test('parses correctly', () => {
assert.deepEqual(parseLinkedText(''), []); assert.deepEqual(parseLinkedText('').nodes, []);
assert.deepEqual(parseLinkedText('hello'), ['hello']); assert.deepEqual(parseLinkedText('hello').nodes, ['hello']);
assert.deepEqual(parseLinkedText('hello there'), ['hello there']); assert.deepEqual(parseLinkedText('hello there').nodes, ['hello there']);
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href).'), [ assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href).').nodes, [
'Some message with ', 'Some message with ',
{ label: 'link text', href: 'http://link.href' }, { label: 'link text', href: 'http://link.href' },
'.' '.'
]); ]);
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a title").'), [ assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a title").').nodes, [
'Some message with ', 'Some message with ',
{ label: 'link text', href: 'http://link.href', title: 'and a title' }, { label: 'link text', href: 'http://link.href', title: 'and a title' },
'.' '.'
]); ]);
assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).'), [ assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).').nodes, [
'Some message with [link text](random stuff).' 'Some message with [link text](random stuff).'
]); ]);
assert.deepEqual(parseLinkedText('Some message with [https link](https://link.href).'), [ assert.deepEqual(parseLinkedText('Some message with [https link](https://link.href).').nodes, [
'Some message with ', 'Some message with ',
{ label: 'https link', href: 'https://link.href' }, { label: 'https link', href: 'https://link.href' },
'.' '.'
]); ]);
assert.deepEqual(parseLinkedText('Some message with [https link](https:).'), [ assert.deepEqual(parseLinkedText('Some message with [https link](https:).').nodes, [
'Some message with [https link](https:).' 'Some message with [https link](https:).'
]); ]);
assert.deepEqual(parseLinkedText('Some message with [a command](command:foobar).'), [ assert.deepEqual(parseLinkedText('Some message with [a command](command:foobar).').nodes, [
'Some message with ', 'Some message with ',
{ label: 'a command', href: 'command:foobar' }, { label: 'a command', href: 'command:foobar' },
'.' '.'
]); ]);
assert.deepEqual(parseLinkedText('Some message with [a command](command:).'), [ assert.deepEqual(parseLinkedText('Some message with [a command](command:).').nodes, [
'Some message with [a command](command:).' 'Some message with [a command](command:).'
]); ]);
assert.deepEqual(parseLinkedText('link [one](command:foo "nice") and link [two](http://foo)...'), [ assert.deepEqual(parseLinkedText('link [one](command:foo "nice") and link [two](http://foo)...').nodes, [
'link ', 'link ',
{ label: 'one', href: 'command:foo', title: 'nice' }, { label: 'one', href: 'command:foo', title: 'nice' },
' and link ', ' and link ',
{ label: 'two', href: 'http://foo' }, { label: 'two', href: 'http://foo' },
'...' '...'
]); ]);
assert.deepEqual(parseLinkedText('link\n[one](command:foo "nice")\nand link [two](http://foo)...'), [ assert.deepEqual(parseLinkedText('link\n[one](command:foo "nice")\nand link [two](http://foo)...').nodes, [
'link\n', 'link\n',
{ label: 'one', href: 'command:foo', title: 'nice' }, { label: 'one', href: 'command:foo', title: 'nice' },
'\nand link ', '\nand link ',
@@ -17,7 +17,7 @@ import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtension
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
import { IRequestService } from 'vs/platform/request/common/request'; import { IRequestService } from 'vs/platform/request/common/request';
import { RequestService } from 'vs/platform/request/browser/requestService'; import { RequestService } from 'vs/platform/request/browser/requestService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+1 -1
View File
@@ -26,7 +26,7 @@ import { IStateService } from 'vs/platform/state/node/state';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { EnvironmentService, xdgRuntimeDir } from 'vs/platform/environment/node/environmentService'; import { EnvironmentService, xdgRuntimeDir } from 'vs/platform/environment/node/environmentService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
import { IRequestService } from 'vs/platform/request/common/request'; import { IRequestService } from 'vs/platform/request/common/request';
import { RequestMainService } from 'vs/platform/request/electron-main/requestMainService'; import { RequestMainService } from 'vs/platform/request/electron-main/requestMainService';
import * as fs from 'fs'; import * as fs from 'fs';
+1 -1
View File
@@ -24,7 +24,7 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper
import { IRequestService } from 'vs/platform/request/common/request'; import { IRequestService } from 'vs/platform/request/common/request';
import { RequestService } from 'vs/platform/request/node/requestService'; import { RequestService } from 'vs/platform/request/node/requestService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
import { mkdirp, writeFile } from 'vs/base/node/pfs'; import { mkdirp, writeFile } from 'vs/base/node/pfs';
import { getBaseLabel } from 'vs/base/common/labels'; import { getBaseLabel } from 'vs/base/common/labels';
@@ -1614,7 +1614,7 @@ export namespace CoreEditingCommands {
constructor() { constructor() {
super({ super({
id: 'deleteLeft', id: 'deleteLeft',
precondition: EditorContextKeys.writable, precondition: undefined,
kbOpts: { kbOpts: {
weight: CORE_WEIGHT, weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus, kbExpr: EditorContextKeys.textInputFocus,
@@ -1639,7 +1639,7 @@ export namespace CoreEditingCommands {
constructor() { constructor() {
super({ super({
id: 'deleteRight', id: 'deleteRight',
precondition: EditorContextKeys.writable, precondition: undefined,
kbOpts: { kbOpts: {
weight: CORE_WEIGHT, weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus, kbExpr: EditorContextKeys.textInputFocus,
@@ -149,6 +149,10 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe
return result; return result;
} }
const enum Constants {
SPAN_MODULO_LIMIT = 16384
}
function renderLine(lineContent: string, initialVisibleColumn: number, tabSize: number, width: number, sb: IStringBuilder): [number[], number[]] { function renderLine(lineContent: string, initialVisibleColumn: number, tabSize: number, width: number, sb: IStringBuilder): [number[], number[]] {
sb.appendASCIIString('<div style="width:'); sb.appendASCIIString('<div style="width:');
sb.appendASCIIString(String(width)); sb.appendASCIIString(String(width));
@@ -164,7 +168,11 @@ function renderLine(lineContent: string, initialVisibleColumn: number, tabSize:
let visibleColumns: number[] = []; let visibleColumns: number[] = [];
let nextCharCode = (0 < len ? lineContent.charCodeAt(0) : CharCode.Null); let nextCharCode = (0 < len ? lineContent.charCodeAt(0) : CharCode.Null);
sb.appendASCIIString('<span>');
for (let charIndex = 0; charIndex < len; charIndex++) { for (let charIndex = 0; charIndex < len; charIndex++) {
if (charIndex !== 0 && charIndex % Constants.SPAN_MODULO_LIMIT === 0) {
sb.appendASCIIString('</span><span>');
}
charOffsets[charIndex] = charOffset; charOffsets[charIndex] = charOffset;
visibleColumns[charIndex] = visibleColumn; visibleColumns[charIndex] = visibleColumn;
const charCode = nextCharCode; const charCode = nextCharCode;
@@ -227,6 +235,7 @@ function renderLine(lineContent: string, initialVisibleColumn: number, tabSize:
charOffset += producedCharacters; charOffset += producedCharacters;
visibleColumn += charWidth; visibleColumn += charWidth;
} }
sb.appendASCIIString('</span>');
charOffsets[lineContent.length] = charOffset; charOffsets[lineContent.length] = charOffset;
visibleColumns[lineContent.length] = visibleColumn; visibleColumns[lineContent.length] = visibleColumn;
@@ -240,10 +249,15 @@ function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent:
if (lineContent.length <= 1) { if (lineContent.length <= 1) {
return null; return null;
} }
const textContentNode = lineDomNode.firstChild!; const spans = <HTMLSpanElement[]>Array.prototype.slice.call(lineDomNode.children, 0);
const breakOffsets: number[] = []; const breakOffsets: number[] = [];
discoverBreaks(range, textContentNode, charOffsets, 0, null, lineContent.length - 1, null, breakOffsets); try {
discoverBreaks(range, spans, charOffsets, 0, null, lineContent.length - 1, null, breakOffsets);
} catch (err) {
console.log(err);
return null;
}
if (breakOffsets.length === 0) { if (breakOffsets.length === 0) {
return null; return null;
@@ -255,13 +269,13 @@ function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent:
type MaybeRects = ClientRectList | DOMRectList | null; type MaybeRects = ClientRectList | DOMRectList | null;
function discoverBreaks(range: Range, textContentNode: Node, charOffsets: number[], low: number, lowRects: MaybeRects, high: number, highRects: MaybeRects, result: number[]): void { function discoverBreaks(range: Range, spans: HTMLSpanElement[], charOffsets: number[], low: number, lowRects: MaybeRects, high: number, highRects: MaybeRects, result: number[]): void {
if (low === high) { if (low === high) {
return; return;
} }
lowRects = lowRects || readClientRect(range, textContentNode, charOffsets[low], charOffsets[low + 1]); lowRects = lowRects || readClientRect(range, spans, charOffsets[low], charOffsets[low + 1]);
highRects = highRects || readClientRect(range, textContentNode, charOffsets[high], charOffsets[high + 1]); highRects = highRects || readClientRect(range, spans, charOffsets[high], charOffsets[high + 1]);
if (Math.abs(lowRects[0].top - highRects[0].top) <= 0.1) { if (Math.abs(lowRects[0].top - highRects[0].top) <= 0.1) {
// same line // same line
@@ -276,13 +290,13 @@ function discoverBreaks(range: Range, textContentNode: Node, charOffsets: number
} }
const mid = low + ((high - low) / 2) | 0; const mid = low + ((high - low) / 2) | 0;
const midRects = readClientRect(range, textContentNode, charOffsets[mid], charOffsets[mid + 1]); const midRects = readClientRect(range, spans, charOffsets[mid], charOffsets[mid + 1]);
discoverBreaks(range, textContentNode, charOffsets, low, lowRects, mid, midRects, result); discoverBreaks(range, spans, charOffsets, low, lowRects, mid, midRects, result);
discoverBreaks(range, textContentNode, charOffsets, mid, midRects, high, highRects, result); discoverBreaks(range, spans, charOffsets, mid, midRects, high, highRects, result);
} }
function readClientRect(range: Range, textContentNode: Node, startOffset: number, endOffset: number): ClientRectList | DOMRectList { function readClientRect(range: Range, spans: HTMLSpanElement[], startOffset: number, endOffset: number): ClientRectList | DOMRectList {
range.setStart(textContentNode, startOffset); range.setStart(spans[(startOffset / Constants.SPAN_MODULO_LIMIT) | 0].firstChild!, startOffset % Constants.SPAN_MODULO_LIMIT);
range.setEnd(textContentNode, endOffset); range.setEnd(spans[(endOffset / Constants.SPAN_MODULO_LIMIT) | 0].firstChild!, endOffset % Constants.SPAN_MODULO_LIMIT);
return range.getClientRects(); return range.getClientRects();
} }
File diff suppressed because it is too large Load Diff
@@ -34,11 +34,13 @@ export class MinimapCharRenderer {
color: RGBA8, color: RGBA8,
backgroundColor: RGBA8, backgroundColor: RGBA8,
fontScale: number, fontScale: number,
useLighterFont: boolean useLighterFont: boolean,
force1pxHeight: boolean
): void { ): void {
const charWidth = Constants.BASE_CHAR_WIDTH * this.scale; const charWidth = Constants.BASE_CHAR_WIDTH * this.scale;
const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale; const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale;
if (dx + charWidth > target.width || dy + charHeight > target.height) { const renderHeight = (force1pxHeight ? 1 : charHeight);
if (dx + charWidth > target.width || dy + renderHeight > target.height) {
console.warn('bad render request outside image data'); console.warn('bad render request outside image data');
return; return;
} }
@@ -60,7 +62,7 @@ export class MinimapCharRenderer {
let sourceOffset = charIndex * charWidth * charHeight; let sourceOffset = charIndex * charWidth * charHeight;
let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT; let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT;
for (let y = 0; y < charHeight; y++) { for (let y = 0; y < renderHeight; y++) {
let column = row; let column = row;
for (let x = 0; x < charWidth; x++) { for (let x = 0; x < charWidth; x++) {
const c = charData[sourceOffset++] / 255; const c = charData[sourceOffset++] / 255;
@@ -80,11 +82,13 @@ export class MinimapCharRenderer {
dy: number, dy: number,
color: RGBA8, color: RGBA8,
backgroundColor: RGBA8, backgroundColor: RGBA8,
useLighterFont: boolean useLighterFont: boolean,
force1pxHeight: boolean
): void { ): void {
const charWidth = Constants.BASE_CHAR_WIDTH * this.scale; const charWidth = Constants.BASE_CHAR_WIDTH * this.scale;
const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale; const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale;
if (dx + charWidth > target.width || dy + charHeight > target.height) { const renderHeight = (force1pxHeight ? 1 : charHeight);
if (dx + charWidth > target.width || dy + renderHeight > target.height) {
console.warn('bad render request outside image data'); console.warn('bad render request outside image data');
return; return;
} }
@@ -108,7 +112,7 @@ export class MinimapCharRenderer {
const dest = target.data; const dest = target.data;
let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT; let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT;
for (let y = 0; y < charHeight; y++) { for (let y = 0; y < renderHeight; y++) {
let column = row; let column = row;
for (let x = 0; x < charWidth; x++) { for (let x = 0; x < charWidth; x++) {
dest[column++] = colorR; dest[column++] = colorR;
@@ -276,7 +276,10 @@ export class DecorationsOverviewRuler extends ViewPart {
return true; return true;
} }
public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean {
return true; if (e.affectsOverviewRuler) {
return true;
}
return false;
} }
public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { public onFlushed(e: viewEvents.ViewFlushedEvent): boolean {
return true; return true;
@@ -287,6 +287,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
public options!: ComputedEditorOptions; public options!: ComputedEditorOptions;
private _isDominatedByLongLines: boolean; private _isDominatedByLongLines: boolean;
private _maxLineNumber: number;
private _lineNumbersDigitCount: number; private _lineNumbersDigitCount: number;
private _rawOptions: IEditorOptions; private _rawOptions: IEditorOptions;
@@ -298,6 +299,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
this.isSimpleWidget = isSimpleWidget; this.isSimpleWidget = isSimpleWidget;
this._isDominatedByLongLines = false; this._isDominatedByLongLines = false;
this._maxLineNumber = 1;
this._lineNumbersDigitCount = 1; this._lineNumbersDigitCount = 1;
this._rawOptions = deepCloneAndMigrateOptions(_options); this._rawOptions = deepCloneAndMigrateOptions(_options);
@@ -347,6 +349,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
fontInfo: this.readConfiguration(bareFontInfo), fontInfo: this.readConfiguration(bareFontInfo),
extraEditorClassName: partialEnv.extraEditorClassName, extraEditorClassName: partialEnv.extraEditorClassName,
isDominatedByLongLines: this._isDominatedByLongLines, isDominatedByLongLines: this._isDominatedByLongLines,
maxLineNumber: this._maxLineNumber,
lineNumbersDigitCount: this._lineNumbersDigitCount, lineNumbersDigitCount: this._lineNumbersDigitCount,
emptySelectionClipboard: partialEnv.emptySelectionClipboard, emptySelectionClipboard: partialEnv.emptySelectionClipboard,
pixelRatio: partialEnv.pixelRatio, pixelRatio: partialEnv.pixelRatio,
@@ -405,11 +408,11 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
} }
public setMaxLineNumber(maxLineNumber: number): void { public setMaxLineNumber(maxLineNumber: number): void {
let digitCount = CommonEditorConfiguration._digitCount(maxLineNumber); if (this._maxLineNumber === maxLineNumber) {
if (this._lineNumbersDigitCount === digitCount) {
return; return;
} }
this._lineNumbersDigitCount = digitCount; this._maxLineNumber = maxLineNumber;
this._lineNumbersDigitCount = CommonEditorConfiguration._digitCount(maxLineNumber);
this._recomputeOptions(); this._recomputeOptions();
} }
+108 -6
View File
@@ -678,6 +678,7 @@ export interface IEnvironmentalOptions {
readonly fontInfo: FontInfo; readonly fontInfo: FontInfo;
readonly extraEditorClassName: string; readonly extraEditorClassName: string;
readonly isDominatedByLongLines: boolean; readonly isDominatedByLongLines: boolean;
readonly maxLineNumber: number;
readonly lineNumbersDigitCount: number; readonly lineNumbersDigitCount: number;
readonly emptySelectionClipboard: boolean; readonly emptySelectionClipboard: boolean;
readonly pixelRatio: number; readonly pixelRatio: number;
@@ -1691,6 +1692,14 @@ export interface EditorLayoutInfo {
* The width of the minimap * The width of the minimap
*/ */
readonly minimapWidth: number; readonly minimapWidth: number;
readonly minimapHeightIsEditorHeight: boolean;
readonly minimapIsSampling: boolean;
readonly minimapScale: number;
readonly minimapLineHeight: number;
readonly minimapCanvasInnerWidth: number;
readonly minimapCanvasInnerHeight: number;
readonly minimapCanvasOuterWidth: number;
readonly minimapCanvasOuterHeight: number;
/** /**
* Minimap render type * Minimap render type
@@ -1724,6 +1733,7 @@ export interface EditorLayoutInfoComputerEnv {
outerWidth: number; outerWidth: number;
outerHeight: number; outerHeight: number;
lineHeight: number; lineHeight: number;
maxLineNumber: number;
lineNumbersDigitCount: number; lineNumbersDigitCount: number;
typicalHalfwidthCharacterWidth: number; typicalHalfwidthCharacterWidth: number;
maxDigitWidth: number; maxDigitWidth: number;
@@ -1747,6 +1757,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
outerWidth: env.outerWidth, outerWidth: env.outerWidth,
outerHeight: env.outerHeight, outerHeight: env.outerHeight,
lineHeight: env.fontInfo.lineHeight, lineHeight: env.fontInfo.lineHeight,
maxLineNumber: env.maxLineNumber,
lineNumbersDigitCount: env.lineNumbersDigitCount, lineNumbersDigitCount: env.lineNumbersDigitCount,
typicalHalfwidthCharacterWidth: env.fontInfo.typicalHalfwidthCharacterWidth, typicalHalfwidthCharacterWidth: env.fontInfo.typicalHalfwidthCharacterWidth,
maxDigitWidth: env.fontInfo.maxDigitWidth, maxDigitWidth: env.fontInfo.maxDigitWidth,
@@ -1754,6 +1765,20 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
}); });
} }
public static computeContainedMinimapLineCount(input: {
modelLineCount: number;
scrollBeyondLastLine: boolean;
height: number;
lineHeight: number;
pixelRatio: number;
}): { typicalViewportLineCount: number; extraLinesBeyondLastLine: number; desiredRatio: number; minimapLineCount: number; } {
const typicalViewportLineCount = input.height / input.lineHeight;
const extraLinesBeyondLastLine = input.scrollBeyondLastLine ? (typicalViewportLineCount - 1) : 0;
const desiredRatio = (input.modelLineCount + extraLinesBeyondLastLine) / (input.pixelRatio * input.height);
const minimapLineCount = Math.floor(input.modelLineCount / desiredRatio);
return { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount };
}
public static computeLayout(options: IComputedEditorOptions, env: EditorLayoutInfoComputerEnv): EditorLayoutInfo { public static computeLayout(options: IComputedEditorOptions, env: EditorLayoutInfoComputerEnv): EditorLayoutInfo {
const outerWidth = env.outerWidth | 0; const outerWidth = env.outerWidth | 0;
const outerHeight = env.outerHeight | 0; const outerHeight = env.outerHeight | 0;
@@ -1766,12 +1791,14 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
const showGlyphMargin = options.get(EditorOption.glyphMargin); const showGlyphMargin = options.get(EditorOption.glyphMargin);
const showLineNumbers = (options.get(EditorOption.lineNumbers).renderType !== RenderLineNumbersType.Off); const showLineNumbers = (options.get(EditorOption.lineNumbers).renderType !== RenderLineNumbersType.Off);
const lineNumbersMinChars = options.get(EditorOption.lineNumbersMinChars) | 0; const lineNumbersMinChars = options.get(EditorOption.lineNumbersMinChars) | 0;
const scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine);
const minimap = options.get(EditorOption.minimap); const minimap = options.get(EditorOption.minimap);
const minimapEnabled = minimap.enabled; const minimapEnabled = minimap.enabled;
const minimapSide = minimap.side; const minimapSide = minimap.side;
const minimapRenderCharacters = minimap.renderCharacters; const minimapRenderCharacters = minimap.renderCharacters;
const minimapScale = (pixelRatio >= 2 ? Math.round(minimap.scale * 2) : minimap.scale); let minimapScale = (pixelRatio >= 2 ? Math.round(minimap.scale * 2) : minimap.scale);
const minimapMaxColumn = minimap.maxColumn | 0; const minimapMaxColumn = minimap.maxColumn | 0;
const minimapMode = minimap.mode;
const scrollbar = options.get(EditorOption.scrollbar); const scrollbar = options.get(EditorOption.scrollbar);
const verticalScrollbarWidth = scrollbar.verticalScrollbarSize | 0; const verticalScrollbarWidth = scrollbar.verticalScrollbarSize | 0;
@@ -1811,19 +1838,65 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
const remainingWidth = outerWidth - glyphMarginWidth - lineNumbersWidth - lineDecorationsWidth; const remainingWidth = outerWidth - glyphMarginWidth - lineNumbersWidth - lineDecorationsWidth;
const baseCharHeight = minimapRenderCharacters ? 2 : 3;
let renderMinimap: RenderMinimap; let renderMinimap: RenderMinimap;
let minimapLeft: number; let minimapLeft: number;
let minimapWidth: number; let minimapWidth: number;
let minimapCanvasInnerWidth: number;
let minimapCanvasInnerHeight = Math.floor(pixelRatio * outerHeight);
let minimapCanvasOuterWidth: number;
const minimapCanvasOuterHeight = minimapCanvasInnerHeight / pixelRatio;
let minimapHeightIsEditorHeight = false;
let minimapIsSampling = false;
let minimapLineHeight = baseCharHeight * minimapScale;
let contentWidth: number; let contentWidth: number;
if (!minimapEnabled) { if (!minimapEnabled) {
minimapLeft = 0; minimapLeft = 0;
minimapWidth = 0; minimapWidth = 0;
minimapCanvasInnerWidth = 0;
minimapCanvasOuterWidth = 0;
minimapLineHeight = 1;
renderMinimap = RenderMinimap.None; renderMinimap = RenderMinimap.None;
contentWidth = remainingWidth; contentWidth = remainingWidth;
} else { } else {
// The minimapScale is also the pixel width of each character. Adjust let minimapCharWidth = minimapScale / pixelRatio;
// for the pixel ratio of the screen. let minimapWidthMultiplier: number = 1;
const minimapCharWidth = minimapScale / pixelRatio;
if (minimapMode === 'cover' || minimapMode === 'contain') {
const modelLineCount = env.maxLineNumber;
const { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({
modelLineCount: modelLineCount,
scrollBeyondLastLine: scrollBeyondLastLine,
height: outerHeight,
lineHeight: lineHeight,
pixelRatio: pixelRatio
});
// ratio is intentionally not part of the layout to avoid the layout changing all the time
// when doing sampling
const ratio = modelLineCount / minimapLineCount;
if (ratio > 1) {
minimapHeightIsEditorHeight = true;
minimapIsSampling = true;
minimapScale = 1;
minimapLineHeight = 1;
minimapCharWidth = minimapScale / pixelRatio;
} else {
const effectiveMinimapHeight = Math.ceil((modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight);
if (minimapMode === 'cover' || effectiveMinimapHeight > minimapCanvasInnerHeight) {
minimapHeightIsEditorHeight = true;
const configuredFontScale = minimapScale;
minimapLineHeight = Math.min(lineHeight * pixelRatio, Math.max(1, Math.floor(1 / desiredRatio)));
minimapScale = Math.min(configuredFontScale + 1, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight)));
if (minimapScale > configuredFontScale) {
minimapWidthMultiplier = Math.min(2, minimapScale / configuredFontScale);
}
minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier;
minimapCanvasInnerHeight = Math.ceil((Math.max(typicalViewportLineCount, modelLineCount + extraLinesBeyondLastLine)) * minimapLineHeight);
}
}
}
renderMinimap = minimapRenderCharacters ? RenderMinimap.Text : RenderMinimap.Blocks; renderMinimap = minimapRenderCharacters ? RenderMinimap.Text : RenderMinimap.Blocks;
// Given: // Given:
@@ -1855,6 +1928,10 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
} else { } else {
minimapLeft = outerWidth - minimapWidth - verticalScrollbarWidth; minimapLeft = outerWidth - minimapWidth - verticalScrollbarWidth;
} }
minimapCanvasInnerWidth = Math.floor(pixelRatio * minimapWidth);
minimapCanvasOuterWidth = minimapCanvasInnerWidth / pixelRatio;
minimapCanvasInnerWidth = Math.floor(minimapCanvasInnerWidth * minimapWidthMultiplier);
} }
// (leaving 2px for the cursor to have space after the last character) // (leaving 2px for the cursor to have space after the last character)
@@ -1881,6 +1958,14 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
renderMinimap: renderMinimap, renderMinimap: renderMinimap,
minimapLeft: minimapLeft, minimapLeft: minimapLeft,
minimapWidth: minimapWidth, minimapWidth: minimapWidth,
minimapHeightIsEditorHeight: minimapHeightIsEditorHeight,
minimapIsSampling: minimapIsSampling,
minimapScale: minimapScale,
minimapLineHeight: minimapLineHeight,
minimapCanvasInnerWidth: minimapCanvasInnerWidth,
minimapCanvasInnerHeight: minimapCanvasInnerHeight,
minimapCanvasOuterWidth: minimapCanvasOuterWidth,
minimapCanvasOuterHeight: minimapCanvasOuterHeight,
viewportColumn: viewportColumn, viewportColumn: viewportColumn,
@@ -1981,6 +2066,11 @@ export interface IEditorMinimapOptions {
* Defaults to 'right'. * Defaults to 'right'.
*/ */
side?: 'right' | 'left'; side?: 'right' | 'left';
/**
* Control the minimap rendering mode.
* Defaults to 'actual'.
*/
mode?: 'actual' | 'cover' | 'contain';
/** /**
* Control the rendering of the minimap slider. * Control the rendering of the minimap slider.
* Defaults to 'mouseover'. * Defaults to 'mouseover'.
@@ -1996,7 +2086,6 @@ export interface IEditorMinimapOptions {
* Defaults to 120. * Defaults to 120.
*/ */
maxColumn?: number; maxColumn?: number;
/** /**
* Relative size of the font in the minimap. Defaults to 1. * Relative size of the font in the minimap. Defaults to 1.
*/ */
@@ -2010,6 +2099,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
constructor() { constructor() {
const defaults: EditorMinimapOptions = { const defaults: EditorMinimapOptions = {
enabled: false, // {{SQL CARBON EDIT}} disable minimap by default enabled: false, // {{SQL CARBON EDIT}} disable minimap by default
mode: 'actual',
side: 'right', side: 'right',
showSlider: 'mouseover', showSlider: 'mouseover',
renderCharacters: true, renderCharacters: true,
@@ -2024,6 +2114,17 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
default: defaults.enabled, default: defaults.enabled,
description: nls.localize('minimap.enabled', "Controls whether the minimap is shown.") description: nls.localize('minimap.enabled', "Controls whether the minimap is shown.")
}, },
'editor.minimap.mode': {
type: 'string',
enum: ['actual', 'cover', 'contain'],
enumDescriptions: [
nls.localize('minimap.mode.actual', "The minimap will be displayed in its original size, so it might be higher than the editor."),
nls.localize('minimap.mode.cover', "The minimap will always have the height of the editor and will stretch or shrink as necessary."),
nls.localize('minimap.mode.contain', "The minimap will shrink as necessary to never be higher than the editor."),
],
default: defaults.mode,
description: nls.localize('minimap.mode', "Controls the rendering mode of the minimap.")
},
'editor.minimap.side': { 'editor.minimap.side': {
type: 'string', type: 'string',
enum: ['left', 'right'], enum: ['left', 'right'],
@@ -2052,7 +2153,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
type: 'number', type: 'number',
default: defaults.maxColumn, default: defaults.maxColumn,
description: nls.localize('minimap.maxColumn', "Limit the width of the minimap to render at most a certain number of columns.") description: nls.localize('minimap.maxColumn', "Limit the width of the minimap to render at most a certain number of columns.")
}, }
} }
); );
} }
@@ -2064,6 +2165,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
const input = _input as IEditorMinimapOptions; const input = _input as IEditorMinimapOptions;
return { return {
enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled), enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled),
mode: EditorStringEnumOption.stringSet<'actual' | 'cover' | 'contain'>(input.mode, this.defaultValue.mode, ['actual', 'cover', 'contain']),
side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']), side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']),
showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']), showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']),
renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters), renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters),
+54 -14
View File
@@ -1423,19 +1423,15 @@ export class TextModel extends Disposable implements model.ITextModel {
private _changeDecorations<T>(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T | null { private _changeDecorations<T>(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T | null {
let changeAccessor: model.IModelDecorationsChangeAccessor = { let changeAccessor: model.IModelDecorationsChangeAccessor = {
addDecoration: (range: IRange, options: model.IModelDecorationOptions): string => { addDecoration: (range: IRange, options: model.IModelDecorationOptions): string => {
this._onDidChangeDecorations.fire();
return this._deltaDecorationsImpl(ownerId, [], [{ range: range, options: options }])[0]; return this._deltaDecorationsImpl(ownerId, [], [{ range: range, options: options }])[0];
}, },
changeDecoration: (id: string, newRange: IRange): void => { changeDecoration: (id: string, newRange: IRange): void => {
this._onDidChangeDecorations.fire();
this._changeDecorationImpl(id, newRange); this._changeDecorationImpl(id, newRange);
}, },
changeDecorationOptions: (id: string, options: model.IModelDecorationOptions) => { changeDecorationOptions: (id: string, options: model.IModelDecorationOptions) => {
this._onDidChangeDecorations.fire();
this._changeDecorationOptionsImpl(id, _normalizeOptions(options)); this._changeDecorationOptionsImpl(id, _normalizeOptions(options));
}, },
removeDecoration: (id: string): void => { removeDecoration: (id: string): void => {
this._onDidChangeDecorations.fire();
this._deltaDecorationsImpl(ownerId, [id], []); this._deltaDecorationsImpl(ownerId, [id], []);
}, },
deltaDecorations: (oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[]): string[] => { deltaDecorations: (oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[]): string[] => {
@@ -1443,7 +1439,6 @@ export class TextModel extends Disposable implements model.ITextModel {
// nothing to do // nothing to do
return []; return [];
} }
this._onDidChangeDecorations.fire();
return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations); return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
} }
}; };
@@ -1474,7 +1469,6 @@ export class TextModel extends Disposable implements model.ITextModel {
try { try {
this._onDidChangeDecorations.beginDeferredEmit(); this._onDidChangeDecorations.beginDeferredEmit();
this._onDidChangeDecorations.fire();
return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations); return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
} finally { } finally {
this._onDidChangeDecorations.endDeferredEmit(); this._onDidChangeDecorations.endDeferredEmit();
@@ -1622,6 +1616,7 @@ export class TextModel extends Disposable implements model.ITextModel {
this._decorationsTree.delete(node); this._decorationsTree.delete(node);
node.reset(this.getVersionId(), startOffset, endOffset, range); node.reset(this.getVersionId(), startOffset, endOffset, range);
this._decorationsTree.insert(node); this._decorationsTree.insert(node);
this._onDidChangeDecorations.checkAffectedAndFire(node.options);
} }
private _changeDecorationOptionsImpl(decorationId: string, options: ModelDecorationOptions): void { private _changeDecorationOptionsImpl(decorationId: string, options: ModelDecorationOptions): void {
@@ -1633,6 +1628,9 @@ export class TextModel extends Disposable implements model.ITextModel {
const nodeWasInOverviewRuler = (node.options.overviewRuler && node.options.overviewRuler.color ? true : false); const nodeWasInOverviewRuler = (node.options.overviewRuler && node.options.overviewRuler.color ? true : false);
const nodeIsInOverviewRuler = (options.overviewRuler && options.overviewRuler.color ? true : false); const nodeIsInOverviewRuler = (options.overviewRuler && options.overviewRuler.color ? true : false);
this._onDidChangeDecorations.checkAffectedAndFire(node.options);
this._onDidChangeDecorations.checkAffectedAndFire(options);
if (nodeWasInOverviewRuler !== nodeIsInOverviewRuler) { if (nodeWasInOverviewRuler !== nodeIsInOverviewRuler) {
// Delete + Insert due to an overview ruler status change // Delete + Insert due to an overview ruler status change
this._decorationsTree.delete(node); this._decorationsTree.delete(node);
@@ -1666,6 +1664,7 @@ export class TextModel extends Disposable implements model.ITextModel {
// (2) remove the node from the tree (if it exists) // (2) remove the node from the tree (if it exists)
if (node) { if (node) {
this._decorationsTree.delete(node); this._decorationsTree.delete(node);
this._onDidChangeDecorations.checkAffectedAndFire(node.options);
} }
} }
@@ -1688,6 +1687,7 @@ export class TextModel extends Disposable implements model.ITextModel {
node.ownerId = ownerId; node.ownerId = ownerId;
node.reset(versionId, startOffset, endOffset, range); node.reset(versionId, startOffset, endOffset, range);
node.setOptions(options); node.setOptions(options);
this._onDidChangeDecorations.checkAffectedAndFire(options);
this._decorationsTree.insert(node); this._decorationsTree.insert(node);
@@ -1713,7 +1713,7 @@ export class TextModel extends Disposable implements model.ITextModel {
throw new Error('Illegal value for lineNumber'); throw new Error('Illegal value for lineNumber');
} }
this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), tokens); this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), tokens, false);
} }
public setTokens(tokens: MultilineTokens[]): void { public setTokens(tokens: MultilineTokens[]): void {
@@ -1725,16 +1725,34 @@ export class TextModel extends Disposable implements model.ITextModel {
for (let i = 0, len = tokens.length; i < len; i++) { for (let i = 0, len = tokens.length; i < len; i++) {
const element = tokens[i]; const element = tokens[i];
ranges.push({ fromLineNumber: element.startLineNumber, toLineNumber: element.startLineNumber + element.tokens.length - 1 }); let minChangedLineNumber = 0;
let maxChangedLineNumber = 0;
let hasChange = false;
for (let j = 0, lenJ = element.tokens.length; j < lenJ; j++) { for (let j = 0, lenJ = element.tokens.length; j < lenJ; j++) {
this.setLineTokens(element.startLineNumber + j, element.tokens[j]); const lineNumber = element.startLineNumber + j;
if (hasChange) {
this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), element.tokens[j], false);
maxChangedLineNumber = lineNumber;
} else {
const lineHasChange = this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), element.tokens[j], true);
if (lineHasChange) {
hasChange = true;
minChangedLineNumber = lineNumber;
maxChangedLineNumber = lineNumber;
}
}
}
if (hasChange) {
ranges.push({ fromLineNumber: minChangedLineNumber, toLineNumber: maxChangedLineNumber });
} }
} }
this._emitModelTokensChangedEvent({ if (ranges.length > 0) {
tokenizationSupportChanged: false, this._emitModelTokensChangedEvent({
ranges: ranges tokenizationSupportChanged: false,
}); ranges: ranges
});
}
} }
public setSemanticTokens(tokens: MultilineTokens2[] | null): void { public setSemanticTokens(tokens: MultilineTokens2[] | null): void {
@@ -3083,11 +3101,15 @@ export class DidChangeDecorationsEmitter extends Disposable {
private _deferredCnt: number; private _deferredCnt: number;
private _shouldFire: boolean; private _shouldFire: boolean;
private _affectsMinimap: boolean;
private _affectsOverviewRuler: boolean;
constructor() { constructor() {
super(); super();
this._deferredCnt = 0; this._deferredCnt = 0;
this._shouldFire = false; this._shouldFire = false;
this._affectsMinimap = false;
this._affectsOverviewRuler = false;
} }
public beginDeferredEmit(): void { public beginDeferredEmit(): void {
@@ -3098,13 +3120,31 @@ export class DidChangeDecorationsEmitter extends Disposable {
this._deferredCnt--; this._deferredCnt--;
if (this._deferredCnt === 0) { if (this._deferredCnt === 0) {
if (this._shouldFire) { if (this._shouldFire) {
const event: IModelDecorationsChangedEvent = {
affectsMinimap: this._affectsMinimap,
affectsOverviewRuler: this._affectsOverviewRuler,
};
this._shouldFire = false; this._shouldFire = false;
this._actual.fire({}); this._affectsMinimap = false;
this._affectsOverviewRuler = false;
this._actual.fire(event);
} }
} }
} }
public checkAffectedAndFire(options: ModelDecorationOptions): void {
if (!this._affectsMinimap) {
this._affectsMinimap = options.minimap && options.minimap.position ? true : false;
}
if (!this._affectsOverviewRuler) {
this._affectsOverviewRuler = options.overviewRuler && options.overviewRuler.color ? true : false;
}
this._shouldFire = true;
}
public fire(): void { public fire(): void {
this._affectsMinimap = true;
this._affectsOverviewRuler = true;
this._shouldFire = true; this._shouldFire = true;
} }
} }
@@ -76,6 +76,8 @@ export interface IModelContentChangedEvent {
* An event describing that model decorations have changed. * An event describing that model decorations have changed.
*/ */
export interface IModelDecorationsChangedEvent { export interface IModelDecorationsChangedEvent {
readonly affectsMinimap: boolean;
readonly affectsOverviewRuler: boolean;
} }
/** /**
+26 -1
View File
@@ -964,10 +964,35 @@ export class TokensStore {
this._len += insertCount; this._len += insertCount;
} }
public setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null): void { public setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null, checkEquality: boolean): boolean {
const tokens = TokensStore._massageTokens(topLevelLanguageId, lineTextLength, _tokens); const tokens = TokensStore._massageTokens(topLevelLanguageId, lineTextLength, _tokens);
this._ensureLine(lineIndex); this._ensureLine(lineIndex);
const oldTokens = this._lineTokens[lineIndex];
this._lineTokens[lineIndex] = tokens; this._lineTokens[lineIndex] = tokens;
if (checkEquality) {
return !TokensStore._equals(oldTokens, tokens);
}
return false;
}
private static _equals(_a: Uint32Array | ArrayBuffer | null, _b: Uint32Array | ArrayBuffer | null) {
if (!_a || !_b) {
return !_a && !_b;
}
const a = toUint32Array(_a);
const b = toUint32Array(_b);
if (a.length !== b.length) {
return false;
}
for (let i = 0, len = a.length; i < len; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
} }
//#region Editing //#region Editing
@@ -274,6 +274,16 @@ export class LanguageConfigurationRegistryImpl {
return ensureValidWordDefinition(value.wordDefinition || null); return ensureValidWordDefinition(value.wordDefinition || null);
} }
public getWordDefinitions(): [LanguageId, RegExp][] {
let result: [LanguageId, RegExp][] = [];
this._entries.forEach((value, language) => {
if (value) {
result.push([language, value.wordDefinition]);
}
});
return result;
}
public getFoldingRules(languageId: LanguageId): FoldingRules { public getFoldingRules(languageId: LanguageId): FoldingRules {
let value = this._getRichEditSupport(languageId); let value = this._getRichEditSupport(languageId);
if (!value) { if (!value) {
+12 -2
View File
@@ -10,6 +10,7 @@ import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config
import { Range } from 'vs/editor/common/core/range'; import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection'; import { Selection } from 'vs/editor/common/core/selection';
import { ScrollType, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; import { ScrollType, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon';
import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents';
export const enum ViewEventType { export const enum ViewEventType {
ViewConfigurationChanged = 1, ViewConfigurationChanged = 1,
@@ -82,8 +83,17 @@ export class ViewDecorationsChangedEvent {
public readonly type = ViewEventType.ViewDecorationsChanged; public readonly type = ViewEventType.ViewDecorationsChanged;
constructor() { readonly affectsMinimap: boolean;
// Nothing to do readonly affectsOverviewRuler: boolean;
constructor(source: IModelDecorationsChangedEvent | null) {
if (source) {
this.affectsMinimap = source.affectsMinimap;
this.affectsOverviewRuler = source.affectsOverviewRuler;
} else {
this.affectsMinimap = true;
this.affectsOverviewRuler = true;
}
} }
} }
@@ -172,7 +172,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
if (this.lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) { if (this.lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) {
eventsCollector.emit(new viewEvents.ViewFlushedEvent()); eventsCollector.emit(new viewEvents.ViewFlushedEvent());
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
this.decorations.onLineMappingChanged(); this.decorations.onLineMappingChanged();
this.viewLayout.onFlushed(this.getLineCount()); this.viewLayout.onFlushed(this.getLineCount());
@@ -185,7 +185,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
if (e.hasChanged(EditorOption.readOnly)) { if (e.hasChanged(EditorOption.readOnly)) {
// Must read again all decorations due to readOnly filtering // Must read again all decorations due to readOnly filtering
this.decorations.reset(); this.decorations.reset();
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
} }
eventsCollector.emit(new viewEvents.ViewConfigurationChangedEvent(e)); eventsCollector.emit(new viewEvents.ViewConfigurationChangedEvent(e));
@@ -291,7 +291,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) { if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) {
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
this.decorations.onLineMappingChanged(); this.decorations.onLineMappingChanged();
} }
} finally { } finally {
@@ -354,7 +354,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
const eventsCollector = this._beginEmit(); const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewFlushedEvent()); eventsCollector.emit(new viewEvents.ViewFlushedEvent());
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
} finally { } finally {
this._endEmit(); this._endEmit();
} }
@@ -365,7 +365,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
this.decorations.onModelDecorationsChanged(); this.decorations.onModelDecorationsChanged();
try { try {
const eventsCollector = this._beginEmit(); const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(e));
} finally { } finally {
this._endEmit(); this._endEmit();
} }
@@ -379,7 +379,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
if (lineMappingChanged) { if (lineMappingChanged) {
eventsCollector.emit(new viewEvents.ViewFlushedEvent()); eventsCollector.emit(new viewEvents.ViewFlushedEvent());
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
this.decorations.onLineMappingChanged(); this.decorations.onLineMappingChanged();
this.viewLayout.onFlushed(this.getLineCount()); this.viewLayout.onFlushed(this.getLineCount());
this.viewLayout.onHeightMaybeChanged(); this.viewLayout.onHeightMaybeChanged();
+3 -2
View File
@@ -419,8 +419,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
} }
if (currentMatch) { if (currentMatch) {
const ariaLabel = nls.localize('ariaSearchNoResultWithLineNum', "{0} found for '{1}', at {2}", label, searchString, currentMatch.startLineNumber + ':' + currentMatch.startColumn); const ariaLabel = nls.localize('ariaSearchNoResultWithLineNum', "{0} found for '{1}', at {2}", label, searchString, currentMatch.startLineNumber + ':' + currentMatch.startColumn);
const lineContent = this._codeEditor.getModel()?.getLineContent(currentMatch.startLineNumber); const model = this._codeEditor.getModel();
if (lineContent) { if (model && (currentMatch.startLineNumber <= model.getLineCount()) && (currentMatch.startLineNumber >= 1)) {
const lineContent = model.getLineContent(currentMatch.startLineNumber);
return `${lineContent}, ${ariaLabel}`; return `${lineContent}, ${ariaLabel}`;
} }
@@ -180,9 +180,11 @@ suite('SmartSelect', () => {
// -- bracket selections // -- bracket selections
async function assertRanges(provider: SelectionRangeProvider, value: string, ...expected: IRange[]): Promise<void> { async function assertRanges(provider: SelectionRangeProvider, value: string, ...expected: IRange[]): Promise<void> {
let index = value.indexOf('|');
value = value.replace('|', '');
let model = modelService.createModel(value, new StaticLanguageSelector(mode.getLanguageIdentifier()), URI.parse('fake:lang')); let model = modelService.createModel(value, new StaticLanguageSelector(mode.getLanguageIdentifier()), URI.parse('fake:lang'));
let pos = model.getPositionAt(value.indexOf('|')); let pos = model.getPositionAt(index);
let all = await provider.provideSelectionRanges(model, [pos], CancellationToken.None); let all = await provider.provideSelectionRanges(model, [pos], CancellationToken.None);
let ranges = all![0]; let ranges = all![0];
@@ -197,18 +199,18 @@ suite('SmartSelect', () => {
test('bracket selection', async () => { test('bracket selection', async () => {
await assertRanges(new BracketSelectionRangeProvider(), '(|)', await assertRanges(new BracketSelectionRangeProvider(), '(|)',
new Range(1, 2, 1, 3), new Range(1, 1, 1, 4) new Range(1, 2, 1, 2), new Range(1, 1, 1, 3)
); );
await assertRanges(new BracketSelectionRangeProvider(), '[[[](|)]]', await assertRanges(new BracketSelectionRangeProvider(), '[[[](|)]]',
new Range(1, 6, 1, 7), new Range(1, 5, 1, 8), // () new Range(1, 6, 1, 6), new Range(1, 5, 1, 7), // ()
new Range(1, 3, 1, 8), new Range(1, 2, 1, 9), // [[]()] new Range(1, 3, 1, 7), new Range(1, 2, 1, 8), // [[]()]
new Range(1, 2, 1, 9), new Range(1, 1, 1, 10), // [[[]()]] new Range(1, 2, 1, 8), new Range(1, 1, 1, 9), // [[[]()]]
); );
await assertRanges(new BracketSelectionRangeProvider(), '[a[](|)a]', await assertRanges(new BracketSelectionRangeProvider(), '[a[](|)a]',
new Range(1, 6, 1, 7), new Range(1, 5, 1, 8), new Range(1, 6, 1, 6), new Range(1, 5, 1, 7),
new Range(1, 2, 1, 9), new Range(1, 1, 1, 10), new Range(1, 2, 1, 8), new Range(1, 1, 1, 9),
); );
// no bracket // no bracket
@@ -219,23 +221,23 @@ suite('SmartSelect', () => {
await assertRanges(new BracketSelectionRangeProvider(), '|[[[]()]]'); await assertRanges(new BracketSelectionRangeProvider(), '|[[[]()]]');
// edge // edge
await assertRanges(new BracketSelectionRangeProvider(), '[|[[]()]]', new Range(1, 2, 1, 9), new Range(1, 1, 1, 10)); await assertRanges(new BracketSelectionRangeProvider(), '[|[[]()]]', new Range(1, 2, 1, 8), new Range(1, 1, 1, 9));
await assertRanges(new BracketSelectionRangeProvider(), '[[[]()]|]', new Range(1, 2, 1, 9), new Range(1, 1, 1, 10)); await assertRanges(new BracketSelectionRangeProvider(), '[[[]()]|]', new Range(1, 2, 1, 8), new Range(1, 1, 1, 9));
await assertRanges(new BracketSelectionRangeProvider(), 'aaa(aaa)bbb(b|b)ccc(ccc)', new Range(1, 13, 1, 16), new Range(1, 12, 1, 17)); await assertRanges(new BracketSelectionRangeProvider(), 'aaa(aaa)bbb(b|b)ccc(ccc)', new Range(1, 13, 1, 15), new Range(1, 12, 1, 16));
await assertRanges(new BracketSelectionRangeProvider(), '(aaa(aaa)bbb(b|b)ccc(ccc))', new Range(1, 14, 1, 17), new Range(1, 13, 1, 18), new Range(1, 2, 1, 26), new Range(1, 1, 1, 27)); await assertRanges(new BracketSelectionRangeProvider(), '(aaa(aaa)bbb(b|b)ccc(ccc))', new Range(1, 14, 1, 16), new Range(1, 13, 1, 17), new Range(1, 2, 1, 25), new Range(1, 1, 1, 26));
}); });
test('bracket with leading/trailing', async () => { test('bracket with leading/trailing', async () => {
await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b){\n foo(|);\n}', await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b){\n foo(|);\n}',
new Range(2, 7, 2, 8), new Range(2, 6, 2, 9), new Range(2, 7, 2, 7), new Range(2, 6, 2, 8),
new Range(1, 13, 3, 1), new Range(1, 12, 3, 2), new Range(1, 13, 3, 1), new Range(1, 12, 3, 2),
new Range(1, 1, 3, 2), new Range(1, 1, 3, 2), new Range(1, 1, 3, 2), new Range(1, 1, 3, 2),
); );
await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b)\n{\n foo(|);\n}', await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b)\n{\n foo(|);\n}',
new Range(3, 7, 3, 8), new Range(3, 6, 3, 9), new Range(3, 7, 3, 7), new Range(3, 6, 3, 8),
new Range(2, 2, 4, 1), new Range(2, 1, 4, 2), new Range(2, 2, 4, 1), new Range(2, 1, 4, 2),
new Range(1, 1, 4, 2), new Range(1, 1, 4, 2), new Range(1, 1, 4, 2), new Range(1, 1, 4, 2),
); );
@@ -244,60 +246,60 @@ suite('SmartSelect', () => {
test('in-word ranges', async () => { test('in-word ranges', async () => {
await assertRanges(new WordSelectionRangeProvider(), 'f|ooBar', await assertRanges(new WordSelectionRangeProvider(), 'f|ooBar',
new Range(1, 1, 1, 5), // foo new Range(1, 1, 1, 4), // foo
new Range(1, 1, 1, 8), // fooBar new Range(1, 1, 1, 7), // fooBar
new Range(1, 1, 1, 8), // doc new Range(1, 1, 1, 7), // doc
); );
await assertRanges(new WordSelectionRangeProvider(), 'f|oo_Ba', await assertRanges(new WordSelectionRangeProvider(), 'f|oo_Ba',
new Range(1, 1, 1, 5), new Range(1, 1, 1, 4),
new Range(1, 1, 1, 8), new Range(1, 1, 1, 7),
new Range(1, 1, 1, 8), new Range(1, 1, 1, 7),
); );
await assertRanges(new WordSelectionRangeProvider(), 'f|oo-Ba', await assertRanges(new WordSelectionRangeProvider(), 'f|oo-Ba',
new Range(1, 1, 1, 5), new Range(1, 1, 1, 4),
new Range(1, 1, 1, 8), new Range(1, 1, 1, 7),
new Range(1, 1, 1, 8), new Range(1, 1, 1, 7),
); );
}); });
test('Default selection should select current word/hump first in camelCase #67493', async function () { test('Default selection should select current word/hump first in camelCase #67493', async function () {
await assertRanges(new WordSelectionRangeProvider(), 'Abs|tractSmartSelect', await assertRanges(new WordSelectionRangeProvider(), 'Abs|tractSmartSelect',
new Range(1, 1, 1, 10), new Range(1, 1, 1, 9),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
); );
await assertRanges(new WordSelectionRangeProvider(), 'AbstractSma|rtSelect', await assertRanges(new WordSelectionRangeProvider(), 'AbstractSma|rtSelect',
new Range(1, 9, 1, 15), new Range(1, 9, 1, 14),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
); );
await assertRanges(new WordSelectionRangeProvider(), 'Abstrac-Sma|rt-elect', await assertRanges(new WordSelectionRangeProvider(), 'Abstrac-Sma|rt-elect',
new Range(1, 9, 1, 15), new Range(1, 9, 1, 14),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
); );
await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt_elect', await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt_elect',
new Range(1, 9, 1, 15), new Range(1, 9, 1, 14),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
); );
await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt-elect', await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt-elect',
new Range(1, 9, 1, 15), new Range(1, 9, 1, 14),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
); );
await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rtSelect', await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rtSelect',
new Range(1, 9, 1, 15), new Range(1, 9, 1, 14),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
new Range(1, 1, 1, 21), new Range(1, 1, 1, 20),
); );
}); });
@@ -321,4 +323,49 @@ suite('SmartSelect', () => {
reg.dispose(); reg.dispose();
}); });
test('Expand selection in words with underscores is inconsistent #90589', async function () {
await assertRanges(new WordSelectionRangeProvider(), 'Hel|lo_World',
new Range(1, 1, 1, 6),
new Range(1, 1, 1, 12),
new Range(1, 1, 1, 12),
);
await assertRanges(new WordSelectionRangeProvider(), 'Hello_Wo|rld',
new Range(1, 7, 1, 12),
new Range(1, 1, 1, 12),
new Range(1, 1, 1, 12),
);
await assertRanges(new WordSelectionRangeProvider(), 'Hello|_World',
new Range(1, 1, 1, 6),
new Range(1, 1, 1, 12),
new Range(1, 1, 1, 12),
);
await assertRanges(new WordSelectionRangeProvider(), 'Hello_|World',
new Range(1, 7, 1, 12),
new Range(1, 1, 1, 12),
new Range(1, 1, 1, 12),
);
await assertRanges(new WordSelectionRangeProvider(), 'Hello|-World',
new Range(1, 1, 1, 6),
new Range(1, 1, 1, 12),
new Range(1, 1, 1, 12),
);
await assertRanges(new WordSelectionRangeProvider(), 'Hello-|World',
new Range(1, 7, 1, 12),
new Range(1, 1, 1, 12),
new Range(1, 1, 1, 12),
);
await assertRanges(new WordSelectionRangeProvider(), 'Hello|World',
new Range(1, 6, 1, 11),
new Range(1, 1, 1, 11),
new Range(1, 1, 1, 11),
);
});
}); });
@@ -40,7 +40,7 @@ export class WordSelectionRangeProvider implements SelectionRangeProvider {
// LEFT anchor (start) // LEFT anchor (start)
for (; start >= 0; start--) { for (; start >= 0; start--) {
let ch = word.charCodeAt(start); let ch = word.charCodeAt(start);
if (ch === CharCode.Underline || ch === CharCode.Dash) { if ((start !== offset) && (ch === CharCode.Underline || ch === CharCode.Dash)) {
// foo-bar OR foo_bar // foo-bar OR foo_bar
break; break;
} else if (isLowerAsciiLetter(ch) && isUpperAsciiLetter(lastCh)) { } else if (isLowerAsciiLetter(ch) && isUpperAsciiLetter(lastCh)) {
@@ -11,6 +11,8 @@ import { Selection } from 'vs/editor/common/core/selection';
import { deserializePipePositions, serializePipePositions, testRepeatedActionAndExtractPositions } from 'vs/editor/contrib/wordOperations/test/wordTestUtils'; import { deserializePipePositions, serializePipePositions, testRepeatedActionAndExtractPositions } from 'vs/editor/contrib/wordOperations/test/wordTestUtils';
import { CursorWordEndLeft, CursorWordEndLeftSelect, CursorWordEndRight, CursorWordEndRightSelect, CursorWordLeft, CursorWordLeftSelect, CursorWordRight, CursorWordRightSelect, CursorWordStartLeft, CursorWordStartLeftSelect, CursorWordStartRight, CursorWordStartRightSelect, DeleteWordEndLeft, DeleteWordEndRight, DeleteWordLeft, DeleteWordRight, DeleteWordStartLeft, DeleteWordStartRight, CursorWordAccessibilityLeft, CursorWordAccessibilityLeftSelect, CursorWordAccessibilityRight, CursorWordAccessibilityRightSelect } from 'vs/editor/contrib/wordOperations/wordOperations'; import { CursorWordEndLeft, CursorWordEndLeftSelect, CursorWordEndRight, CursorWordEndRightSelect, CursorWordLeft, CursorWordLeftSelect, CursorWordRight, CursorWordRightSelect, CursorWordStartLeft, CursorWordStartLeftSelect, CursorWordStartRight, CursorWordStartRightSelect, DeleteWordEndLeft, DeleteWordEndRight, DeleteWordLeft, DeleteWordRight, DeleteWordStartLeft, DeleteWordStartRight, CursorWordAccessibilityLeft, CursorWordAccessibilityLeftSelect, CursorWordAccessibilityRight, CursorWordAccessibilityRightSelect } from 'vs/editor/contrib/wordOperations/wordOperations';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { Handler } from 'vs/editor/common/editorCommon';
import { Cursor } from 'vs/editor/common/controller/cursor';
suite('WordOperations', () => { suite('WordOperations', () => {
@@ -193,6 +195,32 @@ suite('WordOperations', () => {
assert.deepEqual(actual, EXPECTED); assert.deepEqual(actual, EXPECTED);
}); });
test('issue #51275 - cursorWordStartLeft does not push undo/redo stack element', () => {
function cursorCommand(cursor: Cursor, command: string, extraData?: any, overwriteSource?: string) {
cursor.trigger(overwriteSource || 'tests', command, extraData);
}
function type(cursor: Cursor, text: string) {
for (let i = 0; i < text.length; i++) {
cursorCommand(cursor, Handler.Type, { text: text.charAt(i) }, 'keyboard');
}
}
withTestCodeEditor('', {}, (editor, cursor) => {
type(cursor, 'foo bar baz');
assert.equal(editor.getValue(), 'foo bar baz');
cursorWordStartLeft(editor);
cursorWordStartLeft(editor);
type(cursor, 'q');
assert.equal(editor.getValue(), 'foo qbar baz');
cursorCommand(cursor, Handler.Undo, {});
assert.equal(editor.getValue(), 'foo bar baz');
});
});
test('cursorWordEndLeft', () => { test('cursorWordEndLeft', () => {
const EXPECTED = ['| /*| Just| some| more| text| a|+=| 3| +|5|-|3| +| 7| */| '].join('\n'); const EXPECTED = ['| /*| Just| some| more| text| a|+=| 3| +|5|-|3| +| 7| */| '].join('\n');
const [text,] = deserializePipePositions(EXPECTED); const [text,] = deserializePipePositions(EXPECTED);
@@ -52,6 +52,7 @@ export abstract class MoveWordCommand extends EditorCommand {
return this._moveTo(sel, outPosition, this._inSelectionMode); return this._moveTo(sel, outPosition, this._inSelectionMode);
}); });
model.pushStackElement();
editor._getCursors().setStates('moveWordCommand', CursorChangeReason.NotSet, result.map(r => CursorState.fromModelSelection(r))); editor._getCursors().setStates('moveWordCommand', CursorChangeReason.NotSet, result.map(r => CursorState.fromModelSelection(r)));
if (result.length === 1) { if (result.length === 1) {
const pos = new Position(result[0].positionLineNumber, result[0].positionColumn); const pos = new Position(result[0].positionLineNumber, result[0].positionColumn);
@@ -6,6 +6,7 @@
import 'vs/css!./inspectTokens'; import 'vs/css!./inspectTokens';
import { CharCode } from 'vs/base/common/charCode'; import { CharCode } from 'vs/base/common/charCode';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { escape } from 'vs/base/common/strings'; import { escape } from 'vs/base/common/strings';
import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
@@ -48,6 +49,7 @@ class InspectTokensController extends Disposable implements IEditorContribution
this._register(this._editor.onDidChangeModel((e) => this.stop())); this._register(this._editor.onDidChangeModel((e) => this.stop()));
this._register(this._editor.onDidChangeModelLanguage((e) => this.stop())); this._register(this._editor.onDidChangeModelLanguage((e) => this.stop()));
this._register(TokenizationRegistry.onDidChange((e) => this.stop())); this._register(TokenizationRegistry.onDidChange((e) => this.stop()));
this._register(this._editor.onKeyUp((e) => e.keyCode === KeyCode.Escape && this.stop()));
} }
public dispose(): void { public dispose(): void {
@@ -222,13 +224,13 @@ class InspectTokensWidget extends Disposable implements IContentWidget {
result += `<hr class="tokens-inspect-separator" style="clear:both"/>`; result += `<hr class="tokens-inspect-separator" style="clear:both"/>`;
let metadata = this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]); let metadata = (token2Index << 1) + 1 < data.tokens2.length ? this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]) : null;
result += `<table class="tm-metadata-table"><tbody>`; result += `<table class="tm-metadata-table"><tbody>`;
result += `<tr><td class="tm-metadata-key">language</td><td class="tm-metadata-value">${escape(metadata.languageIdentifier.language)}</td>`; result += `<tr><td class="tm-metadata-key">language</td><td class="tm-metadata-value">${metadata ? escape(metadata.languageIdentifier.language) : '-?-'}</td>`;
result += `<tr><td class="tm-metadata-key">token type</td><td class="tm-metadata-value">${this._tokenTypeToString(metadata.tokenType)}</td>`; result += `<tr><td class="tm-metadata-key">token type</td><td class="tm-metadata-value">${metadata ? this._tokenTypeToString(metadata.tokenType) : '-?-'}</td>`;
result += `<tr><td class="tm-metadata-key">font style</td><td class="tm-metadata-value">${this._fontStyleToString(metadata.fontStyle)}</td>`; result += `<tr><td class="tm-metadata-key">font style</td><td class="tm-metadata-value">${metadata ? this._fontStyleToString(metadata.fontStyle) : '-?-'}</td>`;
result += `<tr><td class="tm-metadata-key">foreground</td><td class="tm-metadata-value">${Color.Format.CSS.formatHex(metadata.foreground)}</td>`; result += `<tr><td class="tm-metadata-key">foreground</td><td class="tm-metadata-value">${metadata ? Color.Format.CSS.formatHex(metadata.foreground) : '-?-'}</td>`;
result += `<tr><td class="tm-metadata-key">background</td><td class="tm-metadata-value">${Color.Format.CSS.formatHex(metadata.background)}</td>`; result += `<tr><td class="tm-metadata-key">background</td><td class="tm-metadata-value">${metadata ? Color.Format.CSS.formatHex(metadata.background) : '-?-'}</td>`;
result += `</tbody></table>`; result += `</tbody></table>`;
result += `<hr class="tokens-inspect-separator"/>`; result += `<hr class="tokens-inspect-separator"/>`;
@@ -124,6 +124,13 @@ export interface IGlobalEditorOptions {
* Defaults to 20000. * Defaults to 20000.
*/ */
maxTokenizationLineLength?: number; maxTokenizationLineLength?: number;
/**
* Theme to be used for rendering.
* The current out-of-the-box available themes are: 'vs' (default), 'vs-dark', 'hc-black'.
* You can create custom themes via `monaco.editor.defineTheme`.
* To switch a theme, use `monaco.editor.setTheme`
*/
theme?: string;
} }
/** /**
@@ -334,6 +341,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon
private readonly _contextViewService: ContextViewService; private readonly _contextViewService: ContextViewService;
private readonly _configurationService: IConfigurationService; private readonly _configurationService: IConfigurationService;
private readonly _standaloneThemeService: IStandaloneThemeService;
private _ownsModel: boolean; private _ownsModel: boolean;
constructor( constructor(
@@ -363,6 +371,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon
this._contextViewService = <ContextViewService>contextViewService; this._contextViewService = <ContextViewService>contextViewService;
this._configurationService = configurationService; this._configurationService = configurationService;
this._standaloneThemeService = themeService;
this._register(toDispose); this._register(toDispose);
this._register(themeDomRegistration); this._register(themeDomRegistration);
@@ -391,6 +400,9 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon
public updateOptions(newOptions: IEditorOptions & IGlobalEditorOptions): void { public updateOptions(newOptions: IEditorOptions & IGlobalEditorOptions): void {
applyConfigurationValues(this._configurationService, newOptions, false); applyConfigurationValues(this._configurationService, newOptions, false);
if (typeof newOptions.theme === 'string') {
this._standaloneThemeService.setTheme(newOptions.theme);
}
super.updateOptions(newOptions); super.updateOptions(newOptions);
} }
@@ -414,6 +426,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
private readonly _contextViewService: ContextViewService; private readonly _contextViewService: ContextViewService;
private readonly _configurationService: IConfigurationService; private readonly _configurationService: IConfigurationService;
private readonly _standaloneThemeService: IStandaloneThemeService;
constructor( constructor(
domElement: HTMLElement, domElement: HTMLElement,
@@ -443,6 +456,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
this._contextViewService = <ContextViewService>contextViewService; this._contextViewService = <ContextViewService>contextViewService;
this._configurationService = configurationService; this._configurationService = configurationService;
this._standaloneThemeService = themeService;
this._register(toDispose); this._register(toDispose);
this._register(themeDomRegistration); this._register(themeDomRegistration);
@@ -454,8 +468,11 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
super.dispose(); super.dispose();
} }
public updateOptions(newOptions: IDiffEditorOptions): void { public updateOptions(newOptions: IDiffEditorOptions & IGlobalEditorOptions): void {
applyConfigurationValues(this._configurationService, newOptions, true); applyConfigurationValues(this._configurationService, newOptions, true);
if (typeof newOptions.theme === 'string') {
this._standaloneThemeService.setTheme(newOptions.theme);
}
super.updateOptions(newOptions); super.updateOptions(newOptions);
} }
@@ -78,7 +78,7 @@ suite('MinimapCharRenderer', () => {
imageData.data[4 * i + 2] = background.b; imageData.data[4 * i + 2] = background.b;
imageData.data[4 * i + 3] = 255; imageData.data[4 * i + 3] = 255;
} }
renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 2, false); renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 2, false, false);
let actual: number[] = []; let actual: number[] = [];
for (let i = 0; i < imageData.data.length; i++) { for (let i = 0; i < imageData.data.length; i++) {
@@ -108,7 +108,7 @@ suite('MinimapCharRenderer', () => {
imageData.data[4 * i + 3] = 255; imageData.data[4 * i + 3] = 255;
} }
renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 1, false); renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 1, false, false);
let actual: number[] = []; let actual: number[] = [];
for (let i = 0; i < imageData.data.length; i++) { for (let i = 0; i < imageData.data.length; i++) {
@@ -17,6 +17,7 @@ interface IEditorLayoutProviderOpts {
readonly showLineNumbers: boolean; readonly showLineNumbers: boolean;
readonly lineNumbersMinChars: number; readonly lineNumbersMinChars: number;
readonly lineNumbersDigitCount: number; readonly lineNumbersDigitCount: number;
maxLineNumber?: number;
readonly lineDecorationsWidth: number; readonly lineDecorationsWidth: number;
@@ -32,6 +33,7 @@ interface IEditorLayoutProviderOpts {
readonly minimapSide: 'left' | 'right'; readonly minimapSide: 'left' | 'right';
readonly minimapRenderCharacters: boolean; readonly minimapRenderCharacters: boolean;
readonly minimapMaxColumn: number; readonly minimapMaxColumn: number;
minimapMode?: 'actual' | 'cover' | 'contain';
readonly pixelRatio: number; readonly pixelRatio: number;
} }
@@ -45,6 +47,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
options._write(EditorOption.folding, false); options._write(EditorOption.folding, false);
const minimapOptions: EditorMinimapOptions = { const minimapOptions: EditorMinimapOptions = {
enabled: input.minimap, enabled: input.minimap,
mode: input.minimapMode || 'actual',
side: input.minimapSide, side: input.minimapSide,
renderCharacters: input.minimapRenderCharacters, renderCharacters: input.minimapRenderCharacters,
maxColumn: input.minimapMaxColumn, maxColumn: input.minimapMaxColumn,
@@ -77,6 +80,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
outerWidth: input.outerWidth, outerWidth: input.outerWidth,
outerHeight: input.outerHeight, outerHeight: input.outerHeight,
lineHeight: input.lineHeight, lineHeight: input.lineHeight,
maxLineNumber: input.maxLineNumber || Math.pow(10, input.lineNumbersDigitCount) - 1,
lineNumbersDigitCount: input.lineNumbersDigitCount, lineNumbersDigitCount: input.lineNumbersDigitCount,
typicalHalfwidthCharacterWidth: input.typicalHalfwidthCharacterWidth, typicalHalfwidthCharacterWidth: input.typicalHalfwidthCharacterWidth,
maxDigitWidth: input.maxDigitWidth, maxDigitWidth: input.maxDigitWidth,
@@ -125,6 +129,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.None, renderMinimap: RenderMinimap.None,
minimapLeft: 0, minimapLeft: 0,
minimapWidth: 0, minimapWidth: 0,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 0,
minimapCanvasInnerHeight: 800,
minimapCanvasOuterWidth: 0,
minimapCanvasOuterHeight: 800,
viewportColumn: 98, viewportColumn: 98,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -179,6 +191,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.None, renderMinimap: RenderMinimap.None,
minimapLeft: 0, minimapLeft: 0,
minimapWidth: 0, minimapWidth: 0,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 0,
minimapCanvasInnerHeight: 800,
minimapCanvasOuterWidth: 0,
minimapCanvasOuterHeight: 800,
viewportColumn: 97, viewportColumn: 97,
verticalScrollbarWidth: 11, verticalScrollbarWidth: 11,
@@ -233,6 +253,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.None, renderMinimap: RenderMinimap.None,
minimapLeft: 0, minimapLeft: 0,
minimapWidth: 0, minimapWidth: 0,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 0,
minimapCanvasInnerHeight: 800,
minimapCanvasOuterWidth: 0,
minimapCanvasOuterHeight: 800,
viewportColumn: 88, viewportColumn: 88,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -287,6 +315,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.None, renderMinimap: RenderMinimap.None,
minimapLeft: 0, minimapLeft: 0,
minimapWidth: 0, minimapWidth: 0,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 0,
minimapCanvasInnerHeight: 900,
minimapCanvasOuterWidth: 0,
minimapCanvasOuterHeight: 900,
viewportColumn: 88, viewportColumn: 88,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -341,6 +377,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.None, renderMinimap: RenderMinimap.None,
minimapLeft: 0, minimapLeft: 0,
minimapWidth: 0, minimapWidth: 0,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 0,
minimapCanvasInnerHeight: 900,
minimapCanvasOuterWidth: 0,
minimapCanvasOuterHeight: 900,
viewportColumn: 88, viewportColumn: 88,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -395,6 +439,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.None, renderMinimap: RenderMinimap.None,
minimapLeft: 0, minimapLeft: 0,
minimapWidth: 0, minimapWidth: 0,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 0,
minimapCanvasInnerHeight: 900,
minimapCanvasOuterWidth: 0,
minimapCanvasOuterHeight: 900,
viewportColumn: 83, viewportColumn: 83,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -449,6 +501,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.None, renderMinimap: RenderMinimap.None,
minimapLeft: 0, minimapLeft: 0,
minimapWidth: 0, minimapWidth: 0,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 0,
minimapCanvasInnerHeight: 900,
minimapCanvasOuterWidth: 0,
minimapCanvasOuterHeight: 900,
viewportColumn: 83, viewportColumn: 83,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -503,6 +563,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.None, renderMinimap: RenderMinimap.None,
minimapLeft: 0, minimapLeft: 0,
minimapWidth: 0, minimapWidth: 0,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 0,
minimapCanvasInnerHeight: 900,
minimapCanvasOuterWidth: 0,
minimapCanvasOuterHeight: 900,
viewportColumn: 82, viewportColumn: 82,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -557,6 +625,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.None, renderMinimap: RenderMinimap.None,
minimapLeft: 0, minimapLeft: 0,
minimapWidth: 0, minimapWidth: 0,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 0,
minimapCanvasInnerHeight: 900,
minimapCanvasOuterWidth: 0,
minimapCanvasOuterHeight: 900,
viewportColumn: 171, viewportColumn: 171,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -611,6 +687,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.None, renderMinimap: RenderMinimap.None,
minimapLeft: 0, minimapLeft: 0,
minimapWidth: 0, minimapWidth: 0,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 0,
minimapCanvasInnerHeight: 900,
minimapCanvasOuterWidth: 0,
minimapCanvasOuterHeight: 900,
viewportColumn: 169, viewportColumn: 169,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -665,6 +749,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.Text, renderMinimap: RenderMinimap.Text,
minimapLeft: 903, minimapLeft: 903,
minimapWidth: 97, minimapWidth: 97,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 1,
minimapLineHeight: 2,
minimapCanvasInnerWidth: 97,
minimapCanvasInnerHeight: 800,
minimapCanvasOuterWidth: 97,
minimapCanvasOuterHeight: 800,
viewportColumn: 89, viewportColumn: 89,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -719,6 +811,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.Text, renderMinimap: RenderMinimap.Text,
minimapLeft: 903, minimapLeft: 903,
minimapWidth: 97, minimapWidth: 97,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 2,
minimapLineHeight: 4,
minimapCanvasInnerWidth: 194,
minimapCanvasInnerHeight: 1600,
minimapCanvasOuterWidth: 97,
minimapCanvasOuterHeight: 800,
viewportColumn: 89, viewportColumn: 89,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -773,6 +873,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.Text, renderMinimap: RenderMinimap.Text,
minimapLeft: 945, minimapLeft: 945,
minimapWidth: 55, minimapWidth: 55,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 2,
minimapLineHeight: 4,
minimapCanvasInnerWidth: 220,
minimapCanvasInnerHeight: 3200,
minimapCanvasOuterWidth: 55,
minimapCanvasOuterHeight: 800,
viewportColumn: 93, viewportColumn: 93,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -827,6 +935,270 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.Text, renderMinimap: RenderMinimap.Text,
minimapLeft: 0, minimapLeft: 0,
minimapWidth: 55, minimapWidth: 55,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 2,
minimapLineHeight: 4,
minimapCanvasInnerWidth: 220,
minimapCanvasInnerHeight: 3200,
minimapCanvasOuterWidth: 55,
minimapCanvasOuterHeight: 800,
viewportColumn: 93,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 800,
right: 0
}
});
});
test('EditorLayoutProvider 11 - minimap mode cover without sampling', () => {
doTest({
outerWidth: 1000,
outerHeight: 800,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 0,
lineNumbersDigitCount: 3,
maxLineNumber: 120,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: true,
minimapSide: 'right',
minimapRenderCharacters: true,
minimapMaxColumn: 150,
minimapMode: 'cover',
pixelRatio: 2,
}, {
width: 1000,
height: 800,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
decorationsLeft: 0,
decorationsWidth: 10,
contentLeft: 10,
contentWidth: 893,
renderMinimap: RenderMinimap.Text,
minimapLeft: 903,
minimapWidth: 97,
minimapHeightIsEditorHeight: true,
minimapIsSampling: false,
minimapScale: 3,
minimapLineHeight: 13,
minimapCanvasInnerWidth: 291,
minimapCanvasInnerHeight: 1560,
minimapCanvasOuterWidth: 97,
minimapCanvasOuterHeight: 800,
viewportColumn: 89,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 800,
right: 0
}
});
});
test('EditorLayoutProvider 12 - minimap mode cover with sampling', () => {
doTest({
outerWidth: 1000,
outerHeight: 800,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 0,
lineNumbersDigitCount: 4,
maxLineNumber: 2500,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: true,
minimapSide: 'right',
minimapRenderCharacters: true,
minimapMaxColumn: 150,
minimapMode: 'cover',
pixelRatio: 2,
}, {
width: 1000,
height: 800,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
decorationsLeft: 0,
decorationsWidth: 10,
contentLeft: 10,
contentWidth: 935,
renderMinimap: RenderMinimap.Text,
minimapLeft: 945,
minimapWidth: 55,
minimapHeightIsEditorHeight: true,
minimapIsSampling: true,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 110,
minimapCanvasInnerHeight: 1600,
minimapCanvasOuterWidth: 55,
minimapCanvasOuterHeight: 800,
viewportColumn: 93,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 800,
right: 0
}
});
});
test('EditorLayoutProvider 13 - minimap mode contain without sampling', () => {
doTest({
outerWidth: 1000,
outerHeight: 800,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 0,
lineNumbersDigitCount: 3,
maxLineNumber: 120,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: true,
minimapSide: 'right',
minimapRenderCharacters: true,
minimapMaxColumn: 150,
minimapMode: 'contain',
pixelRatio: 2,
}, {
width: 1000,
height: 800,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
decorationsLeft: 0,
decorationsWidth: 10,
contentLeft: 10,
contentWidth: 893,
renderMinimap: RenderMinimap.Text,
minimapLeft: 903,
minimapWidth: 97,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 2,
minimapLineHeight: 4,
minimapCanvasInnerWidth: 194,
minimapCanvasInnerHeight: 1600,
minimapCanvasOuterWidth: 97,
minimapCanvasOuterHeight: 800,
viewportColumn: 89,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
overviewRuler: {
top: 0,
width: 0,
height: 800,
right: 0
}
});
});
test('EditorLayoutProvider 14 - minimap mode contain with sampling', () => {
doTest({
outerWidth: 1000,
outerHeight: 800,
showGlyphMargin: false,
lineHeight: 16,
showLineNumbers: false,
lineNumbersMinChars: 0,
lineNumbersDigitCount: 4,
maxLineNumber: 2500,
lineDecorationsWidth: 10,
typicalHalfwidthCharacterWidth: 10,
maxDigitWidth: 10,
verticalScrollbarWidth: 0,
horizontalScrollbarHeight: 0,
scrollbarArrowSize: 0,
verticalScrollbarHasArrows: false,
minimap: true,
minimapSide: 'right',
minimapRenderCharacters: true,
minimapMaxColumn: 150,
minimapMode: 'contain',
pixelRatio: 2,
}, {
width: 1000,
height: 800,
glyphMarginLeft: 0,
glyphMarginWidth: 0,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
decorationsLeft: 0,
decorationsWidth: 10,
contentLeft: 10,
contentWidth: 935,
renderMinimap: RenderMinimap.Text,
minimapLeft: 945,
minimapWidth: 55,
minimapHeightIsEditorHeight: true,
minimapIsSampling: true,
minimapScale: 1,
minimapLineHeight: 1,
minimapCanvasInnerWidth: 110,
minimapCanvasInnerHeight: 1600,
minimapCanvasOuterWidth: 55,
minimapCanvasOuterHeight: 800,
viewportColumn: 93, viewportColumn: 93,
verticalScrollbarWidth: 0, verticalScrollbarWidth: 0,
@@ -881,6 +1253,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
renderMinimap: RenderMinimap.Text, renderMinimap: RenderMinimap.Text,
minimapLeft: 1096, minimapLeft: 1096,
minimapWidth: 91, minimapWidth: 91,
minimapHeightIsEditorHeight: false,
minimapIsSampling: false,
minimapScale: 2,
minimapLineHeight: 4,
minimapCanvasInnerWidth: 182,
minimapCanvasInnerHeight: 844,
minimapCanvasOuterWidth: 91,
minimapCanvasOuterHeight: 422,
viewportColumn: 83, viewportColumn: 83,
verticalScrollbarWidth: 14, verticalScrollbarWidth: 14,
+22
View File
@@ -1108,6 +1108,13 @@ declare namespace monaco.editor {
* Defaults to 20000. * Defaults to 20000.
*/ */
maxTokenizationLineLength?: number; maxTokenizationLineLength?: number;
/**
* Theme to be used for rendering.
* The current out-of-the-box available themes are: 'vs' (default), 'vs-dark', 'hc-black'.
* You can create custom themes via `monaco.editor.defineTheme`.
* To switch a theme, use `monaco.editor.setTheme`
*/
theme?: string;
} }
/** /**
@@ -2390,6 +2397,8 @@ declare namespace monaco.editor {
* An event describing that model decorations have changed. * An event describing that model decorations have changed.
*/ */
export interface IModelDecorationsChangedEvent { export interface IModelDecorationsChangedEvent {
readonly affectsMinimap: boolean;
readonly affectsOverviewRuler: boolean;
} }
export interface IModelOptionsChangedEvent { export interface IModelOptionsChangedEvent {
@@ -3334,6 +3343,14 @@ declare namespace monaco.editor {
* The width of the minimap * The width of the minimap
*/ */
readonly minimapWidth: number; readonly minimapWidth: number;
readonly minimapHeightIsEditorHeight: boolean;
readonly minimapIsSampling: boolean;
readonly minimapScale: number;
readonly minimapLineHeight: number;
readonly minimapCanvasInnerWidth: number;
readonly minimapCanvasInnerHeight: number;
readonly minimapCanvasOuterWidth: number;
readonly minimapCanvasOuterHeight: number;
/** /**
* Minimap render type * Minimap render type
*/ */
@@ -3383,6 +3400,11 @@ declare namespace monaco.editor {
* Defaults to 'right'. * Defaults to 'right'.
*/ */
side?: 'right' | 'left'; side?: 'right' | 'left';
/**
* Control the minimap rendering mode.
* Defaults to 'actual'.
*/
mode?: 'actual' | 'cover' | 'contain';
/** /**
* Control the rendering of the minimap slider. * Control the rendering of the minimap slider.
* Defaults to 'mouseover'. * Defaults to 'mouseover'.
@@ -353,7 +353,7 @@ export class UserSettings extends Disposable {
super(); super();
this.parser = new ConfigurationModelParser(this.userSettingsResource.toString(), this.scopes); this.parser = new ConfigurationModelParser(this.userSettingsResource.toString(), this.scopes);
this._register(this.fileService.watch(dirname(this.userSettingsResource))); this._register(this.fileService.watch(dirname(this.userSettingsResource)));
this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.userSettingsResource))(() => this._onDidChange.fire())); this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.userSettingsResource))(() => this._onDidChange.fire()));
} }
async loadConfiguration(): Promise<ConfigurationModel> { async loadConfiguration(): Promise<ConfigurationModel> {
@@ -9,7 +9,7 @@ import * as path from 'vs/base/common/path';
import * as fs from 'fs'; import * as fs from 'fs';
import { Registry } from 'vs/platform/registry/common/platform'; import { Registry } from 'vs/platform/registry/common/platform';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
import * as uuid from 'vs/base/common/uuid'; import * as uuid from 'vs/base/common/uuid';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { testFile } from 'vs/base/test/node/utils'; import { testFile } from 'vs/base/test/node/utils';
+12 -12
View File
@@ -55,7 +55,7 @@ export class FileService extends Disposable implements IFileService {
// Forward events from provider // Forward events from provider
const providerDisposables = new DisposableStore(); const providerDisposables = new DisposableStore();
providerDisposables.add(provider.onDidChangeFile(changes => this._onFileChanges.fire(new FileChangesEvent(changes)))); providerDisposables.add(provider.onDidChangeFile(changes => this._onDidFilesChange.fire(new FileChangesEvent(changes))));
providerDisposables.add(provider.onDidChangeCapabilities(() => this._onDidChangeFileSystemProviderCapabilities.fire({ provider, scheme }))); providerDisposables.add(provider.onDidChangeCapabilities(() => this._onDidChangeFileSystemProviderCapabilities.fire({ provider, scheme })));
if (typeof provider.onDidErrorOccur === 'function') { if (typeof provider.onDidErrorOccur === 'function') {
providerDisposables.add(provider.onDidErrorOccur(error => this._onError.fire(new Error(error)))); providerDisposables.add(provider.onDidErrorOccur(error => this._onError.fire(new Error(error))));
@@ -147,11 +147,11 @@ export class FileService extends Disposable implements IFileService {
//#endregion //#endregion
private _onAfterOperation: Emitter<FileOperationEvent> = this._register(new Emitter<FileOperationEvent>()); private _onDidRunOperation = this._register(new Emitter<FileOperationEvent>());
readonly onAfterOperation: Event<FileOperationEvent> = this._onAfterOperation.event; readonly onDidRunOperation = this._onDidRunOperation.event;
private _onError: Emitter<Error> = this._register(new Emitter<Error>()); private _onError = this._register(new Emitter<Error>());
readonly onError: Event<Error> = this._onError.event; readonly onError = this._onError.event;
//#region File Metadata Resolving //#region File Metadata Resolving
@@ -299,7 +299,7 @@ export class FileService extends Disposable implements IFileService {
const fileStat = await this.writeFile(resource, bufferOrReadableOrStream); const fileStat = await this.writeFile(resource, bufferOrReadableOrStream);
// events // events
this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat)); this._onDidRunOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat));
return fileStat; return fileStat;
} }
@@ -549,7 +549,7 @@ export class FileService extends Disposable implements IFileService {
// resolve and send events // resolve and send events
const fileStat = await this.resolve(target, { resolveMetadata: true }); const fileStat = await this.resolve(target, { resolveMetadata: true });
this._onAfterOperation.fire(new FileOperationEvent(source, mode === 'move' ? FileOperation.MOVE : FileOperation.COPY, fileStat)); this._onDidRunOperation.fire(new FileOperationEvent(source, mode === 'move' ? FileOperation.MOVE : FileOperation.COPY, fileStat));
return fileStat; return fileStat;
} }
@@ -563,7 +563,7 @@ export class FileService extends Disposable implements IFileService {
// resolve and send events // resolve and send events
const fileStat = await this.resolve(target, { resolveMetadata: true }); const fileStat = await this.resolve(target, { resolveMetadata: true });
this._onAfterOperation.fire(new FileOperationEvent(source, mode === 'copy' ? FileOperation.COPY : FileOperation.MOVE, fileStat)); this._onDidRunOperation.fire(new FileOperationEvent(source, mode === 'copy' ? FileOperation.COPY : FileOperation.MOVE, fileStat));
return fileStat; return fileStat;
} }
@@ -717,7 +717,7 @@ export class FileService extends Disposable implements IFileService {
// events // events
const fileStat = await this.resolve(resource, { resolveMetadata: true }); const fileStat = await this.resolve(resource, { resolveMetadata: true });
this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat)); this._onDidRunOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat));
return fileStat; return fileStat;
} }
@@ -799,15 +799,15 @@ export class FileService extends Disposable implements IFileService {
await provider.delete(resource, { recursive, useTrash }); await provider.delete(resource, { recursive, useTrash });
// Events // Events
this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.DELETE)); this._onDidRunOperation.fire(new FileOperationEvent(resource, FileOperation.DELETE));
} }
//#endregion //#endregion
//#region File Watching //#region File Watching
private _onFileChanges: Emitter<FileChangesEvent> = this._register(new Emitter<FileChangesEvent>()); private _onDidFilesChange = this._register(new Emitter<FileChangesEvent>());
readonly onFileChanges: Event<FileChangesEvent> = this._onFileChanges.event; readonly onDidFilesChange = this._onDidFilesChange.event;
private activeWatchers = new Map<string, { disposable: IDisposable, count: number }>(); private activeWatchers = new Map<string, { disposable: IDisposable, count: number }>();
+6 -14
View File
@@ -63,12 +63,12 @@ export interface IFileService {
* Allows to listen for file changes. The event will fire for every file within the opened workspace * Allows to listen for file changes. The event will fire for every file within the opened workspace
* (if any) as well as all files that have been watched explicitly using the #watch() API. * (if any) as well as all files that have been watched explicitly using the #watch() API.
*/ */
readonly onFileChanges: Event<FileChangesEvent>; readonly onDidFilesChange: Event<FileChangesEvent>;
/** /**
* An event that is fired upon successful completion of a certain file operation. * An event that is fired upon successful completion of a certain file operation.
*/ */
readonly onAfterOperation: Event<FileOperationEvent>; readonly onDidRunOperation: Event<FileOperationEvent>;
/** /**
* Resolve the properties of a file/folder identified by the resource. * Resolve the properties of a file/folder identified by the resource.
@@ -471,15 +471,7 @@ export interface IFileChange {
export class FileChangesEvent { export class FileChangesEvent {
private readonly _changes: readonly IFileChange[]; constructor(public readonly changes: readonly IFileChange[]) { }
constructor(changes: readonly IFileChange[]) {
this._changes = changes;
}
get changes() {
return this._changes;
}
/** /**
* Returns true if this change event contains the provided file with the given change type (if provided). In case of * Returns true if this change event contains the provided file with the given change type (if provided). In case of
@@ -493,7 +485,7 @@ export class FileChangesEvent {
const checkForChangeType = !isUndefinedOrNull(type); const checkForChangeType = !isUndefinedOrNull(type);
return this._changes.some(change => { return this.changes.some(change => {
if (checkForChangeType && change.type !== type) { if (checkForChangeType && change.type !== type) {
return false; return false;
} }
@@ -550,11 +542,11 @@ export class FileChangesEvent {
} }
private getOfType(type: FileChangeType): IFileChange[] { private getOfType(type: FileChangeType): IFileChange[] {
return this._changes.filter(change => change.type === type); return this.changes.filter(change => change.type === type);
} }
private hasType(type: FileChangeType): boolean { private hasType(type: FileChangeType): boolean {
return this._changes.some(change => { return this.changes.some(change => {
return change.type === type; return change.type === type;
}); });
} }
@@ -66,9 +66,7 @@ export class InMemoryFileSystemProvider extends Disposable implements IFileSyste
async readdir(resource: URI): Promise<[string, FileType][]> { async readdir(resource: URI): Promise<[string, FileType][]> {
const entry = this._lookupAsDirectory(resource, false); const entry = this._lookupAsDirectory(resource, false);
let result: [string, FileType][] = []; let result: [string, FileType][] = [];
for (const [name, child] of entry.entries) { entry.entries.forEach((child, name) => result.push([name, child.type]));
result.push([name, child.type]);
}
return result; return result;
} }
@@ -20,7 +20,7 @@ export class FileWatcher extends Disposable {
constructor( constructor(
private path: string, private path: string,
private onFileChanges: (changes: IDiskFileChange[]) => void, private onDidFilesChange: (changes: IDiskFileChange[]) => void,
private onLogMessage: (msg: ILogMessage) => void, private onLogMessage: (msg: ILogMessage) => void,
private verboseLogging: boolean private verboseLogging: boolean
) { ) {
@@ -101,7 +101,7 @@ export class FileWatcher extends Disposable {
// Fire // Fire
if (normalizedFileChanges.length > 0) { if (normalizedFileChanges.length > 0) {
this.onFileChanges(normalizedFileChanges); this.onDidFilesChange(normalizedFileChanges);
} }
return Promise.resolve(); return Promise.resolve();
@@ -21,7 +21,7 @@ export class FileWatcher extends Disposable {
constructor( constructor(
private folders: IWatcherRequest[], private folders: IWatcherRequest[],
private onFileChanges: (changes: IDiskFileChange[]) => void, private onDidFilesChange: (changes: IDiskFileChange[]) => void,
private onLogMessage: (msg: ILogMessage) => void, private onLogMessage: (msg: ILogMessage) => void,
private verboseLogging: boolean, private verboseLogging: boolean,
) { ) {
@@ -68,7 +68,7 @@ export class FileWatcher extends Disposable {
this.service.setVerboseLogging(this.verboseLogging); this.service.setVerboseLogging(this.verboseLogging);
const options = {}; const options = {};
this._register(this.service.watch(options)(e => !this.isDisposed && this.onFileChanges(e))); this._register(this.service.watch(options)(e => !this.isDisposed && this.onDidFilesChange(e)));
this._register(this.service.onLogMessage(m => this.onLogMessage(m))); this._register(this.service.onLogMessage(m => this.onLogMessage(m)));
@@ -20,7 +20,7 @@ export class FileWatcher extends Disposable {
constructor( constructor(
private folders: IWatcherRequest[], private folders: IWatcherRequest[],
private onFileChanges: (changes: IDiskFileChange[]) => void, private onDidFilesChange: (changes: IDiskFileChange[]) => void,
private onLogMessage: (msg: ILogMessage) => void, private onLogMessage: (msg: ILogMessage) => void,
private verboseLogging: boolean, private verboseLogging: boolean,
private watcherOptions: IWatcherOptions = {} private watcherOptions: IWatcherOptions = {}
@@ -67,7 +67,7 @@ export class FileWatcher extends Disposable {
this.service.setVerboseLogging(this.verboseLogging); this.service.setVerboseLogging(this.verboseLogging);
this._register(this.service.watch(this.watcherOptions)(e => !this.isDisposed && this.onFileChanges(e))); this._register(this.service.watch(this.watcherOptions)(e => !this.isDisposed && this.onDidFilesChange(e)));
this._register(this.service.onLogMessage(m => this.onLogMessage(m))); this._register(this.service.onLogMessage(m => this.onLogMessage(m)));
@@ -16,7 +16,7 @@ export class FileWatcher implements IDisposable {
constructor( constructor(
folders: { path: string, excludes: string[] }[], folders: { path: string, excludes: string[] }[],
private onFileChanges: (changes: IDiskFileChange[]) => void, private onDidFilesChange: (changes: IDiskFileChange[]) => void,
private onLogMessage: (msg: ILogMessage) => void, private onLogMessage: (msg: ILogMessage) => void,
private verboseLogging: boolean private verboseLogging: boolean
) { ) {
@@ -62,7 +62,7 @@ export class FileWatcher implements IDisposable {
// Emit through event emitter // Emit through event emitter
if (events.length > 0) { if (events.length > 0) {
this.onFileChanges(events); this.onDidFilesChange(events);
} }
} }
@@ -165,7 +165,7 @@ suite('Disk File Service', function () {
test('createFolder', async () => { test('createFolder', async () => {
let event: FileOperationEvent | undefined; let event: FileOperationEvent | undefined;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const parent = await service.resolve(URI.file(testDir)); const parent = await service.resolve(URI.file(testDir));
@@ -185,7 +185,7 @@ suite('Disk File Service', function () {
test('createFolder: creating multiple folders at once', async () => { test('createFolder: creating multiple folders at once', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const multiFolderPaths = ['a', 'couple', 'of', 'folders']; const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
const parent = await service.resolve(URI.file(testDir)); const parent = await service.resolve(URI.file(testDir));
@@ -460,7 +460,7 @@ suite('Disk File Service', function () {
test('deleteFile', async () => { test('deleteFile', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const resource = URI.file(join(testDir, 'deep', 'conway.js')); const resource = URI.file(join(testDir, 'deep', 'conway.js'));
const source = await service.resolve(resource); const source = await service.resolve(resource);
@@ -496,7 +496,7 @@ suite('Disk File Service', function () {
const source = await service.resolve(link); const source = await service.resolve(link);
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
await service.del(source.resource); await service.del(source.resource);
@@ -519,7 +519,7 @@ suite('Disk File Service', function () {
await symlink(target.fsPath, link.fsPath); await symlink(target.fsPath, link.fsPath);
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
await service.del(link); await service.del(link);
@@ -532,7 +532,7 @@ suite('Disk File Service', function () {
test('deleteFolder (recursive)', async () => { test('deleteFolder (recursive)', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const resource = URI.file(join(testDir, 'deep')); const resource = URI.file(join(testDir, 'deep'));
const source = await service.resolve(resource); const source = await service.resolve(resource);
@@ -561,7 +561,7 @@ suite('Disk File Service', function () {
test('move', async () => { test('move', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const source = URI.file(join(testDir, 'index.html')); const source = URI.file(join(testDir, 'index.html'));
const sourceContents = readFileSync(source.fsPath); const sourceContents = readFileSync(source.fsPath);
@@ -641,7 +641,7 @@ suite('Disk File Service', function () {
async function testMoveAcrossProviders(sourceFile = 'index.html'): Promise<void> { async function testMoveAcrossProviders(sourceFile = 'index.html'): Promise<void> {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const source = URI.file(join(testDir, sourceFile)); const source = URI.file(join(testDir, sourceFile));
const sourceContents = readFileSync(source.fsPath); const sourceContents = readFileSync(source.fsPath);
@@ -665,7 +665,7 @@ suite('Disk File Service', function () {
test('move - multi folder', async () => { test('move - multi folder', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const multiFolderPaths = ['a', 'couple', 'of', 'folders']; const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
const renameToPath = join(...multiFolderPaths, 'other.html'); const renameToPath = join(...multiFolderPaths, 'other.html');
@@ -684,7 +684,7 @@ suite('Disk File Service', function () {
test('move - directory', async () => { test('move - directory', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const source = URI.file(join(testDir, 'deep')); const source = URI.file(join(testDir, 'deep'));
@@ -728,7 +728,7 @@ suite('Disk File Service', function () {
async function testMoveFolderAcrossProviders(): Promise<void> { async function testMoveFolderAcrossProviders(): Promise<void> {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const source = URI.file(join(testDir, 'deep')); const source = URI.file(join(testDir, 'deep'));
const sourceChildren = readdirSync(source.fsPath); const sourceChildren = readdirSync(source.fsPath);
@@ -753,7 +753,7 @@ suite('Disk File Service', function () {
test('move - MIX CASE', async () => { test('move - MIX CASE', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true }); const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
assert.ok(source.size > 0); assert.ok(source.size > 0);
@@ -774,7 +774,7 @@ suite('Disk File Service', function () {
test('move - same file', async () => { test('move - same file', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true }); const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
assert.ok(source.size > 0); assert.ok(source.size > 0);
@@ -794,7 +794,7 @@ suite('Disk File Service', function () {
test('move - same file #2', async () => { test('move - same file #2', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true }); const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
assert.ok(source.size > 0); assert.ok(source.size > 0);
@@ -817,7 +817,7 @@ suite('Disk File Service', function () {
test('move - source parent of target', async () => { test('move - source parent of target', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true }); let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
const originalSize = source.size; const originalSize = source.size;
@@ -839,7 +839,7 @@ suite('Disk File Service', function () {
test('move - FILE_MOVE_CONFLICT', async () => { test('move - FILE_MOVE_CONFLICT', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true }); let source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
const originalSize = source.size; const originalSize = source.size;
@@ -863,7 +863,7 @@ suite('Disk File Service', function () {
let createEvent: FileOperationEvent; let createEvent: FileOperationEvent;
let moveEvent: FileOperationEvent; let moveEvent: FileOperationEvent;
let deleteEvent: FileOperationEvent; let deleteEvent: FileOperationEvent;
disposables.add(service.onAfterOperation(e => { disposables.add(service.onDidRunOperation(e => {
if (e.operation === FileOperation.CREATE) { if (e.operation === FileOperation.CREATE) {
createEvent = e; createEvent = e;
} else if (e.operation === FileOperation.DELETE) { } else if (e.operation === FileOperation.DELETE) {
@@ -927,7 +927,7 @@ suite('Disk File Service', function () {
async function doTestCopy(sourceName: string = 'index.html') { async function doTestCopy(sourceName: string = 'index.html') {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const source = await service.resolve(URI.file(join(testDir, sourceName))); const source = await service.resolve(URI.file(join(testDir, sourceName)));
const target = URI.file(join(testDir, 'other.html')); const target = URI.file(join(testDir, 'other.html'));
@@ -952,7 +952,7 @@ suite('Disk File Service', function () {
let createEvent: FileOperationEvent; let createEvent: FileOperationEvent;
let copyEvent: FileOperationEvent; let copyEvent: FileOperationEvent;
let deleteEvent: FileOperationEvent; let deleteEvent: FileOperationEvent;
disposables.add(service.onAfterOperation(e => { disposables.add(service.onDidRunOperation(e => {
if (e.operation === FileOperation.CREATE) { if (e.operation === FileOperation.CREATE) {
createEvent = e; createEvent = e;
} else if (e.operation === FileOperation.DELETE) { } else if (e.operation === FileOperation.DELETE) {
@@ -1057,7 +1057,7 @@ suite('Disk File Service', function () {
test('copy - same file', async () => { test('copy - same file', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true }); const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
assert.ok(source.size > 0); assert.ok(source.size > 0);
@@ -1077,7 +1077,7 @@ suite('Disk File Service', function () {
test('copy - same file #2', async () => { test('copy - same file #2', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true }); const source = await service.resolve(URI.file(join(testDir, 'index.html')), { resolveMetadata: true });
assert.ok(source.size > 0); assert.ok(source.size > 0);
@@ -1567,7 +1567,7 @@ suite('Disk File Service', function () {
async function assertCreateFile(converter: (content: string) => VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise<void> { async function assertCreateFile(converter: (content: string) => VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise<void> {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const contents = 'Hello World'; const contents = 'Hello World';
const resource = URI.file(join(testDir, 'test.txt')); const resource = URI.file(join(testDir, 'test.txt'));
@@ -1600,7 +1600,7 @@ suite('Disk File Service', function () {
test('createFile (allows to overwrite existing)', async () => { test('createFile (allows to overwrite existing)', async () => {
let event: FileOperationEvent; let event: FileOperationEvent;
disposables.add(service.onAfterOperation(e => event = e)); disposables.add(service.onDidRunOperation(e => event = e));
const contents = 'Hello World'; const contents = 'Hello World';
const resource = URI.file(join(testDir, 'test.txt')); const resource = URI.file(join(testDir, 'test.txt'));
@@ -2152,7 +2152,7 @@ suite('Disk File Service', function () {
return event.changes.map(change => `Change: type ${toString(change.type)} path ${change.resource.toString()}`).join('\n'); return event.changes.map(change => `Change: type ${toString(change.type)} path ${change.resource.toString()}`).join('\n');
} }
const listenerDisposable = service.onFileChanges(event => { const listenerDisposable = service.onDidFilesChange(event => {
watcherDisposable.dispose(); watcherDisposable.dispose();
listenerDisposable.dispose(); listenerDisposable.dispose();
@@ -15,14 +15,14 @@ function toFileChangesEvent(changes: IDiskFileChange[]): FileChangesEvent {
} }
class TestFileWatcher { class TestFileWatcher {
private readonly _onFileChanges: Emitter<FileChangesEvent>; private readonly _onDidFilesChange: Emitter<FileChangesEvent>;
constructor() { constructor() {
this._onFileChanges = new Emitter<FileChangesEvent>(); this._onDidFilesChange = new Emitter<FileChangesEvent>();
} }
get onFileChanges(): Event<FileChangesEvent> { get onDidFilesChange(): Event<FileChangesEvent> {
return this._onFileChanges.event; return this._onDidFilesChange.event;
} }
report(changes: IDiskFileChange[]): void { report(changes: IDiskFileChange[]): void {
@@ -36,7 +36,7 @@ class TestFileWatcher {
// Emit through event emitter // Emit through event emitter
if (normalizedEvents.length > 0) { if (normalizedEvents.length > 0) {
this._onFileChanges.fire(toFileChangesEvent(normalizedEvents)); this._onDidFilesChange.fire(toFileChangesEvent(normalizedEvents));
} }
} }
} }
@@ -62,7 +62,7 @@ suite('Normalizer', () => {
{ path: deleted.fsPath, type: FileChangeType.DELETED }, { path: deleted.fsPath, type: FileChangeType.DELETED },
]; ];
watch.onFileChanges(e => { watch.onDidFilesChange(e => {
assert.ok(e); assert.ok(e);
assert.equal(e.changes.length, 3); assert.equal(e.changes.length, 3);
assert.ok(e.contains(added, FileChangeType.ADDED)); assert.ok(e.contains(added, FileChangeType.ADDED));
@@ -101,7 +101,7 @@ suite('Normalizer', () => {
{ path: updatedFile.fsPath, type: FileChangeType.UPDATED } { path: updatedFile.fsPath, type: FileChangeType.UPDATED }
]; ];
watch.onFileChanges(e => { watch.onDidFilesChange(e => {
assert.ok(e); assert.ok(e);
assert.equal(e.changes.length, 5); assert.equal(e.changes.length, 5);
@@ -131,7 +131,7 @@ suite('Normalizer', () => {
{ path: unrelated.fsPath, type: FileChangeType.UPDATED }, { path: unrelated.fsPath, type: FileChangeType.UPDATED },
]; ];
watch.onFileChanges(e => { watch.onDidFilesChange(e => {
assert.ok(e); assert.ok(e);
assert.equal(e.changes.length, 1); assert.equal(e.changes.length, 1);
@@ -156,7 +156,7 @@ suite('Normalizer', () => {
{ path: unrelated.fsPath, type: FileChangeType.UPDATED }, { path: unrelated.fsPath, type: FileChangeType.UPDATED },
]; ];
watch.onFileChanges(e => { watch.onDidFilesChange(e => {
assert.ok(e); assert.ok(e);
assert.equal(e.changes.length, 2); assert.equal(e.changes.length, 2);
@@ -182,7 +182,7 @@ suite('Normalizer', () => {
{ path: unrelated.fsPath, type: FileChangeType.UPDATED }, { path: unrelated.fsPath, type: FileChangeType.UPDATED },
]; ];
watch.onFileChanges(e => { watch.onDidFilesChange(e => {
assert.ok(e); assert.ok(e);
assert.equal(e.changes.length, 2); assert.equal(e.changes.length, 2);
@@ -211,7 +211,7 @@ suite('Normalizer', () => {
{ path: updated.fsPath, type: FileChangeType.DELETED } { path: updated.fsPath, type: FileChangeType.DELETED }
]; ];
watch.onFileChanges(e => { watch.onDidFilesChange(e => {
assert.ok(e); assert.ok(e);
assert.equal(e.changes.length, 2); assert.equal(e.changes.length, 2);
@@ -13,6 +13,8 @@ interface IServiceMock<T> {
service: any; service: any;
} }
const isSinonSpyLike = (fn: Function): fn is sinon.SinonSpy => fn && 'callCount' in fn;
export class TestInstantiationService extends InstantiationService { export class TestInstantiationService extends InstantiationService {
private _servciesMap: Map<ServiceIdentifier<any>, any>; private _servciesMap: Map<ServiceIdentifier<any>, any>;
@@ -37,10 +39,10 @@ export class TestInstantiationService extends InstantiationService {
public stub<T>(service: ServiceIdentifier<T>, ctor: Function): T; public stub<T>(service: ServiceIdentifier<T>, ctor: Function): T;
public stub<T>(service: ServiceIdentifier<T>, obj: Partial<T>): T; public stub<T>(service: ServiceIdentifier<T>, obj: Partial<T>): T;
public stub<T>(service: ServiceIdentifier<T>, ctor: Function, property: string, value: any): sinon.SinonStub; public stub<T, V>(service: ServiceIdentifier<T>, ctor: Function, property: string, value: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
public stub<T>(service: ServiceIdentifier<T>, obj: Partial<T>, property: string, value: any): sinon.SinonStub; public stub<T, V>(service: ServiceIdentifier<T>, obj: Partial<T>, property: string, value: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
public stub<T>(service: ServiceIdentifier<T>, property: string, value: any): sinon.SinonStub; public stub<T, V>(service: ServiceIdentifier<T>, property: string, value: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
public stub<T>(serviceIdentifier: ServiceIdentifier<T>, arg2: any, arg3?: string, arg4?: any): sinon.SinonStub { public stub<T>(serviceIdentifier: ServiceIdentifier<T>, arg2: any, arg3?: string, arg4?: any): sinon.SinonStub | sinon.SinonSpy {
let service = typeof arg2 !== 'string' ? arg2 : undefined; let service = typeof arg2 !== 'string' ? arg2 : undefined;
let serviceMock: IServiceMock<any> = { id: serviceIdentifier, service: service }; let serviceMock: IServiceMock<any> = { id: serviceIdentifier, service: service };
let property = typeof arg2 === 'string' ? arg2 : arg3; let property = typeof arg2 === 'string' ? arg2 : arg3;
@@ -53,9 +55,11 @@ export class TestInstantiationService extends InstantiationService {
stubObject[property].restore(); stubObject[property].restore();
} }
if (typeof value === 'function') { if (typeof value === 'function') {
stubObject[property] = value; const spy = isSinonSpyLike(value) ? value : sinon.spy(value);
stubObject[property] = spy;
return spy;
} else { } else {
let stub = value ? sinon.stub().returns(value) : sinon.stub(); const stub = value ? sinon.stub().returns(value) : sinon.stub();
stubObject[property] = stub; stubObject[property] = stub;
return stub; return stub;
} }
@@ -67,9 +71,9 @@ export class TestInstantiationService extends InstantiationService {
} }
public stubPromise<T>(service?: ServiceIdentifier<T>, fnProperty?: string, value?: any): T | sinon.SinonStub; public stubPromise<T>(service?: ServiceIdentifier<T>, fnProperty?: string, value?: any): T | sinon.SinonStub;
public stubPromise<T>(service?: ServiceIdentifier<T>, ctor?: any, fnProperty?: string, value?: any): sinon.SinonStub; public stubPromise<T, V>(service?: ServiceIdentifier<T>, ctor?: any, fnProperty?: string, value?: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
public stubPromise<T>(service?: ServiceIdentifier<T>, obj?: any, fnProperty?: string, value?: any): sinon.SinonStub; public stubPromise<T, V>(service?: ServiceIdentifier<T>, obj?: any, fnProperty?: string, value?: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
public stubPromise(arg1?: any, arg2?: any, arg3?: any, arg4?: any): sinon.SinonStub { public stubPromise(arg1?: any, arg2?: any, arg3?: any, arg4?: any): sinon.SinonStub | sinon.SinonSpy {
arg3 = typeof arg2 === 'string' ? Promise.resolve(arg3) : arg3; arg3 = typeof arg2 === 'string' ? Promise.resolve(arg3) : arg3;
arg4 = typeof arg2 !== 'string' && typeof arg3 === 'string' ? Promise.resolve(arg4) : arg4; arg4 = typeof arg2 !== 'string' && typeof arg3 === 'string' ? Promise.resolve(arg4) : arg4;
return this.stub(arg1, arg2, arg3, arg4); return this.stub(arg1, arg2, arg3, arg4);
+4 -4
View File
@@ -135,15 +135,15 @@ export namespace IMarkerData {
export function makeKeyOptionalMessage(markerData: IMarkerData, useMessage: boolean): string { export function makeKeyOptionalMessage(markerData: IMarkerData, useMessage: boolean): string {
let result: string[] = [emptyString]; let result: string[] = [emptyString];
if (markerData.source) { if (markerData.source) {
result.push(markerData.source.replace('¦', '\¦')); result.push(markerData.source.replace('¦', '\\¦'));
} else { } else {
result.push(emptyString); result.push(emptyString);
} }
if (markerData.code) { if (markerData.code) {
if (typeof markerData.code === 'string') { if (typeof markerData.code === 'string') {
result.push(markerData.code.replace('¦', '\¦')); result.push(markerData.code.replace('¦', '\\¦'));
} else { } else {
result.push(markerData.code.value.replace('¦', '\¦')); result.push(markerData.code.value.replace('¦', '\\¦'));
} }
} else { } else {
result.push(emptyString); result.push(emptyString);
@@ -157,7 +157,7 @@ export namespace IMarkerData {
// Modifed to not include the message as part of the marker key to work around // Modifed to not include the message as part of the marker key to work around
// https://github.com/microsoft/vscode/issues/77475 // https://github.com/microsoft/vscode/issues/77475
if (markerData.message && useMessage) { if (markerData.message && useMessage) {
result.push(markerData.message.replace('¦', '\¦')); result.push(markerData.message.replace('¦', '\\¦'));
} else { } else {
result.push(emptyString); result.push(emptyString);
} }
@@ -6,7 +6,7 @@
import BaseSeverity from 'vs/base/common/severity'; import BaseSeverity from 'vs/base/common/severity';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IAction } from 'vs/base/common/actions'; import { IAction } from 'vs/base/common/actions';
import { Event, Emitter } from 'vs/base/common/event'; import { Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
export import Severity = BaseSeverity; export import Severity = BaseSeverity;
@@ -318,16 +318,14 @@ export class NoOpNotification implements INotificationHandle {
readonly progress = new NoOpProgress(); readonly progress = new NoOpProgress();
private readonly _onDidClose: Emitter<void> = new Emitter(); readonly onDidClose = Event.None;
readonly onDidClose: Event<void> = this._onDidClose.event; readonly onDidChangeVisibility = Event.None;
updateSeverity(severity: Severity): void { } updateSeverity(severity: Severity): void { }
updateMessage(message: NotificationMessage): void { } updateMessage(message: NotificationMessage): void { }
updateActions(actions?: INotificationActions): void { } updateActions(actions?: INotificationActions): void { }
close(): void { close(): void { }
this._onDidClose.dispose();
}
} }
export class NoOpProgress implements INotificationProgress { export class NoOpProgress implements INotificationProgress {
+17 -19
View File
@@ -17,7 +17,11 @@ export interface IProgressService {
_serviceBrand: undefined; _serviceBrand: undefined;
withProgress<R = any>(options: IProgressOptions | IProgressNotificationOptions | IProgressWindowOptions | IProgressCompositeOptions, task: (progress: IProgress<IProgressStep>) => Promise<R>, onDidCancel?: (choice?: number) => void): Promise<R>; withProgress<R = any>(
options: IProgressOptions | IProgressNotificationOptions | IProgressWindowOptions | IProgressCompositeOptions,
task: (progress: IProgress<IProgressStep>) => Promise<R>,
onDidCancel?: (choice?: number) => void
): Promise<R>;
} }
export interface IProgressIndicator { export interface IProgressIndicator {
@@ -45,19 +49,19 @@ export const enum ProgressLocation {
} }
export interface IProgressOptions { export interface IProgressOptions {
location: ProgressLocation | string; readonly location: ProgressLocation | string;
title?: string; readonly title?: string;
source?: string; readonly source?: string;
total?: number; readonly total?: number;
cancellable?: boolean; readonly cancellable?: boolean;
buttons?: string[]; readonly buttons?: string[];
} }
export interface IProgressNotificationOptions extends IProgressOptions { export interface IProgressNotificationOptions extends IProgressOptions {
readonly location: ProgressLocation.Notification; readonly location: ProgressLocation.Notification;
readonly primaryActions?: ReadonlyArray<IAction>; readonly primaryActions?: ReadonlyArray<IAction>;
readonly secondaryActions?: ReadonlyArray<IAction>; readonly secondaryActions?: ReadonlyArray<IAction>;
delay?: number; readonly delay?: number;
} }
export interface IProgressWindowOptions extends IProgressOptions { export interface IProgressWindowOptions extends IProgressOptions {
@@ -66,8 +70,8 @@ export interface IProgressWindowOptions extends IProgressOptions {
} }
export interface IProgressCompositeOptions extends IProgressOptions { export interface IProgressCompositeOptions extends IProgressOptions {
location: ProgressLocation.Explorer | ProgressLocation.Extensions | ProgressLocation.Scm | string; readonly location: ProgressLocation.Explorer | ProgressLocation.Extensions | ProgressLocation.Scm | string;
delay?: number; readonly delay?: number;
} }
export interface IProgressStep { export interface IProgressStep {
@@ -96,20 +100,14 @@ export interface IProgress<T> {
export class Progress<T> implements IProgress<T> { export class Progress<T> implements IProgress<T> {
private _callback: (data: T) => void;
private _value?: T; private _value?: T;
get value(): T | undefined { return this._value; }
constructor(callback: (data: T) => void) { constructor(private callback: (data: T) => void) { }
this._callback = callback;
}
get value(): T | undefined {
return this._value;
}
report(item: T) { report(item: T) {
this._value = item; this._value = item;
this._callback(this._value); this.callback(this._value);
} }
} }
@@ -224,7 +224,7 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase
this.isWatching = true; this.isWatching = true;
this._register(this.fileService.watch(this.file)); this._register(this.fileService.watch(this.file));
this._register(this.fileService.onFileChanges(e => { this._register(this.fileService.onDidFilesChange(e => {
if (document.hasFocus()) { if (document.hasFocus()) {
return; // optimization: ignore changes from ourselves by checking for focus return; // optimization: ignore changes from ourselves by checking for focus
} }
@@ -428,6 +428,10 @@ export const minimapError = registerColor('minimap.errorHighlight', { dark: new
export const minimapWarning = registerColor('minimap.warningHighlight', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningBorder }, nls.localize('overviewRuleWarning', 'Minimap marker color for warnings.')); export const minimapWarning = registerColor('minimap.warningHighlight', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningBorder }, nls.localize('overviewRuleWarning', 'Minimap marker color for warnings.'));
export const minimapBackground = registerColor('minimap.background', { dark: null, light: null, hc: null }, nls.localize('minimapBackground', "Minimap background color.")); export const minimapBackground = registerColor('minimap.background', { dark: null, light: null, hc: null }, nls.localize('minimapBackground', "Minimap background color."));
export const minimapSliderBackground = registerColor('minimapSlider.background', { light: transparent(scrollbarSliderBackground, 0.5), dark: transparent(scrollbarSliderBackground, 0.5), hc: transparent(scrollbarSliderBackground, 0.5) }, nls.localize('minimapSliderBackground', "Minimap slider background color."));
export const minimapSliderHoverBackground = registerColor('minimapSlider.hoverBackground', { light: transparent(scrollbarSliderHoverBackground, 0.5), dark: transparent(scrollbarSliderHoverBackground, 0.5), hc: transparent(scrollbarSliderHoverBackground, 0.5) }, nls.localize('minimapSliderHoverBackground', "Minimap slider background color when hovering."));
export const minimapSliderActiveBackground = registerColor('minimapSlider.activeBackground', { light: transparent(scrollbarSliderActiveBackground, 0.5), dark: transparent(scrollbarSliderActiveBackground, 0.5), hc: transparent(scrollbarSliderActiveBackground, 0.5) }, nls.localize('minimapSliderActiveBackground', "Minimap slider background color when clicked on."));
export const problemsErrorIconForeground = registerColor('problemsErrorIcon.foreground', { dark: editorErrorForeground, light: editorErrorForeground, hc: editorErrorForeground }, nls.localize('problemsErrorIconForeground', "The color used for the problems error icon.")); export const problemsErrorIconForeground = registerColor('problemsErrorIcon.foreground', { dark: editorErrorForeground, light: editorErrorForeground, hc: editorErrorForeground }, nls.localize('problemsErrorIconForeground', "The color used for the problems error icon."));
export const problemsWarningIconForeground = registerColor('problemsWarningIcon.foreground', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningForeground }, nls.localize('problemsWarningIconForeground', "The color used for the problems warning icon.")); export const problemsWarningIconForeground = registerColor('problemsWarningIcon.foreground', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningForeground }, nls.localize('problemsWarningIconForeground', "The color used for the problems warning icon."));
export const problemsInfoIconForeground = registerColor('problemsInfoIcon.foreground', { dark: editorInfoForeground, light: editorInfoForeground, hc: editorInfoForeground }, nls.localize('problemsInfoIconForeground', "The color used for the problems info icon.")); export const problemsInfoIconForeground = registerColor('problemsInfoIcon.foreground', { dark: editorInfoForeground, light: editorInfoForeground, hc: editorInfoForeground }, nls.localize('problemsInfoIconForeground', "The color used for the problems info icon."));
@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
export const IUndoRedoService = createDecorator<IUndoRedoService>('undoRedoService');
export interface IUndoRedoContext {
replaceCurrentElement(others: IUndoRedoElement[]): void;
}
export interface IUndoRedoElement {
/**
* None, one or multiple resources that this undo/redo element impacts.
*/
readonly resources: URI[];
/**
* The label of the undo/redo element.
*/
readonly label: string;
/**
* Undo.
* Will always be called before `redo`.
* Can be called multiple times.
* e.g. `undo` -> `redo` -> `undo` -> `redo`
*/
undo(ctx: IUndoRedoContext): void;
/**
* Redo.
* Will always be called after `undo`.
* Can be called multiple times.
* e.g. `undo` -> `redo` -> `undo` -> `redo`
*/
redo(ctx: IUndoRedoContext): void;
/**
* Invalidate the edits concerning `resource`.
* i.e. the undo/redo stack for that particular resource has been destroyed.
*/
invalidate(resource: URI): boolean;
}
export interface IUndoRedoService {
_serviceBrand: undefined;
/**
* Add a new element to the `undo` stack.
* This will destroy the `redo` stack.
*/
pushElement(element: IUndoRedoElement): void;
/**
* Get the last pushed element. If the last pushed element has been undone, returns null.
*/
getLastElement(resource: URI): IUndoRedoElement | null;
/**
* Remove elements that target `resource`.
*/
removeElements(resource: URI): void;
canUndo(resource: URI): boolean;
undo(resource: URI): void;
redo(resource: URI): void;
canRedo(resource: URI): boolean;
}
@@ -0,0 +1,241 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IUndoRedoService, IUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo';
import { URI } from 'vs/base/common/uri';
import { getComparisonKey as uriGetComparisonKey } from 'vs/base/common/resources';
import { onUnexpectedError } from 'vs/base/common/errors';
class StackElement {
public readonly actual: IUndoRedoElement;
public readonly label: string;
public readonly resources: URI[];
public readonly strResources: string[];
constructor(actual: IUndoRedoElement) {
this.actual = actual;
this.label = actual.label;
this.resources = actual.resources;
this.strResources = this.resources.map(resource => uriGetComparisonKey(resource));
}
public invalidate(resource: URI): void {
if (this.resources.length > 1) {
this.actual.invalidate(resource);
}
}
}
class ResourceEditStack {
public resource: URI;
public past: StackElement[];
public future: StackElement[];
constructor(resource: URI) {
this.resource = resource;
this.past = [];
this.future = [];
}
}
export class UndoRedoService implements IUndoRedoService {
_serviceBrand: undefined;
private readonly _editStacks: Map<string, ResourceEditStack>;
constructor() {
this._editStacks = new Map<string, ResourceEditStack>();
}
public pushElement(_element: IUndoRedoElement): void {
const element = new StackElement(_element);
for (let i = 0, len = element.resources.length; i < len; i++) {
const resource = element.resources[i];
const strResource = element.strResources[i];
let editStack: ResourceEditStack;
if (this._editStacks.has(strResource)) {
editStack = this._editStacks.get(strResource)!;
} else {
editStack = new ResourceEditStack(resource);
this._editStacks.set(strResource, editStack);
}
// remove the future
for (const futureElement of editStack.future) {
futureElement.invalidate(resource);
}
editStack.future = [];
editStack.past.push(element);
}
}
public getLastElement(resource: URI): IUndoRedoElement | null {
const strResource = uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
if (editStack.future.length > 0) {
return null;
}
if (editStack.past.length === 0) {
return null;
}
return editStack.past[editStack.past.length - 1].actual;
}
return null;
}
public removeElements(resource: URI): void {
const strResource = uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
for (const pastElement of editStack.past) {
pastElement.invalidate(resource);
}
for (const futureElement of editStack.future) {
futureElement.invalidate(resource);
}
this._editStacks.delete(strResource);
}
}
public canUndo(resource: URI): boolean {
const strResource = uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
return (editStack.past.length > 0);
}
return false;
}
public undo(resource: URI): void {
const strResource = uriGetComparisonKey(resource);
if (!this._editStacks.has(strResource)) {
return;
}
const editStack = this._editStacks.get(strResource)!;
if (editStack.past.length === 0) {
return;
}
const element = editStack.past[editStack.past.length - 1];
let replaceCurrentElement: IUndoRedoElement[] | null = null as IUndoRedoElement[] | null;
try {
element.actual.undo({
replaceCurrentElement: (others: IUndoRedoElement[]): void => {
replaceCurrentElement = others;
}
});
} catch (e) {
onUnexpectedError(e);
editStack.past.pop();
editStack.future.push(element);
return;
}
if (replaceCurrentElement === null) {
// regular case
editStack.past.pop();
editStack.future.push(element);
return;
}
const replaceCurrentElementMap = new Map<string, StackElement>();
for (const _replace of replaceCurrentElement) {
const replace = new StackElement(_replace);
for (const strResource of replace.strResources) {
replaceCurrentElementMap.set(strResource, replace);
}
}
for (let i = 0, len = element.strResources.length; i < len; i++) {
const strResource = element.strResources[i];
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
for (let j = editStack.past.length - 1; j >= 0; j--) {
if (editStack.past[j] === element) {
if (replaceCurrentElementMap.has(strResource)) {
editStack.past[j] = replaceCurrentElementMap.get(strResource)!;
} else {
editStack.past.splice(j, 1);
}
break;
}
}
}
}
}
public canRedo(resource: URI): boolean {
const strResource = uriGetComparisonKey(resource);
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
return (editStack.future.length > 0);
}
return false;
}
redo(resource: URI): void {
const strResource = uriGetComparisonKey(resource);
if (!this._editStacks.has(strResource)) {
return;
}
const editStack = this._editStacks.get(strResource)!;
if (editStack.future.length === 0) {
return;
}
const element = editStack.future[editStack.future.length - 1];
let replaceCurrentElement: IUndoRedoElement[] | null = null as IUndoRedoElement[] | null;
try {
element.actual.redo({
replaceCurrentElement: (others: IUndoRedoElement[]): void => {
replaceCurrentElement = others;
}
});
} catch (e) {
onUnexpectedError(e);
editStack.future.pop();
editStack.past.push(element);
return;
}
if (replaceCurrentElement === null) {
// regular case
editStack.future.pop();
editStack.past.push(element);
return;
}
const replaceCurrentElementMap = new Map<string, StackElement>();
for (const _replace of replaceCurrentElement) {
const replace = new StackElement(_replace);
for (const strResource of replace.strResources) {
replaceCurrentElementMap.set(strResource, replace);
}
}
for (let i = 0, len = element.strResources.length; i < len; i++) {
const strResource = element.strResources[i];
if (this._editStacks.has(strResource)) {
const editStack = this._editStacks.get(strResource)!;
for (let j = editStack.future.length - 1; j >= 0; j--) {
if (editStack.future[j] === element) {
if (replaceCurrentElementMap.has(strResource)) {
editStack.future[j] = replaceCurrentElementMap.get(strResource)!;
} else {
editStack.future.splice(j, 1);
}
break;
}
}
}
}
}
}
@@ -174,7 +174,7 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser {
) { ) {
super(source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService); super(source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
this._register(this.fileService.watch(dirname(file))); this._register(this.fileService.watch(dirname(file)));
this._register(this.fileService.onFileChanges(e => this.onFileChanges(e))); this._register(this.fileService.onDidFilesChange(e => this.onFileChanges(e)));
} }
async stop(): Promise<void> { async stop(): Promise<void> {
@@ -39,7 +39,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
) { ) {
super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService); super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
this._register(this.fileService.watch(dirname(this.environmentService.argvResource))); this._register(this.fileService.watch(dirname(this.environmentService.argvResource)));
this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire())); this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire()));
} }
async pull(): Promise<void> { async pull(): Promise<void> {
@@ -14,6 +14,9 @@ export class UserDataAuthTokenService extends Disposable implements IUserDataAut
private _onDidChangeToken: Emitter<string | undefined> = this._register(new Emitter<string | undefined>()); private _onDidChangeToken: Emitter<string | undefined> = this._register(new Emitter<string | undefined>());
readonly onDidChangeToken: Event<string | undefined> = this._onDidChangeToken.event; readonly onDidChangeToken: Event<string | undefined> = this._onDidChangeToken.event;
private _onTokenFailed: Emitter<void> = this._register(new Emitter<void>());
readonly onTokenFailed: Event<void> = this._onTokenFailed.event;
private _token: string | undefined; private _token: string | undefined;
constructor() { constructor() {
@@ -30,4 +33,8 @@ export class UserDataAuthTokenService extends Disposable implements IUserDataAut
this._onDidChangeToken.fire(token); this._onDidChangeToken.fire(token);
} }
} }
sendTokenFailed(): void {
this._onTokenFailed.fire();
}
} }
@@ -72,6 +72,13 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
return this.sync(loop, auto); return this.sync(loop, auto);
} }
} }
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.SessionExpired) {
this.logService.info('Auto Sync: Cloud has new session');
this.logService.info('Auto Sync: Resetting the local sync state.');
await this.userDataSyncService.resetLocal();
this.logService.info('Auto Sync: Completed resetting the local sync state.');
return this.sync(loop, auto);
}
this.logService.error(e); this.logService.error(e);
this.successiveFailures++; this.successiveFailures++;
this._onError.fire(e instanceof UserDataSyncError ? { code: e.code, source: e.source } : { code: UserDataSyncErrorCode.Unknown }); this._onError.fire(e instanceof UserDataSyncError ? { code: e.code, source: e.source } : { code: UserDataSyncErrorCode.Unknown });
@@ -18,7 +18,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IStringDictionary } from 'vs/base/common/collections'; import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { isEqual } from 'vs/base/common/resources'; import { isEqual, joinPath } from 'vs/base/common/resources';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
export const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store'; export const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store';
@@ -120,23 +120,27 @@ export interface IUserData {
} }
export interface IUserDataSyncStore { export interface IUserDataSyncStore {
url: string; url: URI;
authenticationProviderId: string; authenticationProviderId: string;
} }
export function getUserDataSyncStore(configurationService: IConfigurationService): IUserDataSyncStore | undefined { export function getUserDataSyncStore(configurationService: IConfigurationService): IUserDataSyncStore | undefined {
const value = configurationService.getValue<IUserDataSyncStore>(CONFIGURATION_SYNC_STORE_KEY); const value = configurationService.getValue<{ url: string, authenticationProviderId: string }>(CONFIGURATION_SYNC_STORE_KEY);
return value && value.url && value.authenticationProviderId ? value : undefined; if (value && value.url && value.authenticationProviderId) {
return {
url: joinPath(URI.parse(value.url), 'v1'),
authenticationProviderId: value.authenticationProviderId
};
}
return undefined;
} }
export const ALL_RESOURCE_KEYS: ResourceKey[] = ['settings', 'keybindings', 'extensions', 'globalState']; export const ALL_RESOURCE_KEYS: ResourceKey[] = ['settings', 'keybindings', 'extensions', 'globalState'];
export type ResourceKey = 'settings' | 'keybindings' | 'extensions' | 'globalState'; export type ResourceKey = 'settings' | 'keybindings' | 'extensions' | 'globalState';
export interface IUserDataManifest { export interface IUserDataManifest {
settings: string; latest?: Record<ResourceKey, string>
keybindings: string; session: string;
extensions: string;
globalState: string;
} }
export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService'); export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService');
@@ -162,6 +166,7 @@ export enum UserDataSyncErrorCode {
TooLarge = 'TooLarge', TooLarge = 'TooLarge',
NoRef = 'NoRef', NoRef = 'NoRef',
TurnedOff = 'TurnedOff', TurnedOff = 'TurnedOff',
SessionExpired = 'SessionExpired',
// Local Errors // Local Errors
LocalPreconditionFailed = 'LocalPreconditionFailed', LocalPreconditionFailed = 'LocalPreconditionFailed',
@@ -303,9 +308,11 @@ export interface IUserDataAuthTokenService {
_serviceBrand: undefined; _serviceBrand: undefined;
readonly onDidChangeToken: Event<string | undefined>; readonly onDidChangeToken: Event<string | undefined>;
readonly onTokenFailed: Event<void>;
getToken(): Promise<string | undefined>; getToken(): Promise<string | undefined>;
setToken(accessToken: string | undefined): Promise<void>; setToken(accessToken: string | undefined): Promise<void>;
sendTokenFailed(): void;
} }
export const IUserDataSyncLogService = createDecorator<IUserDataSyncLogService>('IUserDataSyncLogService'); export const IUserDataSyncLogService = createDecorator<IUserDataSyncLogService>('IUserDataSyncLogService');
@@ -96,6 +96,7 @@ export class UserDataAuthTokenServiceChannel implements IServerChannel {
listen(_: unknown, event: string): Event<any> { listen(_: unknown, event: string): Event<any> {
switch (event) { switch (event) {
case 'onDidChangeToken': return this.service.onDidChangeToken; case 'onDidChangeToken': return this.service.onDidChangeToken;
case 'onTokenFailed': return this.service.onTokenFailed;
} }
throw new Error(`Event not found: ${event}`); throw new Error(`Event not found: ${event}`);
} }
@@ -14,11 +14,14 @@ import { toErrorMessage } from 'vs/base/common/errorMessage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { equals } from 'vs/base/common/arrays'; import { equals } from 'vs/base/common/arrays';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
type SyncErrorClassification = { type SyncErrorClassification = {
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
}; };
const SESSION_ID_KEY = 'sync.sessionId';
export class UserDataSyncService extends Disposable implements IUserDataSyncService { export class UserDataSyncService extends Disposable implements IUserDataSyncService {
_serviceBrand: any; _serviceBrand: any;
@@ -48,6 +51,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IUserDataAuthTokenService private readonly userDataAuthTokenService: IUserDataAuthTokenService, @IUserDataAuthTokenService private readonly userDataAuthTokenService: IUserDataAuthTokenService,
@ITelemetryService private readonly telemetryService: ITelemetryService, @ITelemetryService private readonly telemetryService: ITelemetryService,
@IStorageService private readonly storageService: IStorageService,
) { ) {
super(); super();
this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser)); this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser));
@@ -96,7 +100,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.setStatus(SyncStatus.Syncing); this.setStatus(SyncStatus.Syncing);
} }
const manifest = await this.userDataSyncStoreService.manifest(); let manifest = await this.userDataSyncStoreService.manifest();
// Server has no data but this machine was synced before // Server has no data but this machine was synced before
if (manifest === null && await this.hasPreviouslySynced()) { if (manifest === null && await this.hasPreviouslySynced()) {
@@ -104,14 +108,30 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
throw new UserDataSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff); throw new UserDataSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff);
} }
const sessionId = this.storageService.get(SESSION_ID_KEY, StorageScope.GLOBAL);
// Server session is different from client session
if (sessionId && manifest && sessionId !== manifest.session) {
throw new UserDataSyncError(localize('session expired', "Cannot sync because current session is expired"), UserDataSyncErrorCode.SessionExpired);
}
for (const synchroniser of this.synchronisers) { for (const synchroniser of this.synchronisers) {
try { try {
await synchroniser.sync(manifest ? manifest[synchroniser.resourceKey] : undefined); await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resourceKey] : undefined);
} catch (e) { } catch (e) {
this.handleSyncError(e, synchroniser.source); this.handleSyncError(e, synchroniser.source);
} }
} }
// After syncing, get the manifest if it was not available before
if (manifest === null) {
manifest = await this.userDataSyncStoreService.manifest();
}
// Update local session id
if (manifest && manifest.session !== sessionId) {
this.storageService.store(SESSION_ID_KEY, manifest.session, StorageScope.GLOBAL);
}
this.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`); this.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`);
} finally { } finally {
@@ -140,26 +160,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return synchroniser.accept(content); return synchroniser.accept(content);
} }
private async hasPreviouslySynced(): Promise<boolean> {
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
if (await synchroniser.hasPreviouslySynced()) {
return true;
}
}
return false;
}
private async hasLocalData(): Promise<boolean> {
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
if (await synchroniser.hasLocalData()) {
return true;
}
}
return false;
}
async getRemoteContent(source: SyncSource, preview: boolean): Promise<string | null> { async getRemoteContent(source: SyncSource, preview: boolean): Promise<string | null> {
await this.checkEnablement(); await this.checkEnablement();
for (const synchroniser of this.synchronisers) { for (const synchroniser of this.synchronisers) {
@@ -189,6 +189,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
async resetLocal(): Promise<void> { async resetLocal(): Promise<void> {
await this.checkEnablement(); await this.checkEnablement();
this.storageService.remove(SESSION_ID_KEY, StorageScope.GLOBAL);
for (const synchroniser of this.synchronisers) { for (const synchroniser of this.synchronisers) {
try { try {
synchroniser.resetLocal(); synchroniser.resetLocal();
@@ -199,6 +200,26 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
} }
} }
private async hasPreviouslySynced(): Promise<boolean> {
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
if (await synchroniser.hasPreviouslySynced()) {
return true;
}
}
return false;
}
private async hasLocalData(): Promise<boolean> {
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
if (await synchroniser.hasLocalData()) {
return true;
}
}
return false;
}
private async resetRemote(): Promise<void> { private async resetRemote(): Promise<void> {
await this.checkEnablement(); await this.checkEnablement();
try { try {
@@ -6,7 +6,6 @@
import { Disposable, } from 'vs/base/common/lifecycle'; import { Disposable, } from 'vs/base/common/lifecycle';
import { IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, IUserDataSyncStore, getUserDataSyncStore, IUserDataAuthTokenService, SyncSource, UserDataSyncStoreError, IUserDataSyncLogService, IUserDataManifest } from 'vs/platform/userDataSync/common/userDataSync'; import { IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, IUserDataSyncStore, getUserDataSyncStore, IUserDataAuthTokenService, SyncSource, UserDataSyncStoreError, IUserDataSyncLogService, IUserDataManifest } from 'vs/platform/userDataSync/common/userDataSync';
import { IRequestService, asText, isSuccess, asJson } from 'vs/platform/request/common/request'; import { IRequestService, asText, isSuccess, asJson } from 'vs/platform/request/common/request';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources'; import { joinPath } from 'vs/base/common/resources';
import { CancellationToken } from 'vs/base/common/cancellation'; import { CancellationToken } from 'vs/base/common/cancellation';
import { IHeaders, IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; import { IHeaders, IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request';
@@ -33,7 +32,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
throw new Error('No settings sync store url configured.'); throw new Error('No settings sync store url configured.');
} }
const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', key, 'latest').toString(); const url = joinPath(this.userDataSyncStore.url, 'resource', key, 'latest').toString();
const headers: IHeaders = {}; const headers: IHeaders = {};
// Disable caching as they are cached by synchronisers // Disable caching as they are cached by synchronisers
headers['Cache-Control'] = 'no-cache'; headers['Cache-Control'] = 'no-cache';
@@ -65,7 +64,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
throw new Error('No settings sync store url configured.'); throw new Error('No settings sync store url configured.');
} }
const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', key).toString(); const url = joinPath(this.userDataSyncStore.url, 'resource', key).toString();
const headers: IHeaders = { 'Content-Type': 'text/plain' }; const headers: IHeaders = { 'Content-Type': 'text/plain' };
if (ref) { if (ref) {
headers['If-Match'] = ref; headers['If-Match'] = ref;
@@ -89,7 +88,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
throw new Error('No settings sync store url configured.'); throw new Error('No settings sync store url configured.');
} }
const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', 'latest').toString(); const url = joinPath(this.userDataSyncStore.url, 'manifest').toString();
const headers: IHeaders = { 'Content-Type': 'application/json' }; const headers: IHeaders = { 'Content-Type': 'application/json' };
const context = await this.request({ type: 'GET', url, headers }, undefined, CancellationToken.None); const context = await this.request({ type: 'GET', url, headers }, undefined, CancellationToken.None);
@@ -105,7 +104,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
throw new Error('No settings sync store url configured.'); throw new Error('No settings sync store url configured.');
} }
const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource').toString(); const url = joinPath(this.userDataSyncStore.url, 'resource').toString();
const headers: IHeaders = { 'Content-Type': 'text/plain' }; const headers: IHeaders = { 'Content-Type': 'text/plain' };
const context = await this.request({ type: 'DELETE', url, headers }, undefined, CancellationToken.None); const context = await this.request({ type: 'DELETE', url, headers }, undefined, CancellationToken.None);
@@ -134,6 +133,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
} }
if (context.res.statusCode === 401) { if (context.res.statusCode === 401) {
this.authTokenService.sendTokenFailed();
throw new UserDataSyncStoreError(`Request '${options.url?.toString()}' failed because of Unauthorized (401).`, UserDataSyncErrorCode.Unauthorized, source); throw new UserDataSyncStoreError(`Request '${options.url?.toString()}' failed because of Unauthorized (401).`, UserDataSyncErrorCode.Unauthorized, source);
} }
@@ -5,11 +5,7 @@
import * as assert from 'assert'; import * as assert from 'assert';
import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge'; import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge';
import { IStringDictionary } from 'vs/base/common/collections'; import { TestUserDataSyncUtilService } from 'vs/platform/userDataSync/test/common/userDataSyncClient';
import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { URI } from 'vs/base/common/uri';
import type { IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
suite('KeybindingsMerge - No Conflicts', () => { suite('KeybindingsMerge - No Conflicts', () => {
@@ -613,7 +609,7 @@ suite('KeybindingsMerge - No Conflicts', () => {
}); });
async function mergeKeybindings(localContent: string, remoteContent: string, baseContent: string | null) { async function mergeKeybindings(localContent: string, remoteContent: string, baseContent: string | null) {
const userDataSyncUtilService = new MockUserDataSyncUtilService(); const userDataSyncUtilService = new TestUserDataSyncUtilService();
const formattingOptions = await userDataSyncUtilService.resolveFormattingOptions(); const formattingOptions = await userDataSyncUtilService.resolveFormattingOptions();
return merge(localContent, remoteContent, baseContent, formattingOptions, userDataSyncUtilService); return merge(localContent, remoteContent, baseContent, formattingOptions, userDataSyncUtilService);
} }
@@ -621,22 +617,3 @@ async function mergeKeybindings(localContent: string, remoteContent: string, bas
function stringify(value: any): string { function stringify(value: any): string {
return JSON.stringify(value, null, '\t'); return JSON.stringify(value, null, '\t');
} }
class MockUserDataSyncUtilService implements IUserDataSyncUtilService {
_serviceBrand: any;
async resolveUserBindings(userbindings: string[]): Promise<IStringDictionary<string>> {
const keys: IStringDictionary<string> = {};
for (const keybinding of userbindings) {
keys[keybinding] = keybinding;
}
return keys;
}
async resolveFormattingOptions(file?: URI): Promise<FormattingOptions> {
return { eol: '\n', insertSpaces: false, tabSize: 4 };
}
async ignoreExtensionsToSync(extensions: IExtensionIdentifier[]): Promise<void> { }
}
@@ -0,0 +1,246 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IRequestService } from 'vs/platform/request/common/request';
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IUserData, ResourceKey, IUserDataManifest, ALL_RESOURCE_KEYS, IUserDataAuthTokenService, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, ISettingsSyncService, IUserDataSyncService } from 'vs/platform/userDataSync/common/userDataSync';
import { bufferToStream, VSBuffer } from 'vs/base/common/buffer';
import { generateUuid } from 'vs/base/common/uuid';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { NullLogService, ILogService } from 'vs/platform/log/common/log';
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { Schemas } from 'vs/base/common/network';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService';
import { IGlobalExtensionEnablementService, IExtensionManagementService, IExtensionGalleryService, DidInstallExtensionEvent, DidUninstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement';
import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
import { Disposable } from 'vs/base/common/lifecycle';
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
import { Emitter } from 'vs/base/common/event';
export class UserDataSyncClient extends Disposable {
readonly instantiationService: TestInstantiationService;
constructor(readonly testServer: UserDataSyncTestServer = new UserDataSyncTestServer()) {
super();
this.instantiationService = new TestInstantiationService();
}
async setUp(empty: boolean = false): Promise<void> {
const userDataDirectory = URI.file('userdata').with({ scheme: Schemas.inMemory });
const userDataSyncHome = joinPath(userDataDirectory, '.sync');
const environmentService = this.instantiationService.stub(IEnvironmentService, <Partial<IEnvironmentService>>{
userDataSyncHome,
settingsResource: joinPath(userDataDirectory, 'settings.json'),
settingsSyncPreviewResource: joinPath(userDataSyncHome, 'settings.json'),
keybindingsResource: joinPath(userDataDirectory, 'keybindings.json'),
keybindingsSyncPreviewResource: joinPath(userDataSyncHome, 'keybindings.json'),
argvResource: joinPath(userDataDirectory, 'argv.json'),
});
const logService = new NullLogService();
this.instantiationService.stub(ILogService, logService);
const fileService = this._register(new FileService(logService));
fileService.registerProvider(Schemas.inMemory, new InMemoryFileSystemProvider());
this.instantiationService.stub(IFileService, fileService);
this.instantiationService.stub(IStorageService, new InMemoryStorageService());
await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({
'configurationSync.store': {
url: this.testServer.url,
authenticationProviderId: 'test'
}
})));
const configurationService = new ConfigurationService(environmentService.settingsResource, fileService);
await configurationService.initialize();
this.instantiationService.stub(IConfigurationService, configurationService);
this.instantiationService.stub(IRequestService, this.testServer);
this.instantiationService.stub(IUserDataAuthTokenService, <Partial<IUserDataAuthTokenService>>{
onDidChangeToken: new Emitter<string | undefined>().event,
async getToken() { return 'token'; }
});
this.instantiationService.stub(IUserDataSyncLogService, logService);
this.instantiationService.stub(ITelemetryService, NullTelemetryService);
this.instantiationService.stub(IUserDataSyncStoreService, this.instantiationService.createInstance(UserDataSyncStoreService));
this.instantiationService.stub(IUserDataSyncUtilService, new TestUserDataSyncUtilService());
this.instantiationService.stub(IUserDataSyncEnablementService, this.instantiationService.createInstance(UserDataSyncEnablementService));
this.instantiationService.stub(IGlobalExtensionEnablementService, this.instantiationService.createInstance(GlobalExtensionEnablementService));
this.instantiationService.stub(IExtensionManagementService, <Partial<IExtensionManagementService>>{
async getInstalled() { return []; },
onDidInstallExtension: new Emitter<DidInstallExtensionEvent>().event,
onDidUninstallExtension: new Emitter<DidUninstallExtensionEvent>().event,
});
this.instantiationService.stub(IExtensionGalleryService, <Partial<IExtensionGalleryService>>{
isEnabled() { return true; },
async getCompatibleExtension() { return null; }
});
this.instantiationService.stub(ISettingsSyncService, this.instantiationService.createInstance(SettingsSynchroniser));
this.instantiationService.stub(IUserDataSyncService, this.instantiationService.createInstance(UserDataSyncService));
if (empty) {
await fileService.del(environmentService.settingsResource);
} else {
await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([])));
await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'en' })));
}
await configurationService.reloadConfiguration();
}
}
export class UserDataSyncTestServer implements IRequestService {
_serviceBrand: any;
readonly url: string = 'http://host:3000';
private session: string | null = null;
private readonly data: Map<ResourceKey, IUserData> = new Map<ResourceKey, IUserData>();
private _requests: { url: string, type: string, headers?: IHeaders }[] = [];
get requests(): { url: string, type: string, headers?: IHeaders }[] { return this._requests; }
private _responses: { status: number }[] = [];
get responses(): { status: number }[] { return this._responses; }
reset(): void { this._requests = []; this._responses = []; }
async resolveProxy(url: string): Promise<string | undefined> { return url; }
async request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> {
const headers: IHeaders = {};
if (options.headers) {
if (options.headers['If-None-Match']) {
headers['If-None-Match'] = options.headers['If-None-Match'];
}
if (options.headers['If-Match']) {
headers['If-Match'] = options.headers['If-Match'];
}
}
this._requests.push({ url: options.url!, type: options.type!, headers });
const requestContext = await this.doRequest(options);
this._responses.push({ status: requestContext.res.statusCode! });
return requestContext;
}
private async doRequest(options: IRequestOptions): Promise<IRequestContext> {
const versionUrl = `${this.url}/v1/`;
const relativePath = options.url!.indexOf(versionUrl) === 0 ? options.url!.substring(versionUrl.length) : undefined;
const segments = relativePath ? relativePath.split('/') : [];
if (options.type === 'GET' && segments.length === 1 && segments[0] === 'manifest') {
return this.getManifest(options.headers);
}
if (options.type === 'GET' && segments.length === 3 && segments[0] === 'resource' && segments[2] === 'latest') {
return this.getLatestData(segments[1], options.headers);
}
if (options.type === 'POST' && segments.length === 2 && segments[0] === 'resource') {
return this.writeData(segments[1], options.data, options.headers);
}
if (options.type === 'DELETE' && segments.length === 1 && segments[0] === 'resource') {
return this.clear(options.headers);
}
return this.toResponse(501);
}
private async getManifest(headers?: IHeaders): Promise<IRequestContext> {
if (this.session) {
const latest: Record<ResourceKey, string> = Object.create({});
const manifest: IUserDataManifest = { session: this.session, latest };
this.data.forEach((value, key) => latest[key] = value.ref);
return this.toResponse(200, { 'Content-Type': 'application/json' }, JSON.stringify(manifest));
}
return this.toResponse(204);
}
private async getLatestData(resource: string, headers: IHeaders = {}): Promise<IRequestContext> {
const resourceKey = ALL_RESOURCE_KEYS.find(key => key === resource);
if (resourceKey) {
const data = this.data.get(resourceKey);
if (!data) {
return this.toResponse(204, { etag: '0' });
}
if (headers['If-None-Match'] === data.ref) {
return this.toResponse(304);
}
return this.toResponse(200, { etag: data.ref }, data.content || '');
}
return this.toResponse(204);
}
private async writeData(resource: string, content: string = '', headers: IHeaders = {}): Promise<IRequestContext> {
if (!headers['If-Match']) {
return this.toResponse(428);
}
if (!this.session) {
this.session = generateUuid();
}
const resourceKey = ALL_RESOURCE_KEYS.find(key => key === resource);
if (resourceKey) {
const data = this.data.get(resourceKey);
if (headers['If-Match'] !== (data ? data.ref : '0')) {
return this.toResponse(412);
}
const ref = `${parseInt(data?.ref || '0') + 1}`;
this.data.set(resourceKey, { ref, content });
return this.toResponse(200, { etag: ref });
}
return this.toResponse(204);
}
private async clear(headers?: IHeaders): Promise<IRequestContext> {
this.data.clear();
this.session = null;
return this.toResponse(204);
}
private toResponse(statusCode: number, headers?: IHeaders, data?: string): IRequestContext {
return {
res: {
headers: headers || {},
statusCode
},
stream: bufferToStream(VSBuffer.fromString(data || ''))
};
}
}
export class TestUserDataSyncUtilService implements IUserDataSyncUtilService {
_serviceBrand: any;
async resolveUserBindings(userbindings: string[]): Promise<IStringDictionary<string>> {
const keys: IStringDictionary<string> = {};
for (const keybinding of userbindings) {
keys[keybinding] = keybinding;
}
return keys;
}
async resolveFormattingOptions(file?: URI): Promise<FormattingOptions> {
return { eol: '\n', insertSpaces: false, tabSize: 4 };
}
}
@@ -0,0 +1,555 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { IUserDataSyncService, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, SyncSource } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IFileService } from 'vs/platform/files/common/files';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { VSBuffer } from 'vs/base/common/buffer';
suite('UserDataSyncService', () => {
const disposableStore = new DisposableStore();
teardown(() => disposableStore.clear());
test('test first time sync ever', async () => {
// Setup the client
const target = new UserDataSyncTestServer();
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
const testObject = client.instantiationService.get(IUserDataSyncService);
// Sync for first time
await testObject.sync();
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
// Settings
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } },
// Keybindings
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '0' } },
// Global state
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } },
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
});
test('test first time sync ever with no data', async () => {
// Setup the client
const target = new UserDataSyncTestServer();
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp(true);
const testObject = client.instantiationService.get(IUserDataSyncService);
// Sync for first time
await testObject.sync();
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
// Settings
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
// Keybindings
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
// Global state
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } },
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
});
test('test first time sync from the client with no changes - pull', async () => {
const target = new UserDataSyncTestServer();
// Setup and sync from the first client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
await client.instantiationService.get(IUserDataSyncService).sync();
// Setup the test client
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
const testObject = testClient.instantiationService.get(IUserDataSyncService);
// Sync (pull) from the test client
target.reset();
await testObject.isFirstTimeSyncWithMerge();
await testObject.pull();
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
// Settings
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
// Keybindings
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
// Global state
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
]);
});
test('test first time sync from the client with changes - pull', async () => {
const target = new UserDataSyncTestServer();
// Setup and sync from the first client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
await client.instantiationService.get(IUserDataSyncService).sync();
// Setup the test client with changes
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
const testObject = testClient.instantiationService.get(IUserDataSyncService);
const fileService = testClient.instantiationService.get(IFileService);
const environmentService = testClient.instantiationService.get(IEnvironmentService);
await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 })));
await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }])));
await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' })));
// Sync (pull) from the test client
target.reset();
await testObject.isFirstTimeSyncWithMerge();
await testObject.pull();
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
// Settings
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
// Keybindings
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
// Global state
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
]);
});
test('test first time sync from the client with no changes - merge', async () => {
const target = new UserDataSyncTestServer();
// Setup and sync from the first client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
await client.instantiationService.get(IUserDataSyncService).sync();
// Setup the test client
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
const testObject = testClient.instantiationService.get(IUserDataSyncService);
// Sync (merge) from the test client
target.reset();
await testObject.isFirstTimeSyncWithMerge();
await testObject.sync();
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
// Settings
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
// Keybindings
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
// Global state
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
]);
});
test('test first time sync from the client with changes - merge', async () => {
const target = new UserDataSyncTestServer();
// Setup and sync from the first client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
await client.instantiationService.get(IUserDataSyncService).sync();
// Setup the test client with changes
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
const fileService = testClient.instantiationService.get(IFileService);
const environmentService = testClient.instantiationService.get(IEnvironmentService);
await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 })));
await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }])));
await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' })));
const testObject = testClient.instantiationService.get(IUserDataSyncService);
// Sync (merge) from the test client
target.reset();
await testObject.isFirstTimeSyncWithMerge();
await testObject.sync();
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
// Settings
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } },
// Keybindings
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '1' } },
// Global state
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '1' } },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
]);
});
test('test sync when there are no changes', async () => {
const target = new UserDataSyncTestServer();
// Setup and sync from the client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
const testObject = client.instantiationService.get(IUserDataSyncService);
await testObject.sync();
// sync from the client again
target.reset();
await testObject.sync();
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
});
test('test sync when there are local changes', async () => {
const target = new UserDataSyncTestServer();
// Setup and sync from the client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
const testObject = client.instantiationService.get(IUserDataSyncService);
await testObject.sync();
target.reset();
// Do changes in the client
const fileService = client.instantiationService.get(IFileService);
const environmentService = client.instantiationService.get(IEnvironmentService);
await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 })));
await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }])));
await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' })));
// Sync from the client
await testObject.sync();
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
// Settings
{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } },
// Keybindings
{ type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '1' } },
// Global state
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '1' } },
]);
});
test('test sync when there are remote changes', async () => {
const target = new UserDataSyncTestServer();
// Sync from first client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
await client.instantiationService.get(IUserDataSyncService).sync();
// Sync from test client
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
const testObject = testClient.instantiationService.get(IUserDataSyncService);
await testObject.sync();
// Do changes in first client and sync
const fileService = client.instantiationService.get(IFileService);
const environmentService = client.instantiationService.get(IEnvironmentService);
await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 })));
await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }])));
await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' })));
await client.instantiationService.get(IUserDataSyncService).sync();
// Sync from test client
target.reset();
await testObject.sync();
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
// Settings
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: { 'If-None-Match': '1' } },
// Keybindings
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: { 'If-None-Match': '1' } },
// Global state
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: { 'If-None-Match': '1' } },
]);
});
test('test delete', async () => {
const target = new UserDataSyncTestServer();
// Sync from the client
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
const testObject = testClient.instantiationService.get(IUserDataSyncService);
await testObject.sync();
// Reset from the client
target.reset();
await testObject.reset();
assert.deepEqual(target.requests, [
// Manifest
{ type: 'DELETE', url: `${target.url}/v1/resource`, headers: {} },
]);
});
test('test delete and sync', async () => {
const target = new UserDataSyncTestServer();
// Sync from the client
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
const testObject = testClient.instantiationService.get(IUserDataSyncService);
await testObject.sync();
// Reset from the client
await testObject.reset();
// Sync again
target.reset();
await testObject.sync();
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
// Settings
{ type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } },
// Keybindings
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '0' } },
// Global state
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } },
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
});
test('test delete on one client throws turned off error on other client while syncing', async () => {
const target = new UserDataSyncTestServer();
// Set up and sync from the client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
await client.instantiationService.get(IUserDataSyncService).sync();
// Set up and sync from the test client
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
const testObject = testClient.instantiationService.get(IUserDataSyncService);
await testObject.sync();
// Reset from the first client
await client.instantiationService.get(IUserDataSyncService).reset();
// Sync from the test client
target.reset();
try {
await testObject.sync();
} catch (e) {
assert.ok(e instanceof UserDataSyncError);
assert.deepEqual((<UserDataSyncError>e).code, UserDataSyncErrorCode.TurnedOff);
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
return;
}
throw assert.fail('Should fail with turned off error');
});
test('test creating new session from one client throws session expired error on another client while syncing', async () => {
const target = new UserDataSyncTestServer();
// Set up and sync from the client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
await client.instantiationService.get(IUserDataSyncService).sync();
// Set up and sync from the test client
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
const testObject = testClient.instantiationService.get(IUserDataSyncService);
await testObject.sync();
// Reset from the first client
await client.instantiationService.get(IUserDataSyncService).reset();
// Sync again from the first client to create new session
await client.instantiationService.get(IUserDataSyncService).sync();
// Sync from the test client
target.reset();
try {
await testObject.sync();
} catch (e) {
assert.ok(e instanceof UserDataSyncError);
assert.deepEqual((<UserDataSyncError>e).code, UserDataSyncErrorCode.SessionExpired);
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
return;
}
throw assert.fail('Should fail with turned off error');
});
test('test sync status', async () => {
const target = new UserDataSyncTestServer();
// Setup the client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
const testObject = client.instantiationService.get(IUserDataSyncService);
// sync from the client
const actualStatuses: SyncStatus[] = [];
const disposable = testObject.onDidChangeStatus(status => actualStatuses.push(status));
await testObject.sync();
disposable.dispose();
assert.deepEqual(actualStatuses, [SyncStatus.Syncing, SyncStatus.Idle, SyncStatus.Syncing, SyncStatus.Idle, SyncStatus.Syncing, SyncStatus.Idle, SyncStatus.Syncing, SyncStatus.Idle]);
});
test('test sync conflicts status', async () => {
const target = new UserDataSyncTestServer();
// Setup and sync from the first client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
let fileService = client.instantiationService.get(IFileService);
let environmentService = client.instantiationService.get(IEnvironmentService);
await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 })));
await client.instantiationService.get(IUserDataSyncService).sync();
// Setup the test client
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
fileService = testClient.instantiationService.get(IFileService);
environmentService = testClient.instantiationService.get(IEnvironmentService);
await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 16 })));
const testObject = testClient.instantiationService.get(IUserDataSyncService);
// sync from the client
await testObject.sync();
assert.deepEqual(testObject.status, SyncStatus.HasConflicts);
assert.deepEqual(testObject.conflictsSources, [SyncSource.Settings]);
});
test('test sync will sync other non conflicted areas', async () => {
const target = new UserDataSyncTestServer();
// Setup and sync from the first client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
let fileService = client.instantiationService.get(IFileService);
let environmentService = client.instantiationService.get(IEnvironmentService);
await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 })));
await client.instantiationService.get(IUserDataSyncService).sync();
// Setup the test client and get conflicts in settings
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
let testFileService = testClient.instantiationService.get(IFileService);
let testEnvironmentService = testClient.instantiationService.get(IEnvironmentService);
await testFileService.writeFile(testEnvironmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 16 })));
const testObject = testClient.instantiationService.get(IUserDataSyncService);
await testObject.sync();
// sync from the first client with changes in keybindings
await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }])));
await client.instantiationService.get(IUserDataSyncService).sync();
// sync from the test client
target.reset();
const actualStatuses: SyncStatus[] = [];
const disposable = testObject.onDidChangeStatus(status => actualStatuses.push(status));
await testObject.sync();
disposable.dispose();
assert.deepEqual(actualStatuses, []);
assert.deepEqual(testObject.status, SyncStatus.HasConflicts);
assert.deepEqual(target.requests, [
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
// Keybindings
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: { 'If-None-Match': '1' } },
]);
});
test('test stop sync reset status', async () => {
const target = new UserDataSyncTestServer();
// Setup and sync from the first client
const client = disposableStore.add(new UserDataSyncClient(target));
await client.setUp();
let fileService = client.instantiationService.get(IFileService);
let environmentService = client.instantiationService.get(IEnvironmentService);
await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 })));
await client.instantiationService.get(IUserDataSyncService).sync();
// Setup the test client
const testClient = disposableStore.add(new UserDataSyncClient(target));
await testClient.setUp();
fileService = testClient.instantiationService.get(IFileService);
environmentService = testClient.instantiationService.get(IEnvironmentService);
await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 16 })));
const testObject = testClient.instantiationService.get(IUserDataSyncService);
await testObject.sync();
// sync from the client
await testObject.stop();
assert.deepEqual(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflictsSources, []);
});
});
@@ -15,6 +15,9 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log'; import { ILogService } from 'vs/platform/log/common/log';
import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
import { URI } from 'vs/base/common/uri';
import { IWaitUntil } from 'vs/base/common/event';
@extHostCustomer @extHostCustomer
export class MainThreadFileSystemEventService { export class MainThreadFileSystemEventService {
@@ -28,6 +31,7 @@ export class MainThreadFileSystemEventService {
@IProgressService progressService: IProgressService, @IProgressService progressService: IProgressService,
@IConfigurationService configService: IConfigurationService, @IConfigurationService configService: IConfigurationService,
@ILogService logService: ILogService, @ILogService logService: ILogService,
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService
) { ) {
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService); const proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService);
@@ -38,7 +42,7 @@ export class MainThreadFileSystemEventService {
changed: [], changed: [],
deleted: [] deleted: []
}; };
this._listener.add(fileService.onFileChanges(event => { this._listener.add(fileService.onDidFilesChange(event => {
for (let change of event.changes) { for (let change of event.changes) {
switch (change.type) { switch (change.type) {
case FileChangeType.ADDED: case FileChangeType.ADDED:
@@ -66,9 +70,7 @@ export class MainThreadFileSystemEventService {
messages.set(FileOperation.DELETE, localize('msg-delete', "Running 'File Delete' participants...")); messages.set(FileOperation.DELETE, localize('msg-delete', "Running 'File Delete' participants..."));
messages.set(FileOperation.MOVE, localize('msg-rename', "Running 'File Rename' participants...")); messages.set(FileOperation.MOVE, localize('msg-rename', "Running 'File Rename' participants..."));
function participateInFileOperation(e: IWaitUntil, operation: FileOperation, target: URI, source?: URI): void {
this._listener.add(textFileService.onWillRunOperation(e => {
const timeout = configService.getValue<number>('files.participants.timeout'); const timeout = configService.getValue<number>('files.participants.timeout');
if (timeout <= 0) { if (timeout <= 0) {
return; // disabled return; // disabled
@@ -76,19 +78,19 @@ export class MainThreadFileSystemEventService {
const p = progressService.withProgress({ location: ProgressLocation.Window }, progress => { const p = progressService.withProgress({ location: ProgressLocation.Window }, progress => {
progress.report({ message: messages.get(e.operation) }); progress.report({ message: messages.get(operation) });
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const cts = new CancellationTokenSource(); const cts = new CancellationTokenSource();
const timeoutHandle = setTimeout(() => { const timeoutHandle = setTimeout(() => {
logService.trace('CANCELLED file participants because of timeout', timeout, e.target, e.operation); logService.trace('CANCELLED file participants because of timeout', timeout, target, operation);
cts.cancel(); cts.cancel();
reject(new Error('timeout')); reject(new Error('timeout'));
}, timeout); }, timeout);
proxy.$onWillRunFileOperation(e.operation, e.target, e.source, timeout, cts.token) proxy.$onWillRunFileOperation(operation, target, source, timeout, cts.token)
.then(resolve, reject) .then(resolve, reject)
.finally(() => clearTimeout(timeoutHandle)); .finally(() => clearTimeout(timeoutHandle));
}); });
@@ -96,10 +98,14 @@ export class MainThreadFileSystemEventService {
}); });
e.waitUntil(p); e.waitUntil(p);
})); }
this._listener.add(textFileService.onWillCreateTextFile(e => participateInFileOperation(e, FileOperation.CREATE, e.resource)));
this._listener.add(workingCopyFileService.onBeforeWorkingCopyFileOperation(e => participateInFileOperation(e, e.operation, e.target, e.source)));
// AFTER file operation // AFTER file operation
this._listener.add(textFileService.onDidRunOperation(e => proxy.$onDidRunFileOperation(e.operation, e.target, e.source))); this._listener.add(textFileService.onDidCreateTextFile(e => proxy.$onDidRunFileOperation(FileOperation.CREATE, e.resource, undefined)));
this._listener.add(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => proxy.$onDidRunFileOperation(e.operation, e.target, e.source)));
} }
dispose(): void { dispose(): void {
@@ -11,7 +11,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
import { CancellationToken } from 'vs/base/common/cancellation'; import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position'; import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range'; import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField, ICodeActionProviderMetadataDto } from '../common/extHost.protocol'; import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField, ICodeActionProviderMetadataDto, ILanguageWordDefinitionDto } from '../common/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService'; import { IModeService } from 'vs/editor/common/services/modeService';
@@ -36,6 +36,34 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
) { ) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostLanguageFeatures); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostLanguageFeatures);
this._modeService = modeService; this._modeService = modeService;
if (this._modeService) {
const updateAllWordDefinitions = () => {
const langWordPairs = LanguageConfigurationRegistry.getWordDefinitions();
let wordDefinitionDtos: ILanguageWordDefinitionDto[] = [];
for (const [languageId, wordDefinition] of langWordPairs) {
const language = this._modeService.getLanguageIdentifier(languageId);
if (!language) {
continue;
}
wordDefinitionDtos.push({
languageId: language.language,
regexSource: wordDefinition.source,
regexFlags: wordDefinition.flags
});
}
this._proxy.$setWordDefinitions(wordDefinitionDtos);
};
LanguageConfigurationRegistry.onDidChange((e) => {
const wordDefinition = LanguageConfigurationRegistry.getWordDefinition(e.languageIdentifier.id);
this._proxy.$setWordDefinitions([{
languageId: e.languageIdentifier.language,
regexSource: wordDefinition.source,
regexFlags: wordDefinition.flags
}]);
});
updateAllWordDefinitions();
}
} }
dispose(): void { dispose(): void {

Some files were not shown because too many files have changed in this diff Show More