mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Merge from vscode 071a5cf16fc999727acc31c790d78f750fa4b166 (#5261)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ts = require("typescript");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*----------------------------------------------------------
|
||||
|
||||
@@ -1211,6 +1211,16 @@ export interface Command {
|
||||
arguments?: any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface CommentThreadTemplate {
|
||||
label: string;
|
||||
acceptInputCommand?: Command;
|
||||
additionalCommands?: Command[];
|
||||
deleteCommand?: Command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@@ -1220,6 +1230,7 @@ export interface CommentInfo {
|
||||
commentingRanges?: (IRange[] | CommentingRanges);
|
||||
reply?: Command;
|
||||
draftMode?: DraftMode;
|
||||
template?: CommentThreadTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1298,8 +1309,7 @@ export interface CommentThread2 {
|
||||
export interface CommentingRanges {
|
||||
readonly resource: URI;
|
||||
ranges: IRange[];
|
||||
newCommentThreadCommand?: Command;
|
||||
newCommentThreadCallback?: (uri: UriComponents, range: IRange) => Promise<void>;
|
||||
newCommentThreadCallback?: (uri: UriComponents, range: IRange) => Promise<CommentThread | undefined>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,7 +19,6 @@ import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { SnippetSession } from './snippetSession';
|
||||
import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState';
|
||||
|
||||
export class SnippetController2 implements IEditorContribution {
|
||||
|
||||
@@ -114,26 +113,10 @@ export class SnippetController2 implements IEditorContribution {
|
||||
|
||||
this._updateState();
|
||||
|
||||
// we listen on model and selection changes. usually
|
||||
// both events come in together and this is to prevent
|
||||
// that we don't call _updateState twice.
|
||||
let state: EditorState;
|
||||
let dedupedUpdateState = () => {
|
||||
if (!state || !state.validate(this._editor)) {
|
||||
this._updateState();
|
||||
state = new EditorState(this._editor, CodeEditorStateFlag.Selection | CodeEditorStateFlag.Value);
|
||||
}
|
||||
};
|
||||
this._snippetListener = [
|
||||
this._editor.onDidChangeModelContent(e => {
|
||||
if (e.isFlush) {
|
||||
this.cancel();
|
||||
} else {
|
||||
setTimeout(dedupedUpdateState, 0);
|
||||
}
|
||||
}),
|
||||
this._editor.onDidChangeCursorSelection(dedupedUpdateState),
|
||||
this._editor.onDidChangeModelContent(e => e.isFlush && this.cancel()),
|
||||
this._editor.onDidChangeModel(() => this.cancel()),
|
||||
this._editor.onDidChangeCursorSelection(() => this._updateState())
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -198,6 +198,10 @@ export class OneSnippet {
|
||||
let ranges: Range[] | undefined;
|
||||
|
||||
for (const placeholder of placeholdersWithEqualIndex) {
|
||||
if (placeholder.isFinalTabstop) {
|
||||
// ignore those
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ranges) {
|
||||
ranges = [];
|
||||
@@ -571,12 +575,6 @@ export class SnippetSession {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (allPossibleSelections.has(0)) {
|
||||
// selection overlaps with a final tab stop which means
|
||||
// we done
|
||||
return false;
|
||||
}
|
||||
|
||||
// add selections from 'this' snippet so that we know all
|
||||
// selections for this placeholder
|
||||
allPossibleSelections.forEach((array, index) => {
|
||||
|
||||
@@ -11,7 +11,6 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { Handler } from 'vs/editor/common/editorCommon';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
suite('SnippetController2', function () {
|
||||
|
||||
@@ -134,11 +133,11 @@ suite('SnippetController2', function () {
|
||||
assertSelections(editor, new Selection(1, 1, 1, 7), new Selection(2, 5, 2, 11));
|
||||
|
||||
editor.trigger('test', 'cut', {});
|
||||
assertContextKeys(contextKeys, false, false, false);
|
||||
assertContextKeys(contextKeys, true, false, true);
|
||||
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
|
||||
|
||||
editor.trigger('test', 'type', { text: 'abc' });
|
||||
assertContextKeys(contextKeys, false, false, false);
|
||||
assertContextKeys(contextKeys, true, false, true);
|
||||
|
||||
ctrl.next();
|
||||
assertContextKeys(contextKeys, false, false, false);
|
||||
@@ -160,9 +159,9 @@ suite('SnippetController2', function () {
|
||||
assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8));
|
||||
assertContextKeys(contextKeys, true, false, true);
|
||||
|
||||
// ctrl.next();
|
||||
// assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
|
||||
// assertContextKeys(contextKeys, true, true, true);
|
||||
ctrl.next();
|
||||
assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
|
||||
assertContextKeys(contextKeys, true, true, true);
|
||||
|
||||
ctrl.next();
|
||||
assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
|
||||
@@ -177,10 +176,10 @@ suite('SnippetController2', function () {
|
||||
|
||||
ctrl.insert('farboo');
|
||||
assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
|
||||
// assertContextKeys(contextKeys, true, false, true);
|
||||
assertContextKeys(contextKeys, true, false, true);
|
||||
|
||||
// ctrl.next();
|
||||
// assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
|
||||
ctrl.next();
|
||||
assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
|
||||
assertContextKeys(contextKeys, false, false, false);
|
||||
});
|
||||
|
||||
@@ -403,22 +402,12 @@ suite('SnippetController2', function () {
|
||||
assertSelections(editor, new Selection(1, 22, 1, 22));
|
||||
});
|
||||
|
||||
test('A little confusing visual effect of highlighting for snippet tabstop #43270', async function () {
|
||||
test('User defined snippet tab stops ignored #72862', function () {
|
||||
const ctrl = new SnippetController2(editor, logService, contextKeys);
|
||||
model.setValue('');
|
||||
editor.setSelection(new Selection(1, 1, 1, 1));
|
||||
|
||||
ctrl.insert('background-color: ${1:fff};$0');
|
||||
assertSelections(editor, new Selection(1, 19, 1, 22));
|
||||
|
||||
editor.setSelection(new Selection(1, 22, 1, 22));
|
||||
ctrl.insert('export default $1');
|
||||
assertContextKeys(contextKeys, true, false, true);
|
||||
editor.trigger('', 'deleteRight', null);
|
||||
|
||||
assert.equal(model.getValue(), 'background-color: fff');
|
||||
|
||||
await timeout(0); // this depends on re-scheduling of events...
|
||||
|
||||
assertContextKeys(contextKeys, false, false, false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -331,7 +331,7 @@ suite('SnippetSession', function () {
|
||||
|
||||
// reset selection to placeholder
|
||||
session.next();
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), false);
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), true);
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(1, 13, 1, 13), new Selection(2, 17, 2, 17));
|
||||
});
|
||||
|
||||
107
src/vs/vscode.proposed.d.ts
vendored
107
src/vs/vscode.proposed.d.ts
vendored
@@ -814,28 +814,35 @@ declare module 'vscode' {
|
||||
/**
|
||||
* A comment is displayed within the editor or the Comments Panel, depending on how it is provided.
|
||||
*/
|
||||
export interface Comment {
|
||||
export class Comment {
|
||||
/**
|
||||
* The id of the comment
|
||||
*/
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* The id of the comment
|
||||
*
|
||||
* @deprecated Use Id instead
|
||||
*/
|
||||
readonly commentId: string;
|
||||
|
||||
/**
|
||||
* The text of the comment
|
||||
* The human-readable comment body
|
||||
*/
|
||||
readonly body: MarkdownString;
|
||||
|
||||
/**
|
||||
* The display name of the user who created the comment
|
||||
*/
|
||||
readonly userName: string;
|
||||
|
||||
/**
|
||||
* Optional label describing the [Comment](#Comment)
|
||||
* Label will be rendered next to userName if exists.
|
||||
*/
|
||||
readonly label?: string;
|
||||
|
||||
/**
|
||||
* The display name of the user who created the comment
|
||||
*/
|
||||
readonly userName: string;
|
||||
|
||||
/**
|
||||
* The icon path for the user who created the comment
|
||||
*/
|
||||
@@ -896,6 +903,13 @@ declare module 'vscode' {
|
||||
* Proposed Comment Reaction
|
||||
*/
|
||||
commentReactions?: CommentReaction[];
|
||||
|
||||
/**
|
||||
* @param id The id of the comment
|
||||
* @param body The human-readable comment body
|
||||
* @param userName The display name of the user who created the comment
|
||||
*/
|
||||
constructor(id: string, body: MarkdownString, userName: string);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -925,6 +939,7 @@ declare module 'vscode' {
|
||||
|
||||
/**
|
||||
* Comment Reactions
|
||||
* Stay in proposed.
|
||||
*/
|
||||
interface CommentReaction {
|
||||
readonly label?: string;
|
||||
@@ -1006,6 +1021,9 @@ declare module 'vscode' {
|
||||
value: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stay in proposed
|
||||
*/
|
||||
export interface CommentReactionProvider {
|
||||
availableReactions: CommentReaction[];
|
||||
toggleReaction?(document: TextDocument, comment: Comment, reaction: CommentReaction): Promise<void>;
|
||||
@@ -1016,12 +1034,50 @@ declare module 'vscode' {
|
||||
* Provide a list of ranges which allow new comment threads creation or null for a given document
|
||||
*/
|
||||
provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult<Range[]>;
|
||||
}
|
||||
|
||||
export interface CommentThreadTemplate {
|
||||
/**
|
||||
* The human-readable label describing the [Comment Thread](#CommentThread)
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* The method `createEmptyCommentThread` is called when users attempt to create new comment thread from the gutter or command palette.
|
||||
* Extensions still need to call `createCommentThread` inside this call when appropriate.
|
||||
* Optional accept input command
|
||||
*
|
||||
* `acceptInputCommand` is the default action rendered on Comment Widget, which is always placed rightmost.
|
||||
* This command will be invoked when users the user accepts the value in the comment editor.
|
||||
* This command will disabled when the comment editor is empty.
|
||||
*/
|
||||
createEmptyCommentThread(document: TextDocument, range: Range): ProviderResult<void>;
|
||||
acceptInputCommand?: Command;
|
||||
|
||||
/**
|
||||
* Optional additonal commands.
|
||||
*
|
||||
* `additionalCommands` are the secondary actions rendered on Comment Widget.
|
||||
*/
|
||||
additionalCommands?: Command[];
|
||||
|
||||
/**
|
||||
* The command to be executed when users try to delete the comment thread. Currently, this is only called
|
||||
* when the user collapses a comment thread that has no comments in it.
|
||||
*/
|
||||
deleteCommand?: Command;
|
||||
}
|
||||
|
||||
export interface EmptyCommentThreadFactory {
|
||||
template: CommentThreadTemplate;
|
||||
/**
|
||||
* When users attempt to create new comment thread from the gutter or command palette, `template` will be used first to create the Comment Thread Widget in the editor for users to start comment drafting.
|
||||
* Then `createEmptyCommentThread` is called after that. Extensions should still call [`createCommentThread`](CommentController.createCommentThread) to create a real [`CommentThread`](#CommentThread)
|
||||
* Extensions still need to call `createCommentThread` inside this call when appropriate.
|
||||
*
|
||||
* @param document The document in which users attempt to create a new comment thread
|
||||
* @param range The range the comment threadill located within the document.
|
||||
*
|
||||
* @returns commentThread The [`CommentThread`](#CommentThread) created by extensions
|
||||
*/
|
||||
createEmptyCommentThread(document: TextDocument, range: Range): ProviderResult<CommentThread>;
|
||||
}
|
||||
|
||||
export interface CommentController {
|
||||
@@ -1036,23 +1092,44 @@ declare module 'vscode' {
|
||||
readonly label: string;
|
||||
|
||||
/**
|
||||
* The active (focused) [comment input box](#CommentInputBox).
|
||||
* The active [comment input box](#CommentInputBox) or `undefined`. The active `inputBox` is the input box of
|
||||
* the comment thread widget that currently has focus. It's `undefined` when the focus is not in any CommentInputBox.
|
||||
*/
|
||||
readonly inputBox?: CommentInputBox;
|
||||
readonly inputBox: CommentInputBox | undefined;
|
||||
|
||||
/**
|
||||
* Create a [CommentThread](#CommentThread)
|
||||
* The active [comment thread](#CommentThread) or `undefined`. The `activeCommentThread` is the comment thread of
|
||||
* the comment thread widget that currently has focus. It's `undefined` when the focus is not in any comment thread widget.
|
||||
*/
|
||||
readonly activeCommentThread: CommentThread | undefined;
|
||||
|
||||
/**
|
||||
* Create a [CommentThread](#CommentThread). The comment thread will be displayed in visible text editors (if the resource matches)
|
||||
* and Comments Panel.
|
||||
* @param id An `id` for the comment thread.
|
||||
* @param resource The uri of the document the thread has been created on.
|
||||
* @param range The range the comment thread is located within the document.
|
||||
* @param comments The ordered comments of the thread.
|
||||
*/
|
||||
createCommentThread(id: string, resource: Uri, range: Range, comments: Comment[]): CommentThread;
|
||||
|
||||
/**
|
||||
* Optional commenting range provider.
|
||||
* Provide a list [ranges](#Range) which support commenting to any given resource uri.
|
||||
* Optional commenting range provider. Provide a list [ranges](#Range) which support commenting to any given resource uri.
|
||||
*
|
||||
* If not provided and `emptyCommentThreadFactory` exits, users can leave comments in any document opened in the editor.
|
||||
*/
|
||||
commentingRangeProvider?: CommentingRangeProvider;
|
||||
|
||||
/**
|
||||
* Optional empty comment thread factory. It's necessary for supporting users to trigger Comment Thread creation from the editor or command palette.
|
||||
*
|
||||
* If not provided, users won't be able to trigger new comment thread creation from the editor gutter area or command palette.
|
||||
*/
|
||||
emptyCommentThreadFactory?: EmptyCommentThreadFactory;
|
||||
|
||||
/**
|
||||
* Optional reaction provider
|
||||
* Stay in proposed.
|
||||
*/
|
||||
reactionProvider?: CommentReactionProvider;
|
||||
|
||||
|
||||
@@ -298,12 +298,18 @@ export class MainThreadCommentController {
|
||||
);
|
||||
|
||||
this._threads.set(commentThreadHandle, thread);
|
||||
this._commentService.updateComments(this._uniqueId, {
|
||||
added: [thread],
|
||||
removed: [],
|
||||
changed: [],
|
||||
draftMode: modes.DraftMode.NotSupported
|
||||
});
|
||||
|
||||
// As we create comment thread from template and then restore from the newly created maint thread comment thread,
|
||||
// we postpone the update event to avoid duplication.
|
||||
// This can be actually removed once we are on the new API.
|
||||
setTimeout(() => {
|
||||
this._commentService.updateComments(this._uniqueId, {
|
||||
added: [thread],
|
||||
removed: [],
|
||||
changed: [],
|
||||
draftMode: modes.DraftMode.NotSupported
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return thread;
|
||||
}
|
||||
@@ -379,10 +385,17 @@ export class MainThreadCommentController {
|
||||
commentingRanges: commentingRanges ?
|
||||
{
|
||||
resource: resource, ranges: commentingRanges, newCommentThreadCallback: async (uri: UriComponents, range: IRange) => {
|
||||
await this._proxy.$createNewCommentWidgetCallback(this.handle, uri, range, token);
|
||||
let threadHandle = await this._proxy.$createNewCommentWidgetCallback(this.handle, uri, range, token);
|
||||
|
||||
if (threadHandle !== undefined) {
|
||||
return this.getKnownThread(threadHandle);
|
||||
}
|
||||
|
||||
return undefined; // {{SQL CARBON EDIT}} @anthonydresser revert back after strict-null-check
|
||||
}
|
||||
} : [],
|
||||
draftMode: modes.DraftMode.NotSupported
|
||||
draftMode: modes.DraftMode.NotSupported,
|
||||
template: this._features.commentThreadTemplate
|
||||
};
|
||||
}
|
||||
|
||||
@@ -458,6 +471,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
||||
this._proxy.$onCommentWidgetInputChange(controller.handle, this._input ? this._input.value : undefined);
|
||||
}));
|
||||
|
||||
await this._proxy.$onActiveCommentThreadChange(controller.handle, controller.activeCommentThread.commentThreadHandle);
|
||||
await this._proxy.$onCommentWidgetInputChange(controller.handle, this._input ? this._input.value : undefined);
|
||||
}));
|
||||
}
|
||||
@@ -510,8 +524,6 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
||||
return undefined;
|
||||
}
|
||||
|
||||
console.log('createCommentThread', commentThreadHandle);
|
||||
|
||||
return provider.createCommentThread(commentThreadHandle, threadId, resource, range);
|
||||
}
|
||||
|
||||
|
||||
@@ -121,11 +121,19 @@ export interface MainThreadCommandsShape extends IDisposable {
|
||||
$getCommands(): Promise<string[]>;
|
||||
}
|
||||
|
||||
export interface CommentThreadTemplate {
|
||||
label: string;
|
||||
acceptInputCommand?: modes.Command;
|
||||
additionalCommands?: modes.Command[];
|
||||
deleteCommand?: modes.Command;
|
||||
}
|
||||
|
||||
export interface CommentProviderFeatures {
|
||||
startDraftLabel?: string;
|
||||
deleteDraftLabel?: string;
|
||||
finishDraftLabel?: string;
|
||||
reactionGroup?: modes.CommentReaction[];
|
||||
commentThreadTemplate?: CommentThreadTemplate;
|
||||
}
|
||||
|
||||
export interface MainThreadCommentsShape extends IDisposable {
|
||||
@@ -1206,10 +1214,11 @@ export interface ExtHostCommentsShape {
|
||||
$provideDocumentComments(handle: number, document: UriComponents): Promise<modes.CommentInfo | null>;
|
||||
$createNewCommentThread(handle: number, document: UriComponents, range: IRange, text: string): Promise<modes.CommentThread | null>;
|
||||
$onCommentWidgetInputChange(commentControllerHandle: number, input: string | undefined): Promise<number | undefined>;
|
||||
$onActiveCommentThreadChange(commentControllerHandle: number, threadHandle: number | undefined): Promise<number | undefined>;
|
||||
$provideCommentingRanges(commentControllerHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise<IRange[] | undefined>;
|
||||
$provideReactionGroup(commentControllerHandle: number): Promise<modes.CommentReaction[] | undefined>;
|
||||
$toggleReaction(commentControllerHandle: number, threadHandle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void>;
|
||||
$createNewCommentWidgetCallback(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, token: CancellationToken): Promise<void>;
|
||||
$createNewCommentWidgetCallback(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, token: CancellationToken): Promise<number | undefined>;
|
||||
$replyToCommentThread(handle: number, document: UriComponents, range: IRange, commentThread: modes.CommentThread, text: string): Promise<modes.CommentThread | null>;
|
||||
$editComment(handle: number, document: UriComponents, comment: modes.Comment, text: string): Promise<void>;
|
||||
$deleteComment(handle: number, document: UriComponents, comment: modes.Comment): Promise<void>;
|
||||
|
||||
@@ -99,6 +99,17 @@ export class ExtHostComments implements ExtHostCommentsShape {
|
||||
return Promise.resolve(commentControllerHandle);
|
||||
}
|
||||
|
||||
$onActiveCommentThreadChange(commentControllerHandle: number, threadHandle: number): Promise<number | undefined> {
|
||||
const commentController = this._commentControllers.get(commentControllerHandle);
|
||||
|
||||
if (!commentController) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
commentController.$onActiveCommentThreadChange(threadHandle);
|
||||
return Promise.resolve(threadHandle);
|
||||
}
|
||||
|
||||
$provideCommentingRanges(commentControllerHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise<IRange[] | undefined> {
|
||||
const commentController = this._commentControllers.get(commentControllerHandle);
|
||||
|
||||
@@ -146,28 +157,34 @@ export class ExtHostComments implements ExtHostCommentsShape {
|
||||
});
|
||||
}
|
||||
|
||||
$createNewCommentWidgetCallback(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, token: CancellationToken): Promise<void> {
|
||||
$createNewCommentWidgetCallback(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, token: CancellationToken): Promise<number | undefined> {
|
||||
const commentController = this._commentControllers.get(commentControllerHandle);
|
||||
|
||||
if (!commentController) {
|
||||
return Promise.resolve();
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
if (!(commentController as any).emptyCommentThreadFactory && !(commentController.commentingRangeProvider && commentController.commentingRangeProvider.createEmptyCommentThread)) {
|
||||
return Promise.resolve();
|
||||
if (!(commentController as any).emptyCommentThreadFactory && !(commentController.commentingRangeProvider && commentController.commentingRangeProvider.createEmptyCommentThread) && !(commentController.emptyCommentThreadFactory && commentController.emptyCommentThreadFactory.createEmptyCommentThread)) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
const document = this._documents.getDocument(URI.revive(uriComponents));
|
||||
return asPromise(() => {
|
||||
// TODO, remove this once GH PR stable deprecates `emptyCommentThreadFactory`.
|
||||
if ((commentController as any).emptyCommentThreadFactory) {
|
||||
return (commentController as any).emptyCommentThreadFactory!.createEmptyCommentThread(document, extHostTypeConverter.Range.to(range));
|
||||
if (commentController.emptyCommentThreadFactory) {
|
||||
return commentController.emptyCommentThreadFactory!.createEmptyCommentThread(document, extHostTypeConverter.Range.to(range));
|
||||
}
|
||||
|
||||
if (commentController.commentingRangeProvider && commentController.commentingRangeProvider.createEmptyCommentThread) {
|
||||
return commentController.commentingRangeProvider.createEmptyCommentThread(document, extHostTypeConverter.Range.to(range));
|
||||
}
|
||||
}).then(() => Promise.resolve());
|
||||
|
||||
return undefined; // {{SQL CARBON EDIT}} @anthonydresser revert back after strict-null-check
|
||||
}).then((commentThread: ExtHostCommentThread | undefined) => {
|
||||
if (commentThread) {
|
||||
return Promise.resolve(commentThread.handle);
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
}
|
||||
|
||||
registerWorkspaceCommentProvider(
|
||||
@@ -451,6 +468,12 @@ export class ExtHostCommentThread implements vscode.CommentThread {
|
||||
|
||||
private _localDisposables: types.Disposable[];
|
||||
|
||||
private _isDiposed: boolean;
|
||||
|
||||
public get isDisposed(): boolean {
|
||||
return this._isDiposed;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private _proxy: MainThreadCommentsShape,
|
||||
private readonly _commandsConverter: CommandsConverter,
|
||||
@@ -469,6 +492,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
|
||||
);
|
||||
|
||||
this._localDisposables = [];
|
||||
this._isDiposed = false;
|
||||
|
||||
this._localDisposables.push(this.onDidUpdateCommentThread(() => {
|
||||
this.eventuallyUpdateCommentThread();
|
||||
@@ -519,6 +543,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
|
||||
this._commentController.handle,
|
||||
this.handle
|
||||
);
|
||||
this._isDiposed = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -562,7 +587,17 @@ class ExtHostCommentController implements vscode.CommentController {
|
||||
return this._label;
|
||||
}
|
||||
|
||||
public inputBox?: ExtHostCommentInputBox;
|
||||
public inputBox: ExtHostCommentInputBox | undefined;
|
||||
private _activeCommentThread: ExtHostCommentThread | undefined;
|
||||
|
||||
public get activeCommentThread(): ExtHostCommentThread | undefined {
|
||||
if (this._activeCommentThread && this._activeCommentThread.isDisposed) {
|
||||
this._activeCommentThread = undefined;
|
||||
}
|
||||
|
||||
return this._activeCommentThread;
|
||||
}
|
||||
|
||||
public activeCommentingRange?: vscode.Range;
|
||||
|
||||
public get handle(): number {
|
||||
@@ -570,7 +605,31 @@ class ExtHostCommentController implements vscode.CommentController {
|
||||
}
|
||||
|
||||
private _threads: Map<number, ExtHostCommentThread> = new Map<number, ExtHostCommentThread>();
|
||||
commentingRangeProvider?: vscode.CommentingRangeProvider;
|
||||
commentingRangeProvider?: vscode.CommentingRangeProvider & { createEmptyCommentThread: (document: vscode.TextDocument, range: types.Range) => Promise<vscode.CommentThread>; };
|
||||
|
||||
private _emptyCommentThreadFactory: vscode.EmptyCommentThreadFactory | undefined;
|
||||
get emptyCommentThreadFactory(): vscode.EmptyCommentThreadFactory | undefined {
|
||||
return this._emptyCommentThreadFactory;
|
||||
}
|
||||
|
||||
set emptyCommentThreadFactory(newEmptyCommentThreadFactory: vscode.EmptyCommentThreadFactory | undefined) {
|
||||
this._emptyCommentThreadFactory = newEmptyCommentThreadFactory;
|
||||
|
||||
if (this._emptyCommentThreadFactory && this._emptyCommentThreadFactory.template) {
|
||||
let template = this._emptyCommentThreadFactory.template;
|
||||
const acceptInputCommand = template.acceptInputCommand ? this._commandsConverter.toInternal(template.acceptInputCommand) : undefined;
|
||||
const additionalCommands = template.additionalCommands ? template.additionalCommands.map(x => this._commandsConverter.toInternal(x)) : [];
|
||||
const deleteCommand = template.deleteCommand ? this._commandsConverter.toInternal(template.deleteCommand) : undefined;
|
||||
this._proxy.$updateCommentControllerFeatures(this.handle, {
|
||||
commentThreadTemplate: {
|
||||
label: template.label,
|
||||
acceptInputCommand,
|
||||
additionalCommands,
|
||||
deleteCommand
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _commentReactionProvider?: vscode.CommentReactionProvider;
|
||||
|
||||
@@ -610,6 +669,10 @@ class ExtHostCommentController implements vscode.CommentController {
|
||||
}
|
||||
}
|
||||
|
||||
$onActiveCommentThreadChange(threadHandle: number) {
|
||||
this._activeCommentThread = this.getCommentThread(threadHandle);
|
||||
}
|
||||
|
||||
getCommentThread(handle: number) {
|
||||
return this._threads.get(handle);
|
||||
}
|
||||
@@ -664,6 +727,7 @@ function convertFromComment(comment: modes.Comment): vscode.Comment {
|
||||
}
|
||||
|
||||
return {
|
||||
id: comment.commentId,
|
||||
commentId: comment.commentId,
|
||||
body: extHostTypeConverter.MarkdownString.to(comment.body),
|
||||
userName: comment.userName,
|
||||
@@ -685,7 +749,7 @@ function convertToModeComment(commentController: ExtHostCommentController, vscod
|
||||
const iconPath = vscodeComment.userIconPath ? vscodeComment.userIconPath.toString() : vscodeComment.gravatar;
|
||||
|
||||
return {
|
||||
commentId: vscodeComment.commentId,
|
||||
commentId: vscodeComment.id || vscodeComment.commentId,
|
||||
body: extHostTypeConverter.MarkdownString.from(vscodeComment.body),
|
||||
userName: vscodeComment.userName,
|
||||
userIconPath: iconPath,
|
||||
|
||||
@@ -2287,6 +2287,61 @@ export enum FoldingRangeKind {
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Comment
|
||||
@es5ClassCompat
|
||||
export class Comment {
|
||||
id: string;
|
||||
body: MarkdownString;
|
||||
userName: string;
|
||||
label?: string;
|
||||
userIconPath?: URI;
|
||||
selectCommand?: vscode.Command;
|
||||
editCommand?: vscode.Command;
|
||||
deleteCommand?: vscode.Command;
|
||||
|
||||
/**
|
||||
* The id of the comment
|
||||
*
|
||||
* @deprecated Use Id instead
|
||||
*/
|
||||
commentId: string;
|
||||
|
||||
/**
|
||||
* @deprecated Use userIconPath instead. The avatar src of the user who created the comment
|
||||
*/
|
||||
gravatar?: string;
|
||||
|
||||
/**
|
||||
* @deprecated, use editCommand
|
||||
*/
|
||||
canEdit?: boolean;
|
||||
|
||||
/**
|
||||
* @deprecated, use deleteCommand
|
||||
*/
|
||||
canDelete?: boolean;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
command?: vscode.Command;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
isDraft?: boolean;
|
||||
|
||||
/**
|
||||
* Proposed Comment Reaction
|
||||
*/
|
||||
commentReactions?: vscode.CommentReaction[];
|
||||
|
||||
constructor(id: string, body: MarkdownString, userName: string) {
|
||||
this.id = id;
|
||||
this.body = body;
|
||||
this.userName = userName;
|
||||
}
|
||||
}
|
||||
|
||||
export enum CommentThreadCollapsibleState {
|
||||
/**
|
||||
@@ -2298,6 +2353,7 @@ export enum CommentThreadCollapsibleState {
|
||||
*/
|
||||
Expanded = 1
|
||||
}
|
||||
//#endregion
|
||||
|
||||
@es5ClassCompat
|
||||
export class QuickInputButtons {
|
||||
|
||||
@@ -780,6 +780,7 @@ export function createApiFactory(
|
||||
Color: extHostTypes.Color,
|
||||
ColorInformation: extHostTypes.ColorInformation,
|
||||
ColorPresentation: extHostTypes.ColorPresentation,
|
||||
Comment: extHostTypes.Comment,
|
||||
CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState,
|
||||
CompletionItem: extHostTypes.CompletionItem,
|
||||
CompletionItemKind: extHostTypes.CompletionItemKind,
|
||||
|
||||
@@ -309,17 +309,17 @@ export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusB
|
||||
hc: Color.black.transparent(0.3),
|
||||
}, nls.localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window."));
|
||||
|
||||
export const STATUS_BAR_HOST_NAME_BACKGROUND = registerColor('statusBarItem.hostBackground', {
|
||||
dark: '#C40057',
|
||||
light: '#C40057',
|
||||
hc: '#C40057'
|
||||
}, nls.localize('statusBarItemHostBackground', "Background color for the host indicator on the status bar."));
|
||||
export const STATUS_BAR_HOST_NAME_BACKGROUND = registerColor('statusBarItem.remoteBackground', {
|
||||
dark: '#16825D',
|
||||
light: '#16825D',
|
||||
hc: '#FFFFFF00'
|
||||
}, nls.localize('statusBarItemHostBackground', "Background color for the remote indicator on the status bar."));
|
||||
|
||||
export const STATUS_BAR_HOST_NAME_FOREGROUND = registerColor('statusBarItem.hostForeground', {
|
||||
export const STATUS_BAR_HOST_NAME_FOREGROUND = registerColor('statusBarItem.remoteForeground', {
|
||||
dark: '#FFFFFF',
|
||||
light: '#FFFFFF',
|
||||
hc: '#FFFFFF'
|
||||
}, nls.localize('statusBarItemHostForeground', "Foreground color for the host indicator on the status bar."));
|
||||
}, nls.localize('statusBarItemHostForeground', "Foreground color for the remote indicator on the status bar."));
|
||||
|
||||
|
||||
// < --- Activity Bar --- >
|
||||
@@ -366,6 +366,18 @@ export const ACTIVITY_BAR_BADGE_FOREGROUND = registerColor('activityBarBadge.for
|
||||
hc: Color.white
|
||||
}, nls.localize('activityBarBadgeForeground', "Activity notification badge foreground color. The activity bar is showing on the far left or right and allows to switch between views of the side bar."));
|
||||
|
||||
export const EXTENSION_BADGE_REMOTE_BACKGROUND = registerColor('extensionBadge.remoteBackground', {
|
||||
dark: ACTIVITY_BAR_BADGE_BACKGROUND,
|
||||
light: ACTIVITY_BAR_BADGE_BACKGROUND,
|
||||
hc: ACTIVITY_BAR_BADGE_BACKGROUND
|
||||
}, nls.localize('extensionBadge.remoteBackground', "Background color for the remote badge in the extensions view"));
|
||||
|
||||
export const EXTENSION_BADGE_REMOTE_FOREGROUND = registerColor('extensionBadge.remoteForeground', {
|
||||
dark: ACTIVITY_BAR_BADGE_FOREGROUND,
|
||||
light: ACTIVITY_BAR_BADGE_FOREGROUND,
|
||||
hc: ACTIVITY_BAR_BADGE_FOREGROUND
|
||||
}, nls.localize('extensionBadge.remoteForeground', "Foreground color for the remote badge in the extensions view"));
|
||||
|
||||
|
||||
// < --- Side Bar --- >
|
||||
|
||||
|
||||
@@ -322,15 +322,17 @@ export class CommentService extends Disposable implements ICommentService {
|
||||
}
|
||||
}
|
||||
|
||||
let commentControlResult: Promise<ICommentInfo>[] = [];
|
||||
let commentControlResult: Promise<ICommentInfo | null>[] = [];
|
||||
|
||||
this._commentControls.forEach(control => {
|
||||
commentControlResult.push(control.getDocumentComments(resource, CancellationToken.None));
|
||||
commentControlResult.push(control.getDocumentComments(resource, CancellationToken.None)
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
return null;
|
||||
}));
|
||||
});
|
||||
|
||||
let ret = [...await Promise.all(result), ...await Promise.all(commentControlResult)];
|
||||
|
||||
return ret;
|
||||
return Promise.all([...result, ...commentControlResult]);
|
||||
}
|
||||
|
||||
async getCommentingRanges(resource: URI): Promise<IRange[]> {
|
||||
|
||||
@@ -212,6 +212,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
} else {
|
||||
const deleteCommand = (this._commentThread as modes.CommentThread2).deleteCommand;
|
||||
if (deleteCommand) {
|
||||
this.commentService.setActiveCommentThread(this._commentThread);
|
||||
return this.commandService.executeCommand(deleteCommand.id, ...(deleteCommand.arguments || []));
|
||||
}
|
||||
}
|
||||
@@ -239,7 +240,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
}
|
||||
}
|
||||
|
||||
async update(commentThread: modes.CommentThread | modes.CommentThread2) {
|
||||
async update(commentThread: modes.CommentThread | modes.CommentThread2, replaceTemplate: boolean = false) {
|
||||
const oldCommentsLen = this._commentElements.length;
|
||||
const newCommentsLen = commentThread.comments ? commentThread.comments.length : 0;
|
||||
|
||||
@@ -288,12 +289,20 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
|
||||
this._commentThread = commentThread;
|
||||
this._commentElements = newCommentNodeList;
|
||||
this.createThreadLabel();
|
||||
this.createThreadLabel(replaceTemplate);
|
||||
|
||||
if (replaceTemplate) {
|
||||
this.createTextModelListener();
|
||||
}
|
||||
|
||||
if (this._formActions && this._commentEditor.hasModel()) {
|
||||
dom.clearNode(this._formActions);
|
||||
const model = this._commentEditor.getModel();
|
||||
this.createCommentWidgetActions2(this._formActions, model);
|
||||
|
||||
if (replaceTemplate) {
|
||||
this.createCommentWidgetActionsListener(this._formActions, model);
|
||||
}
|
||||
}
|
||||
|
||||
// Move comment glyph widget and show position if the line has changed.
|
||||
@@ -348,7 +357,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
this._commentEditor.layout({ height: 5 * 18, width: widthInPixel - 54 /* margin 20px * 10 + scrollbar 14px*/ });
|
||||
}
|
||||
|
||||
display(lineNumber: number) {
|
||||
display(lineNumber: number, fromTemplate: boolean = false) {
|
||||
this._commentGlyph = new CommentGlyphWidget(this.editor, lineNumber);
|
||||
|
||||
this._disposables.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e)));
|
||||
@@ -386,58 +395,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
this._commentEditor.setModel(model);
|
||||
this._disposables.push(this._commentEditor);
|
||||
this._disposables.push(this._commentEditor.getModel()!.onDidChangeContent(() => this.setCommentEditorDecorations()));
|
||||
if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined) {
|
||||
this._disposables.push(this._commentEditor.onDidFocusEditorWidget(() => {
|
||||
let commentThread = this._commentThread as modes.CommentThread2;
|
||||
commentThread.input = {
|
||||
uri: this._commentEditor.getModel()!.uri,
|
||||
value: this._commentEditor.getValue()
|
||||
};
|
||||
this.commentService.setActiveCommentThread(this._commentThread);
|
||||
}));
|
||||
|
||||
this._disposables.push(this._commentEditor.getModel()!.onDidChangeContent(() => {
|
||||
let modelContent = this._commentEditor.getValue();
|
||||
let thread = (this._commentThread as modes.CommentThread2);
|
||||
if (thread.input && thread.input.uri === this._commentEditor.getModel()!.uri && thread.input.value !== modelContent) {
|
||||
let newInput: modes.CommentInput = thread.input;
|
||||
newInput.value = modelContent;
|
||||
thread.input = newInput;
|
||||
}
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeInput(input => {
|
||||
let thread = (this._commentThread as modes.CommentThread2);
|
||||
|
||||
if (thread.input && thread.input.uri !== this._commentEditor.getModel()!.uri) {
|
||||
return;
|
||||
}
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._commentEditor.getValue() !== input.value) {
|
||||
this._commentEditor.setValue(input.value);
|
||||
|
||||
if (input.value === '') {
|
||||
this._pendingComment = '';
|
||||
if (dom.hasClass(this._commentForm, 'expand')) {
|
||||
dom.removeClass(this._commentForm, 'expand');
|
||||
}
|
||||
this._commentEditor.getDomNode()!.style.outline = '';
|
||||
this._error.textContent = '';
|
||||
dom.addClass(this._error, 'hidden');
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeComments(async _ => {
|
||||
await this.update(this._commentThread);
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeLabel(_ => {
|
||||
this.createThreadLabel();
|
||||
}));
|
||||
if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined && !fromTemplate) {
|
||||
this.createTextModelListener();
|
||||
}
|
||||
|
||||
this.setCommentEditorDecorations();
|
||||
@@ -456,50 +415,9 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
this._formActions = dom.append(this._commentForm, dom.$('.form-actions'));
|
||||
if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined) {
|
||||
this.createCommentWidgetActions2(this._formActions, model);
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeAcceptInputCommand(_ => {
|
||||
if (this._formActions) {
|
||||
dom.clearNode(this._formActions);
|
||||
this.createCommentWidgetActions2(this._formActions, model);
|
||||
}
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeAdditionalCommands(_ => {
|
||||
if (this._formActions) {
|
||||
dom.clearNode(this._formActions);
|
||||
this.createCommentWidgetActions2(this._formActions, model);
|
||||
}
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeRange(range => {
|
||||
// Move comment glyph widget and show position if the line has changed.
|
||||
const lineNumber = this._commentThread.range.startLineNumber;
|
||||
let shouldMoveWidget = false;
|
||||
if (this._commentGlyph) {
|
||||
if (this._commentGlyph.getPosition().position!.lineNumber !== lineNumber) {
|
||||
shouldMoveWidget = true;
|
||||
this._commentGlyph.setLineNumber(lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldMoveWidget && this._isExpanded) {
|
||||
this.show({ lineNumber, column: 1 }, 2);
|
||||
}
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeCollasibleState(state => {
|
||||
if (state === modes.CommentThreadCollapsibleState.Expanded && !this._isExpanded) {
|
||||
const lineNumber = this._commentThread.range.startLineNumber;
|
||||
|
||||
this.show({ lineNumber, column: 1 }, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === modes.CommentThreadCollapsibleState.Collapsed && this._isExpanded) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
}));
|
||||
if (!fromTemplate) {
|
||||
this.createCommentWidgetActionsListener(this._formActions, model);
|
||||
}
|
||||
} else {
|
||||
this.createCommentWidgetActions(this._formActions, model);
|
||||
}
|
||||
@@ -526,6 +444,106 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
}
|
||||
}
|
||||
|
||||
private createTextModelListener() {
|
||||
this._disposables.push(this._commentEditor.onDidFocusEditorWidget(() => {
|
||||
let commentThread = this._commentThread as modes.CommentThread2;
|
||||
commentThread.input = {
|
||||
uri: this._commentEditor.getModel()!.uri,
|
||||
value: this._commentEditor.getValue()
|
||||
};
|
||||
this.commentService.setActiveCommentThread(this._commentThread);
|
||||
}));
|
||||
|
||||
this._disposables.push(this._commentEditor.getModel()!.onDidChangeContent(() => {
|
||||
let modelContent = this._commentEditor.getValue();
|
||||
let thread = (this._commentThread as modes.CommentThread2);
|
||||
if (thread.input && thread.input.uri === this._commentEditor.getModel()!.uri && thread.input.value !== modelContent) {
|
||||
let newInput: modes.CommentInput = thread.input;
|
||||
newInput.value = modelContent;
|
||||
thread.input = newInput;
|
||||
}
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeInput(input => {
|
||||
let thread = (this._commentThread as modes.CommentThread2);
|
||||
|
||||
if (thread.input && thread.input.uri !== this._commentEditor.getModel()!.uri) {
|
||||
return;
|
||||
}
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._commentEditor.getValue() !== input.value) {
|
||||
this._commentEditor.setValue(input.value);
|
||||
|
||||
if (input.value === '') {
|
||||
this._pendingComment = '';
|
||||
if (dom.hasClass(this._commentForm, 'expand')) {
|
||||
dom.removeClass(this._commentForm, 'expand');
|
||||
}
|
||||
this._commentEditor.getDomNode()!.style.outline = '';
|
||||
this._error.textContent = '';
|
||||
dom.addClass(this._error, 'hidden');
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeComments(async _ => {
|
||||
await this.update(this._commentThread);
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeLabel(_ => {
|
||||
this.createThreadLabel();
|
||||
}));
|
||||
}
|
||||
|
||||
private createCommentWidgetActionsListener(container: HTMLElement, model: ITextModel) {
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeAcceptInputCommand(_ => {
|
||||
if (container) {
|
||||
dom.clearNode(container);
|
||||
this.createCommentWidgetActions2(container, model);
|
||||
}
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeAdditionalCommands(_ => {
|
||||
if (container) {
|
||||
dom.clearNode(container);
|
||||
this.createCommentWidgetActions2(container, model);
|
||||
}
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeRange(range => {
|
||||
// Move comment glyph widget and show position if the line has changed.
|
||||
const lineNumber = this._commentThread.range.startLineNumber;
|
||||
let shouldMoveWidget = false;
|
||||
if (this._commentGlyph) {
|
||||
if (this._commentGlyph.getPosition().position!.lineNumber !== lineNumber) {
|
||||
shouldMoveWidget = true;
|
||||
this._commentGlyph.setLineNumber(lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldMoveWidget && this._isExpanded) {
|
||||
this.show({ lineNumber, column: 1 }, 2);
|
||||
}
|
||||
}));
|
||||
|
||||
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeCollasibleState(state => {
|
||||
if (state === modes.CommentThreadCollapsibleState.Expanded && !this._isExpanded) {
|
||||
const lineNumber = this._commentThread.range.startLineNumber;
|
||||
|
||||
this.show({ lineNumber, column: 1 }, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === modes.CommentThreadCollapsibleState.Collapsed && this._isExpanded) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private handleError(e: Error) {
|
||||
this._error.textContent = e.message;
|
||||
this._commentEditor.getDomNode()!.style.outline = `1px solid ${this.themeService.getTheme().getColor(inputValidationErrorBorder)}`;
|
||||
@@ -798,13 +816,14 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
}
|
||||
}
|
||||
|
||||
private createThreadLabel() {
|
||||
private createThreadLabel(replaceTemplate: boolean = false) {
|
||||
let label: string | undefined;
|
||||
if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined) {
|
||||
label = (this._commentThread as modes.CommentThread2).label;
|
||||
}
|
||||
|
||||
if (label === undefined) {
|
||||
if (label === undefined && !replaceTemplate) {
|
||||
// if it's for replacing the comment thread template, the comment thread widget title can be undefined as extensions may set it later
|
||||
if (this._commentThread.comments && this._commentThread.comments.length) {
|
||||
const participantsList = this._commentThread.comments.filter(arrays.uniqueFilter(comment => comment.userName)).map(comment => `@${comment.userName}`).join(', ');
|
||||
label = nls.localize('commentThreadParticipants', "Participants: {0}", participantsList);
|
||||
@@ -813,8 +832,11 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
}
|
||||
}
|
||||
|
||||
this._headingLabel.innerHTML = strings.escape(label);
|
||||
this._headingLabel.setAttribute('aria-label', label);
|
||||
if (label) {
|
||||
this._headingLabel.innerHTML = strings.escape(label);
|
||||
this._headingLabel.setAttribute('aria-label', label);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private expandReplyArea() {
|
||||
|
||||
@@ -30,7 +30,7 @@ import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/com
|
||||
import { overviewRulerCommentingRangeForeground } from 'vs/workbench/contrib/comments/browser/commentGlyphWidget';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_ITEM_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ctxCommentEditorFocused, SimpleCommentEditor } from 'vs/workbench/contrib/comments/browser/simpleCommentEditor';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
@@ -64,7 +64,7 @@ class CommentingRangeDecoration {
|
||||
return this._decorationId;
|
||||
}
|
||||
|
||||
constructor(private _editor: ICodeEditor, private _ownerId: string, private _extensionId: string | undefined, private _label: string | undefined, private _range: IRange, private _reply: modes.Command | undefined, commentingOptions: ModelDecorationOptions, private commentingRangesInfo?: modes.CommentingRanges) {
|
||||
constructor(private _editor: ICodeEditor, private _ownerId: string, private _extensionId: string | undefined, private _label: string | undefined, private _range: IRange, private _reply: modes.Command | undefined, commentingOptions: ModelDecorationOptions, private _template: modes.CommentThreadTemplate | undefined, private commentingRangesInfo?: modes.CommentingRanges) {
|
||||
const startLineNumber = _range.startLineNumber;
|
||||
const endLineNumber = _range.endLineNumber;
|
||||
let commentingRangeDecorations = [{
|
||||
@@ -81,13 +81,14 @@ class CommentingRangeDecoration {
|
||||
}
|
||||
}
|
||||
|
||||
public getCommentAction(): { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined } {
|
||||
public getCommentAction(): { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined, template: modes.CommentThreadTemplate | undefined } {
|
||||
return {
|
||||
extensionId: this._extensionId,
|
||||
label: this._label,
|
||||
replyCommand: this._reply,
|
||||
ownerId: this._ownerId,
|
||||
commentingRangesInfo: this.commentingRangesInfo
|
||||
commentingRangesInfo: this.commentingRangesInfo,
|
||||
template: this._template
|
||||
};
|
||||
}
|
||||
|
||||
@@ -124,11 +125,11 @@ class CommentingRangeDecorator {
|
||||
for (const info of commentInfos) {
|
||||
if (Array.isArray(info.commentingRanges)) {
|
||||
info.commentingRanges.forEach(range => {
|
||||
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, info.reply, this.decorationOptions));
|
||||
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, info.reply, this.decorationOptions, info.template));
|
||||
});
|
||||
} else {
|
||||
(info.commentingRanges ? info.commentingRanges.ranges : []).forEach(range => {
|
||||
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, (info.commentingRanges as modes.CommentingRanges).newCommentThreadCommand, this.decorationOptions, info.commentingRanges as modes.CommentingRanges));
|
||||
commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, undefined, this.decorationOptions, info.template, info.commentingRanges as modes.CommentingRanges));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -178,7 +179,6 @@ export class ReviewController implements IEditorContribution {
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@ICommentService private readonly commentService: ICommentService,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
|
||||
@@ -441,6 +441,11 @@ export class ReviewController implements IEditorContribution {
|
||||
}
|
||||
});
|
||||
added.forEach(thread => {
|
||||
let matchedZones = this._commentWidgets.filter(zoneWidget => zoneWidget.owner === e.owner && zoneWidget.commentThread.threadId === thread.threadId);
|
||||
if (matchedZones.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pendingCommentText = this._pendingCommentCache[e.owner] && this._pendingCommentCache[e.owner][thread.threadId];
|
||||
this.displayCommentThread(e.owner, thread, pendingCommentText, draftMode);
|
||||
this._commentInfos.filter(info => info.owner === e.owner)[0].threads.push(thread);
|
||||
@@ -457,6 +462,30 @@ export class ReviewController implements IEditorContribution {
|
||||
this._commentWidgets.push(zoneWidget);
|
||||
}
|
||||
|
||||
private addCommentThreadFromTemplate(lineNumber: number, ownerId: string, extensionId: string | undefined, template: modes.CommentThreadTemplate): ReviewZoneWidget {
|
||||
let templateReviewZoneWidget = this.instantiationService.createInstance(ReviewZoneWidget, this.editor, ownerId, {
|
||||
commentThreadHandle: -1,
|
||||
label: template!.label,
|
||||
acceptInputCommand: template.acceptInputCommand,
|
||||
additionalCommands: template.additionalCommands,
|
||||
deleteCommand: template.deleteCommand,
|
||||
extensionId: extensionId,
|
||||
threadId: null,
|
||||
resource: null,
|
||||
comments: [],
|
||||
range: {
|
||||
startLineNumber: lineNumber,
|
||||
startColumn: 0,
|
||||
endLineNumber: lineNumber,
|
||||
endColumn: 0
|
||||
},
|
||||
collapsibleState: modes.CommentThreadCollapsibleState.Expanded,
|
||||
},
|
||||
'', modes.DraftMode.NotSupported);
|
||||
|
||||
return templateReviewZoneWidget;
|
||||
}
|
||||
|
||||
private addComment(lineNumber: number, replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, draftMode: modes.DraftMode | undefined, pendingComment: string | null) {
|
||||
if (this._newCommentWidget) {
|
||||
this.notificationService.warn(`Please submit the comment at line ${this._newCommentWidget.position ? this._newCommentWidget.position.lineNumber : -1} before creating a new one.`);
|
||||
@@ -612,16 +641,16 @@ export class ReviewController implements IEditorContribution {
|
||||
const commentInfos = newCommentInfos.filter(info => info.ownerId === pick.id);
|
||||
|
||||
if (commentInfos.length) {
|
||||
const { replyCommand, ownerId, extensionId, commentingRangesInfo } = commentInfos[0];
|
||||
this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo);
|
||||
const { replyCommand, ownerId, extensionId, commentingRangesInfo, template } = commentInfos[0];
|
||||
this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo, template);
|
||||
}
|
||||
}).then(() => {
|
||||
this._addInProgress = false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const { replyCommand, ownerId, extensionId, commentingRangesInfo } = newCommentInfos[0]!;
|
||||
this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo);
|
||||
const { replyCommand, ownerId, extensionId, commentingRangesInfo, template } = newCommentInfos[0]!;
|
||||
this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo, template);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
@@ -640,11 +669,11 @@ export class ReviewController implements IEditorContribution {
|
||||
return picks;
|
||||
}
|
||||
|
||||
private getContextMenuActions(commentInfos: { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined }[], lineNumber: number): (IAction | ContextSubMenu)[] {
|
||||
private getContextMenuActions(commentInfos: { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined, template: modes.CommentThreadTemplate | undefined }[], lineNumber: number): (IAction | ContextSubMenu)[] {
|
||||
const actions: (IAction | ContextSubMenu)[] = [];
|
||||
|
||||
commentInfos.forEach(commentInfo => {
|
||||
const { replyCommand, ownerId, extensionId, label, commentingRangesInfo } = commentInfo;
|
||||
const { replyCommand, ownerId, extensionId, label, commentingRangesInfo, template } = commentInfo;
|
||||
|
||||
actions.push(new Action(
|
||||
'addCommentThread',
|
||||
@@ -652,7 +681,7 @@ export class ReviewController implements IEditorContribution {
|
||||
undefined,
|
||||
true,
|
||||
() => {
|
||||
this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo);
|
||||
this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo, template);
|
||||
return Promise.resolve();
|
||||
}
|
||||
));
|
||||
@@ -660,17 +689,25 @@ export class ReviewController implements IEditorContribution {
|
||||
return actions;
|
||||
}
|
||||
|
||||
public addCommentAtLine2(lineNumber: number, replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined) {
|
||||
public addCommentAtLine2(lineNumber: number, replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined, template: modes.CommentThreadTemplate | undefined) {
|
||||
if (commentingRangesInfo) {
|
||||
let range = new Range(lineNumber, 1, lineNumber, 1);
|
||||
if (commentingRangesInfo.newCommentThreadCommand) {
|
||||
if (replyCommand) {
|
||||
const commandId = replyCommand.id;
|
||||
const args = replyCommand.arguments || [];
|
||||
if (commentingRangesInfo.newCommentThreadCallback && template) {
|
||||
// create comment widget through template
|
||||
let commentThreadWidget = this.addCommentThreadFromTemplate(lineNumber, ownerId, extensionId, template);
|
||||
commentThreadWidget.display(lineNumber, true);
|
||||
|
||||
this._commandService.executeCommand(commandId, ...args);
|
||||
this._addInProgress = false;
|
||||
}
|
||||
return commentingRangesInfo.newCommentThreadCallback(this.editor.getModel()!.uri, range)
|
||||
.then(commentThread => {
|
||||
commentThreadWidget.update(commentThread!, true);
|
||||
this._commentWidgets.push(commentThreadWidget);
|
||||
this.processNextThreadToAdd();
|
||||
})
|
||||
.catch(e => {
|
||||
this.notificationService.error(nls.localize('commentThreadAddFailure', "Adding a new comment thread failed: {0}.", e.message));
|
||||
commentThreadWidget.dispose();
|
||||
this.processNextThreadToAdd();
|
||||
});
|
||||
} else if (commentingRangesInfo.newCommentThreadCallback) {
|
||||
return commentingRangesInfo.newCommentThreadCallback(this.editor.getModel()!.uri, range)
|
||||
.then(_ => {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/pla
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { extensionButtonProminentBackground, extensionButtonProminentForeground, DisabledLabelAction, ReloadAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_HOST_NAME_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { EXTENSION_BADGE_REMOTE_BACKGROUND, EXTENSION_BADGE_REMOTE_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
@@ -323,8 +323,8 @@ class RemoteBadge extends Disposable {
|
||||
if (!this.element) {
|
||||
return;
|
||||
}
|
||||
const bgColor = this.themeService.getTheme().getColor(STATUS_BAR_HOST_NAME_BACKGROUND);
|
||||
const fgColor = this.themeService.getTheme().getColor(STATUS_BAR_HOST_NAME_FOREGROUND);
|
||||
const bgColor = this.themeService.getTheme().getColor(EXTENSION_BADGE_REMOTE_BACKGROUND);
|
||||
const fgColor = this.themeService.getTheme().getColor(EXTENSION_BADGE_REMOTE_FOREGROUND);
|
||||
this.element.style.backgroundColor = bgColor ? bgColor.toString() : '';
|
||||
this.element.style.color = fgColor ? fgColor.toString() : '';
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ import { ResourceMap } from 'vs/base/common/map';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { FileOnDiskContentProvider } from 'vs/workbench/contrib/files/common/files';
|
||||
import { FileOnDiskContentProvider, resourceToFileOnDisk } from 'vs/workbench/contrib/files/common/files';
|
||||
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { SAVE_FILE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL } from 'vs/workbench/contrib/files/browser/fileCommands';
|
||||
@@ -248,7 +248,7 @@ class ResolveSaveConflictAction extends Action {
|
||||
|
||||
return this.editorService.openEditor(
|
||||
{
|
||||
leftResource: resource.with({ scheme: CONFLICT_RESOLUTION_SCHEME }),
|
||||
leftResource: resourceToFileOnDisk(CONFLICT_RESOLUTION_SCHEME, resource),
|
||||
rightResource: resource,
|
||||
label: editorLabel,
|
||||
options: { pinned: true }
|
||||
|
||||
@@ -23,8 +23,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { toLocalResource } from 'vs/base/common/resources';
|
||||
|
||||
/**
|
||||
* Explorer viewlet id.
|
||||
@@ -133,6 +131,14 @@ export const SortOrderConfiguration = {
|
||||
|
||||
export type SortOrder = 'default' | 'mixed' | 'filesFirst' | 'type' | 'modified';
|
||||
|
||||
export function resourceToFileOnDisk(scheme: string, resource: URI): URI {
|
||||
return resource.with({ scheme, query: JSON.stringify({ scheme: resource.scheme }) });
|
||||
}
|
||||
|
||||
export function fileOnDiskToResource(resource: URI): URI {
|
||||
return resource.with({ scheme: JSON.parse(resource.query)['scheme'], query: null });
|
||||
}
|
||||
|
||||
export class FileOnDiskContentProvider implements ITextModelContentProvider {
|
||||
private fileWatcherDisposable: IDisposable | undefined;
|
||||
|
||||
@@ -140,13 +146,12 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider {
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IModeService private readonly modeService: IModeService,
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
|
||||
@IModelService private readonly modelService: IModelService
|
||||
) {
|
||||
}
|
||||
|
||||
provideTextContent(resource: URI): Promise<ITextModel> {
|
||||
const savedFileResource = toLocalResource(resource, this.environmentService.configuration.remoteAuthority);
|
||||
const savedFileResource = fileOnDiskToResource(resource);
|
||||
|
||||
// Make sure our file from disk is resolved up to date
|
||||
return this.resolveEditorModel(resource).then(codeEditorModel => {
|
||||
@@ -174,7 +179,7 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider {
|
||||
private resolveEditorModel(resource: URI, createAsNeeded?: true): Promise<ITextModel>;
|
||||
private resolveEditorModel(resource: URI, createAsNeeded?: boolean): Promise<ITextModel | null>;
|
||||
private resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> {
|
||||
const savedFileResource = toLocalResource(resource, this.environmentService.configuration.remoteAuthority);
|
||||
const savedFileResource = fileOnDiskToResource(resource);
|
||||
|
||||
return this.textFileService.readStream(savedFileResource).then(content => {
|
||||
let codeEditorModel = this.modelService.getModel(resource);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { URI } from 'vs/base/common/uri';
|
||||
import { workbenchInstantiationService, TestFileService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { FileOnDiskContentProvider, resourceToFileOnDisk } from 'vs/workbench/contrib/files/common/files';
|
||||
import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
class ServiceAccessor {
|
||||
constructor(
|
||||
@IFileService public fileService: TestFileService
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
suite('Files - FileOnDiskContentProvider', () => {
|
||||
|
||||
let instantiationService: IInstantiationService;
|
||||
let accessor: ServiceAccessor;
|
||||
|
||||
setup(() => {
|
||||
instantiationService = workbenchInstantiationService();
|
||||
accessor = instantiationService.createInstance(ServiceAccessor);
|
||||
});
|
||||
|
||||
test('provideTextContent', async () => {
|
||||
const provider = instantiationService.createInstance(FileOnDiskContentProvider);
|
||||
const uri = URI.parse('testFileOnDiskContentProvider://foo');
|
||||
|
||||
const content = await provider.provideTextContent(resourceToFileOnDisk('conflictResolution', uri));
|
||||
|
||||
assert.equal(snapshotToString(content.createSnapshot()), 'Hello Html');
|
||||
assert.equal(accessor.fileService.getLastReadFileUri().toString(), uri.toString());
|
||||
});
|
||||
});
|
||||
@@ -469,7 +469,7 @@ export class RemoteFileDialog {
|
||||
this.autoCompletePathSegment = '';
|
||||
return false;
|
||||
}
|
||||
const itemBasename = quickPickItem.label;
|
||||
const itemBasename = this.trimTrailingSlash(quickPickItem.label);
|
||||
// Either force the autocomplete, or the old value should be one smaller than the new value and match the new value.
|
||||
if (itemBasename === '..') {
|
||||
// Don't match on the up directory item ever.
|
||||
|
||||
@@ -899,6 +899,7 @@ export class TestFileService implements IFileService {
|
||||
readonly onError: Event<Error> = Event.None;
|
||||
|
||||
private content = 'Hello Html';
|
||||
private lastReadFileUri: URI;
|
||||
|
||||
constructor() {
|
||||
this._onFileChanges = new Emitter<FileChangesEvent>();
|
||||
@@ -913,6 +914,10 @@ export class TestFileService implements IFileService {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
public getLastReadFileUri(): URI {
|
||||
return this.lastReadFileUri;
|
||||
}
|
||||
|
||||
public get onFileChanges(): Event<FileChangesEvent> {
|
||||
return this._onFileChanges.event;
|
||||
}
|
||||
@@ -952,6 +957,8 @@ export class TestFileService implements IFileService {
|
||||
}
|
||||
|
||||
readFile(resource: URI, options?: IReadFileOptions | undefined): Promise<IFileContent> {
|
||||
this.lastReadFileUri = resource;
|
||||
|
||||
return Promise.resolve({
|
||||
resource: resource,
|
||||
value: VSBuffer.fromString(this.content),
|
||||
@@ -964,6 +971,8 @@ export class TestFileService implements IFileService {
|
||||
}
|
||||
|
||||
readFileStream(resource: URI, options?: IReadFileOptions | undefined): Promise<IFileStreamContent> {
|
||||
this.lastReadFileUri = resource;
|
||||
|
||||
return Promise.resolve({
|
||||
resource: resource,
|
||||
value: {
|
||||
|
||||
Reference in New Issue
Block a user