mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 52dcb723a39ae75bee1bd56b3312d7fcdc87aeed (#6719)
This commit is contained in:
@@ -1148,6 +1148,26 @@
|
||||
"description": "%config.enableSmartCommit%",
|
||||
"default": false
|
||||
},
|
||||
"git.smartCommitChanges": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"all",
|
||||
"tracked"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"%config.smartCommitChanges.all%",
|
||||
"%config.smartCommitChanges.tracked%"
|
||||
],
|
||||
"scope": "resource",
|
||||
"description": "%config.smartCommitChanges%",
|
||||
"default": "all"
|
||||
},
|
||||
"git.suggestSmartCommit": {
|
||||
"type": "boolean",
|
||||
"scope": "resource",
|
||||
"description": "%config.suggestSmartCommit%",
|
||||
"default": true
|
||||
},
|
||||
"git.enableCommitSigning": {
|
||||
"type": "boolean",
|
||||
"scope": "resource",
|
||||
@@ -1165,10 +1185,26 @@
|
||||
"default": true,
|
||||
"description": "%config.decorations.enabled%"
|
||||
},
|
||||
"git.promptToSaveFilesBeforeCommit": {
|
||||
"git.enableStatusBarSync": {
|
||||
"type": "boolean",
|
||||
"scope": "resource",
|
||||
"default": true,
|
||||
"description": "%config.enableStatusBarSync%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"git.promptToSaveFilesBeforeCommit": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"always",
|
||||
"staged",
|
||||
"never"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"%config.promptToSaveFilesBeforeCommit.always%",
|
||||
"%config.promptToSaveFilesBeforeCommit.staged%",
|
||||
"%config.promptToSaveFilesBeforeCommit.never%"
|
||||
],
|
||||
"scope": "resource",
|
||||
"default": "always",
|
||||
"description": "%config.promptToSaveFilesBeforeCommit%"
|
||||
},
|
||||
"git.postCommitCommand": {
|
||||
@@ -1312,6 +1348,12 @@
|
||||
"scope": "resource",
|
||||
"default": true,
|
||||
"description": "%config.openDiffOnClick%"
|
||||
},
|
||||
"git.supportCancellation": {
|
||||
"type": "boolean",
|
||||
"scope": "resource",
|
||||
"default": false,
|
||||
"description": "%config.supportCancellation%"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1477,4 +1519,4 @@
|
||||
"@types/which": "^1.0.28",
|
||||
"mocha": "^3.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,10 +89,18 @@
|
||||
"config.ignoreLimitWarning": "Ignores the warning when there are too many changes in a repository.",
|
||||
"config.defaultCloneDirectory": "The default location to clone a git repository.",
|
||||
"config.enableSmartCommit": "Commit all changes when there are no staged changes.",
|
||||
"config.smartCommitChanges": "Control which changes are automatically staged by Smart Commit.",
|
||||
"config.smartCommitChanges.all": "Automatically stage all changes.",
|
||||
"config.smartCommitChanges.tracked": "Automatically staged tracked changes only.",
|
||||
"config.suggestSmartCommit": "Suggests to enable smart commit (commit all changes when there are no staged changes).",
|
||||
"config.enableCommitSigning": "Enables commit signing with GPG.",
|
||||
"config.discardAllScope": "Controls what changes are discarded by the `Discard all changes` command. `all` discards all changes. `tracked` discards only tracked files. `prompt` shows a prompt dialog every time the action is run.",
|
||||
"config.decorations.enabled": "Controls whether Git contributes colors and badges to the explorer and the open editors view.",
|
||||
"config.enableStatusBarSync": "Controls whether the Git Sync command appears in the status bar.",
|
||||
"config.promptToSaveFilesBeforeCommit": "Controls whether Git should check for unsaved files before committing.",
|
||||
"config.promptToSaveFilesBeforeCommit.always": "Check for any unsaved files.",
|
||||
"config.promptToSaveFilesBeforeCommit.staged": "Check only for unsaved staged files.",
|
||||
"config.promptToSaveFilesBeforeCommit.never": "Disable this check.",
|
||||
"config.postCommitCommand": "Runs a git command after a successful commit.",
|
||||
"config.postCommitCommand.none": "Don't run any command after a commit.",
|
||||
"config.postCommitCommand.push": "Run 'Git Push' after a successful commit.",
|
||||
@@ -118,6 +126,7 @@
|
||||
"config.useForcePushWithLease": "Controls whether force pushing uses the safer force-with-lease variant.",
|
||||
"config.confirmForcePush": "Controls whether to ask for confirmation before force-pushing.",
|
||||
"config.openDiffOnClick": "Controls whether the diff editor should be opened when clicking a change. Otherwise the regular editor will be opened.",
|
||||
"config.supportCancellation": "Controls whether a notification comes up when running the Sync action, which allows the user to cancel the operation.",
|
||||
"colors.added": "Color for added resources.",
|
||||
"colors.modified": "Color for modified resources.",
|
||||
"colors.deleted": "Color for deleted resources.",
|
||||
@@ -125,4 +134,4 @@
|
||||
"colors.ignored": "Color for ignored resources.",
|
||||
"colors.conflict": "Color for resources with conflicts.",
|
||||
"colors.submodule": "Color for submodule resources."
|
||||
}
|
||||
}
|
||||
|
||||
3
extensions/git/src/api/git.d.ts
vendored
3
extensions/git/src/api/git.d.ts
vendored
@@ -238,5 +238,6 @@ export const enum GitErrorCodes {
|
||||
CantLockRef = 'CantLockRef',
|
||||
CantRebaseMultipleBranches = 'CantRebaseMultipleBranches',
|
||||
PatchDoesNotApply = 'PatchDoesNotApply',
|
||||
NoPathFound = 'NoPathFound'
|
||||
NoPathFound = 'NoPathFound',
|
||||
UnknownPath = 'UnknownPath',
|
||||
}
|
||||
|
||||
@@ -353,9 +353,11 @@ export class CommandCenter {
|
||||
switch (resource.type) {
|
||||
case Status.INDEX_MODIFIED:
|
||||
case Status.INDEX_RENAMED:
|
||||
case Status.INDEX_ADDED:
|
||||
return this.getURI(resource.original, 'HEAD');
|
||||
|
||||
case Status.MODIFIED:
|
||||
case Status.UNTRACKED:
|
||||
return this.getURI(resource.resourceUri, '~');
|
||||
|
||||
case Status.DELETED_BY_THEM:
|
||||
@@ -414,6 +416,7 @@ export class CommandCenter {
|
||||
switch (resource.type) {
|
||||
case Status.INDEX_MODIFIED:
|
||||
case Status.INDEX_RENAMED:
|
||||
case Status.INDEX_ADDED:
|
||||
return `${basename} (Index)`;
|
||||
|
||||
case Status.MODIFIED:
|
||||
@@ -426,6 +429,10 @@ export class CommandCenter {
|
||||
|
||||
case Status.DELETED_BY_THEM:
|
||||
return `${basename} (Ours)`;
|
||||
|
||||
case Status.UNTRACKED:
|
||||
|
||||
return `${basename} (Untracked)`;
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -450,6 +457,8 @@ export class CommandCenter {
|
||||
return;
|
||||
}
|
||||
|
||||
url = url.trim().replace(/^git\s+clone\s+/, '');
|
||||
|
||||
const config = workspace.getConfiguration('git');
|
||||
let defaultCloneDirectory = config.get<string>('defaultCloneDirectory') || os.homedir();
|
||||
defaultCloneDirectory = defaultCloneDirectory.replace(/^~/, os.homedir());
|
||||
@@ -698,7 +707,13 @@ export class CommandCenter {
|
||||
viewColumn: ViewColumn.Active
|
||||
};
|
||||
|
||||
const document = await workspace.openTextDocument(uri);
|
||||
let document;
|
||||
try {
|
||||
document = await workspace.openTextDocument(uri);
|
||||
} catch (error) {
|
||||
await commands.executeCommand<void>('vscode.open', uri, opts);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if active text editor has same path as other editor. we cannot compare via
|
||||
// URI.toString() here because the schemas can be different. Instead we just go by path.
|
||||
@@ -1225,23 +1240,35 @@ export class CommandCenter {
|
||||
opts?: CommitOptions
|
||||
): Promise<boolean> {
|
||||
const config = workspace.getConfiguration('git', Uri.file(repository.root));
|
||||
const promptToSaveFilesBeforeCommit = config.get<boolean>('promptToSaveFilesBeforeCommit') === true;
|
||||
let promptToSaveFilesBeforeCommit = config.get<'always' | 'staged' | 'never'>('promptToSaveFilesBeforeCommit');
|
||||
|
||||
if (promptToSaveFilesBeforeCommit) {
|
||||
const unsavedTextDocuments = workspace.textDocuments
|
||||
// migration
|
||||
if (promptToSaveFilesBeforeCommit as any === true) {
|
||||
promptToSaveFilesBeforeCommit = 'always';
|
||||
} else if (promptToSaveFilesBeforeCommit as any === false) {
|
||||
promptToSaveFilesBeforeCommit = 'never';
|
||||
}
|
||||
|
||||
if (promptToSaveFilesBeforeCommit !== 'never') {
|
||||
let documents = workspace.textDocuments
|
||||
.filter(d => !d.isUntitled && d.isDirty && isDescendant(repository.root, d.uri.fsPath));
|
||||
|
||||
if (unsavedTextDocuments.length > 0) {
|
||||
const message = unsavedTextDocuments.length === 1
|
||||
? localize('unsaved files single', "The following file is unsaved: {0}.\n\nWould you like to save it before committing?", path.basename(unsavedTextDocuments[0].uri.fsPath))
|
||||
: localize('unsaved files', "There are {0} unsaved files.\n\nWould you like to save them before committing?", unsavedTextDocuments.length);
|
||||
if (promptToSaveFilesBeforeCommit === 'staged') {
|
||||
documents = documents
|
||||
.filter(d => repository.indexGroup.resourceStates.some(s => s.resourceUri.path === d.uri.fsPath));
|
||||
}
|
||||
|
||||
if (documents.length > 0) {
|
||||
const message = documents.length === 1
|
||||
? localize('unsaved files single', "The following file is unsaved and will not be included in the commit if you proceed: {0}.\n\nWould you like to save it before committing?", path.basename(documents[0].uri.fsPath))
|
||||
: localize('unsaved files', "There are {0} unsaved files.\n\nWould you like to save them before committing?", documents.length);
|
||||
const saveAndCommit = localize('save and commit', "Save All & Commit");
|
||||
const commit = localize('commit', "Commit Anyway");
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, saveAndCommit, commit);
|
||||
|
||||
if (pick === saveAndCommit) {
|
||||
await Promise.all(unsavedTextDocuments.map(d => d.save()));
|
||||
await repository.status();
|
||||
await Promise.all(documents.map(d => d.save()));
|
||||
await repository.add(documents.map(d => d.uri));
|
||||
} else if (pick !== commit) {
|
||||
return false; // do not commit on cancel
|
||||
}
|
||||
@@ -1255,15 +1282,24 @@ export class CommandCenter {
|
||||
|
||||
// no changes, and the user has not configured to commit all in this case
|
||||
if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit) {
|
||||
const suggestSmartCommit = config.get<boolean>('suggestSmartCommit') === true;
|
||||
|
||||
if (!suggestSmartCommit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// prompt the user if we want to commit all or not
|
||||
const message = localize('no staged changes', "There are no staged changes to commit.\n\nWould you like to automatically stage all your changes and commit them directly?");
|
||||
const yes = localize('yes', "Yes");
|
||||
const always = localize('always', "Always");
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes, always);
|
||||
const never = localize('never', "Never");
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes, always, never);
|
||||
|
||||
if (pick === always) {
|
||||
config.update('enableSmartCommit', true, true);
|
||||
} else if (pick === never) {
|
||||
config.update('suggestSmartCommit', false, true);
|
||||
return false;
|
||||
} else if (pick !== yes) {
|
||||
return false; // do not commit on cancel
|
||||
}
|
||||
@@ -1301,6 +1337,10 @@ export class CommandCenter {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.all && config.get<'all' | 'tracked'>('smartCommitChanges') === 'tracked') {
|
||||
opts.all = 'tracked';
|
||||
}
|
||||
|
||||
await repository.commit(message, opts);
|
||||
|
||||
const postCommitCommand = config.get<'none' | 'push' | 'sync'>('postCommitCommand');
|
||||
@@ -1350,19 +1390,6 @@ export class CommandCenter {
|
||||
await this.commitWithAnyInput(repository);
|
||||
}
|
||||
|
||||
@command('git.commitWithInput', { repository: true })
|
||||
async commitWithInput(repository: Repository): Promise<void> {
|
||||
if (!repository.inputBox.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const didCommit = await this.smartCommit(repository, async () => repository.inputBox.value);
|
||||
|
||||
if (didCommit) {
|
||||
repository.inputBox.value = await repository.getCommitTemplate();
|
||||
}
|
||||
}
|
||||
|
||||
@command('git.commitStaged', { repository: true })
|
||||
async commitStaged(repository: Repository): Promise<void> {
|
||||
await this.commitWithAnyInput(repository, { all: false });
|
||||
@@ -1487,12 +1514,12 @@ export class CommandCenter {
|
||||
await this._branch(repository, undefined, true);
|
||||
}
|
||||
|
||||
private async _branch(repository: Repository, defaultName?: string, from = false): Promise<void> {
|
||||
private async promptForBranchName(defaultName?: string): Promise<string> {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const branchWhitespaceChar = config.get<string>('branchWhitespaceChar')!;
|
||||
const branchValidationRegex = config.get<string>('branchValidationRegex')!;
|
||||
const sanitize = (name: string) => name ?
|
||||
name.trim().replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, branchWhitespaceChar)
|
||||
name.trim().replace(/^-+/, '').replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, branchWhitespaceChar)
|
||||
: name;
|
||||
|
||||
const rawBranchName = defaultName || await window.showInputBox({
|
||||
@@ -1509,7 +1536,11 @@ export class CommandCenter {
|
||||
}
|
||||
});
|
||||
|
||||
const branchName = sanitize(rawBranchName || '');
|
||||
return sanitize(rawBranchName || '');
|
||||
}
|
||||
|
||||
private async _branch(repository: Repository, defaultName?: string, from = false): Promise<void> {
|
||||
const branchName = await this.promptForBranchName(defaultName);
|
||||
|
||||
if (!branchName) {
|
||||
return;
|
||||
@@ -1571,25 +1602,21 @@ export class CommandCenter {
|
||||
|
||||
@command('git.renameBranch', { repository: true })
|
||||
async renameBranch(repository: Repository): Promise<void> {
|
||||
const name = await window.showInputBox({
|
||||
placeHolder: localize('branch name', "Branch name"),
|
||||
prompt: localize('provide branch name', "Please provide a branch name"),
|
||||
value: repository.HEAD && repository.HEAD.name
|
||||
});
|
||||
const branchName = await this.promptForBranchName();
|
||||
|
||||
if (!name || name.trim().length === 0) {
|
||||
if (!branchName) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await repository.renameBranch(name);
|
||||
await repository.renameBranch(branchName);
|
||||
} catch (err) {
|
||||
switch (err.gitErrorCode) {
|
||||
case GitErrorCodes.InvalidBranchName:
|
||||
window.showErrorMessage(localize('invalid branch name', 'Invalid branch name'));
|
||||
return;
|
||||
case GitErrorCodes.BranchAlreadyExists:
|
||||
window.showErrorMessage(localize('branch already exists', "A branch named '{0}' already exists", name));
|
||||
window.showErrorMessage(localize('branch already exists', "A branch named '{0}' already exists", branchName));
|
||||
return;
|
||||
default:
|
||||
throw err;
|
||||
@@ -1913,7 +1940,17 @@ export class CommandCenter {
|
||||
private async _sync(repository: Repository, rebase: boolean): Promise<void> {
|
||||
const HEAD = repository.HEAD;
|
||||
|
||||
if (!HEAD || !HEAD.upstream) {
|
||||
if (!HEAD) {
|
||||
return;
|
||||
} else if (!HEAD.upstream) {
|
||||
const branchName = HEAD.name;
|
||||
const message = localize('confirm publish branch', "The branch '{0}' has no upstream branch. Would you like to publish this branch?", branchName);
|
||||
const yes = localize('ok', "OK");
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes);
|
||||
|
||||
if (pick === yes) {
|
||||
await this.publish(repository);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1945,8 +1982,16 @@ export class CommandCenter {
|
||||
}
|
||||
|
||||
@command('git.sync', { repository: true })
|
||||
sync(repository: Repository): Promise<void> {
|
||||
return this._sync(repository, false);
|
||||
async sync(repository: Repository): Promise<void> {
|
||||
try {
|
||||
await this._sync(repository, false);
|
||||
} catch (err) {
|
||||
if (/Cancelled/i.test(err && (err.message || err.stderr || ''))) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@command('git._syncAll')
|
||||
@@ -1963,8 +2008,16 @@ export class CommandCenter {
|
||||
}
|
||||
|
||||
@command('git.syncRebase', { repository: true })
|
||||
syncRebase(repository: Repository): Promise<void> {
|
||||
return this._sync(repository, true);
|
||||
async syncRebase(repository: Repository): Promise<void> {
|
||||
try {
|
||||
await this._sync(repository, true);
|
||||
} catch (err) {
|
||||
if (/Cancelled/i.test(err && (err.message || err.stderr || ''))) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@command('git.publish', { repository: true })
|
||||
|
||||
@@ -11,12 +11,15 @@ import * as which from 'which';
|
||||
import { EventEmitter } from 'events';
|
||||
import iconv = require('iconv-lite');
|
||||
import * as filetype from 'file-type';
|
||||
import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent } from './util';
|
||||
import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter } from './util';
|
||||
import { CancellationToken } from 'vscode';
|
||||
import { URI } from 'vscode-uri';
|
||||
import { detectEncoding } from './encoding';
|
||||
import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status } from './api/git';
|
||||
|
||||
// https://github.com/microsoft/vscode/issues/65693
|
||||
const MAX_CLI_LENGTH = 30000;
|
||||
|
||||
const readfile = denodeify<string, string | null, string>(fs.readFile);
|
||||
|
||||
export interface IGit {
|
||||
@@ -339,7 +342,7 @@ export class Git {
|
||||
}
|
||||
|
||||
async clone(url: string, parentPath: string, cancellationToken?: CancellationToken): Promise<string> {
|
||||
let baseFolderName = decodeURI(url).replace(/[\/]+$/, '').replace(/^.*\//, '').replace(/\.git$/, '') || 'repository';
|
||||
let baseFolderName = decodeURI(url).replace(/[\/]+$/, '').replace(/^.*[\/\\]/, '').replace(/\.git$/, '') || 'repository';
|
||||
let folderName = baseFolderName;
|
||||
let folderPath = path.join(parentPath, folderName);
|
||||
let count = 1;
|
||||
@@ -598,13 +601,13 @@ export function parseGitmodules(raw: string): Submodule[] {
|
||||
}
|
||||
|
||||
export function parseGitCommit(raw: string): Commit | null {
|
||||
const match = /^([0-9a-f]{40})\n(.*)\n(.*)\n([^]*)$/m.exec(raw.trim());
|
||||
const match = /^([0-9a-f]{40})\n(.*)\n(.*)(\n([^]*))?$/m.exec(raw.trim());
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parents = match[3] ? match[3].split(' ') : [];
|
||||
return { hash: match[1], message: match[4], parents, authorEmail: match[2] };
|
||||
return { hash: match[1], message: match[5], parents, authorEmail: match[2] };
|
||||
}
|
||||
|
||||
interface LsTreeElement {
|
||||
@@ -639,7 +642,7 @@ export function parseLsFiles(raw: string): LsFilesElement[] {
|
||||
}
|
||||
|
||||
export interface CommitOptions {
|
||||
all?: boolean;
|
||||
all?: boolean | 'tracked';
|
||||
amend?: boolean;
|
||||
signoff?: boolean;
|
||||
signCommit?: boolean;
|
||||
@@ -649,6 +652,7 @@ export interface CommitOptions {
|
||||
export interface PullOptions {
|
||||
unshallow?: boolean;
|
||||
tags?: boolean;
|
||||
readonly cancellationToken?: CancellationToken;
|
||||
}
|
||||
|
||||
export enum ForcePushMode {
|
||||
@@ -797,7 +801,7 @@ export class Repository {
|
||||
const elements = await this.lsfiles(path);
|
||||
|
||||
if (elements.length === 0) {
|
||||
throw new GitError({ message: 'Error running ls-files' });
|
||||
throw new GitError({ message: 'Path not known by git', gitErrorCode: GitErrorCodes.UnknownPath });
|
||||
}
|
||||
|
||||
const { mode, object } = elements[0];
|
||||
@@ -810,7 +814,7 @@ export class Repository {
|
||||
const elements = await this.lstree(treeish, path);
|
||||
|
||||
if (elements.length === 0) {
|
||||
throw new GitError({ message: 'Error running ls-files' });
|
||||
throw new GitError({ message: 'Path not known by git', gitErrorCode: GitErrorCodes.UnknownPath });
|
||||
}
|
||||
|
||||
const { mode, object, size } = elements[0];
|
||||
@@ -1077,8 +1081,16 @@ export class Repository {
|
||||
return result.stdout.trim();
|
||||
}
|
||||
|
||||
async add(paths: string[]): Promise<void> {
|
||||
const args = ['add', '-A', '--'];
|
||||
async add(paths: string[], opts?: { update?: boolean }): Promise<void> {
|
||||
const args = ['add'];
|
||||
|
||||
if (opts && opts.update) {
|
||||
args.push('-u');
|
||||
} else {
|
||||
args.push('-A');
|
||||
}
|
||||
|
||||
args.push('--');
|
||||
|
||||
if (paths && paths.length) {
|
||||
args.push.apply(args, paths);
|
||||
@@ -1116,15 +1128,21 @@ export class Repository {
|
||||
}
|
||||
|
||||
let mode: string;
|
||||
let add: string = '';
|
||||
|
||||
try {
|
||||
const details = await this.getObjectDetails('HEAD', path);
|
||||
mode = details.mode;
|
||||
} catch (err) {
|
||||
if (err.gitErrorCode !== GitErrorCodes.UnknownPath) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
mode = '100644';
|
||||
add = '--add';
|
||||
}
|
||||
|
||||
await this.run(['update-index', '--cacheinfo', mode, hash, path]);
|
||||
await this.run(['update-index', add, '--cacheinfo', mode, hash, path]);
|
||||
}
|
||||
|
||||
async checkout(treeish: string, paths: string[], opts: { track?: boolean } = Object.create(null)): Promise<void> {
|
||||
@@ -1138,13 +1156,14 @@ export class Repository {
|
||||
args.push(treeish);
|
||||
}
|
||||
|
||||
if (paths && paths.length) {
|
||||
args.push('--');
|
||||
args.push.apply(args, paths);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.run(args);
|
||||
if (paths && paths.length > 0) {
|
||||
for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) {
|
||||
await this.run([...args, '--', ...chunk]);
|
||||
}
|
||||
} else {
|
||||
await this.run(args);
|
||||
}
|
||||
} catch (err) {
|
||||
if (/Please,? commit your changes or stash them/.test(err.stderr || '')) {
|
||||
err.gitErrorCode = GitErrorCodes.DirtyWorkTree;
|
||||
@@ -1275,11 +1294,17 @@ export class Repository {
|
||||
async clean(paths: string[]): Promise<void> {
|
||||
const pathsByGroup = groupBy(paths, p => path.dirname(p));
|
||||
const groups = Object.keys(pathsByGroup).map(k => pathsByGroup[k]);
|
||||
const tasks = groups.map(paths => () => this.run(['clean', '-f', '-q', '--'].concat(paths)));
|
||||
|
||||
for (let task of tasks) {
|
||||
await task();
|
||||
const limiter = new Limiter(5);
|
||||
const promises: Promise<any>[] = [];
|
||||
|
||||
for (const paths of groups) {
|
||||
for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) {
|
||||
promises.push(limiter.queue(() => this.run(['clean', '-f', '-q', '--', ...chunk])));
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
async undo(): Promise<void> {
|
||||
@@ -1396,7 +1421,7 @@ export class Repository {
|
||||
}
|
||||
|
||||
try {
|
||||
await this.run(args);
|
||||
await this.run(args, options);
|
||||
} catch (err) {
|
||||
if (/^CONFLICT \([^)]+\): \b/m.test(err.stdout || '')) {
|
||||
err.gitErrorCode = GitErrorCodes.Conflict;
|
||||
@@ -1669,13 +1694,16 @@ export class Repository {
|
||||
async getBranch(name: string): Promise<Branch> {
|
||||
if (name === 'HEAD') {
|
||||
return this.getHEAD();
|
||||
} else if (/^@/.test(name)) {
|
||||
const symbolicFullNameResult = await this.run(['rev-parse', '--symbolic-full-name', name]);
|
||||
const symbolicFullName = symbolicFullNameResult.stdout.trim();
|
||||
name = symbolicFullName || name;
|
||||
}
|
||||
|
||||
const result = await this.run(['rev-parse', name]);
|
||||
let result = await this.run(['rev-parse', name]);
|
||||
|
||||
if (!result.stdout && /^@/.test(name)) {
|
||||
const symbolicFullNameResult = await this.run(['rev-parse', '--symbolic-full-name', name]);
|
||||
name = symbolicFullNameResult.stdout.trim();
|
||||
|
||||
result = await this.run(['rev-parse', name]);
|
||||
}
|
||||
|
||||
if (!result.stdout) {
|
||||
return Promise.reject<Branch>(new Error('No such branch'));
|
||||
@@ -1732,7 +1760,7 @@ export class Repository {
|
||||
}
|
||||
|
||||
const raw = await readfile(templatePath, 'utf8');
|
||||
return raw.replace(/^\s*#.*$\n?/gm, '').trim();
|
||||
return raw.replace(/\n?#.*/g, '');
|
||||
|
||||
} catch (err) {
|
||||
return '';
|
||||
@@ -1745,8 +1773,11 @@ export class Repository {
|
||||
}
|
||||
|
||||
async updateSubmodules(paths: string[]): Promise<void> {
|
||||
const args = ['submodule', 'update', '--', ...paths];
|
||||
await this.run(args);
|
||||
const args = ['submodule', 'update', '--'];
|
||||
|
||||
for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) {
|
||||
await this.run([...args, ...chunk]);
|
||||
}
|
||||
}
|
||||
|
||||
async getSubmodules(): Promise<Submodule[]> {
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType, OutputChannel, LogLevel, env } from 'vscode';
|
||||
import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType, OutputChannel, LogLevel, env, ProgressOptions, CancellationToken } from 'vscode';
|
||||
import { Repository as BaseRepository, Commit, Stash, GitError, Submodule, CommitOptions, ForcePushMode } from './git';
|
||||
import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent, combinedDisposable, watch, IFileWatcher } from './util';
|
||||
import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent, combinedDisposable } from './util';
|
||||
import { memoize, throttle, debounce } from './decorators';
|
||||
import { toGitUri } from './uri';
|
||||
import { AutoFetcher } from './autofetch';
|
||||
@@ -14,6 +14,7 @@ import * as nls from 'vscode-nls';
|
||||
import * as fs from 'fs';
|
||||
import { StatusBarCommands } from './statusbar';
|
||||
import { Branch, Ref, Remote, RefType, GitErrorCodes, Status, LogOptions, Change } from './api/git';
|
||||
import { IFileWatcher, watch } from './watch';
|
||||
|
||||
const timeout = (millis: number) => new Promise(c => setTimeout(c, millis));
|
||||
|
||||
@@ -678,12 +679,15 @@ export class Repository implements Disposable {
|
||||
|
||||
const root = Uri.file(repository.root);
|
||||
this._sourceControl = scm.createSourceControl('git', 'Git', root);
|
||||
this._sourceControl.inputBox.placeholder = localize('commitMessage', "Message (press {0} to commit)");
|
||||
this._sourceControl.acceptInputCommand = { command: 'git.commitWithInput', title: localize('commit', "Commit"), arguments: [this._sourceControl] };
|
||||
|
||||
this._sourceControl.acceptInputCommand = { command: 'git.commit', title: localize('commit', "Commit"), arguments: [this._sourceControl] };
|
||||
this._sourceControl.quickDiffProvider = this;
|
||||
this._sourceControl.inputBox.validateInput = this.validateInput.bind(this);
|
||||
this.disposables.push(this._sourceControl);
|
||||
|
||||
this.updateInputBoxPlaceholder();
|
||||
this.disposables.push(this.onDidRunGitStatus(() => this.updateInputBoxPlaceholder()));
|
||||
|
||||
this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "MERGE CHANGES"));
|
||||
this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "STAGED CHANGES"));
|
||||
this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "CHANGES"));
|
||||
@@ -723,6 +727,10 @@ export class Repository implements Disposable {
|
||||
const progressManager = new ProgressManager(this);
|
||||
this.disposables.push(progressManager);
|
||||
|
||||
const onDidChangeCountBadge = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.countBadge', root));
|
||||
onDidChangeCountBadge(this.setCountBadge, this, this.disposables);
|
||||
this.setCountBadge();
|
||||
|
||||
this.updateCommitTemplate();
|
||||
}
|
||||
|
||||
@@ -905,7 +913,8 @@ export class Repository implements Disposable {
|
||||
if (this.rebaseCommit) {
|
||||
await this.run(Operation.RebaseContinue, async () => {
|
||||
if (opts.all) {
|
||||
await this.repository.add([]);
|
||||
const addOpts = opts.all === 'tracked' ? { update: true } : {};
|
||||
await this.repository.add([], addOpts);
|
||||
}
|
||||
|
||||
await this.repository.rebaseContinue();
|
||||
@@ -913,9 +922,11 @@ export class Repository implements Disposable {
|
||||
} else {
|
||||
await this.run(Operation.Commit, async () => {
|
||||
if (opts.all) {
|
||||
await this.repository.add([]);
|
||||
const addOpts = opts.all === 'tracked' ? { update: true } : {};
|
||||
await this.repository.add([], addOpts);
|
||||
}
|
||||
|
||||
delete opts.all;
|
||||
await this.repository.commit(message, opts);
|
||||
});
|
||||
}
|
||||
@@ -956,21 +967,9 @@ export class Repository implements Disposable {
|
||||
}
|
||||
});
|
||||
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
if (toClean.length > 0) {
|
||||
promises.push(this.repository.clean(toClean));
|
||||
}
|
||||
|
||||
if (toCheckout.length > 0) {
|
||||
promises.push(this.repository.checkout('', toCheckout));
|
||||
}
|
||||
|
||||
if (submodulesToUpdate.length > 0) {
|
||||
promises.push(this.repository.updateSubmodules(submodulesToUpdate));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
await this.repository.clean(toClean);
|
||||
await this.repository.checkout('', toCheckout);
|
||||
await this.repository.updateSubmodules(submodulesToUpdate);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1146,11 +1145,22 @@ export class Repository implements Disposable {
|
||||
const config = workspace.getConfiguration('git', Uri.file(this.root));
|
||||
const fetchOnPull = config.get<boolean>('fetchOnPull');
|
||||
const tags = config.get<boolean>('pullTags');
|
||||
const supportCancellation = config.get<boolean>('supportCancellation');
|
||||
|
||||
if (fetchOnPull) {
|
||||
await this.repository.pull(rebase, undefined, undefined, { tags });
|
||||
const fn = fetchOnPull
|
||||
? async (cancellationToken?: CancellationToken) => await this.repository.pull(rebase, undefined, undefined, { tags, cancellationToken })
|
||||
: async (cancellationToken?: CancellationToken) => await this.repository.pull(rebase, remoteName, pullBranch, { tags, cancellationToken });
|
||||
|
||||
if (supportCancellation) {
|
||||
const opts: ProgressOptions = {
|
||||
location: ProgressLocation.Notification,
|
||||
title: localize('sync is unpredictable', "Syncing. Cancelling may cause serious damages to the repository"),
|
||||
cancellable: true
|
||||
};
|
||||
|
||||
await window.withProgress(opts, (_, token) => fn(token));
|
||||
} else {
|
||||
await this.repository.pull(rebase, remoteName, pullBranch, { tags });
|
||||
await fn();
|
||||
}
|
||||
|
||||
const remote = this.remotes.find(r => r.name === remoteName);
|
||||
@@ -1495,15 +1505,7 @@ export class Repository implements Disposable {
|
||||
this.workingTreeGroup.resourceStates = workingTree;
|
||||
|
||||
// set count badge
|
||||
const countBadge = workspace.getConfiguration('git').get<string>('countBadge');
|
||||
let count = merge.length + index.length + workingTree.length;
|
||||
|
||||
switch (countBadge) {
|
||||
case 'off': count = 0; break;
|
||||
case 'tracked': count = count - workingTree.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED).length; break;
|
||||
}
|
||||
|
||||
this._sourceControl.count = count;
|
||||
this.setCountBadge();
|
||||
|
||||
// Disable `Discard All Changes` for "fresh" repositories
|
||||
// https://github.com/Microsoft/vscode/issues/43066
|
||||
@@ -1517,6 +1519,18 @@ export class Repository implements Disposable {
|
||||
this._onDidChangeStatus.fire();
|
||||
}
|
||||
|
||||
private setCountBadge(): void {
|
||||
const countBadge = workspace.getConfiguration('git').get<string>('countBadge');
|
||||
let count = this.mergeGroup.resourceStates.length + this.indexGroup.resourceStates.length + this.workingTreeGroup.resourceStates.length;
|
||||
|
||||
switch (countBadge) {
|
||||
case 'off': count = 0; break;
|
||||
case 'tracked': count = count - this.workingTreeGroup.resourceStates.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED).length; break;
|
||||
}
|
||||
|
||||
this._sourceControl.count = count;
|
||||
}
|
||||
|
||||
private async getRebaseCommit(): Promise<Commit | undefined> {
|
||||
const rebaseHeadPath = path.join(this.repository.root, '.git', 'REBASE_HEAD');
|
||||
const rebaseApplyPath = path.join(this.repository.root, '.git', 'rebase-apply');
|
||||
@@ -1638,6 +1652,21 @@ export class Repository implements Disposable {
|
||||
return `${this.HEAD.behind}↓ ${this.HEAD.ahead}↑`;
|
||||
}
|
||||
|
||||
private updateInputBoxPlaceholder(): void {
|
||||
const HEAD = this.HEAD;
|
||||
|
||||
if (HEAD) {
|
||||
const tag = this.refs.filter(iref => iref.type === RefType.Tag && iref.commit === HEAD.commit)[0];
|
||||
const tagName = tag && tag.name;
|
||||
const head = HEAD.name || tagName || (HEAD.commit || '').substr(0, 8);
|
||||
|
||||
// '{0}' will be replaced by the corresponding key-command later in the process, which is why it needs to stay.
|
||||
this._sourceControl.inputBox.placeholder = localize('commitMessageWithHeadLabel', "Message ({0} to commit on '{1}')", "{0}", head);
|
||||
} else {
|
||||
this._sourceControl.inputBox.placeholder = localize('commitMessage', "Message ({0} to commit)");
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Disposable, Command, EventEmitter, Event, workspace, Uri } from 'vscode';
|
||||
import { Repository, Operation } from './repository';
|
||||
import { anyEvent, dispose } from './util';
|
||||
import { anyEvent, dispose, filterEvent } from './util';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Branch } from './api/git';
|
||||
|
||||
@@ -27,7 +27,7 @@ class CheckoutStatusBar {
|
||||
|
||||
return {
|
||||
command: 'git.checkout',
|
||||
tooltip: localize('checkout', 'Checkout...'),
|
||||
tooltip: `${this.repository.headLabel}`,
|
||||
title,
|
||||
arguments: [this.repository.sourceControl]
|
||||
};
|
||||
@@ -39,6 +39,7 @@ class CheckoutStatusBar {
|
||||
}
|
||||
|
||||
interface SyncStatusBarState {
|
||||
enabled: boolean;
|
||||
isSyncRunning: boolean;
|
||||
hasRemotes: boolean;
|
||||
HEAD: Branch | undefined;
|
||||
@@ -47,6 +48,7 @@ interface SyncStatusBarState {
|
||||
class SyncStatusBar {
|
||||
|
||||
private static StartState: SyncStatusBarState = {
|
||||
enabled: true,
|
||||
isSyncRunning: false,
|
||||
hasRemotes: false,
|
||||
HEAD: undefined
|
||||
@@ -66,9 +68,20 @@ class SyncStatusBar {
|
||||
constructor(private repository: Repository) {
|
||||
repository.onDidRunGitStatus(this.onModelChange, this, this.disposables);
|
||||
repository.onDidChangeOperations(this.onOperationsChange, this, this.disposables);
|
||||
|
||||
const onEnablementChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.enableStatusBarSync'));
|
||||
onEnablementChange(this.updateEnablement, this, this.disposables);
|
||||
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
private updateEnablement(): void {
|
||||
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
|
||||
const enabled = config.get<boolean>('enableStatusBarSync', true);
|
||||
|
||||
this.state = { ... this.state, enabled };
|
||||
}
|
||||
|
||||
private onOperationsChange(): void {
|
||||
const isSyncRunning = this.repository.operations.isRunning(Operation.Sync) ||
|
||||
this.repository.operations.isRunning(Operation.Push) ||
|
||||
@@ -86,7 +99,7 @@ class SyncStatusBar {
|
||||
}
|
||||
|
||||
get command(): Command | undefined {
|
||||
if (!this.state.hasRemotes) {
|
||||
if (!this.state.enabled || !this.state.hasRemotes) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import 'mocha';
|
||||
import { GitStatusParser, parseGitCommit, parseGitmodules, parseLsTree, parseLsFiles } from '../git';
|
||||
import * as assert from 'assert';
|
||||
import { splitInChunks } from '../util';
|
||||
|
||||
suite('git', () => {
|
||||
suite('GitStatusParser', () => {
|
||||
@@ -292,4 +293,78 @@ This is a commit message.`;
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('splitInChunks', () => {
|
||||
test('unit tests', function () {
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['hello', 'there', 'cool', 'stuff'], 6)],
|
||||
[['hello'], ['there'], ['cool'], ['stuff']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['hello', 'there', 'cool', 'stuff'], 10)],
|
||||
[['hello', 'there'], ['cool', 'stuff']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['hello', 'there', 'cool', 'stuff'], 12)],
|
||||
[['hello', 'there'], ['cool', 'stuff']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['hello', 'there', 'cool', 'stuff'], 14)],
|
||||
[['hello', 'there', 'cool'], ['stuff']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['hello', 'there', 'cool', 'stuff'], 2000)],
|
||||
[['hello', 'there', 'cool', 'stuff']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 1)],
|
||||
[['0'], ['01'], ['012'], ['0'], ['01'], ['012'], ['0'], ['01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 2)],
|
||||
[['0'], ['01'], ['012'], ['0'], ['01'], ['012'], ['0'], ['01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 3)],
|
||||
[['0', '01'], ['012'], ['0', '01'], ['012'], ['0', '01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 4)],
|
||||
[['0', '01'], ['012', '0'], ['01'], ['012', '0'], ['01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 5)],
|
||||
[['0', '01'], ['012', '0'], ['01', '012'], ['0', '01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 6)],
|
||||
[['0', '01', '012'], ['0', '01', '012'], ['0', '01', '012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 7)],
|
||||
[['0', '01', '012', '0'], ['01', '012', '0'], ['01', '012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 8)],
|
||||
[['0', '01', '012', '0'], ['01', '012', '0', '01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 9)],
|
||||
[['0', '01', '012', '0', '01'], ['012', '0', '01', '012']]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, EventEmitter, Uri } from 'vscode';
|
||||
import { dirname, sep, join } from 'path';
|
||||
import { Event } from 'vscode';
|
||||
import { dirname, sep } from 'path';
|
||||
import { Readable } from 'stream';
|
||||
import * as fs from 'fs';
|
||||
import * as byline from 'byline';
|
||||
@@ -345,18 +345,69 @@ export function pathEquals(a: string, b: string): boolean {
|
||||
return a === b;
|
||||
}
|
||||
|
||||
export interface IFileWatcher extends IDisposable {
|
||||
readonly event: Event<Uri>;
|
||||
export function* splitInChunks(array: string[], maxChunkLength: number): IterableIterator<string[]> {
|
||||
let current: string[] = [];
|
||||
let length = 0;
|
||||
|
||||
for (const value of array) {
|
||||
let newLength = length + value.length;
|
||||
|
||||
if (newLength > maxChunkLength && current.length > 0) {
|
||||
yield current;
|
||||
current = [];
|
||||
newLength = value.length;
|
||||
}
|
||||
|
||||
current.push(value);
|
||||
length = newLength;
|
||||
}
|
||||
|
||||
if (current.length > 0) {
|
||||
yield current;
|
||||
}
|
||||
}
|
||||
|
||||
export function watch(location: string): IFileWatcher {
|
||||
const dotGitWatcher = fs.watch(location);
|
||||
const onDotGitFileChangeEmitter = new EventEmitter<Uri>();
|
||||
dotGitWatcher.on('change', (_, e) => onDotGitFileChangeEmitter.fire(Uri.file(join(location, e as string))));
|
||||
dotGitWatcher.on('error', err => console.error(err));
|
||||
|
||||
return new class implements IFileWatcher {
|
||||
event = onDotGitFileChangeEmitter.event;
|
||||
dispose() { dotGitWatcher.close(); }
|
||||
};
|
||||
interface ILimitedTaskFactory<T> {
|
||||
factory: () => Promise<T>;
|
||||
c: (value?: T | Promise<T>) => void;
|
||||
e: (error?: any) => void;
|
||||
}
|
||||
|
||||
export class Limiter<T> {
|
||||
|
||||
private runningPromises: number;
|
||||
private maxDegreeOfParalellism: number;
|
||||
private outstandingPromises: ILimitedTaskFactory<T>[];
|
||||
|
||||
constructor(maxDegreeOfParalellism: number) {
|
||||
this.maxDegreeOfParalellism = maxDegreeOfParalellism;
|
||||
this.outstandingPromises = [];
|
||||
this.runningPromises = 0;
|
||||
}
|
||||
|
||||
queue(factory: () => Promise<T>): Promise<T> {
|
||||
return new Promise<T>((c, e) => {
|
||||
this.outstandingPromises.push({ factory, c, e });
|
||||
this.consume();
|
||||
});
|
||||
}
|
||||
|
||||
private consume(): void {
|
||||
while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) {
|
||||
const iLimitedTask = this.outstandingPromises.shift()!;
|
||||
this.runningPromises++;
|
||||
|
||||
const promise = iLimitedTask.factory();
|
||||
promise.then(iLimitedTask.c, iLimitedTask.e);
|
||||
promise.then(() => this.consumed(), () => this.consumed());
|
||||
}
|
||||
}
|
||||
|
||||
private consumed(): void {
|
||||
this.runningPromises--;
|
||||
|
||||
if (this.outstandingPromises.length > 0) {
|
||||
this.consume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
25
extensions/git/src/watch.ts
Normal file
25
extensions/git/src/watch.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, EventEmitter, Uri } from 'vscode';
|
||||
import { join } from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { IDisposable } from './util';
|
||||
|
||||
export interface IFileWatcher extends IDisposable {
|
||||
readonly event: Event<Uri>;
|
||||
}
|
||||
|
||||
export function watch(location: string): IFileWatcher {
|
||||
const dotGitWatcher = fs.watch(location);
|
||||
const onDotGitFileChangeEmitter = new EventEmitter<Uri>();
|
||||
dotGitWatcher.on('change', (_, e) => onDotGitFileChangeEmitter.fire(Uri.file(join(location, e as string))));
|
||||
dotGitWatcher.on('error', err => console.error(err));
|
||||
|
||||
return new class implements IFileWatcher {
|
||||
event = onDotGitFileChangeEmitter.event;
|
||||
dispose() { dotGitWatcher.close(); }
|
||||
};
|
||||
}
|
||||
@@ -223,7 +223,7 @@ export function activate(context: ExtensionContext) {
|
||||
let languageConfiguration: LanguageConfiguration = {
|
||||
wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/,
|
||||
indentationRules: {
|
||||
increaseIndentPattern: /^.*(\{[^}]*|\[[^\]]*)$/,
|
||||
increaseIndentPattern: /({+(?=([^"]*"[^"]*")*[^"}]*$))|(\[+(?=([^"]*"[^"]*")*[^"\]]*$))/,
|
||||
decreaseIndentPattern: /^\s*[}\]],?\s*$/
|
||||
}
|
||||
};
|
||||
|
||||
@@ -50,7 +50,8 @@
|
||||
".babelrc",
|
||||
".jsonc",
|
||||
".eslintrc",
|
||||
".eslintrc.json"
|
||||
".eslintrc.json",
|
||||
"tslint.json"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
}
|
||||
@@ -74,4 +75,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2566,4 +2566,4 @@
|
||||
"name": "markup.inline.raw.string.markdown"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.0.1",
|
||||
"description": "Dependencies shared by all extensions",
|
||||
"dependencies": {
|
||||
"typescript": "3.5.2"
|
||||
"typescript": "3.6.0-dev.20190810"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "node ./postinstall"
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
"type": "git",
|
||||
"git": {
|
||||
"name": "PowerShell/EditorSyntax",
|
||||
"repositoryUrl": "https://github.com/powershell/editorsyntax",
|
||||
"commitHash": "12b7d7257eb493e45a9af0af9094ec0c2a996712"
|
||||
"repositoryUrl": "https://github.com/PowerShell/EditorSyntax",
|
||||
"commitHash": "d10ae29c0d3ceb248172c383a159ae43b9ccfb4d"
|
||||
}
|
||||
},
|
||||
"license": "MIT",
|
||||
"version": "0.0.0"
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/PowerShell/EditorSyntax/commit/44eac8702f3cbe55a4ec70c1fdb163d42b4162fc",
|
||||
"version": "https://github.com/PowerShell/EditorSyntax/commit/d10ae29c0d3ceb248172c383a159ae43b9ccfb4d",
|
||||
"name": "PowerShell",
|
||||
"scopeName": "source.powershell",
|
||||
"patterns": [
|
||||
@@ -670,7 +670,7 @@
|
||||
}
|
||||
},
|
||||
"comment": "Style preference variables as language variables so that they stand out.",
|
||||
"match": "(\\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|ProgressPreference|PsCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|VerbosePreference|WarningPreference|WhatIfPreference))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?\\b"
|
||||
"match": "(\\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|InformationPreference|LogCommandHealthEvent|LogCommandLifecycleEvent|LogEngineHealthEvent|LogEngineLifecycleEvent|LogProviderHealthEvent|LogProviderLifecycleEvent|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|PSCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoLoadingPreference|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|ProgressPreference|VerbosePreference|WarningPreference|WhatIfPreference))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?\\b"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
@@ -848,7 +848,7 @@
|
||||
}
|
||||
},
|
||||
"comment": "Style preference variables as language variables so that they stand out.",
|
||||
"match": "(\\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|ProgressPreference|PsCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|VerbosePreference|WarningPreference|WhatIfPreference))\\b"
|
||||
"match": "(\\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|InformationPreference|LogCommandHealthEvent|LogCommandLifecycleEvent|LogEngineHealthEvent|LogEngineLifecycleEvent|LogProviderHealthEvent|LogProviderLifecycleEvent|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|PSCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoLoadingPreference|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|ProgressPreference|VerbosePreference|WarningPreference|WhatIfPreference))\\b"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
|
||||
@@ -465,11 +465,11 @@
|
||||
"c": ".IsInRole",
|
||||
"t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell variable.other.readwrite.powershell variable.other.member.powershell",
|
||||
"r": {
|
||||
"dark_plus": "variable: #9CDCFE",
|
||||
"light_plus": "variable: #001080",
|
||||
"dark_plus": "source.powershell variable.other.member: #DCDCAA",
|
||||
"light_plus": "source.powershell variable.other.member: #795E26",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "variable: #9CDCFE"
|
||||
"hc_black": "source.powershell variable.other.member: #DCDCAA"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"scope": [
|
||||
"entity.name.function",
|
||||
"support.function",
|
||||
"support.constant.handlebars"
|
||||
"support.constant.handlebars",
|
||||
"source.powershell variable.other.member"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#DCDCAA"
|
||||
@@ -171,4 +172,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"scope": [
|
||||
"entity.name.function",
|
||||
"support.function",
|
||||
"support.constant.handlebars"
|
||||
"support.constant.handlebars",
|
||||
"source.powershell variable.other.member"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#DCDCAA"
|
||||
@@ -115,4 +116,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"scope": [
|
||||
"entity.name.function",
|
||||
"support.function",
|
||||
"support.constant.handlebars"
|
||||
"support.constant.handlebars",
|
||||
"source.powershell variable.other.member"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#795E26"
|
||||
@@ -172,4 +173,4 @@
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
typescript@3.5.2:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.2.tgz#a09e1dc69bc9551cadf17dba10ee42cf55e5d56c"
|
||||
integrity sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==
|
||||
typescript@3.6.0-dev.20190810:
|
||||
version "3.6.0-dev.20190810"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.0-dev.20190810.tgz#dda80279480131eec9b05e3b78182a1ba1efe105"
|
||||
integrity sha512-gubcQ8Sn2G5AO1KhjvLpoFrutV7o/ZJ7wCDBC1IKgNI8R2vadIxTystJxAFqkb9boQ7tyRrZ6FwM5EL5ZYfJrg==
|
||||
|
||||
Reference in New Issue
Block a user