mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-10 18:22:34 -05:00
Merge from vscode ad407028575a77ea387eb7cc219b323dc017b686
This commit is contained in:
committed by
Anthony Dresser
parent
404260b8a0
commit
4ad73d381c
@@ -124,12 +124,12 @@ class DomCharWidthReader {
|
||||
|
||||
private static _render(testElement: HTMLElement, request: CharWidthRequest): void {
|
||||
if (request.chr === ' ') {
|
||||
let htmlString = ' ';
|
||||
let htmlString = '\u00a0';
|
||||
// Repeat character 256 (2^8) times
|
||||
for (let i = 0; i < 8; i++) {
|
||||
htmlString += htmlString;
|
||||
}
|
||||
testElement.innerHTML = htmlString;
|
||||
testElement.innerText = htmlString;
|
||||
} else {
|
||||
let testString = request.chr;
|
||||
// Repeat character 256 (2^8) times
|
||||
|
||||
@@ -559,9 +559,9 @@ export namespace CoreNavigationCommands {
|
||||
case CursorMove_.Direction.ViewPortCenter:
|
||||
case CursorMove_.Direction.ViewPortIfOutside:
|
||||
return CursorMoveCommands.viewportMove(viewModel, cursors, args.direction, inSelectionMode, value);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -178,14 +178,7 @@ export class TextAreaHandler extends ViewPart {
|
||||
mode
|
||||
};
|
||||
},
|
||||
|
||||
getScreenReaderContent: (currentState: TextAreaState): TextAreaState => {
|
||||
|
||||
if (browser.isIPad) {
|
||||
// Do not place anything in the textarea for the iPad
|
||||
return TextAreaState.EMPTY;
|
||||
}
|
||||
|
||||
if (this._accessibilitySupport === AccessibilitySupport.Disabled) {
|
||||
// We know for a fact that a screen reader is not attached
|
||||
// On OSX, we write the character before the cursor to allow for "long-press" composition
|
||||
|
||||
@@ -580,6 +580,11 @@ export interface ICodeEditor extends editorCommon.IEditor {
|
||||
*/
|
||||
getRawOptions(): IEditorOptions;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
getOverflowWidgetsDomNode(): HTMLElement | undefined;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@@ -1055,3 +1060,14 @@ export function getCodeEditor(thing: any): ICodeEditor | null {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*@internal
|
||||
*/
|
||||
export function getIEditor(thing: any): editorCommon.IEditor | null {
|
||||
if (isCodeEditor(thing) || isDiffEditor(thing)) {
|
||||
return thing;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -109,8 +109,9 @@ export class ViewController {
|
||||
return data.ctrlKey;
|
||||
case 'metaKey':
|
||||
return data.metaKey;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private _hasNonMulticursorModifier(data: IMouseDispatchData): boolean {
|
||||
@@ -121,8 +122,9 @@ export class ViewController {
|
||||
return data.altKey || data.metaKey;
|
||||
case 'metaKey':
|
||||
return data.ctrlKey || data.altKey;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public dispatchMouse(data: IMouseDispatchData): void {
|
||||
|
||||
@@ -379,6 +379,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
return this._configuration.getRawOptions();
|
||||
}
|
||||
|
||||
public getOverflowWidgetsDomNode(): HTMLElement | undefined {
|
||||
return this._overflowWidgetsDomNode;
|
||||
}
|
||||
|
||||
public getConfiguredWordAtPosition(position: Position): IWordAtPosition | null {
|
||||
if (!this._modelData) {
|
||||
return null;
|
||||
|
||||
@@ -702,7 +702,7 @@ export class DiffReview extends Disposable {
|
||||
if (originalLine !== 0) {
|
||||
originalLineNumber.appendChild(document.createTextNode(String(originalLine)));
|
||||
} else {
|
||||
originalLineNumber.innerHTML = ' ';
|
||||
originalLineNumber.innerText = '\u00a0';
|
||||
}
|
||||
cell.appendChild(originalLineNumber);
|
||||
|
||||
@@ -714,7 +714,7 @@ export class DiffReview extends Disposable {
|
||||
if (modifiedLine !== 0) {
|
||||
modifiedLineNumber.appendChild(document.createTextNode(String(modifiedLine)));
|
||||
} else {
|
||||
modifiedLineNumber.innerHTML = ' ';
|
||||
modifiedLineNumber.innerText = '\u00a0';
|
||||
}
|
||||
cell.appendChild(modifiedLineNumber);
|
||||
|
||||
@@ -724,10 +724,10 @@ export class DiffReview extends Disposable {
|
||||
if (spacerIcon) {
|
||||
const spacerCodicon = document.createElement('span');
|
||||
spacerCodicon.className = spacerIcon.classNames;
|
||||
spacerCodicon.innerHTML = '  ';
|
||||
spacerCodicon.innerText = '\u00a0\u00a0';
|
||||
spacer.appendChild(spacerCodicon);
|
||||
} else {
|
||||
spacer.innerHTML = '  ';
|
||||
spacer.innerText = '\u00a0\u00a0';
|
||||
}
|
||||
cell.appendChild(spacer);
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export class EmbeddedCodeEditorWidget extends CodeEditorWidget {
|
||||
@INotificationService notificationService: INotificationService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
super(domElement, parentEditor.getRawOptions(), {}, instantiationService, codeEditorService, commandService, contextKeyService, themeService, notificationService, accessibilityService);
|
||||
super(domElement, { ...parentEditor.getRawOptions(), overflowWidgetsDomNode: parentEditor.getOverflowWidgetsDomNode() }, {}, instantiationService, codeEditorService, commandService, contextKeyService, themeService, notificationService, accessibilityService);
|
||||
|
||||
this._parentEditor = parentEditor;
|
||||
this._overwriteOptions = options;
|
||||
|
||||
@@ -1286,6 +1286,10 @@ class EditorEmptySelectionClipboard extends EditorBooleanOption<EditorOption.emp
|
||||
* Configuration options for editor find widget
|
||||
*/
|
||||
export interface IEditorFindOptions {
|
||||
/**
|
||||
* Controls whether the cursor should move to find matches while typing.
|
||||
*/
|
||||
cursorMoveOnType?: boolean;
|
||||
/**
|
||||
* Controls if we seed search string in the Find Widget with editor selection.
|
||||
*/
|
||||
@@ -1315,6 +1319,7 @@ class EditorFind extends BaseEditorOption<EditorOption.find, EditorFindOptions>
|
||||
|
||||
constructor() {
|
||||
const defaults: EditorFindOptions = {
|
||||
cursorMoveOnType: true,
|
||||
seedSearchStringFromSelection: true,
|
||||
autoFindInSelection: 'never',
|
||||
globalFindClipboard: false,
|
||||
@@ -1324,6 +1329,11 @@ class EditorFind extends BaseEditorOption<EditorOption.find, EditorFindOptions>
|
||||
super(
|
||||
EditorOption.find, 'find', defaults,
|
||||
{
|
||||
'editor.find.cursorMoveOnType': {
|
||||
type: 'boolean',
|
||||
default: defaults.cursorMoveOnType,
|
||||
description: nls.localize('find.cursorMoveOnType', "Controls whether the cursor should jump to find matches while typing.")
|
||||
},
|
||||
'editor.find.seedSearchStringFromSelection': {
|
||||
type: 'boolean',
|
||||
default: defaults.seedSearchStringFromSelection,
|
||||
@@ -1367,6 +1377,7 @@ class EditorFind extends BaseEditorOption<EditorOption.find, EditorFindOptions>
|
||||
}
|
||||
const input = _input as IEditorFindOptions;
|
||||
return {
|
||||
cursorMoveOnType: EditorBooleanOption.boolean(input.cursorMoveOnType, this.defaultValue.cursorMoveOnType),
|
||||
seedSearchStringFromSelection: EditorBooleanOption.boolean(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection),
|
||||
autoFindInSelection: typeof _input.autoFindInSelection === 'boolean'
|
||||
? (_input.autoFindInSelection ? 'always' : 'never')
|
||||
|
||||
@@ -317,9 +317,10 @@ export class CursorMoveCommands {
|
||||
// Move to the last non-whitespace column of the current view line
|
||||
return this._moveToViewLastNonWhitespaceColumn(viewModel, cursors, inSelectionMode);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static viewportMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.ViewportDirection, inSelectionMode: boolean, value: number): PartialCursorState[] | null {
|
||||
@@ -353,9 +354,9 @@ export class CursorMoveCommands {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static findPositionInViewportIfOutside(viewModel: IViewModel, cursor: CursorState, visibleViewRange: Range, inSelectionMode: boolean): PartialCursorState {
|
||||
|
||||
@@ -800,7 +800,7 @@ export interface ITextModel {
|
||||
/**
|
||||
* Search the model.
|
||||
* @param searchString The string used to search. If it is a regular expression, set `isRegex` to true.
|
||||
* @param searchScope Limit the searching to only search inside this range.
|
||||
* @param searchScope Limit the searching to only search inside these ranges.
|
||||
* @param isRegex Used to indicate that `searchString` is a regular expression.
|
||||
* @param matchCase Force the matching to match lower/upper case exactly.
|
||||
* @param wordSeparators Force the matching to match entire words only. Pass null otherwise.
|
||||
@@ -808,7 +808,7 @@ export interface ITextModel {
|
||||
* @param limitResultCount Limit the number of results
|
||||
* @return The ranges where the matches are. It is empty if no matches have been found.
|
||||
*/
|
||||
findMatches(searchString: string, searchScope: IRange, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[];
|
||||
findMatches(searchString: string, searchScope: IRange | IRange[], isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[];
|
||||
/**
|
||||
* Search the model for the next match. Loops to the beginning of the model if needed.
|
||||
* @param searchString The string used to search. If it is a regular expression, set `isRegex` to true.
|
||||
|
||||
@@ -214,8 +214,9 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable {
|
||||
return '\r\n';
|
||||
case EndOfLinePreference.TextDefined:
|
||||
return this.getEOL();
|
||||
default:
|
||||
throw new Error('Unknown EOL preference');
|
||||
}
|
||||
throw new Error('Unknown EOL preference');
|
||||
}
|
||||
|
||||
public setEOL(newEOL: '\r\n' | '\n'): void {
|
||||
|
||||
@@ -1121,13 +1121,35 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
public findMatches(searchString: string, rawSearchScope: any, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount: number = LIMIT_FIND_COUNT): model.FindMatch[] {
|
||||
this._assertNotDisposed();
|
||||
|
||||
let searchRange: Range;
|
||||
if (Range.isIRange(rawSearchScope)) {
|
||||
searchRange = this.validateRange(rawSearchScope);
|
||||
} else {
|
||||
searchRange = this.getFullModelRange();
|
||||
let searchRanges: Range[] | null = null;
|
||||
|
||||
if (rawSearchScope !== null) {
|
||||
if (!Array.isArray(rawSearchScope)) {
|
||||
rawSearchScope = [rawSearchScope];
|
||||
}
|
||||
|
||||
if (rawSearchScope.every((searchScope: Range) => Range.isIRange(searchScope))) {
|
||||
searchRanges = rawSearchScope.map((searchScope: Range) => this.validateRange(searchScope));
|
||||
}
|
||||
}
|
||||
|
||||
if (searchRanges === null) {
|
||||
searchRanges = [this.getFullModelRange()];
|
||||
}
|
||||
|
||||
searchRanges = searchRanges.sort((d1, d2) => d1.startLineNumber - d2.startLineNumber || d1.startColumn - d2.startColumn);
|
||||
|
||||
const uniqueSearchRanges: Range[] = [];
|
||||
uniqueSearchRanges.push(searchRanges.reduce((prev, curr) => {
|
||||
if (Range.areIntersecting(prev, curr)) {
|
||||
return prev.plusRange(curr);
|
||||
}
|
||||
|
||||
uniqueSearchRanges.push(prev);
|
||||
return curr;
|
||||
}));
|
||||
|
||||
let matchMapper: (value: Range, index: number, array: Range[]) => model.FindMatch[];
|
||||
if (!isRegex && searchString.indexOf('\n') < 0) {
|
||||
// not regex, not multi line
|
||||
const searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
|
||||
@@ -1137,10 +1159,12 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);
|
||||
matchMapper = (searchRange: Range) => this.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);
|
||||
} else {
|
||||
matchMapper = (searchRange: Range) => TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount);
|
||||
}
|
||||
|
||||
return TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount);
|
||||
return uniqueSearchRanges.map(matchMapper).reduce((arr, matches: model.FindMatch[]) => arr.concat(matches), []);
|
||||
}
|
||||
|
||||
public findNextMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch | null {
|
||||
|
||||
@@ -813,12 +813,12 @@ export interface DocumentHighlightProvider {
|
||||
*/
|
||||
export interface OnTypeRenameProvider {
|
||||
|
||||
stopPattern?: RegExp;
|
||||
wordPattern?: RegExp;
|
||||
|
||||
/**
|
||||
* Provide a list of ranges that can be live-renamed together.
|
||||
*/
|
||||
provideOnTypeRenameRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<IRange[]>;
|
||||
provideOnTypeRenameRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<{ ranges: IRange[]; wordPattern?: RegExp; }>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -95,7 +95,7 @@ export class CodeLensContribution implements IEditorContribution {
|
||||
.monaco-editor .codelens-decoration.${this._styleClassName} { height: ${height}px; line-height: ${lineHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;}
|
||||
.monaco-editor .codelens-decoration.${this._styleClassName} > a > .codicon { line-height: ${lineHeight}px; font-size: ${fontSize}px; }
|
||||
`;
|
||||
this._styleElement.innerHTML = newStyle;
|
||||
this._styleElement.textContent = newStyle;
|
||||
}
|
||||
|
||||
private _localDispose(): void {
|
||||
@@ -470,5 +470,3 @@ registerEditorAction(class ShowLensesInCurrentLine extends EditorAction {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ class CodeLensContentWidget implements IContentWidget {
|
||||
} else {
|
||||
// symbols and commands
|
||||
if (!innerHtml) {
|
||||
innerHtml = ' ';
|
||||
innerHtml = '\u00a0';
|
||||
}
|
||||
this._domNode.innerHTML = innerHtml;
|
||||
if (this._isEmpty && animate) {
|
||||
|
||||
@@ -233,12 +233,22 @@ export class CommonFindController extends Disposable implements IEditorContribut
|
||||
this._state.change({ searchScope: null }, true);
|
||||
} else {
|
||||
if (this._editor.hasModel()) {
|
||||
let selection = this._editor.getSelection();
|
||||
if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) {
|
||||
selection = selection.setEndPosition(selection.endLineNumber - 1, this._editor.getModel().getLineMaxColumn(selection.endLineNumber - 1));
|
||||
}
|
||||
if (!selection.isEmpty()) {
|
||||
this._state.change({ searchScope: selection }, true);
|
||||
let selections = this._editor.getSelections();
|
||||
selections.map(selection => {
|
||||
if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) {
|
||||
selection = selection.setEndPosition(
|
||||
selection.endLineNumber - 1,
|
||||
this._editor.getModel()!.getLineMaxColumn(selection.endLineNumber - 1)
|
||||
);
|
||||
}
|
||||
if (!selection.isEmpty()) {
|
||||
return selection;
|
||||
}
|
||||
return null;
|
||||
}).filter(element => !!element);
|
||||
|
||||
if (selections.length) {
|
||||
this._state.change({ searchScope: selections }, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,9 +309,9 @@ export class CommonFindController extends Disposable implements IEditorContribut
|
||||
}
|
||||
|
||||
if (opts.updateSearchScope) {
|
||||
let currentSelection = this._editor.getSelection();
|
||||
if (!currentSelection.isEmpty()) {
|
||||
stateChanges.searchScope = currentSelection;
|
||||
let currentSelections = this._editor.getSelections();
|
||||
if (currentSelections.some(selection => !selection.isEmpty())) {
|
||||
stateChanges.searchScope = currentSelections;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export class FindDecorations implements IDisposable {
|
||||
private readonly _editor: IActiveCodeEditor;
|
||||
private _decorations: string[];
|
||||
private _overviewRulerApproximateDecorations: string[];
|
||||
private _findScopeDecorationId: string | null;
|
||||
private _findScopeDecorationIds: string[];
|
||||
private _rangeHighlightDecorationId: string | null;
|
||||
private _highlightedDecorationId: string | null;
|
||||
private _startPosition: Position;
|
||||
@@ -26,7 +26,7 @@ export class FindDecorations implements IDisposable {
|
||||
this._editor = editor;
|
||||
this._decorations = [];
|
||||
this._overviewRulerApproximateDecorations = [];
|
||||
this._findScopeDecorationId = null;
|
||||
this._findScopeDecorationIds = [];
|
||||
this._rangeHighlightDecorationId = null;
|
||||
this._highlightedDecorationId = null;
|
||||
this._startPosition = this._editor.getPosition();
|
||||
@@ -37,7 +37,7 @@ export class FindDecorations implements IDisposable {
|
||||
|
||||
this._decorations = [];
|
||||
this._overviewRulerApproximateDecorations = [];
|
||||
this._findScopeDecorationId = null;
|
||||
this._findScopeDecorationIds = [];
|
||||
this._rangeHighlightDecorationId = null;
|
||||
this._highlightedDecorationId = null;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ export class FindDecorations implements IDisposable {
|
||||
public reset(): void {
|
||||
this._decorations = [];
|
||||
this._overviewRulerApproximateDecorations = [];
|
||||
this._findScopeDecorationId = null;
|
||||
this._findScopeDecorationIds = [];
|
||||
this._rangeHighlightDecorationId = null;
|
||||
this._highlightedDecorationId = null;
|
||||
}
|
||||
@@ -54,9 +54,22 @@ export class FindDecorations implements IDisposable {
|
||||
return this._decorations.length;
|
||||
}
|
||||
|
||||
/** @deprecated use getFindScopes to support multiple selections */
|
||||
public getFindScope(): Range | null {
|
||||
if (this._findScopeDecorationId) {
|
||||
return this._editor.getModel().getDecorationRange(this._findScopeDecorationId);
|
||||
if (this._findScopeDecorationIds[0]) {
|
||||
return this._editor.getModel().getDecorationRange(this._findScopeDecorationIds[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public getFindScopes(): Range[] | null {
|
||||
if (this._findScopeDecorationIds.length) {
|
||||
const scopes = this._findScopeDecorationIds.map(findScopeDecorationId =>
|
||||
this._editor.getModel().getDecorationRange(findScopeDecorationId)
|
||||
).filter(element => !!element);
|
||||
if (scopes.length) {
|
||||
return scopes as Range[];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -133,7 +146,7 @@ export class FindDecorations implements IDisposable {
|
||||
return matchPosition;
|
||||
}
|
||||
|
||||
public set(findMatches: FindMatch[], findScope: Range | null): void {
|
||||
public set(findMatches: FindMatch[], findScopes: Range[] | null): void {
|
||||
this._editor.changeDecorations((accessor) => {
|
||||
|
||||
let findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION;
|
||||
@@ -195,12 +208,12 @@ export class FindDecorations implements IDisposable {
|
||||
}
|
||||
|
||||
// Find scope
|
||||
if (this._findScopeDecorationId) {
|
||||
accessor.removeDecoration(this._findScopeDecorationId);
|
||||
this._findScopeDecorationId = null;
|
||||
if (this._findScopeDecorationIds.length) {
|
||||
this._findScopeDecorationIds.forEach(findScopeDecorationId => accessor.removeDecoration(findScopeDecorationId));
|
||||
this._findScopeDecorationIds = [];
|
||||
}
|
||||
if (findScope) {
|
||||
this._findScopeDecorationId = accessor.addDecoration(findScope, FindDecorations._FIND_SCOPE_DECORATION);
|
||||
if (findScopes?.length) {
|
||||
this._findScopeDecorationIds = findScopes.map(findScope => accessor.addDecoration(findScope, FindDecorations._FIND_SCOPE_DECORATION));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -253,8 +266,8 @@ export class FindDecorations implements IDisposable {
|
||||
let result: string[] = [];
|
||||
result = result.concat(this._decorations);
|
||||
result = result.concat(this._overviewRulerApproximateDecorations);
|
||||
if (this._findScopeDecorationId) {
|
||||
result.push(this._findScopeDecorationId);
|
||||
if (this._findScopeDecorationIds.length) {
|
||||
result.push(...this._findScopeDecorationIds);
|
||||
}
|
||||
if (this._rangeHighlightDecorationId) {
|
||||
result.push(this._rangeHighlightDecorationId);
|
||||
|
||||
@@ -169,26 +169,36 @@ export class FindModelBoundToEditorModel {
|
||||
return model.getFullModelRange();
|
||||
}
|
||||
|
||||
private research(moveCursor: boolean, newFindScope?: Range | null): void {
|
||||
let findScope: Range | null = null;
|
||||
private research(moveCursor: boolean, newFindScope?: Range | Range[] | null): void {
|
||||
let findScopes: Range[] | null = null;
|
||||
if (typeof newFindScope !== 'undefined') {
|
||||
findScope = newFindScope;
|
||||
} else {
|
||||
findScope = this._decorations.getFindScope();
|
||||
}
|
||||
if (findScope !== null) {
|
||||
if (findScope.startLineNumber !== findScope.endLineNumber) {
|
||||
if (findScope.endColumn === 1) {
|
||||
findScope = new Range(findScope.startLineNumber, 1, findScope.endLineNumber - 1, this._editor.getModel().getLineMaxColumn(findScope.endLineNumber - 1));
|
||||
if (newFindScope !== null) {
|
||||
if (!Array.isArray(newFindScope)) {
|
||||
findScopes = [newFindScope as Range];
|
||||
} else {
|
||||
// multiline find scope => expand to line starts / ends
|
||||
findScope = new Range(findScope.startLineNumber, 1, findScope.endLineNumber, this._editor.getModel().getLineMaxColumn(findScope.endLineNumber));
|
||||
findScopes = newFindScope;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
findScopes = this._decorations.getFindScopes();
|
||||
}
|
||||
if (findScopes !== null) {
|
||||
findScopes = findScopes.map(findScope => {
|
||||
if (findScope.startLineNumber !== findScope.endLineNumber) {
|
||||
let endLineNumber = findScope.endLineNumber;
|
||||
|
||||
if (findScope.endColumn === 1) {
|
||||
endLineNumber = endLineNumber - 1;
|
||||
}
|
||||
|
||||
return new Range(findScope.startLineNumber, 1, endLineNumber, this._editor.getModel().getLineMaxColumn(endLineNumber));
|
||||
}
|
||||
return findScope;
|
||||
});
|
||||
}
|
||||
|
||||
let findMatches = this._findMatches(findScope, false, MATCHES_LIMIT);
|
||||
this._decorations.set(findMatches, findScope);
|
||||
let findMatches = this._findMatches(findScopes, false, MATCHES_LIMIT);
|
||||
this._decorations.set(findMatches, findScopes);
|
||||
|
||||
const editorSelection = this._editor.getSelection();
|
||||
let currentMatchesPosition = this._decorations.getCurrentMatchesPosition(editorSelection);
|
||||
@@ -205,7 +215,7 @@ export class FindModelBoundToEditorModel {
|
||||
undefined
|
||||
);
|
||||
|
||||
if (moveCursor) {
|
||||
if (moveCursor && this._editor.getOption(EditorOption.find).cursorMoveOnType) {
|
||||
this._moveToNextMatch(this._decorations.getStartPosition());
|
||||
}
|
||||
}
|
||||
@@ -467,9 +477,12 @@ export class FindModelBoundToEditorModel {
|
||||
}
|
||||
}
|
||||
|
||||
private _findMatches(findScope: Range | null, captureMatches: boolean, limitResultCount: number): FindMatch[] {
|
||||
let searchRange = FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), findScope);
|
||||
return this._editor.getModel().findMatches(this._state.searchString, searchRange, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getOption(EditorOption.wordSeparators) : null, captureMatches, limitResultCount);
|
||||
private _findMatches(findScopes: Range[] | null, captureMatches: boolean, limitResultCount: number): FindMatch[] {
|
||||
const searchRanges = (findScopes as []).map((scope: Range | null) => // {{SQL CARBON EDIT}} strict-null-check
|
||||
FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), scope)
|
||||
);
|
||||
|
||||
return this._editor.getModel().findMatches(this._state.searchString, searchRanges, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getOption(EditorOption.wordSeparators) : null, captureMatches, limitResultCount);
|
||||
}
|
||||
|
||||
public replaceAll(): void {
|
||||
@@ -477,13 +490,13 @@ export class FindModelBoundToEditorModel {
|
||||
return;
|
||||
}
|
||||
|
||||
const findScope = this._decorations.getFindScope();
|
||||
const findScopes = this._decorations.getFindScopes();
|
||||
|
||||
if (findScope === null && this._state.matchesCount >= MATCHES_LIMIT) {
|
||||
if (findScopes === null && this._state.matchesCount >= MATCHES_LIMIT) {
|
||||
// Doing a replace on the entire file that is over ${MATCHES_LIMIT} matches
|
||||
this._largeReplaceAll();
|
||||
} else {
|
||||
this._regularReplaceAll(findScope);
|
||||
this._regularReplaceAll(findScopes);
|
||||
}
|
||||
|
||||
this.research(false);
|
||||
@@ -528,10 +541,10 @@ export class FindModelBoundToEditorModel {
|
||||
this._executeEditorCommand('replaceAll', command);
|
||||
}
|
||||
|
||||
private _regularReplaceAll(findScope: Range | null): void {
|
||||
private _regularReplaceAll(findScopes: Range[] | null): void {
|
||||
const replacePattern = this._getReplacePattern();
|
||||
// Get all the ranges (even more than the highlighted ones)
|
||||
let matches = this._findMatches(findScope, replacePattern.hasReplacementPatterns || this._state.preserveCase, Constants.MAX_SAFE_SMALL_INTEGER);
|
||||
let matches = this._findMatches(findScopes, replacePattern.hasReplacementPatterns || this._state.preserveCase, Constants.MAX_SAFE_SMALL_INTEGER);
|
||||
|
||||
let replaceStrings: string[] = [];
|
||||
for (let i = 0, len = matches.length; i < len; i++) {
|
||||
@@ -547,10 +560,10 @@ export class FindModelBoundToEditorModel {
|
||||
return;
|
||||
}
|
||||
|
||||
let findScope = this._decorations.getFindScope();
|
||||
let findScopes = this._decorations.getFindScopes();
|
||||
|
||||
// Get all the ranges (even more than the highlighted ones)
|
||||
let matches = this._findMatches(findScope, false, Constants.MAX_SAFE_SMALL_INTEGER);
|
||||
let matches = this._findMatches(findScopes, false, Constants.MAX_SAFE_SMALL_INTEGER);
|
||||
let selections = matches.map(m => new Selection(m.range.startLineNumber, m.range.startColumn, m.range.endLineNumber, m.range.endColumn));
|
||||
|
||||
// If one of the ranges is the editor selection, then maintain it as primary
|
||||
|
||||
@@ -46,7 +46,7 @@ export interface INewFindReplaceState {
|
||||
matchCaseOverride?: FindOptionOverride;
|
||||
preserveCase?: boolean;
|
||||
preserveCaseOverride?: FindOptionOverride;
|
||||
searchScope?: Range | null;
|
||||
searchScope?: Range[] | null;
|
||||
loop?: boolean;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export class FindReplaceState extends Disposable {
|
||||
private _matchCaseOverride: FindOptionOverride;
|
||||
private _preserveCase: boolean;
|
||||
private _preserveCaseOverride: FindOptionOverride;
|
||||
private _searchScope: Range | null;
|
||||
private _searchScope: Range[] | null;
|
||||
private _matchesPosition: number;
|
||||
private _matchesCount: number;
|
||||
private _currentMatch: Range | null;
|
||||
@@ -94,7 +94,7 @@ export class FindReplaceState extends Disposable {
|
||||
public get actualMatchCase(): boolean { return this._matchCase; }
|
||||
public get actualPreserveCase(): boolean { return this._preserveCase; }
|
||||
|
||||
public get searchScope(): Range | null { return this._searchScope; }
|
||||
public get searchScope(): Range[] | null { return this._searchScope; }
|
||||
public get matchesPosition(): number { return this._matchesPosition; }
|
||||
public get matchesCount(): number { return this._matchesCount; }
|
||||
public get currentMatch(): Range | null { return this._currentMatch; }
|
||||
@@ -238,7 +238,11 @@ export class FindReplaceState extends Disposable {
|
||||
this._preserveCase = newState.preserveCase;
|
||||
}
|
||||
if (typeof newState.searchScope !== 'undefined') {
|
||||
if (!Range.equalsRange(this._searchScope, newState.searchScope)) {
|
||||
if (!newState.searchScope?.every((newSearchScope) => {
|
||||
return this._searchScope?.some(existingSearchScope => {
|
||||
return !Range.equalsRange(existingSearchScope, newSearchScope);
|
||||
});
|
||||
})) {
|
||||
this._searchScope = newState.searchScope;
|
||||
changeEvent.searchScope = true;
|
||||
somethingChanged = true;
|
||||
|
||||
@@ -804,16 +804,26 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL
|
||||
}
|
||||
|
||||
if (this._toggleSelectionFind.checked) {
|
||||
let selection = this._codeEditor.getSelection();
|
||||
if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) {
|
||||
selection = selection.setEndPosition(selection.endLineNumber - 1, this._codeEditor.getModel().getLineMaxColumn(selection.endLineNumber - 1));
|
||||
}
|
||||
const currentMatch = this._state.currentMatch;
|
||||
if (selection.startLineNumber !== selection.endLineNumber) {
|
||||
if (!Range.equalsRange(selection, currentMatch)) {
|
||||
// Reseed find scope
|
||||
this._state.change({ searchScope: selection }, true);
|
||||
let selections = this._codeEditor.getSelections();
|
||||
|
||||
selections.map(selection => {
|
||||
if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) {
|
||||
selection = selection.setEndPosition(
|
||||
selection.endLineNumber - 1,
|
||||
this._codeEditor.getModel()!.getLineMaxColumn(selection.endLineNumber - 1)
|
||||
);
|
||||
}
|
||||
const currentMatch = this._state.currentMatch;
|
||||
if (selection.startLineNumber !== selection.endLineNumber) {
|
||||
if (!Range.equalsRange(selection, currentMatch)) {
|
||||
return selection;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}).filter(element => !!element);
|
||||
|
||||
if (selections.length) {
|
||||
this._state.change({ searchScope: selections as Range[] }, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1028,12 +1038,19 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL
|
||||
this._register(this._toggleSelectionFind.onChange(() => {
|
||||
if (this._toggleSelectionFind.checked) {
|
||||
if (this._codeEditor.hasModel()) {
|
||||
let selection = this._codeEditor.getSelection();
|
||||
if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) {
|
||||
selection = selection.setEndPosition(selection.endLineNumber - 1, this._codeEditor.getModel().getLineMaxColumn(selection.endLineNumber - 1));
|
||||
}
|
||||
if (!selection.isEmpty()) {
|
||||
this._state.change({ searchScope: selection }, true);
|
||||
let selections = this._codeEditor.getSelections();
|
||||
selections.map(selection => {
|
||||
if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) {
|
||||
selection = selection.setEndPosition(selection.endLineNumber - 1, this._codeEditor.getModel()!.getLineMaxColumn(selection.endLineNumber - 1));
|
||||
}
|
||||
if (!selection.isEmpty()) {
|
||||
return selection;
|
||||
}
|
||||
return null;
|
||||
}).filter(element => !!element);
|
||||
|
||||
if (selections.length) {
|
||||
this._state.change({ searchScope: selections as Range[] }, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -309,10 +309,10 @@ suite.skip('FindController', async () => {
|
||||
assert.equal(findController.getState().searchScope, null);
|
||||
|
||||
findController.getState().change({
|
||||
searchScope: new Range(1, 1, 1, 5)
|
||||
searchScope: [new Range(1, 1, 1, 5)]
|
||||
}, false);
|
||||
|
||||
assert.deepEqual(findController.getState().searchScope, new Range(1, 1, 1, 5));
|
||||
assert.deepEqual(findController.getState().searchScope, [new Range(1, 1, 1, 5)]);
|
||||
|
||||
findController.closeFindWidget();
|
||||
assert.equal(findController.getState().searchScope, null);
|
||||
@@ -523,10 +523,8 @@ suite.skip('FindController query options persistence', async () => {
|
||||
'var z = (3 * 5)',
|
||||
], { serviceCollection: serviceCollection, find: { autoFindInSelection: 'always', globalFindClipboard: false } }, async (editor) => {
|
||||
// clipboardState = '';
|
||||
editor.setSelection(new Range(1, 1, 2, 1));
|
||||
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
|
||||
|
||||
await findController.start({
|
||||
const findConfig = {
|
||||
forceRevealReplace: false,
|
||||
seedSearchStringFromSelection: false,
|
||||
seedSearchStringFromGlobalClipboard: false,
|
||||
@@ -534,9 +532,17 @@ suite.skip('FindController query options persistence', async () => {
|
||||
shouldAnimate: false,
|
||||
updateSearchScope: true,
|
||||
loop: true
|
||||
});
|
||||
};
|
||||
|
||||
assert.deepEqual(findController.getState().searchScope, new Selection(1, 1, 2, 1));
|
||||
editor.setSelection(new Range(1, 1, 2, 1));
|
||||
findController.start(findConfig);
|
||||
assert.deepEqual(findController.getState().searchScope, [new Selection(1, 1, 2, 1)]);
|
||||
|
||||
findController.closeFindWidget();
|
||||
|
||||
editor.setSelections([new Selection(1, 1, 2, 1), new Selection(2, 1, 2, 5)]);
|
||||
findController.start(findConfig);
|
||||
assert.deepEqual(findController.getState().searchScope, [new Selection(1, 1, 2, 1), new Selection(2, 1, 2, 5)]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -584,7 +590,7 @@ suite.skip('FindController query options persistence', async () => {
|
||||
loop: true
|
||||
});
|
||||
|
||||
assert.deepEqual(findController.getState().searchScope, new Selection(1, 2, 1, 3));
|
||||
assert.deepEqual(findController.getState().searchScope, [new Selection(1, 2, 1, 3)]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -609,7 +615,7 @@ suite.skip('FindController query options persistence', async () => {
|
||||
loop: true
|
||||
});
|
||||
|
||||
assert.deepEqual(findController.getState().searchScope, new Selection(1, 6, 2, 1));
|
||||
assert.deepEqual(findController.getState().searchScope, [new Selection(1, 6, 2, 1)]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -210,7 +210,7 @@ suite('FindModel', () => {
|
||||
);
|
||||
|
||||
// simulate adding a search scope
|
||||
findState.change({ searchScope: new Range(8, 1, 10, 1) }, true);
|
||||
findState.change({ searchScope: [new Range(8, 1, 10, 1)] }, true);
|
||||
assertFindState(
|
||||
editor,
|
||||
[8, 14, 8, 19],
|
||||
@@ -443,7 +443,7 @@ suite('FindModel', () => {
|
||||
|
||||
findTest('find model next stays in scope', (editor) => {
|
||||
let findState = new FindReplaceState();
|
||||
findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 9, 1) }, false);
|
||||
findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 9, 1)] }, false);
|
||||
let findModel = new FindModelBoundToEditorModel(editor, findState);
|
||||
|
||||
assertFindState(
|
||||
@@ -493,6 +493,131 @@ suite('FindModel', () => {
|
||||
findState.dispose();
|
||||
});
|
||||
|
||||
findTest('multi-selection find model next stays in scope (overlap)', (editor) => {
|
||||
let findState = new FindReplaceState();
|
||||
findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 8, 2), new Range(8, 1, 9, 1)] }, false);
|
||||
let findModel = new FindModelBoundToEditorModel(editor, findState);
|
||||
|
||||
assertFindState(
|
||||
editor,
|
||||
[1, 1, 1, 1],
|
||||
null,
|
||||
[
|
||||
[7, 14, 7, 19],
|
||||
[8, 14, 8, 19]
|
||||
]
|
||||
);
|
||||
|
||||
findModel.moveToNextMatch();
|
||||
assertFindState(
|
||||
editor,
|
||||
[7, 14, 7, 19],
|
||||
[7, 14, 7, 19],
|
||||
[
|
||||
[7, 14, 7, 19],
|
||||
[8, 14, 8, 19]
|
||||
]
|
||||
);
|
||||
|
||||
findModel.moveToNextMatch();
|
||||
assertFindState(
|
||||
editor,
|
||||
[8, 14, 8, 19],
|
||||
[8, 14, 8, 19],
|
||||
[
|
||||
[7, 14, 7, 19],
|
||||
[8, 14, 8, 19]
|
||||
]
|
||||
);
|
||||
|
||||
findModel.moveToNextMatch();
|
||||
assertFindState(
|
||||
editor,
|
||||
[7, 14, 7, 19],
|
||||
[7, 14, 7, 19],
|
||||
[
|
||||
[7, 14, 7, 19],
|
||||
[8, 14, 8, 19]
|
||||
]
|
||||
);
|
||||
|
||||
findModel.dispose();
|
||||
findState.dispose();
|
||||
});
|
||||
|
||||
findTest('multi-selection find model next stays in scope', (editor) => {
|
||||
let findState = new FindReplaceState();
|
||||
findState.change({ searchString: 'hello', matchCase: true, wholeWord: false, searchScope: [new Range(6, 1, 7, 38), new Range(9, 3, 9, 38)] }, false);
|
||||
let findModel = new FindModelBoundToEditorModel(editor, findState);
|
||||
|
||||
assertFindState(
|
||||
editor,
|
||||
[1, 1, 1, 1],
|
||||
null,
|
||||
[
|
||||
[6, 14, 6, 19],
|
||||
// `matchCase: false` would
|
||||
// find this match as well:
|
||||
// [6, 27, 6, 32],
|
||||
[7, 14, 7, 19],
|
||||
// `wholeWord: true` would
|
||||
// exclude this match:
|
||||
[9, 14, 9, 19],
|
||||
]
|
||||
);
|
||||
|
||||
findModel.moveToNextMatch();
|
||||
assertFindState(
|
||||
editor,
|
||||
[6, 14, 6, 19],
|
||||
[6, 14, 6, 19],
|
||||
[
|
||||
[6, 14, 6, 19],
|
||||
[7, 14, 7, 19],
|
||||
[9, 14, 9, 19],
|
||||
]
|
||||
);
|
||||
|
||||
findModel.moveToNextMatch();
|
||||
assertFindState(
|
||||
editor,
|
||||
[7, 14, 7, 19],
|
||||
[7, 14, 7, 19],
|
||||
[
|
||||
[6, 14, 6, 19],
|
||||
[7, 14, 7, 19],
|
||||
[9, 14, 9, 19],
|
||||
]
|
||||
);
|
||||
|
||||
findModel.moveToNextMatch();
|
||||
assertFindState(
|
||||
editor,
|
||||
[9, 14, 9, 19],
|
||||
[9, 14, 9, 19],
|
||||
[
|
||||
[6, 14, 6, 19],
|
||||
[7, 14, 7, 19],
|
||||
[9, 14, 9, 19],
|
||||
]
|
||||
);
|
||||
|
||||
findModel.moveToNextMatch();
|
||||
assertFindState(
|
||||
editor,
|
||||
[6, 14, 6, 19],
|
||||
[6, 14, 6, 19],
|
||||
[
|
||||
[6, 14, 6, 19],
|
||||
[7, 14, 7, 19],
|
||||
[9, 14, 9, 19],
|
||||
]
|
||||
);
|
||||
|
||||
findModel.dispose();
|
||||
findState.dispose();
|
||||
});
|
||||
|
||||
findTest('find model prev', (editor) => {
|
||||
let findState = new FindReplaceState();
|
||||
findState.change({ searchString: 'hello', wholeWord: true }, false);
|
||||
@@ -581,7 +706,7 @@ suite('FindModel', () => {
|
||||
|
||||
findTest('find model prev stays in scope', (editor) => {
|
||||
let findState = new FindReplaceState();
|
||||
findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 9, 1) }, false);
|
||||
findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 9, 1)] }, false);
|
||||
let findModel = new FindModelBoundToEditorModel(editor, findState);
|
||||
|
||||
assertFindState(
|
||||
@@ -2073,7 +2198,7 @@ suite('FindModel', () => {
|
||||
|
||||
findTest('issue #27083. search scope works even if it is a single line', (editor) => {
|
||||
let findState = new FindReplaceState();
|
||||
findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 8, 1) }, false);
|
||||
findState.change({ searchString: 'hello', wholeWord: true, searchScope: [new Range(7, 1, 8, 1)] }, false);
|
||||
let findModel = new FindModelBoundToEditorModel(editor, findState);
|
||||
|
||||
assertFindState(
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { asArray, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -120,11 +120,12 @@ export abstract class FormattingConflicts {
|
||||
}
|
||||
}
|
||||
|
||||
export async function formatDocumentRangeWithSelectedProvider(
|
||||
export async function formatDocumentRangesWithSelectedProvider(
|
||||
accessor: ServicesAccessor,
|
||||
editorOrModel: ITextModel | IActiveCodeEditor,
|
||||
range: Range,
|
||||
rangeOrRanges: Range | Range[],
|
||||
mode: FormattingMode,
|
||||
progress: IProgress<DocumentRangeFormattingEditProvider>,
|
||||
token: CancellationToken
|
||||
): Promise<void> {
|
||||
|
||||
@@ -133,15 +134,16 @@ export async function formatDocumentRangeWithSelectedProvider(
|
||||
const provider = DocumentRangeFormattingEditProviderRegistry.ordered(model);
|
||||
const selected = await FormattingConflicts.select(provider, model, mode);
|
||||
if (selected) {
|
||||
await instaService.invokeFunction(formatDocumentRangeWithProvider, selected, editorOrModel, range, token);
|
||||
progress.report(selected);
|
||||
await instaService.invokeFunction(formatDocumentRangesWithProvider, selected, editorOrModel, rangeOrRanges, token);
|
||||
}
|
||||
}
|
||||
|
||||
export async function formatDocumentRangeWithProvider(
|
||||
export async function formatDocumentRangesWithProvider(
|
||||
accessor: ServicesAccessor,
|
||||
provider: DocumentRangeFormattingEditProvider,
|
||||
editorOrModel: ITextModel | IActiveCodeEditor,
|
||||
range: Range,
|
||||
rangeOrRanges: Range | Range[],
|
||||
token: CancellationToken
|
||||
): Promise<boolean> {
|
||||
const workerService = accessor.get(IEditorWorkerService);
|
||||
@@ -156,39 +158,53 @@ export async function formatDocumentRangeWithProvider(
|
||||
cts = new TextModelCancellationTokenSource(editorOrModel, token);
|
||||
}
|
||||
|
||||
let edits: TextEdit[] | undefined;
|
||||
try {
|
||||
const rawEdits = await provider.provideDocumentRangeFormattingEdits(
|
||||
model,
|
||||
range,
|
||||
model.getFormattingOptions(),
|
||||
cts.token
|
||||
);
|
||||
edits = await workerService.computeMoreMinimalEdits(model.uri, rawEdits);
|
||||
|
||||
if (cts.token.isCancellationRequested) {
|
||||
return true;
|
||||
// make sure that ranges don't overlap nor touch each other
|
||||
let ranges: Range[] = [];
|
||||
let len = 0;
|
||||
for (let range of asArray(rangeOrRanges).sort(Range.compareRangesUsingStarts)) {
|
||||
if (len > 0 && Range.areIntersectingOrTouching(ranges[len - 1], range)) {
|
||||
ranges[len - 1] = Range.fromPositions(ranges[len - 1].getStartPosition(), range.getEndPosition());
|
||||
} else {
|
||||
len = ranges.push(range);
|
||||
}
|
||||
|
||||
} finally {
|
||||
cts.dispose();
|
||||
}
|
||||
|
||||
if (!edits || edits.length === 0) {
|
||||
const allEdits: TextEdit[] = [];
|
||||
for (let range of ranges) {
|
||||
try {
|
||||
const rawEdits = await provider.provideDocumentRangeFormattingEdits(
|
||||
model,
|
||||
range,
|
||||
model.getFormattingOptions(),
|
||||
cts.token
|
||||
);
|
||||
const minEdits = await workerService.computeMoreMinimalEdits(model.uri, rawEdits);
|
||||
if (minEdits) {
|
||||
allEdits.push(...minEdits);
|
||||
}
|
||||
if (cts.token.isCancellationRequested) {
|
||||
return true;
|
||||
}
|
||||
} finally {
|
||||
cts.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if (allEdits.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCodeEditor(editorOrModel)) {
|
||||
// use editor to apply edits
|
||||
FormattingEdit.execute(editorOrModel, edits, true);
|
||||
alertFormattingEdits(edits);
|
||||
FormattingEdit.execute(editorOrModel, allEdits, true);
|
||||
alertFormattingEdits(allEdits);
|
||||
editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), ScrollType.Immediate);
|
||||
|
||||
} else {
|
||||
// use model to apply edits
|
||||
const [{ range }] = edits;
|
||||
const [{ range }] = allEdits;
|
||||
const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
|
||||
model.pushEditOperations([initialSelection], edits.map(edit => {
|
||||
model.pushEditOperations([initialSelection], allEdits.map(edit => {
|
||||
return {
|
||||
text: edit.text,
|
||||
range: Range.lift(edit.range),
|
||||
|
||||
@@ -16,7 +16,7 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { getOnTypeFormattingEdits, alertFormattingEdits, formatDocumentRangeWithSelectedProvider, formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format';
|
||||
import { getOnTypeFormattingEdits, alertFormattingEdits, formatDocumentRangesWithSelectedProvider, formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format';
|
||||
import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit';
|
||||
import * as nls from 'vs/nls';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
@@ -25,7 +25,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { Progress } from 'vs/platform/progress/common/progress';
|
||||
import { Progress, IEditorProgressService } from 'vs/platform/progress/common/progress';
|
||||
|
||||
class FormatOnType implements IEditorContribution {
|
||||
|
||||
@@ -202,7 +202,7 @@ class FormatOnPaste implements IEditorContribution {
|
||||
if (this.editor.getSelections().length > 1) {
|
||||
return;
|
||||
}
|
||||
this._instantiationService.invokeFunction(formatDocumentRangeWithSelectedProvider, this.editor, range, FormattingMode.Silent, CancellationToken.None).catch(onUnexpectedError);
|
||||
this._instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, this.editor, range, FormattingMode.Silent, Progress.None, CancellationToken.None).catch(onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,11 @@ class FormatDocumentAction extends EditorAction {
|
||||
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
if (editor.hasModel()) {
|
||||
const instaService = accessor.get(IInstantiationService);
|
||||
await instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None);
|
||||
const progressService = accessor.get(IEditorProgressService);
|
||||
await progressService.showWhile(
|
||||
instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None),
|
||||
250
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,7 +271,12 @@ class FormatSelectionAction extends EditorAction {
|
||||
if (range.isEmpty()) {
|
||||
range = new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber));
|
||||
}
|
||||
await instaService.invokeFunction(formatDocumentRangeWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None);
|
||||
|
||||
const progressService = accessor.get(IEditorProgressService);
|
||||
await progressService.showWhile(
|
||||
instaService.invokeFunction(formatDocumentRangesWithSelectedProvider, editor, range, FormattingMode.Explicit, Progress.None, CancellationToken.None),
|
||||
250
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ class MessageWidget {
|
||||
|
||||
let relatedResource = document.createElement('a');
|
||||
dom.addClass(relatedResource, 'filename');
|
||||
relatedResource.innerHTML = `${getBaseLabel(related.resource)}(${related.startLineNumber}, ${related.startColumn}): `;
|
||||
relatedResource.innerText = `${getBaseLabel(related.resource)}(${related.startLineNumber}, ${related.startColumn}): `;
|
||||
relatedResource.title = getPathLabel(related.resource, undefined);
|
||||
this._relatedDiagnostics.set(relatedResource, related);
|
||||
|
||||
|
||||
@@ -429,7 +429,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
|
||||
|
||||
if (this._model.isEmpty) {
|
||||
this.setTitle('');
|
||||
this._messageContainer.innerHTML = nls.localize('noResults', "No results");
|
||||
this._messageContainer.innerText = nls.localize('noResults', "No results");
|
||||
dom.show(this._messageContainer);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -601,13 +601,15 @@ export class MultiCursorSelectionController extends Disposable implements IEdito
|
||||
}
|
||||
|
||||
if (findState.searchScope) {
|
||||
const state = findState.searchScope;
|
||||
const states = findState.searchScope;
|
||||
let inSelection: FindMatch[] | null = [];
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
if (matches[i].range.endLineNumber <= state.endLineNumber && matches[i].range.startLineNumber >= state.startLineNumber) {
|
||||
inSelection.push(matches[i]);
|
||||
}
|
||||
}
|
||||
matches.forEach((match) => {
|
||||
states.forEach((state) => {
|
||||
if (match.range.endLineNumber <= state.endLineNumber && match.range.startLineNumber >= state.startLineNumber) {
|
||||
inSelection!.push(match);
|
||||
}
|
||||
});
|
||||
});
|
||||
matches = inSelection;
|
||||
}
|
||||
|
||||
|
||||
@@ -194,8 +194,8 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget {
|
||||
dom.toggleClass(this.domNodes.element, 'multiple', multiple);
|
||||
this.keyMultipleSignatures.set(multiple);
|
||||
|
||||
this.domNodes.signature.innerHTML = '';
|
||||
this.domNodes.docs.innerHTML = '';
|
||||
this.domNodes.signature.innerText = '';
|
||||
this.domNodes.docs.innerText = '';
|
||||
|
||||
const signature = hints.signatures[hints.activeSignature];
|
||||
if (!signature) {
|
||||
|
||||
@@ -11,7 +11,6 @@ import { Action } from 'vs/base/common/actions';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
|
||||
@@ -223,10 +222,10 @@ export abstract class PeekViewWidget extends ZoneWidget {
|
||||
|
||||
setTitle(primaryHeading: string, secondaryHeading?: string): void {
|
||||
if (this._primaryHeading && this._secondaryHeading) {
|
||||
this._primaryHeading.innerHTML = strings.escape(primaryHeading);
|
||||
this._primaryHeading.innerText = primaryHeading;
|
||||
this._primaryHeading.setAttribute('aria-label', primaryHeading);
|
||||
if (secondaryHeading) {
|
||||
this._secondaryHeading.innerHTML = strings.escape(secondaryHeading);
|
||||
this._secondaryHeading.innerText = secondaryHeading;
|
||||
} else {
|
||||
dom.clearNode(this._secondaryHeading);
|
||||
}
|
||||
@@ -236,7 +235,7 @@ export abstract class PeekViewWidget extends ZoneWidget {
|
||||
setMetaTitle(value: string): void {
|
||||
if (this._metaHeading) {
|
||||
if (value) {
|
||||
this._metaHeading.innerHTML = strings.escape(value);
|
||||
this._metaHeading.innerText = value;
|
||||
dom.show(this._metaHeading);
|
||||
} else {
|
||||
dom.hide(this._metaHeading);
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as nls from 'vs/nls';
|
||||
import { registerEditorContribution, registerModelAndPositionCommand, EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
@@ -16,7 +16,7 @@ import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness, IIdentifiedS
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { OnTypeRenameProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { first, createCancelablePromise, CancelablePromise, RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { first, createCancelablePromise, CancelablePromise, Delayer } from 'vs/base/common/async';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
@@ -24,11 +24,12 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { isPromiseCanceledError, onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
|
||||
export const CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE = new RawContextKey<boolean>('onTypeRenameInputVisible', false);
|
||||
|
||||
@@ -50,14 +51,19 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
|
||||
|
||||
private readonly _visibleContextKey: IContextKey<boolean>;
|
||||
|
||||
private _currentRequest: CancelablePromise<{
|
||||
ranges: IRange[],
|
||||
stopPattern?: RegExp
|
||||
} | null | undefined> | null;
|
||||
private _rangeUpdateTriggerPromise: Promise<any> | null;
|
||||
private _rangeSyncTriggerPromise: Promise<any> | null;
|
||||
|
||||
private _currentRequest: CancelablePromise<any> | null;
|
||||
private _currentRequestPosition: Position | null;
|
||||
private _currentRequestModelVersion: number | null;
|
||||
|
||||
private _currentDecorations: string[]; // The one at index 0 is the reference one
|
||||
private _stopPattern: RegExp;
|
||||
private _languageWordPattern: RegExp | null;
|
||||
private _currentWordPattern: RegExp | null;
|
||||
private _ignoreChangeEvent: boolean;
|
||||
private _updateMirrors: RunOnceScheduler;
|
||||
|
||||
private readonly _localToDispose = this._register(new DisposableStore());
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@@ -65,103 +71,117 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
|
||||
) {
|
||||
super();
|
||||
this._editor = editor;
|
||||
this._enabled = this._editor.getOption(EditorOption.renameOnType);
|
||||
this._enabled = false;
|
||||
this._visibleContextKey = CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE.bindTo(contextKeyService);
|
||||
this._currentRequest = null;
|
||||
|
||||
this._currentDecorations = [];
|
||||
this._stopPattern = /^\s/;
|
||||
this._languageWordPattern = null;
|
||||
this._currentWordPattern = null;
|
||||
this._ignoreChangeEvent = false;
|
||||
this._updateMirrors = this._register(new RunOnceScheduler(() => this._doUpdateMirrors(), 0));
|
||||
this._localToDispose = this._register(new DisposableStore());
|
||||
|
||||
this._register(this._editor.onDidChangeModel((e) => {
|
||||
this.stopAll();
|
||||
this.run();
|
||||
}));
|
||||
this._rangeUpdateTriggerPromise = null;
|
||||
this._rangeSyncTriggerPromise = null;
|
||||
|
||||
this._register(this._editor.onDidChangeConfiguration((e) => {
|
||||
this._currentRequest = null;
|
||||
this._currentRequestPosition = null;
|
||||
this._currentRequestModelVersion = null;
|
||||
|
||||
this._register(this._editor.onDidChangeModel(() => this.reinitialize()));
|
||||
|
||||
this._register(this._editor.onDidChangeConfiguration(e => {
|
||||
if (e.hasChanged(EditorOption.renameOnType)) {
|
||||
this._enabled = this._editor.getOption(EditorOption.renameOnType);
|
||||
this.stopAll();
|
||||
this.run();
|
||||
this.reinitialize();
|
||||
}
|
||||
}));
|
||||
this._register(OnTypeRenameProviderRegistry.onDidChange(() => this.reinitialize()));
|
||||
this._register(this._editor.onDidChangeModelLanguage(() => this.reinitialize()));
|
||||
|
||||
this._register(this._editor.onDidChangeCursorPosition((e) => {
|
||||
// no regions, run
|
||||
if (this._currentDecorations.length === 0) {
|
||||
this.run(e.position);
|
||||
}
|
||||
|
||||
// has cached regions, don't run
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
if (this._currentDecorations.length === 0) {
|
||||
return;
|
||||
}
|
||||
const model = this._editor.getModel();
|
||||
const currentRanges = this._currentDecorations.map(decId => model.getDecorationRange(decId)!);
|
||||
|
||||
// just moving cursor around, don't run again
|
||||
if (Range.containsPosition(currentRanges[0], e.position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// moving cursor out of primary region, run
|
||||
this.run(e.position);
|
||||
}));
|
||||
|
||||
this._register(OnTypeRenameProviderRegistry.onDidChange(() => {
|
||||
this.run();
|
||||
}));
|
||||
|
||||
this._register(this._editor.onDidChangeModelContent((e) => {
|
||||
if (this._ignoreChangeEvent) {
|
||||
return;
|
||||
}
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
if (this._currentDecorations.length === 0) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
if (e.isUndoing || e.isRedoing) {
|
||||
return;
|
||||
}
|
||||
if (e.changes[0] && this._stopPattern.test(e.changes[0].text)) {
|
||||
this.stopAll();
|
||||
return;
|
||||
}
|
||||
this._updateMirrors.schedule();
|
||||
}));
|
||||
this.reinitialize();
|
||||
}
|
||||
|
||||
private _doUpdateMirrors(): void {
|
||||
if (!this._editor.hasModel()) {
|
||||
private reinitialize() {
|
||||
const model = this._editor.getModel();
|
||||
const isEnabled = model !== null && this._editor.getOption(EditorOption.renameOnType) && OnTypeRenameProviderRegistry.has(model);
|
||||
if (isEnabled === this._enabled) {
|
||||
return;
|
||||
}
|
||||
if (this._currentDecorations.length === 0) {
|
||||
|
||||
this._enabled = isEnabled;
|
||||
|
||||
this.clearRanges();
|
||||
this._localToDispose.clear();
|
||||
|
||||
if (!isEnabled || model === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._languageWordPattern = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id);
|
||||
this._localToDispose.add(model.onDidChangeLanguageConfiguration(() => {
|
||||
this._languageWordPattern = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id);
|
||||
}));
|
||||
|
||||
const rangeUpdateScheduler = new Delayer(200);
|
||||
const triggerRangeUpdate = () => {
|
||||
this._rangeUpdateTriggerPromise = rangeUpdateScheduler.trigger(() => this.updateRanges());
|
||||
};
|
||||
const rangeSyncScheduler = new Delayer(0);
|
||||
const triggerRangeSync = (decorations: string[]) => {
|
||||
this._rangeSyncTriggerPromise = rangeSyncScheduler.trigger(() => this._syncRanges(decorations));
|
||||
};
|
||||
this._localToDispose.add(this._editor.onDidChangeCursorPosition((e) => {
|
||||
triggerRangeUpdate();
|
||||
}));
|
||||
this._localToDispose.add(this._editor.onDidChangeModelContent((e) => {
|
||||
if (!this._ignoreChangeEvent) {
|
||||
if (this._currentDecorations.length > 0) {
|
||||
const referenceRange = model.getDecorationRange(this._currentDecorations[0]);
|
||||
if (referenceRange && e.changes.every(c => referenceRange.intersectRanges(c.range))) {
|
||||
triggerRangeSync(this._currentDecorations);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
triggerRangeUpdate();
|
||||
}));
|
||||
this._localToDispose.add({
|
||||
dispose: () => {
|
||||
rangeUpdateScheduler.cancel();
|
||||
rangeSyncScheduler.cancel();
|
||||
}
|
||||
});
|
||||
this.updateRanges();
|
||||
}
|
||||
|
||||
private _syncRanges(decorations: string[]): void {
|
||||
// dalayed invocation, make sure we're still on
|
||||
if (!this._editor.hasModel() || decorations !== this._currentDecorations || decorations.length === 0) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this._editor.getModel();
|
||||
const currentRanges = this._currentDecorations.map(decId => model.getDecorationRange(decId)!);
|
||||
const referenceRange = model.getDecorationRange(decorations[0]);
|
||||
|
||||
const referenceRange = currentRanges[0];
|
||||
if (referenceRange.startLineNumber !== referenceRange.endLineNumber) {
|
||||
return this.stopAll();
|
||||
if (!referenceRange || referenceRange.startLineNumber !== referenceRange.endLineNumber) {
|
||||
return this.clearRanges();
|
||||
}
|
||||
|
||||
const referenceValue = model.getValueInRange(referenceRange);
|
||||
if (this._stopPattern.test(referenceValue)) {
|
||||
return this.stopAll();
|
||||
if (this._currentWordPattern) {
|
||||
const match = referenceValue.match(this._currentWordPattern);
|
||||
const matchLength = match ? match[0].length : 0;
|
||||
if (matchLength !== referenceValue.length) {
|
||||
return this.clearRanges();
|
||||
}
|
||||
}
|
||||
|
||||
let edits: IIdentifiedSingleEditOperation[] = [];
|
||||
for (let i = 1, len = currentRanges.length; i < len; i++) {
|
||||
const mirrorRange = currentRanges[i];
|
||||
for (let i = 1, len = decorations.length; i < len; i++) {
|
||||
const mirrorRange = model.getDecorationRange(decorations[i]);
|
||||
if (!mirrorRange) {
|
||||
continue;
|
||||
}
|
||||
if (mirrorRange.startLineNumber !== mirrorRange.endLineNumber) {
|
||||
edits.push({
|
||||
range: mirrorRange,
|
||||
@@ -207,72 +227,131 @@ export class OnTypeRenameContribution extends Disposable implements IEditorContr
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.clearRanges();
|
||||
super.dispose();
|
||||
this.stopAll();
|
||||
}
|
||||
|
||||
stopAll(): void {
|
||||
public clearRanges(): void {
|
||||
this._visibleContextKey.set(false);
|
||||
this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, []);
|
||||
}
|
||||
|
||||
async run(position: Position | null = this._editor.getPosition(), force = false): Promise<void> {
|
||||
if (!position) {
|
||||
return;
|
||||
}
|
||||
if (!this._enabled && !force) {
|
||||
return;
|
||||
}
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._currentRequest) {
|
||||
this._currentRequest.cancel();
|
||||
this._currentRequest = null;
|
||||
this._currentRequestPosition = null;
|
||||
}
|
||||
}
|
||||
|
||||
public get currentUpdateTriggerPromise(): Promise<any> {
|
||||
return this._rangeUpdateTriggerPromise || Promise.resolve();
|
||||
}
|
||||
|
||||
public get currentSyncTriggerPromise(): Promise<any> {
|
||||
return this._rangeSyncTriggerPromise || Promise.resolve();
|
||||
}
|
||||
|
||||
public async updateRanges(force = false): Promise<void> {
|
||||
if (!this._editor.hasModel()) {
|
||||
this.clearRanges();
|
||||
return;
|
||||
}
|
||||
|
||||
const position = this._editor.getPosition();
|
||||
if (!this._enabled && !force || this._editor.getSelections().length > 1) {
|
||||
// disabled or multicursor
|
||||
this.clearRanges();
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this._editor.getModel();
|
||||
|
||||
this._currentRequest = createCancelablePromise(token => getOnTypeRenameRanges(model, position, token));
|
||||
try {
|
||||
const response = await this._currentRequest;
|
||||
|
||||
let ranges: IRange[] = [];
|
||||
if (response?.ranges) {
|
||||
ranges = response.ranges;
|
||||
const modelVersionId = model.getVersionId();
|
||||
if (this._currentRequestPosition && this._currentRequestModelVersion === modelVersionId) {
|
||||
if (position.equals(this._currentRequestPosition)) {
|
||||
return; // same position
|
||||
}
|
||||
if (response?.stopPattern) {
|
||||
this._stopPattern = response.stopPattern;
|
||||
}
|
||||
|
||||
let foundReferenceRange = false;
|
||||
for (let i = 0, len = ranges.length; i < len; i++) {
|
||||
if (Range.containsPosition(ranges[i], position)) {
|
||||
foundReferenceRange = true;
|
||||
if (i !== 0) {
|
||||
const referenceRange = ranges[i];
|
||||
ranges.splice(i, 1);
|
||||
ranges.unshift(referenceRange);
|
||||
}
|
||||
break;
|
||||
if (this._currentDecorations && this._currentDecorations.length > 0) {
|
||||
const range = model.getDecorationRange(this._currentDecorations[0]);
|
||||
if (range && range.containsPosition(position)) {
|
||||
return; // just moving inside the existing primary range
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundReferenceRange) {
|
||||
// Cannot do on type rename if the ranges are not where the cursor is...
|
||||
this.stopAll();
|
||||
return;
|
||||
}
|
||||
|
||||
const decorations: IModelDeltaDecoration[] = ranges.map(range => ({ range: range, options: OnTypeRenameContribution.DECORATION }));
|
||||
this._visibleContextKey.set(true);
|
||||
this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, decorations);
|
||||
} catch (err) {
|
||||
onUnexpectedError(err);
|
||||
this.stopAll();
|
||||
}
|
||||
|
||||
this._currentRequestPosition = position;
|
||||
this._currentRequestModelVersion = modelVersionId;
|
||||
const request = createCancelablePromise(async token => {
|
||||
try {
|
||||
const response = await getOnTypeRenameRanges(model, position, token);
|
||||
if (request !== this._currentRequest) {
|
||||
return;
|
||||
}
|
||||
this._currentRequest = null;
|
||||
if (modelVersionId !== model.getVersionId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ranges: IRange[] = [];
|
||||
if (response?.ranges) {
|
||||
ranges = response.ranges;
|
||||
}
|
||||
|
||||
this._currentWordPattern = response?.wordPattern || this._languageWordPattern;
|
||||
|
||||
let foundReferenceRange = false;
|
||||
for (let i = 0, len = ranges.length; i < len; i++) {
|
||||
if (Range.containsPosition(ranges[i], position)) {
|
||||
foundReferenceRange = true;
|
||||
if (i !== 0) {
|
||||
const referenceRange = ranges[i];
|
||||
ranges.splice(i, 1);
|
||||
ranges.unshift(referenceRange);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundReferenceRange) {
|
||||
// Cannot do on type rename if the ranges are not where the cursor is...
|
||||
this.clearRanges();
|
||||
return;
|
||||
}
|
||||
|
||||
const decorations: IModelDeltaDecoration[] = ranges.map(range => ({ range: range, options: OnTypeRenameContribution.DECORATION }));
|
||||
this._visibleContextKey.set(true);
|
||||
this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, decorations);
|
||||
} catch (err) {
|
||||
if (!isPromiseCanceledError(err)) {
|
||||
onUnexpectedError(err);
|
||||
}
|
||||
if (this._currentRequest === request || !this._currentRequest) {
|
||||
// stop if we are still the latest request
|
||||
this.clearRanges();
|
||||
}
|
||||
}
|
||||
});
|
||||
this._currentRequest = request;
|
||||
return request;
|
||||
}
|
||||
|
||||
// private printDecorators(model: ITextModel) {
|
||||
// return this._currentDecorations.map(d => {
|
||||
// const range = model.getDecorationRange(d);
|
||||
// if (range) {
|
||||
// return this.printRange(range);
|
||||
// }
|
||||
// return 'invalid';
|
||||
// }).join(',');
|
||||
// }
|
||||
|
||||
// private printChanges(changes: IModelContentChange[]) {
|
||||
// return changes.map(c => {
|
||||
// return `${this.printRange(c.range)} - ${c.text}`;
|
||||
// }
|
||||
// ).join(',');
|
||||
// }
|
||||
|
||||
// private printRange(range: IRange) {
|
||||
// return `${range.startLineNumber},${range.startColumn}/${range.endLineNumber},${range.endColumn}`;
|
||||
// }
|
||||
}
|
||||
|
||||
export class OnTypeRenameAction extends EditorAction {
|
||||
@@ -310,10 +389,10 @@ export class OnTypeRenameAction extends EditorAction {
|
||||
return super.runCommand(accessor, args);
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
const controller = OnTypeRenameContribution.get(editor);
|
||||
if (controller) {
|
||||
return Promise.resolve(controller.run(editor.getPosition(), true));
|
||||
return Promise.resolve(controller.updateRanges(true));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -323,7 +402,7 @@ const OnTypeRenameCommand = EditorCommand.bindToContribution<OnTypeRenameContrib
|
||||
registerEditorCommand(new OnTypeRenameCommand({
|
||||
id: 'cancelOnTypeRenameInput',
|
||||
precondition: CONTEXT_ONTYPE_RENAME_INPUT_VISIBLE,
|
||||
handler: x => x.stopAll(),
|
||||
handler: x => x.clearRanges(),
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.editorTextFocus,
|
||||
weight: KeybindingWeight.EditorContrib + 99,
|
||||
@@ -335,7 +414,7 @@ registerEditorCommand(new OnTypeRenameCommand({
|
||||
|
||||
export function getOnTypeRenameRanges(model: ITextModel, position: Position, token: CancellationToken): Promise<{
|
||||
ranges: IRange[],
|
||||
stopPattern?: RegExp
|
||||
wordPattern?: RegExp
|
||||
} | undefined | null> {
|
||||
const orderedByScore = OnTypeRenameProviderRegistry.ordered(model);
|
||||
|
||||
@@ -344,16 +423,16 @@ export function getOnTypeRenameRanges(model: ITextModel, position: Position, tok
|
||||
// (good = none empty array)
|
||||
return first<{
|
||||
ranges: IRange[],
|
||||
stopPattern?: RegExp
|
||||
wordPattern?: RegExp
|
||||
} | undefined>(orderedByScore.map(provider => () => {
|
||||
return Promise.resolve(provider.provideOnTypeRenameRanges(model, position, token)).then((ranges) => {
|
||||
if (!ranges) {
|
||||
return Promise.resolve(provider.provideOnTypeRenameRanges(model, position, token)).then((res) => {
|
||||
if (!res) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
ranges,
|
||||
stopPattern: provider.stopPattern
|
||||
ranges: res.ranges,
|
||||
wordPattern: res.wordPattern || provider.wordPattern
|
||||
};
|
||||
}, (err) => {
|
||||
onUnexpectedExternalError(err);
|
||||
|
||||
@@ -6,19 +6,29 @@
|
||||
import * as assert from 'assert';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { Handler } from 'vs/editor/common/editorCommon';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { OnTypeRenameContribution } from 'vs/editor/contrib/rename/onTypeRename';
|
||||
import { createTestCodeEditor, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
|
||||
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
|
||||
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper';
|
||||
|
||||
const mockFile = URI.parse('test:somefile.ttt');
|
||||
const mockFileSelector = { scheme: 'test' };
|
||||
const timeout = 30;
|
||||
|
||||
interface TestEditor {
|
||||
setPosition(pos: Position): Promise<any>;
|
||||
setSelection(sel: IRange): Promise<any>;
|
||||
trigger(source: string | null | undefined, handlerId: string, payload: any): Promise<any>;
|
||||
undo(): void;
|
||||
redo(): void;
|
||||
}
|
||||
|
||||
suite('On type rename', () => {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
@@ -45,26 +55,53 @@ suite('On type rename', () => {
|
||||
|
||||
function testCase(
|
||||
name: string,
|
||||
initialState: { text: string | string[], ranges: Range[], stopPattern?: RegExp },
|
||||
operations: (editor: ITestCodeEditor, contrib: OnTypeRenameContribution) => Promise<void>,
|
||||
initialState: { text: string | string[], responseWordPattern?: RegExp, providerWordPattern?: RegExp },
|
||||
operations: (editor: TestEditor) => Promise<void>,
|
||||
expectedEndText: string | string[]
|
||||
) {
|
||||
test(name, async () => {
|
||||
disposables.add(modes.OnTypeRenameProviderRegistry.register(mockFileSelector, {
|
||||
stopPattern: initialState.stopPattern || /^\s/,
|
||||
|
||||
provideOnTypeRenameRanges() {
|
||||
return initialState.ranges;
|
||||
wordPattern: initialState.providerWordPattern,
|
||||
provideOnTypeRenameRanges(model: ITextModel, pos: IPosition) {
|
||||
const wordAtPos = model.getWordAtPosition(pos);
|
||||
if (wordAtPos) {
|
||||
const matches = model.findMatches(wordAtPos.word, false, false, true, USUAL_WORD_SEPARATORS, false);
|
||||
assert.ok(matches.length > 0);
|
||||
return { ranges: matches.map(m => m.range), wordPattern: initialState.responseWordPattern };
|
||||
}
|
||||
return { ranges: [], wordPattern: initialState.responseWordPattern };
|
||||
}
|
||||
}));
|
||||
|
||||
const editor = createMockEditor(initialState.text);
|
||||
editor.updateOptions({ renameOnType: true });
|
||||
const ontypeRenameContribution = editor.registerAndInstantiateContribution(
|
||||
OnTypeRenameContribution.ID,
|
||||
OnTypeRenameContribution
|
||||
);
|
||||
|
||||
await operations(editor, ontypeRenameContribution);
|
||||
const testEditor: TestEditor = {
|
||||
setPosition(pos: Position) {
|
||||
editor.setPosition(pos);
|
||||
return ontypeRenameContribution.currentUpdateTriggerPromise;
|
||||
},
|
||||
setSelection(sel: IRange) {
|
||||
editor.setSelection(sel);
|
||||
return ontypeRenameContribution.currentUpdateTriggerPromise;
|
||||
},
|
||||
trigger(source: string | null | undefined, handlerId: string, payload: any) {
|
||||
editor.trigger(source, handlerId, payload);
|
||||
return ontypeRenameContribution.currentSyncTriggerPromise;
|
||||
},
|
||||
undo() {
|
||||
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
|
||||
},
|
||||
redo() {
|
||||
CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
|
||||
}
|
||||
};
|
||||
|
||||
await operations(testEditor);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
@@ -80,349 +117,322 @@ suite('On type rename', () => {
|
||||
}
|
||||
|
||||
const state = {
|
||||
text: '<ooo></ooo>',
|
||||
ranges: [
|
||||
new Range(1, 2, 1, 5),
|
||||
new Range(1, 8, 1, 11),
|
||||
]
|
||||
text: '<ooo></ooo>'
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple insertion
|
||||
*/
|
||||
testCase('Simple insert - initial', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Simple insert - initial', state, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<iooo></iooo>');
|
||||
|
||||
testCase('Simple insert - middle', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Simple insert - middle', state, async (editor) => {
|
||||
const pos = new Position(1, 3);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<oioo></oioo>');
|
||||
|
||||
testCase('Simple insert - end', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Simple insert - end', state, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<oooi></oooi>');
|
||||
|
||||
/**
|
||||
* Simple insertion - end
|
||||
*/
|
||||
testCase('Simple insert end - initial', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Simple insert end - initial', state, async (editor) => {
|
||||
const pos = new Position(1, 8);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<iooo></iooo>');
|
||||
|
||||
testCase('Simple insert end - middle', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Simple insert end - middle', state, async (editor) => {
|
||||
const pos = new Position(1, 9);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<oioo></oioo>');
|
||||
|
||||
testCase('Simple insert end - end', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Simple insert end - end', state, async (editor) => {
|
||||
const pos = new Position(1, 11);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<oooi></oooi>');
|
||||
|
||||
/**
|
||||
* Boundary insertion
|
||||
*/
|
||||
testCase('Simple insert - out of boundary', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Simple insert - out of boundary', state, async (editor) => {
|
||||
const pos = new Position(1, 1);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, 'i<ooo></ooo>');
|
||||
|
||||
testCase('Simple insert - out of boundary 2', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Simple insert - out of boundary 2', state, async (editor) => {
|
||||
const pos = new Position(1, 6);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<ooo>i</ooo>');
|
||||
|
||||
testCase('Simple insert - out of boundary 3', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Simple insert - out of boundary 3', state, async (editor) => {
|
||||
const pos = new Position(1, 7);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<ooo><i/ooo>');
|
||||
|
||||
testCase('Simple insert - out of boundary 4', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Simple insert - out of boundary 4', state, async (editor) => {
|
||||
const pos = new Position(1, 12);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<ooo></ooo>i');
|
||||
|
||||
/**
|
||||
* Insert + Move
|
||||
*/
|
||||
testCase('Continuous insert', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Continuous insert', state, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<iiooo></iiooo>');
|
||||
|
||||
testCase('Insert - move - insert', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Insert - move - insert', state, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
editor.setPosition(new Position(1, 4));
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(new Position(1, 4));
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<ioioo></ioioo>');
|
||||
|
||||
testCase('Insert - move - insert outside region', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Insert - move - insert outside region', state, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
editor.setPosition(new Position(1, 7));
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(new Position(1, 7));
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<iooo>i</iooo>');
|
||||
|
||||
/**
|
||||
* Selection insert
|
||||
*/
|
||||
testCase('Selection insert - simple', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Selection insert - simple', state, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.setSelection(new Range(1, 2, 1, 3));
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.setSelection(new Range(1, 2, 1, 3));
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<ioo></ioo>');
|
||||
|
||||
testCase('Selection insert - whole', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Selection insert - whole', state, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.setSelection(new Range(1, 2, 1, 5));
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.setSelection(new Range(1, 2, 1, 5));
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<i></i>');
|
||||
|
||||
testCase('Selection insert - across boundary', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Selection insert - across boundary', state, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.setSelection(new Range(1, 1, 1, 3));
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.setSelection(new Range(1, 1, 1, 3));
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, 'ioo></oo>');
|
||||
|
||||
/**
|
||||
* @todo
|
||||
* Undefined behavior
|
||||
*/
|
||||
// testCase('Selection insert - across two boundary', state, async (editor, ontypeRenameContribution) => {
|
||||
// testCase('Selection insert - across two boundary', state, async (editor) => {
|
||||
// const pos = new Position(1, 2);
|
||||
// editor.setPosition(pos);
|
||||
// await ontypeRenameContribution.run(pos, true);
|
||||
// editor.setSelection(new Range(1, 4, 1, 9));
|
||||
// editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
// await editor.setPosition(pos);
|
||||
// await ontypeRenameContribution.updateLinkedUI(pos);
|
||||
// await editor.setSelection(new Range(1, 4, 1, 9));
|
||||
// await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
// }, '<ooioo>');
|
||||
|
||||
/**
|
||||
* Break out behavior
|
||||
*/
|
||||
testCase('Breakout - type space', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Breakout - type space', state, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: ' ' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: ' ' });
|
||||
}, '<ooo ></ooo>');
|
||||
|
||||
testCase('Breakout - type space then undo', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Breakout - type space then undo', state, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: ' ' });
|
||||
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: ' ' });
|
||||
editor.undo();
|
||||
}, '<ooo></ooo>');
|
||||
|
||||
testCase('Breakout - type space in middle', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Breakout - type space in middle', state, async (editor) => {
|
||||
const pos = new Position(1, 4);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: ' ' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: ' ' });
|
||||
}, '<oo o></ooo>');
|
||||
|
||||
testCase('Breakout - paste content starting with space', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Breakout - paste content starting with space', state, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Paste, { text: ' i="i"' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Paste, { text: ' i="i"' });
|
||||
}, '<ooo i="i"></ooo>');
|
||||
|
||||
testCase('Breakout - paste content starting with space then undo', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Breakout - paste content starting with space then undo', state, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Paste, { text: ' i="i"' });
|
||||
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Paste, { text: ' i="i"' });
|
||||
editor.undo();
|
||||
}, '<ooo></ooo>');
|
||||
|
||||
testCase('Breakout - paste content starting with space in middle', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Breakout - paste content starting with space in middle', state, async (editor) => {
|
||||
const pos = new Position(1, 4);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Paste, { text: ' i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Paste, { text: ' i' });
|
||||
}, '<oo io></ooo>');
|
||||
|
||||
/**
|
||||
* Break out with custom stopPattern
|
||||
* Break out with custom provider wordPattern
|
||||
*/
|
||||
|
||||
const state3 = {
|
||||
...state,
|
||||
stopPattern: /^s/
|
||||
providerWordPattern: /[a-yA-Y]+/
|
||||
};
|
||||
|
||||
testCase('Breakout with stop pattern - insert', state3, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Breakout with stop pattern - insert', state3, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<iooo></iooo>');
|
||||
|
||||
testCase('Breakout with stop pattern - insert stop char', state3, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Breakout with stop pattern - insert stop char', state3, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 's' });
|
||||
}, '<sooo></ooo>');
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'z' });
|
||||
}, '<zooo></ooo>');
|
||||
|
||||
testCase('Breakout with stop pattern - paste char', state3, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Breakout with stop pattern - paste char', state3, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Paste, { text: 's' });
|
||||
}, '<sooo></ooo>');
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Paste, { text: 'z' });
|
||||
}, '<zooo></ooo>');
|
||||
|
||||
testCase('Breakout with stop pattern - paste string', state3, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Breakout with stop pattern - paste string', state3, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Paste, { text: 'so' });
|
||||
}, '<soooo></ooo>');
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Paste, { text: 'zo' });
|
||||
}, '<zoooo></ooo>');
|
||||
|
||||
testCase('Breakout with stop pattern - insert at end', state3, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Breakout with stop pattern - insert at end', state3, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 's' });
|
||||
}, '<ooos></ooo>');
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'z' });
|
||||
}, '<oooz></ooo>');
|
||||
|
||||
const state4 = {
|
||||
...state,
|
||||
providerWordPattern: /[a-yA-Y]+/,
|
||||
responseWordPattern: /[a-eA-E]+/
|
||||
};
|
||||
|
||||
testCase('Breakout with stop pattern - insert stop char, respos', state4, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, '<iooo></ooo>');
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*/
|
||||
testCase('Delete - left char', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Delete - left char', state, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', 'deleteLeft', {});
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', 'deleteLeft', {});
|
||||
}, '<oo></oo>');
|
||||
|
||||
testCase('Delete - left char then undo', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Delete - left char then undo', state, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', 'deleteLeft', {});
|
||||
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', 'deleteLeft', {});
|
||||
editor.undo();
|
||||
}, '<ooo></ooo>');
|
||||
|
||||
testCase('Delete - left word', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Delete - left word', state, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', 'deleteWordLeft', {});
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', 'deleteWordLeft', {});
|
||||
}, '<></>');
|
||||
|
||||
testCase('Delete - left word then undo', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Delete - left word then undo', state, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', 'deleteWordLeft', {});
|
||||
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', 'deleteWordLeft', {});
|
||||
editor.undo();
|
||||
editor.undo();
|
||||
}, '<ooo></ooo>');
|
||||
|
||||
/**
|
||||
* Todo: Fix test
|
||||
*/
|
||||
// testCase('Delete - left all', state, async (editor, ontypeRenameContribution) => {
|
||||
// testCase('Delete - left all', state, async (editor) => {
|
||||
// const pos = new Position(1, 3);
|
||||
// editor.setPosition(pos);
|
||||
// await ontypeRenameContribution.run(pos, true);
|
||||
// editor.trigger('keyboard', 'deleteAllLeft', {});
|
||||
// await editor.setPosition(pos);
|
||||
// await ontypeRenameContribution.updateLinkedUI(pos);
|
||||
// await editor.trigger('keyboard', 'deleteAllLeft', {});
|
||||
// }, '></>');
|
||||
|
||||
/**
|
||||
* Todo: Fix test
|
||||
*/
|
||||
// testCase('Delete - left all then undo', state, async (editor, ontypeRenameContribution) => {
|
||||
// testCase('Delete - left all then undo', state, async (editor) => {
|
||||
// const pos = new Position(1, 5);
|
||||
// editor.setPosition(pos);
|
||||
// await ontypeRenameContribution.run(pos, true);
|
||||
// editor.trigger('keyboard', 'deleteAllLeft', {});
|
||||
// CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
|
||||
// await editor.setPosition(pos);
|
||||
// await ontypeRenameContribution.updateLinkedUI(pos);
|
||||
// await editor.trigger('keyboard', 'deleteAllLeft', {});
|
||||
// editor.undo();
|
||||
// }, '></ooo>');
|
||||
|
||||
testCase('Delete - left all then undo twice', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Delete - left all then undo twice', state, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', 'deleteAllLeft', {});
|
||||
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
|
||||
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', 'deleteAllLeft', {});
|
||||
editor.undo();
|
||||
editor.undo();
|
||||
}, '<ooo></ooo>');
|
||||
|
||||
testCase('Delete - selection', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Delete - selection', state, async (editor) => {
|
||||
const pos = new Position(1, 5);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.setSelection(new Range(1, 2, 1, 3));
|
||||
editor.trigger('keyboard', 'deleteLeft', {});
|
||||
await editor.setPosition(pos);
|
||||
await editor.setSelection(new Range(1, 2, 1, 3));
|
||||
await editor.trigger('keyboard', 'deleteLeft', {});
|
||||
}, '<oo></oo>');
|
||||
|
||||
testCase('Delete - selection across boundary', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Delete - selection across boundary', state, async (editor) => {
|
||||
const pos = new Position(1, 3);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.setSelection(new Range(1, 1, 1, 3));
|
||||
editor.trigger('keyboard', 'deleteLeft', {});
|
||||
await editor.setPosition(pos);
|
||||
await editor.setSelection(new Range(1, 1, 1, 3));
|
||||
await editor.trigger('keyboard', 'deleteLeft', {});
|
||||
}, 'oo></oo>');
|
||||
|
||||
/**
|
||||
* Undo / redo
|
||||
*/
|
||||
testCase('Undo/redo - simple undo', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Undo/redo - simple undo', state, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
|
||||
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
editor.undo();
|
||||
editor.undo();
|
||||
}, '<ooo></ooo>');
|
||||
|
||||
testCase('Undo/redo - simple undo/redo', state, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Undo/redo - simple undo/redo', state, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
|
||||
CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
editor.undo();
|
||||
editor.redo();
|
||||
}, '<iooo></iooo>');
|
||||
|
||||
/**
|
||||
@@ -432,18 +442,13 @@ suite('On type rename', () => {
|
||||
text: [
|
||||
'<ooo>',
|
||||
'</ooo>'
|
||||
],
|
||||
ranges: [
|
||||
new Range(1, 2, 1, 5),
|
||||
new Range(2, 3, 2, 6),
|
||||
]
|
||||
};
|
||||
|
||||
testCase('Multiline insert', state2, async (editor, ontypeRenameContribution) => {
|
||||
testCase('Multiline insert', state2, async (editor) => {
|
||||
const pos = new Position(1, 2);
|
||||
editor.setPosition(pos);
|
||||
await ontypeRenameContribution.run(pos, true);
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
await editor.setPosition(pos);
|
||||
await editor.trigger('keyboard', Handler.Type, { text: 'i' });
|
||||
}, [
|
||||
'<iooo>',
|
||||
'</iooo>'
|
||||
|
||||
@@ -11,7 +11,7 @@ import { SnippetParser, Variable, VariableResolver } from 'vs/editor/contrib/sni
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { Workspace, toWorkspaceFolders, IWorkspace, IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test';
|
||||
import { mock } from 'vs/base/test/common/mock';
|
||||
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
|
||||
|
||||
suite('Snippet Variables Resolver', function () {
|
||||
|
||||
@@ -229,7 +229,7 @@ export class SuggestModel implements IDisposable {
|
||||
if (supports) {
|
||||
// keep existing items that where not computed by the
|
||||
// supports/providers that want to trigger now
|
||||
const items: CompletionItem[] | undefined = this._completionModel ? this._completionModel.adopt(supports) : undefined;
|
||||
const items = this._completionModel?.adopt(supports);
|
||||
this.trigger({ auto: true, shy: false, triggerCharacter: lastChar }, Boolean(this._completionModel), supports, items);
|
||||
}
|
||||
};
|
||||
@@ -556,6 +556,12 @@ export class SuggestModel implements IDisposable {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.leadingWord.word.length !== 0 && ctx.leadingWord.startColumn > this._context.leadingWord.startColumn) {
|
||||
// started a new word while IntelliSense shows -> retrigger
|
||||
this.trigger({ auto: this._context.auto, shy: false }, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.column > this._context.column && this._completionModel.incomplete.size > 0 && ctx.leadingWord.word.length !== 0) {
|
||||
// typed -> moved cursor RIGHT & incomple model & still on a word -> retrigger
|
||||
const { incomplete } = this._completionModel;
|
||||
|
||||
@@ -373,7 +373,7 @@ class SuggestionDetails {
|
||||
this.docs.textContent = documentation;
|
||||
} else {
|
||||
this.docs.classList.add('markdown-docs');
|
||||
this.docs.innerHTML = '';
|
||||
this.docs.innerText = '';
|
||||
const renderedContents = this.markdownRenderer.render(documentation);
|
||||
this.renderDisposeable = renderedContents;
|
||||
this.docs.appendChild(renderedContents.element);
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ISuggestMemoryService } from 'vs/editor/contrib/suggest/suggestMemory';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test';
|
||||
import { mock } from 'vs/base/test/common/mock';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { CompletionProviderRegistry, CompletionItemKind, CompletionItemInsertTextRule } from 'vs/editor/common/modes';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
@@ -34,14 +34,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
|
||||
export interface Ctor<T> {
|
||||
new(): T;
|
||||
}
|
||||
|
||||
export function mock<T>(): Ctor<T> {
|
||||
return function () { } as any;
|
||||
}
|
||||
import { mock } from 'vs/base/test/common/mock';
|
||||
|
||||
|
||||
function createMockEditor(model: TextModel): ITestCodeEditor {
|
||||
@@ -798,4 +791,68 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('Trigger (full) completions when (incomplete) completions are already active #99504', function () {
|
||||
|
||||
let countA = 0;
|
||||
let countB = 0;
|
||||
|
||||
disposables.push(CompletionProviderRegistry.register({ scheme: 'test' }, {
|
||||
provideCompletionItems(doc, pos) {
|
||||
countA += 1;
|
||||
return {
|
||||
incomplete: false, // doesn't matter if incomplete or not
|
||||
suggestions: [{
|
||||
kind: CompletionItemKind.Class,
|
||||
label: 'Z aaa',
|
||||
insertText: 'Z aaa',
|
||||
range: new Range(1, 1, pos.lineNumber, pos.column)
|
||||
}],
|
||||
};
|
||||
}
|
||||
}));
|
||||
disposables.push(CompletionProviderRegistry.register({ scheme: 'test' }, {
|
||||
provideCompletionItems(doc, pos) {
|
||||
countB += 1;
|
||||
return {
|
||||
incomplete: false,
|
||||
suggestions: [{
|
||||
kind: CompletionItemKind.Folder,
|
||||
label: 'aaa',
|
||||
insertText: 'aaa',
|
||||
range: getDefaultSuggestRange(doc, pos)
|
||||
}],
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
return withOracle(async (model, editor) => {
|
||||
|
||||
await assertEvent(model.onDidSuggest, () => {
|
||||
editor.setValue('');
|
||||
editor.setSelection(new Selection(1, 1, 1, 1));
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'Z' });
|
||||
|
||||
}, event => {
|
||||
assert.equal(event.auto, true);
|
||||
assert.equal(event.completionModel.items.length, 1);
|
||||
assert.equal(event.completionModel.items[0].textLabel, 'Z aaa');
|
||||
});
|
||||
|
||||
await assertEvent(model.onDidSuggest, () => {
|
||||
// started another word: Z a|
|
||||
// item should be: Z aaa, aaa
|
||||
editor.trigger('keyboard', Handler.Type, { text: ' a' });
|
||||
}, event => {
|
||||
assert.equal(event.auto, true);
|
||||
assert.equal(event.completionModel.items.length, 2);
|
||||
assert.equal(event.completionModel.items[0].textLabel, 'Z aaa');
|
||||
assert.equal(event.completionModel.items[1].textLabel, 'aaa');
|
||||
|
||||
assert.equal(countA, 2); // should we keep the suggestions from the "active" provider?
|
||||
assert.equal(countB, 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker';
|
||||
import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test';
|
||||
import { mock } from 'vs/base/test/common/mock';
|
||||
import { EditorWorkerHost, EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
|
||||
@@ -81,11 +81,16 @@ suite('suggest, word distance', function () {
|
||||
|
||||
distance = await WordDistance.create(service, editor);
|
||||
|
||||
disposables.add(service);
|
||||
disposables.add(mode);
|
||||
disposables.add(model);
|
||||
disposables.add(editor);
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
disposables.clear();
|
||||
});
|
||||
|
||||
function createSuggestItem(label: string, overwriteBefore: number, position: IPosition): CompletionItem {
|
||||
const suggestion: modes.CompletionItem = {
|
||||
label,
|
||||
|
||||
@@ -265,8 +265,8 @@ class InspectTokensWidget extends Disposable implements IContentWidget {
|
||||
case StandardTokenType.Comment: return 'Comment';
|
||||
case StandardTokenType.String: return 'String';
|
||||
case StandardTokenType.RegEx: return 'RegEx';
|
||||
default: return '??';
|
||||
}
|
||||
return '??';
|
||||
}
|
||||
|
||||
private _fontStyleToString(fontStyle: FontStyle): string {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { ITextAreaInputHost, TextAreaInput } from 'vs/editor/browser/controller/textAreaInput';
|
||||
import { ISimpleModel, PagedScreenReaderStrategy, TextAreaState } from 'vs/editor/browser/controller/textAreaState';
|
||||
@@ -74,7 +73,7 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string
|
||||
container.appendChild(title);
|
||||
|
||||
let startBtn = document.createElement('button');
|
||||
startBtn.innerHTML = 'Start';
|
||||
startBtn.innerText = 'Start';
|
||||
container.appendChild(startBtn);
|
||||
|
||||
|
||||
@@ -96,12 +95,6 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string
|
||||
};
|
||||
},
|
||||
getScreenReaderContent: (currentState: TextAreaState): TextAreaState => {
|
||||
|
||||
if (browser.isIPad) {
|
||||
// Do not place anything in the textarea for the iPad
|
||||
return TextAreaState.EMPTY;
|
||||
}
|
||||
|
||||
const selection = new Range(1, 1 + cursorOffset, 1, 1 + cursorOffset + cursorLength);
|
||||
|
||||
return PagedScreenReaderStrategy.fromEditorSelection(currentState, model, selection, 10, true);
|
||||
@@ -141,10 +134,10 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string
|
||||
|
||||
let expected = 'some ' + expectedStr + ' text';
|
||||
if (text === expected) {
|
||||
check.innerHTML = '[GOOD]';
|
||||
check.innerText = '[GOOD]';
|
||||
check.className = 'check good';
|
||||
} else {
|
||||
check.innerHTML = '[BAD]';
|
||||
check.innerText = '[BAD]';
|
||||
check.className = 'check bad';
|
||||
}
|
||||
check.innerHTML += expected;
|
||||
|
||||
Reference in New Issue
Block a user