mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 2b0b9136329c181a9e381463a1f7dc3a2d105a34 (#4880)
This commit is contained in:
@@ -4,9 +4,12 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
|
||||
export const enum CodeEditorStateFlag {
|
||||
Value = 1,
|
||||
@@ -71,6 +74,56 @@ export class EditorState {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cancellation token source that cancels when the editor changes as expressed
|
||||
* by the provided flags
|
||||
*/
|
||||
export class EditorStateCancellationTokenSource extends CancellationTokenSource {
|
||||
|
||||
private readonly _listener: IDisposable[] = [];
|
||||
|
||||
constructor(readonly editor: IActiveCodeEditor, flags: CodeEditorStateFlag, parent?: CancellationToken) {
|
||||
super(parent);
|
||||
|
||||
if (flags & CodeEditorStateFlag.Position) {
|
||||
this._listener.push(editor.onDidChangeCursorPosition(_ => this.cancel()));
|
||||
}
|
||||
if (flags & CodeEditorStateFlag.Selection) {
|
||||
this._listener.push(editor.onDidChangeCursorSelection(_ => this.cancel()));
|
||||
}
|
||||
if (flags & CodeEditorStateFlag.Scroll) {
|
||||
this._listener.push(editor.onDidScrollChange(_ => this.cancel()));
|
||||
}
|
||||
if (flags & CodeEditorStateFlag.Value) {
|
||||
this._listener.push(editor.onDidChangeModel(_ => this.cancel()));
|
||||
this._listener.push(editor.onDidChangeModelContent(_ => this.cancel()));
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
dispose(this._listener);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cancellation token source that cancels when the provided model changes
|
||||
*/
|
||||
export class TextModelCancellationTokenSource extends CancellationTokenSource {
|
||||
|
||||
private _listener: IDisposable;
|
||||
|
||||
constructor(model: ITextModel, parent?: CancellationToken) {
|
||||
super(parent);
|
||||
this._listener = model.onDidChangeContent(() => this.cancel());
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._listener.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class StableEditorScrollState {
|
||||
|
||||
public static capture(editor: ICodeEditor): StableEditorScrollState {
|
||||
|
||||
@@ -1628,8 +1628,6 @@ class EditorContextKeysManager extends Disposable {
|
||||
|
||||
export class EditorModeContext extends Disposable {
|
||||
|
||||
private readonly _editor: CodeEditorWidget;
|
||||
|
||||
private readonly _langId: IContextKey<string>;
|
||||
private readonly _hasCompletionItemProvider: IContextKey<boolean>;
|
||||
private readonly _hasCodeActionsProvider: IContextKey<boolean>;
|
||||
@@ -1651,37 +1649,36 @@ export class EditorModeContext extends Disposable {
|
||||
private readonly _isInWalkThrough: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
editor: CodeEditorWidget,
|
||||
contextKeyService: IContextKeyService
|
||||
private readonly _editor: CodeEditorWidget,
|
||||
private readonly _contextKeyService: IContextKeyService
|
||||
) {
|
||||
super();
|
||||
this._editor = editor;
|
||||
|
||||
this._langId = EditorContextKeys.languageId.bindTo(contextKeyService);
|
||||
this._hasCompletionItemProvider = EditorContextKeys.hasCompletionItemProvider.bindTo(contextKeyService);
|
||||
this._hasCodeActionsProvider = EditorContextKeys.hasCodeActionsProvider.bindTo(contextKeyService);
|
||||
this._hasCodeLensProvider = EditorContextKeys.hasCodeLensProvider.bindTo(contextKeyService);
|
||||
this._hasDefinitionProvider = EditorContextKeys.hasDefinitionProvider.bindTo(contextKeyService);
|
||||
this._hasDeclarationProvider = EditorContextKeys.hasDeclarationProvider.bindTo(contextKeyService);
|
||||
this._hasImplementationProvider = EditorContextKeys.hasImplementationProvider.bindTo(contextKeyService);
|
||||
this._hasTypeDefinitionProvider = EditorContextKeys.hasTypeDefinitionProvider.bindTo(contextKeyService);
|
||||
this._hasHoverProvider = EditorContextKeys.hasHoverProvider.bindTo(contextKeyService);
|
||||
this._hasDocumentHighlightProvider = EditorContextKeys.hasDocumentHighlightProvider.bindTo(contextKeyService);
|
||||
this._hasDocumentSymbolProvider = EditorContextKeys.hasDocumentSymbolProvider.bindTo(contextKeyService);
|
||||
this._hasReferenceProvider = EditorContextKeys.hasReferenceProvider.bindTo(contextKeyService);
|
||||
this._hasRenameProvider = EditorContextKeys.hasRenameProvider.bindTo(contextKeyService);
|
||||
this._hasSignatureHelpProvider = EditorContextKeys.hasSignatureHelpProvider.bindTo(contextKeyService);
|
||||
this._hasDocumentFormattingProvider = EditorContextKeys.hasDocumentFormattingProvider.bindTo(contextKeyService);
|
||||
this._hasDocumentSelectionFormattingProvider = EditorContextKeys.hasDocumentSelectionFormattingProvider.bindTo(contextKeyService);
|
||||
this._hasMultipleDocumentFormattingProvider = EditorContextKeys.hasMultipleDocumentFormattingProvider.bindTo(contextKeyService);
|
||||
this._hasMultipleDocumentSelectionFormattingProvider = EditorContextKeys.hasMultipleDocumentSelectionFormattingProvider.bindTo(contextKeyService);
|
||||
this._isInWalkThrough = EditorContextKeys.isInEmbeddedEditor.bindTo(contextKeyService);
|
||||
this._langId = EditorContextKeys.languageId.bindTo(_contextKeyService);
|
||||
this._hasCompletionItemProvider = EditorContextKeys.hasCompletionItemProvider.bindTo(_contextKeyService);
|
||||
this._hasCodeActionsProvider = EditorContextKeys.hasCodeActionsProvider.bindTo(_contextKeyService);
|
||||
this._hasCodeLensProvider = EditorContextKeys.hasCodeLensProvider.bindTo(_contextKeyService);
|
||||
this._hasDefinitionProvider = EditorContextKeys.hasDefinitionProvider.bindTo(_contextKeyService);
|
||||
this._hasDeclarationProvider = EditorContextKeys.hasDeclarationProvider.bindTo(_contextKeyService);
|
||||
this._hasImplementationProvider = EditorContextKeys.hasImplementationProvider.bindTo(_contextKeyService);
|
||||
this._hasTypeDefinitionProvider = EditorContextKeys.hasTypeDefinitionProvider.bindTo(_contextKeyService);
|
||||
this._hasHoverProvider = EditorContextKeys.hasHoverProvider.bindTo(_contextKeyService);
|
||||
this._hasDocumentHighlightProvider = EditorContextKeys.hasDocumentHighlightProvider.bindTo(_contextKeyService);
|
||||
this._hasDocumentSymbolProvider = EditorContextKeys.hasDocumentSymbolProvider.bindTo(_contextKeyService);
|
||||
this._hasReferenceProvider = EditorContextKeys.hasReferenceProvider.bindTo(_contextKeyService);
|
||||
this._hasRenameProvider = EditorContextKeys.hasRenameProvider.bindTo(_contextKeyService);
|
||||
this._hasSignatureHelpProvider = EditorContextKeys.hasSignatureHelpProvider.bindTo(_contextKeyService);
|
||||
this._hasDocumentFormattingProvider = EditorContextKeys.hasDocumentFormattingProvider.bindTo(_contextKeyService);
|
||||
this._hasDocumentSelectionFormattingProvider = EditorContextKeys.hasDocumentSelectionFormattingProvider.bindTo(_contextKeyService);
|
||||
this._hasMultipleDocumentFormattingProvider = EditorContextKeys.hasMultipleDocumentFormattingProvider.bindTo(_contextKeyService);
|
||||
this._hasMultipleDocumentSelectionFormattingProvider = EditorContextKeys.hasMultipleDocumentSelectionFormattingProvider.bindTo(_contextKeyService);
|
||||
this._isInWalkThrough = EditorContextKeys.isInEmbeddedEditor.bindTo(_contextKeyService);
|
||||
|
||||
const update = () => this._update();
|
||||
|
||||
// update when model/mode changes
|
||||
this._register(editor.onDidChangeModel(update));
|
||||
this._register(editor.onDidChangeModelLanguage(update));
|
||||
this._register(_editor.onDidChangeModel(update));
|
||||
this._register(_editor.onDidChangeModelLanguage(update));
|
||||
|
||||
// update when registries change
|
||||
this._register(modes.CompletionProviderRegistry.onDidChange(update));
|
||||
@@ -1708,23 +1705,25 @@ export class EditorModeContext extends Disposable {
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._langId.reset();
|
||||
this._hasCompletionItemProvider.reset();
|
||||
this._hasCodeActionsProvider.reset();
|
||||
this._hasCodeLensProvider.reset();
|
||||
this._hasDefinitionProvider.reset();
|
||||
this._hasDeclarationProvider.reset();
|
||||
this._hasImplementationProvider.reset();
|
||||
this._hasTypeDefinitionProvider.reset();
|
||||
this._hasHoverProvider.reset();
|
||||
this._hasDocumentHighlightProvider.reset();
|
||||
this._hasDocumentSymbolProvider.reset();
|
||||
this._hasReferenceProvider.reset();
|
||||
this._hasRenameProvider.reset();
|
||||
this._hasDocumentFormattingProvider.reset();
|
||||
this._hasDocumentSelectionFormattingProvider.reset();
|
||||
this._hasSignatureHelpProvider.reset();
|
||||
this._isInWalkThrough.reset();
|
||||
this._contextKeyService.bufferChangeEvents(() => {
|
||||
this._langId.reset();
|
||||
this._hasCompletionItemProvider.reset();
|
||||
this._hasCodeActionsProvider.reset();
|
||||
this._hasCodeLensProvider.reset();
|
||||
this._hasDefinitionProvider.reset();
|
||||
this._hasDeclarationProvider.reset();
|
||||
this._hasImplementationProvider.reset();
|
||||
this._hasTypeDefinitionProvider.reset();
|
||||
this._hasHoverProvider.reset();
|
||||
this._hasDocumentHighlightProvider.reset();
|
||||
this._hasDocumentSymbolProvider.reset();
|
||||
this._hasReferenceProvider.reset();
|
||||
this._hasRenameProvider.reset();
|
||||
this._hasDocumentFormattingProvider.reset();
|
||||
this._hasDocumentSelectionFormattingProvider.reset();
|
||||
this._hasSignatureHelpProvider.reset();
|
||||
this._isInWalkThrough.reset();
|
||||
});
|
||||
}
|
||||
|
||||
private _update() {
|
||||
@@ -1733,25 +1732,27 @@ export class EditorModeContext extends Disposable {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
this._langId.set(model.getLanguageIdentifier().language);
|
||||
this._hasCompletionItemProvider.set(modes.CompletionProviderRegistry.has(model));
|
||||
this._hasCodeActionsProvider.set(modes.CodeActionProviderRegistry.has(model));
|
||||
this._hasCodeLensProvider.set(modes.CodeLensProviderRegistry.has(model));
|
||||
this._hasDefinitionProvider.set(modes.DefinitionProviderRegistry.has(model));
|
||||
this._hasDeclarationProvider.set(modes.DeclarationProviderRegistry.has(model));
|
||||
this._hasImplementationProvider.set(modes.ImplementationProviderRegistry.has(model));
|
||||
this._hasTypeDefinitionProvider.set(modes.TypeDefinitionProviderRegistry.has(model));
|
||||
this._hasHoverProvider.set(modes.HoverProviderRegistry.has(model));
|
||||
this._hasDocumentHighlightProvider.set(modes.DocumentHighlightProviderRegistry.has(model));
|
||||
this._hasDocumentSymbolProvider.set(modes.DocumentSymbolProviderRegistry.has(model));
|
||||
this._hasReferenceProvider.set(modes.ReferenceProviderRegistry.has(model));
|
||||
this._hasRenameProvider.set(modes.RenameProviderRegistry.has(model));
|
||||
this._hasSignatureHelpProvider.set(modes.SignatureHelpProviderRegistry.has(model));
|
||||
this._hasDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.has(model) || modes.DocumentRangeFormattingEditProviderRegistry.has(model));
|
||||
this._hasDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.has(model));
|
||||
this._hasMultipleDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.all(model).length > 1 || modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1);
|
||||
this._hasMultipleDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1);
|
||||
this._isInWalkThrough.set(model.uri.scheme === Schemas.walkThroughSnippet);
|
||||
this._contextKeyService.bufferChangeEvents(() => {
|
||||
this._langId.set(model.getLanguageIdentifier().language);
|
||||
this._hasCompletionItemProvider.set(modes.CompletionProviderRegistry.has(model));
|
||||
this._hasCodeActionsProvider.set(modes.CodeActionProviderRegistry.has(model));
|
||||
this._hasCodeLensProvider.set(modes.CodeLensProviderRegistry.has(model));
|
||||
this._hasDefinitionProvider.set(modes.DefinitionProviderRegistry.has(model));
|
||||
this._hasDeclarationProvider.set(modes.DeclarationProviderRegistry.has(model));
|
||||
this._hasImplementationProvider.set(modes.ImplementationProviderRegistry.has(model));
|
||||
this._hasTypeDefinitionProvider.set(modes.TypeDefinitionProviderRegistry.has(model));
|
||||
this._hasHoverProvider.set(modes.HoverProviderRegistry.has(model));
|
||||
this._hasDocumentHighlightProvider.set(modes.DocumentHighlightProviderRegistry.has(model));
|
||||
this._hasDocumentSymbolProvider.set(modes.DocumentSymbolProviderRegistry.has(model));
|
||||
this._hasReferenceProvider.set(modes.ReferenceProviderRegistry.has(model));
|
||||
this._hasRenameProvider.set(modes.RenameProviderRegistry.has(model));
|
||||
this._hasSignatureHelpProvider.set(modes.SignatureHelpProviderRegistry.has(model));
|
||||
this._hasDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.has(model) || modes.DocumentRangeFormattingEditProviderRegistry.has(model));
|
||||
this._hasDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.has(model));
|
||||
this._hasMultipleDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.all(model).length > 1 || modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1);
|
||||
this._hasMultipleDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1);
|
||||
this._isInWalkThrough.set(model.uri.scheme === Schemas.walkThroughSnippet);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -894,11 +894,19 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
* @param strict Do NOT allow a position inside a high-low surrogate pair
|
||||
*/
|
||||
private _isValidPosition(lineNumber: number, column: number, strict: boolean): boolean {
|
||||
if (isNaN(lineNumber)) {
|
||||
if (typeof lineNumber !== 'number' || typeof column !== 'number') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lineNumber < 1) {
|
||||
if (isNaN(lineNumber) || isNaN(column)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lineNumber < 1 || column < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((lineNumber | 0) !== lineNumber || (column | 0) !== column) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -907,14 +915,6 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isNaN(column)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (column < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const maxColumn = this.getLineMaxColumn(lineNumber);
|
||||
if (column > maxColumn) {
|
||||
return false;
|
||||
|
||||
@@ -460,6 +460,11 @@ export interface CompletionItem {
|
||||
* A command that should be run upon acceptance of this item.
|
||||
*/
|
||||
command?: Command;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface CompletionList {
|
||||
@@ -1265,19 +1270,19 @@ export interface CommentThread2 {
|
||||
resource: string | null;
|
||||
range: IRange;
|
||||
label: string;
|
||||
comments: Comment[];
|
||||
onDidChangeComments: Event<Comment[]>;
|
||||
comments: Comment[] | undefined;
|
||||
onDidChangeComments: Event<Comment[] | undefined>;
|
||||
collapsibleState?: CommentThreadCollapsibleState;
|
||||
input?: CommentInput;
|
||||
onDidChangeInput: Event<CommentInput | undefined>;
|
||||
acceptInputCommand?: Command;
|
||||
additionalCommands: Command[];
|
||||
additionalCommands?: Command[];
|
||||
deleteCommand?: Command;
|
||||
onDidChangeAcceptInputCommand: Event<Command>;
|
||||
onDidChangeAdditionalCommands: Event<Command[]>;
|
||||
onDidChangeAcceptInputCommand: Event<Command | undefined>;
|
||||
onDidChangeAdditionalCommands: Event<Command[] | undefined>;
|
||||
onDidChangeRange: Event<IRange>;
|
||||
onDidChangeLabel: Event<string>;
|
||||
onDidChangeCollasibleState: Event<CommentThreadCollapsibleState>;
|
||||
onDidChangeCollasibleState: Event<CommentThreadCollapsibleState | undefined>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1299,7 +1304,7 @@ export interface CommentThread {
|
||||
threadId: string | null;
|
||||
resource: string | null;
|
||||
range: IRange;
|
||||
comments: Comment[];
|
||||
comments: Comment[] | undefined;
|
||||
collapsibleState?: CommentThreadCollapsibleState;
|
||||
reply?: Command;
|
||||
}
|
||||
|
||||
@@ -43,5 +43,5 @@ export interface ITextResourcePropertiesService {
|
||||
/**
|
||||
* Returns the End of Line characters for the given resource
|
||||
*/
|
||||
getEOL(resource: URI | null | undefined, language?: string): string;
|
||||
getEOL(resource: URI | undefined, language?: string): string;
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { equals, flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
|
||||
@@ -14,6 +14,7 @@ import { ITextModel } from 'vs/editor/common/model';
|
||||
import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './codeActionTrigger';
|
||||
import { TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState';
|
||||
|
||||
export class CodeActionSet {
|
||||
|
||||
@@ -55,14 +56,12 @@ export function getCodeActions(
|
||||
trigger: trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic
|
||||
};
|
||||
|
||||
const chainedCancellation = new CancellationTokenSource();
|
||||
token.onCancellationRequested(() => chainedCancellation.cancel());
|
||||
|
||||
const cts = new TextModelCancellationTokenSource(model, token);
|
||||
const providers = getCodeActionProviders(model, filter);
|
||||
|
||||
const promises = providers.map(provider => {
|
||||
return Promise.resolve(provider.provideCodeActions(model, rangeOrSelection, codeActionContext, chainedCancellation.token)).then(providedCodeActions => {
|
||||
if (!Array.isArray(providedCodeActions)) {
|
||||
return Promise.resolve(provider.provideCodeActions(model, rangeOrSelection, codeActionContext, cts.token)).then(providedCodeActions => {
|
||||
if (cts.token.isCancellationRequested || !Array.isArray(providedCodeActions)) {
|
||||
return [];
|
||||
}
|
||||
return providedCodeActions.filter(action => action && filtersAction(filter, action));
|
||||
@@ -79,7 +78,7 @@ export function getCodeActions(
|
||||
const listener = CodeActionProviderRegistry.onDidChange(() => {
|
||||
const newProviders = CodeActionProviderRegistry.all(model);
|
||||
if (!equals(newProviders, providers)) {
|
||||
chainedCancellation.cancel();
|
||||
cts.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -88,6 +87,7 @@ export function getCodeActions(
|
||||
.then(actions => new CodeActionSet(actions))
|
||||
.finally(() => {
|
||||
listener.dispose();
|
||||
cts.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
120
src/vs/editor/contrib/codelens/codeLensCache.ts
Normal file
120
src/vs/editor/contrib/codelens/codeLensCache.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { ICodeLensData } from 'vs/editor/contrib/codelens/codelens';
|
||||
import { LRUCache, values } from 'vs/base/common/map';
|
||||
import { ICodeLensSymbol, CodeLensProvider } from 'vs/editor/common/modes';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
|
||||
export const ICodeLensCache = createDecorator<ICodeLensCache>('ICodeLensCache');
|
||||
|
||||
export interface ICodeLensCache {
|
||||
_serviceBrand: any;
|
||||
put(model: ITextModel, data: ICodeLensData[]): void;
|
||||
get(model: ITextModel): ICodeLensData[] | undefined;
|
||||
delete(model: ITextModel): void;
|
||||
}
|
||||
|
||||
interface ISerializedCacheData {
|
||||
lineCount: number;
|
||||
lines: number[];
|
||||
}
|
||||
|
||||
class CacheItem {
|
||||
|
||||
constructor(
|
||||
readonly lineCount: number,
|
||||
readonly data: ICodeLensData[]
|
||||
) { }
|
||||
}
|
||||
|
||||
class CodeLensCache implements ICodeLensCache {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private readonly _fakeProvider = new class implements CodeLensProvider {
|
||||
provideCodeLenses(): ICodeLensSymbol[] {
|
||||
throw new Error('not supported');
|
||||
}
|
||||
};
|
||||
|
||||
private readonly _cache = new LRUCache<string, CacheItem>(20, 0.75);
|
||||
|
||||
constructor(@IStorageService storageService: IStorageService) {
|
||||
|
||||
const key = 'codelens/cache';
|
||||
|
||||
// restore lens data on start
|
||||
const raw = storageService.get(key, StorageScope.WORKSPACE, '{}');
|
||||
this._deserialize(raw);
|
||||
|
||||
// store lens data on shutdown
|
||||
const listener = storageService.onWillSaveState(() => {
|
||||
storageService.store(key, this._serialize(), StorageScope.WORKSPACE);
|
||||
listener.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
put(model: ITextModel, data: ICodeLensData[]): void {
|
||||
const item = new CacheItem(model.getLineCount(), data.map(item => {
|
||||
return {
|
||||
symbol: item.symbol,
|
||||
provider: this._fakeProvider
|
||||
};
|
||||
}));
|
||||
this._cache.set(model.uri.toString(), item);
|
||||
}
|
||||
|
||||
get(model: ITextModel) {
|
||||
const item = this._cache.get(model.uri.toString());
|
||||
return item && item.lineCount === model.getLineCount() ? item.data : undefined;
|
||||
}
|
||||
|
||||
delete(model: ITextModel): void {
|
||||
this._cache.delete(model.uri.toString());
|
||||
}
|
||||
|
||||
// --- persistence
|
||||
|
||||
private _serialize(): string {
|
||||
const data: Record<string, ISerializedCacheData> = Object.create(null);
|
||||
this._cache.forEach((value, key) => {
|
||||
const lines = new Set<number>();
|
||||
for (const d of value.data) {
|
||||
lines.add(d.symbol.range.startLineNumber);
|
||||
}
|
||||
data[key] = {
|
||||
lineCount: value.lineCount,
|
||||
lines: values(lines)
|
||||
};
|
||||
});
|
||||
return JSON.stringify(data);
|
||||
}
|
||||
|
||||
private _deserialize(raw: string): void {
|
||||
try {
|
||||
const data: Record<string, ISerializedCacheData> = JSON.parse(raw);
|
||||
for (const key in data) {
|
||||
const element = data[key];
|
||||
const symbols: ICodeLensData[] = [];
|
||||
for (const line of element.lines) {
|
||||
symbols.push({
|
||||
provider: this._fakeProvider,
|
||||
symbol: { range: new Range(line, 1, line, 11) }
|
||||
});
|
||||
}
|
||||
this._cache.set(key, new CacheItem(element.lineCount, symbols));
|
||||
}
|
||||
} catch {
|
||||
// ignore...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(ICodeLensCache, CodeLensCache);
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancelablePromise, RunOnceScheduler, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { CancelablePromise, RunOnceScheduler, createCancelablePromise, disposableTimeout } from 'vs/base/common/async';
|
||||
import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { StableEditorScrollState } from 'vs/editor/browser/core/editorState';
|
||||
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
|
||||
@@ -17,6 +17,7 @@ import { ICodeLensData, getCodeLensData } from 'vs/editor/contrib/codelens/codel
|
||||
import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { ICodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache';
|
||||
|
||||
export class CodeLensContribution implements editorCommon.IEditorContribution {
|
||||
|
||||
@@ -35,7 +36,8 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
|
||||
constructor(
|
||||
private readonly _editor: editorBrowser.ICodeEditor,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@INotificationService private readonly _notificationService: INotificationService
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@ICodeLensCache private readonly _codeLensCache: ICodeLensCache
|
||||
) {
|
||||
this._isEnabled = this._editor.getConfiguration().contribInfo.codeLens;
|
||||
|
||||
@@ -93,7 +95,23 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
|
||||
return;
|
||||
}
|
||||
|
||||
const cachedLenses = this._codeLensCache.get(model);
|
||||
if (cachedLenses) {
|
||||
this._renderCodeLensSymbols(cachedLenses);
|
||||
}
|
||||
|
||||
if (!CodeLensProviderRegistry.has(model)) {
|
||||
// no provider -> return but check with
|
||||
// cached lenses. they expire after 30 seconds
|
||||
if (cachedLenses) {
|
||||
this._localToDispose.push(disposableTimeout(() => {
|
||||
const cachedLensesNow = this._codeLensCache.get(model);
|
||||
if (cachedLenses === cachedLensesNow) {
|
||||
this._codeLensCache.delete(model);
|
||||
this._onModelChange();
|
||||
}
|
||||
}, 30 * 1000));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -106,7 +124,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
|
||||
|
||||
this._detectVisibleLenses = new RunOnceScheduler(() => {
|
||||
this._onViewportChanged();
|
||||
}, 500);
|
||||
}, 250);
|
||||
|
||||
const scheduler = new RunOnceScheduler(() => {
|
||||
const counterValue = ++this._modelChangeCounter;
|
||||
@@ -116,8 +134,9 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
|
||||
|
||||
this._currentFindCodeLensSymbolsPromise = createCancelablePromise(token => getCodeLensData(model, token));
|
||||
|
||||
this._currentFindCodeLensSymbolsPromise.then((result) => {
|
||||
this._currentFindCodeLensSymbolsPromise.then(result => {
|
||||
if (counterValue === this._modelChangeCounter) { // only the last one wins
|
||||
this._codeLensCache.put(model, result);
|
||||
this._renderCodeLensSymbols(result);
|
||||
this._detectVisibleLenses.schedule();
|
||||
}
|
||||
@@ -312,7 +331,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
|
||||
if (!request.symbol.command && typeof request.provider.resolveCodeLens === 'function') {
|
||||
return Promise.resolve(request.provider.resolveCodeLens(model, request.symbol, token)).then(symbol => {
|
||||
resolvedSymbols[i] = symbol;
|
||||
});
|
||||
}, onUnexpectedExternalError);
|
||||
} else {
|
||||
resolvedSymbols[i] = request.symbol;
|
||||
return Promise.resolve(undefined);
|
||||
|
||||
@@ -32,14 +32,11 @@
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } }
|
||||
@-moz-keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } }
|
||||
@-o-keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } }
|
||||
@-webkit-keyframes fadein { 0% { opacity:0; visibility:visible;} 100% { opacity:1; } }
|
||||
@keyframes fadein {
|
||||
0% { opacity: 0; visibility: visible;}
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
.monaco-editor .codelens-decoration.fadein {
|
||||
-webkit-animation: fadein 0.5s linear;
|
||||
-moz-animation: fadein 0.5s linear;
|
||||
-o-animation: fadein 0.5s linear;
|
||||
animation: fadein 0.5s linear;
|
||||
animation: fadein 0.1s linear;
|
||||
}
|
||||
|
||||
@@ -58,13 +58,14 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
|
||||
private readonly _id: string;
|
||||
private readonly _domNode: HTMLElement;
|
||||
private readonly _editor: editorBrowser.ICodeEditor;
|
||||
private readonly _commands = new Map<string, Command>();
|
||||
|
||||
private _widgetPosition: editorBrowser.IContentWidgetPosition;
|
||||
private _commands: { [id: string]: Command } = Object.create(null);
|
||||
|
||||
constructor(
|
||||
editor: editorBrowser.ICodeEditor,
|
||||
symbolRange: Range
|
||||
symbolRange: Range,
|
||||
data: ICodeLensData[]
|
||||
) {
|
||||
this._id = 'codeLensWidget' + (++CodeLensContentWidget._idPool);
|
||||
this._editor = editor;
|
||||
@@ -74,9 +75,8 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
|
||||
this._domNode = document.createElement('span');
|
||||
this._domNode.innerHTML = ' ';
|
||||
dom.addClass(this._domNode, 'codelens-decoration');
|
||||
dom.addClass(this._domNode, 'invisible-cl');
|
||||
this.updateHeight();
|
||||
this.updateVisibility();
|
||||
this.withCommands(data.map(data => data.symbol), false);
|
||||
}
|
||||
|
||||
updateHeight(): void {
|
||||
@@ -88,15 +88,9 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
|
||||
this._domNode.innerHTML = ' ';
|
||||
}
|
||||
|
||||
updateVisibility(): void {
|
||||
if (this.isVisible()) {
|
||||
dom.removeClass(this._domNode, 'invisible-cl');
|
||||
dom.addClass(this._domNode, 'fadein');
|
||||
}
|
||||
}
|
||||
withCommands(inSymbols: Array<ICodeLensSymbol | undefined | null>, animate: boolean): void {
|
||||
this._commands.clear();
|
||||
|
||||
withCommands(inSymbols: Array<ICodeLensSymbol | undefined | null>): void {
|
||||
this._commands = Object.create(null);
|
||||
const symbols = coalesce(inSymbols);
|
||||
if (isFalsyOrEmpty(symbols)) {
|
||||
this._domNode.innerHTML = '<span>no commands</span>';
|
||||
@@ -111,7 +105,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
|
||||
let part: string;
|
||||
if (command.id) {
|
||||
part = `<a id=${i}>${title}</a>`;
|
||||
this._commands[i] = command;
|
||||
this._commands.set(String(i), command);
|
||||
} else {
|
||||
part = `<span>${title}</span>`;
|
||||
}
|
||||
@@ -119,13 +113,17 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
|
||||
}
|
||||
}
|
||||
|
||||
const wasEmpty = this._domNode.innerHTML === '' || this._domNode.innerHTML === ' ';
|
||||
this._domNode.innerHTML = html.join('<span> | </span>');
|
||||
this._editor.layoutContentWidget(this);
|
||||
if (wasEmpty && animate) {
|
||||
dom.addClass(this._domNode, 'fadein');
|
||||
}
|
||||
}
|
||||
|
||||
getCommand(link: HTMLLinkElement): Command | undefined {
|
||||
return link.parentElement === this._domNode
|
||||
? this._commands[link.id]
|
||||
? this._commands.get(link.id)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
@@ -228,7 +226,7 @@ export class CodeLens {
|
||||
});
|
||||
|
||||
if (range) {
|
||||
this._contentWidget = new CodeLensContentWidget(editor, range);
|
||||
this._contentWidget = new CodeLensContentWidget(editor, range, this._data);
|
||||
this._viewZone = new CodeLensViewZone(range.startLineNumber - 1, updateCallback);
|
||||
|
||||
this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone);
|
||||
@@ -273,7 +271,6 @@ export class CodeLens {
|
||||
}
|
||||
|
||||
computeIfNecessary(model: ITextModel): ICodeLensData[] | null {
|
||||
this._contentWidget.updateVisibility(); // trigger the fade in
|
||||
if (!this._contentWidget.isVisible()) {
|
||||
return null;
|
||||
}
|
||||
@@ -289,7 +286,7 @@ export class CodeLens {
|
||||
}
|
||||
|
||||
updateCommands(symbols: Array<ICodeLensSymbol | undefined | null>): void {
|
||||
this._contentWidget.withCommands(symbols);
|
||||
this._contentWidget.withCommands(symbols, true);
|
||||
for (let i = 0; i < this._data.length; i++) {
|
||||
const resolved = symbols[i];
|
||||
if (resolved) {
|
||||
|
||||
@@ -274,9 +274,7 @@ export class OutlineModel extends TreeElement {
|
||||
|
||||
static _create(textModel: ITextModel, token: CancellationToken): Promise<OutlineModel> {
|
||||
|
||||
const chainedCancellation = new CancellationTokenSource();
|
||||
token.onCancellationRequested(() => chainedCancellation.cancel());
|
||||
|
||||
const cts = new CancellationTokenSource(token);
|
||||
const result = new OutlineModel(textModel);
|
||||
const provider = DocumentSymbolProviderRegistry.ordered(textModel);
|
||||
const promises = provider.map((provider, index) => {
|
||||
@@ -284,7 +282,7 @@ export class OutlineModel extends TreeElement {
|
||||
let id = TreeElement.findId(`provider_${index}`, result);
|
||||
let group = new OutlineGroup(id, result, provider, index);
|
||||
|
||||
return Promise.resolve(provider.provideDocumentSymbols(result.textModel, chainedCancellation.token)).then(result => {
|
||||
return Promise.resolve(provider.provideDocumentSymbols(result.textModel, cts.token)).then(result => {
|
||||
for (const info of result || []) {
|
||||
OutlineModel._makeOutlineElement(info, group);
|
||||
}
|
||||
@@ -304,12 +302,12 @@ export class OutlineModel extends TreeElement {
|
||||
const listener = DocumentSymbolProviderRegistry.onDidChange(() => {
|
||||
const newProvider = DocumentSymbolProviderRegistry.ordered(textModel);
|
||||
if (!equals(newProvider, provider)) {
|
||||
chainedCancellation.cancel();
|
||||
cts.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
if (chainedCancellation.token.isCancellationRequested && !token.isCancellationRequested) {
|
||||
if (cts.token.isCancellationRequested && !token.isCancellationRequested) {
|
||||
return OutlineModel._create(textModel, token);
|
||||
} else {
|
||||
return result._compact();
|
||||
|
||||
@@ -764,16 +764,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
appendWholeWordsLabel: this._keybindingLabelFor(FIND_IDS.ToggleWholeWordCommand),
|
||||
appendRegexLabel: this._keybindingLabelFor(FIND_IDS.ToggleRegexCommand),
|
||||
validation: (value: string): InputBoxMessage | null => {
|
||||
if (value.length === 0) {
|
||||
return null;
|
||||
}
|
||||
if (!this._findInput.getRegex()) {
|
||||
if (value.length === 0 || !this._findInput.getRegex()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
/* tslint:disable:no-unused-expression */
|
||||
/* tslint:disable-next-line:no-unused-expression */
|
||||
new RegExp(value);
|
||||
/* tslint:enable:no-unused-expression */
|
||||
return null;
|
||||
} catch (e) {
|
||||
return { content: e.message };
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { Delayer } from 'vs/base/common/async';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
|
||||
import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { SimpleButton } from 'vs/editor/contrib/find/findWidget';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
@@ -44,6 +45,18 @@ export abstract class SimpleFindWidget extends Widget {
|
||||
this._findInput = this._register(new ContextScopedFindInput(null, this._contextViewService, {
|
||||
label: NLS_FIND_INPUT_LABEL,
|
||||
placeholder: NLS_FIND_INPUT_PLACEHOLDER,
|
||||
validation: (value: string): InputBoxMessage | null => {
|
||||
if (value.length === 0 || !this._findInput.getRegex()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
/* tslint:disable-next-line:no-unused-expression */
|
||||
new RegExp(value);
|
||||
return null;
|
||||
} catch (e) {
|
||||
return { content: e.message };
|
||||
}
|
||||
}
|
||||
}, contextKeyService, showOptionButtons));
|
||||
|
||||
// Find History with update delayer
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CodeEditorStateFlag, EditorState } from 'vs/editor/browser/core/editorState';
|
||||
import { CodeEditorStateFlag, EditorState, EditorStateCancellationTokenSource, TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState';
|
||||
import { IActiveCodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerLanguageCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
@@ -227,26 +227,24 @@ export async function formatDocumentWithProvider(
|
||||
const workerService = accessor.get(IEditorWorkerService);
|
||||
|
||||
let model: ITextModel;
|
||||
let validate: () => boolean;
|
||||
let cts: CancellationTokenSource;
|
||||
if (isCodeEditor(editorOrModel)) {
|
||||
model = editorOrModel.getModel();
|
||||
const state = new EditorState(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
validate = () => state.validate(editorOrModel);
|
||||
cts = new EditorStateCancellationTokenSource(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position, token);
|
||||
} else {
|
||||
model = editorOrModel;
|
||||
const versionNow = editorOrModel.getVersionId();
|
||||
validate = () => versionNow === editorOrModel.getVersionId();
|
||||
cts = new TextModelCancellationTokenSource(editorOrModel, token);
|
||||
}
|
||||
|
||||
const rawEdits = await provider.provideDocumentFormattingEdits(
|
||||
model,
|
||||
model.getFormattingOptions(),
|
||||
token
|
||||
cts.token
|
||||
);
|
||||
|
||||
const edits = await workerService.computeMoreMinimalEdits(model.uri, rawEdits);
|
||||
|
||||
if (!validate()) {
|
||||
if (cts.token.isCancellationRequested) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ function getDefinitions<T>(
|
||||
const provider = registry.ordered(model);
|
||||
|
||||
// get results
|
||||
const promises = provider.map((provider): Promise<LocationLink | LocationLink[] | null | undefined> => {
|
||||
const promises = provider.map((provider): Promise<LocationLink | LocationLink[] | undefined> => {
|
||||
return Promise.resolve(provide(provider, model, position)).then(undefined, err => {
|
||||
onUnexpectedExternalError(err);
|
||||
return null;
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
return Promise.all(promises)
|
||||
|
||||
@@ -28,6 +28,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition, getDeclarationsAtPosition } from './goToDefinition';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { EditorStateCancellationTokenSource, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState';
|
||||
|
||||
export class DefinitionActionConfig {
|
||||
|
||||
@@ -61,9 +62,11 @@ export class DefinitionAction extends EditorAction {
|
||||
const model = editor.getModel();
|
||||
const pos = editor.getPosition();
|
||||
|
||||
const definitionPromise = this._getTargetLocationForPosition(model, pos, CancellationToken.None).then(async references => {
|
||||
const cts = new EditorStateCancellationTokenSource(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
|
||||
if (model.isDisposed() || editor.getModel() !== model) {
|
||||
const definitionPromise = this._getTargetLocationForPosition(model, pos, cts.token).then(async references => {
|
||||
|
||||
if (cts.token.isCancellationRequested || model.isDisposed() || editor.getModel() !== model) {
|
||||
// new model, no more model
|
||||
return;
|
||||
}
|
||||
@@ -105,6 +108,8 @@ export class DefinitionAction extends EditorAction {
|
||||
}, (err) => {
|
||||
// report an error
|
||||
notificationService.error(err);
|
||||
}).finally(() => {
|
||||
cts.dispose();
|
||||
});
|
||||
|
||||
progressService.showWhile(definitionPromise, 250);
|
||||
|
||||
@@ -886,6 +886,8 @@ export abstract class AbstractCaseAction extends EditorAction {
|
||||
return;
|
||||
}
|
||||
|
||||
let wordSeparators = editor.getConfiguration().wordSeparators;
|
||||
|
||||
let commands: ICommand[] = [];
|
||||
|
||||
for (let i = 0, len = selections.length; i < len; i++) {
|
||||
@@ -900,12 +902,12 @@ export abstract class AbstractCaseAction extends EditorAction {
|
||||
|
||||
let wordRange = new Range(cursor.lineNumber, word.startColumn, cursor.lineNumber, word.endColumn);
|
||||
let text = model.getValueInRange(wordRange);
|
||||
commands.push(new ReplaceCommandThatPreservesSelection(wordRange, this._modifyText(text),
|
||||
commands.push(new ReplaceCommandThatPreservesSelection(wordRange, this._modifyText(text, wordSeparators),
|
||||
new Selection(cursor.lineNumber, cursor.column, cursor.lineNumber, cursor.column)));
|
||||
|
||||
} else {
|
||||
let text = model.getValueInRange(selection);
|
||||
commands.push(new ReplaceCommandThatPreservesSelection(selection, this._modifyText(text), selection));
|
||||
commands.push(new ReplaceCommandThatPreservesSelection(selection, this._modifyText(text, wordSeparators), selection));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -914,7 +916,7 @@ export abstract class AbstractCaseAction extends EditorAction {
|
||||
editor.pushUndoStop();
|
||||
}
|
||||
|
||||
protected abstract _modifyText(text: string): string;
|
||||
protected abstract _modifyText(text: string, wordSeparators: string): string;
|
||||
}
|
||||
|
||||
export class UpperCaseAction extends AbstractCaseAction {
|
||||
@@ -927,7 +929,7 @@ export class UpperCaseAction extends AbstractCaseAction {
|
||||
});
|
||||
}
|
||||
|
||||
protected _modifyText(text: string): string {
|
||||
protected _modifyText(text: string, wordSeparators: string): string {
|
||||
return text.toLocaleUpperCase();
|
||||
}
|
||||
}
|
||||
@@ -942,11 +944,48 @@ export class LowerCaseAction extends AbstractCaseAction {
|
||||
});
|
||||
}
|
||||
|
||||
protected _modifyText(text: string): string {
|
||||
protected _modifyText(text: string, wordSeparators: string): string {
|
||||
return text.toLocaleLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
export class TitleCaseAction extends AbstractCaseAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.action.transformToTitlecase',
|
||||
label: nls.localize('editor.transformToTitlecase', "Transform to Title Case"),
|
||||
alias: 'Transform to Title Case',
|
||||
precondition: EditorContextKeys.writable
|
||||
});
|
||||
}
|
||||
|
||||
protected _modifyText(text: string, wordSeparators: string): string {
|
||||
const separators = '\r\n\t ' + wordSeparators;
|
||||
const excludedChars = separators.split('');
|
||||
|
||||
let title = '';
|
||||
let startUpperCase = true;
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
let currentChar = text[i];
|
||||
|
||||
if (excludedChars.indexOf(currentChar) >= 0) {
|
||||
startUpperCase = true;
|
||||
|
||||
title += currentChar;
|
||||
} else if (startUpperCase) {
|
||||
startUpperCase = false;
|
||||
|
||||
title += currentChar.toLocaleUpperCase();
|
||||
} else {
|
||||
title += currentChar.toLocaleLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorAction(CopyLinesUpAction);
|
||||
registerEditorAction(CopyLinesDownAction);
|
||||
registerEditorAction(MoveLinesUpAction);
|
||||
@@ -965,3 +1004,4 @@ registerEditorAction(JoinLinesAction);
|
||||
registerEditorAction(TransposeAction);
|
||||
registerEditorAction(UpperCaseAction);
|
||||
registerEditorAction(LowerCaseAction);
|
||||
registerEditorAction(TitleCaseAction);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Position } from 'vs/editor/common/core/position';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { Handler } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { DeleteAllLeftAction, DeleteAllRightAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TransposeAction, UpperCaseAction, DeleteLinesAction } from 'vs/editor/contrib/linesOperations/linesOperations';
|
||||
import { TitleCaseAction, DeleteAllLeftAction, DeleteAllRightAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TransposeAction, UpperCaseAction, DeleteLinesAction } from 'vs/editor/contrib/linesOperations/linesOperations';
|
||||
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
|
||||
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
|
||||
|
||||
@@ -529,6 +529,7 @@ suite('Editor Contrib - Line Operations', () => {
|
||||
let model = editor.getModel()!;
|
||||
let uppercaseAction = new UpperCaseAction();
|
||||
let lowercaseAction = new LowerCaseAction();
|
||||
let titlecaseAction = new TitleCaseAction();
|
||||
|
||||
editor.setSelection(new Selection(1, 1, 1, 12));
|
||||
uppercaseAction.run(null!, editor);
|
||||
@@ -550,15 +551,63 @@ suite('Editor Contrib - Line Operations', () => {
|
||||
assert.equal(model.getLineContent(1), 'hello world', '007');
|
||||
assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 4, 1, 4).toString(), '008');
|
||||
|
||||
editor.setSelection(new Selection(1, 1, 1, 12));
|
||||
titlecaseAction.run(null!, editor);
|
||||
assert.equal(model.getLineContent(1), 'Hello World', '009');
|
||||
assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 1, 12).toString(), '010');
|
||||
|
||||
editor.setSelection(new Selection(2, 1, 2, 6));
|
||||
uppercaseAction.run(null!, editor);
|
||||
assert.equal(model.getLineContent(2), 'ÖÇŞĞÜ', '009');
|
||||
assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 6).toString(), '010');
|
||||
assert.equal(model.getLineContent(2), 'ÖÇŞĞÜ', '011');
|
||||
assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 6).toString(), '012');
|
||||
|
||||
editor.setSelection(new Selection(2, 1, 2, 6));
|
||||
lowercaseAction.run(null!, editor);
|
||||
assert.equal(model.getLineContent(2), 'öçşğü', '011');
|
||||
assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 6).toString(), '012');
|
||||
assert.equal(model.getLineContent(2), 'öçşğü', '013');
|
||||
assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 6).toString(), '014');
|
||||
|
||||
editor.setSelection(new Selection(2, 1, 2, 6));
|
||||
titlecaseAction.run(null!, editor);
|
||||
assert.equal(model.getLineContent(2), 'Öçşğü', '015');
|
||||
assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 6).toString(), '016');
|
||||
}
|
||||
);
|
||||
|
||||
withTestCodeEditor(
|
||||
[
|
||||
'foO baR BaZ',
|
||||
'foO\'baR\'BaZ',
|
||||
'foO[baR]BaZ',
|
||||
'foO`baR~BaZ',
|
||||
'foO^baR%BaZ',
|
||||
'foO$baR!BaZ'
|
||||
], {}, (editor) => {
|
||||
let model = editor.getModel()!;
|
||||
let titlecaseAction = new TitleCaseAction();
|
||||
|
||||
editor.setSelection(new Selection(1, 1, 1, 12));
|
||||
titlecaseAction.run(null!, editor);
|
||||
assert.equal(model.getLineContent(1), 'Foo Bar Baz');
|
||||
|
||||
editor.setSelection(new Selection(2, 1, 2, 12));
|
||||
titlecaseAction.run(null!, editor);
|
||||
assert.equal(model.getLineContent(2), 'Foo\'Bar\'Baz');
|
||||
|
||||
editor.setSelection(new Selection(3, 1, 3, 12));
|
||||
titlecaseAction.run(null!, editor);
|
||||
assert.equal(model.getLineContent(3), 'Foo[Bar]Baz');
|
||||
|
||||
editor.setSelection(new Selection(4, 1, 4, 12));
|
||||
titlecaseAction.run(null!, editor);
|
||||
assert.equal(model.getLineContent(4), 'Foo`Bar~Baz');
|
||||
|
||||
editor.setSelection(new Selection(5, 1, 5, 12));
|
||||
titlecaseAction.run(null!, editor);
|
||||
assert.equal(model.getLineContent(5), 'Foo^Bar%Baz');
|
||||
|
||||
editor.setSelection(new Selection(6, 1, 6, 12));
|
||||
titlecaseAction.run(null!, editor);
|
||||
assert.equal(model.getLineContent(6), 'Foo$Bar!Baz');
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ITextModel } from 'vs/editor/common/model';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/snippetParser';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, pad } from 'vs/base/common/strings';
|
||||
import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, pad, endsWith } from 'vs/base/common/strings';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
|
||||
@@ -269,7 +269,10 @@ export class WorkspaceBasedVariableResolver implements VariableResolver {
|
||||
return basename(workspaceIdentifier.path);
|
||||
}
|
||||
|
||||
const filename = basename(workspaceIdentifier.configPath.path);
|
||||
return filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1);
|
||||
let filename = basename(workspaceIdentifier.configPath.path);
|
||||
if (endsWith(filename, WORKSPACE_EXTENSION)) {
|
||||
filename = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1);
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
* into a typed and checked ILexer definition.
|
||||
*/
|
||||
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as monarchCommon from 'vs/editor/standalone/common/monarch/monarchCommon';
|
||||
import { IMonarchLanguage, IMonarchLanguageBracket } from 'vs/editor/standalone/common/monarch/monarchTypes';
|
||||
|
||||
@@ -52,6 +51,33 @@ function string(prop: any, defValue: string): string {
|
||||
return defValue;
|
||||
}
|
||||
|
||||
|
||||
function arrayToHash(array: string[]): { [name: string]: true } {
|
||||
const result: any = {};
|
||||
for (const e of array) {
|
||||
result[e] = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function createKeywordMatcher(arr: string[], caseInsensitive: boolean = false): (str: string) => boolean {
|
||||
if (caseInsensitive) {
|
||||
arr = arr.map(function (x) { return x.toLowerCase(); });
|
||||
}
|
||||
const hash = arrayToHash(arr);
|
||||
if (caseInsensitive) {
|
||||
return function (word) {
|
||||
return hash[word.toLowerCase()] !== undefined && hash.hasOwnProperty(word.toLowerCase());
|
||||
};
|
||||
} else {
|
||||
return function (word) {
|
||||
return hash[word] !== undefined && hash.hasOwnProperty(word);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Lexer helpers
|
||||
|
||||
/**
|
||||
@@ -142,7 +168,7 @@ function createGuard(lexer: monarchCommon.ILexerMin, ruleName: string, tkey: str
|
||||
|
||||
// special case a regexp that matches just words
|
||||
if ((op === '~' || op === '!~') && /^(\w|\|)*$/.test(pat)) {
|
||||
let inWords = objects.createKeywordMatcher(pat.split('|'), lexer.ignoreCase);
|
||||
let inWords = createKeywordMatcher(pat.split('|'), lexer.ignoreCase);
|
||||
tester = function (s) { return (op === '~' ? inWords(s) : !inWords(s)); };
|
||||
}
|
||||
else if (op === '@' || op === '!@') {
|
||||
@@ -153,7 +179,7 @@ function createGuard(lexer: monarchCommon.ILexerMin, ruleName: string, tkey: str
|
||||
if (!(isArrayOf(function (elem) { return (typeof (elem) === 'string'); }, words))) {
|
||||
throw monarchCommon.createError(lexer, 'the @ match target \'' + pat + '\' must be an array of strings, in rule: ' + ruleName);
|
||||
}
|
||||
let inWords = objects.createKeywordMatcher(words, lexer.ignoreCase);
|
||||
let inWords = createKeywordMatcher(words, lexer.ignoreCase);
|
||||
tester = function (s) { return (op === '@' ? inWords(s) : !inWords(s)); };
|
||||
}
|
||||
else if (op === '~' || op === '!~') {
|
||||
|
||||
@@ -657,6 +657,26 @@ suite('Editor Model - TextModel', () => {
|
||||
assert.deepEqual(m.validatePosition(new Position(NaN, 3)), new Position(1, 3));
|
||||
});
|
||||
|
||||
test('issue #71480: validatePosition handle floats', () => {
|
||||
let m = TextModel.createFromString('line one\nline two');
|
||||
|
||||
assert.deepEqual(m.validatePosition(new Position(0.2, 1)), new Position(1, 1), 'a');
|
||||
assert.deepEqual(m.validatePosition(new Position(1.2, 1)), new Position(1, 1), 'b');
|
||||
assert.deepEqual(m.validatePosition(new Position(1.5, 2)), new Position(1, 2), 'c');
|
||||
assert.deepEqual(m.validatePosition(new Position(1.8, 3)), new Position(1, 3), 'd');
|
||||
assert.deepEqual(m.validatePosition(new Position(1, 0.3)), new Position(1, 1), 'e');
|
||||
assert.deepEqual(m.validatePosition(new Position(2, 0.8)), new Position(2, 1), 'f');
|
||||
assert.deepEqual(m.validatePosition(new Position(1, 1.2)), new Position(1, 1), 'g');
|
||||
assert.deepEqual(m.validatePosition(new Position(2, 1.5)), new Position(2, 1), 'h');
|
||||
});
|
||||
|
||||
test('issue #71480: validateRange handle floats', () => {
|
||||
let m = TextModel.createFromString('line one\nline two');
|
||||
|
||||
assert.deepEqual(m.validateRange(new Range(0.2, 1.5, 0.8, 2.5)), new Range(1, 1, 1, 1));
|
||||
assert.deepEqual(m.validateRange(new Range(1.2, 1.7, 1.8, 2.2)), new Range(1, 1, 1, 2));
|
||||
});
|
||||
|
||||
test('validateRange around high-low surrogate pairs 1', () => {
|
||||
|
||||
let m = TextModel.createFromString('a📚b');
|
||||
|
||||
Reference in New Issue
Block a user