Merge from vscode 2b0b9136329c181a9e381463a1f7dc3a2d105a34 (#4880)

This commit is contained in:
Karl Burtram
2019-04-05 10:09:18 -07:00
committed by GitHub
parent 9bd7e30d18
commit cb5bcf2248
433 changed files with 8915 additions and 8361 deletions

View File

@@ -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 {

View File

@@ -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);
});
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();
});
}

View 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);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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 = '&nbsp;';
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 = '&nbsp;';
}
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 === '&nbsp;';
this._domNode.innerHTML = html.join('<span>&nbsp;|&nbsp;</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) {

View File

@@ -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();

View File

@@ -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 };

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);

View File

@@ -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');
}
);

View File

@@ -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;
}
}

View File

@@ -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 === '!~') {

View File

@@ -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');