Merge from vscode 9bc92b48d945144abb405b9e8df05e18accb9148

This commit is contained in:
ADS Merger
2020-02-19 03:11:35 +00:00
parent 98584d32a7
commit 1e308639e5
253 changed files with 6414 additions and 2296 deletions
+1 -1
View File
@@ -193,7 +193,7 @@
"typemoq",
"sinon",
"vs/nls",
"azdata",
"azdata",
"**/{vs,sql}/base/common/**",
"**/{vs,sql}/base/test/common/**",
"**/{vs,sql}/base/parts/*/common/**",
-2
View File
@@ -16,8 +16,6 @@
"request": "attach",
"name": "Attach to Extension Host",
"port": 5870,
"timeout": 30000,
"restart": true,
"outFiles": [
"${workspaceFolder}/out/**/*.js"
]
-14
View File
@@ -31,20 +31,6 @@
}
}
},
{
"type": "npm",
"script": "strict-function-types-watch",
"label": "TS - Strict Function Types",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"problemMatcher": {
"base": "$tsc-watch",
"owner": "typescript-function-types",
"applyTo": "allDocuments"
}
},
{
"type": "npm",
"script": "strict-vscode-watch",
@@ -207,7 +207,7 @@ steps:
"toolVersion": "1.0"
}
]
SessionTimeout: 120
SessionTimeout: 60
displayName: Notarization
- script: |
+120 -83
View File
@@ -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"
}
}
+6 -1
View File
@@ -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)."
}
+1
View File
@@ -44,6 +44,7 @@ export interface Commit {
readonly authorDate?: Date;
readonly authorName?: string;
readonly authorEmail?: string;
readonly commitDate?: Date;
}
export interface Submodule {
+12 -9
View File
@@ -333,7 +333,7 @@ function sanitizePath(path: string): string {
return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`);
}
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);
+2 -1
View File
@@ -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();
}
+6
View File
@@ -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
View File
@@ -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": {
+26 -2
View File
@@ -21,6 +21,7 @@ interface IToken {
accessToken?: string; // When unable to refresh due to network problems, the access token becomes undefined
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
View File
@@ -50,7 +50,7 @@ function code() {
export VSCODE_LOGS=
# Launch Code
exec "$CODE" . "$@"
exec "$CODE" . --no-sandbox "$@"
}
function code-wsl()
+4 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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;
}
}
+1
View File
@@ -11,6 +11,7 @@
"strictBindCallApply": true,
"strictNullChecks": false,
"strictPropertyInitialization": false,
"strict": false,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
+9 -9
View File
@@ -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);
}
@@ -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';
+1 -1
View File
@@ -26,7 +26,7 @@ import { IStateService } from 'vs/platform/state/node/state';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { 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';
+1 -1
View File
@@ -24,7 +24,7 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper
import { IRequestService } from 'vs/platform/request/common/request';
import { 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();
}
+108 -6
View File
@@ -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),
+54 -14
View File
@@ -1423,19 +1423,15 @@ export class TextModel extends Disposable implements model.ITextModel {
private _changeDecorations<T>(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T | null {
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;
}
/**
+26 -1
View File
@@ -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) {
+12 -2
View File
@@ -10,6 +10,7 @@ import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config
import { Range } from 'vs/editor/common/core/range';
import { 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();
+3 -2
View File
@@ -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,
+22
View File
@@ -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';
+12 -12
View File
@@ -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 }>();
+6 -14
View File
@@ -63,12 +63,12 @@ export interface IFileService {
* Allows to listen for file changes. The event will fire for every file within the opened workspace
* (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;
});
}
@@ -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);
}
}
@@ -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);
+4 -4
View File
@@ -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 -19
View File
@@ -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