Archived
Merge from vscode 9bc92b48d945144abb405b9e8df05e18accb9148
This commit is contained in:
+1
-1
@@ -193,7 +193,7 @@
|
||||
"typemoq",
|
||||
"sinon",
|
||||
"vs/nls",
|
||||
"azdata",
|
||||
"azdata",
|
||||
"**/{vs,sql}/base/common/**",
|
||||
"**/{vs,sql}/base/test/common/**",
|
||||
"**/{vs,sql}/base/parts/*/common/**",
|
||||
|
||||
Vendored
-2
@@ -16,8 +16,6 @@
|
||||
"request": "attach",
|
||||
"name": "Attach to Extension Host",
|
||||
"port": 5870,
|
||||
"timeout": 30000,
|
||||
"restart": true,
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
]
|
||||
|
||||
Vendored
-14
@@ -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",
|
||||
"script": "strict-vscode-watch",
|
||||
|
||||
@@ -207,7 +207,7 @@ steps:
|
||||
"toolVersion": "1.0"
|
||||
}
|
||||
]
|
||||
SessionTimeout: 120
|
||||
SessionTimeout: 60
|
||||
displayName: Notarization
|
||||
|
||||
- script: |
|
||||
|
||||
+120
-83
@@ -467,57 +467,61 @@
|
||||
],
|
||||
"menus": {
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "git.setLogLevel",
|
||||
"when": "config.git.enabled && !git.missing"
|
||||
},
|
||||
{
|
||||
"command": "git.clone",
|
||||
"when": "config.git.enabled"
|
||||
"when": "config.git.enabled && !git.missing"
|
||||
},
|
||||
{
|
||||
"command": "git.init",
|
||||
"when": "config.git.enabled"
|
||||
"when": "config.git.enabled && !git.missing"
|
||||
},
|
||||
{
|
||||
"command": "git.openRepository",
|
||||
"when": "config.git.enabled"
|
||||
"when": "config.git.enabled && !git.missing"
|
||||
},
|
||||
{
|
||||
"command": "git.close",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.refresh",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.openFile",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.openHEADFile",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.openChange",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stage",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stageAll",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stageAllTracked",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stageAllUntracked",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stageSelectedRanges",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stageChange",
|
||||
@@ -525,7 +529,7 @@
|
||||
},
|
||||
{
|
||||
"command": "git.revertSelectedRanges",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.revertChange",
|
||||
@@ -537,51 +541,63 @@
|
||||
},
|
||||
{
|
||||
"command": "git.unstage",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.unstageAll",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.unstageSelectedRanges",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.clean",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.commitStagedAmend",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.commitAll",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.commitAllSigned",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.commitAllAmend",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.restoreCommitTemplate",
|
||||
@@ -593,111 +609,111 @@
|
||||
},
|
||||
{
|
||||
"command": "git.undoCommit",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.checkout",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.branch",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.branchFrom",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.deleteBranch",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.renameBranch",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.pull",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.pullFrom",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.pullRebase",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.pullFrom",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.merge",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.createTag",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.deleteTag",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.fetch",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.fetchPrune",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.fetchAll",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.push",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.removeRemote",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.sync",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.syncRebase",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.publish",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.showOutput",
|
||||
@@ -705,35 +721,35 @@
|
||||
},
|
||||
{
|
||||
"command": "git.ignore",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stashIncludeUntracked",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stash",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stashPop",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stashPopLatest",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stashApply",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stashApplyLatest",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.stashDrop",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.openDiff",
|
||||
@@ -749,11 +765,6 @@
|
||||
}
|
||||
],
|
||||
"scm/title": [
|
||||
{
|
||||
"command": "git.init",
|
||||
"group": "navigation",
|
||||
"when": "config.git.enabled && !scmProvider && gitOpenRepositoryCount == 0 && workspaceFolderCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.commit",
|
||||
"group": "navigation",
|
||||
@@ -1227,76 +1238,76 @@
|
||||
{
|
||||
"command": "git.openFile",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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": [
|
||||
{
|
||||
"command": "git.stageSelectedRanges",
|
||||
"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",
|
||||
"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",
|
||||
"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": [
|
||||
{
|
||||
"command": "git.stageChange",
|
||||
"when": "originalResourceScheme == git"
|
||||
"when": "config.git.enabled && !git.missing && originalResourceScheme == git"
|
||||
},
|
||||
{
|
||||
"command": "git.revertChange",
|
||||
"when": "originalResourceScheme == git"
|
||||
"when": "config.git.enabled && !git.missing && originalResourceScheme == git"
|
||||
}
|
||||
],
|
||||
"timeline/item/context": [
|
||||
{
|
||||
"command": "git.timeline.openDiff",
|
||||
"group": "inline",
|
||||
"when": "timelineItem =~ /git:file\\b/"
|
||||
"when": "config.git.enabled && !git.missing && timelineItem =~ /git:file\\b/"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.openDiff",
|
||||
"group": "1_timeline",
|
||||
"when": "timelineItem =~ /git:file\\b/"
|
||||
"when": "config.git.enabled && !git.missing && timelineItem =~ /git:file\\b/"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.copyCommitId",
|
||||
"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",
|
||||
"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
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"byline": "^5.0.0",
|
||||
@@ -1829,11 +1867,10 @@
|
||||
"@types/file-type": "^5.2.1",
|
||||
"@types/mocha": "2.2.43",
|
||||
"@types/node": "^12.11.7",
|
||||
"@types/vscode": "^1.42",
|
||||
"@types/which": "^1.0.28",
|
||||
"mocha": "^3.2.0",
|
||||
"mocha-junit-reporter": "^1.23.3",
|
||||
"mocha-multi-reporters": "^1.1.7",
|
||||
"vscode-test": "^1.3.0"
|
||||
"vscode": "^1.1.36"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,5 +149,10 @@
|
||||
"colors.untracked": "Color for untracked resources.",
|
||||
"colors.ignored": "Color for ignored resources.",
|
||||
"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)."
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -44,6 +44,7 @@ export interface Commit {
|
||||
readonly authorDate?: Date;
|
||||
readonly authorName?: string;
|
||||
readonly authorEmail?: string;
|
||||
readonly commitDate?: Date;
|
||||
}
|
||||
|
||||
export interface Submodule {
|
||||
|
||||
@@ -333,7 +333,7 @@ function sanitizePath(path: string): string {
|
||||
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 {
|
||||
|
||||
@@ -525,6 +525,7 @@ export interface Commit {
|
||||
authorDate?: Date;
|
||||
authorName?: string;
|
||||
authorEmail?: string;
|
||||
commitDate?: Date;
|
||||
}
|
||||
|
||||
export class GitStatusParser {
|
||||
@@ -655,15 +656,16 @@ export function parseGitmodules(raw: string): Submodule[] {
|
||||
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[] {
|
||||
let commits: Commit[] = [];
|
||||
|
||||
let ref;
|
||||
let name;
|
||||
let email;
|
||||
let date;
|
||||
let authorName;
|
||||
let authorEmail;
|
||||
let authorDate;
|
||||
let commitDate;
|
||||
let parents;
|
||||
let message;
|
||||
let match;
|
||||
@@ -674,7 +676,7 @@ export function parseGitCommits(data: string): Commit[] {
|
||||
break;
|
||||
}
|
||||
|
||||
[, ref, name, email, date, parents, message] = match;
|
||||
[, ref, authorName, authorEmail, authorDate, commitDate, parents, message] = match;
|
||||
|
||||
if (message[message.length - 1] === '\n') {
|
||||
message = message.substr(0, message.length - 1);
|
||||
@@ -685,9 +687,10 @@ export function parseGitCommits(data: string): Commit[] {
|
||||
hash: ` ${ref}`.substr(1),
|
||||
message: ` ${message}`.substr(1),
|
||||
parents: parents ? parents.split(' ') : [],
|
||||
authorDate: new Date(Number(date) * 1000),
|
||||
authorName: ` ${name}`.substr(1),
|
||||
authorEmail: ` ${email}`.substr(1)
|
||||
authorDate: new Date(Number(authorDate) * 1000),
|
||||
authorName: ` ${authorName}`.substr(1),
|
||||
authorEmail: ` ${authorEmail}`.substr(1),
|
||||
commitDate: new Date(Number(commitDate) * 1000),
|
||||
});
|
||||
} while (true);
|
||||
|
||||
|
||||
@@ -178,7 +178,8 @@ export async function activate(context: ExtensionContext): Promise<GitExtension>
|
||||
// console.warn(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();
|
||||
}
|
||||
|
||||
@@ -192,6 +192,7 @@ suite('git', () => {
|
||||
John Doe
|
||||
john.doe@mail.com
|
||||
1580811030
|
||||
1580811031
|
||||
8e5a374372b8393906c7e380dbb09349c5385554
|
||||
This is a commit message.\x00`;
|
||||
|
||||
@@ -202,6 +203,7 @@ This is a commit message.\x00`;
|
||||
authorDate: new Date(1580811030000),
|
||||
authorName: 'John Doe',
|
||||
authorEmail: 'john.doe@mail.com',
|
||||
commitDate: new Date(1580811031000),
|
||||
}]);
|
||||
});
|
||||
|
||||
@@ -210,6 +212,7 @@ This is a commit message.\x00`;
|
||||
John Doe
|
||||
john.doe@mail.com
|
||||
1580811030
|
||||
1580811031
|
||||
8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217
|
||||
This is a commit message.\x00`;
|
||||
|
||||
@@ -220,6 +223,7 @@ This is a commit message.\x00`;
|
||||
authorDate: new Date(1580811030000),
|
||||
authorName: 'John Doe',
|
||||
authorEmail: 'john.doe@mail.com',
|
||||
commitDate: new Date(1580811031000),
|
||||
}]);
|
||||
});
|
||||
|
||||
@@ -228,6 +232,7 @@ This is a commit message.\x00`;
|
||||
John Doe
|
||||
john.doe@mail.com
|
||||
1580811030
|
||||
1580811031
|
||||
|
||||
This is a commit message.\x00`;
|
||||
|
||||
@@ -238,6 +243,7 @@ This is a commit message.\x00`;
|
||||
authorDate: new Date(1580811030000),
|
||||
authorName: 'John Doe',
|
||||
authorEmail: 'john.doe@mail.com',
|
||||
commitDate: new Date(1580811031000),
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
+436
-21
@@ -31,11 +31,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a"
|
||||
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":
|
||||
version "1.0.28"
|
||||
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:
|
||||
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:
|
||||
version "3.0.0"
|
||||
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"
|
||||
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:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
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:
|
||||
version "1.1.8"
|
||||
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"
|
||||
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:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1"
|
||||
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:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
||||
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:
|
||||
version "2.9.0"
|
||||
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"
|
||||
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:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
|
||||
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:
|
||||
version "1.8.19"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.19.tgz#5117dc390d8f8e586d53891dbff3fa308f51abfe"
|
||||
@@ -140,6 +218,11 @@ debug@^3.1.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "0.2.1"
|
||||
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"
|
||||
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:
|
||||
version "4.2.8"
|
||||
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"
|
||||
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:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.2.0.tgz#113cfed52e1d6959ab80248906e2f25a8cdccb74"
|
||||
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:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
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:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
|
||||
@@ -196,7 +338,19 @@ glob@7.1.1:
|
||||
once "^1.3.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"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
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"
|
||||
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:
|
||||
version "1.9.2"
|
||||
resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
|
||||
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:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
|
||||
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:
|
||||
version "1.1.1"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
|
||||
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"
|
||||
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:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
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:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184"
|
||||
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:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
|
||||
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:
|
||||
version "3.2.0"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
@@ -410,6 +648,23 @@ mocha@^3.2.0:
|
||||
mkdirp "0.5.1"
|
||||
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:
|
||||
version "2.0.0"
|
||||
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"
|
||||
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:
|
||||
version "1.4.0"
|
||||
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"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
rimraf@^2.6.3:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
|
||||
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
performance-now@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||
|
||||
"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"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
@@ -449,6 +763,39 @@ semver@^5.3.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
||||
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:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
|
||||
@@ -463,6 +810,62 @@ supports-color@3.1.2:
|
||||
dependencies:
|
||||
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:
|
||||
version "0.1.1"
|
||||
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"
|
||||
integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw==
|
||||
|
||||
vscode-test@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-1.3.0.tgz#3310ab385d9b887b4c82e8f52be1030e7cf9493d"
|
||||
integrity sha512-LddukcBiSU2FVTDr3c1D8lwkiOvwlJdDL2hqVbn6gIz+rpTqUCkMZSKYm94Y1v0WXlHSDQBsXyY+tchWQgGVsw==
|
||||
vscode-test@^0.4.1:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8"
|
||||
integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w==
|
||||
dependencies:
|
||||
http-proxy-agent "^2.1.0"
|
||||
https-proxy-agent "^2.2.4"
|
||||
rimraf "^2.6.3"
|
||||
https-proxy-agent "^2.2.1"
|
||||
|
||||
vscode-uri@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542"
|
||||
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:
|
||||
version "1.3.0"
|
||||
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) {
|
||||
|
||||
let toDispose = context.subscriptions;
|
||||
const toDispose = context.subscriptions;
|
||||
|
||||
let rangeFormatting: Disposable | undefined = undefined;
|
||||
|
||||
let packageInfo = getPackageInfo(context);
|
||||
const packageInfo = getPackageInfo(context);
|
||||
telemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||
|
||||
let serverMain = readJSONFile(context.asAbsolutePath('./server/package.json')).main;
|
||||
let serverModule = context.asAbsolutePath(path.join('server', serverMain));
|
||||
const serverMain = readJSONFile(context.asAbsolutePath('./server/package.json')).main;
|
||||
const serverModule = context.asAbsolutePath(path.join('server', serverMain));
|
||||
|
||||
// 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
|
||||
// Otherwise the run options are used
|
||||
let serverOptions: ServerOptions = {
|
||||
const serverOptions: ServerOptions = {
|
||||
run: { module: serverModule, transport: TransportKind.ipc },
|
||||
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',
|
||||
name: localize('json.resolveError', "JSON: Schema Resolution Error"),
|
||||
alignment: StatusBarAlignment.Right,
|
||||
@@ -109,10 +109,10 @@ export function activate(context: ExtensionContext) {
|
||||
schemaResolutionErrorStatusBarItem.text = '$(alert)';
|
||||
toDispose.push(schemaResolutionErrorStatusBarItem);
|
||||
|
||||
let fileSchemaErrors = new Map<string, string>();
|
||||
const fileSchemaErrors = new Map<string, string>();
|
||||
|
||||
// Options to control the language client
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
const clientOptions: LanguageClientOptions = {
|
||||
// Register the server for json documents
|
||||
documentSelector,
|
||||
initializationOptions: {
|
||||
@@ -172,17 +172,17 @@ export function activate(context: ExtensionContext) {
|
||||
};
|
||||
|
||||
// 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();
|
||||
|
||||
let disposable = client.start();
|
||||
const disposable = client.start();
|
||||
toDispose.push(disposable);
|
||||
client.onReady().then(() => {
|
||||
const schemaDocuments: { [uri: string]: boolean } = {};
|
||||
|
||||
// handle content request
|
||||
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
|
||||
let uri = Uri.parse(uriPath);
|
||||
const uri = Uri.parse(uriPath);
|
||||
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
|
||||
return workspace.openTextDocument(uri).then(doc => {
|
||||
schemaDocuments[uri.toString()] = true;
|
||||
@@ -212,7 +212,7 @@ export function activate(context: ExtensionContext) {
|
||||
}
|
||||
});
|
||||
|
||||
let handleContentChange = (uriString: string) => {
|
||||
const handleContentChange = (uriString: string) => {
|
||||
if (schemaDocuments[uriString]) {
|
||||
client.sendNotification(SchemaContentChangeNotification.type, uriString);
|
||||
return true;
|
||||
@@ -220,7 +220,7 @@ export function activate(context: ExtensionContext) {
|
||||
return false;
|
||||
};
|
||||
|
||||
let handleActiveEditorChange = (activeEditor?: TextEditor) => {
|
||||
const handleActiveEditorChange = (activeEditor?: TextEditor) => {
|
||||
if (!activeEditor) {
|
||||
return;
|
||||
}
|
||||
@@ -244,7 +244,7 @@ export function activate(context: ExtensionContext) {
|
||||
}));
|
||||
toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange));
|
||||
|
||||
let handleRetryResolveSchemaCommand = () => {
|
||||
const handleRetryResolveSchemaCommand = () => {
|
||||
if (window.activeTextEditor) {
|
||||
schemaResolutionErrorStatusBarItem.text = '$(watch)';
|
||||
const activeDocUri = window.activeTextEditor.document.uri.toString();
|
||||
@@ -282,7 +282,7 @@ export function activate(context: ExtensionContext) {
|
||||
|
||||
});
|
||||
|
||||
let languageConfiguration: LanguageConfiguration = {
|
||||
const languageConfiguration: LanguageConfiguration = {
|
||||
wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/,
|
||||
indentationRules: {
|
||||
increaseIndentPattern: /({+(?=([^"]*"[^"]*")*[^"}]*$))|(\[+(?=([^"]*"[^"]*")*[^"\]]*$))/,
|
||||
@@ -300,7 +300,7 @@ export function activate(context: ExtensionContext) {
|
||||
} else if (formatEnabled && !rangeFormatting) {
|
||||
rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, {
|
||||
provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult<TextEdit[]> {
|
||||
let params: DocumentRangeFormattingParams = {
|
||||
const params: DocumentRangeFormattingParams = {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||
range: client.code2ProtocolConverter.asRange(range),
|
||||
options: client.code2ProtocolConverter.asFormattingOptions(options)
|
||||
@@ -325,11 +325,11 @@ export function deactivate(): Promise<any> {
|
||||
}
|
||||
|
||||
function getSchemaAssociation(_context: ExtensionContext): ISchemaAssociations {
|
||||
let associations: ISchemaAssociations = {};
|
||||
const associations: ISchemaAssociations = {};
|
||||
extensions.all.forEach(extension => {
|
||||
let packageJSON = extension.packageJSON;
|
||||
const packageJSON = extension.packageJSON;
|
||||
if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation) {
|
||||
let jsonValidation = packageJSON.contributes.jsonValidation;
|
||||
const jsonValidation = packageJSON.contributes.jsonValidation;
|
||||
if (Array.isArray(jsonValidation)) {
|
||||
jsonValidation.forEach(jv => {
|
||||
let { fileMatch, url } = jv;
|
||||
@@ -359,11 +359,11 @@ function getSchemaAssociation(_context: ExtensionContext): ISchemaAssociations {
|
||||
}
|
||||
|
||||
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: {
|
||||
proxy: httpSettings.get('proxy'),
|
||||
proxyStrictSSL: httpSettings.get('proxyStrictSSL')
|
||||
@@ -373,10 +373,18 @@ function getSettings(): Settings {
|
||||
resultLimit
|
||||
}
|
||||
};
|
||||
let schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null);
|
||||
let collectSchemaSettings = (schemaSettings: JSONSchemaSettings[], rootPath?: string, fileMatchPrefix?: string) => {
|
||||
for (let setting of schemaSettings) {
|
||||
let url = getSchemaId(setting, rootPath);
|
||||
const schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null);
|
||||
const collectSchemaSettings = (schemaSettings: JSONSchemaSettings[], folderUri?: Uri, isMultiRoot?: boolean) => {
|
||||
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
@@ -385,69 +393,78 @@ function getSettings(): Settings {
|
||||
schemaSetting = schemaSettingsById[url] = { url, fileMatch: [] };
|
||||
settings.json!.schemas!.push(schemaSetting);
|
||||
}
|
||||
let fileMatches = setting.fileMatch;
|
||||
let resultingFileMatches = schemaSetting.fileMatch!;
|
||||
const fileMatches = setting.fileMatch;
|
||||
if (Array.isArray(fileMatches)) {
|
||||
if (fileMatchPrefix) {
|
||||
for (let fileMatch of fileMatches) {
|
||||
if (fileMatch[0] === '/') {
|
||||
resultingFileMatches.push(fileMatchPrefix + fileMatch);
|
||||
resultingFileMatches.push(fileMatchPrefix + '/*' + fileMatch);
|
||||
} else {
|
||||
resultingFileMatches.push(fileMatchPrefix + '/' + fileMatch);
|
||||
resultingFileMatches.push(fileMatchPrefix + '/*/' + fileMatch);
|
||||
}
|
||||
const resultingFileMatches = schemaSetting.fileMatch || [];
|
||||
schemaSetting.fileMatch = resultingFileMatches;
|
||||
const addMatch = (pattern: string) => { // filter duplicates
|
||||
if (resultingFileMatches.indexOf(pattern) === -1) {
|
||||
resultingFileMatches.push(pattern);
|
||||
}
|
||||
};
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const folders = workspace.workspaceFolders;
|
||||
|
||||
// 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)) {
|
||||
collectSchemaSettings(globalSettings, workspace.rootPath);
|
||||
if (!folders) {
|
||||
collectSchemaSettings(globalSettings);
|
||||
}
|
||||
}
|
||||
let folders = workspace.workspaceFolders;
|
||||
if (folders) {
|
||||
for (let folder of folders) {
|
||||
let folderUri = folder.uri;
|
||||
const isMultiRoot = folders.length > 1;
|
||||
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)) {
|
||||
let folderPath = folderUri.toString();
|
||||
if (folderPath[folderPath.length - 1] === '/') {
|
||||
folderPath = folderPath.substr(0, folderPath.length - 1);
|
||||
}
|
||||
collectSchemaSettings(folderSchemas, folderUri.fsPath, folderPath);
|
||||
collectSchemaSettings(folderSchemas, folderUri, isMultiRoot);
|
||||
}
|
||||
if (Array.isArray(globalSettings)) {
|
||||
collectSchemaSettings(globalSettings, folderUri, isMultiRoot);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
function getSchemaId(schema: JSONSchemaSettings, rootPath?: string) {
|
||||
function getSchemaId(schema: JSONSchemaSettings, folderUri?: Uri) {
|
||||
let url = schema.url;
|
||||
if (!url) {
|
||||
if (schema.schema) {
|
||||
url = schema.schema.id || `vscode://schemas/custom/${encodeURIComponent(hash(schema.schema).toString(16))}`;
|
||||
}
|
||||
} else if (rootPath && (url[0] === '.' || url[0] === '/')) {
|
||||
url = Uri.file(path.normalize(path.join(rootPath, url))).toString();
|
||||
} else if (folderUri && (url[0] === '.' || url[0] === '/')) {
|
||||
url = folderUri.with({ path: path.posix.join(folderUri.path, url) }).toString();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
function getPackageInfo(context: ExtensionContext): IPackageInfo | undefined {
|
||||
let extensionPackage = readJSONFile(context.asAbsolutePath('./package.json'));
|
||||
const extensionPackage = readJSONFile(context.asAbsolutePath('./package.json'));
|
||||
if (extensionPackage) {
|
||||
return {
|
||||
name: extensionPackage.name,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vscode-json-languageserver",
|
||||
"description": "JSON language server",
|
||||
"version": "1.2.2",
|
||||
"version": "1.2.3",
|
||||
"author": "Microsoft Corporation",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
@@ -21,6 +21,7 @@ interface IToken {
|
||||
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
|
||||
expiresAt?: number; // UNIX epoch time at which token will expire
|
||||
refreshToken: string;
|
||||
|
||||
accountName: string;
|
||||
@@ -183,12 +184,33 @@ export class AzureActiveDirectoryService {
|
||||
private convertToSession(token: IToken): vscode.AuthenticationSession {
|
||||
return {
|
||||
id: token.sessionId,
|
||||
accessToken: () => !token.accessToken ? Promise.reject('Unavailable due to network problems') : Promise.resolve(token.accessToken),
|
||||
accessToken: () => this.resolveAccessToken(token),
|
||||
accountName: token.accountName,
|
||||
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 {
|
||||
try {
|
||||
return JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64').toString());
|
||||
@@ -252,9 +274,9 @@ export class AzureActiveDirectoryService {
|
||||
res.writeHead(302, { Location: '/' });
|
||||
res.end();
|
||||
} catch (err) {
|
||||
Logger.error(err.message);
|
||||
res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` });
|
||||
res.end();
|
||||
throw new Error(err.message);
|
||||
}
|
||||
} catch (e) {
|
||||
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') {
|
||||
await this.loginWithoutLocalServer(scope);
|
||||
}
|
||||
throw new Error(e.message);
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
server.close();
|
||||
@@ -374,6 +397,7 @@ export class AzureActiveDirectoryService {
|
||||
const claims = this.getTokenClaims(json.access_token);
|
||||
return {
|
||||
expiresIn: json.expires_in,
|
||||
expiresAt: Date.now() + json.expires_in * 1000,
|
||||
accessToken: json.access_token,
|
||||
refreshToken: json.refresh_token,
|
||||
scope,
|
||||
|
||||
@@ -22,7 +22,6 @@ export async function activate(_: vscode.ExtensionContext) {
|
||||
await loginService.login(scopes.sort().join(' '));
|
||||
return loginService.sessions[0]!;
|
||||
} catch (e) {
|
||||
vscode.window.showErrorMessage(`Logging in failed: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ function code() {
|
||||
export VSCODE_LOGS=
|
||||
|
||||
# Launch Code
|
||||
exec "$CODE" . "$@"
|
||||
exec "$CODE" . --no-sandbox "$@"
|
||||
}
|
||||
|
||||
function code-wsl()
|
||||
|
||||
+4
-1
@@ -89,7 +89,7 @@ app.once('ready', function () {
|
||||
traceOptions: args['trace-options'] || 'record-until-full,enable-sampling'
|
||||
};
|
||||
|
||||
contentTracing.startRecording(traceOptions, () => onReady());
|
||||
contentTracing.startRecording(traceOptions).finally(() => onReady());
|
||||
} else {
|
||||
onReady();
|
||||
}
|
||||
@@ -169,6 +169,9 @@ function configureCommandlineSwitchesSync(cliArgs) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ export class MainThreadQueryEditor extends Disposable implements MainThreadQuery
|
||||
public $connect(fileUri: string, connectionId: string): Thenable<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
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 options: IConnectionCompletionOptions = {
|
||||
@@ -76,7 +76,7 @@ export class MainThreadQueryEditor extends Disposable implements MainThreadQuery
|
||||
public $connectWithProfile(fileUri: string, connection: azdata.connection.ConnectionProfile): Thenable<void> {
|
||||
return new Promise<void>(async (resolve, reject) => {
|
||||
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;
|
||||
|
||||
@@ -97,7 +97,7 @@ export class MainThreadQueryEditor extends Disposable implements MainThreadQuery
|
||||
}
|
||||
|
||||
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) {
|
||||
let editor = filteredEditors[0];
|
||||
if (editor instanceof QueryEditor) {
|
||||
@@ -119,7 +119,7 @@ export class MainThreadQueryEditor extends Disposable implements MainThreadQuery
|
||||
|
||||
public $createQueryTab(fileUri: string, title: string, componentId: string): void {
|
||||
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;
|
||||
|
||||
@@ -114,7 +114,7 @@ export class EditDataInput extends EditorInput implements IConnectableInput {
|
||||
public save(): Promise<IEditorInput | undefined> { return Promise.resolve(undefined); }
|
||||
public getTypeId(): string { return EditDataInput.ID; }
|
||||
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 setupComplete() { this._setup = true; }
|
||||
public get queryString(): string {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { EditorInput } from 'vs/workbench/common/editor';
|
||||
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
|
||||
@@ -102,4 +103,8 @@ export class EditDataResultsInput extends EditorInput {
|
||||
get uri(): string {
|
||||
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)
|
||||
// so this might be able to be undone when that happens
|
||||
if (!model.getModel(this.getResource())) {
|
||||
model.createModel('', modeService.create('dashboard'), this.getResource());
|
||||
if (!model.getModel(this.resource)) {
|
||||
model.createModel('', modeService.create('dashboard'), this.resource);
|
||||
}
|
||||
this._initializedPromise = _connectionService.connectIfNotConnected(_connectionProfile, 'dashboard').then(
|
||||
u => {
|
||||
@@ -72,7 +72,7 @@ export class DashboardInput extends EditorInput {
|
||||
return DashboardInput.ID;
|
||||
}
|
||||
|
||||
public getResource(): URI {
|
||||
public get resource(): URI {
|
||||
return URI.from({
|
||||
scheme: 'dashboard',
|
||||
path: 'dashboard'
|
||||
|
||||
@@ -287,4 +287,8 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
|
||||
super.dispose();
|
||||
this._profilerService.disconnectSession(this.id);
|
||||
}
|
||||
|
||||
get resource(): URI | undefined {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,11 +89,11 @@ export class ModelViewInput extends EditorInput {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
public getResource(): URI {
|
||||
public get resource(): URI | undefined {
|
||||
if (this._options.resourceName) {
|
||||
return URI.from({ scheme: ModelViewInput.Scheme, path: this._options.resourceName });
|
||||
}
|
||||
return super.getResource();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public get container(): HTMLElement {
|
||||
|
||||
@@ -196,7 +196,7 @@ export class QueryTextEditor extends BaseTextEditor {
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export function getCurrentGlobalConnection(objectExplorerService: IObjectExplore
|
||||
|
||||
let activeInput = workbenchEditorService.activeEditor;
|
||||
if (activeInput) {
|
||||
connection = connectionManagementService.getConnectionProfile(activeInput.getResource().toString());
|
||||
connection = connectionManagementService.getConnectionProfile(activeInput.resource.toString());
|
||||
}
|
||||
|
||||
return connection;
|
||||
|
||||
@@ -166,7 +166,7 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab
|
||||
}
|
||||
|
||||
// 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 results(): QueryResultsInput { return this._results; }
|
||||
// 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
|
||||
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 {
|
||||
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 { GridPanelState } from 'sql/workbench/common/editor/query/gridPanelState';
|
||||
import { QueryModelViewState } from 'sql/workbench/common/editor/query/modelViewState';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export class ResultsViewState {
|
||||
public readonly gridPanelState: GridPanelState = new GridPanelState();
|
||||
@@ -89,4 +90,8 @@ export class QueryResultsInput extends EditorInput {
|
||||
get uri(): string {
|
||||
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
|
||||
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) {
|
||||
let options: IConnectionCompletionOptions = {
|
||||
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
|
||||
language = this.modeService.getModeIdByFilepathOrFirstLine(editor.getResource());
|
||||
language = this.modeService.getModeIdByFilepathOrFirstLine(editor.resource);
|
||||
}
|
||||
|
||||
if (!language) {
|
||||
// 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) {
|
||||
|
||||
@@ -211,7 +211,7 @@ export abstract class NotebookInput extends EditorInput {
|
||||
private _notebookFindModel: NotebookFindModel;
|
||||
|
||||
constructor(private _title: string,
|
||||
private resource: URI,
|
||||
private _resource: URI,
|
||||
private _textInput: TextInput,
|
||||
@ITextModelService private textModelService: ITextModelService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@@ -219,7 +219,6 @@ export abstract class NotebookInput extends EditorInput {
|
||||
@IExtensionService private extensionService: IExtensionService
|
||||
) {
|
||||
super();
|
||||
this.resource = resource;
|
||||
this._standardKernels = [];
|
||||
this._providersLoaded = this.assignProviders();
|
||||
this._notebookEditorOpenedTimestamp = Date.now();
|
||||
@@ -299,8 +298,8 @@ export abstract class NotebookInput extends EditorInput {
|
||||
|
||||
private async setTrustForNewEditor(newInput: IEditorInput | undefined): Promise<void> {
|
||||
let isTrusted = this._model.getNotebookModel().trustedMode;
|
||||
if (isTrusted && newInput && newInput.getResource() !== this.getResource()) {
|
||||
await this.notebookService.serializeNotebookStateChange(newInput.getResource(), NotebookChangeType.Saved, undefined, true);
|
||||
if (isTrusted && newInput && newInput.resource !== this.resource) {
|
||||
await this.notebookService.serializeNotebookStateChange(newInput.resource, NotebookChangeType.Saved, undefined, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,8 +336,8 @@ export abstract class NotebookInput extends EditorInput {
|
||||
|
||||
public abstract getTypeId(): string;
|
||||
|
||||
getResource(): URI {
|
||||
return this.resource;
|
||||
get resource(): URI {
|
||||
return this._resource;
|
||||
}
|
||||
|
||||
public get untitledEditorModel(): IUntitledTextEditorModel {
|
||||
|
||||
@@ -260,7 +260,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
|
||||
let editors = this.editorService.visibleControls;
|
||||
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
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ export class NotebookEditorInputAssociation implements ILanguageAssociation {
|
||||
|
||||
convertInput(activeEditor: IEditorInput): NotebookInput {
|
||||
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) {
|
||||
return this.instantiationService.createInstance(UntitledNotebookInput, activeEditor.getName(), activeEditor.getResource(), activeEditor);
|
||||
return this.instantiationService.createInstance(UntitledNotebookInput, activeEditor.getName(), activeEditor.resource, activeEditor);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
@@ -48,7 +48,7 @@ export class FileNoteBookEditorInputFactory implements IEditorInputFactory {
|
||||
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): FileNotebookInput | undefined {
|
||||
const factory = editorInputFactoryRegistry.getEditorInputFactory(FILE_EDITOR_INPUT_ID);
|
||||
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
|
||||
@@ -68,7 +68,7 @@ export class UntitledNoteBookEditorInputFactory implements IEditorInputFactory {
|
||||
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledNotebookInput | undefined {
|
||||
const factory = editorInputFactoryRegistry.getEditorInputFactory(UntitledTextEditorInput.ID);
|
||||
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
|
||||
|
||||
@@ -115,7 +115,7 @@ suite('Notebook Input', function (): void {
|
||||
assert.strictEqual(untitledNotebookInput.untitledEditorModel, testModel);
|
||||
|
||||
// getResource
|
||||
assert.strictEqual(untitledNotebookInput.getResource(), untitledUri);
|
||||
assert.strictEqual(untitledNotebookInput.resource, untitledUri);
|
||||
|
||||
// Standard kernels
|
||||
let testKernels = [{
|
||||
|
||||
@@ -96,12 +96,12 @@ export class SqlFlavorStatusbarItem extends Disposable implements IWorkbenchCont
|
||||
}
|
||||
|
||||
private _onEditorClosed(event: IEditorCloseEvent): void {
|
||||
let uri = event.editor.getResource().toString();
|
||||
let uri = event.editor.resource.toString();
|
||||
if (uri && uri in this._sqlStatusEditors) {
|
||||
// If active editor is being closed, hide the query status.
|
||||
let activeEditor = this.editorService.activeControl;
|
||||
if (activeEditor) {
|
||||
let currentUri = activeEditor.input.getResource().toString();
|
||||
let currentUri = activeEditor.input.resource.toString();
|
||||
if (uri === currentUri) {
|
||||
this.hide();
|
||||
}
|
||||
@@ -114,7 +114,7 @@ export class SqlFlavorStatusbarItem extends Disposable implements IWorkbenchCont
|
||||
private _onEditorsChanged(): void {
|
||||
let activeEditor = this.editorService.activeControl;
|
||||
if (activeEditor) {
|
||||
let uri = activeEditor.input.getResource().toString();
|
||||
let uri = activeEditor.input.resource.toString();
|
||||
|
||||
// Show active editor's language flavor status
|
||||
if (uri) {
|
||||
@@ -145,7 +145,7 @@ export class SqlFlavorStatusbarItem extends Disposable implements IWorkbenchCont
|
||||
private _showStatus(uri: string): void {
|
||||
let activeEditor = this.editorService.activeControl;
|
||||
if (activeEditor) {
|
||||
let currentUri = activeEditor.input.getResource().toString();
|
||||
let currentUri = activeEditor.input.resource.toString();
|
||||
if (uri === currentUri) {
|
||||
let flavor: SqlProviderEntry = this._sqlStatusEditors[uri];
|
||||
if (flavor) {
|
||||
@@ -186,7 +186,7 @@ export class ChangeFlavorAction extends Action {
|
||||
|
||||
public run(): Promise<any> {
|
||||
let activeEditor = this._editorService.activeControl;
|
||||
let currentUri = activeEditor?.input.getResource().toString();
|
||||
let currentUri = activeEditor?.input.resource.toString();
|
||||
if (this._connectionManagementService.isConnected(currentUri)) {
|
||||
let currentProvider = this._connectionManagementService.getProviderIdFromUri(currentUri);
|
||||
return this._showMessage(Severity.Info, nls.localize('alreadyConnected',
|
||||
@@ -226,4 +226,3 @@ export class ChangeFlavorAction extends Action {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ export class QueryEditor extends BaseEditor {
|
||||
this.queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService);
|
||||
|
||||
// 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 {
|
||||
@@ -317,7 +317,7 @@ export class QueryEditor extends BaseEditor {
|
||||
this.inputDisposables.add(this.input.state.onChange(c => this.updateState(c)));
|
||||
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) {
|
||||
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
|
||||
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) { }
|
||||
|
||||
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;
|
||||
if (activeEditor instanceof FileEditorInput) {
|
||||
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 fileEditorInput = factory.deserialize(instantiationService, serializedEditorInput) as FileEditorInput;
|
||||
// only successfully deserilize the file if the resource actually exists
|
||||
if (this.fileService.exists(fileEditorInput.getResource())) {
|
||||
const queryResultsInput = instantiationService.createInstance(QueryResultsInput, fileEditorInput.getResource().toString());
|
||||
if (this.fileService.exists(fileEditorInput.resource)) {
|
||||
const queryResultsInput = instantiationService.createInstance(QueryResultsInput, fileEditorInput.resource.toString());
|
||||
return instantiationService.createInstance(FileQueryEditorInput, '', fileEditorInput, queryResultsInput);
|
||||
} else {
|
||||
fileEditorInput.dispose();
|
||||
@@ -110,7 +110,7 @@ export class UntitledQueryEditorInputFactory implements IEditorInputFactory {
|
||||
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledQueryEditorInput | undefined {
|
||||
const factory = editorInputFactoryRegistry.getEditorInputFactory(UntitledTextEditorInput.ID);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export class QueryPlanConverter implements ILanguageAssociation {
|
||||
constructor(@IInstantiationService private instantiationService: IInstantiationService) { }
|
||||
|
||||
convertInput(activeEditor: IEditorInput): QueryPlanInput {
|
||||
return this.instantiationService.createInstance(QueryPlanInput, activeEditor.getResource());
|
||||
return this.instantiationService.createInstance(QueryPlanInput, activeEditor.resource);
|
||||
}
|
||||
|
||||
createBase(activeEditor: QueryPlanInput): IEditorInput {
|
||||
@@ -83,4 +83,8 @@ export class QueryPlanInput extends EditorInput {
|
||||
public get uniqueSelector(): string {
|
||||
return this._uniqueSelector;
|
||||
}
|
||||
|
||||
get resource(): URI | undefined {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"strictBindCallApply": true,
|
||||
"strictNullChecks": false,
|
||||
"strictPropertyInitialization": false,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
|
||||
@@ -251,14 +251,14 @@ export interface IGlobOptions {
|
||||
}
|
||||
|
||||
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[];
|
||||
patterns?: string[];
|
||||
allBasenames?: string[];
|
||||
allPaths?: string[];
|
||||
}
|
||||
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;
|
||||
allBasenames?: string[];
|
||||
allPaths?: string[];
|
||||
@@ -374,7 +374,7 @@ function trivia3(pattern: string, options: IGlobOptions): ParsedStringPattern {
|
||||
if (n === 1) {
|
||||
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++) {
|
||||
if ((<ParsedStringPattern>parsedPatterns[i])(path, basename)) {
|
||||
return pattern;
|
||||
@@ -409,7 +409,7 @@ function trivia4and5(path: string, pattern: string, matchPathEnds: boolean): Par
|
||||
function toRegExp(pattern: string): ParsedStringPattern {
|
||||
try {
|
||||
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!
|
||||
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) {
|
||||
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);
|
||||
};
|
||||
if (parsedPattern.allBasenames) {
|
||||
@@ -540,7 +540,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse
|
||||
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++) {
|
||||
// Pattern matches path
|
||||
const result = (<ParsedStringPattern>parsedPatterns[i])(path, basename);
|
||||
@@ -565,7 +565,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse
|
||||
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;
|
||||
|
||||
for (let i = 0, n = parsedPatterns.length; i < n; i++) {
|
||||
@@ -620,12 +620,12 @@ function parseExpressionPattern(pattern: string, value: boolean | SiblingClause,
|
||||
if (value) {
|
||||
const when = (<SiblingClause>value).when;
|
||||
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)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const clausePattern = when.replace('$(basename)', name);
|
||||
const clausePattern = when.replace('$(basename)', name!);
|
||||
const matched = hasSibling(clausePattern);
|
||||
return isThenable(matched) ?
|
||||
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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
|
||||
export interface ILink {
|
||||
readonly label: string;
|
||||
readonly href: string;
|
||||
@@ -10,7 +12,16 @@ export interface 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;
|
||||
|
||||
@@ -40,5 +51,5 @@ export function parseLinkedText(text: string): LinkedText {
|
||||
result.push(text.substring(index));
|
||||
}
|
||||
|
||||
return result;
|
||||
return new LinkedText(result);
|
||||
}
|
||||
+13
-13
@@ -4,50 +4,50 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { parseLinkedText } from 'vs/base/browser/linkedText';
|
||||
import { parseLinkedText } from 'vs/base/common/linkedText';
|
||||
|
||||
suite('LinkedText', () => {
|
||||
test('parses correctly', () => {
|
||||
assert.deepEqual(parseLinkedText(''), []);
|
||||
assert.deepEqual(parseLinkedText('hello'), ['hello']);
|
||||
assert.deepEqual(parseLinkedText('hello there'), ['hello there']);
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href).'), [
|
||||
assert.deepEqual(parseLinkedText('').nodes, []);
|
||||
assert.deepEqual(parseLinkedText('hello').nodes, ['hello']);
|
||||
assert.deepEqual(parseLinkedText('hello there').nodes, ['hello there']);
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href).').nodes, [
|
||||
'Some message with ',
|
||||
{ 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 ',
|
||||
{ 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).'
|
||||
]);
|
||||
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 ',
|
||||
{ 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:).'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('Some message with [a command](command:foobar).'), [
|
||||
assert.deepEqual(parseLinkedText('Some message with [a command](command:foobar).').nodes, [
|
||||
'Some message with ',
|
||||
{ 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:).'
|
||||
]);
|
||||
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 ',
|
||||
{ label: 'one', href: 'command:foo', title: 'nice' },
|
||||
' and link ',
|
||||
{ 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',
|
||||
{ label: 'one', href: 'command:foo', title: 'nice' },
|
||||
'\nand link ',
|
||||
@@ -17,7 +17,7 @@ import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtension
|
||||
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
|
||||
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 { RequestService } from 'vs/platform/request/browser/requestService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
@@ -26,7 +26,7 @@ import { IStateService } from 'vs/platform/state/node/state';
|
||||
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { EnvironmentService, xdgRuntimeDir } from 'vs/platform/environment/node/environmentService';
|
||||
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 { RequestMainService } from 'vs/platform/request/electron-main/requestMainService';
|
||||
import * as fs from 'fs';
|
||||
|
||||
@@ -24,7 +24,7 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { RequestService } from 'vs/platform/request/node/requestService';
|
||||
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 { mkdirp, writeFile } from 'vs/base/node/pfs';
|
||||
import { getBaseLabel } from 'vs/base/common/labels';
|
||||
|
||||
@@ -1614,7 +1614,7 @@ export namespace CoreEditingCommands {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'deleteLeft',
|
||||
precondition: EditorContextKeys.writable,
|
||||
precondition: undefined,
|
||||
kbOpts: {
|
||||
weight: CORE_WEIGHT,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
@@ -1639,7 +1639,7 @@ export namespace CoreEditingCommands {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'deleteRight',
|
||||
precondition: EditorContextKeys.writable,
|
||||
precondition: undefined,
|
||||
kbOpts: {
|
||||
weight: CORE_WEIGHT,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
|
||||
@@ -149,6 +149,10 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe
|
||||
return result;
|
||||
}
|
||||
|
||||
const enum Constants {
|
||||
SPAN_MODULO_LIMIT = 16384
|
||||
}
|
||||
|
||||
function renderLine(lineContent: string, initialVisibleColumn: number, tabSize: number, width: number, sb: IStringBuilder): [number[], number[]] {
|
||||
sb.appendASCIIString('<div style="width:');
|
||||
sb.appendASCIIString(String(width));
|
||||
@@ -164,7 +168,11 @@ function renderLine(lineContent: string, initialVisibleColumn: number, tabSize:
|
||||
let visibleColumns: number[] = [];
|
||||
let nextCharCode = (0 < len ? lineContent.charCodeAt(0) : CharCode.Null);
|
||||
|
||||
sb.appendASCIIString('<span>');
|
||||
for (let charIndex = 0; charIndex < len; charIndex++) {
|
||||
if (charIndex !== 0 && charIndex % Constants.SPAN_MODULO_LIMIT === 0) {
|
||||
sb.appendASCIIString('</span><span>');
|
||||
}
|
||||
charOffsets[charIndex] = charOffset;
|
||||
visibleColumns[charIndex] = visibleColumn;
|
||||
const charCode = nextCharCode;
|
||||
@@ -227,6 +235,7 @@ function renderLine(lineContent: string, initialVisibleColumn: number, tabSize:
|
||||
charOffset += producedCharacters;
|
||||
visibleColumn += charWidth;
|
||||
}
|
||||
sb.appendASCIIString('</span>');
|
||||
|
||||
charOffsets[lineContent.length] = charOffset;
|
||||
visibleColumns[lineContent.length] = visibleColumn;
|
||||
@@ -240,10 +249,15 @@ function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent:
|
||||
if (lineContent.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
const textContentNode = lineDomNode.firstChild!;
|
||||
const spans = <HTMLSpanElement[]>Array.prototype.slice.call(lineDomNode.children, 0);
|
||||
|
||||
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) {
|
||||
return null;
|
||||
@@ -255,13 +269,13 @@ function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent:
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
lowRects = lowRects || readClientRect(range, textContentNode, charOffsets[low], charOffsets[low + 1]);
|
||||
highRects = highRects || readClientRect(range, textContentNode, charOffsets[high], charOffsets[high + 1]);
|
||||
lowRects = lowRects || readClientRect(range, spans, charOffsets[low], charOffsets[low + 1]);
|
||||
highRects = highRects || readClientRect(range, spans, charOffsets[high], charOffsets[high + 1]);
|
||||
|
||||
if (Math.abs(lowRects[0].top - highRects[0].top) <= 0.1) {
|
||||
// same line
|
||||
@@ -276,13 +290,13 @@ function discoverBreaks(range: Range, textContentNode: Node, charOffsets: number
|
||||
}
|
||||
|
||||
const mid = low + ((high - low) / 2) | 0;
|
||||
const midRects = readClientRect(range, textContentNode, charOffsets[mid], charOffsets[mid + 1]);
|
||||
discoverBreaks(range, textContentNode, charOffsets, low, lowRects, mid, midRects, result);
|
||||
discoverBreaks(range, textContentNode, charOffsets, mid, midRects, high, highRects, result);
|
||||
const midRects = readClientRect(range, spans, charOffsets[mid], charOffsets[mid + 1]);
|
||||
discoverBreaks(range, spans, charOffsets, low, lowRects, mid, midRects, result);
|
||||
discoverBreaks(range, spans, charOffsets, mid, midRects, high, highRects, result);
|
||||
}
|
||||
|
||||
function readClientRect(range: Range, textContentNode: Node, startOffset: number, endOffset: number): ClientRectList | DOMRectList {
|
||||
range.setStart(textContentNode, startOffset);
|
||||
range.setEnd(textContentNode, endOffset);
|
||||
function readClientRect(range: Range, spans: HTMLSpanElement[], startOffset: number, endOffset: number): ClientRectList | DOMRectList {
|
||||
range.setStart(spans[(startOffset / Constants.SPAN_MODULO_LIMIT) | 0].firstChild!, startOffset % Constants.SPAN_MODULO_LIMIT);
|
||||
range.setEnd(spans[(endOffset / Constants.SPAN_MODULO_LIMIT) | 0].firstChild!, endOffset % Constants.SPAN_MODULO_LIMIT);
|
||||
return range.getClientRects();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,11 +34,13 @@ export class MinimapCharRenderer {
|
||||
color: RGBA8,
|
||||
backgroundColor: RGBA8,
|
||||
fontScale: number,
|
||||
useLighterFont: boolean
|
||||
useLighterFont: boolean,
|
||||
force1pxHeight: boolean
|
||||
): void {
|
||||
const charWidth = Constants.BASE_CHAR_WIDTH * 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');
|
||||
return;
|
||||
}
|
||||
@@ -60,7 +62,7 @@ export class MinimapCharRenderer {
|
||||
let sourceOffset = charIndex * charWidth * charHeight;
|
||||
|
||||
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;
|
||||
for (let x = 0; x < charWidth; x++) {
|
||||
const c = charData[sourceOffset++] / 255;
|
||||
@@ -80,11 +82,13 @@ export class MinimapCharRenderer {
|
||||
dy: number,
|
||||
color: RGBA8,
|
||||
backgroundColor: RGBA8,
|
||||
useLighterFont: boolean
|
||||
useLighterFont: boolean,
|
||||
force1pxHeight: boolean
|
||||
): void {
|
||||
const charWidth = Constants.BASE_CHAR_WIDTH * 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');
|
||||
return;
|
||||
}
|
||||
@@ -108,7 +112,7 @@ export class MinimapCharRenderer {
|
||||
const dest = target.data;
|
||||
|
||||
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;
|
||||
for (let x = 0; x < charWidth; x++) {
|
||||
dest[column++] = colorR;
|
||||
|
||||
@@ -276,7 +276,10 @@ export class DecorationsOverviewRuler extends ViewPart {
|
||||
return true;
|
||||
}
|
||||
public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean {
|
||||
return true;
|
||||
if (e.affectsOverviewRuler) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public onFlushed(e: viewEvents.ViewFlushedEvent): boolean {
|
||||
return true;
|
||||
|
||||
@@ -287,6 +287,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
|
||||
public options!: ComputedEditorOptions;
|
||||
|
||||
private _isDominatedByLongLines: boolean;
|
||||
private _maxLineNumber: number;
|
||||
private _lineNumbersDigitCount: number;
|
||||
|
||||
private _rawOptions: IEditorOptions;
|
||||
@@ -298,6 +299,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
|
||||
this.isSimpleWidget = isSimpleWidget;
|
||||
|
||||
this._isDominatedByLongLines = false;
|
||||
this._maxLineNumber = 1;
|
||||
this._lineNumbersDigitCount = 1;
|
||||
|
||||
this._rawOptions = deepCloneAndMigrateOptions(_options);
|
||||
@@ -347,6 +349,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
|
||||
fontInfo: this.readConfiguration(bareFontInfo),
|
||||
extraEditorClassName: partialEnv.extraEditorClassName,
|
||||
isDominatedByLongLines: this._isDominatedByLongLines,
|
||||
maxLineNumber: this._maxLineNumber,
|
||||
lineNumbersDigitCount: this._lineNumbersDigitCount,
|
||||
emptySelectionClipboard: partialEnv.emptySelectionClipboard,
|
||||
pixelRatio: partialEnv.pixelRatio,
|
||||
@@ -405,11 +408,11 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
|
||||
}
|
||||
|
||||
public setMaxLineNumber(maxLineNumber: number): void {
|
||||
let digitCount = CommonEditorConfiguration._digitCount(maxLineNumber);
|
||||
if (this._lineNumbersDigitCount === digitCount) {
|
||||
if (this._maxLineNumber === maxLineNumber) {
|
||||
return;
|
||||
}
|
||||
this._lineNumbersDigitCount = digitCount;
|
||||
this._maxLineNumber = maxLineNumber;
|
||||
this._lineNumbersDigitCount = CommonEditorConfiguration._digitCount(maxLineNumber);
|
||||
this._recomputeOptions();
|
||||
}
|
||||
|
||||
|
||||
@@ -678,6 +678,7 @@ export interface IEnvironmentalOptions {
|
||||
readonly fontInfo: FontInfo;
|
||||
readonly extraEditorClassName: string;
|
||||
readonly isDominatedByLongLines: boolean;
|
||||
readonly maxLineNumber: number;
|
||||
readonly lineNumbersDigitCount: number;
|
||||
readonly emptySelectionClipboard: boolean;
|
||||
readonly pixelRatio: number;
|
||||
@@ -1691,6 +1692,14 @@ export interface EditorLayoutInfo {
|
||||
* The width of the minimap
|
||||
*/
|
||||
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
|
||||
@@ -1724,6 +1733,7 @@ export interface EditorLayoutInfoComputerEnv {
|
||||
outerWidth: number;
|
||||
outerHeight: number;
|
||||
lineHeight: number;
|
||||
maxLineNumber: number;
|
||||
lineNumbersDigitCount: number;
|
||||
typicalHalfwidthCharacterWidth: number;
|
||||
maxDigitWidth: number;
|
||||
@@ -1747,6 +1757,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
outerWidth: env.outerWidth,
|
||||
outerHeight: env.outerHeight,
|
||||
lineHeight: env.fontInfo.lineHeight,
|
||||
maxLineNumber: env.maxLineNumber,
|
||||
lineNumbersDigitCount: env.lineNumbersDigitCount,
|
||||
typicalHalfwidthCharacterWidth: env.fontInfo.typicalHalfwidthCharacterWidth,
|
||||
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 {
|
||||
const outerWidth = env.outerWidth | 0;
|
||||
const outerHeight = env.outerHeight | 0;
|
||||
@@ -1766,12 +1791,14 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
const showGlyphMargin = options.get(EditorOption.glyphMargin);
|
||||
const showLineNumbers = (options.get(EditorOption.lineNumbers).renderType !== RenderLineNumbersType.Off);
|
||||
const lineNumbersMinChars = options.get(EditorOption.lineNumbersMinChars) | 0;
|
||||
const scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine);
|
||||
const minimap = options.get(EditorOption.minimap);
|
||||
const minimapEnabled = minimap.enabled;
|
||||
const minimapSide = minimap.side;
|
||||
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 minimapMode = minimap.mode;
|
||||
|
||||
const scrollbar = options.get(EditorOption.scrollbar);
|
||||
const verticalScrollbarWidth = scrollbar.verticalScrollbarSize | 0;
|
||||
@@ -1811,19 +1838,65 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
|
||||
const remainingWidth = outerWidth - glyphMarginWidth - lineNumbersWidth - lineDecorationsWidth;
|
||||
|
||||
const baseCharHeight = minimapRenderCharacters ? 2 : 3;
|
||||
let renderMinimap: RenderMinimap;
|
||||
let minimapLeft: 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;
|
||||
if (!minimapEnabled) {
|
||||
minimapLeft = 0;
|
||||
minimapWidth = 0;
|
||||
minimapCanvasInnerWidth = 0;
|
||||
minimapCanvasOuterWidth = 0;
|
||||
minimapLineHeight = 1;
|
||||
renderMinimap = RenderMinimap.None;
|
||||
contentWidth = remainingWidth;
|
||||
} else {
|
||||
// The minimapScale is also the pixel width of each character. Adjust
|
||||
// for the pixel ratio of the screen.
|
||||
const minimapCharWidth = minimapScale / pixelRatio;
|
||||
let minimapCharWidth = minimapScale / pixelRatio;
|
||||
let minimapWidthMultiplier: number = 1;
|
||||
|
||||
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;
|
||||
|
||||
// Given:
|
||||
@@ -1855,6 +1928,10 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
} else {
|
||||
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)
|
||||
@@ -1881,6 +1958,14 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
renderMinimap: renderMinimap,
|
||||
minimapLeft: minimapLeft,
|
||||
minimapWidth: minimapWidth,
|
||||
minimapHeightIsEditorHeight: minimapHeightIsEditorHeight,
|
||||
minimapIsSampling: minimapIsSampling,
|
||||
minimapScale: minimapScale,
|
||||
minimapLineHeight: minimapLineHeight,
|
||||
minimapCanvasInnerWidth: minimapCanvasInnerWidth,
|
||||
minimapCanvasInnerHeight: minimapCanvasInnerHeight,
|
||||
minimapCanvasOuterWidth: minimapCanvasOuterWidth,
|
||||
minimapCanvasOuterHeight: minimapCanvasOuterHeight,
|
||||
|
||||
viewportColumn: viewportColumn,
|
||||
|
||||
@@ -1981,6 +2066,11 @@ export interface IEditorMinimapOptions {
|
||||
* Defaults to 'right'.
|
||||
*/
|
||||
side?: 'right' | 'left';
|
||||
/**
|
||||
* Control the minimap rendering mode.
|
||||
* Defaults to 'actual'.
|
||||
*/
|
||||
mode?: 'actual' | 'cover' | 'contain';
|
||||
/**
|
||||
* Control the rendering of the minimap slider.
|
||||
* Defaults to 'mouseover'.
|
||||
@@ -1996,7 +2086,6 @@ export interface IEditorMinimapOptions {
|
||||
* Defaults to 120.
|
||||
*/
|
||||
maxColumn?: number;
|
||||
|
||||
/**
|
||||
* Relative size of the font in the minimap. Defaults to 1.
|
||||
*/
|
||||
@@ -2010,6 +2099,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
constructor() {
|
||||
const defaults: EditorMinimapOptions = {
|
||||
enabled: false, // {{SQL CARBON EDIT}} disable minimap by default
|
||||
mode: 'actual',
|
||||
side: 'right',
|
||||
showSlider: 'mouseover',
|
||||
renderCharacters: true,
|
||||
@@ -2024,6 +2114,17 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
default: defaults.enabled,
|
||||
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': {
|
||||
type: 'string',
|
||||
enum: ['left', 'right'],
|
||||
@@ -2052,7 +2153,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
type: 'number',
|
||||
default: defaults.maxColumn,
|
||||
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;
|
||||
return {
|
||||
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']),
|
||||
showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']),
|
||||
renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters),
|
||||
|
||||
@@ -1423,19 +1423,15 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
private _changeDecorations<T>(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T | null {
|
||||
let changeAccessor: model.IModelDecorationsChangeAccessor = {
|
||||
addDecoration: (range: IRange, options: model.IModelDecorationOptions): string => {
|
||||
this._onDidChangeDecorations.fire();
|
||||
return this._deltaDecorationsImpl(ownerId, [], [{ range: range, options: options }])[0];
|
||||
},
|
||||
changeDecoration: (id: string, newRange: IRange): void => {
|
||||
this._onDidChangeDecorations.fire();
|
||||
this._changeDecorationImpl(id, newRange);
|
||||
},
|
||||
changeDecorationOptions: (id: string, options: model.IModelDecorationOptions) => {
|
||||
this._onDidChangeDecorations.fire();
|
||||
this._changeDecorationOptionsImpl(id, _normalizeOptions(options));
|
||||
},
|
||||
removeDecoration: (id: string): void => {
|
||||
this._onDidChangeDecorations.fire();
|
||||
this._deltaDecorationsImpl(ownerId, [id], []);
|
||||
},
|
||||
deltaDecorations: (oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[]): string[] => {
|
||||
@@ -1443,7 +1439,6 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
// nothing to do
|
||||
return [];
|
||||
}
|
||||
this._onDidChangeDecorations.fire();
|
||||
return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
|
||||
}
|
||||
};
|
||||
@@ -1474,7 +1469,6 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
try {
|
||||
this._onDidChangeDecorations.beginDeferredEmit();
|
||||
this._onDidChangeDecorations.fire();
|
||||
return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
|
||||
} finally {
|
||||
this._onDidChangeDecorations.endDeferredEmit();
|
||||
@@ -1622,6 +1616,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
this._decorationsTree.delete(node);
|
||||
node.reset(this.getVersionId(), startOffset, endOffset, range);
|
||||
this._decorationsTree.insert(node);
|
||||
this._onDidChangeDecorations.checkAffectedAndFire(node.options);
|
||||
}
|
||||
|
||||
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 nodeIsInOverviewRuler = (options.overviewRuler && options.overviewRuler.color ? true : false);
|
||||
|
||||
this._onDidChangeDecorations.checkAffectedAndFire(node.options);
|
||||
this._onDidChangeDecorations.checkAffectedAndFire(options);
|
||||
|
||||
if (nodeWasInOverviewRuler !== nodeIsInOverviewRuler) {
|
||||
// Delete + Insert due to an overview ruler status change
|
||||
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)
|
||||
if (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.reset(versionId, startOffset, endOffset, range);
|
||||
node.setOptions(options);
|
||||
this._onDidChangeDecorations.checkAffectedAndFire(options);
|
||||
|
||||
this._decorationsTree.insert(node);
|
||||
|
||||
@@ -1713,7 +1713,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
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 {
|
||||
@@ -1725,16 +1725,34 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
for (let i = 0, len = tokens.length; i < len; 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++) {
|
||||
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({
|
||||
tokenizationSupportChanged: false,
|
||||
ranges: ranges
|
||||
});
|
||||
if (ranges.length > 0) {
|
||||
this._emitModelTokensChangedEvent({
|
||||
tokenizationSupportChanged: false,
|
||||
ranges: ranges
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public setSemanticTokens(tokens: MultilineTokens2[] | null): void {
|
||||
@@ -3083,11 +3101,15 @@ export class DidChangeDecorationsEmitter extends Disposable {
|
||||
|
||||
private _deferredCnt: number;
|
||||
private _shouldFire: boolean;
|
||||
private _affectsMinimap: boolean;
|
||||
private _affectsOverviewRuler: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._deferredCnt = 0;
|
||||
this._shouldFire = false;
|
||||
this._affectsMinimap = false;
|
||||
this._affectsOverviewRuler = false;
|
||||
}
|
||||
|
||||
public beginDeferredEmit(): void {
|
||||
@@ -3098,13 +3120,31 @@ export class DidChangeDecorationsEmitter extends Disposable {
|
||||
this._deferredCnt--;
|
||||
if (this._deferredCnt === 0) {
|
||||
if (this._shouldFire) {
|
||||
const event: IModelDecorationsChangedEvent = {
|
||||
affectsMinimap: this._affectsMinimap,
|
||||
affectsOverviewRuler: this._affectsOverviewRuler,
|
||||
};
|
||||
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 {
|
||||
this._affectsMinimap = true;
|
||||
this._affectsOverviewRuler = true;
|
||||
this._shouldFire = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ export interface IModelContentChangedEvent {
|
||||
* An event describing that model decorations have changed.
|
||||
*/
|
||||
export interface IModelDecorationsChangedEvent {
|
||||
readonly affectsMinimap: boolean;
|
||||
readonly affectsOverviewRuler: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -964,10 +964,35 @@ export class TokensStore {
|
||||
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);
|
||||
this._ensureLine(lineIndex);
|
||||
const oldTokens = this._lineTokens[lineIndex];
|
||||
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
|
||||
|
||||
@@ -274,6 +274,16 @@ export class LanguageConfigurationRegistryImpl {
|
||||
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 {
|
||||
let value = this._getRichEditSupport(languageId);
|
||||
if (!value) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ScrollType, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
|
||||
export const enum ViewEventType {
|
||||
ViewConfigurationChanged = 1,
|
||||
@@ -82,8 +83,17 @@ export class ViewDecorationsChangedEvent {
|
||||
|
||||
public readonly type = ViewEventType.ViewDecorationsChanged;
|
||||
|
||||
constructor() {
|
||||
// Nothing to do
|
||||
readonly affectsMinimap: boolean;
|
||||
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)) {
|
||||
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this.decorations.onLineMappingChanged();
|
||||
this.viewLayout.onFlushed(this.getLineCount());
|
||||
|
||||
@@ -185,7 +185,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
if (e.hasChanged(EditorOption.readOnly)) {
|
||||
// Must read again all decorations due to readOnly filtering
|
||||
this.decorations.reset();
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
}
|
||||
|
||||
eventsCollector.emit(new viewEvents.ViewConfigurationChangedEvent(e));
|
||||
@@ -291,7 +291,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
|
||||
if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) {
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this.decorations.onLineMappingChanged();
|
||||
}
|
||||
} finally {
|
||||
@@ -354,7 +354,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
const eventsCollector = this._beginEmit();
|
||||
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
} finally {
|
||||
this._endEmit();
|
||||
}
|
||||
@@ -365,7 +365,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
this.decorations.onModelDecorationsChanged();
|
||||
try {
|
||||
const eventsCollector = this._beginEmit();
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(e));
|
||||
} finally {
|
||||
this._endEmit();
|
||||
}
|
||||
@@ -379,7 +379,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
if (lineMappingChanged) {
|
||||
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this.decorations.onLineMappingChanged();
|
||||
this.viewLayout.onFlushed(this.getLineCount());
|
||||
this.viewLayout.onHeightMaybeChanged();
|
||||
|
||||
@@ -419,8 +419,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
}
|
||||
if (currentMatch) {
|
||||
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);
|
||||
if (lineContent) {
|
||||
const model = this._codeEditor.getModel();
|
||||
if (model && (currentMatch.startLineNumber <= model.getLineCount()) && (currentMatch.startLineNumber >= 1)) {
|
||||
const lineContent = model.getLineContent(currentMatch.startLineNumber);
|
||||
return `${lineContent}, ${ariaLabel}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -180,9 +180,11 @@ suite('SmartSelect', () => {
|
||||
// -- bracket selections
|
||||
|
||||
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 pos = model.getPositionAt(value.indexOf('|'));
|
||||
let pos = model.getPositionAt(index);
|
||||
let all = await provider.provideSelectionRanges(model, [pos], CancellationToken.None);
|
||||
let ranges = all![0];
|
||||
|
||||
@@ -197,18 +199,18 @@ suite('SmartSelect', () => {
|
||||
|
||||
test('bracket selection', async () => {
|
||||
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(), '[[[](|)]]',
|
||||
new Range(1, 6, 1, 7), new Range(1, 5, 1, 8), // ()
|
||||
new Range(1, 3, 1, 8), new Range(1, 2, 1, 9), // [[]()]
|
||||
new Range(1, 2, 1, 9), new Range(1, 1, 1, 10), // [[[]()]]
|
||||
new Range(1, 6, 1, 6), new Range(1, 5, 1, 7), // ()
|
||||
new Range(1, 3, 1, 7), new Range(1, 2, 1, 8), // [[]()]
|
||||
new Range(1, 2, 1, 8), new Range(1, 1, 1, 9), // [[[]()]]
|
||||
);
|
||||
|
||||
await assertRanges(new BracketSelectionRangeProvider(), '[a[](|)a]',
|
||||
new Range(1, 6, 1, 7), new Range(1, 5, 1, 8),
|
||||
new Range(1, 2, 1, 9), new Range(1, 1, 1, 10),
|
||||
new Range(1, 6, 1, 6), new Range(1, 5, 1, 7),
|
||||
new Range(1, 2, 1, 8), new Range(1, 1, 1, 9),
|
||||
);
|
||||
|
||||
// no bracket
|
||||
@@ -219,23 +221,23 @@ suite('SmartSelect', () => {
|
||||
await assertRanges(new BracketSelectionRangeProvider(), '|[[[]()]]');
|
||||
|
||||
// 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, 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, 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, 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, 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, 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 () => {
|
||||
|
||||
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, 1, 3, 2), new Range(1, 1, 3, 2),
|
||||
);
|
||||
|
||||
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(1, 1, 4, 2), new Range(1, 1, 4, 2),
|
||||
);
|
||||
@@ -244,60 +246,60 @@ suite('SmartSelect', () => {
|
||||
test('in-word ranges', async () => {
|
||||
|
||||
await assertRanges(new WordSelectionRangeProvider(), 'f|ooBar',
|
||||
new Range(1, 1, 1, 5), // foo
|
||||
new Range(1, 1, 1, 8), // fooBar
|
||||
new Range(1, 1, 1, 8), // doc
|
||||
new Range(1, 1, 1, 4), // foo
|
||||
new Range(1, 1, 1, 7), // fooBar
|
||||
new Range(1, 1, 1, 7), // doc
|
||||
);
|
||||
|
||||
await assertRanges(new WordSelectionRangeProvider(), 'f|oo_Ba',
|
||||
new Range(1, 1, 1, 5),
|
||||
new Range(1, 1, 1, 8),
|
||||
new Range(1, 1, 1, 8),
|
||||
new Range(1, 1, 1, 4),
|
||||
new Range(1, 1, 1, 7),
|
||||
new Range(1, 1, 1, 7),
|
||||
);
|
||||
|
||||
await assertRanges(new WordSelectionRangeProvider(), 'f|oo-Ba',
|
||||
new Range(1, 1, 1, 5),
|
||||
new Range(1, 1, 1, 8),
|
||||
new Range(1, 1, 1, 8),
|
||||
new Range(1, 1, 1, 4),
|
||||
new Range(1, 1, 1, 7),
|
||||
new Range(1, 1, 1, 7),
|
||||
);
|
||||
});
|
||||
|
||||
test('Default selection should select current word/hump first in camelCase #67493', async function () {
|
||||
|
||||
await assertRanges(new WordSelectionRangeProvider(), 'Abs|tractSmartSelect',
|
||||
new Range(1, 1, 1, 10),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 1, 1, 9),
|
||||
new Range(1, 1, 1, 20),
|
||||
new Range(1, 1, 1, 20),
|
||||
);
|
||||
|
||||
await assertRanges(new WordSelectionRangeProvider(), 'AbstractSma|rtSelect',
|
||||
new Range(1, 9, 1, 15),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 9, 1, 14),
|
||||
new Range(1, 1, 1, 20),
|
||||
new Range(1, 1, 1, 20),
|
||||
);
|
||||
|
||||
await assertRanges(new WordSelectionRangeProvider(), 'Abstrac-Sma|rt-elect',
|
||||
new Range(1, 9, 1, 15),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 9, 1, 14),
|
||||
new Range(1, 1, 1, 20),
|
||||
new Range(1, 1, 1, 20),
|
||||
);
|
||||
|
||||
await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt_elect',
|
||||
new Range(1, 9, 1, 15),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 9, 1, 14),
|
||||
new Range(1, 1, 1, 20),
|
||||
new Range(1, 1, 1, 20),
|
||||
);
|
||||
|
||||
await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt-elect',
|
||||
new Range(1, 9, 1, 15),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 9, 1, 14),
|
||||
new Range(1, 1, 1, 20),
|
||||
new Range(1, 1, 1, 20),
|
||||
);
|
||||
|
||||
await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rtSelect',
|
||||
new Range(1, 9, 1, 15),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 1, 1, 21),
|
||||
new Range(1, 9, 1, 14),
|
||||
new Range(1, 1, 1, 20),
|
||||
new Range(1, 1, 1, 20),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -321,4 +323,49 @@ suite('SmartSelect', () => {
|
||||
|
||||
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)
|
||||
for (; start >= 0; 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
|
||||
break;
|
||||
} 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 { 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 { Handler } from 'vs/editor/common/editorCommon';
|
||||
import { Cursor } from 'vs/editor/common/controller/cursor';
|
||||
|
||||
suite('WordOperations', () => {
|
||||
|
||||
@@ -193,6 +195,32 @@ suite('WordOperations', () => {
|
||||
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', () => {
|
||||
const EXPECTED = ['| /*| Just| some| more| text| a|+=| 3| +|5|-|3| +| 7| */| '].join('\n');
|
||||
const [text,] = deserializePipePositions(EXPECTED);
|
||||
|
||||
@@ -52,6 +52,7 @@ export abstract class MoveWordCommand extends EditorCommand {
|
||||
return this._moveTo(sel, outPosition, this._inSelectionMode);
|
||||
});
|
||||
|
||||
model.pushStackElement();
|
||||
editor._getCursors().setStates('moveWordCommand', CursorChangeReason.NotSet, result.map(r => CursorState.fromModelSelection(r)));
|
||||
if (result.length === 1) {
|
||||
const pos = new Position(result[0].positionLineNumber, result[0].positionColumn);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import 'vs/css!./inspectTokens';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
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.onDidChangeModelLanguage((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 {
|
||||
@@ -222,13 +224,13 @@ class InspectTokensWidget extends Disposable implements IContentWidget {
|
||||
|
||||
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 += `<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">token type</td><td class="tm-metadata-value">${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">foreground</td><td class="tm-metadata-value">${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">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">${metadata ? this._tokenTypeToString(metadata.tokenType) : '-?-'}</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">${metadata ? Color.Format.CSS.formatHex(metadata.foreground) : '-?-'}</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 += `<hr class="tokens-inspect-separator"/>`;
|
||||
|
||||
@@ -124,6 +124,13 @@ export interface IGlobalEditorOptions {
|
||||
* Defaults to 20000.
|
||||
*/
|
||||
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 _configurationService: IConfigurationService;
|
||||
private readonly _standaloneThemeService: IStandaloneThemeService;
|
||||
private _ownsModel: boolean;
|
||||
|
||||
constructor(
|
||||
@@ -363,6 +371,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon
|
||||
|
||||
this._contextViewService = <ContextViewService>contextViewService;
|
||||
this._configurationService = configurationService;
|
||||
this._standaloneThemeService = themeService;
|
||||
this._register(toDispose);
|
||||
this._register(themeDomRegistration);
|
||||
|
||||
@@ -391,6 +400,9 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon
|
||||
|
||||
public updateOptions(newOptions: IEditorOptions & IGlobalEditorOptions): void {
|
||||
applyConfigurationValues(this._configurationService, newOptions, false);
|
||||
if (typeof newOptions.theme === 'string') {
|
||||
this._standaloneThemeService.setTheme(newOptions.theme);
|
||||
}
|
||||
super.updateOptions(newOptions);
|
||||
}
|
||||
|
||||
@@ -414,6 +426,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
|
||||
|
||||
private readonly _contextViewService: ContextViewService;
|
||||
private readonly _configurationService: IConfigurationService;
|
||||
private readonly _standaloneThemeService: IStandaloneThemeService;
|
||||
|
||||
constructor(
|
||||
domElement: HTMLElement,
|
||||
@@ -443,6 +456,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
|
||||
|
||||
this._contextViewService = <ContextViewService>contextViewService;
|
||||
this._configurationService = configurationService;
|
||||
this._standaloneThemeService = themeService;
|
||||
|
||||
this._register(toDispose);
|
||||
this._register(themeDomRegistration);
|
||||
@@ -454,8 +468,11 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public updateOptions(newOptions: IDiffEditorOptions): void {
|
||||
public updateOptions(newOptions: IDiffEditorOptions & IGlobalEditorOptions): void {
|
||||
applyConfigurationValues(this._configurationService, newOptions, true);
|
||||
if (typeof newOptions.theme === 'string') {
|
||||
this._standaloneThemeService.setTheme(newOptions.theme);
|
||||
}
|
||||
super.updateOptions(newOptions);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ suite('MinimapCharRenderer', () => {
|
||||
imageData.data[4 * i + 2] = background.b;
|
||||
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[] = [];
|
||||
for (let i = 0; i < imageData.data.length; i++) {
|
||||
@@ -108,7 +108,7 @@ suite('MinimapCharRenderer', () => {
|
||||
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[] = [];
|
||||
for (let i = 0; i < imageData.data.length; i++) {
|
||||
|
||||
@@ -17,6 +17,7 @@ interface IEditorLayoutProviderOpts {
|
||||
readonly showLineNumbers: boolean;
|
||||
readonly lineNumbersMinChars: number;
|
||||
readonly lineNumbersDigitCount: number;
|
||||
maxLineNumber?: number;
|
||||
|
||||
readonly lineDecorationsWidth: number;
|
||||
|
||||
@@ -32,6 +33,7 @@ interface IEditorLayoutProviderOpts {
|
||||
readonly minimapSide: 'left' | 'right';
|
||||
readonly minimapRenderCharacters: boolean;
|
||||
readonly minimapMaxColumn: number;
|
||||
minimapMode?: 'actual' | 'cover' | 'contain';
|
||||
readonly pixelRatio: number;
|
||||
}
|
||||
|
||||
@@ -45,6 +47,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
options._write(EditorOption.folding, false);
|
||||
const minimapOptions: EditorMinimapOptions = {
|
||||
enabled: input.minimap,
|
||||
mode: input.minimapMode || 'actual',
|
||||
side: input.minimapSide,
|
||||
renderCharacters: input.minimapRenderCharacters,
|
||||
maxColumn: input.minimapMaxColumn,
|
||||
@@ -77,6 +80,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
outerWidth: input.outerWidth,
|
||||
outerHeight: input.outerHeight,
|
||||
lineHeight: input.lineHeight,
|
||||
maxLineNumber: input.maxLineNumber || Math.pow(10, input.lineNumbersDigitCount) - 1,
|
||||
lineNumbersDigitCount: input.lineNumbersDigitCount,
|
||||
typicalHalfwidthCharacterWidth: input.typicalHalfwidthCharacterWidth,
|
||||
maxDigitWidth: input.maxDigitWidth,
|
||||
@@ -125,6 +129,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 800,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 98,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -179,6 +191,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 800,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 97,
|
||||
|
||||
verticalScrollbarWidth: 11,
|
||||
@@ -233,6 +253,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 800,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 88,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -287,6 +315,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 88,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -341,6 +377,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 88,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -395,6 +439,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 83,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -449,6 +501,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 83,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -503,6 +563,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 82,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -557,6 +625,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 171,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -611,6 +687,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 169,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -665,6 +749,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 903,
|
||||
minimapWidth: 97,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 2,
|
||||
minimapCanvasInnerWidth: 97,
|
||||
minimapCanvasInnerHeight: 800,
|
||||
minimapCanvasOuterWidth: 97,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 89,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -719,6 +811,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
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,
|
||||
@@ -773,6 +873,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 945,
|
||||
minimapWidth: 55,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 2,
|
||||
minimapLineHeight: 4,
|
||||
minimapCanvasInnerWidth: 220,
|
||||
minimapCanvasInnerHeight: 3200,
|
||||
minimapCanvasOuterWidth: 55,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 93,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -827,6 +935,270 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 0,
|
||||
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,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -881,6 +1253,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 1096,
|
||||
minimapWidth: 91,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 2,
|
||||
minimapLineHeight: 4,
|
||||
minimapCanvasInnerWidth: 182,
|
||||
minimapCanvasInnerHeight: 844,
|
||||
minimapCanvasOuterWidth: 91,
|
||||
minimapCanvasOuterHeight: 422,
|
||||
viewportColumn: 83,
|
||||
|
||||
verticalScrollbarWidth: 14,
|
||||
|
||||
Vendored
+22
@@ -1108,6 +1108,13 @@ declare namespace monaco.editor {
|
||||
* Defaults to 20000.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
export interface IModelDecorationsChangedEvent {
|
||||
readonly affectsMinimap: boolean;
|
||||
readonly affectsOverviewRuler: boolean;
|
||||
}
|
||||
|
||||
export interface IModelOptionsChangedEvent {
|
||||
@@ -3334,6 +3343,14 @@ declare namespace monaco.editor {
|
||||
* The width of the minimap
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -3383,6 +3400,11 @@ declare namespace monaco.editor {
|
||||
* Defaults to 'right'.
|
||||
*/
|
||||
side?: 'right' | 'left';
|
||||
/**
|
||||
* Control the minimap rendering mode.
|
||||
* Defaults to 'actual'.
|
||||
*/
|
||||
mode?: 'actual' | 'cover' | 'contain';
|
||||
/**
|
||||
* Control the rendering of the minimap slider.
|
||||
* Defaults to 'mouseover'.
|
||||
|
||||
@@ -353,7 +353,7 @@ export class UserSettings extends Disposable {
|
||||
super();
|
||||
this.parser = new ConfigurationModelParser(this.userSettingsResource.toString(), this.scopes);
|
||||
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> {
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as path from 'vs/base/common/path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
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 { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { testFile } from 'vs/base/test/node/utils';
|
||||
|
||||
@@ -55,7 +55,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
|
||||
// Forward events from provider
|
||||
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 })));
|
||||
if (typeof provider.onDidErrorOccur === 'function') {
|
||||
providerDisposables.add(provider.onDidErrorOccur(error => this._onError.fire(new Error(error))));
|
||||
@@ -147,11 +147,11 @@ export class FileService extends Disposable implements IFileService {
|
||||
|
||||
//#endregion
|
||||
|
||||
private _onAfterOperation: Emitter<FileOperationEvent> = this._register(new Emitter<FileOperationEvent>());
|
||||
readonly onAfterOperation: Event<FileOperationEvent> = this._onAfterOperation.event;
|
||||
private _onDidRunOperation = this._register(new Emitter<FileOperationEvent>());
|
||||
readonly onDidRunOperation = this._onDidRunOperation.event;
|
||||
|
||||
private _onError: Emitter<Error> = this._register(new Emitter<Error>());
|
||||
readonly onError: Event<Error> = this._onError.event;
|
||||
private _onError = this._register(new Emitter<Error>());
|
||||
readonly onError = this._onError.event;
|
||||
|
||||
//#region File Metadata Resolving
|
||||
|
||||
@@ -299,7 +299,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
const fileStat = await this.writeFile(resource, bufferOrReadableOrStream);
|
||||
|
||||
// events
|
||||
this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat));
|
||||
this._onDidRunOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat));
|
||||
|
||||
return fileStat;
|
||||
}
|
||||
@@ -549,7 +549,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
|
||||
// resolve and send events
|
||||
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;
|
||||
}
|
||||
@@ -563,7 +563,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
|
||||
// resolve and send events
|
||||
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;
|
||||
}
|
||||
@@ -717,7 +717,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
|
||||
// events
|
||||
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;
|
||||
}
|
||||
@@ -799,15 +799,15 @@ export class FileService extends Disposable implements IFileService {
|
||||
await provider.delete(resource, { recursive, useTrash });
|
||||
|
||||
// Events
|
||||
this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.DELETE));
|
||||
this._onDidRunOperation.fire(new FileOperationEvent(resource, FileOperation.DELETE));
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region File Watching
|
||||
|
||||
private _onFileChanges: Emitter<FileChangesEvent> = this._register(new Emitter<FileChangesEvent>());
|
||||
readonly onFileChanges: Event<FileChangesEvent> = this._onFileChanges.event;
|
||||
private _onDidFilesChange = this._register(new Emitter<FileChangesEvent>());
|
||||
readonly onDidFilesChange = this._onDidFilesChange.event;
|
||||
|
||||
private activeWatchers = new Map<string, { disposable: IDisposable, count: number }>();
|
||||
|
||||
|
||||
@@ -63,12 +63,12 @@ export interface IFileService {
|
||||
* 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.
|
||||
*/
|
||||
readonly onFileChanges: Event<FileChangesEvent>;
|
||||
readonly onDidFilesChange: Event<FileChangesEvent>;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -471,15 +471,7 @@ export interface IFileChange {
|
||||
|
||||
export class FileChangesEvent {
|
||||
|
||||
private readonly _changes: readonly IFileChange[];
|
||||
|
||||
constructor(changes: readonly IFileChange[]) {
|
||||
this._changes = changes;
|
||||
}
|
||||
|
||||
get changes() {
|
||||
return this._changes;
|
||||
}
|
||||
constructor(public readonly changes: readonly IFileChange[]) { }
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
return this._changes.some(change => {
|
||||
return this.changes.some(change => {
|
||||
if (checkForChangeType && change.type !== type) {
|
||||
return false;
|
||||
}
|
||||
@@ -550,11 +542,11 @@ export class FileChangesEvent {
|
||||
}
|
||||
|
||||
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 {
|
||||
return this._changes.some(change => {
|
||||
return this.changes.some(change => {
|
||||
return change.type === type;
|
||||
});
|
||||
}
|
||||
|
||||
+1
-3
@@ -66,9 +66,7 @@ export class InMemoryFileSystemProvider extends Disposable implements IFileSyste
|
||||
async readdir(resource: URI): Promise<[string, FileType][]> {
|
||||
const entry = this._lookupAsDirectory(resource, false);
|
||||
let result: [string, FileType][] = [];
|
||||
for (const [name, child] of entry.entries) {
|
||||
result.push([name, child.type]);
|
||||
}
|
||||
entry.entries.forEach((child, name) => result.push([name, child.type]));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export class FileWatcher extends Disposable {
|
||||
|
||||
constructor(
|
||||
private path: string,
|
||||
private onFileChanges: (changes: IDiskFileChange[]) => void,
|
||||
private onDidFilesChange: (changes: IDiskFileChange[]) => void,
|
||||
private onLogMessage: (msg: ILogMessage) => void,
|
||||
private verboseLogging: boolean
|
||||
) {
|
||||
@@ -101,7 +101,7 @@ export class FileWatcher extends Disposable {
|
||||
|
||||
// Fire
|
||||
if (normalizedFileChanges.length > 0) {
|
||||
this.onFileChanges(normalizedFileChanges);
|
||||
this.onDidFilesChange(normalizedFileChanges);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
|
||||
@@ -21,7 +21,7 @@ export class FileWatcher extends Disposable {
|
||||
|
||||
constructor(
|
||||
private folders: IWatcherRequest[],
|
||||
private onFileChanges: (changes: IDiskFileChange[]) => void,
|
||||
private onDidFilesChange: (changes: IDiskFileChange[]) => void,
|
||||
private onLogMessage: (msg: ILogMessage) => void,
|
||||
private verboseLogging: boolean,
|
||||
) {
|
||||
@@ -68,7 +68,7 @@ export class FileWatcher extends Disposable {
|
||||
this.service.setVerboseLogging(this.verboseLogging);
|
||||
|
||||
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)));
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ export class FileWatcher extends Disposable {
|
||||
|
||||
constructor(
|
||||
private folders: IWatcherRequest[],
|
||||
private onFileChanges: (changes: IDiskFileChange[]) => void,
|
||||
private onDidFilesChange: (changes: IDiskFileChange[]) => void,
|
||||
private onLogMessage: (msg: ILogMessage) => void,
|
||||
private verboseLogging: boolean,
|
||||
private watcherOptions: IWatcherOptions = {}
|
||||
@@ -67,7 +67,7 @@ export class FileWatcher extends Disposable {
|
||||
|
||||
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)));
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export class FileWatcher implements IDisposable {
|
||||
|
||||
constructor(
|
||||
folders: { path: string, excludes: string[] }[],
|
||||
private onFileChanges: (changes: IDiskFileChange[]) => void,
|
||||
private onDidFilesChange: (changes: IDiskFileChange[]) => void,
|
||||
private onLogMessage: (msg: ILogMessage) => void,
|
||||
private verboseLogging: boolean
|
||||
) {
|
||||
@@ -62,7 +62,7 @@ export class FileWatcher implements IDisposable {
|
||||
|
||||
// Emit through event emitter
|
||||
if (events.length > 0) {
|
||||
this.onFileChanges(events);
|
||||
this.onDidFilesChange(events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,4 +72,4 @@ export class FileWatcher implements IDisposable {
|
||||
this.service = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('createFolder', async () => {
|
||||
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));
|
||||
|
||||
@@ -185,7 +185,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('createFolder: creating multiple folders at once', async () => {
|
||||
let event: FileOperationEvent;
|
||||
disposables.add(service.onAfterOperation(e => event = e));
|
||||
disposables.add(service.onDidRunOperation(e => event = e));
|
||||
|
||||
const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
|
||||
const parent = await service.resolve(URI.file(testDir));
|
||||
@@ -460,7 +460,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('deleteFile', async () => {
|
||||
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 source = await service.resolve(resource);
|
||||
@@ -496,7 +496,7 @@ suite('Disk File Service', function () {
|
||||
const source = await service.resolve(link);
|
||||
|
||||
let event: FileOperationEvent;
|
||||
disposables.add(service.onAfterOperation(e => event = e));
|
||||
disposables.add(service.onDidRunOperation(e => event = e));
|
||||
|
||||
await service.del(source.resource);
|
||||
|
||||
@@ -519,7 +519,7 @@ suite('Disk File Service', function () {
|
||||
await symlink(target.fsPath, link.fsPath);
|
||||
|
||||
let event: FileOperationEvent;
|
||||
disposables.add(service.onAfterOperation(e => event = e));
|
||||
disposables.add(service.onDidRunOperation(e => event = e));
|
||||
|
||||
await service.del(link);
|
||||
|
||||
@@ -532,7 +532,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('deleteFolder (recursive)', async () => {
|
||||
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 source = await service.resolve(resource);
|
||||
@@ -561,7 +561,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('move', async () => {
|
||||
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 sourceContents = readFileSync(source.fsPath);
|
||||
@@ -641,7 +641,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
async function testMoveAcrossProviders(sourceFile = 'index.html'): Promise<void> {
|
||||
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 sourceContents = readFileSync(source.fsPath);
|
||||
@@ -665,7 +665,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('move - multi folder', async () => {
|
||||
let event: FileOperationEvent;
|
||||
disposables.add(service.onAfterOperation(e => event = e));
|
||||
disposables.add(service.onDidRunOperation(e => event = e));
|
||||
|
||||
const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
|
||||
const renameToPath = join(...multiFolderPaths, 'other.html');
|
||||
@@ -684,7 +684,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('move - directory', async () => {
|
||||
let event: FileOperationEvent;
|
||||
disposables.add(service.onAfterOperation(e => event = e));
|
||||
disposables.add(service.onDidRunOperation(e => event = e));
|
||||
|
||||
const source = URI.file(join(testDir, 'deep'));
|
||||
|
||||
@@ -728,7 +728,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
async function testMoveFolderAcrossProviders(): Promise<void> {
|
||||
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 sourceChildren = readdirSync(source.fsPath);
|
||||
@@ -753,7 +753,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('move - MIX CASE', async () => {
|
||||
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 });
|
||||
assert.ok(source.size > 0);
|
||||
@@ -774,7 +774,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('move - same file', async () => {
|
||||
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 });
|
||||
assert.ok(source.size > 0);
|
||||
@@ -794,7 +794,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('move - same file #2', async () => {
|
||||
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 });
|
||||
assert.ok(source.size > 0);
|
||||
@@ -817,7 +817,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('move - source parent of target', async () => {
|
||||
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 });
|
||||
const originalSize = source.size;
|
||||
@@ -839,7 +839,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('move - FILE_MOVE_CONFLICT', async () => {
|
||||
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 });
|
||||
const originalSize = source.size;
|
||||
@@ -863,7 +863,7 @@ suite('Disk File Service', function () {
|
||||
let createEvent: FileOperationEvent;
|
||||
let moveEvent: FileOperationEvent;
|
||||
let deleteEvent: FileOperationEvent;
|
||||
disposables.add(service.onAfterOperation(e => {
|
||||
disposables.add(service.onDidRunOperation(e => {
|
||||
if (e.operation === FileOperation.CREATE) {
|
||||
createEvent = e;
|
||||
} else if (e.operation === FileOperation.DELETE) {
|
||||
@@ -927,7 +927,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
async function doTestCopy(sourceName: string = 'index.html') {
|
||||
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 target = URI.file(join(testDir, 'other.html'));
|
||||
@@ -952,7 +952,7 @@ suite('Disk File Service', function () {
|
||||
let createEvent: FileOperationEvent;
|
||||
let copyEvent: FileOperationEvent;
|
||||
let deleteEvent: FileOperationEvent;
|
||||
disposables.add(service.onAfterOperation(e => {
|
||||
disposables.add(service.onDidRunOperation(e => {
|
||||
if (e.operation === FileOperation.CREATE) {
|
||||
createEvent = e;
|
||||
} else if (e.operation === FileOperation.DELETE) {
|
||||
@@ -1057,7 +1057,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('copy - same file', async () => {
|
||||
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 });
|
||||
assert.ok(source.size > 0);
|
||||
@@ -1077,7 +1077,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('copy - same file #2', async () => {
|
||||
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 });
|
||||
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> {
|
||||
let event: FileOperationEvent;
|
||||
disposables.add(service.onAfterOperation(e => event = e));
|
||||
disposables.add(service.onDidRunOperation(e => event = e));
|
||||
|
||||
const contents = 'Hello World';
|
||||
const resource = URI.file(join(testDir, 'test.txt'));
|
||||
@@ -1600,7 +1600,7 @@ suite('Disk File Service', function () {
|
||||
|
||||
test('createFile (allows to overwrite existing)', async () => {
|
||||
let event: FileOperationEvent;
|
||||
disposables.add(service.onAfterOperation(e => event = e));
|
||||
disposables.add(service.onDidRunOperation(e => event = e));
|
||||
|
||||
const contents = 'Hello World';
|
||||
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');
|
||||
}
|
||||
|
||||
const listenerDisposable = service.onFileChanges(event => {
|
||||
const listenerDisposable = service.onDidFilesChange(event => {
|
||||
watcherDisposable.dispose();
|
||||
listenerDisposable.dispose();
|
||||
|
||||
|
||||
@@ -15,14 +15,14 @@ function toFileChangesEvent(changes: IDiskFileChange[]): FileChangesEvent {
|
||||
}
|
||||
|
||||
class TestFileWatcher {
|
||||
private readonly _onFileChanges: Emitter<FileChangesEvent>;
|
||||
private readonly _onDidFilesChange: Emitter<FileChangesEvent>;
|
||||
|
||||
constructor() {
|
||||
this._onFileChanges = new Emitter<FileChangesEvent>();
|
||||
this._onDidFilesChange = new Emitter<FileChangesEvent>();
|
||||
}
|
||||
|
||||
get onFileChanges(): Event<FileChangesEvent> {
|
||||
return this._onFileChanges.event;
|
||||
get onDidFilesChange(): Event<FileChangesEvent> {
|
||||
return this._onDidFilesChange.event;
|
||||
}
|
||||
|
||||
report(changes: IDiskFileChange[]): void {
|
||||
@@ -36,7 +36,7 @@ class TestFileWatcher {
|
||||
|
||||
// Emit through event emitter
|
||||
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 },
|
||||
];
|
||||
|
||||
watch.onFileChanges(e => {
|
||||
watch.onDidFilesChange(e => {
|
||||
assert.ok(e);
|
||||
assert.equal(e.changes.length, 3);
|
||||
assert.ok(e.contains(added, FileChangeType.ADDED));
|
||||
@@ -101,7 +101,7 @@ suite('Normalizer', () => {
|
||||
{ path: updatedFile.fsPath, type: FileChangeType.UPDATED }
|
||||
];
|
||||
|
||||
watch.onFileChanges(e => {
|
||||
watch.onDidFilesChange(e => {
|
||||
assert.ok(e);
|
||||
assert.equal(e.changes.length, 5);
|
||||
|
||||
@@ -131,7 +131,7 @@ suite('Normalizer', () => {
|
||||
{ path: unrelated.fsPath, type: FileChangeType.UPDATED },
|
||||
];
|
||||
|
||||
watch.onFileChanges(e => {
|
||||
watch.onDidFilesChange(e => {
|
||||
assert.ok(e);
|
||||
assert.equal(e.changes.length, 1);
|
||||
|
||||
@@ -156,7 +156,7 @@ suite('Normalizer', () => {
|
||||
{ path: unrelated.fsPath, type: FileChangeType.UPDATED },
|
||||
];
|
||||
|
||||
watch.onFileChanges(e => {
|
||||
watch.onDidFilesChange(e => {
|
||||
assert.ok(e);
|
||||
assert.equal(e.changes.length, 2);
|
||||
|
||||
@@ -182,7 +182,7 @@ suite('Normalizer', () => {
|
||||
{ path: unrelated.fsPath, type: FileChangeType.UPDATED },
|
||||
];
|
||||
|
||||
watch.onFileChanges(e => {
|
||||
watch.onDidFilesChange(e => {
|
||||
assert.ok(e);
|
||||
assert.equal(e.changes.length, 2);
|
||||
|
||||
@@ -211,7 +211,7 @@ suite('Normalizer', () => {
|
||||
{ path: updated.fsPath, type: FileChangeType.DELETED }
|
||||
];
|
||||
|
||||
watch.onFileChanges(e => {
|
||||
watch.onDidFilesChange(e => {
|
||||
assert.ok(e);
|
||||
assert.equal(e.changes.length, 2);
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ interface IServiceMock<T> {
|
||||
service: any;
|
||||
}
|
||||
|
||||
const isSinonSpyLike = (fn: Function): fn is sinon.SinonSpy => fn && 'callCount' in fn;
|
||||
|
||||
export class TestInstantiationService extends InstantiationService {
|
||||
|
||||
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>, obj: Partial<T>): T;
|
||||
public stub<T>(service: ServiceIdentifier<T>, ctor: Function, property: string, value: any): sinon.SinonStub;
|
||||
public stub<T>(service: ServiceIdentifier<T>, obj: Partial<T>, property: string, value: any): sinon.SinonStub;
|
||||
public stub<T>(service: ServiceIdentifier<T>, property: string, value: any): sinon.SinonStub;
|
||||
public stub<T>(serviceIdentifier: ServiceIdentifier<T>, arg2: any, arg3?: string, arg4?: 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, V>(service: ServiceIdentifier<T>, obj: Partial<T>, property: string, value: V): V extends Function ? sinon.SinonSpy : 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 | sinon.SinonSpy {
|
||||
let service = typeof arg2 !== 'string' ? arg2 : undefined;
|
||||
let serviceMock: IServiceMock<any> = { id: serviceIdentifier, service: service };
|
||||
let property = typeof arg2 === 'string' ? arg2 : arg3;
|
||||
@@ -53,9 +55,11 @@ export class TestInstantiationService extends InstantiationService {
|
||||
stubObject[property].restore();
|
||||
}
|
||||
if (typeof value === 'function') {
|
||||
stubObject[property] = value;
|
||||
const spy = isSinonSpyLike(value) ? value : sinon.spy(value);
|
||||
stubObject[property] = spy;
|
||||
return spy;
|
||||
} else {
|
||||
let stub = value ? sinon.stub().returns(value) : sinon.stub();
|
||||
const stub = value ? sinon.stub().returns(value) : sinon.stub();
|
||||
stubObject[property] = 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>, ctor?: any, fnProperty?: string, value?: any): sinon.SinonStub;
|
||||
public stubPromise<T>(service?: ServiceIdentifier<T>, obj?: any, fnProperty?: string, value?: any): sinon.SinonStub;
|
||||
public stubPromise(arg1?: any, arg2?: any, arg3?: any, arg4?: 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, 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 | sinon.SinonSpy {
|
||||
arg3 = typeof arg2 === 'string' ? Promise.resolve(arg3) : arg3;
|
||||
arg4 = typeof arg2 !== 'string' && typeof arg3 === 'string' ? Promise.resolve(arg4) : arg4;
|
||||
return this.stub(arg1, arg2, arg3, arg4);
|
||||
@@ -124,4 +128,4 @@ export class TestInstantiationService extends InstantiationService {
|
||||
interface SinonOptions {
|
||||
mock?: boolean;
|
||||
stub?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,15 +135,15 @@ export namespace IMarkerData {
|
||||
export function makeKeyOptionalMessage(markerData: IMarkerData, useMessage: boolean): string {
|
||||
let result: string[] = [emptyString];
|
||||
if (markerData.source) {
|
||||
result.push(markerData.source.replace('¦', '\¦'));
|
||||
result.push(markerData.source.replace('¦', '\\¦'));
|
||||
} else {
|
||||
result.push(emptyString);
|
||||
}
|
||||
if (markerData.code) {
|
||||
if (typeof markerData.code === 'string') {
|
||||
result.push(markerData.code.replace('¦', '\¦'));
|
||||
result.push(markerData.code.replace('¦', '\\¦'));
|
||||
} else {
|
||||
result.push(markerData.code.value.replace('¦', '\¦'));
|
||||
result.push(markerData.code.value.replace('¦', '\\¦'));
|
||||
}
|
||||
} else {
|
||||
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
|
||||
// https://github.com/microsoft/vscode/issues/77475
|
||||
if (markerData.message && useMessage) {
|
||||
result.push(markerData.message.replace('¦', '\¦'));
|
||||
result.push(markerData.message.replace('¦', '\\¦'));
|
||||
} else {
|
||||
result.push(emptyString);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import BaseSeverity from 'vs/base/common/severity';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
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';
|
||||
|
||||
export import Severity = BaseSeverity;
|
||||
@@ -318,16 +318,14 @@ export class NoOpNotification implements INotificationHandle {
|
||||
|
||||
readonly progress = new NoOpProgress();
|
||||
|
||||
private readonly _onDidClose: Emitter<void> = new Emitter();
|
||||
readonly onDidClose: Event<void> = this._onDidClose.event;
|
||||
readonly onDidClose = Event.None;
|
||||
readonly onDidChangeVisibility = Event.None;
|
||||
|
||||
updateSeverity(severity: Severity): void { }
|
||||
updateMessage(message: NotificationMessage): void { }
|
||||
updateActions(actions?: INotificationActions): void { }
|
||||
|
||||
close(): void {
|
||||
this._onDidClose.dispose();
|
||||
}
|
||||
close(): void { }
|
||||
}
|
||||
|
||||
export class NoOpProgress implements INotificationProgress {
|
||||
|
||||
@@ -17,7 +17,11 @@ export interface IProgressService {
|
||||
|
||||
_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 {
|
||||
@@ -45,19 +49,19 @@ export const enum ProgressLocation {
|
||||
}
|
||||
|
||||
export interface IProgressOptions {
|
||||
location: ProgressLocation | string;
|
||||
title?: string;
|
||||
source?: string;
|
||||
total?: number;
|
||||
cancellable?: boolean;
|
||||
buttons?: string[];
|
||||
readonly location: ProgressLocation | string;
|
||||
readonly title?: string;
|
||||
readonly source?: string;
|
||||
readonly total?: number;
|
||||
readonly cancellable?: boolean;
|
||||
readonly buttons?: string[];
|
||||
}
|
||||
|
||||
export interface IProgressNotificationOptions extends IProgressOptions {
|
||||
readonly location: ProgressLocation.Notification;
|
||||
readonly primaryActions?: ReadonlyArray<IAction>;
|
||||
readonly secondaryActions?: ReadonlyArray<IAction>;
|
||||
delay?: number;
|
||||
readonly delay?: number;
|
||||
}
|
||||
|
||||
export interface IProgressWindowOptions extends IProgressOptions {
|
||||
@@ -66,8 +70,8 @@ export interface IProgressWindowOptions extends IProgressOptions {
|
||||
}
|
||||
|
||||
export interface IProgressCompositeOptions extends IProgressOptions {
|
||||
location: ProgressLocation.Explorer | ProgressLocation.Extensions | ProgressLocation.Scm | string;
|
||||
delay?: number;
|
||||
readonly location: ProgressLocation.Explorer | ProgressLocation.Extensions | ProgressLocation.Scm | string;
|
||||
readonly delay?: number;
|
||||
}
|
||||
|
||||
export interface IProgressStep {
|
||||
@@ -96,20 +100,14 @@ export interface IProgress<T> {
|
||||
|
||||
export class Progress<T> implements IProgress<T> {
|
||||
|
||||
private _callback: (data: T) => void;
|
||||
private _value?: T;
|
||||
get value(): T | undefined { return this._value; }
|
||||
|
||||
constructor(callback: (data: T) => void) {
|
||||
this._callback = callback;
|
||||
}
|
||||
|
||||
get value(): T | undefined {
|
||||
return this._value;
|
||||
}
|
||||
constructor(private callback: (data: T) => void) { }
|
||||
|
||||
report(item: T) {
|
||||
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._register(this.fileService.watch(this.file));
|
||||
this._register(this.fileService.onFileChanges(e => {
|
||||
this._register(this.fileService.onDidFilesChange(e => {
|
||||
if (document.hasFocus()) {
|
||||
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 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 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."));
|
||||
|
||||
@@ -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);
|
||||
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> {
|
||||
|
||||
@@ -39,7 +39,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
|
||||
) {
|
||||
super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService);
|
||||
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> {
|
||||
|
||||
@@ -14,6 +14,9 @@ export class UserDataAuthTokenService extends Disposable implements IUserDataAut
|
||||
private _onDidChangeToken: Emitter<string | undefined> = this._register(new Emitter<string | undefined>());
|
||||
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;
|
||||
|
||||
constructor() {
|
||||
@@ -30,4 +33,8 @@ export class UserDataAuthTokenService extends Disposable implements IUserDataAut
|
||||
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);
|
||||
}
|
||||
}
|
||||
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.successiveFailures++;
|
||||
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 { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||
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';
|
||||
|
||||
export const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store';
|
||||
@@ -120,23 +120,27 @@ export interface IUserData {
|
||||
}
|
||||
|
||||
export interface IUserDataSyncStore {
|
||||
url: string;
|
||||
url: URI;
|
||||
authenticationProviderId: string;
|
||||
}
|
||||
|
||||
export function getUserDataSyncStore(configurationService: IConfigurationService): IUserDataSyncStore | undefined {
|
||||
const value = configurationService.getValue<IUserDataSyncStore>(CONFIGURATION_SYNC_STORE_KEY);
|
||||
return value && value.url && value.authenticationProviderId ? value : undefined;
|
||||
const value = configurationService.getValue<{ url: string, authenticationProviderId: string }>(CONFIGURATION_SYNC_STORE_KEY);
|
||||
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 type ResourceKey = 'settings' | 'keybindings' | 'extensions' | 'globalState';
|
||||
|
||||
export interface IUserDataManifest {
|
||||
settings: string;
|
||||
keybindings: string;
|
||||
extensions: string;
|
||||
globalState: string;
|
||||
latest?: Record<ResourceKey, string>
|
||||
session: string;
|
||||
}
|
||||
|
||||
export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService');
|
||||
@@ -162,6 +166,7 @@ export enum UserDataSyncErrorCode {
|
||||
TooLarge = 'TooLarge',
|
||||
NoRef = 'NoRef',
|
||||
TurnedOff = 'TurnedOff',
|
||||
SessionExpired = 'SessionExpired',
|
||||
|
||||
// Local Errors
|
||||
LocalPreconditionFailed = 'LocalPreconditionFailed',
|
||||
@@ -303,9 +308,11 @@ export interface IUserDataAuthTokenService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
readonly onDidChangeToken: Event<string | undefined>;
|
||||
readonly onTokenFailed: Event<void>;
|
||||
|
||||
getToken(): Promise<string | undefined>;
|
||||
setToken(accessToken: string | undefined): Promise<void>;
|
||||
sendTokenFailed(): void;
|
||||
}
|
||||
|
||||
export const IUserDataSyncLogService = createDecorator<IUserDataSyncLogService>('IUserDataSyncLogService');
|
||||
|
||||
@@ -96,6 +96,7 @@ export class UserDataAuthTokenServiceChannel implements IServerChannel {
|
||||
listen(_: unknown, event: string): Event<any> {
|
||||
switch (event) {
|
||||
case 'onDidChangeToken': return this.service.onDidChangeToken;
|
||||
case 'onTokenFailed': return this.service.onTokenFailed;
|
||||
}
|
||||
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 { equals } from 'vs/base/common/arrays';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
|
||||
type SyncErrorClassification = {
|
||||
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
||||
};
|
||||
|
||||
const SESSION_ID_KEY = 'sync.sessionId';
|
||||
|
||||
export class UserDataSyncService extends Disposable implements IUserDataSyncService {
|
||||
|
||||
_serviceBrand: any;
|
||||
@@ -48,6 +51,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
|
||||
@IUserDataAuthTokenService private readonly userDataAuthTokenService: IUserDataAuthTokenService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
) {
|
||||
super();
|
||||
this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser));
|
||||
@@ -96,7 +100,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
try {
|
||||
await synchroniser.sync(manifest ? manifest[synchroniser.resourceKey] : undefined);
|
||||
await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resourceKey] : undefined);
|
||||
} catch (e) {
|
||||
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`);
|
||||
|
||||
} finally {
|
||||
@@ -140,26 +160,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
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> {
|
||||
await this.checkEnablement();
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
@@ -189,6 +189,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
|
||||
async resetLocal(): Promise<void> {
|
||||
await this.checkEnablement();
|
||||
this.storageService.remove(SESSION_ID_KEY, StorageScope.GLOBAL);
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
try {
|
||||
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> {
|
||||
await this.checkEnablement();
|
||||
try {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
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 { 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 { CancellationToken } from 'vs/base/common/cancellation';
|
||||
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.');
|
||||
}
|
||||
|
||||
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 = {};
|
||||
// Disable caching as they are cached by synchronisers
|
||||
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.');
|
||||
}
|
||||
|
||||
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' };
|
||||
if (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.');
|
||||
}
|
||||
|
||||
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 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.');
|
||||
}
|
||||
|
||||
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 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) {
|
||||
this.authTokenService.sendTokenFailed();
|
||||
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 { merge } from 'vs/platform/userDataSync/common/keybindingsMerge';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
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';
|
||||
import { TestUserDataSyncUtilService } from 'vs/platform/userDataSync/test/common/userDataSyncClient';
|
||||
|
||||
suite('KeybindingsMerge - No Conflicts', () => {
|
||||
|
||||
@@ -613,7 +609,7 @@ suite('KeybindingsMerge - No Conflicts', () => {
|
||||
});
|
||||
|
||||
async function mergeKeybindings(localContent: string, remoteContent: string, baseContent: string | null) {
|
||||
const userDataSyncUtilService = new MockUserDataSyncUtilService();
|
||||
const userDataSyncUtilService = new TestUserDataSyncUtilService();
|
||||
const formattingOptions = await userDataSyncUtilService.resolveFormattingOptions();
|
||||
return merge(localContent, remoteContent, baseContent, formattingOptions, userDataSyncUtilService);
|
||||
}
|
||||
@@ -621,22 +617,3 @@ async function mergeKeybindings(localContent: string, remoteContent: string, bas
|
||||
function stringify(value: any): string {
|
||||
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 { ILogService } from 'vs/platform/log/common/log';
|
||||
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
|
||||
export class MainThreadFileSystemEventService {
|
||||
@@ -28,6 +31,7 @@ export class MainThreadFileSystemEventService {
|
||||
@IProgressService progressService: IProgressService,
|
||||
@IConfigurationService configService: IConfigurationService,
|
||||
@ILogService logService: ILogService,
|
||||
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService
|
||||
) {
|
||||
|
||||
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService);
|
||||
@@ -38,7 +42,7 @@ export class MainThreadFileSystemEventService {
|
||||
changed: [],
|
||||
deleted: []
|
||||
};
|
||||
this._listener.add(fileService.onFileChanges(event => {
|
||||
this._listener.add(fileService.onDidFilesChange(event => {
|
||||
for (let change of event.changes) {
|
||||
switch (change.type) {
|
||||
case FileChangeType.ADDED:
|
||||
@@ -66,9 +70,7 @@ export class MainThreadFileSystemEventService {
|
||||
messages.set(FileOperation.DELETE, localize('msg-delete', "Running 'File Delete' participants..."));
|
||||
messages.set(FileOperation.MOVE, localize('msg-rename', "Running 'File Rename' participants..."));
|
||||
|
||||
|
||||
this._listener.add(textFileService.onWillRunOperation(e => {
|
||||
|
||||
function participateInFileOperation(e: IWaitUntil, operation: FileOperation, target: URI, source?: URI): void {
|
||||
const timeout = configService.getValue<number>('files.participants.timeout');
|
||||
if (timeout <= 0) {
|
||||
return; // disabled
|
||||
@@ -76,19 +78,19 @@ export class MainThreadFileSystemEventService {
|
||||
|
||||
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) => {
|
||||
|
||||
const cts = new CancellationTokenSource();
|
||||
|
||||
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();
|
||||
reject(new Error('timeout'));
|
||||
}, timeout);
|
||||
|
||||
proxy.$onWillRunFileOperation(e.operation, e.target, e.source, timeout, cts.token)
|
||||
proxy.$onWillRunFileOperation(operation, target, source, timeout, cts.token)
|
||||
.then(resolve, reject)
|
||||
.finally(() => clearTimeout(timeoutHandle));
|
||||
});
|
||||
@@ -96,10 +98,14 @@ export class MainThreadFileSystemEventService {
|
||||
});
|
||||
|
||||
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
|
||||
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 {
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Position as EditorPosition } from 'vs/editor/common/core/position';
|
||||
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 { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
@@ -36,6 +36,34 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostLanguageFeatures);
|
||||
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 {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user